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

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



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))
+
 #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;
 };
-- 
2.53.0



--
Julian Vetter | Vates Hypervisor & Kernel 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®.