xen/arm: IRQ: Do not allow IRQ to be shared between domains and XEN
authorJulien Grall <julien.grall@linaro.org>
Tue, 22 Apr 2014 12:58:44 +0000 (13:58 +0100)
committerIan Campbell <ian.campbell@citrix.com>
Fri, 2 May 2014 12:14:09 +0000 (13:14 +0100)
The current dt_route_irq_to_guest implementation sets IRQ_GUEST even if the
IRQ is correctly setup.

An IRQ can be shared between devices, if the devices are not assigned to the
same domain or Xen, then this could result in routing the IRQ to the domain
instead of Xen ...

Also avoid to relying on wrong the behaviour when Xen is routing an IRQ to
DOM0. Therefore check the return code from route_dt_irq_to_guest in
map_device.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
xen/arch/arm/domain_build.c
xen/arch/arm/irq.c

index 5b636c81deb78400db5e714a837e110b52b1e246..9dcff1cd6c8ee67db80a1cf90fc38a452913e9c2 100644 (file)
@@ -725,8 +725,13 @@ static int map_device(struct domain *d, const struct dt_device_node *dev)
         }
 
         DPRINT("irq %u = %u type = 0x%x\n", i, irq.irq, irq.type);
-        /* Don't check return because the IRQ can be use by multiple device */
-        route_dt_irq_to_guest(d, &irq, dt_node_name(dev));
+        res = route_dt_irq_to_guest(d, &irq, dt_node_name(dev));
+        if ( res )
+        {
+            printk(XENLOG_ERR "Unable to route IRQ %u to domain %u\n",
+                   irq.irq, d->domain_id);
+            return res;
+        }
     }
 
     /* Map the address ranges */
index 9f1ca4029c511d8b6d98dfea72568f9e3c1b7ceb..44696e74e9f97d090a613d9bfcba6226a208691c 100644 (file)
@@ -256,6 +256,16 @@ int setup_dt_irq(const struct dt_irq *irq, struct irqaction *new)
 
     spin_lock_irqsave(&desc->lock, flags);
 
+    if ( desc->status & IRQ_GUEST )
+    {
+        struct domain *d = irq_get_domain(desc);
+
+        spin_unlock_irqrestore(&desc->lock, flags);
+        printk(XENLOG_ERR "ERROR: IRQ %u is already in use by the domain %u\n",
+               irq->irq, d->domain_id);
+        return -EBUSY;
+    }
+
     disabled = (desc->action == NULL);
 
     rc = __setup_irq(desc, new);
@@ -292,7 +302,7 @@ int route_dt_irq_to_guest(struct domain *d, const struct dt_irq *irq,
     struct irqaction *action;
     struct irq_desc *desc = irq_to_desc(irq->irq);
     unsigned long flags;
-    int retval;
+    int retval = 0;
     bool_t level;
 
     action = xmalloc(struct irqaction);
@@ -305,19 +315,42 @@ int route_dt_irq_to_guest(struct domain *d, const struct dt_irq *irq,
 
     spin_lock_irqsave(&desc->lock, flags);
 
-    retval = __setup_irq(desc, action);
-    if ( retval )
+    /* If the IRQ is already used by someone
+     *  - If it's the same domain -> Xen doesn't need to update the IRQ desc
+     *  - Otherwise -> For now, don't allow the IRQ to be shared between
+     *  Xen and domains.
+     */
+    if ( desc->action != NULL )
     {
-        xfree(action);
+        struct domain *ad = irq_get_domain(desc);
+
+        if ( (desc->status & IRQ_GUEST) && d == ad )
+            goto out;
+
+        if ( desc->status & IRQ_GUEST )
+            printk(XENLOG_ERR "ERROR: IRQ %u is already used by domain %u\n",
+                   irq->irq, ad->domain_id);
+        else
+            printk(XENLOG_ERR "ERROR: IRQ %u is already used by Xen\n",
+                   irq->irq);
+        retval = -EBUSY;
         goto out;
     }
 
+    retval = __setup_irq(desc, action);
+    if ( retval )
+        goto out;
+
     level = dt_irq_is_level_triggered(irq);
     gic_route_irq_to_guest(d, desc, level, cpumask_of(smp_processor_id()),
                            GIC_PRI_IRQ);
+    spin_unlock_irqrestore(&desc->lock, flags);
+    return 0;
 
 out:
     spin_unlock_irqrestore(&desc->lock, flags);
+    xfree(action);
+
     return retval;
 }