feat: proof-of-concept tty chardev

This commit is contained in:
2021-11-02 15:36:34 +02:00
parent 1feec06ad0
commit 753841e1a0
31 changed files with 661 additions and 93 deletions
+2 -1
View File
@@ -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 \
+2
View File
@@ -11,4 +11,6 @@ pub enum Errno {
AlreadyExists,
NotImplemented,
TimedOut,
EndOfFile,
Interrupt
}
+11 -5
View File
@@ -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
View File
@@ -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];
+70
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+3
View File
@@ -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;
+5 -3
View File
@@ -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]
+6 -4
View File
@@ -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
+4 -1
View File
@@ -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;
+2
View File
@@ -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()?;
+2 -2
View File
@@ -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]
+2
View File
@@ -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 {
+23 -2
View File
@@ -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,
}
-7
View File
@@ -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
}
+133
View File
@@ -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)
}
}
+24
View File
@@ -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(())
}
+2
View File
@@ -6,6 +6,8 @@ use crate::mem::{
};
use memfs::BlockAllocator;
pub mod devfs;
#[derive(Clone, Copy)]
pub struct MemfsBlockAlloc;
+19 -3
View File
@@ -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!");
+22 -1
View File
@@ -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
View File
@@ -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 {
+7
View File
@@ -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
View File
@@ -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),
}
}
+42
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}