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>
This commit is contained in:
committed by
Paolo Bonzini
parent
72bef33b15
commit
478e27cf87
@@ -8,7 +8,6 @@ struct AccelCPUState {
|
||||
bool interruptable;
|
||||
bool ready_for_pic_interrupt;
|
||||
uint64_t tpr;
|
||||
uint64_t apic_base;
|
||||
bool interruption_pending;
|
||||
/* Must be the last field as it may have a tail */
|
||||
WHV_RUN_VP_EXIT_CONTEXT exit_ctx;
|
||||
|
||||
@@ -139,7 +139,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = {
|
||||
#ifdef TARGET_X86_64
|
||||
WHvX64RegisterKernelGsBase,
|
||||
#endif
|
||||
WHvX64RegisterApicBase,
|
||||
/* WHvX64RegisterPat, */
|
||||
WHvX64RegisterSysenterCs,
|
||||
WHvX64RegisterSysenterEip,
|
||||
@@ -420,7 +419,6 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
|
||||
r86 = !(env->cr[0] & CR0_PE_MASK);
|
||||
|
||||
vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
|
||||
vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
|
||||
|
||||
idx = 0;
|
||||
|
||||
@@ -538,9 +536,6 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
|
||||
vcxt.values[idx++].Reg64 = env->kernelgsbase;
|
||||
#endif
|
||||
|
||||
assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
|
||||
vcxt.values[idx++].Reg64 = vcpu->apic_base;
|
||||
|
||||
/* WHvX64RegisterPat - Skipped */
|
||||
|
||||
assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
|
||||
@@ -575,6 +570,12 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
|
||||
error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
|
||||
hr);
|
||||
}
|
||||
|
||||
if (level >= WHPX_LEVEL_FULL_STATE) {
|
||||
WHV_REGISTER_VALUE apic_base = {};
|
||||
apic_base.Reg64 = cpu_get_apic_base(X86_CPU(cpu)->apic_state);
|
||||
whpx_set_reg(cpu, WHvX64RegisterApicBase, apic_base);
|
||||
}
|
||||
}
|
||||
|
||||
static int whpx_get_tsc(CPUState *cpu)
|
||||
@@ -666,7 +667,7 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
|
||||
X86CPU *x86_cpu = X86_CPU(cpu);
|
||||
CPUX86State *env = &x86_cpu->env;
|
||||
struct whpx_register_set vcxt;
|
||||
uint64_t tpr, apic_base;
|
||||
uint64_t tpr;
|
||||
HRESULT hr;
|
||||
int idx;
|
||||
int idx_next;
|
||||
@@ -798,13 +799,6 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
|
||||
env->kernelgsbase = vcxt.values[idx++].Reg64;
|
||||
#endif
|
||||
|
||||
assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
|
||||
apic_base = vcxt.values[idx++].Reg64;
|
||||
if (apic_base != vcpu->apic_base) {
|
||||
vcpu->apic_base = apic_base;
|
||||
cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base);
|
||||
}
|
||||
|
||||
/* WHvX64RegisterPat - Skipped */
|
||||
|
||||
assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
|
||||
@@ -2082,8 +2076,7 @@ int whpx_vcpu_run(CPUState *cpu)
|
||||
val = X86_CPU(cpu)->env.apic_bus_freq;
|
||||
}
|
||||
|
||||
if (!whpx_irqchip_in_kernel() &&
|
||||
vcpu->exit_ctx.MsrAccess.MsrNumber == MSR_IA32_APICBASE) {
|
||||
if (vcpu->exit_ctx.MsrAccess.MsrNumber == MSR_IA32_APICBASE) {
|
||||
is_known_msr = 1;
|
||||
if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
|
||||
/* Read path unreachable on Hyper-V */
|
||||
@@ -2233,6 +2226,13 @@ int whpx_vcpu_run(CPUState *cpu)
|
||||
} else {
|
||||
reg_values[2].Reg32 &= ~CPUID_EXT_X2APIC;
|
||||
}
|
||||
|
||||
/* CPUID[1:EDX].APIC is dynamic */
|
||||
if (env->features[FEAT_1_EDX] & CPUID_APIC) {
|
||||
reg_values[3].Reg32 |= CPUID_APIC;
|
||||
} else {
|
||||
reg_values[3].Reg32 &= ~CPUID_APIC;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dynamic depending on XCR0 and XSS, so query DefaultResult */
|
||||
@@ -2804,9 +2804,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
|
||||
|
||||
memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
|
||||
prop.X64MsrExitBitmap.UnhandledMsrs = 1;
|
||||
if (!whpx_irqchip_in_kernel()) {
|
||||
prop.X64MsrExitBitmap.ApicBaseMsrWrite = 1;
|
||||
}
|
||||
prop.X64MsrExitBitmap.ApicBaseMsrWrite = 1;
|
||||
|
||||
hr = whp_dispatch.WHvSetPartitionProperty(
|
||||
whpx->partition,
|
||||
|
||||
@@ -90,9 +90,70 @@ static void whpx_get_apic_state(APICCommonState *s,
|
||||
apic_next_timer(s, s->initial_count_load_time);
|
||||
}
|
||||
|
||||
static int whpx_apic_set_base(APICCommonState *s, uint64_t val)
|
||||
static int apic_set_base_check(APICCommonState *s, uint64_t val)
|
||||
{
|
||||
s->apicbase = val;
|
||||
/* Enable x2apic when x2apic is not supported by CPU */
|
||||
if (!cpu_has_x2apic_feature(&s->cpu->env) &&
|
||||
val & MSR_IA32_APICBASE_EXTD) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transition into invalid state
|
||||
* (s->apicbase & MSR_IA32_APICBASE_ENABLE == 0) &&
|
||||
* (s->apicbase & MSR_IA32_APICBASE_EXTD) == 1
|
||||
*/
|
||||
if (!(val & MSR_IA32_APICBASE_ENABLE) &&
|
||||
(val & MSR_IA32_APICBASE_EXTD)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Invalid transition from disabled mode to x2APIC */
|
||||
if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
|
||||
!(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
|
||||
(val & MSR_IA32_APICBASE_ENABLE) &&
|
||||
(val & MSR_IA32_APICBASE_EXTD)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Invalid transition from x2APIC to xAPIC */
|
||||
if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
|
||||
(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
|
||||
(val & MSR_IA32_APICBASE_ENABLE) &&
|
||||
!(val & MSR_IA32_APICBASE_EXTD)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apic_set_base(APICCommonState *s, uint64_t val)
|
||||
{
|
||||
if (apic_set_base_check(s, val) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->apicbase = (val & MSR_IA32_APICBASE_BASE) |
|
||||
(s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
|
||||
if (!(val & MSR_IA32_APICBASE_ENABLE)) {
|
||||
s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
|
||||
cpu_clear_apic_feature(&s->cpu->env);
|
||||
}
|
||||
|
||||
/* Transition from disabled mode to xAPIC */
|
||||
if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
|
||||
(val & MSR_IA32_APICBASE_ENABLE)) {
|
||||
s->apicbase |= MSR_IA32_APICBASE_ENABLE;
|
||||
cpu_set_apic_feature(&s->cpu->env);
|
||||
}
|
||||
|
||||
/* Transition from xAPIC to x2APIC */
|
||||
if (cpu_has_x2apic_feature(&s->cpu->env) &&
|
||||
!(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
|
||||
(val & MSR_IA32_APICBASE_EXTD)) {
|
||||
s->apicbase |= MSR_IA32_APICBASE_EXTD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -235,6 +296,10 @@ static void whpx_apic_mem_write(void *opaque, hwaddr addr,
|
||||
static const MemoryRegionOps whpx_apic_io_ops = {
|
||||
.read = whpx_apic_mem_read,
|
||||
.write = whpx_apic_mem_write,
|
||||
.impl.min_access_size = 1,
|
||||
.impl.max_access_size = 4,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
@@ -262,7 +327,7 @@ static void whpx_apic_class_init(ObjectClass *klass, const void *data)
|
||||
|
||||
k->realize = whpx_apic_realize;
|
||||
k->reset = whpx_apic_reset;
|
||||
k->set_base = whpx_apic_set_base;
|
||||
k->set_base = apic_set_base;
|
||||
k->set_tpr = whpx_apic_set_tpr;
|
||||
k->get_tpr = whpx_apic_get_tpr;
|
||||
k->post_load = whpx_apic_post_load;
|
||||
|
||||
Reference in New Issue
Block a user