***: fix incorrect tlbi + add Read/OpenDirectory

This commit is contained in:
Mark Poliakov 2023-07-22 00:39:08 +03:00
parent 64233db3df
commit 867edf7748
12 changed files with 277 additions and 35 deletions

View File

@ -2,8 +2,11 @@ use core::marker::PhantomData;
use alloc::boxed::Box; use alloc::boxed::Box;
use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef}; use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef, DIR_POSITION_FROM_CACHE};
use yggdrasil_abi::error::Error; use yggdrasil_abi::{
error::Error,
io::{FileMode, OpenOptions},
};
use crate::{block::BlockAllocator, bvec::BVec, file::FileNode}; use crate::{block::BlockAllocator, bvec::BVec, file::FileNode};
@ -23,6 +26,15 @@ impl<A: BlockAllocator> VnodeImpl for DirectoryNode<A> {
} }
Ok(child) Ok(child)
} }
fn open(
&mut self,
_node: &VnodeRef,
_opts: OpenOptions,
_mode: FileMode,
) -> Result<u64, Error> {
Ok(DIR_POSITION_FROM_CACHE)
}
} }
impl<A: BlockAllocator> DirectoryNode<A> { impl<A: BlockAllocator> DirectoryNode<A> {

View File

@ -1,12 +1,15 @@
use core::cell::RefCell; use core::{cell::RefCell, mem::MaybeUninit};
use alloc::rc::Rc; use alloc::rc::Rc;
use bitflags::bitflags; use bitflags::bitflags;
use yggdrasil_abi::error::Error; use yggdrasil_abi::{
error::Error,
io::{DirectoryEntry, FileType},
};
use crate::{ use crate::{
node::{VnodeKind, VnodeRef}, node::{VnodeKind, VnodeRef},
Read, Seek, SeekFrom, Write, Read, ReadDirectory, Seek, SeekFrom, Write, DIR_POSITION_FROM_CACHE,
}; };
bitflags! { bitflags! {
@ -18,13 +21,28 @@ bitflags! {
pub type FileRef = Rc<RefCell<File>>; pub type FileRef = Rc<RefCell<File>>;
enum DirectoryPosition {
DiskPosition(u64),
// TODO not the best implementation, but at least somewhat safe?
CachePosition(usize),
Dot,
DotDot,
End,
}
pub struct NormalFile { pub struct NormalFile {
vnode: VnodeRef, vnode: VnodeRef,
pos: u64, pos: u64,
} }
pub struct Directory {
vnode: VnodeRef,
pos: DirectoryPosition,
}
pub enum FileInner { pub enum FileInner {
Normal(NormalFile), Normal(NormalFile),
Directory(Directory),
} }
pub struct File { pub struct File {
@ -39,6 +57,20 @@ impl File {
flags, 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 { impl Write for File {
@ -55,6 +87,7 @@ impl Write for File {
} }
Ok(count) Ok(count)
} }
FileInner::Directory(_) => unimplemented!(),
} }
} }
} }
@ -73,6 +106,7 @@ impl Read for File {
} }
Ok(count) Ok(count)
} }
FileInner::Directory(_) => Err(Error::IsADirectory),
} }
} }
} }
@ -98,16 +132,78 @@ impl Seek for File {
Ok(pos) Ok(pos)
} }
FileInner::Directory(_) => todo!(),
} }
} }
} }
impl ReadDirectory for File {
fn read_dir_entries(
&mut self,
entries: &mut [MaybeUninit<DirectoryEntry>],
) -> Result<usize, Error> {
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 { impl Drop for File {
fn drop(&mut self) { fn drop(&mut self) {
match &mut self.inner { match &mut self.inner {
FileInner::Normal(inner) => { FileInner::Normal(inner) => {
inner.vnode.close().ok(); inner.vnode.close().ok();
} }
FileInner::Directory(inner) => {
inner.vnode.close().ok();
}
} }
} }
} }

View File

@ -1,6 +1,9 @@
#![no_std] #![no_std]
use core::mem::MaybeUninit;
use yggdrasil_abi::error::Error; use yggdrasil_abi::error::Error;
use yggdrasil_abi::io::DirectoryEntry;
extern crate alloc; extern crate alloc;
@ -24,6 +27,8 @@ pub use fs::Filesystem;
pub use ioctx::IoContext; pub use ioctx::IoContext;
pub use node::{Vnode, VnodeDump, VnodeImpl, VnodeKind, VnodeRef, VnodeWeak}; pub use node::{Vnode, VnodeDump, VnodeImpl, VnodeKind, VnodeRef, VnodeWeak};
pub const DIR_POSITION_FROM_CACHE: u64 = u64::MAX;
#[derive(Debug)] #[derive(Debug)]
pub enum SeekFrom { pub enum SeekFrom {
Start(u64), Start(u64),
@ -47,6 +52,13 @@ pub trait Seek {
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error>; fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error>;
} }
pub trait ReadDirectory {
fn read_dir_entries(
&mut self,
entries: &mut [MaybeUninit<DirectoryEntry>],
) -> Result<usize, Error>;
}
fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [u8]) -> Result<(), Error> { fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [u8]) -> Result<(), Error> {
while !buf.is_empty() { while !buf.is_empty() {
match this.read(buf) { match this.read(buf) {

View File

@ -11,12 +11,13 @@ use alloc::{
}; };
use yggdrasil_abi::{ use yggdrasil_abi::{
error::Error, error::Error,
io::{FileMode, OpenOptions}, io::{FileMode, FileType, OpenOptions},
}; };
use crate::{ use crate::{
file::{File, FileFlags, FileRef}, file::{File, FileFlags, FileRef},
fs::Filesystem, fs::Filesystem,
DIR_POSITION_FROM_CACHE,
}; };
pub type VnodeRef = Rc<Vnode>; pub type VnodeRef = Rc<Vnode>;
@ -163,6 +164,11 @@ impl Vnode {
parent_borrow.children.push(child); parent_borrow.children.push(child);
} }
pub fn child_at(self: &VnodeRef, index: usize) -> Option<VnodeRef> {
let tree = self.tree.borrow();
tree.children.get(index).cloned()
}
pub fn dump(&self, f: &mut fmt::Formatter<'_>, depth: usize, indent: bool) -> fmt::Result { pub fn dump(&self, f: &mut fmt::Formatter<'_>, depth: usize, indent: bool) -> fmt::Result {
if indent { if indent {
for _ in 0..depth { for _ in 0..depth {
@ -244,11 +250,25 @@ impl Vnode {
} }
} }
pub fn open_directory(self: &VnodeRef) -> Result<FileRef, Error> {
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> { pub fn close(self: &VnodeRef) -> Result<(), Error> {
if let Some(ref mut data) = *self.data() { if let Some(ref mut data) = *self.data() {
data.close(self) data.close(self)
} else { } else {
todo!() Ok(())
} }
} }
@ -377,3 +397,14 @@ impl fmt::Debug for VnodeDump {
self.node.dump(f, 0, true) self.node.dump(f, 0, true)
} }
} }
impl From<VnodeKind> 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,
}
}
}

View File

@ -232,13 +232,13 @@ pub fn kernel_main(dtb_phys: usize) -> ! {
devfs::init(); devfs::init();
PLATFORM.init(true).unwrap(); PLATFORM.init(true).unwrap();
let dt = ARCHITECTURE.dt.get(); // let dt = ARCHITECTURE.dt.get();
if let Err(e) = smp::start_ap_cores(dt) { // if let Err(e) = smp::start_ap_cores(dt) {
errorln!( // errorln!(
"Could not initialize AP CPUs: {:?}. Will continue with one CPU.", // "Could not initialize AP CPUs: {:?}. Will continue with one CPU.",
e // e
); // );
} // }
Cpu::init_ipi_queues(); Cpu::init_ipi_queues();

View File

@ -5,8 +5,10 @@ use core::{
sync::atomic::{AtomicU8, Ordering}, sync::atomic::{AtomicU8, Ordering},
}; };
use aarch64_cpu::registers::DAIF;
use abi::error::Error; use abi::error::Error;
use bitflags::bitflags; use bitflags::bitflags;
use tock_registers::interfaces::Readable;
use crate::mem::{ use crate::mem::{
phys::{self, PageUsage}, phys::{self, PageUsage},
@ -77,6 +79,8 @@ bitflags! {
const AP_BOTH_READWRITE = 1 << 6; const AP_BOTH_READWRITE = 1 << 6;
/// For page/block mappings, only allows read access for EL0/EL1 /// For page/block mappings, only allows read access for EL0/EL1
const AP_BOTH_READONLY = 3 << 6; const AP_BOTH_READONLY = 3 << 6;
const NON_GLOBAL = 1 << 11;
} }
} }
@ -301,8 +305,6 @@ impl FixedTables {
} }
self.device_l3i += count; self.device_l3i += count;
tlb_flush_vaae1(virt);
Ok(virt) Ok(virt)
} }
} }
@ -315,6 +317,9 @@ impl VirtualMemoryManager for AddressSpace {
len: usize, len: usize,
attrs: PageAttributes, attrs: PageAttributes,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
assert_eq!(DAIF.read(DAIF::I), 1);
assert_eq!(len, 1);
if hint.is_some() { if hint.is_some() {
todo!(); todo!();
} }
@ -341,12 +346,17 @@ impl VirtualMemoryManager for AddressSpace {
} }
fn deallocate(&self, addr: usize, len: usize) -> Result<(), Error> { 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) { for page in (addr..addr + len).step_by(0x1000) {
let Some(_phys) = self.translate(page) else { let Some(phys) = self.translate(page) else {
todo!(); todo!();
}; };
self.write_entry(page, PageEntry::INVALID, true)?; self.write_entry(page, PageEntry::INVALID, true)?;
unsafe {
phys::free_page(phys);
}
} }
Ok(()) Ok(())
@ -402,6 +412,8 @@ impl AddressSpace {
} }
l3[l3i] = entry; l3[l3i] = entry;
tlb_flush_vaae1(virt);
Ok(()) Ok(())
} }
@ -416,11 +428,11 @@ impl AddressSpace {
} }
} }
/// Flushes the virtual address from TLB /// Flush a virtual address from EL1/EL0 TLB for all ASIDs
pub fn tlb_flush_vaae1(page: usize) { pub fn tlb_flush_vaae1(mut page: usize) {
assert_eq!(page & 0xFFF, 0); page >>= 12;
unsafe { unsafe {
core::arch::asm!("tlbi vaae1, {addr}", addr = in(reg) page); core::arch::asm!("tlbi vaae1, {page}", page = in(reg) page);
} }
} }

