diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
index c7ed3ce..3320642 100644
--- a/xen/arch/arm/vgic-v2.c
+++ b/xen/arch/arm/vgic-v2.c
@@ -166,9 +166,7 @@ static int vgic_v2_distr_mmio_read(struct vcpu *v, mmio_info_t *info,
                                    register_t *r, void *priv)
 {
     struct hsr_dabt dabt = info->dabt;
-    struct vgic_irq_rank *rank;
     int gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase);
-    unsigned long flags;
     unsigned int irq;
 
     perfc_incr(vgicd_reads);
@@ -222,20 +220,16 @@ static int vgic_v2_distr_mmio_read(struct vcpu *v, mmio_info_t *info,
 
     case VRANGE32(GICD_ISENABLER, GICD_ISENABLERN):
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISENABLER, DABT_WORD);
-        if ( rank == NULL) goto read_as_zero;
-        vgic_lock_rank(v, rank, flags);
-        *r = vreg_reg32_extract(rank->ienable, info);
-        vgic_unlock_rank(v, rank, flags);
+        irq = (gicd_reg - GICD_ISENABLER) * 8;
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto read_as_zero;
+        *r = vgic_fetch_irq_enabled(v, irq);
         return 1;
 
     case VRANGE32(GICD_ICENABLER, GICD_ICENABLERN):
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICENABLER, DABT_WORD);
-        if ( rank == NULL) goto read_as_zero;
-        vgic_lock_rank(v, rank, flags);
-        *r = vreg_reg32_extract(rank->ienable, info);
-        vgic_unlock_rank(v, rank, flags);
+        irq = (gicd_reg - GICD_ICENABLER) * 8;
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto read_as_zero;
+        *r = vgic_fetch_irq_enabled(v, irq);
         return 1;
 
     /* Read the pending status of an IRQ via GICD is not supported */
@@ -386,10 +380,7 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v, mmio_info_t *info,
                                     register_t r, void *priv)
 {
     struct hsr_dabt dabt = info->dabt;
-    struct vgic_irq_rank *rank;
     int gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase);
-    uint32_t tr;
-    unsigned long flags;
     unsigned int irq;
 
     perfc_incr(vgicd_writes);
@@ -426,24 +417,16 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v, mmio_info_t *info,
 
     case VRANGE32(GICD_ISENABLER, GICD_ISENABLERN):
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISENABLER, DABT_WORD);
-        if ( rank == NULL) goto write_ignore;
-        vgic_lock_rank(v, rank, flags);
-        tr = rank->ienable;
-        vreg_reg32_setbits(&rank->ienable, r, info);
-        vgic_enable_irqs(v, (rank->ienable) & (~tr), rank->index);
-        vgic_unlock_rank(v, rank, flags);
+        irq = (gicd_reg - GICD_ISENABLER) * 8;
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto write_ignore;
+        vgic_store_irq_enable(v, irq, r);
         return 1;
 
     case VRANGE32(GICD_ICENABLER, GICD_ICENABLERN):
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICENABLER, DABT_WORD);
-        if ( rank == NULL) goto write_ignore;
-        vgic_lock_rank(v, rank, flags);
-        tr = rank->ienable;
-        vreg_reg32_clearbits(&rank->ienable, r, info);
-        vgic_disable_irqs(v, (~rank->ienable) & tr, rank->index);
-        vgic_unlock_rank(v, rank, flags);
+        irq = (gicd_reg - GICD_ICENABLER) * 8;
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto write_ignore;
+        vgic_store_irq_disable(v, irq, r);
         return 1;
 
     case VRANGE32(GICD_ISPENDR, GICD_ISPENDRN):
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index e9d46af..00cc1e5 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -676,8 +676,6 @@ static int __vgic_v3_distr_common_mmio_read(const char *name, struct vcpu *v,
                                             register_t *r)
 {
     struct hsr_dabt dabt = info->dabt;
-    struct vgic_irq_rank *rank;
-    unsigned long flags;
     unsigned int irq;
 
     switch ( reg )
@@ -689,20 +687,16 @@ static int __vgic_v3_distr_common_mmio_read(const char *name, struct vcpu *v,
 
     case VRANGE32(GICD_ISENABLER, GICD_ISENABLERN):
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 1, reg - GICD_ISENABLER, DABT_WORD);
-        if ( rank == NULL ) goto read_as_zero;
-        vgic_lock_rank(v, rank, flags);
-        *r = vreg_reg32_extract(rank->ienable, info);
-        vgic_unlock_rank(v, rank, flags);
+        irq = (reg - GICD_ISENABLER) * 8;
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto read_as_zero;
+        *r = vgic_fetch_irq_enabled(v, irq);
         return 1;
 
     case VRANGE32(GICD_ICENABLER, GICD_ICENABLERN):
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 1, reg - GICD_ICENABLER, DABT_WORD);
-        if ( rank == NULL ) goto read_as_zero;
-        vgic_lock_rank(v, rank, flags);
-        *r = vreg_reg32_extract(rank->ienable, info);
-        vgic_unlock_rank(v, rank, flags);
+        irq = (reg - GICD_ICENABLER) * 8;
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto read_as_zero;
+        *r = vgic_fetch_irq_enabled(v, irq);
         return 1;
 
     /* Read the pending status of an IRQ via GICD/GICR is not supported */
