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:
125
crates/adapter-riscv/src/boot_fs.rs
Normal file
125
crates/adapter-riscv/src/boot_fs.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
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(§or_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(§or_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)
|
||||
}
|
||||
Reference in New Issue
Block a user