1use crate::config::{Config, Dipsw};
2use crate::context::{current_trap_rsp_offset, current_user_rsp_offset};
3use crate::trap::{interrupt_handler, syscall_handler};
4use alloc::boxed::Box;
5use alloc::string::String;
6use alloc::sync::Arc;
7use alloc::vec;
8use alloc::vec::Vec;
9use bitfield_struct::bitfield;
10use config::KernelMap;
11use core::arch::{asm, global_asm, naked_asm};
12use core::fmt::Write;
13use core::mem::{transmute, zeroed};
14use x86_64::{
15 Dpl, Efer, Gdtr, Rflags, SegmentDescriptor, SegmentSelector, Star, Tss64, TssDescriptor,
16};
17
18pub const GDT_KERNEL_CS: SegmentSelector = SegmentSelector::new().with_si(3);
19pub const GDT_KERNEL_DS: SegmentSelector = SegmentSelector::new().with_si(4);
20pub const GDT_USER_CS32: SegmentSelector = SegmentSelector::new().with_si(5).with_rpl(Dpl::Ring3);
21
22pub fn identify_cpu() -> CpuInfo {
29 let mut flags: u64;
32
33 unsafe { asm!("pushfq", "pop {v}", v = out(reg) flags, options(nomem, preserves_flags)) };
34
35 let flags = Rflags::from_bits(flags);
37
38 if !flags.id() {
39 panic!("CPUID instruction is not available");
40 }
41
42 let mut cpu_vendor = String::with_capacity(128);
44 let r = unsafe { core::arch::x86_64::__cpuid(0) };
45 let cpu_high = r.eax;
46 let mut buf = [0u8; 12];
47
48 assert!(cpu_high >= 1);
49
50 buf[..4].copy_from_slice(&r.ebx.to_le_bytes());
51 buf[4..8].copy_from_slice(&r.edx.to_le_bytes());
52 buf[8..].copy_from_slice(&r.ecx.to_le_bytes());
53
54 write!(cpu_vendor, "{}", core::str::from_utf8(&buf).unwrap()).unwrap();
55
56 let r = unsafe { core::arch::x86_64::__cpuid(1) };
58 let cpu_id = r.eax;
59
60 CpuInfo { cpu_vendor, cpu_id }
62}
63
64pub unsafe fn setup_main_cpu(
67 config: &Config,
68 cpu: CpuInfo,
69 map: &'static KernelMap,
70) -> Arc<ArchConfig> {
71 let mut gdt = vec![
73 SegmentDescriptor::new(),
75 SegmentDescriptor::new(),
77 SegmentDescriptor::new(),
79 SegmentDescriptor::new()
81 .with_ty(0b1000) .with_s(true) .with_p(true)
84 .with_l(true), SegmentDescriptor::new()
87 .with_ty(0b0010) .with_s(true) .with_p(true),
90 SegmentDescriptor::new(),
92 SegmentDescriptor::new(),
94 SegmentDescriptor::new(),
96 ];
97
98 let trap_rsp = Box::new([0u8; 1024 * 128]);
100 let trap_rsp = Box::leak(trap_rsp);
101 let tss = unsafe { push_tss(&mut gdt, trap_rsp) };
102
103 let limit = (size_of::<SegmentDescriptor>() * gdt.len() - 1)
105 .try_into()
106 .unwrap();
107
108 gdt.shrink_to_fit();
109
110 unsafe {
111 set_gdtr(
112 &Gdtr {
113 limit,
114 addr: gdt.leak().as_ptr(),
115 },
116 GDT_KERNEL_CS,
117 GDT_KERNEL_DS,
118 )
119 };
120
121 unsafe {
123 asm!(
124 "ltr {v:x}",
125 v = in(reg) tss.into_bits(),
126 options(preserves_flags, nostack)
127 )
128 };
129
130 const IDT_LEN: usize = 256;
132 static mut IDT: [GateDescriptor; IDT_LEN] = unsafe { zeroed() };
133
134 let set_idt = |n: usize, f: unsafe extern "C" fn() -> !, ty, dpl, ist| {
135 let f = f as usize;
136 let d = GateDescriptor::new()
137 .with_offset1(f as u16)
138 .with_selector(GDT_KERNEL_CS)
139 .with_ist(ist)
140 .with_ty(ty)
141 .with_dpl(dpl)
142 .with_p(true)
143 .with_offset2((f >> 16).try_into().unwrap());
144
145 unsafe { IDT[n] = d };
146 };
147
148 set_idt(3, Xbpt, 0b1110, Dpl::Ring3, 0);
149
150 let limit = (size_of::<GateDescriptor>() * IDT_LEN - 1)
152 .try_into()
153 .unwrap();
154 let addr = (&raw const IDT).cast();
155 let idtr = Idtr { limit, addr };
156
157 unsafe {
158 asm!(
159 "lidt qword ptr [{v}]",
160 v = in(reg) &idtr,
161 options(preserves_flags, nostack)
162 )
163 };
164
165 let star = Star::new()
167 .with_syscall_sel(GDT_KERNEL_CS)
168 .with_sysret_sel(GDT_USER_CS32)
169 .into_bits()
170 .try_into()
171 .unwrap();
172
173 unsafe { wrmsr(0xC0000081, star) };
174
175 unsafe { wrmsr(0xC0000082, syscall_entry64 as *const () as usize) };
177 unsafe { wrmsr(0xC0000083, syscall_entry32 as *const () as usize) };
178
179 let mask = Rflags::new()
181 .with_cf(true)
182 .with_tf(true)
183 .with_if(true) .with_df(true)
185 .with_nt(true)
186 .into_bits()
187 .try_into()
188 .unwrap();
189
190 unsafe { wrmsr(0xC0000084, mask) };
191
192 let efer = Efer::new()
194 .with_sce(true) .with_lme(true) .with_lma(true) .into_bits()
198 .try_into()
199 .unwrap();
200
201 unsafe { wrmsr(0xC0000080, efer) };
202
203 init_pmap(config, map);
206
207 let len = unsafe { secondary_end.as_ptr().offset_from(secondary_start.as_ptr()) }
209 .try_into()
210 .unwrap();
211
212 Arc::new(ArchConfig {
213 cpu,
214 trap_rsp: trap_rsp.as_mut_ptr() as usize,
215 secondary_start: unsafe { core::slice::from_raw_parts(secondary_start.as_ptr(), len) },
216 })
217}
218
219pub unsafe fn wrmsr(reg: u32, val: usize) {
220 unsafe {
221 asm!(
222 "wrmsr",
223 in("ecx") reg,
224 in("edx") val >> 32,
225 in("eax") val,
226 options(nomem, preserves_flags, nostack)
227 )
228 };
229}
230
231fn init_pmap(config: &Config, _: &'static KernelMap) {
238 if config.is_allow_disabling_aslr() && config.dipsw(Dipsw::DisabledKaslr) {
239 todo!()
240 } else {
241 }
244}
245
246unsafe fn push_tss<const L: usize>(
249 gdt: &mut Vec<SegmentDescriptor>,
250 trap_rsp: *mut [u8; L],
251) -> SegmentSelector {
252 let tss = Box::new(Tss64::default());
254 let tss = Box::leak(tss);
255
256 unsafe { tss.rsp0 = (trap_rsp.add(1) as usize).try_into().unwrap() }; let si = gdt.len();
260
261 gdt.push(SegmentDescriptor::new());
262 gdt.push(SegmentDescriptor::new());
263
264 let desc: &mut TssDescriptor = unsafe { transmute(&mut gdt[si]) };
266 let base = tss as *mut Tss64 as usize;
267
268 desc.set_limit1((size_of::<Tss64>() - 1).try_into().unwrap());
269 desc.set_base1((base & 0xFFFFFF).try_into().unwrap());
270 desc.set_base2((base >> 24).try_into().unwrap());
271 desc.set_ty(0b1001); desc.set_p(true);
273
274 SegmentSelector::new().with_si(si.try_into().unwrap())
275}
276
277#[unsafe(naked)]
284unsafe extern "C" fn set_gdtr(v: &Gdtr, code: SegmentSelector, data: SegmentSelector) {
285 naked_asm!(
286 "lgdt qword ptr [rdi]",
287 "mov ds, dx",
288 "mov es, dx",
289 "mov fs, dx",
290 "mov gs, dx",
291 "mov ss, dx",
292 "pop rax", "push rsi", "push rax",
295 "retfq" )
297}
298
299unsafe extern "C" {
300 safe static secondary_start: [u8; 0];
301 safe static secondary_end: [u8; 0];
302
303 fn Xbpt() -> !;
304 fn syscall_entry64() -> !;
305 fn syscall_entry32() -> !;
306}
307
308global_asm!(
310 "Xbpt:", "sub rsp, 0x80", "mov dword ptr [rsp+0x78], 3", "mov rdi, rsp",
314 "call {f}",
315 f = sym interrupt_handler
316);
317
318global_asm!(
320 "syscall_entry64:",
321 "swapgs",
322 "mov gs:[{user_rsp}], rsp", "mov rsp, gs:[{trap_rsp}]",
324 "call {handler}",
325 "ud2",
326 user_rsp = const current_user_rsp_offset(),
327 trap_rsp = const current_trap_rsp_offset(),
328 handler = sym syscall_handler
329);
330
331global_asm!("syscall_entry32:", "ud2");
333
334global_asm!("secondary_start:", "ud2", "secondary_end:");
336
337#[repr(C, packed)]
342struct Idtr {
343 limit: u16,
344 addr: *const GateDescriptor,
345}
346
347#[bitfield(u128)]
352struct GateDescriptor {
353 offset1: u16,
354 #[bits(16)]
355 selector: SegmentSelector,
356 #[bits(3)]
357 ist: u8,
358 #[bits(5)]
359 __: u8,
360 #[bits(4)]
361 ty: u8,
362 __: bool,
363 #[bits(2)]
364 dpl: Dpl,
365 p: bool,
366 #[bits(48)]
367 offset2: u64,
368 __: u32,
369}
370
371pub struct CpuInfo {
373 pub cpu_vendor: String, pub cpu_id: u32, }
376
377pub struct ArchConfig {
379 pub cpu: CpuInfo,
380 pub trap_rsp: usize,
381 pub secondary_start: &'static [u8],
382}