* [PATCH v3 0/3] xlnx-zynqmp: add support to boot on RPUs @ 2025-06-13 13:42 Clément Chigot 2025-06-13 13:42 ` [PATCH v3 1/3] hw/arm: make cpu targeted by arm_load_kernel the primary CPU Clément Chigot ` (2 more replies) 0 siblings, 3 replies; 6+ messages in thread From: Clément Chigot @ 2025-06-13 13:42 UTC (permalink / raw) To: qemu-devel Cc: qemu-arm, peter.maydell, edgar.iglesias, alistair, Clément Chigot This series enhances Xilinx ZynqMP support to allow booting on RPUs. It was validated with home-made binaries. FreeRTOS was tested but without success: outputs/IRQ seems broken. AFAICT, FreeRTOS is expecting Xilinx's QEMU thus I didn't investigate further. I'd still like advice on the 3rd patch ("wire a second GIC") since it could be related to it. Changes sinve v2: - address review for patch 3 (typo, create function to compute num_rpus, simplify num_rpus usage). - remove patch 4 (swapping cluster ids) Changes since v1: - add doc for "first-cpu-index" new property in arm_gic.h. Clément Chigot (1): hw/arm: make cpu targeted by arm_load_kernel the primary CPU. Frederic Konrad (2): hw/intc/arm_gic: introduce a first-cpu-index property hw/arm/xlnx-zynqmp: wire a second GIC for the Cortex-R5 hw/arm/boot.c | 15 +++-- hw/arm/xlnx-zynqmp.c | 103 ++++++++++++++++++++++++++++--- hw/intc/arm_gic.c | 2 +- hw/intc/arm_gic_common.c | 1 + include/hw/arm/boot.h | 3 + include/hw/arm/xlnx-zynqmp.h | 5 ++ include/hw/intc/arm_gic.h | 2 + include/hw/intc/arm_gic_common.h | 2 + 8 files changed, 114 insertions(+), 19 deletions(-) -- 2.34.1 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 1/3] hw/arm: make cpu targeted by arm_load_kernel the primary CPU. 2025-06-13 13:42 [PATCH v3 0/3] xlnx-zynqmp: add support to boot on RPUs Clément Chigot @ 2025-06-13 13:42 ` Clément Chigot 2025-06-16 5:44 ` Philippe Mathieu-Daudé 2025-06-13 13:42 ` [PATCH v3 2/3] hw/intc/arm_gic: introduce a first-cpu-index property Clément Chigot 2025-06-13 13:42 ` [PATCH v3 3/3] hw/arm/xlnx-zynqmp: wire a second GIC for the Cortex-R5 Clément Chigot 2 siblings, 1 reply; 6+ messages in thread From: Clément Chigot @ 2025-06-13 13:42 UTC (permalink / raw) To: qemu-devel Cc: qemu-arm, peter.maydell, edgar.iglesias, alistair, Clément Chigot Currently, arm booting processus assumes that the first_cpu is the CPU that will boot: `arm_load_kernel` is powering off all but the `first_cpu`; `do_cpu_reset` is setting the loader address only for this `first_cpu`. For most of the boards, this isn't an issue as the kernel is loaded and booted on the first CPU anyway. However, for zynqmp, the option "boot-cpu" allows to choose any CPUs. Create a new arm_boot_info entry `primary_cpu` recording which CPU will be boot first. This one is set when `arm_boot_kernel` is called. Signed-off-by: Clément Chigot <chigot@adacore.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/boot.c | 15 +++++++-------- include/hw/arm/boot.h | 3 +++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 79afb51b8a..3c93d87985 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -744,7 +744,7 @@ static void do_cpu_reset(void *opaque) } else { if (arm_feature(env, ARM_FEATURE_EL3) && (info->secure_boot || - (info->secure_board_setup && cs == first_cpu))) { + (info->secure_board_setup && cpu == info->primary_cpu))) { /* Start this CPU in Secure SVC */ target_el = 3; } @@ -752,7 +752,7 @@ static void do_cpu_reset(void *opaque) arm_emulate_firmware_reset(cs, target_el); - if (cs == first_cpu) { + if (cpu == info->primary_cpu) { AddressSpace *as = arm_boot_address_space(cpu, info); cpu_set_pc(cs, info->loader_start); @@ -1239,6 +1239,9 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) info->dtb_filename = ms->dtb; info->dtb_limit = 0; + /* We assume the CPU passed as argument is the primary CPU. */ + info->primary_cpu = cpu; + /* Load the kernel. */ if (!info->kernel_filename || info->firmware_loaded) { arm_setup_firmware_boot(cpu, info); @@ -1288,12 +1291,8 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) object_property_set_int(cpuobj, "psci-conduit", info->psci_conduit, &error_abort); - /* - * Secondary CPUs start in PSCI powered-down state. Like the - * code in do_cpu_reset(), we assume first_cpu is the primary - * CPU. - */ - if (cs != first_cpu) { + /* Secondary CPUs start in PSCI powered-down state. */ + if (ARM_CPU(cs) != info->primary_cpu) { object_property_set_bool(cpuobj, "start-powered-off", true, &error_abort); } diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index b12bf61ca8..a2e22bda8a 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -132,6 +132,9 @@ struct arm_boot_info { bool secure_board_setup; arm_endianness endianness; + + /* CPU having load the kernel and that should be the first to boot. */ + ARMCPU *primary_cpu; }; /** -- 2.34.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3 1/3] hw/arm: make cpu targeted by arm_load_kernel the primary CPU. 2025-06-13 13:42 ` [PATCH v3 1/3] hw/arm: make cpu targeted by arm_load_kernel the primary CPU Clément Chigot @ 2025-06-16 5:44 ` Philippe Mathieu-Daudé 0 siblings, 0 replies; 6+ messages in thread From: Philippe Mathieu-Daudé @ 2025-06-16 5:44 UTC (permalink / raw) To: Clément Chigot, qemu-devel Cc: qemu-arm, peter.maydell, edgar.iglesias, alistair On 13/6/25 15:42, Clément Chigot wrote: > Currently, arm booting processus assumes that the first_cpu is the CPU > that will boot: `arm_load_kernel` is powering off all but the `first_cpu`; > `do_cpu_reset` is setting the loader address only for this `first_cpu`. > > For most of the boards, this isn't an issue as the kernel is loaded and > booted on the first CPU anyway. However, for zynqmp, the option > "boot-cpu" allows to choose any CPUs. > > Create a new arm_boot_info entry `primary_cpu` recording which CPU will > be boot first. This one is set when `arm_boot_kernel` is called. > > Signed-off-by: Clément Chigot <chigot@adacore.com> > Reviewed-by: Peter Maydell <peter.maydell@linaro.org> > --- > hw/arm/boot.c | 15 +++++++-------- > include/hw/arm/boot.h | 3 +++ > 2 files changed, 10 insertions(+), 8 deletions(-) Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 2/3] hw/intc/arm_gic: introduce a first-cpu-index property 2025-06-13 13:42 [PATCH v3 0/3] xlnx-zynqmp: add support to boot on RPUs Clément Chigot 2025-06-13 13:42 ` [PATCH v3 1/3] hw/arm: make cpu targeted by arm_load_kernel the primary CPU Clément Chigot @ 2025-06-13 13:42 ` Clément Chigot 2025-06-13 13:42 ` [PATCH v3 3/3] hw/arm/xlnx-zynqmp: wire a second GIC for the Cortex-R5 Clément Chigot 2 siblings, 0 replies; 6+ messages in thread From: Clément Chigot @ 2025-06-13 13:42 UTC (permalink / raw) To: qemu-devel Cc: qemu-arm, peter.maydell, edgar.iglesias, alistair, Frederic Konrad, Clément Chigot From: Frederic Konrad <konrad.frederic@yahoo.fr> This introduces a first-cpu-index property to the arm-gic, as some SOCs could have two separate GIC (ie: the zynqmp). Signed-off-by: Clément Chigot <chigot@adacore.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> --- hw/intc/arm_gic.c | 2 +- hw/intc/arm_gic_common.c | 1 + include/hw/intc/arm_gic.h | 2 ++ include/hw/intc/arm_gic_common.h | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index d18bef40fc..899f133363 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -59,7 +59,7 @@ static const uint8_t gic_id_gicv2[] = { static inline int gic_get_current_cpu(GICState *s) { if (!qtest_enabled() && s->num_cpu > 1) { - return current_cpu->cpu_index; + return current_cpu->cpu_index - s->first_cpu_index; } return 0; } diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 0f0c48d89a..ed5be05645 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -350,6 +350,7 @@ static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, static const Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1), + DEFINE_PROP_UINT32("first-cpu-index", GICState, first_cpu_index, 0), DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32), /* Revision can be 1 or 2 for GIC architecture specification * versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC. diff --git a/include/hw/intc/arm_gic.h b/include/hw/intc/arm_gic.h index 48f6a51a70..6faccf8ef6 100644 --- a/include/hw/intc/arm_gic.h +++ b/include/hw/intc/arm_gic.h @@ -27,6 +27,8 @@ * implement the security extensions * + QOM property "has-virtualization-extensions": set true if the GIC should * implement the virtualization extensions + * + QOM property "first-cpu-index": index of the first cpu attached to the + * GIC. * + unnamed GPIO inputs: (where P is number of SPIs, i.e. num-irq - 32) * [0..P-1] SPIs * [P..P+31] PPIs for CPU 0 diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index 97fea4102d..93a3cc2bf8 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -129,6 +129,8 @@ struct GICState { uint32_t num_lrs; uint32_t num_cpu; + /* cpu_index of the first CPU, attached to this GIC. */ + uint32_t first_cpu_index; MemoryRegion iomem; /* Distributor */ /* This is just so we can have an opaque pointer which identifies -- 2.34.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 3/3] hw/arm/xlnx-zynqmp: wire a second GIC for the Cortex-R5 2025-06-13 13:42 [PATCH v3 0/3] xlnx-zynqmp: add support to boot on RPUs Clément Chigot 2025-06-13 13:42 ` [PATCH v3 1/3] hw/arm: make cpu targeted by arm_load_kernel the primary CPU Clément Chigot 2025-06-13 13:42 ` [PATCH v3 2/3] hw/intc/arm_gic: introduce a first-cpu-index property Clément Chigot @ 2025-06-13 13:42 ` Clément Chigot 2025-08-25 9:50 ` Clément Chigot 2 siblings, 1 reply; 6+ messages in thread From: Clément Chigot @ 2025-06-13 13:42 UTC (permalink / raw) To: qemu-devel Cc: qemu-arm, peter.maydell, edgar.iglesias, alistair, Frederic Konrad, Clément Chigot From: Frederic Konrad <konrad.frederic@yahoo.fr> This wires a second GIC for the Cortex-R5, all the IRQs are split when there is an RPU instanciated. Signed-off-by: Clément Chigot <chigot@adacore.com> --- hw/arm/xlnx-zynqmp.c | 103 +++++++++++++++++++++++++++++++---- include/hw/arm/xlnx-zynqmp.h | 5 ++ 2 files changed, 98 insertions(+), 10 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index ec96a46eec..ffed6e5126 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -26,8 +26,6 @@ #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" -#define GIC_NUM_SPI_INTR 160 - #define ARM_PHYS_TIMER_PPI 30 #define ARM_VIRT_TIMER_PPI 27 #define ARM_HYP_TIMER_PPI 26 @@ -206,17 +204,26 @@ static const XlnxZynqMPGICRegion xlnx_zynqmp_gic_regions[] = { static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index) { - return GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index; + return XLNX_ZYNQMP_GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index; +} + +static unsigned int xlnx_zynqmp_get_rpu_number(MachineState *ms) +{ + /* + * RPUs will be created only if "-smp" is higher than the maximum + * of APUs. Round it up to 0 to avoid dealing with negative values. + */ + return MAX(0, MIN((int)(ms->smp.cpus - XLNX_ZYNQMP_NUM_APU_CPUS), + XLNX_ZYNQMP_NUM_RPU_CPUS)); } static void xlnx_zynqmp_create_rpu(MachineState *ms, XlnxZynqMPState *s, const char *boot_cpu, Error **errp) { int i; - int num_rpus = MIN((int)(ms->smp.cpus - XLNX_ZYNQMP_NUM_APU_CPUS), - XLNX_ZYNQMP_NUM_RPU_CPUS); + int num_rpus = xlnx_zynqmp_get_rpu_number(ms); - if (num_rpus <= 0) { + if (!num_rpus) { /* Don't create rpu-cluster object if there's nothing to put in it */ return; } @@ -377,6 +384,7 @@ static void xlnx_zynqmp_init(Object *obj) XlnxZynqMPState *s = XLNX_ZYNQMP(obj); int i; int num_apus = MIN(ms->smp.cpus, XLNX_ZYNQMP_NUM_APU_CPUS); + int num_rpus = xlnx_zynqmp_get_rpu_number(ms); object_initialize_child(obj, "apu-cluster", &s->apu_cluster, TYPE_CPU_CLUSTER); @@ -390,6 +398,12 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize_child(obj, "gic", &s->gic, gic_class_name()); + if (num_rpus) { + /* Do not create the rpu_gic if we don't have rpus */ + object_initialize_child(obj, "rpu_gic", &s->rpu_gic, + gic_class_name()); + } + for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { object_initialize_child(obj, "gem[*]", &s->gem[i], TYPE_CADENCE_GEM); object_initialize_child(obj, "gem-irq-orgate[*]", @@ -439,6 +453,15 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize_child(obj, "qspi-irq-orgate", &s->qspi_irq_orgate, TYPE_OR_IRQ); + if (num_rpus) { + for (i = 0; i < ARRAY_SIZE(s->splitter); i++) { + g_autofree char *name = g_strdup_printf("irq-splitter%d", i); + object_initialize_child(obj, name, &s->splitter[i], TYPE_SPLIT_IRQ); + } + } + + + for (i = 0; i < XLNX_ZYNQMP_NUM_USB; i++) { object_initialize_child(obj, "usb[*]", &s->usb[i], TYPE_USB_DWC3); } @@ -452,9 +475,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) uint8_t i; uint64_t ram_size; int num_apus = MIN(ms->smp.cpus, XLNX_ZYNQMP_NUM_APU_CPUS); + int num_rpus = xlnx_zynqmp_get_rpu_number(ms); const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]"; ram_addr_t ddr_low_size, ddr_high_size; - qemu_irq gic_spi[GIC_NUM_SPI_INTR]; + qemu_irq gic_spi[XLNX_ZYNQMP_GIC_NUM_SPI_INTR]; Error *err = NULL; ram_size = memory_region_size(s->ddr_ram); @@ -502,13 +526,22 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) g_free(ocm_name); } - qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", + XLNX_ZYNQMP_GIC_NUM_SPI_INTR + 32); qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", num_apus); qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", s->secure); qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", s->virt); + if (num_rpus) { + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "num-irq", + XLNX_ZYNQMP_GIC_NUM_SPI_INTR + 32); + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "revision", 1); + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "num-cpu", num_rpus); + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "first-cpu-index", 4); + } + qdev_realize(DEVICE(&s->apu_cluster), NULL, &error_fatal); /* Realize APUs before realizing the GIC. KVM requires this. */ @@ -608,13 +641,63 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) return; } + if (num_rpus) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rpu_gic), errp)) { + return; + } + + for (i = 0; i < num_rpus; i++) { + qemu_irq irq; + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rpu_gic), i + 1, + GIC_BASE_ADDR + i * 0x1000); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i, + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), + ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i + num_rpus, + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), + ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i + num_rpus * 2, + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), + ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i + num_rpus * 3, + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), + ARM_CPU_VFIQ)); + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), + arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_PHYS, irq); + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), + arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_VIRT, irq); + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), + arm_gic_ppi_index(i, ARM_HYP_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_HYP, irq); + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), + arm_gic_ppi_index(i, ARM_SEC_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_SEC, irq); + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rpu_gic), 0, GIC_BASE_ADDR); + } + if (!s->boot_cpu_ptr) { error_setg(errp, "ZynqMP Boot cpu %s not found", boot_cpu); return; } - for (i = 0; i < GIC_NUM_SPI_INTR; i++) { - gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i); + for (i = 0; i < XLNX_ZYNQMP_GIC_NUM_SPI_INTR; i++) { + if (num_rpus) { + DeviceState *splitter = DEVICE(&s->splitter[i]); + qdev_prop_set_uint16(splitter, "num-lines", 2); + qdev_realize(splitter, NULL, &error_abort); + gic_spi[i] = qdev_get_gpio_in(splitter, 0); + qdev_connect_gpio_out(splitter, 0, + qdev_get_gpio_in(DEVICE(&s->gic), i)); + qdev_connect_gpio_out(splitter, 1, + qdev_get_gpio_in(DEVICE(&s->rpu_gic), i)); + } else { + gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i); + } } for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index c137ac59e8..0ae00e10f6 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -42,6 +42,7 @@ #include "hw/misc/xlnx-zynqmp-crf.h" #include "hw/timer/cadence_ttc.h" #include "hw/usb/hcd-dwc3.h" +#include "hw/core/split-irq.h" #define TYPE_XLNX_ZYNQMP "xlnx-zynqmp" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) @@ -87,6 +88,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) XLNX_ZYNQMP_MAX_HIGH_RAM_SIZE) #define XLNX_ZYNQMP_NUM_TTC 4 +#define XLNX_ZYNQMP_GIC_NUM_SPI_INTR 160 /* * Unimplemented mmio regions needed to boot some images. @@ -105,6 +107,9 @@ struct XlnxZynqMPState { GICState gic; MemoryRegion gic_mr[XLNX_ZYNQMP_GIC_REGIONS][XLNX_ZYNQMP_GIC_ALIASES]; + GICState rpu_gic; + SplitIRQ splitter[XLNX_ZYNQMP_GIC_NUM_SPI_INTR]; + MemoryRegion ocm_ram[XLNX_ZYNQMP_NUM_OCM_BANKS]; MemoryRegion *ddr_ram; -- 2.34.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3 3/3] hw/arm/xlnx-zynqmp: wire a second GIC for the Cortex-R5 2025-06-13 13:42 ` [PATCH v3 3/3] hw/arm/xlnx-zynqmp: wire a second GIC for the Cortex-R5 Clément Chigot @ 2025-08-25 9:50 ` Clément Chigot 0 siblings, 0 replies; 6+ messages in thread From: Clément Chigot @ 2025-08-25 9:50 UTC (permalink / raw) To: qemu-devel Cc: qemu-arm, peter.maydell, edgar.iglesias, alistair, Frederic Konrad On Fri, Jun 13, 2025 at 3:42 PM Clément Chigot <chigot@adacore.com> wrote: > > From: Frederic Konrad <konrad.frederic@yahoo.fr> > > This wires a second GIC for the Cortex-R5, all the IRQs are split when there > is an RPU instanciated. > > Signed-off-by: Clément Chigot <chigot@adacore.com> Gentle ping here (just realized this patch hasn't been merged as I thought...) TIA, Clément > --- > hw/arm/xlnx-zynqmp.c | 103 +++++++++++++++++++++++++++++++---- > include/hw/arm/xlnx-zynqmp.h | 5 ++ > 2 files changed, 98 insertions(+), 10 deletions(-) > > diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c > index ec96a46eec..ffed6e5126 100644 > --- a/hw/arm/xlnx-zynqmp.c > +++ b/hw/arm/xlnx-zynqmp.c > @@ -26,8 +26,6 @@ > #include "target/arm/cpu-qom.h" > #include "target/arm/gtimer.h" > > -#define GIC_NUM_SPI_INTR 160 > - > #define ARM_PHYS_TIMER_PPI 30 > #define ARM_VIRT_TIMER_PPI 27 > #define ARM_HYP_TIMER_PPI 26 > @@ -206,17 +204,26 @@ static const XlnxZynqMPGICRegion xlnx_zynqmp_gic_regions[] = { > > static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index) > { > - return GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index; > + return XLNX_ZYNQMP_GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index; > +} > + > +static unsigned int xlnx_zynqmp_get_rpu_number(MachineState *ms) > +{ > + /* > + * RPUs will be created only if "-smp" is higher than the maximum > + * of APUs. Round it up to 0 to avoid dealing with negative values. > + */ > + return MAX(0, MIN((int)(ms->smp.cpus - XLNX_ZYNQMP_NUM_APU_CPUS), > + XLNX_ZYNQMP_NUM_RPU_CPUS)); > } > > static void xlnx_zynqmp_create_rpu(MachineState *ms, XlnxZynqMPState *s, > const char *boot_cpu, Error **errp) > { > int i; > - int num_rpus = MIN((int)(ms->smp.cpus - XLNX_ZYNQMP_NUM_APU_CPUS), > - XLNX_ZYNQMP_NUM_RPU_CPUS); > + int num_rpus = xlnx_zynqmp_get_rpu_number(ms); > > - if (num_rpus <= 0) { > + if (!num_rpus) { > /* Don't create rpu-cluster object if there's nothing to put in it */ > return; > } > @@ -377,6 +384,7 @@ static void xlnx_zynqmp_init(Object *obj) > XlnxZynqMPState *s = XLNX_ZYNQMP(obj); > int i; > int num_apus = MIN(ms->smp.cpus, XLNX_ZYNQMP_NUM_APU_CPUS); > + int num_rpus = xlnx_zynqmp_get_rpu_number(ms); > > object_initialize_child(obj, "apu-cluster", &s->apu_cluster, > TYPE_CPU_CLUSTER); > @@ -390,6 +398,12 @@ static void xlnx_zynqmp_init(Object *obj) > > object_initialize_child(obj, "gic", &s->gic, gic_class_name()); > > + if (num_rpus) { > + /* Do not create the rpu_gic if we don't have rpus */ > + object_initialize_child(obj, "rpu_gic", &s->rpu_gic, > + gic_class_name()); > + } > + > for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { > object_initialize_child(obj, "gem[*]", &s->gem[i], TYPE_CADENCE_GEM); > object_initialize_child(obj, "gem-irq-orgate[*]", > @@ -439,6 +453,15 @@ static void xlnx_zynqmp_init(Object *obj) > object_initialize_child(obj, "qspi-irq-orgate", > &s->qspi_irq_orgate, TYPE_OR_IRQ); > > + if (num_rpus) { > + for (i = 0; i < ARRAY_SIZE(s->splitter); i++) { > + g_autofree char *name = g_strdup_printf("irq-splitter%d", i); > + object_initialize_child(obj, name, &s->splitter[i], TYPE_SPLIT_IRQ); > + } > + } > + > + > + > for (i = 0; i < XLNX_ZYNQMP_NUM_USB; i++) { > object_initialize_child(obj, "usb[*]", &s->usb[i], TYPE_USB_DWC3); > } > @@ -452,9 +475,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) > uint8_t i; > uint64_t ram_size; > int num_apus = MIN(ms->smp.cpus, XLNX_ZYNQMP_NUM_APU_CPUS); > + int num_rpus = xlnx_zynqmp_get_rpu_number(ms); > const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]"; > ram_addr_t ddr_low_size, ddr_high_size; > - qemu_irq gic_spi[GIC_NUM_SPI_INTR]; > + qemu_irq gic_spi[XLNX_ZYNQMP_GIC_NUM_SPI_INTR]; > Error *err = NULL; > > ram_size = memory_region_size(s->ddr_ram); > @@ -502,13 +526,22 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) > g_free(ocm_name); > } > > - qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32); > + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", > + XLNX_ZYNQMP_GIC_NUM_SPI_INTR + 32); > qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); > qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", num_apus); > qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", s->secure); > qdev_prop_set_bit(DEVICE(&s->gic), > "has-virtualization-extensions", s->virt); > > + if (num_rpus) { > + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "num-irq", > + XLNX_ZYNQMP_GIC_NUM_SPI_INTR + 32); > + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "revision", 1); > + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "num-cpu", num_rpus); > + qdev_prop_set_uint32(DEVICE(&s->rpu_gic), "first-cpu-index", 4); > + } > + > qdev_realize(DEVICE(&s->apu_cluster), NULL, &error_fatal); > > /* Realize APUs before realizing the GIC. KVM requires this. */ > @@ -608,13 +641,63 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) > return; > } > > + if (num_rpus) { > + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rpu_gic), errp)) { > + return; > + } > + > + for (i = 0; i < num_rpus; i++) { > + qemu_irq irq; > + > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rpu_gic), i + 1, > + GIC_BASE_ADDR + i * 0x1000); > + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i, > + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), > + ARM_CPU_IRQ)); > + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i + num_rpus, > + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), > + ARM_CPU_FIQ)); > + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i + num_rpus * 2, > + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), > + ARM_CPU_VIRQ)); > + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rpu_gic), i + num_rpus * 3, > + qdev_get_gpio_in(DEVICE(&s->rpu_cpu[i]), > + ARM_CPU_VFIQ)); > + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), > + arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI)); > + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_PHYS, irq); > + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), > + arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI)); > + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_VIRT, irq); > + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), > + arm_gic_ppi_index(i, ARM_HYP_TIMER_PPI)); > + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_HYP, irq); > + irq = qdev_get_gpio_in(DEVICE(&s->rpu_gic), > + arm_gic_ppi_index(i, ARM_SEC_TIMER_PPI)); > + qdev_connect_gpio_out(DEVICE(&s->rpu_cpu[i]), GTIMER_SEC, irq); > + } > + > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rpu_gic), 0, GIC_BASE_ADDR); > + } > + > if (!s->boot_cpu_ptr) { > error_setg(errp, "ZynqMP Boot cpu %s not found", boot_cpu); > return; > } > > - for (i = 0; i < GIC_NUM_SPI_INTR; i++) { > - gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i); > + for (i = 0; i < XLNX_ZYNQMP_GIC_NUM_SPI_INTR; i++) { > + if (num_rpus) { > + DeviceState *splitter = DEVICE(&s->splitter[i]); > + qdev_prop_set_uint16(splitter, "num-lines", 2); > + qdev_realize(splitter, NULL, &error_abort); > + gic_spi[i] = qdev_get_gpio_in(splitter, 0); > + qdev_connect_gpio_out(splitter, 0, > + qdev_get_gpio_in(DEVICE(&s->gic), i)); > + qdev_connect_gpio_out(splitter, 1, > + qdev_get_gpio_in(DEVICE(&s->rpu_gic), i)); > + } else { > + gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i); > + } > } > > for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { > diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h > index c137ac59e8..0ae00e10f6 100644 > --- a/include/hw/arm/xlnx-zynqmp.h > +++ b/include/hw/arm/xlnx-zynqmp.h > @@ -42,6 +42,7 @@ > #include "hw/misc/xlnx-zynqmp-crf.h" > #include "hw/timer/cadence_ttc.h" > #include "hw/usb/hcd-dwc3.h" > +#include "hw/core/split-irq.h" > > #define TYPE_XLNX_ZYNQMP "xlnx-zynqmp" > OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) > @@ -87,6 +88,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) > XLNX_ZYNQMP_MAX_HIGH_RAM_SIZE) > > #define XLNX_ZYNQMP_NUM_TTC 4 > +#define XLNX_ZYNQMP_GIC_NUM_SPI_INTR 160 > > /* > * Unimplemented mmio regions needed to boot some images. > @@ -105,6 +107,9 @@ struct XlnxZynqMPState { > GICState gic; > MemoryRegion gic_mr[XLNX_ZYNQMP_GIC_REGIONS][XLNX_ZYNQMP_GIC_ALIASES]; > > + GICState rpu_gic; > + SplitIRQ splitter[XLNX_ZYNQMP_GIC_NUM_SPI_INTR]; > + > MemoryRegion ocm_ram[XLNX_ZYNQMP_NUM_OCM_BANKS]; > > MemoryRegion *ddr_ram; > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-08-25 9:51 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-06-13 13:42 [PATCH v3 0/3] xlnx-zynqmp: add support to boot on RPUs Clément Chigot 2025-06-13 13:42 ` [PATCH v3 1/3] hw/arm: make cpu targeted by arm_load_kernel the primary CPU Clément Chigot 2025-06-16 5:44 ` Philippe Mathieu-Daudé 2025-06-13 13:42 ` [PATCH v3 2/3] hw/intc/arm_gic: introduce a first-cpu-index property Clément Chigot 2025-06-13 13:42 ` [PATCH v3 3/3] hw/arm/xlnx-zynqmp: wire a second GIC for the Cortex-R5 Clément Chigot 2025-08-25 9:50 ` Clément Chigot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).