Home | History | Annotate | Line # | Download | only in tprof
tprof_armv7.c revision 1.9
      1  1.9       ryo /* $NetBSD: tprof_armv7.c,v 1.9 2022/12/01 00:29:51 ryo Exp $ */
      2  1.1  jmcneill 
      3  1.1  jmcneill /*-
      4  1.1  jmcneill  * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
      5  1.1  jmcneill  * All rights reserved.
      6  1.1  jmcneill  *
      7  1.1  jmcneill  * Redistribution and use in source and binary forms, with or without
      8  1.1  jmcneill  * modification, are permitted provided that the following conditions
      9  1.1  jmcneill  * are met:
     10  1.1  jmcneill  * 1. Redistributions of source code must retain the above copyright
     11  1.1  jmcneill  *    notice, this list of conditions and the following disclaimer.
     12  1.1  jmcneill  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1  jmcneill  *    notice, this list of conditions and the following disclaimer in the
     14  1.1  jmcneill  *    documentation and/or other materials provided with the distribution.
     15  1.1  jmcneill  *
     16  1.1  jmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  1.1  jmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  1.1  jmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  1.1  jmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  1.1  jmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  1.1  jmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  1.1  jmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  1.1  jmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  1.1  jmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  1.1  jmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  1.1  jmcneill  * SUCH DAMAGE.
     27  1.1  jmcneill  */
     28  1.1  jmcneill 
     29  1.1  jmcneill #include <sys/cdefs.h>
     30  1.9       ryo __KERNEL_RCSID(0, "$NetBSD: tprof_armv7.c,v 1.9 2022/12/01 00:29:51 ryo Exp $");
     31  1.1  jmcneill 
     32  1.1  jmcneill #include <sys/param.h>
     33  1.1  jmcneill #include <sys/bus.h>
     34  1.1  jmcneill #include <sys/cpu.h>
     35  1.1  jmcneill #include <sys/xcall.h>
     36  1.1  jmcneill 
     37  1.1  jmcneill #include <dev/tprof/tprof.h>
     38  1.1  jmcneill 
     39  1.1  jmcneill #include <arm/armreg.h>
     40  1.1  jmcneill #include <arm/locore.h>
     41  1.1  jmcneill 
     42  1.1  jmcneill #include <dev/tprof/tprof_armv7.h>
     43  1.1  jmcneill 
     44  1.5     skrll #define	PMCR_N			__BITS(15,11)
     45  1.1  jmcneill #define	PMCR_D			__BIT(3)
     46  1.1  jmcneill #define	PMCR_E			__BIT(0)
     47  1.1  jmcneill 
     48  1.8       ryo #define	PMINTEN_C		__BIT(31)
     49  1.8       ryo #define	PMINTEN_P		__BITS(30,0)
     50  1.8       ryo #define	PMCNTEN_C		__BIT(31)
     51  1.8       ryo #define	PMCNTEN_P		__BITS(30,0)
     52  1.8       ryo 
     53  1.1  jmcneill #define	PMEVTYPER_P		__BIT(31)
     54  1.1  jmcneill #define	PMEVTYPER_U		__BIT(30)
     55  1.1  jmcneill #define	PMEVTYPER_EVTCOUNT	__BITS(7,0)
     56  1.1  jmcneill 
     57  1.1  jmcneill static tprof_param_t armv7_pmu_param;
     58  1.1  jmcneill static const u_int armv7_pmu_counter = 1;
     59  1.1  jmcneill static uint32_t counter_val;
     60  1.1  jmcneill static uint32_t counter_reset_val;
     61  1.1  jmcneill 
     62  1.7  jmcneill static uint16_t cortexa9_events[] = {
     63  1.7  jmcneill 	0x40, 0x41, 0x42,
     64  1.7  jmcneill 	0x50, 0x51,
     65  1.7  jmcneill 	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
     66  1.7  jmcneill 	0x6e,
     67  1.7  jmcneill 	0x70, 0x71, 0x72, 0x73, 0x74,
     68  1.7  jmcneill 	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
     69  1.7  jmcneill 	0x8a, 0x8b,
     70  1.7  jmcneill 	0x90, 0x91, 0x92, 0x93,
     71  1.7  jmcneill 	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5
     72  1.7  jmcneill };
     73  1.7  jmcneill 
     74  1.1  jmcneill static bool
     75  1.1  jmcneill armv7_pmu_event_implemented(uint16_t event)
     76  1.1  jmcneill {
     77  1.7  jmcneill 	if (CPU_ID_CORTEX_A9_P(curcpu()->ci_midr)) {
     78  1.7  jmcneill 		/* Cortex-A9 with PMUv1 lacks PMCEID0/1 */
     79  1.7  jmcneill 		u_int n;
     80  1.7  jmcneill 
     81  1.7  jmcneill 		/* Events specific to the Cortex-A9 */
     82  1.7  jmcneill 		for (n = 0; n < __arraycount(cortexa9_events); n++) {
     83  1.7  jmcneill 			if (cortexa9_events[n] == event) {
     84  1.7  jmcneill 				return true;
     85  1.7  jmcneill 			}
     86  1.7  jmcneill 		}
     87  1.7  jmcneill 		/* Supported architectural events */
     88  1.7  jmcneill 		if (event != 0x08 && event != 0x0e && event < 0x1e) {
     89  1.7  jmcneill 			return true;
     90  1.7  jmcneill 		}
     91  1.7  jmcneill 	} else {
     92  1.7  jmcneill 		/* PMUv2 */
     93  1.7  jmcneill 		uint32_t eid[2];
     94  1.7  jmcneill 
     95  1.7  jmcneill 		if (event >= 64) {
     96  1.7  jmcneill 			return false;
     97  1.7  jmcneill 		}
     98  1.7  jmcneill 
     99  1.7  jmcneill 		eid[0] = armreg_pmceid0_read();
    100  1.7  jmcneill 		eid[1] = armreg_pmceid1_read();
    101  1.7  jmcneill 
    102  1.7  jmcneill 		const u_int idx = event / 32;
    103  1.7  jmcneill 		const u_int bit = event % 32;
    104  1.7  jmcneill 
    105  1.7  jmcneill 		if (eid[idx] & __BIT(bit)) {
    106  1.7  jmcneill 			return true;
    107  1.7  jmcneill 		}
    108  1.7  jmcneill 	}
    109  1.1  jmcneill 
    110  1.1  jmcneill 	return false;
    111  1.1  jmcneill }
    112  1.1  jmcneill 
    113  1.1  jmcneill static void
    114  1.1  jmcneill armv7_pmu_set_pmevtyper(u_int counter, uint64_t val)
    115  1.1  jmcneill {
    116  1.1  jmcneill 	armreg_pmselr_write(counter);
    117  1.4     skrll 	isb();
    118  1.1  jmcneill 	armreg_pmxevtyper_write(val);
    119  1.1  jmcneill }
    120  1.1  jmcneill 
    121  1.1  jmcneill static void
    122  1.1  jmcneill armv7_pmu_set_pmevcntr(u_int counter, uint32_t val)
    123  1.1  jmcneill {
    124  1.1  jmcneill 	armreg_pmselr_write(counter);
    125  1.4     skrll 	isb();
    126  1.1  jmcneill 	armreg_pmxevcntr_write(val);
    127  1.1  jmcneill }
    128  1.1  jmcneill 
    129  1.1  jmcneill static void
    130  1.1  jmcneill armv7_pmu_start_cpu(void *arg1, void *arg2)
    131  1.1  jmcneill {
    132  1.1  jmcneill 	const uint32_t counter_mask = __BIT(armv7_pmu_counter);
    133  1.1  jmcneill 	uint64_t pmcr, pmevtyper;
    134  1.1  jmcneill 
    135  1.1  jmcneill 	/* Enable performance monitor */
    136  1.1  jmcneill 	pmcr = armreg_pmcr_read();
    137  1.1  jmcneill 	pmcr |= PMCR_E;
    138  1.1  jmcneill 	armreg_pmcr_write(pmcr);
    139  1.1  jmcneill 
    140  1.1  jmcneill 	/* Disable event counter */
    141  1.1  jmcneill 	armreg_pmcntenclr_write(counter_mask);
    142  1.1  jmcneill 
    143  1.1  jmcneill 	/* Configure event counter */
    144  1.1  jmcneill 	pmevtyper = __SHIFTIN(armv7_pmu_param.p_event, PMEVTYPER_EVTCOUNT);
    145  1.1  jmcneill 	if (!ISSET(armv7_pmu_param.p_flags, TPROF_PARAM_USER))
    146  1.1  jmcneill 		pmevtyper |= PMEVTYPER_U;
    147  1.1  jmcneill 	if (!ISSET(armv7_pmu_param.p_flags, TPROF_PARAM_KERN))
    148  1.1  jmcneill 		pmevtyper |= PMEVTYPER_P;
    149  1.1  jmcneill 
    150  1.1  jmcneill 	armv7_pmu_set_pmevtyper(armv7_pmu_counter, pmevtyper);
    151  1.1  jmcneill 
    152  1.1  jmcneill 	/* Enable overflow interrupts */
    153  1.1  jmcneill 	armreg_pmintenset_write(counter_mask);
    154  1.1  jmcneill 
    155  1.1  jmcneill 	/* Clear overflow flag */
    156  1.1  jmcneill 	armreg_pmovsr_write(counter_mask);
    157  1.1  jmcneill 
    158  1.1  jmcneill 	/* Initialize event counter value */
    159  1.1  jmcneill 	armv7_pmu_set_pmevcntr(armv7_pmu_counter, counter_reset_val);
    160  1.1  jmcneill 
    161  1.1  jmcneill 	/* Enable event counter */
    162  1.1  jmcneill 	armreg_pmcntenset_write(counter_mask);
    163  1.1  jmcneill }
    164  1.1  jmcneill 
    165  1.1  jmcneill static void
    166  1.1  jmcneill armv7_pmu_stop_cpu(void *arg1, void *arg2)
    167  1.1  jmcneill {
    168  1.1  jmcneill 	const uint32_t counter_mask = __BIT(armv7_pmu_counter);
    169  1.1  jmcneill 
    170  1.1  jmcneill 	/* Disable overflow interrupts */
    171  1.1  jmcneill 	armreg_pmintenclr_write(counter_mask);
    172  1.1  jmcneill 
    173  1.1  jmcneill 	/* Disable event counter */
    174  1.1  jmcneill 	armreg_pmcntenclr_write(counter_mask);
    175  1.1  jmcneill }
    176  1.1  jmcneill 
    177  1.1  jmcneill static uint64_t
    178  1.1  jmcneill armv7_pmu_estimate_freq(void)
    179  1.1  jmcneill {
    180  1.1  jmcneill 	uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq;
    181  1.1  jmcneill 	uint64_t freq = 10000;
    182  1.1  jmcneill 	uint32_t pmcr;
    183  1.1  jmcneill 
    184  1.1  jmcneill 	counter_val = cpufreq / freq;
    185  1.1  jmcneill 	if (counter_val == 0)
    186  1.1  jmcneill 		counter_val = 4000000000ULL / freq;
    187  1.1  jmcneill 
    188  1.1  jmcneill 	pmcr = armreg_pmcr_read();
    189  1.1  jmcneill 	if (pmcr & PMCR_D)
    190  1.1  jmcneill 		counter_val /= 64;
    191  1.1  jmcneill 
    192  1.1  jmcneill 	return freq;
    193  1.1  jmcneill }
    194  1.1  jmcneill 
    195  1.1  jmcneill static uint32_t
    196  1.1  jmcneill armv7_pmu_ident(void)
    197  1.1  jmcneill {
    198  1.1  jmcneill 	return TPROF_IDENT_ARMV7_GENERIC;
    199  1.1  jmcneill }
    200  1.1  jmcneill 
    201  1.1  jmcneill static int
    202  1.1  jmcneill armv7_pmu_start(const tprof_param_t *param)
    203  1.1  jmcneill {
    204  1.5     skrll 	/* PMCR.N of 0 means that no event counters are available */
    205  1.5     skrll 	if (__SHIFTOUT(armreg_pmcr_read(), PMCR_N) == 0) {
    206  1.5     skrll 		return EINVAL;
    207  1.5     skrll 	}
    208  1.1  jmcneill 
    209  1.1  jmcneill 	if (!armv7_pmu_event_implemented(param->p_event)) {
    210  1.3       rin 		printf("%s: event %#llx not implemented on this CPU\n",
    211  1.1  jmcneill 		    __func__, param->p_event);
    212  1.1  jmcneill 		return EINVAL;
    213  1.1  jmcneill 	}
    214  1.1  jmcneill 
    215  1.1  jmcneill 	counter_reset_val = -counter_val + 1;
    216  1.1  jmcneill 
    217  1.1  jmcneill 	armv7_pmu_param = *param;
    218  1.6  christos 	uint64_t xc = xc_broadcast(0, armv7_pmu_start_cpu, NULL, NULL);
    219  1.1  jmcneill 	xc_wait(xc);
    220  1.1  jmcneill 
    221  1.1  jmcneill 	return 0;
    222  1.1  jmcneill }
    223  1.1  jmcneill 
    224  1.1  jmcneill static void
    225  1.1  jmcneill armv7_pmu_stop(const tprof_param_t *param)
    226  1.1  jmcneill {
    227  1.1  jmcneill 	uint64_t xc;
    228  1.1  jmcneill 
    229  1.1  jmcneill 	xc = xc_broadcast(0, armv7_pmu_stop_cpu, NULL, NULL);
    230  1.1  jmcneill 	xc_wait(xc);
    231  1.1  jmcneill }
    232  1.1  jmcneill 
    233  1.1  jmcneill static const tprof_backend_ops_t tprof_armv7_pmu_ops = {
    234  1.1  jmcneill 	.tbo_estimate_freq = armv7_pmu_estimate_freq,
    235  1.1  jmcneill 	.tbo_ident = armv7_pmu_ident,
    236  1.1  jmcneill 	.tbo_start = armv7_pmu_start,
    237  1.1  jmcneill 	.tbo_stop = armv7_pmu_stop,
    238  1.1  jmcneill };
    239  1.1  jmcneill 
    240  1.1  jmcneill int
    241  1.1  jmcneill armv7_pmu_intr(void *priv)
    242  1.1  jmcneill {
    243  1.1  jmcneill 	const struct trapframe * const tf = priv;
    244  1.1  jmcneill 	const uint32_t counter_mask = __BIT(armv7_pmu_counter);
    245  1.1  jmcneill 	tprof_frame_info_t tfi;
    246  1.1  jmcneill 
    247  1.1  jmcneill 	const uint32_t pmovsr = armreg_pmovsr_read();
    248  1.1  jmcneill 	if ((pmovsr & counter_mask) != 0) {
    249  1.1  jmcneill 		tfi.tfi_pc = tf->tf_pc;
    250  1.1  jmcneill 		tfi.tfi_inkernel = tfi.tfi_pc >= VM_MIN_KERNEL_ADDRESS &&
    251  1.1  jmcneill 		    tfi.tfi_pc < VM_MAX_KERNEL_ADDRESS;
    252  1.1  jmcneill 		tprof_sample(NULL, &tfi);
    253  1.1  jmcneill 
    254  1.1  jmcneill 		armv7_pmu_set_pmevcntr(armv7_pmu_counter, counter_reset_val);
    255  1.1  jmcneill 	}
    256  1.1  jmcneill 	armreg_pmovsr_write(pmovsr);
    257  1.1  jmcneill 
    258  1.1  jmcneill 	return 1;
    259  1.1  jmcneill }
    260  1.1  jmcneill 
    261  1.9       ryo static void
    262  1.9       ryo armv7_pmu_init_cpu(void *arg1, void *arg2)
    263  1.1  jmcneill {
    264  1.2  jmcneill 	/* Disable user mode access to performance monitors */
    265  1.2  jmcneill 	armreg_pmuserenr_write(0);
    266  1.2  jmcneill 
    267  1.2  jmcneill 	/* Disable interrupts */
    268  1.8       ryo 	armreg_pmintenclr_write(PMINTEN_P);
    269  1.2  jmcneill 
    270  1.2  jmcneill 	/* Disable counters */
    271  1.8       ryo 	armreg_pmcntenclr_write(PMCNTEN_P);
    272  1.9       ryo }
    273  1.9       ryo 
    274  1.9       ryo int
    275  1.9       ryo armv7_pmu_init(void)
    276  1.9       ryo {
    277  1.9       ryo 	uint64_t xc = xc_broadcast(0, armv7_pmu_init_cpu, NULL, NULL);
    278  1.9       ryo 	xc_wait(xc);
    279  1.2  jmcneill 
    280  1.1  jmcneill 	return tprof_backend_register("tprof_armv7", &tprof_armv7_pmu_ops,
    281  1.1  jmcneill 	    TPROF_BACKEND_VERSION);
    282  1.1  jmcneill }
    283