@@ -752,9 +746,6 @@ static int __vgic_v3_distr_common_mmio_write(const char *name, struct vcpu *v,
                                              register_t r)
 {
     struct hsr_dabt dabt = info->dabt;
-    struct vgic_irq_rank *rank;
-    uint32_t tr;
-    unsigned long flags;
     unsigned int irq;
 
     switch ( reg )
@@ -765,24 +756,16 @@ static int __vgic_v3_distr_common_mmio_write(const char *name, struct vcpu *v,
 
     case VRANGE32(GICD_ISENABLER, GICD_ISENABLERN):
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 1, reg - GICD_ISENABLER, DABT_WORD);
-        if ( rank == NULL ) goto write_ignore;
-        vgic_lock_rank(v, rank, flags);
-        tr = rank->ienable;
-        vreg_reg32_setbits(&rank->ienable, r, info);
-        vgic_enable_irqs(v, (rank->ienable) & (~tr), rank->index);
-        vgic_unlock_rank(v, rank, flags);
+        irq = (reg - GICD_ISENABLER) * 8;
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto write_ignore;
+        vgic_store_irq_enable(v, irq, r);
         return 1;
 
     case VRANGE32(GICD_ICENABLER, GICD_ICENABLERN):
         if ( dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 1, reg - GICD_ICENABLER, DABT_WORD);
-        if ( rank == NULL ) goto write_ignore;
-        vgic_lock_rank(v, rank, flags);
-        tr = rank->ienable;
-        vreg_reg32_clearbits(&rank->ienable, r, info);
-        vgic_disable_irqs(v, (~rank->ienable) & tr, rank->index);
-        vgic_unlock_rank(v, rank, flags);
+        irq = (reg - GICD_ICENABLER) * 8;
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto write_ignore;
+        vgic_store_irq_disable(v, irq, r);
         return 1;
 
     case VRANGE32(GICD_ISPENDR, GICD_ISPENDRN):
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index a49fcde..dd969e2 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -261,6 +261,60 @@ struct vcpu *vgic_get_target_vcpu(struct domain *d, struct pending_irq *p)
     return d->vcpu[p->vcpu_id];
 }
 
+/* Takes a locked pending_irq and enables the interrupt, also unlocking it. */
+static void vgic_enable_irq_unlock(struct domain *d, struct pending_irq *p)
+{
+    struct vcpu *v_target;
+    unsigned long flags;
+    struct irq_desc *desc;
+
+    v_target = vgic_lock_vcpu_irq(d, p, &flags);
+
+    clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
+    gic_remove_from_lr_pending(v_target, p);
+    desc = p->desc;
+    spin_unlock(&p->lock);
+    spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags);
+
+    if ( desc != NULL )
+    {
+        spin_lock_irqsave(&desc->lock, flags);
+        desc->handler->disable(desc);
+        spin_unlock_irqrestore(&desc->lock, flags);
+    }
+}
+
+/* Takes a locked pending_irq and disables the interrupt, also unlocking it. */
+static void vgic_disable_irq_unlock(struct domain *d, struct pending_irq *p)
+{
+    struct vcpu *v_target;
+    unsigned long flags;
+    struct irq_desc *desc;
+    int int_type;
+
+    v_target = vgic_lock_vcpu_irq(d, p, &flags);
+
+    set_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
+    int_type = test_bit(GIC_IRQ_GUEST_LEVEL, &p->status) ? IRQ_TYPE_LEVEL_HIGH :
+                                                           IRQ_TYPE_EDGE_RISING;
+    if ( !list_empty(&p->inflight) &&
+         !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
+        gic_raise_guest_irq(v_target, p->irq, p->cur_priority);
+    desc = p->desc;
+    spin_unlock(&p->lock);
+    spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags);
+
+    if ( desc != NULL )
+    {
+        spin_lock_irqsave(&desc->lock, flags);
+        irq_set_affinity(desc, cpumask_of(v_target->processor));
+        if ( irq_type_set_by_domain(d) )
+            gic_set_irq_type(desc, int_type);
+        desc->handler->enable(desc);
+        spin_unlock_irqrestore(&desc->lock, flags);
+    }
+}
+
 #define MAX_IRQS_PER_IPRIORITYR 4
 uint32_t vgic_fetch_irq_priority(struct vcpu *v, unsigned int nrirqs,
                                  unsigned int first_irq)
