From f60c3ac6440e5f864b61fc4c00556a5ecd2db812 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 20 Aug 2021 13:15:25 +0300 Subject: [PATCH] Context switch --- address/src/virt.rs | 5 ++ error/src/lib.rs | 2 +- kernel/src/arch/exception.rs | 2 + kernel/src/main.rs | 9 +-- kernel/src/mem/phys/manager.rs | 47 ++++++++++++--- kernel/src/mem/phys/mod.rs | 10 ++-- kernel/src/mem/phys/pbox.rs | 7 ++- kernel/src/proc/context.S | 38 +++++++++++++ kernel/src/proc/mod.rs | 101 +++++++++++++++++++++++++++++++++ 9 files changed, 198 insertions(+), 23 deletions(-) create mode 100644 kernel/src/proc/context.S create mode 100644 kernel/src/proc/mod.rs diff --git a/address/src/virt.rs b/address/src/virt.rs index 39aa711..031c7f6 100644 --- a/address/src/virt.rs +++ b/address/src/virt.rs @@ -107,6 +107,11 @@ impl VirtualAddress { core::slice::from_raw_parts_mut(self.0 as *mut _, count) } + #[inline(always)] + pub fn as_mut_ptr(self) -> *mut U { + self.0 as *mut U + } + #[inline(always)] pub unsafe fn as_mut(self) -> &'static mut U { &mut *(self.0 as *mut U) diff --git a/error/src/lib.rs b/error/src/lib.rs index 4d2f980..dc438b1 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -2,5 +2,5 @@ #[derive(PartialEq, Debug, Clone, Copy)] pub enum Errno { - + OutOfMemory } diff --git a/kernel/src/arch/exception.rs b/kernel/src/arch/exception.rs index 8880744..3aa10fe 100644 --- a/kernel/src/arch/exception.rs +++ b/kernel/src/arch/exception.rs @@ -30,5 +30,7 @@ struct ExceptionContext { #[no_mangle] extern "C" fn exc_handler(context: ExceptionContext) -> ! { + debug!("Unhandled exception"); + loop {} } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 738ce69..50119a9 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -8,11 +8,10 @@ pub mod debug; pub mod arch; pub mod boot; pub mod mem; +pub mod proc; pub use mem::KernelSpace; -use core::fmt::{self, Write}; - use address::PhysicalAddress; use mem::phys::UsableMemory; #[derive(Clone)] @@ -45,11 +44,7 @@ extern "C" fn kernel_main() -> ! { mem::phys::initialize(iter); } - loop { - unsafe { - llvm_asm!("wfe"); - } - } + proc::enter(); } use core::panic::PanicInfo; diff --git a/kernel/src/mem/phys/manager.rs b/kernel/src/mem/phys/manager.rs index d0f4c46..b7beb8b 100644 --- a/kernel/src/mem/phys/manager.rs +++ b/kernel/src/mem/phys/manager.rs @@ -1,13 +1,17 @@ use super::{PageInfo, PageUsage}; -use crate::KernelSpace; +use crate::{KernelSpace, mem::PAGE_SIZE}; use address::{PhysicalAddress, VirtualAddress}; use core::mem::{self, MaybeUninit}; use error::Errno; use spin::Mutex; pub unsafe trait Manager { - fn alloc_page(&mut self) -> Result; - fn alloc_contiguous_pages(&mut self, count: usize) -> Result; + fn alloc_page(&mut self, pu: PageUsage) -> Result; + fn alloc_contiguous_pages( + &mut self, + pu: PageUsage, + count: usize, + ) -> Result; fn free_page(&mut self, page: PhysicalAddress) -> Result<(), Errno>; // TODO status() @@ -48,11 +52,40 @@ impl SimpleManager { } unsafe impl Manager for SimpleManager { - fn alloc_page(&mut self) -> Result { - todo!() + fn alloc_page(&mut self, pu: PageUsage) -> Result { + for index in 0..self.pages.len() { + let mut page = self.pages[index].lock(); + + if page.usage == PageUsage::Available { + page.usage = pu; + page.refcount = 1; + return Ok(PhysicalAddress::from(index * PAGE_SIZE)); + } + } + Err(Errno::OutOfMemory) } - fn alloc_contiguous_pages(&mut self, _count: usize) -> Result { - todo!() + fn alloc_contiguous_pages( + &mut self, + pu: PageUsage, + count: usize, + ) -> Result { + 'l0: for i in 0..self.pages.len() { + for j in 0..count { + if self.pages[i + j].lock().usage != PageUsage::Available { + continue 'l0; + } + } + + for j in 0..count { + let mut page = self.pages[i + j].lock(); + assert!(page.usage == PageUsage::Available); + page.usage = pu; + page.refcount = 1; + } + + return Ok(PhysicalAddress::from(i * PAGE_SIZE)); + } + Err(Errno::OutOfMemory) } fn free_page(&mut self, _page: PhysicalAddress) -> Result<(), Errno> { todo!() diff --git a/kernel/src/mem/phys/mod.rs b/kernel/src/mem/phys/mod.rs index 8fa55eb..b4c1c52 100644 --- a/kernel/src/mem/phys/mod.rs +++ b/kernel/src/mem/phys/mod.rs @@ -14,7 +14,7 @@ pub use pbox::PhysBox; type ManagerImpl = SimpleManager; -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone, Copy)] pub enum PageUsage { Reserved, Available, @@ -34,16 +34,16 @@ pub struct UsableMemory { const MAX_PAGES: usize = 1024; -pub fn alloc_page() -> Result { - MANAGER.lock().as_mut().unwrap().alloc_page() +pub fn alloc_page(pu: PageUsage) -> Result { + MANAGER.lock().as_mut().unwrap().alloc_page(pu) } -pub fn alloc_contiguous_pages(count: usize) -> Result { +pub fn alloc_contiguous_pages(pu: PageUsage, count: usize) -> Result { MANAGER .lock() .as_mut() .unwrap() - .alloc_contiguous_pages(count) + .alloc_contiguous_pages(pu, count) } pub fn free_page(page: PhysicalAddress) -> Result<(), Errno> { diff --git a/kernel/src/mem/phys/pbox.rs b/kernel/src/mem/phys/pbox.rs index 4dcc8ad..39d5e13 100644 --- a/kernel/src/mem/phys/pbox.rs +++ b/kernel/src/mem/phys/pbox.rs @@ -1,3 +1,4 @@ +use super::PageUsage; use address::PhysicalAddress; use error::Errno; @@ -7,10 +8,10 @@ pub struct PhysBox { } impl PhysBox { - pub fn new() -> Result { + pub fn new(pu: PageUsage) -> Result { Ok(Self { - base: super::alloc_page()?, - count: 1 + base: super::alloc_page(pu)?, + count: 1, }) } } diff --git a/kernel/src/proc/context.S b/kernel/src/proc/context.S new file mode 100644 index 0000000..524c672 --- /dev/null +++ b/kernel/src/proc/context.S @@ -0,0 +1,38 @@ +.section .text +context_enter_kernel: + ldp x0, xzr, [sp, #0] + ldp lr, x1, [sp, #16] + mov sp, x1 + ret + +context_switch: + // Store old callee-saved regs on stack + sub sp, sp, #96 + + stp x19, x20, [sp, #0] + stp x21, x22, [sp, #16] + stp x23, x24, [sp, #32] + stp x25, x26, [sp, #48] + stp x27, x29, [sp, #64] + stp xzr, lr, [sp, #80] + + // Store old stack pointer + mov x19, sp + str x19, [x1] +context_switch_to: + // Load new stack + ldr x0, [x0] + mov sp, x0 + + // Load new callee-saved regs on stack + ldp x19, x20, [sp, #0] + ldp x21, x22, [sp, #16] + ldp x23, x24, [sp, #32] + ldp x25, x26, [sp, #48] + ldp x27, x29, [sp, #64] + ldp xzr, lr, [sp, #80] + + add sp, sp, #96 + + // Simulate/perform a return + ret diff --git a/kernel/src/proc/mod.rs b/kernel/src/proc/mod.rs new file mode 100644 index 0000000..d776b9d --- /dev/null +++ b/kernel/src/proc/mod.rs @@ -0,0 +1,101 @@ +use crate::{ + mem::phys::{self, PageUsage}, + KernelSpace, +}; +use address::VirtualAddress; +use core::mem::{size_of, MaybeUninit}; + +global_asm!(include_str!("context.S")); + +#[repr(C)] +struct Context { + kernel_sp: VirtualAddress, +} + +struct Stack { + bp: VirtualAddress, + sp: VirtualAddress, + tp: VirtualAddress, +} + +impl Context { + fn new_kernel(entry: usize, arg: usize) -> Context { + let kstack_phys = phys::alloc_contiguous_pages(PageUsage::Kernel, 4).unwrap(); + let mut stack = unsafe { Stack::new(kstack_phys.into(), 4096 * 4) }; + + stack.push(stack.tp); // popped into stack register before ERET + stack.push(entry); // ELR before ERET + stack.push(0usize); // padding + stack.push(arg); + + stack.push(context_enter_kernel as usize); // x30 LR + stack.push(0usize); // padding + stack.push(0usize); // x29 + stack.push(0usize); // x27 + stack.push(0usize); // x26 + stack.push(0usize); // x25 + stack.push(0usize); // x24 + stack.push(0usize); // x23 + stack.push(0usize); // x22 + stack.push(0usize); // x21 + stack.push(0usize); // x20 + stack.push(0usize); // x19 + + Context { + kernel_sp: stack.sp, + } + } +} + +impl Stack { + pub unsafe fn new(bp: VirtualAddress, size: usize) -> Stack { + Stack { + bp, + sp: bp + size, + tp: bp + size, + } + } + + pub fn push>(&mut self, value: A) { + if self.sp == self.bp { + panic!("Stack overflow"); + } + self.sp -= size_of::(); + unsafe { + core::ptr::write(self.sp.as_mut_ptr(), value.into()); + } + } +} + +extern "C" { + fn context_switch_to(dst: *mut Context); + fn context_switch(dst: *mut Context, src: *mut Context); + + fn context_enter_kernel(); +} + +fn f0(arg: usize) { + loop { + unsafe { + debug!("{}", arg); + if arg == 0 { + context_switch(C1.as_mut_ptr(), C0.as_mut_ptr()); + } else { + context_switch(C0.as_mut_ptr(), C1.as_mut_ptr()); + } + } + } +} + +static mut C0: MaybeUninit = MaybeUninit::uninit(); +static mut C1: MaybeUninit = MaybeUninit::uninit(); + +pub fn enter() -> ! { + unsafe { + C0.write(Context::new_kernel(f0 as usize, 0)); + C1.write(Context::new_kernel(f0 as usize, 1)); + + context_switch_to(C0.as_mut_ptr()); + } + loop {} +}