Skip to main content

component/
lib.rs

1//! Component initialization system for Strat9-OS.
2//!
3//! Provides modular component registration and dependency-ordered initialization.
4//! Components are registered at compile time via `#[init_component]` and discovered
5//! at runtime through the `.component_entries` linker section.
6//!
7//! # Usage
8//!
9//! ```rust,no_run
10//! #[component::init_component(bootstrap, priority = 1)]
11//! fn vfs_init() -> Result<(), component::ComponentInitError> {
12//!     vfs::init();
13//!     Ok(())
14//! }
15//!
16//! #[component::init_component(kthread, priority = 2, depends_on = vfs_init)]
17//! fn fs_ext4_init() -> Result<(), component::ComponentInitError> {
18//!     fs_ext4::init();
19//!     Ok(())
20//! }
21//! ```
22//!
23//! Call `component::init_all(InitStage::Bootstrap)` at the appropriate point
24//! in `kernel_main` to run all registered components in dependency order.
25
26#![no_std]
27#![allow(unsafe_code)]
28#![allow(unsafe_op_in_unsafe_fn)]
29
30extern crate alloc;
31
32use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
33
34pub use component_macro::{init_component, parse_components_toml};
35
36// ─── Stage ───────────────────────────────────────────────────────────────────
37
38/// Initialization stages for components.
39///
40/// - `Bootstrap` — Early kernel initialization, before SMP.
41/// - `Kthread`   — After SMP enabled, in kernel-thread context.
42/// - `Process`   — After first user process created.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
44#[repr(u8)]
45pub enum InitStage {
46    Bootstrap = 0,
47    Kthread = 1,
48    Process = 2,
49}
50
51// ─── Error ───────────────────────────────────────────────────────────────────
52
53/// Errors that a component initializer may return.
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum ComponentInitError {
56    UninitializedDependencies(String),
57    InitFailed(&'static str),
58    Unknown,
59}
60
61impl core::fmt::Display for ComponentInitError {
62    /// Performs the fmt operation.
63    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64        match self {
65            Self::UninitializedDependencies(dep) => {
66                write!(f, "Uninitialized dependency: {dep}")
67            }
68            Self::InitFailed(msg) => write!(f, "Init failed: {msg}"),
69            Self::Unknown => write!(f, "Unknown error"),
70        }
71    }
72}
73
74// ─── ComponentEntry ───────────────────────────────────────────────────────────
75
76/// Entry placed in `.component_entries` by `#[init_component]`.
77///
78/// All fields are `'static` because this struct lives in a `#[used] static`.
79///
80/// # Memory layout
81///
82/// `#[repr(C)]` ensures a stable layout so the linker-section scan in
83/// `init_all()` can iterate entries with `ptr::add(1)`.
84#[repr(C)]
85pub struct ComponentEntry {
86    /// Function name as written in source — used for dependency resolution.
87    pub name: &'static str,
88    /// Lifecycle stage this component belongs to.
89    pub stage: InitStage,
90    /// The registered initializer.
91    pub init_fn: fn() -> Result<(), ComponentInitError>,
92    /// `"file!():fn_name"` — for log messages only.
93    pub path: &'static str,
94    /// Lower value = earlier init within the same topological level.
95    pub priority: u32,
96    /// Names of same-stage functions that must complete before this one.
97    pub depends_on: &'static [&'static str],
98}
99
100impl ComponentEntry {
101    /// Construct a `ComponentEntry` (usable in `const` / `static` contexts).
102    pub const fn new(
103        name: &'static str,
104        stage: InitStage,
105        init_fn: fn() -> Result<(), ComponentInitError>,
106        path: &'static str,
107        priority: u32,
108        depends_on: &'static [&'static str],
109    ) -> Self {
110        Self {
111            name,
112            stage,
113            init_fn,
114            path,
115            priority,
116            depends_on,
117        }
118    }
119}
120
121// ─── ComponentInfo ────────────────────────────────────────────────────────────
122
123/// Human-readable component metadata (not stored in the linker section).
124#[derive(Debug)]
125pub struct ComponentInfo {
126    pub name: String,
127    pub path: String,
128    pub priority: u32,
129}
130
131impl ComponentInfo {
132    /// Creates a new instance.
133    pub fn new(name: &'static str, path: &'static str, priority: u32) -> Self {
134        Self {
135            name: String::from(name),
136            path: String::from(path),
137            priority,
138        }
139    }
140}
141
142impl PartialEq for ComponentInfo {
143    /// Performs the eq operation.
144    fn eq(&self, o: &Self) -> bool {
145        self.priority == o.priority
146    }
147}
148impl Eq for ComponentInfo {}
149impl Ord for ComponentInfo {
150    /// Performs the cmp operation.
151    fn cmp(&self, o: &Self) -> core::cmp::Ordering {
152        self.priority.cmp(&o.priority)
153    }
154}
155impl PartialOrd for ComponentInfo {
156    /// Performs the partial cmp operation.
157    fn partial_cmp(&self, o: &Self) -> Option<core::cmp::Ordering> {
158        Some(self.cmp(o))
159    }
160}
161
162// ─── Linker section symbols ───────────────────────────────────────────────────
163
164// SAFETY: symbols defined by linker-limine.ld; bracket the `.component_entries`
165// section.  All objects between them are `ComponentEntry` structs placed by the
166// `#[init_component]` macro.
167#[allow(improper_ctypes)]
168extern "C" {
169    static __start_component_entries: ComponentEntry;
170    static __stop_component_entries: ComponentEntry;
171}
172
173// ─── init_all ────────────────────────────────────────────────────────────────
174
175/// Initialize all components registered for `stage` in dependency order.
176///
177/// ## Algorithm (Kahn's topological sort)
178///
179/// 1. Collect every `ComponentEntry` in `.component_entries` that matches the
180///    requested stage.
181/// 2. Build a directed graph from `depends_on` edges (A→B: A must run first).
182/// 3. Topological sort with `priority` as the tiebreaker when multiple
183///    components become ready at the same time (lower number = earlier).
184/// 4. Execute each initializer in the computed order.
185///
186/// Cross-stage dependencies (names not found in the current stage) are warned
187/// about and skipped — they are assumed to have already run in a prior stage.
188///
189/// Detected cycles are logged as errors; cyclic components are appended in
190/// priority order after the acyclic ones (best-effort fallback).
191#[allow(unsafe_code)]
192pub fn init_all(stage: InitStage) -> Result<(), ComponentInitError> {
193    // ── 1. Collect entries for this stage ────────────────────────────────────
194    let mut components: Vec<&'static ComponentEntry> = Vec::new();
195
196    // SAFETY: linker guarantees the section boundaries are valid and all
197    // objects in the section are `ComponentEntry` structs placed by the macro.
198    unsafe {
199        let start = &raw const __start_component_entries;
200        let stop = &raw const __stop_component_entries;
201        let mut cur = start;
202        while cur < stop {
203            let entry = &*cur;
204            if entry.stage == stage {
205                components.push(entry);
206            }
207            cur = cur.add(1);
208        }
209    }
210
211    if components.is_empty() {
212        log::info!("[component] No components registered for {:?} stage", stage);
213        return Ok(());
214    }
215
216    // ── 2. Build dependency graph ────────────────────────────────────────────
217    let n = components.len();
218
219    // name → index in `components`
220    let name_to_idx: BTreeMap<&str, usize> = components
221        .iter()
222        .enumerate()
223        .map(|(i, e)| (e.name, i))
224        .collect();
225
226    // in_degree[i] = number of unresolved same-stage deps of component i
227    let mut in_degree = vec![0usize; n];
228    // adj[i] = indices of components that must run AFTER component i
229    let mut adj: Vec<Vec<usize>> = (0..n).map(|_| Vec::new()).collect();
230
231    for (i, entry) in components.iter().enumerate() {
232        for dep_name in entry.depends_on {
233            if let Some(&dep_idx) = name_to_idx.get(dep_name) {
234                adj[dep_idx].push(i);
235                in_degree[i] += 1;
236            } else {
237                // Not in this stage — assumed handled in a prior stage.
238                log::warn!(
239                    "[component] '{}': dep '{}' not in {:?} stage (cross-stage, skipped)",
240                    entry.name,
241                    dep_name,
242                    stage
243                );
244            }
245        }
246    }
247
248    // ── 3. Kahn's topological sort with priority tiebreaker ──────────────────
249    // `ready` is sorted ascending by priority so `remove(0)` always gives the
250    // component with the smallest priority number (= earliest boot precedence).
251    let mut ready: Vec<usize> = (0..n).filter(|&i| in_degree[i] == 0).collect();
252    ready.sort_by_key(|&i| components[i].priority);
253
254    let mut ordered: Vec<usize> = Vec::with_capacity(n);
255
256    while !ready.is_empty() {
257        let idx = ready.remove(0);
258        ordered.push(idx);
259
260        let mut newly_ready: Vec<usize> = Vec::new();
261        for &succ in &adj[idx] {
262            in_degree[succ] -= 1;
263            if in_degree[succ] == 0 {
264                newly_ready.push(succ);
265            }
266        }
267
268        // Merge newly-ready nodes while maintaining sort by priority.
269        for nr in newly_ready {
270            let pos = ready.partition_point(|&i| components[i].priority <= components[nr].priority);
271            ready.insert(pos, nr);
272        }
273    }
274
275    // ── 4. Cycle detection fallback ──────────────────────────────────────────
276    if ordered.len() != n {
277        log::error!(
278            "[component] Dependency cycle in {:?} stage — cyclic components will run last",
279            stage
280        );
281        let mut remaining: Vec<usize> = (0..n).filter(|i| !ordered.contains(i)).collect();
282        remaining.sort_by_key(|&i| components[i].priority);
283        ordered.extend(remaining);
284    }
285
286    // ── 5. Execute ───────────────────────────────────────────────────────────
287    log::info!(
288        "[component] {:?} stage — {} component(s) to initialize",
289        stage,
290        ordered.len()
291    );
292
293    for idx in &ordered {
294        let e = components[*idx];
295        log::info!(
296            "[component]  [{:3}] {}{}",
297            e.priority,
298            e.name,
299            if e.depends_on.is_empty() {
300                String::new()
301            } else {
302                alloc::format!(" (after: {})", e.depends_on.join(", "))
303            }
304        );
305    }
306
307    let mut failed = 0usize;
308    for idx in ordered {
309        let entry = components[idx];
310        match (entry.init_fn)() {
311            Ok(()) => log::info!("[component]   OK  {}", entry.name),
312            Err(e) => {
313                log::error!("[component]   ERR {}: {}", entry.name, e);
314                failed += 1;
315            }
316        }
317    }
318
319    log::info!("[component] {:?} stage complete ({} failed)", stage, failed);
320    Ok(())
321}
322
323// ─── list_components ─────────────────────────────────────────────────────────
324
325/// Return all registered components sorted by stage then priority (for debug).
326#[allow(unsafe_code)]
327pub fn list_components() -> Vec<&'static ComponentEntry> {
328    let mut components: Vec<&'static ComponentEntry> = Vec::new();
329
330    // SAFETY: linker guarantees the section boundaries are valid and all
331    // objects in the section are `ComponentEntry` structs placed by the macro.
332    unsafe {
333        let start = &raw const __start_component_entries;
334        let stop = &raw const __stop_component_entries;
335        let mut cur = start;
336        while cur < stop {
337            components.push(&*cur);
338            cur = cur.add(1);
339        }
340    }
341
342    components.sort_by(|a, b| {
343        a.stage
344            .cmp(&b.stage)
345            .then_with(|| a.priority.cmp(&b.priority))
346            .then_with(|| a.name.cmp(b.name))
347    });
348
349    components
350}
351
352// ─── parse_metadata! ─────────────────────────────────────────────────────────
353
354/// Parse `Components.toml` at compile time and return the dependency metadata.
355///
356/// Delegates to [`parse_components_toml!`] which searches for `Components.toml`
357/// starting from the calling crate's directory.
358///
359/// Returns `&'static [(&'static str, &'static [&'static str])]`
360/// where each element is `(component_name, &[dep1, dep2, ...])`.
361#[macro_export]
362macro_rules! parse_metadata {
363    () => {
364        component::parse_components_toml!()
365    };
366}