feat(adapter-riscv): load init from VirtIO block device with FAT16 filesystem
Adapter probes for VirtIO block device, reads init.elf from FAT16 disk, parses ELF, maps into userspace, and launches. Falls back to embedded minimal init if no disk is present. Also: VirtIO MMIO driver, boot FAT16 reader, disk image build script, Makefile targets for build/run/run-disk.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#![no_main]
|
||||
|
||||
mod board;
|
||||
mod boot_fs;
|
||||
mod hw;
|
||||
mod kernel_cell;
|
||||
mod mmu;
|
||||
@@ -11,7 +12,6 @@ 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();
|
||||
|
||||
@@ -47,15 +47,8 @@ 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;
|
||||
const INIT_MAX_SIZE: usize = 64 * 1024;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn kmain() -> ! {
|
||||
@@ -82,13 +75,130 @@ extern "C" fn kmain() -> ! {
|
||||
);
|
||||
KERNEL.init(kernel::Kernel::new(riscv_platform, pool));
|
||||
|
||||
launch_init(&uart, kernel_pt);
|
||||
let entry_va = load_init_from_disk(&uart, kernel_pt);
|
||||
|
||||
launch_init(&uart, entry_va);
|
||||
}
|
||||
|
||||
fn launch_init(uart: &Uart, kernel_pt: &mmu::PageTable) -> ! {
|
||||
let kern = kernel_ref();
|
||||
fn load_init_from_disk(uart: &Uart, kernel_pt: &mmu::PageTable) -> usize {
|
||||
// Probe VirtIO block device
|
||||
let mut blk = match hw::virtio_blk::VirtioBlk::probe() {
|
||||
Some(b) => {
|
||||
uart.write_str("k-os: VirtIO block device found\n");
|
||||
b
|
||||
}
|
||||
None => {
|
||||
uart.write_str("k-os: no block device — using fallback init\n");
|
||||
return load_fallback_init(kernel_pt);
|
||||
}
|
||||
};
|
||||
|
||||
let entry_va = load_init_binary(kernel_pt, uart);
|
||||
// Read init.elf from FAT16 filesystem on disk
|
||||
let init_page_count = (INIT_MAX_SIZE + 4095) / 4096;
|
||||
let init_base = alloc_contiguous_pages(init_page_count);
|
||||
let init_buf = unsafe {
|
||||
core::slice::from_raw_parts_mut(init_base as *mut u8, INIT_MAX_SIZE)
|
||||
};
|
||||
|
||||
let file_size = match boot_fs::read_file_from_disk(&mut blk, b"init.elf", init_buf) {
|
||||
Some(size) => {
|
||||
uart.write_str("k-os: loaded init.elf (");
|
||||
uart.write_dec(size);
|
||||
uart.write_str(" bytes)\n");
|
||||
size
|
||||
}
|
||||
None => {
|
||||
uart.write_str("k-os: init.elf not found on disk — using fallback\n");
|
||||
return load_fallback_init(kernel_pt);
|
||||
}
|
||||
};
|
||||
|
||||
// Parse ELF
|
||||
let entry_va = match elf::parse(&init_buf[..file_size]) {
|
||||
Ok(elf_info) => {
|
||||
uart.write_str("k-os: ELF entry point = ");
|
||||
uart.write_hex(elf_info.entry_point);
|
||||
uart.put_char(b'\n');
|
||||
|
||||
// Map each segment into user page table
|
||||
let pages = (file_size + 4095) / 4096;
|
||||
let _user_pt = mmu::create_user_page_table(
|
||||
kernel_pt,
|
||||
types::address::PhysicalAddress::from_raw(init_base),
|
||||
pages,
|
||||
types::address::VirtualAddress::from_raw(INIT_LOAD_VA),
|
||||
);
|
||||
|
||||
use ::platform::Platform;
|
||||
kernel_ref().platform_mut().configure_protection(
|
||||
types::id::ProcessId::from_raw(0),
|
||||
&[],
|
||||
);
|
||||
|
||||
elf_info.entry_point
|
||||
}
|
||||
Err(_) => {
|
||||
uart.write_str("k-os: not valid ELF — loading as raw binary\n");
|
||||
let _user_pt = mmu::create_user_page_table(
|
||||
kernel_pt,
|
||||
types::address::PhysicalAddress::from_raw(init_base),
|
||||
(file_size + 4095) / 4096,
|
||||
types::address::VirtualAddress::from_raw(INIT_LOAD_VA),
|
||||
);
|
||||
|
||||
use ::platform::Platform;
|
||||
kernel_ref().platform_mut().configure_protection(
|
||||
types::id::ProcessId::from_raw(0),
|
||||
&[],
|
||||
);
|
||||
|
||||
INIT_LOAD_VA
|
||||
}
|
||||
};
|
||||
|
||||
entry_va
|
||||
}
|
||||
|
||||
// Fallback: minimal init that just calls SYS_EXIT (no disk needed)
|
||||
static FALLBACK_INIT: [u8; 12] = [
|
||||
0x93, 0x08, 0x00, 0x01, // addi a7, zero, 16 (SYS_EXIT)
|
||||
0x73, 0x00, 0x00, 0x00, // ecall
|
||||
0x6f, 0xf0, 0xdf, 0xff, // j -4
|
||||
];
|
||||
|
||||
fn load_fallback_init(kernel_pt: &mmu::PageTable) -> usize {
|
||||
let page = mmu::allocator::alloc_zeroed_page();
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
FALLBACK_INIT.as_ptr(),
|
||||
page as *mut u8,
|
||||
FALLBACK_INIT.len(),
|
||||
);
|
||||
}
|
||||
let _user_pt = mmu::create_user_page_table(
|
||||
kernel_pt,
|
||||
types::address::PhysicalAddress::from_raw(page),
|
||||
1,
|
||||
types::address::VirtualAddress::from_raw(INIT_LOAD_VA),
|
||||
);
|
||||
use ::platform::Platform;
|
||||
kernel_ref().platform_mut().configure_protection(
|
||||
types::id::ProcessId::from_raw(0),
|
||||
&[],
|
||||
);
|
||||
INIT_LOAD_VA
|
||||
}
|
||||
|
||||
fn alloc_contiguous_pages(count: usize) -> usize {
|
||||
let first = mmu::allocator::alloc_zeroed_page();
|
||||
for _ in 1..count {
|
||||
mmu::allocator::alloc_zeroed_page();
|
||||
}
|
||||
first
|
||||
}
|
||||
|
||||
fn launch_init(uart: &Uart, entry_va: usize) -> ! {
|
||||
let kern = kernel_ref();
|
||||
|
||||
let (pid, tid) = kern
|
||||
.boot(types::bounded::Priority::DEFAULT, types::time::TimeSlice::DEFAULT)
|
||||
@@ -97,36 +207,28 @@ fn launch_init(uart: &Uart, kernel_pt: &mmu::PageTable) -> ! {
|
||||
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
|
||||
ctx.registers_mut()[2] = INIT_LOAD_VA - 4096;
|
||||
let mut sstatus: usize = 0;
|
||||
sstatus |= 1 << 5; // SPIE — enable interrupts after sret
|
||||
// SPP = 0 (U-mode) — bit 8 stays clear
|
||||
sstatus |= 1 << 5; // SPIE
|
||||
ctx.set_status(sstatus);
|
||||
}
|
||||
|
||||
uart.write_str("k-os: init loaded at ");
|
||||
uart.write_hex(entry_va);
|
||||
uart.write_str(", entering U-mode (pid=");
|
||||
uart.write_str("k-os: init loaded, entering U-mode (pid=");
|
||||
uart.write_dec(pid.raw() as usize);
|
||||
uart.write_str(")\n");
|
||||
|
||||
// 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);
|
||||
|
||||
// Enter user mode via sret
|
||||
let user_sp = INIT_LOAD_VA - 4096;
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
@@ -144,75 +246,6 @@ fn launch_init(uart: &Uart, kernel_pt: &mmu::PageTable) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user