2023-02-02 07:32:10

by Sumit Garg

[permalink] [raw]
Subject: [PATCH v6 0/2] arm64: Fix pending single-step debugging issues

This patch-set reworks pending fixes from Wei's series [1] to make
single-step debugging via kgdb/kdb on arm64 work as expected. There was
a prior discussion on ML [2] regarding if we should keep the interrupts
enabled during single-stepping. So patch #1 follows suggestion from Will
[3] to not disable interrupts during single stepping but rather skip
single stepping within interrupt handler.

[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/CAD=FV=Voyfq3Qz0T3RY+aYWYJ0utdH=P_AweB=13rcV8GDBeyQ@mail.gmail.com/
[3] https://lore.kernel.org/all/20200626095551.GA9312@willie-the-truck/

Changes in v6:
- Fix incorrect rescheduling check introduced by rework for v5.
- Patch #2: s/kernel_regs_reset_single_step/kernel_rewind_single_step/
- Collected Daniel's tags.

Changes in v5:
- Incorporated misc. comments from Mark.

Changes in v4:
- Rebased to the tip of mainline.
- Picked up Doug's Tested-by tag.

Changes in v3:
- Reword commit descriptions as per Daniel's suggestions.

Changes in v2:
- Replace patch #1 to rather follow Will's suggestion.

Sumit Garg (2):
arm64: entry: Skip single stepping into interrupt handlers
arm64: kgdb: Set PSTATE.SS to 1 to re-enable single-step

arch/arm64/include/asm/debug-monitors.h | 1 +
arch/arm64/kernel/debug-monitors.c | 5 +++++
arch/arm64/kernel/entry-common.c | 22 ++++++++++++++++++++--
arch/arm64/kernel/kgdb.c | 2 ++
4 files changed, 28 insertions(+), 2 deletions(-)

--
2.34.1



2023-02-02 07:32:14

by Sumit Garg

[permalink] [raw]
Subject: [PATCH v6 1/2] arm64: entry: Skip single stepping into interrupt handlers

Currently on systems where the timer interrupt (or any other
fast-at-human-scale periodic interrupt) is active then it is impossible
to step any code with interrupts unlocked because we will always end up
stepping into the timer interrupt instead of stepping the user code.

The common user's goal while single stepping is that when they step then
the system will stop at PC+4 or PC+I for a branch that gets taken
relative to the instruction they are stepping. So, fix broken single step
implementation via skipping single stepping into interrupt handlers.

The methodology is when we receive an interrupt from EL1, check if we
are single stepping (pstate.SS). If yes then we save MDSCR_EL1.SS and
clear the register bit if it was set. Then unmask only D and leave I set.
On return from the interrupt, set D and restore MDSCR_EL1.SS. Along with
this skip reschedule if we were stepping.

Suggested-by: Will Deacon <[email protected]>
Signed-off-by: Sumit Garg <[email protected]>
Tested-by: Douglas Anderson <[email protected]>
Acked-by: Daniel Thompson <[email protected]>
Tested-by: Daniel Thompson <[email protected]>
---
arch/arm64/kernel/entry-common.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
index cce1167199e3..568481f66977 100644
--- a/arch/arm64/kernel/entry-common.c
+++ b/arch/arm64/kernel/entry-common.c
@@ -231,11 +231,15 @@ DEFINE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched);
#define need_irq_preemption() (IS_ENABLED(CONFIG_PREEMPTION))
#endif

-static void __sched arm64_preempt_schedule_irq(void)
+static void __sched arm64_preempt_schedule_irq(struct pt_regs *regs)
{
if (!need_irq_preemption())
return;

+ /* Don't reschedule in case we are single stepping */
+ if (regs->pstate & DBG_SPSR_SS)
+ return;
+
/*
* Note: thread_info::preempt_count includes both thread_info::count
* and thread_info::need_resched, and is not equivalent to
@@ -471,19 +475,33 @@ static __always_inline void __el1_irq(struct pt_regs *regs,
do_interrupt_handler(regs, handler);
irq_exit_rcu();

- arm64_preempt_schedule_irq();
+ arm64_preempt_schedule_irq(regs);

exit_to_kernel_mode(regs);
}
+
static void noinstr el1_interrupt(struct pt_regs *regs,
void (*handler)(struct pt_regs *))
{
+ unsigned long mdscr;
+
+ /* Disable single stepping within interrupt handler */
+ if (regs->pstate & DBG_SPSR_SS) {
+ mdscr = read_sysreg(mdscr_el1);
+ write_sysreg(mdscr & ~DBG_MDSCR_SS, mdscr_el1);
+ }
+
write_sysreg(DAIF_PROCCTX_NOIRQ, daif);

if (IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && !interrupts_enabled(regs))
__el1_pnmi(regs, handler);
else
__el1_irq(regs, handler);
+
+ if (regs->pstate & DBG_SPSR_SS) {
+ write_sysreg(DAIF_PROCCTX_NOIRQ | PSR_D_BIT, daif);
+ write_sysreg(mdscr, mdscr_el1);
+ }
}

