Skip to content

Commit 16b6691

Browse files
committed
feat: Enable gdb debugging on x86
Enabling GDB support for debugging the guest kernel. This allows us to connect a gdb server to firecracker and debug the guest. Signed-off-by: Jack Thomson <[email protected]>
1 parent bc0ba43 commit 16b6691

File tree

13 files changed

+1255
-10
lines changed

13 files changed

+1255
-10
lines changed

Cargo.lock

+39
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/firecracker/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ serde_json = "1.0.128"
4949

5050
[features]
5151
tracing = ["log-instrument", "seccompiler/tracing", "utils/tracing", "vmm/tracing"]
52+
gdb = ["vmm/gdb"]
5253

5354
[lints]
5455
workspace = true

src/vmm/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ bench = false
1111
[dependencies]
1212
acpi_tables = { path = "../acpi-tables" }
1313
aes-gcm = { version = "0.10.1", default-features = false, features = ["aes"] }
14+
arrayvec = { version = "0.7.6", optional = true }
1415
aws-lc-rs = { version = "1.9.0", features = ["bindgen"] }
1516
base64 = "0.22.1"
1617
bincode = "1.2.1"
@@ -19,6 +20,8 @@ crc64 = "2.0.0"
1920
derive_more = { version = "1.0.0", default-features = false, features = ["from", "display"] }
2021
displaydoc = "0.2.5"
2122
event-manager = "0.4.0"
23+
gdbstub = { version = "0.7.2", optional = true }
24+
gdbstub_arch = { version = "0.3.0", optional = true }
2225
kvm-bindings = { version = "0.9.1", features = ["fam-wrappers", "serde"] }
2326
kvm-ioctls = "0.18.0"
2427
lazy_static = "1.5.0"
@@ -56,7 +59,9 @@ itertools = "0.13.0"
5659
proptest = { version = "1.5.0", default-features = false, features = ["std"] }
5760

5861
[features]
62+
default = []
5963
tracing = ["log-instrument"]
64+
gdb = ["arrayvec", "gdbstub", "gdbstub_arch"]
6065

6166
[[bench]]
6267
name = "cpu_templates"

src/vmm/src/builder.rs

+46-10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use std::convert::TryFrom;
88
use std::fmt::Debug;
99
use std::io::{self, Seek, SeekFrom};
10+
#[cfg(feature = "gdb")]
11+
use std::sync::mpsc;
1012
use std::sync::{Arc, Mutex};
1113

1214
use event_manager::{MutEventSubscriber, SubscriberOps};
@@ -26,6 +28,9 @@ use vm_superio::Rtc;
2628
use vm_superio::Serial;
2729
use vmm_sys_util::eventfd::EventFd;
2830

