1use crate::context::{current_trap_rsp_offset, current_user_rsp_offset};
2use crate::trap::{interrupt_handler, syscall_handler};
3use alloc::boxed::Box;
4use alloc::string::String;
5use alloc::sync::Arc;
6use alloc::vec;
7use alloc::vec::Vec;
8use bitfield_struct::bitfield;
9use core::arch::{asm, global_asm};
10use core::fmt::Write;
11use core::mem::{transmute, zeroed};
12use x86_64::{
13 Dpl, Efer, Gdtr, Rflags, SegmentDescriptor, SegmentSelector, Star, Tss64, TssDescriptor,
14};
15
16pub const GDT_KERNEL_CS: SegmentSelector = SegmentSelector::new().with_si(3);
17pub const GDT_KERNEL_DS: SegmentSelector = SegmentSelector::new().with_si(4);
18pub const GDT_USER_CS32: SegmentSelector = SegmentSelector::new().with_si(5).with_rpl(Dpl::Ring3);
19
20pub fn identify_cpu() -> CpuInfo {
27 let mut flags: u64;
30
31 unsafe { asm!("pushfq", "pop {v}", v = out(reg) flags, options(nomem, preserves_flags)) };
32
33 let flags = Rflags::from_bits(flags);
35
36 if !flags.id() {
37 panic!("CPUID instruction is not available");
38 }
39
40 let mut cpu_vendor = String::with_capacity(128);
42 let r = unsafe { core::arch::x86_64::__cpuid(0) };
43 let cpu_high = r.eax;
44 let mut buf = [0u8; 12];
45
46 assert!(cpu_high >= 1);
47
48 buf[..4].copy_from_slice(&r.ebx.to_le_bytes());
49 buf[4..8].copy_from_slice(&r.edx.to_le_bytes());
50 buf[8..].copy_from_slice(&r.ecx.to_le_bytes());
51
52 write!(cpu_vendor, "{}", core::str::from_utf8(&buf).unwrap()).unwrap();
53
54 let r = unsafe { core::arch::x86_64::__cpuid(1) };
56 let cpu_id = r.eax;
57
58 CpuInfo { cpu_vendor, cpu_id }
60}
61
62pub unsafe fn setup_main_cpu(cpu: CpuInfo) -> Arc<ArchConfig> {
65 let mut gdt = vec![
67 SegmentDescriptor::new(),
69 SegmentDescriptor::new(),
71 SegmentDescriptor::new(),
73 SegmentDescriptor::new()
75 .with_ty(0b1000) .with_s(true) .with_p(true)
78 .with_l(true), SegmentDescriptor::new()
81 .with_ty(0b0010) .with_s(true) .with_p(true),
84 SegmentDescriptor::new(),
86 SegmentDescriptor::new(),
88 SegmentDescriptor::new(),
90 ];
91
92 let trap_rsp = Box::new([0u8; 1024 * 128]);
94 let trap_rsp = Box::leak(trap_rsp);
95 let tss = unsafe { push_tss(&mut gdt, trap_rsp) };
96
97 let limit = (size_of::<SegmentDescriptor>() * gdt.len() - 1)
99 .try_into()
100 .unwrap();
101
102 gdt.shrink_to_fit();
103
104 unsafe {
105 set_gdtr(
106 &Gdtr {
107 limit,
108 addr: gdt.leak().as_ptr(),
109 },
110 GDT_KERNEL_CS,
111 GDT_KERNEL_DS,
112 )
113 };
114
115 unsafe {
117 asm!(
118 "ltr {v:x}",
119 v = in(reg) tss.into_bits(),
120 options(preserves_flags, nostack)
121 )
122 };
123
124 const IDT_LEN: usize = 256;
126 static mut IDT: [GateDescriptor; IDT_LEN] = unsafe { zeroed() };
127
128 let set_idt = |n: usize, f: unsafe extern "C" fn() -> !, ty, dpl, ist| {
129 let f = f as usize;
130 let d = GateDescriptor::new()
131 .with_offset1(f as u16)
132 .with_selector(GDT_KERNEL_CS)
133 .with_ist(ist)
134 .with_ty(ty)
135 .with_dpl(dpl)
136 .with_p(true)
137 .with_offset2((f >> 16).try_into().unwrap());
138
139 unsafe { IDT[n] = d };
140 };
141
142 set_idt(3, Xbpt, 0b1110, Dpl::Ring3, 0);
143
144 let limit = (size_of::<GateDescriptor>() * IDT_LEN - 1)
146 .try_into()
147 .unwrap();
148 let addr = (&raw const IDT).cast();
149 let idtr = Idtr { limit, addr };
150
151 unsafe {
152 asm!(
153 "lidt qword ptr [{v}]",
154 v = in(reg) &idtr,
155 options(preserves_flags, nostack)
156 )
157 };
158
159 let star = Star::new()
161 .with_syscall_sel(GDT_KERNEL_CS)
162 .with_sysret_sel(GDT_USER_CS32)
163 .into_bits()
164 .try_into()
165 .unwrap();
166
167 unsafe { wrmsr(0xC0000081, star) };
168
169 unsafe { wrmsr(0xC0000082, syscall_entry64 as usize) };
171 unsafe { wrmsr(0xC0000083, syscall_entry32 as usize) };
172
173 let mask = Rflags::new()
175 .with_cf(true)
176 .with_tf(true)
177 .with_if(true) .with_df(true)
179 .with_nt(true)
180 .into_bits()
181 .try_into()
182 .unwrap();
183
184 unsafe { wrmsr(0xC0000084, mask) };
185
186 let efer = Efer::new()
188 .with_sce(true) .with_lme(true) .with_lma(true) .into_bits()
192 .try_into()
193 .unwrap();
194
195 unsafe { wrmsr(0xC0000080, efer) };
196
197 let len = unsafe { secondary_end.as_ptr().offset_from(secondary_start.as_ptr()) }
199 .try_into()
200 .unwrap();
201
202 Arc::new(ArchConfig {
203 cpu,
204 trap_rsp: trap_rsp.as_mut_ptr() as usize,
205 secondary_start: unsafe { core::slice::from_raw_parts(secondary_start.as_ptr(), len) },
206 })
207}
208
209pub unsafe fn wrmsr(reg: u32, val: usize) {
210 unsafe {
211 asm!(
212 "wrmsr",
213 in("ecx") reg,
214 in("edx") val >> 32,
215 in("eax") val,
216 options(nomem, preserves_flags, nostack)
217 )
218 };
219}
220
221unsafe fn push_tss<const L: usize>(
224 gdt: &mut Vec<SegmentDescriptor>,
225 trap_rsp: *mut [u8; L],
226) -> SegmentSelector {
227 let tss = Box::new(Tss64::default());
229 let tss = Box::leak(tss);
230
231 unsafe { tss.rsp0 = (trap_rsp.add(1) as usize).try_into().unwrap() }; let si = gdt.len();
235
236 gdt.push(SegmentDescriptor::new());
237 gdt.push(SegmentDescriptor::new());
238
239 let desc: &mut TssDescriptor = unsafe { transmute(&mut gdt[si]) };
241 let base = tss as *mut Tss64 as usize;
242
243 desc.set_limit1((size_of::<Tss64>() - 1).try_into().unwrap());
244 desc.set_base1((base & 0xFFFFFF).try_into().unwrap());
245 desc.set_base2((base >> 24).try_into().unwrap());
246 desc.set_ty(0b1001); desc.set_p(true);
248
249 SegmentSelector::new().with_si(si.try_into().unwrap())
250}
251
252unsafe extern "C" {
253 safe static secondary_start: [u8; 0];
254 safe static secondary_end: [u8; 0];
255
256 fn set_gdtr(v: &Gdtr, code: SegmentSelector, data: SegmentSelector);
257 fn Xbpt() -> !;
258 fn syscall_entry64() -> !;
259 fn syscall_entry32() -> !;
260}
261
262global_asm!(
264 "set_gdtr:",
265 "lgdt qword ptr [rdi]",
266 "mov ds, dx",
267 "mov es, dx",
268 "mov fs, dx",
269 "mov gs, dx",
270 "mov ss, dx",
271 "pop rax", "push rsi", "push rax",
274 "retfq" );
276
277global_asm!(
279 "Xbpt:", "sub rsp, 0x80", "mov dword ptr [rsp+0x78], 3", "mov rdi, rsp",
283 "call {f}",
284 f = sym interrupt_handler
285);
286
287global_asm!(
289 "syscall_entry64:",
290 "swapgs",
291 "mov gs:[{user_rsp}], rsp", "mov rsp, gs:[{trap_rsp}]",
293 "call {handler}",
294 "ud2",
295 user_rsp = const current_user_rsp_offset(),
296 trap_rsp = const current_trap_rsp_offset(),
297 handler = sym syscall_handler
298);
299
300global_asm!("syscall_entry32:", "ud2");
302
303global_asm!("secondary_start:", "ud2", "secondary_end:");
305
306#[repr(C, packed)]
311struct Idtr {
312 limit: u16,
313 addr: *const GateDescriptor,
314}
315
316#[bitfield(u128)]
321struct GateDescriptor {
322 offset1: u16,
323 #[bits(16)]
324 selector: SegmentSelector,
325 #[bits(3)]
326 ist: u8,
327 #[bits(5)]
328 __: u8,
329 #[bits(4)]
330 ty: u8,
331 __: bool,
332 #[bits(2)]
333 dpl: Dpl,
334 p: bool,
335 #[bits(48)]
336 offset2: u64,
337 __: u32,
338}
339
340pub struct CpuInfo {
342 pub cpu_vendor: String, pub cpu_id: u32, }
345
346pub struct ArchConfig {
348 pub cpu: CpuInfo,
349 pub trap_rsp: usize,
350 pub secondary_start: &'static [u8],
351}