***: 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 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<A: BlockAllocator> VnodeImpl for DirectoryNode<A> {
}
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> {

View File

@ -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<RefCell<File>>;
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<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 {
fn drop(&mut self) {
match &mut self.inner {
FileInner::Normal(inner) => {
inner.vnode.close().ok();
}
FileInner::Directory(inner) => {
inner.vnode.close().ok();
}
}
}
}

View File

@ -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<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> {
while !buf.is_empty() {
match this.read(buf) {

View File

@ -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<Vnode>;
@ -163,6 +164,11 @@ impl Vnode {
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 {
if indent {
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> {
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<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();
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();

View File

@ -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<usize, Error> {
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);
}
}

View File

@ -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

View File

@ -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<Spinlock<PhysicalMemoryManager>> = OneTimeInit::new();
pub static PHYSICAL_MEMORY: OneTimeInit<IrqSafeSpinlock<PhysicalMemoryManager>> =
OneTimeInit::new();
/// Allocates a single physical page from the global manager
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)
}
pub unsafe fn free_page(addr: usize) {
PHYSICAL_MEMORY.get().lock().free_page(addr)
}
fn physical_memory_range<I: Iterator<Item = PhysicalMemoryRegion>>(
it: I,
) -> Option<(usize, usize)> {
@ -182,7 +188,7 @@ pub unsafe fn init_from_iter<I: Iterator<Item = PhysicalMemoryRegion> + Clone>(
infoln!("{} available pages", page_count);
PHYSICAL_MEMORY.init(Spinlock::new(manager));
PHYSICAL_MEMORY.init(IrqSafeSpinlock::new(manager));
Ok(())
}

View File

@ -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;

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)?;
// 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<Rc<Process>, 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<Rc<P
space.map_page(
virt_stack_base + i * 0x1000,
phys,
PageAttributes::AP_BOTH_READWRITE,
PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL,
)?;
}

View File

@ -1,5 +1,6 @@
use core::alloc::Layout;
use abi::io::RawFd;
use yggdrasil_abi::error::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> {
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::<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
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<usize, Error>
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<usize, Error>
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<usize, Error>
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)
}
}
}