From 7fe4e7f010d2735ec0c08b09275c5b635e4be6cf Mon Sep 17 00:00:00 2001 From: pigeonmoelleux <pigeonmoelleux@crans.org> Date: Mon, 22 May 2023 23:13:49 +0200 Subject: [PATCH] feat(ext2): read superblock + block group descriptors --- .gitignore | 2 ++ Cargo.toml | 2 ++ src/fs/ext2/block.rs | 47 ++++++++++++++++++++++++++++++++++++ src/fs/ext2/directory.rs | 22 +++++++++++++++++ src/fs/ext2/mod.rs | 46 +++++++++++++++++++++++++++++++++++ src/fs/mod.rs | 24 ++++++++++++++++++ src/fs/node.rs | 8 +++--- src/kernel/device/ata.rs | 14 +++++++---- src/kernel/device/block.rs | 4 +-- src/kernel/device/mod.rs | 4 +++ src/kernel/interrupt/cmos.rs | 3 +++ src/kernel/mod.rs | 5 ---- src/main.rs | 22 +++++++++++++++++ 13 files changed, 188 insertions(+), 15 deletions(-) create mode 100644 src/fs/ext2/directory.rs diff --git a/.gitignore b/.gitignore index 6d33677..e7ebaf2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /coverage /target + +storage \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 7f751a5..e1ffa5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ x86_64 = "0.14" minicov = "0.3" [package.metadata.skavos-bootimage] +run-storage = "storage" test-success-exit-code = 13 test-args = [ "-serial", "stdio", @@ -44,3 +45,4 @@ test-args = [ "-display", "none", "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04" ] +test-mountpoint = "fs_tests" \ No newline at end of file diff --git a/src/fs/ext2/block.rs b/src/fs/ext2/block.rs index 8ecfaeb..6ba2a10 100644 --- a/src/fs/ext2/block.rs +++ b/src/fs/ext2/block.rs @@ -2,8 +2,25 @@ //! //! See the [OSdev wiki](https://wiki.osdev.org/Ext2) for more informations +use alloc::sync::Arc; +use core::mem; + +use anyhow::{anyhow, Result}; use bitflags::bitflags; use log::error; +use spin::Mutex; + +use super::read; +use crate::kernel::device::storage::Device; + +/// Starting byte of the superblock in a ext2 storage device +const SUPERBLOCK_START_BYTE: usize = 1024; + +/// Starting byte of the block group descriptor table in a ext2 storage device +const BLOCK_GROUP_DESCRIPTOR_TABLE_START_BYTE: usize = 2048; + +/// Ext2 signature, used to help confirm the presence of an Ext2 volume +const EXT2_SIGNATURE: u16 = 0xef53; /// Superblock of the ext2 filesystem /// @@ -138,6 +155,29 @@ pub struct Superblock { pub head_orphan_node_list: u32, } +impl Superblock { + /// Returns the superblock of the device if it exists + pub fn new(device: &Arc<Mutex<dyn Device + Send>>) -> Result<Self> { + // SAFETY: if the read is successful, a `Superblock` should be present in the first bytes of the buffer + let Ok(superblock) = (unsafe { read::<Superblock>(device, SUPERBLOCK_START_BYTE) }) else { return Err(anyhow!("ext2 superblock read: could not read the buffer pointer")) }; + if superblock.ext2_signature == EXT2_SIGNATURE + && superblock.total_blocks.div_ceil(superblock.blocks_per_group) + == superblock.total_inodes.div_ceil(superblock.inodes_per_group) + { + Ok(superblock) + } else { + Err(anyhow!("ext2 superblock read: malformed ext2 superblock")) + } + } + + /// Returns the number of block groups + /// + /// It is equal to the round up of the total number of blocks divided by the number of blocks per block group + const fn total_block_groups(&self) -> usize { + self.total_blocks.div_ceil(self.blocks_per_group) as usize + } +} + /// File System States /// /// See the [OSdev wiki](https://wiki.osdev.org/Ext2#File_System_States) for more informations @@ -296,3 +336,10 @@ pub struct BlockGroupDescriptor { /// Number of directories in group pub total_dir: u16, } + +impl BlockGroupDescriptor { + /// Returns the `n`th block group descriptor + pub fn new(device: &Arc<Mutex<dyn Device + Send>>, n: usize) -> Result<Self> { + unsafe { read::<Self>(device, BLOCK_GROUP_DESCRIPTOR_TABLE_START_BYTE + n * mem::size_of::<Self>()) } + } +} diff --git a/src/fs/ext2/directory.rs b/src/fs/ext2/directory.rs new file mode 100644 index 0000000..7e38856 --- /dev/null +++ b/src/fs/ext2/directory.rs @@ -0,0 +1,22 @@ +//! Implementation of directories in a ext2 filesystem +//! +//! See more informations on the [OSdev wiki](https://wiki.osdev.org/Ext2#Directories) + +/// Special inodes containing entries as contents +#[derive(Debug)] +pub struct Directory { + /// Inode + pub inode: u32, + + /// Total size of this entry (Including all subfields) + pub total_size: u16, + + /// Name Length least-significant 8 bits + pub name_length_least_bits: u8, + + /// Type indicator (only if the feature bit for "directory entries have file type byte" is set, else this is the + /// most-significant 8 bits of the Name Length) + pub type_indicator: u8, + + pub name: [u8], +} diff --git a/src/fs/ext2/mod.rs b/src/fs/ext2/mod.rs index 9f2c900..f2595ba 100644 --- a/src/fs/ext2/mod.rs +++ b/src/fs/ext2/mod.rs @@ -1,6 +1,52 @@ //! Implementation of the Second Extended Filesystem (ext2fs) filesystem +use alloc::slice; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::{mem, ptr}; + +use anyhow::Result; +use spin::Mutex; + +use self::block::{BlockGroupDescriptor, Superblock}; +use crate::kernel::device::storage::Device; +use crate::println; + #[allow(clippy::same_name_method)] mod block; +mod directory; #[allow(clippy::same_name_method)] mod inode; + +/// Reads the device at the given offset **in bytes** and returns the expected structure +/// +/// # Safety +/// +/// Must ensure that the structure `T` is present on the device at the given offset +unsafe fn read<T>(device: &Arc<Mutex<dyn Device + Send>>, offset: usize) -> Result<T> { + let mut binding = device.lock(); + let block_size = binding.block_size(); + + let mut buffer_size = 0_usize; + while buffer_size < mem::size_of::<T>() { + buffer_size += block_size; + } + + let (raw_buffer, _, _) = Vec::<u8>::with_capacity(buffer_size).into_raw_parts(); + + let buffer = slice::from_raw_parts_mut(raw_buffer, buffer_size); + + binding.read(buffer, offset / block_size, buffer_size / block_size)?; + + Ok(ptr::read::<T>((buffer.as_ptr().add(mem::size_of::<T>() * (offset % block_size))) as *const _)) +} + +/// Searches for an ext2 filesystem, and returns the root if found +pub fn search(device: &Arc<Mutex<dyn Device + Send>>) -> Result<Arc<Mutex<dyn super::node::Directory + Send>>> { + let superblock = Superblock::new(device); + println!("{:?}", superblock); + println!("{:?}", BlockGroupDescriptor::new(device, 0)); + println!("{:?}", BlockGroupDescriptor::new(device, 1)); + + todo!() +} diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 90153e5..45ebef9 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -2,7 +2,31 @@ //! //! Contains modules for the Virtual File System (vfs) and an ext2 implementations +use alloc::sync::Arc; + +use conquer_once::spin::OnceCell; +use log::{error, trace}; +use spin::Mutex; + +use self::node::Directory; +use crate::kernel::device::storage::Device; + mod ext2; pub mod node; pub mod path; pub mod vfs; + +/// Root of the filesystem +pub static ROOT: OnceCell<Arc<Mutex<dyn Directory + Send>>> = OnceCell::uninit(); + +/// Initializes the filesystem in the given storage device and return the root of this filesystem +/// +/// If no filesystem is found, an empty one will be created +pub fn init(device: Arc<Mutex<dyn Device + Send>>) { + trace!("Initialization of the device {:?}", device.lock().uuid()); + + match ext2::search(&device) { + Ok(root) => ROOT.init_once(|| root), + Err(err) => error!("{}", err), + } +} diff --git a/src/fs/node.rs b/src/fs/node.rs index 85d0386..3a5e8bf 100644 --- a/src/fs/node.rs +++ b/src/fs/node.rs @@ -10,7 +10,6 @@ use alloc::sync::Arc; use alloc::vec::Vec; use spin::Mutex; -use x86_64::structures::paging::page::PageRangeInclusive; use super::path::Path; use crate::kernel::device::block::Manipulation; @@ -145,8 +144,11 @@ pub trait Directory: Interface { /// General interface for files pub trait File: Interface + Manipulation { - /// Returns a memory region on which the file is mounted - fn as_pages(&self) -> anyhow::Result<PageRangeInclusive>; + /// Returns the raw bytes corresponding to the file + fn as_ref(&self) -> &[u8]; + + /// Returns the mutable raw bytes corresponding to the file + fn as_mut(&mut self) -> &mut [u8]; } /// General interface for symbolic links diff --git a/src/kernel/device/ata.rs b/src/kernel/device/ata.rs index 05606b7..a2e7191 100644 --- a/src/kernel/device/ata.rs +++ b/src/kernel/device/ata.rs @@ -614,7 +614,7 @@ impl Bus { /// # Safety /// /// Must ensure that `lba_start` and `sector` are well-defined - unsafe fn write_pio(&mut self, buffer: &mut [u8], role: DriveRole, lba_start: usize, sector_count: usize) -> Result<usize> { + unsafe fn write_pio(&mut self, buffer: &[u8], role: DriveRole, lba_start: usize, sector_count: usize) -> Result<usize> { if sector_count == 0 { return Ok(0); } @@ -627,7 +627,7 @@ impl Bus { for _ in lba_start..(lba_start + sector_count) { self.wait_ready()?; - for chunk in buffer.get_unchecked_mut(buffer_offset..(buffer_offset + BLOCK_SIZE)).chunks_exact(2) { + for chunk in buffer.get_unchecked(buffer_offset..(buffer_offset + BLOCK_SIZE)).chunks_exact(2) { let word = u16::from(*chunk.get_unchecked(1)) << 8_u8 | u16::from(*chunk.get_unchecked(0)); self.data.write(word); } @@ -1160,7 +1160,7 @@ impl Manipulation for Drive { self.read_pio(buffer, offset, length) } - fn write(&mut self, buffer: &mut [u8], offset: usize, length: usize) -> Result<usize> { + fn write(&mut self, buffer: &[u8], offset: usize, length: usize) -> Result<usize> { self.write_pio(buffer, offset, length) } @@ -1238,7 +1238,7 @@ impl Drive { /// # Safety /// /// Must ensure that the given buffer has a byte length of at least `length * BLOCK_SIZE` - fn write_pio(&mut self, buffer: &mut [u8], offset: usize, length: usize) -> Result<usize> { + fn write_pio(&mut self, buffer: &[u8], offset: usize, length: usize) -> Result<usize> { if offset > self.size_in_blocks() { error!("Write PIO: offset out of bounds"); return Err(anyhow::Error::msg("Write PIO: Offset out of bounds")); @@ -1346,7 +1346,11 @@ impl IdeController { info!( "Found an ATA controller at {:?} containing :\n * Primary master: {:?}\n * Primary slave: {:?}\n * Secondary master: {:?}\n * Secondary slave: {:?}", - device.location, primary_master, primary_slave, secondary_master, secondary_slave + device.location, + primary_master.as_ref().map(|_| "Ok"), + primary_slave.as_ref().map(|_| "Ok"), + secondary_master.as_ref().map(|_| "Ok"), + secondary_slave.as_ref().map(|_| "Ok") ); let to_ref = |drive| Arc::new(Mutex::new(drive)); diff --git a/src/kernel/device/block.rs b/src/kernel/device/block.rs index b1a8fb8..ae7a7ad 100644 --- a/src/kernel/device/block.rs +++ b/src/kernel/device/block.rs @@ -1,6 +1,6 @@ //! Structures and traits for block manipulation //! -//! See the [OSdev wiki](https://wiki.osdev.org/Ext2#What_is_a_Block.3F) for more informations +//! Blocks are stream of bytes of fixed length use anyhow::Result; @@ -20,7 +20,7 @@ pub trait Manipulation { /// Writes `length` blocks of data from the given `buffer` into I/O stream starting at the given `offset` (in **blocks**) /// /// Returns the number of blocks written - fn write(&mut self, buffer: &mut [u8], offset: usize, length: usize) -> Result<usize>; + fn write(&mut self, buffer: &[u8], offset: usize, length: usize) -> Result<usize>; /// Flushes the output stream, ensuring all contents in intermediate buffers are fully written out fn flush(&mut self) -> Result<()>; diff --git a/src/kernel/device/mod.rs b/src/kernel/device/mod.rs index 082fb9e..4a0ac2e 100644 --- a/src/kernel/device/mod.rs +++ b/src/kernel/device/mod.rs @@ -20,6 +20,10 @@ pub mod storage; /// List of references towards all initialized storage controllers pub static STORAGE_CONTROLLERS: OnceCell<Vec<Arc<Mutex<dyn storage::Controller + Send>>>> = OnceCell::uninit(); +/// UUID of the UEFI file which should not be considered as an available storage device +pub const UEFI_UUID: Uuid = + [0x4D, 0x51, 0x30, 0x30, 0x30, 0x30, 0x20, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20]; + /// Initializes the devices /// /// Currently, is is only used for storage devices. diff --git a/src/kernel/interrupt/cmos.rs b/src/kernel/interrupt/cmos.rs index 54276ed..96fe640 100644 --- a/src/kernel/interrupt/cmos.rs +++ b/src/kernel/interrupt/cmos.rs @@ -48,6 +48,7 @@ impl Display for Rtc { /// See the [OSdev wiki](https://wiki.osdev.org/CMOS#Getting_Current_Date_and_Time_from_RTC) for more details #[repr(u8)] #[derive(Debug, Clone)] +#[allow(unused)] enum Register { /// Second register Second = 0x00, @@ -213,6 +214,7 @@ impl Cmos { } /// Waits until the CMOS is updated + #[allow(unused)] fn wait_while_updating(&mut self) { while self.is_updating() { spin_loop(); @@ -220,6 +222,7 @@ impl Cmos { } /// Returns the current Real-Time Clock + #[allow(unused)] pub fn rtc(&mut self) -> Rtc { /// Believe in the docs const SRB_BCD_MODE: u8 = 0x04; diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index 6884d46..a45e7f7 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -16,9 +16,7 @@ use x86_64::VirtAddr; use self::memory::paging::{PhysFrameAllocator, MAPPER}; use self::memory::vmm::VIRTUAL_MEMORY_MANAGER; -use crate::kernel::interrupt::cmos::CMOS; use crate::kernel::screen::buffer::Buffer; -use crate::println; /// Initializes the kernel pub fn init(boot_info: &'static mut BootInfo) { @@ -72,7 +70,4 @@ pub fn init(boot_info: &'static mut BootInfo) { device::init(); info!("Devices initialized"); - - println!("Hello world!"); - println!("It is currently {}.", CMOS.rtc()); } diff --git a/src/main.rs b/src/main.rs index ab39a49..861475d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,9 +59,11 @@ #![feature(custom_test_frameworks)] #![feature(error_in_core)] #![feature(extend_one)] +#![feature(int_roundings)] #![feature(no_coverage)] #![feature(strict_provenance)] #![feature(trait_upcasting)] +#![feature(vec_into_raw_parts)] #![reexport_test_harness_main = "test_main"] #![test_runner(test::runner)] @@ -78,13 +80,18 @@ mod user; #[cfg(test)] mod test; +use alloc::sync::Arc; +use alloc::vec::Vec; use core::alloc::Layout; use bootloader_api::config::{Mapping, Mappings}; use bootloader_api::{entry_point, BootInfo, BootloaderConfig}; +use kernel::device::storage::Device; +use kernel::device::{STORAGE_CONTROLLERS, UEFI_UUID}; use kernel::task::executor::Executor; use kernel::task::Task; use log::{logger, set_logger, set_max_level, LevelFilter}; +use spin::Mutex; #[cfg(test)] use test::exit; use user::tty::print_key; @@ -112,6 +119,21 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { kernel::init(boot_info); + let storage_devices = STORAGE_CONTROLLERS + .get() + .expect("Storage controllers have not been initialized yet") + .iter() + .map(|controller| controller.lock().devices()) + .flatten() + .collect::<Vec<Arc<Mutex<dyn Device + Send>>>>() + .into_iter() + .filter(|device| if device.lock().uuid() == UEFI_UUID { false } else { true }) + .collect::<Vec<Arc<Mutex<dyn Device + Send>>>>(); + + if let Some(device) = storage_devices.first() { + fs::init(Arc::clone(device)); + } + #[cfg(test)] { test_main(); -- GitLab