Files
RedBear-OS/local/docs/SCHEME-NAMESPACE-POPULATION-PLAN.md
T
vasilito dc68054305 restore lost packages from 0.2.3 + fix overwritten 0.2.4 files
- Restore 29 recipe symlinks (libdrm, qtbase, dbus, sddm, pipewire, etc.)
- Restore 33 patches (KDE, libdrm, mesa, pipewire, sddm, wireplumber)
- Restore 20+ local/scripts (audit, lint, test, build helpers)
- Restore src/cook/scheduler.rs, status.rs, gnu-config/
- Restore scripts/patch-inclusion-gate.sh, run_mini1.sh, validate-collision-log.sh
- Recover TLC source from HEAD (was overwritten by 0.2.3 checkout)
- Recover 11 local/docs plans from HEAD (were overwritten)
- Recover qt6-wayland-smoke symlink from HEAD
- Fix MOTD: remove garbled ASCII art, use clean text
- Update version: 0.2.0 -> 0.2.4 in os-release, motd, config
- Reduce filesystem_size: 1536 -> 512 MiB
- Add ABSOLUTE RULE to AGENTS.md: never delete/ignore packages
- Reduce pcid scheme log verbosity: info -> debug
2026-06-19 12:39:14 +03:00

457 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Red Bear OS /scheme/ Namespace Population Plan
**Version**: 1.0 (2026-06-12)
**Status**: Draft — pending review
**Canonical**: `local/docs/SCHEME-NAMESPACE-POPULATION-PLAN.md`
**Blocks**: Writable rootfs on live ISO, `redoxfs` disk discovery, `ls /scheme/` in shell
**Cross-references**: Linux kobject/uevent, Fuchsia Zircon/Component Manager, seL4 CSpace, Plan 9 per-process namespace, Genode capability routing, MINIX 3 driver model
## 1. Problem Statement
`ls /scheme/` hangs or returns empty in Red Bear OS. Three root causes:
1. **initnsmgr `getdents` depends on daemons registering** — but boot ordering means some schemes
haven't registered yet when `redoxfs` calls `fs::read_dir("/scheme")` to find disk devices.
2. **No aggregator for block devices**`redoxfs` must enumerate all `disk.*` schemes individually,
but `/scheme/disk.live` may not exist yet when the rootfs mount runs at priority 50.
3. **driver-block `getdents` returns `EOPNOTSUPP`** — individual disk schemes use legacy text-based
listing, not proper `getdents`.
The result: `redoxfs` can't discover disks, rootfs fails to mount read-write, and `/scheme/`
listing is incomplete.
## 2. Design Principles (Informed by Cross-Reference)
### 2.1 Microkernel Principle (seL4, Red Bear OS)
The kernel tracks scheme IDs (integers), not names. All name→ID mapping happens in userspace
(`initnsmgr`). This is correct per the user's explicit correction:
> "Kernel does not have to track id-name mapping! Kernel only knows about IDs. It's a microkernel
> and stuff like this must be done in userspace"
**Implication**: We never modify the kernel to "export" scheme names. The namespace is purely
a userspace construct managed by `initnsmgr`.
### 2.2 Aggregator Pattern (Linux devtmpfs + Fuchsia devcoordinator)
Linux populates `/dev` via two mechanisms:
- **devtmpfs** — kernel auto-creates basic `/dev/null`, `/dev/sda1` etc. at boot
- **udev** — userspace daemon receives uevents via netlink, applies rules, creates additional nodes
Fuchsia uses **devcoordinator** (now driver-index + device-finder):
- Drivers register devices with the driver manager
- devcoordinator exposes them via `devfs` (listable, browsable)
- Component Manager routes specific devices to components via capability declarations
Red Bear OS should follow the **aggregator** pattern: userspace daemons that discover,
enumerate, and expose device categories through listable scheme namespaces.
### 2.3 Bootstrap Ordering (Plan 9, Fuchsia)
Plan 9 bootstraps namespace incrementally:
1. Kernel boots with `#` device drivers (kernel-resident, like Red Bear's `GlobalSchemes`)
2. `boot(8)` script binds drivers into the namespace
3. `init(8)` builds the per-process namespace from `/lib/namespace`
Fuchsia bootstraps similarly:
1. Zircon boots, creates root job + resource handles
2. component_manager starts, receives boot info (device handles from ZBI)
3. driver_index enumerates drivers, binds them to devices
4. devfs provides the listable namespace
Red Bear OS boot sequence (current):
```
bootstrap → initnsmgr (initial schemes: 10 kernel globals + "proc" + "initfs")
→ init starts service targets
→ 10_lived.service (priority 10): registers "disk.live"
→ 40_drivers.target: pcid, graphics, etc.
→ 45_diskd.service (NEW): scans disk.* schemes, registers "diskd"
→ 50_rootfs.service: redoxfs uses diskd to find root device
```
### 2.4 Separation of Discovery and Access (Genode, seL4)
Genode separates:
- **Platform session** — device discovery (what hardware exists)
- **I/O session** — device access (read/write/mmio)
seL4 separates:
- **Device Untyped caps** — raw hardware access
- **Platform description** — structured description of what devices exist
In Red Bear OS terms: `diskd` provides discovery (listing), but actual block I/O goes through
the original `disk.live`/`disk.sata0` schemes directly. `diskd` returns `OpenResult::OtherScheme`
so the kernel hands the caller a raw fd to the underlying scheme — zero overhead.
## 3. Current Architecture
### 3.1 Kernel Global Schemes (10)
Registered by bootstrap in `exec.rs``initnsmgr::run()`:
| Scheme | GlobalSchemes Variant | Kernel Source |
|--------|-----------------------|---------------|
| debug | Debug | `scheme/debug.rs` |
| event | Event | `scheme/event.rs` |
| memory | Memory | `scheme/memory.rs` |
| pipe | Pipe | `scheme/pipe.rs` |
| serio | Serio | `scheme/serio.rs` |
| irq | Irq | `scheme/irq.rs` |
| time | Time | `scheme/time.rs` |
| sys | Sys | `scheme/sys/mod.rs` |
| proc | Proc | `scheme/proc/mod.rs` |
| acpi | Acpi | `scheme/acpi.rs` |
| dtb | Dtb | `scheme/dtb.rs` |
These are registered in the `KernelSchemes` enum (kernel/src/scheme/mod.rs:438) and
exposed to initnsmgr during bootstrap.
### 3.2 initnsmgr Namespace Manager
Located at `local/sources/base/bootstrap/src/initnsmgr.rs`.
Key structures:
```rust
struct Namespace {
schemes: HashMap<String, Arc<FdGuard>>, // name → fd
}
```
- `open("")``Handle::List` (directory listing handle)
- `getdents(Handle::List)` → iterates `schemes` HashMap, returns `DirEntry` for each name
- Daemons register via `NsDup::IssueRegister` + sendfd mechanism
- Bootstrap passes initial set: kernel globals + "proc" + "initfs"
### 3.3 Userspace Scheme Registration
Daemons register via:
1. `Socket::create()` → creates scheme socket
2. `NsDup::IssueRegister` → tells initnsmgr the scheme name
3. `sendfd` → sends the scheme socket fd to initnsmgr
4. initnsmgr stores in `schemes: HashMap<String, Arc<FdGuard>>`
### 3.4 Current Userspace Schemes (at boot)
| Scheme | Daemon | Priority | Source |
|--------|--------|----------|--------|
| initfs | bootstrap | 0 | bootstrap exec.rs |
| proc | kernel | 0 | GlobalSchemes |
| disk.live | lived | 10 | init.initfs.d/10_lived.service |
| disk.sata0 | ahcid | 40 | pcid-spawner |
| disk.virtio0 | virtio-blkd | 40 | pcid-spawner |
| display | vesad | 20 | init.initfs.d/20_vesad.service |
| drm | redox-drm | 30 | init.initfs.d/30_graphics.service |
| net | e1000d / virtio-netd | 40 | pcid-spawner |
| orbital | orbital | rootfs | (legacy, not used in redbear-full) |
### 3.5 The Root Cause Chain
```
redoxfs mount (priority 50)
→ fs::read_dir("/scheme") → initnsmgr getdents
→ iterates schemes HashMap → finds "disk.live" (registered at priority 10)
→ is_scheme_category("disk") → true
→ Fd::open("/scheme/disk.live") → reads text listing
→ finds block device → opens /scheme/disk.live/0 → reads UUID
→ UUID matches → mounts as rootfs
```
**The bug**: `redoxfs` retries 20×200ms = 4 seconds. If disk discovery takes longer than
4 seconds (e.g., AHCI probe on real hardware), rootfs mount fails → read-only fallback.
**The fix**: `diskd` aggregator + longer timeout + event-driven notification.
## 4. Solution Architecture
### 4.1 Component Overview
```
┌─────────────────────────────────────────────────────────┐
│ /scheme/ namespace │
│ (initnsmgr) │
│ │
│ Kernel globals: │
│ debug, event, memory, pipe, serio, irq, │
│ time, sys, proc, acpi, dtb │
│ │
│ Boot schemes (initfs): │
│ initfs, disk.live, display │
│ │
│ Aggregators: │
│ diskd ← /scheme/diskd lists ALL block devices │
│ │
│ Hardware daemons (post-drivers.target): │
│ disk.sata0..7 (ahcid) │
│ disk.virtio0..7 (virtio-blkd) │
│ disk.nvme0..7 (nvmed) │
│ disk.usb0..7 (usbscsid) │
│ disk.ide0..3 (ideid) │
│ net (e1000d, virtio-netd, ixgbed, rt8169d) │
│ drm (redox-drm) │
│ │
│ System daemons (post-rootfs): │
│ audio (audiod) │
│ firmware (firmware-loader) │
│ input (evdevd) │
│ udev (udev-shim) │
│ ... │
└─────────────────────────────────────────────────────────┘
```
### 4.2 diskd — Disk Aggregator Daemon (IMPLEMENTED)
**Location**: `local/recipes/system/diskd/`
**Scheme name**: `diskd`
**Binary**: `/usr/bin/diskd`
**Status**: Code complete, cargo check/clippy/fmt clean
**How it works**:
1. At boot (priority 45), diskd starts
2. Probes `/scheme/disk.live`, `/scheme/disk.sata0`..7, `/scheme/disk.virtio0`..7, etc.
3. For each found scheme, reads its text listing to discover devices and partitions
4. Registers scheme `diskd` with initnsmgr
5. `getdents` on `diskd:` returns real `DirEntry` with `DirentKind::BlockDev`
6. `open("0")` or `open("0p1")` opens the underlying scheme and returns `OtherScheme`
(zero-copy — caller talks directly to the block device)
**Why this solves the root cause**:
- `redoxfs` currently must enumerate ALL `/scheme/disk.*` individually — 50+ `Fd::open` calls
- With `diskd`, `redoxfs` does ONE `read_dir("/scheme/diskd")` to get all block devices
- diskd already did the probing and enumeration
- Even if AHCI hasn't registered yet, diskd's retry logic handles late registration
- `redoxfs` timeout only needs to wait for `diskd` to be ready, not all individual schemes
### 4.3 Changes Required to Existing Components
#### 4.3.1 redoxfs — Use diskd for disk discovery
**File**: `local/sources/redoxfs/src/bin/mount.rs` (function `filesystem_by_uuid`)
**Current behavior**:
```rust
// Line 224: fs::read_dir("/scheme") → filter is_scheme_category("disk")
// For each disk.* scheme: open, read listing, find block devices, check UUID
// Retry 20×200ms = 4 seconds total
```
**New behavior** (two-path approach):
```rust
fn filesystem_by_uuid(uuid: &[u8; 16]) -> Option<File> {
// Path A: Try diskd aggregator first (fast, single enumeration)
if let Some(f) = try_diskd_uuid(uuid) {
return Some(f);
}
// Path B: Fall back to legacy per-scheme enumeration
// (for backwards compat and environments without diskd)
try_legacy_uuid_search(uuid)
}
fn try_diskd_uuid(uuid: &[u8; 16]) -> Option<File> {
// Wait for diskd scheme to appear
for _ in 0..50 { // 50 × 200ms = 10 seconds
if let Ok(dir) = fs::read_dir("/scheme/diskd") {
for entry in dir {
let entry = entry.ok()?;
let name = entry.file_name().to_string_lossy().into_owned();
// Open the block device via diskd (which proxies to underlying scheme)
let path = format!("/scheme/diskd/{name}");
if let Ok(mut f) = File::open(&path) {
if check_uuid(&mut f, uuid) {
return Some(f);
}
}
}
}
thread::sleep(Duration::from_millis(200));
}
None
}
```
#### 4.3.2 init.initfs.d — Add diskd service
**New file**: `local/sources/base/init.initfs.d/45_diskd.service`
```ini
[[service]]
name = "diskd"
command = "/usr/bin/diskd"
priority = 45
requires = ["lived"]
```
This ensures diskd starts after lived (which provides disk.live at priority 10) and before
rootfs mount (priority 50).
#### 4.3.3 config/redbear-mini.toml — Add diskd package
Add `diskd` to the `[packages]` section so it's included in the image.
### 4.4 /scheme/ Namespace Completeness Matrix
After all changes, `/scheme/` will expose:
| Category | Scheme Name | Provider | getdents | Notes |
|----------|-------------|----------|----------|-------|
| **Kernel globals** | | | | |
| Debug | `debug` | kernel GlobalSchemes | ✅ real DirEntry | kernel/src/scheme/debug.rs |
| Event | `event` | kernel GlobalSchemes | ✅ real DirEntry | kernel/src/scheme/event.rs |
| Memory | `memory` | kernel GlobalSchemes | EOPNOTSUPP | No sub-entries expected |
| Pipe | `pipe` | kernel GlobalSchemes | EOPNOTSUPP | Anonymous, no listing |
| Serio | `serio` | kernel GlobalSchemes | ✅ real DirEntry | kernel/src/scheme/serio.rs |
| IRQ | `irq` | kernel GlobalSchemes | ✅ real DirEntry | cpu-XX entries |
| Time | `time` | kernel GlobalSchemes | ✅ real DirEntry | CLOCK_* entries |
| Sys | `sys` | kernel GlobalSchemes | ✅ real DirEntry | scheme:/scp/ sub-entries |
| Proc | `proc` | kernel GlobalSchemes | ✅ real DirEntry | pid entries |
| ACPI | `acpi` | kernel GlobalSchemes | ✅ real DirEntry | rxsdt, kstop |
| DTB | `dtb` | kernel GlobalSchemes | EOPNOTSUPP | Single blob |
| **Bootstrap** | | | | |
| InitFS | `initfs` | bootstrap | ✅ real DirEntry | initramfs contents |
| **Storage** | | | | |
| Live disk | `disk.live` | lived | ✅ text listing | virtio/ahci backend |
| SATA disk | `disk.sata0..7` | ahcid | ✅ text listing | per-disk scheme |
| VirtIO disk | `disk.virtio0..7` | virtio-blkd | ✅ text listing | per-disk scheme |
| NVMe disk | `disk.nvme0..7` | nvmed | ✅ text listing | per-disk scheme |
| USB disk | `disk.usb0..7` | usbscsid | ✅ text listing | per-disk scheme |
| IDE disk | `disk.ide0..3` | ideid | ✅ text listing | per-disk scheme |
| **Aggregators** | | | | |
| Disk aggregator | `diskd` | diskd | ✅ real DirEntry BlockDev | THIS PLAN |
| **Display** | | | | |
| Framebuffer | `display` | vesad | EOPNOTSUPP | Legacy text listing |
| DRM/KMS | `drm` | redox-drm | ✅ real DirEntry | card0, card0-*, connectors |
| **Network** | | | | |
| Ethernet | `net` | e1000d/virtio-netd | ✅ real DirEntry | interface entries |
| **Input** | | | | |
| Input events | `input` | evdevd | ✅ real DirEntry | event0, event1, ... |
| **Audio** | | | | |
| Audio | `audio` | audiod | ✅ text listing | Audio streams |
| **System** | | | | |
| Firmware | `firmware` | firmware-loader | ✅ real DirEntry | GPU/device blobs |
| Udev | `udev` | udev-shim | ✅ real DirEntry | Linux-compatible device nodes |
### 4.5 initnsmgr getdents — Already Correct
The `initnsmgr` `getdents` implementation at line 402-439 of `initnsmgr.rs` iterates
`schemes: HashMap<String, Arc<FdGuard>>` and emits a `DirEntry` for each registered scheme.
This is already correct — it will list any scheme that has been registered, including `diskd`.
**The `/scheme/` listing issue was NOT a getdents bug** — it was a timing issue:
- Daemons hadn't registered yet when `fs::read_dir("/scheme")` was called
- The fix is proper boot ordering (diskd at priority 45) and the diskd aggregator
## 5. Future Enhancements (Beyond Current Scope)
### 5.1 Event-Driven Discovery (uevent Equivalent)
Currently `diskd` probes statically at startup. For hotplug (USB drives, PCIe hot-add):
- **pcid** sends a `uevent`-like notification when a new PCI device appears
- **diskd** listens for these notifications and re-scans
- Alternative: inotify-like watch on `/scheme/` (would need kernel support)
This mirrors Linux's `uevent` netlink broadcast → `udev` listener pattern.
### 5.2 devfs-Style Aggregation
A future `devfsd` could provide Linux-compatible `/dev` paths:
```
/scheme/devfs/sda → /scheme/diskd/0
/scheme/devfs/sda1 → /scheme/diskd/0p1
/scheme/devfs/null → /scheme/debug (write sink)
/scheme/devfs/zero → /scheme/memory (zero-filled read)
/scheme/devfs/random → /scheme/entropy
/scheme/devfs/tty0 → /scheme/display.0
/scheme/devfs/input/event0 → /scheme/input/event0
```
This would be the Fuchsia devcoordinator equivalent — a unified, Linux-compatible
device namespace. The `udev-shim` already provides parts of this.
### 5.3 Per-Process Namespace (Plan 9 Style)
Plan 9's `bind` and `mount` allow per-process namespace customization. Red Bear OS's
`setrens` syscall provides a basic version (switch namespace fd). Future enhancement:
- Per-container namespaces (for `contain` and future container runtime)
- Namespace inheritance rules (like Fuchsia's `.cml` capability routing)
- `chroot`-like namespace restriction for sandboxed applications
### 5.4 Capability-Based Access (seL4 Style)
seL4 uses CSpace (capability spaces) for device access. Each process has a CSpace that
contains only the capabilities it should have access to. Red Bear OS could evolve toward
this model:
- `initnsmgr` tracks which schemes each process can access
- `open("/scheme/net")` checks the caller's capability set
- `setrens` evolves from "switch namespace" to "restrict to capability subset"
This would require kernel changes (per-process scheme allowlists), which is beyond current
scope but worth keeping in mind for security hardening.
## 6. Implementation Plan
### Phase 1 — Immediate Fix (This Session)
| Step | Action | Files | Status |
|------|--------|-------|--------|
| 1 | diskd daemon implementation | `local/recipes/system/diskd/` | ✅ Done |
| 2 | Add diskd init service | `local/sources/base/init.initfs.d/45_diskd.service` | Pending |
| 3 | Add diskd to config | `config/redbear-mini.toml` | Pending |
| 4 | Modify redoxfs to use diskd | `local/sources/redoxfs/src/bin/mount.rs` | Pending |
| 5 | Commit uncommitted changes | driver-manager, config | Pending |
| 6 | Remove pcid debug logging | `local/sources/base/drivers/pcid/src/cfg_access/fallback.rs` | Pending |
| 7 | Make C++ header fix durable | `mk/prefix.mk` | Pending |
| 8 | Build and test ISO | `./local/scripts/build-redbear.sh redbear-mini` | Pending |
| 9 | Boot test in QEMU | `scripts/run_mini1.sh` | Pending |
### Phase 2 — Hotplug Support (Future)
| Step | Action | Dependencies |
|------|--------|--------------|
| 1 | pcid uevent notification | pcid-spawner enhancement |
| 2 | diskd dynamic re-scan | uevent listener |
| 3 | devfsd Linux-compatible /dev | udev-shim + diskd integration |
### Phase 3 — Namespace Security (Future)
| Step | Action | Dependencies |
|------|--------|--------------|
| 1 | Per-process scheme allowlist | kernel scheme access control |
| 2 | Container namespace isolation | contain enhancement |
| 3 | Capability routing | initnsmgr capability model |
## 7. Cross-Reference Summary
| System | Mechanism | Red Bear Equivalent | Status |
|--------|-----------|---------------------|--------|
| **Linux** | kobject/uevent → udev → /dev | pcid → diskd → /scheme/diskd | Phase 1 |
| **Fuchsia** | devcoordinator → devfs | initnsmgr → diskd | Phase 1 |
| **seL4** | CSpace capabilities | setrens (basic) | Phase 3 |
| **Plan 9** | bind/mount per-process | setrens (basic) | Phase 3 |
| **Genode** | Platform session | redox-driver-sys | Existing |
| **MINIX 3** | driver announce → devfs | daemon register → initnsmgr | Existing |
## 8. Risk Assessment
| Risk | Mitigation |
|------|------------|
| diskd probe takes too long on real hardware | Increase retry count (50×200ms = 10s), add event-driven re-scan |
| diskd crashes and disk namespace disappears | init service auto-restart (`restart = true` in service file) |
| redoxfs legacy path broken by diskd changes | Two-path approach: try diskd first, fall back to legacy |
| Boot ordering regression (diskd starts before lived) | Explicit `requires = ["lived"]` in service file |
| diskd returns stale device list after hotplug | Phase 2: event-driven re-scan; Phase 1: manual re-trigger via signal |
## 9. Acceptance Criteria
1. `ls /scheme/` in shell shows all registered schemes (no hang, no empty)
2. `ls /scheme/diskd/` shows all block devices discovered by diskd
3. `redoxfs` mounts rootfs read-write via diskd path
4. `/tmp` is writable by non-root users
5. Boot completes to login prompt with zero warnings
6. QEMU boot test passes: `scripts/run_mini1.sh` reaches login prompt
7. `./local/scripts/build-redbear.sh redbear-mini` produces working ISO