* [PATCH v3 1/4] target/riscv: add riscv_cpu_has_supervisor_mode()
2026-10-22 20:22 [PATCH v3 0/4] hw/riscv: describe only the PLIC contexts that exist Ralf Ramsauer
@ 2026-10-22 20:22 ` Ralf Ramsauer
2026-10-23 2:19 ` Chao Liu
2026-10-22 20:22 ` [PATCH v3 2/4] hw/riscv/virt: only describe PLIC contexts that exist Ralf Ramsauer
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Ralf Ramsauer @ 2026-10-22 20:22 UTC (permalink / raw)
To: qemu-devel, Daniel Henrique Barboza
Cc: Ralf Ramsauer, Ralf Ramsauer, qemu-riscv, Palmer Dabbelt,
Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu, Bin Meng,
Lukas Landgraf
From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
Whether a hart has supervisor mode is decided in two places: KVM harts
always have it, and for TCG it follows from RVS in the ISA string.
riscv_plic_hart_config_string() already spells out both cases.
Board code needs the same answer to describe the interrupt controller,
but it should not have to reach into CPURISCVState for it. Add a helper
next to riscv_cpu_is_vendor() so the next patches can ask the CPU
instead of inspecting its internals.
Suggested-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
---
target/riscv/cpu.c | 14 ++++++++++++++
target/riscv/cpu.h | 1 +
2 files changed, 15 insertions(+)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 08a4d14ea1..7be1c65902 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -338,6 +338,20 @@ bool riscv_cpu_is_vendor(Object *cpu_obj)
return object_dynamic_cast(cpu_obj, TYPE_RISCV_VENDOR_CPU) != NULL;
}
+bool riscv_cpu_has_supervisor_mode(RISCVCPU *cpu)
+{
+#ifndef CONFIG_USER_ONLY
+ /* KVM always provides supervisor mode, regardless of the ISA string */
+ if (kvm_enabled()) {
+ return true;
+ }
+
+ return riscv_has_ext(&cpu->env, RVS);
+#else
+ return false;
+#endif
+}
+
const char * const riscv_int_regnames[] = {
"x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1",
"x7/t2", "x8/s0", "x9/s1", "x10/a0", "x11/a1", "x12/a2", "x13/a3",
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index b65d631994..a7509b0114 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -995,6 +995,7 @@ void isa_ext_update_enabled(RISCVCPU *cpu, uint32_t ext_offset, bool en);
bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset);
void riscv_cpu_set_misa_ext(CPURISCVState *env, uint32_t ext);
bool riscv_cpu_is_vendor(Object *cpu_obj);
+bool riscv_cpu_has_supervisor_mode(RISCVCPU *cpu);
typedef struct isa_ext_data {
const char *name;
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v3 1/4] target/riscv: add riscv_cpu_has_supervisor_mode()
2026-10-22 20:22 ` [PATCH v3 1/4] target/riscv: add riscv_cpu_has_supervisor_mode() Ralf Ramsauer
@ 2026-10-23 2:19 ` Chao Liu
0 siblings, 0 replies; 8+ messages in thread
From: Chao Liu @ 2026-10-23 2:19 UTC (permalink / raw)
To: Ralf Ramsauer
Cc: qemu-devel, Daniel Henrique Barboza, Ralf Ramsauer, qemu-riscv,
Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei, Bin Meng,
Lukas Landgraf
On Thu, Oct 22, 2026 at 10:22:15PM +0800, Ralf Ramsauer wrote:
> From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
>
> Whether a hart has supervisor mode is decided in two places: KVM harts
> always have it, and for TCG it follows from RVS in the ISA string.
> riscv_plic_hart_config_string() already spells out both cases.
>
> Board code needs the same answer to describe the interrupt controller,
> but it should not have to reach into CPURISCVState for it. Add a helper
> next to riscv_cpu_is_vendor() so the next patches can ask the CPU
> instead of inspecting its internals.
>
> Suggested-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
> Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Reviewed-by: Chao Liu <chao.liu@processmission.com>
Thanks,
Chao
> ---
> target/riscv/cpu.c | 14 ++++++++++++++
> target/riscv/cpu.h | 1 +
> 2 files changed, 15 insertions(+)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 08a4d14ea1..7be1c65902 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -338,6 +338,20 @@ bool riscv_cpu_is_vendor(Object *cpu_obj)
> return object_dynamic_cast(cpu_obj, TYPE_RISCV_VENDOR_CPU) != NULL;
> }
>
> +bool riscv_cpu_has_supervisor_mode(RISCVCPU *cpu)
> +{
> +#ifndef CONFIG_USER_ONLY
> + /* KVM always provides supervisor mode, regardless of the ISA string */
> + if (kvm_enabled()) {
> + return true;
> + }
> +
> + return riscv_has_ext(&cpu->env, RVS);
> +#else
> + return false;
> +#endif
> +}
> +
> const char * const riscv_int_regnames[] = {
> "x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1",
> "x7/t2", "x8/s0", "x9/s1", "x10/a0", "x11/a1", "x12/a2", "x13/a3",
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index b65d631994..a7509b0114 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -995,6 +995,7 @@ void isa_ext_update_enabled(RISCVCPU *cpu, uint32_t ext_offset, bool en);
> bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset);
> void riscv_cpu_set_misa_ext(CPURISCVState *env, uint32_t ext);
> bool riscv_cpu_is_vendor(Object *cpu_obj);
> +bool riscv_cpu_has_supervisor_mode(RISCVCPU *cpu);
>
> typedef struct isa_ext_data {
> const char *name;
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 2/4] hw/riscv/virt: only describe PLIC contexts that exist
2026-10-22 20:22 [PATCH v3 0/4] hw/riscv: describe only the PLIC contexts that exist Ralf Ramsauer
2026-10-22 20:22 ` [PATCH v3 1/4] target/riscv: add riscv_cpu_has_supervisor_mode() Ralf Ramsauer
@ 2026-10-22 20:22 ` Ralf Ramsauer
2026-10-23 3:05 ` Chao Liu
2026-10-22 20:22 ` [PATCH v3 3/4] hw/riscv/sifive_u: derive PLIC contexts from the harts Ralf Ramsauer
2026-10-22 20:22 ` [PATCH v3 4/4] hw/riscv: share the PLIC interrupt cells between boards Ralf Ramsauer
3 siblings, 1 reply; 8+ messages in thread
From: Ralf Ramsauer @ 2026-10-22 20:22 UTC (permalink / raw)
To: qemu-devel, Daniel Henrique Barboza
Cc: Ralf Ramsauer, Ralf Ramsauer, qemu-riscv, Palmer Dabbelt,
Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu, Bin Meng,
Lukas Landgraf
From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
The PLIC device is created from the topology string built by
riscv_plic_hart_config_string(), which gives a hart a supervisor context
only if that hart actually has supervisor mode. The device tree, in
contrast, lists a machine and a supervisor context for every hart,
unconditionally.
The two have disagreed since commit 7d10ff8a4d ("hw/riscv: virt: Use the
PLIC config helper function") taught the device about harts without
S-mode and left the device tree behind. Boot with
-cpu rv64,s=false,u=false -smp 4
and the PLIC implements four contexts while the device tree advertises
eight. Contexts are numbered by their position in 'interrupts-extended',
so a guest that takes its context number from the property gets it wrong
for every hart but the first: hart 1 drives the context of hart 2, and
the upper harts address contexts the PLIC never created. This is what an
M-mode kernel on the virt machine runs into.
Emit the supervisor entry only for the harts that have supervisor mode.
The KVM case then becomes the same rule, not a separate one: its harts
have no M-mode and always have supervisor mode, so the common loop
describes the single supervisor context per hart that it used to spell
out on its own.
Fixes: 7d10ff8a4de7 ("hw/riscv: virt: Use the PLIC config helper function")
Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
---
hw/riscv/virt.c | 28 ++++++++++++----------------
1 file changed, 12 insertions(+), 16 deletions(-)
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index d4b501802f..a2851b506d 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -156,23 +156,19 @@ static void create_fdt_socket_plic(RISCVVirtState *s,
plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr);
- if (kvm_enabled()) {
- cells_length = s->soc[socket].num_harts * 2;
- plic_cells = g_new0(uint32_t, cells_length);
-
- for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
- plic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
- plic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT);
+ plic_cells = g_new0(uint32_t,
+ s->soc[socket].num_harts * (kvm_enabled() ? 2 : 4));
+ cells_length = 0;
+
+ for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
+ if (!kvm_enabled()) {
+ plic_cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
+ plic_cells[cells_length++] = cpu_to_be32(IRQ_M_EXT);
}
- } else {
- cells_length = s->soc[socket].num_harts * 4;
- plic_cells = g_new0(uint32_t, cells_length);
-
- for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
- plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
- plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
- plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
- plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+
+ if (riscv_cpu_has_supervisor_mode(&s->soc[socket].harts[cpu])) {
+ plic_cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
+ plic_cells[cells_length++] = cpu_to_be32(IRQ_S_EXT);
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v3 2/4] hw/riscv/virt: only describe PLIC contexts that exist
2026-10-22 20:22 ` [PATCH v3 2/4] hw/riscv/virt: only describe PLIC contexts that exist Ralf Ramsauer
@ 2026-10-23 3:05 ` Chao Liu
0 siblings, 0 replies; 8+ messages in thread
From: Chao Liu @ 2026-10-23 3:05 UTC (permalink / raw)
To: Ralf Ramsauer
Cc: qemu-devel, Daniel Henrique Barboza, Ralf Ramsauer, qemu-riscv,
Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei, Bin Meng,
Lukas Landgraf
On Thu, Oct 22, 2026 at 10:22:16PM +0800, Ralf Ramsauer wrote:
> From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
>
> The PLIC device is created from the topology string built by
> riscv_plic_hart_config_string(), which gives a hart a supervisor context
> only if that hart actually has supervisor mode. The device tree, in
> contrast, lists a machine and a supervisor context for every hart,
> unconditionally.
>
> The two have disagreed since commit 7d10ff8a4d ("hw/riscv: virt: Use the
> PLIC config helper function") taught the device about harts without
> S-mode and left the device tree behind. Boot with
>
> -cpu rv64,s=false,u=false -smp 4
>
> and the PLIC implements four contexts while the device tree advertises
> eight. Contexts are numbered by their position in 'interrupts-extended',
> so a guest that takes its context number from the property gets it wrong
> for every hart but the first: hart 1 drives the context of hart 2, and
> the upper harts address contexts the PLIC never created. This is what an
> M-mode kernel on the virt machine runs into.
>
> Emit the supervisor entry only for the harts that have supervisor mode.
> The KVM case then becomes the same rule, not a separate one: its harts
> have no M-mode and always have supervisor mode, so the common loop
> describes the single supervisor context per hart that it used to spell
> out on its own.
>
> Fixes: 7d10ff8a4de7 ("hw/riscv: virt: Use the PLIC config helper function")
> Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Reviewed-by: Chao Liu <chao.liu@processmission.com>
Thanks,
Chao
> ---
> hw/riscv/virt.c | 28 ++++++++++++----------------
> 1 file changed, 12 insertions(+), 16 deletions(-)
>
> diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
> index d4b501802f..a2851b506d 100644
> --- a/hw/riscv/virt.c
> +++ b/hw/riscv/virt.c
> @@ -156,23 +156,19 @@ static void create_fdt_socket_plic(RISCVVirtState *s,
>
> plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr);
>
> - if (kvm_enabled()) {
> - cells_length = s->soc[socket].num_harts * 2;
> - plic_cells = g_new0(uint32_t, cells_length);
> -
> - for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
> - plic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
> - plic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT);
> + plic_cells = g_new0(uint32_t,
> + s->soc[socket].num_harts * (kvm_enabled() ? 2 : 4));
> + cells_length = 0;
> +
> + for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
> + if (!kvm_enabled()) {
> + plic_cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
> + plic_cells[cells_length++] = cpu_to_be32(IRQ_M_EXT);
> }
> - } else {
> - cells_length = s->soc[socket].num_harts * 4;
> - plic_cells = g_new0(uint32_t, cells_length);
> -
> - for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
> - plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
> - plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
> - plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
> - plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
> +
> + if (riscv_cpu_has_supervisor_mode(&s->soc[socket].harts[cpu])) {
> + plic_cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
> + plic_cells[cells_length++] = cpu_to_be32(IRQ_S_EXT);
> }
> }
>
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 3/4] hw/riscv/sifive_u: derive PLIC contexts from the harts
2026-10-22 20:22 [PATCH v3 0/4] hw/riscv: describe only the PLIC contexts that exist Ralf Ramsauer
2026-10-22 20:22 ` [PATCH v3 1/4] target/riscv: add riscv_cpu_has_supervisor_mode() Ralf Ramsauer
2026-10-22 20:22 ` [PATCH v3 2/4] hw/riscv/virt: only describe PLIC contexts that exist Ralf Ramsauer
@ 2026-10-22 20:22 ` Ralf Ramsauer
2026-10-23 3:06 ` Chao Liu
2026-10-22 20:22 ` [PATCH v3 4/4] hw/riscv: share the PLIC interrupt cells between boards Ralf Ramsauer
3 siblings, 1 reply; 8+ messages in thread
From: Ralf Ramsauer @ 2026-10-22 20:22 UTC (permalink / raw)
To: qemu-devel, Daniel Henrique Barboza
Cc: Ralf Ramsauer, Ralf Ramsauer, qemu-riscv, Palmer Dabbelt,
Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu, Bin Meng,
Lukas Landgraf
From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
The PLIC device is created from riscv_plic_hart_config_string(), which
gives a hart a supervisor context only if that hart has supervisor mode.
The device tree instead hardcodes the fu540 topology: no supervisor
context for the management hart, one for every other hart.
Both agree as long as the application harts keep the default CPU type.
The board sets no valid_cpu_types, though, and passes machine->cpu_type
on to the U cluster, so -cpu rv64,s=false,u=false builds a PLIC without
any supervisor context while the device tree still advertises one per
application hart. Contexts are numbered by their position in
'interrupts-extended', so from the second hart on the numbering drifts
apart, as it does on the virt machine.
Ask the harts instead of assuming, like create_fdt_socket_plic() does.
No change for the default CPU type: the E51 management hart has no RVS,
the U54 application harts have.
Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
---
hw/riscv/sifive_u.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index b7d5e7b4da..8f50c67a14 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -99,6 +99,7 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
MachineState *ms = MACHINE(s);
void *fdt;
int cpu;
+ RISCVCPU *hart;
uint32_t *cells, cells_length;
char *nodename;
uint32_t plic_phandle, prci_phandle, gpio_phandle, phandle = 1;
@@ -195,18 +196,18 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
g_free(nodename);
plic_phandle = phandle++;
- cells_length = ms->smp.cpus * 4 - 2;
- cells = g_new0(uint32_t, cells_length);
+ cells = g_new0(uint32_t, ms->smp.cpus * 4 - 2);
+ cells_length = 0;
for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
/* cpu 0 is the management hart that does not have S-mode */
- if (cpu == 0) {
- cells[0] = cpu_to_be32(intc_phandles[cpu]);
- cells[1] = cpu_to_be32(IRQ_M_EXT);
- } else {
- cells[cpu * 4 - 2] = cpu_to_be32(intc_phandles[cpu]);
- cells[cpu * 4 - 1] = cpu_to_be32(IRQ_M_EXT);
- cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
- cells[cpu * 4 + 1] = cpu_to_be32(IRQ_S_EXT);
+ hart = cpu ? &s->soc.u_cpus.harts[cpu - 1] : &s->soc.e_cpus.harts[0];
+
+ cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
+ cells[cells_length++] = cpu_to_be32(IRQ_M_EXT);
+
+ if (riscv_cpu_has_supervisor_mode(hart)) {
+ cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
+ cells[cells_length++] = cpu_to_be32(IRQ_S_EXT);
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v3 3/4] hw/riscv/sifive_u: derive PLIC contexts from the harts
2026-10-22 20:22 ` [PATCH v3 3/4] hw/riscv/sifive_u: derive PLIC contexts from the harts Ralf Ramsauer
@ 2026-10-23 3:06 ` Chao Liu
0 siblings, 0 replies; 8+ messages in thread
From: Chao Liu @ 2026-10-23 3:06 UTC (permalink / raw)
To: Ralf Ramsauer
Cc: qemu-devel, Daniel Henrique Barboza, Ralf Ramsauer, qemu-riscv,
Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei, Bin Meng,
Lukas Landgraf
On Thu, Oct 22, 2026 at 10:22:17PM +0800, Ralf Ramsauer wrote:
> From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
>
> The PLIC device is created from riscv_plic_hart_config_string(), which
> gives a hart a supervisor context only if that hart has supervisor mode.
> The device tree instead hardcodes the fu540 topology: no supervisor
> context for the management hart, one for every other hart.
>
> Both agree as long as the application harts keep the default CPU type.
> The board sets no valid_cpu_types, though, and passes machine->cpu_type
> on to the U cluster, so -cpu rv64,s=false,u=false builds a PLIC without
> any supervisor context while the device tree still advertises one per
> application hart. Contexts are numbered by their position in
> 'interrupts-extended', so from the second hart on the numbering drifts
> apart, as it does on the virt machine.
>
> Ask the harts instead of assuming, like create_fdt_socket_plic() does.
> No change for the default CPU type: the E51 management hart has no RVS,
> the U54 application harts have.
>
> Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Reviewed-by: Chao Liu <chao.liu@processmission.com>
Thanks,
Chao
> ---
> hw/riscv/sifive_u.c | 21 +++++++++++----------
> 1 file changed, 11 insertions(+), 10 deletions(-)
>
> diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
> index b7d5e7b4da..8f50c67a14 100644
> --- a/hw/riscv/sifive_u.c
> +++ b/hw/riscv/sifive_u.c
> @@ -99,6 +99,7 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
> MachineState *ms = MACHINE(s);
> void *fdt;
> int cpu;
> + RISCVCPU *hart;
> uint32_t *cells, cells_length;
> char *nodename;
> uint32_t plic_phandle, prci_phandle, gpio_phandle, phandle = 1;
> @@ -195,18 +196,18 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
> g_free(nodename);
>
> plic_phandle = phandle++;
> - cells_length = ms->smp.cpus * 4 - 2;
> - cells = g_new0(uint32_t, cells_length);
> + cells = g_new0(uint32_t, ms->smp.cpus * 4 - 2);
> + cells_length = 0;
> for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
> /* cpu 0 is the management hart that does not have S-mode */
> - if (cpu == 0) {
> - cells[0] = cpu_to_be32(intc_phandles[cpu]);
> - cells[1] = cpu_to_be32(IRQ_M_EXT);
> - } else {
> - cells[cpu * 4 - 2] = cpu_to_be32(intc_phandles[cpu]);
> - cells[cpu * 4 - 1] = cpu_to_be32(IRQ_M_EXT);
> - cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
> - cells[cpu * 4 + 1] = cpu_to_be32(IRQ_S_EXT);
> + hart = cpu ? &s->soc.u_cpus.harts[cpu - 1] : &s->soc.e_cpus.harts[0];
> +
> + cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
> + cells[cells_length++] = cpu_to_be32(IRQ_M_EXT);
> +
> + if (riscv_cpu_has_supervisor_mode(hart)) {
> + cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
> + cells[cells_length++] = cpu_to_be32(IRQ_S_EXT);
> }
> }
>
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 4/4] hw/riscv: share the PLIC interrupt cells between boards
2026-10-22 20:22 [PATCH v3 0/4] hw/riscv: describe only the PLIC contexts that exist Ralf Ramsauer
` (2 preceding siblings ...)
2026-10-22 20:22 ` [PATCH v3 3/4] hw/riscv/sifive_u: derive PLIC contexts from the harts Ralf Ramsauer
@ 2026-10-22 20:22 ` Ralf Ramsauer
3 siblings, 0 replies; 8+ messages in thread
From: Ralf Ramsauer @ 2026-10-22 20:22 UTC (permalink / raw)
To: qemu-devel, Daniel Henrique Barboza
Cc: Ralf Ramsauer, Ralf Ramsauer, qemu-riscv, Palmer Dabbelt,
Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu, Bin Meng,
Lukas Landgraf
From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
virt and sifive_u now describe a hart's PLIC contexts with the same rule:
a machine context unless the hart runs under KVM, where there is no
M-mode, and a supervisor context if the hart has supervisor mode.
Writing that rule down twice is what let the virt device tree drift away
from riscv_plic_hart_config_string().
This makes the sifive_u device tree ask whether KVM is in use, which it
did not do before. Its PLIC device already does, so the two now agree.
Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
---
Cleanup, not a fix. Can be dropped, the rest does not depend on it.
hw/riscv/fdt-common.c | 18 ++++++++++++++++++
hw/riscv/sifive_u.c | 12 ++++--------
hw/riscv/virt.c | 16 ++++++----------
include/hw/riscv/fdt-common.h | 3 +++
4 files changed, 31 insertions(+), 18 deletions(-)
diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c
index ce2dcb43b4..503d818dd0 100644
--- a/hw/riscv/fdt-common.c
+++ b/hw/riscv/fdt-common.c
@@ -212,6 +212,24 @@ riscv_create_fdt_socket_cpu_sifive(void *fdt, char *clust_name,
phandle, intc_phandles, false, false);
}
+uint32_t riscv_plic_hart_fdt_cells(uint32_t *cells, RISCVCPU *hart,
+ uint32_t intc_phandle, bool has_m_mode)
+{
+ uint32_t len = 0;
+
+ if (has_m_mode) {
+ cells[len++] = cpu_to_be32(intc_phandle);
+ cells[len++] = cpu_to_be32(IRQ_M_EXT);
+ }
+
+ if (riscv_cpu_has_supervisor_mode(hart)) {
+ cells[len++] = cpu_to_be32(intc_phandle);
+ cells[len++] = cpu_to_be32(IRQ_S_EXT);
+ }
+
+ return len;
+}
+
void riscv_create_fdt_plic(void *fdt, hwaddr addr, uint64_t size,
uint32_t plic_phandle, uint32_t int_cells,
uint32_t addr_cells, uint32_t *plic_cells,
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 8f50c67a14..be194f8aec 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -58,6 +58,7 @@
#include "chardev/char.h"
#include "net/eth.h"
#include "system/device_tree.h"
+#include "system/kvm.h"
#include "system/runstate.h"
#include "system/system.h"
@@ -201,14 +202,9 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
/* cpu 0 is the management hart that does not have S-mode */
hart = cpu ? &s->soc.u_cpus.harts[cpu - 1] : &s->soc.e_cpus.harts[0];
-
- cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
- cells[cells_length++] = cpu_to_be32(IRQ_M_EXT);
-
- if (riscv_cpu_has_supervisor_mode(hart)) {
- cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
- cells[cells_length++] = cpu_to_be32(IRQ_S_EXT);
- }
+ cells_length += riscv_plic_hart_fdt_cells(&cells[cells_length], hart,
+ intc_phandles[cpu],
+ !kvm_enabled());
}
riscv_create_fdt_plic(fdt, memmap[SIFIVE_U_DEV_PLIC].base,
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index a2851b506d..ece8ef0070 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -152,24 +152,20 @@ static void create_fdt_socket_plic(RISCVVirtState *s,
unsigned long plic_addr = s->memmap[VIRT_PLIC].base +
(s->memmap[VIRT_PLIC].size * socket);
bool numa_enabled = riscv_numa_enabled(MACHINE(s));
+ bool has_m_mode = !kvm_enabled();
uint32_t cells_length;
plic_name = g_strdup_printf("/soc/interrupt-controller@%lx", plic_addr);
plic_cells = g_new0(uint32_t,
- s->soc[socket].num_harts * (kvm_enabled() ? 2 : 4));
+ s->soc[socket].num_harts * (has_m_mode ? 4 : 2));
cells_length = 0;
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
- if (!kvm_enabled()) {
- plic_cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
- plic_cells[cells_length++] = cpu_to_be32(IRQ_M_EXT);
- }
-
- if (riscv_cpu_has_supervisor_mode(&s->soc[socket].harts[cpu])) {
- plic_cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
- plic_cells[cells_length++] = cpu_to_be32(IRQ_S_EXT);
- }
+ cells_length += riscv_plic_hart_fdt_cells(&plic_cells[cells_length],
+ &s->soc[socket].harts[cpu],
+ intc_phandles[cpu],
+ has_m_mode);
}
plic_phandles[socket] = (*phandle)++;
diff --git a/include/hw/riscv/fdt-common.h b/include/hw/riscv/fdt-common.h
index eb9db8278d..58e635e9ed 100644
--- a/include/hw/riscv/fdt-common.h
+++ b/include/hw/riscv/fdt-common.h
@@ -93,6 +93,9 @@ void riscv_create_fdt_socket_cpu_sifive(void *fdt, char *clust_name,
int socket_hartid_base,
uint32_t *phandle,
uint32_t *intc_phandles);
+/* Describe one hart's PLIC contexts, returns the number of cells written */
+uint32_t riscv_plic_hart_fdt_cells(uint32_t *cells, RISCVCPU *hart,
+ uint32_t intc_phandle, bool has_m_mode);
void riscv_create_fdt_plic(void *fdt, hwaddr addr, uint64_t size,
uint32_t plic_phandle, uint32_t int_cells,
uint32_t addr_cells, uint32_t *plic_cells,
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread