@@ -97,7 +97,7 @@ void gic_restore_state(struct vcpu *v)
}
/* desc->irq needs to be disabled before calling this function */
-static void gic_set_irq_type(struct irq_desc *desc, unsigned int type)
+void gic_set_irq_type(struct irq_desc *desc, unsigned int type)
{
/*
* IRQ must be disabled before configuring it (see 4.3.13 in ARM IHI
@@ -160,7 +160,8 @@ int gic_route_irq_to_guest(struct domain *d, unsigned int virq,
desc->handler = gic_hw_ops->gic_guest_irq_type;
set_bit(_IRQ_GUEST, &desc->status);
- gic_set_irq_type(desc, desc->arch.type);
+ if ( !irq_type_set_by_domain(d) )
+ gic_set_irq_type(desc, desc->arch.type);
gic_set_irq_priority(desc, priority);
p->desc = desc;
@@ -395,6 +395,17 @@ bool_t is_assignable_irq(unsigned int irq)
}
/*
+ * Only the hardware domain is allowed to set the configure the
+ * interrupt type for now.
+ *
+ * XXX: See whether it is possible to let any domain configure the type.
+ */
+bool_t irq_type_set_by_domain(const struct domain *d)
+{
+ return (d == hardware_domain);
+}
+
+/*
* Route an IRQ to a specific guest.
* For now only SPIs are assignable to the guest.
*/
@@ -449,7 +460,7 @@ int route_irq_to_guest(struct domain *d, unsigned int virq,
spin_lock_irqsave(&desc->lock, flags);
- if ( desc->arch.type == IRQ_TYPE_INVALID )
+ if ( !irq_type_set_by_domain(d) && desc->arch.type == IRQ_TYPE_INVALID )
{
printk(XENLOG_G_ERR "IRQ %u has not been configured\n", irq);
retval = -EIO;
@@ -340,6 +340,22 @@ void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n)
}
}
+#define VGIC_ICFG_MASK(intr) (1 << ((2 * ((intr) % 16)) + 1))
+
+/* The function should be called with the rank lock taken */
+static inline unsigned int vgic_get_virq_type(struct vcpu *v, int n, int index)
+{
+ struct vgic_irq_rank *r = vgic_get_rank(v, n);
+ uint32_t tr = r->icfg[index >> 4];
+
+ ASSERT(spin_is_locked(&r->lock));
+
+ if ( tr & VGIC_ICFG_MASK(index) )
+ return IRQ_TYPE_EDGE_RISING;
+ else
+ return IRQ_TYPE_LEVEL_HIGH;
+}
+
void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n)
{
const unsigned long mask = r;
@@ -348,6 +364,7 @@ void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n)
unsigned long flags;
int i = 0;
struct vcpu *v_target;
+ struct domain *d = v->domain;
while ( (i = find_next_bit(&mask, 32, i)) < 32 ) {
irq = i + (32 * n);
@@ -362,6 +379,13 @@ void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n)
{
irq_set_affinity(p->desc, cpumask_of(v_target->processor));
spin_lock_irqsave(&p->desc->lock, flags);
+ /*
+ * 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, vgic_get_virq_type(v, n, i));
p->desc->handler->enable(p->desc);
spin_unlock_irqrestore(&p->desc->lock, flags);
}
@@ -222,6 +222,9 @@ enum gic_version {
extern enum gic_version gic_hw_version(void);
+/* Program the IRQ type into the GIC */
+void gic_set_irq_type(struct irq_desc *desc, unsigned int type);
+
/* Program the GIC to route an interrupt */
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,
@@ -58,6 +58,12 @@ int platform_get_irq(const struct dt_device_node *device, int index);
void irq_set_affinity(struct irq_desc *desc, const cpumask_t *cpu_mask);
+/*
+ * Use this helper in places that need to know whether the IRQ type is
+ * set by the domain.
+ */
+bool_t irq_type_set_by_domain(const struct domain *d);
+
#endif /* _ASM_HW_IRQ_H */
/*
* Local variables: