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