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
