Merge remote-tracking branch 'official/master' into net
# Conflicts: # src/libos/src/fs/mod.rs # src/libos/src/syscall/mod.rs # test/Makefile
This commit is contained in:
commit
b5697ab611
52
src/libos/src/fs/access.rs
Normal file
52
src/libos/src/fs/access.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use super::*;
|
||||
|
||||
//int faccessat(int dirfd, const char *pathname, int mode, int flags);
|
||||
//int access(const char *pathname, int mode);
|
||||
|
||||
bitflags! {
|
||||
pub struct AccessModes : u32 {
|
||||
const X_OK = 1;
|
||||
const W_OK = 2;
|
||||
const R_OK = 4;
|
||||
}
|
||||
}
|
||||
|
||||
impl AccessModes {
|
||||
pub fn from_u32(bits: u32) -> Result<AccessModes, Error> {
|
||||
AccessModes::from_bits(bits).ok_or_else(|| Error::new(Errno::EINVAL, "invalid mode"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bitflags! {
|
||||
pub struct AccessFlags : u32 {
|
||||
const AT_SYMLINK_NOFOLLOW = 0x100;
|
||||
const AT_EACCESS = 0x200;
|
||||
}
|
||||
}
|
||||
|
||||
impl AccessFlags {
|
||||
pub fn from_u32(bits: u32) -> Result<AccessFlags, Error> {
|
||||
AccessFlags::from_bits(bits).ok_or_else(|| Error::new(Errno::EINVAL, "invalid flags"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub const AT_FDCWD : i32 = -100;
|
||||
|
||||
pub fn do_faccessat(dirfd: Option<FileDesc>, path: &str, mode: AccessModes, flags: AccessFlags) -> Result<(), Error> {
|
||||
match dirfd {
|
||||
// TODO: handle dirfd
|
||||
Some(dirfd) => errno!(ENOSYS, "cannot accept dirfd"),
|
||||
None => do_access(path, mode),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_access(path: &str, mode: AccessModes) -> Result<(), Error> {
|
||||
let current_ref = process::get_current();
|
||||
let mut current = current_ref.lock().unwrap();
|
||||
let inode = current.lookup_inode(path)?;
|
||||
//let metadata = inode.get_metadata();
|
||||
// TODO: check metadata.mode with mode
|
||||
Ok(())
|
||||
}
|
@ -11,12 +11,6 @@ pub struct FileTable {
|
||||
num_fds: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileTableEntry {
|
||||
file: FileRef,
|
||||
close_on_spawn: bool,
|
||||
}
|
||||
|
||||
impl FileTable {
|
||||
pub fn new() -> FileTable {
|
||||
FileTable {
|
||||
@ -25,12 +19,38 @@ impl FileTable {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dup(&mut self, fd: FileDesc, min_fd: FileDesc, close_on_spawn: bool) -> Result<FileDesc, Error> {
|
||||
let file_ref = self.get(fd)?;
|
||||
|
||||
let min_fd = min_fd as usize;
|
||||
let min_free_fd = {
|
||||
let mut table = &mut self.table;
|
||||
|
||||
// Make sure that min_fd does not exceed the capacity of the table
|
||||
if min_fd >= table.len() {
|
||||
let expand_size = min_fd - table.len() + 1;
|
||||
for _ in 0..expand_size {
|
||||
table.push(None)
|
||||
}
|
||||
}
|
||||
|
||||
table.iter()
|
||||
.enumerate()
|
||||
.skip(min_fd as usize)
|
||||
.find(|&(idx, opt)| opt.is_none())
|
||||
.unwrap().0
|
||||
} as FileDesc;
|
||||
|
||||
self.put_at(min_free_fd, file_ref, close_on_spawn);
|
||||
|
||||
Ok(min_free_fd)
|
||||
}
|
||||
|
||||
pub fn put(&mut self, file: FileRef, close_on_spawn: bool) -> FileDesc {
|
||||
let mut table = &mut self.table;
|
||||
|
||||
let min_free_fd = if self.num_fds < table.len() {
|
||||
table
|
||||
.iter()
|
||||
table.iter()
|
||||
.enumerate()
|
||||
.find(|&(idx, opt)| opt.is_none())
|
||||
.unwrap()
|
||||
@ -59,13 +79,30 @@ impl FileTable {
|
||||
}
|
||||
|
||||
pub fn get(&self, fd: FileDesc) -> Result<FileRef, Error> {
|
||||
let entry = self.get_entry(fd)?;
|
||||
Ok(entry.file.clone())
|
||||
}
|
||||
|
||||
pub fn get_entry(&self, fd: FileDesc) -> Result<&FileTableEntry, Error> {
|
||||
if fd as usize >= self.table.len() {
|
||||
return errno!(EBADF, "Invalid file descriptor");
|
||||
}
|
||||
|
||||
let table = &self.table;
|
||||
match table[fd as usize].as_ref() {
|
||||
Some(table_entry) => Ok(table_entry.file.clone()),
|
||||
Some(table_entry) => Ok(table_entry),
|
||||
None => errno!(EBADF, "Invalid file descriptor"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_entry_mut(&mut self, fd: FileDesc) -> Result<&mut FileTableEntry, Error> {
|
||||
if fd as usize >= self.table.len() {
|
||||
return errno!(EBADF, "Invalid file descriptor");
|
||||
}
|
||||
|
||||
let table = &mut self.table;
|
||||
match table[fd as usize].as_mut() {
|
||||
Some(table_entry) => Ok(table_entry),
|
||||
None => errno!(EBADF, "Invalid file descriptor"),
|
||||
}
|
||||
}
|
||||
@ -114,11 +151,33 @@ impl Clone for FileTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileTableEntry {
|
||||
file: FileRef,
|
||||
close_on_spawn: bool,
|
||||
}
|
||||
|
||||
impl FileTableEntry {
|
||||
fn new(file: FileRef, close_on_spawn: bool) -> FileTableEntry {
|
||||
pub fn new(file: FileRef, close_on_spawn: bool) -> FileTableEntry {
|
||||
FileTableEntry {
|
||||
file,
|
||||
close_on_spawn,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_file(&self) -> &FileRef {
|
||||
&self.file
|
||||
}
|
||||
|
||||
pub fn is_close_on_spawn(&self) -> bool {
|
||||
self.close_on_spawn
|
||||
}
|
||||
|
||||
pub fn get_file_mut(&mut self) -> &mut FileRef {
|
||||
&mut self.file
|
||||
}
|
||||
|
||||
pub fn set_close_on_spawn(&mut self, close_on_spawn: bool) {
|
||||
self.close_on_spawn = close_on_spawn;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ pub use self::socket_file::SocketFile;
|
||||
use self::inode_file::OpenOptions;
|
||||
pub use self::pipe::Pipe;
|
||||
pub use self::io_multiplexing::*;
|
||||
pub use self::access::{AccessModes, AccessFlags, AT_FDCWD, do_access, do_faccessat};
|
||||
|
||||
mod file;
|
||||
mod file_table;
|
||||
@ -21,6 +22,7 @@ mod socket_file;
|
||||
mod pipe;
|
||||
mod sgx_impl;
|
||||
mod io_multiplexing;
|
||||
mod access;
|
||||
|
||||
|
||||
pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc, Error> {
|
||||
@ -387,7 +389,7 @@ fn split_path(path: &str) -> (&str, &str) {
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct OpenFlags: u32 {
|
||||
pub struct OpenFlags: u32 {
|
||||
/// read only
|
||||
const RDONLY = 0;
|
||||
/// write only
|
||||
@ -611,3 +613,89 @@ pub unsafe fn write_cstr(ptr: *mut u8, s: &str) {
|
||||
ptr.copy_from(s.as_ptr(), s.len());
|
||||
ptr.add(s.len()).write(0);
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FcntlCmd {
|
||||
/// Duplicate the file descriptor fd using the lowest-numbered available
|
||||
/// file descriptor greater than or equal to arg.
|
||||
DupFd(FileDesc),
|
||||
/// As for `DupFd`, but additionally set the close-on-exec flag for the
|
||||
/// duplicate file descriptor.
|
||||
DupFdCloexec(FileDesc),
|
||||
/// Return (as the function result) the file descriptor flags
|
||||
GetFd(),
|
||||
/// Set the file descriptor to be close-on-exec or not
|
||||
SetFd(u32),
|
||||
/// Get the file status flags
|
||||
GetFl(),
|
||||
/// Set the file status flags
|
||||
SetFl(OpenFlags),
|
||||
}
|
||||
|
||||
pub const F_DUPFD : u32 = 0;
|
||||
pub const F_GETFD : u32 = 1;
|
||||
pub const F_SETFD : u32 = 2;
|
||||
pub const F_GETFL : u32 = 3;
|
||||
pub const F_SETFL : u32 = 4;
|
||||
pub const F_DUPFD_CLOEXEC : u32 = 1030;
|
||||
|
||||
pub const FD_CLOEXEC : u32 = 1;
|
||||
|
||||
impl FcntlCmd {
|
||||
#[deny(unreachable_patterns)]
|
||||
pub fn from_raw(cmd: u32, arg: u64) -> Result<FcntlCmd, Error> {
|
||||
Ok(match cmd {
|
||||
F_DUPFD => FcntlCmd::DupFd(arg as FileDesc),
|
||||
F_DUPFD_CLOEXEC => FcntlCmd::DupFdCloexec(arg as FileDesc),
|
||||
F_GETFD => FcntlCmd::GetFd(),
|
||||
F_SETFD => FcntlCmd::SetFd(arg as u32),
|
||||
F_GETFL => FcntlCmd::GetFl(),
|
||||
F_SETFL => FcntlCmd::SetFl(OpenFlags::from_bits_truncate(arg as u32)),
|
||||
_ => return errno!(EINVAL, "invalid command"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result<isize, Error> {
|
||||
info!("do_fcntl: {:?}, {:?}", &fd, cmd);
|
||||
let current_ref = process::get_current();
|
||||
let mut current = current_ref.lock().unwrap();
|
||||
let files_ref = current.get_files();
|
||||
let mut files = files_ref.lock().unwrap();
|
||||
Ok(match cmd {
|
||||
FcntlCmd::DupFd(min_fd) => {
|
||||
let dup_fd = files.dup(fd, *min_fd, false)?;
|
||||
dup_fd as isize
|
||||
},
|
||||
FcntlCmd::DupFdCloexec(min_fd) => {
|
||||
let dup_fd = files.dup(fd, *min_fd, true)?;
|
||||
dup_fd as isize
|
||||
},
|
||||
FcntlCmd::GetFd() => {
|
||||
let entry = files.get_entry(fd)?;
|
||||
let fd_flags = if entry.is_close_on_spawn() {
|
||||
FD_CLOEXEC
|
||||
} else {
|
||||
0
|
||||
};
|
||||
fd_flags as isize
|
||||
},
|
||||
FcntlCmd::SetFd(fd_flags) => {
|
||||
let entry = files.get_entry_mut(fd)?;
|
||||
entry.set_close_on_spawn((fd_flags & FD_CLOEXEC) != 0);
|
||||
0
|
||||
},
|
||||
FcntlCmd::GetFl() => {
|
||||
unimplemented!();
|
||||
},
|
||||
FcntlCmd::SetFl(flags) => {
|
||||
unimplemented!();
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
// TODO: support symbolic links
|
||||
errno!(EINVAL, "not a symbolic link")
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ mod syscall;
|
||||
mod time;
|
||||
mod util;
|
||||
mod vm;
|
||||
mod misc;
|
||||
|
||||
use prelude::*;
|
||||
|
||||
|
7
src/libos/src/misc/mod.rs
Normal file
7
src/libos/src/misc/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use super::*;
|
||||
|
||||
mod uname;
|
||||
mod rlimit;
|
||||
|
||||
pub use self::uname::{utsname_t, do_uname};
|
||||
pub use self::rlimit::{rlimit_t, resource_t, ResourceLimits, ResourceLimitsRef, do_prlimit};
|
123
src/libos/src/misc/rlimit.rs
Normal file
123
src/libos/src/misc/rlimit.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use super::*;
|
||||
use process::{pid_t};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ResourceLimits {
|
||||
rlimits: [rlimit_t; RLIMIT_COUNT],
|
||||
}
|
||||
pub type ResourceLimitsRef = Arc<SgxMutex<ResourceLimits>>;
|
||||
|
||||
impl ResourceLimits {
|
||||
pub fn get(&self, resource: resource_t) -> &rlimit_t {
|
||||
&self.rlimits[resource as usize]
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, resource: resource_t) -> &mut rlimit_t {
|
||||
&mut self.rlimits[resource as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ResourceLimits {
|
||||
fn default() -> ResourceLimits {
|
||||
// TODO: set appropriate limits for resources
|
||||
let mut rlimits = ResourceLimits {
|
||||
rlimits: [ Default::default(); RLIMIT_COUNT ],
|
||||
};
|
||||
rlimits
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct rlimit_t {
|
||||
cur: u64,
|
||||
max: u64,
|
||||
}
|
||||
|
||||
impl Default for rlimit_t {
|
||||
fn default() -> rlimit_t {
|
||||
rlimit_t {
|
||||
cur: u64::max_value(),
|
||||
max: u64::max_value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum resource_t {
|
||||
RLIMIT_CPU = 0,
|
||||
RLIMIT_FSIZE = 1,
|
||||
RLIMIT_DATA = 2,
|
||||
RLIMIT_STACK = 3,
|
||||
RLIMIT_CORE = 4,
|
||||
RLIMIT_RSS = 5,
|
||||
RLIMIT_NPROC = 6,
|
||||
RLIMIT_NOFILE = 7,
|
||||
RLIMIT_MEMLOCK = 8,
|
||||
RLIMIT_AS = 9,
|
||||
RLIMIT_LOCKS = 10,
|
||||
RLIMIT_SIGPENDING = 11,
|
||||
RLIMIT_MSGQUEUE = 12,
|
||||
RLIMIT_NICE = 13,
|
||||
RLIMIT_RTPRIO = 14,
|
||||
}
|
||||
const RLIMIT_COUNT: usize = 15;
|
||||
|
||||
impl resource_t {
|
||||
pub fn from_u32(bits: u32) -> Result<resource_t, Error> {
|
||||
match bits {
|
||||
0 => Ok(resource_t::RLIMIT_CPU),
|
||||
1 => Ok(resource_t::RLIMIT_FSIZE),
|
||||
2 => Ok(resource_t::RLIMIT_DATA),
|
||||
3 => Ok(resource_t::RLIMIT_STACK),
|
||||
4 => Ok(resource_t::RLIMIT_CORE),
|
||||
5 => Ok(resource_t::RLIMIT_RSS),
|
||||
6 => Ok(resource_t::RLIMIT_NPROC),
|
||||
7 => Ok(resource_t::RLIMIT_NOFILE),
|
||||
8 => Ok(resource_t::RLIMIT_MEMLOCK),
|
||||
9 => Ok(resource_t::RLIMIT_AS),
|
||||
10 => Ok(resource_t::RLIMIT_LOCKS),
|
||||
11 => Ok(resource_t::RLIMIT_SIGPENDING),
|
||||
12 => Ok(resource_t::RLIMIT_MSGQUEUE),
|
||||
13 => Ok(resource_t::RLIMIT_NICE),
|
||||
14 => Ok(resource_t::RLIMIT_RTPRIO),
|
||||
_ => errno!(EINVAL, "invalid resource"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn do_prlimit(
|
||||
pid: pid_t,
|
||||
resource: resource_t,
|
||||
new_limit: Option<&rlimit_t>,
|
||||
old_limit: Option<&mut rlimit_t>,
|
||||
) -> Result<(), Error> {
|
||||
let process_ref = if pid == 0 {
|
||||
process::get_current()
|
||||
}
|
||||
else {
|
||||
process::get(pid)?
|
||||
};
|
||||
let mut process = process_ref.lock().unwrap();
|
||||
let rlimits_ref = process.get_rlimits();
|
||||
let mut rlimits = rlimits_ref.lock().unwrap();
|
||||
if let Some(old_limit) = old_limit {
|
||||
*old_limit = *rlimits.get(resource)
|
||||
}
|
||||
if let Some(new_limit) = new_limit {
|
||||
*rlimits.get_mut(resource) = *new_limit;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn do_getrlimit(resource: resource_t, old_limit: &mut rlimit_t) -> Result<(), Error> {
|
||||
do_prlimit(0 as pid_t, resource, None, Some(old_limit))
|
||||
}
|
||||
|
||||
pub fn do_setrlimit(resource: resource_t, new_limit: &rlimit_t) -> Result<(), Error> {
|
||||
do_prlimit(0 as pid_t, resource, Some(new_limit), None)
|
||||
}
|
51
src/libos/src/misc/uname.rs
Normal file
51
src/libos/src/misc/uname.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use super::*;
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
/// A sample of `struct utsname`
|
||||
/// ```
|
||||
/// sysname = Linux
|
||||
/// nodename = tian-nuc
|
||||
/// release = 4.15.0-42-generic
|
||||
/// version = #45~16.04.1-Ubuntu SMP Mon Nov 19 13:02:27 UTC 2018
|
||||
/// machine = x86_64
|
||||
/// domainname = (none)
|
||||
/// ```
|
||||
///
|
||||
/// By the way, UTS stands for UNIX Timesharing System.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct utsname_t {
|
||||
sysname: [u8; 65],
|
||||
nodename: [u8; 65],
|
||||
release: [u8; 65],
|
||||
version: [u8; 65],
|
||||
machine: [u8; 65],
|
||||
domainname: [u8; 65],
|
||||
}
|
||||
|
||||
pub fn do_uname(name: &mut utsname_t) -> Result<(), Error> {
|
||||
copy_from_cstr_to_u8_array(&SYSNAME, &mut name.sysname);
|
||||
copy_from_cstr_to_u8_array(&NODENAME, &mut name.nodename);
|
||||
copy_from_cstr_to_u8_array(&RELEASE, &mut name.release);
|
||||
copy_from_cstr_to_u8_array(&VERSION, &mut name.version);
|
||||
copy_from_cstr_to_u8_array(&MACHINE, &mut name.machine);
|
||||
copy_from_cstr_to_u8_array(&DOMAINNAME, &mut name.domainname);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref SYSNAME : CString = CString::new("Occlum").unwrap();
|
||||
static ref NODENAME: CString = CString::new("occlum-node").unwrap();
|
||||
static ref RELEASE: CString = CString::new("0.1").unwrap();
|
||||
static ref VERSION: CString = CString::new("0.1").unwrap();
|
||||
static ref MACHINE: CString = CString::new("x86-64").unwrap();
|
||||
static ref DOMAINNAME: CString = CString::new("").unwrap();
|
||||
}
|
||||
|
||||
fn copy_from_cstr_to_u8_array(src: &CStr, dst: &mut [u8]) {
|
||||
let src : &[u8] = src.to_bytes_with_nul();
|
||||
let len = min(dst.len() - 1, src.len());
|
||||
dst[..len].copy_from_slice(&src[..len]);
|
||||
dst[len] = 0;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ pub use std::iter::Iterator;
|
||||
pub use std::rc::Rc;
|
||||
pub use std::string::String;
|
||||
pub use std::vec::Vec;
|
||||
pub use std::cmp::{min, max};
|
||||
|
||||
pub use errno::Errno;
|
||||
pub use errno::Errno::*;
|
||||
@ -53,3 +54,7 @@ pub fn align_up(addr: usize, align: usize) -> usize {
|
||||
pub fn align_down(addr: usize, align: usize) -> usize {
|
||||
addr & !(align - 1)
|
||||
}
|
||||
|
||||
pub fn unbox<T>(value: Box<T>) -> T {
|
||||
*value
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
pub use self::process::{Status, IDLE_PROCESS};
|
||||
pub use self::task::{get_current, run_task};
|
||||
pub mod table {
|
||||
pub use super::process_table::get;
|
||||
}
|
||||
pub use self::process_table::{get};
|
||||
pub use self::exit::{do_exit, do_wait4, ChildProcessFilter};
|
||||
pub use self::spawn::{do_spawn, FileAction};
|
||||
pub use self::wait::{WaitQueue, Waiter};
|
||||
@ -30,6 +28,7 @@ pub struct Process {
|
||||
waiting_children: Option<WaitQueue<ChildProcessFilter, pid_t>>,
|
||||
vm: ProcessVMRef,
|
||||
file_table: FileTableRef,
|
||||
rlimits: ResourceLimitsRef,
|
||||
}
|
||||
|
||||
pub type ProcessRef = Arc<SgxMutex<Process>>;
|
||||
@ -79,3 +78,4 @@ use self::task::Task;
|
||||
use super::*;
|
||||
use fs::{File, FileRef, FileTable};
|
||||
use vm::{ProcessVM, VMRangeTrait};
|
||||
use misc::{ResourceLimitsRef};
|
||||
|
@ -10,7 +10,7 @@ lazy_static! {
|
||||
task: Default::default(),
|
||||
status: Default::default(),
|
||||
pid: 0,
|
||||
pgid: 0,
|
||||
pgid: 1,
|
||||
tgid: 0,
|
||||
exit_status: 0,
|
||||
cwd: "/".to_owned(),
|
||||
@ -20,6 +20,7 @@ lazy_static! {
|
||||
waiting_children: Default::default(),
|
||||
vm: Default::default(),
|
||||
file_table: Default::default(),
|
||||
rlimits: Default::default(),
|
||||
}))
|
||||
};
|
||||
}
|
||||
@ -30,13 +31,14 @@ impl Process {
|
||||
task: Task,
|
||||
vm_ref: ProcessVMRef,
|
||||
file_table_ref: FileTableRef,
|
||||
rlimits_ref: ResourceLimitsRef,
|
||||
) -> 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,
|
||||
pgid: new_pid,
|
||||
pgid: 1, // TODO: implement pgid
|
||||
tgid: new_pid,
|
||||
cwd: cwd.to_owned(),
|
||||
clear_child_tid: None,
|
||||
@ -46,6 +48,7 @@ impl Process {
|
||||
waiting_children: None,
|
||||
vm: vm_ref,
|
||||
file_table: file_table_ref,
|
||||
rlimits: rlimits_ref,
|
||||
}));
|
||||
Ok((new_pid, new_process_ref))
|
||||
}
|
||||
@ -97,6 +100,9 @@ impl Process {
|
||||
self.cwd += path;
|
||||
}
|
||||
}
|
||||
pub fn get_rlimits(&self) -> &ResourceLimitsRef {
|
||||
&self.rlimits
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Process {
|
||||
|
@ -14,8 +14,10 @@ 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())
|
||||
pub fn get(pid: pid_t) -> Result<ProcessRef, Error> {
|
||||
PROCESS_TABLE.lock().unwrap().get(&pid)
|
||||
.map(|pr| pr.clone())
|
||||
.ok_or_else(|| Error::new(Errno::ENOENT, "process not found"))
|
||||
}
|
||||
|
||||
static NEXT_PID: AtomicU32 = AtomicU32::new(1);
|
||||
|
@ -6,6 +6,7 @@ use std::ffi::{CStr, CString};
|
||||
use std::path::Path;
|
||||
use std::sgxfs::SgxFile;
|
||||
use vm::{ProcessVM, VMRangeTrait};
|
||||
use misc::{ResourceLimitsRef};
|
||||
|
||||
use super::*;
|
||||
use super::task::Task;
|
||||
@ -72,7 +73,8 @@ pub fn do_spawn<P: AsRef<Path>>(
|
||||
let files = init_files(parent_ref, file_actions)?;
|
||||
Arc::new(SgxMutex::new(files))
|
||||
};
|
||||
Process::new(&cwd, task, vm_ref, files_ref)?
|
||||
let rlimits_ref = Default::default();
|
||||
Process::new(&cwd, task, vm_ref, files_ref, rlimits_ref)?
|
||||
};
|
||||
parent_adopts_new_child(&parent_ref, &new_process_ref);
|
||||
process_table::put(new_pid, new_process_ref.clone());
|
||||
|
@ -51,8 +51,9 @@ pub fn do_clone(
|
||||
let task = new_thread_task(stack_addr, new_tls)?;
|
||||
let vm_ref = current.get_vm().clone();
|
||||
let files_ref = current.get_files().clone();
|
||||
let rlimits_ref = current.get_rlimits().clone();
|
||||
let cwd = ¤t.cwd;
|
||||
Process::new(cwd, task, vm_ref, files_ref)?
|
||||
Process::new(cwd, task, vm_ref, files_ref, rlimits_ref)?
|
||||
};
|
||||
|
||||
if let Some(ctid) = ctid {
|
||||
@ -67,6 +68,8 @@ pub fn do_clone(
|
||||
let mut new_thread = new_thread_ref.lock().unwrap();
|
||||
parent.children.push(Arc::downgrade(&new_thread_ref));
|
||||
new_thread.parent = Some(parent_ref.clone());
|
||||
|
||||
new_thread.tgid = current.tgid;
|
||||
}
|
||||
|
||||
process_table::put(new_thread_pid, new_thread_ref.clone());
|
||||
|
@ -7,7 +7,7 @@
|
||||
//! 3. Dispatch the syscall to `do_*` (at this file)
|
||||
//! 4. Do some memory checks then call `mod::do_*` (at each module)
|
||||
|
||||
use fs::{File, SocketFile, FileDesc, FileRef, EpollOp};
|
||||
use fs::{File, SocketFile, FileDesc, FileRef, EpollOp, AccessModes, AccessFlags, AT_FDCWD, FcntlCmd};
|
||||
use prelude::*;
|
||||
use process::{ChildProcessFilter, FileAction, pid_t, CloneFlags, FutexFlags, FutexOp};
|
||||
use std::ffi::{CStr, CString};
|
||||
@ -16,6 +16,7 @@ use time::timeval_t;
|
||||
use util::mem_util::from_user::*;
|
||||
use vm::{VMAreaFlags, VMResizeOptions};
|
||||
use {fs, process, std, vm};
|
||||
use misc::{utsname_t, resource_t, rlimit_t};
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -66,6 +67,8 @@ pub extern "C" fn dispatch_syscall(
|
||||
SYS_STAT => do_stat(arg0 as *const i8, arg1 as *mut fs::Stat),
|
||||
SYS_FSTAT => do_fstat(arg0 as FileDesc, arg1 as *mut fs::Stat),
|
||||
SYS_LSTAT => do_lstat(arg0 as *const i8, arg1 as *mut fs::Stat),
|
||||
SYS_ACCESS => do_access(arg0 as *const i8, arg1 as u32),
|
||||
SYS_FACCESSAT => do_faccessat(arg0 as i32, arg1 as *const i8, arg2 as u32, arg3 as u32),
|
||||
SYS_LSEEK => do_lseek(arg0 as FileDesc, arg1 as off_t, arg2 as i32),
|
||||
SYS_FSYNC => do_fsync(arg0 as FileDesc),
|
||||
SYS_FDATASYNC => do_fdatasync(arg0 as FileDesc),
|
||||
@ -80,6 +83,8 @@ pub extern "C" fn dispatch_syscall(
|
||||
SYS_RMDIR => do_rmdir(arg0 as *const i8),
|
||||
SYS_LINK => do_link(arg0 as *const i8, arg1 as *const i8),
|
||||
SYS_UNLINK => do_unlink(arg0 as *const i8),
|
||||
SYS_READLINK => do_readlink(arg0 as *const i8, arg1 as *mut u8, arg2 as usize),
|
||||
SYS_FCNTL => do_fcntl(arg0 as FileDesc, arg1 as u32, arg2 as u64),
|
||||
|
||||
// IO multiplexing
|
||||
SYS_SELECT => do_select(
|
||||
@ -119,9 +124,19 @@ pub extern "C" fn dispatch_syscall(
|
||||
arg4 as *const FdOp,
|
||||
),
|
||||
SYS_WAIT4 => do_wait4(arg0 as i32, arg1 as *mut i32),
|
||||
|
||||
SYS_GETPID => do_getpid(),
|
||||
SYS_GETTID => do_gettid(),
|
||||
SYS_GETPPID => do_getppid(),
|
||||
SYS_GETPGID => do_getpgid(),
|
||||
|
||||
SYS_GETUID => do_getuid(),
|
||||
SYS_GETGID => do_getgid(),
|
||||
SYS_GETEUID => do_geteuid(),
|
||||
SYS_GETEGID => do_getegid(),
|
||||
|
||||
SYS_RT_SIGACTION => do_rt_sigaction(),
|
||||
SYS_RT_SIGPROCMASK => do_rt_sigprocmask(),
|
||||
|
||||
SYS_CLONE => do_clone(
|
||||
arg0 as u32,
|
||||
@ -171,6 +186,11 @@ pub extern "C" fn dispatch_syscall(
|
||||
|
||||
SYS_GETTIMEOFDAY => do_gettimeofday(arg0 as *mut timeval_t),
|
||||
|
||||
SYS_UNAME => do_uname(arg0 as *mut utsname_t),
|
||||
|
||||
SYS_PRLIMIT64 => do_prlimit(arg0 as pid_t, arg1 as u32, arg2 as *const rlimit_t, arg3 as *mut rlimit_t),
|
||||
|
||||
|
||||
// socket
|
||||
SYS_SOCKET => do_socket(arg0 as c_int, arg1 as c_int, arg2 as c_int),
|
||||
SYS_CONNECT => do_connect(
|
||||
@ -658,6 +678,30 @@ fn do_getppid() -> Result<isize, Error> {
|
||||
Ok(ppid as isize)
|
||||
}
|
||||
|
||||
fn do_getpgid() -> Result<isize, Error> {
|
||||
let pgid = process::do_getpgid();
|
||||
Ok(pgid as isize)
|
||||
}
|
||||
|
||||
// TODO: implement uid, gid, euid, egid
|
||||
|
||||
fn do_getuid() -> Result<isize, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn do_getgid() -> Result<isize, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn do_geteuid() -> Result<isize, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn do_getegid() -> Result<isize, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
|
||||
fn do_pipe2(fds_u: *mut i32, flags: u32) -> Result<isize, Error> {
|
||||
check_mut_array(fds_u, 2)?;
|
||||
// TODO: how to deal with open flags???
|
||||
@ -778,6 +822,21 @@ fn do_unlink(path: *const i8) -> Result<isize, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result<isize, Error> {
|
||||
let path = clone_cstring_safely(path)?.to_string_lossy().into_owned();
|
||||
let buf = {
|
||||
check_array(buf, size)?;
|
||||
unsafe { std::slice::from_raw_parts_mut(buf, size) }
|
||||
};
|
||||
let len = fs::do_readlink(&path, buf)?;
|
||||
Ok(len as isize)
|
||||
}
|
||||
|
||||
fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result<isize, Error> {
|
||||
let cmd = FcntlCmd::from_raw(cmd, arg)?;
|
||||
fs::do_fcntl(fd, &cmd)
|
||||
}
|
||||
|
||||
fn do_arch_prctl(code: u32, addr: *mut usize) -> Result<isize, Error> {
|
||||
let code = process::ArchPrctlCode::from_u32(code)?;
|
||||
check_mut_ptr(addr)?;
|
||||
@ -1087,3 +1146,62 @@ impl AsSocket for FileRef {
|
||||
.ok_or(Error::new(Errno::EBADF, "not a socket"))
|
||||
}
|
||||
}
|
||||
|
||||
fn do_uname(name: *mut utsname_t) -> Result<isize, Error> {
|
||||
check_mut_ptr(name)?;
|
||||
let name = unsafe { &mut *name };
|
||||
misc::do_uname(name).map(|_| 0)
|
||||
}
|
||||
|
||||
fn do_prlimit(pid: pid_t, resource: u32, new_limit: *const rlimit_t, old_limit: *mut rlimit_t) -> Result<isize, Error> {
|
||||
let resource = resource_t::from_u32(resource)?;
|
||||
let new_limit = {
|
||||
if new_limit != ptr::null() {
|
||||
check_ptr(new_limit)?;
|
||||
Some(unsafe { &*new_limit })
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let old_limit = {
|
||||
if old_limit != ptr::null_mut() {
|
||||
check_mut_ptr(old_limit)?;
|
||||
Some(unsafe { &mut *old_limit })
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
};
|
||||
misc::do_prlimit(pid, resource, new_limit, old_limit).map(|_| 0)
|
||||
}
|
||||
|
||||
fn do_access(path: *const i8, mode: u32) -> Result<isize, Error> {
|
||||
let path = clone_cstring_safely(path)?.to_string_lossy().into_owned();
|
||||
let mode = AccessModes::from_u32(mode)?;
|
||||
fs::do_access(&path, mode).map(|_| 0)
|
||||
}
|
||||
|
||||
fn do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32) -> Result<isize, Error> {
|
||||
let dirfd = if dirfd >= 0 {
|
||||
Some(dirfd as FileDesc)
|
||||
} else if dirfd == AT_FDCWD {
|
||||
None
|
||||
} else {
|
||||
return errno!(EINVAL, "invalid dirfd");
|
||||
};
|
||||
let path = clone_cstring_safely(path)?.to_string_lossy().into_owned();
|
||||
let mode = AccessModes::from_u32(mode)?;
|
||||
let flags = AccessFlags::from_u32(flags)?;
|
||||
fs::do_faccessat(dirfd, &path, mode, flags).map(|_| 0)
|
||||
}
|
||||
|
||||
// TODO: implement signals
|
||||
|
||||
fn do_rt_sigaction() -> Result<isize, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn do_rt_sigprocmask() -> Result<isize, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
|
@ -3,17 +3,15 @@ use prelude::*;
|
||||
use process::{get_current, Process, ProcessRef};
|
||||
use std::fmt;
|
||||
|
||||
// TODO: Rename VMSpace to VMUniverse
|
||||
|
||||
#[macro_use]
|
||||
mod vm_range;
|
||||
mod process_vm;
|
||||
mod vm_area;
|
||||
mod vm_domain;
|
||||
mod vm_space;
|
||||
mod process_vm;
|
||||
|
||||
pub use self::process_vm::ProcessVM;
|
||||
pub use self::vm_range::{VMRange, VMRangeTrait};
|
||||
pub use self::vm_area::{VMSpace, VMDomain, VMArea, VMAreaFlags, VM_AREA_FLAG_R, VM_AREA_FLAG_W, VM_AREA_FLAG_X};
|
||||
pub use self::process_vm::ProcessVM;
|
||||
|
||||
|
||||
// TODO: separate proc and flags
|
||||
// TODO: accept fd and offset
|
||||
@ -56,23 +54,6 @@ pub fn do_brk(addr: usize) -> Result<usize, Error> {
|
||||
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VMSpace {
|
||||
range: VMRange,
|
||||
guard_type: VMGuardAreaType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct VMDomain {
|
||||
range: VMRange,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct VMArea {
|
||||
range: VMRange,
|
||||
flags: VMAreaFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum VMGuardAreaType {
|
||||
None,
|
||||
@ -80,32 +61,14 @@ pub enum VMGuardAreaType {
|
||||
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)]
|
||||
#[derive(Clone, PartialEq, Default)]
|
||||
pub struct VMAllocOptions {
|
||||
size: usize,
|
||||
addr: VMAddrOption,
|
||||
growth: Option<VMGrowthType>,
|
||||
growth: VMGrowthType,
|
||||
description: String,
|
||||
fill_zeros: bool,
|
||||
}
|
||||
|
||||
impl VMAllocOptions {
|
||||
@ -128,7 +91,17 @@ impl VMAllocOptions {
|
||||
}
|
||||
|
||||
pub fn growth(&mut self, growth: VMGrowthType) -> Result<&mut Self, Error> {
|
||||
self.growth = Some(growth);
|
||||
self.growth = growth;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn description(&mut self, description: &str) -> Result<&mut Self, Error> {
|
||||
self.description = description.to_owned();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn fill_zeros(&mut self, fill_zeros: bool) -> Result<&mut Self, Error> {
|
||||
self.fill_zeros = fill_zeros;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@ -143,15 +116,6 @@ impl fmt::Debug for VMAllocOptions {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VMAllocOptions {
|
||||
fn default() -> VMAllocOptions {
|
||||
VMAllocOptions {
|
||||
size: 0,
|
||||
addr: VMAddrOption::Any,
|
||||
growth: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum VMAddrOption {
|
||||
@ -161,6 +125,12 @@ pub enum VMAddrOption {
|
||||
Beyond(usize), // Must be greater or equal to the given address
|
||||
}
|
||||
|
||||
impl Default for VMAddrOption {
|
||||
fn default() -> VMAddrOption {
|
||||
VMAddrOption::Any
|
||||
}
|
||||
}
|
||||
|
||||
impl VMAddrOption {
|
||||
pub fn is_addr_given(&self) -> bool {
|
||||
match self {
|
||||
@ -179,18 +149,27 @@ impl VMAddrOption {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// How VMRange may grow:
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum VMGrowthType {
|
||||
Fixed,
|
||||
Upward, // e.g., mmaped regions grow upward
|
||||
Downward, // e.g., stacks grows downward
|
||||
Fixed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
impl Default for VMGrowthType {
|
||||
fn default() -> VMGrowthType {
|
||||
VMGrowthType::Fixed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct VMResizeOptions {
|
||||
new_size: usize,
|
||||
new_addr: Option<VMAddrOption>,
|
||||
new_addr: VMAddrOption,
|
||||
fill_zeros: bool,
|
||||
}
|
||||
|
||||
impl VMResizeOptions {
|
||||
@ -205,16 +184,12 @@ impl VMResizeOptions {
|
||||
}
|
||||
|
||||
pub fn addr(&mut self, new_addr: VMAddrOption) -> &mut Self {
|
||||
self.new_addr = Some(new_addr);
|
||||
self.new_addr = new_addr;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fill_zeros(&mut self, fill_zeros: bool) -> &mut Self {
|
||||
self.fill_zeros = fill_zeros;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VMResizeOptions {
|
||||
fn default() -> VMResizeOptions {
|
||||
VMResizeOptions {
|
||||
new_size: 0,
|
||||
new_addr: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ lazy_static! {
|
||||
(addr, size)
|
||||
};
|
||||
let vm_space = unsafe {
|
||||
match VMSpace::new(addr, size, VMGuardAreaType::None) {
|
||||
match VMSpace::new(addr, size, VMGuardAreaType::None, "DATA_SPACE") {
|
||||
Ok(vm_space) => vm_space,
|
||||
Err(_) => panic!("Failed to create a VMSpace"),
|
||||
}
|
||||
@ -27,11 +27,11 @@ extern "C" {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ProcessVM {
|
||||
//code_domain: VMDomain,
|
||||
data_domain: VMDomain,
|
||||
code_vma: VMArea,
|
||||
data_vma: VMArea,
|
||||
heap_vma: VMArea,
|
||||
stack_vma: VMArea,
|
||||
data_domain: Option<Box<VMDomain>>,
|
||||
code_vma: Option<Box<VMArea>>,
|
||||
data_vma: Option<Box<VMArea>>,
|
||||
heap_vma: Option<Box<VMArea>>,
|
||||
stack_vma: Option<Box<VMArea>>,
|
||||
mmap_vmas: Vec<Box<VMArea>>,
|
||||
brk: usize,
|
||||
}
|
||||
@ -44,29 +44,39 @@ impl ProcessVM {
|
||||
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,
|
||||
)?;
|
||||
// Allocate the data domain from the global data space
|
||||
let mut data_domain = {
|
||||
let data_domain_size = code_size + data_size + heap_size
|
||||
+ stack_size + mmap_size;
|
||||
let data_domain = DATA_SPACE.lock().unwrap().alloc_domain(
|
||||
data_domain_size, "data_domain")?;
|
||||
data_domain
|
||||
};
|
||||
// Allocate vmas from the data domain
|
||||
let (code_vma, data_vma, heap_vma, stack_vma) =
|
||||
match ProcessVM::alloc_vmas(&mut data_domain, code_size,
|
||||
data_size, heap_size, stack_size) {
|
||||
Err(e) => {
|
||||
// Note: we need to handle error here so that we can
|
||||
// deallocate the data domain explictly.
|
||||
DATA_SPACE.lock().unwrap().dealloc_domain(data_domain);
|
||||
return Err(e);
|
||||
},
|
||||
Ok(vmas) => vmas,
|
||||
};
|
||||
// Initial value of the program break
|
||||
let brk = heap_vma.get_start();
|
||||
// No mmapped vmas initially
|
||||
let mmap_vmas = Vec::new();
|
||||
|
||||
let vm = ProcessVM {
|
||||
data_domain,
|
||||
code_vma,
|
||||
data_vma,
|
||||
heap_vma,
|
||||
stack_vma,
|
||||
mmap_vmas,
|
||||
brk,
|
||||
data_domain: Some(Box::new(data_domain)),
|
||||
code_vma: Some(Box::new(code_vma)),
|
||||
data_vma: Some(Box::new(data_vma)),
|
||||
heap_vma: Some(Box::new(heap_vma)),
|
||||
stack_vma: Some(Box::new(stack_vma)),
|
||||
mmap_vmas: mmap_vmas,
|
||||
brk: brk,
|
||||
};
|
||||
Ok(vm)
|
||||
}
|
||||
@ -79,11 +89,13 @@ impl ProcessVM {
|
||||
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> {
|
||||
|addr: &mut usize, desc, size, flags, growth, fill_zeros| -> Result<_, Error> {
|
||||
let mut options = VMAllocOptions::new(size)?;
|
||||
options.addr(VMAddrOption::Fixed(*addr))?.growth(growth)?;
|
||||
options.addr(VMAddrOption::Fixed(*addr))?
|
||||
.growth(growth)?
|
||||
.description(desc)?
|
||||
.fill_zeros(fill_zeros)?;
|
||||
let new_vma = data_domain.alloc_area(&options, flags)?;
|
||||
*addr += size;
|
||||
Ok(new_vma)
|
||||
@ -92,39 +104,42 @@ impl ProcessVM {
|
||||
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)?;
|
||||
let code_vma = alloc_vma_continuously(&mut addr, "code_vma", code_size,
|
||||
rx_flags, VMGrowthType::Fixed, true)?;
|
||||
let data_vma = alloc_vma_continuously(&mut addr, "data_vma", data_size,
|
||||
rw_flags, VMGrowthType::Fixed, true)?;
|
||||
let heap_vma = alloc_vma_continuously(&mut addr, "heap_vma", 0,
|
||||
rw_flags, VMGrowthType::Upward, true)?;
|
||||
// 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)?;
|
||||
let stack_vma = alloc_vma_continuously(&mut addr, "stack_vma", stack_size,
|
||||
rw_flags, VMGrowthType::Downward, false)?;
|
||||
Ok((code_vma, data_vma, heap_vma, stack_vma))
|
||||
}
|
||||
|
||||
pub fn get_base_addr(&self) -> usize {
|
||||
self.code_vma.get_start()
|
||||
self.get_code_vma().get_start()
|
||||
}
|
||||
|
||||
pub fn get_code_vma(&self) -> &VMArea {
|
||||
&self.code_vma
|
||||
&self.code_vma.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_data_vma(&self) -> &VMArea {
|
||||
&self.data_vma
|
||||
&self.data_vma.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_heap_vma(&self) -> &VMArea {
|
||||
&self.heap_vma
|
||||
&self.heap_vma.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_stack_vma(&self) -> &VMArea {
|
||||
&self.stack_vma
|
||||
&self.stack_vma.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_stack_top(&self) -> usize {
|
||||
self.stack_vma.get_end()
|
||||
self.get_stack_vma().get_end()
|
||||
}
|
||||
|
||||
pub fn get_mmap_vmas(&self) -> &[Box<VMArea>] {
|
||||
@ -156,36 +171,37 @@ impl ProcessVM {
|
||||
if addr < mmap_start_addr {
|
||||
return Err(Error::new(Errno::EINVAL, "Beyond valid memory range"));
|
||||
}
|
||||
VMAddrOption::Fixed(addr)
|
||||
// TODO: Fixed or Hint? Should hanle mmap flags
|
||||
VMAddrOption::Hint(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 new_mmap_vma = self.get_data_domain_mut()
|
||||
.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.
|
||||
|
||||
pub fn munmap(&mut self, addr: usize, size: usize) -> Result<(), Error> {
|
||||
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(());
|
||||
return errno!(EINVAL, "memory area not found");
|
||||
}
|
||||
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);
|
||||
let removed_mmap_vma = self.mmap_vmas.swap_remove(mmap_vma_i);
|
||||
self.get_data_domain_mut().dealloc_area(unbox(removed_mmap_vma));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -200,43 +216,52 @@ impl ProcessVM {
|
||||
}
|
||||
|
||||
pub fn brk(&mut self, new_brk: usize) -> Result<usize, Error> {
|
||||
let (heap_start, heap_end) = {
|
||||
let heap_vma = self.heap_vma.as_ref().unwrap();
|
||||
(heap_vma.get_start(), heap_vma.get_end())
|
||||
};
|
||||
if new_brk == 0 {
|
||||
return Ok(self.get_brk());
|
||||
} else if new_brk < self.heap_vma.get_start() {
|
||||
} else if new_brk < heap_start {
|
||||
return errno!(EINVAL, "New brk address is too low");
|
||||
} else if new_brk <= self.heap_vma.get_end() {
|
||||
} else if new_brk > heap_end {
|
||||
let resize_options = {
|
||||
let new_heap_end = align_up(new_brk, PAGE_SIZE);
|
||||
let new_heap_size = new_heap_end - heap_start;
|
||||
let mut options = VMResizeOptions::new(new_heap_size)?;
|
||||
options.addr(VMAddrOption::Fixed(heap_start))
|
||||
.fill_zeros(true);
|
||||
options
|
||||
};
|
||||
let heap_vma = self.heap_vma.as_mut().unwrap();
|
||||
let data_domain = self.data_domain.as_mut().unwrap();
|
||||
data_domain.resize_area(heap_vma, &resize_options)?;
|
||||
}
|
||||
self.brk = new_brk;
|
||||
return Ok(new_brk);
|
||||
}
|
||||
|
||||
// TODO: init the memory with zeros for the expanded area
|
||||
let resize_options = {
|
||||
let brk_start = self.get_brk_start();
|
||||
let new_heap_size = align_up(new_brk, 4096) - 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)
|
||||
fn get_data_domain_mut(&mut self) -> &mut Box<VMDomain> {
|
||||
self.data_domain.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
{
|
||||
let data_domain = self.data_domain.as_mut().unwrap();
|
||||
data_domain.dealloc_area(unbox(self.code_vma.take().unwrap()));
|
||||
data_domain.dealloc_area(unbox(self.data_vma.take().unwrap()));
|
||||
data_domain.dealloc_area(unbox(self.heap_vma.take().unwrap()));
|
||||
data_domain.dealloc_area(unbox(self.stack_vma.take().unwrap()));
|
||||
for mmap_vma in self.mmap_vmas.drain(..) {
|
||||
data_domain.dealloc_area(unbox(mmap_vma));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the domain from its parent space
|
||||
DATA_SPACE.lock().unwrap().dealloc_domain(data_domain);
|
||||
DATA_SPACE.lock().unwrap().dealloc_domain(
|
||||
unbox(self.data_domain.take().unwrap()));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,93 @@
|
||||
use super::*;
|
||||
|
||||
impl super::VMArea {
|
||||
#[derive(Debug)]
|
||||
pub struct VMSpace {
|
||||
range: VMRange,
|
||||
guard_type: VMGuardAreaType,
|
||||
}
|
||||
|
||||
impl_vmrange_trait_for!(VMSpace, range);
|
||||
|
||||
impl VMSpace {
|
||||
pub unsafe fn new(
|
||||
addr: usize,
|
||||
size: usize,
|
||||
guard_type: VMGuardAreaType,
|
||||
desc: &str,
|
||||
) -> Result<VMSpace, Error> {
|
||||
let addr = align_up(addr, PAGE_SIZE);
|
||||
let size = align_down(size, PAGE_SIZE);
|
||||
let range = unsafe { VMRange::new(addr, addr + size, VMGrowthType::Fixed, desc)? };
|
||||
Ok(VMSpace { range, guard_type })
|
||||
}
|
||||
|
||||
pub fn get_guard_type(&self) -> VMGuardAreaType {
|
||||
self.guard_type
|
||||
}
|
||||
|
||||
pub fn alloc_domain(&mut self, size: usize, desc: &str) -> Result<VMDomain, Error> {
|
||||
let mut options = VMAllocOptions::new(size)?;
|
||||
options.growth(VMGrowthType::Upward)?
|
||||
.description(desc)?;
|
||||
|
||||
let new_range = self.range.alloc_subrange(&options)?;
|
||||
Ok(VMDomain { range: new_range })
|
||||
}
|
||||
|
||||
pub fn dealloc_domain(&mut self, mut domain: 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VMDomain {
|
||||
range: VMRange,
|
||||
}
|
||||
|
||||
impl_vmrange_trait_for!(VMDomain, range);
|
||||
|
||||
impl VMDomain {
|
||||
pub fn alloc_area(
|
||||
&mut self,
|
||||
options: &VMAllocOptions,
|
||||
flags: VMAreaFlags,
|
||||
) -> Result<VMArea, Error> {
|
||||
let new_range = self.range.alloc_subrange(options)?;
|
||||
Ok(VMArea {
|
||||
range: new_range,
|
||||
flags: flags,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn dealloc_area(&mut self, mut area: 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VMArea {
|
||||
range: VMRange,
|
||||
flags: VMAreaFlags,
|
||||
}
|
||||
|
||||
impl_vmrange_trait_for!(VMArea, range);
|
||||
|
||||
impl VMArea {
|
||||
pub fn get_flags(&self) -> &VMAreaFlags {
|
||||
&self.flags
|
||||
}
|
||||
@ -9,3 +96,25 @@ impl super::VMArea {
|
||||
&mut self.flags
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
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> {
|
||||
// TODO: init memory with zeros when expanding!
|
||||
self.range.resize_subrange(&mut area.range, options)
|
||||
}
|
||||
}
|
||||
|
||||
#[link(name = "sgx_tstdc")]
|
||||
extern "C" {
|
||||
pub fn memset(p: *mut c_void, c: c_int, n: size_t) -> *mut c_void;
|
||||
}
|
@ -35,132 +35,61 @@ macro_rules! impl_vmrange_trait_for {
|
||||
};
|
||||
}
|
||||
|
||||
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>>,
|
||||
is_dealloced: bool,
|
||||
description: String,
|
||||
}
|
||||
|
||||
impl_vmrange_trait_for!(VMRange, inner);
|
||||
|
||||
impl VMRange {
|
||||
pub unsafe fn new(start: usize, end: usize, growth: VMGrowthType) -> Result<VMRange, Error> {
|
||||
pub unsafe fn new(start: usize, end: usize, growth: VMGrowthType, description: &str) -> 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,
|
||||
is_dealloced: false,
|
||||
description: description.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
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);
|
||||
debug_assert!(!self.is_dealloced);
|
||||
|
||||
// Lazy initialize the subrange array upon the first allocation
|
||||
if !self.has_subranges() {
|
||||
self.init_subranges()?;
|
||||
if self.sub_ranges.is_none() {
|
||||
self.init_subrange_array()?;
|
||||
}
|
||||
|
||||
// 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];
|
||||
// Find a free space that satisfies the options
|
||||
let free_space = self.look_for_free_space(options)?;
|
||||
// Allocate a new subrange from the free space
|
||||
let (new_subrange_idx, new_subrange_inner) = {
|
||||
let (new_subrange_start, new_subrange_end) =
|
||||
self.alloc_from_free_space(&free_space, options);
|
||||
debug_assert!(free_space.contains(new_subrange_start));
|
||||
debug_assert!(free_space.contains(new_subrange_end));
|
||||
|
||||
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
|
||||
(free_space.index_in_subranges, VMRangeInner::new(
|
||||
new_subrange_start, new_subrange_end, options.growth))
|
||||
};
|
||||
|
||||
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);
|
||||
.insert(new_subrange_idx, new_subrange_inner);
|
||||
|
||||
if options.fill_zeros {
|
||||
// Init the memory area with all zeros
|
||||
unsafe {
|
||||
let mem_ptr = new_subrange_inner.get_start() as *mut c_void;
|
||||
let mem_size = new_subrange_inner.get_size() as size_t;
|
||||
memset(mem_ptr, 0 as c_int, mem_size);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -169,29 +98,28 @@ impl VMRange {
|
||||
// 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,
|
||||
is_dealloced: false,
|
||||
description: options.description.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
debug_assert!(!self.is_dealloced);
|
||||
debug_assert!(!subrange.is_dealloced);
|
||||
debug_assert!(self.sub_ranges.is_some());
|
||||
|
||||
// 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();
|
||||
subrange.inner.end = subrange.inner.start;
|
||||
subrange.is_dealloced = true;
|
||||
}
|
||||
|
||||
pub fn resize_subrange(
|
||||
@ -199,7 +127,9 @@ impl VMRange {
|
||||
subrange: &mut VMRange,
|
||||
options: &VMResizeOptions,
|
||||
) -> Result<(), Error> {
|
||||
self.ensure_subrange_is_a_child(subrange);
|
||||
debug_assert!(!self.is_dealloced);
|
||||
debug_assert!(!subrange.is_dealloced);
|
||||
debug_assert!(self.sub_ranges.is_some());
|
||||
|
||||
// Get valid parameters from options
|
||||
let new_size = options.new_size;
|
||||
@ -219,11 +149,15 @@ impl VMRange {
|
||||
}
|
||||
// Grow
|
||||
else {
|
||||
self.grow_subrange_to(subrange, new_size)
|
||||
self.grow_subrange_to(subrange, new_size, options.fill_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
fn init_subranges(&mut self) -> Result<(), Error> {
|
||||
pub fn get_description(&self) -> &str {
|
||||
&self.description
|
||||
}
|
||||
|
||||
fn init_subrange_array(&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();
|
||||
@ -234,11 +168,126 @@ impl VMRange {
|
||||
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");
|
||||
}*/
|
||||
// Find a free space for allocating a sub VMRange
|
||||
fn look_for_free_space(&mut self, options: &VMAllocOptions) -> Result<FreeSpace, Error> {
|
||||
// TODO: reduce the complexity from O(N) to O(log(N)), where N is
|
||||
// the number of existing subranges.
|
||||
|
||||
// Get valid parameters from options
|
||||
let size = options.size;
|
||||
let addr = options.addr;
|
||||
let growth = options.growth;
|
||||
|
||||
// Record the minimal free space that satisfies the options
|
||||
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 (free_range_start, free_range_end)= {
|
||||
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)
|
||||
};
|
||||
let mut free_space = 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,
|
||||
),
|
||||
};
|
||||
|
||||
match addr {
|
||||
// Want a minimal free_space
|
||||
VMAddrOption::Any => { },
|
||||
// Prefer to have free_space.start == addr
|
||||
VMAddrOption::Hint(addr) => {
|
||||
if free_space.contains(addr) {
|
||||
if free_space.end - addr >= size {
|
||||
free_space.start = addr;
|
||||
return Ok(free_space);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Must have free_space.start == addr
|
||||
VMAddrOption::Fixed(addr) => {
|
||||
if !free_space.contains(addr) {
|
||||
continue;
|
||||
}
|
||||
if free_space.end - addr < size {
|
||||
return errno!(ENOMEM, "not enough memory");
|
||||
}
|
||||
free_space.start = addr;
|
||||
return Ok(free_space);
|
||||
}
|
||||
// Must have free_space.start >= addr
|
||||
VMAddrOption::Beyond(addr) => {
|
||||
if free_space.end < addr {
|
||||
continue;
|
||||
}
|
||||
if free_space.contains(addr) {
|
||||
free_space.start = addr;
|
||||
if free_space.get_size() < size {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if min_big_enough_free_space == None ||
|
||||
free_space < *min_big_enough_free_space.as_ref().unwrap() {
|
||||
min_big_enough_free_space = Some(free_space);
|
||||
}
|
||||
}
|
||||
|
||||
min_big_enough_free_space
|
||||
.ok_or_else(|| Error::new(Errno::ENOMEM, "not enough space"))
|
||||
}
|
||||
|
||||
fn alloc_from_free_space(&self, free_space: &FreeSpace, options: &VMAllocOptions) -> (usize, usize) {
|
||||
// Get valid parameters from options
|
||||
let size = options.size;
|
||||
let addr_option = options.addr;
|
||||
let growth = options.growth;
|
||||
|
||||
if let VMAddrOption::Fixed(addr) = addr_option {
|
||||
return (addr, addr + size);
|
||||
}
|
||||
else if let VMAddrOption::Hint(addr) = addr_option {
|
||||
if free_space.start == addr {
|
||||
return (addr, addr + size);
|
||||
}
|
||||
}
|
||||
|
||||
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 addr = 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.
|
||||
let offset = align_down((free_space.get_size() - size) / 2, PAGE_SIZE);
|
||||
free_space.start + offset
|
||||
};
|
||||
|
||||
(addr, addr + size)
|
||||
}
|
||||
|
||||
fn position_subrange(&self, subrange: &VMRange) -> usize {
|
||||
@ -257,10 +306,6 @@ impl VMRange {
|
||||
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();
|
||||
@ -304,51 +349,61 @@ impl VMRange {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn grow_subrange_to(&mut self, subrange: &mut VMRange, new_size: usize) -> Result<(), Error> {
|
||||
fn grow_subrange_to(&mut self, subrange: &mut VMRange, new_size: usize, fill_zeros: bool) -> Result<(), Error> {
|
||||
let subrange_i = self.position_subrange(subrange);
|
||||
let subranges = self.get_subranges_mut();
|
||||
|
||||
let subrange_old_start = subrange.inner.start;
|
||||
let subrange_old_end = subrange.inner.end;
|
||||
let subrange_old_size = subrange.get_size();
|
||||
|
||||
if subrange.inner.growth == VMGrowthType::Upward {
|
||||
// Can we grow?
|
||||
// Can we grow upward?
|
||||
let max_new_size = {
|
||||
let next_subrange = &subranges[subrange_i + 1];
|
||||
next_subrange.start - subrange.inner.start
|
||||
next_subrange.start - subrange_old_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;
|
||||
let subrange_new_end = subrange_old_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?
|
||||
// Init memory
|
||||
if fill_zeros {
|
||||
unsafe {
|
||||
let mem_ptr = subrange_old_end as *mut c_void;
|
||||
let mem_size = (subrange_new_end - subrange_old_end) as size_t;
|
||||
memset(mem_ptr, 0 as c_int, mem_size);
|
||||
}
|
||||
}
|
||||
} else { // self.growth == VMGrowthType::Downward
|
||||
// Can we grow downard?
|
||||
let max_new_size = {
|
||||
let pre_subrange = &subranges[subrange_i - 1];
|
||||
subrange.inner.end - pre_subrange.end
|
||||
subrange_old_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;
|
||||
let subrange_new_start = subrange_old_end - new_size;
|
||||
subrange.inner.start = subrange_new_start;
|
||||
// Sync state
|
||||
subranges[subrange_i].start = subrange_new_start;
|
||||
// Init memory
|
||||
if fill_zeros {
|
||||
unsafe {
|
||||
let mem_ptr = subrange_new_start as *mut c_void;
|
||||
let mem_size = (subrange_old_start - subrange_new_start) as size_t;
|
||||
memset(mem_ptr, 0 as c_int, mem_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
@ -365,29 +420,15 @@ impl PartialEq for VMRange {
|
||||
|
||||
impl Drop for VMRange {
|
||||
fn drop(&mut self) {
|
||||
if !self.is_dealloced() {
|
||||
println!("VMRange::drop::panic1");
|
||||
if !self.is_dealloced {
|
||||
panic!("A range must be dealloc'ed before drop");
|
||||
}
|
||||
if self.has_subranges() {
|
||||
println!("VMRange::drop::panic2");
|
||||
panic!("All sub-ranges must be removed explicitly before drop");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for VMRange {}
|
||||
unsafe impl Sync for VMRange {}
|
||||
|
||||
impl Default for VMRange {
|
||||
fn default() -> VMRange {
|
||||
VMRange {
|
||||
inner: VMRangeInner::new(0, 0, VMGrowthType::Fixed),
|
||||
parent_range: 0 as *const VMRange,
|
||||
sub_ranges: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct VMRangeInner {
|
||||
@ -398,6 +439,8 @@ pub struct VMRangeInner {
|
||||
|
||||
impl VMRangeInner {
|
||||
pub fn new(start: usize, end: usize, growth: VMGrowthType) -> VMRangeInner {
|
||||
debug_assert!(start % PAGE_SIZE == 0);
|
||||
debug_assert!(end % PAGE_SIZE == 0);
|
||||
VMRangeInner {
|
||||
start: start,
|
||||
end: end,
|
||||
@ -478,9 +521,14 @@ impl FreeSpace {
|
||||
pressure += if self.may_neighbor_grow.1 { 1 } else { 0 };
|
||||
pressure
|
||||
}
|
||||
|
||||
fn get_size(&self) -> usize {
|
||||
self.end - self.start
|
||||
}
|
||||
|
||||
fn contains(&self, addr: usize) -> bool {
|
||||
self.start <= addr && addr < self.end
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FreeSpace {
|
||||
@ -512,3 +560,8 @@ impl PartialOrd for FreeSpace {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[link(name = "sgx_tstdc")]
|
||||
extern "C" {
|
||||
pub fn memset(p: *mut c_void, c: c_int, n: size_t) -> *mut c_void;
|
||||
}
|
||||
|
@ -1,33 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ PROJECT_DIR := $(realpath $(CUR_DIR)/../)
|
||||
# Dependencies: need to be compiled but not to run by any Makefile target
|
||||
TEST_DEPS := dev_null
|
||||
# Tests: need to be compiled and run by test-% target
|
||||
TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link tls pthread client server
|
||||
TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link tls pthread uname rlimit client server
|
||||
# Benchmarks: need to be compiled and run by bench-% target
|
||||
BENCHES := spawn_and_exit_latency pipe_throughput
|
||||
|
||||
|
5
test/rlimit/Makefile
Normal file
5
test/rlimit/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
include ../test_common.mk
|
||||
|
||||
EXTRA_C_FLAGS :=
|
||||
EXTRA_LINK_FLAGS :=
|
||||
BIN_ARGS :=
|
15
test/rlimit/main.c
Normal file
15
test/rlimit/main.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include <sys/resource.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
struct rlimit rlim;
|
||||
if (getrlimit(RLIMIT_AS, &rlim) < 0) {
|
||||
printf("ERROR: getrlimit failed\n");
|
||||
return -1;
|
||||
}
|
||||
if (setrlimit(RLIMIT_AS, &rlim) < 0) {
|
||||
printf("ERROR: getrlimit failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -21,6 +21,11 @@ int main(int argc, const char* argv[]) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (access(FILE_NAME, F_OK) < 0) {
|
||||
printf("cannot access the new file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ftruncate(fd, TRUNC_LEN);
|
||||
if (ret < 0) {
|
||||
printf("failed to ftruncate the file\n");
|
||||
|
5
test/uname/Makefile
Normal file
5
test/uname/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
include ../test_common.mk
|
||||
|
||||
EXTRA_C_FLAGS :=
|
||||
EXTRA_LINK_FLAGS :=
|
||||
BIN_ARGS :=
|
14
test/uname/main.c
Normal file
14
test/uname/main.c
Normal file
@ -0,0 +1,14 @@
|
||||
#include <sys/utsname.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
struct utsname name;
|
||||
uname(&name);
|
||||
printf("sysname = %s\n", (const char*)&name.sysname);
|
||||
printf("nodename = %s\n", (const char*)&name.nodename);
|
||||
printf("release = %s\n", (const char*)&name.release);
|
||||
printf("version = %s\n", (const char*)&name.version);
|
||||
printf("machine = %s\n", (const char*)&name.machine);
|
||||
printf("domainname = %s\n", (const char*)&name.__domainname);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user