KVM: ARM: vgic: introduce vgic_ops and LR manipulation primitives
authorMarc Zyngier <marc.zyngier@arm.com>
Mon, 3 Jun 2013 14:55:02 +0000 (15:55 +0100)
committerChristoffer Dall <christoffer.dall@linaro.org>
Fri, 11 Jul 2014 11:57:31 +0000 (04:57 -0700)
In order to split the various register manipulation from the main vgic
code, introduce a vgic_ops structure, and start by abstracting the
LR manipulation code with a couple of accessors.

Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
include/kvm/arm_vgic.h
virt/kvm/arm/vgic.c

index f738e5a..17bbe51 100644 (file)
@@ -68,6 +68,24 @@ struct vgic_bytemap {
        u32 shared[VGIC_NR_SHARED_IRQS  / 4];
 };
 
+struct kvm_vcpu;
+
+#define LR_STATE_PENDING       (1 << 0)
+#define LR_STATE_ACTIVE                (1 << 1)
+#define LR_STATE_MASK          (3 << 0)
+#define LR_EOI_INT             (1 << 2)
+
+struct vgic_lr {
+       u16     irq;
+       u8      source;
+       u8      state;
+};
+
+struct vgic_ops {
+       struct vgic_lr  (*get_lr)(const struct kvm_vcpu *, int);
+       void    (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr);
+};
+
 struct vgic_dist {
 #ifdef CONFIG_KVM_ARM_VGIC
        spinlock_t              lock;
index 0ba1ab0..11408fe 100644 (file)
@@ -94,9 +94,12 @@ static struct device_node *vgic_node;
 #define ACCESS_WRITE_MASK(x)   ((x) & (3 << 1))
 
 static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
+static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu);
 static void vgic_update_state(struct kvm *kvm);
 static void vgic_kick_vcpus(struct kvm *kvm);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
+static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr);
+static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc);
 static u32 vgic_nr_lr;
 
 static unsigned int vgic_maint_irq;
@@ -593,18 +596,6 @@ static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu,
        return false;
 }
 
-#define LR_CPUID(lr)   \
-       (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT)
-#define LR_IRQID(lr)   \
-       ((lr) & GICH_LR_VIRTUALID)
-
-static void vgic_retire_lr(int lr_nr, int irq, struct vgic_cpu *vgic_cpu)
-{
-       clear_bit(lr_nr, vgic_cpu->lr_used);
-       vgic_cpu->vgic_v2.vgic_lr[lr_nr] &= ~GICH_LR_STATE;
-       vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
-}
-
 /**
  * vgic_unqueue_irqs - move pending IRQs from LRs to the distributor
  * @vgic_cpu: Pointer to the vgic_cpu struct holding the LRs
@@ -622,13 +613,10 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
        struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
        struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
        int vcpu_id = vcpu->vcpu_id;
-       int i, irq, source_cpu;
-       u32 *lr;
+       int i;
 
        for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
-               lr = &vgic_cpu->vgic_v2.vgic_lr[i];
-               irq = LR_IRQID(*lr);
-               source_cpu = LR_CPUID(*lr);
+               struct vgic_lr lr = vgic_get_lr(vcpu, i);
 
                /*
                 * There are three options for the state bits:
@@ -640,7 +628,7 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
                 * If the LR holds only an active interrupt (not pending) then
                 * just leave it alone.
                 */
-               if ((*lr & GICH_LR_STATE) == GICH_LR_ACTIVE_BIT)
+               if ((lr.state & LR_STATE_MASK) == LR_STATE_ACTIVE)
                        continue;
 
                /*
@@ -649,18 +637,19 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
                 * is fine, then we are only setting a few bits that were
                 * already set.
                 */
