feat: proof-of-concept tty chardev
This commit is contained in:
@@ -91,7 +91,8 @@ initrd:
|
||||
-Z build-std=core,alloc,compiler_builtins \
|
||||
$(CARGO_COMMON_OPTS)
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)
|
||||
cd $(O) && tar cf initrd.img init
|
||||
echo This is a test file >$(O)/test.txt
|
||||
cd $(O) && tar cf initrd.img init test.txt
|
||||
ifeq ($(MACH),orangepi3)
|
||||
$(MKIMAGE) \
|
||||
-A arm64 \
|
||||
|
||||
@@ -11,4 +11,6 @@ pub enum Errno {
|
||||
AlreadyExists,
|
||||
NotImplemented,
|
||||
TimedOut,
|
||||
EndOfFile,
|
||||
Interrupt
|
||||
}
|
||||
|
||||
+11
-5
@@ -1,4 +1,5 @@
|
||||
use vfs::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
use vfs::{VnodeImpl, VnodeKind, VnodeRef, Vnode};
|
||||
use alloc::boxed::Box;
|
||||
use error::Errno;
|
||||
|
||||
pub struct DirInode;
|
||||
@@ -7,14 +8,19 @@ impl VnodeImpl for DirInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
_parent: VnodeRef,
|
||||
_name: &str,
|
||||
_kind: VnodeKind,
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
todo!()
|
||||
let vnode = Vnode::new(name, kind, Vnode::SEEKABLE);
|
||||
match kind {
|
||||
VnodeKind::Directory => vnode.set_data(Box::new(DirInode {})),
|
||||
_ => todo!()
|
||||
}
|
||||
Ok(vnode)
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _parent: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
panic!()
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
|
||||
fn remove(&mut self, _parent: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
|
||||
+4
-2
@@ -64,6 +64,8 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
match kind {
|
||||
VnodeKind::Directory => node.set_data(Box::new(DirInode {})),
|
||||
VnodeKind::Regular => {}
|
||||
VnodeKind::Char => todo!(),
|
||||
VnodeKind::Block => todo!(),
|
||||
};
|
||||
node
|
||||
}
|
||||
@@ -176,9 +178,9 @@ mod tests {
|
||||
let root = fs.root().unwrap();
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
assert!(Rc::ptr_eq(&ioctx.find(None, "/").unwrap(), &root));
|
||||
assert!(Rc::ptr_eq(&ioctx.find(None, "/", true).unwrap(), &root));
|
||||
|
||||
let node = ioctx.find(None, "/test1.txt").unwrap();
|
||||
let node = ioctx.find(None, "/test1.txt", true).unwrap();
|
||||
let mut file = node.open().unwrap();
|
||||
let mut buf = [0u8; 1024];
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
use crate::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
use error::Errno;
|
||||
|
||||
/// Generic character device trait
|
||||
pub trait CharDevice {
|
||||
/// Performs a read from the device into [data] buffer.
|
||||
///
|
||||
/// If no data is available and `blocking` is set, will ask
|
||||
/// the OS to suspend the calling thread until data arrives.
|
||||
/// Otherwise, will immediately return an error.
|
||||
fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
/// Performs a write to the device from [data] buffer.
|
||||
///
|
||||
/// If the device cannot (at the moment) accept data and
|
||||
/// `blocking` is set, will block until it's available. Otherwise,
|
||||
/// will immediately return an error.
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
/// Wrapper struct to attach [VnodeImpl] implementation
|
||||
/// to [CharDevice]s
|
||||
pub struct CharDeviceWrapper {
|
||||
device: &'static dyn CharDevice,
|
||||
}
|
||||
|
||||
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) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
self.device.read(true, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
self.device.write(true, data)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDeviceWrapper {
|
||||
/// Creates a wrapper for static [CharDevice] trait object to
|
||||
/// auto-implement [VnodeImpl] trait for the device
|
||||
pub const fn new(device: &'static dyn CharDevice) -> Self {
|
||||
Self { device }
|
||||
}
|
||||
}
|
||||
+9
-3
@@ -1,4 +1,4 @@
|
||||
use crate::VnodeRef;
|
||||
use crate::{VnodeRef, VnodeKind};
|
||||
use core::cmp::min;
|
||||
use error::Errno;
|
||||
use libcommon::{Read, Seek, SeekDir, Write};
|
||||
@@ -25,7 +25,9 @@ impl Read for File {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
let count = inner.vnode.read(inner.pos, data)?;
|
||||
inner.pos += count;
|
||||
if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
@@ -38,7 +40,9 @@ impl Write for File {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
let count = inner.vnode.write(inner.pos, data)?;
|
||||
inner.pos += count;
|
||||
if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
@@ -111,6 +115,8 @@ mod tests {
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
#[cfg(test)]
|
||||
println!("read {}", pos);
|
||||
let len = 123;
|
||||
if pos >= len {
|
||||
return Ok(0);
|
||||
|
||||
+85
-29
@@ -1,4 +1,4 @@
|
||||
use crate::{FileMode, VnodeRef};
|
||||
use crate::{FileMode, VnodeRef, VnodeKind};
|
||||
use error::Errno;
|
||||
use libcommon::{path_component_left, path_component_right};
|
||||
|
||||
@@ -17,7 +17,7 @@ impl Ioctx {
|
||||
}
|
||||
}
|
||||
|
||||
fn _find(&self, mut at: VnodeRef, path: &str) -> Result<VnodeRef, Errno> {
|
||||
fn _find(&self, mut at: VnodeRef, path: &str, follow: bool) -> Result<VnodeRef, Errno> {
|
||||
let mut element;
|
||||
let mut rest = path;
|
||||
|
||||
@@ -42,17 +42,27 @@ impl Ioctx {
|
||||
}
|
||||
assert!(!element.is_empty());
|
||||
|
||||
let node = at.lookup_or_load(element)?;
|
||||
let mut node = at.lookup_or_load(element)?;
|
||||
|
||||
while let Some(target) = node.target() {
|
||||
assert!(node.kind() == VnodeKind::Directory);
|
||||
node = target;
|
||||
}
|
||||
|
||||
if rest.is_empty() {
|
||||
Ok(node)
|
||||
} else {
|
||||
self._find(node, rest)
|
||||
self._find(node, rest, follow)
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up a path in given ioctx
|
||||
pub fn find(&self, at: Option<VnodeRef>, mut path: &str) -> Result<VnodeRef, Errno> {
|
||||
pub fn find(
|
||||
&self,
|
||||
at: Option<VnodeRef>,
|
||||
mut path: &str,
|
||||
follow: bool,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let at = if path.starts_with('/') {
|
||||
path = path.trim_start_matches('/');
|
||||
self.root.clone()
|
||||
@@ -62,7 +72,7 @@ impl Ioctx {
|
||||
self.cwd.clone()
|
||||
};
|
||||
|
||||
self._find(at, path)
|
||||
self._find(at, path, follow)
|
||||
}
|
||||
|
||||
/// Creates a new directory
|
||||
@@ -73,7 +83,7 @@ impl Ioctx {
|
||||
mode: FileMode,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let (parent, name) = path_component_right(path);
|
||||
self.find(at, parent)?.mkdir(name, mode)
|
||||
self.find(at, parent, true)?.mkdir(name.trim_start_matches('/'), mode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,42 +157,65 @@ mod tests {
|
||||
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/").unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/.").unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/./.").unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/.///.").unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/..").unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/../").unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/../.").unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/../..").unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/.", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/./.", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
&ioctx.find(None, "/.///.", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/..", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/../", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
&ioctx.find(None, "/../.", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
&ioctx.find(None, "/../..", false).unwrap()
|
||||
));
|
||||
|
||||
assert!(Rc::ptr_eq(&d0, &ioctx.find(None, "/dir0").unwrap()));
|
||||
assert!(Rc::ptr_eq(&d1, &ioctx.find(None, "/dir1").unwrap()));
|
||||
assert!(Rc::ptr_eq(&d0, &ioctx.find(None, "/dir1/../dir0").unwrap()));
|
||||
assert!(Rc::ptr_eq(&d0, &ioctx.find(None, "/dir0", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&d1, &ioctx.find(None, "/dir1", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir1/../dir0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d1,
|
||||
&ioctx.find(None, "/dir1/../dir0/./../../.././dir1").unwrap()
|
||||
&ioctx
|
||||
.find(None, "/dir1/../dir0/./../../.././dir1", false)
|
||||
.unwrap()
|
||||
));
|
||||
|
||||
assert!(Rc::ptr_eq(&d0d0, &ioctx.find(None, "/dir0/dir0").unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0d0,
|
||||
&ioctx.find(None, "/dir0/dir0/.").unwrap()
|
||||
&ioctx.find(None, "/dir0/dir0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(&d0, &ioctx.find(None, "/dir0/dir0/..").unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/../").unwrap()
|
||||
&d0d0,
|
||||
&ioctx.find(None, "/dir0/dir0/.", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/../.").unwrap()
|
||||
&ioctx.find(None, "/dir0/dir0/..", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/../", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/../.", false).unwrap()
|
||||
));
|
||||
|
||||
assert!(Rc::ptr_eq(&d0f0, &ioctx.find(None, "/dir0/file0").unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0f0,
|
||||
&ioctx.find(None, "/dir1/../dir0/./file0").unwrap()
|
||||
&ioctx.find(None, "/dir0/file0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0f0,
|
||||
&ioctx.find(None, "/dir1/../dir0/./file0", false).unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -198,11 +231,11 @@ mod tests {
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
assert_eq!(
|
||||
ioctx.find(None, "/dir0/file0/.").unwrap_err(),
|
||||
ioctx.find(None, "/dir0/file0/.", false).unwrap_err(),
|
||||
Errno::NotADirectory
|
||||
);
|
||||
assert_eq!(
|
||||
ioctx.find(None, "/dir0/file0/..").unwrap_err(),
|
||||
ioctx.find(None, "/dir0/file0/..", false).unwrap_err(),
|
||||
Errno::NotADirectory
|
||||
);
|
||||
|
||||
@@ -225,4 +258,27 @@ mod tests {
|
||||
Errno::AlreadyExists
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_mount() {
|
||||
let root_outer = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let root_inner = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0);
|
||||
|
||||
root_outer.clone().attach(dir0.clone());
|
||||
root_inner.clone().attach(dir1.clone());
|
||||
|
||||
let ioctx = Ioctx::new(root_outer.clone());
|
||||
|
||||
assert_eq!(ioctx.find(None, "/dir0/dir1", false).unwrap_err(), Errno::DoesNotExist);
|
||||
|
||||
dir0.mount(root_inner.clone()).unwrap();
|
||||
|
||||
assert!(Rc::ptr_eq(&root_inner, &ioctx.find(None, "/dir0", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&dir1, &ioctx.find(None, "/dir0/dir1", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root_inner, &ioctx.find(None, "/dir0/dir1/..", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&dir0, &ioctx.find(None, "/dir0/dir1/../..", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root_outer, &ioctx.find(None, "/dir0/dir1/../../..", false).unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -1,8 +1,12 @@
|
||||
//! Virtual filesystem API and facilities
|
||||
#![warn(missing_docs)]
|
||||
#![feature(destructuring_assignment)]
|
||||
#![feature(destructuring_assignment, const_fn_trait_bound)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod block;
|
||||
@@ -17,3 +21,5 @@ mod ioctx;
|
||||
pub use ioctx::Ioctx;
|
||||
mod file;
|
||||
pub use file::File;
|
||||
mod char;
|
||||
pub use crate::char::{CharDevice, CharDeviceWrapper};
|
||||
|
||||
+48
-4
@@ -14,6 +14,10 @@ pub enum VnodeKind {
|
||||
Directory,
|
||||
/// Node is a regular file
|
||||
Regular,
|
||||
/// Node is a character device
|
||||
Char,
|
||||
/// Node is a block device
|
||||
Block,
|
||||
}
|
||||
|
||||
pub(crate) struct TreeNode {
|
||||
@@ -36,6 +40,7 @@ pub struct Vnode {
|
||||
kind: VnodeKind,
|
||||
flags: u32,
|
||||
|
||||
target: RefCell<Option<VnodeRef>>,
|
||||
fs: RefCell<Option<Rc<dyn Filesystem>>>,
|
||||
data: RefCell<Option<Box<dyn VnodeImpl>>>,
|
||||
}
|
||||
@@ -88,11 +93,17 @@ impl Vnode {
|
||||
parent: None,
|
||||
children: Vec::new(),
|
||||
}),
|
||||
target: RefCell::new(None),
|
||||
fs: RefCell::new(None),
|
||||
data: RefCell::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
pub fn name<'a>(&'a self) -> &'a str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Sets an associated [VnodeImpl] for the [Vnode]
|
||||
pub fn set_data(&self, data: Box<dyn VnodeImpl>) {
|
||||
*self.data.borrow_mut() = Some(data);
|
||||
@@ -158,11 +169,38 @@ impl Vnode {
|
||||
parent_borrow.children.remove(index);
|
||||
}
|
||||
|
||||
///
|
||||
pub fn mount(self: &VnodeRef, root: VnodeRef) -> Result<(), Errno> {
|
||||
if !self.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if !root.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if self.target.borrow().is_some() {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
let mut child_borrow = root.tree.borrow_mut();
|
||||
if child_borrow.parent.is_some() {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
child_borrow.parent = Some(self.clone());
|
||||
*self.target.borrow_mut() = Some(root.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns this vnode's parent or itself if it has none
|
||||
pub fn parent(self: &VnodeRef) -> VnodeRef {
|
||||
self.tree.borrow().parent.as_ref().unwrap_or(self).clone()
|
||||
}
|
||||
|
||||
///
|
||||
pub fn target(self: &VnodeRef) -> Option<VnodeRef> {
|
||||
self.target.borrow().clone()
|
||||
}
|
||||
|
||||
/// Looks up a child `name` in in-memory tree cache
|
||||
pub fn lookup(self: &VnodeRef, name: &str) -> Option<VnodeRef> {
|
||||
assert!(self.is_directory());
|
||||
@@ -189,7 +227,7 @@ impl Vnode {
|
||||
self.attach(vnode.clone());
|
||||
Ok(vnode)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +236,9 @@ impl Vnode {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if name.contains('/') {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
match self.lookup_or_load(name) {
|
||||
Err(Errno::DoesNotExist) => {}
|
||||
@@ -223,6 +264,9 @@ impl Vnode {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if name.contains('/') {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
let vnode = self.lookup(name).ok_or(Errno::DoesNotExist)?;
|
||||
@@ -236,7 +280,7 @@ impl Vnode {
|
||||
|
||||
/// Opens a vnode for access
|
||||
pub fn open(self: &VnodeRef) -> Result<File, Errno> {
|
||||
if self.kind != VnodeKind::Regular {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
@@ -250,7 +294,7 @@ impl Vnode {
|
||||
|
||||
/// Reads data from offset `pos` into `buf`
|
||||
pub fn read(self: &VnodeRef, pos: usize, buf: &mut [u8]) -> Result<usize, Errno> {
|
||||
if self.kind != VnodeKind::Regular {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
@@ -263,7 +307,7 @@ impl Vnode {
|
||||
|
||||
/// Writes data from `buf` to offset `pos`
|
||||
pub fn write(self: &VnodeRef, pos: usize, buf: &[u8]) -> Result<usize, Errno> {
|
||||
if self.kind != VnodeKind::Regular {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -8,10 +8,10 @@ extern crate libusr;
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
loop {
|
||||
println!("Hello to stdout");
|
||||
trace!("Hello from userspace");
|
||||
unsafe {
|
||||
asm!("svc #0", in("x8") 121, in("x0") 1000000000);
|
||||
}
|
||||
}
|
||||
123
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::arch::{
|
||||
aarch64::reg::{CNTKCTL_EL1, CPACR_EL1},
|
||||
machine,
|
||||
};
|
||||
use crate::fs::devfs;
|
||||
use crate::dev::{fdt::DeviceTree, irq::IntSource, Device};
|
||||
//use crate::debug::Level;
|
||||
use crate::mem::{
|
||||
@@ -56,6 +57,8 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
|
||||
heap::init(heap_base_virt, 16 * 1024 * 1024);
|
||||
}
|
||||
|
||||
devfs::init();
|
||||
|
||||
machine::init_board().unwrap();
|
||||
|
||||
let initrd;
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
.global __aa64_ctx_switch_to
|
||||
.global __aa64_ctx_enter_kernel
|
||||
|
||||
.set PT_REGS_SIZE, 16 * 6
|
||||
.set PT_REGS_SIZE, 16 * 7
|
||||
|
||||
__aa64_ctx_enter_user:
|
||||
ldp x0, x1, [sp, #0]
|
||||
msr sp_el0, x0
|
||||
msr ttbr0_el1, x1
|
||||
|
||||
msr spsr_el1, xzr
|
||||
ldp x0, x1, [sp, #16]
|
||||
@@ -21,7 +20,6 @@ __return_to_user:
|
||||
|
||||
__aa64_ctx_enter_kernel:
|
||||
msr sp_el0, xzr
|
||||
msr ttbr0_el1, xzr
|
||||
|
||||
mov x0, #5
|
||||
msr spsr_el1, x0
|
||||
@@ -41,6 +39,8 @@ __aa64_ctx_switch:
|
||||
stp x25, x26, [sp, #16 * 3]
|
||||
stp x27, x28, [sp, #16 * 4]
|
||||
stp x29, x30, [sp, #16 * 5]
|
||||
mrs x19, TTBR0_EL1
|
||||
stp x19, xzr, [sp, #16 * 6]
|
||||
|
||||
mov x19, sp
|
||||
str x19, [x1]
|
||||
@@ -48,6 +48,8 @@ __aa64_ctx_switch_to:
|
||||
ldr x0, [x0]
|
||||
mov sp, x0
|
||||
|
||||
ldp x19, xzr, [sp, #16 * 6]
|
||||
msr TTBR0_EL1, x19
|
||||
ldp x19, x20, [sp, #16 * 0]
|
||||
ldp x21, x22, [sp, #16 * 1]
|
||||
ldp x23, x24, [sp, #16 * 2]
|
||||
|
||||
@@ -29,7 +29,7 @@ impl Context {
|
||||
stack.push(entry);
|
||||
stack.push(arg);
|
||||
|
||||
stack.setup_common(__aa64_ctx_enter_kernel as usize);
|
||||
stack.setup_common(__aa64_ctx_enter_kernel as usize, 0);
|
||||
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
@@ -45,10 +45,10 @@ impl Context {
|
||||
|
||||
stack.push(entry);
|
||||
stack.push(arg);
|
||||
stack.push(ttbr0);
|
||||
stack.push(/* ttbr0 */ 0);
|
||||
stack.push(ustack);
|
||||
|
||||
stack.setup_common(__aa64_ctx_enter_user as usize);
|
||||
stack.setup_common(__aa64_ctx_enter_user as usize, ttbr0);
|
||||
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
@@ -89,7 +89,9 @@ impl Stack {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_common(&mut self, entry: usize) {
|
||||
pub fn setup_common(&mut self, entry: usize, ttbr: usize) {
|
||||
self.push(0);
|
||||
self.push(ttbr);
|
||||
self.push(entry); // x30/lr
|
||||
self.push(0); // x29
|
||||
self.push(0); // x28
|
||||
|
||||
@@ -85,7 +85,10 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
||||
unsafe {
|
||||
match syscall::syscall(exc.x[8], &exc.x[..6]) {
|
||||
Ok(val) => exc.x[0] = val,
|
||||
Err(_) => exc.x[0] = usize::MAX,
|
||||
Err(err) => {
|
||||
warnln!("syscall {} failed: {:?}", exc.x[8], err);
|
||||
exc.x[0] = usize::MAX
|
||||
},
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::dev::{
|
||||
serial::{pl011::Pl011, SerialDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::fs::devfs;
|
||||
use crate::mem::phys;
|
||||
use error::Errno;
|
||||
|
||||
@@ -46,6 +47,7 @@ pub fn init_board() -> Result<(), Errno> {
|
||||
GIC.enable()?;
|
||||
|
||||
UART0.init_irqs()?;
|
||||
devfs::add_char_device("uart0", &UART0)?;
|
||||
|
||||
RTC.enable()?;
|
||||
RTC.init_irqs()?;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
mrs x0, spsr_el1
|
||||
mrs x1, elr_el1
|
||||
mrs x2, sp_el0
|
||||
mrs x3, ttbr0_el1
|
||||
// mrs x3, ttbr0_el1
|
||||
|
||||
stp x0, x1, [sp, #16 * 16]
|
||||
stp x2, x3, [sp, #16 * 17]
|
||||
@@ -38,7 +38,7 @@
|
||||
msr spsr_el1, x0
|
||||
msr elr_el1, x1
|
||||
msr sp_el0, x2
|
||||
msr ttbr0_el1, x3
|
||||
// msr ttbr0_el1, x3
|
||||
|
||||
ldp x0, x1, [sp, #16 * 0]
|
||||
ldp x2, x3, [sp, #16 * 1]
|
||||
|
||||
@@ -13,6 +13,8 @@ pub mod pci;
|
||||
pub mod rtc;
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
#[allow(missing_docs)]
|
||||
pub mod tty;
|
||||
|
||||
/// Generic device trait
|
||||
pub trait Device {
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
Device,
|
||||
tty::CharRing, Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
@@ -16,6 +16,7 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use vfs::CharDevice;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -78,6 +79,7 @@ struct Pl011Inner {
|
||||
/// Device struct for PL011
|
||||
pub struct Pl011 {
|
||||
inner: InitOnce<IrqSafeSpinLock<Pl011Inner>>,
|
||||
ring: CharRing<16>,
|
||||
base: usize,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
@@ -135,7 +137,8 @@ impl IntSource for Pl011 {
|
||||
|
||||
let byte = inner.regs.DR.get();
|
||||
drop(inner);
|
||||
debugln!("irq byte = {:#04x}", byte);
|
||||
|
||||
self.ring.putc(byte as u8, false).ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -166,6 +169,23 @@ impl SerialDevice for Pl011 {
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for Pl011 {
|
||||
fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
assert!(blocking);
|
||||
self.ring.line_read(data, self)
|
||||
}
|
||||
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Errno> {
|
||||
assert!(blocking);
|
||||
for &byte in data {
|
||||
unsafe {
|
||||
self.inner.get().lock().send(byte);
|
||||
}
|
||||
}
|
||||
Ok(data.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Pl011 {
|
||||
fn name(&self) -> &'static str {
|
||||
"PL011 UART"
|
||||
@@ -192,6 +212,7 @@ impl Pl011 {
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
ring: CharRing::new(),
|
||||
base,
|
||||
irq,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! Timer interface
|
||||
|
||||
use crate::dev::Device;
|
||||
use crate::proc::Pid;
|
||||
use core::time::Duration;
|
||||
use error::Errno;
|
||||
|
||||
@@ -10,9 +9,3 @@ pub trait TimestampSource: Device {
|
||||
/// Reads current timestamp as a [Duration] from system start time
|
||||
fn timestamp(&self) -> Result<Duration, Errno>;
|
||||
}
|
||||
|
||||
///
|
||||
pub struct Sleep {
|
||||
deadline: Duration,
|
||||
pid: Pid
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
use error::Errno;
|
||||
use crate::proc::wait::Wait;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use vfs::CharDevice;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug)]
|
||||
struct CharRingInner<const N: usize> {
|
||||
rd: usize,
|
||||
wr: usize,
|
||||
data: [u8; N],
|
||||
flags: u8
|
||||
}
|
||||
|
||||
///
|
||||
pub struct CharRing<const N: usize> {
|
||||
wait_read: Wait,
|
||||
wait_write: Wait,
|
||||
inner: IrqSafeSpinLock<CharRingInner<N>>,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<const N: usize> CharRingInner<N> {
|
||||
#[inline]
|
||||
const fn is_readable(&self) -> bool {
|
||||
if self.rd <= self.wr {
|
||||
(self.wr - self.rd) > 0
|
||||
} else {
|
||||
(self.wr + (N - self.rd)) > 0
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_unchecked(&mut self) -> u8 {
|
||||
let res = self.data[self.rd];
|
||||
self.rd = (self.rd + 1) % N;
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_unchecked(&mut self, ch: u8) {
|
||||
self.data[self.wr] = ch;
|
||||
self.wr = (self.wr + 1) % N;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<const N: usize> CharRing<N> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: IrqSafeSpinLock::new(CharRingInner {
|
||||
rd: 0,
|
||||
wr: 0,
|
||||
data: [0; N],
|
||||
flags: 0
|
||||
}),
|
||||
wait_read: Wait::new(),
|
||||
wait_write: Wait::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getc(&self) -> Result<u8, Errno> {
|
||||
let mut lock = self.inner.lock();
|
||||
loop {
|
||||
if !lock.is_readable() && lock.flags == 0 {
|
||||
drop(lock);
|
||||
self.wait_read.sleep_on(None)?;
|
||||
lock = self.inner.lock();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if lock.flags != 0 {
|
||||
todo!();
|
||||
}
|
||||
let byte = lock.read_unchecked();
|
||||
self.wait_write.wakeup_one();
|
||||
Ok(byte)
|
||||
}
|
||||
|
||||
pub fn dump(&self) {
|
||||
debugln!("{:?}", self.inner.lock());
|
||||
}
|
||||
|
||||
pub fn putc(&self, ch: u8, blocking: bool) -> Result<(), Errno> {
|
||||
let mut lock = self.inner.lock();
|
||||
if blocking {
|
||||
todo!()
|
||||
}
|
||||
lock.write_unchecked(ch);
|
||||
self.wait_read.wakeup_one();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn line_read<T: CharDevice>(&self, data: &mut [u8], dev: &T) -> Result<usize, Errno> {
|
||||
let mut rem = data.len();
|
||||
let mut off = 0;
|
||||
|
||||
while rem != 0 {
|
||||
let byte = match self.getc() {
|
||||
Ok(ch) => ch,
|
||||
Err(Errno::Interrupt) => {
|
||||
todo!()
|
||||
},
|
||||
Err(Errno::EndOfFile) => {
|
||||
todo!()
|
||||
},
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if byte == b'\n' || byte == b'\r' {
|
||||
break;
|
||||
}
|
||||
|
||||
if byte == 0x7F {
|
||||
if off > 0 {
|
||||
dev.write(true, b"\x1B[D \x1B[D").ok();
|
||||
off -= 1;
|
||||
rem += 1;
|
||||
}
|
||||
continue;
|
||||
} else if byte >= b' ' {
|
||||
// TODO tty options
|
||||
dev.write(true, &[byte]).ok();
|
||||
}
|
||||
|
||||
data[off] = byte;
|
||||
off += 1;
|
||||
rem -= 1;
|
||||
}
|
||||
Ok(off)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
use vfs::{Vnode, VnodeKind, CharDevice, VnodeRef, CharDeviceWrapper};
|
||||
use alloc::boxed::Box;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
|
||||
static DEVFS_ROOT: InitOnce<VnodeRef> = InitOnce::new();
|
||||
|
||||
pub fn init() {
|
||||
DEVFS_ROOT.init(Vnode::new("", VnodeKind::Directory, 0));
|
||||
}
|
||||
|
||||
pub fn root() -> &'static VnodeRef {
|
||||
DEVFS_ROOT.get()
|
||||
}
|
||||
|
||||
pub fn add_char_device(name: &str, dev: &'static dyn CharDevice) -> Result<(), Errno> {
|
||||
debugln!("Add device: {}", name);
|
||||
let node = Vnode::new(name, VnodeKind::Char, 0);
|
||||
node.set_data(Box::new(CharDeviceWrapper::new(dev)));
|
||||
|
||||
DEVFS_ROOT.get().attach(node);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -6,6 +6,8 @@ use crate::mem::{
|
||||
};
|
||||
use memfs::BlockAllocator;
|
||||
|
||||
pub mod devfs;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MemfsBlockAlloc;
|
||||
|
||||
|
||||
+19
-3
@@ -63,12 +63,13 @@ pub unsafe fn enter(initrd: Option<(usize, usize)>) -> ! {
|
||||
|
||||
spawn!(fn (initrd_ptr: usize) {
|
||||
use memfs::Ramfs;
|
||||
use vfs::{Filesystem, Ioctx};
|
||||
use crate::fs::MemfsBlockAlloc;
|
||||
use vfs::{Filesystem, Ioctx, FileMode};
|
||||
use crate::fs::{MemfsBlockAlloc, devfs};
|
||||
debugln!("Running kernel init process");
|
||||
|
||||
let initrd = unsafe { *(initrd_ptr as *const Option<(usize, usize)>) };
|
||||
if let Some((start, end)) = initrd {
|
||||
let proc = Process::current();
|
||||
let size = end - start;
|
||||
let start = mem::virtualize(start);
|
||||
|
||||
@@ -78,12 +79,27 @@ pub unsafe fn enter(initrd: Option<(usize, usize)>) -> ! {
|
||||
};
|
||||
infoln!("Done constructing ramfs");
|
||||
let root = fs.root().unwrap();
|
||||
let devfs_root = devfs::root();
|
||||
|
||||
let dir = root.mkdir("dev", FileMode::default_dir()).unwrap();
|
||||
dir.mount(devfs_root.clone()).unwrap();
|
||||
|
||||
let ioctx = Ioctx::new(root);
|
||||
|
||||
// Open a test file
|
||||
let node = ioctx.find(None, "/init").unwrap();
|
||||
let node = ioctx.find(None, "/init", true).unwrap();
|
||||
let mut file = node.open().unwrap();
|
||||
|
||||
proc.set_ioctx(ioctx);
|
||||
|
||||
// Open stdout
|
||||
{
|
||||
let mut io = proc.io.lock();
|
||||
let node = io.ioctx.as_ref().unwrap().find(None, "/dev/uart0", true).unwrap();
|
||||
// TODO fd cloning?
|
||||
io.files.push(node.open().unwrap());
|
||||
}
|
||||
|
||||
Process::execve(|space| elf::load_elf(space, &mut file), 0).unwrap();
|
||||
} else {
|
||||
infoln!("No initrd, exiting!");
|
||||
|
||||
@@ -6,11 +6,13 @@ use crate::mem::{
|
||||
};
|
||||
use crate::proc::{PROCESSES, SCHED};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::{vec::Vec, rc::Rc};
|
||||
use core::cell::UnsafeCell;
|
||||
use core::fmt;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use error::Errno;
|
||||
use vfs::Ioctx;
|
||||
use vfs::File;
|
||||
|
||||
pub use crate::arch::platform::context::{self, Context};
|
||||
|
||||
@@ -48,11 +50,21 @@ struct ProcessInner {
|
||||
exit: Option<ExitCode>,
|
||||
}
|
||||
|
||||
///
|
||||
pub struct ProcessIo {
|
||||
///
|
||||
pub ioctx: Option<Ioctx>,
|
||||
///
|
||||
pub files: Vec<File>,
|
||||
}
|
||||
|
||||
/// Structure describing an operating system process
|
||||
#[allow(dead_code)]
|
||||
pub struct Process {
|
||||
ctx: UnsafeCell<Context>,
|
||||
inner: IrqSafeSpinLock<ProcessInner>,
|
||||
///
|
||||
pub io: IrqSafeSpinLock<ProcessIo>,
|
||||
}
|
||||
|
||||
impl From<i32> for ExitCode {
|
||||
@@ -126,6 +138,11 @@ impl Process {
|
||||
const USTACK_VIRT_TOP: usize = 0x100000000;
|
||||
const USTACK_PAGES: usize = 4;
|
||||
|
||||
///
|
||||
pub fn set_ioctx(&self, ioctx: Ioctx) {
|
||||
self.io.lock().ioctx = Some(ioctx);
|
||||
}
|
||||
|
||||
/// Returns currently executing process
|
||||
pub fn current() -> ProcessRef {
|
||||
SCHED.current_process()
|
||||
@@ -206,6 +223,10 @@ impl Process {
|
||||
let id = Pid::new_kernel();
|
||||
let res = Rc::new(Self {
|
||||
ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)),
|
||||
io: IrqSafeSpinLock::new(ProcessIo {
|
||||
ioctx: None,
|
||||
files: Vec::new(),
|
||||
}),
|
||||
inner: IrqSafeSpinLock::new(ProcessInner {
|
||||
id,
|
||||
exit: None,
|
||||
|
||||
+11
-11
@@ -1,8 +1,8 @@
|
||||
use crate::arch::machine;
|
||||
use crate::proc::{self, sched::SCHED, Pid, Process, ProcessState};
|
||||
use crate::dev::timer::TimestampSource;
|
||||
use crate::proc::{self, sched::SCHED, Pid, Process};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use alloc::{vec::Vec, collections::{VecDeque, LinkedList}};
|
||||
use alloc::collections::LinkedList;
|
||||
use core::time::Duration;
|
||||
use error::Errno;
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct Wait {
|
||||
|
||||
pub struct Timeout {
|
||||
pid: Pid,
|
||||
deadline: Duration
|
||||
deadline: Duration,
|
||||
}
|
||||
|
||||
static TICK_LIST: IrqSafeSpinLock<LinkedList<Timeout>> = IrqSafeSpinLock::new(LinkedList::new());
|
||||
@@ -36,13 +36,13 @@ pub fn tick() {
|
||||
pub fn sleep(timeout: Duration) {
|
||||
// Dummy wait descriptor which will never receive notifications
|
||||
static SLEEP_NOTIFY: Wait = Wait::new();
|
||||
SLEEP_NOTIFY.sleep_on(Some(timeout));
|
||||
SLEEP_NOTIFY.sleep_on(Some(timeout)).ok();
|
||||
}
|
||||
|
||||
impl Wait {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
queue: IrqSafeSpinLock::new(LinkedList::new())
|
||||
queue: IrqSafeSpinLock::new(LinkedList::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,14 +74,14 @@ impl Wait {
|
||||
pub fn sleep_on(&self, timeout: Option<Duration>) -> Result<(), Errno> {
|
||||
let proc = Process::current();
|
||||
let deadline = timeout.map(|t| machine::local_timer().timestamp().unwrap() + t);
|
||||
let mut queue_lock = Some(self.queue.lock());
|
||||
let mut queue_lock = self.queue.lock();
|
||||
|
||||
queue_lock.as_mut().unwrap().push_back(proc.id());
|
||||
queue_lock.push_back(proc.id());
|
||||
proc.set_wait_flag(true);
|
||||
if let Some(deadline) = deadline {
|
||||
TICK_LIST.lock().push_back(Timeout {
|
||||
pid: proc.id(),
|
||||
deadline
|
||||
deadline,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -90,13 +90,13 @@ impl Wait {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
queue_lock = None;
|
||||
drop(queue_lock);
|
||||
proc.enter_wait();
|
||||
queue_lock = Some(self.queue.lock());
|
||||
queue_lock = self.queue.lock();
|
||||
|
||||
if let Some(deadline) = deadline {
|
||||
if machine::local_timer().timestamp()? > deadline {
|
||||
let mut cursor = queue_lock.as_mut().unwrap().cursor_front_mut();
|
||||
let mut cursor = queue_lock.cursor_front_mut();
|
||||
|
||||
while let Some(&mut item) = cursor.current() {
|
||||
if proc.id() == item {
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::arch::platform::{irq_mask_save, irq_restore};
|
||||
use core::cell::UnsafeCell;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::fmt;
|
||||
|
||||
/// Lock structure ensuring IRQs are disabled when inner value is accessed
|
||||
pub struct IrqSafeSpinLock<T> {
|
||||
@@ -70,6 +71,12 @@ impl<T> DerefMut for IrqSafeSpinLockGuard<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for IrqSafeSpinLockGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", &*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for IrqSafeSpinLockGuard<'_, T> {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) {
|
||||
|
||||
+77
-2
@@ -13,7 +13,7 @@ fn translate(virt: usize) -> Option<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_user_ptr(base: usize, len: usize) -> Result<(), Errno> {
|
||||
fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> {
|
||||
if base > mem::KERNEL_OFFSET || base + len > mem::KERNEL_OFFSET {
|
||||
warnln!(
|
||||
"User region refers to kernel memory: base={:#x}, len={:#x}",
|
||||
@@ -35,7 +35,48 @@ fn validate_user_ptr(base: usize, len: usize) -> Result<(), Errno> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(unsafe { core::slice::from_raw_parts_mut(base as *mut u8, len) })
|
||||
}
|
||||
|
||||
fn validate_user_str<'a>(base: usize, limit: usize) -> Result<&'a str, Errno> {
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
||||
@@ -68,6 +109,40 @@ pub unsafe fn syscall(num: usize, args: &[usize]) -> Result<usize, Errno> {
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
// sys_open
|
||||
2 => {
|
||||
use crate::proc::Process;
|
||||
let path = validate_user_str(args[0], 256)?;
|
||||
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
let node = io.ioctx.as_ref().unwrap().find(None, path, true)?;
|
||||
// TODO check access
|
||||
io.files.push(node.open()?);
|
||||
|
||||
Ok(io.files.len() - 1)
|
||||
}
|
||||
// sys_read
|
||||
3 => {
|
||||
use crate::proc::Process;
|
||||
use libcommon::Read;
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
let buf = validate_user_ptr(args[1], args[2])?;
|
||||
|
||||
io.files[args[0]].read(buf)
|
||||
}
|
||||
// sys_write
|
||||
4 => {
|
||||
use crate::proc::Process;
|
||||
use libcommon::Write;
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
let buf = validate_user_ptr(args[1], args[2])?;
|
||||
|
||||
io.files[args[0]].write(buf)
|
||||
}
|
||||
_ => panic!("Undefined system call: {}", num),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
use crate::sys;
|
||||
use core::fmt;
|
||||
|
||||
const STDOUT_FILENO: i32 = 0;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($args:tt)+) => ($crate::io::_print(format_args!($($args)+)))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($($args:tt)+) => (print!("{}\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(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(STDOUT_FILENO, &BUFFER as *const _, writer.pos);
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -5,7 +5,8 @@ use core::panic::PanicInfo;
|
||||
|
||||
pub mod mem;
|
||||
pub mod os;
|
||||
mod sys;
|
||||
pub mod io;
|
||||
pub mod sys;
|
||||
|
||||
#[link_section = ".text._start"]
|
||||
#[no_mangle]
|
||||
@@ -22,5 +23,7 @@ extern "C" fn _start(_arg: usize) -> ! {
|
||||
fn panic_handler(pi: &PanicInfo) -> ! {
|
||||
// TODO formatted messages
|
||||
trace!("Panic ocurred: {}", pi);
|
||||
sys::sys_exit(-1);
|
||||
unsafe {
|
||||
sys::sys_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -29,5 +29,7 @@ pub fn _trace(args: fmt::Arguments) {
|
||||
pos: 0,
|
||||
};
|
||||
writer.write_fmt(args).ok();
|
||||
sys::sys_ex_debug_trace(unsafe { &BUFFER } as *const _, writer.pos);
|
||||
unsafe {
|
||||
sys::sys_ex_debug_trace(&BUFFER as *const _, writer.pos);
|
||||
}
|
||||
}
|
||||
|
||||
+30
-8
@@ -1,27 +1,49 @@
|
||||
macro_rules! syscall {
|
||||
($num:expr, $a0:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
unsafe {
|
||||
asm!("svc #0", inout("x0") res, in("x8") $num, options(nostack));
|
||||
}
|
||||
asm!("svc #0",
|
||||
inout("x0") res,
|
||||
in("x8") $num, options(nostack));
|
||||
res
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
unsafe {
|
||||
asm!("svc #0", inout("x0") res, in("x1") $a1, in("x8") $num, options(nostack));
|
||||
}
|
||||
asm!("svc #0",
|
||||
inout("x0") res, in("x1") $a1,
|
||||
in("x8") $num, options(nostack));
|
||||
res
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
asm!("svc #0",
|
||||
inout("x0") res, in("x1") $a1, in("x2") $a2,
|
||||
in("x8") $num, options(nostack));
|
||||
res
|
||||
}};
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_exit(status: i32) -> ! {
|
||||
pub unsafe fn sys_exit(status: i32) -> ! {
|
||||
syscall!(1, status as usize);
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_ex_debug_trace(msg: *const u8, len: usize) -> usize {
|
||||
pub unsafe fn sys_ex_debug_trace(msg: *const u8, len: usize) -> usize {
|
||||
syscall!(120, msg as usize, len)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_open(pathname: *const u8, mode: u32, flags: u32) -> i32 {
|
||||
syscall!(2, pathname as usize, mode as usize, flags as usize) as i32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_read(fd: i32, data: *mut u8, len: usize) -> isize {
|
||||
syscall!(3, fd as usize, data as usize, len) as isize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn sys_write(fd: i32, data: *const u8, len: usize) -> isize {
|
||||
syscall!(4, fd as usize, data as usize, len) as isize
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user