fs: rework sysfs

This commit is contained in:
Mark Poliakov 2024-12-17 16:42:21 +02:00
parent 047746d134
commit cb5814a5ce
25 changed files with 662 additions and 138 deletions

1
Cargo.lock generated
View File

@ -458,6 +458,7 @@ dependencies = [
"device-api", "device-api",
"discrete_range_map", "discrete_range_map",
"fdt-rs", "fdt-rs",
"libk",
"libk-mm", "libk-mm",
"libk-util", "libk-util",
"log", "log",

View File

@ -12,7 +12,7 @@ use device_api::{
interrupt::{InterruptAffinity, InterruptHandler}, interrupt::{InterruptAffinity, InterruptHandler},
}; };
use error::AhciError; use error::AhciError;
use libk::{devfs, device::manager::probe_partitions, task::runtime}; use libk::{device::manager::probe_partitions, fs::devfs, task::runtime};
use libk_mm::{address::AsPhysicalAddress, device::DeviceMemoryIo, PageBox}; use libk_mm::{address::AsPhysicalAddress, device::DeviceMemoryIo, PageBox};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit}; use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use port::AhciPort; use port::AhciPort;

View File

@ -10,6 +10,7 @@ yggdrasil-abi.workspace = true
device-api = { path = "../device-api", features = ["derive"] } device-api = { path = "../device-api", features = ["derive"] }
libk-mm.workspace = true libk-mm.workspace = true
libk-util.workspace = true libk-util.workspace = true
libk.workspace = true
fdt-rs.workspace = true fdt-rs.workspace = true
log.workspace = true log.workspace = true

View File

@ -70,6 +70,11 @@ impl<'a> DeviceTree<'a> {
self.tree.totalsize() self.tree.totalsize()
} }
/// Returns the underlying FDT blob bytes
pub fn as_bytes(&self) -> &'a [u8] {
self.tree.buf()
}
/// Returns the root node of this device tree /// Returns the root node of this device tree
pub fn root(&self) -> TNode { pub fn root(&self) -> TNode {
self.index.root() self.index.root()
@ -171,3 +176,5 @@ impl<'a> DeviceTree<'a> {
DevTree::read_totalsize(header).map_err(|_| Error::InvalidArgument) DevTree::read_totalsize(header).map_err(|_| Error::InvalidArgument)
} }
} }
unsafe impl Sync for DeviceTree<'_> {}

View File

@ -1,5 +1,13 @@
//! Utility functions for device tree handling //! Utility functions for device tree handling
use fdt_rs::index::iters::DevTreeIndexNodeSiblingIter; use fdt_rs::index::iters::DevTreeIndexNodeSiblingIter;
use libk::{
error::Error,
fs::sysfs::{
self,
attribute::{BytesAttribute, BytesAttributeOps},
object::KObject,
},
};
use libk_mm::{address::PhysicalAddress, phys::PhysicalMemoryRegion}; use libk_mm::{address::PhysicalAddress, phys::PhysicalMemoryRegion};
use crate::{node::DeviceTreeNodeExt, property::DeviceTreePropertyRead, tree::DeviceTree}; use crate::{node::DeviceTreeNodeExt, property::DeviceTreePropertyRead, tree::DeviceTree};
@ -44,3 +52,35 @@ impl Iterator for DeviceTreeMemoryRegionIter<'_> {
} }
} }
} }
/// Registers sysfs objects related to the device tree
pub fn create_sysfs_nodes(dt: &'static DeviceTree) {
struct Raw;
impl BytesAttributeOps for Raw {
type Data = &'static DeviceTree<'static>;
const NAME: &'static str = "raw";
fn read(state: &Self::Data, pos: usize, buffer: &mut [u8]) -> Result<usize, Error> {
let data = state.as_bytes();
if pos >= data.len() {
return Ok(0);
}
let amount = (data.len() - pos).min(buffer.len());
buffer[..amount].copy_from_slice(&data[pos..pos + amount]);
Ok(amount)
}
fn size(state: &Self::Data) -> usize {
state.size()
}
}
if let Some(device) = sysfs::device() {
let object = KObject::new(dt);
object.add_attribute(BytesAttribute::from(Raw)).ok();
device.add_object("device-tree", object).ok();
}
}

