Use the newly defined masks to extract the full 15-bit destination ID from guest MSI addresses and IO-APIC RTEs. In hvm_inject_msi() combine the standard bits [19:12] with the extended bits [11:5] of the MSI address into a 15-bit destination ID for LAPIC delivery. Increase the dest parameter of vmsi_deliver() and hvm_girq_dest_2_vcpu_id() from uint8_t to uint32_t. In vmsi_deliver_pirq() extract the full destination from gflags via XEN_DOMCTL_VMSI_X86_FULL_DEST(). In msi_gflags() pack the extended bits from the MSI address into the new XEN_DOMCTL_VMSI_X86_EXT_DEST_ID_MASK field of gflags. In vioapic_deliver() read the combined 15-bit destination using the VIOAPIC_RTE_DEST() macro. Extend ioapic_check() to check for extended destination bits set in a domain that does not advertise XEN_HVM_CPUID_EXT_DEST_ID and refuse to restore the IO-APIC state, preventing silent interrupt misrouting after live migration.
Signed-off-by: Julian Vetter <[email protected]> --- Changes in V3: - Added additional check to the vioapic_check that makes sure that the extended bits are not set for domains that don't support it - Addressed comments from Jan -> Replaced all constants by a proper define --- xen/arch/x86/hvm/irq.c | 11 ++++++++++- xen/arch/x86/hvm/vioapic.c | 21 +++++++++++++++++++-- xen/arch/x86/hvm/vmsi.c | 8 +++++--- xen/arch/x86/include/asm/hvm/hvm.h | 4 ++-- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c index 5f64361113..b520fc1150 100644 --- a/xen/arch/x86/hvm/irq.c +++ b/xen/arch/x86/hvm/irq.c @@ -374,7 +374,16 @@ int hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq) int hvm_inject_msi(struct domain *d, uint64_t addr, uint32_t data) { uint32_t tmp = (uint32_t) addr; - uint8_t dest = (tmp & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; + /* + * Standard MSI destination address bits 19:12 (8 bits). + * Extended MSI destination address bits 11:5 (7 more bits). + * + * As XEN_HVM_CPUID_EXT_DEST_ID is advertised, the guest may use bits 11:5 + * for high destination ID bits, expanding to 15 bits total. Guests unaware + * of this feature set these bits to 0, so this is backwards-compatible. + */ + uint32_t dest = (MASK_EXTR(tmp, MSI_ADDR_EXT_DEST_ID_MASK) << MSI_ADDR_DEST_ID_BITS) | + MASK_EXTR(tmp, MSI_ADDR_DEST_ID_MASK); uint8_t dest_mode = !!(tmp & MSI_ADDR_DESTMODE_MASK); uint8_t delivery_mode = (data & MSI_DATA_DELIVERY_MODE_MASK) >> MSI_DATA_DELIVERY_MODE_SHIFT; diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c index d7a4105a57..9602572dc4 100644 --- a/xen/arch/x86/hvm/vioapic.c +++ b/xen/arch/x86/hvm/vioapic.c @@ -411,7 +411,9 @@ static void ioapic_inj_irq( static void vioapic_deliver(struct hvm_vioapic *vioapic, unsigned int pin) { - uint16_t dest = vioapic->redirtbl[pin].fields.dest_id; + uint32_t dest = ((uint32_t)vioapic->redirtbl[pin].fields.ext_dest_id << + VIOAPIC_RTE_DEST_ID_UPPER_BITS) | + vioapic->redirtbl[pin].fields.dest_id; uint8_t dest_mode = vioapic->redirtbl[pin].fields.dest_mode; uint8_t delivery_mode = vioapic->redirtbl[pin].fields.delivery_mode; uint8_t vector = vioapic->redirtbl[pin].fields.vector; @@ -618,6 +620,21 @@ static int cf_check ioapic_check(const struct domain *d, hvm_domain_context_t *h e->fields.reserved[0] || e->fields.reserved[1] || e->fields.reserved[2] || e->fields.reserved2 ) return -EINVAL; + + /* + * An RTE in the saved state has ext_dest_id bits set. Check that + * the destination Xen has extended destination ID support enabled, + * otherwise interrupt routing to APIC IDs > 255 would be broken + * after restore. + */ + if ( e->fields.ext_dest_id && !d->arch.hvm.ext_dest_id_enabled ) + { + printk(XENLOG_G_ERR "HVM restore: %pd IO-APIC RTE %u has " + "extended destination ID bits set but " + "XEN_HVM_CPUID_EXT_DEST_ID is not enabled\n", + d, i); + return -EINVAL; + } } return 0; @@ -659,7 +676,7 @@ static int cf_check ioapic_load(struct domain *d, hvm_domain_context_t *h) return 0; } -HVM_REGISTER_SAVE_RESTORE(IOAPIC, ioapic_save, NULL, ioapic_load, 1, +HVM_REGISTER_SAVE_RESTORE(IOAPIC, ioapic_save, ioapic_check, ioapic_load, 1, HVMSR_PER_DOM); void vioapic_reset(struct domain *d) diff --git a/xen/arch/x86/hvm/vmsi.c b/xen/arch/x86/hvm/vmsi.c index 27b1f089e2..36ea898ac7 100644 --- a/xen/arch/x86/hvm/vmsi.c +++ b/xen/arch/x86/hvm/vmsi.c @@ -66,7 +66,7 @@ static void vmsi_inj_irq( int vmsi_deliver( struct domain *d, int vector, - uint8_t dest, uint8_t dest_mode, + uint32_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t trig_mode) { struct vlapic *target; @@ -109,7 +109,7 @@ void vmsi_deliver_pirq(struct domain *d, const struct hvm_pirq_dpci *pirq_dpci) { uint32_t flags = pirq_dpci->gmsi.gflags; int vector = pirq_dpci->gmsi.gvec; - uint8_t dest = (uint8_t)flags; + uint32_t dest = XEN_DOMCTL_VMSI_X86_FULL_DEST(flags); bool dest_mode = flags & XEN_DOMCTL_VMSI_X86_DM_MASK; uint8_t delivery_mode = MASK_EXTR(flags, XEN_DOMCTL_VMSI_X86_DELIV_MASK); bool trig_mode = flags & XEN_DOMCTL_VMSI_X86_TRIG_MASK; @@ -125,7 +125,7 @@ void vmsi_deliver_pirq(struct domain *d, const struct hvm_pirq_dpci *pirq_dpci) } /* Return value, -1 : multi-dests, non-negative value: dest_vcpu_id */ -int hvm_girq_dest_2_vcpu_id(struct domain *d, uint8_t dest, uint8_t dest_mode) +int hvm_girq_dest_2_vcpu_id(struct domain *d, uint32_t dest, uint8_t dest_mode) { int dest_vcpu_id = -1, w = 0; struct vcpu *v; @@ -802,6 +802,8 @@ static unsigned int msi_gflags(uint16_t data, uint64_t addr, bool masked) */ return MASK_INSR(MASK_EXTR(addr, MSI_ADDR_DEST_ID_MASK), XEN_DOMCTL_VMSI_X86_DEST_ID_MASK) | + MASK_INSR(MASK_EXTR(addr, MSI_ADDR_EXT_DEST_ID_MASK), + XEN_DOMCTL_VMSI_X86_EXT_DEST_ID_MASK) | MASK_INSR(MASK_EXTR(addr, MSI_ADDR_REDIRECTION_MASK), XEN_DOMCTL_VMSI_X86_RH_MASK) | MASK_INSR(MASK_EXTR(addr, MSI_ADDR_DESTMODE_MASK), diff --git a/xen/arch/x86/include/asm/hvm/hvm.h b/xen/arch/x86/include/asm/hvm/hvm.h index 7d9774df59..11256d5e67 100644 --- a/xen/arch/x86/include/asm/hvm/hvm.h +++ b/xen/arch/x86/include/asm/hvm/hvm.h @@ -295,11 +295,11 @@ uint64_t hvm_get_guest_time_fixed(const struct vcpu *v, uint64_t at_tsc); int vmsi_deliver( struct domain *d, int vector, - uint8_t dest, uint8_t dest_mode, + uint32_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t trig_mode); struct hvm_pirq_dpci; void vmsi_deliver_pirq(struct domain *d, const struct hvm_pirq_dpci *pirq_dpci); -int hvm_girq_dest_2_vcpu_id(struct domain *d, uint8_t dest, uint8_t dest_mode); +int hvm_girq_dest_2_vcpu_id(struct domain *d, uint32_t dest, uint8_t dest_mode); enum hvm_intblk hvm_interrupt_blocked(struct vcpu *v, struct hvm_intack intack); -- 2.51.0 -- Julian Vetter | Vates Hypervisor & Kernel Developer XCP-ng & Xen Orchestra - Vates solutions web: https://vates.tech
