Skip to content

Commit

Permalink
Switches from kernel debugging to OS debugging (#1041)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Oct 17, 2024
1 parent 72a851d commit 8897efe
Show file tree
Hide file tree
Showing 18 changed files with 203 additions and 254 deletions.
8 changes: 4 additions & 4 deletions gui/src/vmm/cpu/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ use std::thread::JoinHandle;
/// Contains objects to control a CPU from outside.
pub struct CpuController {
thread: ManuallyDrop<JoinHandle<()>>,
debug: Debuggee<GdbRegs>,
debug: Option<Debuggee<GdbRegs>>,
}

impl CpuController {
pub fn new(thread: JoinHandle<()>, debug: Debuggee<GdbRegs>) -> Self {
pub fn new(thread: JoinHandle<()>, debug: Option<Debuggee<GdbRegs>>) -> Self {
Self {
thread: ManuallyDrop::new(thread),
debug,
}
}

pub fn debug_mut(&mut self) -> &mut Debuggee<GdbRegs> {
&mut self.debug
pub fn debug_mut(&mut self) -> Option<&mut Debuggee<GdbRegs>> {
self.debug.as_mut()
}
}

Expand Down
124 changes: 94 additions & 30 deletions gui/src/vmm/cpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub use self::arch::*;

use self::controller::CpuController;
use super::debug::{debug_controller, Debugger};
use super::hv::{Cpu, CpuExit, CpuIo, CpuRun, Hypervisor};
use super::hv::{Cpu, CpuExit, CpuIo, CpuRun, CpuStates, Hypervisor};
use super::hw::{DeviceContext, DeviceTree};
use super::ram::RamMap;
use super::screen::Screen;
Expand All @@ -12,6 +12,7 @@ use crate::error::RustError;
use std::collections::BTreeMap;
use std::num::NonZero;
use std::ops::{Deref, DerefMut};
use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

Expand Down Expand Up @@ -48,7 +49,7 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
}
}

pub fn spawn(&mut self, start: usize, map: Option<RamMap>) {
pub fn spawn(&mut self, start: usize, map: Option<RamMap>, debug: bool) {
// Setup arguments.
let args = Args {
hv: self.hv.clone(),
Expand All @@ -58,8 +59,14 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
shutdown: self.shutdown.clone(),
};

// Setup debug controller.
let (debuggee, debugger) = if debug {
Some(debug_controller()).unzip()
} else {
None.unzip()
};

// Spawn thread to drive vCPU.
let (debuggee, debugger) = debug_controller();
let t = match map {
Some(map) => std::thread::spawn(move || Self::main_cpu(args, debugger, start, map)),
None => todo!(),
Expand All @@ -72,7 +79,8 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
DebugLock(self)
}

fn main_cpu(args: Args<H, S>, debug: Debugger<GdbRegs>, entry: usize, map: RamMap) {
fn main_cpu(args: Args<H, S>, mut debug: Option<Debugger<GdbRegs>>, entry: usize, map: RamMap) {
// Create CPU.
let mut cpu = match args.hv.create_cpu(0) {
Ok(v) => v,
Err(e) => {
Expand All @@ -88,82 +96,112 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
return;
}

// Wait for debugger.
if let Some(debug) = &mut debug {
// Get states.
let mut states = match cpu.states() {
Ok(v) => v,
Err(e) => {
let e = RustError::with_source("couldn't get CPU states", e);
unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
return;
}
};

// Get registers.
let regs = match Self::get_debug_regs(&mut states) {
Ok(v) => v,
Err(e) => {
unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
return;
}
};

// Notify GUI. This will block until the debugger has completed their works.
let resp = debug.send(regs);
let stop = null_mut();

unsafe { args.event.invoke(VmmEvent::Breakpoint { stop }) };

// Update registers from debugger.
if let Err(e) = Self::set_debug_regs(&mut states, resp.into_response()) {
unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
return;
}
}

// Run.
Self::run_cpu(&args, debug, cpu);
}

fn run_cpu<'a>(args: &'a Args<H, S>, debug: Debugger<GdbRegs>, mut cpu: H::Cpu<'a>) {
fn run_cpu<'a>(args: &'a Args<H, S>, debug: Option<Debugger<GdbRegs>>, mut cpu: H::Cpu<'a>) {
// Build device contexts for this CPU.
let mut devices = BTreeMap::<usize, Device<'a, H::Cpu<'a>>>::new();
let t = &args.devices;

Device::insert(&mut devices, t.console(), |d| d.create_context(&*args.hv));
Device::insert(&mut devices, t.debugger(), |d| d.create_context(debug));
Device::insert(&mut devices, t.vmm(), |d| d.create_context());

// Dispatch CPU events until shutdown.
'main: while !args.shutdown.load(Ordering::Relaxed) {
let e = 'main: loop {
// Check for shutdown signal.
if args.shutdown.load(Ordering::Relaxed) {
break None;
}

// Run the vCPU.
let mut exit = match cpu.run() {
Ok(v) => v,
Err(e) => {
let e = RustError::with_source("couldn't run CPU", e);
unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
break;
}
Err(e) => break Some(RustError::with_source("couldn't run CPU", e)),
};

// Execute VM exited event.
for d in devices.values_mut() {
let r = match d.context.exited(exit.cpu()) {
Ok(v) => v,
Err(e) => {
let e = RustError::with_source(
break 'main Some(RustError::with_source(
format!("couldn't execute a VM exited event on a {}", d.name),
e.deref(),
);

unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
break 'main;
));
}
};

if !r {
break 'main;
break 'main None;
}
}

// Handle exit.
let r = match Self::handle_exit(&mut devices, exit) {
Ok(v) => v,
Err(e) => {
unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
break;
}
Err(e) => break Some(e),
};

if !r {
break;
break None;
}

// Execute post exit event.
for d in devices.values_mut() {
let r = match d.context.post(&mut cpu) {
Ok(v) => v,
Err(e) => {
let e = RustError::with_source(
break 'main Some(RustError::with_source(
format!("couldn't execute a post VM exit on a {}", d.name),
e.deref(),
);

unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
break 'main;
));
}
};

if !r {
break 'main;
break 'main None;
}
}
};

if let Some(e) = e {
unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
}

// Shutdown other CPUs.
Expand All @@ -182,8 +220,14 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
};

// Check if I/O.
match exit.into_io() {
let exit = match exit.into_io() {
Ok(io) => return Self::handle_io(devices, io),
Err(v) => v,
};

// Check if debug.
match exit.into_debug() {
Ok(_) => todo!(),
Err(_) => todo!(),
}
}
Expand Down Expand Up @@ -219,6 +263,26 @@ impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
)
})
}

#[cfg(target_arch = "aarch64")]
fn get_debug_regs(_: &mut impl CpuStates) -> Result<GdbRegs, RustError> {
todo!()
}

#[cfg(target_arch = "x86_64")]
fn get_debug_regs(_: &mut impl CpuStates) -> Result<GdbRegs, RustError> {
todo!()
}

#[cfg(target_arch = "aarch64")]
fn set_debug_regs(_: &mut impl CpuStates, _: GdbRegs) -> Result<(), RustError> {
todo!()
}

#[cfg(target_arch = "x86_64")]
fn set_debug_regs(_: &mut impl CpuStates, _: GdbRegs) -> Result<(), RustError> {
todo!()
}
}

/// RAII struct to unlock all CPUs when dropped.
Expand Down
18 changes: 16 additions & 2 deletions gui/src/vmm/hv/linux/cpu.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use super::arch::{KvmStates, StatesError};
use super::ffi::kvm_run;
use super::ffi::{kvm_run, KVM_EXIT_DEBUG};
use super::run::KvmRun;
use crate::vmm::hv::{Cpu, CpuExit, CpuIo, CpuRun, IoBuf};
use crate::vmm::hv::{Cpu, CpuDebug, CpuExit, CpuIo, CpuRun, IoBuf};
use libc::munmap;
use std::os::fd::{AsRawFd, OwnedFd};
use std::sync::MutexGuard;
Expand Down Expand Up @@ -61,6 +61,7 @@ pub struct KvmExit<'a, 'b>(&'a mut KvmCpu<'b>);
impl<'a, 'b> CpuExit for KvmExit<'a, 'b> {
type Cpu = KvmCpu<'b>;
type Io = KvmIo<'a, 'b>;
type Debug = KvmDebug<'a, 'b>;

fn cpu(&mut self) -> &mut Self::Cpu {
self.0
Expand All @@ -82,6 +83,14 @@ impl<'a, 'b> CpuExit for KvmExit<'a, 'b> {
Err(self)
}
}

fn into_debug(self) -> Result<Self::Debug, Self> {
if unsafe { (*self.0.cx.0).exit_reason } == KVM_EXIT_DEBUG {
Ok(KvmDebug(self.0))
} else {
Err(self)
}
}
}

