mirror of https://github.com/vinc/moros.git
Add process table and exit syscall (#268)
* Add process table * Add exit syscall * Update binaries * Make pid mutable * Use RW lock for the process table * Change pid atomic ordering * Fix return code * Fix lock issue * Add debug print * Add exit to sleep binary * Enable asm_sym feature * Use the end of code area for stack area * Add debug macro * Use array instead of vec for process * Refactor process data clone * Increase max file handles to 32 * Increase sleep to 5 seconds * Fix MAX_PID calculation * Revert max file handle value to fix crash * Close file handle after dup syscall * Remove init * Run the test in release mode * Refactor debug * Refactor debug output of syscall * Remove NULL syscall * Save and restore stack frame * Fix install * Save only scratch registers to the stack * Add write volatile to registers * Overwrite rax register on exit * Set pid back to 0 after exit * Add alloc_page function * Remove debug output * Unmap pages after exit * Allocate memory after kernel heap * Hide unmap error
This commit is contained in:
parent
35f03ee644
commit
e75c287098
|
@ -1,6 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
- Add process table and exit syscall (#268)
|
||||
- Improve UTF-8 support (#267)
|
||||
- Bump acpi from 4.0.0 to 4.1.0 (#265)
|
||||
- Add shell redirections (#262)
|
||||
|
|
2
Makefile
2
Makefile
|
@ -52,7 +52,7 @@ qemu:
|
|||
qemu-system-x86_64 $(opts)
|
||||
|
||||
test:
|
||||
cargo test --lib --no-default-features --features serial,$(nic) -- \
|
||||
cargo test --release --lib --no-default-features --features serial,$(nic) -- \
|
||||
-m 32 -display none -serial stdio -device isa-debug-exit,iobase=0xf4,iosize=0x04
|
||||
|
||||
clean:
|
||||
|
|
BIN
dsk/bin/hello
BIN
dsk/bin/hello
Binary file not shown.
BIN
dsk/bin/sleep
BIN
dsk/bin/sleep
Binary file not shown.
|
@ -11,4 +11,6 @@ _start:
|
|||
mov rsi, msg ; addr of string
|
||||
mov rdx, 14 ; size of string
|
||||
int 0x80
|
||||
jmp _start
|
||||
mov rax, 1 ; syscall number for EXIT
|
||||
mov rdi, 0 ; no error
|
||||
int 0x80
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
[bits 64]
|
||||
_start:
|
||||
mov rax, 9 ; syscall number for SLEEP
|
||||
mov rdi, __float64__(1.0) ; time to sleep in seconds
|
||||
mov rdi, __float64__(5.0) ; time to sleep in seconds
|
||||
mov rsi, 0
|
||||
mov rdx, 0
|
||||
int 0x80
|
||||
jmp _start
|
||||
mov rax, 1 ; syscall number for EXIT
|
||||
mov rdi, 0 ; no error
|
||||
int 0x80
|
||||
|
|
|
@ -149,7 +149,8 @@ pub fn reopen(path: &str, handle: usize) -> Result<usize, ()> {
|
|||
};
|
||||
if let Some(old_handle) = res {
|
||||
syscall::dup(old_handle, handle);
|
||||
return Ok(old_handle);
|
||||
syscall::close(old_handle);
|
||||
return Ok(handle);
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
|
|
@ -3,16 +3,6 @@ use alloc::vec::Vec;
|
|||
use core::convert::From;
|
||||
use core::ops::RangeBounds;
|
||||
|
||||
// TODO: Remove this when tests are done
|
||||
const DEBUG: bool = false;
|
||||
macro_rules! debug {
|
||||
($($arg:tt)*) => ({
|
||||
if DEBUG {
|
||||
println!("{}", format_args!($($arg)*));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// See "A Regular Expression Matcher" by Rob Pike and Brian Kernighan (2007)
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -72,7 +62,6 @@ pub struct Regex(String);
|
|||
|
||||
impl Regex {
|
||||
pub fn new(re: &str) -> Self {
|
||||
debug!("debug: Regex::new({:?})", re);
|
||||
Self(re.to_string())
|
||||
}
|
||||
pub fn is_match(&self, text: &str) -> bool {
|
||||
|
@ -92,7 +81,6 @@ impl Regex {
|
|||
}
|
||||
|
||||
fn is_match(re: &[char], text: &[char], start: &mut usize, end: &mut usize) -> bool {
|
||||
debug!("debug: is_match({:?}, {:?})", re, text);
|
||||
if re.len() == 0 {
|
||||
return true;
|
||||
}
|
||||
|
@ -116,7 +104,6 @@ fn is_match(re: &[char], text: &[char], start: &mut usize, end: &mut usize) -> b
|
|||
}
|
||||
|
||||
fn is_match_here(re: &[char], text: &[char], end: &mut usize) -> bool {
|
||||
debug!("debug: is_match_here({:?}, {:?})", re, text);
|
||||
if re.len() == 0 {
|
||||
return true;
|
||||
}
|
||||
|
@ -148,22 +135,18 @@ fn is_match_here(re: &[char], text: &[char], end: &mut usize) -> bool {
|
|||
}
|
||||
|
||||
fn is_match_star(lazy: bool, mc: MetaChar, re: &[char], text: &[char], end: &mut usize) -> bool {
|
||||
debug!("debug: is_match_star({:?}, {:?}, {:?}", mc, re, text);
|
||||
is_match_char(lazy, mc, re, text, .., end)
|
||||
}
|
||||
|
||||
fn is_match_plus(lazy: bool, mc: MetaChar, re: &[char], text: &[char], end: &mut usize) -> bool {
|
||||
debug!("debug: is_match_plus({:?}, {:?}, {:?}", mc, re, text);
|
||||
is_match_char(lazy, mc, re, text, 1.., end)
|
||||
}
|
||||
|
||||
fn is_match_ques(lazy: bool, mc: MetaChar, re: &[char], text: &[char], end: &mut usize) -> bool {
|
||||
debug!("debug: is_match_ques({:?}, {:?}, {:?}", mc, re, text);
|
||||
is_match_char(lazy, mc, re, text, ..2, end)
|
||||
}
|
||||
|
||||
fn is_match_char<T: RangeBounds<usize>>(lazy: bool, mc: MetaChar, re: &[char], text: &[char], range: T, end: &mut usize) -> bool {
|
||||
debug!("debug: is_match_char({:?}, {:?}, {:?}", mc, re, text);
|
||||
let mut i = 0;
|
||||
let n = text.len();
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ use crate::syscall;
|
|||
use crate::sys::syscall::number::*;
|
||||
use crate::sys::fs::FileStat;
|
||||
|
||||
pub fn exit(code: usize) -> usize {
|
||||
unsafe { syscall!(EXIT, code as u64) }
|
||||
}
|
||||
|
||||
pub fn sleep(seconds: f64) {
|
||||
unsafe { syscall!(SLEEP, seconds.to_bits()) };
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ fn panic(_info: &PanicInfo) -> ! {
|
|||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "sysv64" fn _start() -> ! {
|
||||
loop {
|
||||
syscall::write(1, b"Hello, World!\n");
|
||||
}
|
||||
syscall::exit(0);
|
||||
loop {}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ fn panic(_info: &PanicInfo) -> ! {
|
|||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "sysv64" fn _start() -> ! {
|
||||
loop {
|
||||
syscall::sleep(1.0);
|
||||
}
|
||||
syscall::sleep(5.0);
|
||||
syscall::exit(0);
|
||||
loop {}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,46 @@ pub fn init_heap(mapper: &mut impl Mapper<Size4KiB>, frame_allocator: &mut impl
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn alloc_pages(addr: u64, size: u64) {
|
||||
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));
|
||||
Page::range_inclusive(start_page, end_page)
|
||||
};
|
||||
for page in pages {
|
||||
let frame = frame_allocator.allocate_frame().unwrap();
|
||||
unsafe {
|
||||
if let Ok(mapping) = mapper.map_to(page, frame, flags, &mut frame_allocator) {
|
||||
mapping.flush();
|
||||
} else {
|
||||
debug!("Could not map {:?}", page);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use x86_64::structures::paging::page::PageRangeInclusive;
|
||||
|
||||
// TODO: Replace `free` by `dealloc`
|
||||
pub fn free_pages(addr: u64, size: u64) {
|
||||
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));
|
||||
Page::range_inclusive(start_page, end_page)
|
||||
};
|
||||
for page in pages {
|
||||
if let Ok((_frame, mapping)) = mapper.unmap(page) {
|
||||
mapping.flush();
|
||||
} else {
|
||||
//debug!("Could not unmap {:?}", page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PhysBuf {
|
||||
buf: Arc<Mutex<Vec<u8>>>,
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::sys;
|
||||
use crate::sys::process::Registers;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
use x86_64::instructions::interrupts;
|
||||
use x86_64::instructions::port::Port;
|
||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
|
||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, InterruptStackFrameValue, PageFaultErrorCode};
|
||||
|
||||
const PIC1: u16 = 0x21;
|
||||
const PIC2: u16 = 0xA1;
|
||||
|
@ -116,36 +118,14 @@ extern "x86-interrupt" fn segment_not_present_handler(stack_frame: InterruptStac
|
|||
panic!("EXCEPTION: SEGMENT NOT PRESENT\n{:#?}", stack_frame);
|
||||
}
|
||||
|
||||
// See: https://github.com/xfoxfu/rust-xos/blob/8a07a69ef/kernel/src/interrupts/handlers.rs#L92
|
||||
#[repr(align(8), C)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Registers {
|
||||
r15: usize,
|
||||
r14: usize,
|
||||
r13: usize,
|
||||
r12: usize,
|
||||
r11: usize,
|
||||
r10: usize,
|
||||
r9: usize,
|
||||
r8: usize,
|
||||
rdi: usize,
|
||||
rsi: usize,
|
||||
rdx: usize,
|
||||
rcx: usize,
|
||||
rbx: usize,
|
||||
rax: usize,
|
||||
rbp: usize,
|
||||
}
|
||||
|
||||
// See: https://github.com/xfoxfu/rust-xos/blob/8a07a69ef/kernel/src/interrupts/handlers.rs#L112
|
||||
// Naked function wrapper saving all scratch registers to the stack
|
||||
// See: https://os.phil-opp.com/returning-from-exceptions/#a-naked-wrapper-function
|
||||
macro_rules! wrap {
|
||||
($fn: ident => $w:ident) => {
|
||||
#[naked]
|
||||
pub unsafe extern "sysv64" fn $w() {
|
||||
asm!(
|
||||
"push rbp",
|
||||
"push rax",
|
||||
"push rbx",
|
||||
"push rcx",
|
||||
"push rdx",
|
||||
"push rsi",
|
||||
|
@ -154,18 +134,10 @@ macro_rules! wrap {
|
|||
"push r9",
|
||||
"push r10",
|
||||
"push r11",
|
||||
"push r12",
|
||||
"push r13",
|
||||
"push r14",
|
||||
"push r15",
|
||||
"mov rsi, rsp", // Arg #2: register list
|
||||
"mov rdi, rsp", // Arg #1: interupt frame
|
||||
"add rdi, 15 * 8",
|
||||
"add rdi, 9 * 8",
|
||||
"call {}",
|
||||
"pop r15",
|
||||
"pop r14",
|
||||
"pop r13",
|
||||
"pop r12",
|
||||
"pop r11",
|
||||
"pop r10",
|
||||
"pop r9",
|
||||
|
@ -174,9 +146,7 @@ macro_rules! wrap {
|
|||
"pop rsi",
|
||||
"pop rdx",
|
||||
"pop rcx",
|
||||
"pop rbx",
|
||||
"pop rax",
|
||||
"pop rbp",
|
||||
"iretq",
|
||||
sym $fn,
|
||||
options(noreturn)
|
||||
|
@ -190,13 +160,31 @@ wrap!(syscall_handler => wrapped_syscall_handler);
|
|||
// NOTE: We can't use "x86-interrupt" for syscall_handler because we need to
|
||||
// return a result in the RAX register and it will be overwritten when the
|
||||
// context of the caller is restored.
|
||||
extern "sysv64" fn syscall_handler(_stack_frame: &mut InterruptStackFrame, regs: &mut Registers) {
|
||||
extern "sysv64" fn syscall_handler(stack_frame: &mut InterruptStackFrame, regs: &mut Registers) {
|
||||
// The registers order follow the System V ABI convention
|
||||
let n = regs.rax;
|
||||
let arg1 = regs.rdi;
|
||||
let arg2 = regs.rsi;
|
||||
let arg3 = regs.rdx;
|
||||
regs.rax = sys::syscall::dispatcher(n, arg1, arg2, arg3);
|
||||
|
||||
if n == sys::syscall::number::SPAWN { // Backup CPU context
|
||||
sys::process::set_stack_frame(stack_frame.clone());
|
||||
sys::process::set_registers(regs.clone());
|
||||
}
|
||||
|
||||
let res = sys::syscall::dispatcher(n, arg1, arg2, arg3);
|
||||
|
||||
if n == sys::syscall::number::EXIT { // Restore CPU context
|
||||
let sf = sys::process::stack_frame();
|
||||
unsafe {
|
||||
//stack_frame.as_mut().write(sf);
|
||||
core::ptr::write_volatile(stack_frame.as_mut().extract_inner() as *mut InterruptStackFrameValue, sf); // FIXME
|
||||
core::ptr::write_volatile(regs, sys::process::registers());
|
||||
}
|
||||
}
|
||||
|
||||
regs.rax = res;
|
||||
|
||||
unsafe { sys::pic::PICS.lock().notify_end_of_interrupt(0x80) };
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,17 @@ macro_rules! printk {
|
|||
});
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)*) => ({
|
||||
let csi_color = $crate::api::console::Style::color("Yellow");
|
||||
let csi_reset = $crate::api::console::Style::reset();
|
||||
$crate::sys::console::print_fmt(format_args!("{}DEBUG: ", csi_color));
|
||||
$crate::sys::console::print_fmt(format_args!($($arg)*));
|
||||
$crate::sys::console::print_fmt(format_args!("{}\n", csi_reset));
|
||||
});
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log {
|
||||
($($arg:tt)*) => ({
|
||||
|
@ -14,6 +25,7 @@ macro_rules! log {
|
|||
let csi_reset = $crate::api::console::Style::reset();
|
||||
$crate::sys::console::print_fmt(format_args!("{}[{:.6}]{} ", csi_color, uptime, csi_reset));
|
||||
$crate::sys::console::print_fmt(format_args!($($arg)*));
|
||||
// TODO: Add newline
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,83 +2,101 @@ use crate::sys::fs::{Resource, Device};
|
|||
use crate::sys::console::Console;
|
||||
use alloc::collections::btree_map::BTreeMap;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use lazy_static::lazy_static;
|
||||
use object::{Object, ObjectSegment};
|
||||
use spin::Mutex;
|
||||
use spin::RwLock;
|
||||
use x86_64::structures::idt::InterruptStackFrameValue;
|
||||
|
||||
const MAX_FILE_HANDLES: usize = 1024;
|
||||
const MAX_FILE_HANDLES: usize = 16; // FIXME Increasing this cause boot crashes
|
||||
const MAX_PROCS: usize = 2; // TODO: Update this when EXIT syscall is working
|
||||
|
||||
lazy_static! {
|
||||
pub static ref PIDS: AtomicUsize = AtomicUsize::new(0);
|
||||
pub static ref PROCESS: Mutex<ProcessData> = Mutex::new(ProcessData::new("/", None)); // TODO
|
||||
pub static ref PID: AtomicUsize = AtomicUsize::new(0);
|
||||
pub static ref MAX_PID: AtomicUsize = AtomicUsize::new(1);
|
||||
pub static ref PROCESS_TABLE: RwLock<[Process; MAX_PROCS]> = RwLock::new([(); MAX_PROCS].map(|_| Process::new(0)));
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ProcessData {
|
||||
id: usize,
|
||||
env: BTreeMap<String, String>,
|
||||
dir: String,
|
||||
user: Option<String>,
|
||||
file_handles: Vec<Option<Resource>>,
|
||||
code_addr: u64,
|
||||
file_handles: [Option<Resource>; MAX_FILE_HANDLES],
|
||||
}
|
||||
|
||||
impl ProcessData {
|
||||
pub fn new(dir: &str, user: Option<&str>) -> Self {
|
||||
let id = PIDS.fetch_add(1, Ordering::SeqCst);
|
||||
let env = BTreeMap::new();
|
||||
let dir = dir.to_string();
|
||||
let user = user.map(String::from);
|
||||
let code_addr = 0;
|
||||
let mut file_handles = vec![None; MAX_FILE_HANDLES];
|
||||
let mut file_handles = [(); MAX_FILE_HANDLES].map(|_| None);
|
||||
file_handles[0] = Some(Resource::Device(Device::Console(Console::new())));
|
||||
file_handles[1] = Some(Resource::Device(Device::Console(Console::new())));
|
||||
file_handles[2] = Some(Resource::Device(Device::Console(Console::new())));
|
||||
Self { id, env, dir, user, file_handles, code_addr }
|
||||
Self { env, dir, user, file_handles }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id() -> usize {
|
||||
PROCESS.lock().id
|
||||
PID.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn set_id(id: usize) {
|
||||
PID.store(id, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn env(key: &str) -> Option<String> {
|
||||
PROCESS.lock().env.get(key).cloned()
|
||||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
proc.data.env.get(key).cloned()
|
||||
}
|
||||
|
||||
pub fn envs() -> BTreeMap<String, String> {
|
||||
PROCESS.lock().env.clone()
|
||||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
proc.data.env.clone()
|
||||
}
|
||||
|
||||
pub fn dir() -> String {
|
||||
PROCESS.lock().dir.clone()
|
||||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
proc.data.dir.clone()
|
||||
}
|
||||
|
||||
pub fn user() -> Option<String> {
|
||||
PROCESS.lock().user.clone()
|
||||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
proc.data.user.clone()
|
||||
}
|
||||
|
||||
pub fn set_env(key: &str, val: &str) {
|
||||
PROCESS.lock().env.insert(key.into(), val.into());
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let proc = &mut table[id()];
|
||||
proc.data.env.insert(key.into(), val.into());
|
||||
}
|
||||
|
||||
pub fn set_dir(dir: &str) {
|
||||
PROCESS.lock().dir = dir.into();
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let proc = &mut table[id()];
|
||||
proc.data.dir = dir.into();
|
||||
}
|
||||
|
||||
pub fn set_user(user: &str) {
|
||||
PROCESS.lock().user = Some(user.into())
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let proc = &mut table[id()];
|
||||
proc.data.user = Some(user.into())
|
||||
}
|
||||
|
||||
pub fn create_file_handle(file: Resource) -> Result<usize, ()> {
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let proc = &mut table[id()];
|
||||
|
||||
let min = 4; // The first 4 file handles are reserved
|
||||
let max = MAX_FILE_HANDLES;
|
||||
let proc = &mut *PROCESS.lock();
|
||||
for handle in min..max {
|
||||
if proc.file_handles[handle].is_none() {
|
||||
proc.file_handles[handle] = Some(file);
|
||||
if proc.data.file_handles[handle].is_none() {
|
||||
proc.data.file_handles[handle] = Some(file);
|
||||
return Ok(handle);
|
||||
}
|
||||
}
|
||||
|
@ -86,30 +104,69 @@ pub fn create_file_handle(file: Resource) -> Result<usize, ()> {
|
|||
}
|
||||
|
||||
pub fn update_file_handle(handle: usize, file: Resource) {
|
||||
let proc = &mut *PROCESS.lock();
|
||||
proc.file_handles[handle] = Some(file);
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let proc = &mut table[id()];
|
||||
proc.data.file_handles[handle] = Some(file);
|
||||
}
|
||||
|
||||
pub fn delete_file_handle(handle: usize) {
|
||||
let proc = &mut *PROCESS.lock();
|
||||
proc.file_handles[handle] = None;
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let proc = &mut table[id()];
|
||||
proc.data.file_handles[handle] = None;
|
||||
}
|
||||
|
||||
pub fn file_handle(handle: usize) -> Option<Resource> {
|
||||
let proc = &mut *PROCESS.lock();
|
||||
proc.file_handles[handle].clone()
|
||||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
proc.data.file_handles[handle].clone()
|
||||
}
|
||||
|
||||
pub fn code_addr() -> u64 {
|
||||
PROCESS.lock().code_addr
|
||||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
proc.code_addr
|
||||
}
|
||||
|
||||
pub fn set_code_addr(addr: u64) {
|
||||
PROCESS.lock().code_addr = addr;
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let mut proc = &mut table[id()];
|
||||
proc.code_addr = addr;
|
||||
}
|
||||
|
||||
pub fn ptr_from_addr(addr: u64) -> *mut u8 {
|
||||
(PROCESS.lock().code_addr + addr) as *mut u8
|
||||
(code_addr() + addr) as *mut u8
|
||||
}
|
||||
|
||||
pub fn registers() -> Registers {
|
||||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
proc.registers.clone()
|
||||
}
|
||||
|
||||
pub fn set_registers(regs: Registers) {
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let mut proc = &mut table[id()];
|
||||
proc.registers = regs
|
||||
}
|
||||
|
||||
pub fn stack_frame() -> InterruptStackFrameValue {
|
||||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
proc.stack_frame.clone()
|
||||
}
|
||||
|
||||
pub fn set_stack_frame(stack_frame: InterruptStackFrameValue) {
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let mut proc = &mut table[id()];
|
||||
proc.stack_frame = stack_frame;
|
||||
}
|
||||
|
||||
pub fn exit() {
|
||||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
sys::allocator::free_pages(proc.code_addr, proc.code_size);
|
||||
MAX_PID.fetch_sub(1, Ordering::SeqCst);
|
||||
set_id(0); // FIXME: No process manager so we switch back to process 0
|
||||
}
|
||||
|
||||
/************************
|
||||
|
@ -123,66 +180,82 @@ use crate::sys;
|
|||
use crate::sys::gdt::GDT;
|
||||
use core::sync::atomic::AtomicU64;
|
||||
use x86_64::VirtAddr;
|
||||
use x86_64::structures::paging::{Mapper, FrameAllocator};
|
||||
use x86_64::structures::paging::{Page, PageTableFlags};
|
||||
|
||||
static STACK_ADDR: AtomicU64 = AtomicU64::new(0x200_0000);
|
||||
static CODE_ADDR: AtomicU64 = AtomicU64::new(0x100_0000);
|
||||
static CODE_ADDR: AtomicU64 = AtomicU64::new((sys::allocator::HEAP_START as u64) + (16 << 20)); // 16 MB
|
||||
const PAGE_SIZE: u64 = 4 * 1024;
|
||||
|
||||
#[repr(align(8), C)]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Registers {
|
||||
pub r11: usize,
|
||||
pub r10: usize,
|
||||
pub r9: usize,
|
||||
pub r8: usize,
|
||||
pub rdi: usize,
|
||||
pub rsi: usize,
|
||||
pub rdx: usize,
|
||||
pub rcx: usize,
|
||||
pub rax: usize,
|
||||
}
|
||||
|
||||
const ELF_MAGIC: [u8; 4] = [0x74, b'E', b'L', b'F'];
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Process {
|
||||
stack_addr: u64,
|
||||
stack_size: u64,
|
||||
id: usize,
|
||||
code_addr: u64,
|
||||
code_size: u64,
|
||||
entry_point: u64,
|
||||
stack_frame: InterruptStackFrameValue,
|
||||
registers: Registers,
|
||||
data: ProcessData,
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub fn create(bin: &[u8]) -> Result<Process, ()> {
|
||||
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 stack_size = 256 * PAGE_SIZE;
|
||||
let stack_addr = STACK_ADDR.fetch_add(stack_size, Ordering::SeqCst);
|
||||
let pages = {
|
||||
let stack_start_page = Page::containing_address(VirtAddr::new(stack_addr));
|
||||
let stack_end_page = Page::containing_address(VirtAddr::new(stack_addr + stack_size));
|
||||
Page::range_inclusive(stack_start_page, stack_end_page)
|
||||
pub fn new(id: usize) -> Self {
|
||||
let isf = InterruptStackFrameValue {
|
||||
instruction_pointer: VirtAddr::new(0),
|
||||
code_segment: 0,
|
||||
cpu_flags: 0,
|
||||
stack_pointer: VirtAddr::new(0),
|
||||
stack_segment: 0,
|
||||
};
|
||||
for page in pages {
|
||||
let frame = frame_allocator.allocate_frame().unwrap();
|
||||
unsafe {
|
||||
mapper.map_to(page, frame, flags, &mut frame_allocator).unwrap().flush();
|
||||
Self {
|
||||
id,
|
||||
code_addr: 0,
|
||||
code_size: 0,
|
||||
entry_point: 0,
|
||||
stack_frame: isf,
|
||||
registers: Registers::default(),
|
||||
data: ProcessData::new("/", None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn(bin: &[u8]) {
|
||||
if let Ok(pid) = Self::create(bin) {
|
||||
let proc = {
|
||||
let table = PROCESS_TABLE.read();
|
||||
table[pid].clone()
|
||||
};
|
||||
proc.exec();
|
||||
}
|
||||
}
|
||||
|
||||
fn create(bin: &[u8]) -> Result<usize, ()> {
|
||||
let code_size = 1024 * PAGE_SIZE;
|
||||
let code_addr = CODE_ADDR.fetch_add(code_size, Ordering::SeqCst);
|
||||
let pages = {
|
||||
let code_start_page = Page::containing_address(VirtAddr::new(code_addr));
|
||||
let code_end_page = Page::containing_address(VirtAddr::new(code_addr + code_size));
|
||||
Page::range_inclusive(code_start_page, code_end_page)
|
||||
};
|
||||
for page in pages {
|
||||
let frame = frame_allocator.allocate_frame().unwrap();
|
||||
unsafe {
|
||||
mapper.map_to(page, frame, flags, &mut frame_allocator).unwrap().flush();
|
||||
}
|
||||
}
|
||||
sys::allocator::alloc_pages(code_addr, code_size);
|
||||
|
||||
let mut entry_point = 0;
|
||||
let code_ptr = code_addr as *mut u8;
|
||||
if &bin[1..4] == b"ELF" { // ELF binary
|
||||
if &bin[0..4] == ELF_MAGIC { // ELF binary
|
||||
if let Ok(obj) = object::File::parse(bin) {
|
||||
entry_point = obj.entry();
|
||||
for segment in obj.segments() {
|
||||
let addr = segment.address() as usize;
|
||||
if let Ok(data) = segment.data() {
|
||||
unsafe {
|
||||
for (i, op) in data.iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = code_ptr.add(addr + i);
|
||||
core::ptr::write(ptr, *op);
|
||||
}
|
||||
|
@ -191,24 +264,31 @@ impl Process {
|
|||
}
|
||||
}
|
||||
} else { // Raw binary
|
||||
unsafe {
|
||||
for (i, op) in bin.iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = code_ptr.add(i);
|
||||
core::ptr::write(ptr, *op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_code_addr(code_addr);
|
||||
let mut table = PROCESS_TABLE.write();
|
||||
let parent = &table[id()];
|
||||
|
||||
Ok(Process { stack_addr, stack_size, code_addr, entry_point })
|
||||
let data = parent.data.clone();
|
||||
let registers = parent.registers.clone();
|
||||
let stack_frame = parent.stack_frame.clone();
|
||||
|
||||
let id = MAX_PID.fetch_add(1, Ordering::SeqCst);
|
||||
let proc = Process { id, code_addr, code_size, entry_point, data, stack_frame, registers };
|
||||
table[id] = proc;
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
// Switch to user mode and execute the program
|
||||
pub fn exec(&self) {
|
||||
//x86_64::instructions::tlb::flush_all();
|
||||
let data = GDT.1.user_data.0;
|
||||
let code = GDT.1.user_code.0;
|
||||
fn exec(&self) {
|
||||
set_id(self.id); // Change PID
|
||||
unsafe {
|
||||
asm!(
|
||||
"cli", // Disable interrupts
|
||||
|
@ -218,9 +298,9 @@ impl Process {
|
|||
"push rdx", // Code segment (CS)
|
||||
"push rdi", // Instruction pointer (RIP)
|
||||
"iretq",
|
||||
in("rax") data,
|
||||
in("rsi") self.stack_addr + self.stack_size,
|
||||
in("rdx") code,
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ use crate::sys::fs::FileStat;
|
|||
|
||||
pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
|
||||
match n {
|
||||
number::EXIT => {
|
||||
service::exit(arg1)
|
||||
}
|
||||
number::SLEEP => {
|
||||
service::sleep(f64::from_bits(arg1 as u64));
|
||||
0
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pub const NULL: usize = 0x0;
|
||||
pub const EXIT: usize = 0x1;
|
||||
pub const SPAWN: usize = 0x2;
|
||||
pub const READ: usize = 0x3;
|
||||
|
|
|
@ -4,6 +4,11 @@ use crate::sys::fs::FileIO;
|
|||
use crate::sys::process::Process;
|
||||
use alloc::vec;
|
||||
|
||||
pub fn exit(_code: usize) -> usize {
|
||||
sys::process::exit();
|
||||
0
|
||||
}
|
||||
|
||||
pub fn sleep(seconds: f64) {
|
||||
sys::time::sleep(seconds);
|
||||
}
|
||||
|
@ -71,11 +76,9 @@ pub fn spawn(path: &str) -> isize {
|
|||
let mut buf = vec![0; file.size()];
|
||||
if let Ok(bytes) = file.read(&mut buf) {
|
||||
buf.resize(bytes, 0);
|
||||
if let Ok(process) = Process::create(&buf) {
|
||||
process.exec();
|
||||
Process::spawn(&buf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
|
|
@ -181,6 +181,7 @@ pub fn exec(cmd: &str) -> ExitCode {
|
|||
|
||||
let res = match args[0] {
|
||||
"" => ExitCode::CommandSuccessful,
|
||||
"test" => { api::syscall::exit(0); ExitCode::CommandSuccessful },
|
||||
"a" | "alias" => ExitCode::CommandUnknown,
|
||||
"b" => ExitCode::CommandUnknown,
|
||||
"c" | "copy" => usr::copy::main(&args),
|
||||
|
|
Loading…
Reference in New Issue