1 // Copyright 2022 Akira Moroo. 2 // Portions Copyright 2020 The Chromium OS Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE-BSD-3-Clause file. 5 // 6 // SPDX-License-Identifier: BSD-3-Clause 7 8 use std::{os::unix::net::UnixListener, sync::mpsc}; 9 10 use gdbstub::{ 11 arch::Arch, 12 common::{Signal, Tid}, 13 conn::{Connection, ConnectionExt}, 14 stub::{run_blocking, DisconnectReason, MultiThreadStopReason}, 15 target::{ 16 ext::{ 17 base::{ 18 multithread::{ 19 MultiThreadBase, MultiThreadResume, MultiThreadResumeOps, 20 MultiThreadSingleStep, MultiThreadSingleStepOps, 21 }, 22 BaseOps, 23 }, 24 breakpoints::{Breakpoints, BreakpointsOps, HwBreakpoint, HwBreakpointOps}, 25 }, 26 Target, TargetError, TargetResult, 27 }, 28 }; 29 #[cfg(target_arch = "x86_64")] 30 use gdbstub_arch::x86::reg::X86_64CoreRegs as CoreRegs; 31 #[cfg(target_arch = "x86_64")] 32 use gdbstub_arch::x86::X86_64_SSE as GdbArch; 33 use vm_memory::GuestAddress; 34 35 #[cfg(target_arch = "x86_64")] 36 type ArchUsize = u64; 37 38 #[derive(Debug)] 39 pub enum DebuggableError { 40 SetDebug(hypervisor::HypervisorCpuError), 41 Pause(vm_migration::MigratableError), 42 Resume(vm_migration::MigratableError), 43 ReadRegs(crate::cpu::Error), 44 WriteRegs(crate::cpu::Error), 45 ReadMem(hypervisor::HypervisorVmError), 46 WriteMem(hypervisor::HypervisorVmError), 47 TranslateGva(crate::cpu::Error), 48 PoisonedState, 49 } 50 51 pub trait Debuggable: vm_migration::Pausable { 52 fn set_guest_debug( 53 &self, 54 cpu_id: usize, 55 addrs: &[GuestAddress], 56 singlestep: bool, 57 ) -> Result<(), DebuggableError>; 58 fn debug_pause(&mut self) -> std::result::Result<(), DebuggableError>; 59 fn debug_resume(&mut self) -> std::result::Result<(), DebuggableError>; 60 fn read_regs(&self, cpu_id: usize) -> std::result::Result<CoreRegs, DebuggableError>; 61 fn write_regs( 62 &self, 63 cpu_id: usize, 64 regs: &CoreRegs, 65 ) -> std::result::Result<(), DebuggableError>; 66 fn read_mem( 67 &self, 68 cpu_id: usize, 69 vaddr: GuestAddress, 70 len: usize, 71 ) -> std::result::Result<Vec<u8>, DebuggableError>; 72 fn write_mem( 73 &self, 74 cpu_id: usize, 75 vaddr: &GuestAddress, 76 data: &[u8], 77 ) -> std::result::Result<(), DebuggableError>; 78 fn active_vcpus(&self) -> usize; 79 } 80 81 #[derive(Debug)] 82 pub enum Error { 83 Vm(crate::vm::Error), 84 GdbRequest, 85 GdbResponseNotify(std::io::Error), 86 GdbResponse(mpsc::RecvError), 87 GdbResponseTimeout(mpsc::RecvTimeoutError), 88 } 89 type GdbResult<T> = std::result::Result<T, Error>; 90 91 #[derive(Debug)] 92 pub struct GdbRequest { 93 pub sender: mpsc::Sender<GdbResponse>, 94 pub payload: GdbRequestPayload, 95 pub cpu_id: usize, 96 } 97 98 #[derive(Debug)] 99 pub enum GdbRequestPayload { 100 ReadRegs, 101 WriteRegs(Box<CoreRegs>), 102 ReadMem(GuestAddress, usize), 103 WriteMem(GuestAddress, Vec<u8>), 104 Pause, 105 Resume, 106 SetSingleStep(bool), 107 SetHwBreakPoint(Vec<GuestAddress>), 108 ActiveVcpus, 109 } 110 111 pub type GdbResponse = std::result::Result<GdbResponsePayload, Error>; 112 113 #[derive(Debug)] 114 pub enum GdbResponsePayload { 115 CommandComplete, 116 RegValues(Box<CoreRegs>), 117 MemoryRegion(Vec<u8>), 118 ActiveVcpus(usize), 119 } 120 121 pub struct GdbStub { 122 gdb_sender: mpsc::Sender<GdbRequest>, 123 gdb_event: vmm_sys_util::eventfd::EventFd, 124 vm_event: vmm_sys_util::eventfd::EventFd, 125 126 hw_breakpoints: Vec<GuestAddress>, 127 single_step: bool, 128 } 129 130 impl GdbStub { 131 pub fn new( 132 gdb_sender: mpsc::Sender<GdbRequest>, 133 gdb_event: vmm_sys_util::eventfd::EventFd, 134 vm_event: vmm_sys_util::eventfd::EventFd, 135 ) -> Self { 136 Self { 137 gdb_sender, 138 gdb_event, 139 vm_event, 140 hw_breakpoints: Default::default(), 141 single_step: false, 142 } 143 } 144 145 fn vm_request( 146 &self, 147 payload: GdbRequestPayload, 148 cpu_id: usize, 149 ) -> GdbResult<GdbResponsePayload> { 150 let (response_sender, response_receiver) = std::sync::mpsc::channel(); 151 let request = GdbRequest { 152 sender: response_sender, 153 payload, 154 cpu_id, 155 }; 156 self.gdb_sender 157 .send(request) 158 .map_err(|_| Error::GdbRequest)?; 159 self.gdb_event.write(1).map_err(Error::GdbResponseNotify)?; 160 let res = response_receiver.recv().map_err(Error::GdbResponse)??; 161 Ok(res) 162 } 163 } 164 165 impl Target for GdbStub { 166 type Arch = GdbArch; 167 type Error = String; 168 169 #[inline(always)] 170 fn base_ops(&mut self) -> BaseOps<Self::Arch, Self::Error> { 171 BaseOps::MultiThread(self) 172 } 173 174 #[inline(always)] 175 fn support_breakpoints(&mut self) -> Option<BreakpointsOps<Self>> { 176 Some(self) 177 } 178 179 #[inline(always)] 180 fn guard_rail_implicit_sw_breakpoints(&self) -> bool { 181 true 182 } 183 } 184 185 fn tid_to_cpuid(tid: Tid) -> usize { 186 tid.get() - 1 187 } 188 189 fn cpuid_to_tid(cpu_id: usize) -> Tid { 190 Tid::new(get_raw_tid(cpu_id)).unwrap() 191 } 192 193 pub fn get_raw_tid(cpu_id: usize) -> usize { 194 cpu_id + 1 195 } 196 197 impl MultiThreadBase for GdbStub { 198 fn read_registers( 199 &mut self, 200 regs: &mut <Self::Arch as Arch>::Registers, 201 tid: Tid, 202 ) -> TargetResult<(), Self> { 203 match self.vm_request(GdbRequestPayload::ReadRegs, tid_to_cpuid(tid)) { 204 Ok(GdbResponsePayload::RegValues(r)) => { 205 *regs = *r; 206 Ok(()) 207 } 208 Ok(s) => { 209 error!("Unexpected response for ReadRegs: {:?}", s); 210 Err(TargetError::NonFatal) 211 } 212 Err(e) => { 213 error!("Failed to request ReadRegs: {:?}", e); 214 Err(TargetError::NonFatal) 215 } 216 } 217 } 218 219 fn write_registers( 220 &mut self, 221 regs: &<Self::Arch as Arch>::Registers, 222 tid: Tid, 223 ) -> TargetResult<(), Self> { 224 match self.vm_request( 225 GdbRequestPayload::WriteRegs(Box::new(regs.clone())), 226 tid_to_cpuid(tid), 227 ) { 228 Ok(_) => Ok(()), 229 Err(e) => { 230 error!("Failed to request WriteRegs: {:?}", e); 231 Err(TargetError::NonFatal) 232 } 233 } 234 } 235 236 fn read_addrs( 237 &mut self, 238 start_addr: <Self::Arch as Arch>::Usize, 239 data: &mut [u8], 240 tid: Tid, 241 ) -> TargetResult<(), Self> { 242 match self.vm_request( 243 GdbRequestPayload::ReadMem(GuestAddress(start_addr), data.len()), 244 tid_to_cpuid(tid), 245 ) { 246 Ok(GdbResponsePayload::MemoryRegion(r)) => { 247 for (dst, v) in data.iter_mut().zip(r.iter()) { 248 *dst = *v; 249 } 250 Ok(()) 251 } 252 Ok(s) => { 253 error!("Unexpected response for ReadMem: {:?}", s); 254 Err(TargetError::NonFatal) 255 } 256 Err(e) => { 257 error!("Failed to request ReadMem: {:?}", e); 258 Err(TargetError::NonFatal) 259 } 260 } 261 } 262 263 fn write_addrs( 264 &mut self, 265 start_addr: <Self::Arch as Arch>::Usize, 266 data: &[u8], 267 tid: Tid, 268 ) -> TargetResult<(), Self> { 269 match self.vm_request( 270 GdbRequestPayload::WriteMem(GuestAddress(start_addr), data.to_owned()), 271 tid_to_cpuid(tid), 272 ) { 273 Ok(_) => Ok(()), 274 Err(e) => { 275 error!("Failed to request WriteMem: {:?}", e); 276 Err(TargetError::NonFatal) 277 } 278 } 279 } 280 281 fn list_active_threads( 282 &mut self, 283 thread_is_active: &mut dyn FnMut(Tid), 284 ) -> Result<(), Self::Error> { 285 match self.vm_request(GdbRequestPayload::ActiveVcpus, 0) { 286 Ok(GdbResponsePayload::ActiveVcpus(active_vcpus)) => { 287 (0..active_vcpus).for_each(|cpu_id| { 288 thread_is_active(cpuid_to_tid(cpu_id)); 289 }); 290 Ok(()) 291 } 292 Ok(s) => Err(format!("Unexpected response for ActiveVcpus: {:?}", s)), 293 Err(e) => Err(format!("Failed to request ActiveVcpus: {:?}", e)), 294 } 295 } 296 297 #[inline(always)] 298 fn support_resume(&mut self) -> Option<MultiThreadResumeOps<'_, Self>> { 299 Some(self) 300 } 301 } 302 303 impl MultiThreadResume for GdbStub { 304 fn resume(&mut self) -> Result<(), Self::Error> { 305 match self.vm_request(GdbRequestPayload::Resume, 0) { 306 Ok(_) => Ok(()), 307 Err(e) => Err(format!("Failed to resume the target: {:?}", e)), 308 } 309 } 310 311 fn clear_resume_actions(&mut self) -> Result<(), Self::Error> { 312 if self.single_step { 313 match self.vm_request(GdbRequestPayload::SetSingleStep(false), 0) { 314 Ok(_) => { 315 self.single_step = false; 316 } 317 Err(e) => { 318 return Err(format!("Failed to request SetSingleStep: {:?}", e)); 319 } 320 } 321 } 322 Ok(()) 323 } 324 325 fn set_resume_action_continue( 326 &mut self, 327 tid: Tid, 328 signal: Option<Signal>, 329 ) -> Result<(), Self::Error> { 330 if signal.is_some() { 331 return Err("no support for continuing with signal".to_owned()); 332 } 333 match self.vm_request(GdbRequestPayload::Resume, tid_to_cpuid(tid)) { 334 Ok(_) => Ok(()), 335 Err(e) => Err(format!("Failed to resume the target: {:?}", e)), 336 } 337 } 338 339 #[inline(always)] 340 fn support_single_step(&mut self) -> Option<MultiThreadSingleStepOps<'_, Self>> { 341 Some(self) 342 } 343 } 344 345 impl MultiThreadSingleStep for GdbStub { 346 fn set_resume_action_step( 347 &mut self, 348 tid: Tid, 349 signal: Option<Signal>, 350 ) -> Result<(), Self::Error> { 351 if signal.is_some() { 352 return Err("no support for stepping with signal".to_owned()); 353 } 354 355 if !self.single_step { 356 match self.vm_request(GdbRequestPayload::SetSingleStep(true), tid_to_cpuid(tid)) { 357 Ok(_) => { 358 self.single_step = true; 359 } 360 Err(e) => { 361 return Err(format!("Failed to request SetSingleStep: {:?}", e)); 362 } 363 } 364 } 365 match self.vm_request(GdbRequestPayload::Resume, tid_to_cpuid(tid)) { 366 Ok(_) => Ok(()), 367 Err(e) => { 368 return Err(format!("Failed to resume the target: {:?}", e)); 369 } 370 } 371 } 372 } 373 374 impl Breakpoints for GdbStub { 375 #[inline(always)] 376 fn support_hw_breakpoint(&mut self) -> Option<HwBreakpointOps<Self>> { 377 Some(self) 378 } 379 } 380 381 impl HwBreakpoint for GdbStub { 382 fn add_hw_breakpoint( 383 &mut self, 384 addr: <Self::Arch as Arch>::Usize, 385 _kind: <Self::Arch as Arch>::BreakpointKind, 386 ) -> TargetResult<bool, Self> { 387 // If we already have 4 breakpoints, we cannot set a new one. 388 if self.hw_breakpoints.len() >= 4 { 389 error!("Not allowed to set more than 4 HW breakpoints"); 390 return Ok(false); 391 } 392 393 self.hw_breakpoints.push(GuestAddress(addr)); 394 395 let payload = GdbRequestPayload::SetHwBreakPoint(self.hw_breakpoints.clone()); 396 match self.vm_request(payload, 0) { 397 Ok(_) => Ok(true), 398 Err(e) => { 399 error!("Failed to request SetHwBreakPoint: {:?}", e); 400 Err(TargetError::NonFatal) 401 } 402 } 403 } 404 fn remove_hw_breakpoint( 405 &mut self, 406 addr: <Self::Arch as Arch>::Usize, 407 _kind: <Self::Arch as Arch>::BreakpointKind, 408 ) -> TargetResult<bool, Self> { 409 match self.hw_breakpoints.iter().position(|&b| b.0 == addr) { 410 None => return Ok(false), 411 Some(pos) => self.hw_breakpoints.remove(pos), 412 }; 413 414 let payload = GdbRequestPayload::SetHwBreakPoint(self.hw_breakpoints.clone()); 415 match self.vm_request(payload, 0) { 416 Ok(_) => Ok(true), 417 Err(e) => { 418 error!("Failed to request SetHwBreakPoint: {:?}", e); 419 Err(TargetError::NonFatal) 420 } 421 } 422 } 423 } 424 425 enum GdbEventLoop {} 426 427 impl run_blocking::BlockingEventLoop for GdbEventLoop { 428 type Target = GdbStub; 429 type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>; 430 type StopReason = MultiThreadStopReason<ArchUsize>; 431 432 #[allow(clippy::type_complexity)] 433 fn wait_for_stop_reason( 434 target: &mut Self::Target, 435 conn: &mut Self::Connection, 436 ) -> Result< 437 run_blocking::Event<Self::StopReason>, 438 run_blocking::WaitForStopReasonError< 439 <Self::Target as Target>::Error, 440 <Self::Connection as Connection>::Error, 441 >, 442 > { 443 // Polling 444 loop { 445 // This read is non-blocking. 446 match target.vm_event.read() { 447 Ok(tid) => { 448 target 449 .vm_request(GdbRequestPayload::Pause, 0) 450 .map_err(|_| { 451 run_blocking::WaitForStopReasonError::Target( 452 "Failed to pause VM".to_owned(), 453 ) 454 })?; 455 let stop_reason = if target.single_step { 456 MultiThreadStopReason::DoneStep 457 } else { 458 MultiThreadStopReason::HwBreak(Tid::new(tid as usize).unwrap()) 459 }; 460 return Ok(run_blocking::Event::TargetStopped(stop_reason)); 461 } 462 Err(e) => { 463 if e.kind() != std::io::ErrorKind::WouldBlock { 464 return Err(run_blocking::WaitForStopReasonError::Connection(e)); 465 } 466 } 467 } 468 469 if conn.peek().map(|b| b.is_some()).unwrap_or(true) { 470 let byte = conn 471 .read() 472 .map_err(run_blocking::WaitForStopReasonError::Connection)?; 473 return Ok(run_blocking::Event::IncomingData(byte)); 474 } 475 } 476 } 477 478 fn on_interrupt( 479 target: &mut Self::Target, 480 ) -> Result<Option<Self::StopReason>, <Self::Target as Target>::Error> { 481 target 482 .vm_request(GdbRequestPayload::Pause, 0) 483 .map_err(|e| { 484 error!("Failed to pause the target: {:?}", e); 485 "Failed to pause the target" 486 })?; 487 Ok(Some(MultiThreadStopReason::Signal(Signal::SIGINT))) 488 } 489 } 490 491 pub fn gdb_thread(mut gdbstub: GdbStub, path: &std::path::Path) { 492 let listener = match UnixListener::bind(path) { 493 Ok(s) => s, 494 Err(e) => { 495 error!("Failed to create a Unix domain socket listener: {}", e); 496 return; 497 } 498 }; 499 info!("Waiting for a GDB connection on {}...", path.display()); 500 501 let (stream, addr) = match listener.accept() { 502 Ok(v) => v, 503 Err(e) => { 504 error!("Failed to accept a connection from GDB: {}", e); 505 return; 506 } 507 }; 508 info!("GDB connected from {:?}", addr); 509 510 let connection: Box<dyn ConnectionExt<Error = std::io::Error>> = Box::new(stream); 511 let gdb = gdbstub::stub::GdbStub::new(connection); 512 513 match gdb.run_blocking::<GdbEventLoop>(&mut gdbstub) { 514 Ok(disconnect_reason) => match disconnect_reason { 515 DisconnectReason::Disconnect => { 516 info!("GDB client has disconnected. Running..."); 517 518 if let Err(e) = gdbstub.vm_request(GdbRequestPayload::SetSingleStep(false), 0) { 519 error!("Failed to disable single step: {:?}", e); 520 } 521 522 if let Err(e) = 523 gdbstub.vm_request(GdbRequestPayload::SetHwBreakPoint(Vec::new()), 0) 524 { 525 error!("Failed to remove breakpoints: {:?}", e); 526 } 527 528 if let Err(e) = gdbstub.vm_request(GdbRequestPayload::Resume, 0) { 529 error!("Failed to resume the VM: {:?}", e); 530 } 531 } 532 _ => { 533 error!("Target exited or terminated"); 534 } 535 }, 536 Err(e) => { 537 error!("error occurred in GDB session: {}", e); 538 } 539 } 540 } 541