View File

@ -78,6 +78,14 @@ impl PhysicalMemoryManager {
Err(Error::OutOfMemory) 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. /// Marks a previously reserved page as available.
/// ///
/// # Panics /// # Panics

View File

@ -10,6 +10,7 @@ use crate::{
phys::reserved::{is_reserved, reserve_region}, phys::reserved::{is_reserved, reserve_region},
ConvertAddress, KERNEL_PHYS_BASE, ConvertAddress, KERNEL_PHYS_BASE,
}, },
sync::IrqSafeSpinlock,
util::OneTimeInit, util::OneTimeInit,
}; };
@ -64,7 +65,8 @@ impl PhysicalMemoryRegion {
} }
/// Global physical memory manager /// Global physical memory manager
pub static PHYSICAL_MEMORY: OneTimeInit<Spinlock<PhysicalMemoryManager>> = OneTimeInit::new(); pub static PHYSICAL_MEMORY: OneTimeInit<IrqSafeSpinlock<PhysicalMemoryManager>> =
OneTimeInit::new();
/// Allocates a single physical page from the global manager /// Allocates a single physical page from the global manager
pub fn alloc_page(usage: PageUsage) -> Result<usize, Error> { pub fn alloc_page(usage: PageUsage) -> Result<usize, Error> {
@ -79,6 +81,10 @@ pub fn alloc_pages_contiguous(count: usize, usage: PageUsage) -> Result<usize, E
.alloc_contiguous_pages(count, usage) .alloc_contiguous_pages(count, usage)
} }
pub unsafe fn free_page(addr: usize) {
PHYSICAL_MEMORY.get().lock().free_page(addr)
}
fn physical_memory_range<I: Iterator<Item = PhysicalMemoryRegion>>( fn physical_memory_range<I: Iterator<Item = PhysicalMemoryRegion>>(
it: I, it: I,
) -> Option<(usize, usize)> { ) -> Option<(usize, usize)> {
@ -182,7 +188,7 @@ pub unsafe fn init_from_iter<I: Iterator<Item = PhysicalMemoryRegion> + Clone>(
infoln!("{} available pages", page_count); infoln!("{} available pages", page_count);
PHYSICAL_MEMORY.init(Spinlock::new(manager)); PHYSICAL_MEMORY.init(IrqSafeSpinlock::new(manager));
Ok(()) Ok(())
} }

View File

@ -73,7 +73,7 @@ where
(_, 0) => PageAttributes::AP_BOTH_READWRITE, (_, 0) => PageAttributes::AP_BOTH_READWRITE,
(0, _) => PageAttributes::AP_BOTH_READONLY, (0, _) => PageAttributes::AP_BOTH_READONLY,
(_, _) => PageAttributes::AP_BOTH_READWRITE, (_, _) => PageAttributes::AP_BOTH_READWRITE,
}; } | PageAttributes::NON_GLOBAL;
let dst_page_off = addr & 0xFFF; let dst_page_off = addr & 0xFFF;
let dst_page_aligned = addr & !0xFFF; let dst_page_aligned = addr & !0xFFF;