View File

@ -2,7 +2,7 @@ use bytemuck::{Pod, Zeroable};
use crate::RawTable; use crate::RawTable;
pub const KERNEL_L3_COUNT: usize = 4; pub const KERNEL_L3_COUNT: usize = 8;
#[derive(Clone, Copy, Pod, Zeroable)] #[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)] #[repr(C)]

View File

@ -5,10 +5,15 @@
use core::{ use core::{
fmt::{self, Arguments}, fmt::{self, Arguments},
str::FromStr,
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
}; };
use alloc::sync::Arc; use alloc::{
format,
string::{String, ToString},
sync::Arc,
};
use libk_util::{ use libk_util::{
ring::RingBuffer, ring::RingBuffer,
sync::{ sync::{
@ -19,7 +24,14 @@ use libk_util::{
}; };
use yggdrasil_abi::error::Error; use yggdrasil_abi::error::Error;
use crate::task::{process::Process, thread::Thread}; use crate::{
fs::sysfs::{
self,
attribute::{StringAttribute, StringAttributeOps},
object::KObject,
},
task::{process::Process, thread::Thread},
};
const MAX_DEBUG_SINKS: usize = 8; const MAX_DEBUG_SINKS: usize = 8;
const RING_LOGGER_CAPACITY: usize = 65536; const RING_LOGGER_CAPACITY: usize = 65536;
@ -116,6 +128,12 @@ impl DebugSinkWrapper {
Self::Arc(level, _) => *level, Self::Arc(level, _) => *level,
} }
} }
pub fn set_level(&mut self, target: LogLevel) {
match self {
Self::Arc(level, _) => *level = target,
}
}
} }
impl log::Log for DebugSinkWrapper { impl log::Log for DebugSinkWrapper {
@ -315,9 +333,51 @@ static DEBUG_SINKS: IrqSafeRwLock<StaticVector<DebugSinkWrapper, MAX_DEBUG_SINKS
/// See [RingLoggerSink] /// See [RingLoggerSink]
pub static RING_LOGGER_SINK: RingLoggerSink = RingLoggerSink::new(); pub static RING_LOGGER_SINK: RingLoggerSink = RingLoggerSink::new();
fn make_sysfs_sink_object(index: usize) -> Arc<KObject<usize>> {
struct Level;
impl StringAttributeOps for Level {
type Data = usize;
const NAME: &'static str = "level";
const LIMIT: usize = 16;
const WRITEABLE: bool = true;
fn read(state: &Self::Data) -> Result<String, Error> {
let sinks = DEBUG_SINKS.read();
let sink = sinks.get(*state).ok_or(Error::InvalidFile)?;
Ok(sink.level().to_string())
}
fn write(state: &Self::Data, value: &str) -> Result<(), Error> {
let level = LogLevel::from_str(value)?;
let mut sinks = DEBUG_SINKS.write();
let sink = sinks.get_mut(*state).ok_or(Error::InvalidFile)?;
sink.set_level(level);
Ok(())
}
}
let object = KObject::new(index);
object.add_attribute(StringAttribute::from(Level)).ok();
object
}
/// Adds a debugging output sink /// Adds a debugging output sink
pub fn add_sink(sink: Arc<dyn DebugSink>, level: LogLevel) { pub fn add_sink(sink: Arc<dyn DebugSink>, level: LogLevel) {
DEBUG_SINKS.write().push(DebugSinkWrapper::Arc(level, sink)); let index = {
let mut sinks = DEBUG_SINKS.write();
let index = sinks.len();
sinks.push(DebugSinkWrapper::Arc(level, sink.clone()));
index
};
if let Some(debug) = sysfs::debug() {
debug
.add_object(format!("{index}"), make_sysfs_sink_object(index))
.ok();
}
} }
pub fn add_serial_sink(sink: Arc<dyn DebugSink>, level: LogLevel) { pub fn add_serial_sink(sink: Arc<dyn DebugSink>, level: LogLevel) {
@ -349,3 +409,34 @@ pub fn init() {
.map(|_| log::set_max_level(log::LevelFilter::Trace)) .map(|_| log::set_max_level(log::LevelFilter::Trace))
.ok(); .ok();
} }
impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let level = match self {
Self::Trace => "trace",
Self::Debug => "debug",
Self::Info => "info",
Self::Warning => "warn",
Self::Error => "error",
Self::Fatal => "fatal",
};
f.write_str(level)
}
}
impl FromStr for LogLevel {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"trace" | "t" => Ok(Self::Trace),
"debug" | "d" => Ok(Self::Debug),
"info" | "i" => Ok(Self::Info),
"warn" | "w" => Ok(Self::Warning),
"error" | "e" => Ok(Self::Error),
"fatal" | "f" => Ok(Self::Fatal),
_ => Err(Error::InvalidArgument),
}
}
}

