feature: fork()
This commit is contained in:
+40
-23
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user