feat: user-space processes

This commit is contained in:
2021-10-20 11:41:47 +03:00
parent 1970e24808
commit a19dcb7a28
12 changed files with 358 additions and 233 deletions
+5 -2
View File
@@ -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;
}
+16 -10
View File
@@ -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
+35 -16
View File
@@ -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);
}
+6 -4
View File
@@ -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");
}
@@ -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;
+3
View File
@@ -2,3 +2,6 @@
pub mod cpacr_el1;
pub use cpacr_el1::CPACR_EL1;
pub mod cntkctl_el1;
pub use cntkctl_el1::CNTKCTL_EL1;
+4
View File
@@ -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 {}
+1 -1
View File
@@ -28,7 +28,7 @@ pub enum PageUsage {
/// Translation tables
Paging,
/// Userspace page
UserStack,
UserPrivate,
}
/// Data structure representing a single physical memory page
+3
View File
@@ -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;
}
}
+46 -84
View File
@@ -61,110 +61,62 @@ struct Phdr<E: Elf> {
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<F>(
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<usize, Errno>
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<usize, Errno>
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,
)?;
+187 -101
View File
@@ -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<UnsafeCell<Process>>;
/// 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<Box<>>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<u32, ProcessRef>,
queue: VecDeque<u32>,
idle: u32,
current: Option<u32>,
processes: BTreeMap<Pid, ProcessRef>,
queue: VecDeque<Pid>,
idle: Option<Pid>,
current: Option<Pid>,
}
/// Process scheduler state and queues
@@ -43,54 +64,68 @@ pub struct Scheduler {
inner: InitOnce<IrqSafeNullLock<SchedulerInner>>,
}
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<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
&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<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
&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(&current).unwrap().clone(),
inner.processes.get(&next).unwrap().clone(),
)
let from = inner.processes.get(&current).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<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
&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();
}