• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
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 use base::{error, info, Tube, TubeError};
10 
11 use sync::Mutex;
12 use vm_control::{
13     VcpuControl, VcpuDebug, VcpuDebugStatus, VcpuDebugStatusMessage, VmRequest, VmResponse,
14 };
15 use vm_memory::GuestAddress;
16 
17 use gdbstub::arch::Arch;
18 use gdbstub::target::ext::base::singlethread::{ResumeAction, SingleThreadOps, StopReason};
19 use gdbstub::target::ext::base::{BaseOps, GdbInterrupt};
20 use gdbstub::target::ext::breakpoints::{
21     Breakpoints, BreakpointsOps, HwBreakpoint, HwBreakpointOps,
22 };
23 use gdbstub::target::TargetError::NonFatal;
24 use gdbstub::target::{Target, TargetResult};
25 use gdbstub::Connection;
26 #[cfg(target_arch = "x86_64")]
27 use gdbstub_arch::x86::X86_64_SSE as GdbArch;
28 use remain::sorted;
29 use thiserror::Error as ThisError;
30 
31 #[cfg(target_arch = "x86_64")]
32 type ArchUsize = u64;
33 
gdb_thread(mut gdbstub: GdbStub, port: u32)34 pub fn gdb_thread(mut gdbstub: GdbStub, port: u32) {
35     let addr = format!("0.0.0.0:{}", port);
36     let listener = match TcpListener::bind(addr.clone()) {
37         Ok(s) => s,
38         Err(e) => {
39             error!("Failed to create a TCP listener: {}", e);
40             return;
41         }
42     };
43     info!("Waiting for a GDB connection on {:?}...", addr);
44 
45     let (stream, addr) = match listener.accept() {
46         Ok(v) => v,
47         Err(e) => {
48             error!("Failed to accept a connection from GDB: {}", e);
49             return;
50         }
51     };
52     info!("GDB connected from {}", addr);
53 
54     let connection: Box<dyn Connection<Error = std::io::Error>> = Box::new(stream);
55     let mut gdb = gdbstub::GdbStub::new(connection);
56 
57     match gdb.run(&mut gdbstub) {
58         Ok(reason) => {
59             info!("GDB session closed: {:?}", reason);
60         }
61         Err(e) => {
62             error!("error occurred in GDB session: {}", e);
63         }
64     }
65 
66     // Resume the VM when GDB session is disconnected.
67     if let Err(e) = gdbstub.vm_request(VmRequest::Resume) {
68         error!("Failed to resume the VM after GDB disconnected: {}", e);
69     }
70 }
71 
72 #[sorted]
73 #[derive(ThisError, Debug)]
74 enum Error {
75     /// Got an unexpected VM response.
76     #[error("Got an unexpected VM response: {0}")]
77     UnexpectedVmResponse(VmResponse),
78     /// Failed to send a vCPU request.
79     #[error("failed to send a vCPU request: {0}")]
80     VcpuRequest(mpsc::SendError<VcpuControl>),
81     /// Failed to receive a vCPU response.
82     #[error("failed to receive a vCPU response: {0}")]
83     VcpuResponse(mpsc::RecvTimeoutError),
84     /// Failed to send a VM request.
85     #[error("failed to send a VM request: {0}")]
86     VmRequest(TubeError),
87     /// Failed to receive a VM request.
88     #[error("failed to receive a VM response: {0}")]
89     VmResponse(TubeError),
90 }
91 type GdbResult<T> = std::result::Result<T, Error>;
92 
93 pub struct GdbStub {
94     vm_tube: Mutex<Tube>,
95     vcpu_com: Vec<mpsc::Sender<VcpuControl>>,
96     from_vcpu: mpsc::Receiver<VcpuDebugStatusMessage>,
97 
98     hw_breakpoints: Vec<GuestAddress>,
99 }
100 
101 impl GdbStub {
new( vm_tube: Tube, vcpu_com: Vec<mpsc::Sender<VcpuControl>>, from_vcpu: mpsc::Receiver<VcpuDebugStatusMessage>, ) -> Self102     pub fn new(
103         vm_tube: Tube,
104         vcpu_com: Vec<mpsc::Sender<VcpuControl>>,
105         from_vcpu: mpsc::Receiver<VcpuDebugStatusMessage>,
106     ) -> Self {
107         GdbStub {
108             vm_tube: Mutex::new(vm_tube),
109             vcpu_com,
110             from_vcpu,
111             hw_breakpoints: Default::default(),
112         }
113     }
114 
vcpu_request(&self, request: VcpuControl) -> GdbResult<VcpuDebugStatus>115     fn vcpu_request(&self, request: VcpuControl) -> GdbResult<VcpuDebugStatus> {
116         // We use the only one vCPU when GDB is enabled.
117         self.vcpu_com[0].send(request).map_err(Error::VcpuRequest)?;
118 
119         match self.from_vcpu.recv_timeout(Duration::from_millis(500)) {
120             Ok(msg) => Ok(msg.msg),
121             Err(e) => Err(Error::VcpuResponse(e)),
122         }
123     }
124 
vm_request(&self, request: VmRequest) -> GdbResult<()>125     fn vm_request(&self, request: VmRequest) -> GdbResult<()> {
126         let vm_tube = self.vm_tube.lock();
127         vm_tube.send(&request).map_err(Error::VmRequest)?;
128         match vm_tube.recv() {
129             Ok(VmResponse::Ok) => Ok(()),
130             Ok(r) => Err(Error::UnexpectedVmResponse(r)),
131             Err(e) => Err(Error::VmResponse(e)),
132         }
133     }
134 }
135 
136 impl Target for GdbStub {
137     // TODO(keiichiw): Replace `()` with `X86_64CoreRegId` when we update the gdbstub crate.
138     type Arch = GdbArch<()>;
139     type Error = &'static str;
140 
base_ops(&mut self) -> BaseOps<Self::Arch, Self::Error>141     fn base_ops(&mut self) -> BaseOps<Self::Arch, Self::Error> {
142         BaseOps::SingleThread(self)
143     }
144 
145     // TODO(keiichiw): sw_breakpoint, hw_watchpoint, extended_mode, monitor_cmd, section_offsets
breakpoints(&mut self) -> Option<BreakpointsOps<Self>>146     fn breakpoints(&mut self) -> Option<BreakpointsOps<Self>> {
147         Some(self)
148     }
149 }
150 
151 impl SingleThreadOps for GdbStub {
resume( &mut self, action: ResumeAction, check_gdb_interrupt: GdbInterrupt, ) -> Result<StopReason<ArchUsize>, Self::Error>152     fn resume(
153         &mut self,
154         action: ResumeAction,
155         check_gdb_interrupt: GdbInterrupt,
156     ) -> Result<StopReason<ArchUsize>, Self::Error> {
157         let single_step = ResumeAction::Step == action;
158 
159         if single_step {
160             match self.vcpu_request(VcpuControl::Debug(VcpuDebug::EnableSinglestep)) {
161                 Ok(VcpuDebugStatus::CommandComplete) => {}
162                 Ok(s) => {
163                     error!("Unexpected vCPU response for EnableSinglestep: {:?}", s);
164                     return Err("Unexpected vCPU response for EnableSinglestep");
165                 }
166                 Err(e) => {
167                     error!("Failed to request EnableSinglestep: {}", e);
168                     return Err("Failed to request EnableSinglestep");
169                 }
170             };
171         }
172 
173         self.vm_request(VmRequest::Resume).map_err(|e| {
174             error!("Failed to resume the target: {}", e);
175             "Failed to resume the target"
176         })?;
177 
178         let mut check_gdb_interrupt = check_gdb_interrupt.no_async();
179         // Polling
180         loop {
181             // TODO(keiichiw): handle error?
182             if let Ok(msg) = self
183                 .from_vcpu
184                 .recv_timeout(std::time::Duration::from_millis(100))
185             {
186                 match msg.msg {
187                     VcpuDebugStatus::HitBreakPoint => {
188                         if single_step {
189                             return Ok(StopReason::DoneStep);
190                         } else {
191                             return Ok(StopReason::HwBreak);
192                         }
193                     }
194                     status => {
195                         error!("Unexpected VcpuDebugStatus: {:?}", status);
196                     }
197                 }
198             }
199 
200             if check_gdb_interrupt.pending() {
201                 self.vm_request(VmRequest::Suspend).map_err(|e| {
202                     error!("Failed to suspend the target: {}", e);
203                     "Failed to suspend the target"
204                 })?;
205                 return Ok(StopReason::GdbInterrupt);
206             }
207         }
208     }
209 
read_registers( &mut self, regs: &mut <Self::Arch as Arch>::Registers, ) -> TargetResult<(), Self>210     fn read_registers(
211         &mut self,
212         regs: &mut <Self::Arch as Arch>::Registers,
213     ) -> TargetResult<(), Self> {
214         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::ReadRegs)) {
215             Ok(VcpuDebugStatus::RegValues(r)) => {
216                 *regs = r;
217                 Ok(())
218             }
219             Ok(s) => {
220                 error!("Unexpected vCPU response for ReadRegs: {:?}", s);
221                 Err(NonFatal)
222             }
223             Err(e) => {
224                 error!("Failed to request ReadRegs: {}", e);
225                 Err(NonFatal)
226             }
227         }
228     }
229 
write_registers( &mut self, regs: &<Self::Arch as Arch>::Registers, ) -> TargetResult<(), Self>230     fn write_registers(
231         &mut self,
232         regs: &<Self::Arch as Arch>::Registers,
233     ) -> TargetResult<(), Self> {
234         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::WriteRegs(Box::new(
235             regs.clone(),
236         )))) {
237             Ok(VcpuDebugStatus::CommandComplete) => Ok(()),
238             Ok(s) => {
239                 error!("Unexpected vCPU response for WriteRegs: {:?}", s);
240                 Err(NonFatal)
241             }
242             Err(e) => {
243                 error!("Failed to request WriteRegs: {}", e);
244                 Err(NonFatal)
245             }
246         }
247     }
248 
read_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &mut [u8], ) -> TargetResult<(), Self>249     fn read_addrs(
250         &mut self,
251         start_addr: <Self::Arch as Arch>::Usize,
252         data: &mut [u8],
253     ) -> TargetResult<(), Self> {
254         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::ReadMem(
255             GuestAddress(start_addr),
256             data.len(),
257         ))) {
258             Ok(VcpuDebugStatus::MemoryRegion(r)) => {
259                 for (dst, v) in data.iter_mut().zip(r.iter()) {
260                     *dst = *v;
261                 }
262                 Ok(())
263             }
264             Ok(s) => {
265                 error!("Unexpected vCPU response for ReadMem: {:?}", s);
266                 Err(NonFatal)
267             }
268             Err(e) => {
269                 error!("Failed to request ReadMem: {}", e);
270                 Err(NonFatal)
271             }
272         }
273     }
274 
write_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &[u8], ) -> TargetResult<(), Self>275     fn write_addrs(
276         &mut self,
277         start_addr: <Self::Arch as Arch>::Usize,
278         data: &[u8],
279     ) -> TargetResult<(), Self> {
280         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::WriteMem(
281             GuestAddress(start_addr),
282             data.to_owned(),
283         ))) {
284             Ok(VcpuDebugStatus::CommandComplete) => Ok(()),
285             Ok(s) => {
286                 error!("Unexpected vCPU response for WriteMem: {:?}", s);
287                 Err(NonFatal)
288             }
289             Err(e) => {
290                 error!("Failed to request WriteMem: {}", e);
291                 Err(NonFatal)
292             }
293         }
294     }
295 }
296 
297 impl Breakpoints for GdbStub {
hw_breakpoint(&mut self) -> Option<HwBreakpointOps<Self>>298     fn hw_breakpoint(&mut self) -> Option<HwBreakpointOps<Self>> {
299         Some(self)
300     }
301 }
302 
303 impl HwBreakpoint for GdbStub {
304     /// Add a new hardware breakpoint.
305     /// 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>306     fn add_hw_breakpoint(
307         &mut self,
308         addr: <Self::Arch as Arch>::Usize,
309         _kind: <Self::Arch as Arch>::BreakpointKind,
310     ) -> TargetResult<bool, Self> {
311         // If we already have 4 breakpoints, we cannot set a new one.
312         if self.hw_breakpoints.len() >= 4 {
313             error!("Not allowed to set more than 4 HW breakpoints");
314             return Err(NonFatal);
315         }
316         self.hw_breakpoints.push(GuestAddress(addr));
317 
318         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::SetHwBreakPoint(
319             self.hw_breakpoints.clone(),
320         ))) {
321             Ok(VcpuDebugStatus::CommandComplete) => Ok(true),
322             Ok(s) => {
323                 error!("Unexpected vCPU response for SetHwBreakPoint: {:?}", s);
324                 Err(NonFatal)
325             }
326             Err(e) => {
327                 error!("Failed to request SetHwBreakPoint: {}", e);
328                 Err(NonFatal)
329             }
330         }
331     }
332 
333     /// Remove an existing hardware breakpoint.
334     /// 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>335     fn remove_hw_breakpoint(
336         &mut self,
337         addr: <Self::Arch as Arch>::Usize,
338         _kind: <Self::Arch as Arch>::BreakpointKind,
339     ) -> TargetResult<bool, Self> {
340         self.hw_breakpoints.retain(|&b| b.0 != addr);
341 
342         match self.vcpu_request(VcpuControl::Debug(VcpuDebug::SetHwBreakPoint(
343             self.hw_breakpoints.clone(),
344         ))) {
345             Ok(VcpuDebugStatus::CommandComplete) => Ok(true),
346             Ok(s) => {
347                 error!("Unexpected vCPU response for SetHwBreakPoint: {:?}", s);
348                 Err(NonFatal)
349             }
350             Err(e) => {
351                 error!("Failed to request SetHwBreakPoint: {}", e);
352                 Err(NonFatal)
353             }
354         }
355     }
356 }
357