Add virtual memory subsystem
This commit is contained in:
parent
4107a65248
commit
852903de20
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use prelude::*;
|
||||
use {std};
|
||||
use std::{fmt};
|
||||
use std::borrow::BorrowMut;
|
||||
|
||||
use std::sgxfs as fs_impl;
|
||||
|
||||
|
@ -50,7 +50,7 @@ pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc, Error> {
|
||||
|
||||
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<FileDesc, Error> {
|
||||
pub fn do_write(fd: FileDesc, buf: &[u8]) -> Result<usize, Error> {
|
||||
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<usize, Error> {
|
||||
pub fn do_read(fd: FileDesc, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
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<usize, Error> {
|
||||
pub fn do_writev<'a, 'b>(fd: FileDesc, bufs: &'a [&'b [u8]]) -> Result<usize, Error> {
|
||||
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<usize, Er
|
||||
pub fn do_readv<'a, 'b>(fd: FileDesc, bufs: &'a mut [&'b mut [u8]]) -> Result<usize, Error> {
|
||||
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<us
|
||||
pub fn do_lseek<'a, 'b>(fd: FileDesc, offset: SeekFrom) -> Result<off_t, Error> {
|
||||
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<off_t, Error>
|
||||
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]")),
|
||||
|
@ -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::*;
|
||||
|
@ -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<Self, Error>
|
||||
{
|
||||
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;
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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<HashMap<u32, ProcessRef>> = {
|
||||
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<ProcessRef> {
|
||||
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<P: AsRef<Path>>(elf_path: &P, argv: &[CString], envp: &[CString])
|
||||
-> Result<u32, Error>
|
||||
{
|
||||
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<Box<File>> = Arc::new(Box::new(StdinFile::new()));
|
||||
let stdout : Arc<Box<File>> = 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<Process>> =
|
||||
Cell::new(0 as *const SgxMutex<Process>);
|
||||
}
|
||||
|
||||
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<Process>;
|
||||
_CURRENT_PROCESS_PTR.with(|cp| {
|
||||
process_ptr = cp.get();
|
||||
cp.set(0 as *const SgxMutex<Process>);
|
||||
});
|
||||
|
||||
// Prevent memory leakage
|
||||
unsafe { drop(Arc::from_raw(process_ptr)); }
|
||||
}
|
||||
|
||||
pub fn get_current() -> &'static SgxMutex<Process> {
|
||||
let mut process_ptr : *const SgxMutex<Process> = 0 as *const SgxMutex<Process>;
|
||||
_CURRENT_PROCESS_PTR.with(|cp| {
|
||||
process_ptr = cp.get();
|
||||
});
|
||||
unsafe {
|
||||
mem::transmute::<*const SgxMutex<Process>, &'static SgxMutex<Process>>(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<i32, Error> {
|
||||
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<P: AsRef<Path>>(path: &P) -> Result<Vec<u8>, 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::<u8>::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<SgxMutex<Process>>;
|
||||
|
||||
impl Process {
|
||||
pub fn new(elf_file: &ElfFile, argv: &[CString], envp: &[CString])
|
||||
-> Result<Process, Error>
|
||||
{
|
||||
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<usize, Error>
|
||||
{
|
||||
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<VecDeque<ProcessRef>> = {
|
||||
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<ProcessRef> {
|
||||
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;
|
||||
}
|
45
src/libos/src/process/mod.rs
Normal file
45
src/libos/src/process/mod.rs
Normal file
@ -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<i32, Error> {
|
||||
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::*;
|
79
src/libos/src/process/process.rs
Normal file
79
src/libos/src/process/process.rs
Normal file
@ -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<SgxMutex<Process>>;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
32
src/libos/src/process/process_table.rs
Normal file
32
src/libos/src/process/process_table.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use super::*;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
lazy_static! {
|
||||
static ref PROCESS_TABLE: SgxMutex<HashMap<pid_t, ProcessRef>> = {
|
||||
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<ProcessRef> {
|
||||
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:
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use prelude::*;
|
||||
use super::*;
|
||||
|
||||
use xmas_elf::{sections, ElfFile, program, P64};
|
||||
use xmas_elf::symbol_table::{Entry64, DynEntry64};
|
@ -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<usize, Error>
|
||||
{
|
||||
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<usize>,
|
||||
}
|
||||
|
||||
impl StackBuf {
|
||||
pub fn new(stack_top: *const u8, stack_bottom: *const u8) -> Result<StackBuf, Error>{
|
||||
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<StackBuf, Error>{
|
||||
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::<T>();
|
||||
let val_align = mem::align_of::<T>();
|
||||
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);
|
||||
|
106
src/libos/src/process/spawn/init_vm.rs
Normal file
106
src/libos/src/process/spawn/init_vm.rs
Normal file
@ -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<ProcessVM, Error> {
|
||||
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;
|
||||
}
|
106
src/libos/src/process/spawn/mod.rs
Normal file
106
src/libos/src/process/spawn/mod.rs
Normal file
@ -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<P: AsRef<Path>>(elf_path: &P, argv: &[CString], envp: &[CString])
|
||||
-> Result<u32, Error>
|
||||
{
|
||||
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::<u8>::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<FileTable, Error> {
|
||||
let mut file_table = FileTable::new();
|
||||
|
||||
let stdin : Arc<Box<File>> = Arc::new(Box::new(StdinFile::new()));
|
||||
let stdout : Arc<Box<File>> = 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<Task, Error>
|
||||
{
|
||||
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<usize, Error>
|
||||
{
|
||||
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)
|
||||
}
|
102
src/libos/src/process/spawn/segment.rs
Normal file
102
src/libos/src/process/spawn/segment.rs
Normal file
@ -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<Segment, Error> {
|
||||
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<Segment, 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"))?;
|
||||
Segment::from_program_header(&code_ph)
|
||||
}
|
||||
|
||||
pub fn get_data_segment(elf_file: &ElfFile) -> Result<Segment, Error> {
|
||||
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;
|
||||
}
|
100
src/libos/src/process/task.rs
Normal file
100
src/libos/src/process/task.rs
Normal file
@ -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<VecDeque<ProcessRef>> = {
|
||||
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<ProcessRef> {
|
||||
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<Process>> =
|
||||
Cell::new(0 as *const SgxMutex<Process>);
|
||||
}
|
||||
|
||||
pub fn get_current() -> &'static SgxMutex<Process> {
|
||||
let mut process_ptr = 0 as *const SgxMutex<Process>;
|
||||
_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<Process>;
|
||||
_CURRENT_PROCESS_PTR.with(|cp| {
|
||||
process_ptr = cp.get();
|
||||
cp.set(0 as *const SgxMutex<Process>);
|
||||
});
|
||||
|
||||
// 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;
|
||||
}
|
178
src/libos/src/vm/mod.rs
Normal file
178
src/libos/src/vm/mod.rs
Normal file
@ -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<VMGrowthType>,
|
||||
}
|
||||
|
||||
impl VMAllocOptions {
|
||||
pub fn new(size: usize) -> Result<VMAllocOptions, Error> {
|
||||
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<VMAddrOption>,
|
||||
}
|
||||
|
||||
impl VMResizeOptions {
|
||||
pub fn new(new_size: usize) -> Result<VMResizeOptions, Error> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
245
src/libos/src/vm/process_vm.rs
Normal file
245
src/libos/src/vm/process_vm.rs
Normal file
@ -0,0 +1,245 @@
|
||||
use super::*;
|
||||
|
||||
const DATA_SPACE_SIZE : usize = 12 * 1024 * 1024; // 16MB
|
||||
|
||||
lazy_static! {
|
||||
static ref DATA_SPACE: SgxMutex<VMSpace> = {
|
||||
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<Box<VMArea>>,
|
||||
}
|
||||
|
||||
impl ProcessVM {
|
||||
pub fn new(code_size: usize, data_size: usize,
|
||||
heap_size: usize, stack_size: usize,
|
||||
mmap_size: usize)
|
||||
-> Result<ProcessVM, Error>
|
||||
{
|
||||
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<VMArea>] {
|
||||
&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<usize, Error>
|
||||
{
|
||||
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<usize, Error>
|
||||
{
|
||||
// TODO: Implement this!
|
||||
Err(Error::new(Errno::EINVAL, "Not implemented"))
|
||||
}
|
||||
|
||||
pub fn brk(&mut self, new_brk: usize) -> Result<usize, Error> {
|
||||
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);
|
||||
}
|
||||
}
|
11
src/libos/src/vm/vm_area.rs
Normal file
11
src/libos/src/vm/vm_area.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
31
src/libos/src/vm/vm_domain.rs
Normal file
31
src/libos/src/vm/vm_domain.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use super::*;
|
||||
|
||||
impl VMDomain {
|
||||
pub fn alloc_area(&mut self, options: &VMAllocOptions, flags: VMAreaFlags) -> Result<VMArea, Error> {
|
||||
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;
|
||||
}
|
512
src/libos/src/vm/vm_range.rs
Normal file
512
src/libos/src/vm/vm_range.rs
Normal file
@ -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<Vec<VMRangeInner>>,
|
||||
}
|
||||
|
||||
impl VMRange {
|
||||
pub unsafe fn new(start: usize, end: usize, growth: VMGrowthType) -> Result<VMRange, Error> {
|
||||
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<VMRange, Error> {
|
||||
// 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<FreeSpace> = 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<VMRangeInner> {
|
||||
self.sub_ranges.as_ref().unwrap()
|
||||
}
|
||||
|
||||
fn get_subranges_mut(&mut self) -> &mut Vec<VMRangeInner> {
|
||||
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<Ordering> {
|
||||
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<Ordering> {
|
||||
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<Ordering> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
src/libos/src/vm/vm_space.rs
Normal file
39
src/libos/src/vm/vm_space.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use super::*;
|
||||
|
||||
impl VMSpace {
|
||||
pub unsafe fn new(addr: usize, size: usize, guard_type: VMGuardAreaType)
|
||||
-> Result<VMSpace, Error>
|
||||
{
|
||||
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<VMDomain, Error>
|
||||
{
|
||||
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)
|
||||
}
|
||||
}
|
@ -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<MemObj>,
|
||||
}
|
||||
|
||||
const VMA_MIN_MEM_ALIGN: usize = (4 * 1024);
|
||||
|
||||
impl Vma {
|
||||
pub fn from_program_header<'a>(ph: &ProgramHeader<'a>)
|
||||
-> Result<Vma, Error>
|
||||
{
|
||||
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<Self, Error>
|
||||
{
|
||||
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<usize, Error>
|
||||
{
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user