Skip to content

Commit cc55d3a

Browse files
committed
Use Gdb results and store vcpus in vec
Updated to bubble up gdb -> vcpu errors back to the server to allow for easier debugging. Updated to store vcpus in a vec rather than a map Signed-off-by: Jack Thomson <[email protected]>
1 parent 5393dd0 commit cc55d3a

File tree

4 files changed

+90
-115
lines changed

4 files changed

+90
-115
lines changed

src/vmm/src/builder.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ pub enum StartMicrovmError {
132132
/// Error configuring ACPI: {0}
133133
#[cfg(target_arch = "x86_64")]
134134
Acpi(#[from] crate::acpi::AcpiError),
135+
/// Error starting GDB debug session
136+
#[cfg(feature = "debug")]
137+
GdbServer(gdb::target::Error),
135138
}
136139

137140
/// It's convenient to automatically convert `linux_loader::cmdline::Error`s
@@ -313,8 +316,7 @@ pub fn build_microvm_for_boot(
313316
#[cfg(feature = "debug")]
314317
vcpus
315318
.iter_mut()
316-
.enumerate()
317-
.for_each(|(id, vcpu)| vcpu.attach_debug_info(gdb_tx.clone(), id));
319+
.for_each(|vcpu| vcpu.attach_debug_info(gdb_tx.clone()));
318320

319321
// The boot timer device needs to be the first device attached in order
320322
// to maintain the same MMIO address referenced in the documentation
@@ -366,7 +368,7 @@ pub fn build_microvm_for_boot(
366368
let vmm = Arc::new(Mutex::new(vmm));
367369

368370
#[cfg(feature = "debug")]
369-
gdb::server::gdb_thread(vmm.clone(), &vcpus, gdb_rx, entry_addr);
371+
gdb::server::gdb_thread(vmm.clone(), &vcpus, gdb_rx, entry_addr).map_err(GdbServer)?;
370372

371373
// Move vcpus to their own threads and start their state machine in the 'Paused' state.
372374
vmm.lock()

src/vmm/src/gdb/server.rs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
use std::os::unix::net::{UnixListener, UnixStream};
4+
use std::os::unix::net::UnixListener;
55
use std::path::Path;
66
use std::sync::mpsc::Receiver;
77
use std::sync::{Arc, Mutex};
@@ -14,20 +14,13 @@ use kvm_ioctls::VcpuFd;
1414
use vm_memory::GuestAddress;
1515

1616
use super::event_loop::event_loop;
17-
use crate::logger::{error, info};
17+
use super::target::Error;
18+
use crate::logger::info;
1819
use crate::{Vcpu, Vmm};
1920

2021
const KVM_DEBUG_ENABLE: u64 = 0x0600;
2122

22-
fn listen_for_connection(path: &std::path::Path) -> Result<UnixStream, Box<dyn std::error::Error>> {
23-
let listener = UnixListener::bind(path)?;
24-
info!("Waiting for GDB server connection on {}...", path.display());
25-
let (stream, _addr) = listener.accept()?;
26-
27-
Ok(stream)
28-
}
29-
30-
fn set_kvm_debug(control: u32, vcpu: &VcpuFd, addrs: &[GuestAddress]) {
23+
fn set_kvm_debug(control: u32, vcpu: &VcpuFd, addrs: &[GuestAddress]) -> Result<(), Error> {
3124
let mut dbg = kvm_guest_debug {
3225
control,
3326
..Default::default()
@@ -37,17 +30,15 @@ fn set_kvm_debug(control: u32, vcpu: &VcpuFd, addrs: &[GuestAddress]) {
3730

3831
for (i, addr) in addrs.iter().enumerate() {
3932
dbg.arch.debugreg[i] = addr.0;
40-
// Set global breakpoint enable flag
33+
// Set global breakpoint enable flag for the specific breakpoint number by setting the bit
4134
dbg.arch.debugreg[7] |= 2 << (i * 2);
4235
}
4336

44-
if vcpu.set_guest_debug(&dbg).is_err() {
45-
error!("Error setting debug");
46-
}
37+
vcpu.set_guest_debug(&dbg).map_err(|_| Error::VcpuKvmError)
4738
}
4839

49-
/// Configures the VCPU for
50-
pub fn kvm_debug(vcpu: &VcpuFd, addrs: &[GuestAddress], step: bool) {
40+
/// Configures the VCPU for debugging and sets the hardware breakpoints on the vcpu
41+
pub fn vcpu_set_debug(vcpu: &VcpuFd, addrs: &[GuestAddress], step: bool) -> Result<(), Error> {
5142
let mut control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP | KVM_GUESTDBG_USE_SW_BP;
5243
if step {
5344
control |= KVM_GUESTDBG_SINGLESTEP;
@@ -58,7 +49,7 @@ pub fn kvm_debug(vcpu: &VcpuFd, addrs: &[GuestAddress], step: bool) {
5849

5950
/// Injects a BP back into the guest kernel for it to handle, this is particularly useful for the
6051
/// kernels selftesting which can happen during boot.
61-
pub fn kvm_inject_bp(vcpu: &VcpuFd, addrs: &[GuestAddress], step: bool) {
52+
pub fn vcpu_inject_bp(vcpu: &VcpuFd, addrs: &[GuestAddress], step: bool) -> Result<(), Error> {
6253
let mut control = KVM_GUESTDBG_ENABLE
6354
| KVM_GUESTDBG_USE_HW_BP
6455
| KVM_GUESTDBG_USE_SW_BP
@@ -87,20 +78,24 @@ pub fn gdb_thread(
8778
vcpus: &[Vcpu],
8879
gdb_event_receiver: Receiver<usize>,
8980
entry_addr: GuestAddress,
90-
) {
81+
) -> Result<(), Error> {
9182
// We register a hw breakpoint at the entry point as GDB expects the application
9283
// to be stopped as it connects. This also allows us to set breakpoints before kernel starts
93-
kvm_debug(&vcpus[0].kvm_vcpu.fd, &[entry_addr], false);
84+
vcpu_set_debug(&vcpus[0].kvm_vcpu.fd, &[entry_addr], false)?;
9485

9586
for vcpu in vcpus.iter().skip(1) {
96-
kvm_debug(&vcpu.kvm_vcpu.fd, &[], false);
87+
vcpu_set_debug(&vcpu.kvm_vcpu.fd, &[], false)?;
9788
}
9889

9990
let path = Path::new("/tmp/gdb.socket");
100-
let connection = listen_for_connection(path).expect("Error waiting for connection");
91+
let listener = UnixListener::bind(path).map_err(|_| Error::ServerSocketError)?;
92+
info!("Waiting for GDB server connection on {}...", path.display());
93+
let (connection, _addr) = listener.accept().map_err(|_| Error::ServerSocketError)?;
10194

10295
std::thread::Builder::new()
10396
.name("gdb".into())
10497
.spawn(move || event_loop(connection, vmm, gdb_event_receiver, entry_addr))
105-
.expect("Error spawning GDB thread");
98+
.map_err(|_| Error::GdbThreadError)?;
99+
100+
Ok(())
106101
}

src/vmm/src/gdb/target.rs

Lines changed: 34 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ use gdbstub::target::ext::breakpoints::{
1818
};
1919
use gdbstub::target::ext::thread_extra_info::{ThreadExtraInfo, ThreadExtraInfoOps};
2020
use gdbstub::target::{Target, TargetError, TargetResult};
21-
#[cfg(target_arch = "aarch64")]
22-
use gdbstub_arch::aarch64::reg::AArch64CoreRegs as CoreRegs;
23-
#[cfg(target_arch = "aarch64")]
24-
use gdbstub_arch::aarch64::AArch64 as GdbArch;
2521
#[cfg(target_arch = "x86_64")]
2622
use gdbstub_arch::x86::reg::X86_64CoreRegs as CoreRegs;
2723
#[cfg(target_arch = "x86_64")]
@@ -30,7 +26,8 @@ use kvm_bindings::kvm_regs;
3026
use vm_memory::{Bytes, GuestAddress};
3127

3228
use crate::logger::{error, info};
33-
use crate::{FcExitCode, VcpuEvent, VcpuHandle, VcpuResponse, Vmm};
29+
use crate::vstate::vcpu::VcpuError;
30+
use crate::{arch, FcExitCode, VcpuEvent, VcpuHandle, VcpuResponse, Vmm};
3431

3532
const X86_SW_BP_OP: u8 = 0xCC;
3633

@@ -51,6 +48,12 @@ pub enum Error {
5148
VCPURequestError,
5249
/// No currently paused Vcpu error
5350
NoPausedVCpu,
51+
/// Error when setting vcpu debug flags
52+
VcpuKvmError,
53+
/// Server socket Error
54+
ServerSocketError,
55+
/// Error with creating GDB thread
56+
GdbThreadError,
5457
}
5558

5659
impl<E> From<Error> for TargetError<E> {
@@ -72,7 +75,7 @@ pub struct FirecrackerTarget {
7275
hw_breakpoints: Vec<GuestAddress>,
7376
sw_breakpoints: HashMap<<GdbArch as Arch>::Usize, [u8; 1]>,
7477

75-
vcpu_state: HashMap<Tid, VCpuState>,
78+
vcpu_state: Vec<VCpuState>,
7679

7780
paused_vcpu: Option<Tid>,
7881
}
@@ -98,22 +101,19 @@ impl FirecrackerTarget {
98101
/// will handle requests from GDB and perform the appropriate actions, while also updating GDB
99102
/// with the state of the VMM / VCPU's as we hit debug events
100103
pub fn new(vmm: Arc<Mutex<Vmm>>, gdb_event: Receiver<usize>, entry_addr: GuestAddress) -> Self {
101-
let mut vcpu_state = HashMap::new();
102104
let cpu_count = {
103105
vmm.lock()
104106
.expect("Exception unlocking vmm")
105107
.vcpus_handles
106108
.len()
107109
};
108110

109-
for cpu_id in 0..cpu_count {
110-
let new_state = VCpuState {
111+
let vcpu_state = (0..cpu_count)
112+
.map(|_| VCpuState {
111113
paused: false,
112114
single_step: false,
113-
};
114-
115-
vcpu_state.insert(cpuid_to_tid(cpu_id), new_state);
116-
}
115+
})
116+
.collect();
117117

118118
Self {
119119
vmm,
@@ -138,24 +138,19 @@ impl FirecrackerTarget {
138138
/// This is used to notify the target that the provided Tid
139139
/// is in a paused state
140140
pub fn notify_paused_vcpu(&mut self, tid: Tid) {
141-
let found = match self.vcpu_state.get_mut(&tid) {
142-
Some(res) => res,
143-
None => {
144-
info!("TID {tid} was not known.");
145-
return;
146-
}
147-
};
148-
141+
let found = &mut self.vcpu_state[tid_to_cpuid(tid)];
149142
found.paused = true;
143+
150144
self.paused_vcpu = Some(tid);
151145
}
152146

153147
fn resume_execution(&mut self) -> Result<(), Error> {
154148
let to_resume: Vec<Tid> = self
155149
.vcpu_state
156150
.iter()
157-
.filter_map(|(tid, state)| match state.paused {
158-
true => Some(*tid),
151+
.enumerate()
152+
.filter_map(|(cpu_id, state)| match state.paused {
153+
true => Some(cpuid_to_tid(cpu_id)),
159154
false => None,
160155
})
161156
.collect();
@@ -165,7 +160,7 @@ impl FirecrackerTarget {
165160
self.request_resume(tid)?;
166161
}
167162

168-
self.vcpu_state.iter_mut().for_each(|(_, state)| {
163+
self.vcpu_state.iter_mut().for_each(|state| {
169164
state.paused = false;
170165
});
171166

@@ -179,7 +174,7 @@ impl FirecrackerTarget {
179174
}
180175

181176
fn reset_all_states(&mut self) {
182-
for (_, value) in self.vcpu_state.iter_mut() {
177+
for value in self.vcpu_state.iter_mut() {
183178
Self::reset_vcpu_state(value);
184179
}
185180
}
@@ -194,13 +189,7 @@ impl FirecrackerTarget {
194189

195190
/// This method can be used to manually pause the requested vcpu
196191
pub fn request_pause(&mut self, tid: Tid) -> Result<(), Error> {
197-
let vcpu_state = match self.vcpu_state.get_mut(&tid) {
198-
Some(res) => res,
199-
None => {
200-
info!("Attempted to pause a vcpu we have no state for.");
201-
return Err(Error::VCPURequestError);
202-
}
203-
};
192+
let vcpu_state = &mut self.vcpu_state[tid_to_cpuid(tid)];
204193

205194
if vcpu_state.paused {
206195
info!("Attempted to pause a vcpu already paused.");
@@ -268,13 +257,7 @@ impl FirecrackerTarget {
268257
/// Used to request the specified core to resume execution. The function
269258
/// will return early if the vcpu is already paused and not currently running
270259
pub fn request_resume(&mut self, tid: Tid) -> Result<(), Error> {
271-
let vcpu_state = match self.vcpu_state.get_mut(&tid) {
272-
Some(res) => res,
273-
None => {
274-
error!("Attempted to resume a vcpu we have no state for.");
275-
return Err(Error::VCPURequestError);
276-
}
277-
};
260+
let vcpu_state = &mut self.vcpu_state[tid_to_cpuid(tid)];
278261

279262
if !vcpu_state.paused {
280263
info!("Attempted to resume a vcpu already running.");
@@ -288,6 +271,7 @@ impl FirecrackerTarget {
288271
cpu_handle
289272
.send_event(VcpuEvent::Resume)
290273
.expect("Error sending message to vcpu");
274+
291275
let response = cpu_handle
292276
.response_receiver()
293277
.recv()
@@ -305,13 +289,7 @@ impl FirecrackerTarget {
305289
let cpu_handle =
306290
&self.vmm.lock().expect("error unlocking vmm").vcpus_handles[tid_to_cpuid(tid)];
307291

308-
let vcpu_state = match self.vcpu_state.get(&tid) {
309-
Some(res) => res,
310-
None => {
311-
error!("Attempted to write kvm debug to a vcpu we have no state for.");
312-
return Err(Error::VCPURequestError);
313-
}
314-
};
292+
let vcpu_state = &self.vcpu_state[tid_to_cpuid(tid)];
315293

316294
cpu_handle
317295
.send_event(VcpuEvent::SetKvmDebug(
@@ -325,6 +303,7 @@ impl FirecrackerTarget {
325303
error!("Response resume : {message}");
326304
Err(Error::VCPURequestError)
327305
}
306+
Ok(VcpuResponse::Error(VcpuError::GdbRequest(e))) => Err(e),
328307
Err(_) => Err(Error::VCPURequestError),
329308
_ => Ok(()),
330309
}
@@ -340,7 +319,10 @@ impl FirecrackerTarget {
340319
.expect("Error recieving message from vcpu");
341320

342321
if let VcpuResponse::GvaTranslation(response) = response {
343-
return Ok(response);
322+
return match response {
323+
Some(res) => Ok(res.into()),
324+
None => Err(Error::VCPURequestError),
325+
};
344326
}
345327

346328
if let VcpuResponse::NotAllowed(message) = response {
@@ -364,6 +346,7 @@ impl FirecrackerTarget {
364346
error!("Response resume : {message}");
365347
Err(Error::VCPURequestError)
366348
}
349+
Ok(VcpuResponse::Error(VcpuError::GdbRequest(e))) => Err(e),
367350
Err(_) => Err(Error::VCPURequestError),
368351
_ => Ok(()),
369352
}
@@ -387,13 +370,7 @@ impl FirecrackerTarget {
387370
}
388371

389372
let cpu_regs = self.get_regs(tid);
390-
391-
let vcpu_state = match self.vcpu_state.get(&tid) {
392-
Some(res) => res,
393-
None => {
394-
return None;
395-
}
396-
};
373+
let vcpu_state = &mut self.vcpu_state[tid_to_cpuid(tid)];
397374

398375
if vcpu_state.single_step {
399376
return Some(MultiThreadStopReason::SignalWithThread {
@@ -539,7 +516,7 @@ impl MultiThreadBase for FirecrackerTarget {
539516
}
540517
};
541518

542-
let psize = 4096;
519+
let psize = arch::PAGE_SIZE;
543520
let read_len = std::cmp::min(len - total_read, psize - (paddr & (psize - 1)));
544521

545522
if memory
@@ -629,13 +606,7 @@ impl MultiThreadResume for FirecrackerTarget {
629606
tid: Tid,
630607
_signal: Option<Signal>,
631608
) -> Result<(), Self::Error> {
632-
let vcpu_state = match self.vcpu_state.get_mut(&tid) {
633-
Some(res) => res,
634-
None => {
635-
error!("Attempted to set action on a vcpu we have no state for.");
636-
return Err(Error::VCPURequestError);
637-
}
638-
};
609+
let vcpu_state = &mut self.vcpu_state[tid_to_cpuid(tid)];
639610
vcpu_state.single_step = false;
640611

641612
Ok(())
@@ -663,13 +634,7 @@ impl MultiThreadSingleStep for FirecrackerTarget {
663634
tid: Tid,
664635
_signal: Option<Signal>,
665636
) -> Result<(), Self::Error> {
666-
let vcpu_state = match self.vcpu_state.get_mut(&tid) {
667-
Some(res) => res,
668-
None => {
669-
info!("Attempted to set action on a vcpu we have no state for.");
670-
return Ok(());
671-
}
672-
};
637+
let vcpu_state = &mut self.vcpu_state[tid_to_cpuid(tid)];
673638
vcpu_state.single_step = true;
674639

675640
Ok(())

0 commit comments

Comments
 (0)