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