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


Reply via email to