Skip to content

Commit be0272b

Browse files
committed
[ot] hw/riscv: dm: Handle haltreqs for unresponsive harts
This patch works around the Debug Module's current non-conformance with the RISC-V debug specification in how it handles halt requests (haltreqs) to unresponsive harts (e.g. harts currently in reset). Several parts of the RISC-V Debug Specification refer to this behaviour (see sections 3.2, 3.4, 3.5) but section C.1.3 is specifically a bug fix defined just for this behaviour. The current DM implementation just ignores incoming halt requests if the hart is unresponsive. This commit instead latches these requests in a bitmask, so that when the hart comes out of reset (i.e. starts executing instructions) it can be checked and used to immediately halt the hart. Unfortunately, a fully correct implementation of this behaviour would likely require a direct link between the RISC-V CPU and the DM to allow the hart to be halted at this point. Such links are not currently supported by QEMU. This commit introduces a reasonable workaround where we poll the availability of cores on a `dmstatus` read and halt newly responsive harts with latched haltreqs there, relying on the fact that most debuggers interacting with the DM will write the haltreq and then repeatedly poll `dmstatus` to watch the harts halt (see e.g. section B.3 of the RISC-V Debug specification). This is not 100% conformant (if dmstatus is not polled for a while then the hart will execute some instructions whereas in real HW it would halt immediately before executing guest code) but is a small workaround / hack that can support the majority of practical use cases reasonably enough. Signed-off-by: Alex Jones <[email protected]>
1 parent 1247809 commit be0272b

File tree

1 file changed

+26
-0
lines changed

1 file changed

+26
-0
lines changed

hw/riscv/dm.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ struct RISCVDMState {
334334
const char *soc; /* Subsystem name, for debug */
335335
uint64_t nonexistent_bm; /* Selected harts that are not existent */
336336
uint64_t unavailable_bm; /* Selected harts that are not available */
337+
uint64_t haltreq_bm; /* Selected harts that have a pending halt request */
337338
uint64_t to_go_bm; /* Harts that have been flagged for debug exec */
338339
uint32_t address; /* DM register addr: only bADDRESS_BITS..b0 are used */
339340
uint32_t *regs; /* Debug module register values */
@@ -1335,6 +1336,8 @@ static CmdErr riscv_dm_dmcontrol_write(RISCVDMState *dm, uint32_t value)
13351336
trace_riscv_dm_unavailable_hart_control(dm->soc, hartsel,
13361337
"halt");
13371338
ret = CMD_ERR_HALT_RESUME;
1339+
/* flag the haltreq as pending while unavailable */
1340+
dm->haltreq_bm |= hartbit;
13381341
} else {
13391342
riscv_dm_halt_hart(dm, hartsel);
13401343
}
@@ -1538,6 +1541,27 @@ static CmdErr riscv_dm_dmstatus_read(RISCVDMState *dm, uint32_t *value)
15381541
trace_riscv_dm_status(dm->soc, cs->cpu_index, "became available");
15391542
/* clear the unavailability flag and resume w/ "regular" states */
15401543
dm->unavailable_bm &= ~mask;
1544+
1545+
/*
1546+
* If a pending haltreq exists for this hart, enter debug mode.
1547+
*
1548+
* @todo: This is a workaround for the fact that the current
1549+
* implementation does not conform to the debug specification. See
1550+
* e.g. section C.1.4: "When a hart comes out of reset and haltreq
1551+
* is set, the hart will immediately enter Debug Mode". To support
1552+
* this the CPU would need to query the DM when it begins executing
1553+
* instructions, and hence would need a reference to the DM, which
1554+
* is not ideal.
1555+
*
1556+
* For now, we can support most real use cases by exploiting the
1557+
* fact that reasonable SW will usually send a haltreq and then
1558+
* repeatedly poll `dmstatus` to see the hart(s) halt. Hence, we
1559+
* latch haltreqs for unresponsive cores and halt any hart that is
1560+
* polled as newly available/responsive on a `dmstatus` read.
1561+
*/
1562+
if (dm->haltreq_bm & mask) {
1563+
riscv_dm_halt_hart(dm, hix);
1564+
}
15411565
}
15421566
if (hart->resumed) {
15431567
resumeack += 1;
@@ -2412,6 +2436,7 @@ static void riscv_dm_halt_hart(RISCVDMState *dm, unsigned hartsel)
24122436

24132437
/* Note: NMI are not yet supported */
24142438
cpu_exit(cs);
2439+
dm->haltreq_bm &= ~(1u << hartsel);
24152440
/* not sure if the real HW clear this flag on halt */
24162441
dm->hart->resumed = false;
24172442
riscv_dm_set_cs(dm, true);
@@ -2573,6 +2598,7 @@ static void riscv_dm_reset_enter(Object *obj, ResetType type)
25732598
/* Hart statuses are updated on reset_exit */
25742599
dm->nonexistent_bm = 0;
25752600
dm->unavailable_bm = 0;
2601+
dm->haltreq_bm = 0;
25762602
dm->address = 0;
25772603
dm->to_go_bm = 0;
25782604
for (unsigned ix = 0; ix < dm->hart_count; ix++) {

0 commit comments

Comments
 (0)