From 33e098c124b23a8d6372f5f631104fbe2a24529a Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 15 Sep 2016 08:35:07 -0600 Subject: [PATCH] Fix implementation of clone and exec. Now the init process can load and execute the pci driver --- arch/x86_64/src/lib.rs | 13 +- arch/x86_64/src/start.rs | 3 +- init/src/main.rs | 6 +- kernel/context/file.rs | 1 + kernel/context/memory.rs | 8 +- kernel/context/mod.rs | 1 - kernel/elf.rs | 4 + kernel/lib.rs | 18 ++- kernel/syscall/process.rs | 257 ++++++++++++++++++++++++++++---------- 9 files changed, 233 insertions(+), 78 deletions(-) diff --git a/arch/x86_64/src/lib.rs b/arch/x86_64/src/lib.rs index 21dd805..bbc68ce 100644 --- a/arch/x86_64/src/lib.rs +++ b/arch/x86_64/src/lib.rs @@ -44,16 +44,25 @@ pub extern crate x86; /// Size of kernel percpu variables pub const KERNEL_PERCPU_SIZE: usize = 64 * 1024; // 64 KB + /// Offset to user image + pub const USER_OFFSET: usize = 0; + /// Offset to user heap - pub const USER_HEAP_OFFSET: usize = PML4_SIZE; + pub const USER_HEAP_OFFSET: usize = USER_OFFSET + PML4_SIZE; /// Offset to user stack pub const USER_STACK_OFFSET: usize = USER_HEAP_OFFSET + PML4_SIZE; /// Size of user stack pub const USER_STACK_SIZE: usize = 1024 * 1024; // 1 MB + /// Offset to user temporary image (used when cloning) + pub const USER_TMP_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE; + + /// Offset to user temporary heap (used when cloning) + pub const USER_TMP_HEAP_OFFSET: usize = USER_TMP_OFFSET + PML4_SIZE; + /// Offset to user temporary stack (used when cloning) - pub const USER_TMP_STACK_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE; + pub const USER_TMP_STACK_OFFSET: usize = USER_TMP_HEAP_OFFSET + PML4_SIZE; /// Print to console diff --git a/arch/x86_64/src/start.rs b/arch/x86_64/src/start.rs index 568e62e..213ff4a 100644 --- a/arch/x86_64/src/start.rs +++ b/arch/x86_64/src/start.rs @@ -181,7 +181,8 @@ pub unsafe extern fn kstart_ap(cpu_id: usize, page_table: usize, stack_start: us pub unsafe fn usermode(ip: usize, sp: usize) -> ! { // Go to usermode - asm!("mov ds, ax + asm!("xchg bx, bx + mov ds, ax mov es, ax mov fs, ax mov gs, ax diff --git a/init/src/main.rs b/init/src/main.rs index 093f66a..104f9da 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -1,4 +1,4 @@ -use std::{env, thread}; +use std::env; use std::fs::File; use std::io::{BufRead, BufReader}; use std::process::Command; @@ -47,8 +47,4 @@ pub fn main() { } } } - - loop { - thread::yield_now(); - } } diff --git a/kernel/context/file.rs b/kernel/context/file.rs index 91643ef..67a28eb 100644 --- a/kernel/context/file.rs +++ b/kernel/context/file.rs @@ -1,6 +1,7 @@ //! File struct /// A file +//TODO: Close on exec #[derive(Copy, Clone, Debug)] pub struct File { /// The scheme that this file refers to diff --git a/kernel/context/memory.rs b/kernel/context/memory.rs index e0eaa5f..71ed47c 100644 --- a/kernel/context/memory.rs +++ b/kernel/context/memory.rs @@ -1,6 +1,6 @@ use arch::externs::memset; use arch::paging::{ActivePageTable, InactivePageTable, Page, PageIter, VirtualAddress}; -use arch::paging::entry::EntryFlags; +use arch::paging::entry::{self, EntryFlags}; use arch::paging::temporary_page::TemporaryPage; #[derive(Debug)] @@ -31,6 +31,10 @@ impl Memory { self.size } + pub fn flags(&self) -> EntryFlags { + self.flags + } + pub fn pages(&self) -> PageIter { let start_page = Page::containing_address(self.start); let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1)); @@ -57,7 +61,7 @@ impl Memory { } if clear { - assert!(flush); + assert!(flush && self.flags.contains(entry::WRITABLE)); unsafe { memset(self.start_address().get() as *mut u8, 0, self.size); } } } diff --git a/kernel/context/mod.rs b/kernel/context/mod.rs index 4b657da..e243123 100644 --- a/kernel/context/mod.rs +++ b/kernel/context/mod.rs @@ -167,7 +167,6 @@ pub unsafe fn switch() { // TODO: Sleep, wait for interrupt // Unset global lock if no context found arch::context::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst); - println!("No to_ptr"); return; } diff --git a/kernel/elf.rs b/kernel/elf.rs index 7fb7f9a..5d37ace 100644 --- a/kernel/elf.rs +++ b/kernel/elf.rs @@ -61,8 +61,11 @@ impl<'a> Elf<'a> { let mut context = context_lock.write(); // Unmap previous image and stack + println!("Clear image"); context.image.clear(); + println!("Drop heap"); drop(context.heap.take()); + println!("Drop stack"); drop(context.stack.take()); for segment in self.segments() { @@ -112,6 +115,7 @@ impl<'a> Elf<'a> { } // Go to usermode + println!("Execute {:X}", self.entry()); unsafe { usermode(self.entry(), arch::USER_STACK_OFFSET + arch::USER_STACK_SIZE - 256); } } } diff --git a/kernel/lib.rs b/kernel/lib.rs index bbd70f4..828aac5 100644 --- a/kernel/lib.rs +++ b/kernel/lib.rs @@ -100,6 +100,8 @@ extern crate bitflags; extern crate goblin; extern crate spin; +use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + /// Context management pub mod context; @@ -117,6 +119,14 @@ pub mod syscall; #[cfg(test)] pub mod tests; +#[thread_local] +static CPU_ID: AtomicUsize = ATOMIC_USIZE_INIT; + +#[inline(always)] +pub fn cpu_id() -> usize { + CPU_ID.load(Ordering::Relaxed) +} + pub extern fn userspace_init() { assert_eq!(syscall::open(b"debug:", 0), Ok(0)); assert_eq!(syscall::open(b"debug:", 0), Ok(1)); @@ -129,6 +139,8 @@ pub extern fn userspace_init() { #[no_mangle] pub extern fn kmain() { + CPU_ID.store(0, Ordering::SeqCst); + context::init(); let pid = syscall::getpid(); @@ -144,15 +156,17 @@ pub extern fn kmain() { } } - unsafe { context::switch(); } - loop { unsafe { interrupt::enable_and_halt(); } + + unsafe { context::switch(); } } } #[no_mangle] pub extern fn kmain_ap(id: usize) { + CPU_ID.store(id, Ordering::SeqCst); + context::init(); let pid = syscall::getpid(); diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs index d760d5c..74ed689 100644 --- a/kernel/syscall/process.rs +++ b/kernel/syscall/process.rs @@ -9,6 +9,7 @@ use arch::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, ent use arch::paging::temporary_page::TemporaryPage; use context; use elf; +use scheme; use syscall::{self, Error, Result}; pub fn brk(address: usize) -> Result { @@ -46,82 +47,201 @@ pub fn brk(address: usize) -> Result { } pub fn clone(flags: usize, stack_base: usize) -> Result { + //TODO: Implement flags + //TODO: Copy on write? println!("Clone {:X}: {:X}", flags, stack_base); - let arch; - let mut kstack_option = None; - let mut offset = 0; - let mut stack_option = None; - - // Copy from old process - { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::NoProcess)?; - let context = context_lock.read(); - arch = context.arch.clone(); - if let Some(ref stack) = context.kstack { - offset = stack_base - stack.as_ptr() as usize - mem::size_of::(); // Add clone ret - let mut new_stack = stack.clone(); - unsafe { - let func_ptr = new_stack.as_mut_ptr().offset(offset as isize); - *(func_ptr as *mut usize) = arch::interrupt::syscall::clone_ret as usize; - } - kstack_option = Some(new_stack); - } - if let Some(ref stack) = context.stack { - let new_stack = context::memory::Memory::new( - VirtualAddress::new(arch::USER_TMP_STACK_OFFSET), - stack.size(), - entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE, - true, - true //TODO: Don't clear stack? - ); - unsafe { - arch::externs::memcpy(new_stack.start_address().get() as *mut u8, - stack.start_address().get() as *const u8, - stack.size()); - } - stack_option = Some(new_stack); - } - } - - // Set up new process let pid; { - let mut contexts = context::contexts_mut(); - let context_lock = contexts.new_context()?; - let mut context = context_lock.write(); - context.arch = arch; + let arch; + let mut kstack_option = None; + let mut offset = 0; + let mut image = vec![]; + let mut heap_option = None; + let mut stack_option = None; + let mut files = vec![]; - let mut active_table = unsafe { ActivePageTable::new() }; + // Copy from old process + { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::NoProcess)?; + let context = context_lock.read(); + arch = context.arch.clone(); - let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(0x8_0000_0000))); + println!("Clone kstack"); + if let Some(ref stack) = context.kstack { + offset = stack_base - stack.as_ptr() as usize - mem::size_of::(); // Add clone ret + let mut new_stack = stack.clone(); + unsafe { + let func_ptr = new_stack.as_mut_ptr().offset(offset as isize); + *(func_ptr as *mut usize) = arch::interrupt::syscall::clone_ret as usize; + } + kstack_option = Some(new_stack); + } - let mut new_table = { - let frame = allocate_frame().expect("no more frames in syscall::clone new_table"); - InactivePageTable::new(frame, &mut active_table, &mut temporary_page) - }; + println!("Clone image"); + for memory in context.image.iter() { + let mut new_memory = context::memory::Memory::new( + VirtualAddress::new(memory.start_address().get() + arch::USER_TMP_OFFSET), + memory.size(), + entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE, + true, + false + ); + unsafe { + arch::externs::memcpy(new_memory.start_address().get() as *mut u8, + memory.start_address().get() as *const u8, + memory.size()); + } + new_memory.remap(memory.flags(), true); + image.push(new_memory); + } - // Copy kernel mapping - let kernel_frame = active_table.p4()[510].pointed_frame().expect("kernel table not mapped"); - active_table.with(&mut new_table, &mut temporary_page, |mapper| { - mapper.p4_mut()[510].set(kernel_frame, entry::PRESENT | entry::WRITABLE); - }); + println!("Clone heap"); + if let Some(ref heap) = context.heap { + let mut new_heap = context::memory::Memory::new( + VirtualAddress::new(arch::USER_TMP_HEAP_OFFSET), + heap.size(), + entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE, + true, + false + ); + unsafe { + arch::externs::memcpy(new_heap.start_address().get() as *mut u8, + heap.start_address().get() as *const u8, + heap.size()); + } + new_heap.remap(heap.flags(), true); + heap_option = Some(new_heap); + } - if let Some(stack) = kstack_option.take() { - context.arch.set_stack(stack.as_ptr() as usize + offset); - context.kstack = Some(stack); + println!("Clone stack"); + if let Some(ref stack) = context.stack { + let mut new_stack = context::memory::Memory::new( + VirtualAddress::new(arch::USER_TMP_STACK_OFFSET), + stack.size(), + entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE, + true, + false + ); + unsafe { + arch::externs::memcpy(new_stack.start_address().get() as *mut u8, + stack.start_address().get() as *const u8, + stack.size()); + } + new_stack.remap(stack.flags(), true); + stack_option = Some(new_stack); + } + + println!("Clone files"); + for (fd, file_option) in context.files.iter().enumerate() { + if let Some(file) = *file_option { + let result = { + let schemes = scheme::schemes(); + let scheme_mutex = schemes.get(file.scheme).ok_or(Error::BadFile)?; + let result = scheme_mutex.lock().dup(file.number); + result + }; + match result { + Ok(new_number) => { + files.push(Some(context::file::File { scheme: file.scheme, number: new_number })); + }, + Err(err) => { + println!("clone: failed to dup {}: {:?}", fd, err); + } + } + } else { + files.push(None); + } + } } - if let Some(mut stack) = stack_option.take() { - stack.move_to(VirtualAddress::new(arch::USER_STACK_OFFSET), &mut new_table, &mut temporary_page, true); - context.stack = Some(stack); + // Set up new process + { + let mut contexts = context::contexts_mut(); + let context_lock = contexts.new_context()?; + let mut context = context_lock.write(); + + pid = context.id; + + context.arch = arch; + + let mut active_table = unsafe { ActivePageTable::new() }; + + let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(0x8_0000_0000))); + + let mut new_table = { + let frame = allocate_frame().expect("no more frames in syscall::clone new_table"); + InactivePageTable::new(frame, &mut active_table, &mut temporary_page) + }; + + // Copy kernel mapping + println!("Set kernel mapping"); + { + let kernel_frame = active_table.p4()[510].pointed_frame().expect("kernel table not mapped"); + active_table.with(&mut new_table, &mut temporary_page, |mapper| { + mapper.p4_mut()[510].set(kernel_frame, entry::PRESENT | entry::WRITABLE); + }); + } + + // Copy percpu mapping + println!("Set kernel percpu"); + { + extern { + /// The starting byte of the thread data segment + static mut __tdata_start: u8; + /// The ending byte of the thread BSS segment + static mut __tbss_end: u8; + } + + let size = unsafe { & __tbss_end as *const _ as usize - & __tdata_start as *const _ as usize }; + + let start = arch::KERNEL_PERCPU_OFFSET + arch::KERNEL_PERCPU_SIZE * ::cpu_id(); + let end = start + size; + + let start_page = Page::containing_address(VirtualAddress::new(start)); + let end_page = Page::containing_address(VirtualAddress::new(end - 1)); + for page in Page::range_inclusive(start_page, end_page) { + let frame = active_table.translate_page(page).expect("kernel percpu not mapped"); + active_table.with(&mut new_table, &mut temporary_page, |mapper| { + mapper.map_to(page, frame, entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE); + }); + } + } + + + println!("Set kstack"); + if let Some(stack) = kstack_option.take() { + context.arch.set_stack(stack.as_ptr() as usize + offset); + context.kstack = Some(stack); + } + + println!("Set image"); + for memory in image.iter_mut() { + let start = VirtualAddress::new(memory.start_address().get() - arch::USER_TMP_OFFSET + arch::USER_OFFSET); + memory.move_to(start, &mut new_table, &mut temporary_page, true); + } + context.image = image; + + println!("Set heap"); + if let Some(mut heap) = heap_option.take() { + heap.move_to(VirtualAddress::new(arch::USER_HEAP_OFFSET), &mut new_table, &mut temporary_page, true); + context.heap = Some(heap); + } + + println!("Set stack"); + if let Some(mut stack) = stack_option.take() { + stack.move_to(VirtualAddress::new(arch::USER_STACK_OFFSET), &mut new_table, &mut temporary_page, true); + context.stack = Some(stack); + } + + println!("Set files"); + context.files = files; + + context.arch.set_page_table(unsafe { new_table.address() }); + + context.blocked = false; } - - context.arch.set_page_table(unsafe { new_table.address() }); - - context.blocked = false; - pid = context.id; } unsafe { context::switch(); } @@ -153,8 +273,10 @@ pub fn exec(path: &[u8], _args: &[[usize; 2]]) -> Result { //TODO: Drop data vec println!("Exec {}", unsafe { str::from_utf8_unchecked(path) }); + println!("Open"); let file = syscall::open(path, 0)?; let mut data = vec![]; + println!("Reading"); loop { let mut buf = [0; 4096]; let count = syscall::read(file, &mut buf)?; @@ -164,10 +286,15 @@ pub fn exec(path: &[u8], _args: &[[usize; 2]]) -> Result { break; } } + println!("Close"); let _ = syscall::close(file); + println!("Parse"); match elf::Elf::from(&data) { - Ok(elf) => elf.run().and(Ok(0)), + Ok(elf) => { + println!("Run"); + elf.run().and(Ok(0)) + }, Err(err) => { println!("failed to execute {}: {}", unsafe { str::from_utf8_unchecked(path) }, err); Err(Error::NoExec)