View File

@ -32,7 +32,11 @@ fn setup_args(space: &AddressSpace, virt: usize, args: &[&str]) -> Result<(), Er
let phys_page = phys::alloc_page(PageUsage::Used)?; let phys_page = phys::alloc_page(PageUsage::Used)?;
// TODO check if this doesn't overwrite anything // 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() }; 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<Rc<Process>, Error> { fn setup_binary(space: AddressSpace, entry: usize, args: &[&str]) -> Result<Rc<Process>, Error> {
const USER_STACK_PAGES: usize = 8; const USER_STACK_PAGES: usize = 8;
let virt_stack_base = 0x10000000; let virt_stack_base = 0x3000000;
// 0x1000 of guard page // 0x1000 of guard page
let virt_args_base = virt_stack_base + (USER_STACK_PAGES + 1) * 0x1000; 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<Rc<P
space.map_page( space.map_page(
virt_stack_base + i * 0x1000, virt_stack_base + i * 0x1000,
phys, phys,
PageAttributes::AP_BOTH_READWRITE, PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL,
)?; )?;
} }

View File

@ -1,5 +1,6 @@
use core::alloc::Layout; use core::alloc::Layout;
use abi::io::RawFd;
use yggdrasil_abi::error::Error; use yggdrasil_abi::error::Error;
pub(super) fn arg_buffer_ref<'a>(base: usize, len: usize) -> Result<&'a [u8], Error> { pub(super) fn arg_buffer_ref<'a>(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> { pub(super) fn arg_user_str<'a>(base: usize, len: usize) -> Result<&'a str, Error> {
let slice = arg_buffer_ref(base, len)?; let slice = arg_buffer_ref(base, len)?;
if slice.contains(&0) {
todo!("Incorrect ptr: {:#x}", base);
}
Ok(core::str::from_utf8(slice).unwrap()) 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) }; let value = unsafe { core::mem::transmute::<_, &'a T>(addr) };
Ok(value) 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::<T>(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<RawFd> {
if raw == u32::MAX {
None
} else {
Some(RawFd(raw))
}
}

View File

@ -1,12 +1,12 @@
//! System function call handlers //! System function call handlers
use core::time::Duration; use core::{mem::MaybeUninit, sync::atomic::AtomicUsize, time::Duration};
use abi::{ use abi::{
error::Error, error::Error,
io::{FileMode, OpenOptions, RawFd}, io::{DirectoryEntry, FileMode, OpenOptions, RawFd},
syscall::SyscallFunction, syscall::SyscallFunction,
}; };
use vfs::{Read, Write}; use vfs::{Read, ReadDirectory, Write};
use yggdrasil_abi::{ use yggdrasil_abi::{
error::SyscallResult, error::SyscallResult,
io::{MountOptions, UnmountOptions}, io::{MountOptions, UnmountOptions},
@ -58,10 +58,11 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
todo!(); todo!();
} }
let addr = space.allocate(None, len / 0x1000, PageAttributes::AP_BOTH_READWRITE); space.allocate(
debugln!("mmap({:#x}) = {:x?}", len, addr); None,
len / 0x1000,
addr PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL,
)
} }
SyscallFunction::UnmapMemory => { SyscallFunction::UnmapMemory => {
let addr = args[0] as usize; let addr = args[0] as usize;
@ -74,7 +75,6 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
todo!(); todo!();
} }
debugln!("munmap({:#x}, {:#x})", addr, len);
space.deallocate(addr, len)?; space.deallocate(addr, len)?;
Ok(0) Ok(0)
@ -154,6 +154,37 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
Ok(0) 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::<MaybeUninit<DirectoryEntry>>(
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)
}
} }
} }