diff --git a/Cargo.toml b/Cargo.toml index 80c0a74..a2e8d17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,8 @@ members = ["boot"] [dependencies] +[build-dependencies] + [profile.dev] panic = "abort" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..b7a0533 --- /dev/null +++ b/build.rs @@ -0,0 +1,112 @@ +use std::{env, path::PathBuf, process::Command}; + +fn main() { + let target = env::var("TARGET").unwrap(); + if target != "x86_64-unknown-uefi" { + return; + } + + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let lwext4_dir = manifest_dir.join("third_party/lwext4"); + let config_dir = manifest_dir.join("third_party/lwext4_config/include/generated"); + let shim = manifest_dir.join("src/fs/lwext4_shim.c"); + + println!("cargo:rerun-if-changed={}", shim.display()); + println!( + "cargo:rerun-if-changed={}", + lwext4_dir.join("src").display() + ); + println!( + "cargo:rerun-if-changed={}", + manifest_dir + .join("third_party/lwext4_config/include/generated/ext4_config.h") + .display() + ); + + let sources = [ + "ext4.c", + "ext4_balloc.c", + "ext4_bitmap.c", + "ext4_bcache.c", + "ext4_block_group.c", + "ext4_blockdev.c", + "ext4_crc32.c", + "ext4_debug.c", + "ext4_dir.c", + "ext4_dir_idx.c", + "ext4_extent.c", + "ext4_fs.c", + "ext4_hash.c", + "ext4_ialloc.c", + "ext4_inode.c", + "ext4_journal.c", + "ext4_mbr.c", + "ext4_super.c", + "ext4_trans.c", + "ext4_xattr.c", + ]; + + let clang = env::var("CLANG").unwrap_or_else(|_| "clang".to_string()); + let ar = env::var("AR").unwrap_or_else(|_| "llvm-ar".to_string()); + let mut objects = Vec::new(); + + for source in sources { + let input = lwext4_dir.join("src").join(source); + let output = out_dir.join(format!("{source}.obj")); + compile_c(&clang, &input, &output, &lwext4_dir, &config_dir); + objects.push(output); + } + + let shim_obj = out_dir.join("zeroos_lwext4_shim.obj"); + compile_c(&clang, &shim, &shim_obj, &lwext4_dir, &config_dir); + objects.push(shim_obj); + + let lib = out_dir.join("libzeroos_lwext4.a"); + let status = Command::new(&ar) + .arg("crs") + .arg(&lib) + .args(&objects) + .status() + .unwrap_or_else(|error| panic!("failed to run {ar}: {error}")); + assert!(status.success(), "{ar} failed while archiving lwext4"); + + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!("cargo:rustc-link-lib=static=zeroos_lwext4"); +} + +fn compile_c( + clang: &str, + input: &PathBuf, + output: &PathBuf, + lwext4_dir: &PathBuf, + config_dir: &PathBuf, +) { + let status = Command::new(clang) + .arg("-target") + .arg("x86_64-unknown-windows") + .arg("-ffreestanding") + .arg("-fno-stack-protector") + .arg("-fno-builtin") + .arg("-mno-red-zone") + .arg("-O2") + .arg("-Wall") + .arg("-Wno-unused-parameter") + .arg("-Wno-missing-braces") + .arg("-DCONFIG_USE_DEFAULT_CFG=0") + .arg("-I") + .arg(lwext4_dir.join("include")) + .arg("-I") + .arg(config_dir.parent().unwrap()) + .arg("-c") + .arg(input) + .arg("-o") + .arg(output) + .status() + .unwrap_or_else(|error| panic!("failed to run {clang}: {error}")); + assert!( + status.success(), + "{clang} failed while compiling {}", + input.display() + ); +} diff --git a/src/fs/ext4.rs b/src/fs/ext4.rs index 00f1811..bef379d 100644 --- a/src/fs/ext4.rs +++ b/src/fs/ext4.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use crate::drivers::storage::{ ahci::AhciDisk, block::{BlockDevice, BlockError}, diff --git a/src/fs/lwext4.rs b/src/fs/lwext4.rs new file mode 100644 index 0000000..39e5a22 --- /dev/null +++ b/src/fs/lwext4.rs @@ -0,0 +1,706 @@ +use crate::drivers::storage::{ + ahci::AhciDisk, + block::{BlockDevice, BlockError}, + partition::PartitionBlockDevice, +}; +use crate::fs::uefi::LoadedFile; +use crate::memory::{PAGE_SIZE, PhysicalMemoryManager}; +use core::ffi::c_void; + +const EIO: isize = 5; +const ENOENT: isize = 2; +const ENODEV: isize = 19; +const EFBIG: isize = 27; +const EXT4_DE_REG_FILE: u8 = 1; +const EXT4_DE_DIR: u8 = 2; +const MAX_PATH: usize = 256; +const CACHE_SLOTS: usize = 64; +const EXEC_CACHE_SLOTS: usize = 8; + +static mut ROOT_DEVICE: Option> = None; +static mut MOUNTED: bool = false; +static mut MEMORY_MANAGER: *mut PhysicalMemoryManager = core::ptr::null_mut(); +static mut CACHE: [CacheEntry; CACHE_SLOTS] = [CacheEntry::empty(); CACHE_SLOTS]; +static mut CACHE_CLOCK: u64 = 0; +static mut EXEC_CACHE: [ExecCacheEntry; EXEC_CACHE_SLOTS] = + [ExecCacheEntry::empty(); EXEC_CACHE_SLOTS]; + +unsafe extern "C" { + fn zeroos_lwext4_mount(block_count: u64) -> i32; + fn zeroos_lwext4_read( + path: *const u8, + offset: u64, + buf: *mut u8, + len: usize, + done: *mut usize, + ) -> i32; + fn zeroos_lwext4_write( + path: *const u8, + offset: u64, + buf: *const u8, + len: usize, + done: *mut usize, + ) -> i32; + fn zeroos_lwext4_truncate(path: *const u8, size: u64) -> i32; + fn zeroos_lwext4_size(path: *const u8, size: *mut u64) -> i32; + fn zeroos_lwext4_create_file(path: *const u8) -> i32; + fn zeroos_lwext4_mkdir(path: *const u8) -> i32; + fn ext4_dir_open(dir: *mut Ext4Dir, path: *const u8) -> i32; + fn ext4_dir_close(dir: *mut Ext4Dir) -> i32; + fn ext4_dir_entry_next(dir: *mut Ext4Dir) -> *const Ext4DirEntry; +} + +#[repr(C)] +struct Ext4File { + mp: *mut c_void, + inode: u32, + flags: u32, + fsize: u64, + fpos: u64, +} + +#[repr(C)] +struct Ext4DirEntry { + inode: u32, + entry_length: u16, + name_length: u8, + inode_type: u8, + name: [u8; 255], +} + +#[repr(C)] +struct Ext4Dir { + f: Ext4File, + de: Ext4DirEntry, + next_off: u64, +} + +#[derive(Clone, Copy)] +struct CacheEntry { + valid: bool, + dirty: bool, + stamp: u64, + page_index: u64, + path: [u8; MAX_PATH], + data: [u8; PAGE_SIZE], +} + +impl CacheEntry { + const fn empty() -> Self { + Self { + valid: false, + dirty: false, + stamp: 0, + page_index: 0, + path: [0; MAX_PATH], + data: [0; PAGE_SIZE], + } + } +} + +#[derive(Clone, Copy)] +struct ExecCacheEntry { + valid: bool, + hits: u64, + stamp: u64, + path: [u8; MAX_PATH], + ptr: *const u8, + len: usize, + pages: usize, +} + +impl ExecCacheEntry { + const fn empty() -> Self { + Self { + valid: false, + hits: 0, + stamp: 0, + path: [0; MAX_PATH], + ptr: core::ptr::null(), + len: 0, + pages: 0, + } + } +} + +pub unsafe fn mount_root( + device: PartitionBlockDevice, + sector_count: u64, +) -> Result<(), isize> { + unsafe { + ROOT_DEVICE = Some(device); + let rc = zeroos_lwext4_mount(sector_count); + if rc == 0 { + MOUNTED = true; + Ok(()) + } else { + ROOT_DEVICE = None; + Err(map_error(rc)) + } + } +} + +pub fn is_mounted() -> bool { + unsafe { MOUNTED } +} + +pub fn init_cache(memory: *mut PhysicalMemoryManager) { + unsafe { + MEMORY_MANAGER = memory; + } +} + +pub fn read_path_at( + path: &[u8], + offset: usize, + buffer: *mut u8, + len: usize, +) -> Result { + if !is_mounted() { + return Err(-ENODEV); + } + if buffer.is_null() { + return Err(-EIO); + } + let path = c_path(path)?; + cache_read(&path, offset, buffer, len) +} + +pub fn write_path_at( + path: &[u8], + offset: usize, + buffer: *const u8, + len: usize, +) -> Result { + if !is_mounted() { + return Err(-ENODEV); + } + if buffer.is_null() { + return Err(-EIO); + } + let Some(end) = offset.checked_add(len) else { + return Err(-EFBIG); + }; + if end as u64 > u64::MAX { + return Err(-EFBIG); + } + let path = c_path(path)?; + invalidate_exec_cache(&path); + cache_write(&path, offset, buffer, len) +} + +pub fn truncate_path(path: &[u8], size: usize) -> Result<(), isize> { + if !is_mounted() { + return Err(-ENODEV); + } + let path = c_path(path)?; + flush_path_cache(&path)?; + invalidate_exec_cache(&path); + let rc = unsafe { zeroos_lwext4_truncate(path.as_ptr(), size as u64) }; + if rc == 0 { + invalidate_path_cache(&path); + Ok(()) + } else { + Err(map_error(rc)) + } +} + +pub fn size_path(path: &[u8]) -> Result { + if !is_mounted() { + return Err(-ENODEV); + } + let path = c_path(path)?; + let mut size = 0u64; + let rc = unsafe { zeroos_lwext4_size(path.as_ptr(), &mut size) }; + if rc == 0 { + usize::try_from(size).map_err(|_| -EFBIG) + } else { + Err(map_error(rc)) + } +} + +pub fn load_path(memory: &mut PhysicalMemoryManager, path: &[u8]) -> Result { + let path = c_path(path)?; + if let Some(image) = exec_cache_get(&path) { + return Ok(image); + } + + let len = size_path(&path)?; + let pages = len.div_ceil(PAGE_SIZE).max(1); + let Some(buffer) = memory.alloc_pages(pages) else { + return Err(-EIO); + }; + let slice = unsafe { core::slice::from_raw_parts_mut(buffer, pages * PAGE_SIZE) }; + slice[..len].fill(0); + match cache_read(&path, 0, slice.as_mut_ptr(), len) { + Ok(_) => { + let image = LoadedFile { + ptr: buffer as *const u8, + len, + }; + exec_cache_insert(&path, image.ptr, image.len, pages); + Ok(image) + } + Err(error) => { + unsafe { + memory.free_pages(buffer, pages); + } + Err(error) + } + } +} + +pub fn create_file(path: &[u8]) -> Result<(), isize> { + if !is_mounted() { + return Err(-ENODEV); + } + let path = c_path(path)?; + flush_path_cache(&path)?; + invalidate_exec_cache(&path); + let rc = unsafe { zeroos_lwext4_create_file(path.as_ptr()) }; + if rc == 0 { + invalidate_path_cache(&path); + Ok(()) + } else { + Err(map_error(rc)) + } +} + +pub fn create_dir(path: &[u8]) -> Result<(), isize> { + if !is_mounted() { + return Err(-ENODEV); + } + let path = c_path(path)?; + let rc = unsafe { zeroos_lwext4_mkdir(path.as_ptr()) }; + if rc == 0 { Ok(()) } else { Err(map_error(rc)) } +} + +pub fn mount_vfs_tree() -> Result<(), isize> { + let mut root = [0u8; MAX_PATH]; + root[0] = b'/'; + mount_vfs_dir(&root, 0) +} + +pub fn flush_cache() -> Result<(), isize> { + for index in 0..CACHE_SLOTS { + flush_entry(index)?; + } + Ok(()) +} + +#[unsafe(no_mangle)] +pub extern "C" fn zeroos_lwext4_read_blocks( + buffer: *mut u8, + block_id: u64, + block_count: u32, +) -> i32 { + if buffer.is_null() { + return EIO as i32; + } + let byte_count = block_count as usize * 512; + let offset = block_id.saturating_mul(512); + unsafe { + let Some(mut device) = ROOT_DEVICE else { + return ENODEV as i32; + }; + let slice = core::slice::from_raw_parts_mut(buffer, byte_count); + let result = device.read_at(offset, slice); + ROOT_DEVICE = Some(device); + block_result(result) + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn zeroos_lwext4_write_blocks( + buffer: *const u8, + block_id: u64, + block_count: u32, +) -> i32 { + if buffer.is_null() { + return EIO as i32; + } + let byte_count = block_count as usize * 512; + let offset = block_id.saturating_mul(512); + unsafe { + let Some(mut device) = ROOT_DEVICE else { + return ENODEV as i32; + }; + let slice = core::slice::from_raw_parts(buffer, byte_count); + let result = device.write_at(offset, slice); + ROOT_DEVICE = Some(device); + block_result(result) + } +} + +fn block_result(result: Result<(), BlockError>) -> i32 { + match result { + Ok(()) => 0, + Err(_) => EIO as i32, + } +} + +fn cache_read( + path: &[u8; MAX_PATH], + offset: usize, + buffer: *mut u8, + len: usize, +) -> Result { + if len == 0 { + return Ok(0); + } + let mut copied = 0usize; + while copied < len { + let absolute = offset + copied; + let page_index = (absolute / PAGE_SIZE) as u64; + let page_offset = absolute % PAGE_SIZE; + let chunk = (PAGE_SIZE - page_offset).min(len - copied); + let slot = cache_get_page_slot(path, page_index, true)?; + unsafe { + core::ptr::copy_nonoverlapping( + CACHE[slot].data.as_ptr().add(page_offset), + buffer.add(copied), + chunk, + ); + } + copied += chunk; + } + Ok(copied) +} + +fn cache_write( + path: &[u8; MAX_PATH], + offset: usize, + buffer: *const u8, + len: usize, +) -> Result { + if len == 0 { + return Ok(0); + } + let mut written = 0usize; + while written < len { + let absolute = offset + written; + let page_index = (absolute / PAGE_SIZE) as u64; + let page_offset = absolute % PAGE_SIZE; + let chunk = (PAGE_SIZE - page_offset).min(len - written); + let slot = cache_get_page_slot(path, page_index, true)?; + unsafe { + core::ptr::copy_nonoverlapping( + buffer.add(written), + CACHE[slot].data.as_mut_ptr().add(page_offset), + chunk, + ); + CACHE[slot].dirty = true; + CACHE[slot].stamp = next_stamp(); + } + written += chunk; + } + Ok(written) +} + +fn cache_get_page_slot(path: &[u8; MAX_PATH], page_index: u64, load: bool) -> Result { + unsafe { + let mut free_slot = None; + let mut lru_slot = 0usize; + let mut lru_stamp = u64::MAX; + + for index in 0..CACHE_SLOTS { + let entry = &mut CACHE[index]; + if entry.valid { + if entry.page_index == page_index && entry.path == *path { + entry.stamp = next_stamp(); + return Ok(index); + } + if entry.stamp < lru_stamp { + lru_stamp = entry.stamp; + lru_slot = index; + } + } else if free_slot.is_none() { + free_slot = Some(index); + } + } + + let slot = free_slot.unwrap_or(lru_slot); + if CACHE[slot].valid && CACHE[slot].dirty { + flush_entry(slot)?; + } + let entry = &mut CACHE[slot]; + entry.valid = true; + entry.dirty = false; + entry.stamp = next_stamp(); + entry.page_index = page_index; + entry.path = *path; + entry.data.fill(0); + + if load { + load_page(slot, path, page_index)?; + } + + Ok(slot) + } +} + +fn load_page(slot: usize, path: &[u8; MAX_PATH], page_index: u64) -> Result<(), isize> { + let mut done = 0usize; + let offset = page_index as usize * PAGE_SIZE; + let rc = unsafe { + zeroos_lwext4_read( + path.as_ptr(), + offset as u64, + CACHE[slot].data.as_mut_ptr(), + PAGE_SIZE, + &mut done, + ) + }; + if rc == 0 { + if done < PAGE_SIZE { + unsafe { + CACHE[slot].data[done..].fill(0); + } + } + Ok(()) + } else { + Err(map_error(rc)) + } +} + +fn flush_path_cache(path: &[u8; MAX_PATH]) -> Result<(), isize> { + unsafe { + for index in 0..CACHE_SLOTS { + if CACHE[index].valid && CACHE[index].path == *path && CACHE[index].dirty { + flush_entry(index)?; + } + } + } + Ok(()) +} + +fn invalidate_path_cache(path: &[u8; MAX_PATH]) { + unsafe { + for index in 0..CACHE_SLOTS { + if CACHE[index].valid && CACHE[index].path == *path { + CACHE[index] = CacheEntry::empty(); + } + } + } +} + +fn flush_entry(index: usize) -> Result<(), isize> { + unsafe { + let entry = &mut CACHE[index]; + if !entry.valid || !entry.dirty { + return Ok(()); + } + let mut done = 0usize; + let rc = zeroos_lwext4_write( + entry.path.as_ptr(), + entry.page_index * PAGE_SIZE as u64, + entry.data.as_ptr(), + PAGE_SIZE, + &mut done, + ); + if rc == 0 { + entry.dirty = false; + Ok(()) + } else { + Err(map_error(rc)) + } + } +} + +fn exec_cache_get(path: &[u8; MAX_PATH]) -> Option { + unsafe { + for index in 0..EXEC_CACHE_SLOTS { + let entry = &mut *core::ptr::addr_of_mut!(EXEC_CACHE[index]); + if entry.valid && entry.path == *path { + entry.hits = entry.hits.saturating_add(1); + entry.stamp = next_stamp(); + return Some(LoadedFile { + ptr: entry.ptr, + len: entry.len, + }); + } + } + } + None +} + +fn exec_cache_insert(path: &[u8; MAX_PATH], ptr: *const u8, len: usize, pages: usize) { + unsafe { + let mut free_slot = None; + let mut lru_slot = 0usize; + let mut lru_score = u64::MAX; + for index in 0..EXEC_CACHE_SLOTS { + let entry = EXEC_CACHE[index]; + if !entry.valid { + free_slot = Some(index); + break; + } + let score = entry.stamp.saturating_sub(entry.hits.min(64)); + if score < lru_score { + lru_score = score; + lru_slot = index; + } + } + let slot = free_slot.unwrap_or(lru_slot); + if EXEC_CACHE[slot].valid { + free_exec_entry(slot); + } + let entry = &mut EXEC_CACHE[slot]; + entry.valid = true; + entry.hits = 1; + entry.stamp = next_stamp(); + entry.path = *path; + entry.ptr = ptr; + entry.len = len; + entry.pages = pages; + } +} + +fn invalidate_exec_cache(path: &[u8; MAX_PATH]) { + unsafe { + for index in 0..EXEC_CACHE_SLOTS { + if EXEC_CACHE[index].valid && EXEC_CACHE[index].path == *path { + free_exec_entry(index); + } + } + } +} + +fn free_exec_entry(index: usize) { + unsafe { + let entry = &mut *core::ptr::addr_of_mut!(EXEC_CACHE[index]); + if entry.valid && !entry.ptr.is_null() && !MEMORY_MANAGER.is_null() && entry.pages > 0 { + (*MEMORY_MANAGER).free_pages(entry.ptr as *mut u8, entry.pages); + } + EXEC_CACHE[index] = ExecCacheEntry::empty(); + } +} + +fn next_stamp() -> u64 { + unsafe { + CACHE_CLOCK = CACHE_CLOCK.wrapping_add(1); + CACHE_CLOCK + } +} + +fn map_error(error: i32) -> isize { + if error == 0 { + return 0; + } + let errno = error.unsigned_abs() as isize; + match errno { + 2 => -ENOENT, + 5 => -EIO, + 19 => -ENODEV, + 27 => -EFBIG, + _ => -EIO, + } +} + +fn mount_vfs_dir(path: &[u8], depth: usize) -> Result<(), isize> { + if depth > 16 { + return Ok(()); + } + let mut dir = Ext4Dir { + f: Ext4File { + mp: core::ptr::null_mut(), + inode: 0, + flags: 0, + fsize: 0, + fpos: 0, + }, + de: Ext4DirEntry { + inode: 0, + entry_length: 0, + name_length: 0, + inode_type: 0, + name: [0; 255], + }, + next_off: 0, + }; + let c_path = c_path(path)?; + let rc = unsafe { ext4_dir_open(&mut dir, c_path.as_ptr()) }; + if rc != 0 { + return Err(map_error(rc)); + } + + loop { + let entry = unsafe { ext4_dir_entry_next(&mut dir) }; + if entry.is_null() { + break; + } + let entry = unsafe { &*entry }; + let name_len = entry.name_length as usize; + if name_len == 0 || name_len > entry.name.len() { + continue; + } + let name = &entry.name[..name_len]; + if name == b"." || name == b".." { + continue; + } + let mut child_path = [0u8; MAX_PATH]; + if !join_path(path, name, &mut child_path) { + continue; + } + + match entry.inode_type { + EXT4_DE_DIR => { + let _ = crate::fs::vfs::mount_ext4_node( + &child_path, + crate::fs::vfs::NodeKind::Directory, + ); + let _ = mount_vfs_dir(&child_path, depth + 1); + } + EXT4_DE_REG_FILE => { + let _ = + crate::fs::vfs::mount_ext4_node(&child_path, crate::fs::vfs::NodeKind::Regular); + } + _ => {} + } + } + + unsafe { + let _ = ext4_dir_close(&mut dir); + } + Ok(()) +} + +fn c_path(path: &[u8]) -> Result<[u8; MAX_PATH], isize> { + let len = nul_len(path); + if len == 0 || len >= MAX_PATH { + return Err(-ENOENT); + } + let mut out = [0u8; MAX_PATH]; + out[..len].copy_from_slice(&path[..len]); + Ok(out) +} + +fn join_path(parent: &[u8], name: &[u8], out: &mut [u8; MAX_PATH]) -> bool { + let parent_len = nul_len(parent); + if parent_len == 0 || name.is_empty() { + return false; + } + let mut pos = 0; + while pos < parent_len && pos < MAX_PATH - 1 { + out[pos] = parent[pos]; + pos += 1; + } + if !(pos == 1 && out[0] == b'/') { + if pos >= MAX_PATH - 1 { + return false; + } + out[pos] = b'/'; + pos += 1; + } + if pos + name.len() >= MAX_PATH { + return false; + } + out[pos..pos + name.len()].copy_from_slice(name); + true +} + +fn nul_len(path: &[u8]) -> usize { + path.iter() + .position(|byte| *byte == 0) + .unwrap_or(path.len()) +} diff --git a/src/fs/lwext4_shim.c b/src/fs/lwext4_shim.c new file mode 100644 index 0000000..2c6d846 --- /dev/null +++ b/src/fs/lwext4_shim.c @@ -0,0 +1,329 @@ +#include +#include +#include +#include +#include +#include + +extern int zeroos_lwext4_read_blocks(void *buf, uint64_t blk_id, uint32_t blk_cnt); +extern int zeroos_lwext4_write_blocks(const void *buf, uint64_t blk_id, uint32_t blk_cnt); + +#define ZEROOS_LWEXT4_HEAP_SIZE (4u * 1024u * 1024u) +#define ZEROOS_LWEXT4_ALIGN 16u + +struct zeroos_alloc_header { + size_t size; + int free; + struct zeroos_alloc_header *next; +}; + +static uint8_t zeroos_lwext4_heap[ZEROOS_LWEXT4_HEAP_SIZE]; +static struct zeroos_alloc_header *zeroos_heap_head; +static uint8_t zeroos_bbuf[512]; +static struct ext4_blockdev zeroos_bdev; +static struct ext4_blockdev_iface zeroos_bdev_iface; +static int zeroos_mounted; + +static size_t zeroos_align_up(size_t value) { + return (value + ZEROOS_LWEXT4_ALIGN - 1u) & ~(ZEROOS_LWEXT4_ALIGN - 1u); +} + +static void zeroos_heap_init(void) { + if (zeroos_heap_head) { + return; + } + zeroos_heap_head = (struct zeroos_alloc_header *)zeroos_lwext4_heap; + zeroos_heap_head->size = ZEROOS_LWEXT4_HEAP_SIZE - sizeof(struct zeroos_alloc_header); + zeroos_heap_head->free = 1; + zeroos_heap_head->next = 0; +} + +void *ext4_user_malloc(size_t size) { + zeroos_heap_init(); + size = zeroos_align_up(size); + struct zeroos_alloc_header *node = zeroos_heap_head; + while (node) { + if (node->free && node->size >= size) { + size_t remaining = node->size - size; + if (remaining > sizeof(struct zeroos_alloc_header) + ZEROOS_LWEXT4_ALIGN) { + struct zeroos_alloc_header *next = + (struct zeroos_alloc_header *)((uint8_t *)(node + 1) + size); + next->size = remaining - sizeof(struct zeroos_alloc_header); + next->free = 1; + next->next = node->next; + node->next = next; + node->size = size; + } + node->free = 0; + return node + 1; + } + node = node->next; + } + return 0; +} + +void *ext4_user_calloc(size_t count, size_t size) { + size_t total = count * size; + void *ptr = ext4_user_malloc(total); + if (ptr) { + memset(ptr, 0, total); + } + return ptr; +} + +void ext4_user_free(void *ptr) { + if (!ptr) { + return; + } + struct zeroos_alloc_header *node = ((struct zeroos_alloc_header *)ptr) - 1; + node->free = 1; + while (node->next && node->next->free) { + node->size += sizeof(struct zeroos_alloc_header) + node->next->size; + node->next = node->next->next; + } +} + +void *memcpy(void *dest, const void *src, size_t n) { + uint8_t *d = (uint8_t *)dest; + const uint8_t *s = (const uint8_t *)src; + for (size_t i = 0; i < n; i++) { + d[i] = s[i]; + } + return dest; +} + +void *memmove(void *dest, const void *src, size_t n) { + uint8_t *d = (uint8_t *)dest; + const uint8_t *s = (const uint8_t *)src; + if (d < s) { + for (size_t i = 0; i < n; i++) { + d[i] = s[i]; + } + } else { + while (n > 0) { + n--; + d[n] = s[n]; + } + } + return dest; +} + +void *memset(void *dest, int c, size_t n) { + uint8_t *d = (uint8_t *)dest; + for (size_t i = 0; i < n; i++) { + d[i] = (uint8_t)c; + } + return dest; +} + +int memcmp(const void *a, const void *b, size_t n) { + const uint8_t *pa = (const uint8_t *)a; + const uint8_t *pb = (const uint8_t *)b; + for (size_t i = 0; i < n; i++) { + if (pa[i] != pb[i]) { + return (int)pa[i] - (int)pb[i]; + } + } + return 0; +} + +size_t strlen(const char *s) { + size_t len = 0; + while (s[len]) { + len++; + } + return len; +} + +int strcmp(const char *a, const char *b) { + while (*a && *a == *b) { + a++; + b++; + } + return (int)(uint8_t)*a - (int)(uint8_t)*b; +} + +int strncmp(const char *a, const char *b, size_t n) { + for (size_t i = 0; i < n; i++) { + if (a[i] != b[i] || a[i] == 0 || b[i] == 0) { + return (int)(uint8_t)a[i] - (int)(uint8_t)b[i]; + } + } + return 0; +} + +char *strcpy(char *dest, const char *src) { + char *out = dest; + while ((*dest++ = *src++)) { + } + return out; +} + +void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) { + uint8_t *items = (uint8_t *)base; + uint8_t *tmp = (uint8_t *)ext4_user_malloc(size); + if (!tmp) { + return; + } + for (size_t i = 1; i < nmemb; i++) { + memcpy(tmp, items + i * size, size); + size_t j = i; + while (j > 0 && compar(items + (j - 1) * size, tmp) > 0) { + memmove(items + j * size, items + (j - 1) * size, size); + j--; + } + memcpy(items + j * size, tmp, size); + } + ext4_user_free(tmp); +} + +static int zeroos_bdev_open(struct ext4_blockdev *bdev) { + (void)bdev; + return EOK; +} + +static int zeroos_bdev_close(struct ext4_blockdev *bdev) { + (void)bdev; + return EOK; +} + +static int zeroos_bdev_lock(struct ext4_blockdev *bdev) { + (void)bdev; + return EOK; +} + +static int zeroos_bdev_unlock(struct ext4_blockdev *bdev) { + (void)bdev; + return EOK; +} + +static int zeroos_bdev_bread( + struct ext4_blockdev *bdev, + void *buf, + uint64_t blk_id, + uint32_t blk_cnt +) { + (void)bdev; + return zeroos_lwext4_read_blocks(buf, blk_id, blk_cnt); +} + +static int zeroos_bdev_bwrite( + struct ext4_blockdev *bdev, + const void *buf, + uint64_t blk_id, + uint32_t blk_cnt +) { + (void)bdev; + return zeroos_lwext4_write_blocks(buf, blk_id, blk_cnt); +} + +int zeroos_lwext4_mount(uint64_t block_count) { + if (zeroos_mounted) { + return EOK; + } + + zeroos_bdev_iface.open = zeroos_bdev_open; + zeroos_bdev_iface.bread = zeroos_bdev_bread; + zeroos_bdev_iface.bwrite = zeroos_bdev_bwrite; + zeroos_bdev_iface.close = zeroos_bdev_close; + zeroos_bdev_iface.lock = zeroos_bdev_lock; + zeroos_bdev_iface.unlock = zeroos_bdev_unlock; + zeroos_bdev_iface.ph_bsize = 512; + zeroos_bdev_iface.ph_bcnt = block_count; + zeroos_bdev_iface.ph_bbuf = zeroos_bbuf; + zeroos_bdev_iface.ph_refctr = 0; + zeroos_bdev_iface.bread_ctr = 0; + zeroos_bdev_iface.bwrite_ctr = 0; + zeroos_bdev_iface.p_user = 0; + + memset(&zeroos_bdev, 0, sizeof(zeroos_bdev)); + zeroos_bdev.bdif = &zeroos_bdev_iface; + zeroos_bdev.part_offset = 0; + zeroos_bdev.part_size = block_count * 512u; + + int rc = ext4_device_register(&zeroos_bdev, "zeroos"); + if (rc != EOK) { + return rc; + } + rc = ext4_mount("zeroos", "/", false); + if (rc != EOK) { + return rc; + } + ext4_cache_write_back("/", false); + zeroos_mounted = 1; + return EOK; +} + +int zeroos_lwext4_read(const char *path, uint64_t offset, void *buf, size_t len, size_t *done) { + ext4_file file; + *done = 0; + int rc = ext4_fopen2(&file, path, O_RDONLY); + if (rc != EOK) { + return rc; + } + rc = ext4_fseek(&file, (int64_t)offset, SEEK_SET); + if (rc == EOK) { + rc = ext4_fread(&file, buf, len, done); + } + ext4_fclose(&file); + return rc; +} + +int zeroos_lwext4_write(const char *path, uint64_t offset, const void *buf, size_t len, size_t *done) { + ext4_file file; + *done = 0; + int rc = ext4_fopen2(&file, path, O_RDWR | O_CREAT); + if (rc != EOK) { + return rc; + } + rc = ext4_fseek(&file, (int64_t)offset, SEEK_SET); + if (rc == EOK) { + rc = ext4_fwrite(&file, buf, len, done); + } + ext4_fclose(&file); + ext4_cache_flush("/"); + return rc; +} + +int zeroos_lwext4_truncate(const char *path, uint64_t size) { + ext4_file file; + int rc = ext4_fopen2(&file, path, O_RDWR | O_CREAT); + if (rc != EOK) { + return rc; + } + rc = ext4_ftruncate(&file, size); + ext4_fclose(&file); + ext4_cache_flush("/"); + return rc; +} + +int zeroos_lwext4_size(const char *path, uint64_t *size) { + ext4_file file; + *size = 0; + int rc = ext4_fopen2(&file, path, O_RDONLY); + if (rc != EOK) { + return rc; + } + *size = ext4_fsize(&file); + ext4_fclose(&file); + return EOK; +} + +int zeroos_lwext4_create_file(const char *path) { + ext4_file file; + int rc = ext4_fopen2(&file, path, O_RDWR | O_CREAT); + if (rc == EOK) { + ext4_fclose(&file); + ext4_cache_flush("/"); + } + return rc; +} + +int zeroos_lwext4_mkdir(const char *path) { + int rc = ext4_dir_mk(path); + if (rc == EEXIST) { + return EOK; + } + ext4_cache_flush("/"); + return rc; +} diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 2e52be2..fae5901 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -1,3 +1,4 @@ pub mod ext4; +pub mod lwext4; pub mod uefi; pub mod vfs; diff --git a/src/fs/vfs.rs b/src/fs/vfs.rs index 28f3731..134a3a4 100644 --- a/src/fs/vfs.rs +++ b/src/fs/vfs.rs @@ -15,7 +15,6 @@ const EINVAL: isize = 22; const ENOENT: isize = 2; const ENOTDIR: isize = 20; const EMFILE: isize = 24; -const EIO: isize = 5; #[derive(Clone, Copy, PartialEq, Eq)] enum Color { @@ -226,12 +225,25 @@ impl Vfs { return -EINVAL; } - let offset = self.fds[fd].offset.min(node.len); - let available = node.len - offset; + if node.data.is_null() { + let count = + match crate::fs::lwext4::read_path_at(&node.path, self.fds[fd].offset, buffer, len) + { + Ok(count) => count, + Err(error) => return error, + }; + self.fds[fd].offset = self.fds[fd].offset.saturating_add(count); + return count as isize; + } + + let node_len = self.node_len(node_index); + let offset = self.fds[fd].offset.min(node_len); + let available = node_len - offset; let count = len.min(available); if count > 0 { unsafe { - core::ptr::copy_nonoverlapping(node.data.add(offset), buffer, count); + let source = node.data.add(offset); + core::ptr::copy_nonoverlapping(source, buffer, count); } } self.fds[fd].offset = offset + count; @@ -245,6 +257,9 @@ impl Vfs { let Some(node_index) = self.fds[fd].node else { return -EBADF; }; + if self.nodes[node_index].kind == NodeKind::Regular { + return self.write_regular_fd(fd, node_index, buffer, len); + } if self.nodes[node_index].kind != NodeKind::CharDevice { return -EINVAL; } @@ -253,6 +268,31 @@ impl Vfs { crate::console::write(buffer) as isize } + fn write_regular_fd( + &mut self, + fd: usize, + node_index: usize, + buffer: *const u8, + len: usize, + ) -> isize { + let offset = self.fds[fd].offset; + let count = match crate::fs::lwext4::write_path_at( + &self.nodes[node_index].path, + offset, + buffer, + len, + ) { + Ok(count) => count, + Err(error) => return error, + }; + let end = offset.saturating_add(count); + self.fds[fd].offset = end; + if end > self.nodes[node_index].len { + self.nodes[node_index].len = end; + } + count as isize + } + fn close_fd(&mut self, fd: usize) -> isize { if fd < 3 || fd >= MAX_FDS || self.fds[fd].node.is_none() { return -EBADF; @@ -298,7 +338,7 @@ impl Vfs { let Some(node_index) = self.fds[fd].node else { return -EBADF; }; - let len = self.nodes[node_index].len as isize; + let len = self.node_len(node_index) as isize; let current = self.fds[fd].offset as isize; let next = match whence { 0 => offset, @@ -323,6 +363,24 @@ impl Vfs { write_stat(stat, self.nodes[node_index]) } + fn ftruncate_fd(&mut self, fd: usize, size: usize) -> isize { + if fd >= MAX_FDS { + return -EBADF; + } + let Some(node_index) = self.fds[fd].node else { + return -EBADF; + }; + if self.nodes[node_index].kind != NodeKind::Regular { + return -EINVAL; + } + match crate::fs::lwext4::truncate_path(&self.nodes[node_index].path, size) { + Ok(()) => {} + Err(error) => return error, + } + self.nodes[node_index].len = size; + 0 + } + fn is_tty_fd(&self, fd: usize) -> bool { if fd >= MAX_FDS { return false; @@ -387,6 +445,22 @@ impl Vfs { write_stat(stat, self.nodes[node_index]) } + fn truncate_path(&mut self, path: &[u8], size: usize) -> isize { + let Some(node_index) = self.find(path) else { + return -ENOENT; + }; + if self.nodes[node_index].kind != NodeKind::Regular { + return -EINVAL; + } + match crate::fs::lwext4::truncate_path(&self.nodes[node_index].path, size) { + Ok(()) => { + self.nodes[node_index].len = size; + 0 + } + Err(error) => error, + } + } + fn chdir_path(&mut self, path: &[u8]) -> isize { let Some(node_index) = self.find(path) else { return -ENOENT; @@ -414,6 +488,15 @@ impl Vfs { } } + fn node_len(&self, node_index: usize) -> usize { + let node = self.nodes[node_index]; + if node.kind == NodeKind::Regular && node.data.is_null() { + crate::fs::lwext4::size_path(&node.path).unwrap_or(node.len) + } else { + node.len + } + } + fn getcwd(&self, buffer: *mut u8, len: usize) -> isize { if buffer.is_null() { return -EFAULT; @@ -576,10 +659,7 @@ pub unsafe fn init() { } } -pub unsafe fn mount_static_file(path: &'static [u8], data: *const u8, len: usize) -> Option<()> { - unsafe { vfs_mut().insert(path, NodeKind::Regular, data, len) } -} - +#[allow(dead_code)] pub fn mount_ext4_node(path: &[u8], kind: NodeKind) -> Option<()> { unsafe { vfs_mut().insert(path, kind, core::ptr::null(), 0) } } @@ -655,6 +735,10 @@ pub fn fstat(fd: usize, stat: *mut u8) -> isize { unsafe { vfs_ref().stat_fd(fd, stat) } } +pub fn ftruncate(fd: usize, size: usize) -> isize { + unsafe { vfs_mut().ftruncate_fd(fd, size) } +} + pub fn is_tty(fd: usize) -> bool { unsafe { vfs_ref().is_tty_fd(fd) } } @@ -684,6 +768,13 @@ pub fn stat_user_path(path: *const u8, stat: *mut u8) -> isize { unsafe { vfs_ref().stat_path(&path, stat) } } +pub fn truncate_user_path(path: *const u8, size: usize) -> isize { + let Some(path) = (unsafe { read_user_path(path) }) else { + return -EFAULT; + }; + unsafe { vfs_mut().truncate_path(&path, size) } +} + pub fn statat_user_path(dirfd: isize, path: *const u8, stat: *mut u8) -> isize { if unsafe { path_starts_with_slash(path) } || dirfd == AT_FDCWD { return stat_user_path(path, stat); @@ -761,9 +852,9 @@ fn create_directory_path(path: &[u8]) -> isize { if unsafe { vfs_ref().find(path).is_some() } { return 0; } - match crate::fs::ext4::create_dir(path) { + match crate::fs::lwext4::create_dir(path) { Ok(()) => unsafe { vfs_mut().create_path(path, NodeKind::Directory) }, - Err(_) => -EIO, + Err(error) => error, } } @@ -771,9 +862,9 @@ fn create_regular_path(path: &[u8]) -> isize { if unsafe { vfs_ref().find(path).is_some() } { return 0; } - match crate::fs::ext4::create_file(path) { + match crate::fs::lwext4::create_file(path) { Ok(()) => unsafe { vfs_mut().create_path(path, NodeKind::Regular) }, - Err(_) => -EIO, + Err(error) => error, } } @@ -781,15 +872,11 @@ pub fn getcwd(buffer: *mut u8, len: usize) -> isize { unsafe { vfs_ref().getcwd(buffer, len) } } -pub fn file_data_user_path(path: *const u8) -> Option<(*const u8, usize)> { +pub fn exec_path_user(path: *const u8) -> Option<[u8; MAX_PATH]> { let path = unsafe { read_user_path(path) }?; unsafe { let index = vfs_ref().resolve_executable_path(&path)?; - let node = &vfs_ref().nodes[index]; - if node.kind != NodeKind::Regular { - return None; - } - Some((node.data, node.len)) + Some(vfs_ref().nodes[index].path) } } diff --git a/src/main.rs b/src/main.rs index c526081..93c8730 100644 --- a/src/main.rs +++ b/src/main.rs @@ -84,87 +84,48 @@ extern "efiapi" fn efi_main( let _ = (stats.total_pages, stats.free_pages, stats.used_pages); logger.ok(b"physical memory manager"); - fs::vfs::init(); + services::vfs::init(); logger.ok(b"vfs"); ipc::init(); logger.ok(b"ipc"); - let ahci_disks = drivers::storage::ahci::init(memory); - let mut ext4_user_image = None; - let mut ext4_user_path = None; - if ahci_disks > 0 { + let storage = services::storage::init(memory); + if storage.ahci_disks > 0 { logger.ok(b"ahci"); - if let Some(mut disk) = drivers::storage::ahci::first_disk() { - match drivers::storage::partition::find_linux_partition(&mut disk) { - Ok(partition) => { - let mut root = - drivers::storage::partition::PartitionBlockDevice::new(disk, partition); - match fs::ext4::mount(&mut root) { - Ok(ext4) => { - logger.ok(b"ext4"); - let _ = fs::ext4::mount_vfs_tree(&mut root, &ext4); - fs::ext4::register_root(root, ext4); - match fs::ext4::load_path( - &mut root, - &ext4, - memory, - b"/usr/bin/busybox", - ) { - Ok(image) => { - ext4_user_image = Some(image); - ext4_user_path = Some(b"/usr/bin/busybox" as &'static [u8]); - logger.ok(b"ext4 /usr/bin/busybox"); - } - Err(_) => match fs::ext4::load_path( - &mut root, - &ext4, - memory, - b"/usr/bin/sh", - ) { - Ok(image) => { - ext4_user_image = Some(image); - ext4_user_path = Some(b"/usr/bin/sh" as &'static [u8]); - logger.ok(b"ext4 /usr/bin/sh"); - } - Err(_) => logger.info(b"ext4 userspace not loaded"), - }, - } - } - Err(_) => logger.info(b"ext4 not mounted"), - } - } - Err(_) => logger.info(b"no linux gpt partition"), - } - } } else { logger.info(b"no ahci disk"); } + if storage.ext4_mounted { + logger.ok(b"ext4"); + } else if storage.ahci_disks > 0 { + logger.info(b"ext4 not mounted"); + } + if storage.userspace_path == Some(b"/usr/bin/busybox" as &'static [u8]) { + logger.ok(b"ext4 /usr/bin/busybox"); + } else if storage.userspace_path == Some(b"/usr/bin/sh" as &'static [u8]) { + logger.ok(b"ext4 /usr/bin/sh"); + } else if storage.ext4_mounted { + logger.info(b"ext4 userspace not loaded"); + } - let (loaded_user_image, loaded_user_path) = if let Some(image) = ext4_user_image { - (Some(image), ext4_user_path) - } else if let Some(image) = - fs::uefi::load_file(image_handle, boot_services, memory, fs::uefi::BASH_PATH) - { - (Some(image), Some(b"/bin/bash" as &'static [u8])) - } else if let Some(image) = - fs::uefi::load_file(image_handle, boot_services, memory, fs::uefi::SH_PATH) - { - (Some(image), Some(b"/bin/sh" as &'static [u8])) - } else { - (None, None) - }; - let userspace_image_found = loaded_user_image.is_some(); - if loaded_user_image.is_some() { - if let (Some(image), Some(path)) = (&loaded_user_image, loaded_user_path) { - let _ = fs::vfs::mount_static_file(path, image.ptr, image.len); - let _ = fs::vfs::mount_static_file(b"/usr/bin/busybox", image.ptr, image.len); - let _ = fs::vfs::mount_static_file(b"/bin/busybox", image.ptr, image.len); - let _ = fs::vfs::mount_static_file(b"/usr/bin/sh", image.ptr, image.len); - let _ = fs::vfs::mount_static_file(b"/usr/bin/bash", image.ptr, image.len); - let _ = fs::vfs::mount_static_file(b"/bin/sh", image.ptr, image.len); - let _ = fs::vfs::mount_static_file(b"/bin/bash", image.ptr, image.len); + let (loaded_user_image, loaded_user_path) = match storage.userspace_image { + Some(image) => (Some(image), storage.userspace_path), + None => { + if let Some(image) = + fs::uefi::load_file(image_handle, boot_services, memory, fs::uefi::BASH_PATH) + { + (Some(image), Some(b"/bin/bash" as &'static [u8])) + } else if let Some(image) = + fs::uefi::load_file(image_handle, boot_services, memory, fs::uefi::SH_PATH) + { + (Some(image), Some(b"/bin/sh" as &'static [u8])) + } else { + (None, None) + } } - logger.ok(b"userspace image loaded from esp"); + }; + if loaded_user_image.is_some() { + logger.ok(b"userspace image loaded"); } else { logger.info(b"no /bin/bash or /bin/sh on esp, using built-in user program"); } @@ -189,8 +150,8 @@ extern "efiapi" fn efi_main( logger.ok(b"address space"); let mut userspace_program = None; - let user_entry = if let Some(image) = loaded_user_image { - match elf::load_user_elf(&image, memory) { + let user_entry = if let Some(ref image) = loaded_user_image { + match elf::load_user_elf(image, memory) { Some(program) => { logger.ok(b"elf userspace program"); userspace_program = Some(user::UserAux { @@ -209,6 +170,13 @@ extern "efiapi" fn efi_main( } else { None }; + let user_args: &[&[u8]] = match loaded_user_path { + Some(b"/usr/bin/busybox") => &[b"/usr/bin/busybox", b"sh"], + Some(b"/usr/bin/sh") => &[b"/usr/bin/sh"], + Some(b"/usr/bin/bash") => &[b"/usr/bin/bash"], + Some(b"/bin/sh") => &[b"/bin/sh"], + _ => &[b"/bin/bash"], + }; let Some(test_page) = memory.alloc_page() else { halt_forever(); @@ -251,16 +219,10 @@ extern "efiapi" fn efi_main( logger.ok(b"cr3"); tty.clear(); if user_entry.is_some() { - if loaded_user_path == Some(b"/usr/bin/busybox" as &'static [u8]) { - console::write(b"enter userspace: /usr/bin/busybox sh\r\n"); - } else if loaded_user_path == Some(b"/usr/bin/sh" as &'static [u8]) { - console::write(b"enter userspace: /usr/bin/sh\r\n"); - } else if loaded_user_path == Some(b"/usr/bin/bash" as &'static [u8]) { - console::write(b"enter userspace: /usr/bin/bash\r\n"); - } else { - console::write(b"enter userspace: /bin/bash\r\n"); - } - } else if userspace_image_found { + console::write(b"enter userspace: "); + console::write(user_args[0]); + console::write(b"\r\n"); + } else if loaded_user_image.is_some() { console::write(b"/bin/bash found but ELF load failed; enter built-in userspace\r\n"); } else { console::write(b"/bin/bash not found on ESP; enter built-in userspace\r\n"); @@ -268,37 +230,11 @@ extern "efiapi" fn efi_main( syscall::init(memory, &raw mut address_space); io::enable_interrupts(); if let Some(entry) = user_entry { - if loaded_user_path == Some(b"/usr/bin/busybox" as &'static [u8]) { - user::enter_elf_with_args( - entry, - &[b"/usr/bin/busybox", b"sh"], - user_program_or_default(user_entry, userspace_program), - ); - } else if loaded_user_path == Some(b"/usr/bin/sh" as &'static [u8]) { - user::enter_elf_with_args( - entry, - &[b"/usr/bin/sh"], - user_program_or_default(user_entry, userspace_program), - ); - } else if loaded_user_path == Some(b"/usr/bin/bash" as &'static [u8]) { - user::enter_elf_with_args( - entry, - &[b"/usr/bin/bash"], - user_program_or_default(user_entry, userspace_program), - ); - } else if loaded_user_path == Some(b"/bin/sh" as &'static [u8]) { - user::enter_elf_with_args( - entry, - &[b"/bin/sh"], - user_program_or_default(user_entry, userspace_program), - ); - } else { - user::enter_elf_with_args( - entry, - &[b"/bin/bash"], - user_program_or_default(user_entry, userspace_program), - ); - } + user::enter_elf_with_args( + entry, + user_args, + user_program_or_default(user_entry, userspace_program), + ); } else { user::enter(); } diff --git a/src/services/mod.rs b/src/services/mod.rs index b5ed10d..960d8cd 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -1,10 +1,12 @@ #![allow(dead_code)] +pub mod storage; pub mod tty; pub mod vfs; pub const SERVICE_VFS: usize = 1; pub const SERVICE_TTY: usize = 2; +pub const SERVICE_STORAGE: usize = 3; #[derive(Clone, Copy)] pub enum ServiceRequest { diff --git a/src/services/storage.rs b/src/services/storage.rs new file mode 100644 index 0000000..1e1b2e9 --- /dev/null +++ b/src/services/storage.rs @@ -0,0 +1,54 @@ +use crate::{drivers, fs, memory::PhysicalMemoryManager}; + +pub struct StorageInitResult { + pub ahci_disks: usize, + pub ext4_mounted: bool, + pub userspace_image: Option, + pub userspace_path: Option<&'static [u8]>, +} + +pub fn init(memory: &mut PhysicalMemoryManager) -> StorageInitResult { + let ahci_disks = unsafe { drivers::storage::ahci::init(memory) }; + let mut result = StorageInitResult { + ahci_disks, + ext4_mounted: false, + userspace_image: None, + userspace_path: None, + }; + + if ahci_disks == 0 { + return result; + } + + let Some(mut disk) = drivers::storage::ahci::first_disk() else { + return result; + }; + let Ok(partition) = drivers::storage::partition::find_linux_partition(&mut disk) else { + return result; + }; + + let root = drivers::storage::partition::PartitionBlockDevice::new(disk, partition); + let root_sector_count = partition.last_lba - partition.first_lba + 1; + if unsafe { fs::lwext4::mount_root(root, root_sector_count) }.is_err() { + return result; + } + fs::lwext4::init_cache(memory as *mut PhysicalMemoryManager); + + result.ext4_mounted = true; + let _ = fs::lwext4::mount_vfs_tree(); + + match fs::lwext4::load_path(memory, b"/usr/bin/busybox") { + Ok(image) => { + result.userspace_image = Some(image); + result.userspace_path = Some(b"/usr/bin/busybox"); + } + Err(_) => { + if let Ok(image) = fs::lwext4::load_path(memory, b"/usr/bin/sh") { + result.userspace_image = Some(image); + result.userspace_path = Some(b"/usr/bin/sh"); + } + } + } + + result +} diff --git a/src/services/tty.rs b/src/services/tty.rs index d03f538..5a8aed1 100644 --- a/src/services/tty.rs +++ b/src/services/tty.rs @@ -1,9 +1,69 @@ -use crate::ipc::Message; +use crate::{console, fs::vfs, ipc::Message}; pub const OP_READ: u16 = 1; pub const OP_WRITE: u16 = 2; pub const OP_IOCTL: u16 = 3; +pub const OP_RESET: u16 = 4; +pub const OP_SET_RAW: u16 = 5; +pub const OP_SIZE: u16 = 6; +pub const OP_IS_TTY: u16 = 7; -pub fn dispatch(_message: Message) -> isize { - -38 +pub fn dispatch(message: Message) -> isize { + match message.opcode { + OP_WRITE => console::write(unsafe { + core::slice::from_raw_parts(message.args[0] as *const u8, message.args[1]) + }) as isize, + OP_RESET => { + console::reset_terminal(); + 0 + } + OP_SET_RAW => { + console::set_raw_mode(message.args[0] != 0); + 0 + } + OP_SIZE => { + let (columns, rows) = console::size(); + ((rows & 0xffff) << 16 | (columns & 0xffff)) as isize + } + OP_IS_TTY => { + if message.args[0] <= 2 || vfs::is_tty(message.args[0]) { + 1 + } else { + 0 + } + } + _ => -38, + } +} + +pub fn write(bytes: &[u8]) -> usize { + dispatch(message( + OP_WRITE, + [bytes.as_ptr() as usize, bytes.len(), 0, 0, 0, 0], + )) as usize +} + +pub fn reset_terminal() { + let _ = dispatch(message(OP_RESET, [0, 0, 0, 0, 0, 0])); +} + +pub fn set_raw_mode(enabled: bool) { + let _ = dispatch(message(OP_SET_RAW, [enabled as usize, 0, 0, 0, 0, 0])); +} + +pub fn size() -> (usize, usize) { + let packed = dispatch(message(OP_SIZE, [0, 0, 0, 0, 0, 0])) as usize; + (packed & 0xffff, (packed >> 16) & 0xffff) +} + +pub fn is_tty(fd: usize) -> bool { + dispatch(message(OP_IS_TTY, [fd, 0, 0, 0, 0, 0])) != 0 +} + +fn message(opcode: u16, args: [usize; 6]) -> Message { + Message { + sender: crate::task::current_pid(), + opcode, + args, + } } diff --git a/src/services/vfs.rs b/src/services/vfs.rs index 66884e9..fcfac78 100644 --- a/src/services/vfs.rs +++ b/src/services/vfs.rs @@ -1,10 +1,188 @@ -use crate::ipc::Message; +use crate::{fs::vfs, ipc::Message}; pub const OP_OPEN: u16 = 1; pub const OP_READ: u16 = 2; pub const OP_WRITE: u16 = 3; pub const OP_CLOSE: u16 = 4; +pub const OP_OPENAT: u16 = 5; +pub const OP_ACCESS: u16 = 6; +pub const OP_FSTAT: u16 = 7; +pub const OP_LSEEK: u16 = 8; +pub const OP_DUP: u16 = 9; +pub const OP_DUP_MIN: u16 = 10; +pub const OP_DUP2: u16 = 11; +pub const OP_GETCWD: u16 = 12; +pub const OP_CHDIR: u16 = 13; +pub const OP_MKDIR: u16 = 14; +pub const OP_MKDIRAT: u16 = 15; +pub const OP_TOUCH: u16 = 16; +pub const OP_GETDENTS64: u16 = 17; +pub const OP_STAT: u16 = 18; +pub const OP_STATAT: u16 = 19; +pub const OP_FTRUNCATE: u16 = 20; +pub const OP_TRUNCATE: u16 = 21; -pub fn dispatch(_message: Message) -> isize { - -38 +pub fn dispatch(message: Message) -> isize { + match message.opcode { + OP_OPEN => vfs::open_user_path_with_flags(message.args[0] as *const u8, message.args[1]), + OP_OPENAT => vfs::openat_user_path( + message.args[0] as isize, + message.args[1] as *const u8, + message.args[2], + ), + OP_ACCESS => vfs::access_user_path(message.args[0] as *const u8), + OP_READ => vfs::read(message.args[0], message.args[1] as *mut u8, message.args[2]), + OP_WRITE => vfs::write( + message.args[0], + message.args[1] as *const u8, + message.args[2], + ), + OP_CLOSE => vfs::close(message.args[0]), + OP_FSTAT => vfs::fstat(message.args[0], message.args[1] as *mut u8), + OP_LSEEK => vfs::lseek(message.args[0], message.args[1] as isize, message.args[2]), + OP_DUP => vfs::dup(message.args[0]), + OP_DUP_MIN => vfs::dup_min(message.args[0], message.args[1]), + OP_DUP2 => vfs::dup2(message.args[0], message.args[1]), + OP_GETCWD => vfs::getcwd(message.args[0] as *mut u8, message.args[1]), + OP_CHDIR => vfs::chdir_user_path(message.args[0] as *const u8), + OP_MKDIR => vfs::mkdir_user_path(message.args[0] as *const u8), + OP_MKDIRAT => { + vfs::mkdirat_user_path(message.args[0] as isize, message.args[1] as *const u8) + } + OP_TOUCH => vfs::touch_user_path(message.args[0] as isize, message.args[1] as *const u8), + OP_GETDENTS64 => { + vfs::getdents64(message.args[0], message.args[1] as *mut u8, message.args[2]) + } + OP_STAT => vfs::stat_user_path(message.args[0] as *const u8, message.args[1] as *mut u8), + OP_STATAT => vfs::statat_user_path( + message.args[0] as isize, + message.args[1] as *const u8, + message.args[2] as *mut u8, + ), + OP_FTRUNCATE => vfs::ftruncate(message.args[0], message.args[1]), + OP_TRUNCATE => vfs::truncate_user_path(message.args[0] as *const u8, message.args[1]), + _ => -38, + } +} + +pub const AT_FDCWD: isize = vfs::AT_FDCWD; + +pub unsafe fn init() { + unsafe { vfs::init() } +} + +pub fn open_user_path_with_flags(path: *const u8, flags: usize) -> isize { + dispatch(message(OP_OPEN, [path as usize, flags, 0, 0, 0, 0])) +} + +pub fn openat_user_path(dirfd: isize, path: *const u8, flags: usize) -> isize { + dispatch(message( + OP_OPENAT, + [dirfd as usize, path as usize, flags, 0, 0, 0], + )) +} + +pub fn access_user_path(path: *const u8) -> isize { + dispatch(message(OP_ACCESS, [path as usize, 0, 0, 0, 0, 0])) +} + +pub fn is_tty(fd: usize) -> bool { + vfs::is_tty(fd) +} + +pub fn read(fd: usize, buffer: *mut u8, len: usize) -> isize { + dispatch(message(OP_READ, [fd, buffer as usize, len, 0, 0, 0])) +} + +pub fn write(fd: usize, buffer: *const u8, len: usize) -> isize { + dispatch(message(OP_WRITE, [fd, buffer as usize, len, 0, 0, 0])) +} + +pub fn poll(fd: usize, events: i16) -> Option { + vfs::poll(fd, events) +} + +pub fn close(fd: usize) -> isize { + dispatch(message(OP_CLOSE, [fd, 0, 0, 0, 0, 0])) +} + +pub fn fstat(fd: usize, stat: *mut u8) -> isize { + dispatch(message(OP_FSTAT, [fd, stat as usize, 0, 0, 0, 0])) +} + +pub fn ftruncate(fd: usize, size: usize) -> isize { + dispatch(message(OP_FTRUNCATE, [fd, size, 0, 0, 0, 0])) +} + +pub fn truncate_user_path(path: *const u8, size: usize) -> isize { + dispatch(message(OP_TRUNCATE, [path as usize, size, 0, 0, 0, 0])) +} + +pub fn lseek(fd: usize, offset: isize, whence: usize) -> isize { + dispatch(message(OP_LSEEK, [fd, offset as usize, whence, 0, 0, 0])) +} + +pub fn dup(fd: usize) -> isize { + dispatch(message(OP_DUP, [fd, 0, 0, 0, 0, 0])) +} + +pub fn dup_min(fd: usize, min_newfd: usize) -> isize { + dispatch(message(OP_DUP_MIN, [fd, min_newfd, 0, 0, 0, 0])) +} + +pub fn dup2(oldfd: usize, newfd: usize) -> isize { + dispatch(message(OP_DUP2, [oldfd, newfd, 0, 0, 0, 0])) +} + +pub fn exec_path_user(path: *const u8) -> Option<[u8; 256]> { + vfs::exec_path_user(path) +} + +pub fn getcwd(buffer: *mut u8, len: usize) -> isize { + dispatch(message(OP_GETCWD, [buffer as usize, len, 0, 0, 0, 0])) +} + +pub fn chdir_user_path(path: *const u8) -> isize { + dispatch(message(OP_CHDIR, [path as usize, 0, 0, 0, 0, 0])) +} + +pub fn mkdir_user_path(path: *const u8) -> isize { + dispatch(message(OP_MKDIR, [path as usize, 0, 0, 0, 0, 0])) +} + +pub fn mkdirat_user_path(dirfd: isize, path: *const u8) -> isize { + dispatch(message( + OP_MKDIRAT, + [dirfd as usize, path as usize, 0, 0, 0, 0], + )) +} + +pub fn touch_user_path(dirfd: isize, path: *const u8) -> isize { + dispatch(message( + OP_TOUCH, + [dirfd as usize, path as usize, 0, 0, 0, 0], + )) +} + +pub fn getdents64(fd: usize, buffer: *mut u8, len: usize) -> isize { + dispatch(message(OP_GETDENTS64, [fd, buffer as usize, len, 0, 0, 0])) +} + +pub fn stat_user_path(path: *const u8, stat: *mut u8) -> isize { + dispatch(message(OP_STAT, [path as usize, stat as usize, 0, 0, 0, 0])) +} + +pub fn statat_user_path(dirfd: isize, path: *const u8, stat: *mut u8) -> isize { + dispatch(message( + OP_STATAT, + [dirfd as usize, path as usize, stat as usize, 0, 0, 0], + )) +} + +fn message(opcode: u16, args: [usize; 6]) -> Message { + Message { + sender: crate::task::current_pid(), + opcode, + args, + } } diff --git a/src/syscall.rs b/src/syscall.rs index 82cba40..321f4ef 100644 --- a/src/syscall.rs +++ b/src/syscall.rs @@ -1,13 +1,11 @@ use core::arch::{asm, global_asm}; use crate::{ - console, drivers::platform::timer, - elf, - fs::vfs, - gdt, ipc, + elf, gdt, ipc, memory::{PAGE_SIZE, PhysicalMemoryManager}, paging::{AddressSpace, PageFlags}, + services::{tty as tty_service, vfs}, task, user, }; @@ -48,12 +46,17 @@ pub const SYS_FORK: usize = 57; pub const SYS_VFORK: usize = 58; pub const SYS_EXECVE: usize = 59; pub const SYS_WAIT4: usize = 61; +pub const SYS_FSYNC: usize = 74; +pub const SYS_FDATASYNC: usize = 75; +pub const SYS_TRUNCATE: usize = 76; +pub const SYS_FTRUNCATE: usize = 77; pub const SYS_FCNTL: usize = 72; pub const SYS_DUP: usize = 32; pub const SYS_DUP2: usize = 33; pub const SYS_GETDENTS64: usize = 217; pub const SYS_GETRANDOM: usize = 318; pub const SYS_PRLIMIT64: usize = 302; +pub const SYS_SYNCFS: usize = 306; pub const SYS_GETPGID: usize = 121; pub const SYS_SETPGID: usize = 109; pub const SYS_GETSID: usize = 124; @@ -63,6 +66,7 @@ pub const SYS_CHDIR: usize = 80; pub const SYS_MKDIR: usize = 83; pub const SYS_READLINK: usize = 89; pub const SYS_PRCTL: usize = 157; +pub const SYS_SYNC: usize = 162; pub const SYS_TIME: usize = 201; pub const SYS_GETUID: usize = 102; pub const SYS_GETGID: usize = 104; @@ -401,11 +405,15 @@ pub extern "C" fn zeroos_syscall_dispatch( SYS_VFORK => sys_vfork(frame), SYS_EXECVE => sys_execve(arg0 as *const u8, arg1 as *const *const u8, frame), SYS_WAIT4 => sys_wait4(arg0 as isize, arg1 as *mut i32), + SYS_FSYNC | SYS_FDATASYNC => sys_sync(), + SYS_TRUNCATE => vfs::truncate_user_path(arg0 as *const u8, arg1), + SYS_FTRUNCATE => vfs::ftruncate(arg0, arg1), SYS_FCNTL => sys_fcntl(arg0, arg1, arg2), SYS_GETCWD => sys_getcwd(arg0 as *mut u8, arg1), SYS_CHDIR => sys_chdir(arg0 as *const u8), SYS_READLINK => -ENOENT, SYS_PRCTL => 0, + SYS_SYNC => sys_sync(), SYS_TIME => sys_time(arg0 as *mut u64), SYS_UNAME => sys_uname(arg0 as *mut u8), SYS_GETUID | SYS_GETGID | SYS_GETEUID | SYS_GETEGID => 0, @@ -424,6 +432,7 @@ pub extern "C" fn zeroos_syscall_dispatch( SYS_RSEQ => sys_rseq(arg0, arg1, arg2), SYS_GETRANDOM => sys_getrandom(arg0 as *mut u8, arg1, arg2), SYS_PRLIMIT64 => sys_prlimit64(arg0, arg1, arg2 as *const u8, arg3 as *mut u8), + SYS_SYNCFS => sys_sync(), SYS_GETDENTS64 => trace_fs_result( b"getdents", arg0, @@ -464,9 +473,9 @@ fn trace_syscall(number: usize) { } SYSCALL_TRACE_COUNT += 1; } - console::write(b"sys "); + tty_service::write(b"sys "); write_usize(number); - console::write(b"\n"); + tty_service::write(b"\n"); } fn sys_read(fd: usize, buffer: *mut u8, len: usize) -> isize { @@ -477,6 +486,16 @@ fn sys_read(fd: usize, buffer: *mut u8, len: usize) -> isize { return -EBADF; } + if vfs::is_tty(fd) { + loop { + let read = vfs::read(fd, buffer, len); + if read != 0 { + return read; + } + core::hint::spin_loop(); + } + } + loop { if let Some(read) = ipc::read(fd, buffer, len) { if read != -EAGAIN { @@ -710,9 +729,9 @@ fn sys_fcntl(fd: usize, command: usize, arg: usize) -> isize { F_GETFL => 0, F_SETFL => 0, _ => { - console::write(b"unsupported fcntl "); + tty_service::write(b"unsupported fcntl "); write_usize(command); - console::write(b"\n"); + tty_service::write(b"\n"); -EINVAL } } @@ -726,7 +745,7 @@ fn sys_execve(path: *const u8, argv: *const *const u8, frame: *mut TrapFrame) -> if path.is_null() || frame.is_null() { return -EFAULT; } - let Some((ptr, len)) = vfs::file_data_user_path(path) else { + let Some(exec_path) = vfs::exec_path_user(path) else { return -ENOENT; }; @@ -738,11 +757,13 @@ fn sys_execve(path: *const u8, argv: *const *const u8, frame: *mut TrapFrame) -> args[index] = &arg_storage[index][..arg_lens[index]]; } - let image = crate::fs::uefi::LoadedFile { ptr, len }; unsafe { if MEMORY.is_null() { return -ENODEV; } + let Ok(image) = crate::fs::lwext4::load_path(&mut *MEMORY, &exec_path) else { + return -ENOENT; + }; let Some(program) = elf::load_user_elf(&image, &mut *MEMORY) else { return -ENOENT; }; @@ -973,7 +994,7 @@ fn sys_ioctl(fd: usize, request: usize, arg: *mut u8, frame: *mut TrapFrame) -> if arg.is_null() { return -EBADF; } - if fd > STDERR_FILENO && !vfs::is_tty(fd) { + if fd > STDERR_FILENO && !tty_service::is_tty(fd) { return -EBADF; } @@ -1000,7 +1021,7 @@ fn sys_ioctl(fd: usize, request: usize, arg: *mut u8, frame: *mut TrapFrame) -> 0 } TIOCGWINSZ => { - let (columns, rows) = console::size(); + let (columns, rows) = tty_service::size(); let winsize = arg as *mut u16; winsize.add(0).write(rows as u16); winsize.add(1).write(columns as u16); @@ -1034,7 +1055,7 @@ unsafe fn set_termios_mode(arg: *mut u8, _termios2: bool) { unsafe { let lflag = (arg as *const u32).add(3).read(); const ICANON: u32 = 0o0000002; - crate::console::set_raw_mode(lflag & ICANON == 0); + tty_service::set_raw_mode(lflag & ICANON == 0); } } @@ -1134,7 +1155,7 @@ fn sys_arch_prctl(code: usize, addr: usize) -> isize { } fn sys_isatty(fd: usize) -> isize { - if fd <= STDERR_FILENO || vfs::is_tty(fd) { + if fd <= STDERR_FILENO || tty_service::is_tty(fd) { 1 } else { 0 @@ -1316,7 +1337,8 @@ fn sys_prlimit64( } fn sys_exit(code: isize, frame: *mut TrapFrame) -> isize { - crate::console::reset_terminal(); + let _ = crate::fs::lwext4::flush_cache(); + tty_service::reset_terminal(); if !frame.is_null() && task::exit_current(code, unsafe { &mut *frame }) { restore_user_heap(); return unsafe { (*frame).rax as isize }; @@ -1329,6 +1351,13 @@ fn sys_exit(code: isize, frame: *mut TrapFrame) -> isize { } } +fn sys_sync() -> isize { + match crate::fs::lwext4::flush_cache() { + Ok(()) => 0, + Err(error) => error, + } +} + pub fn snapshot_user_heap() { unsafe { snapshot_user_elf_image(); @@ -1426,9 +1455,9 @@ fn trace_unsupported_syscall(number: usize) { LAST_UNSUPPORTED_SYSCALL = number; } - console::write(b"unsupported syscall "); + tty_service::write(b"unsupported syscall "); write_usize(number); - console::write(b"\n"); + tty_service::write(b"\n"); } fn write_usize(mut value: usize) { @@ -1436,7 +1465,7 @@ fn write_usize(mut value: usize) { let mut index = buffer.len(); if value == 0 { - console::write(b"0"); + tty_service::write(b"0"); return; } @@ -1446,19 +1475,19 @@ fn write_usize(mut value: usize) { value /= 10; } - console::write(&buffer[index..]); + tty_service::write(&buffer[index..]); } fn trace_alloc(name: &[u8], addr: usize, len: usize) { if !TRACE_ALLOC { return; } - console::write(name); - console::write(b" "); + tty_service::write(name); + tty_service::write(b" "); write_hex_usize(addr); - console::write(b" "); + tty_service::write(b" "); write_hex_usize(len); - console::write(b"\n"); + tty_service::write(b"\n"); } fn trace_ioctl(request: usize, arg: usize, frame: *mut TrapFrame) { @@ -1471,42 +1500,42 @@ fn trace_ioctl(request: usize, arg: usize, frame: *mut TrapFrame) { } IOCTL_TRACE_COUNT += 1; } - console::write(b"ioctl req="); + tty_service::write(b"ioctl req="); write_hex_usize(request); - console::write(b" arg="); + tty_service::write(b" arg="); write_hex_usize(arg); if !frame.is_null() { let frame = unsafe { &*frame }; - console::write(b" ret="); + tty_service::write(b" ret="); write_hex_usize(frame.rcx); - console::write(b" rsp="); + tty_service::write(b" rsp="); write_hex_usize(frame.rsp); } - console::write(b"\n"); + tty_service::write(b"\n"); } fn trace_fs_result(name: &[u8], arg0: usize, arg1: usize, result: isize) -> isize { if !TRACE_FS_FDS { return result; } - console::write(name); - console::write(b" a0="); + tty_service::write(name); + tty_service::write(b" a0="); write_hex_usize(arg0); - console::write(b" a1="); + tty_service::write(b" a1="); write_hex_usize(arg1); - console::write(b" -> "); + tty_service::write(b" -> "); if result < 0 { - console::write(b"-"); + tty_service::write(b"-"); write_usize((-result) as usize); } else { write_usize(result as usize); } - console::write(b"\n"); + tty_service::write(b"\n"); result } fn write_hex_usize(value: usize) { - console::write(b"0x"); + tty_service::write(b"0x"); let mut shift = usize::BITS as usize; let mut started = false; while shift > 0 { @@ -1519,7 +1548,7 @@ fn write_hex_usize(value: usize) { } else { b'a' + digit - 10 }; - console::write(&[ch]); + tty_service::write(&[ch]); } } } diff --git a/third_party/lwext4 b/third_party/lwext4 new file mode 160000 index 0000000..58bcf89 --- /dev/null +++ b/third_party/lwext4 @@ -0,0 +1 @@ +Subproject commit 58bcf89a121b72d4fb66334f1693d3b30e4cb9c5 diff --git a/third_party/lwext4_config/include/assert.h b/third_party/lwext4_config/include/assert.h new file mode 100644 index 0000000..39f1702 --- /dev/null +++ b/third_party/lwext4_config/include/assert.h @@ -0,0 +1,6 @@ +#ifndef ZEROOS_ASSERT_H_ +#define ZEROOS_ASSERT_H_ + +#define assert(x) ((void)(x)) + +#endif diff --git a/third_party/lwext4_config/include/errno.h b/third_party/lwext4_config/include/errno.h new file mode 100644 index 0000000..a36a001 --- /dev/null +++ b/third_party/lwext4_config/include/errno.h @@ -0,0 +1,6 @@ +#ifndef ZEROOS_ERRNO_H_ +#define ZEROOS_ERRNO_H_ + +#include + +#endif diff --git a/third_party/lwext4_config/include/fcntl.h b/third_party/lwext4_config/include/fcntl.h new file mode 100644 index 0000000..1fc61a4 --- /dev/null +++ b/third_party/lwext4_config/include/fcntl.h @@ -0,0 +1,6 @@ +#ifndef ZEROOS_FCNTL_H_ +#define ZEROOS_FCNTL_H_ + +#include + +#endif diff --git a/third_party/lwext4_config/include/generated/ext4_config.h b/third_party/lwext4_config/include/generated/ext4_config.h new file mode 100644 index 0000000..69744e6 --- /dev/null +++ b/third_party/lwext4_config/include/generated/ext4_config.h @@ -0,0 +1,23 @@ +#ifndef ZEROOS_LWEXT4_GENERATED_CONFIG_H_ +#define ZEROOS_LWEXT4_GENERATED_CONFIG_H_ + +#define CONFIG_EXT_FEATURE_SET_LVL F_SET_EXT4 +#define CONFIG_JOURNALING_ENABLE 0 +#define CONFIG_XATTR_ENABLE 1 +#define CONFIG_EXTENTS_ENABLE 1 +#define CONFIG_HAVE_OWN_ERRNO 1 +#define CONFIG_DEBUG_PRINTF 0 +#define CONFIG_DEBUG_ASSERT 0 +#define CONFIG_HAVE_OWN_ASSERT 1 +#define CONFIG_BLOCK_DEV_ENABLE_STATS 0 +#define CONFIG_BLOCK_DEV_CACHE_SIZE 16 +#define CONFIG_EXT4_MAX_BLOCKDEV_NAME 32 +#define CONFIG_EXT4_BLOCKDEVS_COUNT 2 +#define CONFIG_EXT4_MAX_MP_NAME 32 +#define CONFIG_EXT4_MOUNTPOINTS_COUNT 2 +#define CONFIG_HAVE_OWN_OFLAGS 1 +#define CONFIG_MAX_TRUNCATE_SIZE (16ul * 1024ul * 1024ul) +#define CONFIG_UNALIGNED_ACCESS 0 +#define CONFIG_USE_USER_MALLOC 1 + +#endif diff --git a/third_party/lwext4_config/include/inttypes.h b/third_party/lwext4_config/include/inttypes.h new file mode 100644 index 0000000..2cdcfb3 --- /dev/null +++ b/third_party/lwext4_config/include/inttypes.h @@ -0,0 +1,9 @@ +#ifndef ZEROOS_INTTYPES_H_ +#define ZEROOS_INTTYPES_H_ + +#include + +#define PRIu64 "llu" +#define PRId64 "lld" + +#endif diff --git a/third_party/lwext4_config/include/stdarg.h b/third_party/lwext4_config/include/stdarg.h new file mode 100644 index 0000000..672b6ad --- /dev/null +++ b/third_party/lwext4_config/include/stdarg.h @@ -0,0 +1,10 @@ +#ifndef ZEROOS_STDARG_H_ +#define ZEROOS_STDARG_H_ + +typedef __builtin_va_list va_list; +#define va_start(ap, last) __builtin_va_start(ap, last) +#define va_end(ap) __builtin_va_end(ap) +#define va_arg(ap, type) __builtin_va_arg(ap, type) +#define va_copy(dest, src) __builtin_va_copy(dest, src) + +#endif diff --git a/third_party/lwext4_config/include/stdbool.h b/third_party/lwext4_config/include/stdbool.h new file mode 100644 index 0000000..1a75245 --- /dev/null +++ b/third_party/lwext4_config/include/stdbool.h @@ -0,0 +1,8 @@ +#ifndef ZEROOS_STDBOOL_H_ +#define ZEROOS_STDBOOL_H_ + +#define bool _Bool +#define true 1 +#define false 0 + +#endif diff --git a/third_party/lwext4_config/include/stddef.h b/third_party/lwext4_config/include/stddef.h new file mode 100644 index 0000000..0369bdb --- /dev/null +++ b/third_party/lwext4_config/include/stddef.h @@ -0,0 +1,8 @@ +#ifndef ZEROOS_STDDEF_H_ +#define ZEROOS_STDDEF_H_ + +#define NULL ((void *)0) +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; + +#endif diff --git a/third_party/lwext4_config/include/stdint.h b/third_party/lwext4_config/include/stdint.h new file mode 100644 index 0000000..d2e14c9 --- /dev/null +++ b/third_party/lwext4_config/include/stdint.h @@ -0,0 +1,18 @@ +#ifndef ZEROOS_STDINT_H_ +#define ZEROOS_STDINT_H_ + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +typedef __INTPTR_TYPE__ intptr_t; +typedef __UINTPTR_TYPE__ uintptr_t; + +#define UINT32_MAX 0xffffffffu +#define UINT64_MAX 0xffffffffffffffffull + +#endif diff --git a/third_party/lwext4_config/include/stdio.h b/third_party/lwext4_config/include/stdio.h new file mode 100644 index 0000000..3b79072 --- /dev/null +++ b/third_party/lwext4_config/include/stdio.h @@ -0,0 +1,8 @@ +#ifndef ZEROOS_STDIO_H_ +#define ZEROOS_STDIO_H_ + +#define stdout ((void *)0) +int printf(const char *fmt, ...); +int fflush(void *stream); + +#endif diff --git a/third_party/lwext4_config/include/stdlib.h b/third_party/lwext4_config/include/stdlib.h new file mode 100644 index 0000000..4e46e23 --- /dev/null +++ b/third_party/lwext4_config/include/stdlib.h @@ -0,0 +1,16 @@ +#ifndef ZEROOS_STDLIB_H_ +#define ZEROOS_STDLIB_H_ + +#include + +void *ext4_user_malloc(size_t size); +void *ext4_user_calloc(size_t count, size_t size); +void ext4_user_free(void *ptr); +void qsort( + void *base, + size_t nmemb, + size_t size, + int (*compar)(const void *, const void *) +); + +#endif diff --git a/third_party/lwext4_config/include/string.h b/third_party/lwext4_config/include/string.h new file mode 100644 index 0000000..697f7f5 --- /dev/null +++ b/third_party/lwext4_config/include/string.h @@ -0,0 +1,15 @@ +#ifndef ZEROOS_STRING_H_ +#define ZEROOS_STRING_H_ + +#include + +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *dest, int c, size_t n); +int memcmp(const void *a, const void *b, size_t n); +size_t strlen(const char *s); +int strcmp(const char *a, const char *b); +int strncmp(const char *a, const char *b, size_t n); +char *strcpy(char *dest, const char *src); + +#endif diff --git a/third_party/lwext4_config/include/unistd.h b/third_party/lwext4_config/include/unistd.h new file mode 100644 index 0000000..5a4ab97 --- /dev/null +++ b/third_party/lwext4_config/include/unistd.h @@ -0,0 +1,6 @@ +#ifndef ZEROOS_UNISTD_H_ +#define ZEROOS_UNISTD_H_ + +#include + +#endif