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.
126 lines
4.3 KiB
Rust
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(§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)
|
|
}
|