aboutsummaryrefslogtreecommitdiff
path: root/src/proc/task.rs
diff options
context:
space:
mode:
authorTianhao Wang <shrik3@mailbox.org>2024-06-10 20:47:54 +0200
committerTianhao Wang <shrik3@mailbox.org>2024-06-11 15:17:14 +0200
commit13e3eca8ac3f6bd8c7a8fddde8b1937757e358ee (patch)
treeeb2d4992d5692287c232ac729ad18f9690eaa153 /src/proc/task.rs
parent8aaad696463004a9e51d35e4c466c131b3402822 (diff)
proc: basic task/stack creation
Now we can do a simple context swap (without scheduler though)
Diffstat (limited to 'src/proc/task.rs')
-rw-r--r--src/proc/task.rs102
1 files changed, 83 insertions, 19 deletions
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);
}
}