Skip to content

Kernel/aarch64+LibDebug: Add basic debugger functions support #25975

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 13, 2025

Conversation

NoobZang
Copy link
Contributor

@NoobZang NoobZang commented Jun 1, 2025

This patch adds the following basic debugger capabilities for AArch64:

  1. Breakpoint insertion (software breakpoints)
  2. Single-step execution
  3. Continue execution
截屏2025-06-01 10 26 34

@github-actions github-actions bot added the 👀 pr-needs-review PR needs review from a maintainer or community member label Jun 1, 2025
Copy link
Member

@spholz spholz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding basic aarch64 debugging support!

LibDebug still seems to peek/poke debug registers and expect x86 values to be there. But I guess it's fine for now. Just returning ENOTUSP in {peek,poke}_debug seems to be good enough to make this work. Although it's a bit hacky.

// to PSTATE.SS. To enable single stepping, the MDSCR_EL1.SS must also be set to 1.
constexpr u32 SS_FLAG = 0x200000;
regs.spsr_el1 |= SS_FLAG;
peer->debug_register_state().mdscr_el1 |= 0x1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We set mdscr_el1.ss to 1 after the first PT_SINGLESTEP and never set it to zero again.
Should we clear it somewhere again?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cleared it in exception_common(). Originally, I cleared current_thread->debug_register_state().mdscr_el1, but now we probably need to use clear_debug_registers() to clear the hardware register values directly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh clearing it in exception_common seems fine. Sorry I missed that as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you should directly write to the actual register, not our debug register state struct. That struct is only used to save/restore the debug register contents during context switches.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach also works: calling clear_debug_registers() in exception_common() to clear mdscr_el1.SS, and using ptrace to first read and then write DebugRegisterState to set mdscr_el1.SS.
But I’d like to ask why is this restriction necessary? I thought DebugRegisterState was similar to RegisterState, which can be modified inside the kernel and then applied to userspace.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DebugRegisterState is more like FPUState. FPUState is also only saved/restored on a context switch.
We should avoid saving/restoring registers if it isn't necessary and instead do it more lazily. We don't need FPU or debug registers in the kernel, so we don't need to save the userspace values on each trap entry.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, Sorry for missing something. When the debugger invokes PT_SINGLESTEP via a syscall, we write directly to MDSCR_EL1. After returning to userspace, if the CPU is no longer running the debuggee (which is very likely—it will be running the debugger itself), then you end up with MDSCR_EL1.SS = 1 while SPSR_EL1.SS = 0. In that situation, a single‐step exception fires immediately and kills the current process with SIGTRAP. Therefore, I think in this case we still need to use DebugRegisterState so that the CPU can set MDSCR_EL1.SS properly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's true. You need to set MDSCR_EL1.SS via accessing the debug register state of the debugee. Sorry for causing confusion.

@NoobZang NoobZang force-pushed the aarch64-sdb branch 2 times, most recently from 0b5cd11 to aca03be Compare June 9, 2025 15:21
@NoobZang NoobZang requested a review from spholz June 10, 2025 15:28
Copy link

stale bot commented Jul 7, 2025

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions!

@stale stale bot added the stale label Jul 7, 2025
@spholz spholz removed the stale label Jul 8, 2025
NoobZang added 7 commits July 13, 2025 22:29
`ptrace` need to be able to set the SS bit in the spsr_el1
register to control the software step exception.

When copying user-supplied PtraceRegisters' spsr_el1 into
the kernel’s RegisterState, only the ARMv8 condition flags
(N, Z, C, V) should be accepted. Other fields in spsr_el1
must remain under kernel control.
The least significant bit of mdscr_el1 (the SS bit) is used to
enable or disable software step.
Add a PT_SINGLESTEP request to the ptrace API and implement its
handing logic in the kernel.

Update the debugger to call ptrace(PT_SINGLESTEP) instead of directly
poking registers.

Ensure MDSCR_EL1.SS is properly cleared and then reloaded when a
software step occurs.
Copy link
Member

@spholz spholz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@spholz spholz added ✅ pr-maintainer-approved-but-awaiting-ci PR has been approved by a maintainer and can be merged after CI has passed and removed 👀 pr-needs-review PR needs review from a maintainer or community member labels Jul 13, 2025
@nico nico merged commit 2a417fc into SerenityOS:master Jul 13, 2025
22 of 23 checks passed
@github-actions github-actions bot removed the ✅ pr-maintainer-approved-but-awaiting-ci PR has been approved by a maintainer and can be merged after CI has passed label Jul 13, 2025
@NoobZang NoobZang deleted the aarch64-sdb branch July 14, 2025 00:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants