strat9_kernel/arch/x86_64/
ring3_diag.rs1use x86_64::{
18 instructions::tables::sgdt, registers::control::Cr3, structures::paging::PageTableFlags,
19};
20
21const DESC_PRESENT_BIT: u64 = 1 << 47;
25const DESC_DPL_SHIFT: u32 = 45;
27const DESC_DPL_MASK: u64 = 0x3 << DESC_DPL_SHIFT;
28const DESC_L_BIT: u64 = 1 << 53;
30const DESC_S_BIT: u64 = 1 << 44;
32const DESC_EXEC_BIT: u64 = 1 << 43;
34
35#[inline]
43unsafe fn read_gdt_raw(gdt_base: *const u64, index: usize) -> u64 {
44 unsafe { *gdt_base.add(index) }
46}
47
48#[inline]
51fn desc_present(raw: u64) -> bool {
52 raw & DESC_PRESENT_BIT != 0
53}
54
55#[inline]
56fn desc_dpl(raw: u64) -> u8 {
57 ((raw & DESC_DPL_MASK) >> DESC_DPL_SHIFT) as u8
58}
59
60#[inline]
61fn desc_is_code(raw: u64) -> bool {
62 raw & DESC_S_BIT != 0 && raw & DESC_EXEC_BIT != 0
64}
65
66#[inline]
67fn desc_long_mode(raw: u64) -> bool {
68 raw & DESC_L_BIT != 0
69}
70
71const PHYS_ADDR_MASK: u64 = 0x000F_FFFF_FFFF_F000;
75
76fn check_user_mapping(vaddr: u64) -> Result<(), &'static str> {
85 let hhdm = crate::memory::hhdm_offset();
86
87 let (pml4_frame, _) = Cr3::read();
89 let pml4_phys = pml4_frame.start_address().as_u64();
90 let pml4_ptr = (pml4_phys + hhdm) as *const u64;
91 let pml4_idx = ((vaddr >> 39) & 0x1FF) as usize;
92
93 let pml4_e = unsafe { *pml4_ptr.add(pml4_idx) };
95 let f4 = PageTableFlags::from_bits_truncate(pml4_e);
96
97 if !f4.contains(PageTableFlags::PRESENT) {
98 return Err("PML4: entry not present (PRESENT=0)");
99 }
100 if !f4.contains(PageTableFlags::USER_ACCESSIBLE) {
101 return Err("PML4: bit USER_ACCESSIBLE missing");
102 }
103
104 let pdpt_ptr = ((pml4_e & PHYS_ADDR_MASK) + hhdm) as *const u64;
106 let pdpt_idx = ((vaddr >> 30) & 0x1FF) as usize;
107
108 let pdpt_e = unsafe { *pdpt_ptr.add(pdpt_idx) };
110 let fp = PageTableFlags::from_bits_truncate(pdpt_e);
111
112 if !fp.contains(PageTableFlags::PRESENT) {
113 return Err("PDPT: entrée non présente (PRESENT=0)");
114 }
115 if !fp.contains(PageTableFlags::USER_ACCESSIBLE) {
116 return Err("PDPT: bit USER_ACCESSIBLE missing");
117 }
118 if fp.contains(PageTableFlags::HUGE_PAGE) {
120 return Ok(());
121 }
122
123 let pd_ptr = ((pdpt_e & PHYS_ADDR_MASK) + hhdm) as *const u64;
125 let pd_idx = ((vaddr >> 21) & 0x1FF) as usize;
126
127 let pd_e = unsafe { *pd_ptr.add(pd_idx) };
129 let fd = PageTableFlags::from_bits_truncate(pd_e);
130
131 if !fd.contains(PageTableFlags::PRESENT) {
132 return Err("PD: entry not preset (PRESENT=0)");
133 }
134 if !fd.contains(PageTableFlags::USER_ACCESSIBLE) {
135 return Err("PD: bit USER_ACCESSIBLE missing");
136 }
137 if fd.contains(PageTableFlags::HUGE_PAGE) {
139 return Ok(());
140 }
141
142 let pt_ptr = ((pd_e & PHYS_ADDR_MASK) + hhdm) as *const u64;
144 let pt_idx = ((vaddr >> 12) & 0x1FF) as usize;
145
146 let pt_e = unsafe { *pt_ptr.add(pt_idx) };
148 let ft = PageTableFlags::from_bits_truncate(pt_e);
149
150 if !ft.contains(PageTableFlags::PRESENT) {
151 return Err("PT (page 4 KiB): entry not here (PRESENT=0)");
152 }
153 if !ft.contains(PageTableFlags::USER_ACCESSIBLE) {
154 return Err("PT (page 4 KiB): bit USER_ACCESSIBLE missing");
155 }
156
157 Ok(())
158}
159
160pub fn validate_ring3_state(target_rip: u64, target_rsp: u64, cs: u16, ss: u16) {
183 crate::serial_force_println!(
184 "[validate_ring3] === Begin validation Ring 3 === \
185 RIP={:#x} RSP={:#x} CS={:#x} SS={:#x}",
186 target_rip,
187 target_rsp,
188 cs,
189 ss,
190 );
191
192 let gdtr = sgdt();
198 let gdt_base = gdtr.base.as_u64() as *const u64;
199 let gdt_limit = gdtr.limit as usize; crate::serial_force_println!(
202 "[validate_ring3] GDTR base={:#x} limit={:#x}",
203 gdtr.base.as_u64(),
204 gdt_limit,
205 );
206
207 let cs_byte_offset = (cs & !0x7) as usize; let ss_byte_offset = (ss & !0x7) as usize;
211 let cs_index = cs_byte_offset / 8;
212 let ss_index = ss_byte_offset / 8;
213
214 if cs_byte_offset + 7 > gdt_limit {
216 panic!(
217 "[validate_ring3] GDT: CS selector {:#x} (byte offset {}) \
218 exceeds GDTR limit ({:#x}). GDT too small or invalid selector.",
219 cs, cs_byte_offset, gdt_limit,
220 );
221 }
222 if ss_byte_offset + 7 > gdt_limit {
223 panic!(
224 "[validate_ring3] GDT: selector SS {:#x} (byte offset {}) \
225 exceeds GDTR limit ({:#x}). GDT too small or invalid selector.",
226 ss, ss_byte_offset, gdt_limit,
227 );
228 }
229
230 let cs_raw = unsafe { read_gdt_raw(gdt_base, cs_index) };
232 let ss_raw = unsafe { read_gdt_raw(gdt_base, ss_index) };
233
234 crate::serial_force_println!(
235 "[validate_ring3] GDT[{}] CS raw={:#018x} GDT[{}] SS raw={:#018x}",
236 cs_index,
237 cs_raw,
238 ss_index,
239 ss_raw,
240 );
241
242 if !desc_present(cs_raw) {
244 panic!(
245 "[validate_ring3] GDT CS {:#x} (index {}): bit P (Present) = 0 ! \
246 The descriptor is marked as absent. raw={:#018x}",
247 cs, cs_index, cs_raw,
248 );
249 }
250
251 let cs_dpl = desc_dpl(cs_raw);
253 if cs_dpl != 3 {
254 panic!(
255 "[validate_ring3] GDT CS {:#x} (index {}): DPL={} (3 expected). \
256 The descriptor will not allow execution in Ring 3. raw={:#018x}",
257 cs, cs_index, cs_dpl, cs_raw,
258 );
259 }
260
261 if !desc_is_code(cs_raw) {
263 panic!(
264 "[validate_ring3] GDT CS {:#x} (index {}): this is not a code segment \
265 (S={} E={}). IRETQ with a data selector in CS will cause a #GP. \
266 raw={:#018x}",
267 cs,
268 cs_index,
269 (cs_raw >> 44) & 1,
270 (cs_raw >> 43) & 1,
271 cs_raw,
272 );
273 }
274
275 if !desc_long_mode(cs_raw) {
277 panic!(
278 "[validate_ring3] GDT CS {:#x} (index {}): bit L (Long Mode 64-bit) = 0 ! \
279 In 64-bit mode, all Ring 3 code segments must have L=1. \
280 Without L=1, the CPU switches to 32-bit compatibility mode -> TRIPLE FAULT guaranteed. \
281 raw={:#018x}",
282 cs, cs_index, cs_raw,
283 );
284 }
285
286 if !desc_present(ss_raw) {
288 panic!(
289 "[validate_ring3] GDT SS {:#x} (index {}): bit P (Present) = 0 ! \
290 raw={:#018x}",
291 ss, ss_index, ss_raw,
292 );
293 }
294
295 let ss_dpl = desc_dpl(ss_raw);
297 if ss_dpl != 3 {
298 panic!(
299 "[validate_ring3] GDT SS {:#x} (index {}): DPL={} (3 attendu). \
300 IRETQ requiert DPL(SS) == RPL(CS) == 3. raw={:#018x}",
301 ss, ss_index, ss_dpl, ss_raw,
302 );
303 }
304
305 crate::serial_force_println!(
306 "[validate_ring3] [1/4] GDT OK : \
307 CS={:#x} P=1 DPL={} L=1 | SS={:#x} P=1 DPL={}",
308 cs,
309 cs_dpl,
310 ss,
311 ss_dpl,
312 );
313
314 if target_rsp & 0xF != 0 {
324 panic!(
325 "[validate_ring3] RSP={:#x} not 16-byte aligned \
326 (RSP & 0xF = {:#x}). The System V ABI requires RSP ≡ 0 (mod 16) \
327 before calling _start. Align the stack by subtracting {}.",
328 target_rsp,
329 target_rsp & 0xF,
330 target_rsp & 0xF,
331 );
332 }
333
334 crate::serial_force_println!(
335 "[validate_ring3] [2/4] Alignement RSP OK : RSP={:#x} ≡ 0 (mod 16)",
336 target_rsp,
337 );
338
339 match check_user_mapping(target_rip) {
349 Ok(()) => {
350 crate::serial_force_println!(
351 "[validate_ring3] Pagination RIP OK : {:#x} USER_ACCESSIBLE \
352 on 4 levels (PML4 -> PDPT -> PD -> PT)",
353 target_rip,
354 );
355 }
356 Err(reason) => {
357 panic!(
358 "[validate_ring3] Pagination RIP {:#x} INVALID : {}. \
359 The CPU will trigger a #PF immediately after iretq, \
360 then a TRIPLE FAULT (as rsp0 might also be invalid).",
361 target_rip, reason,
362 );
363 }
364 }
365
366 for &probe in &[target_rsp, target_rsp.wrapping_sub(8)] {
373 match check_user_mapping(probe) {
374 Ok(()) => {
375 crate::serial_force_println!(
376 "[validate_ring3] Pagination RSP OK : page de {:#x} USER_ACCESSIBLE",
377 probe,
378 );
379 }
380 Err(reason) => {
381 panic!(
382 "[validate_ring3] Pagination RSP probe {:#x} INVALID : {}. \
383 The user stack is not accessible from Ring 3.",
384 probe, reason,
385 );
386 }
387 }
388 }
389
390 crate::serial_force_println!(
391 "[validate_ring3] [3/4] Pagination OK : RIP={:#x} RSP={:#x} USER_ACCESSIBLE",
392 target_rip,
393 target_rsp,
394 );
395
396 let tr_sel: u16;
406 unsafe {
409 core::arch::asm!(
410 "str {0:x}",
411 out(reg) tr_sel,
412 options(nostack, nomem),
413 );
414 }
415
416 if tr_sel == 0 {
417 panic!(
418 "[validate_ring3] TR=0 : no TSS loaded (instruction `ltr` never executed). \
419 Without a TSS, the CPU cannot recover the kernel stack during an exception \
420 from Ring 3 → immediate Triple Fault. Call gdt::init() before this trampoline.",
421 );
422 }
423
424 let cpu_index = crate::arch::x86_64::percpu::current_cpu_index();
426 let tss = crate::arch::x86_64::tss::tss_for(cpu_index);
427 let rsp0 = tss.privilege_stack_table[0].as_u64();
428 let loaded_tss = crate::arch::x86_64::tss::loaded_tss_info();
429
430 if rsp0 == 0 {
431 panic!(
432 "[validate_ring3] TSS.rsp0=0 : kernel stack (Ring 0) not configured. \
433 Call tss::set_kernel_stack() with the current thread's stack \
434 before entering Ring 3.",
435 );
436 }
437
438 const KERNEL_VADDR_START: u64 = 0xffff_8000_0000_0000;
442
443 if rsp0 < KERNEL_VADDR_START {
444 panic!(
445 "[validate_ring3] TSS.rsp0={:#x} is a user address (< {:#x}). \
446 The CPU would load a user stack in Ring 0 during an exception, \
447 which allows trivial privilege escalation. \
448 Call tss::set_kernel_stack() with a valid kernel address.",
449 rsp0, KERNEL_VADDR_START,
450 );
451 }
452
453 crate::serial_force_println!(
454 "[validate_ring3] [4/4] TSS OK : TR={:#x} rsp0={:#x} (kernel space, CPU {})",
455 tr_sel,
456 rsp0,
457 cpu_index,
458 );
459 crate::e9_println!(
460 "[validate_ring3] [4/4] TSS OK TR={:#x} rsp0={:#x} cpu={}",
461 tr_sel,
462 rsp0,
463 cpu_index,
464 );
465 if let Some(info) = loaded_tss {
466 crate::serial_force_println!(
467 "[validate_ring3] TSS live : TR={:#x} base={:#x} rsp0={:#x}",
468 info.tr_selector,
469 info.tss_base,
470 info.rsp0,
471 );
472 crate::e9_println!(
473 "[validate_ring3] TSS live TR={:#x} base={:#x} rsp0={:#x}",
474 info.tr_selector,
475 info.tss_base,
476 info.rsp0,
477 );
478 if info.rsp0 != rsp0 {
479 crate::serial_force_println!(
480 "[validate_ring3] TSS MISMATCH : software_rsp0={:#x} live_rsp0={:#x} cpu={}",
481 rsp0,
482 info.rsp0,
483 cpu_index,
484 );
485 crate::e9_println!(
486 "[validate_ring3] TSS MISMATCH sw={:#x} live={:#x} cpu={}",
487 rsp0,
488 info.rsp0,
489 cpu_index,
490 );
491 }
492 }
493
494 for vector in [
495 crate::arch::x86_64::apic::LVT_TIMER_VECTOR,
496 crate::arch::x86_64::apic::IPI_RESCHED_VECTOR,
497 crate::arch::x86_64::idt::irq::MOUSE,
498 0x0e,
499 ] {
500 if let Some(gate) = crate::arch::x86_64::idt::live_gate_info(vector) {
501 crate::serial_force_println!(
502 "[validate_ring3] IDT live vec={:#x} sel={:#x} opts={:#x} off={:#x}",
503 gate.vector,
504 gate.selector,
505 gate.options,
506 gate.offset,
507 );
508 crate::e9_println!(
509 "[validate_ring3] IDT live vec={:#x} sel={:#x} opts={:#x} off={:#x}",
510 gate.vector,
511 gate.selector,
512 gate.options,
513 gate.offset,
514 );
515 }
516 }
517
518 crate::serial_force_println!(
522 "[validate_ring3] === ALL RING 3 PREREQUISiTES VALIDATED === \
523 RIP={:#x} RSP={:#x} CS={:#x} SS={:#x} → iretq",
524 target_rip,
525 target_rsp,
526 cs,
527 ss,
528 );
529}