View File

@ -10,7 +10,7 @@ use yggdrasil_abi::error::Error;
use crate::{ use crate::{
debug::{self, DebugSink, LogLevel}, debug::{self, DebugSink, LogLevel},
devfs, fs::devfs,
vfs::NodeRef, vfs::NodeRef,
}; };

View File

@ -0,0 +1,2 @@
pub mod devfs;
pub mod sysfs;

View File

@ -0,0 +1,128 @@
use core::{any::Any, marker::PhantomData};
use alloc::sync::Arc;
use yggdrasil_abi::{
error::Error,
io::{FileMode, OpenOptions},
};
use crate::{
fs::sysfs::object::KObject,
vfs::{CommonImpl, InstanceData, Node, NodeFlags, NodeRef, RegularImpl},
};
use super::Attribute;
pub trait BytesAttributeOps: Send + Sync + 'static {
type Data: Send + 'static = ();
const NAME: &'static str;
const WRITEABLE: bool = false;
fn read(state: &Self::Data, pos: usize, buffer: &mut [u8]) -> Result<usize, Error> {
let _ = state;
let _ = pos;
let _ = buffer;
Err(Error::NotImplemented)
}
fn write(state: &Self::Data, pos: usize, buffer: &[u8]) -> Result<usize, Error> {
let _ = state;
let _ = pos;
let _ = buffer;
Err(Error::ReadOnly)
}
fn size(state: &Self::Data) -> usize {
let _ = state;
0
}
}
pub struct BytesAttribute<V: BytesAttributeOps>(PhantomData<V>);
struct BytesAttributeNode<V: BytesAttributeOps> {
object: Arc<KObject<V::Data>>,
_pd: PhantomData<V>,
}
impl<V: BytesAttributeOps> CommonImpl for BytesAttributeNode<V> {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(V::size(&self.object.data) as u64)
}
fn as_any(&self) -> &dyn Any {
self as _
}
}
impl<V: BytesAttributeOps> RegularImpl for BytesAttributeNode<V> {
fn open(
&self,
_node: &NodeRef,
opts: OpenOptions,
) -> Result<(u64, Option<InstanceData>), Error> {
if opts.contains(OpenOptions::WRITE) && !V::WRITEABLE {
return Err(Error::ReadOnly);
}
Ok((0, None))
}
fn close(&self, _node: &NodeRef, _instance: Option<&InstanceData>) -> Result<(), Error> {
Ok(())
}
fn read(
&self,
_node: &NodeRef,
_instance: Option<&InstanceData>,
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
let pos = pos.try_into().map_err(|_| Error::InvalidArgument)?;
V::read(&self.object.data, pos, buf)
}
fn write(
&self,
_node: &NodeRef,
_instance: Option<&InstanceData>,
pos: u64,
buf: &[u8],
) -> Result<usize, Error> {
let pos = pos.try_into().map_err(|_| Error::InvalidArgument)?;
V::write(&self.object.data, pos, buf)
}
fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
Ok(())
}
}
impl<V: BytesAttributeOps> From<V> for BytesAttribute<V> {
fn from(_value: V) -> Self {
Self(PhantomData)
}
}
impl<V: BytesAttributeOps> Attribute<V::Data> for BytesAttribute<V> {
fn instantiate(&self, parent: &Arc<KObject<V::Data>>) -> Result<NodeRef, Error> {
let mode = match V::WRITEABLE {
false => FileMode::new(0o444),
true => FileMode::new(0o644),
};
Ok(Node::regular_kernel(
BytesAttributeNode {
object: parent.clone(),
_pd: PhantomData::<V>,
},
NodeFlags::IN_MEMORY_PROPS,
mode,
))
}
fn name(&self) -> &str {
V::NAME
}
}

