diff --git a/flake.nix b/flake.nix index cf12d77..a05a145 100644 --- a/flake.nix +++ b/flake.nix @@ -112,7 +112,12 @@ src = builtins.filterSource (path: type: baseNameOf path != "target" ) ./.; - cargoLock = { lockFile = ./Cargo.lock; }; + cargoLock = { + lockFile = ./Cargo.lock; + outputHashes = { + "fatfs-0.4.0" = "sha256-P7IgvhwTPXtNhcyv8cFqwO2UdaEcCGJY7UBG6+yBFSg="; + }; + }; nativeBuildInputs = [ cargo-xbuild llvmPackages_11.clang-unwrapped ]; buildPhase = '' diff --git a/libconfig/Cargo.toml b/libconfig/Cargo.toml index 69e8472..811e76c 100644 --- a/libconfig/Cargo.toml +++ b/libconfig/Cargo.toml @@ -6,10 +6,14 @@ edition = "2021" [dependencies] libboard_zynq = { path = "../libboard_zynq" } -core_io = { version = "0.1", features = ["collections"] } -fatfs = { version = "0.3", features = ["core_io"], default-features = false } log = "0.4" +[dependencies.fatfs] +git = "https://github.com/rafalh/rust-fatfs" +rev = "85f06e0" +default-features = false +features = ["alloc", "lfn"] + [features] target_zc706 = [] target_coraz7 = [] @@ -17,4 +21,4 @@ target_ebaz4205 = [] target_redpitaya = [] target_kasli_soc = [] ipv6 = [] -fat_lfn = [ "fatfs/alloc" ] \ No newline at end of file +fat_lfn = [ "fatfs/alloc" ] diff --git a/libconfig/src/bootgen.rs b/libconfig/src/bootgen.rs index 884f22c..d6da362 100644 --- a/libconfig/src/bootgen.rs +++ b/libconfig/src/bootgen.rs @@ -1,8 +1,12 @@ use alloc::vec::Vec; -use core_io::{Error, Read, Seek, SeekFrom}; +use fatfs::{self, Read, Seek, SeekFrom}; use libboard_zynq::devc; +use crate::sd_reader; +use crate::File; use log::debug; +type Error = fatfs::Error; + #[derive(Debug)] pub enum BootgenLoadingError { InvalidBootImageHeader, @@ -58,7 +62,7 @@ struct PartitionHeader { } /// Read a u32 word from the reader. -fn read_u32(reader: &mut Reader) -> Result { +fn read_u32<'a>(reader: &mut File<'a>) -> Result { let mut buffer: [u8; 4] = [0; 4]; reader.read_exact(&mut buffer)?; let mut result: u32 = 0; @@ -69,8 +73,8 @@ fn read_u32(reader: &mut Reader) -> Result( - file: &mut File, +fn load_pl_header<'a>( + file: &mut File<'a>, ) -> Result, BootgenLoadingError> { let mut buffer: [u8; 0x40] = [0; 0x40]; file.read_exact(&mut buffer)?; @@ -82,8 +86,8 @@ fn load_pl_header( } } -fn load_ps_header( - file: &mut File, +fn load_ps_header<'a>( + file: &mut File<'a>, ) -> Result, BootgenLoadingError> { let mut buffer: [u8; 0x40] = [0; 0x40]; file.read_exact(&mut buffer)?; @@ -98,10 +102,10 @@ fn load_ps_header( /// Locate the partition from the image, and return the size (in bytes) of the partition if successful. /// This function would seek the file to the location of the partition. fn locate< - File: Read + Seek, - F: Fn(&mut File) -> Result, BootgenLoadingError>, + 'a, + F: Fn(&mut File<'a>) -> Result, BootgenLoadingError>, >( - file: &mut File, + file: &mut File<'a>, f: F, ) -> Result { file.seek(SeekFrom::Start(0))?; @@ -149,7 +153,7 @@ fn locate< /// Load bitstream from bootgen file. /// This function parses the file, locate the bitstream and load it through the PCAP driver. /// It requires a large buffer, please enable the DDR RAM before using it. -pub fn load_bitstream(file: &mut File) -> Result<(), BootgenLoadingError> { +pub fn load_bitstream<'a>(file: &mut File<'a>) -> Result<(), BootgenLoadingError> { let size = locate(file, load_pl_header)?; unsafe { // align to 64 bytes @@ -170,7 +174,7 @@ pub fn load_bitstream(file: &mut File) -> Result<(), BootgenL } } -pub fn get_runtime(file: &mut File) -> Result, BootgenLoadingError> { +pub fn get_runtime<'a>(file: &mut File<'a>) -> Result, BootgenLoadingError> { let size = locate(file, load_ps_header)?; let mut buffer = Vec::with_capacity(size); unsafe { diff --git a/libconfig/src/lib.rs b/libconfig/src/lib.rs index f1bf1b1..667d1d3 100644 --- a/libconfig/src/lib.rs +++ b/libconfig/src/lib.rs @@ -1,19 +1,24 @@ #![no_std] extern crate alloc; +use alloc::{rc::Rc, string::FromUtf8Error, string::String, vec::Vec}; +use sd_reader::SdReader; use core::fmt; -use alloc::{string::FromUtf8Error, string::String, vec::Vec, rc::Rc}; -use core_io::{self as io, BufRead, BufReader, Read, Write, Seek, SeekFrom}; +use fatfs::{self, Read, Seek, SeekFrom, Write}; use libboard_zynq::sdio; -pub mod sd_reader; -pub mod net_settings; pub mod bootgen; +pub mod net_settings; +pub mod sd_reader; + +type SdReadError = fatfs::Error; + +pub type File<'a> = fatfs::File<'a, SdReader, fatfs::NullTimeProvider, fatfs::LossyOemCpConverter>; #[derive(Debug)] pub enum Error<'a> { SdError(sdio::sd_card::CardInitializationError), - IoError(io::Error), + IoError(SdReadError), Utf8Error(FromUtf8Error), KeyNotFoundError(&'a str), NoConfig, @@ -39,8 +44,8 @@ impl<'a> From for Error<'a> { } } -impl<'a> From for Error<'a> { - fn from(error: io::Error) -> Self { +impl<'a> From for Error<'a> { + fn from(error: SdReadError) -> Self { Error::IoError(error) } } @@ -51,14 +56,36 @@ impl<'a> From for Error<'a> { } } +// Simplified replacements to `read_to_end` and `read_to_string` from core_io +fn read_to_end<'a>(file: &mut File<'a>, buffer: &mut Vec) -> Result<'a, ()> { + let mut temp_buffer = [0; 1024]; + loop { + let read_bytes = file.read(&mut temp_buffer)?; + if read_bytes == 0 { + break; + } + else { + buffer.extend_from_slice(&temp_buffer[..read_bytes]); + } + } + Ok(()) +} + +fn read_to_string<'a>(file: &mut File<'a>) -> Result<'a, String> { + let mut buffer: Vec = Vec::new(); + read_to_end(file, &mut buffer)?; + Ok(String::from_utf8(buffer)?) +} + fn parse_config<'a>( key: &'a str, buffer: &mut Vec, - file: fatfs::File, + mut file: File<'a>, ) -> Result<'a, ()> { let prefix = [key, "="].concat().to_ascii_lowercase(); - for line in BufReader::new(file).lines() { - let line = line?.to_ascii_lowercase(); + let read_buffer = read_to_string(&mut file)?; + for line in read_buffer.lines() { + let line = line.to_ascii_lowercase(); if line.starts_with(&prefix) { buffer.extend(line[prefix.len()..].as_bytes()); return Ok(()); @@ -83,7 +110,9 @@ impl Config { let reader = sd_reader::SdReader::new(sd); let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?; - Ok(Config { fs: Some(Rc::new(fs)) }) + Ok(Config { + fs: Some(Rc::new(fs)), + }) } pub fn from_fs(fs: Option>>) -> Self { @@ -94,12 +123,12 @@ impl Config { Config { fs: None } } - pub fn read<'b>(&self, key: &'b str) -> Result<'b, Vec> { + pub fn read<'b>(&'b self, key: &'b str) -> Result<'b, Vec> { if let Some(fs) = &self.fs { let root_dir = fs.root_dir(); let mut buffer: Vec = Vec::new(); match root_dir.open_file(&["/CONFIG/", key, ".BIN"].concat()) { - Ok(mut f) => f.read_to_end(&mut buffer).map(|_| ())?, + Ok(mut f) => read_to_end( &mut f, &mut buffer)?, Err(_) => match root_dir.open_file("/CONFIG.TXT") { Ok(f) => parse_config(key, &mut buffer, f)?, Err(_) => return Err(Error::KeyNotFoundError(key)), @@ -111,11 +140,11 @@ impl Config { } } - pub fn read_str<'b>(&self, key: &'b str) -> Result<'b, String> { + pub fn read_str<'b>(&'b self, key: &'b str) -> Result<'b, String> { Ok(String::from_utf8(self.read(key)?)?) } - pub fn remove<'b>(&self, key: &'b str) -> Result<'b, ()> { + pub fn remove<'b>(&'b self, key: &'b str) -> Result<'b, ()> { if let Some(fs) = &self.fs { let root_dir = fs.root_dir(); match root_dir.remove(&["/CONFIG/", key, ".BIN"].concat()) { @@ -124,19 +153,19 @@ impl Config { let prefix = [key, "="].concat().to_ascii_lowercase(); match root_dir.create_file("/CONFIG.TXT") { Ok(mut f) => { - let mut buffer = String::new(); - f.read_to_string(&mut buffer)?; + let buffer = read_to_string(&mut f)?; f.seek(SeekFrom::Start(0))?; f.truncate()?; for line in buffer.lines() { - if line.len() > 0 && !line.to_ascii_lowercase().starts_with(&prefix) { + if line.len() > 0 && !line.to_ascii_lowercase().starts_with(&prefix) + { f.write(line.as_bytes())?; f.write(NEWLINE)?; } } Ok(()) - }, - Err(_) => Err(Error::KeyNotFoundError(key)) + } + Err(_) => Err(Error::KeyNotFoundError(key)), } } } @@ -162,7 +191,10 @@ impl Config { if is_str { let mut f = root_dir.create_file("/CONFIG.TXT")?; f.seek(SeekFrom::End(0))?; - write!(f, "{}={}\n", key, String::from_utf8(value).unwrap())?; + f.write(key.as_bytes())?; + f.write("=".as_bytes())?; + f.write(value.as_slice())?; + f.write(NEWLINE)?; } else { let dir = root_dir.create_dir("/CONFIG")?; let mut f = dir.create_file(&[key, ".BIN"].concat())?; diff --git a/libconfig/src/sd_reader.rs b/libconfig/src/sd_reader.rs index 5254a07..6a11ea9 100644 --- a/libconfig/src/sd_reader.rs +++ b/libconfig/src/sd_reader.rs @@ -1,8 +1,8 @@ -use core_io::{BufRead, Error, ErrorKind, Read, Result as IoResult, Seek, SeekFrom, Write}; -use fatfs; +use alloc::vec::Vec; +use core::fmt; +use fatfs::{self, IoBase, IoError, Read, Seek, SeekFrom, Write}; use libboard_zynq::sdio::{sd_card::SdCard, CmdTransferError}; use log::debug; -use alloc::vec::Vec; const MBR_SIGNATURE: [u8; 2] = [0x55, 0xAA]; const PARTID_FAT12: u8 = 0x01; @@ -12,13 +12,46 @@ const PARTID_FAT32: u8 = 0x0B; const PARTID_FAT32_LBA: u8 = 0x0C; const PARTID_FAT16_LBA: u8 = 0x0E; -fn cmd_error_to_io_error(_: CmdTransferError) -> Error { - Error::new(ErrorKind::Other, "Command transfer error") -} - const BLOCK_SIZE: usize = 512; -/// SdReader struct implementing `Read + BufRead + Write + Seek` traits for `core_io`. +#[derive(Debug)] +pub struct Error { + message: &'static str, +} + +impl Error { + pub fn new(message: &'static str) -> Self { + Self { message: message } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl IoError for Error { + fn is_interrupted(&self) -> bool { + false + } + + fn new_unexpected_eof_error() -> Self { + Error::new("Unexpected end of file error") + } + + fn new_write_zero_error() -> Self { + Error::new("Write zero error") + } +} + +impl From for Error { + fn from(_: CmdTransferError) -> Self { + Error::new("Command transfer error") + } +} + +/// SdReader struct implementing `Read + Write + Seek` traits for `core_io`. /// Used as an adaptor for fatfs crate, but could be used directly for raw data access. /// /// Implementation: all read/writes would be split into unaligned and block-aligned parts, @@ -43,6 +76,10 @@ pub struct SdReader { offset: u32, } +impl IoBase for SdReader { + type Error = Error; +} + #[derive(Copy, Clone)] #[allow(unused)] // Partition entry enum, normally we would use entry1. @@ -72,7 +109,7 @@ impl SdReader { /// Internal read function for unaligned read. /// The read must not cross block boundary. - fn read_unaligned(&mut self, buf: &mut [u8]) -> IoResult { + fn read_unaligned(&mut self, buf: &mut [u8]) -> Result { if buf.len() == 0 { return Ok(0); } @@ -86,7 +123,7 @@ impl SdReader { /// Internal write function for unaligned write. /// The write must not cross block boundary. - fn write_unaligned(&mut self, buf: &[u8]) -> IoResult { + fn write_unaligned(&mut self, buf: &[u8]) -> Result { if buf.len() == 0 { return Ok(0); } @@ -137,7 +174,7 @@ impl SdReader { } /// Set the base offset of the SD card, to transform from physical address to logical address. - fn set_base_offset(&mut self, offset: u32) -> IoResult { + fn set_base_offset(&mut self, offset: u32) -> Result { self.offset = offset; self.seek(SeekFrom::Start(0)) } @@ -145,29 +182,28 @@ impl SdReader { /// Mount fatfs from partition entry, and return the fatfs object if success. /// This takes the ownership of self, so currently there is no way to recover from an error, /// except creating a new SD card instance. - pub fn mount_fatfs(mut self, entry: PartitionEntry) -> IoResult> { + pub fn mount_fatfs( + mut self, + entry: PartitionEntry, + ) -> Result, fatfs::Error> { let mut buffer: [u8; 4] = [0; 4]; self.seek(SeekFrom::Start(0x1FE))?; self.read_exact(&mut buffer[..2])?; // check MBR signature if buffer[..2] != MBR_SIGNATURE { - return Err(Error::new( - ErrorKind::InvalidData, - "Incorrect signature for MBR sector.", - )); + return Err(fatfs::Error::Io(Error::new("Incorrect signature for MBR sector."))); } // Read partition ID. self.seek(SeekFrom::Start(entry as u64 + 0x4))?; self.read_exact(&mut buffer[..1])?; debug!("Partition ID: {:0X}", buffer[0]); match buffer[0] { - PARTID_FAT12 | PARTID_FAT16_LESS32M | PARTID_FAT16 | - PARTID_FAT16_LBA | PARTID_FAT32 | PARTID_FAT32_LBA => {} + PARTID_FAT12 | PARTID_FAT16_LESS32M | PARTID_FAT16 | PARTID_FAT16_LBA + | PARTID_FAT32 | PARTID_FAT32_LBA => {} _ => { - return Err(Error::new( - ErrorKind::InvalidData, + return Err(fatfs::Error::Io(Error::new( "No FAT partition found for the specified entry.", - )); + ))); } } // Read LBA @@ -183,10 +219,29 @@ impl SdReader { // setup fatfs fatfs::FileSystem::new(self, fatfs::FsOptions::new()) } + + fn fill_buf(&mut self) -> Result<&[u8], Error> { + if self.index == BLOCK_SIZE { + // flush the buffer if it is dirty before overwriting it with new data + if self.dirty { + self.flush()?; + } + // reload buffer + self.sd + .read_block(self.byte_addr / (BLOCK_SIZE as u32), 1, &mut self.buffer)?; + self.index = (self.byte_addr as usize) % BLOCK_SIZE; + } + Ok(&self.buffer[self.index..]) + } + + fn consume(&mut self, amt: usize) { + self.index += amt; + self.byte_addr += amt as u32; + } } impl Read for SdReader { - fn read(&mut self, buf: &mut [u8]) -> IoResult { + fn read(&mut self, buf: &mut [u8]) -> Result { let total_length = buf.len(); let (a, b, c) = self.block_align_mut(buf); self.read_unaligned(a)?; @@ -211,30 +266,8 @@ impl Read for SdReader { } } -impl BufRead for SdReader { - fn fill_buf(&mut self) -> IoResult<&[u8]> { - if self.index == BLOCK_SIZE { - // flush the buffer if it is dirty before overwriting it with new data - if self.dirty { - self.flush()?; - } - // reload buffer - self.sd - .read_block(self.byte_addr / (BLOCK_SIZE as u32), 1, &mut self.buffer) - .map_err(cmd_error_to_io_error)?; - self.index = (self.byte_addr as usize) % BLOCK_SIZE; - } - Ok(&self.buffer[self.index..]) - } - - fn consume(&mut self, amt: usize) { - self.index += amt; - self.byte_addr += amt as u32; - } -} - impl Write for SdReader { - fn write(&mut self, buf: &[u8]) -> IoResult { + fn write(&mut self, buf: &[u8]) -> Result { let (a, b, c) = self.block_align(buf); self.write_unaligned(a)?; if b.len() > 0 { @@ -255,12 +288,10 @@ impl Write for SdReader { Ok(buf.len()) } - fn flush(&mut self) -> IoResult<()> { + fn flush(&mut self) -> Result<(), Error> { if self.dirty { let block_addr = (self.byte_addr - self.index as u32) / (BLOCK_SIZE as u32); - self.sd - .write_block(block_addr, 1, &self.buffer) - .map_err(cmd_error_to_io_error)?; + self.sd.write_block(block_addr, 1, &self.buffer)?; self.dirty = false; } Ok(()) @@ -268,14 +299,14 @@ impl Write for SdReader { } impl Seek for SdReader { - fn seek(&mut self, pos: SeekFrom) -> IoResult { + fn seek(&mut self, pos: SeekFrom) -> Result { let raw_target = match pos { SeekFrom::Start(x) => self.offset as i64 + x as i64, SeekFrom::Current(x) => self.byte_addr as i64 + x, SeekFrom::End(_) => panic!("SD card does not support seek from end"), }; if raw_target < self.offset as i64 || raw_target > core::u32::MAX as i64 { - return Err(Error::new(ErrorKind::InvalidInput, "Invalid address")); + return Err(Error::new("Invalid address")); } let target_byte_addr = raw_target as u32; let address_same_block = diff --git a/szl/Cargo.toml b/szl/Cargo.toml index 5dfcd90..b0d5fb8 100644 --- a/szl/Cargo.toml +++ b/szl/Cargo.toml @@ -16,10 +16,15 @@ default = ["target_zc706"] [dependencies] log = "0.4" byteorder = { version = "1.3", default-features = false } -core_io = { version = "0.1", features = ["collections"] } libboard_zynq = { path = "../libboard_zynq" } libsupport_zynq = { path = "../libsupport_zynq" } libcortex_a9 = { path = "../libcortex_a9" } libregister = { path = "../libregister" } libconfig = { path = "../libconfig" } + +[dependencies.fatfs] +git = "https://github.com/rafalh/rust-fatfs" +rev = "85f06e0" +default-features = false +features = ["alloc", "lfn"] diff --git a/szl/src/main.rs b/szl/src/main.rs index fc91b7e..3ffbb95 100644 --- a/szl/src/main.rs +++ b/szl/src/main.rs @@ -8,7 +8,6 @@ mod netboot; use alloc::rc::Rc; use core::mem; -use core_io::{Read, Seek}; use libboard_zynq::{ self as zynq, clocks::source::{ArmPll, ClockSource, IoPll}, @@ -16,7 +15,7 @@ use libboard_zynq::{ logger, println, sdio, slcr, timer::GlobalTimer, }; -use libconfig::{bootgen, sd_reader, Config}; +use libconfig::{bootgen, sd_reader, Config, File}; use libcortex_a9::{ asm::{dsb, isb}, cache::{bpiall, dcciall, iciallu}, @@ -30,8 +29,8 @@ extern "C" { static mut __runtime_end: usize; } -fn boot_sd( - file: &mut Option, +fn boot_sd<'a>( + file: &mut Option>, runtime_start: *mut u8, runtime_max: usize, ) -> Result<(), ()> { diff --git a/szl/src/netboot.rs b/szl/src/netboot.rs index 8552a6e..dcbb26d 100644 --- a/szl/src/netboot.rs +++ b/szl/src/netboot.rs @@ -1,7 +1,6 @@ use alloc::vec; use alloc::vec::Vec; use byteorder::{ByteOrder, NetworkEndian}; -use core_io::{Read, Seek}; use libboard_zynq::{ devc, eth::Eth, @@ -13,7 +12,7 @@ use libboard_zynq::{ }, timer::GlobalTimer, }; -use libconfig::{bootgen, net_settings, Config}; +use libconfig::{bootgen, net_settings, Config, File}; enum NetConnState { WaitCommand, @@ -48,7 +47,7 @@ impl NetConn { self.gateware_downloaded = false; } - fn input_partial( + fn input_partial( &mut self, bootgen_file: &mut Option, runtime_start: *mut u8, @@ -284,9 +283,9 @@ impl NetConn { } } - fn input( + fn input<'a>( &mut self, - bootgen_file: &mut Option, + bootgen_file: &mut Option>, runtime_start: *mut u8, runtime_max_len: usize, buf: &[u8], @@ -309,8 +308,8 @@ impl NetConn { } } -pub fn netboot( - bootgen_file: &mut Option, +pub fn netboot<'a>( + bootgen_file: &mut Option>, cfg: Config, runtime_start: *mut u8, runtime_max_len: usize,