diff --git a/kernel/src/arch/gdt.rs b/kernel/src/arch/gdt.rs index e64ebe8..6b3dc7d 100644 --- a/kernel/src/arch/gdt.rs +++ b/kernel/src/arch/gdt.rs @@ -1,7 +1,10 @@ -//! Global Descriptor Table (GDT) and Task State Segment (TSS) for x86_64 +//! Global Descriptor Table (GDT) and Task State Segment (TSS) for x86_64 SMP +//! Supports up to 4 CPU Cores with dedicated TSS descriptors. use core::mem::size_of; +pub const MAX_CPUS: usize = 4; + #[repr(C, packed)] pub struct TaskStateSegment { _reserved1: u32, @@ -40,37 +43,60 @@ struct GdtDescriptor { #[no_mangle] pub static mut TSS: TaskStateSegment = TaskStateSegment::new(); -// 8 entries: Null, KCode, KData, UData, UCode, TSS Low, TSS High, Null -static mut GDT: [u64; 8] = [ +#[no_mangle] +pub static mut PER_CPU_TSS: [TaskStateSegment; MAX_CPUS] = [const { TaskStateSegment::new() }; MAX_CPUS]; + +// GDT entries: +// 0: Null +// 1: Kernel Code 64 (0x08) +// 2: Kernel Data 64 (0x10) +// 3: User Data 64 (0x18) +// 4: User Code 64 (0x20) +// 5,6: TSS CPU 0 (0x28) +// 7,8: TSS CPU 1 (0x38) +// 9,10: TSS CPU 2 (0x48) +// 11,12: TSS CPU 3 (0x58) +// 13,14,15: padding +static mut GDT: [u64; 16] = [ 0x0000000000000000, // 0x00: Null 0x00af9a000000ffff, // 0x08: Kernel Code 64 (Ring 0) 0x00cf92000000ffff, // 0x10: Kernel Data 64 (Ring 0) 0x00cff2000000ffff, // 0x18: User Data 64 (Ring 3) 0x00affa000000ffff, // 0x20: User Code 64 (Ring 3) - 0x0000000000000000, // 0x28: TSS Low (populated at init) - 0x0000000000000000, // 0x30: TSS High (populated at init) - 0x0000000000000000, // 0x38: Alignment / padding + 0, 0, // 0x28: TSS CPU 0 + 0, 0, // 0x38: TSS CPU 1 + 0, 0, // 0x48: TSS CPU 2 + 0, 0, // 0x58: TSS CPU 3 + 0, 0, 0, // Padding ]; pub unsafe fn init() { - let tss_addr = core::ptr::addr_of!(TSS) as u64; - let tss_size = (size_of::() - 1) as u64; - - // Build 64-bit TSS descriptor - let tss_low = (tss_size & 0xffff) - | ((tss_addr & 0xffff) << 16) - | (((tss_addr >> 16) & 0xff) << 32) - | (0x89u64 << 40) // Present, 64-bit TSS (Available) - | (((tss_size >> 16) & 0xf) << 48) - | (((tss_addr >> 24) & 0xff) << 56); - let tss_high = tss_addr >> 32; - let gdt_ptr = core::ptr::addr_of_mut!(GDT) as *mut u64; - *gdt_ptr.add(5) = tss_low; - *gdt_ptr.add(6) = tss_high; + + // Populate TSS descriptors for all supported CPU cores + for cpu in 0..MAX_CPUS { + let tss_addr = if cpu == 0 { + core::ptr::addr_of!(TSS) as u64 + } else { + core::ptr::addr_of!(PER_CPU_TSS[cpu]) as u64 + }; + let tss_size = (size_of::() - 1) as u64; + + let tss_low = (tss_size & 0xffff) + | ((tss_addr & 0xffff) << 16) + | (((tss_addr >> 16) & 0xff) << 32) + | (0x89u64 << 40) // Present, 64-bit TSS (Available) + | (((tss_size >> 16) & 0xf) << 48) + | (((tss_addr >> 24) & 0xff) << 56); + let tss_high = tss_addr >> 32; + + let entry_idx = 5 + cpu * 2; + *gdt_ptr.add(entry_idx) = tss_low; + *gdt_ptr.add(entry_idx + 1) = tss_high; + } let descriptor = GdtDescriptor { - limit: (size_of::<[u64; 8]>() - 1) as u16, + limit: (size_of::<[u64; 16]>() - 1) as u16, base: gdt_ptr as u64, }; @@ -95,6 +121,37 @@ pub unsafe fn init() { ); } +/// Initialize GDT and load dedicated TSS for an Application Processor (AP) +pub unsafe fn init_ap(cpu_id: usize) { + let gdt_ptr = core::ptr::addr_of_mut!(GDT) as *mut u64; + let descriptor = GdtDescriptor { + limit: (size_of::<[u64; 16]>() - 1) as u16, + base: gdt_ptr as u64, + }; + + let tss_sel = (0x28 + (cpu_id.min(MAX_CPUS - 1) * 16)) as u16; + + core::arch::asm!( + "lgdt [{0}]", + "push 0x08", + "lea {tmp}, [2f + rip]", + "push {tmp}", + "retfq", + "2:", + "mov ax, 0x10", + "mov ds, ax", + "mov es, ax", + "mov ss, ax", + "mov fs, ax", + "mov gs, ax", + "ltr {tss_sel:x}", + in(reg) &descriptor, + tmp = out(reg) _, + tss_sel = in(reg) tss_sel, + options(preserves_flags) + ); +} + #[no_mangle] pub extern "C" fn set_kernel_stack(stack_top: u64) { unsafe { diff --git a/kernel/src/arch/smp.rs b/kernel/src/arch/smp.rs index dde679b..2e254e2 100644 --- a/kernel/src/arch/smp.rs +++ b/kernel/src/arch/smp.rs @@ -12,15 +12,15 @@ pub extern "C" fn ap_startup(info: *const LimineSmpInfo) -> ! { let cpu_id = (*info).processor_id; let lapic_id = (*info).lapic_id; - // Initialize CPU Architecture for this AP - super::gdt::init(); + // Initialize CPU Architecture for this AP with dedicated TSS selector + super::gdt::init_ap(cpu_id as usize); super::idt::init(); super::syscall::init(); CORES_ONLINE.fetch_add(1, Ordering::SeqCst); - kprintln!("[SMP] CPU Core [{}] (LAPIC ID: {}) is ONLINE and entering scheduler.", cpu_id, lapic_id); + kprintln!("[SMP] CPU Core [{}] (LAPIC ID: {}) is ONLINE.", cpu_id, lapic_id); - // Enter AP idle / scheduling loop + // AP idle loop - ready for SMP thread scheduling loop { core::arch::asm!("sti; hlt"); } @@ -36,14 +36,22 @@ pub unsafe fn init() { let cpu_count = (*smp_resp).cpu_count; let bsp_lapic_id = (*smp_resp).bsp_lapic_id; - kprintln!("[SMP] Detected {} CPU Core(s) (BSP LAPIC ID: {})", cpu_count, bsp_lapic_id); +} + +/// Wake up all secondary Application Processors after memory & scheduler are initialized +pub unsafe fn boot_aps() { + let smp_resp = SMP_REQUEST.response; + if smp_resp.is_null() { + return; + } + + let cpu_count = (*smp_resp).cpu_count; + let bsp_lapic_id = (*smp_resp).bsp_lapic_id; - // Boot all Application Processors (APs) for i in 0..cpu_count { let cpu_info = *(*smp_resp).cpus.add(i as usize); if (*cpu_info).lapic_id != bsp_lapic_id { - // Write our entry trampoline to wake up the AP let info_mut = cpu_info as *mut LimineSmpInfo; (*info_mut).goto_address = Some(ap_startup); } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 8b8eeb1..07b8651 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -82,7 +82,12 @@ pub extern "C" fn _start() -> ! { // 8. Initialize Scheduler sched::init(); - // 9. Inspect and Load Initial Userspace Processes + // 9. Bring up SMP Cores (APs) + unsafe { + arch::smp::boot_aps(); + } + + // 10. Inspect and Load Initial Userspace Processes unsafe { let mod_resp = MODULE_REQUEST.response; if !mod_resp.is_null() && (*mod_resp).module_count > 0 { diff --git a/kernel/src/mm/pfa.rs b/kernel/src/mm/pfa.rs index 611ad7b..3713d89 100644 --- a/kernel/src/mm/pfa.rs +++ b/kernel/src/mm/pfa.rs @@ -35,22 +35,17 @@ impl FrameAllocator { let entry_count = (*memmap).entry_count as usize; let entries_ptr = (*memmap).entries; - kprintln!("[PFA] entry_count={}, entries_ptr={:#018x}", entry_count, entries_ptr as u64); - let mut max_paddr: u64 = 0; for i in 0..entry_count { let entry = *entries_ptr.add(i); let end = (*entry).base + (*entry).length; - kprintln!("[PFA] entry[{}]: base={:#x} len={:#x} type={}", i, (*entry).base, (*entry).length, (*entry).typ); if end > max_paddr { max_paddr = end; } } - kprintln!("[PFA] max_paddr={:#x}", max_paddr); self.total_frames = (max_paddr as usize) / PAGE_SIZE; self.bitmap_size_bytes = (self.total_frames + 7) / 8; - kprintln!("[PFA] total_frames={}, bitmap_size={} bytes", self.total_frames, self.bitmap_size_bytes); // Find a usable region large enough to place the bitmap let mut bitmap_paddr: u64 = 0; @@ -68,11 +63,8 @@ impl FrameAllocator { kprintln!("[FATAL] Could not find memory for PFA bitmap!"); return; } - kprintln!("[PFA] Bitmap at phys={:#x}, virt={:#x}", bitmap_paddr, bitmap_paddr + hhdm); - self.bitmap = (bitmap_paddr + hhdm) as *mut u8; core::ptr::write_bytes(self.bitmap, 0xFF, self.bitmap_size_bytes); - kprintln!("[PFA] Bitmap zeroed OK"); self.free_frames = 0; for i in 0..entry_count {