diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown
index 9eb85d6..5a90625 100644
--- a/docs/misc/xen-command-line.markdown
+++ b/docs/misc/xen-command-line.markdown
@@ -1172,6 +1172,15 @@ based interrupts. Any higher IRQs will be available for use via PCI MSI.
 ### maxcpus
 > `= <integer>`
 
+### max\_its\_device\_bits
+> `= <integer>`
+
+Specifies the maximum number of devices using MSIs on the ARM GICv3 ITS
+controller to allocate table entries for. Each table entry uses a hardware
+specific size, typically 8 or 16 bytes. This value is given as the number
+of bits required to hold one device ID.
+Defaults to the machine provided value, which is at most 32 bits.
+
 ### mce
 > `= <integer>`
 
diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index 6b02349..58c6ac0 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -19,8 +19,10 @@
  */
 
 #include <xen/lib.h>
+#include <xen/mm.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/gic_v3_its.h>
+#include <asm/io.h>
 
 /*
  * No lock here, as this list gets only populated upon boot while scanning
@@ -33,6 +35,42 @@ bool gicv3_its_host_has_its(void)
     return !list_empty(&host_its_list);
 }
 
+/* Allow a user to limit the number of devices. */
+static unsigned int max_its_device_bits = 32;
+integer_param("max_its_device_bits", max_its_device_bits);
+
+static int gicv3_its_init_single_its(struct host_its *hw_its)
+{
+    uint64_t reg;
+
+    hw_its->its_base = ioremap_nocache(hw_its->addr, hw_its->size);
+    if ( !hw_its->its_base )
+        return -ENOMEM;
+
+    reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
+    hw_its->devid_bits = GITS_TYPER_DEVICE_ID_BITS(reg);
+    hw_its->devid_bits = min(hw_its->devid_bits, max_its_device_bits);
+    hw_its->evid_bits = GITS_TYPER_EVENT_ID_BITS(reg);
+    hw_its->itte_size = GITS_TYPER_ITT_SIZE(reg);
+
+    return 0;
+}
+
+int gicv3_its_init(void)
+{
+    struct host_its *hw_its;
+    int ret;
+
+    list_for_each_entry(hw_its, &host_its_list, entry)
+    {
+        ret = gicv3_its_init_single_its(hw_its);
+        if ( ret )
+            return ret;
+    }
+
+    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/gic-v3.c b/xen/arch/arm/gic-v3.c
index b626298..d3d5784 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -1590,6 +1590,11 @@ static int __init gicv3_init(void)
     spin_lock(&gicv3.lock);
 
     gicv3_dist_init();
+
+    res = gicv3_its_init();
+    if ( res )
+        panic("GICv3: ITS: initialization failed: %d\n", res);
+
     res = gicv3_cpu_init();
     gicv3_hyp_init();
 
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 765a655..7d88987 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -20,6 +20,38 @@
 #ifndef __ASM_ARM_ITS_H__
 #define __ASM_ARM_ITS_H__
 
+#define GITS_CTLR                       0x000
+#define GITS_IIDR                       0x004
+#define GITS_TYPER                      0x008
+#define GITS_CBASER                     0x080
+#define GITS_CWRITER                    0x088
+#define GITS_CREADR                     0x090
+#define GITS_BASER_NR_REGS              8
+#define GITS_BASER0                     0x100
+#define GITS_BASER1                     0x108
+#define GITS_BASER2                     0x110
+#define GITS_BASER3                     0x118
+#define GITS_BASER4                     0x120
+#define GITS_BASER5                     0x128
+#define GITS_BASER6                     0x130
+#define GITS_BASER7                     0x138
+
+/* Register bits */
+#define GITS_TYPER_DEVIDS_SHIFT         13
+#define GITS_TYPER_DEVIDS_MASK          (0x1fUL << GITS_TYPER_DEVIDS_SHIFT)
+#define GITS_TYPER_DEVICE_ID_BITS(r)    (((r & GITS_TYPER_DEVIDS_MASK) >> \
+                                               GITS_TYPER_DEVIDS_SHIFT) + 1)
+
+#define GITS_TYPER_IDBITS_SHIFT         8
+#define GITS_TYPER_IDBITS_MASK          (0x1fUL << GITS_TYPER_IDBITS_SHIFT)
+#define GITS_TYPER_EVENT_ID_BITS(r)     (((r & GITS_TYPER_IDBITS_MASK) >> \
+                                               GITS_TYPER_IDBITS_SHIFT) + 1)
+
+#define GITS_TYPER_ITT_SIZE_SHIFT       4
+#define GITS_TYPER_ITT_SIZE_MASK        (0xfUL << GITS_TYPER_ITT_SIZE_SHIFT)
+#define GITS_TYPER_ITT_SIZE(r)          ((((r) & GITS_TYPER_ITT_SIZE_MASK) >> \
+                                                 GITS_TYPER_ITT_SIZE_SHIFT) + 1)
+
 #include <xen/device_tree.h>
 
 /* data structure for each hardware ITS */
@@ -28,6 +60,10 @@ struct host_its {
     const struct dt_device_node *dt_node;
     paddr_t addr;
     paddr_t size;
+    void __iomem *its_base;
+    unsigned int devid_bits;
+    unsigned int evid_bits;
+    unsigned int itte_size;
 };
 
 
@@ -40,6 +76,9 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
 
 bool gicv3_its_host_has_its(void);
 
+/* Initialize the host structures for the host ITSes. */
+int gicv3_its_init(void);
+
 #else
 
 static LIST_HEAD(host_its_list);
@@ -53,6 +92,11 @@ static inline bool gicv3_its_host_has_its(void)
     return false;
 }
 
+static inline int gicv3_its_init(void)
+{
+    return 0;
+}
+
 #endif /* CONFIG_HAS_ITS */
 
 #endif
