From: Mengchen Li <mengchenli64@gmail.com>
To: catalin.marinas@arm.com, will@kernel.org
Cc: linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org,
Mengchen Li <mengchenli64@gmail.com>
Subject: [PATCH] arm64: kgdb: Ensure atomic single-step execution
Date: Thu, 4 Sep 2025 15:47:23 +0800 [thread overview]
Message-ID: <1756972043-12854-1-git-send-email-mengchenli64@gmail.com> (raw)
The existing KGDB single-step handling on ARM64 is susceptible to
interference from external interrupts. If an interrupt arrives in the
narrow time window between the execution of the instruction under test
and the generation of the step exception, the CPU will vector to the
interrupt handler (e.g., el1_interrupt -> __el1_irq) instead of
triggering the debug exception immediately.
When the step exception is finally taken, the context is no longer that
of the original instruction. This causes the debugger to appear "stuck",
as it repeatedly tries to single-step through the interrupt handler's
code (e.g., irq_enter_rcu()) instead of the target code.
The fix is to make the single-step operation atomic by masking interrupts
for its duration:
1. Upon receiving a step ('s') request from GDB, save the current
PSTATE and then mask IRQs by setting the PSTATE.I bit.
2. After the single-step exception is taken, in kgdb_single_step_handler(),
disable the kernel's single-step mechanism and meticulously restore
the original interrupt mask state from the saved PSTATE.
This guarantees the instruction is executed without interruption and the
debug exception is taken in the correct context.
As a result of this new approach, the following cleanups are also made:
- The global `kgdb_single_step` flag is removed, as state is now precisely
managed by `kgdb_cpu_doing_single_step` and the interrupt mask.
- The logic to disable single-step and manage the flag in the 'c'ontinue
case is removed, as it is rendered redundant.
- The call to `kernel_rewind_single_step()` is unnecessary and is removed.
Tested on OrangePi 3B (RK3566) via serial console (kgdboc);
allows reliable single-stepping with GDB where it previously failed.
Signed-off-by: Mengchen Li <mengchenli64@gmail.com>
---
arch/arm64/kernel/kgdb.c | 49 ++++++++++++++++++++----------------------------
1 file changed, 20 insertions(+), 29 deletions(-)
diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c
index 968324a..ee8a7e3 100644
--- a/arch/arm64/kernel/kgdb.c
+++ b/arch/arm64/kernel/kgdb.c
@@ -101,6 +101,8 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
{ "fpcr", 4, -1 },
};
+static DEFINE_PER_CPU(unsigned int, kgdb_pstate);
+
char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
{
if (regno >= DBG_MAX_REG_NUM || regno < 0)
@@ -128,25 +130,15 @@ int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
void
sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
{
- struct cpu_context *cpu_context = &task->thread.cpu_context;
+ struct pt_regs *thread_regs;
/* Initialize to zero */
memset((char *)gdb_regs, 0, NUMREGBYTES);
- gdb_regs[19] = cpu_context->x19;
- gdb_regs[20] = cpu_context->x20;
- gdb_regs[21] = cpu_context->x21;
- gdb_regs[22] = cpu_context->x22;
- gdb_regs[23] = cpu_context->x23;
- gdb_regs[24] = cpu_context->x24;
- gdb_regs[25] = cpu_context->x25;
- gdb_regs[26] = cpu_context->x26;
- gdb_regs[27] = cpu_context->x27;
- gdb_regs[28] = cpu_context->x28;
- gdb_regs[29] = cpu_context->fp;
-
- gdb_regs[31] = cpu_context->sp;
- gdb_regs[32] = cpu_context->pc;
+ thread_regs = task_pt_regs(task);
+ memcpy((void *)gdb_regs, (void *)thread_regs->regs, GP_REG_BYTES);
+ /* Special case for PSTATE */
+ dbg_get_reg(33, gdb_regs + GP_REG_BYTES, thread_regs);
}
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
@@ -195,18 +187,12 @@ int kgdb_arch_handle_exception(int exception_vector, int signo,
* over and over again.
*/
kgdb_arch_update_addr(linux_regs, remcom_in_buffer);
- atomic_set(&kgdb_cpu_doing_single_step, -1);
- kgdb_single_step = 0;
-
- /*
- * Received continue command, disable single step
- */
- if (kernel_active_single_step())
- kernel_disable_single_step();
-
err = 0;
break;
case 's':
+ /* mask interrupts while single stepping */
+ __this_cpu_write(kgdb_pstate, linux_regs->pstate);
+ linux_regs->pstate |= (1 << 7);
/*
* Update step address value with address passed
* with step packet.
@@ -217,15 +203,12 @@ int kgdb_arch_handle_exception(int exception_vector, int signo,
*/
kgdb_arch_update_addr(linux_regs, remcom_in_buffer);
atomic_set(&kgdb_cpu_doing_single_step, raw_smp_processor_id());
- kgdb_single_step = 1;
/*
* Enable single step handling
*/
if (!kernel_active_single_step())
kernel_enable_single_step(linux_regs);
- else
- kernel_rewind_single_step(linux_regs);
err = 0;
break;
default:
@@ -252,9 +235,17 @@ NOKPROBE_SYMBOL(kgdb_compiled_brk_handler);
int kgdb_single_step_handler(struct pt_regs *regs, unsigned long esr)
{
- if (!kgdb_single_step)
- return DBG_HOOK_ERROR;
+ unsigned int pstate;
+
+ kernel_disable_single_step();
+ atomic_set(&kgdb_cpu_doing_single_step, -1);
+ /* restore interrupt mask status */
+ pstate = __this_cpu_read(kgdb_pstate);
+ if (pstate & (1 << 7))
+ regs->pstate |= (1 << 7);
+ else
+ regs->pstate &= ~(1 << 7);
kgdb_handle_exception(0, SIGTRAP, 0, regs);
return DBG_HOOK_HANDLED;
}
--
2.7.4
next reply other threads:[~2025-09-04 8:11 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-04 7:47 Mengchen Li [this message]
2025-09-22 11:08 ` [PATCH] arm64: kgdb: Ensure atomic single-step execution Will Deacon
2025-09-22 11:08 ` Will Deacon
2025-09-24 12:21 ` Mark Rutland
[not found] ` <CAEkTZs2x4RzUbYWXO=B1ZGtxycbryNT4YbROH2k_k+0L_B6Erg@mail.gmail.com>
2025-10-08 8:43 ` Will Deacon
2025-10-08 11:19 ` Mark Rutland
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1756972043-12854-1-git-send-email-mengchenli64@gmail.com \
--to=mengchenli64@gmail.com \
--cc=catalin.marinas@arm.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.