* [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
* [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 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
* 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).