Merge pull request 'VFS implementation' (#22) from theoddgarlic/ableos:vfs into master

Reviewed-on: https://git.ablecorp.us:443/able/ableos/pulls/22
master
able 2022-08-08 05:25:07 +00:00
commit 489bcc69c2
16 changed files with 1092 additions and 444 deletions

View File

@ -58,7 +58,7 @@ test-args = [
[dependencies] [dependencies]
lazy_static = { version = "1.4.0", features = ["spin_no_std"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
qrcode = { path = "../qrcode-rust" } qrcode = { path = "../qrcode-rust" }
bitflags = "1.2.1" bitflags = "1.3"
lliw = "0.2.0" lliw = "0.2.0"
spin = "0.9" spin = "0.9"
pretty-hex = "0.2.1" pretty-hex = "0.2.1"

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
*
* SPDX-License-Identifier: MPL-2.0
*/
#[derive(Copy, Clone, Debug)]
pub enum FsError {
EndOfFile,
InodeNotFound,
InvalidDevice,
InvalidPath,
IsDirectory,
NotAbsolute,
NotADirectory,
NotFound,
Recursion,
UnsupportedOperation,
}
impl Into<FsError> for ext2::error::Error {
fn into(self) -> FsError {
match self {
ext2::error::Error::Other(_) => todo!(),
ext2::error::Error::BadMagic { magic: _ } => todo!(),
ext2::error::Error::OutOfBounds { index: _ } => todo!(),
ext2::error::Error::AddressOutOfBounds {
sector: _,
offset: _,
size: _,
} => todo!(),
ext2::error::Error::BadBlockGroupCount {
by_blocks: _,
by_inodes: _,
} => todo!(),
ext2::error::Error::InodeNotFound { inode: _ } => FsError::InodeNotFound,
ext2::error::Error::NotADirectory { inode: _, name: _ } => FsError::NotADirectory,
ext2::error::Error::NotAbsolute { name: _ } => todo!(),
ext2::error::Error::NotFound { name: _ } => FsError::NotFound,
}
}
}

View File

@ -0,0 +1,177 @@
/*
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
*
* SPDX-License-Identifier: MPL-2.0
*/
use alloc::sync::{Arc, Weak};
use ext2::fs::{sync::Synced, Ext2};
use ext2::sector::SectorSize;
use ext2::sys::inode::TypePerm;
use ext2::volume::Volume;
use crate::handle::{Handle, HandleResource};
use super::errors::FsError;
use super::vfs::{DirectoryEntry, FsFlags, VirtualFileSystem, VFS};
use super::{FsNode, FsResult as Result, StorageDevice};
pub struct Ext2StorageDevice<S, V>
where
S: SectorSize,
V: Volume<u8, S>,
{
fs: Synced<Ext2<S, V>>,
device_handle: Handle,
root_handle: Handle,
}
impl<S, V> Ext2StorageDevice<S, V>
where
S: SectorSize,
V: Volume<u8, S>,
{
pub fn new(volume: V) -> Result<Self> {
let fs = Synced::new(volume).map_err(|e| e.into())?;
let root_inode = fs.root_inode();
let device_handle = Handle::new(HandleResource::StorageDevice);
let root = Arc::new(FsNode::new(
FsFlags::DIRECTORY,
root_inode.size(),
2,
device_handle,
Weak::new(),
));
let mut vfs = VFS.lock();
let root_handle = vfs.add_fs_node(root.clone());
Ok(Self {
fs,
device_handle,
root_handle,
})
}
}
impl<S, V> StorageDevice for Ext2StorageDevice<S, V>
where
S: SectorSize + Send,
V: Volume<u8, S> + Send,
{
fn read(&self, node: &FsNode, offset: usize, size: usize, buffer: &mut Vec<u8>) -> Result<()> {
let inode = self
.fs
.inode_nth(node.inode() as usize)
.ok_or_else(|| FsError::InodeNotFound)?;
// FIXME: I don't really know how Ext2 works, so for now we don't
// support non-zero offsets and buffer sizes that don't match
// the file length. We always read the whole file.
if offset > 0 || size != inode.size() {
Err(FsError::UnsupportedOperation)?;
}
inode.read_to_end(buffer).map_err(|e| e.into())?;
Ok(())
}
fn write(&self, _node: &FsNode, _offset: usize, _buffer: &[u8]) -> Result<()> {
todo!()
}
fn read_dir(&self, node: &FsNode, index: usize) -> Result<DirectoryEntry> {
let inode = self
.fs
.inode_nth(node.inode() as usize)
.ok_or_else(|| FsError::InodeNotFound)?;
let mut dir = inode.directory().ok_or_else(|| FsError::NotADirectory)?;
let entry = dir
.nth(index)
.ok_or_else(|| FsError::InodeNotFound)?
.map_err(|e| e.into())?;
let entry_inode = self
.fs
.inode_nth(entry.inode)
.ok_or_else(|| FsError::InodeNotFound)?;
let mut vfs = VFS.lock();
let entry_node_handle = vfs
.find_fs_node(entry.inode, self.device_handle)
.unwrap_or_else(|| {
vfs.add_fs_node(Arc::new(FsNode::new(
ext2_type_to_fs_flags(entry_inode.type_perm()),
entry_inode.size(),
entry.inode,
self.device_handle,
Weak::new(),
)))
});
Ok(DirectoryEntry::new(
entry.file_name_string(),
entry_node_handle,
))
}
fn find_dir(&self, vfs: &mut VirtualFileSystem, node: &FsNode, name: &str) -> Result<Handle> {
let inode = self
.fs
.inode_nth(node.inode() as usize)
.ok_or_else(|| FsError::InodeNotFound)?;
let dir = inode.directory().ok_or_else(|| FsError::NotADirectory)?;
let mut found_node = Err(FsError::NotFound);
for entry in dir {
if entry.is_err() {
continue;
}
let entry = entry.unwrap();
let entry_inode = self
.fs
.inode_nth(entry.inode as usize)
.ok_or_else(|| FsError::InodeNotFound)?;
if entry.file_name_string() == name {
found_node = Ok(vfs
.find_fs_node(entry.inode, self.device_handle)
.unwrap_or_else(|| {
vfs.add_fs_node(Arc::new(FsNode::new(
ext2_type_to_fs_flags(entry_inode.type_perm()),
entry_inode.size(),
entry.inode,
self.device_handle,
Weak::new(),
)))
}));
break;
}
}
found_node
}
fn root(&self) -> Handle {
self.root_handle
}
fn device_handle(&self) -> Handle {
self.device_handle
}
}
fn ext2_type_to_fs_flags(type_perm: TypePerm) -> FsFlags {
trace!("{type_perm:?}");
let is_directory = type_perm & TypePerm::DIRECTORY == TypePerm::DIRECTORY;
let is_symlink = type_perm & TypePerm::SYMLINK == TypePerm::SYMLINK;
let t = if is_directory {
FsFlags::DIRECTORY
} else {
FsFlags::FILE
};
let s = if is_symlink {
FsFlags::SYMBOLIC_LINK
} else {
FsFlags::empty()
};
t | s
}

View File

@ -1,42 +1,90 @@
use ext2::{ /*
fs::{ * Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
sync::{Inode, Synced}, *
Ext2, * SPDX-License-Identifier: MPL-2.0
}, */
sector::{SectorSize, Size1024},
volume::Volume, pub mod errors;
pub mod ext2;
pub mod vfs;
use ::ext2::sector::Size1024;
use alloc::sync::Arc;
use crate::{filesystem::vfs::VFS, handle::Handle, KERNEL_STATE};
use self::{
errors::FsError,
ext2::Ext2StorageDevice,
vfs::{DirectoryEntry, FsNode, VirtualFileSystem},
}; };
use spin::Lazy; use FsResult as Result;
pub static FILE_SYSTEM: Lazy<spin::Mutex<Synced<Ext2<Size1024, Vec<u8>>>>> = pub type FsResult<T> = core::result::Result<T, FsError>;
Lazy::new(|| spin::Mutex::new(load_fs()));
pub fn walk<S: SectorSize, V: Volume<u8, S>>( /// The methods on this trait are to be used internally.
fs: &Synced<Ext2<S, V>>, pub trait StorageDevice
inode: Inode<S, V>, where
name: String, Self: Send,
) { {
if let Some(dir) = inode.directory() { fn read(&self, node: &FsNode, offset: usize, size: usize, buffer: &mut Vec<u8>) -> Result<()>;
fn write(&self, node: &FsNode, offset: usize, buffer: &[u8]) -> Result<()>;
fn read_dir(&self, node: &FsNode, index: usize) -> Result<DirectoryEntry>;
fn find_dir(&self, vfs: &mut VirtualFileSystem, node: &FsNode, name: &str) -> Result<Handle>;
// TODO: flush to disk
fn root(&self) -> Handle;
fn device_handle(&self) -> Handle;
}
pub fn init() -> Result<()> {
let mut state = KERNEL_STATE.lock();
let fs = load_fs()?;
let mut vfs = VFS.lock();
vfs.set_root(fs.root())?;
state.add_storage_device(fs);
Ok(())
}
fn load_fs() -> Result<Ext2StorageDevice<Size1024, Vec<u8>>> {
let mut volume = Vec::new();
volume.extend_from_slice(include_bytes!("../../../userland/root_fs/ext2.img"));
Ext2StorageDevice::new(volume)
}
pub fn tree(path: &str) -> Result<()> {
let dir = {
let mut vfs = VFS.lock();
let handle = vfs.resolve(path)?;
vfs.fs_node(handle).ok_or_else(|| FsError::NotFound)?
};
tree_inner(
dir,
if path.starts_with('/') {
&path[1..]
} else {
path
},
);
Ok(())
}
fn tree_inner<S: Into<String>>(dir: Arc<FsNode>, path: S) {
let path = path.into();
if let Some(dir) = dir.directory() {
for entry in dir { for entry in dir {
assert!(entry.is_ok()); let fs_node = {
let entry = entry.unwrap(); let vfs = VFS.lock();
let entry_name = String::from_utf8_lossy(&entry.name); vfs.fs_node(entry.node()).unwrap()
};
println!("{}/{} => {}", name, entry_name, entry.inode,); println!("{}/{} => {}", path, entry.name(), fs_node.inode());
if entry_name != "." && entry_name != ".." { trace!("{entry:#?}");
walk( if entry.name() != "." && entry.name() != ".." {
fs, tree_inner(fs_node, format!("{}/{}", path, entry.name()));
fs.inode_nth(entry.inode).unwrap(),
format!("{}/{}", name, entry_name),
);
} }
} }
} }
} }
fn load_fs() -> Synced<Ext2<Size1024, Vec<u8>>> {
let mut volume = Vec::new();
volume.extend_from_slice(include_bytes!("../../../userland/root_fs/ext2.img"));
Synced::<Ext2<Size1024, _>>::new(volume).unwrap()
}

View File

@ -0,0 +1,305 @@
/*
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
*
* SPDX-License-Identifier: MPL-2.0
*/
use core::cmp;
use alloc::sync::{Arc, Weak};
use hashbrown::HashMap;
use lazy_static::lazy_static;
use spin::Mutex;
use super::{errors::FsError, FsResult as Result};
use crate::{
handle::{Handle, HandleResource},
KERNEL_STATE,
};
/// The limit for symlink recursion. In POSIX, this is at least 8. In Linux, this is 40.
const SYMLINK_RECURSION_LIMIT: u8 = 8;
lazy_static! {
pub static ref VFS: Mutex<VirtualFileSystem> = Default::default();
}
pub struct VirtualFileSystem {
fs_nodes: HashMap<Handle, Arc<FsNode>>,
root_node: Weak<FsNode>,
root_handle: Option<Handle>,
}
impl VirtualFileSystem {
/// Sets the VFS root to the given VFS node handle.
pub fn set_root(&mut self, handle: Handle) -> Result<()> {
let root_node = self.fs_node(handle).ok_or_else(|| FsError::NotFound)?;
self.root_node = Arc::downgrade(&root_node);
self.root_handle = Some(handle);
Ok(())
}
/// Resolves the path to a handle. If the resulting node is a symlink,
/// the symlink itself is returned. All symlinks but the resulting node
/// are traversed.
///
/// Requires a mutable reference because internally it might open new VFS
/// nodes while resolving the path.
pub fn resolve<S: AsRef<str>>(&mut self, path: S) -> Result<Handle> {
// TODO: caching
let path = path.as_ref();
if !path.starts_with('/') {
// FIXME: use current process working directory for relative paths?
Err(FsError::NotAbsolute)?;
}
let mut components = path.split_terminator('/');
components.next(); // throw the empty string caused by the root /
// will be initialised beforehand so okay to unwrap
let mut resolved_node = self.root_handle.unwrap();
// let mut symlink_recursion_level = 0;
for component in components {
// if symlink_recursion_level >= SYMLINK_RECURSION_LIMIT {
// Err(FsError::Recursion)?;
// }
if component == "" {
Err(FsError::InvalidPath)?;
}
// checked by previous iteration so okay to unwrap
let parent = self.fs_node(resolved_node).unwrap();
// TODO: permission checks
// FIXME: find_dir checks that this is a directory already but
// that's just more boilerplate in StorageDevice impls
// we should probably check that here instead to reduce
// required boilerplate
// if !parent.is_dir() {
// Err(FsError::NotADirectory)?;
// }
// FIXME: handle mount points
// FIXME: handle symlinks
resolved_node = parent.find_dir(self, component)?;
}
Ok(resolved_node)
}
pub fn add_fs_node(&mut self, fs_node: Arc<FsNode>) -> Handle {
let handle = Handle::new(HandleResource::FsNode);
self.fs_nodes.insert(handle, fs_node);
handle
}
pub fn find_fs_node(&mut self, inode: usize, device_handle: Handle) -> Option<Handle> {
self.fs_nodes.iter().find_map(|(handle, fs_node)| {
if fs_node.inode == inode && fs_node.device_handle == device_handle {
Some(*handle)
} else {
None
}
})
}
pub fn fs_node(&self, handle: Handle) -> Option<Arc<FsNode>> {
self.fs_nodes.get(&handle).cloned()
}
pub fn root_node(&self) -> Arc<FsNode> {
// okay to unwrap since this should never be called before init
self.root_node.upgrade().unwrap()
}
}
impl Default for VirtualFileSystem {
fn default() -> Self {
Self {
fs_nodes: HashMap::new(),
root_node: Weak::new(),
root_handle: None,
}
}
}
/// A VFS node, that can either be a file or a directory.
///
/// VFS nodes are created whenever a file that doesn't have an open VFS node is
/// opened. When there are no open file descriptors to a file, the associated
/// VFS node is dropped.
#[derive(Debug)]
pub struct FsNode {
flags: FsFlags,
size: usize, // in bytes
inode: usize, // implementation specific identifier for the node
device_handle: Handle, // uniquely assigned device handle
ptr: Weak<FsNode>, // used by mountpoints and symlinks
// TODO: permissions mask
// TODO: owning user/group
}
impl FsNode {
pub fn new(
flags: FsFlags,
size: usize,
inode: usize,
device_handle: Handle,
ptr: Weak<FsNode>,
) -> Self {
Self {
flags,
size,
inode,
device_handle,
ptr,
}
}
/// This method opens a new file descriptor for this VFS node.
// TODO: make this take flags
pub fn open(self: Arc<Self>) -> Result<Handle> {
let mut state = KERNEL_STATE.lock();
let handle = state.open_file_descriptor(self);
Ok(handle)
}
/// This method reads from the VFS node without opening a new file
/// descriptor. This is intended to be used internally, if you're trying to
/// read a file then you probably want to read from a file descriptor.
pub fn read(&self, offset: usize, size: usize, buffer: &mut Vec<u8>) -> Result<usize> {
let state = KERNEL_STATE.lock();
let device = state
.storage_device(self.device_handle)
.ok_or_else(|| FsError::InvalidDevice)?;
if self.is_dir() {
Err(FsError::IsDirectory)?;
}
if offset > self.size {
Err(FsError::EndOfFile)?;
}
let readable_size = cmp::min(size, self.size - offset);
device.read(self, offset, readable_size, buffer)?;
Ok(readable_size)
}
/// This method writes to the VFS node without opening a new file
/// descriptor. This is intended to be used internally, if you're trying to
/// write to a file then you probably want to write to a file descriptor.
pub fn write(&self, offset: usize, buffer: &[u8]) -> Result<()> {
let state = KERNEL_STATE.lock();
let device = state
.storage_device(self.device_handle)
.ok_or_else(|| FsError::InvalidDevice)?;
device.write(self, offset, buffer)
}
pub fn read_dir(&self, index: usize) -> Result<DirectoryEntry> {
let state = KERNEL_STATE.lock();
let device = state
.storage_device(self.device_handle)
.ok_or_else(|| FsError::InvalidDevice)?;
device.read_dir(self, index)
}
pub fn find_dir(&self, vfs: &mut VirtualFileSystem, name: &str) -> Result<Handle> {
let state = KERNEL_STATE.lock();
let device = state
.storage_device(self.device_handle)
.ok_or_else(|| FsError::InvalidDevice)?;
device.find_dir(vfs, self, name)
}
pub fn directory(self: Arc<Self>) -> Option<Directory> {
if self.is_dir() {
Some(Directory::new(self))
} else {
None
}
}
pub fn is_dir(&self) -> bool {
(self.flags & FsFlags::DIRECTORY) == FsFlags::DIRECTORY
}
pub fn inode(&self) -> usize {
self.inode
}
pub fn size(&self) -> usize {
self.size
}
}
impl Drop for FsNode {
fn drop(&mut self) {
trace!("dropping VFS node: {self:#?}");
// TODO: flush to disk here
}
}
bitflags! {
/// Flags associated with VFS nodes and file descriptors
///
/// 0x00000MST \
/// T is set to 0 for files, 1 for directories \
/// S is set when the file is a symbolic link \
/// M is set if the file is an active mount point
pub struct FsFlags: u8 {
const FILE = 0b00000000;
const DIRECTORY = 0b00000001;
const SYMBOLIC_LINK = 0b00000010;
const MOUNT_POINT = 0b00000100;
}
}
pub struct Directory {
node: Arc<FsNode>,
index: usize,
}
impl Directory {
fn new(node: Arc<FsNode>) -> Self {
Self { node, index: 0 }
}
}
impl Iterator for Directory {
type Item = DirectoryEntry;
fn next(&mut self) -> Option<Self::Item> {
let value = self.node.read_dir(self.index).ok();
self.index += 1;
value
}
}
#[derive(Clone, Debug)]
pub struct DirectoryEntry {
name: String,
node: Handle,
}
impl DirectoryEntry {
pub(super) fn new(name: String, node: Handle) -> Self {
Self { name, node }
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn node(&self) -> Handle {
self.node
}
}

View File

@ -1,3 +1,9 @@
/*
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
*
* SPDX-License-Identifier: MPL-2.0
*/
//! A handle is a u128 with a set of permissions //! A handle is a u128 with a set of permissions
//! and a resource connected to it //! and a resource connected to it
@ -14,8 +20,10 @@ pub struct BinaryData {
pub enum HandleResource { pub enum HandleResource {
Channel, Channel,
Socket, Socket,
BinaryData, BinaryData,
StorageDevice,
FileDescriptor,
FsNode,
} }
#[derive(Debug, Eq, Hash, PartialEq, Clone, Copy)] #[derive(Debug, Eq, Hash, PartialEq, Clone, Copy)]
@ -30,16 +38,19 @@ impl Display for Handle {
match &self.res { match &self.res {
HandleResource::Channel => write!(f, "-Channel")?, HandleResource::Channel => write!(f, "-Channel")?,
HandleResource::BinaryData => write!(f, "-Binary")?, HandleResource::BinaryData => write!(f, "-Binary")?,
Socket => write!(f, "-Socket")?, HandleResource::Socket => write!(f, "-Socket")?,
HandleResource::StorageDevice => write!(f, "-StorageDevice")?,
HandleResource::FileDescriptor => write!(f, "-FileDescriptor")?,
HandleResource::FsNode => write!(f, "-FsNode")?,
} }
Ok(()) Ok(())
} }
} }
use crate::handle::HandleResource::*;
impl Handle { impl Handle {
pub fn new(htype: HandleResource) -> Self { pub fn new(htype: HandleResource) -> Self {
// FIXME: check if inner collides
Self { Self {
inner: generate_process_pass(), inner: generate_process_pass(),
res: htype, res: htype,

View File

@ -1,10 +1,26 @@
/*
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
*
* SPDX-License-Identifier: MPL-2.0
*/
use alloc::sync::Arc;
use hashbrown::HashMap;
use spin::Lazy; use spin::Lazy;
use crate::{
filesystem::{vfs::FsNode, StorageDevice},
handle::{Handle, HandleResource},
};
pub static KERNEL_STATE: Lazy<spin::Mutex<KernelInternalState>> = pub static KERNEL_STATE: Lazy<spin::Mutex<KernelInternalState>> =
Lazy::new(|| spin::Mutex::new(KernelInternalState::new())); Lazy::new(|| spin::Mutex::new(KernelInternalState::new()));
pub struct KernelInternalState { pub struct KernelInternalState {
pub hostname: String, pub hostname: String,
pub storage_devices: HashMap<Handle, Box<dyn StorageDevice>>,
// FIXME: should this be per-process?
file_table: HashMap<Handle, FileTableEntry>,
should_shutdown: bool, should_shutdown: bool,
} }
@ -12,6 +28,8 @@ impl KernelInternalState {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
should_shutdown: false, should_shutdown: false,
storage_devices: HashMap::new(),
file_table: HashMap::new(),
hostname: "".to_string(), hostname: "".to_string(),
} }
} }
@ -19,9 +37,35 @@ impl KernelInternalState {
pub fn set_hostname(&mut self, hostname: String) { pub fn set_hostname(&mut self, hostname: String) {
self.hostname = hostname; self.hostname = hostname;
} }
pub fn add_storage_device(&mut self, device: impl StorageDevice + Send + 'static) {
let device = Box::new(device);
self.storage_devices.insert(device.device_handle(), device);
}
pub fn storage_device(&self, handle: Handle) -> Option<&dyn StorageDevice> {
self.storage_devices.get(&handle).map(|d| &**d)
}
// TODO: implement flags here
pub fn open_file_descriptor(&mut self, fs_node: Arc<FsNode>) -> Handle {
let handle = Handle::new(HandleResource::FileDescriptor);
self.file_table.insert(handle, FileTableEntry::new(fs_node));
handle
}
pub fn file_descriptor(&self, handle: Handle) -> Option<&FileTableEntry> {
self.file_table.get(&handle)
}
pub fn close_file_descriptor(&mut self, handle: Handle) {
self.file_table.remove(&handle);
}
pub fn shutdown(&mut self) { pub fn shutdown(&mut self) {
self.should_shutdown = true; self.should_shutdown = true;
} }
pub fn update_state(&mut self) { pub fn update_state(&mut self) {
if self.should_shutdown { if self.should_shutdown {
crate::arch::shutdown(); crate::arch::shutdown();
@ -34,3 +78,18 @@ impl Default for KernelInternalState {
Self::new() Self::new()
} }
} }
pub struct FileTableEntry {
fs_node: Arc<FsNode>,
// TODO: permissions, flags, owner, maybe cache stuff here?
}
impl FileTableEntry {
fn new(fs_node: Arc<FsNode>) -> Self {
Self { fs_node }
}
pub fn fs_node(&self) -> Arc<FsNode> {
self.fs_node.clone()
}
}

View File

@ -1,13 +1,19 @@
/*
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
*
* SPDX-License-Identifier: MPL-2.0
*/
#![allow(clippy::empty_loop)] #![allow(clippy::empty_loop)]
use core::sync::atomic::AtomicU64; use core::sync::atomic::AtomicU64;
use crate::arch::{drivers::sysinfo::master, init, sloop}; use crate::arch::{drivers::sysinfo::master, init, sloop};
use crate::hardware;
use crate::relib::network::socket::{SimpleSock, Socket}; use crate::relib::network::socket::{SimpleSock, Socket};
use crate::{ use crate::{
boot_conf::KernelConfig, scratchpad, systeminfo::RELEASE_TYPE, TERM, boot_conf::KernelConfig, scratchpad, systeminfo::RELEASE_TYPE, TERM,
}; };
use crate::{filesystem, hardware};
use kernel::KERNEL_VERSION; use kernel::KERNEL_VERSION;
use spin::Lazy; use spin::Lazy;
@ -36,6 +42,9 @@ pub fn kernel_main() -> ! {
x86_64::instructions::interrupts::without_interrupts(|| { x86_64::instructions::interrupts::without_interrupts(|| {
hardware::init_mouse(); hardware::init_mouse();
}); });
filesystem::init().unwrap();
/* /*
// println!("abc"); // println!("abc");

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2022, Able <able@ablecorp.us> * Copyright (c) 2022, Able <able@ablecorp.us>
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
* *
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -25,6 +26,9 @@
#[macro_use] #[macro_use]
pub extern crate log; pub extern crate log;
#[macro_use]
pub extern crate bitflags;
pub extern crate alloc; pub extern crate alloc;
pub extern crate externc_libm as libm; pub extern crate externc_libm as libm;

View File

@ -5,57 +5,57 @@ use core::fmt::{Display, Error, Formatter};
#[repr(transparent)] #[repr(transparent)]
pub struct Kilosecond(u32); pub struct Kilosecond(u32);
impl Kilosecond { impl Kilosecond {
pub fn from_ms(ms: u32) -> Self { pub fn from_ms(ms: u32) -> Self {
Self(ms) Self(ms)
} }
pub fn from_sec(sec: u32) -> Self { pub fn from_sec(sec: u32) -> Self {
Self(sec * 1000) Self(sec * 1000)
} }
pub fn from_minutes(min: u32) -> Self { pub fn from_minutes(min: u32) -> Self {
Self(min * 60 * 1000) Self(min * 60 * 1000)
} }
pub fn from_hours(hrs: u32) -> Self { pub fn from_hours(hrs: u32) -> Self {
Self(hrs * 60 * 60 * 1000) Self(hrs * 60 * 60 * 1000)
} }
pub fn from_days(days: u32) -> Self { pub fn from_days(days: u32) -> Self {
Self(days * 24 * 60 * 60 * 1000) Self(days * 24 * 60 * 60 * 1000)
} }
pub fn s(&self) -> u32 { pub fn s(&self) -> u32 {
(self.0 % 1000) (self.0 % 1000)
} }
pub fn k(&self) -> u32 { pub fn k(&self) -> u32 {
self.0 / 1000 self.0 / 1000
} }
} }
impl Display for Kilosecond { impl Display for Kilosecond {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write![f, "{}K {}S", self.k(), self.s()] write![f, "{}K {}S", self.k(), self.s()]
} }
} }
impl core::ops::Add for Kilosecond { impl core::ops::Add for Kilosecond {
type Output = Self; type Output = Self;
fn add(self, rhs: Self) -> Self { fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0) Self(self.0 + rhs.0)
} }
} }
impl core::ops::Sub for Kilosecond { impl core::ops::Sub for Kilosecond {
type Output = Self; type Output = Self;
fn sub(self, rhs: Self) -> Self { fn sub(self, rhs: Self) -> Self {
Self(self.0 - rhs.0) Self(self.0 - rhs.0)
} }
} }
impl From<Time> for Kilosecond { impl From<Time> for Kilosecond {
fn from(t: Time) -> Self { fn from(t: Time) -> Self {
Self((t.hour as u32 * 3600 + t.minutes as u32 * 60 + t.seconds as u32) * 1000) Self((t.hour as u32 * 3600 + t.minutes as u32 * 60 + t.seconds as u32) * 1000)
} }
} }

View File

@ -1,7 +1,7 @@
use crate::arch::drivers::sysinfo::master; use crate::arch::drivers::sysinfo::master;
use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE}; use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE};
use crate::{filesystem::FILE_SYSTEM, time::fetch_time, KERNEL_STATE}; use crate::{time::fetch_time, KERNEL_STATE};
use genfs::{Fs, OpenOptions}; // use genfs::{Fs, OpenOptions};
use kernel::allocator::ALLOCATOR; use kernel::allocator::ALLOCATOR;
// use rhai::Engine; // use rhai::Engine;
use spin::Lazy; use spin::Lazy;
@ -103,23 +103,23 @@ pub fn poke_memory(ptr: i64, val: i64) {
} }
} }
pub fn ls() { // pub fn ls() {
let current_dir = CURRENT_DIR.lock(); // let current_dir = CURRENT_DIR.lock();
let fs = &*FILE_SYSTEM.lock(); // let fs = &*FILE_SYSTEM.lock();
let file = fs // let file = fs
.open(current_dir.as_bytes(), OpenOptions::new().read(true)) // .open(current_dir.as_bytes(), OpenOptions::new().read(true))
.unwrap(); // .unwrap();
let mut files = file.directory().unwrap(); // let mut files = file.directory().unwrap();
println!("current dir: {}", *current_dir); // println!("current dir: {}", *current_dir);
while let Some(Ok(entry)) = files.next() { // while let Some(Ok(entry)) = files.next() {
let inode_name = entry.name; // let inode_name = entry.name;
let s = String::from_utf8_lossy(&inode_name); // let s = String::from_utf8_lossy(&inode_name);
println!("{}", s); // println!("{}", s);
} // }
} // }
pub fn log_dump() { pub fn log_dump() {
use crate::network::socket::SimpleSock; use crate::network::socket::SimpleSock;
@ -144,55 +144,55 @@ pub fn log_dump() {
} }
} }
pub fn echo_file(path: String) { // pub fn echo_file(path: String) {
let mut current_dir = CURRENT_DIR.lock(); // let mut current_dir = CURRENT_DIR.lock();
let fs = &*FILE_SYSTEM.lock(); // let fs = &*FILE_SYSTEM.lock();
current_dir.push_str(&path); // current_dir.push_str(&path);
let file = fs // let file = fs
.open(current_dir.as_bytes(), OpenOptions::new().read(true)) // .open(current_dir.as_bytes(), OpenOptions::new().read(true))
.unwrap(); // .unwrap();
if file.is_dir() { // if file.is_dir() {
println!("{} is a directory", path); // println!("{} is a directory", path);
} else { // } else {
let mut file_contents = Vec::new(); // let mut file_contents = Vec::new();
let _ret = file.read_to_end(&mut file_contents).unwrap(); // let _ret = file.read_to_end(&mut file_contents).unwrap();
let file_contents_str = String::from_utf8_lossy(&file_contents); // let file_contents_str = String::from_utf8_lossy(&file_contents);
println!("{}", file_contents_str); // println!("{}", file_contents_str);
} // }
} // }
pub fn change_directory(path: String) { // pub fn change_directory(path: String) {
let mut current_dir = CURRENT_DIR.lock(); // let mut current_dir = CURRENT_DIR.lock();
let _fs = &*FILE_SYSTEM.lock(); // let _fs = &*FILE_SYSTEM.lock();
if path == "." || path == ".." { // if path == "." || path == ".." {
let mut split_dir = current_dir.split('/').collect::<Vec<&str>>(); // let mut split_dir = current_dir.split('/').collect::<Vec<&str>>();
let mut new_dir = String::new(); // let mut new_dir = String::new();
split_dir.remove(split_dir.len() - 1); // split_dir.remove(split_dir.len() - 1);
println!("{:?}", split_dir); // println!("{:?}", split_dir);
if split_dir.is_empty() { // if split_dir.is_empty() {
new_dir = "/".to_string(); // new_dir = "/".to_string();
} else { // } else {
for x in split_dir { // for x in split_dir {
new_dir.push_str(x); // new_dir.push_str(x);
new_dir.push('/'); // new_dir.push('/');
} // }
} // }
*current_dir = new_dir; // *current_dir = new_dir;
} else { // } else {
if !current_dir.ends_with('/') { // if !current_dir.ends_with('/') {
current_dir.push('/'); // current_dir.push('/');
} // }
current_dir.push_str(&path); // current_dir.push_str(&path);
} // }
} // }
// fn engine_construction() -> Engine { // fn engine_construction() -> Engine {
// let mut engine = rhai::Engine::new(); // let mut engine = rhai::Engine::new();

View File

@ -7,19 +7,20 @@
use crate::arch::drivers::sysinfo::master; use crate::arch::drivers::sysinfo::master;
use crate::arch::interrupts::{reset_pit_for_cpu, set_pit_2}; use crate::arch::interrupts::{reset_pit_for_cpu, set_pit_2};
use crate::devices::pci::brute_force_scan; use crate::devices::pci::brute_force_scan;
use crate::filesystem;
use crate::filesystem::vfs::VFS;
use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE}; use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE};
use crate::time::fetch_time; use crate::time::fetch_time;
use crate::KERNEL_STATE;
use crate::{ use crate::{
arch::shutdown, filesystem::FILE_SYSTEM, rhai_shell::KEYBUFF, vterm::VTerm, arch::shutdown,
rhai_shell::KEYBUFF,
vterm::VTerm,
KERNEL_STATE,
wasm_jumploader::run_program, wasm_jumploader::run_program,
}; };
use acpi::{AcpiTables, PlatformInfo}; use acpi::{AcpiTables, PlatformInfo};
use cpuio::{inb, outb}; use cpuio::{inb, outb};
use ext2::fs::sync::{DirectoryEntry, Synced};
use ext2::{fs::Ext2, sector::Size1024};
use genfs::{Fs, OpenOptions};
use kernel::allocator::ALLOCATOR; use kernel::allocator::ALLOCATOR;
use spin::Lazy; use spin::Lazy;
use x86_64::instructions::interrupts::{disable, enable}; use x86_64::instructions::interrupts::{disable, enable};
@ -82,7 +83,7 @@ pub fn scratchpad() {
disable(); disable();
let tick_time = fetch_time(); let tick_time = fetch_time();
let hostname = &KERNEL_STATE.lock().hostname; let hostname = KERNEL_STATE.lock().hostname.clone();
let allocator = ALLOCATOR.lock(); let allocator = ALLOCATOR.lock();
let size = allocator.size(); let size = allocator.size();
@ -193,18 +194,16 @@ pub fn real_shell() {
} }
pub fn command_parser(user: String, command: String) { pub fn command_parser(user: String, command: String) {
let fs = &*FILE_SYSTEM.lock();
let mut iter = command.split_whitespace(); let mut iter = command.split_whitespace();
// TODO: update the open() function to take either a ableOS path or a b"/" type path
let current_path = Path::new("/home/able".to_string()); let current_path = Path::new("/home/able".to_string());
trace!("Current path: {:?}", current_path); trace!("Current path: {:?}", current_path);
let current_path = b"/home/able/"; let current_path = "/home/able/";
let bin_name = iter.next().unwrap(); let bin_name = iter.next().unwrap();
let mut strin = String::new(); let mut strin = String::new();
for stri in iter { for stri in iter.clone() {
trace!("{}", stri); trace!("{}", stri);
strin.push_str(stri); strin.push_str(stri);
} }
@ -222,38 +221,39 @@ pub fn command_parser(user: String, command: String) {
// note: able asked for rhaish to stay in the repo but will be removed // note: able asked for rhaish to stay in the repo but will be removed
// in the future so just comment it out for now // in the future so just comment it out for now
// "rhai" => { // "rhai" => {
// drop(fs);
// shell(); // shell();
// } // }
"list" | "ls" => { "list" | "ls" => {
for dir_entry in list_files_in_dir(fs, current_path) { let mut vfs = VFS.lock();
println!("{}", dir_entry.file_name_string()); let handle = vfs.resolve(current_path).unwrap();
let dir = vfs.fs_node(handle).unwrap();
drop(vfs);
for dir_entry in dir.directory().unwrap() {
println!("{}", dir_entry.name());
} }
} }
"echo" => match conf_args.1.arguments.get("p") { "echo" => match conf_args.1.arguments.get("p") {
Some(path) => echo_file(path.to_string(), fs), Some(path) => echo_file(path.to_string()),
None => println!("No path provided"), None => println!("No path provided"),
}, }
"test" => {} "test" => {}
"quit" => shutdown(), "quit" => shutdown(),
"tree" => filesystem::tree("/").unwrap(),
_ => { _ => {
let mut options = OpenOptions::new();
options.read(true);
let file = { let file = {
let mut vfs = VFS.lock();
let path = format!("/home/{user}/bins/{bin_name}.wasm"); let path = format!("/home/{user}/bins/{bin_name}.wasm");
if let Ok(file) = fs.open(&path.as_bytes(), &options) { let handle = if let Ok(file) = vfs.resolve(path) {
file file
} else { } else {
let path = format!("/shared/bins/{bin_name}.wasm"); let path = format!("/shared/bins/{bin_name}.wasm");
if let Ok(file) = fs.open(&path.as_bytes(), &options) { if let Ok(file) = vfs.resolve(path) {
file file
} else { } else {
let path = format!("/system/bins/{bin_name}.wasm"); let path = format!("/system/bins/{bin_name}.wasm");
match fs.open(&path.as_bytes(), &options) { match vfs.resolve(path) {
Ok(file) => file, Ok(file) => file,
Err(error) => { Err(error) => {
trace!("{:?}", error); trace!("{:?}", error);
@ -263,14 +263,16 @@ pub fn command_parser(user: String, command: String) {
} }
} }
} }
} };
vfs.fs_node(handle).unwrap()
}; };
let mut binary = vec![]; let mut binary = vec![];
file.read_to_end(&mut binary).unwrap(); file.read(0, file.size(), &mut binary).unwrap();
// let args = iter.collect::<Vec<&str>>(); let args = iter.collect::<Vec<&str>>();
// println!("{:?}", args); println!("{:?}", args);
run_program(&binary); run_program(&binary);
} }
} }
@ -302,40 +304,21 @@ pub fn sound_off() {
reset_pit_for_cpu(); reset_pit_for_cpu();
} }
pub fn list_files_in_dir( pub fn echo_file(path: String) {
fs: &Synced<Ext2<Size1024, Vec<u8>>>, let mut current_path = String::from("/");
_path: &[u8], current_path.push_str(&path);
) -> Vec<DirectoryEntry> { let mut vfs = VFS.lock();
let mut entry_list = vec![]; let handle = vfs
.resolve(&current_path)
let dirr = fs.read_dir(b"/").unwrap();
for dir_entry in dirr {
entry_list.push(dir_entry.unwrap());
}
entry_list
}
pub static CURRENT_DIR: Lazy<spin::Mutex<String>> = Lazy::new(|| spin::Mutex::new("/".to_string()));
pub fn echo_file(path: String, fs: &Synced<Ext2<Size1024, Vec<u8>>>) {
let mut current_dir = CURRENT_DIR.lock();
current_dir.push_str(&path);
let file = fs
.open(current_dir.as_bytes(), OpenOptions::new().read(true))
.unwrap(); .unwrap();
let file = vfs.fs_node(handle).unwrap();
if file.is_dir() { if file.is_dir() {
// println!("{} is a directory", path); // println!("{} is a directory", path);
} else { } else {
let mut file_contents = Vec::new(); let mut file_contents = Vec::new();
file.read(0, file.size(), &mut file_contents).unwrap();
let _ret = file.read_to_end(&mut file_contents).unwrap();
let file_contents_str = String::from_utf8_lossy(&file_contents); let file_contents_str = String::from_utf8_lossy(&file_contents);
println!("{}", file_contents_str); println!("{}", file_contents_str);
} }
} }

View File

@ -1,101 +1,106 @@
/*
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
*
* SPDX-License-Identifier: MPL-2.0
*/
pub mod host_functions; pub mod host_functions;
use crate::{filesystem::FILE_SYSTEM, wasm_jumploader::host_functions::HostExternals}; use crate::wasm_jumploader::host_functions::HostExternals;
use genfs::{Fs, OpenOptions};
use wasmi::{ImportsBuilder, ModuleInstance}; use wasmi::{ImportsBuilder, ModuleInstance};
pub fn interp() { pub fn interp() {
trace!("Interpreting..."); trace!("Interpreting...");
let fs = &*FILE_SYSTEM.lock(); // let fs = &*FILE_SYSTEM.lock();
trace!("Got filesystem"); // trace!("Got filesystem");
let file = fs // let file = fs
.open( // .open(
b"/home/able/bins/aos_test.wasm", // b"/home/able/bins/aos_test.wasm",
OpenOptions::new().read(true), // OpenOptions::new().read(true),
) // )
.unwrap(); // .unwrap();
let mut wasm_binary = Vec::new(); // let mut wasm_binary = Vec::new();
let ret = file.read_to_end(&mut wasm_binary).unwrap(); // let ret = file.read_to_end(&mut wasm_binary).unwrap();
trace!("Binary size {}", ret); // trace!("Binary size {}", ret);
// Load wasm binary and prepare it for instantiation. // // Load wasm binary and prepare it for instantiation.
let module = wasmi::Module::from_buffer(&wasm_binary).expect("failed to load wasm"); // let module = wasmi::Module::from_buffer(&wasm_binary).expect("failed to load wasm");
trace!("Loaded wasm binary"); // trace!("Loaded wasm binary");
let imports = ImportsBuilder::new().with_resolver("env", &host_functions::HostExternals {}); // let imports = ImportsBuilder::new().with_resolver("env", &host_functions::HostExternals {});
trace!("Created imports"); // trace!("Created imports");
// Instantiate a module with empty imports and // // Instantiate a module with empty imports and
// assert that there is no `start` function. // // assert that there is no `start` function.
let instance = ModuleInstance::new(&module, &imports); // .expect("failed to instantiate wasm module") // let instance = ModuleInstance::new(&module, &imports); // .expect("failed to instantiate wasm module")
match instance { // match instance {
Ok(inst) => { // Ok(inst) => {
let instance = inst.assert_no_start(); // let instance = inst.assert_no_start();
let mut is_driver = false; // let mut is_driver = false;
let _is_program = false; // let _is_program = false;
let mut has_driver_entry = false; // let mut has_driver_entry = false;
let mut has_driver_exit = false; // let mut has_driver_exit = false;
let mut has_start = false; // let mut has_start = false;
if let Some(_val) = instance.export_by_name("driver_entry") { // if let Some(_val) = instance.export_by_name("driver_entry") {
has_driver_entry = true; // has_driver_entry = true;
} // }
if let Some(_val) = instance.export_by_name("driver_exit") { // if let Some(_val) = instance.export_by_name("driver_exit") {
has_driver_exit = true; // has_driver_exit = true;
} // }
match instance.export_by_name("start") { // match instance.export_by_name("start") {
Some(_val) => { // Some(_val) => {
trace!("Program start function found"); // trace!("Program start function found");
has_start = true; // has_start = true;
} // }
None => debug!("No start function found"), // None => debug!("No start function found"),
} // }
match instance.export_by_name("main") { // match instance.export_by_name("main") {
Some(_val) => { // Some(_val) => {
trace!("Program main function found"); // trace!("Program main function found");
has_start = true; // has_start = true;
} // }
None => debug!("No main function found"), // None => debug!("No main function found"),
} // }
match (has_driver_entry, has_driver_exit) { // match (has_driver_entry, has_driver_exit) {
(true, true) => { // (true, true) => {
trace!("Valid driver entry and exit functions found"); // trace!("Valid driver entry and exit functions found");
is_driver = true; // is_driver = true;
} // }
(true, false) => error!("Driver entry function found but no driver exit function"), // (true, false) => error!("Driver entry function found but no driver exit function"),
(false, true) => error!("Driver exit function found but no driver entry function"), // (false, true) => error!("Driver exit function found but no driver entry function"),
(false, false) => { // (false, false) => {
trace!("No driver entry or exit functions found"); // trace!("No driver entry or exit functions found");
} // }
} // }
if has_start && has_driver_entry { // if has_start && has_driver_entry {
error!( // error!(
"A program should not have both a start function and a driver entry function. It Will be treated as a program." // "A program should not have both a start function and a driver entry function. It Will be treated as a program."
); // );
} // }
if has_start { // if has_start {
let ret = instance // let ret = instance
.invoke_export("start", &[], &mut HostExternals {}) // .invoke_export("start", &[], &mut HostExternals {})
.expect("failed to execute export"); // .expect("failed to execute export");
println!("collected wasm return value: {:?}", ret); // println!("collected wasm return value: {:?}", ret);
} else if is_driver { // } else if is_driver {
let ret = instance // let ret = instance
.invoke_export("driver_entry", &[], &mut HostExternals {}) // .invoke_export("driver_entry", &[], &mut HostExternals {})
.expect("failed to execute export"); // .expect("failed to execute export");
println!("collected wasm return value: {:?}", ret); // println!("collected wasm return value: {:?}", ret);
} // }
} // }
Err(err) => error!("{}", err), // Err(err) => error!("{}", err),
} // }
} }
pub fn run_program(program: &[u8]) { pub fn run_program(program: &[u8]) {

View File

@ -62,8 +62,8 @@ pub enum Error {
/// inode name /// inode name
name: String, name: String,
}, },
#[cfg(any(test, not(feature = "no_std")))] // #[cfg(any(test, not(feature = "no_std")))]
Io { inner: io::Error }, // Io { inner: io::Error },
} }
impl Display for Error { impl Display for Error {

View File

@ -1,5 +1,7 @@
//! //!
use crate::sys::inode::TypePerm;
use { use {
super::Ext2, super::Ext2,
alloc::{ alloc::{
@ -391,8 +393,6 @@ impl<S: SectorSize, V: Volume<u8, S>> Inode<S, V> {
} }
/// Determine if an inode is a directory /// Determine if an inode is a directory
pub fn is_dir(&self) -> bool { pub fn is_dir(&self) -> bool {
use sys::inode::TypePerm;
{ self.inner.type_perm }.contains(TypePerm::DIRECTORY) { self.inner.type_perm }.contains(TypePerm::DIRECTORY)
// self.inner.type_perm.contains(TypePerm::DIRECTORY) // self.inner.type_perm.contains(TypePerm::DIRECTORY)
} }
@ -543,6 +543,11 @@ impl<S: SectorSize, V: Volume<u8, S>> Inode<S, V> {
pub fn size(&self) -> usize { pub fn size(&self) -> usize {
self.size32() as usize self.size32() as usize
} }
/// ableOS: expose type_perm
pub fn type_perm(&self) -> TypePerm {
self.inner.type_perm
}
} }
impl<S: SectorSize, V: Volume<u8, S>> File for Inode<S, V> { impl<S: SectorSize, V: Volume<u8, S>> File for Inode<S, V> {

View File

@ -10,213 +10,213 @@ use std::{fs, process::Command};
use colored::*; use colored::*;
struct Options { struct Options {
pub subcommand: Subcommand, pub subcommand: Subcommand,
pub arguments: Vec<String>, pub arguments: Vec<String>,
} }
enum Subcommand { enum Subcommand {
Doc, Doc,
Help, Help,
Run, Run,
Empty, Empty,
/// Run all tests for all architectures /// Run all tests for all architectures
Test, Test,
Unknown(String), Unknown(String),
} }
impl Subcommand { impl Subcommand {
fn from_str<S: AsRef<str>>(str: S) -> Subcommand { fn from_str<S: AsRef<str>>(str: S) -> Subcommand {
match str.as_ref() { match str.as_ref() {
"doc" => Subcommand::Doc, "doc" => Subcommand::Doc,
"help" => Subcommand::Help, "help" => Subcommand::Help,
"run" | "r" => Subcommand::Run, "run" | "r" => Subcommand::Run,
"test" | "t" => Subcommand::Test, "test" | "t" => Subcommand::Test,
"" => Subcommand::Empty, "" => Subcommand::Empty,
unknown => Subcommand::Unknown(unknown.to_string()), unknown => Subcommand::Unknown(unknown.to_string()),
} }
} }
} }
enum MachineType { enum MachineType {
X86_64, X86_64,
RiscV64, RiscV64,
AArch64, AArch64,
Unknown(String), Unknown(String),
} }
fn main() { fn main() {
let options = options(); let options = options();
match options.subcommand { match options.subcommand {
Subcommand::Test => { Subcommand::Test => {
Command::new("cargo") Command::new("cargo")
.args(["test", "--target=json_targets/x86_64-ableos.json"]) .args(["test", "--target=json_targets/x86_64-ableos.json"])
.current_dir(fs::canonicalize("./ableos").unwrap()) .current_dir(fs::canonicalize("./ableos").unwrap())
.status() .status()
.unwrap(); .unwrap();
// panic!("Test Infrastructure missing"); // panic!("Test Infrastructure missing");
} }
Subcommand::Doc => { Subcommand::Doc => {
let machine_text = options.arguments.get(0).cloned().unwrap_or_default(); let machine_text = options.arguments.get(0).cloned().unwrap_or_default();
match machine(machine_text) { match machine(machine_text) {
MachineType::X86_64 => { MachineType::X86_64 => {
Command::new("cargo") Command::new("cargo")
.args(["doc", "--open"]) .args(["doc", "--open"])
.current_dir(fs::canonicalize("./ableos").unwrap()) .current_dir(fs::canonicalize("./ableos").unwrap())
.status() .status()
.unwrap(); .unwrap();
}
MachineType::RiscV64 => {
Command::new("cargo")
.args(["doc", "--open", "--target=riscv64gc-unknown-none-elf"])
.current_dir(fs::canonicalize("./ableos").unwrap())
.status()
.unwrap();
}
MachineType::AArch64 => {
Command::new("cargo")
.args(["doc", "--open", "--target=json_targets/aarch64-ableos.json"])
.current_dir(fs::canonicalize("./ableos").unwrap())
.status()
.unwrap();
}
MachineType::Unknown(unknown) => {
eprintln!(
"{}: unknown machine type `{}`",
"error".red().bold(),
unknown.bold(),
);
eprintln!("expected one of x86_64, riscv64 or aarch64");
}
} }
MachineType::RiscV64 => { }
Command::new("cargo") Subcommand::Help => help(),
.args(["doc", "--open", "--target=riscv64gc-unknown-none-elf"]) Subcommand::Run => {
.current_dir(fs::canonicalize("./ableos").unwrap()) let machine_text = options.arguments.get(0).cloned().unwrap_or_default();
.status() let debug = options.arguments.get(1).cloned().unwrap_or_default();
.unwrap(); let debug = matches!(debug.as_str(), "--debug" | "--dbg" | "-d");
}
MachineType::AArch64 => {
Command::new("cargo")
.args(["doc", "--open", "--target=json_targets/aarch64-ableos.json"])
.current_dir(fs::canonicalize("./ableos").unwrap())
.status()
.unwrap();
}
MachineType::Unknown(unknown) => {
eprintln!(
"{}: unknown machine type `{}`",
"error".red().bold(),
unknown.bold(),
);
eprintln!("expected one of x86_64, riscv64 or aarch64");
}
}
}
Subcommand::Help => help(),
Subcommand::Run => {
let machine_text = options.arguments.get(0).cloned().unwrap_or_default();
let debug = options.arguments.get(1).cloned().unwrap_or_default();
let debug = matches!(debug.as_str(), "--debug" | "--dbg" | "-d");
match machine(machine_text) { match machine(machine_text) {
MachineType::X86_64 if debug => { MachineType::X86_64 if debug => {
Command::new("cargo") Command::new("cargo")
.args(["run", "--", "-S", "-gdb", "tcp:9000"]) .args(["run", "--", "-S", "-gdb", "tcp:9000"])
.current_dir(fs::canonicalize("./ableos").unwrap()) .current_dir(fs::canonicalize("./ableos").unwrap())
.status() .status()
.unwrap(); .unwrap();
} }
MachineType::X86_64 => { MachineType::X86_64 => {
Command::new("cargo") Command::new("cargo")
.args(["run", "--release"]) .args(["run", "--release"])
.current_dir(fs::canonicalize("./ableos").unwrap()) .current_dir(fs::canonicalize("./ableos").unwrap())
.status() .status()
.unwrap(); .unwrap();
} }
MachineType::RiscV64 if debug => { MachineType::RiscV64 if debug => {
eprintln!( eprintln!(
"{}: debug is not implemented for riscv64", "{}: debug is not implemented for riscv64",
"error".red().bold() "error".red().bold()
); );
} }
MachineType::RiscV64 => { MachineType::RiscV64 => {
Command::new("cargo") Command::new("cargo")
.args(["build", "--release", "--target=riscv64gc-unknown-none-elf"]) .args(["build", "--release", "--target=riscv64gc-unknown-none-elf"])
.current_dir(fs::canonicalize("./ableos").unwrap()) .current_dir(fs::canonicalize("./ableos").unwrap())
.status() .status()
.unwrap(); .unwrap();
Command::new("qemu-system-riscv64") Command::new("qemu-system-riscv64")
.args(["-machine", "virt"]) .args(["-machine", "virt"])
.args(["-cpu", "rv64"]) .args(["-cpu", "rv64"])
.args(["-smp", "8"]) .args(["-smp", "8"])
.args(["-m", "128M"]) .args(["-m", "128M"])
.arg("-bios") .arg("-bios")
.arg("src/arch/riscv/firmwear/opensbi-riscv64-generic-fw_jump.bin") .arg("src/arch/riscv/firmwear/opensbi-riscv64-generic-fw_jump.bin")
.arg("-kernel") .arg("-kernel")
.arg("target/riscv64gc-unknown-none-elf/release/ableos") .arg("target/riscv64gc-unknown-none-elf/release/ableos")
.current_dir(fs::canonicalize("./ableos").unwrap()) .current_dir(fs::canonicalize("./ableos").unwrap())
.status() .status()
.unwrap(); .unwrap();
} }
MachineType::AArch64 if debug => { MachineType::AArch64 if debug => {
eprintln!( eprintln!(
"{}: debug is not implemented for aarch64", "{}: debug is not implemented for aarch64",
"error".red().bold() "error".red().bold()
); );
} }
MachineType::AArch64 => { MachineType::AArch64 => {
Command::new("cargo") Command::new("cargo")
.args([ .args([
"build", "build",
"--release", "--release",
"--target=json_targets/aarch64-ableos.json", "--target=json_targets/aarch64-ableos.json",
]) ])
.current_dir(fs::canonicalize("./ableos").unwrap()) .current_dir(fs::canonicalize("./ableos").unwrap())
.status() .status()
.unwrap(); .unwrap();
Command::new("qemu-system-aarch64") Command::new("qemu-system-aarch64")
.args(["-machine", "virt"]) .args(["-machine", "virt"])
.args(["-m", "1024M"]) .args(["-m", "1024M"])
.args(["-cpu", "cortex-a53"]) .args(["-cpu", "cortex-a53"])
.args(["-kernel", "target/aarch64-ableos/release/ableos"]) .args(["-kernel", "target/aarch64-ableos/release/ableos"])
.args(["-device", "virtio-keyboard"]) .args(["-device", "virtio-keyboard"])
.current_dir(fs::canonicalize("./ableos").unwrap()) .current_dir(fs::canonicalize("./ableos").unwrap())
.status() .status()
.unwrap(); .unwrap();
}
MachineType::Unknown(unknown) => {
eprintln!(
"{}: unknown machine type `{}`",
"error".red().bold(),
unknown.bold(),
);
eprintln!("expected one of x86_64, riscv64 or aarch64");
}
} }
MachineType::Unknown(unknown) => { }
eprintln!( Subcommand::Empty => {
"{}: unknown machine type `{}`", eprintln!("{}: no subcommand passed", "error".red().bold());
"error".red().bold(), help();
unknown.bold(), }
); Subcommand::Unknown(unknown) => {
eprintln!("expected one of x86_64, riscv64 or aarch64"); eprintln!(
} "{}: unknown subcommand `{}`",
} "error".red().bold(),
} unknown.bold()
Subcommand::Empty => { );
eprintln!("{}: no subcommand passed", "error".red().bold()); help();
help(); }
} }
Subcommand::Unknown(unknown) => {
eprintln!(
"{}: unknown subcommand `{}`",
"error".red().bold(),
unknown.bold()
);
help();
}
}
} }
fn options() -> Options { fn options() -> Options {
let subcommand = std::env::args().nth(1).unwrap_or_default(); let subcommand = std::env::args().nth(1).unwrap_or_default();
let arguments = std::env::args().skip(2).collect(); let arguments = std::env::args().skip(2).collect();
Options { Options {
subcommand: Subcommand::from_str(subcommand), subcommand: Subcommand::from_str(subcommand),
arguments, arguments,
} }
} }
fn machine<S: AsRef<str>>(text: S) -> MachineType { fn machine<S: AsRef<str>>(text: S) -> MachineType {
match text.as_ref() { match text.as_ref() {
"x86" | "x86_64" => MachineType::X86_64, "x86" | "x86_64" => MachineType::X86_64,
"riscv" | "riscv64" => MachineType::RiscV64, "riscv" | "riscv64" => MachineType::RiscV64,
"arm" | "arm64" | "aarch64" => MachineType::AArch64, "arm" | "arm64" | "aarch64" => MachineType::AArch64,
"" => { "" => {
eprintln!( eprintln!(
"{}: no machine type passed, defaulting to x86_64", "{}: no machine type passed, defaulting to x86_64",
"warning".yellow().bold() "warning".yellow().bold()
); );
MachineType::X86_64 MachineType::X86_64
} }
unknown => MachineType::Unknown(unknown.to_string()), unknown => MachineType::Unknown(unknown.to_string()),
} }
} }
fn help() { fn help() {
todo!("`help`") todo!("`help`")
} }