Guests using the IMSIC interrupt controller require a corresponding
Device Tree description. Add support for generating an IMSIC node when
building the guest DT.

Keep a reference to the host IMSIC DT node and reuse its compatible
property while constructing the guest-visible node. The generated node
contains guest-specific information such as the MMIO region sized for
the number of vCPUs and the interrupts-extended property referencing
per-vCPU interrupt controllers.

This allows guests to discover and use the IMSIC interrupt controller.

Co-developed-by: Romain Caritey <[email protected]>
Signed-off-by: Oleksii Kurochko <[email protected]>
---
 xen/arch/riscv/imsic.c             | 113 +++++++++++++++++++++++++++++
 xen/arch/riscv/include/asm/imsic.h |   7 ++
 2 files changed, 120 insertions(+)

diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c
index a4460576f620..0956b187705f 100644
--- a/xen/arch/riscv/imsic.c
+++ b/xen/arch/riscv/imsic.c
@@ -13,8 +13,11 @@
 #include <xen/const.h>
 #include <xen/cpumask.h>
 #include <xen/device_tree.h>
+#include <xen/domain.h>
 #include <xen/errno.h>
+#include <xen/fdt-kernel.h>
 #include <xen/init.h>
+#include <xen/libfdt/libfdt.h>
 #include <xen/macros.h>
 #include <xen/smp.h>
 #include <xen/spinlock.h>
@@ -329,6 +332,8 @@ int __init imsic_init(const struct dt_device_node *node)
     struct imsic_mmios *mmios;
     struct imsic_msi *msi = NULL;
 
+    imsic_cfg.host_node = node;
+
     /* Parse IMSIC node */
     rc = imsic_parse_node(node, &nr_parent_irqs, &nr_mmios);
     /*
@@ -487,3 +492,111 @@ int __init imsic_init(const struct dt_device_node *node)
 
     return rc;
 }
+
+static int __init imsic_make_reg_property(struct domain *d, void *fdt)
+{
+    __be32 regs[4];
+
+    regs[0] = cpu_to_be32(imsic_cfg.base_addr >> 32);
+    regs[1] = cpu_to_be32(imsic_cfg.base_addr);
+    regs[2] = cpu_to_be32((IMSIC_MMIO_PAGE_SZ * d->max_vcpus) >> 32);
+    regs[3] = cpu_to_be32(IMSIC_MMIO_PAGE_SZ * d->max_vcpus);
+
+    return fdt_property(fdt, "reg", regs, sizeof(regs));
+}
+
+static int __init imsic_set_interrupt_extended_prop(struct domain *d,
+                                                    void *fdt)
+{
+    uint32_t len = 0, pos = 0, cpu, phandle;
+    uint32_t *irq_ext;
+    char buf[64];
+    int res;
+
+    irq_ext = xvzalloc_array(uint32_t, d->max_vcpus * 2);
+    if ( !irq_ext )
+        return -ENOMEM;
+
+    for ( cpu = 0; cpu < d->max_vcpus; cpu++ )
+    {
+        snprintf(buf, sizeof(buf), "/cpus/cpu@%u/interrupt-controller", cpu);
+        phandle = fdt_get_phandle(fdt, fdt_path_offset(fdt, buf));
+
+        if ( phandle <= 0 )
+            return phandle;
+
+        irq_ext[pos++] = cpu_to_be32(phandle);
+        len += sizeof(uint32_t);
+        irq_ext[pos++] = cpu_to_be32(IRQ_S_EXT);
+        len += sizeof(uint32_t);
+    }
+
+    res = fdt_property(fdt, "interrupts-extended", irq_ext, len);
+
+    XVFREE(irq_ext);
+
+    return res;
+}
+
+int __init imsic_make_dt_node(const struct kernel_info *kinfo)
+{
+    uint32_t len;
+    const void *data = NULL;
+    int res = 0;
+    void *fdt = kinfo->fdt;
+    const struct dt_device_node *host_imsic_node = imsic_cfg.host_node;
+    uint32_t *next_phandle = &kinfo->bd.d->arch.next_phandle;
+
+    res = fdt_begin_node(fdt, host_imsic_node->full_name);
+    if ( res )
+        return res;
+
+    data = dt_get_property(host_imsic_node, "compatible", &len);
+    if ( !data )
+    {
+        printk(XENLOG_ERR "%s: Can't find 'compatible' property\n",
+               host_imsic_node->full_name);
+
+        return -ENOENT;
+    }
+
+    res = fdt_property(fdt, "compatible", data, len);
+    if ( res )
+        return res;
+
+    res = imsic_make_reg_property(kinfo->bd.d, fdt);
+    if ( res )
+        return res;
+
+    res = imsic_set_interrupt_extended_prop(kinfo->bd.d, fdt);
+    if ( res )
+        return res;
+
+    res = fdt_property_u32(fdt, "riscv,num-ids", imsic_cfg.nr_ids);
+    if ( res )
+        return res;
+
+    res = fdt_property(fdt, "msi-controller", NULL, 0);
+    if ( res )
+        return res;
+
+    res = fdt_property_u32(fdt, "#msi-cells", 0);
+    if ( res )
+        return res;
+
+    res = fdt_property(fdt, "interrupt-controller", NULL, 0);
+    if ( res )
+        return res;
+
+    res = fdt_property_u32(fdt, "#interrupt-cells", 0);
+    if ( res )
+        return res;
+
+    imsic_cfg.phandle = (*next_phandle)++;
+
+    res = fdt_property_cell(fdt, "phandle", imsic_cfg.phandle);
+    if ( res )
+        return res;
+
+    return fdt_end_node(fdt);
+}
diff --git a/xen/arch/riscv/include/asm/imsic.h 
b/xen/arch/riscv/include/asm/imsic.h
index c6c59215df20..a63d56fbd5d9 100644
--- a/xen/arch/riscv/include/asm/imsic.h
+++ b/xen/arch/riscv/include/asm/imsic.h
@@ -57,11 +57,16 @@ struct imsic_config {
     /* MSI */
     const struct imsic_msi *msi;
 
+    /* DT node of IMSIC */
+    const struct dt_device_node *host_node;
+
     /* Lock to protect access to IMSIC's stuff */
     spinlock_t lock;
 };
 
 struct dt_device_node;
+struct kernel_info;
+
 int imsic_init(const struct dt_device_node *node);
 
 const struct imsic_config *imsic_get_config(void);
@@ -71,4 +76,6 @@ void imsic_irq_disable(unsigned int hwirq);
 
 void imsic_ids_local_delivery(bool enable);
 
+int imsic_make_dt_node(const struct kernel_info *kinfo);
+
 #endif /* ASM_RISCV_IMSIC_H */
-- 
2.53.0


Reply via email to