proc: add simple initrd filesystem

This commit is contained in:
Mark Poliakov 2023-07-19 21:58:59 +03:00
parent 901f311856
commit aa6b2ac469
18 changed files with 818 additions and 54 deletions

View File

@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
vfs = { path = "lib/vfs" }
vfs-macros = { path = "lib/vfs/macros" }
aarch64-cpu = "9.3.1"
atomic_enum = "0.2.0"
@ -18,4 +19,4 @@ spinning_top = "0.2.5"
static_assertions = "1.1.0"
tock-registers = "0.8.1"
elf = { version = "0.7.2", default-features = false }
elf = { version = "0.7.2", path = "../../rust-elf", default-features = false, features = ["no_std_stream"] }

View File

@ -7,5 +7,5 @@ edition = "2021"
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
macros = { path = "macros" }
vfs-macros = { path = "macros" }
bitflags = "2.3.3"

View File

@ -1,5 +1,5 @@
[package]
name = "macros"
name = "vfs-macros"
version = "0.1.0"
edition = "2021"

View File

@ -1,7 +1,7 @@
use std::collections::HashSet;
use proc_macro::TokenStream;
use proc_macro2::Ident;
use proc_macro2::{Ident, Span};
use proc_macro_crate::{crate_name, FoundCrate};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, ImplItem, ItemImpl};
@ -14,9 +14,11 @@ pub fn auto_vnode_impl(attr: TokenStream, input: TokenStream) -> TokenStream {
let current_crate = crate_name("vfs").unwrap();
let vfs_crate = match current_crate {
FoundCrate::Itself => quote!(crate),
FoundCrate::Name(name) => quote!( #name ),
FoundCrate::Itself => Ident::new("crate", Span::call_site()),
FoundCrate::Name(name) => Ident::new(&name, Span::call_site()),
};
let vfs_crate = quote! { #vfs_crate };
let mut impl_item = parse_macro_input!(input as ItemImpl);
let behavior = if attr.is_empty() {
"unimplemented".to_string()

View File

@ -6,7 +6,7 @@ use quote::quote;
fn impl_open(vfs: &TS2, behavior: &TS2) -> TS2 {
quote! {
fn open(&mut self, _node: &#vfs::VnodeRef, _opts: yggdrasil_abi::io::OpenFlags)
-> Result<usize, yggdrasil_abi::error::Error> {
-> Result<u64, yggdrasil_abi::error::Error> {
#behavior
}
}
@ -31,7 +31,7 @@ fn impl_create(vfs: &TS2, behavior: &TS2) -> TS2 {
fn impl_write(vfs: &TS2, behavior: &TS2) -> TS2 {
quote! {
fn write(&mut self, _node: &#vfs::VnodeRef, _pos: usize, _data: &[u8])
fn write(&mut self, _node: &#vfs::VnodeRef, _pos: u64, _data: &[u8])
-> Result<usize, yggdrasil_abi::error::Error> {
#behavior
}
@ -40,7 +40,7 @@ fn impl_write(vfs: &TS2, behavior: &TS2) -> TS2 {
fn impl_read(vfs: &TS2, behavior: &TS2) -> TS2 {
quote! {
fn read(&mut self, _node: &#vfs::VnodeRef, _pos: usize, _data: &mut [u8])
fn read(&mut self, _node: &#vfs::VnodeRef, _pos: u64, _data: &mut [u8])
-> Result<usize, yggdrasil_abi::error::Error> {
#behavior
}

View File

@ -1,4 +1,4 @@
use macros::auto_vnode_impl;
use vfs_macros::auto_vnode_impl;
use yggdrasil_abi::{error::Error, io::OpenFlags};
use crate::{node::VnodeImpl, VnodeRef};
@ -20,7 +20,7 @@ impl CharDeviceWrapper {
#[auto_vnode_impl(error)]
impl VnodeImpl for CharDeviceWrapper {
fn open(&mut self, _node: &VnodeRef, _opts: OpenFlags) -> Result<usize, Error> {
fn open(&mut self, _node: &VnodeRef, _opts: OpenFlags) -> Result<u64, Error> {
Ok(0)
}
@ -28,11 +28,11 @@ impl VnodeImpl for CharDeviceWrapper {
Ok(())
}
fn read(&mut self, _node: &VnodeRef, _pos: usize, data: &mut [u8]) -> Result<usize, Error> {
fn read(&mut self, _node: &VnodeRef, _pos: u64, data: &mut [u8]) -> Result<usize, Error> {
self.device.read(true, data)
}
fn write(&mut self, _node: &VnodeRef, _pos: usize, data: &[u8]) -> Result<usize, Error> {
fn write(&mut self, _node: &VnodeRef, _pos: u64, data: &[u8]) -> Result<usize, Error> {
self.device.write(true, data)
}
}

View File

@ -6,7 +6,7 @@ use yggdrasil_abi::error::Error;
use crate::{
node::{VnodeKind, VnodeRef},
Read, Write,
Read, Seek, SeekFrom, Write,
};
bitflags! {
@ -20,7 +20,7 @@ pub type FileRef = Rc<RefCell<File>>;
pub struct NormalFile {
vnode: VnodeRef,
pos: usize,
pos: u64,
}
pub enum FileInner {
@ -33,7 +33,7 @@ pub struct File {
}
impl File {
pub fn normal(vnode: VnodeRef, pos: usize, flags: FileFlags) -> FileRef {
pub fn normal(vnode: VnodeRef, pos: u64, flags: FileFlags) -> FileRef {
Rc::new(RefCell::new(Self {
inner: FileInner::Normal(NormalFile { vnode, pos }),
flags,
@ -51,7 +51,7 @@ impl Write for File {
FileInner::Normal(inner) => {
let count = inner.vnode.write(inner.pos, data)?;
if inner.vnode.kind() != VnodeKind::Char {
inner.pos += count;
inner.pos += count as u64;
}
Ok(count)
}
@ -69,7 +69,7 @@ impl Read for File {
FileInner::Normal(inner) => {
let count = inner.vnode.read(inner.pos, data)?;
if inner.vnode.kind() != VnodeKind::Char {
inner.pos += count;
inner.pos += count as u64;
}
Ok(count)
}
@ -77,6 +77,31 @@ impl Read for File {
}
}
impl Seek for File {
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error> {
match &mut self.inner {
FileInner::Normal(inner) => {
// TODO check if the file is actually seekable
let size = inner.vnode.size()?;
let pos = match pos {
SeekFrom::Start(offset) => {
if offset > size {
todo!();
}
offset
}
SeekFrom::End(0) => size,
_ => todo!(),
};
inner.pos = pos;
Ok(pos)
}
}
}
}
impl Drop for File {
fn drop(&mut self) {
match &mut self.inner {

View File

@ -17,8 +17,16 @@ pub(crate) mod node;
pub use self::block::BlockDevice;
pub use self::char::{CharDevice, CharDeviceWrapper};
pub use file::{File, FileFlags, FileRef};
pub use fs::Filesystem;
pub use ioctx::IoContext;
pub use node::{Vnode, VnodeImpl, VnodeKind, VnodeRef, VnodeWeak};
pub use node::{Vnode, VnodeDump, VnodeImpl, VnodeKind, VnodeRef, VnodeWeak};
#[derive(Debug)]
pub enum SeekFrom {
Start(u64),
End(i64),
Current(i64),
}
pub trait Write {
fn write(&mut self, data: &[u8]) -> Result<usize, Error>;
@ -26,4 +34,31 @@ pub trait Write {
pub trait Read {
fn read(&mut self, data: &mut [u8]) -> Result<usize, Error>;
fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Error> {
default_read_exact(self, data)
}
}
pub trait Seek {
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error>;
}
fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [u8]) -> Result<(), Error> {
while !buf.is_empty() {
match this.read(buf) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
}
Err(e) => todo!("default_read_exact: {:?}", e),
}
}
if !buf.is_empty() {
todo!("default_read_exact unexpected eof")
} else {
Ok(())
}
}

View File

@ -11,11 +11,18 @@ use alloc::{
};
use yggdrasil_abi::{error::Error, io::OpenFlags};
use crate::file::{File, FileFlags, FileRef};
use crate::{
file::{File, FileFlags, FileRef},
fs::Filesystem,
};
pub type VnodeRef = Rc<Vnode>;
pub type VnodeWeak = Weak<Vnode>;
pub struct VnodeDump {
node: VnodeRef,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum VnodeKind {
Directory,
@ -34,16 +41,21 @@ pub struct Vnode {
tree: RefCell<TreeNode>,
kind: VnodeKind,
data: RefCell<Option<Box<dyn VnodeImpl>>>,
fs: RefCell<Option<Rc<dyn Filesystem>>>,
}
pub trait VnodeImpl {
fn create(&mut self, at: &VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Error>;
fn open(&mut self, node: &VnodeRef, opts: OpenFlags) -> Result<usize, Error>;
fn open(&mut self, node: &VnodeRef, opts: OpenFlags) -> Result<u64, Error>;
fn close(&mut self, node: &VnodeRef) -> Result<(), Error>;
fn read(&mut self, node: &VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Error>;
fn write(&mut self, node: &VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Error>;
fn read(&mut self, node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error>;
fn write(&mut self, node: &VnodeRef, pos: u64, data: &[u8]) -> Result<usize, Error>;
fn size(&mut self, _node: &VnodeRef) -> Result<u64, Error> {
unimplemented!()
}
}
impl Vnode {
@ -56,6 +68,7 @@ impl Vnode {
}),
kind,
data: RefCell::new(None),
fs: RefCell::new(None),
})
}
@ -74,6 +87,11 @@ impl Vnode {
self.data.borrow_mut()
}
#[inline]
pub fn fs(&self) -> Option<Rc<dyn Filesystem>> {
self.fs.borrow().clone()
}
pub fn parent(self: &VnodeRef) -> VnodeRef {
match &self.tree.borrow().parent {
Some(parent) => parent.upgrade().unwrap(),
@ -85,6 +103,10 @@ impl Vnode {
self.data.borrow_mut().replace(data);
}
pub fn set_fs(&self, data: Rc<dyn Filesystem>) {
self.fs.replace(Some(data));
}
#[inline]
pub fn is_directory(&self) -> bool {
self.kind == VnodeKind::Directory
@ -186,7 +208,33 @@ impl Vnode {
}
}
pub fn write(self: &VnodeRef, pos: usize, buf: &[u8]) -> Result<usize, Error> {
pub fn create(self: &VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Error> {
if self.kind != VnodeKind::Directory {
todo!();
}
if name.contains('/') {
return Err(Error::InvalidArgument);
}
match self.lookup_or_load(name) {
Err(Error::DoesNotExist) => {}
Ok(_) => return Err(Error::AlreadyExists),
e => return e,
};
if let Some(ref mut data) = *self.data() {
let vnode = data.create(self, name, kind)?;
if let Some(fs) = self.fs() {
vnode.set_fs(fs);
}
self.add_child(vnode.clone());
Ok(vnode)
} else {
Err(Error::NotImplemented)
}
}
pub fn write(self: &VnodeRef, pos: u64, buf: &[u8]) -> Result<usize, Error> {
if self.kind == VnodeKind::Directory {
todo!();
}
@ -198,7 +246,7 @@ impl Vnode {
}
}
pub fn read(self: &VnodeRef, pos: usize, buf: &mut [u8]) -> Result<usize, Error> {
pub fn read(self: &VnodeRef, pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
if self.kind == VnodeKind::Directory {
todo!();
}
@ -209,6 +257,14 @@ impl Vnode {
todo!()
}
}
pub fn size(self: &VnodeRef) -> Result<u64, Error> {
if let Some(ref mut data) = *self.data() {
data.size(self)
} else {
todo!();
}
}
}
impl fmt::Debug for Vnode {
@ -223,3 +279,15 @@ impl fmt::Debug for Vnode {
write!(f, "[{} {}]", prefix, self.name)
}
}
impl VnodeDump {
pub fn new(node: VnodeRef) -> Self {
Self { node }
}
}
impl fmt::Debug for VnodeDump {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.node.dump(f, 0)
}
}

View File

@ -126,7 +126,7 @@ fn find_node<'a>(at: TNode<'a>, path: &str) -> Option<TNode<'a>> {
fn dump_node(node: &TNode, depth: usize, level: LogLevel) {
fn indent(level: LogLevel, depth: usize) {
for _ in 0..depth {
log_print!(level, " ");
log_print_raw!(level, " ");
}
}
@ -138,18 +138,18 @@ fn dump_node(node: &TNode, depth: usize, level: LogLevel) {
}
indent(level, depth);
log_print!(level, "{:?} {{\n", node_name);
log_print_raw!(level, "{:?} {{\n", node_name);
for prop in node.props() {
indent(level, depth + 1);
let name = prop.name().unwrap();
log_print!(level, "{name:?} = ");
log_print_raw!(level, "{name:?} = ");
match name {
"compatible" | "stdout-path" => log_print!(level, "{:?}", prop.str().unwrap()),
_ => log_print!(level, "{:x?}", prop.raw()),
"compatible" | "stdout-path" => log_print_raw!(level, "{:?}", prop.str().unwrap()),
_ => log_print_raw!(level, "{:x?}", prop.raw()),
}
log_print!(level, "\n");
log_print_raw!(level, "\n");
}
for child in node.children() {
@ -157,5 +157,5 @@ fn dump_node(node: &TNode, depth: usize, level: LogLevel) {
}
indent(level, depth);
log_print!(level, "}}\n");
log_print_raw!(level, "}}\n");
}

View File

@ -4,6 +4,7 @@ use core::sync::atomic::Ordering;
use aarch64_cpu::registers::{DAIF, ID_AA64MMFR0_EL1, SCTLR_EL1, TCR_EL1, TTBR0_EL1, TTBR1_EL1};
use abi::error::Error;
use fdt_rs::prelude::PropReader;
use plat_qemu::PLATFORM;
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
@ -15,7 +16,7 @@ use crate::{
},
debug,
device::platform::Platform,
fs::devfs,
fs::{devfs, Initrd, INITRD_DATA},
mem::{
heap,
phys::{self, reserved::reserve_region, PageUsage, PhysicalMemoryRegion},
@ -135,6 +136,16 @@ impl AArch64 {
unsafe fn init_physical_memory(&self, dtb_phys: usize) -> Result<(), Error> {
let dt = self.device_tree();
if let Some(initrd) = INITRD_DATA.try_get() {
reserve_region(
"initrd",
PhysicalMemoryRegion {
base: initrd.phys_page_start,
size: initrd.phys_page_len,
},
);
}
reserve_region(
"dtb",
PhysicalMemoryRegion {
@ -148,6 +159,41 @@ impl AArch64 {
}
}
fn setup_initrd() {
let dt = ARCHITECTURE.device_tree();
let Some(chosen) = dt.node_by_path("/chosen") else {
return;
};
let Some(initrd_start) = devtree::find_prop(&chosen, "linux,initrd-start") else {
return;
};
let Some(initrd_end) = devtree::find_prop(&chosen, "linux,initrd-end") else {
return;
};
let initrd_start = initrd_start.u64(0).unwrap() as usize;
let initrd_end = initrd_end.u64(0).unwrap() as usize;
let start_aligned = initrd_start & !0xFFF;
let end_aligned = initrd_end & !0xFFF;
let data = unsafe {
core::slice::from_raw_parts(
initrd_start.virtualize() as *const _,
initrd_end - initrd_start,
)
};
let initrd = Initrd {
phys_page_start: start_aligned,
phys_page_len: end_aligned - start_aligned,
data,
};
INITRD_DATA.init(initrd);
}
/// AArch64 kernel main entry point
pub fn kernel_main(dtb_phys: usize) -> ! {
// NOTE it is critical that the code does not panic until the debug is set up, otherwise no
@ -167,6 +213,9 @@ pub fn kernel_main(dtb_phys: usize) -> ! {
exception::init_exceptions();
// Setup initrd
setup_initrd();
debugln!("Initializing {} platform", PLATFORM.name());
unsafe {
ARCHITECTURE

View File

@ -99,6 +99,26 @@ impl fmt::Write for DebugPrinter {
}
}
pub fn hex_dump(level: LogLevel, addr_offset: usize, data: &[u8]) {
for (i, b) in data.iter().enumerate() {
if i % 16 == 0 {
log_print_raw!(level, "{:X}: ", addr_offset + i)
}
log_print_raw!(level, "{:02X}", data[i]);
if i % 16 == 15 {
log_print_raw!(level, "\n");
} else if i % 2 == 1 {
log_print_raw!(level, " ");
}
}
if data.len() % 16 != 0 {
log_print_raw!(level, "\n");
}
}
/// Initializes the debug logging faclities.
///
/// # Panics

View File

@ -1,3 +1,14 @@
//! Filesystem implementations
use crate::util::OneTimeInit;
pub struct Initrd {
pub phys_page_start: usize,
pub phys_page_len: usize,
pub data: &'static [u8],
}
pub static INITRD_DATA: OneTimeInit<Initrd> = OneTimeInit::new();
pub mod devfs;
pub mod tar;

332
src/fs/tar.rs Normal file
View File

@ -0,0 +1,332 @@
use abi::error::Error;
use alloc::{boxed::Box, rc::Rc};
use vfs::{Filesystem, Vnode, VnodeImpl, VnodeKind, VnodeRef};
use vfs_macros::auto_vnode_impl;
use yggdrasil_abi::io::OpenFlags;
use crate::util::OneTimeInit;
#[repr(C)]
pub struct OctalField<const N: usize> {
data: [u8; N],
}
#[repr(C)]
pub struct TarString<const N: usize> {
data: [u8; N],
}
pub struct TarIterator<'a> {
data: &'a [u8],
offset: usize,
zero_blocks: usize,
}
#[repr(packed)]
pub struct TarEntry {
name: TarString<100>,
mode: OctalField<8>,
uid: OctalField<8>,
gid: OctalField<8>,
size: OctalField<12>,
mtime: OctalField<12>,
checksum: OctalField<8>,
type_: u8,
link_name: [u8; 100],
magic: [u8; 8],
user: [u8; 32],
group: [u8; 32],
dev_major: OctalField<8>,
dev_minor: OctalField<8>,
prefix: [u8; 155],
__pad: [u8; 12],
}
impl<'a> TarIterator<'a> {
pub const fn new(data: &'a [u8]) -> Self {
Self {
data,
offset: 0,
zero_blocks: 0,
}
}
}
impl<'a> Iterator for TarIterator<'a> {
type Item = Result<(&'a TarEntry, Option<&'a [u8]>), Error>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.offset + 512 > self.data.len() {
break None;
}
let hdr_ptr = &self.data[self.offset..];
let hdr = unsafe { core::mem::transmute::<*const u8, &TarEntry>(hdr_ptr.as_ptr()) };
if hdr.is_empty() {
if self.zero_blocks == 1 {
self.offset = self.data.len();
return None;
}
self.zero_blocks += 1;
continue;
}
let (data, size_aligned) = match hdr.type_ {
0 | b'0' => {
let size = usize::from(&hdr.size);
if self.offset + 512 + size > self.data.len() {
return Some(Err(Error::InvalidArgument));
}
let data = &self.data[self.offset + 512..self.offset + 512 + size];
let size_aligned = (size + 511) & !511;
(Some(data), size_aligned)
}
// Directory
b'5' => (None, 0),
_ => todo!("Unknown node kind: {}", hdr.type_),
};
self.offset += size_aligned + 512;
break Some(Ok((hdr, data)));
}
}
}
impl<const N: usize> From<&OctalField<N>> for usize {
fn from(value: &OctalField<N>) -> Self {
let mut acc = 0;
for i in 0..N {
if value.data[i] == 0 {
break;
}
acc <<= 3;
acc |= (value.data[i] - b'0') as usize;
}
acc
}
}
impl<const N: usize> TarString<N> {
pub fn as_str(&self) -> Result<&str, Error> {
core::str::from_utf8(&self.data[..self.len()]).map_err(|_| Error::InvalidArgument)
}
pub fn len(&self) -> usize {
for i in 0..N {
if self.data[i] == 0 {
return i;
}
}
N
}
}
impl TarEntry {
pub fn is_empty(&self) -> bool {
self.name.data[0] == 0
}
pub fn node_kind(&self) -> VnodeKind {
match self.type_ {
0 | b'0' => VnodeKind::Regular,
b'5' => VnodeKind::Directory,
_ => todo!(),
}
}
}
pub struct TarFilesystem {
root: OneTimeInit<VnodeRef>,
}
impl Filesystem for TarFilesystem {
fn dev(self: Rc<Self>) -> Option<&'static dyn vfs::BlockDevice> {
todo!()
}
fn root(self: Rc<Self>) -> Result<VnodeRef, Error> {
self.root.try_get().cloned().ok_or(Error::DoesNotExist)
}
fn data(&self) -> Option<core::cell::Ref<dyn core::any::Any>> {
todo!()
}
}
struct DirInode;
struct RegularInode {
data: &'static [u8],
}
#[auto_vnode_impl]
impl VnodeImpl for DirInode {
fn create(&mut self, _at: &VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Error> {
let child = Vnode::new(name, kind);
match kind {
VnodeKind::Directory => child.set_data(Box::new(DirInode)),
VnodeKind::Regular => (),
_ => todo!(),
}
Ok(child)
}
}
#[auto_vnode_impl]
impl VnodeImpl for RegularInode {
fn open(&mut self, node: &VnodeRef, opts: OpenFlags) -> Result<u64, Error> {
if opts.is_write() {
panic!("TODO: tarfs write");
}
Ok(0)
}
fn close(&mut self, node: &VnodeRef) -> Result<(), Error> {
Ok(())
}
fn read(&mut self, node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error> {
let pos = pos as usize;
if pos > self.data.len() {
return Err(Error::InvalidFile);
}
let mut rem = core::cmp::min(self.data.len() - pos, data.len());
data[..rem].copy_from_slice(&self.data[pos..pos + rem]);
Ok(rem)
}
fn size(&mut self, _node: &VnodeRef) -> Result<u64, Error> {
Ok(self.data.len() as u64)
}
}
impl TarFilesystem {
fn make_path(
self: &Rc<Self>,
at: &VnodeRef,
path: &str,
create: bool,
) -> Result<VnodeRef, Error> {
debugln!("make_path {:?}", path);
if path.is_empty() {
return Ok(at.clone());
}
let (element, rest) = abi::path::split_left(path);
assert!(!element.is_empty());
let node = at.lookup(element);
let node = match node {
Some(node) => node,
None => {
if !create {
debugln!("path {:?} does not exist", path);
return Err(Error::DoesNotExist);
}
infoln!("Create {:?}", element);
at.create(element, VnodeKind::Directory)?
}
};
if rest.is_empty() {
Ok(node)
} else {
self.make_path(&node, rest, create)
}
}
fn create_node_initial(
self: &Rc<Self>,
name: &str,
hdr: &TarEntry,
data: Option<&[u8]>,
) -> VnodeRef {
assert!(!name.is_empty());
assert!(!name.contains('/'));
let kind = hdr.node_kind();
let node = Vnode::new(name, kind);
node.set_fs(self.clone());
match kind {
VnodeKind::Directory => node.set_data(Box::new(DirInode)),
VnodeKind::Regular => {}
_ => todo!(),
}
node
}
fn from_slice_internal(self: &Rc<Self>, tar_data: &'static [u8]) -> Result<VnodeRef, Error> {
let root = Vnode::new("", VnodeKind::Directory);
root.set_fs(self.clone());
root.set_data(Box::new(DirInode));
// 1. Create paths in tar
for item in TarIterator::new(tar_data) {
let Ok((hdr, data)) = item else {
warnln!("Tar image is truncated");
return Err(Error::InvalidArgument);
};
let path = hdr.name.as_str()?.trim_matches('/');
infoln!("path = {:?}", path);
let (dirname, filename) = abi::path::split_right(path);
let parent = self.make_path(&root, dirname, true)?;
let node = self.create_node_initial(filename, hdr, data);
parent.add_child(node);
}
// 2. Associate files with their data
for item in TarIterator::new(tar_data) {
let Ok((hdr, data)) = item else {
panic!("Unreachable");
};
if hdr.node_kind() == VnodeKind::Regular {
let data = data.unwrap();
let path = hdr.name.as_str()?.trim_matches('/');
let node = self.make_path(&root, path, false)?;
node.set_data(Box::new(RegularInode { data }));
}
}
Ok(root)
}
pub fn from_slice(tar_data: &'static [u8]) -> Result<Rc<Self>, Error> {
let fs = Rc::new(TarFilesystem {
root: OneTimeInit::new(),
});
let root = fs.from_slice_internal(tar_data)?;
fs.root.init(root);
Ok(fs)
}
}
// pub fn init() {
// let Some(initrd) = INITRD_DATA.try_get() else {
// warnln!("No initrd found");
// return;
// };
//
// let fs = match TarFilesystem::from_slice(initrd.data) {
// Ok(fs) => fs,
// Err(err) => {
// warnln!("Could not initialize tar filesystem: {:?}", err);
// return;
// }
// };
//
// let r = fs.root().unwrap();
// let dump = VnodeDump::new(r);
// infoln!("{:?}", dump);
//
// todo!()
// }

View File

@ -15,7 +15,15 @@
extern crate yggdrasil_abi as abi;
use core::ops::DerefMut;
use abi::{
error::Error,
io::{OpenFlags, RawFd},
};
use fs::{devfs, tar::TarFilesystem, INITRD_DATA};
use task::process::Process;
use vfs::{Filesystem, IoContext, Read, VnodeRef};
extern crate alloc;
@ -34,6 +42,12 @@ pub mod syscall;
pub mod task;
pub mod util;
fn setup_root() -> Result<VnodeRef, Error> {
let initrd_data = INITRD_DATA.get();
let fs = TarFilesystem::from_slice(initrd_data.data)?;
fs.root()
}
/// Entry point for common kernel code.
///
/// # Note
@ -41,15 +55,40 @@ pub mod util;
/// This function is meant to be used as a kernel-space process after all the platform-specific
/// initialization has finished.
pub fn kernel_main() {
let root = match setup_root() {
Ok(root) => root,
Err(err) => {
warnln!("Could not setup root from initrd: {:?}", err);
return;
}
};
let devfs_root = devfs::root();
let tty_node = devfs_root.lookup("ttyS0").unwrap();
let ioctx = IoContext::new(root);
let node = ioctx.find(None, "/init", false).unwrap();
let file = node.open(OpenFlags::new().read()).unwrap();
{
let user_init = proc::exec::load_elf(file, &["/init"]).unwrap();
let mut io = user_init.io.lock();
io.set_ioctx(ioctx);
let stdout = tty_node.open(OpenFlags::new().write()).unwrap();
let stderr = stdout.clone();
io.set_file(RawFd::STDOUT, stdout).unwrap();
io.set_file(RawFd::STDERR, stderr).unwrap();
drop(io);
user_init.enqueue_somewhere();
}
// static USER_PROGRAM: &[u8] = include_bytes!(concat!(
// "../../target/aarch64-unknown-yggdrasil/",
// env!("PROFILE"),
// "/test_program"
// ));
// let devfs_root = devfs::root();
// let tty_node = devfs_root.lookup("ttyS0").unwrap();
// let ioctx = IoContext::new(devfs_root.clone());
// // Spawn a test user task
@ -61,15 +100,6 @@ pub fn kernel_main() {
// // Setup I/O for the process
// // let mut io = proc.io.lock();
// // io.set_file(RawFd::STDOUT, todo!()).unwrap();
// {
// let mut io = proc.io.lock();
// io.set_ioctx(ioctx);
// let stdout = tty_node.open(OpenFlags::new().write()).unwrap();
// let stderr = stdout.clone();
// io.set_file(RawFd::STDOUT, stdout).unwrap();
// io.set_file(RawFd::STDERR, stderr).unwrap();
// }
// proc.enqueue_somewhere();
// }

View File

@ -3,6 +3,7 @@ use core::mem::size_of;
use abi::error::Error;
use alloc::rc::Rc;
use vfs::{FileRef, Read, Seek};
use crate::{
arch::aarch64::context::TaskContext,
@ -66,13 +67,13 @@ fn setup_args(space: &mut AddressSpace, virt: usize, args: &[&str]) -> Result<()
Ok(())
}
/// Sets up a userspace structure from a slice defining an ELF binary
pub fn create_from_memory(data: &[u8], args: &[&str]) -> Result<Rc<Process>, Error> {
fn setup_binary(
mut space: AddressSpace,
entry: usize,
args: &[&str],
) -> Result<Rc<Process>, Error> {
const USER_STACK_PAGES: usize = 8;
let mut space = AddressSpace::new_empty()?;
let elf_entry = proc::load_elf_from_memory(&mut space, data);
let virt_stack_base = 0x10000000;
// 0x1000 of guard page
let virt_args_base = virt_stack_base + (USER_STACK_PAGES + 1) * 0x1000;
@ -88,10 +89,16 @@ pub fn create_from_memory(data: &[u8], args: &[&str]) -> Result<Rc<Process>, Err
setup_args(&mut space, virt_args_base, args)?;
debugln!("Entry: {:#x}", elf_entry);
debugln!(
"Entry: {:#x}, Stack: {:#x}..{:#x}, Args: {:#x}",
entry,
virt_stack_base,
virt_stack_base + USER_STACK_PAGES * 0x1000,
virt_args_base
);
let context = TaskContext::user(
elf_entry,
entry,
virt_args_base,
space.physical_address(),
virt_stack_base + USER_STACK_PAGES * 0x1000,
@ -99,3 +106,18 @@ pub fn create_from_memory(data: &[u8], args: &[&str]) -> Result<Rc<Process>, Err
Ok(Process::new_with_context(Some(space), context))
}
pub fn load_elf(file: FileRef, args: &[&str]) -> Result<Rc<Process>, Error> {
let mut space = AddressSpace::new_empty()?;
let elf_entry = proc::load_elf_from_file(&mut space, file)?;
setup_binary(space, elf_entry, args)
}
/// Sets up a userspace structure from a slice defining an ELF binary
pub fn create_from_memory(data: &[u8], args: &[&str]) -> Result<Rc<Process>, Error> {
let mut space = AddressSpace::new_empty()?;
let elf_entry = proc::load_elf_from_memory(&mut space, data);
setup_binary(space, elf_entry, args)
}

View File

@ -4,22 +4,76 @@ pub mod exec;
pub mod io;
pub mod wait;
use aarch64_cpu::registers::TTBR0_EL1;
use aarch64_cpu::registers::{TCR_EL1::A1::TTBR0, TTBR0_EL1};
use alloc::rc::Rc;
use elf::{
abi::{PF_W, PF_X, PT_LOAD},
endian::AnyEndian,
ElfBytes,
ElfBytes, ElfStream, ParseError,
};
use tock_registers::interfaces::Writeable;
use vfs::{FileRef, Read, Seek, SeekFrom};
use yggdrasil_abi::error::Error;
use crate::{
arch::aarch64::table::tlb_flush_vaae1,
debug::hex_dump,
mem::{
phys::{self, PageUsage},
table::{AddressSpace, PageAttributes},
ConvertAddress,
},
};
#[derive(Clone, Copy)]
pub struct FileReader<'a> {
file: &'a FileRef,
}
impl elf::io_traits::Read for FileReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, elf::io_traits::StreamError> {
todo!()
}
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), elf::io_traits::StreamError> {
self.file
.borrow_mut()
.read_exact(buf)
.map_err(conv_stream_error)
}
}
impl elf::io_traits::Seek for FileReader<'_> {
fn seek(&mut self, pos: elf::io_traits::SeekFrom) -> Result<u64, elf::io_traits::StreamError> {
self.file
.borrow_mut()
.seek(conv_seek_from(pos))
.map_err(conv_stream_error)
}
}
#[inline]
fn conv_stream_error(v: Error) -> elf::io_traits::StreamError {
elf::io_traits::StreamError {
message: "Elf read error",
}
}
#[inline]
fn conv_seek_from(v: elf::io_traits::SeekFrom) -> SeekFrom {
match v {
elf::io_traits::SeekFrom::End(off) => SeekFrom::End(off),
elf::io_traits::SeekFrom::Start(off) => SeekFrom::Start(off),
_ => todo!(),
}
}
#[inline]
fn from_parse_error(v: ParseError) -> Error {
warnln!("ELF loading error: {:?}", v);
Error::InvalidFile
}
fn load_segment(space: &mut AddressSpace, addr: usize, data: &[u8], memsz: usize, elf_attrs: u32) {
let attrs = match (elf_attrs & PF_W, elf_attrs & PF_X) {
(0, 0) => PageAttributes::AP_BOTH_READONLY,
@ -62,6 +116,113 @@ fn load_segment(space: &mut AddressSpace, addr: usize, data: &[u8], memsz: usize
}
}
fn load_bytes<F>(
space: &mut AddressSpace,
addr: usize,
mut src: F,
len: usize,
elf_attrs: u32,
) -> Result<(), Error>
where
F: FnMut(usize, &mut [u8]) -> Result<(), Error>,
{
// TODO check for crazy addresses here
let attrs = match (elf_attrs & PF_W, elf_attrs & PF_X) {
(0, 0) => PageAttributes::AP_BOTH_READONLY,
(_, 0) => PageAttributes::AP_BOTH_READWRITE,
(0, _) => PageAttributes::AP_BOTH_READONLY,
(_, _) => PageAttributes::AP_BOTH_READWRITE,
};
let dst_page_off = addr & 0xFFF;
let dst_page_aligned = addr & !0xFFF;
let mut off = 0usize;
let mut rem = len;
while rem != 0 {
let page_idx = (dst_page_off + off) / 0x1000;
let page_off = (dst_page_off + off) % 0x1000;
let count = core::cmp::min(rem, 0x1000 - page_off);
let virt_page = dst_page_aligned + page_idx * 0x1000;
assert_eq!(virt_page & 0xFFF, 0);
if let Some(_) = space.translate(virt_page) {
// Handle these cases
todo!();
}
let phys_page = phys::alloc_page(PageUsage::Used)?;
debugln!("map {:#x} -> {:#x}", virt_page, phys_page);
space.map_page(virt_page, phys_page, attrs)?;
let dst_slice = unsafe {
let addr = (phys_page + page_off).virtualize();
core::slice::from_raw_parts_mut(addr as *mut u8, count)
};
src(off, dst_slice)?;
debugln!("{:#x} (off = {}):", virt_page + page_off, page_off);
// hex_dump(
// crate::debug::LogLevel::Debug,
// virt_page + page_off,
// dst_slice,
// );
rem -= count;
off += count;
}
Ok(())
}
pub fn load_elf_from_file(space: &mut AddressSpace, file: FileRef) -> Result<usize, Error> {
let file = FileReader { file: &file };
let elf = ElfStream::<AnyEndian, _>::open_stream(file).map_err(from_parse_error)?;
for phdr in elf.segments() {
if phdr.p_type != PT_LOAD {
continue;
}
debugln!("LOAD {:#x}", phdr.p_vaddr);
if phdr.p_filesz > 0 {
load_bytes(
space,
phdr.p_vaddr as usize,
|off, dst| {
let mut source = file.file.borrow_mut();
source.seek(SeekFrom::Start(phdr.p_offset + off as u64))?;
source.read_exact(dst)
},
phdr.p_filesz as usize,
phdr.p_flags,
)?;
}
if phdr.p_memsz > phdr.p_filesz {
let addr = (phdr.p_vaddr + phdr.p_filesz) as usize;
let len = (phdr.p_memsz - phdr.p_filesz) as usize;
load_bytes(
space,
addr,
|_, dst| {
dst.fill(0);
Ok(())
},
len,
phdr.p_flags,
)?;
}
}
Ok(elf.ehdr.e_entry as usize)
}
/// Loads an ELF image into the address space from a slice
pub fn load_elf_from_memory(space: &mut AddressSpace, src: &[u8]) -> usize {
// Map the address space temporarily

View File

@ -69,6 +69,14 @@ impl<T> OneTimeInit<T> {
unsafe { (*self.value.get()).assume_init_ref() }
}
pub fn try_get(&self) -> Option<&T> {
if self.state.load(Ordering::Acquire) {
Some(self.get())
} else {
None
}
}
}
impl<T, const N: usize> StaticVector<T, N> {