@samitouri / QOSamiQemu / commits / 6fd2fcdc61

target/arm: teach arm_cpu_has_work about halting reasons

With the advent of WFE and WFI we need to pay closer attention to the reason why the vCPU may be sleeping to figure out if we should wake it up. Create env->halt_reason to track this and then re-order the tests so we: - ignore everything is the vCPU is powered off - wake up if the event_register is set and we were in a WFE - otherwise any IRQ event does wake the vCPU up. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260529082948.363931-3-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Alex Bennée committed May 29, 2026 at 09:29 UTC 6fd2fcdc61b709470b8bcfb9d10e714d4b222199
7 files changed +66 -17
target/arm/arm-powerctl.c
+3 -3
@@ -78,7 +78,7 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
78
79 /* Finally set the power status */
80 assert(bql_locked());
81 - target_cpu->power_state = PSCI_ON;
81 + arm_set_cpu_power_state(target_cpu, PSCI_ON);
82 }
83
84 int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
@@ -186,7 +186,7 @@ static void arm_set_cpu_on_and_reset_async_work(CPUState *target_cpu_state,
186
187 /* Finally set the power status */
188 assert(bql_locked());
189 - target_cpu->power_state = PSCI_ON;
189 + arm_set_cpu_power_state(target_cpu, PSCI_ON);
190 }
191
192 int arm_set_cpu_on_and_reset(uint64_t cpuid)
@@ -239,7 +239,7 @@ static void arm_set_cpu_off_async_work(CPUState *target_cpu_state,
239 ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
240
241 assert(bql_locked());
242 - target_cpu->power_state = PSCI_OFF;
242 + arm_set_cpu_power_state(target_cpu, PSCI_OFF);
243 target_cpu_state->halted = 1;
244 target_cpu_state->exception_index = EXCP_HLT;
245 }
target/arm/cpu.c
+29 -11
@@ -145,18 +145,36 @@ static bool arm_cpu_has_work(CPUState *cs)
145 {
146 ARMCPU *cpu = ARM_CPU(cs);
147
148 - if (arm_feature(&cpu->env, ARM_FEATURE_M)) {
149 - if (cpu->env.event_register) {
150 - return true;
151 - }
148 + /*
149 + * Only another PSCI call can wake the CPU up in which case the
150 + * power_state would be set by arm_set_cpu_on_and_reset_async_work()
151 + */
152 + if (cpu->power_state == PSCI_OFF) {
153 + g_assert(cpu->env.halt_reason == HALT_PSCI);
154 + return false;
155 + }
156 +
157 + /*
158 + * A wake-up event should only wake us if we are halted on a WFE
159 + */
160 + if (cpu->env.halt_reason == HALT_WFE && cpu->env.event_register) {
161 + cpu->env.halt_reason = NOT_HALTED;
162 + return true;
163 + }
164 +
165 + /*
166 + * Otherwise pretty much any IRQ would wake us up
167 + */
168 + if (cpu_test_interrupt(cs,
169 + CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD
170 + | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI
171 + | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR
172 + | CPU_INTERRUPT_EXITTB)) {
173 + cpu->env.halt_reason = NOT_HALTED;
174 + return true;
175 }
176
154 - return (cpu->power_state != PSCI_OFF)
155 - && cpu_test_interrupt(cs,
156 - CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD
157 - | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI
158 - | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR
159 - | CPU_INTERRUPT_EXITTB);
177 + return false;
178 }
179 #endif /* !CONFIG_USER_ONLY */
180
@@ -327,7 +345,7 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type)
345 env->vfp.xregs[ARM_VFP_MVFR1] = cpu->isar.mvfr1;
346 env->vfp.xregs[ARM_VFP_MVFR2] = cpu->isar.mvfr2;
347
330 - cpu->power_state = cs->start_powered_off ? PSCI_OFF : PSCI_ON;
348 + arm_set_cpu_power_state(cpu, cs->start_powered_off ? PSCI_OFF : PSCI_ON);
349
350 if (arm_feature(env, ARM_FEATURE_AARCH64)) {
351 /* 64 bit CPUs always start in 64 bit mode */
target/arm/cpu.h
+16
@@ -224,6 +224,19 @@ typedef enum ARMFPStatusFlavour {
224 /* Architecturally there are 128 PPIs in a GICv5 */
225 #define GICV5_NUM_PPIS 128
226
227 +/**
228 + * ARMHaltReason - the reason we have entered halt state
229 + *
230 + * To be able to correctly wake up via arm_cpu_has_work() we need to
231 + * track the reason we went to sleep.
232 + */
233 +typedef enum {
234 + NOT_HALTED = 0,
235 + HALT_PSCI,
236 + HALT_WFI,
237 + HALT_WFE
238 +} ARMHaltReason;
239 +
240 typedef struct CPUArchState {
241 /* Regs for current mode. */
242 uint32_t regs[16];
@@ -746,6 +759,9 @@ typedef struct CPUArchState {
759 /* Optional fault info across tlb lookup. */
760 ARMMMUFaultInfo *tlb_fi;
761
762 + /* Reason the CPU is halted */
763 + ARMHaltReason halt_reason;
764 +
765 /*
766 * The event register is shared by all ARM profiles (A/R/M),
767 * so it is stored in the top-level CPU state.
target/arm/internals.h
+11
@@ -2063,4 +2063,15 @@ bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
2063 ARMCPRegMigToleranceType type);
2064
2065
2066 +/**
2067 + * arm_set_cpu_power_state() - set power state synced with halt_reason
2068 + */
2069 +static inline void arm_set_cpu_power_state(ARMCPU *cpu, ARMPSCIState state)
2070 +{
2071 + CPUARMState *env = &cpu->env;
2072 +
2073 + cpu->power_state = state;
2074 + env->halt_reason = state == PSCI_OFF ? HALT_PSCI : NOT_HALTED;
2075 +}
2076 +
2077 #endif
target/arm/kvm.c
+3 -2
@@ -1149,11 +1149,12 @@ static int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu)
1149 if (cap_has_mp_state) {
1150 struct kvm_mp_state mp_state;
1151 int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MP_STATE, &mp_state);
1152 + ARMPSCIState state;
1153 if (ret) {
1154 return ret;
1155 }
1155 - cpu->power_state = (mp_state.mp_state == KVM_MP_STATE_STOPPED) ?
1156 - PSCI_OFF : PSCI_ON;
1156 + state = (mp_state.mp_state == KVM_MP_STATE_STOPPED) ? PSCI_OFF : PSCI_ON;
1157 + arm_set_cpu_power_state(cpu, state);
1158 }
1159 return 0;
1160 }
target/arm/machine.c
+1 -1
@@ -916,7 +916,7 @@ static int get_power(QEMUFile *f, void *opaque, size_t size,
916 {
917 ARMCPU *cpu = opaque;
918 bool powered_off = qemu_get_byte(f);
919 - cpu->power_state = powered_off ? PSCI_OFF : PSCI_ON;
919 + arm_set_cpu_power_state(cpu, powered_off ? PSCI_OFF : PSCI_ON);
920 return 0;
921 }
922
target/arm/tcg/op_helper.c
+3
@@ -402,6 +402,7 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len)
402 target_el);
403 }
404
405 + env->halt_reason = HALT_WFI;
406 cs->exception_index = EXCP_HLT;
407 cs->halted = 1;
408 cpu_loop_exit(cs);
@@ -463,6 +464,7 @@ void HELPER(wfit)(CPUARMState *env, uint32_t rd)
464 } else {
465 timer_mod(cpu->wfxt_timer, nexttick);
466 }
467 + env->halt_reason = HALT_WFI;
468 cs->exception_index = EXCP_HLT;
469 cs->halted = 1;
470 cpu_loop_exit(cs);
@@ -507,6 +509,7 @@ void HELPER(wfe)(CPUARMState *env)
509 return;
510 }
511
512 + env->halt_reason = HALT_WFE;
513 cs->exception_index = EXCP_HLT;
514 cs->halted = 1;
515 cpu_loop_exit(cs);