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