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

[PATCH v5 08/11] x86/hvm: Decode extended MSI / IO-APIC destination IDs when opted in



Add the plumbing to allow for 15-bit destination IDs:

- struct hvm_domain gains a tri-state ext_dest_id (UNSET / DISABLED /
  ENABLED). hvm_ext_dest_id_active() is the single point to guard every
  extended decode.

- The vIO-APIC RTE save record grows an ext_dest_id:7 field (the top 7
  bits of a 15-bit APIC ID) and VIOAPIC_RTE_DEST() combines it with
  dest_id.

- vmsi_deliver() / hvm_girq_dest_2_vcpu_id() take a uint32_t dest.

- hvm_inject_msi(), vioapic_deliver(), vmsi_deliver_pirq(),
  pt_irq_bind_msi() and _hvm_dpci_msi_eoi() fold in the extended bits
  only when hvm_ext_dest_id_active(), otherwise an unaware guest that
  left non-zero values in those bits would have its interrupts
  misrouted.

Since ext_dest_id is never ENABLED yet, there is no functional change.

Signed-off-by: Julian Vetter <julian.vetter@xxxxxxxxxx>
---
Changes in v5:
- Rewritten to use a tri-state d->arch.hvm.ext_dest_id (UNSET / DISABLED
  / ENABLED).
- Addressed Jan's comment about "wrong way round": every decode site
  folds in the extended bits only when active and ignores them
  otherwise, rather than rejecting a guest that left values there.
- VIOAPIC_RTE_DEST() takes a union vioapic_redir_entry and uses .dest_id
  / .ext_dest_id.
- Replaced comments and added one central explanation.

Signed-off-by: Julian Vetter <julian.vetter@xxxxxxxxxx>
---
 xen/arch/x86/hvm/irq.c                 |  6 ++++--
 xen/arch/x86/hvm/vioapic.c             |  5 ++++-
 xen/arch/x86/hvm/vmsi.c                |  8 +++++---
 xen/arch/x86/include/asm/hvm/domain.h  | 13 +++++++++++++
 xen/arch/x86/include/asm/hvm/hvm.h     | 12 ++++++++++--
 xen/arch/x86/include/asm/hvm/vioapic.h | 10 ++++++++++
 xen/drivers/passthrough/x86/hvm.c      |  8 ++++++--
 xen/include/public/arch-x86/hvm/save.h |  4 +++-
 8 files changed, 55 insertions(+), 11 deletions(-)

diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c
index 5f64361113..aa5926529a 100644
--- a/xen/arch/x86/hvm/irq.c
+++ b/xen/arch/x86/hvm/irq.c
@@ -374,7 +374,8 @@ 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;
+    uint8_t  dest = MASK_EXTR(tmp, MSI_ADDR_DEST_ID_MASK);
+    uint32_t full_dest = hvm_ext_dest_id_active(d) ? MSI_ADDR_DEST(tmp) : dest;
     uint8_t  dest_mode = !!(tmp & MSI_ADDR_DESTMODE_MASK);
     uint8_t  delivery_mode = (data & MSI_DATA_DELIVERY_MODE_MASK)
         >> MSI_DATA_DELIVERY_MODE_SHIFT;
@@ -412,7 +413,8 @@ int hvm_inject_msi(struct domain *d, uint64_t addr, 
uint32_t data)
         return -ERANGE;
     }
 
-    return vmsi_deliver(d, vector, dest, dest_mode, delivery_mode, trig_mode);
+    return vmsi_deliver(d, vector, full_dest, dest_mode, delivery_mode,
+                        trig_mode);
 }
 
 void hvm_set_callback_via(struct domain *d, uint64_t via)
diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c
index 80dc9148a9..ca9b34d3ea 100644
--- a/xen/arch/x86/hvm/vioapic.c
+++ b/xen/arch/x86/hvm/vioapic.c
@@ -39,6 +39,7 @@
 #include <asm/event.h>
 #include <asm/io_apic.h>
 #include <asm/x86_emulate.h>
+#include <asm/msi.h>
 
 /* HACK: Route IRQ0 only to VCPU0 to prevent time jumps. */
 #define IRQ0_SPECIAL_ROUTING 1
@@ -413,7 +414,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;
+    union vioapic_redir_entry rte = vioapic->redirtbl[pin];
+    uint32_t dest = hvm_ext_dest_id_active(vioapic_domain(vioapic))
+                    ? VIOAPIC_RTE_DEST(rte) : rte.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;
diff --git a/xen/arch/x86/hvm/vmsi.c b/xen/arch/x86/hvm/vmsi.c
index 6966cabfa7..eede42a202 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;
@@ -111,7 +111,9 @@ void vmsi_deliver_pirq(struct domain *d, const struct 
hvm_pirq_dpci *pirq_dpci)
     uint64_t addr = pirq_dpci->gmsi.addr;
     uint32_t data = pirq_dpci->gmsi.data;
     unsigned int vector = MASK_EXTR(data, MSI_DATA_VECTOR_MASK);
-    uint32_t dest = MSI_ADDR_DEST(addr);
+    uint32_t dest = hvm_ext_dest_id_active(d)
+                    ? MSI_ADDR_DEST(addr)
+                    : MASK_EXTR(addr, MSI_ADDR_DEST_ID_MASK);
     bool dest_mode = addr & MSI_ADDR_DESTMODE_MASK;
     unsigned int delivery_mode = MASK_EXTR(data, MSI_DATA_DELIVERY_MODE_MASK);
     bool trig_mode = data & MSI_DATA_TRIGGER_MASK;
