[PATCH v7 15/20] xen/riscv: create APLIC DT node for guest domains
Oleksii Kurochko <[email protected]> Tue, 4 Aug 2026 17:48:05 +0200
| Newsgroups | gmane.comp.emulators.xen.devel |
|---|---|
| Message-ID | <6ecfa02afc7237ee229a1ca2d12369b226ca816f.1785836421.git.oleksii.kurochko@gmail.com> |
Guests require a Device Tree description of the interrupt controller topology. Add support for creating an APLIC node when building the guest DT. Provide stub for imsic_make_dt_node() it will be introduced properly in follow-up patch. The value chosen for GUEST_APLIC_S_BASE is based on QEMU one. DT-building functions are marked __init because domain creation happens at boot time, before the init sections are freed. In a typical deployment libxl creates the interrupt controller node in userspace and hands the complete FDT to Xen, so these functions are only called during early domain construction. Co-developed-by: Romain Caritey <[email protected]> Signed-off-by: Oleksii Kurochko <[email protected]> Acked-by: Jan Beulich <[email protected]> --- Changes in v7: - Update the comment for guest_aplic_num_sources: drop "wired" to be less confusing, and mention that it is identical for every domain only for now. - Use ARRAY_SIZE() instead of sizeof() for the size argument of snprintf() in vaplic_make_domu_dt_node(). - Add Acked-by: Jan Beulich <[email protected]> --- Changes in v6: - Redefine GUEST_APLIC_MAX_SOURCES as 96U to avoid '+OU' in min(...). - s/guest_num_sources/guest_aplic_num_sources. --- Changes in v5: - Drop pointless initializer for local variable res in vaplic_make_domu_dt_node(). - Limit guest_num_sources in the similar way to IMSIC. - Rename VAPLIC_NUM_SOURCES to GUEST_APLIC_MAX_SOURCES to be aligned with the similar place in vIMSIC related code. --- Changes in v4: - Drop spurious <xen/fdt-kernel.h> and <xen/libfdt/libfdt.h> includes from aplic.c (mistakenly added, they belong to vaplic.c). - Reduce vaplic_name[] from 128 to 32 bytes in vaplic_make_domu_dt_node(). - Use __initconstrel (with const) for init_ops instead of __initdata. - s/__ULL/_UL for defintion of GUEST_APLIC_S_BASE. --- Changes in v3: - Fix rebase conflicts becuase of this patch is reordered after IMSIC DT node creation is intoduced. - Update the commit message. - Move initialization of domaincfg with APLIC_DOMAINCFG_RO80 from this patch to earlier. - Change paddr_t aplic_size to unsigned int in vaplic_make_domu_dt_node() and replace the UB (after it started to be uint) aplic_size >> 32 with an explicit 0 in the DT reg property. - Add BUILD_BUG_ON() to be sure that aplic size isn't bigger then UINT32_MAX. --- Changes in v2: - Avoid as max as possible of host properties inheritance. Only number of APLIC's irqs are checked what leads to an introduction of get_aplic_irqs_num(). - Move this patch earlier what leads to an introduction of vimsic_make_domu_dt_node() stub. - s/vimsic_make_domu_dt_node/imsic_make_domu_dt_node. - Refactor vimsic_make_domu_dt_node() to avoid re-usage of APLIC host properties. - Drop next_phandle as it is now in common code. - Drop const for kinfo argument of vimsic_make_domu_dt_node() is is going to be updated inside vimsic_make_domu_dt_node(). - Use introduced before vintc->num_irqs. --- --- xen/arch/riscv/aplic-priv.h | 13 ++++ xen/arch/riscv/aplic.c | 2 + xen/arch/riscv/include/asm/aplic.h | 8 +++ xen/arch/riscv/include/asm/guest-layout.h | 6 ++ xen/arch/riscv/vaplic.c | 77 +++++++++++++++++++++++ 5 files changed, 106 insertions(+) diff --git a/xen/arch/riscv/aplic-priv.h b/xen/arch/riscv/aplic-priv.h index 85e0d028d1ae..35100d3a64fe 100644 --- a/xen/arch/riscv/aplic-priv.h +++ b/xen/arch/riscv/aplic-priv.h @@ -34,4 +34,17 @@ struct aplic_priv { const struct imsic_config *imsic_cfg; }; +/* + * Value is inspired by what QEMU is using for riscv,num-sources property for + * APLIC node. + */ +#define GUEST_APLIC_MAX_SOURCES 96U + +/* + * Specifies the number of interrupt sources supported by guest APLIC domain. + * Could be limited by host interrupt controller and is identical for every + * domain for now. + */ +extern unsigned int guest_aplic_num_sources; + #endif /* ASM_RISCV_APLIC_PRIV_H */ diff --git a/xen/arch/riscv/aplic.c b/xen/arch/riscv/aplic.c index d08401db46b3..c2d7183e1852 100644 --- a/xen/arch/riscv/aplic.c +++ b/xen/arch/riscv/aplic.c @@ -92,6 +92,8 @@ static int __init cf_check aplic_init(void) panic("%s: failed to get number of interrupt sources\n", node->full_name); + guest_aplic_num_sources = min(GUEST_APLIC_MAX_SOURCES, aplic_info.num_irqs); + if ( aplic_info.num_irqs > ARRAY_SIZE(aplic.regs->sourcecfg) ) aplic_info.num_irqs = ARRAY_SIZE(aplic.regs->sourcecfg); diff --git a/xen/arch/riscv/include/asm/aplic.h b/xen/arch/riscv/include/asm/aplic.h index 5a7fcb6ec4f1..07318aaac25d 100644 --- a/xen/arch/riscv/include/asm/aplic.h +++ b/xen/arch/riscv/include/asm/aplic.h @@ -34,6 +34,14 @@ #define APLIC_TARGET_HART_IDX_SHIFT 18 +#define APLIC_IDC_SIZE 32 + +#define APLIC_MIN_SIZE 0x4000 +#define APLIC_SIZE_ALIGN(x) ROUNDUP(x, APLIC_MIN_SIZE) + +#define APLIC_SIZE(nr_cpus) (APLIC_MIN_SIZE + \ + APLIC_SIZE_ALIGN(APLIC_IDC_SIZE * (nr_cpus))) + struct aplic_regs { uint32_t domaincfg; /* 0x0000 */ uint32_t sourcecfg[1023]; /* 0x0004 */ diff --git a/xen/arch/riscv/include/asm/guest-layout.h b/xen/arch/riscv/include/asm/guest-layout.h index 5e566450bdfa..90603f06bb91 100644 --- a/xen/arch/riscv/include/asm/guest-layout.h +++ b/xen/arch/riscv/include/asm/guest-layout.h @@ -3,6 +3,12 @@ #include <public/xen.h> +/* + * Base address of the guest's supervisor-mode APLIC. The value is the address + * typically used for APLIC by QEMU. + */ +#define GUEST_APLIC_S_BASE _UL(0xd000000) + /* * Base address of the guest's supervisor-mode IMSIC. The value is the address * typically used for IMSIC by QEMU. diff --git a/xen/arch/riscv/vaplic.c b/xen/arch/riscv/vaplic.c index c813979a2ecb..72bb2c4dc3c5 100644 --- a/xen/arch/riscv/vaplic.c +++ b/xen/arch/riscv/vaplic.c @@ -9,6 +9,8 @@ */ #include <xen/errno.h> +#include <xen/fdt-kernel.h> +#include <xen/libfdt/libfdt.h> #include <xen/sched.h> #include <xen/xvmalloc.h> @@ -19,6 +21,12 @@ #include "aplic-priv.h" +unsigned int __ro_after_init guest_aplic_num_sources; + +#define VAPLIC_COMPATIBLE "riscv,aplic" + +#define FDT_VAPLIC_INT_CELLS 2 + static int cf_check vaplic_init(struct vcpu *v) { return vcpu_imsic_init(v); @@ -29,6 +37,74 @@ static void cf_check vaplic_deinit(struct vcpu *v) return vcpu_imsic_deinit(v); } +static int __init cf_check vaplic_make_domu_dt_node(struct kernel_info *kinfo) +{ + struct domain *d = kinfo->bd.d; + int res; + void *fdt = kinfo->fdt; + unsigned int msi_parent_phandle; + char vaplic_name[32]; + unsigned int aplic_size = APLIC_SIZE(d->max_vcpus); + const __be32 reg[] = { + cpu_to_be32(GUEST_APLIC_S_BASE >> 32), + cpu_to_be32(GUEST_APLIC_S_BASE), + cpu_to_be32(0), + cpu_to_be32(aplic_size), + }; + + BUILD_BUG_ON(APLIC_SIZE(MAX_VIRT_CPUS) > UINT_MAX); + + res = snprintf(vaplic_name, ARRAY_SIZE(vaplic_name), "/soc/aplic@%lx", + GUEST_APLIC_S_BASE); + if ( res >= sizeof(vaplic_name) ) + { + dprintk(XENLOG_DEBUG, "vaplic name is truncated\n"); + return -ENOBUFS; + } + + res = vimsic_make_domu_dt_node(kinfo, &msi_parent_phandle); + if ( res ) + return res; + + res = fdt_begin_node(fdt, vaplic_name); + if ( res ) + return res; + + res = fdt_property_cell(fdt, "#interrupt-cells", FDT_VAPLIC_INT_CELLS); + if ( res ) + return res; + + res = fdt_property(fdt, "reg", reg, sizeof(reg)); + if ( res ) + return res; + + res = fdt_property_cell(fdt, "riscv,num-sources", guest_aplic_num_sources); + if ( res ) + return res; + + res = fdt_property(fdt, "interrupt-controller", NULL, 0); + if ( res ) + return res; + + res = fdt_property_string(fdt, "compatible", VAPLIC_COMPATIBLE); + if ( res ) + return res; + + res = fdt_property_cell(fdt, "msi-parent", msi_parent_phandle); + if ( res ) + return res; + + res = fdt_property_cell(fdt, "phandle", kinfo->phandle_intc); + if ( res ) + return res; + + return fdt_end_node(fdt); +} + +static const struct vintc_init_ops __initconstrel init_ops = { + .make_domu_dt_node = vaplic_make_domu_dt_node, +}; + static const struct vintc_ops vintc_ops = { .vcpu_init = vaplic_init, .vcpu_deinit = vaplic_deinit, @@ -43,6 +119,7 @@ int domain_vaplic_init(struct domain *d) d->arch.vintc = &vaplic->vintc; d->arch.vintc->ops = &vintc_ops; + d->arch.vintc->init_ops = &init_ops; vaplic->regs.domaincfg = APLIC_DOMAINCFG_RO; -- 2.55.0