diff --git a/crates/adapter-riscv/Cargo.toml b/crates/adapter-riscv/Cargo.toml index 9b2d511..ce9e7ed 100644 --- a/crates/adapter-riscv/Cargo.toml +++ b/crates/adapter-riscv/Cargo.toml @@ -11,6 +11,7 @@ bench = false [dependencies] types = { workspace = true } domain = { workspace = true } +elf = { workspace = true } platform = { workspace = true } kernel = { workspace = true } diff --git a/crates/adapter-riscv/src/main.rs b/crates/adapter-riscv/src/main.rs index 3459cb8..76eae8a 100644 --- a/crates/adapter-riscv/src/main.rs +++ b/crates/adapter-riscv/src/main.rs @@ -11,6 +11,7 @@ mod trap; use core::arch::global_asm; use hw::uart::Uart; use kernel_cell::KernelCell; +use ::platform::Platform; static KERNEL: KernelCell> = KernelCell::uninit(); @@ -46,6 +47,16 @@ fn zero_bss() { } } +// Embedded init binary — in production this comes from the boot image. +// For now, a minimal program: call SYS_EXIT and loop. +static INIT_BINARY: [u8; 12] = [ + 0x93, 0x08, 0x00, 0x01, // addi a7, zero, 16 (SYS_EXIT) + 0x73, 0x00, 0x00, 0x00, // ecall + 0x6f, 0xf0, 0xdf, 0xff, // j -4 +]; + +const INIT_LOAD_VA: usize = 0x10000; + #[unsafe(no_mangle)] extern "C" fn kmain() -> ! { zero_bss(); @@ -70,57 +81,53 @@ extern "C" fn kmain() -> ! { board::USER_POOL_PAGES, ); KERNEL.init(kernel::Kernel::new(riscv_platform, pool)); - let kern = kernel_ref(); - let (_pid, _tid) = kern - .boot(types::bounded::Priority::DEFAULT, types::time::TimeSlice::DEFAULT) - .unwrap_or_else(|_| loop { unsafe { core::arch::asm!("wfi") }; }); - uart.write_str("k-os: kernel started\n"); - - userspace_demo(&uart, kernel_pt); - - uart.write_str("k-os: demo complete\n"); - loop { - unsafe { core::arch::asm!("wfi") }; - } + launch_init(&uart, kernel_pt); } -static USER_EXIT_PROGRAM: [u8; 12] = [ - 0x93, 0x08, 0x00, 0x01, // addi a7, zero, 16 (SYS_EXIT) - 0x73, 0x00, 0x00, 0x00, // ecall - 0x6f, 0xf0, 0xdf, 0xff, // j -4 -]; +fn launch_init(uart: &Uart, kernel_pt: &mmu::PageTable) -> ! { + let kern = kernel_ref(); -const USER_VA_RAW: usize = 0x10000; + let entry_va = load_init_binary(kernel_pt, uart); -fn userspace_demo(uart: &Uart, kernel_pt: &mmu::PageTable) { - uart.write_str("k-os: --- userspace demo ---\n"); + let (pid, tid) = kern + .boot(types::bounded::Priority::DEFAULT, types::time::TimeSlice::DEFAULT) + .unwrap_or_else(|_| { + uart.write_str("k-os: FATAL — boot failed\n"); + loop { unsafe { core::arch::asm!("wfi") }; } + }); - let program_page = mmu::allocator::alloc_zeroed_page(); - unsafe { - core::ptr::copy_nonoverlapping( - USER_EXIT_PROGRAM.as_ptr(), - program_page as *mut u8, - USER_EXIT_PROGRAM.len(), - ); + // Set init thread's entry point to the loaded binary + if let Some(ctx) = kern.thread_context_mut(tid) { + ctx.set_pc(entry_va); + ctx.registers_mut()[2] = INIT_LOAD_VA - 4096; // sp below program + // Set sstatus for U-mode return + let mut sstatus: usize = 0; + sstatus |= 1 << 5; // SPIE — enable interrupts after sret + // SPP = 0 (U-mode) — bit 8 stays clear + ctx.set_status(sstatus); } - let user_pt = mmu::create_user_page_table( - kernel_pt, - types::address::PhysicalAddress::from_raw(program_page), - 1, - types::address::VirtualAddress::from_raw(USER_VA_RAW), - ); + uart.write_str("k-os: init loaded at "); + uart.write_hex(entry_va); + uart.write_str(", entering U-mode (pid="); + uart.write_dec(pid.raw() as usize); + uart.write_str(")\n"); - uart.write_str("k-os: user page table created, entering U-mode\n"); - - mmu::activate(user_pt); + // Activate init's page table + let init_pt_addr = kern.platform_mut().page_table_for(pid); + if init_pt_addr != 0 { + let pt = unsafe { &*(init_pt_addr as *const mmu::PageTable) }; + mmu::activate(pt); + } + // Set kernel stack in sscratch for trap handler let kernel_sp: usize; unsafe { core::arch::asm!("mv {0}, sp", out(reg) kernel_sp) }; trap::set_kernel_stack(kernel_sp); - let user_stack = USER_VA_RAW - 8; + // Enter user mode via sret + let user_sp = INIT_LOAD_VA - 4096; unsafe { core::arch::asm!( "csrc sstatus, {spp}", @@ -130,13 +137,82 @@ fn userspace_demo(uart: &Uart, kernel_pt: &mmu::PageTable) { "sret", spp = in(reg) 1usize << 8, spie = in(reg) 1usize << 5, - entry = in(reg) USER_VA_RAW, - stack = in(reg) user_stack, + entry = in(reg) entry_va, + stack = in(reg) user_sp, options(noreturn), ); } } +fn load_init_binary(kernel_pt: &mmu::PageTable, uart: &Uart) -> usize { + // Try to parse as ELF + if let Ok(elf_info) = elf::parse(&INIT_BINARY) { + uart.write_str("k-os: init is ELF, entry="); + uart.write_hex(elf_info.entry_point); + uart.put_char(b'\n'); + + for segment in elf_info.segments() { + let pages = (segment.mem_size + 4095) / 4096; + for p in 0..pages { + let page = mmu::allocator::alloc_zeroed_page(); + let src_offset = segment.file_offset + p * 4096; + let copy_len = if p * 4096 < segment.file_size { + (segment.file_size - p * 4096).min(4096) + } else { + 0 + }; + if copy_len > 0 && src_offset + copy_len <= INIT_BINARY.len() { + unsafe { + core::ptr::copy_nonoverlapping( + INIT_BINARY.as_ptr().add(src_offset), + page as *mut u8, + copy_len, + ); + } + } + } + } + + let kern = kernel_ref(); + kern.platform_mut().configure_protection( + types::id::ProcessId::from_raw(0), + &[], + ); + + elf_info.entry_point + } else { + // Raw binary — copy to a page and map at INIT_LOAD_VA + uart.write_str("k-os: init is raw binary, loading at "); + uart.write_hex(INIT_LOAD_VA); + uart.put_char(b'\n'); + + let program_page = mmu::allocator::alloc_zeroed_page(); + unsafe { + core::ptr::copy_nonoverlapping( + INIT_BINARY.as_ptr(), + program_page as *mut u8, + INIT_BINARY.len(), + ); + } + + let _user_pt = mmu::create_user_page_table( + kernel_pt, + types::address::PhysicalAddress::from_raw(program_page), + 1, + types::address::VirtualAddress::from_raw(INIT_LOAD_VA), + ); + + // Store the page table for init (pid 0) + let kern = kernel_ref(); + kern.platform_mut().configure_protection( + types::id::ProcessId::from_raw(0), + &[], + ); + + INIT_LOAD_VA + } +} + #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { let uart = Uart::new();