xref: /qemu/target/ppc/power8-pmu.c (revision b83a80e8)
1 /*
2  * PMU emulation helpers for TCG IBM POWER chips
3  *
4  *  Copyright IBM Corp. 2021
5  *
6  * Authors:
7  *  Daniel Henrique Barboza      <danielhb413@gmail.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "cpu.h"
15 #include "helper_regs.h"
16 #include "exec/exec-all.h"
17 #include "exec/helper-proto.h"
18 #include "qemu/error-report.h"
19 #include "qemu/main-loop.h"
20 #include "hw/ppc/ppc.h"
21 #include "power8-pmu.h"
22 
23 #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
24 
25 #define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL
26 
27 static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn)
28 {
29     if (sprn == SPR_POWER_PMC1) {
30         return env->spr[SPR_POWER_MMCR0] & MMCR0_PMC1CE;
31     }
32 
33     return env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE;
34 }
35 
36 void pmu_update_summaries(CPUPPCState *env)
37 {
38     target_ulong mmcr0 = env->spr[SPR_POWER_MMCR0];
39     target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1];
40     int ins_cnt = 0;
41     int cyc_cnt = 0;
42 
43     if (mmcr0 & MMCR0_FC) {
44         goto hflags_calc;
45     }
46 
47     if (!(mmcr0 & MMCR0_FC14) && mmcr1 != 0) {
48         target_ulong sel;
49 
50         sel = extract64(mmcr1, MMCR1_PMC1EVT_EXTR, MMCR1_EVT_SIZE);
51         switch (sel) {
52         case 0x02:
53         case 0xfe:
54             ins_cnt |= 1 << 1;
55             break;
56         case 0x1e:
57         case 0xf0:
58             cyc_cnt |= 1 << 1;
59             break;
60         }
61 
62         sel = extract64(mmcr1, MMCR1_PMC2EVT_EXTR, MMCR1_EVT_SIZE);
63         ins_cnt |= (sel == 0x02) << 2;
64         cyc_cnt |= (sel == 0x1e) << 2;
65 
66         sel = extract64(mmcr1, MMCR1_PMC3EVT_EXTR, MMCR1_EVT_SIZE);
67         ins_cnt |= (sel == 0x02) << 3;
68         cyc_cnt |= (sel == 0x1e) << 3;
69 
70         sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE);
71         ins_cnt |= ((sel == 0xfa) || (sel == 0x2)) << 4;
72         cyc_cnt |= (sel == 0x1e) << 4;
73     }
74 
75     ins_cnt |= !(mmcr0 & MMCR0_FC56) << 5;
76     cyc_cnt |= !(mmcr0 & MMCR0_FC56) << 6;
77 
78  hflags_calc:
79     env->pmc_ins_cnt = ins_cnt;
80     env->pmc_cyc_cnt = cyc_cnt;
81     env->hflags = deposit32(env->hflags, HFLAGS_INSN_CNT, 1, ins_cnt != 0);
82 }
83 
84 static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns)
85 {
86     target_ulong mmcr0 = env->spr[SPR_POWER_MMCR0];
87     unsigned ins_cnt = env->pmc_ins_cnt;
88     bool overflow_triggered = false;
89     target_ulong tmp;
90 
91     if (unlikely(ins_cnt & 0x1e)) {
92         if (ins_cnt & (1 << 1)) {
93             tmp = env->spr[SPR_POWER_PMC1];
94             tmp += num_insns;
95             if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMC1CE)) {
96                 tmp = PMC_COUNTER_NEGATIVE_VAL;
97                 overflow_triggered = true;
98             }
99             env->spr[SPR_POWER_PMC1] = tmp;
100         }
101 
102         if (ins_cnt & (1 << 2)) {
103             tmp = env->spr[SPR_POWER_PMC2];
104             tmp += num_insns;
105             if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
106                 tmp = PMC_COUNTER_NEGATIVE_VAL;
107                 overflow_triggered = true;
108             }
109             env->spr[SPR_POWER_PMC2] = tmp;
110         }
111 
112         if (ins_cnt & (1 << 3)) {
113             tmp = env->spr[SPR_POWER_PMC3];
114             tmp += num_insns;
115             if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
116                 tmp = PMC_COUNTER_NEGATIVE_VAL;
117                 overflow_triggered = true;
118             }
119             env->spr[SPR_POWER_PMC3] = tmp;
120         }
121 
122         if (ins_cnt & (1 << 4)) {
123             target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1];
124             int sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE);
125             if (sel == 0x02 || (env->spr[SPR_CTRL] & CTRL_RUN)) {
126                 tmp = env->spr[SPR_POWER_PMC4];
127                 tmp += num_insns;
128                 if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
129                     tmp = PMC_COUNTER_NEGATIVE_VAL;
130                     overflow_triggered = true;
131                 }
132                 env->spr[SPR_POWER_PMC4] = tmp;
133             }
134         }
135     }
136 
137     if (ins_cnt & (1 << 5)) {
138         tmp = env->spr[SPR_POWER_PMC5];
139         tmp += num_insns;
140         if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
141             tmp = PMC_COUNTER_NEGATIVE_VAL;
142             overflow_triggered = true;
143         }
144         env->spr[SPR_POWER_PMC5] = tmp;
145     }
146 
147     return overflow_triggered;
148 }
149 
150 static void pmu_update_cycles(CPUPPCState *env)
151 {
152     uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
153     uint64_t time_delta = now - env->pmu_base_time;
154     int sprn, cyc_cnt = env->pmc_cyc_cnt;
155 
156     for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC6; sprn++) {
157         if (cyc_cnt & (1 << (sprn - SPR_POWER_PMC1 + 1))) {
158             /*
159              * The pseries and powernv clock runs at 1Ghz, meaning
160              * that 1 nanosec equals 1 cycle.
161              */
162             env->spr[sprn] += time_delta;
163         }
164     }
165 
166     /* Update base_time for future calculations */
167     env->pmu_base_time = now;
168 }
169 
170 /*
171  * Helper function to retrieve the cycle overflow timer of the
172  * 'sprn' counter.
173  */
174 static QEMUTimer *get_cyc_overflow_timer(CPUPPCState *env, int sprn)
175 {
176     return env->pmu_cyc_overflow_timers[sprn - SPR_POWER_PMC1];
177 }
178 
179 static void pmc_update_overflow_timer(CPUPPCState *env, int sprn)
180 {
181     QEMUTimer *pmc_overflow_timer = get_cyc_overflow_timer(env, sprn);
182     int64_t timeout;
183 
184     /*
185      * PMC5 does not have an overflow timer and this pointer
186      * will be NULL.
187      */
188     if (!pmc_overflow_timer) {
189         return;
190     }
191 
192     if (!(env->pmc_cyc_cnt & (1 << (sprn - SPR_POWER_PMC1 + 1))) ||
193         !pmc_has_overflow_enabled(env, sprn)) {
194         /* Overflow timer is not needed for this counter */
195         timer_del(pmc_overflow_timer);
196         return;
197     }
198 
199     if (env->spr[sprn] >= PMC_COUNTER_NEGATIVE_VAL) {
200         timeout = 0;
201     } else {
202         timeout = PMC_COUNTER_NEGATIVE_VAL - env->spr[sprn];
203     }
204 
205     /*
206      * Use timer_mod_anticipate() because an overflow timer might
207      * be already running for this PMC.
208      */
209     timer_mod_anticipate(pmc_overflow_timer, env->pmu_base_time + timeout);
210 }
211 
212 static void pmu_update_overflow_timers(CPUPPCState *env)
213 {
214     int sprn;
215 
216     /*
217      * Scroll through all PMCs and start counter overflow timers for
218      * PM_CYC events, if needed.
219      */
220     for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC6; sprn++) {
221         pmc_update_overflow_timer(env, sprn);
222     }
223 }
224 
225 void helper_store_mmcr0(CPUPPCState *env, target_ulong value)
226 {
227     bool hflags_pmcc0 = (value & MMCR0_PMCC0) != 0;
228     bool hflags_pmcc1 = (value & MMCR0_PMCC1) != 0;
229 
230     pmu_update_cycles(env);
231 
232     env->spr[SPR_POWER_MMCR0] = value;
233 
234     /* MMCR0 writes can change HFLAGS_PMCC[01] and HFLAGS_INSN_CNT */
235     env->hflags = deposit32(env->hflags, HFLAGS_PMCC0, 1, hflags_pmcc0);
236     env->hflags = deposit32(env->hflags, HFLAGS_PMCC1, 1, hflags_pmcc1);
237 
238     pmu_update_summaries(env);
239 
240     /* Update cycle overflow timers with the current MMCR0 state */
241     pmu_update_overflow_timers(env);
242 }
243 
244 void helper_store_mmcr1(CPUPPCState *env, uint64_t value)
245 {
246     pmu_update_cycles(env);
247 
248     env->spr[SPR_POWER_MMCR1] = value;
249 
250     /* MMCR1 writes can change HFLAGS_INSN_CNT */
251     pmu_update_summaries(env);
252 }
253 
254 target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn)
255 {
256     pmu_update_cycles(env);
257 
258     return env->spr[sprn];
259 }
260 
261 void helper_store_pmc(CPUPPCState *env, uint32_t sprn, uint64_t value)
262 {
263     pmu_update_cycles(env);
264 
265     env->spr[sprn] = value;
266 
267     pmc_update_overflow_timer(env, sprn);
268 }
269 
270 static void fire_PMC_interrupt(PowerPCCPU *cpu)
271 {
272     CPUPPCState *env = &cpu->env;
273 
274     if (!(env->spr[SPR_POWER_MMCR0] & MMCR0_EBE)) {
275         return;
276     }
277 
278     /* PMC interrupt not implemented yet */
279     return;
280 }
281 
282 /* This helper assumes that the PMC is running. */
283 void helper_insns_inc(CPUPPCState *env, uint32_t num_insns)
284 {
285     bool overflow_triggered;
286     PowerPCCPU *cpu;
287 
288     overflow_triggered = pmu_increment_insns(env, num_insns);
289 
290     if (overflow_triggered) {
291         cpu = env_archcpu(env);
292         fire_PMC_interrupt(cpu);
293     }
294 }
295 
296 static void cpu_ppc_pmu_timer_cb(void *opaque)
297 {
298     PowerPCCPU *cpu = opaque;
299 
300     fire_PMC_interrupt(cpu);
301 }
302 
303 void cpu_ppc_pmu_init(CPUPPCState *env)
304 {
305     PowerPCCPU *cpu = env_archcpu(env);
306     int i, sprn;
307 
308     for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC6; sprn++) {
309         if (sprn == SPR_POWER_PMC5) {
310             continue;
311         }
312 
313         i = sprn - SPR_POWER_PMC1;
314 
315         env->pmu_cyc_overflow_timers[i] = timer_new_ns(QEMU_CLOCK_VIRTUAL,
316                                                        &cpu_ppc_pmu_timer_cb,
317                                                        cpu);
318     }
319 }
320 #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */
321