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 <magnuskulke@linux.microsoft.com>
---
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