@@ -92,8 +92,7 @@ void gic_save_state(struct vcpu *v)
void gic_save_and_mask_hwppi(struct vcpu *v, const unsigned virq,
struct hwppi_state *s)
{
- struct pending_irq *p = irq_to_pending(v, virq);
- struct irq_desc *desc = p->desc;
+ struct irq_desc *desc = vgic_get_hw_irq_desc(NULL, v, virq);
spin_lock(&desc->lock);
@@ -123,7 +122,6 @@ void gic_restore_hwppi(struct vcpu *v,
const unsigned virq,
const struct hwppi_state *s)
{
- struct pending_irq *p = irq_to_pending(v, virq);
struct irq_desc *desc = irq_to_desc(s->irq);
spin_lock(&desc->lock);
@@ -131,7 +129,8 @@ void gic_restore_hwppi(struct vcpu *v,
ASSERT(virq >= 16 && virq < 32);
ASSERT(!is_idle_vcpu(v));
- p->desc = desc; /* Migrate to new physical processor */
+ /* Migrate to new physical processor */
+ vgic_connect_hw_irq(v->domain, v, virq, desc, true);
irq_set_virq(desc, virq);
@@ -178,6 +177,32 @@ void gic_route_irq_to_xen(struct irq_desc *desc, unsigned int priority)
gic_set_irq_priority(desc, priority);
}
+/*
+ * Program the GIC to route an interrupt to the current guest.
+ *
+ * That is, the IRQ is delivered to whichever VCPU happens to be
+ * resident on the PCPU when the interrupt arrives.
+ *
+ * Currently the interrupt *must* be a PPI and the code responsible
+ * for the related hardware must save and restore the PPI with
+ * gic_save_and_mask_hwppi/gic_restore_hwppi.
+ */
+int gic_route_irq_to_current_guest(struct irq_desc *desc,
+ unsigned int priority)
+{
+ ASSERT(spin_is_locked(&desc->lock));
+ ASSERT(desc->irq >= 16 && desc->irq < 32);
+
+ desc->handler = gic_hw_ops->gic_guest_irq_type;
+ set_bit(_IRQ_GUEST, &desc->status);
+ set_bit(_IRQ_PER_CPU, &desc->status);
+
+ gic_set_irq_type(desc, desc->arch.type);
+ gic_set_irq_priority(desc, GIC_PRI_IRQ);
+
+ return 0;
+}
+
/* Program the GIC to route an interrupt to a guest
* - desc.lock must be held
*/
@@ -236,6 +236,7 @@ void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq)
if ( test_bit(_IRQ_GUEST, &desc->status) )
{
struct irq_guest *info = irq_get_guest_info(desc);
+ struct vcpu *v;
perfc_incr(guest_irqs);
desc->handler->end(desc);
@@ -243,10 +244,15 @@ void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq)
set_bit(_IRQ_INPROGRESS, &desc->status);
/*
- * The irq cannot be a PPI, we only support delivery of SPIs to
- * guests.
+ * A PPI exposed to a guest must always be in IRQ_GUEST|IRQ_PER_CPU
+ * mode ("route to active VCPU"), so we use current.
+ *
+ * For SPI, we use NULL. In this case, vgic_inject_irq() will look up
+ * the required target for delivery to a specific guest.
*/
- vgic_inject_irq(info->d, NULL, info->virq, true);
+ v = test_bit(_IRQ_PER_CPU, &desc->status) ? current : NULL;
+ vgic_inject_irq(info->d, v, info->virq, true);
+
goto out_no_end;
}
@@ -362,11 +368,15 @@ int setup_irq(unsigned int irq, unsigned int irqflags, struct irqaction *new)
if ( test_bit(_IRQ_GUEST, &desc->status) )
{
- struct domain *d = irq_get_domain(desc);
+ struct irq_guest *info = irq_get_guest_info(desc);
spin_unlock_irqrestore(&desc->lock, flags);
- printk(XENLOG_ERR "ERROR: IRQ %u is already in use by the domain %u\n",
- irq, d->domain_id);
+ if ( !test_bit(_IRQ_PER_CPU, &desc->status) )
+ printk(XENLOG_ERR "ERROR: IRQ %u is already in use by domain %u\n",
+ irq, info->d->domain_id);
+ else
+ printk(XENLOG_ERR
+ "ERROR: IRQ %u is already in use by <current-vcpu>\n", irq);
return -EBUSY;
}
@@ -450,8 +460,14 @@ static int setup_guest_irq(struct irq_desc *desc, unsigned int virq,
if ( d != ad )
{
- printk(XENLOG_G_ERR "IRQ %u is already used by domain %u\n",
- irq, ad->domain_id);
+ if ( !test_bit(_IRQ_PER_CPU, &desc->status) )
+ printk(XENLOG_G_ERR
+ "ERROR: IRQ %u is already used by domain %u\n",
+ irq, ad->domain_id);
+ else
+ printk(XENLOG_G_ERR
+ "ERROR: IRQ %u is already used by <current-vcpu>\n",
+ irq);
retval = -EBUSY;
}
else if ( irq_get_guest_info(desc)->virq != virq )
@@ -552,6 +568,54 @@ free_info:
return retval;
}
+/*
+ * Route a PPI such that it is always delivered to the current vcpu on
+ * the pcpu. The driver for the peripheral must use
+ * gic_{save_and_mask,restore}_hwppi as part of the context switch.
+ */
+int route_hwppi_to_current_vcpu(unsigned int irq, const char *devname)
+{
+ struct irq_guest *info;
+ struct irq_desc *desc;
+ unsigned long flags;
+ int retval = 0;
+
+ /* Can only route PPIs to current VCPU */
+ if ( irq < 16 || irq >= 32 )
+ return -EINVAL;
+
+ desc = irq_to_desc(irq);
+
+ info = xmalloc(struct irq_guest);
+ if ( !info )
+ return -ENOMEM;
+
+ info->d = NULL; /* Routed to current vcpu, so no specific domain */
+ /* info->virq is set by gic_restore_hwppi. */
+
+ spin_lock_irqsave(&desc->lock, flags);
+
+ retval = setup_guest_irq(desc, irq, flags, info, devname);
+ if ( retval )
+ {
+ xfree(info);
+ return retval;
+ }
+
+ retval = gic_route_irq_to_current_guest(desc, GIC_PRI_IRQ);
+
+ spin_unlock_irqrestore(&desc->lock, flags);
+
+ if ( retval )
+ {
+ release_irq(desc->irq, info);
+ xfree(info);
+ return retval;
+ }
+
+ return 0;
+}
+
int release_guest_irq(struct domain *d, unsigned int virq)
{
struct irq_desc *desc;
@@ -244,6 +244,8 @@ extern void gic_route_irq_to_xen(struct irq_desc *desc, unsigned int priority);
extern int gic_route_irq_to_guest(struct domain *, unsigned int virq,
struct irq_desc *desc,
unsigned int priority);
+int gic_route_irq_to_current_guest(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,
@@ -77,6 +77,7 @@ void init_secondary_IRQ(void);
int route_irq_to_guest(struct domain *d, unsigned int virq,
unsigned int irq, const char *devname);
+int route_hwppi_to_current_vcpu(unsigned int irq, const char *devname);
int release_guest_irq(struct domain *d, unsigned int irq);
void arch_move_irqs(struct vcpu *v);