]>
Commit | Line | Data |
---|---|---|
3780e337 AP |
1 | /* |
2 | * RISC-V PMU file. | |
3 | * | |
4 | * Copyright (c) 2021 Western Digital Corporation or its affiliates. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2 or later, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along with | |
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include "qemu/osdep.h" | |
20 | #include "cpu.h" | |
21 | #include "pmu.h" | |
14664483 | 22 | #include "sysemu/cpu-timers.h" |
abd9a206 | 23 | #include "sysemu/device_tree.h" |
14664483 AP |
24 | |
25 | #define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ | |
26 | #define MAKE_32BIT_MASK(shift, length) \ | |
27 | (((uint32_t)(~0UL) >> (32 - (length))) << (shift)) | |
28 | ||
abd9a206 AP |
29 | /* |
30 | * To keep it simple, any event can be mapped to any programmable counters in | |
31 | * QEMU. The generic cycle & instruction count events can also be monitored | |
32 | * using programmable counters. In that case, mcycle & minstret must continue | |
33 | * to provide the correct value as well. Heterogeneous PMU per hart is not | |
34 | * supported yet. Thus, number of counters are same across all harts. | |
35 | */ | |
36 | void riscv_pmu_generate_fdt_node(void *fdt, int num_ctrs, char *pmu_name) | |
37 | { | |
38 | uint32_t fdt_event_ctr_map[20] = {}; | |
39 | uint32_t cmask; | |
40 | ||
41 | /* All the programmable counters can map to any event */ | |
42 | cmask = MAKE_32BIT_MASK(3, num_ctrs); | |
43 | ||
44 | /* | |
45 | * The event encoding is specified in the SBI specification | |
46 | * Event idx is a 20bits wide number encoded as follows: | |
47 | * event_idx[19:16] = type | |
48 | * event_idx[15:0] = code | |
49 | * The code field in cache events are encoded as follows: | |
50 | * event_idx.code[15:3] = cache_id | |
51 | * event_idx.code[2:1] = op_id | |
52 | * event_idx.code[0:0] = result_id | |
53 | */ | |
54 | ||
55 | /* SBI_PMU_HW_CPU_CYCLES: 0x01 : type(0x00) */ | |
56 | fdt_event_ctr_map[0] = cpu_to_be32(0x00000001); | |
57 | fdt_event_ctr_map[1] = cpu_to_be32(0x00000001); | |
58 | fdt_event_ctr_map[2] = cpu_to_be32(cmask | 1 << 0); | |
59 | ||
60 | /* SBI_PMU_HW_INSTRUCTIONS: 0x02 : type(0x00) */ | |
61 | fdt_event_ctr_map[3] = cpu_to_be32(0x00000002); | |
62 | fdt_event_ctr_map[4] = cpu_to_be32(0x00000002); | |
63 | fdt_event_ctr_map[5] = cpu_to_be32(cmask | 1 << 2); | |
64 | ||
65 | /* SBI_PMU_HW_CACHE_DTLB : 0x03 READ : 0x00 MISS : 0x00 type(0x01) */ | |
66 | fdt_event_ctr_map[6] = cpu_to_be32(0x00010019); | |
67 | fdt_event_ctr_map[7] = cpu_to_be32(0x00010019); | |
68 | fdt_event_ctr_map[8] = cpu_to_be32(cmask); | |
69 | ||
70 | /* SBI_PMU_HW_CACHE_DTLB : 0x03 WRITE : 0x01 MISS : 0x00 type(0x01) */ | |
71 | fdt_event_ctr_map[9] = cpu_to_be32(0x0001001B); | |
72 | fdt_event_ctr_map[10] = cpu_to_be32(0x0001001B); | |
73 | fdt_event_ctr_map[11] = cpu_to_be32(cmask); | |
74 | ||
75 | /* SBI_PMU_HW_CACHE_ITLB : 0x04 READ : 0x00 MISS : 0x00 type(0x01) */ | |
76 | fdt_event_ctr_map[12] = cpu_to_be32(0x00010021); | |
77 | fdt_event_ctr_map[13] = cpu_to_be32(0x00010021); | |
78 | fdt_event_ctr_map[14] = cpu_to_be32(cmask); | |
79 | ||
80 | /* This a OpenSBI specific DT property documented in OpenSBI docs */ | |
81 | qemu_fdt_setprop(fdt, pmu_name, "riscv,event-to-mhpmcounters", | |
82 | fdt_event_ctr_map, sizeof(fdt_event_ctr_map)); | |
83 | } | |
84 | ||
14664483 AP |
85 | static bool riscv_pmu_counter_valid(RISCVCPU *cpu, uint32_t ctr_idx) |
86 | { | |
87 | if (ctr_idx < 3 || ctr_idx >= RV_MAX_MHPMCOUNTERS || | |
88 | !(cpu->pmu_avail_ctrs & BIT(ctr_idx))) { | |
89 | return false; | |
90 | } else { | |
91 | return true; | |
92 | } | |
93 | } | |
94 | ||
95 | static bool riscv_pmu_counter_enabled(RISCVCPU *cpu, uint32_t ctr_idx) | |
96 | { | |
97 | CPURISCVState *env = &cpu->env; | |
98 | ||
99 | if (riscv_pmu_counter_valid(cpu, ctr_idx) && | |
100 | !get_field(env->mcountinhibit, BIT(ctr_idx))) { | |
101 | return true; | |
102 | } else { | |
103 | return false; | |
104 | } | |
105 | } | |
106 | ||
107 | static int riscv_pmu_incr_ctr_rv32(RISCVCPU *cpu, uint32_t ctr_idx) | |
108 | { | |
109 | CPURISCVState *env = &cpu->env; | |
110 | target_ulong max_val = UINT32_MAX; | |
111 | PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; | |
112 | bool virt_on = riscv_cpu_virt_enabled(env); | |
113 | ||
114 | /* Privilege mode filtering */ | |
115 | if ((env->priv == PRV_M && | |
116 | (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_MINH)) || | |
117 | (env->priv == PRV_S && virt_on && | |
118 | (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_VSINH)) || | |
119 | (env->priv == PRV_U && virt_on && | |
120 | (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_VUINH)) || | |
121 | (env->priv == PRV_S && !virt_on && | |
122 | (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_SINH)) || | |
123 | (env->priv == PRV_U && !virt_on && | |
124 | (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_UINH))) { | |
125 | return 0; | |
126 | } | |
127 | ||
128 | /* Handle the overflow scenario */ | |
129 | if (counter->mhpmcounter_val == max_val) { | |
130 | if (counter->mhpmcounterh_val == max_val) { | |
131 | counter->mhpmcounter_val = 0; | |
132 | counter->mhpmcounterh_val = 0; | |
133 | /* Generate interrupt only if OF bit is clear */ | |
134 | if (!(env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_OF)) { | |
135 | env->mhpmeventh_val[ctr_idx] |= MHPMEVENTH_BIT_OF; | |
136 | riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); | |
137 | } | |
138 | } else { | |
139 | counter->mhpmcounterh_val++; | |
140 | } | |
141 | } else { | |
142 | counter->mhpmcounter_val++; | |
143 | } | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | static int riscv_pmu_incr_ctr_rv64(RISCVCPU *cpu, uint32_t ctr_idx) | |
149 | { | |
150 | CPURISCVState *env = &cpu->env; | |
151 | PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; | |
152 | uint64_t max_val = UINT64_MAX; | |
153 | bool virt_on = riscv_cpu_virt_enabled(env); | |
154 | ||
155 | /* Privilege mode filtering */ | |
156 | if ((env->priv == PRV_M && | |
157 | (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_MINH)) || | |
158 | (env->priv == PRV_S && virt_on && | |
159 | (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VSINH)) || | |
160 | (env->priv == PRV_U && virt_on && | |
161 | (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VUINH)) || | |
162 | (env->priv == PRV_S && !virt_on && | |
163 | (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_SINH)) || | |
164 | (env->priv == PRV_U && !virt_on && | |
165 | (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_UINH))) { | |
166 | return 0; | |
167 | } | |
168 | ||
169 | /* Handle the overflow scenario */ | |
170 | if (counter->mhpmcounter_val == max_val) { | |
171 | counter->mhpmcounter_val = 0; | |
172 | /* Generate interrupt only if OF bit is clear */ | |
173 | if (!(env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_OF)) { | |
174 | env->mhpmevent_val[ctr_idx] |= MHPMEVENT_BIT_OF; | |
175 | riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); | |
176 | } | |
177 | } else { | |
178 | counter->mhpmcounter_val++; | |
179 | } | |
180 | return 0; | |
181 | } | |
182 | ||
183 | int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) | |
184 | { | |
185 | uint32_t ctr_idx; | |
186 | int ret; | |
187 | CPURISCVState *env = &cpu->env; | |
188 | gpointer value; | |
189 | ||
190 | if (!cpu->cfg.pmu_num) { | |
191 | return 0; | |
192 | } | |
193 | value = g_hash_table_lookup(cpu->pmu_event_ctr_map, | |
194 | GUINT_TO_POINTER(event_idx)); | |
195 | if (!value) { | |
196 | return -1; | |
197 | } | |
198 | ||
199 | ctr_idx = GPOINTER_TO_UINT(value); | |
200 | if (!riscv_pmu_counter_enabled(cpu, ctr_idx) || | |
201 | get_field(env->mcountinhibit, BIT(ctr_idx))) { | |
202 | return -1; | |
203 | } | |
204 | ||
205 | if (riscv_cpu_mxl(env) == MXL_RV32) { | |
206 | ret = riscv_pmu_incr_ctr_rv32(cpu, ctr_idx); | |
207 | } else { | |
208 | ret = riscv_pmu_incr_ctr_rv64(cpu, ctr_idx); | |
209 | } | |
210 | ||
211 | return ret; | |
212 | } | |
3780e337 AP |
213 | |
214 | bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, | |
215 | uint32_t target_ctr) | |
216 | { | |
14664483 AP |
217 | RISCVCPU *cpu; |
218 | uint32_t event_idx; | |
219 | uint32_t ctr_idx; | |
220 | ||
221 | /* Fixed instret counter */ | |
222 | if (target_ctr == 2) { | |
223 | return true; | |
224 | } | |
225 | ||
226 | cpu = RISCV_CPU(env_cpu(env)); | |
227 | if (!cpu->pmu_event_ctr_map) { | |
228 | return false; | |
229 | } | |
230 | ||
231 | event_idx = RISCV_PMU_EVENT_HW_INSTRUCTIONS; | |
232 | ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, | |
233 | GUINT_TO_POINTER(event_idx))); | |
234 | if (!ctr_idx) { | |
235 | return false; | |
236 | } | |
237 | ||
238 | return target_ctr == ctr_idx ? true : false; | |
3780e337 AP |
239 | } |
240 | ||
241 | bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr) | |
242 | { | |
14664483 AP |
243 | RISCVCPU *cpu; |
244 | uint32_t event_idx; | |
245 | uint32_t ctr_idx; | |
246 | ||
247 | /* Fixed mcycle counter */ | |
248 | if (target_ctr == 0) { | |
249 | return true; | |
250 | } | |
251 | ||
252 | cpu = RISCV_CPU(env_cpu(env)); | |
253 | if (!cpu->pmu_event_ctr_map) { | |
254 | return false; | |
255 | } | |
256 | ||
257 | event_idx = RISCV_PMU_EVENT_HW_CPU_CYCLES; | |
258 | ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, | |
259 | GUINT_TO_POINTER(event_idx))); | |
260 | ||
261 | /* Counter zero is not used for event_ctr_map */ | |
262 | if (!ctr_idx) { | |
263 | return false; | |
264 | } | |
265 | ||
266 | return (target_ctr == ctr_idx) ? true : false; | |
267 | } | |
268 | ||
269 | static gboolean pmu_remove_event_map(gpointer key, gpointer value, | |
270 | gpointer udata) | |
271 | { | |
272 | return (GPOINTER_TO_UINT(value) == GPOINTER_TO_UINT(udata)) ? true : false; | |
273 | } | |
274 | ||
275 | static int64_t pmu_icount_ticks_to_ns(int64_t value) | |
276 | { | |
277 | int64_t ret = 0; | |
278 | ||
279 | if (icount_enabled()) { | |
280 | ret = icount_to_ns(value); | |
281 | } else { | |
282 | ret = (NANOSECONDS_PER_SECOND / RISCV_TIMEBASE_FREQ) * value; | |
283 | } | |
284 | ||
285 | return ret; | |
286 | } | |
287 | ||
288 | int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, | |
289 | uint32_t ctr_idx) | |
290 | { | |
291 | uint32_t event_idx; | |
292 | RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); | |
293 | ||
294 | if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->pmu_event_ctr_map) { | |
295 | return -1; | |
296 | } | |
297 | ||
298 | /* | |
299 | * Expected mhpmevent value is zero for reset case. Remove the current | |
300 | * mapping. | |
301 | */ | |
302 | if (!value) { | |
303 | g_hash_table_foreach_remove(cpu->pmu_event_ctr_map, | |
304 | pmu_remove_event_map, | |
305 | GUINT_TO_POINTER(ctr_idx)); | |
306 | return 0; | |
307 | } | |
308 | ||
309 | event_idx = value & MHPMEVENT_IDX_MASK; | |
310 | if (g_hash_table_lookup(cpu->pmu_event_ctr_map, | |
311 | GUINT_TO_POINTER(event_idx))) { | |
312 | return 0; | |
313 | } | |
314 | ||
315 | switch (event_idx) { | |
316 | case RISCV_PMU_EVENT_HW_CPU_CYCLES: | |
317 | case RISCV_PMU_EVENT_HW_INSTRUCTIONS: | |
318 | case RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS: | |
319 | case RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS: | |
320 | case RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS: | |
321 | break; | |
322 | default: | |
323 | /* We don't support any raw events right now */ | |
324 | return -1; | |
325 | } | |
326 | g_hash_table_insert(cpu->pmu_event_ctr_map, GUINT_TO_POINTER(event_idx), | |
327 | GUINT_TO_POINTER(ctr_idx)); | |
328 | ||
329 | return 0; | |
330 | } | |
331 | ||
332 | static void pmu_timer_trigger_irq(RISCVCPU *cpu, | |
333 | enum riscv_pmu_event_idx evt_idx) | |
334 | { | |
335 | uint32_t ctr_idx; | |
336 | CPURISCVState *env = &cpu->env; | |
337 | PMUCTRState *counter; | |
338 | target_ulong *mhpmevent_val; | |
339 | uint64_t of_bit_mask; | |
340 | int64_t irq_trigger_at; | |
341 | ||
342 | if (evt_idx != RISCV_PMU_EVENT_HW_CPU_CYCLES && | |
343 | evt_idx != RISCV_PMU_EVENT_HW_INSTRUCTIONS) { | |
344 | return; | |
345 | } | |
346 | ||
347 | ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, | |
348 | GUINT_TO_POINTER(evt_idx))); | |
349 | if (!riscv_pmu_counter_enabled(cpu, ctr_idx)) { | |
350 | return; | |
351 | } | |
352 | ||
353 | if (riscv_cpu_mxl(env) == MXL_RV32) { | |
354 | mhpmevent_val = &env->mhpmeventh_val[ctr_idx]; | |
355 | of_bit_mask = MHPMEVENTH_BIT_OF; | |
356 | } else { | |
357 | mhpmevent_val = &env->mhpmevent_val[ctr_idx]; | |
358 | of_bit_mask = MHPMEVENT_BIT_OF; | |
359 | } | |
360 | ||
361 | counter = &env->pmu_ctrs[ctr_idx]; | |
362 | if (counter->irq_overflow_left > 0) { | |
363 | irq_trigger_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + | |
364 | counter->irq_overflow_left; | |
365 | timer_mod_anticipate_ns(cpu->pmu_timer, irq_trigger_at); | |
366 | counter->irq_overflow_left = 0; | |
367 | return; | |
368 | } | |
369 | ||
370 | if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) { | |
371 | /* Generate interrupt only if OF bit is clear */ | |
372 | if (!(*mhpmevent_val & of_bit_mask)) { | |
373 | *mhpmevent_val |= of_bit_mask; | |
374 | riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); | |
375 | } | |
376 | } | |
377 | } | |
378 | ||
379 | /* Timer callback for instret and cycle counter overflow */ | |
380 | void riscv_pmu_timer_cb(void *priv) | |
381 | { | |
382 | RISCVCPU *cpu = priv; | |
383 | ||
384 | /* Timer event was triggered only for these events */ | |
385 | pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_CPU_CYCLES); | |
386 | pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_INSTRUCTIONS); | |
387 | } | |
388 | ||
389 | int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) | |
390 | { | |
391 | uint64_t overflow_delta, overflow_at; | |
392 | int64_t overflow_ns, overflow_left = 0; | |
393 | RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); | |
394 | PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; | |
395 | ||
396 | if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf) { | |
397 | return -1; | |
398 | } | |
399 | ||
400 | if (value) { | |
401 | overflow_delta = UINT64_MAX - value + 1; | |
402 | } else { | |
403 | overflow_delta = UINT64_MAX; | |
404 | } | |
405 | ||
406 | /* | |
407 | * QEMU supports only int64_t timers while RISC-V counters are uint64_t. | |
408 | * Compute the leftover and save it so that it can be reprogrammed again | |
409 | * when timer expires. | |
410 | */ | |
411 | if (overflow_delta > INT64_MAX) { | |
412 | overflow_left = overflow_delta - INT64_MAX; | |
413 | } | |
414 | ||
415 | if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || | |
416 | riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { | |
417 | overflow_ns = pmu_icount_ticks_to_ns((int64_t)overflow_delta); | |
418 | overflow_left = pmu_icount_ticks_to_ns(overflow_left) ; | |
419 | } else { | |
420 | return -1; | |
421 | } | |
422 | overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + overflow_ns; | |
423 | ||
424 | if (overflow_at > INT64_MAX) { | |
425 | overflow_left += overflow_at - INT64_MAX; | |
426 | counter->irq_overflow_left = overflow_left; | |
427 | overflow_at = INT64_MAX; | |
428 | } | |
429 | timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); | |
430 | ||
431 | return 0; | |
432 | } | |
433 | ||
434 | ||
435 | int riscv_pmu_init(RISCVCPU *cpu, int num_counters) | |
436 | { | |
437 | if (num_counters > (RV_MAX_MHPMCOUNTERS - 3)) { | |
438 | return -1; | |
439 | } | |
440 | ||
441 | cpu->pmu_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); | |
442 | if (!cpu->pmu_event_ctr_map) { | |
443 | /* PMU support can not be enabled */ | |
444 | qemu_log_mask(LOG_UNIMP, "PMU events can't be supported\n"); | |
445 | cpu->cfg.pmu_num = 0; | |
446 | return -1; | |
447 | } | |
448 | ||
449 | /* Create a bitmask of available programmable counters */ | |
450 | cpu->pmu_avail_ctrs = MAKE_32BIT_MASK(3, num_counters); | |
451 | ||
452 | return 0; | |
3780e337 | 453 | } |