diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 54e604a..0acbd83 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -754,6 +754,55 @@ retry:
     return 0;
 }
 
+static union host_lpi *find_guest_lpi(struct domain *d,
+                                      uint32_t devid, uint32_t eventid)
+{
+    struct its_devices *dev;
+    uint32_t lpi;
+
+    list_for_each_entry( dev, &d->arch.vgic.its_devices, entry )
+    {
+        if (dev->guest_devid != devid )
+            continue;
+
+        if ( eventid >= dev->eventids )
+            return NULL;
+
+        lpi = dev->host_lpis[eventid / 32] + (eventid % 32);
+        if (lpi < 8192)
+            return NULL;
+
+        lpi -= 8192;
+
+        return &lpi_data.host_lpis[lpi / HOST_LPIS_PER_PAGE][lpi % HOST_LPIS_PER_PAGE];
+    }
+
+    return NULL;
+}
+
+/* Connects the event ID for an already assigned device to the given VCPU/vLPI
+ * pair. The corresponding physical LPI is already mapped on the host side
+ * (when assigning the physical device to the guest), so we just connect the
+ * target VCPU/vLPI pair to that interrupt to inject it properly if it fires.
+ */
+int gicv3_assign_guest_event(struct domain *d, uint32_t devid, uint32_t eventid,
+                             struct vcpu *v, uint32_t virt_lpi)
+{
+    union host_lpi hlpi, *hlpip;
+
+    hlpip = find_guest_lpi(d, devid, eventid);
+    if ( !hlpip )
+        return -1;
+
+    hlpi.virt_lpi = virt_lpi;
+    hlpi.dom_id = d->domain_id;
+    hlpi.vcpu_id = v->vcpu_id;
+
+    hlpip->data = hlpi.data;
+
+    return 0;
+}
+
 /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
 void gicv3_its_dt_init(const struct dt_device_node *node)
 {
diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index 918b504..f9f438a 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -276,6 +276,46 @@ static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
     return 0;
 }
 
+static int its_handle_mapti(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t devid = its_cmd_get_deviceid(cmdptr);
+    uint32_t eventid = its_cmd_get_id(cmdptr);
+    uint32_t intid = its_cmd_get_physical_id(cmdptr);
+    int collid = its_cmd_get_collection(cmdptr);
+    struct vits_itte *itte;
+    struct vcpu *vcpu;
+    int ret = -1;
+
+    if ( its_cmd_get_command(cmdptr) == GITS_CMD_MAPI )
+        intid = eventid;
+
+    if ( collid >= its->max_collections )
+        return -1;
+
+    spin_lock(&its->its_lock);
+    vcpu = get_vcpu_from_collection(its, collid);
+    if ( !vcpu )
+        goto out_unlock;
+
+    itte = get_devid_evid(its, devid, eventid);
+    if ( !itte )
+        goto out_unlock;
+
+    itte->vlpi = intid;
+    itte->collection = collid;
+
+    gicv3_assign_guest_event(its->d, devid, eventid, vcpu, intid);
+
+    ret = 0;
+
+    put_devid_evid(its, itte);
+
+out_unlock:
+    spin_unlock(&its->its_lock);
+    
+    return ret;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
@@ -308,6 +348,10 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         case GITS_CMD_MAPD:
             its_handle_mapd(its, cmdptr);
 	    break;
+        case GITS_CMD_MAPI:
+        case GITS_CMD_MAPTI:
+            its_handle_mapti(its, cmdptr);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
 	    break;
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index d1ebc19..9843674 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -137,6 +137,10 @@ void gicv3_its_setup_collection(int cpu);
 int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
                          int bits, bool valid);
 
+int gicv3_assign_guest_event(struct domain *d,
+                             uint32_t devid, uint32_t eventid,
+                             struct vcpu *v, uint32_t virt_lpi);
+
 #else
 
 static inline void gicv3_its_dt_init(const struct dt_device_node *node)
