Files
k-os/crates/adapter-riscv/src/boot_fs.rs
Gabriel Kaszewski d43c6a7e74 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.
2026-10-18 04:19:19 +02:00

126 lines
4.3 KiB
Rust

use crate::hw::virtio_blk::VirtioBlk;
const SECTOR_SIZE: usize = 512;
const MAX_FILE_SIZE: usize = 64 * 1024; // 64K max for init binary
struct BootFat16 {
reserved_sectors: u16,
sectors_per_fat: 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<Self> {
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 + 511) / 512;
let data_start = root_dir_start + root_dir_sectors;
Some(Self {
reserved_sectors,
sectors_per_fat,
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<usize> {
let mut sector_buf = [0u8; SECTOR_SIZE];
// Read boot sector
if !blk.read_sector(0, &mut sector_buf) { return None; }
let fs = BootFat16::parse(&sector_buf)?;
let fat_name = to_fat_name(filename);
// Search root directory
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) as u32, &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 && cluster >= 2 && cluster < 0xFFF8 {
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(&sector_buf[..chunk]);
read += chunk;
}
// Read next cluster from FAT
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)
}