Nothing to see here, move along
1use core::arch::global_asm;
2
3use lancer_core::object_layout::SchedContextObject;
4use x86_64::structures::paging::PhysFrame;
5
6use crate::arch::syscall as arch_syscall;
7use crate::proc::context::CpuContext;
8use crate::proc::{ProcessManager, ProcessState};
9use crate::sync::SyncUnsafe;
10use crate::types::{MAX_CPUS, Pid};
11use crate::wcet::region::WcetTracker;
12
13const IA32_FS_BASE: u32 = 0xC000_0100;
14
15#[inline]
16pub(crate) fn read_fs_base() -> u64 {
17 unsafe {
18 let lo: u32;
19 let hi: u32;
20 core::arch::asm!(
21 "rdmsr",
22 in("ecx") IA32_FS_BASE,
23 out("eax") lo,
24 out("edx") hi,
25 options(nomem, nostack, preserves_flags),
26 );
27 ((hi as u64) << 32) | (lo as u64)
28 }
29}
30
31#[inline]
32pub(crate) fn write_fs_base(val: u64) {
33 unsafe {
34 core::arch::asm!(
35 "wrmsr",
36 in("ecx") IA32_FS_BASE,
37 in("eax") val as u32,
38 in("edx") (val >> 32) as u32,
39 options(nomem, nostack, preserves_flags),
40 );
41 }
42}
43
44unsafe extern "C" {
45 fn restore_context_and_iretq(ctx: *const CpuContext);
46}
47
48static SWITCH_WCET: [SyncUnsafe<WcetTracker<16>>; MAX_CPUS] =
49 [const { SyncUnsafe::new(WcetTracker::new("context_switch")) }; MAX_CPUS];
50
51#[allow(dead_code)]
52pub fn log_switch_wcet() {
53 let idx = arch_syscall::cpu_index();
54 unsafe { SWITCH_WCET[idx].as_ref_unchecked() }.log_summary();
55}
56
57pub fn enter_context(ctx: &CpuContext) -> ! {
58 unsafe { restore_context_and_iretq(ctx as *const CpuContext) };
59 unreachable!()
60}
61
62pub fn switch_to(
63 ptable: &mut ProcessManager,
64 from_pid: Pid,
65 to_pid: Pid,
66 frame: &mut CpuContext,
67) -> bool {
68 if from_pid == to_pid {
69 return false;
70 }
71
72 let idx = arch_syscall::cpu_index();
73 let tracker = unsafe { SWITCH_WCET[idx].as_mut_unchecked() };
74 let _guard = crate::wcet::region::WcetGuard::new(tracker);
75
76 let (from_was_running, old_pml4, new_pml4, sc_ctx, fs_base) = {
77 let Some(((from_sched, from_exec), (to_sched, to_exec))) =
78 ptable.pair_mut(from_pid, to_pid)
79 else {
80 return false;
81 };
82
83 from_exec.fs_base = read_fs_base();
84 from_exec.saved_context = *frame;
85 from_exec.seal_context();
86
87 from_sched.run_cpu = None;
88
89 let from_was_running = from_sched.state() == ProcessState::Running;
90 if from_was_running && from_sched.transition_to(ProcessState::Ready).is_err() {
91 crate::kprintln!(
92 "[sched] BUG: pid {} failed Running -> Ready",
93 from_pid.raw()
94 );
95 }
96
97 let old_pml4 = from_exec.pml4_phys.raw();
98
99 if to_sched.transition_to(ProcessState::Running).is_err() {
100 crate::kprintln!(
101 "[sched] BUG: pid {} failed -> Running (state={:?}), skipping switch",
102 to_pid.raw(),
103 to_sched.state()
104 );
105 if from_was_running {
106 let r = from_sched.transition_to(ProcessState::Running);
107 debug_assert!(r.is_ok());
108 }
109 return false;
110 }
111
112 let new_pml4 = to_exec.pml4_phys.raw();
113
114 to_exec.verify_context(to_pid);
115 *frame = to_exec.saved_context;
116 frame.validate_user(to_pid.raw());
117
118 arch_syscall::set_fpu_state_ptr(to_exec.fpu_state.data.as_mut_ptr());
119
120 to_sched.run_cpu = Some(idx as u16);
121
122 let sc_ctx = to_sched.sched_context();
123 let fs_base = to_exec.fs_base;
124
125 (from_was_running, old_pml4, new_pml4, sc_ctx, fs_base)
126 };
127
128 if from_was_running {
129 ptable.enqueue_ready(from_pid);
130 }
131
132 if let Some((sc_id, sc_gen)) = sc_ctx {
133 let now_us = crate::wcet::tsc::cycles_to_ns(crate::wcet::tsc::read_tsc()) / 1000;
134 let mut pool = crate::cap::pool::POOL.lock();
135 if let Ok(sc) = pool.write_as::<SchedContextObject>(sc_id, sc_gen) {
136 super::context::mark_dispatched(sc, now_us);
137 }
138 }
139
140 write_fs_base(fs_base);
141
142 arch_syscall::set_current_process(to_pid);
143 super::set_current(to_pid);
144
145 if old_pml4 != new_pml4 {
146 unsafe {
147 x86_64::registers::control::Cr3::write(
148 PhysFrame::containing_address(new_pml4),
149 x86_64::registers::control::Cr3Flags::empty(),
150 );
151 }
152 }
153
154 true
155}
156
157global_asm!(
158 r#"
159.global restore_context_and_iretq
160.type restore_context_and_iretq, @function
161restore_context_and_iretq:
162 cli
163
164 push qword ptr [rdi + 152]
165 push qword ptr [rdi + 128]
166 push qword ptr [rdi + 136]
167 push qword ptr [rdi + 144]
168 push qword ptr [rdi + 120]
169
170 mov rax, [rdi + 0]
171 mov rbx, [rdi + 8]
172 mov rcx, [rdi + 16]
173 mov rdx, [rdi + 24]
174 mov rsi, [rdi + 32]
175 mov rbp, [rdi + 48]
176 mov r8, [rdi + 56]
177 mov r9, [rdi + 64]
178 mov r10, [rdi + 72]
179 mov r11, [rdi + 80]
180 mov r12, [rdi + 88]
181 mov r13, [rdi + 96]
182 mov r14, [rdi + 104]
183 mov r15, [rdi + 112]
184 mov rdi, [rdi + 40]
185
186 push rax
187 mov rax, [rsp + 16]
188 test rax, 3
189 pop rax
190 jz .restore_no_swap
191 swapgs
192.restore_no_swap:
193 iretq
194
195.size restore_context_and_iretq, . - restore_context_and_iretq
196"#
197);