diff options
| author | Tianhao Wang <shrik3@mailbox.org> | 2024-06-10 20:47:54 +0200 |
|---|---|---|
| committer | Tianhao Wang <shrik3@mailbox.org> | 2024-06-11 15:17:14 +0200 |
| commit | 13e3eca8ac3f6bd8c7a8fddde8b1937757e358ee (patch) | |
| tree | eb2d4992d5692287c232ac729ad18f9690eaa153 /src/proc | |
| parent | 8aaad696463004a9e51d35e4c466c131b3402822 (diff) | |
proc: basic task/stack creation
Now we can do a simple context swap (without scheduler though)
Diffstat (limited to 'src/proc')
| -rw-r--r-- | src/proc/sched.rs | 16 | ||||
| -rw-r--r-- | src/proc/task.rs | 102 |
2 files changed, 84 insertions, 34 deletions
diff --git a/src/proc/sched.rs b/src/proc/sched.rs index 8e86c7f..81648bf 100644 --- a/src/proc/sched.rs +++ b/src/proc/sched.rs @@ -1,17 +1,3 @@ use crate::proc::task::*; -use alloc::collections::linked_list::LinkedList; +use alloc::collections::VecDeque; // TODO the lifetime here is pretty much broken. Fix this later -pub struct Scheduler<'a> { - run_list: LinkedList<&'a Task>, -} - -impl<'a> Scheduler<'a> { - #[inline] - pub fn pop_front(&mut self) -> Option<&Task> { - self.run_list.pop_front() - } - #[inline] - pub fn push_back(&mut self, t: &'a Task) { - self.run_list.push_back(t); - } -} diff --git a/src/proc/task.rs b/src/proc/task.rs index 5cbe2f6..75a6c1a 100644 --- a/src/proc/task.rs +++ b/src/proc/task.rs @@ -2,21 +2,45 @@ use crate::arch::x86_64::arch_regs; use crate::defs::*; use crate::io::*; use core::arch::asm; +use core::ptr; /// currently only kernelSp and Context are important. /// the task struct will be placed on the starting addr (low addr) of the kernel stack. /// therefore we can retrive the task struct at anytime by masking the kernel stack -#[repr(C)] -#[repr(packed)] +/// NOTE: we don't use repr(C) or repr(packed) here pub struct Task { pub magic: u64, pub task_id: u32, + /// note that this points to the stack bottom (low addr) pub kernel_stack: u64, // pub user_stack: u64, - pub context: arch_regs::Context64, pub state: TaskState, + pub context: arch_regs::Context64, +} + +/// not to confuse with a integer TID. A TaskID identifies a task and __locate__ +/// it. In this case the TaskID wraps around the task struct's address. The +/// reason why the scheduler doesn't directly store Box<Task> (or alike) is that +/// the smart pointer types automatically drops the owned values when their +/// lifetime end. For now want to have manual control of when, where and how I +/// drop the Task because there could be more plans than just freeing the memory +pub struct TaskId(u64); + +impl TaskId { + pub fn new(addr: u64) -> Self { + Self { 0: addr } + } + + pub unsafe fn get_task_ref(&self) -> &Task { + &*(self.0 as *mut Task) + } + + pub unsafe fn get_task_ref_mut(&self) -> &mut Task { + &mut *(self.0 as *mut Task) + } } +#[derive(Debug)] pub enum TaskState { Run, Block, @@ -30,27 +54,67 @@ pub enum TaskState { #[no_mangle] pub extern "C" fn _task_entry() -> ! { println!("I'm Mr.Meeseeks, look at me~"); - unsafe { asm!("hlt") }; - panic!("shoud not reach"); + let t = Task::current().unwrap(); + println!( + "I am PID {}, kernel stack {:#X}, task state {:#X?}", + t.task_id, t.kernel_stack, t.state + ); + unsafe { asm!("cli; hlt") }; + panic!("should not reach"); } extern "C" { - fn context_swap(from_ctx: u64, to_ctx: u64); - fn context_swap_to(to_ctx: u64); + pub fn context_swap(from_ctx: u64, to_ctx: u64); + pub fn context_swap_to(to_ctx: u64); } impl Task { - /// create new task. This is tricky because the task struct sits at the - /// bottom of the kernel stack, so that you can get the current task by - /// masking the stack pointer. - /// 1. allocate the kernel stack with predefined size and make sure the - /// address is properly aligned - /// 2. cast a task struct onto the stack - /// 3. set whatever fields necessary in the task struct, including a magic - /// 4. create a new stack frame, and update the stack pointer in the context, - /// so that when first swapped to, the task will immediately "return to" - /// the _func_ptr - pub fn new(_id: u32, _func_ptr: u64) -> Self { - todo!() + /// TODO implement a proper ::new. For now use settle_on_stack instead + pub fn new(_id: u32, _kstack: u64, _func_ptr: u64) -> Self { + panic!( + "never ever try to manually create a task struct\n + gather the parts and call Task::settle_on_stack() instead" + ) + } + + /// unsafe because you have to make sure the stack pointer is valid + /// i.e. allocated through KStackAllocator. + #[inline(always)] + pub unsafe fn settle_on_stack<'a>(stack_addr: u64, t: Task) -> &'a mut Task { + ptr::write_volatile(stack_addr as *mut Task, t); + return &mut *(stack_addr as *mut Task); + } + + /// settle_on_stack and prepare_context must be called before switching to + /// the task. TODO: combine them into one single API + #[inline(always)] + pub fn prepare_context(&mut self, entry: u64) { + // this is like OOStuBS "toc_settle" + let mut sp = self.get_init_kernel_sp(); + // FIXME this is ugly + unsafe { + sp -= 8; + *(sp as *mut u64) = 0; + sp -= 8; + *(sp as *mut u64) = entry; + } + self.context.rsp = sp; + } + + // stack pointer may have alignment requirement. We do 8 bytes + #[inline(always)] + fn get_init_kernel_sp(&self) -> u64 { + let mut sp = self.kernel_stack + Mem::KERNEL_STACK_SIZE - 1; + sp = sp & (!0b111); + sp + } + + pub fn current<'a>() -> Option<&'a mut Task> { + let addr = arch_regs::get_sp() & !Mem::KERNEL_STACK_MASK; + let t = unsafe { &mut *(addr as *mut Task) }; + if t.magic != Mem::KERNEL_STACK_TASK_MAGIC { + return None; + } + return Some(t); } } |
