diff --git a/arch/aarch64/src/asm/boot.S b/arch/aarch64/src/asm/boot.S index 06f5774..8ce60b0 100644 --- a/arch/aarch64/src/asm/boot.S +++ b/arch/aarch64/src/asm/boot.S @@ -1,4 +1,7 @@ // TEAM_422: AArch64 boot assembly with early MMU setup +// TEAM_427: Investigation of boot failures - see .teams/TEAM_427_aarch64_boot_investigation.md +// Status: Partial fix - kernel prints boot messages but crashes during memory::init() +// with L0 translation fault at FAR 0xffff800040082000 // // Boot sequence: // 1. Save boot registers (x0-x3) from bootloader @@ -59,6 +62,13 @@ _start: // Disable interrupts msr daifset, #0xf + // TEAM_427: Enable FP/SIMD (required for Rust code) FIRST + // Use x9 as a scratch register to preserve x0-x3 boot registers + // Set CPACR_EL1.FPEN to 0b11 (bits 21:20) to enable EL1/EL0 access + mov x9, #(3 << 20) + msr cpacr_el1, x9 + isb + // Save boot registers to physical-address symbols // x0 = DTB address (on QEMU virt), x1-x3 = bootloader-specific adrp x4, BOOT_REGS_PHYS @@ -77,8 +87,10 @@ _start: // But wait - at this point we're using the linker symbol directly // and the literal pool is at physical address, containing the virtual address. // We need to calculate the physical address. - movz x5, #0x8000, lsl #48 // 0xFFFF800000000000 high bits - movk x5, #0xFFFF, lsl #48 + // TEAM_427: Build 0xFFFF_8000_0000_0000 correctly + // movz sets bits 63:48 to 0xFFFF, movk sets bits 47:32 to 0x8000 + movz x5, #0xFFFF, lsl #48 // x5 = 0xFFFF_0000_0000_0000 + movk x5, #0x8000, lsl #32 // x5 = 0xFFFF_8000_0000_0000 sub x4, x4, x5 // Convert VA to PA mov sp, x4 @@ -184,7 +196,29 @@ _start: // Load TTBR1_EL1 (higher-half map) - x10 still holds __boot_l0_ttbr1 msr ttbr1_el1, x10 - // Ensure all table writes are visible before enabling MMU + // TEAM_428: Ensure page table writes are visible to MMU + // CRITICAL FIX: Must clean+invalidate the ENTIRE page table region, + // not just the entries we wrote. The zeroing loop may have left + // stale data in the cache that could corrupt the MMU's view. + // + // Page tables span 16KB (4 x 4KB tables) from __boot_l0_ttbr1 to + // __boot_page_tables_end. That's 256 cache lines (64 bytes each). + dsb sy + + // Clean and invalidate entire page table region (16KB) + // x10 = __boot_l0_ttbr1 (start of page tables) + mov x3, x10 // Start address + mov x4, #256 // 256 cache lines = 16KB +.Lflush_pt_cache: + dc civac, x3 // Clean+invalidate cache line at x3 + add x3, x3, #64 // Next cache line (64 bytes) + subs x4, x4, #1 // Decrement counter + b.ne .Lflush_pt_cache // Loop until done + dsb sy + isb + + // Invalidate TLB before enabling MMU + tlbi vmalle1 dsb sy isb @@ -198,6 +232,13 @@ _start: dsb sy isb + // TEAM_427: Set up exception vectors BEFORE jumping to Rust + // This ensures any early exceptions are properly caught + .extern vectors + ldr x4, =vectors + msr vbar_el1, x4 + isb + // === Jump to higher-half Rust entry point === // Now that MMU is on, we can use virtual addresses ldr x4, =rust_main diff --git a/levitate/src/boot/dtb.rs b/levitate/src/boot/dtb.rs index d9dd28e..48c2543 100644 --- a/levitate/src/boot/dtb.rs +++ b/levitate/src/boot/dtb.rs @@ -7,6 +7,7 @@ use super::{BootInfo, BootProtocol, FirmwareInfo, MemoryKind, MemoryRegion}; use los_hal::aarch64::fdt::{self, Fdt}; +use los_hal::mmu::{KERNEL_PHYS_END, KERNEL_PHYS_START}; /// TEAM_282: Parse DTB into BootInfo. /// @@ -28,13 +29,9 @@ pub unsafe fn parse(dtb_ptr: usize) -> BootInfo { let dtb_slice = unsafe { core::slice::from_raw_parts(dtb_ptr as *const u8, 1024 * 1024) }; if let Ok(fdt_obj) = Fdt::new(dtb_slice) { - // Extract memory regions using HAL helper + // TEAM_428: Split memory regions around kernel to avoid overwriting boot page tables fdt::for_each_memory_region(&fdt_obj, |region| { - let _ = boot_info.memory_map.push(MemoryRegion::new( - region.start, - region.end - region.start, - MemoryKind::Usable, - )); + add_memory_region_with_kernel_split(&mut boot_info, region.start, region.end); }); // Try to find initramfs @@ -61,13 +58,9 @@ pub fn parse_from_slice(dtb_slice: &[u8], dtb_phys: usize) -> BootInfo { boot_info.firmware = FirmwareInfo::DeviceTree { dtb: dtb_phys }; if let Ok(fdt_obj) = Fdt::new(dtb_slice) { - // Extract memory regions using HAL helper + // TEAM_428: Split memory regions around kernel to avoid overwriting boot page tables fdt::for_each_memory_region(&fdt_obj, |region| { - let _ = boot_info.memory_map.push(MemoryRegion::new( - region.start, - region.end - region.start, - MemoryKind::Usable, - )); + add_memory_region_with_kernel_split(&mut boot_info, region.start, region.end); }); // Try to find initramfs @@ -84,3 +77,46 @@ pub fn parse_from_slice(dtb_slice: &[u8], dtb_phys: usize) -> BootInfo { boot_info } + +/// TEAM_428: Add a memory region to boot_info, splitting around the kernel physical region. +/// +/// This prevents the page array allocation from overlapping with the kernel and +/// boot page tables, which would cause an L0 translation fault when memory::init() +/// zeros the page array. +fn add_memory_region_with_kernel_split(boot_info: &mut BootInfo, start: usize, end: usize) { + // Check if this region overlaps with kernel + if end <= KERNEL_PHYS_START || start >= KERNEL_PHYS_END { + // No overlap - add as usable + let _ = boot_info.memory_map.push(MemoryRegion::new( + start, + end - start, + MemoryKind::Usable, + )); + } else { + // Region overlaps with kernel - split into parts + // Part before kernel + if start < KERNEL_PHYS_START { + let _ = boot_info.memory_map.push(MemoryRegion::new( + start, + KERNEL_PHYS_START - start, + MemoryKind::Usable, + )); + } + // Kernel region itself (reserved) + let kernel_start = start.max(KERNEL_PHYS_START); + let kernel_end = end.min(KERNEL_PHYS_END); + let _ = boot_info.memory_map.push(MemoryRegion::new( + kernel_start, + kernel_end - kernel_start, + MemoryKind::Kernel, + )); + // Part after kernel + if end > KERNEL_PHYS_END { + let _ = boot_info.memory_map.push(MemoryRegion::new( + KERNEL_PHYS_END, + end - KERNEL_PHYS_END, + MemoryKind::Usable, + )); + } + } +} diff --git a/levitate/src/init.rs b/levitate/src/init.rs index 1fee80b..df524fe 100644 --- a/levitate/src/init.rs +++ b/levitate/src/init.rs @@ -358,6 +358,15 @@ fn init_display() { /// Initialize VirtIO devices (block, network, input). fn init_devices() { crate::virtio::init(); + + // TEAM_429: Register VirtIO keyboard as secondary console input + // This allows the shell to receive keyboard input from VirtIO devices + fn virtio_keyboard_input() -> Option { + // Poll VirtIO input devices first to get fresh events + crate::input::poll(); + crate::input::read_char() + } + los_hal::console::set_secondary_input(virtio_keyboard_input); } /// Initialize userspace: load and spawn init from initramfs. @@ -418,10 +427,68 @@ fn init_userspace() -> bool { *global_archive = Some(sb); } + // Register process hooks for syscalls + register_process_hooks(); + // Spawn init from initramfs spawn_init() } +/// Register process hooks for syscall layer. +/// +/// These hooks allow the syscall layer (los_syscall) to access kernel-specific +/// functionality like initramfs and spawn_from_elf without circular dependencies. +fn register_process_hooks() { + use core::sync::atomic::Ordering; + use los_syscall::process::{ + RESOLVE_EXECUTABLE_HOOK, SPAWN_FROM_ELF_HOOK, SPAWN_FROM_ELF_WITH_ARGS_HOOK, + }; + + // Resolver: reads ELF data from initramfs by path + fn resolve_executable(path: &str) -> Result, u32> { + const ENOENT: u32 = 2; // No such file or directory + + let archive_lock = crate::fs::INITRAMFS.lock(); + let Some(sb) = archive_lock.as_ref() else { + return Err(ENOENT); + }; + + // Strip leading slashes for initramfs lookup + let name = path.trim_start_matches('/'); + + sb.archive + .iter() + .find(|e| e.name == name) + .map(|e| e.data.to_vec()) + .ok_or(ENOENT) + } + + // Spawn hook: creates a UserTask from ELF data + fn spawn_from_elf_hook( + elf_data: &[u8], + fd_table: crate::task::fd_table::SharedFdTable, + ) -> Result { + crate::process::spawn_from_elf(elf_data, fd_table) + } + + // Spawn with args hook: creates a UserTask with argv/envp + fn spawn_from_elf_with_args_hook( + elf_data: &[u8], + _argv: &[&str], + _envp: &[&str], + fd_table: crate::task::fd_table::SharedFdTable, + ) -> Result { + // TODO: Pass argv/envp to spawn_from_elf + crate::process::spawn_from_elf(elf_data, fd_table) + } + + RESOLVE_EXECUTABLE_HOOK.store(resolve_executable as *mut (), Ordering::Release); + SPAWN_FROM_ELF_HOOK.store(spawn_from_elf_hook as *mut (), Ordering::Release); + SPAWN_FROM_ELF_WITH_ARGS_HOOK.store(spawn_from_elf_with_args_hook as *mut (), Ordering::Release); + + log::trace!("[BOOT] Process hooks registered"); +} + /// Spawn the init process from initramfs. fn spawn_init() -> bool { log::info!("[BOOT] spawning init task..."); diff --git a/levitate/src/main.rs b/levitate/src/main.rs index f6dc470..69fb7c7 100644 --- a/levitate/src/main.rs +++ b/levitate/src/main.rs @@ -62,12 +62,19 @@ mod aarch64_handlers { crate::arch::cpu::halt(); } - /// Handle IRQ dispatch + /// Handle IRQ dispatch - delegates to HAL registered handlers #[unsafe(no_mangle)] pub extern "C" fn handle_irq_dispatch(irq: u32) -> bool { - // TODO: Implement proper IRQ handling - log::trace!("IRQ {}", irq); - false + #[cfg(target_arch = "aarch64")] + { + los_hal::aarch64::gic::dispatch(irq) + } + #[cfg(target_arch = "x86_64")] + { + // x86_64 handles dispatch in HAL exceptions.rs directly + let _ = irq; + false + } } /// Check and deliver signals before returning to userspace diff --git a/lib/hal/src/console.rs b/lib/hal/src/console.rs index 99af498..1476002 100644 --- a/lib/hal/src/console.rs +++ b/lib/hal/src/console.rs @@ -14,6 +14,11 @@ type SecondaryOutputFn = fn(&str); static SECONDARY_OUTPUT: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut()); static SECONDARY_OUTPUT_ENABLED: AtomicBool = AtomicBool::new(false); +// TEAM_429: Secondary input callback for VirtIO keyboard +type SecondaryInputFn = fn() -> Option; +static SECONDARY_INPUT: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut()); +static SECONDARY_INPUT_ENABLED: AtomicBool = AtomicBool::new(false); + static RX_BUFFER: IrqSafeLock> = IrqSafeLock::new(RingBuffer::new(0)); /// TEAM_244: Flag set when Ctrl+C (0x03) is received via serial interrupt @@ -65,6 +70,17 @@ pub fn poll_for_ctrl_c() -> bool { } pub fn read_byte() -> Option { + // TEAM_429: Check secondary input (VirtIO keyboard) first + if SECONDARY_INPUT_ENABLED.load(Ordering::Acquire) { + let ptr = SECONDARY_INPUT.load(Ordering::Acquire); + if !ptr.is_null() { + let callback: SecondaryInputFn = unsafe { core::mem::transmute(ptr) }; + if let Some(c) = callback() { + return Some(c as u8); + } + } + } + if let Some(byte) = RX_BUFFER.lock().pop() { return Some(byte); } @@ -76,6 +92,12 @@ pub fn set_secondary_output(callback: SecondaryOutputFn) { SECONDARY_OUTPUT_ENABLED.store(true, Ordering::SeqCst); } +/// TEAM_429: Register secondary input source (e.g., VirtIO keyboard) +pub fn set_secondary_input(callback: SecondaryInputFn) { + SECONDARY_INPUT.store(callback as *mut (), Ordering::SeqCst); + SECONDARY_INPUT_ENABLED.store(true, Ordering::SeqCst); +} + #[allow(dead_code)] pub fn disable_secondary_output() { SECONDARY_OUTPUT_ENABLED.store(false, Ordering::SeqCst); diff --git a/lib/hal/src/x86_64/cpu/gdt.rs b/lib/hal/src/x86_64/cpu/gdt.rs index 21300d4..ea67483 100644 --- a/lib/hal/src/x86_64/cpu/gdt.rs +++ b/lib/hal/src/x86_64/cpu/gdt.rs @@ -104,6 +104,32 @@ pub unsafe fn init() { }; crate::x86_64::cpu::lgdt(&ptr); + + // TEAM_429: Reload segment registers after loading new GDT + // Limine leaves us with its segment selectors, which may not match our GDT. + // CS must be reloaded via far return, other segments via mov. + core::arch::asm!( + // Reload CS via far return + "push {kernel_code}", // Push new CS (0x08) + "lea rax, [rip + 2f]", // Get return address + "push rax", // Push return address + "retfq", // Far return to reload CS + "2:", + // Reload data segment registers with kernel data (0x10) + "mov ax, {kernel_data}", + "mov ds, ax", + "mov es, ax", + "mov ss, ax", + // FS and GS are used for TLS, set to 0 initially + "xor ax, ax", + "mov fs, ax", + "mov gs, ax", + kernel_code = const KERNEL_CODE as u64, + kernel_data = const KERNEL_DATA, + out("rax") _, + options(nostack) + ); + crate::x86_64::cpu::ltr(TSS_SELECTOR); } } diff --git a/lib/hal/src/x86_64/mod.rs b/lib/hal/src/x86_64/mod.rs index ae01c59..c081359 100644 --- a/lib/hal/src/x86_64/mod.rs +++ b/lib/hal/src/x86_64/mod.rs @@ -68,6 +68,28 @@ pub fn init() { idt::init(); exceptions::init(); + // TEAM_429: Enable SSE/FPU for userspace + // Limine may have SSE enabled, but we ensure it explicitly after GDT reload. + // CR0: Clear EM (bit 2), set MP (bit 1) + // CR4: Set OSFXSR (bit 9), OSXMMEXCPT (bit 10) + unsafe { + core::arch::asm!( + // Modify CR0: clear EM (bit 2), set MP (bit 1) + "mov rax, cr0", + "and ax, 0xFFFB", // Clear EM (bit 2) + "or ax, 0x2", // Set MP (bit 1) + "mov cr0, rax", + // Modify CR4: set OSFXSR (bit 9), OSXMMEXCPT (bit 10) + "mov rax, cr4", + "or ax, 0x600", // Set bits 9 and 10 + "mov cr4, rax", + // Initialize FPU + "fninit", + out("rax") _, + options(nostack) + ); + } + // 3. Initialize legacy 8259 PIC // TEAM_319: APIC MMIO (0xFEE00000) is outside HHDM range, use legacy PIC instead. // This remaps IRQ0-7 to vectors 32-39 and unmasks IRQ0 (timer). diff --git a/syscall/src/process/mod.rs b/syscall/src/process/mod.rs index e78fe10..0216050 100644 --- a/syscall/src/process/mod.rs +++ b/syscall/src/process/mod.rs @@ -20,6 +20,8 @@ pub use identity::{ pub use lifecycle::{ sys_exec, sys_exit, sys_exit_group, sys_get_foreground, sys_getpid, sys_getppid, sys_set_foreground, sys_spawn, sys_spawn_args, sys_waitpid, sys_yield, + // TEAM_429: Hooks for kernel integration + RESOLVE_EXECUTABLE_HOOK, SPAWN_FROM_ELF_HOOK, SPAWN_FROM_ELF_WITH_ARGS_HOOK, }; pub use resources::{sys_getrusage, sys_prlimit64}; pub use thread::{sys_clone, sys_set_tid_address};