Merge pull request #197 from arihant2math/sysfs

This commit is contained in:
Ashwin Naren
2026-02-09 13:59:28 -08:00
committed by GitHub
6 changed files with 413 additions and 1 deletions

View File

@@ -51,6 +51,8 @@ bitflags::bitflags! {
// Reserved psuedo filesystem instances created internally in the kernel.
pub const DEVFS_ID: u64 = 1;
pub const PROCFS_ID: u64 = 2;
pub const SYSFS_ID: u64 = 3;
pub const CGROUPFS_ID: u64 = 4;
pub const FS_ID_START: u64 = 10;
/// Trait for a mounted filesystem instance. Its main role is to act as a

View File

@@ -23,4 +23,4 @@ bin="${elf%.elf}.bin"
# Convert to binary format
aarch64-none-elf-objcopy -O binary "$elf" "$bin"
qemu-system-aarch64 -M virt,gic-version=3 -initrd moss.img -cpu cortex-a72 -m 2G -smp 4 -nographic -s -kernel "$bin" -append "$append_args --rootfs=ext4fs --automount=/dev,devfs --automount=/tmp,tmpfs --automount=/proc,procfs"
qemu-system-aarch64 -M virt,gic-version=3 -initrd moss.img -cpu cortex-a72 -m 2G -smp 4 -nographic -s -kernel "$bin" -append "$append_args --rootfs=ext4fs --automount=/dev,devfs --automount=/tmp,tmpfs --automount=/proc,procfs --automount=/sys,sysfs"

View File

@@ -1,6 +1,7 @@
mkdir /bin
mkdir /dev
mkdir /proc
mkdir /sys
mkdir /tmp
symlink /bin/[ /bin/busybox
symlink /bin/[[ /bin/busybox

View File

@@ -0,0 +1,177 @@
use crate::drivers::Driver;
use crate::fs::FilesystemDriver;
use crate::sync::OnceLock;
use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::vec::Vec;
use async_trait::async_trait;
use core::hash::Hasher;
use libkernel::error::FsError;
use libkernel::fs::attr::FileAttr;
use libkernel::fs::{
BlockDevice, CGROUPFS_ID, DirStream, Dirent, FileType, Inode, InodeId, SimpleDirStream,
};
use libkernel::{
error::{KernelError, Result},
fs::Filesystem,
};
use log::warn;
/// Deterministically generates an inode ID for the given path segments within the sysfs filesystem.
fn get_inode_id(path_segments: &[&str]) -> u64 {
let mut hasher = rustc_hash::FxHasher::default();
// Ensure non-collision if other filesystems also use this method
hasher.write(b"cgroupfs");
for segment in path_segments {
hasher.write(segment.as_bytes());
}
let hash = hasher.finish();
assert_ne!(hash, 0, "Generated inode ID cannot be zero");
hash
}
macro_rules! static_dir {
($name:ident, $path:expr, $( $entry_name:expr => $entry_type:expr, $entry_ident:ident ),* $(,)? ) => {
struct $name {
id: InodeId,
attr: FileAttr,
}
impl $name {
fn new(id: InodeId) -> Self {
Self {
id,
attr: FileAttr {
file_type: FileType::Directory,
..FileAttr::default()
},
}
}
const fn num_entries() -> usize {
0 $(+ { let _ = $entry_name; 1 })*
}
}
#[async_trait]
impl Inode for $name {
fn id(&self) -> InodeId {
self.id
}
async fn getattr(&self) -> Result<FileAttr> {
Ok(self.attr.clone())
}
async fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
match name {
$(
$entry_name => {
let inode_id = InodeId::from_fsid_and_inodeid(
SYSFS_ID,
get_inode_id(&[$path, $entry_name]),
);
Ok(Arc::new($entry_ident::new(inode_id)))
},
)*
_ => Err(KernelError::Fs(FsError::NotFound)),
}
}
async fn readdir(&self, start_offset: u64) -> Result<Box<dyn DirStream>> {
#[allow(unused_mut)]
let mut entries: Vec<Dirent> = Vec::with_capacity(Self::num_entries());
$(
entries.push(Dirent {
id: InodeId::from_fsid_and_inodeid(
SYSFS_ID,
get_inode_id(&[$path, $entry_name]),
),
name: $entry_name.to_string(),
offset: (entries.len() + 1) as u64,
file_type: $entry_type,
});
)*
Ok(Box::new(SimpleDirStream::new(entries, start_offset)))
}
}
};
}
static_dir! {
RootInode,
"",
}
pub struct CgroupFs {
root: Arc<RootInode>,
}
impl CgroupFs {
fn new() -> Arc<Self> {
Arc::new(Self {
root: Arc::new(RootInode::new(InodeId::from_fsid_and_inodeid(
CGROUPFS_ID,
get_inode_id(&[]),
))),
})
}
}
#[async_trait]
impl Filesystem for CgroupFs {
async fn root_inode(&self) -> Result<Arc<dyn Inode>> {
Ok(self.root.clone())
}
fn id(&self) -> u64 {
CGROUPFS_ID
}
}
static SYSFS_INSTANCE: OnceLock<Arc<CgroupFs>> = OnceLock::new();
/// Initializes and/or returns the global singleton [`CgroupFs`] instance.
/// This is the main entry point for the rest of the kernel to interact with cgroupfs.
pub fn cgroupfs() -> Arc<CgroupFs> {
SYSFS_INSTANCE
.get_or_init(|| {
log::info!("cgroupfs initialized");
CgroupFs::new()
})
.clone()
}
pub struct CgroupFsDriver;
impl CgroupFsDriver {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Driver for CgroupFsDriver {
fn name(&self) -> &'static str {
"cgroupfs"
}
fn as_filesystem_driver(self: Arc<Self>) -> Option<Arc<dyn FilesystemDriver>> {
Some(self)
}
}
#[async_trait]
impl FilesystemDriver for CgroupFsDriver {
async fn construct(
&self,
_fs_id: u64,
device: Option<Box<dyn BlockDevice>>,
) -> Result<Arc<dyn Filesystem>> {
if device.is_some() {
warn!("cgroupfs should not be constructed with a block device");
return Err(KernelError::InvalidValue);
}
Ok(cgroupfs())
}
}

View File

@@ -1,16 +1,20 @@
use alloc::sync::Arc;
use cgroup::CgroupFsDriver;
use dev::DevFsDriver;
use ext4::Ext4FsDriver;
use fat32::Fat32FsDriver;
use proc::ProcFsDriver;
use sys::SysFsDriver;
use tmpfs::TmpFsDriver;
use super::DM;
pub mod cgroup;
pub mod dev;
pub mod ext4;
pub mod fat32;
pub mod proc;
pub mod sys;
pub mod tmpfs;
pub fn register_fs_drivers() {
@@ -20,5 +24,7 @@ pub fn register_fs_drivers() {
dm.insert_driver(Arc::new(Fat32FsDriver::new()));
dm.insert_driver(Arc::new(DevFsDriver::new()));
dm.insert_driver(Arc::new(ProcFsDriver::new()));
dm.insert_driver(Arc::new(SysFsDriver::new()));
dm.insert_driver(Arc::new(TmpFsDriver::new()));
dm.insert_driver(Arc::new(CgroupFsDriver::new()));
}

226
src/drivers/fs/sys/mod.rs Normal file
View File

@@ -0,0 +1,226 @@
use crate::drivers::Driver;
use crate::fs::FilesystemDriver;
use crate::sync::OnceLock;
use alloc::boxed::Box;
use alloc::string::ToString;
use alloc::sync::Arc;
use alloc::vec::Vec;
use async_trait::async_trait;
use core::hash::Hasher;
use libkernel::error::FsError;
use libkernel::fs::attr::FileAttr;
use libkernel::fs::{
BlockDevice, DirStream, Dirent, FileType, Inode, InodeId, SYSFS_ID, SimpleDirStream,
};
use libkernel::{
error::{KernelError, Result},
fs::Filesystem,
};
use log::warn;
/// Deterministically generates an inode ID for the given path segments within the sysfs filesystem.
fn get_inode_id(path_segments: &[&str]) -> u64 {
let mut hasher = rustc_hash::FxHasher::default();
// Ensure non-collision if other filesystems also use this method
hasher.write(b"sysfs");
for segment in path_segments {
hasher.write(segment.as_bytes());
}
let hash = hasher.finish();
assert_ne!(hash, 0, "Generated inode ID cannot be zero");
hash
}
macro_rules! static_dir {
($name:ident, $path:expr, $( $entry_name:expr => $entry_type:expr, $entry_ident:ident ),* $(,)? ) => {
struct $name {
id: InodeId,
attr: FileAttr,
}
impl $name {
fn new(id: InodeId) -> Self {
Self {
id,
attr: FileAttr {
file_type: FileType::Directory,
..FileAttr::default()
},
}
}
const fn num_entries() -> usize {
0 $(+ { let _ = $entry_name; 1 })*
}
}
#[async_trait]
impl Inode for $name {
fn id(&self) -> InodeId {
self.id
}
async fn getattr(&self) -> Result<FileAttr> {
Ok(self.attr.clone())
}
async fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
match name {
$(
$entry_name => {
let inode_id = InodeId::from_fsid_and_inodeid(
SYSFS_ID,
get_inode_id(&[$path, $entry_name]),
);
Ok(Arc::new($entry_ident::new(inode_id)))
},
)*
_ => Err(KernelError::Fs(FsError::NotFound)),
}
}
async fn readdir(&self, start_offset: u64) -> Result<Box<dyn DirStream>> {
#[allow(unused_mut)]
let mut entries: Vec<Dirent> = Vec::with_capacity(Self::num_entries());
$(
entries.push(Dirent {
id: InodeId::from_fsid_and_inodeid(
SYSFS_ID,
get_inode_id(&[$path, $entry_name]),
),
name: $entry_name.to_string(),
offset: (entries.len() + 1) as u64,
file_type: $entry_type,
});
)*
Ok(Box::new(SimpleDirStream::new(entries, start_offset)))
}
}
};
}
static_dir! {
DevBlockInode,
"dev/block",
}
static_dir! {
DevCharInode,
"dev/char",
}
static_dir! {
DevInode,
"dev",
"block" => FileType::Directory, DevBlockInode,
"char" => FileType::Directory, DevCharInode,
}
static_dir! {
DevicesInode,
"devices",
}
static_dir! {
FirmwareInode,
"firmware",
}
static_dir! {
CgroupInode,
"fs/cgroup",
}
static_dir! {
FsInode,
"fs",
"cgroup" => FileType::Directory, CgroupInode,
}
static_dir! {
KernelInode,
"kernel",
}
static_dir! {
RootInode,
"",
"dev" => FileType::Directory, DevInode,
"devices" => FileType::Directory, DevicesInode,
"firmware" => FileType::Directory, FirmwareInode,
"fs" => FileType::Directory, FsInode,
"kernel" => FileType::Directory, KernelInode,
}
pub struct SysFs {
root: Arc<RootInode>,
}
impl SysFs {
fn new() -> Arc<Self> {
Arc::new(Self {
root: Arc::new(RootInode::new(InodeId::from_fsid_and_inodeid(
SYSFS_ID,
get_inode_id(&[]),
))),
})
}
}
#[async_trait]
impl Filesystem for SysFs {
async fn root_inode(&self) -> Result<Arc<dyn Inode>> {
Ok(self.root.clone())
}
fn id(&self) -> u64 {
SYSFS_ID
}
}
static SYSFS_INSTANCE: OnceLock<Arc<SysFs>> = OnceLock::new();
/// Initializes and/or returns the global singleton [`SysFs`] instance.
/// This is the main entry point for the rest of the kernel to interact with sysfs.
pub fn sysfs() -> Arc<SysFs> {
SYSFS_INSTANCE
.get_or_init(|| {
log::info!("sysfs initialized");
SysFs::new()
})
.clone()
}
pub struct SysFsDriver;
impl SysFsDriver {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Driver for SysFsDriver {
fn name(&self) -> &'static str {
"sysfs"
}
fn as_filesystem_driver(self: Arc<Self>) -> Option<Arc<dyn FilesystemDriver>> {
Some(self)
}
}
#[async_trait]
impl FilesystemDriver for SysFsDriver {
async fn construct(
&self,
_fs_id: u64,
device: Option<Box<dyn BlockDevice>>,
) -> Result<Arc<dyn Filesystem>> {
if device.is_some() {
warn!("sysfs should not be constructed with a block device");
return Err(KernelError::InvalidValue);
}
Ok(sysfs())
}
}