31+
#[cfg(all(feature = "gdb", target_arch = "aarch64"))]
32+
compile_error!("GDB feature not supported on ARM");
33+
2934
#[cfg(target_arch = "x86_64")]
3035
use crate::acpi;
3136
use crate::arch::InitrdConfig;
@@ -56,6 +61,8 @@ use crate::devices::virtio::net::Net;
5661
use crate::devices::virtio::rng::Entropy;
5762
use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend};
5863
use crate::devices::BusDevice;
64+
#[cfg(feature = "gdb")]
65+
use crate::gdb;
5966
use crate::logger::{debug, error};
6067
use crate::persist::{MicrovmState, MicrovmStateError};
6168
use crate::resources::VmResources;
@@ -128,6 +135,12 @@ pub enum StartMicrovmError {
128135
/// Error configuring ACPI: {0}
129136
#[cfg(target_arch = "x86_64")]
130137
Acpi(#[from] crate::acpi::AcpiError),
138+
/// Error starting GDB debug session
139+
#[cfg(feature = "gdb")]
140+
GdbServer(gdb::target::GdbTargetError),
141+
/// Error cloning Vcpu fds
142+
#[cfg(feature = "gdb")]
143+
VcpuFdCloneError(#[from] crate::vstate::vcpu::CopyKvmFdError),
131144
}
132145

133146
/// It's convenient to automatically convert `linux_loader::cmdline::Error`s
@@ -274,6 +287,18 @@ pub fn build_microvm_for_boot(
274287
cpu_template.kvm_capabilities.clone(),
275288
)?;
276289

290+
#[cfg(feature = "gdb")]
291+
let (gdb_tx, gdb_rx) = mpsc::channel();
292+
#[cfg(feature = "gdb")]
293+
vcpus
294+
.iter_mut()
295+
.for_each(|vcpu| vcpu.attach_debug_info(gdb_tx.clone()));
296+
#[cfg(feature = "gdb")]
297+
let vcpu_fds = vcpus
298+
.iter()
299+
.map(|vcpu| vcpu.copy_kvm_vcpu_fd(vmm.vm()))
300+
.collect::<Result<Vec<_>, _>>()?;
301+
277302
// The boot timer device needs to be the first device attached in order
278303
// to maintain the same MMIO address referenced in the documentation
279304
// and tests.
@@ -321,16 +346,28 @@ pub fn build_microvm_for_boot(
321346
boot_cmdline,
322347
)?;
323348

349+
let vmm = Arc::new(Mutex::new(vmm));
350+
351+
#[cfg(feature = "gdb")]
352+
if let Some(gdb_socket_addr) = &vm_resources.gdb_socket_addr {
353+
gdb::gdb_thread(vmm.clone(), vcpu_fds, gdb_rx, entry_addr, gdb_socket_addr)
354+
.map_err(GdbServer)?;
355+
} else {
356+
debug!("No GDB socket provided not starting gdb server.");
357+
}
358+
324359
// Move vcpus to their own threads and start their state machine in the 'Paused' state.
325-
vmm.start_vcpus(
326-
vcpus,
327-
seccomp_filters
328-
.get("vcpu")
329-
.ok_or_else(|| MissingSeccompFilters("vcpu".to_string()))?
330-
.clone(),
331-
)
332-
.map_err(VmmError::VcpuStart)
333-
.map_err(Internal)?;
360+
vmm.lock()
361+
.unwrap()
362+
.start_vcpus(
363+
vcpus,
364+
seccomp_filters
365+
.get("vcpu")
366+
.ok_or_else(|| MissingSeccompFilters("vcpu".to_string()))?
367+
.clone(),
368+
)
369+
.map_err(VmmError::VcpuStart)
370+
.map_err(Internal)?;
334371

335372
// Load seccomp filters for the VMM thread.
336373
// Execution panics if filters cannot be loaded, use --no-seccomp if skipping filters
@@ -344,7 +381,6 @@ pub fn build_microvm_for_boot(
344381
.map_err(VmmError::SeccompFilters)
345382
.map_err(Internal)?;
346383

347-
let vmm = Arc::new(Mutex::new(vmm));
348384
event_manager.add_subscriber(vmm.clone());
349385

350386
Ok(vmm)

src/vmm/src/gdb/arch/aarch64.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use gdbstub_arch::aarch64::reg::AArch64CoreRegs as CoreRegs;
5+
use kvm_ioctls::VcpuFd;
6+
use vm_memory::GuestAddress;
7+
8+
use crate::gdb::target::GdbTargetError;
9+
10+
/// Configures the number of bytes required for a software breakpoint
11+
pub const SW_BP_SIZE: usize = 1;
12+
13+
/// The bytes stored for a software breakpoint
14+
pub const SW_BP: [u8; SW_BP_SIZE] = [0];
15+
16+
/// Gets the RIP value for a Vcpu
17+
pub fn get_instruction_pointer(_vcpu_fd: &VcpuFd) -> Result<u64, GdbTargetError> {
18+
unimplemented!()
19+
}
20+
21+
/// Translates a virtual address according to the vCPU's current address translation mode.
22+
pub fn translate_gva(_vcpu_fd: &VcpuFd, _gva: u64) -> Result<u64, GdbTargetError> {
23+
unimplemented!()
24+
}
25+
26+
/// Configures the kvm guest debug regs to register the hardware breakpoints
27+
fn set_kvm_debug(
28+
_control: u32,
29+
_vcpu_fd: &VcpuFd,
30+
_addrs: &[GuestAddress],
31+
) -> Result<(), GdbTargetError> {
32+
unimplemented!()
33+
}
34+
35+
/// Configures the Vcpu for debugging and sets the hardware breakpoints on the Vcpu
36+
pub fn vcpu_set_debug(
37+
_vcpu_fd: &VcpuFd,
38+
_addrs: &[GuestAddress],
39+
_step: bool,
40+
) -> Result<(), GdbTargetError> {
41+
unimplemented!()
42+
}
43+
44+
/// Injects a BP back into the guest kernel for it to handle, this is particularly useful for the
45+
/// kernels selftesting which can happen during boot.
46+
pub fn vcpu_inject_bp(
47+
_vcpu_fd: &VcpuFd,
48+
_addrs: &[GuestAddress],
49+
_step: bool,
50+
) -> Result<(), GdbTargetError> {
51+
unimplemented!()
52+
}
53+
54+
/// Reads the registers for the Vcpu
55+
pub fn read_registers(_vcpu_fd: &VcpuFd, _regs: &mut CoreRegs) -> Result<(), GdbTargetError> {
56+
unimplemented!()
57+
}
58+
59+
/// Writes to the registers for the Vcpu
60+
pub fn write_registers(_vcpu_fd: &VcpuFd, _regs: &CoreRegs) -> Result<(), GdbTargetError> {
61+
unimplemented!()
62+
}

src/vmm/src/gdb/arch/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
5+
#[cfg(target_arch = "aarch64")]
6+
mod aarch64;
7+
#[cfg(target_arch = "aarch64")]
8+
pub use aarch64::*;
9+
10+
#[cfg(target_arch = "x86_64")]
11+
mod x86;
12+
#[cfg(target_arch = "x86_64")]
13+
pub use x86::*;

0 commit comments

Comments
 (0)