1use crate::context::{current_trap_rsp_offset, current_user_rsp_offset};
2use crate::trap::{interrupt_handler, syscall_handler};
3use alloc::boxed::Box;
4use alloc::sync::Arc;
5use alloc::vec;
6use alloc::vec::Vec;
7use bitfield_struct::bitfield;
8use core::arch::{asm, global_asm};
9use core::mem::{transmute, zeroed};
10use core::ptr::addr_of;
11use x86_64::{Dpl, Efer, Rflags, SegmentDescriptor, SegmentSelector, Star};
12
13pub const GDT_KERNEL_CS: SegmentSelector = SegmentSelector::new().with_si(3);
14pub const GDT_KERNEL_DS: SegmentSelector = SegmentSelector::new().with_si(4);
15pub const GDT_USER_CS32: SegmentSelector = SegmentSelector::new().with_si(5).with_rpl(Dpl::Ring3);
16
17pub unsafe fn setup_main_cpu() -> Arc<ArchConfig> {
20 let mut gdt = vec![
22 SegmentDescriptor::new(),
24 SegmentDescriptor::new(),
26 SegmentDescriptor::new(),
28 SegmentDescriptor::new()
30 .with_ty(0b1000) .with_s(true) .with_p(true)
33 .with_l(true), SegmentDescriptor::new()
36 .with_ty(0b0010) .with_s(true) .with_p(true),
39 SegmentDescriptor::new(),
41 SegmentDescriptor::new(),
43 SegmentDescriptor::new(),
45 ];
46
47 let trap_rsp = Box::new([0u8; 1024 * 128]);
49 let trap_rsp = Box::leak(trap_rsp);
50 let tss = unsafe { push_tss(&mut gdt, trap_rsp) };
51
52 let limit = (size_of::<SegmentDescriptor>() * gdt.len() - 1)
54 .try_into()
55 .unwrap();
56
57 gdt.shrink_to_fit();
58
59 unsafe {
60 set_gdtr(
61 &Gdtr {
62 limit,
63 addr: gdt.leak().as_ptr(),
64 },
65 GDT_KERNEL_CS,
66 GDT_KERNEL_DS,
67 )
68 };
69
70 unsafe {
72 asm!(
73 "ltr {v:x}",
74 v = in(reg) tss.into_bits(),
75 options(preserves_flags, nostack)
76 )
77 };
78
79 const IDT_LEN: usize = 256;
81 static mut IDT: [GateDescriptor; IDT_LEN] = unsafe { zeroed() };
82
83 let set_idt = |n: usize, f: unsafe extern "C" fn() -> !, ty, dpl, ist| {
84 let f = f as usize;
85 let d = GateDescriptor::new()
86 .with_offset1(f as u16)
87 .with_selector(GDT_KERNEL_CS)
88 .with_ist(ist)
89 .with_ty(ty)
90 .with_dpl(dpl)
91 .with_p(true)
92 .with_offset2((f >> 16).try_into().unwrap());
93
94 unsafe { IDT[n] = d };
95 };
96
97 set_idt(3, Xbpt, 0b1110, Dpl::Ring3, 0);
98
99 let limit = (size_of::<GateDescriptor>() * IDT_LEN - 1)
101 .try_into()
102 .unwrap();
103 let addr = (&raw const IDT).cast();
104 let idtr = Idtr { limit, addr };
105
106 unsafe {
107 asm!(
108 "lidt qword ptr [{v}]",
109 v = in(reg) &idtr,
110 options(preserves_flags, nostack)
111 )
112 };
113
114 let star = Star::new()
116 .with_syscall_sel(GDT_KERNEL_CS)
117 .with_sysret_sel(GDT_USER_CS32)
118 .into_bits()
119 .try_into()
120 .unwrap();
121
122 unsafe { wrmsr(0xC0000081, star) };
123
124 unsafe { wrmsr(0xC0000082, syscall_entry64 as usize) };
126 unsafe { wrmsr(0xC0000083, syscall_entry32 as usize) };
127
128 let mask = Rflags::new()
130 .with_cf(true)
131 .with_tf(true)
132 .with_if(true) .with_df(true)
134 .with_nt(true)
135 .into_bits()
136 .try_into()
137 .unwrap();
138
139 unsafe { wrmsr(0xC0000084, mask) };
140
141 let efer = Efer::new()
143 .with_sce(true) .with_lme(true) .with_lma(true) .into_bits()
147 .try_into()
148 .unwrap();
149
150 unsafe { wrmsr(0xC0000080, efer) };
151
152 let len = unsafe { secondary_end.as_ptr().offset_from(secondary_start.as_ptr()) }
154 .try_into()
155 .unwrap();
156
157 Arc::new(ArchConfig {
158 trap_rsp: trap_rsp.as_mut_ptr() as usize,
159 secondary_start: unsafe { core::slice::from_raw_parts(secondary_start.as_ptr(), len) },
160 })
161}
162
163pub unsafe fn wrmsr(reg: u32, val: usize) {
164 unsafe {
165 asm!(
166 "wrmsr",
167 in("ecx") reg,
168 in("edx") val >> 32,
169 in("eax") val,
170 options(nomem, preserves_flags, nostack)
171 )
172 };
173}
174
175unsafe fn push_tss<const L: usize>(
178 gdt: &mut Vec<SegmentDescriptor>,
179 trap_rsp: *mut [u8; L],
180) -> SegmentSelector {
181 static mut TSS: Tss = unsafe { zeroed() };
183
184 unsafe { TSS.rsp0 = trap_rsp.add(1) as usize }; let tss = gdt.len();
188
189 gdt.push(SegmentDescriptor::new());
190 gdt.push(SegmentDescriptor::new());
191
192 let desc: &mut TssDescriptor = unsafe { transmute(&mut gdt[tss]) };
194 let base = addr_of!(TSS) as usize;
195
196 desc.set_limit1((size_of::<Tss>() - 1).try_into().unwrap());
197 desc.set_base1((base & 0xFFFFFF).try_into().unwrap());
198 desc.set_base2((base >> 24).try_into().unwrap());
199 desc.set_ty(0b1001); desc.set_p(true);
201
202 SegmentSelector::new().with_si(tss.try_into().unwrap())
203}
204
205unsafe extern "C" {
206 safe static secondary_start: [u8; 0];
207 safe static secondary_end: [u8; 0];
208
209 fn set_gdtr(v: &Gdtr, code: SegmentSelector, data: SegmentSelector);
210 fn Xbpt() -> !;
211 fn syscall_entry64() -> !;
212 fn syscall_entry32() -> !;
213}
214
215global_asm!(
217 "set_gdtr:",
218 "lgdt qword ptr [rdi]",
219 "mov ds, dx",
220 "mov es, dx",
221 "mov fs, dx",
222 "mov gs, dx",
223 "mov ss, dx",
224 "pop rax", "push rsi", "push rax",
227 "retfq" );
229
230global_asm!(
232 "Xbpt:", "sub rsp, 0x80", "mov dword ptr [rsp+0x78], 3", "mov rdi, rsp",
236 "call {f}",
237 f = sym interrupt_handler
238);
239
240global_asm!(
242 "syscall_entry64:",
243 "swapgs",
244 "mov gs:[{user_rsp}], rsp", "mov rsp, gs:[{trap_rsp}]",
246 "call {handler}",
247 "ud2",
248 user_rsp = const current_user_rsp_offset(),
249 trap_rsp = const current_trap_rsp_offset(),
250 handler = sym syscall_handler
251);
252
253global_asm!("syscall_entry32:", "ud2");
255
256global_asm!("secondary_start:", "ud2", "secondary_end:");
258
259#[repr(C, packed)]
264struct Gdtr {
265 limit: u16,
266 addr: *const SegmentDescriptor,
267}
268
269#[bitfield(u128)]
273struct TssDescriptor {
274 limit1: u16,
275 #[bits(24)]
276 base1: u32,
277 #[bits(4)]
278 ty: u8,
279 #[bits(access = None)]
280 s: bool,
281 #[bits(2)]
282 dpl: Dpl,
283 p: bool,
284 #[bits(4)]
285 limit2: u8,
286 avl: bool,
287 #[bits(2)]
288 __: u8,
289 g: bool,
290 #[bits(40)]
291 base2: u64,
292 __: u32,
293}
294
295#[repr(C, packed)]
300#[derive(Default)]
301struct Tss {
302 reserved1: u32,
303 rsp0: usize,
304 rsp1: usize,
305 rsp2: usize,
306 reserved2: u64,
307 ist1: usize,
308 ist2: usize,
309 ist3: usize,
310 ist4: usize,
311 ist5: usize,
312 ist6: usize,
313 ist7: usize,
314 reserved3: u64,
315 reserved4: u16,
316 io_map_base_address: u16,
317}
318
319#[repr(C, packed)]
324struct Idtr {
325 limit: u16,
326 addr: *const GateDescriptor,
327}
328
329#[bitfield(u128)]
334struct GateDescriptor {
335 offset1: u16,
336 #[bits(16)]
337 selector: SegmentSelector,
338 #[bits(3)]
339 ist: u8,
340 #[bits(5)]
341 __: u8,
342 #[bits(4)]
343 ty: u8,
344 __: bool,
345 #[bits(2)]
346 dpl: Dpl,
347 p: bool,
348 #[bits(48)]
349 offset2: u64,
350 __: u32,
351}
352
353pub struct ArchConfig {
355 pub trap_rsp: usize,
356 pub secondary_start: &'static [u8],
357}