36 Commits

Author SHA1 Message Date
alnyan a82751c146 refactor: update to rust 1.60.0 2022-01-23 20:58:38 +02:00
alnyan fcbe412732 feature: simple sysfs 2021-12-09 09:02:28 +02:00
alnyan b4b99915ef feature: verbose feature for libusr 2021-12-05 23:45:24 +02:00
alnyan afa7e4cecb feature: simple pseudo devices 2021-12-05 21:58:03 +02:00
alnyan 94450e6537 fix: use u128 for nanos in timer 2021-12-05 21:13:13 +02:00
alnyan 2985f1429e feature: update Orange Pi 3 code 2021-12-05 21:12:56 +02:00
alnyan 2b160343b7 fix: invalidate TLB on syscall CoW 2021-12-05 21:12:29 +02:00
alnyan b97db3a0c4 feature: better login program 2021-12-02 13:02:45 +02:00
alnyan bd3d4e964d fix: enter signal handler AFTER syscall abort 2021-12-01 12:37:49 +02:00
alnyan a577b2dcc4 refactor: fix clippy warnings 2021-11-30 20:34:17 +02:00
alnyan d0681eb589 refactor: replace u32 with Tid type 2021-11-30 18:35:10 +02:00
alnyan e965c25181 refactor: better Pid type 2021-11-30 18:07:39 +02:00
alnyan 4ffbb8c115 refactor: fix warnings 2021-11-30 09:55:13 +02:00
alnyan 4c3374de36 feature: MapMemory and UnmapMemory system calls 2021-11-29 16:57:21 +02:00
alnyan 3ed41501cb feature: cat and hexd 2021-11-28 12:24:17 +02:00
alnyan cd71ee25ab refactor: better user dir structure 2021-11-28 11:50:59 +02:00
alnyan a7a0c8bf2c feature: login program 2021-11-28 11:46:55 +02:00
alnyan ed51f233ee fix: memcmp() was comparing in reverse lmao 2021-11-28 00:47:45 +02:00
alnyan 61a92920c2 feature: trace! levels 2021-11-25 12:02:25 +02:00
alnyan 47b67fa93c feature: passing args to execve() 2021-11-24 15:16:34 +02:00
alnyan 7f939543fe refactor: make Vnode::stat() return Stat 2021-11-23 18:01:48 +02:00
alnyan 564d10e1be feature: simple ls(1p) 2021-11-23 17:55:58 +02:00
alnyan a7d89158cb feature: Ctrl+C signal to foreground pgid 2021-11-23 14:16:37 +02:00
alnyan 349418ed36 feature: print elr on unresolved data aborts 2021-11-23 09:32:27 +02:00
alnyan fabf4e8d3f refactor: fix non-doc warnings 2021-11-22 15:42:43 +02:00
alnyan 4cfa1f2958 feature: faster single-page alloc 2021-11-22 14:02:29 +02:00
alnyan da36ecef13 fix: ptr validation did not work for CoW pages 2021-11-22 12:00:57 +02:00
alnyan 7c809f3b11 feature: extended user pointer validation 2021-11-21 14:01:48 +02:00
alnyan 1820009dee feature: "aggressive" syscall memory checking 2021-11-21 12:36:20 +02:00
alnyan bf1a215730 feature: better ABI for system call numbers 2021-11-21 12:26:11 +02:00
alnyan 3121cc9ba9 feature: fuzzy 2021-11-21 11:44:33 +02:00
alnyan 7c622a78f8 feature: thread::current() using tpidr_el0 2021-11-20 15:46:38 +02:00
alnyan 6eac5287a2 feature: shell exec 2021-11-20 13:54:06 +02:00
alnyan 87c13d3920 feature: thread join() with exit value 2021-11-19 16:38:38 +02:00
alnyan d582a9b58b feature: spawn for closures 2021-11-19 16:14:13 +02:00
alnyan adb95ac52e feature: add threads (WIP) 2021-11-17 13:05:51 +02:00
83 changed files with 4572 additions and 1071 deletions
Generated
+28 -3
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",
]
@@ -35,6 +35,17 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6419a5c75e40011b9fe0174db3fe24006ab122fbe1b7e9cc5974b338a755c76"
[[package]]
name = "enum-repr"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad30c9c0fa1aaf1ae5010dab11f1117b15d35faf62cda4bbbc53b9987950f18"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
@@ -58,7 +69,7 @@ checksum = "99a40cabc11c8258822a593f5c51f2d9f4923e715ca9e2a0630cf77ae15f390b"
dependencies = [
"endian-type-rs",
"fallible-iterator",
"memoffset",
"memoffset 0.5.6",
"num-derive",
"num-traits",
"rustc_version",
@@ -82,6 +93,7 @@ dependencies = [
"cfg-if",
"cortex-a",
"fdt-rs",
"fs-macros",
"kernel-macros",
"libsys",
"memfs",
@@ -111,6 +123,7 @@ name = "libsys"
version = "0.1.0"
dependencies = [
"bitflags",
"enum-repr",
]
[[package]]
@@ -119,6 +132,7 @@ version = "0.1.0"
dependencies = [
"lazy_static",
"libsys",
"memoffset 0.6.4",
]
[[package]]
@@ -139,6 +153,15 @@ dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
dependencies = [
"autocfg",
]
[[package]]
name = "num-derive"
version = "0.3.3"
@@ -250,6 +273,8 @@ checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80"
name = "user"
version = "0.1.0"
dependencies = [
"lazy_static",
"libsys",
"libusr",
]
+13 -4
View File
@@ -17,12 +17,12 @@ CARGO_COMMON_OPTS=
ifeq ($(PROFILE),release)
CARGO_COMMON_OPTS+=--release
endif
ifeq ($(VERBOSE),1)
CARGO_COMMON_OPTS+=--features verbose
endif
CARGO_BUILD_OPTS=$(CARGO_COMMON_OPTS) \
--target=../etc/$(ARCH)-$(MACH).json
ifeq ($(VERBOSE),1)
CARGO_BUILD_OPTS+=--features verbose
endif
ifneq ($(MACH),)
CARGO_BUILD_OPTS+=--features mach_$(MACH)
endif
@@ -92,9 +92,18 @@ initrd:
--target=../etc/$(ARCH)-osdev5.json \
-Z build-std=core,alloc,compiler_builtins \
$(CARGO_COMMON_OPTS)
mkdir -p $(O)/rootfs/bin
mkdir -p $(O)/rootfs/bin $(O)/rootfs/sbin $(O)/rootfs/dev $(O)/rootfs/etc $(O)/rootfs/sys
cp etc/initrd/passwd $(O)/rootfs/etc
cp etc/initrd/shadow $(O)/rootfs/etc
touch $(O)/rootfs/dev/.do_not_remove
touch $(O)/rootfs/sys/.do_not_remove
cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init
cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin
cp target/$(ARCH)-osdev5/$(PROFILE)/fuzzy $(O)/rootfs/bin
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)/login $(O)/rootfs/sbin
cd $(O)/rootfs && tar cf ../initrd.img `find -type f -printf "%P\n"`
ifeq ($(MACH),orangepi3)
$(MKIMAGE) \
+2
View File
@@ -0,0 +1,2 @@
root:0:0:root:/:/bin/shell
alnyan:1000:1000:alnyan:/:/bin/shell
+2
View File
@@ -0,0 +1,2 @@
root:toor
alnyan:
+15 -2
View File
@@ -31,8 +31,8 @@ fn impl_inode_fn<T: ToTokens>(name: &str, behavior: T) -> ImplItem {
}
},
"stat" => quote! {
fn stat(&mut self, _at: VnodeRef, _stat: &mut libsys::stat::Stat) ->
Result<(), libsys::error::Errno>
fn stat(&mut self, _at: VnodeRef) ->
Result<libsys::stat::Stat, libsys::error::Errno>
{
#behavior
}
@@ -94,6 +94,18 @@ fn impl_inode_fn<T: ToTokens>(name: &str, behavior: T) -> ImplItem {
#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),
})
}
@@ -126,6 +138,7 @@ pub fn auto_inode(attr: TokenStream, input: TokenStream) -> TokenStream {
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 {
+11 -2
View File
@@ -1,6 +1,6 @@
use crate::{BlockAllocator, Bvec, FileInode};
use alloc::boxed::Box;
use libsys::error::Errno;
use libsys::{error::Errno, stat::Stat};
use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
pub struct DirInode<A: BlockAllocator + Copy + 'static> {
@@ -15,7 +15,7 @@ impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
name: &str,
kind: VnodeKind,
) -> Result<VnodeRef, Errno> {
let vnode = Vnode::new(name, kind, Vnode::SEEKABLE);
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)))),
@@ -31,6 +31,15 @@ impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
fn remove(&mut self, _parent: VnodeRef, _name: &str) -> Result<(), Errno> {
Ok(())
}
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
let props = node.props();
Ok(Stat {
size: 0,
blksize: 4096,
mode: props.mode,
})
}
}
impl<A: BlockAllocator + Copy + 'static> DirInode<A> {
+7 -5
View File
@@ -35,11 +35,13 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
Ok(self.data.size())
}
fn stat(&mut self, _node: VnodeRef, stat: &mut Stat) -> Result<(), Errno> {
stat.size = self.data.size() as u64;
stat.blksize = 4096;
stat.mode = 0o755;
Ok(())
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
})
}
}
+10 -6
View File
@@ -1,7 +1,6 @@
#![feature(
const_fn_trait_bound,
const_mut_refs,
maybe_uninit_extra,
maybe_uninit_uninit_array
)]
#![no_std]
@@ -29,7 +28,7 @@ pub use block::{BlockAllocator, BlockRef};
mod bvec;
use bvec::Bvec;
mod tar;
use tar::TarIterator;
use tar::{TarIterator, Tar};
mod file;
use file::FileInode;
mod dir;
@@ -67,8 +66,10 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
Ok(res)
}
fn create_node_initial(self: Rc<Self>, name: &str, kind: VnodeKind) -> VnodeRef {
let node = Vnode::new(name, kind, Vnode::SEEKABLE);
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))),
@@ -111,7 +112,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 = self.clone().create_node_initial("", VnodeKind::Directory);
let root = Vnode::new("", VnodeKind::Directory, 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
for block in TarIterator::new(base, base.add(size)) {
@@ -120,7 +124,7 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
let parent = self.clone().make_path(root.clone(), dirname, true)?;
let node = self
.clone()
.create_node_initial(basename, block.node_kind());
.create_node_initial(basename, block);
assert_eq!(node.kind(), block.node_kind());
parent.attach(node);
}
+10 -1
View File
@@ -1,4 +1,4 @@
use libsys::error::Errno;
use libsys::{error::Errno, stat::FileMode};
use vfs::VnodeKind;
#[repr(packed)]
@@ -81,6 +81,15 @@ impl Tar {
}
}
pub fn mode(&self) -> FileMode {
let t = match self.node_kind() {
VnodeKind::Regular => FileMode::S_IFREG,
VnodeKind::Directory => FileMode::S_IFDIR,
_ => todo!()
};
FileMode::from_bits(from_octal(&self.mode) as u32).unwrap() | t
}
pub fn data(&self) -> &[u8] {
unsafe {
core::slice::from_raw_parts(
+1
View File
@@ -19,6 +19,7 @@ pub trait CharDevice {
/// Performs a TTY control request
fn ioctl(&self, cmd: IoctlCmd, ptr: usize, lim: usize) -> Result<usize, Errno>;
/// Returns `true` if the device is ready for an operation
fn is_ready(&self, write: bool) -> Result<bool, Errno>;
}
+64 -1
View File
@@ -1,9 +1,11 @@
use crate::{VnodeKind, VnodeRef};
use crate::{VnodeKind, VnodeRef, Vnode};
use alloc::rc::Rc;
use core::cell::RefCell;
use core::cmp::min;
use core::str::FromStr;
use libsys::{
error::Errno,
stat::DirectoryEntry,
traits::{Read, Seek, SeekDir, Write},
};
@@ -97,6 +99,11 @@ impl File {
/// File has to be closed on execve() calls
pub const CLOEXEC: u32 = 1 << 2;
/// Special position for cache-readdir: "." entry
pub const POS_CACHE_DOT: usize = usize::MAX - 1;
/// Special position for cache-readdir: ".." entry
pub const POS_CACHE_DOT_DOT: usize = usize::MAX;
/// Constructs a new file handle for a regular file
pub fn normal(vnode: VnodeRef, pos: usize, flags: u32) -> FileRef {
Rc::new(RefCell::new(Self {
@@ -119,12 +126,68 @@ impl File {
self.flags & Self::CLOEXEC != 0
}
/// Returns `true` if the file is ready for an operation
pub fn is_ready(&self, write: bool) -> Result<bool, Errno> {
match &self.inner {
FileInner::Normal(inner) => inner.vnode.is_ready(write),
_ => todo!(),
}
}
fn cache_readdir(inner: &mut NormalFile, entries: &mut [DirectoryEntry]) -> Result<usize, Errno> {
let mut count = entries.len();
let mut offset = 0usize;
if inner.pos == Self::POS_CACHE_DOT {
if count == 0 {
return Ok(offset);
}
entries[offset] = DirectoryEntry::from_str(".").unwrap();
inner.pos = Self::POS_CACHE_DOT_DOT;
offset += 1;
count -= 1;
}
if inner.pos == Self::POS_CACHE_DOT_DOT {
if count == 0 {
return Ok(offset);
}
entries[offset] = DirectoryEntry::from_str("..").unwrap();
inner.pos = 0;
offset += 1;
count -= 1;
}
if count == 0 {
return Ok(offset);
}
let count = inner.vnode.for_each_entry(inner.pos, count, |i, e| {
entries[offset + i] = DirectoryEntry::from_str(e.name()).unwrap();
});
inner.pos += count;
Ok(offset + count)
}
/// Reads directory entries into the target buffer
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);
if inner.vnode.flags() & Vnode::CACHE_READDIR != 0 {
Self::cache_readdir(inner, entries)
} else {
todo!();
}
},
_ => todo!(),
}
}
}
impl Drop for File {
+23 -2
View File
@@ -1,8 +1,8 @@
use crate::{FileRef, VnodeKind, VnodeRef};
use libsys::{
error::Errno,
stat::{OpenFlags, FileMode},
path::{path_component_left, path_component_right},
stat::{FileMode, GroupId, OpenFlags, UserId},
};
/// I/O context structure
@@ -10,13 +10,19 @@ use libsys::{
pub struct Ioctx {
root: VnodeRef,
cwd: VnodeRef,
/// Process user ID
pub uid: UserId,
/// Process group ID
pub gid: GroupId,
}
impl Ioctx {
/// Creates a new I/O context with given root node
pub fn new(root: VnodeRef) -> Self {
pub fn new(root: VnodeRef, uid: UserId, gid: GroupId) -> Self {
Self {
cwd: root.clone(),
uid,
gid,
root,
}
}
@@ -41,6 +47,11 @@ impl Ioctx {
}
}
while let Some(target) = at.target() {
assert!(at.kind() == VnodeKind::Directory);
at = target;
}
if element.is_empty() && rest.is_empty() {
return Ok(at);
}
@@ -113,6 +124,16 @@ impl Ioctx {
node.open(opts)
}
/// 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);
}
self.cwd = node;
Ok(())
}
}
#[cfg(test)]
+1 -1
View File
@@ -1,6 +1,6 @@
//! Virtual filesystem API and facilities
#![warn(missing_docs)]
#![feature(destructuring_assignment, const_fn_trait_bound)]
#![feature(const_fn_trait_bound)]
#![no_std]
#[cfg(test)]
+137 -35
View File
@@ -1,11 +1,11 @@
use crate::{File, FileRef, Filesystem};
use crate::{File, FileRef, Filesystem, Ioctx};
use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec};
use core::cell::{RefCell, RefMut};
use core::cell::{Ref, RefCell, RefMut};
use core::fmt;
use libsys::{
error::Errno,
ioctl::IoctlCmd,
stat::{FileMode, OpenFlags, Stat},
stat::{AccessMode, DirectoryEntry, FileMode, OpenFlags, Stat},
};
/// Convenience type alias for [Rc<Vnode>]
@@ -31,7 +31,8 @@ pub(crate) struct TreeNode {
/// File property cache struct
pub struct VnodeProps {
mode: FileMode,
/// Node permissions and type
pub mode: FileMode,
}
/// Virtual filesystem node struct, generalizes access to
@@ -74,12 +75,21 @@ pub trait VnodeImpl {
/// 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, stat: &mut Stat) -> Result<(), Errno>;
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
@@ -97,6 +107,11 @@ impl Vnode {
/// be seeked to arbitrary offsets
pub const SEEKABLE: u32 = 1 << 0;
/// If set, readdir() uses only in-memory node tree
pub const CACHE_READDIR: u32 = 1 << 1;
/// If set, stat() uses only in-memory stat data
pub const CACHE_STAT: u32 = 1 << 2;
/// 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 {
@@ -122,6 +137,16 @@ impl Vnode {
&self.name
}
/// Returns a borrowed reference to cached file properties
pub fn props_mut(&self) -> RefMut<VnodeProps> {
self.props.borrow_mut()
}
/// Returns a borrowed reference to cached file properties
pub fn props(&self) -> Ref<VnodeProps> {
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);
@@ -158,6 +183,12 @@ impl Vnode {
self.kind
}
/// Returns flags of the vnode
#[inline(always)]
pub const fn flags(&self) -> u32 {
self.flags
}
// Tree operations
/// Attaches `child` vnode to `self` in in-memory tree. NOTE: does not
@@ -230,14 +261,35 @@ impl Vnode {
.cloned()
}
pub(crate) fn for_each_entry<F: FnMut(usize, &VnodeRef)>(
&self,
offset: usize,
limit: usize,
mut f: F,
) -> usize {
assert!(self.is_directory());
let mut count = 0;
for (index, item) in self
.tree
.borrow()
.children
.iter()
.skip(offset)
.take(limit)
.enumerate()
{
f(index, item);
count += 1;
}
count
}
/// 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> {
if let Some(node) = self.lookup(name) {
return Ok(node);
}
if let Some(ref mut data) = *self.data() {
Ok(node)
} else if let Some(ref mut data) = *self.data() {
let vnode = data.lookup(self.clone(), name)?;
if let Some(fs) = self.fs() {
vnode.set_fs(fs);
@@ -303,22 +355,36 @@ impl Vnode {
/// Opens a vnode for access
pub fn open(self: &VnodeRef, flags: OpenFlags) -> Result<FileRef, Errno> {
if self.kind == VnodeKind::Directory {
return Err(Errno::IsADirectory);
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!(),
}
}
let mut open_flags = 0;
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 let Some(ref mut data) = *self.data() {
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 {
@@ -328,7 +394,9 @@ impl Vnode {
/// Closes a vnode
pub fn close(self: &VnodeRef) -> Result<(), Errno> {
if let Some(ref mut data) = *self.data() {
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)
@@ -338,10 +406,8 @@ impl Vnode {
/// 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 {
return Err(Errno::IsADirectory);
}
if let Some(ref mut data) = *self.data() {
Err(Errno::IsADirectory)
} else if let Some(ref mut data) = *self.data() {
data.read(self.clone(), pos, buf)
} else {
Err(Errno::NotImplemented)
@@ -351,10 +417,8 @@ impl Vnode {
/// Writes data from `buf` to offset `pos`
pub fn write(self: &VnodeRef, pos: usize, buf: &[u8]) -> Result<usize, Errno> {
if self.kind == VnodeKind::Directory {
return Err(Errno::IsADirectory);
}
if let Some(ref mut data) = *self.data() {
Err(Errno::IsADirectory)
} else if let Some(ref mut data) = *self.data() {
data.write(self.clone(), pos, buf)
} else {
Err(Errno::NotImplemented)
@@ -364,10 +428,8 @@ impl Vnode {
/// Resizes the vnode data
pub fn truncate(self: &VnodeRef, size: usize) -> Result<(), Errno> {
if self.kind != VnodeKind::Regular {
return Err(Errno::IsADirectory);
}
if let Some(ref mut data) = *self.data() {
Err(Errno::IsADirectory)
} else if let Some(ref mut data) = *self.data() {
data.truncate(self.clone(), size)
} else {
Err(Errno::NotImplemented)
@@ -384,9 +446,16 @@ impl Vnode {
}
/// Reports file status
pub fn stat(self: &VnodeRef, stat: &mut Stat) -> Result<(), Errno> {
if let Some(ref mut data) = *self.data() {
data.stat(self.clone(), stat)
pub fn stat(self: &VnodeRef) -> Result<Stat, Errno> {
if self.flags & Self::CACHE_STAT != 0 {
let props = self.props();
Ok(Stat {
blksize: 0,
size: 0,
mode: props.mode,
})
} else if let Some(ref mut data) = *self.data() {
data.stat(self.clone())
} else {
Err(Errno::NotImplemented)
}
@@ -401,6 +470,7 @@ impl Vnode {
}
}
/// 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)
@@ -408,6 +478,38 @@ impl Vnode {
Err(Errno::NotImplemented)
}
}
/// Checks if given [Ioctx] has `access` permissions to the vnode
pub fn check_access(&self, _ioctx: &Ioctx, access: AccessMode) -> Result<(), Errno> {
let props = self.props.borrow();
let mode = props.mode;
if access.contains(AccessMode::F_OK) {
if access.intersects(AccessMode::R_OK | AccessMode::W_OK | AccessMode::X_OK) {
return Err(Errno::InvalidArgument);
}
} else {
if access.contains(AccessMode::F_OK) {
return Err(Errno::InvalidArgument);
}
// Check user
if access.contains(AccessMode::R_OK) && !mode.contains(FileMode::USER_READ) {
return Err(Errno::PermissionDenied);
}
if access.contains(AccessMode::W_OK) && !mode.contains(FileMode::USER_WRITE) {
return Err(Errno::PermissionDenied);
}
if access.contains(AccessMode::X_OK) && !mode.contains(FileMode::USER_EXEC) {
return Err(Errno::PermissionDenied);
}
// TODO check group
// TODO check other
}
Ok(())
}
}
impl fmt::Debug for Vnode {
+4 -1
View File
@@ -18,14 +18,17 @@ tock-registers = "0.7.x"
fdt-rs = { version = "0.x.x", default-features = false }
bitflags = "^1.3.0"
kernel-macros = { path = "macros" }
fs-macros = { path = "../fs/macros" }
[target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "6.x.x" }
cortex-a = { version = "7.0.x" }
[features]
default = ["aggressive_syscall"]
pl011 = []
pl031 = []
verbose = []
aggressive_syscall = []
mach_qemu = ["pl011", "pl031"]
mach_orangepi3 = []
-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
+17 -13
View File
@@ -4,13 +4,15 @@ use crate::arch::{
aarch64::reg::{CNTKCTL_EL1, CPACR_EL1},
machine,
};
use core::arch::global_asm;
use crate::config::{ConfigKey, CONFIG};
use crate::dev::{
fdt::{find_prop, DeviceTree},
irq::IntSource,
Device,
};
use crate::fs::devfs;
use crate::fs::{devfs, sysfs};
use crate::dev::pseudo;
use libsys::error::Errno;
//use crate::debug::Level;
use crate::mem::{
@@ -23,22 +25,16 @@ use cortex_a::asm::barrier::{self, dsb, isb};
use cortex_a::registers::{SCTLR_EL1, VBAR_EL1};
use tock_registers::interfaces::{ReadWriteable, Writeable};
fn init_device_tree(fdt_base_phys: usize) -> Result<(), Errno> {
fn init_device_tree(fdt_base_phys: usize) -> Result<Option<DeviceTree>, Errno> {
use fdt_rs::prelude::*;
let fdt = if fdt_base_phys != 0 {
DeviceTree::from_phys(fdt_base_phys + 0xFFFFFF8000000000)?
} else {
warnln!("No FDT present");
return Ok(());
return Ok(None);
};
#[cfg(feature = "verbose")]
{
use crate::debug::Level;
fdt.dump(Level::Debug);
}
let mut cfg = CONFIG.lock();
if let Some(chosen) = fdt.node_by_path("/chosen") {
@@ -56,7 +52,7 @@ fn init_device_tree(fdt_base_phys: usize) -> Result<(), Errno> {
}
}
Ok(())
Ok(Some(fdt))
}
#[no_mangle]
@@ -87,7 +83,7 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
// Enable MMU
virt::enable().expect("Failed to initialize virtual memory");
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
@@ -102,9 +98,19 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
}
devfs::init();
sysfs::init();
machine::init_board().unwrap();
#[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();
infoln!("Machine init finished");
unsafe {
@@ -115,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
+4 -23
View File
@@ -32,27 +32,6 @@ __aa64_ctx_enter_kernel:
eret
__aa64_ctx_enter_from_fork:
// stack.push(frame.x[18]);
// stack.push(frame.x[17]);
// stack.push(frame.x[16]);
// stack.push(frame.x[15]);
// stack.push(frame.x[14]);
// stack.push(frame.x[13]);
// stack.push(frame.x[12]);
// stack.push(frame.x[11]);
// stack.push(frame.x[10]);
// stack.push(frame.x[9]);
// stack.push(frame.x[8]);
// stack.push(frame.x[7]);
// stack.push(frame.x[6]);
// stack.push(frame.x[5]);
// stack.push(frame.x[4]);
// stack.push(frame.x[3]);
// stack.push(frame.x[2]);
// stack.push(frame.x[1]);
// stack.push(frame.elr_el1 as usize);
// stack.push(frame.sp_el0 as usize);
ldp x0, x1, [sp, #16 * 0]
msr sp_el0, x0
msr elr_el1, x1
@@ -82,7 +61,8 @@ __aa64_ctx_switch:
stp x27, x28, [sp, #16 * 4]
stp x29, x30, [sp, #16 * 5]
mrs x19, TTBR0_EL1
stp x19, xzr, [sp, #16 * 6]
mrs x20, TPIDR_EL0
stp x19, x20, [sp, #16 * 6]
mov x19, sp
str x19, [x1]
@@ -90,8 +70,9 @@ __aa64_ctx_switch_to:
ldr x0, [x0]
mov sp, x0
ldp x19, xzr, [sp, #16 * 6]
ldp x19, x20, [sp, #16 * 6]
msr TTBR0_EL1, x19
msr TPIDR_EL0, x20
ldp x19, x20, [sp, #16 * 0]
ldp x21, x22, [sp, #16 * 1]
ldp x23, x24, [sp, #16 * 2]
+16 -15
View File
@@ -6,6 +6,7 @@ use crate::mem::{
phys::{self, PageUsage},
};
use core::mem::size_of;
use core::arch::global_asm;
struct Stack {
bp: usize,
@@ -67,7 +68,7 @@ impl Context {
stack.push(frame.sp_el0 as usize);
// Setup common
stack.push(0);
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
@@ -96,7 +97,7 @@ impl Context {
stack.push(entry);
stack.push(arg);
stack.push(/* ttbr0 */ 0);
stack.push(0);
stack.push(ustack);
stack.setup_common(__aa64_ctx_enter_user as usize, ttbr0);
@@ -176,20 +177,20 @@ impl Stack {
}
pub fn setup_common(&mut self, entry: usize, ttbr: usize) {
self.push(0);
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) {
+44 -28
View File
@@ -4,11 +4,12 @@ use crate::arch::machine;
use crate::debug::Level;
use crate::dev::irq::{IntController, IrqContext};
use crate::mem;
use crate::proc::{sched, Process};
use crate::proc::{sched, Process, Thread};
use crate::syscall;
use cortex_a::registers::{ESR_EL1, FAR_EL1};
use libsys::{abi, signal::Signal};
use libsys::{abi::SystemCall, signal::Signal, error::Errno};
use tock_registers::interfaces::Readable;
use core::arch::global_asm;
/// Trapped SIMD/FP functionality
pub const EC_FP_TRAP: u64 = 0b000111;
@@ -88,56 +89,71 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
match err_code {
EC_DATA_ABORT_EL0 | EC_DATA_ABORT_ELX => {
let far = FAR_EL1.get() as usize;
let iss = esr & 0x1FFFFFF;
if far < mem::KERNEL_OFFSET && sched::is_ready() {
let proc = Process::current();
if iss & (1 << 6) != 0 && far < mem::KERNEL_OFFSET && sched::is_ready() {
let thread = Thread::current();
let proc = thread.owner().unwrap();
let asid = proc.asid();
if proc
.manipulate_space(|space| space.try_cow_copy(far))
.is_err()
{
let res = proc.manipulate_space(|space| {
space.try_cow_copy(far)?;
Process::invalidate_asid(asid);
Result::<(), Errno>::Ok(())
});
if res.is_err() {
// Kill program
errorln!("Data abort from {:#x}", exc.elr_el1);
dump_data_abort(Level::Error, esr, far as u64);
proc.enter_signal(Signal::SegmentationFault);
}
unsafe {
use cortex_a::registers::TTBR0_EL1;
let ttbr = TTBR0_EL1.get() as usize;
let asid = (ttbr >> 48) & 0xFF;
asm!("tlbi aside1, {}", in(reg) (asid << 48));
proc.enter_fault_signal(thread, Signal::SegmentationFault);
}
return;
}
errorln!("Unresolved data abort");
errorln!("Data abort from {:#x}", exc.elr_el1);
dump_data_abort(Level::Error, esr, far as u64);
}
EC_SVC_AA64 => {
unsafe {
if exc.x[8] == abi::SYS_FORK {
match syscall::sys_fork(exc) {
Ok(pid) => exc.x[0] = pid.value() as usize,
Err(err) => {
exc.x[0] = err.to_negative_isize() as usize;
}
}
return;
}
let num = SystemCall::from_repr(exc.x[8]);
if num.is_none() {
todo!();
}
let num = num.unwrap();
match syscall::syscall(exc.x[8], &exc.x[..6]) {
Ok(val) => exc.x[0] = val,
if num == SystemCall::Fork {
match unsafe { syscall::sys_fork(exc) } {
Ok(pid) => exc.x[0] = u32::from(pid) as usize,
Err(err) => {
exc.x[0] = err.to_negative_isize() as usize;
}
}
return;
}
match syscall::syscall(num, &exc.x[..6]) {
Ok(val) => exc.x[0] = val,
Err(err) => {
exc.x[0] = err.to_negative_isize() as usize;
}
}
return;
}
_ => {}
}
if sched::is_ready() {
let thread = Thread::current();
errorln!(
"Unhandled exception in thread {:?}, {:?}",
thread.id(),
thread.owner().map(|e| e.id())
);
}
errorln!(
"Unhandled exception at ELR={:#018x}, ESR={:#010x}",
exc.elr_el1,
+2 -8
View File
@@ -28,7 +28,6 @@ pub struct Gic {
gicd: InitOnce<Gicd>,
gicd_base: usize,
gicc_base: usize,
scheduler_irq: IrqNumber,
table: IrqSafeSpinLock<[Option<&'static (dyn IntSource + Sync)>; MAX_IRQ]>,
}
@@ -86,9 +85,7 @@ impl IntController for Gic {
return;
}
if self.scheduler_irq.0 == irq_number {
gicc.clear_irq(irq_number as u32, ic);
}
gicc.clear_irq(irq_number as u32, ic);
{
let table = self.table.lock();
@@ -100,8 +97,6 @@ impl IntController for Gic {
}
}
}
gicc.clear_irq(irq_number as u32, ic);
}
fn register_handler(
@@ -128,13 +123,12 @@ impl Gic {
/// # Safety
///
/// Does not perform `gicd_base` and `gicc_base` validation.
pub const unsafe fn new(gicd_base: usize, gicc_base: usize, scheduler_irq: IrqNumber) -> Self {
pub const unsafe fn new(gicd_base: usize, gicc_base: usize) -> Self {
Self {
gicc: InitOnce::new(),
gicd: InitOnce::new(),
gicd_base,
gicc_base,
scheduler_irq,
table: IrqSafeSpinLock::new([None; MAX_IRQ]),
}
}
@@ -96,4 +96,4 @@ static UART0: Uart = unsafe { Uart::new(UART0_BASE, IrqNumber::new(32)) };
static LOCAL_TIMER: GenericTimer = GenericTimer::new(LOCAL_TIMER_IRQ);
pub(super) static GPIO: Gpio = unsafe { Gpio::new(PIO_BASE) };
static RTC: Rtc = unsafe { Rtc::new(RTC_BASE, RTC_IRQ) };
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE, LOCAL_TIMER_IRQ) };
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE) };
+1 -1
View File
@@ -78,6 +78,6 @@ pub fn intc() -> &'static impl IntController<IrqNumber = IrqNumber> {
static UART0: Pl011 = unsafe { Pl011::new(UART0_BASE, UART0_IRQ) };
static RTC: Pl031 = unsafe { Pl031::new(RTC_BASE, RTC_IRQ) };
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE, LOCAL_TIMER_IRQ) };
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE) };
static PCIE: GenericPcieHost = unsafe { GenericPcieHost::new(ECAM_BASE, 8) };
static LOCAL_TIMER: GenericTimer = GenericTimer::new(LOCAL_TIMER_IRQ);
+1
View File
@@ -2,6 +2,7 @@
use cortex_a::registers::DAIF;
use tock_registers::interfaces::{Readable, Writeable};
use core::arch::asm;
pub mod boot;
pub mod context;
@@ -1,6 +1,8 @@
//! CNTKCTL_EL1 register
#![allow(missing_docs)]
use core::arch::asm;
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields,
+1
View File
@@ -1,6 +1,7 @@
//! CPACR_EL1 register
#![allow(missing_docs)]
use core::arch::asm;
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields,
+8 -4
View File
@@ -1,7 +1,9 @@
//! ARM generic timer implementation
use crate::arch::machine::{self, IrqNumber};
use crate::proc;
use crate::dev::{
pseudo,
irq::{IntController, IntSource},
timer::TimestampSource,
Device,
@@ -34,9 +36,9 @@ impl IntSource for GenericTimer {
fn handle_irq(&self) -> Result<(), Errno> {
CNTP_TVAL_EL0.set(TIMER_TICK);
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
use crate::proc;
proc::wait::tick();
proc::switch();
pseudo::RANDOM.set_state(CNTPCT_EL0.get() as u32);
Ok(())
}
@@ -50,9 +52,11 @@ impl IntSource for GenericTimer {
impl TimestampSource for GenericTimer {
fn timestamp(&self) -> Result<Duration, Errno> {
let cnt = CNTPCT_EL0.get() * 1_000_000_000;
let frq = CNTFRQ_EL0.get();
Ok(Duration::from_nanos(cnt / frq))
let cnt = (CNTPCT_EL0.get() as u128) * 1_000_000_000u128;
let frq = CNTFRQ_EL0.get() as u128;
let secs = ((cnt / frq) / 1_000_000_000) as u64;
let nanos = ((cnt / frq) % 1_000_000_000) as u32;
Ok(Duration::new(secs, nanos))
}
}
+44 -10
View File
@@ -12,19 +12,51 @@
//! * [errorln!]
use crate::dev::serial::SerialDevice;
use libsys::{debug::TraceLevel, error::Errno};
use core::convert::TryFrom;
use core::fmt;
pub static LEVEL: Level = Level::Debug;
/// Kernel logging levels
#[derive(Clone, Copy, PartialEq)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[repr(u32)]
pub enum Level {
/// Debugging information
Debug,
Debug = 1,
/// General informational messages
Info,
Info = 2,
/// Non-critical warnings
Warn,
Warn = 3,
/// Critical errors
Error,
Error = 4,
}
impl TryFrom<u32> for Level {
type Error = Errno;
#[inline(always)]
fn try_from(l: u32) -> Result<Level, Errno> {
match l {
1 => Ok(Level::Debug),
2 => Ok(Level::Info),
3 => Ok(Level::Warn),
4 => Ok(Level::Error),
_ => Err(Errno::InvalidArgument)
}
}
}
impl From<TraceLevel> for Level {
#[inline(always)]
fn from(l: TraceLevel) -> Self {
match l {
TraceLevel::Debug => Self::Debug,
TraceLevel::Info => Self::Info,
TraceLevel::Warn => Self::Warn,
TraceLevel::Error => Self::Error,
}
}
}
struct SerialOutput<T: 'static + SerialDevice> {
@@ -101,13 +133,15 @@ macro_rules! errorln {
}
#[doc(hidden)]
pub fn _debug(_level: Level, args: fmt::Arguments) {
pub fn _debug(level: Level, args: fmt::Arguments) {
use crate::arch::machine;
use fmt::Write;
SerialOutput {
inner: machine::console(),
if level >= LEVEL {
SerialOutput {
inner: machine::console(),
}
.write_fmt(args)
.ok();
}
.write_fmt(args)
.ok();
}
+1
View File
@@ -11,6 +11,7 @@ pub mod rtc;
pub mod sd;
pub mod serial;
pub mod timer;
pub mod pseudo;
pub mod tty;
/// Generic device trait
+102
View File
@@ -0,0 +1,102 @@
use crate::arch::machine::{self, IrqNumber};
use crate::dev::{
irq::{IntController, IntSource},
serial::SerialDevice,
tty::{CharRing, TtyDevice},
Device,
};
use crate::mem::virt::DeviceMemoryIo;
use crate::sync::IrqSafeSpinLock;
use crate::util::InitOnce;
use libsys::{error::Errno, ioctl::IoctlCmd};
use core::sync::atomic::{AtomicU32, Ordering};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use vfs::CharDevice;
pub struct Random {
state: AtomicU32
}
pub struct Zero;
impl Device for Random {
fn name(&self) -> &'static str {
"Pseudo-random device"
}
unsafe fn enable(&self) -> Result<(), Errno> {
Ok(())
}
}
impl CharDevice for Random {
fn read(&self, _blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
for byte in data.iter_mut() {
*byte = self.read_single() as u8;
}
Ok(data.len())
}
fn write(&self, _blocking: bool, _data: &[u8]) -> Result<usize, Errno> {
Ok(0)
}
fn is_ready(&self, _write: bool) -> Result<bool, Errno> {
Ok(true)
}
fn ioctl(&self, _cmd: IoctlCmd, _ptr: usize, _lim: usize) -> Result<usize, Errno> {
Err(Errno::InvalidArgument)
}
}
impl Device for Zero {
fn name(&self) -> &'static str {
"Zero device"
}
unsafe fn enable(&self) -> Result<(), Errno> {
Ok(())
}
}
impl CharDevice for Zero {
fn read(&self, _blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
data.fill(0);
Ok(data.len())
}
fn write(&self, _blocking: bool, _data: &[u8]) -> Result<usize, Errno> {
Ok(0)
}
fn is_ready(&self, _write: bool) -> Result<bool, Errno> {
Ok(true)
}
fn ioctl(&self, _cmd: IoctlCmd, _ptr: usize, _lim: usize) -> Result<usize, Errno> {
Err(Errno::InvalidArgument)
}
}
impl Random {
pub fn set_state(&self, state: u32) {
self.state.store(state, Ordering::Release);
}
pub fn read_single(&self) -> u32 {
let mut x = self.state.load(Ordering::Acquire);
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state.store(x, Ordering::Release);
x
}
}
pub static RANDOM: Random = Random { state: AtomicU32::new(0) };
pub static ZERO: Zero = Zero;
+30 -6
View File
@@ -1,14 +1,16 @@
//! Teletype (TTY) device facilities
use crate::dev::serial::SerialDevice;
use crate::proc::wait::{Wait, WAIT_SELECT};
use crate::proc::{Process, wait::{Wait, WAIT_SELECT}};
use crate::sync::IrqSafeSpinLock;
use libsys::error::Errno;
use libsys::{
termios::{Termios, TermiosIflag, TermiosLflag, TermiosOflag},
proc::Pid,
signal::Signal,
ioctl::IoctlCmd
};
use core::mem::size_of;
use crate::syscall::arg::validate_user_ptr_struct;
use crate::syscall::arg;
#[derive(Debug)]
struct CharRingInner<const N: usize> {
@@ -16,6 +18,7 @@ struct CharRingInner<const N: usize> {
wr: usize,
data: [u8; N],
flags: u8,
fg_pgid: Option<Pid>,
}
/// Ring buffer for TTYs
@@ -31,6 +34,7 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
/// Returns a reference to character device's ring buffer
fn ring(&self) -> &CharRing<N>;
/// Returns `true` if the TTY is ready for an operation
fn is_ready(&self, write: bool) -> Result<bool, Errno> {
let ring = self.ring();
if write {
@@ -45,15 +49,20 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
match cmd {
IoctlCmd::TtyGetAttributes => {
// TODO validate size
let res = validate_user_ptr_struct::<Termios>(ptr)?;
let res = arg::struct_mut::<Termios>(ptr)?;
*res = self.ring().config.lock().clone();
Ok(size_of::<Termios>())
},
IoctlCmd::TtySetAttributes => {
let src = validate_user_ptr_struct::<Termios>(ptr)?;
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)
}
}
@@ -110,6 +119,19 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
}
}
if byte == 0x3 && config.lflag.contains(TermiosLflag::ISIG) {
drop(config);
let pgid = ring.inner.lock().fg_pgid;
if let Some(pgid) = pgid {
// TODO send to pgid
let proc = Process::get(pgid);
if let Some(proc) = proc {
proc.set_signal(Signal::Interrupt);
}
}
return;
}
self.ring().putc(byte, false).ok();
}
@@ -232,17 +254,19 @@ impl<const N: usize> CharRing<N> {
pub const fn new() -> Self {
Self {
inner: IrqSafeSpinLock::new(CharRingInner {
fg_pgid: None,
rd: 0,
wr: 0,
data: [0; N],
flags: 0,
}),
config: IrqSafeSpinLock::new(Termios::new()),
wait_read: Wait::new(),
wait_write: Wait::new(),
wait_read: Wait::new("tty_read"),
wait_write: Wait::new("tty_write"),
}
}
/// Returns `true` if a character/line is available for reception
pub fn is_readable(&self) -> bool {
let inner = self.inner.lock();
let config = self.config.lock();
+8 -5
View File
@@ -2,7 +2,7 @@
use crate::util::InitOnce;
use alloc::boxed::Box;
use core::sync::atomic::{AtomicUsize, Ordering};
use libsys::error::Errno;
use libsys::{stat::FileMode, error::Errno};
use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeKind, VnodeRef};
/// Possible character device kinds
@@ -16,7 +16,9 @@ static DEVFS_ROOT: InitOnce<VnodeRef> = InitOnce::new();
/// Initializes devfs
pub fn init() {
DEVFS_ROOT.init(Vnode::new("", VnodeKind::Directory, 0));
let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
node.props_mut().mode = FileMode::default_dir();
DEVFS_ROOT.init(node);
}
/// Returns devfs root node reference
@@ -24,10 +26,11 @@ pub fn root() -> &'static VnodeRef {
DEVFS_ROOT.get()
}
fn _add_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> {
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, 0);
let node = Vnode::new(name, VnodeKind::Char, Vnode::CACHE_STAT);
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);
@@ -53,5 +56,5 @@ pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Re
let name = core::str::from_utf8(&buf[..=prefix.len()]).map_err(|_| Errno::InvalidArgument)?;
_add_char_device(dev, name)
add_named_char_device(dev, name)
}
+14
View File
@@ -3,9 +3,12 @@ use crate::mem::{
self,
phys::{self, PageUsage},
};
use libsys::{error::Errno, stat::MountOptions};
use vfs::VnodeRef;
use memfs::BlockAllocator;
pub mod devfs;
pub mod sysfs;
/// Allocator implementation for memfs
#[derive(Clone, Copy)]
@@ -25,3 +28,14 @@ unsafe impl BlockAllocator for MemfsBlockAlloc {
phys::free_page(phys).unwrap();
}
}
/// Creates a filesystem instance based on `options`
pub fn create_filesystem(options: &MountOptions) -> Result<VnodeRef, Errno> {
let fs_name = options.fs.unwrap();
match fs_name {
"devfs" => Ok(devfs::root().clone()),
"sysfs" => Ok(sysfs::root().clone()),
_ => todo!()
}
}
+169
View File
@@ -0,0 +1,169 @@
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::fmt::{self, Write};
use core::str::FromStr;
use crate::debug::{self, Level};
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
}
impl<'a> fmt::Write for BufferWriter<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.bytes() {
if self.pos == self.dst.len() {
todo!();
}
self.dst[self.pos] = byte;
self.pos += 1;
}
Ok(())
}
}
impl<'a> BufferWriter<'a> {
pub const fn new(dst: &'a mut [u8]) -> Self {
Self { dst, pos: 0 }
}
pub const fn count(&self) -> usize {
self.pos
}
}
#[auto_inode]
impl<
R: Fn(&mut [u8]) -> Result<usize, Errno>,
W: Fn(&[u8]) -> Result<usize, Errno>,
> VnodeImpl for NodeData<R, W>
{
fn open(&mut self, _node: VnodeRef, _mode: 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> {
if pos != 0 {
// TODO handle this
Ok(0)
} else {
(self.read_func)(data)
}
}
fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno> {
if pos != 0 {
todo!();
}
(self.write_func)(data)
}
}
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 {
read_func,
write_func,
}
}
}
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)
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);
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);
} else {
SYSFS_ROOT.get().attach(node);
}
}
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)
}
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))
}
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);
node.props_mut().mode = FileMode::from_bits(0o500).unwrap() | FileMode::S_IFDIR;
if let Some(parent) = parent {
parent.attach(node.clone());
} else {
SYSFS_ROOT.get().attach(node.clone());
}
Ok(node)
}
pub fn root() -> &'static VnodeRef {
SYSFS_ROOT.get()
}
pub fn init() {
let node = Vnode::new("", VnodeKind::Directory, 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_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)?;
Ok(writer.count())
});
}
+4 -3
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};
use libsys::stat::{FileDescriptor, OpenFlags, UserId, GroupId};
use memfs::Ramfs;
use vfs::{Filesystem, Ioctx};
@@ -29,7 +29,7 @@ pub extern "C" fn init_fn(_arg: usize) -> ! {
unsafe { Ramfs::open(initrd_start as *mut u8, initrd_size, MemfsBlockAlloc {}).unwrap() };
let root = fs.root().unwrap();
let ioctx = Ioctx::new(root);
let ioctx = Ioctx::new(root, UserId::root(), GroupId::root());
let node = ioctx.find(None, "/init", true).unwrap();
let file = node.open(OpenFlags::O_RDONLY | OpenFlags::O_EXEC).unwrap();
@@ -54,10 +54,11 @@ pub extern "C" fn init_fn(_arg: usize) -> ! {
io.set_file(FileDescriptor::STDIN, stdin).unwrap();
io.set_file(FileDescriptor::STDOUT, stdout).unwrap();
io.set_file(FileDescriptor::STDERR, stderr).unwrap();
io.set_ctty(tty_node);
}
drop(cfg);
Process::execve(|space| elf::load_elf(space, file), 0).unwrap();
Process::execve(|space| elf::load_elf(space, file), &["/init"]).unwrap();
panic!("Unreachable");
}
+4 -5
View File
@@ -1,18 +1,15 @@
//! osdve5 crate (lol)
#![feature(
asm,
global_asm,
const_for,
const_mut_refs,
const_raw_ptr_deref,
const_fn_fn_ptr_basics,
const_fn_trait_bound,
const_trait_impl,
const_panic,
panic_info_message,
alloc_error_handler,
linked_list_cursors,
const_btree_new
const_btree_new,
asm_const,
)]
#![no_std]
#![no_main]
@@ -40,6 +37,8 @@ pub mod sync;
pub mod syscall;
pub mod util;
use core::arch::asm;
#[panic_handler]
fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
unsafe {
+37 -27
View File
@@ -17,6 +17,7 @@ pub struct SimpleManager {
pages: &'static mut [PageInfo],
stats: PageStatistics,
base_index: usize,
last_index: usize,
}
impl SimpleManager {
pub(super) unsafe fn initialize(base: usize, at: usize, count: usize) -> Self {
@@ -34,6 +35,7 @@ impl SimpleManager {
}
Self {
base_index: base / PAGE_SIZE,
last_index: 0,
stats: PageStatistics {
available: 0,
kernel: 0,
@@ -57,11 +59,21 @@ impl SimpleManager {
}
fn alloc_single_index(&mut self, pu: PageUsage) -> Result<usize, Errno> {
for index in 0..self.pages.len() {
for index in self.last_index..self.pages.len() {
let page = &mut self.pages[index];
if page.usage == PageUsage::Available {
page.usage = pu;
page.refcount = 1;
self.last_index = index;
return Ok(index);
}
}
for index in 0..self.last_index {
let page = &mut self.pages[index];
if page.usage == PageUsage::Available {
page.usage = pu;
page.refcount = 1;
self.last_index = index;
return Ok(index);
}
}
@@ -81,18 +93,18 @@ impl SimpleManager {
self.stats.available -= count;
}
fn update_stats_free(&mut self, pu: PageUsage, count: usize) {
let field = match pu {
PageUsage::Kernel => &mut self.stats.kernel,
PageUsage::KernelHeap => &mut self.stats.kernel_heap,
PageUsage::Paging => &mut self.stats.paging,
PageUsage::UserPrivate => &mut self.stats.user_private,
PageUsage::Filesystem => &mut self.stats.filesystem,
_ => panic!("TODO {:?}", pu),
};
*field -= count;
self.stats.available += count;
}
// fn update_stats_free(&mut self, pu: PageUsage, count: usize) {
// let field = match pu {
// PageUsage::Kernel => &mut self.stats.kernel,
// PageUsage::KernelHeap => &mut self.stats.kernel_heap,
// PageUsage::Paging => &mut self.stats.paging,
// PageUsage::UserPrivate => &mut self.stats.user_private,
// PageUsage::Filesystem => &mut self.stats.filesystem,
// _ => panic!("TODO {:?}", pu),
// };
// *field -= count;
// self.stats.available += count;
// }
}
unsafe impl Manager for SimpleManager {
fn alloc_page(&mut self, pu: PageUsage) -> Result<usize, Errno> {
@@ -122,23 +134,21 @@ unsafe impl Manager for SimpleManager {
Err(Errno::OutOfMemory)
}
fn free_page(&mut self, addr: usize) -> Result<(), Errno> {
let usage = {
let index = self.page_index(addr);
let page = &mut self.pages[index];
let index = self.page_index(addr);
let page = &mut self.pages[index];
let usage = page.usage;
assert!(page.usage != PageUsage::Reserved && page.usage != PageUsage::Available);
assert!(page.usage != PageUsage::Reserved && page.usage != PageUsage::Available);
if page.refcount > 1 {
page.refcount -= 1;
} else {
assert_eq!(page.refcount, 1);
page.usage = PageUsage::Available;
page.refcount = 0;
}
if page.refcount > 1 {
page.refcount -= 1;
} else {
assert_eq!(page.refcount, 1);
page.usage = PageUsage::Available;
page.refcount = 0;
self.last_index = index;
}
usage
};
// FIXME
// self.update_stats_free(usage, 1);
+98 -5
View File
@@ -6,6 +6,7 @@ use crate::mem::{
};
use core::ops::{Index, IndexMut};
use libsys::{error::Errno, mem::memset};
use core::arch::asm;
/// Transparent wrapper structure representing a single
/// translation table entry
@@ -210,6 +211,26 @@ impl Space {
}
}
/// 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> {
@@ -247,6 +268,71 @@ impl Space {
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()?;
@@ -271,12 +357,19 @@ impl Space {
todo!();
// res.map(virt_addr, dst_phys, flags)?;
} else {
// TODO only apply CoW to writable pages
flags |= MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW;
l2_table[l2i].set_cow();
unsafe {
asm!("tlbi vaae1, {}", in(reg) virt_addr);
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)?;
}
}
+71 -2
View File
@@ -1,12 +1,13 @@
//! Process file descriptors and I/O context
use alloc::collections::BTreeMap;
use libsys::{error::Errno, stat::FileDescriptor};
use vfs::{FileRef, Ioctx};
use libsys::{error::Errno, stat::{FileDescriptor, UserId, GroupId}};
use vfs::{FileRef, Ioctx, VnodeRef, VnodeKind};
/// Process I/O context. Contains file tables, root/cwd info etc.
pub struct ProcessIo {
ioctx: Option<Ioctx>,
files: BTreeMap<u32, FileRef>,
ctty: Option<VnodeRef>,
}
impl ProcessIo {
@@ -21,6 +22,73 @@ impl ProcessIo {
Ok(dst)
}
/// Sets controlling terminal for the process
pub fn set_ctty(&mut self, node: VnodeRef) {
assert_eq!(node.kind(), VnodeKind::Char);
self.ctty = Some(node);
}
/// Returns current controlling terminal of the process
pub fn ctty(&mut self) -> Option<VnodeRef> {
self.ctty.clone()
}
/// Returns user ID of the process
#[inline(always)]
pub fn uid(&self) -> UserId {
self.ioctx.as_ref().unwrap().uid
}
/// Returns group ID of the process
#[inline(always)]
pub fn gid(&self) -> GroupId {
self.ioctx.as_ref().unwrap().gid
}
/// Changes (if permitted) user ID of the process
#[inline(always)]
pub fn set_uid(&mut self, uid: UserId) -> Result<(), Errno> {
let old_uid = self.uid();
if old_uid == uid {
Ok(())
} else if !old_uid.is_root() {
Err(Errno::PermissionDenied)
} else {
self.ioctx.as_mut().unwrap().uid = uid;
Ok(())
}
}
/// Changes (if permitted) group ID of the process
#[inline(always)]
pub fn set_gid(&mut self, gid: GroupId) -> Result<(), Errno> {
let old_gid = self.gid();
if old_gid == gid {
Ok(())
} else if !old_gid.is_root() {
Err(Errno::PermissionDenied)
} else {
self.ioctx.as_mut().unwrap().gid = gid;
Ok(())
}
}
/// 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> {
let file_ref = self.file(src)?;
if let Some(dst) = dst {
let idx = u32::from(dst);
if self.files.get(&idx).is_some() {
return Err(Errno::AlreadyExists);
}
self.files.insert(idx, file_ref);
Ok(dst)
} else {
self.place_file(file_ref)
}
}
/// 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)
@@ -54,6 +122,7 @@ impl ProcessIo {
Self {
files: BTreeMap::new(),
ioctx: None,
ctty: None,
}
}
+9 -28
View File
@@ -3,11 +3,14 @@
use crate::init;
use crate::sync::IrqSafeSpinLock;
use alloc::collections::BTreeMap;
use libsys::proc::Pid;
use libsys::proc::{Tid, Pid};
pub mod elf;
pub mod thread;
pub use thread::{Thread, ThreadRef, State as ThreadState};
pub(self) use thread::Context;
pub mod process;
pub use process::{Process, ProcessRef, State as ProcessState};
pub use process::{Process, ProcessRef, ProcessState};
pub mod io;
pub use io::ProcessIo;
@@ -17,25 +20,6 @@ pub mod sched;
pub use sched::Scheduler;
pub(self) use sched::SCHED;
// macro_rules! spawn {
// (fn ($dst_arg:ident : usize) $body:block, $src_arg:expr) => {{
// #[inline(never)]
// extern "C" fn __inner_func($dst_arg : usize) -> ! {
// let __res = $body;
// {
// #![allow(unreachable_code)]
// SCHED.current_process().exit(__res);
// panic!();
// }
// }
//
// let __proc = $crate::proc::Process::new_kernel(__inner_func, $src_arg).unwrap();
// $crate::proc::SCHED.enqueue(__proc.id());
// }};
//
// (fn () $body:block) => (spawn!(fn (_arg: usize) $body, 0usize))
// }
/// Performs a task switch.
///
/// See [Scheduler::switch]
@@ -43,15 +27,12 @@ pub fn switch() {
SCHED.switch(false);
}
///
pub fn process(id: Pid) -> ProcessRef {
PROCESSES.lock().get(&id).unwrap().clone()
}
/// Global list of all processes in the system
pub(self) static PROCESSES: IrqSafeSpinLock<BTreeMap<Pid, ProcessRef>> =
IrqSafeSpinLock::new(BTreeMap::new());
pub(self) static THREADS: IrqSafeSpinLock<BTreeMap<Tid, ThreadRef>> =
IrqSafeSpinLock::new(BTreeMap::new());
/// Sets up initial process and enters it.
///
/// See [Scheduler::enter]
@@ -61,6 +42,6 @@ pub(self) static PROCESSES: IrqSafeSpinLock<BTreeMap<Pid, ProcessRef>> =
/// Unsafe: May only be called once.
pub unsafe fn enter() -> ! {
SCHED.init();
SCHED.enqueue(Process::new_kernel(init::init_fn, 0).unwrap().id());
Process::new_kernel(init::init_fn, 0).unwrap().enqueue();
SCHED.enter();
}
+379 -264
View File
@@ -5,50 +5,50 @@ use crate::mem::{
phys::{self, PageUsage},
virt::{MapAttributes, Space},
};
use crate::proc::{wait::Wait, ProcessIo, PROCESSES, SCHED};
use crate::sync::IrqSafeSpinLock;
use alloc::rc::Rc;
use core::cell::UnsafeCell;
use crate::proc::{
wait::Wait, Context, ProcessIo, Thread, ThreadRef, ThreadState, PROCESSES, SCHED, Tid,
};
use crate::sync::{IrqSafeSpinLock};
use alloc::{rc::Rc, vec::Vec};
use core::sync::atomic::{AtomicU32, Ordering};
use libsys::{error::Errno, signal::Signal, proc::{ExitCode, Pid}};
pub use crate::arch::platform::context::{self, Context};
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>;
/// List of possible process states
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum State {
/// Process is ready to be executed and/or is scheduled for it
Ready,
/// Process is currently running or is in system call/interrupt handler
Running,
pub enum ProcessState {
/// Process is alive
Active,
/// Process has finished execution and is waiting to be reaped
Finished,
/// Process is waiting for some external event
Waiting,
}
struct ProcessInner {
space: Option<&'static mut Space>,
state: State,
state: ProcessState,
id: Pid,
wait_flag: bool,
pgid: Pid,
ppid: Option<Pid>,
sid: Pid,
exit: Option<ExitCode>,
signal_entry: usize,
signal_stack: usize,
threads: Vec<Tid>,
}
/// Structure describing an operating system process
#[allow(dead_code)]
pub struct Process {
ctx: UnsafeCell<Context>,
signal_ctx: UnsafeCell<Context>,
inner: IrqSafeSpinLock<ProcessInner>,
exit_wait: Wait,
signal_state: AtomicU32,
signal_pending: AtomicU32,
/// Process I/O context
pub io: IrqSafeSpinLock<ProcessIo>,
}
@@ -57,9 +57,89 @@ impl Process {
const USTACK_VIRT_TOP: usize = 0x100000000;
const USTACK_PAGES: usize = 4;
/// Returns currently executing process
/// Returns the process ID
#[inline]
pub fn id(&self) -> Pid {
self.inner.lock().id
}
/// Returns the process session ID
#[inline]
pub fn sid(&self) -> Pid {
self.inner.lock().sid
}
/// Returns parent's [Pid]
#[inline]
pub fn pgid(&self) -> Pid {
self.inner.lock().pgid
}
/// Returns parent's [Pid]
#[inline]
pub fn ppid(&self) -> Option<Pid> {
self.inner.lock().ppid
}
/// Sets a new group id for the process
pub fn set_pgid(&self, pgid: Pid) {
self.inner.lock().pgid = pgid;
}
/// Sets a new session id for the process
pub fn set_sid(&self, sid: Pid) {
self.inner.lock().sid = sid;
}
/// Returns [Rc]-reference to current process
#[inline]
pub fn current() -> ProcessRef {
SCHED.current_process()
Thread::current().owner().unwrap()
}
/// Executes a closure performing manipulations on the process address space
#[inline]
pub fn manipulate_space<R, F>(&self, f: F) -> R
where
F: FnOnce(&mut Space) -> R,
{
f(self.inner.lock().space.as_mut().unwrap())
}
/// Creates a new kernel process
pub fn new_kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<ProcessRef, Errno> {
let id = new_kernel_pid();
let thread = Thread::new_kernel(Some(id), entry, arg)?;
let mut inner = ProcessInner {
threads: Vec::new(),
id,
pgid: id,
ppid: None,
sid: id,
exit: None,
space: None,
state: ProcessState::Active,
};
inner.threads.push(thread.id());
let res = Rc::new(Self {
exit_wait: Wait::new("process_exit"),
io: IrqSafeSpinLock::new(ProcessIo::new()),
signal_state: AtomicU32::new(0),
inner: IrqSafeSpinLock::new(inner),
});
debugln!("New kernel process: {:?}", id);
let prev = PROCESSES.lock().insert(id, res.clone());
assert!(prev.is_none());
Ok(res)
}
/// Adds all of the process threads to scheduler queue
pub fn enqueue(&self) {
let inner = self.inner.lock();
for &tid in inner.threads.iter() {
SCHED.enqueue(tid);
}
}
/// Returns process (if any) to which `pid` refers
@@ -67,203 +147,84 @@ impl Process {
PROCESSES.lock().get(&pid).cloned()
}
fn find1(a: u32) -> Option<usize> {
for i in 0..32 {
if a & (1 << i) != 0 {
return Some(i);
}
}
None
}
/// Handles all pending signals (when returning from aborted syscall)
pub fn handle_pending_signals(&self) {
let mut lock = self.inner.lock();
let ttbr0 = lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
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, ttbr0);
} else {
break;
}
}
}
/// Sets a pending signal for a process
pub fn set_signal(&self, signal: Signal) {
let lock = self.inner.lock();
let mut lock = self.inner.lock();
let ttbr0 = lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
let main_thread = Thread::get(lock.threads[0]).unwrap();
drop(lock);
match lock.state {
State::Running => {
drop(lock);
self.enter_signal(signal);
// 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, ttbr0);
}
State::Waiting => {
// TODO abort whatever the process is waiting for
todo!()
ThreadState::Waiting => {
self.signal_state.fetch_or(1 << (signal as u32), Ordering::Release);
main_thread.interrupt_wait(true);
}
State::Ready => {
todo!()
ThreadState::Ready => {
main_thread.clone().setup_signal(signal, ttbr0);
main_thread.interrupt_wait(false);
}
State::Finished => {
ThreadState::Finished => {
// TODO report error back
todo!()
}
}
}
/// Switches current thread back from signal handler
pub fn return_from_signal(&self) {
if self.signal_pending.load(Ordering::Acquire) == 0 {
panic!("TODO handle cases when returning from no signal");
}
self.signal_pending.store(0, Ordering::Release);
let src_ctx = self.signal_ctx.get();
let dst_ctx = self.ctx.get();
assert_eq!(self.inner.lock().state, State::Running);
unsafe {
(&mut *src_ctx).switch(&mut *dst_ctx);
}
}
/// Switches current thread to a signal handler
pub fn enter_signal(&self, signal: Signal) {
if self
.signal_pending
.compare_exchange_weak(0, signal as u32, Ordering::SeqCst, Ordering::Relaxed)
.is_err()
{
panic!("Already handling a signal (maybe handle this case)");
}
/// Immediately delivers a signal to requested thread
pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
let mut lock = self.inner.lock();
let signal_ctx = unsafe { &mut *self.signal_ctx.get() };
let dst_id = lock.id;
let dst_space_phys = lock.space.as_mut().unwrap().address_phys();
let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
debugln!(
"Signal entry: pc={:#x}, sp={:#x}, ttbr0={:#x}",
lock.signal_entry,
lock.signal_stack,
dst_ttbr0
);
assert_eq!(lock.state, State::Running);
unsafe {
signal_ctx.setup_signal_entry(
lock.signal_entry,
signal as usize,
dst_ttbr0,
lock.signal_stack,
);
}
let src_ctx = self.ctx.get();
let ttbr0 = lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
drop(lock);
unsafe {
(&mut *src_ctx).switch(signal_ctx);
}
thread.enter_signal(signal, ttbr0);
}
/// Sets up values needed for signal entry
pub fn setup_signal_context(&self, entry: usize, stack: usize) {
/// 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();
lock.signal_entry = entry;
lock.signal_stack = stack;
}
/// Schedules an initial thread for execution
///
/// # Safety
///
/// Unsafe: only allowed to be called once, repeated calls
/// will generate undefined behavior
pub unsafe fn enter(proc: ProcessRef) -> ! {
// FIXME use some global lock to guarantee atomicity of thread entry?
proc.inner.lock().state = State::Running;
proc.current_context().enter()
}
let space_phys = lock.space.as_mut().unwrap().address_phys();
let ttbr0 = space_phys | ((lock.id.asid() as usize) << 48);
/// Executes a function allowing mutation of the process address space
#[inline]
pub fn manipulate_space<F: FnOnce(&mut Space) -> Result<(), Errno>>(
&self,
f: F,
) -> Result<(), Errno> {
f(self.inner.lock().space.as_mut().unwrap())
}
let thread = Thread::new_user(lock.id, entry, stack, arg, ttbr0)?;
let tid = thread.id();
lock.threads.push(tid);
SCHED.enqueue(tid);
#[allow(clippy::mut_from_ref)]
fn current_context(&self) -> &mut Context {
if self.signal_pending.load(Ordering::Acquire) != 0 {
unsafe { &mut *self.signal_ctx.get() }
} else {
unsafe { &mut *self.ctx.get() }
}
}
/// Schedules a next thread for execution
///
/// # Safety
///
/// Unsafe:
///
/// * Does not ensure src and dst threads are not the same thread
/// * Does not ensure src is actually current context
pub unsafe fn switch(src: ProcessRef, dst: ProcessRef, discard: bool) {
{
let mut src_lock = src.inner.lock();
let mut dst_lock = dst.inner.lock();
if !discard {
assert_eq!(src_lock.state, State::Running);
src_lock.state = State::Ready;
}
assert!(dst_lock.state == State::Ready || dst_lock.state == State::Waiting);
dst_lock.state = State::Running;
}
let src_ctx = src.current_context();
let dst_ctx = dst.current_context();
(&mut *src_ctx).switch(&mut *dst_ctx);
}
/// Suspends current process with a "waiting" status
pub fn enter_wait(&self) {
let drop = {
let mut lock = self.inner.lock();
let drop = lock.state == State::Running;
lock.state = State::Waiting;
SCHED.dequeue(lock.id);
drop
};
if drop {
SCHED.switch(true);
}
}
/// Changes process wait condition status
pub fn set_wait_flag(&self, v: bool) {
self.inner.lock().wait_flag = v;
}
/// Returns `true` if process wait condition has not been reached
pub fn wait_flag(&self) -> bool {
self.inner.lock().wait_flag
}
/// Returns the process ID
pub fn id(&self) -> Pid {
self.inner.lock().id
}
/// Creates a new kernel process
pub fn new_kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<ProcessRef, Errno> {
let id = new_kernel_pid();
let res = Rc::new(Self {
ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)),
signal_ctx: UnsafeCell::new(Context::empty()),
io: IrqSafeSpinLock::new(ProcessIo::new()),
exit_wait: Wait::new(),
signal_state: AtomicU32::new(0),
signal_pending: AtomicU32::new(0),
inner: IrqSafeSpinLock::new(ProcessInner {
signal_entry: 0,
signal_stack: 0,
id,
exit: None,
space: None,
wait_flag: false,
state: State::Ready,
}),
});
debugln!("New kernel process: {:?}", id);
assert!(PROCESSES.lock().insert(id, res.clone()).is_none());
Ok(res)
Ok(tid)
}
/// Creates a "fork" of the process, cloning its address space and
@@ -277,63 +238,114 @@ impl Process {
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 dst = Rc::new(Self {
ctx: UnsafeCell::new(Context::fork(frame, dst_ttbr0)),
signal_ctx: UnsafeCell::new(Context::empty()),
exit_wait: Wait::new("process_exit"),
io: IrqSafeSpinLock::new(src_io.fork()?),
exit_wait: Wait::new(),
signal_state: AtomicU32::new(0),
signal_pending: AtomicU32::new(0),
inner: IrqSafeSpinLock::new(ProcessInner {
signal_entry: 0,
signal_stack: 0,
id: dst_id,
threads,
exit: None,
space: Some(dst_space),
state: State::Ready,
wait_flag: false,
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());
SCHED.enqueue(dst_id);
SCHED.enqueue(tid);
Ok(dst_id)
}
/// Terminates a process.
pub fn exit<I: Into<ExitCode>>(&self, status: I) {
let status = status.into();
let drop = {
let mut lock = self.inner.lock();
let drop = lock.state == State::Running;
infoln!("Process {:?} is exiting: {:?}", lock.id, status);
assert!(lock.exit.is_none());
lock.exit = Some(status);
lock.state = State::Finished;
pub fn exit(self: ProcessRef, status: ExitCode) {
let thread = Thread::current();
let mut lock = self.inner.lock();
let is_running = thread.owner_id().map(|e| e == lock.id).unwrap_or(false);
if let Some(space) = lock.space.take() {
unsafe {
Space::release(space);
asm!("tlbi aside1, {}", in(reg) ((lock.id.asid() as usize) << 48));
}
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);
}
self.io.lock().handle_exit();
if let Some(space) = lock.space.take() {
unsafe {
Space::release(space);
Process::invalidate_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();
drop(lock);
SCHED.dequeue(lock.id);
drop
};
self.exit_wait.wakeup_all();
if drop {
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) {
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;
}
lock.threads.retain(|&e| e != tid);
thread.terminate(status);
SCHED.dequeue(tid);
debugln!("Thread {:?} terminated", tid);
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!();
}
}
fn collect(&self) -> Option<ExitCode> {
let lock = self.inner.lock();
if lock.state == State::Finished {
if lock.state == ProcessState::Finished {
lock.exit
} else {
None
@@ -352,7 +364,6 @@ impl Process {
if let Some(r) = proc.collect() {
// TODO drop the process struct itself
PROCESSES.lock().remove(&proc.id());
debugln!("pid {:?} has {} refs", proc.id(), Rc::strong_count(&proc));
return Ok(r);
}
@@ -360,43 +371,147 @@ 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::SH_OUTER
| MapAttributes::NOT_GLOBAL
| MapAttributes::UXN
| MapAttributes::PXN
| MapAttributes::AP_BOTH_READONLY;
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::SH_OUTER
| MapAttributes::NOT_GLOBAL
| MapAttributes::UXN
| MapAttributes::PXN
| MapAttributes::AP_BOTH_READONLY;
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> {
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())?;
offset += arg.len();
}
// Align
offset = (offset + 15) & !15;
let argv_offset = offset;
// 2. Store arg pointers
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())?;
offset += 16;
data_offset += arg.len();
}
// 3. Store ProgramArgs
let data = ProgramArgs {
argc: argv.len(),
argv: base + argv_offset,
storage: base,
size: offset + core::mem::size_of::<ProgramArgs>(),
};
Self::write_paged(space, base + offset, data)?;
Ok(base + offset)
}
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>>(
loader: F,
arg: usize,
argv: &[&str],
) -> Result<(), Errno> {
unsafe {
// Run with interrupts disabled
asm!("msr daifset, #2");
}
let proc = SCHED.current_process();
let mut lock = proc.inner.lock();
if lock.id.is_kernel() {
let mut proc_lock = PROCESSES.lock();
let old_pid = lock.id;
assert!(
proc_lock.remove(&old_pid).is_some(),
"Failed to downgrade kernel process (remove kernel pid)"
);
lock.id = new_user_pid();
debugln!(
"Process downgrades from kernel to user: {:?} -> {:?}",
old_pid,
lock.id
);
assert!(proc_lock.insert(lock.id, proc.clone()).is_none());
unsafe {
SCHED.hack_current_pid(lock.id);
}
} else {
// Invalidate user ASID
let input = (lock.id.asid() as usize) << 48;
unsafe {
asm!("tlbi aside1, {}", in(reg) input);
}
let proc = Process::current();
let mut process_lock = proc.inner.lock();
if process_lock.threads.len() != 1 {
todo!();
}
let thread = Thread::get(process_lock.threads[0]).unwrap();
if process_lock.id.is_kernel() {
let mut processes = PROCESSES.lock();
let old_pid = process_lock.id;
let new_pid = new_user_pid();
debugln!("Downgrading process {:?} -> {:?}", old_pid, new_pid);
let r = processes.remove(&old_pid);
assert!(r.is_some());
process_lock.id = new_pid;
process_lock.pgid = new_pid;
process_lock.sid = new_pid;
let r = processes.insert(new_pid, proc.clone());
assert!(r.is_none());
}
thread.set_owner(process_lock.id);
proc.io.lock().handle_cloexec();
let new_space = Space::alloc_empty()?;
@@ -416,25 +531,25 @@ impl Process {
}
let entry = loader(new_space)?;
let arg = Self::store_arguments(new_space, argv)?;
debugln!("Will now enter at {:#x}", entry);
// TODO drop old address space
lock.space = Some(new_space);
process_lock.space = Some(new_space);
unsafe {
// TODO drop old context
let ctx = proc.ctx.get();
let ctx = thread.ctx.get();
let asid = (process_lock.id.asid() as usize) << 48;
Process::invalidate_asid(asid);
ctx.write(Context::user(
entry,
arg,
new_space_phys | ((lock.id.asid() as usize) << 48),
new_space_phys | asid,
Self::USTACK_VIRT_TOP,
));
assert_eq!(lock.state, State::Running);
drop(lock);
drop(process_lock);
(*ctx).enter();
}
+38 -21
View File
@@ -1,13 +1,15 @@
//!
use crate::proc::{Pid, Process, ProcessRef, PROCESSES};
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;
struct SchedulerInner {
queue: VecDeque<Pid>,
idle: Option<Pid>,
current: Option<Pid>,
queue: VecDeque<Tid>,
idle: Option<Tid>,
current: Option<Tid>,
}
/// Process scheduler state and queues
@@ -23,7 +25,9 @@ impl SchedulerInner {
current: None,
};
this.idle = Some(Process::new_kernel(idle_fn, 0).unwrap().id());
let idle = Thread::new_kernel(None, idle_fn, 0).unwrap().id();
assert_eq!(idle, Tid::IDLE);
this.idle = Some(idle);
this
}
@@ -39,13 +43,13 @@ impl Scheduler {
}
/// Schedules a thread for execution
pub fn enqueue(&self, pid: Pid) {
self.inner.get().lock().queue.push_back(pid);
pub fn enqueue(&self, tid: Tid) {
self.inner.get().lock().queue.push_back(tid);
}
/// Removes given `pid` from execution queue
pub fn dequeue(&self, pid: Pid) {
self.inner.get().lock().queue.retain(|&p| p != pid)
/// Removes given `tid` from execution queue
pub fn dequeue(&self, tid: Tid) {
self.inner.get().lock().queue.retain(|&p| p != tid)
}
/// Performs initial process entry.
@@ -63,11 +67,11 @@ impl Scheduler {
};
inner.current = Some(id);
PROCESSES.lock().get(&id).unwrap().clone()
THREADS.lock().get(&id).unwrap().clone()
};
asm!("msr daifset, #2");
Process::enter(thread)
Thread::enter(thread)
}
/// This hack is required to be called from execve() when downgrading current
@@ -76,8 +80,14 @@ impl Scheduler {
/// # Safety
///
/// Unsafe: only allowed to be called from Process::execve()
pub unsafe fn hack_current_pid(&self, new: Pid) {
self.inner.get().lock().current = Some(new);
pub unsafe fn hack_current_tid(&self, old: Tid, new: Tid) {
let mut lock = self.inner.get().lock();
match lock.current {
Some(t) if t == old => {
lock.current = Some(new);
}
_ => {}
}
}
/// Switches to the next task scheduled for execution. If there're
@@ -87,7 +97,7 @@ impl Scheduler {
let mut inner = self.inner.get().lock();
let current = inner.current.unwrap();
if !discard && current != Pid::IDLE {
if !discard && current != Tid::IDLE {
// Put the process into the back of the queue
inner.queue.push_back(current);
}
@@ -100,7 +110,7 @@ impl Scheduler {
inner.current = Some(next);
let (from, to) = {
let lock = PROCESSES.lock();
let lock = THREADS.lock();
(
lock.get(&current).unwrap().clone(),
lock.get(&next).unwrap().clone(),
@@ -113,17 +123,24 @@ impl Scheduler {
if !Rc::ptr_eq(&from, &to) {
unsafe {
asm!("msr daifset, #2");
Process::switch(from, to, discard);
Thread::switch(from, to, discard);
}
}
}
/// Returns a Rc-reference to currently running process
pub fn current_process(&self) -> ProcessRef {
/// Returns a [Rc]-reference to currently running Thread
pub fn current_thread(&self) -> ThreadRef {
let inner = self.inner.get().lock();
let current = inner.current.unwrap();
PROCESSES.lock().get(&current).unwrap().clone()
let id = inner.current.unwrap();
THREADS.lock().get(&id).unwrap().clone()
}
// /// Returns a Rc-reference to currently running process
// pub fn current_process(&self) -> ProcessRef {
// let inner = self.inner.get().lock();
// let current = inner.current.unwrap();
// PROCESSES.lock().get(&current).unwrap().clone()
// }
}
/// Returns `true` if the scheduler has been initialized
+411
View File
@@ -0,0 +1,411 @@
//! Facilities for controlling threads - smallest units of
//! execution in the operating system
use crate::arch::aarch64::exception::ExceptionFrame;
use crate::proc::{
wait::{Wait, WaitStatus},
Process, ProcessRef, SCHED, THREADS,
};
use crate::sync::IrqSafeSpinLock;
use crate::util::InitOnce;
use alloc::rc::Rc;
use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicU32, Ordering};
use libsys::{
error::Errno,
proc::{ExitCode, Pid, Tid},
signal::Signal,
};
pub use crate::arch::platform::context::{self, Context};
/// Convenience wrapper for [Thread] references
pub type ThreadRef = Rc<Thread>;
/// List of possible process states
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum State {
/// Process is ready to be executed and/or is scheduled for it
Ready,
/// Process is currently running or is in system call/interrupt handler
Running,
/// Process has finished execution and is waiting to be reaped
Finished,
/// Process is waiting for some external event
Waiting,
}
struct ThreadInner {
id: Tid,
state: State,
owner: Option<Pid>,
pending_wait: Option<&'static Wait>,
wait_status: WaitStatus,
signal_entry: usize,
signal_stack: usize,
}
/// Thread control data
pub struct Thread {
inner: IrqSafeSpinLock<ThreadInner>,
exit_wait: Wait,
exit_status: InitOnce<ExitCode>,
pub(super) ctx: UnsafeCell<Context>,
signal_ctx: UnsafeCell<Context>,
signal_pending: AtomicU32,
}
impl Thread {
/// Returns currently active thread [Rc]-reference
#[inline]
pub fn current() -> ThreadRef {
SCHED.current_thread()
}
/// Returns `true` if the thread is currently executing a signal handler context
pub fn is_handling_signal(&self) -> bool {
self.signal_pending.load(Ordering::Acquire) != 0
}
/// Returns a reference to thread `tid`, if it exists
#[inline]
pub fn get(tid: Tid) -> Option<ThreadRef> {
THREADS.lock().get(&tid).cloned()
}
/// Returns the owner process
#[inline]
pub fn owner(&self) -> Option<ProcessRef> {
self.inner.lock().owner.and_then(Process::get)
}
/// Returns [Pid] of the owner process
pub fn owner_id(&self) -> Option<Pid> {
self.inner.lock().owner
}
/// Creates a new kernel process
pub fn new_kernel(
owner: Option<Pid>,
entry: extern "C" fn(usize) -> !,
arg: usize,
) -> Result<ThreadRef, Errno> {
let id = new_tid();
let res = Rc::new(Self {
ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)),
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!("New kernel thread: {:?}", id);
assert!(THREADS.lock().insert(id, res.clone()).is_none());
Ok(res)
}
/// Creates a new userspace process
pub fn new_user(
owner: Pid,
entry: usize,
stack: usize,
arg: usize,
ttbr0: usize,
) -> Result<ThreadRef, Errno> {
let id = new_tid();
let res = Rc::new(Self {
ctx: UnsafeCell::new(Context::user(entry, arg, ttbr0, stack)),
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: Some(owner),
pending_wait: None,
wait_status: WaitStatus::Done,
state: State::Ready,
}),
});
debugln!("New userspace thread: {:?}", id);
assert!(THREADS.lock().insert(id, res.clone()).is_none());
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();
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)
}
/// Returns the thread ID
#[inline]
pub fn id(&self) -> Tid {
self.inner.lock().id
}
/// Schedules an initial thread for execution
///
/// # Safety
///
/// Unsafe: only allowed to be called once, repeated calls
/// will generate undefined behavior
pub unsafe fn enter(thread: ThreadRef) -> ! {
// FIXME use some global lock to guarantee atomicity of thread entry?
thread.inner.lock().state = State::Running;
thread.current_context().enter()
}
/// Schedules a next thread for execution
///
/// # Safety
///
/// Unsafe:
///
/// * Does not ensure src and dst threads are not the same thread
/// * Does not ensure src is actually current context
pub unsafe fn switch(src: ThreadRef, dst: ThreadRef, discard: bool) {
{
let mut src_lock = src.inner.lock();
let mut dst_lock = dst.inner.lock();
if !discard {
assert_eq!(src_lock.state, State::Running);
src_lock.state = State::Ready;
}
// assert!(dst_lock.state == State::Ready || dst_lock.state == State::Waiting);
dst_lock.state = State::Running;
}
let src_ctx = src.current_context();
let dst_ctx = dst.current_context();
(&mut *src_ctx).switch(&mut *dst_ctx);
}
#[allow(clippy::mut_from_ref)]
fn current_context(&self) -> &mut Context {
if self.signal_pending.load(Ordering::Acquire) != 0 {
unsafe { &mut *self.signal_ctx.get() }
} else {
unsafe { &mut *self.ctx.get() }
}
}
/// Suspends current process with a "waiting" status
pub fn enter_wait(&self) {
let drop = {
let mut lock = self.inner.lock();
let drop = lock.state == State::Running;
lock.state = State::Waiting;
SCHED.dequeue(lock.id);
drop
};
if drop {
SCHED.switch(true);
}
}
/// Changes process wait condition status
pub fn setup_wait(&self, wait: *const Wait) {
#![allow(clippy::not_unsafe_ptr_arg_deref)]
let mut lock = self.inner.lock();
// FIXME this is not cool
lock.pending_wait = Some(unsafe { &*wait });
lock.wait_status = WaitStatus::Pending;
}
/// Suspends current thread until thread `tid` terminates
pub fn waittid(tid: Tid) -> Result<(), Errno> {
loop {
let thread = THREADS
.lock()
.get(&tid)
.cloned()
.ok_or(Errno::DoesNotExist)?;
if thread.state() == State::Finished {
// TODO remove thread from its parent?
return Ok(());
}
thread.exit_wait.wait(None)?;
}
}
/// Updates pending wait status
pub fn set_wait_status(&self, status: WaitStatus) {
let mut lock = self.inner.lock();
lock.wait_status = status;
}
/// Resets wait channel back to initial state
pub fn reset_wait(&self) {
let mut lock = self.inner.lock();
lock.pending_wait = None;
lock.wait_status = WaitStatus::Done;
}
/// Returns status of the thread's pending wait
pub fn wait_status(&self) -> WaitStatus {
self.inner.lock().wait_status
}
/// Switches current thread back from signal handler
pub fn return_from_signal(&self) {
if self.signal_pending.load(Ordering::Acquire) == 0 {
panic!("TODO handle cases when returning from no signal");
}
self.signal_pending.store(0, Ordering::Release);
let src_ctx = self.signal_ctx.get();
let dst_ctx = self.ctx.get();
assert_eq!(self.inner.lock().state, State::Running);
unsafe {
(&mut *src_ctx).switch(&mut *dst_ctx);
}
}
/// Returns the thread state
#[inline]
pub fn state(&self) -> State {
self.inner.lock().state
}
/// Sets the thread's owner process ID
pub fn set_owner(&self, pid: Pid) {
self.inner.lock().owner = Some(pid);
}
/// Sets up values needed for signal entry
pub fn set_signal_entry(&self, entry: usize, stack: usize) {
let mut lock = self.inner.lock();
lock.signal_entry = entry;
lock.signal_stack = stack;
}
/// Sets up a context for signal handler
pub fn setup_signal(self: ThreadRef, signal: Signal, ttbr0: usize) {
if self
.signal_pending
.compare_exchange_weak(0, signal as u32, Ordering::SeqCst, Ordering::Relaxed)
.is_err()
{
panic!("Already handling a signal (maybe handle this case)");
}
let lock = self.inner.lock();
if lock.signal_entry == 0 || lock.signal_stack == 0 {
drop(lock);
Process::exit_thread(self, ExitCode::from(-1));
return;
}
let signal_ctx = unsafe { &mut *self.signal_ctx.get() };
debugln!(
"Signal entry: tid={:?}, pc={:#x}, sp={:#x}, ttbr0={:#x}",
lock.id,
lock.signal_entry,
lock.signal_stack,
ttbr0
);
unsafe {
signal_ctx.setup_signal_entry(
lock.signal_entry,
signal as usize,
ttbr0,
lock.signal_stack,
);
}
}
/// Switches process main thread to a signal handler
pub fn enter_signal(self: ThreadRef, signal: Signal, ttbr0: usize) {
let src_ctx = self.ctx.get();
let signal_ctx = unsafe { &mut *self.signal_ctx.get() };
assert_eq!(self.state(), State::Running);
self.setup_signal(signal, ttbr0);
unsafe {
(&mut *src_ctx).switch(signal_ctx);
}
}
/// Interrupts pending wait (from signal routines)
pub fn interrupt_wait(&self, enqueue: bool) {
let mut lock = self.inner.lock();
let tid = lock.id;
let wait = lock.pending_wait.take();
drop(lock);
if let Some(wait) = wait {
wait.abort(tid, enqueue);
}
}
/// Cleans up any resources of the thread and aborts
/// pending wait, if any
pub fn terminate(&self, status: ExitCode) {
let mut lock = self.inner.lock();
lock.state = State::Finished;
let tid = lock.id;
let wait = lock.pending_wait.take();
drop(lock);
if let Some(wait) = wait {
wait.abort(tid, false);
}
self.exit_status.init(status);
self.exit_wait.wakeup_all();
}
}
impl Drop for Thread {
fn drop(&mut self) {
debugln!("Dropping process {:?}", self.id());
}
}
/// Allocates a new thread ID
pub fn new_tid() -> Tid {
static LAST: AtomicU32 = AtomicU32::new(0);
let id = LAST.fetch_add(1, Ordering::Relaxed);
Tid::from(id)
}
+87 -28
View File
@@ -2,25 +2,40 @@
use crate::arch::machine;
use crate::dev::timer::TimestampSource;
use crate::proc::{self, sched::SCHED, Process, ProcessRef};
use crate::proc::{sched::SCHED, Thread, ThreadRef};
use crate::sync::IrqSafeSpinLock;
use alloc::collections::LinkedList;
use core::time::Duration;
use libsys::{error::Errno, stat::FdSet, proc::Pid};
use libsys::{error::Errno, proc::Tid, stat::FdSet};
/// Wait channel structure. Contains a queue of processes
/// waiting for some event to happen.
pub struct Wait {
queue: IrqSafeSpinLock<LinkedList<Pid>>,
queue: IrqSafeSpinLock<LinkedList<Tid>>,
#[allow(dead_code)]
name: &'static str
}
/// Status of a (possibly) pending wait
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum WaitStatus {
/// In progress
Pending,
/// Wait was interrupted by a signal
Interrupted,
/// Channel reported data available
Done,
}
struct Timeout {
pid: Pid,
tid: Tid,
deadline: Duration,
}
static TICK_LIST: IrqSafeSpinLock<LinkedList<Timeout>> = IrqSafeSpinLock::new(LinkedList::new());
pub static WAIT_SELECT: Wait = Wait::new();
/// Global wait channel for blocking on select. Gets notified
/// of ANY I/O operations available, so not very efficient.
pub static WAIT_SELECT: Wait = Wait::new("select");
/// Checks for any timed out wait channels and interrupts them
pub fn tick() {
@@ -30,9 +45,9 @@ pub fn tick() {
while let Some(item) = cursor.current() {
if time > item.deadline {
let pid = item.pid;
let tid = item.tid;
cursor.remove_current();
SCHED.enqueue(pid);
SCHED.enqueue(tid);
} else {
cursor.move_next();
}
@@ -42,7 +57,7 @@ pub fn tick() {
/// Suspends current process for given duration
pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
// Dummy wait descriptor which will never receive notifications
static SLEEP_NOTIFY: Wait = Wait::new();
static SLEEP_NOTIFY: Wait = Wait::new("sleep");
let deadline = machine::local_timer().timestamp()? + timeout;
match SLEEP_NOTIFY.wait(Some(deadline)) {
Err(Errno::Interrupt) => {
@@ -55,22 +70,28 @@ pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
}
}
/// Suspends current process until some file descriptor
/// signals data available
pub fn select(
proc: ProcessRef,
thread: ThreadRef,
mut rfds: Option<&mut FdSet>,
mut wfds: Option<&mut FdSet>,
timeout: Option<Duration>,
) -> Result<usize, Errno> {
// TODO support wfds
if wfds.is_some() || rfds.is_none() {
if wfds.is_none() && rfds.is_none() {
todo!();
}
let read = rfds.as_deref().map(FdSet::clone);
let write = wfds.as_deref().map(FdSet::clone);
rfds.as_deref_mut().map(FdSet::reset);
wfds.as_deref_mut().map(FdSet::reset);
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 {
@@ -104,9 +125,40 @@ pub fn select(
impl Wait {
/// Constructs a new wait channel
pub const fn new() -> Self {
pub const fn new(name: &'static str) -> Self {
Self {
queue: IrqSafeSpinLock::new(LinkedList::new()),
name
}
}
/// Interrupt wait pending on the channel
pub fn abort(&self, tid: Tid, enqueue: bool) {
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();
}
}
}
@@ -115,12 +167,12 @@ impl Wait {
let mut queue = self.queue.lock();
let mut count = 0;
while limit != 0 && !queue.is_empty() {
let pid = queue.pop_front();
if let Some(pid) = pid {
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 pid == item.pid {
if tid == item.tid {
cursor.remove_current();
break;
} else {
@@ -129,8 +181,8 @@ impl Wait {
}
drop(tick_lock);
proc::process(pid).set_wait_flag(false);
SCHED.enqueue(pid);
Thread::get(tid).unwrap().set_wait_status(WaitStatus::Done);
SCHED.enqueue(tid);
}
limit -= 1;
@@ -152,26 +204,33 @@ impl Wait {
/// Suspends current process until event is signalled or
/// (optional) deadline is reached
pub fn wait(&self, deadline: Option<Duration>) -> Result<(), Errno> {
let proc = Process::current();
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(proc.id());
proc.set_wait_flag(true);
queue_lock.push_back(thread.id());
thread.setup_wait(self);
if let Some(deadline) = deadline {
TICK_LIST.lock().push_back(Timeout {
pid: proc.id(),
tid: thread.id(),
deadline,
});
}
loop {
if !proc.wait_flag() {
return Ok(());
}
match thread.wait_status() {
WaitStatus::Pending => {}
WaitStatus::Done => {
return Ok(());
}
WaitStatus::Interrupted => {
return Err(Errno::Interrupt);
}
};
drop(queue_lock);
proc.enter_wait();
thread.enter_wait();
queue_lock = self.queue.lock();
if let Some(deadline) = deadline {
@@ -179,7 +238,7 @@ impl Wait {
let mut cursor = queue_lock.cursor_front_mut();
while let Some(&mut item) = cursor.current() {
if proc.id() == item {
if thread.id() == item {
cursor.remove_current();
break;
} else {
+149 -74
View File
@@ -1,73 +1,194 @@
//! System call argument ABI helpers
use crate::mem;
use core::mem::size_of;
use core::alloc::Layout;
use libsys::error::Errno;
use crate::proc::Process;
use core::arch::asm;
fn translate(virt: usize) -> Option<usize> {
// TODO _mut() versions checking whether pages are actually writable
macro_rules! invalid_memory {
($($args:tt)+) => {
warnln!($($args)+);
#[cfg(feature = "aggressive_syscall")]
{
use libsys::signal::Signal;
use crate::proc::Thread;
let thread = Thread::current();
let proc = thread.owner().unwrap();
proc.enter_fault_signal(thread, Signal::SegmentationFault);
}
return Err(Errno::InvalidArgument);
}
}
#[inline(always)]
fn is_el0_accessible(virt: usize, write: bool) -> bool {
let mut res: usize;
unsafe {
asm!("at s1e1r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
}
if res & 1 == 0 {
Some(res & !(0xFFF | (0xFF << 56)))
} else {
None
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
}
/// Unwraps a slim structure pointer
pub fn validate_user_ptr_struct<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
validate_user_ptr_struct_option(base).and_then(|e| e.ok_or(Errno::InvalidArgument))
/// Checks given argument and interprets it as a `T` reference
pub fn struct_ref<'a, T>(base: usize) -> Result<&'a T, Errno> {
let layout = Layout::new::<T>();
if base % layout.align() != 0 {
invalid_memory!(
"Structure pointer is misaligned: base={:#x}, expected {:?}",
base,
layout
);
}
let bytes = buf_ref(base, layout.size())?;
Ok(unsafe { &*(bytes.as_ptr() as *const T) })
}
pub fn validate_user_ptr_struct_option<'a, T>(base: usize) -> Result<Option<&'a mut T>, Errno> {
/// Checks given argument and interprets it as a `T` mutable reference
pub fn struct_mut<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
let layout = Layout::new::<T>();
if base % layout.align() != 0 {
invalid_memory!(
"Structure pointer is misaligned: base={:#x}, expected {:?}",
base,
layout
);
}
let bytes = buf_mut(base, layout.size())?;
Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })
}
/// Checks given argument and interprets it as a `T` array buffer of size `count`
pub fn struct_buf_ref<'a, T>(base: usize, count: usize) -> Result<&'a [T], Errno> {
let layout = Layout::array::<T>(count).unwrap();
if base % layout.align() != 0 {
invalid_memory!(
"Structure pointer is misaligned: base={:#x}, expected {:?}",
base,
layout
);
}
let bytes = buf_ref(base, layout.size())?;
Ok(unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const T, count) })
}
/// Checks given argument and interprets it as a `T` array buffer of size `count`
pub fn struct_buf_mut<'a, T>(base: usize, count: usize) -> Result<&'a mut [T], Errno> {
let layout = Layout::array::<T>(count).unwrap();
if base % layout.align() != 0 {
invalid_memory!(
"Structure pointer is misaligned: base={:#x}, expected {:?}",
base,
layout
);
}
let bytes = buf_mut(base, layout.size())?;
Ok(unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut T, count) })
}
/// Checks given argument and interprets it as a `Option<&'a T>`
pub fn option_struct_ref<'a, T>(base: usize) -> Result<Option<&'a T>, Errno> {
if base == 0 {
Ok(None)
} else {
let bytes = validate_user_ptr(base, size_of::<T>())?;
Ok(Some(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) }))
struct_ref(base).map(Some)
}
}
/// Unwraps an user buffer reference
pub fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> {
/// Checks given argument and interprets it as a `Option<&'a mut T>`
pub fn option_struct_mut<'a, T>(base: usize) -> Result<Option<&'a mut T>, Errno> {
if base == 0 {
Ok(None)
} else {
struct_mut(base).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> {
if base > mem::KERNEL_OFFSET || base + len > mem::KERNEL_OFFSET {
warnln!(
invalid_memory!(
"User region refers to kernel memory: base={:#x}, len={:#x}",
base,
len
);
return Err(Errno::InvalidArgument);
}
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 translate(i * mem::PAGE_SIZE).is_none() {
warnln!(
"User region refers to unmapped memory: base={:#x}, len={:#x} (page {:#x})",
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 {
process.manipulate_space(|space| {
space.try_cow_copy(i * mem::PAGE_SIZE)?;
Process::invalidate_asid(asid);
Ok(())
})
} else {
Err(Errno::DoesNotExist)
};
if res.is_ok() {
continue;
}
invalid_memory!(
"User region refers to inaccessible/unmapped memory: base={:#x}, len={:#x} (page {:#x}, write={})",
base,
len,
i * mem::PAGE_SIZE
i * mem::PAGE_SIZE,
write
);
return Err(Errno::InvalidArgument);
}
}
Ok(())
}
/// Checks given argument and interprets it as a byte buffer
pub fn buf_ref<'a>(base: usize, len: usize) -> Result<&'a [u8], Errno> {
validate_ptr(base, len, false)?;
Ok(unsafe { core::slice::from_raw_parts(base as *const u8, len) })
}
/// Checks given argument and interprets it as a mutable byte buffer
pub fn buf_mut<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> {
validate_ptr(base, len, true)?;
Ok(unsafe { core::slice::from_raw_parts_mut(base as *mut u8, len) })
}
/// Unwraps a nullable user buffer reference
pub fn validate_user_ptr_null<'a>(base: usize, len: usize) -> Result<Option<&'a mut [u8]>, Errno> {
/// Checks possibly NULL given argument and interprets it as a byte buffer
pub fn option_buf_ref<'a>(base: usize, len: usize) -> Result<Option<&'a [u8]>, Errno> {
if base == 0 {
Ok(None)
} else {
validate_user_ptr(base, len).map(Some)
buf_ref(base, len).map(Some)
}
}
/// Checks possibly NULL given argument and interprets it as a mutable byte buffer
pub fn option_buf_mut<'a>(base: usize, len: usize) -> Result<Option<&'a mut [u8]>, Errno> {
if base == 0 {
Ok(None)
} else {
buf_mut(base, len).map(Some)
}
}
/// Unwraps user string argument
pub fn validate_user_str<'a>(base: usize, len: usize) -> Result<&'a str, Errno> {
let bytes = validate_user_ptr(base, len)?;
pub fn str_ref<'a>(base: usize, len: usize) -> Result<&'a str, Errno> {
let bytes = buf_ref(base, len)?;
core::str::from_utf8(bytes).map_err(|_| {
warnln!(
"User string contains invalid UTF-8 characters: base={:#x}, len={:#x}",
@@ -77,49 +198,3 @@ pub fn validate_user_str<'a>(base: usize, len: usize) -> Result<&'a str, Errno>
Errno::InvalidArgument
})
}
// if base > mem::KERNEL_OFFSET {
// warnln!("User string refers to kernel memory: base={:#x}", base);
// return Err(Errno::InvalidArgument);
// }
//
// let base_ptr = base as *const u8;
// let mut len = 0;
// let mut page_valid = false;
// loop {
// if len == limit {
// warnln!("User string exceeded limit: base={:#x}", base);
// return Err(Errno::InvalidArgument);
// }
//
// if (base + len) % mem::PAGE_SIZE == 0 {
// page_valid = false;
// }
//
// if !page_valid && translate((base + len) & !0xFFF).is_none() {
// warnln!(
// "User string refers to unmapped memory: base={:#x}, off={:#x}",
// base,
// len
// );
// return Err(Errno::InvalidArgument);
// }
//
// page_valid = true;
//
// let byte = unsafe { *base_ptr.add(len) };
// if byte == 0 {
// break;
// }
//
// len += 1;
// }
//
// let slice = unsafe { core::slice::from_raw_parts(base_ptr, len) };
// core::str::from_utf8(slice).map_err(|_| {
// warnln!(
// "User string contains invalid UTF-8 characters: base={:#x}",
// base
// );
// Errno::InvalidArgument
// })
// }
+343 -102
View File
@@ -1,24 +1,30 @@
//! System call implementation
use crate::arch::platform::exception::ExceptionFrame;
use crate::arch::{machine, platform::exception::ExceptionFrame};
use crate::debug::Level;
use crate::proc::{elf, wait, Process, ProcessIo};
use crate::dev::timer::TimestampSource;
use crate::fs::create_filesystem;
use crate::mem::{phys::PageUsage, virt::MapAttributes};
use crate::proc::{self, elf, wait, Process, ProcessIo, Thread};
use core::mem::size_of;
use core::ops::DerefMut;
use core::time::Duration;
use libsys::{
abi,
abi::SystemCall,
debug::TraceLevel,
error::Errno,
ioctl::IoctlCmd,
proc::Pid,
proc::{ExitCode, MemoryAccess, Pid, Tid},
signal::{Signal, SignalDestination},
stat::{FdSet, FileDescriptor, FileMode, OpenFlags, Stat, AT_EMPTY_PATH},
stat::{
AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, GroupId, MountOptions,
OpenFlags, Stat, UserId, AT_EMPTY_PATH,
},
traits::{Read, Write},
};
use vfs::VnodeRef;
pub mod arg;
pub use arg::*;
/// Creates a "fork" process from current one using its register frame.
/// See [Process::fork()].
@@ -50,19 +56,28 @@ fn find_at_node<T: DerefMut<Target = ProcessIo>>(
}
}
/// Main system call dispatcher function
pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
match num {
// Process management system calls
abi::SYS_EXIT => {
Process::current().exit(args[0] as i32);
unreachable!();
}
// I/O
SystemCall::Read => {
let proc = Process::current();
let fd = FileDescriptor::from(args[0] as u32);
let mut io = proc.io.lock();
let buf = arg::buf_mut(args[1], args[2])?;
// I/O system calls
abi::SYS_OPENAT => {
io.file(fd)?.borrow_mut().read(buf)
}
SystemCall::Write => {
let proc = Process::current();
let fd = FileDescriptor::from(args[0] as u32);
let mut io = proc.io.lock();
let buf = arg::buf_ref(args[1], args[2])?;
io.file(fd)?.borrow_mut().write(buf)
}
SystemCall::Open => {
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
let path = validate_user_str(args[1], args[2])?;
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)?;
@@ -78,34 +93,7 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
let file = io.ioctx().open(at, path, mode, opts)?;
Ok(u32::from(io.place_file(file)?) as usize)
}
abi::SYS_READ => {
let proc = Process::current();
let fd = FileDescriptor::from(args[0] as u32);
let mut io = proc.io.lock();
let buf = validate_user_ptr(args[1], args[2])?;
io.file(fd)?.borrow_mut().read(buf)
}
abi::SYS_WRITE => {
let proc = Process::current();
let fd = FileDescriptor::from(args[0] as u32);
let mut io = proc.io.lock();
let buf = validate_user_ptr(args[1], args[2])?;
io.file(fd)?.borrow_mut().write(buf)
}
abi::SYS_FSTATAT => {
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
let filename = validate_user_str(args[1], args[2])?;
let buf = validate_user_ptr_struct::<Stat>(args[3])?;
let flags = args[4] as u32;
let proc = Process::current();
let mut io = proc.io.lock();
find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat(buf)?;
Ok(0)
}
abi::SYS_CLOSE => {
SystemCall::Close => {
let proc = Process::current();
let mut io = proc.io.lock();
let fd = FileDescriptor::from(args[0] as u32);
@@ -113,34 +101,20 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
io.close_file(fd)?;
Ok(0)
}
abi::SYS_EXECVE => {
let node = {
let proc = Process::current();
let mut io = proc.io.lock();
let filename = validate_user_str(args[0], args[1])?;
// 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(|space| elf::load_elf(space, file), 0).unwrap();
panic!();
}
abi::SYS_WAITPID => {
// TODO special "pid" values
let pid = unsafe { Pid::from_raw(args[0] as u32) };
let status = validate_user_ptr_struct::<i32>(args[1])?;
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;
match Process::waitpid(pid) {
Ok(exit) => {
*status = i32::from(exit);
Ok(0)
}
_ => todo!(),
}
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)
}
abi::SYS_IOCTL => {
SystemCall::Ioctl => {
let fd = FileDescriptor::from(args[0] as u32);
let cmd = IoctlCmd::try_from(args[1] as u32)?;
@@ -150,19 +124,196 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
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))
};
// Extra system calls
abi::SYS_EX_DEBUG_TRACE => {
let buf = validate_user_ptr(args[0], args[1])?;
print!(Level::Debug, "[trace] ");
for &byte in buf.iter() {
print!(Level::Debug, "{}", byte as char);
}
println!(Level::Debug, "");
Ok(args[1])
wait::select(Thread::current(), rfds, wfds, timeout)
}
abi::SYS_EX_NANOSLEEP => {
let rem_buf = validate_user_ptr_null(args[1], size_of::<u64>() * 2)?;
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 {
return Err(Errno::InvalidArgument);
}
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::SH_OUTER | MapAttributes::PXN;
if !acc.contains(MemoryAccess::READ) {
return Err(Errno::NotImplemented);
}
if acc.contains(MemoryAccess::WRITE) {
if acc.contains(MemoryAccess::EXEC) {
return Err(Errno::PermissionDenied);
}
attrs |= MapAttributes::AP_BOTH_READWRITE;
} else {
attrs |= MapAttributes::AP_BOTH_READONLY;
}
if !acc.contains(MemoryAccess::EXEC) {
attrs |= MapAttributes::UXN;
}
// TODO don't ignore flags
let usage = PageUsage::UserPrivate;
let proc = Process::current();
proc.manipulate_space(move |space| {
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) {
@@ -173,17 +324,15 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
}
res.map(|_| 0)
}
abi::SYS_EX_SIGNAL => {
let proc = Process::current();
proc.setup_signal_context(args[0], args[1]);
SystemCall::SetSignalEntry => {
Thread::current().set_signal_entry(args[0], args[1]);
Ok(0)
}
abi::SYS_EX_SIGRETURN => {
let proc = Process::current();
proc.return_from_signal();
panic!("This code won't run");
SystemCall::SignalReturn => {
Thread::current().return_from_signal();
unreachable!();
}
abi::SYS_EX_KILL => {
SystemCall::SendSignal => {
let target = SignalDestination::from(args[0] as isize);
let signal = Signal::try_from(args[1] as u32)?;
@@ -196,25 +345,117 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
};
Ok(0)
}
abi::SYS_SELECT => {
let rfds = validate_user_ptr_struct_option::<FdSet>(args[0])?;
let wfds = validate_user_ptr_struct_option::<FdSet>(args[1])?;
let timeout = if args[2] == 0 {
None
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 {
Some(Duration::from_nanos(args[2] as u64))
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();
wait::select(proc, rfds, wfds, timeout)
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)
}
_ => {
let proc = Process::current();
errorln!("Undefined system call: {}", num);
proc.enter_signal(Signal::InvalidSystemCall);
todo!()
// 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])?;
let proc = Process::current();
let mut io = proc.io.lock();
debugln!("mount(target={:?}, options={:#x?})", target, options);
let target_node = io.ioctx().find(None, target, true)?;
let root = create_filesystem(options)?;
target_node.mount(root)?;
Ok(0)
}
// Debugging
SystemCall::DebugTrace => {
let level = TraceLevel::from_repr(args[0])
.map(Level::from)
.ok_or(Errno::InvalidArgument)?;
let buf = arg::str_ref(args[1], args[2])?;
let thread = Thread::current();
let proc = thread.owner().unwrap();
println!(level, "[trace {:?}:{:?}] {}", proc.id(), thread.id(), buf);
Ok(args[1])
}
// Handled elsewhere
SystemCall::Fork => unreachable!(),
}
}
/// Main system call dispatcher function
pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
let thread = Thread::current();
let process = thread.owner().unwrap();
let result = _syscall(num, args);
if !thread.is_handling_signal() {
process.handle_pending_signals();
}
result
}
+1
View File
@@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
bitflags = "^1.3.0"
enum-repr = "^0.2.6"
[features]
user = []
+49 -16
View File
@@ -1,18 +1,51 @@
pub const SYS_EX_DEBUG_TRACE: usize = 128;
pub const SYS_EX_NANOSLEEP: usize = 129;
use enum_repr::EnumRepr;
pub const SYS_EX_SIGNAL: usize = 130;
pub const SYS_EX_SIGRETURN: usize = 131;
pub const SYS_EX_KILL: usize = 132;
#[EnumRepr(type = "usize")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum SystemCall {
// I/O
Read = 1,
Write = 2,
Open = 3,
Close = 4,
FileStatus = 5,
Ioctl = 6,
Select = 7,
Access = 8,
ReadDirectory = 9,
GetUserId = 10,
GetGroupId = 11,
DuplicateFd = 12,
SetUserId = 13,
SetGroupId = 14,
SetCurrentDirectory = 15,
GetCurrentDirectory = 16,
Seek = 17,
MapMemory = 18,
UnmapMemory = 19,
pub const SYS_EXIT: usize = 1;
pub const SYS_READ: usize = 2;
pub const SYS_WRITE: usize = 3;
pub const SYS_OPENAT: usize = 4;
pub const SYS_FSTATAT: usize = 5;
pub const SYS_CLOSE: usize = 6;
pub const SYS_FORK: usize = 7;
pub const SYS_EXECVE: usize = 8;
pub const SYS_WAITPID: usize = 9;
pub const SYS_IOCTL: usize = 10;
pub const SYS_SELECT: usize = 11;
// Process manipulation
Fork = 32,
Clone = 33,
Exec = 34,
Exit = 35,
WaitPid = 36,
WaitTid = 37,
GetPid = 38,
GetTid = 39,
Sleep = 40,
SetSignalEntry = 41,
SignalReturn = 42,
SendSignal = 43,
Yield = 44,
GetSid = 45,
GetPgid = 46,
GetPpid = 47,
SetSid = 48,
SetPgid = 49,
// System
GetCpuTime = 64,
Mount = 65,
// Debugging
DebugTrace = 128
}
+225 -62
View File
@@ -1,53 +1,60 @@
use crate::abi;
use crate::abi::SystemCall;
use core::arch::asm;
use crate::{
debug::TraceLevel,
error::Errno,
ioctl::IoctlCmd,
proc::{ExitCode, Pid},
proc::{ExitCode, MemoryAccess, MemoryMap, Pid, Tid},
signal::{Signal, SignalDestination},
stat::{FdSet, FileDescriptor, FileMode, OpenFlags, Stat},
stat::{
AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, GroupId, MountOptions,
OpenFlags, Stat, UserId,
},
};
use core::time::Duration;
// TODO document the syscall ABI
// TODO move this to libusr
macro_rules! syscall {
($num:expr) => {{
let mut res: usize;
asm!("svc #0", out("x0") res, in("x8") $num, options(nostack));
asm!("svc #0", out("x0") res, in("x8") $num.repr(), options(nostack));
res
}};
($num:expr, $a0:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res,
in("x8") $num, options(nostack));
in("x8") $num.repr(), options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1,
in("x8") $num, options(nostack));
in("x8") $num.repr(), options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x8") $num, options(nostack));
in("x8") $num.repr(), options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x8") $num, options(nostack));
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;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x4") $a4, in("x8") $num, options(nostack));
in("x3") $a3, in("x4") $a4, in("x8") $num.repr(), options(nostack));
res
}};
}
@@ -69,52 +76,38 @@ macro_rules! argp {
// ($a:expr) => ($a as *const core::ffi::c_void as usize)
// }
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_exit(code: ExitCode) -> ! {
unsafe {
syscall!(abi::SYS_EXIT, argn!(i32::from(code)));
syscall!(SystemCall::Exit, argn!(i32::from(code)), argn!(0));
}
unreachable!();
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_close(fd: FileDescriptor) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe { syscall!(abi::SYS_CLOSE, argn!(u32::from(fd))) })
Errno::from_syscall_unit(unsafe { syscall!(SystemCall::Close, argn!(u32::from(fd))) })
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_ex_nanosleep(ns: u64, rem: &mut [u64; 2]) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(abi::SYS_EX_NANOSLEEP, argn!(ns), argp!(rem.as_mut_ptr()))
syscall!(SystemCall::Sleep, argn!(ns), argp!(rem.as_mut_ptr()))
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_ex_debug_trace(msg: &[u8]) -> Result<(), Errno> {
pub fn sys_ex_debug_trace(level: TraceLevel, msg: &[u8]) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
abi::SYS_EX_DEBUG_TRACE,
SystemCall::DebugTrace,
argn!(level.repr()),
argp!(msg.as_ptr()),
argn!(msg.len())
)
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_openat(
at: Option<FileDescriptor>,
@@ -124,7 +117,7 @@ pub fn sys_openat(
) -> Result<FileDescriptor, Errno> {
Errno::from_syscall(unsafe {
syscall!(
abi::SYS_OPENAT,
SystemCall::Open,
argn!(FileDescriptor::into_i32(at)),
argp!(pathname.as_ptr()),
argn!(pathname.len()),
@@ -135,14 +128,11 @@ pub fn sys_openat(
.map(|e| FileDescriptor::from(e as u32))
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_read(fd: FileDescriptor, data: &mut [u8]) -> Result<usize, Errno> {
Errno::from_syscall(unsafe {
syscall!(
abi::SYS_READ,
SystemCall::Read,
argn!(u32::from(fd)),
argp!(data.as_mut_ptr()),
argn!(data.len())
@@ -154,7 +144,7 @@ pub fn sys_read(fd: FileDescriptor, data: &mut [u8]) -> Result<usize, Errno> {
pub fn sys_write(fd: FileDescriptor, data: &[u8]) -> Result<usize, Errno> {
Errno::from_syscall(unsafe {
syscall!(
abi::SYS_WRITE,
SystemCall::Write,
argn!(u32::from(fd)),
argp!(data.as_ptr()),
argn!(data.len())
@@ -162,9 +152,6 @@ pub fn sys_write(fd: FileDescriptor, data: &[u8]) -> Result<usize, Errno> {
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_fstatat(
at: Option<FileDescriptor>,
@@ -174,7 +161,7 @@ pub fn sys_fstatat(
) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
abi::SYS_FSTATAT,
SystemCall::FileStatus,
argn!(FileDescriptor::into_i32(at)),
argp!(pathname.as_ptr()),
argn!(pathname.len()),
@@ -188,47 +175,40 @@ pub fn sys_fstatat(
///
/// System call
#[inline(always)]
pub fn sys_fork() -> Result<Option<Pid>, Errno> {
Errno::from_syscall(unsafe { syscall!(abi::SYS_FORK) }).map(|res| {
pub unsafe fn sys_fork() -> Result<Option<Pid>, Errno> {
Errno::from_syscall(syscall!(SystemCall::Fork)).and_then(|res| {
if res != 0 {
Some(unsafe { Pid::from_raw(res as u32) })
Pid::try_from(res as u32).map(Some)
} else {
None
Ok(None)
}
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_execve(pathname: &str) -> Result<(), Errno> {
pub fn sys_execve(pathname: &str, argv: &[&str]) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
abi::SYS_EXECVE,
SystemCall::Exec,
argp!(pathname.as_ptr()),
argn!(pathname.len())
argn!(pathname.len()),
argp!(argv.as_ptr()),
argn!(argv.len())
)
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_waitpid(pid: Pid, status: &mut i32) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
abi::SYS_WAITPID,
argn!(pid.value()),
SystemCall::WaitPid,
argn!(u32::from(pid)),
argp!(status as *mut i32)
)
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_ioctl(
fd: FileDescriptor,
@@ -238,7 +218,7 @@ pub fn sys_ioctl(
) -> Result<usize, Errno> {
Errno::from_syscall(unsafe {
syscall!(
abi::SYS_IOCTL,
SystemCall::Ioctl,
argn!(u32::from(fd)),
argn!(cmd),
argn!(ptr),
@@ -247,15 +227,23 @@ pub fn sys_ioctl(
})
}
#[inline(always)]
pub fn sys_ex_getcputime() -> Result<Duration, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::GetCpuTime) })
.map(|e| Duration::from_nanos(e as u64))
}
#[inline(always)]
pub fn sys_ex_signal(entry: usize, stack: usize) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe { syscall!(abi::SYS_EX_SIGNAL, argn!(entry), argn!(stack)) })
Errno::from_syscall_unit(unsafe {
syscall!(SystemCall::SetSignalEntry, argn!(entry), argn!(stack))
})
}
#[inline(always)]
pub fn sys_ex_sigreturn() -> ! {
unsafe {
syscall!(abi::SYS_EX_SIGRETURN);
syscall!(SystemCall::SignalReturn);
}
unreachable!();
}
@@ -264,13 +252,41 @@ pub fn sys_ex_sigreturn() -> ! {
pub fn sys_ex_kill(pid: SignalDestination, signum: Signal) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
abi::SYS_EX_KILL,
SystemCall::SendSignal,
argn!(isize::from(pid)),
argn!(signum as u32)
)
})
}
#[inline(always)]
pub fn sys_ex_clone(entry: usize, stack: usize, arg: usize) -> Result<Tid, Errno> {
Errno::from_syscall(unsafe {
syscall!(SystemCall::Clone, argn!(entry), argn!(stack), argn!(arg))
}).map(|e| Tid::from(e as u32))
}
#[inline(always)]
pub fn sys_ex_thread_exit(status: ExitCode) -> ! {
unsafe {
syscall!(SystemCall::Exit, argn!(i32::from(status)), argn!(1));
}
unreachable!();
}
#[inline(always)]
pub fn sys_ex_thread_wait(tid: Tid) -> Result<ExitCode, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::WaitTid, argn!(u32::from(tid))) })
.map(|_| ExitCode::from(0))
}
#[inline(always)]
pub fn sys_ex_yield() {
unsafe {
syscall!(SystemCall::Yield);
}
}
#[inline(always)]
pub fn sys_select(
read_fds: Option<&mut FdSet>,
@@ -279,7 +295,7 @@ pub fn sys_select(
) -> Result<usize, Errno> {
Errno::from_syscall(unsafe {
syscall!(
abi::SYS_SELECT,
SystemCall::Select,
argp!(read_fds
.map(|e| e as *mut _)
.unwrap_or(core::ptr::null_mut())),
@@ -290,3 +306,150 @@ pub fn sys_select(
)
})
}
#[inline(always)]
pub fn sys_faccessat(
fd: Option<FileDescriptor>,
name: &str,
mode: AccessMode,
flags: u32,
) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
SystemCall::Access,
argn!(FileDescriptor::into_i32(fd)),
argp!(name.as_ptr()),
argn!(name.len()),
argn!(mode.bits()),
argn!(flags)
)
})
}
#[inline(always)]
pub fn sys_ex_gettid() -> Tid {
Tid::from(unsafe { syscall!(SystemCall::GetTid) as u32 })
}
#[inline(always)]
pub fn sys_getpid() -> Pid {
Pid::try_from(unsafe { syscall!(SystemCall::GetPid) as u32 }).unwrap()
}
#[inline(always)]
pub fn sys_getpgid(pid: Option<Pid>) -> Result<Pid, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::GetPgid, argn!(Pid::from_option(pid))) })
.and_then(|e| Pid::try_from(e as u32))
}
#[inline(always)]
pub fn sys_setpgid(pid: Option<Pid>, pgid: Option<Pid>) -> Result<Pid, Errno> {
Errno::from_syscall(unsafe {
syscall!(
SystemCall::SetPgid,
argn!(Pid::from_option(pid)),
argn!(Pid::from_option(pgid))
)
})
.and_then(|e| Pid::try_from(e as u32))
}
#[inline(always)]
pub fn sys_readdir(fd: FileDescriptor, buf: &mut [DirectoryEntry]) -> Result<usize, Errno> {
Errno::from_syscall(unsafe {
syscall!(
SystemCall::ReadDirectory,
argn!(u32::from(fd)),
argp!(buf.as_mut_ptr()),
argn!(buf.len())
)
})
}
#[inline(always)]
pub fn sys_getuid() -> UserId {
UserId::from(unsafe { syscall!(SystemCall::GetUserId) as u32 })
}
#[inline(always)]
pub fn sys_getgid() -> GroupId {
GroupId::from(unsafe { syscall!(SystemCall::GetGroupId) as u32 })
}
#[inline(always)]
pub fn sys_setsid() -> Result<Pid, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::SetSid) })
.and_then(|e| Pid::try_from(e as u32))
}
#[inline(always)]
pub fn sys_mount(target: &str, options: &MountOptions) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
SystemCall::Mount,
argp!(target.as_ptr()),
argn!(target.len()),
argp!(options as *const _)
)
})
}
#[inline(always)]
pub fn sys_dup(src: FileDescriptor, dst: Option<FileDescriptor>) -> Result<FileDescriptor, Errno> {
Errno::from_syscall(unsafe {
syscall!(
SystemCall::DuplicateFd,
argn!(u32::from(src)),
argn!(FileDescriptor::into_i32(dst))
)
})
.map(|e| FileDescriptor::from(e as u32))
}
#[inline(always)]
pub fn sys_setuid(uid: UserId) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe { syscall!(SystemCall::SetUserId, u32::from(uid) as usize) })
}
#[inline(always)]
pub fn sys_setgid(gid: GroupId) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe { syscall!(SystemCall::SetGroupId, u32::from(gid) as usize) })
}
#[inline(always)]
pub fn sys_chdir(path: &str) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
SystemCall::SetCurrentDirectory,
argp!(path.as_ptr()),
argn!(path.len())
)
})
}
#[inline(always)]
pub fn sys_mmap(
hint: usize,
len: usize,
acc: MemoryAccess,
flags: MemoryMap,
) -> Result<usize, Errno> {
Errno::from_syscall(unsafe {
syscall!(
SystemCall::MapMemory,
argn!(hint),
argn!(len),
argn!(acc.bits()),
argn!(flags.bits())
)
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub unsafe fn sys_munmap(addr: usize, len: usize) -> Result<(), Errno> {
Errno::from_syscall_unit(syscall!(SystemCall::UnmapMemory, argn!(addr), argn!(len)))
}
+10
View File
@@ -0,0 +1,10 @@
use enum_repr::EnumRepr;
#[EnumRepr(type = "usize")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum TraceLevel {
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
}
+2 -1
View File
@@ -15,6 +15,7 @@ pub enum Errno {
NotADirectory,
NotImplemented,
OutOfMemory,
PermissionDenied,
ReadOnly,
TimedOut,
TooManyDescriptors,
@@ -47,6 +48,6 @@ impl Errno {
impl From<usize> for Errno {
fn from(u: usize) -> Errno {
todo!()
unsafe { core::mem::transmute(u as u32) }
}
}
+2
View File
@@ -7,6 +7,7 @@ use crate::error::Errno;
pub enum IoctlCmd {
TtySetAttributes = 1,
TtyGetAttributes = 2,
TtySetPgrp = 3,
}
impl TryFrom<u32> for IoctlCmd {
@@ -17,6 +18,7 @@ impl TryFrom<u32> for IoctlCmd {
match u {
1 => Ok(Self::TtySetAttributes),
2 => Ok(Self::TtyGetAttributes),
3 => Ok(Self::TtySetPgrp),
_ => Err(Errno::InvalidArgument)
}
}
+58 -1
View File
@@ -1,10 +1,10 @@
#![feature(asm, const_panic)]
#![no_std]
#[macro_use]
extern crate bitflags;
pub mod abi;
pub mod debug;
pub mod error;
pub mod ioctl;
pub mod mem;
@@ -15,6 +15,63 @@ pub mod stat;
pub mod termios;
pub mod traits;
#[derive(Debug)]
pub struct ProgramArgs {
pub argv: usize,
pub argc: usize,
pub storage: usize,
pub size: usize
}
// TODO utils
use core::fmt;
#[derive(Clone, Copy)]
pub struct FixedStr<const N: usize> {
len: usize,
data: [u8; N],
}
impl<const N: usize> FixedStr<N> {
pub const fn empty() -> Self {
Self {
len: 0,
data: [0; N]
}
}
pub fn copy_from_str(&mut self, src: &str) {
if src.len() > self.data.len() {
panic!("copy_from_str: src len > data len");
}
self.len = src.len();
self.data[..self.len].copy_from_slice(src.as_bytes());
}
pub fn as_str(&self) -> &str {
unsafe {
core::str::from_utf8_unchecked(&self.data[..self.len])
}
}
}
impl<const N: usize> fmt::Debug for FixedStr<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"")?;
fmt::Display::fmt(self, f)?;
write!(f, "\"")
}
}
impl<const N: usize> fmt::Display for FixedStr<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &byte in &self.data[..self.len] {
write!(f, "{}", byte as char)?;
}
Ok(())
}
}
#[cfg(feature = "user")]
pub mod calls;
#[cfg(feature = "user")]
+7 -6
View File
@@ -13,7 +13,7 @@ pub fn read_le16(src: &[u8]) -> u16 {
/// Unsafe: writes to arbitrary memory locations, performs no pointer
/// validation.
#[no_mangle]
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *mut u8, mut len: usize) -> *mut u8 {
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *const u8, mut len: usize) -> *mut u8 {
while len != 0 {
len -= 1;
*dst.add(len) = *src.add(len);
@@ -28,15 +28,16 @@ pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *mut u8, mut len: usize) -> *
/// Unsafe: performs reads from arbitrary memory locations, performs no
/// pointer validation.
#[no_mangle]
pub unsafe extern "C" fn memcmp(a: *mut u8, b: *mut u8, mut len: usize) -> isize {
while len != 0 {
len -= 1;
if *a.add(len) < *b.add(len) {
pub unsafe extern "C" fn memcmp(a: *mut u8, b: *mut u8, len: usize) -> isize {
let mut off = 0;
while off != len {
if *a.add(off) < *b.add(off) {
return -1;
}
if *a.add(len) > *b.add(len) {
if *a.add(off) > *b.add(off) {
return 1;
}
off += 1;
}
0
}
+82 -14
View File
@@ -12,10 +12,33 @@ pub struct ExitCode(i32);
#[repr(transparent)]
pub struct Pid(u32);
/// Wrapper type for thread ID
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
#[repr(transparent)]
pub struct Tid(u32);
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
#[repr(transparent)]
pub struct Pgid(u32);
bitflags! {
pub struct MemoryAccess: u32 {
const READ = 1 << 0;
const WRITE = 1 << 1;
const EXEC = 1 << 2;
}
}
bitflags! {
pub struct MemoryMap: u32 {
const BACKEND = 0x3;
const ANONYMOUS = 1;
const SHARING = 0x3 << 2;
const PRIVATE = 1 << 2;
}
}
impl From<i32> for ExitCode {
fn from(f: i32) -> Self {
Self(f)
@@ -35,14 +58,15 @@ impl From<ExitCode> for i32 {
}
impl Pid {
/// Kernel idle process always has PID of zero
pub const IDLE: Self = Self(Self::KERNEL_BIT);
const KERNEL_BIT: u32 = 1 << 31;
const USER_MAX: u32 = 256;
/// Constructs an instance of user-space PID
pub const fn user(id: u32) -> Self {
assert!(id < 256, "PID is too high");
assert!(id < Self::USER_MAX, "PID is too high");
if id == 0 {
panic!("User PID cannot be zero");
}
Self(id)
}
@@ -65,18 +89,20 @@ impl Pid {
self.0 as u8
}
/// Returns bit value of this pid
pub const fn value(self) -> u32 {
self.0
pub fn from_option(m: Option<Self>) -> u32 {
if let Some(pid) = m {
u32::from(pid)
} else {
0
}
}
/// Constructs [Pid] from raw [u32] value
///
/// # Safety
///
/// Unsafe: does not check `num`
pub const unsafe fn from_raw(num: u32) -> Self {
Self(num)
pub fn to_option(m: u32) -> Option<Self> {
if m != 0 {
Some(Self::try_from(m).unwrap())
} else {
None
}
}
}
@@ -91,6 +117,26 @@ impl fmt::Debug for Pid {
}
}
impl TryFrom<u32> for Pid {
type Error = Errno;
fn try_from(raw: u32) -> Result<Pid, Errno> {
if raw & Self::KERNEL_BIT != 0 {
Ok(Pid::kernel(raw & !Self::KERNEL_BIT))
} else if raw != 0 && raw < Self::USER_MAX {
Ok(Pid::user(raw))
} else {
Err(Errno::InvalidArgument)
}
}
}
impl From<Pid> for u32 {
fn from(pid: Pid) -> u32 {
pid.0
}
}
impl TryFrom<Pid> for Pgid {
type Error = Errno;
@@ -114,3 +160,25 @@ impl From<Pgid> for u32 {
p.0
}
}
impl Tid {
pub const IDLE: Tid = Tid(0);
}
impl fmt::Debug for Tid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Tid(#{})", self.0)
}
}
impl From<u32> for Tid {
fn from(p: u32) -> Tid {
Self(p)
}
}
impl From<Tid> for u32 {
fn from(p: Tid) -> u32 {
p.0
}
}
+1 -1
View File
@@ -37,7 +37,7 @@ impl From<isize> for SignalDestination {
impl From<SignalDestination> for isize {
fn from(p: SignalDestination) -> isize {
match p {
SignalDestination::Process(pid) => pid.value() as isize,
SignalDestination::Process(pid) => u32::from(pid) as isize,
SignalDestination::Group(pgid) => -(u32::from(pgid) as isize),
SignalDestination::This => 0,
SignalDestination::All => -1
+163 -12
View File
@@ -1,5 +1,7 @@
use core::fmt;
// TODO split up this file
use crate::error::Errno;
use core::str::FromStr;
use core::fmt;
const AT_FDCWD: i32 = -2;
pub const AT_EMPTY_PATH: u32 = 1 << 16;
@@ -14,11 +16,18 @@ bitflags! {
const O_CREAT = 1 << 4;
const O_EXEC = 1 << 5;
const O_CLOEXEC = 1 << 6;
const O_DIRECTORY = 1 << 7;
const O_CTTY = 1 << 8;
}
}
bitflags! {
pub struct FileMode: u32 {
const FILE_TYPE = 0xF << 12;
const S_IFREG = 0x8 << 12;
const S_IFDIR = 0x4 << 12;
const S_IFCHR = 0x2 << 12;
const USER_READ = 1 << 8;
const USER_WRITE = 1 << 7;
const USER_EXEC = 1 << 6;
@@ -31,28 +40,135 @@ bitflags! {
}
}
bitflags! {
pub struct AccessMode: u32 {
const R_OK = 1 << 0;
const W_OK = 1 << 1;
const X_OK = 1 << 2;
const F_OK = 1 << 3;
}
}
#[derive(Clone, Debug)]
pub struct MountOptions<'a> {
pub device: Option<&'a str>,
pub fs: Option<&'a str>,
// TODO flags etc.
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct UserId(u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct GroupId(u32);
impl UserId {
pub const fn root() -> Self {
Self(0)
}
pub const fn is_root(self) -> bool {
self.0 == 0
}
}
impl From<u32> for UserId {
#[inline(always)]
fn from(v: u32) -> Self {
Self(v)
}
}
impl From<UserId> for u32 {
#[inline(always)]
fn from(v: UserId) -> u32 {
v.0
}
}
impl GroupId {
pub const fn root() -> Self {
Self(0)
}
pub const fn is_root(self) -> bool {
self.0 == 0
}
}
impl From<u32> for GroupId {
#[inline(always)]
fn from(v: u32) -> Self {
Self(v)
}
}
impl From<GroupId> for u32 {
#[inline(always)]
fn from(v: GroupId) -> u32 {
v.0
}
}
#[derive(Clone, Default)]
pub struct FdSet {
bits: [u64; 2]
bits: [u64; 2],
}
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct FileDescriptor(u32);
#[derive(Clone, Copy)]
pub struct DirectoryEntry {
name: [u8; 64],
}
struct FdSetIter<'a> {
idx: u32,
set: &'a FdSet
set: &'a FdSet,
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct Stat {
pub mode: u32,
pub mode: FileMode,
pub size: u64,
pub blksize: u32,
}
impl DirectoryEntry {
pub const fn empty() -> Self {
Self { name: [0; 64] }
}
pub fn as_str(&self) -> &str {
let zero = self.name.iter().position(|&c| c == 0).unwrap();
core::str::from_utf8(&self.name[..zero]).unwrap()
}
}
impl FromStr for DirectoryEntry {
type Err = Errno;
fn from_str(i: &str) -> Result<Self, Errno> {
let mut res = DirectoryEntry { name: [0; 64] };
let bytes = i.as_bytes();
res.name[..bytes.len()].copy_from_slice(bytes);
Ok(res)
}
}
impl fmt::Debug for DirectoryEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("DirectoryEntry")
.field("name", &self.as_str())
.finish()
}
}
impl FdSet {
pub const fn empty() -> Self {
Self { bits: [0; 2] }
@@ -84,10 +200,7 @@ impl FdSet {
}
pub fn iter(&self) -> impl Iterator<Item = FileDescriptor> + '_ {
FdSetIter {
idx: 0,
set: self
}
FdSetIter { idx: 0, set: self }
}
}
@@ -122,13 +235,51 @@ impl fmt::Debug for FdSet {
impl FileMode {
/// Returns default permission set for directories
pub const fn default_dir() -> Self {
unsafe { Self::from_bits_unchecked(0o755) }
pub fn default_dir() -> Self {
unsafe { Self::from_bits_unchecked(0o755) | Self::S_IFDIR }
}
/// Returns default permission set for regular files
pub const fn default_reg() -> Self {
unsafe { Self::from_bits_unchecked(0o644) }
pub fn default_reg() -> Self {
unsafe { Self::from_bits_unchecked(0o644) | Self::S_IFREG }
}
}
fn choose<T>(q: bool, a: T, b: T) -> T {
if q { a } else { b }
}
impl Default for FileMode {
fn default() -> Self {
unsafe { Self::from_bits_unchecked(0) }
}
}
impl fmt::Display for FileMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}{}{}{}{}{}{}{}{}{}",
// File type
match *self & Self::FILE_TYPE {
Self::S_IFCHR => 'c',
Self::S_IFDIR => 'd',
Self::S_IFREG => '-',
_ => '?'
},
// User
choose(self.contains(Self::USER_READ), 'r', '-'),
choose(self.contains(Self::USER_WRITE), 'w', '-'),
choose(self.contains(Self::USER_EXEC), 'x', '-'),
// Group
choose(self.contains(Self::GROUP_READ), 'r', '-'),
choose(self.contains(Self::GROUP_WRITE), 'w', '-'),
choose(self.contains(Self::GROUP_EXEC), 'x', '-'),
// Other
choose(self.contains(Self::OTHER_READ), 'r', '-'),
choose(self.contains(Self::OTHER_WRITE), 'w', '-'),
choose(self.contains(Self::OTHER_EXEC), 'x', '-'),
)
}
}
+4
View File
@@ -8,3 +8,7 @@ edition = "2021"
[dependencies]
libsys = { path = "../libsys", features = ["user"] }
lazy_static = { version = "^1.4.0", features = ["spin_no_std"] }
memoffset = "^0.6.4"
[features]
verbose = []
+266
View File
@@ -0,0 +1,266 @@
use core::alloc::{GlobalAlloc, Layout};
use core::mem::{size_of, MaybeUninit};
use core::ptr::null_mut;
use libsys::{
calls::{sys_mmap, sys_munmap},
error::Errno,
proc::{MemoryAccess, MemoryMap},
};
use memoffset::offset_of;
use crate::trace_debug;
struct Allocator;
const BLOCK_MAGIC: u32 = 0xBADB10C0;
const BLOCK_MAGIC_MASK: u32 = 0xFFFFFFF0;
const BLOCK_ALLOC: u32 = 1 << 0;
const SMALL_ZONE_ELEM: usize = 256;
const SMALL_ZONE_SIZE: usize = 6 * 0x1000;
const MID_ZONE_ELEM: usize = 2048;
const MID_ZONE_SIZE: usize = 24 * 0x1000;
const LARGE_ZONE_ELEM: usize = 8192;
const LARGE_ZONE_SIZE: usize = 48 * 0x1000;
struct ZoneList {
prev: *mut ZoneList,
next: *mut ZoneList,
}
#[repr(C)]
struct Zone {
size: usize,
list: ZoneList,
}
#[repr(C)]
struct Block {
prev: *mut Block,
next: *mut Block,
flags: u32,
size: u32,
}
static mut SMALL_ZONE_LIST: MaybeUninit<ZoneList> = MaybeUninit::uninit();
static mut MID_ZONE_LIST: MaybeUninit<ZoneList> = MaybeUninit::uninit();
static mut LARGE_ZONE_LIST: MaybeUninit<ZoneList> = MaybeUninit::uninit();
impl ZoneList {
fn init(&mut self) {
self.prev = self;
self.next = self;
}
unsafe fn init_uninit(list: &mut MaybeUninit<Self>) {
list.assume_init_mut().init()
}
fn add(&mut self, new: *mut ZoneList) {
let new = unsafe { &mut *new };
let next = unsafe { &mut *self.next };
next.prev = new;
new.next = next;
new.prev = self;
self.next = new;
}
fn del(&mut self) {
let prev = unsafe { &mut *self.prev };
let next = unsafe { &mut *self.next };
next.prev = prev;
prev.next = next;
}
}
impl Zone {
fn alloc(size: usize) -> Result<*mut Self, Errno> {
let pages = sys_mmap(
0,
size,
MemoryAccess::READ | MemoryAccess::WRITE,
MemoryMap::ANONYMOUS | MemoryMap::PRIVATE,
)?;
#[cfg(feature = "verbose")]
trace_debug!("Zone::alloc({}) => {:#x}", size, pages);
let zone_ptr = pages as *mut Zone;
let head_ptr = (pages + size_of::<Zone>()) as *mut Block;
let zone = unsafe { &mut *zone_ptr };
let head = unsafe { &mut *head_ptr };
zone.list.init();
zone.size = size - size_of::<Zone>();
head.size = (size - (size_of::<Zone>() + size_of::<Block>())) as u32;
head.flags = BLOCK_MAGIC;
head.prev = null_mut();
head.next = null_mut();
Ok(zone)
}
unsafe fn free(zone: *mut Self) {
#[cfg(feature = "verbose")]
trace_debug!("Zone::free({:p})", zone);
sys_munmap(zone as usize, (*zone).size + size_of::<Zone>())
.expect("Failed to unmap heap pages");
}
fn get(item: *mut ZoneList) -> *mut Zone {
((item as usize) - offset_of!(Zone, list)) as *mut Zone
}
}
unsafe fn zone_alloc(zone: &mut Zone, size: usize) -> *mut u8 {
assert_eq!(size & 15, 0);
let begin = ((zone as *mut _ as usize) + size_of::<Zone>()) as *mut Block;
let mut block = begin;
while !block.is_null() {
let block_ref = &mut *block;
if block_ref.flags & BLOCK_ALLOC != 0 {
block = block_ref.next;
continue;
}
if size == block_ref.size as usize {
block_ref.flags |= BLOCK_ALLOC;
let ptr = block.add(1) as *mut u8;
// TODO fill with zeros
return ptr;
} else if block_ref.size as usize >= size + size_of::<Block>() {
let cur_next = block_ref.next;
let cur_next_ref = &mut *cur_next;
let new_block = ((block as usize) + size_of::<Block>() + size) as *mut Block;
let new_block_ref = &mut *new_block;
if !cur_next.is_null() {
cur_next_ref.prev = new_block;
}
new_block_ref.next = cur_next;
new_block_ref.prev = block;
new_block_ref.size = ((block_ref.size as usize) - size_of::<Block>() - size) as u32;
new_block_ref.flags = BLOCK_MAGIC;
block_ref.next = new_block;
block_ref.size = size as u32;
block_ref.flags |= BLOCK_ALLOC;
let ptr = block.add(1) as *mut u8;
// TODO fill with zeros
return ptr;
}
block = block_ref.next;
}
null_mut()
}
unsafe fn alloc_from(list: &mut ZoneList, zone_size: usize, size: usize) -> *mut u8 {
loop {
let mut zone = list.next;
while zone != list {
let ptr = zone_alloc(&mut *Zone::get(zone), size);
if !ptr.is_null() {
return ptr;
}
zone = (*zone).next;
}
let zone = match Zone::alloc(zone_size) {
Ok(zone) => zone,
Err(e) => {
trace_debug!("Zone alloc failed: {:?}", e);
return null_mut();
}
};
list.add(&mut (*zone).list);
}
}
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
assert!(layout.align() < 16);
let size = (layout.size() + 15) & !15;
#[cfg(feature = "verbose")]
trace_debug!("alloc({:?})", layout);
if size <= SMALL_ZONE_ELEM {
alloc_from(SMALL_ZONE_LIST.assume_init_mut(), SMALL_ZONE_SIZE, size)
} else if size <= MID_ZONE_ELEM {
alloc_from(MID_ZONE_LIST.assume_init_mut(), MID_ZONE_SIZE, size)
} else if size <= LARGE_ZONE_ELEM {
alloc_from(LARGE_ZONE_LIST.assume_init_mut(), LARGE_ZONE_SIZE, size)
} else {
todo!();
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
#[cfg(feature = "verbose")]
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;
if block_ref.flags & BLOCK_MAGIC_MASK != BLOCK_MAGIC {
panic!("Heap block is malformed: block={:p}, ptr={:p}", block, ptr);
}
if block_ref.flags & BLOCK_ALLOC == 0 {
panic!(
"Double free error in heap: block={:p}, ptr={:p}",
block, ptr
);
}
block_ref.flags &= !BLOCK_ALLOC;
let prev = block_ref.prev;
let next = block_ref.next;
let prev_ref = &mut *prev;
let next_ref = &mut *next;
if !prev.is_null() && prev_ref.flags & BLOCK_ALLOC == 0 {
block_ref.flags = 0;
prev_ref.next = next;
if !next.is_null() {
next_ref.prev = prev;
}
prev_ref.size += (block_ref.size as usize + size_of::<Block>()) as u32;
block = prev;
block_ref = &mut *block;
}
if !next.is_null() && next_ref.flags & BLOCK_ALLOC == 0 {
next_ref.flags = 0;
if !next_ref.next.is_null() {
(*next_ref.next).prev = block;
}
block_ref.next = next_ref.next;
block_ref.size += (next_ref.size as usize + size_of::<Block>()) as u32;
}
if block_ref.prev.is_null() && block_ref.next.is_null() {
let zone = (block as usize - size_of::<Zone>()) as *mut Zone;
assert_eq!((zone as usize) & 0xFFF, 0);
(*zone).list.del();
Zone::free(zone);
}
}
}
#[alloc_error_handler]
fn alloc_error_handler(layout: Layout) -> ! {
panic!("Allocation failed: {:?}", layout);
}
#[global_allocator]
static ALLOC: Allocator = Allocator;
pub unsafe fn init() {
ZoneList::init_uninit(&mut SMALL_ZONE_LIST);
ZoneList::init_uninit(&mut MID_ZONE_LIST);
ZoneList::init_uninit(&mut LARGE_ZONE_LIST);
}
+30
View File
@@ -0,0 +1,30 @@
use crate::trace;
use alloc::vec::Vec;
use libsys::{
debug::TraceLevel,
ProgramArgs,
};
mod passwd;
pub use passwd::UserInfo;
mod shadow;
pub use shadow::UserShadow;
static mut PROGRAM_ARGS: Vec<&'static str> = Vec::new();
pub fn args() -> &'static [&'static str] {
unsafe { &PROGRAM_ARGS }
}
pub(crate) unsafe fn setup_env(arg: &ProgramArgs) {
for i in 0..arg.argc {
let base = core::ptr::read((arg.argv + i * 16) as *const *const u8);
let len = core::ptr::read((arg.argv + i * 16 + 8) as *const usize);
let string = core::str::from_utf8(core::slice::from_raw_parts(base, len)).unwrap();
PROGRAM_ARGS.push(string);
}
#[cfg(feature = "verbose")]
trace!(TraceLevel::Debug, "args = {:?}", PROGRAM_ARGS);
}
+99
View File
@@ -0,0 +1,99 @@
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}};
#[derive(Debug, Clone, Copy)]
pub struct UserInfo {
name: FixedStr<32>,
uid: UserId,
gid: GroupId,
home: FixedStr<64>,
shell: FixedStr<64>,
}
impl UserInfo {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn home(&self) -> &str {
self.home.as_str()
}
pub fn shell(&self) -> &str {
self.shell.as_str()
}
pub fn uid(&self) -> UserId {
self.uid
}
pub fn gid(&self) -> GroupId {
self.gid
}
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, ()> {
let mut file = File::open("/etc/passwd").map_err(|_| ())?;
let mut buf = [0; 128];
loop {
let line = read_line(&mut file, &mut buf).map_err(|_| ())?;
if let Some(line) = line {
let ent = UserInfo::from_str(line)?;
if pred(&ent) {
return Ok(ent);
}
} else {
break;
}
}
Err(())
}
pub fn by_name(name: &str) -> Result<Self, ()> {
Self::find(|ent| ent.name() == name)
}
}
impl FromStr for UserInfo {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
let mut iter = s.split(":");
let name = iter.next().ok_or(())?;
let uid = iter
.next()
.ok_or(())
.and_then(|e| u32::from_str(e).map_err(|_| ()))
.map(UserId::from)?;
let gid = iter
.next()
.ok_or(())
.and_then(|e| u32::from_str(e).map_err(|_| ()))
.map(GroupId::from)?;
let comment = iter.next().ok_or(())?;
let home = iter.next().ok_or(())?;
let shell = iter.next().ok_or(())?;
if iter.next().is_some() {
return Err(());
}
let mut res = Self {
uid,
gid,
name: FixedStr::empty(),
home: FixedStr::empty(),
shell: FixedStr::empty(),
};
res.name.copy_from_str(&name);
res.home.copy_from_str(&home);
res.shell.copy_from_str(&shell);
Ok(res)
}
}
+67
View File
@@ -0,0 +1,67 @@
use crate::file::File;
use crate::io::{Read, read_line};
use core::str::FromStr;
use libsys::FixedStr;
#[derive(Debug, Clone, Copy)]
pub struct UserShadow {
name: FixedStr<32>,
password: FixedStr<64>,
}
impl UserShadow {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn password(&self) -> &str {
self.password.as_str()
}
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, ()> {
let mut file = File::open("/etc/shadow").map_err(|_| ())?;
let mut buf = [0; 128];
loop {
let line = read_line(&mut file, &mut buf).map_err(|_| ())?;
if let Some(line) = line {
let ent = UserShadow::from_str(line)?;
if pred(&ent) {
return Ok(ent);
}
} else {
break;
}
}
Err(())
}
pub fn by_name(name: &str) -> Result<Self, ()> {
Self::find(|ent| ent.name() == name)
}
}
impl FromStr for UserShadow {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
let mut iter = s.split(':');
let name = iter.next().ok_or(())?;
let password = iter.next().ok_or(())?;
if iter.next().is_some() {
return Err(());
}
let mut res = Self {
name: FixedStr::empty(),
password: FixedStr::empty(),
};
res.name.copy_from_str(name);
res.password.copy_from_str(password);
Ok(res)
}
}
+15 -6
View File
@@ -1,7 +1,8 @@
use crate::io::{AsRawFd, Error};
use crate::os;
use crate::trace;
use libsys::stat::FileDescriptor;
use crate::io::{AsRawFd, Error, Read};
use libsys::{
calls::{sys_openat, sys_read, sys_close},
stat::{FileDescriptor, FileMode, OpenFlags},
};
pub struct File {
fd: FileDescriptor,
@@ -9,7 +10,9 @@ pub struct File {
impl File {
pub fn open(path: &str) -> Result<File, Error> {
todo!()
let fd = sys_openat(None, path, FileMode::default_reg(), OpenFlags::O_RDONLY)
.map_err(Error::from)?;
Ok(File { fd })
}
}
@@ -21,6 +24,12 @@ impl AsRawFd for File {
impl Drop for File {
fn drop(&mut self) {
todo!();
sys_close(self.fd).ok();
}
}
impl Read for File {
fn read(&mut self, bytes: &mut [u8]) -> Result<usize, Error> {
sys_read(self.fd, bytes).map_err(Error::from)
}
}
+1
View File
@@ -2,6 +2,7 @@ use libsys::error::Errno;
#[derive(Debug)]
pub struct Error {
#[allow(dead_code)]
repr: Repr,
}
+38 -2
View File
@@ -1,7 +1,11 @@
use libsys::{
calls::sys_fstatat,
calls::{sys_fstatat, sys_ioctl},
stat::{FileDescriptor, Stat},
ioctl::IoctlCmd,
error::Errno,
proc::Pid
};
use core::mem::size_of;
use core::fmt;
mod error;
@@ -24,9 +28,41 @@ pub trait AsRawFd {
fn as_raw_fd(&self) -> FileDescriptor;
}
pub fn tcgetpgrp(_fd: FileDescriptor) -> Result<Pid, Errno> {
todo!()
}
pub fn tcsetpgrp(fd: FileDescriptor, pgid: Pid) -> Result<(), Errno> {
sys_ioctl(fd, IoctlCmd::TtySetPgrp, &pgid as *const _ as usize, size_of::<Pid>()).map(|_| ())
}
pub fn stat(pathname: &str) -> Result<Stat, Error> {
let mut buf = Stat::default();
// TODO error handling
let res = sys_fstatat(None, pathname, &mut buf, 0).unwrap();
sys_fstatat(None, pathname, &mut buf, 0).unwrap();
Ok(buf)
}
// 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>, ()> {
let mut pos = 0;
loop {
if pos == buf.len() {
return Err(());
}
let count = f.read(&mut buf[pos..=pos]).map_err(|_| ())?;
if count == 0 {
if pos == 0 {
return Ok(None);
}
break;
}
if buf[pos] == b'\n' {
break;
}
pos += 1;
}
core::str::from_utf8(&buf[..pos]).map_err(|_| ()).map(Some)
}
+44 -36
View File
@@ -1,40 +1,40 @@
use libsys::{
stat::FileDescriptor,
calls::{sys_read, sys_write}
};
use crate::io::{Read, Write, Error};
use crate::sync::{Mutex, MutexGuard};
use crate::io::{Error, Read, Write};
use crate::sync::Mutex;
use core::fmt;
use libsys::{
calls::{sys_read, sys_write},
stat::FileDescriptor,
};
struct InputInner {
fd: FileDescriptor
fd: FileDescriptor,
}
struct OutputInner {
fd: FileDescriptor
fd: FileDescriptor,
}
pub struct StdinLock<'a> {
lock: MutexGuard<'a, InputInner>
}
pub struct StdoutLock<'a> {
lock: MutexGuard<'a, OutputInner>
}
pub struct StderrLock<'a> {
lock: MutexGuard<'a, OutputInner>
}
//pub struct StdinLock<'a> {
// lock: MutexGuard<'a, InputInner>
//}
//
//pub struct StdoutLock<'a> {
// lock: MutexGuard<'a, OutputInner>
//}
//
//pub struct StderrLock<'a> {
// lock: MutexGuard<'a, OutputInner>
//}
pub struct Stdin {
inner: &'static Mutex<InputInner>,
}
pub struct Stdout {
inner: &'static Mutex<OutputInner>
inner: &'static Mutex<OutputInner>,
}
pub struct Stderr {
inner: &'static Mutex<OutputInner>
inner: &'static Mutex<OutputInner>,
}
// STDIN
@@ -51,6 +51,14 @@ impl Read for Stdin {
}
}
// impl Stdin {
// pub fn lock(&self) -> StdinLock {
// StdinLock {
// lock: self.inner.lock()
// }
// }
// }
// STDOUT/STDERR
impl fmt::Write for OutputInner {
@@ -89,21 +97,21 @@ impl Write for Stderr {
}
}
impl Stdout {
pub fn lock(&self) -> StdoutLock {
StdoutLock {
lock: self.inner.lock()
}
}
}
impl Stderr {
pub fn lock(&self) -> StderrLock {
StderrLock {
lock: self.inner.lock()
}
}
}
// impl Stdout {
// pub fn lock(&self) -> StdoutLock {
// StdoutLock {
// lock: self.inner.lock()
// }
// }
// }
//
// impl Stderr {
// pub fn lock(&self) -> StderrLock {
// StderrLock {
// lock: self.inner.lock()
// }
// }
// }
lazy_static! {
static ref STDIN: Mutex<InputInner> = Mutex::new(InputInner {
+5 -3
View File
@@ -1,5 +1,5 @@
use core::fmt;
use crate::io::{self, Write};
use crate::io::Write;
#[macro_export]
macro_rules! print {
@@ -8,7 +8,8 @@ macro_rules! print {
#[macro_export]
macro_rules! println {
($($args:tt)+) => (print!("{}\n", format_args!($($args)+)))
($($args:tt)+) => (print!("{}\n", format_args!($($args)+)));
() => (print!("\n"));
}
#[macro_export]
@@ -18,7 +19,8 @@ macro_rules! eprint {
#[macro_export]
macro_rules! eprintln {
($($args:tt)+) => (eprint!("{}\n", format_args!($($args)+)))
($($args:tt)+) => (eprint!("{}\n", format_args!($($args)+)));
() => (eprint!("\n"));
}
pub fn _print<T: Write>(out: fn() -> T, args: fmt::Arguments) {
+17 -19
View File
@@ -1,39 +1,35 @@
#![feature(asm, alloc_error_handler)]
#![feature(alloc_error_handler)]
#![no_std]
use core::panic::PanicInfo;
use libsys::proc::ExitCode;
#[macro_use]
extern crate lazy_static;
extern crate alloc;
use core::panic::PanicInfo;
use libsys::{debug::TraceLevel, ProgramArgs, proc::ExitCode};
mod allocator;
pub mod env;
pub mod file;
pub mod io;
pub mod os;
pub mod sys;
pub mod sync;
#[inline(never)]
extern "C" fn _signal_handler(arg: sys::Signal) -> ! {
trace!("Entered signal handler: arg={:?}", arg);
sys::sys_ex_sigreturn();
}
static mut SIGNAL_STACK: [u8; 4096] = [0; 4096];
pub mod thread;
pub mod signal;
#[link_section = ".text._start"]
#[no_mangle]
extern "C" fn _start(_arg: usize) -> ! {
extern "C" fn _start(arg: &'static ProgramArgs) -> ! {
extern "Rust" {
fn main() -> i32;
}
unsafe {
sys::sys_ex_signal(
_signal_handler as usize,
SIGNAL_STACK.as_ptr() as usize + 4096,
)
.unwrap();
allocator::init();
thread::init_main();
env::setup_env(arg);
}
let res = unsafe { main() };
@@ -42,7 +38,9 @@ extern "C" fn _start(_arg: usize) -> ! {
#[panic_handler]
fn panic_handler(pi: &PanicInfo) -> ! {
// TODO unwind to send panic argument back to parent thread
// TODO print to stdout/stderr (if available)
trace!("Panic ocurred: {}", pi);
let thread = thread::current();
trace!(TraceLevel::Error, "{:?} panicked: {:?}", thread, pi);
sys::sys_exit(ExitCode::from(-1));
}
+10 -5
View File
@@ -1,11 +1,16 @@
use libsys::debug::TraceLevel;
use crate::sys;
use core::fmt;
use core::mem::{size_of, MaybeUninit};
use libsys::{ioctl::IoctlCmd, stat::FileDescriptor, termios::Termios};
#[macro_export]
macro_rules! trace {
($($args:tt)+) => ($crate::os::_trace(format_args!($($args)+)))
($level:expr, $($args:tt)+) => ($crate::os::_trace($level, format_args!($($args)+)))
}
#[macro_export]
macro_rules! trace_debug {
($($args:tt)+) => ($crate::os::_trace($crate::sys::debug::TraceLevel::Debug, format_args!($($args)+)))
}
struct BufferWriter<'a> {
@@ -23,7 +28,7 @@ impl fmt::Write for BufferWriter<'_> {
}
}
pub fn _trace(args: fmt::Arguments) {
pub fn _trace(level: TraceLevel, args: fmt::Arguments) {
use core::fmt::Write;
static mut BUFFER: [u8; 4096] = [0; 4096];
let mut writer = BufferWriter {
@@ -31,5 +36,5 @@ pub fn _trace(args: fmt::Arguments) {
pos: 0,
};
writer.write_fmt(args).ok();
sys::sys_ex_debug_trace(unsafe { &BUFFER[..writer.pos] }).ok();
sys::sys_ex_debug_trace(level, unsafe { &BUFFER[..writer.pos] }).ok();
}
+42
View File
@@ -0,0 +1,42 @@
use crate::trace;
use libsys::{
debug::TraceLevel,
calls::{sys_ex_sigreturn, sys_exit},
proc::ExitCode,
signal::Signal,
};
#[derive(Clone, Copy)]
pub enum SignalHandler {
Func(fn(Signal) -> ()),
Ignore,
Terminate,
}
// TODO per-thread signal handler table
static mut SIGNAL_HANDLERS: [SignalHandler; 32] = [SignalHandler::Terminate; 32];
pub fn set_handler(sig: Signal, handler: SignalHandler) -> SignalHandler {
unsafe {
let old = SIGNAL_HANDLERS[sig as usize];
SIGNAL_HANDLERS[sig as usize] = handler;
old
}
}
#[inline(never)]
pub(crate) extern "C" fn signal_handler(arg: Signal) -> ! {
// TODO tpidr_el0 is invalidated when entering signal context
trace!(TraceLevel::Debug, "Entered signal handler: arg={:?}", arg);
let no = arg as usize;
if no >= 32 {
panic!("Undefined signal number: {}", no);
}
match unsafe { SIGNAL_HANDLERS[no] } {
SignalHandler::Func(f) => f(arg),
SignalHandler::Ignore => (),
SignalHandler::Terminate => sys_exit(ExitCode::from(-1)),
}
sys_ex_sigreturn();
}
+6 -7
View File
@@ -1,7 +1,11 @@
pub use libsys::signal::{Signal, SignalDestination};
pub use libsys::proc::{self, ExitCode};
pub use libsys::termios;
pub use libsys::abi;
pub use libsys::calls::*;
pub use libsys::stat::{self, FileDescriptor};
pub use libsys::stat::{self, AccessMode, FileDescriptor};
pub use libsys::error::Errno;
pub use libsys::debug;
use core::sync::atomic::{Ordering, AtomicBool};
@@ -20,15 +24,10 @@ impl RawMutex {
self.inner.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok()
}
#[inline]
unsafe fn is_locked(&self) -> bool {
self.inner.load(Ordering::Acquire)
}
#[inline]
pub unsafe fn lock(&self) {
while !self.try_lock() {
asm!("nop");
sys_ex_yield();
}
}
+143
View File
@@ -0,0 +1,143 @@
use crate::signal;
use alloc::{boxed::Box, sync::Arc, vec};
use core::any::Any;
use core::cell::UnsafeCell;
use core::fmt;
use core::mem::MaybeUninit;
use libsys::{
calls::{sys_ex_clone, sys_ex_gettid, sys_ex_signal, sys_ex_thread_exit, sys_ex_thread_wait},
proc::{ExitCode, Tid},
};
use core::arch::asm;
struct NativeData<F, T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
closure: F,
result: ThreadPacket<T>,
stack: usize,
}
#[derive(Clone)]
pub struct Thread {
id: Tid,
}
pub type ThreadResult<T> = Result<T, Box<dyn Any + Send + Sync>>;
pub type ThreadPacket<T> = Arc<UnsafeCell<MaybeUninit<ThreadResult<T>>>>;
pub struct JoinHandle<T> {
native: Tid,
result: ThreadPacket<T>,
}
impl Thread {
pub const fn id(&self) -> Tid {
self.id
}
}
impl fmt::Debug for Thread {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Thread")
.field("id", &self.id)
.finish_non_exhaustive()
}
}
impl<T> JoinHandle<T> {
pub fn join(self) -> ThreadResult<T> {
sys_ex_thread_wait(self.native).unwrap();
unsafe {
Arc::try_unwrap(self.result)
.unwrap()
.into_inner()
.assume_init()
}
}
}
unsafe fn init_common(signal_stack_pointer: *mut u8) {
let tid = u32::from(sys_ex_gettid()) as u64;
asm!("msr tpidr_el0, {:x}", in(reg) tid);
// thread::current() should be valid at this point
sys_ex_signal(
signal::signal_handler as usize,
signal_stack_pointer as usize,
)
.unwrap();
}
pub(crate) unsafe fn init_main() {
#[repr(align(16))]
struct StackWrapper {
data: [u8; 8192],
}
static mut STACK: StackWrapper = StackWrapper { data: [0; 8192] };
init_common(STACK.data.as_mut_ptr().add(8192))
}
pub fn current() -> Thread {
let mut id: u64;
unsafe {
asm!("mrs {:x}, tpidr_el0", out(reg) id);
}
Thread { id: Tid::from(id as u32) }
}
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
let stack = vec![0u8; 8192].leak();
let result = Arc::new(UnsafeCell::new(MaybeUninit::uninit()));
#[inline(never)]
extern "C" fn thread_entry<F, T>(data: *mut NativeData<F, T>) -> !
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
let (_stack, _len) = {
// Setup signal handling
let mut signal_stack = vec![0u8; 8192];
unsafe {
init_common(signal_stack.as_mut_ptr().add(signal_stack.len()));
}
let data: Box<NativeData<F, T>> = unsafe { Box::from_raw(data) };
let res = (data.closure)();
unsafe {
(&mut *data.result.get()).write(Ok(res));
}
(data.stack, 8192)
};
// TODO free stack
sys_ex_thread_exit(ExitCode::from(0));
}
let native = {
let stack = stack.as_mut_ptr() as usize + stack.len();
let data: *mut NativeData<F, T> = Box::into_raw(Box::new(NativeData {
closure: f,
stack,
result: result.clone(),
}));
sys_ex_clone(thread_entry::<F, T> as usize, stack, data as usize).unwrap()
};
JoinHandle { native, result }
}
+26 -1
View File
@@ -11,7 +11,32 @@ path = "src/init/main.rs"
[[bin]]
name = "shell"
path = "src/shell/main.rs"
path = "src/bin/shell.rs"
[[bin]]
name = "fuzzy"
path = "src/bin/fuzzy.rs"
[[bin]]
name = "ls"
path = "src/bin/ls.rs"
[[bin]]
name = "cat"
path = "src/bin/cat.rs"
[[bin]]
name = "hexd"
path = "src/bin/hexd.rs"
[[bin]]
name = "login"
path = "src/sbin/login.rs"
[dependencies]
libusr = { path = "../libusr" }
libsys = { path = "../libsys" }
lazy_static = { version = "*", features = ["spin_no_std"] }
[features]
verbose = ["libusr/verbose"]
+46
View File
@@ -0,0 +1,46 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate libusr;
use libusr::io::{self, Read, Write};
use libusr::file::File;
fn do_cat<F: Read>(mut fd: F) -> Result<(), io::Error> {
let mut buf = [0; 4096];
let mut out = io::stdout();
loop {
let count = fd.read(&mut buf)?;
if count == 0 {
break;
}
out.write(&buf[..count])?;
}
Ok(())
}
#[no_mangle]
fn main() -> i32 {
let args = libusr::env::args();
let mut res = 0;
if args.len() == 1 {
if let Err(e) = do_cat(io::stdin()) {
eprintln!(".: {:?}", e);
res = -1;
}
} else {
for arg in &args[1..] {
if let Err(e) = File::open(arg).map(do_cat) {
eprintln!("{}: {:?}", arg, e);
res = -1;
}
}
}
res
}
+139
View File
@@ -0,0 +1,139 @@
#![no_std]
#![no_main]
#![allow(unused_macros)]
#![allow(dead_code)]
#[macro_use]
extern crate libusr;
use libusr::sys::{abi::SystemCall, stat::Stat};
use core::arch::asm;
static mut STATE: u64 = 0;
macro_rules! syscall {
($num:expr) => {{
let mut res: usize;
asm!("svc #0", out("x0") res, in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res,
in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1,
in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x4") $a4, in("x8") $num, options(nostack));
res
}};
}
/// Integer/size argument
macro_rules! argn {
($a:expr) => {
$a as usize
};
}
/// Pointer/base argument
macro_rules! argp {
($a:expr) => {
$a as usize
};
}
fn random_set_seed(seed: u64) {
unsafe { STATE = seed; }
}
fn random_u64() -> u64 {
let mut x = unsafe { STATE };
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
unsafe {
STATE = x;
}
x
}
fn random_ascii_char() -> u8 {
((random_u64() % (0x7F - 0x20)) as u8) + 0x20
}
fn random_str_range(buf: &mut [u8], min: usize, max: usize) -> &str {
let max = core::cmp::min(buf.len(), max);
assert!(max > min);
let len = ((random_u64() as usize) % (max - min)) + min;
for c in buf[..len].iter_mut() {
*c = random_ascii_char();
}
core::str::from_utf8(&buf[..len]).unwrap()
}
fn random_str(buf: &mut [u8]) -> &str {
random_str_range(buf, 0, buf.len())
}
fn random_bytes(buf: &mut [u8]) {
for byte in buf.iter_mut() {
*byte = (random_u64() & 0xFF) as u8;
}
}
#[no_mangle]
fn main() -> i32 {
let seed = libusr::sys::sys_ex_getcputime().unwrap().as_nanos() as u64 / 13;
println!("Using seed: {:#x}", seed);
random_set_seed(seed);
let mut buf = [0; 256];
// Test sys_ex_getcputime()
let mut prev_time = libusr::sys::sys_ex_getcputime().unwrap().as_nanos();
for _ in 0..1000 {
let t = libusr::sys::sys_ex_getcputime().unwrap().as_nanos();
assert!(t >= prev_time);
prev_time = t;
}
// Test non-utf8 input fed into syscalls expecting strings
// let old_signal = signal::set_handler(Signal::InvalidSystemCall, SignalHandler::Ignore);
for _ in 0..10000 {
random_bytes(&mut buf);
let mut stat = Stat::default();
unsafe {
syscall!(SystemCall::FileStatus.repr(), (-2i32) as usize, buf.as_mut_ptr() as usize, buf.len(), (&mut stat) as *mut _ as usize);
}
}
// signal::set_handler(Signal::InvalidSystemCall, old_signal);
0
}
+69
View File
@@ -0,0 +1,69 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate libusr;
use libusr::io::{self, Read};
use libusr::file::File;
fn line_print(off: usize, line: &[u8]) {
print!("{:08x}: ", off);
for i in 0..16 {
if i < line.len() {
print!("{:02x}", line[i]);
} else {
print!(" ");
}
if i % 2 != 0 {
print!(" ");
}
}
print!("| ");
for &b in line.iter() {
if b.is_ascii() && !b.is_ascii_control() {
print!("{}", b as char);
} else {
print!(".");
}
}
println!();
}
fn do_hexd<F: Read>(mut fd: F) -> Result<(), io::Error> {
let mut buf = [0; 16];
let mut off = 0;
loop {
let count = fd.read(&mut buf)?;
if count == 0 {
break;
}
line_print(off, &buf[..count]);
off += count;
}
Ok(())
}
#[no_mangle]
fn main() -> i32 {
let args = libusr::env::args();
let mut res = 0;
if args.len() == 1 {
if let Err(e) = do_hexd(io::stdin()) {
eprintln!(".: {:?}", e);
res = -1;
}
} else {
for arg in &args[1..] {
if let Err(e) = File::open(arg).map(do_hexd) {
eprintln!("{}: {:?}", arg, e);
res = -1;
}
}
}
res
}
+73
View File
@@ -0,0 +1,73 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate libusr;
#[macro_use]
extern crate alloc;
use alloc::borrow::ToOwned;
use libusr::sys::{
stat::{DirectoryEntry, FileMode, OpenFlags, Stat},
sys_close, sys_fstatat, sys_openat, sys_readdir, Errno,
};
fn list_directory(path: &str) -> Result<(), Errno> {
let mut buffer = [DirectoryEntry::empty(); 8];
let mut stat = Stat::default();
let mut data = vec![];
let fd = sys_openat(
None,
path,
FileMode::default_dir(),
OpenFlags::O_DIRECTORY | OpenFlags::O_RDONLY,
)?;
loop {
let count = sys_readdir(fd, &mut buffer)?;
if count == 0 {
break;
}
buffer.iter().take(count).for_each(|e| {
data.push(e.as_str().to_owned());
});
}
data.sort();
data.iter().for_each(|item| {
let stat = sys_fstatat(Some(fd), item, &mut stat, 0).map(|_| &stat);
if let Ok(stat) = stat {
print!("{} ", stat.mode);
} else {
print!("?????????? ");
}
println!("{}", item);
});
sys_close(fd)
}
#[no_mangle]
fn main() -> i32 {
let args = libusr::env::args();
let mut res = 0;
if args.len() == 1 {
if let Err(e) = list_directory(".") {
eprintln!(".: {:?}", e);
res = -1;
}
} else {
for arg in &args[1..] {
if let Err(e) = list_directory(arg) {
eprintln!("{}: {:?}", arg, e);
res = -1;
}
}
}
res
}
+111
View File
@@ -0,0 +1,111 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate libusr;
extern crate alloc;
use alloc::{borrow::ToOwned, vec::Vec};
use libusr::io::{self, Read};
use libusr::signal::{self, SignalHandler};
use libusr::sys::{
sys_chdir, sys_execve, sys_exit, sys_faccessat, sys_fork, sys_getpgid, sys_setpgid,
sys_waitpid, AccessMode, Errno, ExitCode, FileDescriptor, Signal,
};
struct Builtin {
func: fn(&[&str]) -> ExitCode,
name: &'static str,
}
fn cmd_cd(args: &[&str]) -> ExitCode {
if args.len() != 2 {
eprintln!("Usage: cd DIR");
ExitCode::from(-1)
} else if let Err(err) = sys_chdir(args[1]) {
eprintln!("{}: {:?}", args[1], err);
ExitCode::from(-1)
} else {
ExitCode::from(0)
}
}
static BUILTINS: [Builtin; 1] = [Builtin {
name: "cd",
func: cmd_cd,
}];
fn readline<'a, F: Read>(f: &mut F, bytes: &'a mut [u8]) -> Result<Option<&'a str>, io::Error> {
let size = f.read(bytes)?;
Ok(if size == 0 {
None
} else {
Some(
core::str::from_utf8(&bytes[..size])
.unwrap()
.trim_end_matches('\n'),
)
})
}
fn execute(line: &str) -> Result<ExitCode, Errno> {
// TODO proper arg handling
let args: Vec<&str> = line.split(' ').collect();
let cmd = args[0];
for item in BUILTINS.iter() {
if item.name == cmd {
return Ok((item.func)(&args));
}
}
let filename = "/bin/".to_owned() + cmd;
sys_faccessat(None, &filename, AccessMode::X_OK, 0)?;
if let Some(pid) = unsafe { sys_fork()? } {
let mut status = 0;
sys_waitpid(pid, &mut status)?;
let pgid = sys_getpgid(None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
Ok(ExitCode::from(status))
} else {
let pgid = sys_setpgid(None, None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
sys_execve(&filename, &args).unwrap();
sys_exit(ExitCode::from(-1));
}
}
#[no_mangle]
fn main() -> i32 {
let mut buf = [0; 256];
let mut stdin = io::stdin();
signal::set_handler(Signal::Interrupt, SignalHandler::Ignore);
let pgid = sys_setpgid(None, None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
loop {
print!("> ");
match readline(&mut stdin, &mut buf) {
Ok(line) => {
if line.is_none() {
break;
}
let line = line.unwrap().trim_start_matches(' ');
if line.is_empty() {
continue;
}
if let Err(e) = execute(line) {
eprintln!("{}: {:?}", line.split(' ').next().unwrap(), e);
}
}
Err(_) => {
println!("Interrupt!");
continue;
}
}
}
0
}
+24 -6
View File
@@ -1,17 +1,35 @@
#![feature(asm)]
#![no_std]
#![no_main]
#[macro_use]
extern crate libusr;
use core::arch::asm;
use libusr::sys::{stat::MountOptions, sys_execve, sys_fork, sys_mount, sys_waitpid};
#[no_mangle]
fn main() -> i32 {
let pid = libusr::sys::sys_fork().unwrap();
sys_mount(
"/dev",
&MountOptions {
device: None,
fs: Some("devfs"),
},
)
.expect("Failed to mount devfs");
sys_mount(
"/sys",
&MountOptions {
device: None,
fs: Some("sysfs"),
},
)
.expect("Failed to mount sysfs");
if let Some(pid) = pid {
if let Some(pid) = unsafe { sys_fork().unwrap() } {
let mut status = 0;
libusr::sys::sys_waitpid(pid, &mut status).unwrap();
sys_waitpid(pid, &mut status).unwrap();
println!("Process {:?} exited with status {}", pid, status);
loop {
@@ -20,7 +38,7 @@ fn main() -> i32 {
}
}
} else {
libusr::sys::sys_execve("/bin/shell").unwrap();
loop {}
sys_execve("/sbin/login", &["/sbin/login", "/dev/ttyS0"]).unwrap();
unreachable!();
}
}
+163
View File
@@ -0,0 +1,163 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate libusr;
use libsys::{
calls::{
sys_close, sys_dup, sys_fork, sys_getgid, sys_getpgid, sys_getuid, sys_ioctl, sys_openat,
sys_read, sys_setgid, sys_setpgid, sys_setsid, sys_setuid, sys_waitpid, sys_execve
},
error::Errno,
ioctl::IoctlCmd,
stat::{FileDescriptor, FileMode, GroupId, OpenFlags, UserId},
termios::{Termios, TermiosLflag},
};
use libusr::{env::{self, UserInfo, UserShadow}, io};
use core::str::FromStr;
struct HiddenInput {
fd: FileDescriptor,
termios: Termios,
}
impl HiddenInput {
fn open(fd: FileDescriptor) -> Result<Self, Errno> {
use core::mem::{size_of, MaybeUninit};
let mut termios: MaybeUninit<Termios> = MaybeUninit::uninit();
sys_ioctl(
fd,
IoctlCmd::TtyGetAttributes,
termios.as_mut_ptr() as usize,
size_of::<Termios>(),
)?;
let termios = unsafe { termios.assume_init() };
let mut new_termios = termios.clone();
new_termios.lflag &= !(TermiosLflag::ECHO | TermiosLflag::ECHOK | TermiosLflag::ECHOE);
sys_ioctl(
fd,
IoctlCmd::TtySetAttributes,
&new_termios as *const _ as usize,
size_of::<Termios>(),
)?;
Ok(Self { fd, termios })
}
fn readline<'a>(&mut self, buf: &'a mut [u8]) -> Result<&'a str, Errno> {
readline(self.fd, buf)
}
}
impl Drop for HiddenInput {
fn drop(&mut self) {
use core::mem::size_of;
sys_ioctl(
self.fd,
IoctlCmd::TtySetAttributes,
&self.termios as *const _ as usize,
size_of::<Termios>(),
)
.ok();
}
}
fn readline(fd: FileDescriptor, buf: &mut [u8]) -> Result<&str, Errno> {
let len = sys_read(fd, buf)?;
if len == 0 {
Ok("")
} else {
Ok(core::str::from_utf8(&buf[..len - 1]).unwrap())
}
}
fn login(uid: UserId, gid: GroupId, shell: &str) -> Result<(), Errno> {
if let Some(pid) = unsafe { sys_fork() }? {
let mut status = 0;
sys_waitpid(pid, &mut status).ok();
let pgid = sys_getpgid(None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
Ok(())
} else {
sys_setuid(uid).expect("setuid failed");
sys_setgid(gid).expect("setgid failed");
let pgid = sys_setpgid(None, None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
sys_execve(shell, &[shell]).expect("execve() failed");
panic!();
}
}
fn login_as(name: &str) -> Result<(), Errno> {
let ent = UserInfo::by_name(name).map_err(|_| Errno::DoesNotExist)?;
login(ent.uid(), ent.gid(), ent.shell())
}
// TODO baud rate and misc port settings
#[no_mangle]
fn main() -> i32 {
if !sys_getuid().is_root() || !sys_getgid().is_root() {
panic!("This program must be run as root");
}
let args = env::args();
if args.len() != 2 {
panic!("Usage: {} TTY", args[0]);
}
sys_setsid().expect("setsid() failed");
// Close controlling terminal
// NOTE this will invalidate rust-side Stdin, Stdout, Stderr
// until replacement is re-opened using the specified TTY
sys_close(FileDescriptor::STDERR).unwrap();
sys_close(FileDescriptor::STDOUT).unwrap();
sys_close(FileDescriptor::STDIN).unwrap();
sys_openat(
None,
args[1],
FileMode::default_reg(),
OpenFlags::O_RDONLY | OpenFlags::O_CTTY,
)
.expect("Failed to open stdin");
sys_openat(
None,
args[1],
FileMode::default_reg(),
OpenFlags::O_WRONLY | OpenFlags::O_CTTY,
)
.expect("Failed to open stdout");
sys_dup(FileDescriptor::STDOUT, Some(FileDescriptor::STDERR)).expect("Failed to open stderr");
let mut user_buf = [0; 128];
let mut password_buf = [0; 128];
loop {
print!("login: ");
let username = readline(FileDescriptor::STDIN, &mut user_buf).expect("Login read failed");
let shadow = match UserShadow::by_name(username) {
Ok(e) => e,
Err(_) => continue
};
if !shadow.password().is_empty() {
print!("password: ");
let password = {
let mut input = HiddenInput::open(FileDescriptor::STDIN).unwrap();
input.readline(&mut password_buf)
}
.expect("Password read failed");
if password != shadow.password() {
eprintln!("Incorrect password");
continue;
}
}
login_as(username);
}
}
-26
View File
@@ -1,26 +0,0 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate libusr;
use libusr::io::{self, Read};
#[no_mangle]
fn main() -> i32 {
let mut buf = [0; 512];
let mut stdin = io::stdin();
eprintln!("stderr test");
loop {
let count = stdin.read(&mut buf).unwrap();
if count == 0 {
break;
}
let line = core::str::from_utf8(&buf[..count]).unwrap();
println!("{:?}", line);
}
0
}