asmlinkage void noinstr el1h_64_irq_handler(struct pt_regs *regs)
--
2.34.1


2023-02-02 07:32:20

by Sumit Garg

[permalink] [raw]
Subject: [PATCH v6 2/2] arm64: kgdb: Set PSTATE.SS to 1 to re-enable single-step

Currently only the first attempt to single-step has any effect. After
that all further stepping remains "stuck" at the same program counter
value.

Refer to the ARM Architecture Reference Manual (ARM DDI 0487E.a) D2.12,
PSTATE.SS=1 should be set at each step before transferring the PE to the
'Active-not-pending' state. The problem here is PSTATE.SS=1 is not set
since the second single-step.

After the first single-step, the PE transferes to the 'Inactive' state,
with PSTATE.SS=0 and MDSCR.SS=1, thus PSTATE.SS won't be set to 1 due to
kernel_active_single_step()=true. Then the PE transferes to the
'Active-pending' state when ERET and returns to the debugger by step
exception.

Before this patch:
==================
Entering kdb (current=0xffff3376039f0000, pid 1) on processor 0 due to Keyboard Entry
[0]kdb>

[0]kdb>
[0]kdb> bp write_sysrq_trigger
Instruction(i) BP #0 at 0xffffa45c13d09290 (write_sysrq_trigger)
is enabled addr at ffffa45c13d09290, hardtype=0 installed=0

[0]kdb> go
$ echo h > /proc/sysrq-trigger

Entering kdb (current=0xffff4f7e453f8000, pid 175) on processor 1 due to Breakpoint @ 0xffffad651a309290
[1]kdb> ss

Entering kdb (current=0xffff4f7e453f8000, pid 175) on processor 1 due to SS trap @ 0xffffad651a309294
[1]kdb> ss

Entering kdb (current=0xffff4f7e453f8000, pid 175) on processor 1 due to SS trap @ 0xffffad651a309294
[1]kdb>

After this patch:
=================
Entering kdb (current=0xffff6851c39f0000, pid 1) on processor 0 due to Keyboard Entry
[0]kdb> bp write_sysrq_trigger
Instruction(i) BP #0 at 0xffffc02d2dd09290 (write_sysrq_trigger)
is enabled addr at ffffc02d2dd09290, hardtype=0 installed=0

[0]kdb> go
$ echo h > /proc/sysrq-trigger

Entering kdb (current=0xffff6851c53c1840, pid 174) on processor 1 due to Breakpoint @ 0xffffc02d2dd09290
[1]kdb> ss

Entering kdb (current=0xffff6851c53c1840, pid 174) on processor 1 due to SS trap @ 0xffffc02d2dd09294
[1]kdb> ss

Entering kdb (current=0xffff6851c53c1840, pid 174) on processor 1 due to SS trap @ 0xffffc02d2dd09298
[1]kdb> ss

Entering kdb (current=0xffff6851c53c1840, pid 174) on processor 1 due to SS trap @ 0xffffc02d2dd0929c
[1]kdb>

Fixes: 44679a4f142b ("arm64: KGDB: Add step debugging support")
Co-developed-by: Wei Li <[email protected]>
Signed-off-by: Wei Li <[email protected]>
Signed-off-by: Sumit Garg <[email protected]>
Tested-by: Douglas Anderson <[email protected]>
Acked-by: Daniel Thompson <[email protected]>
Tested-by: Daniel Thompson <[email protected]>
---
arch/arm64/include/asm/debug-monitors.h | 1 +
arch/arm64/kernel/debug-monitors.c | 5 +++++
arch/arm64/kernel/kgdb.c | 2 ++
3 files changed, 8 insertions(+)

diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h
index 7b7e05c02691..13d437bcbf58 100644
--- a/arch/arm64/include/asm/debug-monitors.h
+++ b/arch/arm64/include/asm/debug-monitors.h
@@ -104,6 +104,7 @@ void user_regs_reset_single_step(struct user_pt_regs *regs,
void kernel_enable_single_step(struct pt_regs *regs);
void kernel_disable_single_step(void);
int kernel_active_single_step(void);
+void kernel_rewind_single_step(struct pt_regs *regs);

#ifdef CONFIG_HAVE_HW_BREAKPOINT
int reinstall_suspended_bps(struct pt_regs *regs);
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index 3da09778267e..64f2ecbdfe5c 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -438,6 +438,11 @@ int kernel_active_single_step(void)
}
NOKPROBE_SYMBOL(kernel_active_single_step);

+void kernel_rewind_single_step(struct pt_regs *regs)
+{
+ set_regs_spsr_ss(regs);
+}
+
/* ptrace API */
void user_enable_single_step(struct task_struct *task)
{
diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c
index cda9c1e9864f..4e1f983df3d1 100644
--- a/arch/arm64/kernel/kgdb.c
+++ b/arch/arm64/kernel/kgdb.c
@@ -224,6 +224,8 @@ int kgdb_arch_handle_exception(int exception_vector, int signo,
*/
if (!kernel_active_single_step())
kernel_enable_single_step(linux_regs);
+ else
+ kernel_rewind_single_step(linux_regs);
err = 0;
break;
default:
--
2.34.1


2023-02-22 07:07:15

by Sumit Garg

[permalink] [raw]
Subject: Re: [PATCH v6 0/2] arm64: Fix pending single-step debugging issues

Hi Will,

On Thu, 2 Feb 2023 at 13:02, Sumit Garg <[email protected]> wrote:
>
> This patch-set reworks pending fixes from Wei's series [1] to make
> single-step debugging via kgdb/kdb on arm64 work as expected. There was
> a prior discussion on ML [2] regarding if we should keep the interrupts
> enabled during single-stepping. So patch #1 follows suggestion from Will
> [3] to not disable interrupts during single stepping but rather skip
> single stepping within interrupt handler.
>
> [1] https://lore.kernel.org/all/[email protected]/
> [2] https://lore.kernel.org/all/CAD=FV=Voyfq3Qz0T3RY+aYWYJ0utdH=P_AweB=13rcV8GDBeyQ@mail.gmail.com/
> [3] https://lore.kernel.org/all/20200626095551.GA9312@willie-the-truck/
>
> Changes in v6:
> - Fix incorrect rescheduling check introduced by rework for v5.
> - Patch #2: s/kernel_regs_reset_single_step/kernel_rewind_single_step/
> - Collected Daniel's tags.
>

Would you be happy to pick up patch #2 independently? Also, it would
be great if you can provide some guidance regarding patch #1 which
certainly fixes single stepping on arm64 but I am not sure how it
would have side-effects.

-Sumit

> Changes in v5:
> - Incorporated misc. comments from Mark.
>
> Changes in v4:
> - Rebased to the tip of mainline.
> - Picked up Doug's Tested-by tag.
>
> Changes in v3:
> - Reword commit descriptions as per Daniel's suggestions.
>
> Changes in v2:
> - Replace patch #1 to rather follow Will's suggestion.
>
> Sumit Garg (2):
> arm64: entry: Skip single stepping into interrupt handlers
> arm64: kgdb: Set PSTATE.SS to 1 to re-enable single-step
>
> arch/arm64/include/asm/debug-monitors.h | 1 +
> arch/arm64/kernel/debug-monitors.c | 5 +++++
> arch/arm64/kernel/entry-common.c | 22 ++++++++++++++++++++--
> arch/arm64/kernel/kgdb.c | 2 ++
> 4 files changed, 28 insertions(+), 2 deletions(-)
>
> --
> 2.34.1
>

2023-04-14 14:48:41

by Will Deacon

[permalink] [raw]
Subject: Re: [PATCH v6 0/2] arm64: Fix pending single-step debugging issues

On Thu, 2 Feb 2023 13:01:46 +0530, Sumit Garg wrote:
> This patch-set reworks pending fixes from Wei's series [1] to make
> single-step debugging via kgdb/kdb on arm64 work as expected. There was
> a prior discussion on ML [2] regarding if we should keep the interrupts
> enabled during single-stepping. So patch #1 follows suggestion from Will
> [3] to not disable interrupts during single stepping but rather skip
> single stepping within interrupt handler.
>
> [...]

Applied second patch to arm64 (for-next/misc), thanks!

[2/2] arm64: kgdb: Set PSTATE.SS to 1 to re-enable single-step
https://git.kernel.org/arm64/c/af6c0bd59f4f

Cheers,
--
Will

https://fixes.arm64.dev
https://next.arm64.dev
https://will.arm64.dev