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