arm64: fix hardware single-stepping from EL1

The main issue is that debug exceptions must to be disabled for the
entire duration that SS bit in MDSCR_EL1 is set. Otherwise, a
single-step exception will be generated immediately. This can occur
before returning from the debugger (when MDSCR is written to) or before
re-entering it after the single-step (when debug exceptions are unmasked
in the exception handler).

Solve this by delaying the unmask to C code for EL1, and avoid unmasking
at all while handling debug exceptions, thus avoiding any recursive
debug traps.

Reviewed by:	markj, jhb
MFC after:	5 days
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D28944
This commit is contained in:
Mitchell Horne 2021-03-01 09:59:25 -04:00
parent 79fbd48378
commit 874635e381
3 changed files with 19 additions and 1 deletions

View File

@ -186,6 +186,9 @@ void
kdb_cpu_set_singlestep(void)
{
KASSERT((READ_SPECIALREG(daif) & PSR_D) == PSR_D,
("%s: debug exceptions are not masked", __func__));
kdb_frame->tf_spsr |= DBG_SPSR_SS;
WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) |
DBG_MDSCR_SS | DBG_MDSCR_KDE);
@ -205,6 +208,9 @@ void
kdb_cpu_clear_singlestep(void)
{
KASSERT((READ_SPECIALREG(daif) & PSR_D) == PSR_D,
("%s: debug exceptions are not masked", __func__));
WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) &
~(DBG_MDSCR_SS | DBG_MDSCR_KDE));

View File

@ -75,8 +75,12 @@ __FBSDID("$FreeBSD$");
ldr x0, [x18, #(PC_CURTHREAD)]
bl dbg_monitor_enter
.endif
msr daifclr, #8 /* Enable the debug exception */
.endif
/*
* For EL1, debug exceptions are conditionally unmasked in
* do_el1h_sync().
*/
.endm
.macro restore_registers el

View File

@ -377,6 +377,14 @@ do_el1h_sync(struct thread *td, struct trapframe *frame)
"do_el1_sync: curthread: %p, esr %lx, elr: %lx, frame: %p", td,
esr, frame->tf_elr, frame);
/*
* Enable debug exceptions if we aren't already handling one. They will
* be masked again in the exception handler's epilogue.
*/
if (exception != EXCP_BRK && exception != EXCP_WATCHPT_EL1 &&
exception != EXCP_SOFTSTP_EL1)
dbg_enable();
switch (exception) {
case EXCP_FP_SIMD:
case EXCP_TRAP_FP: