xref: /cloud-hypervisor/vmm/src/gdb.rs (revision ed63b352d1ebf70f36c7d36a0d6b52fc96186581)
1 // Copyright 2022 Akira Moroo.
2 // Portions Copyright 2020 The Chromium OS Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE-BSD-3-Clause file.
5 //
6 // SPDX-License-Identifier: BSD-3-Clause
7 
8 use std::os::unix::net::UnixListener;
9 use std::sync::mpsc;
10 
11 use gdbstub::arch::Arch;
12 use gdbstub::common::{Signal, Tid};
13 use gdbstub::conn::{Connection, ConnectionExt};
14 use gdbstub::stub::{run_blocking, DisconnectReason, MultiThreadStopReason};
15 use gdbstub::target::ext::base::multithread::{
16     MultiThreadBase, MultiThreadResume, MultiThreadResumeOps, MultiThreadSingleStep,
17     MultiThreadSingleStepOps,
18 };
19 use gdbstub::target::ext::base::BaseOps;
20 use gdbstub::target::ext::breakpoints::{
21     Breakpoints, BreakpointsOps, HwBreakpoint, HwBreakpointOps,
22 };
23 use gdbstub::target::{Target, TargetError, TargetResult};
24 #[cfg(target_arch = "aarch64")]
25 use gdbstub_arch::aarch64::reg::AArch64CoreRegs as CoreRegs;
26 #[cfg(target_arch = "aarch64")]
27 use gdbstub_arch::aarch64::AArch64 as GdbArch;
28 #[cfg(target_arch = "x86_64")]
29 use gdbstub_arch::x86::reg::X86_64CoreRegs as CoreRegs;
30 #[cfg(target_arch = "x86_64")]
31 use gdbstub_arch::x86::X86_64_SSE as GdbArch;
32 use thiserror::Error;
33 use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryError};
34 
35 use crate::GuestMemoryMmap;
36 
37 type ArchUsize = u64;
38 
39 #[derive(Error, Debug)]
40 pub enum DebuggableError {
41     #[error("Setting debug failed")]
42     SetDebug(#[source] hypervisor::HypervisorCpuError),
43     #[error("Pausing failed")]
44     Pause(#[source] vm_migration::MigratableError),
45     #[error("Resuming failed")]
46     Resume(#[source] vm_migration::MigratableError),
47     #[error("Reading registers failed")]
48     ReadRegs(#[source] crate::cpu::Error),
49     #[error("Writing registers failed")]
50     WriteRegs(#[source] crate::cpu::Error),
51     #[error("Reading memory failed")]
52     ReadMem(#[source] GuestMemoryError),
53     #[error("Writing memory failed")]
54     WriteMem(#[source] GuestMemoryError),
55     #[error("Translating GVA failed")]
56     TranslateGva(#[source] crate::cpu::Error),
57     #[error("The lock is poisened")]
58     PoisonedState,
59 }
60 
61 pub trait Debuggable: vm_migration::Pausable {
62     fn set_guest_debug(
63         &self,
64         cpu_id: usize,
65         addrs: &[GuestAddress],
66         singlestep: bool,
67     ) -> Result<(), DebuggableError>;
68     fn debug_pause(&mut self) -> std::result::Result<(), DebuggableError>;
69     fn debug_resume(&mut self) -> std::result::Result<(), DebuggableError>;
70     fn read_regs(&self, cpu_id: usize) -> std::result::Result<CoreRegs, DebuggableError>;
71     fn write_regs(
72         &self,
73         cpu_id: usize,
74         regs: &CoreRegs,
75     ) -> std::result::Result<(), DebuggableError>;
76     fn read_mem(
77         &self,
78         guest_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
79         cpu_id: usize,
80         vaddr: GuestAddress,
81         len: usize,
82     ) -> std::result::Result<Vec<u8>, DebuggableError>;
83     fn write_mem(
84         &self,
85         guest_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
86         cpu_id: usize,
87         vaddr: &GuestAddress,
88         data: &[u8],
89     ) -> std::result::Result<(), DebuggableError>;
90     fn active_vcpus(&self) -> usize;
91 }
92 
93 #[derive(Error, Debug)]
94 pub enum Error {
95     #[error("VM failed")]
96     Vm(#[source] crate::vm::Error),
97     #[error("GDB request failed")]
98     GdbRequest,
99     #[error("GDB couldn't be notified")]
100     GdbResponseNotify(#[source] std::io::Error),
101     #[error("GDB response failed")]
102     GdbResponse(#[source] mpsc::RecvError),
103     #[error("GDB response timeout")]
104     GdbResponseTimeout(#[source] mpsc::RecvTimeoutError),
105 }
106 type GdbResult<T> = std::result::Result<T, Error>;
107 
108 #[derive(Debug)]
109 pub struct GdbRequest {
110     pub sender: mpsc::Sender<GdbResponse>,
111     pub payload: GdbRequestPayload,
112     pub cpu_id: usize,
113 }
114 
115 #[derive(Debug)]
116 pub enum GdbRequestPayload {
117     ReadRegs,
118     WriteRegs(Box<CoreRegs>),
119     ReadMem(GuestAddress, usize),
120     WriteMem(GuestAddress, Vec<u8>),
121     Pause,
122     Resume,
123     SetSingleStep(bool),
124     SetHwBreakPoint(Vec<GuestAddress>),
125     ActiveVcpus,
126 }
127 
128 pub type GdbResponse = std::result::Result<GdbResponsePayload, Error>;
129 
130 #[derive(Debug)]
131 pub enum GdbResponsePayload {
132     CommandComplete,
133     RegValues(Box<CoreRegs>),
134     MemoryRegion(Vec<u8>),
135     ActiveVcpus(usize),
136 }
137 
138 pub struct GdbStub {
139     gdb_sender: mpsc::Sender<GdbRequest>,
140     gdb_event: vmm_sys_util::eventfd::EventFd,
141     vm_event: vmm_sys_util::eventfd::EventFd,
142     hw_breakpoints: Vec<GuestAddress>,
143     single_step: bool,
144 }
145 
146 impl GdbStub {
147     pub fn new(
148         gdb_sender: mpsc::Sender<GdbRequest>,
149         gdb_event: vmm_sys_util::eventfd::EventFd,
150         vm_event: vmm_sys_util::eventfd::EventFd,
151         hw_breakpoints: usize,
152     ) -> Self {
153         Self {
154             gdb_sender,
155             gdb_event,
156             vm_event,
157             hw_breakpoints: Vec::with_capacity(hw_breakpoints),
158             single_step: false,
159         }
160     }
161 
162     fn vm_request(
163         &self,
164         payload: GdbRequestPayload,
165         cpu_id: usize,
166     ) -> GdbResult<GdbResponsePayload> {
167         let (response_sender, response_receiver) = std::sync::mpsc::channel();
168         let request = GdbRequest {
169             sender: response_sender,
170             payload,
171             cpu_id,
172         };
173         self.gdb_sender
174             .send(request)
175             .map_err(|_| Error::GdbRequest)?;
176         self.gdb_event.write(1).map_err(Error::GdbResponseNotify)?;
177         let res = response_receiver.recv().map_err(Error::GdbResponse)??;
178         Ok(res)
179     }
180 }
181 
182 impl Target for GdbStub {
183     type Arch = GdbArch;
184     type Error = String;
185 
186     #[inline(always)]
187     fn base_ops(&mut self) -> BaseOps<'_, Self::Arch, Self::Error> {
188         BaseOps::MultiThread(self)
189     }
190 
191     #[inline(always)]
192     fn support_breakpoints(&mut self) -> Option<BreakpointsOps<'_, Self>> {
193         Some(self)
194     }
195 
196     #[inline(always)]
197     fn guard_rail_implicit_sw_breakpoints(&self) -> bool {
198         true
199     }
200 }
201 
202 fn tid_to_cpuid(tid: Tid) -> usize {
203     tid.get() - 1
204 }
205 
206 fn cpuid_to_tid(cpu_id: usize) -> Tid {
207     Tid::new(get_raw_tid(cpu_id)).unwrap()
208 }
209 
210 pub fn get_raw_tid(cpu_id: usize) -> usize {
211     cpu_id + 1
212 }
213 
214 impl MultiThreadBase for GdbStub {
215     fn read_registers(
216         &mut self,
217         regs: &mut <Self::Arch as Arch>::Registers,
218         tid: Tid,
219     ) -> TargetResult<(), Self> {
220         match self.vm_request(GdbRequestPayload::ReadRegs, tid_to_cpuid(tid)) {
221             Ok(GdbResponsePayload::RegValues(r)) => {
222                 *regs = *r;
223                 Ok(())
224             }
225             Ok(s) => {
226                 error!("Unexpected response for ReadRegs: {:?}", s);
227                 Err(TargetError::NonFatal)
228             }
229             Err(e) => {
230                 error!("Failed to request ReadRegs: {:?}", e);
231                 Err(TargetError::NonFatal)
232             }
233         }
234     }
235 
236     fn write_registers(
237         &mut self,
238         regs: &<Self::Arch as Arch>::Registers,
239         tid: Tid,
240     ) -> TargetResult<(), Self> {
241         match self.vm_request(
242             GdbRequestPayload::WriteRegs(Box::new(regs.clone())),
243             tid_to_cpuid(tid),
244         ) {
245             Ok(_) => Ok(()),
246             Err(e) => {
247                 error!("Failed to request WriteRegs: {:?}", e);
248                 Err(TargetError::NonFatal)
249             }
250         }
251     }
252 
253     fn read_addrs(
254         &mut self,
255         start_addr: <Self::Arch as Arch>::Usize,
256         data: &mut [u8],
257         tid: Tid,
258     ) -> TargetResult<usize, Self> {
259         match self.vm_request(
260             GdbRequestPayload::ReadMem(GuestAddress(start_addr), data.len()),
261             tid_to_cpuid(tid),
262         ) {
263             Ok(GdbResponsePayload::MemoryRegion(r)) => {
264                 for (dst, v) in data.iter_mut().zip(r.iter()) {
265                     *dst = *v;
266                 }
267                 Ok(std::cmp::min(data.len(), r.len()))
268             }
269             Ok(s) => {
270                 error!("Unexpected response for ReadMem: {:?}", s);
271                 Err(TargetError::NonFatal)
272             }
273             Err(e) => {
274                 error!("Failed to request ReadMem: {:?}", e);
275                 Err(TargetError::NonFatal)
276             }
277         }
278     }
279 
280     fn write_addrs(
281         &mut self,
282         start_addr: <Self::Arch as Arch>::Usize,
283         data: &[u8],
284         tid: Tid,
285     ) -> TargetResult<(), Self> {
286         match self.vm_request(
287             GdbRequestPayload::WriteMem(GuestAddress(start_addr), data.to_owned()),
288             tid_to_cpuid(tid),
289         ) {
290             Ok(_) => Ok(()),
291             Err(e) => {
292                 error!("Failed to request WriteMem: {:?}", e);
293                 Err(TargetError::NonFatal)
294             }
295         }
296     }
297 
298     fn list_active_threads(
299         &mut self,
300         thread_is_active: &mut dyn FnMut(Tid),
301     ) -> Result<(), Self::Error> {
302         match self.vm_request(GdbRequestPayload::ActiveVcpus, 0) {
303             Ok(GdbResponsePayload::ActiveVcpus(active_vcpus)) => {
304                 (0..active_vcpus).for_each(|cpu_id| {
305                     thread_is_active(cpuid_to_tid(cpu_id));
306                 });
307                 Ok(())
308             }
309             Ok(s) => Err(format!("Unexpected response for ActiveVcpus: {s:?}")),
310             Err(e) => Err(format!("Failed to request ActiveVcpus: {e:?}")),
311         }
312     }
313 
314     #[inline(always)]
315     fn support_resume(&mut self) -> Option<MultiThreadResumeOps<'_, Self>> {
316         Some(self)
317     }
318 }
319 
320 impl MultiThreadResume for GdbStub {
321     fn resume(&mut self) -> Result<(), Self::Error> {
322         match self.vm_request(GdbRequestPayload::Resume, 0) {
323             Ok(_) => Ok(()),
324             Err(e) => Err(format!("Failed to resume the target: {e:?}")),
325         }
326     }
327 
328     fn clear_resume_actions(&mut self) -> Result<(), Self::Error> {
329         if self.single_step {
330             match self.vm_request(GdbRequestPayload::SetSingleStep(false), 0) {
331                 Ok(_) => {
332                     self.single_step = false;
333                 }
334                 Err(e) => {
335                     return Err(format!("Failed to request SetSingleStep: {e:?}"));
336                 }
337             }
338         }
339         Ok(())
340     }
341 
342     fn set_resume_action_continue(
343         &mut self,
344         tid: Tid,
345         signal: Option<Signal>,
346     ) -> Result<(), Self::Error> {
347         if signal.is_some() {
348             return Err("no support for continuing with signal".to_owned());
349         }
350         match self.vm_request(GdbRequestPayload::Resume, tid_to_cpuid(tid)) {
351             Ok(_) => Ok(()),
352             Err(e) => Err(format!("Failed to resume the target: {e:?}")),
353         }
354     }
355 
356     #[inline(always)]
357     fn support_single_step(&mut self) -> Option<MultiThreadSingleStepOps<'_, Self>> {
358         Some(self)
359     }
360 }
361 
362 impl MultiThreadSingleStep for GdbStub {
363     fn set_resume_action_step(
364         &mut self,
365         tid: Tid,
366         signal: Option<Signal>,
367     ) -> Result<(), Self::Error> {
368         if signal.is_some() {
369             return Err("no support for stepping with signal".to_owned());
370         }
371 
372         if !self.single_step {
373             match self.vm_request(GdbRequestPayload::SetSingleStep(true), tid_to_cpuid(tid)) {
374                 Ok(_) => {
375                     self.single_step = true;
376                 }
377                 Err(e) => {
378                     return Err(format!("Failed to request SetSingleStep: {e:?}"));
379                 }
380             }
381         }
382         match self.vm_request(GdbRequestPayload::Resume, tid_to_cpuid(tid)) {
383             Ok(_) => Ok(()),
384             Err(e) => Err(format!("Failed to resume the target: {e:?}")),
385         }
386     }
387 }
388 
389 impl Breakpoints for GdbStub {
390     #[inline(always)]
391     fn support_hw_breakpoint(&mut self) -> Option<HwBreakpointOps<'_, Self>> {
392         Some(self)
393     }
394 }
395 
396 impl HwBreakpoint for GdbStub {
397     fn add_hw_breakpoint(
398         &mut self,
399         addr: <Self::Arch as Arch>::Usize,
400         _kind: <Self::Arch as Arch>::BreakpointKind,
401     ) -> TargetResult<bool, Self> {
402         // If the HW breakpoints reach the limit, no more can be added.
403         if self.hw_breakpoints.len() >= self.hw_breakpoints.capacity() {
404             error!(
405                 "Not allowed to set more than {} HW breakpoints",
406                 self.hw_breakpoints.capacity()
407             );
408             return Ok(false);
409         }
410 
411         self.hw_breakpoints.push(GuestAddress(addr));
412 
413         let payload = GdbRequestPayload::SetHwBreakPoint(self.hw_breakpoints.clone());
414         match self.vm_request(payload, 0) {
415             Ok(_) => Ok(true),
416             Err(e) => {
417                 error!("Failed to request SetHwBreakPoint: {:?}", e);
418                 Err(TargetError::NonFatal)
419             }
420         }
421     }
422     fn remove_hw_breakpoint(
423         &mut self,
424         addr: <Self::Arch as Arch>::Usize,
425         _kind: <Self::Arch as Arch>::BreakpointKind,
426     ) -> TargetResult<bool, Self> {
427         match self.hw_breakpoints.iter().position(|&b| b.0 == addr) {
428             None => return Ok(false),
429             Some(pos) => self.hw_breakpoints.remove(pos),
430         };
431 
432         let payload = GdbRequestPayload::SetHwBreakPoint(self.hw_breakpoints.clone());
433         match self.vm_request(payload, 0) {
434             Ok(_) => Ok(true),
435             Err(e) => {
436                 error!("Failed to request SetHwBreakPoint: {:?}", e);
437                 Err(TargetError::NonFatal)
438             }
439         }
440     }
441 }
442 
443 enum GdbEventLoop {}
444 
445 impl run_blocking::BlockingEventLoop for GdbEventLoop {
446     type Target = GdbStub;
447     type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>;
448     type StopReason = MultiThreadStopReason<ArchUsize>;
449 
450     fn wait_for_stop_reason(
451         target: &mut Self::Target,
452         conn: &mut Self::Connection,
453     ) -> Result<
454         run_blocking::Event<Self::StopReason>,
455         run_blocking::WaitForStopReasonError<
456             <Self::Target as Target>::Error,
457             <Self::Connection as Connection>::Error,
458         >,
459     > {
460         // Polling
461         loop {
462             // This read is non-blocking.
463             match target.vm_event.read() {
464                 Ok(tid) => {
465                     target
466                         .vm_request(GdbRequestPayload::Pause, 0)
467                         .map_err(|_| {
468                             run_blocking::WaitForStopReasonError::Target(
469                                 "Failed to pause VM".to_owned(),
470                             )
471                         })?;
472                     let stop_reason = if target.single_step {
473                         MultiThreadStopReason::DoneStep
474                     } else {
475                         MultiThreadStopReason::HwBreak(Tid::new(tid as usize).unwrap())
476                     };
477                     return Ok(run_blocking::Event::TargetStopped(stop_reason));
478                 }
479                 Err(e) => {
480                     if e.kind() != std::io::ErrorKind::WouldBlock {
481                         return Err(run_blocking::WaitForStopReasonError::Connection(e));
482                     }
483                 }
484             }
485 
486             if conn.peek().map(|b| b.is_some()).unwrap_or(true) {
487                 let byte = conn
488                     .read()
489                     .map_err(run_blocking::WaitForStopReasonError::Connection)?;
490                 return Ok(run_blocking::Event::IncomingData(byte));
491             }
492         }
493     }
494 
495     fn on_interrupt(
496         target: &mut Self::Target,
497     ) -> Result<Option<Self::StopReason>, <Self::Target as Target>::Error> {
498         target
499             .vm_request(GdbRequestPayload::Pause, 0)
500             .map_err(|e| {
501                 error!("Failed to pause the target: {:?}", e);
502                 "Failed to pause the target"
503             })?;
504         Ok(Some(MultiThreadStopReason::Signal(Signal::SIGINT)))
505     }
506 }
507 
508 pub fn gdb_thread(mut gdbstub: GdbStub, path: &std::path::Path) {
509     let listener = match UnixListener::bind(path) {
510         Ok(s) => s,
511         Err(e) => {
512             error!("Failed to create a Unix domain socket listener: {}", e);
513             return;
514         }
515     };
516     info!("Waiting for a GDB connection on {}...", path.display());
517 
518     let (stream, addr) = match listener.accept() {
519         Ok(v) => v,
520         Err(e) => {
521             error!("Failed to accept a connection from GDB: {}", e);
522             return;
523         }
524     };
525     info!("GDB connected from {:?}", addr);
526 
527     let connection: Box<dyn ConnectionExt<Error = std::io::Error>> = Box::new(stream);
528     let gdb = gdbstub::stub::GdbStub::new(connection);
529 
530     match gdb.run_blocking::<GdbEventLoop>(&mut gdbstub) {
531         Ok(disconnect_reason) => match disconnect_reason {
532             DisconnectReason::Disconnect => {
533                 info!("GDB client has disconnected. Running...");
534 
535                 if let Err(e) = gdbstub.vm_request(GdbRequestPayload::SetSingleStep(false), 0) {
536                     error!("Failed to disable single step: {:?}", e);
537                 }
538 
539                 if let Err(e) =
540                     gdbstub.vm_request(GdbRequestPayload::SetHwBreakPoint(Vec::new()), 0)
541                 {
542                     error!("Failed to remove breakpoints: {:?}", e);
543                 }
544 
545                 if let Err(e) = gdbstub.vm_request(GdbRequestPayload::Resume, 0) {
546                     error!("Failed to resume the VM: {:?}", e);
547                 }
548             }
549             _ => {
550                 error!("Target exited or terminated");
551             }
552         },
553         Err(e) => {
554             error!("error occurred in GDB session: {}", e);
555         }
556     }
557 }
558