diff --git a/kernel/comps/systree/src/utils.rs b/kernel/comps/systree/src/utils.rs index 77f240a2e..e451c6d59 100644 --- a/kernel/comps/systree/src/utils.rs +++ b/kernel/comps/systree/src/utils.rs @@ -617,10 +617,10 @@ macro_rules! inherit_sys_branch_node { } } - fn child(&self, name: &str) -> Option> { + fn child(&self, name: &str) -> Option> { self.$field .child(name) - .map(|child| child as Arc) + .map(|child| child as Arc) } fn create_child(&self, name: &str) -> $crate::Result> { diff --git a/kernel/src/fs/configfs/fs.rs b/kernel/src/fs/configfs/fs.rs new file mode 100644 index 000000000..26f3dfff2 --- /dev/null +++ b/kernel/src/fs/configfs/fs.rs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::sync::Arc; + +use aster_block::BlockDevice; +use aster_systree::SysNode; +use spin::Once; + +use super::inode::ConfigInode; +use crate::{ + fs::{ + configfs::systree_node::ConfigRootNode, + registry::{FsProperties, FsType}, + utils::{systree_inode::SysTreeInodeTy, FileSystem, FsFlags, Inode, SuperBlock}, + Result, + }, + prelude::*, +}; + +/// A file system that provides a user-space interface for configuring kernel objects. +/// +/// `ConfigFs` is a RAM-based file system that allows user-space applications to create, +/// configure, and manage kernel objects through a virtual file system interface. +/// Unlike sysfs which is primarily read-only and represents existing kernel state, +/// `ConfigFs` is designed for dynamic creation and configuration of kernel objects. +pub struct ConfigFs { + sb: SuperBlock, + root: Arc, +} + +// Magic number for `ConfigFs` (taken from Linux). +const MAGIC_NUMBER: u64 = 0x62656570; +const BLOCK_SIZE: usize = 4096; +const NAME_MAX: usize = 255; + +impl ConfigFs { + /// Returns the `CgroupFs` singleton. + pub(super) fn singleton() -> &'static Arc { + static SINGLETON: Once> = Once::new(); + + SINGLETON.call_once(|| Self::new(ConfigRootNode::singleton().clone())) + } + + fn new(root_node: Arc) -> Arc { + let sb = SuperBlock::new(MAGIC_NUMBER, BLOCK_SIZE, NAME_MAX); + let root_inode = ConfigInode::new_root(root_node); + + Arc::new(Self { + sb, + root: root_inode, + }) + } +} + +impl FileSystem for ConfigFs { + fn sync(&self) -> Result<()> { + // `ConfigFs` is volatile, sync is a no-op + Ok(()) + } + + fn root_inode(&self) -> Arc { + self.root.clone() + } + + fn sb(&self) -> SuperBlock { + self.sb.clone() + } + + fn flags(&self) -> FsFlags { + FsFlags::empty() + } +} + +pub(super) struct ConfigFsType; + +impl FsType for ConfigFsType { + fn name(&self) -> &'static str { + "configfs" + } + + fn properties(&self) -> FsProperties { + FsProperties::empty() + } + + fn create( + &self, + _args: Option, + _disk: Option>, + ) -> Result> { + Ok(ConfigFs::singleton().clone() as _) + } + + fn sysnode(&self) -> Option> { + None + } +} diff --git a/kernel/src/fs/configfs/inode.rs b/kernel/src/fs/configfs/inode.rs new file mode 100644 index 000000000..3b7abe4d6 --- /dev/null +++ b/kernel/src/fs/configfs/inode.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::sync::{Arc, Weak}; + +use ostd::sync::RwLock; + +use crate::{ + fs::{ + configfs::fs::ConfigFs, + utils::{ + systree_inode::{SysTreeInodeTy, SysTreeNodeKind}, + FileSystem, Inode, InodeMode, Metadata, + }, + }, + Result, +}; + +/// An inode abstraction used in the `ConfigFs`. +pub struct ConfigInode { + /// The corresponding node in the SysTree. + node_kind: SysTreeNodeKind, + /// The metadata of this inode. + metadata: Metadata, + /// The file mode (permissions) of this inode, protected by a lock. + mode: RwLock, + /// Weak reference to the parent inode. + parent: Weak, + /// Weak self-reference for cyclic data structures. + this: Weak, +} + +impl SysTreeInodeTy for ConfigInode { + fn new_arc( + node_kind: SysTreeNodeKind, + metadata: Metadata, + mode: InodeMode, + parent: Weak, + ) -> Arc + where + Self: Sized, + { + Arc::new_cyclic(|this| Self { + node_kind, + metadata, + mode: RwLock::new(mode), + parent, + this: this.clone(), + }) + } + + fn node_kind(&self) -> &SysTreeNodeKind { + &self.node_kind + } + + fn metadata(&self) -> &Metadata { + &self.metadata + } + + fn mode(&self) -> Result { + Ok(*self.mode.read()) + } + + fn set_mode(&self, mode: InodeMode) -> Result<()> { + *self.mode.write() = mode; + Ok(()) + } + + fn parent(&self) -> &Weak { + &self.parent + } + + fn this(&self) -> Arc { + self.this.upgrade().expect("Weak ref invalid") + } +} + +impl Inode for ConfigInode { + fn fs(&self) -> Arc { + ConfigFs::singleton().clone() + } +} diff --git a/kernel/src/fs/configfs/mod.rs b/kernel/src/fs/configfs/mod.rs new file mode 100644 index 000000000..1986ad1e1 --- /dev/null +++ b/kernel/src/fs/configfs/mod.rs @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::sync::Arc; + +use aster_systree::{EmptyNode, SysBranchNode}; +use systree_node::ConfigRootNode; + +use crate::{fs::configfs::fs::ConfigFsType, prelude::*}; + +mod fs; +mod inode; +mod systree_node; +#[cfg(ktest)] +mod test; + +// This method should be called during kernel file system initialization, +// _after_ `aster_systree::init`. +pub(super) fn init() { + let config_kernel_sysnode = EmptyNode::new("config".into()); + super::sysfs::register_kernel_sysnode(config_kernel_sysnode).unwrap(); + + super::registry::register(&ConfigFsType).unwrap(); +} + +/// Registers a subsystem `SysTree` node under the root node of [`ConfigFs`]. +/// +/// If a subsystem with the same name has already been registered, +/// this function returns an error. +pub fn register_subsystem(subsystem: Arc) -> Result<()> { + ConfigRootNode::singleton().add_child(subsystem)?; + + Ok(()) +} + +/// Unregisters a subsystem from the root node of [`ConfigFs`] by its name. +/// +/// If no subsystem with the given name exists, this function returns an error. +#[expect(dead_code)] +pub fn unregister_subsystem(name: &str) -> Result<()> { + ConfigRootNode::singleton().remove_child(name)?; + + Ok(()) +} + +#[cfg(ktest)] +pub fn init_for_ktest() { + aster_systree::init_for_ktest(); + super::registry::init(); + super::sysfs::init(); + init(); +} diff --git a/kernel/src/fs/configfs/systree_node.rs b/kernel/src/fs/configfs/systree_node.rs new file mode 100644 index 000000000..1f83dfcd3 --- /dev/null +++ b/kernel/src/fs/configfs/systree_node.rs @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::sync::{Arc, Weak}; +use core::fmt::Debug; + +use aster_systree::{ + inherit_sys_branch_node, BranchNodeFields, Result, SysAttrSet, SysBranchNode, SysObj, SysPerms, + SysStr, +}; +use inherit_methods_macro::inherit_methods; +use spin::Once; + +/// The `SysTree` node that represents the root node of the `ConfigFs`. +#[derive(Debug)] +pub struct ConfigRootNode { + fields: BranchNodeFields, +} + +#[inherit_methods(from = "self.fields")] +impl ConfigRootNode { + /// Returns the `ConfigRootNode` singleton. + pub(super) fn singleton() -> &'static Arc { + static SINGLETON: Once> = Once::new(); + + SINGLETON.call_once(Self::new) + } + + fn new() -> Arc { + let name = SysStr::from("config"); + + let attrs = SysAttrSet::new_empty(); + Arc::new_cyclic(|weak_self| { + let fields = BranchNodeFields::new(name, attrs, weak_self.clone()); + ConfigRootNode { fields } + }) + } + + /// Adds a child node. + pub fn add_child(&self, new_child: Arc) -> Result<()>; +} + +inherit_sys_branch_node!(ConfigRootNode, fields, { + fn is_root(&self) -> bool { + true + } + + fn init_parent(&self, _parent: Weak) { + // This method should be a no-op for `RootNode`. + } + + fn perms(&self) -> SysPerms { + SysPerms::DEFAULT_RW_PERMS + } +}); diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index 8c90e3d19..bcb580834 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 pub mod cgroupfs; +pub mod configfs; pub mod device; pub mod devpts; pub mod epoll; @@ -60,6 +61,7 @@ pub fn init() { sysfs::init(); procfs::init(); cgroupfs::init(); + configfs::init(); ramfs::init(); tmpfs::init(); devpts::init(); diff --git a/kernel/src/fs/sysfs/kernel.rs b/kernel/src/fs/sysfs/kernel.rs new file mode 100644 index 000000000..5a68f36c9 --- /dev/null +++ b/kernel/src/fs/sysfs/kernel.rs @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::sync::Arc; + +use aster_systree::{ + inherit_sys_branch_node, BranchNodeFields, Error, Result, SysAttrSetBuilder, SysBranchNode, + SysNode, SysPerms, SysStr, +}; +use inherit_methods_macro::inherit_methods; +use ostd::mm::{VmReader, VmWriter}; +use spin::Once; + +/// Registers a new kernel `SysNode`. +pub(super) fn register(config_obj: Arc) -> crate::Result<()> { + KERNEL_SYS_NODE_ROOT.get().unwrap().add_child(config_obj)?; + Ok(()) +} + +/// Unregisters a kernel `SysNode`. +pub(super) fn unregister(name: &str) -> crate::Result<()> { + let _ = KERNEL_SYS_NODE_ROOT.get().unwrap().remove_child(name)?; + Ok(()) +} + +pub(super) fn init() { + KERNEL_SYS_NODE_ROOT.call_once(|| { + let singleton = KernelSysNodeRoot::new(); + super::systree_singleton() + .root() + .add_child(singleton.clone()) + .unwrap(); + + singleton + }); +} + +static KERNEL_SYS_NODE_ROOT: Once> = Once::new(); + +/// A systree node representing the `/sys/kernel` directory. +/// +/// This node serves as the root for all kernel-related sysfs entries, +/// including kernel parameters, debugging interfaces, and various +/// kernel subsystem information. It corresponds to the `/kernel` +/// directory in the sysfs filesystem. +#[derive(Debug)] +pub struct KernelSysNodeRoot { + fields: BranchNodeFields, +} + +#[inherit_methods(from = "self.fields")] +impl KernelSysNodeRoot { + /// Creates a new `KernelSysNodeRoot` instance. + fn new() -> Arc { + let name = SysStr::from("kernel"); + let builder = SysAttrSetBuilder::new(); + // TODO: Add more kernel-specific attributes. + let attrs = builder + .build() + .expect("Failed to build kernel attribute set"); + Arc::new_cyclic(|weak_self| { + let fields = BranchNodeFields::new(name, attrs, weak_self.clone()); + KernelSysNodeRoot { fields } + }) + } + + /// Adds a kernel `SysNode` to this node. + fn add_child(&self, new_child: Arc) -> Result<()>; +} + +inherit_sys_branch_node!(KernelSysNodeRoot, fields, { + fn read_attr(&self, _name: &str, _writer: &mut VmWriter) -> Result { + // TODO: Add support for reading attributes. + Err(Error::AttributeError) + } + + fn write_attr(&self, _name: &str, _reader: &mut VmReader) -> Result { + // TODO: Add support for writing attributes. + Err(Error::AttributeError) + } + + fn perms(&self) -> SysPerms { + SysPerms::DEFAULT_RW_PERMS + } +}); diff --git a/kernel/src/fs/sysfs/mod.rs b/kernel/src/fs/sysfs/mod.rs index f830fc890..d03cfe4e2 100644 --- a/kernel/src/fs/sysfs/mod.rs +++ b/kernel/src/fs/sysfs/mod.rs @@ -2,14 +2,31 @@ mod fs; mod inode; +mod kernel; #[cfg(ktest)] mod test; pub use aster_systree::primary_tree as systree_singleton; +use aster_systree::SysNode; use fs::SysFsType; +use crate::prelude::*; + // This method should be called during kernel file system initialization, // _after_ `aster_systree::init`. pub fn init() { super::registry::register(&SysFsType).unwrap(); + + kernel::init(); +} + +/// Registers a new kernel `SysNode`. +pub fn register_kernel_sysnode(config_obj: Arc) -> Result<()> { + kernel::register(config_obj) +} + +/// Unregisters a kernel `SysNode`. +#[expect(dead_code)] +pub fn unregister_kernel_sysnode(name: &str) -> Result<()> { + kernel::unregister(name) } diff --git a/test/src/etc/profile.d/init.sh b/test/src/etc/profile.d/init.sh index 253d4b220..9ed2c2da6 100644 --- a/test/src/etc/profile.d/init.sh +++ b/test/src/etc/profile.d/init.sh @@ -6,4 +6,5 @@ # a generic init process. It should later be replaced by the actual init process. mount -t sysfs none /sys mount -t proc none /proc -mount -t cgroup2 none /sys/fs/cgroup \ No newline at end of file +mount -t cgroup2 none /sys/fs/cgroup +mount -t configfs none /sys/kernel/config \ No newline at end of file