32 Commits

Author SHA1 Message Date
alnyan b74cc88789 fix: proper O_CREAT 2022-03-24 16:56:48 +02:00
alnyan d50cc08f86 refactor: fix warnings, add docs 2022-03-23 18:36:56 +02:00
alnyan 38c9fa783d feature: x86_64 fuzzy revert 2022-03-23 17:41:50 +02:00
alnyan da9991cd55 refactor: split syscall modules 2022-03-23 12:26:51 +02:00
alnyan 994a6952c0 refactor: move user syscall impl to libusr 2022-03-23 12:22:09 +02:00
alnyan 3e3f57c8b4 feature: revert user panic 2022-03-23 12:16:26 +02:00
alnyan 04a47a6ff9 feature: x86_64 copy-on-write 2022-03-23 11:01:36 +02:00
alnyan 200f18b425 feature: x86_64 user pf handling 2022-03-22 18:44:10 +02:00
alnyan a318bf0c2c feature: x86_64 all syscalls 2022-03-22 18:36:45 +02:00
alnyan 07b62026fa feature: x86_64 timer and sleep 2022-03-22 18:12:15 +02:00
alnyan f372ed65c7 feature: x86_64 signal entry 2022-03-22 17:07:27 +02:00
alnyan cf104ecf28 feature: x86_64 serial console 2022-03-22 15:37:33 +02:00
alnyan 6a1a6a8910 fix: incorrect forked CPL, waitpid 2022-03-22 12:18:31 +02:00
alnyan 3f4e6cd128 feat: x86_64 forking, fixed stack alignment issues 2022-03-21 23:00:45 +02:00
alnyan a887df2f07 Almost forking, UB 2022-03-21 19:35:13 +02:00
alnyan d129c460cc Better MSR/CR interface for x86_64 2022-03-21 15:24:15 +02:00
alnyan 67187e038d tss rsp0 for proper isr entry 2022-03-21 14:56:55 +02:00
alnyan 227678bdc5 Make x86_64 work (pre-syscalls) 2022-03-21 14:56:52 +02:00
alnyan df9e81e735 feature: framebuffer console WIP 2022-03-21 14:02:17 +02:00
alnyan 57908b189c feature: framebuffer console WIP 2022-03-21 14:02:15 +02:00
alnyan 898e465715 feature: initial support for x86_64 2022-03-21 12:03:28 +02:00
alnyan 4f5572b7c6 refactor: better write_paged/write_paged_bytes 2022-03-16 17:51:04 +02:00
alnyan 0d8e117e22 refactor: format sources 2022-03-15 20:04:37 +02:00
alnyan 865d358860 refactor: change trait from table to space 2022-03-15 20:04:21 +02:00
alnyan 5ea133d2bd refactor: detach aarch64 virt implementation 2022-03-15 12:40:33 +02:00
alnyan 4b844a8774 refactor: is_ready -> ready 2022-02-12 16:32:44 +02:00
alnyan a7a1639ff7 refactor: fix all warnings 2022-02-09 18:51:41 +02:00
alnyan 0f9f8fc1bf feat: mkdir(1) command 2022-02-09 16:57:33 +02:00
alnyan 97c591f58f fix: bcm2837 serial 2022-01-30 17:15:28 +02:00
alnyan d45a8adc34 refactor: move asm! usage to intrin module 2022-01-26 15:15:38 +02:00
alnyan 01da9e7ee5 refactor: new VFS design 2022-01-24 20:46:45 +02:00
alnyan a82751c146 refactor: update to rust 1.60.0 2022-01-23 20:58:38 +02:00
116 changed files with 4077 additions and 3155 deletions
Generated
+2 -2
View File
@@ -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",
]
+6
View File
@@ -50,6 +50,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 \
@@ -67,6 +68,9 @@ endif
ifeq ($(QEMU_PAUSE),1)
QEMU_OPTS+=-S
endif
ifeq ($(QEMU_KVM),1)
QEMU_OPTS+=-enable-kvm -cpu host
endif
.PHONY: address error etc kernel src
@@ -117,6 +121,8 @@ 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)/fuzzy $(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.
+2 -2
View File
@@ -1,8 +1,8 @@
use crate::Bpb;
use libsys::{
stat::{Stat, OpenFlags},
error::Errno,
ioctl::IoctlCmd,
error::Errno
stat::{OpenFlags, Stat},
};
use vfs::{VnodeImpl, VnodeKind, VnodeRef};
+1 -4
View File
@@ -12,10 +12,7 @@ extern crate alloc;
use alloc::{boxed::Box, rc::Rc};
use core::any::Any;
use core::cell::{Ref, RefCell};
use libsys::{
mem::read_le32,
error::Errno,
};
use libsys::{error::Errno, mem::read_le32};
use vfs::{BlockDevice, Filesystem, Vnode, VnodeKind, VnodeRef};
pub mod dir;
+161 -161
View File
@@ -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()
//}
+3
View File
@@ -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
+67 -12
View File
@@ -1,27 +1,41 @@
use crate::{BlockAllocator, Bvec, FileInode};
use alloc::boxed::Box;
use libsys::{error::Errno, stat::Stat};
use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
use core::cell::RefCell;
use libsys::{
error::Errno,
ioctl::IoctlCmd,
stat::{DirectoryEntry, OpenFlags, Stat},
};
use vfs::{Vnode, VnodeCommon, VnodeCreateKind, VnodeData, VnodeDirectory, VnodeRef};
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 +46,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 +66,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> {
+34 -16
View File
@@ -1,16 +1,17 @@
use crate::{BlockAllocator, Bvec};
use libsys::{
error::Errno,
ioctl::IoctlCmd,
stat::{OpenFlags, Stat},
};
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,7 +19,37 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
Ok(())
}
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
Ok(self.data.size())
}
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
let props = node.props();
Ok(Stat {
size: self.data.size() as u64,
blksize: 4096,
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)
}
@@ -30,19 +61,6 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
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())
}
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
let props = node.props();
Ok(Stat {
size: self.data.size() as u64,
blksize: 4096,
mode: props.mode
})
}
}
impl<'a, A: BlockAllocator + Copy + 'static> FileInode<'a, A> {
+32 -37
View File
@@ -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,10 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
mod tests {
use super::*;
use alloc::boxed::Box;
use libcommon::Read;
use libsys::{
stat::{GroupId, OpenFlags, UserId},
traits::Read,
};
use vfs::Ioctx;
#[test]
@@ -189,15 +184,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
View File
@@ -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
View File
@@ -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 -26
View File
@@ -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,14 +182,14 @@ 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)
} else {
todo!();
}
},
}
_ => todo!(),
}
}
@@ -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 crate::node::{VnodeCommon, VnodeFile};
use alloc::boxed::Box;
use alloc::rc::Rc;
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> {
@@ -231,7 +250,9 @@ mod tests {
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
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 +270,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];
+198 -199
View File
@@ -1,4 +1,4 @@
use crate::{FileRef, VnodeKind, VnodeRef};
use crate::{FileRef, VnodeCreateKind, VnodeRef};
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,
)
}
@@ -114,10 +115,10 @@ impl Ioctx {
opts: OpenFlags,
) -> Result<FileRef, Errno> {
let node = match self.find(at.clone(), path, true) {
Err(Errno::DoesNotExist) => {
Err(Errno::DoesNotExist) if opts.contains(OpenFlags::O_CREAT) => {
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()
// ));
// }
// }
+5 -6
View File
@@ -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,12 @@ pub use block::BlockDevice;
mod fs;
pub use fs::Filesystem;
mod node;
pub use node::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
pub use node::{
Vnode, VnodeCommon, VnodeCreateKind, VnodeData, VnodeDirectory, VnodeFile, VnodeRef,
};
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;
+322 -194
View File
@@ -1,7 +1,8 @@
use crate::{File, FileRef, Filesystem, Ioctx};
use crate::{CharDevice, File, FileRef, Filesystem, Ioctx};
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,123 @@ 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 +517,41 @@ 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 +601,80 @@ 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 +684,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 +713,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
View File
@@ -23,7 +23,7 @@ fs-macros = { path = "../fs/macros" }
multiboot2 = { git = "https://github.com/alnyan/multiboot2", branch = "expose-extra-traits-for-iters" }
[target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "6.x.x" }
cortex-a = { version = "7.0.x" }
fdt-rs = { version = "0.x.x", default-features = false }
[features]
-16
View File
@@ -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
+8 -9
View File
@@ -5,13 +5,14 @@ use crate::arch::{
machine,
};
use crate::config::{ConfigKey, CONFIG};
use crate::dev::pseudo;
use crate::dev::{
fdt::{find_prop, DeviceTree},
irq::IntSource,
Device,
};
use crate::fs::{devfs, sysfs};
use crate::dev::pseudo;
use core::arch::global_asm;
use libsys::error::Errno;
//use crate::debug::Level;
use crate::mem::{
@@ -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
@@ -101,11 +102,11 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
machine::init_board().unwrap();
#[cfg(feature = "verbose")]
if let Some(fdt) = fdt {
use crate::debug::Level;
fdt.dump(Level::Debug);
}
// #[cfg(feature = "verbose")]
// if let Some(fdt) = fdt {
// use crate::debug::Level;
// fdt.dump(Level::Debug);
// }
devfs::add_named_char_device(&pseudo::ZERO, "zero").unwrap();
devfs::add_named_char_device(&pseudo::RANDOM, "random").unwrap();
@@ -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"));
+111
View File
@@ -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
-95
View File
@@ -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
+24 -17
View File
@@ -5,6 +5,7 @@ use crate::mem::{
self,
phys::{self, PageUsage},
};
use core::arch::global_asm;
use core::mem::size_of;
struct Stack {
@@ -67,7 +68,7 @@ impl Context {
stack.push(frame.sp_el0 as usize);
// Setup common
stack.push(0); // tpidr_el0
stack.push(0); // tpidr_el0
stack.push(ttbr0);
stack.push(__aa64_ctx_enter_from_fork as usize); // x30/lr
stack.push(frame.x[29]); // x29
@@ -115,7 +116,7 @@ impl Context {
Self {
k_sp: stack.sp,
stack_base: stack.bp,
stack_page_count: 8
stack_page_count: 8,
}
}
@@ -124,7 +125,13 @@ impl Context {
/// # Safety
///
/// Unsafe: may clobber an already active context
pub unsafe fn setup_signal_entry(&mut self, entry: usize, arg: usize, ttbr0: usize, ustack: usize) {
pub unsafe fn setup_signal_entry(
&mut self,
entry: usize,
arg: usize,
ttbr0: usize,
ustack: usize,
) {
let mut stack = Stack::from_base_size(self.stack_base, self.stack_page_count);
stack.push(entry);
@@ -171,25 +178,25 @@ impl Stack {
pub unsafe fn from_base_size(bp: usize, page_count: usize) -> Stack {
Stack {
bp,
sp: bp + page_count * mem::PAGE_SIZE
sp: bp + page_count * mem::PAGE_SIZE,
}
}
pub fn setup_common(&mut self, entry: usize, ttbr: usize) {
self.push(0); // tpidr_el0
self.push(0); // tpidr_el0
self.push(ttbr);
self.push(entry); // x30/lr
self.push(0); // x29
self.push(0); // x28
self.push(0); // x27
self.push(0); // x26
self.push(0); // x25
self.push(0); // x24
self.push(0); // x23
self.push(0); // x22
self.push(0); // x21
self.push(0); // x20
self.push(0); // x19
self.push(entry); // x30/lr
self.push(0); // x29
self.push(0); // x28
self.push(0); // x27
self.push(0); // x26
self.push(0); // x25
self.push(0); // x24
self.push(0); // x23
self.push(0); // x22
self.push(0); // x21
self.push(0); // x20
self.push(0); // x19
}
pub fn push(&mut self, value: usize) {
+9 -6
View File
@@ -1,13 +1,14 @@
//! AArch64 exception handling
use crate::arch::machine;
use crate::arch::{intrin, machine};
use crate::debug::Level;
use crate::dev::irq::{IntController, IrqContext};
use crate::mem;
use crate::proc::{sched, Process, Thread};
use crate::mem::{self, virt::table::Space};
use crate::proc::{sched, Thread};
use crate::syscall;
use core::arch::global_asm;
use cortex_a::registers::{ESR_EL1, FAR_EL1};
use libsys::{abi::SystemCall, signal::Signal, error::Errno};
use libsys::{abi::SystemCall, error::Errno, signal::Signal};
use tock_registers::interfaces::Readable;
/// Trapped SIMD/FP functionality
@@ -58,7 +59,7 @@ const fn data_abort_access_size(iss: u64) -> &'static str {
#[no_mangle]
extern "C" fn __aa64_exc_irq_handler(_exc: &mut ExceptionFrame) {
unsafe {
let ic = IrqContext::new();
let ic = IrqContext::new(0);
machine::intc().handle_pending_irqs(&ic);
}
}
@@ -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(())
});
+33
View File
@@ -0,0 +1,33 @@
//! 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) {
asm!("tlbi vaae1, {}", in(reg) addr);
}
/// 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,3 +1,4 @@
use crate::arch::intrin;
use crate::dev::Device;
use crate::mem::virt::DeviceMemoryIo;
use crate::sync::IrqSafeSpinLock;
@@ -72,7 +73,7 @@ impl RWdog {
regs.CTRL.write(CTRL::KEY::Value + CTRL::RESTART::SET);
loop {
asm!("wfe");
intrin::hang();
}
}
+2
View File
@@ -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()?;
}
+5 -1
View File
@@ -6,9 +6,13 @@ 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;
pub mod virt;
pub use exception::ExceptionFrame as ForkFrame;
cfg_if! {
if #[cfg(feature = "mach_qemu")] {
@@ -34,7 +38,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;
-57
View File
@@ -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;
+65 -4
View File
@@ -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) []
]);
-422
View File
@@ -1,422 +0,0 @@
//
// #[no_mangle]
// static mut KERNEL_TTBR1: FixedTableGroup = FixedTableGroup::empty();
/// Transparent wrapper structure representing a single
/// translation table entry
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Entry(u64);
/// Structure describing a single level of translation mappings
#[repr(C, align(0x1000))]
pub struct Table {
entries: [Entry; 512],
}
/// Wrapper for top-most level of address translation tables
#[repr(transparent)]
pub struct Space(Table);
bitflags! {
/// Attributes attached to each translation [Entry]
pub struct MapAttributes: u64 {
// TODO use 2 lower bits to determine mapping size?
/// nG bit -- determines whether a TLB entry associated with this mapping
/// applies only to current ASID or all ASIDs.
const NOT_GLOBAL = 1 << 11;
/// AF bit -- must be set by software, otherwise Access Error exception is
/// generated when the page is accessed
const ACCESS = 1 << 10;
/// The memory region is outer-shareable
const SH_OUTER = 2 << 8;
/// This page is used for device-MMIO mapping and uses MAIR attribute #1
const DEVICE = 1 << 2;
/// Pages marked with this bit are Copy-on-Write
const EX_COW = 1 << 55;
/// UXN bit -- if set, page may not be used for instruction fetching from EL0
const UXN = 1 << 54;
/// PXN bit -- if set, page may not be used for instruction fetching from EL1
const PXN = 1 << 53;
// AP field
// Default behavior is: read-write for EL1, no access for EL0
/// If set, the page referred to by this entry is read-only for both EL0/EL1
const AP_BOTH_READONLY = 3 << 6;
/// If set, the page referred to by this entry is read-write for both EL0/EL1
const AP_BOTH_READWRITE = 1 << 6;
}
}
impl Table {
/// Returns next-level translation table reference for `index`, if one is present.
/// If `index` represents a `Block`-type mapping, will return an error.
/// If `index` does not map to any translation table, will try to allocate, init and
/// map a new one, returning it after doing so.
pub fn next_level_table_or_alloc(&mut self, index: usize) -> Result<&'static mut Table, Errno> {
let entry = self[index];
if entry.is_present() {
if !entry.is_table() {
return Err(Errno::InvalidArgument);
}
Ok(unsafe { &mut *(mem::virtualize(entry.address_unchecked()) as *mut _) })
} else {
let phys = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
self[index] = Entry::table(phys, MapAttributes::empty());
res.entries.fill(Entry::invalid());
Ok(res)
}
}
/// Returns next-level translation table reference for `index`, if one is present.
/// Same as [next_level_table_or_alloc], but returns `None` if no table is mapped.
pub fn next_level_table(&mut self, index: usize) -> Option<&'static mut Table> {
let entry = self[index];
if entry.is_present() {
if !entry.is_table() {
panic!("Entry is not a table: idx={}", index);
}
Some(unsafe { &mut *(mem::virtualize(entry.address_unchecked()) as *mut _) })
} else {
None
}
}
/// Constructs and fills a [Table] with non-present mappings
pub const fn empty() -> Table {
Table {
entries: [Entry::invalid(); 512],
}
}
}
impl Index<usize> for Table {
type Output = Entry;
fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}
impl IndexMut<usize> for Table {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.entries[index]
}
}
impl Entry {
const PRESENT: u64 = 1 << 0;
const TABLE: u64 = 1 << 1;
const PHYS_MASK: u64 = 0x0000FFFFFFFFF000;
/// Constructs a single non-present mapping
pub const fn invalid() -> Self {
Self(0)
}
/// Constructs a `Block`-type memory mapping
pub const fn block(phys: usize, attrs: MapAttributes) -> Self {
Self((phys as u64 & Self::PHYS_MASK) | attrs.bits() | Self::PRESENT)
}
/// Constructs a `Table` or `Page`-type mapping depending on translation level
/// this entry is used at
pub const fn table(phys: usize, attrs: MapAttributes) -> Self {
Self((phys as u64 & Self::PHYS_MASK) | attrs.bits() | Self::PRESENT | Self::TABLE)
}
/// Returns `true` if this entry is not invalid
pub const fn is_present(self) -> bool {
self.0 & Self::PRESENT != 0
}
/// Returns `true` if this entry is a `Table` or `Page`-type mapping
pub const fn is_table(self) -> bool {
self.0 & Self::TABLE != 0
}
/// Returns the target address of this translation entry.
///
/// # Safety
///
/// Does not check if the entry is actually valid.
pub const unsafe fn address_unchecked(self) -> usize {
(self.0 & Self::PHYS_MASK) as usize
}
unsafe fn set_address(&mut self, address: usize) {
self.0 &= !Self::PHYS_MASK;
self.0 |= (address as u64) & Self::PHYS_MASK;
}
unsafe fn fork_flags(self) -> MapAttributes {
MapAttributes::from_bits_unchecked(self.0 & !Self::PHYS_MASK)
}
fn set_cow(&mut self) {
self.0 |= (MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW).bits();
}
fn clear_cow(&mut self) {
self.0 &= !(MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW).bits();
self.0 |= MapAttributes::AP_BOTH_READWRITE.bits();
}
#[inline]
fn is_cow(self) -> bool {
let attrs = (MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW).bits();
self.0 & attrs == attrs
}
}
impl Space {
/// Creates a new virtual address space and fills it with [Entry::invalid()]
/// mappings. Does physical memory page allocation.
pub fn alloc_empty() -> Result<&'static mut Self, Errno> {
let phys = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
res.0.entries.fill(Entry::invalid());
Ok(res)
}
/// Inserts a single `virt` -> `phys` translation entry to this address space.
///
/// TODO: only works with 4K-sized pages at this moment.
pub fn map(&mut self, virt: usize, phys: usize, flags: MapAttributes) -> Result<(), Errno> {
let l0i = virt >> 30;
let l1i = (virt >> 21) & 0x1FF;
let l2i = (virt >> 12) & 0x1FF;
let l1_table = self.0.next_level_table_or_alloc(l0i)?;
let l2_table = l1_table.next_level_table_or_alloc(l1i)?;
if l2_table[l2i].is_present() {
Err(Errno::AlreadyExists)
} else {
l2_table[l2i] = Entry::table(phys, flags | MapAttributes::ACCESS);
#[cfg(feature = "verbose")]
debugln!("{:#p} Map {:#x} -> {:#x}, {:?}", self, virt, phys, flags);
Ok(())
}
}
/// Translates a virtual address into a corresponding physical one.
///
/// Only works for 4K pages atm.
// TODO extract attributes
pub fn translate(&mut self, virt: usize) -> Result<usize, Errno> {
let l0i = virt >> 30;
let l1i = (virt >> 21) & 0x1FF;
let l2i = (virt >> 12) & 0x1FF;
let l1_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l2i];
if entry.is_present() {
Ok(unsafe { entry.address_unchecked() })
} else {
Err(Errno::DoesNotExist)
}
}
/// Attempts to resolve a page fault at `virt` address by copying the
/// underlying Copy-on-Write mapping (if any is present)
pub fn try_cow_copy(&mut self, virt: usize) -> Result<(), Errno> {
let virt = virt & !0xFFF;
let l0i = virt >> 30;
let l1i = (virt >> 21) & 0x1FF;
let l2i = (virt >> 12) & 0x1FF;
let l1_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l2i];
if !entry.is_present() {
warnln!("Entry is not present: {:#x}", virt);
return Err(Errno::DoesNotExist);
}
let src_phys = unsafe { entry.address_unchecked() };
if !entry.is_cow() {
warnln!(
"Entry is not marked as CoW: {:#x}, points to {:#x}",
virt,
src_phys
);
return Err(Errno::DoesNotExist);
}
let dst_phys = unsafe { phys::copy_cow_page(src_phys)? };
unsafe {
l2_table[l2i].set_address(dst_phys);
}
l2_table[l2i].clear_cow();
Ok(())
}
/// Allocates a contiguous region from the address space and maps
/// physical pages to it
pub fn allocate(
&mut self,
start: usize,
end: usize,
len: usize,
flags: MapAttributes,
usage: PageUsage,
) -> Result<usize, Errno> {
'l0: for page in (start..end).step_by(0x1000) {
for i in 0..len {
if self.translate(page + i * 0x1000).is_ok() {
continue 'l0;
}
}
for i in 0..len {
let phys = phys::alloc_page(usage).unwrap();
self.map(page + i * 0x1000, phys, flags).unwrap();
}
return Ok(page);
}
Err(Errno::OutOfMemory)
}
/// Removes a single 4K page mapping from the table and
/// releases the underlying physical memory
pub fn unmap_single(&mut self, page: usize) -> Result<(), Errno> {
let l0i = page >> 30;
let l1i = (page >> 21) & 0x1FF;
let l2i = (page >> 12) & 0x1FF;
let l1_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l2i];
if !entry.is_present() {
return Err(Errno::DoesNotExist);
}
let phys = unsafe { entry.address_unchecked() };
unsafe {
phys::free_page(phys)?;
}
l2_table[l2i] = Entry::invalid();
unsafe {
asm!("tlbi vaae1, {}", in(reg) page);
}
// TODO release paging structure memory
Ok(())
}
/// Releases a range of virtual pages and their corresponding physical pages
pub fn free(&mut self, start: usize, len: usize) -> Result<(), Errno> {
for i in 0..len {
self.unmap_single(start + i * 0x1000)?;
}
Ok(())
}
/// Performs a copy of the address space, cloning data owned by it
pub fn fork(&mut self) -> Result<&'static mut Self, Errno> {
let res = Self::alloc_empty()?;
for l0i in 0..512 {
if let Some(l1_table) = self.0.next_level_table(l0i) {
for l1i in 0..512 {
if let Some(l2_table) = l1_table.next_level_table(l1i) {
for l2i in 0..512 {
let entry = l2_table[l2i];
if !entry.is_present() {
continue;
}
assert!(entry.is_table());
let src_phys = unsafe { entry.address_unchecked() };
let virt_addr = (l0i << 30) | (l1i << 21) | (l2i << 12);
let dst_phys = unsafe { phys::fork_page(src_phys)? };
let mut flags = unsafe { entry.fork_flags() };
if dst_phys != src_phys {
todo!();
// res.map(virt_addr, dst_phys, flags)?;
} else {
let writable = flags & MapAttributes::AP_BOTH_READONLY
== MapAttributes::AP_BOTH_READWRITE;
if writable {
flags |=
MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW;
l2_table[l2i].set_cow();
unsafe {
asm!("tlbi vaae1, {}", in(reg) virt_addr);
}
}
res.map(virt_addr, dst_phys, flags)?;
}
}
}
}
}
}
Ok(res)
}
/// Releases all the mappings from the address space. Frees all
/// memory pages referenced by this space as well as those used for
/// its paging tables.
///
/// # Safety
///
/// Unsafe: may invalidate currently active address space
pub unsafe fn release(space: &mut Self) {
for l0i in 0..512 {
let l0_entry = space.0[l0i];
if !l0_entry.is_present() {
continue;
}
assert!(l0_entry.is_table());
let l1_table = &mut *(mem::virtualize(l0_entry.address_unchecked()) as *mut Table);
for l1i in 0..512 {
let l1_entry = l1_table[l1i];
if !l1_entry.is_present() {
continue;
}
assert!(l1_entry.is_table());
let l2_table = &mut *(mem::virtualize(l1_entry.address_unchecked()) as *mut Table);
for l2i in 0..512 {
let entry = l2_table[l2i];
if !entry.is_present() {
continue;
}
assert!(entry.is_table());
phys::free_page(entry.address_unchecked()).unwrap();
}
phys::free_page(l1_entry.address_unchecked()).unwrap();
}
phys::free_page(l0_entry.address_unchecked()).unwrap();
}
memset(space as *mut Space as *mut u8, 0, 4096);
}
/// Returns the physical address of this structure
pub fn address_phys(&mut self) -> usize {
(self as *mut _ as usize) - mem::KERNEL_OFFSET
}
}
+2 -2
View File
@@ -1,13 +1,13 @@
//! ARM generic timer implementation
use crate::arch::machine::{self, IrqNumber};
use crate::proc;
use crate::dev::{
pseudo,
irq::{IntController, IntSource},
pseudo,
timer::TimestampSource,
Device,
};
use crate::proc;
use core::time::Duration;
use cortex_a::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0};
use libsys::error::Errno;
@@ -1,21 +1,16 @@
//! Fixed-size table group for device MMIO mappings
use crate::mem::{
self,
virt::{Entry, MapAttributes, Table},
};
use super::{table::TableImpl, EntryImpl};
use crate::mem;
use crate::mem::virt::table::{Entry, MapAttributes};
use cortex_a::asm::barrier::{self, dsb, isb};
use libsys::error::Errno;
const DEVICE_MAP_OFFSET: usize = mem::KERNEL_OFFSET + (256usize << 30);
/// Fixed-layout group of tables describing device MMIO and kernel identity
/// mappings
#[repr(C, align(0x1000))]
pub struct FixedTableGroup {
l0: Table,
l1: Table,
l2: Table,
l0: TableImpl,
l1: TableImpl,
l2: TableImpl,
pages_4k: usize,
pages_2m: usize,
@@ -27,9 +22,9 @@ impl FixedTableGroup {
/// entries
pub const fn empty() -> Self {
Self {
l0: Table::empty(),
l1: Table::empty(),
l2: Table::empty(),
l0: TableImpl::empty(),
l1: TableImpl::empty(),
l2: TableImpl::empty(),
pages_4k: 0,
pages_2m: 1,
@@ -44,7 +39,7 @@ impl FixedTableGroup {
pub fn map_region(&mut self, phys: usize, count: usize) -> Result<usize, Errno> {
// TODO generalize region allocation
let phys_page = phys & !0xFFF;
let attrs = MapAttributes::SH_OUTER | MapAttributes::DEVICE | MapAttributes::ACCESS;
let attrs = MapAttributes::SHARE_OUTER | MapAttributes::DEVICE_MEMORY;
match count {
262144 => {
@@ -54,7 +49,7 @@ impl FixedTableGroup {
}
self.pages_1g += 1;
self.l0[count + 256] = Entry::block(phys_page, attrs);
self.l0[count + 256] = EntryImpl::block(phys_page, attrs | MapAttributes::ACCESS);
unsafe {
dsb(barrier::SY);
isb(barrier::SY);
@@ -69,7 +64,7 @@ impl FixedTableGroup {
}
self.pages_2m += 1;
self.l1[count] = Entry::block(phys_page, attrs);
self.l1[count] = EntryImpl::block(phys_page, attrs | MapAttributes::ACCESS);
unsafe {
dsb(barrier::SY);
isb(barrier::SY);
@@ -84,7 +79,7 @@ impl FixedTableGroup {
}
self.pages_4k += 1;
self.l2[count] = Entry::table(phys_page, attrs);
self.l2[count] = EntryImpl::normal(phys_page, attrs | MapAttributes::ACCESS);
unsafe {
dsb(barrier::SY);
isb(barrier::SY);
@@ -95,13 +90,28 @@ impl FixedTableGroup {
_ => unimplemented!(),
}
}
/// Sets up initial mappings for 4K, 2M and 1G device memory page translation
pub fn init_device_map(&mut self) {
let l1_phys = (&self.l1 as *const _) as usize - mem::KERNEL_OFFSET;
let l2_phys = (&self.l2 as *const _) as usize - mem::KERNEL_OFFSET;
self.l0[256] = Entry::table(l1_phys, MapAttributes::empty());
self.l1[0] = Entry::table(l2_phys, MapAttributes::empty());
}
}
const DEVICE_MAP_OFFSET: usize = mem::KERNEL_OFFSET + (256usize << 30);
#[no_mangle]
static mut KERNEL_TTBR1: FixedTableGroup = FixedTableGroup::empty();
/// Allocates a range of virtual memory of requested size and maps
/// it to specified device memory
pub fn map_device_memory(phys: usize, count: usize) -> Result<usize, Errno> {
unsafe { KERNEL_TTBR1.map_region(phys, count) }
}
/// Sets up initial mappings for device-memory virtual tables.
///
/// # Safety
///
/// Only safe to be called once during virtual memory init.
pub unsafe fn init_device_map() {
let l1_phys = (&KERNEL_TTBR1.l1 as *const _) as usize - mem::KERNEL_OFFSET;
let l2_phys = (&KERNEL_TTBR1.l2 as *const _) as usize - mem::KERNEL_OFFSET;
KERNEL_TTBR1.l0[256] = Entry::normal(l1_phys, MapAttributes::empty());
KERNEL_TTBR1.l1[0] = Entry::normal(l2_phys, MapAttributes::empty());
}
+102
View File
@@ -0,0 +1,102 @@
//! AArch64 virtual memory management implementation
use crate::mem::virt::table::MapAttributes;
use cortex_a::{
asm::barrier::{self, isb, dsb},
registers::TTBR0_EL1
};
use tock_registers::interfaces::Writeable;
mod fixed;
mod table;
pub use fixed::{init_device_map, map_device_memory};
pub use table::{EntryImpl, SpaceImpl};
bitflags! {
/// Raw attributes for AArch64 [Entry] implementation
pub struct RawAttributesImpl: u64 {
// TODO use 2 lower bits to determine mapping size?
/// nG bit -- determines whether a TLB entry associated with this mapping
/// applies only to current ASID or all ASIDs.
const NOT_GLOBAL = 1 << 11;
/// AF bit -- must be set by software, otherwise Access Error exception is
/// generated when the page is accessed
const ACCESS = 1 << 10;
/// The memory region is outer-shareable
const SH_OUTER = 2 << 8;
/// This page is used for device-MMIO mapping and uses MAIR attribute #1
const DEVICE = 1 << 2;
/// Pages marked with this bit are Copy-on-Write
const EX_COW = 1 << 55;
/// UXN bit -- if set, page may not be used for instruction fetching from EL0
const UXN = 1 << 54;
/// PXN bit -- if set, page may not be used for instruction fetching from EL1
const PXN = 1 << 53;
// AP field
// Default behavior is: read-write for EL1, no access for EL0
/// If set, the page referred to by this entry is read-only for both EL0/EL1
const AP_BOTH_READONLY = 3 << 6;
/// If set, the page referred to by this entry is read-write for both EL0/EL1
const AP_BOTH_READWRITE = 1 << 6;
}
}
impl From<MapAttributes> for RawAttributesImpl {
fn from(src: MapAttributes) -> Self {
let mut res = RawAttributesImpl::empty();
if src.contains(MapAttributes::SHARE_OUTER) {
res |= RawAttributesImpl::SH_OUTER;
}
if !src.contains(MapAttributes::GLOBAL) {
res |= RawAttributesImpl::NOT_GLOBAL;
}
if !src.contains(MapAttributes::USER_EXEC) {
res |= RawAttributesImpl::UXN;
}
if !src.contains(MapAttributes::KERNEL_EXEC) {
res |= RawAttributesImpl::PXN;
}
if src.contains(MapAttributes::USER_READ) {
if src.contains(MapAttributes::USER_WRITE) {
res |= RawAttributesImpl::AP_BOTH_READWRITE;
} else {
res |= RawAttributesImpl::AP_BOTH_READONLY;
}
}
if src.contains(MapAttributes::DEVICE_MEMORY) {
res |= RawAttributesImpl::DEVICE;
}
if src.contains(MapAttributes::ACCESS) {
res |= RawAttributesImpl::ACCESS;
}
res
}
}
/// Performs initialization of virtual memory control by kernel
///
/// # Safety
///
/// Only safe to be called once during virtual memory init.
pub unsafe fn enable() {
fixed::init_device_map();
dsb(barrier::ISH);
isb(barrier::SY);
// Disable lower-half translation
TTBR0_EL1.set(0);
//TCR_EL1.modify(TCR_EL1::EPD0::SET);
}
+300
View File
@@ -0,0 +1,300 @@
use crate::arch::aarch64::intrin::flush_tlb_virt;
use crate::mem::{
self,
phys::{self, PageUsage},
virt::table::{Entry, MapAttributes, Space},
};
use core::ops::{Index, IndexMut};
use libsys::{error::Errno, mem::memset};
use super::RawAttributesImpl;
/// Transparent wrapper structure representing a single
/// translation table entry
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct EntryImpl(u64);
/// Structure describing a single level of translation mappings
#[repr(C, align(0x1000))]
pub struct TableImpl {
entries: [EntryImpl; 512],
}
/// Top-level translation table wrapper
#[repr(transparent)]
pub struct SpaceImpl(TableImpl);
impl EntryImpl {
const PRESENT: u64 = 1 << 0;
const TABLE: u64 = 1 << 1;
const PHYS_MASK: u64 = 0x0000FFFFFFFFF000;
}
impl Entry for EntryImpl {
type RawAttributes = RawAttributesImpl;
const EMPTY: Self = Self(0);
#[inline]
fn normal(addr: usize, attrs: MapAttributes) -> Self {
Self((addr as u64) | RawAttributesImpl::from(attrs).bits() | (1 << 1) | (1 << 0))
}
#[inline]
fn block(addr: usize, attrs: MapAttributes) -> Self {
Self((addr as u64) | RawAttributesImpl::from(attrs).bits() | (1 << 0))
}
#[inline]
fn address(self) -> usize {
(self.0 & Self::PHYS_MASK) as usize
}
#[inline]
fn set_address(&mut self, virt: usize) {
self.0 = (self.0 & !Self::PHYS_MASK) | ((virt as u64) & Self::PHYS_MASK);
}
#[inline]
fn is_present(self) -> bool {
self.0 & Self::PRESENT != 0
}
#[inline]
fn is_normal(self) -> bool {
self.0 & Self::TABLE != 0
}
#[inline]
fn fork_with_cow(&mut self) -> Self {
self.0 |= (RawAttributesImpl::AP_BOTH_READONLY | RawAttributesImpl::EX_COW).bits();
*self
}
#[inline]
fn copy_from_cow(self, new_addr: usize) -> Self {
let attrs = self.0
& !(Self::PHYS_MASK
| RawAttributesImpl::AP_BOTH_READONLY.bits()
| RawAttributesImpl::EX_COW.bits());
Self(
((new_addr as u64) & Self::PHYS_MASK)
| (attrs | RawAttributesImpl::AP_BOTH_READWRITE.bits()),
)
}
#[inline]
fn is_cow(self) -> bool {
self.0 & RawAttributesImpl::EX_COW.bits() != 0
}
#[inline]
fn is_user_writable(self) -> bool {
self.0 & RawAttributesImpl::AP_BOTH_READONLY.bits()
== RawAttributesImpl::AP_BOTH_READWRITE.bits()
}
}
impl Space for SpaceImpl {
type Entry = EntryImpl;
fn alloc_empty() -> Result<&'static mut Self, Errno> {
let phys = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
res.0.entries.fill(EntryImpl::EMPTY);
Ok(res)
}
unsafe fn release(space: &'static mut Self) {
for l0i in 0..512 {
let l0_entry = space.0[l0i];
if !l0_entry.is_present() {
continue;
}
assert!(l0_entry.is_normal());
let l1_table = &mut *(mem::virtualize(l0_entry.address()) as *mut TableImpl);
for l1i in 0..512 {
let l1_entry = l1_table[l1i];
if !l1_entry.is_present() {
continue;
}
assert!(l1_entry.is_normal());
let l2_table = &mut *(mem::virtualize(l1_entry.address()) as *mut TableImpl);
for l2i in 0..512 {
let entry = l2_table[l2i];
if !entry.is_present() {
continue;
}
assert!(entry.is_normal());
phys::free_page(entry.address()).unwrap();
}
phys::free_page(l1_entry.address()).unwrap();
}
phys::free_page(l0_entry.address()).unwrap();
}
memset(space as *mut Self as *mut u8, 0, 4096);
}
fn fork(&mut self) -> Result<&'static mut Self, Errno> {
let res = Self::alloc_empty()?;
for l0i in 0..512 {
if let Some(l1_table) = self.0.next_level_table_mut(l0i) {
for l1i in 0..512 {
if let Some(l2_table) = l1_table.next_level_table_mut(l1i) {
for l2i in 0..512 {
let entry = &mut l2_table[l2i];
if !entry.is_present() {
continue;
}
assert!(entry.is_normal());
let src_phys = entry.address();
let virt_addr = (l0i << 30) | (l1i << 21) | (l2i << 12);
let dst_phys = unsafe { phys::fork_page(src_phys)? };
let new_entry = if dst_phys != src_phys {
todo!()
} else if entry.is_user_writable() {
entry.fork_with_cow()
} else {
*entry
};
unsafe {
flush_tlb_virt(virt_addr);
res.write_last_level(virt_addr, new_entry, true, false)?;
}
}
}
}
}
}
Ok(res)
}
unsafe fn write_last_level(
&mut self,
virt: usize,
entry: Self::Entry,
_create_intermediate: bool, // TODO handle this properly
overwrite: bool,
) -> Result<(), Errno> {
let l0i = virt >> 30;
let l1i = (virt >> 21) & 0x1FF;
let l2i = (virt >> 12) & 0x1FF;
let l1_table = self.0.next_level_table_or_alloc(l0i)?;
let l2_table = l1_table.next_level_table_or_alloc(l1i)?;
if l2_table[l2i].is_present() && !overwrite {
return Err(Errno::AlreadyExists);
};
l2_table[l2i] = entry;
#[cfg(feature = "verbose")]
debugln!(
"{:#p} Map {:#x} -> {:#x}, {:#x}",
self,
virt,
entry.address(),
entry.0 & !EntryImpl::PHYS_MASK
);
flush_tlb_virt(virt);
Ok(())
}
fn read_last_level(&self, virt: usize) -> Result<Self::Entry, Errno> {
let l0i = virt >> 30;
let l1i = (virt >> 21) & 0x1FF;
let l2i = (virt >> 12) & 0x1FF;
let l1_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l2i];
if entry.is_present() {
Ok(entry)
} else {
Err(Errno::DoesNotExist)
}
}
}
impl TableImpl {
/// Constructs a table with no valid mappings
pub const fn empty() -> Self {
Self {
entries: [EntryImpl::EMPTY; 512],
}
}
/// Returns next-level translation table reference for `index`, if one is present.
/// If `index` represents a `Block`-type mapping, will return an error.
/// If `index` does not map to any translation table, will try to allocate, init and
/// map a new one, returning it after doing so.
pub fn next_level_table_or_alloc(&mut self, index: usize) -> Result<&'static mut Self, Errno> {
let entry = self[index];
if entry.is_present() {
if !entry.is_normal() {
return Err(Errno::InvalidArgument);
}
Ok(unsafe { &mut *(mem::virtualize(entry.address()) as *mut _) })
} else {
let phys = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
self[index] = EntryImpl::normal(phys, MapAttributes::empty());
res.entries.fill(EntryImpl::EMPTY);
Ok(res)
}
}
/// Returns next-level translation table reference for `index`, if one is present.
/// Same as [next_level_table_or_alloc], but returns `None` if no table is mapped.
pub fn next_level_table(&self, index: usize) -> Option<&'static Self> {
let entry = self[index];
if entry.is_present() {
if !entry.is_normal() {
panic!("Entry is not a table: idx={}", index);
}
Some(unsafe { &*(mem::virtualize(entry.address()) as *const _) })
} else {
None
}
}
/// Returns mutable next-level translation table reference for `index`,
/// if one is present. Same as [next_level_table_or_alloc], but returns
/// `None` if no table is mapped.
pub fn next_level_table_mut(&mut self, index: usize) -> Option<&'static mut Self> {
let entry = self[index];
if entry.is_present() {
if !entry.is_normal() {
panic!("Entry is not a table: idx={}", index);
}
Some(unsafe { &mut *(mem::virtualize(entry.address()) as *mut _) })
} else {
None
}
}
}
impl Index<usize> for TableImpl {
type Output = EntryImpl;
fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}
impl IndexMut<usize> for TableImpl {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.entries[index]
}
}
+2 -1
View File
@@ -14,12 +14,13 @@ cfg_if! {
pub mod aarch64;
pub use aarch64 as platform;
pub use aarch64::machine;
pub use aarch64::{machine, intrin};
} else if #[cfg(target_arch = "x86_64")] {
pub mod x86_64;
pub use x86_64 as platform;
pub use x86_64 as machine;
pub use x86_64::intrin;
}
}
+13 -5
View File
@@ -1,3 +1,4 @@
//! x86_64 common boot logic
use crate::arch::x86_64::{
self, gdt, idt, intc,
reg::{CR0, CR4},
@@ -5,24 +6,26 @@ use crate::arch::x86_64::{
};
use crate::config::{ConfigKey, CONFIG};
use crate::debug;
use crate::dev::{display::FramebufferInfo, pseudo, Device};
use crate::dev::{display::FramebufferInfo, irq::IntSource, pseudo, Device};
use crate::font;
use crate::fs::{devfs::{self, CharDeviceType}, sysfs};
use crate::fs::{
devfs::{self, CharDeviceType},
sysfs,
};
use crate::mem::{
self, heap,
phys::{self, MemoryRegion, PageUsage, ReservedRegion},
virt,
};
use crate::proc;
use core::arch::{asm, global_asm};
use core::arch::global_asm;
use core::mem::MaybeUninit;
use multiboot2::{BootInformation, MemoryArea};
use tock_registers::interfaces::ReadWriteable;
static mut RESERVED_REGION_MB2: MaybeUninit<ReservedRegion> = MaybeUninit::uninit();
#[no_mangle]
extern "C" fn __x86_64_bsp_main(mb_checksum: u32, mb_info_ptr: u32) -> ! {
extern "C" fn __x86_64_bsp_main(_mb_checksum: u32, mb_info_ptr: u32) -> ! {
CR4.modify(CR4::OSXMMEXCPT::SET + CR4::OSFXSR::SET);
CR0.modify(CR0::EM::CLEAR + CR0::MP::SET);
@@ -93,6 +96,11 @@ extern "C" fn __x86_64_bsp_main(mb_checksum: u32, mb_info_ptr: u32) -> ! {
phys_base: fb_info.address as usize,
virt_base: virt,
});
unsafe {
x86_64::COM1.init_irqs().unwrap();
x86_64::local_timer().init_irqs().unwrap();
x86_64::local_timer().enable().unwrap();
}
font::init();
debug::set_display(&x86_64::DISPLAY);
syscall::init();
+1 -1
View File
@@ -76,7 +76,7 @@ __x86_64_enter_upper:
mov %ax, %ss
.code64
mov $KERNEL_OFFSET, %rax
movabsq $KERNEL_OFFSET, %rax
add %rax, %rbx
jmp *%rbx
+45 -8
View File
@@ -10,6 +10,8 @@ __x86_64_ctx_enter_user:
pop %rdi
pop %rdx
push %rdx
push $0x1B
push %rcx
@@ -23,6 +25,10 @@ __x86_64_ctx_enter_user:
__x86_64_ctx_enter_kernel:
pop %rdi
pop %rdx
// 8-byte fixup for proper stack alignment
push %rdx
mov %rsp, %rcx
push $0x10
@@ -36,12 +42,42 @@ __x86_64_ctx_enter_kernel:
iretq
__x86_64_ctx_enter_from_fork:
jmp .
pop %rdi
pop %rsi
pop %rdx
pop %r10
pop %r8
pop %r9
pop %rax
xor %rax, %rax
pop %r11 // rsp3
pop %rcx // rip3
push $0x1B
push %r11
push $0x200
push $0x23
push %rcx
iretq
__x86_64_ctx_switch:
// %rsi -- src ctx ptr
// %rdi -- dst ctx ptr
// push %tss_rsp0
// TODO save gs_base
mov (4 + TSS)(%rip), %rax
push %rax
// push %cr3
mov %cr3, %rax
push %rax
push %r15
push %r14
push %r13
@@ -49,16 +85,14 @@ __x86_64_ctx_switch:
push %rbx
push %rbp
mov %cr3, %rax
push %rax
// TODO save gs_base
mov (4 + TSS)(%rip), %rax
push %rax
// TODO SAVE FP CONTEXT
mov %rsp, (%rsi)
__x86_64_ctx_switch_to:
mov (%rdi), %rsp
// TODO RESTORE FP CONTEXT
pop %rbp
pop %rbx
pop %r12
@@ -66,13 +100,16 @@ __x86_64_ctx_switch_to:
pop %r14
pop %r15
// pop %cr3
pop %rax
test %rax, %rax
jz 1f
mov %rax, %cr3
1:
pop %rax
mov %rax, (4 + TSS)(%rip)
// TODO set gs_base = rax
// pop %tss_rsp0
pop %rax
mov %rax, (4 + TSS)(%rip)
ret
+68 -13
View File
@@ -1,9 +1,11 @@
//! Thread context
use crate::arch::platform::ForkFrame;
use crate::mem::{
self,
phys::{self, PageUsage},
};
use core::mem::size_of;
use core::arch::global_asm;
use core::mem::size_of;
struct Stack {
bp: usize,
@@ -65,7 +67,7 @@ impl Context {
Self {
k_sp: stack.sp,
stack_base: stack.bp,
stack_page_count: 8
stack_page_count: 8,
}
}
@@ -74,8 +76,61 @@ impl Context {
/// # Safety
///
/// Unsafe: may clobber an already active context
pub unsafe fn setup_signal_entry(&mut self, entry: usize, arg: usize, cr3: usize, ustack: usize) {
todo!()
pub unsafe fn setup_signal_entry(
&mut self,
entry: usize,
arg: usize,
cr3: usize,
ustack: usize,
) {
let cr3 = cr3 & 0xFFFFFFFF;
let mut stack = Stack::from_base_size(self.stack_base, self.stack_page_count);
let stack_top = stack.sp;
stack.push(entry);
stack.push(arg);
stack.push(0);
stack.push(ustack);
stack.setup_common(__x86_64_ctx_enter_user as usize, cr3, stack_top);
self.k_sp = stack.sp;
}
/// Clones a process context from given `frame`
pub fn fork(frame: &ForkFrame, cr3: usize) -> Self {
let cr3 = cr3 & 0xFFFFFFFF;
let mut stack = Stack::new(8);
let stack_top = stack.sp;
stack.push(frame.saved_rip);
stack.push(frame.saved_rsp);
stack.push(frame.x[6]); // rax
stack.push(frame.x[5]); // r9
stack.push(frame.x[4]); // r8
stack.push(frame.x[3]); // r10
stack.push(frame.x[2]); // rdx
stack.push(frame.x[1]); // rsi
stack.push(frame.x[0]); // rdi
// Setup common
stack.push(__x86_64_ctx_enter_from_fork as usize); // return address
stack.push(stack_top); // gs_base
stack.push(cr3);
stack.push(frame.x[9]); // r15
stack.push(frame.x[10]); // r14
stack.push(frame.x[11]); // r13
stack.push(frame.x[12]); // r12
stack.push(frame.x[7]); // rbx
stack.push(frame.x[8]); // rbp
Self {
k_sp: stack.sp,
stack_base: stack.bp,
stack_page_count: 8,
}
}
/// Performs initial thread entry
@@ -112,20 +167,20 @@ impl Stack {
pub unsafe fn from_base_size(bp: usize, page_count: usize) -> Stack {
Stack {
bp,
sp: bp + page_count * mem::PAGE_SIZE
sp: bp + page_count * mem::PAGE_SIZE,
}
}
pub fn setup_common(&mut self, entry: usize, cr3: usize, tss_rsp0: usize) {
self.push(entry); // return address
self.push(tss_rsp0); // gs_base
self.push(entry); // return address
self.push(tss_rsp0); // gs_base
self.push(cr3);
self.push(0); // r15
self.push(0); // r14
self.push(0); // r13
self.push(0); // r12
self.push(0); // rbx
self.push(0); // rbp
self.push(0); // r15
self.push(0); // r14
self.push(0); // r13
self.push(0); // r12
self.push(0); // rbx
self.push(0); // rbp
}
pub fn push(&mut self, value: usize) {
+36 -5
View File
@@ -1,8 +1,12 @@
use crate::arch::x86_64;
use crate::arch::{intrin, x86_64};
use crate::debug::Level;
use crate::dev::irq::{IntController, IrqContext};
use core::arch::{asm, global_asm};
use crate::mem::{self, virt::table::Space};
use crate::proc::{sched, Thread};
use core::arch::asm;
use libsys::{error::Errno, signal::Signal};
#[allow(dead_code)]
#[derive(Debug)]
struct ExceptionFrame {
r15: u64,
@@ -48,7 +52,7 @@ fn pfault_access_type(code: u64) -> &'static str {
}
}
fn pfault_dump(level: Level, frame: &ExceptionFrame, cr2: u64) {
fn pfault_dump(level: Level, frame: &ExceptionFrame, cr2: usize) {
println!(level, "\x1B[41;1mPage fault:");
println!(
level,
@@ -61,8 +65,35 @@ fn pfault_dump(level: Level, frame: &ExceptionFrame, cr2: u64) {
#[no_mangle]
extern "C" fn __x86_64_exception_handler(frame: &mut ExceptionFrame) {
if frame.err_no == 14 {
// TODO userspace page faults
let cr2 = pfault_read_cr2();
let cr2 = pfault_read_cr2() as usize;
let is_write = frame.err_code & (1 << 1) != 0;
if is_write && cr2 < mem::KERNEL_OFFSET && sched::is_ready() {
let thread = Thread::current();
let proc = thread.owner().unwrap();
let res = proc.manipulate_space(|space| {
space.try_cow_copy(cr2 & !0xFFF)?;
unsafe {
intrin::flush_tlb_virt(cr2 & !0xFFF);
}
Result::<(), Errno>::Ok(())
});
if res.is_err() {
errorln!(
"Page fault at {:#x} in user {:?}",
frame.rip,
thread.owner_id()
);
pfault_dump(Level::Error, frame, cr2);
proc.enter_fault_signal(thread, Signal::SegmentationFault);
}
return;
}
errorln!("Unresolved page fault:");
pfault_dump(Level::Error, frame, cr2);
}
+13 -7
View File
@@ -1,6 +1,7 @@
use core::mem::size_of_val;
use core::arch::asm;
use core::mem::size_of_val;
#[allow(dead_code)]
#[repr(packed)]
struct Entry {
limit_lo: u16,
@@ -11,6 +12,7 @@ struct Entry {
base_hi: u8,
}
#[allow(dead_code)]
#[repr(packed)]
struct Tss {
__res0: u32,
@@ -30,6 +32,7 @@ struct Tss {
iopb_base: u16,
}
#[allow(dead_code)]
#[repr(packed)]
struct Pointer {
size: u16,
@@ -123,6 +126,14 @@ static mut GDT: [Entry; SIZE] = [
Entry::null(),
];
#[inline]
unsafe fn load_gdt(ptr: &Pointer, tr: u16) {
asm!(r#"
lgdt ({})
ltr %ax
"#, in(reg) ptr, in("ax") tr, options(att_syntax));
}
pub unsafe fn init() {
let tss_addr = &TSS as *const _ as usize;
@@ -138,10 +149,5 @@ pub unsafe fn init() {
size: size_of_val(&GDT) as u16 - 1,
offset: &GDT as *const _ as usize,
};
asm!(r#"
lgdt ({})
mov $0x28, %ax
ltr %ax
"#, in(reg) &gdtr, options(att_syntax));
load_gdt(&gdtr, 0x28);
}
+18 -4
View File
@@ -35,10 +35,24 @@ __x86_64_isr_common:
mov %rsp, %rdi
call __x86_64_exception_handler
1:
cli
hlt
jmp 1b
pop %r15
pop %r14
pop %r13
pop %r12
pop %r11
pop %r10
pop %r9
pop %r8
pop %rdi
pop %rsi
pop %rbp
pop %rbx
pop %rdx
pop %rcx
pop %rax
add $16, %rsp
iretq
isr_nerr 0
isr_nerr 1
+9 -2
View File
@@ -1,6 +1,7 @@
use core::arch::{asm, global_asm};
use core::mem::size_of_val;
#[allow(dead_code)]
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct Entry {
@@ -13,6 +14,7 @@ pub struct Entry {
__res1: u32,
}
#[allow(dead_code)]
#[repr(packed)]
struct Pointer {
limit: u16,
@@ -52,7 +54,12 @@ impl Entry {
static mut IDT: [Entry; SIZE] = [Entry::empty(); SIZE];
pub unsafe fn init<F: FnOnce(&mut [Entry; SIZE]) -> ()>(f: F) {
#[inline]
unsafe fn load_idt(ptr: &Pointer) {
asm!("lidt ({})", in(reg) ptr, options(att_syntax));
}
pub unsafe fn init<F: FnOnce(&mut [Entry; SIZE])>(f: F) {
extern "C" {
static __x86_64_exception_vectors: [usize; 32];
}
@@ -67,7 +74,7 @@ pub unsafe fn init<F: FnOnce(&mut [Entry; SIZE]) -> ()>(f: F) {
limit: size_of_val(&IDT) as u16 - 1,
offset: &IDT as *const _ as usize,
};
asm!("lidt ({})", in(reg) &idtr, options(att_syntax));
load_idt(&idtr);
}
global_asm!(include_str!("idt.S"), options(att_syntax));
+7 -8
View File
@@ -7,8 +7,8 @@ use crate::dev::{
Device,
};
use crate::sync::IrqSafeSpinLock;
use libsys::error::Errno;
use core::arch::global_asm;
use libsys::error::Errno;
const ICW1_INIT: u8 = 0x10;
const ICW1_ICW4: u8 = 0x01;
@@ -24,13 +24,16 @@ pub(super) struct I8259 {
table: IrqSafeSpinLock<[Option<&'static (dyn IntSource + Sync)>; 15]>,
}
/// Interrupt line number wrapper struct
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct IrqNumber(u32);
impl IrqNumber {
/// IRQ line number limit
pub const MAX: u32 = 16;
/// Constructs a wrapped IRQ line number
pub const fn new(u: u32) -> Self {
if u > Self::MAX {
panic!();
@@ -70,11 +73,7 @@ impl IntController for I8259 {
irq: Self::IrqNumber,
handler: &'static (dyn IntSource + Sync),
) -> Result<(), Errno> {
if irq.0 == 0 {
return Err(Errno::InvalidArgument);
}
let index = (irq.0 - 1) as usize;
let index = irq.0 as usize;
let mut lock = self.table.lock();
if lock[index].is_some() {
return Err(Errno::AlreadyExists);
@@ -98,8 +97,8 @@ impl IntController for I8259 {
fn handle_pending_irqs<'irq_context>(&'irq_context self, ic: &IrqContext<'irq_context>) {
let irq_number = ic.token();
assert!(irq_number > 0);
// Clear irq
if irq_number > 8 {
self.cmd_b.write(0x20);
}
@@ -107,7 +106,7 @@ impl IntController for I8259 {
{
let table = self.table.lock();
match table[irq_number - 1] {
match table[irq_number] {
None => panic!("No handler registered for irq{}", irq_number),
Some(handler) => {
drop(table);
+40
View File
@@ -1,5 +1,40 @@
//! x86_64-specific assembly functions
use core::arch::asm;
/// Disables delievery of IRQs
///
/// # Safety
///
/// Unsafe: requires ring 0
#[inline(always)]
pub unsafe fn irq_disable() {
asm!("cli");
}
/// Discards an entry related to `addr` from TLB cache
///
/// # Safety
///
/// Unsafe: requires ring 0
#[inline(always)]
pub unsafe fn flush_tlb_virt(addr: usize) {
asm!("invlpg ({})", in(reg) addr, options(att_syntax));
}
/// Discards all entries related to `asid` from TLB cache
///
/// # Safety
///
/// Only safe to use for known [Process]es and their ASIDs
// TODO actually implement this on x86-64
#[inline(always)]
pub unsafe fn flush_tlb_asid(_asid: usize) {}
/// Read a value from a model-specific register
///
/// # Safety
///
/// Unsafe: arbitrary MSR reads, may cause CPU exceptions
#[inline(always)]
pub unsafe fn rdmsr(a: u32) -> u64 {
let mut eax: u32;
@@ -8,6 +43,11 @@ pub unsafe fn rdmsr(a: u32) -> u64 {
(eax as u64) | ((edx as u64) << 32)
}
/// Writes a value to a model-specific register
///
/// # Safety
///
/// Unsafe: arbitrary MSR writes
#[inline(always)]
pub unsafe fn wrmsr(a: u32, b: u64) {
let eax = b as u32;
+6 -3
View File
@@ -1,14 +1,17 @@
use core::marker::PhantomData;
use core::arch::asm;
use core::marker::PhantomData;
pub struct PortIo<T> {
port: u16,
_pd: PhantomData<T>
_pd: PhantomData<T>,
}
impl<T> PortIo<T> {
pub const unsafe fn new(port: u16) -> Self {
Self { port, _pd: PhantomData }
Self {
port,
_pd: PhantomData,
}
}
}
+50 -35
View File
@@ -24,41 +24,6 @@ __x86_64_irq_\no:
mov %rsp, %rdi
call __x86_64_irq_handler
jmp .
.endm
.section .text
__x86_64_irq_0:
cli
push %rax
push %rcx
push %rdx
push %rbx
push %rbp
push %rsi
push %rdi
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
mov $0x3F8, %dx
mov $'T', %al
outb %al, %dx
mov $0x20, %al
mov $0x20, %dx
outb %al, %dx
call sched_yield
pop %r15
pop %r14
pop %r13
@@ -76,8 +41,58 @@ __x86_64_irq_0:
pop %rcx
pop %rax
add $16, %rsp
iretq
.endm
.section .text
// __x86_64_irq_0:
// cli
//
// push %rax
// push %rcx
// push %rdx
// push %rbx
// push %rbp
// push %rsi
// push %rdi
//
// push %r8
// push %r9
// push %r10
// push %r11
// push %r12
// push %r13
// push %r14
// push %r15
//
// mov $0x20, %al
// mov $0x20, %dx
// outb %al, %dx
//
// call sched_yield
//
// pop %r15
// pop %r14
// pop %r13
// pop %r12
// pop %r11
// pop %r10
// pop %r9
// pop %r8
//
// pop %rdi
// pop %rsi
// pop %rbp
// pop %rbx
// pop %rdx
// pop %rcx
// pop %rax
//
// iretq
irq_entry 0
irq_entry 1
irq_entry 2
irq_entry 3
+19 -7
View File
@@ -1,23 +1,27 @@
use crate::dev::{serial::SerialDevice, display::StaticFramebuffer, irq::IntController};
//! x86_64 platform implementation details
use crate::dev::{display::StaticFramebuffer, irq::IntController, serial::SerialDevice};
use core::arch::asm;
mod uart;
use uart::Uart;
mod intc;
use intc::I8259;
pub use intc::IrqNumber;
mod timer;
use timer::Timer;
mod io;
pub(self) use io::PortIo;
pub mod boot;
pub mod table;
pub mod context;
pub(self) mod exception;
pub(self) mod gdt;
pub(self) mod idt;
pub mod intrin;
pub mod reg;
pub(self) mod syscall;
pub(self) mod gdt;
pub(self) mod idt;
pub(self) mod exception;
pub mod virt;
pub use syscall::SyscallFrame as ForkFrame;
@@ -46,14 +50,22 @@ pub unsafe fn irq_restore(state: u64) {
}
}
/// Returns a reference to interrupt controller device
pub fn intc() -> &'static impl IntController {
&INTC
}
/// Returns a reference to primary console device
pub fn console() -> &'static impl SerialDevice {
&COM1
}
static COM1: Uart = unsafe { Uart::new(0x3F8) };
static INTC: I8259 = I8259::new();
/// Returns a reference to CPU-local timer device
pub fn local_timer() -> &'static Timer {
&TIMER
}
static COM1: Uart = unsafe { Uart::new(0x3F8, IrqNumber::new(4)) };
pub(self) static INTC: I8259 = I8259::new();
pub(self) static DISPLAY: StaticFramebuffer = StaticFramebuffer::uninit();
pub(self) static TIMER: Timer = Timer::new(IrqNumber::new(0));
+17
View File
@@ -1,10 +1,14 @@
//! x86_64 model-specific and control register interfaces
macro_rules! wrap_msr {
($struct_name:ident, $name:ident, $address:expr, $fields:tt) => {
register_bitfields! {
u64,
#[allow(missing_docs)]
pub $name $fields
}
#[allow(missing_docs)]
pub struct $struct_name;
impl Readable for $struct_name {
@@ -31,6 +35,7 @@ macro_rules! wrap_msr {
}
}
#[allow(missing_docs)]
pub const $name: $struct_name = $struct_name;
}
}
@@ -45,21 +50,31 @@ use crate::arch::x86_64::intrin::{rdmsr, wrmsr};
// CRn registers
register_bitfields! {
u64,
/// Control register CR4 fields
#[allow(missing_docs)]
pub CR4 [
/// Indicates OS support for FXSR/FXRSTOR instructions
OSFXSR OFFSET(9) NUMBITS(1) [],
/// Indicates OS support for unmasked SIMD exceptions
OSXMMEXCPT OFFSET(10) NUMBITS(1) []
]
}
register_bitfields! {
u64,
/// Control register CR0 fields
#[allow(missing_docs)]
pub CR0 [
/// Indicates requirement for x87 emulation
EM OFFSET(2) NUMBITS(1) [],
/// Controls x87 exception handling
MP OFFSET(1) NUMBITS(1) []
]
}
#[allow(missing_docs)]
pub struct Cr4;
#[allow(missing_docs)]
pub struct Cr0;
impl Readable for Cr4 {
@@ -114,7 +129,9 @@ impl Writeable for Cr0 {
}
}
/// Control register CR4
pub const CR4: Cr4 = Cr4;
/// Control register CR0
pub const CR0: Cr0 = Cr0;
wrap_msr!(MsrIa32Efer, MSR_IA32_EFER, 0xC0000080, [
+11 -5
View File
@@ -4,13 +4,19 @@ use tock_registers::interfaces::{ReadWriteable, Writeable};
use libsys::abi::SystemCall;
use crate::syscall;
/// Syscall registers
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct SyscallFrame {
x: [usize; 13],
/// General-purpose registers
pub x: [usize; 13],
saved_rsp: usize,
saved_rflags: usize,
saved_rip: usize,
/// Caller (ring 3) saved stack pointer
pub saved_rsp: usize,
/// Caller (ring 3) saved processor flags
pub saved_rflags: usize,
/// Caller (ring 3) return address
pub saved_rip: usize,
}
pub(super) fn init() {
@@ -19,7 +25,7 @@ pub(super) fn init() {
}
MSR_IA32_SFMASK.write(MSR_IA32_SFMASK::IF::SET);
MSR_IA32_LSTAR.set(__x86_64_syscall_entry as u64);
MSR_IA32_LSTAR.set(__x86_64_syscall_entry as usize as u64);
MSR_IA32_STAR
.write(MSR_IA32_STAR::SYSRET_CS_SS.val(0x1B - 8) + MSR_IA32_STAR::SYSCALL_CS_SS.val(0x08));
MSR_IA32_EFER.modify(MSR_IA32_EFER::SCE::SET);
-286
View File
@@ -1,286 +0,0 @@
use crate::mem::{
self,
virt::{AddressSpace, table::{MapAttributes, Entry as AbstractEntry}},
phys::{self, PageUsage}
};
use core::ops::{Index, IndexMut};
use libsys::error::Errno;
use core::arch::asm;
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Entry(u64);
#[derive(Clone, Copy)]
#[repr(C, align(0x1000))]
pub struct Table {
entries: [Entry; 512]
}
#[repr(C, align(0x1000))]
pub struct FixedTableGroup {
pml4: Table,
pdpt: Table,
pd: [Table; 16]
}
#[repr(transparent)]
pub struct Space(Table);
bitflags! {
/// Attributes attached to each translation [Entry]
pub struct RawAttributes: u64 {
const PRESENT = 1 << 0;
const WRITE = 1 << 1;
const USER = 1 << 2;
const BLOCK = 1 << 7;
const GLOBAL = 1 << 8;
}
}
// Upper mappings
#[no_mangle]
static mut KERNEL_FIXED: FixedTableGroup = FixedTableGroup {
pml4: Table::empty(),
pdpt: Table::empty(),
pd: [Table::empty(); 16]
};
impl TryFrom<MapAttributes> for RawAttributes {
type Error = Errno;
fn try_from(i: MapAttributes) -> Result<Self, Errno> {
let mut res = RawAttributes::empty();
if i.contains(MapAttributes::USER_READ) {
res |= RawAttributes::USER;
}
if i.contains(MapAttributes::USER_WRITE) || i.contains(MapAttributes::KERNEL_WRITE) {
res |= RawAttributes::WRITE;
}
Ok(res)
}
}
impl AbstractEntry for Entry {
fn from_parts(phys: usize, attrs: MapAttributes) -> Self {
let attrs = RawAttributes::try_from(attrs).unwrap();
Self((phys as u64) | attrs.bits() | 1)
}
fn is_present(self) -> bool {
self.0 & (1 << 0) != 0
}
fn is_table(self) -> bool {
self.0 & (1 << 7) == 0
}
fn target(self) -> usize {
(self.0 & !0xFFF) as usize
}
}
impl Entry {
const fn invalid() -> Self {
Self(0)
}
}
impl Table {
const fn empty() -> Self {
Self {
entries: [Entry::invalid(); 512]
}
}
/// Returns next-level translation table reference for `index`, if one is present.
/// If `index` represents a `Block`-type mapping, will return an error.
/// If `index` does not map to any translation table, will try to allocate, init and
/// map a new one, returning it after doing so.
pub fn next_level_table_or_alloc(&mut self, index: usize) -> Result<&'static mut Table, Errno> {
let entry = self[index];
if entry.is_present() {
if !entry.is_table() {
return Err(Errno::InvalidArgument);
}
Ok(unsafe { &mut *(mem::virtualize(entry.target()) as *mut _) })
} else {
let phys = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
self[index] = Entry::from_parts(phys, MapAttributes::USER_WRITE | MapAttributes::USER_READ | MapAttributes::NOT_GLOBAL);
res.entries.fill(Entry::invalid());
Ok(res)
}
}
/// Returns next-level translation table reference for `index`, if one is present.
/// Same as [next_level_table_or_alloc], but returns `None` if no table is mapped.
pub fn next_level_table(&mut self, index: usize) -> Option<&'static mut Table> {
let entry = self[index];
if entry.is_present() {
if !entry.is_table() {
panic!("Entry is not a table: idx={}", index);
}
Some(unsafe { &mut *(mem::virtualize(entry.target()) as *mut _) })
} else {
None
}
}
}
impl Space {
const fn empty() -> Self {
Self(Table::empty())
}
}
impl AddressSpace for Space {
type Entry = Entry;
fn alloc_empty() -> Result<&'static mut Self, Errno> {
let pdpt_phys = unsafe {
&KERNEL_FIXED.pdpt as *const _ as usize - mem::KERNEL_OFFSET
};
let page = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(page) as *mut Self) };
res.0.entries[..511].fill(Entry::invalid());
res.0.entries[511] = Entry::from_parts(pdpt_phys, MapAttributes::SHARE_OUTER | MapAttributes::KERNEL_EXEC | MapAttributes::KERNEL_WRITE | MapAttributes::NOT_GLOBAL);
Ok(res)
}
fn release(space: &mut Self) {
todo!()
}
fn address_phys(&mut self) -> usize {
todo!();
}
fn read_last_level_entry(&mut self, virt: usize) -> Result<Entry, Errno> {
let l0i = virt >> 39;
let l1i = (virt >> 30) & 0x1FF;
let l2i = (virt >> 21) & 0x1FF;
let l3i = (virt >> 12) & 0x1FF;
let l0_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l1_table = l0_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l2i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l3i];
if entry.is_present() {
Ok(entry)
} else {
Err(Errno::DoesNotExist)
}
}
fn write_last_level_entry(
&mut self,
virt: usize,
entry: Entry,
map_intermediate: bool,
) -> Result<(), Errno> {
let l0i = virt >> 39;
let l1i = (virt >> 30) & 0x1FF;
let l2i = (virt >> 21) & 0x1FF;
let l3i = (virt >> 12) & 0x1FF;
let l0_table = self.0.next_level_table_or_alloc(l0i)?;
let l1_table = l0_table.next_level_table_or_alloc(l1i)?;
let l2_table = l1_table.next_level_table_or_alloc(l2i)?;
if l2_table[l3i].is_present() {
warnln!("Entry already exists for address: virt={:#x}, prev={:#x}, new={:#x}", virt, l2_table[l3i].target(), entry.target());
Err(Errno::AlreadyExists)
} else {
l2_table[l3i] = entry;
unsafe {
core::arch::asm!("invlpg ({})", in(reg) virt, options(att_syntax));
}
#[cfg(feature = "verbose")]
debugln!("{:#p} Map {:#x} -> {:#x}", self, virt, entry.target());
Ok(())
}
}
/// Performs a copy of the address space, cloning data owned by it
fn fork(&mut self) -> Result<&'static mut Self, Errno> {
let res = Self::alloc_empty()?;
let pdpt0 = self.0.next_level_table(0).unwrap();
for pdpti in 0..512 {
if let Some(pd) = pdpt0.next_level_table(pdpti) {
for pdi in 0..512 {
if let Some(pt) = pd.next_level_table(pdi) {
for pti in 0..512 {
let entry = pt[pti];
if !entry.is_present() {
continue;
}
assert!(entry.is_table());
todo!();
// let src_phys = unsafe { entry.address_unchecked() };
// let virt_addr = (l0i << 30) | (l1i << 21) | (l2i << 12);
// let dst_phys = unsafe { phys::fork_page(src_phys)? };
// let mut flags = unsafe { entry.fork_flags() };
// if dst_phys != src_phys {
// todo!();
// // res.map(virt_addr, dst_phys, flags)?;
// } else {
// let writable = flags & MapAttributes::AP_BOTH_READONLY
// == MapAttributes::AP_BOTH_READWRITE;
// if writable {
// flags |=
// MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW;
// l2_table[l2i].set_cow();
// unsafe {
// asm!("tlbi vaae1, {}", in(reg) virt_addr);
// }
// }
// res.map(virt_addr, dst_phys, flags)?;
// }
}
}
}
}
}
Ok(res)
}
}
impl Index<usize> for Table {
type Output = Entry;
fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}
impl IndexMut<usize> for Table {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.entries[index]
}
}
pub fn enable() -> Result<(), Errno> {
unsafe {
// Remove the lower mapping
KERNEL_FIXED.pml4.entries[0] = Entry::invalid();
// Flush the TLB by reloading cr3
asm!("mov %cr3, %rax; mov %rax, %cr3", options(att_syntax));
}
Ok(())
}
+69
View File
@@ -0,0 +1,69 @@
//! i.... timer implementation
use crate::arch::machine::{self, IrqNumber, PortIo};
use crate::dev::{
irq::{IntController, IntSource},
pseudo,
timer::TimestampSource,
Device,
};
use crate::proc;
use core::sync::atomic::{AtomicU64, Ordering};
use core::time::Duration;
use libsys::error::Errno;
// 1.1931816666 MHz base freq
/// Generic timer struct
pub struct Timer {
data0: PortIo<u8>,
counter: AtomicU64,
irq: IrqNumber,
}
impl Device for Timer {
fn name(&self) -> &'static str {
"Intel Timer"
}
unsafe fn enable(&self) -> Result<(), Errno> {
const DIV: u16 = (1193182u32 / 1000) as u16;
self.data0.write((DIV & 0xFF) as u8);
self.data0.write((DIV >> 8) as u8);
Ok(())
}
}
impl TimestampSource for Timer {
fn timestamp(&self) -> Result<Duration, Errno> {
Ok(Duration::from_millis(self.counter.load(Ordering::Relaxed)))
}
}
impl IntSource for Timer {
fn handle_irq(&self) -> Result<(), Errno> {
let value = self.counter.fetch_add(1, Ordering::Relaxed);
proc::wait::tick();
pseudo::RANDOM.set_state(value as u32);
proc::switch();
Ok(())
}
fn init_irqs(&'static self) -> Result<(), Errno> {
machine::INTC.register_handler(self.irq, self)?;
machine::INTC.enable_irq(self.irq)?;
Ok(())
}
}
impl Timer {
/// Constructs a new instance of ARM Generic Timer
pub const fn new(irq: IrqNumber) -> Self {
Self {
data0: unsafe { PortIo::new(0x40) },
counter: AtomicU64::new(0),
irq,
}
}
}
+30 -6
View File
@@ -1,16 +1,19 @@
use crate::arch::x86_64::PortIo;
use libsys::error::Errno;
use crate::arch::x86_64::{self, IrqNumber, PortIo};
use crate::dev::{
tty::{CharRing, TtyDevice},
irq::{IntController, IntSource},
serial::SerialDevice,
tty::{CharRing, TtyDevice},
Device,
};
use libsys::error::Errno;
#[derive(TtyCharDevice)]
pub(super) struct Uart {
dr: PortIo<u8>,
ring: CharRing<16>
ier: PortIo<u8>,
isr: PortIo<u8>,
ring: CharRing<16>,
irq: IrqNumber,
}
impl Device for Uart {
@@ -29,6 +32,24 @@ impl TtyDevice<16> for Uart {
}
}
impl IntSource for Uart {
fn handle_irq(&self) -> Result<(), Errno> {
if self.isr.read() != 0 {
self.recv_byte(self.dr.read());
}
Ok(())
}
fn init_irqs(&'static self) -> Result<(), Errno> {
// TODO shared IRQs between COM# ports
x86_64::INTC.register_handler(self.irq, self)?;
self.ier.write(1 << 0);
x86_64::INTC.enable_irq(self.irq)?;
Ok(())
}
}
impl SerialDevice for Uart {
fn send(&self, byte: u8) -> Result<(), Errno> {
self.dr.write(byte);
@@ -41,10 +62,13 @@ impl SerialDevice for Uart {
}
impl Uart {
pub const unsafe fn new(base: u16) -> Self {
pub const unsafe fn new(base: u16, irq: IrqNumber) -> Self {
Self {
dr: PortIo::new(base),
ring: CharRing::new()
ier: PortIo::new(base + 1),
isr: PortIo::new(base + 2),
ring: CharRing::new(),
irq,
}
}
}
+16
View File
@@ -0,0 +1,16 @@
use super::table::TableImpl;
#[repr(C, align(0x1000))]
pub struct FixedTableGroup {
pub pml4: TableImpl,
pub pdpt: TableImpl,
pub pd: [TableImpl; 16],
}
// Upper mappings
#[no_mangle]
pub(super) static mut KERNEL_FIXED: FixedTableGroup = FixedTableGroup {
pml4: TableImpl::empty(),
pdpt: TableImpl::empty(),
pd: [TableImpl::empty(); 16],
};
+61
View File
@@ -0,0 +1,61 @@
//! x86_64 virtual memory management implementation
use crate::mem::virt::table::{MapAttributes, Entry};
use core::arch::asm;
use libsys::error::Errno;
mod table;
mod fixed;
pub use table::{EntryImpl, SpaceImpl};
use fixed::KERNEL_FIXED;
bitflags! {
/// Raw attributes for x86_64 [Entry] implementation
pub struct RawAttributesImpl: u64 {
/// Entry is valid and mapped
const PRESENT = EntryImpl::PRESENT;
/// Entry is writable by user processes
const WRITE = EntryImpl::WRITE;
/// Entry is accessible (readable) by user processes
const USER = EntryImpl::USER;
/// Entry points to a block instead of a next-level table
const BLOCK = EntryImpl::BLOCK;
/// Entry is global across virtual address spaces
const GLOBAL = 1 << 8;
/// Entry is marked as Copy-on-Write
const EX_COW = EntryImpl::EX_COW;
}
}
impl From<MapAttributes> for RawAttributesImpl {
fn from(i: MapAttributes) -> Self {
let mut res = RawAttributesImpl::empty();
if i.contains(MapAttributes::USER_READ) {
res |= RawAttributesImpl::USER;
}
if i.contains(MapAttributes::USER_WRITE) {
res |= RawAttributesImpl::WRITE | RawAttributesImpl::USER;
}
res
}
}
/// Performs initialization of virtual memory control by kernel
///
/// # Safety
///
/// Only safe to be called once during virtual memory init.
pub unsafe fn enable() {
// Remove the lower mapping
KERNEL_FIXED.pml4[0] = EntryImpl::EMPTY;
// Flush the TLB by reloading cr3
asm!("mov %cr3, %rax; mov %rax, %cr3", options(att_syntax));
}
/// Allocates a range of virtual memory of requested size and maps
/// it to specified device memory
pub fn map_device_memory(_phys: usize, _count: usize) -> Result<usize, Errno> {
todo!()
}
+353
View File
@@ -0,0 +1,353 @@
use crate::arch::intrin;
use crate::mem::{
self,
phys::{self, PageUsage},
virt::table::{Entry, MapAttributes, Space},
};
use core::fmt;
use core::ops::{Index, IndexMut};
use libsys::error::Errno;
use super::{RawAttributesImpl, KERNEL_FIXED};
/// Transparent wrapper structure representing a single
/// translation table entry
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct EntryImpl(u64);
/// Structure describing a single level of translation mappings
#[derive(Clone, Copy)]
#[repr(C, align(0x1000))]
pub struct TableImpl {
entries: [EntryImpl; 512],
}
/// Top-level translation table wrapper
#[repr(transparent)]
pub struct SpaceImpl(TableImpl);
impl EntryImpl {
pub(super) const PRESENT: u64 = 1 << 0;
pub(super) const WRITE: u64 = 1 << 1;
pub(super) const USER: u64 = 1 << 2;
pub(super) const BLOCK: u64 = 1 << 7;
pub(super) const EX_COW: u64 = 1 << 62;
const PHYS_MASK: u64 = 0x0000FFFFFFFFF000;
}
impl fmt::Debug for EntryImpl {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EntryImpl")
.field("address", &self.address())
.field("flags", &unsafe {
RawAttributesImpl::from_bits_unchecked(self.0 & 0xFFF)
})
.finish_non_exhaustive()
}
}
impl Entry for EntryImpl {
type RawAttributes = RawAttributesImpl;
const EMPTY: Self = Self(0);
#[inline]
fn normal(addr: usize, attrs: MapAttributes) -> Self {
Self((addr as u64) | RawAttributesImpl::from(attrs).bits() | Self::PRESENT)
}
#[inline]
fn block(addr: usize, attrs: MapAttributes) -> Self {
Self((addr as u64) | RawAttributesImpl::from(attrs).bits() | Self::BLOCK | Self::PRESENT)
}
#[inline]
fn address(self) -> usize {
(self.0 & Self::PHYS_MASK) as usize
}
#[inline]
fn set_address(&mut self, virt: usize) {
self.0 &= !Self::PHYS_MASK;
self.0 |= (virt as u64) & Self::PHYS_MASK;
}
#[inline]
fn is_present(self) -> bool {
self.0 & Self::PRESENT != 0
}
#[inline]
fn is_normal(self) -> bool {
self.0 & Self::BLOCK == 0
}
#[inline]
fn fork_with_cow(&mut self) -> Self {
self.0 &= !Self::WRITE;
self.0 |= Self::EX_COW;
*self
}
#[inline]
fn copy_from_cow(self, new_addr: usize) -> Self {
let attrs = self.0 & !(Self::PHYS_MASK | Self::EX_COW);
Self(((new_addr as u64) & Self::PHYS_MASK) | (attrs | Self::WRITE))
}
#[inline]
fn is_cow(self) -> bool {
self.0 & Self::EX_COW != 0
}
#[inline]
fn is_user_writable(self) -> bool {
const BITS: u64 = EntryImpl::USER | EntryImpl::WRITE;
self.0 & BITS == BITS
}
}
impl Space for SpaceImpl {
type Entry = EntryImpl;
fn alloc_empty() -> Result<&'static mut Self, Errno> {
let kernel_pdpt_phys =
unsafe { &KERNEL_FIXED.pdpt as *const _ as usize - mem::KERNEL_OFFSET };
let page = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(page) as *mut Self) };
res.0.entries[..511].fill(EntryImpl::EMPTY);
res.0.entries[511] = EntryImpl::normal(
kernel_pdpt_phys,
MapAttributes::SHARE_OUTER
| MapAttributes::KERNEL_EXEC
| MapAttributes::KERNEL_WRITE
| MapAttributes::USER_READ
| MapAttributes::USER_WRITE,
);
Ok(res)
}
unsafe fn release(space: &'static mut Self) {
let pdpt0 = space.0.next_level_table_mut(0).unwrap();
for pdpti in 0..512 {
let pdpt_entry = pdpt0[pdpti];
if !pdpt_entry.is_present() {
continue;
}
assert!(pdpt_entry.is_normal());
let pd = &mut *(mem::virtualize(pdpt_entry.address()) as *mut TableImpl);
for pdi in 0..512 {
let pd_entry = pd[pdi];
if !pd_entry.is_present() {
continue;
}
assert!(pd_entry.is_normal());
let pt = &mut *(mem::virtualize(pd_entry.address()) as *mut TableImpl);
for pti in 0..512 {
let entry = pt[pti];
if !entry.is_present() {
continue;
}
assert!(entry.is_normal());
phys::free_page(entry.address()).unwrap();
}
phys::free_page(pd_entry.address()).unwrap();
}
phys::free_page(pdpt_entry.address()).unwrap();
}
phys::free_page(space.0[0].address()).unwrap();
}
fn fork(&mut self) -> Result<&'static mut Self, Errno> {
let res = Self::alloc_empty()?;
let pdpt0 = self.0.next_level_table_mut(0).unwrap();
for pdpti in 0..512 {
if let Some(pd) = pdpt0.next_level_table_mut(pdpti) {
for pdi in 0..512 {
if let Some(pt) = pd.next_level_table_mut(pdi) {
for pti in 0..512 {
let entry = &mut pt[pti];
let virt_addr = (pdpti << 30) | (pdi << 21) | (pti << 12);
if !entry.is_present() {
continue;
}
assert!(entry.is_normal());
let src_phys = entry.address();
// let dst_phys = phys::alloc_page(PageUsage::UserPrivate)?;
// unsafe {
// use libsys::mem::memcpy;
// memcpy(
// mem::virtualize(dst_phys) as *mut u8,
// mem::virtualize(src_phys) as *const u8,
// 4096
// );
// }
// debugln!("Clone page {:#x}", virt_addr);
// let new_entry = EntryImpl::normal(dst_phys, MapAttributes::USER_WRITE | MapAttributes::USER_READ);
// TODO check exact page usage
let dst_phys = unsafe { phys::fork_page(src_phys)? };
let new_entry = if dst_phys != src_phys {
todo!()
} else if entry.is_user_writable() {
entry.fork_with_cow()
} else {
*entry
};
unsafe {
res.write_last_level(virt_addr, new_entry, true, false)?;
intrin::flush_tlb_virt(virt_addr);
}
}
}
}
}
}
Ok(res)
}
unsafe fn write_last_level(
&mut self,
virt: usize,
entry: Self::Entry,
_create_intermediate: bool, // TODO handle this properly
overwrite: bool,
) -> Result<(), Errno> {
let l0i = virt >> 39;
let l1i = (virt >> 30) & 0x1FF;
let l2i = (virt >> 21) & 0x1FF;
let l3i = (virt >> 12) & 0x1FF;
let l0_table = self.0.next_level_table_or_alloc(l0i)?;
let l1_table = l0_table.next_level_table_or_alloc(l1i)?;
let l2_table = l1_table.next_level_table_or_alloc(l2i)?;
if l2_table[l3i].is_present() && !overwrite {
warnln!(
"Entry already exists for address: virt={:#x}, prev={:#x}, new={:#x}",
virt,
l2_table[l3i].address(),
entry.address()
);
Err(Errno::AlreadyExists)
} else {
l2_table[l3i] = entry;
intrin::flush_tlb_virt(virt);
Ok(())
}
}
fn read_last_level(&self, virt: usize) -> Result<Self::Entry, Errno> {
let l0i = virt >> 39;
let l1i = (virt >> 30) & 0x1FF;
let l2i = (virt >> 21) & 0x1FF;
let l3i = (virt >> 12) & 0x1FF;
let l0_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l1_table = l0_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l2i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l3i];
if entry.is_present() {
Ok(entry)
} else {
Err(Errno::DoesNotExist)
}
}
}
impl TableImpl {
/// Constructs a table with no valid mappings
pub const fn empty() -> Self {
Self {
entries: [EntryImpl::EMPTY; 512],
}
}
/// Returns next-level translation table reference for `index`, if one is present.
/// If `index` represents a `Block`-type mapping, will return an error.
/// If `index` does not map to any translation table, will try to allocate, init and
/// map a new one, returning it after doing so.
pub fn next_level_table_or_alloc(&mut self, index: usize) -> Result<&'static mut Self, Errno> {
let entry = self[index];
if entry.is_present() {
if !entry.is_normal() {
return Err(Errno::InvalidArgument);
}
Ok(unsafe { &mut *(mem::virtualize(entry.address()) as *mut _) })
} else {
let phys = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
self[index] = EntryImpl::normal(
phys,
MapAttributes::USER_WRITE
| MapAttributes::USER_READ
| MapAttributes::KERNEL_WRITE
| MapAttributes::USER_EXEC,
);
res.entries.fill(EntryImpl::EMPTY);
Ok(res)
}
}
/// Returns next-level translation table reference for `index`, if one is present.
/// Same as [next_level_table_or_alloc], but returns `None` if no table is mapped.
pub fn next_level_table(&self, index: usize) -> Option<&'static Self> {
let entry = self[index];
if entry.is_present() {
if !entry.is_normal() {
panic!("Entry is not a table: idx={}", index);
}
Some(unsafe { &*(mem::virtualize(entry.address()) as *const _) })
} else {
None
}
}
/// Returns mutable next-level translation table reference for `index`,
/// if one is present. Same as [next_level_table_or_alloc], but returns
/// `None` if no table is mapped.
pub fn next_level_table_mut(&mut self, index: usize) -> Option<&'static mut Self> {
let entry = self[index];
if entry.is_present() {
if !entry.is_normal() {
panic!("Entry is not a table: idx={}", index);
}
Some(unsafe { &mut *(mem::virtualize(entry.address()) as *mut _) })
} else {
None
}
}
}
impl Index<usize> for TableImpl {
type Output = EntryImpl;
fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}
impl IndexMut<usize> for TableImpl {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.entries[index]
}
}
+5 -1
View File
@@ -21,6 +21,7 @@ use core::convert::TryFrom;
use core::fmt;
use libsys::{debug::TraceLevel, error::Errno, mem::memcpy};
/// Currently active print level
pub static LEVEL: Level = Level::Debug;
static COLOR_MAP: [u32; 16] = [
0x000000, 0x0000AA, 0x00AA00, 0x00AAAA, 0xAA0000, 0xAA00AA, 0xAA5500, 0xAAAAAA, 0x555555,
@@ -63,13 +64,14 @@ impl fmt::Write for FramebufferOutput {
let fb = self.display.unwrap().framebuffer().unwrap();
for ch in s.chars() {
self.putc(&fb, ch);
self.putc(fb, ch);
}
Ok(())
}
}
/// Sets active display for debug console output
pub fn set_display(disp: &'static dyn Display) {
DISPLAY.lock().display = Some(disp);
}
@@ -235,6 +237,8 @@ impl FramebufferOutput {
}
pub fn putc(&mut self, fb: &FramebufferInfo, ch: char) {
#![allow(clippy::single_match)]
match self.esc {
EscapeState::None => {
match ch {
+16 -2
View File
@@ -1,24 +1,36 @@
//! Graphical display interfaces
use crate::dev::Device;
use libsys::error::Errno;
use crate::util::InitOnce;
/// Description of a framebuffer
pub struct FramebufferInfo {
/// Width in pixels
pub width: usize,
/// Height in pixels
pub height: usize,
/// Physical start address
pub phys_base: usize,
/// Virtual address where the framebuffer is mapped
pub virt_base: usize
}
/// Generic display interface
pub trait Display: Device {
/// Changes currently active display mode
fn set_mode(&self, mode: DisplayMode) -> Result<(), Errno>;
fn framebuffer<'a>(&'a self) -> Result<&'a FramebufferInfo, Errno>;
/// Returns currently active framebuffer information
fn framebuffer(&self) -> Result<&FramebufferInfo, Errno>;
}
/// Display configuration details
#[allow(dead_code)]
pub struct DisplayMode {
width: u16,
height: u16,
}
/// Generic single-mode framebuffer
pub struct StaticFramebuffer {
framebuffer: InitOnce<FramebufferInfo>
}
@@ -34,7 +46,7 @@ impl Device for StaticFramebuffer {
}
impl Display for StaticFramebuffer {
fn set_mode(&self, mode: DisplayMode) -> Result<(), Errno> {
fn set_mode(&self, _mode: DisplayMode) -> Result<(), Errno> {
Err(Errno::InvalidOperation)
}
@@ -48,10 +60,12 @@ impl Display for StaticFramebuffer {
}
impl StaticFramebuffer {
/// Constructs an empty [StaticFramebuffer] object
pub const fn uninit() -> Self {
Self { framebuffer: InitOnce::new() }
}
/// Initializes the device from existing framebuffer object
pub fn set_framebuffer(&self, framebuffer: FramebufferInfo) {
self.framebuffer.init(framebuffer);
}
+1
View File
@@ -50,6 +50,7 @@ impl<'q> IrqContext<'q> {
Self { token, _0: PhantomData }
}
/// Returns the value this object was initialized with
pub const fn token(&self) -> usize {
self.token
}
+7 -5
View File
@@ -3,13 +3,15 @@
use libsys::error::Errno;
// Device classes
// pub mod fdt;
// pub mod gpio;
#[cfg(target_arch = "aarch64")]
pub mod fdt;
pub mod gpio;
pub mod irq;
pub mod display;
// pub mod pci;
// pub mod rtc;
// pub mod sd;
pub mod pci;
pub mod rtc;
#[cfg(target_arch = "aarch64")]
pub mod sd;
pub mod serial;
pub mod timer;
pub mod pseudo;
+13 -16
View File
@@ -1,22 +1,14 @@
use crate::dev::{
serial::SerialDevice,
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 {
@@ -50,7 +42,6 @@ impl CharDevice for Random {
}
}
impl Device for Zero {
fn name(&self) -> &'static str {
"Zero device"
@@ -81,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;
@@ -95,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;
+13 -10
View File
@@ -1,16 +1,19 @@
//! Teletype (TTY) device facilities
use crate::dev::serial::SerialDevice;
use crate::proc::{Process, wait::{Wait, WAIT_SELECT}};
use crate::proc::{
wait::{Wait, WAIT_SELECT},
Process,
};
use crate::sync::IrqSafeSpinLock;
use crate::syscall::arg;
use core::mem::size_of;
use libsys::error::Errno;
use libsys::{
termios::{Termios, TermiosIflag, TermiosLflag, TermiosOflag},
ioctl::IoctlCmd,
proc::Pid,
signal::Signal,
ioctl::IoctlCmd
termios::{Termios, TermiosIflag, TermiosLflag, TermiosOflag},
};
use core::mem::size_of;
use crate::syscall::arg;
#[derive(Debug)]
struct CharRingInner<const N: usize> {
@@ -52,18 +55,18 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
let res = arg::struct_mut::<Termios>(ptr)?;
*res = self.ring().config.lock().clone();
Ok(size_of::<Termios>())
},
}
IoctlCmd::TtySetAttributes => {
let src = arg::struct_ref::<Termios>(ptr)?;
*self.ring().config.lock() = src.clone();
Ok(size_of::<Termios>())
},
}
IoctlCmd::TtySetPgrp => {
let src = arg::struct_ref::<u32>(ptr)?;
self.ring().inner.lock().fg_pgid = Some(Pid::try_from(*src)?);
Ok(0)
},
_ => Err(Errno::InvalidArgument)
}
_ => Err(Errno::InvalidArgument),
}
}
@@ -127,7 +130,7 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
let proc = Process::get(pgid);
if let Some(proc) = proc {
// TODO
// proc.set_signal(Signal::Interrupt);
proc.set_signal(Signal::Interrupt);
}
}
return;
+8 -2
View File
@@ -1,3 +1,5 @@
//! Text drawing routines and font support
use crate::util::InitOnce;
use libsys::mem::read_le32;
use crate::dev::display::FramebufferInfo;
@@ -5,6 +7,7 @@ use crate::dev::display::FramebufferInfo;
static FONT_DATA: &[u8] = include_bytes!("../../etc/default8x16.psfu");
static FONT: InitOnce<Font> = InitOnce::new();
/// Font data description structure
pub struct Font {
char_width: usize,
char_height: usize,
@@ -13,8 +16,9 @@ pub struct Font {
}
impl Font {
/// Renders a glyph onto the framebuffer
pub fn draw(&self, fb: &FramebufferInfo, bx: usize, by: usize, ch: char, fg: u32, bg: u32) {
if ch >= ' ' && ch < '\x7B' {
if (' ' .. '\x7B').contains(&ch) {
let char_data = &self.data[ch as usize * self.bytes_per_glyph..];
for iy in 0..self.char_height {
@@ -33,8 +37,9 @@ impl Font {
}
}
/// Sets up the global [Font] object from PSF
pub fn init() {
assert_eq!(read_le32(&FONT_DATA[..]), 0x864ab572);
assert_eq!(read_le32(FONT_DATA), 0x864ab572);
FONT.init(Font {
char_width: read_le32(&FONT_DATA[28..]) as usize,
@@ -44,6 +49,7 @@ pub fn init() {
});
}
/// Returns a reference to global [Font] object
pub fn get() -> &'static Font {
FONT.get()
}
+14 -6
View File
@@ -1,9 +1,10 @@
//! Device list pseudo-filesystem
use crate::util::InitOnce;
use alloc::boxed::Box;
use core::cell::RefCell;
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::CharDevice;
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);
+2 -2
View File
@@ -4,8 +4,8 @@ use crate::mem::{
phys::{self, PageUsage},
};
use libsys::{error::Errno, stat::MountOptions};
use vfs::VnodeRef;
use memfs::BlockAllocator;
use vfs::VnodeRef;
pub mod devfs;
pub mod sysfs;
@@ -36,6 +36,6 @@ pub fn create_filesystem(options: &MountOptions) -> Result<VnodeRef, Errno> {
match fs_name {
"devfs" => Ok(devfs::root().clone()),
"sysfs" => Ok(sysfs::root().clone()),
_ => todo!()
_ => todo!(),
}
}
+105 -42
View File
@@ -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,34 +185,48 @@ 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;
use crate::dev::timer::TimestampSource;
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)?;
let time = machine::local_timer().timestamp()?;
writeln!(&mut writer, "{} {}", time.as_secs(), time.subsec_nanos())
.map_err(|_| Errno::InvalidArgument)?;
Ok(writer.count())
});
}
+1 -1
View File
@@ -4,7 +4,7 @@ use crate::config::{ConfigKey, CONFIG};
use crate::fs::{devfs, MemfsBlockAlloc};
use crate::mem;
use crate::proc::{elf, Process};
use libsys::stat::{FileDescriptor, OpenFlags, UserId, GroupId};
use libsys::stat::{FileDescriptor, GroupId, OpenFlags, UserId};
use memfs::Ramfs;
use vfs::{Filesystem, Ioctx};
+6 -10
View File
@@ -1,20 +1,16 @@
//! 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,
const_btree_new,
linked_list_cursors,
panic_info_message,
alloc_error_handler,
linked_list_cursors,
const_btree_new,
asm_const,
core_intrinsics,
const_generics_defaults,
)]
#![no_std]
#![no_main]
@@ -45,9 +41,9 @@ pub mod util;
#[panic_handler]
fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
// unsafe {
// asm!("msr daifset, #2");
// }
unsafe {
arch::intrin::irq_disable();
}
errorln!("Panic: {:?}", pi);
// TODO
+27
View File
@@ -4,6 +4,16 @@ pub mod heap;
pub mod phys;
pub mod virt;
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
/// 8-byte padding for x86_64 for user stack alignment
pub const USTACK_PADDING: usize = 8;
} else {
/// No stack alignment required
pub const USTACK_PADDING: usize = 0;
}
}
/// Virtual offset applied to kernel address space
pub const KERNEL_OFFSET: usize = 0xFFFFFF8000000000;
@@ -26,3 +36,20 @@ 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
#[cfg(target_arch = "aarch64")]
#[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
}
+7 -3
View File
@@ -1,9 +1,12 @@
use super::{PageInfo, PageUsage, PageStatistics};
use super::{PageInfo, PageStatistics, PageUsage};
use crate::mem::{virtualize, PAGE_SIZE};
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>;
@@ -42,7 +45,7 @@ impl SimpleManager {
kernel_heap: 0,
paging: 0,
user_private: 0,
filesystem: 0
filesystem: 0,
},
pages,
}
@@ -108,7 +111,8 @@ impl SimpleManager {
}
unsafe impl Manager for SimpleManager {
fn alloc_page(&mut self, pu: PageUsage) -> Result<usize, Errno> {
let res = self.alloc_single_index(pu)
let res = self
.alloc_single_index(pu)
.map(|r| (self.base_index + r) * PAGE_SIZE);
if res.is_ok() {
self.update_stats_alloc(pu, 1);
+56 -35
View File
@@ -2,18 +2,13 @@
use core::marker::PhantomData;
use core::ops::Deref;
// use cortex_a::asm::barrier::{self, dsb, isb};
// use cortex_a::registers::TTBR0_EL1;
use libsys::error::Errno;
// use tock_registers::interfaces::Writeable;
//
pub mod table;
pub use table::{AddressSpace, Space};
use libsys::{mem::memcpy, error::Errno};
use crate::mem::{self, phys::{self, PageUsage}};
// pub use table::{Entry, MapAttributes, Space, Table};
// pub mod fixed;
// pub use fixed::FixedTableGroup;
use crate::arch::platform::table as plat_table;
pub mod table;
use crate::arch::platform::virt as virt_impl;
use table::{Space, SpaceImpl, MapAttributes};
/// Structure representing a region of memory used for MMIO/device access
// TODO: this shouldn't be trivially-cloneable and should instead incorporate
@@ -45,16 +40,15 @@ impl DeviceMemory {
///
/// See [FixedTableGroup::map_region]
pub fn map(name: &'static str, phys: usize, count: usize) -> Result<Self, Errno> {
todo!();
// let base = unsafe { KERNEL_TTBR1.map_region(phys, count) }?;
// debugln!(
// "Mapping {:#x}..{:#x} -> {:#x} for {:?}",
// base,
// base + count * 0x1000,
// phys,
// name
// );
// Ok(Self { name, base, count })
let base = virt_impl::map_device_memory(phys, count)?;
debugln!(
"Mapping {:#x}..{:#x} -> {:#x} for {:?}",
base,
base + count * 0x1000,
phys,
name
);
Ok(Self { name, base, count })
}
}
@@ -91,18 +85,45 @@ impl<T> Deref for DeviceMemoryIo<T> {
/// Sets up device mapping tables and disable lower-half
/// identity-mapped translation
pub fn enable() -> Result<(), Errno> {
plat_table::enable()
unsafe {
virt_impl::enable();
}
Ok(())
}
/// Writes a [Copy]able object to `dst` address in `space`. Will allocate any affected pages.
///
/// # Safety
///
/// Unsafe: arbitrary memory write.
pub unsafe fn write_paged<T: Clone + Copy>(space: &mut SpaceImpl, dst: usize, src: T) -> Result<(), Errno> {
write_paged_bytes(space, dst, core::slice::from_raw_parts(&src as *const _ as *const u8, core::mem::size_of::<T>()))
}
/// Writes a byte slice to `dst` address in `space`. Will allocate any affected pages.
///
/// # Safety
///
/// Unsafe: arbitrary memory write.
pub unsafe fn write_paged_bytes(space: &mut SpaceImpl, dst: usize, src: &[u8]) -> Result<(), Errno> {
if (src.len() + (dst % 4096)) > 4096 {
todo!("Object crossed page boundary");
}
let page_virt = dst & !4095;
let page_phys = if let Ok(phys) = space.translate(dst) {
phys
} else {
let page = phys::alloc_page(PageUsage::UserPrivate)?;
let flags = MapAttributes::SHARE_OUTER | MapAttributes::USER_READ;
space.map(page_virt, page, flags)?;
page
};
memcpy(
(mem::virtualize(page_phys) + (dst % 4096)) as *mut u8,
src.as_ptr(),
src.len(),
);
Ok(())
}
// unsafe {
// KERNEL_TTBR1.init_device_map();
//
// dsb(barrier::ISH);
// isb(barrier::SY);
// }
//
// // Disable lower-half translation
// TTBR0_EL1.set(0);
// //TCR_EL1.modify(TCR_EL1::EPD0::SET);
//
// Ok(())
// }
+134 -30
View File
@@ -1,63 +1,167 @@
//! Translation table manipulation facilities
use crate::arch::platform::virt as virt_impl;
use crate::mem::{
self,
phys::{self, PageUsage},
};
use core::ops::{Index, IndexMut};
use libsys::{error::Errno, mem::memset};
use crate::arch::platform::table;
pub use table::{Space, Table};
use core::ffi::c_void;
use libsys::error::Errno;
pub use virt_impl::{EntryImpl, SpaceImpl};
bitflags! {
/// Attributes attached to each translation [Entry]
/// Virtual space entry attributes
pub struct MapAttributes: u64 {
const USER_READ = 1 << 1;
const USER_WRITE = 1 << 2;
const USER_EXEC = 1 << 3;
const KERNEL_WRITE = 1 << 4;
const KERNEL_EXEC = 1 << 5;
/// Entry is readable by user threads
const USER_READ = 1 << 0;
/// Entry is writable by user threads
const USER_WRITE = 1 << 1;
/// Data from entry can be executed by user threads
const USER_EXEC = 1 << 2;
const SHARE_OUTER = 1 << 6;
const SHARE_INNER = 2 << 6;
/// Entry is writable by kernel
const KERNEL_WRITE = 1 << 3;
/// Data from entry can be executed by kernel
const KERNEL_EXEC = 1 << 4;
const NOT_GLOBAL = 1 << 8;
/// TODO TBD
const SHARE_OUTER = 1 << 5;
/// Memory is used for device interaction
const DEVICE_MEMORY = 1 << 6;
/// Entry is marked as Copy-on-Write
const COPY_ON_WRITE = 1 << 7;
/// Access flag for entry
const ACCESS = 1 << 8;
/// Entry is global across virtual address spaces
const GLOBAL = 1 << 9;
}
}
/// Interface for a single element of paging mapping
pub trait Entry: Clone + Copy {
fn from_parts(phys: usize, attrs: MapAttributes) -> Self;
fn target(self) -> usize;
/// Platform-specific entry attribute representation
type RawAttributes: From<MapAttributes> + Copy + Clone;
/// Invalid entry with no association
const EMPTY: Self;
/// Constructs an entry pointing to next-level table or page
fn normal(addr: usize, attrs: MapAttributes) -> Self;
/// Constructs an entry pointing to a contiguous block
fn block(addr: usize, attrs: MapAttributes) -> Self;
/// Returns physical address the entry points to
fn address(self) -> usize;
/// Changes the entry physical address
fn set_address(&mut self, value: usize);
/// Marks page as CoW and removes user write ability
fn fork_with_cow(&mut self) -> Self;
/// Clones a CoW entry
fn copy_from_cow(self, new_addr: usize) -> Self;
/// Returns `true` if entry maps a paging element
fn is_present(self) -> bool;
fn is_table(self) -> bool;
/// Returns `true` if page is a 4KiB one
fn is_normal(self) -> bool;
/// Returns `true` if page is marked as Copy-on-Write
fn is_cow(self) -> bool;
/// Returns `true` if page is write-accessible for user threads
fn is_user_writable(self) -> bool;
}
pub trait AddressSpace {
/// Interface for virtual address space manipulation
pub trait Space {
/// Single table entry data type
type Entry: Entry;
/// Creates an empty address space
fn alloc_empty() -> Result<&'static mut Self, Errno>;
/// Removes all non-kernel entries from the space.
///
/// # Safety
///
/// Only safe to call on spaces not currently in use, otherwise will
/// trigger undefined behavior and/or page fault.
unsafe fn release(space: &'static mut Self);
/// Forks a process virtual memory space
fn fork(&mut self) -> Result<&'static mut Self, Errno>;
fn release(space: &mut Self);
fn address_phys(&mut self) -> usize;
fn read_last_level_entry(&mut self, virt: usize) -> Result<Self::Entry, Errno>;
fn write_last_level_entry(
/// Writes an entry corresponding to `virt` address
/// to last-level table of this address space.
///
/// # Safety
///
/// Unsafe: arbitrary memory space manipulation.
unsafe fn write_last_level(
&mut self,
virt: usize,
entry: Self::Entry,
map_intermediate: bool,
create_intermediate: bool,
overwrite: bool,
) -> Result<(), Errno>;
#[inline(always)]
fn map(&mut self, virt: usize, phys: usize, attrs: MapAttributes) -> Result<(), Errno> {
let entry = Entry::from_parts(phys, attrs);
self.write_last_level_entry(virt, entry, true).map(|_| ())
/// Reads an entry corresponding to `virt` address
fn read_last_level(&self, virt: usize) -> Result<Self::Entry, Errno>;
/// Returns physical address of this table
fn address_phys(&mut self) -> usize {
self as *mut _ as *mut c_void as usize - mem::KERNEL_OFFSET
}
#[inline(always)]
/// Performs Copy-on-Write cloning on page fault
fn try_cow_copy(&mut self, virt: usize) -> Result<(), Errno> {
let entry = self.read_last_level(virt)?;
let src_phys = entry.address();
if !entry.is_cow() {
warnln!(
"Entry is not marked as CoW: {:#x}, points to {:#x}",
virt,
src_phys
);
return Err(Errno::DoesNotExist);
}
let dst_phys = unsafe { phys::copy_cow_page(src_phys)? };
unsafe {
self.write_last_level(virt, entry.copy_from_cow(dst_phys), false, true)?;
}
Ok(())
}
/// Creates a new virtual -> physical memory mapping. Will fail if one is
/// already associated with given virtual address.
fn map(&mut self, virt: usize, phys: usize, attrs: MapAttributes) -> Result<(), Errno> {
#[cfg(feature = "verbose")]
debugln!("Map {:#x} -> {:#x}, {:?}", virt, phys, attrs);
unsafe {
self.write_last_level(
virt,
Entry::normal(phys, attrs | MapAttributes::ACCESS),
true,
false,
)
}
}
/// Returns a virtual address physical mapping destination
fn translate(&mut self, virt: usize) -> Result<usize, Errno> {
self.read_last_level_entry(virt).map(Entry::target)
self.read_last_level(virt).map(Entry::address)
}
/// Releases memory from virtual address range `start`..`start + len * 0x1000`
fn free(&mut self, start: usize, len: usize) -> Result<(), Errno> {
for i in 0..len {
unsafe {
self.write_last_level(start + i * 0x1000, Self::Entry::EMPTY, false, true)?;
}
}
Ok(())
}
/// Allocates a contiguous region from the address space and maps
+19 -15
View File
@@ -2,12 +2,12 @@
use crate::mem::{
self,
phys::{self, PageUsage},
virt::{Space, table::{AddressSpace, MapAttributes}},
virt::table::{MapAttributes, Space, SpaceImpl},
};
use core::mem::{size_of, MaybeUninit};
use libsys::{
error::Errno,
traits::{Read, Seek, SeekDir}
traits::{Read, Seek, SeekDir},
};
use vfs::FileRef;
@@ -66,10 +66,10 @@ struct Phdr<E: Elf> {
}
fn map_flags(elf_flags: usize) -> MapAttributes {
let mut dst_flags = MapAttributes::SHARE_OUTER | MapAttributes::NOT_GLOBAL;
let mut dst_flags = MapAttributes::SHARE_OUTER;
if elf_flags & (1 << 0) /* PF_X */ != 0 {
dst_flags |= MapAttributes::USER_EXEC | MapAttributes::KERNEL_EXEC;
dst_flags |= MapAttributes::USER_EXEC;
}
match (elf_flags & (3 << 1)) >> 1 {
@@ -80,7 +80,7 @@ fn map_flags(elf_flags: usize) -> MapAttributes {
// Read-only
2 => dst_flags |= MapAttributes::USER_READ,
// Read+Write
3 => dst_flags |= MapAttributes::USER_READ | MapAttributes::USER_WRITE | MapAttributes::KERNEL_WRITE,
3 => dst_flags |= MapAttributes::USER_WRITE | MapAttributes::USER_READ,
_ => unreachable!(),
};
@@ -88,7 +88,7 @@ fn map_flags(elf_flags: usize) -> MapAttributes {
}
unsafe fn load_bytes<F>(
space: &mut Space,
space: &mut SpaceImpl,
dst_virt: usize,
mut read: F,
size: usize,
@@ -107,15 +107,17 @@ where
let page_off = (dst_page_off + off) % mem::PAGE_SIZE;
let count = core::cmp::min(rem, mem::PAGE_SIZE - page_off);
let page = phys::alloc_page(PageUsage::UserPrivate)?;
let page = if let Ok(page) = space.translate(dst_page + page_idx * mem::PAGE_SIZE) {
page
} else {
let page = phys::alloc_page(PageUsage::UserPrivate)?;
// TODO fetch existing mapping and test flag equality instead
// if flags differ, bail out
if let Err(e) = space.map(dst_page + page_idx * mem::PAGE_SIZE, page, map_flags(flags)) {
if e != Errno::AlreadyExists {
return Err(e);
}
}
// TODO fetch existing mapping and test flag equality instead
// if flags differ, bail out
space.map(dst_page + page_idx * mem::PAGE_SIZE, page, map_flags(flags))?;
page
};
let dst_page_virt = mem::virtualize(page + page_off);
let dst = core::slice::from_raw_parts_mut(dst_page_virt as *mut u8, count);
@@ -146,7 +148,7 @@ unsafe fn read_struct<T>(src: &FileRef, pos: usize) -> Result<T, Errno> {
}
/// Loads an ELF program from `source` into target `space`
pub fn load_elf(space: &mut Space, source: FileRef) -> Result<usize, Errno> {
pub fn load_elf(space: &mut SpaceImpl, source: FileRef) -> Result<usize, Errno> {
let ehdr: Ehdr<Elf64> = unsafe { read_struct(&source, 0).unwrap() };
if &ehdr.ident[0..4] != b"\x7FELF" {
@@ -169,6 +171,7 @@ pub fn load_elf(space: &mut Space, source: FileRef) -> Result<usize, Errno> {
);
if phdr.filesz > 0 {
debugln!("Load bytes {:#x}..{:#x}", phdr.vaddr, phdr.vaddr + phdr.filesz);
unsafe {
load_bytes(
space,
@@ -190,6 +193,7 @@ pub fn load_elf(space: &mut Space, source: FileRef) -> Result<usize, Errno> {
if phdr.memsz > phdr.filesz {
let len = (phdr.memsz - phdr.filesz) as usize;
debugln!("Zero bytes {:#x}..{:#x}", phdr.vaddr + phdr.filesz, phdr.vaddr + phdr.memsz);
unsafe {
load_bytes(
space,
+15 -5
View File
@@ -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
+2 -2
View File
@@ -3,12 +3,12 @@
use crate::init;
use crate::sync::IrqSafeSpinLock;
use alloc::collections::BTreeMap;
use libsys::proc::{Tid, Pid};
use libsys::proc::{Pid, Tid};
pub mod elf;
pub mod thread;
pub use thread::{Thread, ThreadRef, State as ThreadState};
pub(self) use thread::Context;
pub use thread::{State as ThreadState, Thread, ThreadRef};
pub mod process;
pub use process::{Process, ProcessRef, ProcessState};
pub mod io;
+172 -237
View File
@@ -1,24 +1,22 @@
//! Process data and control
use crate::arch::platform::ForkFrame;
use crate::mem::{
self,
phys::{self, PageUsage},
virt::{table::{MapAttributes, AddressSpace}, Space},
virt::{write_paged, write_paged_bytes, table::{MapAttributes, Space, SpaceImpl}},
};
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::arch::{intrin, platform::ForkFrame};
use crate::sync::{IrqSafeSpinLock, IrqSafeSpinLockGuard};
use alloc::{rc::Rc, vec::Vec};
use core::sync::atomic::{AtomicU32, Ordering};
use libsys::{
error::Errno,
mem::memcpy,
proc::{ExitCode, Pid},
signal::Signal,
ProgramArgs,
};
use core::arch::asm;
/// Wrapper type for a process struct reference
pub type ProcessRef = Rc<Process>;
@@ -33,7 +31,7 @@ pub enum ProcessState {
}
struct ProcessInner {
space: Option<&'static mut Space>,
space: Option<&'static mut SpaceImpl>,
state: ProcessState,
id: Pid,
pgid: Pid,
@@ -101,11 +99,80 @@ impl Process {
#[inline]
pub fn manipulate_space<R, F>(&self, f: F) -> R
where
F: FnOnce(&mut Space) -> R,
F: FnOnce(&mut SpaceImpl) -> R,
{
f(self.inner.lock().space.as_mut().unwrap())
}
/// Handles all pending signals (when returning from aborted syscall)
pub fn handle_pending_signals(&self) {
let mut lock = self.inner.lock();
let table = Self::space_phys(&mut lock);
let main_thread = Thread::get(lock.threads[0]).unwrap();
drop(lock);
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);
main_thread.clone().enter_signal(signal, table);
} else {
break;
}
}
}
/// Sets a pending signal for process
pub fn set_signal(&self, signal: Signal) {
let mut lock = self.inner.lock();
let table = Self::space_phys(&mut lock);
let main_thread = Thread::get(lock.threads[0]).unwrap();
drop(lock);
// TODO check that `signal` is not a fault signal
// it is illegal to call this function with
// fault signals
match main_thread.state() {
ThreadState::Running => {
main_thread.enter_signal(signal, table);
}
ThreadState::Waiting => {
self.signal_state.fetch_or(1 << (signal as u32), Ordering::Release);
main_thread.interrupt_wait(true);
}
ThreadState::Ready => {
main_thread.clone().setup_signal(signal, table);
main_thread.interrupt_wait(false);
}
ThreadState::Finished => {
// TODO report error back
todo!()
}
}
}
/// Immediately delivers a signal to requested thread
pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
let mut lock = self.inner.lock();
let table = Self::space_phys(&mut lock);
drop(lock);
thread.enter_signal(signal, table);
}
/// Crates a new thread in the process
pub fn new_user_thread(&self, entry: usize, stack: usize, arg: usize) -> Result<Tid, Errno> {
let mut lock = self.inner.lock();
let table = Self::space_phys(&mut lock);
let thread = Thread::new_user(lock.id, entry, stack, arg, table)?;
let tid = thread.id();
lock.threads.push(tid);
SCHED.enqueue(tid);
Ok(tid)
}
/// Creates a new kernel process
pub fn new_kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<ProcessRef, Errno> {
let id = new_kernel_pid();
@@ -160,196 +227,121 @@ impl Process {
lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48)
}
// /// Handles all pending signals (when returning from aborted syscall)
// pub fn handle_pending_signals(&self) {
// let mut lock = self.inner.lock();
// let table = Self::space_phys(&lock);
// let main_thread = Thread::get(lock.threads[0]).unwrap();
// drop(lock);
// 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);
// main_thread.clone().enter_signal(signal, table);
// } else {
// break;
// }
// }
// }
// /// Sets a pending signal for a process
// pub fn set_signal(&self, signal: Signal) {
// let mut lock = self.inner.lock();
// let table = Self::space_phys(&lock);
// let main_thread = Thread::get(lock.threads[0]).unwrap();
// drop(lock);
// // TODO check that `signal` is not a fault signal
// // it is illegal to call this function with
// // fault signals
// match main_thread.state() {
// ThreadState::Running => {
// main_thread.enter_signal(signal, table);
// }
// ThreadState::Waiting => {
// self.signal_state.fetch_or(1 << (signal as u32), Ordering::Release);
// main_thread.interrupt_wait(true);
// }
// ThreadState::Ready => {
// main_thread.clone().setup_signal(signal, table);
// main_thread.interrupt_wait(false);
// }
// ThreadState::Finished => {
// // TODO report error back
// todo!()
// }
// }
// }
// /// Immediately delivers a signal to requested thread
// pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
// let mut lock = self.inner.lock();
// let table = Self::space_phys(&lock);
// drop(lock);
// thread.enter_signal(signal, table);
// }
// /// Crates a new thread in the process
// pub fn new_user_thread(&self, entry: usize, stack: usize, arg: usize) -> Result<Tid, Errno> {
// let mut lock = self.inner.lock();
// let table = Self::space_phys(&lock);
// let thread = Thread::new_user(lock.id, entry, stack, arg, table)?;
// let tid = thread.id();
// lock.threads.push(tid);
// SCHED.enqueue(tid);
// Ok(tid)
// }
/// Creates a "fork" of the process, cloning its address space and
/// resources
pub fn fork(&self, frame: &mut ForkFrame) -> Result<Pid, Errno> {
todo!();
let src_io = self.io.lock();
let mut src_inner = self.inner.lock();
let dst_id = new_user_pid();
let dst_space = src_inner.space.as_mut().unwrap().fork()?;
todo!()
let dst_space_phys = (dst_space as *mut _ as usize) - mem::KERNEL_OFFSET;
let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
// let dst_space_phys = (dst_space as *mut _ as usize) - mem::KERNEL_OFFSET;
// let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
let mut threads = Vec::new();
let tid = Thread::fork(Some(dst_id), frame, dst_ttbr0)?.id();
threads.push(tid);
// let mut threads = Vec::new();
// let tid = Thread::fork(Some(dst_id), frame, dst_ttbr0)?.id();
// threads.push(tid);
let dst = Rc::new(Self {
exit_wait: Wait::new("process_exit"),
io: IrqSafeSpinLock::new(src_io.fork()?),
signal_state: AtomicU32::new(0),
inner: IrqSafeSpinLock::new(ProcessInner {
threads,
exit: None,
space: Some(dst_space),
state: ProcessState::Active,
id: dst_id,
pgid: src_inner.pgid,
ppid: Some(src_inner.id),
sid: src_inner.sid,
}),
});
// let dst = Rc::new(Self {
// exit_wait: Wait::new("process_exit"),
// io: IrqSafeSpinLock::new(src_io.fork()?),
// signal_state: AtomicU32::new(0),
// inner: IrqSafeSpinLock::new(ProcessInner {
// threads,
// exit: None,
// space: Some(dst_space),
// state: ProcessState::Active,
// id: dst_id,
// pgid: src_inner.pgid,
// ppid: Some(src_inner.id),
// sid: src_inner.sid,
// }),
// });
debugln!("Process {:?} forked into {:?}", src_inner.id, dst_id);
assert!(PROCESSES.lock().insert(dst_id, dst).is_none());
// debugln!("Process {:?} forked into {:?}", src_inner.id, dst_id);
// assert!(PROCESSES.lock().insert(dst_id, dst).is_none());
SCHED.enqueue(tid);
// SCHED.enqueue(tid);
// Ok(dst_id)
Ok(dst_id)
}
/// Terminates a process.
pub fn exit(self: ProcessRef, status: ExitCode) {
todo!()
// let thread = Thread::current();
// let mut lock = self.inner.lock();
// let is_running = thread.owner_id().map(|e| e == lock.id).unwrap_or(false);
let thread = Thread::current();
let mut lock = self.inner.lock();
let is_running = thread.owner_id().map(|e| e == lock.id).unwrap_or(false);
// infoln!("Process {:?} is exiting: {:?}", lock.id, status);
// assert!(lock.exit.is_none());
// lock.exit = Some(status);
// lock.state = ProcessState::Finished;
infoln!("Process {:?} is exiting: {:?}", lock.id, status);
assert!(lock.exit.is_none());
lock.exit = Some(status);
lock.state = ProcessState::Finished;
// for &tid in lock.threads.iter() {
// let thread = Thread::get(tid).unwrap();
// if thread.state() == ThreadState::Waiting {
// todo!()
// }
// thread.terminate(status);
// SCHED.dequeue(tid);
// }
for &tid in lock.threads.iter() {
let thread = Thread::get(tid).unwrap();
if thread.state() == ThreadState::Waiting {
todo!()
}
thread.terminate(status);
SCHED.dequeue(tid);
}
// if let Some(space) = lock.space.take() {
// unsafe {
// Space::release(space);
// Process::invalidate_asid((lock.id.asid() as usize) << 48);
// }
// }
if let Some(space) = lock.space.take() {
unsafe {
SpaceImpl::release(space);
intrin::flush_tlb_asid((lock.id.asid() as usize) << 48);
}
}
// // TODO when exiting from signal handler interrupting an IO operation
// // deadlock is achieved
// self.io.lock().handle_exit();
// TODO when exiting from signal handler interrupting an IO operation
// deadlock is achieved
self.io.lock().handle_exit();
// drop(lock);
drop(lock);
// self.exit_wait.wakeup_all();
self.exit_wait.wakeup_all();
// if is_running {
// SCHED.switch(true);
// panic!("This code should never run");
// }
if is_running {
SCHED.switch(true);
panic!("This code should never run");
}
}
/// Terminates a thread of the process. If the thread is the only
/// one remaining, process itself is exited (see [Process::exit])
pub fn exit_thread(thread: ThreadRef, status: ExitCode) {
todo!()
let switch = {
let switch = thread.state() == ThreadState::Running;
let process = thread.owner().unwrap();
let mut lock = process.inner.lock();
let tid = thread.id();
// let switch = {
// let switch = thread.state() == ThreadState::Running;
// let process = thread.owner().unwrap();
// let mut lock = process.inner.lock();
// let tid = thread.id();
if lock.threads.len() == 1 {
// TODO call Process::exit instead?
drop(lock);
process.exit(status);
return;
}
// if lock.threads.len() == 1 {
// // TODO call Process::exit instead?
// drop(lock);
// process.exit(status);
// return;
// }
lock.threads.retain(|&e| e != tid);
// lock.threads.retain(|&e| e != tid);
thread.terminate(status);
SCHED.dequeue(tid);
debugln!("Thread {:?} terminated", tid);
// thread.terminate(status);
// SCHED.dequeue(tid);
// debugln!("Thread {:?} terminated", tid);
switch
};
// switch
// };
// if switch {
// // TODO retain thread ID in process "finished" list and
// // drop it when process finishes
// SCHED.switch(true);
// panic!("This code should not run");
// } else {
// // Can drop this thread: it's not running
// todo!();
// }
if switch {
// TODO retain thread ID in process "finished" list and
// drop it when process finishes
SCHED.switch(true);
panic!("This code should not run");
} else {
// Can drop this thread: it's not running
todo!();
}
}
fn collect(&self) -> Option<ExitCode> {
@@ -380,64 +372,16 @@ impl Process {
}
}
fn write_paged<T>(space: &mut Space, dst: usize, src: T) -> Result<(), Errno> {
let size = core::mem::size_of::<T>();
if (size + (dst % 4096)) > 4096 {
todo!("Object crossed page boundary");
}
let page_virt = dst & !4095;
let page_phys = if let Ok(phys) = space.translate(dst) {
phys
} else {
let page = phys::alloc_page(PageUsage::UserPrivate)?;
let flags = MapAttributes::SHARE_OUTER
| MapAttributes::NOT_GLOBAL
| MapAttributes::USER_READ;
space.map(page_virt, page, flags)?;
page
};
unsafe {
core::ptr::write((mem::virtualize(page_phys) + (dst % 4096)) as *mut T, src);
}
Ok(())
}
fn write_paged_bytes(space: &mut Space, dst: usize, src: &[u8]) -> Result<(), Errno> {
if (src.len() + (dst % 4096)) > 4096 {
todo!("Object crossed page boundary");
}
let page_virt = dst & !4095;
let page_phys = if let Ok(phys) = space.translate(dst) {
phys
} else {
let page = phys::alloc_page(PageUsage::UserPrivate)?;
let flags = MapAttributes::SHARE_OUTER
| MapAttributes::NOT_GLOBAL
| MapAttributes::USER_READ;
space.map(page_virt, page, flags)?;
page
};
unsafe {
memcpy(
(mem::virtualize(page_phys) + (dst % 4096)) as *mut u8,
src.as_ptr(),
src.len(),
);
}
Ok(())
}
fn store_arguments(space: &mut Space, argv: &[&str]) -> Result<usize, Errno> {
fn store_arguments(space: &mut SpaceImpl, argv: &[&str]) -> Result<usize, Errno> {
let mut offset = 0usize;
// TODO vmalloc?
let base = 0x60000000;
// 1. Store program argument string bytes
for arg in argv.iter() {
Self::write_paged_bytes(space, base + offset, arg.as_bytes())?;
unsafe {
write_paged_bytes(space, base + offset, arg.as_bytes())?;
}
offset += arg.len();
}
// Align
@@ -448,8 +392,10 @@ impl Process {
let mut data_offset = 0usize;
for arg in argv.iter() {
// XXX this is really unsafe and I am not really sure ABI will stay like this XXX
Self::write_paged(space, base + offset, base + data_offset)?;
Self::write_paged(space, base + offset + 8, arg.len())?;
unsafe {
write_paged(space, base + offset, base + data_offset)?;
write_paged(space, base + offset + 8, arg.len())?;
}
offset += 16;
data_offset += arg.len();
}
@@ -461,35 +407,26 @@ impl Process {
storage: base,
size: offset + core::mem::size_of::<ProgramArgs>(),
};
Self::write_paged(space, base + offset, data)?;
unsafe {
write_paged(space, base + offset, data)?;
}
Ok(base + offset)
}
/// Returns the process's address space ID
pub fn asid(&self) -> usize {
(self.id().asid() as usize) << 48
}
// pub fn invalidate_tlb(&self) {
// Process::invalidate_asid(self.asid());
// }
// #[inline]
// pub fn invalidate_asid(asid: usize) {
// unsafe {
// asm!("tlbi aside1, {}", in(reg) asid);
// }
// }
/// Loads a new program into current process address space
pub fn execve<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
pub fn execve<F: FnOnce(&mut SpaceImpl) -> Result<usize, Errno>>(
loader: F,
argv: &[&str],
) -> Result<(), Errno> {
unsafe {
// Run with interrupts disabled
// asm!("msr daifset, #2");
asm!("cli");
intrin::irq_disable();
}
let proc = Process::current();
@@ -520,17 +457,15 @@ impl Process {
proc.io.lock().handle_cloexec();
let new_space = Space::alloc_empty()?;
let new_space = SpaceImpl::alloc_empty()?;
let new_space_phys = (new_space as *mut _ as usize) - mem::KERNEL_OFFSET;
let ustack_virt_bottom = Self::USTACK_VIRT_TOP - Self::USTACK_PAGES * mem::PAGE_SIZE;
for i in 0..Self::USTACK_PAGES {
let page = phys::alloc_page(PageUsage::UserPrivate).unwrap();
let flags = MapAttributes::SHARE_OUTER
| MapAttributes::NOT_GLOBAL
| MapAttributes::USER_WRITE
| MapAttributes::USER_READ
| MapAttributes::KERNEL_WRITE;
| MapAttributes::USER_WRITE;
new_space
.map(ustack_virt_bottom + i * mem::PAGE_SIZE, page, flags)
.unwrap();
@@ -546,13 +481,13 @@ 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,
arg,
new_space_phys | asid,
Self::USTACK_VIRT_TOP,
Self::USTACK_VIRT_TOP - mem::USTACK_PADDING,
));
drop(process_lock);
+5 -7
View File
@@ -1,10 +1,10 @@
//!
use crate::arch::intrin;
use crate::proc::{Thread, ThreadRef, THREADS};
use crate::sync::IrqSafeSpinLock;
use crate::util::InitOnce;
use libsys::proc::Tid;
use alloc::{collections::VecDeque, rc::Rc};
use core::arch::asm;
use libsys::proc::Tid;
struct SchedulerInner {
queue: VecDeque<Tid>,
@@ -70,8 +70,7 @@ impl Scheduler {
THREADS.lock().get(&id).unwrap().clone()
};
asm!("cli");
// asm!("msr daifset, #2");
intrin::irq_disable();
Thread::enter(thread)
}
@@ -123,8 +122,7 @@ impl Scheduler {
if !Rc::ptr_eq(&from, &to) {
unsafe {
asm!("cli");
// asm!("msr daifset, #2");
intrin::irq_disable();
Thread::switch(from, to, discard);
}
}
@@ -153,7 +151,7 @@ pub fn is_ready() -> bool {
#[inline(never)]
extern "C" fn idle_fn(_a: usize) -> ! {
loop {
// cortex_a::asm::wfi();
core::hint::spin_loop();
}
}
+28 -27
View File
@@ -1,6 +1,7 @@
//! Facilities for controlling threads - smallest units of
//! execution in the operating system
// use crate::arch::aarch64::exception::ExceptionFrame;
use crate::arch::platform::ForkFrame;
use crate::proc::{
wait::{Wait, WaitStatus},
Process, ProcessRef, SCHED, THREADS,
@@ -143,34 +144,34 @@ impl Thread {
Ok(res)
}
// /// Creates a fork thread cloning `frame` context
// pub fn fork(
// owner: Option<Pid>,
// frame: &ExceptionFrame,
// ttbr0: usize,
// ) -> Result<ThreadRef, Errno> {
// let id = new_tid();
/// Creates a fork thread cloning `frame` context
pub fn fork(
owner: Option<Pid>,
frame: &ForkFrame,
space_phys: usize,
) -> Result<ThreadRef, Errno> {
let id = new_tid();
// let res = Rc::new(Self {
// ctx: UnsafeCell::new(Context::fork(frame, ttbr0)),
// signal_ctx: UnsafeCell::new(Context::empty()),
// signal_pending: AtomicU32::new(0),
// exit_wait: Wait::new("thread_exit"),
// exit_status: InitOnce::new(),
// inner: IrqSafeSpinLock::new(ThreadInner {
// signal_entry: 0,
// signal_stack: 0,
// id,
// owner,
// pending_wait: None,
// wait_status: WaitStatus::Done,
// state: State::Ready,
// }),
// });
// debugln!("Forked new user thread: {:?}", id);
// assert!(THREADS.lock().insert(id, res.clone()).is_none());
// Ok(res)
// }
let res = Rc::new(Self {
ctx: UnsafeCell::new(Context::fork(frame, space_phys)),
signal_ctx: UnsafeCell::new(Context::empty()),
signal_pending: AtomicU32::new(0),
exit_wait: Wait::new("thread_exit"),
exit_status: InitOnce::new(),
inner: IrqSafeSpinLock::new(ThreadInner {
signal_entry: 0,
signal_stack: 0,
id,
owner,
pending_wait: None,
wait_status: WaitStatus::Done,
state: State::Ready,
}),
});
debugln!("Forked new user thread: {:?}", id);
assert!(THREADS.lock().insert(id, res.clone()).is_none());
Ok(res)
}
/// Returns the thread ID
#[inline]
+118 -170
View File
@@ -13,7 +13,7 @@ use libsys::{error::Errno, proc::Tid, stat::FdSet};
pub struct Wait {
queue: IrqSafeSpinLock<LinkedList<Tid>>,
#[allow(dead_code)]
name: &'static str
name: &'static str,
}
/// Status of a (possibly) pending wait
@@ -39,92 +39,46 @@ pub static WAIT_SELECT: Wait = Wait::new("select");
/// Checks for any timed out wait channels and interrupts them
pub fn tick() {
todo!();
// let time = machine::local_timer().timestamp().unwrap();
// let mut list = TICK_LIST.lock();
// let mut cursor = list.cursor_front_mut();
let time = machine::local_timer().timestamp().unwrap();
let mut list = TICK_LIST.lock();
let mut cursor = list.cursor_front_mut();
// while let Some(item) = cursor.current() {
// if time > item.deadline {
// let tid = item.tid;
// cursor.remove_current();
// SCHED.enqueue(tid);
// } else {
// cursor.move_next();
// }
// }
while let Some(item) = cursor.current() {
if time > item.deadline {
let tid = item.tid;
cursor.remove_current();
SCHED.enqueue(tid);
} else {
cursor.move_next();
}
}
}
/// Suspends current process for given duration
pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
todo!()
// // Dummy wait descriptor which will never receive notifications
// static SLEEP_NOTIFY: Wait = Wait::new("sleep");
// let deadline = machine::local_timer().timestamp()? + timeout;
// match SLEEP_NOTIFY.wait(Some(deadline)) {
// Err(Errno::Interrupt) => {
// *remaining = deadline - machine::local_timer().timestamp()?;
// Err(Errno::Interrupt)
// }
// Err(Errno::TimedOut) => Ok(()),
// Ok(_) => panic!("Impossible result"),
// res => res,
// }
// Dummy wait descriptor which will never receive notifications
static SLEEP_NOTIFY: Wait = Wait::new("sleep");
let deadline = machine::local_timer().timestamp()? + timeout;
match SLEEP_NOTIFY.wait(Some(deadline)) {
Err(Errno::Interrupt) => {
*remaining = deadline - machine::local_timer().timestamp()?;
Err(Errno::Interrupt)
}
Err(Errno::TimedOut) => Ok(()),
Ok(_) => panic!("Impossible result"),
res => res,
}
}
/// Suspends current process until some file descriptor
/// signals data available
pub fn select(
thread: ThreadRef,
mut rfds: Option<&mut FdSet>,
mut wfds: Option<&mut FdSet>,
timeout: Option<Duration>,
_thread: ThreadRef,
_rfds: Option<&mut FdSet>,
_wfds: Option<&mut FdSet>,
_timeout: Option<Duration>,
) -> Result<usize, Errno> {
todo!();
// if wfds.is_none() && rfds.is_none() {
// todo!();
// }
// let read = rfds.as_deref().map(FdSet::clone);
// let write = wfds.as_deref().map(FdSet::clone);
// if let Some(rfds) = &mut rfds {
// rfds.reset();
// }
// if let Some(wfds) = &mut wfds {
// wfds.reset();
// }
// let deadline = timeout.map(|v| v + machine::local_timer().timestamp().unwrap());
// let proc = thread.owner().unwrap();
// let mut io = proc.io.lock();
// loop {
// if let Some(read) = &read {
// for fd in read.iter() {
// let file = io.file(fd)?;
// if file.borrow().is_ready(false)? {
// rfds.as_mut().unwrap().set(fd);
// return Ok(1);
// }
// }
// }
// if let Some(write) = &write {
// for fd in write.iter() {
// let file = io.file(fd)?;
// if file.borrow().is_ready(true)? {
// wfds.as_mut().unwrap().set(fd);
// return Ok(1);
// }
// }
// }
// // Suspend
// match WAIT_SELECT.wait(deadline) {
// Err(Errno::TimedOut) => return Ok(0),
// Err(e) => return Err(e),
// Ok(_) => {}
// }
// }
}
impl Wait {
@@ -132,71 +86,67 @@ impl Wait {
pub const fn new(name: &'static str) -> Self {
Self {
queue: IrqSafeSpinLock::new(LinkedList::new()),
name
name,
}
}
/// Interrupt wait pending on the channel
pub fn abort(&self, tid: Tid, enqueue: bool) {
todo!();
let mut queue = self.queue.lock();
let mut tick_lock = TICK_LIST.lock();
let mut cursor = tick_lock.cursor_front_mut();
while let Some(item) = cursor.current() {
if tid == item.tid {
cursor.remove_current();
break;
} else {
cursor.move_next();
}
}
// let mut queue = self.queue.lock();
// let mut tick_lock = TICK_LIST.lock();
// let mut cursor = tick_lock.cursor_front_mut();
// while let Some(item) = cursor.current() {
// if tid == item.tid {
// cursor.remove_current();
// break;
// } else {
// cursor.move_next();
// }
// }
// let mut cursor = queue.cursor_front_mut();
// while let Some(item) = cursor.current() {
// if tid == *item {
// cursor.remove_current();
// let thread = Thread::get(tid).unwrap();
// thread.set_wait_status(WaitStatus::Interrupted);
// if enqueue {
// SCHED.enqueue(tid);
// }
// break;
// } else {
// cursor.move_next();
// }
// }
let mut cursor = queue.cursor_front_mut();
while let Some(item) = cursor.current() {
if tid == *item {
cursor.remove_current();
let thread = Thread::get(tid).unwrap();
thread.set_wait_status(WaitStatus::Interrupted);
if enqueue {
SCHED.enqueue(tid);
}
break;
} else {
cursor.move_next();
}
}
}
fn wakeup_some(&self, mut limit: usize) -> usize {
todo!();
// No IRQs will arrive now == safe to manipulate tick list
let mut queue = self.queue.lock();
let mut count = 0;
while limit != 0 && !queue.is_empty() {
let tid = queue.pop_front();
if let Some(tid) = tid {
let mut tick_lock = TICK_LIST.lock();
let mut cursor = tick_lock.cursor_front_mut();
while let Some(item) = cursor.current() {
if tid == item.tid {
cursor.remove_current();
break;
} else {
cursor.move_next();
}
}
drop(tick_lock);
// // No IRQs will arrive now == safe to manipulate tick list
// let mut queue = self.queue.lock();
// let mut count = 0;
// while limit != 0 && !queue.is_empty() {
// let tid = queue.pop_front();
// if let Some(tid) = tid {
// let mut tick_lock = TICK_LIST.lock();
// let mut cursor = tick_lock.cursor_front_mut();
// while let Some(item) = cursor.current() {
// if tid == item.tid {
// cursor.remove_current();
// break;
// } else {
// cursor.move_next();
// }
// }
// drop(tick_lock);
Thread::get(tid).unwrap().set_wait_status(WaitStatus::Done);
SCHED.enqueue(tid);
}
// Thread::get(tid).unwrap().set_wait_status(WaitStatus::Done);
// SCHED.enqueue(tid);
// }
// limit -= 1;
// count += 1;
// }
// count
limit -= 1;
count += 1;
}
count
}
/// Notifies all processes waiting for this event
@@ -212,53 +162,51 @@ impl Wait {
/// Suspends current process until event is signalled or
/// (optional) deadline is reached
pub fn wait(&self, deadline: Option<Duration>) -> Result<(), Errno> {
todo!();
let thread = Thread::current();
//let deadline = timeout.map(|t| machine::local_timer().timestamp().unwrap() + t);
let mut queue_lock = self.queue.lock();
// let thread = Thread::current();
// //let deadline = timeout.map(|t| machine::local_timer().timestamp().unwrap() + t);
// let mut queue_lock = self.queue.lock();
queue_lock.push_back(thread.id());
thread.setup_wait(self);
// queue_lock.push_back(thread.id());
// thread.setup_wait(self);
if let Some(deadline) = deadline {
TICK_LIST.lock().push_back(Timeout {
tid: thread.id(),
deadline,
});
}
// if let Some(deadline) = deadline {
// TICK_LIST.lock().push_back(Timeout {
// tid: thread.id(),
// deadline,
// });
// }
loop {
match thread.wait_status() {
WaitStatus::Pending => {}
WaitStatus::Done => {
return Ok(());
}
WaitStatus::Interrupted => {
return Err(Errno::Interrupt);
}
};
// loop {
// match thread.wait_status() {
// WaitStatus::Pending => {}
// WaitStatus::Done => {
// return Ok(());
// }
// WaitStatus::Interrupted => {
// return Err(Errno::Interrupt);
// }
// };
drop(queue_lock);
thread.enter_wait();
queue_lock = self.queue.lock();
// drop(queue_lock);
// thread.enter_wait();
// queue_lock = self.queue.lock();
if let Some(deadline) = deadline {
if machine::local_timer().timestamp()? > deadline {
let mut cursor = queue_lock.cursor_front_mut();
// if let Some(deadline) = deadline {
// if machine::local_timer().timestamp()? > deadline {
// let mut cursor = queue_lock.cursor_front_mut();
while let Some(&mut item) = cursor.current() {
if thread.id() == item {
cursor.remove_current();
break;
} else {
cursor.move_next();
}
}
// while let Some(&mut item) = cursor.current() {
// if thread.id() == item {
// cursor.remove_current();
// break;
// } else {
// cursor.move_next();
// }
// }
// return Err(Errno::TimedOut);
// }
// }
// }
return Err(Errno::TimedOut);
}
}
}
}
}
+36 -14
View File
@@ -1,9 +1,10 @@
//! System call argument ABI helpers
use crate::mem;
use crate::arch::intrin;
use crate::mem::{self, virt::table::Space};
use crate::proc::Process;
use core::alloc::Layout;
use libsys::error::Errno;
use crate::proc::Process;
// TODO _mut() versions checking whether pages are actually writable
@@ -12,13 +13,12 @@ macro_rules! invalid_memory {
warnln!($($args)+);
#[cfg(feature = "aggressive_syscall")]
{
todo!()
// use libsys::signal::Signal;
// use crate::proc::Thread;
use libsys::signal::Signal;
use crate::proc::Thread;
// let thread = Thread::current();
// let proc = thread.owner().unwrap();
// proc.enter_fault_signal(thread, Signal::SegmentationFault);
let thread = Thread::current();
let proc = thread.owner().unwrap();
proc.enter_fault_signal(thread, Signal::SegmentationFault);
}
return Err(Errno::InvalidArgument);
}
@@ -28,6 +28,7 @@ cfg_if! {
if #[cfg(target_arch = "aarch64")] {
#[inline(always)]
fn is_el0_accessible(virt: usize, write: bool) -> bool {
use core::arch::asm;
let mut res: usize;
unsafe {
if write {
@@ -41,8 +42,17 @@ cfg_if! {
} else {
#[inline(always)]
fn is_el0_accessible(virt: usize, write: bool) -> bool {
// TODO implement this
true
use crate::mem::virt::table::Entry;
let proc = Process::current();
proc.manipulate_space(|space| {
let entry = space.read_last_level(virt & !0xFFF);
if let Ok(entry) = entry {
// TODO is_user_readable()?
entry.is_user_writable() || !write
} else {
false
}
})
}
}
}
@@ -121,6 +131,18 @@ pub fn option_struct_mut<'a, T>(base: usize) -> Result<Option<&'a mut T>, Errno>
}
}
/// Checks given argument and interprets it as a `Option<&'a mut [T]>`
pub fn option_struct_buf_mut<'a, T>(
base: usize,
count: usize,
) -> Result<Option<&'a mut [T]>, Errno> {
if base == 0 {
Ok(None)
} else {
struct_buf_mut(base, count).map(Some)
}
}
/// Validates that the argument pointer is accessible for requested operation
/// for current process
pub fn validate_ptr(base: usize, len: usize, write: bool) -> Result<(), Errno> {
@@ -133,17 +155,17 @@ pub fn validate_ptr(base: usize, len: usize, write: bool) -> Result<(), Errno> {
}
let process = Process::current();
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) {
// It's possible a CoW page hasn't yet been cloned when trying
// a write access
let res = if write {
todo!();
process.manipulate_space(|space| {
// space.try_cow_copy(i * mem::PAGE_SIZE)?;
// Process::invalidate_asid(asid);
space.try_cow_copy(i * mem::PAGE_SIZE)?;
unsafe {
intrin::flush_tlb_virt(i * mem::PAGE_SIZE);
}
Ok(())
})
} else {
+318 -319
View File
@@ -1,18 +1,17 @@
//! System call implementation
// use crate::arch::{machine, platform::exception::ExceptionFrame};
use crate::arch::platform::ForkFrame;
use crate::arch::{machine, platform::ForkFrame};
use crate::debug::Level;
// use crate::dev::timer::TimestampSource;
use crate::dev::timer::TimestampSource;
use crate::fs::create_filesystem;
use crate::mem::{
phys::PageUsage,
virt::table::{AddressSpace, MapAttributes},
virt::table::{MapAttributes, Space},
};
use crate::proc::{self, elf, wait, Process, ProcessIo, Thread};
// use core::mem::size_of;
use core::mem::size_of;
use core::ops::DerefMut;
// use core::time::Duration;
use core::time::Duration;
use libsys::{
abi::SystemCall,
debug::TraceLevel,
@@ -79,132 +78,132 @@ fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
io.file(fd)?.borrow_mut().write(buf)
}
// SystemCall::Open => {
// 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)?;
// let opts = OpenFlags::from_bits(args[4] as u32).ok_or(Errno::InvalidArgument)?;
//
// 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
// };
//
// let file = io.ioctx().open(at, path, mode, opts)?;
// Ok(u32::from(io.place_file(file)?) as usize)
// }
// SystemCall::Close => {
// let proc = Process::current();
// let mut io = proc.io.lock();
// let fd = FileDescriptor::from(args[0] as u32);
//
// io.close_file(fd)?;
// Ok(0)
// }
// SystemCall::FileStatus => {
// let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
// let filename = arg::str_ref(args[1], args[2])?;
// let buf = arg::struct_mut::<Stat>(args[3])?;
// let flags = args[4] as u32;
//
// let proc = Process::current();
// let mut io = proc.io.lock();
// let stat =
// find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat()?;
// *buf = stat;
// Ok(0)
// }
// SystemCall::Ioctl => {
// let fd = FileDescriptor::from(args[0] as u32);
// let cmd = IoctlCmd::try_from(args[1] as u32)?;
//
// let proc = Process::current();
// let mut io = proc.io.lock();
//
// let node = io.file(fd)?.borrow().node().ok_or(Errno::InvalidFile)?;
// node.ioctl(cmd, args[2], args[3])
// }
// SystemCall::Select => {
// let rfds = arg::option_struct_mut::<FdSet>(args[0])?;
// let wfds = arg::option_struct_mut::<FdSet>(args[1])?;
// let timeout = if args[2] == 0 {
// None
// } else {
// Some(Duration::from_nanos(args[2] as u64))
// };
//
// wait::select(Thread::current(), rfds, wfds, timeout)
// }
// SystemCall::Access => {
// let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
// let path = arg::str_ref(args[1], args[2])?;
// let mode = AccessMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
// let flags = args[4] as u32;
//
// let proc = Process::current();
// let mut io = proc.io.lock();
//
// find_at_node(&mut io, at_fd, path, flags & AT_EMPTY_PATH != 0)?
// .check_access(io.ioctx(), mode)?;
// Ok(0)
// }
// SystemCall::ReadDirectory => {
// let proc = Process::current();
// let fd = FileDescriptor::from(args[0] as u32);
// let mut io = proc.io.lock();
// let buf = arg::struct_buf_mut::<DirectoryEntry>(args[1], args[2])?;
//
// io.file(fd)?.borrow_mut().readdir(buf)
// }
// SystemCall::GetUserId => {
// let proc = Process::current();
// let uid = proc.io.lock().uid();
// Ok(u32::from(uid) as usize)
// }
// SystemCall::GetGroupId => {
// let proc = Process::current();
// let gid = proc.io.lock().gid();
// Ok(u32::from(gid) as usize)
// }
// SystemCall::DuplicateFd => {
// let src = FileDescriptor::from(args[0] as u32);
// let dst = FileDescriptor::from_i32(args[1] as i32)?;
//
// let proc = Process::current();
// let mut io = proc.io.lock();
//
// let res = io.duplicate_file(src, dst)?;
//
// Ok(u32::from(res) as usize)
// }
// SystemCall::SetUserId => {
// let uid = UserId::from(args[0] as u32);
// let proc = Process::current();
// proc.io.lock().set_uid(uid)?;
// Ok(0)
// }
// SystemCall::SetGroupId => {
// let gid = GroupId::from(args[0] as u32);
// let proc = Process::current();
// proc.io.lock().set_gid(gid)?;
// Ok(0)
// }
// SystemCall::SetCurrentDirectory => {
// let path = arg::str_ref(args[0], args[1])?;
// let proc = Process::current();
// proc.io.lock().ioctx().chdir(path)?;
// Ok(0)
// }
// SystemCall::GetCurrentDirectory => {
// todo!()
// }
// SystemCall::Seek => {
// todo!()
// }
SystemCall::Open => {
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)?;
let opts = OpenFlags::from_bits(args[4] as u32).ok_or(Errno::InvalidArgument)?;
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
};
let file = io.ioctx().open(at, path, mode, opts)?;
Ok(u32::from(io.place_file(file)?) as usize)
}
SystemCall::Close => {
let proc = Process::current();
let mut io = proc.io.lock();
let fd = FileDescriptor::from(args[0] as u32);
io.close_file(fd)?;
Ok(0)
}
SystemCall::FileStatus => {
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
let filename = arg::str_ref(args[1], args[2])?;
let buf = arg::struct_mut::<Stat>(args[3])?;
let flags = args[4] as u32;
let proc = Process::current();
let mut io = proc.io.lock();
let stat =
find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat()?;
*buf = stat;
Ok(0)
}
SystemCall::Ioctl => {
let fd = FileDescriptor::from(args[0] as u32);
let cmd = IoctlCmd::try_from(args[1] as u32)?;
let proc = Process::current();
let mut io = proc.io.lock();
let node = io.file(fd)?.borrow().node().ok_or(Errno::InvalidFile)?;
node.ioctl(cmd, args[2], args[3])
}
SystemCall::Select => {
let rfds = arg::option_struct_mut::<FdSet>(args[0])?;
let wfds = arg::option_struct_mut::<FdSet>(args[1])?;
let timeout = if args[2] == 0 {
None
} else {
Some(Duration::from_nanos(args[2] as u64))
};
wait::select(Thread::current(), rfds, wfds, timeout)
}
SystemCall::Access => {
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
let path = arg::str_ref(args[1], args[2])?;
let mode = AccessMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
let flags = args[4] as u32;
let proc = Process::current();
let mut io = proc.io.lock();
find_at_node(&mut io, at_fd, path, flags & AT_EMPTY_PATH != 0)?
.check_access(io.ioctx(), mode)?;
Ok(0)
}
SystemCall::ReadDirectory => {
let proc = Process::current();
let fd = FileDescriptor::from(args[0] as u32);
let mut io = proc.io.lock();
let buf = arg::struct_buf_mut::<DirectoryEntry>(args[1], args[2])?;
io.file(fd)?.borrow_mut().readdir(buf)
}
SystemCall::GetUserId => {
let proc = Process::current();
let uid = proc.io.lock().uid();
Ok(u32::from(uid) as usize)
}
SystemCall::GetGroupId => {
let proc = Process::current();
let gid = proc.io.lock().gid();
Ok(u32::from(gid) as usize)
}
SystemCall::DuplicateFd => {
let src = FileDescriptor::from(args[0] as u32);
let dst = FileDescriptor::from_i32(args[1] as i32)?;
let proc = Process::current();
let mut io = proc.io.lock();
let res = io.duplicate_file(src, dst)?;
Ok(u32::from(res) as usize)
}
SystemCall::SetUserId => {
let uid = UserId::from(args[0] as u32);
let proc = Process::current();
proc.io.lock().set_uid(uid)?;
Ok(0)
}
SystemCall::SetGroupId => {
let gid = GroupId::from(args[0] as u32);
let proc = Process::current();
proc.io.lock().set_gid(gid)?;
Ok(0)
}
SystemCall::SetCurrentDirectory => {
let path = arg::str_ref(args[0], args[1])?;
let proc = Process::current();
proc.io.lock().ioctx().chdir(path)?;
Ok(0)
}
SystemCall::GetCurrentDirectory => {
todo!()
}
SystemCall::Seek => {
todo!()
}
SystemCall::MapMemory => {
let len = args[1];
if len == 0 || (len & 0xFFF) != 0 {
@@ -213,8 +212,7 @@ fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
let acc = MemoryAccess::from_bits(args[2] as u32).ok_or(Errno::InvalidArgument)?;
let _flags = MemoryAccess::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
let mut attrs =
MapAttributes::NOT_GLOBAL | MapAttributes::SHARE_OUTER | MapAttributes::USER_READ;
let mut attrs = MapAttributes::SHARE_OUTER | MapAttributes::USER_READ;
if !acc.contains(MemoryAccess::READ) {
return Err(Errno::NotImplemented);
}
@@ -222,7 +220,7 @@ fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
if acc.contains(MemoryAccess::EXEC) {
return Err(Errno::PermissionDenied);
}
attrs |= MapAttributes::USER_WRITE | MapAttributes::KERNEL_WRITE;
attrs |= MapAttributes::USER_WRITE;
} else {
attrs |= MapAttributes::USER_READ;
}
@@ -239,186 +237,187 @@ fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
space.allocate(0x100000000, 0xF00000000, len / 4096, attrs, usage)
})
}
// SystemCall::UnmapMemory => {
// let addr = args[0];
// let len = args[1];
//
// if addr == 0 || len == 0 || addr & 0xFFF != 0 || len & 0xFFF != 0 {
// return Err(Errno::InvalidArgument);
// }
//
// let proc = Process::current();
// proc.manipulate_space(move |space| space.free(addr, len / 4096))?;
// Ok(0)
// }
//
// // Process
// SystemCall::Clone => {
// let entry = args[0];
// let stack = args[1];
// let arg = args[2];
//
// Process::current()
// .new_user_thread(entry, stack, arg)
// .map(|e| u32::from(e) as usize)
// }
// SystemCall::Exec => {
// let filename = arg::str_ref(args[0], args[1])?;
// let argv = arg::struct_buf_ref::<&str>(args[2], args[3])?;
// // Validate each argument as well
// for item in argv.iter() {
// arg::validate_ptr(item.as_ptr() as usize, item.len(), false)?;
// }
// let node = {
// let proc = Process::current();
// let mut io = proc.io.lock();
// // TODO argv, envp array passing ABI?
// let node = io.ioctx().find(None, filename, true)?;
// drop(io);
// node
// };
// let file = node.open(OpenFlags::O_RDONLY)?;
// Process::execve(move |space| elf::load_elf(space, file), argv).unwrap();
// panic!();
// }
// SystemCall::Exit => {
// let status = ExitCode::from(args[0] as i32);
// let flags = args[1];
//
// if flags & (1 << 0) != 0 {
// Process::exit_thread(Thread::current(), status);
// } else {
// Process::current().exit(status);
// }
//
// unreachable!();
// }
// SystemCall::WaitPid => {
// // TODO special "pid" values
// let pid = Pid::try_from(args[0] as u32)?;
// let status = arg::struct_mut::<i32>(args[1])?;
//
// match Process::waitpid(pid) {
// Ok(exit) => {
// *status = i32::from(exit);
// Ok(0)
// }
// e => e.map(|e| i32::from(e) as usize),
// }
// }
// SystemCall::WaitTid => {
// let tid = Tid::from(args[0] as u32);
//
// match Thread::waittid(tid) {
// Ok(_) => Ok(0),
// _ => todo!(),
// }
// }
// SystemCall::GetPid => Ok(u32::from(Process::current().id()) as usize),
// SystemCall::GetTid => Ok(u32::from(Thread::current().id()) as usize),
// SystemCall::Sleep => {
// let rem_buf = arg::option_buf_ref(args[1], size_of::<u64>() * 2)?;
// let mut rem = Duration::new(0, 0);
// let res = wait::sleep(Duration::from_nanos(args[0] as u64), &mut rem);
// if res == Err(Errno::Interrupt) {
// warnln!("Sleep interrupted, {:?} remaining", rem);
// if rem_buf.is_some() {
// todo!()
// }
// }
// res.map(|_| 0)
// }
// SystemCall::SetSignalEntry => {
// Thread::current().set_signal_entry(args[0], args[1]);
// Ok(0)
// }
// SystemCall::SignalReturn => {
// Thread::current().return_from_signal();
// unreachable!();
// }
// SystemCall::SendSignal => {
// let target = SignalDestination::from(args[0] as isize);
// let signal = Signal::try_from(args[1] as u32)?;
//
// match target {
// SignalDestination::This => Process::current().set_signal(signal),
// SignalDestination::Process(pid) => Process::get(pid)
// .ok_or(Errno::DoesNotExist)?
// .set_signal(signal),
// _ => todo!(),
// };
// Ok(0)
// }
// SystemCall::Yield => {
// proc::switch();
// Ok(0)
// }
// SystemCall::GetSid => {
// // TODO handle kernel processes here?
// let pid = Pid::to_option(args[0] as u32);
// let current = Process::current();
// let proc = if let Some(pid) = pid {
// let proc = Process::get(pid).ok_or(Errno::DoesNotExist)?;
// if proc.sid() != current.sid() {
// return Err(Errno::PermissionDenied);
// }
// proc
// } else {
// current
// };
//
// Ok(u32::from(proc.sid()) as usize)
// }
// SystemCall::GetPgid => {
// // TODO handle kernel processes here?
// let pid = Pid::to_option(args[0] as u32);
// let current = Process::current();
// let proc = if let Some(pid) = pid {
// Process::get(pid).ok_or(Errno::DoesNotExist)?
// } else {
// current
// };
//
// Ok(u32::from(proc.pgid()) as usize)
// }
// SystemCall::GetPpid => Ok(u32::from(Process::current().ppid().unwrap()) as usize),
// SystemCall::SetSid => {
// let proc = Process::current();
// let mut io = proc.io.lock();
//
// if let Some(_ctty) = io.ctty() {
// todo!();
// }
//
// let id = proc.id();
// proc.set_sid(id);
// Ok(u32::from(id) as usize)
// }
// SystemCall::SetPgid => {
// let pid = Pid::to_option(args[0] as u32);
// let pgid = Pid::to_option(args[1] as u32);
//
// let current = Process::current();
// let proc = if let Some(_pid) = pid {
// todo!()
// } else {
// current
// };
//
// if let Some(_pgid) = pgid {
// todo!();
// } else {
// proc.set_pgid(proc.id());
// }
//
// Ok(u32::from(proc.pgid()) as usize)
// }
//
// // System
// SystemCall::GetCpuTime => {
// let time = machine::local_timer().timestamp()?;
// Ok(time.as_nanos() as usize)
// }
SystemCall::UnmapMemory => {
let addr = args[0];
let len = args[1];
if addr == 0 || len == 0 || addr & 0xFFF != 0 || len & 0xFFF != 0 {
return Err(Errno::InvalidArgument);
}
let proc = Process::current();
proc.manipulate_space(move |space| space.free(addr, len / 4096))?;
Ok(0)
}
// Process
SystemCall::Clone => {
let entry = args[0];
let stack = args[1];
let arg = args[2];
Process::current()
.new_user_thread(entry, stack, arg)
.map(|e| u32::from(e) as usize)
}
SystemCall::Exec => {
let filename = arg::str_ref(args[0], args[1])?;
let argv = arg::struct_buf_ref::<&str>(args[2], args[3])?;
// Validate each argument as well
for item in argv.iter() {
arg::validate_ptr(item.as_ptr() as usize, item.len(), false)?;
}
let node = {
let proc = Process::current();
let mut io = proc.io.lock();
// TODO argv, envp array passing ABI?
let node = io.ioctx().find(None, filename, true)?;
drop(io);
node
};
let file = node.open(OpenFlags::O_RDONLY)?;
Process::execve(move |space| elf::load_elf(space, file), argv).unwrap();
panic!();
}
SystemCall::Exit => {
let status = ExitCode::from(args[0] as i32);
let flags = args[1];
if flags & (1 << 0) != 0 {
Process::exit_thread(Thread::current(), status);
} else {
Process::current().exit(status);
}
unreachable!();
}
SystemCall::WaitPid => {
// TODO special "pid" values
let pid = Pid::try_from(args[0] as u32)?;
let status = arg::struct_mut::<i32>(args[1])?;
match Process::waitpid(pid) {
Ok(exit) => {
*status = i32::from(exit);
Ok(0)
}
e => e.map(|e| i32::from(e) as usize),
}
}
SystemCall::WaitTid => {
let tid = Tid::from(args[0] as u32);
match Thread::waittid(tid) {
Ok(_) => Ok(0),
_ => todo!(),
}
}
SystemCall::GetPid => Ok(u32::from(Process::current().id()) as usize),
SystemCall::GetTid => Ok(u32::from(Thread::current().id()) as usize),
SystemCall::Sleep => {
let rem_buf = arg::option_struct_buf_mut::<u64>(args[1], size_of::<u64>() * 2)?;
let mut rem = Duration::new(0, 0);
let res = wait::sleep(Duration::from_nanos(args[0] as u64), &mut rem);
if res == Err(Errno::Interrupt) {
warnln!("Sleep interrupted, {:?} remaining", rem);
if let Some(rem_buf) = rem_buf {
rem_buf[0] = rem.as_secs();
rem_buf[1] = rem.subsec_nanos() as u64;
}
}
res.map(|_| 0)
}
SystemCall::SetSignalEntry => {
Thread::current().set_signal_entry(args[0], args[1]);
Ok(0)
}
SystemCall::SignalReturn => {
Thread::current().return_from_signal();
unreachable!();
}
SystemCall::SendSignal => {
let target = SignalDestination::from(args[0] as isize);
let signal = Signal::try_from(args[1] as u32)?;
match target {
SignalDestination::This => Process::current().set_signal(signal),
SignalDestination::Process(pid) => Process::get(pid)
.ok_or(Errno::DoesNotExist)?
.set_signal(signal),
_ => todo!(),
};
Ok(0)
}
SystemCall::Yield => {
proc::switch();
Ok(0)
}
SystemCall::GetSid => {
// TODO handle kernel processes here?
let pid = Pid::to_option(args[0] as u32);
let current = Process::current();
let proc = if let Some(pid) = pid {
let proc = Process::get(pid).ok_or(Errno::DoesNotExist)?;
if proc.sid() != current.sid() {
return Err(Errno::PermissionDenied);
}
proc
} else {
current
};
Ok(u32::from(proc.sid()) as usize)
}
SystemCall::GetPgid => {
// TODO handle kernel processes here?
let pid = Pid::to_option(args[0] as u32);
let current = Process::current();
let proc = if let Some(pid) = pid {
Process::get(pid).ok_or(Errno::DoesNotExist)?
} else {
current
};
Ok(u32::from(proc.pgid()) as usize)
}
SystemCall::GetPpid => Ok(u32::from(Process::current().ppid().unwrap()) as usize),
SystemCall::SetSid => {
let proc = Process::current();
let mut io = proc.io.lock();
if let Some(_ctty) = io.ctty() {
todo!();
}
let id = proc.id();
proc.set_sid(id);
Ok(u32::from(id) as usize)
}
SystemCall::SetPgid => {
let pid = Pid::to_option(args[0] as u32);
let pgid = Pid::to_option(args[1] as u32);
let current = Process::current();
let proc = if let Some(_pid) = pid {
todo!()
} else {
current
};
if let Some(_pgid) = pgid {
todo!();
} else {
proc.set_pgid(proc.id());
}
Ok(u32::from(proc.pgid()) as usize)
}
// System
SystemCall::GetCpuTime => {
let time = machine::local_timer().timestamp()?;
Ok(time.as_nanos() as usize)
}
SystemCall::Mount => {
let target = arg::str_ref(args[0], args[1])?;
let options = arg::struct_ref::<MountOptions>(args[2])?;
@@ -434,7 +433,7 @@ fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
target_node.mount(root)?;
Ok(0)
},
}
// Debugging
SystemCall::DebugTrace => {
@@ -448,8 +447,8 @@ fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
Ok(args[1])
}
// // Handled elsewhere
// SystemCall::Fork => unreachable!(),
// Handled elsewhere
SystemCall::Fork => unreachable!(),
_ => panic!("Unimplemented: {:?}", num),
}
}
@@ -460,7 +459,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
let process = thread.owner().unwrap();
let result = _syscall(num, args);
if !thread.is_handling_signal() {
// process.handle_pending_signals();
process.handle_pending_signals();
}
result
}
+10 -3
View File
@@ -20,6 +20,8 @@ impl<T> InitOnce<T> {
}
}
/// Returns a reference to the initialized value, if one is present.
/// Returns [None] otherwise.
pub fn as_ref_option(&self) -> Option<&T> {
if self.is_initialized() {
Some(self.get())
@@ -37,8 +39,13 @@ impl<T> InitOnce<T> {
/// Returns the initialized value. Will panic if the value has not
/// yet been initialized.
#[allow(clippy::mut_from_ref)]
pub fn get(&self) -> &T {
assert!(self.is_initialized(), "Access to uninitialized InitOnce<T>");
#[track_caller]
pub fn get(&self) -> &mut T {
assert!(
self.is_initialized(),
"Access to uninitialized InitOnce<T>: {:?}",
core::panic::Location::caller()
);
unsafe { (*self.inner.get()).assume_init_mut() }
}
@@ -78,6 +85,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))
};
}
+2 -1
View File
@@ -23,6 +23,7 @@ pub enum SystemCall {
Seek = 17,
MapMemory = 18,
UnmapMemory = 19,
CreateDirectory = 20,
// Process manipulation
Fork = 32,
@@ -47,5 +48,5 @@ pub enum SystemCall {
GetCpuTime = 64,
Mount = 65,
// Debugging
DebugTrace = 128
DebugTrace = 128,
}
+2 -2
View File
@@ -1,5 +1,5 @@
use core::convert::TryFrom;
use crate::error::Errno;
use core::convert::TryFrom;
#[derive(Clone, Copy, Debug)]
#[repr(u32)]
@@ -19,7 +19,7 @@ impl TryFrom<u32> for IoctlCmd {
1 => Ok(Self::TtySetAttributes),
2 => Ok(Self::TtyGetAttributes),
3 => Ok(Self::TtySetPgrp),
_ => Err(Errno::InvalidArgument)
_ => Err(Errno::InvalidArgument),
}
}
}
+8 -11
View File
@@ -1,4 +1,3 @@
#![feature(asm, const_panic)]
#![no_std]
#[macro_use]
@@ -16,12 +15,12 @@ pub mod stat;
pub mod termios;
pub mod traits;
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct ProgramArgs {
pub argv: usize,
pub argc: usize,
pub storage: usize,
pub size: usize
pub size: usize,
}
// TODO utils
@@ -37,7 +36,7 @@ impl<const N: usize> FixedStr<N> {
pub const fn empty() -> Self {
Self {
len: 0,
data: [0; N]
data: [0; N],
}
}
@@ -50,9 +49,7 @@ impl<const N: usize> FixedStr<N> {
}
pub fn as_str(&self) -> &str {
unsafe {
core::str::from_utf8_unchecked(&self.data[..self.len])
}
unsafe { core::str::from_utf8_unchecked(&self.data[..self.len]) }
}
}
@@ -73,7 +70,7 @@ impl<const N: usize> fmt::Display for FixedStr<N> {
}
}
#[cfg(feature = "user")]
pub mod calls;
#[cfg(feature = "user")]
pub use calls::*;
// #[cfg(feature = "user")]
// pub mod calls;
// #[cfg(feature = "user")]
// pub use calls::*;
+5 -5
View File
@@ -1,5 +1,5 @@
use crate::error::Errno;
use crate::proc::{Pid, Pgid};
use crate::proc::{Pgid, Pid};
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u32)]
@@ -9,7 +9,7 @@ pub enum Signal {
FloatError = 8,
Kill = 9,
SegmentationFault = 11,
InvalidSystemCall = 31
InvalidSystemCall = 31,
}
#[derive(Clone, Copy, PartialEq, Debug)]
@@ -17,7 +17,7 @@ pub enum SignalDestination {
Group(Pgid),
Process(Pid),
All,
This
This,
}
impl From<isize> for SignalDestination {
@@ -40,7 +40,7 @@ impl From<SignalDestination> for isize {
SignalDestination::Process(pid) => u32::from(pid) as isize,
SignalDestination::Group(pgid) => -(u32::from(pgid) as isize),
SignalDestination::This => 0,
SignalDestination::All => -1
SignalDestination::All => -1,
}
}
}
@@ -57,7 +57,7 @@ impl TryFrom<u32> for Signal {
9 => Ok(Self::Kill),
11 => Ok(Self::SegmentationFault),
31 => Ok(Self::InvalidSystemCall),
_ => Err(Errno::InvalidArgument)
_ => Err(Errno::InvalidArgument),
}
}
}
+7 -3
View File
@@ -1,7 +1,7 @@
// TODO split up this file
use crate::error::Errno;
use core::str::FromStr;
use core::fmt;
use core::str::FromStr;
const AT_FDCWD: i32 = -2;
pub const AT_EMPTY_PATH: u32 = 1 << 16;
@@ -246,7 +246,11 @@ impl FileMode {
}
fn choose<T>(q: bool, a: T, b: T) -> T {
if q { a } else { b }
if q {
a
} else {
b
}
}
impl Default for FileMode {
@@ -265,7 +269,7 @@ impl fmt::Display for FileMode {
Self::S_IFCHR => 'c',
Self::S_IFDIR => 'd',
Self::S_IFREG => '-',
_ => '?'
_ => '?',
},
// User
choose(self.contains(Self::USER_READ), 'r', '-'),
+2 -2
View File
@@ -43,7 +43,7 @@ pub struct Termios {
pub iflag: TermiosIflag,
pub oflag: TermiosOflag,
pub lflag: TermiosLflag,
pub chars: TermiosChars
pub chars: TermiosChars,
}
impl TermiosChars {
@@ -70,7 +70,7 @@ impl Termios {
(1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5),
)
},
chars: TermiosChars::new()
chars: TermiosChars::new(),
}
}
+3 -3
View File
@@ -1,8 +1,8 @@
use core::alloc::{GlobalAlloc, Layout};
use core::mem::{size_of, MaybeUninit};
use core::ptr::null_mut;
use crate::sys::{sys_mmap, sys_munmap};
use libsys::{
calls::{sys_mmap, sys_munmap},
error::Errno,
proc::{MemoryAccess, MemoryMap},
};
@@ -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;
+43
View File
@@ -0,0 +1,43 @@
#[macro_export]
macro_rules! syscall {
($num:expr) => {{
let mut res: usize;
core::arch::asm!("svc #0", out("x0") res, in("x8") $num.repr(), options(nostack));
res
}};
($num:expr, $a0:expr) => {{
let mut res: usize = $a0;
core::arch::asm!("svc #0",
inout("x0") res,
in("x8") $num.repr(), options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr) => {{
let mut res: usize = $a0;
core::arch::asm!("svc #0",
inout("x0") res, in("x1") $a1,
in("x8") $num.repr(), options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr) => {{
let mut res: usize = $a0;
core::arch::asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x8") $num.repr(), options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {{
let mut res: usize = $a0;
core::arch::asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x8") $num.repr(), options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
let mut res: usize = $a0;
core::arch::asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x4") $a4, in("x8") $num.repr(), options(nostack));
res
}};
}
+7
View File
@@ -0,0 +1,7 @@
#[cfg(target_arch = "aarch64")]
#[macro_use]
pub mod aarch64;
#[cfg(target_arch = "x86_64")]
#[macro_use]
pub mod x86_64;
+45
View File
@@ -0,0 +1,45 @@
#[macro_export]
macro_rules! syscall {
($num:expr) => {{
let mut res: usize = $num.repr();
core::arch::asm!("syscall",
inout("rax") res,
options(nostack));
res
}};
($num:expr, $a0:expr) => {{
let mut res: usize = $num.repr();
core::arch::asm!("syscall",
inout("rax") res, in("rdi") $a0,
options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr) => {{
let mut res: usize = $num.repr();
core::arch::asm!("syscall",
inout("rax") res, in("rdi") $a0, in("rsi") $a1,
options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr) => {{
let mut res: usize = $num.repr();
core::arch::asm!("syscall",
inout("rax") res, in("rdi") $a0, in("rsi") $a1,
in("rdx") $a2, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {{
let mut res: usize = $num.repr();
core::arch::asm!("syscall",
inout("rax") res, in("rdi") $a0, in("rsi") $a1,
in("rdx") $a2, in("r10") $a3, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
let mut res: usize = $num.repr();
core::arch::asm!("syscall",
inout("rax") res, in("rdi") $a0, in("rsi") $a1,
in("rdx") $a2, in("r10") $a3, in("r8") $a4, options(nostack));
res
}};
}
+4 -4
View File
@@ -1,9 +1,9 @@
#[cfg(feature = "verbose")]
use crate::trace;
use alloc::vec::Vec;
use libsys::{
debug::TraceLevel,
ProgramArgs,
};
#[cfg(feature = "verbose")]
use libsys::debug::TraceLevel;
use libsys::ProgramArgs;
mod passwd;
pub use passwd::UserInfo;
+35 -25
View File
@@ -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::{
error::Errno,
stat::{GroupId, UserId},
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,45 @@ 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 +100,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)
}
+16 -13
View File
@@ -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::{error::Errno, FixedStr};
#[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,29 @@ 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 {
+2 -4
View File
@@ -1,8 +1,6 @@
use crate::io::{AsRawFd, Error, Read};
use libsys::{
calls::{sys_openat, sys_read, sys_close},
stat::{FileDescriptor, FileMode, OpenFlags},
};
use crate::sys::{sys_close, sys_openat, sys_read};
use libsys::stat::{FileDescriptor, FileMode, OpenFlags};
pub struct File {
fd: FileDescriptor,
+1 -3
View File
@@ -29,8 +29,6 @@ impl Error {
impl From<Errno> for Error {
fn from(e: Errno) -> Self {
Self {
repr: Repr::Os(e)
}
Self { repr: Repr::Os(e) }
}
}
+25 -14
View File
@@ -1,17 +1,17 @@
use libsys::{
calls::{sys_fstatat, sys_ioctl},
stat::{FileDescriptor, Stat},
ioctl::IoctlCmd,
error::Errno,
proc::Pid
};
use core::mem::size_of;
use crate::sys::{sys_fstatat, sys_ioctl};
use core::fmt;
use core::mem::size_of;
use libsys::{
error::Errno,
ioctl::IoctlCmd,
proc::Pid,
stat::{FileDescriptor, Stat},
};
mod error;
pub use error::{Error, ErrorKind};
mod writer;
pub use writer::{_print};
pub use writer::_print;
mod stdio;
pub use stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
@@ -33,7 +33,13 @@ pub fn tcgetpgrp(_fd: FileDescriptor) -> Result<Pid, Errno> {
}
pub fn tcsetpgrp(fd: FileDescriptor, pgid: Pid) -> Result<(), Errno> {
sys_ioctl(fd, IoctlCmd::TtySetPgrp, &pgid as *const _ as usize, size_of::<Pid>()).map(|_| ())
sys_ioctl(
fd,
IoctlCmd::TtySetPgrp,
&pgid as *const _ as usize,
size_of::<Pid>(),
)
.map(|_| ())
}
pub fn stat(pathname: &str) -> Result<Stat, Error> {
@@ -44,14 +50,17 @@ 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 +73,7 @@ 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)
}
+2 -4
View File
@@ -1,10 +1,8 @@
use crate::io::{Error, Read, Write};
use crate::sync::Mutex;
use crate::sys::{sys_read, sys_write};
use core::fmt;
use libsys::{
calls::{sys_read, sys_write},
stat::FileDescriptor,
};
use libsys::stat::FileDescriptor;
struct InputInner {
fd: FileDescriptor,

Some files were not shown because too many files have changed in this diff Show More