Skip to content

Commit

Permalink
Prepares CPU interruption from debugger (#1034)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Oct 13, 2024
1 parent 805920a commit e101b8b
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 92 deletions.
1 change: 1 addition & 0 deletions gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
10 changes: 9 additions & 1 deletion gui/src/vmm/cpu/controller.rs
Original file line number Diff line number Diff line change
@@ -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<JoinHandle<()>>,
state: Arc<Mutex<CpuState>>,
}

impl CpuController {
pub fn new(thread: JoinHandle<()>) -> Self {
pub fn new(thread: JoinHandle<()>, state: Arc<Mutex<CpuState>>) -> Self {
Self {
thread: ManuallyDrop::new(thread),
state,
}
}
}
Expand All @@ -20,3 +23,8 @@ impl Drop for CpuController {
unsafe { ManuallyDrop::take(&mut self.thread).join().unwrap() };
}
}

/// State of a CPU.
pub enum CpuState {
Running,
}
58 changes: 44 additions & 14 deletions gui/src/vmm/cpu/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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")]
Expand All @@ -21,7 +24,6 @@ mod controller;
pub struct CpuManager<H: Hypervisor, S: Screen> {
hv: Arc<H>,
screen: Arc<S::Buffer>,
feats: Arc<CpuFeats>,
devices: Arc<DeviceTree<H>>,
event: VmmEventHandler,
cpus: Vec<CpuController>,
Expand All @@ -32,15 +34,13 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
pub fn new(
hv: Arc<H>,
screen: Arc<S::Buffer>,
feats: CpuFeats,
devices: Arc<DeviceTree<H>>,
event: VmmEventHandler,
shutdown: Arc<AtomicBool>,
) -> Self {
Self {
hv,
screen,
feats: Arc::new(feats),
devices,
event,
cpus: Vec::new(),
Expand All @@ -53,22 +53,30 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
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<H, S> {
DebugLock(self)
}

fn main_cpu(args: &Args<H, S>, entry: usize, map: RamMap) {
fn main_cpu(args: Args<H, S>, state: Arc<Mutex<CpuState>>, entry: usize, map: RamMap) {
let mut cpu = match args.hv.create_cpu(0) {
Ok(v) => v,
Err(e) => {
Expand All @@ -78,24 +86,24 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
}
};

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<H, S>) {
fn run_cpu<'a>(args: &'a Args<H, S>, state: &'a Mutex<CpuState>, mut cpu: H::Cpu<'a>) {
// Build device contexts for this CPU.
let mut devices = args
.devices
.map()
.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::<BTreeMap<usize, (Box<dyn DeviceContext<H::Cpu<'a>>>, NonZero<usize>)>>();

Expand Down Expand Up @@ -149,11 +157,33 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
}
}

/// RAII struct to unlock all CPUs when dropped.
pub struct DebugLock<'a, H: Hypervisor, S: Screen>(&'a mut CpuManager<H, S>);

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<H, S>;

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<H: Hypervisor, S: Screen> {
hv: Arc<H>,
screen: Arc<S::Buffer>,
feats: Arc<CpuFeats>,
devices: Arc<DeviceTree<H>>,
event: VmmEventHandler,
shutdown: Arc<AtomicBool>,
Expand Down
73 changes: 13 additions & 60 deletions gui/src/vmm/cpu/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<H: Hypervisor, S: Screen> gdbstub::target::Target for CpuManager<H, S> {
type Arch = Arch;
type Arch = X86_64_SSE;
type Error = TargetError;

fn base_ops(&mut self) -> BaseOps<'_, Self::Arch, Self::Error> {
Expand All @@ -26,11 +29,11 @@ impl<H: Hypervisor, S: Screen> gdbstub::target::Target for CpuManager<H, S> {
}

impl<H: Hypervisor, S: Screen> MultiThreadBase for CpuManager<H, S> {
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!()
}

Expand All @@ -55,7 +58,11 @@ impl<H: Hypervisor, S: Screen> MultiThreadBase for CpuManager<H, S> {
&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)]
Expand All @@ -71,15 +78,11 @@ impl<H: Hypervisor, S: Screen> Breakpoints for CpuManager<H, S> {
}

impl<H: Hypervisor, S: Screen> SwBreakpoint for CpuManager<H, S> {
fn add_sw_breakpoint(&mut self, addr: u64, kind: BreakpointKind) -> TargetResult<bool, Self> {
fn add_sw_breakpoint(&mut self, addr: u64, kind: usize) -> TargetResult<bool, Self> {
todo!()
}

fn remove_sw_breakpoint(
&mut self,
addr: u64,
kind: BreakpointKind,
) -> TargetResult<bool, Self> {
fn remove_sw_breakpoint(&mut self, addr: u64, kind: usize) -> TargetResult<bool, Self> {
todo!()
}
}
Expand All @@ -90,56 +93,6 @@ impl<H: Hypervisor, S: Screen> ThreadExtraInfo for CpuManager<H, S> {
}
}

/// 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<u8>)) {
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<Self> {
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<std::num::NonZeroUsize>)> {
todo!()
}
}

/// Implementation of [`gdbstub::target::Target::Error`] for x86-64.
#[derive(Debug, Error)]
pub enum TargetError {}
8 changes: 7 additions & 1 deletion gui/src/vmm/hw/console/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -35,7 +37,11 @@ impl<H: Hypervisor> Device<H> for Console {
self.len
}

fn create_context<'a>(&'a self, hv: &'a H) -> Box<dyn DeviceContext<H::Cpu<'a>> + 'a> {
fn create_context<'a>(
&'a self,
hv: &'a H,
_: &'a Mutex<CpuState>,
) -> Box<dyn DeviceContext<H::Cpu<'a>> + 'a> {
Box::new(Context::new(self, hv))
}
}
10 changes: 8 additions & 2 deletions gui/src/vmm/hw/debugger/context.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
// 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;
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<CpuState>,
}

impl<'a> Context<'a> {
pub fn new(dev: &'a Debugger) -> Self {
Self { dev }
pub fn new(dev: &'a Debugger, state: &'a Mutex<CpuState>) -> Self {
Self { dev, state }
}
}

Expand All @@ -26,10 +29,13 @@ impl<'a, C: Cpu> DeviceContext<C> 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(),
};
Expand Down
10 changes: 8 additions & 2 deletions gui/src/vmm/hw/debugger/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -35,7 +37,11 @@ impl<H: Hypervisor> Device<H> for Debugger {
self.len
}

fn create_context<'a>(&'a self, _: &'a H) -> Box<dyn DeviceContext<H::Cpu<'a>> + 'a> {
Box::new(Context::new(self))
fn create_context<'a>(
&'a self,
_: &'a H,
state: &'a Mutex<CpuState>,
) -> Box<dyn DeviceContext<H::Cpu<'a>> + 'a> {
Box::new(Context::new(self, state))
}
}
Loading

0 comments on commit e101b8b

Please sign in to comment.