Home | History | Annotate | Line # | Download | only in tprof
tprof_armv8.c revision 1.1
      1 /* $NetBSD: tprof_armv8.c,v 1.1 2018/07/15 16:05:24 jmcneill 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_armv8.c,v 1.1 2018/07/15 16:05:24 jmcneill 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_armv8.h>
     43 
     44 static tprof_param_t armv8_pmu_param;
     45 static const u_int armv8_pmu_counter = 1;
     46 static uint32_t counter_val;
     47 static uint32_t counter_reset_val;
     48 
     49 static bool
     50 armv8_pmu_event_implemented(uint16_t event)
     51 {
     52 	uint64_t eid[2];
     53 
     54 	if (event >= 64)
     55 		return false;
     56 
     57 	eid[0] = reg_pmceid0_el0_read();
     58 	eid[1] = reg_pmceid1_el0_read();
     59 
     60 	const u_int idx = event / 32;
     61 	const u_int bit = event % 32;
     62 
     63 	if (eid[idx] & __BIT(bit))
     64 		return true;
     65 
     66 	return false;
     67 }
     68 
     69 static void
     70 armv8_pmu_set_pmevtyper(u_int counter, uint64_t val)
     71 {
     72 	reg_pmselr_el0_write(counter);
     73 	arm_isb();
     74 	reg_pmxevtyper_el0_write(val);
     75 }
     76 
     77 static void
     78 armv8_pmu_set_pmevcntr(u_int counter, uint32_t val)
     79 {
     80 	reg_pmselr_el0_write(counter);
     81 	arm_isb();
     82 	reg_pmxevcntr_el0_write(val);
     83 }
     84 
     85 static void
     86 armv8_pmu_start_cpu(void *arg1, void *arg2)
     87 {
     88 	const uint32_t counter_mask = __BIT(armv8_pmu_counter);
     89 	uint64_t pmcr, pmevtyper;
     90 
     91 	/* Enable performance monitor */
     92 	pmcr = reg_pmcr_el0_read();
     93 	pmcr |= PMCR_E;
     94 	reg_pmcr_el0_write(pmcr);
     95 
     96 	/* Disable event counter */
     97 	reg_pmcntenclr_el0_write(counter_mask);
     98 
     99 	/* Configure event counter */
    100 	pmevtyper = __SHIFTIN(armv8_pmu_param.p_event, PMEVTYPER_EVTCOUNT);
    101 	if (!ISSET(armv8_pmu_param.p_flags, TPROF_PARAM_USER))
    102 		pmevtyper |= PMEVTYPER_U;
    103 	if (!ISSET(armv8_pmu_param.p_flags, TPROF_PARAM_KERN))
    104 		pmevtyper |= PMEVTYPER_P;
    105 
    106 	armv8_pmu_set_pmevtyper(armv8_pmu_counter, pmevtyper);
    107 
    108 	/* Enable overflow interrupts */
    109 	reg_pmintenset_el1_write(counter_mask);
    110 
    111 	/* Clear overflow flag */
    112 	reg_pmovsclr_el0_write(counter_mask);
    113 
    114 	/* Initialize event counter value */
    115 	armv8_pmu_set_pmevcntr(armv8_pmu_counter, counter_reset_val);
    116 
    117 	/* Enable event counter */
    118 	reg_pmcntenset_el0_write(counter_mask);
    119 }
    120 
    121 static void
    122 armv8_pmu_stop_cpu(void *arg1, void *arg2)
    123 {
    124 	const uint32_t counter_mask = __BIT(armv8_pmu_counter);
    125 	uint32_t pmcr;
    126 
    127 	/* Disable overflow interrupts */
    128 	reg_pmintenclr_el1_write(counter_mask);
    129 
    130 	/* Disable event counter */
    131 	reg_pmcntenclr_el0_write(counter_mask);
    132 
    133 	/* Disable performance monitor */
    134 	pmcr = reg_pmcr_el0_read();
    135 	pmcr &= ~PMCR_E;
    136 	reg_pmcr_el0_write(pmcr);
    137 }
    138 
    139 static uint64_t
    140 armv8_pmu_estimate_freq(void)
    141 {
    142 	uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq;
    143 	uint64_t freq = 10000;
    144 	uint32_t pmcr;
    145 
    146 	counter_val = cpufreq / freq;
    147 	if (counter_val == 0)
    148 		counter_val = 4000000000ULL / freq;
    149 
    150 	pmcr = reg_pmcr_el0_read();
    151 	if (pmcr & PMCR_D)
    152 		counter_val /= 64;
    153 
    154 	return freq;
    155 }
    156 
    157 static uint32_t
    158 armv8_pmu_ident(void)
    159 {
    160 	return TPROF_IDENT_ARMV8_GENERIC;
    161 }
    162 
    163 static int
    164 armv8_pmu_start(const tprof_param_t *param)
    165 {
    166 	uint64_t xc;
    167 
    168 	if (!armv8_pmu_event_implemented(param->p_event)) {
    169 		printf("%s: event 0x%#llx not implemented on this CPU\n",
    170 		    __func__, param->p_event);
    171 		return EINVAL;
    172 	}
    173 
    174 	counter_reset_val = -counter_val + 1;
    175 
    176 	armv8_pmu_param = *param;
    177 	xc = xc_broadcast(0, armv8_pmu_start_cpu, NULL, NULL);
    178 	xc_wait(xc);
    179 
    180 	return 0;
    181 }
    182 
    183 static void
    184 armv8_pmu_stop(const tprof_param_t *param)
    185 {
    186 	uint64_t xc;
    187 
    188 	xc = xc_broadcast(0, armv8_pmu_stop_cpu, NULL, NULL);
    189 	xc_wait(xc);
    190 }
    191 
    192 static const tprof_backend_ops_t tprof_armv8_pmu_ops = {
    193 	.tbo_estimate_freq = armv8_pmu_estimate_freq,
    194 	.tbo_ident = armv8_pmu_ident,
    195 	.tbo_start = armv8_pmu_start,
    196 	.tbo_stop = armv8_pmu_stop,
    197 };
    198 
    199 int
    200 armv8_pmu_intr(void *priv)
    201 {
    202 	const struct trapframe * const tf = priv;
    203 	const uint32_t counter_mask = __BIT(armv8_pmu_counter);
    204 	tprof_frame_info_t tfi;
    205 
    206 	const uint32_t pmovs = reg_pmovsset_el0_read();
    207 	if ((pmovs & counter_mask) != 0) {
    208 		tfi.tfi_pc = tf->tf_pc;
    209 		tfi.tfi_inkernel = tfi.tfi_pc >= VM_MIN_KERNEL_ADDRESS &&
    210 		    tfi.tfi_pc < VM_MAX_KERNEL_ADDRESS;
    211 		tprof_sample(NULL, &tfi);
    212 
    213 		armv8_pmu_set_pmevcntr(armv8_pmu_counter, counter_reset_val);
    214 	}
    215 	reg_pmovsclr_el0_write(pmovs);
    216 
    217 	return 1;
    218 }
    219 
    220 int
    221 armv8_pmu_init(void)
    222 {
    223 	return tprof_backend_register("tprof_armv8", &tprof_armv8_pmu_ops,
    224 	    TPROF_BACKEND_VERSION);
    225 }
    226