| // Trivial tar loader. |
| |
| use core::mem::{size_of, transmute}; |
| use core::ptr; |
| use matcha_hal::dprintf; |
| |
| const EFLASH_START: u32 = 0x44000000; |
| const EFLASH_END: u32 = 0x45000000; |
| |
| #[repr(C, packed)] |
| pub struct TarHeader { |
| /* byte offset */ |
| name: [u8; 100], /* 0 */ |
| mode: [u8; 8], /* 100 */ |
| uid: [u8; 8], /* 108 */ |
| gid: [u8; 8], /* 116 */ |
| size: [u8; 12], /* 124 */ |
| mtime: [u8; 12], /* 136 */ |
| chksum: [u8; 8], /* 148 */ |
| typeflag: u8, /* 156 */ |
| linkname: [u8; 100], /* 157 */ |
| magic: [u8; 6], /* 257 */ |
| version: [u8; 2], /* 263 */ |
| uname: [u8; 32], /* 265 */ |
| gname: [u8; 32], /* 297 */ |
| devmajor: [u8; 8], /* 329 */ |
| devminor: [u8; 8], /* 337 */ |
| prefix: [u8; 155], /* 345 */ |
| reserved: [u8; 12], /* 512 */ |
| } |
| |
| impl Default for TarHeader { |
| fn default() -> Self { |
| return TarHeader { |
| name: [0; 100], |
| mode: [0; 8], |
| uid: [0; 8], |
| gid: [0; 8], |
| size: [0; 12], |
| mtime: [0; 12], |
| chksum: [0; 8], |
| typeflag: 0, |
| linkname: [0; 100], |
| magic: [0; 6], |
| version: [0; 2], |
| uname: [0; 32], |
| gname: [0; 32], |
| devmajor: [0; 8], |
| devminor: [0; 8], |
| prefix: [0; 155], |
| reserved: [0; 12], |
| }; |
| } |
| } |
| |
| impl TarHeader { |
| pub fn from_bytes(b: &[u8]) -> TarHeader { |
| unsafe { b.as_ptr().cast::<TarHeader>().read() } |
| } |
| |
| pub fn has_magic(&self) -> bool { |
| self.magic[..5] == *b"ustar" |
| } |
| |
| pub fn name(&self) -> &str { |
| if let Some(len) = self.name.iter().position(|&x| x == 0) { |
| core::str::from_utf8(&self.name[..len]) |
| } else { |
| core::str::from_utf8(&self.name) |
| } |
| .unwrap_or("<invalid>") |
| } |
| |
| pub fn size(&self) -> u32 { |
| parse_octal(&self.size) |
| } |
| } |
| |
| fn parse_octal(text: &[u8]) -> u32 { |
| text.iter() |
| .take_while(|&&x| x != 0) |
| .fold(0u32, |acc, &x| (acc << 3) + (x as u32 - '0' as u32)) |
| } |
| |
| pub unsafe fn find_file(name: &str) -> (u32, u32) { |
| let mut cursor = EFLASH_START; |
| while (cursor + 512) < EFLASH_END { |
| let tar_header = &*(cursor as *const TarHeader); |
| if !tar_header.has_magic() { |
| // Our tar files have the "ustar" magic field; use it |
| // to avoid scanning all EFLASH. |
| break; |
| } |
| cursor += size_of::<TarHeader>() as u32; |
| let tar_name = tar_header.name(); |
| let tar_size = tar_header.size(); |
| if tar_name == name { |
| return (cursor, tar_size); |
| } else { |
| fn roundup(a: u32, b: u32) -> u32 { |
| ((a + b - 1) / b) * b |
| } |
| cursor += roundup( |
| tar_size, |
| size_of::<TarHeader>() as u32, |
| ); |
| } |
| } |
| |
| dprintf!("Could not find tar entry for '{}'\n", name); |
| return (0, 0); |
| } |
| |
| // TODO(sleffler): rethink api, e.g. supply a range for bounds check |
| pub unsafe fn copy_file(name: &str, offset: u32) -> bool { |
| let (cursor, size) = find_file(name); |
| if cursor != 0 { |
| ptr::copy_nonoverlapping::<u8>( |
| transmute(cursor), |
| transmute(offset), |
| size as usize, |
| ); |
| true |
| } else { |
| false |
| } |
| } |