View File

@ -0,0 +1,17 @@
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
use crate::vfs::NodeRef;
use super::object::KObject;
mod bytes;
mod string;
pub trait Attribute<D>: Sync + Send {
fn instantiate(&self, parent: &Arc<KObject<D>>) -> Result<NodeRef, Error>;
fn name(&self) -> &str;
}
pub use bytes::{BytesAttribute, BytesAttributeOps};
pub use string::{StringAttribute, StringAttributeOps};

View File

@ -0,0 +1,193 @@
use core::{
any::Any,
marker::PhantomData,
sync::atomic::{AtomicBool, Ordering},
};
use alloc::{string::String, sync::Arc, vec::Vec};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use yggdrasil_abi::{
error::Error,
io::{FileMode, OpenOptions},
};
use crate::{
fs::sysfs::object::KObject,
vfs::{CommonImpl, InstanceData, Node, NodeFlags, NodeRef, RegularImpl},
};
use super::Attribute;
pub trait StringAttributeOps: Sync + Send + 'static {
type Data: Send + 'static = ();
const WRITEABLE: bool = false;
const NAME: &'static str;
const LIMIT: usize = 4096;
fn read(state: &Self::Data) -> Result<String, Error> {
let _ = state;
Err(Error::NotImplemented)
}
fn write(state: &Self::Data, value: &str) -> Result<(), Error> {
let _ = state;
let _ = value;
Err(Error::ReadOnly)
}
}
pub struct StringAttribute<V: StringAttributeOps>(PhantomData<V>);
struct StringAttributeNode<V: StringAttributeOps> {
object: Arc<KObject<V::Data>>,
_pd: PhantomData<V>,
}
struct StringAttributeState {
value: IrqSafeRwLock<Vec<u8>>,
modified: AtomicBool,
}
impl<V: StringAttributeOps> CommonImpl for StringAttributeNode<V> {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(0)
}
fn as_any(&self) -> &dyn Any {
self as _
}
}
impl<V: StringAttributeOps> RegularImpl for StringAttributeNode<V> {
fn open(
&self,
_node: &NodeRef,
opts: OpenOptions,
) -> Result<(u64, Option<InstanceData>), Error> {
if opts.contains(OpenOptions::WRITE) && !V::WRITEABLE {
return Err(Error::ReadOnly);
}
let mut value = V::read(self.object.data())?.into_bytes();
value.push(b'\n');
let instance = StringAttributeState {
value: IrqSafeRwLock::new(value),
modified: AtomicBool::new(false),
};
Ok((0, Some(Arc::new(instance))))
}
fn close(&self, _node: &NodeRef, instance: Option<&InstanceData>) -> Result<(), Error> {
if V::WRITEABLE {
let instance = instance.ok_or(Error::InvalidFile)?;
let instance = instance
.downcast_ref::<StringAttributeState>()
.ok_or(Error::InvalidFile)?;
if instance.modified.load(Ordering::Acquire) {
let value = instance.value.read();
let value_str =
core::str::from_utf8(&value[..]).map_err(|_| Error::InvalidArgument)?;
// Trim whitespace and newlines
V::write(&self.object.data, value_str.trim())?;
}
}
Ok(())
}
fn read(
&self,
_node: &NodeRef,
instance: Option<&InstanceData>,
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
let instance = instance.ok_or(Error::InvalidFile)?;
let instance = instance
.downcast_ref::<StringAttributeState>()
.ok_or(Error::InvalidFile)?;
let value = instance.value.read();
let len = value.len();
if pos >= len as u64 {
return Ok(0);
}
let pos = pos as usize;
let amount = (len - pos).min(buf.len());
buf[..amount].copy_from_slice(&value[pos..pos + amount]);
Ok(amount)
}
fn write(
&self,
_node: &NodeRef,
instance: Option<&InstanceData>,
pos: u64,
buf: &[u8],
) -> Result<usize, Error> {
if !V::WRITEABLE {
return Err(Error::InvalidFile);
}
let instance = instance.ok_or(Error::InvalidFile)?;
let instance = instance
.downcast_ref::<StringAttributeState>()
.ok_or(Error::InvalidFile)?;
let mut value = instance.value.write();
let pos: usize = pos.try_into().map_err(|_| Error::InvalidFile)?;
if pos > value.len() {
return Err(Error::InvalidArgument);
}
if pos + buf.len() > V::LIMIT {
return Err(Error::InvalidArgument);
}
let amount_copy = (value.len() - pos).min(buf.len());
value[pos..pos + amount_copy].copy_from_slice(&buf[..amount_copy]);
if amount_copy < buf.len() {
value.extend_from_slice(&buf[amount_copy..]);
}
instance.modified.store(true, Ordering::Release);
Ok(buf.len())
}
fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
Ok(())
}
}
impl<V: StringAttributeOps> From<V> for StringAttribute<V> {
fn from(_value: V) -> Self {
Self(PhantomData)
}
}
impl<V: StringAttributeOps> Attribute<V::Data> for StringAttribute<V> {
fn instantiate(&self, parent: &Arc<KObject<V::Data>>) -> Result<Arc<Node>, Error> {
let mode = match V::WRITEABLE {
false => FileMode::new(0o444),
true => FileMode::new(0o644),
};
Ok(Node::regular_kernel(
StringAttributeNode {
object: parent.clone(),
_pd: PhantomData::<V>,
},
NodeFlags::IN_MEMORY_PROPS,
mode,
))
}
fn name(&self) -> &str {
V::NAME
}
}

