> On 28. Oct 2026, at 13:53, Magnus Kulke <[email protected]> 
> wrote:
> 
> To inject legacy interrupts in the MSHV accelerator, we have to request
> a "deliverability notification" once the PIC asserts an interrupt. Once
> we receive a INTERRUPTION_DELIVERABLE exit from the hypervisor we can
> proceed to acknowledge the interrupt and write to the PENDING_EVENT0
> register with EXT_INT event type and the respective vector.
> 
> Since an interrupt is supposed to wake a vCPU stopped by HLT, we do
> clear the halt_suspend field of the INTERNAL_ACTIVITY_STATE register.

Keeping this bug on the Hyper-V side is a bit unfortunate. But oh well...
> 
> We also need to consider pending EXT_INT events in migration,
> splitting the mapping of pending_exc into two discrete EXCEPTION and
> EXT_INT event types.
> 
> Signed-off-by: Magnus Kulke <[email protected]>

Reviewed-by: Mohamed Mediouni <[email protected]>
> ---
> hw/i386/x86-cpu.c              |   9 +-
> include/hw/hyperv/hvgdk_mini.h |  42 ++++++++-
> include/system/mshv_int.h      |   1 +
> meson.build                    |   1 +
> target/i386/mshv/mshv-cpu.c    | 155 +++++++++++++++++++++++++++++++--
> target/i386/mshv/trace-events  |   7 ++
> target/i386/mshv/trace.h       |   2 +
> 7 files changed, 200 insertions(+), 17 deletions(-)
> create mode 100644 target/i386/mshv/trace-events
> create mode 100644 target/i386/mshv/trace.h
> 
> diff --git a/hw/i386/x86-cpu.c b/hw/i386/x86-cpu.c
> index fba313376c..7b9299272a 100644
> --- a/hw/i386/x86-cpu.c
> +++ b/hw/i386/x86-cpu.c
> @@ -46,12 +46,8 @@ static void pic_irq_request(void *opaque, int irq, int 
> level)
> 
>     trace_x86_pic_interrupt(irq, level);
> 
> -    if (mshv_irqchip_in_kernel()) {
> -        return;
> -    }
> -
>     if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() &&
> -        !whpx_irqchip_in_kernel()) {
> +        !mshv_irqchip_in_kernel() && !whpx_irqchip_in_kernel()) {
>         CPU_FOREACH(cs) {
>             cpu = X86_CPU(cs);
>             if (apic_accept_pic_intr(cpu->apic_state)) {
> @@ -77,7 +73,8 @@ int cpu_get_pic_interrupt(CPUX86State *env)
>     X86CPU *cpu = env_archcpu(env);
>     int intno;
> 
> -    if (!kvm_irqchip_in_kernel() && !whpx_irqchip_in_kernel()) {
> +    if (!kvm_irqchip_in_kernel() && !mshv_irqchip_in_kernel() &&
> +        !whpx_irqchip_in_kernel()) {
>         intno = apic_get_interrupt(cpu->apic_state);
>         if (intno >= 0) {
>             return intno;
> diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
> index 0983e3a58d..e585661b2c 100644
> --- a/include/hw/hyperv/hvgdk_mini.h
> +++ b/include/hw/hyperv/hvgdk_mini.h
> @@ -30,10 +30,11 @@ typedef enum hv_register_name {
>     HV_REGISTER_INTERNAL_ACTIVITY_STATE = 0x00000004,
> 
>     /* Pending Interruption Register */
> -    HV_REGISTER_PENDING_INTERRUPTION = 0x00010002,
> -    HV_REGISTER_INTERRUPT_STATE      = 0x00010003,
> -    HV_REGISTER_PENDING_EVENT0       = 0x00010004,
> -    HV_REGISTER_PENDING_EVENT1       = 0x00010005,
> +    HV_REGISTER_PENDING_INTERRUPTION         = 0x00010002,
> +    HV_REGISTER_INTERRUPT_STATE              = 0x00010003,
> +    HV_REGISTER_PENDING_EVENT0               = 0x00010004,
> +    HV_REGISTER_PENDING_EVENT1               = 0x00010005,
> +    HV_REGISTER_DELIVERABILITY_NOTIFICATIONS = 0x00010006,
> 
>     /* X64 User-Mode Registers */
>     HV_X64_REGISTER_RAX     = 0x00020000,
> @@ -313,6 +314,21 @@ union hv_x64_pending_exception_event {
>     };
> };
> 
> +#define HV_X64_PENDING_EVENT_EXCEPTION 0
> +#define HV_X64_PENDING_EVENT_EXT_INT   5
> +
> +union hv_x64_pending_ext_int_event {
> +    uint64_t as_uint64[2];
> +    struct {
> +        uint64_t event_pending:1;
> +        uint64_t event_type:3;
> +        uint64_t reserved0:4;
> +        uint64_t vector:8;
> +        uint64_t reserved1:48;
> +        uint64_t reserved2;
> +    };
> +};
> +
> union hv_x64_pending_virtualization_fault_event {
>     uint64_t as_uint64[2];
>     struct {
> @@ -443,6 +459,17 @@ union hv_x64_interrupt_state_register {
>     };
> };
> 
> +union hv_x64_deliverability_notifications_register {
> +    uint64_t as_uint64;
> +    struct {
> +        uint64_t nmi_notification:1;
> +        uint64_t interrupt_notification:1;
> +        uint64_t interrupt_priority:4;
> +        uint64_t reserved:42;
> +        uint64_t sints:16;
> +    };
> +};
> +
> union hv_intercept_suspend_register {
>     uint64_t as_uint64;
>     struct {
> @@ -469,6 +496,7 @@ typedef union hv_register_value {
>     union hv_x64_pending_interruption_register pending_interruption;
>     union hv_x64_msr_npiep_config_contents npiep_config;
>     union hv_x64_pending_exception_event pending_exception_event;
> +    union hv_x64_pending_ext_int_event pending_ext_int_event;
>     union hv_x64_pending_virtualization_fault_event
>         pending_virtualization_fault_event;
>     union hv_x64_register_sev_control sev_control;
> @@ -715,6 +743,12 @@ struct hv_x64_intercept_message_header {
>     uint64_t rflags;
> } QEMU_PACKED;
> 
> +struct hv_x64_interruption_deliverable_message {
> +    struct hv_x64_intercept_message_header header;
> +    uint32_t deliverable_type;
> +    uint32_t reserved;
> +} QEMU_PACKED;
> +
> union hv_x64_io_port_access_info {
>     uint8_t as_uint8;
>     struct {
> diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
> index 1f77ffd152..d527cd8d77 100644
> --- a/include/system/mshv_int.h
> +++ b/include/system/mshv_int.h
> @@ -45,6 +45,7 @@ typedef struct MshvHvCallArgs {
> struct AccelCPUState {
>     int cpufd;
>     MshvHvCallArgs hvcall_args;
> +    bool pic_interrupt_window;
> };
> 
> typedef struct MshvMemoryListener {
> diff --git a/meson.build b/meson.build
> index bbef9d9c29..24caec97a9 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -3686,6 +3686,7 @@ if have_system or have_user
>     'target/hppa',
>     'target/i386',
>     'target/i386/kvm',
> +    'target/i386/mshv',
>     'target/i386/whpx',
>     'target/loongarch',
>     'target/mips/tcg',
> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index e3c155da2e..7032e0f6ac 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -771,6 +771,7 @@ static int get_vcpu_events(CPUState *cpu)
>     union hv_x64_pending_interruption_register pending_int;
>     union hv_x64_interrupt_state_register int_state;
>     union hv_x64_pending_exception_event pending_exc;
> +    union hv_x64_pending_ext_int_event pending_ext_int;
>     int ret;
> 
>     ret = mshv_get_generic_regs(cpu, assocs, ARRAY_SIZE(assocs));
> @@ -782,6 +783,7 @@ static int get_vcpu_events(CPUState *cpu)
>     pending_int.as_uint64 = assocs[0].value.reg64;
>     int_state.as_uint64 = assocs[1].value.reg64;
>     pending_exc = assocs[2].value.pending_exception_event;
> +    pending_ext_int = assocs[2].value.pending_ext_int_event;
> 
>     /* Clear previous state. injected ints/excs are blanked w/ -1 */
>     env->interrupt_injected    = -1;
> @@ -841,14 +843,25 @@ static int get_vcpu_events(CPUState *cpu)
>         env->hflags2 &= ~HF2_NMI_MASK;
>     }
> 
> -    /* HV_REGISTER_PENDING_EVENT0: pending exception not yet injected */
> +    /* HV_REGISTER_PENDING_EVENT0: event not yet injected */
>     if (pending_exc.event_pending) {
> -        env->exception_pending     = 1;
> -        env->exception_nr          = pending_exc.vector;
> -        env->has_error_code        = pending_exc.deliver_error_code;
> -        env->error_code            = pending_exc.error_code;
> -        env->exception_has_payload = (pending_exc.exception_parameter != 0);
> -        env->exception_payload     = pending_exc.exception_parameter;
> +        switch (pending_exc.event_type) {
> +        case HV_X64_PENDING_EVENT_EXCEPTION:
> +            env->exception_pending     = 1;
> +            env->exception_nr          = pending_exc.vector;
> +            env->has_error_code        = pending_exc.deliver_error_code;
> +            env->error_code            = pending_exc.error_code;
> +            env->exception_has_payload = pending_exc.exception_parameter != 
> 0;
> +            env->exception_payload     = pending_exc.exception_parameter;
> +            break;
> +        case HV_X64_PENDING_EVENT_EXT_INT:
> +            env->interrupt_injected = pending_ext_int.vector;
> +            break;
> +        default:
> +            error_report("unknown pending event type %u",
> +                         pending_exc.event_type);
> +            return -EINVAL;
> +        }
>     }
> 
>     /*
> @@ -2091,6 +2104,119 @@ static int handle_exception_interrupt(CPUState *cpu,
>     return 0;
> }
> 
> +static int set_pic_interrupt_window(CPUState *cpu, bool enabled)
> +{
> +    union hv_x64_deliverability_notifications_register notifications = { 0 };
> +    struct hv_register_assoc assoc = { 0 };
> +    AccelCPUState *cpu_state = cpu->accel;
> +    int ret;
> +
> +    notifications.interrupt_notification = enabled;
> +
> +    assoc.name        = HV_REGISTER_DELIVERABILITY_NOTIFICATIONS;
> +    assoc.value.reg64 = notifications.as_uint64;
> +
> +    /* we are already waiting for a PIC interrupt window */
> +    if (cpu_state->pic_interrupt_window == enabled) {
> +        return 0;
> +    }
> +
> +    trace_mshv_set_pic_interrupt_window(cpu->cpu_index, enabled);
> +
> +    ret = mshv_set_generic_regs(cpu, &assoc, 1);
> +    if (ret < 0) {
> +        error_report("failed to set deliverability notifications register");
> +        return ret;
> +    }
> +
> +    cpu_state->pic_interrupt_window = enabled;
> +    return 0;
> +}
> +
> +static inline int request_pic_interrupt_window(CPUState *cpu)
> +{
> +    return set_pic_interrupt_window(cpu, true);
> +}
> +
> +static inline int clear_pic_interrupt_window(CPUState *cpu)
> +{
> +    return set_pic_interrupt_window(cpu, false);
> +}
> +
> +static int handle_interrupt_deliverable(CPUState *cpu,
> +                                        const struct hyperv_message *msg)
> +{
> +    CPUX86State *env = &X86_CPU(cpu)->env;
> +    struct hv_x64_interruption_deliverable_message info;
> +    union hv_x64_pending_ext_int_event event = { 0 };
> +    struct hv_register_assoc assocs[2] = { 0 };
> +    struct hv_register_assoc *event_assoc = &assocs[0],
> +                             *activity_assoc = &assocs[1];
> +    union hv_internal_activity_register activity;
> +    bool is_suspended;
> +    int ret, vector;
> +
> +    memcpy(&info, msg->payload, sizeof(info));
> +    if (info.deliverable_type != MSHV_HV_INTERRUPTION_TYPE_EXT_INT) {
> +        error_report("unexpected deliverable interruption type %u",
> +                     info.deliverable_type);
> +        return -1;
> +    }
> +
> +    ret = clear_pic_interrupt_window(cpu);
> +    if (ret < 0) {
> +        error_report("failed to clear PIC interrupt window");
> +        return -1;
> +    }
> +
> +    bql_lock();
> +    cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD);
> +    vector = cpu_get_pic_interrupt(env);
> +    bql_unlock();
> +
> +    /* vector == -1 means the PIC has no longer a pending interrupt */
> +    if (vector < 0) {
> +        return 0;
> +    }
> +
> +    trace_mshv_pic_interrupt(cpu->cpu_index, vector);
> +
> +    /* we check whether the vcpu is halted first */
> +    activity_assoc->name = HV_REGISTER_INTERNAL_ACTIVITY_STATE;
> +    ret = mshv_get_generic_regs(cpu, activity_assoc, 1);
> +    if (ret < 0) {
> +        error_report("failed to read activity state before PIC delivery");
> +        return -1;
> +    }
> +
> +    event.event_pending = 1;
> +    event.event_type    = HV_X64_PENDING_EVENT_EXT_INT;
> +    event.vector        = vector;
> +
> +    event_assoc->name                        = HV_REGISTER_PENDING_EVENT0;
> +    event_assoc->value.pending_ext_int_event = event;
> +
> +    /*
> +     * If the vcpu was hlt suspended we clear the suspend bit, otherwise we
> +     * only set the pending event.
> +     */
> +    activity = activity_assoc->value.internal_activity;
> +    is_suspended = activity.halt_suspend;
> +    if (is_suspended) {
> +        activity.halt_suspend = 0;
> +        activity_assoc->value.internal_activity = activity;
> +    }
> +
> +    ret = mshv_set_generic_regs(cpu, assocs, is_suspended ? 2 : 1);
> +    if (ret < 0) {
> +        error_report("failed to set pending event%s",
> +                     is_suspended ? " and activity state" : "");
> +        return ret;
> +    }
> +
> +    return 0;
> +}
> +
> /*
>  * Flip RFLAGS.TF like WHPX. Set it only on the live register, not 
> env->eflags,
>  * so a later store won't put it back.
> @@ -2139,6 +2265,14 @@ int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message 
> *msg, MshvVmExit *exit)
>         }
>     }
> 
> +    if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) {
> +        ret = request_pic_interrupt_window(cpu);
> +        if (ret < 0) {
> +            error_report("failed to request PIC interrupt window");
> +            return -1;
> +        }
> +    }
> +
>     ret = ioctl(cpu_fd, MSHV_RUN_VP, msg);
>     if (ret < 0) {
>         if (errno == EINTR) {
> @@ -2185,6 +2319,13 @@ int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message 
> *msg, MshvVmExit *exit)
>             return -1;
>         }
>         return 0;
> +    case HVMSG_X64_INTERRUPTION_DELIVERABLE:
> +        ret = handle_interrupt_deliverable(cpu, msg);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +        *exit = MshvVmExitIgnore;
> +        return 0;
>     default:
>         break;
>     }
> diff --git a/target/i386/mshv/trace-events b/target/i386/mshv/trace-events
> new file mode 100644
> index 0000000000..347ce040ab
> --- /dev/null
> +++ b/target/i386/mshv/trace-events
> @@ -0,0 +1,7 @@
> +# Authors: Magnus Kulke <[email protected]>
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +mshv_run_vcpu_eintr(uint32_t cpu) "cpu_index=%d"
> +mshv_set_pic_interrupt_window(uint32_t cpu, bool enabled) "cpu_index=%d 
> enabled=%d"
> +mshv_pic_interrupt(uint32_t cpu, uint8_t vector) "cpu_index=%d vector=0x%x"
> diff --git a/target/i386/mshv/trace.h b/target/i386/mshv/trace.h
> new file mode 100644
> index 0000000000..81b6b655c7
> --- /dev/null
> +++ b/target/i386/mshv/trace.h
> @@ -0,0 +1,2 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +#include "trace/trace-target_i386_mshv.h"
> -- 
> 2.34.1
> 
> 


Reply via email to