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, ®s)
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