@@ -163,6 +163,51 @@ out:
return res;
}
+/* This function only works with SPIs for now */
+int gic_remove_irq_from_guest(struct domain *d, unsigned int virq,
+ struct irq_desc *desc)
+{
+ struct vcpu *v_target = vgic_get_target_vcpu(d->vcpu[0], virq);
+ struct vgic_irq_rank *rank = vgic_rank_irq(v_target, virq);
+ struct pending_irq *p = irq_to_pending(v_target, virq);
+ unsigned long flags;
+
+ ASSERT(spin_is_locked(&desc->lock));
+ ASSERT(test_bit(_IRQ_GUEST, &desc->status));
+ ASSERT(p->desc == desc);
+
+ vgic_lock_rank(v_target, rank, flags);
+
+ if ( d->is_dying )
+ {
+ desc->handler->shutdown(desc);
+
+ /* EOI the IRQ it it has not been done by the guest */
+ if ( test_bit(_IRQ_INPROGRESS, &desc->status) )
+ gic_hw_ops->deactivate_irq(desc);
+ clear_bit(_IRQ_INPROGRESS, &desc->status);
+ }
+ else
+ {
+ /*
+ * TODO: Handle eviction from LRs For now, deny
+ * remove if the IRQ is inflight or not disabled.
+ */
+ if ( test_bit(_IRQ_INPROGRESS, &desc->status) ||
+ !test_bit(_IRQ_DISABLED, &desc->status) )
+ return -EBUSY;
+ }
+
+ clear_bit(_IRQ_GUEST, &desc->status);
+ desc->handler = &no_irq_type;
+
+ p->desc = NULL;
+
+ vgic_unlock_rank(v_target, rank, flags);
+
+ return 0;
+}
+
int gic_irq_xlate(const u32 *intspec, unsigned int intsize,
unsigned int *out_hwirq,
unsigned int *out_type)
@@ -513,6 +513,52 @@ free_info:
return retval;
}
+int release_guest_irq(struct domain *d, unsigned int virq)
+{
+ struct irq_desc *desc;
+ struct irq_guest *info;
+ unsigned long flags;
+ struct pending_irq *p;
+ int ret;
+
+ /* Only SPIs are supported */
+ if ( virq < NR_LOCAL_IRQS || virq >= vgic_num_irqs(d) )
+ return -EINVAL;
+
+ p = spi_to_pending(d, virq);
+ if ( !p->desc )
+ return -EINVAL;
+
+ desc = p->desc;
+
+ spin_lock_irqsave(&desc->lock, flags);
+
+ ret = -EINVAL;
+ if ( !test_bit(_IRQ_GUEST, &desc->status) )
+ goto unlock;
+
+ info = irq_get_guest_info(desc);
+ ret = -EINVAL;
+ if ( d != info->d )
+ goto unlock;
+
+ ret = gic_remove_irq_from_guest(d, virq, desc);
+ if ( ret )
+ goto unlock;
+
+ spin_unlock_irqrestore(&desc->lock, flags);
+
+ release_irq(desc->irq, info);
+ xfree(info);
+
+ return 0;
+
+unlock:
+ spin_unlock_irqrestore(&desc->lock, flags);
+
+ return ret;
+}
+
/*
* pirq event channels. We don't use these on ARM, instead we use the
* features of the GIC to inject virtualised normal interrupts.
@@ -135,6 +135,22 @@ void register_vgic_ops(struct domain *d, const struct vgic_ops *ops)
void domain_vgic_free(struct domain *d)
{
+ int i;
+ int ret;
+
+ for ( i = 0; i < (d->arch.vgic.nr_spis); i++ )
+ {
+ struct pending_irq *p = spi_to_pending(d, i + 32);
+
+ if ( p->desc )
+ {
+ ret = release_guest_irq(d, p->irq);
+ if ( ret )
+ dprintk(XENLOG_G_WARNING, "d%u: Failed to release virq %u ret = %d\n",
+ d->domain_id, p->irq, ret);
+ }
+ }
+
xfree(d->arch.vgic.shared_irqs);
xfree(d->arch.vgic.pending_irqs);
xfree(d->arch.vgic.allocated_irqs);
@@ -220,6 +220,10 @@ extern int gic_route_irq_to_guest(struct domain *, unsigned int virq,
struct irq_desc *desc,
unsigned int priority);
+/* Remove an IRQ passthrough to a guest */
+int gic_remove_irq_from_guest(struct domain *d, unsigned int virq,
+ struct irq_desc *desc);
+
extern void gic_inject(void);
extern void gic_clear_pending_irqs(struct vcpu *v);
extern int gic_events_need_delivery(void);
@@ -44,6 +44,8 @@ void init_secondary_IRQ(void);
int route_irq_to_guest(struct domain *d, unsigned int virq,
unsigned int irq, const char *devname);
+int release_guest_irq(struct domain *d, unsigned int irq);
+
void arch_move_irqs(struct vcpu *v);
/* Set IRQ type for an SPI */