obkrnl/
x86_64.rs

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
20/// See `identify_cpu` on the Orbis for a reference.
21///
22/// # Reference offsets
23/// | Version | Offset |
24/// |---------|--------|
25/// |PS4 11.00|0x2311E0|
26pub fn identify_cpu() -> CpuInfo {
27    // In order to activate long mode on a bare hardware it is required CPUID. However, it is
28    // possible for CPUID to not available on the VM so we need to check.
29    let mut flags: u64;
30
31    unsafe { asm!("pushfq", "pop {v}", v = out(reg) flags, options(nomem, preserves_flags)) };
32
33    // CPUID is essential so just panic.
34    let flags = Rflags::from_bits(flags);
35
36    if !flags.id() {
37        panic!("CPUID instruction is not available");
38    }
39
40    // Get cpu_high.
41    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    // TODO: Get cpu_vendor_id.
55    let r = unsafe { core::arch::x86_64::__cpuid(1) };
56    let cpu_id = r.eax;
57
58    // TODO: Get cpu_feature.
59    CpuInfo { cpu_vendor, cpu_id }
60}
61
62/// # Safety
63/// This function can be called only once and must be called by main CPU entry point.
64pub unsafe fn setup_main_cpu(cpu: CpuInfo) -> Arc<ArchConfig> {
65    // Setup GDT.
66    let mut gdt = vec![
67        // Null descriptor.
68        SegmentDescriptor::new(),
69        // 32-bit GS for user.
70        SegmentDescriptor::new(),
71        // 32-bit FS for user.
72        SegmentDescriptor::new(),
73        // CS for kernel.
74        SegmentDescriptor::new()
75            .with_ty(0b1000) // This required somehow although the docs said it is ignored.
76            .with_s(true) // Same here.
77            .with_p(true)
78            .with_l(true), // 64-bit mode.
79        // DS for kernel.
80        SegmentDescriptor::new()
81            .with_ty(0b0010) // This required somehow although the docs said it is ignored.
82            .with_s(true) // Same here.
83            .with_p(true),
84        // 32-bit CS for user.
85        SegmentDescriptor::new(),
86        // DS for user.
87        SegmentDescriptor::new(),
88        // 64-bit CS for user.
89        SegmentDescriptor::new(),
90    ];
91
92    // Setup Task State Segment (TSS).
93    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    // Switch GDT from bootloader GDT to our own.
98    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    // Set Task Register (TR).
116    unsafe {
117        asm!(
118            "ltr {v:x}",
119            v = in(reg) tss.into_bits(),
120            options(preserves_flags, nostack)
121        )
122    };
123
124    // See idt0 on the PS4 for a reference.
125    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    // Set IDT.
145    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    // Set CS and SS for syscall and sysret instruction.
160    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    // Set entry point for syscall instruction.
170    unsafe { wrmsr(0xC0000082, syscall_entry64 as usize) };
171    unsafe { wrmsr(0xC0000083, syscall_entry32 as usize) };
172
173    // Set SFMASK for syscall.
174    let mask = Rflags::new()
175        .with_cf(true)
176        .with_tf(true)
177        .with_if(true) // https://wiki.osdev.org/SWAPGS#Complications,_Part_2
178        .with_df(true)
179        .with_nt(true)
180        .into_bits()
181        .try_into()
182        .unwrap();
183
184    unsafe { wrmsr(0xC0000084, mask) };
185
186    // Switch EFER from bootloader to our own.
187    let efer = Efer::new()
188        .with_sce(true) // Enable syscall and sysret instruction.
189        .with_lme(true) // Long Mode Enable.
190        .with_lma(true) // Long Mode Active.
191        .into_bits()
192        .try_into()
193        .unwrap();
194
195    unsafe { wrmsr(0xC0000080, efer) };
196
197    // TODO: Find a better way.
198    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
221/// # Safety
222/// `trap_rsp` must live forever.
223unsafe fn push_tss<const L: usize>(
224    gdt: &mut Vec<SegmentDescriptor>,
225    trap_rsp: *mut [u8; L],
226) -> SegmentSelector {
227    // Setup Task State Segment (TSS).
228    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() }; // Top-down.
232
233    // Add placeholder for TSS descriptor.
234    let si = gdt.len();
235
236    gdt.push(SegmentDescriptor::new());
237    gdt.push(SegmentDescriptor::new());
238
239    // Setup TSS descriptor.
240    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); // Available 64-bit TSS.
247    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
262// See lgdt on the PS4 for a reference.
263global_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",  // Return address.
272    "push rsi", // Code segment selector.
273    "push rax",
274    "retfq" // Set CS then return.
275);
276
277// See Xbpt on the PS4 for a reference.
278global_asm!(
279    "Xbpt:", // TODO: Check if coming from user-space.
280    "sub rsp, 0x80", // TODO: Use const from Rust 1.82.
281    "mov dword ptr [rsp+0x78], 3", // TODO: Use const from Rust 1.82.
282    "mov rdi, rsp",
283    "call {f}",
284    f = sym interrupt_handler
285);
286
287// See Xfast_syscall on the PS4 for a reference.
288global_asm!(
289    "syscall_entry64:",
290    "swapgs",
291    "mov gs:[{user_rsp}], rsp", // Save user RSP.
292    "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
300// See Xfast_syscall32 on the Orbis for a reference.
301global_asm!("syscall_entry32:", "ud2");
302
303// See mptramp_start and mptramp_end on the Orbis for a reference.
304global_asm!("secondary_start:", "ud2", "secondary_end:");
305
306/// Raw value of a Interrupt Descriptor-Table Register.
307///
308/// See Interrupt Descriptor-Table Register section on AMD64 Architecture Programmer's Manual Volume
309/// 2 for details.
310#[repr(C, packed)]
311struct Idtr {
312    limit: u16,
313    addr: *const GateDescriptor,
314}
315
316/// Raw value of a Gate Descriptor.
317///
318/// See Gate Descriptors section on AMD64 Architecture Programmer's Manual Volume 2 for more
319/// details.
320#[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
340/// Contains information for CPU on current machine.
341pub struct CpuInfo {
342    pub cpu_vendor: String, // cpu_vendor
343    pub cpu_id: u32,        // cpu_id
344}
345
346/// Contains architecture-specific configurations obtained from [`setup_main_cpu()`].
347pub struct ArchConfig {
348    pub cpu: CpuInfo,
349    pub trap_rsp: usize,
350    pub secondary_start: &'static [u8],
351}