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.

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: 7d10ff8a4d ("hw/riscv: virt: Use the PLIC config helper function")
Signed-off-by: Ralf Ramsauer <[email protected]>
---
v2:
 - ask riscv_cpu_has_supervisor_mode() instead of reading RVS out of the
   hart's CPURISCVState (Daniel)
 - fold the KVM branch into the common loop, it describes the same rule
 - drop the claim about sifive_u from the commit message, patch 3 fixes
   that board rather than leaving it as the good example

 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


Reply via email to