refactor(adapter-riscv): split kmain into boot/, devices/ modules

kmain is now 20 lines — six function calls:

  hardware::init        → BSS, MMU, traps, timer, PLIC, kernel
  loader::load_init     → read init.elf, create process, map ELF
  loader::load_server   → read server ELF, create process, map ELF
  storage_setup         → VirtIO block re-init, shared buffer, phys-info
  gpu_setup             → VirtIO GPU init, framebuffer alloc, phys-info
  caps::grant_all       → endpoints, capability grants

New modules:
  boot/hardware.rs   — hardware init (BSS, MMU, traps, kernel)
  boot/loader.rs     — ELF loading, thread setup, enter_userspace
  boot/caps.rs       — endpoint creation, capability grants
  devices/storage_setup.rs — VirtIO block for storage server
  devices/gpu_setup.rs     — VirtIO GPU for display server

All unsafe contained in hw/, mmu/, trap/. Boot and device
modules call safe wrappers.
This commit is contained in:
2026-10-18 13:19:16 +02:00
parent 5766c6f7f7
commit 8d53fa9ac5
8 changed files with 495 additions and 461 deletions

View File

@@ -0,0 +1,50 @@
use crate::hw::uart::Uart;
use crate::kernel_ref;
use types::id::ProcessId;
pub fn grant_all(
uart: &Uart,
init_pid: ProcessId,
storage_pid: ProcessId,
display_pid: ProcessId,
) {
let all_perms = types::permission::PermissionSet::empty()
.with(types::permission::Permission::Read)
.with(types::permission::Permission::Write)
.with(types::permission::Permission::Execute)
.with(types::permission::Permission::Delegate);
let ipc_perms = types::permission::PermissionSet::empty()
.with(types::permission::Permission::Read)
.with(types::permission::Permission::Execute);
// Storage endpoint
let storage_ep = kernel_ref().create_endpoint()
.unwrap_or_else(|_| crate::halt(uart, "storage endpoint failed"));
// Init: slot 0 = ProcessCreate (from boot), slot 1 = Process(storage), slot 2 = Endpoint(storage)
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(storage_pid), all_perms)
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Endpoint(storage_ep), all_perms)
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
// Storage: slot 0 = Endpoint
kernel_ref().grant_capability(storage_pid, domain::resource::Resource::Endpoint(storage_ep), ipc_perms)
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
// Display endpoint
let display_ep = kernel_ref().create_endpoint()
.unwrap_or_else(|_| crate::halt(uart, "display endpoint failed"));
// Display: slot 0 = Endpoint
kernel_ref().grant_capability(display_pid, domain::resource::Resource::Endpoint(display_ep), ipc_perms)
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
// Init: slot 3 = Endpoint(display), slot 4 = Process(display)
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Endpoint(display_ep), all_perms)
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(display_pid), all_perms)
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
uart.write_str("k-os: capabilities granted\n");
}

View File

@@ -0,0 +1,52 @@
use crate::hw::uart::Uart;
use crate::hw::virtio_blk::VirtioBlk;
use crate::{board, kernel_cell::KernelCell, mmu, platform, trap, KERNEL};
unsafe extern "C" {
static _bss_start: u8;
static _bss_end: u8;
static _heap_start: u8;
}
pub fn init(uart: &Uart) -> &'static mut mmu::PageTable {
zero_bss();
let heap_start = unsafe { &_heap_start as *const u8 as usize };
mmu::init_allocator(heap_start);
let kernel_pt = mmu::init_kernel_page_table();
mmu::activate(kernel_pt);
uart.write_str("k-os: MMU enabled (Sv39)\n");
trap::init();
crate::hw::timer::init();
crate::hw::plic::Plic::init();
uart.write_str("k-os: traps and timer initialized\n");
let riscv_platform = platform::RiscVPlatform::new(kernel_pt as *const mmu::PageTable as usize);
let pool = domain::memory::page_pool::PagePool::new(
types::address::PhysicalAddress::from_raw(board::USER_POOL_BASE),
board::USER_POOL_PAGES,
);
KERNEL.init(kernel::Kernel::new(riscv_platform, pool));
kernel_pt
}
pub fn probe_block_device(uart: &Uart) -> Option<VirtioBlk> {
let blk = VirtioBlk::probe();
if blk.is_some() {
uart.write_str("k-os: VirtIO block device found\n");
} else {
uart.write_str("k-os: no block device\n");
}
blk
}
fn zero_bss() {
unsafe {
let start = &_bss_start as *const u8 as *mut u8;
let end = &_bss_end as *const u8 as *mut u8;
let len = end as usize - start as usize;
core::ptr::write_bytes(start, 0, len);
}
}

View File

