Merge branch 'feat/thread' into feat/smp
This commit is contained in:
Generated
+70
-28
@@ -36,8 +36,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6419a5c75e40011b9fe0174db3fe24006ab122fbe1b7e9cc5974b338a755c76"
|
||||
|
||||
[[package]]
|
||||
name = "error"
|
||||
version = "0.1.0"
|
||||
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"
|
||||
@@ -49,8 +56,8 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
name = "fat32"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"error",
|
||||
"libcommon",
|
||||
"fs-macros",
|
||||
"libsys",
|
||||
"vfs",
|
||||
]
|
||||
|
||||
@@ -62,7 +69,7 @@ checksum = "99a40cabc11c8258822a593f5c51f2d9f4923e715ca9e2a0630cf77ae15f390b"
|
||||
dependencies = [
|
||||
"endian-type-rs",
|
||||
"fallible-iterator",
|
||||
"memoffset",
|
||||
"memoffset 0.5.6",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"rustc_version",
|
||||
@@ -70,6 +77,14 @@ dependencies = [
|
||||
"unsafe_unwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
@@ -77,35 +92,54 @@ dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cortex-a",
|
||||
"error",
|
||||
"fdt-rs",
|
||||
"libcommon",
|
||||
"kernel-macros",
|
||||
"libsys",
|
||||
"memfs",
|
||||
"syscall",
|
||||
"tock-registers",
|
||||
"vfs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcommon"
|
||||
name = "kernel-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"error",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"enum-repr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libusr"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"syscall",
|
||||
"lazy_static",
|
||||
"libsys",
|
||||
"memoffset 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memfs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"error",
|
||||
"libcommon",
|
||||
"fs-macros",
|
||||
"libsys",
|
||||
"vfs",
|
||||
]
|
||||
|
||||
@@ -118,6 +152,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"
|
||||
@@ -144,9 +187,9 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.29"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -184,6 +227,12 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -192,23 +241,15 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.80"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
|
||||
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syscall"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.7.0"
|
||||
@@ -231,6 +272,8 @@ checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80"
|
||||
name = "user"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libsys",
|
||||
"libusr",
|
||||
]
|
||||
|
||||
@@ -238,7 +281,6 @@ dependencies = [
|
||||
name = "vfs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"error",
|
||||
"libcommon",
|
||||
"syscall",
|
||||
"fs-macros",
|
||||
"libsys",
|
||||
]
|
||||
|
||||
+5
-5
@@ -9,13 +9,13 @@ edition = "2018"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"fs/vfs",
|
||||
"fs/memfs",
|
||||
"fs/fat32",
|
||||
"syscall",
|
||||
"libcommon",
|
||||
"fs/macros",
|
||||
"fs/memfs",
|
||||
"fs/vfs",
|
||||
"kernel",
|
||||
"kernel/macros",
|
||||
"libsys",
|
||||
"libusr",
|
||||
"user",
|
||||
"error"
|
||||
]
|
||||
|
||||
@@ -20,6 +20,9 @@ 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
|
||||
@@ -90,9 +93,15 @@ 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
|
||||
touch $(O)/rootfs/dev/.do_no_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) \
|
||||
@@ -122,7 +131,7 @@ doc-open:
|
||||
|
||||
clippy:
|
||||
cd kernel && cargo clippy $(CARGO_BUILD_OPTS)
|
||||
cd init && cargo clippy \
|
||||
cd user && cargo clippy \
|
||||
--target=../etc/$(ARCH)-osdev5.json \
|
||||
-Zbuild-std=core,alloc,compiler_builtins $(CARGO_COMMON_OPTS)
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#![no_std]
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Errno {
|
||||
AlreadyExists,
|
||||
BadExecutable,
|
||||
Busy,
|
||||
DeviceError,
|
||||
DoesNotExist,
|
||||
EndOfFile,
|
||||
Interrupt,
|
||||
InvalidArgument,
|
||||
InvalidFile,
|
||||
InvalidOperation,
|
||||
IsADirectory,
|
||||
NotADirectory,
|
||||
NotImplemented,
|
||||
OutOfMemory,
|
||||
ReadOnly,
|
||||
TimedOut,
|
||||
TooManyDescriptors,
|
||||
WouldBlock,
|
||||
}
|
||||
+2
-2
@@ -7,5 +7,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vfs = { path = "../vfs" }
|
||||
libcommon = { path = "../../libcommon" }
|
||||
error = { path = "../../error" }
|
||||
fs-macros = { path = "../macros" }
|
||||
libsys = { path = "../../libsys" }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use libcommon::{read_le16, read_le32};
|
||||
use libsys::mem::{read_le16, read_le32};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Bpb {
|
||||
|
||||
+7
-39
@@ -1,7 +1,11 @@
|
||||
use crate::{Bpb, FileInode};
|
||||
use alloc::{borrow::ToOwned, boxed::Box, string::String};
|
||||
use error::Errno;
|
||||
use libcommon::{read_le16, read_le32};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
mem::{read_le16, read_le32},
|
||||
stat::{OpenFlags, Stat},
|
||||
};
|
||||
use vfs::{BlockDevice, Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
|
||||
pub struct DirectoryInode {
|
||||
@@ -26,20 +30,8 @@ pub struct Dirent {
|
||||
pub cluster: u32,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DirectoryInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
_parent: VnodeRef,
|
||||
_name: &str,
|
||||
_kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn remove(&mut self, _parent: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn lookup(&mut self, parent: VnodeRef, name: &str) -> Result<VnodeRef, Errno> {
|
||||
let fs = parent.fs().unwrap();
|
||||
let dirent = {
|
||||
@@ -72,30 +64,6 @@ impl VnodeImpl for DirectoryInode {
|
||||
}
|
||||
Ok(vnode)
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for FatIterator<'_> {
|
||||
|
||||
+10
-30
@@ -1,5 +1,9 @@
|
||||
use crate::Bpb;
|
||||
use error::Errno;
|
||||
use libsys::{
|
||||
stat::{Stat, OpenFlags},
|
||||
ioctl::IoctlCmd,
|
||||
error::Errno
|
||||
};
|
||||
use vfs::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
|
||||
pub struct FileInode {
|
||||
@@ -7,27 +11,15 @@ pub struct FileInode {
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for FileInode {
|
||||
fn create(&mut self, _at: VnodeRef, _name: &str, _kind: VnodeKind) -> Result<VnodeRef, Errno> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn remove(&mut self, _parent: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _parent: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
fn open(&mut self, _node: VnodeRef, flags: OpenFlags) -> Result<usize, Errno> {
|
||||
if flags & OpenFlags::O_ACCESS != OpenFlags::O_RDONLY {
|
||||
return Err(Errno::ReadOnly);
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
let size = self.size as usize;
|
||||
if pos >= size {
|
||||
@@ -60,16 +52,4 @@ impl VnodeImpl for FileInode {
|
||||
|
||||
Ok(off)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -4,13 +4,18 @@
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[macro_use]
|
||||
extern crate fs_macros;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use core::any::Any;
|
||||
use core::cell::{Ref, RefCell};
|
||||
use error::Errno;
|
||||
use libcommon::read_le32;
|
||||
use libsys::{
|
||||
mem::read_le32,
|
||||
error::Errno,
|
||||
};
|
||||
use vfs::{BlockDevice, Filesystem, Vnode, VnodeKind, VnodeRef};
|
||||
|
||||
pub mod dir;
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
[package]
|
||||
name = "error"
|
||||
name = "fs-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "^1.0.81", features = ["full"] }
|
||||
quote = "^1.0.10"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
@@ -0,0 +1,162 @@
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use std::collections::HashSet;
|
||||
use syn::{parse_macro_input, ImplItem, ItemImpl, Ident};
|
||||
|
||||
fn impl_inode_fn<T: ToTokens>(name: &str, behavior: T) -> ImplItem {
|
||||
// TODO somehow know if current crate is vfs or not?
|
||||
ImplItem::Verbatim(match name {
|
||||
"create" => quote! {
|
||||
fn create(&mut self, _at: VnodeRef, _name: &str, kind: VnodeKind) ->
|
||||
Result<VnodeRef, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"remove" => quote! {
|
||||
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), libsys::error::Errno> {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"lookup" => quote! {
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) ->
|
||||
Result<VnodeRef, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"stat" => quote! {
|
||||
fn stat(&mut self, _at: VnodeRef) ->
|
||||
Result<libsys::stat::Stat, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"truncate" => quote! {
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) ->
|
||||
Result<(), libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"size" => quote! {
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, libsys::error::Errno> {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"read" => quote! {
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"write" => quote! {
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"open" => quote! {
|
||||
fn open(&mut self, _node: VnodeRef, _flags: libsys::stat::OpenFlags) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"close" => quote! {
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), libsys::error::Errno> {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"ioctl" => quote! {
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_cmd: libsys::ioctl::IoctlCmd,
|
||||
_ptr: usize,
|
||||
_len: usize) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"is_ready" => quote! {
|
||||
fn is_ready(&mut self, _node: VnodeRef, _write: bool) ->
|
||||
Result<bool, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"readdir" => quote! {
|
||||
fn readdir(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_pos: usize,
|
||||
_entries: &mut [libsys::stat::DirectoryEntry]
|
||||
) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
_ => panic!("TODO implement {:?}", name),
|
||||
})
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn auto_inode(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut impl_item = parse_macro_input!(input as ItemImpl);
|
||||
let mut missing = HashSet::<String>::new();
|
||||
let behavior = if attr.is_empty() {
|
||||
"unimplemented".to_string()
|
||||
} else {
|
||||
parse_macro_input!(attr as Ident).to_string()
|
||||
};
|
||||
let behavior = match behavior.as_str() {
|
||||
"unimplemented" => quote! { unimplemented!() },
|
||||
"panic" => quote! { panic!() },
|
||||
"error" => quote! { Err(libsys::error::Errno::NotImplemented) },
|
||||
_ => panic!("Unknown #[auto_inode] behavior: {:?}", behavior)
|
||||
};
|
||||
|
||||
missing.insert("create".to_string());
|
||||
missing.insert("remove".to_string());
|
||||
missing.insert("lookup".to_string());
|
||||
missing.insert("open".to_string());
|
||||
missing.insert("close".to_string());
|
||||
missing.insert("truncate".to_string());
|
||||
missing.insert("read".to_string());
|
||||
missing.insert("write".to_string());
|
||||
missing.insert("stat".to_string());
|
||||
missing.insert("size".to_string());
|
||||
missing.insert("ioctl".to_string());
|
||||
missing.insert("is_ready".to_string());
|
||||
missing.insert("readdir".to_string());
|
||||
|
||||
for item in &impl_item.items {
|
||||
match item {
|
||||
ImplItem::Method(method) => {
|
||||
let name = &method.sig.ident.to_string();
|
||||
if missing.contains(name) {
|
||||
missing.remove(name);
|
||||
}
|
||||
}
|
||||
_ => panic!("Unexpected impl item"),
|
||||
}
|
||||
}
|
||||
|
||||
for item in &missing {
|
||||
impl_item
|
||||
.items
|
||||
.push(impl_inode_fn(item, behavior.clone()));
|
||||
}
|
||||
|
||||
impl_item.to_token_stream().into()
|
||||
}
|
||||
+2
-2
@@ -7,8 +7,8 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vfs = { path = "../vfs" }
|
||||
error = { path = "../../error" }
|
||||
libcommon = { path = "../../libcommon" }
|
||||
fs-macros = { path = "../macros" }
|
||||
libsys = { path = "../../libsys" }
|
||||
|
||||
[features]
|
||||
cow = []
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::mem::{size_of, MaybeUninit};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
pub const SIZE: usize = 4096;
|
||||
pub const ENTRY_COUNT: usize = SIZE / size_of::<usize>();
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{block, BlockAllocator, BlockRef};
|
||||
use core::cmp::min;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
const L0_BLOCKS: usize = 32; // 128K
|
||||
const L1_BLOCKS: usize = 8; // 16M
|
||||
|
||||
+11
-39
@@ -1,12 +1,13 @@
|
||||
use crate::{BlockAllocator, Bvec, FileInode};
|
||||
use alloc::boxed::Box;
|
||||
use error::Errno;
|
||||
use vfs::{OpenFlags, Stat, Vnode, VnodeImpl, VnodeKind, VnodeRef, IoctlCmd};
|
||||
use libsys::{error::Errno, stat::Stat};
|
||||
use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
|
||||
pub struct DirInode<A: BlockAllocator + Copy + 'static> {
|
||||
alloc: A,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
|
||||
fn create(
|
||||
&mut self,
|
||||
@@ -14,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,42 +32,13 @@ impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn stat(&mut self, _node: VnodeRef, _stat: &mut Stat) -> Result<(), Errno> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
|
||||
let props = node.props();
|
||||
Ok(Stat {
|
||||
size: 0,
|
||||
blksize: 4096,
|
||||
mode: props.mode,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-34
@@ -1,29 +1,16 @@
|
||||
use crate::{BlockAllocator, Bvec};
|
||||
use error::Errno;
|
||||
use vfs::{OpenFlags, Stat, VnodeImpl, VnodeKind, VnodeRef, IoctlCmd};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::{OpenFlags, Stat},
|
||||
};
|
||||
use vfs::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
|
||||
pub struct FileInode<'a, A: BlockAllocator + Copy + 'static> {
|
||||
data: Bvec<'a, A>,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
|
||||
fn create(
|
||||
&mut self,
|
||||
_parent: VnodeRef,
|
||||
_name: &str,
|
||||
_kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _parent: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn remove(&mut self, _parent: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
@@ -48,21 +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 ioctl(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-8
@@ -11,19 +11,25 @@ extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[macro_use]
|
||||
extern crate fs_macros;
|
||||
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use core::any::Any;
|
||||
use core::cell::{Ref, RefCell};
|
||||
use error::Errno;
|
||||
use libcommon::*;
|
||||
use vfs::{BlockDevice, FileMode, Filesystem, Vnode, VnodeKind, VnodeRef};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
path::{path_component_left, path_component_right},
|
||||
stat::FileMode,
|
||||
};
|
||||
use vfs::{BlockDevice, Filesystem, Vnode, VnodeKind, VnodeRef};
|
||||
|
||||
mod block;
|
||||
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;
|
||||
@@ -61,8 +67,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))),
|
||||
@@ -105,7 +113,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)) {
|
||||
@@ -114,7 +125,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
@@ -1,4 +1,4 @@
|
||||
use 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(
|
||||
|
||||
+2
-3
@@ -6,6 +6,5 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
error = { path = "../../error" }
|
||||
libcommon = { path = "../../libcommon" }
|
||||
syscall = { path = "../../syscall" }
|
||||
libsys = { path = "../../libsys" }
|
||||
fs-macros = { path = "../macros" }
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// Block device interface
|
||||
pub trait BlockDevice {
|
||||
|
||||
+16
-25
@@ -1,5 +1,5 @@
|
||||
use crate::{OpenFlags, Stat, VnodeImpl, VnodeKind, VnodeRef, IoctlCmd};
|
||||
use error::Errno;
|
||||
use crate::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
use libsys::{error::Errno, ioctl::IoctlCmd, stat::OpenFlags};
|
||||
|
||||
/// Generic character device trait
|
||||
pub trait CharDevice {
|
||||
@@ -16,7 +16,11 @@ pub trait CharDevice {
|
||||
/// will immediately return an error.
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Errno>;
|
||||
|
||||
/// 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>;
|
||||
}
|
||||
|
||||
/// Wrapper struct to attach [VnodeImpl] implementation
|
||||
@@ -25,19 +29,8 @@ pub struct CharDeviceWrapper {
|
||||
device: &'static dyn CharDevice,
|
||||
}
|
||||
|
||||
#[auto_inode(error)]
|
||||
impl VnodeImpl for CharDeviceWrapper {
|
||||
fn create(&mut self, _at: VnodeRef, _name: &str, _kind: VnodeKind) -> Result<VnodeRef, Errno> {
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
@@ -54,19 +47,17 @@ impl VnodeImpl for CharDeviceWrapper {
|
||||
self.device.write(true, data)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
|
||||
panic!();
|
||||
fn is_ready(&mut self, _node: VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
self.device.is_ready(write)
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn stat(&mut self, _node: VnodeRef, _stat: &mut Stat) -> Result<(), Errno> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn ioctl(&mut self, _node: VnodeRef, cmd: IoctlCmd, ptr: usize, len: usize) -> Result<usize, Errno> {
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
self.device.ioctl(cmd, ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
+83
-35
@@ -1,9 +1,12 @@
|
||||
use crate::{VnodeKind, VnodeRef};
|
||||
use crate::{VnodeKind, VnodeRef, Vnode};
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::RefCell;
|
||||
use core::cmp::min;
|
||||
use error::Errno;
|
||||
use libcommon::{Read, Seek, SeekDir, Write};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::DirectoryEntry,
|
||||
traits::{Read, Seek, SeekDir, Write},
|
||||
};
|
||||
|
||||
struct NormalFile {
|
||||
vnode: VnodeRef,
|
||||
@@ -95,6 +98,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 {
|
||||
@@ -116,6 +124,69 @@ impl File {
|
||||
pub fn is_cloexec(&self) -> bool {
|
||||
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(".");
|
||||
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("..");
|
||||
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());
|
||||
});
|
||||
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 {
|
||||
@@ -133,11 +204,13 @@ impl Drop for File {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
use libsys::{stat::OpenFlags, ioctl::IoctlCmd, stat::Stat};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
|
||||
struct DummyInode;
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DummyInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
@@ -150,25 +223,17 @@ mod tests {
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
#[cfg(test)]
|
||||
println!("read {}", pos);
|
||||
println!("read {} at {}", data.len(), pos);
|
||||
let len = 123;
|
||||
if pos >= len {
|
||||
return Ok(0);
|
||||
@@ -183,41 +248,24 @@ mod tests {
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normal_read() {
|
||||
let node = Vnode::new("", VnodeKind::Regular, 0);
|
||||
node.set_data(Box::new(DummyInode {}));
|
||||
let mut file = node.open().unwrap();
|
||||
|
||||
match &file.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
assert!(Rc::ptr_eq(&inner.vnode, &node));
|
||||
assert_eq!(inner.pos, 0);
|
||||
}
|
||||
_ => panic!("Invalid file.inner"),
|
||||
}
|
||||
|
||||
let mut file = node.open(OpenFlags::O_RDONLY).unwrap();
|
||||
let mut buf = [0u8; 4096];
|
||||
|
||||
assert_eq!(file.read(&mut buf[0..32]).unwrap(), 32);
|
||||
assert_eq!(file.borrow_mut().read(&mut buf[0..32]).unwrap(), 32);
|
||||
for i in 0..32 {
|
||||
assert_eq!((i & 0xFF) as u8, buf[i]);
|
||||
}
|
||||
assert_eq!(file.read(&mut buf[0..64]).unwrap(), 64);
|
||||
assert_eq!(file.borrow_mut().read(&mut buf[0..64]).unwrap(), 64);
|
||||
for i in 0..64 {
|
||||
assert_eq!(((i + 32) & 0xFF) as u8, buf[i]);
|
||||
}
|
||||
assert_eq!(file.read(&mut buf[0..64]).unwrap(), 27);
|
||||
assert_eq!(file.borrow_mut().read(&mut buf[0..64]).unwrap(), 27);
|
||||
for i in 0..27 {
|
||||
assert_eq!(((i + 96) & 0xFF) as u8, buf[i]);
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ use crate::{BlockDevice, VnodeRef};
|
||||
use alloc::rc::Rc;
|
||||
use core::any::Any;
|
||||
use core::cell::Ref;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// General filesystem interface
|
||||
pub trait Filesystem {
|
||||
|
||||
+30
-32
@@ -1,19 +1,28 @@
|
||||
use crate::{FileMode, FileRef, OpenFlags, VnodeKind, VnodeRef};
|
||||
use error::Errno;
|
||||
use libcommon::{path_component_left, path_component_right};
|
||||
use crate::{FileRef, VnodeKind, VnodeRef};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
path::{path_component_left, path_component_right},
|
||||
stat::{FileMode, GroupId, OpenFlags, UserId},
|
||||
};
|
||||
|
||||
/// I/O context structure
|
||||
#[derive(Clone)]
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -38,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);
|
||||
}
|
||||
@@ -110,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)]
|
||||
@@ -117,9 +141,11 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{Vnode, VnodeImpl, VnodeKind};
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat};
|
||||
|
||||
pub struct DummyInode;
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DummyInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
@@ -132,37 +158,9 @@ mod tests {
|
||||
Ok(vnode)
|
||||
}
|
||||
|
||||
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+5
-2
@@ -7,10 +7,13 @@
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[macro_use]
|
||||
extern crate fs_macros;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub use syscall::stat::{FileMode, OpenFlags, Stat};
|
||||
pub use syscall::ioctl::IoctlCmd;
|
||||
// pub use libsys::stat::{FileMode, OpenFlags, Stat};
|
||||
// pub use libsys::ioctl::IoctlCmd;
|
||||
|
||||
mod block;
|
||||
pub use block::BlockDevice;
|
||||
|
||||
+161
-50
@@ -1,8 +1,12 @@
|
||||
use crate::{File, FileMode, FileRef, Filesystem, IoctlCmd, OpenFlags, Stat};
|
||||
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::{RefCell, RefMut, Ref};
|
||||
use core::fmt;
|
||||
use error::Errno;
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
stat::{AccessMode, DirectoryEntry, FileMode, OpenFlags, Stat},
|
||||
};
|
||||
|
||||
/// Convenience type alias for [Rc<Vnode>]
|
||||
pub type VnodeRef = Rc<Vnode>;
|
||||
@@ -27,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
|
||||
@@ -70,12 +75,24 @@ 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
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
@@ -90,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 {
|
||||
@@ -115,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);
|
||||
@@ -151,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
|
||||
@@ -223,6 +261,29 @@ 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> {
|
||||
@@ -296,35 +357,55 @@ 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() {
|
||||
let pos = data.open(self.clone(), flags)?;
|
||||
Ok(File::normal(self.clone(), pos, open_flags))
|
||||
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
|
||||
Ok(File::normal(self.clone(), File::POS_CACHE_DOT, open_flags))
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
let pos = data.open(self.clone(), flags)?;
|
||||
Ok(File::normal(self.clone(), pos, open_flags))
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Closes a vnode
|
||||
pub fn close(self: &VnodeRef) -> Result<(), Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.close(self.clone())
|
||||
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.close(self.clone())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,9 +458,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)
|
||||
}
|
||||
@@ -393,6 +481,48 @@ impl Vnode {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the node is ready for operation
|
||||
pub fn is_ready(self: &VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.is_ready(self.clone(), write)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
return Ok(());
|
||||
} 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
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Vnode {
|
||||
@@ -405,8 +535,10 @@ impl fmt::Debug for Vnode {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat};
|
||||
pub struct DummyInode;
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DummyInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
@@ -426,30 +558,6 @@ mod tests {
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -469,10 +577,13 @@ mod tests {
|
||||
|
||||
root.set_data(Box::new(DummyInode {}));
|
||||
|
||||
let node = root.mkdir("test", FileMode::default_dir()).unwrap();
|
||||
let node = root
|
||||
.create("test", FileMode::default_dir(), VnodeKind::Directory)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
root.mkdir("test", FileMode::default_dir()).unwrap_err(),
|
||||
root.create("test", FileMode::default_dir(), VnodeKind::Directory)
|
||||
.unwrap_err(),
|
||||
Errno::AlreadyExists
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -10,23 +10,24 @@ name = "kernel"
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
error = { path = "../error" }
|
||||
vfs = { path = "../fs/vfs" }
|
||||
memfs = { path = "../fs/memfs" }
|
||||
libcommon = { path = "../libcommon" }
|
||||
syscall = { path = "../syscall" }
|
||||
libsys = { path = "../libsys" }
|
||||
cfg-if = "1.x.x"
|
||||
tock-registers = "0.7.x"
|
||||
fdt-rs = { version = "0.x.x", default-features = false }
|
||||
bitflags = "^1.3.0"
|
||||
kernel-macros = { path = "macros" }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
cortex-a = { version = "6.x.x" }
|
||||
|
||||
[features]
|
||||
default = ["aggressive_syscall"]
|
||||
pl011 = []
|
||||
pl031 = []
|
||||
verbose = []
|
||||
aggressive_syscall = []
|
||||
|
||||
mach_qemu = ["pl011", "pl031"]
|
||||
mach_orangepi3 = []
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
[package]
|
||||
name = "libcommon"
|
||||
name = "kernel-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
error = { path = "../error" }
|
||||
syn = "^1.0.81"
|
||||
quote = "^1.0.10"
|
||||
@@ -0,0 +1,42 @@
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
#[proc_macro_derive(TtyCharDevice)]
|
||||
pub fn derive_tty_char_device(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
if !ast.generics.params.is_empty() {
|
||||
panic!(
|
||||
"Derived TtyCharDevice cannot have generic parameters: {:?}",
|
||||
ast.ident
|
||||
);
|
||||
}
|
||||
let ident = ast.ident;
|
||||
|
||||
quote! {
|
||||
impl vfs::CharDevice for #ident {
|
||||
fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, libsys::error::Errno> {
|
||||
assert!(blocking);
|
||||
crate::dev::tty::TtyDevice::line_read(self, data)
|
||||
}
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, libsys::error::Errno> {
|
||||
assert!(blocking);
|
||||
crate::dev::tty::TtyDevice::line_write(self, data)
|
||||
}
|
||||
fn ioctl(&self, cmd: libsys::ioctl::IoctlCmd, ptr: usize, len: usize) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
crate::dev::tty::TtyDevice::tty_ioctl(self, cmd, ptr, len)
|
||||
}
|
||||
fn is_ready(&self, write: bool) -> Result<bool, libsys::error::Errno> {
|
||||
crate::dev::tty::TtyDevice::is_ready(self, write)
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
@@ -14,7 +14,7 @@ use crate::config::{ConfigKey, CONFIG};
|
||||
use crate::dev::fdt::find_prop;
|
||||
use crate::dev::{fdt::DeviceTree, irq::IntSource, Device};
|
||||
use crate::fs::devfs;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
//use crate::debug::Level;
|
||||
use crate::mem::{
|
||||
self, heap,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct Context {
|
||||
/// Thread's kernel stack pointer
|
||||
pub k_sp: usize, // 0x00
|
||||
|
||||
stack_base_phys: usize,
|
||||
stack_base: usize,
|
||||
stack_page_count: usize,
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ impl Context {
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
|
||||
stack_base_phys: stack.bp,
|
||||
stack_base: stack.bp,
|
||||
stack_page_count: 8,
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,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
|
||||
@@ -85,7 +85,7 @@ impl Context {
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
|
||||
stack_base_phys: stack.bp,
|
||||
stack_base: stack.bp,
|
||||
stack_page_count: 8,
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,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);
|
||||
@@ -104,11 +104,39 @@ impl Context {
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
|
||||
stack_base_phys: stack.bp,
|
||||
stack_base: stack.bp,
|
||||
stack_page_count: 8,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs an uninitialized thread context
|
||||
pub fn empty() -> Self {
|
||||
let stack = Stack::new(8);
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
stack_base: stack.bp,
|
||||
stack_page_count: 8
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up a context for signal entry
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: may clobber an already active context
|
||||
pub unsafe fn setup_signal_entry(&mut self, entry: usize, arg: usize, ttbr0: usize, ustack: usize) {
|
||||
let mut stack = Stack::from_base_size(self.stack_base, self.stack_page_count);
|
||||
|
||||
stack.push(entry);
|
||||
stack.push(arg);
|
||||
stack.push(0);
|
||||
stack.push(ustack);
|
||||
|
||||
stack.setup_common(__aa64_ctx_enter_user as usize, ttbr0);
|
||||
|
||||
self.k_sp = stack.sp;
|
||||
}
|
||||
|
||||
/// Performs initial thread entry
|
||||
///
|
||||
/// # Safety
|
||||
@@ -140,21 +168,28 @@ impl Stack {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn from_base_size(bp: usize, page_count: usize) -> Stack {
|
||||
Stack {
|
||||
bp,
|
||||
sp: bp + page_count * mem::PAGE_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use cortex_a::registers::{TPIDR_EL1, MPIDR_EL1};
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use core::ptr::null_mut;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::sync::atomic::{Ordering, AtomicUsize};
|
||||
use crate::proc::{Scheduler, process::Context};
|
||||
use crate::proc::Scheduler;
|
||||
use crate::util::InitOnce;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr::null_mut;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use cortex_a::registers::{MPIDR_EL1, TPIDR_EL1};
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Cpu {
|
||||
active_context: *mut Context, // 0x00
|
||||
counter: AtomicUsize, // 0x08
|
||||
counter: AtomicUsize, // 0x08
|
||||
|
||||
id: usize,
|
||||
scheduler: Scheduler
|
||||
scheduler: Scheduler,
|
||||
}
|
||||
|
||||
impl Cpu {
|
||||
pub fn new(id: usize) -> Self {
|
||||
Self {
|
||||
active_context: null_mut(),
|
||||
counter: AtomicUsize::new(0),
|
||||
|
||||
id,
|
||||
scheduler: Scheduler::new()
|
||||
scheduler: Scheduler::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +44,9 @@ impl Cpu {
|
||||
}
|
||||
|
||||
pub unsafe fn cpus() -> impl Iterator<Item = &'static mut Cpu> {
|
||||
CPUS[..CPU_COUNT.load(Ordering::Acquire)].iter_mut().map(|c| c.assume_init_mut())
|
||||
CPUS[..CPU_COUNT.load(Ordering::Acquire)]
|
||||
.iter_mut()
|
||||
.map(|c| c.assume_init_mut())
|
||||
}
|
||||
|
||||
pub unsafe fn by_index(idx: usize) -> &'static mut Cpu {
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
use crate::arch::machine;
|
||||
use crate::debug::Level;
|
||||
use crate::dev::irq::{IntController, IrqContext};
|
||||
use crate::proc::{sched, Process};
|
||||
use crate::mem;
|
||||
use crate::proc::{sched, Thread};
|
||||
use crate::syscall;
|
||||
use ::syscall::abi;
|
||||
use cortex_a::registers::{ESR_EL1, FAR_EL1};
|
||||
use libsys::{abi::SystemCall, signal::Signal};
|
||||
use tock_registers::interfaces::Readable;
|
||||
|
||||
/// Trapped SIMD/FP functionality
|
||||
@@ -76,7 +76,7 @@ fn dump_data_abort(level: Level, esr: u64, far: u64) {
|
||||
} else {
|
||||
print!(level, " at UNKNOWN");
|
||||
}
|
||||
println!(level, "");
|
||||
println!(level, "\x1B[0m");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -88,15 +88,21 @@ 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;
|
||||
|
||||
// TODO handle scenarios when sheduler is not yet initialized
|
||||
if iss & (1 << 6) != 0 && far < mem::KERNEL_OFFSET {
|
||||
let thread = Thread::current();
|
||||
let proc = thread.owner().unwrap();
|
||||
|
||||
if far < mem::KERNEL_OFFSET {
|
||||
let proc = Process::current();
|
||||
|
||||
if let Err(e) = proc.manipulate_space(|space| space.try_cow_copy(far)) {
|
||||
if proc
|
||||
.manipulate_space(|space| space.try_cow_copy(far))
|
||||
.is_err()
|
||||
{
|
||||
// Kill program
|
||||
errorln!("Data abort from {:#x}", exc.elr_el1);
|
||||
dump_data_abort(Level::Error, esr, far as u64);
|
||||
panic!("CoW copy returned {:?}", e);
|
||||
proc.enter_fault_signal(thread, Signal::SegmentationFault);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
@@ -110,34 +116,47 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
||||
}
|
||||
|
||||
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) => {
|
||||
warnln!("fork() syscall failed: {:?}", err);
|
||||
exc.x[0] = usize::MAX;
|
||||
}
|
||||
}
|
||||
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] = pid.value() as usize,
|
||||
Err(err) => {
|
||||
warnln!("syscall {} failed: {:?}", exc.x[8], err);
|
||||
exc.x[0] = usize::MAX
|
||||
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,
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::dev::{
|
||||
use crate::mem::virt::{DeviceMemory, DeviceMemoryIo};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
mod gicc;
|
||||
use gicc::Gicc;
|
||||
@@ -30,7 +30,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]>,
|
||||
}
|
||||
|
||||
@@ -89,23 +88,27 @@ impl IntController for Gic {
|
||||
return;
|
||||
}
|
||||
|
||||
//<<<<<<< HEAD
|
||||
if irq_number == 1 {
|
||||
gicc.clear_irq(irq_number as u32, ic);
|
||||
debugln!("Received IPI");
|
||||
loop {}
|
||||
}
|
||||
|
||||
if self.scheduler_irq.0 == irq_number {
|
||||
use crate::proc::sched;
|
||||
use cortex_a::registers::{CNTP_TVAL_EL0, CNTP_CTL_EL0};
|
||||
use tock_registers::interfaces::Writeable;
|
||||
use crate::arch::platform::cpu::Cpu;
|
||||
gicc.clear_irq(irq_number as u32, ic);
|
||||
CNTP_TVAL_EL0.set(1000000);
|
||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
|
||||
sched::switch(false);
|
||||
return;
|
||||
}
|
||||
//
|
||||
// if self.scheduler_irq.0 == irq_number {
|
||||
// use crate::proc::sched;
|
||||
// use cortex_a::registers::{CNTP_TVAL_EL0, CNTP_CTL_EL0};
|
||||
// use tock_registers::interfaces::Writeable;
|
||||
// use crate::arch::platform::cpu::Cpu;
|
||||
// gicc.clear_irq(irq_number as u32, ic);
|
||||
// CNTP_TVAL_EL0.set(1000000);
|
||||
// CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
|
||||
// sched::switch(false);
|
||||
// return;
|
||||
// }
|
||||
//=======
|
||||
gicc.clear_irq(irq_number as u32, ic);
|
||||
//>>>>>>> feat/thread
|
||||
|
||||
{
|
||||
// TODO make timer interrupt a special case and drop table lock
|
||||
@@ -118,8 +121,6 @@ impl IntController for Gic {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gicc.clear_irq(irq_number as u32, ic);
|
||||
}
|
||||
|
||||
fn register_handler(
|
||||
@@ -157,13 +158,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]),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::dev::{
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::register_structs;
|
||||
use tock_registers::registers::ReadWrite;
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::dev::{
|
||||
};
|
||||
use crate::fs::devfs::{self, CharDeviceType};
|
||||
use crate::mem::phys;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
mod gpio;
|
||||
mod rtc;
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::dev::{
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
|
||||
@@ -8,11 +8,10 @@ use crate::dev::{
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
use tock_registers::registers::{Aliased, ReadOnly, ReadWrite};
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
use vfs::{CharDevice, IoctlCmd};
|
||||
|
||||
register_bitfields! [
|
||||
u32,
|
||||
@@ -78,6 +77,7 @@ struct UartInner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
}
|
||||
|
||||
#[derive(TtyCharDevice)]
|
||||
pub(super) struct Uart {
|
||||
inner: InitOnce<IrqSafeSpinLock<UartInner>>,
|
||||
ring: CharRing<16>,
|
||||
@@ -123,22 +123,6 @@ impl SerialDevice for Uart {
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for Uart {
|
||||
fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
assert!(blocking);
|
||||
self.line_read(data)
|
||||
}
|
||||
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Errno> {
|
||||
assert!(blocking);
|
||||
self.line_write(data)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, ptr: usize, len: usize) -> Result<usize, Errno> {
|
||||
self.tty_ioctl(cmd, ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl TtyDevice<16> for Uart {
|
||||
fn ring(&self) -> &CharRing<16> {
|
||||
&self.ring
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::dev::Device;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::Writeable, register_bitfields, register_structs, registers::ReadWrite,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::dev::{
|
||||
};
|
||||
use crate::fs::devfs::{self, CharDeviceType};
|
||||
use crate::mem::phys;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
pub use gic::IrqNumber;
|
||||
|
||||
@@ -84,6 +84,6 @@ pub fn ipi_sender() -> &'static Gic {
|
||||
|
||||
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);
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::dev::Device;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use core::fmt;
|
||||
use cortex_a::registers::MPIDR_EL1;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::register_structs;
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::dev::Device;
|
||||
use crate::mem::{self, virt::DeviceMemoryIo};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::registers::{ReadOnly, WriteOnly};
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::dev::{
|
||||
Device,
|
||||
};
|
||||
use crate::mem::phys;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
pub mod irqchip;
|
||||
pub use irqchip::{Bcm283xIrqchip, IrqNumber};
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::mem::{
|
||||
phys::{self, PageUsage},
|
||||
};
|
||||
use cortex_a::registers::MPIDR_EL1;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use fdt_rs::prelude::*;
|
||||
use tock_registers::interfaces::Readable;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::dev::{
|
||||
};
|
||||
use core::time::Duration;
|
||||
use cortex_a::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0};
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
|
||||
/// Generic timer struct
|
||||
@@ -34,15 +34,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);
|
||||
todo!();
|
||||
//<<<<<<< HEAD
|
||||
// use crate::proc;
|
||||
// proc::wait::tick();
|
||||
// proc::switch();
|
||||
//=======
|
||||
// use crate::proc::sched;
|
||||
// sched::switch(false);
|
||||
//>>>>>>> 61fa9d1 (feat: dirty smp)
|
||||
use crate::proc::{wait, sched};
|
||||
wait::tick();
|
||||
sched::switch(false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use libsys::debug::TraceLevel;
|
||||
use core::fmt;
|
||||
|
||||
/// Kernel logging levels
|
||||
@@ -28,6 +29,18 @@ pub enum Level {
|
||||
Error,
|
||||
}
|
||||
|
||||
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> {
|
||||
inner: &'static T,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Device tree facilities
|
||||
use crate::debug::Level;
|
||||
use error::Errno;
|
||||
use fdt_rs::prelude::*;
|
||||
use fdt_rs::{
|
||||
base::DevTree,
|
||||
@@ -8,7 +7,7 @@ use fdt_rs::{
|
||||
iters::DevTreeIndexCompatibleNodeIter, DevTreeIndex, DevTreeIndexNode, DevTreeIndexProp,
|
||||
},
|
||||
};
|
||||
use libcommon::path_component_left;
|
||||
use libsys::{error::Errno, path::path_component_left};
|
||||
|
||||
#[repr(align(16))]
|
||||
struct Wrap {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! GPIO and pin control interfaces
|
||||
|
||||
use crate::dev::Device;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// Pin function mode
|
||||
pub enum PinMode {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use crate::arch::platform::smp::NodeAddress;
|
||||
use crate::dev::Device;
|
||||
use core::marker::PhantomData;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// Token to indicate the local core is running in IRQ context
|
||||
pub struct IrqContext<'irq_context> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Module for device interfaces and drivers
|
||||
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
// Device classes
|
||||
pub mod fdt;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::dev::Device;
|
||||
use core::fmt;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
pub mod pcie;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::dev::{
|
||||
};
|
||||
use crate::mem::virt::DeviceMemory;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// GPEX host controller struct
|
||||
pub struct GenericPcieHost {
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::dev::{
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! SD host controller interface and card operation facilities
|
||||
use crate::dev::Device;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use vfs::BlockDevice;
|
||||
|
||||
/// Generic SD/MMC host controller interface
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Module for serial device drivers
|
||||
|
||||
use crate::dev::Device;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
#[cfg(feature = "pl011")]
|
||||
pub mod pl011;
|
||||
|
||||
@@ -10,14 +10,12 @@ use crate::dev::{
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use core::fmt;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use vfs::{CharDevice, IoctlCmd};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -78,6 +76,7 @@ struct Pl011Inner {
|
||||
}
|
||||
|
||||
/// Device struct for PL011
|
||||
#[derive(TtyCharDevice)]
|
||||
pub struct Pl011 {
|
||||
inner: InitOnce<IrqSafeSpinLock<Pl011Inner>>,
|
||||
ring: CharRing<16>,
|
||||
@@ -117,16 +116,16 @@ impl Pl011Inner {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for Pl011Inner {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for &c in s.as_bytes() {
|
||||
unsafe {
|
||||
self.send(c);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
// impl fmt::Write for Pl011Inner {
|
||||
// fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
// for &c in s.as_bytes() {
|
||||
// unsafe {
|
||||
// self.send(c);
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
impl IntSource for Pl011 {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
@@ -167,21 +166,21 @@ impl SerialDevice for Pl011 {
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for Pl011 {
|
||||
fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
assert!(blocking);
|
||||
self.line_read(data)
|
||||
}
|
||||
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Errno> {
|
||||
assert!(blocking);
|
||||
self.line_write(data)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, ptr: usize, len: usize) -> Result<usize, Errno> {
|
||||
self.tty_ioctl(cmd, ptr, len)
|
||||
}
|
||||
}
|
||||
// impl CharDevice for Pl011 {
|
||||
// fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
// assert!(blocking);
|
||||
// self.line_read(data)
|
||||
// }
|
||||
//
|
||||
// fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Errno> {
|
||||
// assert!(blocking);
|
||||
// self.line_write(data)
|
||||
// }
|
||||
//
|
||||
// fn ioctl(&self, cmd: IoctlCmd, ptr: usize, len: usize) -> Result<usize, Errno> {
|
||||
// self.tty_ioctl(cmd, ptr, len)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl TtyDevice<16> for Pl011 {
|
||||
fn ring(&self) -> &CharRing<16> {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::dev::Device;
|
||||
use core::time::Duration;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// Interface for generic timestamp source
|
||||
pub trait TimestampSource: Device {
|
||||
|
||||
+96
-20
@@ -1,15 +1,16 @@
|
||||
//! Teletype (TTY) device facilities
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use crate::proc::wait::Wait;
|
||||
use crate::proc::{Process, wait::{Wait, WAIT_SELECT}};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use error::Errno;
|
||||
use syscall::{
|
||||
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 vfs::CharDevice;
|
||||
use crate::syscall::arg;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CharRingInner<const N: usize> {
|
||||
@@ -17,6 +18,7 @@ struct CharRingInner<const N: usize> {
|
||||
wr: usize,
|
||||
data: [u8; N],
|
||||
flags: u8,
|
||||
fg_pgid: Option<Pid>,
|
||||
}
|
||||
|
||||
/// Ring buffer for TTYs
|
||||
@@ -27,26 +29,45 @@ pub struct CharRing<const N: usize> {
|
||||
inner: IrqSafeSpinLock<CharRingInner<N>>,
|
||||
}
|
||||
|
||||
/// Generic teletype device interface
|
||||
pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
/// Returns a reference to character device's ring buffer
|
||||
fn ring(&self) -> &CharRing<N>;
|
||||
|
||||
fn tty_ioctl(&self, cmd: IoctlCmd, ptr: usize, len: usize) -> Result<usize, Errno> {
|
||||
/// 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 {
|
||||
todo!()
|
||||
} else {
|
||||
Ok(ring.is_readable())
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a TTY control request
|
||||
fn tty_ioctl(&self, cmd: IoctlCmd, ptr: usize, _len: usize) -> Result<usize, Errno> {
|
||||
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(unsafe { Pid::from_raw(*src) });
|
||||
Ok(0)
|
||||
},
|
||||
_ => Err(Errno::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes and writes output an output byte
|
||||
fn line_send(&self, byte: u8) -> Result<(), Errno> {
|
||||
let config = self.ring().config.lock();
|
||||
|
||||
@@ -57,10 +78,19 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
self.send(byte)
|
||||
}
|
||||
|
||||
/// Receives input bytes and processes them
|
||||
fn recv_byte(&self, mut byte: u8) {
|
||||
let ring = self.ring();
|
||||
let config = ring.config.lock();
|
||||
|
||||
if byte == b'@' {
|
||||
use crate::mem::phys;
|
||||
let stat = phys::statistics();
|
||||
debugln!("Physical memory stats:");
|
||||
debugln!("{:#?}", stat);
|
||||
return;
|
||||
}
|
||||
|
||||
if byte == b'\r' && config.iflag.contains(TermiosIflag::ICRNL) {
|
||||
byte = b'\n';
|
||||
}
|
||||
@@ -89,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();
|
||||
}
|
||||
|
||||
@@ -97,7 +140,7 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
let ring = self.ring();
|
||||
let mut config = ring.config.lock();
|
||||
|
||||
if data.len() == 0 {
|
||||
if data.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
@@ -105,7 +148,7 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
drop(config);
|
||||
let byte = ring.getc()?;
|
||||
data[0] = byte;
|
||||
return Ok(1);
|
||||
Ok(1)
|
||||
} else {
|
||||
let mut rem = data.len();
|
||||
let mut off = 0;
|
||||
@@ -132,7 +175,7 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
let idx = data[..off].iter().rposition(|&ch| ch == b' ').unwrap_or(0);
|
||||
let len = off;
|
||||
|
||||
for i in idx..len {
|
||||
for _ in idx..len {
|
||||
self.raw_write(b"\x1B[D \x1B[D").ok();
|
||||
off -= 1;
|
||||
rem += 1;
|
||||
@@ -165,6 +208,7 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes and writes string bytes
|
||||
fn line_write(&self, data: &[u8]) -> Result<usize, Errno> {
|
||||
for &byte in data.iter() {
|
||||
self.line_send(byte)?;
|
||||
@@ -172,6 +216,7 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
Ok(data.len())
|
||||
}
|
||||
|
||||
/// Writes string bytes without any processing
|
||||
fn raw_write(&self, data: &[u8]) -> Result<usize, Errno> {
|
||||
for &byte in data.iter() {
|
||||
self.send(byte)?;
|
||||
@@ -209,14 +254,48 @@ 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();
|
||||
if config.lflag.contains(TermiosLflag::ICANON) {
|
||||
// TODO optimize this somehow?
|
||||
let mut rd = inner.rd;
|
||||
let mut count = 0usize;
|
||||
loop {
|
||||
let readable = if rd <= inner.wr {
|
||||
(inner.wr - rd) > 0
|
||||
} else {
|
||||
(inner.wr + (N - rd)) > 0
|
||||
};
|
||||
|
||||
if !readable {
|
||||
break;
|
||||
}
|
||||
|
||||
let byte = inner.data[rd];
|
||||
if byte == b'\n' || byte == config.chars.eof {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
rd = (rd + 1) % N;
|
||||
}
|
||||
|
||||
count != 0 || inner.flags != 0
|
||||
} else {
|
||||
inner.is_readable() || inner.flags != 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,15 +311,10 @@ impl<const N: usize> CharRing<N> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if lock.flags != 0 {
|
||||
if lock.flags & (1 << 0) != 0 {
|
||||
lock.flags &= !(1 << 0);
|
||||
return Err(Errno::EndOfFile);
|
||||
}
|
||||
todo!();
|
||||
}
|
||||
let byte = lock.read_unchecked();
|
||||
drop(lock);
|
||||
self.wait_write.wakeup_one();
|
||||
WAIT_SELECT.wakeup_all();
|
||||
Ok(byte)
|
||||
}
|
||||
|
||||
@@ -251,7 +325,9 @@ impl<const N: usize> CharRing<N> {
|
||||
todo!()
|
||||
}
|
||||
lock.write_unchecked(ch);
|
||||
drop(lock);
|
||||
self.wait_read.wakeup_one();
|
||||
WAIT_SELECT.wakeup_all();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use crate::util::InitOnce;
|
||||
use alloc::boxed::Box;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use 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
|
||||
@@ -27,7 +29,8 @@ pub fn root() -> &'static VnodeRef {
|
||||
fn _add_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);
|
||||
|
||||
@@ -3,6 +3,8 @@ use crate::mem::{
|
||||
self,
|
||||
phys::{self, PageUsage},
|
||||
};
|
||||
use libsys::{error::Errno, stat::MountOptions};
|
||||
use vfs::VnodeRef;
|
||||
use memfs::BlockAllocator;
|
||||
|
||||
pub mod devfs;
|
||||
@@ -25,3 +27,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();
|
||||
|
||||
if fs_name == "devfs" {
|
||||
Ok(devfs::root().clone())
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
+8
-6
@@ -4,8 +4,9 @@ use crate::config::{ConfigKey, CONFIG};
|
||||
use crate::fs::{devfs, MemfsBlockAlloc};
|
||||
use crate::mem;
|
||||
use crate::proc::{elf, Process};
|
||||
use libsys::stat::{FileDescriptor, OpenFlags, UserId, GroupId};
|
||||
use memfs::Ramfs;
|
||||
use vfs::{Filesystem, Ioctx, OpenFlags};
|
||||
use vfs::{Filesystem, Ioctx};
|
||||
|
||||
/// Kernel init process function
|
||||
#[inline(never)]
|
||||
@@ -28,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();
|
||||
@@ -50,13 +51,14 @@ pub extern "C" fn init_fn(_arg: usize) -> ! {
|
||||
let stdout = tty_node.open(OpenFlags::O_WRONLY).unwrap();
|
||||
let stderr = stdout.clone();
|
||||
|
||||
io.set_file(0, stdin).unwrap();
|
||||
io.set_file(1, stdout).unwrap();
|
||||
io.set_file(2, stderr).unwrap();
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#![no_main]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate kernel_macros;
|
||||
#[macro_use]
|
||||
extern crate cfg_if;
|
||||
#[macro_use]
|
||||
|
||||
@@ -26,73 +26,3 @@ pub fn kernel_end_phys() -> usize {
|
||||
}
|
||||
unsafe { &__kernel_end as *const _ as usize - KERNEL_OFFSET }
|
||||
}
|
||||
|
||||
/// See memcpy(3p).
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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 {
|
||||
while len != 0 {
|
||||
len -= 1;
|
||||
*dst.add(len) = *src.add(len);
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
/// See memcmp(3p).
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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) {
|
||||
return -1;
|
||||
}
|
||||
if *a.add(len) > *b.add(len) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// See memmove(3p)
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: writes to arbitrary memory locations, performs no pointer
|
||||
/// validation.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memmove(dst: *mut u8, src: *mut u8, len: usize) -> *mut u8 {
|
||||
if dst < src {
|
||||
for i in 0..len {
|
||||
*dst.add(i) = *src.add(i);
|
||||
}
|
||||
} else {
|
||||
for i in 0..len {
|
||||
*dst.add(len - (i + 1)) = *src.add(len - (i + 1));
|
||||
}
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
/// See memset(3p)
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: writes to arbitrary memory locations, performs no pointer
|
||||
/// validation.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memset(buf: *mut u8, val: u8, mut len: usize) -> *mut u8 {
|
||||
while len != 0 {
|
||||
len -= 1;
|
||||
*buf.add(len) = val;
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::{PageInfo, PageUsage};
|
||||
use crate::mem::{memcpy, memset, virtualize, PAGE_SIZE};
|
||||
use super::{PageInfo, PageUsage, PageStatistics};
|
||||
use crate::mem::{virtualize, PAGE_SIZE};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use core::mem;
|
||||
use error::Errno;
|
||||
use libsys::{error::Errno, mem::memcpy};
|
||||
|
||||
pub unsafe trait Manager {
|
||||
fn alloc_page(&mut self, pu: PageUsage) -> Result<usize, Errno>;
|
||||
@@ -10,11 +10,14 @@ pub unsafe trait Manager {
|
||||
fn free_page(&mut self, page: usize) -> Result<(), Errno>;
|
||||
fn copy_cow_page(&mut self, src: usize) -> Result<usize, Errno>;
|
||||
fn fork_page(&mut self, src: usize) -> Result<usize, Errno>;
|
||||
fn statistics(&self) -> PageStatistics;
|
||||
// TODO status()
|
||||
}
|
||||
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 {
|
||||
@@ -32,6 +35,15 @@ impl SimpleManager {
|
||||
}
|
||||
Self {
|
||||
base_index: base / PAGE_SIZE,
|
||||
last_index: 0,
|
||||
stats: PageStatistics {
|
||||
available: 0,
|
||||
kernel: 0,
|
||||
kernel_heap: 0,
|
||||
paging: 0,
|
||||
user_private: 0,
|
||||
filesystem: 0
|
||||
},
|
||||
pages,
|
||||
}
|
||||
}
|
||||
@@ -39,6 +51,7 @@ impl SimpleManager {
|
||||
let page = &mut self.pages[self.page_index(addr)];
|
||||
assert!(page.refcount == 0 && page.usage == PageUsage::Reserved);
|
||||
page.usage = PageUsage::Available;
|
||||
self.stats.available += 1;
|
||||
}
|
||||
|
||||
fn page_index(&self, page: usize) -> usize {
|
||||
@@ -46,21 +59,61 @@ 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);
|
||||
}
|
||||
}
|
||||
Err(Errno::OutOfMemory)
|
||||
}
|
||||
|
||||
fn update_stats_alloc(&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> {
|
||||
self.alloc_single_index(pu)
|
||||
.map(|r| (self.base_index + r) * PAGE_SIZE)
|
||||
let res = self.alloc_single_index(pu)
|
||||
.map(|r| (self.base_index + r) * PAGE_SIZE);
|
||||
if res.is_ok() {
|
||||
self.update_stats_alloc(pu, 1);
|
||||
}
|
||||
res
|
||||
}
|
||||
fn alloc_contiguous_pages(&mut self, pu: PageUsage, count: usize) -> Result<usize, Errno> {
|
||||
'l0: for i in 0..self.pages.len() {
|
||||
@@ -75,6 +128,7 @@ unsafe impl Manager for SimpleManager {
|
||||
page.usage = pu;
|
||||
page.refcount = 1;
|
||||
}
|
||||
self.update_stats_alloc(pu, count);
|
||||
return Ok((self.base_index + i) * PAGE_SIZE);
|
||||
}
|
||||
Err(Errno::OutOfMemory)
|
||||
@@ -91,31 +145,40 @@ unsafe impl Manager for SimpleManager {
|
||||
assert_eq!(page.refcount, 1);
|
||||
page.usage = PageUsage::Available;
|
||||
page.refcount = 0;
|
||||
|
||||
self.last_index = index;
|
||||
}
|
||||
|
||||
// FIXME
|
||||
// self.update_stats_free(usage, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_cow_page(&mut self, src: usize) -> Result<usize, Errno> {
|
||||
let src_index = self.page_index(src);
|
||||
let page = &mut self.pages[src_index];
|
||||
let usage = page.usage;
|
||||
if usage != PageUsage::UserPrivate {
|
||||
panic!("CoW not available for non-UserPrivate pages: {:?}", usage);
|
||||
}
|
||||
let (usage, refcount) = {
|
||||
let page = &mut self.pages[src_index];
|
||||
let usage = page.usage;
|
||||
if usage != PageUsage::UserPrivate {
|
||||
panic!("CoW not available for non-UserPrivate pages: {:?}", usage);
|
||||
}
|
||||
let count = page.refcount;
|
||||
if count > 1 {
|
||||
page.refcount -= 1;
|
||||
}
|
||||
(usage, count)
|
||||
};
|
||||
|
||||
if page.refcount > 1 {
|
||||
page.refcount -= 1;
|
||||
drop(page);
|
||||
if refcount == 0 {
|
||||
Ok(src)
|
||||
} else {
|
||||
let dst_index = self.alloc_single_index(usage)?;
|
||||
let dst = (self.base_index + dst_index) * PAGE_SIZE;
|
||||
unsafe {
|
||||
memcpy(virtualize(dst) as *mut u8, virtualize(src) as *mut u8, 4096);
|
||||
}
|
||||
Ok(dst)
|
||||
} else {
|
||||
assert_eq!(page.refcount, 1);
|
||||
// No additional operations needed
|
||||
Ok(src)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +193,10 @@ unsafe impl Manager for SimpleManager {
|
||||
}
|
||||
Ok(src)
|
||||
}
|
||||
|
||||
fn statistics(&self) -> PageStatistics {
|
||||
self.stats.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) static MANAGER: IrqSafeSpinLock<Option<SimpleManager>> = IrqSafeSpinLock::new(None);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use crate::config::{ConfigKey, CONFIG};
|
||||
use crate::mem::PAGE_SIZE;
|
||||
use core::mem::size_of;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
mod manager;
|
||||
mod reserved;
|
||||
@@ -32,6 +32,18 @@ pub enum PageUsage {
|
||||
Filesystem,
|
||||
}
|
||||
|
||||
/// Represents counts of allocated/available pages
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PageStatistics {
|
||||
pub available: usize,
|
||||
pub kernel: usize,
|
||||
pub kernel_heap: usize,
|
||||
pub paging: usize,
|
||||
pub user_private: usize,
|
||||
pub filesystem: usize,
|
||||
}
|
||||
|
||||
/// Data structure representing a single physical memory page
|
||||
pub struct PageInfo {
|
||||
refcount: usize,
|
||||
@@ -66,18 +78,57 @@ impl Iterator for SimpleMemoryIterator {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose")]
|
||||
fn trace_alloc(loc: &core::panic::Location, pu: PageUsage, base: usize, count: usize) {
|
||||
use crate::debug::Level;
|
||||
println!(
|
||||
Level::Debug,
|
||||
"\x1B[36;1m[phys/alloc] {}:{} {:?} {:#x}..{:#x}\x1B[0m",
|
||||
loc.file(),
|
||||
loc.line(),
|
||||
pu,
|
||||
base,
|
||||
base + count * PAGE_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose")]
|
||||
fn trace_free(loc: &core::panic::Location, page: usize) {
|
||||
use crate::debug::Level;
|
||||
println!(
|
||||
Level::Debug,
|
||||
"\x1B[36;1m[phys/free] {}:{} {:#x}..{:#x}\x1B[0m",
|
||||
loc.file(),
|
||||
loc.line(),
|
||||
page,
|
||||
page + PAGE_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
/// Allocates a contiguous range of `count` physical memory pages.
|
||||
#[cfg_attr(feature = "verbose", track_caller)]
|
||||
pub fn alloc_contiguous_pages(pu: PageUsage, count: usize) -> Result<usize, Errno> {
|
||||
MANAGER
|
||||
let res = MANAGER
|
||||
.lock()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.alloc_contiguous_pages(pu, count)
|
||||
.alloc_contiguous_pages(pu, count);
|
||||
#[cfg(feature = "verbose")]
|
||||
if let Ok(base) = res {
|
||||
trace_alloc(&core::panic::Location::caller(), pu, base, count);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Allocates a single physical memory page.
|
||||
#[cfg_attr(feature = "verbose", track_caller)]
|
||||
pub fn alloc_page(pu: PageUsage) -> Result<usize, Errno> {
|
||||
MANAGER.lock().as_mut().unwrap().alloc_page(pu)
|
||||
let res = MANAGER.lock().as_mut().unwrap().alloc_page(pu);
|
||||
#[cfg(feature = "verbose")]
|
||||
if let Ok(base) = res {
|
||||
trace_alloc(&core::panic::Location::caller(), pu, base, 1);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Releases a single physical memory page back for further allocation.
|
||||
@@ -85,10 +136,26 @@ pub fn alloc_page(pu: PageUsage) -> Result<usize, Errno> {
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: accepts arbitrary `page` arguments
|
||||
#[cfg_attr(feature = "verbose", track_caller)]
|
||||
pub unsafe fn free_page(page: usize) -> Result<(), Errno> {
|
||||
#[cfg(feature = "verbose")]
|
||||
{
|
||||
trace_free(&core::panic::Location::caller(), page);
|
||||
}
|
||||
MANAGER.lock().as_mut().unwrap().free_page(page)
|
||||
}
|
||||
|
||||
/// Returns current statistics for page allocation
|
||||
pub fn statistics() -> PageStatistics {
|
||||
MANAGER.lock().as_ref().unwrap().statistics()
|
||||
}
|
||||
|
||||
/// Clones the source page.
|
||||
///
|
||||
/// If returned address is the same as `page`, this means
|
||||
/// `page`'s refcount has increased and the page is Copy-on-Write.
|
||||
/// This case has to be handled accordingly
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: accepts arbitrary `page` arguments
|
||||
@@ -96,6 +163,12 @@ pub unsafe fn fork_page(page: usize) -> Result<usize, Errno> {
|
||||
MANAGER.lock().as_mut().unwrap().fork_page(page)
|
||||
}
|
||||
|
||||
/// Copies a Copy-on-Write page. If refcount is already 1,
|
||||
/// page does not need to be copied and the same address is returned.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: accepts arbitrary `page` arguments
|
||||
pub unsafe fn copy_cow_page(page: usize) -> Result<usize, Errno> {
|
||||
MANAGER.lock().as_mut().unwrap().copy_cow_page(page)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::mem::{
|
||||
virt::{Entry, MapAttributes, Table},
|
||||
};
|
||||
use cortex_a::asm::barrier::{self, dsb, isb};
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
|
||||
const DEVICE_MAP_OFFSET: usize = mem::KERNEL_OFFSET + (256usize << 30);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use core::marker::PhantomData;
|
||||
use core::ops::Deref;
|
||||
use cortex_a::asm::barrier::{self, dsb, isb};
|
||||
use cortex_a::registers::TTBR0_EL1;
|
||||
use error::Errno;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::Writeable;
|
||||
|
||||
pub mod table;
|
||||
|
||||
+126
-25
@@ -5,7 +5,7 @@ use crate::mem::{
|
||||
phys::{self, PageUsage},
|
||||
};
|
||||
use core::ops::{Index, IndexMut};
|
||||
use error::Errno;
|
||||
use libsys::{error::Errno, mem::memset};
|
||||
|
||||
/// Transparent wrapper structure representing a single
|
||||
/// translation table entry
|
||||
@@ -38,6 +38,7 @@ bitflags! {
|
||||
/// This page is used for device-MMIO mapping and uses MAIR attribute #1
|
||||
const DEVICE = 1 << 2;
|
||||
|
||||
/// Pages marked with this bit are Copy-on-Write
|
||||
const EX_COW = 1 << 55;
|
||||
|
||||
/// UXN bit -- if set, page may not be used for instruction fetching from EL0
|
||||
@@ -204,11 +205,33 @@ impl Space {
|
||||
} else {
|
||||
l2_table[l2i] = Entry::table(phys, flags | MapAttributes::ACCESS);
|
||||
#[cfg(feature = "verbose")]
|
||||
debugln!("Map {:#x} -> {:#x}", virt, phys);
|
||||
debugln!("{:#p} Map {:#x} -> {:#x}, {:?}", self, virt, phys, flags);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Translates a virtual address into a corresponding physical one.
|
||||
///
|
||||
/// Only works for 4K pages atm.
|
||||
// TODO extract attributes
|
||||
pub fn translate(&mut self, virt: usize) -> Result<usize, Errno> {
|
||||
let l0i = virt >> 30;
|
||||
let l1i = (virt >> 21) & 0x1FF;
|
||||
let l2i = (virt >> 12) & 0x1FF;
|
||||
|
||||
let l1_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
|
||||
let l2_table = l1_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
|
||||
|
||||
let entry = l2_table[l2i];
|
||||
if entry.is_present() {
|
||||
Ok(unsafe { entry.address_unchecked() })
|
||||
} else {
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to resolve a page fault at `virt` address by copying the
|
||||
/// underlying Copy-on-Write mapping (if any is present)
|
||||
pub fn try_cow_copy(&mut self, virt: usize) -> Result<(), Errno> {
|
||||
let virt = virt & !0xFFF;
|
||||
let l0i = virt >> 30;
|
||||
@@ -227,7 +250,11 @@ impl Space {
|
||||
|
||||
let src_phys = unsafe { entry.address_unchecked() };
|
||||
if !entry.is_cow() {
|
||||
warnln!("Entry is not marked as CoW: {:#x}, points to {:#x}", virt, src_phys);
|
||||
warnln!(
|
||||
"Entry is not marked as CoW: {:#x}, points to {:#x}",
|
||||
virt,
|
||||
src_phys
|
||||
);
|
||||
return Err(Errno::DoesNotExist);
|
||||
}
|
||||
|
||||
@@ -240,6 +267,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()?;
|
||||
@@ -264,13 +356,20 @@ 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);
|
||||
|
||||
res.map(virt_addr, dst_phys, flags)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,6 +379,13 @@ impl Space {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Releases all the mappings from the address space. Frees all
|
||||
/// memory pages referenced by this space as well as those used for
|
||||
/// its paging tables.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: may invalidate currently active address space
|
||||
pub unsafe fn release(space: &mut Self) {
|
||||
for l0i in 0..512 {
|
||||
let l0_entry = space.0[l0i];
|
||||
@@ -288,8 +394,7 @@ impl Space {
|
||||
}
|
||||
|
||||
assert!(l0_entry.is_table());
|
||||
let l1_table =
|
||||
unsafe { &mut *(mem::virtualize(l0_entry.address_unchecked()) as *mut Table) };
|
||||
let l1_table = &mut *(mem::virtualize(l0_entry.address_unchecked()) as *mut Table);
|
||||
|
||||
for l1i in 0..512 {
|
||||
let l1_entry = l1_table[l1i];
|
||||
@@ -297,8 +402,7 @@ impl Space {
|
||||
continue;
|
||||
}
|
||||
assert!(l1_entry.is_table());
|
||||
let l2_table =
|
||||
unsafe { &mut *(mem::virtualize(l1_entry.address_unchecked()) as *mut Table) };
|
||||
let l2_table = &mut *(mem::virtualize(l1_entry.address_unchecked()) as *mut Table);
|
||||
|
||||
for l2i in 0..512 {
|
||||
let entry = l2_table[l2i];
|
||||
@@ -307,20 +411,17 @@ impl Space {
|
||||
}
|
||||
|
||||
assert!(entry.is_table());
|
||||
unsafe {
|
||||
phys::free_page(unsafe { entry.address_unchecked() });
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
phys::free_page(unsafe { l1_entry.address_unchecked() });
|
||||
phys::free_page(entry.address_unchecked()).unwrap();
|
||||
}
|
||||
phys::free_page(l1_entry.address_unchecked()).unwrap();
|
||||
}
|
||||
unsafe {
|
||||
phys::free_page(unsafe { l0_entry.address_unchecked() });
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
mem::memset(space as *mut Space as *mut u8, 0, 4096);
|
||||
phys::free_page(l0_entry.address_unchecked()).unwrap();
|
||||
}
|
||||
memset(space as *mut Space as *mut u8, 0, 4096);
|
||||
}
|
||||
|
||||
/// Returns the physical address of this structure
|
||||
pub fn address_phys(&mut self) -> usize {
|
||||
(self as *mut _ as usize) - mem::KERNEL_OFFSET
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ use crate::mem::{
|
||||
virt::{MapAttributes, Space},
|
||||
};
|
||||
use core::mem::{size_of, MaybeUninit};
|
||||
use error::Errno;
|
||||
use libcommon::{Read, Seek, SeekDir};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
traits::{Read, Seek, SeekDir}
|
||||
};
|
||||
use vfs::FileRef;
|
||||
|
||||
trait Elf {
|
||||
|
||||
+80
-10
@@ -1,12 +1,13 @@
|
||||
//! Process file descriptors and I/O context
|
||||
use alloc::collections::BTreeMap;
|
||||
use error::Errno;
|
||||
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<usize, FileRef>,
|
||||
files: BTreeMap<u32, FileRef>,
|
||||
ctty: Option<VnodeRef>,
|
||||
}
|
||||
|
||||
impl ProcessIo {
|
||||
@@ -21,9 +22,76 @@ 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, idx: usize) -> Result<FileRef, Errno> {
|
||||
self.files.get(&idx).cloned().ok_or(Errno::InvalidFile)
|
||||
pub fn file(&mut self, fd: FileDescriptor) -> Result<FileRef, Errno> {
|
||||
self.files.get(&u32::from(fd)).cloned().ok_or(Errno::InvalidFile)
|
||||
}
|
||||
|
||||
/// Returns [Ioctx] structure reference of this I/O context
|
||||
@@ -32,19 +100,19 @@ impl ProcessIo {
|
||||
}
|
||||
|
||||
/// Allocates a file descriptor and associates a [File] struct with it
|
||||
pub fn place_file(&mut self, file: FileRef) -> Result<usize, Errno> {
|
||||
pub fn place_file(&mut self, file: FileRef) -> Result<FileDescriptor, Errno> {
|
||||
for idx in 0..64 {
|
||||
if self.files.get(&idx).is_none() {
|
||||
self.files.insert(idx, file);
|
||||
return Ok(idx);
|
||||
return Ok(FileDescriptor::from(idx));
|
||||
}
|
||||
}
|
||||
Err(Errno::TooManyDescriptors)
|
||||
}
|
||||
|
||||
/// Performs [File] close and releases its associated file descriptor `idx`
|
||||
pub fn close_file(&mut self, idx: usize) -> Result<(), Errno> {
|
||||
let res = self.files.remove(&idx);
|
||||
pub fn close_file(&mut self, idx: FileDescriptor) -> Result<(), Errno> {
|
||||
let res = self.files.remove(&u32::from(idx));
|
||||
assert!(res.is_some());
|
||||
Ok(())
|
||||
}
|
||||
@@ -54,12 +122,14 @@ impl ProcessIo {
|
||||
Self {
|
||||
files: BTreeMap::new(),
|
||||
ioctx: None,
|
||||
ctty: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Assigns a descriptor number to an open file. If the number is not available,
|
||||
/// returns [Errno::AlreadyExists].
|
||||
pub fn set_file(&mut self, idx: usize, file: FileRef) -> Result<(), Errno> {
|
||||
pub fn set_file(&mut self, idx: FileDescriptor, file: FileRef) -> Result<(), Errno> {
|
||||
let idx = u32::from(idx);
|
||||
if self.files.get(&idx).is_none() {
|
||||
self.files.insert(idx, file);
|
||||
Ok(())
|
||||
|
||||
+24
-30
@@ -9,10 +9,14 @@ use alloc::{
|
||||
};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use crate::arch::platform::cpu::{self, Cpu};
|
||||
use libsys::proc::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::{Pid, Process, ProcessRef, State as ProcessState};
|
||||
pub use process::{Process, ProcessRef, ProcessState};
|
||||
pub mod io;
|
||||
pub use io::ProcessIo;
|
||||
|
||||
@@ -22,6 +26,7 @@ pub mod sched;
|
||||
pub use sched::Scheduler;
|
||||
//pub(self) use sched::SCHED;
|
||||
|
||||
//<<<<<<< HEAD
|
||||
// <<<<<<< HEAD
|
||||
// macro_rules! spawn {
|
||||
// (fn ($dst_arg:ident : usize) $body:block, $src_arg:expr) => {{
|
||||
@@ -49,10 +54,10 @@ pub use sched::Scheduler;
|
||||
// SCHED.switch(false);
|
||||
//}
|
||||
|
||||
///
|
||||
pub fn process(id: Pid) -> ProcessRef {
|
||||
PROCESSES.lock().get(&id).unwrap().clone()
|
||||
}
|
||||
// ///
|
||||
// pub fn process(id: Pid) -> ProcessRef {
|
||||
// PROCESSES.lock().get(&id).unwrap().clone()
|
||||
// }
|
||||
|
||||
macro_rules! spawn {
|
||||
(fn ($dst_arg:ident : usize) $body:block, $src_arg:expr) => {{
|
||||
@@ -74,17 +79,22 @@ macro_rules! spawn {
|
||||
(fn () $body:block) => (spawn!(fn (_arg: usize) $body, 0usize))
|
||||
}
|
||||
|
||||
/// Global list of all processes in the system
|
||||
// /// Global list of all processes in the system
|
||||
// // =======
|
||||
// /// Performs a task switch.
|
||||
// ///
|
||||
// /// See [Scheduler::switch]
|
||||
// pub fn switch() {
|
||||
// SCHED.switch(false);
|
||||
// }
|
||||
|
||||
// >>>>>>> feat/thread
|
||||
pub(self) static PROCESSES: IrqSafeSpinLock<BTreeMap<Pid, ProcessRef>> =
|
||||
IrqSafeSpinLock::new(BTreeMap::new());
|
||||
|
||||
/// Sets up initial process and enters it.
|
||||
///
|
||||
/// See [Scheduler::enter]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: May only be called once.
|
||||
pub(self) static THREADS: IrqSafeSpinLock<BTreeMap<u32, ThreadRef>> =
|
||||
IrqSafeSpinLock::new(BTreeMap::new());
|
||||
|
||||
pub unsafe fn enter(is_bsp: bool) -> ! {
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
@@ -97,24 +107,8 @@ pub unsafe fn enter(is_bsp: bool) -> ! {
|
||||
}
|
||||
|
||||
if is_bsp {
|
||||
sched::enqueue(Process::new_kernel(init::init_fn, 0).unwrap().id());
|
||||
} else {
|
||||
spawn!(fn () {
|
||||
loop {}
|
||||
});
|
||||
Process::new_kernel(init::init_fn, 0).unwrap().enqueue();
|
||||
}
|
||||
// if let Some((start, end)) = initrd {
|
||||
// let initrd = Box::into_raw(Box::new((mem::virtualize(start), mem::virtualize(end))));
|
||||
|
||||
// spawn!(fn (initrd_ptr: usize) {
|
||||
// loop {}
|
||||
// // debugln!("Running kernel init process");
|
||||
|
||||
// // let (start, _end) = unsafe { *(initrd_ptr as *const (usize, usize)) };
|
||||
// // Process::execve(|space| elf::load_elf(space, start as *const u8), 0).unwrap();
|
||||
// // panic!("This code should not run");
|
||||
// }, initrd as usize);
|
||||
// }
|
||||
|
||||
sched.enter();
|
||||
}
|
||||
|
||||
+497
-293
@@ -5,340 +5,410 @@ use crate::mem::{
|
||||
phys::{self, PageUsage},
|
||||
virt::{MapAttributes, Space},
|
||||
};
|
||||
use crate::proc::{wait::Wait, ProcessIo, PROCESSES, sched};
|
||||
use crate::proc::{
|
||||
wait::Wait, Context, ProcessIo, Thread, ThreadRef, ThreadState, PROCESSES, sched,
|
||||
};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::fmt;
|
||||
use alloc::{rc::Rc, vec::Vec};
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use error::Errno;
|
||||
|
||||
pub use crate::arch::platform::context::{self, Context};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
mem::memcpy,
|
||||
proc::{ExitCode, Pid},
|
||||
signal::Signal,
|
||||
ProgramArgs,
|
||||
};
|
||||
|
||||
/// Wrapper type for a process struct reference
|
||||
pub type ProcessRef = Rc<Process>;
|
||||
|
||||
/// Wrapper type for process exit code
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct ExitCode(i32);
|
||||
|
||||
/// Wrapper type for process ID
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Pid(u32);
|
||||
|
||||
/// 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>,
|
||||
threads: Vec<u32>,
|
||||
}
|
||||
|
||||
/// Structure describing an operating system process
|
||||
#[allow(dead_code)]
|
||||
pub struct Process {
|
||||
ctx: UnsafeCell<Context>,
|
||||
inner: IrqSafeSpinLock<ProcessInner>,
|
||||
exit_wait: Wait,
|
||||
signal_state: AtomicU32,
|
||||
/// Process I/O context
|
||||
pub io: IrqSafeSpinLock<ProcessIo>,
|
||||
cpu: AtomicU32,
|
||||
}
|
||||
|
||||
impl From<i32> for ExitCode {
|
||||
fn from(f: i32) -> Self {
|
||||
Self(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for ExitCode {
|
||||
fn from(_: ()) -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExitCode> for i32 {
|
||||
fn from(f: ExitCode) -> Self {
|
||||
f.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Pid {
|
||||
/// Kernel idle process always has PID of zero
|
||||
pub const IDLE: Self = Self(Self::KERNEL_BIT);
|
||||
|
||||
const KERNEL_BIT: u32 = 1 << 31;
|
||||
|
||||
/// Constructs an instance of user-space PID
|
||||
pub const fn user(id: u32) -> Self {
|
||||
assert!(id < 256, "PID is too high");
|
||||
Self(id)
|
||||
}
|
||||
|
||||
/// Allocates a new kernel-space PID
|
||||
pub fn new_kernel() -> Self {
|
||||
static LAST: AtomicU32 = AtomicU32::new(0);
|
||||
let id = LAST.fetch_add(1, Ordering::Relaxed);
|
||||
assert!(id & Self::KERNEL_BIT == 0, "Out of kernel PIDs");
|
||||
Self(id | Self::KERNEL_BIT)
|
||||
}
|
||||
|
||||
/// Allocates a new user-space PID.
|
||||
///
|
||||
/// First user PID is #1.
|
||||
pub fn new_user() -> Self {
|
||||
static LAST: AtomicU32 = AtomicU32::new(1);
|
||||
let id = LAST.fetch_add(1, Ordering::Relaxed);
|
||||
assert!(id < 256, "Out of user PIDs");
|
||||
Self(id)
|
||||
}
|
||||
|
||||
/// Returns `true` if this PID belongs to a kernel process
|
||||
pub fn is_kernel(self) -> bool {
|
||||
self.0 & Self::KERNEL_BIT != 0
|
||||
}
|
||||
|
||||
/// Returns address space ID of a user-space process.
|
||||
///
|
||||
/// Panics if called on kernel process PID.
|
||||
pub fn asid(self) -> u8 {
|
||||
assert!(!self.is_kernel());
|
||||
self.0 as u8
|
||||
}
|
||||
|
||||
/// Returns bit value of this pid
|
||||
pub const fn value(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub const unsafe fn from_raw(num: u32) -> Self {
|
||||
Self(num)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Pid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Pid(#{}{})",
|
||||
if self.is_kernel() { "K" } else { "U" },
|
||||
self.0 & !Self::KERNEL_BIT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Process {
|
||||
const USTACK_VIRT_TOP: usize = 0x100000000;
|
||||
const USTACK_PAGES: usize = 4;
|
||||
const CPU_NONE: u32 = u32::MAX;
|
||||
|
||||
/// Returns currently executing process
|
||||
pub fn current() -> ProcessRef {
|
||||
sched::current_process()
|
||||
}
|
||||
|
||||
pub fn get(pid: Pid) -> Option<ProcessRef> {
|
||||
PROCESSES.lock().get(&pid).cloned()
|
||||
}
|
||||
|
||||
/// Schedules an initial thread for execution
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: only allowed to be called once, repeated calls
|
||||
/// will generate undefined behavior
|
||||
pub unsafe fn enter(cpu: u32, proc: ProcessRef) -> ! {
|
||||
// FIXME use some global lock to guarantee atomicity of thread entry?
|
||||
proc.inner.lock().state = State::Running;
|
||||
proc.cpu.store(cpu, Ordering::SeqCst);
|
||||
let ctx = proc.ctx.get();
|
||||
|
||||
// I don't think this is bad: process can't be dropped fully unless
|
||||
// it's been reaped (and this function won't run for such process)
|
||||
// drop(proc);
|
||||
(&mut *ctx).enter()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn manipulate_space<F: FnOnce(&mut Space) -> Result<(), Errno>>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> Result<(), Errno> {
|
||||
f(self.inner.lock().space.as_mut().unwrap())
|
||||
}
|
||||
|
||||
/// 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(cpu: u32, 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;
|
||||
|
||||
src.cpu.store(Self::CPU_NONE, Ordering::SeqCst);
|
||||
dst.cpu.store(cpu, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
let src_ctx = src.ctx.get();
|
||||
let dst_ctx = dst.ctx.get();
|
||||
|
||||
// See "drop" note in Process::enter()
|
||||
// drop(src);
|
||||
// drop(dst);
|
||||
|
||||
(&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);
|
||||
// SCHED.dequeue(lock.id);
|
||||
drop
|
||||
};
|
||||
if drop {
|
||||
sched::switch(true);
|
||||
// todo!();
|
||||
// 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
|
||||
#[inline]
|
||||
pub fn id(&self) -> Pid {
|
||||
self.inner.lock().id
|
||||
}
|
||||
|
||||
///
|
||||
pub fn cpu(&self) -> u32 {
|
||||
self.cpu.load(Ordering::SeqCst)
|
||||
/// 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 {
|
||||
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 = Pid::new_kernel();
|
||||
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 {
|
||||
ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)),
|
||||
exit_wait: Wait::new("process_exit"),
|
||||
io: IrqSafeSpinLock::new(ProcessIo::new()),
|
||||
exit_wait: Wait::new(),
|
||||
inner: IrqSafeSpinLock::new(ProcessInner {
|
||||
id,
|
||||
exit: None,
|
||||
space: None,
|
||||
wait_flag: false,
|
||||
state: State::Ready,
|
||||
}),
|
||||
cpu: AtomicU32::new(Self::CPU_NONE),
|
||||
signal_state: AtomicU32::new(0),
|
||||
inner: IrqSafeSpinLock::new(inner),
|
||||
});
|
||||
debugln!("New kernel process: {}", id);
|
||||
assert!(PROCESSES.lock().insert(id, res.clone()).is_none());
|
||||
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
|
||||
pub fn get(pid: Pid) -> Option<ProcessRef> {
|
||||
PROCESSES.lock().get(&pid).cloned()
|
||||
}
|
||||
|
||||
// <<<<<<< HEAD
|
||||
// /// Schedules an initial thread for execution
|
||||
// ///
|
||||
// /// # Safety
|
||||
// ///
|
||||
// /// Unsafe: only allowed to be called once, repeated calls
|
||||
// /// will generate undefined behavior
|
||||
// pub unsafe fn enter(cpu: u32, proc: ProcessRef) -> ! {
|
||||
// // FIXME use some global lock to guarantee atomicity of thread entry?
|
||||
// proc.inner.lock().state = State::Running;
|
||||
// proc.cpu.store(cpu, Ordering::SeqCst);
|
||||
// let ctx = proc.ctx.get();
|
||||
|
||||
// // I don't think this is bad: process can't be dropped fully unless
|
||||
// // it's been reaped (and this function won't run for such process)
|
||||
// // drop(proc);
|
||||
// (&mut *ctx).enter()
|
||||
// }
|
||||
// =======
|
||||
/// Sets a pending signal for a process
|
||||
pub fn set_signal(&self, signal: Signal) {
|
||||
todo!();
|
||||
// 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);
|
||||
|
||||
// // 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);
|
||||
// }
|
||||
// ThreadState::Waiting => {
|
||||
// main_thread.clone().setup_signal(signal, ttbr0);
|
||||
// main_thread.interrupt_wait(true);
|
||||
// }
|
||||
// ThreadState::Ready => {
|
||||
// main_thread.clone().setup_signal(signal, ttbr0);
|
||||
// main_thread.interrupt_wait(false);
|
||||
// }
|
||||
// ThreadState::Finished => {
|
||||
// // TODO report error back
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/// Immediately delivers a signal to requested thread
|
||||
pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
|
||||
todo!();
|
||||
// let mut lock = self.inner.lock();
|
||||
// let ttbr0 = lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
|
||||
// thread.enter_signal(signal, ttbr0);
|
||||
}
|
||||
|
||||
// /// 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(cpu: u32, 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;
|
||||
|
||||
// src.cpu.store(Self::CPU_NONE, Ordering::SeqCst);
|
||||
// dst.cpu.store(cpu, Ordering::SeqCst);
|
||||
// }
|
||||
|
||||
// let src_ctx = src.ctx.get();
|
||||
// let dst_ctx = dst.ctx.get();
|
||||
|
||||
// // See "drop" note in Process::enter()
|
||||
// // drop(src);
|
||||
// // drop(dst);
|
||||
|
||||
// (&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);
|
||||
// // SCHED.dequeue(lock.id);
|
||||
// drop
|
||||
// };
|
||||
// if drop {
|
||||
// sched::switch(true);
|
||||
// // todo!();
|
||||
// // SCHED.switch(true);
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Crates a new thread in the process
|
||||
pub fn new_user_thread(&self, entry: usize, stack: usize, arg: usize) -> Result<u32, Errno> {
|
||||
let mut lock = self.inner.lock();
|
||||
|
||||
let space_phys = lock.space.as_mut().unwrap().address_phys();
|
||||
let ttbr0 = space_phys | ((lock.id.asid() as usize) << 48);
|
||||
|
||||
let thread = Thread::new_user(lock.id, entry, stack, arg, ttbr0)?;
|
||||
let tid = thread.id();
|
||||
lock.threads.push(tid);
|
||||
sched::enqueue(tid);
|
||||
|
||||
Ok(tid)
|
||||
}
|
||||
|
||||
// /// Creates a new kernel process
|
||||
// pub fn new_kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<ProcessRef, Errno> {
|
||||
// let id = Pid::new_kernel();
|
||||
// let res = Rc::new(Self {
|
||||
// ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)),
|
||||
// io: IrqSafeSpinLock::new(ProcessIo::new()),
|
||||
// exit_wait: Wait::new(),
|
||||
// inner: IrqSafeSpinLock::new(ProcessInner {
|
||||
// id,
|
||||
// exit: None,
|
||||
// space: None,
|
||||
// wait_flag: false,
|
||||
// state: State::Ready,
|
||||
// }),
|
||||
// cpu: AtomicU32::new(Self::CPU_NONE),
|
||||
// });
|
||||
// debugln!("New kernel process: {}", id);
|
||||
// assert!(PROCESSES.lock().insert(id, res.clone()).is_none());
|
||||
// Ok(res)
|
||||
// }
|
||||
|
||||
/// Creates a "fork" of the process, cloning its address space and
|
||||
/// resources
|
||||
pub fn fork(&self, frame: &mut ExceptionFrame) -> Result<Pid, Errno> {
|
||||
let src_io = self.io.lock();
|
||||
let mut src_inner = self.inner.lock();
|
||||
|
||||
let dst_id = Pid::new_user();
|
||||
let dst_id = new_user_pid();
|
||||
let dst_space = src_inner.space.as_mut().unwrap().fork()?;
|
||||
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)),
|
||||
exit_wait: Wait::new("process_exit"),
|
||||
io: IrqSafeSpinLock::new(src_io.fork()?),
|
||||
exit_wait: Wait::new(),
|
||||
signal_state: AtomicU32::new(0),
|
||||
inner: IrqSafeSpinLock::new(ProcessInner {
|
||||
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,
|
||||
}),
|
||||
cpu: AtomicU32::new(Self::CPU_NONE)
|
||||
});
|
||||
debugln!("Process {} forked into {}", src_inner.id, dst_id);
|
||||
|
||||
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);
|
||||
// SCHED.enqueue(dst_id);
|
||||
|
||||
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() {
|
||||
Thread::get(tid).unwrap().terminate(status);
|
||||
sched::dequeue(tid);
|
||||
// SCHED.dequeue(tid);
|
||||
}
|
||||
|
||||
if let Some(space) = lock.space.take() {
|
||||
unsafe {
|
||||
Space::release(space);
|
||||
asm!("tlbi aside1, {}", in(reg) ((lock.id.asid() as usize) << 48));
|
||||
}
|
||||
}
|
||||
|
||||
self.io.lock().handle_exit();
|
||||
// TODO when exiting from signal handler interrupting an IO operation
|
||||
// deadlock is achieved
|
||||
self.io.lock().handle_exit();
|
||||
|
||||
drop(lock);
|
||||
|
||||
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);
|
||||
todo!();
|
||||
// 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);
|
||||
todo!();
|
||||
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
|
||||
@@ -357,7 +427,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);
|
||||
}
|
||||
|
||||
@@ -365,43 +434,160 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads a new program into current process address space
|
||||
pub fn execve<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
|
||||
loader: F,
|
||||
arg: usize,
|
||||
) -> Result<(), Errno> {
|
||||
unsafe {
|
||||
// Run with interrupts disabled
|
||||
asm!("msr daifset, #2");
|
||||
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 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 = Pid::new_user();
|
||||
debugln!(
|
||||
"Process downgrades from kernel to user: {} -> {}",
|
||||
old_pid,
|
||||
lock.id
|
||||
);
|
||||
assert!(proc_lock.insert(lock.id, proc.clone()).is_none());
|
||||
unsafe {
|
||||
use crate::arch::platform::cpu::Cpu;
|
||||
Cpu::get().scheduler().hack_current_pid(lock.id);
|
||||
}
|
||||
let page_virt = dst & !4095;
|
||||
let page_phys = if let Ok(phys) = space.translate(dst) {
|
||||
phys
|
||||
} else {
|
||||
// Invalidate user ASID
|
||||
let input = (lock.id.asid() as usize) << 48;
|
||||
unsafe {
|
||||
asm!("tlbi aside1, {}", in(reg) input);
|
||||
}
|
||||
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 + 0, 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)
|
||||
}
|
||||
|
||||
/// Loads a new program into current process address space
|
||||
pub fn execve<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
|
||||
loader: F,
|
||||
argv: &[&str],
|
||||
) -> Result<(), Errno> {
|
||||
unsafe {
|
||||
// Run with interrupts disabled
|
||||
asm!("msr daifset, #2");
|
||||
}
|
||||
|
||||
// <<<<<<< HEAD
|
||||
// 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 = Pid::new_user();
|
||||
// debugln!(
|
||||
// "Process downgrades from kernel to user: {} -> {}",
|
||||
// old_pid,
|
||||
// lock.id
|
||||
// );
|
||||
// assert!(proc_lock.insert(lock.id, proc.clone()).is_none());
|
||||
// unsafe {
|
||||
// use crate::arch::platform::cpu::Cpu;
|
||||
// Cpu::get().scheduler().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!();
|
||||
// >>>>>>> feat/thread
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -422,25 +608,26 @@ 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;
|
||||
asm!("tlbi aside1, {}", in(reg) 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();
|
||||
}
|
||||
@@ -449,6 +636,23 @@ impl Process {
|
||||
|
||||
impl Drop for Process {
|
||||
fn drop(&mut self) {
|
||||
debugln!("Dropping process {}", self.id());
|
||||
debugln!("Dropping process {:?}", self.id());
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a new kernel-space PID
|
||||
pub fn new_kernel_pid() -> Pid {
|
||||
static LAST: AtomicU32 = AtomicU32::new(0);
|
||||
let id = LAST.fetch_add(1, Ordering::Relaxed);
|
||||
Pid::kernel(id)
|
||||
}
|
||||
|
||||
/// Allocates a new user-space PID.
|
||||
///
|
||||
/// First user PID is #1.
|
||||
pub fn new_user_pid() -> Pid {
|
||||
static LAST: AtomicU32 = AtomicU32::new(1);
|
||||
let id = LAST.fetch_add(1, Ordering::Relaxed);
|
||||
assert!(id < 256, "Out of user PIDs");
|
||||
Pid::user(id)
|
||||
}
|
||||
|
||||
+69
-39
@@ -1,5 +1,5 @@
|
||||
//!
|
||||
use crate::proc::{Pid, Process, ProcessRef, PROCESSES};
|
||||
use crate::proc::{Thread, ThreadRef, THREADS};
|
||||
use crate::util::InitOnce;
|
||||
use alloc::{collections::VecDeque, rc::Rc};
|
||||
use crate::sync::{IrqSafeSpinLock, IrqSafeSpinLockGuard};
|
||||
@@ -9,9 +9,9 @@ use core::ops::Deref;
|
||||
use tock_registers::interfaces::Readable;
|
||||
|
||||
struct SchedulerInner {
|
||||
queue: VecDeque<Pid>,
|
||||
idle: Option<Pid>,
|
||||
current: Option<Pid>,
|
||||
queue: VecDeque<u32>,
|
||||
idle: Option<u32>,
|
||||
current: Option<u32>,
|
||||
}
|
||||
|
||||
/// Process scheduler state and queues
|
||||
@@ -27,7 +27,7 @@ impl SchedulerInner {
|
||||
current: None,
|
||||
};
|
||||
|
||||
this.idle = Some(Process::new_kernel(idle_fn, 0).unwrap().id());
|
||||
this.idle = Some(Thread::new_kernel(None, idle_fn, 0).unwrap().id());
|
||||
|
||||
this
|
||||
}
|
||||
@@ -61,13 +61,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: u32) {
|
||||
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: u32) {
|
||||
self.inner.get().lock().queue.retain(|&p| p != tid)
|
||||
}
|
||||
|
||||
/// Performs initial process entry.
|
||||
@@ -85,10 +85,11 @@ impl Scheduler {
|
||||
};
|
||||
|
||||
inner.current = Some(id);
|
||||
PROCESSES.lock().get(&id).unwrap().clone()
|
||||
THREADS.lock().get(&id).unwrap().clone()
|
||||
};
|
||||
|
||||
Process::enter((MPIDR_EL1.get() & 0xF) as u32, thread)
|
||||
asm!("msr daifset, #2");
|
||||
Thread::enter((MPIDR_EL1.get() & 0xF) as u32, thread)
|
||||
}
|
||||
|
||||
/// This hack is required to be called from execve() when downgrading current
|
||||
@@ -97,8 +98,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: u32, new: u32) {
|
||||
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
|
||||
@@ -108,7 +115,11 @@ impl Scheduler {
|
||||
let mut inner = self.inner.get().lock();
|
||||
let current = inner.current.unwrap();
|
||||
|
||||
//<<<<<<< HEAD
|
||||
if !discard && current != inner.idle.unwrap() {
|
||||
//=======
|
||||
// if !discard && current != 0 {
|
||||
//>>>>>>> feat/thread
|
||||
// Put the process into the back of the queue
|
||||
if !enqueue_somewhere_else((MPIDR_EL1.get() & 0xF) as usize, current, &sched_lock) {
|
||||
inner.queue.push_back(current);
|
||||
@@ -123,7 +134,7 @@ impl Scheduler {
|
||||
|
||||
inner.current = Some(next);
|
||||
let (from, to) = {
|
||||
let lock = PROCESSES.lock();
|
||||
let lock = THREADS.lock();
|
||||
(
|
||||
lock.get(¤t).unwrap().clone(),
|
||||
lock.get(&next).unwrap().clone(),
|
||||
@@ -135,23 +146,42 @@ impl Scheduler {
|
||||
|
||||
if !Rc::ptr_eq(&from, &to) {
|
||||
unsafe {
|
||||
//<<<<<<< HEAD
|
||||
drop(sched_lock);
|
||||
Process::switch((MPIDR_EL1.get() & 0xF) as u32, from, to, discard);
|
||||
// Process::switch((MPIDR_EL1.get() & 0xF) as u32, from, to, discard);
|
||||
//=======
|
||||
asm!("msr daifset, #2");
|
||||
Thread::switch((MPIDR_EL1.get() & 0xF) as u32, from, to, discard);
|
||||
//>>>>>>> feat/thread
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(¤t).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(¤t).unwrap().clone()
|
||||
// }
|
||||
}
|
||||
|
||||
// <<<<<<< HEAD
|
||||
// // pub fn is_ready() -> bool {
|
||||
// // SCHED.inner.is_initialized()
|
||||
// // }
|
||||
// =======
|
||||
// /// Returns `true` if the scheduler has been initialized
|
||||
// pub fn is_ready() -> bool {
|
||||
// SCHED.inner.is_initialized()
|
||||
// }
|
||||
// >>>>>>> feat/thread
|
||||
|
||||
#[inline(never)]
|
||||
extern "C" fn idle_fn(_a: usize) -> ! {
|
||||
@@ -160,6 +190,11 @@ extern "C" fn idle_fn(_a: usize) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_thread() -> ThreadRef {
|
||||
let guard = SCHED_LOCK.lock();
|
||||
unsafe { Cpu::get().scheduler().current_thread() }
|
||||
}
|
||||
|
||||
/// Performs a task switch.
|
||||
///
|
||||
/// See [Scheduler::switch]
|
||||
@@ -170,16 +205,17 @@ pub fn switch(discard: bool) {
|
||||
}
|
||||
|
||||
///
|
||||
pub fn enqueue_to(cpu: usize, pid: Pid) {
|
||||
let _lock = SCHED_LOCK.lock();
|
||||
debugln!("Queue {} to cpu{}", pid, cpu);
|
||||
unsafe {
|
||||
cpu::by_index(cpu).scheduler().enqueue(pid)
|
||||
}
|
||||
pub fn enqueue_to(cpu: usize, tid: u32) {
|
||||
todo!()
|
||||
//let _lock = SCHED_LOCK.lock();
|
||||
//debugln!("Queue {} to cpu{}", pid, cpu);
|
||||
//unsafe {
|
||||
// cpu::by_index(cpu).scheduler().enqueue(pid)
|
||||
//}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn enqueue(pid: Pid) {
|
||||
pub fn enqueue(tid: u32) {
|
||||
let _lock = SCHED_LOCK.lock();
|
||||
let mut min_idx = 0;
|
||||
let mut min_cnt = usize::MAX;
|
||||
@@ -193,12 +229,12 @@ pub fn enqueue(pid: Pid) {
|
||||
|
||||
// debugln!("Queue {} to cpu{}", pid, min_idx);
|
||||
unsafe {
|
||||
cpu::by_index(min_idx).scheduler().enqueue(pid)
|
||||
cpu::by_index(min_idx).scheduler().enqueue(tid)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn enqueue_somewhere_else(ignore: usize, pid: Pid, _guard: &IrqSafeSpinLockGuard<()>) -> bool {
|
||||
pub fn enqueue_somewhere_else(ignore: usize, tid: u32, _guard: &IrqSafeSpinLockGuard<()>) -> bool {
|
||||
let mut min_idx = 0;
|
||||
//let mut min_cnt = usize::MAX;
|
||||
static mut LAST: usize = 0;
|
||||
@@ -222,28 +258,22 @@ pub fn enqueue_somewhere_else(ignore: usize, pid: Pid, _guard: &IrqSafeSpinLockG
|
||||
false
|
||||
} else {
|
||||
unsafe {
|
||||
cpu::by_index(min_idx).scheduler().enqueue(pid)
|
||||
cpu::by_index(min_idx).scheduler().enqueue(tid)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn dequeue(pid: Pid) {
|
||||
pub fn dequeue(tid: u32) {
|
||||
// TODO process can be rescheduled to other CPU between scheduler locks
|
||||
let lock = SCHED_LOCK.lock();
|
||||
let cpu_id = PROCESSES.lock().get(&pid).unwrap().cpu();
|
||||
let cpu_id = Thread::get(tid).unwrap().cpu();
|
||||
unsafe {
|
||||
cpu::by_index(cpu_id as usize).scheduler().dequeue(pid);
|
||||
cpu::by_index(cpu_id as usize).scheduler().dequeue(tid);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn current_process() -> ProcessRef {
|
||||
let _lock = SCHED_LOCK.lock();
|
||||
unsafe { Cpu::get().scheduler().current_process() }
|
||||
}
|
||||
|
||||
static SCHED_LOCK: IrqSafeSpinLock<()> = IrqSafeSpinLock::new(());
|
||||
|
||||
// TODO maybe move this into a per-CPU struct
|
||||
|
||||
@@ -0,0 +1,423 @@
|
||||
//! 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},
|
||||
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: u32,
|
||||
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,
|
||||
cpu: AtomicU32,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
const CPU_NONE: u32 = u32::MAX;
|
||||
|
||||
/// Returns currently active thread [Rc]-reference
|
||||
#[inline]
|
||||
pub fn current() -> ThreadRef {
|
||||
sched::current_thread()
|
||||
}
|
||||
|
||||
/// Returns a reference to thread `tid`, if it exists
|
||||
#[inline]
|
||||
pub fn get(tid: u32) -> 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 {
|
||||
cpu: AtomicU32::new(Self::CPU_NONE),
|
||||
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 {
|
||||
cpu: AtomicU32::new(Self::CPU_NONE),
|
||||
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 {
|
||||
cpu: AtomicU32::new(Self::CPU_NONE),
|
||||
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) -> u32 {
|
||||
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(cpu: u32, thread: ThreadRef) -> ! {
|
||||
// FIXME use some global lock to guarantee atomicity of thread entry?
|
||||
thread.inner.lock().state = State::Running;
|
||||
thread.cpu.store(cpu, Ordering::SeqCst);
|
||||
thread.current_context().enter()
|
||||
}
|
||||
|
||||
///
|
||||
pub fn cpu(&self) -> u32 {
|
||||
self.cpu.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// 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(cpu: u32, 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;
|
||||
|
||||
src.cpu.store(Self::CPU_NONE, Ordering::SeqCst);
|
||||
dst.cpu.store(cpu, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
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);
|
||||
// SCHED.dequeue(lock.id);
|
||||
drop
|
||||
};
|
||||
if drop {
|
||||
sched::switch(true);
|
||||
// SCHED.switch(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Changes process wait condition status
|
||||
pub fn setup_wait(&self, wait: *const Wait) {
|
||||
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: u32) -> 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() -> u32 {
|
||||
static LAST: AtomicU32 = AtomicU32::new(1);
|
||||
let id = LAST.fetch_add(1, Ordering::Relaxed);
|
||||
assert!(id < 256, "Out of user TIDs");
|
||||
id
|
||||
}
|
||||
+131
-22
@@ -2,24 +2,40 @@
|
||||
|
||||
use crate::arch::machine;
|
||||
use crate::dev::timer::TimestampSource;
|
||||
use crate::proc::{self, sched, Pid, Process};
|
||||
use crate::proc::{self, sched, Thread, ThreadRef};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use alloc::collections::LinkedList;
|
||||
use core::time::Duration;
|
||||
use error::Errno;
|
||||
use libsys::{error::Errno, 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<u32>>,
|
||||
#[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: u32,
|
||||
deadline: Duration,
|
||||
}
|
||||
|
||||
static TICK_LIST: IrqSafeSpinLock<LinkedList<Timeout>> = IrqSafeSpinLock::new(LinkedList::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() {
|
||||
@@ -29,9 +45,14 @@ 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);
|
||||
todo!();
|
||||
//<<<<<<< HEAD
|
||||
// sched::enqueue(pid);
|
||||
//=======
|
||||
// SCHED.enqueue(tid);
|
||||
//>>>>>>> feat/thread
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
@@ -41,7 +62,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) => {
|
||||
@@ -54,11 +75,92 @@ pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Suspends current process until some file descriptor
|
||||
/// signals data available
|
||||
pub fn select(
|
||||
thread: ThreadRef,
|
||||
mut rfds: Option<&mut FdSet>,
|
||||
mut wfds: Option<&mut FdSet>,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<usize, Errno> {
|
||||
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);
|
||||
|
||||
let deadline = timeout.map(|v| v + machine::local_timer().timestamp().unwrap());
|
||||
let proc = thread.owner().unwrap();
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
loop {
|
||||
if let Some(read) = &read {
|
||||
for fd in read.iter() {
|
||||
let file = io.file(fd)?;
|
||||
if file.borrow().is_ready(false)? {
|
||||
rfds.as_mut().unwrap().set(fd);
|
||||
return Ok(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(write) = &write {
|
||||
for fd in write.iter() {
|
||||
let file = io.file(fd)?;
|
||||
if file.borrow().is_ready(true)? {
|
||||
wfds.as_mut().unwrap().set(fd);
|
||||
return Ok(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Suspend
|
||||
match WAIT_SELECT.wait(deadline) {
|
||||
Err(Errno::TimedOut) => return Ok(0),
|
||||
Err(e) => return Err(e),
|
||||
Ok(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Wait {
|
||||
/// 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: u32, 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);
|
||||
// SCHED.enqueue(tid);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,12 +169,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 {
|
||||
@@ -81,8 +183,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;
|
||||
@@ -104,26 +206,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 {
|
||||
@@ -131,7 +240,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 {
|
||||
|
||||
+153
-74
@@ -1,65 +1,190 @@
|
||||
//! System call argument ABI helpers
|
||||
|
||||
use crate::mem;
|
||||
use core::mem::size_of;
|
||||
use error::Errno;
|
||||
use core::alloc::Layout;
|
||||
use libsys::error::Errno;
|
||||
use crate::proc::Process;
|
||||
|
||||
fn translate(virt: usize) -> Option<usize> {
|
||||
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
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwraps a slim structure pointer
|
||||
pub fn validate_user_ptr_struct<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
|
||||
let bytes = validate_user_ptr(base, size_of::<T>())?;
|
||||
#[inline(always)]
|
||||
fn is_el0_accessible(virt: usize, write: bool) -> bool {
|
||||
let mut res: usize;
|
||||
unsafe {
|
||||
if write {
|
||||
asm!("at s1e0w, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
|
||||
} else {
|
||||
asm!("at s1e0r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
|
||||
}
|
||||
}
|
||||
res & 1 == 0
|
||||
}
|
||||
|
||||
/// Checks given argument and interprets it as a `T` reference
|
||||
pub fn struct_ref<'a, T>(base: usize) -> Result<&'a T, Errno> {
|
||||
let layout = Layout::new::<T>();
|
||||
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) })
|
||||
}
|
||||
|
||||
/// 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) })
|
||||
}
|
||||
|
||||
/// 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 `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 {
|
||||
struct_ref(base).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
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)
|
||||
})
|
||||
} 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}",
|
||||
@@ -69,49 +194,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
|
||||
// })
|
||||
// }
|
||||
|
||||
+370
-93
@@ -1,21 +1,30 @@
|
||||
//! System call implementation
|
||||
|
||||
use crate::arch::platform::exception::ExceptionFrame;
|
||||
use crate::arch::{machine, platform::exception::ExceptionFrame};
|
||||
use crate::mem::{virt::MapAttributes, phys::PageUsage};
|
||||
use crate::debug::Level;
|
||||
use crate::proc::{elf, wait, Pid, Process, ProcessIo};
|
||||
use crate::dev::timer::TimestampSource;
|
||||
use crate::fs::create_filesystem;
|
||||
use crate::proc::{self, sched, elf, wait, Process, ProcessIo, Thread};
|
||||
use core::mem::size_of;
|
||||
use core::ops::DerefMut;
|
||||
use core::time::Duration;
|
||||
use error::Errno;
|
||||
use libcommon::{Read, Write};
|
||||
use syscall::{
|
||||
abi,
|
||||
stat::{AT_EMPTY_PATH, AT_FDCWD},
|
||||
use libsys::{
|
||||
abi::SystemCall,
|
||||
debug::TraceLevel,
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
proc::{ExitCode, Pid, MemoryAccess},
|
||||
signal::{Signal, SignalDestination},
|
||||
stat::{
|
||||
AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, GroupId, MountOptions,
|
||||
OpenFlags, Stat, UserId, AT_EMPTY_PATH,
|
||||
},
|
||||
traits::{Read, Write},
|
||||
};
|
||||
use vfs::{FileMode, OpenFlags, Stat, VnodeRef, IoctlCmd};
|
||||
use vfs::VnodeRef;
|
||||
|
||||
pub mod arg;
|
||||
pub use arg::*;
|
||||
|
||||
/// Creates a "fork" process from current one using its register frame.
|
||||
/// See [Process::fork()].
|
||||
@@ -30,11 +39,11 @@ pub unsafe fn sys_fork(regs: &mut ExceptionFrame) -> Result<Pid, Errno> {
|
||||
|
||||
fn find_at_node<T: DerefMut<Target = ProcessIo>>(
|
||||
io: &mut T,
|
||||
at_fd: usize,
|
||||
at_fd: Option<FileDescriptor>,
|
||||
filename: &str,
|
||||
empty_path: bool,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let at = if at_fd as i32 != AT_FDCWD {
|
||||
let at = if let Some(at_fd) = at_fd {
|
||||
io.file(at_fd)?.borrow().node()
|
||||
} else {
|
||||
None
|
||||
@@ -48,95 +57,66 @@ fn find_at_node<T: DerefMut<Target = ProcessIo>>(
|
||||
}
|
||||
|
||||
/// Main system call dispatcher function
|
||||
pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
||||
pub 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 => {
|
||||
let at_fd = args[0];
|
||||
let path = validate_user_str(args[1], args[2])?;
|
||||
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 = arg::str_ref(args[1], args[2])?;
|
||||
let mode = FileMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
|
||||
let opts = OpenFlags::from_bits(args[4] as u32).ok_or(Errno::InvalidArgument)?;
|
||||
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
let at = if at_fd as i32 == AT_FDCWD {
|
||||
None
|
||||
let at = if let Some(fd) = at_fd {
|
||||
io.file(fd)?.borrow().node()
|
||||
} else {
|
||||
io.file(at_fd)?.borrow().node()
|
||||
None
|
||||
};
|
||||
|
||||
let file = io.ioctx().open(at, path, mode, opts)?;
|
||||
io.place_file(file)
|
||||
Ok(u32::from(io.place_file(file)?) as usize)
|
||||
}
|
||||
abi::SYS_READ => {
|
||||
SystemCall::Close => {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
let buf = validate_user_ptr(args[1], args[2])?;
|
||||
|
||||
io.file(args[0])?.borrow_mut().read(buf)
|
||||
}
|
||||
abi::SYS_WRITE => {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
let buf = validate_user_ptr(args[1], args[2])?;
|
||||
|
||||
io.file(args[0])?.borrow_mut().write(buf)
|
||||
}
|
||||
abi::SYS_FSTATAT => {
|
||||
let at_fd = args[0];
|
||||
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 => {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
let fd = args[0];
|
||||
let fd = FileDescriptor::from(args[0] as u32);
|
||||
|
||||
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 => {
|
||||
let fd = args[0];
|
||||
SystemCall::Ioctl => {
|
||||
let fd = FileDescriptor::from(args[0] as u32);
|
||||
let cmd = IoctlCmd::try_from(args[1] as u32)?;
|
||||
|
||||
let proc = Process::current();
|
||||
@@ -144,20 +124,198 @@ 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])
|
||||
},
|
||||
|
||||
// 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])
|
||||
}
|
||||
abi::SYS_EX_NANOSLEEP => {
|
||||
let rem_buf = validate_user_ptr_null(args[1], size_of::<u64>() * 2)?;
|
||||
SystemCall::Select => {
|
||||
let rfds = arg::option_struct_mut::<FdSet>(args[0])?;
|
||||
let wfds = arg::option_struct_mut::<FdSet>(args[1])?;
|
||||
let timeout = if args[2] == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Duration::from_nanos(args[2] as u64))
|
||||
};
|
||||
|
||||
wait::select(Thread::current(), rfds, wfds, timeout)
|
||||
}
|
||||
SystemCall::Access => {
|
||||
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
|
||||
let path = arg::str_ref(args[1], args[2])?;
|
||||
let mode = AccessMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
|
||||
let flags = args[4] as u32;
|
||||
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
find_at_node(&mut io, at_fd, path, flags & AT_EMPTY_PATH != 0)?
|
||||
.check_access(io.ioctx(), mode)?;
|
||||
Ok(0)
|
||||
}
|
||||
SystemCall::ReadDirectory => {
|
||||
let proc = Process::current();
|
||||
let fd = FileDescriptor::from(args[0] as u32);
|
||||
let mut io = proc.io.lock();
|
||||
let buf = arg::struct_buf_mut::<DirectoryEntry>(args[1], args[2])?;
|
||||
|
||||
io.file(fd)?.borrow_mut().readdir(buf)
|
||||
}
|
||||
SystemCall::GetUserId => {
|
||||
let proc = Process::current();
|
||||
let uid = proc.io.lock().uid();
|
||||
Ok(u32::from(uid) as usize)
|
||||
}
|
||||
SystemCall::GetGroupId => {
|
||||
let proc = Process::current();
|
||||
let gid = proc.io.lock().gid();
|
||||
Ok(u32::from(gid) as usize)
|
||||
}
|
||||
SystemCall::DuplicateFd => {
|
||||
let src = FileDescriptor::from(args[0] as u32);
|
||||
let dst = FileDescriptor::from_i32(args[1] as i32)?;
|
||||
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
let res = io.duplicate_file(src, dst)?;
|
||||
|
||||
Ok(u32::from(res) as usize)
|
||||
}
|
||||
SystemCall::SetUserId => {
|
||||
let uid = UserId::from(args[0] as u32);
|
||||
let proc = Process::current();
|
||||
proc.io.lock().set_uid(uid)?;
|
||||
Ok(0)
|
||||
}
|
||||
SystemCall::SetGroupId => {
|
||||
let gid = GroupId::from(args[0] as u32);
|
||||
let proc = Process::current();
|
||||
proc.io.lock().set_gid(gid)?;
|
||||
Ok(0)
|
||||
}
|
||||
SystemCall::SetCurrentDirectory => {
|
||||
let path = arg::str_ref(args[0], args[1])?;
|
||||
let proc = Process::current();
|
||||
proc.io.lock().ioctx().chdir(path)?;
|
||||
Ok(0)
|
||||
}
|
||||
SystemCall::GetCurrentDirectory => {
|
||||
todo!()
|
||||
}
|
||||
SystemCall::Seek => {
|
||||
todo!()
|
||||
}
|
||||
SystemCall::MapMemory => {
|
||||
let len = args[1];
|
||||
if len == 0 || (len & 0xFFF) != 0 {
|
||||
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| 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 = unsafe { Pid::from_raw(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 = args[0] as u32;
|
||||
|
||||
match Thread::waittid(tid) {
|
||||
Ok(_) => Ok(0),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
SystemCall::GetPid => Ok(Process::current().id().value() as usize),
|
||||
SystemCall::GetTid => Ok(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) {
|
||||
@@ -168,6 +326,125 @@ pub fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
||||
}
|
||||
res.map(|_| 0)
|
||||
}
|
||||
_ => panic!("Undefined system call: {}", num),
|
||||
SystemCall::SetSignalEntry => {
|
||||
Thread::current().set_signal_entry(args[0], args[1]);
|
||||
Ok(0)
|
||||
}
|
||||
SystemCall::SignalReturn => {
|
||||
Thread::current().return_from_signal();
|
||||
unreachable!();
|
||||
}
|
||||
SystemCall::SendSignal => {
|
||||
let target = SignalDestination::from(args[0] as isize);
|
||||
let signal = Signal::try_from(args[1] as u32)?;
|
||||
|
||||
match target {
|
||||
SignalDestination::This => Process::current().set_signal(signal),
|
||||
SignalDestination::Process(pid) => Process::get(pid)
|
||||
.ok_or(Errno::DoesNotExist)?
|
||||
.set_signal(signal),
|
||||
_ => todo!(),
|
||||
};
|
||||
Ok(0)
|
||||
}
|
||||
SystemCall::Yield => {
|
||||
sched::switch(false);
|
||||
Ok(0)
|
||||
}
|
||||
SystemCall::GetSid => {
|
||||
// TODO handle kernel processes here?
|
||||
let pid = args[0] as u32;
|
||||
let current = Process::current();
|
||||
let proc = if pid == 0 {
|
||||
current
|
||||
} else {
|
||||
let pid = unsafe { Pid::from_raw(pid) };
|
||||
let proc = Process::get(pid).ok_or(Errno::DoesNotExist)?;
|
||||
if proc.sid() != current.sid() {
|
||||
return Err(Errno::PermissionDenied);
|
||||
}
|
||||
proc
|
||||
};
|
||||
|
||||
Ok(proc.sid().value() as usize)
|
||||
}
|
||||
SystemCall::GetPgid => {
|
||||
// TODO handle kernel processes here?
|
||||
let pid = args[0] as u32;
|
||||
let current = Process::current();
|
||||
let proc = if pid == 0 {
|
||||
current
|
||||
} else {
|
||||
let pid = unsafe { Pid::from_raw(pid) };
|
||||
Process::get(pid).ok_or(Errno::DoesNotExist)?
|
||||
};
|
||||
|
||||
Ok(proc.pgid().value() as usize)
|
||||
}
|
||||
SystemCall::GetPpid => Ok(Process::current().ppid().unwrap().value() as usize),
|
||||
SystemCall::SetSid => {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
if let Some(_ctty) = io.ctty() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
let id = proc.id();
|
||||
proc.set_sid(id);
|
||||
Ok(id.value() as usize)
|
||||
}
|
||||
SystemCall::SetPgid => {
|
||||
let pid = args[0] as u32;
|
||||
let pgid = args[1] as u32;
|
||||
|
||||
let current = Process::current();
|
||||
let proc = if pid == 0 { current } else { todo!() };
|
||||
|
||||
if pgid == 0 {
|
||||
proc.set_pgid(proc.id());
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
|
||||
Ok(proc.pgid().value() as usize)
|
||||
}
|
||||
|
||||
// System
|
||||
SystemCall::GetCpuTime => {
|
||||
let time = machine::local_timer().timestamp()?;
|
||||
Ok(time.as_nanos() as usize)
|
||||
}
|
||||
SystemCall::Mount => {
|
||||
let target = arg::str_ref(args[0], args[1])?;
|
||||
let options = arg::struct_ref::<MountOptions>(args[2])?;
|
||||
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#![no_std]
|
||||
|
||||
use error::Errno;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum SeekDir {
|
||||
Set,
|
||||
End,
|
||||
Current,
|
||||
}
|
||||
|
||||
pub trait Read {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
pub trait Seek {
|
||||
fn seek(&mut self, off: isize, whence: SeekDir) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
pub trait Write {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
pub fn path_component_left(path: &str) -> (&str, &str) {
|
||||
if let Some((left, right)) = path.split_once('/') {
|
||||
(left, right.trim_start_matches('/'))
|
||||
} else {
|
||||
(path, "")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path_component_right(path: &str) -> (&str, &str) {
|
||||
if let Some((left, right)) = path.trim_end_matches('/').rsplit_once('/') {
|
||||
(left.trim_end_matches('/'), right)
|
||||
} else {
|
||||
("", path)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_le32(src: &[u8]) -> u32 {
|
||||
(src[0] as u32) | ((src[1] as u32) << 8) | ((src[2] as u32) << 16) | ((src[3] as u32) << 24)
|
||||
}
|
||||
|
||||
pub fn read_le16(src: &[u8]) -> u16 {
|
||||
(src[0] as u16) | ((src[1] as u16) << 8)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "syscall"
|
||||
name = "libsys"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "^1.3.0"
|
||||
error = { path = "../error" }
|
||||
enum-repr = "^0.2.6"
|
||||
|
||||
[features]
|
||||
user = []
|
||||
@@ -0,0 +1,51 @@
|
||||
use enum_repr::EnumRepr;
|
||||
|
||||
#[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,
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
use crate::abi::SystemCall;
|
||||
use crate::{
|
||||
debug::TraceLevel,
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
proc::{ExitCode, MemoryAccess, MemoryMap, Pid},
|
||||
signal::{Signal, SignalDestination},
|
||||
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.repr(), options(nostack));
|
||||
res
|
||||
}};
|
||||
($num:expr, $a0:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
asm!("svc #0",
|
||||
inout("x0") res,
|
||||
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.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.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.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.repr(), 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
|
||||
};
|
||||
}
|
||||
// /// Immutable pointer/base argument
|
||||
// macro_rules! argpi {
|
||||
// ($a:expr) => ($a as *const core::ffi::c_void as usize)
|
||||
// }
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// System call
|
||||
#[inline(always)]
|
||||
pub fn sys_exit(code: ExitCode) -> ! {
|
||||
unsafe {
|
||||
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!(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!(SystemCall::Sleep, argn!(ns), argp!(rem.as_mut_ptr()))
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// System call
|
||||
#[inline(always)]
|
||||
pub fn sys_ex_debug_trace(level: TraceLevel, msg: &[u8]) -> Result<(), Errno> {
|
||||
Errno::from_syscall_unit(unsafe {
|
||||
syscall!(
|
||||
SystemCall::DebugTrace,
|
||||
argn!(level.repr()),
|
||||
argp!(msg.as_ptr()),
|
||||
argn!(msg.len())
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// System call
|
||||
#[inline(always)]
|
||||
pub fn sys_openat(
|
||||
at: Option<FileDescriptor>,
|
||||
pathname: &str,
|
||||
mode: FileMode,
|
||||
flags: OpenFlags,
|
||||
) -> Result<FileDescriptor, Errno> {
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(
|
||||
SystemCall::Open,
|
||||
argn!(FileDescriptor::into_i32(at)),
|
||||
argp!(pathname.as_ptr()),
|
||||
argn!(pathname.len()),
|
||||
argn!(mode.bits()),
|
||||
argn!(flags.bits())
|
||||
)
|
||||
})
|
||||
.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!(
|
||||
SystemCall::Read,
|
||||
argn!(u32::from(fd)),
|
||||
argp!(data.as_mut_ptr()),
|
||||
argn!(data.len())
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_write(fd: FileDescriptor, data: &[u8]) -> Result<usize, Errno> {
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(
|
||||
SystemCall::Write,
|
||||
argn!(u32::from(fd)),
|
||||
argp!(data.as_ptr()),
|
||||
argn!(data.len())
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// System call
|
||||
#[inline(always)]
|
||||
pub fn sys_fstatat(
|
||||
at: Option<FileDescriptor>,
|
||||
pathname: &str,
|
||||
statbuf: &mut Stat,
|
||||
flags: u32,
|
||||
) -> Result<(), Errno> {
|
||||
Errno::from_syscall_unit(unsafe {
|
||||
syscall!(
|
||||
SystemCall::FileStatus,
|
||||
argn!(FileDescriptor::into_i32(at)),
|
||||
argp!(pathname.as_ptr()),
|
||||
argn!(pathname.len()),
|
||||
argp!(statbuf as *mut Stat),
|
||||
argn!(flags)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// System call
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_fork() -> Result<Option<Pid>, Errno> {
|
||||
Errno::from_syscall(syscall!(SystemCall::Fork)).map(|res| {
|
||||
if res != 0 {
|
||||
Some(unsafe { Pid::from_raw(res as u32) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// System call
|
||||
#[inline(always)]
|
||||
pub fn sys_execve(pathname: &str, argv: &[&str]) -> Result<(), Errno> {
|
||||
Errno::from_syscall_unit(unsafe {
|
||||
syscall!(
|
||||
SystemCall::Exec,
|
||||
argp!(pathname.as_ptr()),
|
||||
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!(
|
||||
SystemCall::WaitPid,
|
||||
argn!(pid.value()),
|
||||
argp!(status as *mut i32)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// System call
|
||||
#[inline(always)]
|
||||
pub fn sys_ioctl(
|
||||
fd: FileDescriptor,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(
|
||||
SystemCall::Ioctl,
|
||||
argn!(u32::from(fd)),
|
||||
argn!(cmd),
|
||||
argn!(ptr),
|
||||
argn!(len)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[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!(SystemCall::SetSignalEntry, argn!(entry), argn!(stack))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_ex_sigreturn() -> ! {
|
||||
unsafe {
|
||||
syscall!(SystemCall::SignalReturn);
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_ex_kill(pid: SignalDestination, signum: Signal) -> Result<(), Errno> {
|
||||
Errno::from_syscall_unit(unsafe {
|
||||
syscall!(
|
||||
SystemCall::SendSignal,
|
||||
argn!(isize::from(pid)),
|
||||
argn!(signum as u32)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_ex_clone(entry: usize, stack: usize, arg: usize) -> Result<usize, Errno> {
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(SystemCall::Clone, argn!(entry), argn!(stack), argn!(arg))
|
||||
})
|
||||
}
|
||||
|
||||
#[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: u32) -> Result<ExitCode, Errno> {
|
||||
Errno::from_syscall(unsafe { syscall!(SystemCall::WaitTid, argn!(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>,
|
||||
write_fds: Option<&mut FdSet>,
|
||||
timeout: u64,
|
||||
) -> Result<usize, Errno> {
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(
|
||||
SystemCall::Select,
|
||||
argp!(read_fds
|
||||
.map(|e| e as *mut _)
|
||||
.unwrap_or(core::ptr::null_mut())),
|
||||
argp!(write_fds
|
||||
.map(|e| e as *mut _)
|
||||
.unwrap_or(core::ptr::null_mut())),
|
||||
argn!(timeout)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[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() -> u32 {
|
||||
unsafe { syscall!(SystemCall::GetTid) as u32 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_getpid() -> Pid {
|
||||
unsafe { Pid::from_raw(syscall!(SystemCall::GetPid) as u32) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_getpgid(pid: Pid) -> Result<Pid, Errno> {
|
||||
Errno::from_syscall(unsafe { syscall!(SystemCall::GetPgid, argn!(pid.value())) })
|
||||
.map(|e| unsafe { Pid::from_raw(e as u32) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_setpgid(pid: Pid, pgid: Pid) -> Result<Pid, Errno> {
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(SystemCall::SetPgid, argn!(pid.value()), argn!(pgid.value()))
|
||||
})
|
||||
.map(|e| unsafe { Pid::from_raw(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) })
|
||||
.map(|e| unsafe { Pid::from_raw(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())
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_munmap(addr: usize, len: usize) -> Result<(), Errno> {
|
||||
Errno::from_syscall_unit(syscall!(SystemCall::UnmapMemory, argn!(addr), argn!(len)))
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
pub enum Errno {
|
||||
AlreadyExists,
|
||||
BadExecutable,
|
||||
Busy,
|
||||
DeviceError,
|
||||
DoesNotExist,
|
||||
EndOfFile,
|
||||
Interrupt,
|
||||
InvalidArgument,
|
||||
InvalidFile,
|
||||
InvalidOperation,
|
||||
IsADirectory,
|
||||
NotADirectory,
|
||||
NotImplemented,
|
||||
OutOfMemory,
|
||||
PermissionDenied,
|
||||
ReadOnly,
|
||||
TimedOut,
|
||||
TooManyDescriptors,
|
||||
WouldBlock,
|
||||
}
|
||||
|
||||
impl Errno {
|
||||
pub const fn to_negative_isize(self) -> isize {
|
||||
-(self as isize)
|
||||
}
|
||||
|
||||
pub fn from_syscall(u: usize) -> Result<usize, Self> {
|
||||
let i = u as isize;
|
||||
if i < 0 {
|
||||
Err(Self::from((-i) as usize))
|
||||
} else {
|
||||
Ok(u)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_syscall_unit(u: usize) -> Result<(), Self> {
|
||||
let i = u as isize;
|
||||
if i < 0 {
|
||||
Err(Self::from((-i) as usize))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Errno {
|
||||
fn from(u: usize) -> Errno {
|
||||
unsafe { core::mem::transmute(u as u32) }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
use core::convert::TryFrom;
|
||||
use error::Errno;
|
||||
|
||||
use crate::error::Errno;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#![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;
|
||||
pub mod path;
|
||||
pub mod proc;
|
||||
pub mod signal;
|
||||
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
|
||||
}
|
||||
|
||||
#[cfg(feature = "user")]
|
||||
pub mod calls;
|
||||
#[cfg(feature = "user")]
|
||||
pub use calls::*;
|
||||
@@ -1,3 +1,11 @@
|
||||
pub fn read_le32(src: &[u8]) -> u32 {
|
||||
(src[0] as u32) | ((src[1] as u32) << 8) | ((src[2] as u32) << 16) | ((src[3] as u32) << 24)
|
||||
}
|
||||
|
||||
pub fn read_le16(src: &[u8]) -> u16 {
|
||||
(src[0] as u16) | ((src[1] as u16) << 8)
|
||||
}
|
||||
|
||||
/// See memcpy(3p).
|
||||
///
|
||||
/// # Safety
|
||||
@@ -5,7 +13,7 @@
|
||||
/// 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);
|
||||
@@ -20,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
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
pub fn path_component_left(path: &str) -> (&str, &str) {
|
||||
if let Some((left, right)) = path.split_once('/') {
|
||||
(left, right.trim_start_matches('/'))
|
||||
} else {
|
||||
(path, "")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path_component_right(path: &str) -> (&str, &str) {
|
||||
if let Some((left, right)) = path.trim_end_matches('/').rsplit_once('/') {
|
||||
(left.trim_end_matches('/'), right)
|
||||
} else {
|
||||
("", path)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
use crate::error::Errno;
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
|
||||
/// Wrapper type for process exit code
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct ExitCode(i32);
|
||||
|
||||
/// Wrapper type for process ID
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Pid(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 << 0;
|
||||
const ANONYMOUS = 1 << 0;
|
||||
|
||||
const SHARING = 0x3 << 2;
|
||||
const PRIVATE = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for ExitCode {
|
||||
fn from(f: i32) -> Self {
|
||||
Self(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for ExitCode {
|
||||
fn from(_: ()) -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExitCode> for i32 {
|
||||
fn from(f: ExitCode) -> Self {
|
||||
f.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Pid {
|
||||
/// Kernel idle process always has PID of zero
|
||||
pub const IDLE: Self = Self(Self::KERNEL_BIT);
|
||||
|
||||
const KERNEL_BIT: u32 = 1 << 31;
|
||||
|
||||
/// Constructs an instance of user-space PID
|
||||
pub const fn user(id: u32) -> Self {
|
||||
assert!(id < 256, "PID is too high");
|
||||
Self(id)
|
||||
}
|
||||
|
||||
/// Constructs an instance of kernel-space PID
|
||||
pub const fn kernel(id: u32) -> Self {
|
||||
assert!(id & Self::KERNEL_BIT == 0, "PID is too high");
|
||||
Self(id | Self::KERNEL_BIT)
|
||||
}
|
||||
|
||||
/// Returns `true` if this PID belongs to a kernel process
|
||||
pub fn is_kernel(self) -> bool {
|
||||
self.0 & Self::KERNEL_BIT != 0
|
||||
}
|
||||
|
||||
/// Returns address space ID of a user-space process.
|
||||
///
|
||||
/// Panics if called on kernel process PID.
|
||||
pub fn asid(self) -> u8 {
|
||||
assert!(!self.is_kernel());
|
||||
self.0 as u8
|
||||
}
|
||||
|
||||
/// Returns bit value of this pid
|
||||
pub const fn value(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Constructs [Pid] from raw [u32] value
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: does not check `num`
|
||||
pub const unsafe fn from_raw(num: u32) -> Self {
|
||||
Self(num)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Pid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Pid(#{}{})",
|
||||
if self.is_kernel() { "K" } else { "U" },
|
||||
self.0 & !Self::KERNEL_BIT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Pid> for Pgid {
|
||||
type Error = Errno;
|
||||
|
||||
fn try_from(pid: Pid) -> Result<Pgid, Errno> {
|
||||
if pid.is_kernel() {
|
||||
Err(Errno::InvalidArgument)
|
||||
} else {
|
||||
Ok(Pgid(pid.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Pgid {
|
||||
fn from(p: u32) -> Pgid {
|
||||
Self(p)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pgid> for u32 {
|
||||
fn from(p: Pgid) -> u32 {
|
||||
p.0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use crate::error::Errno;
|
||||
use crate::proc::{Pid, Pgid};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum Signal {
|
||||
Interrupt = 2,
|
||||
IllegalInstruction = 4,
|
||||
FloatError = 8,
|
||||
Kill = 9,
|
||||
SegmentationFault = 11,
|
||||
InvalidSystemCall = 31
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum SignalDestination {
|
||||
Group(Pgid),
|
||||
Process(Pid),
|
||||
All,
|
||||
This
|
||||
}
|
||||
|
||||
impl From<isize> for SignalDestination {
|
||||
fn from(num: isize) -> Self {
|
||||
if num > 0 {
|
||||
Self::Process(Pid::user(num as u32))
|
||||
} else if num == 0 {
|
||||
Self::This
|
||||
} else if num == -1 {
|
||||
Self::All
|
||||
} else {
|
||||
Self::Group(Pgid::from((-num) as u32))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignalDestination> for isize {
|
||||
fn from(p: SignalDestination) -> isize {
|
||||
match p {
|
||||
SignalDestination::Process(pid) => pid.value() as isize,
|
||||
SignalDestination::Group(pgid) => -(u32::from(pgid) as isize),
|
||||
SignalDestination::This => 0,
|
||||
SignalDestination::All => -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for Signal {
|
||||
type Error = Errno;
|
||||
|
||||
#[inline]
|
||||
fn try_from(u: u32) -> Result<Self, Errno> {
|
||||
match u {
|
||||
2 => Ok(Self::Interrupt),
|
||||
4 => Ok(Self::IllegalInstruction),
|
||||
8 => Ok(Self::FloatError),
|
||||
9 => Ok(Self::Kill),
|
||||
11 => Ok(Self::SegmentationFault),
|
||||
31 => Ok(Self::InvalidSystemCall),
|
||||
_ => Err(Errno::InvalidArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
// TODO split up this file
|
||||
use crate::error::Errno;
|
||||
use core::fmt;
|
||||
|
||||
const AT_FDCWD: i32 = -2;
|
||||
pub const AT_EMPTY_PATH: u32 = 1 << 16;
|
||||
|
||||
bitflags! {
|
||||
pub struct OpenFlags: u32 {
|
||||
const O_RDONLY = 1;
|
||||
const O_WRONLY = 2;
|
||||
const O_RDWR = 3;
|
||||
const O_ACCESS = 0x7;
|
||||
|
||||
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;
|
||||
const GROUP_READ = 1 << 5;
|
||||
const GROUP_WRITE = 1 << 4;
|
||||
const GROUP_EXEC = 1 << 3;
|
||||
const OTHER_READ = 1 << 2;
|
||||
const OTHER_WRITE = 1 << 1;
|
||||
const OTHER_EXEC = 1 << 0;
|
||||
}
|
||||
}
|
||||
|
||||
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],
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Stat {
|
||||
pub mode: FileMode,
|
||||
pub size: u64,
|
||||
pub blksize: u32,
|
||||
}
|
||||
|
||||
impl DirectoryEntry {
|
||||
pub const fn empty() -> Self {
|
||||
Self { name: [0; 64] }
|
||||
}
|
||||
|
||||
pub fn from_str(i: &str) -> DirectoryEntry {
|
||||
let mut res = DirectoryEntry { name: [0; 64] };
|
||||
let bytes = i.as_bytes();
|
||||
res.name[..bytes.len()].copy_from_slice(bytes);
|
||||
res
|
||||
}
|
||||
|
||||
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 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] }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reset(&mut self) {
|
||||
self.bits.fill(0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.bits.iter().any(|&x| x != 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set(&mut self, fd: FileDescriptor) {
|
||||
self.bits[(fd.0 as usize) / 64] |= 1 << (fd.0 % 64);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self, fd: FileDescriptor) {
|
||||
self.bits[(fd.0 as usize) / 64] &= !(1 << (fd.0 % 64));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set(&self, fd: FileDescriptor) -> bool {
|
||||
self.bits[(fd.0 as usize) / 64] & (1 << (fd.0 % 64)) != 0
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = FileDescriptor> + '_ {
|
||||
FdSetIter { idx: 0, set: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for FdSetIter<'_> {
|
||||
type Item = FileDescriptor;
|
||||
|
||||
fn next(&mut self) -> Option<FileDescriptor> {
|
||||
while self.idx < 128 {
|
||||
if self.set.is_set(FileDescriptor(self.idx)) {
|
||||
let res = self.idx;
|
||||
self.idx += 1;
|
||||
return Some(FileDescriptor::from(res));
|
||||
}
|
||||
self.idx += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FdSet {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "FdSet {{ ")?;
|
||||
for (count, fd) in self.iter().enumerate() {
|
||||
if count != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{:?}", fd)?;
|
||||
}
|
||||
write!(f, " }}")
|
||||
}
|
||||
}
|
||||
|
||||
impl FileMode {
|
||||
/// Returns default permission set for directories
|
||||
pub fn default_dir() -> Self {
|
||||
unsafe { Self::from_bits_unchecked(0o755) | Self::S_IFDIR }
|
||||
}
|
||||
|
||||
/// Returns default permission set for regular files
|
||||
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', '-'),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileDescriptor {
|
||||
pub const STDIN: Self = Self(0);
|
||||
pub const STDOUT: Self = Self(1);
|
||||
pub const STDERR: Self = Self(2);
|
||||
|
||||
pub fn from_i32(u: i32) -> Result<Option<Self>, Errno> {
|
||||
if u >= 0 {
|
||||
Ok(Some(Self(u as u32)))
|
||||
} else if u == AT_FDCWD {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(Errno::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_i32(u: Option<Self>) -> i32 {
|
||||
if let Some(u) = u {
|
||||
u.0 as i32
|
||||
} else {
|
||||
AT_FDCWD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for FileDescriptor {
|
||||
fn from(u: u32) -> Self {
|
||||
Self(u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FileDescriptor> for u32 {
|
||||
fn from(u: FileDescriptor) -> u32 {
|
||||
u.0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use crate::error::Errno;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum SeekDir {
|
||||
Set,
|
||||
End,
|
||||
Current,
|
||||
}
|
||||
|
||||
pub trait Read {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
pub trait Seek {
|
||||
fn seek(&mut self, off: isize, whence: SeekDir) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
pub trait Write {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
pub trait RandomRead {
|
||||
fn pread(&mut self, pos: usize, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
pub trait RandomWrite {
|
||||
fn pwrite(&mut self, pos: usize, data: &[u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
+3
-1
@@ -6,4 +6,6 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
syscall = { path = "../syscall", features = ["user"] }
|
||||
libsys = { path = "../libsys", features = ["user"] }
|
||||
lazy_static = { version = "^1.4.0", features = ["spin_no_std"] }
|
||||
memoffset = "^0.6.4"
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
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,
|
||||
)?;
|
||||
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) {
|
||||
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 = (&mut *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 (&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;
|
||||
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) {
|
||||
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() {
|
||||
(&mut *(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);
|
||||
(&mut *zone).list.del();
|
||||
Zone::free(zone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn alloc_error_handler(_layout: Layout) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use libsys::{debug::TraceLevel, ProgramArgs};
|
||||
use alloc::vec::Vec;
|
||||
use crate::trace;
|
||||
|
||||
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 + 0) 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);
|
||||
}
|
||||
|
||||
trace!(TraceLevel::Debug, "args = {:?}", PROGRAM_ARGS);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use crate::io::{AsRawFd, Error, Read};
|
||||
use libsys::{
|
||||
calls::{sys_openat, sys_read, sys_close},
|
||||
stat::{FileDescriptor, FileMode, OpenFlags},
|
||||
};
|
||||
|
||||
pub struct File {
|
||||
fd: FileDescriptor,
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn open(path: &str) -> Result<File, Error> {
|
||||
let fd = sys_openat(None, path, FileMode::default_reg(), OpenFlags::O_RDONLY)
|
||||
.map_err(Error::from)?;
|
||||
Ok(File { fd })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for File {
|
||||
fn as_raw_fd(&self) -> FileDescriptor {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for File {
|
||||
fn drop(&mut self) {
|
||||
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,65 +0,0 @@
|
||||
use crate::sys::{self, Stat};
|
||||
use core::fmt;
|
||||
use syscall::stat::AT_FDCWD;
|
||||
|
||||
// TODO populate this type
|
||||
pub struct Error;
|
||||
|
||||
pub fn stat(pathname: &str) -> Result<Stat, Error> {
|
||||
let mut buf = Stat::default();
|
||||
let res = unsafe { sys::sys_fstatat(AT_FDCWD, pathname, &mut buf, 0) };
|
||||
if res != 0 {
|
||||
todo!();
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
// print!/println! group
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($args:tt)+) => ($crate::io::_print($crate::sys::STDOUT_FILENO, format_args!($($args)+)))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($($args:tt)+) => (print!("{}\n", format_args!($($args)+)))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! eprint {
|
||||
($($args:tt)+) => ($crate::io::_print($crate::sys::STDERR_FILENO, format_args!($($args)+)))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! eprintln {
|
||||
($($args:tt)+) => (eprint!("{}\n", format_args!($($args)+)))
|
||||
}
|
||||
|
||||
struct BufferWriter<'a> {
|
||||
buf: &'a mut [u8],
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl fmt::Write for BufferWriter<'_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for byte in s.bytes() {
|
||||
self.buf[self.pos] = byte;
|
||||
self.pos += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _print(fd: i32, args: fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
static mut BUFFER: [u8; 4096] = [0; 4096];
|
||||
let mut writer = BufferWriter {
|
||||
buf: unsafe { &mut BUFFER },
|
||||
pos: 0,
|
||||
};
|
||||
writer.write_fmt(args).ok();
|
||||
unsafe {
|
||||
sys::sys_write(fd, &BUFFER[..writer.pos]);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user