To set the local interrupt controller state, perform hv calls retrieving partition state from the hypervisor.
Signed-off-by: Magnus Kulke <magnusku...@linux.microsoft.com> --- target/i386/mshv/mshv-cpu.c | 120 ++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index ad42a09b99..dd856a2242 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -17,6 +17,7 @@ #include "qemu/atomic.h" #include "qemu/lockable.h" #include "qemu/error-report.h" +#include "qemu/memalign.h" #include "qemu/typedefs.h" #include "system/mshv.h" @@ -108,6 +109,10 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = { HV_X64_REGISTER_XMM_CONTROL_STATUS, }; +/* Defines poached from apicdef.h kernel header. */ +static u_int32_t APIC_MODE_NMI = 0x4; +static u_int32_t APIC_MODE_EXTINT = 0x7; + static void add_cpu_guard(int cpu_fd) { QemuMutex *guard; @@ -545,6 +550,114 @@ static int set_cpu_state(const CPUState *cpu, const MshvFPU *fpu_regs, return 0; } +static int get_vp_state(int cpu_fd, mshv_get_set_vp_state *state) +{ + int ret; + + ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, state); + if (ret < 0) { + error_report("failed to get partition state: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int get_lapic(int cpu_fd, + struct hv_local_interrupt_controller_state *state) +{ + int ret; + size_t size = 4096; + /* buffer aligned to 4k, as *state requires that */ + void *buffer = qemu_memalign(size, size); + struct mshv_get_set_vp_state mshv_state = { 0 }; + + mshv_state.buf_ptr = (uint64_t) buffer; + mshv_state.buf_sz = size; + mshv_state.type = MSHV_VP_STATE_LAPIC; + + ret = get_vp_state(cpu_fd, &mshv_state); + if (ret == 0) { + memcpy(state, buffer, sizeof(*state)); + } + qemu_vfree(buffer); + if (ret < 0) { + error_report("failed to get lapic"); + return -1; + } + + return 0; +} + +static uint32_t set_apic_delivery_mode(uint32_t reg, uint32_t mode) +{ + return ((reg) & ~0x700) | ((mode) << 8); +} + +static int set_vp_state(int cpu_fd, const mshv_get_set_vp_state *state) +{ + int ret; + + ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, state); + if (ret < 0) { + error_report("failed to set partition state: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int set_lapic(int cpu_fd, + const struct hv_local_interrupt_controller_state *state) +{ + int ret; + size_t size = 4096; + /* buffer aligned to 4k, as *state requires that */ + void *buffer = qemu_memalign(size, size); + struct mshv_get_set_vp_state mshv_state = { 0 }; + + if (!state) { + error_report("lapic state is NULL"); + return -1; + } + memcpy(buffer, state, sizeof(*state)); + + mshv_state.buf_ptr = (uint64_t) buffer; + mshv_state.buf_sz = size; + mshv_state.type = MSHV_VP_STATE_LAPIC; + + ret = set_vp_state(cpu_fd, &mshv_state); + qemu_vfree(buffer); + if (ret < 0) { + error_report("failed to set lapic: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int set_lint(int cpu_fd) +{ + int ret; + uint32_t *lvt_lint0, *lvt_lint1; + + struct hv_local_interrupt_controller_state lapic_state = { 0 }; + ret = get_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + return ret; + } + + lvt_lint0 = &lapic_state.apic_lvt_lint0; + *lvt_lint0 = set_apic_delivery_mode(*lvt_lint0, APIC_MODE_EXTINT); + + lvt_lint1 = &lapic_state.apic_lvt_lint1; + *lvt_lint1 = set_apic_delivery_mode(*lvt_lint1, APIC_MODE_NMI); + + /* TODO: should we skip setting lapic if the values are the same? */ + + return set_lapic(cpu_fd, &lapic_state); +} + /* * TODO: populate topology info: * @@ -556,6 +669,7 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu, uint64_t xcr0) { int ret; + int cpu_fd = mshv_vcpufd(cpu); ret = set_cpu_state(cpu, fpu, xcr0); if (ret < 0) { @@ -563,6 +677,12 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu, return -1; } + ret = set_lint(cpu_fd); + if (ret < 0) { + error_report("failed to set lpic int"); + return -1; + } + return 0; } -- 2.34.1