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