diff --git a/xen/arch/riscv/aplic-priv.h b/xen/arch/riscv/aplic-priv.h index 85e0d028d1..35100d3a64 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 d08401db46..c2d7183e18 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 5a7fcb6ec4..07318aaac2 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 5e566450bd..90603f06bb 100644 --- a/xen/arch/riscv/include/asm/guest-layout.h +++ b/xen/arch/riscv/include/asm/guest-layout.h @@ -3,6 +3,12 @@ #include +/* + * 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 c9f188b989..a529a5b1dc 100644 --- a/xen/arch/riscv/vaplic.c +++ b/xen/arch/riscv/vaplic.c @@ -9,6 +9,8 @@ */ #include +#include +#include #include #include @@ -19,6 +21,80 @@ #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 __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 = vcpu_imsic_init, .vcpu_deinit = vcpu_imsic_deinit, @@ -33,6 +109,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;