Add initial support for assigning device interrupts to domains in
dom0less configurations.

IRQs described in the device tree are retrieved and authorized for
the target domain. Interrupts that are not connected to the primary
interrupt controller (at the momemnt, I haven't seen the platform
with secondary interrupt controller) are ignored, and IRQs already
assigned to another domain are rejected to prevent sharing.

Signed-off-by: Oleksii Kurochko <[email protected]>
---
For better undesrstanding how   auth_irq_bmp[] is going to be used
it is also introduced two extra patches which are going after this.

They aren't really needed now as this code will be used when guest
will be ran, but it improves understanding and it in general can
go with this patch series.
---
---
 xen/arch/riscv/include/asm/aplic.h |   2 +
 xen/arch/riscv/include/asm/intc.h  |  12 +++
 xen/arch/riscv/include/asm/setup.h |  17 ++++
 xen/arch/riscv/intc.c              |   8 ++
 xen/arch/riscv/vaplic.c            | 124 ++++++++++++++++++++++++++++-
 5 files changed, 162 insertions(+), 1 deletion(-)

diff --git a/xen/arch/riscv/include/asm/aplic.h 
b/xen/arch/riscv/include/asm/aplic.h
index b0724fe6f360..55cd4a128de7 100644
--- a/xen/arch/riscv/include/asm/aplic.h
+++ b/xen/arch/riscv/include/asm/aplic.h
@@ -15,6 +15,8 @@
 
 #include <asm/imsic.h>
 
+#define APLIC_NUM_REGS 32
+
 #define APLIC_DOMAINCFG_IE      BIT(8, U)
 #define APLIC_DOMAINCFG_DM      BIT(2, U)
 
diff --git a/xen/arch/riscv/include/asm/intc.h 
b/xen/arch/riscv/include/asm/intc.h
index c5a869db8bc5..76d2fd09cb8b 100644
--- a/xen/arch/riscv/include/asm/intc.h
+++ b/xen/arch/riscv/include/asm/intc.h
@@ -16,6 +16,7 @@ struct cpu_user_regs;
 struct dt_device_node;
 struct irq_desc;
 struct kernel_info;
