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