From 852903de200e90cc553db0a1a95f07998d21c6d9 Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Sat, 15 Dec 2018 12:36:02 +0800 Subject: [PATCH] Add virtual memory subsystem --- src/libos/Makefile | 8 +- src/libos/{src => include}/errno.h | 0 src/libos/{src => include}/syscall.h | 0 src/libos/{src => include}/syscall_nr.h | 0 src/libos/{src => include}/task.h | 0 src/libos/src/errno.rs | 3 +- src/libos/src/file.rs | 1 + src/libos/src/fs.rs | 14 +- src/libos/src/lib.rs | 6 +- src/libos/src/mm.rs | 81 --- src/libos/src/prelude.rs | 26 +- src/libos/src/process.rs | 417 -------------- src/libos/src/process/mod.rs | 45 ++ src/libos/src/process/process.rs | 79 +++ src/libos/src/process/process_table.rs | 32 ++ .../src/{ => process/spawn}/elf_helper.rs | 2 +- .../src/{ => process/spawn}/init_stack.rs | 80 ++- src/libos/src/process/spawn/init_vm.rs | 106 ++++ src/libos/src/process/spawn/mod.rs | 106 ++++ src/libos/src/process/spawn/segment.rs | 102 ++++ src/libos/src/{ => process}/task.c | 0 src/libos/src/process/task.rs | 100 ++++ src/libos/src/{ => process}/task_x86-64.S | 0 src/libos/src/vm/mod.rs | 178 ++++++ src/libos/src/vm/process_vm.rs | 245 +++++++++ src/libos/src/vm/vm_area.rs | 11 + src/libos/src/vm/vm_domain.rs | 31 ++ src/libos/src/vm/vm_range.rs | 512 ++++++++++++++++++ src/libos/src/vm/vm_space.rs | 39 ++ src/libos/src/vma.rs | 211 -------- 30 files changed, 1659 insertions(+), 776 deletions(-) rename src/libos/{src => include}/errno.h (100%) rename src/libos/{src => include}/syscall.h (100%) rename src/libos/{src => include}/syscall_nr.h (100%) rename src/libos/{src => include}/task.h (100%) delete mode 100644 src/libos/src/mm.rs delete mode 100644 src/libos/src/process.rs create mode 100644 src/libos/src/process/mod.rs create mode 100644 src/libos/src/process/process.rs create mode 100644 src/libos/src/process/process_table.rs rename src/libos/src/{ => process/spawn}/elf_helper.rs (99%) rename src/libos/src/{ => process/spawn}/init_stack.rs (81%) create mode 100644 src/libos/src/process/spawn/init_vm.rs create mode 100644 src/libos/src/process/spawn/mod.rs create mode 100644 src/libos/src/process/spawn/segment.rs rename src/libos/src/{ => process}/task.c (100%) create mode 100644 src/libos/src/process/task.rs rename src/libos/src/{ => process}/task_x86-64.S (100%) create mode 100644 src/libos/src/vm/mod.rs create mode 100644 src/libos/src/vm/process_vm.rs create mode 100644 src/libos/src/vm/vm_area.rs create mode 100644 src/libos/src/vm/vm_domain.rs create mode 100644 src/libos/src/vm/vm_range.rs create mode 100644 src/libos/src/vm/vm_space.rs delete mode 100644 src/libos/src/vma.rs diff --git a/src/libos/Makefile b/src/libos/Makefile index 16309c25..abcb38a8 100644 --- a/src/libos/Makefile +++ b/src/libos/Makefile @@ -6,17 +6,17 @@ LIBOS_ENCLAVE := libocclum.signed.so LIBOS_SO := libocclum.so # Link $(LIBOS_A), $(C_OBJS) and all dependencies LIBOS_A := liblibos.a # Built from Rust code -RUST_SRCS := $(wildcard src/*.rs) -C_SRCS := $(sort $(filter-out src/Enclave_t.c, $(wildcard src/*.c))) src/Enclave_t.c +RUST_SRCS := $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs) +C_SRCS := $(sort $(filter-out src/Enclave_t.c, $(wildcard src/*.c src/*/*.c))) src/Enclave_t.c C_OBJS := $(C_SRCS:.c=.o) -S_SRCS := $(wildcard src/*.S) +S_SRCS := $(wildcard src/*.S src/*/*.S) S_OBJS := $(S_SRCS:.S=.o) EDL_C_SRCS := src/Enclave_t.c src/Enclave_t.h ENCLAVE_CONFIG := Enclave_config.xml ENCLAVE_KEY := Enclave_private.pem -C_FLAGS := $(SGX_CFLAGS_T) -fno-stack-protector +C_FLAGS := $(SGX_CFLAGS_T) -fno-stack-protector -I./include/ _Other_Link_Flags := -L../../deps/rust-sgx-sdk/compiler-rt/ -L. _Other_Enclave_Libs := -lcompiler-rt-patch -llibos -lsgx_tprotected_fs LINK_FLAGS := $(SGX_LFLAGS_T) diff --git a/src/libos/src/errno.h b/src/libos/include/errno.h similarity index 100% rename from src/libos/src/errno.h rename to src/libos/include/errno.h diff --git a/src/libos/src/syscall.h b/src/libos/include/syscall.h similarity index 100% rename from src/libos/src/syscall.h rename to src/libos/include/syscall.h diff --git a/src/libos/src/syscall_nr.h b/src/libos/include/syscall_nr.h similarity index 100% rename from src/libos/src/syscall_nr.h rename to src/libos/include/syscall_nr.h diff --git a/src/libos/src/task.h b/src/libos/include/task.h similarity index 100% rename from src/libos/src/task.h rename to src/libos/include/task.h diff --git a/src/libos/src/errno.rs b/src/libos/src/errno.rs index 01a598cc..d16dce2f 100644 --- a/src/libos/src/errno.rs +++ b/src/libos/src/errno.rs @@ -1,6 +1,8 @@ use prelude::*; use std::{fmt, error, convert,}; +// TODO: remove errno.h + #[derive(Clone, Copy, Debug, PartialEq)] pub struct Error { pub errno: Errno, @@ -13,7 +15,6 @@ impl Error { errno, desc, }; - println!("{}", ret); ret } } diff --git a/src/libos/src/file.rs b/src/libos/src/file.rs index 8c1c97a0..697b0234 100644 --- a/src/libos/src/file.rs +++ b/src/libos/src/file.rs @@ -1,6 +1,7 @@ use prelude::*; use {std}; use std::{fmt}; +use std::borrow::BorrowMut; use std::sgxfs as fs_impl; diff --git a/src/libos/src/fs.rs b/src/libos/src/fs.rs index f22e8e06..1bae66ed 100644 --- a/src/libos/src/fs.rs +++ b/src/libos/src/fs.rs @@ -50,7 +50,7 @@ pub fn do_open(path: &str, flags: u32, mode: u32) -> Result { let current_ref = process::get_current(); let mut current_process = current_ref.lock().unwrap(); - let fd = current_process.file_table.put(file_ref); + let fd = current_process.get_files_mut().put(file_ref); Ok(fd) } @@ -58,7 +58,7 @@ pub fn do_open(path: &str, flags: u32, mode: u32) -> Result { pub fn do_write(fd: FileDesc, buf: &[u8]) -> Result { let current_ref = process::get_current(); let current_process = current_ref.lock().unwrap(); - let file_ref = current_process.file_table.get(fd) + let file_ref = current_process.get_files().get(fd) .ok_or_else(|| Error::new(Errno::EBADF, "Invalid file descriptor [do_write]"))?; file_ref.write(buf) } @@ -66,7 +66,7 @@ pub fn do_write(fd: FileDesc, buf: &[u8]) -> Result { pub fn do_read(fd: FileDesc, buf: &mut [u8]) -> Result { let current_ref = process::get_current(); let current_process = current_ref.lock().unwrap(); - let file_ref = current_process.file_table.get(fd) + let file_ref = current_process.get_files().get(fd) .ok_or_else(|| Error::new(Errno::EBADF, "Invalid file descriptor [do_read]"))?; file_ref.read(buf) } @@ -74,7 +74,7 @@ pub fn do_read(fd: FileDesc, buf: &mut [u8]) -> Result { pub fn do_writev<'a, 'b>(fd: FileDesc, bufs: &'a [&'b [u8]]) -> Result { let current_ref = process::get_current(); let current_process = current_ref.lock().unwrap(); - let file_ref = current_process.file_table.get(fd) + let file_ref = current_process.get_files().get(fd) .ok_or_else(|| Error::new(Errno::EBADF, "Invalid file descriptor [do_write]"))?; file_ref.writev(bufs) } @@ -82,7 +82,7 @@ pub fn do_writev<'a, 'b>(fd: FileDesc, bufs: &'a [&'b [u8]]) -> Result(fd: FileDesc, bufs: &'a mut [&'b mut [u8]]) -> Result { let current_ref = process::get_current(); let current_process = current_ref.lock().unwrap(); - let file_ref = current_process.file_table.get(fd) + let file_ref = current_process.get_files().get(fd) .ok_or_else(|| Error::new(Errno::EBADF, "Invalid file descriptor [do_read]"))?; file_ref.readv(bufs) } @@ -90,7 +90,7 @@ pub fn do_readv<'a, 'b>(fd: FileDesc, bufs: &'a mut [&'b mut [u8]]) -> Result(fd: FileDesc, offset: SeekFrom) -> Result { let current_ref = process::get_current(); let current_process = current_ref.lock().unwrap(); - let file_ref = current_process.file_table.get(fd) + let file_ref = current_process.get_files().get(fd) .ok_or_else(|| Error::new(Errno::EBADF, "Invalid file descriptor [do_lseek]"))?; file_ref.seek(offset) } @@ -98,7 +98,7 @@ pub fn do_lseek<'a, 'b>(fd: FileDesc, offset: SeekFrom) -> Result pub fn do_close(fd: FileDesc) -> Result<(), Error> { let current_ref = process::get_current(); let mut current_process = current_ref.lock().unwrap(); - let file_table = &mut current_process.file_table; + let file_table = current_process.get_files_mut(); match file_table.del(fd) { Some(_) => Ok(()), None => Err(Error::new(Errno::EBADF, "Invalid file descriptor [do_close]")), diff --git a/src/libos/src/lib.rs b/src/libos/src/lib.rs index 0a8a40ce..80fffdae 100644 --- a/src/libos/src/lib.rs +++ b/src/libos/src/lib.rs @@ -7,6 +7,7 @@ #![cfg_attr(target_env = "sgx", feature(rustc_private))] #![feature(allocator_api)] #![feature(integer_atomics)] +#![feature(range_contains)] extern crate sgx_types; #[cfg(not(target_env = "sgx"))] @@ -26,16 +27,13 @@ use sgx_trts::libc; #[macro_use] mod prelude; -mod elf_helper; mod errno; mod file; mod file_table; mod fs; -mod mm; mod process; mod syscall; -mod vma; -mod init_stack; +mod vm; /// Export system calls pub use syscall::*; diff --git a/src/libos/src/mm.rs b/src/libos/src/mm.rs deleted file mode 100644 index 67b9d06d..00000000 --- a/src/libos/src/mm.rs +++ /dev/null @@ -1,81 +0,0 @@ -use prelude::*; - -use std::mem; - -#[derive(Clone, Debug)] -pub struct MemObj { - mem_ptr: *mut c_void, - mem_size: usize, - mem_align: usize, -} - -impl MemObj { - pub fn new(mem_size: usize, mem_align: usize) - -> Result - { - if mem_size == 0 || !is_power_of_two(mem_align) || - mem_align % mem::size_of::<*const c_void>() != 0 { - return Err((Errno::EINVAL, "Invalid argument").into()); - } - - let mem_ptr = unsafe { aligned_malloc(mem_size, mem_align) }; - if mem_ptr == (0 as *mut c_void) { - return Err((Errno::ENOMEM, "Out of memory").into()); - }; - unsafe { memset(mem_ptr, 0 as c_int, mem_size as size_t) }; - - Ok(MemObj { - mem_ptr, - mem_size, - mem_align, - }) - } - - pub fn get_addr(&self) -> usize { - self.mem_ptr as usize - } -} - -impl Default for MemObj { - fn default() -> Self { - MemObj { - mem_ptr: 0 as *mut c_void, - mem_size: 0, - mem_align: 1 - } - } -} - -impl Drop for MemObj { - fn drop(&mut self) { - if self.mem_ptr != (0 as *mut c_void) { - unsafe { free(self.mem_ptr); } - } - } -} - -unsafe impl Send for MemObj {} -unsafe impl Sync for MemObj {} - -fn is_power_of_two(x: usize) -> bool { - return (x != 0) && ((x & (x - 1)) == 0); -} - -unsafe fn aligned_malloc(mem_size: usize, mem_align: usize) -> *mut c_void { - let mut mem_ptr = ::core::ptr::null_mut(); - let ret = libc::posix_memalign(&mut mem_ptr, mem_align, mem_size); - if ret == 0 { - mem_ptr - } else { - 0 as *mut c_void - } -} - -unsafe fn free(mem_ptr: *mut c_void) { - libc::free(mem_ptr) -} - -#[link(name = "sgx_tstdc")] -extern { - pub fn memset(p: *mut c_void, c: c_int, n: size_t) -> *mut c_void; -} diff --git a/src/libos/src/prelude.rs b/src/libos/src/prelude.rs index 2bf8f5ec..f1f40e93 100644 --- a/src/libos/src/prelude.rs +++ b/src/libos/src/prelude.rs @@ -7,18 +7,23 @@ pub use sgx_trts::libc; pub use std::marker::{Sync, Send}; pub use std::sync::{Arc, SgxMutex, SgxMutexGuard, SgxRwLock, SgxRwLockReadGuard, SgxRwLockWriteGuard}; -pub use std::cell::{Cell}; +pub use std::cell::{Cell, RefCell}; pub use std::result::Result; -pub use std::borrow::BorrowMut; +//pub use std::borrow::BorrowMut; pub use std::boxed::Box; pub use std::vec::Vec; pub use std::string::{String}; pub use std::collections::{HashMap, VecDeque}; pub use std::fmt::{Debug, Display}; pub use std::io::{Read, Write, Seek, SeekFrom}; +pub use std::rc::{Rc}; +pub use std::iter::{Iterator}; +pub use std::cmp::{Ordering, PartialOrd}; +pub use std::borrow::{ToOwned}; pub use errno::Error as Error; pub use errno::Errno; +pub use errno::Errno::{*}; pub use fs::off_t; @@ -27,3 +32,20 @@ macro_rules! debug_trace { println!("> Line = {}, File = {}", line!(), file!()) }; } + +macro_rules! errno { + ($errno: ident, $msg: expr) => { + { + println!("ERROR: {} ({}, line {} in file {})", $errno, $msg, line!(), file!()); + Err(Error::new($errno, $msg)) + } + } +} + +pub fn align_up(addr: usize, align: usize) -> usize { + (addr + (align - 1)) / align * align +} + +pub fn align_down(addr: usize, align: usize) -> usize { + addr & !(align - 1) +} diff --git a/src/libos/src/process.rs b/src/libos/src/process.rs deleted file mode 100644 index 43666822..00000000 --- a/src/libos/src/process.rs +++ /dev/null @@ -1,417 +0,0 @@ -use prelude::*; -use {std, elf_helper, vma, syscall, file, file_table}; -use std::{io, mem}; -use std::path::Path; -use std::io::{Read}; -use std::sync::atomic::{AtomicU32, Ordering}; -use std::sgxfs::SgxFile; -use std::thread; -use std::cell::Cell; -use std::ffi::{CStr, CString}; - -use xmas_elf::{ElfFile, header, program, sections}; -use xmas_elf::symbol_table::Entry; - -use vma::Vma; -use file::{File, StdinFile, StdoutFile/*, StderrFile*/}; -use file_table::{FileTable}; -use init_stack::{StackBuf, AuxKey, AuxTable, do_init_process_stack}; - -lazy_static! { - static ref PROCESS_TABLE: SgxMutex> = { - SgxMutex::new(HashMap::new()) - }; -} - -fn put_into_pid_table(pid: u32, process: ProcessRef) { - PROCESS_TABLE.lock().unwrap().insert(pid, process); -} - -fn del_from_pid_table(pid: u32) { - PROCESS_TABLE.lock().unwrap().remove(&pid); -} - -fn look_up_pid_table(pid: u32) -> Option { - PROCESS_TABLE.lock().unwrap().get(&pid).map(|pr| pr.clone()) -} - - -static NEXT_PID : AtomicU32 = AtomicU32::new(1); - -fn alloc_pid() -> u32 { - NEXT_PID.fetch_add(1, Ordering::SeqCst) -} - -fn free_pid(pid: u32) { - // TODO: -} - - -pub fn do_spawn>(elf_path: &P, argv: &[CString], envp: &[CString]) - -> Result -{ - let elf_buf = open_elf(&elf_path) - .map_err(|e| (e.errno, "Failed to open the ELF file"))?; - - let elf_file = { - let elf_file = ElfFile::new(&elf_buf) - .map_err(|e| (Errno::ENOEXEC, "Failed to parse the ELF file"))?; - header::sanity_check(&elf_file) - .map_err(|e| (Errno::ENOEXEC, "Failed to parse the ELF file"))?; - - /* - elf_helper::print_program_headers(&elf_file)?; - elf_helper::print_sections(&elf_file)?; - elf_helper::print_pltrel_section(&elf_file)?; - */ - elf_file - }; - - let new_process = { - let mut new_process = Process::new(&elf_file, argv, envp) - .map_err(|e| (Errno::EUNDEF, "Failed to create the process"))?; - - { - let file_table = &mut new_process.file_table; - - //let stdin = Arc::new(SgxMutex::new(Box::new(StdinFile::new()))); - let stdin : Arc> = Arc::new(Box::new(StdinFile::new())); - let stdout : Arc> = Arc::new(Box::new(StdoutFile::new())); - // TODO: implement and use a real stderr - let stderr = stdout.clone(); - file_table.put(stdin); - file_table.put(stdout); - file_table.put(stderr); - }; - - new_process - }; - - let new_pid = new_process.pid; - let new_process_ref = Arc::new(SgxMutex::new(new_process)); - enqueue_new_process(new_process_ref.clone()); - put_into_pid_table(new_pid, new_process_ref.clone()); - - // FIXME: if ocall_new_task failed, then new_process will not be dropped - let mut ret = 0; - let ocall_status = unsafe { ocall_run_new_task(&mut ret) }; - if ocall_status != sgx_status_t::SGX_SUCCESS || ret != 0 { - return Err((Errno::EUNDEF, "Failed to start the process").into()); - } - - Ok(new_pid) -} - -thread_local! { - static _CURRENT_PROCESS_PTR: Cell<*const SgxMutex> = - Cell::new(0 as *const SgxMutex); -} - -pub fn set_current(process: &ProcessRef) { - let process_ref_clone = process.clone(); - let process_ptr = Arc::into_raw(process_ref_clone); - - _CURRENT_PROCESS_PTR.with(|cp| { - cp.set(process_ptr); - }); -} - -pub fn reset_current() { - let mut process_ptr = 0 as *const SgxMutex; - _CURRENT_PROCESS_PTR.with(|cp| { - process_ptr = cp.get(); - cp.set(0 as *const SgxMutex); - }); - - // Prevent memory leakage - unsafe { drop(Arc::from_raw(process_ptr)); } -} - -pub fn get_current() -> &'static SgxMutex { - let mut process_ptr : *const SgxMutex = 0 as *const SgxMutex; - _CURRENT_PROCESS_PTR.with(|cp| { - process_ptr = cp.get(); - }); - unsafe { - mem::transmute::<*const SgxMutex, &'static SgxMutex>(process_ptr) - } -} - -pub fn do_getpid() -> u32 { - let current_ref = get_current(); - let current_process = current_ref.lock().unwrap(); - current_process.pid -} - -pub fn do_exit(exit_code: i32) { - { - let current_ref = get_current(); - let mut current_process = current_ref.lock().unwrap(); - current_process.exit_code = exit_code; - current_process.status = Status::ZOMBIE; - } -} - -pub fn do_wait4(child_pid: u32) -> Result { - let child_process = look_up_pid_table(child_pid) - .ok_or_else(|| (Errno::ECHILD, "Cannot find child process with the given PID"))?; - let mut exit_code = 0; - loop { - let guard = child_process.lock().unwrap(); - if guard.status == Status::ZOMBIE { - exit_code = guard.exit_code; - break; - } - drop(guard); - } - - let child_pid = child_process.lock().unwrap().pid; - del_from_pid_table(child_pid); - - Ok(exit_code) -} - -pub fn run_task() -> Result<(), Error> { - let new_process : ProcessRef = dequeue_new_process() - .ok_or_else(|| (Errno::EAGAIN, "No new processes to run"))?; - set_current(&new_process); - - let pid; - let new_task; - { - let guard = new_process.lock().unwrap(); - let process : &Process = &guard; - pid = process.pid; - //println!("Run process: {:#x?}", process); - //println!("Run process (pid = {})", process.pid); - new_task = &process.task as *const Task - }; - - unsafe { do_run_task(new_task as *const Task); } - - // Init process does not have any parent, so it has to release itself - if pid == 1 { - del_from_pid_table(1); - } - - reset_current(); - - Ok(()) -} - -fn open_elf>(path: &P) -> Result, Error> { - let key : sgx_key_128bit_t = [0 as uint8_t; 16]; - let mut elf_file = SgxFile::open_ex(path, &key) - .map_err(|e| (Errno::ENOENT, "Failed to open the SGX-protected file"))?; - - let mut elf_buf = Vec::::new(); - elf_file.read_to_end(&mut elf_buf); - - Ok(elf_buf) -} - - -#[derive(Debug, Default)] -#[repr(C)] -pub struct Process { - pub task: Task, - pub status: Status, - pub pid: u32, - pub exit_code: i32, - pub code_vma: Vma, - pub data_vma: Vma, - pub stack_vma: Vma, - pub program_base_addr: usize, - pub program_entry_addr: usize, - pub file_table: FileTable, -} -pub type ProcessRef = Arc>; - -impl Process { - pub fn new(elf_file: &ElfFile, argv: &[CString], envp: &[CString]) - -> Result - { - let mut new_process : Process = Default::default(); - new_process.create_process_image(elf_file)?; - new_process.reloc_symbols(elf_file)?; - new_process.link_syscalls(elf_file)?; - new_process.mprotect()?; - //println!("Process image: {:#X?}", new_process); - - new_process.task = Task { - user_stack_addr: new_process.init_stack(argv, envp)? as usize, - user_entry_addr: new_process.program_entry_addr, - fs_base_addr: 0, - .. Default::default() - }; - - new_process.pid = alloc_pid(); - - Ok(new_process) - } - - fn init_stack(&mut self, argv: &[CString], envp: &[CString]) - -> Result<*const u8, Error> - { - let stack = StackBuf::new(self.stack_vma.mem_end as *const u8, - self.stack_vma.mem_begin as *const u8)?; - - let mut auxtbl = AuxTable::new(); - auxtbl.set_val(AuxKey::AT_PAGESZ, 4096)?; - auxtbl.set_val(AuxKey::AT_UID, 0)?; - auxtbl.set_val(AuxKey::AT_GID, 0)?; - auxtbl.set_val(AuxKey::AT_EUID, 0)?; - auxtbl.set_val(AuxKey::AT_EGID, 0)?; - auxtbl.set_val(AuxKey::AT_SECURE, 0)?; - - do_init_process_stack(&stack, &argv, &envp, &auxtbl)?; - - Ok(stack.get_pos()) - } - - fn create_process_image(self: &mut Process, elf_file: &ElfFile) - -> Result<(), Error> - { - let code_ph = elf_helper::get_code_program_header(elf_file) - .map_err(|e| (Errno::ENOEXEC, "Failed to get the program header of code"))?; - let data_ph = elf_helper::get_data_program_header(elf_file) - .map_err(|e| (Errno::ENOEXEC, "Failed to get the program header of code"))?; - - self.code_vma = Vma::from_program_header(&code_ph)?; - self.data_vma = Vma::from_program_header(&data_ph)?; - self.stack_vma = Vma::new(32 * 1024 * 1024, 4096, - vma::Perms(vma::PERM_R | vma::PERM_W))?; - - self.program_base_addr = self.alloc_mem_for_vmas(elf_file)?; - self.program_entry_addr = self.program_base_addr + - elf_helper::get_start_address(elf_file)?; - if !self.code_vma.contains(self.program_entry_addr) { - return Err((Errno::EINVAL, "Entry address is out of the code segment").into()); - } - - Ok(()) - } - - fn alloc_mem_for_vmas(self: &mut Process, elf_file: &ElfFile) - -> Result - { - let mut vma_list = vec![&mut self.code_vma, &mut self.data_vma, &mut self.stack_vma]; - let base_addr = vma::malloc_batch(&mut vma_list, elf_file.input)?; - - Ok(base_addr) - } - - fn reloc_symbols(self: &mut Process, elf_file: &ElfFile) - -> Result<(), Error> - { - let rela_entries = elf_helper::get_rela_entries(&elf_file, ".rela.dyn")?; - for rela_entry in rela_entries { - /* - println!("\toffset: {:#X}, symbol index: {}, type: {}, addend: {:#X}", - rela_entry.get_offset(), - rela_entry.get_symbol_table_index(), - rela_entry.get_type(), - rela_entry.get_addend()); - */ - - /* reloc type == R_X86_64_RELATIVE */ - match rela_entry.get_type() { - 8 if rela_entry.get_symbol_table_index() == 0 => { - let rela_addr = self.program_base_addr + rela_entry.get_offset() as usize; - let rela_val = self.program_base_addr + rela_entry.get_addend() as usize; - unsafe { - std::ptr::write_unaligned(rela_addr as *mut usize, rela_val); - } - } - // TODO: need to handle other relocation types - _ => { } - } - } - Ok(()) - } - - fn link_syscalls(self: &mut Process, elf_file: &ElfFile) - -> Result<(), Error> - { - let syscall_addr = __occlum_syscall as *const () as usize; - - let rela_entries = elf_helper::get_rela_entries(&elf_file, ".rela.plt")?; - let dynsym_entries = elf_helper::get_dynsym_entries(&elf_file)?; - for rela_entry in rela_entries { - let dynsym_idx = rela_entry.get_symbol_table_index() as usize; - let dynsym_entry = &dynsym_entries[dynsym_idx]; - let dynsym_str = dynsym_entry.get_name(elf_file) - .map_err(|e| Error::new(Errno::ENOEXEC, - "Failed to get the name of dynamic symbol"))?; - - if dynsym_str == "__occlum_syscall" { - let rela_addr = self.program_base_addr + rela_entry.get_offset() as usize; - unsafe { - std::ptr::write_unaligned(rela_addr as *mut usize, syscall_addr); - } - } - } - - Ok(()) - } - - fn mprotect(self: &mut Process) -> Result<(), Error> { - let vma_list = vec![&self.code_vma, &self.data_vma, &self.stack_vma]; - vma::mprotect_batch(&vma_list) - } -} - -impl Drop for Process { - fn drop(&mut self) { - free_pid(self.pid); - } -} - -/// Note: this definition must be in sync with task.h -#[derive(Clone, Debug, Default)] -#[repr(C)] -pub struct Task { - pub syscall_stack_addr: usize, - pub user_stack_addr: usize, - pub user_entry_addr: usize, - pub fs_base_addr: usize, - pub saved_state: usize, // struct jmpbuf* -} - -lazy_static! { - static ref new_process_queue: SgxMutex> = { - SgxMutex::new(VecDeque::new()) - }; -} - - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Status { - RUNNING, - INTERRUPTIBLE, - ZOMBIE, - STOPPED, -} - -impl Default for Status { - fn default() -> Status { - Status::RUNNING - } -} - - -fn dequeue_new_process() -> Option { - new_process_queue.lock().unwrap().pop_front() -} - -fn enqueue_new_process(new_process: ProcessRef) { - new_process_queue.lock().unwrap().push_back(new_process) -} - - -extern { - fn ocall_run_new_task(ret: *mut i32) -> sgx_status_t; - fn do_run_task(task: *const Task) -> i32; - fn do_exit_task(); - fn __occlum_syscall(num: i32, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64) -> i64; -} diff --git a/src/libos/src/process/mod.rs b/src/libos/src/process/mod.rs new file mode 100644 index 00000000..3a9a55b5 --- /dev/null +++ b/src/libos/src/process/mod.rs @@ -0,0 +1,45 @@ +pub use self::process::{Process, ProcessRef, Status, pid_t}; +pub use self::task::{get_current, run_task}; +pub mod table { + pub use super::process_table::{get}; +} +pub use self::spawn::{do_spawn}; + +pub fn do_getpid() -> pid_t { + let current_ref = get_current(); + let current_process = current_ref.lock().unwrap(); + current_process.get_pid() +} + +pub fn do_exit(exit_code: i32) { + let current_ref = get_current(); + let mut current_process = current_ref.lock().unwrap(); + current_process.exit(exit_code); +} + +pub fn do_wait4(child_pid: u32) -> Result { + let child_process = process_table::get(child_pid) + .ok_or_else(|| (Errno::ECHILD, "Cannot find child process with the given PID"))?; + + let mut exit_code = 0; + loop { + let guard = child_process.lock().unwrap(); + if guard.get_status() == Status::ZOMBIE { + exit_code = guard.get_exit_code(); + break; + } + drop(guard); + } + + let child_pid = child_process.lock().unwrap().get_pid(); + process_table::remove(child_pid); + + Ok(exit_code) +} + +mod task; +mod process; +mod process_table; +mod spawn; + +use prelude::*; diff --git a/src/libos/src/process/process.rs b/src/libos/src/process/process.rs new file mode 100644 index 00000000..50c842bd --- /dev/null +++ b/src/libos/src/process/process.rs @@ -0,0 +1,79 @@ +use super::*; +use super::task::{Task}; +use vm::{ProcessVM, VMRangeTrait}; +use file_table::{FileTable}; +use file::{File, FileRef,}; + +#[allow(non_camel_case_types)] +pub type pid_t = u32; + +#[derive(Debug)] +pub struct Process { + task: Task, + status: Status, + pid: pid_t, + tgid: pid_t, + exit_code: i32, + exec_path: String, + vm: ProcessVM, + file_table: FileTable, +} + +pub type ProcessRef = Arc>; + +impl Process { + pub fn new(exec_path: &str, task: Task, vm: ProcessVM, file_table: FileTable) + -> Result<(pid_t, ProcessRef), Error> + { + let new_pid = process_table::alloc_pid(); + let new_process_ref = Arc::new(SgxMutex::new(Process { + task: task, + status: Default::default(), + pid: new_pid, + tgid: new_pid, + exec_path: exec_path.to_owned(), + exit_code: 0, + vm: vm, + file_table: file_table, + })); + Ok((new_pid, new_process_ref)) + } + + pub fn get_task(&self) -> &Task { &self.task } + pub fn get_task_mut(&mut self) -> &mut Task { &mut self.task } + pub fn get_pid(&self) -> pid_t { self.pid } + pub fn get_tgid(&self) -> pid_t { self.tgid } + pub fn get_status(&self) -> Status { self.status } + pub fn get_exit_code(&self) -> i32 { self.exit_code } + pub fn get_exec_path(&self) -> &str { &self.exec_path } + pub fn get_vm(&self) -> &ProcessVM { &self.vm } + pub fn get_vm_mut(&mut self) -> &mut ProcessVM { &mut self.vm } + pub fn get_files(&self) -> &FileTable { &self.file_table } + pub fn get_files_mut(&mut self) -> &mut FileTable { &mut self.file_table } + + pub fn exit(&mut self, exit_code: i32) { + self.exit_code = exit_code; + self.status = Status::ZOMBIE; + } +} + +impl Drop for Process { + fn drop(&mut self) { + process_table::free_pid(self.pid); + } +} + + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Status { + RUNNING, + INTERRUPTIBLE, + ZOMBIE, + STOPPED, +} + +impl Default for Status { + fn default() -> Status { + Status::RUNNING + } +} diff --git a/src/libos/src/process/process_table.rs b/src/libos/src/process/process_table.rs new file mode 100644 index 00000000..51fc851d --- /dev/null +++ b/src/libos/src/process/process_table.rs @@ -0,0 +1,32 @@ +use super::*; +use std::sync::atomic::{AtomicU32, Ordering}; + +lazy_static! { + static ref PROCESS_TABLE: SgxMutex> = { + SgxMutex::new(HashMap::new()) + }; +} + +pub fn put(pid: pid_t, process: ProcessRef) { + PROCESS_TABLE.lock().unwrap().insert(pid, process); +} + +pub fn remove(pid: pid_t) { + PROCESS_TABLE.lock().unwrap().remove(&pid); +} + +pub fn get(pid: pid_t) -> Option { + PROCESS_TABLE.lock().unwrap().get(&pid).map(|pr| pr.clone()) +} + + +static NEXT_PID : AtomicU32 = AtomicU32::new(1); + +pub fn alloc_pid() -> u32 { + NEXT_PID.fetch_add(1, Ordering::SeqCst) +} + +pub fn free_pid(pid: u32) { + // TODO: +} + diff --git a/src/libos/src/elf_helper.rs b/src/libos/src/process/spawn/elf_helper.rs similarity index 99% rename from src/libos/src/elf_helper.rs rename to src/libos/src/process/spawn/elf_helper.rs index 1a2cb171..889a9939 100644 --- a/src/libos/src/elf_helper.rs +++ b/src/libos/src/process/spawn/elf_helper.rs @@ -1,4 +1,4 @@ -use prelude::*; +use super::*; use xmas_elf::{sections, ElfFile, program, P64}; use xmas_elf::symbol_table::{Entry64, DynEntry64}; diff --git a/src/libos/src/init_stack.rs b/src/libos/src/process/spawn/init_stack.rs similarity index 81% rename from src/libos/src/init_stack.rs rename to src/libos/src/process/spawn/init_stack.rs index dfa0065c..e32c175e 100644 --- a/src/libos/src/init_stack.rs +++ b/src/libos/src/process/spawn/init_stack.rs @@ -1,4 +1,5 @@ -use prelude::*; +use super::*; + use {std, std::mem, std::ptr}; use std::ffi::{CStr, CString}; use std::os::raw::c_char; @@ -46,17 +47,19 @@ use std::os::raw::c_char; * */ -pub fn do_init_process_stack<'a, 'b>(stack: &'a StackBuf, - argv: &'b [CString], envp: &'b [CString], auxtbl: &'b AuxTable) - -> Result<(), Error> +pub fn do_init(stack_top: usize, init_area_size: usize, + argv: &[CString], envp: &[CString], auxtbl: &AuxTable) + -> Result { - let envp_cloned = clone_cstrings_on_stack(stack, envp)?; - let argv_cloned = clone_cstrings_on_stack(stack, argv)?; - dump_auxtbl_on_stack(stack, auxtbl)?; - dump_cstrptrs_on_stack(stack, &envp_cloned); - dump_cstrptrs_on_stack(stack, &argv_cloned); - stack.put(argv.len() as u64); - Ok(()) + let stack_buf = unsafe { StackBuf::new(stack_top, init_area_size)? }; + let envp_cloned = clone_cstrings_on_stack(&stack_buf, envp)?; + let argv_cloned = clone_cstrings_on_stack(&stack_buf, argv)?; + dump_auxtbl_on_stack(&stack_buf, auxtbl)?; + dump_auxtbl_on_stack(&stack_buf, auxtbl)?; + dump_cstrptrs_on_stack(&stack_buf, &envp_cloned); + dump_cstrptrs_on_stack(&stack_buf, &argv_cloned); + stack_buf.put(argv.len() as u64); + Ok(stack_buf.get_pos()) } /// StackBuf is a buffer that is filled in from high addresses to low @@ -64,29 +67,19 @@ pub fn do_init_process_stack<'a, 'b>(stack: &'a StackBuf, /// [self.bottom, self.top). #[derive(Debug)] pub struct StackBuf { - stack_top: *const u8, - stack_bottom: *const u8, - stack_pos: Cell<*const u8>, -} - -impl Default for StackBuf { - fn default() -> StackBuf { - StackBuf { - stack_top: 0 as *const u8, - stack_bottom: 0 as *const u8, - stack_pos: Cell::new(0 as *const u8), - } - } + stack_top: usize, + stack_bottom: usize, + stack_pos: Cell, } impl StackBuf { - pub fn new(stack_top: *const u8, stack_bottom: *const u8) -> Result{ - if stack_top as usize <= stack_bottom as usize{ - return Err(Error::new(Errno::EINVAL, "Invalid stack range")); + pub unsafe fn new(stack_top: usize, stack_size: usize) -> Result{ + if stack_top % 16 != 0 || stack_size == 0 || stack_top < stack_size { + return errno!(EINVAL, "Invalid stack range"); }; Ok(StackBuf { stack_top: stack_top, - stack_bottom: stack_bottom, + stack_bottom: stack_top - stack_size, stack_pos: Cell::new(stack_top), }) } @@ -106,7 +99,11 @@ impl StackBuf { { let val_size = mem::size_of::(); let val_align = mem::align_of::(); - let total_size = val_size * vals.len(); + let total_size = { + let num_vals = vals.len(); + if num_vals == 0 { return Ok(self.get_pos() as *const T); } + val_size * num_vals + }; let base_ptr = self.alloc(total_size, val_align)? as *mut T; let mut val_ptr = base_ptr; @@ -123,31 +120,18 @@ impl StackBuf { self.put_slice(bytes) } - pub fn get_pos(&self) -> *const u8 { + pub fn get_pos(&self) -> usize { self.stack_pos.get() } - fn alloc(&self, size: usize, align_power2: usize) -> Result<*mut u8, Error> { - if size == 0 || !align_power2.is_power_of_two() { - return Err(Error::new(Errno::EINVAL, "Invalid size or align")); - } - - let old_pos = { - let old_pos = self.stack_pos.get() as usize; - let remain_size = old_pos - self.stack_bottom as usize; - if size > remain_size { - return Err(Error::new(Errno::ENOMEM, "No enough space in buffer")); - } - old_pos - }; - + fn alloc(&self, size: usize, align: usize) -> Result<*mut u8, Error> { let new_pos = { - let mask = (-(align_power2 as isize)) as usize; - let new_pos = (old_pos - size) & mask; - if new_pos < self.stack_bottom as usize { + let old_pos = self.stack_pos.get(); + let new_pos = align_down(old_pos - size, align); + if new_pos < self.stack_bottom { return Err(Error::new(Errno::ENOMEM, "No enough space in buffer")); } - new_pos as *const u8 + new_pos }; self.stack_pos.set(new_pos); diff --git a/src/libos/src/process/spawn/init_vm.rs b/src/libos/src/process/spawn/init_vm.rs new file mode 100644 index 00000000..10384d47 --- /dev/null +++ b/src/libos/src/process/spawn/init_vm.rs @@ -0,0 +1,106 @@ +use super::*; +use std::{ptr}; +use xmas_elf::{ElfFile, header, program, sections}; +use self::segment::{*}; + +pub const DEFAULT_STACK_SIZE : usize = 1 * 1024 * 1024; +pub const DEFAULT_HEAP_SIZE : usize = 2 * 1024 * 1024; +pub const DEFAULT_MMAP_SIZE : usize = 2 * 1024 * 1024; + +pub fn do_init(elf_file: &ElfFile, elf_buf: &[u8]) -> Result { + let mut code_seg = get_code_segment(elf_file)?; + let mut data_seg = get_data_segment(elf_file)?; + + // Alloc all virtual memory areas + let code_start = 0; + let code_end = align_down(data_seg.get_mem_addr(), data_seg.get_mem_align()); + let data_start = code_end; + let data_end = align_up(data_seg.get_mem_addr() + data_seg.get_mem_size(), 4096); + let code_size = code_end - code_start; + let data_size = data_end - data_start; + let stack_size = DEFAULT_STACK_SIZE; + let heap_size = DEFAULT_HEAP_SIZE; + let mmap_size = DEFAULT_MMAP_SIZE; + let mut process_vm = ProcessVM::new(code_size, data_size, heap_size, + stack_size, mmap_size)?; + + // Calculate the "real" addresses + let process_base_addr = process_vm.get_base_addr(); + let code_start = process_base_addr + code_start; + let code_end = process_base_addr + code_end; + let data_start = process_base_addr + data_start; + let data_end = process_base_addr + data_end; + code_seg.set_runtime_info(process_base_addr, code_start, code_end); + data_seg.set_runtime_info(process_base_addr, data_start, data_end); + + // Load code and data + code_seg.load_from_file(elf_buf); + data_seg.load_from_file(elf_buf); + + // Relocate symbols + reloc_symbols(process_base_addr, elf_file)?; + link_syscalls(process_base_addr, elf_file)?; + + // Make code executable + code_seg.mprotect(PERM_R | PERM_W | PERM_X); + + Ok(process_vm) +} + +fn reloc_symbols(process_base_addr: usize, elf_file: &ElfFile) + -> Result<(), Error> +{ + let rela_entries = elf_helper::get_rela_entries(elf_file, ".rela.dyn")?; + for rela_entry in rela_entries { + /* + println!("\toffset: {:#X}, symbol index: {}, type: {}, addend: {:#X}", + rela_entry.get_offset(), + rela_entry.get_symbol_table_index(), + rela_entry.get_type(), + rela_entry.get_addend()); + */ + + /* reloc type == R_X86_64_RELATIVE */ + match rela_entry.get_type() { + 8 if rela_entry.get_symbol_table_index() == 0 => { + let rela_addr = process_base_addr + rela_entry.get_offset() as usize; + let rela_val = process_base_addr + rela_entry.get_addend() as usize; + unsafe { + ptr::write_unaligned(rela_addr as *mut usize, rela_val); + } + } + // TODO: need to handle other relocation types + _ => { } + } + } + Ok(()) +} + +fn link_syscalls(process_base_addr: usize, elf_file: &ElfFile) + -> Result<(), Error> +{ + let syscall_addr = __occlum_syscall as *const () as usize; + + let rela_entries = elf_helper::get_rela_entries(elf_file, ".rela.plt")?; + let dynsym_entries = elf_helper::get_dynsym_entries(elf_file)?; + for rela_entry in rela_entries { + let dynsym_idx = rela_entry.get_symbol_table_index() as usize; + let dynsym_entry = &dynsym_entries[dynsym_idx]; + let dynsym_str = dynsym_entry.get_name(elf_file) + .map_err(|e| Error::new(Errno::ENOEXEC, + "Failed to get the name of dynamic symbol"))?; + + if dynsym_str == "__occlum_syscall" { + let rela_addr = process_base_addr + rela_entry.get_offset() as usize; + unsafe { + ptr::write_unaligned(rela_addr as *mut usize, syscall_addr); + } + } + } + + Ok(()) +} + +extern { + fn __occlum_syscall(num: i32, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64) -> i64; +} diff --git a/src/libos/src/process/spawn/mod.rs b/src/libos/src/process/spawn/mod.rs new file mode 100644 index 00000000..1575a5a1 --- /dev/null +++ b/src/libos/src/process/spawn/mod.rs @@ -0,0 +1,106 @@ +use super::*; +use file::{File, StdinFile, StdoutFile/*, StderrFile*/}; +use file_table::{FileTable}; +use std::path::Path; +use std::ffi::{CStr, CString}; +use std::sgxfs::SgxFile; +use xmas_elf::{ElfFile, header, program, sections}; +use xmas_elf::symbol_table::Entry; +use self::init_stack::{AuxKey, AuxTable}; +use super::task::{Task}; +use vm::{ProcessVM, VMRangeTrait}; + +mod init_stack; +mod init_vm; +mod elf_helper; +mod segment; + +pub fn do_spawn>(elf_path: &P, argv: &[CString], envp: &[CString]) + -> Result +{ + let mut elf_buf = { + let key : sgx_key_128bit_t = [0 as uint8_t; 16]; + let mut sgx_file = SgxFile::open_ex(elf_path, &key) + .map_err(|e| (Errno::ENOENT, "Failed to open the SGX-protected file"))?; + + let mut elf_buf = Vec::::new(); + sgx_file.read_to_end(&mut elf_buf); + elf_buf + }; + + let elf_file = { + let elf_file = ElfFile::new(&elf_buf) + .map_err(|e| (Errno::ENOEXEC, "Failed to parse the ELF file"))?; + header::sanity_check(&elf_file) + .map_err(|e| (Errno::ENOEXEC, "Failed to parse the ELF file"))?; + /* + elf_helper::print_program_headers(&elf_file)?; + elf_helper::print_sections(&elf_file)?; + elf_helper::print_pltrel_section(&elf_file)?; + */ + elf_file + }; + + let (new_pid, new_process_ref) = { + let vm = init_vm::do_init(&elf_file, &elf_buf[..])?; + let task = { + let program_entry = { + let program_entry = vm.get_base_addr() + + elf_helper::get_start_address(&elf_file)?; + if !vm.get_code_vma().contains_obj(program_entry, 16) { + return Err(Error::new(Errno::EINVAL, "Invalid program entry")); + } + program_entry + }; + let stack_top = vm.get_stack_top(); + init_task(program_entry, stack_top, argv, envp)? + }; + let files = init_files()?; + let exec_path = elf_path.as_ref().to_str().unwrap(); + Process::new(exec_path, task, vm, files)? + }; + process_table::put(new_pid, new_process_ref.clone()); + task::enqueue_task(new_process_ref); + Ok(new_pid) +} + +fn init_files() -> Result { + let mut file_table = FileTable::new(); + + let stdin : Arc> = Arc::new(Box::new(StdinFile::new())); + let stdout : Arc> = Arc::new(Box::new(StdoutFile::new())); + // TODO: implement and use a real stderr + let stderr = stdout.clone(); + file_table.put(stdin); + file_table.put(stdout); + file_table.put(stderr); + + Ok(file_table) +} + +fn init_task(user_entry: usize, stack_top: usize, + argv: &[CString], envp: &[CString]) + -> Result +{ + let user_stack = init_stack(stack_top, argv, envp)?; + Ok(Task { + user_stack_addr: user_stack, + user_entry_addr: user_entry, + fs_base_addr: 0, + .. Default::default() + }) +} + +fn init_stack(stack_top: usize, argv: &[CString], envp: &[CString]) + -> Result +{ + let mut auxtbl = AuxTable::new(); + auxtbl.set_val(AuxKey::AT_PAGESZ, 4096)?; + auxtbl.set_val(AuxKey::AT_UID, 0)?; + auxtbl.set_val(AuxKey::AT_GID, 0)?; + auxtbl.set_val(AuxKey::AT_EUID, 0)?; + auxtbl.set_val(AuxKey::AT_EGID, 0)?; + auxtbl.set_val(AuxKey::AT_SECURE, 0)?; + + init_stack::do_init(stack_top, 4096, argv, envp, &auxtbl) +} diff --git a/src/libos/src/process/spawn/segment.rs b/src/libos/src/process/spawn/segment.rs new file mode 100644 index 00000000..8ceabe15 --- /dev/null +++ b/src/libos/src/process/spawn/segment.rs @@ -0,0 +1,102 @@ +use super::*; +use std::{slice}; +use xmas_elf::program::{ProgramHeader}; + + +#[derive(Debug, Default)] +pub struct Segment { + // Static info from ELF + mem_addr: usize, + mem_align: usize, + mem_size: usize, + file_offset: usize, + file_size: usize, + // Runtime info after loaded + process_base_addr: usize, + start_addr: usize, + end_addr: usize, +} + +pub const PERM_R: u32 = 0x1; +pub const PERM_W: u32 = 0x2; +pub const PERM_X: u32 = 0x4; + +impl Segment { + pub fn get_mem_addr(&self) -> usize { self.mem_addr } + pub fn get_mem_align(&self) -> usize { self.mem_align } + pub fn get_mem_size(&self) -> usize { self.mem_size } + + pub fn from_program_header(ph: &ProgramHeader) -> Result { + let ph64 = match ph { + ProgramHeader::Ph32(ph) => { + return Err((Errno::ENOEXEC, "Not support 32-bit ELF").into()) + } + ProgramHeader::Ph64(ph64) => { + ph64 + } + }; + if ph64.align > 1 && ((ph64.offset % ph64.align) != + (ph64.virtual_addr % ph64.align)) { + return Err((Errno::EINVAL, + "Memory address and file offset is not equal, per modulo").into()); + } + if ph64.mem_size < ph64.file_size { + return Err((Errno::EINVAL, + "Memory size must be greater than file size").into()); + } + if !ph64.align.is_power_of_two() { + return Err((Errno::EINVAL, + "Memory alignment must be a power of two").into()); + } + + Ok(Segment { + mem_addr: ph64.virtual_addr as usize, + mem_align: ph64.align as usize, + mem_size: ph64.mem_size as usize, + file_offset: ph64.offset as usize, + file_size: ph64.file_size as usize, + ..Default::default() + }) + } + + pub fn load_from_file(&self, elf_buf: &[u8]) { + let mut target_buf = unsafe { + slice::from_raw_parts_mut((self.process_base_addr + self.mem_addr) + as *mut u8, + self.file_size) + }; + target_buf.copy_from_slice(&elf_buf[self.file_offset.. + (self.file_offset + self.file_size)]); + } + + pub fn set_runtime_info(&mut self, process_base_addr: usize, + start_addr: usize, end_addr: usize) { + self.process_base_addr = process_base_addr; + self.start_addr = start_addr; + self.end_addr = end_addr; + } + + pub fn mprotect(&mut self, perm: u32) { + unsafe { + trts_mprotect(self.start_addr, self.end_addr - self.start_addr, + perm as u64); + } + } +} + +pub fn get_code_segment(elf_file: &ElfFile) -> Result { + let code_ph = elf_helper::get_code_program_header(elf_file) + .map_err(|e| (Errno::ENOEXEC, "Failed to get the program header of code"))?; + Segment::from_program_header(&code_ph) +} + +pub fn get_data_segment(elf_file: &ElfFile) -> Result { + let data_ph = elf_helper::get_data_program_header(elf_file) + .map_err(|e| (Errno::ENOEXEC, "Failed to get the program header of code"))?; + Segment::from_program_header(&data_ph) +} + +#[link(name = "sgx_trts")] +extern { + pub fn trts_mprotect(start: size_t, size: size_t, perms: uint64_t) -> sgx_status_t; +} diff --git a/src/libos/src/task.c b/src/libos/src/process/task.c similarity index 100% rename from src/libos/src/task.c rename to src/libos/src/process/task.c diff --git a/src/libos/src/process/task.rs b/src/libos/src/process/task.rs new file mode 100644 index 00000000..cd41cda5 --- /dev/null +++ b/src/libos/src/process/task.rs @@ -0,0 +1,100 @@ +use super::*; +use std::{mem}; + +/// Note: this definition must be in sync with task.h +#[derive(Clone, Debug, Default)] +#[repr(C)] +pub struct Task { + pub syscall_stack_addr: usize, + pub user_stack_addr: usize, + pub user_entry_addr: usize, + pub fs_base_addr: usize, + pub saved_state: usize, // struct jmpbuf* +} + + +lazy_static! { + static ref new_process_queue: SgxMutex> = { + SgxMutex::new(VecDeque::new()) + }; +} + +pub fn enqueue_task(new_process: ProcessRef) { + new_process_queue.lock().unwrap().push_back(new_process); + + let mut ret = 0; + let ocall_status = unsafe { ocall_run_new_task(&mut ret) }; + if ocall_status != sgx_status_t::SGX_SUCCESS || ret != 0 { + panic!("Failed to start the process"); + } +} + +fn dequeue_task() -> Option { + new_process_queue.lock().unwrap().pop_front() +} + + +pub fn run_task() -> Result<(), Error> { + let new_process : ProcessRef = dequeue_task() + .ok_or_else(|| (Errno::EAGAIN, "No new processes to run"))?; + set_current(&new_process); + + let (pid, task) = { + let mut process = new_process.lock().unwrap(); + let pid = process.get_pid(); + let task = process.get_task_mut() as *mut Task; + (pid, task) + }; + + unsafe { + // task may only be modified by this function; so no lock is needed + do_run_task(task); + } + + // Init process does not have any parent, so it has to release itself + if pid == 1 { + process_table::remove(1); + } + + reset_current(); + Ok(()) +} + + +thread_local! { + static _CURRENT_PROCESS_PTR: Cell<*const SgxMutex> = + Cell::new(0 as *const SgxMutex); +} + +pub fn get_current() -> &'static SgxMutex { + let mut process_ptr = 0 as *const SgxMutex; + _CURRENT_PROCESS_PTR.with(|cp| { + process_ptr = cp.get(); + }); + unsafe { mem::transmute(process_ptr) } +} + +fn set_current(process: &ProcessRef) { + let process_ref_clone = process.clone(); + let process_ptr = Arc::into_raw(process_ref_clone); + + _CURRENT_PROCESS_PTR.with(|cp| { + cp.set(process_ptr); + }); +} + +fn reset_current() { + let mut process_ptr = 0 as *const SgxMutex; + _CURRENT_PROCESS_PTR.with(|cp| { + process_ptr = cp.get(); + cp.set(0 as *const SgxMutex); + }); + + // Prevent memory leakage + unsafe { drop(Arc::from_raw(process_ptr)); } +} + +extern { + fn ocall_run_new_task(ret: *mut i32) -> sgx_status_t; + fn do_run_task(task: *mut Task) -> i32; +} diff --git a/src/libos/src/task_x86-64.S b/src/libos/src/process/task_x86-64.S similarity index 100% rename from src/libos/src/task_x86-64.S rename to src/libos/src/process/task_x86-64.S diff --git a/src/libos/src/vm/mod.rs b/src/libos/src/vm/mod.rs new file mode 100644 index 00000000..d1564474 --- /dev/null +++ b/src/libos/src/vm/mod.rs @@ -0,0 +1,178 @@ +use prelude::*; +use std::{fmt}; + +// TODO: Rename VMSpace to VMUniverse + +#[macro_use] +mod vm_range; +mod vm_space; +mod vm_domain; +mod vm_area; +mod process_vm; + +pub use self::vm_range::{VMRange, VMRangeTrait}; +pub use self::process_vm::{ProcessVM}; + +pub const PAGE_SIZE : usize = 4096; + + +#[derive(Debug)] +pub struct VMSpace { + range: VMRange, + guard_type: VMGuardAreaType, +} + +#[derive(Debug)] +pub struct VMDomain { + range: VMRange, +} + +#[derive(Debug)] +pub struct VMArea { + range: VMRange, + flags: VMAreaFlags, +} + + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum VMGuardAreaType { + None, + Static { size: usize, align: usize }, + Dynamic { size: usize }, +} + + +#[derive(Copy, Clone, Debug, Default, PartialEq)] +pub struct VMAreaFlags(pub u32); + +pub const VM_AREA_FLAG_R: u32 = 0x1; +pub const VM_AREA_FLAG_W: u32 = 0x2; +pub const VM_AREA_FLAG_X: u32 = 0x4; + +impl VMAreaFlags { + pub fn can_execute(&self) -> bool { + self.0 & VM_AREA_FLAG_X == VM_AREA_FLAG_X + } + + pub fn can_write(&self) -> bool { + self.0 & VM_AREA_FLAG_W == VM_AREA_FLAG_W + } + + pub fn can_read(&self) -> bool { + self.0 & VM_AREA_FLAG_R == VM_AREA_FLAG_R + } +} + + + +#[derive(Clone, Copy, PartialEq)] +pub struct VMAllocOptions { + size: usize, + addr: VMAddrOption, + growth: Option, +} + +impl VMAllocOptions { + pub fn new(size: usize) -> Result { + if size % PAGE_SIZE != 0 { + return Err(Error::new(Errno::EINVAL, "Size is not page-aligned")); + } + Ok(VMAllocOptions { size, ..Default::default() }) + } + + pub fn addr(&mut self, addr: VMAddrOption) -> Result<&mut Self, Error> { + if addr.is_addr_given() && addr.get_addr() % PAGE_SIZE != 0 { + return Err(Error::new(Errno::EINVAL, "Invalid address")); + } + self.addr = addr; + Ok(self) + } + + pub fn growth(&mut self, growth: VMGrowthType) -> Result<&mut Self, Error> { + self.growth = Some(growth); + Ok(self) + } +} + +impl fmt::Debug for VMAllocOptions { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VMAllocOptions {{ size: 0x{:X?}, addr: {:?}, growth: {:?} }}", + self.size, self.addr, self.growth) + } +} + +impl Default for VMAllocOptions { + fn default() -> VMAllocOptions{ + VMAllocOptions { + size: 0, + addr: VMAddrOption::Any, + growth: None, + } + } +} + + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum VMAddrOption { + Any, // Free to choose any address + Hint(usize), // Near the given address + Fixed(usize), // Must be the given address + Beyond(usize), // Must be greater or equal to the given address +} + +impl VMAddrOption { + pub fn is_addr_given(&self) -> bool { + match self { + VMAddrOption::Any => false, + _ => true, + } + } + + pub fn get_addr(&self) -> usize { + match self { + VMAddrOption::Hint(addr) | + VMAddrOption::Fixed(addr) | + VMAddrOption::Beyond(addr) => *addr, + VMAddrOption::Any => panic!("No address given"), + } + } +} + + +/// How VMRange may grow: +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum VMGrowthType { + Upward, // e.g., mmaped regions grow upward + Downward, // e.g., stacks grows downward + Fixed, +} + + +#[derive(Clone, Debug)] +pub struct VMResizeOptions { + new_size: usize, + new_addr: Option, +} + +impl VMResizeOptions { + pub fn new(new_size: usize) -> Result { + if new_size % PAGE_SIZE != 0 { + return Err(Error::new(Errno::EINVAL, "Size is not page-aligned")); + } + Ok(VMResizeOptions { new_size, ..Default::default() }) + } + + pub fn addr(&mut self, new_addr: VMAddrOption) -> &mut Self { + self.new_addr = Some(new_addr); + self + } +} + +impl Default for VMResizeOptions { + fn default() -> VMResizeOptions{ + VMResizeOptions { + new_size: 0, + new_addr: None, + } + } +} diff --git a/src/libos/src/vm/process_vm.rs b/src/libos/src/vm/process_vm.rs new file mode 100644 index 00000000..a5526f4f --- /dev/null +++ b/src/libos/src/vm/process_vm.rs @@ -0,0 +1,245 @@ +use super::*; + +const DATA_SPACE_SIZE : usize = 12 * 1024 * 1024; // 16MB + +lazy_static! { + static ref DATA_SPACE: SgxMutex = { + let size = DATA_SPACE_SIZE; + let addr = { + let ptr = unsafe { aligned_malloc(size, PAGE_SIZE) }; + if ptr == (0 as *mut c_void) { + panic!("Out of memory"); + }; + ptr as usize + }; + let vm_space = unsafe { + match VMSpace::new(addr, size, VMGuardAreaType::None) { + Ok(vm_space) => vm_space, + Err(_) => panic!("Failed to create a VMSpace"), + } + }; + SgxMutex::new(vm_space) + }; +} + +unsafe fn aligned_malloc(mem_size: usize, mem_align: usize) -> *mut c_void { + let mut mem_ptr = ::core::ptr::null_mut(); + let ret = libc::posix_memalign(&mut mem_ptr, mem_align, mem_size); + if ret == 0 { + mem_ptr + } else { + 0 as *mut c_void + } +} + +#[derive(Debug)] +pub struct ProcessVM { + //code_domain: VMDomain, + data_domain: VMDomain, + code_vma: VMArea, + data_vma: VMArea, + heap_vma: VMArea, + stack_vma: VMArea, + mmap_vmas: Vec>, +} + +impl ProcessVM { + pub fn new(code_size: usize, data_size: usize, + heap_size: usize, stack_size: usize, + mmap_size: usize) + -> Result + { + let data_domain_size = code_size + data_size + heap_size + stack_size + mmap_size; + let mut data_domain = DATA_SPACE.lock().unwrap(). + alloc_domain(data_domain_size)?; + + let (code_vma, data_vma, heap_vma, stack_vma) = + ProcessVM::alloc_vmas(&mut data_domain, code_size, data_size, + heap_size, stack_size)?; + // No mmapped vmas initially + let mmap_vmas = Vec::new(); + + Ok(ProcessVM { + data_domain, + code_vma, + data_vma, + heap_vma, + stack_vma, + mmap_vmas, + }) + } + + fn alloc_vmas(data_domain: &mut VMDomain, + code_size: usize, data_size: usize, + heap_size: usize, stack_size: usize) + -> Result<(VMArea, VMArea, VMArea, VMArea), Error> + { + let mut addr = data_domain.get_start(); + + let mut alloc_vma_continuously = |addr: &mut usize, size, flags, growth| -> Result<_, Error> { + let mut options = VMAllocOptions::new(size)?; + options.addr(VMAddrOption::Fixed(*addr))?.growth(growth)?; + let new_vma = data_domain.alloc_area(&options, flags)?; + *addr += size; + Ok(new_vma) + }; + + let rx_flags = VMAreaFlags(VM_AREA_FLAG_R | VM_AREA_FLAG_X); + let rw_flags = VMAreaFlags(VM_AREA_FLAG_R | VM_AREA_FLAG_W); + + let code_vma = alloc_vma_continuously(&mut addr, + code_size, rx_flags, + VMGrowthType::Fixed)?; + let data_vma = alloc_vma_continuously(&mut addr, + data_size, rw_flags, + VMGrowthType::Fixed)?; + let heap_vma = alloc_vma_continuously(&mut addr, + 0, rw_flags, + VMGrowthType::Upward)?; + // Preserve the space for heap + addr += heap_size; + // After the heap is the stack + let stack_vma = alloc_vma_continuously(&mut addr, + stack_size, rw_flags, + VMGrowthType::Downward)?; + Ok((code_vma, data_vma, heap_vma, stack_vma)) + } + + pub fn get_base_addr(&self) -> usize { + self.code_vma.get_start() + } + + pub fn get_code_vma(&self) -> &VMArea { + &self.code_vma + } + + pub fn get_data_vma(&self) -> &VMArea { + &self.data_vma + } + + pub fn get_heap_vma(&self) -> &VMArea { + &self.heap_vma + } + + pub fn get_stack_vma(&self) -> &VMArea { + &self.stack_vma + } + + pub fn get_stack_top(&self) -> usize { + self.stack_vma.get_end() + } + + pub fn get_mmap_vmas(&self) -> &[Box] { + &self.mmap_vmas[..] + } + + pub fn get_brk_start(&self) -> usize { + self.get_heap_vma().get_start() + } + + pub fn get_brk(&self) -> usize { + self.get_heap_vma().get_end() + } + + pub fn get_mmap_start(&self) -> usize { + self.get_stack_vma().get_end() + } + + pub fn mmap(&mut self, addr: usize, size: usize, flags: VMAreaFlags) + -> Result + { + let alloc_options = { + let mmap_start_addr = self.get_mmap_start(); + + let mut alloc_options = VMAllocOptions::new(size)?; + alloc_options.addr( + if addr == 0 { + VMAddrOption::Beyond(mmap_start_addr) + } + else { + if addr < mmap_start_addr { + return Err(Error::new(Errno::EINVAL, + "Beyond valid memory range")); + } + VMAddrOption::Fixed(addr) + } + )?.growth(VMGrowthType::Upward)?; + alloc_options + }; + // TODO: when failed, try to resize data_domain + let new_mmap_vma = self.data_domain.alloc_area(&alloc_options, flags)?; + let addr = new_mmap_vma.get_start(); + self.mmap_vmas.push(Box::new(new_mmap_vma)); + Ok(addr) + } + + pub fn munmap(&mut self, addr: usize, size: usize) + -> Result<(), Error> + { + // TODO: handle the case when the given range [addr, addr + size) + // does not match exactly with any vma. For example, when this range + // cover multiple ranges or cover some range partially. + + let mmap_vma_i = { + let mmap_vma_i = self.get_mmap_vmas().iter().position(|vma| { + vma.get_start() == addr && vma.get_end() == addr + size + }); + if mmap_vma_i.is_none() { return Ok(()) } + mmap_vma_i.unwrap() + }; + + let mut removed_mmap_vma = self.mmap_vmas.swap_remove(mmap_vma_i); + self.data_domain.dealloc_area(&mut removed_mmap_vma); + Ok(()) + } + + pub fn mremap(&mut self, old_addr: usize, old_size: usize, + options: &VMResizeOptions) + -> Result + { + // TODO: Implement this! + Err(Error::new(Errno::EINVAL, "Not implemented")) + } + + pub fn brk(&mut self, new_brk: usize) -> Result { + if new_brk == 0 { + return Ok(self.get_brk()); + } + + let resize_options = { + let brk_start = self.get_brk_start(); + + let new_heap_size = { + if new_brk < brk_start { + return Err(Error::new(Errno::EINVAL, "Invalid brk")); + } + new_brk - brk_start + }; + + let mut options = VMResizeOptions::new(new_heap_size)?; + options.addr(VMAddrOption::Fixed(brk_start)); + options + }; + self.data_domain.resize_area(&mut self.heap_vma, &resize_options)?; + Ok(new_brk) + } +} + + +impl Drop for ProcessVM { + fn drop(&mut self) { + let data_domain = &mut self.data_domain; + + // Remove all vma from the domain + data_domain.dealloc_area(&mut self.code_vma); + data_domain.dealloc_area(&mut self.data_vma); + data_domain.dealloc_area(&mut self.heap_vma); + data_domain.dealloc_area(&mut self.stack_vma); + for mmap_vma in &mut self.mmap_vmas { + data_domain.dealloc_area(mmap_vma); + } + + // Remove the domain from its parent space + DATA_SPACE.lock().unwrap().dealloc_domain(data_domain); + } +} diff --git a/src/libos/src/vm/vm_area.rs b/src/libos/src/vm/vm_area.rs new file mode 100644 index 00000000..60cab4cc --- /dev/null +++ b/src/libos/src/vm/vm_area.rs @@ -0,0 +1,11 @@ +use super::*; + +impl super::VMArea { + pub fn get_flags(&self) -> &VMAreaFlags { + &self.flags + } + + pub fn get_flags_mut(&mut self) -> &mut VMAreaFlags { + &mut self.flags + } +} diff --git a/src/libos/src/vm/vm_domain.rs b/src/libos/src/vm/vm_domain.rs new file mode 100644 index 00000000..53807bee --- /dev/null +++ b/src/libos/src/vm/vm_domain.rs @@ -0,0 +1,31 @@ +use super::*; + +impl VMDomain { + pub fn alloc_area(&mut self, options: &VMAllocOptions, flags: VMAreaFlags) -> Result { + let new_range = self.range.alloc_subrange(options)?; + + // Init the memory area with all zeros + unsafe { + let mem_ptr = new_range.get_start() as *mut c_void; + let mem_size = new_range.get_size() as size_t; + memset(mem_ptr, 0 as c_int, mem_size); + } + + Ok(VMArea { range: new_range, flags: flags }) + } + + pub fn dealloc_area(&mut self, area: &mut VMArea) { + self.range.dealloc_subrange(&mut area.range) + } + + pub fn resize_area(&mut self, area: &mut VMArea, options: &VMResizeOptions) + -> Result<(), Error> + { + self.range.resize_subrange(&mut area.range, options) + } +} + +#[link(name = "sgx_tstdc")] +extern { + pub fn memset(p: *mut c_void, c: c_int, n: size_t) -> *mut c_void; +} diff --git a/src/libos/src/vm/vm_range.rs b/src/libos/src/vm/vm_range.rs new file mode 100644 index 00000000..b2cbdd42 --- /dev/null +++ b/src/libos/src/vm/vm_range.rs @@ -0,0 +1,512 @@ +use super::*; +use std::{fmt}; + +pub trait VMRangeTrait { + fn get_start(&self) -> usize; + fn get_end(&self) -> usize; + fn get_size(&self) -> usize; + fn get_growth(&self) -> VMGrowthType; + fn contains_obj(&self, ptr: usize, size: usize) -> bool; +} + +macro_rules! impl_vmrange_trait_for { + ($struct_name: ident, $field: ident) => { + impl VMRangeTrait for $struct_name { + fn get_start(&self) -> usize { + self.$field.get_start() + } + + fn get_end(&self) -> usize { + self.$field.get_end() + } + + fn get_size(&self) -> usize { + self.$field.get_end() - self.$field.get_start() + } + + fn get_growth(&self) -> VMGrowthType { + self.$field.get_growth() + } + + fn contains_obj(&self, ptr: usize, size: usize) -> bool { + self.$field.contains_obj(ptr, size) + } + } + } +} + +impl_vmrange_trait_for!(VMRange, inner); +impl_vmrange_trait_for!(VMSpace, range); +impl_vmrange_trait_for!(VMDomain, range); +impl_vmrange_trait_for!(VMArea, range); + + +#[derive(Debug)] +pub struct VMRange { + inner: VMRangeInner, + parent_range: *const VMRange, + sub_ranges: Option>, +} + +impl VMRange { + pub unsafe fn new(start: usize, end: usize, growth: VMGrowthType) -> Result { + if start % PAGE_SIZE != 0 || end % PAGE_SIZE != 0 { + return errno!(EINVAL, "Invalid start and/or end"); + } + Ok(VMRange { + inner: VMRangeInner::new(start, end, growth), + parent_range: 0 as *const VMRange, + sub_ranges: None, + }) + } + + pub fn alloc_subrange(&mut self, options: &VMAllocOptions) -> Result { + // Get valid parameters from options + let size = options.size; + let addr = options.addr; + let growth = options.growth.unwrap_or(VMGrowthType::Fixed); + + // Lazy initialize the subrange array upon the first allocation + if !self.has_subranges() { + self.init_subranges()?; + } + + // Find a free space for allocating a VMRange + let free_space = { + // Look for the minimal big-enough free space + let mut min_big_enough_free_space : Option = None; + let sub_ranges = self.get_subranges(); + for (idx, range_pair) in sub_ranges.windows(2).enumerate() { + let pre_range = &range_pair[0]; + let next_range = &range_pair[1]; + + let mut free_range = { + let free_range_start = pre_range.get_end(); + let free_range_end = next_range.get_start(); + + let free_range_size = free_range_end - free_range_start; + if free_range_size < size { continue } + + free_range_start..free_range_end + }; + + match addr { + VMAddrOption::Hint(addr) | VMAddrOption::Fixed(addr) => { + if !free_range.contains(&addr) { continue } + free_range.start = addr; + } + VMAddrOption::Beyond(addr) => { + if free_range.start < addr { continue } + } + _ => {} + } + + let free_space = Some(FreeSpace { + index_in_subranges: idx + 1, + start: free_range.start, + end: free_range.end, + may_neighbor_grow: (pre_range.growth == VMGrowthType::Upward, + next_range.growth == VMGrowthType::Downward), + }); + + if min_big_enough_free_space == None || + free_space < min_big_enough_free_space + { + min_big_enough_free_space = free_space; + + match addr { + VMAddrOption::Hint(addr) | VMAddrOption::Fixed(addr) => { + break + } + _ => {}, + } + } + } + + if min_big_enough_free_space.is_none() { + return errno!(ENOMEM, "No enough space"); + } + min_big_enough_free_space.unwrap() + }; + + // Given the free space, determine the start and end of the sub-range + let (new_subrange_start, new_subrange_end) = match addr { + VMAddrOption::Any | VMAddrOption::Beyond(_) => { + let should_no_gap_to_pre_domain = + free_space.may_neighbor_grow.0 == false && + growth != VMGrowthType::Downward; + let should_no_gap_to_next_domain = + free_space.may_neighbor_grow.1 == false && + growth != VMGrowthType::Upward; + let domain_start = if should_no_gap_to_pre_domain { + free_space.start + } + else if should_no_gap_to_next_domain { + free_space.end - size + } + else { + // We want to leave some space at both ends in case + // this sub-range or neighbor sub-range needs to grow later. + // As a simple heuristic, we put this sub-range near the + // center between the previous and next sub-ranges. + free_space.start + (free_space.get_size() - size) / 2 + }; + (domain_start, domain_start + size) + } + VMAddrOption::Fixed(addr) => { + (addr, addr + size) + } + VMAddrOption::Hint(addr) => { + return errno!(EINVAL, "Not implemented"); + } + }; + + let new_subrange_inner = VMRangeInner::new(new_subrange_start, + new_subrange_end, growth); + self.get_subranges_mut().insert(free_space.index_in_subranges, + new_subrange_inner); + // Although there are two copies of the newly created VMRangeInner obj, + // we can keep them in sync as all mutation on VMRange object must + // be carried out through dealloc_subrange() and resize_subrange() that + // takes both a (parent) range and its (child) sub-range as parameters. + // We update both copies of VMRangeInner, one in parent and the + // other in child, in dealloc_subrange and resize_subrange functions. + Ok(VMRange { + inner: new_subrange_inner, + parent_range: self as *const VMRange, + sub_ranges: None, + }) + } + + pub fn dealloc_subrange(&mut self, subrange: &mut VMRange) { + self.ensure_subrange_is_a_child(subrange); + if subrange.has_subranges() { + panic!("A range can only be dealloc'ed when it has no sub-ranges"); + } + + // Remove the sub-range + let domain_i = self.position_subrange(subrange); + self.get_subranges_mut().remove(domain_i); + + // When all sub-ranges are removed, remove the sub-range array + if self.get_subranges().len() == 2 { // two sentinel sub-ranges excluded + self.sub_ranges = None; + } + + // Mark a range as dealloc'ed + subrange.mark_as_dealloced(); + } + + pub fn resize_subrange(&mut self, subrange: &mut VMRange, options: &VMResizeOptions) + -> Result<(), Error> { + self.ensure_subrange_is_a_child(subrange); + + // Get valid parameters from options + let new_size = options.new_size; + let new_addr = options.new_addr; + + // Handle no-resizing cases + if subrange.get_size() == new_size { + return Ok(()); + } + if subrange.get_growth() == VMGrowthType::Fixed { + return errno!(EINVAL, "Cannot resize a fixed range"); + } + + // Shrink + if new_size < subrange.get_size() { + self.shrink_subrange_to(subrange, new_size) + } + // Grow + else { + self.grow_subrange_to(subrange, new_size) + } + } + + fn init_subranges(&mut self) -> Result<(), Error> { + // Use dummy VMRange as sentinel object at both ends to make the allocation + // and deallocation algorithm simpler + let start = self.get_start(); + let end = self.get_end(); + let start_sentry = VMRangeInner::new(start, start, VMGrowthType::Fixed); + let end_sentry = VMRangeInner::new(end, end, VMGrowthType::Fixed); + self.sub_ranges = Some(vec![start_sentry, end_sentry]); + Ok(()) + } + + fn ensure_subrange_is_a_child(&self, subrange: &VMRange) { + // FIXME: + /*if subrange.parent_range != self as *const VMRange { + panic!("This range does not contain the given sub-range"); + }*/ + } + + fn position_subrange(&self, subrange: &VMRange) -> usize { + let sub_ranges = self.get_subranges(); + sub_ranges.iter().position(|d| d == &subrange.inner).unwrap() + } + + fn get_subranges(&self) -> &Vec { + self.sub_ranges.as_ref().unwrap() + } + + fn get_subranges_mut(&mut self) -> &mut Vec { + self.sub_ranges.as_mut().unwrap() + } + + fn has_subranges(&self) -> bool { + self.sub_ranges.is_some() + } + + fn shrink_subrange_to(&mut self, subrange: &mut VMRange, new_size: usize) + -> Result<(), Error> + { + let subrange_i = self.position_subrange(subrange); + let subranges = self.get_subranges_mut(); + + if subrange.inner.growth == VMGrowthType::Upward { + // Can we do shrink? + let min_new_size = match subrange.sub_ranges.as_mut() { + Some(child_subranges) => { + let child_last_subrange = &child_subranges[ + child_subranges.len() - 2]; + child_last_subrange.end - subrange.inner.start + } + None => { + 0 + } + }; + if new_size < min_new_size { + return errno!(ENOMEM, "Cannot shrink to new size"); + } + // Do shrink + let new_subrange_end = subrange.inner.start + new_size; + subrange.inner.end = new_subrange_end; + // Sync state + subranges[subrange_i].end = new_subrange_end; + } + else { // self.growth == VMGrowthType::Downward + // Can we do shrink? + let min_new_size = match subrange.sub_ranges.as_mut() { + Some(child_subranges) => { + let child_first_subrange = &child_subranges[1]; + subrange.inner.end - child_first_subrange.start + } + None => { + 0 + } + }; + if new_size < min_new_size { + return errno!(ENOMEM, "Cannot shrink to new size"); + } + // Do shrink + let new_subrange_start = subrange.inner.end - new_size; + subrange.inner.start = new_subrange_start; + // Sync state + subranges[subrange_i].start = new_subrange_start; + } + Ok(()) + } + + fn grow_subrange_to(&mut self, subrange: &mut VMRange, new_size: usize) + -> Result<(), Error> + { + let subrange_i = self.position_subrange(subrange); + let subranges = self.get_subranges_mut(); + + if subrange.inner.growth == VMGrowthType::Upward { + // Can we grow? + let max_new_size = { + let next_subrange = &subranges[subrange_i + 1]; + next_subrange.start - subrange.inner.start + }; + if new_size > max_new_size { + return errno!(ENOMEM, "Cannot grow to new size"); + } + // Do grow + let subrange_new_end = subrange.inner.start + new_size; + subrange.inner.end = subrange_new_end; + // Sync state + subranges[subrange_i].end = subrange_new_end; + } + else { // self.growth == VMGrowthType::Downward + // Can we grow? + let max_new_size = { + let pre_subrange = &subranges[subrange_i - 1]; + subrange.inner.end - pre_subrange.end + }; + if new_size > max_new_size { + return errno!(ENOMEM, "Cannot grow to new size"); + } + // Do grow + let subrange_new_start = subrange.inner.end - new_size; + subrange.inner.start = subrange_new_start; + // Sync state + subranges[subrange_i].start = subrange_new_start; + } + Ok(()) + } + + fn mark_as_dealloced(&mut self) { + self.parent_range = 0 as *const VMRange; + self.inner.start = self.inner.end; + } + + fn is_dealloced(&self) -> bool { + self.parent_range == 0 as *const VMRange + } +} + +impl PartialOrd for VMRange { + fn partial_cmp(&self, other: &VMRange) -> Option { + self.inner.partial_cmp(&other.inner) + } +} + +impl PartialEq for VMRange { + fn eq(&self, other: &VMRange) -> bool { + self.inner.eq(&other.inner) + } +} + +impl Drop for VMRange { + fn drop(&mut self) { + if !self.is_dealloced() { + panic!("A range must be dealloc'ed before drop"); + } + if self.has_subranges() { + panic!("All sub-ranges must be removed explicitly before drop"); + } + } +} + +unsafe impl Send for VMRange {} +unsafe impl Sync for VMRange {} + + +#[derive(Clone, Copy)] +pub struct VMRangeInner { + start: usize, + end: usize, + growth: VMGrowthType, +} + +impl VMRangeInner { + pub fn new(start: usize, end: usize, growth: VMGrowthType) -> VMRangeInner + { + VMRangeInner { + start: start, + end: end, + growth: growth, + } + } +} + +impl VMRangeTrait for VMRangeInner { + fn get_start(&self) -> usize { + self.start + } + + fn get_end(&self) -> usize { + self.end + } + + fn get_size(&self) -> usize { + self.end - self.start + } + + fn get_growth(&self) -> VMGrowthType { + self.growth + } + + fn contains_obj(&self, ptr: usize, size: usize) -> bool { + let obj_begin = ptr as usize; + let obj_end = obj_begin + size; + self.start <= obj_begin && obj_end < self.end + } +} + +impl fmt::Debug for VMRangeInner { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VMRangeInner {{ start: 0x{:X?}, end: 0x{:X?}, size: 0x{:X?}, growth: {:?} }}", + self.start, self.end, self.get_size(), self.growth) + } +} + +impl PartialOrd for VMRangeInner { + fn partial_cmp(&self, other: &VMRangeInner) -> Option { + if self.end <= other.start { + return Some(Ordering::Less); + } + else if self.start >= other.end { + return Some(Ordering::Greater); + } + else if self.start == other.start && self.end == other.end { + return Some(Ordering::Equal); + } + else { + return None; + } + } +} + +impl PartialEq for VMRangeInner { + fn eq(&self, other: &VMRangeInner) -> bool { + self.start == other.start && self.end == other.end + } +} + + +#[derive(Debug)] +struct FreeSpace { + index_in_subranges: usize, + start: usize, + end: usize, + may_neighbor_grow: (bool, bool), +} + +impl FreeSpace { + fn get_neighbor_pressure(&self) -> u32 { + let mut pressure = 0; + pressure += if self.may_neighbor_grow.0 { 1 } else { 0 }; + pressure += if self.may_neighbor_grow.1 { 1 } else { 0 }; + pressure + } + fn get_size(&self) -> usize { + self.end - self.start + } +} + +impl PartialEq for FreeSpace { + fn eq(&self, other: &FreeSpace) -> bool { + self.get_size() == other.get_size() && + self.get_neighbor_pressure() == other.get_neighbor_pressure() + } +} + +impl PartialOrd for FreeSpace { + fn partial_cmp(&self, other: &FreeSpace) -> Option { + let self_size = self.get_size(); + let other_size = other.get_size(); + if self_size < other_size { + Some(Ordering::Less) + } + else if self_size > other_size { + Some(Ordering::Greater) + } + else { + // The less neighbor pressure, the larger the free space + let self_neighbor_pressure = self.get_neighbor_pressure(); + let other_neighbor_pressure = other.get_neighbor_pressure(); + if self_neighbor_pressure > other_neighbor_pressure { + Some(Ordering::Less) + } + else if self_neighbor_pressure < other_neighbor_pressure { + Some(Ordering::Greater) + } + else { + Some(Ordering::Equal) + } + } + } +} diff --git a/src/libos/src/vm/vm_space.rs b/src/libos/src/vm/vm_space.rs new file mode 100644 index 00000000..c506c904 --- /dev/null +++ b/src/libos/src/vm/vm_space.rs @@ -0,0 +1,39 @@ +use super::*; + +impl VMSpace { + pub unsafe fn new(addr: usize, size: usize, guard_type: VMGuardAreaType) + -> Result + { + let range = unsafe { + VMRange::new(addr, addr + size, VMGrowthType::Fixed)? + }; + Ok(VMSpace { range, guard_type }) + } + + pub fn get_guard_type(&self) -> VMGuardAreaType { + self.guard_type + } + + pub fn alloc_domain(&mut self, size: usize) + -> Result + { + let mut options = VMAllocOptions::new(size)?; + options.growth(VMGrowthType::Upward)?; + + let new_range = self.range.alloc_subrange(&options)?; + Ok(VMDomain { + range: new_range, + }) + } + + pub fn dealloc_domain(&mut self, domain: &mut VMDomain) { + self.range.dealloc_subrange(&mut domain.range) + } + + pub fn resize_domain(&mut self, domain: &mut VMDomain, new_size: usize) + -> Result<(), Error> + { + let options = VMResizeOptions::new(new_size)?; + self.range.resize_subrange(&mut domain.range, &options) + } +} diff --git a/src/libos/src/vma.rs b/src/libos/src/vma.rs deleted file mode 100644 index 7da1c610..00000000 --- a/src/libos/src/vma.rs +++ /dev/null @@ -1,211 +0,0 @@ -/// Virtuam Memory Area (VMA) -use prelude::*; -use {std}; - -use xmas_elf::program; -use xmas_elf::program::{ProgramHeader}; - -use mm::MemObj; - -#[derive(Clone, Debug, Default)] -#[repr(C)] -pub struct Vma { - /// Basic info - pub mem_size: usize, - pub mem_align: usize, - pub mem_flags: Perms, - - /// File mapping - pub file_is_mapped: bool, - pub mem_addr: usize, - pub file_offset: usize, - pub file_size: usize, - - /// Memory allocation - pub mem_begin: usize, - pub mem_end: usize, - underlying: Arc, -} - -const VMA_MIN_MEM_ALIGN: usize = (4 * 1024); - -impl Vma { - pub fn from_program_header<'a>(ph: &ProgramHeader<'a>) - -> Result - { - let ph64 = match ph { - ProgramHeader::Ph32(ph) => { - return Err((Errno::ENOEXEC, "Not support 32-bit ELF").into()) - } - ProgramHeader::Ph64(ph64) => { - ph64 - } - }; - if ph64.align > 1 && ((ph64.offset % ph64.align) != - (ph64.virtual_addr % ph64.align)) { - return Err((Errno::EINVAL, - "Memory address and file offset is not equal, per modulo").into()); - } - if ph64.mem_size < ph64.file_size { - return Err((Errno::EINVAL, - "Memory size must be greater than file size").into()); - } - - let mut new_vma = Vma::new(ph64.mem_size as usize, - ph64.align as usize, - Perms::from(&ph64.flags))?; - - new_vma.mem_addr = ph64.virtual_addr as usize; - new_vma.file_is_mapped = true; - new_vma.file_offset = ph64.offset as usize; - new_vma.file_size = ph64.file_size as usize; - - Ok(new_vma) - } - - pub fn new(mem_size: usize, mem_align: usize, mem_flags: Perms) - -> Result - { - if mem_align == 0 || mem_align % VMA_MIN_MEM_ALIGN != 0 { - return Err((Errno::EINVAL, - "Memory alignment is not a multiple of 4KB").into()); - } - if mem_size == 0 { - return Err((Errno::EINVAL, - "Memory size must be greater than zero").into()); - } - - Ok(Vma { - mem_size: mem_size, - mem_align: mem_align, - mem_flags: mem_flags, - .. Default::default() - }) - } - - pub fn contains(&self, mem_addr: usize) -> bool { - self.mem_begin <= mem_addr && mem_addr <= self.mem_end - } -} - -pub fn malloc_batch(vma_list: &mut [&mut Vma], mapped_data: &[u8]) - -> Result -{ - let mut max_align = VMA_MIN_MEM_ALIGN; - let mut total_size = 0; - for vma in vma_list.into_iter() { - let mem_begin = align_up(total_size, vma.mem_align); - let mem_end = mem_begin + align_up(vma.mem_size, vma.mem_align); - - if vma.file_is_mapped { - if vma.mem_addr < mem_begin || - vma.mem_addr + vma.mem_size > mem_end { - return Err((Errno::EINVAL, - "Impossible memory layout for the VMA").into()); - } - if vma.file_offset > mapped_data.len() || - vma.file_offset + vma.file_size > mapped_data.len() { - return Err((Errno::EINVAL, - "Impossible to load data from file").into()); - } - } - - total_size = mem_end; - if vma.mem_align > max_align { - max_align = vma.mem_align; - } - } - - let memobj = Arc::new(MemObj::new(total_size, max_align)?); - let program_base_addr = memobj.get_addr(); - let mut mem_cur = program_base_addr; - for vma in vma_list.into_iter() { - vma.mem_begin = align_up(mem_cur, vma.mem_align); - vma.mem_end = vma.mem_begin + align_up(vma.mem_size, vma.mem_align); - vma.mem_addr += program_base_addr; - vma.underlying = memobj.clone(); - - if vma.file_is_mapped { - let mut vma_data = unsafe { - std::slice::from_raw_parts_mut(vma.mem_addr as *mut u8, vma.file_size) - }; - vma_data.copy_from_slice(&mapped_data[vma.file_offset.. - vma.file_offset + vma.file_size]); - } - - mem_cur = vma.mem_end; - } - - Ok(program_base_addr) -} - -pub fn mprotect_batch(vma_list: &[&Vma]) - -> Result<(), Error> -{ - for vma in vma_list.into_iter() { - // If don't need to change memory permissions - if vma.mem_flags == Perms(PERM_R | PERM_W) { - continue; - } - - let start = align_down(vma.mem_addr, 4096); - let size = align_up(vma.mem_size, 4096); - let perms = vma.mem_flags.0 as uint64_t; - let status = unsafe { - //TODO: use proper permissions - //TODO: reset the permissions when drop VMA - //trts_mprotect(start, size, perms) - //println!("trts_mprotect: start = {}, size = {}", start, size); - trts_mprotect(start, size, (PERM_R | PERM_W | PERM_X) as uint64_t) - }; - if (status != sgx_status_t::SGX_SUCCESS) { - return Err((Errno::EACCES, "trts_mprotect failed").into()); - } - } - Ok(()) -} - - -#[derive(Copy, Clone, Debug, Default, PartialEq)] -pub struct Perms(pub u32); - -pub const PERM_R: u32 = 0x1; -pub const PERM_W: u32 = 0x2; -pub const PERM_X: u32 = 0x4; - -impl Perms { - pub fn is_execute(&self) -> bool { - self.0 & PERM_X == PERM_X - } - - pub fn is_write(&self) -> bool { - self.0 & PERM_W == PERM_W - } - - pub fn is_read(&self) -> bool { - self.0 & PERM_R == PERM_R - } -} - -impl<'a> From<&'a program::Flags> for Perms { - fn from(flags: &'a program::Flags) -> Self { - let mut val = 0; - if flags.is_execute() { val |= PERM_X; } - if flags.is_read() { val |= PERM_R; } - if flags.is_write() { val |= PERM_W; } - Perms(val) - } -} - -fn align_up(addr: usize, align: usize) -> usize { - (addr + (align - 1)) / align * align -} - -fn align_down(addr: usize, align: usize) -> usize { - addr & !(align - 1) -} - -#[link(name = "sgx_trts")] -extern { - pub fn trts_mprotect(start: size_t, size: size_t, perms: uint64_t) -> sgx_status_t; -}