2 * PMU emulation helpers for TCG IBM POWER chips
4 * Copyright IBM Corp. 2021
7 * Daniel Henrique Barboza <danielhb413@gmail.com>
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.
13 #include "qemu/osdep.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/timer.h"
20 #include "hw/ppc/ppc.h"
21 #include "power8-pmu.h"
23 #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
25 static bool pmc_has_overflow_enabled(CPUPPCState
*env
, int sprn
)
27 if (sprn
== SPR_POWER_PMC1
) {
28 return env
->spr
[SPR_POWER_MMCR0
] & MMCR0_PMC1CE
;
31 return env
->spr
[SPR_POWER_MMCR0
] & MMCR0_PMCjCE
;
35 * Called after MMCR0 or MMCR1 changes to update pmc_ins_cnt and pmc_cyc_cnt.
36 * hflags must subsequently be updated.
38 static void pmu_update_summaries(CPUPPCState
*env
)
40 target_ulong mmcr0
= env
->spr
[SPR_POWER_MMCR0
];
41 target_ulong mmcr1
= env
->spr
[SPR_POWER_MMCR1
];
45 if (mmcr0
& MMCR0_FC
) {
49 if (!(mmcr0
& MMCR0_FC14
) && mmcr1
!= 0) {
52 sel
= extract64(mmcr1
, MMCR1_PMC1EVT_EXTR
, MMCR1_EVT_SIZE
);
64 sel
= extract64(mmcr1
, MMCR1_PMC2EVT_EXTR
, MMCR1_EVT_SIZE
);
65 ins_cnt
|= (sel
== 0x02) << 2;
66 cyc_cnt
|= (sel
== 0x1e) << 2;
68 sel
= extract64(mmcr1
, MMCR1_PMC3EVT_EXTR
, MMCR1_EVT_SIZE
);
69 ins_cnt
|= (sel
== 0x02) << 3;
70 cyc_cnt
|= (sel
== 0x1e) << 3;
72 sel
= extract64(mmcr1
, MMCR1_PMC4EVT_EXTR
, MMCR1_EVT_SIZE
);
73 ins_cnt
|= ((sel
== 0xfa) || (sel
== 0x2)) << 4;
74 cyc_cnt
|= (sel
== 0x1e) << 4;
77 ins_cnt
|= !(mmcr0
& MMCR0_FC56
) << 5;
78 cyc_cnt
|= !(mmcr0
& MMCR0_FC56
) << 6;
81 env
->pmc_ins_cnt
= ins_cnt
;
82 env
->pmc_cyc_cnt
= cyc_cnt
;
85 void pmu_mmcr01_updated(CPUPPCState
*env
)
87 PowerPCCPU
*cpu
= env_archcpu(env
);
89 pmu_update_summaries(env
);
90 hreg_update_pmu_hflags(env
);
92 if (env
->spr
[SPR_POWER_MMCR0
] & MMCR0_PMAO
) {
93 ppc_set_irq(cpu
, PPC_INTERRUPT_PERFM
, 1);
95 ppc_set_irq(cpu
, PPC_INTERRUPT_PERFM
, 0);
99 * Should this update overflow timers (if mmcr0 is updated) so they
100 * get set in cpu_post_load?
104 static bool pmu_increment_insns(CPUPPCState
*env
, uint32_t num_insns
)
106 target_ulong mmcr0
= env
->spr
[SPR_POWER_MMCR0
];
107 unsigned ins_cnt
= env
->pmc_ins_cnt
;
108 bool overflow_triggered
= false;
111 if (ins_cnt
& (1 << 1)) {
112 tmp
= env
->spr
[SPR_POWER_PMC1
];
114 if (tmp
>= PMC_COUNTER_NEGATIVE_VAL
&& (mmcr0
& MMCR0_PMC1CE
)) {
115 tmp
= PMC_COUNTER_NEGATIVE_VAL
;
116 overflow_triggered
= true;
118 env
->spr
[SPR_POWER_PMC1
] = tmp
;
121 if (ins_cnt
& (1 << 2)) {
122 tmp
= env
->spr
[SPR_POWER_PMC2
];
124 if (tmp
>= PMC_COUNTER_NEGATIVE_VAL
&& (mmcr0
& MMCR0_PMCjCE
)) {
125 tmp
= PMC_COUNTER_NEGATIVE_VAL
;
126 overflow_triggered
= true;
128 env
->spr
[SPR_POWER_PMC2
] = tmp
;
131 if (ins_cnt
& (1 << 3)) {
132 tmp
= env
->spr
[SPR_POWER_PMC3
];
134 if (tmp
>= PMC_COUNTER_NEGATIVE_VAL
&& (mmcr0
& MMCR0_PMCjCE
)) {
135 tmp
= PMC_COUNTER_NEGATIVE_VAL
;
136 overflow_triggered
= true;
138 env
->spr
[SPR_POWER_PMC3
] = tmp
;
141 if (ins_cnt
& (1 << 4)) {
142 target_ulong mmcr1
= env
->spr
[SPR_POWER_MMCR1
];
143 int sel
= extract64(mmcr1
, MMCR1_PMC4EVT_EXTR
, MMCR1_EVT_SIZE
);
144 if (sel
== 0x02 || (env
->spr
[SPR_CTRL
] & CTRL_RUN
)) {
145 tmp
= env
->spr
[SPR_POWER_PMC4
];
147 if (tmp
>= PMC_COUNTER_NEGATIVE_VAL
&& (mmcr0
& MMCR0_PMCjCE
)) {
148 tmp
= PMC_COUNTER_NEGATIVE_VAL
;
149 overflow_triggered
= true;
151 env
->spr
[SPR_POWER_PMC4
] = tmp
;
155 if (ins_cnt
& (1 << 5)) {
156 tmp
= env
->spr
[SPR_POWER_PMC5
];
158 if (tmp
>= PMC_COUNTER_NEGATIVE_VAL
&& (mmcr0
& MMCR0_PMCjCE
)) {
159 tmp
= PMC_COUNTER_NEGATIVE_VAL
;
160 overflow_triggered
= true;
162 env
->spr
[SPR_POWER_PMC5
] = tmp
;
165 return overflow_triggered
;
168 static void pmu_update_cycles(CPUPPCState
*env
)
170 uint64_t now
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
171 uint64_t time_delta
= now
- env
->pmu_base_time
;
172 int sprn
, cyc_cnt
= env
->pmc_cyc_cnt
;
174 for (sprn
= SPR_POWER_PMC1
; sprn
<= SPR_POWER_PMC6
; sprn
++) {
175 if (cyc_cnt
& (1 << (sprn
- SPR_POWER_PMC1
+ 1))) {
177 * The pseries and powernv clock runs at 1Ghz, meaning
178 * that 1 nanosec equals 1 cycle.
180 env
->spr
[sprn
] += time_delta
;
184 /* Update base_time for future calculations */
185 env
->pmu_base_time
= now
;
189 * Helper function to retrieve the cycle overflow timer of the
192 static QEMUTimer
*get_cyc_overflow_timer(CPUPPCState
*env
, int sprn
)
194 return env
->pmu_cyc_overflow_timers
[sprn
- SPR_POWER_PMC1
];
197 static void pmc_update_overflow_timer(CPUPPCState
*env
, int sprn
)
199 QEMUTimer
*pmc_overflow_timer
= get_cyc_overflow_timer(env
, sprn
);
203 * PMC5 does not have an overflow timer and this pointer
206 if (!pmc_overflow_timer
) {
210 if (!(env
->pmc_cyc_cnt
& (1 << (sprn
- SPR_POWER_PMC1
+ 1))) ||
211 !pmc_has_overflow_enabled(env
, sprn
)) {
212 /* Overflow timer is not needed for this counter */
213 timer_del(pmc_overflow_timer
);
217 if (env
->spr
[sprn
] >= PMC_COUNTER_NEGATIVE_VAL
) {
220 timeout
= PMC_COUNTER_NEGATIVE_VAL
- env
->spr
[sprn
];
224 * Use timer_mod_anticipate() because an overflow timer might
225 * be already running for this PMC.
227 timer_mod_anticipate(pmc_overflow_timer
, env
->pmu_base_time
+ timeout
);
230 static void pmu_update_overflow_timers(CPUPPCState
*env
)
235 * Scroll through all PMCs and start counter overflow timers for
236 * PM_CYC events, if needed.
238 for (sprn
= SPR_POWER_PMC1
; sprn
<= SPR_POWER_PMC6
; sprn
++) {
239 pmc_update_overflow_timer(env
, sprn
);
243 static void pmu_delete_timers(CPUPPCState
*env
)
245 QEMUTimer
*pmc_overflow_timer
;
248 for (sprn
= SPR_POWER_PMC1
; sprn
<= SPR_POWER_PMC6
; sprn
++) {
249 pmc_overflow_timer
= get_cyc_overflow_timer(env
, sprn
);
251 if (pmc_overflow_timer
) {
252 timer_del(pmc_overflow_timer
);
257 void helper_store_mmcr0(CPUPPCState
*env
, target_ulong value
)
259 pmu_update_cycles(env
);
261 env
->spr
[SPR_POWER_MMCR0
] = value
;
263 pmu_mmcr01_updated(env
);
265 /* Update cycle overflow timers with the current MMCR0 state */
266 pmu_update_overflow_timers(env
);
269 void helper_store_mmcr1(CPUPPCState
*env
, uint64_t value
)
271 pmu_update_cycles(env
);
273 env
->spr
[SPR_POWER_MMCR1
] = value
;
275 pmu_mmcr01_updated(env
);
278 target_ulong
helper_read_pmc(CPUPPCState
*env
, uint32_t sprn
)
280 pmu_update_cycles(env
);
282 return env
->spr
[sprn
];
285 void helper_store_pmc(CPUPPCState
*env
, uint32_t sprn
, uint64_t value
)
287 pmu_update_cycles(env
);
289 env
->spr
[sprn
] = (uint32_t)value
;
291 pmc_update_overflow_timer(env
, sprn
);
294 static void perfm_alert(PowerPCCPU
*cpu
)
296 CPUPPCState
*env
= &cpu
->env
;
298 pmu_update_cycles(env
);
300 if (env
->spr
[SPR_POWER_MMCR0
] & MMCR0_FCECE
) {
301 env
->spr
[SPR_POWER_MMCR0
] |= MMCR0_FC
;
303 /* Changing MMCR0_FC requires summaries and hflags update */
304 pmu_mmcr01_updated(env
);
307 * Delete all pending timers if we need to freeze
308 * the PMC. We'll restart them when the PMC starts
311 pmu_delete_timers(env
);
314 if (env
->spr
[SPR_POWER_MMCR0
] & MMCR0_PMAE
) {
315 /* These MMCR0 bits do not require summaries or hflags update. */
316 env
->spr
[SPR_POWER_MMCR0
] &= ~MMCR0_PMAE
;
317 env
->spr
[SPR_POWER_MMCR0
] |= MMCR0_PMAO
;
318 ppc_set_irq(cpu
, PPC_INTERRUPT_PERFM
, 1);
321 raise_ebb_perfm_exception(env
);
324 void helper_handle_pmc5_overflow(CPUPPCState
*env
)
326 env
->spr
[SPR_POWER_PMC5
] = PMC_COUNTER_NEGATIVE_VAL
;
327 perfm_alert(env_archcpu(env
));
330 /* This helper assumes that the PMC is running. */
331 void helper_insns_inc(CPUPPCState
*env
, uint32_t num_insns
)
333 bool overflow_triggered
;
335 overflow_triggered
= pmu_increment_insns(env
, num_insns
);
336 if (overflow_triggered
) {
337 perfm_alert(env_archcpu(env
));
341 static void cpu_ppc_pmu_timer_cb(void *opaque
)
343 PowerPCCPU
*cpu
= opaque
;
348 void cpu_ppc_pmu_init(CPUPPCState
*env
)
350 PowerPCCPU
*cpu
= env_archcpu(env
);
353 for (sprn
= SPR_POWER_PMC1
; sprn
<= SPR_POWER_PMC6
; sprn
++) {
354 if (sprn
== SPR_POWER_PMC5
) {
358 i
= sprn
- SPR_POWER_PMC1
;
360 env
->pmu_cyc_overflow_timers
[i
] = timer_new_ns(QEMU_CLOCK_VIRTUAL
,
361 &cpu_ppc_pmu_timer_cb
,
365 #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */