refactor(adapter-riscv): replace hardcoded demo with proper init launch via ELF parsing

Adapter now: parses init binary (ELF or raw), allocates pages, creates
user page table, sets thread entry point in SavedContext, enters U-mode.
Init takes over from there — no more IPC demo hardcoded in the adapter.
This commit is contained in:
2026-10-18 04:13:13 +02:00
parent 41f7e7db10
commit a910c06cf7
2 changed files with 116 additions and 39 deletions

View File

@@ -11,6 +11,7 @@ bench = false
[dependencies]
types = { workspace = true }
domain = { workspace = true }
elf = { workspace = true }
platform = { workspace = true }
kernel = { workspace = true }

View File

@@ -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<kernel::Kernel<platform::RiscVPlatform>> = 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));
launch_init(&uart, kernel_pt);
}
fn launch_init(uart: &Uart, kernel_pt: &mmu::PageTable) -> ! {
let kern = kernel_ref();
let (_pid, _tid) = kern
let entry_va = load_init_binary(kernel_pt, uart);
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");
.unwrap_or_else(|_| {
uart.write_str("k-os: FATAL — boot failed\n");
loop { unsafe { core::arch::asm!("wfi") }; }
});
userspace_demo(&uart, kernel_pt);
uart.write_str("k-os: demo complete\n");
loop {
unsafe { core::arch::asm!("wfi") };
}
// 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);
}
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
];
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");
const USER_VA_RAW: usize = 0x10000;
fn userspace_demo(uart: &Uart, kernel_pt: &mmu::PageTable) {
uart.write_str("k-os: --- userspace demo ---\n");
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(),
);
// 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);
}
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: user page table created, entering U-mode\n");
mmu::activate(user_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();