Skip to content

Commit 54ffc0d

Browse files
authored
Gdb support for mshv guests (#327)
* dbg: change visibility of debugging related functions/structs Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: move kvm vcpu debug type to another file - this is done in preparation of accomodating support for other hypervisors which means some of the functionality whould be common. - moving the debug code to a separate file can enable us to group common behavior in traits Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: move hardware breakpoints functionality to separate file - also add a trait that contains all the needed methods for interacting with a vCPU Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: move read/write registers and get_stop_reason to vCPU functionality trait Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: modify hw breakpoints and move common functionality in trait Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: move read/write address and add/remove sw breakpoint to separate file - add trait to define common behavior to be used by other hypervisors later Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: remove specific method that adds entry point breakpoint - it can be done using the generic way to add hw breakpoints - also remove the entry point breakpoint after the vCPU stops to avoid hanging there Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: improve vCPU exit reason checking - verify the debug registers that report what kind of exception was triggered - in case there is an unknown reason, report it as a SwBp so that the gdb client can inspect what happened Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: prepare for mshv guest debugging implementation Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: add basic skeleton for mshv guest debug Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: add mshv intercepts for #DB and #BP exceptions Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: mshv guest debugging support for vCPU operations Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: mshv guest debugging support for memory inspection Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: read/write addresses check offset calculation - use checked_sub to avoid issues from addresses that could cause underflow Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: enable ci guest debugging tests on mshv - change gdb commands in test to be usable with older gdb versions - change Justfile to provide mshv3 feature - change gdb test to invoke cargo test with correct features for mshv Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: update documentation to specify the mshv debug support Signed-off-by: Doru Blânzeanu <[email protected]> * dbg: add specific logging for each debug command error encountered Signed-off-by: Doru Blânzeanu <[email protected]> --------- Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent 1ffacdf commit 54ffc0d

14 files changed

+1320
-520
lines changed

.github/workflows/dep_rust.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,11 @@ jobs:
124124
run: just run-rust-examples-linux ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}}
125125

126126
- name: Run Rust Gdb tests - linux
127-
if: runner.os == 'Linux' && matrix.hypervisor == 'kvm'
127+
if: runner.os == 'Linux'
128128
env:
129129
CARGO_TERM_COLOR: always
130130
RUST_LOG: debug
131-
run: just test-rust-gdb-debugging ${{ matrix.config }}
131+
run: just test-rust-gdb-debugging ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}}
132132

133133
### Benchmarks ###
134134
- name: Install github-cli (Linux mariner)

Justfile

+3-3
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,9 @@ test-rust-feature-compilation-fail target=default-target:
117117
{{ if os() == "linux" { "! cargo check -p hyperlight-host --no-default-features 2> /dev/null"} else { "" } }}
118118

