From 4030dd4315aa4b51eecb7c7553b4bdc08a712e2d Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Tue, 27 Oct 2026 16:58:11 +0100 Subject: [PATCH] feat(adapter): init+storage packed into kernel image; drop adapter FAT16 reader + virtio-blk --- Makefile | 5 +- crates/adapter-riscv/Cargo.toml | 1 - crates/adapter-riscv/build.rs | 21 ++++ crates/adapter-riscv/src/boot/hardware.rs | 11 -- crates/adapter-riscv/src/boot/loader.rs | 51 +++----- crates/adapter-riscv/src/boot_fs.rs | 143 ---------------------- crates/adapter-riscv/src/hw/mod.rs | 1 - crates/adapter-riscv/src/hw/virtio_blk.rs | 63 ---------- crates/adapter-riscv/src/main.rs | 10 +- crates/drivers/input/virtio/src/lib.rs | 2 +- 10 files changed, 43 insertions(+), 265 deletions(-) delete mode 100644 crates/adapter-riscv/src/boot_fs.rs delete mode 100644 crates/adapter-riscv/src/hw/virtio_blk.rs diff --git a/Makefile b/Makefile index fec52f3..845b92b 100644 --- a/Makefile +++ b/Makefile @@ -48,12 +48,15 @@ kani-kernel: kani: kani-types kani-domain kani-kernel +# Programs first: the kernel image packs init and storage (see the +# adapter's build.rs). build: cargo build --target $(RISCV_TARGET) --release \ - -p adapter-riscv -p init -p echo -p snake \ + -p init -p echo -p snake \ -p display-qemu -p input-qemu -p storage-qemu \ -p log-server -p permission-manager -p session-server -p smptest -p spawntest -p hello \ -p terminal-server -p shell + cargo build --target $(RISCV_TARGET) --release -p adapter-riscv image: build scripts/make-disk-image.sh diff --git a/crates/adapter-riscv/Cargo.toml b/crates/adapter-riscv/Cargo.toml index 2146002..ce9e7ed 100644 --- a/crates/adapter-riscv/Cargo.toml +++ b/crates/adapter-riscv/Cargo.toml @@ -14,7 +14,6 @@ domain = { workspace = true } elf = { workspace = true } platform = { workspace = true } kernel = { workspace = true } -hw-virtio = { workspace = true } [profile.release] panic = "abort" diff --git a/crates/adapter-riscv/build.rs b/crates/adapter-riscv/build.rs index c0935ae..83636f3 100644 --- a/crates/adapter-riscv/build.rs +++ b/crates/adapter-riscv/build.rs @@ -1,5 +1,26 @@ +use std::path::PathBuf; + fn main() { let ld = std::path::Path::new("linker.ld").canonicalize().unwrap(); println!("cargo:rustc-link-arg=-T{}", ld.display()); println!("cargo:rerun-if-changed=linker.ld"); + + // Init and storage are packed into the kernel image: they are what + // reads the disk, so they cannot come from it. `make build` builds them + // first; without them the image boots to "init missing". + let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + let bins = manifest.join("../../target/riscv64gc-unknown-none-elf/release"); + let out = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + for (var, bin) in [("KOS_INIT_ELF", "init"), ("KOS_STORAGE_ELF", "storage-qemu")] { + let path = bins.join(bin); + println!("cargo:rerun-if-changed={}", path.display()); + let path = if path.is_file() { + path + } else { + let empty = out.join(bin); + std::fs::write(&empty, []).unwrap(); + empty + }; + println!("cargo:rustc-env={}={}", var, path.display()); + } } diff --git a/crates/adapter-riscv/src/boot/hardware.rs b/crates/adapter-riscv/src/boot/hardware.rs index 40cd906..8849122 100644 --- a/crates/adapter-riscv/src/boot/hardware.rs +++ b/crates/adapter-riscv/src/boot/hardware.rs @@ -1,5 +1,4 @@ use crate::hw::uart::Uart; -use crate::hw::virtio_blk::VirtioBlk; use crate::{KERNEL, board, mmu, platform, trap}; unsafe extern "C" { @@ -72,16 +71,6 @@ pub fn init(uart: &Uart, boot_hart: usize) -> &'static mut mmu::PageTable { kernel_pt } -pub fn probe_block_device(uart: &Uart) -> Option { - 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; diff --git a/crates/adapter-riscv/src/boot/loader.rs b/crates/adapter-riscv/src/boot/loader.rs index 30c5482..600ee95 100644 --- a/crates/adapter-riscv/src/boot/loader.rs +++ b/crates/adapter-riscv/src/boot/loader.rs @@ -1,6 +1,5 @@ use crate::hw::uart::Uart; -use crate::hw::virtio_blk::VirtioBlk; -use crate::{boot_fs, mmu}; +use crate::mmu; use types::address::VirtualAddress; use types::budget::Limits; use types::id::{ProcessId, ThreadId}; @@ -10,7 +9,6 @@ const PAGE_SIZE: usize = mmu::PAGE_SIZE; const USER_VA_BASE: usize = 0x10000; /// The stack sits just below the image and stops short of the null page. const USER_STACK_PAGES: usize = USER_VA_BASE / PAGE_SIZE - 1; -const MAX_FILE_SIZE: usize = 64 * 1024; pub struct LoadedProcess { pub pid: ProcessId, @@ -20,23 +18,11 @@ fn kern() -> &'static mut kernel::Kernel { unsafe { &mut *crate::kernel_ptr() } } -/// Scratch space for reading ELF files, allocated once from the boot heap. -fn elf_buffer() -> &'static mut [u8] { - static mut BUF: usize = 0; - // SAFETY: boot is single threaded; the buffer is kernel-only memory - // from the boot allocator and is never handed to a process. - unsafe { - if BUF == 0 { - BUF = mmu::allocator::alloc_zeroed_page(); - for _ in 1..MAX_FILE_SIZE / PAGE_SIZE { - mmu::allocator::alloc_zeroed_page(); - } - } - core::slice::from_raw_parts_mut(BUF as *mut u8, MAX_FILE_SIZE) - } -} +/// The two programs boot starts, packed into the kernel image. +pub static INIT_ELF: &[u8] = include_bytes!(env!("KOS_INIT_ELF")); +pub static STORAGE_ELF: &[u8] = include_bytes!(env!("KOS_STORAGE_ELF")); -pub fn load_init(uart: &Uart, blk: &mut VirtioBlk) -> LoadedProcess { +pub fn load_init(uart: &Uart) -> LoadedProcess { let (pid, tid) = kern() .boot( types::bounded::Priority::DEFAULT, @@ -44,7 +30,7 @@ pub fn load_init(uart: &Uart, blk: &mut VirtioBlk) -> LoadedProcess { ) .unwrap_or_else(|_| crate::halt(uart, "boot failed")); - let entry = load_elf(uart, blk, pid, b"init.elf"); + let entry = load_elf(uart, pid, INIT_ELF); setup_thread(tid, entry); uart.write_str("k-os: init loaded\n"); @@ -60,24 +46,18 @@ const STORAGE: Limits = Limits { regions: 12, }; -pub fn load_server( - uart: &Uart, - blk: &mut VirtioBlk, - init: ProcessId, - filename: &[u8], - label: &str, -) -> LoadedProcess { +/// Storage, which init needs to read anything else, its budget carved +/// from init's. +pub fn load_storage(uart: &Uart, init: ProcessId) -> LoadedProcess { let pid = kern() .create_process_internal(init, STORAGE) .unwrap_or_else(|| crate::halt(uart, "process create failed")); - let entry = load_elf(uart, blk, pid, filename); + let entry = load_elf(uart, pid, STORAGE_ELF); let tid = kern() .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"); + uart.write_str("k-os: storage loaded\n"); LoadedProcess { pid } } @@ -127,13 +107,10 @@ fn segment_perms(flags: elf::SegmentFlags) -> Option { /// Loads `filename` into `pid` through the kernel's own mapping API, so /// every page is tracked, charged and checked like any other. Halts on any /// malformed or overlapping image: boot servers are trusted to be sane. -fn load_elf(uart: &Uart, blk: &mut VirtioBlk, pid: ProcessId, filename: &[u8]) -> usize { - let buf = elf_buffer(); - let file_size = boot_fs::read_file_from_disk(blk, filename, buf).unwrap_or(0); - if file_size == 0 { - crate::halt(uart, "ELF not found"); +fn load_elf(uart: &Uart, pid: ProcessId, file: &[u8]) -> usize { + if file.is_empty() { + crate::halt(uart, "program missing from the kernel image (make build)"); } - let file = &buf[..file_size]; let elf_info = elf::parse(file).unwrap_or_else(|_| crate::halt(uart, "bad ELF")); for segment in elf_info.segments() { diff --git a/crates/adapter-riscv/src/boot_fs.rs b/crates/adapter-riscv/src/boot_fs.rs deleted file mode 100644 index d2345cf..0000000 --- a/crates/adapter-riscv/src/boot_fs.rs +++ /dev/null @@ -1,143 +0,0 @@ -use crate::hw::virtio_blk::VirtioBlk; - -const SECTOR_SIZE: usize = 512; - -struct BootFat16 { - reserved_sectors: u16, - root_dir_start: u32, - root_dir_sectors: u32, - data_start: u32, - sectors_per_cluster: u8, -} - -impl BootFat16 { - fn parse(boot_sector: &[u8; SECTOR_SIZE]) -> Option { - if boot_sector[510] != 0x55 || boot_sector[511] != 0xAA { - return None; - } - - let bytes_per_sector = u16::from_le_bytes([boot_sector[11], boot_sector[12]]); - if bytes_per_sector != SECTOR_SIZE as u16 { - return None; - } - - let sectors_per_cluster = boot_sector[13]; - let reserved_sectors = u16::from_le_bytes([boot_sector[14], boot_sector[15]]); - let num_fats = boot_sector[16]; - let root_entry_count = u16::from_le_bytes([boot_sector[17], boot_sector[18]]); - let sectors_per_fat = u16::from_le_bytes([boot_sector[22], boot_sector[23]]); - - let fat_start = reserved_sectors as u32; - let root_dir_start = fat_start + (num_fats as u32) * (sectors_per_fat as u32); - let root_dir_sectors = ((root_entry_count as u32) * 32).div_ceil(512); - let data_start = root_dir_start + root_dir_sectors; - - Some(Self { - reserved_sectors, - root_dir_start, - root_dir_sectors, - data_start, - sectors_per_cluster, - }) - } - - fn cluster_to_sector(&self, cluster: u16) -> u32 { - self.data_start + ((cluster as u32) - 2) * (self.sectors_per_cluster as u32) - } -} - -fn to_fat_name(name: &[u8]) -> [u8; 11] { - let mut fat = [b' '; 11]; - let dot_pos = name.iter().position(|&b| b == b'.').unwrap_or(name.len()); - for i in 0..dot_pos.min(8) { - fat[i] = name[i].to_ascii_uppercase(); - } - if dot_pos < name.len() { - for i in 0..(name.len() - dot_pos - 1).min(3) { - fat[8 + i] = name[dot_pos + 1 + i].to_ascii_uppercase(); - } - } - fat -} - -pub fn read_file_from_disk( - blk: &mut VirtioBlk, - filename: &[u8], - out_buf: &mut [u8], -) -> Option { - let mut sector_buf = [0u8; SECTOR_SIZE]; - - if !blk.read_sector(0, &mut sector_buf) { - return None; - } - let fs = BootFat16::parse(§or_buf)?; - - let fat_name = to_fat_name(filename); - - let mut first_cluster: u16 = 0; - let mut file_size: u32 = 0; - let mut found = false; - - for s in 0..fs.root_dir_sectors { - if !blk.read_sector(fs.root_dir_start + s, &mut sector_buf) { - continue; - } - for i in 0..(SECTOR_SIZE / 32) { - let off = i * 32; - if sector_buf[off] == 0x00 { - break; - } - if sector_buf[off] == 0xE5 { - continue; - } - if sector_buf[off..off + 11] == fat_name { - first_cluster = u16::from_le_bytes([sector_buf[off + 26], sector_buf[off + 27]]); - file_size = u32::from_le_bytes([ - sector_buf[off + 28], - sector_buf[off + 29], - sector_buf[off + 30], - sector_buf[off + 31], - ]); - found = true; - break; - } - } - if found { - break; - } - } - - if !found { - return None; - } - - let size = (file_size as usize).min(out_buf.len()); - let mut read = 0; - let mut cluster = first_cluster; - let sectors_per_cluster = fs.sectors_per_cluster as u32; - - while read < size && (2..0xFFF8).contains(&cluster) { - for s in 0..sectors_per_cluster { - if read >= size { - break; - } - let sector = fs.cluster_to_sector(cluster) + s; - if !blk.read_sector(sector, &mut sector_buf) { - return Some(read); - } - let chunk = (size - read).min(SECTOR_SIZE); - out_buf[read..read + chunk].copy_from_slice(§or_buf[..chunk]); - read += chunk; - } - - let fat_offset = (cluster as u32) * 2; - let fat_sector = fs.reserved_sectors as u32 + fat_offset / SECTOR_SIZE as u32; - let fat_byte = (fat_offset % SECTOR_SIZE as u32) as usize; - if !blk.read_sector(fat_sector, &mut sector_buf) { - break; - } - cluster = u16::from_le_bytes([sector_buf[fat_byte], sector_buf[fat_byte + 1]]); - } - - Some(read) -} diff --git a/crates/adapter-riscv/src/hw/mod.rs b/crates/adapter-riscv/src/hw/mod.rs index 679c465..88a95ab 100644 --- a/crates/adapter-riscv/src/hw/mod.rs +++ b/crates/adapter-riscv/src/hw/mod.rs @@ -1,4 +1,3 @@ pub mod plic; pub mod timer; pub mod uart; -pub mod virtio_blk; diff --git a/crates/adapter-riscv/src/hw/virtio_blk.rs b/crates/adapter-riscv/src/hw/virtio_blk.rs deleted file mode 100644 index c08b711..0000000 --- a/crates/adapter-riscv/src/hw/virtio_blk.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::mmu::{phys_to_virt, virt_to_phys}; -use hw_virtio::block::{self, DmaAddrs}; -use hw_virtio::mmio::{self, DirectMmio}; -use hw_virtio::queue::{QUEUE_SIZE, QueueAddrs, VirtioQueue}; -use types::address::{PhysicalAddress, VirtualAddress}; - -const SECTOR_SIZE: usize = 512; - -#[repr(C, align(4096))] -struct VirtqMem { - _data: [u8; 8192], -} - -static mut VQUEUE: VirtqMem = VirtqMem { _data: [0; 8192] }; -static mut REQ_HEADER: block::BlockReqHeader = block::BlockReqHeader { - req_type: 0, - reserved: 0, - sector: 0, -}; -static mut REQ_STATUS: u8 = 0xFF; -static mut SECTOR_BUF: [u8; SECTOR_SIZE] = [0; SECTOR_SIZE]; - -pub struct VirtioBlk { - mmio: DirectMmio, - queue: VirtioQueue, - dma: DmaAddrs, -} - -impl VirtioBlk { - pub fn probe() -> Option { - let mmio = mmio::scan_for_device(phys_to_virt(mmio::MMIO_FIRST), mmio::DEVICE_ID_BLOCK)?; - - // The device sees physical addresses; the CPU uses kernel ones. - let vq_va = core::ptr::addr_of!(VQUEUE) as usize; - mmio::init_legacy_device(&mmio, virt_to_phys(vq_va), QUEUE_SIZE as u32); - - let queue = VirtioQueue::new(QueueAddrs::from_base(vq_va)); - - let pair = |va: usize| { - ( - PhysicalAddress::from_raw(virt_to_phys(va)), - VirtualAddress::from_raw(va), - ) - }; - let (header_pa, header_va) = pair(core::ptr::addr_of_mut!(REQ_HEADER) as usize); - let (status_pa, status_va) = pair(core::ptr::addr_of_mut!(REQ_STATUS) as usize); - let (data_pa, data_va) = pair(core::ptr::addr_of_mut!(SECTOR_BUF) as usize); - let dma = DmaAddrs { - header_pa, - header_va, - status_pa, - status_va, - data_pa, - data_va, - }; - - Some(Self { mmio, queue, dma }) - } - - pub fn read_sector(&mut self, sector: u32, buf: &mut [u8; SECTOR_SIZE]) -> bool { - block::read_sector(&self.mmio, &self.queue, &self.dma, sector, buf) - } -} diff --git a/crates/adapter-riscv/src/main.rs b/crates/adapter-riscv/src/main.rs index 05a7dbd..da77d3e 100644 --- a/crates/adapter-riscv/src/main.rs +++ b/crates/adapter-riscv/src/main.rs @@ -3,7 +3,6 @@ mod board; mod boot; -mod boot_fs; mod devices; mod entry; mod hw; @@ -36,12 +35,9 @@ extern "C" fn kmain(hart: usize) -> ! { boot::hardware::init(&uart, hart); // Not before init: zeroing .bss resets the lock. smp::lock(); - let mut blk = - boot::hardware::probe_block_device(&uart).unwrap_or_else(|| halt(&uart, "no block device")); - - let init = boot::loader::load_init(&uart, &mut blk); - // Init needs storage to load anything else; the rest is init's to start. - let storage = boot::loader::load_server(&uart, &mut blk, init.pid, b"storage.elf", "storage"); + // Both come from the kernel image; init starts everything else from disk. + let init = boot::loader::load_init(&uart); + let storage = boot::loader::load_storage(&uart, init.pid); let board = board::devices().unwrap_or_else(|| halt(&uart, "bad board table")); kern_mut() diff --git a/crates/drivers/input/virtio/src/lib.rs b/crates/drivers/input/virtio/src/lib.rs index 03ccc3c..a896245 100644 --- a/crates/drivers/input/virtio/src/lib.rs +++ b/crates/drivers/input/virtio/src/lib.rs @@ -3,7 +3,7 @@ mod keymap; use hw_virtio::mmio::MmioAccess; -use port_input_device::{InputDevice, InputEvent, Key}; +use port_input_device::{InputDevice, InputEvent}; const INPUT_MMIO_VA: usize = 0x25000; const VQUEUE_VA: usize = 0x26000;