fs: rework sysfs
This commit is contained in:
parent
047746d134
commit
cb5814a5ce
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -458,6 +458,7 @@ dependencies = [
|
||||
"device-api",
|
||||
"discrete_range_map",
|
||||
"fdt-rs",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
|
@ -12,7 +12,7 @@ use device_api::{
|
||||
interrupt::{InterruptAffinity, InterruptHandler},
|
||||
};
|
||||
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_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use port::AhciPort;
|
||||
|
@ -10,6 +10,7 @@ yggdrasil-abi.workspace = true
|
||||
device-api = { path = "../device-api", features = ["derive"] }
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
|
||||
fdt-rs.workspace = true
|
||||
log.workspace = true
|
||||
|
@ -70,6 +70,11 @@ impl<'a> DeviceTree<'a> {
|
||||
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
|
||||
pub fn root(&self) -> TNode {
|
||||
self.index.root()
|
||||
@ -171,3 +176,5 @@ impl<'a> DeviceTree<'a> {
|
||||
DevTree::read_totalsize(header).map_err(|_| Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for DeviceTree<'_> {}
|
||||
|
@ -1,5 +1,13 @@
|
||||
//! Utility functions for device tree handling
|
||||
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 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();
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use crate::RawTable;
|
||||
|
||||
pub const KERNEL_L3_COUNT: usize = 4;
|
||||
pub const KERNEL_L3_COUNT: usize = 8;
|
||||
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
|
@ -5,10 +5,15 @@
|
||||
|
||||
use core::{
|
||||
fmt::{self, Arguments},
|
||||
str::FromStr,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{
|
||||
format,
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
};
|
||||
use libk_util::{
|
||||
ring::RingBuffer,
|
||||
sync::{
|
||||
@ -19,7 +24,14 @@ use libk_util::{
|
||||
};
|
||||
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 RING_LOGGER_CAPACITY: usize = 65536;
|
||||
@ -116,6 +128,12 @@ impl DebugSinkWrapper {
|
||||
Self::Arc(level, _) => *level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_level(&mut self, target: LogLevel) {
|
||||
match self {
|
||||
Self::Arc(level, _) => *level = target,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for DebugSinkWrapper {
|
||||
@ -315,9 +333,51 @@ static DEBUG_SINKS: IrqSafeRwLock<StaticVector<DebugSinkWrapper, MAX_DEBUG_SINKS
|
||||
/// See [RingLoggerSink]
|
||||
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
|
||||
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) {
|
||||
@ -349,3 +409,34 @@ pub fn init() {
|
||||
.map(|_| log::set_max_level(log::LevelFilter::Trace))
|
||||
.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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
debug::{self, DebugSink, LogLevel},
|
||||
devfs,
|
||||
fs::devfs,
|
||||
vfs::NodeRef,
|
||||
};
|
||||
|
||||
|
2
kernel/libk/src/fs/mod.rs
Normal file
2
kernel/libk/src/fs/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod devfs;
|
||||
pub mod sysfs;
|
128
kernel/libk/src/fs/sysfs/attribute/bytes.rs
Normal file
128
kernel/libk/src/fs/sysfs/attribute/bytes.rs
Normal 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
|
||||
}
|
||||
}
|
17
kernel/libk/src/fs/sysfs/attribute/mod.rs
Normal file
17
kernel/libk/src/fs/sysfs/attribute/mod.rs
Normal 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};
|
193
kernel/libk/src/fs/sysfs/attribute/string.rs
Normal file
193
kernel/libk/src/fs/sysfs/attribute/string.rs
Normal 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
|
||||
}
|
||||
}
|
30
kernel/libk/src/fs/sysfs/mod.rs
Normal file
30
kernel/libk/src/fs/sysfs/mod.rs
Normal 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());
|
||||
}
|
69
kernel/libk/src/fs/sysfs/object.rs
Normal file
69
kernel/libk/src/fs/sysfs/object.rs
Normal 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()
|
||||
}
|
@ -36,8 +36,8 @@ pub mod task;
|
||||
|
||||
pub mod arch;
|
||||
pub mod debug;
|
||||
pub mod devfs;
|
||||
pub mod device;
|
||||
pub mod fs;
|
||||
pub mod module;
|
||||
pub mod random;
|
||||
pub mod time;
|
||||
|
@ -175,14 +175,30 @@ impl Node {
|
||||
(master, slave)
|
||||
}
|
||||
|
||||
/// Creates a new directory node with given [DirectoryImpl]
|
||||
pub fn directory<T: DirectoryImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
|
||||
/// Creates a new directory node with given [DirectoryImpl] and permissions
|
||||
pub fn directory_kernel<T: DirectoryImpl + 'static>(
|
||||
data: T,
|
||||
flags: NodeFlags,
|
||||
mode: FileMode,
|
||||
) -> NodeRef {
|
||||
let data = NodeImpl::Directory(DirectoryData {
|
||||
imp: Box::new(data),
|
||||
mountpoint: IrqSafeSpinlock::new(None),
|
||||
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
|
||||
@ -196,11 +212,7 @@ impl Node {
|
||||
flags,
|
||||
Metadata {
|
||||
mode,
|
||||
uid: UserId::root(),
|
||||
gid: GroupId::root(),
|
||||
block_size: 0,
|
||||
block_count: 0,
|
||||
inode: None,
|
||||
..Metadata::default_file()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -13,7 +13,10 @@ use kernel_arch_aarch64::{
|
||||
mem::{self, table::L3},
|
||||
CPU_COUNT,
|
||||
};
|
||||
use libk::{devfs, task::runtime};
|
||||
use libk::{
|
||||
fs::{devfs, sysfs},
|
||||
task::runtime,
|
||||
};
|
||||
use libk_mm::{
|
||||
address::{PhysicalAddress, Virtualize},
|
||||
phys,
|
||||
@ -121,6 +124,7 @@ unsafe extern "C" fn __aarch64_bsp_upper_entry(dtb: PhysicalAddress) -> ! {
|
||||
|
||||
exception::init_exceptions();
|
||||
|
||||
sysfs::init();
|
||||
devfs::init();
|
||||
|
||||
runtime::init_task_queue();
|
||||
|
@ -284,6 +284,9 @@ impl AArch64 {
|
||||
|
||||
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);
|
||||
|
||||
if let Some(compatible) = machine_compatible {
|
||||
|
@ -158,7 +158,7 @@ impl Device for Pl011 {
|
||||
|
||||
DEVICE_REGISTRY
|
||||
.serial_terminal
|
||||
.register(terminal.clone(), Some((self.clone(), LogLevel::Debug)))
|
||||
.register(terminal.clone(), Some((self.clone(), LogLevel::Info)))
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
|
@ -5,7 +5,8 @@ use core::ptr::NonNull;
|
||||
use abi::path::Path;
|
||||
use ext2::Ext2Fs;
|
||||
use libk::{
|
||||
block, devfs,
|
||||
block,
|
||||
fs::{devfs, sysfs},
|
||||
vfs::{self, register_root, IoContext, NodeRef},
|
||||
};
|
||||
use libk_mm::{
|
||||
@ -21,7 +22,7 @@ use yggdrasil_abi::{error::Error, io::MountOptions};
|
||||
pub use pseudo::add_pseudo_devices;
|
||||
|
||||
pub mod pseudo;
|
||||
pub mod sysfs;
|
||||
// pub mod sysfs;
|
||||
|
||||
/// Describes in-memory filesystem image used as initial root
|
||||
pub struct Initrd {
|
||||
|
@ -6,8 +6,8 @@ use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use device_api::device::Device;
|
||||
use libk::{
|
||||
devfs,
|
||||
device::char::CharDevice,
|
||||
fs::devfs,
|
||||
random,
|
||||
vfs::{impls::read_fn_node, FileReadiness},
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
|
||||
use abi::error::Error;
|
||||
use libk::{
|
||||
devfs,
|
||||
device::display::console,
|
||||
fs::devfs,
|
||||
random,
|
||||
task::{binary::LoadOptions, process::Process, runtime, thread::Thread},
|
||||
vfs::{impls::fn_symlink, IoContext, NodeRef},
|
||||
|
@ -20,7 +20,8 @@
|
||||
exact_size_is_empty,
|
||||
maybe_uninit_uninit_array,
|
||||
never_type,
|
||||
format_args_nl
|
||||
format_args_nl,
|
||||
associated_type_defaults
|
||||
)]
|
||||
#![allow(
|
||||
clippy::new_without_default,
|
||||
@ -34,12 +35,21 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use abi::error::Error;
|
||||
use alloc::{borrow::ToOwned, string::String};
|
||||
use arch::Platform;
|
||||
use fs::sysfs;
|
||||
use git_version::git_version;
|
||||
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 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 alloc;
|
||||
#[cfg(not(rust_analyzer))]
|
||||
extern crate compiler_builtins;
|
||||
|
||||
#[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.
|
||||
///
|
||||
/// # Prerequisites
|
||||
@ -103,7 +140,7 @@ pub fn kernel_main() -> ! {
|
||||
Cpu::init_ipi_queues(ArchitectureImpl::cpu_count());
|
||||
|
||||
// Setup the sysfs
|
||||
sysfs::init();
|
||||
register_sysfs_attributes();
|
||||
fs::add_pseudo_devices().unwrap();
|
||||
|
||||
// Wait until all APs initialize
|
||||
|
Loading…
x
Reference in New Issue
Block a user