View File

@ -0,0 +1,30 @@
use alloc::sync::Arc;
use libk_util::OneTimeInit;
use object::KObject;
use crate::vfs::NodeRef;
pub mod attribute;
pub mod object;
static ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
pub fn root() -> &'static NodeRef {
ROOT.get()
}
pub fn kernel() -> Option<&'static Arc<KObject<()>>> {
object::KERNEL_OBJECT.try_get()
}
pub fn debug() -> Option<&'static Arc<KObject<()>>> {
object::DEBUG_OBJECT.try_get()
}
pub fn device() -> Option<&'static Arc<KObject<()>>> {
object::DEVICE_OBJECT.try_get()
}
pub fn init() {
ROOT.init(object::setup_fixed_objects());
}

View File

@ -0,0 +1,69 @@
use alloc::{string::String, sync::Arc};
use libk_util::OneTimeInit;
use yggdrasil_abi::{error::Error, io::FileMode};
use crate::vfs::{impls::MemoryDirectory, Node, NodeFlags};
use super::attribute::Attribute;
pub struct KObject<D> {
pub(super) data: D,
pub(super) node: Arc<Node>,
}
impl<D> KObject<D> {
pub fn new(data: D) -> Arc<Self> {
let node = Node::directory_kernel(
MemoryDirectory,
NodeFlags::IN_MEMORY_SIZE | NodeFlags::IN_MEMORY_PROPS,
FileMode::new(0o555),
);
Arc::new(Self { data, node })
}
pub fn add_attribute<A: Attribute<D>>(self: &Arc<Self>, attribute: A) -> Result<(), Error> {
let node = attribute.instantiate(self)?;
self.node.add_child(attribute.name(), node)?;
Ok(())
}
pub fn add_object<S: Into<String>, T>(
&self,
name: S,
child: Arc<KObject<T>>,
) -> Result<(), Error> {
self.node.add_child(name, child.node.clone())
}
pub(super) fn data(&self) -> &D {
&self.data
}
}
unsafe impl<D: Send> Send for KObject<D> {}
unsafe impl<D: Send> Sync for KObject<D> {}
// Static, fixed objects
// `/`
pub static ROOT_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
// `/kernel`
pub static KERNEL_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
// `/device`
pub static DEVICE_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
// `/debug`
pub static DEBUG_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
fn setup_fixed_object(root: &Arc<KObject<()>>, obj: &OneTimeInit<Arc<KObject<()>>>, name: &str) {
let obj = obj.init(KObject::new(()));
root.add_object(name, obj.clone()).ok();
}
pub fn setup_fixed_objects() -> Arc<Node> {
let root = ROOT_OBJECT.init(KObject::new(()));
setup_fixed_object(root, &KERNEL_OBJECT, "kernel");
setup_fixed_object(root, &DEVICE_OBJECT, "device");
setup_fixed_object(root, &DEBUG_OBJECT, "debug");
root.node.clone()
}