+struct rangeset;
 struct vcpu;
 
 struct intc_info {
@@ -54,11 +55,22 @@ struct vintc_ops {
 
     /* Check if a register is virtual interrupt controller MMIO */
     int (*is_access)(const struct vcpu *vcpu, const unsigned long addr);
+
+    /*
+     * Retrieves the interrupts configuration from a device tree node and maps
+     * those interrupts to the target domain.
+     */
+    int (*map_device_irqs_to_domain)(struct domain *d,
+                                     struct dt_device_node *dev,
+                                     bool need_mapping,
+                                     struct rangeset *irq_ranges);
 };
 
 struct vintc {
     const struct intc_info *info;
     const struct vintc_ops *ops;
+
+    void *private;
 };
 
 void intc_preinit(void);
diff --git a/xen/arch/riscv/include/asm/setup.h 
b/xen/arch/riscv/include/asm/setup.h
index 2215894cfbb1..1c23043f409f 100644
--- a/xen/arch/riscv/include/asm/setup.h
+++ b/xen/arch/riscv/include/asm/setup.h
@@ -5,6 +5,10 @@
 
 #include <xen/types.h>
 
+struct domain;
+struct dt_device_node;
+struct rangeset;
+
 #define max_init_domid (0)
 
 void setup_mm(void);
@@ -13,6 +17,19 @@ void copy_from_paddr(void *dst, paddr_t paddr, unsigned long 
len);
 
 void init_csr_masks(void);
 
+/* TODO: move somewhere to common header? */
+/*
+ * Retrieves the interrupts configuration from a device tree node and maps
+ * those interrupts to the target domain.
+ *
+ * Returns:
+ *   < 0 error
+ *   0   success
+ */
+int map_device_irqs_to_domain(struct domain *d, struct dt_device_node *dev,
+                              bool need_mapping,
+                              struct rangeset *irq_ranges);
+
 #endif /* ASM__RISCV__SETUP_H */
 
 /*
diff --git a/xen/arch/riscv/intc.c b/xen/arch/riscv/intc.c
index ff7a76accaca..a91dbc5e997c 100644
--- a/xen/arch/riscv/intc.c
+++ b/xen/arch/riscv/intc.c
@@ -79,3 +79,11 @@ int __init intc_make_domu_dt_node(const struct kernel_info 
*kinfo)
 
     return -ENOSYS;
 }
+
+int map_device_irqs_to_domain(struct domain *d, struct dt_device_node *dev,
+                              bool need_mapping,
+                              struct rangeset *irq_ranges)
+{
+    return d->arch.vintc->ops->map_device_irqs_to_domain(d, dev, need_mapping,
+                                                         irq_ranges);
+}
diff --git a/xen/arch/riscv/vaplic.c b/xen/arch/riscv/vaplic.c
index 9b105de7ed7d..0c69f087cf4d 100644
--- a/xen/arch/riscv/vaplic.c
+++ b/xen/arch/riscv/vaplic.c
@@ -9,6 +9,7 @@
  */
 
 #include <xen/errno.h>
+#include <xen/iocap.h>
 #include <xen/sched.h>
 #include <xen/xvmalloc.h>
 
@@ -19,6 +20,113 @@
 
 #include "aplic-priv.h"
 
+struct vaplic_priv {
+    /* Contains a legal interrupts for a domain */
+    uint32_t auth_irq_bmp[APLIC_NUM_REGS];
+};
+
+static bool is_irq_shared_among_domains(const struct domain *d,
+                                        const unsigned int irq_num)
+{
+    struct domain *tmp;
+    unsigned int reg_num = irq_num / APLIC_NUM_REGS;
+    unsigned int bit_pos = irq_num % APLIC_NUM_REGS;
+
+    for_each_domain ( tmp )
+    {
+        uint32_t *auth_irq_bmp;
+
+        if ( tmp == d )
+            continue;
+
+        auth_irq_bmp = tmp->arch.vintc->private;
+
+        if ( auth_irq_bmp[reg_num] & BIT(bit_pos, U) )
+        {
+            printk("%s: irq%d is shared between %pd and %pd\n", __func__,
+                   irq_num, tmp, d);
+
+            return true;
+        }
+    }
+
+    return false;
+}
+
+int vaplic_map_device_irqs_to_domain(struct domain *d,
+                                     struct dt_device_node *dev,
+                                     bool need_mapping,
+                                     struct rangeset *irq_ranges)
+{
+    unsigned int i, nirq;
+    int res, irq;
+    struct dt_raw_irq rirq;
+    uint32_t *auth_irq_bmp = d->arch.vintc->private;
+    unsigned int reg_num;
+
+    nirq = dt_number_of_irq(dev);
+
+    /* Give permission and map IRQs */
+    for ( i = 0; i < nirq; i++ )
+    {
+        res = dt_device_get_raw_irq(dev, i, &rirq);
+        if ( res )
+        {
+            printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
+                   i, dt_node_full_name(dev));
+            return res;
+        }
+
+        /*
+         * Don't map IRQ that have no physical meaning
+         * ie: IRQ whose controller is not APLIC/IMSIC/PLIC.
+         */
+        if ( rirq.controller != dt_interrupt_controller )
+        {
+            dt_dprintk("irq %u not connected to primary controller."
+                       "Connected to %s\n", i,
+                       dt_node_full_name(rirq.controller));
+            continue;
+        }
+
+        irq = platform_get_irq(dev, i);
+        if ( irq < 0 )
+        {
+            printk("Unable to get irq %u for %s\n", i, dt_node_full_name(dev));
+            return irq;
+        }
+
+        res = irq_permit_access(d, irq);
+        if ( res )
+        {
+            printk(XENLOG_ERR "Unable to permit to %pd access to IRQ %u\n", d,
+                   irq);
+            return res;
+        }
+
+        reg_num = irq / APLIC_NUM_REGS;
+
+        if ( is_irq_shared_among_domains(d, irq) )
+        {
+            printk("%s: Shared IRQ isn't supported\n", __func__);
+            return -EINVAL;
+        }
+
+        auth_irq_bmp[reg_num] |= BIT(irq % APLIC_NUM_REGS, U);
+
+        dt_dprintk("  - IRQ: %u\n", irq);
+
+        if ( irq_ranges )
+        {
+            res = rangeset_add_singleton(irq_ranges, irq);
+            if ( res )
+                return res;
+        }
+    }
+
+    return 0;
+}
+
 static int __init cf_check vcpu_vaplic_init(struct vcpu *v)
 {
     int rc = 0;
@@ -34,6 +142,7 @@ static int __init cf_check vcpu_vaplic_init(struct vcpu *v)
 
 static const struct vintc_ops vaplic_ops = {
     .vcpu_init = vcpu_vaplic_init,
+    .map_device_irqs_to_domain = vaplic_map_device_irqs_to_domain,
 };
 
 static struct vintc * __init vaplic_alloc(void)
@@ -62,13 +171,26 @@ int __init domain_vaplic_init(struct domain *d)
     to_vaplic(d->arch.vintc)->regs.domaincfg =
         APLIC_DOMAINCFG_IE | APLIC_DOMAINCFG_DM;
 
+    d->arch.vintc->private = xvzalloc(struct vaplic_priv);
+    if ( !d->arch.vintc->private )
+    {
+        ret = -ENOMEM;
+        goto fail;
+    }
+
+    return ret;
+
  fail:
+    domain_vaplic_deinit(d);
+
     return ret;
 }
 
 void __init domain_vaplic_deinit(struct domain *d)
 {
-    struct vaplic *vaplic = to_vaplic(d->arch.vintc);
+    struct vintc *vintc = d->arch.vintc;
+    struct vaplic *vaplic = to_vaplic(vintc);
 
+    XVFREE(vintc->private);
     XVFREE(vaplic);
 }
-- 
2.53.0


Reply via email to