From: Ralf Ramsauer <[email protected]>
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; sifive_u already omits the
supervisor entry for its management hart.
Emit the supervisor entry only for the harts that have supervisor mode.
Fixes: 7d10ff8a4d ("hw/riscv: virt: Use the PLIC config helper function")
Signed-off-by: Ralf Ramsauer <[email protected]>
---
hw/riscv/virt.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index d4b501802f..d0ce9dc862 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -165,14 +165,23 @@ static void create_fdt_socket_plic(RISCVVirtState *s,
plic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT);
}
} else {
- cells_length = s->soc[socket].num_harts * 4;
- plic_cells = g_new0(uint32_t, cells_length);
+ plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
+ cells_length = 0;
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);
+ CPURISCVState *env = &s->soc[socket].harts[cpu].env;
+
+ plic_cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
+ plic_cells[cells_length++] = cpu_to_be32(IRQ_M_EXT);
+
+ /*
+ * The PLIC implements a supervisor context only for harts that
+ * have supervisor mode, see riscv_plic_hart_config_string().
+ */
+ if (riscv_has_ext(env, RVS)) {
+ plic_cells[cells_length++] = cpu_to_be32(intc_phandles[cpu]);
+ plic_cells[cells_length++] = cpu_to_be32(IRQ_S_EXT);
+ }
}
}
base-commit: 81ce3a87737aa50716c42db8886082d12783e0a1
--
2.54.0