119119
# Test rust gdb debugging
120-
test-rust-gdb-debugging target=default-target: (build-rust target)
121-
{{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --example guest-debugging --features gdb
122-
{{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --features gdb -- test_gdb
120+
test-rust-gdb-debugging target=default-target features="": (build-rust target)
121+
{{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --example guest-debugging {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }}
122+
{{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} -- test_gdb
123123

124124
test target=default-target: (test-rust target)
125125

docs/debugging-hyperlight.md

+4
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,7 @@ cargo test --package hyperlight-host --test integration_test --features print_de
4242
To dump the details of the memory configuration, the virtual processors register state and the contents of the VM memory set the feature `crashdump` and run a debug build. This will result in a dump file being created in the temporary directory. The name and location of the dump file will be printed to the console and logged as an error message.
4343

4444
There are no tools at this time to analyze the dump file, but it can be useful for debugging.
45+
46+
## Debugging guests
47+
48+
For more information on how to debug the Hyperlight guests check the following [link](./how-to-debug-a-hyperlight-guest.md).

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
# How to debug a Hyperlight **KVM** guest using gdb
1+
# How to debug a Hyperlight guest using gdb on Linux
22

3-
Hyperlight supports gdb debugging of a **KVM** guest running inside a Hyperlight sandbox.
4-
When Hyperlight is compiled with the `gdb` feature enabled, a Hyperlight KVM sandbox can be configured
3+
Hyperlight supports gdb debugging of a **KVM** or **MSHV** guest running inside a Hyperlight sandbox on Linux.
4+
When Hyperlight is compiled with the `gdb` feature enabled, a Hyperlight sandbox can be configured
55
to start listening for a gdb connection.
66

77
## Supported features
88

9-
The Hyperlight `gdb` feature enables **KVM** guest debugging:
10-
- an entry point breakpoint is automatically set for the guest to stop
9+
The Hyperlight `gdb` feature enables **KVM** and **MSHV** guest debugging to:
10+
- stop at an entry point breakpoint which is automatically set by Hyperlight
1111
- add and remove HW breakpoints (maximum 4 set breakpoints at a time)
1212
- add and remove SW breakpoints
1313
- read and write registers
@@ -18,7 +18,7 @@ The Hyperlight `gdb` feature enables **KVM** guest debugging:
1818
## Expected behavior
1919

2020
Below is a list describing some cases of expected behavior from a gdb debug
21-
session of a guest binary running inside a KVM Hyperlight sandbox.
21+
session of a guest binary running inside a Hyperlight sandbox on Linux.
2222

2323
- when the `gdb` feature is enabled and a SandboxConfiguration is provided a
2424
debug port, the created sandbox will wait for a gdb client to connect on the
@@ -154,7 +154,7 @@ is sent over the communication channel to the hypervisor handler for the sandbox
154154
to resolve.
155155

156156
Below is a sequence diagram that shows the interaction between the entities
157-
involved in the gdb debugging of a Hyperlight guest running inside a KVM sandbox.
157+
involved in the gdb debugging of a Hyperlight guest running inside a **KVM** or **MSHV** sandbox.
158158

159159
```
160160
┌───────────────────────────────────────────────────────────────────────────────────────────────┐

src/hyperlight_host/build.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ fn main() -> Result<()> {
8989
// Essentially the kvm and mshv features are ignored on windows as long as you use #[cfg(kvm)] and not #[cfg(feature = "kvm")].
9090
// You should never use #[cfg(feature = "kvm")] or #[cfg(feature = "mshv")] in the codebase.
9191
cfg_aliases::cfg_aliases! {
92-
gdb: { all(feature = "gdb", debug_assertions, feature = "kvm", target_os = "linux") },
92+
gdb: { all(feature = "gdb", debug_assertions, any(feature = "kvm", feature = "mshv2", feature = "mshv3"), target_os = "linux") },
9393
kvm: { all(feature = "kvm", target_os = "linux") },
9494
mshv: { all(any(feature = "mshv2", feature = "mshv3"), target_os = "linux") },
9595
// inprocess feature is aliased with debug_assertions to make it only available in debug-builds.

src/hyperlight_host/examples/guest-debugging/main.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ mod tests {
107107
108108
set pagination off
109109
set logging file {out_file_path}
110-
set logging enabled on
110+
set logging on
111111
112112
break hyperlight_main
113113
commands
@@ -118,7 +118,7 @@ mod tests {
118118
119119
continue
120120
121-
set logging enabled off
121+
set logging off
122122
quit
123123
"
124124
)
@@ -134,12 +134,17 @@ mod tests {
134134
write_cmds_file(&cmd_file_path, &out_file_path)
135135
.expect("Failed to write gdb commands to file");
136136

137+
#[cfg(mshv3)]
138+
let features = "gdb,mshv3";
139+
#[cfg(not(mshv3))]
140+
let features = "gdb";
141+
137142
let mut guest_child = Command::new("cargo")
138143
.arg("run")
139144
.arg("--example")
140145
.arg("guest-debugging")
141146
.arg("--features")
142-
.arg("gdb")
147+
.arg(features)
143148
.stdin(Stdio::piped())
144149
.stdout(Stdio::piped())
145150
.spawn()

src/hyperlight_host/src/hypervisor/gdb/event_loop.rs

+8-10
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ limitations under the License.
1616

1717
use gdbstub::common::Signal;
1818
use gdbstub::conn::ConnectionExt;
19-
use gdbstub::stub::run_blocking::{self, WaitForStopReasonError};
20-
use gdbstub::stub::{BaseStopReason, DisconnectReason, GdbStub, SingleThreadStopReason};
19+
use gdbstub::stub::{
20+
run_blocking, BaseStopReason, DisconnectReason, GdbStub, SingleThreadStopReason,
21+
};
2122
use libc::{pthread_kill, SIGRTMIN};
2223

2324
use super::x86_64_target::HyperlightSandboxTarget;
2425
use super::{DebugResponse, GdbTargetError, VcpuStopReason};
2526

26-
pub struct GdbBlockingEventLoop;
27+
struct GdbBlockingEventLoop;
2728

2829
impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
2930
type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>;
@@ -57,13 +58,10 @@ impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
5758
signal: Signal(SIGRTMIN() as u8),
5859
},
5960
VcpuStopReason::Unknown => {
60-
log::warn!("Unknown stop reason - resuming execution");
61+
log::warn!("Unknown stop reason received");
6162

62-
target
63-
.resume_vcpu()
64-
.map_err(WaitForStopReasonError::Target)?;
65-
66-
continue;
63+
// Marking as a SwBreak so the gdb inspect where/why it stopped
64+
BaseStopReason::SwBreak(())
6765
}
6866
};
6967

@@ -115,7 +113,7 @@ impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
115113
}
116114
}
117115

118-
pub fn event_loop_thread(
116+
pub(crate) fn event_loop_thread(
119117
debugger: GdbStub<HyperlightSandboxTarget, Box<dyn ConnectionExt<Error = std::io::Error>>>,
120118
target: &mut HyperlightSandboxTarget,
121119
) {

0 commit comments

Comments
 (0)