Home | History | Annotate | Line # | Download | only in tprof
tprof_armv7.c revision 1.7
      1  1.7  jmcneill /* $NetBSD: tprof_armv7.c,v 1.7 2022/11/01 11:03:01 jmcneill 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.7  jmcneill __KERNEL_RCSID(0, "$NetBSD: tprof_armv7.c,v 1.7 2022/11/01 11:03:01 jmcneill 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.1  jmcneill #define	PMEVTYPER_P		__BIT(31)
     49  1.1  jmcneill #define	PMEVTYPER_U		__BIT(30)
     50  1.1  jmcneill #define	PMEVTYPER_EVTCOUNT	__BITS(7,0)
     51  1.1  jmcneill 
     52  1.1  jmcneill static tprof_param_t armv7_pmu_param;
     53  1.1  jmcneill static const u_int armv7_pmu_counter = 1;
     54  1.1  jmcneill static uint32_t counter_val;
     55  1.1  jmcneill static uint32_t counter_reset_val;
     56  1.1  jmcneill 
     57  1.7  jmcneill static uint16_t cortexa9_events[] = {
     58  1.7  jmcneill 	0x40, 0x41, 0x42,
     59  1.7  jmcneill 	0x50, 0x51,
     60  1.7  jmcneill 	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
     61  1.7  jmcneill 	0x6e,
     62  1.7  jmcneill 	0x70, 0x71, 0x72, 0x73, 0x74,
     63  1.7  jmcneill 	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
     64  1.7  jmcneill 	0x8a, 0x8b,
     65  1.7  jmcneill 	0x90, 0x91, 0x92, 0x93,
     66  1.7  jmcneill 	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5
     67  1.7  jmcneill };
     68  1.7  jmcneill 
     69  1.1  jmcneill static bool
     70  1.1  jmcneill armv7_pmu_event_implemented(uint16_t event)
     71  1.1  jmcneill {
     72  1.7  jmcneill 	if (CPU_ID_CORTEX_A9_P(curcpu()->ci_midr)) {
     73  1.7  jmcneill 		/* Cortex-A9 with PMUv1 lacks PMCEID0/1 */
     74  1.7  jmcneill 		u_int n;
     75  1.7  jmcneill 
     76  1.7  jmcneill 		/* Events specific to the Cortex-A9 */
     77  1.7  jmcneill 		for (n = 0; n < __arraycount(cortexa9_events); n++) {
     78  1.7  jmcneill 			if (cortexa9_events[n] == event) {
     79  1.7  jmcneill 				return true;
     80  1.7  jmcneill 			}
     81  1.7  jmcneill 		}
     82  1.7  jmcneill 		/* Supported architectural events */
     83  1.7  jmcneill 		if (event != 0x08 && event != 0x0e && event < 0x1e) {
     84  1.7  jmcneill 			return true;
     85  1.7  jmcneill 		}
     86  1.7  jmcneill 	} else {
     87  1.7  jmcneill 		/* PMUv2 */
     88  1.7  jmcneill 		uint32_t eid[2];
     89  1.7  jmcneill 
     90  1.7  jmcneill 		if (event >= 64) {
     91  1.7  jmcneill 			return false;
     92  1.7  jmcneill 		}
     93  1.7  jmcneill 
     94  1.7  jmcneill 		eid[0] = armreg_pmceid0_read();
     95  1.7  jmcneill 		eid[1] = armreg_pmceid1_read();
     96  1.7  jmcneill 
     97  1.7  jmcneill 		const u_int idx = event / 32;
     98  1.7  jmcneill 		const u_int bit = event % 32;
     99  1.7  jmcneill 
    100  1.7  jmcneill 		if (eid[idx] & __BIT(bit)) {
    101  1.7  jmcneill 			return true;
    102  1.7  jmcneill 		}
    103  1.7  jmcneill 	}
    104  1.1  jmcneill 
    105  1.1  jmcneill 	return false;
    106  1.1  jmcneill }
    107  1.1  jmcneill 
    108  1.1  jmcneill static void
    109  1.1  jmcneill armv7_pmu_set_pmevtyper(u_int counter, uint64_t val)
    110  1.1  jmcneill {
    111  1.1  jmcneill 	armreg_pmselr_write(counter);
    112  1.4     skrll 	isb();
    113  1.1  jmcneill 	armreg_pmxevtyper_write(val);
    114  1.1  jmcneill }
    115  1.1  jmcneill 
    116  1.1  jmcneill static void
    117  1.1  jmcneill armv7_pmu_set_pmevcntr(u_int counter, uint32_t val)
    118  1.1  jmcneill {
    119  1.1  jmcneill 	armreg_pmselr_write(counter);
    120  1.4     skrll 	isb();
    121  1.1  jmcneill 	armreg_pmxevcntr_write(val);
    122  1.1  jmcneill }
    123  1.1  jmcneill 
    124  1.1  jmcneill static void
    125  1.1  jmcneill armv7_pmu_start_cpu(void *arg1, void *arg2)
    126  1.1  jmcneill {
    127  1.1  jmcneill 	const uint32_t counter_mask = __BIT(armv7_pmu_counter);
    128  1.1  jmcneill 	uint64_t pmcr, pmevtyper;
    129  1.1  jmcneill 
    130  1.1  jmcneill 	/* Enable performance monitor */
    131  1.1  jmcneill 	pmcr = armreg_pmcr_read();
    132  1.1  jmcneill 	pmcr |= PMCR_E;
    133  1.1  jmcneill 	armreg_pmcr_write(pmcr);
    134  1.1  jmcneill 
    135  1.1  jmcneill 	/* Disable event counter */
    136  1.1  jmcneill 	armreg_pmcntenclr_write(counter_mask);
    137  1.1  jmcneill 
    138  1.1  jmcneill 	/* Configure event counter */
    139  1.1  jmcneill 	pmevtyper = __SHIFTIN(armv7_pmu_param.p_event, PMEVTYPER_EVTCOUNT);
    140  1.1  jmcneill 	if (!ISSET(armv7_pmu_param.p_flags, TPROF_PARAM_USER))
    141  1.1  jmcneill 		pmevtyper |= PMEVTYPER_U;
    142  1.1  jmcneill 	if (!ISSET(armv7_pmu_param.p_flags, TPROF_PARAM_KERN))
    143  1.1  jmcneill 		pmevtyper |= PMEVTYPER_P;
    144  1.1  jmcneill 
    145  1.1  jmcneill 	armv7_pmu_set_pmevtyper(armv7_pmu_counter, pmevtyper);
    146  1.1  jmcneill 
    147  1.1  jmcneill 	/* Enable overflow interrupts */
    148  1.1  jmcneill 	armreg_pmintenset_write(counter_mask);
    149  1.1  jmcneill 
    150  1.1  jmcneill 	/* Clear overflow flag */
    151  1.1  jmcneill 	armreg_pmovsr_write(counter_mask);
    152  1.1  jmcneill 
    153  1.1  jmcneill 	/* Initialize event counter value */
    154  1.1  jmcneill 	armv7_pmu_set_pmevcntr(armv7_pmu_counter, counter_reset_val);
    155  1.1  jmcneill 
    156  1.1  jmcneill 	/* Enable event counter */
    157  1.1  jmcneill 	armreg_pmcntenset_write(counter_mask);
    158  1.1  jmcneill }
    159  1.1  jmcneill 
    160  1.1  jmcneill static void
    161  1.1  jmcneill armv7_pmu_stop_cpu(void *arg1, void *arg2)
    162  1.1  jmcneill {
    163  1.1  jmcneill 	const uint32_t counter_mask = __BIT(armv7_pmu_counter);
    164  1.1  jmcneill 	uint32_t pmcr;
    165  1.1  jmcneill 
    166  1.1  jmcneill 	/* Disable overflow interrupts */
    167  1.1  jmcneill 	armreg_pmintenclr_write(counter_mask);
    168  1.1  jmcneill 
    169  1.1  jmcneill 	/* Disable event counter */
    170  1.1  jmcneill 	armreg_pmcntenclr_write(counter_mask);
    171  1.1  jmcneill 
    172  1.1  jmcneill 	/* Disable performance monitor */
    173  1.1  jmcneill 	pmcr = armreg_pmcr_read();
    174  1.1  jmcneill 	pmcr &= ~PMCR_E;
    175  1.1  jmcneill 	armreg_pmcr_write(pmcr);
    176  1.1  jmcneill }
    177  1.1  jmcneill 
    178  1.1  jmcneill static uint64_t
    179  1.1  jmcneill armv7_pmu_estimate_freq(void)
    180  1.1  jmcneill {
    181  1.1  jmcneill 	uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq;
    182  1.1  jmcneill 	uint64_t freq = 10000;
    183  1.1  jmcneill 	uint32_t pmcr;
    184  1.1  jmcneill 
    185  1.1  jmcneill 	counter_val = cpufreq / freq;
    186  1.1  jmcneill 	if (counter_val == 0)
    187  1.1  jmcneill 		counter_val = 4000000000ULL / freq;
    188  1.1  jmcneill 
    189  1.1  jmcneill 	pmcr = armreg_pmcr_read();
    190  1.1  jmcneill 	if (pmcr & PMCR_D)
    191  1.1  jmcneill 		counter_val /= 64;
    192  1.1  jmcneill 
    193  1.1  jmcneill 	return freq;
    194  1.1  jmcneill }
    195  1.1  jmcneill 
    196  1.1  jmcneill static uint32_t
    197  1.1  jmcneill armv7_pmu_ident(void)
    198  1.1  jmcneill {
    199  1.1  jmcneill 	return TPROF_IDENT_ARMV7_GENERIC;
    200  1.1  jmcneill }
    201  1.1  jmcneill 
    202  1.1  jmcneill static int
    203  1.1  jmcneill armv7_pmu_start(const tprof_param_t *param)
    204  1.1  jmcneill {
    205  1.5     skrll 	/* PMCR.N of 0 means that no event counters are available */
    206  1.5     skrll 	if (__SHIFTOUT(armreg_pmcr_read(), PMCR_N) == 0) {
    207  1.5     skrll 		return EINVAL;
    208  1.5     skrll 	}
    209  1.1  jmcneill 
    210  1.1  jmcneill 	if (!armv7_pmu_event_implemented(param->p_event)) {
    211  1.3       rin 		printf("%s: event %#llx not implemented on this CPU\n",
    212  1.1  jmcneill 		    __func__, param->p_event);
    213  1.1  jmcneill 		return EINVAL;
    214  1.1  jmcneill 	}
    215  1.1  jmcneill 
    216  1.1  jmcneill 	counter_reset_val = -counter_val + 1;
    217  1.1  jmcneill 
    218  1.1  jmcneill 	armv7_pmu_param = *param;
    219  1.6  christos 	uint64_t xc = xc_broadcast(0, armv7_pmu_start_cpu, NULL, NULL);
    220  1.1  jmcneill 	xc_wait(xc);
    221  1.1  jmcneill 
    222  1.1  jmcneill 	return 0;
    223  1.1  jmcneill }
    224  1.1  jmcneill 
    225  1.1  jmcneill static void
    226  1.1  jmcneill armv7_pmu_stop(const tprof_param_t *param)
    227  1.1  jmcneill {
    228  1.1  jmcneill 	uint64_t xc;
    229  1.1  jmcneill 
    230  1.1  jmcneill 	xc = xc_broadcast(0, armv7_pmu_stop_cpu, NULL, NULL);
    231  1.1  jmcneill 	xc_wait(xc);
    232  1.1  jmcneill }
    233  1.1  jmcneill 
    234  1.1  jmcneill static const tprof_backend_ops_t tprof_armv7_pmu_ops = {
    235  1.1  jmcneill 	.tbo_estimate_freq = armv7_pmu_estimate_freq,
    236  1.1  jmcneill 	.tbo_ident = armv7_pmu_ident,
    237  1.1  jmcneill 	.tbo_start = armv7_pmu_start,
    238  1.1  jmcneill 	.tbo_stop = armv7_pmu_stop,
    239  1.1  jmcneill };
    240  1.1  jmcneill 
    241  1.1  jmcneill int
    242  1.1  jmcneill armv7_pmu_intr(void *priv)
    243  1.1  jmcneill {
    244  1.1  jmcneill 	const struct trapframe * const tf = priv;
    245  1.1  jmcneill 	const uint32_t counter_mask = __BIT(armv7_pmu_counter);
    246  1.1  jmcneill 	tprof_frame_info_t tfi;
    247  1.1  jmcneill 
    248  1.1  jmcneill 	const uint32_t pmovsr = armreg_pmovsr_read();
    249  1.1  jmcneill 	if ((pmovsr & counter_mask) != 0) {
    250  1.1  jmcneill 		tfi.tfi_pc = tf->tf_pc;
    251  1.1  jmcneill 		tfi.tfi_inkernel = tfi.tfi_pc >= VM_MIN_KERNEL_ADDRESS &&
    252  1.1  jmcneill 		    tfi.tfi_pc < VM_MAX_KERNEL_ADDRESS;
    253  1.1  jmcneill 		tprof_sample(NULL, &tfi);
    254  1.1  jmcneill 
    255  1.1  jmcneill 		armv7_pmu_set_pmevcntr(armv7_pmu_counter, counter_reset_val);
    256  1.1  jmcneill 	}
    257  1.1  jmcneill 	armreg_pmovsr_write(pmovsr);
    258  1.1  jmcneill 
    259  1.1  jmcneill 	return 1;
    260  1.1  jmcneill }
    261  1.1  jmcneill 
    262  1.1  jmcneill int
    263  1.1  jmcneill armv7_pmu_init(void)
    264  1.1  jmcneill {
    265  1.2  jmcneill 	/* Disable user mode access to performance monitors */
    266  1.2  jmcneill 	armreg_pmuserenr_write(0);
    267  1.2  jmcneill 
    268  1.2  jmcneill 	/* Disable interrupts */
    269  1.2  jmcneill 	armreg_pmintenclr_write(~0U);
    270  1.2  jmcneill 
    271  1.2  jmcneill 	/* Disable counters */
    272  1.2  jmcneill 	armreg_pmcntenclr_write(~0U);
    273  1.2  jmcneill 
    274  1.2  jmcneill 	/* Disable performance monitor */
    275  1.2  jmcneill 	armreg_pmcr_write(0);
    276  1.2  jmcneill 
    277  1.1  jmcneill 	return tprof_backend_register("tprof_armv7", &tprof_armv7_pmu_ops,
    278  1.1  jmcneill 	    TPROF_BACKEND_VERSION);
    279  1.1  jmcneill }
    280