1 /* $NetBSD: tprof_armv7.c,v 1.4 2020/10/30 18:54:37 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2018 Jared McNeill <jmcneill@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.4 2020/10/30 18:54:37 skrll 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 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 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