[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v4 6/9] x86/hvm: Support extended destination IDs in virtual MSI and IO-APIC



Le 27/04/2026 à 15:57, Julian Vetter a écrit :
> Add IO_APIC_REDIR_DEST_MASK and IO_APIC_REDIR_EXT_DEST_MASK for the
> standard and extended destination fields of the IO-APIC RTE, and a
> VIOAPIC_RTE_DEST() helper that extracts the combined 15-bit destination
> ID. Extend the IO-APIC RTE save/restore struct with an ext_dest_id field
> so migration preserves the extended bits.
> 
> 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. Widen the dest
> parameter of vmsi_deliver() and hvm_girq_dest_2_vcpu_id() from uint8_t
> to uint32_t to accommodate the larger range. 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 <julian.vetter@xxxxxxxxxx>
> ---
> Changes in v4:
> - Corresponds to v3 patch 3, but adapted for the new raw-addr/data
>    storage introduced in patch 5:
>      - In v3, vmsi_deliver_pirq() extracted the full destination from
>        gflags via XEN_DOMCTL_VMSI_X86_FULL_DEST(), and msi_gflags()
>        packed the extended address bits into gflags
>      - In v4 both helpers are gone. vmsi_deliver_pirq() reads
>        pirq_dpci->gmsi.addr and pirq_dpci->gmsi.data directly using the
>        standard MSI masks
> - Moved the IO-APIC masks and VIOAPIC_RTE_DEST() helper (previously in
>    v3 patch 2) into this patch
> - Added ioapic_check() ext destination safety check (refusing migration
>    with ext_dest_id bits set when XEN_HVM_CPUID_EXT_DEST_ID is not
>    advertised)
> ---
>   xen/arch/x86/hvm/irq.c                 |  9 ++++++++-
>   xen/arch/x86/hvm/vioapic.c             |  2 +-
>   xen/arch/x86/hvm/vmsi.c                |  4 ++--
>   xen/arch/x86/include/asm/hvm/hvm.h     |  4 ++--
>   xen/arch/x86/include/asm/hvm/vioapic.h | 12 ++++++++++++
>   xen/include/public/arch-x86/hvm/save.h |  4 +++-
>   6 files changed, 28 insertions(+), 7 deletions(-)
> 
> diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c
> index 5f64361113..b43adf8b96 100644
> --- a/xen/arch/x86/hvm/irq.c
> +++ b/xen/arch/x86/hvm/irq.c
> @@ -374,7 +374,14 @@ 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 carry the 8-bit APIC ID.
> +     * When XEN_HVM_CPUID_EXT_DEST_ID is enabled, bits 11:5 carry APIC ID 
> bits
> +     * [14:8], extending the addressable range to 15 bits. Guests that do not
> +     * use extended IDs leave these bits at zero, so the combined extraction 
> is
> +     * safe regardless.
> +     */
> +    uint32_t dest = MSI_ADDR_DEST(tmp);
>       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 43fb165f84..527cc770b7 100644
> --- a/xen/arch/x86/hvm/vioapic.c
> +++ b/xen/arch/x86/hvm/vioapic.c
> @@ -411,7 +411,7 @@ 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 = VIOAPIC_RTE_DEST(vioapic->redirtbl[pin].bits);
>       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;
> diff --git a/xen/arch/x86/hvm/vmsi.c b/xen/arch/x86/hvm/vmsi.c
> index 2a4b97e2e1..7b338c4ddc 100644
> --- a/xen/arch/x86/hvm/vmsi.c
> +++ b/xen/arch/x86/hvm/vmsi.c
> @@ -67,7 +67,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;
> @@ -126,7 +126,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;
> diff --git a/xen/arch/x86/include/asm/hvm/hvm.h 
> b/xen/arch/x86/include/asm/hvm/hvm.h
> index e7c1364802..884dd44c81 100644
> --- a/xen/arch/x86/include/asm/hvm/hvm.h
> +++ b/xen/arch/x86/include/asm/hvm/hvm.h
> @@ -294,11 +294,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);
> diff --git a/xen/arch/x86/include/asm/hvm/vioapic.h 
> b/xen/arch/x86/include/asm/hvm/vioapic.h
> index 68af6dce79..4499208bad 100644
> --- a/xen/arch/x86/include/asm/hvm/vioapic.h
> +++ b/xen/arch/x86/include/asm/hvm/vioapic.h
> @@ -32,6 +32,18 @@
>   #define VIOAPIC_EDGE_TRIG  0
>   #define VIOAPIC_LEVEL_TRIG 1
>   
> +/*
> + * Extract the destination ID from a 64-bit IO-APIC RTE, including the
> + * extended bits (55:49) used when XEN_HVM_CPUID_EXT_DEST_ID is advertised.
> + */
> +#define IO_APIC_REDIR_DEST_MASK         (0xffULL << 56)
> +#define IO_APIC_REDIR_EXT_DEST_MASK     (0x7fULL << 49)
> +
> +#define VIOAPIC_RTE_DEST(rte) \
> +    (MASK_EXTR((rte), IO_APIC_REDIR_DEST_MASK) | \
> +     (MASK_EXTR((rte), IO_APIC_REDIR_EXT_DEST_MASK) << \
> +      MSI_ADDR_DEST_ID_UPPER_BITS))
> +

We can probably simplify that by using vioapic_redir_entry.dest_id and 
vioapic_redir_entry.ext_dest_id directly instead of reparsing it from 
vioapic_redir_entry.bits.

IOW, replace MASK_EXTR((rte), IO_APIC_REDIR_DEST_MASK) with 
(rte).dest_id and MASK_EXTR((rte), IO_APIC_REDIR_EXT_DEST_MASK) with 
(rte).ext_dest_id. So VIOAPIC_RTE_DEST() would now take 
vioapic_redir_entry as parameter.

>   #define VIOAPIC_DEFAULT_BASE_ADDRESS  0xfec00000U
>   #define VIOAPIC_MEM_LENGTH            0x100
>   > diff --git a/xen/include/public/arch-x86/hvm/save.h 
b/xen/include/public/arch-x86/hvm/save.h
> index 9c4bfc7ebd..483097d940 100644
> --- a/xen/include/public/arch-x86/hvm/save.h
> +++ b/xen/include/public/arch-x86/hvm/save.h
> @@ -359,7 +359,9 @@ union vioapic_redir_entry
>           uint8_t trig_mode:1;
>           uint8_t mask:1;
>           uint8_t reserve:7;
> -        uint8_t reserved[4];
> +        uint8_t reserved[3];
> +        uint8_t reserved2:1;
> +        uint8_t ext_dest_id:7;
>           uint8_t dest_id;
>       } fields;
>   };

Teddy


--
Teddy Astie | Vates XCP-ng Developer

XCP-ng & Xen Orchestra - Vates solutions

web: https://vates.tech

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.