x86: Add a new operation in HVMOP to inject emulated MSI.
authorWei Liu <liuw@liuw.name>
Thu, 26 May 2011 13:58:28 +0000 (14:58 +0100)
committerWei Liu <liuw@liuw.name>
Thu, 26 May 2011 13:58:28 +0000 (14:58 +0100)
The original vmsi_deliver is renamed to vmsi_deliver_pirq. New
vmsi_deliver is dedicated to the actually delivering.

Original HVMOP number is unchanged. New operation is numbered 16
and enclosed by (__XEN__) and (__XEN_TOOLS__).

Signed-off-by: Wei Liu <liuw@liuw.name>
Signed-off-by: Keir Fraser <keir@xen.org>
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/irq.c
xen/arch/x86/hvm/vmsi.c
xen/drivers/passthrough/io.c
xen/include/asm-x86/hvm/hvm.h
xen/include/asm-x86/msi.h
xen/include/public/hvm/hvm_op.h
xen/include/xen/hvm/irq.h
xen/include/xsm/xsm.h

index 35bec23b750e7ff00eb8d92c7cb6a7cc13bb582d..9b92fc01863af2ef8e3a697019d73615640339be 100644 (file)
@@ -3285,6 +3285,36 @@ static int hvmop_set_pci_link_route(
     return rc;
 }
 
+static int hvmop_inj_msi(
+    XEN_GUEST_HANDLE(xen_hvm_inj_msi_t) uop)
+{
+    struct xen_hvm_inj_msi op;
+    struct domain *d;
+    int rc;
+
+    if ( copy_from_guest(&op, uop, 1) )
+        return -EFAULT;
+
+    rc = rcu_lock_remote_target_domain_by_id(op.domid, &d);
+    if ( rc != 0 )
+        return rc;
+
+    rc = -EINVAL;
+    if ( !is_hvm_domain(d) )
+        goto out;
+
+    rc = xsm_hvm_inj_msi(d);
+    if ( rc )
+        goto out;
+
+    rc = 0;
+    hvm_inj_msi(d, op.addr, op.data);
+
+ out:
+    rcu_unlock_domain(d);
+    return rc;
+}
+
 static int hvmop_flush_tlb_all(void)
 {
     struct domain *d = current->domain;
@@ -3563,6 +3593,11 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
             guest_handle_cast(arg, xen_hvm_set_isa_irq_level_t));
         break;
 
+    case HVMOP_inj_msi:
+        rc = hvmop_inj_msi(
+            guest_handle_cast(arg, xen_hvm_inj_msi_t));
+        break;
+
     case HVMOP_set_pci_link_route:
         rc = hvmop_set_pci_link_route(
             guest_handle_cast(arg, xen_hvm_set_pci_link_route_t));
index f560e392e6b6e9f1e2ad2ce72b16e2d3ff672df0..f6f2613e218fcc46d00c9d5aa01903e842ded3d3 100644 (file)
@@ -26,6 +26,7 @@
 #include <xen/irq.h>
 #include <asm/hvm/domain.h>
 #include <asm/hvm/support.h>
+#include <asm/msi.h>
 
 /* Must be called with hvm_domain->irq_lock hold */
 static void assert_irq(struct domain *d, unsigned ioapic_gsi, unsigned pic_irq)
@@ -259,6 +260,20 @@ void hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq)
             d->domain_id, link, old_isa_irq, isa_irq);
 }
 
+void hvm_inj_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_mode = !!(tmp & MSI_ADDR_DESTMODE_MASK);
+    uint8_t  delivery_mode = (data & MSI_DATA_DELIVERY_MODE_MASK)
+        >> MSI_DATA_DELIVERY_MODE_SHIFT;
+    uint8_t trig_mode = (data & MSI_DATA_TRIGGER_MASK)
+        >> MSI_DATA_TRIGGER_SHIFT;
+    uint8_t vector = data & MSI_DATA_VECTOR_MASK;
+
+    vmsi_deliver(d, vector, dest, dest_mode, delivery_mode, trig_mode);
+}
+
 void hvm_set_callback_via(struct domain *d, uint64_t via)
 {
     struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
index cba074852f9d01ed81df4247ad9467dfc23e8628..2457fb407ea4583eae63233b285b28781b1025dd 100644 (file)
@@ -65,29 +65,14 @@ static void vmsi_inj_irq(
     }
 }
 
