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