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