rstubs/arch/
context.rs

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

use core::arch::naked_asm;

use super::gdt::SegmentSelector;

/// Switch to the user by faking a return from an interrupt.
///
/// `iretd` expects the following stack layout:
///
/// | Offset | Value       |
/// | ------ | ----------- |
/// | 16     | SS (Ring 3) |
/// | 12     | ESP         |
/// | 8      | EFLAGS      |
/// | 4      | CS (Ring 3) |
/// | 0      | EIP         |
///
/// The top of stack (ESP) points the EIP at the bottom (stack grows downwards).
pub unsafe extern "C" fn switch_to_user(
    ds: SegmentSelector,
    sp: *mut u32,
    cs: SegmentSelector,
    ip: *mut (),
    syscall_ret: u32,
) -> ! {
    todo!("BST B1 - Implement ring switch")
}

/// Starts the thread for the first time
#[naked]
pub unsafe extern "C" fn launch(thread: &Registers) {
    naked_asm!(
        // args
        "mov eax,[esp+4]",
        // switch stack
        "mov esp,[eax+16]",
        // load context
        "mov ebx,[eax+0]",
        "mov esi,[eax+4]",
        "mov edi,[eax+8]",
        "mov ebp,[eax+12]",
        "ret",
    );
}

/// Switches between two threads
#[naked]
pub unsafe extern "C" fn switch(current: &mut Registers, next: &Registers) {
    naked_asm!(
        // args
        "mov eax,[esp+4]",
        "mov ecx,[esp+8]",
        // 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]",
        "ret",
    );
}

/// Non-volatile registers that have to survive function calls.
///
/// See <https://wiki.osdev.org/Calling_Conventions>
#[derive(Copy, Clone, Debug)]
#[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() -> Self {
        Self {
            ebx: 0,
            esi: 0,
            edi: 0,
            ebp: 0,
            esp: 0,
        }
    }
}