From e101b8b7ebf5f440f367ef2191e9696a5b9c811f Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Sun, 13 Oct 2024 18:30:51 +0700 Subject: [PATCH] Prepares CPU interruption from debugger (#1034) --- gui/Cargo.toml | 1 + gui/src/vmm/cpu/controller.rs | 10 +++- gui/src/vmm/cpu/mod.rs | 58 ++++++++++++++++++------ gui/src/vmm/cpu/x86_64.rs | 73 ++++++------------------------ gui/src/vmm/hw/console/mod.rs | 8 +++- gui/src/vmm/hw/debugger/context.rs | 10 +++- gui/src/vmm/hw/debugger/mod.rs | 10 +++- gui/src/vmm/hw/mod.rs | 17 ++++--- gui/src/vmm/hw/vmm/mod.rs | 8 +++- gui/src/vmm/mod.rs | 9 ++-- 10 files changed, 112 insertions(+), 92 deletions(-) diff --git a/gui/Cargo.toml b/gui/Cargo.toml index c8672def..cdd4049f 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["staticlib"] [dependencies] bitfield-struct = "0.8.0" gdbstub = "0.7.2" +gdbstub_arch = "0.3.1" humansize = "2.1.3" libc = "0.2.155" obconf = { path = "../src/obconf", features = ["serde", "virt"] } diff --git a/gui/src/vmm/cpu/controller.rs b/gui/src/vmm/cpu/controller.rs index f1392eab..03c1b72e 100644 --- a/gui/src/vmm/cpu/controller.rs +++ b/gui/src/vmm/cpu/controller.rs @@ -1,16 +1,19 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use std::mem::ManuallyDrop; +use std::sync::{Arc, Mutex}; use std::thread::JoinHandle; /// Contains objects to control a CPU from outside. pub struct CpuController { thread: ManuallyDrop>, + state: Arc>, } impl CpuController { - pub fn new(thread: JoinHandle<()>) -> Self { + pub fn new(thread: JoinHandle<()>, state: Arc>) -> Self { Self { thread: ManuallyDrop::new(thread), + state, } } } @@ -20,3 +23,8 @@ impl Drop for CpuController { unsafe { ManuallyDrop::take(&mut self.thread).join().unwrap() }; } } + +/// State of a CPU. +pub enum CpuState { + Running, +} diff --git a/gui/src/vmm/cpu/mod.rs b/gui/src/vmm/cpu/mod.rs index e0531d9e..2f36c158 100644 --- a/gui/src/vmm/cpu/mod.rs +++ b/gui/src/vmm/cpu/mod.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +pub use self::controller::CpuState; + use self::controller::CpuController; -use super::hv::{Cpu, CpuExit, CpuFeats, CpuIo, Hypervisor}; +use super::hv::{Cpu, CpuExit, CpuIo, Hypervisor}; use super::hw::{DeviceContext, DeviceTree}; use super::ram::RamMap; use super::screen::Screen; @@ -9,8 +11,9 @@ use crate::error::RustError; use std::collections::BTreeMap; use std::error::Error; use std::num::NonZero; +use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] @@ -21,7 +24,6 @@ mod controller; pub struct CpuManager { hv: Arc, screen: Arc, - feats: Arc, devices: Arc>, event: VmmEventHandler, cpus: Vec, @@ -32,7 +34,6 @@ impl CpuManager { pub fn new( hv: Arc, screen: Arc, - feats: CpuFeats, devices: Arc>, event: VmmEventHandler, shutdown: Arc, @@ -40,7 +41,6 @@ impl CpuManager { Self { hv, screen, - feats: Arc::new(feats), devices, event, cpus: Vec::new(), @@ -53,22 +53,30 @@ impl CpuManager { let args = Args { hv: self.hv.clone(), screen: self.screen.clone(), - feats: self.feats.clone(), devices: self.devices.clone(), event: self.event, shutdown: self.shutdown.clone(), }; // Spawn thread to drive vCPU. + let state = Arc::new(Mutex::new(CpuState::Running)); let t = match map { - Some(map) => std::thread::spawn(move || Self::main_cpu(&args, start, map)), + Some(map) => std::thread::spawn({ + let state = state.clone(); + + move || Self::main_cpu(args, state, start, map) + }), None => todo!(), }; - self.cpus.push(CpuController::new(t)); + self.cpus.push(CpuController::new(t, state)); + } + + pub fn debug_lock(&mut self) -> DebugLock { + DebugLock(self) } - fn main_cpu(args: &Args, entry: usize, map: RamMap) { + fn main_cpu(args: Args, state: Arc>, entry: usize, map: RamMap) { let mut cpu = match args.hv.create_cpu(0) { Ok(v) => v, Err(e) => { @@ -78,16 +86,16 @@ impl CpuManager { } }; - if let Err(e) = super::arch::setup_main_cpu(&mut cpu, entry, map, &args.feats) { + if let Err(e) = super::arch::setup_main_cpu(&mut cpu, entry, map, args.hv.cpu_features()) { let e = RustError::with_source("couldn't setup main CPU", e); unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) }; return; } - Self::run_cpu(cpu, args); + Self::run_cpu(&args, &state, cpu); } - fn run_cpu<'a>(mut cpu: H::Cpu<'a>, args: &'a Args) { + fn run_cpu<'a>(args: &'a Args, state: &'a Mutex, mut cpu: H::Cpu<'a>) { // Build device contexts for this CPU. let mut devices = args .devices @@ -95,7 +103,7 @@ impl CpuManager { .map(|(addr, dev)| { let end = dev.len().checked_add(addr).unwrap(); - (addr, (dev.create_context(&args.hv), end)) + (addr, (dev.create_context(&args.hv, state), end)) }) .collect::>>, NonZero)>>(); @@ -149,11 +157,33 @@ impl CpuManager { } } +/// RAII struct to unlock all CPUs when dropped. +pub struct DebugLock<'a, H: Hypervisor, S: Screen>(&'a mut CpuManager); + +impl<'a, H: Hypervisor, S: Screen> Drop for DebugLock<'a, H, S> { + fn drop(&mut self) { + todo!() + } +} + +impl<'a, H: Hypervisor, S: Screen> Deref for DebugLock<'a, H, S> { + type Target = CpuManager; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a, H: Hypervisor, S: Screen> DerefMut for DebugLock<'a, H, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + /// Encapsulates arguments for a function to run a CPU. struct Args { hv: Arc, screen: Arc, - feats: Arc, devices: Arc>, event: VmmEventHandler, shutdown: Arc, diff --git a/gui/src/vmm/cpu/x86_64.rs b/gui/src/vmm/cpu/x86_64.rs index 8bbbde0c..a21e515e 100644 --- a/gui/src/vmm/cpu/x86_64.rs +++ b/gui/src/vmm/cpu/x86_64.rs @@ -10,10 +10,13 @@ use gdbstub::target::ext::breakpoints::{ }; use gdbstub::target::ext::thread_extra_info::{ThreadExtraInfo, ThreadExtraInfoOps}; use gdbstub::target::TargetResult; +use gdbstub_arch::x86::reg::X86_64CoreRegs; +use gdbstub_arch::x86::X86_64_SSE; +use std::num::NonZero; use thiserror::Error; impl gdbstub::target::Target for CpuManager { - type Arch = Arch; + type Arch = X86_64_SSE; type Error = TargetError; fn base_ops(&mut self) -> BaseOps<'_, Self::Arch, Self::Error> { @@ -26,11 +29,11 @@ impl gdbstub::target::Target for CpuManager { } impl MultiThreadBase for CpuManager { - fn read_registers(&mut self, regs: &mut Registers, tid: Tid) -> TargetResult<(), Self> { + fn read_registers(&mut self, regs: &mut X86_64CoreRegs, tid: Tid) -> TargetResult<(), Self> { todo!() } - fn write_registers(&mut self, regs: &Registers, tid: Tid) -> TargetResult<(), Self> { + fn write_registers(&mut self, regs: &X86_64CoreRegs, tid: Tid) -> TargetResult<(), Self> { todo!() } @@ -55,7 +58,11 @@ impl MultiThreadBase for CpuManager { &mut self, thread_is_active: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error> { - todo!() + for id in (0..self.cpus.len()).map(|v| unsafe { NonZero::new_unchecked(v + 1) }) { + thread_is_active(id); + } + + Ok(()) } #[inline(always)] @@ -71,15 +78,11 @@ impl Breakpoints for CpuManager { } impl SwBreakpoint for CpuManager { - fn add_sw_breakpoint(&mut self, addr: u64, kind: BreakpointKind) -> TargetResult { + fn add_sw_breakpoint(&mut self, addr: u64, kind: usize) -> TargetResult { todo!() } - fn remove_sw_breakpoint( - &mut self, - addr: u64, - kind: BreakpointKind, - ) -> TargetResult { + fn remove_sw_breakpoint(&mut self, addr: u64, kind: usize) -> TargetResult { todo!() } } @@ -90,56 +93,6 @@ impl ThreadExtraInfo for CpuManager { } } -/// Implementation of [`gdbstub::arch::Arch`] for x86-64. -pub enum Arch {} - -impl gdbstub::arch::Arch for Arch { - type Usize = u64; - type Registers = Registers; - type BreakpointKind = BreakpointKind; - type RegId = RegId; -} - -/// Implementation of [`gdbstub::arch::Registers`] for x86-64. -#[derive(Default, Debug, Clone, PartialEq)] -pub struct Registers {} - -impl gdbstub::arch::Registers for Registers { - type ProgramCounter = u64; - - fn pc(&self) -> Self::ProgramCounter { - todo!() - } - - fn gdb_serialize(&self, write_byte: impl FnMut(Option)) { - todo!() - } - - fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { - todo!() - } -} - -/// Implementation of [`gdbstub::arch::BreakpointKind`] for x86-64. -#[derive(Debug)] -pub struct BreakpointKind {} - -impl gdbstub::arch::BreakpointKind for BreakpointKind { - fn from_usize(kind: usize) -> Option { - todo!() - } -} - -/// Implementation of [`gdbstub::arch::RegId`] for x86-64. -#[derive(Debug)] -pub struct RegId {} - -impl gdbstub::arch::RegId for RegId { - fn from_raw_id(id: usize) -> Option<(Self, Option)> { - todo!() - } -} - /// Implementation of [`gdbstub::target::Target::Error`] for x86-64. #[derive(Debug, Error)] pub enum TargetError {} diff --git a/gui/src/vmm/hw/console/mod.rs b/gui/src/vmm/hw/console/mod.rs index 89ade5e6..7cd92281 100644 --- a/gui/src/vmm/hw/console/mod.rs +++ b/gui/src/vmm/hw/console/mod.rs @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use self::context::Context; use super::{Device, DeviceContext}; +use crate::vmm::cpu::CpuState; use crate::vmm::hv::Hypervisor; use crate::vmm::VmmEventHandler; use obconf::ConsoleMemory; use std::num::NonZero; +use std::sync::Mutex; mod context; @@ -35,7 +37,11 @@ impl Device for Console { self.len } - fn create_context<'a>(&'a self, hv: &'a H) -> Box> + 'a> { + fn create_context<'a>( + &'a self, + hv: &'a H, + _: &'a Mutex, + ) -> Box> + 'a> { Box::new(Context::new(self, hv)) } } diff --git a/gui/src/vmm/hw/debugger/context.rs b/gui/src/vmm/hw/debugger/context.rs index 1305f7a8..ef4c66ca 100644 --- a/gui/src/vmm/hw/debugger/context.rs +++ b/gui/src/vmm/hw/debugger/context.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use super::Debugger; +use crate::vmm::cpu::CpuState; use crate::vmm::hv::{Cpu, CpuExit, CpuIo}; use crate::vmm::hw::{read_u8, DeviceContext, MmioError}; use crate::vmm::VmmEvent; @@ -7,16 +8,18 @@ use obconf::{DebuggerMemory, StopReason}; use std::error::Error; use std::mem::offset_of; use std::ptr::null_mut; +use std::sync::Mutex; use thiserror::Error; /// Implementation of [`DeviceContext`]. pub struct Context<'a> { dev: &'a Debugger, + state: &'a Mutex, } impl<'a> Context<'a> { - pub fn new(dev: &'a Debugger) -> Self { - Self { dev } + pub fn new(dev: &'a Debugger, state: &'a Mutex) -> Self { + Self { dev, state } } } @@ -26,10 +29,13 @@ impl<'a, C: Cpu> DeviceContext for Context<'a> { let off = exit.addr() - self.dev.addr; if off == offset_of!(DebuggerMemory, stop) { + // Read stop reason. let stop = read_u8(exit).map_err(|e| ExecError::ReadFailed(off, e))?; let stop: StopReason = stop .try_into() .map_err(|_| Box::new(ExecError::InvalidStop(stop)))?; + + // Notify GUI. let stop = match stop { StopReason::WaitForDebugger => null_mut(), }; diff --git a/gui/src/vmm/hw/debugger/mod.rs b/gui/src/vmm/hw/debugger/mod.rs index 16804c0e..bf7c07d2 100644 --- a/gui/src/vmm/hw/debugger/mod.rs +++ b/gui/src/vmm/hw/debugger/mod.rs @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use self::context::Context; use super::{Device, DeviceContext}; +use crate::vmm::cpu::CpuState; use crate::vmm::hv::Hypervisor; use crate::vmm::VmmEventHandler; use obconf::DebuggerMemory; use std::num::NonZero; +use std::sync::Mutex; mod context; @@ -35,7 +37,11 @@ impl Device for Debugger { self.len } - fn create_context<'a>(&'a self, _: &'a H) -> Box> + 'a> { - Box::new(Context::new(self)) + fn create_context<'a>( + &'a self, + _: &'a H, + state: &'a Mutex, + ) -> Box> + 'a> { + Box::new(Context::new(self, state)) } } diff --git a/gui/src/vmm/hw/mod.rs b/gui/src/vmm/hw/mod.rs index 1137c2d0..9f508eef 100644 --- a/gui/src/vmm/hw/mod.rs +++ b/gui/src/vmm/hw/mod.rs @@ -1,16 +1,17 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +pub use self::console::*; +pub use self::debugger::*; +pub use self::vmm::*; + +use super::cpu::CpuState; use super::hv::{Cpu, CpuExit, CpuIo, Hypervisor, IoBuf}; use super::VmmEventHandler; use std::collections::BTreeMap; use std::error::Error; use std::num::NonZero; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use thiserror::Error; -pub use self::console::*; -pub use self::debugger::*; -pub use self::vmm::*; - mod console; mod debugger; mod vmm; @@ -126,7 +127,11 @@ pub trait Device: Send + Sync { /// Total size of device memory, in bytes. fn len(&self) -> NonZero; - fn create_context<'a>(&'a self, hv: &'a H) -> Box> + 'a>; + fn create_context<'a>( + &'a self, + hv: &'a H, + state: &'a Mutex, + ) -> Box> + 'a>; } /// Context to execute memory-mapped I/O operations on a virtual device. diff --git a/gui/src/vmm/hw/vmm/mod.rs b/gui/src/vmm/hw/vmm/mod.rs index fc5dda11..5ec2efcf 100644 --- a/gui/src/vmm/hw/vmm/mod.rs +++ b/gui/src/vmm/hw/vmm/mod.rs @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use self::context::Context; use super::{Device, DeviceContext}; +use crate::vmm::cpu::CpuState; use crate::vmm::hv::Hypervisor; use crate::vmm::VmmEventHandler; use obconf::VmmMemory; use std::num::NonZero; +use std::sync::Mutex; mod context; @@ -35,7 +37,11 @@ impl Device for Vmm { self.len } - fn create_context<'a>(&'a self, _: &'a H) -> Box> + 'a> { + fn create_context<'a>( + &'a self, + _: &'a H, + _: &'a Mutex, + ) -> Box> + 'a> { Box::new(Context::new(self)) } } diff --git a/gui/src/vmm/mod.rs b/gui/src/vmm/mod.rs index c0ddd7b4..0e585689 100644 --- a/gui/src/vmm/mod.rs +++ b/gui/src/vmm/mod.rs @@ -442,7 +442,6 @@ pub unsafe extern "C" fn vmm_start( let mut cpu = CpuManager::new( Arc::new(hv), screen.buffer().clone(), - feats, devices, event, shutdown.clone(), @@ -501,14 +500,14 @@ pub unsafe extern "C" fn vmm_dispatch_debug(vmm: *mut Vmm, stop: *mut KernelStop // Setup target object. let vmm = &mut *vmm; - let target = &mut vmm.cpu; + let mut target = vmm.cpu.debug_lock(); loop { // Check current state. let r = match vmm.gdb.take().unwrap() { - GdbStubStateMachine::Idle(s) => self::debug::dispatch_idle(target, s), + GdbStubStateMachine::Idle(s) => self::debug::dispatch_idle(&mut target, s), GdbStubStateMachine::Running(s) => { - match self::debug::dispatch_running(target, s, stop.take()) { + match self::debug::dispatch_running(&mut target, s, stop.take()) { Ok(Ok(v)) => Ok(v), Ok(Err(v)) => { // No pending data from the debugger. @@ -519,7 +518,7 @@ pub unsafe extern "C" fn vmm_dispatch_debug(vmm: *mut Vmm, stop: *mut KernelStop } } GdbStubStateMachine::CtrlCInterrupt(s) => { - match s.interrupt_handled(target, None::>) { + match s.interrupt_handled(&mut target, None::>) { Ok(v) => vmm.gdb = Some(v), Err(e) => { let r = RustError::with_source("couldn't handle CTRL+C from a debugger", e);