@@ -709,6 +709,7 @@ static void _gic_clear_lr(struct vcpu *v, int i)
p = irq_to_pending(v, irq);
if ( lr & GICH_LR_ACTIVE )
{
+ set_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
/* HW interrupts cannot be ACTIVE and PENDING */
if ( p->desc == NULL &&
test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) &&
@@ -723,6 +724,7 @@ static void _gic_clear_lr(struct vcpu *v, int i)
if ( p->desc != NULL )
p->desc->status &= ~IRQ_INPROGRESS;
clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
+ clear_bit(GIC_IRQ_GUEST_ACTIVE, &p->status);
p->lr = nr_lrs;
if ( test_bit(GIC_IRQ_GUEST_PENDING, &p->status) &&
test_bit(GIC_IRQ_GUEST_ENABLED, &p->status))
@@ -750,22 +752,47 @@ void gic_clear_lrs(struct vcpu *v)
static void gic_restore_pending_irqs(struct vcpu *v)
{
- int i;
- struct pending_irq *p, *t;
+ int i = 0, lrs = nr_lrs;
+ struct pending_irq *p, *t, *p_r;
unsigned long flags;
+ if ( list_empty(&v->arch.vgic.lr_pending) )
+ return;
+
+ spin_lock_irqsave(&v->arch.vgic.lock, flags);
+
+ p_r = list_entry(v->arch.vgic.inflight_irqs.prev,
+ typeof(*p_r), inflight);
list_for_each_entry_safe ( p, t, &v->arch.vgic.lr_pending, lr_queue )
{
i = find_first_zero_bit(&this_cpu(lr_mask), nr_lrs);
- if ( i >= nr_lrs ) return;
+ if ( i >= nr_lrs )
+ {
+ while ( !test_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status) ||
+ test_bit(GIC_IRQ_GUEST_ACTIVE, &p_r->status) )
+ {
+ p_r = list_entry(p_r->inflight.prev, typeof(*p_r), inflight);
+ if ( &p_r->inflight == p->inflight.next )
+ goto out;
+ }
+ i = p_r->lr;
+ p_r->lr = nr_lrs;
+ set_bit(GIC_IRQ_GUEST_PENDING, &p_r->status);
+ clear_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status);
+ gic_add_to_lr_pending(v, p_r->irq, p_r->priority);
+ }
- spin_lock_irqsave(&v->arch.vgic.lock, flags);
gic_set_lr(v, i, p->irq, GICH_LR_PENDING, p->priority);
list_del_init(&p->lr_queue);
set_bit(i, &this_cpu(lr_mask));
- spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
+
+ lrs--;
+ if ( lrs == 0 )
+ break;
}
+out:
+ spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
}
void gic_clear_pending_irqs(struct vcpu *v)
@@ -779,8 +806,38 @@ void gic_clear_pending_irqs(struct vcpu *v)
int gic_events_need_delivery(void)
{
- return (!list_empty(¤t->arch.vgic.lr_pending) ||
- this_cpu(lr_mask));
+ int mask_priority, lrs = nr_lrs;
+ int max_priority = 0xff, active_priority = 0xff;
+ struct vcpu *v = current;
+ struct pending_irq *p;
+ unsigned long flags;
+
+ mask_priority = (GICH[GICH_VMCR] >> GICH_VMCR_PRIORITY_SHIFT) & GICH_VMCR_PRIORITY_MASK;
+
+ spin_lock_irqsave(&v->arch.vgic.lock, flags);
+
+ list_for_each_entry( p, &v->arch.vgic.inflight_irqs, inflight )
+ {
+ if ( test_bit(GIC_IRQ_GUEST_ACTIVE, &p->status) )
+ {
+ if ( p->priority < active_priority )
+ active_priority = p->priority;
+ } else if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) ) {
+ if ( p->priority < max_priority )
+ max_priority = p->priority;
+ }
+ lrs--;
+ if ( lrs == 0 )
+ break;
+ }
+
+ spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
+
+ if ( max_priority < active_priority &&
+ (max_priority >> 3) < mask_priority )
+ return 1;
+ else
+ return 0;
}
void gic_inject(void)
@@ -56,8 +56,9 @@ struct pending_irq
*
*/
#define GIC_IRQ_GUEST_PENDING 0
-#define GIC_IRQ_GUEST_VISIBLE 1
-#define GIC_IRQ_GUEST_ENABLED 2
+#define GIC_IRQ_GUEST_ACTIVE 1
+#define GIC_IRQ_GUEST_VISIBLE 2
+#define GIC_IRQ_GUEST_ENABLED 3
unsigned long status;
uint8_t lr;
struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */
@@ -129,6 +129,9 @@
#define GICH_LR_CPUID_SHIFT 9
#define GICH_VTR_NRLRGS 0x3f
+#define GICH_VMCR_PRIORITY_MASK 0x1f
+#define GICH_VMCR_PRIORITY_SHIFT 27
+
/*
* The minimum GICC_BPR is required to be in the range 0-3. We set
* GICC_BPR to 0 but we must expect that it might be 3. This means we