target/riscv: throw debug exception before page fault

In the RISC-V privileged ISA section 3.1.15 table 15, it is determined
that a debug exception that is triggered from a load/store has a higher
priority than a possible fault that this access might trigger.

This is not the case ATM as shown in [1]. Adding a breakpoint in an
address that deliberately will fault is causing a load page fault
instead of a debug exception. The reason is that we're throwing in the
page fault as soon as the fault occurs (end of riscv_cpu_tlb_fill(),
raise_mmu_exception()), not allowing the installed watchpoints to
trigger.

Call cpu_check_watchpoint() in the page fault path to search and execute
any watchpoints that might exist for the address, never returning back
to the fault path. If no watchpoints are found cpu_check_watchpoint()
will return and we'll fall-through the regular path to
raise_mmu_exception().

[1] https://gitlab.com/qemu-project/qemu/-/issues/2627

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2627
Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-ID: <20250121170626.1992570-3-dbarboza@ventanamicro.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Daniel Henrique Barboza 2025-01-21 14:06:26 -03:00 committed by Alistair Francis
parent 3fba76e61c
commit c86edc5476

View File

@ -27,6 +27,7 @@
#include "exec/page-protection.h" #include "exec/page-protection.h"
#include "instmap.h" #include "instmap.h"
#include "tcg/tcg-op.h" #include "tcg/tcg-op.h"
#include "hw/core/tcg-cpu-ops.h"
#include "trace.h" #include "trace.h"
#include "semihosting/common-semi.h" #include "semihosting/common-semi.h"
#include "system/cpu-timers.h" #include "system/cpu-timers.h"
@ -1697,6 +1698,23 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
} else if (probe) { } else if (probe) {
return false; return false;
} else { } else {
int wp_access = 0;
if (access_type == MMU_DATA_LOAD) {
wp_access |= BP_MEM_READ;
} else if (access_type == MMU_DATA_STORE) {
wp_access |= BP_MEM_WRITE;
}
/*
* If a watchpoint isn't found for 'addr' this will
* be a no-op and we'll resume the mmu_exception path.
* Otherwise we'll throw a debug exception and execution
* will continue elsewhere.
*/
cpu_check_watchpoint(cs, address, size, MEMTXATTRS_UNSPECIFIED,
wp_access, retaddr);
raise_mmu_exception(env, address, access_type, pmp_violation, raise_mmu_exception(env, address, access_type, pmp_violation,
first_stage_error, two_stage_lookup, first_stage_error, two_stage_lookup,
two_stage_indirect_error); two_stage_indirect_error);