diff --git a/src/arch/x86_64/interrupt/exception.rs b/src/arch/x86_64/interrupt/exception.rs index 7d58efd98267d5a185431b2bea12583e03549232..864a4c32f81c90fd27de00f44e3dbdda40360994 100644 --- a/src/arch/x86_64/interrupt/exception.rs +++ b/src/arch/x86_64/interrupt/exception.rs @@ -1,10 +1,5 @@ -use crate::{ - common::unique::Unique, - context, - interrupt::stack_trace, - ptrace, - syscall::flag::* -}; +use crate::interrupt::stack_trace; +use crate::syscall::flag::*; extern { fn ksignal(signal: usize); @@ -18,41 +13,9 @@ interrupt_stack_p!(divide_by_zero, stack, { }); interrupt_stack!(debug, stack, { - match ptrace::breakpoint_callback_dryrun(true) { - Some(_) => { - { - let contexts = context::contexts(); - if let Some(context) = contexts.current() { - let mut context = context.write(); - if let Some(ref mut kstack) = context.kstack { - context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new_unchecked(stack))); - } - } - } - - let had_singlestep = stack.iret.rflags & (1 << 8) == 1 << 8; - stack.set_singlestep(false); - if ptrace::breakpoint_callback(true).is_none() { - // There is no guarantee that this is Some(_) just - // because the dryrun is Some(_). So, if there wasn't - // *actually* any breakpoint, restore the trap flag. - stack.set_singlestep(had_singlestep); - } - - { - let contexts = context::contexts(); - if let Some(context) = contexts.current() { - let mut context = context.write(); - context.regs = None; - } - } - }, - None => { - println!("Debug trap"); - stack.dump(); - ksignal(SIGTRAP); - } - } + println!("Debug trap"); + stack.dump(); + ksignal(SIGTRAP); }); interrupt_stack!(non_maskable, stack, { diff --git a/src/arch/x86_64/interrupt/irq.rs b/src/arch/x86_64/interrupt/irq.rs index fbc5bb87159574d344a5c9f9faee516aa2830f9d..6ffeb8c4271032f1ecdcb5253d75430c026f23d8 100644 --- a/src/arch/x86_64/interrupt/irq.rs +++ b/src/arch/x86_64/interrupt/irq.rs @@ -1,6 +1,5 @@ use core::sync::atomic::{AtomicUsize, Ordering}; -use crate::common::unique::Unique; use crate::context; use crate::context::timeout; use crate::device::pic; @@ -41,7 +40,7 @@ pub unsafe fn acknowledge(irq: usize) { } } -interrupt_stack!(pit, stack, { +interrupt!(pit, { // Saves CPU time by not sending IRQ event irq_trigger(0); const PIT_RATE: u64 = 2_250_286; @@ -62,25 +61,7 @@ interrupt_stack!(pit, stack, { timeout::trigger(); if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 { - { - let contexts = crate::context::contexts(); - if let Some(context) = contexts.current() { - let mut context = context.write(); - // Make all registers available to e.g. the proc: - // scheme - if let Some(ref mut kstack) = context.kstack { - context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new_unchecked(stack))); - } - } - } let _ = context::switch(); - { - let contexts = crate::context::contexts(); - if let Some(context) = contexts.current() { - let mut context = context.write(); - context.regs = None; - } - } } }); diff --git a/src/arch/x86_64/interrupt/syscall.rs b/src/arch/x86_64/interrupt/syscall.rs index 2a4d68e74b74ebb158727b4cddf909ef30da123e..099904ffaea3f1523129653e14f99fcbb12efd01 100644 --- a/src/arch/x86_64/interrupt/syscall.rs +++ b/src/arch/x86_64/interrupt/syscall.rs @@ -1,74 +1,25 @@ -use crate::arch::macros::InterruptStack; use crate::arch::{gdt, pti}; -use crate::common::unique::Unique; -use crate::{context, ptrace, syscall}; +use crate::syscall; use x86::shared::msr; pub unsafe fn init() { msr::wrmsr(msr::IA32_STAR, ((gdt::GDT_KERNEL_CODE as u64) << 3) << 32); msr::wrmsr(msr::IA32_LSTAR, syscall_instruction as u64); - msr::wrmsr(msr::IA32_FMASK, 0x0300); // Clear trap flag and interrupt enable + msr::wrmsr(msr::IA32_FMASK, 1 << 9); msr::wrmsr(msr::IA32_KERNEL_GS_BASE, &gdt::TSS as *const _ as u64); let efer = msr::rdmsr(msr::IA32_EFER); msr::wrmsr(msr::IA32_EFER, efer | 1); } -// Not a function pointer because it somehow messes up the returning -// from clone() (via clone_ret()). Not sure what the problem is. -macro_rules! with_interrupt_stack { - (unsafe fn $wrapped:ident($stack:ident) -> usize $code:block) => { - /// Because of how clones work, we need a function that returns a - /// usize. Here, `inner` will be this function. The child process in a - /// clone will terminate this function with a 0 return value, and it - /// might also have updated the interrupt_stack pointer. - #[inline(never)] - unsafe fn $wrapped(stack: *mut SyscallStack) { - let stack = &mut *stack; - { - let contexts = context::contexts(); - if let Some(context) = contexts.current() { - let mut context = context.write(); - if let Some(ref mut kstack) = context.kstack { - context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new_unchecked(&mut stack.interrupt_stack))); - } - } - } - - let is_sysemu = ptrace::breakpoint_callback(false); - if !is_sysemu.unwrap_or(false) { - // If not on a sysemu breakpoint - let $stack = &mut *stack; - $stack.interrupt_stack.scratch.rax = $code; - - if is_sysemu.is_some() { - // Only callback if there was a pre-syscall - // callback too. - ptrace::breakpoint_callback(false); - } - } - - { - let contexts = context::contexts(); - if let Some(context) = contexts.current() { - let mut context = context.write(); - context.regs = None; - } - } - } - } -} - #[naked] pub unsafe extern fn syscall_instruction() { - with_interrupt_stack! { - unsafe fn inner(stack) -> usize { - let rbp; - asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); - - let scratch = &stack.interrupt_stack.scratch; - syscall::syscall(scratch.rax, scratch.rdi, scratch.rsi, scratch.rdx, scratch.r10, scratch.r8, rbp, stack) - } + #[inline(never)] + unsafe fn inner(stack: &mut SyscallStack) -> usize { + let rbp; + asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); + + syscall::syscall(stack.rax, stack.rdi, stack.rsi, stack.rdx, stack.r10, stack.r8, rbp, stack) } // Yes, this is magic. No, you don't need to understand @@ -89,52 +40,76 @@ pub unsafe extern fn syscall_instruction() { : : "intel", "volatile"); - // Push scratch registers - scratch_push!(); - asm!("push fs - mov r11, 0x18 - mov fs, r11 - push rbx" - : : : : "intel", "volatile"); - - // Get reference to stack variables - let rsp: usize; - asm!("" : "={rsp}"(rsp) : : : "intel", "volatile"); - - // Map kernel - pti::map(); - - inner(rsp as *mut SyscallStack); - - // Unmap kernel - pti::unmap(); - - // Interrupt return - asm!("pop rbx - pop fs" - : : : : "intel", "volatile"); - scratch_pop!(); - asm!("iretq" : : : : "intel", "volatile"); + // Push scratch registers + asm!("push rax + push rbx + push rcx + push rdx + push rdi + push rsi + push r8 + push r9 + push r10 + push r11 + push fs + mov r11, 0x18 + mov fs, r11" + : : : : "intel", "volatile"); + + // Get reference to stack variables + let rsp: usize; + asm!("" : "={rsp}"(rsp) : : : "intel", "volatile"); + + // Map kernel + pti::map(); + + let a = inner(&mut *(rsp as *mut SyscallStack)); + + // Unmap kernel + pti::unmap(); + + asm!("" : : "{rax}"(a) : : "intel", "volatile"); + + // Interrupt return + asm!("pop fs + pop r11 + pop r10 + pop r9 + pop r8 + pop rsi + pop rdi + pop rdx + pop rcx + pop rbx + add rsp, 8 + iretq" + : : : : "intel", "volatile"); } #[naked] pub unsafe extern fn syscall() { - with_interrupt_stack! { - unsafe fn inner(stack) -> usize { - let rbp; - asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); - - let scratch = &stack.interrupt_stack.scratch; - syscall::syscall(scratch.rax, stack.rbx, scratch.rcx, scratch.rdx, scratch.rsi, scratch.rdi, rbp, stack) - } + #[inline(never)] + unsafe fn inner(stack: &mut SyscallStack) -> usize { + let rbp; + asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); + + syscall::syscall(stack.rax, stack.rbx, stack.rcx, stack.rdx, stack.rsi, stack.rdi, rbp, stack) } // Push scratch registers - scratch_push!(); - asm!("push fs + asm!("push rax + push rbx + push rcx + push rdx + push rdi + push rsi + push r8 + push r9 + push r10 + push r11 + push fs mov r11, 0x18 - mov fs, r11 - push rbx" + mov fs, r11" : : : : "intel", "volatile"); // Get reference to stack variables @@ -144,41 +119,56 @@ pub unsafe extern fn syscall() { // Map kernel pti::map(); - inner(rsp as *mut SyscallStack); + let a = inner(&mut *(rsp as *mut SyscallStack)); // Unmap kernel pti::unmap(); + asm!("" : : "{rax}"(a) : : "intel", "volatile"); + // Interrupt return - asm!("pop rbx - pop fs" - : : : : "intel", "volatile"); - scratch_pop!(); - asm!("iretq" : : : : "intel", "volatile"); + asm!("pop fs + pop r11 + pop r10 + pop r9 + pop r8 + pop rsi + pop rdi + pop rdx + pop rcx + pop rbx + add rsp, 8 + iretq" + : : : : "intel", "volatile"); } #[allow(dead_code)] #[repr(packed)] pub struct SyscallStack { + pub fs: usize, + pub r11: usize, + pub r10: usize, + pub r9: usize, + pub r8: usize, + pub rsi: usize, + pub rdi: usize, + pub rdx: usize, + pub rcx: usize, pub rbx: usize, - pub interrupt_stack: InterruptStack, - + pub rax: usize, + pub rip: usize, + pub cs: usize, + pub rflags: usize, // Will only be present if syscall is called from another ring pub rsp: usize, pub ss: usize, } #[naked] -pub unsafe extern "C" fn clone_ret() { - // The C x86_64 ABI specifies that rbp is pushed to save the old - // call frame. Popping rbp means we're using the parent's call - // frame and thus will not only return from this function but also - // from the function above this one. - // When this is called, the stack should have been - // interrupt->inner->syscall->clone - // then changed to - // interrupt->inner->clone_ret->clone - // so this will return from "inner". - - asm!("pop rbp" : : : : "intel", "volatile"); +pub unsafe extern fn clone_ret() { + asm!(" + pop rbp + xor rax, rax + " + : : : : "intel", "volatile"); } diff --git a/src/arch/x86_64/macros.rs b/src/arch/x86_64/macros.rs index da0b037377ce28b034b39afd355f1ff2971bdc2f..d41ee9338b0c9d0395704274b926f6c0dc792c94 100644 --- a/src/arch/x86_64/macros.rs +++ b/src/arch/x86_64/macros.rs @@ -1,5 +1,3 @@ -use syscall::data::IntRegisters; - /// Print to console #[macro_export] macro_rules! print { @@ -206,50 +204,6 @@ impl InterruptStack { self.scratch.dump(); println!("FS: {:>016X}", { self.fs }); } - /// Saves all registers to a struct used by the proc: - /// scheme to read/write registers. - pub fn save(&self, all: &mut IntRegisters) { - all.fs = self.fs; - all.r11 = self.scratch.r11; - all.r10 = self.scratch.r10; - all.r9 = self.scratch.r9; - all.r8 = self.scratch.r8; - all.rsi = self.scratch.rsi; - all.rdi = self.scratch.rdi; - all.rdx = self.scratch.rdx; - all.rcx = self.scratch.rcx; - all.rax = self.scratch.rax; - all.rip = self.iret.rip; - all.cs = self.iret.cs; - all.eflags = self.iret.rflags; - } - /// Loads all registers from a struct used by the proc: - /// scheme to read/write registers. - pub fn load(&mut self, all: &IntRegisters) { - self.fs = all.fs; - self.scratch.r11 = all.r11; - self.scratch.r10 = all.r10; - self.scratch.r9 = all.r9; - self.scratch.r8 = all.r8; - self.scratch.rsi = all.rsi; - self.scratch.rdi = all.rdi; - self.scratch.rdx = all.rdx; - self.scratch.rcx = all.rcx; - self.scratch.rax = all.rax; - self.iret.rip = all.rip; - self.iret.cs = all.cs; - self.iret.rflags = all.eflags; - } - /// Enables the "Trap Flag" in the FLAGS register, causing the CPU - /// to send a Debug exception after the next instruction. This is - /// used for singlestep in the proc: scheme. - pub fn set_singlestep(&mut self, enabled: bool) { - if enabled { - self.iret.rflags |= 1 << 8; - } else { - self.iret.rflags &= !(1 << 8); - } - } } #[macro_export] diff --git a/src/common/mod.rs b/src/common/mod.rs index 7ad826b3c8b793cd4872df57e6e4ceaea44e6d9d..29f6412e8a85bc9b664e0c39b2ea731f6bfe0ca9 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,3 +1,2 @@ #[macro_use] pub mod int_like; -pub mod unique; diff --git a/src/common/unique.rs b/src/common/unique.rs deleted file mode 100644 index 214f5b53a3a02dd1ab23f2e3f9e6d79e0f5a9d71..0000000000000000000000000000000000000000 --- a/src/common/unique.rs +++ /dev/null @@ -1,33 +0,0 @@ -use core::{fmt, ptr::NonNull}; - -/// A small wrapper around NonNull<T> that is Send + Sync, which is -/// only correct if the pointer is never accessed from multiple -/// locations across threads. Which is always, if the pointer is -/// unique. -pub struct Unique<T>(NonNull<T>); - -impl<T> Copy for Unique<T> {} -impl<T> Clone for Unique<T> { - fn clone(&self) -> Self { - *self - } -} -unsafe impl<T> Send for Unique<T> {} -unsafe impl<T> Sync for Unique<T> {} - -impl<T> Unique<T> { - pub fn new(ptr: *mut T) -> Self { - Self(NonNull::new(ptr).unwrap()) - } - pub unsafe fn new_unchecked(ptr: *mut T) -> Self { - Self(NonNull::new_unchecked(ptr)) - } - pub fn as_ptr(&self) -> *mut T { - self.0.as_ptr() - } -} -impl<T> fmt::Debug for Unique<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} diff --git a/src/context/context.rs b/src/context/context.rs index 269b4ee4095953e4fa4302edac360205d76cfe67..c27f7de3e5341da1567086e14ce584ab0bba8ca1 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -7,16 +7,15 @@ use core::cmp::Ordering; use core::mem; use spin::Mutex; -use crate::arch::{macros::InterruptStack, paging::PAGE_SIZE}; -use crate::common::unique::Unique; +use crate::arch::paging::PAGE_SIZE; use crate::context::arch; use crate::context::file::FileDescriptor; use crate::context::memory::{Grant, Memory, SharedMemory, Tls}; use crate::ipi::{ipi, IpiKind, IpiTarget}; use crate::scheme::{SchemeNamespace, FileHandle}; -use crate::sync::WaitMap; use crate::syscall::data::SigAction; use crate::syscall::flag::SIG_DFL; +use crate::sync::WaitMap; /// Unique identifier for a context (i.e. `pid`). use ::core::sync::atomic::AtomicUsize; @@ -166,15 +165,6 @@ pub struct Context { pub files: Arc<Mutex<Vec<Option<FileDescriptor>>>>, /// Signal actions pub actions: Arc<Mutex<Vec<(SigAction, usize)>>>, - /// The pointer to the user-space registers, saved after certain - /// interrupts. This pointer is somewhere inside kstack, and the - /// kstack address at the time of creation is the first element in - /// this tuple. - pub regs: Option<(usize, Unique<InterruptStack>)>, - /// A somewhat hacky way to initially stop a context when creating - /// a new instance of the proc: scheme, entirely separate from - /// signals or any other way to restart a process. - pub ptrace_stop: bool } impl Context { @@ -226,8 +216,6 @@ impl Context { }, 0 ); 128])), - regs: None, - ptrace_stop: false } } diff --git a/src/context/switch.rs b/src/context/switch.rs index cf55220fc802dbbcbda6456318df42a4fcf20efa..7d2536575cbf55a33b3f41622e121c7705ffa271 100644 --- a/src/context/switch.rs +++ b/src/context/switch.rs @@ -55,7 +55,7 @@ unsafe fn update(context: &mut Context, cpu_id: usize) { unsafe fn runnable(context: &Context, cpu_id: usize) -> bool { // Switch to context if it needs to run, is not currently running, and is owned by the current CPU - !context.running && !context.ptrace_stop && context.status == Status::Runnable && context.cpu_id == Some(cpu_id) + !context.running && context.status == Status::Runnable && context.cpu_id == Some(cpu_id) } /// Switch to the next context diff --git a/src/lib.rs b/src/lib.rs index 24b265af194e42401320a04a2a3b7f421b610a60..109ea3914800449f84374a86d4bedd6798a94033 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,9 +93,6 @@ pub mod memory; #[cfg(not(any(feature="doc", test)))] pub mod panic; -/// Process tracing -pub mod ptrace; - /// Schemes, filesystem handlers pub mod scheme; diff --git a/src/ptrace.rs b/src/ptrace.rs deleted file mode 100644 index 5f8cb2763c0a368ab750820d9d9f86965dde9a77..0000000000000000000000000000000000000000 --- a/src/ptrace.rs +++ /dev/null @@ -1,208 +0,0 @@ -use crate::{ - arch::macros::InterruptStack, - common::unique::Unique, - context::{self, Context, ContextId, Status}, - sync::WaitCondition -}; - -use alloc::{ - boxed::Box, - collections::BTreeMap, - sync::Arc -}; -use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; -use syscall::error::*; - -// ____ _ _ _ -// | __ ) _ __ ___ __ _| | ___ __ ___ (_)_ __ | |_ ___ -// | _ \| '__/ _ \/ _` | |/ / '_ \ / _ \| | '_ \| __/ __| -// | |_) | | | __/ (_| | <| |_) | (_) | | | | | |_\__ \ -// |____/|_| \___|\__,_|_|\_\ .__/ \___/|_|_| |_|\__|___/ -// |_| - -struct Handle { - tracee: Arc<WaitCondition>, - tracer: Arc<WaitCondition>, - reached: bool, - - sysemu: bool, - singlestep: bool -} - -static BREAKPOINTS: Once<RwLock<BTreeMap<ContextId, Handle>>> = Once::new(); - -fn init_breakpoints() -> RwLock<BTreeMap<ContextId, Handle>> { - RwLock::new(BTreeMap::new()) -} -fn breakpoints() -> RwLockReadGuard<'static, BTreeMap<ContextId, Handle>> { - BREAKPOINTS.call_once(init_breakpoints).read() -} -fn breakpoints_mut() -> RwLockWriteGuard<'static, BTreeMap<ContextId, Handle>> { - BREAKPOINTS.call_once(init_breakpoints).write() -} - -fn inner_cont(pid: ContextId) -> Option<Handle> { - // Remove the breakpoint to both save space and also make sure any - // yet unreached but obsolete breakpoints don't stop the program. - let handle = breakpoints_mut().remove(&pid)?; - handle.tracee.notify(); - Some(handle) -} - -/// Continue the process with the specified ID -pub fn cont(pid: ContextId) { - inner_cont(pid); -} - -/// Create a new breakpoint for the specified tracee, optionally with a sysemu flag -pub fn set_breakpoint(pid: ContextId, sysemu: bool, singlestep: bool) { - let (tracee, tracer) = match inner_cont(pid) { - Some(breakpoint) => (breakpoint.tracee, breakpoint.tracer), - None => ( - Arc::new(WaitCondition::new()), - Arc::new(WaitCondition::new()) - ) - }; - - breakpoints_mut().insert(pid, Handle { - tracee, - tracer, - reached: false, - sysemu, - singlestep - }); -} - -/// Wait for the tracee to stop. -/// Note: Don't call while holding any locks, this will switch contexts -pub fn wait_breakpoint(pid: ContextId) -> Result<()> { - let tracer = { - let breakpoints = breakpoints(); - match breakpoints.get(&pid) { - Some(breakpoint) if !breakpoint.reached => Arc::clone(&breakpoint.tracer), - _ => return Ok(()) - } - }; - while !tracer.wait() {} - - let contexts = context::contexts(); - let context = contexts.get(pid).ok_or(Error::new(ESRCH))?; - let context = context.read(); - if let Status::Exited(_) = context.status { - return Err(Error::new(ESRCH)); - } - Ok(()) -} - -/// Returns the same value as breakpoint_callback would do, but -/// doesn't actually perform the action. You should not rely too -/// heavily on this value, as the lock *is* released between this call -/// and another. -pub fn breakpoint_callback_dryrun(singlestep: bool) -> Option<bool> { - let contexts = context::contexts(); - let context = contexts.current()?; - let context = context.read(); - - let breakpoints = breakpoints(); - let breakpoint = breakpoints.get(&context.id)?; - if breakpoint.singlestep != singlestep { - return None; - } - Some(breakpoint.sysemu) -} - -/// Notify the tracer and await green flag to continue. -/// Note: Don't call while holding any locks, this will switch contexts -pub fn breakpoint_callback(singlestep: bool) -> Option<bool> { - // Can't hold any locks when executing wait() - let (tracee, sysemu) = { - let contexts = context::contexts(); - let context = contexts.current()?; - let context = context.read(); - - let mut breakpoints = breakpoints_mut(); - let breakpoint = breakpoints.get_mut(&context.id)?; - - // TODO: How should singlesteps interact with syscalls? How - // does Linux handle this? - - // if singlestep && !breakpoint.singlestep { - if breakpoint.singlestep != singlestep { - return None; - } - - breakpoint.tracer.notify(); - // In case no tracer is waiting, make sure the next one gets - // the memo - breakpoint.reached = true; - - ( - Arc::clone(&breakpoint.tracee), - breakpoint.sysemu - ) - }; - - while !tracee.wait() {} - - Some(sysemu) -} - -/// Call when a context is closed to alert any tracers -pub fn close(pid: ContextId) { - { - let breakpoints = breakpoints(); - if let Some(breakpoint) = breakpoints.get(&pid) { - breakpoint.tracer.notify(); - } - } - - breakpoints_mut().remove(&pid); -} - -// ____ _ _ -// | _ \ ___ __ _(_)___| |_ ___ _ __ ___ -// | |_) / _ \/ _` | / __| __/ _ \ '__/ __| -// | _ < __/ (_| | \__ \ || __/ | \__ \ -// |_| \_\___|\__, |_|___/\__\___|_| |___/ -// |___/ - -/// Return the InterruptStack pointer, but relative to the specified -/// stack instead of the original. -pub unsafe fn rebase_regs_ptr( - regs: Option<(usize, Unique<InterruptStack>)>, - kstack: Option<&Box<[u8]>> -) -> Option<*const InterruptStack> { - let (old_base, ptr) = regs?; - let new_base = kstack?.as_ptr() as usize; - Some((ptr.as_ptr() as usize - old_base + new_base) as *const _) -} -/// Return the InterruptStack pointer, but relative to the specified -/// stack instead of the original. -pub unsafe fn rebase_regs_ptr_mut( - regs: Option<(usize, Unique<InterruptStack>)>, - kstack: Option<&mut Box<[u8]>> -) -> Option<*mut InterruptStack> { - let (old_base, ptr) = regs?; - let new_base = kstack?.as_mut_ptr() as usize; - Some((ptr.as_ptr() as usize - old_base + new_base) as *mut _) -} - -/// Return a reference to the InterruptStack struct in memory. If the -/// kernel stack has been backed up by a signal handler, this instead -/// returns the struct inside that memory, as that will later be -/// restored and otherwise undo all your changes. See `update(...)` in -/// context/switch.rs. -pub unsafe fn regs_for(context: &Context) -> Option<&InterruptStack> { - Some(&*match context.ksig { - Some((_, _, ref kstack)) => rebase_regs_ptr(context.regs, kstack.as_ref())?, - None => context.regs?.1.as_ptr() - }) -} - -/// Mutable version of `regs_for` -pub unsafe fn regs_for_mut(context: &mut Context) -> Option<&mut InterruptStack> { - Some(&mut *match context.ksig { - Some((_, _, ref mut kstack)) => rebase_regs_ptr_mut(context.regs, kstack.as_mut())?, - None => context.regs?.1.as_ptr() - }) -} diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index da5f5cd783221cd86b2c8048506d58855ec0a877..4cf995830f4213a1ac9eff74e4b12943c3c52ea8 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -22,7 +22,6 @@ use self::irq::IrqScheme; use self::itimer::ITimerScheme; use self::memory::MemoryScheme; use self::pipe::PipeScheme; -use self::proc::ProcScheme; use self::root::RootScheme; use self::sys::SysScheme; use self::time::TimeScheme; @@ -52,9 +51,6 @@ pub mod memory; /// `pipe:` - used internally by the kernel to implement `pipe` pub mod pipe; -/// `proc:` - allows tracing processes and reading/writing their memory -pub mod proc; - /// `:` - allows the creation of userspace schemes, tightly dependent on `user` pub mod root; @@ -132,21 +128,29 @@ impl SchemeList { } /// Initialize the root namespace + #[cfg(not(feature="live"))] fn new_root(&mut self) { // Do common namespace initialization let ns = self.new_ns(); - // These schemes should only be available on the root + // Debug, Initfs and IRQ are only available in the root namespace. Pipe is special self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(Box::new(DebugScheme::new(scheme_id)))).unwrap(); self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(Box::new(InitFsScheme::new()))).unwrap(); self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(Box::new(IrqScheme::new(scheme_id)))).unwrap(); - self.insert(ns, Box::new(*b"proc"), |_| Arc::new(Box::new(ProcScheme::new()))).unwrap(); + self.insert(ns, Box::new(*b"pipe"), |scheme_id| Arc::new(Box::new(PipeScheme::new(scheme_id)))).unwrap(); + } - #[cfg(feature = "live")] { - self.insert(ns, Box::new(*b"disk/live"), |_| Arc::new(Box::new(self::live::DiskScheme::new()))).unwrap(); - } + /// Initialize the root namespace - with live disk + #[cfg(feature="live")] + fn new_root(&mut self) { + // Do common namespace initialization + let ns = self.new_ns(); - // Pipe is special and needs to be in the root namespace + // Debug, Disk, Initfs and IRQ are only available in the root namespace. Pipe is special + self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(Box::new(DebugScheme::new(scheme_id)))).unwrap(); + self.insert(ns, Box::new(*b"disk/live"), |_| Arc::new(Box::new(self::live::DiskScheme::new()))).unwrap(); + self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(Box::new(InitFsScheme::new()))).unwrap(); + self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(Box::new(IrqScheme::new(scheme_id)))).unwrap(); self.insert(ns, Box::new(*b"pipe"), |scheme_id| Arc::new(Box::new(PipeScheme::new(scheme_id)))).unwrap(); } diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs deleted file mode 100644 index 6edd478571b82eb9773cb52885790a6f5877abb3..0000000000000000000000000000000000000000 --- a/src/scheme/proc.rs +++ /dev/null @@ -1,351 +0,0 @@ -use crate::{ - context::{self, ContextId, Status}, - ptrace -}; - -use alloc::collections::{BTreeMap, BTreeSet}; -use core::{ - cmp, - mem, - slice, - sync::atomic::{AtomicUsize, Ordering} -}; -use spin::{Mutex, RwLock}; -use syscall::{ - data::{IntRegisters, FloatRegisters}, - error::*, - flag::*, - scheme::Scheme -}; - -#[derive(Clone, Copy)] -enum RegsKind { - Float, - Int -} -#[derive(Clone, Copy)] -enum Operation { - Memory, - Regs(RegsKind), - Trace -} - -#[derive(Clone, Copy)] -struct Handle { - flags: usize, - pid: ContextId, - operation: Operation -} - -pub struct ProcScheme { - next_id: AtomicUsize, - handles: RwLock<BTreeMap<usize, Handle>>, - traced: Mutex<BTreeSet<ContextId>> -} - -impl ProcScheme { - pub fn new() -> Self { - Self { - next_id: AtomicUsize::new(0), - handles: RwLock::new(BTreeMap::new()), - traced: Mutex::new(BTreeSet::new()) - } - } -} - -impl Scheme for ProcScheme { - fn open(&self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result<usize> { - let path = core::str::from_utf8(path).map_err(|_| Error::new(EINVAL))?; - let mut parts = path.splitn(2, '/'); - let pid = parts.next() - .and_then(|s| s.parse().ok()) - .map(ContextId::from) - .ok_or(Error::new(EINVAL))?; - let operation = match parts.next() { - Some("mem") => Operation::Memory, - Some("regs/float") => Operation::Regs(RegsKind::Float), - Some("regs/int") => Operation::Regs(RegsKind::Int), - Some("trace") => Operation::Trace, - _ => return Err(Error::new(EINVAL)) - }; - - let contexts = context::contexts(); - let context = contexts.get(pid).ok_or(Error::new(ESRCH))?; - - { - // TODO: Put better security here? - - let context = context.read(); - if uid != 0 && gid != 0 - && uid != context.euid && gid != context.egid { - return Err(Error::new(EPERM)); - } - } - - if let Operation::Trace = operation { - let mut traced = self.traced.lock(); - - if traced.contains(&pid) { - return Err(Error::new(EBUSY)); - } - traced.insert(pid); - - let mut context = context.write(); - context.ptrace_stop = true; - } - - let id = self.next_id.fetch_add(1, Ordering::SeqCst); - self.handles.write().insert(id, Handle { - flags, - pid, - operation - }); - Ok(id) - } - - /// Using dup for `proc:` simply opens another operation on the same PID - /// ```rust,ignore - /// let trace = syscall::open("proc:1234/trace")?; - /// - /// // let regs = syscall::open("proc:1234/regs/int")?; - /// let regs = syscall::dup(trace, "regs/int")?; - /// ``` - fn dup(&self, old_id: usize, buf: &[u8]) -> Result<usize> { - let handle = { - let handles = self.handles.read(); - *handles.get(&old_id).ok_or(Error::new(EBADF))? - }; - let mut path = format!("{}/", handle.pid.into()).into_bytes(); - path.extend_from_slice(buf); - - let (uid, gid) = { - let contexts = context::contexts(); - let context = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context.read(); - (context.euid, context.egid) - }; - - self.open(&path, handle.flags, uid, gid) - } - - fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> { - // Can't hold locks during the context switch later when - // waiting for a process to stop running. - let handle = { - let handles = self.handles.read(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - match handle.operation { - Operation::Memory => { - // let contexts = context::contexts(); - // let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?; - // let context = context.read(); - - // for grant in &*context.grants.lock() { - // println!("Grant: {} -> {}", grant.start.get(), grant.size); - // } - // unimplemented!(); - return Err(Error::new(EBADF)); - }, - Operation::Regs(kind) => { - union Output { - _float: FloatRegisters, - int: IntRegisters - } - let mut first = true; - let (output, size) = loop { - if !first { - // We've tried this before, so lets wait before retrying - unsafe { context::switch(); } - } - first = false; - - let contexts = context::contexts(); - let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?; - let context = context.read(); - - break match kind { - RegsKind::Float => { - // TODO!! - // (Output { float: FloatRegisters::default() }, mem::size_of::<FloatRegisters>()) - return Err(Error::new(EBADF)); - }, - RegsKind::Int => match unsafe { ptrace::regs_for(&context) } { - None => { - // Another CPU is running this process, wait until it's stopped. - continue; - }, - Some(stack) => { - let mut regs = IntRegisters::default(); - - stack.save(&mut regs); - - (Output { int: regs }, mem::size_of::<IntRegisters>()) - } - } - }; - }; - - let bytes = unsafe { - slice::from_raw_parts(&output as *const _ as *const u8, mem::size_of::<Output>()) - }; - let len = cmp::min(buf.len(), size); - buf[..len].copy_from_slice(&bytes[..len]); - - Ok(len) - }, - Operation::Trace => Err(Error::new(EBADF)) - } - } - - fn write(&self, id: usize, buf: &[u8]) -> Result<usize> { - // Can't hold locks during the context switch later when - // waiting for a process to stop running. - let handle = { - let handles = self.handles.read(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - let mut first = true; - match handle.operation { - Operation::Memory => { - // unimplemented!() - return Err(Error::new(EBADF)); - }, - Operation::Regs(kind) => loop { - if !first { - // We've tried this before, so lets wait before retrying - unsafe { context::switch(); } - } - first = false; - - let contexts = context::contexts(); - let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?; - let mut context = context.write(); - - break match kind { - RegsKind::Float => { - // TODO!! - unimplemented!(); - }, - RegsKind::Int => match unsafe { ptrace::regs_for_mut(&mut context) } { - None => { - // Another CPU is running this process, wait until it's stopped. - continue; - }, - Some(stack) => { - if buf.len() < mem::size_of::<IntRegisters>() { - return Ok(0); - } - let regs = unsafe { - *(buf as *const _ as *const IntRegisters) - }; - - stack.load(®s); - - Ok(mem::size_of::<IntRegisters>()) - } - } - }; - }, - Operation::Trace => { - if buf.len() < 1 { - return Ok(0); - } - let op = buf[0]; - let sysemu = op & PTRACE_SYSEMU == PTRACE_SYSEMU; - - let mut blocking = handle.flags & O_NONBLOCK != O_NONBLOCK; - let mut wait_breakpoint = false; - let mut singlestep = false; - - match op & PTRACE_OPERATIONMASK { - PTRACE_CONT => { ptrace::cont(handle.pid); }, - PTRACE_SYSCALL | PTRACE_SINGLESTEP => { // <- not a bitwise OR - singlestep = op & PTRACE_OPERATIONMASK == PTRACE_SINGLESTEP; - ptrace::set_breakpoint(handle.pid, sysemu, singlestep); - wait_breakpoint = true; - }, - PTRACE_WAIT => { - wait_breakpoint = true; - blocking = true; - }, - _ => return Err(Error::new(EINVAL)) - } - - let mut first = true; - loop { - if !first { - // We've tried this before, so lets wait before retrying - unsafe { context::switch(); } - } - first = false; - - let contexts = context::contexts(); - let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?; - let mut context = context.write(); - if let Status::Exited(_) = context.status { - return Err(Error::new(ESRCH)); - } - - if singlestep { - match unsafe { ptrace::regs_for_mut(&mut context) } { - None => continue, - Some(stack) => stack.set_singlestep(true) - } - } - - context.ptrace_stop = false; - break; - } - - if wait_breakpoint && blocking { - ptrace::wait_breakpoint(handle.pid)?; - } - - Ok(1) - } - } - } - - fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> { - let mut handles = self.handles.write(); - let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - - match cmd { - F_SETFL => { handle.flags = arg; Ok(0) }, - F_GETFL => return Ok(handle.flags), - _ => return Err(Error::new(EINVAL)) - } - } - - fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> { - let handles = self.handles.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - - let path = format!("proc:{}/{}", handle.pid.into(), match handle.operation { - Operation::Memory => "mem", - Operation::Regs(RegsKind::Float) => "regs/float", - Operation::Regs(RegsKind::Int) => "regs/int", - Operation::Trace => "trace" - }); - - let len = cmp::min(path.len(), buf.len()); - buf[..len].copy_from_slice(&path.as_bytes()[..len]); - - Ok(len) - } - - fn close(&self, id: usize) -> Result<usize> { - let handle = self.handles.write().remove(&id).ok_or(Error::new(EBADF))?; - ptrace::cont(handle.pid); - - let contexts = context::contexts(); - if let Some(context) = contexts.get(handle.pid) { - let mut context = context.write(); - context.ptrace_stop = false; - } - Ok(0) - } -} diff --git a/src/syscall/debug.rs b/src/syscall/debug.rs index 853380f1dd236ff667201fc1cb56a557460fbfc0..149929869fbae8c3dde5a4a09fec1761bce9f5ce 100644 --- a/src/syscall/debug.rs +++ b/src/syscall/debug.rs @@ -1,4 +1,5 @@ -use core::{ascii, mem}; +use core::mem; +use core::ops::Range; use alloc::string::String; use alloc::vec::Vec; @@ -7,13 +8,47 @@ use super::flag::*; use super::number::*; use super::validate::*; +// Copied from std +pub struct EscapeDefault { + range: Range<usize>, + data: [u8; 4], +} + +pub fn escape_default(c: u8) -> EscapeDefault { + let (data, len) = match c { + b'\t' => ([b'\\', b't', 0, 0], 2), + b'\r' => ([b'\\', b'r', 0, 0], 2), + b'\n' => ([b'\\', b'n', 0, 0], 2), + b'\\' => ([b'\\', b'\\', 0, 0], 2), + b'\'' => ([b'\\', b'\'', 0, 0], 2), + b'"' => ([b'\\', b'"', 0, 0], 2), + b'\x20' ... b'\x7e' => ([c, 0, 0, 0], 1), + _ => ([b'\\', b'x', hexify(c >> 4), hexify(c & 0xf)], 4), + }; + + return EscapeDefault { range: (0.. len), data: data }; + + fn hexify(b: u8) -> u8 { + match b { + 0 ... 9 => b'0' + b, + _ => b'a' + b - 10, + } + } +} + +impl Iterator for EscapeDefault { + type Item = u8; + fn next(&mut self) -> Option<u8> { self.range.next().map(|i| self.data[i]) } + fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() } +} + struct ByteStr<'a>(&'a[u8]); impl<'a> ::core::fmt::Debug for ByteStr<'a> { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "\"")?; for i in self.0 { - for ch in ascii::escape_default(*i) { + for ch in escape_default(*i) { write!(f, "{}", ch as char)?; } } diff --git a/src/syscall/driver.rs b/src/syscall/driver.rs index 360d3a4d3da9a996239eddadb3b36879eb078627..5672706f02b60fc78163b9c586f8c8d90e577a89 100644 --- a/src/syscall/driver.rs +++ b/src/syscall/driver.rs @@ -25,8 +25,7 @@ pub fn iopl(level: usize, stack: &mut SyscallStack) -> Result<usize> { return Err(Error::new(EINVAL)); } - let iret = &mut stack.interrupt_stack.iret; - iret.rflags = (iret.rflags & !(3 << 12)) | ((level & 3) << 12); + stack.rflags = (stack.rflags & !(3 << 12)) | ((level & 3) << 12); Ok(0) } diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 7d07c4191ad00baf1750211421a76451c0ab7fc7..d1a7298c66bef0768f3dddb31b063a2469bba652 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -6,21 +6,21 @@ use core::{intrinsics, mem}; use core::ops::DerefMut; use spin::Mutex; -use crate::context::file::FileDescriptor; -use crate::context::{ContextId, WaitpidKey}; -use crate::context; -#[cfg(not(feature="doc"))] -use crate::elf::{self, program_header}; -use crate::interrupt; -use crate::ipi::{ipi, IpiKind, IpiTarget}; use crate::memory::allocate_frames; +use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PAGE_SIZE}; use crate::paging::entry::EntryFlags; use crate::paging::mapper::MapperFlushAll; use crate::paging::temporary_page::TemporaryPage; -use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PAGE_SIZE}; -use crate::ptrace; -use crate::scheme::FileHandle; use crate::start::usermode; +use crate::interrupt; +use crate::context; +use crate::context::{ContextId, WaitpidKey}; +use crate::context::file::FileDescriptor; +#[cfg(not(feature="doc"))] +use crate::elf::{self, program_header}; +use crate::ipi::{ipi, IpiKind, IpiTarget}; +use crate::scheme::FileHandle; +use crate::syscall; use crate::syscall::data::{SigAction, Stat}; use crate::syscall::error::*; use crate::syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, CLONE_STACK, @@ -28,7 +28,6 @@ use crate::syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_S SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, SIGCONT, SIGTERM, WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, wifstopped}; use crate::syscall::validate::{validate_slice, validate_slice_mut}; -use crate::syscall; pub fn brk(address: usize) -> Result<usize> { let contexts = context::contexts(); @@ -129,25 +128,10 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> { } if let Some(ref stack) = context.kstack { - // Get the relative offset to the return address of this function - // (base pointer - start of stack) - one offset = stack_base - stack.as_ptr() as usize - mem::size_of::<usize>(); // Add clone ret let mut new_stack = stack.clone(); unsafe { - if let Some(regs) = ptrace::rebase_regs_ptr_mut(context.regs, Some(&mut new_stack)) { - // We'll need to tell the clone that it should - // return 0, but that's it. We don't actually - // clone the registers, because it will then - // become None and be exempt from all kinds of - // ptracing until the current syscall has - // completed. - (*regs).scratch.rax = 0; - } - - // Change the return address of the child - // (previously syscall) to the arch-specific - // clone_ret callback let func_ptr = new_stack.as_mut_ptr().offset(offset as isize); *(func_ptr as *mut usize) = interrupt::syscall::clone_ret as usize; } @@ -1068,8 +1052,6 @@ pub fn exit(status: usize) -> ! { context.id }; - ptrace::close(pid); - // Files must be closed while context is valid so that messages can be passed for (_fd, file_option) in close_files.drain(..).enumerate() { if let Some(file) = file_option { diff --git a/syscall b/syscall index f8eda5ce1bd6fe7f276302493ec54a75a7335fd0..5cdc240d1338b53a1d8b9f652b0cc628317f3109 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit f8eda5ce1bd6fe7f276302493ec54a75a7335fd0 +Subproject commit 5cdc240d1338b53a1d8b9f652b0cc628317f3109