1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use core::fmt;

use crate::arch::context::*;
use crate::interrupts::guard::GUARD;

/// The Thread is an object used by the scheduler.
pub struct Thread {
    /// Thread id, each thread should have a different one
    pub id: usize,
    /// Saved registers
    pub registers: Registers,
    /// Kernel stack
    kernel_stack: &'static mut [u32],
    /// If this thread has been killed
    pub exited: bool,
    /// Function pointer
    action: extern "C" fn(),
}

impl fmt::Debug for Thread {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Thread")
            .field("id", &self.id)
            .field("registers", &self.registers)
            .field("kernel_stack", &self.kernel_stack())
            .field("exited", &self.exited)
            .finish()
    }
}

impl Thread {
    /// Creates a new thread with the given action.
    ///
    /// If the `user_stack` is null, it is set up with the action as entry point.
    pub const fn new(action: extern "C" fn()) -> Self {
        Self {
            id: 0,
            registers: Registers::new(),
            kernel_stack: &mut [],
            exited: false,
            action,
        }
    }

    pub fn init(&mut self, kernel_stack: &'static mut [u32]) {
        // Setup stack
        self.registers.esp = {
            let len = kernel_stack.len();
            kernel_stack[len - 1] = self.action as u32; // arg 1
            kernel_stack[len - 2] = 0xbeefcace; // return addr
            kernel_stack[len - 3] = Self::kernel_kickoff as *const () as u32; // start function

            (&kernel_stack[len - 3]) as *const u32 as u32
        };
        self.kernel_stack = kernel_stack;
    }

    /// Returns the top of the kernel stack pointer.
    pub fn kernel_stack(&self) -> *mut () {
        self.kernel_stack.as_ptr_range().end.cast_mut().cast()
    }

    /// Kickoff function that is called in kernel mode.
    ///
    /// If the thread was configured with a user stack, `user_kickoff` is called in user mode.
    /// Otherwise the `action` is executed directly in the kernel.
    extern "C" fn kernel_kickoff(action: extern "C" fn()) {
        GUARD.leave();

        (action)();

        GUARD.run(|g| g.scheduler.exit());
    }

    /// Switches from the currently running thread to the `next` one.
    ///
    /// The values currently present in the callee-saved registers will be
    /// stored in this threads context-structure, the corresponding values
    /// belonging to `next` thread will be loaded.
    pub fn resume(&mut self, next: &Self) {
        unsafe { context_switch(&mut self.registers, &next.registers) };
    }

    /// Activates the first thread on this CPU.
    ///
    /// Calling the method starts the first thread on the calling CPU.
    /// From then on, [Thread::resume] must be used all subsequent context
    /// switches.
    pub fn go(&self) {
        unsafe { context_launch(&self.registers) };
    }
}