-int vmsi_deliver(struct domain *d, int pirq)
+int vmsi_deliver(
+    struct domain *d, int vector,
+    uint8_t dest, uint8_t dest_mode,
+    uint8_t delivery_mode, uint8_t trig_mode)
 {
-    struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci;
-    uint32_t flags = hvm_irq_dpci->mirq[pirq].gmsi.gflags;
-    int vector = hvm_irq_dpci->mirq[pirq].gmsi.gvec;
-    uint8_t dest = (uint8_t)flags;
-    uint8_t dest_mode = !!(flags & VMSI_DM_MASK);
-    uint8_t delivery_mode = (flags & VMSI_DELIV_MASK) >> GFLAGS_SHIFT_DELIV_MODE;
-    uint8_t trig_mode = (flags & VMSI_TRIG_MODE) >> GFLAGS_SHIFT_TRG_MODE;
     struct vlapic *target;
     struct vcpu *v;
 
-    HVM_DBG_LOG(DBG_LEVEL_IOAPIC,
-                "msi: dest=%x dest_mode=%x delivery_mode=%x "
-                "vector=%x trig_mode=%x\n",
-                dest, dest_mode, delivery_mode, vector, trig_mode);
-
-    if ( !( hvm_irq_dpci->mirq[pirq].flags & HVM_IRQ_DPCI_GUEST_MSI ) )
-    {
-        gdprintk(XENLOG_WARNING, "pirq %x not msi \n", pirq);
-        return 0;
-    }
-
     switch ( delivery_mode )
     {
     case dest_LowestPrio:
@@ -125,6 +110,32 @@ int vmsi_deliver(struct domain *d, int pirq)
     return 1;
 }
 
+int vmsi_deliver_pirq(struct domain *d, int pirq)
+{
+    struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci;
+    uint32_t flags = hvm_irq_dpci->mirq[pirq].gmsi.gflags;
+    int vector = hvm_irq_dpci->mirq[pirq].gmsi.gvec;
+    uint8_t dest = (uint8_t)flags;
+    uint8_t dest_mode = !!(flags & VMSI_DM_MASK);
+    uint8_t delivery_mode = (flags & VMSI_DELIV_MASK)
+        >> GFLAGS_SHIFT_DELIV_MODE;
+    uint8_t trig_mode = (flags&VMSI_TRIG_MODE) >> GFLAGS_SHIFT_TRG_MODE;
+
+    HVM_DBG_LOG(DBG_LEVEL_IOAPIC,
+                "msi: dest=%x dest_mode=%x delivery_mode=%x "
+                "vector=%x trig_mode=%x\n",
+                dest, dest_mode, delivery_mode, vector, trig_mode);
+
+    if ( !(hvm_irq_dpci->mirq[pirq].flags & HVM_IRQ_DPCI_GUEST_MSI) )
+    {
+        gdprintk(XENLOG_WARNING, "pirq %x not msi \n", pirq);
+        return 0;
+    }
+
+    vmsi_deliver(d, vector, dest, dest_mode, delivery_mode, trig_mode);
+    return 1;
+}
+
 /* 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)
 {
index d918c9ca6296b4dd853ab17122fd21408b19ee84..aa7f07d844013896d363f612b306d559497ef67d 100644 (file)
@@ -454,10 +454,9 @@ void hvm_dpci_msi_eoi(struct domain *d, int vector)
 
 static int hvm_pci_msi_assert(struct domain *d, int pirq)
 {
-    if ( hvm_domain_use_pirq(d, pirq) )
-        return send_guest_pirq(d, pirq);
-    else
-        return vmsi_deliver(d, pirq);
+    return (hvm_domain_use_pirq(d, pirq)
+            ? send_guest_pirq(d, pirq)
+            : vmsi_deliver_pirq(d, pirq));
 }
 #endif
 
index 3ce78d75781140dacf3e611dda5c5b84715f0bfe..f9981414cd21bac88354b676e840690e9f57aa2c 100644 (file)
@@ -199,7 +199,11 @@ void hvm_init_guest_time(struct domain *d);
 void hvm_set_guest_time(struct vcpu *v, u64 guest_time);
 u64 hvm_get_guest_time(struct vcpu *v);
 
-int vmsi_deliver(struct domain *d, int pirq);
+int vmsi_deliver(
+    struct domain *d, int vector,
+    uint8_t dest, uint8_t dest_mode,
+    uint8_t delivery_mode, uint8_t trig_mode);
+int vmsi_deliver_pirq(struct domain *d, int pirq);
 int hvm_girq_dest_2_vcpu_id(struct domain *d, uint8_t dest, uint8_t dest_mode);
 
 #define hvm_paging_enabled(v) \
index 760315e923ce3222bcb92ec5e9df14e687680768..f7290ee7688a80394253746fa08d25c1f15f80bb 100644 (file)
@@ -19,6 +19,7 @@
 #define MSI_DATA_DELIVERY_MODE_SHIFT   8
 #define  MSI_DATA_DELIVERY_FIXED       (0 << MSI_DATA_DELIVERY_MODE_SHIFT)
 #define  MSI_DATA_DELIVERY_LOWPRI      (1 << MSI_DATA_DELIVERY_MODE_SHIFT)
+#define  MSI_DATA_DELIVERY_MODE_MASK    0x00000700
 
 #define MSI_DATA_LEVEL_SHIFT           14
 #define         MSI_DATA_LEVEL_DEASSERT        (0 << MSI_DATA_LEVEL_SHIFT)
@@ -27,6 +28,7 @@
 #define MSI_DATA_TRIGGER_SHIFT         15
 #define  MSI_DATA_TRIGGER_EDGE         (0 << MSI_DATA_TRIGGER_SHIFT)
 #define  MSI_DATA_TRIGGER_LEVEL                (1 << MSI_DATA_TRIGGER_SHIFT)
+#define  MSI_DATA_TRIGGER_MASK          0x00008000
 
 /*
  * Shift/mask fields for msi address
@@ -39,6 +41,7 @@
 #define MSI_ADDR_DESTMODE_SHIFT     2
 #define MSI_ADDR_DESTMODE_PHYS      (0 << MSI_ADDR_DESTMODE_SHIFT)
 #define MSI_ADDR_DESTMODE_LOGIC     (1 << MSI_ADDR_DESTMODE_SHIFT)
+#define MSI_ADDR_DESTMODE_MASK      0x4
 
 #define MSI_ADDR_REDIRECTION_SHIFT  3
 #define MSI_ADDR_REDIRECTION_CPU    (0 << MSI_ADDR_REDIRECTION_SHIFT)
index 2a597da7d67188c20765ce234cf9c24fc70c839c..afe82789f6187a04ae666a66b690a7acff08e3dd 100644 (file)
@@ -240,4 +240,22 @@ struct xen_hvm_get_mem_type {
 typedef struct xen_hvm_get_mem_type xen_hvm_get_mem_type_t;
 DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_mem_type_t);
 
+/* Following tools-only interfaces may change in future. */
+#if defined(__XEN__) || defined(__XEN_TOOLS__)
+
+/* MSI injection for emulated devices */
+#define HVMOP_inj_msi         16
+struct xen_hvm_inj_msi {
+    /* Domain to be injected */
+    domid_t   domid;
+    /* Data -- lower 32 bits */
+    uint32_t  data;
+    /* Address (0xfeexxxxx) */
+    uint64_t  addr;
+};
+typedef struct xen_hvm_inj_msi xen_hvm_inj_msi_t;
+DEFINE_XEN_GUEST_HANDLE(xen_hvm_inj_msi_t);
+
+#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */
+
 #endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
index ae0531b3281efe80d52060437edd14ccab6ec0a9..3ef46b03cc1a4d073010f2b5bec704cb141bbb85 100644 (file)
@@ -116,6 +116,8 @@ void hvm_isa_irq_deassert(
 
 void hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq);
 
+void hvm_inj_msi(struct domain *d, uint64_t addr, uint32_t data);
+
 void hvm_maybe_deassert_evtchn_irq(void);
 void hvm_assert_evtchn_irq(struct vcpu *v);
 void hvm_set_callback_via(struct domain *d, uint64_t via);
index 512c070c811aefe6663fbc90441e2c1c86a5f773..e6587574c616f1707f23d4d97e9c84379439656b 100644 (file)
@@ -123,6 +123,7 @@ struct xsm_operations {
     int (*hvm_set_pci_intx_level) (struct domain *d);
     int (*hvm_set_isa_irq_level) (struct domain *d);
     int (*hvm_set_pci_link_route) (struct domain *d);
+    int (*hvm_inj_msi) (struct domain *d);
     int (*apic) (struct domain *d, int cmd);
     int (*assign_vector) (struct domain *d, uint32_t pirq);
     int (*xen_settime) (void);
@@ -507,6 +508,11 @@ static inline int xsm_hvm_set_pci_link_route (struct domain *d)
     return xsm_call(hvm_set_pci_link_route(d));
 }
 
+static inline int xsm_hvm_inj_msi (struct domain *d)
+{
+    return xsm_call(hvm_inj_msi(d));
+}
+
 static inline int xsm_apic (struct domain *d, int cmd)
 {
     return xsm_call(apic(d, cmd));