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
//! Context and ring switching

use core::{arch::asm, fmt};

use crate::arch::regs::Flags;

use super::gdt::SegmentSelector;

/// Prepare for return from interrupt to user,
/// the expected current stack layout is:
///
/// ```text
///  _____________________________
/// | User Data Segment Selector  |
/// | User Stack Pointer          |
/// | EFLAGS (Interrupts enabled) |
/// | User Code Segment Selector  |
/// | Target Instruction Pointer  |
/// ```
pub extern "C" fn switch_to_user(
    ds: SegmentSelector,
    sp: *mut u32,
    cs: SegmentSelector,
    ip: *mut (),
) -> ! {
    unsafe {
        let eflags = u32::from(Flags::new().with_interrupt(true));

        // Note: use "iretd", "iret" emits the wrong instruction!
        todo!("A1: Implement ring switch")
    }
}

/// Starts the thread for the first time
#[inline(never)]
pub unsafe extern "C" fn context_launch(thread: &Registers) {
    asm!(
        "mov esp,[eax+16]",
        // load context
        "mov ebx,[eax+0]",
        "mov esi,[eax+4]",
        "mov edi,[eax+8]",
        "mov ebp,[eax+12]",
        in("eax") thread,
        options(nostack)
    )
}

/// Switches between two threads
#[inline(never)]
pub unsafe extern "C" fn context_switch(current: &mut Registers, next: &Registers) {
    asm!(
        // save context
        "mov [eax+0],ebx",
        "mov [eax+4],esi",
        "mov [eax+8],edi",
        "mov [eax+12],ebp",
        "mov [eax+16],esp",

        // switch stack
        "mov esp,[ecx+16]",
        // load context
        "mov ebx,[ecx+0]",
        "mov esi,[ecx+4]",
        "mov edi,[ecx+8]",
        "mov ebp,[ecx+12]",
        in("eax") current,
        in("ecx") next,
        options(nostack)
    )
}

/// Non-volatile registers that have to survive function calls.
///
/// See <https://wiki.osdev.org/Calling_Conventions>
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Registers {
    pub ebx: u32,
    pub esi: u32,
    pub edi: u32,
    pub ebp: u32,
    pub esp: u32,
}

impl Registers {
    pub const fn new() -> Registers {
        Registers {
            ebx: 0,
            esi: 0,
            edi: 0,
            ebp: 0,
            esp: 0,
        }
    }
}

impl fmt::Debug for Registers {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let Self {
            ebx,
            esi,
            edi,
            ebp,
            esp,
        } = self;
        write!(
            f,
            "Registers {{ ebx: {ebx:x}, esi: {esi:x}, \
            edi: {edi:x}, ebp: {ebp:x}, esp: {esp:x} }}"
        )
    }
}