-               vgic_dist_irq_set(vcpu, irq);
-               if (irq < VGIC_NR_SGIS)
-                       dist->irq_sgi_sources[vcpu_id][irq] |= 1 << source_cpu;
-               *lr &= ~GICH_LR_PENDING_BIT;
+               vgic_dist_irq_set(vcpu, lr.irq);
+               if (lr.irq < VGIC_NR_SGIS)
+                       dist->irq_sgi_sources[vcpu_id][lr.irq] |= 1 << lr.source;
+               lr.state &= ~LR_STATE_PENDING;
+               vgic_set_lr(vcpu, i, lr);
 
                /*
                 * If there's no state left on the LR (it could still be
                 * active), then the LR does not hold any useful info and can
                 * be marked as free for other use.
                 */
-               if (!(*lr & GICH_LR_STATE))
-                       vgic_retire_lr(i, irq, vgic_cpu);
+               if (!(lr.state & LR_STATE_MASK))
+                       vgic_retire_lr(i, lr.irq, vcpu);
 
                /* Finally update the VGIC state. */
                vgic_update_state(vcpu->kvm);
@@ -989,8 +978,69 @@ static void vgic_update_state(struct kvm *kvm)
        }
 }
 
-#define MK_LR_PEND(src, irq)   \
-       (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq))
+static struct vgic_lr vgic_v2_get_lr(const struct kvm_vcpu *vcpu, int lr)
+{
+       struct vgic_lr lr_desc;
+       u32 val = vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr];
+
+       lr_desc.irq     = val & GICH_LR_VIRTUALID;
+       if (lr_desc.irq <= 15)
+               lr_desc.source  = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7;
+       else
+               lr_desc.source = 0;
+       lr_desc.state   = 0;
+
+       if (val & GICH_LR_PENDING_BIT)
+               lr_desc.state |= LR_STATE_PENDING;
+       if (val & GICH_LR_ACTIVE_BIT)
+               lr_desc.state |= LR_STATE_ACTIVE;
+       if (val & GICH_LR_EOI)
+               lr_desc.state |= LR_EOI_INT;
+
+       return lr_desc;
+}
+
+static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr,
+                          struct vgic_lr lr_desc)
+{
+       u32 lr_val = (lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) | lr_desc.irq;
+
+       if (lr_desc.state & LR_STATE_PENDING)
+               lr_val |= GICH_LR_PENDING_BIT;
+       if (lr_desc.state & LR_STATE_ACTIVE)
+               lr_val |= GICH_LR_ACTIVE_BIT;
+       if (lr_desc.state & LR_EOI_INT)
+               lr_val |= GICH_LR_EOI;
+
+       vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val;
+}
+
+static const struct vgic_ops vgic_ops = {
+       .get_lr                 = vgic_v2_get_lr,
+       .set_lr                 = vgic_v2_set_lr,
+};
+
+static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr)
+{
+       return vgic_ops.get_lr(vcpu, lr);
+}
+
+static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr,
+                              struct vgic_lr vlr)
+{
+       vgic_ops.set_lr(vcpu, lr, vlr);
+}
+
+static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_lr vlr = vgic_get_lr(vcpu, lr_nr);
+
+       vlr.state = 0;
+       vgic_set_lr(vcpu, lr_nr, vlr);
+       clear_bit(lr_nr, vgic_cpu->lr_used);
+       vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+}
 
 /*
  * An interrupt may have been disabled after being made pending on the
@@ -1007,12 +1057,12 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
        int lr;
 
        for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
-               int irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID;
+               struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
 
-               if (!vgic_irq_is_enabled(vcpu, irq)) {
-                       vgic_retire_lr(lr, irq, vgic_cpu);
-                       if (vgic_irq_is_active(vcpu, irq))
-                               vgic_irq_clear_active(vcpu, irq);
+               if (!vgic_irq_is_enabled(vcpu, vlr.irq)) {
+                       vgic_retire_lr(lr, vlr.irq, vcpu);
+                       if (vgic_irq_is_active(vcpu, vlr.irq))
+                               vgic_irq_clear_active(vcpu, vlr.irq);
                }
        }
 }
@@ -1024,6 +1074,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
 static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 {
        struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_lr vlr;
        int lr;
 
        /* Sanitize the input... */
