Home | History | Annotate | Line # | Download | only in tprof
      1  1.13   msaitoh /* $NetBSD: tprof_armv7.c,v 1.13 2023/04/11 10:07:12 msaitoh 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.13   msaitoh __KERNEL_RCSID(0, "$NetBSD: tprof_armv7.c,v 1.13 2023/04/11 10:07:12 msaitoh 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.10       ryo #include <sys/percpu.h>
     36   1.1  jmcneill #include <sys/xcall.h>
     37   1.1  jmcneill 
     38   1.1  jmcneill #include <dev/tprof/tprof.h>
     39   1.1  jmcneill 
     40   1.1  jmcneill #include <arm/armreg.h>
     41   1.1  jmcneill #include <arm/locore.h>
     42   1.1  jmcneill 
     43   1.1  jmcneill #include <dev/tprof/tprof_armv7.h>
     44   1.1  jmcneill 
     45   1.7  jmcneill static uint16_t cortexa9_events[] = {
     46   1.7  jmcneill 	0x40, 0x41, 0x42,
     47   1.7  jmcneill 	0x50, 0x51,
     48   1.7  jmcneill 	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
     49   1.7  jmcneill 	0x6e,
     50   1.7  jmcneill 	0x70, 0x71, 0x72, 0x73, 0x74,
     51   1.7  jmcneill 	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
     52   1.7  jmcneill 	0x8a, 0x8b,
     53   1.7  jmcneill 	0x90, 0x91, 0x92, 0x93,
     54   1.7  jmcneill 	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5
     55   1.7  jmcneill };
     56   1.7  jmcneill 
     57   1.1  jmcneill static bool
     58   1.1  jmcneill armv7_pmu_event_implemented(uint16_t event)
     59   1.1  jmcneill {
     60   1.7  jmcneill 	if (CPU_ID_CORTEX_A9_P(curcpu()->ci_midr)) {
     61   1.7  jmcneill 		/* Cortex-A9 with PMUv1 lacks PMCEID0/1 */
     62   1.7  jmcneill 		u_int n;
     63   1.7  jmcneill 
     64   1.7  jmcneill 		/* Events specific to the Cortex-A9 */
     65   1.7  jmcneill 		for (n = 0; n < __arraycount(cortexa9_events); n++) {
     66   1.7  jmcneill 			if (cortexa9_events[n] == event) {
     67   1.7  jmcneill 				return true;
     68   1.7  jmcneill 			}
     69   1.7  jmcneill 		}
     70   1.7  jmcneill 		/* Supported architectural events */
     71   1.7  jmcneill 		if (event != 0x08 && event != 0x0e && event < 0x1e) {
     72   1.7  jmcneill 			return true;
     73   1.7  jmcneill 		}
     74   1.7  jmcneill 	} else {
     75   1.7  jmcneill 		/* PMUv2 */
     76   1.7  jmcneill 		uint32_t eid[2];
     77   1.7  jmcneill 
     78   1.7  jmcneill 		if (event >= 64) {
     79   1.7  jmcneill 			return false;
     80   1.7  jmcneill 		}
     81   1.7  jmcneill 
     82   1.7  jmcneill 		eid[0] = armreg_pmceid0_read();
     83   1.7  jmcneill 		eid[1] = armreg_pmceid1_read();
     84   1.7  jmcneill 
     85   1.7  jmcneill 		const u_int idx = event / 32;
     86   1.7  jmcneill 		const u_int bit = event % 32;
     87   1.7  jmcneill 
     88   1.7  jmcneill 		if (eid[idx] & __BIT(bit)) {
     89   1.7  jmcneill 			return true;
     90   1.7  jmcneill 		}
     91   1.7  jmcneill 	}
     92   1.1  jmcneill 
     93   1.1  jmcneill 	return false;
     94   1.1  jmcneill }
     95   1.1  jmcneill 
     96   1.1  jmcneill static void
     97   1.1  jmcneill armv7_pmu_set_pmevtyper(u_int counter, uint64_t val)
     98   1.1  jmcneill {
     99   1.1  jmcneill 	armreg_pmselr_write(counter);
    100   1.4     skrll 	isb();
    101   1.1  jmcneill 	armreg_pmxevtyper_write(val);
    102   1.1  jmcneill }
    103   1.1  jmcneill 
    104  1.10       ryo static inline void
    105   1.1  jmcneill armv7_pmu_set_pmevcntr(u_int counter, uint32_t val)
    106   1.1  jmcneill {
    107   1.1  jmcneill 	armreg_pmselr_write(counter);
    108   1.4     skrll 	isb();
    109   1.1  jmcneill 	armreg_pmxevcntr_write(val);
    110   1.1  jmcneill }
    111   1.1  jmcneill 
    112  1.10       ryo static inline uint64_t
    113  1.10       ryo armv7_pmu_get_pmevcntr(u_int counter)
    114   1.1  jmcneill {
    115  1.10       ryo 	armreg_pmselr_write(counter);
    116  1.10       ryo 	isb();
    117  1.10       ryo 	return armreg_pmxevcntr_read();
    118  1.10       ryo }
    119   1.1  jmcneill 
    120  1.13   msaitoh /* Read and write at once */
    121  1.10       ryo static inline uint64_t
    122  1.10       ryo armv7_pmu_getset_pmevcntr(u_int counter, uint64_t val)
    123  1.10       ryo {
    124  1.10       ryo 	uint64_t c;
    125   1.1  jmcneill 
    126  1.10       ryo 	armreg_pmselr_write(counter);
    127  1.10       ryo 	isb();
    128  1.10       ryo 	c = armreg_pmxevcntr_read();
    129  1.10       ryo 	armreg_pmxevcntr_write(val);
    130  1.10       ryo 	return c;
    131  1.10       ryo }
    132   1.1  jmcneill 
    133  1.10       ryo static uint32_t
    134  1.10       ryo armv7_pmu_ncounters(void)
    135  1.10       ryo {
    136  1.10       ryo 	return __SHIFTOUT(armreg_pmcr_read(), PMCR_N);
    137   1.1  jmcneill }
    138   1.1  jmcneill 
    139  1.10       ryo static u_int
    140  1.10       ryo armv7_pmu_counter_bitwidth(u_int counter)
    141   1.1  jmcneill {
    142  1.10       ryo 	return 32;
    143   1.1  jmcneill }
    144   1.1  jmcneill 
    145   1.1  jmcneill static uint64_t
    146  1.10       ryo armv7_pmu_counter_estimate_freq(u_int counter)
    147   1.1  jmcneill {
    148   1.1  jmcneill 	uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq;
    149   1.1  jmcneill 
    150  1.10       ryo 	if (ISSET(armreg_pmcr_read(), PMCR_D))
    151  1.10       ryo 		cpufreq /= 64;
    152  1.10       ryo 	return cpufreq;
    153   1.1  jmcneill }
    154   1.1  jmcneill 
    155   1.1  jmcneill static int
    156  1.10       ryo armv7_pmu_valid_event(u_int counter, const tprof_param_t *param)
    157   1.1  jmcneill {
    158   1.1  jmcneill 	if (!armv7_pmu_event_implemented(param->p_event)) {
    159  1.10       ryo 		printf("%s: event %#" PRIx64 " not implemented on this CPU\n",
    160   1.1  jmcneill 		    __func__, param->p_event);
    161   1.1  jmcneill 		return EINVAL;
    162   1.1  jmcneill 	}
    163  1.10       ryo 	return 0;
    164  1.10       ryo }
    165   1.1  jmcneill 
    166  1.10       ryo static void
    167  1.10       ryo armv7_pmu_configure_event(u_int counter, const tprof_param_t *param)
    168  1.10       ryo {
    169  1.10       ryo 	/* Disable event counter */
    170  1.10       ryo 	armreg_pmcntenclr_write(__BIT(counter) & PMCNTEN_P);
    171  1.10       ryo 
    172  1.10       ryo 	/* Disable overflow interrupts */
    173  1.10       ryo 	armreg_pmintenclr_write(__BIT(counter) & PMINTEN_P);
    174   1.1  jmcneill 
    175  1.10       ryo 	/* Configure event counter */
    176  1.10       ryo 	uint32_t pmevtyper = __SHIFTIN(param->p_event, PMEVTYPER_EVTCOUNT);
    177  1.10       ryo 	if (!ISSET(param->p_flags, TPROF_PARAM_USER))
    178  1.10       ryo 		pmevtyper |= PMEVTYPER_U;
    179  1.10       ryo 	if (!ISSET(param->p_flags, TPROF_PARAM_KERN))
    180  1.10       ryo 		pmevtyper |= PMEVTYPER_P;
    181  1.10       ryo 	armv7_pmu_set_pmevtyper(counter, pmevtyper);
    182  1.10       ryo 
    183  1.10       ryo 	/*
    184  1.10       ryo 	 * Enable overflow interrupts.
    185  1.10       ryo 	 * Whether profiled or not, the counter width of armv7 is 32 bits,
    186  1.10       ryo 	 * so overflow handling is required anyway.
    187  1.10       ryo 	 */
    188  1.10       ryo 	armreg_pmintenset_write(__BIT(counter) & PMINTEN_P);
    189  1.10       ryo 
    190  1.10       ryo 	/* Clear overflow flag */
    191  1.10       ryo 	armreg_pmovsr_write(__BIT(counter) & PMOVS_P);
    192   1.1  jmcneill 
    193  1.13   msaitoh 	/* Reset the counter */
    194  1.10       ryo 	armv7_pmu_set_pmevcntr(counter, param->p_value);
    195   1.1  jmcneill }
    196   1.1  jmcneill 
    197   1.1  jmcneill static void
    198  1.10       ryo armv7_pmu_start(tprof_countermask_t runmask)
    199   1.1  jmcneill {
    200  1.10       ryo 	/* Enable event counters */
    201  1.10       ryo 	armreg_pmcntenset_write(runmask & PMCNTEN_P);
    202  1.10       ryo 
    203  1.10       ryo 	/*
    204  1.10       ryo 	 * PMCR.E is shared with PMCCNTR and event counters.
    205  1.10       ryo 	 * It is set here in case PMCCNTR is not used in the system.
    206  1.10       ryo 	 */
    207  1.10       ryo 	armreg_pmcr_write(armreg_pmcr_read() | PMCR_E);
    208  1.10       ryo }
    209   1.1  jmcneill 
    210  1.10       ryo static void
    211  1.10       ryo armv7_pmu_stop(tprof_countermask_t stopmask)
    212  1.10       ryo {
    213  1.10       ryo 	/* Disable event counter */
    214  1.10       ryo 	armreg_pmcntenclr_write(stopmask & PMCNTEN_P);
    215   1.1  jmcneill }
    216   1.1  jmcneill 
    217  1.10       ryo /* XXX: argument of armv8_pmu_intr() */
    218  1.10       ryo extern struct tprof_backend *tprof_backend;
    219  1.10       ryo static void *pmu_intr_arg;
    220   1.1  jmcneill 
    221   1.1  jmcneill int
    222   1.1  jmcneill armv7_pmu_intr(void *priv)
    223   1.1  jmcneill {
    224   1.1  jmcneill 	const struct trapframe * const tf = priv;
    225  1.10       ryo 	tprof_backend_softc_t *sc = pmu_intr_arg;
    226   1.1  jmcneill 	tprof_frame_info_t tfi;
    227  1.10       ryo 	int bit;
    228  1.12       ryo 	const uint32_t pmovs = armreg_pmovsr_read();
    229   1.1  jmcneill 
    230  1.10       ryo 	uint64_t *counters_offset =
    231  1.10       ryo 	    percpu_getptr_remote(sc->sc_ctr_offset_percpu, curcpu());
    232  1.10       ryo 	uint32_t mask = pmovs;
    233  1.10       ryo 	while ((bit = ffs(mask)) != 0) {
    234  1.10       ryo 		bit--;
    235  1.10       ryo 		CLR(mask, __BIT(bit));
    236  1.10       ryo 
    237  1.10       ryo 		if (ISSET(sc->sc_ctr_prof_mask, __BIT(bit))) {
    238  1.13   msaitoh 			/* Account for the counter, and reset */
    239  1.10       ryo 			uint64_t ctr = armv7_pmu_getset_pmevcntr(bit,
    240  1.10       ryo 			    sc->sc_count[bit].ctr_counter_reset_val);
    241  1.10       ryo 			counters_offset[bit] +=
    242  1.10       ryo 			    sc->sc_count[bit].ctr_counter_val + ctr;
    243  1.10       ryo 
    244  1.13   msaitoh 			/* Record a sample */
    245  1.10       ryo 			tfi.tfi_pc = tf->tf_pc;
    246  1.10       ryo 			tfi.tfi_counter = bit;
    247  1.10       ryo 			tfi.tfi_inkernel =
    248  1.10       ryo 			    tfi.tfi_pc >= VM_MIN_KERNEL_ADDRESS &&
    249  1.10       ryo 			    tfi.tfi_pc < VM_MAX_KERNEL_ADDRESS;
    250  1.10       ryo 			tprof_sample(NULL, &tfi);
    251  1.12       ryo 		} else if (ISSET(sc->sc_ctr_ovf_mask, __BIT(bit))) {
    252  1.13   msaitoh 			/* Counter has overflowed */
    253  1.10       ryo 			counters_offset[bit] += __BIT(32);
    254  1.10       ryo 		}
    255   1.1  jmcneill 	}
    256  1.10       ryo 	armreg_pmovsr_write(pmovs);
    257   1.1  jmcneill 
    258   1.1  jmcneill 	return 1;
    259   1.1  jmcneill }
    260   1.1  jmcneill 
    261  1.10       ryo static uint32_t
    262  1.10       ryo armv7_pmu_ident(void)
    263  1.10       ryo {
    264  1.10       ryo 	return TPROF_IDENT_ARMV7_GENERIC;
    265  1.10       ryo }
    266  1.10       ryo 
    267  1.10       ryo static const tprof_backend_ops_t tprof_armv7_pmu_ops = {
    268  1.10       ryo 	.tbo_ident = armv7_pmu_ident,
    269  1.10       ryo 	.tbo_ncounters = armv7_pmu_ncounters,
    270  1.10       ryo 	.tbo_counter_bitwidth = armv7_pmu_counter_bitwidth,
    271  1.10       ryo 	.tbo_counter_read = armv7_pmu_get_pmevcntr,
    272  1.10       ryo 	.tbo_counter_estimate_freq = armv7_pmu_counter_estimate_freq,
    273  1.10       ryo 	.tbo_valid_event = armv7_pmu_valid_event,
    274  1.10       ryo 	.tbo_configure_event = armv7_pmu_configure_event,
    275  1.10       ryo 	.tbo_start = armv7_pmu_start,
    276  1.10       ryo 	.tbo_stop = armv7_pmu_stop,
    277  1.10       ryo 	.tbo_establish = NULL,
    278  1.10       ryo 	.tbo_disestablish = NULL,
    279  1.10       ryo };
    280  1.10       ryo 
    281   1.9       ryo static void
    282   1.9       ryo armv7_pmu_init_cpu(void *arg1, void *arg2)
    283   1.1  jmcneill {
    284   1.2  jmcneill 	/* Disable user mode access to performance monitors */
    285   1.2  jmcneill 	armreg_pmuserenr_write(0);
    286   1.2  jmcneill 
    287   1.2  jmcneill 	/* Disable interrupts */
    288   1.8       ryo 	armreg_pmintenclr_write(PMINTEN_P);
    289   1.2  jmcneill 
    290   1.2  jmcneill 	/* Disable counters */
    291   1.8       ryo 	armreg_pmcntenclr_write(PMCNTEN_P);
    292   1.9       ryo }
    293   1.9       ryo 
    294   1.9       ryo int
    295   1.9       ryo armv7_pmu_init(void)
    296   1.9       ryo {
    297  1.10       ryo 	int error, ncounters;
    298  1.10       ryo 
    299  1.10       ryo 	ncounters = armv7_pmu_ncounters();
    300  1.10       ryo 	if (ncounters == 0)
    301  1.10       ryo 		return ENOTSUP;
    302  1.10       ryo 
    303   1.9       ryo 	uint64_t xc = xc_broadcast(0, armv7_pmu_init_cpu, NULL, NULL);
    304   1.9       ryo 	xc_wait(xc);
    305   1.2  jmcneill 
    306  1.10       ryo 	error = tprof_backend_register("tprof_armv7", &tprof_armv7_pmu_ops,
    307   1.1  jmcneill 	    TPROF_BACKEND_VERSION);
    308  1.10       ryo 	if (error == 0) {
    309  1.10       ryo 		/* XXX: for argument of armv7_pmu_intr() */
    310  1.10       ryo 		pmu_intr_arg = tprof_backend;
    311  1.10       ryo 	}
    312  1.10       ryo 
    313  1.10       ryo 	return error;
    314   1.1  jmcneill }
    315