diff --git a/Makefile b/Makefile index 11eec56..3309c5b 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/error/src/lib.rs b/error/src/lib.rs index 0d65793..0792c9e 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -11,4 +11,6 @@ pub enum Errno { AlreadyExists, NotImplemented, TimedOut, + EndOfFile, + Interrupt } diff --git a/fs/memfs/src/dir.rs b/fs/memfs/src/dir.rs index 8756735..4bb4605 100644 --- a/fs/memfs/src/dir.rs +++ b/fs/memfs/src/dir.rs @@ -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 { - 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 { - panic!() + Err(Errno::DoesNotExist) } fn remove(&mut self, _parent: VnodeRef, _name: &str) -> Result<(), Errno> { diff --git a/fs/memfs/src/lib.rs b/fs/memfs/src/lib.rs index 4905c4b..3f3388d 100644 --- a/fs/memfs/src/lib.rs +++ b/fs/memfs/src/lib.rs @@ -64,6 +64,8 @@ impl Ramfs { 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]; diff --git a/fs/vfs/src/char.rs b/fs/vfs/src/char.rs new file mode 100644 index 0000000..09fd7ff --- /dev/null +++ b/fs/vfs/src/char.rs @@ -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; + /// 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; +} + +/// 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 { + panic!(); + } + + fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> { + panic!(); + } + + fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result { + panic!(); + } + + fn open(&mut self, _node: VnodeRef) -> Result { + Ok(0) + } + + fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { + Ok(()) + } + + fn read(&mut self, _node: VnodeRef, _pos: usize, data: &mut [u8]) -> Result { + self.device.read(true, data) + } + + fn write(&mut self, _node: VnodeRef, _pos: usize, data: &[u8]) -> Result { + self.device.write(true, data) + } + + fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { + panic!(); + } + + fn size(&mut self, _node: VnodeRef) -> Result { + 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 } + } +} diff --git a/fs/vfs/src/file.rs b/fs/vfs/src/file.rs index 6531e1a..8ddb929 100644 --- a/fs/vfs/src/file.rs +++ b/fs/vfs/src/file.rs @@ -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 { + #[cfg(test)] + println!("read {}", pos); let len = 123; if pos >= len { return Ok(0); diff --git a/fs/vfs/src/ioctx.rs b/fs/vfs/src/ioctx.rs index f80a61d..f3d4e97 100644 --- a/fs/vfs/src/ioctx.rs +++ b/fs/vfs/src/ioctx.rs @@ -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 { + fn _find(&self, mut at: VnodeRef, path: &str, follow: bool) -> Result { 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, mut path: &str) -> Result { + pub fn find( + &self, + at: Option, + mut path: &str, + follow: bool, + ) -> Result { 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 { 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())); + } } diff --git a/fs/vfs/src/lib.rs b/fs/vfs/src/lib.rs index 0881fdd..1afae46 100644 --- a/fs/vfs/src/lib.rs +++ b/fs/vfs/src/lib.rs @@ -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}; diff --git a/fs/vfs/src/node.rs b/fs/vfs/src/node.rs index b2ac043..695c726 100644 --- a/fs/vfs/src/node.rs +++ b/fs/vfs/src/node.rs @@ -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>, fs: RefCell>>, data: RefCell>>, } @@ -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) { *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 { + self.target.borrow().clone() + } + /// Looks up a child `name` in in-memory tree cache pub fn lookup(self: &VnodeRef, name: &str) -> Option { 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 { - 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 { - 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 { - if self.kind != VnodeKind::Regular { + if self.kind == VnodeKind::Directory { return Err(Errno::IsADirectory); } diff --git a/init/src/main.rs b/init/src/main.rs index 4226c75..3b2e91f 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -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 } diff --git a/kernel/src/arch/aarch64/boot/mod.rs b/kernel/src/arch/aarch64/boot/mod.rs index a595818..58698e4 100644 --- a/kernel/src/arch/aarch64/boot/mod.rs +++ b/kernel/src/arch/aarch64/boot/mod.rs @@ -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; diff --git a/kernel/src/arch/aarch64/context.S b/kernel/src/arch/aarch64/context.S index e7da456..1a3f16e 100644 --- a/kernel/src/arch/aarch64/context.S +++ b/kernel/src/arch/aarch64/context.S @@ -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] diff --git a/kernel/src/arch/aarch64/context.rs b/kernel/src/arch/aarch64/context.rs index c62c559..d396314 100644 --- a/kernel/src/arch/aarch64/context.rs +++ b/kernel/src/arch/aarch64/context.rs @@ -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 diff --git a/kernel/src/arch/aarch64/exception.rs b/kernel/src/arch/aarch64/exception.rs index 29d16d8..e52cd90 100644 --- a/kernel/src/arch/aarch64/exception.rs +++ b/kernel/src/arch/aarch64/exception.rs @@ -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; diff --git a/kernel/src/arch/aarch64/mach_qemu/mod.rs b/kernel/src/arch/aarch64/mach_qemu/mod.rs index 39de913..be8039f 100644 --- a/kernel/src/arch/aarch64/mach_qemu/mod.rs +++ b/kernel/src/arch/aarch64/mach_qemu/mod.rs @@ -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()?; diff --git a/kernel/src/arch/aarch64/vectors.S b/kernel/src/arch/aarch64/vectors.S index 29d2ff9..922adc8 100644 --- a/kernel/src/arch/aarch64/vectors.S +++ b/kernel/src/arch/aarch64/vectors.S @@ -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] diff --git a/kernel/src/dev/mod.rs b/kernel/src/dev/mod.rs index 79f2102..fda658b 100644 --- a/kernel/src/dev/mod.rs +++ b/kernel/src/dev/mod.rs @@ -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 { diff --git a/kernel/src/dev/serial/pl011.rs b/kernel/src/dev/serial/pl011.rs index 5d13d6e..4f529ae 100644 --- a/kernel/src/dev/serial/pl011.rs +++ b/kernel/src/dev/serial/pl011.rs @@ -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>, + 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 { + assert!(blocking); + self.ring.line_read(data, self) + } + + fn write(&self, blocking: bool, data: &[u8]) -> Result { + 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, } diff --git a/kernel/src/dev/timer.rs b/kernel/src/dev/timer.rs index a946442..4494fda 100644 --- a/kernel/src/dev/timer.rs +++ b/kernel/src/dev/timer.rs @@ -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; } - -/// -pub struct Sleep { - deadline: Duration, - pid: Pid -} diff --git a/kernel/src/dev/tty.rs b/kernel/src/dev/tty.rs new file mode 100644 index 0000000..db0ddcb --- /dev/null +++ b/kernel/src/dev/tty.rs @@ -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 { + rd: usize, + wr: usize, + data: [u8; N], + flags: u8 +} + +/// +pub struct CharRing { + wait_read: Wait, + wait_write: Wait, + inner: IrqSafeSpinLock>, +} + +#[allow(missing_docs)] +impl CharRingInner { + #[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 CharRing { + 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 { + 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(&self, data: &mut [u8], dev: &T) -> Result { + 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) + } +} diff --git a/kernel/src/fs/devfs.rs b/kernel/src/fs/devfs.rs new file mode 100644 index 0000000..a938891 --- /dev/null +++ b/kernel/src/fs/devfs.rs @@ -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 = 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(()) +} diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index d336cf1..193bf02 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -6,6 +6,8 @@ use crate::mem::{ }; use memfs::BlockAllocator; +pub mod devfs; + #[derive(Clone, Copy)] pub struct MemfsBlockAlloc; diff --git a/kernel/src/proc/mod.rs b/kernel/src/proc/mod.rs index c998390..0fe6641 100644 --- a/kernel/src/proc/mod.rs +++ b/kernel/src/proc/mod.rs @@ -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!"); diff --git a/kernel/src/proc/process.rs b/kernel/src/proc/process.rs index 32fce41..c2230a2 100644 --- a/kernel/src/proc/process.rs +++ b/kernel/src/proc/process.rs @@ -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, } +/// +pub struct ProcessIo { + /// + pub ioctx: Option, + /// + pub files: Vec, +} + /// Structure describing an operating system process #[allow(dead_code)] pub struct Process { ctx: UnsafeCell, inner: IrqSafeSpinLock, + /// + pub io: IrqSafeSpinLock, } impl From 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, diff --git a/kernel/src/proc/wait.rs b/kernel/src/proc/wait.rs index c7cccaf..416f733 100644 --- a/kernel/src/proc/wait.rs +++ b/kernel/src/proc/wait.rs @@ -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> = 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) -> 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 { diff --git a/kernel/src/sync.rs b/kernel/src/sync.rs index 49ae64d..6102a37 100644 --- a/kernel/src/sync.rs +++ b/kernel/src/sync.rs @@ -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 { @@ -70,6 +71,12 @@ impl DerefMut for IrqSafeSpinLockGuard<'_, T> { } } +impl fmt::Debug for IrqSafeSpinLockGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", &*self) + } +} + impl Drop for IrqSafeSpinLockGuard<'_, T> { #[inline(always)] fn drop(&mut self) { diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index 450d6d6..1a57a33 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -13,7 +13,7 @@ fn translate(virt: usize) -> Option { } } -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 { @@ -68,6 +109,40 @@ pub unsafe fn syscall(num: usize, args: &[usize]) -> Result { 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), } } diff --git a/libusr/src/io.rs b/libusr/src/io.rs new file mode 100644 index 0000000..a888811 --- /dev/null +++ b/libusr/src/io.rs @@ -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); + } +} diff --git a/libusr/src/lib.rs b/libusr/src/lib.rs index 5e9648e..463ae5d 100644 --- a/libusr/src/lib.rs +++ b/libusr/src/lib.rs @@ -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); + } } diff --git a/libusr/src/os.rs b/libusr/src/os.rs index afbc0ca..ac8e5f6 100644 --- a/libusr/src/os.rs +++ b/libusr/src/os.rs @@ -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); + } } diff --git a/libusr/src/sys.rs b/libusr/src/sys.rs index ba68e54..4ebdc24 100644 --- a/libusr/src/sys.rs +++ b/libusr/src/sys.rs @@ -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 +}