Files
k-os/crates/adapter-riscv/src/boot_fs.rs

144 lines
4.3 KiB
Rust

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<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).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<usize> {
let mut sector_buf = [0u8; SECTOR_SIZE];
if !blk.read_sector(0, &mut sector_buf) {
return None;
}
let fs = BootFat16::parse(&sector_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(&sector_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)
}