View File

@ -36,8 +36,8 @@ pub mod task;
pub mod arch; pub mod arch;
pub mod debug; pub mod debug;
pub mod devfs;
pub mod device; pub mod device;
pub mod fs;
pub mod module; pub mod module;
pub mod random; pub mod random;
pub mod time; pub mod time;

View File

@ -175,14 +175,30 @@ impl Node {
(master, slave) (master, slave)
} }
/// Creates a new directory node with given [DirectoryImpl] /// Creates a new directory node with given [DirectoryImpl] and permissions
pub fn directory<T: DirectoryImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef { pub fn directory_kernel<T: DirectoryImpl + 'static>(
data: T,
flags: NodeFlags,
mode: FileMode,
) -> NodeRef {
let data = NodeImpl::Directory(DirectoryData { let data = NodeImpl::Directory(DirectoryData {
imp: Box::new(data), imp: Box::new(data),
mountpoint: IrqSafeSpinlock::new(None), mountpoint: IrqSafeSpinlock::new(None),
children: IrqSafeSpinlock::new(Vec::new()), children: IrqSafeSpinlock::new(Vec::new()),
}); });
Self::new(data, flags, Metadata::default_dir()) Self::new(
data,
flags,
Metadata {
mode,
..Metadata::default_dir()
},
)
}
/// Creates a new directory node with given [DirectoryImpl]
pub fn directory<T: DirectoryImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
Self::directory_kernel(data, flags, FileMode::default_dir())
} }
/// Creates a new file node with given [RegularImpl] and permissions /// Creates a new file node with given [RegularImpl] and permissions
@ -196,11 +212,7 @@ impl Node {
flags, flags,
Metadata { Metadata {
mode, mode,
uid: UserId::root(), ..Metadata::default_file()
gid: GroupId::root(),
block_size: 0,
block_count: 0,
inode: None,
}, },
) )
} }

View File

