Skip to content

Commit 3fe37c6

Browse files
committed
Add context switch
1 parent ba56fc2 commit 3fe37c6

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

scheduler/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
pub mod task;
2+
3+
use crate::task::{Task, TaskContext, TASKS};
4+
use alloc::vec::Vec;
5+
6+
/// Create a new user task (simple bare-metal “function as process”)
7+
pub fn spawn(entry: extern "C" fn() -> !) {
8+
const STACK_SIZE: usize = 4096 * 4;
9+
let mut stack: Vec<u8> = Vec::with_capacity(STACK_SIZE);
10+
let stack_top = stack.as_mut_ptr() as u64 + STACK_SIZE as u64;
11+
12+
// Build initial context so that when we switch to it,
13+
// it “returns” into the function entry().
14+
let context = TaskContext {
15+
r15: 0,
16+
r14: 0,
17+
r13: 0,
18+
r12: 0,
19+
rbx: 0,
20+
rbp: 0,
21+
rip: entry as u64,
22+
};
23+
24+
let task = Task {
25+
context,
26+
stack_top,
27+
page_table: x86_64::registers::control::Cr3::read(),
28+
};
29+
TASKS.lock().push(task);
30+
}

scheduler/task.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use core::arch::asm;
2+
use x86_64::registers::control::Cr3;
3+
use x86_64::structures::idt::InterruptStackFrame;
4+
5+
// Saved CPU state for a context-switchable task
6+
#[repr(C)]
7+
pub struct TaskContext {
8+
// General-purpose registers (callee-saved first, per SysV ABI)
9+
r15: u64,
10+
r14: u64,
11+
r13: u64,
12+
r12: u64,
13+
rbx: u64,
14+
rbp: u64,
15+
// Instruction pointer to resume execution
16+
rip: u64,
17+
}
18+
19+
// Simplest task descriptor: just context + stack + page table
20+
pub struct Task {
21+
pub context: TaskContext,
22+
pub stack_top: u64,
23+
pub page_table: Cr3,
24+
}
25+
26+
// The global task list and current index
27+
use spin::Mutex;
28+
static TASKS: Mutex<Vec<Task>> = Mutex::new(Vec::new());
29+
static mut CURRENT: usize = 0;
30+
31+
/// Performs the low-level switch.
32+
/// Saves callee-saved regs + RIP into `old`, then loads from `new`.
33+
#[naked]
34+
pub unsafe extern "C" fn switch_context(old: *mut TaskContext, new: *const TaskContext) {
35+
asm!(
36+
// Save callee-saved registers onto the *old* context struct
37+
"mov [rdi + 0x00], r15",
38+
"mov [rdi + 0x08], r14",
39+
"mov [rdi + 0x10], r13",
40+
"mov [rdi + 0x18], r12",
41+
"mov [rdi + 0x20], rbx",
42+
"mov [rdi + 0x28], rbp",
43+
// Store the return RIP (address after the call to `switch_context`)
44+
"lea rax, [rip + 0f]",
45+
"mov [rdi + 0x30], rax",
46+
// Load new context
47+
"mov r15, [rsi + 0x00]",
48+
"mov r14, [rsi + 0x08]",
49+
"mov r13, [rsi + 0x10]",
50+
"mov r12, [rsi + 0x18]",
51+
"mov rbx, [rsi + 0x20]",
52+
"mov rbp, [rsi + 0x28]",
53+
"mov rax, [rsi + 0x30]",
54+
// Jump to new RIP
55+
"jmp rax",
56+
"0:",
57+
"ret",
58+
options(noreturn)
59+
)
60+
}

0 commit comments

Comments
 (0)