feature: fork()

This commit is contained in:
2021-11-04 11:26:15 +02:00
parent 166fc19b7a
commit 37a78ad8ae
11 changed files with 292 additions and 36 deletions
+40 -23
View File
@@ -5,37 +5,54 @@
#[macro_use]
extern crate libusr;
use libusr::sys::{OpenFlags, AT_FDCWD};
use libusr::io;
use libusr::sys::{OpenFlags, AT_FDCWD};
#[no_mangle]
fn main() -> i32 {
let mut buf = [0; 128];
print!("\x1B[2J\x1B[1;1H");
println!("Hello!");
loop {
print!("> ");
let pid = unsafe { libusr::sys::sys_fork() };
let count = unsafe {
libusr::sys::sys_read(0, buf.as_mut_ptr(), buf.len())
};
if count < 0 {
trace!("Read from stdio failed");
break;
}
let count = count as usize;
if let Ok(s) = core::str::from_utf8(&buf[..count]) {
println!("Got string {:?}", s);
if s == "quit" {
break;
if pid == 0 {
trace!("Hello!");
unsafe {
libusr::sys::sys_ex_nanosleep(3_000_000_000, core::ptr::null_mut());
}
trace!("Exiting");
return 0;
} else {
println!("Got string (non-utf8) {:?}", &buf[..count]);
trace!("Spawned {}", pid);
unsafe {
libusr::sys::sys_ex_nanosleep(5_000_000_000, core::ptr::null_mut());
}
}
}
-1
//let mut buf = [0; 128];
// print!("\x1B[2J\x1B[1;1H");
// println!("Hello!");
// loop {
// print!("> ");
// let count = unsafe {
// libusr::sys::sys_read(0, buf.as_mut_ptr(), buf.len())
// };
// if count < 0 {
// trace!("Read from stdio failed");
// break;
// }
// let count = count as usize;
// if let Ok(s) = core::str::from_utf8(&buf[..count]) {
// println!("Got string {:?}", s);
// if s == "quit" {
// break;
// }
// } else {
// println!("Got string (non-utf8) {:?}", &buf[..count]);
// }
// }
// -1
}
+42
View File
@@ -2,6 +2,7 @@
.global __aa64_ctx_switch
.global __aa64_ctx_switch_to
.global __aa64_ctx_enter_kernel
.global __aa64_ctx_enter_from_fork
.set PT_REGS_SIZE, 16 * 7
@@ -30,6 +31,47 @@ __aa64_ctx_enter_kernel:
mov x1, xzr
eret
__aa64_ctx_enter_from_fork:
// stack.push(frame.x[18]);
// stack.push(frame.x[17]);
// stack.push(frame.x[16]);
// stack.push(frame.x[15]);
// stack.push(frame.x[14]);
// stack.push(frame.x[13]);
// stack.push(frame.x[12]);
// stack.push(frame.x[11]);
// stack.push(frame.x[10]);
// stack.push(frame.x[9]);
// stack.push(frame.x[8]);
// stack.push(frame.x[7]);
// stack.push(frame.x[6]);
// stack.push(frame.x[5]);
// stack.push(frame.x[4]);
// stack.push(frame.x[3]);
// stack.push(frame.x[2]);
// stack.push(frame.x[1]);
// stack.push(frame.elr_el1 as usize);
// stack.push(frame.sp_el0 as usize);
ldp x0, x1, [sp, #16 * 0]
msr sp_el0, x0
msr elr_el1, x1
msr spsr_el1, xzr
ldp x1, x2, [sp, #16 * 1]
ldp x3, x4, [sp, #16 * 2]
ldp x5, x6, [sp, #16 * 3]
ldp x7, x8, [sp, #16 * 4]
ldp x9, x10, [sp, #16 * 5]
ldp x11, x12, [sp, #16 * 6]
ldp x13, x14, [sp, #16 * 7]
ldp x15, x16, [sp, #16 * 8]
ldp x17, x18, [sp, #16 * 9]
mov x0, xzr
eret
__aa64_ctx_switch:
sub sp, sp, #PT_REGS_SIZE
+52
View File
@@ -4,6 +4,7 @@ use crate::mem::{
self,
phys::{self, PageUsage},
};
use crate::arch::aarch64::exception::ExceptionFrame;
use core::mem::size_of;
struct Stack {
@@ -39,6 +40,56 @@ impl Context {
}
}
///
pub fn fork(frame: &ExceptionFrame, ttbr0: usize) -> Self {
let mut stack = Stack::new(8);
stack.push(frame.x[18]);
stack.push(frame.x[17]);
stack.push(frame.x[16]);
stack.push(frame.x[15]);
stack.push(frame.x[14]);
stack.push(frame.x[13]);
stack.push(frame.x[12]);
stack.push(frame.x[11]);
stack.push(frame.x[10]);
stack.push(frame.x[9]);
stack.push(frame.x[8]);
stack.push(frame.x[7]);
stack.push(frame.x[6]);
stack.push(frame.x[5]);
stack.push(frame.x[4]);
stack.push(frame.x[3]);
stack.push(frame.x[2]);
stack.push(frame.x[1]);
stack.push(frame.elr_el1 as usize);
stack.push(frame.sp_el0 as usize);
// Setup common
stack.push(0);
stack.push(ttbr0);
stack.push(__aa64_ctx_enter_from_fork as usize); // x30/lr
stack.push(frame.x[29]); // x29
stack.push(frame.x[28]); // x28
stack.push(frame.x[27]); // x27
stack.push(frame.x[26]); // x26
stack.push(frame.x[25]); // x25
stack.push(frame.x[24]); // x24
stack.push(frame.x[23]); // x23
stack.push(frame.x[22]); // x22
stack.push(frame.x[21]); // x21
stack.push(frame.x[20]); // x20
stack.push(frame.x[19]); // x19
Self {
k_sp: stack.sp,
stack_base_phys: stack.bp,
stack_page_count: 8
}
}
/// Constructs a new user-space thread context
pub fn user(entry: usize, arg: usize, ttbr0: usize, ustack: usize) -> Self {
let mut stack = Stack::new(8);
@@ -119,6 +170,7 @@ impl Stack {
}
extern "C" {
fn __aa64_ctx_enter_from_fork();
fn __aa64_ctx_enter_kernel();
fn __aa64_ctx_enter_user();
fn __aa64_ctx_switch(dst: *mut Context, src: *mut Context);
+18 -6
View File
@@ -6,6 +6,7 @@ use crate::dev::irq::{IntController, IrqContext};
use crate::syscall;
use cortex_a::registers::{ESR_EL1, FAR_EL1};
use tock_registers::interfaces::Readable;
use ::syscall::abi;
/// Trapped SIMD/FP functionality
pub const EC_FP_TRAP: u64 = 0b000111;
@@ -16,14 +17,15 @@ pub const EC_DATA_ABORT_EL0: u64 = 0b100100;
/// SVC instruction in AA64 state
pub const EC_SVC_AA64: u64 = 0b010101;
#[allow(missing_docs)]
#[derive(Debug)]
#[repr(C)]
struct ExceptionFrame {
x: [usize; 32],
spsr_el1: u64,
elr_el1: u64,
sp_el0: u64,
ttbr0_el1: u64,
pub struct ExceptionFrame {
pub x: [usize; 32],
pub spsr_el1: u64,
pub elr_el1: u64,
pub sp_el0: u64,
pub ttbr0_el1: u64,
}
#[inline(always)]
@@ -83,6 +85,16 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
}
EC_SVC_AA64 => {
unsafe {
if exc.x[8] == abi::SYS_FORK {
match syscall::sys_fork(exc) {
Ok(pid) => exc.x[0] = pid.value() as usize,
Err(err) => {
todo!()
},
}
return;
}
match syscall::syscall(exc.x[8], &exc.x[..6]) {
Ok(val) => exc.x[0] = val,
Err(err) => {
+30 -6
View File
@@ -1,5 +1,5 @@
use super::{PageInfo, PageUsage};
use crate::mem::{virtualize, PAGE_SIZE};
use crate::mem::{memcpy, virtualize, PAGE_SIZE};
use crate::sync::IrqSafeSpinLock;
use core::mem;
use error::Errno;
@@ -8,6 +8,7 @@ pub unsafe trait Manager {
fn alloc_page(&mut self, pu: PageUsage) -> Result<usize, Errno>;
fn alloc_contiguous_pages(&mut self, pu: PageUsage, count: usize) -> Result<usize, Errno>;
fn free_page(&mut self, page: usize) -> Result<(), Errno>;
fn clone_page(&mut self, src: usize) -> Result<usize, Errno>;
// TODO status()
}
pub struct SimpleManager {
@@ -34,23 +35,33 @@ impl SimpleManager {
}
}
pub(super) unsafe fn add_page(&mut self, addr: usize) {
let page = &mut self.pages[addr / PAGE_SIZE - self.base_index];
let page = &mut self.pages[self.page_index(addr)];
assert!(page.refcount == 0 && page.usage == PageUsage::Reserved);
page.usage = PageUsage::Available;
}
}
unsafe impl Manager for SimpleManager {
fn alloc_page(&mut self, pu: PageUsage) -> Result<usize, Errno> {
fn page_index(&self, page: usize) -> usize {
page / PAGE_SIZE - self.base_index
}
fn alloc_single_index(&mut self, pu: PageUsage) -> Result<usize, Errno> {
for index in 0..self.pages.len() {
let page = &mut self.pages[index];
if page.usage == PageUsage::Available {
page.usage = pu;
page.refcount = 1;
return Ok((self.base_index + index) * PAGE_SIZE);
return Ok(index);
}
}
Err(Errno::OutOfMemory)
}
}
unsafe impl Manager for SimpleManager {
fn alloc_page(&mut self, pu: PageUsage) -> Result<usize, Errno> {
self.alloc_single_index(pu)
.map(|r| (self.base_index + r) * PAGE_SIZE)
//return Ok((self.base_index + index) * PAGE_SIZE);
}
fn alloc_contiguous_pages(&mut self, pu: PageUsage, count: usize) -> Result<usize, Errno> {
'l0: for i in 0..self.pages.len() {
for j in 0..count {
@@ -71,6 +82,19 @@ unsafe impl Manager for SimpleManager {
fn free_page(&mut self, _page: usize) -> Result<(), Errno> {
todo!()
}
fn clone_page(&mut self, src: usize) -> Result<usize, Errno> {
let src_index = self.page_index(src);
let src_page = &self.pages[src_index];
assert_eq!(src_page.refcount, 1);
assert!(src_page.usage != PageUsage::Available && src_page.usage != PageUsage::Reserved);
let dst_index = self.alloc_single_index(src_page.usage)?;
let dst = (self.base_index + dst_index) * PAGE_SIZE;
unsafe {
memcpy(virtualize(dst) as *mut u8, virtualize(src) as *mut u8, 4096);
}
Ok(dst)
}
}
pub(super) static MANAGER: IrqSafeSpinLock<Option<SimpleManager>> = IrqSafeSpinLock::new(None);
+5
View File
@@ -87,6 +87,11 @@ pub unsafe fn free_page(page: usize) -> Result<(), Errno> {
MANAGER.lock().as_mut().unwrap().free_page(page)
}
///
pub fn clone_page(src: usize) -> Result<usize, Errno> {
MANAGER.lock().as_mut().unwrap().clone_page(src)
}
fn find_contiguous<T: Iterator<Item = MemoryRegion>>(iter: T, count: usize) -> Option<usize> {
for region in iter {
let mut collected = 0;
+46
View File
@@ -74,6 +74,20 @@ impl Table {
}
}
///
pub fn next_level_table(&mut self, index: usize) -> Option<&'static mut Table> {
let entry = self[index];
if entry.is_present() {
if !entry.is_table() {
panic!("Entry is not a table: idx={}", index);
}
Some(unsafe { &mut *(mem::virtualize(entry.address_unchecked()) as *mut _) })
} else {
None
}
}
/// Constructs and fills a [Table] with non-present mappings
pub const fn empty() -> Table {
Table {
@@ -135,6 +149,10 @@ impl Entry {
pub const unsafe fn address_unchecked(self) -> usize {
(self.0 & Self::PHYS_MASK) as usize
}
unsafe fn fork_flags(self) -> MapAttributes {
MapAttributes::from_bits_unchecked(self.0 & !Self::PHYS_MASK)
}
}
impl Space {
@@ -166,4 +184,32 @@ impl Space {
Ok(())
}
}
///
pub fn fork(&mut self) -> Result<&'static mut Self, Errno> {
let mut res = Self::alloc_empty()?;
for l0i in 0..512 {
if let Some(l1_table) = self.0.next_level_table(l0i) {
for l1i in 0..512 {
if let Some(l2_table) = l1_table.next_level_table(l1i) {
for l2i in 0..512 {
let entry = l2_table[l2i];
// TODO copy-on-write
if entry.is_present() {
assert!(entry.is_table());
let src_phys = unsafe { entry.address_unchecked() };
let virt_addr = (l0i << 30) | (l1i << 21) | (l2i << 12);
debugln!("Fork page {:#x}:{:#x}", virt_addr, src_phys);
let dst_phys = phys::clone_page(src_phys)?;
res.map(virt_addr, dst_phys, unsafe { entry.fork_flags() })?;
}
}
}
}
}
}
Ok(res)
}
}
+40
View File
@@ -4,6 +4,7 @@ use crate::mem::{
phys::{self, PageUsage},
virt::{MapAttributes, Space},
};
use crate::arch::aarch64::exception::ExceptionFrame;
use crate::proc::{PROCESSES, SCHED};
use crate::sync::IrqSafeSpinLock;
use alloc::{rc::Rc, collections::BTreeMap};
@@ -123,6 +124,11 @@ impl Pid {
assert!(!self.is_kernel());
self.0 as u8
}
///
pub const fn value(self) -> u32 {
self.0
}
}
impl fmt::Display for Pid {
@@ -137,6 +143,12 @@ impl fmt::Display for Pid {
}
impl ProcessIo {
///
pub fn fork(&self) -> Result<ProcessIo, Errno> {
// TODO
Ok(Self::new())
}
///
pub fn file(&mut self, idx: usize) -> Result<&mut File, Errno> {
self.files.get_mut(&idx).ok_or(Errno::InvalidFile)
@@ -285,6 +297,34 @@ impl Process {
Ok(res)
}
///
pub fn fork(&self, frame: &mut ExceptionFrame) -> Result<Pid, Errno> {
let src_io = self.io.lock();
let mut src_inner = self.inner.lock();
let dst_id = Pid::new_user();
let dst_space = src_inner.space.as_mut().unwrap().fork()?;
let dst_space_phys = (dst_space as *mut _ as usize) - mem::KERNEL_OFFSET;
let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
let dst = Rc::new(Self {
ctx: UnsafeCell::new(Context::fork(frame, dst_ttbr0)),
io: IrqSafeSpinLock::new(src_io.fork()?),
inner: IrqSafeSpinLock::new(ProcessInner {
id: dst_id,
exit: None,
space: Some(dst_space),
state: State::Ready,
wait_flag: false
})
});
debugln!("Process {} forked into {}", src_inner.id, dst_id);
assert!(PROCESSES.lock().insert(dst_id, dst.clone()).is_none());
SCHED.enqueue(dst_id);
Ok(dst_id)
}
/// Terminates a process.
pub fn exit<I: Into<ExitCode>>(&self, status: I) {
let status = status.into();
+8 -1
View File
@@ -1,12 +1,13 @@
use crate::debug::Level;
use crate::mem;
use crate::proc::{wait, Process};
use crate::proc::{wait, Process, Pid};
use core::mem::size_of;
use core::time::Duration;
use error::Errno;
use libcommon::{Read, Write};
use syscall::{abi, stat::AT_FDCWD};
use vfs::{FileMode, OpenFlags, Stat};
use crate::arch::platform::exception::ExceptionFrame;
fn translate(virt: usize) -> Option<usize> {
let mut res: usize;
@@ -106,6 +107,12 @@ fn validate_user_str<'a>(base: usize, limit: usize) -> Result<&'a str, Errno> {
})
}
pub unsafe fn sys_fork(regs: &mut ExceptionFrame) -> Result<Pid, Errno> {
let proc = Process::current();
let res = proc.fork(regs);
res
}
pub unsafe fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
match num {
// Process management system calls
+1
View File
@@ -9,3 +9,4 @@ pub const SYS_WRITE: usize = 3;
pub const SYS_OPENAT: usize = 4;
pub const SYS_FSTATAT: usize = 5;
pub const SYS_CLOSE: usize = 6;
pub const SYS_FORK: usize = 7;
+10
View File
@@ -2,6 +2,11 @@ use crate::abi;
use crate::stat::Stat;
macro_rules! syscall {
($num:expr) => {{
let mut res: usize;
asm!("svc #0", out("x0") res, in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
@@ -84,3 +89,8 @@ pub unsafe fn sys_fstatat(at: i32, pathname: *const u8, statbuf: *mut Stat, flag
flags as usize
) as i32
}
#[inline(always)]
pub unsafe fn sys_fork() -> i32 {
syscall!(abi::SYS_FORK) as i32
}