From a19dcb7a2832ca4efeb106227b1524f34c70908f Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Wed, 20 Oct 2021 11:41:47 +0300 Subject: [PATCH] feat: user-space processes --- init/src/main.rs | 19 +- kernel/src/arch/aarch64/boot/mod.rs | 7 +- kernel/src/arch/aarch64/context.S | 26 +- kernel/src/arch/aarch64/context.rs | 51 ++-- kernel/src/arch/aarch64/exception.rs | 10 +- kernel/src/arch/aarch64/reg/cntkctl_el1.rs | 48 ++++ kernel/src/arch/aarch64/reg/mod.rs | 3 + kernel/src/main.rs | 4 + kernel/src/mem/phys/mod.rs | 2 +- kernel/src/mem/virt/table.rs | 3 + kernel/src/proc/elf.rs | 130 ++++------ kernel/src/proc/mod.rs | 288 +++++++++++++-------- 12 files changed, 358 insertions(+), 233 deletions(-) create mode 100644 kernel/src/arch/aarch64/reg/cntkctl_el1.rs diff --git a/init/src/main.rs b/init/src/main.rs index fe3c0c9..dc8a51c 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -4,25 +4,14 @@ use core::panic::PanicInfo; -static RODATA: [u8; 4] = [1, 2, 3, 4]; -static mut WDATA: [u8; 4] = [1, 2, 3, 4]; -static mut WBSS: [u8; 16] = [0; 16]; - #[link_section = ".text._start"] #[no_mangle] -extern "C" fn _start(_arg: usize) -> ! { - let mut c0; - - unsafe { - let d: &mut [u8; 4] = &mut *(&WBSS as *const _ as *mut _); - d[0] = 2; - } - - c0 = unsafe { &mut WDATA as *mut _ as usize }; - c0 = unsafe { &mut WBSS as *mut _ as usize }; - let mut c1 = 1u64; +extern "C" fn _start(arg: usize) -> ! { + let mut c0 = arg; + let mut c1: usize; loop { unsafe { + asm!("mrs {}, cntpct_el0", out(reg) c1); asm!("svc #0", inout("x0") c0, in("x1") c1); } diff --git a/kernel/src/arch/aarch64/boot/mod.rs b/kernel/src/arch/aarch64/boot/mod.rs index d71f643..8cd6127 100644 --- a/kernel/src/arch/aarch64/boot/mod.rs +++ b/kernel/src/arch/aarch64/boot/mod.rs @@ -1,8 +1,8 @@ //! aarch64 common boot logic -use crate::arch::{aarch64::reg::CPACR_EL1, machine}; +use crate::arch::{aarch64::reg::{CPACR_EL1, CNTKCTL_EL1}, machine}; use crate::dev::{fdt::DeviceTree, irq::IntSource, Device}; -use crate::debug::Level; +//use crate::debug::Level; use crate::mem::{ self, heap, phys::{self, PageUsage}, @@ -18,6 +18,9 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! { // Disable FP instruction trapping CPACR_EL1.modify(CPACR_EL1::FPEN::TrapNone); + // Disable CNTPCT and CNTFRQ trapping from EL0 + CNTKCTL_EL1.modify(CNTKCTL_EL1::EL0PCTEN::SET); + extern "C" { static aa64_el1_vectors: u8; } diff --git a/kernel/src/arch/aarch64/context.S b/kernel/src/arch/aarch64/context.S index 24b9368..e7da456 100644 --- a/kernel/src/arch/aarch64/context.S +++ b/kernel/src/arch/aarch64/context.S @@ -5,24 +5,30 @@ .set PT_REGS_SIZE, 16 * 6 -__aa64_ctx_enter_kernel: +__aa64_ctx_enter_user: ldp x0, x1, [sp, #0] msr sp_el0, x0 msr ttbr0_el1, x1 - tst x0, x0 - mov x0, #5 - bne 1f - b 2f -1: - mov x0, #4 -2: - - msr spsr_el1, x0 + msr spsr_el1, xzr ldp x0, x1, [sp, #16] msr elr_el1, x1 add sp, sp, #32 + mov x1, xzr +__return_to_user: + eret + +__aa64_ctx_enter_kernel: + msr sp_el0, xzr + msr ttbr0_el1, xzr + + mov x0, #5 + msr spsr_el1, x0 + ldp x0, x1, [sp, #0] + msr elr_el1, x1 + add sp, sp, #16 + mov x1, xzr eret diff --git a/kernel/src/arch/aarch64/context.rs b/kernel/src/arch/aarch64/context.rs index c5d9532..c62c559 100644 --- a/kernel/src/arch/aarch64/context.rs +++ b/kernel/src/arch/aarch64/context.rs @@ -16,8 +16,6 @@ struct Stack { pub struct Context { /// Thread's kernel stack pointer pub k_sp: usize, // 0x00 - /// Thread's translation table physical address with ASID - pub ttbr0: usize, // 0x08 stack_base_phys: usize, stack_page_count: usize, @@ -25,7 +23,24 @@ pub struct Context { impl Context { /// Constructs a new kernel-space thread context - pub fn kernel(entry: usize, arg: usize, ttbr0: usize, ustack: usize) -> Self { + pub fn kernel(entry: usize, arg: usize) -> Self { + let mut stack = Stack::new(8); + + stack.push(entry); + stack.push(arg); + + stack.setup_common(__aa64_ctx_enter_kernel as usize); + + 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); stack.push(entry); @@ -33,22 +48,10 @@ impl Context { stack.push(ttbr0); stack.push(ustack); - stack.push(__aa64_ctx_enter_kernel as usize); // x30/lr - stack.push(0); // x29 - stack.push(0); // x28 - stack.push(0); // x27 - stack.push(0); // x26 - stack.push(0); // x25 - stack.push(0); // x24 - stack.push(0); // x23 - stack.push(0); // x22 - stack.push(0); // x21 - stack.push(0); // x20 - stack.push(0); // x19 + stack.setup_common(__aa64_ctx_enter_user as usize); Self { k_sp: stack.sp, - ttbr0, stack_base_phys: stack.bp, stack_page_count: 8, @@ -86,6 +89,21 @@ impl Stack { } } + pub fn setup_common(&mut self, entry: usize) { + self.push(entry); // x30/lr + self.push(0); // x29 + self.push(0); // x28 + self.push(0); // x27 + self.push(0); // x26 + self.push(0); // x25 + self.push(0); // x24 + self.push(0); // x23 + self.push(0); // x22 + self.push(0); // x21 + self.push(0); // x20 + self.push(0); // x19 + } + pub fn push(&mut self, value: usize) { if self.bp == self.sp { panic!("Stack overflow"); @@ -100,6 +118,7 @@ impl Stack { extern "C" { fn __aa64_ctx_enter_kernel(); + fn __aa64_ctx_enter_user(); fn __aa64_ctx_switch(dst: *mut Context, src: *mut Context); fn __aa64_ctx_switch_to(dst: *mut Context); } diff --git a/kernel/src/arch/aarch64/exception.rs b/kernel/src/arch/aarch64/exception.rs index 2e77e45..3e3fb2d 100644 --- a/kernel/src/arch/aarch64/exception.rs +++ b/kernel/src/arch/aarch64/exception.rs @@ -1,15 +1,17 @@ //! AArch64 exception handling use crate::arch::machine; +use crate::debug::Level; use crate::dev::irq::{IntController, IrqContext}; use cortex_a::registers::{ESR_EL1, FAR_EL1}; use tock_registers::interfaces::Readable; -use crate::debug::Level; /// Trapped SIMD/FP functionality pub const EC_FP_TRAP: u64 = 0b000111; /// Data Abort at current EL pub const EC_DATA_ABORT_ELX: u64 = 0b100101; +/// Data Abort at lower EL +pub const EC_DATA_ABORT_EL0: u64 = 0b100100; /// SVC instruction in AA64 state pub const EC_SVC_AA64: u64 = 0b010101; @@ -74,7 +76,7 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) { #[allow(clippy::single_match)] match err_code { - EC_DATA_ABORT_ELX => { + EC_DATA_ABORT_ELX | EC_DATA_ABORT_EL0 => { let far = FAR_EL1.get(); dump_data_abort(Level::Error, esr, far); } @@ -87,11 +89,11 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) { } errorln!( - "Unhandled exception at ELR={:#018x}, ESR={:#010x}, exc ctx @ {:p}", + "Unhandled exception at ELR={:#018x}, ESR={:#010x}", exc.elr_el1, esr, - exc ); + errorln!("Error code: {:#08b}", err_code); panic!("Unhandled exception"); } diff --git a/kernel/src/arch/aarch64/reg/cntkctl_el1.rs b/kernel/src/arch/aarch64/reg/cntkctl_el1.rs new file mode 100644 index 0000000..ab2221e --- /dev/null +++ b/kernel/src/arch/aarch64/reg/cntkctl_el1.rs @@ -0,0 +1,48 @@ +//! CNTKCTL_EL1 register + +use tock_registers::{ + interfaces::{Readable, Writeable}, + register_bitfields, +}; + +register_bitfields! { + u64, + #[allow(missing_docs)] + /// Counter-timer Kernel Control Register + pub CNTKCTL_EL1 [ + /// If set, disables CNTPCT and CNTFRQ trapping from EL0 + EL0PCTEN OFFSET(0) NUMBITS(1) [] + ] +} + +/// CNTKCTL_EL1 register +pub struct Reg; + +impl Readable for Reg { + type T = u64; + type R = CNTKCTL_EL1::Register; + + #[inline(always)] + fn get(&self) -> Self::T { + let mut tmp; + unsafe { + asm!("mrs {}, cntkctl_el1", out(reg) tmp); + } + tmp + } +} + +impl Writeable for Reg { + type T = u64; + type R = CNTKCTL_EL1::Register; + + #[inline(always)] + fn set(&self, value: Self::T) { + unsafe { + asm!("msr cntkctl_el1, {}", in(reg) value); + } + } +} + +/// CNTKCTL_EL1 register +pub const CNTKCTL_EL1: Reg = Reg; diff --git a/kernel/src/arch/aarch64/reg/mod.rs b/kernel/src/arch/aarch64/reg/mod.rs index a85fe8e..01666e3 100644 --- a/kernel/src/arch/aarch64/reg/mod.rs +++ b/kernel/src/arch/aarch64/reg/mod.rs @@ -2,3 +2,6 @@ pub mod cpacr_el1; pub use cpacr_el1::CPACR_EL1; + +pub mod cntkctl_el1; +pub use cntkctl_el1::CNTKCTL_EL1; diff --git a/kernel/src/main.rs b/kernel/src/main.rs index d9ff92a..5329ed7 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -33,6 +33,10 @@ pub mod util; #[panic_handler] fn panic_handler(pi: &core::panic::PanicInfo) -> ! { + unsafe { + asm!("msr daifset, #2"); + } + errorln!("Panic: {:?}", pi); // TODO loop {} diff --git a/kernel/src/mem/phys/mod.rs b/kernel/src/mem/phys/mod.rs index 06bc637..04adfd9 100644 --- a/kernel/src/mem/phys/mod.rs +++ b/kernel/src/mem/phys/mod.rs @@ -28,7 +28,7 @@ pub enum PageUsage { /// Translation tables Paging, /// Userspace page - UserStack, + UserPrivate, } /// Data structure representing a single physical memory page diff --git a/kernel/src/mem/virt/table.rs b/kernel/src/mem/virt/table.rs index a5b6eea..c2998dd 100644 --- a/kernel/src/mem/virt/table.rs +++ b/kernel/src/mem/virt/table.rs @@ -44,8 +44,11 @@ bitflags! { const PXN = 1 << 53; // AP field + // Default behavior is: read-write for EL1, no access for EL0 /// If set, the page referred to by this entry is read-only for both EL0/EL1 const AP_BOTH_READONLY = 3 << 6; + /// If set, the page referred to by this entry is read-write for both EL0/EL1 + const AP_BOTH_READWRITE = 1 << 6; } } diff --git a/kernel/src/proc/elf.rs b/kernel/src/proc/elf.rs index 63959a1..9a9ffb3 100644 --- a/kernel/src/proc/elf.rs +++ b/kernel/src/proc/elf.rs @@ -61,110 +61,62 @@ struct Phdr { align: E::Xword, } -unsafe fn load_bytes( - space: &mut Space, - dst_virt: usize, - src: *const u8, - size: usize, - flags: usize, -) -> Result<(), Errno> { - let mut off = 0usize; - let mut rem = size; +fn map_flags(elf_flags: usize) -> MapAttributes { + let mut dst_flags = MapAttributes::NOT_GLOBAL | MapAttributes::SH_OUTER; - // TODO unaligned loads - assert!(dst_virt & 0xFFF == 0); - - while rem != 0 { - let page_idx = off / mem::PAGE_SIZE; - let page_off = off % mem::PAGE_SIZE; - let count = core::cmp::min(rem, mem::PAGE_SIZE - page_off); - - let page = phys::alloc_page(PageUsage::Kernel)?; - let mut dst_flags = MapAttributes::NOT_GLOBAL | MapAttributes::SH_OUTER; - - if flags & (1 << 0) /* PF_X */ == 0 { - dst_flags |= MapAttributes::UXN | MapAttributes::PXN; - } - - match (flags & (3 << 1)) >> 1 { - // No access: not sure if such mapping should exist at all - 0 => todo!(), - // Write-only: not sure if such mapping should exist at all - 1 => todo!(), - // Read-only - 2 => dst_flags |= MapAttributes::AP_BOTH_READONLY, - // Read+Write - 3 => {} - _ => unreachable!(), - }; - - debugln!( - "Mapping {:#x} {:?}", - dst_virt + page_idx * mem::PAGE_SIZE, - dst_flags - ); - space.map(dst_virt + page_idx * mem::PAGE_SIZE, page, dst_flags)?; - - let dst = - core::slice::from_raw_parts_mut(mem::virtualize(page + page_off) as *mut u8, count); - let src = core::slice::from_raw_parts(src.add(off), count); - - dst.copy_from_slice(src); - - rem -= count; - off += count; + if elf_flags & (1 << 0) /* PF_X */ == 0 { + dst_flags |= MapAttributes::UXN | MapAttributes::PXN; } - Ok(()) + match (elf_flags & (3 << 1)) >> 1 { + // No access: not sure if such mapping should exist at all + 0 => todo!(), + // Write-only: not sure if such mapping should exist at all + 1 => todo!(), + // Read-only + 2 => dst_flags |= MapAttributes::AP_BOTH_READONLY, + // Read+Write + 3 => dst_flags |= MapAttributes::AP_BOTH_READWRITE, + _ => unreachable!(), + }; + + dst_flags } -unsafe fn zero_bytes( +unsafe fn load_bytes( space: &mut Space, dst_virt: usize, + read: F, size: usize, flags: usize, -) -> Result<(), Errno> { +) -> Result<(), Errno> +where + F: Fn(usize, &mut [u8]) -> Result<(), Errno>, +{ + let dst_page_off = dst_virt & 0xFFF; + let dst_page = dst_virt & !0xFFF; let mut off = 0usize; let mut rem = size; while rem != 0 { - let page_idx = (dst_virt + off - (dst_virt & !0xFFF)) / mem::PAGE_SIZE; - let page_off = (dst_virt + off) % mem::PAGE_SIZE; + let page_idx = (dst_page_off + off) / mem::PAGE_SIZE; + let page_off = (dst_page_off + off) % mem::PAGE_SIZE; let count = core::cmp::min(rem, mem::PAGE_SIZE - page_off); let page = phys::alloc_page(PageUsage::Kernel)?; - let mut dst_flags = MapAttributes::NOT_GLOBAL | MapAttributes::SH_OUTER; - if flags & (1 << 0) /* PF_X */ == 0 { - dst_flags |= MapAttributes::UXN | MapAttributes::PXN; - } - - match (flags & (3 << 1)) >> 1 { - // No access: not sure if such mapping should exist at all - 0 => todo!(), - // Write-only: not sure if such mapping should exist at all - 1 => todo!(), - // Read-only - 2 => dst_flags |= MapAttributes::AP_BOTH_READONLY, - // Read+Write - 3 => {} - _ => unreachable!(), - }; - - debugln!( - "Mapping {:#x} {:?}", - dst_virt + page_idx * mem::PAGE_SIZE, - dst_flags - ); - if let Err(e) = space.map(dst_virt + page_idx * mem::PAGE_SIZE, page, dst_flags) { + // TODO fetch existing mapping and test flag equality instead + // if flags differ, bail out + if let Err(e) = space.map(dst_page + page_idx * mem::PAGE_SIZE, page, map_flags(flags)) { if e != Errno::AlreadyExists { return Err(e); } } - let dst = - core::slice::from_raw_parts_mut(mem::virtualize(page + page_off) as *mut u8, count); - dst.fill(0); + let dst_page_virt = mem::virtualize(page + page_off); + let dst = core::slice::from_raw_parts_mut(dst_page_virt as *mut u8, count); + + read(off, dst)?; rem -= count; off += count; @@ -201,7 +153,13 @@ pub fn load_elf(space: &mut Space, elf_base: *const u8) -> Result load_bytes( space, phdr.vaddr as usize, - elf_base.add(phdr.offset as usize), + |off, dst| { + let src = elf_base.add(phdr.offset as usize + off); + assert!(off + dst.len() <= phdr.filesz as usize); + let src_slice = core::slice::from_raw_parts(src, dst.len()); + dst.copy_from_slice(src_slice); + Ok(()) + }, phdr.filesz as usize, phdr.flags as usize, )?; @@ -211,9 +169,13 @@ pub fn load_elf(space: &mut Space, elf_base: *const u8) -> Result if phdr.memsz > phdr.filesz { let len = (phdr.memsz - phdr.filesz) as usize; unsafe { - zero_bytes( + load_bytes( space, phdr.vaddr as usize + phdr.filesz as usize, + |_, dst| { + dst.fill(0); + Ok(()) + }, len, phdr.flags as usize, )?; diff --git a/kernel/src/proc/mod.rs b/kernel/src/proc/mod.rs index e0989d8..240c833 100644 --- a/kernel/src/proc/mod.rs +++ b/kernel/src/proc/mod.rs @@ -7,11 +7,12 @@ use crate::mem::{ }; use crate::sync::IrqSafeNullLock; use crate::util::InitOnce; +use alloc::boxed::Box; use alloc::collections::{BTreeMap, VecDeque}; use alloc::rc::Rc; use core::cell::UnsafeCell; +use core::fmt; use core::sync::atomic::{AtomicU32, Ordering}; -use alloc::boxed::Box; use error::Errno; pub use crate::arch::platform::context::{self, Context}; @@ -21,21 +22,41 @@ pub mod elf; /// Wrapper type for a process struct reference pub type ProcessRef = Rc>; +/// Wrapper type for process ID +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] +#[repr(transparent)] +pub struct Pid(u32); + +/// List of possible process states +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum ProcessState { + /// Process is ready to be executed and/or is scheduled for it + Ready, + /// Process is currently running or is in system call/interrupt handler + Running, + /// Process has finished execution and is waiting to be reaped + Finished, + /// Process is waiting for some external event + Waiting, +} + /// Structure describing an operating system process #[allow(dead_code)] pub struct Process { ctx: Context, - space: &'static mut Space, - id: u32, + // TODO move to Option>ed user data struct + space: Option<&'static mut Space>, + state: ProcessState, + id: Pid, } struct SchedulerInner { // TODO the process list itself is not a scheduler-related thing so maybe // move it outside? - processes: BTreeMap, - queue: VecDeque, - idle: u32, - current: Option, + processes: BTreeMap, + queue: VecDeque, + idle: Option, + current: Option, } /// Process scheduler state and queues @@ -43,54 +64,68 @@ pub struct Scheduler { inner: InitOnce>, } -static LAST_PID: AtomicU32 = AtomicU32::new(0); +impl Pid { + /// Kernel idle process always has PID of zero + pub const IDLE: Self = Self(0 | Self::KERNEL_BIT); + + const KERNEL_BIT: u32 = 1 << 31; + + /// Allocates a new kernel-space PID + pub fn new_kernel() -> Self { + static LAST: AtomicU32 = AtomicU32::new(0); + let id = LAST.fetch_add(1, Ordering::Relaxed); + assert!(id & Self::KERNEL_BIT == 0, "Out of kernel PIDs"); + Self(id | Self::KERNEL_BIT) + } + + /// Allocates a new user-space PID. + /// + /// First user PID is #1. + pub fn new_user() -> Self { + static LAST: AtomicU32 = AtomicU32::new(1); + let id = LAST.fetch_add(1, Ordering::Relaxed); + assert!(id < 256, "Out of user PIDs"); + Self(id) + } + + /// Returns `true` if this PID belongs to a kernel process + pub fn is_kernel(self) -> bool { + self.0 & Self::KERNEL_BIT != 0 + } + + /// Returns address space ID of a user-space process. + /// + /// Panics if called on kernel process PID. + pub fn asid(self) -> u8 { + assert!(!self.is_kernel()); + self.0 as u8 + } +} + +impl fmt::Display for Pid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Pid(#{}{})", + if self.is_kernel() { "K" } else { "U" }, + self.0 & !Self::KERNEL_BIT + ) + } +} + impl SchedulerInner { const USTACK_VIRT_TOP: usize = 0x100000000; - fn new_kernel Result>( - &mut self, - loader: F, - ustack_pages: usize, - arg: usize, - ) -> u32 { - let id = LAST_PID.fetch_add(1, Ordering::Relaxed); - if id == 256 { - panic!("Ran out of ASIDs (TODO FIXME)"); - } - let space = Space::alloc_empty().unwrap(); - - let ustack_virt_bottom = Self::USTACK_VIRT_TOP - ustack_pages * mem::PAGE_SIZE; - for i in 0..ustack_pages { - let page = phys::alloc_page(PageUsage::Kernel).unwrap(); - space - .map( - ustack_virt_bottom + i * mem::PAGE_SIZE, - page, - MapAttributes::SH_OUTER - | MapAttributes::NOT_GLOBAL - | MapAttributes::UXN - | MapAttributes::PXN, - ) - .unwrap(); - } - - let entry = loader(space).unwrap(); + fn new_kernel(&mut self, entry: extern "C" fn(usize) -> !, arg: usize) -> Pid { + let id = Pid::new_kernel(); let proc = Process { - ctx: Context::kernel( - entry, - arg, - ((space as *mut _ as usize) - mem::KERNEL_OFFSET) | ((id as usize) << 48), - if ustack_pages != 0 { - Self::USTACK_VIRT_TOP - } else { - 0 - }, - ), - space, + ctx: Context::kernel(entry as usize, arg), + space: None, + state: ProcessState::Ready, id, }; - debugln!("Created kernel process with PID {}", id); + debugln!("Created kernel process: {}", id); assert!(self .processes @@ -100,19 +135,15 @@ impl SchedulerInner { id } - fn new_idle(&mut self) -> u32 { - self.new_kernel(|_| Ok(idle_fn as usize), 0, 0) - } - fn new() -> Self { let mut this = Self { processes: BTreeMap::new(), queue: VecDeque::new(), - idle: 0, + idle: None, current: None, }; - this.idle = this.new_idle(); + this.idle = Some(this.new_kernel(idle_fn, 0)); this } @@ -122,16 +153,8 @@ impl Scheduler { /// Constructs a new kernel-space process with `entry` and `arg`. /// Returns resulting process ID // TODO see the first TODO here - pub fn new_kernel Result>( - &self, - loader: F, - ustack_pages: usize, - arg: usize, - ) -> u32 { - self.inner - .get() - .lock() - .new_kernel(loader, ustack_pages, arg) + pub fn new_kernel(&self, entry: extern "C" fn(usize) -> !, arg: usize) -> Pid { + self.inner.get().lock().new_kernel(entry, arg) } /// Initializes inner data structure: @@ -143,7 +166,7 @@ impl Scheduler { } /// Schedules a thread for execution - pub fn enqueue(&self, pid: u32) { + pub fn enqueue(&self, pid: Pid) { self.inner.get().lock().queue.push_back(pid); } @@ -156,13 +179,15 @@ impl Scheduler { let thread = { let mut inner = self.inner.get().lock(); let id = if inner.queue.is_empty() { - inner.idle + inner.idle.unwrap() } else { inner.queue.pop_front().unwrap() }; inner.current = Some(id); - inner.processes.get(&id).unwrap().clone() + let proc = inner.processes.get(&id).unwrap().clone(); + (*proc.get()).state = ProcessState::Running; + proc }; (*thread.get()).ctx.enter(); @@ -170,23 +195,38 @@ impl Scheduler { /// Switches to the next task scheduled for execution. If there're /// none present in the queue, switches to the idle task. - pub fn switch(&self) { + pub fn switch(&self, discard: bool) { let (from, to) = { let mut inner = self.inner.get().lock(); let current = inner.current.unwrap(); - // Put the process into the back of the queue - inner.queue.push_back(current); + + if !discard && current != Pid::IDLE { + // Put the process into the back of the queue + inner.queue.push_back(current); + } + let next = if inner.queue.is_empty() { - inner.idle + inner.idle.unwrap() } else { inner.queue.pop_front().unwrap() }; inner.current = Some(next); - ( - inner.processes.get(¤t).unwrap().clone(), - inner.processes.get(&next).unwrap().clone(), - ) + let from = inner.processes.get(¤t).unwrap().clone(); + let to = inner.processes.get(&next).unwrap().clone(); + + if !discard { + unsafe { + assert_eq!((*from.get()).state, ProcessState::Running); + (*from.get()).state = ProcessState::Ready; + } + } + unsafe { + assert_eq!((*to.get()).state, ProcessState::Ready); + (*to.get()).state = ProcessState::Running; + } + + (from, to) }; if !Rc::ptr_eq(&from, &to) { @@ -197,7 +237,7 @@ impl Scheduler { } } - /// + /// Returns a Rc-reference to currently running process pub fn current_process(&self) -> ProcessRef { let inner = self.inner.get().lock(); let current = inner.current.unwrap(); @@ -206,7 +246,26 @@ impl Scheduler { } impl Process { + /// Returns current process Rc-reference. /// + /// See [Scheduler::current_process]. + #[inline] + pub fn this() -> ProcessRef { + SCHED.current_process() + } + + /// Terminates a process. + /// + /// # Safety + /// + /// Unsafe: only allowed to be called on "self" process at this moment. + pub unsafe fn exit(&mut self) -> ! { + self.state = ProcessState::Finished; + SCHED.switch(true); + panic!("This code should never run"); + } + + /// Loads a new program into process address space pub fn execve Result>( &mut self, loader: F, @@ -217,64 +276,82 @@ impl Process { asm!("msr daifset, #2"); } + let id = if self.id.is_kernel() { + let r = Pid::new_user(); + debugln!( + "Process downgrades from kernel to user: {} -> {}", + self.id, + r + ); + r + } else { + self.id + }; + let ustack_pages = 4; let new_space = Space::alloc_empty()?; - let new_space_phys = ((new_space as *mut _ as usize) - mem::KERNEL_OFFSET); // | ((id as usize) << 48), + let new_space_phys = (new_space as *mut _ as usize) - mem::KERNEL_OFFSET; let ustack_virt_bottom = SchedulerInner::USTACK_VIRT_TOP - ustack_pages * mem::PAGE_SIZE; for i in 0..ustack_pages { - let page = phys::alloc_page(PageUsage::Kernel).unwrap(); + let page = phys::alloc_page(PageUsage::UserPrivate).unwrap(); + let flags = MapAttributes::SH_OUTER + | MapAttributes::NOT_GLOBAL + | MapAttributes::UXN + | MapAttributes::PXN + | MapAttributes::AP_BOTH_READWRITE; new_space - .map( - ustack_virt_bottom + i * mem::PAGE_SIZE, - page, - MapAttributes::SH_OUTER - | MapAttributes::NOT_GLOBAL - | MapAttributes::UXN - | MapAttributes::PXN, - ) + .map(ustack_virt_bottom + i * mem::PAGE_SIZE, page, flags) .unwrap(); } let entry = loader(new_space)?; - self.ctx = Context::kernel( + debugln!("Will now enter at {:#x}", entry); + self.ctx = Context::user( entry, - 0, - new_space_phys | ((self.id as usize) << 48), + arg, + new_space_phys | ((id.asid() as usize) << 48), SchedulerInner::USTACK_VIRT_TOP, ); - self.space = new_space; + self.space = Some(new_space); + // TODO drop old address space unsafe { self.ctx.enter(); } - panic!("This should not run"); } } +#[inline(never)] extern "C" fn idle_fn(_a: usize) -> ! { loop {} } -#[inline(never)] -extern "C" fn init_fn(initrd_ptr: usize) -> ! { - debugln!("Running kernel init process"); +macro_rules! spawn { + (fn ($dst_arg:ident : usize) $body:block, $src_arg:expr) => {{ + #[inline(never)] + extern "C" fn __inner_func($dst_arg : usize) -> ! { + $body; + #[allow(unreachable_code)] + unsafe { (*Process::this().get()).exit() } + } - let (start, _end) = unsafe { *(initrd_ptr as *const (usize, usize)) }; - let proc = unsafe { &mut *SCHED.current_process().get() }; - proc.execve(|space| elf::load_elf(space, start as *const u8), 0).unwrap(); - loop {} + $crate::proc::SCHED.enqueue($crate::proc::SCHED.new_kernel(__inner_func, $src_arg)); + }}; + + (fn () $body:block) => (spawn!(fn (_arg: usize) $body, 0usize)) } /// Performs a task switch. /// /// See [Scheduler::switch] pub fn switch() { - SCHED.switch(); + SCHED.switch(false); } -static SCHED: Scheduler = Scheduler { +/// +pub static SCHED: Scheduler = Scheduler { inner: InitOnce::new(), }; @@ -289,7 +366,16 @@ pub unsafe fn enter(initrd: Option<(usize, usize)>) -> ! { SCHED.init(); if let Some((start, end)) = initrd { let initrd = Box::into_raw(Box::new((mem::virtualize(start), mem::virtualize(end)))); - SCHED.enqueue(SCHED.new_kernel(|_| Ok(init_fn as usize), 0, initrd as usize)); + + spawn!(fn (initrd_ptr: usize) { + debugln!("Running kernel init process"); + + let (start, _end) = unsafe { *(initrd_ptr as *const (usize, usize)) }; + let proc = unsafe { &mut *SCHED.current_process().get() }; + proc.execve(|space| elf::load_elf(space, start as *const u8), 0) + .unwrap(); + panic!("This code should not run"); + }, initrd as usize); } SCHED.enter(); }