diff --git a/lib/memfs/src/dir.rs b/lib/memfs/src/dir.rs index 77f4f796..995c3f33 100644 --- a/lib/memfs/src/dir.rs +++ b/lib/memfs/src/dir.rs @@ -2,8 +2,11 @@ use core::marker::PhantomData; use alloc::boxed::Box; -use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef}; -use yggdrasil_abi::error::Error; +use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef, DIR_POSITION_FROM_CACHE}; +use yggdrasil_abi::{ + error::Error, + io::{FileMode, OpenOptions}, +}; use crate::{block::BlockAllocator, bvec::BVec, file::FileNode}; @@ -23,6 +26,15 @@ impl VnodeImpl for DirectoryNode { } Ok(child) } + + fn open( + &mut self, + _node: &VnodeRef, + _opts: OpenOptions, + _mode: FileMode, + ) -> Result { + Ok(DIR_POSITION_FROM_CACHE) + } } impl DirectoryNode { diff --git a/lib/vfs/src/file.rs b/lib/vfs/src/file.rs index a7a8e9d5..c12442cb 100644 --- a/lib/vfs/src/file.rs +++ b/lib/vfs/src/file.rs @@ -1,12 +1,15 @@ -use core::cell::RefCell; +use core::{cell::RefCell, mem::MaybeUninit}; use alloc::rc::Rc; use bitflags::bitflags; -use yggdrasil_abi::error::Error; +use yggdrasil_abi::{ + error::Error, + io::{DirectoryEntry, FileType}, +}; use crate::{ node::{VnodeKind, VnodeRef}, - Read, Seek, SeekFrom, Write, + Read, ReadDirectory, Seek, SeekFrom, Write, DIR_POSITION_FROM_CACHE, }; bitflags! { @@ -18,13 +21,28 @@ bitflags! { pub type FileRef = Rc>; +enum DirectoryPosition { + DiskPosition(u64), + // TODO not the best implementation, but at least somewhat safe? + CachePosition(usize), + Dot, + DotDot, + End, +} + pub struct NormalFile { vnode: VnodeRef, pos: u64, } +pub struct Directory { + vnode: VnodeRef, + pos: DirectoryPosition, +} + pub enum FileInner { Normal(NormalFile), + Directory(Directory), } pub struct File { @@ -39,6 +57,20 @@ impl File { flags, })) } + + pub fn directory(vnode: VnodeRef, pos: u64) -> FileRef { + let pos = if pos == DIR_POSITION_FROM_CACHE { + // Read from cache + DirectoryPosition::Dot + } else { + // Reading from a "physical" directory + todo!() + }; + Rc::new(RefCell::new(Self { + inner: FileInner::Directory(Directory { vnode, pos }), + flags: FileFlags::READ, + })) + } } impl Write for File { @@ -55,6 +87,7 @@ impl Write for File { } Ok(count) } + FileInner::Directory(_) => unimplemented!(), } } } @@ -73,6 +106,7 @@ impl Read for File { } Ok(count) } + FileInner::Directory(_) => Err(Error::IsADirectory), } } } @@ -98,16 +132,78 @@ impl Seek for File { Ok(pos) } + FileInner::Directory(_) => todo!(), } } } +impl ReadDirectory for File { + fn read_dir_entries( + &mut self, + entries: &mut [MaybeUninit], + ) -> Result { + let FileInner::Directory(inner) = &mut self.inner else { + return Err(Error::NotADirectory); + }; + + let mut nread = 0; + let mut rem = entries.len(); + + while rem != 0 { + let mut entry = DirectoryEntry { + name: [0; 256], + ty: FileType::File, + }; + + let next_position = match inner.pos { + DirectoryPosition::End => { + break; + } + DirectoryPosition::Dot => { + entry.name[0] = b'.'; + entry.ty = FileType::Directory; + DirectoryPosition::DotDot + } + DirectoryPosition::DotDot => { + entry.name[..2].copy_from_slice(b".."); + entry.ty = FileType::Directory; + DirectoryPosition::CachePosition(0) + } + DirectoryPosition::CachePosition(index) => { + let child = inner.vnode.child_at(index); + + if let Some(child) = child { + let child_name = child.name(); + entry.name[..child_name.len()].copy_from_slice(child_name.as_bytes()); + entry.ty = FileType::from(child.kind()); + DirectoryPosition::CachePosition(index + 1) + } else { + break; + } + } + DirectoryPosition::DiskPosition(_) => todo!(), + }; + inner.pos = next_position; + + entries[nread].write(entry); + + nread += 1; + rem -= 1; + } + + Ok(nread) + } +} + impl Drop for File { fn drop(&mut self) { match &mut self.inner { FileInner::Normal(inner) => { inner.vnode.close().ok(); } + FileInner::Directory(inner) => { + inner.vnode.close().ok(); + } } } } diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index 10937641..e382838f 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -1,6 +1,9 @@ #![no_std] +use core::mem::MaybeUninit; + use yggdrasil_abi::error::Error; +use yggdrasil_abi::io::DirectoryEntry; extern crate alloc; @@ -24,6 +27,8 @@ pub use fs::Filesystem; pub use ioctx::IoContext; pub use node::{Vnode, VnodeDump, VnodeImpl, VnodeKind, VnodeRef, VnodeWeak}; +pub const DIR_POSITION_FROM_CACHE: u64 = u64::MAX; + #[derive(Debug)] pub enum SeekFrom { Start(u64), @@ -47,6 +52,13 @@ pub trait Seek { fn seek(&mut self, pos: SeekFrom) -> Result; } +pub trait ReadDirectory { + fn read_dir_entries( + &mut self, + entries: &mut [MaybeUninit], + ) -> Result; +} + fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<(), Error> { while !buf.is_empty() { match this.read(buf) { diff --git a/lib/vfs/src/node.rs b/lib/vfs/src/node.rs index 82088b3a..0a37b2ec 100644 --- a/lib/vfs/src/node.rs +++ b/lib/vfs/src/node.rs @@ -11,12 +11,13 @@ use alloc::{ }; use yggdrasil_abi::{ error::Error, - io::{FileMode, OpenOptions}, + io::{FileMode, FileType, OpenOptions}, }; use crate::{ file::{File, FileFlags, FileRef}, fs::Filesystem, + DIR_POSITION_FROM_CACHE, }; pub type VnodeRef = Rc; @@ -163,6 +164,11 @@ impl Vnode { parent_borrow.children.push(child); } + pub fn child_at(self: &VnodeRef, index: usize) -> Option { + let tree = self.tree.borrow(); + tree.children.get(index).cloned() + } + pub fn dump(&self, f: &mut fmt::Formatter<'_>, depth: usize, indent: bool) -> fmt::Result { if indent { for _ in 0..depth { @@ -244,11 +250,25 @@ impl Vnode { } } + pub fn open_directory(self: &VnodeRef) -> Result { + if !self.is_directory() { + return Err(Error::IsADirectory); + } + + if let Some(ref mut data) = *self.data() { + let pos = data.open(self, OpenOptions::READ, FileMode::empty())?; + Ok(File::directory(self.clone(), pos)) + } else { + // TODO: some options here? + Ok(File::directory(self.clone(), DIR_POSITION_FROM_CACHE)) + } + } + pub fn close(self: &VnodeRef) -> Result<(), Error> { if let Some(ref mut data) = *self.data() { data.close(self) } else { - todo!() + Ok(()) } } @@ -377,3 +397,14 @@ impl fmt::Debug for VnodeDump { self.node.dump(f, 0, true) } } + +impl From for FileType { + fn from(value: VnodeKind) -> Self { + match value { + VnodeKind::Regular => Self::File, + VnodeKind::Directory => Self::Directory, + VnodeKind::Block => Self::Block, + VnodeKind::Char => Self::Char, + } + } +} diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index 02385a64..2525daf1 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -232,13 +232,13 @@ pub fn kernel_main(dtb_phys: usize) -> ! { devfs::init(); PLATFORM.init(true).unwrap(); - let dt = ARCHITECTURE.dt.get(); - if let Err(e) = smp::start_ap_cores(dt) { - errorln!( - "Could not initialize AP CPUs: {:?}. Will continue with one CPU.", - e - ); - } + // let dt = ARCHITECTURE.dt.get(); + // if let Err(e) = smp::start_ap_cores(dt) { + // errorln!( + // "Could not initialize AP CPUs: {:?}. Will continue with one CPU.", + // e + // ); + // } Cpu::init_ipi_queues(); diff --git a/src/arch/aarch64/table.rs b/src/arch/aarch64/table.rs index 0a6fd586..c28e0b89 100644 --- a/src/arch/aarch64/table.rs +++ b/src/arch/aarch64/table.rs @@ -5,8 +5,10 @@ use core::{ sync::atomic::{AtomicU8, Ordering}, }; +use aarch64_cpu::registers::DAIF; use abi::error::Error; use bitflags::bitflags; +use tock_registers::interfaces::Readable; use crate::mem::{ phys::{self, PageUsage}, @@ -77,6 +79,8 @@ bitflags! { const AP_BOTH_READWRITE = 1 << 6; /// For page/block mappings, only allows read access for EL0/EL1 const AP_BOTH_READONLY = 3 << 6; + + const NON_GLOBAL = 1 << 11; } } @@ -301,8 +305,6 @@ impl FixedTables { } self.device_l3i += count; - tlb_flush_vaae1(virt); - Ok(virt) } } @@ -315,6 +317,9 @@ impl VirtualMemoryManager for AddressSpace { len: usize, attrs: PageAttributes, ) -> Result { + assert_eq!(DAIF.read(DAIF::I), 1); + + assert_eq!(len, 1); if hint.is_some() { todo!(); } @@ -341,12 +346,17 @@ impl VirtualMemoryManager for AddressSpace { } fn deallocate(&self, addr: usize, len: usize) -> Result<(), Error> { + assert_eq!(DAIF.read(DAIF::I), 1); + for page in (addr..addr + len).step_by(0x1000) { - let Some(_phys) = self.translate(page) else { + let Some(phys) = self.translate(page) else { todo!(); }; self.write_entry(page, PageEntry::INVALID, true)?; + unsafe { + phys::free_page(phys); + } } Ok(()) @@ -402,6 +412,8 @@ impl AddressSpace { } l3[l3i] = entry; + tlb_flush_vaae1(virt); + Ok(()) } @@ -416,11 +428,11 @@ impl AddressSpace { } } -/// Flushes the virtual address from TLB -pub fn tlb_flush_vaae1(page: usize) { - assert_eq!(page & 0xFFF, 0); +/// Flush a virtual address from EL1/EL0 TLB for all ASIDs +pub fn tlb_flush_vaae1(mut page: usize) { + page >>= 12; unsafe { - core::arch::asm!("tlbi vaae1, {addr}", addr = in(reg) page); + core::arch::asm!("tlbi vaae1, {page}", page = in(reg) page); } } diff --git a/src/mem/phys/manager.rs b/src/mem/phys/manager.rs index d38a6d16..34651016 100644 --- a/src/mem/phys/manager.rs +++ b/src/mem/phys/manager.rs @@ -78,6 +78,14 @@ impl PhysicalMemoryManager { Err(Error::OutOfMemory) } + pub unsafe fn free_page(&mut self, addr: usize) { + assert!(addr > self.offset); + let index = (addr - self.offset) / 0x1000; + let page = &mut self.pages[index]; + assert_eq!(page.usage, PageUsage::Used); + page.usage = PageUsage::Available; + } + /// Marks a previously reserved page as available. /// /// # Panics diff --git a/src/mem/phys/mod.rs b/src/mem/phys/mod.rs index 1a2dc4ab..c5b6272b 100644 --- a/src/mem/phys/mod.rs +++ b/src/mem/phys/mod.rs @@ -10,6 +10,7 @@ use crate::{ phys::reserved::{is_reserved, reserve_region}, ConvertAddress, KERNEL_PHYS_BASE, }, + sync::IrqSafeSpinlock, util::OneTimeInit, }; @@ -64,7 +65,8 @@ impl PhysicalMemoryRegion { } /// Global physical memory manager -pub static PHYSICAL_MEMORY: OneTimeInit> = OneTimeInit::new(); +pub static PHYSICAL_MEMORY: OneTimeInit> = + OneTimeInit::new(); /// Allocates a single physical page from the global manager pub fn alloc_page(usage: PageUsage) -> Result { @@ -79,6 +81,10 @@ pub fn alloc_pages_contiguous(count: usize, usage: PageUsage) -> Result>( it: I, ) -> Option<(usize, usize)> { @@ -182,7 +188,7 @@ pub unsafe fn init_from_iter + Clone>( infoln!("{} available pages", page_count); - PHYSICAL_MEMORY.init(Spinlock::new(manager)); + PHYSICAL_MEMORY.init(IrqSafeSpinlock::new(manager)); Ok(()) } diff --git a/src/proc/elf.rs b/src/proc/elf.rs index c74b6d26..4eac3817 100644 --- a/src/proc/elf.rs +++ b/src/proc/elf.rs @@ -73,7 +73,7 @@ where (_, 0) => PageAttributes::AP_BOTH_READWRITE, (0, _) => PageAttributes::AP_BOTH_READONLY, (_, _) => PageAttributes::AP_BOTH_READWRITE, - }; + } | PageAttributes::NON_GLOBAL; let dst_page_off = addr & 0xFFF; let dst_page_aligned = addr & !0xFFF; diff --git a/src/proc/exec.rs b/src/proc/exec.rs index 7e038341..7b506315 100644 --- a/src/proc/exec.rs +++ b/src/proc/exec.rs @@ -32,7 +32,11 @@ fn setup_args(space: &AddressSpace, virt: usize, args: &[&str]) -> Result<(), Er let phys_page = phys::alloc_page(PageUsage::Used)?; // TODO check if this doesn't overwrite anything - space.map_page(virt, phys_page, PageAttributes::AP_BOTH_READWRITE)?; + space.map_page( + virt, + phys_page, + PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL, + )?; let write = unsafe { phys_page.virtualize() }; @@ -70,7 +74,7 @@ fn setup_args(space: &AddressSpace, virt: usize, args: &[&str]) -> Result<(), Er fn setup_binary(space: AddressSpace, entry: usize, args: &[&str]) -> Result, Error> { const USER_STACK_PAGES: usize = 8; - let virt_stack_base = 0x10000000; + let virt_stack_base = 0x3000000; // 0x1000 of guard page let virt_args_base = virt_stack_base + (USER_STACK_PAGES + 1) * 0x1000; @@ -79,7 +83,7 @@ fn setup_binary(space: AddressSpace, entry: usize, args: &[&str]) -> Result(base: usize, len: usize) -> Result<&'a [u8], Error> { @@ -18,6 +19,9 @@ pub(super) fn arg_buffer_mut<'a>(base: usize, len: usize) -> Result<&'a mut [u8] pub(super) fn arg_user_str<'a>(base: usize, len: usize) -> Result<&'a str, Error> { let slice = arg_buffer_ref(base, len)?; + if slice.contains(&0) { + todo!("Incorrect ptr: {:#x}", base); + } Ok(core::str::from_utf8(slice).unwrap()) } @@ -30,3 +34,29 @@ pub(super) fn arg_user_ref<'a, T: Sized>(addr: usize) -> Result<&'a T, Error> { let value = unsafe { core::mem::transmute::<_, &'a T>(addr) }; Ok(value) } + +pub(super) fn arg_user_slice_mut<'a, T: Sized>( + base: usize, + count: usize, +) -> Result<&'a mut [T], Error> { + let layout = Layout::array::(count).unwrap(); + + if base % layout.align() != 0 { + todo!("Misaligned buffer"); + } + + if base + layout.size() > crate::mem::KERNEL_VIRT_OFFSET { + panic!("Invalid argument"); + } + + let slice = unsafe { core::slice::from_raw_parts_mut(base as *mut _, count) }; + Ok(slice) +} + +pub(super) fn arg_option_fd(raw: u32) -> Option { + if raw == u32::MAX { + None + } else { + Some(RawFd(raw)) + } +} diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 6c39c17c..966f369f 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -1,12 +1,12 @@ //! System function call handlers -use core::time::Duration; +use core::{mem::MaybeUninit, sync::atomic::AtomicUsize, time::Duration}; use abi::{ error::Error, - io::{FileMode, OpenOptions, RawFd}, + io::{DirectoryEntry, FileMode, OpenOptions, RawFd}, syscall::SyscallFunction, }; -use vfs::{Read, Write}; +use vfs::{Read, ReadDirectory, Write}; use yggdrasil_abi::{ error::SyscallResult, io::{MountOptions, UnmountOptions}, @@ -58,10 +58,11 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result todo!(); } - let addr = space.allocate(None, len / 0x1000, PageAttributes::AP_BOTH_READWRITE); - debugln!("mmap({:#x}) = {:x?}", len, addr); - - addr + space.allocate( + None, + len / 0x1000, + PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL, + ) } SyscallFunction::UnmapMemory => { let addr = args[0] as usize; @@ -74,7 +75,6 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result todo!(); } - debugln!("munmap({:#x}, {:#x})", addr, len); space.deallocate(addr, len)?; Ok(0) @@ -154,6 +154,37 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result Ok(0) } + SyscallFunction::OpenDirectory => { + let at = arg_option_fd(args[0] as u32); + let path = arg_user_str(args[1] as usize, args[2] as usize)?; + + let proc = Process::current(); + let mut io = proc.io.lock(); + + // TODO handle at + assert!(at.is_none()); + + let node = io.ioctx().find(None, path, true, true)?; + let file = node.open_directory()?; + let fd = io.place_file(file)?; + + Ok(fd.0 as usize) + } + SyscallFunction::ReadDirectory => { + let fd = RawFd(args[0] as u32); + let buffer = arg_user_slice_mut::>( + args[1] as usize, + args[2] as usize, + )?; + + let proc = Process::current(); + let mut io = proc.io.lock(); + + let file = io.file(fd)?; + let mut file_borrow = file.borrow_mut(); + + file_borrow.read_dir_entries(buffer) + } } }