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