@@ -1036,13 +1087,15 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
        lr = vgic_cpu->vgic_irq_lr_map[irq];
 
        /* Do we have an active interrupt for the same CPUID? */
-       if (lr != LR_EMPTY &&
-           (LR_CPUID(vgic_cpu->vgic_v2.vgic_lr[lr]) == sgi_source_id)) {
-               kvm_debug("LR%d piggyback for IRQ%d %x\n",
-                         lr, irq, vgic_cpu->vgic_v2.vgic_lr[lr]);
-               BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
-               vgic_cpu->vgic_v2.vgic_lr[lr] |= GICH_LR_PENDING_BIT;
-               return true;
+       if (lr != LR_EMPTY) {
+               vlr = vgic_get_lr(vcpu, lr);
+               if (vlr.source == sgi_source_id) {
+                       kvm_debug("LR%d piggyback for IRQ%d\n", lr, vlr.irq);
+                       BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
+                       vlr.state |= LR_STATE_PENDING;
+                       vgic_set_lr(vcpu, lr, vlr);
+                       return true;
+               }
        }
 
        /* Try to use another LR for this interrupt */
@@ -1052,12 +1105,16 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
                return false;
 
        kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
-       vgic_cpu->vgic_v2.vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
        vgic_cpu->vgic_irq_lr_map[irq] = lr;
        set_bit(lr, vgic_cpu->lr_used);
 
+       vlr.irq = irq;
+       vlr.source = sgi_source_id;
+       vlr.state = LR_STATE_PENDING;
        if (!vgic_irq_is_edge(vcpu, irq))
-               vgic_cpu->vgic_v2.vgic_lr[lr] |= GICH_LR_EOI;
+               vlr.state |= LR_EOI_INT;
+
+       vgic_set_lr(vcpu, lr, vlr);
 
        return true;
 }
@@ -1180,21 +1237,23 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
                 * Some level interrupts have been EOIed. Clear their
                 * active bit.
                 */
-               int lr, irq;
+               int lr;
 
                for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_eisr,
                                 vgic_cpu->nr_lr) {
-                       irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID;
+                       struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
 
-                       vgic_irq_clear_active(vcpu, irq);
-                       vgic_cpu->vgic_v2.vgic_lr[lr] &= ~GICH_LR_EOI;
+                       vgic_irq_clear_active(vcpu, vlr.irq);
+                       WARN_ON(vlr.state & LR_STATE_MASK);
+                       vlr.state = 0;
+                       vgic_set_lr(vcpu, lr, vlr);
 
                        /* Any additional pending interrupt? */
-                       if (vgic_dist_irq_is_pending(vcpu, irq)) {
-                               vgic_cpu_irq_set(vcpu, irq);
+                       if (vgic_dist_irq_is_pending(vcpu, vlr.irq)) {
+                               vgic_cpu_irq_set(vcpu, vlr.irq);
                                level_pending = true;
                        } else {
-                               vgic_cpu_irq_clear(vcpu, irq);
+                               vgic_cpu_irq_clear(vcpu, vlr.irq);
                        }
 
                        /*
@@ -1202,7 +1261,6 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
                         * been marked as empty.
                         */
                        set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr);
-                       vgic_cpu->vgic_v2.vgic_lr[lr] &= ~GICH_LR_ACTIVE_BIT;
                }
        }
 
@@ -1228,15 +1286,15 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
        /* Clear mappings for empty LRs */
        for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr,
                         vgic_cpu->nr_lr) {
-               int irq;
+               struct vgic_lr vlr;
 
                if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
                        continue;
 
-               irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID;
+               vlr = vgic_get_lr(vcpu, lr);
 
-               BUG_ON(irq >= VGIC_NR_IRQS);
-               vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+               BUG_ON(vlr.irq >= VGIC_NR_IRQS);
+               vgic_cpu->vgic_irq_lr_map[vlr.irq] = LR_EMPTY;
        }
 
        /* Check if we still have something up our sleeve... */