From 019564ee0e6fccc0a2c1fd21c6a088f4fb8904fb Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 15 Sep 2025 20:53:51 +0200 Subject: [PATCH] Statically allocate PCR for the BSP --- src/arch/x86_shared/gdt.rs | 96 +++++++++++++++++++++++++----------- src/arch/x86_shared/start.rs | 10 +--- 2 files changed, 69 insertions(+), 37 deletions(-) diff --git a/src/arch/x86_shared/gdt.rs b/src/arch/x86_shared/gdt.rs index 11eb40a910..42e3875bf3 100644 --- a/src/arch/x86_shared/gdt.rs +++ b/src/arch/x86_shared/gdt.rs @@ -1,5 +1,7 @@ //! Global descriptor table +use core::ptr; + #[cfg(target_arch = "x86")] use x86::bits32::task::TaskStateSegment; #[cfg(target_arch = "x86_64")] @@ -66,6 +68,8 @@ const SEGMENT_FLAGS: u8 = GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE; #[cfg(target_arch = "x86_64")] const SEGMENT_FLAGS: u8 = GDT_F_LONG_MODE; +// Make sure that the entries we load as segments have the dirty flag set already! +// INIT_GDT is read-only, but loading a segment would set the dirty flag if it isn't already. static INIT_GDT: [GdtEntry; 3] = [ // Null GdtEntry::new(0, 0, 0, 0), @@ -73,14 +77,19 @@ static INIT_GDT: [GdtEntry; 3] = [ GdtEntry::new( 0, SEGMENT_LIMIT, - GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, + GDT_A_PRESENT + | GDT_A_DIRTY + | GDT_A_RING_0 + | GDT_A_SYSTEM + | GDT_A_EXECUTABLE + | GDT_A_PRIVILEGE, SEGMENT_FLAGS, ), // Kernel data GdtEntry::new( 0, SEGMENT_LIMIT, - GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, + GDT_A_PRESENT | GDT_A_DIRTY | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, SEGMENT_FLAGS, ), ]; @@ -168,7 +177,7 @@ struct Align([u64; 2]); pub struct ProcessorControlRegion { // TODO: When both KASLR and KPTI are implemented, the PCR may need to be split into two pages, // such that "secret" kernel addresses are only stored in the protected half. - pub self_ref: usize, + pub self_ref: *mut ProcessorControlRegion, pub user_rsp_tmp: usize, // The GDT *must* be stored in the PCR! The paranoid interrupt handler, lacking a reliable way @@ -194,6 +203,21 @@ const _: () = { } }; +impl ProcessorControlRegion { + const fn new_partial_init(cpu_id: LogicalCpuId) -> Self { + Self { + self_ref: ptr::null_mut(), + user_rsp_tmp: 0, + gdt: BASE_GDT, + percpu: PercpuBlock::init(cpu_id), + _rsvd: Align([0; 2]), + tss: TaskStateSegment::new(), + _iobitmap: [0; IOBITMAP_SIZE as usize], + _all_ones: 0xFF, + } + } +} + pub unsafe fn pcr() -> *mut ProcessorControlRegion { unsafe { // Primitive benchmarking of RDFSBASE and RDGSBASE in userspace, appears to indicate that @@ -301,20 +325,10 @@ unsafe fn load_segments() { } } -/// Initialize GDT and configure percpu. -#[cold] -pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { - let alloc_order = size_of::() - .div_ceil(PAGE_SIZE) - .next_power_of_two() - .trailing_zeros(); +unsafe fn init_and_install_pcr(pcr_ptr: *mut ProcessorControlRegion, stack_end: usize) { + let pcr = unsafe { &mut *pcr_ptr }; - let pcr_frame = crate::memory::allocate_p2frame(alloc_order).expect("failed to allocate PCR"); - let pcr = unsafe { - &mut *(RmmA::phys_to_virt(pcr_frame.base()).data() as *mut ProcessorControlRegion) - }; - - pcr.self_ref = pcr as *const _ as usize; + pcr.self_ref = pcr_ptr; // Setup the GDT. pcr.gdt = BASE_GDT; @@ -327,8 +341,7 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { }; #[cfg(target_arch = "x86")] - unsafe { - pcr._all_ones = 0xFF; + { pcr.tss.iobp_offset = 0xFFFF; let tss = &pcr.tss as *const _ as usize as u32; @@ -337,9 +350,8 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { } #[cfg(target_arch = "x86_64")] - unsafe { + { pcr.tss.iomap_base = 0xFFFF; - pcr._all_ones = 0xFF; let tss = &mut pcr.tss as *mut TaskStateSegment as usize as u64; let tss_lo = (tss & 0xFFFF_FFFF) as u32; @@ -348,9 +360,11 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { pcr.gdt[GDT_TSS].set_offset(tss_lo); pcr.gdt[GDT_TSS].set_limit(size_of::() as u32 + IOBITMAP_SIZE); - (&mut pcr.gdt[GDT_TSS_HIGH] as *mut GdtEntry) - .cast::() - .write(tss_hi); + unsafe { + (&mut pcr.gdt[GDT_TSS_HIGH] as *mut GdtEntry) + .cast::() + .write(tss_hi); + } } // Load the new GDT, which is correctly located in thread local storage. @@ -388,14 +402,38 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { // Set the stack pointer to use when coming back from userspace. unsafe { - set_tss_stack(pcr, stack_offset); + set_tss_stack(pcr, stack_end); } // Load the task register unsafe { task::load_tr(SegmentSelector::new(GDT_TSS as u16, Ring::Ring0)) }; - pcr.percpu = PercpuBlock::init(cpu_id); - unsafe { crate::percpu::init_tlb_shootdown(cpu_id, &mut pcr.percpu) }; + unsafe { crate::percpu::init_tlb_shootdown(pcr.percpu.cpu_id, &mut pcr.percpu) }; +} + +/// Initialize GDT and configure percpu for the BSP. +#[cold] +pub unsafe fn init_bsp(stack_end: usize) { + static mut BSP_PCR: ProcessorControlRegion = + ProcessorControlRegion::new_partial_init(LogicalCpuId::BSP); + + unsafe { init_and_install_pcr(ptr::addr_of_mut!(BSP_PCR), stack_end) }; +} + +/// Initialize GDT and configure percpu. +#[cold] +pub unsafe fn init_paging(stack_end: usize, cpu_id: LogicalCpuId) { + let alloc_order = size_of::() + .div_ceil(PAGE_SIZE) + .next_power_of_two() + .trailing_zeros(); + + let pcr_frame = crate::memory::allocate_p2frame(alloc_order).expect("failed to allocate PCR"); + let pcr_ptr = + unsafe { RmmA::phys_to_virt(pcr_frame.base()).data() as *mut ProcessorControlRegion }; + unsafe { core::ptr::write(pcr_ptr, ProcessorControlRegion::new_partial_init(cpu_id)) }; + + unsafe { init_and_install_pcr(pcr_ptr, stack_end) }; } #[derive(Copy, Clone, Debug)] @@ -422,17 +460,17 @@ impl GdtEntry { } #[cfg(target_arch = "x86")] - pub fn offset(&self) -> u32 { + pub const fn offset(&self) -> u32 { (self.offsetl as u32) | ((self.offsetm as u32) << 16) | ((self.offseth as u32) << 24) } - pub fn set_offset(&mut self, offset: u32) { + pub const fn set_offset(&mut self, offset: u32) { self.offsetl = offset as u16; self.offsetm = (offset >> 16) as u8; self.offseth = (offset >> 24) as u8; } - pub fn set_limit(&mut self, limit: u32) { + pub const fn set_limit(&mut self, limit: u32) { self.limitl = limit as u16; self.flags_limith = self.flags_limith & 0xF0 | ((limit >> 16) as u8) & 0x0F; } diff --git a/src/arch/x86_shared/start.rs b/src/arch/x86_shared/start.rs index d17b4e5888..fd947a03aa 100644 --- a/src/arch/x86_shared/start.rs +++ b/src/arch/x86_shared/start.rs @@ -116,8 +116,8 @@ pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! { { args.bootstrap_base } + { args.bootstrap_size } ); - // Set up GDT before paging - gdt::init(); + // Set up GDT + gdt::init_bsp(args.stack_base as usize + args.stack_size as usize); // Set up IDT before paging idt::init(); @@ -157,12 +157,6 @@ pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! { // Initialize paging paging::init(); - // Set up GDT after paging with TLS - gdt::init_paging( - args.stack_base as usize + args.stack_size as usize, - LogicalCpuId::BSP, - ); - // Set up IDT idt::init_paging_bsp();