Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b844a8774 | |||
| a7a1639ff7 | |||
| 0f9f8fc1bf | |||
| 97c591f58f | |||
| d45a8adc34 | |||
| 01da9e7ee5 | |||
| a82751c146 |
Generated
+2
-2
@@ -22,9 +22,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-a"
|
||||
version = "6.1.0"
|
||||
version = "7.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "509fc35485a2b4ddbacabe0bf2212cdfff88da93658608e5cc651afcb75b7733"
|
||||
checksum = "5bd95fd055d118f77d4e4d527201b6ceccd13586b19b4dac1270f7081fef0f98"
|
||||
dependencies = [
|
||||
"tock-registers",
|
||||
]
|
||||
|
||||
@@ -45,6 +45,7 @@ endif
|
||||
ifeq ($(MACH),rpi3)
|
||||
QEMU_OPTS+=-kernel $(O)/kernel.bin \
|
||||
-initrd $(O)/initrd.img \
|
||||
-dtb etc/bcm2837-rpi-3-b-plus.dtb \
|
||||
-M raspi3b \
|
||||
-serial mon:stdio \
|
||||
-display none \
|
||||
@@ -103,6 +104,7 @@ initrd:
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/ls $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/cat $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/hexd $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/mkdir $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/login $(O)/rootfs/sbin
|
||||
cd $(O)/rootfs && tar cf ../initrd.img `find -type f -printf "%P\n"`
|
||||
ifeq ($(MACH),orangepi3)
|
||||
|
||||
Binary file not shown.
+161
-161
@@ -1,162 +1,162 @@
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
// extern crate proc_macro;
|
||||
// extern crate syn;
|
||||
// #[macro_use]
|
||||
// extern crate quote;
|
||||
//
|
||||
// use proc_macro::TokenStream;
|
||||
// use quote::ToTokens;
|
||||
// use std::collections::HashSet;
|
||||
// use syn::{parse_macro_input, ImplItem, ItemImpl, Ident};
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use std::collections::HashSet;
|
||||
use syn::{parse_macro_input, ImplItem, ItemImpl, Ident};
|
||||
|
||||
fn impl_inode_fn<T: ToTokens>(name: &str, behavior: T) -> ImplItem {
|
||||
// TODO somehow know if current crate is vfs or not?
|
||||
ImplItem::Verbatim(match name {
|
||||
"create" => quote! {
|
||||
fn create(&mut self, _at: VnodeRef, _name: &str, kind: VnodeKind) ->
|
||||
Result<VnodeRef, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"remove" => quote! {
|
||||
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), libsys::error::Errno> {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"lookup" => quote! {
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) ->
|
||||
Result<VnodeRef, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"stat" => quote! {
|
||||
fn stat(&mut self, _at: VnodeRef) ->
|
||||
Result<libsys::stat::Stat, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"truncate" => quote! {
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) ->
|
||||
Result<(), libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"size" => quote! {
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, libsys::error::Errno> {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"read" => quote! {
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"write" => quote! {
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"open" => quote! {
|
||||
fn open(&mut self, _node: VnodeRef, _flags: libsys::stat::OpenFlags) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"close" => quote! {
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), libsys::error::Errno> {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"ioctl" => quote! {
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_cmd: libsys::ioctl::IoctlCmd,
|
||||
_ptr: usize,
|
||||
_len: usize) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"is_ready" => quote! {
|
||||
fn is_ready(&mut self, _node: VnodeRef, _write: bool) ->
|
||||
Result<bool, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"readdir" => quote! {
|
||||
fn readdir(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_pos: usize,
|
||||
_entries: &mut [libsys::stat::DirectoryEntry]
|
||||
) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
_ => panic!("TODO implement {:?}", name),
|
||||
})
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn auto_inode(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut impl_item = parse_macro_input!(input as ItemImpl);
|
||||
let mut missing = HashSet::<String>::new();
|
||||
let behavior = if attr.is_empty() {
|
||||
"unimplemented".to_string()
|
||||
} else {
|
||||
parse_macro_input!(attr as Ident).to_string()
|
||||
};
|
||||
let behavior = match behavior.as_str() {
|
||||
"unimplemented" => quote! { unimplemented!() },
|
||||
"panic" => quote! { panic!() },
|
||||
"error" => quote! { Err(libsys::error::Errno::NotImplemented) },
|
||||
_ => panic!("Unknown #[auto_inode] behavior: {:?}", behavior)
|
||||
};
|
||||
|
||||
missing.insert("create".to_string());
|
||||
missing.insert("remove".to_string());
|
||||
missing.insert("lookup".to_string());
|
||||
missing.insert("open".to_string());
|
||||
missing.insert("close".to_string());
|
||||
missing.insert("truncate".to_string());
|
||||
missing.insert("read".to_string());
|
||||
missing.insert("write".to_string());
|
||||
missing.insert("stat".to_string());
|
||||
missing.insert("size".to_string());
|
||||
missing.insert("ioctl".to_string());
|
||||
missing.insert("is_ready".to_string());
|
||||
missing.insert("readdir".to_string());
|
||||
|
||||
for item in &impl_item.items {
|
||||
match item {
|
||||
ImplItem::Method(method) => {
|
||||
let name = &method.sig.ident.to_string();
|
||||
if missing.contains(name) {
|
||||
missing.remove(name);
|
||||
}
|
||||
}
|
||||
_ => panic!("Unexpected impl item"),
|
||||
}
|
||||
}
|
||||
|
||||
for item in &missing {
|
||||
impl_item
|
||||
.items
|
||||
.push(impl_inode_fn(item, behavior.clone()));
|
||||
}
|
||||
|
||||
impl_item.to_token_stream().into()
|
||||
}
|
||||
//fn impl_inode_fn<T: ToTokens>(name: &str, behavior: T) -> ImplItem {
|
||||
// // TODO somehow know if current crate is vfs or not?
|
||||
// ImplItem::Verbatim(match name {
|
||||
// "create" => quote! {
|
||||
// fn create(&mut self, _at: VnodeRef, _name: &str, kind: VnodeKind) ->
|
||||
// Result<VnodeRef, libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "remove" => quote! {
|
||||
// fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), libsys::error::Errno> {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "lookup" => quote! {
|
||||
// fn lookup(&mut self, _at: VnodeRef, _name: &str) ->
|
||||
// Result<VnodeRef, libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "stat" => quote! {
|
||||
// fn stat(&mut self, _at: VnodeRef) ->
|
||||
// Result<libsys::stat::Stat, libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "truncate" => quote! {
|
||||
// fn truncate(&mut self, _node: VnodeRef, _size: usize) ->
|
||||
// Result<(), libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "size" => quote! {
|
||||
// fn size(&mut self, _node: VnodeRef) -> Result<usize, libsys::error::Errno> {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "read" => quote! {
|
||||
// fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) ->
|
||||
// Result<usize, libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "write" => quote! {
|
||||
// fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) ->
|
||||
// Result<usize, libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "open" => quote! {
|
||||
// fn open(&mut self, _node: VnodeRef, _flags: libsys::stat::OpenFlags) ->
|
||||
// Result<usize, libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "close" => quote! {
|
||||
// fn close(&mut self, _node: VnodeRef) -> Result<(), libsys::error::Errno> {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "ioctl" => quote! {
|
||||
// fn ioctl(
|
||||
// &mut self,
|
||||
// _node: VnodeRef,
|
||||
// _cmd: libsys::ioctl::IoctlCmd,
|
||||
// _ptr: usize,
|
||||
// _len: usize) ->
|
||||
// Result<usize, libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "is_ready" => quote! {
|
||||
// fn is_ready(&mut self, _node: VnodeRef, _write: bool) ->
|
||||
// Result<bool, libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// "readdir" => quote! {
|
||||
// fn readdir(
|
||||
// &mut self,
|
||||
// _node: VnodeRef,
|
||||
// _pos: usize,
|
||||
// _entries: &mut [libsys::stat::DirectoryEntry]
|
||||
// ) ->
|
||||
// Result<usize, libsys::error::Errno>
|
||||
// {
|
||||
// #behavior
|
||||
// }
|
||||
// },
|
||||
// _ => panic!("TODO implement {:?}", name),
|
||||
// })
|
||||
//}
|
||||
//
|
||||
//#[proc_macro_attribute]
|
||||
//pub fn auto_inode(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
// let mut impl_item = parse_macro_input!(input as ItemImpl);
|
||||
// let mut missing = HashSet::<String>::new();
|
||||
// let behavior = if attr.is_empty() {
|
||||
// "unimplemented".to_string()
|
||||
// } else {
|
||||
// parse_macro_input!(attr as Ident).to_string()
|
||||
// };
|
||||
// let behavior = match behavior.as_str() {
|
||||
// "unimplemented" => quote! { unimplemented!() },
|
||||
// "panic" => quote! { panic!() },
|
||||
// "error" => quote! { Err(libsys::error::Errno::NotImplemented) },
|
||||
// _ => panic!("Unknown #[auto_inode] behavior: {:?}", behavior)
|
||||
// };
|
||||
//
|
||||
// missing.insert("create".to_string());
|
||||
// missing.insert("remove".to_string());
|
||||
// missing.insert("lookup".to_string());
|
||||
// missing.insert("open".to_string());
|
||||
// missing.insert("close".to_string());
|
||||
// missing.insert("truncate".to_string());
|
||||
// missing.insert("read".to_string());
|
||||
// missing.insert("write".to_string());
|
||||
// missing.insert("stat".to_string());
|
||||
// missing.insert("size".to_string());
|
||||
// missing.insert("ioctl".to_string());
|
||||
// missing.insert("is_ready".to_string());
|
||||
// missing.insert("readdir".to_string());
|
||||
//
|
||||
// for item in &impl_item.items {
|
||||
// match item {
|
||||
// ImplItem::Method(method) => {
|
||||
// let name = &method.sig.ident.to_string();
|
||||
// if missing.contains(name) {
|
||||
// missing.remove(name);
|
||||
// }
|
||||
// }
|
||||
// _ => panic!("Unexpected impl item"),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for item in &missing {
|
||||
// impl_item
|
||||
// .items
|
||||
// .push(impl_inode_fn(item, behavior.clone()));
|
||||
// }
|
||||
//
|
||||
// impl_item.to_token_stream().into()
|
||||
//}
|
||||
|
||||
@@ -11,6 +11,9 @@ pub struct BlockRef<'a, A: BlockAllocator + Copy> {
|
||||
alloc: MaybeUninit<A>,
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait is unsafe to implement due to its direct memory management
|
||||
pub unsafe trait BlockAllocator {
|
||||
fn alloc(&self) -> *mut u8;
|
||||
/// # Safety
|
||||
|
||||
+55
-12
@@ -1,27 +1,29 @@
|
||||
use crate::{BlockAllocator, Bvec, FileInode};
|
||||
use alloc::boxed::Box;
|
||||
use libsys::{error::Errno, stat::Stat};
|
||||
use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
use libsys::{error::Errno, stat::{Stat, DirectoryEntry, OpenFlags}, ioctl::IoctlCmd};
|
||||
use vfs::{Vnode, VnodeCommon, VnodeDirectory, VnodeRef, VnodeCreateKind, VnodeData};
|
||||
use core::cell::RefCell;
|
||||
|
||||
pub struct DirInode<A: BlockAllocator + Copy + 'static> {
|
||||
alloc: A,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
|
||||
impl<A: BlockAllocator + Copy + 'static> VnodeDirectory for DirInode<A> {
|
||||
fn create(
|
||||
&mut self,
|
||||
_parent: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
kind: VnodeCreateKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let vnode = Vnode::new(name, kind, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
match kind {
|
||||
VnodeKind::Directory => vnode.set_data(Box::new(DirInode { alloc: self.alloc })),
|
||||
VnodeKind::Regular => vnode.set_data(Box::new(FileInode::new(Bvec::new(self.alloc)))),
|
||||
_ => todo!(),
|
||||
}
|
||||
Ok(vnode)
|
||||
let data = match kind {
|
||||
VnodeCreateKind::Directory => VnodeData::Directory(RefCell::new(Some(Box::new(DirInode { alloc: self.alloc })))),
|
||||
VnodeCreateKind::File => VnodeData::File(RefCell::new(Some(Box::new(FileInode::new(Bvec::new(self.alloc)))))),
|
||||
};
|
||||
Ok(Vnode::new(name, data, Vnode::SEEKABLE | Vnode::CACHE_READDIR))
|
||||
// match kind {
|
||||
// _ => todo!(),
|
||||
// }
|
||||
// Ok(vnode)
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _parent: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
@@ -32,6 +34,18 @@ impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read directory entries into target buffer
|
||||
fn readdir(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_pos: usize,
|
||||
_data: &mut [DirectoryEntry],
|
||||
) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator + Copy + 'static> VnodeCommon for DirInode<A> {
|
||||
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
|
||||
let props = node.props();
|
||||
Ok(Stat {
|
||||
@@ -40,6 +54,35 @@ impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
|
||||
mode: props.mode,
|
||||
})
|
||||
}
|
||||
|
||||
/// Performs filetype-specific request
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_cmd: IoctlCmd,
|
||||
_ptr: usize,
|
||||
_len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Reports the size of this filesystem object in bytes
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns `true` if node is ready for an operation
|
||||
fn ready(&mut self, _node: VnodeRef, _write: bool) -> Result<bool, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator + Copy + 'static> DirInode<A> {
|
||||
|
||||
+35
-16
@@ -2,15 +2,16 @@ use crate::{BlockAllocator, Bvec};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::{OpenFlags, Stat},
|
||||
ioctl::IoctlCmd
|
||||
};
|
||||
use vfs::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
use vfs::{VnodeCommon, VnodeFile, VnodeRef};
|
||||
|
||||
pub struct FileInode<'a, A: BlockAllocator + Copy + 'static> {
|
||||
data: Bvec<'a, A>,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
|
||||
// #[auto_inode]
|
||||
impl<'a, A: BlockAllocator + Copy + 'static> VnodeCommon for FileInode<'a, A> {
|
||||
fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
@@ -18,19 +19,6 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
self.data.read(pos, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
self.data.write(pos, data)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, size: usize) -> Result<(), Errno> {
|
||||
self.data.resize((size + 4095) / 4096)
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
Ok(self.data.size())
|
||||
}
|
||||
@@ -43,6 +31,37 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
|
||||
mode: props.mode
|
||||
})
|
||||
}
|
||||
|
||||
/// Performs filetype-specific request
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_cmd: IoctlCmd,
|
||||
_ptr: usize,
|
||||
_len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns `true` if node is ready for an operation
|
||||
fn ready(&mut self, _node: VnodeRef, _write: bool) -> Result<bool, Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy + 'static> VnodeFile for FileInode<'a, A> {
|
||||
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
self.data.read(pos, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
self.data.write(pos, data)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, size: usize) -> Result<(), Errno> {
|
||||
self.data.resize((size + 4095) / 4096)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy + 'static> FileInode<'a, A> {
|
||||
|
||||
+29
-37
@@ -1,9 +1,4 @@
|
||||
#![feature(
|
||||
const_fn_trait_bound,
|
||||
const_mut_refs,
|
||||
maybe_uninit_extra,
|
||||
maybe_uninit_uninit_array
|
||||
)]
|
||||
#![feature(const_fn_trait_bound, const_mut_refs, maybe_uninit_uninit_array)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -11,9 +6,6 @@ extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[macro_use]
|
||||
extern crate fs_macros;
|
||||
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use core::any::Any;
|
||||
use core::cell::{Ref, RefCell};
|
||||
@@ -22,14 +14,14 @@ use libsys::{
|
||||
path::{path_component_left, path_component_right},
|
||||
stat::FileMode,
|
||||
};
|
||||
use vfs::{BlockDevice, Filesystem, Vnode, VnodeKind, VnodeRef};
|
||||
use vfs::{BlockDevice, Filesystem, Vnode, VnodeCreateKind, VnodeData, VnodeRef};
|
||||
|
||||
mod block;
|
||||
pub use block::{BlockAllocator, BlockRef};
|
||||
mod bvec;
|
||||
use bvec::Bvec;
|
||||
mod tar;
|
||||
use tar::{TarIterator, Tar};
|
||||
use tar::{Tar, TarIterator};
|
||||
mod file;
|
||||
use file::FileInode;
|
||||
mod dir;
|
||||
@@ -68,16 +60,16 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
}
|
||||
|
||||
fn create_node_initial(self: Rc<Self>, name: &str, tar: &Tar) -> VnodeRef {
|
||||
let kind = tar.node_kind();
|
||||
let node = Vnode::new(name, kind, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
node.props_mut().mode = tar.mode();
|
||||
node.set_fs(self.clone());
|
||||
match kind {
|
||||
VnodeKind::Directory => node.set_data(Box::new(DirInode::new(self.alloc))),
|
||||
VnodeKind::Regular => {}
|
||||
VnodeKind::Char => todo!(),
|
||||
VnodeKind::Block => todo!(),
|
||||
let kind = tar.node_create_kind();
|
||||
let data = match kind {
|
||||
VnodeCreateKind::Directory => {
|
||||
VnodeData::Directory(RefCell::new(Some(Box::new(DirInode::new(self.alloc)))))
|
||||
}
|
||||
VnodeCreateKind::File => VnodeData::File(RefCell::new(None)),
|
||||
};
|
||||
let node = Vnode::new(name, data, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
node.props_mut().mode = tar.mode();
|
||||
node.set_fs(self);
|
||||
node
|
||||
}
|
||||
|
||||
@@ -101,7 +93,8 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
return Err(Errno::DoesNotExist);
|
||||
}
|
||||
// TODO file modes
|
||||
at.create(element, FileMode::default_dir(), VnodeKind::Directory)?
|
||||
at.create(element, FileMode::default_dir(), VnodeCreateKind::Directory)?
|
||||
// todo!();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -113,9 +106,10 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
}
|
||||
|
||||
unsafe fn load_tar(self: Rc<Self>, base: *const u8, size: usize) -> Result<VnodeRef, Errno> {
|
||||
let root = Vnode::new("", VnodeKind::Directory, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
let root_data =
|
||||
VnodeData::Directory(RefCell::new(Some(Box::new(DirInode::new(self.alloc)))));
|
||||
let root = Vnode::new("", root_data, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
root.set_fs(self.clone());
|
||||
root.set_data(Box::new(DirInode::new(self.alloc)));
|
||||
root.props_mut().mode = FileMode::default_dir();
|
||||
|
||||
// 1. Create all the paths in TAR
|
||||
@@ -123,10 +117,7 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
let (dirname, basename) = path_component_right(block.path()?);
|
||||
|
||||
let parent = self.clone().make_path(root.clone(), dirname, true)?;
|
||||
let node = self
|
||||
.clone()
|
||||
.create_node_initial(basename, block);
|
||||
assert_eq!(node.kind(), block.node_kind());
|
||||
let node = self.clone().create_node_initial(basename, block);
|
||||
parent.attach(node);
|
||||
}
|
||||
|
||||
@@ -135,16 +126,17 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
if block.is_file() {
|
||||
// Will not create any dirs
|
||||
let node = self.clone().make_path(root.clone(), block.path()?, false)?;
|
||||
assert_eq!(node.kind(), block.node_kind());
|
||||
|
||||
#[cfg(feature = "cow")]
|
||||
{
|
||||
let data = block.data();
|
||||
node.set_data(Box::new(FileInode::new(Bvec::new_copy_on_write(
|
||||
self.alloc,
|
||||
data.as_ptr(),
|
||||
data.len(),
|
||||
))));
|
||||
node.as_file()
|
||||
.unwrap()
|
||||
.replace(Some(Box::new(FileInode::new(Bvec::new_copy_on_write(
|
||||
self.alloc,
|
||||
data.as_ptr(),
|
||||
data.len(),
|
||||
)))));
|
||||
}
|
||||
#[cfg(not(feature = "cow"))]
|
||||
{
|
||||
@@ -167,7 +159,7 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloc::boxed::Box;
|
||||
use libcommon::Read;
|
||||
use libsys::{traits::Read, stat::{UserId, GroupId, OpenFlags}};
|
||||
use vfs::Ioctx;
|
||||
|
||||
#[test]
|
||||
@@ -189,15 +181,15 @@ mod tests {
|
||||
let fs = unsafe { Ramfs::open(data.as_ptr(), data.bytes().len(), A {}).unwrap() };
|
||||
|
||||
let root = fs.root().unwrap();
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
let ioctx = Ioctx::new(root.clone(), UserId::root(), GroupId::root());
|
||||
|
||||
assert!(Rc::ptr_eq(&ioctx.find(None, "/", true).unwrap(), &root));
|
||||
|
||||
let node = ioctx.find(None, "/test1.txt", true).unwrap();
|
||||
let mut file = node.open().unwrap();
|
||||
let mut file = node.open(OpenFlags::O_RDONLY).unwrap();
|
||||
let mut buf = [0u8; 1024];
|
||||
|
||||
assert_eq!(file.read(&mut buf).unwrap(), 20);
|
||||
assert_eq!(file.borrow_mut().read(&mut buf).unwrap(), 20);
|
||||
let s = core::str::from_utf8(&buf[..20]).unwrap();
|
||||
assert_eq!(s, "This is a test file\n");
|
||||
}
|
||||
|
||||
+7
-8
@@ -1,5 +1,5 @@
|
||||
use libsys::{error::Errno, stat::FileMode};
|
||||
use vfs::VnodeKind;
|
||||
use vfs::VnodeCreateKind;
|
||||
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
@@ -73,19 +73,18 @@ impl Tar {
|
||||
core::str::from_utf8(&self.name[..zero_index]).map_err(|_| Errno::InvalidArgument)
|
||||
}
|
||||
|
||||
pub fn node_kind(&self) -> VnodeKind {
|
||||
pub fn node_create_kind(&self) -> VnodeCreateKind {
|
||||
match self.type_ {
|
||||
0 | b'0' => VnodeKind::Regular,
|
||||
b'5' => VnodeKind::Directory,
|
||||
0 | b'0' => VnodeCreateKind::File,
|
||||
b'5' => VnodeCreateKind::Directory,
|
||||
p => panic!("Unrecognized tar entry type: '{}'", p as char),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> FileMode {
|
||||
let t = match self.node_kind() {
|
||||
VnodeKind::Regular => FileMode::S_IFREG,
|
||||
VnodeKind::Directory => FileMode::S_IFDIR,
|
||||
_ => todo!()
|
||||
let t = match self.node_create_kind() {
|
||||
VnodeCreateKind::File => FileMode::S_IFREG,
|
||||
VnodeCreateKind::Directory => FileMode::S_IFDIR,
|
||||
};
|
||||
FileMode::from_bits(from_octal(&self.mode) as u32).unwrap() | t
|
||||
}
|
||||
|
||||
+1
-49
@@ -1,5 +1,4 @@
|
||||
use crate::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
use libsys::{error::Errno, ioctl::IoctlCmd, stat::OpenFlags};
|
||||
use libsys::{error::Errno, ioctl::IoctlCmd};
|
||||
|
||||
/// Generic character device trait
|
||||
pub trait CharDevice {
|
||||
@@ -22,50 +21,3 @@ pub trait CharDevice {
|
||||
/// Returns `true` if the device is ready for an operation
|
||||
fn is_ready(&self, write: bool) -> Result<bool, Errno>;
|
||||
}
|
||||
|
||||
/// Wrapper struct to attach [VnodeImpl] implementation
|
||||
/// to [CharDevice]s
|
||||
pub struct CharDeviceWrapper {
|
||||
device: &'static dyn CharDevice,
|
||||
}
|
||||
|
||||
#[auto_inode(error)]
|
||||
impl VnodeImpl for CharDeviceWrapper {
|
||||
fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
self.device.read(true, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
self.device.write(true, data)
|
||||
}
|
||||
|
||||
fn is_ready(&mut self, _node: VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
self.device.is_ready(write)
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
self.device.ioctl(cmd, ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDeviceWrapper {
|
||||
/// Creates a wrapper for static [CharDevice] trait object to
|
||||
/// auto-implement [VnodeImpl] trait for the device
|
||||
pub const fn new(device: &'static dyn CharDevice) -> Self {
|
||||
Self { device }
|
||||
}
|
||||
}
|
||||
|
||||
+54
-25
@@ -1,4 +1,4 @@
|
||||
use crate::{VnodeKind, VnodeRef, Vnode};
|
||||
use crate::{Vnode, VnodeRef};
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::RefCell;
|
||||
use core::cmp::min;
|
||||
@@ -39,9 +39,10 @@ impl Read for File {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
let count = inner.vnode.read(inner.pos, data)?;
|
||||
if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count;
|
||||
}
|
||||
// TODO
|
||||
// if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count;
|
||||
// }
|
||||
Ok(count)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
@@ -58,9 +59,10 @@ impl Write for File {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
let count = inner.vnode.write(inner.pos, data)?;
|
||||
if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count;
|
||||
}
|
||||
// TODO
|
||||
// if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count;
|
||||
// }
|
||||
Ok(count)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
@@ -127,14 +129,17 @@ impl File {
|
||||
}
|
||||
|
||||
/// Returns `true` if the file is ready for an operation
|
||||
pub fn is_ready(&self, write: bool) -> Result<bool, Errno> {
|
||||
pub fn ready(&self, write: bool) -> Result<bool, Errno> {
|
||||
match &self.inner {
|
||||
FileInner::Normal(inner) => inner.vnode.is_ready(write),
|
||||
FileInner::Normal(inner) => inner.vnode.ready(write),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cache_readdir(inner: &mut NormalFile, entries: &mut [DirectoryEntry]) -> Result<usize, Errno> {
|
||||
fn cache_readdir(
|
||||
inner: &mut NormalFile,
|
||||
entries: &mut [DirectoryEntry],
|
||||
) -> Result<usize, Errno> {
|
||||
let mut count = entries.len();
|
||||
let mut offset = 0usize;
|
||||
|
||||
@@ -177,7 +182,7 @@ impl File {
|
||||
pub fn readdir(&mut self, entries: &mut [DirectoryEntry]) -> Result<usize, Errno> {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
assert_eq!(inner.vnode.kind(), VnodeKind::Directory);
|
||||
// assert_eq!(inner.vnode.kind(), VnodeKind::Directory);
|
||||
|
||||
if inner.vnode.flags() & Vnode::CACHE_READDIR != 0 {
|
||||
Self::cache_readdir(inner, entries)
|
||||
@@ -204,24 +209,38 @@ impl Drop for File {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
use libsys::{stat::OpenFlags, ioctl::IoctlCmd, stat::Stat};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use crate::node::{VnodeCommon, VnodeFile};
|
||||
use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat};
|
||||
|
||||
struct DummyInode;
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DummyInode {
|
||||
fn create(
|
||||
impl VnodeCommon for DummyInode {
|
||||
/// Performs filetype-specific request
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_at: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let node = Vnode::new(name, kind, 0);
|
||||
node.set_data(Box::new(DummyInode {}));
|
||||
Ok(node)
|
||||
node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Retrieves file status
|
||||
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Reports the size of this filesystem object in bytes
|
||||
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns `true` if node is ready for an operation
|
||||
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
|
||||
@@ -232,6 +251,9 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl VnodeFile for DummyInode {
|
||||
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
#[cfg(test)]
|
||||
println!("read {} at {}", data.len(), pos);
|
||||
@@ -249,12 +271,19 @@ mod tests {
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normal_read() {
|
||||
let node = Vnode::new("", VnodeKind::Regular, 0);
|
||||
node.set_data(Box::new(DummyInode {}));
|
||||
let node = Vnode::new(
|
||||
"",
|
||||
VnodeData::File(RefCell::new(Some(Box::new(DummyInode {})))),
|
||||
0,
|
||||
);
|
||||
let mut file = node.open(OpenFlags::O_RDONLY).unwrap();
|
||||
let mut buf = [0u8; 4096];
|
||||
|
||||
|
||||
+197
-198
@@ -1,4 +1,4 @@
|
||||
use crate::{FileRef, VnodeKind, VnodeRef};
|
||||
use crate::{FileRef, VnodeRef, VnodeCreateKind};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
path::{path_component_left, path_component_right},
|
||||
@@ -34,9 +34,10 @@ impl Ioctx {
|
||||
loop {
|
||||
(element, rest) = path_component_left(rest);
|
||||
|
||||
if !at.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
at.as_directory()?;
|
||||
// if !at.is_directory() {
|
||||
// return Err(Errno::NotADirectory);
|
||||
// }
|
||||
|
||||
match element {
|
||||
".." => {
|
||||
@@ -48,7 +49,7 @@ impl Ioctx {
|
||||
}
|
||||
|
||||
while let Some(target) = at.target() {
|
||||
assert!(at.kind() == VnodeKind::Directory);
|
||||
// assert!(at.kind() == VnodeKind::Directory);
|
||||
at = target;
|
||||
}
|
||||
|
||||
@@ -60,7 +61,7 @@ impl Ioctx {
|
||||
let mut node = at.lookup_or_load(element)?;
|
||||
|
||||
while let Some(target) = node.target() {
|
||||
assert!(node.kind() == VnodeKind::Directory);
|
||||
// assert!(node.kind() == VnodeKind::Directory);
|
||||
node = target;
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ impl Ioctx {
|
||||
self.find(at, parent, true)?.create(
|
||||
name.trim_start_matches('/'),
|
||||
mode,
|
||||
VnodeKind::Directory,
|
||||
VnodeCreateKind::Directory
|
||||
)
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ impl Ioctx {
|
||||
Err(Errno::DoesNotExist) => {
|
||||
let (parent, name) = path_component_right(path);
|
||||
let at = self.find(at, parent, true)?;
|
||||
at.create(name, mode, VnodeKind::Regular)
|
||||
at.create(name, mode, VnodeCreateKind::File)
|
||||
}
|
||||
o => o,
|
||||
}?;
|
||||
@@ -128,198 +129,196 @@ impl Ioctx {
|
||||
/// Changes current working directory of the process
|
||||
pub fn chdir(&mut self, path: &str) -> Result<(), Errno> {
|
||||
let node = self.find(None, path, true)?;
|
||||
if !node.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
let _dir = node.as_directory()?;
|
||||
self.cwd = node;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Vnode, VnodeImpl, VnodeKind};
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat};
|
||||
|
||||
pub struct DummyInode;
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DummyInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
_at: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let vnode = Vnode::new(name, kind, 0);
|
||||
vnode.set_data(Box::new(DummyInode {}));
|
||||
Ok(vnode)
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_existing_absolute() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let d0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let d1 = Vnode::new("dir1", VnodeKind::Directory, 0);
|
||||
let d0d0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0);
|
||||
let d1f0 = Vnode::new("file0", VnodeKind::Regular, 0);
|
||||
|
||||
root.attach(d0.clone());
|
||||
root.attach(d1.clone());
|
||||
d0.attach(d0d0.clone());
|
||||
d0.attach(d0f0.clone());
|
||||
d1.attach(d1f0.clone());
|
||||
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/.", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/./.", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
&ioctx.find(None, "/.///.", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/..", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/../", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
&ioctx.find(None, "/../.", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
&ioctx.find(None, "/../..", false).unwrap()
|
||||
));
|
||||
|
||||
assert!(Rc::ptr_eq(&d0, &ioctx.find(None, "/dir0", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&d1, &ioctx.find(None, "/dir1", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir1/../dir0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d1,
|
||||
&ioctx
|
||||
.find(None, "/dir1/../dir0/./../../.././dir1", false)
|
||||
.unwrap()
|
||||
));
|
||||
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0d0,
|
||||
&ioctx.find(None, "/dir0/dir0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0d0,
|
||||
&ioctx.find(None, "/dir0/dir0/.", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/..", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/../", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/../.", false).unwrap()
|
||||
));
|
||||
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0f0,
|
||||
&ioctx.find(None, "/dir0/file0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0f0,
|
||||
&ioctx.find(None, "/dir1/../dir0/./file0", false).unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_rejects_file_dots() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let d0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0);
|
||||
|
||||
root.attach(d0.clone());
|
||||
d0.attach(d0f0.clone());
|
||||
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
assert_eq!(
|
||||
ioctx.find(None, "/dir0/file0/.", false).unwrap_err(),
|
||||
Errno::NotADirectory
|
||||
);
|
||||
assert_eq!(
|
||||
ioctx.find(None, "/dir0/file0/..", false).unwrap_err(),
|
||||
Errno::NotADirectory
|
||||
);
|
||||
|
||||
// TODO handle this case
|
||||
// assert_eq!(ioctx.find(None, "/dir0/file0/").unwrap_err(), Errno::NotADirectory);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mkdir() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
root.set_data(Box::new(DummyInode {}));
|
||||
|
||||
assert!(ioctx.mkdir(None, "/dir0", FileMode::default_dir()).is_ok());
|
||||
assert_eq!(
|
||||
ioctx
|
||||
.mkdir(None, "/dir0", FileMode::default_dir())
|
||||
.unwrap_err(),
|
||||
Errno::AlreadyExists
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_mount() {
|
||||
let root_outer = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let root_inner = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0);
|
||||
|
||||
root_outer.clone().attach(dir0.clone());
|
||||
root_inner.clone().attach(dir1.clone());
|
||||
|
||||
let ioctx = Ioctx::new(root_outer.clone());
|
||||
|
||||
assert_eq!(
|
||||
ioctx.find(None, "/dir0/dir1", false).unwrap_err(),
|
||||
Errno::DoesNotExist
|
||||
);
|
||||
|
||||
dir0.mount(root_inner.clone()).unwrap();
|
||||
|
||||
assert!(Rc::ptr_eq(
|
||||
&root_inner,
|
||||
&ioctx.find(None, "/dir0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&dir1,
|
||||
&ioctx.find(None, "/dir0/dir1", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root_inner,
|
||||
&ioctx.find(None, "/dir0/dir1/..", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&dir0,
|
||||
&ioctx.find(None, "/dir0/dir1/../..", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root_outer,
|
||||
&ioctx.find(None, "/dir0/dir1/../../..", false).unwrap()
|
||||
));
|
||||
}
|
||||
}
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::*;
|
||||
// use crate::{Vnode, VnodeImpl, VnodeKind};
|
||||
// use alloc::{boxed::Box, rc::Rc};
|
||||
// use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat};
|
||||
//
|
||||
// pub struct DummyInode;
|
||||
//
|
||||
// #[auto_inode]
|
||||
// impl VnodeImpl for DummyInode {
|
||||
// fn create(
|
||||
// &mut self,
|
||||
// _at: VnodeRef,
|
||||
// name: &str,
|
||||
// kind: VnodeKind,
|
||||
// ) -> Result<VnodeRef, Errno> {
|
||||
// let vnode = Vnode::new(name, kind, 0);
|
||||
// vnode.set_data(Box::new(DummyInode {}));
|
||||
// Ok(vnode)
|
||||
// }
|
||||
//
|
||||
// fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
// Err(Errno::DoesNotExist)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_find_existing_absolute() {
|
||||
// let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
// let d0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
// let d1 = Vnode::new("dir1", VnodeKind::Directory, 0);
|
||||
// let d0d0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
// let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0);
|
||||
// let d1f0 = Vnode::new("file0", VnodeKind::Regular, 0);
|
||||
//
|
||||
// root.attach(d0.clone());
|
||||
// root.attach(d1.clone());
|
||||
// d0.attach(d0d0.clone());
|
||||
// d0.attach(d0f0.clone());
|
||||
// d1.attach(d1f0.clone());
|
||||
//
|
||||
// let ioctx = Ioctx::new(root.clone());
|
||||
//
|
||||
// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/", false).unwrap()));
|
||||
// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/.", false).unwrap()));
|
||||
// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/./.", false).unwrap()));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &root,
|
||||
// &ioctx.find(None, "/.///.", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/..", false).unwrap()));
|
||||
// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/../", false).unwrap()));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &root,
|
||||
// &ioctx.find(None, "/../.", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &root,
|
||||
// &ioctx.find(None, "/../..", false).unwrap()
|
||||
// ));
|
||||
//
|
||||
// assert!(Rc::ptr_eq(&d0, &ioctx.find(None, "/dir0", false).unwrap()));
|
||||
// assert!(Rc::ptr_eq(&d1, &ioctx.find(None, "/dir1", false).unwrap()));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &d0,
|
||||
// &ioctx.find(None, "/dir1/../dir0", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &d1,
|
||||
// &ioctx
|
||||
// .find(None, "/dir1/../dir0/./../../.././dir1", false)
|
||||
// .unwrap()
|
||||
// ));
|
||||
//
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &d0d0,
|
||||
// &ioctx.find(None, "/dir0/dir0", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &d0d0,
|
||||
// &ioctx.find(None, "/dir0/dir0/.", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &d0,
|
||||
// &ioctx.find(None, "/dir0/dir0/..", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &d0,
|
||||
// &ioctx.find(None, "/dir0/dir0/../", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &d0,
|
||||
// &ioctx.find(None, "/dir0/dir0/../.", false).unwrap()
|
||||
// ));
|
||||
//
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &d0f0,
|
||||
// &ioctx.find(None, "/dir0/file0", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &d0f0,
|
||||
// &ioctx.find(None, "/dir1/../dir0/./file0", false).unwrap()
|
||||
// ));
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_find_rejects_file_dots() {
|
||||
// let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
// let d0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
// let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0);
|
||||
//
|
||||
// root.attach(d0.clone());
|
||||
// d0.attach(d0f0.clone());
|
||||
//
|
||||
// let ioctx = Ioctx::new(root.clone());
|
||||
//
|
||||
// assert_eq!(
|
||||
// ioctx.find(None, "/dir0/file0/.", false).unwrap_err(),
|
||||
// Errno::NotADirectory
|
||||
// );
|
||||
// assert_eq!(
|
||||
// ioctx.find(None, "/dir0/file0/..", false).unwrap_err(),
|
||||
// Errno::NotADirectory
|
||||
// );
|
||||
//
|
||||
// // TODO handle this case
|
||||
// // assert_eq!(ioctx.find(None, "/dir0/file0/").unwrap_err(), Errno::NotADirectory);
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_mkdir() {
|
||||
// let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
// let ioctx = Ioctx::new(root.clone());
|
||||
//
|
||||
// root.set_data(Box::new(DummyInode {}));
|
||||
//
|
||||
// assert!(ioctx.mkdir(None, "/dir0", FileMode::default_dir()).is_ok());
|
||||
// assert_eq!(
|
||||
// ioctx
|
||||
// .mkdir(None, "/dir0", FileMode::default_dir())
|
||||
// .unwrap_err(),
|
||||
// Errno::AlreadyExists
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_find_mount() {
|
||||
// let root_outer = Vnode::new("", VnodeKind::Directory, 0);
|
||||
// let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
// let root_inner = Vnode::new("", VnodeKind::Directory, 0);
|
||||
// let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0);
|
||||
//
|
||||
// root_outer.clone().attach(dir0.clone());
|
||||
// root_inner.clone().attach(dir1.clone());
|
||||
//
|
||||
// let ioctx = Ioctx::new(root_outer.clone());
|
||||
//
|
||||
// assert_eq!(
|
||||
// ioctx.find(None, "/dir0/dir1", false).unwrap_err(),
|
||||
// Errno::DoesNotExist
|
||||
// );
|
||||
//
|
||||
// dir0.mount(root_inner.clone()).unwrap();
|
||||
//
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &root_inner,
|
||||
// &ioctx.find(None, "/dir0", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &dir1,
|
||||
// &ioctx.find(None, "/dir0/dir1", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &root_inner,
|
||||
// &ioctx.find(None, "/dir0/dir1/..", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &dir0,
|
||||
// &ioctx.find(None, "/dir0/dir1/../..", false).unwrap()
|
||||
// ));
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &root_outer,
|
||||
// &ioctx.find(None, "/dir0/dir1/../../..", false).unwrap()
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
|
||||
+3
-6
@@ -1,15 +1,12 @@
|
||||
//! Virtual filesystem API and facilities
|
||||
#![warn(missing_docs)]
|
||||
#![feature(destructuring_assignment, const_fn_trait_bound)]
|
||||
#![feature(const_fn_trait_bound, const_discriminant)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[macro_use]
|
||||
extern crate fs_macros;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
// pub use libsys::stat::{FileMode, OpenFlags, Stat};
|
||||
@@ -20,10 +17,10 @@ pub use block::BlockDevice;
|
||||
mod fs;
|
||||
pub use fs::Filesystem;
|
||||
mod node;
|
||||
pub use node::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
pub use node::{Vnode, VnodeData, VnodeRef, VnodeCreateKind, VnodeCommon, VnodeFile, VnodeDirectory};
|
||||
mod ioctx;
|
||||
pub use ioctx::Ioctx;
|
||||
mod file;
|
||||
pub use file::{File, FileRef};
|
||||
mod char;
|
||||
pub use crate::char::{CharDevice, CharDeviceWrapper};
|
||||
pub use crate::char::CharDevice;
|
||||
|
||||
+311
-194
@@ -1,7 +1,8 @@
|
||||
use crate::{File, FileRef, Filesystem, Ioctx};
|
||||
use crate::{File, FileRef, Filesystem, Ioctx, CharDevice};
|
||||
use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec};
|
||||
use core::cell::{Ref, RefCell, RefMut};
|
||||
use core::fmt;
|
||||
use core::mem::Discriminant;
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
@@ -10,20 +11,81 @@ use libsys::{
|
||||
|
||||
/// Convenience type alias for [Rc<Vnode>]
|
||||
pub type VnodeRef = Rc<Vnode>;
|
||||
pub type VnodeKind = Discriminant<VnodeData>;
|
||||
|
||||
/// List of possible vnode types
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum VnodeKind {
|
||||
/// Node is a directory with create/lookup/remove operations
|
||||
Directory,
|
||||
/// Node is a regular file
|
||||
Regular,
|
||||
/// Node is a character device
|
||||
Char,
|
||||
/// Node is a block device
|
||||
Block,
|
||||
/// Trait implemented by both regular files and directories
|
||||
pub trait VnodeCommon {
|
||||
/// Performs filetype-specific request
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno>;
|
||||
|
||||
/// Retrieves file status
|
||||
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno>;
|
||||
|
||||
/// Reports the size of this filesystem object in bytes
|
||||
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno>;
|
||||
|
||||
/// Returns `true` if node is ready for an operation
|
||||
fn ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno>;
|
||||
|
||||
/// Opens a vnode for access. Returns initial file position.
|
||||
fn open(&mut self, node: VnodeRef, opts: OpenFlags) -> Result<usize, Errno>;
|
||||
/// Closes a vnode
|
||||
fn close(&mut self, node: VnodeRef) -> Result<(), Errno>;
|
||||
}
|
||||
|
||||
/// Regular file access interface
|
||||
pub trait VnodeFile: VnodeCommon {
|
||||
/// Changes file's underlying storage size
|
||||
fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno>;
|
||||
/// Reads `data.len()` bytes into the buffer from file offset `pos`
|
||||
fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
/// Writes `data.len()` bytes from the buffer to file offset `pos`.
|
||||
/// Resizes the file storage if necessary.
|
||||
fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
/// Directory access interface
|
||||
pub trait VnodeDirectory: VnodeCommon {
|
||||
/// Creates entry `name` of type `kind` in directory `at`
|
||||
fn create(
|
||||
&mut self,
|
||||
at: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeCreateKind,
|
||||
) -> Result<VnodeRef, Errno>;
|
||||
/// Removes entry `name` for directory `at`
|
||||
fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno>;
|
||||
/// Loads an entry `name` in directory `at` from filesystem
|
||||
fn lookup(&mut self, at: VnodeRef, name: &str) -> Result<VnodeRef, Errno>;
|
||||
|
||||
/// Read directory entries into target buffer
|
||||
fn readdir(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
pos: usize,
|
||||
data: &mut [DirectoryEntry],
|
||||
) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
// /// List of possible vnode types
|
||||
// #[derive(Debug, Clone, Copy, PartialEq)]
|
||||
// pub enum VnodeKind {
|
||||
// /// Node is a directory with create/lookup/remove operations
|
||||
// Directory,
|
||||
// /// Node is a regular file
|
||||
// Regular,
|
||||
// /// Node is a character device
|
||||
// Char,
|
||||
// /// Node is a block device
|
||||
// Block,
|
||||
// }
|
||||
|
||||
pub(crate) struct TreeNode {
|
||||
parent: Option<VnodeRef>,
|
||||
children: Vec<VnodeRef>,
|
||||
@@ -35,6 +97,25 @@ pub struct VnodeProps {
|
||||
pub mode: FileMode,
|
||||
}
|
||||
|
||||
/// Specific node implementation data
|
||||
pub enum VnodeData {
|
||||
/// Directory node
|
||||
Directory(RefCell<Option<Box<dyn VnodeDirectory>>>),
|
||||
/// Regular file node
|
||||
File(RefCell<Option<Box<dyn VnodeFile>>>),
|
||||
/// Character device node
|
||||
Char(&'static dyn CharDevice)
|
||||
}
|
||||
|
||||
/// Node types for create() calls
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum VnodeCreateKind {
|
||||
/// Directory node
|
||||
Directory,
|
||||
/// Regular file node
|
||||
File,
|
||||
}
|
||||
|
||||
/// Virtual filesystem node struct, generalizes access to
|
||||
/// underlying real filesystems
|
||||
pub struct Vnode {
|
||||
@@ -42,64 +123,13 @@ pub struct Vnode {
|
||||
tree: RefCell<TreeNode>,
|
||||
props: RefCell<VnodeProps>,
|
||||
|
||||
kind: VnodeKind,
|
||||
// kind: VnodeKind,
|
||||
data: VnodeData,
|
||||
flags: u32,
|
||||
|
||||
target: RefCell<Option<VnodeRef>>,
|
||||
fs: RefCell<Option<Rc<dyn Filesystem>>>,
|
||||
data: RefCell<Option<Box<dyn VnodeImpl>>>,
|
||||
}
|
||||
|
||||
/// Interface for "inode" of a real filesystem
|
||||
pub trait VnodeImpl {
|
||||
// Directory-only operations
|
||||
/// Creates a new vnode, sets it up, attaches it (in real FS) to `at` with `name` and
|
||||
/// returns it
|
||||
fn create(&mut self, at: VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno>;
|
||||
/// Removes the filesystem inode from its parent by erasing its directory entry
|
||||
fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno>;
|
||||
/// Looks up a corresponding directory entry for `name`. If present, loads its inode from
|
||||
/// storage medium and returns a new vnode associated with it.
|
||||
fn lookup(&mut self, at: VnodeRef, name: &str) -> Result<VnodeRef, Errno>;
|
||||
|
||||
/// Opens a vnode for access. Returns initial file position.
|
||||
fn open(&mut self, node: VnodeRef, opts: OpenFlags) -> Result<usize, Errno>;
|
||||
/// Closes a vnode
|
||||
fn close(&mut self, node: VnodeRef) -> Result<(), Errno>;
|
||||
|
||||
/// Changes file's underlying storage size
|
||||
fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno>;
|
||||
/// Reads `data.len()` bytes into the buffer from file offset `pos`
|
||||
fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
/// Writes `data.len()` bytes from the buffer to file offset `pos`.
|
||||
/// Resizes the file storage if necessary.
|
||||
fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno>;
|
||||
|
||||
/// Read directory entries into target buffer
|
||||
fn readdir(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
pos: usize,
|
||||
data: &mut [DirectoryEntry],
|
||||
) -> Result<usize, Errno>;
|
||||
|
||||
/// Retrieves file status
|
||||
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno>;
|
||||
|
||||
/// Reports the size of this filesystem object in bytes
|
||||
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno>;
|
||||
|
||||
/// Returns `true` if node is ready for an operation
|
||||
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno>;
|
||||
|
||||
/// Performs filetype-specific request
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno>;
|
||||
// data: RefCell<Option<Box<dyn VnodeImpl>>>,
|
||||
}
|
||||
|
||||
impl Vnode {
|
||||
@@ -114,10 +144,10 @@ impl Vnode {
|
||||
|
||||
/// Constructs a new [Vnode], wrapping it in [Rc]. The resulting node
|
||||
/// then needs to have [Vnode::set_data()] called on it to be usable.
|
||||
pub fn new(name: &str, kind: VnodeKind, flags: u32) -> VnodeRef {
|
||||
pub fn new(name: &str, data: VnodeData, flags: u32) -> VnodeRef {
|
||||
Rc::new(Self {
|
||||
name: name.to_owned(),
|
||||
kind,
|
||||
data,
|
||||
flags,
|
||||
props: RefCell::new(VnodeProps {
|
||||
mode: FileMode::empty(),
|
||||
@@ -128,7 +158,6 @@ impl Vnode {
|
||||
}),
|
||||
target: RefCell::new(None),
|
||||
fs: RefCell::new(None),
|
||||
data: RefCell::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -147,19 +176,30 @@ impl Vnode {
|
||||
self.props.borrow()
|
||||
}
|
||||
|
||||
/// Sets an associated [VnodeImpl] for the [Vnode]
|
||||
pub fn set_data(&self, data: Box<dyn VnodeImpl>) {
|
||||
*self.data.borrow_mut() = Some(data);
|
||||
}
|
||||
// /// Sets an associated [VnodeImpl] for the [Vnode]
|
||||
// pub fn set_data(&self, data: Box<dyn VnodeImpl>) {
|
||||
// *self.data.borrow_mut() = Some(data);
|
||||
// }
|
||||
|
||||
/// Sets an associated [Filesystem] for the [Vnode]
|
||||
pub fn set_fs(&self, fs: Rc<dyn Filesystem>) {
|
||||
*self.fs.borrow_mut() = Some(fs);
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated [VnodeImpl]
|
||||
pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> {
|
||||
self.data.borrow_mut()
|
||||
/// Returns node's directory implementation data
|
||||
pub fn as_directory(&self) -> Result<&RefCell<Option<Box<dyn VnodeDirectory>>>, Errno> {
|
||||
match &self.data {
|
||||
VnodeData::Directory(data) => Ok(data),
|
||||
_ => Err(Errno::NotADirectory),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns node's regular file implementation data
|
||||
pub fn as_file(&self) -> Result<&RefCell<Option<Box<dyn VnodeFile>>>, Errno> {
|
||||
match &self.data {
|
||||
VnodeData::File(data) => Ok(data),
|
||||
_ => Err(Errno::IsADirectory),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the associated [Fileystem]
|
||||
@@ -167,11 +207,6 @@ impl Vnode {
|
||||
self.fs.borrow().clone()
|
||||
}
|
||||
|
||||
/// Returns `true` if the vnode represents a directory
|
||||
pub fn is_directory(&self) -> bool {
|
||||
self.kind == VnodeKind::Directory
|
||||
}
|
||||
|
||||
/// Returns `true` if the vnode allows arbitrary seeking
|
||||
pub fn is_seekable(&self) -> bool {
|
||||
self.flags & Self::SEEKABLE != 0
|
||||
@@ -180,7 +215,7 @@ impl Vnode {
|
||||
/// Returns kind of the vnode
|
||||
#[inline(always)]
|
||||
pub const fn kind(&self) -> VnodeKind {
|
||||
self.kind
|
||||
core::mem::discriminant(&self.data)
|
||||
}
|
||||
|
||||
/// Returns flags of the vnode
|
||||
@@ -220,12 +255,9 @@ impl Vnode {
|
||||
|
||||
/// Attaches some filesystem's root directory node at another directory
|
||||
pub fn mount(self: &VnodeRef, root: VnodeRef) -> Result<(), Errno> {
|
||||
if !self.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if !root.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
let _dir = self.as_directory()?;
|
||||
let _root_dir = root.as_directory()?;
|
||||
|
||||
if self.target.borrow().is_some() {
|
||||
return Err(Errno::Busy);
|
||||
}
|
||||
@@ -252,7 +284,7 @@ impl Vnode {
|
||||
|
||||
/// Looks up a child `name` in in-memory tree cache
|
||||
pub fn lookup(self: &VnodeRef, name: &str) -> Option<VnodeRef> {
|
||||
assert!(self.is_directory());
|
||||
// assert!(self.is_directory());
|
||||
self.tree
|
||||
.borrow()
|
||||
.children
|
||||
@@ -267,7 +299,8 @@ impl Vnode {
|
||||
limit: usize,
|
||||
mut f: F,
|
||||
) -> usize {
|
||||
assert!(self.is_directory());
|
||||
// TODO
|
||||
// assert!(self.is_directory());
|
||||
let mut count = 0;
|
||||
for (index, item) in self
|
||||
.tree
|
||||
@@ -287,10 +320,12 @@ impl Vnode {
|
||||
/// Looks up a child `name` in `self`. Will first try looking up a cached
|
||||
/// vnode and will load it from disk if it's missing.
|
||||
pub fn lookup_or_load(self: &VnodeRef, name: &str) -> Result<VnodeRef, Errno> {
|
||||
let dir = self.as_directory()?;
|
||||
|
||||
if let Some(node) = self.lookup(name) {
|
||||
Ok(node)
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
let vnode = data.lookup(self.clone(), name)?;
|
||||
} else if let Some(ref mut dir) = *dir.borrow_mut() {
|
||||
let vnode = dir.lookup(self.clone(), name)?;
|
||||
if let Some(fs) = self.fs() {
|
||||
vnode.set_fs(fs);
|
||||
}
|
||||
@@ -306,11 +341,10 @@ impl Vnode {
|
||||
self: &VnodeRef,
|
||||
name: &str,
|
||||
mode: FileMode,
|
||||
kind: VnodeKind,
|
||||
kind: VnodeCreateKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
let dir = self.as_directory()?;
|
||||
|
||||
if name.contains('/') {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
@@ -321,8 +355,8 @@ impl Vnode {
|
||||
e => return e,
|
||||
};
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
let vnode = data.create(self.clone(), name, kind)?;
|
||||
if let Some(ref mut dir) = *dir.borrow_mut() {
|
||||
let vnode = dir.create(self.clone(), name, kind)?;
|
||||
if let Some(fs) = self.fs() {
|
||||
vnode.set_fs(fs);
|
||||
}
|
||||
@@ -336,16 +370,14 @@ impl Vnode {
|
||||
|
||||
/// Removes a directory entry `name` from `self`
|
||||
pub fn unlink(self: &VnodeRef, name: &str) -> Result<(), Errno> {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
let dir = self.as_directory()?;
|
||||
if name.contains('/') {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(ref mut dir) = *dir.borrow_mut() {
|
||||
let vnode = self.lookup(name).ok_or(Errno::DoesNotExist)?;
|
||||
data.remove(self.clone(), name)?;
|
||||
dir.remove(self.clone(), name)?;
|
||||
vnode.detach();
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -356,92 +388,119 @@ impl Vnode {
|
||||
/// Opens a vnode for access
|
||||
pub fn open(self: &VnodeRef, flags: OpenFlags) -> Result<FileRef, Errno> {
|
||||
let mut open_flags = 0;
|
||||
if flags.contains(OpenFlags::O_DIRECTORY) {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if flags & OpenFlags::O_ACCESS != OpenFlags::O_RDONLY {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
open_flags = File::READ;
|
||||
} else {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
match flags & OpenFlags::O_ACCESS {
|
||||
OpenFlags::O_RDONLY => open_flags |= File::READ,
|
||||
OpenFlags::O_WRONLY => open_flags |= File::WRITE,
|
||||
OpenFlags::O_RDWR => open_flags |= File::READ | File::WRITE,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
if flags.contains(OpenFlags::O_CLOEXEC) {
|
||||
open_flags |= File::CLOEXEC;
|
||||
}
|
||||
|
||||
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
|
||||
Ok(File::normal(self.clone(), File::POS_CACHE_DOT, open_flags))
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
let pos = data.open(self.clone(), flags)?;
|
||||
Ok(File::normal(self.clone(), pos, open_flags))
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
match flags & OpenFlags::O_ACCESS {
|
||||
OpenFlags::O_RDONLY => open_flags |= File::READ,
|
||||
OpenFlags::O_WRONLY => open_flags |= File::WRITE,
|
||||
OpenFlags::O_RDWR => open_flags |= File::READ | File::WRITE,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
||||
match &self.data {
|
||||
VnodeData::Directory(_) => {
|
||||
if !flags.contains(OpenFlags::O_DIRECTORY) {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
|
||||
if self.flags & Vnode::CACHE_READDIR != 0 {
|
||||
Ok(File::normal(self.clone(), File::POS_CACHE_DOT, open_flags))
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
},
|
||||
VnodeData::File(file) => {
|
||||
if flags.contains(OpenFlags::O_DIRECTORY) {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
if let Some(ref mut file) = *file.borrow_mut() {
|
||||
let pos = file.open(self.clone(), flags)?;
|
||||
Ok(File::normal(self.clone(), pos, open_flags))
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
},
|
||||
VnodeData::Char(_) => {
|
||||
if flags.contains(OpenFlags::O_DIRECTORY) {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
Ok(File::normal(self.clone(), 0, open_flags))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Closes a vnode
|
||||
pub fn close(self: &VnodeRef) -> Result<(), Errno> {
|
||||
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
|
||||
Ok(())
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.close(self.clone())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
match &self.data {
|
||||
VnodeData::Directory(dir) => {
|
||||
if let Some(ref mut dir) = *dir.borrow_mut() {
|
||||
return dir.close(self.clone());
|
||||
}
|
||||
},
|
||||
VnodeData::File(file) => {
|
||||
if let Some(ref mut file) = *file.borrow_mut() {
|
||||
return file.close(self.clone());
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
/// Reads data from offset `pos` into `buf`
|
||||
pub fn read(self: &VnodeRef, pos: usize, buf: &mut [u8]) -> Result<usize, Errno> {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
Err(Errno::IsADirectory)
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.read(self.clone(), pos, buf)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
match &self.data {
|
||||
VnodeData::File(file) => {
|
||||
file.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.read(self.clone(), pos, buf)
|
||||
}
|
||||
VnodeData::Char(chr) => {
|
||||
chr.read(true, buf)
|
||||
}
|
||||
_ => Err(Errno::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes data from `buf` to offset `pos`
|
||||
pub fn write(self: &VnodeRef, pos: usize, buf: &[u8]) -> Result<usize, Errno> {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
Err(Errno::IsADirectory)
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.write(self.clone(), pos, buf)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
match &self.data {
|
||||
VnodeData::File(file) => {
|
||||
file.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.write(self.clone(), pos, buf)
|
||||
}
|
||||
VnodeData::Char(chr) => {
|
||||
chr.write(true, buf)
|
||||
}
|
||||
_ => Err(Errno::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resizes the vnode data
|
||||
pub fn truncate(self: &VnodeRef, size: usize) -> Result<(), Errno> {
|
||||
if self.kind != VnodeKind::Regular {
|
||||
Err(Errno::IsADirectory)
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.truncate(self.clone(), size)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
match &self.data {
|
||||
VnodeData::File(file) => {
|
||||
file.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.truncate(self.clone(), size)
|
||||
}
|
||||
_ => Err(Errno::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns current vnode data size
|
||||
pub fn size(self: &VnodeRef) -> Result<usize, Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.size(self.clone())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
match &self.data {
|
||||
VnodeData::File(file) => {
|
||||
file.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.size(self.clone())
|
||||
},
|
||||
VnodeData::Directory(dir) => {
|
||||
dir.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.size(self.clone())
|
||||
}
|
||||
VnodeData::Char(_) => {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,28 +513,33 @@ impl Vnode {
|
||||
size: 0,
|
||||
mode: props.mode,
|
||||
})
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.stat(self.clone())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
match &self.data {
|
||||
VnodeData::File(file) => file.borrow_mut().as_mut().ok_or(Errno::NotADirectory)?.stat(self.clone()),
|
||||
VnodeData::Directory(dir) => dir.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.stat(self.clone()),
|
||||
// TODO stat() for char/blk devs
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs node-specific requests
|
||||
pub fn ioctl(self: &VnodeRef, cmd: IoctlCmd, ptr: usize, len: usize) -> Result<usize, Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.ioctl(self.clone(), cmd, ptr, len)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
match &self.data {
|
||||
VnodeData::Char(chr) => {
|
||||
chr.ioctl(cmd, ptr, len)
|
||||
},
|
||||
_ => Err(Errno::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the node is ready for operation
|
||||
pub fn is_ready(self: &VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.is_ready(self.clone(), write)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
pub fn ready(self: &VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
match &self.data {
|
||||
VnodeData::File(file) => {
|
||||
file.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.ready(self.clone(), write)
|
||||
},
|
||||
_ => Err(Errno::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,32 +589,81 @@ mod tests {
|
||||
use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat};
|
||||
pub struct DummyInode;
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DummyInode {
|
||||
fn create(
|
||||
// TODO derive macro for this
|
||||
impl VnodeCommon for DummyInode {
|
||||
/// Performs filetype-specific request
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_at: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let node = Vnode::new(name, kind, 0);
|
||||
node.set_data(Box::new(DummyInode {}));
|
||||
Ok(node)
|
||||
node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
/// Retrieves file status
|
||||
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Reports the size of this filesystem object in bytes
|
||||
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns `true` if node is ready for an operation
|
||||
fn ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
}
|
||||
|
||||
impl VnodeDirectory for DummyInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
at: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeCreateKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let data = match kind {
|
||||
VnodeCreateKind::Directory => {
|
||||
VnodeData::Directory(RefCell::new(Some(Box::new(DummyInode {}))))
|
||||
}
|
||||
_ => todo!(),
|
||||
};
|
||||
Ok(Vnode::new(name, data, 0))
|
||||
}
|
||||
fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
fn lookup(&mut self, at: VnodeRef, name: &str) -> Result<VnodeRef, Errno> {
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
|
||||
/// Read directory entries into target buffer
|
||||
fn readdir(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
pos: usize,
|
||||
data: &mut [DirectoryEntry],
|
||||
) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parent() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let node = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let root = Vnode::new("", VnodeData::Directory(RefCell::new(None)), 0);
|
||||
let node = Vnode::new("dir0", VnodeData::Directory(RefCell::new(None)), 0);
|
||||
|
||||
root.attach(node.clone());
|
||||
|
||||
@@ -560,23 +673,27 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_mkdir_unlink() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
|
||||
root.set_data(Box::new(DummyInode {}));
|
||||
let root = Vnode::new(
|
||||
"",
|
||||
VnodeData::Directory(RefCell::new(Some(Box::new(DummyInode {})))),
|
||||
0,
|
||||
);
|
||||
|
||||
let node = root
|
||||
.create("test", FileMode::default_dir(), VnodeKind::Directory)
|
||||
.create("test", FileMode::default_dir(), VnodeCreateKind::Directory)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
root.create("test", FileMode::default_dir(), VnodeKind::Directory)
|
||||
root.create("test", FileMode::default_dir(), VnodeCreateKind::Directory)
|
||||
.unwrap_err(),
|
||||
Errno::AlreadyExists
|
||||
);
|
||||
|
||||
assert_eq!(node.props.borrow().mode, FileMode::default_dir());
|
||||
assert!(Rc::ptr_eq(&node, &root.lookup("test").unwrap()));
|
||||
assert!(node.data.borrow().is_some());
|
||||
let inner = node.as_directory().unwrap();
|
||||
assert!(matches!(*inner.borrow_mut(), Some(_)));
|
||||
// assert!(node.data.borrow().is_some());
|
||||
|
||||
root.unlink("test").unwrap();
|
||||
|
||||
@@ -585,9 +702,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_lookup_attach_detach() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0);
|
||||
let root = Vnode::new("", VnodeData::Directory(RefCell::new(None)), 0);
|
||||
let dir0 = Vnode::new("dir0", VnodeData::Directory(RefCell::new(None)), 0);
|
||||
let dir1 = Vnode::new("dir1", VnodeData::Directory(RefCell::new(None)), 0);
|
||||
|
||||
root.attach(dir0.clone());
|
||||
root.attach(dir1.clone());
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ kernel-macros = { path = "macros" }
|
||||
fs-macros = { path = "../fs/macros" }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
cortex-a = { version = "6.x.x" }
|
||||
cortex-a = { version = "7.0.x" }
|
||||
|
||||
[features]
|
||||
default = ["aggressive_syscall"]
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
.macro MOV_L reg, value
|
||||
mov \reg, #((\value) & 0xFFFF)
|
||||
movk \reg, #((\value) >> 16), lsl #16
|
||||
.endm
|
||||
|
||||
.macro ADR_REL reg, sym
|
||||
adrp \reg, \sym
|
||||
add \reg, \reg, #:lo12:\sym
|
||||
.endm
|
||||
|
||||
.macro ADR_ABS reg, sym
|
||||
movz \reg, #:abs_g3:\sym
|
||||
movk \reg, #:abs_g2_nc:\sym
|
||||
movk \reg, #:abs_g1_nc:\sym
|
||||
movk \reg, #:abs_g0_nc:\sym
|
||||
.endm
|
||||
@@ -4,6 +4,7 @@ use crate::arch::{
|
||||
aarch64::reg::{CNTKCTL_EL1, CPACR_EL1},
|
||||
machine,
|
||||
};
|
||||
use core::arch::global_asm;
|
||||
use crate::config::{ConfigKey, CONFIG};
|
||||
use crate::dev::{
|
||||
fdt::{find_prop, DeviceTree},
|
||||
@@ -82,7 +83,7 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
|
||||
// Enable MMU
|
||||
virt::enable().expect("Failed to initialize virtual memory");
|
||||
|
||||
let fdt = init_device_tree(fdt_base).expect("Device tree init failed");
|
||||
let _fdt = init_device_tree(fdt_base).expect("Device tree init failed");
|
||||
|
||||
// Most basic machine init: initialize proper debug output
|
||||
// physical memory
|
||||
@@ -120,6 +121,4 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
global_asm!(include_str!("macros.S"));
|
||||
global_asm!(include_str!("uboot.S"));
|
||||
global_asm!(include_str!("upper.S"));
|
||||
|
||||
@@ -1,5 +1,54 @@
|
||||
// vi:ft=a64asm.asm:
|
||||
|
||||
.macro MOV_L reg, value
|
||||
mov \reg, #((\value) & 0xFFFF)
|
||||
movk \reg, #((\value) >> 16), lsl #16
|
||||
.endm
|
||||
|
||||
.macro ADR_REL reg, sym
|
||||
adrp \reg, \sym
|
||||
add \reg, \reg, #:lo12:\sym
|
||||
.endm
|
||||
|
||||
.macro ADR_ABS reg, sym
|
||||
movz \reg, #:abs_g3:\sym
|
||||
movk \reg, #:abs_g2_nc:\sym
|
||||
movk \reg, #:abs_g1_nc:\sym
|
||||
movk \reg, #:abs_g0_nc:\sym
|
||||
.endm
|
||||
|
||||
.set PTE_BLOCK_AF, 1 << 10
|
||||
.set PTE_BLOCK_ISH, 3 << 8
|
||||
.set PTE_PRESENT, 1 << 0
|
||||
|
||||
.set MAIR_EL1_Attr0_Normal_Inner_NC, (4 << 0)
|
||||
.set MAIR_EL1_Attr0_Normal_Outer_NC, (4 << 4)
|
||||
.set MAIR_EL1_Attr1_Device, (0 << 12)
|
||||
.set MAIR_EL1_Attr1_Device_nGnRE, (1 << 8)
|
||||
|
||||
.set ID_AA64MMFR0_EL1_TGran4, (0xF << 28)
|
||||
|
||||
.set TCR_EL1_IPS_SHIFT, 32
|
||||
|
||||
.set TCR_EL1_TG1_4K, (2 << 30)
|
||||
.set TCR_EL1_SH1_Outer, (2 << 28)
|
||||
.set TCR_EL1_ORGN1_NC, (0 << 26)
|
||||
.set TCR_EL1_IRGN1_NC, (0 << 24)
|
||||
.set TCR_EL1_T1SZ_SHIFT, 16
|
||||
|
||||
.set TCR_EL1_TG0_4K, (0 << 14)
|
||||
.set TCR_EL1_SH0_Outer, (2 << 12)
|
||||
.set TCR_EL1_ORGN0_NC, (0 << 10)
|
||||
.set TCR_EL1_IRGN0_NC, (0 << 8)
|
||||
.set TCR_EL1_T0SZ_SHIFT, 0
|
||||
|
||||
.set TCR_EL1_ATTRS, (TCR_EL1_TG1_4K | TCR_EL1_SH1_Outer | TCR_EL1_TG0_4K | TCR_EL1_SH0_Outer | (25 << TCR_EL1_T1SZ_SHIFT) | (25 << TCR_EL1_T0SZ_SHIFT))
|
||||
|
||||
.set SCTLR_EL1_I, (1 << 12)
|
||||
.set SCTLR_EL1_C, (1 << 2)
|
||||
.set SCTLR_EL1_M, (1 << 0)
|
||||
|
||||
|
||||
.set SCTLR_EL2_RES1, 0x30C50830
|
||||
|
||||
.set SPSR_EL2_EL1h, 0x5
|
||||
@@ -61,6 +110,68 @@ _entry:
|
||||
ADR_ABS x9, __aa64_entry_upper
|
||||
b __aa64_enter_upper
|
||||
|
||||
.global __aa64_enter_upper
|
||||
.type __aa64_enter_upper, %function
|
||||
__aa64_enter_upper:
|
||||
// x8 -- FDT base
|
||||
// x9 -- upper entry point
|
||||
|
||||
// Setup TTBR1_EL1
|
||||
// TODO fix macros
|
||||
ADR_ABS x5, KERNEL_TTBR1
|
||||
ADR_ABS x6, KERNEL_OFFSET
|
||||
|
||||
// x5 = KERNEL_TTBR1 physical address
|
||||
sub x5, x5, x6
|
||||
|
||||
// Fill KERNEL_TTBR1 table with upper-mapped Normal memory
|
||||
.fill_ttbr1:
|
||||
mov x2, #256
|
||||
1:
|
||||
sub x2, x2, #1
|
||||
|
||||
// x0 = (x2 << 30) | attrs...
|
||||
lsl x1, x2, #30
|
||||
mov x0, #(PTE_BLOCK_ISH | PTE_BLOCK_AF | PTE_PRESENT)
|
||||
orr x0, x0, x1
|
||||
|
||||
str x0, [x5, x2, lsl #3]
|
||||
|
||||
cbnz x2, 1b
|
||||
|
||||
.init_mmu_regs:
|
||||
mov x0, #(MAIR_EL1_Attr0_Normal_Outer_NC | MAIR_EL1_Attr0_Normal_Inner_NC | MAIR_EL1_Attr1_Device | MAIR_EL1_Attr1_Device_nGnRE)
|
||||
msr mair_el1, x0
|
||||
|
||||
// Test for 4KiB page support
|
||||
mrs x0, ID_AA64MMFR0_EL1
|
||||
mov x1, ID_AA64MMFR0_EL1_TGran4
|
||||
tst x0, x1
|
||||
bne .no_4k_gran
|
||||
|
||||
// x0 = PARange
|
||||
and x0, x0, #0xF
|
||||
lsl x0, x0, #TCR_EL1_IPS_SHIFT
|
||||
MOV_L x1, TCR_EL1_ATTRS
|
||||
orr x0, x0, x1
|
||||
msr tcr_el1, x0
|
||||
|
||||
msr ttbr0_el1, x5
|
||||
msr ttbr1_el1, x5
|
||||
|
||||
dsb ish
|
||||
isb
|
||||
|
||||
mrs x0, sctlr_el1
|
||||
orr x0, x0, #SCTLR_EL1_M
|
||||
msr sctlr_el1, x0
|
||||
|
||||
mov x0, x8
|
||||
br x9
|
||||
.no_4k_gran:
|
||||
b .
|
||||
.size __aa64_enter_upper, . - __aa64_enter_upper
|
||||
|
||||
.section .text._entry_upper
|
||||
__aa64_entry_upper:
|
||||
// x0 -- fdt address
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
// vi:ft=a64asm:
|
||||
|
||||
.set PTE_BLOCK_AF, 1 << 10
|
||||
.set PTE_BLOCK_ISH, 3 << 8
|
||||
.set PTE_PRESENT, 1 << 0
|
||||
|
||||
.set MAIR_EL1_Attr0_Normal_Inner_NC, (4 << 0)
|
||||
.set MAIR_EL1_Attr0_Normal_Outer_NC, (4 << 4)
|
||||
.set MAIR_EL1_Attr1_Device, (0 << 12)
|
||||
.set MAIR_EL1_Attr1_Device_nGnRE, (1 << 8)
|
||||
|
||||
.set ID_AA64MMFR0_EL1_TGran4, (0xF << 28)
|
||||
|
||||
.set TCR_EL1_IPS_SHIFT, 32
|
||||
|
||||
.set TCR_EL1_TG1_4K, (2 << 30)
|
||||
.set TCR_EL1_SH1_Outer, (2 << 28)
|
||||
.set TCR_EL1_ORGN1_NC, (0 << 26)
|
||||
.set TCR_EL1_IRGN1_NC, (0 << 24)
|
||||
.set TCR_EL1_T1SZ_SHIFT, 16
|
||||
|
||||
.set TCR_EL1_TG0_4K, (0 << 14)
|
||||
.set TCR_EL1_SH0_Outer, (2 << 12)
|
||||
.set TCR_EL1_ORGN0_NC, (0 << 10)
|
||||
.set TCR_EL1_IRGN0_NC, (0 << 8)
|
||||
.set TCR_EL1_T0SZ_SHIFT, 0
|
||||
|
||||
.set TCR_EL1_ATTRS, (TCR_EL1_TG1_4K | TCR_EL1_SH1_Outer | TCR_EL1_TG0_4K | TCR_EL1_SH0_Outer | (25 << TCR_EL1_T1SZ_SHIFT) | (25 << TCR_EL1_T0SZ_SHIFT))
|
||||
|
||||
.set SCTLR_EL1_I, (1 << 12)
|
||||
.set SCTLR_EL1_C, (1 << 2)
|
||||
.set SCTLR_EL1_M, (1 << 0)
|
||||
|
||||
.section .text._entry
|
||||
.global __aa64_enter_upper
|
||||
.type __aa64_enter_upper, %function
|
||||
__aa64_enter_upper:
|
||||
// x8 -- FDT base
|
||||
// x9 -- upper entry point
|
||||
|
||||
// Setup TTBR1_EL1
|
||||
// TODO fix macros
|
||||
ADR_ABS x5, KERNEL_TTBR1
|
||||
ADR_ABS x6, KERNEL_OFFSET
|
||||
|
||||
// x5 = KERNEL_TTBR1 physical address
|
||||
sub x5, x5, x6
|
||||
|
||||
// Fill KERNEL_TTBR1 table with upper-mapped Normal memory
|
||||
.fill_ttbr1:
|
||||
mov x2, #256
|
||||
1:
|
||||
sub x2, x2, #1
|
||||
|
||||
// x0 = (x2 << 30) | attrs...
|
||||
lsl x1, x2, #30
|
||||
mov x0, #(PTE_BLOCK_ISH | PTE_BLOCK_AF | PTE_PRESENT)
|
||||
orr x0, x0, x1
|
||||
|
||||
str x0, [x5, x2, lsl #3]
|
||||
|
||||
cbnz x2, 1b
|
||||
|
||||
.init_mmu_regs:
|
||||
mov x0, #(MAIR_EL1_Attr0_Normal_Outer_NC | MAIR_EL1_Attr0_Normal_Inner_NC | MAIR_EL1_Attr1_Device | MAIR_EL1_Attr1_Device_nGnRE)
|
||||
msr mair_el1, x0
|
||||
|
||||
// Test for 4KiB page support
|
||||
mrs x0, ID_AA64MMFR0_EL1
|
||||
mov x1, ID_AA64MMFR0_EL1_TGran4
|
||||
tst x0, x1
|
||||
bne .no_4k_gran
|
||||
|
||||
// x0 = PARange
|
||||
and x0, x0, #0xF
|
||||
lsl x0, x0, #TCR_EL1_IPS_SHIFT
|
||||
MOV_L x1, TCR_EL1_ATTRS
|
||||
orr x0, x0, x1
|
||||
msr tcr_el1, x0
|
||||
|
||||
msr ttbr0_el1, x5
|
||||
msr ttbr1_el1, x5
|
||||
|
||||
dsb ish
|
||||
isb
|
||||
|
||||
mrs x0, sctlr_el1
|
||||
orr x0, x0, #SCTLR_EL1_M
|
||||
msr sctlr_el1, x0
|
||||
|
||||
mov x0, x8
|
||||
br x9
|
||||
.no_4k_gran:
|
||||
b .
|
||||
.size __aa64_enter_upper, . - __aa64_enter_upper
|
||||
@@ -6,6 +6,7 @@ use crate::mem::{
|
||||
phys::{self, PageUsage},
|
||||
};
|
||||
use core::mem::size_of;
|
||||
use core::arch::global_asm;
|
||||
|
||||
struct Stack {
|
||||
bp: usize,
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
//! AArch64 exception handling
|
||||
|
||||
use crate::arch::machine;
|
||||
use crate::arch::{machine, intrin};
|
||||
use crate::debug::Level;
|
||||
use crate::dev::irq::{IntController, IrqContext};
|
||||
use crate::mem;
|
||||
use crate::proc::{sched, Process, Thread};
|
||||
use crate::proc::{sched, Thread};
|
||||
use crate::syscall;
|
||||
use cortex_a::registers::{ESR_EL1, FAR_EL1};
|
||||
use libsys::{abi::SystemCall, signal::Signal, error::Errno};
|
||||
use tock_registers::interfaces::Readable;
|
||||
use core::arch::global_asm;
|
||||
|
||||
/// Trapped SIMD/FP functionality
|
||||
pub const EC_FP_TRAP: u64 = 0b000111;
|
||||
@@ -97,7 +98,9 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
||||
|
||||
let res = proc.manipulate_space(|space| {
|
||||
space.try_cow_copy(far)?;
|
||||
Process::invalidate_asid(asid);
|
||||
unsafe {
|
||||
intrin::flush_tlb_asid(asid);
|
||||
}
|
||||
Result::<(), Errno>::Ok(())
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//! AArch64-specific assembly functions
|
||||
use core::arch::asm;
|
||||
|
||||
/// Disables delievery of IRQs
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: requires EL0
|
||||
#[inline(always)]
|
||||
pub unsafe fn irq_disable() {
|
||||
asm!("msr daifset, {bits}", bits = const 2, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
/// Discards an entry related to `addr` from TLB cache
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: requires EL0
|
||||
#[inline(always)]
|
||||
pub unsafe fn flush_tlb_virt(_addr: usize) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Discards all entries related to `asid` from TLB cache
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only safe to use for known [Process]es and their ASIDs
|
||||
// TODO non-portable
|
||||
#[inline(always)]
|
||||
pub unsafe fn flush_tlb_asid(asid: usize) {
|
||||
asm!("tlbi aside1, {}", in(reg) asid);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::dev::Device;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::arch::intrin;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
@@ -72,7 +73,7 @@ impl RWdog {
|
||||
regs.CTRL.write(CTRL::KEY::Value + CTRL::RESTART::SET);
|
||||
|
||||
loop {
|
||||
asm!("wfe");
|
||||
intrin::hang();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::dev::{
|
||||
serial::{pl011::Pl011, SerialDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::fs::devfs::{self, CharDeviceType};
|
||||
use crate::mem::phys;
|
||||
use libsys::error::Errno;
|
||||
|
||||
@@ -37,6 +38,7 @@ pub fn init_board() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
IRQCHIP.enable()?;
|
||||
UART.init_irqs()?;
|
||||
devfs::add_char_device(&UART, CharDeviceType::TtySerial)?;
|
||||
|
||||
EMMC.enable()?;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use tock_registers::interfaces::{Readable, Writeable};
|
||||
pub mod boot;
|
||||
pub mod context;
|
||||
pub mod exception;
|
||||
pub mod intrin;
|
||||
pub mod irq;
|
||||
pub mod reg;
|
||||
pub mod timer;
|
||||
@@ -34,7 +35,7 @@ cfg_if! {
|
||||
#[inline(always)]
|
||||
pub unsafe fn irq_mask_save() -> u64 {
|
||||
let state = DAIF.get();
|
||||
asm!("msr daifset, {bits}", bits = const 2, options(nomem, nostack, preserves_flags));
|
||||
intrin::irq_disable();
|
||||
state
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
//! CNTKCTL_EL1 register
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u64,
|
||||
/// Counter-timer Kernel Control Register
|
||||
pub CNTKCTL_EL1 [
|
||||
/// If set, disables CNTPCT and CNTFRQ trapping from EL0
|
||||
EL0PCTEN OFFSET(0) NUMBITS(1) []
|
||||
]
|
||||
}
|
||||
|
||||
/// CNTKCTL_EL1 register
|
||||
pub struct Reg;
|
||||
|
||||
impl Readable for Reg {
|
||||
type T = u64;
|
||||
type R = CNTKCTL_EL1::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&self) -> Self::T {
|
||||
let mut tmp;
|
||||
unsafe {
|
||||
asm!("mrs {}, cntkctl_el1", out(reg) tmp);
|
||||
}
|
||||
tmp
|
||||
}
|
||||
}
|
||||
|
||||
impl Writeable for Reg {
|
||||
type T = u64;
|
||||
type R = CNTKCTL_EL1::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn set(&self, value: Self::T) {
|
||||
unsafe {
|
||||
asm!("msr cntkctl_el1, {}", in(reg) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CNTKCTL_EL1 register
|
||||
pub const CNTKCTL_EL1: Reg = Reg;
|
||||
@@ -1,57 +0,0 @@
|
||||
//! CPACR_EL1 register
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u64,
|
||||
/// EL1 Architectural Feature Access Control Register
|
||||
pub CPACR_EL1 [
|
||||
/// Enable EL0 and EL1 SIMD/FP accesses to EL1
|
||||
FPEN OFFSET(20) NUMBITS(2) [
|
||||
/// Trap both EL0 and EL1
|
||||
TrapAll = 0,
|
||||
/// Trap EL0
|
||||
TrapEl0 = 1,
|
||||
/// Trap EL1
|
||||
TrapEl1 = 2,
|
||||
/// Do not trap any SIMD/FP instructions
|
||||
TrapNone = 3
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
/// CPACR_EL1 register
|
||||
pub struct Reg;
|
||||
|
||||
impl Readable for Reg {
|
||||
type T = u64;
|
||||
type R = CPACR_EL1::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&self) -> Self::T {
|
||||
let mut tmp;
|
||||
unsafe {
|
||||
asm!("mrs {}, cpacr_el1", out(reg) tmp);
|
||||
}
|
||||
tmp
|
||||
}
|
||||
}
|
||||
|
||||
impl Writeable for Reg {
|
||||
type T = u64;
|
||||
type R = CPACR_EL1::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn set(&self, value: Self::T) {
|
||||
unsafe {
|
||||
asm!("msr cpacr_el1, {}", in(reg) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CPACR_EL1 register
|
||||
pub const CPACR_EL1: Reg = Reg;
|
||||
@@ -1,7 +1,68 @@
|
||||
//! AArch64 architectural registers
|
||||
|
||||
pub mod cpacr_el1;
|
||||
pub use cpacr_el1::CPACR_EL1;
|
||||
use core::arch::asm;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields,
|
||||
};
|
||||
|
||||
pub mod cntkctl_el1;
|
||||
pub use cntkctl_el1::CNTKCTL_EL1;
|
||||
macro_rules! wrap_msr {
|
||||
($struct_name:ident, $name:ident, $reg:literal, $fields:tt) => {
|
||||
#[allow(missing_docs)]
|
||||
pub struct $struct_name;
|
||||
|
||||
register_bitfields! {
|
||||
u64,
|
||||
#[allow(missing_docs)]
|
||||
pub $name $fields
|
||||
}
|
||||
|
||||
impl Readable for $struct_name {
|
||||
type T = u64;
|
||||
type R = $name::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&self) -> Self::T {
|
||||
let mut value;
|
||||
unsafe {
|
||||
asm!(concat!("mrs {}, ", $reg), out(reg) value)
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl Writeable for $struct_name {
|
||||
type T = u64;
|
||||
type R = $name::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn set(&self, value: Self::T) {
|
||||
unsafe {
|
||||
asm!(concat!("msr ", $reg, ", {}"), in(reg) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const $name: $struct_name = $struct_name;
|
||||
};
|
||||
}
|
||||
|
||||
wrap_msr!(CpacrEl1, CPACR_EL1, "cpacr_el1", [
|
||||
/// Enable EL0 and EL1 SIMD/FP accesses to EL1
|
||||
FPEN OFFSET(20) NUMBITS(2) [
|
||||
/// Trap both EL0 and EL1
|
||||
TrapAll = 0,
|
||||
/// Trap EL0
|
||||
TrapEl0 = 1,
|
||||
/// Trap EL1
|
||||
TrapEl1 = 2,
|
||||
/// Do not trap any SIMD/FP instructions
|
||||
TrapNone = 3
|
||||
]
|
||||
]);
|
||||
|
||||
wrap_msr!(CntkctlEl1, CNTKCTL_EL1, "cntkctl_el1", [
|
||||
/// If set, disables CNTPCT and CNTFRQ trapping from EL0
|
||||
EL0PCTEN OFFSET(0) NUMBITS(1) []
|
||||
]);
|
||||
|
||||
@@ -15,6 +15,8 @@ cfg_if! {
|
||||
|
||||
pub use aarch64 as platform;
|
||||
pub use aarch64::machine;
|
||||
|
||||
pub use aarch64::intrin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ use libsys::{debug::TraceLevel, error::Errno};
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
|
||||
/// Currently active print level
|
||||
pub static LEVEL: Level = Level::Debug;
|
||||
|
||||
/// Kernel logging levels
|
||||
|
||||
+13
-19
@@ -1,25 +1,14 @@
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
tty::{CharRing, TtyDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::{error::Errno, ioctl::IoctlCmd};
|
||||
//! Virtual (pseudo) device implemetation
|
||||
use crate::dev::Device;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use libsys::{error::Errno, ioctl::IoctlCmd};
|
||||
use vfs::CharDevice;
|
||||
|
||||
/// Pseudorandom number generator device
|
||||
pub struct Random {
|
||||
state: AtomicU32
|
||||
state: AtomicU32,
|
||||
}
|
||||
/// Zero device
|
||||
pub struct Zero;
|
||||
|
||||
impl Device for Random {
|
||||
@@ -53,7 +42,6 @@ impl CharDevice for Random {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Device for Zero {
|
||||
fn name(&self) -> &'static str {
|
||||
"Zero device"
|
||||
@@ -84,10 +72,12 @@ impl CharDevice for Zero {
|
||||
}
|
||||
|
||||
impl Random {
|
||||
/// Initializes PRNG with a seed value
|
||||
pub fn set_state(&self, state: u32) {
|
||||
self.state.store(state, Ordering::Release);
|
||||
}
|
||||
|
||||
/// Returns a single pseudo-random value
|
||||
pub fn read_single(&self) -> u32 {
|
||||
let mut x = self.state.load(Ordering::Acquire);
|
||||
x ^= x << 13;
|
||||
@@ -98,5 +88,9 @@ impl Random {
|
||||
}
|
||||
}
|
||||
|
||||
pub static RANDOM: Random = Random { state: AtomicU32::new(0) };
|
||||
/// Pseudorandom number generator device
|
||||
pub static RANDOM: Random = Random {
|
||||
state: AtomicU32::new(0),
|
||||
};
|
||||
/// Zero device
|
||||
pub static ZERO: Zero = Zero;
|
||||
|
||||
+14
-6
@@ -1,9 +1,10 @@
|
||||
//! Device list pseudo-filesystem
|
||||
use crate::util::InitOnce;
|
||||
use alloc::boxed::Box;
|
||||
use core::cell::RefCell;
|
||||
use vfs::CharDevice;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use libsys::{stat::FileMode, error::Errno};
|
||||
use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeKind, VnodeRef};
|
||||
use libsys::{error::Errno, stat::FileMode};
|
||||
use vfs::{Vnode, VnodeData, VnodeRef};
|
||||
|
||||
/// Possible character device kinds
|
||||
#[derive(Debug)]
|
||||
@@ -16,7 +17,12 @@ static DEVFS_ROOT: InitOnce<VnodeRef> = InitOnce::new();
|
||||
|
||||
/// Initializes devfs
|
||||
pub fn init() {
|
||||
let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
|
||||
let node = Vnode::new(
|
||||
"",
|
||||
VnodeData::Directory(RefCell::new(None)),
|
||||
Vnode::CACHE_READDIR | Vnode::CACHE_STAT,
|
||||
);
|
||||
// let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
|
||||
node.props_mut().mode = FileMode::default_dir();
|
||||
DEVFS_ROOT.init(node);
|
||||
}
|
||||
@@ -26,12 +32,14 @@ pub fn root() -> &'static VnodeRef {
|
||||
DEVFS_ROOT.get()
|
||||
}
|
||||
|
||||
/// Adds device `dev` to devfs with `name`
|
||||
pub fn add_named_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> {
|
||||
infoln!("Add char device: {}", name);
|
||||
|
||||
let node = Vnode::new(name, VnodeKind::Char, Vnode::CACHE_STAT);
|
||||
let node = Vnode::new(name, VnodeData::Char(dev), Vnode::CACHE_STAT);
|
||||
// let node = Vnode::new(name, VnodeKind::Char, Vnode::CACHE_STAT);
|
||||
// node.set_data(Box::new(CharDeviceWrapper::new(dev)));
|
||||
node.props_mut().mode = FileMode::from_bits(0o600).unwrap() | FileMode::S_IFCHR;
|
||||
node.set_data(Box::new(CharDeviceWrapper::new(dev)));
|
||||
|
||||
DEVFS_ROOT.get().attach(node);
|
||||
|
||||
|
||||
+104
-41
@@ -1,27 +1,25 @@
|
||||
//! System control/info virtual filesystem
|
||||
use crate::debug::{self, Level};
|
||||
use crate::util::InitOnce;
|
||||
use alloc::boxed::Box;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use fs_macros::auto_inode;
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::{FileMode, OpenFlags, Stat},
|
||||
};
|
||||
use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
use core::cell::RefCell;
|
||||
use core::fmt::{self, Write};
|
||||
use core::str::FromStr;
|
||||
use crate::debug::{self, Level};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
stat::{FileMode, OpenFlags, Stat},
|
||||
};
|
||||
use vfs::{Vnode, VnodeCommon, VnodeData, VnodeFile, VnodeRef};
|
||||
|
||||
struct NodeData<
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno>,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno>,
|
||||
> {
|
||||
struct NodeData<R: Fn(&mut [u8]) -> Result<usize, Errno>, W: Fn(&[u8]) -> Result<usize, Errno>> {
|
||||
read_func: R,
|
||||
write_func: W,
|
||||
}
|
||||
|
||||
struct BufferWriter<'a> {
|
||||
dst: &'a mut [u8],
|
||||
pos: usize
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Write for BufferWriter<'a> {
|
||||
@@ -47,11 +45,8 @@ impl<'a> BufferWriter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl<
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno>,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno>,
|
||||
> VnodeImpl for NodeData<R, W>
|
||||
impl<R: Fn(&mut [u8]) -> Result<usize, Errno>, W: Fn(&[u8]) -> Result<usize, Errno>> VnodeCommon
|
||||
for NodeData<R, W>
|
||||
{
|
||||
fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
@@ -60,7 +55,36 @@ impl<
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
/// Performs filetype-specific request
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_cmd: IoctlCmd,
|
||||
_ptr: usize,
|
||||
_len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Retrieves file status
|
||||
fn stat(&mut self, _node: VnodeRef) -> Result<Stat, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Reports the size of this filesystem object in bytes
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns `true` if node is ready for an operation
|
||||
fn ready(&mut self, _node: VnodeRef, _write: bool) -> Result<bool, Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Fn(&mut [u8]) -> Result<usize, Errno>, W: Fn(&[u8]) -> Result<usize, Errno>> VnodeFile
|
||||
for NodeData<R, W>
|
||||
{
|
||||
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
if pos != 0 {
|
||||
// TODO handle this
|
||||
@@ -76,12 +100,13 @@ impl<
|
||||
}
|
||||
(self.write_func)(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno>,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno>,
|
||||
> NodeData<R, W>
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
impl<R: Fn(&mut [u8]) -> Result<usize, Errno>, W: Fn(&[u8]) -> Result<usize, Errno>>
|
||||
NodeData<R, W>
|
||||
{
|
||||
pub const fn new(read_func: R, write_func: W) -> Self {
|
||||
Self {
|
||||
@@ -92,7 +117,6 @@ impl<
|
||||
}
|
||||
|
||||
static SYSFS_ROOT: InitOnce<VnodeRef> = InitOnce::new();
|
||||
static TEST_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
// TODO subdirs
|
||||
fn add_generic_node<R, W>(parent: Option<VnodeRef>, name: &str, mode: FileMode, read: R, write: W)
|
||||
@@ -100,9 +124,12 @@ where
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno> + 'static,
|
||||
{
|
||||
let node = Vnode::new(name, VnodeKind::Regular, Vnode::CACHE_STAT);
|
||||
let node = Vnode::new(
|
||||
name,
|
||||
VnodeData::File(RefCell::new(Some(Box::new(NodeData::new(read, write))))),
|
||||
Vnode::CACHE_STAT,
|
||||
);
|
||||
node.props_mut().mode = mode | FileMode::S_IFREG;
|
||||
node.set_data(Box::new(NodeData::new(read, write)));
|
||||
|
||||
if let Some(parent) = parent {
|
||||
parent.attach(node);
|
||||
@@ -111,20 +138,42 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a node with and `read` and `write` operations
|
||||
pub fn add_read_write_node<R, W>(parent: Option<VnodeRef>, name: &str, read: R, write: W)
|
||||
where
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno> + 'static,
|
||||
{
|
||||
add_generic_node(parent, name, FileMode::from_bits(0o600).unwrap(), read, write)
|
||||
add_generic_node(
|
||||
parent,
|
||||
name,
|
||||
FileMode::from_bits(0o600).unwrap(),
|
||||
read,
|
||||
write,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_read_node<R>(parent: Option<VnodeRef>, name: &str, read: R) where R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static {
|
||||
add_generic_node(parent, name, FileMode::from_bits(0o400).unwrap(), read, |_| Err(Errno::ReadOnly))
|
||||
/// Adds `read`-only node
|
||||
pub fn add_read_node<R>(parent: Option<VnodeRef>, name: &str, read: R)
|
||||
where
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static,
|
||||
{
|
||||
add_generic_node(
|
||||
parent,
|
||||
name,
|
||||
FileMode::from_bits(0o400).unwrap(),
|
||||
read,
|
||||
|_| Err(Errno::ReadOnly),
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a directory in sysfs structure
|
||||
pub fn add_directory(parent: Option<VnodeRef>, name: &str) -> Result<VnodeRef, Errno> {
|
||||
let node = Vnode::new(name, VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
|
||||
let node = Vnode::new(
|
||||
name,
|
||||
VnodeData::Directory(RefCell::new(None)),
|
||||
Vnode::CACHE_READDIR | Vnode::CACHE_STAT,
|
||||
);
|
||||
node.props_mut().mode = FileMode::from_bits(0o500).unwrap() | FileMode::S_IFDIR;
|
||||
|
||||
if let Some(parent) = parent {
|
||||
@@ -136,26 +185,39 @@ pub fn add_directory(parent: Option<VnodeRef>, name: &str) -> Result<VnodeRef, E
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
/// Returns sysfs root node reference
|
||||
pub fn root() -> &'static VnodeRef {
|
||||
SYSFS_ROOT.get()
|
||||
}
|
||||
|
||||
/// Sets up the sysfs tree
|
||||
pub fn init() {
|
||||
let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
|
||||
let node = Vnode::new(
|
||||
"",
|
||||
VnodeData::Directory(RefCell::new(None)),
|
||||
Vnode::CACHE_READDIR | Vnode::CACHE_STAT,
|
||||
);
|
||||
node.props_mut().mode = FileMode::default_dir();
|
||||
SYSFS_ROOT.init(node);
|
||||
|
||||
let debug_dir = add_directory(None, "debug").unwrap();
|
||||
|
||||
add_read_write_node(Some(debug_dir.clone()), "level", |buf| {
|
||||
let mut writer = BufferWriter::new(buf);
|
||||
write!(&mut writer, "{}\n", debug::LEVEL as u32).map_err(|_| Errno::InvalidArgument)?;
|
||||
Ok(writer.count())
|
||||
}, |buf| {
|
||||
let s = core::str::from_utf8(buf).map_err(|_| Errno::InvalidArgument)?;
|
||||
let value = u32::from_str(s).map_err(|_| Errno::InvalidArgument).and_then(Level::try_from)?;
|
||||
todo!()
|
||||
});
|
||||
add_read_write_node(
|
||||
Some(debug_dir),
|
||||
"level",
|
||||
|buf| {
|
||||
let mut writer = BufferWriter::new(buf);
|
||||
writeln!(&mut writer, "{}", debug::LEVEL as u32).map_err(|_| Errno::InvalidArgument)?;
|
||||
Ok(writer.count())
|
||||
},
|
||||
|buf| {
|
||||
let s = core::str::from_utf8(buf).map_err(|_| Errno::InvalidArgument)?;
|
||||
let _value = u32::from_str(s)
|
||||
.map_err(|_| Errno::InvalidArgument)
|
||||
.and_then(Level::try_from)?;
|
||||
todo!()
|
||||
},
|
||||
);
|
||||
|
||||
add_read_node(None, "uptime", |buf| {
|
||||
use crate::arch::machine;
|
||||
@@ -163,7 +225,8 @@ pub fn init() {
|
||||
|
||||
let mut writer = BufferWriter::new(buf);
|
||||
let time = machine::local_timer().timestamp()?;
|
||||
write!(&mut writer, "{} {}\n", time.as_secs(), time.subsec_nanos()).map_err(|_| Errno::InvalidArgument)?;
|
||||
writeln!(&mut writer, "{} {}", time.as_secs(), time.subsec_nanos())
|
||||
.map_err(|_| Errno::InvalidArgument)?;
|
||||
Ok(writer.count())
|
||||
});
|
||||
}
|
||||
|
||||
+2
-6
@@ -1,19 +1,15 @@
|
||||
//! osdve5 crate (lol)
|
||||
#![feature(
|
||||
asm,
|
||||
global_asm,
|
||||
const_for,
|
||||
const_mut_refs,
|
||||
const_raw_ptr_deref,
|
||||
const_fn_fn_ptr_basics,
|
||||
const_fn_trait_bound,
|
||||
const_trait_impl,
|
||||
const_panic,
|
||||
panic_info_message,
|
||||
alloc_error_handler,
|
||||
linked_list_cursors,
|
||||
const_btree_new,
|
||||
const_generics_defaults,
|
||||
asm_const,
|
||||
)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
@@ -44,7 +40,7 @@ pub mod util;
|
||||
#[panic_handler]
|
||||
fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
asm!("msr daifset, #2");
|
||||
arch::intrin::irq_disable();
|
||||
}
|
||||
|
||||
errorln!("Panic: {:?}", pi);
|
||||
|
||||
@@ -26,3 +26,19 @@ pub fn kernel_end_phys() -> usize {
|
||||
}
|
||||
unsafe { &__kernel_end as *const _ as usize - KERNEL_OFFSET }
|
||||
}
|
||||
|
||||
// TODO cross-platform variant
|
||||
/// Returns `true` if `virt` address is accessible for requested operation
|
||||
#[inline(always)]
|
||||
pub fn is_el0_accessible(virt: usize, write: bool) -> bool {
|
||||
use core::arch::asm;
|
||||
let mut res: usize;
|
||||
unsafe {
|
||||
if write {
|
||||
asm!("at s1e0w, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
|
||||
} else {
|
||||
asm!("at s1e0r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
|
||||
}
|
||||
}
|
||||
res & 1 == 0
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ use crate::sync::IrqSafeSpinLock;
|
||||
use core::mem;
|
||||
use libsys::{error::Errno, mem::memcpy};
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe to implement because of direct memory manipulation
|
||||
pub unsafe trait Manager {
|
||||
fn alloc_page(&mut self, pu: PageUsage) -> Result<usize, Errno>;
|
||||
fn alloc_contiguous_pages(&mut self, pu: PageUsage, count: usize) -> Result<usize, Errno>;
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::mem::{
|
||||
};
|
||||
use core::ops::{Index, IndexMut};
|
||||
use libsys::{error::Errno, mem::memset};
|
||||
use core::arch::asm;
|
||||
|
||||
/// Transparent wrapper structure representing a single
|
||||
/// translation table entry
|
||||
|
||||
+15
-5
@@ -1,7 +1,10 @@
|
||||
//! Process file descriptors and I/O context
|
||||
use alloc::collections::BTreeMap;
|
||||
use libsys::{error::Errno, stat::{FileDescriptor, UserId, GroupId}};
|
||||
use vfs::{FileRef, Ioctx, VnodeRef, VnodeKind};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::{FileDescriptor, GroupId, UserId},
|
||||
};
|
||||
use vfs::{FileRef, Ioctx, VnodeRef};
|
||||
|
||||
/// Process I/O context. Contains file tables, root/cwd info etc.
|
||||
pub struct ProcessIo {
|
||||
@@ -24,7 +27,7 @@ impl ProcessIo {
|
||||
|
||||
/// Sets controlling terminal for the process
|
||||
pub fn set_ctty(&mut self, node: VnodeRef) {
|
||||
assert_eq!(node.kind(), VnodeKind::Char);
|
||||
// assert_eq!(node.kind(), VnodeKind::Char);
|
||||
self.ctty = Some(node);
|
||||
}
|
||||
|
||||
@@ -74,7 +77,11 @@ impl ProcessIo {
|
||||
}
|
||||
|
||||
/// Clones a file descriptor into an available slot or, if specified, requested one
|
||||
pub fn duplicate_file(&mut self, src: FileDescriptor, dst: Option<FileDescriptor>) -> Result<FileDescriptor, Errno> {
|
||||
pub fn duplicate_file(
|
||||
&mut self,
|
||||
src: FileDescriptor,
|
||||
dst: Option<FileDescriptor>,
|
||||
) -> Result<FileDescriptor, Errno> {
|
||||
let file_ref = self.file(src)?;
|
||||
if let Some(dst) = dst {
|
||||
let idx = u32::from(dst);
|
||||
@@ -91,7 +98,10 @@ impl ProcessIo {
|
||||
|
||||
/// Returns [File] struct referred to by file descriptor `idx`
|
||||
pub fn file(&mut self, fd: FileDescriptor) -> Result<FileRef, Errno> {
|
||||
self.files.get(&u32::from(fd)).cloned().ok_or(Errno::InvalidFile)
|
||||
self.files
|
||||
.get(&u32::from(fd))
|
||||
.cloned()
|
||||
.ok_or(Errno::InvalidFile)
|
||||
}
|
||||
|
||||
/// Returns [Ioctx] structure reference of this I/O context
|
||||
|
||||
+13
-14
@@ -1,14 +1,14 @@
|
||||
//! Process data and control
|
||||
use crate::arch::aarch64::exception::ExceptionFrame;
|
||||
use crate::arch::{aarch64::exception::ExceptionFrame, intrin};
|
||||
use crate::mem::{
|
||||
self,
|
||||
phys::{self, PageUsage},
|
||||
virt::{MapAttributes, Space},
|
||||
};
|
||||
use crate::proc::{
|
||||
wait::Wait, Context, ProcessIo, Thread, ThreadRef, ThreadState, PROCESSES, SCHED, Tid,
|
||||
wait::Wait, Context, ProcessIo, Thread, ThreadRef, ThreadState, Tid, PROCESSES, SCHED,
|
||||
};
|
||||
use crate::sync::{IrqSafeSpinLock};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use alloc::{rc::Rc, vec::Vec};
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use libsys::{
|
||||
@@ -165,7 +165,8 @@ impl Process {
|
||||
loop {
|
||||
let state = self.signal_state.load(Ordering::Acquire);
|
||||
if let Some(signal) = Self::find1(state).map(|e| Signal::try_from(e as u32).unwrap()) {
|
||||
self.signal_state.fetch_and(!(1 << (signal as u32)), Ordering::Release);
|
||||
self.signal_state
|
||||
.fetch_and(!(1 << (signal as u32)), Ordering::Release);
|
||||
main_thread.clone().enter_signal(signal, ttbr0);
|
||||
} else {
|
||||
break;
|
||||
@@ -189,7 +190,8 @@ impl Process {
|
||||
main_thread.enter_signal(signal, ttbr0);
|
||||
}
|
||||
ThreadState::Waiting => {
|
||||
self.signal_state.fetch_or(1 << (signal as u32), Ordering::Release);
|
||||
self.signal_state
|
||||
.fetch_or(1 << (signal as u32), Ordering::Release);
|
||||
main_thread.interrupt_wait(true);
|
||||
}
|
||||
ThreadState::Ready => {
|
||||
@@ -288,7 +290,7 @@ impl Process {
|
||||
if let Some(space) = lock.space.take() {
|
||||
unsafe {
|
||||
Space::release(space);
|
||||
Process::invalidate_asid((lock.id.asid() as usize) << 48);
|
||||
intrin::flush_tlb_asid((lock.id.asid() as usize) << 48);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,18 +462,15 @@ impl Process {
|
||||
Ok(base + offset)
|
||||
}
|
||||
|
||||
/// Returns the process's address space ID
|
||||
pub fn asid(&self) -> usize {
|
||||
(self.id().asid() as usize) << 48
|
||||
}
|
||||
|
||||
/// Flushes TLB cache for the process address space
|
||||
pub fn invalidate_tlb(&self) {
|
||||
Process::invalidate_asid(self.asid());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn invalidate_asid(asid: usize) {
|
||||
unsafe {
|
||||
asm!("tlbi aside1, {}", in(reg) asid);
|
||||
intrin::flush_tlb_asid(self.asid());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,7 +481,7 @@ impl Process {
|
||||
) -> Result<(), Errno> {
|
||||
unsafe {
|
||||
// Run with interrupts disabled
|
||||
asm!("msr daifset, #2");
|
||||
intrin::irq_disable();
|
||||
}
|
||||
|
||||
let proc = Process::current();
|
||||
@@ -539,7 +538,7 @@ impl Process {
|
||||
// TODO drop old context
|
||||
let ctx = thread.ctx.get();
|
||||
let asid = (process_lock.id.asid() as usize) << 48;
|
||||
Process::invalidate_asid(asid);
|
||||
intrin::flush_tlb_asid(asid);
|
||||
|
||||
ctx.write(Context::user(
|
||||
entry,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//!
|
||||
use crate::proc::{Thread, ThreadRef, THREADS};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::arch::intrin;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::proc::Tid;
|
||||
use alloc::{collections::VecDeque, rc::Rc};
|
||||
@@ -69,7 +70,7 @@ impl Scheduler {
|
||||
THREADS.lock().get(&id).unwrap().clone()
|
||||
};
|
||||
|
||||
asm!("msr daifset, #2");
|
||||
intrin::irq_disable();
|
||||
Thread::enter(thread)
|
||||
}
|
||||
|
||||
@@ -121,7 +122,7 @@ impl Scheduler {
|
||||
|
||||
if !Rc::ptr_eq(&from, &to) {
|
||||
unsafe {
|
||||
asm!("msr daifset, #2");
|
||||
intrin::irq_disable();
|
||||
Thread::switch(from, to, discard);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ pub fn select(
|
||||
if let Some(read) = &read {
|
||||
for fd in read.iter() {
|
||||
let file = io.file(fd)?;
|
||||
if file.borrow().is_ready(false)? {
|
||||
if file.borrow().ready(false)? {
|
||||
rfds.as_mut().unwrap().set(fd);
|
||||
return Ok(1);
|
||||
}
|
||||
@@ -107,7 +107,7 @@ pub fn select(
|
||||
if let Some(write) = &write {
|
||||
for fd in write.iter() {
|
||||
let file = io.file(fd)?;
|
||||
if file.borrow().is_ready(true)? {
|
||||
if file.borrow().ready(true)? {
|
||||
wfds.as_mut().unwrap().set(fd);
|
||||
return Ok(1);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! System call argument ABI helpers
|
||||
|
||||
use crate::mem;
|
||||
use crate::arch::intrin;
|
||||
use core::alloc::Layout;
|
||||
use libsys::error::Errno;
|
||||
use crate::proc::Process;
|
||||
@@ -23,19 +24,6 @@ macro_rules! invalid_memory {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_el0_accessible(virt: usize, write: bool) -> bool {
|
||||
let mut res: usize;
|
||||
unsafe {
|
||||
if write {
|
||||
asm!("at s1e0w, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
|
||||
} else {
|
||||
asm!("at s1e0r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
|
||||
}
|
||||
}
|
||||
res & 1 == 0
|
||||
}
|
||||
|
||||
/// Checks given argument and interprets it as a `T` reference
|
||||
pub fn struct_ref<'a, T>(base: usize) -> Result<&'a T, Errno> {
|
||||
let layout = Layout::new::<T>();
|
||||
@@ -125,13 +113,15 @@ pub fn validate_ptr(base: usize, len: usize, write: bool) -> Result<(), Errno> {
|
||||
let asid = process.asid();
|
||||
|
||||
for i in (base / mem::PAGE_SIZE)..((base + len + mem::PAGE_SIZE - 1) / mem::PAGE_SIZE) {
|
||||
if !is_el0_accessible(i * mem::PAGE_SIZE, write) {
|
||||
if !mem::is_el0_accessible(i * mem::PAGE_SIZE, write) {
|
||||
// It's possible a CoW page hasn't yet been cloned when trying
|
||||
// a write access
|
||||
let res = if write {
|
||||
process.manipulate_space(|space| {
|
||||
space.try_cow_copy(i * mem::PAGE_SIZE)?;
|
||||
Process::invalidate_asid(asid);
|
||||
unsafe {
|
||||
intrin::flush_tlb_asid(asid);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -247,6 +247,26 @@ fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
proc.manipulate_space(move |space| space.free(addr, len / 4096))?;
|
||||
Ok(0)
|
||||
}
|
||||
SystemCall::CreateDirectory => {
|
||||
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
|
||||
let path = arg::str_ref(args[1], args[2])?;
|
||||
let mode = FileMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
|
||||
// TODO honor this option
|
||||
let _flags = args[4] as u32;
|
||||
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
let at = if let Some(fd) = at_fd {
|
||||
io.file(fd)?.borrow().node()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
io.ioctx().mkdir(at, path, mode)?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
// Process
|
||||
SystemCall::Clone => {
|
||||
|
||||
+3
-2
@@ -29,8 +29,9 @@ impl<T> InitOnce<T> {
|
||||
/// Returns the initialized value. Will panic if the value has not
|
||||
/// yet been initialized.
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
#[track_caller]
|
||||
pub fn get(&self) -> &mut T {
|
||||
assert!(self.is_initialized(), "Access to uninitialized InitOnce<T>");
|
||||
assert!(self.is_initialized(), "Access to uninitialized InitOnce<T>: {:?}", core::panic::Location::caller());
|
||||
unsafe { (*self.inner.get()).assume_init_mut() }
|
||||
}
|
||||
|
||||
@@ -70,6 +71,6 @@ macro_rules! block {
|
||||
}};
|
||||
|
||||
($cond:expr, $timeout:expr) => {
|
||||
crate::block!($cond, $timeout, return Err(error::Errno::TimedOut))
|
||||
crate::block!($cond, $timeout, return Err(libsys::error::Errno::TimedOut))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ pub enum SystemCall {
|
||||
Seek = 17,
|
||||
MapMemory = 18,
|
||||
UnmapMemory = 19,
|
||||
CreateDirectory = 20,
|
||||
|
||||
// Process manipulation
|
||||
Fork = 32,
|
||||
|
||||
+17
-2
@@ -10,6 +10,7 @@ use crate::{
|
||||
OpenFlags, Stat, UserId,
|
||||
},
|
||||
};
|
||||
use core::arch::asm;
|
||||
use core::time::Duration;
|
||||
|
||||
// TODO document the syscall ABI
|
||||
@@ -262,7 +263,8 @@ pub fn sys_ex_kill(pid: SignalDestination, signum: Signal) -> Result<(), Errno>
|
||||
pub fn sys_ex_clone(entry: usize, stack: usize, arg: usize) -> Result<Tid, Errno> {
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(SystemCall::Clone, argn!(entry), argn!(stack), argn!(arg))
|
||||
}).map(|e| Tid::from(e as u32))
|
||||
})
|
||||
.map(|e| Tid::from(e as u32))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -444,7 +446,6 @@ pub fn sys_mmap(
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// System call
|
||||
@@ -452,3 +453,17 @@ pub fn sys_mmap(
|
||||
pub unsafe fn sys_munmap(addr: usize, len: usize) -> Result<(), Errno> {
|
||||
Errno::from_syscall_unit(syscall!(SystemCall::UnmapMemory, argn!(addr), argn!(len)))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_mkdirat(fd: Option<FileDescriptor>, path: &str, mode: FileMode, flags: u32) -> Result<(), Errno> {
|
||||
Errno::from_syscall_unit(unsafe {
|
||||
syscall!(
|
||||
SystemCall::CreateDirectory,
|
||||
argn!(FileDescriptor::into_i32(fd)),
|
||||
argp!(path.as_ptr()),
|
||||
argn!(path.len()),
|
||||
argn!(mode.bits()),
|
||||
argn!(flags)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![feature(asm, const_panic)]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
|
||||
@@ -198,9 +198,9 @@ unsafe impl GlobalAlloc for Allocator {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||
#[cfg(feature = "verbose")]
|
||||
trace_debug!("free({:p}, {:?})", ptr, layout);
|
||||
trace_debug!("free({:p}, {:?})", ptr, _layout);
|
||||
assert!(!ptr.is_null());
|
||||
let mut block = ptr.sub(size_of::<Block>()) as *mut Block;
|
||||
let mut block_ref = &mut *block;
|
||||
|
||||
Vendored
+3
-1
@@ -1,7 +1,9 @@
|
||||
#[cfg(feature = "verbose")]
|
||||
use crate::trace;
|
||||
use alloc::vec::Vec;
|
||||
#[cfg(feature = "verbose")]
|
||||
use libsys::debug::TraceLevel;
|
||||
use libsys::{
|
||||
debug::TraceLevel,
|
||||
ProgramArgs,
|
||||
};
|
||||
|
||||
|
||||
Vendored
+27
-25
@@ -1,9 +1,11 @@
|
||||
use crate::io::{Read, read_line};
|
||||
use core::str::FromStr;
|
||||
use core::fmt;
|
||||
use crate::trace_debug;
|
||||
use crate::file::File;
|
||||
use libsys::{FixedStr, stat::{UserId, GroupId}};
|
||||
use crate::io::{self, read_line};
|
||||
use core::str::FromStr;
|
||||
use libsys::{
|
||||
stat::{GroupId, UserId},
|
||||
error::Errno,
|
||||
FixedStr,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct UserInfo {
|
||||
@@ -35,11 +37,11 @@ impl UserInfo {
|
||||
self.gid
|
||||
}
|
||||
|
||||
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, ()> {
|
||||
let mut file = File::open("/etc/passwd").map_err(|_| ())?;
|
||||
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, io::Error> {
|
||||
let mut file = File::open("/etc/passwd")?;
|
||||
let mut buf = [0; 128];
|
||||
loop {
|
||||
let line = read_line(&mut file, &mut buf).map_err(|_| ())?;
|
||||
let line = read_line(&mut file, &mut buf)?;
|
||||
if let Some(line) = line {
|
||||
let ent = UserInfo::from_str(line)?;
|
||||
if pred(&ent) {
|
||||
@@ -49,37 +51,37 @@ impl UserInfo {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
Err(io::Error::from(Errno::InvalidArgument))
|
||||
}
|
||||
|
||||
pub fn by_name(name: &str) -> Result<Self, ()> {
|
||||
pub fn by_name(name: &str) -> Result<Self, io::Error> {
|
||||
Self::find(|ent| ent.name() == name)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UserInfo {
|
||||
type Err = ();
|
||||
type Err = io::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, ()> {
|
||||
let mut iter = s.split(":");
|
||||
fn from_str(s: &str) -> Result<Self, io::Error> {
|
||||
let mut iter = s.split(':');
|
||||
|
||||
let name = iter.next().ok_or(())?;
|
||||
let name = iter.next().ok_or_else(|| io::Error::from(Errno::InvalidArgument))?;
|
||||
let uid = iter
|
||||
.next()
|
||||
.ok_or(())
|
||||
.and_then(|e| u32::from_str(e).map_err(|_| ()))
|
||||
.ok_or_else(|| io::Error::from(Errno::InvalidArgument))
|
||||
.and_then(|e| u32::from_str(e).map_err(|_| io::Error::from(Errno::InvalidArgument)))
|
||||
.map(UserId::from)?;
|
||||
let gid = iter
|
||||
.next()
|
||||
.ok_or(())
|
||||
.and_then(|e| u32::from_str(e).map_err(|_| ()))
|
||||
.ok_or_else(|| io::Error::from(Errno::InvalidArgument))
|
||||
.and_then(|e| u32::from_str(e).map_err(|_| io::Error::from(Errno::InvalidArgument)))
|
||||
.map(GroupId::from)?;
|
||||
let comment = iter.next().ok_or(())?;
|
||||
let home = iter.next().ok_or(())?;
|
||||
let shell = iter.next().ok_or(())?;
|
||||
let _comment = iter.next().ok_or_else(|| io::Error::from(Errno::InvalidArgument))?;
|
||||
let home = iter.next().ok_or_else(|| io::Error::from(Errno::InvalidArgument))?;
|
||||
let shell = iter.next().ok_or_else(|| io::Error::from(Errno::InvalidArgument))?;
|
||||
|
||||
if iter.next().is_some() {
|
||||
return Err(());
|
||||
return Err(io::Error::from(Errno::InvalidArgument));
|
||||
}
|
||||
|
||||
let mut res = Self {
|
||||
@@ -90,9 +92,9 @@ impl FromStr for UserInfo {
|
||||
shell: FixedStr::empty(),
|
||||
};
|
||||
|
||||
res.name.copy_from_str(&name);
|
||||
res.home.copy_from_str(&home);
|
||||
res.shell.copy_from_str(&shell);
|
||||
res.name.copy_from_str(name);
|
||||
res.home.copy_from_str(home);
|
||||
res.shell.copy_from_str(shell);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
Vendored
+12
-13
@@ -1,7 +1,7 @@
|
||||
use crate::file::File;
|
||||
use crate::io::{Read, read_line};
|
||||
use crate::io::{self, read_line};
|
||||
use core::str::FromStr;
|
||||
use libsys::FixedStr;
|
||||
use libsys::{FixedStr, error::Errno};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct UserShadow {
|
||||
@@ -18,12 +18,11 @@ impl UserShadow {
|
||||
self.password.as_str()
|
||||
}
|
||||
|
||||
|
||||
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, ()> {
|
||||
let mut file = File::open("/etc/shadow").map_err(|_| ())?;
|
||||
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, io::Error> {
|
||||
let mut file = File::open("/etc/shadow")?;
|
||||
let mut buf = [0; 128];
|
||||
loop {
|
||||
let line = read_line(&mut file, &mut buf).map_err(|_| ())?;
|
||||
let line = read_line(&mut file, &mut buf)?;
|
||||
if let Some(line) = line {
|
||||
let ent = UserShadow::from_str(line)?;
|
||||
if pred(&ent) {
|
||||
@@ -33,25 +32,25 @@ impl UserShadow {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
Err(io::Error::from(Errno::DoesNotExist))
|
||||
}
|
||||
|
||||
pub fn by_name(name: &str) -> Result<Self, ()> {
|
||||
pub fn by_name(name: &str) -> Result<Self, io::Error> {
|
||||
Self::find(|ent| ent.name() == name)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UserShadow {
|
||||
type Err = ();
|
||||
type Err = io::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, ()> {
|
||||
fn from_str(s: &str) -> Result<Self, io::Error> {
|
||||
let mut iter = s.split(':');
|
||||
|
||||
let name = iter.next().ok_or(())?;
|
||||
let password = iter.next().ok_or(())?;
|
||||
let name = iter.next().ok_or_else(|| io::Error::from(Errno::InvalidArgument))?;
|
||||
let password = iter.next().ok_or_else(|| io::Error::from(Errno::InvalidArgument))?;
|
||||
|
||||
if iter.next().is_some() {
|
||||
return Err(());
|
||||
return Err(io::Error::from(Errno::InvalidArgument));
|
||||
}
|
||||
|
||||
let mut res = Self {
|
||||
|
||||
@@ -44,14 +44,14 @@ pub fn stat(pathname: &str) -> Result<Stat, Error> {
|
||||
}
|
||||
|
||||
// TODO use BufRead instead once it's implemented
|
||||
pub(crate) fn read_line<'a, F: Read>(f: &mut F, buf: &'a mut [u8]) -> Result<Option<&'a str>, ()> {
|
||||
pub(crate) fn read_line<'a, F: Read>(f: &mut F, buf: &'a mut [u8]) -> Result<Option<&'a str>, Error> {
|
||||
let mut pos = 0;
|
||||
loop {
|
||||
if pos == buf.len() {
|
||||
return Err(());
|
||||
return Err(Error::from(Errno::OutOfMemory));
|
||||
}
|
||||
|
||||
let count = f.read(&mut buf[pos..=pos]).map_err(|_| ())?;
|
||||
let count = f.read(&mut buf[pos..=pos])?;
|
||||
if count == 0 {
|
||||
if pos == 0 {
|
||||
return Ok(None);
|
||||
@@ -64,5 +64,5 @@ pub(crate) fn read_line<'a, F: Read>(f: &mut F, buf: &'a mut [u8]) -> Result<Opt
|
||||
|
||||
pos += 1;
|
||||
}
|
||||
core::str::from_utf8(&buf[..pos]).map_err(|_| ()).map(Some)
|
||||
core::str::from_utf8(&buf[..pos]).map_err(|_| Error::from(Errno::InvalidArgument)).map(Some)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#![feature(asm, alloc_error_handler)]
|
||||
#![feature(alloc_error_handler)]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
|
||||
@@ -8,6 +8,7 @@ use libsys::{
|
||||
calls::{sys_ex_clone, sys_ex_gettid, sys_ex_signal, sys_ex_thread_exit, sys_ex_thread_wait},
|
||||
proc::{ExitCode, Tid},
|
||||
};
|
||||
use core::arch::asm;
|
||||
|
||||
struct NativeData<F, T>
|
||||
where
|
||||
|
||||
@@ -9,6 +9,10 @@ edition = "2021"
|
||||
name = "init"
|
||||
path = "src/init/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "mkdir"
|
||||
path = "src/bin/mkdir.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "shell"
|
||||
path = "src/bin/shell.rs"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![feature(asm)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
@@ -9,6 +8,7 @@
|
||||
extern crate libusr;
|
||||
|
||||
use libusr::sys::{abi::SystemCall, stat::Stat};
|
||||
use core::arch::asm;
|
||||
|
||||
static mut STATE: u64 = 0;
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate libusr;
|
||||
|
||||
use libsys::{sys_mkdirat, stat::FileMode};
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let args = libusr::env::args();
|
||||
|
||||
if args.len() < 2 {
|
||||
eprintln!("Usage: {} DIR1 ...", args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
let mut status = 0;
|
||||
for &item in args.iter().skip(1) {
|
||||
if let Err(err) = sys_mkdirat(None, item, FileMode::default_dir(), 0) {
|
||||
eprintln!("{}: {:?}", item, err);
|
||||
status = -1;
|
||||
}
|
||||
}
|
||||
status
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
#![feature(asm)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate libusr;
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
use libusr::sys::{stat::MountOptions, sys_execve, sys_fork, sys_mount, sys_waitpid};
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -15,7 +15,6 @@ use libsys::{
|
||||
termios::{Termios, TermiosLflag},
|
||||
};
|
||||
use libusr::{env::{self, UserInfo, UserShadow}, io};
|
||||
use core::str::FromStr;
|
||||
|
||||
struct HiddenInput {
|
||||
fd: FileDescriptor,
|
||||
@@ -158,6 +157,6 @@ fn main() -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
login_as(username);
|
||||
login_as(username).ok();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user