Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read command line args from userspace programs #351

Merged
merged 11 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ jobs:
- run: sudo apt-get -qqy update
- run: sudo apt-get -qqy install qemu-system-x86
- run: make
- run: make user-rust
- run: make test
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ user-rust:
-C relocation-model=static
basename -s .rs src/bin/*.rs | xargs -I {} \
cp target/x86_64-moros/release/{} dsk/bin/{}
strip dsk/bin/*

bin = target/x86_64-moros/release/bootimage-moros.bin
img = disk.img
Expand Down
Binary file modified dsk/bin/clear
Binary file not shown.
Binary file modified dsk/bin/halt
Binary file not shown.
Binary file modified dsk/bin/hello
Binary file not shown.
Binary file added dsk/bin/print
Binary file not shown.
Binary file modified dsk/bin/reboot
Binary file not shown.
Binary file modified dsk/bin/sleep
Binary file not shown.
4 changes: 2 additions & 2 deletions src/api/process.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::api::syscall;

pub fn spawn(path: &str) -> Result<(), ()> {
pub fn spawn(path: &str, args: &[&str]) -> Result<(), ()> {
if syscall::info(path).is_some() {
return syscall::spawn(path);
return syscall::spawn(path, args);
}
Err(())
}
18 changes: 12 additions & 6 deletions src/api/syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,23 +80,29 @@ pub fn close(handle: usize) {
unsafe { syscall!(CLOSE, handle as usize) };
}

pub fn spawn(path: &str) -> Result<(), ()> {
let ptr = path.as_ptr() as usize;
let len = path.len() as usize;
let res = unsafe { syscall!(SPAWN, ptr, len) } as isize;
pub fn spawn(path: &str, args: &[&str]) -> Result<(), ()> {
let path_ptr = path.as_ptr() as usize;
let path_len = path.len() as usize;
let args_ptr = args.as_ptr() as usize;
let args_len = args.len() as usize;
let res = unsafe { syscall!(SPAWN, path_ptr, path_len, args_ptr, args_len) } as isize;
if res.is_negative() {
Err(())
} else {
Ok(())
}
}

pub fn stop(code: usize) {
unsafe { syscall!(STOP, code) };
}

pub fn reboot() {
unsafe { syscall!(STOP, 0xcafe) };
stop(0xcafe);
}

pub fn halt() {
unsafe { syscall!(STOP, 0xdead) };
stop(0xdead);
}

#[test_case]
Expand Down
30 changes: 30 additions & 0 deletions src/bin/print.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![no_std]
#![no_main]

use moros::api::syscall;
use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
syscall::write(1, b"An exception occured!\n");
loop {}
}

#[no_mangle]
pub unsafe extern "sysv64" fn _start(args_ptr: u64, args_len: usize) {
let args = core::slice::from_raw_parts(args_ptr as *const _, args_len);
let code = main(args);
syscall::exit(code);
}

fn main(args: &[&str]) -> usize {
let n = args.len();
for i in 1..n {
syscall::write(1, args[i].as_bytes());
if i < n - 1 {
syscall::write(1, b" ");
}
}
syscall::write(1, b"\n");
0
}
28 changes: 28 additions & 0 deletions src/bin/sleep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#![no_std]
#![no_main]

use moros::api::syscall;
use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
syscall::write(1, b"An exception occured!\n");
loop {}
}

#[no_mangle]
pub unsafe extern "sysv64" fn _start(args_ptr: u64, args_len: usize) {
let args = core::slice::from_raw_parts(args_ptr as *const _, args_len);
let code = main(args);
syscall::exit(code);
}

fn main(args: &[&str]) -> usize {
if args.len() == 2 {
if let Ok(duration) = args[1].parse::<f64>() {
syscall::sleep(duration);
return 0
}
}
1
}
10 changes: 6 additions & 4 deletions src/sys/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,18 @@ pub fn init_heap(mapper: &mut impl Mapper<Size4KiB>, frame_allocator: &mut impl
Ok(())
}

pub fn alloc_pages(addr: u64, size: u64) {
pub fn alloc_pages(addr: u64, size: usize) {
//debug!("Alloc pages (addr={:#x}, size={})", addr, size);
let mut mapper = unsafe { sys::mem::mapper(VirtAddr::new(sys::mem::PHYS_MEM_OFFSET)) };
let mut frame_allocator = unsafe { sys::mem::BootInfoFrameAllocator::init(sys::mem::MEMORY_MAP.unwrap()) };
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE;
let pages = {
let start_page = Page::containing_address(VirtAddr::new(addr));
let end_page = Page::containing_address(VirtAddr::new(addr + size - 1));
let end_page = Page::containing_address(VirtAddr::new(addr + (size as u64) - 1));
Page::range_inclusive(start_page, end_page)
};
for page in pages {
//debug!("Alloc page {:?}", page);
if let Some(frame) = frame_allocator.allocate_frame() {
unsafe {
if let Ok(mapping) = mapper.map_to(page, frame, flags, &mut frame_allocator) {
Expand All @@ -71,11 +73,11 @@ pub fn alloc_pages(addr: u64, size: u64) {
use x86_64::structures::paging::page::PageRangeInclusive;

// TODO: Replace `free` by `dealloc`
pub fn free_pages(addr: u64, size: u64) {
pub fn free_pages(addr: u64, size: usize) {
let mut mapper = unsafe { sys::mem::mapper(VirtAddr::new(sys::mem::PHYS_MEM_OFFSET)) };
let pages: PageRangeInclusive<Size4KiB> = {
let start_page = Page::containing_address(VirtAddr::new(addr));
let end_page = Page::containing_address(VirtAddr::new(addr + size - 1));
let end_page = Page::containing_address(VirtAddr::new(addr + (size as u64) - 1));
Page::range_inclusive(start_page, end_page)
};
for page in pages {
Expand Down
3 changes: 2 additions & 1 deletion src/sys/idt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,14 @@ extern "sysv64" fn syscall_handler(stack_frame: &mut InterruptStackFrame, regs:
let arg1 = regs.rdi;
let arg2 = regs.rsi;
let arg3 = regs.rdx;
let arg4 = regs.r8;

if n == sys::syscall::number::SPAWN { // Backup CPU context
sys::process::set_stack_frame(**stack_frame);
sys::process::set_registers(*regs);
}

let res = sys::syscall::dispatcher(n, arg1, arg2, arg3);
let res = sys::syscall::dispatcher(n, arg1, arg2, arg3, arg4);

if n == sys::syscall::number::EXIT { // Restore CPU context
let sf = sys::process::stack_frame();
Expand Down
93 changes: 61 additions & 32 deletions src/sys/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use x86_64::structures::idt::InterruptStackFrameValue;

const MAX_FILE_HANDLES: usize = 64;
const MAX_PROCS: usize = 2; // TODO: Update this when more than one process can run at once
const MAX_PROC_SIZE: usize = 1 << 20; // 1 MB

lazy_static! {
pub static ref PID: AtomicUsize = AtomicUsize::new(0);
Expand Down Expand Up @@ -145,7 +146,12 @@ pub fn set_code_addr(addr: u64) {
}

pub fn ptr_from_addr(addr: u64) -> *mut u8 {
(code_addr() + addr) as *mut u8
let base = code_addr();
if addr < base {
(base + addr) as *mut u8
} else {
addr as *mut u8
}
}

pub fn registers() -> Registers {
Expand Down Expand Up @@ -175,7 +181,7 @@ pub fn set_stack_frame(stack_frame: InterruptStackFrameValue) {
pub fn exit() {
let table = PROCESS_TABLE.read();
let proc = &table[id()];
sys::allocator::free_pages(proc.code_addr, proc.code_size);
sys::allocator::free_pages(proc.code_addr, MAX_PROC_SIZE);
MAX_PID.fetch_sub(1, Ordering::SeqCst);
set_id(0); // FIXME: No process manager so we switch back to process 0
}
Expand All @@ -193,7 +199,6 @@ use core::sync::atomic::AtomicU64;
use x86_64::VirtAddr;

static CODE_ADDR: AtomicU64 = AtomicU64::new((sys::allocator::HEAP_START as u64) + (16 << 20));
const PAGE_SIZE: u64 = 4 * 1024;

#[repr(align(8), C)]
#[derive(Debug, Clone, Copy, Default)]
Expand All @@ -216,7 +221,7 @@ const BIN_MAGIC: [u8; 4] = [0x7F, b'B', b'I', b'N'];
pub struct Process {
id: usize,
code_addr: u64,
code_size: u64,
stack_addr: u64,
entry_point: u64,
stack_frame: InterruptStackFrameValue,
registers: Registers,
Expand All @@ -235,57 +240,51 @@ impl Process {
Self {
id,
code_addr: 0,
code_size: 0,
stack_addr: 0,
entry_point: 0,
stack_frame: isf,
registers: Registers::default(),
data: ProcessData::new("/", None),
}
}

pub fn spawn(bin: &[u8]) -> Result<(), ()> {
pub fn spawn(bin: &[u8], args_ptr: usize, args_len: usize) -> Result<(), ()> {
if let Ok(pid) = Self::create(bin) {
let proc = {
let table = PROCESS_TABLE.read();
table[pid].clone()
};
proc.exec();
proc.exec(args_ptr, args_len);
Ok(())
} else {
Err(())
}
}

fn create(bin: &[u8]) -> Result<usize, ()> {
// Allocate some memory for the code and the stack of the program
let code_size = 1 * PAGE_SIZE;
let code_addr = CODE_ADDR.fetch_add(code_size, Ordering::SeqCst);
let proc_size = MAX_PROC_SIZE as u64;
let code_addr = CODE_ADDR.fetch_add(proc_size, Ordering::SeqCst);
let stack_addr = code_addr + proc_size;

let mut entry_point = 0;
let code_ptr = code_addr as *mut u8;
if bin[0..4] == ELF_MAGIC { // ELF binary
if let Ok(obj) = object::File::parse(bin) {
sys::allocator::alloc_pages(code_addr, code_size);
//sys::allocator::alloc_pages(code_addr, code_size);
entry_point = obj.entry();
for segment in obj.segments() {
let addr = segment.address() as usize;
if let Ok(data) = segment.data() {
for (i, op) in data.iter().enumerate() {
unsafe {
let ptr = code_ptr.add(addr + i);
core::ptr::write(ptr, *op);
}
for (i, b) in data.iter().enumerate() {
unsafe { core::ptr::write(code_ptr.add(addr + i), *b) };
}
}
}
}
} else if bin[0..4] == BIN_MAGIC { // Flat binary
sys::allocator::alloc_pages(code_addr, code_size);
for (i, op) in bin.iter().skip(4).enumerate() {
unsafe {
let ptr = code_ptr.add(i);
core::ptr::write(ptr, *op);
}
//sys::allocator::alloc_pages(code_addr, code_size);
for (i, b) in bin.iter().skip(4).enumerate() {
unsafe { core::ptr::write(code_ptr.add(i), *b) };
}
} else {
return Err(());
Expand All @@ -299,28 +298,58 @@ impl Process {
let stack_frame = parent.stack_frame;

let id = MAX_PID.fetch_add(1, Ordering::SeqCst);
let proc = Process { id, code_addr, code_size, entry_point, data, stack_frame, registers };
let proc = Process { id, code_addr, stack_addr, entry_point, data, stack_frame, registers };
table[id] = Box::new(proc);

Ok(id)
}

// Switch to user mode and execute the program
fn exec(&self) {
fn exec(&self, args_ptr: usize, args_len: usize) {
let args_ptr = ptr_from_addr(args_ptr as u64) as usize;
let args: &[&str] = unsafe { core::slice::from_raw_parts(args_ptr as *const &str, args_len) };
let heap_addr = self.code_addr + (self.stack_addr - self.code_addr) / 2;
let mut ptr = heap_addr;
let vec: Vec<&str> = args.iter().map(|arg| {
let src_len = arg.len();
let src_ptr = arg.as_ptr();
let dst_ptr = ptr as *mut u8;
//let dst_ptr_translated = ((dst_ptr as u64) - self.code_addr) as *const u8; // Userspace address
ptr = ((dst_ptr as usize) + src_len) as u64;
unsafe {
core::ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len);
//core::str::from_utf8_unchecked(core::slice::from_raw_parts(dst_ptr_translated, src_len))
core::str::from_utf8_unchecked(core::slice::from_raw_parts(dst_ptr, src_len))
}
}).collect();
let args = vec.as_slice();
let src_len = args.len();
let src_ptr = args.as_ptr();
let dst_ptr = ptr as *mut &str;
let args: &[&str] = unsafe {
core::ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len);
core::slice::from_raw_parts(dst_ptr, src_len)
};

//let args_ptr = (args.as_ptr() as u64) - self.code_addr; // userspace address
let args_ptr = args.as_ptr() as u64;

set_id(self.id); // Change PID
unsafe {
asm!(
"cli", // Disable interrupts
"push rax", // Stack segment (SS)
"push rsi", // Stack pointer (RSP)
"push {:r}", // Stack segment (SS)
"push {:r}", // Stack pointer (RSP)
"push 0x200", // RFLAGS with interrupts enabled
"push rdx", // Code segment (CS)
"push rdi", // Instruction pointer (RIP)
"push {:r}", // Code segment (CS)
"push {:r}", // Instruction pointer (RIP)
"iretq",
in("rax") GDT.1.user_data.0,
in("rsi") self.code_addr + self.code_size,
in("rdx") GDT.1.user_code.0,
in("rdi") self.code_addr + self.entry_point,
in(reg) GDT.1.user_data.0,
in(reg) self.stack_addr,
in(reg) GDT.1.user_code.0,
in(reg) self.code_addr + self.entry_point,
in("rdi") args_ptr,
in("rsi") args_len,
);
}
}
Expand Down
Loading