diff --git a/src/arch/aarch64/start.rs b/src/arch/aarch64/start.rs index 8d09be5780..659fb20358 100644 --- a/src/arch/aarch64/start.rs +++ b/src/arch/aarch64/start.rs @@ -9,7 +9,7 @@ use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use crate::memory::{Frame}; use crate::paging::{Page, PAGE_SIZE, PhysicalAddress, VirtualAddress}; -use crate::allocator; +use crate::{allocator, dtb}; use crate::device; #[cfg(feature = "graphical_debug")] use crate::devices::graphical_debug; @@ -154,6 +154,8 @@ pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! { // Initialize devices device::init(); + dtb::init(Some((crate::PHYS_OFFSET + args.dtb_base, args.dtb_size))); + // Initialize all of the non-core devices not otherwise needed to complete initialization device::init_noncore(); diff --git a/src/dtb/mod.rs b/src/dtb/mod.rs new file mode 100644 index 0000000000..a13b92c7cc --- /dev/null +++ b/src/dtb/mod.rs @@ -0,0 +1,23 @@ +use core::slice; + +use alloc::vec::Vec; +use spin::once::Once; + +pub static DTB_BINARY: Once> = Once::new(); + +pub unsafe fn init(dtb: Option<(usize, usize)>) { + let mut initialized = false; + DTB_BINARY.call_once(|| { + initialized = true; + + let mut binary = Vec::new(); + if let Some((dtb_base, dtb_size)) = dtb { + let data = unsafe { slice::from_raw_parts(dtb_base as *const u8, dtb_size) }; + binary.extend(data); + }; + binary + }); + if !initialized { + println!("DTB_BINARY INIT TWICE!"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 40995f8eaa..110bfcb9b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,9 @@ pub mod allocator; #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] mod acpi; +#[cfg(all(any(target_arch = "aarch64")))] +mod dtb; + /// Context management pub mod context; diff --git a/src/scheme/dtb.rs b/src/scheme/dtb.rs new file mode 100644 index 0000000000..c8bb25be02 --- /dev/null +++ b/src/scheme/dtb.rs @@ -0,0 +1,181 @@ +use core::sync::atomic::{self, AtomicUsize}; + +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use spin::{Once, RwLock}; + +use crate::dtb::DTB_BINARY; +use crate::scheme::SchemeId; +use crate::syscall::flag::{ + MODE_FILE, + O_STAT, + SEEK_CUR, SEEK_END, SEEK_SET, +}; +use crate::syscall::data::Stat; +use crate::syscall::scheme::Scheme; +use crate::syscall::error::*; +use crate::syscall::usercopy::{UserSliceRo, UserSliceWo}; +use super::KernelScheme; + + +pub struct DtbScheme; + +#[derive(Eq, PartialEq)] +enum HandleKind { + RawData, +} + +struct Handle { + offset: usize, + kind: HandleKind, + stat: bool, +} + +static HANDLES: RwLock> = RwLock::new(BTreeMap::new()); +static NEXT_FD: AtomicUsize = AtomicUsize::new(0); +static DATA: Once> = Once::new(); +static SCHEME_ID: Once = Once::new(); + +impl DtbScheme { + pub fn new(id: SchemeId) -> Self { + let mut id_init = false; + let mut data_init = false; + + DATA.call_once(|| { + data_init = true; + + let dtb = match DTB_BINARY.get() { + Some(dtb) => dtb.as_slice(), + None => &[] + }; + + Box::from(dtb) + }); + + SCHEME_ID.call_once(|| { + id_init = true; + + id + }); + if !data_init || !id_init { + log::error!("DtbScheme::new called multiple times"); + } + + Self + } +} + +impl Scheme for DtbScheme { + fn open(&self, path: &str, _flags: usize, uid: u32, _gid: u32) -> Result { + + if uid != 0 { + return Err(Error::new(EACCES)); + } + + let path = path.trim_matches('/'); + + if path.is_empty() { + let id = NEXT_FD.fetch_add(1, atomic::Ordering::Relaxed); + + let mut handles_guard = HANDLES.write(); + + let _ = handles_guard.insert(id, Handle { + offset: 0, + kind: HandleKind::RawData, + stat: _flags & O_STAT == O_STAT, + }); + return Ok(id) + } + + Err(Error::new(ENOENT)) + } + + fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { + let mut handles = HANDLES.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + if handle.stat { + return Err(Error::new(EBADF)); + } + + let file_len = match handle.kind { + HandleKind::RawData => DATA.get().ok_or(Error::new(EBADFD))?.len(), + }; + + let new_offset = match whence { + SEEK_SET => pos as usize, + SEEK_CUR => if pos < 0 { + handle.offset.checked_sub((-pos) as usize).ok_or(Error::new(EINVAL))? + } else { + handle.offset.saturating_add(pos as usize) + } + SEEK_END => if pos < 0 { + file_len.checked_sub((-pos) as usize).ok_or(Error::new(EINVAL))? + } else { + file_len + } + _ => return Err(Error::new(EINVAL)), + }; + + handle.offset = new_offset; + + Ok(new_offset as isize) + } + + fn close(&self, id: usize) -> Result { + if HANDLES.write().remove(&id).is_none() { + return Err(Error::new(EBADF)); + } + Ok(0) + } +} + +impl KernelScheme for DtbScheme { + + fn kwrite(&self, _id: usize, _buf: UserSliceRo) -> Result { + Err(Error::new(EBADF)) + } + + fn kread(&self, id: usize, dst_buf: UserSliceWo) -> Result { + let mut handles = HANDLES.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + if handle.stat { + return Err(Error::new(EBADF)); + } + + let data = match handle.kind { + HandleKind::RawData => DATA.get().ok_or(Error::new(EBADFD))?, + }; + + let src_offset = core::cmp::min(handle.offset, data.len()); + let src_buf = data + .get(src_offset..) + .expect("expected data to be at least data.len() bytes long"); + + let bytes_copied = dst_buf.copy_common_bytes_from_slice(src_buf)?; + handle.offset += bytes_copied; + + Ok(bytes_copied) + } + + fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result { + + let handles = HANDLES.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + buf.copy_exactly(&match handle.kind { + HandleKind::RawData => { + let data = DATA.get().ok_or(Error::new(EBADFD))?; + Stat { + st_mode: MODE_FILE, + st_uid: 0, + st_gid: 0, + st_size: data.len().try_into().unwrap_or(u64::max_value()), + ..Default::default() + } + } + })?; + + Ok(0) + } +} diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index a5d7767539..6418b8f023 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -25,6 +25,8 @@ use crate::syscall::usercopy::{UserSliceRo, UserSliceWo}; #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] use self::acpi::AcpiScheme; +#[cfg(all(any(target_arch = "aarch64")))] +use self::dtb::DtbScheme; use self::debug::DebugScheme; use self::event::EventScheme; @@ -42,6 +44,8 @@ use self::user::UserInner; /// When compiled with the "acpi" feature - `acpi:` - allows drivers to read a limited set of ACPI tables. #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] pub mod acpi; +#[cfg(all(any(target_arch = "aarch64")))] +pub mod dtb; /// `debug:` - provides access to serial console pub mod debug; @@ -164,6 +168,9 @@ impl SchemeList { #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] { self.insert(ns, "kernel/acpi", |scheme_id| Arc::new(AcpiScheme::new(scheme_id))).unwrap(); } + #[cfg(all(any(target_arch = "aarch64")))] { + self.insert(ns, "kernel/dtb", |scheme_id| Arc::new(DtbScheme::new(scheme_id))).unwrap(); + } self.insert(ns, "debug", |scheme_id| Arc::new(DebugScheme::new(scheme_id))).unwrap(); self.insert(ns, "irq", |scheme_id| Arc::new(IrqScheme::new(scheme_id))).unwrap(); self.insert(ns, "proc", |scheme_id| Arc::new(ProcScheme::new(scheme_id))).unwrap();