@ -13,7 +13,10 @@ use kernel_arch_aarch64::{
mem::{self, table::L3}, mem::{self, table::L3},
CPU_COUNT, CPU_COUNT,
}; };
use libk::{devfs, task::runtime}; use libk::{
fs::{devfs, sysfs},
task::runtime,
};
use libk_mm::{ use libk_mm::{
address::{PhysicalAddress, Virtualize}, address::{PhysicalAddress, Virtualize},
phys, phys,
@ -121,6 +124,7 @@ unsafe extern "C" fn __aarch64_bsp_upper_entry(dtb: PhysicalAddress) -> ! {
exception::init_exceptions(); exception::init_exceptions();
sysfs::init();
devfs::init(); devfs::init();
runtime::init_task_queue(); runtime::init_task_queue();

View File

@ -284,6 +284,9 @@ impl AArch64 {
let dt = self.dt.get(); let dt = self.dt.get();
// Create device tree sysfs nodes
device_tree::util::create_sysfs_nodes(dt);
let (machine_compatible, machine_name) = Self::machine_name(dt); let (machine_compatible, machine_name) = Self::machine_name(dt);
if let Some(compatible) = machine_compatible { if let Some(compatible) = machine_compatible {

View File

@ -158,7 +158,7 @@ impl Device for Pl011 {
DEVICE_REGISTRY DEVICE_REGISTRY
.serial_terminal .serial_terminal
.register(terminal.clone(), Some((self.clone(), LogLevel::Debug))) .register(terminal.clone(), Some((self.clone(), LogLevel::Info)))
.ok(); .ok();
Ok(()) Ok(())

View File

@ -5,7 +5,8 @@ use core::ptr::NonNull;
use abi::path::Path; use abi::path::Path;
use ext2::Ext2Fs; use ext2::Ext2Fs;
use libk::{ use libk::{
block, devfs, block,
fs::{devfs, sysfs},
vfs::{self, register_root, IoContext, NodeRef}, vfs::{self, register_root, IoContext, NodeRef},
}; };
use libk_mm::{ use libk_mm::{
@ -21,7 +22,7 @@ use yggdrasil_abi::{error::Error, io::MountOptions};
pub use pseudo::add_pseudo_devices; pub use pseudo::add_pseudo_devices;
pub mod pseudo; pub mod pseudo;
pub mod sysfs; // pub mod sysfs;
/// Describes in-memory filesystem image used as initial root /// Describes in-memory filesystem image used as initial root
pub struct Initrd { pub struct Initrd {

View File

@ -6,8 +6,8 @@ use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait; use async_trait::async_trait;
use device_api::device::Device; use device_api::device::Device;
use libk::{ use libk::{
devfs,
device::char::CharDevice, device::char::CharDevice,
fs::devfs,
random, random,
vfs::{impls::read_fn_node, FileReadiness}, vfs::{impls::read_fn_node, FileReadiness},
}; };

View File

@ -1,112 +0,0 @@
//! "System" filesystem implementation
use core::sync::atomic::Ordering;
use abi::error::Error;
use alloc::{format, string::String};
use git_version::git_version;
use kernel_arch_interface::cpu::CpuFeatureSet;
use libk::{
arch::Cpu,
debug,
task::{cpu_count, sched},
vfs::{
impls::{const_value_node, mdir, read_fn_node, ReadOnlyFnValueNode},
NodeRef,
},
};
use libk_mm::phys;
use libk_util::OneTimeInit;
use crate::util;
static ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
/// Returns the root of the filesystem
pub fn root() -> &'static NodeRef {
ROOT.get()
}
fn read_kernel_log(pos: u64, buffer: &mut [u8]) -> Result<usize, Error> {
Ok(debug::RING_LOGGER_SINK.read(pos as usize, buffer))
}
fn feature_list<C: CpuFeatureSet>(features: Option<&C>) -> String {
let mut list = String::new();
if let Some(features) = features {
let mut cnt = 0;
features.iter().for_each(|feature| {
if cnt != 0 {
list.push(' ');
}
list.push_str(feature);
cnt += 1;
});
}
list
}
fn sched_stats() -> String {
let mut res = String::new();
for i in 0..cpu_count() {
let stats = sched::stats(i);
res.push_str(&format!(
"{} {} {} {}",
stats.idle.load(Ordering::Relaxed),
stats.user.load(Ordering::Relaxed),
stats.kernel.load(Ordering::Relaxed),
stats.total.load(Ordering::Relaxed)
));
}
res
}
/// Sets up the entries within the filesystem
pub fn init() {
let d_kernel = mdir([
("version", const_value_node(env!("CARGO_PKG_VERSION"))),
("rev", const_value_node(git_version!())),
("log", read_fn_node(read_kernel_log)),
]);
let d_proc = mdir([(
"sched_stats",
ReadOnlyFnValueNode::new_node(|| Ok(sched_stats())),
)]);
let d_mem_phys = mdir([
(
"total_pages",
ReadOnlyFnValueNode::new_node(|| Ok(phys::stats().total_usable_pages)),
),
(
"free_pages",
ReadOnlyFnValueNode::new_node(|| Ok(phys::stats().free_pages)),
),
(
"allocated_pages",
ReadOnlyFnValueNode::new_node(|| Ok(phys::stats().allocated_pages)),
),
]);
let d_mem = mdir([("phys", d_mem_phys)]);
// TODO per-CPU entries
let d_cpu_features = mdir([
(
"enabled",
ReadOnlyFnValueNode::new_node(|| Ok(feature_list(Cpu::local().enabled_features()))),
),
(
"available",
ReadOnlyFnValueNode::new_node(|| Ok(feature_list(Cpu::local().available_features()))),
),
]);
let d_cpu = mdir([("features", d_cpu_features)]);
let root = mdir([
("kernel", d_kernel),
("cpu", d_cpu),
("mem", d_mem),
("proc", d_proc),
("arch", const_value_node(util::arch_str())),
("machine", const_value_node(util::machine_name())),
]);
ROOT.init(root);
}

View File

@ -2,8 +2,8 @@
use abi::error::Error; use abi::error::Error;
use libk::{ use libk::{
devfs,
device::display::console, device::display::console,
fs::devfs,
random, random,
task::{binary::LoadOptions, process::Process, runtime, thread::Thread}, task::{binary::LoadOptions, process::Process, runtime, thread::Thread},
vfs::{impls::fn_symlink, IoContext, NodeRef}, vfs::{impls::fn_symlink, IoContext, NodeRef},

View File

@ -20,7 +20,8 @@
exact_size_is_empty, exact_size_is_empty,
maybe_uninit_uninit_array, maybe_uninit_uninit_array,
never_type, never_type,
format_args_nl format_args_nl,
associated_type_defaults
)] )]
#![allow( #![allow(
clippy::new_without_default, clippy::new_without_default,
@ -34,12 +35,21 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use alloc::borrow::ToOwned; use abi::error::Error;
use alloc::{borrow::ToOwned, string::String};
use arch::Platform; use arch::Platform;
use fs::sysfs;
use git_version::git_version; use git_version::git_version;
use kernel_arch::{Architecture, ArchitectureImpl}; use kernel_arch::{Architecture, ArchitectureImpl};
use libk::{arch::Cpu, devfs}; use libk::{
arch::Cpu,
fs::{
devfs,
sysfs::{
self,
attribute::{StringAttribute, StringAttributeOps},
},
},
};
use libk_util::sync::SpinFence; use libk_util::sync::SpinFence;
use crate::{arch::PLATFORM, task::spawn_kernel_closure}; use crate::{arch::PLATFORM, task::spawn_kernel_closure};
@ -47,6 +57,7 @@ use crate::{arch::PLATFORM, task::spawn_kernel_closure};
extern crate yggdrasil_abi as abi; extern crate yggdrasil_abi as abi;
extern crate alloc; extern crate alloc;
#[cfg(not(rust_analyzer))]
extern crate compiler_builtins; extern crate compiler_builtins;
#[macro_use] #[macro_use]
@ -75,6 +86,32 @@ pub fn kernel_secondary_main() -> ! {
} }
} }
fn register_sysfs_attributes() {
struct Version;
struct Arch;
impl StringAttributeOps for Version {
const NAME: &'static str = "version";
fn read(_state: &Self::Data) -> Result<String, Error> {
Ok(git_version!().into())
}
}
impl StringAttributeOps for Arch {
const NAME: &'static str = "arch";
fn read(_state: &Self::Data) -> Result<String, Error> {
Ok(util::arch_str().into())
}
}
let kernel = sysfs::kernel().unwrap();
kernel.add_attribute(StringAttribute::from(Version)).ok();
kernel.add_attribute(StringAttribute::from(Arch)).ok();
}
/// Common kernel main function. Must be called for BSP processor only. /// Common kernel main function. Must be called for BSP processor only.
/// ///
/// # Prerequisites /// # Prerequisites
@ -103,7 +140,7 @@ pub fn kernel_main() -> ! {
Cpu::init_ipi_queues(ArchitectureImpl::cpu_count()); Cpu::init_ipi_queues(ArchitectureImpl::cpu_count());
// Setup the sysfs // Setup the sysfs
sysfs::init(); register_sysfs_attributes();
fs::add_pseudo_devices().unwrap(); fs::add_pseudo_devices().unwrap();
// Wait until all APs initialize // Wait until all APs initialize