Merge pull request 'VFS implementation' (#22) from theoddgarlic/ableos:vfs into master
Reviewed-on: https://git.ablecorp.us:443/able/ableos/pulls/22
This commit is contained in:
commit
55b494c154
|
@ -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"
|
||||||
|
|
42
ableos/src/filesystem/errors.rs
Normal file
42
ableos/src/filesystem/errors.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
177
ableos/src/filesystem/ext2.rs
Normal file
177
ableos/src/filesystem/ext2.rs
Normal 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
|
||||||
|
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
305
ableos/src/filesystem/vfs.rs
Normal file
305
ableos/src/filesystem/vfs.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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(¤t_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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`")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue