diff --git a/Cargo.lock b/Cargo.lock index 20ec92d0c..eb90a635b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1594,6 +1594,7 @@ dependencies = [ "thiserror 2.0.3", "uuid", "windows-sys 0.59.0", + "winit", "winres", "x86-64", "xdg", diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 4e20e16e3..25b893fb4 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -5,8 +5,7 @@ find_package(Threads REQUIRED) # Setup GUI. add_executable(obliteration WIN32 MACOSX_BUNDLE main.cpp - main_window.cpp - resources.qrc) + main_window.cpp) set_target_properties(obliteration PROPERTIES AUTOMOC ON AUTORCC ON) diff --git a/gui/Cargo.toml b/gui/Cargo.toml index 45d1a0572..3a5ee9dc0 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -24,6 +24,7 @@ redb = "2.2.0" serde = { version = "1.0.209", features = ["derive"] } thiserror = "2.0.3" uuid = { version = "1.11.0", features = ["serde", "v4"] } +winit = "0.30.5" [dependencies.obfw] git = "https://github.com/obhq/firmware-dumper.git" diff --git a/gui/main.cpp b/gui/main.cpp index 2f4d2ef30..44d1821ff 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -25,8 +25,6 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); - QGuiApplication::setWindowIcon(QIcon(":/resources/obliteration-icon.png")); - // Setup main window. MainWindow win; diff --git a/gui/resources.qrc b/gui/resources.qrc deleted file mode 100644 index fc410c5d5..000000000 --- a/gui/resources.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - - - resources/obliteration-icon.png - - diff --git a/gui/resources/obliteration-icon.png b/gui/resources/obliteration-icon.png deleted file mode 100644 index 2af4030af..000000000 Binary files a/gui/resources/obliteration-icon.png and /dev/null differ diff --git a/gui/src/hv/mod.rs b/gui/src/hv/mod.rs index 1cd1d8960..d25fa506e 100644 --- a/gui/src/hv/mod.rs +++ b/gui/src/hv/mod.rs @@ -60,7 +60,7 @@ pub trait Cpu { type Exit<'a>: CpuExit where Self: 'a; - type TranslateErr: Error + Send + 'static; + type TranslateErr: Error + Send + Sync + 'static; fn id(&self) -> usize; diff --git a/gui/src/main.rs b/gui/src/main.rs index 4b66c827a..0c3b7b9c4 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -1,16 +1,13 @@ #![windows_subsystem = "windows"] use self::data::{DataError, DataMgr}; -use self::debug::DebugClient; use self::graphics::{Graphics, GraphicsError, PhysicalDevice, Screen}; use self::profile::Profile; use self::setup::{run_setup, SetupError}; use self::ui::{ErrorWindow, MainWindow, ProfileModel, ResolutionModel}; -use self::vmm::{Vmm, VmmError}; +use self::vmm::{Vmm, VmmArgs, VmmError, VmmEvent}; use clap::{Parser, ValueEnum}; use debug::DebugServer; -use gdbstub::stub::MultiThreadStopReason; -use obconf::ConsoleType; use slint::{ComponentHandle, ModelRc, SharedString, VecModel}; use std::cell::Cell; use std::error::Error; @@ -20,6 +17,8 @@ use std::process::ExitCode; use std::rc::Rc; use std::sync::Arc; use thiserror::Error; +use winit::error::EventLoopError; +use winit::event_loop::EventLoop; mod data; mod debug; @@ -39,7 +38,7 @@ fn main() -> ExitCode { use std::fmt::Write; // Check program mode. - let args = CliArgs::parse(); + let args = ProgramArgs::parse(); let r = match &args.mode { Some(ProgramMode::PanicHandler) => self::panic::run_handler(), None => run_vmm(&args), @@ -75,23 +74,23 @@ fn main() -> ExitCode { ExitCode::FAILURE } -fn run_vmm(args: &CliArgs) -> Result<(), ApplicationError> { +fn run_vmm(args: &ProgramArgs) -> Result<(), ProgramError> { // Spawn panic handler. let exe = std::env::current_exe() .and_then(std::fs::canonicalize) - .map_err(ApplicationError::GetCurrentExePath)?; + .map_err(ProgramError::GetCurrentExePath)?; self::panic::spawn_handler(&exe)?; #[cfg(unix)] - rlim::set_rlimit_nofile().map_err(ApplicationError::FdLimit)?; + rlim::set_rlimit_nofile().map_err(ProgramError::FdLimit)?; // Initialize graphics engine. - let mut graphics = graphics::new().map_err(ApplicationError::InitGraphics)?; + let mut graphics = graphics::new().map_err(ProgramError::InitGraphics)?; // Run setup wizard. This will simply return the data manager if the user already has required // settings. - let data = match run_setup().map_err(ApplicationError::Setup)? { + let data = match run_setup().map_err(ProgramError::Setup)? { Some(v) => Arc::new(v), None => return Ok(()), }; @@ -123,9 +122,9 @@ fn run_vmm(args: &CliArgs) -> Result<(), ApplicationError> { // Load profiles. let mut profiles = Vec::new(); - for l in data.prof().list().map_err(ApplicationError::ListProfile)? { - let l = l.map_err(ApplicationError::ListProfile)?; - let p = Profile::load(&l).map_err(ApplicationError::LoadProfile)?; + for l in data.prof().list().map_err(ProgramError::ListProfile)? { + let l = l.map_err(ProgramError::ListProfile)?; + let p = Profile::load(&l).map_err(ProgramError::LoadProfile)?; profiles.push(p); } @@ -137,11 +136,11 @@ fn run_vmm(args: &CliArgs) -> Result<(), ApplicationError> { let l = data.prof().data(p.id()); if let Err(e) = std::fs::create_dir(&l) { - return Err(ApplicationError::CreateDirectory(l, e)); + return Err(ProgramError::CreateDirectory(l, e)); } // Save. - p.save(&l).map_err(ApplicationError::SaveDefaultProfile)?; + p.save(&l).map_err(ProgramError::SaveDefaultProfile)?; profiles.push(p); } @@ -165,11 +164,11 @@ fn run_vmm(args: &CliArgs) -> Result<(), ApplicationError> { // Wait for debugger. let debugger = if let Some(listen) = debug { let debug_server = - DebugServer::new(listen).map_err(|e| ApplicationError::StartDebugServer(e, listen))?; + DebugServer::new(listen).map_err(|e| ProgramError::StartDebugServer(e, listen))?; let debugger = debug_server .accept() - .map_err(ApplicationError::CreateDebugClient)?; + .map_err(ProgramError::CreateDebugClient)?; Some(debugger) } else { @@ -177,27 +176,31 @@ fn run_vmm(args: &CliArgs) -> Result<(), ApplicationError> { }; // Setup VMM screen. + let mut el = EventLoop::::with_user_event(); + let screen = graphics .create_screen(&profile) - .map_err(|e| ApplicationError::CreateScreen(Box::new(e)))?; + .map_err(|e| ProgramError::CreateScreen(Box::new(e)))?; // Start VMM. + let el = el.build().map_err(ProgramError::CreateVmmEventLoop)?; + std::thread::scope(|scope| { let vmm = Vmm::new( VmmArgs { profile: &profile, kernel, debugger, + el: el.create_proxy(), }, - VmmHandler {}, scope, ) - .map_err(ApplicationError::StartVmm)?; + .map_err(ProgramError::StartVmm)?; // Run the screen. screen .run() - .map_err(|e| ApplicationError::RunScreen(Box::new(e)))?; + .map_err(|e| ProgramError::RunScreen(Box::new(e)))?; Ok(()) }) @@ -207,9 +210,9 @@ fn run_launcher( graphics: &impl Graphics, data: &Arc, profiles: Vec, -) -> Result, ApplicationError> { +) -> Result, ProgramError> { // Create window and register callback handlers. - let win = MainWindow::new().map_err(ApplicationError::CreateMainWindow)?; + let win = MainWindow::new().map_err(ProgramError::CreateMainWindow)?; let resolutions = Rc::new(ResolutionModel::default()); let profiles = Rc::new(ProfileModel::new(profiles, resolutions.clone())); let exit = Rc::new(Cell::new(None)); @@ -293,7 +296,7 @@ fn run_launcher( profiles.select(row, &win); // Run the window. - win.run().map_err(ApplicationError::RunMainWindow)?; + win.run().map_err(ProgramError::RunMainWindow)?; // Update selected profile. let profile = win.get_selected_profile(); @@ -318,7 +321,7 @@ fn run_launcher( /// Program arguments parsed from command line. #[derive(Parser)] #[command(about = None)] -struct CliArgs { +struct ProgramArgs { #[arg(long, value_enum, hide = true)] mode: Option, @@ -337,43 +340,15 @@ enum ExitAction { RunDebug(SocketAddrV4), } -/// Encapsulates arguments for [`Vmm::new()`]. -struct VmmArgs<'a> { - profile: &'a Profile, - kernel: PathBuf, - debugger: Option, -} - -/// Provides method to handle VMM event. -struct VmmHandler {} - -impl self::vmm::VmmHandler for VmmHandler { - fn error(&self, cpu: usize, reason: impl Into>) { - todo!() - } - - fn exiting(&self, success: bool) { - todo!() - } - - fn log(&self, ty: ConsoleType, msg: &str) { - todo!() - } - - fn breakpoint(&self, stop: Option>) { - todo!() - } -} - /// Mode of our program. #[derive(Clone, ValueEnum)] enum ProgramMode { PanicHandler, } -/// Represents an error when [`run()`] fails. +/// Represents an error when our program fails. #[derive(Debug, Error)] -enum ApplicationError { +enum ProgramError { #[error("couldn't spawn panic handler process")] SpawnPanicHandler(#[source] std::io::Error), @@ -420,6 +395,9 @@ enum ApplicationError { #[error("couldn't create VMM screen")] CreateScreen(#[source] Box), + #[error("couldn't create VMM event loop")] + CreateVmmEventLoop(#[source] EventLoopError), + #[error("couldn't start VMM")] StartVmm(#[source] VmmError), diff --git a/gui/src/panic/mod.rs b/gui/src/panic/mod.rs index e9eed05ea..0f44a41fe 100644 --- a/gui/src/panic/mod.rs +++ b/gui/src/panic/mod.rs @@ -1,5 +1,5 @@ use crate::ui::ErrorWindow; -use crate::ApplicationError; +use crate::ProgramError; use serde::{Deserialize, Serialize}; use slint::ComponentHandle; use std::borrow::Cow; @@ -9,13 +9,13 @@ use std::path::Path; use std::process::{Child, Command, Stdio}; use std::sync::Mutex; -pub fn spawn_handler(exe: &Path) -> Result<(), ApplicationError> { +pub fn spawn_handler(exe: &Path) -> Result<(), ProgramError> { // Spawn the process in panic handler mode. let ph = Command::new(exe) .args(["--mode", "panic-handler"]) .stdin(Stdio::piped()) .spawn() - .map_err(ApplicationError::SpawnPanicHandler)?; + .map_err(ProgramError::SpawnPanicHandler)?; // Set panic hook to send panic to the handler. let ph = Mutex::new(Some(PanicHandler(ph))); @@ -25,7 +25,7 @@ pub fn spawn_handler(exe: &Path) -> Result<(), ApplicationError> { Ok(()) } -pub fn run_handler() -> Result<(), ApplicationError> { +pub fn run_handler() -> Result<(), ProgramError> { use std::io::ErrorKind; // Wait for panic info. @@ -34,7 +34,7 @@ pub fn run_handler() -> Result<(), ApplicationError> { let info: PanicInfo = match ciborium::from_reader(&mut stdin) { Ok(v) => v, Err(ciborium::de::Error::Io(e)) if e.kind() == ErrorKind::UnexpectedEof => return Ok(()), - Err(e) => return Err(ApplicationError::ReadPanicInfo(e)), + Err(e) => return Err(ProgramError::ReadPanicInfo(e)), }; // Display panic info. diff --git a/gui/src/vmm/cpu/aarch64.rs b/gui/src/vmm/cpu/aarch64.rs index 37dd25cc0..7195c2580 100644 --- a/gui/src/vmm/cpu/aarch64.rs +++ b/gui/src/vmm/cpu/aarch64.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use super::{CpuManager, GdbError}; use crate::hv::Hypervisor; -use crate::vmm::VmmHandler; use gdbstub::target::ext::base::BaseOps; use gdbstub::target::ext::breakpoints::{ Breakpoints, BreakpointsOps, SwBreakpoint, SwBreakpointOps, @@ -13,7 +12,7 @@ pub type GdbRegs = gdbstub_arch::aarch64::reg::AArch64CoreRegs; pub(super) const BREAKPOINT_SIZE: NonZero = unsafe { NonZero::new_unchecked(4) }; -impl<'a, 'b, H: Hypervisor, E: VmmHandler> gdbstub::target::Target for CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> gdbstub::target::Target for CpuManager<'a, 'b, H> { type Arch = gdbstub_arch::aarch64::AArch64; type Error = GdbError; @@ -26,13 +25,13 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> gdbstub::target::Target for CpuManage } } -impl<'a, 'b, H: Hypervisor, E: VmmHandler> Breakpoints for CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> Breakpoints for CpuManager<'a, 'b, H> { fn support_sw_breakpoint(&mut self) -> Option> { Some(self) } } -impl<'a, 'b, H: Hypervisor, E: VmmHandler> SwBreakpoint for CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> SwBreakpoint for CpuManager<'a, 'b, H> { fn add_sw_breakpoint(&mut self, addr: u64, kind: usize) -> TargetResult { todo!() } diff --git a/gui/src/vmm/cpu/mod.rs b/gui/src/vmm/cpu/mod.rs index 23ccf5f99..0b83ab636 100644 --- a/gui/src/vmm/cpu/mod.rs +++ b/gui/src/vmm/cpu/mod.rs @@ -5,7 +5,7 @@ use self::controller::CpuController; use self::debug::{DebugReq, DebugRes, Debugger}; use super::hw::{DeviceContext, DeviceTree}; use super::ram::RamMap; -use super::VmmHandler; +use super::VmmEvent; use crate::hv::{Cpu, CpuDebug, CpuExit, CpuIo, CpuRun, CpuStates, Hypervisor}; use gdbstub::common::{Signal, Tid}; use gdbstub::stub::MultiThreadStopReason; @@ -21,6 +21,7 @@ use std::ops::Deref; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use thiserror::Error; +use winit::event_loop::EventLoopProxy; #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] @@ -29,9 +30,9 @@ mod controller; mod debug; /// Manage all virtual CPUs. -pub struct CpuManager<'a, 'b, H, E> { +pub struct CpuManager<'a, 'b, H> { hv: Arc, - handler: Arc, + el: EventLoopProxy, scope: &'a std::thread::Scope<'a, 'b>, devices: Arc, cpus: Vec>, @@ -40,20 +41,20 @@ pub struct CpuManager<'a, 'b, H, E> { shutdown: Arc, } -impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> CpuManager<'a, 'b, H> { const GDB_ENOENT: u8 = 2; const GDB_EFAULT: u8 = 14; pub fn new( hv: Arc, - handler: Arc, + el: EventLoopProxy, scope: &'a std::thread::Scope<'a, 'b>, devices: Arc, shutdown: Arc, ) -> Self { Self { hv, - handler, + el, scope, devices, cpus: Vec::new(), @@ -67,7 +68,7 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { // Setup arguments. let args = Args { hv: self.hv.clone(), - handler: self.handler.clone(), + el: self.el.clone(), devices: self.devices.clone(), breakpoint: self.breakpoint.clone(), shutdown: self.shutdown.clone(), @@ -103,18 +104,22 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { } } - fn main_cpu(args: Args, debug: Option, entry: usize, map: RamMap) { + fn main_cpu(args: Args, debug: Option, entry: usize, map: RamMap) { // Create CPU. let mut cpu = match args.hv.create_cpu(0) { Ok(v) => v, Err(e) => { - args.handler.error(0, CpuError::Create(Box::new(e))); + args.el + .send_event(VmmEvent::error(0, CpuError::Create(Box::new(e)))) + .unwrap(); return; } }; if let Err(e) = super::arch::setup_main_cpu(&mut cpu, entry, map, args.hv.cpu_features()) { - args.handler.error(0, CpuError::Setup(Box::new(e))); + args.el + .send_event(VmmEvent::error(0, CpuError::Setup(Box::new(e)))) + .unwrap(); return; } @@ -124,7 +129,7 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { Ok(true) => {} Ok(false) => return, Err(e) => { - args.handler.error(0, e); + args.el.send_event(VmmEvent::error(0, e)).unwrap(); return; } } @@ -134,15 +139,17 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { Self::run_cpu(&args, debug, cpu, 0); } - fn run_cpu<'c>(args: &'c Args, debug: Option, mut cpu: H::Cpu<'c>, id: usize) { + fn run_cpu<'c>(args: &'c Args, debug: Option, mut cpu: H::Cpu<'c>, id: usize) { // Build device contexts for this CPU. let hv = args.hv.deref(); - let handler = args.handler.deref(); + let el = &args.el; let mut devices = BTreeMap::>>::new(); let t = &args.devices; - Device::insert(&mut devices, t.console(), |d| d.create_context(hv, handler)); - Device::insert(&mut devices, t.vmm(), |d| d.create_context(handler)); + Device::insert(&mut devices, t.console(), |d| { + d.create_context(hv, el.clone()) + }); + Device::insert(&mut devices, t.vmm(), |d| d.create_context(el.clone())); // Dispatch CPU events until shutdown. let e = 'main: loop { @@ -195,7 +202,7 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { }; if let Some(e) = e { - args.handler.error(id, e); + args.el.send_event(VmmEvent::error(id, e)).unwrap(); } // Shutdown other CPUs. @@ -203,7 +210,7 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { } fn handle_exit<'c, C: Cpu>( - args: &'c Args, + args: &'c Args, debugger: Option<&Debugger>, devices: &mut BTreeMap>, exit: C::Exit<'_>, @@ -259,7 +266,7 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { } fn handle_breakpoint( - args: &Args, + args: &Args, debug: &Debugger, cpu: &mut impl Cpu, stop: Option>, @@ -267,7 +274,7 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { // Notify GUI. We need to allow only one CPU to enter the debugger dispatch loop. let lock = args.breakpoint.lock().unwrap(); - args.handler.breakpoint(stop); + args.el.send_event(VmmEvent::Breakpoint(stop)).unwrap(); // Wait for command from debugger thread. loop { @@ -401,7 +408,7 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { } } -impl<'a, 'b, H: Hypervisor, E: VmmHandler> MultiThreadBase for CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> MultiThreadBase for CpuManager<'a, 'b, H> { fn read_registers(&mut self, regs: &mut GdbRegs, tid: Tid) -> TargetResult<(), Self> { let cpu = self .cpus @@ -485,13 +492,13 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> MultiThreadBase for CpuManager<'a, 'b } } -impl<'a, 'b, H: Hypervisor, E: VmmHandler> ThreadExtraInfo for CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> ThreadExtraInfo for CpuManager<'a, 'b, H> { fn thread_extra_info(&self, tid: Tid, buf: &mut [u8]) -> Result { todo!() } } -impl<'a, 'b, H: Hypervisor, E: VmmHandler> MultiThreadResume for CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> MultiThreadResume for CpuManager<'a, 'b, H> { fn resume(&mut self) -> Result<(), Self::Error> { self.release(); @@ -516,9 +523,9 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> MultiThreadResume for CpuManager<'a, } /// Encapsulates arguments for a function to run a CPU. -struct Args { +struct Args { hv: Arc, - handler: Arc, + el: EventLoopProxy, devices: Arc, breakpoint: Arc>, shutdown: Arc, @@ -552,34 +559,34 @@ impl<'a, C: Cpu> Device<'a, C> { #[derive(Debug, Error)] pub enum CpuError { #[error("couldn't create vCPU")] - Create(#[source] Box), + Create(#[source] Box), #[error("couldn't setup vCPU")] - Setup(#[source] Box), + Setup(#[source] Box), #[error("couldn't run vCPU")] - Run(#[source] Box), + Run(#[source] Box), #[error("couldn't execute a VM exited event on a {0}")] - DeviceExitHandler(String, #[source] Box), + DeviceExitHandler(String, #[source] Box), #[error("the vCPU attempt to execute a memory-mapped I/O on a non-mapped address {0:#x}")] MmioAddr(usize), #[error("couldn't execute a memory-mapped I/O on a {0}")] - Mmio(String, #[source] Box), + Mmio(String, #[source] Box), #[error("couldn't get vCPU states")] - GetStates(#[source] Box), + GetStates(#[source] Box), #[error("couldn't read {0} register")] - ReadReg(&'static str, #[source] Box), + ReadReg(&'static str, #[source] Box), #[error("couldn't translate address {0:#x}")] - TranslateAddr(usize, #[source] Box), + TranslateAddr(usize, #[source] Box), #[error("couldn't execute a post VM exit on a {0}")] - DevicePostExitHandler(String, #[source] Box), + DevicePostExitHandler(String, #[source] Box), } /// Implementation of [`gdbstub::target::Target::Error`]. diff --git a/gui/src/vmm/cpu/x86_64.rs b/gui/src/vmm/cpu/x86_64.rs index b0b597558..347a9205d 100644 --- a/gui/src/vmm/cpu/x86_64.rs +++ b/gui/src/vmm/cpu/x86_64.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use super::{CpuManager, GdbError}; use crate::hv::Hypervisor; -use crate::vmm::VmmHandler; use gdbstub::target::ext::base::BaseOps; use gdbstub::target::ext::breakpoints::{ Breakpoints, BreakpointsOps, SwBreakpoint, SwBreakpointOps, @@ -15,7 +14,7 @@ pub type GdbRegs = gdbstub_arch::x86::reg::X86_64CoreRegs; pub(super) const BREAKPOINT_SIZE: NonZero = unsafe { NonZero::new_unchecked(1) }; -impl<'a, 'b, H: Hypervisor, E: VmmHandler> gdbstub::target::Target for CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> gdbstub::target::Target for CpuManager<'a, 'b, H> { type Arch = X86_64_SSE; type Error = GdbError; @@ -28,13 +27,13 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> gdbstub::target::Target for CpuManage } } -impl<'a, 'b, H: Hypervisor, E: VmmHandler> Breakpoints for CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> Breakpoints for CpuManager<'a, 'b, H> { fn support_sw_breakpoint(&mut self) -> Option> { Some(self) } } -impl<'a, 'b, H: Hypervisor, E: VmmHandler> SwBreakpoint for CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> SwBreakpoint for CpuManager<'a, 'b, H> { fn add_sw_breakpoint(&mut self, addr: u64, _kind: usize) -> TargetResult { let Entry::Vacant(entry) = self.sw_breakpoints.entry(addr) else { return Ok(false); diff --git a/gui/src/vmm/debug/mod.rs b/gui/src/vmm/debug/mod.rs index dba70487b..4a328755d 100644 --- a/gui/src/vmm/debug/mod.rs +++ b/gui/src/vmm/debug/mod.rs @@ -1,6 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use super::cpu::{CpuManager, GdbError}; -use super::VmmHandler; use crate::debug::DebugClient; use crate::hv::Hypervisor; use gdbstub::stub::state_machine::state::{Idle, Running}; @@ -8,22 +7,22 @@ use gdbstub::stub::state_machine::{GdbStubStateMachine, GdbStubStateMachineInner use gdbstub::stub::MultiThreadStopReason; use thiserror::Error; -impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { +impl<'a, 'b, H: Hypervisor> CpuManager<'a, 'b, H> { pub(super) fn dispatch_gdb_idle( &mut self, mut state: GdbStubStateMachineInner< 'static, - Idle>, - CpuManager<'a, 'b, H, E>, + Idle>, + CpuManager<'a, 'b, H>, DebugClient, >, ) -> Result< Result< - GdbStubStateMachine<'static, CpuManager<'a, 'b, H, E>, DebugClient>, + GdbStubStateMachine<'static, CpuManager<'a, 'b, H>, DebugClient>, GdbStubStateMachineInner< 'static, - Idle>, - CpuManager<'a, 'b, H, E>, + Idle>, + CpuManager<'a, 'b, H>, DebugClient, >, >, @@ -43,17 +42,12 @@ impl<'a, 'b, H: Hypervisor, E: VmmHandler> CpuManager<'a, 'b, H, E> { pub(super) fn dispatch_gdb_running( &mut self, - mut state: GdbStubStateMachineInner< - 'static, - Running, - CpuManager<'a, 'b, H, E>, - DebugClient, - >, + mut state: GdbStubStateMachineInner<'static, Running, CpuManager<'a, 'b, H>, DebugClient>, stop: Option>, ) -> Result< Result< - GdbStubStateMachine<'static, CpuManager<'a, 'b, H, E>, DebugClient>, - GdbStubStateMachineInner<'static, Running, CpuManager<'a, 'b, H, E>, DebugClient>, + GdbStubStateMachine<'static, CpuManager<'a, 'b, H>, DebugClient>, + GdbStubStateMachineInner<'static, Running, CpuManager<'a, 'b, H>, DebugClient>, >, DispatchGdbRunningError, > { diff --git a/gui/src/vmm/hw/console/context.rs b/gui/src/vmm/hw/console/context.rs index 2d19c3c42..5e885b7d8 100644 --- a/gui/src/vmm/hw/console/context.rs +++ b/gui/src/vmm/hw/console/context.rs @@ -2,36 +2,40 @@ use super::Console; use crate::hv::{Cpu, CpuExit, CpuIo, Hypervisor}; use crate::vmm::hw::{read_ptr, read_u8, read_usize, DeviceContext, MmioError}; -use crate::vmm::VmmHandler; +use crate::vmm::VmmEvent; use obconf::{ConsoleMemory, ConsoleType}; use std::error::Error; use std::mem::offset_of; use std::num::NonZero; use thiserror::Error; +use winit::event_loop::EventLoopProxy; /// Implementation of [`DeviceContext`]. -pub struct Context<'a, H, E> { +pub struct Context<'a, H> { dev: &'a Console, hv: &'a H, - handler: &'a E, + el: EventLoopProxy, msg_len: Option>, msg: Vec, } -impl<'a, H, E> Context<'a, H, E> { - pub fn new(dev: &'a Console, hv: &'a H, handler: &'a E) -> Self { +impl<'a, H> Context<'a, H> { + pub fn new(dev: &'a Console, hv: &'a H, el: EventLoopProxy) -> Self { Self { dev, hv, - handler, + el, msg_len: None, msg: Vec::new(), } } } -impl DeviceContext for Context<'_, H, E> { - fn mmio(&mut self, exit: &mut as CpuExit>::Io) -> Result> { +impl DeviceContext for Context<'_, H> { + fn mmio( + &mut self, + exit: &mut as CpuExit>::Io, + ) -> Result> { // Check field. let off = exit.addr() - self.dev.addr; @@ -67,7 +71,7 @@ impl DeviceContext for Context<'_, H, E // single allocation when the handler clone the string. let msg = std::str::from_utf8(&self.msg).map_err(|_| ExecError::InvalidMsg)?; - self.handler.log(ty, msg); + self.el.send_event(VmmEvent::Log(ty, msg.into())).unwrap(); self.msg.clear(); } else { return Err(Box::new(ExecError::UnknownField(off))); diff --git a/gui/src/vmm/hw/console/mod.rs b/gui/src/vmm/hw/console/mod.rs index 06f627b8e..9a1ecd31b 100644 --- a/gui/src/vmm/hw/console/mod.rs +++ b/gui/src/vmm/hw/console/mod.rs @@ -2,9 +2,10 @@ use self::context::Context; use super::{Device, DeviceContext}; use crate::hv::Hypervisor; -use crate::vmm::VmmHandler; +use crate::vmm::VmmEvent; use obconf::ConsoleMemory; use std::num::NonZero; +use winit::event_loop::EventLoopProxy; mod context; @@ -24,12 +25,12 @@ impl Console { Self { addr, len } } - pub fn create_context<'a, H: Hypervisor, E: VmmHandler>( + pub fn create_context<'a, H: Hypervisor>( &'a self, hv: &'a H, - handler: &'a E, + el: EventLoopProxy, ) -> Box> + 'a> { - Box::new(Context::new(self, hv, handler)) + Box::new(Context::new(self, hv, el)) } } diff --git a/gui/src/vmm/hw/mod.rs b/gui/src/vmm/hw/mod.rs index 1d2544b62..550d0427f 100644 --- a/gui/src/vmm/hw/mod.rs +++ b/gui/src/vmm/hw/mod.rs @@ -114,16 +114,19 @@ pub trait Device: Send + Sync { /// Context for a CPU to execute operations on a virtual device. pub trait DeviceContext { /// Execute immeditately after the VM exited. - fn exited(&mut self, cpu: &mut C) -> Result> { + fn exited(&mut self, cpu: &mut C) -> Result> { let _ = cpu; Ok(true) } /// Execute only if the CPU read or write into this device address. - fn mmio(&mut self, exit: &mut as CpuExit>::Io) -> Result>; + fn mmio( + &mut self, + exit: &mut as CpuExit>::Io, + ) -> Result>; /// Always execute after the exited event has been handled (before enter the VM again). - fn post(&mut self, cpu: &mut C) -> Result> { + fn post(&mut self, cpu: &mut C) -> Result> { let _ = cpu; Ok(true) } @@ -156,7 +159,7 @@ enum MmioError { InvalidData, #[error("couldn't translate {0:#x} to physical address")] - TranslateVaddrFailed(usize, #[source] Box), + TranslateVaddrFailed(usize, #[source] Box), #[error("address {vaddr:#x} ({paddr:#x}) is not allocated")] InvalidAddr { vaddr: usize, paddr: usize }, diff --git a/gui/src/vmm/hw/vmm/context.rs b/gui/src/vmm/hw/vmm/context.rs index 0ac85cb17..db21ba3b7 100644 --- a/gui/src/vmm/hw/vmm/context.rs +++ b/gui/src/vmm/hw/vmm/context.rs @@ -2,26 +2,30 @@ use super::Vmm; use crate::hv::{Cpu, CpuExit, CpuIo}; use crate::vmm::hw::{read_u8, DeviceContext, MmioError}; -use crate::vmm::VmmHandler; +use crate::vmm::VmmEvent; use obconf::{KernelExit, VmmMemory}; use std::error::Error; use std::mem::offset_of; use thiserror::Error; +use winit::event_loop::EventLoopProxy; /// Implementation of [`DeviceContext`]. -pub struct Context<'a, E> { +pub struct Context<'a> { dev: &'a Vmm, - handler: &'a E, + el: EventLoopProxy, } -impl<'a, E> Context<'a, E> { - pub fn new(dev: &'a Vmm, handler: &'a E) -> Self { - Self { dev, handler } +impl<'a> Context<'a> { + pub fn new(dev: &'a Vmm, el: EventLoopProxy) -> Self { + Self { dev, el } } } -impl DeviceContext for Context<'_, E> { - fn mmio(&mut self, exit: &mut as CpuExit>::Io) -> Result> { +impl DeviceContext for Context<'_> { + fn mmio( + &mut self, + exit: &mut as CpuExit>::Io, + ) -> Result> { // Check field. let off = exit.addr() - self.dev.addr; @@ -31,7 +35,11 @@ impl DeviceContext for Context<'_, E> { .try_into() .map_err(|_| Box::new(ExecError::InvalidExit(exit)))?; - self.handler.exiting(exit == KernelExit::Success); + self.el + .send_event(VmmEvent::Exiting { + success: exit == KernelExit::Success, + }) + .unwrap(); Ok(false) } else { diff --git a/gui/src/vmm/hw/vmm/mod.rs b/gui/src/vmm/hw/vmm/mod.rs index f65d60a01..068b87c49 100644 --- a/gui/src/vmm/hw/vmm/mod.rs +++ b/gui/src/vmm/hw/vmm/mod.rs @@ -2,9 +2,10 @@ use self::context::Context; use super::{Device, DeviceContext}; use crate::hv::Cpu; -use crate::vmm::VmmHandler; +use crate::vmm::VmmEvent; use obconf::VmmMemory; use std::num::NonZero; +use winit::event_loop::EventLoopProxy; mod context; @@ -24,11 +25,11 @@ impl Vmm { Self { addr, len } } - pub fn create_context<'a, C: Cpu, E: VmmHandler>( + pub fn create_context<'a, C: Cpu>( &'a self, - handler: &'a E, + el: EventLoopProxy, ) -> Box + 'a> { - Box::new(Context::new(self, handler)) + Box::new(Context::new(self, el)) } } diff --git a/gui/src/vmm/mod.rs b/gui/src/vmm/mod.rs index f3ebdbc70..5cd8b2496 100644 --- a/gui/src/vmm/mod.rs +++ b/gui/src/vmm/mod.rs @@ -7,7 +7,7 @@ use self::kernel::{ use self::ram::RamBuilder; use crate::debug::DebugClient; use crate::hv::{Hypervisor, Ram}; -use crate::VmmArgs; +use crate::profile::Profile; use cpu::GdbError; use gdbstub::common::Signal; use gdbstub::stub::state_machine::GdbStubStateMachine; @@ -23,6 +23,7 @@ use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use thiserror::Error; +use winit::event_loop::EventLoopProxy; #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] @@ -56,20 +57,14 @@ fn get_page_size() -> Result, std::io::Error> { } /// Manage a virtual machine that run the kernel. -pub struct Vmm<'a, 'b, E: VmmHandler> { - cpu: CpuManager<'a, 'b, crate::hv::Default, E>, // Drop first. - gdb: Option< - GdbStubStateMachine<'static, CpuManager<'a, 'b, crate::hv::Default, E>, DebugClient>, - >, +pub struct Vmm<'a, 'b> { + cpu: CpuManager<'a, 'b, crate::hv::Default>, // Drop first. + gdb: Option, DebugClient>>, shutdown: Arc, } -impl<'a, 'b, E: VmmHandler> Vmm<'a, 'b, E> { - pub fn new( - args: VmmArgs<'b>, - handler: E, - scope: &'a std::thread::Scope<'a, 'b>, - ) -> Result { +impl<'a, 'b> Vmm<'a, 'b> { + pub fn new(args: VmmArgs<'b>, scope: &'a std::thread::Scope<'a, 'b>) -> Result { let path = &args.kernel; let debugger = args.debugger; @@ -296,13 +291,7 @@ impl<'a, 'b, E: VmmHandler> Vmm<'a, 'b, E> { // Setup CPU manager. let shutdown = Arc::new(AtomicBool::new(false)); - let mut cpu = CpuManager::new( - Arc::new(hv), - Arc::new(handler), - scope, - devices, - shutdown.clone(), - ); + let mut cpu = CpuManager::new(Arc::new(hv), args.el, scope, devices, shutdown.clone()); // Setup GDB stub. let gdb = debugger @@ -375,7 +364,7 @@ impl<'a, 'b, E: VmmHandler> Vmm<'a, 'b, E> { } } -impl<'a, 'b, E: VmmHandler> Drop for Vmm<'a, 'b, E> { +impl<'a, 'b> Drop for Vmm<'a, 'b> { fn drop(&mut self) { // Set shutdown flag before dropping the other fields so their background thread can stop // before they try to join with it. @@ -383,12 +372,35 @@ impl<'a, 'b, E: VmmHandler> Drop for Vmm<'a, 'b, E> { } } -/// Provides methods to handle VMM events. -pub trait VmmHandler: Send + Sync + 'static { - fn error(&self, cpu: usize, reason: impl Into>); - fn exiting(&self, success: bool); - fn log(&self, ty: ConsoleType, msg: &str); - fn breakpoint(&self, stop: Option>); +/// Encapsulates arguments for [`Vmm::new()`]. +pub struct VmmArgs<'a> { + pub profile: &'a Profile, + pub kernel: PathBuf, + pub debugger: Option, + pub el: EventLoopProxy, +} + +/// Event from VMM. +#[derive(Debug)] +pub enum VmmEvent { + Error { + cpu: usize, + reason: Box, + }, + Exiting { + success: bool, + }, + Log(ConsoleType, String), + Breakpoint(Option>), +} + +impl VmmEvent { + fn error(cpu: usize, reason: impl Error + Send + 'static) -> Self { + Self::Error { + cpu, + reason: Box::new(reason), + } + } } pub enum DispatchDebugResult { diff --git a/src/obconf/src/env/vm.rs b/src/obconf/src/env/vm.rs index 4a6155575..8af8ed4db 100644 --- a/src/obconf/src/env/vm.rs +++ b/src/obconf/src/env/vm.rs @@ -50,7 +50,7 @@ pub struct ConsoleMemory { /// Type of console message. #[cfg(feature = "virt")] #[repr(u8)] -#[derive(Clone, Copy, num_enum::IntoPrimitive, num_enum::TryFromPrimitive)] +#[derive(Debug, Clone, Copy, num_enum::IntoPrimitive, num_enum::TryFromPrimitive)] pub enum ConsoleType { Info, Warn,