diff --git a/src/core/Cargo.toml b/src/core/Cargo.toml index 962aded3e..fad4c0924 100644 --- a/src/core/Cargo.toml +++ b/src/core/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" crate-type = ["staticlib"] [dependencies] +bitflags = "2.6.0" humansize = "2.1.3" libc = "0.2.155" obfw = { git = "https://github.com/obhq/firmware-dumper.git", features = ["read", "std"] } diff --git a/src/core/src/vmm/cpu.rs b/src/core/src/vmm/cpu.rs index 50938bcaa..e43efd2a6 100644 --- a/src/core/src/vmm/cpu.rs +++ b/src/core/src/vmm/cpu.rs @@ -56,6 +56,12 @@ pub trait CpuStates { #[cfg(target_arch = "x86_64")] fn set_ss(&mut self, p: bool); + + #[cfg(target_arch = "aarch64")] + fn set_sp(&mut self, v: usize); + + #[cfg(target_arch = "aarch64")] + fn set_pc(&mut self, v: usize); } /// Contains information when VM exited. diff --git a/src/core/src/vmm/linux/cpu.rs b/src/core/src/vmm/linux/cpu.rs index 0f1117836..a1888d49b 100644 --- a/src/core/src/vmm/linux/cpu.rs +++ b/src/core/src/vmm/linux/cpu.rs @@ -173,6 +173,16 @@ impl<'a> CpuStates for KvmStates<'a> { self.sregs.ss.present = p.into(); self.sdirty = true; } + + #[cfg(target_arch = "aarch64")] + fn set_sp(&mut self, v: usize) { + todo!() + } + + #[cfg(target_arch = "aarch64")] + fn set_pc(&mut self, v: usize) { + todo!() + } } impl<'a> Drop for KvmStates<'a> { diff --git a/src/core/src/vmm/linux/regs.rs b/src/core/src/vmm/linux/regs.rs index 33e8e4ce3..8f2707c2f 100644 --- a/src/core/src/vmm/linux/regs.rs +++ b/src/core/src/vmm/linux/regs.rs @@ -25,6 +25,34 @@ pub struct KvmRegs { pub rflags: u64, } +#[cfg(target_arch = "aarch64")] +#[repr(C)] +struct KvmRegs { + pub regs: UserPtRegs, + pub sp_el1: usize, + pub elr_el1: usize, + pub sprs: [usize; 5], + pub fp_regs: UserFpRegs, +} + +#[cfg(target_arch = "aarch64")] +#[repr(C)] +struct UserPtRegs { + pub regs: [usize; 31], + pub sp: usize, + pub pc: usize, + pub pstate: usize, +} + +#[cfg(target_arch = "aarch64")] +#[repr(C)] +struct UserFpSimdState { + pub vregs: [u128; 32], + pub fpsr: u32, + pub fpcr: u32, + pub reserved: [u32; 2], +} + #[cfg(target_arch = "x86_64")] #[repr(C)] pub struct KvmSpecialRegs { @@ -52,6 +80,10 @@ pub struct KvmSpecialRegs { pub interrupt_bitmap: [u64; 4], } +#[cfg(target_arch = "aarch64")] +#[repr(C)] +struct KvmSpecialRegs {} + #[cfg(target_arch = "x86_64")] #[repr(C)] pub struct KvmSegment { diff --git a/src/core/src/vmm/macos/cpu.rs b/src/core/src/vmm/macos/cpu.rs index a699fae2f..be6f5be83 100644 --- a/src/core/src/vmm/macos/cpu.rs +++ b/src/core/src/vmm/macos/cpu.rs @@ -1,8 +1,37 @@ use crate::vmm::{Cpu, CpuExit, CpuStates}; use hv_sys::hv_vcpu_destroy; use std::marker::PhantomData; +use std::mem::MaybeUninit; +use std::num::NonZero; use thiserror::Error; +#[cfg(target_arch = "x86_64")] +use crate::vmm::CpuIo; + +#[cfg(target_arch = "aarch64")] +#[allow(non_camel_case_types)] +type hv_vcpu_t = hv_sys::hv_vcpu_t; + +#[cfg(target_arch = "x86_64")] +#[allow(non_camel_case_types)] +type hv_vcpu_t = hv_sys::hv_vcpuid_t; + +macro_rules! wrap_return { + ($ret:expr) => { + match NonZero::new($ret) { + Some(errno) => Err(errno), + None => Ok(()), + } + }; + + ($ret:expr, $err:path) => { + match NonZero::new($ret) { + Some(errno) => Err($err(errno)), + None => Ok(()), + } + }; +} + /// Implementation of [`Cpu`] for Hypervisor Framework. pub struct HfCpu<'a> { id: usize, @@ -18,77 +47,241 @@ impl<'a> HfCpu<'a> { vm: PhantomData, } } -} -impl<'a> Drop for HfCpu<'a> { - fn drop(&mut self) { - let ret = unsafe { hv_vcpu_destroy(self.instance) }; + #[cfg(target_arch = "x86_64")] + fn read_register( + &self, + register: hv_sys::hv_x86_reg_t, + ) -> Result> { + let mut value = MaybeUninit::::uninit(); - if ret != 0 { - panic!("hv_vcpu_destroy() fails with {ret:#x}"); - } + wrap_return!(unsafe { + hv_sys::hv_vcpu_read_register(self.instance, register, value.as_mut_ptr().cast()) + })?; + + Ok(unsafe { value.assume_init() }) + } + + #[cfg(target_arch = "aarch64")] + fn read_register( + &self, + register: hv_sys::hv_reg_t, + ) -> Result> { + let mut value = MaybeUninit::::uninit(); + + wrap_return!(unsafe { + hv_sys::hv_vcpu_get_reg(self.instance, register, value.as_mut_ptr().cast()) + })?; + + Ok(unsafe { value.assume_init() }) + } + + #[cfg(target_arch = "x86_64")] + fn write_register( + &mut self, + register: hv_sys::hv_x86_reg_t, + value: usize, + ) -> Result<(), NonZero> { + wrap_return!(unsafe { + hv_sys::hv_vcpu_write_register(self.instance, register, value as u64) + }) + } + + #[cfg(target_arch = "x86_64")] + fn write_vmcs(&mut self, field: u32, value: u64) -> Result<(), NonZero> { + wrap_return!(unsafe { hv_sys::hv_vmx_vcpu_write_vmcs(self.instance, field, value) }) } } impl<'a> Cpu for HfCpu<'a> { - type States<'b> = HfStates<'b> where Self: 'b; + type States<'b> = HfStates<'b, 'a> where Self: 'b; type GetStatesErr = GetStatesError; type Exit<'b> = HfExit<'b> where Self: 'b; - type RunErr = std::io::Error; + type RunErr = RunError; fn id(&self) -> usize { self.id } + #[cfg(target_arch = "x86_64")] + fn states(&mut self) -> Result, Self::GetStatesErr> { + let cr0 = self + .read_register(hv_sys::hv_x86_reg_t_HV_X86_CR0) + .map_err(GetStatesError::ReadCr0)?; + let cr4 = self + .read_register(hv_sys::hv_x86_reg_t_HV_X86_CR4) + .map_err(GetStatesError::ReadCr4)?; + + let mut cs = 0u64; + let mut ds = 0u64; + let mut es = 0u64; + let mut fs = 0u64; + let mut gs = 0u64; + let mut ss = 0u64; + + unsafe { + wrap_return!( + hv_sys::hv_vmx_vcpu_read_vmcs(self.instance, hv_sys::VMCS_GUEST_CS_AR, &mut cs), + GetStatesError::ReadCs + )?; + wrap_return!( + hv_sys::hv_vmx_vcpu_read_vmcs(self.instance, hv_sys::VMCS_GUEST_DS_AR, &mut ds), + GetStatesError::ReadDs + )?; + wrap_return!( + hv_sys::hv_vmx_vcpu_read_vmcs(self.instance, hv_sys::VMCS_GUEST_ES_AR, &mut es), + GetStatesError::ReadEs + )?; + wrap_return!( + hv_sys::hv_vmx_vcpu_read_vmcs(self.instance, hv_sys::VMCS_GUEST_FS_AR, &mut fs), + GetStatesError::ReadFs + )?; + wrap_return!( + hv_sys::hv_vmx_vcpu_read_vmcs(self.instance, hv_sys::VMCS_GUEST_GS_AR, &mut gs), + GetStatesError::ReadGs + )?; + wrap_return!( + hv_sys::hv_vmx_vcpu_read_vmcs(self.instance, hv_sys::VMCS_GUEST_SS_AR, &mut ss), + GetStatesError::ReadSs + )?; + } + + Ok(HfStates { + cpu: self, + dirty_flags: DirtyFlags::empty(), + rsp: 0, + rip: 0, + cr0: cr0.try_into().unwrap(), + cr3: 0, + cr4: cr4.try_into().unwrap(), + cs, + ds: ds.try_into().unwrap(), + es: es.try_into().unwrap(), + fs: fs.try_into().unwrap(), + gs: gs.try_into().unwrap(), + ss: ss.try_into().unwrap(), + }) + } + + #[cfg(target_arch = "aarch64")] fn states(&mut self) -> Result, Self::GetStatesErr> { todo!() } + #[cfg(target_arch = "x86_64")] + fn run(&mut self) -> Result, Self::RunErr> { + wrap_return!( + unsafe { hv_sys::hv_vcpu_run_until(self.instance, hv_sys::HV_DEADLINE_FOREVER) }, + RunError::Run + )?; + + let mut exit_reason = 0u64; + + wrap_return!( + unsafe { + hv_sys::hv_vmx_vcpu_read_vmcs( + self.instance, + hv_sys::VMCS_RO_EXIT_REASON, + &mut exit_reason, + ) + }, + RunError::ReadExitReason + )?; + + Ok(HfExit { + cpu: PhantomData, + exit_reason, + }) + } + + #[cfg(target_arch = "aarch64")] fn run(&mut self) -> Result, Self::RunErr> { todo!() } } -#[cfg(target_arch = "aarch64")] -type hv_vcpu_t = hv_sys::hv_vcpu_t; +impl<'a> Drop for HfCpu<'a> { + fn drop(&mut self) { + let ret = unsafe { hv_vcpu_destroy(self.instance) }; -#[cfg(target_arch = "x86_64")] -type hv_vcpu_t = hv_sys::hv_vcpuid_t; + if ret != 0 { + panic!("hv_vcpu_destroy() fails with {ret:#x}"); + } + } +} + +bitflags::bitflags! { + struct DirtyFlags: u16 { + const RSP = 0x0001; + const RIP = 0x0002; + const CR0 = 0x0004; + const CR3 = 0x0008; + const CR4 = 0x0010; + const CS = 0x0020; + const DS = 0x0040; + const ES = 0x0080; + const FS = 0x0100; + const GS = 0x0200; + const SS = 0x0400; + } +} /// Implementation of [`Cpu::States`] for Hypervisor Framework. -pub struct HfStates<'a> { - cpu: PhantomData<&'a mut HfCpu<'a>>, +pub struct HfStates<'a, 'b> { + cpu: &'a mut HfCpu<'b>, + dirty_flags: DirtyFlags, + + rsp: usize, + rip: usize, + + cr0: usize, + cr3: usize, + cr4: usize, + + cs: u64, + ds: usize, + es: usize, + fs: usize, + gs: usize, + ss: usize, } -impl<'a> CpuStates for HfStates<'a> { +impl<'a, 'b> CpuStates for HfStates<'a, 'b> { #[cfg(target_arch = "x86_64")] fn set_rsp(&mut self, v: usize) { - todo!() + self.rsp = v; + self.dirty_flags.insert(DirtyFlags::RSP); } #[cfg(target_arch = "x86_64")] fn set_rip(&mut self, v: usize) { - todo!() + self.rip = v; + self.dirty_flags.insert(DirtyFlags::RIP); } #[cfg(target_arch = "x86_64")] fn set_cr0(&mut self, v: usize) { - todo!() + self.cr0 = v; + self.dirty_flags.insert(DirtyFlags::CR0); } #[cfg(target_arch = "x86_64")] fn set_cr3(&mut self, v: usize) { - todo!() + self.cr3 = v; + self.dirty_flags.insert(DirtyFlags::CR3); } #[cfg(target_arch = "x86_64")] fn set_cr4(&mut self, v: usize) { - todo!() + self.cr4 = v; + self.dirty_flags.insert(DirtyFlags::CR4); } #[cfg(target_arch = "x86_64")] fn set_efer(&mut self, v: usize) { - todo!() + // LME and LMA bits + // There seems to be now way to actually set these, however they appear to be set by default + assert_eq!(v, 0x100 | 0x400); } #[cfg(target_arch = "x86_64")] @@ -120,25 +313,134 @@ impl<'a> CpuStates for HfStates<'a> { fn set_ss(&mut self, p: bool) { todo!() } + + #[cfg(target_arch = "aarch64")] + fn set_sp(&mut self, v: usize) { + todo!() + } + + #[cfg(target_arch = "aarch64")] + fn set_pc(&mut self, v: usize) { + todo!() + } +} + +impl<'a, 'b> Drop for HfStates<'a, 'b> { + #[cfg(target_arch = "x86_64")] + fn drop(&mut self) { + if self.dirty_flags.is_empty() { + return; + } + + if self.dirty_flags.contains(DirtyFlags::RIP) { + self.cpu + .write_register(hv_sys::hv_x86_reg_t_HV_X86_RIP, self.rip) + .unwrap(); + } + if self.dirty_flags.contains(DirtyFlags::RSP) { + self.cpu + .write_register(hv_sys::hv_x86_reg_t_HV_X86_RSP, self.rsp) + .unwrap(); + } + if self.dirty_flags.contains(DirtyFlags::CR0) { + self.cpu + .write_register(hv_sys::hv_x86_reg_t_HV_X86_CR0, self.cr0) + .unwrap(); + } + if self.dirty_flags.contains(DirtyFlags::CR3) { + self.cpu + .write_register(hv_sys::hv_x86_reg_t_HV_X86_CR3, self.cr3) + .unwrap(); + } + if self.dirty_flags.contains(DirtyFlags::CR4) { + self.cpu + .write_register(hv_sys::hv_x86_reg_t_HV_X86_CR4, self.cr4) + .unwrap(); + } + if self.dirty_flags.contains(DirtyFlags::CS) { + todo!() + } + if self.dirty_flags.contains(DirtyFlags::DS) { + todo!() + } + if self.dirty_flags.contains(DirtyFlags::ES) { + todo!() + } + if self.dirty_flags.contains(DirtyFlags::FS) { + todo!() + } + if self.dirty_flags.contains(DirtyFlags::GS) { + todo!() + } + if self.dirty_flags.contains(DirtyFlags::SS) { + todo!() + } + } + + #[cfg(target_arch = "aarch64")] + fn drop(&mut self) { + todo!() + } } /// Implementation of [`Cpu::Exit`] for Hypervisor Framework. pub struct HfExit<'a> { cpu: PhantomData<&'a mut HfCpu<'a>>, + #[cfg(target_arch = "x86_64")] + exit_reason: u64, } impl<'a> CpuExit for HfExit<'a> { #[cfg(target_arch = "x86_64")] fn is_hlt(&self) -> bool { - todo!() + match self.exit_reason.try_into() { + Ok(hv_sys::VMX_REASON_HLT) => true, + _ => false, + } } #[cfg(target_arch = "x86_64")] - fn is_io(&mut self) -> Option { - todo!() + fn is_io(&mut self) -> Option { + match self.exit_reason.try_into() { + Ok(hv_sys::VMX_REASON_IO) => todo!(), + _ => None, + } } } +/// Implementation of [`Cpu::RunErr`]. +#[derive(Debug, Error)] +pub enum RunError { + #[error("error running vcpu: {0:#x}")] + Run(NonZero), + + #[error("error while reading exit reason: {0:#x}")] + ReadExitReason(NonZero), +} /// Implementation of [`Cpu::GetStatesErr`]. #[derive(Debug, Error)] -pub enum GetStatesError {} +pub enum GetStatesError { + #[error("error while reading CR0: {0:#x}")] + ReadCr0(NonZero), + + #[error("error while reading CR4: {0:#x}")] + ReadCr4(NonZero), + + #[error("error while reading CS: {0:#x}")] + ReadCs(NonZero), + + #[error("error while reading DS: {0:#x}")] + ReadDs(NonZero), + + #[error("error while reading ES: {0:#x}")] + ReadEs(NonZero), + + #[error("error while reading FS: {0:#x}")] + ReadFs(NonZero), + + #[error("error while reading GS: {0:#x}")] + ReadGs(NonZero), + + #[error("error while reading SS: {0:#x}")] + ReadSs(NonZero), +} diff --git a/src/core/src/vmm/windows/cpu.rs b/src/core/src/vmm/windows/cpu.rs index bd39e1ad9..cda8dca0d 100644 --- a/src/core/src/vmm/windows/cpu.rs +++ b/src/core/src/vmm/windows/cpu.rs @@ -249,6 +249,16 @@ impl<'a, 'b> CpuStates for WhpStates<'a, 'b> { self.dirty = true; } + + #[cfg(target_arch = "aarch64")] + fn set_sp(&mut self, v: usize) { + todo!() + } + + #[cfg(target_arch = "aarch64")] + fn set_pc(&mut self, v: usize) { + todo!() + } } /// Implementation of [`Cpu::Exit`] for Windows Hypervisor Platform.