@samitouri / QOSamiQemu / commits / b2c0cc6300

target/i386/mshv: migrate pending ints/excs

We use PENDING_INTERRUPTION, INTERRUPT_STATE, PENDING_EVENT hv registers to map and roundtrip from/to CPUX86State. We ignore HV_REGISTER_PENDING_EVENT1 which represent events for nested virt contexts, as we don't support nested virt with MSHV currently. Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com> Link: https://lore.kernel.org/r/20260417105618.3621-30-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Magnus Kulke committed Apr 17, 2026 at 12:56 UTC b2c0cc6300f0a5fd1b5c69926565b6f9982962c2
3 files changed +185
include/hw/hyperv/hvgdk_mini.h
+3
@@ -12,6 +12,9 @@
12 typedef enum hv_register_name {
13 /* Pending Interruption Register */
14 HV_REGISTER_PENDING_INTERRUPTION = 0x00010002,
15 + HV_REGISTER_INTERRUPT_STATE = 0x00010003,
16 + HV_REGISTER_PENDING_EVENT0 = 0x00010004,
17 + HV_REGISTER_PENDING_EVENT1 = 0x00010005,
18
19 /* X64 User-Mode Registers */
20 HV_X64_REGISTER_RAX = 0x00020000,
include/system/mshv_int.h
+14
@@ -17,6 +17,20 @@
17 #include "hw/hyperv/hvhdk.h"
18
19 #define MSHV_MSR_ENTRIES_COUNT 64
20 +
21 +/*
22 + * Interruption-type encoding, used by the hypervisor in
23 + * hv_x64_pending_interruption_register.interruption_type
24 + * See TLFS 6.0 section 7.9.2, p55
25 + * https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs
26 + */
27 +#define MSHV_HV_INTERRUPTION_TYPE_EXT_INT 0
28 +#define MSHV_HV_INTERRUPTION_TYPE_NMI 2
29 +#define MSHV_HV_INTERRUPTION_TYPE_HW_EXC 3
30 +#define MSHV_HV_INTERRUPTION_TYPE_SW_INT 4
31 +#define MSHV_HV_INTERRUPTION_TYPE_PRIV_SW_EXC 5
32 +#define MSHV_HV_INTERRUPTION_TYPE_SW_EXC 6
33 +
34 #define MSHV_DIRTY_PAGES_BATCH_SIZE 0x10000
35
36 typedef struct hyperv_message hv_message;
target/i386/mshv/mshv-cpu.c
+168
@@ -535,6 +535,164 @@ static int load_regs(CPUState *cpu)
535 return 0;
536 }
537
538 +static int get_vcpu_events(CPUState *cpu)
539 +{
540 + X86CPU *x86cpu = X86_CPU(cpu);
541 + CPUX86State *env = &x86cpu->env;
542 + struct hv_register_assoc assocs[] = {
543 + { .name = HV_REGISTER_PENDING_INTERRUPTION },
544 + { .name = HV_REGISTER_INTERRUPT_STATE },
545 + { .name = HV_REGISTER_PENDING_EVENT0 },
546 + };
547 + union hv_x64_pending_interruption_register pending_int;
548 + union hv_x64_interrupt_state_register int_state;
549 + union hv_x64_pending_exception_event pending_exc;
550 + int ret;
551 +
552 + ret = mshv_get_generic_regs(cpu, assocs, ARRAY_SIZE(assocs));
553 + if (ret < 0) {
554 + error_report("failed to get vcpu event registers");
555 + return -1;
556 + }
557 +
558 + pending_int.as_uint64 = assocs[0].value.reg64;
559 + int_state.as_uint64 = assocs[1].value.reg64;
560 + pending_exc = assocs[2].value.pending_exception_event;
561 +
562 + /* Clear previous state. injected ints/excs are blanked w/ -1 */
563 + env->interrupt_injected = -1;
564 + env->soft_interrupt = 0;
565 + env->exception_injected = 0;
566 + env->exception_pending = 0;
567 + env->exception_nr = -1;
568 + env->has_error_code = 0;
569 + env->error_code = 0;
570 + env->exception_has_payload = 0;
571 + env->exception_payload = 0;
572 + env->nmi_injected = 0;
573 +
574 + if (pending_int.interruption_pending) {
575 + switch (pending_int.interruption_type) {
576 + case MSHV_HV_INTERRUPTION_TYPE_EXT_INT:
577 + env->interrupt_injected = pending_int.interruption_vector;
578 + break;
579 + case MSHV_HV_INTERRUPTION_TYPE_NMI:
580 + env->nmi_injected = 1;
581 + break;
582 + case MSHV_HV_INTERRUPTION_TYPE_HW_EXC:
583 + env->exception_injected = 1;
584 + env->exception_nr = pending_int.interruption_vector;
585 + env->has_error_code = pending_int.deliver_error_code;
586 + env->error_code = pending_int.error_code;
587 + break;
588 + case MSHV_HV_INTERRUPTION_TYPE_SW_INT:
589 + env->interrupt_injected = pending_int.interruption_vector;
590 + env->soft_interrupt = 1;
591 + break;
592 + case MSHV_HV_INTERRUPTION_TYPE_SW_EXC:
593 + case MSHV_HV_INTERRUPTION_TYPE_PRIV_SW_EXC:
594 + env->exception_injected = 1;
595 + env->exception_nr = pending_int.interruption_vector;
596 + env->has_error_code = pending_int.deliver_error_code;
597 + env->error_code = pending_int.error_code;
598 + break;
599 + default:
600 + error_report("unknown interruption type %u",
601 + pending_int.interruption_type);
602 + return -EINVAL;
603 + }
604 + }
605 +
606 + /* disabled for one instr after STI, MOV/POP SS, see hvf_store_events() */
607 + if (int_state.interrupt_shadow) {
608 + env->hflags |= HF_INHIBIT_IRQ_MASK;
609 + } else {
610 + env->hflags &= ~HF_INHIBIT_IRQ_MASK;
611 + }
612 +
613 + /* see kvm_get_vcpu_events(), hvf_store_events() */
614 + if (int_state.nmi_masked) {
615 + env->hflags2 |= HF2_NMI_MASK;
616 + } else {
617 + env->hflags2 &= ~HF2_NMI_MASK;
618 + }
619 +
620 + /* HV_REGISTER_PENDING_EVENT0: pending exception not yet injected */
621 + if (pending_exc.event_pending) {
622 + env->exception_pending = 1;
623 + env->exception_nr = pending_exc.vector;
624 + env->has_error_code = pending_exc.deliver_error_code;
625 + env->error_code = pending_exc.error_code;
626 + env->exception_has_payload = (pending_exc.exception_parameter != 0);
627 + env->exception_payload = pending_exc.exception_parameter;
628 + }
629 +
630 + /*
631 + * Ignoring HV_REGISTER_PENDING_EVENT1, virtualization fault events, MSHV
632 + * does not support nested virtualization.
633 + */
634 +
635 + return 0;
636 +}
637 +
638 +static int set_vcpu_events(const CPUState *cpu)
639 +{
640 + X86CPU *x86cpu = X86_CPU(cpu);
641 + CPUX86State *env = &x86cpu->env;
642 + union hv_x64_pending_interruption_register pending_int = { 0 };
643 + union hv_x64_interrupt_state_register int_state = { 0 };
644 + union hv_x64_pending_exception_event pending_exc = { 0 };
645 + struct hv_register_assoc assocs[3];
646 + int ret;
647 +
648 + /* build pending_int from CPUX86State */
649 + if (env->exception_injected) {
650 + pending_int.interruption_pending = 1;
651 + pending_int.interruption_type = MSHV_HV_INTERRUPTION_TYPE_HW_EXC;
652 + pending_int.interruption_vector = env->exception_nr;
653 + pending_int.deliver_error_code = env->has_error_code;
654 + pending_int.error_code = env->error_code;
655 + } else if (env->nmi_injected) {
656 + pending_int.interruption_pending = 1;
657 + pending_int.interruption_type = MSHV_HV_INTERRUPTION_TYPE_NMI;
658 + pending_int.interruption_vector = EXCP02_NMI;
659 + } else if (env->interrupt_injected >= 0) {
660 + pending_int.interruption_pending = 1;
661 + pending_int.interruption_type = env->soft_interrupt
662 + ? MSHV_HV_INTERRUPTION_TYPE_SW_INT
663 + : MSHV_HV_INTERRUPTION_TYPE_EXT_INT;
664 + pending_int.interruption_vector = env->interrupt_injected;
665 + }
666 +
667 + /* build int_state, normalize to bool */
668 + int_state.interrupt_shadow = !!(env->hflags & HF_INHIBIT_IRQ_MASK);
669 + int_state.nmi_masked = !!(env->hflags2 & HF2_NMI_MASK);
670 +
671 + /* build pending_exc */
672 + if (env->exception_pending) {
673 + pending_exc.event_pending = 1;
674 + pending_exc.vector = env->exception_nr;
675 + pending_exc.deliver_error_code = env->has_error_code;
676 + pending_exc.error_code = env->error_code;
677 + pending_exc.exception_parameter = env->exception_payload;
678 + }
679 +
680 + assocs[0].name = HV_REGISTER_PENDING_INTERRUPTION;
681 + assocs[0].value.reg64 = pending_int.as_uint64;
682 + assocs[1].name = HV_REGISTER_INTERRUPT_STATE;
683 + assocs[1].value.reg64 = int_state.as_uint64;
684 + assocs[2].name = HV_REGISTER_PENDING_EVENT0;
685 + assocs[2].value.pending_exception_event = pending_exc;
686 +
687 + ret = mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs));
688 + if (ret < 0) {
689 + error_report("failed to set vcpu event registers");
690 + return -1;
691 + }
692 +
693 + return 0;
694 +}
695 +
696 int mshv_arch_load_vcpu_state(CPUState *cpu)
697 {
698 int ret;
@@ -559,6 +717,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu)
717 return ret;
718 }
719
720 + ret = get_vcpu_events(cpu);
721 + if (ret < 0) {
722 + return ret;
723 + }
724 +
725 return 0;
726 }
727
@@ -1100,6 +1263,11 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu)
1263 return ret;
1264 }
1265
1266 + ret = set_vcpu_events(cpu);
1267 + if (ret < 0) {
1268 + return ret;
1269 + }
1270 +
1271 return 0;
1272 }
1273