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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! This crate contains structures and routines for context switching 
//! when SSE/SIMD extensions are not active. 

use zerocopy::FromBytes;

/// The registers saved before a context switch and restored after a context switch.
///
/// Note: the order of the registers here MUST MATCH the order of 
/// registers popped in the [`restore_registers_regular!`] macro. 
#[derive(FromBytes)]
#[repr(C, packed)]
pub struct ContextRegular {
    rflags: usize,
    r15: usize, 
    r14: usize,
    r13: usize,
    r12: usize,
    rbp: usize,
    rbx: usize,
    /// The instruction pointer.
    ///
    /// `rip` is implicitly pushed onto the stack when a function is called, and
    /// popped off when returning. When a task's stack is set to an instance of
    /// [`ContextRegular`], [`context_switch_regular`] will execute `ret` when
    /// the stack pointer is pointing to the value of `rip`. Hence, the program
    /// will "return" to that address and continue executing.
    rip: usize,
}

impl ContextRegular {
    /// Creates a new [`ContextRegular`] struct that will cause the
    /// Task containing it to begin its execution at the given `rip`.
    pub fn new(rip: usize) -> ContextRegular {
        ContextRegular {
            // The ninth bit is the interrupt enable flag. When a task is first
            // run, interrupts should already be enabled.
            rflags: 1 << 9,
            r15: 0,
            r14: 0,
            r13: 0,
            r12: 0,
            rbp: 0,
            rbx: 0,
            rip,
        }
    }

    /// Sets the value of the first register to the given `value`.
    /// 
    /// This is useful for storing a value (e.g., task ID) in that register
    /// and then recovering it later with [`read_first_register()`].
    /// 
    /// On x86_64, this sets the `r15` register.
    pub fn set_first_register(&mut self, value: usize) {
        self.r15 = value;
    }
}

/// Reads the value of the first register from the actual CPU register hardware.
/// 
/// This can be called at any time, but is intended for use as the second half
/// of "saving and restoring" a register value.
/// The first half was a previous call to [`ContextRegular::set_first_register()`],
/// and the second half is a call to this function immediately after the original
/// `ContextRegular` has been used for switching to a new task for the first time.
/// 
/// Returns the current value held in the specified CPU register.
/// On x86_64, this reads the `r15` register.
#[naked]
pub extern "C" fn read_first_register() -> usize {
    // SAFE: simply reads and returns the value of `r15`.
    unsafe {
        core::arch::asm!(
            "mov rax, r15", // rax is used for return values on x86_64
            "ret",
            options(noreturn)
        )
    }
}


/// An assembly block for saving regular x86_64 registers
/// by pushing them onto the stack.
#[macro_export]
macro_rules! save_registers_regular {
    () => (
        // Save all general purpose registers into the previous task.
        r#"
            push rbx
            push rbp
            push r12
            push r13
            push r14
            push r15
            pushfq
        "#
    );
}


/// An assembly block for switching stacks,
/// which is the integral part of the actual context switching routine.
/// 
/// * The `rdi` register must contain a pointer to the previous task's stack pointer.
/// * The `rsi` register must contain the value of the next task's stack pointer.
#[macro_export]
macro_rules! switch_stacks {
    () => (
        // switch the stack pointers
        r#"
            mov [rdi], rsp
            mov rsp, rsi
        "#
    );
}


/// An assembly block for restoring regular x86_64 registers
/// by popping them off of the stack.
/// 
/// This assembly statement ends with an explicit `ret` instruction at the end,
/// which is the final component of a context switch operation. 
/// Note that this is intentional and required in order to accommodate 
/// the `noreturn` option is required by Rust's naked functions.
#[macro_export]
macro_rules! restore_registers_regular {
    () => (
        // Restore the next task's general purpose registers.
        r#" 
            popfq
            pop r15
            pop r14
            pop r13
            pop r12
            pop rbp
            pop rbx
            ret
        "#
    );
}


/// Switches context from a regular Task to another regular Task.
/// 
/// # Arguments
/// * First argument  (in `RDI`): mutable pointer to the previous task's stack pointer
/// * Second argument (in `RSI`): the value of the next task's stack pointer
/// 
/// # Safety
/// This function is unsafe because it changes the content on both task's stacks. 
#[naked]
pub unsafe extern "C" fn context_switch_regular(_prev_stack_pointer: *mut usize, _next_stack_pointer_value: usize) {
    // Since this is a naked function that expects its arguments in two registers,
    // you CANNOT place any log statements or other instructions here
    // before, in between, or after anything below.
    core::arch::asm!(
        save_registers_regular!(),
        switch_stacks!(),
        restore_registers_regular!(),
        options(noreturn)
    );
}