• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::net::TcpListener;
6 use std::sync::mpsc;
7 use std::time::Duration;
8 
9 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
10 use aarch64::AArch64 as CrosvmArch;
11 use anyhow::Context;
12 use base::error;
13 use base::info;
14 use base::Tube;
15 use base::TubeError;
16 use gdbstub::arch::Arch;
17 use gdbstub::common::Signal;
18 use gdbstub::conn::Connection;
19 use gdbstub::conn::ConnectionExt;
20 use gdbstub::stub::run_blocking;
21 use gdbstub::stub::run_blocking::BlockingEventLoop;
22 use gdbstub::stub::SingleThreadStopReason;
23 use gdbstub::target::ext::base::single_register_access::SingleRegisterAccess;
24 #[cfg(target_arch = "aarch64")]
25 use gdbstub::target::ext::base::single_register_access::SingleRegisterAccessOps;
26 use gdbstub::target::ext::base::singlethread::SingleThreadBase;
27 use gdbstub::target::ext::base::singlethread::SingleThreadResume;
28 use gdbstub::target::ext::base::singlethread::SingleThreadResumeOps;
29 use gdbstub::target::ext::base::singlethread::SingleThreadSingleStep;
30 use gdbstub::target::ext::base::singlethread::SingleThreadSingleStepOps;
31 use gdbstub::target::ext::base::BaseOps;
32 use gdbstub::target::ext::breakpoints::Breakpoints;
33 use gdbstub::target::ext::breakpoints::BreakpointsOps;
34 use gdbstub::target::ext::breakpoints::HwBreakpoint;
35 use gdbstub::target::ext::breakpoints::HwBreakpointOps;
36 use gdbstub::target::Target;
37 use gdbstub::target::TargetError::NonFatal;
38 use gdbstub::target::TargetResult;
39 #[cfg(target_arch = "aarch64")]
40 use gdbstub_arch::aarch64::AArch64 as GdbArch;
41 #[cfg(target_arch = "x86_64")]
42 use gdbstub_arch::x86::X86_64_SSE as GdbArch;
43 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
44 use hypervisor::VcpuAArch64 as VcpuArch;
45 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
46 use hypervisor::VcpuX86_64 as VcpuArch;
47 use remain::sorted;
48 use sync::Mutex;
49 use thiserror::Error as ThisError;
50 use vm_control::VcpuControl;
51 use vm_control::VcpuDebug;
52 use vm_control::VcpuDebugStatus;
53 use vm_control::VcpuDebugStatusMessage;
54 use vm_control::VmRequest;
55 use vm_control::VmResponse;
56 use vm_memory::GuestAddress;
57 use vm_memory::GuestMemory;
58 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
59 use x86_64::X8664arch as CrosvmArch;
60 
gdb_thread(mut gdbstub: GdbStub, port: u32)61 pub fn gdb_thread(mut gdbstub: GdbStub, port: u32) {
62     let addr = format!("0.0.0.0:{}", port);
63     let listener = match TcpListener::bind(addr.clone()) {
64         Ok(s) => s,
65         Err(e) => {
66             error!("Failed to create a TCP listener: {}", e);
67             return;
68         }
69     };
70     info!("Waiting for a GDB connection on {:?}...", addr);
71 
72     let (stream, addr) = match listener.accept() {
73         Ok(v) => v,
74         Err(e) => {
75             error!("Failed to accept a connection from GDB: {}", e);
76             return;
77         }
78     };
79     info!("GDB connected from {}", addr);
80 
81     let connection: Box<dyn ConnectionExt<Error = std::io::Error>> = Box::new(stream);
82     let gdb = gdbstub::stub::GdbStub::new(connection);
83 
84     match gdb.run_blocking::<GdbStubEventLoop>(&mut gdbstub) {
85         Ok(reason) => {
86             info!("GDB session closed: {:?}", reason);
87         }
88         Err(e) => {
89             error!("error occurred in GDB session: {}", e);
90         }
91     }
92 
93     // Resume the VM when GDB session is disconnected.
94     if let Err(e) = gdbstub.vm_request(VmRequest::Resume) {
95         error!("Failed to resume the VM after GDB disconnected: {}", e);
96     }
97 }
98 
99 #[sorted]
100 #[derive(ThisError, Debug)]
101 enum Error {
102     /// Got an unexpected VM response.
103     #[error("Got an unexpected VM response: {0}")]
104     UnexpectedVmResponse(VmResponse),
105     /// Failed to send a vCPU request.
106     #[error("failed to send a vCPU request: {0}")]
107     VcpuRequest(mpsc::SendError<VcpuControl>),
108     /// Failed to receive a vCPU response.
109     #[error("failed to receive a vCPU response: {0}")]
110     VcpuResponse(mpsc::RecvTimeoutError),
111     /// Failed to send a VM request.
112     #[error("failed to send a VM request: {0}")]
113     VmRequest(TubeError),
114     /// Failed to receive a VM request.
115     #[error("failed to receive a VM response: {0}")]
116     VmResponse(TubeError),
117 }
118 type GdbResult<T> = std::result::Result<T, Error>;
119 
120 pub struct GdbStub {
121     vm_tube: Mutex<Tube>,
122     vcpu_com: Vec<mpsc::Sender<VcpuControl>>,
123     from_vcpu: mpsc::Receiver<VcpuDebugStatusMessage>,
124 
125     single_step: bool,
126     max_hw_breakpoints: Option<usize>,
127     hw_breakpoints: Vec<GuestAddress>,
128 }
129 
130 impl GdbStub {
new( vm_tube: Tube, vcpu_com: Vec<mpsc::Sender<VcpuControl>>, from_vcpu: mpsc::Receiver<VcpuDebugStatusMessage>, ) -> Self131     pub fn new(
132         vm_tube: Tube,
133         vcpu_com: Vec<mpsc::Sender<VcpuControl>>,
134         from_vcpu: mpsc::Receiver<VcpuDebugStatusMessage>,
135     ) -> Self {
136         GdbStub {
137             vm_tube: Mutex::new(vm_tube),
138             vcpu_com,
139             from_vcpu,
140             single_step: false,
141             max_hw_breakpoints: None,
142             hw_breakpoints: Default::default(),
143         }
144     }
145 
vcpu_request(&self, request: VcpuControl) -> GdbResult<VcpuDebugStatus>146     fn vcpu_request(&self, request: VcpuControl) -> GdbResult<VcpuDebugStatus> {
147         // We use the only one vCPU when GDB is enabled.
148         self.vcpu_com[0].send(request).map_err(Error::VcpuRequest)?;
149 
150         match self.from_vcpu.recv_timeout(Duration::from_millis(500)) {
151             Ok(msg) => Ok(msg.msg),
152             Err(e) => Err(Error::VcpuResponse(e)),
153         }
154     }
155 
vm_request(&self, request: VmRequest) -> GdbResult<()>156     fn vm_request(&self, request: VmRequest) -> GdbResult<()> {
157         let vm_tube = self.vm_tube.lock();
158         vm_tube.send(&request).map_err(Error::VmRequest)?;
159         match vm_tube.recv() {
160             Ok(VmResponse::Ok) => Ok(()),
161             Ok(r) => Err(Error::UnexpectedVmResponse(r)),
162             Err(e) => Err(Error::VmResponse(e)),
163         }
164     }
165 
max_hw_breakpoints_request(&self) -> TargetResult<usize, Self>166     fn max_hw_breakpoints_request(&self) -> TargetResult<usize, Self> {
167         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::GetHwBreakPointCount)) {
168             Ok(VcpuDebugStatus::HwBreakPointCount(n)) => Ok(n),
169             Ok(s) => {
170                 error!("Unexpected vCPU response for GetHwBreakPointCount: {:?}", s);
171                 Err(NonFatal)
172             }
173             Err(e) => {
174                 error!("Failed to request GetHwBreakPointCount: {}", e);
175                 Err(NonFatal)
176             }
177         }
178     }
179 }
180 
181 impl Target for GdbStub {
182     type Arch = GdbArch;
183     type Error = &'static str;
184 
base_ops(&mut self) -> BaseOps<Self::Arch, Self::Error>185     fn base_ops(&mut self) -> BaseOps<Self::Arch, Self::Error> {
186         BaseOps::SingleThread(self)
187     }
188 
189     // TODO(keiichiw): sw_breakpoint, hw_watchpoint, extended_mode, monitor_cmd, section_offsets
support_breakpoints(&mut self) -> Option<BreakpointsOps<Self>>190     fn support_breakpoints(&mut self) -> Option<BreakpointsOps<Self>> {
191         Some(self)
192     }
193 
194     // TODO(crbug.com/1141812): Remove this override once proper software breakpoint
195     // support has been added.
196     //
197     // Overriding this method to return `true` allows GDB to implement software
198     // breakpoints by writing breakpoint instructions directly into the target's
199     // instruction stream. This will be redundant once proper software breakpoints
200     // have been implemented. See the trait method's docs for more information:
201     // https://docs.rs/gdbstub/0.6.1/gdbstub/target/trait.Target.html#method.guard_rail_implicit_sw_breakpoints
guard_rail_implicit_sw_breakpoints(&self) -> bool202     fn guard_rail_implicit_sw_breakpoints(&self) -> bool {
203         true
204     }
205 }
206 
207 impl SingleThreadBase for GdbStub {
read_registers( &mut self, regs: &mut <Self::Arch as Arch>::Registers, ) -> TargetResult<(), Self>208     fn read_registers(
209         &mut self,
210         regs: &mut <Self::Arch as Arch>::Registers,
211     ) -> TargetResult<(), Self> {
212         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::ReadRegs)) {
213             Ok(VcpuDebugStatus::RegValues(r)) => {
214                 *regs = r;
215                 Ok(())
216             }
217             Ok(s) => {
218                 error!("Unexpected vCPU response for ReadRegs: {:?}", s);
219                 Err(NonFatal)
220             }
221             Err(e) => {
222                 error!("Failed to request ReadRegs: {}", e);
223                 Err(NonFatal)
224             }
225         }
226     }
227 
write_registers( &mut self, regs: &<Self::Arch as Arch>::Registers, ) -> TargetResult<(), Self>228     fn write_registers(
229         &mut self,
230         regs: &<Self::Arch as Arch>::Registers,
231     ) -> TargetResult<(), Self> {
232         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::WriteRegs(Box::new(
233             regs.clone(),
234         )))) {
235             Ok(VcpuDebugStatus::CommandComplete) => Ok(()),
236             Ok(s) => {
237                 error!("Unexpected vCPU response for WriteRegs: {:?}", s);
238                 Err(NonFatal)
239             }
240             Err(e) => {
241                 error!("Failed to request WriteRegs: {}", e);
242                 Err(NonFatal)
243             }
244         }
245     }
246 
read_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &mut [u8], ) -> TargetResult<(), Self>247     fn read_addrs(
248         &mut self,
249         start_addr: <Self::Arch as Arch>::Usize,
250         data: &mut [u8],
251     ) -> TargetResult<(), Self> {
252         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::ReadMem(
253             GuestAddress(start_addr),
254             data.len(),
255         ))) {
256             Ok(VcpuDebugStatus::MemoryRegion(r)) => {
257                 for (dst, v) in data.iter_mut().zip(r.iter()) {
258                     *dst = *v;
259                 }
260                 Ok(())
261             }
262             Ok(s) => {
263                 error!("Unexpected vCPU response for ReadMem: {:?}", s);
264                 Err(NonFatal)
265             }
266             Err(e) => {
267                 error!("Failed to request ReadMem: {}", e);
268                 Err(NonFatal)
269             }
270         }
271     }
272 
write_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &[u8], ) -> TargetResult<(), Self>273     fn write_addrs(
274         &mut self,
275         start_addr: <Self::Arch as Arch>::Usize,
276         data: &[u8],
277     ) -> TargetResult<(), Self> {
278         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::WriteMem(
279             GuestAddress(start_addr),
280             data.to_owned(),
281         ))) {
282             Ok(VcpuDebugStatus::CommandComplete) => Ok(()),
283             Ok(s) => {
284                 error!("Unexpected vCPU response for WriteMem: {:?}", s);
285                 Err(NonFatal)
286             }
287             Err(e) => {
288                 error!("Failed to request WriteMem: {}", e);
289                 Err(NonFatal)
290             }
291         }
292     }
293 
294     #[inline(always)]
support_resume(&mut self) -> Option<SingleThreadResumeOps<Self>>295     fn support_resume(&mut self) -> Option<SingleThreadResumeOps<Self>> {
296         Some(self)
297     }
298 
299     #[cfg(target_arch = "aarch64")]
300     #[inline(always)]
support_single_register_access(&mut self) -> Option<SingleRegisterAccessOps<(), Self>>301     fn support_single_register_access(&mut self) -> Option<SingleRegisterAccessOps<(), Self>> {
302         Some(self)
303     }
304 }
305 
306 impl SingleThreadResume for GdbStub {
resume(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error>307     fn resume(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
308         // TODO: Handle any incoming signal.
309 
310         self.vm_request(VmRequest::Resume).map_err(|e| {
311             error!("Failed to resume the target: {}", e);
312             "Failed to resume the target"
313         })
314     }
315 
316     #[inline(always)]
support_single_step(&mut self) -> Option<SingleThreadSingleStepOps<'_, Self>>317     fn support_single_step(&mut self) -> Option<SingleThreadSingleStepOps<'_, Self>> {
318         Some(self)
319     }
320 }
321 
322 impl SingleThreadSingleStep for GdbStub {
step(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error>323     fn step(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
324         // TODO: Handle any incoming signal.
325 
326         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::EnableSinglestep)) {
327             Ok(VcpuDebugStatus::CommandComplete) => {
328                 self.single_step = true;
329             }
330             Ok(s) => {
331                 error!("Unexpected vCPU response for EnableSinglestep: {:?}", s);
332                 return Err("Unexpected vCPU response for EnableSinglestep");
333             }
334             Err(e) => {
335                 error!("Failed to request EnableSinglestep: {}", e);
336                 return Err("Failed to request EnableSinglestep");
337             }
338         };
339 
340         self.vm_request(VmRequest::Resume).map_err(|e| {
341             error!("Failed to resume the target: {}", e);
342             "Failed to resume the target"
343         })?;
344 
345         Ok(())
346     }
347 }
348 
349 impl Breakpoints for GdbStub {
support_hw_breakpoint(&mut self) -> Option<HwBreakpointOps<Self>>350     fn support_hw_breakpoint(&mut self) -> Option<HwBreakpointOps<Self>> {
351         Some(self)
352     }
353 }
354 
355 impl HwBreakpoint for GdbStub {
356     /// Add a new hardware breakpoint.
357     /// Return `Ok(false)` if the operation could not be completed.
add_hw_breakpoint( &mut self, addr: <Self::Arch as Arch>::Usize, _kind: <Self::Arch as Arch>::BreakpointKind, ) -> TargetResult<bool, Self>358     fn add_hw_breakpoint(
359         &mut self,
360         addr: <Self::Arch as Arch>::Usize,
361         _kind: <Self::Arch as Arch>::BreakpointKind,
362     ) -> TargetResult<bool, Self> {
363         let max_count = *(match &mut self.max_hw_breakpoints {
364             None => self
365                 .max_hw_breakpoints
366                 .insert(self.max_hw_breakpoints_request()?),
367             Some(c) => c,
368         });
369         if self.hw_breakpoints.len() >= max_count {
370             error!("Not allowed to set more than {} HW breakpoints", max_count);
371             return Err(NonFatal);
372         }
373         self.hw_breakpoints.push(GuestAddress(addr));
374 
375         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::SetHwBreakPoint(
376             self.hw_breakpoints.clone(),
377         ))) {
378             Ok(VcpuDebugStatus::CommandComplete) => Ok(true),
379             Ok(s) => {
380                 error!("Unexpected vCPU response for SetHwBreakPoint: {:?}", s);
381                 Err(NonFatal)
382             }
383             Err(e) => {
384                 error!("Failed to request SetHwBreakPoint: {}", e);
385                 Err(NonFatal)
386             }
387         }
388     }
389 
390     /// Remove an existing hardware breakpoint.
391     /// Return `Ok(false)` if the operation could not be completed.
remove_hw_breakpoint( &mut self, addr: <Self::Arch as Arch>::Usize, _kind: <Self::Arch as Arch>::BreakpointKind, ) -> TargetResult<bool, Self>392     fn remove_hw_breakpoint(
393         &mut self,
394         addr: <Self::Arch as Arch>::Usize,
395         _kind: <Self::Arch as Arch>::BreakpointKind,
396     ) -> TargetResult<bool, Self> {
397         self.hw_breakpoints.retain(|&b| b.0 != addr);
398 
399         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::SetHwBreakPoint(
400             self.hw_breakpoints.clone(),
401         ))) {
402             Ok(VcpuDebugStatus::CommandComplete) => Ok(true),
403             Ok(s) => {
404                 error!("Unexpected vCPU response for SetHwBreakPoint: {:?}", s);
405                 Err(NonFatal)
406             }
407             Err(e) => {
408                 error!("Failed to request SetHwBreakPoint: {}", e);
409                 Err(NonFatal)
410             }
411         }
412     }
413 }
414 
415 impl SingleRegisterAccess<()> for GdbStub {
read_register( &mut self, _tid: (), reg_id: <Self::Arch as Arch>::RegId, buf: &mut [u8], ) -> TargetResult<usize, Self>416     fn read_register(
417         &mut self,
418         _tid: (),
419         reg_id: <Self::Arch as Arch>::RegId,
420         buf: &mut [u8],
421     ) -> TargetResult<usize, Self> {
422         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::ReadReg(reg_id))) {
423             Ok(VcpuDebugStatus::RegValue(r)) => {
424                 if buf.len() != r.len() {
425                     error!(
426                         "Register size mismatch in RegValue: {} != {}",
427                         buf.len(),
428                         r.len()
429                     );
430                     return Err(NonFatal);
431                 }
432                 for (dst, v) in buf.iter_mut().zip(r.iter()) {
433                     *dst = *v;
434                 }
435                 Ok(r.len())
436             }
437             Ok(s) => {
438                 error!("Unexpected vCPU response for ReadReg: {:?}", s);
439                 Err(NonFatal)
440             }
441             Err(e) => {
442                 error!("Failed to request ReadReg: {}", e);
443                 Err(NonFatal)
444             }
445         }
446     }
447 
write_register( &mut self, _tid: (), reg_id: <Self::Arch as Arch>::RegId, val: &[u8], ) -> TargetResult<(), Self>448     fn write_register(
449         &mut self,
450         _tid: (),
451         reg_id: <Self::Arch as Arch>::RegId,
452         val: &[u8],
453     ) -> TargetResult<(), Self> {
454         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::WriteReg(
455             reg_id,
456             val.to_owned(),
457         ))) {
458             Ok(VcpuDebugStatus::CommandComplete) => Ok(()),
459             Ok(s) => {
460                 error!("Unexpected vCPU response for WriteReg: {:?}", s);
461                 Err(NonFatal)
462             }
463             Err(e) => {
464                 error!("Failed to request WriteReg: {}", e);
465                 Err(NonFatal)
466             }
467         }
468     }
469 }
470 
471 struct GdbStubEventLoop;
472 
473 impl BlockingEventLoop for GdbStubEventLoop {
474     type Target = GdbStub;
475     type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>;
476     type StopReason = SingleThreadStopReason<<GdbArch as Arch>::Usize>;
477 
wait_for_stop_reason( target: &mut Self::Target, conn: &mut Self::Connection, ) -> Result< run_blocking::Event<Self::StopReason>, run_blocking::WaitForStopReasonError< <Self::Target as Target>::Error, <Self::Connection as Connection>::Error, >, >478     fn wait_for_stop_reason(
479         target: &mut Self::Target,
480         conn: &mut Self::Connection,
481     ) -> Result<
482         run_blocking::Event<Self::StopReason>,
483         run_blocking::WaitForStopReasonError<
484             <Self::Target as Target>::Error,
485             <Self::Connection as Connection>::Error,
486         >,
487     > {
488         loop {
489             // TODO(keiichiw): handle error?
490             if let Ok(msg) = target
491                 .from_vcpu
492                 .recv_timeout(std::time::Duration::from_millis(100))
493             {
494                 match msg.msg {
495                     VcpuDebugStatus::HitBreakPoint => {
496                         if target.single_step {
497                             target.single_step = false;
498                             return Ok(run_blocking::Event::TargetStopped(
499                                 SingleThreadStopReason::DoneStep,
500                             ));
501                         } else {
502                             return Ok(run_blocking::Event::TargetStopped(
503                                 SingleThreadStopReason::HwBreak(()),
504                             ));
505                         }
506                     }
507                     status => {
508                         error!("Unexpected VcpuDebugStatus: {:?}", status);
509                     }
510                 }
511             }
512 
513             // If no message was received within the timeout check for incoming data from
514             // the GDB client.
515             if conn
516                 .peek()
517                 .map_err(run_blocking::WaitForStopReasonError::Connection)?
518                 .is_some()
519             {
520                 let byte = conn
521                     .read()
522                     .map_err(run_blocking::WaitForStopReasonError::Connection)?;
523                 return Ok(run_blocking::Event::IncomingData(byte));
524             }
525         }
526     }
527 
on_interrupt( target: &mut Self::Target, ) -> Result<Option<Self::StopReason>, <Self::Target as Target>::Error>528     fn on_interrupt(
529         target: &mut Self::Target,
530     ) -> Result<Option<Self::StopReason>, <Self::Target as Target>::Error> {
531         target.vm_request(VmRequest::Suspend).map_err(|e| {
532             error!("Failed to suspend the target: {}", e);
533             "Failed to suspend the target"
534         })?;
535 
536         Ok(Some(SingleThreadStopReason::Signal(Signal::SIGINT)))
537     }
538 }
539 
540 /// Notify the GDB thread that a VCPU has stopped because of a breakpoint.
vcpu_exit_debug( cpu: usize, to_gdb_tube: Option<&mpsc::Sender<VcpuDebugStatusMessage>>, ) -> anyhow::Result<()>541 pub fn vcpu_exit_debug(
542     cpu: usize,
543     to_gdb_tube: Option<&mpsc::Sender<VcpuDebugStatusMessage>>,
544 ) -> anyhow::Result<()> {
545     if let Some(ch) = to_gdb_tube.as_ref() {
546         ch.send(VcpuDebugStatusMessage {
547             cpu,
548             msg: VcpuDebugStatus::HitBreakPoint,
549         })
550         .context("failed to send breakpoint status to gdb thread")?;
551     }
552     Ok(())
553 }
554 
555 /// Handle a `VcpuDebug` request for a given `vcpu`.
vcpu_control_debug<V>( cpu_id: usize, vcpu: &V, guest_mem: &GuestMemory, d: VcpuDebug, reply_tube: Option<&mpsc::Sender<VcpuDebugStatusMessage>>, ) -> anyhow::Result<()> where V: VcpuArch + 'static,556 pub fn vcpu_control_debug<V>(
557     cpu_id: usize,
558     vcpu: &V,
559     guest_mem: &GuestMemory,
560     d: VcpuDebug,
561     reply_tube: Option<&mpsc::Sender<VcpuDebugStatusMessage>>,
562 ) -> anyhow::Result<()>
563 where
564     V: VcpuArch + 'static,
565 {
566     let reply_tube = reply_tube
567         .as_ref()
568         .context("VcpuControl::Debug received while debugger not connected")?;
569 
570     let debug_status = match d {
571         VcpuDebug::ReadRegs => VcpuDebugStatus::RegValues(
572             <CrosvmArch as arch::GdbOps<V>>::read_registers(vcpu as &V)
573                 .context("failed to handle a gdb ReadRegs command")?,
574         ),
575         VcpuDebug::WriteRegs(regs) => {
576             <CrosvmArch as arch::GdbOps<V>>::write_registers(vcpu as &V, &regs)
577                 .context("failed to handle a gdb WriteRegs command")?;
578             VcpuDebugStatus::CommandComplete
579         }
580         VcpuDebug::ReadReg(reg) => VcpuDebugStatus::RegValue(
581             <CrosvmArch as arch::GdbOps<V>>::read_register(vcpu as &V, reg)
582                 .context("failed to handle a gdb ReadReg command")?,
583         ),
584         VcpuDebug::WriteReg(reg, buf) => {
585             <CrosvmArch as arch::GdbOps<V>>::write_register(vcpu as &V, reg, &buf)
586                 .context("failed to handle a gdb WriteReg command")?;
587             VcpuDebugStatus::CommandComplete
588         }
589         VcpuDebug::ReadMem(vaddr, len) => VcpuDebugStatus::MemoryRegion(
590             <CrosvmArch as arch::GdbOps<V>>::read_memory(vcpu as &V, guest_mem, vaddr, len)
591                 .unwrap_or(Vec::new()),
592         ),
593         VcpuDebug::WriteMem(vaddr, buf) => {
594             <CrosvmArch as arch::GdbOps<V>>::write_memory(vcpu as &V, guest_mem, vaddr, &buf)
595                 .context("failed to handle a gdb WriteMem command")?;
596             VcpuDebugStatus::CommandComplete
597         }
598         VcpuDebug::EnableSinglestep => {
599             <CrosvmArch as arch::GdbOps<V>>::enable_singlestep(vcpu as &V)
600                 .context("failed to handle a gdb EnableSingleStep command")?;
601             VcpuDebugStatus::CommandComplete
602         }
603         VcpuDebug::GetHwBreakPointCount => VcpuDebugStatus::HwBreakPointCount(
604             <CrosvmArch as arch::GdbOps<V>>::get_max_hw_breakpoints(vcpu as &V)
605                 .context("failed to get max number of HW breakpoints")?,
606         ),
607         VcpuDebug::SetHwBreakPoint(addrs) => {
608             <CrosvmArch as arch::GdbOps<V>>::set_hw_breakpoints(vcpu as &V, &addrs)
609                 .context("failed to handle a gdb SetHwBreakPoint command")?;
610             VcpuDebugStatus::CommandComplete
611         }
612     };
613 
614     reply_tube
615         .send(VcpuDebugStatusMessage {
616             cpu: cpu_id,
617             msg: debug_status,
618         })
619         .context("failed to send a debug status to GDB thread")
620 }
621