Skip to content

Commit d38fe01

Browse files
cshungCopilot
andcommitted
feat(aarch64): add GDB debugging for Linux MSHV
MSHV forwards guest debug exceptions through intercepted HVCs. Register access uses the saved exception context and active stack pointer. Two hardware breakpoints and single-step survive replacement-VM reset. Native ARM64 GDB tests run in debug CI. Non-debug KVM use is available with default features. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: cshung <3410332+cshung@users.noreply.github.com>
1 parent 92dad03 commit d38fe01

8 files changed

Lines changed: 620 additions & 8 deletions

File tree

‎.github/workflows/dep_build_test.yml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,16 @@ jobs:
229229
RUST_LOG: debug
230230
run: just test-rust-gdb-debugging ${{ inputs.config }}
231231

232+
- name: Run ARM64 MSHV Gdb tests
233+
if: inputs.arch == 'arm64' && inputs.hypervisor == 'mshv3' && inputs.config == 'debug'
234+
env:
235+
RUST_LOG: debug
236+
run: |
237+
if ! command -v gdb > /dev/null 2>&1; then
238+
sudo tdnf install -y gdb
239+
fi
240+
just test-rust-gdb-debugging debug mshv3
241+
232242
- name: Run Rust Crashdump tests
233243
if: inputs.arch == 'X64'
234244
env:

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

77
### Added
88
* ARM64 Linux guests can run on MSHV.
9+
* ARM64 Linux/MSHV guest debugging supports software and hardware breakpoints, register and memory access, and hardware single-step.
910

1011
### Fixed
1112
* GDB memory reads and writes translate each guest page on hosts with larger pages.

‎Justfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ test-no-surrogate target=default-target:
335335
# runs tests that exercise gdb debugging
336336
test-rust-gdb-debugging target=default-target features="":
337337
{{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --example guest-debugging {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }}
338-
{{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} -- hypervisor::gdb:: hypervisor::hyperlight_vm::debug::tests:: gdb_recovery_tests::
338+
{{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} -- hypervisor::gdb:: hypervisor::hyperlight_vm::debug::tests:: gdb_recovery_tests:: hypervisor::virtual_machine::mshv::aarch64::
339339

340340
# rust test for crashdump
341341
test-rust-crashdump target=default-target features="":

‎docs/how-to-debug-a-hyperlight-guest.md‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,26 @@ to start listening for a gdb connection.
77
## Supported features
88

99
The Hyperlight `gdb` feature enables guest debugging to:
10-
- stop at an entry point breakpoint which is automatically set by Hyperlight
11-
- add and remove HW breakpoints (maximum 4 set breakpoints at a time)
10+
- stop at the entry point
1211
- add and remove SW breakpoints
1312
- read and write registers
1413
- read and write addresses
15-
- step/continue
14+
- continue
15+
- single-step
1616
- get code offset from target
1717
- stop when a crash occurs and only allow read access to the guest memory and registers
1818

19+
On x86_64, gdb supports up to four hardware breakpoints.
20+
ARM64 Linux/MSHV supports two hardware breakpoints, four-byte `BRK` software
21+
breakpoints, and hardware single-step. Guest exception vectors forward debug
22+
exceptions through HVC. Guest binaries must retain the
23+
`hyperlight_debug_vectors` symbol so the host can install these vectors before
24+
the initial debug stop. Watchpoints are not advertised.
25+
26+
ARM64 snapshots reject installed software breakpoints, including those added
27+
during checkpointing. Restoring a poisoned sandbox discards its software
28+
breakpoint state and restores clean snapshot memory.
29+
1930
## Expected behavior
2031

2132
Below is a list describing some cases of expected behavior from a gdb debug

‎src/hyperlight_host/build.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ fn main() -> Result<()> {
151151
// Essentially the kvm and mshv3 features are ignored on windows as long as you use #[cfg(kvm)] and not #[cfg(feature = "kvm")].
152152
// You should never use #[cfg(feature = "kvm")] or #[cfg(feature = "mshv3")] in the codebase.
153153
cfg_aliases::cfg_aliases! {
154-
gdb: { all(feature = "gdb", debug_assertions, target_arch = "x86_64") },
154+
gdb_platform: { any(target_arch = "x86_64", all(target_arch = "aarch64", target_os = "linux", feature = "mshv3")) },
155+
gdb: { all(feature = "gdb", debug_assertions, gdb_platform) },
155156
kvm: { all(feature = "kvm", target_os = "linux") },
156157
mshv3: { all(feature = "mshv3", target_os = "linux") },
157158
hvf: { all(feature = "hvf", target_os = "macos") },

‎src/hyperlight_host/src/hypervisor/virtual_machine/kvm/aarch64.rs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use kvm_bindings::{
1212
use kvm_ioctls::{Kvm, VcpuExit, VcpuFd, VmFd};
1313
use tracing::{Span, instrument};
1414

15+
#[cfg(gdb)]
16+
use crate::hypervisor::gdb::{DebugError, DebugMemoryView, DebuggableVm};
1517
use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
1618
use crate::hypervisor::virtual_machine::{
1719
CreateVmError, HypervisorError, MapMemoryError, RegisterError, ResetVcpuError, RunVcpuError,
@@ -21,6 +23,21 @@ use crate::hypervisor::virtual_machine::{
2123
static KVM: LazyLock<std::result::Result<Kvm, CreateVmError>> =
2224
LazyLock::new(|| Kvm::new().map_err(|e| CreateVmError::HypervisorNotAvailable(e.into())));
2325

26+
#[cfg(gdb)]
27+
impl DebuggableVm for KvmVm {
28+
fn translate_gva(&self, _gva: u64, _memory: &DebugMemoryView<'_>) -> Result<u64, DebugError> {
29+
Err(DebugError::Unsupported("ARM64 KVM debugging"))
30+
}
31+
32+
fn set_debug(&mut self, enable: bool) -> Result<(), DebugError> {
33+
if enable {
34+
Err(DebugError::Unsupported("ARM64 KVM debugging"))
35+
} else {
36+
Ok(())
37+
}
38+
}
39+
}
40+
2441
/// Return `true` if the KVM API is available
2542
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
2643
pub(crate) fn is_hypervisor_present() -> bool {

0 commit comments

Comments
 (0)