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