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