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