@samitouri / QOSamiQemu / commits / 478e27cf87

whpx: i386: fix CPUID[1:EDX].APIC reporting

Hyper-V always has CPUID[1:EDX].APIC set, even when the APIC isn't enabled yet. Work around this by also using the APICBASE trap for kernel-irqchip=on. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Link: https://lore.kernel.org/r/20260422214225.2242-16-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Mohamed Mediouni committed Apr 22, 2026 at 23:42 UTC 478e27cf879ec2d12deb358d1c7fddb7935fef30
3 files changed +84 -22
include/system/whpx-common.h
-1
@@ -8,7 +8,6 @@ struct AccelCPUState {
8 bool interruptable;
9 bool ready_for_pic_interrupt;
10 uint64_t tpr;
11 - uint64_t apic_base;
11 bool interruption_pending;
12 /* Must be the last field as it may have a tail */
13 WHV_RUN_VP_EXIT_CONTEXT exit_ctx;
target/i386/whpx/whpx-all.c
+16 -18
@@ -139,7 +139,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = {
139 #ifdef TARGET_X86_64
140 WHvX64RegisterKernelGsBase,
141 #endif
142 - WHvX64RegisterApicBase,
142 /* WHvX64RegisterPat, */
143 WHvX64RegisterSysenterCs,
144 WHvX64RegisterSysenterEip,
@@ -420,7 +419,6 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
419 r86 = !(env->cr[0] & CR0_PE_MASK);
420
421 vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
423 - vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
422
423 idx = 0;
424
@@ -538,9 +536,6 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
536 vcxt.values[idx++].Reg64 = env->kernelgsbase;
537 #endif
538
541 - assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
542 - vcxt.values[idx++].Reg64 = vcpu->apic_base;
543 -
539 /* WHvX64RegisterPat - Skipped */
540
541 assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
@@ -575,6 +570,12 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
570 error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
571 hr);
572 }
573 +
574 + if (level >= WHPX_LEVEL_FULL_STATE) {
575 + WHV_REGISTER_VALUE apic_base = {};
576 + apic_base.Reg64 = cpu_get_apic_base(X86_CPU(cpu)->apic_state);
577 + whpx_set_reg(cpu, WHvX64RegisterApicBase, apic_base);
578 + }
579 }
580
581 static int whpx_get_tsc(CPUState *cpu)
@@ -666,7 +667,7 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
667 X86CPU *x86_cpu = X86_CPU(cpu);
668 CPUX86State *env = &x86_cpu->env;
669 struct whpx_register_set vcxt;
669 - uint64_t tpr, apic_base;
670 + uint64_t tpr;
671 HRESULT hr;
672 int idx;
673 int idx_next;
@@ -798,13 +799,6 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
799 env->kernelgsbase = vcxt.values[idx++].Reg64;
800 #endif
801
801 - assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
802 - apic_base = vcxt.values[idx++].Reg64;
803 - if (apic_base != vcpu->apic_base) {
804 - vcpu->apic_base = apic_base;
805 - cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base);
806 - }
807 -
802 /* WHvX64RegisterPat - Skipped */
803
804 assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
@@ -2082,8 +2076,7 @@ int whpx_vcpu_run(CPUState *cpu)
2076 val = X86_CPU(cpu)->env.apic_bus_freq;
2077 }
2078
2085 - if (!whpx_irqchip_in_kernel() &&
2086 - vcpu->exit_ctx.MsrAccess.MsrNumber == MSR_IA32_APICBASE) {
2079 + if (vcpu->exit_ctx.MsrAccess.MsrNumber == MSR_IA32_APICBASE) {
2080 is_known_msr = 1;
2081 if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
2082 /* Read path unreachable on Hyper-V */
@@ -2233,6 +2226,13 @@ int whpx_vcpu_run(CPUState *cpu)
2226 } else {
2227 reg_values[2].Reg32 &= ~CPUID_EXT_X2APIC;
2228 }
2229 +
2230 + /* CPUID[1:EDX].APIC is dynamic */
2231 + if (env->features[FEAT_1_EDX] & CPUID_APIC) {
2232 + reg_values[3].Reg32 |= CPUID_APIC;
2233 + } else {
2234 + reg_values[3].Reg32 &= ~CPUID_APIC;
2235 + }
2236 }
2237
2238 /* Dynamic depending on XCR0 and XSS, so query DefaultResult */
@@ -2804,9 +2804,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
2804
2805 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
2806 prop.X64MsrExitBitmap.UnhandledMsrs = 1;
2807 - if (!whpx_irqchip_in_kernel()) {
2808 - prop.X64MsrExitBitmap.ApicBaseMsrWrite = 1;
2809 - }
2807 + prop.X64MsrExitBitmap.ApicBaseMsrWrite = 1;
2808
2809 hr = whp_dispatch.WHvSetPartitionProperty(
2810 whpx->partition,
target/i386/whpx/whpx-apic.c
+68 -3
@@ -90,9 +90,70 @@ static void whpx_get_apic_state(APICCommonState *s,
90 apic_next_timer(s, s->initial_count_load_time);
91 }
92
93 -static int whpx_apic_set_base(APICCommonState *s, uint64_t val)
93 +static int apic_set_base_check(APICCommonState *s, uint64_t val)
94 {
95 - s->apicbase = val;
95 + /* Enable x2apic when x2apic is not supported by CPU */
96 + if (!cpu_has_x2apic_feature(&s->cpu->env) &&
97 + val & MSR_IA32_APICBASE_EXTD) {
98 + return -1;
99 + }
100 +
101 + /*
102 + * Transition into invalid state
103 + * (s->apicbase & MSR_IA32_APICBASE_ENABLE == 0) &&
104 + * (s->apicbase & MSR_IA32_APICBASE_EXTD) == 1
105 + */
106 + if (!(val & MSR_IA32_APICBASE_ENABLE) &&
107 + (val & MSR_IA32_APICBASE_EXTD)) {
108 + return -1;
109 + }
110 +
111 + /* Invalid transition from disabled mode to x2APIC */
112 + if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
113 + !(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
114 + (val & MSR_IA32_APICBASE_ENABLE) &&
115 + (val & MSR_IA32_APICBASE_EXTD)) {
116 + return -1;
117 + }
118 +
119 + /* Invalid transition from x2APIC to xAPIC */
120 + if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
121 + (s->apicbase & MSR_IA32_APICBASE_EXTD) &&
122 + (val & MSR_IA32_APICBASE_ENABLE) &&
123 + !(val & MSR_IA32_APICBASE_EXTD)) {
124 + return -1;
125 + }
126 +
127 + return 0;
128 +}
129 +
130 +static int apic_set_base(APICCommonState *s, uint64_t val)
131 +{
132 + if (apic_set_base_check(s, val) < 0) {
133 + return -1;
134 + }
135 +
136 + s->apicbase = (val & MSR_IA32_APICBASE_BASE) |
137 + (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
138 + if (!(val & MSR_IA32_APICBASE_ENABLE)) {
139 + s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
140 + cpu_clear_apic_feature(&s->cpu->env);
141 + }
142 +
143 + /* Transition from disabled mode to xAPIC */
144 + if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
145 + (val & MSR_IA32_APICBASE_ENABLE)) {
146 + s->apicbase |= MSR_IA32_APICBASE_ENABLE;
147 + cpu_set_apic_feature(&s->cpu->env);
148 + }
149 +
150 + /* Transition from xAPIC to x2APIC */
151 + if (cpu_has_x2apic_feature(&s->cpu->env) &&
152 + !(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
153 + (val & MSR_IA32_APICBASE_EXTD)) {
154 + s->apicbase |= MSR_IA32_APICBASE_EXTD;
155 + }
156 +
157 return 0;
158 }
159
@@ -235,6 +296,10 @@ static void whpx_apic_mem_write(void *opaque, hwaddr addr,
296 static const MemoryRegionOps whpx_apic_io_ops = {
297 .read = whpx_apic_mem_read,
298 .write = whpx_apic_mem_write,
299 + .impl.min_access_size = 1,
300 + .impl.max_access_size = 4,
301 + .valid.min_access_size = 1,
302 + .valid.max_access_size = 4,
303 .endianness = DEVICE_LITTLE_ENDIAN,
304 };
305
@@ -262,7 +327,7 @@ static void whpx_apic_class_init(ObjectClass *klass, const void *data)
327
328 k->realize = whpx_apic_realize;
329 k->reset = whpx_apic_reset;
265 - k->set_base = whpx_apic_set_base;
330 + k->set_base = apic_set_base;
331 k->set_tpr = whpx_apic_set_tpr;
332 k->get_tpr = whpx_apic_get_tpr;
333 k->post_load = whpx_apic_post_load;