diff --git a/tools/include/xendevicemodel.h b/tools/include/xendevicemodel.h
index 797e0c6b29..e28710a6a5 100644
--- a/tools/include/xendevicemodel.h
+++ b/tools/include/xendevicemodel.h
@@ -236,6 +236,10 @@ int xendevicemodel_inject_msi(
     xendevicemodel_handle *dmod, domid_t domid, uint64_t msi_addr,
     uint32_t msi_data);
 
+int xendevicemodel_arm_inject_msi(
+    xendevicemodel_handle *dmod, domid_t domid, uint64_t msi_addr, uint32_t devid,
+    uint32_t data);
+
 /**
  * This function enables tracking of changes in the VRAM area.
  *
diff --git a/tools/libs/devicemodel/core.c b/tools/libs/devicemodel/core.c
index 8e619eeb0a..d15ffa46fb 100644
--- a/tools/libs/devicemodel/core.c
+++ b/tools/libs/devicemodel/core.c
@@ -448,6 +448,25 @@ int xendevicemodel_set_irq_level(
     return xendevicemodel_op(dmod, domid, 1, &op, sizeof(op));
 }
 
+int xendevicemodel_arm_inject_msi(
+    xendevicemodel_handle *dmod, domid_t domid, uint64_t msi_addr, uint32_t msi_data,
+    uint32_t devid)
+{
+    struct xen_dm_op op;
+    struct xen_dm_op_arm_inject_msi *data;
+
+    memset(&op, 0, sizeof(op));
+
+    op.op = XEN_DMOP_arm_inject_msi;
+    data = &op.u.arm_inject_msi;
+
+    data->addr = msi_addr;
+    data->devid = devid;
+    data->data = msi_data;
+
+    return xendevicemodel_op(dmod, domid, 1, &op, sizeof(op));
+}
+
 int xendevicemodel_set_pci_link_route(
     xendevicemodel_handle *dmod, domid_t domid, uint8_t link, uint8_t irq)
 {
diff --git a/tools/libs/devicemodel/libxendevicemodel.map b/tools/libs/devicemodel/libxendevicemodel.map
index f7f9e3d932..8dceba5056 100644
--- a/tools/libs/devicemodel/libxendevicemodel.map
+++ b/tools/libs/devicemodel/libxendevicemodel.map
@@ -44,3 +44,8 @@ VERS_1.4 {
 		xendevicemodel_set_irq_level;
 		xendevicemodel_nr_vcpus;
 } VERS_1.3;
+
+VERS_1.5 {
+	global:
+		xendevicemodel_arm_inject_msi;
+} VERS_1.4;
diff --git a/xen/arch/arm/dm.c b/xen/arch/arm/dm.c
index 5569efa121..b42f11e569 100644
--- a/xen/arch/arm/dm.c
+++ b/xen/arch/arm/dm.c
@@ -27,6 +27,7 @@ int dm_op(const struct dmop_args *op_args)
         [XEN_DMOP_set_ioreq_server_state]           = sizeof(struct xen_dm_op_set_ioreq_server_state),
         [XEN_DMOP_destroy_ioreq_server]             = sizeof(struct xen_dm_op_destroy_ioreq_server),
         [XEN_DMOP_set_irq_level]                    = sizeof(struct xen_dm_op_set_irq_level),
+        [XEN_DMOP_arm_inject_msi]                   = sizeof(struct xen_dm_op_arm_inject_msi),
         [XEN_DMOP_nr_vcpus]                         = sizeof(struct xen_dm_op_nr_vcpus),
     };
 
@@ -112,6 +113,20 @@ int dm_op(const struct dmop_args *op_args)
         break;
     }
 
+    case XEN_DMOP_arm_inject_msi:
+    {
+        const struct xen_dm_op_arm_inject_msi *data =
+            &op.u.arm_inject_msi;
+
+        if ( d->arch.vgic.its == NULL )
+        {
+            rc = -EINVAL;
+            break;
+        }
+        vgic_its_trigger_msi(d, d->arch.vgic.its, data->devid, data->data);
+        break;
+
+    }
     case XEN_DMOP_nr_vcpus:
     {
         struct xen_dm_op_nr_vcpus *data = &op.u.nr_vcpus;
diff --git a/xen/arch/arm/include/asm/new_vgic.h b/xen/arch/arm/include/asm/new_vgic.h
index dfc434ab41..dedc294ce9 100644
--- a/xen/arch/arm/include/asm/new_vgic.h
+++ b/xen/arch/arm/include/asm/new_vgic.h
@@ -275,6 +275,8 @@ int vgic_its_add_device(struct domain *d, struct vgic_its_device *its_dev);
 void vgic_its_delete_device(struct domain *d, struct vgic_its_device *its_dev);
 struct vgic_its_device* vgic_its_get_device(struct domain *d, paddr_t vdoorbell,
                                          uint32_t vdevid);
+int vgic_its_trigger_msi(struct domain *d, struct vgic_its *its,
+                                u32 devid, u32 eventid);
 #else
 static inline void vgic_enable_lpis(struct vcpu *vcpu)
 {
diff --git a/xen/arch/arm/vgic/vgic-its.c b/xen/arch/arm/vgic/vgic-its.c
index fd5aaf4a70..706987d93a 100644
--- a/xen/arch/arm/vgic/vgic-its.c
+++ b/xen/arch/arm/vgic/vgic-its.c
@@ -608,7 +608,7 @@ int vgic_its_inject_cached_translation(struct domain *d, struct vgic_its *its, u
  * Returns 0 on success, a positive error value for any ITS mapping
  * related errors and negative error values for generic errors.
  */
-static int vgic_its_trigger_msi(struct domain *d, struct vgic_its *its,
+int vgic_its_trigger_msi(struct domain *d, struct vgic_its *its,
                                 u32 devid, u32 eventid)
 {
     struct vgic_irq *irq = NULL;
diff --git a/xen/include/public/hvm/dm_op.h b/xen/include/public/hvm/dm_op.h
index fa98551914..a7d72e4389 100644
--- a/xen/include/public/hvm/dm_op.h
+++ b/xen/include/public/hvm/dm_op.h
@@ -444,6 +444,15 @@ struct xen_dm_op_nr_vcpus {
 };
 typedef struct xen_dm_op_nr_vcpus xen_dm_op_nr_vcpus_t;
 
+#define XEN_DMOP_arm_inject_msi 21
+
+struct xen_dm_op_arm_inject_msi {
+    uint64_t addr;
+    uint32_t data;
+    uint32_t devid;
+};
+typedef struct xen_dm_op_arm_inject_msi xen_dm_op_arm_inject_msi_t;
+
 struct xen_dm_op {
     uint32_t op;
     uint32_t pad;
@@ -463,6 +472,7 @@ struct xen_dm_op {
         xen_dm_op_set_mem_type_t set_mem_type;
         xen_dm_op_inject_event_t inject_event;
         xen_dm_op_inject_msi_t inject_msi;
+        xen_dm_op_arm_inject_msi_t arm_inject_msi;
         xen_dm_op_map_mem_type_to_ioreq_server_t map_mem_type_to_ioreq_server;
         xen_dm_op_remote_shutdown_t remote_shutdown;
         xen_dm_op_relocate_memory_t relocate_memory;
