⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/sysfs/
mod.rs

1//! Virtual sysfs filesystem for VeridianOS.
2//!
3//! Provides a Linux-compatible sysfs interface for kernel subsystem
4//! configuration and status. Virtual files under `/sys/` expose kernel
5//! parameters to user-space tools and desktop environments (e.g., KDE
6//! PowerDevil reads `/sys/power/state` and `/sys/class/backlight/`).
7//!
8//! Each sysfs node is a `SysfsNode` with read and/or write handlers.
9//! Nodes are registered at init time and looked up by path.
10
11#![allow(dead_code)]
12
13extern crate alloc;
14
15use alloc::{string::String, vec::Vec};
16
17use spin::Mutex;
18
19use crate::error::{KernelError, KernelResult};
20
21pub mod power;
22
23// ---------------------------------------------------------------------------
24// Sysfs node definitions
25// ---------------------------------------------------------------------------
26
27/// Maximum number of sysfs nodes.
28const MAX_SYSFS_NODES: usize = 64;
29
30/// Type alias for sysfs read handler.
31/// Returns the contents of the virtual file as a String.
32type SysfsReadFn = fn() -> String;
33
34/// Type alias for sysfs write handler.
35/// Receives the value written to the virtual file.
36/// Returns Ok(()) on success or an error.
37type SysfsWriteFn = fn(&str) -> KernelResult<()>;
38
39/// A virtual sysfs filesystem node.
40#[derive(Clone)]
41pub struct SysfsNode {
42    /// Full path (e.g., "/sys/power/state").
43    pub path: &'static str,
44    /// Human-readable description.
45    pub description: &'static str,
46    /// Read handler (None = write-only).
47    pub read_fn: Option<SysfsReadFn>,
48    /// Write handler (None = read-only).
49    pub write_fn: Option<SysfsWriteFn>,
50}
51
52impl SysfsNode {
53    /// Create a new read-only sysfs node.
54    pub const fn read_only(
55        path: &'static str,
56        description: &'static str,
57        read_fn: SysfsReadFn,
58    ) -> Self {
59        Self {
60            path,
61            description,
62            read_fn: Some(read_fn),
63            write_fn: None,
64        }
65    }
66
67    /// Create a new read-write sysfs node.
68    pub const fn read_write(
69        path: &'static str,
70        description: &'static str,
71        read_fn: SysfsReadFn,
72        write_fn: SysfsWriteFn,
73    ) -> Self {
74        Self {
75            path,
76            description,
77            read_fn: Some(read_fn),
78            write_fn: Some(write_fn),
79        }
80    }
81
82    /// Whether this node is writable.
83    pub fn is_writable(&self) -> bool {
84        self.write_fn.is_some()
85    }
86
87    /// Whether this node is readable.
88    pub fn is_readable(&self) -> bool {
89        self.read_fn.is_some()
90    }
91}
92
93/// Trait for sysfs entry registration.
94pub trait SysfsEntry {
95    /// Returns the path of this sysfs entry.
96    fn path(&self) -> &str;
97
98    /// Read the sysfs entry value.
99    fn read(&self) -> KernelResult<String>;
100
101    /// Write a value to the sysfs entry.
102    fn write(&self, value: &str) -> KernelResult<()>;
103}
104
105// ---------------------------------------------------------------------------
106// Global registry
107// ---------------------------------------------------------------------------
108
109struct SysfsRegistry {
110    nodes: Vec<SysfsNode>,
111}
112
113impl SysfsRegistry {
114    const fn new() -> Self {
115        Self { nodes: Vec::new() }
116    }
117}
118
119static SYSFS_REGISTRY: Mutex<SysfsRegistry> = Mutex::new(SysfsRegistry::new());
120
121// ---------------------------------------------------------------------------
122// Registration API
123// ---------------------------------------------------------------------------
124
125/// Register a sysfs node.
126pub fn register_node(node: SysfsNode) -> KernelResult<()> {
127    let mut registry = SYSFS_REGISTRY.lock();
128
129    if registry.nodes.len() >= MAX_SYSFS_NODES {
130        return Err(KernelError::ResourceExhausted {
131            resource: "sysfs nodes",
132        });
133    }
134
135    // Check for duplicate paths.
136    for existing in &registry.nodes {
137        if existing.path == node.path {
138            return Err(KernelError::AlreadyExists {
139                resource: "sysfs node",
140                id: 0,
141            });
142        }
143    }
144
145    println!("[SYSFS] Registered: {}", node.path);
146    registry.nodes.push(node);
147    Ok(())
148}
149
150/// Look up a sysfs node by path.
151pub fn lookup(path: &str) -> Option<SysfsNode> {
152    let registry = SYSFS_REGISTRY.lock();
153    for node in &registry.nodes {
154        if node.path == path {
155            return Some(node.clone());
156        }
157    }
158    None
159}
160
161/// Read a sysfs virtual file.
162pub fn sysfs_read(path: &str) -> KernelResult<String> {
163    let node = lookup(path).ok_or(KernelError::NotFound {
164        resource: "sysfs",
165        id: 0,
166    })?;
167
168    let read_fn = node.read_fn.ok_or(KernelError::PermissionDenied {
169        operation: "sysfs read (write-only node)",
170    })?;
171
172    Ok(read_fn())
173}
174
175/// Write to a sysfs virtual file.
176pub fn sysfs_write(path: &str, value: &str) -> KernelResult<()> {
177    let node = lookup(path).ok_or(KernelError::NotFound {
178        resource: "sysfs",
179        id: 0,
180    })?;
181
182    let write_fn = node.write_fn.ok_or(KernelError::PermissionDenied {
183        operation: "sysfs write (read-only node)",
184    })?;
185
186    write_fn(value)
187}
188
189/// List all registered sysfs nodes.
190pub fn list_nodes() -> Vec<&'static str> {
191    let registry = SYSFS_REGISTRY.lock();
192    registry.nodes.iter().map(|n| n.path).collect()
193}
194
195// ---------------------------------------------------------------------------
196// Initialization
197// ---------------------------------------------------------------------------
198
199/// Initialize the sysfs subsystem and register all built-in nodes.
200pub fn sysfs_init() -> KernelResult<()> {
201    println!("[SYSFS] Initializing virtual filesystem...");
202
203    // Register power-related sysfs nodes.
204    power::sysfs_power_init()?;
205
206    let count = {
207        let registry = SYSFS_REGISTRY.lock();
208        registry.nodes.len()
209    };
210
211    println!("[SYSFS] Initialized: {} nodes registered", count);
212    Ok(())
213}
214
215// ---------------------------------------------------------------------------
216// Tests
217// ---------------------------------------------------------------------------
218
219#[cfg(test)]
220mod tests {
221    use super::*;
222
223    fn test_read() -> String {
224        String::from("test_value")
225    }
226
227    fn test_write(value: &str) -> KernelResult<()> {
228        if value.is_empty() {
229            return Err(KernelError::InvalidArgument {
230                name: "value",
231                value: "empty",
232            });
233        }
234        Ok(())
235    }
236
237    #[test]
238    fn test_sysfs_node_read_only() {
239        let node = SysfsNode::read_only("/test/ro", "test read-only", test_read);
240        assert!(node.is_readable());
241        assert!(!node.is_writable());
242        assert_eq!(node.path, "/test/ro");
243    }
244
245    #[test]
246    fn test_sysfs_node_read_write() {
247        let node = SysfsNode::read_write("/test/rw", "test read-write", test_read, test_write);
248        assert!(node.is_readable());
249        assert!(node.is_writable());
250    }
251
252    #[test]
253    fn test_sysfs_node_read_fn() {
254        let node = SysfsNode::read_only("/test/read", "test", test_read);
255        let val = (node.read_fn.unwrap())();
256        assert_eq!(val, "test_value");
257    }
258
259    #[test]
260    fn test_sysfs_node_write_fn() {
261        let node = SysfsNode::read_write("/test/write", "test", test_read, test_write);
262        let result = (node.write_fn.unwrap())("hello");
263        assert!(result.is_ok());
264    }
265
266    #[test]
267    fn test_sysfs_node_write_empty_error() {
268        let node = SysfsNode::read_write("/test/write_err", "test", test_read, test_write);
269        let result = (node.write_fn.unwrap())("");
270        assert!(result.is_err());
271    }
272}