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:
173
crates/adapter-riscv/src/boot/loader.rs
Normal file
173
crates/adapter-riscv/src/boot/loader.rs
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user