@@ -127,7 +129,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/domain.h 
b/xen/arch/x86/include/asm/hvm/domain.h
index dd7fa96aad..16f0586907 100644
--- a/xen/arch/x86/include/asm/hvm/domain.h
+++ b/xen/arch/x86/include/asm/hvm/domain.h
@@ -102,6 +102,19 @@ struct hvm_domain {
 
     bool                   is_s3_suspended;
 
+    /*
+     * Whether extended (15-bit) MSI / IO-APIC destination IDs are honoured for
+     * this domain. Tri-state: EXT_DEST_ID_UNSET until the value is locked (at
+     * creation_finished, or restored from a migration stream). Afterwards it
+     * is a stable, guest-visible property advertised through
+     * XEN_HVM_CPUID_EXT_DEST_ID.
+     */
+    enum {
+        EXT_DEST_ID_UNSET = 0,
+        EXT_DEST_ID_DISABLED,
+        EXT_DEST_ID_ENABLED,
+    } ext_dest_id;
+
     /* Compatibility setting for a bug in x2APIC LDR */
     bool bug_x2apic_ldr_vcpu_id;
 
diff --git a/xen/arch/x86/include/asm/hvm/hvm.h 
b/xen/arch/x86/include/asm/hvm/hvm.h
index 16383e1084..cedaa28820 100644
--- a/xen/arch/x86/include/asm/hvm/hvm.h
+++ b/xen/arch/x86/include/asm/hvm/hvm.h
@@ -296,11 +296,19 @@ 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);
+
+/*
+ * True when this domain has extended (15-bit) MSI / IO-APIC destination IDs
+ * enabled. Only then may address[11:5] / RTE[55:49] be folded into the
+ * destination ID.
+ */
+#define hvm_ext_dest_id_active(d) \
+    ((d)->arch.hvm.ext_dest_id == EXT_DEST_ID_ENABLED)
 
 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..3df2aa7fe5 100644
--- a/xen/arch/x86/include/asm/hvm/vioapic.h
+++ b/xen/arch/x86/include/asm/hvm/vioapic.h
@@ -32,6 +32,16 @@
 #define VIOAPIC_EDGE_TRIG  0
 #define VIOAPIC_LEVEL_TRIG 1
 
+/*
+ * Combined 15-bit destination ID of an IO-APIC redirection table entry: the
+ * architectural dest_id byte plus the 7 "Extended Destination ID" bits. The
+ * caller is responsible for only using the extended part when the guest has
+ * opted in (hvm_ext_dest_id_active()).
+ */
+#define VIOAPIC_RTE_DEST(rte) \
+    ((rte).fields.dest_id | \
+     ((uint32_t)(rte).fields.ext_dest_id << MSI_ADDR_DEST_ID_WIDTH))
+
 #define VIOAPIC_DEFAULT_BASE_ADDRESS  0xfec00000U
 #define VIOAPIC_MEM_LENGTH            0x100
 
diff --git a/xen/drivers/passthrough/x86/hvm.c 
b/xen/drivers/passthrough/x86/hvm.c
index bdab065eb7..a13dd86610 100644
--- a/xen/drivers/passthrough/x86/hvm.c
+++ b/xen/drivers/passthrough/x86/hvm.c
@@ -380,7 +380,8 @@ int pt_irq_bind_msi(struct domain *d, unsigned int 
machine_irq,
 
     /* Calculate dest_vcpu_id for MSI-type pirq migration. */
     gvec = MASK_EXTR(msi_data, MSI_DATA_VECTOR_MASK);
-    dest = MSI_ADDR_DEST(msi_addr);
+    dest = hvm_ext_dest_id_active(d) ? MSI_ADDR_DEST(msi_addr)
+                                     : MASK_EXTR(msi_addr, 
MSI_ADDR_DEST_ID_MASK);
     dest_mode = msi_addr & MSI_ADDR_DESTMODE_MASK;
     delivery_mode = MASK_EXTR(msi_data, MSI_DATA_DELIVERY_MODE_MASK);
 
@@ -883,7 +884,10 @@ static int cf_check _hvm_dpci_msi_eoi(
     if ( (pirq_dpci->flags & HVM_IRQ_DPCI_MACH_MSI) &&
          MASK_EXTR(pirq_dpci->gmsi.data, MSI_DATA_VECTOR_MASK) == vector )
     {
-        unsigned int dest = MSI_ADDR_DEST(pirq_dpci->gmsi.addr);
+        unsigned int dest = hvm_ext_dest_id_active(d)
+                            ? MSI_ADDR_DEST(pirq_dpci->gmsi.addr)
+                            : MASK_EXTR(pirq_dpci->gmsi.addr,
+                                        MSI_ADDR_DEST_ID_MASK);
         bool dest_mode = pirq_dpci->gmsi.addr & MSI_ADDR_DESTMODE_MASK;
 
         if ( vlapic_match_dest(vcpu_vlapic(current), NULL, 0, dest,
diff --git a/xen/include/public/arch-x86/hvm/save.h 
b/xen/include/public/arch-x86/hvm/save.h
index 44d7924777..ff73b83d65 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®.