@@ -347,6 +401,75 @@ void vgic_store_irq_config(struct vcpu *v, unsigned int first_irq,
     local_irq_restore(flags);
 }
 
+#define IRQS_PER_ENABLER        32
+/**
+ * vgic_fetch_irq_enabled: assemble the enabled bits for a group of 32 IRQs
+ * @v: the VCPU for private IRQs, any VCPU of a domain for SPIs
+ * @first_irq: the first IRQ to be queried, must be aligned to 32
+ */
+uint32_t vgic_fetch_irq_enabled(struct vcpu *v, unsigned int first_irq)
+{
+    struct pending_irq *pirqs[IRQS_PER_ENABLER];
+    unsigned long flags;
+    uint32_t reg = 0;
+    unsigned int i;
+
+    local_irq_save(flags);
+    vgic_lock_irqs(v, IRQS_PER_ENABLER, first_irq, pirqs);
+
+    for ( i = 0; i < 32; i++ )
+        if ( test_bit(GIC_IRQ_GUEST_ENABLED, &pirqs[i]->status) )
+            reg |= BIT(i);
+
+    vgic_unlock_irqs(pirqs, IRQS_PER_ENABLER);
+    local_irq_restore(flags);
+
+    return reg;
+}
+
+void vgic_store_irq_enable(struct vcpu *v, unsigned int first_irq,
+                           uint32_t value)
+{
+    struct pending_irq *pirqs[IRQS_PER_ENABLER];
+    unsigned long flags;
+    int i;
+
+    local_irq_save(flags);
+    vgic_lock_irqs(v, IRQS_PER_ENABLER, first_irq, pirqs);
+
+    /* This goes backwards, as it unlocks the IRQs during the process */
+    for ( i = IRQS_PER_ENABLER - 1; i >= 0; i-- )
+    {
+        if ( !test_bit(GIC_IRQ_GUEST_ENABLED, &pirqs[i]->status) &&
+             (value & BIT(i)) )
+            vgic_enable_irq_unlock(v->domain, pirqs[i]);
+        else
+            spin_unlock(&pirqs[i]->lock);
+    }
+    local_irq_restore(flags);
+}
+
+void vgic_store_irq_disable(struct vcpu *v, unsigned int first_irq,
+                            uint32_t value)
+{
+    struct pending_irq *pirqs[IRQS_PER_ENABLER];
+    unsigned long flags;
+    int i;
+
+    local_irq_save(flags);
+    vgic_lock_irqs(v, IRQS_PER_ENABLER, first_irq, pirqs);
+
+    /* This goes backwards, as it unlocks the IRQs during the process */
+    for ( i = 31; i >= 0; i-- )
+    {
+        if ( test_bit(GIC_IRQ_GUEST_ENABLED, &pirqs[i]->status) &&
+             (value & BIT(i)) )
+            vgic_disable_irq_unlock(v->domain, pirqs[i]);
+        else
+            spin_unlock(&pirqs[i]->lock);
+    }
+}
+
 bool vgic_migrate_irq(struct pending_irq *p, unsigned long *flags,
                       struct vcpu *new)
 {
@@ -437,40 +560,6 @@ void arch_move_irqs(struct vcpu *v)
     }
 }
 
