diff --git a/src/arch/aarch64/interrupt/handler.rs b/src/arch/aarch64/interrupt/handler.rs index a31916f..ddf3201 100644 --- a/src/arch/aarch64/interrupt/handler.rs +++ b/src/arch/aarch64/interrupt/handler.rs @@ -165,6 +165,48 @@ impl InterruptStack { all.x1 = self.scratch.x1; all.x0 = self.scratch.x0; } + + /// Loads all registers from a struct used by the proc: + /// scheme to read/write registers. + pub fn load(&mut self, all: &IntRegisters) { + self.iret.elr_el1 = all.elr_el1; + self.iret.tpidr_el0 = all.tpidr_el0; + self.iret.tpidrro_el0 = all.tpidrro_el0; + self.iret.spsr_el1 = all.spsr_el1; + self.iret.esr_el1 = all.esr_el1; + self.iret.sp_el0 = all.sp_el0; + self.preserved.x30 = all.x30; + self.preserved.x29 = all.x29; + self.preserved.x28 = all.x28; + self.preserved.x27 = all.x27; + self.preserved.x26 = all.x26; + self.preserved.x25 = all.x25; + self.preserved.x24 = all.x24; + self.preserved.x23 = all.x23; + self.preserved.x22 = all.x22; + self.preserved.x21 = all.x21; + self.preserved.x20 = all.x20; + self.preserved.x19 = all.x19; + self.scratch.x18 = all.x18; + self.scratch.x17 = all.x17; + self.scratch.x16 = all.x16; + self.scratch.x15 = all.x15; + self.scratch.x14 = all.x14; + self.scratch.x13 = all.x13; + self.scratch.x12 = all.x12; + self.scratch.x11 = all.x11; + self.scratch.x10 = all.x10; + self.scratch.x9 = all.x9; + self.scratch.x8 = all.x8; + self.scratch.x7 = all.x7; + self.scratch.x6 = all.x6; + self.scratch.x5 = all.x5; + self.scratch.x4 = all.x4; + self.scratch.x3 = all.x3; + self.scratch.x2 = all.x2; + self.scratch.x1 = all.x1; + self.scratch.x0 = all.x0; + } //TODO pub fn is_singlestep(&self) -> bool { false } diff --git a/src/context/arch/aarch64.rs b/src/context/arch/aarch64.rs index 342e161..6233cfc 100644 --- a/src/context/arch/aarch64.rs +++ b/src/context/arch/aarch64.rs @@ -208,6 +208,16 @@ impl Context { } } +impl super::Context { + pub fn get_fx_regs(&self) -> FloatRegisters { + self.arch.get_fx_regs().expect("TODO: make get_fx_regs always valid") + } + + pub fn set_fx_regs(&mut self, mut new: FloatRegisters) { + assert!(self.arch.set_fx_regs(new), "TODO: make set_fx_regs always valid") + } +} + pub static EMPTY_CR3: Once = Once::new(); // SAFETY: EMPTY_CR3 must be initialized. diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index d156cbf..06de0a0 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -397,6 +397,93 @@ impl ProcScheme { Ok(id) } + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + fn read_env_regs(&self) -> Result { + //TODO: Support other archs + Err(Error::new(EINVAL)) + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn read_env_regs(&self) -> Result { + let (fsbase, gsbase) = if info.pid == context::context_id() { + #[cfg(not(feature = "x86_fsgsbase"))] + unsafe { + ( + x86::msr::rdmsr(x86::msr::IA32_FS_BASE), + x86::msr::rdmsr(x86::msr::IA32_KERNEL_GSBASE), + ) + } + #[cfg(feature = "x86_fsgsbase")] + unsafe { + use x86::bits64::segmentation::*; + + ( + rdfsbase(), + { + swapgs(); + let gsbase = rdgsbase(); + swapgs(); + gsbase + } + ) + } + } else { + try_stop_context(info.pid, |context| { + Ok((context.arch.fsbase as u64, context.arch.gsbase as u64)) + })? + }; + Ok(EnvRegisters { fsbase: fsbase as _, gsbase: gsbase as _ }) + } + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + fn write_env_regs(&self, regs: EnvRegisters) -> Result<()> { + //TODO: Support other archs + Err(Error::new(EINVAL)) + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn write_env_regs(&self, regs: EnvRegisters) -> Result<()> { + if !(RmmA::virt_is_valid(VirtualAddress::new(regs.fsbase as usize)) && RmmA::virt_is_valid(VirtualAddress::new(regs.gsbase as usize))) { + return Err(Error::new(EINVAL)); + } + + if info.pid == context::context_id() { + #[cfg(not(feature = "x86_fsgsbase"))] + unsafe { + x86::msr::wrmsr(x86::msr::IA32_FS_BASE, regs.fsbase as u64); + // We have to write to KERNEL_GSBASE, because when the kernel returns to + // userspace, it will have executed SWAPGS first. + x86::msr::wrmsr(x86::msr::IA32_KERNEL_GSBASE, regs.gsbase as u64); + + match context::contexts().current().ok_or(Error::new(ESRCH))?.write().arch { + ref mut arch => { + arch.fsbase = regs.fsbase as usize; + arch.gsbase = regs.gsbase as usize; + } + } + } + #[cfg(feature = "x86_fsgsbase")] + unsafe { + use x86::bits64::segmentation::*; + + wrfsbase(regs.fsbase); + swapgs(); + wrgsbase(regs.gsbase); + swapgs(); + + // No need to update the current context; with fsgsbase enabled, these + // registers are automatically saved and restored. + } + } else { + try_stop_context(info.pid, |context| { + context.arch.fsbase = regs.fsbase as usize; + context.arch.gsbase = regs.gsbase as usize; + Ok(()) + })?; + } + Ok(()) + } } impl Scheme for ProcScheme { @@ -497,13 +584,6 @@ impl Scheme for ProcScheme { Ok(value) } - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - //TODO: support other archs - Err(Error::new(EINVAL)) - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn read(&self, id: usize, buf: &mut [u8]) -> Result { // Don't hold a global lock during the context switch later on let info = { @@ -598,34 +678,7 @@ impl Scheme for ProcScheme { } })?, RegsKind::Env => { - let (fsbase, gsbase) = if info.pid == context::context_id() { - #[cfg(not(feature = "x86_fsgsbase"))] - unsafe { - ( - x86::msr::rdmsr(x86::msr::IA32_FS_BASE), - x86::msr::rdmsr(x86::msr::IA32_KERNEL_GSBASE), - ) - } - #[cfg(feature = "x86_fsgsbase")] - unsafe { - use x86::bits64::segmentation::*; - - ( - rdfsbase(), - { - swapgs(); - let gsbase = rdgsbase(); - swapgs(); - gsbase - } - ) - } - } else { - try_stop_context(info.pid, |context| { - Ok((context.arch.fsbase as u64, context.arch.gsbase as u64)) - })? - }; - (Output { env: EnvRegisters { fsbase: fsbase as _, gsbase: gsbase as _ }}, mem::size_of::()) + (Output { env: self.read_env_regs()? }, mem::size_of::()) } }; @@ -709,13 +762,6 @@ impl Scheme for ProcScheme { } } - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - fn write(&self, id: usize, buf: &[u8]) -> Result { - //TODO: support other archs - Err(Error::new(EINVAL)) - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn write(&self, id: usize, buf: &[u8]) -> Result { // Don't hold a global lock during the context switch later on let info = { @@ -843,44 +889,7 @@ impl Scheme for ProcScheme { let regs = unsafe { *(buf as *const _ as *const EnvRegisters) }; - if !(RmmA::virt_is_valid(VirtualAddress::new(regs.fsbase as usize)) && RmmA::virt_is_valid(VirtualAddress::new(regs.gsbase as usize))) { - return Err(Error::new(EINVAL)); - } - - if info.pid == context::context_id() { - #[cfg(not(feature = "x86_fsgsbase"))] - unsafe { - x86::msr::wrmsr(x86::msr::IA32_FS_BASE, regs.fsbase as u64); - // We have to write to KERNEL_GSBASE, because when the kernel returns to - // userspace, it will have executed SWAPGS first. - x86::msr::wrmsr(x86::msr::IA32_KERNEL_GSBASE, regs.gsbase as u64); - - match context::contexts().current().ok_or(Error::new(ESRCH))?.write().arch { - ref mut arch => { - arch.fsbase = regs.fsbase as usize; - arch.gsbase = regs.gsbase as usize; - } - } - } - #[cfg(feature = "x86_fsgsbase")] - unsafe { - use x86::bits64::segmentation::*; - - wrfsbase(regs.fsbase); - swapgs(); - wrgsbase(regs.gsbase); - swapgs(); - - // No need to update the current context; with fsgsbase enabled, these - // registers are automatically saved and restored. - } - } else { - try_stop_context(info.pid, |context| { - context.arch.fsbase = regs.fsbase as usize; - context.arch.gsbase = regs.gsbase as usize; - Ok(()) - })?; - } + self.write_env_regs(regs)?; Ok(mem::size_of::()) } },