feature: passing args to execve()

This commit is contained in:
Mark Poliakov 2021-11-24 15:16:34 +02:00
parent 7f939543fe
commit 47b67fa93c
13 changed files with 228 additions and 53 deletions

View File

@ -59,6 +59,6 @@ pub extern "C" fn init_fn(_arg: usize) -> ! {
drop(cfg);
Process::execve(|space| elf::load_elf(space, file), 0).unwrap();
Process::execve(|space| elf::load_elf(space, file), &["/init"]).unwrap();
panic!("Unreachable");
}

View File

@ -210,6 +210,23 @@ impl Space {
}
}
// TODO extract attributes
pub fn translate(&mut self, virt: usize) -> Result<usize, Errno> {
let l0i = virt >> 30;
let l1i = (virt >> 21) & 0x1FF;
let l2i = (virt >> 12) & 0x1FF;
let l1_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l2i];
if entry.is_present() {
Ok(unsafe { entry.address_unchecked() })
} else {
Err(Errno::DoesNotExist)
}
}
/// Attempts to resolve a page fault at `virt` address by copying the
/// underlying Copy-on-Write mapping (if any is present)
pub fn try_cow_copy(&mut self, virt: usize) -> Result<(), Errno> {

View File

@ -10,13 +10,16 @@ use crate::proc::{
};
use crate::sync::{IrqSafeSpinLock, IrqSafeSpinLockGuard};
use alloc::{rc::Rc, vec::Vec};
use core::alloc::Layout;
use core::sync::atomic::{AtomicU32, Ordering};
use libsys::{
error::Errno,
proc::{ExitCode, Pid},
signal::Signal,
mem::memcpy,
ProgramArgs,
};
use vfs::{VnodeRef, VnodeKind};
use vfs::{VnodeKind, VnodeRef};
/// Wrapper type for a process struct reference
pub type ProcessRef = Rc<Process>;
@ -134,8 +137,7 @@ impl Process {
/// Sets a pending signal for a process
pub fn set_signal(&self, signal: Signal) {
let mut lock = self.inner.lock();
let ttbr0 =
lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
let ttbr0 = lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
let main_thread = Thread::get(lock.threads[0]).unwrap();
drop(lock);
@ -164,8 +166,7 @@ impl Process {
pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
let mut lock = self.inner.lock();
let ttbr0 =
lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
let ttbr0 = lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
thread.enter_signal(signal, ttbr0);
}
@ -210,7 +211,7 @@ impl Process {
id: dst_id,
pgid: src_inner.pgid,
ppid: Some(src_inner.id),
sid: src_inner.sid
sid: src_inner.sid,
}),
});
@ -319,10 +320,96 @@ impl Process {
}
}
fn write_paged<T>(space: &mut Space, dst: usize, src: T) -> Result<(), Errno> {
let size = core::mem::size_of::<T>();
if (size + (dst % 4096)) > 4096 {
todo!("Object crossed page boundary");
}
let page_virt = dst & !4095;
let page_phys = if let Ok(phys) = space.translate(dst) {
phys
} else {
let page = phys::alloc_page(PageUsage::UserPrivate)?;
let flags = MapAttributes::SH_OUTER |
MapAttributes::NOT_GLOBAL |
MapAttributes::UXN |
MapAttributes::PXN |
MapAttributes::AP_BOTH_READONLY;
space.map(page_virt, page, flags)?;
page
};
unsafe {
core::ptr::write((mem::virtualize(page_phys) + (dst % 4096)) as *mut T, src);
}
Ok(())
}
fn write_paged_bytes(space: &mut Space, dst: usize, src: &[u8]) -> Result<(), Errno> {
if (src.len() + (dst % 4096)) > 4096 {
todo!("Object crossed page boundary");
}
let page_virt = dst & !4095;
let page_phys = if let Ok(phys) = space.translate(dst) {
phys
} else {
let page = phys::alloc_page(PageUsage::UserPrivate)?;
let flags = MapAttributes::SH_OUTER |
MapAttributes::NOT_GLOBAL |
MapAttributes::UXN |
MapAttributes::PXN |
MapAttributes::AP_BOTH_READONLY;
space.map(page_virt, page, flags)?;
page
};
unsafe {
memcpy((mem::virtualize(page_phys) + (dst % 4096)) as *mut u8, src.as_ptr(), src.len());
}
Ok(())
}
fn store_arguments(space: &mut Space, argv: &[&str]) -> Result<usize, Errno> {
let mut offset = 0usize;
// TODO vmalloc?
let base = 0x60000000;
// 1. Store program argument string bytes
for arg in argv.iter() {
Self::write_paged_bytes(space, base + offset, arg.as_bytes())?;
offset += arg.len();
}
// Align
offset = (offset + 15) & !15;
let argv_offset = offset;
// 2. Store arg pointers
let mut data_offset = 0usize;
for arg in argv.iter() {
// XXX this is really unsafe and I am not really sure ABI will stay like this XXX
Self::write_paged(space, base + offset + 0, base + data_offset)?;
Self::write_paged(space, base + offset + 8, arg.len())?;
offset += 16;
data_offset += arg.len();
}
// 3. Store ProgramArgs
let data = ProgramArgs {
argc: argv.len(),
argv: base + argv_offset,
storage: base,
size: offset + core::mem::size_of::<ProgramArgs>()
};
Self::write_paged(space, base + offset, data)?;
Ok(base + offset)
}
/// Loads a new program into current process address space
pub fn execve<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
loader: F,
arg: usize,
argv: &[&str],
) -> Result<(), Errno> {
unsafe {
// Run with interrupts disabled
@ -351,12 +438,6 @@ impl Process {
process_lock.sid = new_pid;
let r = processes.insert(new_pid, proc.clone());
assert!(r.is_none());
} else {
// Invalidate user ASID
let input = (process_lock.id.asid() as usize) << 48;
unsafe {
asm!("tlbi aside1, {}", in(reg) input);
}
}
thread.set_owner(process_lock.id);
@ -380,6 +461,7 @@ impl Process {
}
let entry = loader(new_space)?;
let arg = Self::store_arguments(new_space, argv)?;
debugln!("Will now enter at {:#x}", entry);
// TODO drop old address space
@ -388,11 +470,13 @@ impl Process {
unsafe {
// TODO drop old context
let ctx = thread.ctx.get();
let asid = (process_lock.id.asid() as usize) << 48;
asm!("tlbi aside1, {}", in(reg) asid);
ctx.write(Context::user(
entry,
arg,
new_space_phys | ((process_lock.id.asid() as usize) << 48),
new_space_phys | asid,
Self::USTACK_VIRT_TOP,
));

View File

@ -62,6 +62,19 @@ pub fn struct_mut<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })
}
pub fn struct_buf_ref<'a, T>(base: usize, count: usize) -> Result<&'a [T], Errno> {
let layout = Layout::array::<T>(count).unwrap();
if base % layout.align() != 0 {
invalid_memory!(
"Structure pointer is misaligned: base={:#x}, expected {:?}",
base,
layout
);
}
let bytes = buf_ref(base, layout.size())?;
Ok(unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const T, count) })
}
pub fn struct_buf_mut<'a, T>(base: usize, count: usize) -> Result<&'a mut [T], Errno> {
let layout = Layout::array::<T>(count).unwrap();
if base % layout.align() != 0 {
@ -91,7 +104,7 @@ pub fn option_struct_mut<'a, T>(base: usize) -> Result<Option<&'a mut T>, Errno>
}
}
fn validate_ptr(base: usize, len: usize, write: bool) -> Result<(), Errno> {
pub fn validate_ptr(base: usize, len: usize, write: bool) -> Result<(), Errno> {
if base > mem::KERNEL_OFFSET || base + len > mem::KERNEL_OFFSET {
invalid_memory!(
"User region refers to kernel memory: base={:#x}, len={:#x}",
@ -111,8 +124,7 @@ fn validate_ptr(base: usize, len: usize, write: bool) -> Result<(), Errno> {
space.try_cow_copy(i * mem::PAGE_SIZE)
})
} else {
todo!();
// Err(Errno::DoesNotExist)
Err(Errno::DoesNotExist)
};
if res.is_ok() {

View File

@ -54,7 +54,6 @@ fn find_at_node<T: DerefMut<Target = ProcessIo>>(
/// Main system call dispatcher function
pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
// debugln!("syscall {:?}", num);
match num {
// I/O
SystemCall::Read => {
@ -165,17 +164,22 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
.map(|e| e as usize)
}
SystemCall::Exec => {
let filename = arg::str_ref(args[0], args[1])?;
let argv = arg::struct_buf_ref::<&str>(args[2], args[3])?;
// Validate each argument as well
for item in argv.iter() {
arg::validate_ptr(item.as_ptr() as usize, item.len(), false)?;
}
let node = {
let proc = Process::current();
let mut io = proc.io.lock();
let filename = arg::str_ref(args[0], args[1])?;
// TODO argv, envp array passing ABI?
let node = io.ioctx().find(None, filename, true)?;
drop(io);
node
};
let file = node.open(OpenFlags::O_RDONLY)?;
Process::execve(move |space| elf::load_elf(space, file), 0).unwrap();
Process::execve(move |space| elf::load_elf(space, file), argv).unwrap();
panic!();
}
SystemCall::Exit => {

View File

@ -204,12 +204,14 @@ pub unsafe fn sys_fork() -> Result<Option<Pid>, Errno> {
///
/// System call
#[inline(always)]
pub fn sys_execve(pathname: &str) -> Result<(), Errno> {
pub fn sys_execve(pathname: &str, argv: &[&str]) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
SystemCall::Exec,
argp!(pathname.as_ptr()),
argn!(pathname.len())
argn!(pathname.len()),
argp!(argv.as_ptr()),
argn!(argv.len())
)
})
}

View File

@ -15,6 +15,14 @@ pub mod stat;
pub mod termios;
pub mod traits;
#[derive(Debug)]
pub struct ProgramArgs {
pub argv: usize,
pub argc: usize,
pub storage: usize,
pub size: usize
}
#[cfg(feature = "user")]
pub mod calls;
#[cfg(feature = "user")]

View File

@ -13,7 +13,7 @@ pub fn read_le16(src: &[u8]) -> u16 {
/// Unsafe: writes to arbitrary memory locations, performs no pointer
/// validation.
#[no_mangle]
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *mut u8, mut len: usize) -> *mut u8 {
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *const u8, mut len: usize) -> *mut u8 {
while len != 0 {
len -= 1;
*dst.add(len) = *src.add(len);

21
libusr/src/env.rs Normal file
View File

@ -0,0 +1,21 @@
use libsys::ProgramArgs;
use alloc::vec::Vec;
use crate::trace;
static mut PROGRAM_ARGS: Vec<&'static str> = Vec::new();
pub fn args() -> &'static [&'static str] {
unsafe { &PROGRAM_ARGS }
}
pub(crate) unsafe fn setup_env(arg: &ProgramArgs) {
for i in 0..arg.argc {
let base = core::ptr::read((arg.argv + i * 16 + 0) as *const *const u8);
let len = core::ptr::read((arg.argv + i * 16 + 8) as *const usize);
let string = core::str::from_utf8(core::slice::from_raw_parts(base, len)).unwrap();
PROGRAM_ARGS.push(string);
}
trace!("args = {:?}", PROGRAM_ARGS);
}

View File

@ -1,15 +1,16 @@
#![feature(asm, alloc_error_handler)]
#![no_std]
use core::panic::PanicInfo;
use libsys::proc::ExitCode;
#[macro_use]
extern crate lazy_static;
extern crate alloc;
use core::panic::PanicInfo;
use libsys::{ProgramArgs, proc::ExitCode};
mod allocator;
pub mod env;
pub mod file;
pub mod io;
pub mod os;
@ -18,16 +19,16 @@ pub mod sync;
pub mod thread;
pub mod signal;
#[link_section = ".text._start"]
#[no_mangle]
extern "C" fn _start(_arg: usize) -> ! {
extern "C" fn _start(arg: &'static ProgramArgs) -> ! {
extern "Rust" {
fn main() -> i32;
}
unsafe {
thread::init_main();
env::setup_env(arg);
}
let res = unsafe { main() };

View File

@ -20,7 +20,7 @@ fn main() -> i32 {
}
}
} else {
libusr::sys::sys_execve("/bin/shell").unwrap();
libusr::sys::sys_execve("/bin/shell", &["/bin/shell"]).unwrap();
loop {}
}
}

View File

@ -6,24 +6,33 @@ extern crate libusr;
#[macro_use]
extern crate alloc;
use libusr::sys::{sys_readdir, sys_openat, sys_close, sys_fstatat, stat::{FileMode, OpenFlags, DirectoryEntry, Stat}};
use alloc::{string::String, borrow::ToOwned};
use alloc::{borrow::ToOwned, string::String};
use libusr::sys::{
stat::{DirectoryEntry, FileMode, OpenFlags, Stat},
sys_close, sys_fstatat, sys_openat, sys_readdir, Errno,
};
#[no_mangle]
fn main() -> i32 {
let mut buffer = [DirectoryEntry::empty(); 16];
fn list_directory(path: &str) -> Result<(), Errno> {
let mut buffer = [DirectoryEntry::empty(); 8];
let mut stat = Stat::default();
let mut data = vec![];
let fd = sys_openat(None, "/", FileMode::default_dir(), OpenFlags::O_DIRECTORY | OpenFlags::O_RDONLY).unwrap();
let fd = sys_openat(
None,
path,
FileMode::default_dir(),
OpenFlags::O_DIRECTORY | OpenFlags::O_RDONLY,
)?;
loop {
let count = sys_readdir(fd, &mut buffer).unwrap();
let count = sys_readdir(fd, &mut buffer)?;
if count == 0 {
break;
}
buffer.iter().take(count).for_each(|e| data.push(e.as_str().to_owned()));
buffer.iter().take(count).for_each(|e| {
data.push(e.as_str().to_owned());
});
}
data.sort();
@ -38,8 +47,27 @@ fn main() -> i32 {
println!("{}", item);
});
sys_close(fd).unwrap();
0
sys_close(fd)
}
#[no_mangle]
fn main() -> i32 {
let args = libusr::env::args();
let mut res = 0;
if args.len() == 1 {
if let Err(e) = list_directory(".") {
eprintln!("{}: {:?}", ".", e);
res = -1;
}
} else {
for arg in &args[1..] {
if let Err(e) = list_directory(arg) {
eprintln!("{}: {:?}", arg, e);
res = -1;
}
}
}
res
}

View File

@ -5,7 +5,7 @@
extern crate libusr;
extern crate alloc;
use alloc::borrow::ToOwned;
use alloc::{borrow::ToOwned, vec::Vec};
use libusr::io::{self, Read};
use libusr::signal::{self, SignalHandler};
use libusr::sys::{
@ -26,16 +26,10 @@ fn readline<'a, F: Read>(f: &mut F, bytes: &'a mut [u8]) -> Result<Option<&'a st
})
}
fn execvp(cmd: &str) -> ! {
let pgid = sys_setpgid(unsafe { Pid::from_raw(0) }, unsafe { Pid::from_raw(0) }).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
sys_execve(&("/bin/".to_owned() + cmd)).unwrap();
sys_exit(ExitCode::from(-1));
}
fn execute(line: &str) -> Result<ExitCode, Errno> {
let mut words = line.split(' ');
let cmd = words.next().unwrap();
// TODO proper arg handling
let args: Vec<&str> = line.split(' ').collect();
let cmd = args[0];
if let Some(pid) = unsafe { sys_fork()? } {
let mut status = 0;
@ -44,7 +38,11 @@ fn execute(line: &str) -> Result<ExitCode, Errno> {
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
Ok(ExitCode::from(status))
} else {
execvp(cmd);
let pgid = sys_setpgid(unsafe { Pid::from_raw(0) }, unsafe { Pid::from_raw(0) }).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
let filename = "/bin/".to_owned() + cmd;
sys_execve(&filename, &args).unwrap();
sys_exit(ExitCode::from(-1));
}
}