-void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n)
-{
-    const unsigned long mask = r;
-    struct pending_irq *p;
-    struct irq_desc *desc;
-    unsigned int irq;
-    unsigned long flags;
-    int i = 0;
-    struct vcpu *v_target;
-
-    /* LPIs will never be disabled via this function. */
-    ASSERT(!is_lpi(32 * n + 31));
-
-    while ( (i = find_next_bit(&mask, 32, i)) < 32 ) {
-        irq = i + (32 * n);
-        p = irq_to_pending(v, irq);
-        v_target = vgic_get_target_vcpu(v->domain, p);
-
-        spin_lock_irqsave(&v_target->arch.vgic.lock, flags);
-        clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
-        gic_remove_from_lr_pending(v_target, p);
-        desc = p->desc;
-        spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags);
-
-        if ( desc != NULL )
-        {
-            spin_lock_irqsave(&desc->lock, flags);
-            desc->handler->disable(desc);
-            spin_unlock_irqrestore(&desc->lock, flags);
-        }
-        i++;
-    }
-}
-
 void vgic_lock_irqs(struct vcpu *v, unsigned int nrirqs,
                     unsigned int first_irq, struct pending_irq **pirqs)
 {
@@ -491,50 +580,6 @@ void vgic_unlock_irqs(struct pending_irq **pirqs, unsigned int nrirqs)
         spin_unlock(&pirqs[i]->lock);
 }
 
-void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n)
-{
-    const unsigned long mask = r;
-    struct pending_irq *p;
-    unsigned int irq, int_type;
-    unsigned long flags, vcpu_flags;
-    int i = 0;
-    struct vcpu *v_target;
-    struct domain *d = v->domain;
-
-    /* LPIs will never be enabled via this function. */
-    ASSERT(!is_lpi(32 * n + 31));
-
-    while ( (i = find_next_bit(&mask, 32, i)) < 32 ) {
-        irq = i + (32 * n);
-        p = irq_to_pending(v, irq);
-        v_target = vgic_get_target_vcpu(v->domain, p);
-        spin_lock_irqsave(&v_target->arch.vgic.lock, vcpu_flags);
-        vgic_irq_lock(p, flags);
-        set_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
-        int_type = test_bit(GIC_IRQ_GUEST_LEVEL, &p->status) ?
-                            IRQ_TYPE_LEVEL_HIGH : IRQ_TYPE_EDGE_RISING;
-        if ( !list_empty(&p->inflight) && !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
-            gic_raise_guest_irq(v_target, irq, p->cur_priority);
-        vgic_irq_unlock(p, flags);
-        spin_unlock_irqrestore(&v_target->arch.vgic.lock, vcpu_flags);
-        if ( p->desc != NULL )
-        {
-            spin_lock_irqsave(&p->desc->lock, flags);
-            irq_set_affinity(p->desc, cpumask_of(v_target->processor));
-            /*
-             * The irq cannot be a PPI, we only support delivery of SPIs
-             * to guests.
-             */
-            ASSERT(irq >= 32);
-            if ( irq_type_set_by_domain(d) )
-                gic_set_irq_type(p->desc, int_type);
-            p->desc->handler->enable(p->desc);
-            spin_unlock_irqrestore(&p->desc->lock, flags);
-        }
-        i++;
-    }
-}
-
 bool vgic_to_sgi(struct vcpu *v, register_t sgir, enum gic_sgi_mode irqmode,
                  int virq, const struct sgi_target *target)
 {
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index fe4d53d..233ff1f 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -109,9 +109,6 @@ struct vgic_irq_rank {
     spinlock_t lock; /* Covers access to all other members of this struct */
 
     uint8_t index;
-
-    uint32_t ienable;
-
 };
 
 struct sgi_target {
@@ -187,6 +184,11 @@ void vgic_store_irq_priority(struct vcpu *v, unsigned int nrirqs,
 uint32_t vgic_fetch_irq_config(struct vcpu *v, unsigned int first_irq);
 void vgic_store_irq_config(struct vcpu *v, unsigned int first_irq,
                            uint32_t reg);
+uint32_t vgic_fetch_irq_enabled(struct vcpu *v, unsigned int first_irq);
+void vgic_store_irq_enable(struct vcpu *v, unsigned int first_irq,
+                           uint32_t value);
+void vgic_store_irq_disable(struct vcpu *v, unsigned int first_irq,
+                            uint32_t value);
 
 enum gic_sgi_mode;
 
@@ -218,8 +220,6 @@ extern struct pending_irq *spi_to_pending(struct domain *d, unsigned int irq);
 extern struct vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s);
 extern struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq);
 extern bool vgic_emulate(struct cpu_user_regs *regs, union hsr hsr);
-extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n);
-extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n);
 extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops);
 int vgic_v2_init(struct domain *d, int *mmio_count);
 int vgic_v3_init(struct domain *d, int *mmio_count);
