diff --git a/context/context.rs b/context/context.rs index c280dae..1563e6f 100644 --- a/context/context.rs +++ b/context/context.rs @@ -5,7 +5,7 @@ use spin::Mutex; use arch; use context::file::File; -use context::memory::{Grant, Memory, SharedMemory}; +use context::memory::{Grant, Memory, SharedMemory, Tls}; use syscall::data::Event; use sync::{WaitCondition, WaitQueue}; @@ -36,7 +36,7 @@ pub struct Context { /// Context running or not pub running: bool, /// CPU ID, if locked - pub cpuid: Option, + pub cpu_id: Option, /// Context is halting parent pub vfork: bool, /// Context is being waited on @@ -55,6 +55,8 @@ pub struct Context { pub heap: Option, /// User stack pub stack: Option, + /// User Tls + pub tls: Option, /// User grants pub grants: Arc>>, /// The name of the context @@ -81,7 +83,7 @@ impl Context { egid: 0, status: Status::Blocked, running: false, - cpuid: None, + cpu_id: None, vfork: false, waitpid: Arc::new(WaitCondition::new()), wake: None, @@ -91,6 +93,7 @@ impl Context { image: Vec::new(), heap: None, stack: None, + tls: None, grants: Arc::new(Mutex::new(Vec::new())), name: Arc::new(Mutex::new(Vec::new())), cwd: Arc::new(Mutex::new(Vec::new())), @@ -153,6 +156,13 @@ impl Context { pub fn unblock(&mut self) -> bool { if self.status == Status::Blocked { self.status = Status::Runnable; + if let Some(cpu_id) = self.cpu_id { + if cpu_id != ::cpu_id() { + // Send IPI if not on current CPU + // TODO: Make this more architecture independent + unsafe { arch::device::local_apic::LOCAL_APIC.ipi(cpu_id) }; + } + } true } else { false diff --git a/context/memory.rs b/context/memory.rs index 911f619..bc74005 100644 --- a/context/memory.rs +++ b/context/memory.rs @@ -332,3 +332,10 @@ impl Drop for Memory { self.unmap(true); } } + +#[derive(Debug)] +pub struct Tls { + pub master: VirtualAddress, + pub file_size: usize, + pub mem: Memory +} diff --git a/context/mod.rs b/context/mod.rs index 882eb6c..1562d93 100644 --- a/context/mod.rs +++ b/context/mod.rs @@ -50,7 +50,7 @@ pub fn init() { context.kfx = Some(fx); context.status = Status::Runnable; context.running = true; - context.cpuid = Some(::cpu_id()); + context.cpu_id = Some(::cpu_id()); CONTEXT_ID.store(context.id, Ordering::SeqCst); } diff --git a/context/switch.rs b/context/switch.rs index ccc5176..f3ace74 100644 --- a/context/switch.rs +++ b/context/switch.rs @@ -16,27 +16,34 @@ pub unsafe fn switch() -> bool { arch::interrupt::pause(); } + let cpu_id = ::cpu_id(); + let from_ptr; let mut to_ptr = 0 as *mut Context; { let contexts = contexts(); { - let context_lock = contexts.current().expect("context::switch: Not inside of context"); + let context_lock = contexts.current().expect("context::switch: not inside of context"); let mut context = context_lock.write(); from_ptr = context.deref_mut() as *mut Context; } let check_context = |context: &mut Context| -> bool { - if context.cpuid == None || context.cpuid == Some(::cpu_id()) { - if context.status == Status::Blocked && context.wake.is_some() { - let wake = context.wake.expect("context::switch: wake not set"); + if context.cpu_id == None && cpu_id == 0 { + context.cpu_id = Some(cpu_id); + println!("{}: take {} {}", cpu_id, context.id, ::core::str::from_utf8_unchecked(&context.name.lock())); + } - let current = arch::time::monotonic(); - if current.0 > wake.0 || (current.0 == wake.0 && current.1 >= wake.1) { - context.unblock(); - } + if context.status == Status::Blocked && context.wake.is_some() { + let wake = context.wake.expect("context::switch: wake not set"); + + let current = arch::time::monotonic(); + if current.0 > wake.0 || (current.0 == wake.0 && current.1 >= wake.1) { + context.unblock(); } + } + if context.cpu_id == Some(cpu_id) { if context.status == Status::Runnable && ! context.running { return true; } @@ -74,8 +81,6 @@ pub unsafe fn switch() -> bool { return false; } - // println!("{}: Switch {} to {}", ::cpu_id(), (&*from_ptr).id, (&*to_ptr).id); - (&mut *from_ptr).running = false; (&mut *to_ptr).running = true; if let Some(ref stack) = (*to_ptr).kstack { @@ -83,6 +88,9 @@ pub unsafe fn switch() -> bool { } CONTEXT_ID.store((&mut *to_ptr).id, Ordering::SeqCst); + // Unset global lock before switch, as arch is only usable by the current CPU at this time + arch::context::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst); + (&mut *from_ptr).arch.switch_to(&mut (&mut *to_ptr).arch); true diff --git a/lib.rs b/lib.rs index 5da383d..baefe32 100644 --- a/lib.rs +++ b/lib.rs @@ -152,6 +152,19 @@ pub extern fn userspace_init() { panic!("initfs:init returned") } +/// Allow exception handlers to send signal to arch-independant kernel +#[no_mangle] +pub extern fn ksignal(signal: usize) { + println!("SIGNAL {}, CPU {}, PID {}", signal, cpu_id(), context::context_id()); + { + let contexts = context::contexts(); + if let Some(context_lock) = contexts.current() { + let context = context_lock.read(); + println!("NAME {}", unsafe { ::core::str::from_utf8_unchecked(&context.name.lock()) }); + } + } +} + #[no_mangle] pub extern fn kmain(cpus: usize) { CPU_ID.store(0, Ordering::SeqCst); @@ -195,6 +208,14 @@ pub extern fn kmain_ap(id: usize) { println!("AP {}: {:?}", id, pid); loop { - unsafe { interrupt::enable_and_halt() } + unsafe { + interrupt::disable(); + if context::switch() { + interrupt::enable_and_nop(); + } else { + // Enable interrupts, then halt CPU (to save power) until the next interrupt is actually fired. + interrupt::enable_and_halt(); + } + } } } diff --git a/scheme/sys/context.rs b/scheme/sys/context.rs index 8c99a16..75b5e4f 100644 --- a/scheme/sys/context.rs +++ b/scheme/sys/context.rs @@ -5,12 +5,13 @@ use context; use syscall::error::Result; pub fn resource() -> Result> { - let mut string = format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n", + let mut string = format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n", "PID", "PPID", "UID", "GID", "STAT", + "CPU", "MEM", "NAME"); { @@ -18,6 +19,35 @@ pub fn resource() -> Result> { for (_id, context_lock) in contexts.iter() { let context = context_lock.read(); + let mut stat_string = String::new(); + if context.stack.is_some() { + stat_string.push('U'); + } else { + stat_string.push('K'); + } + match context.status { + context::Status::Runnable => { + stat_string.push('R'); + }, + context::Status::Blocked => if context.wake.is_some() { + stat_string.push('S'); + } else { + stat_string.push('B'); + }, + context::Status::Exited(_status) => { + stat_string.push('Z'); + } + } + if context.running { + stat_string.push('+'); + } + + let cpu_string = if let Some(cpu_id) = context.cpu_id { + format!("{}", cpu_id) + } else { + format!("?") + }; + let mut memory = 0; if let Some(ref kfx) = context.kstack { memory += kfx.len(); @@ -49,38 +79,16 @@ pub fn resource() -> Result> { format!("{} B", memory) }; - let mut stat_string = String::new(); - if context.stack.is_some() { - stat_string.push('U'); - } else { - stat_string.push('K'); - } - match context.status { - context::Status::Runnable => { - stat_string.push('R'); - }, - context::Status::Blocked => if context.wake.is_some() { - stat_string.push('S'); - } else { - stat_string.push('B'); - }, - context::Status::Exited(_status) => { - stat_string.push('Z'); - } - } - if context.running { - stat_string.push('+'); - } - let name_bytes = context.name.lock(); let name = str::from_utf8(&name_bytes).unwrap_or(""); - string.push_str(&format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n", + string.push_str(&format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n", context.id, context.ppid, context.euid, context.egid, stat_string, + cpu_string, memory_string, name)); } diff --git a/syscall/process.rs b/syscall/process.rs index e3cd886..39309cf 100644 --- a/syscall/process.rs +++ b/syscall/process.rs @@ -62,6 +62,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { let rgid; let euid; let egid; + let mut cpu_id = None; let arch; let vfork; let mut kfx_option = None; @@ -70,6 +71,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { let mut image = vec![]; let mut heap_option = None; let mut stack_option = None; + let mut tls_option = None; let grants; let name; let cwd; @@ -88,6 +90,10 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { euid = context.euid; egid = context.egid; + if flags & CLONE_VM == CLONE_VM { + cpu_id = context.cpu_id; + } + arch = context.arch.clone(); if let Some(ref fx) = context.kfx { @@ -181,6 +187,29 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { stack_option = Some(new_stack); } + if let Some(ref tls) = context.tls { + let mut new_tls = context::memory::Tls { + master: tls.master, + file_size: tls.file_size, + mem: context::memory::Memory::new( + VirtualAddress::new(arch::USER_TMP_TLS_OFFSET), + tls.mem.size(), + entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE, + true, + false + ) + }; + + unsafe { + arch::externs::memcpy(new_tls.mem.start_address().get() as *mut u8, + tls.master.get() as *const u8, + tls.file_size); + } + + new_tls.mem.remap(tls.mem.flags(), true); + tls_option = Some(new_tls); + } + if flags & CLONE_VM == CLONE_VM { grants = context.grants.clone(); } else { @@ -277,6 +306,8 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { context.euid = euid; context.egid = egid; + context.cpu_id = cpu_id; + context.status = context::Status::Runnable; context.vfork = vfork; @@ -393,6 +424,12 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { context.stack = Some(stack); } + // Setup user TLS + if let Some(mut tls) = tls_option { + tls.mem.move_to(VirtualAddress::new(arch::USER_TLS_OFFSET), &mut new_table, &mut temporary_page, true); + context.tls = Some(tls); + } + context.name = name; context.cwd = cwd; @@ -411,6 +448,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { let entry; let mut sp = arch::USER_STACK_OFFSET + arch::USER_STACK_SIZE - 256; + let fs = arch::USER_STACK_OFFSET; { let mut args = Vec::new(); @@ -466,11 +504,12 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { // Set name context.name = Arc::new(Mutex::new(canonical)); - // Unmap previous image and stack + // Unmap previous image, heap, grants, stack, and tls context.image.clear(); drop(context.heap.take()); - drop(context.stack.take()); context.grants = Arc::new(Mutex::new(Vec::new())); + drop(context.stack.take()); + drop(context.tls.take()); if stat.st_mode & syscall::flag::MODE_SETUID == syscall::flag::MODE_SETUID { context.euid = stat.st_uid; @@ -481,6 +520,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { } // Map and copy new segments + let mut tls_option = None; for segment in elf.segments() { if segment.p_type == program_header::PT_LOAD { let mut memory = context::memory::Memory::new( @@ -514,6 +554,12 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { memory.remap(flags, true); context.image.push(memory.to_shared()); + } else if segment.p_type == program_header::PT_TLS { + tls_option = Some(( + VirtualAddress::new(segment.p_vaddr as usize), + segment.p_filesz as usize, + segment.p_memsz as usize + )); } } @@ -535,6 +581,35 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { true )); + // Map TLS + if let Some((master, file_size, size)) = tls_option { + let tls = context::memory::Tls { + master: master, + file_size: file_size, + mem: context::memory::Memory::new( + VirtualAddress::new(arch::USER_TLS_OFFSET), + size, + entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE, + true, + true + ) + }; + + unsafe { + // Copy file data + memcpy(tls.mem.start_address().get() as *mut u8, + master.get() as *const u8, + file_size); + } + + // Set TLS pointer + //TODO: Do not use stack to store TLS pointer, use a TCB structure instead + unsafe { *(arch::USER_STACK_OFFSET as *mut usize) = tls.mem.start_address().get() + size; } + + context.tls = Some(tls); + } + + // Push arguments let mut arg_size = 0; for arg in args.iter().rev() { sp -= mem::size_of::(); @@ -597,7 +672,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { } // Go to usermode - unsafe { usermode(entry, sp); } + unsafe { usermode(entry, sp, fs); } } pub fn exit(status: usize) -> ! { @@ -897,6 +972,7 @@ pub fn virttophys(virtual_address: usize) -> Result { pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result { loop { let mut exited = false; + let mut running; let waitpid; { let contexts = context::contexts(); @@ -909,10 +985,23 @@ pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result { } exited = true; } + running = context.running; waitpid = context.waitpid.clone(); } if exited { + // Spin until not running + while running { + { + let contexts = context::contexts(); + let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?; + let context = context_lock.read(); + running = context.running; + } + + arch::interrupt::pause(); + } + let mut contexts = context::contexts_mut(); return contexts.remove(pid).ok_or(Error::new(ESRCH)).and(Ok(pid)); } else if flags & WNOHANG == WNOHANG {