diff --git a/Cargo.lock b/Cargo.lock index 341dc877..f262808c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,9 +63,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitfield-struct" -version = "0.5.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef59f0fcc43c87f48e1be2fb5bae934819066bef8be717850944bb882ea232d" +checksum = "6d7a33e7b9505a52e33ed0ad66db6434f18cda0b1c72665fabf14e85cdd39e43" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 5c2d3a25..efb719c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,7 @@ members = [ version = "4.0.0" authors = ["alloncm "] rust-version = "1.73" # cause of cargo-ndk used to build for android platform -edition = "2021" \ No newline at end of file +edition = "2021" + +[profile.release] +lto = true # Samller binaries and faster code \ No newline at end of file diff --git a/README.md b/README.md index 66afadbc..8a0833b9 100644 --- a/README.md +++ b/README.md @@ -97,13 +97,14 @@ Choose a game with the Joypad bindings (Dpad and A to confirm) ### Raspberry Pi Baremetal Currently only Raspberry Pi 4 is supported using the following instructions: -* Format a sd card and make a single `FAT32` partition called `boot` -* Copy the file `config.txt` to the root dir of the sd card -* Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card: - - [fixup4.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup4.dat) - - [start4.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start4.elf) - - [bcm2711-rpi-4-b.dtb](https://github.com/raspberrypi/firmware/raw/master/boot/bcm2711-rpi-4-b.dtb) -* Copy `kernel7.img` onto the SD card +* Format a sd card to MBR (not GPT) and create a single `FAT32` partition (On windows you can use Rufus) +* Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/191360eaf2e5933eaa0ed76ac0d62722b6f9a58f/boot) onto the SD card: + - [fixup4.dat](https://github.com/raspberrypi/firmware/raw/191360eaf2e5933eaa0ed76ac0d62722b6f9a58f/boot/fixup4.dat) + - [start4.elf](https://github.com/raspberrypi/firmware/raw/191360eaf2e5933eaa0ed76ac0d62722b6f9a58f/boot/start4.elf) + - [bcm2711-rpi-4-b.dtb](https://github.com/raspberrypi/firmware/raw/191360eaf2e5933eaa0ed76ac0d62722b6f9a58f/boot/bcm2711-rpi-4-b.dtb) + + _**Notice**: This is a specific revision, for some reason it broke on the latest version of those files_ +* Copy `kernel7.img` and `config.txt` to the SD card * Connect all the peripherals (ili9341 display and gpio buttons) * Insert the SD card to the RPI4 and boot it diff --git a/core/src/mmu/carts/mod.rs b/core/src/mmu/carts/mod.rs index ff1cd58f..3cd8e13f 100644 --- a/core/src/mmu/carts/mod.rs +++ b/core/src/mmu/carts/mod.rs @@ -19,11 +19,11 @@ pub const MBC_RAM_SIZE_LOCATION:usize = 0x149; pub fn get_ram_size(ram_size_register:u8)->usize{ match ram_size_register{ 0x0=>0, - 0x1=>0x800, + 0x1=>0x800, // Unofficial - Undefined according to official docs 0x2=>0x4000, 0x3=>0x8000, - 0x4=>0x20000, - 0x5=>0x10000, + 0x4=>0x2_0000, + 0x5=>0x1_0000, _=>core::panic!("invalid ram size register {:#X}", ram_size_register) } } @@ -34,7 +34,7 @@ pub fn init_ram(ram_reg:u8, external_ram:Option<&'static mut[u8]>)->&'static mut match external_ram{ Some(ram)=>{ if ram.len() != ram_size{ - core::panic!("external rom is not in the correct size for the cartridge"); + core::panic!("External ram is not in the correct size for the cartridge, the save seems corrupted, either fix or delete it and try again"); } return ram; diff --git a/core/src/mmu/gb_mmu.rs b/core/src/mmu/gb_mmu.rs index 66027cec..8204d653 100644 --- a/core/src/mmu/gb_mmu.rs +++ b/core/src/mmu/gb_mmu.rs @@ -41,7 +41,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G return self.io_bus.ppu.vram.read_current_bank(address-0x8000); } else{ - log::warn!("bad vram read"); + log::trace!("bad vram read"); return BAD_READ_VALUE; } }, @@ -50,7 +50,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G return self.io_bus.ppu.oam[(address-0xFE00) as usize]; } else{ - log::warn!("bad oam read"); + log::trace!("bad oam read"); return BAD_READ_VALUE; } } @@ -80,7 +80,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G self.io_bus.ppu.vram.write_current_bank(address-0x8000, value); } else{ - log::warn!("bad vram write: address - {:#X}, value - {:#X}, bank - {}", address, value, self.io_bus.ppu.vram.get_bank_reg()); + log::trace!("bad vram write: address - {:#X}, value - {:#X}, bank - {}", address, value, self.io_bus.ppu.vram.get_bank_reg()); } }, 0xFE00..=0xFE9F=>{ @@ -88,7 +88,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G self.io_bus.ppu.oam[(address-0xFE00) as usize] = value; } else{ - log::warn!("bad oam write") + log::trace!("bad oam write") } }, _=>self.write_unprotected(address, value) @@ -249,12 +249,12 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ } fn bad_dma_read(address:u16)->u8{ - log::warn!("bad memory read during dma. {:#X}", address); + log::trace!("bad memory read during dma. {:#X}", address); return BAD_READ_VALUE; } fn bad_dma_write(address:u16){ - log::warn!("bad memory write during dma. {:#X}", address) + log::trace!("bad memory write during dma. {:#X}", address) } fn write_color_ram(&mut self, address:u16, color: Color){ diff --git a/core/src/ppu/gb_ppu.rs b/core/src/ppu/gb_ppu.rs index 21f8d129..33147d52 100644 --- a/core/src/ppu/gb_ppu.rs +++ b/core/src/ppu/gb_ppu.rs @@ -616,7 +616,7 @@ impl GbPpu{ color_ram[(*pallete_index_register & 0b11_1111) as usize] = value; } else{ - log::warn!("bad color ram write: index - {:#X}, value: - {:#X}", pallete_index_register, value); + log::trace!("bad color ram write: index - {:#X}, value: - {:#X}", pallete_index_register, value); } // if bit 7 is set inderement the dest adderess after write diff --git a/rpi/Cargo.toml b/rpi/Cargo.toml index 22f6720e..e1f8d8c2 100644 --- a/rpi/Cargo.toml +++ b/rpi/Cargo.toml @@ -11,7 +11,7 @@ magenboy_core = {path = "../core"} magenboy_common = {path = "../common"} log = "0.4" cfg-if = "1" -bitfield-struct = "0.5" +bitfield-struct = {version = "0.9", optional = true} libc = {version = "0.2", optional = true} nix = {version = "0.24", optional = true} crossbeam-channel = {version = "0.5", optional = true} @@ -20,7 +20,7 @@ arrayvec = {version = "0.7.6", default-features = false, optional = true} [features] os = ["magenboy_common/std", "libc", "nix/ioctl", "crossbeam-channel", "rppal"] -bm = ["arrayvec"] +bm = ["arrayvec", "bitfield-struct"] [[bin]] name = "rpios" diff --git a/rpi/build.rs b/rpi/build.rs index f871bdf3..af0f26ae 100644 --- a/rpi/build.rs +++ b/rpi/build.rs @@ -44,11 +44,5 @@ fn main(){ let rpi_version = std::env::var(config::RPI_ENV_VAR_NAME) .expect(std::format!("{} env must be set", config::RPI_ENV_VAR_NAME).as_str()); println!("cargo:rustc-cfg=rpi=\"{}\"", rpi_version); - - // Silent warnings for this cfg - println!("cargo:rustc-check-cfg=cfg(rpi, values(\"4\", \"2\"))"); } - - // Silent warnings for this cfg - println!("cargo:rustc-check-cfg=cfg(rpi)"); } \ No newline at end of file diff --git a/rpi/src/bin/baremetal/link.ld b/rpi/src/bin/baremetal/link.ld index 64aeea77..8e96c63b 100644 --- a/rpi/src/bin/baremetal/link.ld +++ b/rpi/src/bin/baremetal/link.ld @@ -1,7 +1,7 @@ /* Place _start procedure at the entry address for RPI */ __rpi_32_phys_binary_load_addr = 0x8000; __isr_table_addr = 0; -__stack_size = 0x100000; /* 1MB stack */ +__stack_size = 0x300000; /* 3MB stack */ ENTRY(__rpi_32_phys_binary_load_addr) /* enry point */ SECTIONS diff --git a/rpi/src/bin/baremetal/main.rs b/rpi/src/bin/baremetal/main.rs index f7db68bc..e6052322 100644 --- a/rpi/src/bin/baremetal/main.rs +++ b/rpi/src/bin/baremetal/main.rs @@ -9,14 +9,24 @@ use core::panic::PanicInfo; use arrayvec::ArrayString; use magenboy_common::{joypad_menu::{joypad_gfx_menu::{self, GfxDeviceMenuRenderer}, JoypadMenu, }, menu::*, VERSION}; -use magenboy_core::machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}; -use magenboy_rpi::{drivers::*, peripherals::{PERIPHERALS, GpioPull}, configuration::{display::*, joypad::button_to_bcm_pin, emulation::*}, MENU_PIN_BCM, delay}; +use magenboy_core::{machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}, mmu::carts::Mbc}; +use magenboy_rpi::{drivers::*, peripherals::{PERIPHERALS, GpioPull, ResetMode, Power}, configuration::{display::*, joypad::button_to_bcm_pin, emulation::*}, MENU_PIN_BCM, delay}; + +#[panic_handler] +fn panic(info:&PanicInfo)->!{ + log::error!("An error has occurred!: \r\n{}", info); + + // SAFETY: Defined in boot asm code, no params passed and this function does not returned so no calling convention is even needed + unsafe{boot::hang_led()}; +} const MAX_ROM_SIZE:usize = 0x80_0000; // 8 MiB, Max size of MBC5 rom +const MAX_RAM_SIZE:usize = 0x2_0000; // 128 KiB // Allocating as static buffer (on the .bss) because it is a very large buffer and -// I dont want to cause problems in stack making it overflow and shit (I can increase it when needed but I afraid Id forget) +// I don't want to cause problems in stack making it overflow and shit (I can increase it when needed but I afraid Id forget) static mut ROM_BUFFER:[u8; MAX_ROM_SIZE] = [0;MAX_ROM_SIZE]; +static mut RAM_BUFFER:[u8; MAX_RAM_SIZE] = [0;MAX_RAM_SIZE]; // This function is no regular main. // It will not return and will be jumped to from the _start proc in the boot code @@ -28,14 +38,14 @@ pub extern "C" fn main()->!{ log::info!("Initialized logger"); log::info!("running at exec mode: {:#X}", boot::get_cpu_execution_mode()); - let mut power_manager = unsafe{PERIPHERALS.take_power()}; + let power_manager = unsafe{PERIPHERALS.take_power()}; - let mut fs = Fat32::new(); + let mut fs = Fat32Fs::new(); let mut gfx = Ili9341GfxDevice::new(RESET_PIN_BCM, LED_PIN_BCM, TURBO, FRAME_LIMITER); let mut pause_menu_gfx = gfx.clone(); let mut joypad_provider = GpioJoypadProvider::new(button_to_bcm_pin); let mut pause_menu_joypad_provider = joypad_provider.clone(); - log::info!("Initialize all drivers succesfully"); + log::info!("Initialize all drivers successfully"); let menu_renderer = joypad_gfx_menu::GfxDeviceMenuRenderer::new(&mut gfx); @@ -46,16 +56,18 @@ pub extern "C" fn main()->!{ let selected_rom = menu.get_menu_selection(&mut joypad_provider); log::info!("Selected ROM: {}", selected_rom.get_name()); + // SAFETY: Only ref to this static mut var let rom = unsafe{&mut ROM_BUFFER}; fs.read_file(selected_rom, rom); - let mbc = initialize_mbc(&rom[0..selected_rom.size as usize], None); + let save_data = try_read_save_file(selected_rom, &mut fs); + let mbc = initialize_mbc(&rom[0..selected_rom.size as usize], save_data); let mode = mbc.detect_preferred_mode(); let mut gameboy = GameBoy::new_with_mode(mbc, joypad_provider, magenboy_rpi::BlankAudioDevice, gfx, mode); log::info!("Initialized gameboy!"); let menu_pin = unsafe {PERIPHERALS.get_gpio().take_pin(MENU_PIN_BCM).into_input(GpioPull::PullUp)}; - let pause_menu_header:ArrayString<30> = ArrayString::try_from(format_args!("MagenBoy bm v{}", VERSION)).unwrap(); + let pause_menu_header:ArrayString<30> = ArrayString::try_from(format_args!("MagenBoy v{}", VERSION)).unwrap(); let pause_menu_renderer = GfxDeviceMenuRenderer::new(&mut pause_menu_gfx); let mut pause_menu = JoypadMenu::new(&GAME_MENU_OPTIONS, pause_menu_header.as_str(), pause_menu_renderer); loop{ @@ -64,14 +76,12 @@ pub extern "C" fn main()->!{ match pause_menu.get_menu_selection(&mut pause_menu_joypad_provider){ EmulatorMenuOption::Resume => {}, EmulatorMenuOption::Restart => { - log::info!("Reseting system"); - delay::wait_ms(100); - power_manager.reset(magenboy_rpi::peripherals::ResetMode::Partition0) + log::info!("Resetting system"); + reset_system(mbc, fs, power_manager, ResetMode::Partition0, selected_rom); } EmulatorMenuOption::Shutdown => { log::info!("Shuting down system"); - delay::wait_ms(100); - power_manager.reset(magenboy_rpi::peripherals::ResetMode::Halt) + reset_system(mbc, fs, power_manager, ResetMode::Halt, selected_rom); } } } @@ -79,14 +89,38 @@ pub extern "C" fn main()->!{ } } -fn read_menu_options(fs: &mut Fat32, menu_options: &mut [MenuOption>; 255]) -> usize { +fn reset_system<'a>(mbc: &'a mut dyn Mbc, mut fs: Fat32Fs, mut power_manager: Power, mode: ResetMode, selected_rom: &FileEntry)->!{ + let filename = get_save_filename(selected_rom); + fs.write_file(filename.as_str(), mbc.get_ram()); + + // delaying the reset operation so other low level tasks will have enough time to finish (like uart transmission) + delay::wait_ms(100); + power_manager.reset(mode); +} + +fn try_read_save_file(selected_rom: &FileEntry, mut fs: &mut Fat32Fs) -> Option<&'static [u8]> { + let save_filename = get_save_filename(selected_rom); + let file = search_file(&mut fs, save_filename.as_str())?; + + // SAFETY: The only reference to this static mut var + let ram = unsafe{&mut RAM_BUFFER[0..file.size as usize]}; + fs.read_file(&file, ram); + log::info!("Found save file for selected rom: {}", file.get_name()); + return Some(ram); +} + +fn get_save_filename(selected_rom: &FileEntry) -> ArrayString<11> { + ArrayString::try_from(format_args!("{}SAV",&selected_rom.get_name()[..8])).unwrap() +} + +fn read_menu_options(fs: &mut Fat32Fs, menu_options: &mut [MenuOption>; 255]) -> usize { + const FILES_PER_LIST:usize = 20; + let mut menu_options_size = 0; let mut root_dir_offset = 0; - const FILES_PER_LIST:usize = 20; - 'search_dir_loop: loop{ + loop{ let dir_entries = fs.root_dir_list::(root_dir_offset); - for entry in dir_entries{ - let Some(entry) = entry else {break 'search_dir_loop}; + for entry in &dir_entries{ let extension = entry.get_extension(); if extension.eq_ignore_ascii_case("gb") || extension.eq_ignore_ascii_case("gbc"){ menu_options[menu_options_size] = MenuOption{ value: entry.clone(), prompt: ArrayString::from(entry.get_name()).unwrap() }; @@ -94,15 +128,28 @@ fn read_menu_options(fs: &mut Fat32, menu_options: &mut [MenuOption!{ - log::error!("An error has occoured!"); - log::error!("{}", info); - - unsafe{boot::hang_led()}; +fn search_file(fs:&mut Fat32Fs, filename: &str)->Option{ + let mut root_dir_offset = 0; + const FILES_PER_LIST:usize = 20; + loop{ + let dir_entries = fs.root_dir_list::(root_dir_offset); + for entry in &dir_entries{ + if entry.get_name() == filename{ + return Some(entry.clone()); + } + } + if dir_entries.remaining_capacity() != 0{ + return None; + } + root_dir_offset += FILES_PER_LIST; + } } \ No newline at end of file diff --git a/rpi/src/drivers/disk.rs b/rpi/src/drivers/disk.rs index 7e4496d9..f9ada387 100644 --- a/rpi/src/drivers/disk.rs +++ b/rpi/src/drivers/disk.rs @@ -4,7 +4,7 @@ use super::as_mut_buffer; #[repr(C, packed)] struct PartitionEntry{ status:u8, - frist_sector_chs_address:[u8;3], + first_sector_chs_address:[u8;3], partition_type:u8, last_sector_chs_address:[u8;3], first_sector_index:u32, @@ -13,7 +13,7 @@ struct PartitionEntry{ impl Default for PartitionEntry{ fn default() -> Self { - Self { status: Default::default(), frist_sector_chs_address: Default::default(), partition_type: Default::default(), last_sector_chs_address: Default::default(), first_sector_index: Default::default(), sectors_count: Default::default() } + Self { status: Default::default(), first_sector_chs_address: Default::default(), partition_type: Default::default(), last_sector_chs_address: Default::default(), first_sector_index: Default::default(), sectors_count: Default::default() } } } @@ -37,11 +37,14 @@ pub struct Disk{ } impl Disk{ + pub const BLOCK_SIZE:u32 = Emmc::get_block_size(); + pub fn new()->Self{ let mut emmc = unsafe{PERIPHERALS.take_emmc()}; emmc.init(); let mut mbr = MasterBootRecord::default(); + // SAFETY: MasterBootRecord is repr(C) let buffer = unsafe{as_mut_buffer(&mut mbr)}; if !emmc.read(buffer){ @@ -54,24 +57,60 @@ impl Disk{ Self { emmc, mbr } } - /// Returns the number of blocks the read operation fetched - /// The user knows how much of the buffer is filled - pub fn read(&mut self, block_index:u32, buffer:&mut [u8]) -> u32 { - let block_size = self.emmc.get_block_size(); - let buffer_size = buffer.len(); - if buffer_size % block_size as usize != 0{ - core::panic!("buffer size must be a division of block size: {}", block_size); + pub fn read(&mut self, block_index:u32, buffer:&mut [u8]){ + let (max_aligned_buffer_len, buffer_len_reminder) = Self::get_max_alligned_buffer_len_and_reminder(buffer); + let aligned_buffer = &mut buffer[..max_aligned_buffer_len]; + + self.emmc.seek((block_index * Self::BLOCK_SIZE) as u64); + // Verify the buffer is larger than a single block + if aligned_buffer.len() != 0{ + self.emmc_read(aligned_buffer); + // early return if the buffer is aligned + if buffer_len_reminder == 0 {return}; } - self.emmc.seek((block_index * block_size) as u64); + // handle the case buffer length is not aligned for block size + let mut temp_buffer:[u8;Self::BLOCK_SIZE as usize] = [0;Self::BLOCK_SIZE as usize]; + self.emmc.seek(((block_index + (max_aligned_buffer_len as u32 / Self::BLOCK_SIZE)) * Self::BLOCK_SIZE) as u64); + self.emmc_read(&mut temp_buffer); + buffer[max_aligned_buffer_len..].copy_from_slice(&mut temp_buffer[..buffer_len_reminder]); + } + + fn emmc_read(&mut self, buffer: &mut [u8]) { if !self.emmc.read(buffer){ - core::panic!("Error while reading object of size: {}", buffer_size); + core::panic!("Error while reading object of size: {}", buffer.len()); } - return buffer_size as u32 / block_size; } + pub fn write(&mut self, block_index:u32, buffer:&[u8]){ + let (max_aligned_buffer_len, buffer_len_reminder) = Self::get_max_alligned_buffer_len_and_reminder(buffer); + let aligned_buffer = &buffer[..max_aligned_buffer_len]; + + self.emmc.seek((block_index * Self::BLOCK_SIZE) as u64); + if aligned_buffer.len() != 0{ + self.emmc_write(aligned_buffer); + // early return since the buffer is aligned + if buffer_len_reminder == 0 {return}; + } + // handle the case buffer length is not aligned for block size + let mut temp_buffer:[u8;Self::BLOCK_SIZE as usize] = [0;Self::BLOCK_SIZE as usize]; + temp_buffer[..buffer_len_reminder].copy_from_slice(&buffer[max_aligned_buffer_len..]); + self.emmc.seek(((block_index + (max_aligned_buffer_len as u32 / Self::BLOCK_SIZE)) * Self::BLOCK_SIZE) as u64); + self.emmc_write(&temp_buffer); + } + pub fn get_partition_first_sector_index(&self, partition_index:u8)->u32{ self.mbr.partitions[partition_index as usize].first_sector_index } - pub fn get_block_size(&self)->u32{self.emmc.get_block_size()} + fn emmc_write(&mut self, buffer: &[u8]) { + if !self.emmc.write(buffer){ + core::panic!("Error while writing object of size: {}", buffer.len()); + } + } + + fn get_max_alligned_buffer_len_and_reminder(buffer: &[u8]) -> (usize, usize) { + let buffer_len_reminder = buffer.len() % Self::BLOCK_SIZE as usize; + let max_aligned_buffer_len = buffer.len() - buffer_len_reminder; + return (max_aligned_buffer_len, buffer_len_reminder); + } } \ No newline at end of file diff --git a/rpi/src/drivers/fat32.rs b/rpi/src/drivers/fat32.rs deleted file mode 100644 index 97055b97..00000000 --- a/rpi/src/drivers/fat32.rs +++ /dev/null @@ -1,247 +0,0 @@ -use core::mem::size_of; - -use crate::peripherals::compile_time_size_assert; -use super::{as_mut_buffer, disk::*}; - -#[derive(Default)] -#[repr(C, packed)] -struct Fat32BiosParameterBlock{ - // Base fields - bytes_per_sector: u16, - sectors_per_cluster: u8, - reserved_sectors_count: u16, - fats_count: u8, - root_entrires_count:u16, - total_sectors_count_16: u16, - media: u8, - sectors_per_fat_16:u16, - sectors_per_track:u16, - heads_count:u16, - hidden_sectors_count:u32, - total_sectors_count_32:u32, - - // extended fat32 fields - sectors_per_fat_32:u32, - extended_flags:u16, - fs_version:u16, - root_dir_first_cluster:u32, - fs_info_sector:u16, - backup_boot_sector:u16, - _reserved0:[u8;12], -} -compile_time_size_assert!(Fat32BiosParameterBlock, 53); - -#[repr(C, packed)] -struct Fat32BootSector{ - jump_boot:[u8;3], - oem_name:[u8;8], - fat32_bpb:Fat32BiosParameterBlock, - drive_num:u8, - _reserved1:u8, - boot_signature:u8, - volume_id:u32, - volume_label:[u8;11], - fs_type_label:[u8;8], - _pad:[u8;420], - signature_word:[u8;2], -} -compile_time_size_assert!(Fat32BootSector, 512); - -impl Default for Fat32BootSector{ - fn default() -> Self { - Self { - jump_boot: Default::default(), oem_name: Default::default(), fat32_bpb: Default::default(), drive_num: Default::default(), - _reserved1: Default::default(), boot_signature: Default::default(), volume_id: Default::default(), - volume_label: Default::default(), fs_type_label: Default::default(), _pad: [0;420], signature_word: Default::default() - } - } -} - -#[derive(Clone, Copy, Default)] -#[repr(C, packed)] -struct FatShortDirEntry{ - file_name:[u8;8], - file_extension:[u8;3], - attributes:u8, - nt_reserve:u8, - creation_time_tenth_secs:u8, - creation_time:u16, - creation_date:u16, - last_access_date:u16, - first_cluster_index_high:u16, - last_write_time:u16, - last_write_date:u16, - first_cluster_index_low:u16, - file_size:u32, -} - -// This struct is for support to the long filenames that I will add later -// unused for now -#[derive(Clone, Copy)] -#[repr(C,packed)] -struct FatLongDirEntry{ - order:u8, - name1:[u16;5], - attributes:u8, - ext_type:u8, - check_sum:u8, - name2:[u16;6], - _first_cluster_low:u16, - name3:[u16;2] -} - -const DISK_PARTITION_INDEX:u8 = 0; -const SECTOR_SIZE:u32 = 512; -const FAT_ENTRY_SIZE:usize = size_of::(); // each fat entry in fat32 is 4 the size of u32 -const FAT_ENTRY_EOF_INDEX:u32 = 0x0FFF_FFFF; -const FAT_ENTRY_MASK:u32 = 0x0FFF_FFFF; -const DELETED_DIR_ENTRY_PREFIX:u8 = 0xE5; -const DIR_EOF_PREFIX:u8 = 0; - -const FAT_DIR_ENTRIES_PER_SECTOR:usize = SECTOR_SIZE as usize / core::mem::size_of::(); - -const ATTR_READ_ONLY:u8 = 0x1; -const ATTR_HIDDEN:u8 = 0x2; -const ATTR_SYSTEM:u8 = 0x4; -const ATTR_VOLUME_ID:u8 = 0x8; -const ATTR_LONG_NAME:u8 = ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID; - -#[derive(Default, Clone, Copy)] -pub struct FileEntry{ - name:[u8;Self::FILENAME_SIZE], - first_cluster_index:u32, - pub size:u32, -} - -impl FileEntry{ - pub const FILENAME_SIZE:usize = 11; - - pub fn get_name<'a>(&'a self)->&'a str{ - core::str::from_utf8(&self.name).unwrap().trim() - } - - pub fn get_extension<'a>(&'a self)->&'a str{ - core::str::from_utf8(&self.name[8..]).unwrap().trim() - } -} - -pub struct Fat32{ - disk: Disk, - boot_sector:Fat32BootSector, - partition_start_sector_index:u32, -} - -impl Fat32{ - pub fn new()->Self{ - let mut disk = Disk::new(); - // This driver currently support only a single partition (some has more than one for backup or stuff I dont know) - let bpb_sector_index = disk.get_partition_first_sector_index(DISK_PARTITION_INDEX); - - let mut boot_sector:Fat32BootSector = Default::default(); - let buffer = unsafe{as_mut_buffer(&mut boot_sector)}; - let _ = disk.read(bpb_sector_index, buffer); - - let fs_type_label = boot_sector.fs_type_label.clone(); - if &fs_type_label[0..3] != b"FAT"{ - core::panic!("File system is not FAT"); - } - if boot_sector.fat32_bpb.sectors_per_fat_16 != 0{ - core::panic!("Detected FAT16 and not FAT32 file system"); - } - let bytes_per_sector = boot_sector.fat32_bpb.bytes_per_sector as u32; - if bytes_per_sector != disk.get_block_size() || bytes_per_sector != SECTOR_SIZE{ - core::panic!("Currently dont support fat32 disks with sectors size other than {}", SECTOR_SIZE); - } - let fat_count = boot_sector.fat32_bpb.fats_count; - log::debug!("FAT count: {}", fat_count); - - return Self { disk, boot_sector, partition_start_sector_index:bpb_sector_index }; - } - - pub fn root_dir_list(&mut self, offset:usize)->[Option;RESULT_MAX_LEN]{ - let root_start_sector_index = self.get_cluster_start_sector_index(self.boot_sector.fat32_bpb.root_dir_first_cluster); - - let mut root_dir_files_count = 0; - let mut output_dir = [None;RESULT_MAX_LEN]; - let mut sector_offset = 0; - let mut discard = offset; - - 'search: loop{ - let mut root_dir = [FatShortDirEntry::default();FAT_DIR_ENTRIES_PER_SECTOR]; - let buffer = unsafe{as_mut_buffer(&mut root_dir)}; - sector_offset += self.disk.read(root_start_sector_index + sector_offset, buffer); - for dir in root_dir{ - if dir.file_name[0] == DIR_EOF_PREFIX{ - break 'search; - } - if dir.file_name[0] == DELETED_DIR_ENTRY_PREFIX{ - continue; - } - if dir.attributes == ATTR_LONG_NAME{ - continue; - // handle long file names here - } - if discard > 0{ - discard -= 1; - continue; - } - - let mut filename:[u8;11] = [0;11]; - filename[..8].copy_from_slice(&dir.file_name); - filename[8..11].copy_from_slice(&dir.file_extension); - let first_cluster_index = dir.first_cluster_index_low as u32 | ((dir.first_cluster_index_high as u32) << 16); - - output_dir[root_dir_files_count] = Some(FileEntry{ name: filename, first_cluster_index, size: dir.file_size }); - root_dir_files_count += 1; - if root_dir_files_count == RESULT_MAX_LEN{ - break 'search; - } - } - } - - return output_dir; - } - - /// Reads a file from the first FAT - pub fn read_file(&mut self, file_entry:&FileEntry, output:&mut [u8]){ - log::debug!("Reading file {}, size {}, cluster: {}", file_entry.get_name(), file_entry.size, file_entry.first_cluster_index); - let fat_offset = file_entry.first_cluster_index * FAT_ENTRY_SIZE as u32; - let mut fat_sector_number = self.partition_start_sector_index + self.boot_sector.fat32_bpb.reserved_sectors_count as u32 + (fat_offset / SECTOR_SIZE); - let mut fat_entry_offset = (fat_offset % SECTOR_SIZE) as usize; - - let sectors_per_cluster = self.boot_sector.fat32_bpb.sectors_per_cluster; - let mut current_cluster = file_entry.first_cluster_index; - let mut cluster_counter = 0; - let mut fat_buffer = [0; SECTOR_SIZE as usize]; - let _ = self.disk.read(fat_sector_number, &mut fat_buffer); - - loop{ - let start_sector = self.get_cluster_start_sector_index(current_cluster); - let start_index = sectors_per_cluster as usize * cluster_counter * SECTOR_SIZE as usize; - let end_index = start_index + (sectors_per_cluster as usize * SECTOR_SIZE as usize); - let _ = self.disk.read(start_sector, &mut output[start_index..end_index]); - - let fat_entry = u32::from_ne_bytes(fat_buffer[fat_entry_offset .. fat_entry_offset + FAT_ENTRY_SIZE].try_into().unwrap()) & FAT_ENTRY_MASK; - if fat_entry == FAT_ENTRY_EOF_INDEX{ - return; - } - current_cluster = fat_entry; - cluster_counter += 1; - fat_entry_offset += FAT_ENTRY_SIZE; - if fat_entry_offset >= SECTOR_SIZE as usize{ - fat_entry_offset = 0; - fat_sector_number += 1; - let _ = self.disk.read(fat_sector_number, &mut fat_buffer); - } - } - - } - - fn get_cluster_start_sector_index(&self, cluster:u32)->u32{ - const FIRST_DATA_CLUSTER:u32 = 2; - - self.partition_start_sector_index + self.boot_sector.fat32_bpb.reserved_sectors_count as u32 + - ((cluster - FIRST_DATA_CLUSTER) * self.boot_sector.fat32_bpb.sectors_per_cluster as u32) + - (self.boot_sector.fat32_bpb.sectors_per_fat_32 * self.boot_sector.fat32_bpb.fats_count as u32) - } -} \ No newline at end of file diff --git a/rpi/src/drivers/fat32/fat_buffer.rs b/rpi/src/drivers/fat32/fat_buffer.rs new file mode 100644 index 00000000..96531fa7 --- /dev/null +++ b/rpi/src/drivers/fat32/fat_buffer.rs @@ -0,0 +1,123 @@ +use crate::drivers::disk::Disk; + +use super::SECTOR_SIZE; + +const FAT_ENTRY_SIZE:usize = 4; +const FAT_ENTRY_MASK:u32 = 0x0FFF_FFFF; + +#[derive(Clone, Debug)] +pub struct FatIndex{ + sector_index:u32, + sector_offset:usize, +} + +#[derive(Clone, Copy)] +pub struct FatInfo{ + first_fat_start_sector:usize, + sectors_per_fat:usize, + fats_count:usize +} + +impl FatInfo{ + pub fn new(first_fat_start_sector:usize, sectors_per_fat:usize, fats_count: usize)->Self{ + Self { first_fat_start_sector, sectors_per_fat, fats_count } + } +} + +/// This is the default size of a fat buffer. +/// This size is just a result of tweaking between fewer read operations and smaller working buffers +pub const DEFAULT_FAT_BUFFER_SIZE:usize = SECTOR_SIZE as usize * 100; + +pub struct FatBuffer{ + buffer:[u8;FBS], + buffer_len: usize, + /// Start of the buffer (immutable) + fat_start_index:FatIndex, + /// Running counter of the current postion on the buffer (mutable) + fat_internal_index:FatIndex, + fat_info:FatInfo, +} + +impl FatBuffer{ + // The buffer Im reading will be the same buffer that Im writing back + // so it must be aligned in order to not corrupt the fat table + pub fn new(fat_info:FatInfo, first_cluster_index:usize, entries_count: Option, disk: &mut Disk)->Self{ + let entries_count = entries_count.unwrap_or((FBS - SECTOR_SIZE) / FAT_ENTRY_SIZE); // The max size is smaller cause I need some padding space for alignment + let mut buffer = [0; FBS]; + let fat_offset = first_cluster_index * FAT_ENTRY_SIZE; + let fat_index = FatIndex{ + sector_index: (fat_info.first_fat_start_sector + (fat_offset / SECTOR_SIZE)) as u32, + sector_offset: fat_offset % SECTOR_SIZE + }; + + // Align the end read to SECTOR_SIZE, since the FAT table is not aligned we need to read exactly X sectors in order to be able to write them back later + let fat_end_read = (entries_count * FAT_ENTRY_SIZE) + (SECTOR_SIZE - ((entries_count * FAT_ENTRY_SIZE) % SECTOR_SIZE)); + if fat_end_read > FBS{ + core::panic!("Error fat entries count is too much: expected:{}, actual: {}", FBS / FAT_ENTRY_SIZE, entries_count); + } + disk.read(fat_index.sector_index, &mut buffer[..fat_end_read]); + + return Self { + buffer, + fat_start_index: fat_index.clone(), + fat_internal_index: fat_index, + buffer_len: fat_end_read, + fat_info + }; + } + + /// On success returns the FAT entry, on error returns the last valid fat index + pub fn read(&mut self)->Result{ + let entry_slice = self.get_internal_sector_index_entry_slice()?; + let entry = Self::bytes_to_fat_entry((*entry_slice).try_into().unwrap()); + self.increment_fat_internal_index(); + // Mask the entry to hide the reserved bits + return Ok(entry & FAT_ENTRY_MASK); + } + + /// On error returns the last valid fat index + pub fn write(&mut self, mut value:u32)->Result<(), FatIndex>{ + let entry_slice = self.get_internal_sector_index_entry_slice()?; + let entry = Self::bytes_to_fat_entry((*entry_slice).try_into().unwrap()); + let reserved_bits = entry & (!FAT_ENTRY_MASK); + value = (value & FAT_ENTRY_MASK) | reserved_bits; + entry_slice.copy_from_slice(&Self::fat_entry_to_bytes(value)); + self.increment_fat_internal_index(); + return Ok(()); + } + + /// Sync the fat buffer to the disk + pub fn flush(&mut self, disk:&mut Disk){ + // Sync all the fat sectors to disk + for i in 0..self.fat_info.fats_count{ + let start_sector = self.fat_start_index.sector_index + (self.fat_info.sectors_per_fat * i) as u32; + let _ = disk.write(start_sector, &mut self.buffer[..self.buffer_len]); + } + } + + // Returns the internal sector index slice, on error returns the last valid fat index + fn get_internal_sector_index_entry_slice(&mut self) -> Result<&mut [u8], FatIndex> { + let internal_sector_index = self.get_internal_sector_index()?; + let start_index = (internal_sector_index * SECTOR_SIZE) + self.fat_internal_index.sector_offset; + return Ok(&mut self.buffer[start_index .. start_index + FAT_ENTRY_SIZE]); + } + + fn increment_fat_internal_index(&mut self) { + self.fat_internal_index.sector_offset += FAT_ENTRY_SIZE; + if self.fat_internal_index.sector_offset >= SECTOR_SIZE{ + self.fat_internal_index.sector_index += 1; + self.fat_internal_index.sector_offset = 0; + } + } + + fn get_internal_sector_index(&self)->Result{ + let internal_sector_index = (self.fat_internal_index.sector_index - self.fat_start_index.sector_index) as usize; + if internal_sector_index * SECTOR_SIZE >= self.buffer_len{ + return Err(self.fat_internal_index.clone()); + } + return Ok(internal_sector_index); + } + + fn bytes_to_fat_entry(buffer:&[u8;FAT_ENTRY_SIZE])->u32 {u32::from_ne_bytes(*buffer)} + fn fat_entry_to_bytes(entry:u32)->[u8;FAT_ENTRY_SIZE] {u32::to_ne_bytes(entry)} +} \ No newline at end of file diff --git a/rpi/src/drivers/fat32/mod.rs b/rpi/src/drivers/fat32/mod.rs new file mode 100644 index 00000000..1944fffb --- /dev/null +++ b/rpi/src/drivers/fat32/mod.rs @@ -0,0 +1,543 @@ +mod fat_buffer; + +use core::{mem::size_of, ops::ControlFlow}; + +use arrayvec::ArrayVec; + +use crate::peripherals::compile_time_size_assert; +use super::{as_mut_buffer, disk::*}; +use fat_buffer::*; + +#[derive(Default)] +#[repr(C, packed)] +struct Fat32BiosParameterBlock{ + // Base fields + bytes_per_sector: u16, + sectors_per_cluster: u8, + reserved_sectors_count: u16, + fats_count: u8, + root_entries_count:u16, + total_sectors_count_16: u16, + media: u8, + sectors_per_fat_16:u16, + sectors_per_track:u16, + heads_count:u16, + hidden_sectors_count:u32, + total_sectors_count_32:u32, + + // extended fat32 fields + sectors_per_fat_32:u32, + extended_flags:u16, + fs_version:u16, + root_dir_first_cluster:u32, + fs_info_sector:u16, + backup_boot_sector:u16, + _reserved0:[u8;12], +} +compile_time_size_assert!(Fat32BiosParameterBlock, 53); + +#[repr(C, packed)] +struct Fat32BootSector{ + jump_boot:[u8;3], + oem_name:[u8;8], + fat32_bpb:Fat32BiosParameterBlock, + drive_num:u8, + _reserved1:u8, + boot_signature:u8, + volume_id:u32, + volume_label:[u8;11], + fs_type_label:[u8;8], + _pad:[u8;420], + signature_word:[u8;2], +} +compile_time_size_assert!(Fat32BootSector, 512); + +impl Default for Fat32BootSector{ + fn default() -> Self { + Self { + jump_boot: Default::default(), oem_name: Default::default(), fat32_bpb: Default::default(), drive_num: Default::default(), + _reserved1: Default::default(), boot_signature: Default::default(), volume_id: Default::default(), + volume_label: Default::default(), fs_type_label: Default::default(), _pad: [0;420], signature_word: Default::default() + } + } +} + +#[derive(Clone, Copy, Default)] +#[repr(C, packed)] +struct FatShortDirEntry{ + file_name:[u8;Self::FILE_NAME_SIZE], + file_extension:[u8;Self::FILE_EXTENSION_SIZE], + attributes:u8, + nt_reserve:u8, + creation_time_tenth_secs:u8, + creation_time:u16, + creation_date:u16, + last_access_date:u16, + first_cluster_index_high:u16, + last_write_time:u16, + last_write_date:u16, + first_cluster_index_low:u16, + size:u32, +} +compile_time_size_assert!(FatShortDirEntry, 32); + +impl FatShortDirEntry{ + const FILE_NAME_SIZE:usize = 8; + const FILE_EXTENSION_SIZE:usize = 3; + const FULL_FILENAME_SIZE:usize = Self::FILE_NAME_SIZE + Self::FILE_EXTENSION_SIZE; + + fn new(name:[u8;Self::FILE_NAME_SIZE], extension:[u8;Self::FILE_EXTENSION_SIZE], size:u32)->Self{ + Self { + file_name: name, file_extension: extension, attributes: 0, nt_reserve: 0, creation_time_tenth_secs: 0, creation_time: 0, + creation_date: 0, last_access_date: 0, first_cluster_index_high:0, last_write_time: 0, last_write_date: 0, first_cluster_index_low:0, size + } + } + fn get_first_cluster_index(&self)->u32{ + self.first_cluster_index_low as u32 | ((self.first_cluster_index_high as u32) << 16) + } + fn set_first_cluster_index(&mut self, first_cluster_index:u32){ + self.first_cluster_index_low = (first_cluster_index & 0xFFFF) as u16; + self.first_cluster_index_high = (first_cluster_index >> 16) as u16; + } + fn get_filename(&self)->[u8;Self::FULL_FILENAME_SIZE]{ + let mut filename = [0;Self::FULL_FILENAME_SIZE]; + filename[.. Self::FILE_NAME_SIZE].copy_from_slice(&self.file_name); + filename[Self::FILE_NAME_SIZE ..].copy_from_slice(&self.file_extension); + return filename; + } +} + +// This struct is for support to the long filenames that I will add later +#[allow(unused)] +#[derive(Clone, Copy)] +#[repr(C,packed)] +struct FatLongDirEntry{ + order:u8, + name1:[u16;5], + attributes:u8, + ext_type:u8, + check_sum:u8, + name2:[u16;6], + _first_cluster_low:u16, + name3:[u16;2] +} + +const DISK_PARTITION_INDEX:u8 = 0; +const SECTOR_SIZE:usize = Disk::BLOCK_SIZE as usize; +const FAT_ENTRY_EOF_INDEX:u32 = 0x0FFF_FFFF; +const FAT_ENTRY_FREE_INDEX:u32 = 0; +const DELETED_DIR_ENTRY_PREFIX:u8 = 0xE5; +const DIR_EOF_PREFIX:u8 = 0; + +const FAT_DIR_ENTRIES_PER_SECTOR:usize = SECTOR_SIZE as usize / core::mem::size_of::(); + +const ATTR_READ_ONLY:u8 = 0x1; +const ATTR_HIDDEN:u8 = 0x2; +const ATTR_SYSTEM:u8 = 0x4; +const ATTR_VOLUME_ID:u8 = 0x8; +const ATTR_LONG_NAME:u8 = ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID; + +#[derive(Default, Clone, Copy)] +pub struct FileEntry{ + name:[u8;Self::FILENAME_SIZE], + first_cluster_index:u32, + pub size:u32, +} + +impl FileEntry{ + pub const FILENAME_SIZE:usize = 11; + pub fn get_name<'a>(&'a self)->&'a str{ + core::str::from_utf8(&self.name).unwrap().trim() + } + pub fn get_extension<'a>(&'a self)->&'a str{ + core::str::from_utf8(&self.name[8..]).unwrap().trim() + } +} + +/// Represent a consecutive fat entries with the same value. +/// This is an implementation detail and not defined by the specefications. +#[derive(Clone, Debug)] +struct FatSegment{ + state:FatSegmentState, + len:u32, + start_index:u32, +} + +impl FatSegment{ + fn new(value:u32, start_index:u32)->Self{ + Self { state: value.into(), len: 1, start_index} + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +enum FatSegmentState{ + Free, + Allocated, + AllocatedEof, + Reserved, + Bad, +} + +impl From for FatSegmentState{ + fn from(value: u32) -> Self { + match value{ + 0 => Self::Free, + 2..=0xFFF_FFF5 => Self::Allocated, + 0xFFF_FFFF => Self::AllocatedEof, + 0xFFF_FFF7 => Self::Bad, + _ => Self::Reserved + } + } +} + +impl FatSegmentState{ + /// Checks whether a value should be part of this segment or not + fn should_continue_segment(&self, other: &Self)->bool{ + // AllocatedEof should never continue segment + // otherwise fallback to check raw values of the enum + if *self == Self::AllocatedEof || *other == Self::AllocatedEof{ + return false; + } + return self == other; + } +} + +pub struct Fat32Fs{ + disk: Disk, + boot_sector:Fat32BootSector, + partition_start_sector_index:u32, + + clusters_count:u32, + fat_info:FatInfo, + fat_table_cache: ArrayVec, + root_dir_cache: ArrayVec, + root_dir_allocated_clusters_count: u32, +} + +impl Fat32Fs{ + // Currently the driver support only 0x100 files in the root directory + const MAX_FILES: usize = 0x100; + + // Assuming each file contains at max 2 segments (allocated and eof) and between every 2 files + // there is another free segment I minimized it and added a buffer just in case + const MAX_FAT_SEGMENTS_COUNT: usize = Self::MAX_FILES * 5; + + pub fn new()->Self{ + let mut disk = Disk::new(); + // This driver currently support only a single partition (some has more than one for backup or stuff I don't know) + let bpb_sector_index = disk.get_partition_first_sector_index(DISK_PARTITION_INDEX); + + let mut boot_sector:Fat32BootSector = Default::default(); + // SAFETY: Fat32BootSector is repr(C) and therefore safe to transmute to byte slice + let buffer = unsafe{as_mut_buffer(&mut boot_sector)}; + disk.read(bpb_sector_index, buffer); + + let fs_type_label = boot_sector.fs_type_label.clone(); + let fat_magic = &fs_type_label[0..3]; + if fat_magic != b"FAT"{ + core::panic!("File system is not FAT, found magic: {:?}", fat_magic); + } + if boot_sector.fat32_bpb.sectors_per_fat_16 != 0{ + core::panic!("Detected FAT16 and not FAT32 file system"); + } + let bytes_per_sector = boot_sector.fat32_bpb.bytes_per_sector as u32; + if bytes_per_sector != SECTOR_SIZE as u32{ + core::panic!("Currently not supporting fat32 disks with sectors size other than {}", SECTOR_SIZE); + } + let fat_count = boot_sector.fat32_bpb.fats_count; + log::debug!("FAT count: {}", fat_count); + + let fat32_data_sectors = boot_sector.fat32_bpb.total_sectors_count_32 - (boot_sector.fat32_bpb.reserved_sectors_count as u32 + (boot_sector.fat32_bpb.sectors_per_fat_32 as u32 * boot_sector.fat32_bpb.fats_count as u32)); + let clusters_count = fat32_data_sectors / boot_sector.fat32_bpb.sectors_per_cluster as u32; + let fat_start_sector = (bpb_sector_index + boot_sector.fat32_bpb.reserved_sectors_count as u32) as usize; + let mut fat32 = Self { + fat_info:FatInfo::new( fat_start_sector, boot_sector.fat32_bpb.sectors_per_fat_32 as usize, boot_sector.fat32_bpb.fats_count as usize ), + disk, boot_sector, partition_start_sector_index:bpb_sector_index, clusters_count, + fat_table_cache: ArrayVec::::new(), + root_dir_cache: ArrayVec::::new(), + root_dir_allocated_clusters_count: 0 + }; + fat32.init_root_directory_cache(); + fat32.init_fat_table_cache(); + + log::info!("Initialized the FAT32 driver"); + + return fat32; + } + + fn init_root_directory_cache(&mut self){ + let root_start_sector_index = self.get_cluster_start_sector_index(self.boot_sector.fat32_bpb.root_dir_first_cluster); + let mut sector_offset = 0; + 'search: loop{ + let mut root_dir = [FatShortDirEntry::default();FAT_DIR_ENTRIES_PER_SECTOR]; + // SAFETY: FatShortDirEntry is repr(C) and packed and since arrays has the same alingnment as T it is safe + let buffer = unsafe{as_mut_buffer(&mut root_dir)}; + self.disk.read(root_start_sector_index + sector_offset, buffer); + sector_offset += 1; // Since root_dir buffer contains enough entries for exactly 1 sector + for dir in root_dir{ + // Pushing also the DIR_EOF in order to support syncing the whole dir easily + self.root_dir_cache.push(dir); + if dir.file_name[0] == DIR_EOF_PREFIX { + break 'search; + } + } + } + let sectors_per_cluster = self.boot_sector.fat32_bpb.sectors_per_cluster as u32; + self.root_dir_allocated_clusters_count = sector_offset / sectors_per_cluster + ((sector_offset % sectors_per_cluster) != 0) as u32; + log::debug!("Root dir allocated clusters count: {}", self.root_dir_allocated_clusters_count); + } + + // Failed Optimization Attempt: I tried to read the files from the root dir, and once I have all the entries abort and mark the rest of the clusters as free + // for some reason there were allocated entries on the FAT that I couldn't understand what allocated them and couldn't predict and calculate the expected entries count + // Ill leave it like that for now + fn init_fat_table_cache(&mut self){ + // This buffer is bigger then the default in order to minimize the number of read operations + // The value is tweaked for faster reads + const INIT_FAT_BUFFER_SIZE:usize = DEFAULT_FAT_BUFFER_SIZE * 10; + let mut fat_buffer:FatBuffer = FatBuffer::new(self.fat_info, 0, None, &mut self.disk); + + // The fat has entry per cluster in the volume, were adding 2 for the first 2 reserved entries (0,1) + // This way the array is larger by 2 (fat entry at position clusters_count + 1 is the last valid entry) + let fat_entries_count = self.clusters_count + 1; + log::debug!("fat entries count {}", fat_entries_count); + + // Since indices [0,1] are reserved ignore and discard them + let _ = fat_buffer.read().ok().unwrap(); + let _ = fat_buffer.read().ok().unwrap(); + + // Index 2 to bootstrap + let fat_entry = fat_buffer.read().ok().unwrap(); + let mut current_segment = FatSegment::new(fat_entry, 2); + + for i in 3..=fat_entries_count{ + let fat_entry = fat_buffer.read().unwrap_or_else(|_|{ + fat_buffer = FatBuffer::new(self.fat_info, i as usize, None, &mut self.disk); + fat_buffer.read().ok().unwrap() + }); + if current_segment.state.should_continue_segment(&FatSegmentState::from(fat_entry)) { + current_segment.len += 1; + continue; + } + self.fat_table_cache.push(current_segment.clone()); + current_segment = FatSegment::new(fat_entry, i); + } + self.fat_table_cache.push(current_segment); + log::debug!("Fat segments count {}", self.fat_table_cache.len()); + } + + pub fn root_dir_list(&mut self, offset:usize)->ArrayVec{ + let mut output_dir = ArrayVec::::new(); + let mut discard = offset; + + for dir in &self.root_dir_cache { + if dir.file_name[0] == DIR_EOF_PREFIX{ + break; + } + if dir.file_name[0] == DELETED_DIR_ENTRY_PREFIX{ + continue; + } + if dir.attributes == ATTR_LONG_NAME{ + continue; + // TODO: handle long file names here + } + if discard > 0{ + discard -= 1; + continue; + } + let filename = dir.get_filename(); + let first_cluster_index = dir.get_first_cluster_index(); + + output_dir.push(FileEntry{ name: filename, first_cluster_index, size: dir.size }); + if output_dir.remaining_capacity() == 0{ + break; + } + } + + return output_dir; + } + + // In this implementation Im trying to read as much many clusters as possible at a time + // in order to improve performance + /// Reads a file from the first FAT + pub fn read_file(&mut self, file_entry:&FileEntry, output:&mut [u8]){ + log::debug!("Reading file {}, size {}, cluster: {}", file_entry.get_name(), file_entry.size, file_entry.first_cluster_index); + if file_entry.size == 0 {return} + + let sectors_per_cluster = self.boot_sector.fat32_bpb.sectors_per_cluster; + let mut fat_first_entry = self.fat_table_cache.as_slice().into_iter().find(|t|t.start_index == file_entry.first_cluster_index).unwrap().clone(); + match fat_first_entry.state { + FatSegmentState::Allocated => fat_first_entry.len += 1, + FatSegmentState::AllocatedEof => {}, + _ => core::panic!("FAT32 Driver Error! - tried to read file: {} but the fat segment was not allocated: {}", file_entry.get_name(), file_entry.first_cluster_index), + } + let mut fat_buffer:FatBuffer = FatBuffer::new(self.fat_info, file_entry.first_cluster_index as usize, Some(fat_first_entry.len as usize), &mut self.disk); + + let mut current_cluster = file_entry.first_cluster_index; + let mut next_read_cluster = current_cluster; + let mut clusters_sequence = 1; + let mut cluster_counter:usize = 0; + while cluster_counter < fat_first_entry.len as usize{ + let fat_entry = fat_buffer.read().ok().unwrap(); + if current_cluster + 1 == fat_entry{ + current_cluster = fat_entry; + clusters_sequence += 1; + continue; + } + let start_sector = self.get_cluster_start_sector_index(next_read_cluster); + let start_index = sectors_per_cluster as usize * cluster_counter * SECTOR_SIZE as usize; + let end_index = core::cmp::min(output.len(), start_index + (sectors_per_cluster as usize * SECTOR_SIZE as usize * clusters_sequence)); + self.disk.read(start_sector, &mut output[start_index..end_index]); + + next_read_cluster = fat_entry; + current_cluster = fat_entry; + cluster_counter += clusters_sequence; + clusters_sequence = 1; + } + } + + /// Write a file to the root dir + pub fn write_file(&mut self, filename:&str, content:&[u8]){ + log::debug!("Writing file: {}, size: {}", filename, content.len()); + let sectors_per_cluster = self.boot_sector.fat32_bpb.sectors_per_cluster as u32; + let cluster_size = sectors_per_cluster * SECTOR_SIZE as u32; + let (name, extension) = self.create_filename(filename).unwrap_or_else(|_|core::panic!("File name format is bad: {}", filename)); + + // check if file exists, if exists try to overwrite it, if cant mark it as deleted + if let ControlFlow::Break(()) = self.handle_existing_filename(name, extension, content) {return} + + // create a new file by allocating place in the root dir and then picking some free fat segment to use it's clusters + let new_dir_entry = match self.root_dir_cache.as_mut_slice().into_iter().find(|d|d.file_name[0] == DELETED_DIR_ENTRY_PREFIX){ + Some(dir) => { + dir.file_name = name; + dir.file_extension = extension; + dir + } + None => { + // Check the root dir allocation size to check it needs to be reallocated + let root_dir_allocation_size = (self.root_dir_allocated_clusters_count * self.boot_sector.fat32_bpb.sectors_per_cluster as u32) as usize * SECTOR_SIZE; + let expected_root_dir_size_after_allocation = (self.root_dir_cache.len() + 1) * size_of::(); + if root_dir_allocation_size <= expected_root_dir_size_after_allocation { + core::panic!("root dir is too small: {:#X} and driver do not support resizing of the root dir", root_dir_allocation_size); + } + // Allocate new entry in the root dir + self.root_dir_cache.insert(self.root_dir_cache.len() - 1, FatShortDirEntry::new(name, extension, content.len() as u32)); // write to the last place pushing the last item to index len(), in order to keep the DIR_EOF last + let root_dir_cache_updated_len = self.root_dir_cache.len(); + + // Root dir cache len must be at least 2 (entry for the root dir itself and a EOF) and not the one I inserted (so actually 3) + // This returns the last non EOF entry + &mut self.root_dir_cache[root_dir_cache_updated_len - 2] + } + }; + let required_clusters_count = (content.len() as u32 / cluster_size) + (content.len() as u32 % cluster_size != 0) as u32; + let free_fat_segment = self.fat_table_cache.as_slice().into_iter().find(|t|t.state == FatSegmentState::Free && t.len >= required_clusters_count).unwrap(); + let first_cluster_index = free_fat_segment.start_index; + + // Update the fat and the root directory + log::debug!("File first cluster index: {}", first_cluster_index); + new_dir_entry.set_first_cluster_index(first_cluster_index); + new_dir_entry.size = content.len() as u32; + + let mut fat_buffer:FatBuffer = FatBuffer::new(self.fat_info, first_cluster_index as usize, Some(required_clusters_count as usize), &mut self.disk); + for i in 0..required_clusters_count - 1{ + // Adding 1 in order to point to the next fat entry, since all the cluster are allocated in contiguous disk clusters + fat_buffer.write(first_cluster_index + i + 1).unwrap(); + } + fat_buffer.write(FAT_ENTRY_EOF_INDEX).unwrap(); + + // write the data to the clusters, since the cluster index is the initial index in the fat I can know which one is free or allocated + self.write_to_data_section(content, first_cluster_index); + + // sync the modifications + fat_buffer.flush(&mut self.disk); + self.write_root_dir_cache(); + } + + fn handle_existing_filename(&mut self, name: [u8; 8], extension: [u8; 3], content: &[u8]) -> ControlFlow<()> { + if let Some(existing_entry) = self.root_dir_cache.as_mut_slice().into_iter().find(|d|d.file_name == name && d.file_extension == extension){ + log::debug!("File already exists, overwriting it"); + // Early return if the existing file is empty (and contain no allocated space) + if existing_entry.size == 0 { + existing_entry.file_name[0] = DELETED_DIR_ENTRY_PREFIX; + return ControlFlow::Continue(()) + }; + + let first_cluster_index = existing_entry.get_first_cluster_index(); + let segment = self.fat_table_cache.as_slice().into_iter().find(|f|f.start_index == first_cluster_index).unwrap().clone(); + let segment_len = match segment.state { + FatSegmentState::Allocated => segment.len + 1, + FatSegmentState::AllocatedEof => 1, + _ => core::panic!("FAT32 FS Error: received not allocated segment"), + }; + let mut fat_buffer:FatBuffer = FatBuffer::new(self.fat_info, first_cluster_index as usize, Some(segment_len as usize), &mut self.disk); + + // Possible Optimization: Allow more cases to reuse the allocation + // 1. if its in the range of the cluster alignment + // 2. If its smaller than the required size (can use some of the allocation) + // + // This check also verifies that the allocation is continuous, allocation done by this driver are continous but other drivers can allocate differently. + // If the allocation is not continous the overwrite logic will not work as it assumes continous allocation. + // The check done by the fact that it checks for allocated segment for this file allocation + // and the if the allocated segment is the len of the file -> the allocation is continous. + if (existing_entry.size as usize) == content.len(){ + log::debug!("Using existing allocation"); + let first_cluster_index = existing_entry.get_first_cluster_index(); + self.write_to_data_section(content, first_cluster_index); + return ControlFlow::Break(()); + } + else{ + existing_entry.file_name[0] = DELETED_DIR_ENTRY_PREFIX; + // Mark the fat entries as free in order to make them usable + for _ in 0..segment_len{ + fat_buffer.write(FAT_ENTRY_FREE_INDEX).ok().unwrap(); + } + fat_buffer.flush(&mut self.disk); + // The root dir cache is written to disk at the end of the write operation + } + } + return ControlFlow::Continue(()); + } + + fn write_to_data_section(&mut self, content: &[u8], first_cluster_index: u32) { + let start_sector = self.get_cluster_start_sector_index(first_cluster_index); + self.disk.write(start_sector, content); + } + + fn create_filename(&self, filename:&str)->Result<([u8;8],[u8;3]), ()>{ + const FAT32_ILLEGAL_CHARS:[u8;16] = [b'"', b'*', b'+', b',', b'.', b'/', b':', b';', b'<', b'=', b'>', b'?', b'[',b'\\', b']', b'|' ]; + if filename.len() != FatShortDirEntry::FULL_FILENAME_SIZE || + !filename.is_ascii() || + filename.as_bytes().into_iter().any(|b|FAT32_ILLEGAL_CHARS.contains(b) && *b > 0x20) || + filename.as_bytes().into_iter().any(|c| *c >= b'a' && *c <= b'z'){ + return Err(()); + } + let raw_filename:[u8;FatShortDirEntry::FULL_FILENAME_SIZE] = filename.as_bytes().try_into().unwrap(); + let name:[u8;FatShortDirEntry::FILE_NAME_SIZE] = raw_filename[.. FatShortDirEntry::FILE_NAME_SIZE].try_into().unwrap(); + let extension:[u8;FatShortDirEntry::FILE_EXTENSION_SIZE] = raw_filename[FatShortDirEntry::FILE_NAME_SIZE ..].try_into().unwrap(); + return Ok((name, extension)); + } + + fn write_root_dir_cache(&mut self){ + let root_sector_index = self.get_cluster_start_sector_index(self.boot_sector.fat32_bpb.root_dir_first_cluster); + // SAFETY: FatShortDirEntry layout is repr(C) + let buffer = unsafe{Self::arrayvec_as_buffer(&self.root_dir_cache)}; + self.disk.write(root_sector_index, buffer); + } + + fn get_cluster_start_sector_index(&self, cluster:u32)->u32{ + const FIRST_DATA_CLUSTER:u32 = 2; + + self.partition_start_sector_index + self.boot_sector.fat32_bpb.reserved_sectors_count as u32 + + ((cluster - FIRST_DATA_CLUSTER) * self.boot_sector.fat32_bpb.sectors_per_cluster as u32) + + (self.boot_sector.fat32_bpb.sectors_per_fat_32 * self.boot_sector.fat32_bpb.fats_count as u32) + } + + /// Takes an `ArrayVec` and converts it to a byte slice + /// This is a function in order to borrow the input properly and bind in to the output slice + /// The function borrows the vec and returns a slice binded to the vec borrow + /// + /// ## SAFETY + /// T layout must be known (AKA `repr(C)`) + unsafe fn arrayvec_as_buffer<'a, T, const CAP:usize>(vec:&'a ArrayVec)->&'a [u8]{ + core::slice::from_raw_parts(vec.as_ptr() as *const u8, vec.len() * core::mem::size_of::()) + } +} \ No newline at end of file diff --git a/rpi/src/drivers/mod.rs b/rpi/src/drivers/mod.rs index 5f6ba12a..664b3d9a 100644 --- a/rpi/src/drivers/mod.rs +++ b/rpi/src/drivers/mod.rs @@ -1,6 +1,6 @@ mod gpio_joypad; mod ili9341_gfx_device; -cfg_if::cfg_if!{ if #[cfg(not(feature = "os"))]{ +cfg_if::cfg_if!{ if #[cfg(feature = "bm")]{ pub(super) mod disk; mod fat32; pub use fat32::*; @@ -10,8 +10,11 @@ pub use gpio_joypad::*; pub use ili9341_gfx_device::*; -#[cfg(not(feature = "os"))] +#[cfg(feature = "bm")] +/// Casts a type to slice of bytes while keeping the lifetime (fancy reinterepter cast to byte array) +/// +/// ## SAFETY +/// `T` byte representation must be known (aka `repr(C)`) in order for the slice to be usable without UB pub(crate) unsafe fn as_mut_buffer<'a, T>(t:&'a mut T)->&'a mut [u8]{ - let buffer = &mut *core::ptr::slice_from_raw_parts_mut(t as *mut T as *mut _, core::mem::size_of::()); - return buffer; + core::slice::from_raw_parts_mut(t as *mut T as *mut _, core::mem::size_of::()) } \ No newline at end of file diff --git a/rpi/src/lib.rs b/rpi/src/lib.rs index bb744b58..25dd797d 100644 --- a/rpi/src/lib.rs +++ b/rpi/src/lib.rs @@ -1,12 +1,12 @@ -#![cfg_attr(not(feature = "os"), no_std)] +#![cfg_attr(feature = "bm", no_std)] -#[cfg(all(feature = "os", rpi))] -core::compile_error!("The os feature and the rpi cfg value cant be set at the same time"); +#[cfg(all(feature = "os", feature = "bm"))] +core::compile_error!("The os feature and bm feature cant be set at the same time"); pub mod configuration; pub mod peripherals; pub mod drivers; -cfg_if::cfg_if!{ if #[cfg(not(feature = "os"))]{ +cfg_if::cfg_if!{ if #[cfg(feature = "bm")]{ pub mod syncronization; pub mod delay; }} diff --git a/rpi/src/peripherals/emmc.rs b/rpi/src/peripherals/emmc.rs index fdee624f..0245afe0 100644 --- a/rpi/src/peripherals/emmc.rs +++ b/rpi/src/peripherals/emmc.rs @@ -100,6 +100,7 @@ struct SdCommand{ impl SdCommand{ fn get_command(self)->u32{ + // Reverting the app_command bit in case it will disrupt with the emmc device self.with_app_command(false).0 } } @@ -117,10 +118,12 @@ const fn resolve_command(command_type:SdCommandType)->SdCommand{ SdCommandType::ReadBlock => command.with_direction(true).with_response_type(CommandResponseType::B48).with_crc_enable(true).with_is_data(true), SdCommandType::ReadMultiple => command.with_block_count(true).with_auto_command(1).with_direction(true).with_multiblock(true) .with_response_type(CommandResponseType::B48).with_crc_enable(true).with_is_data(true), + SdCommandType::WriteBlock => command.with_direction(false).with_response_type(CommandResponseType::B48).with_crc_enable(true).with_is_data(true), + SdCommandType::WriteMultiple => command.with_block_count(true).with_auto_command(1).with_direction(false).with_multiblock(true) + .with_response_type(CommandResponseType::B48).with_crc_enable(true).with_is_data(true), SdCommandType::OcrCheck => command.with_response_type(CommandResponseType::B48).with_app_command(true), SdCommandType::SendScr => command.with_direction(true).with_response_type(CommandResponseType::B48).with_crc_enable(true).with_is_data(true).with_app_command(true), - SdCommandType::App => command.with_response_type(CommandResponseType::B48).with_crc_enable(true), - _=> unreachable!() + SdCommandType::App => command.with_response_type(CommandResponseType::B48).with_crc_enable(true) }; } @@ -158,6 +161,20 @@ struct Scr{ version:u32, } +enum InternalBuffer<'a>{ + Mutable(&'a mut [u8]), + Immutable(&'a [u8]), +} + +impl<'a> InternalBuffer<'a>{ + fn len(&self)->usize{ + match self{ + Self::Mutable(b) => b.len(), + Self::Immutable(b) => b.len(), + } + } +} + // Mailbox params const GPIO_TAG_PIN_1_8V_CONTROL:u32 = 132; const SD_CARD_DEVICE_ID:u32 = 0; @@ -280,21 +297,26 @@ impl Emmc{ } pub fn read(&mut self, buffer:&mut [u8])->bool{ - if self.offset % BLOCK_SIZE as u64 != 0{ - return false; - } - - let block = self.offset / BLOCK_SIZE as u64; + memory_barrier(); + let result = self.execute_data_transfer_command(false, InternalBuffer::Mutable(buffer)).is_ok(); + memory_barrier(); + return result; + } + pub fn write(&mut self, buffer: &[u8])->bool{ memory_barrier(); - let result = self.execute_data_transfer_command(false, buffer, block as u32).is_ok(); + let result = self.execute_data_transfer_command(true, InternalBuffer::Immutable(buffer)).is_ok(); memory_barrier(); return result; } - pub fn get_block_size(&self)->u32{self.block_size} + pub const fn get_block_size()->u32{BLOCK_SIZE} - fn execute_data_transfer_command(&mut self, write: bool, buffer: &mut [u8], mut block_index:u32)->Result<(), SdError>{ + fn execute_data_transfer_command(&mut self, write: bool, mut buffer: InternalBuffer)->Result<(), SdError>{ + if self.offset % BLOCK_SIZE as u64 != 0{ + return Err(SdError::Error); + } + let mut block_index = (self.offset / BLOCK_SIZE as u64) as u32; if !self.sdhc_support{ block_index *= BLOCK_SIZE; } @@ -316,7 +338,7 @@ impl Emmc{ else{SdCommandType::ReadBlock}; for _ in 0..3{ - if self.send_command(command_type, block_index, 5000, Some(buffer)).is_ok(){ + if self.send_command(command_type, block_index, 5000, Some(&mut buffer)).is_ok(){ return Ok(()); } } @@ -397,7 +419,7 @@ impl Emmc{ let mut scr_buffer = [0;8]; self.block_size = 8; self.transfer_blocks = 1; - self.send_command(SdCommandType::SendScr, 0, 30000, Some(&mut scr_buffer)).unwrap(); + self.send_command(SdCommandType::SendScr, 0, 30000, Some(&mut InternalBuffer::Mutable(&mut scr_buffer))).unwrap(); // continue later self.block_size = BLOCK_SIZE; self.scr.register[0] = u32::from_ne_bytes(scr_buffer[0..4].try_into().unwrap()); @@ -428,7 +450,7 @@ impl Emmc{ log::debug!("SD Spec version: {}", self.scr.version); } - fn transfer_data(&mut self, command:SdCommand, buffer:&mut [u8]){ + fn transfer_data(&mut self, command:SdCommand, buffer:&mut InternalBuffer){ let (wr_irpt, write) = if command.direction(){(INTERRUPT_READ_RDY, false)} else{(INTERRUPT_WRITE_RDY, true)}; for i in 0..self.transfer_blocks{ @@ -444,12 +466,14 @@ impl Emmc{ let iteration_len = self.block_size as usize / DATA_REG_SIZE; let block_index = i as usize * self.block_size as usize; if write{ + let InternalBuffer::Immutable(buffer) = buffer else {core::unreachable!("Internal Emmc Error!, Received Mutable buffer on emmc write operation")}; for j in 0..iteration_len{ let data:[u8; DATA_REG_SIZE as usize] = buffer[block_index + (j * DATA_REG_SIZE) .. block_index + ((j + 1) * DATA_REG_SIZE)].try_into().unwrap(); self.registers.data.write(u32::from_ne_bytes(data)); } } else{ + let InternalBuffer::Mutable(buffer) = buffer else {core::unreachable!("Internal Emmc Error!, Received Immutable buffer on emmc read operation")}; for j in 0..iteration_len{ let data = u32::to_ne_bytes(self.registers.data.read()); buffer[block_index + (j * DATA_REG_SIZE) .. block_index + ((j + 1) * DATA_REG_SIZE)].copy_from_slice(&data); @@ -464,7 +488,7 @@ impl Emmc{ return Self::wait_timeout(&self.registers.control[1], CONTROL1_SRST_CMD, true, 1, 10000).is_ok(); } - fn send_command(&mut self, command_type:SdCommandType, arg:u32, timeout_ms:u32, buffer:Option<&mut [u8]>)->Result<(), SdError>{ + fn send_command(&mut self, command_type:SdCommandType, arg:u32, timeout_ms:u32, buffer:Option<&mut InternalBuffer>)->Result<(), SdError>{ log::trace!("Received command type: {:#?}", command_type); let command = resolve_command(command_type); @@ -478,7 +502,7 @@ impl Emmc{ return self.send_raw_command(command, arg, timeout_ms, buffer); } - fn send_raw_command(&mut self, command:SdCommand, arg:u32, timeout_ms:u32, buffer:Option<&mut [u8]>)->Result<(), SdError>{ + fn send_raw_command(&mut self, command:SdCommand, arg:u32, timeout_ms:u32, buffer:Option<&mut InternalBuffer>)->Result<(), SdError>{ log::trace!("Command {:#X} is being processed", command.0); let block_size_count_value = self.block_size | (self.transfer_blocks << 16); @@ -565,7 +589,6 @@ impl Emmc{ self::delay::wait_ms(3); } - fn setup_peripheral_clock(&mut self, mbox:&mut Mailbox){ self.registers.control2.write(0); // clear according to Circle and LLD let clock_rate = self.get_base_clock(mbox); diff --git a/rpi/src/peripherals/gpio.rs b/rpi/src/peripherals/gpio.rs index b202bdf9..83908fec 100644 --- a/rpi/src/peripherals/gpio.rs +++ b/rpi/src/peripherals/gpio.rs @@ -1,4 +1,4 @@ -#[cfg(not(feature = "os"))] +#[cfg(feature = "bm")] pub use no_std_impl::*; #[cfg(feature = "os")] pub use std_impl::*; @@ -23,7 +23,7 @@ pub enum Trigger{ RisingEdge } -#[cfg(not(feature = "os"))] +#[cfg(feature = "bm")] pub mod no_std_impl{ use crate::{syncronization::Mutex, peripherals::utils::{compile_time_size_assert, MmioReg32, get_static_peripheral, memory_barrier, BulkWrite}}; use super::*; diff --git a/rpi/src/peripherals/mod.rs b/rpi/src/peripherals/mod.rs index 47785d3c..1ebda5f1 100644 --- a/rpi/src/peripherals/mod.rs +++ b/rpi/src/peripherals/mod.rs @@ -6,15 +6,17 @@ mod gpu; mod spi; mod dma; mod timer; -#[cfg(feature = "os")] -mod bcm_host; -cfg_if::cfg_if!{ if #[cfg(not(feature = "os"))]{ + +cfg_if::cfg_if!{ if #[cfg(feature = "bm")]{ mod emmc; mod power; pub(crate) use utils::compile_time_size_assert; pub use utils::PERIPHERALS_BASE_ADDRESS; pub use emmc::Emmc; pub use power::*; +} +else if #[cfg(feature = "os")]{ + mod bcm_host; }} pub use gpio::*; @@ -32,9 +34,9 @@ pub struct Peripherals{ mailbox: Peripheral, timer: Peripheral, spi0: Peripheral, - #[cfg(not(feature = "os"))] + #[cfg(feature = "bm")] emmc: Peripheral, - #[cfg(not(feature = "os"))] + #[cfg(feature = "bm")] power: Peripheral } @@ -62,11 +64,11 @@ impl Peripherals{ pub fn take_spi0(&mut self)->Spi0{ self.spi0.take(||spi::Spi0::new(SPI0_DC_BCM_PIN)) } - #[cfg(not(feature = "os"))] + #[cfg(feature = "bm")] pub fn take_emmc(&mut self)->emmc::Emmc{ self.emmc.take(||emmc::Emmc::new()) } - #[cfg(not(feature = "os"))] + #[cfg(feature = "bm")] pub fn take_power(&mut self)->Power{ self.power.take(||Power::new()) } @@ -78,8 +80,8 @@ pub static mut PERIPHERALS: Peripherals = Peripherals{ mailbox: Peripheral::Uninit, timer: Peripheral::Uninit, spi0: Peripheral::Uninit, - #[cfg(not(feature = "os"))] + #[cfg(feature = "bm")] emmc: Peripheral::Uninit, - #[cfg(not(feature = "os"))] + #[cfg(feature = "bm")] power: Peripheral::Uninit }; \ No newline at end of file diff --git a/rpi/src/peripherals/power.rs b/rpi/src/peripherals/power.rs index 3e454800..ffd3efbb 100644 --- a/rpi/src/peripherals/power.rs +++ b/rpi/src/peripherals/power.rs @@ -65,7 +65,7 @@ impl Power{ Self { registers: get_static_peripheral(PM_BASE_OFFSET) } } - pub fn reset(&mut self, mode:ResetMode){ + pub fn reset(&mut self, mode:ResetMode)->!{ let mbox = unsafe{PERIPHERALS.get_mailbox()}; for device_id in 0..RPI_DEVICES_COUNT{ mbox.call(Tag::SetPowerState, [device_id as u32, 0 /* power off, no wait */]); @@ -88,5 +88,9 @@ impl Power{ rstc_reg |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; self.registers.rstc.write(rstc_reg); memory_barrier(); + + // The program should not return from reset + // If it returns panic + core::unreachable!("Failed reset attempt"); } } \ No newline at end of file diff --git a/rpi/src/peripherals/utils.rs b/rpi/src/peripherals/utils.rs index 464a81d7..fae7ddbf 100644 --- a/rpi/src/peripherals/utils.rs +++ b/rpi/src/peripherals/utils.rs @@ -30,7 +30,7 @@ impl BulkWrite for [MmioReg32; SIZE]{ } } -// According to the docs the raspberrypi requires memory barrier between reads and writes to differnet peripherals +// According to the docs the raspberrypi requires memory barrier between reads and writes to different peripherals #[inline] pub(super) fn memory_barrier(){ core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); @@ -49,7 +49,7 @@ impl Peripheral{ } return match self{ Self::Init(t) => t, - Self::Taken => core::panic!("Peripheral is unavaliable, its been taken "), + Self::Taken => core::panic!("Peripheral is unavailable, its been taken "), Self::Uninit => core::unreachable!("At this point the peripheral must be initialized"), }; } @@ -59,7 +59,7 @@ impl Peripheral{ return match s{ Self::Uninit => init_callback(), Self::Init(t) => t, - Self::Taken => core::panic!("Peripheral is unavaliable, its been taken"), + Self::Taken => core::panic!("Peripheral is unavailable, its been taken"), }; } }