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