/// Implementation of [`CpuIo`] for KVM.
Expand Down Expand Up @@ -133,6 +142,11 @@ impl<'a, 'b> CpuIo for KvmIo<'a, 'b> {
}
}

/// Implementation of [`CpuDebug`] for KVM.
pub struct KvmDebug<'a, 'b>(&'a mut KvmCpu<'b>);

impl<'a, 'b> CpuDebug for KvmDebug<'a, 'b> {}

#[cfg(target_arch = "x86_64")]
#[repr(C)]
struct KvmTranslation {
Expand Down
19 changes: 19 additions & 0 deletions gui/src/vmm/hv/linux/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub const KVM_CHECK_EXTENSION: c_ulong = _IO(KVMIO, 0x03);
pub const KVM_GET_VCPU_MMAP_SIZE: c_ulong = _IO(KVMIO, 0x04);
pub const KVM_CREATE_VCPU: c_ulong = _IO(KVMIO, 0x41);
pub const KVM_SET_USER_MEMORY_REGION: c_ulong = _IOW::<KvmUserspaceMemoryRegion>(KVMIO, 0x46);
pub const KVM_SET_GUEST_DEBUG: c_ulong = _IOW::<KvmGuestDebug>(KVMIO, 0x9b);
#[cfg(target_arch = "aarch64")]
pub const KVM_GET_ONE_REG: c_ulong = _IOW::<KvmOneReg<()>>(KVMIO, 0xab);
#[cfg(target_arch = "aarch64")]
Expand All @@ -24,6 +25,11 @@ pub const KVM_CAP_ONE_REG: c_int = 70;
#[cfg(target_arch = "aarch64")]
pub const KVM_CAP_ARM_VM_IPA_SIZE: c_int = 165;

pub const KVM_EXIT_DEBUG: u32 = 4;

pub const KVM_GUESTDBG_ENABLE: u32 = 0x00000001;
pub const KVM_GUESTDBG_USE_SW_BP: u32 = 0x00010000;

const KVMIO: c_ulong = 0xAE;

const _IOC_NONE: c_ulong = 0;
Expand Down Expand Up @@ -89,6 +95,19 @@ pub struct KvmUserspaceMemoryRegion {
pub userspace_addr: u64,
}

#[repr(C)]
pub struct KvmGuestDebug {
pub control: u32,
pub pad: u32,
pub arch: KvmGuestDebugArch,
}

#[cfg(target_arch = "x86_64")]
#[repr(C)]
pub struct KvmGuestDebugArch {
pub debugreg: [u64; 8],
}

#[cfg(target_arch = "aarch64")]
#[repr(C)]
pub struct KvmOneReg<'a, T> {
Expand Down
Loading

0 comments on commit 8897efe

Please sign in to comment.