@@ -0,0 +1,173 @@
use crate::hw::uart::Uart;
use crate::hw::virtio_blk::VirtioBlk;
use crate::{board, boot_fs, kernel_ref, mmu};
use types::id::{ProcessId, ThreadId};
const USER_VA_BASE: usize = 0x10000;
const MAX_FILE_SIZE: usize = 64 * 1024;
pub struct LoadedProcess {
pub pid: ProcessId,
pub entry: usize,
}
pub fn load_init(
uart: &Uart,
blk: &mut VirtioBlk,
kernel_pt: &mmu::PageTable,
) -> (LoadedProcess, ThreadId) {
let (pid, tid) = kernel_ref()
.boot(types::bounded::Priority::DEFAULT, types::time::TimeSlice::DEFAULT)
.unwrap_or_else(|_| crate::halt(uart, "boot failed"));
let entry = load_elf(blk, kernel_pt, pid, b"init.elf");
if entry == 0 { crate::halt(uart, "init.elf not found"); }
setup_thread(tid, entry);
uart.write_str("k-os: init loaded\n");
(LoadedProcess { pid, entry }, tid)
}
pub fn load_server(
uart: &Uart,
blk: &mut VirtioBlk,
kernel_pt: &mmu::PageTable,
filename: &[u8],
label: &str,
) -> LoadedProcess {
let pid = kernel_ref().create_process_internal()
.unwrap_or_else(|| crate::halt(uart, "process create failed"));
let entry = load_elf(blk, kernel_pt, pid, filename);
if entry == 0 { crate::halt(uart, "elf not found"); }
let tid = kernel_ref().spawn_thread_internal(pid)
.unwrap_or_else(|| crate::halt(uart, "thread spawn failed"));
setup_thread(tid, entry);
uart.write_str("k-os: ");
uart.write_str(label);
uart.write_str(" loaded\n");
LoadedProcess { pid, entry }
}
pub fn enter_userspace(pid: ProcessId, entry: usize, shared_buf_va: usize) -> ! {
let kern = kernel_ref();
let pt_addr = kern.platform_mut().page_table_for(pid);
if pt_addr != 0 {
mmu::activate(unsafe { &*(pt_addr as *const mmu::PageTable) });
}
let kernel_sp: usize;
unsafe { core::arch::asm!("mv {0}, sp", out(reg) kernel_sp) };
crate::trap::set_kernel_stack(kernel_sp);
unsafe {
core::arch::asm!(
"csrc sstatus, {spp}",
"csrs sstatus, {spie}",
"csrw sepc, {entry}",
"mv sp, {stack}",
"mv a0, {arg}",
"sret",
spp = in(reg) 1usize << 8,
spie = in(reg) 1usize << 5,
entry = in(reg) entry,
stack = in(reg) USER_VA_BASE,
arg = in(reg) shared_buf_va,
options(noreturn),
);
}
}
fn setup_thread(tid: ThreadId, entry: usize) {
if let Some(ctx) = kernel_ref().thread_context_mut(tid) {
ctx.set_pc(entry);
ctx.registers_mut()[2] = USER_VA_BASE;
ctx.set_status(1 << 5);
}
}
fn load_elf(
blk: &mut VirtioBlk,
kernel_pt: &mmu::PageTable,
pid: ProcessId,
filename: &[u8],
) -> usize {
let buf_base = mmu::allocator::alloc_zeroed_page();
let file_pages = MAX_FILE_SIZE / 4096;
for _ in 1..file_pages {
mmu::allocator::alloc_zeroed_page();
}
let buf = unsafe { core::slice::from_raw_parts_mut(buf_base as *mut u8, MAX_FILE_SIZE) };
let file_size = boot_fs::read_file_from_disk(blk, filename, buf).unwrap_or(0);
if file_size == 0 { return 0; }
let elf_info = match elf::parse(&buf[..file_size]) {
Ok(info) => info,
Err(_) => return 0,
};
let user_pt = mmu::create_user_page_table(
kernel_pt,
types::address::PhysicalAddress::from_raw(0),
0,
types::address::VirtualAddress::from_raw(USER_VA_BASE),
);
for segment in elf_info.segments() {
let page_start = segment.vaddr & !0xFFF;
let offset_in_page = segment.vaddr & 0xFFF;
let total_size = offset_in_page + segment.mem_size;
let seg_pages = (total_size + 4095) / 4096;
for p in 0..seg_pages {
let page = mmu::allocator::alloc_zeroed_page();
let page_va = page_start + p * 4096;
let seg_file_start = if p == 0 { 0 } else { p * 4096 - offset_in_page };
let dest_off = if p == 0 { offset_in_page } else { 0 };
let copy_len = if seg_file_start < segment.file_size {
(segment.file_size - seg_file_start).min(4096 - dest_off)
} else { 0 };
if copy_len > 0 {
let src = segment.file_offset + seg_file_start;
if src + copy_len <= file_size {
unsafe {
core::ptr::copy_nonoverlapping(
buf.as_ptr().add(src),
(page as *mut u8).add(dest_off),
copy_len,
);
}
}
}
let flags = if segment.flags.execute {
mmu::Sv39Flags::user_rx()
} else if segment.flags.write {
mmu::Sv39Flags::user_rw()
} else {
mmu::Sv39Flags::user_rx()
};
mmu::map(user_pt, mmu::Mapping {
va: types::address::VirtualAddress::from_raw(page_va),
pa: types::address::PhysicalAddress::from_raw(page),
pages: 1,
flags,
});
}
}
for i in 0..4usize {
let sp = mmu::allocator::alloc_zeroed_page();
mmu::map(user_pt, mmu::Mapping {
va: types::address::VirtualAddress::from_raw(USER_VA_BASE - (4 - i) * 4096),
pa: types::address::PhysicalAddress::from_raw(sp),
pages: 1,
flags: mmu::Sv39Flags::user_rw(),
});
}
kernel_ref().platform_mut().set_page_table(pid, user_pt as *const mmu::PageTable as usize);
elf_info.entry_point
}

View File

@@ -0,0 +1,3 @@
pub mod caps;
pub mod hardware;
pub mod loader;