This change implements loading and storing the hyperv lapic state as part of the load/store routines for a vcpu.
The HyperV LAPIC is similar to the the split-irqchip in KVM, it will only handle MSI/X interrupts. PIC and IOAPIC have to be handled in userland. An opaque blob is added to the APICCommonState, guarded behind a flag, hence it will be covered by a migration, as we declare VMSTATE_BUFFER for the hv_lapic_state field. In the future we might want to introduce a dedicated class for MSHV, that would require us to wire up an IOAPIC delivery path to QEMU's userland emulation. Signed-off-by: Magnus Kulke <[email protected]> --- hw/intc/apic_common.c | 3 ++ include/hw/i386/apic_internal.h | 5 +++ target/i386/mshv/mshv-cpu.c | 61 +++++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index bf4abc21d7..a7df870f1a 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -380,6 +380,9 @@ static const VMStateDescription vmstate_apic_common = { VMSTATE_INT64(next_time, APICCommonState), VMSTATE_INT64(timer_expiry, APICCommonState), /* open-coded timer state */ +#ifdef CONFIG_MSHV + VMSTATE_BUFFER(hv_lapic_state, APICCommonState), +#endif VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * const []) { diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h index 0cb06bbc76..6d4ccca4e8 100644 --- a/include/hw/i386/apic_internal.h +++ b/include/hw/i386/apic_internal.h @@ -23,6 +23,7 @@ #include "cpu.h" #include "hw/i386/apic.h" +#include "hw/hyperv/hvgdk_mini.h" #include "system/memory.h" #include "qemu/timer.h" #include "target/i386/cpu-qom.h" @@ -188,6 +189,10 @@ struct APICCommonState { DeviceState *vapic; hwaddr vapic_paddr; /* note: persistence via kvmvapic */ uint32_t extended_log_dest; + +#ifdef CONFIG_MSHV + uint8_t hv_lapic_state[sizeof(struct hv_local_interrupt_controller_state)]; +#endif }; typedef struct VAPICState { diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 54c262d8bc..906f5b0c3d 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -112,6 +112,25 @@ static int get_generic_regs(CPUState *cpu, struct hv_register_assoc *assocs, size_t n_regs); +static int get_lapic(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + APICCommonState *apic = APIC_COMMON(x86cpu->apic_state); + int cpu_fd = mshv_vcpufd(cpu); + int ret; + struct hv_local_interrupt_controller_state lapic_state = { 0 }; + + ret = mshv_get_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + error_report("failed to get lapic state"); + return -1; + } + + memcpy(&apic->hv_lapic_state, &lapic_state, sizeof(lapic_state)); + + return 0; +} + static void populate_fpu(const hv_register_assoc *assocs, X86CPU *x86cpu) { union hv_register_value value; @@ -559,6 +578,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu) return ret; } + ret = get_lapic(cpu); + if (ret < 0) { + return ret; + } + return 0; } @@ -952,9 +976,11 @@ int mshv_set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state) static int init_lint(const CPUState *cpu) { - int ret; + X86CPU *x86cpu = X86_CPU(cpu); + APICCommonState *apic = APIC_COMMON(x86cpu->apic_state); uint32_t *lvt_lint0, *lvt_lint1; int cpu_fd = mshv_vcpufd(cpu); + int ret; struct hv_local_interrupt_controller_state lapic_state = { 0 }; ret = mshv_get_lapic(cpu_fd, &lapic_state); @@ -970,7 +996,32 @@ static int init_lint(const CPUState *cpu) /* TODO: should we skip setting lapic if the values are the same? */ - return mshv_set_lapic(cpu_fd, &lapic_state); + ret = mshv_set_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + return -1; + } + + memcpy(apic->hv_lapic_state, &lapic_state, sizeof(lapic_state)); + + return 0; +} + +static int set_lapic(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + APICCommonState *apic = APIC_COMMON(x86cpu->apic_state); + int cpu_fd = mshv_vcpufd(cpu); + int ret; + + struct hv_local_interrupt_controller_state lapic_state = { 0 }; + memcpy(&lapic_state, &apic->hv_lapic_state, sizeof(lapic_state)); + ret = mshv_set_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + error_report("failed to set lapic"); + return -1; + } + + return 0; } int mshv_arch_store_vcpu_state(const CPUState *cpu) @@ -997,6 +1048,12 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu) return ret; } + /* INVARIANT: special regs (APIC_BASE) must be restored before LAPIC */ + ret = set_lapic(cpu); + if (ret < 0) { + return ret; + } + return 0; } -- 2.34.1
