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