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