26 Commits

Author SHA1 Message Date
vasilito 520e92cad8 chore: checkpoint before 0.2.3 build system migration 2026-05-29 21:53:11 +03:00
vasilito aa9d14a90e docs: update AGENTS.md and PATCH-GOVERNANCE.md
AGENTS.md: updated session progress, coretempd/login fix notes, Intel plan references. PATCH-GOVERNANCE.md: added mega-patch discipline section and P-patch workflow documentation.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-29 21:50:28 +03:00
vasilito 44bcf2b75a docs: add Intel driver modernization plan
Comprehensive 6-phase plan (1,055 lines) for updating the Intel GPU driver from current 1,590-line stub to full Gen9+ support ported from Linux 7.1 i915. Covers register abstraction, GMBUS I2C, DMC firmware, power wells, CDCLK, display pipeline, modesetting, and hardware validation.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-29 21:50:07 +03:00
vasilito 7cd5bfbb83 fix: enable redox-rt proc feature in userutils to fix login crash
userutils compiled redox-rt with default-features=false, disabling the proc feature. This caused login's fork to not pass proc fd to child shell, triggering assertion failed: info.has_proc_fd in redox-rt. P8 patch enables features=['proc']. Verified: zero panics on boot, login works for user/root.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-29 21:49:47 +03:00
vasilito 5987fffde7 fix: P12 init_debug import error in base init
The init_debug macro was used without importing it, causing a compile error. P12 patch adds the missing import. Wired into base recipe.toml patches list.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-29 21:49:07 +03:00
vasilito 706050482b fix: rewrite coretempd to use redox_scheme Socket + SchemeSync
Replaced broken UnixListener::bind(':coretemp') with proper redox_scheme::Socket::create() + SchemeSync trait impl. Event loop uses next_request/handle_sync/write_response pattern. Verified: registers scheme:coretemp, detects CPU info, zero panics.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-29 21:48:51 +03:00
vasilito daf131d435 P7 login diagnostics, P11 init noise reduction, config layering fix 2026-05-29 19:13:16 +03:00
vasilito 0ccc233131 P10: fix Arrow Lake device IDs and DMC firmware mapping
The driver incorrectly labeled Lunar Lake IDs (0x6420/64A0/64B0) as
Arrow Lake, and placed the real Arrow Lake IDs (0x7D41/7D51/etc.) in the
Meteor Lake bucket. This meant:
- Arrow Lake notebooks were misidentified as Meteor Lake
- Lunar Lake was completely missing from the device tables
- The 0xB640 ID (ARL-H) was also misfiled

Fix:
- Move real Arrow Lake IDs (0x7D41, 0x7D51, 0x7DD1, 0x7D67, 0xB640)
  to INTEL_GEN12_ARL_IDS
- Move Lunar Lake IDs (0x6420, 0x64A0, 0x64B0) to INTEL_GEN12_LNL_IDS
- Map Arrow Lake DMC firmware to INTEL_MTL_DMC_KEYS (mtl_dmc.bin),
  since Arrow Lake uses the same display IP 14.0 as Meteor Lake
- Remove Arrow Lake IDs from the Meteor Lake bucket

Per Linux 7.1 reference: Arrow Lake display engine is IP 14.0 (Xe_LPD+),
same as Meteor Lake — NOT Xe2. The i915-style register programming is
correct for Arrow Lake.
2026-05-29 17:49:11 +03:00
vasilito c0a93e5cfa Add input driver init services and driver-manager configs
- ps2d: PS/2 keyboard/mouse (init service + driver-manager wildcard match)
- i2c-hidd: I2C HID keyboard/touchpad (init service + driver-manager match)
- intel-thc-hidd: Intel Touch Host Controller HID (init service + PCI match)

Fixes i2c-hidd path: /usr/bin/i2c-hidd (not /usr/lib/drivers/).

These drivers were already built in the base package but were not wired
into the boot process. Modern Intel notebooks typically use either
i8042 EC emulation (PS/2) or I2C HID for keyboard/touchpad.
2026-05-29 15:44:35 +03:00
vasilito 0a4a77a56b P27: add missing proc.rs guard.caps derivation from euid
The capability bitmask patch was missing the critical proc.rs hunk that
derives caps from euid when procmgr writes ProcSchemeAttrs to a child
context. Without this, all child processes had caps=0, causing
EACCES on dup("create-scheme") and crashing boot.

Fix: in ContextHandle::Attr kwrite path, after setting euid/egid/pid/prio,
set guard.caps = CAP_ALL when euid==0, else 0.
2026-05-29 14:25:37 +03:00
vasilito d2c761a56c fix: P27 caps init + P6 type errors and overlap with P5
P27: add caps: 0 to Context::new() default initialization.
P6: fix syscall::Error vs libredox::Error type mismatch (use ?),
    fix usize->u32 casts for Resugid fields, remove P5 overlap
    (issue/motd/consecutive_failures already in P5), add namespace
    isolation to password-verified auth path.

All 39 kernel patches validate. Full image builds.
2026-05-29 12:24:51 +03:00
vasilito f40b751bca fix: regenerate P27 kernel capability patch against clean P0-P26 baseline
P27-capability-bitmask.patch was generated against an incorrectly
patched source tree, causing hunk mismatches in validate-patches.
Regenerated from clean upstream + P0-P26 baseline using git diff -U0 -w.

All 39 patches now validate successfully.
2026-05-29 11:37:22 +03:00
vasilito ce9ff8aebd feat: Phase 2 - kernel capability bitmask (uid==0 -> has_cap())
Replace all 9 kernel uid==0 privilege checks with a capability bitmask
model. Adds caps:u64 field to Context and CallerCtx, with CAP_ALL for
root processes. Zero behavioral change - uid==0 still gets all caps.

New module: src/scheme/caps.rs with 10 capability constants.
9 check sites converted: acpi, irq, memory, debug, serio, sys (msr+write),
scheme registration, and fchown.

Patch: local/patches/kernel/P27-capability-bitmask.patch
2026-05-29 10:25:09 +03:00
vasilito bb3ae6e63f feat: Phase 1 - Plan 9 namespace privilege drop + branding
- login.rs: drop privileges via setresugid after authentication
- login.rs: add namespace isolation to password auth path (was missing)
- login.rs: add drm, input schemes to DEFAULT_SCHEMES
- sudo service: rename 00_sudo -> 12_sudo, type daemon (no boot block)
- Branded login screen with figlet RedBear OS v0.2.2 'Liliya'
- Root user kept but not advertised on login screen
- P6-login-privilege-drop.patch generated and wired

Implements Phase 1 of Plan 9 namespace privilege model:
login creates restricted namespace (mkns/setns) then drops
uid/gid to authenticated user before spawning shell.
2026-05-29 09:54:28 +03:00
vasilito 61135b0cce chore: bump OS version to 0.2.2 2026-05-29 09:06:24 +03:00
vasilito 9db9c3bdc9 feat: ISO size reduction, user account, SDDM, PAM, VirtIO fixes, KDE/Qt patches
- Trim redbear-firmware from 1816MB to 143MB (GPU+WiFi only)
- Reduce filesystem_size from 8192 to 2048 MB
- Add unprivileged user account (uid=1000, sudo group)
- Add SDDM display manager recipe with Wayland-only patches
- Add pam-redbear PAM module for authentication
- Fix VirtIO queue timeout (SeqCst fence, remove permanent failure)
- KDE/KWin build fixes (libinput, wayland socket, ramfile, tabletmode)
- Qt6 build fixes (platformdefs, socket engine, Wayland integration)
- KF6 CMake fixes (attica, kcmutils, kcolorscheme, kcompletion, etc.)
- libxml2 build fix, libxkbcommon recipe fix
- Remove gcc-native/binutils-native from desktop ISO
2026-05-29 09:00:55 +03:00
vasilito 845ae99f9d fix graphical boot: DRM scheme detection, ConsoleKit bypass, boot chain deps
Three fixes for the KWin DRM device discovery failure:

1. drm_scheme_ready(): replace head -c 1 with exec 3< open test.
   Reading from a DRM scheme fd blocks because the scheme expects
   ioctl-style request/response, not streaming reads. Use open()
   success as the scheme availability probe instead.

2. ConsoleKitSession::create(): return nullptr immediately.
   The D-Bus isServiceRegistered() call can block indefinitely when
   the bus daemon doesn't fully implement org.freedesktop.DBus.
   With both LogindSession and ConsoleKitSession returning nullptr,
   Session::create() falls through to NoopSession which uses plain
   open() for DRM device access.

3. Boot chain deps: redox-drm depends on driver-manager,
   greeter depends on evdevd (keyboard/mouse ready before login).

Also includes: KF6 CMake build fixes, Qt6 platform patches,
libdrm Redox ioctl shim, and wayland.toml scheme check fix.
2026-05-28 23:19:49 +03:00
vasilito 5c5f853192 fix: correct libdrm patch relative paths for symlink resolution
The cookbook resolves patch paths from recipe.dir which is the symlink
path (recipes/libs/libdrm/), not the physical path (local/recipes/).
Fix ../../../patches/libdrm/ → ../../../local/patches/libdrm/ to
match the convention used by kernel, base, relibc, and other recipes.
2026-05-28 18:36:57 +03:00
vasilito ece9837d15 fix: auto-discover all local recipes in integrate-redbear.sh
Replace 95-line manual symlink list with auto-discovery of all
local/recipes/<category>/<name>/ directories. This fixes 15 missing
symlinks that would have blocked the redbear-full build, including
critical packages: libdrm, qtbase, qtwayland, libinput, libevdev,
seatd, and wayland-protocols.

Special-case aliases preserved:
- kf6-kirigami → kirigami (KDE expects both names)
- wip/wayland/qt6-wayland-smoke (historical WIP path)
2026-05-28 18:31:21 +03:00
vasilito d26675708e Phase 4: RAM-disk boot, recipe catalog, collision validation
L1: Add make qemu-ram target — copies disk image to host tmpfs before
    QEMU boots, eliminating host disk I/O during OS runtime.
    Usage: make qemu-ram CONFIG_NAME=redbear-full QEMU_MEM=12288

L2: Create local/recipes/AGENTS.md — comprehensive catalog of all 165
    custom recipes across 15 categories with descriptions.

L3: CollisionTracker already fully implemented and wired into installer
    (recipes/core/installer/source/src/collision.rs, 267 lines).

L4: Add scripts/validate-collision-log.sh to make validate target —
    scans build logs for [COLLISION-ERROR]/[COLLISION-WARN] markers
    from the runtime CollisionTracker.
2026-05-28 18:16:48 +03:00
vasilito 2d11c98428 fix: add 8 missing recipes to protected-recipes.toml
Missing from initial TOML conversion:
- kf6-ksvg, kf6-pty, kf6-notifyconfig, kf6-parts (KDE frameworks)
- kglobalacceld (KDE global accelerator daemon)
- redbear-keymapd, redbear-ime, redbear-accessibility (input services)

Total protected recipes: 119 (matches original hardcoded list)
2026-05-28 17:54:07 +03:00
vasilito 5c127bf6f4 fix stale config names in 13 test/validation scripts
redbear-minimal -> redbear-mini (config renamed, old name never existed as file)
redbear-desktop -> redbear-full (desktop is the full target)
redbear-live-full/redbear-live-minimal -> removed (never existed)

Also fix verify-overlay-integrity.sh critical config list:
- Remove 4 nonexistent configs (redbear-live-full, redbear-live-minimal, redbear-desktop)
- Add 2 missing configs (redbear-grub, redbear-grub-policy, redbear-boot-stages)
2026-05-28 17:46:52 +03:00
vasilito a0244075e7 build system audit: implement Phase 1-3 fixes comprehensively
Phase 1 (Critical):
- Fix broken config includes: redbear-minimal -> redbear-mini in wifi/bt experimental configs
- Fix 05_boot-essential.target dependency: 00_base -> 04_drivers for correct boot ordering
- Fix IOMMU service dependency: 00_base -> 05_boot-essential
- Fix firmware-loader dependency: 00_base -> 05_boot-essential
- Fix messagebus shell: /usr/bin/zsh -> /usr/bin/false (security)
- Add offline gate to fetch-firmware.sh (REPO_OFFLINE=1 blocks network access)
- Add --upstream gate to fetch-all-sources.sh (network access requires explicit opt-in)
- Gate U-Boot wget calls in mk/qemu.mk with REPO_OFFLINE check
- Fix patch-inclusion-gate.sh: rewrite from Python deps to pure shell implementation
- Fix build-redbear.sh: remove direct patch application, let repo fetch handle it atomically

Phase 2 (High):
- Increase redbear-full filesystem_size: 4096 -> 8192 MiB for KDE desktop
- Deprecate redbear-greeter-services.toml (orphaned, not included by any config)
- Add cascade rebuild target to Makefile (make cascade.<package>)
- Gate cargo-update.sh with REDBEAR_ALLOW_UPSTREAM
- Add deprecation notice to apply-patches.sh
- Make protected recipe list data-driven via config/protected-recipes.toml
- Replace 127-entry hardcoded Rust matches! with TOML config file reader

Phase 3 (Medium):
- Fix 5 phantom doc references in local/AGENTS.md (retired/removed docs)
- Fix stale config names: redbear-minimal -> redbear-mini across scripts
- Fix duplicate references in docs/README.md
- Fix run_full.sh and run_mini.sh: hardcoded paths -> relative paths + error handling
2026-05-28 17:24:50 +03:00
vasilito 2b11b20a2f libdrm: fix drmGetDeviceFromDevId for Redox (P4)
Add #ifdef __redox__ path to drmGetDeviceFromDevId() that mirrors the
working drmGetDevice2() Redox implementation. On Redox there is no
/dev/dri/ directory — DRM devices are accessed via /scheme/drm/card0.
The patch constructs a drmDevice with both PRIMARY and RENDER nodes
pointing to /scheme/drm/card0, since the redox-drm scheme serves both
roles through a single endpoint.

Also fixes drmParseSubsystemType() to return DRM_BUS_PCI on Redox.

Fix P3 patch paths (strip local/recipes/libs/libdrm/source/ prefix
from diff headers so patches apply correctly during repo fetch).
2026-05-28 16:35:16 +03:00
vasilito cb50169517 P0-P3 baseline for P4 2026-05-28 15:49:45 +03:00
vasilito 328d1abbcd rate-limit scheme error spam to prevent serial log flood 2026-05-28 00:36:17 +03:00
197 changed files with 23948 additions and 31727 deletions
+153 -3
View File
@@ -1,6 +1,6 @@
# RED BEAR OS BUILD SYSTEM — PROJECT KNOWLEDGE BASE
**Generated:** 2026-04-12 (P1/P2 complete)
**Generated:** 2026-04-12 (P1/P2 complete) · Updated: 2026-05-29 (mega-patch discipline)
**Toolchain:** Rust nightly-2025-10-03 (edition 2024)
**Architecture:** Microkernel OS in Rust, ~38k files, ~294k LoC Rust
**Target Hardware**: AMD64 bare metal, with AMD and Intel machines treated as equal-priority Red Bear OS targets
@@ -457,6 +457,49 @@ See `local/docs/BUILD-SYSTEM-HARDENING-PLAN.md` for the full plan.
- **DO NOT** skip warnings — investigate, diagnose, and fix the root cause; suppressing or ignoring warnings is not acceptable when a fix is feasible
- **DO NOT** remove patches from `recipe.toml` to fix build failures — rebase the patch instead (see `local/docs/PATCH-GOVERNANCE.md`)
- **DO NOT** remove BINS entries to fix build failures — fix the source or use EXISTING_BINS filtering
- **DO NOT** use the VESA display driver (`vesad`) as the primary display surface after GPU detection. vesad is only for early-boot framebuffer handoff — after redox-drm loads, the display path is `/scheme/drm/card0`. See **NO VESA POLICY** below.
## NO VESA POLICY
Red Bear OS does not use the VESA display driver as the primary display surface. All display
output goes through the DRM/KMS path via real GPU drivers:
| Environment | GPU Driver | 3D Support |
|---|---|---|
| QEMU | virtio-gpu (via redox-drm) | ✅ virgl |
| Intel hardware | Intel i915-like (via redox-drm) | ✅ Mesa i965/iris |
| AMD hardware | amdgpu (via redox-drm + linux-kpi) | ✅ Mesa radeonsi |
| Future | nouveau reimplementation (Rust, via redox-drm) | ✅ Mesa nouveau |
**vesad is allowed ONLY as an early-boot framebuffer handoff.** The bootloader sets up a linear
framebuffer before the kernel starts. vesad takes over this framebuffer so the initfs has console
output (fbcond, fbbootlogd) before real GPU drivers are available. Once redox-drm initializes and
registers `scheme:drm/card0`, vesad must hand off and NOT register `scheme:display.vesa` as the
primary display surface.
The display path for redbear-full:
```
Bootloader linear framebuffer
→ vesad (initfs, service 20): temporary FB handoff for text console
→ redox-drm (initfs, service 30): detects GPU hardware, takes over via DRM/KMS
→ redox-drm (rootfs, service 14): full DRM driver with 3D (Mesa)
→ KWin compositor: DRM/KMS master, composites desktop via /scheme/drm/card0
```
For redbear-mini: vesad handles the bootloader framebuffer for the text-only console. No GPU
driver loads — mini is text-only by design.
**After GPU detection, any code that opens `/scheme/display.vesa/` is incorrect.** The correct
display path is `/scheme/drm/card0` via the DRM scheme.
Rationale: VESA is a legacy BIOS-era standard with no hardware acceleration, no mode setting
beyond what the bootloader provides, no 3D, and no future. Red Bear OS targets real GPU
hardware with full DRM/KMS and Mesa support. vesad serves only as a bridge between bootloader
FB and the real GPU driver — it is never the final display path.
This policy also covers future GPU driver work: any new GPU support (nouveau Rust reimplementation,
ARM Mali, etc.) must go through the redox-drm + DRM/KMS path, never through VESA fallback.
## ZERO TOLERANCE FOR STUBS
@@ -769,10 +812,116 @@ patches must be respected:
When reordering patches: remove the source tree, re-fetch, and rebuild to verify.
### MEGA-PATCH + INDIVIDUAL PATCH DISCIPLINE (CRITICAL)
**This is the single most common source of build failures in Red Bear OS.**
Violating these rules causes patches to silently drift, overlap, or conflict —
wasting hours of debugging time.
#### The Two-Layer Architecture
Each patched component (base, kernel, relibc) uses two patch layers:
1. **The mega-patch** (`redox.patch`) — a single consolidated `git diff` of the
entire source tree against upstream. Applied first. This is the **frozen baseline**
of all Red Bear work up to a known date.
2. **Individual P-patches** (`P10-*.patch`, `P11-*.patch`, etc.) — granular,
single-purpose patches for new work done AFTER the mega-patch was generated.
Applied after the mega-patch, in listed order.
The `recipe.toml` patches list looks like:
```toml
patches = [
"redox.patch", # Layer 1: frozen baseline
"P10-rootfs-uuid-search-no-block.patch", # Layer 2: new work on top
"P11-init-noise-reduction.patch", # Layer 2: new work on top
]
```
#### The Discipline (MANDATORY)
| Rule | Why | What happens if violated |
|------|-----|--------------------------|
| **NEVER regenerate `redox.patch` from a source tree that includes P-patches** | The mega-patch absorbs P-patch changes, making P-patches redundant. They then fail to apply ("Reversed or previously applied") on the next clean build. | P-patches conflict with their own changes inside the mega-patch. Build fails. |
| **New work always goes as P-patches AFTER the mega-patch** | Keeps the mega-patch frozen. Each P-patch is a small, reviewable delta. | Mixing changes into the mega-patch makes it a monolith with no logical structure. |
| **To regenerate `redox.patch`, first fold all P-patches into it, then remove them from `recipe.toml`** | Consolidation pass must be atomic — absorb and remove in one step. | Orphan P-patches in `recipe.toml` reference changes already in the mega-patch. |
| **P-patches MUST apply cleanly on top of the mega-patch-only state** | The build system applies patches sequentially: upstream → mega-patch → P-patches. | Build fails on clean fetch. |
| **Validate with `repo validate-patches` after ANY patch change** | Catches drift before it reaches the build. | Drift silently accumulates until the next clean build explodes. |
#### How to Make a New Change (Correct Workflow)
```bash
# 1. Source tree already has mega-patch + existing P-patches applied (working tree)
cd recipes/core/base/source
# 2. Make your edit
vim init/src/main.rs
# 3. Generate the patch against the current git HEAD (upstream rev)
git diff -U0 -w -- init/src/main.rs > ../../../local/patches/base/P<next>-<desc>.patch
# 4. Create symlink and wire into recipe.toml
cd ../../../recipes/core/base
ln -s ../../../local/patches/base/P<next>-<desc>.patch P<next>-<desc>.patch
# Add "P<next>-<desc>.patch" to patches list in recipe.toml
# 5. Validate and rebuild
cd ../../..
./target/release/repo validate-patches base
CI=1 ./target/release/repo cook base-initfs
```
#### How to Consolidate (Periodic Maintenance)
When P-patches accumulate and the mega-patch should absorb them:
```bash
# 1. Source tree has mega-patch + all P-patches applied
cd recipes/core/base/source
# 2. Generate NEW mega-patch from full diff
git diff -U0 -w > ../../../local/patches/base/redox.patch
# 3. Remove ALL P-patches from recipe.toml patches list
# Keep P-patch FILES in local/patches/base/ for history — just remove from recipe.toml
# 4. Validate
./target/release/repo validate-patches base
# 5. Rebuild
CI=1 ./target/release/repo cook base-initfs
```
#### How NOT to Break Things
| Action | Correct | WRONG |
|--------|---------|-------|
| Add new feature | Create P-patch, add after mega-patch in recipe.toml | Regenerate mega-patch from tree that includes P-patches |
| Fix a bug | Create P-patch | Edit mega-patch directly |
| Consolidate | Regenerate mega-patch, remove ALL P-patches from recipe.toml | Regenerate mega-patch but leave P-patches in recipe.toml |
| Update upstream | Provision new release, rebase mega-patch | Cherry-pick upstream commits into source tree |
#### Root Cause of Past Failures
The pattern that has recurred multiple times:
1. Mega-patch generated at time T
2. P-patches added at time T+1, T+2, etc.
3. Someone regenerates mega-patch from source tree at T+3 (which includes P-patch changes)
4. Mega-patch now contains P-patch changes
5. P-patches still in `recipe.toml` try to re-apply their changes → conflicts
6. Build fails with "Reversed or previously applied" and hunk failures
7. Hours spent debugging why "patches that used to work" now fail
**The fix is always the same**: either (a) remove the absorbed P-patches from `recipe.toml`,
or (b) regenerate the mega-patch from a tree WITHOUT P-patch changes. Option (a) is faster.
### Large Patch Files (redox.patch)
`local/patches/base/redox.patch` (consolidated mega-patch) is stored as 90 MB
chunks under `local/patches/base/redox-patch-chunks/` and reassembled by:
`local/patches/base/redox.patch` (consolidated mega-patch) is currently ~544K.
If it grows beyond a manageable size, it can be chunked under
`local/patches/base/redox-patch-chunks/` and reassembled by:
```bash
local/patches/base/reassemble-redox-patch.sh
```
@@ -787,6 +936,7 @@ Critical rules:
- **Source trees are disposable** — `repo clean`/`distclean` destroy them
- **All source changes must be patches** in `local/patches/`
- **Commit patch files and recipe.toml changes** before session end
- **NEVER regenerate mega-patch from a tree that includes P-patches** — see MEGA-PATCH DISCIPLINE above
### Build Validation
+2
View File
@@ -231,3 +231,5 @@ packages-sync: ; @bash local/scripts/sync-packages.sh
packages-list: ; @ls -la Packages/*.pkgar 2>/dev/null | wc -l && echo "pkgar files in Packages/"
validate-patches:
@bash local/scripts/validate-patches.sh
cascade.%: FORCE
@bash local/scripts/rebuild-cascade.sh $(basename $(subst cascade,, $*))
+3 -4
View File
@@ -122,10 +122,10 @@ data = """
[[files]]
path = "/usr/lib/os-release"
data = """
PRETTY_NAME="Red Bear OS 0.1.0"
PRETTY_NAME="Red Bear OS 0.2.2"
NAME="Red Bear OS"
VERSION_ID="0.1.0"
VERSION="0.1.0"
VERSION_ID="0.2.2"
VERSION="0.2.2"
ID="redbear-os"
ID_LIKE="redox-os"
@@ -310,7 +310,6 @@ gid = 0
shell = "/usr/bin/zsh"
[users.user]
# Password is unset
password = ""
shell = "/usr/bin/zsh"
+98
View File
@@ -0,0 +1,98 @@
# Protected recipes — these recipes are NEVER re-fetched from upstream.
# They use offline/archived sources from sources/redbear-<release>/.
#
# Protection reasons:
# - patched: carries Red Bear patches (upstream changes could break patches)
# - custom: Red Bear-specific recipe (no upstream equivalent)
# - core: core system component (kernel, libc, bootloader, etc.)
#
# The Rust code in src/cook/fetch.rs reads this file at startup.
# Recipes NOT listed here but carrying patches (patches = [...] in recipe.toml)
# are automatically protected by the recipe_has_patches() check.
# Core patched recipes (upstream + Red Bear patches)
[patched]
recipes = [
"relibc", "bootloader", "kernel", "base", "base-initfs",
"installer", "redoxfs", "grub",
]
# Red Bear custom core recipes
[custom]
recipes = [
"ext4d", "fatd",
]
# Red Bear driver infrastructure
[drivers]
recipes = [
"redox-driver-sys", "linux-kpi", "firmware-loader",
"redbear-btusb", "redbear-iwlwifi",
"redox-drm", "amdgpu",
]
# Red Bear system tools
[system]
recipes = [
"cub", "evdevd", "udev-shim", "iommu",
"redbear-firmware", "redbear-hwutils", "redbear-info", "rbos-info",
"redbear-meta", "redbear-netctl", "redbear-netctl-console",
"redbear-netstat", "redbear-btctl", "redbear-wifictl",
"redbear-traceroute", "redbear-mtr", "redbear-nmap",
"redbear-sessiond", "redbear-authd", "redbear-session-launch",
"redbear-greeter", "redbear-dbus-services", "redbear-notifications",
"redbear-upower", "redbear-udisks", "redbear-polkit", "redbear-quirks",
"redbear-release", "redbear-keymapd", "redbear-ime", "redbear-accessibility",
]
# Qt stack with Red Bear patches
[qt]
recipes = [
"qtbase", "qtwayland", "qtdeclarative", "qtbase-compat",
]
# Graphics / display stack with Red Bear patches
[graphics]
recipes = [
"libdrm", "mesa",
"libwayland", "libevdev", "libinput",
"dbus", "glib",
]
# Red Bear library stubs and custom libs
[libs]
recipes = [
"libepoxy-stub", "libdisplay-info-stub", "lcms2-stub",
"libxcvt-stub", "libudev-stub", "zbus", "libqrencode",
]
# Red Bear Wayland
[wayland]
recipes = [
"qt6-wayland-smoke", "smallvil", "seatd-redox",
]
# Red Bear KDE (47 recipes)
[kde]
recipes = [
"kf6-extra-cmake-modules", "kf6-kcoreaddons", "kf6-kwidgetsaddons",
"kf6-kconfig", "kf6-ki18n", "kf6-kcodecs", "kf6-kguiaddons",
"kf6-kcolorscheme", "kf6-kauth", "kf6-kitemmodels", "kf6-kitemviews",
"kf6-karchive", "kf6-kwindowsystem", "kf6-knotifications",
"kf6-kjobwidgets", "kf6-kconfigwidgets", "kf6-kcrash", "kf6-kdbusaddons",
"kf6-kglobalaccel", "kf6-kservice", "kf6-kpackage", "kf6-kiconthemes",
"kf6-kxmlgui", "kf6-ktextwidgets", "kf6-solid", "kf6-sonnet",
"kf6-kio", "kf6-kbookmarks", "kf6-kcompletion", "kf6-kdeclarative",
"kf6-kcmutils", "kf6-kidletime", "kf6-kwayland", "kf6-knewstuff",
"kf6-kwallet", "kf6-prison", "kf6-kirigami",
"kf6-ksvg", "kf6-pty", "kf6-notifyconfig", "kf6-parts",
"kdecoration", "kwin", "plasma-desktop", "plasma-workspace",
"plasma-framework", "plasma-wayland-protocols", "kirigami",
"kglobalacceld",
]
# Orbutils (has local patch)
[other]
recipes = [
"orbutils",
]
+1 -1
View File
@@ -7,7 +7,7 @@
# The current slice is explicit-startup, USB-attached, BLE-first, and intentionally not wired to
# USB-class autospawn yet.
include = ["redbear-minimal.toml", "redbear-bluetooth-services.toml"]
include = ["redbear-mini.toml", "redbear-bluetooth-services.toml"]
[general]
filesystem_size = 2048
+77 -1
View File
@@ -240,6 +240,46 @@ command = ["/usr/lib/drivers/ps2d"]
[[driver.match]]
vendor = 0xFFFF
device = 0xFFFF
[[driver]]
name = "i2c-hidd"
description = "I2C HID keyboard and touchpad driver"
priority = 85
command = ["/usr/bin/i2c-hidd"]
[[driver.match]]
vendor = 0xFFFF
device = 0xFFFF
[[driver]]
name = "intel-thc-hidd"
description = "Intel Touch Host Controller HID driver"
priority = 85
command = ["/usr/lib/drivers/intel-thc-hidd"]
[[driver.match]]
vendor = 0x8086
device = 0x7eb8
[[driver.match]]
vendor = 0x8086
device = 0x7eb9
[[driver.match]]
vendor = 0x8086
device = 0x7ebd
[[driver.match]]
vendor = 0x8086
device = 0x7ebe
[[driver.match]]
vendor = 0x8086
device = 0xa8b8
[[driver.match]]
vendor = 0x8086
device = 0xa8b9
"""
[[files]]
@@ -444,7 +484,7 @@ requires_weak = ["04_drivers.target"]
[service]
cmd = "/usr/bin/coretempd"
type = { scheme = "coretemp" }
type = "oneshot_async"
"""
[[files]]
@@ -506,3 +546,39 @@ requires_weak = ["04_drivers.target"]
cmd = "/usr/bin/redbear-usbaudiod"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/10_ps2d.service"
data = """
[unit]
description = "PS/2 keyboard and mouse driver"
requires_weak = ["00_driver-manager.service"]
[service]
cmd = "/usr/lib/drivers/ps2d"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/10_i2c-hidd.service"
data = """
[unit]
description = "I2C HID keyboard and touchpad driver"
requires_weak = ["00_driver-manager.service"]
[service]
cmd = "/usr/bin/i2c-hidd"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/10_intel-thc-hidd.service"
data = """
[unit]
description = "Intel Touch Host Controller HID driver"
requires_weak = ["00_driver-manager.service"]
[service]
cmd = "/usr/lib/drivers/intel-thc-hidd"
type = "oneshot_async"
"""
+103 -22
View File
@@ -6,18 +6,25 @@
#
# Extends redbear-mini with the full desktop/graphics stack:
# Wayland, Qt6, KF6, KWin, Mesa, DRM drivers, firmware, greeter.
#
# GPU/display policy: DRM/KMS ONLY. No VESA. Real GPU drivers:
# QEMU → virtio-gpu via redox-drm (virgl 3D)
# Intel → i915-like via redox-drm (Mesa i965/iris)
# AMD → amdgpu via redox-drm + linux-kpi (Mesa radeonsi)
# Display path: bootloader FB → redox-drm → DRM/KMS → KWin compositor
# Consult local/reference/linux-7.0/ for driver behavior reference.
include = ["redbear-mini.toml"]
[general]
filesystem_size = 4096
filesystem_size = 2048
[users.messagebus]
uid = 100
gid = 100
name = "messagebus"
home = "/nonexistent"
shell = "/usr/bin/zsh"
shell = "/usr/bin/false"
[users.root]
password = "password"
@@ -25,6 +32,14 @@ uid = 0
gid = 0
shell = "/usr/bin/zsh"
[users.user]
password = ""
uid = 1000
gid = 1000
name = "user"
home = "/home/user"
shell = "/usr/bin/zsh"
[packages]
# Runtime driver parameter control surface.
driver-params = {}
@@ -57,7 +72,7 @@ fontconfig = {}
libwayland = {}
wayland-protocols = {}
plasma-wayland-protocols = {}
redbear-compositor = "ignore" # replaced by kwin
redbear-compositor = {}
# Keyboard/input
libxkbcommon = {}
@@ -138,6 +153,7 @@ redbear-authd = {}
redbear-session-launch = {}
seatd = {}
redbear-greeter = {}
sddm = {}
amdgpu = {}
# Core Red Bear umbrella package
@@ -146,12 +162,10 @@ redbear-meta = {}
# Phase 1 runtime validation tests (POSIX: signalfd, timerfd, eventfd, shm_open, sem_open, waitid)
relibc-phase1-tests = {}
# Native build toolchain (Phase 3: GCC + binutils running on redox)
# Produces gcc/g++/as/ld that execute inside Red Bear OS
gcc-native = {}
binutils-native = {}
# llvm-native = {} # suppressed: Redox C++/pthread header gaps; not needed for greeter proof
# rust-native = {} # suppressed: depends on llvm-native; not needed for greeter proof
# Native build toolchain — excluded from desktop ISO to reduce size.
# For on-OS development, build redbear-dev config or install separately.
# gcc-native = {}
# binutils-native = {}
# Desktop fonts and icons
dejavu = {}
@@ -199,6 +213,15 @@ depends_on = ["pci"]
[[driver.match]]
class = 0x03
vendor = 0x1002
[[driver.match]]
class = 0x03
vendor = 0x8086
[[driver.match]]
class = 0x03
vendor = 0x1af4
"""
[[files]]
@@ -223,7 +246,7 @@ data = """
[unit]
description = "Firmware loading scheme"
requires_weak = [
"00_base.target",
"05_boot-essential.target",
]
[service]
@@ -237,7 +260,7 @@ data = """
[unit]
description = "Boot essential services target"
requires_weak = [
"00_base.target",
"04_drivers.target",
]
"""
@@ -247,7 +270,7 @@ data = """
[unit]
description = "IOMMU DMA remapping daemon"
requires_weak = [
"00_base.target",
"05_boot-essential.target",
]
[service]
@@ -256,12 +279,13 @@ type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/14_redox-drm.service"
path = "/etc/init.d/10_redox-drm.service"
data = """
[unit]
description = "DRM/KMS display driver (AMD + Intel + VirtIO)"
requires_weak = [
"05_boot-essential.target",
"00_driver-manager.service",
]
[service]
@@ -405,7 +429,7 @@ type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/19_redbear-authd.service"
path = "/etc/init.d/11_redbear-authd.service"
data = """
[unit]
description = "Red Bear authentication daemon"
@@ -420,22 +444,23 @@ type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/20_greeter.service"
path = "/etc/init.d/12_sddm.service"
data = """
[unit]
description = "Red Bear greeter service"
description = "SDDM display manager"
requires_weak = [
"00_driver-manager.service",
"14_redox-drm.service",
"10_redox-drm.service",
"10_evdevd.service",
"12_dbus.service",
"13_redbear-sessiond.service",
"13_seatd.service",
"19_redbear-authd.service",
"11_redbear-authd.service",
]
[service]
cmd = "/usr/bin/redbear-greeterd"
envs = { VT = "3", REDBEAR_GREETER_USER = "greeter", KWIN_DRM_DEVICES = "/scheme/drm/card0", REDBEAR_DRM_WAIT_SECONDS = "10" }
cmd = "/usr/bin/sddm"
envs = { QT_PLUGIN_PATH = "/usr/plugins", QT_QPA_PLATFORM_PLUGIN_PATH = "/usr/plugins/platforms", QML2_IMPORT_PATH = "/usr/qml", XCURSOR_THEME = "Pop", XKB_CONFIG_ROOT = "/usr/share/X11/xkb" }
type = "oneshot_async"
"""
@@ -506,17 +531,73 @@ password = ""
uid = 101
gid = 101
name = "greeter"
home = "/nonexistent"
home = "/var/lib/sddm"
shell = "/usr/bin/zsh"
[users.sddm]
password = ""
uid = 102
gid = 102
name = "sddm"
home = "/var/lib/sddm"
shell = "/usr/bin/nologin"
[groups.greeter]
gid = 101
members = ["greeter"]
members = ["greeter", "sddm"]
[groups.sddm]
gid = 102
members = ["sddm"]
[groups.sudo]
gid = 1
members = ["user"]
[groups.user]
gid = 1000
members = ["user"]
[groups.messagebus]
gid = 100
members = ["messagebus"]
[[files]]
path = "/etc/sddm.conf"
data = """
[General]
DisplayServer=wayland
GreeterEnvironment=QT_PLUGIN_PATH=/usr/plugins,QML2_IMPORT_PATH=/usr/qml,QT_QPA_PLATFORM_PLUGIN_PATH=/usr/plugins/platforms
[Theme]
Current=mayagrid
ThemeDir=/usr/share/sddm/themes
[Wayland]
CompositorCommand=/usr/libexec/sddm-helper-start-wayland kwin_wayland --drm /scheme/drm/card0
[Users]
DefaultPath=/usr/bin
MinimumUid=1000
MaximumUid=60000
RememberLastUser=true
[Autologin]
User=
Session=plasmawayland
"""
[[files]]
path = "/usr/share/wayland-sessions/plasmawayland.desktop"
data = """
[Desktop Entry]
Name=Plasma Wayland
Comment=KDE Plasma on Wayland
Exec=/usr/bin/kwin_wayland --drm /scheme/drm/card0
Type=Application
DesktopNames=KDE
"""
[[files]]
path = "/etc/pcid.d/ihdgd.toml"
data = """
+9 -7
View File
@@ -1,8 +1,10 @@
# Red Bear greeter/login service wiring
#
# This fragment is intended to be included by the active desktop/graphics target.
# DEPRECATED: This fragment is NO LONGER INCLUDED by any active config.
# All greeter/auth/session wiring is now inlined in redbear-full.toml.
# This file is retained for reference only. Do not include it in new configs.
# To add greeter services, edit redbear-full.toml directly.
[[files]]
# Original contents below (preserved for reference):
#[[files]]
path = "/etc/init.d/05_boot-essential.target"
data = """
[unit]
@@ -30,7 +32,7 @@ redbear-session-launch = {}
redbear-greeter = {}
[[files]]
path = "/etc/init.d/19_redbear-authd.service"
path = "/etc/init.d/11_redbear-authd.service"
data = """
[unit]
description = "Red Bear authentication daemon"
@@ -61,7 +63,7 @@ type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/20_greeter.service"
path = "/etc/init.d/12_greeter.service"
data = """
[unit]
description = "Red Bear greeter service (experimental Phase 3 user session bring-up)"
@@ -70,7 +72,7 @@ requires_weak = [
"12_dbus.service",
"13_redbear-sessiond.service",
"13_seatd.service",
"19_redbear-authd.service",
"11_redbear-authd.service",
]
[service]
+20 -17
View File
@@ -19,7 +19,7 @@ uid = 100
gid = 100
name = "messagebus"
home = "/nonexistent"
shell = "/usr/bin/zsh"
shell = "/usr/bin/false"
[packages]
# Red Bear OS branding and host utilities.
@@ -144,29 +144,32 @@ type = "oneshot_async"
[[files]]
path = "/etc/issue"
postinstall = true
data = """
########## Red Bear OS #########
# Login with the following: #
# `user` #
# `root`:`password` #
################################
____ _ ____ ___ ____
| _ \\ ___ __| | __ ) ___ __ _ _ __ / _ \\/ ___|
| |_) / _ \\ / _` | _ \\ / _ \\/ _` | '__| | | | \\___ \\
| _ < __/ (_| | |_) | __/ (_| | | | |_| |___) |
|_| \\_\\___|\\__,_|____/ \\___|\\__,_|_| \\___/|____/
v0.2.2 "Liliya"
Login as `user` (no password)
"""
[[files]]
path = "/etc/motd"
postinstall = true
data = """
_ _
| | (_)
| | ___ _ ___ _ __ _ _ ___
| |/ / || |/ _ \\ | '_ \\| | | / __|
| < | || | (_) || |_) | |_| \\__ \\
|_|\\_\\|_|/ |\\___/ | .__/ \\__,_|___/
|__/ | |
|_|
____ _ ____ ___ ____
| _ \\ ___ __| | __ ) ___ __ _ _ __ / _ \\/ ___|
| |_) / _ \\ / _` | _ \\ / _ \\/ _` | '__| | | | \\___ \\
| _ < __/ (_| | |_) | __/ (_| | | | |_| |___) |
|_| \\_\\___|\\__,_|____/ \\___|\\__,_|_| \\___/|____/
v0.2.2 "Liliya" · Built on Redox OS
Red Bear OS v0.2.0 "Liliya" — Built on Redox OS
Type 'help' for available commands.
"""
[[files]]
+1 -1
View File
@@ -1,6 +1,6 @@
# Red Bear OS shared network profile wiring
#
# Shared by redbear-minimal, redbear-desktop, redbear-full, and redbear-kde.
# Shared by redbear-mini, redbear-full, and other network-enabled configs.
[[files]]
path = "/etc/netctl"
+1 -1
View File
@@ -6,7 +6,7 @@
# to the bounded Wi-Fi path and adds the first Intel driver-side package on top of the shared
# firmware/control/profile tooling.
include = ["redbear-minimal.toml"]
include = ["redbear-mini.toml"]
[general]
filesystem_size = 2048
+1 -1
View File
@@ -239,7 +239,7 @@ fi
export XCURSOR_THEME="${XCURSOR_THEME:-Pop}"
export XKB_CONFIG_ROOT="${XKB_CONFIG_ROOT:-/usr/share/X11/xkb}"
if [ -z "${KWIN_DRM_DEVICES:-}" ] && [ -e /scheme/drm/card0 ]; then
if [ -z "${KWIN_DRM_DEVICES:-}" ] && ( exec 3<"/scheme/drm/card0" && exec 3>&- ) >/dev/null 2>&1; then
export KWIN_DRM_DEVICES=/scheme/drm/card0
fi
+1 -3
View File
@@ -21,8 +21,7 @@ current/canonical versus historical/reference split obvious.
> **Red Bear note:** newer subsystem plans can also live under `local/docs/` when they are Red Bear-
> specific rather than general Redox architecture material. In particular, see
> `local/docs/WIFI-IMPLEMENTATION-PLAN.md` for the current Wi-Fi direction,
> `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` for the canonical desktop path,
> and `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` for the canonical desktop path.
> `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` for the canonical desktop path.
> **Repository model:** RedBearOS relates to Redox in the same way Ubuntu relates to Debian.
> Upstream Redox remains the base platform; Red Bear carries packaging, patch, validation, and
@@ -101,7 +100,6 @@ This summary is only a quick orientation layer. For canonical current-state deta
- `docs/07-RED-BEAR-OS-IMPLEMENTATION-PLAN.md` for repository-wide execution order,
- `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` for the canonical comprehensive plan,
- `local/docs/PROFILE-MATRIX.md` for support-language by tracked profile,
- `local/docs/PROFILE-MATRIX.md` for support-language by tracked profile,
- and the active subsystem plans under `local/docs/` for detailed current workstreams.
- **Compile targets**: the supported compile targets are `redbear-mini`, `redbear-full`, and `redbear-grub`
+3 -9
View File
@@ -534,15 +534,10 @@ When mainline updates affect our work:
- `local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md` is the current DRM-focused execution plan beneath
the canonical desktop path. It keeps Intel and AMD at the same evidence bar while separating
display/KMS maturity from render/3D maturity.
- Older GPU-specific docs such as `local/docs/AMD-FIRST-INTEGRATION.md`,
`local/docs/HARDWARE-3D-ASSESSMENT.md`, and `local/docs/DMA-BUF-IMPROVEMENT-PLAN.md` remain
useful reference material, but they are not the planning authority when sequencing or acceptance
criteria differ.
- Older GPU-specific docs (`AMD-FIRST-INTEGRATION.md`, `HARDWARE-3D-ASSESSMENT.md`, `DMA-BUF-IMPROVEMENT-PLAN.md`) have been retired and removed from the tree. Their content is subsumed by `CONSOLE-TO-KDE-DESKTOP-PLAN.md` and `DRM-MODERNIZATION-EXECUTION-PLAN.md`.
- `DESKTOP-STACK-CURRENT-STATUS.md` has been retired — its content merged into `CONSOLE-TO-KDE-DESKTOP-PLAN.md`.
- `local/docs/AMD-FIRST-INTEGRATION.md` remains the deeper AMD-specific technical roadmap, but AMD
and Intel machines are now equal-priority Red Bear OS targets.
- The earlier Phase 03 reassessment bridge has been retired. Its reconciliation role is now
covered by `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md`,
`local/docs/DESKTOP-STACK-CURRENT-STATUS.md`, and `docs/07-RED-BEAR-OS-IMPLEMENTATION-PLAN.md`.
- `local/docs/WIFI-IMPLEMENTATION-PLAN.md` is the current Wi-Fi architecture and rollout plan,
including the bounded role of `linux-kpi` and the native wireless control-plane direction.
- `local/docs/USB-IMPLEMENTATION-PLAN.md` and `local/docs/BLUETOOTH-IMPLEMENTATION-PLAN.md` should
@@ -551,8 +546,7 @@ When mainline updates affect our work:
IRQ delivery, MSI/MSI-X quality, IOMMU validation, and other low-level controller completeness work.
- `local/docs/QUIRKS-SYSTEM.md` documents the hardware quirks infrastructure: compiled-in tables,
TOML runtime files, DMI matching, driver integration, and the linux-kpi C FFI bridge.
- `local/docs/QUIRKS-IMPROVEMENT-PLAN.md` is the current follow-up plan for removing quirks drift,
integrating quirks into real drivers, and converging on one source of truth.
- `local/docs/QUIRKS-IMPROVEMENT-PLAN.md` has been retired — quirks convergence is tracked in `QUIRKS-SYSTEM.md` and the canonical desktop path plan.
- `local/docs/DBUS-INTEGRATION-PLAN.md` is the canonical D-Bus architecture and implementation plan for KDE Plasma 6 on Wayland. It defines the phased approach to D-Bus service integration, the `redbear-sessiond` login1-compatible session broker, and the gap analysis for desktop-facing D-Bus services.
- `local/docs/GREETER-LOGIN-IMPLEMENTATION-PLAN.md` is the canonical Red Bear-native greeter/login design and current implementation plan for the `redbear-full` desktop path. It defines the `redbear-authd` / `redbear-session-launch` / `redbear-greeter` split, service wiring, validation surface, and the current boundary between the active greeter path and the older `redbear-validation-session` helper flows.
@@ -0,0 +1,363 @@
# Driver Discovery and Dynamic Hardware Mapping Plan
**Status**: Draft — implementation pending
**Date**: 2026-05-27
**Supersedes**: Ad-hoc pcid-spawner + hardcoded lived disk paths
**Author**: Red Bear OS team
---
## 1. Problem Statement
Red Bear OS has two critical gaps in hardware discovery:
1. **lived's disk fallback is broken**: The live ISO boot daemon (`lived`) tries hardcoded paths `/scheme/disk/0` and `/scheme/usbscsi/0` to find the physical boot disk. But no disk driver registers those exact scheme names — they register `disk.pci-00-1F-2_ahci`, `disk.usb-xhci+1-scsi`, etc. The fallback **never works**.
2. **No dynamic hardware mapping**: The system does not distinguish between "hardware present" and "driver needed." On bare metal with no virtio devices, the system should not try to load `virtio-blkd`. On QEMU with no real AHCI controller, the system should not try to load `ahcid`. Today, the driver-manager loads whatever matches its static config files regardless of whether the hardware exists.
Linux solves both problems with a two-stage model:
- **Stage 1 (initramfs)**: Enumerate PCI bus, load ONLY the storage driver matching the boot controller, mount rootfs.
- **Stage 2 (rootfs)**: Full enumeration, udev + modprobe dynamically load all remaining drivers based on actual hardware.
---
## 2. Current Architecture
### 2.1 Boot Sequence (Initfs Phase)
```
Bootstrap (PID 1) → init → services start in dependency order:
00_runtime.target randd, nulld, zerod, rtcd, logd
10_inputd.service VT input multiplexer
10_lived.service Live disk daemon (RAM preload + disk fallback)
20_graphics.target vesad (FB handoff), fbcond, fbbootlogd
41_acpid.service ACPI interpreter → scheme:acpi
40_hwd.service Hardware manager → spawns pcid internally
pcid → enumerates PCI bus → registers scheme:pci
00_driver-manager-initfs.service (if P26 applied)
Loads /scheme/initfs/lib/drivers.d/00-storage.toml
Only: ahcid, ided, nvmed, virtio-blkd
40_drivers.target All initfs drivers
50_rootfs.service Mount rootfs (hard dep on drivers.target)
90_initfs.target Trigger switchroot
```
### 2.2 Driver Registration Contract
All disk drivers using `driver_block::DiskScheme` register schemes starting with `"disk"`:
| Driver | Scheme Name Pattern | Match Criteria |
|--------|---------------------|----------------|
| ided | `disk.pci-XX-XX-X_ide` | PCI class 0x01, subclass 0x01 |
| ahcid | `disk.pci-XX-XX-X_ahci` | PCI class 0x01, subclass 0x06 |
| nvmed | `disk.pci-XX-XX-X-nvme` | PCI class 0x01, subclass 0x08 |
| virtio-blkd | `disk.pci-XX-XX-X_virtio_blk` | PCI vendor 0x1AF4, device 0x1001 |
| usbscsid | `disk.usb-xhci+PORT-scsi` | USB SCSI transport |
| lived | `disk.live` | RAM-backed (our daemon) |
The `DiskScheme::new()` assertion (`assert!(scheme_name.starts_with("disk"))`) is the **contract** that enables dynamic discovery: any consumer can find all disk schemes by listing `/scheme/` and filtering for the `"disk"` prefix.
### 2.3 The Two Driver-Loading Paths
| Path | Mechanism | Config Source | Drivers |
|------|-----------|---------------|---------|
| **Initfs** | `driver-manager --initfs` | `/scheme/initfs/lib/drivers.d/00-storage.toml` | Storage only (4 drivers) |
| **Rootfs** | `driver-manager --hotplug` | `/lib/drivers.d/*.toml` | All categories (40+ drivers) |
### 2.4 How Linux Does It (Reference)
Linux uses a two-tier ordering:
**Tier 1 — Initcall levels** (include/linux/init.h):
```
Level 0: pure_initcall (architecture setup)
Level 2: postcore_initcall (PCI subsystem registers here)
Level 4: subsys_initcall (SCSI, networking subsystems)
Level 6: device_initcall (module_init → all built-in drivers)
Level 7: late_initcall (late-stage platform drivers)
```
**Tier 2 — Link order** within device_initcall (drivers/Makefile):
```
Line 49: obj-y += virtio/ # VirtIO before block
Line 76: obj-y += block/ # Block devices (storage)
Line 84: obj-y += nvme/ # NVMe
Line 85: obj-y += ata/ # ATA/AHCI
Line 92: obj-y += net/ # Network
Line 68: obj-y += gpu/ # GPU comes AFTER storage
```
**The critical principle**: Storage must load before GPU not because of PCI ordering, but because GPU drivers need firmware blobs from `/lib/firmware/` — which requires a mounted filesystem. Storage drivers are needed to mount that filesystem.
**Dynamic loading** (after rootfs mount): `MODULE_DEVICE_TABLE` entries in every driver generate `modules.alias` patterns. udev receives kernel uevents with `MODALIAS=pci:v00001AF4d00001001...`, calls `modprobe`, which looks up the alias and loads the matching `.ko` module.
---
## 3. Design: Two-Stage Dynamic Hardware Discovery
### 3.1 Stage 1 — Initfs Boot (Storage-Only)
**Goal**: Load exactly the storage driver(s) needed to mount the root filesystem. No more, no less.
**Mechanism**: driver-manager `--initfs` already exists and does PCI class/vendor matching. The missing piece is that the P26 patch (which creates `00_driver-manager-initfs.service` and `initfs-storage.toml`) is wired in `recipe.toml` but needs to be applied.
**Initfs driver config** (`initfs-storage.toml`):
```toml
# Only storage drivers — needed to mount rootfs
# GPU/display deliberately excluded (handled by rootfs DRM/KMS stack)
[[driver]]
name = "nvmed"
description = "NVMe storage driver"
priority = 100
command = ["/scheme/initfs/lib/drivers/nvmed"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 8
[[driver]]
name = "ahcid"
description = "AHCI SATA driver"
priority = 100
command = ["/scheme/initfs/lib/drivers/ahcid"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 6
[[driver]]
name = "ided"
description = "PATA IDE driver"
priority = 100
command = ["/scheme/initfs/lib/drivers/ided"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 1
[[driver]]
name = "virtio-blkd"
description = "VirtIO block device driver"
priority = 100
command = ["/scheme/initfs/lib/drivers/virtio-blkd"]
[[driver.match]]
bus = "pci"
vendor = 0x1AF4
device = 0x1001
```
**How this is already dynamic**: The driver-manager only spawns a driver when the PCI bus actually reports a matching device. If QEMU has no AHCI controller, `ahcid` is never spawned. If bare metal has no VirtIO devices, `virtio-blkd` is never spawned. The TOML match table is a **candidate list**, not a **must-load list**.
**What's needed**: Ensure P26 is applied, ensure `virtio-blkd` is in the BINS list, and ensure the initfs binary staging includes all 4 storage drivers.
### 3.2 Stage 2 — Rootfs (Full Hardware Discovery)
**Goal**: After rootfs is mounted, dynamically discover and load ALL remaining drivers based on actual hardware.
**Mechanism**: `driver-manager --hotplug` already reads `/lib/drivers.d/*.toml` (8 config files, 40+ drivers), enumerates PCI + ACPI buses, and spawns matching drivers. It also runs a hotplug loop for device add/remove.
**The existing driver configs are already data-driven and dynamic**:
| Config File | Category | Priority | Matching |
|-------------|----------|----------|----------|
| `00-storage.toml` | Storage | 100 | PCI class-based |
| `10-network.toml` | Network | 50 | PCI vendor + class |
| `20-usb.toml` | USB | 80 | PCI class + prog_if |
| `30-graphics.toml` | GPU/Display | 60 | PCI class 0x03 |
| `40-input.toml` | Input | 40 | Sentinel (vendor=0xFFFF) |
| `50-audio.toml` | Audio | 40 | PCI vendor + class |
| `60-gpio-i2c.toml` | GPIO/I2C | 30 | ACPI bus matching |
| `70-usb-class.toml` | USB class | 20 | Sentinel (vendor=0xFFFF) |
**Key property**: Priority ordering ensures storage (100) > USB (80) > GPU (60) > network (50) > audio (40). This mirrors Linux's link-order principle.
### 3.3 lived Disk Fallback Fix
**Current bug**: `lived` tries `/scheme/disk/0` — but real schemes are named `disk.pci-00-1F-2_ahci`, never just `disk`.
**Fix**: Replace hardcoded paths with RedoxFS-style dynamic scheme discovery (same pattern as `filesystem_by_uuid` in `redoxfs/src/bin/mount.rs`):
```rust
fn try_open_disk(&self) -> Result<File, String> {
for attempt in 0..DISK_OPEN_MAX_RETRIES {
// List /scheme/ to find all registered disk schemes
if let Ok(entries) = std::fs::read_dir("/scheme") {
for entry in entries.flatten() {
let name = entry.file_name();
let name_str = name.to_string_lossy();
// All disk schemes start with "disk." (driver-block contract)
// Skip our own "disk.live" scheme
if name_str.starts_with("disk.") && name_str != "disk.live" {
// Try opening disk 0 on this scheme
let path = format!("/scheme/{}/0", name_str);
if let Ok(file) = File::open(&path) {
eprintln!("lived: opened physical disk at {} (attempt {})",
path, attempt + 1);
return Ok(file);
}
}
}
}
if attempt < DISK_OPEN_MAX_RETRIES - 1 {
std::thread::sleep(std::time::Duration::from_millis(
DISK_OPEN_RETRY_INTERVAL_MS
));
}
}
Err(format!("no disk scheme found after {} retries", DISK_OPEN_MAX_RETRIES))
}
```
**This is the exact pattern RedoxFS uses** in `filesystem_by_uuid()`. It:
1. Lists `/scheme/` (all registered schemes)
2. Filters to names starting with `"disk."` (the `driver-block` contract)
3. Skips `disk.live` (our own RAM-backed scheme)
4. Tries opening disk 0 on each discovered scheme
**Boot timing**: lived starts at service 10, before disk drivers. The retry loop (60 × 500ms = 30s) gives driver-manager and storage drivers time to load and register their schemes. As soon as ANY storage driver registers `disk.*`, lived finds it.
---
## 4. What Needs to Change
### 4.1 Patches Required
| Component | Patch | What It Does |
|-----------|-------|--------------|
| **base** | P60 (new) | Add `virtio-blkd` to BINS + staged files; update lived's `try_open_disk()` with dynamic scheme discovery |
| **kernel** | P26 (existing) | DebugDisplay scrolling fix (already done) |
| **base** | P26-driver-manager-initfs-conversion.patch (existing, wired but needs application verification) | Replaces pcid-spawner with driver-manager in initfs |
### 4.2 Changes to `recipes/core/base/recipe.toml`
1. **Add `virtio-blkd` to BINS** (already done in working tree)
2. **Add `virtio-blkd` to staged files list** (already done in working tree)
3. **No changes to driver configs**`initfs-storage.toml` already lists all 4 storage drivers
### 4.3 Changes to `recipes/core/base/source/drivers/storage/lived/src/main.rs`
Replace the hardcoded `candidates` array in `try_open_disk()` with `/scheme/` directory enumeration that discovers disk schemes dynamically.
### 4.4 No Changes Needed
- **driver-manager** — already does dynamic PCI matching
- **initfs-storage.toml** — already has the right 4 storage drivers
- **Driver configs** (`/lib/drivers.d/*.toml`) — already data-driven with vendor/class matching
- **pcid** — already enumerates PCI bus correctly
- **Boot service order** — already correct (lived at 10, driver-manager-initfs at 00, rootfs at 50)
---
## 5. Verification Plan
### 5.1 QEMU with IDE (default)
```bash
timeout 60 qemu-system-x86_64 \
-drive file=build/x86_64/redbear-full.iso,format=raw \
-m 4G -smp 4 -serial stdio -no-reboot
```
Expected: lived finds `disk.pci-00-01-1_ide` scheme from `ided`, mounts rootfs.
### 5.2 QEMU with virtio-blk
```bash
timeout 60 qemu-system-x86_64 \
-device virtio-blk-pci,drive=drive0 \
-drive id=drive0,file=build/x86_64/redbear-full.iso,format=raw,if=none \
-m 4G -smp 4 -serial stdio -no-reboot
```
Expected: lived finds `disk.pci-00-XX-X_virtio_blk` scheme from `virtio-blkd`, mounts rootfs.
### 5.3 Bare Metal USB Boot
Expected: lived finds `disk.usb-xhci+PORT-scsi` scheme from `usbscsid`, mounts rootfs.
### 5.4 No Unnecessary Drivers
On QEMU with only virtio-blk (no AHCI), `ahcid` should NOT be spawned. Verify via boot log:
```
driver-manager: no driver found for pci 0000:00:01.1 # IDE controller — no match
driver-manager: bound: 0000:00:04.0 -> virtio-blkd # VirtIO block — matched
```
---
## 6. PCI Class Code Reference
From Linux `include/linux/pci_ids.h` and our driver configs:
| Class | Subclass | Prog IF | Device Type | Red Bear Driver |
|-------|----------|---------|-------------|-----------------|
| 0x01 | 0x01 | — | IDE/PATA | `ided` |
| 0x01 | 0x06 | 0x01 | AHCI SATA | `ahcid` |
| 0x01 | 0x08 | 0x02 | NVMe | `nvmed` |
| 0x01 | 0x00 | — | VirtIO Block (vendor 0x1AF4, device 0x1001) | `virtio-blkd` |
| 0x02 | — | — | Ethernet | `e1000d`, `rtl8168d`, etc. |
| 0x03 | — | — | Display/GPU | `redox-drm` |
| 0x04 | 0x03 | — | Audio (HDA) | `ihdad` |
| 0x0C | 0x03 | 0x30 | xHCI USB | `xhcid` |
| 0x0C | 0x03 | 0x00 | UHCI USB | `uhcid` |
| 0x0C | 0x03 | 0x10 | OHCI USB | `ohcid` |
| 0x0C | 0x03 | 0x20 | EHCI USB | `ehcid` |
---
## 7. Boot Timeline (Target State)
```
T+0ms Bootstrap starts, creates initfs/procmgr/namespace schemes
T+50ms init starts, launches 00_randd → 00_logd → 00_runtime.target
T+200ms lived starts (service 10), loads 128 MiB preload
T+300ms vesad starts (FB handoff for text console)
T+400ms acpid starts → ACPI interpreter → scheme:acpi
T+500ms hwd starts → spawns pcid → PCI bus scan → scheme:pci
driver-manager --initfs starts:
Loads 00-storage.toml (4 storage drivers)
Enumerates PCI bus via /scheme/pci/
QEMU: finds 8086:7010 (IDE) → spawns ided
finds 1234:1111 (virtio-gpu) → no storage match, skipped
finds 1AF4:1050 (virtio-net) → no storage match, skipped
T+1500ms ided registers disk.pci-00-01-1_ide
lived discovers disk.pci-00-01-1_ide via /scheme/ enumeration
lived disk fallback succeeds
T+2000ms redoxfs mounts rootfs from lived
T+2500ms switchroot → rootfs init starts
T+3000ms driver-manager --hotplug starts (rootfs):
Loads all /lib/drivers.d/*.toml configs
Detects ided already bound → skips
Finds 1234:1111 (display class 0x03) → spawns redox-drm
Finds 8086:100E (network class 0x02) → spawns e1000d
Finds 1AF4:1050 (virtio-net) → spawns virtio-netd
T+5000ms All drivers bound, system fully operational
```
---
## 8. Principles
1. **Data-driven, not hardcoded**: Driver matching via TOML configs with vendor/device/class fields. No binary name hardcoding, no path guessing.
2. **Enumerate first, match second**: PCI bus scan produces ALL devices. Driver matching filters to supported ones. Unknown hardware is logged but doesn't block boot.
3. **Priority ordering**: Storage (100) before USB (80) before GPU (60) before network (50) before audio (40). Mirrors Linux's link-order principle.
4. **Stage 1 = minimum viable set**: Initfs loads ONLY storage drivers. Everything else waits for rootfs.
5. **Dynamic scheme discovery**: lived discovers disk schemes by reading `/scheme/` and filtering for the `"disk."` prefix — the same contract that `driver-block` enforces.
6. **No unnecessary drivers**: If hardware doesn't exist, the driver is never spawned. `driver-manager` only calls `probe()` for devices that actually exist on the PCI/ACPI bus.
7. **Deferred retry for timing**: Drivers that start before their dependencies are ready get retried (3 times in initfs, 5 times in hotplug). After max retries, the device is permanently skipped with a logged reason.
File diff suppressed because it is too large Load Diff
+483
View File
@@ -0,0 +1,483 @@
# Live ISO Mount — Architecture, Failure Analysis, and Fix Plan
**Date:** 2026-05-27
**Status:** Draft — fixes not yet implemented
**Scope:** Bootloader live preload, lived daemon, RedoxFS mount chain
---
## 1. Current Architecture
### 1.1 Boot Flow (Live ISO)
```
UEFI firmware
→ Bootloader (recipes/core/bootloader/source/src/main.rs)
1. Find RedoxFS partition on disk
2. Read filesystem header → get total filesystem size (e.g., 4093 MiB)
3. Live preload: read first N MiB of filesystem into RAM
- Cap: max_preload = 1024 MiB (line 559)
- Set env: DISK_LIVE_ADDR=<phys addr>, DISK_LIVE_SIZE=<preload size>
- Set env: REDOXFS_BLOCK=0 (start of partition)
4. Load kernel from RedoxFS into memory
5. Load initfs from RedoxFS into memory
6. Set up paging, pass env to kernel
7. Jump to kernel entry point
Kernel
→ bootstrap (initfs)
→ init daemon
→ lived daemon (10_lived.service)
- Reads DISK_LIVE_ADDR + DISK_LIVE_SIZE from env
- Maps preloaded RAM as LiveDisk via /scheme/memory/physical
- Registers scheme:disk.live
- LiveDisk.size() = preloaded size (1024 MiB)
- LiveDisk.block_size() = PAGE_SIZE (4096) [P6 patch changes to 512]
→ redoxfs daemon (50_rootfs.service)
- Opens /scheme/disk.live/0 as DiskFile
- Calls FileSystem::open(disk, password, block=0, cleanup=true)
- Reads header at block 0 (inside preloaded region → works)
- Calls fs.reset_allocator() → walks the allocation tree
- Calls fs.cleanup() → may read blocks across the entire filesystem
- FAILURE: any read beyond preloaded size returns EINVAL
```
### 1.2 Component Map
| Component | Source | Role |
|-----------|--------|------|
| **Bootloader** | `recipes/core/bootloader/source/src/main.rs` | Preloads filesystem into RAM, passes env vars |
| **lived** | `recipes/core/base/source/drivers/storage/lived/src/main.rs` | Maps preloaded RAM as `scheme:disk.live` |
| **RedoxFS mount** | `recipes/core/redoxfs/source/src/bin/mount.rs` | Opens disk scheme, calls FileSystem::open |
| **RedoxFS lib** | `recipes/core/redoxfs/source/src/filesystem.rs` | Reads header, walks allocator tree |
| **driver-block** | `recipes/core/base/source/drivers/storage/driver-block/src/lib.rs` | DiskWrapper with block_size alignment checks |
| **P6 patch** | `local/patches/base/P6-lived-block-size-512.patch` | Changes block_size from PAGE_SIZE to 512 |
### 1.3 The Preload Cap
```rust
// bootloader/src/main.rs:559
let max_preload: u64 = 1024 * MIBI as u64; // 1 GiB hard cap
let preload_size = if size > max_preload {
max_preload // Cap at 1 GiB
} else {
size // Preload entire filesystem if ≤ 1 GiB
};
```
For redbear-full (4093 MiB filesystem): preloads 1024 MiB, 3069 MiB must come from disk.
For redbear-mini (1533 MiB filesystem): preloads 1024 MiB, 509 MiB must come from disk.
### 1.4 The lived Disk
```rust
// lived/src/main.rs - LiveDisk::read (CURRENT, unpatched source)
fn block_size(&self) -> u32 {
PAGE_SIZE as u32 // P6 changes this to 512
}
fn size(&self) -> u64 {
self.original.len() as u64 // This is the PRELOADED size, not total filesystem size
}
async fn read(&mut self, mut block: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
let mut offset = (block as usize) * PAGE_SIZE;
if offset + buffer.len() > self.original.len() {
return Err(syscall::Error::new(EINVAL)); // ← THIS IS THE FAILURE POINT
}
// ... read from preloaded buffer
}
```
**The fundamental problem:** `lived` only has the preloaded buffer (1024 MiB). It has no
access to the remaining filesystem data on the physical disk. When RedoxFS tries to read
beyond 1024 MiB, lived returns EINVAL.
---
## 2. Failure Analysis
### 2.1 Why Does the Mini ISO Work?
The mini ISO (1533 MiB) also has 509 MiB beyond the preload. However:
1. RedoxFS `FileSystem::open` reads the header at block 0 (within preload) → OK
2. `reset_allocator` walks the free block tree. For a 1533 MiB filesystem with minimal
contents, the allocator metadata is concentrated near the start → likely within 1024 MiB
3. `cleanup` reads extent nodes — for a small filesystem, these are also near the start
For the full ISO (4093 MiB) with hundreds of packages:
- The allocator tree and extent nodes span the entire 4093 MiB range
- RedoxFS needs to read blocks at offsets > 1024 MiB during `FileSystem::open`
- lived rejects those reads → mount fails
**The mini ISO works by luck** — its metadata happens to fit within the preload window.
This is not a reliable design.
### 2.2 The Exact Error Chain
```
RedoxFS FileSystem::open
→ disk.read_at(block_N, &mut header)
→ DiskFile::read_at(buffer, block_N * BLOCK_SIZE)
→ syscall::read(scheme:disk.live/0, offset=block_N * 512)
→ lived::LiveDisk::read(block_N, buffer)
→ offset = block_N * PAGE_SIZE // or 512 with P6
→ if offset + buffer.len() > self.original.len():
return Err(EINVAL) // ← HERE
```
The error propagates:
- lived → EINVAL
- DiskFile → "RedoxFS: IO ERROR: Invalid argument (os error 22)"
- FileSystem::open → Err(EINVAL)
- mount.rs → "not able to mount uuid ..."
### 2.3 The P6 Block Size Patch
The P6 patch (`local/patches/base/P6-lived-block-size-512.patch`) fixes a different but
related issue: the original `block_size()` returned `PAGE_SIZE` (4096), but RedoxFS reads
in 512-byte chunks (`BLOCK_SIZE = 4096` but individual reads may be 512). The `DiskWrapper`
in `driver-block` rejects misaligned reads. Changing to 512 fixes alignment but does NOT
fix the size/out-of-bounds problem.
**Note:** The current source tree (`recipes/core/base/source/drivers/storage/lived/src/main.rs`)
does NOT have the P6 patch applied — it still shows `PAGE_SIZE as u32`. The P6 patch is
applied during `repo fetch base` and only exists durably in `local/patches/base/`.
---
## 3. Fix Strategy
### 3.1 Design Principle
> Preload the minimum needed to boot the kernel + initfs. Once the OS is running, mount
> the filesystem from the actual disk device, not from the RAM preload.
The bootloader already loads kernel + initfs from RedoxFS before switching to live mode.
After that, the running OS has access to the AHCI driver (ahcid) and can mount the
filesystem directly from the physical disk.
### 3.2 Two-Phase Approach
**Phase A: Bootloader Changes** (bootloader is UEFI code, runs before the OS)
1. **Reduce preload to the minimum needed for kernel + initfs discovery**
- The bootloader needs to read the RedoxFS superblock + directory tree to find
`usr/lib/boot/kernel` and `usr/lib/boot/initfs`. This requires reading the header,
the root node, and walking directory entries.
- Instead of preloading a fixed 1024 MiB, preload only what's needed to locate and
read these two files. In practice, this is the first few MiB of the filesystem.
- Fallback: if the filesystem is small enough (≤ 64 MiB?), preload everything.
2. **Pass the physical disk location to the kernel**
- Set `DISK_PHYS_ADDR` and `DISK_PHYS_SIZE` env vars with the full disk geometry
- Keep `DISK_LIVE_ADDR` / `DISK_LIVE_SIZE` for the minimal preload
- Add `REDOXFS_FULL_SIZE` so the OS knows the true filesystem extent
**Phase B: lived Daemon Changes** (OS-level, patchable via `local/patches/base/`)
1. **Accept the full filesystem size as an additional env var**
- Read `REDOXFS_FULL_SIZE` or derive from the RedoxFS header
- Report `LiveDisk::size()` as the FULL filesystem size, not just the preload
2. **Fall through to the physical disk for reads beyond the preload**
- When `read(block, buffer)` is called with an offset beyond `self.original.len()`:
- Open the underlying block device (e.g., `/scheme/disk/0` after ahcid starts)
- Read the data from the physical disk
- Cache the result in the overlay HashMap
- This makes lived act as a write-through cache: preload in RAM, fallback to disk
3. **Alternative simpler approach: bypass lived entirely for large images**
- After ahcid starts and registers `/scheme/disk/0`, the init system could mount
RedoxFS directly from `/scheme/disk/0` instead of `/scheme/disk.live/0`
- The preload would only be used by the bootloader to load kernel + initfs
- Once the OS boots, lived is unnecessary — mount from the real disk
---
## 4. Concrete Fix Plan
### 4.1 Fix 1: Reduce Bootloader Preload (bootloader patch)
**File:** `recipes/core/bootloader/source/src/main.rs`
**Current:**
```rust
let max_preload: u64 = 1024 * MIBI as u64;
```
**Proposed change:**
```rust
// Only preload what the bootloader actually needs:
// - RedoxFS header + allocator (first ~1 MiB)
// - Root directory tree (typically first 32-64 MiB)
// - kernel and initfs files (loaded separately after preload)
// 64 MiB is generous for the metadata region of any reasonable filesystem.
// The kernel and initfs are loaded separately via fs.disk.read_at() directly
// from the physical disk, so they don't need to be in the preload.
let max_preload: u64 = 64 * MIBI as u64;
```
Wait — this doesn't work. The bootloader reads kernel and initfs from the RedoxFS
filesystem using `load_to_memory(os, &mut fs, "usr/lib/boot/kernel", ...)`. After the
preload, the bootloader has already switched the disk to the live buffer. So the kernel
and initfs must be within the preload, OR the bootloader must load them before switching
to live mode.
**Looking at the actual bootloader flow:**
```
1. Open RedoxFS from physical disk → fs
2. Preload first N MiB into RAM buffer
3. Set LIVE_OPT = Some((fs.block, buffer))
4. Load kernel from fs (still using physical disk? or from buffer?)
5. Load initfs from fs
6. Pass LIVE_OPT to kernel env
```
The live buffer is set in `LIVE_OPT` at line 625, but the kernel and initfs are loaded
at lines 642-663, AFTER the live preload. The `load_to_memory` function uses `fs` which
still uses the original disk handle. So the kernel and initfs are read from the physical
disk, not from the live buffer.
**This means the preload doesn't need to include kernel or initfs at all.** The preload
exists solely so that `lived` can serve the filesystem to the running OS via `scheme:disk.live`.
**Revised Fix 1:** Reduce max_preload to a small value (e.g., 4-64 MiB) that covers just
the RedoxFS metadata needed for initial mount, then rely on the disk fallback for the rest.
BUT: this only works if `lived` can fall through to the physical disk for out-of-bounds
reads. Without the fallback, reducing preload makes the problem worse.
### 4.2 Fix 2: lived Disk Fallback (base patch)
**File:** `recipes/core/base/source/drivers/storage/lived/src/main.rs`
This is the core fix. Make `lived` aware of the full filesystem and able to read from
the physical disk when the preload doesn't cover the requested region.
**Design:**
```rust
struct LiveDisk {
// Preloaded RAM buffer (may be smaller than total filesystem)
preload: &'static [u8],
// Full filesystem size (from RedoxFS header or env var)
total_size: u64,
// Physical disk offset where the filesystem starts
disk_block: u64,
// Handle to the physical disk (opened after ahcid starts)
disk_handle: Option<File>,
// Write overlay (same as before)
overlay: HashMap<u64, Box<[u8]>>,
}
impl Disk for LiveDisk {
fn block_size(&self) -> u32 { 512 }
fn size(&self) -> u64 { self.total_size }
async fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
let bs = self.block_size() as usize;
let offset = (block as usize) * bs;
if offset + buffer.len() > self.total_size as usize {
return Err(syscall::Error::new(EINVAL));
}
let preload_bytes = self.preload.len();
for (i, chunk) in buffer.chunks_mut(bs).enumerate() {
let block_i = block + i as u64;
let offset_i = offset + i * bs;
// Check overlay first
if let Some(overlay) = self.overlay.get(&block_i) {
chunk.copy_from_slice(&overlay[..chunk.len()]);
continue;
}
if offset_i + chunk.len() <= preload_bytes {
// Within preload → read from RAM
chunk.copy_from_slice(&self.preload[offset_i..offset_i + chunk.len()]);
} else {
// Beyond preload → read from physical disk
self.read_from_disk(block_i, chunk)?;
}
}
Ok(buffer.len())
}
fn read_from_disk(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<()> {
// Try to open the physical disk if not already open
if self.disk_handle.is_none() {
// Try common disk scheme paths
for path in &["/scheme/disk/0", "/scheme/disk/1"] {
if let Ok(file) = OpenOptions::new().read(true).open(path) {
self.disk_handle = Some(file);
break;
}
}
}
if let Some(ref mut disk) = self.disk_handle {
// Seek to the correct block (accounting for partition offset)
let abs_block = self.disk_block + block;
disk.read_at(buffer, abs_block * self.block_size() as u64)
.map_err(|_| syscall::Error::new(EIO))?;
Ok(())
} else {
// No disk available yet — return what we have from preload
// (fill with zeros for regions not in preload)
buffer.fill(0);
Err(syscall::Error::new(EIO))
}
}
}
```
**Problem with this approach:** `lived` starts before `ahcid` (it's at priority 10 in
init.initfs.d, while ahcid is at priority 40). So when lived first starts, there IS no
`/scheme/disk/0` to fall back to. The disk fallback would only work after ahcid initializes.
### 4.3 Fix 3: Two-Stage Mount (Recommended)
The cleanest fix is to change the init sequence:
**Stage 1: lived serves the preloaded buffer (for early boot)**
- lived starts as before, serves the preload via `scheme:disk.live`
- RedoxFS does NOT mount from `disk.live` for the root filesystem
- The initfs has everything needed for early boot (lived, ahcid, basic tools)
**Stage 2: Mount from physical disk (after drivers start)**
- After `40_drivers.target` completes, ahcid has registered `/scheme/disk/0`
- Init runs `redoxfs --uuid $REDOXFS_UUID file $REDOXFS_BLOCK` pointing to `/scheme/disk/0`
- This reads the full filesystem from the physical disk
**Current init flow:**
```
10_lived.service → starts lived (scheme:disk.live)
40_drivers.target → starts ahcid (scheme:disk/0)
50_rootfs.service → redoxfs mounts from... whichever disk scheme it finds first
(scans all /scheme/disk/* for matching UUID)
```
**The issue is timing:** `50_rootfs.service` requires `40_drivers.target`, so it should
wait for ahcid. But `redoxfs` scans ALL disk schemes, and `disk.live` matches first
(since lived starts earlier). RedoxFS finds the UUID in `disk.live` and tries to mount
from it, but the disk is too small.
**Proposed fix:**
1. **Make lived report the full filesystem size** by reading the RedoxFS header from
the preload buffer to determine `total_size`. Report that as `size()`.
2. **Make lived fall through to disk reads** for out-of-bounds regions. Use a lazy-open
approach: when a read goes beyond the preload and no disk handle is open yet, try
to open `/scheme/disk/0`. If it fails, return EIO (which RedoxFS will retry).
3. **Reduce the preload** in the bootloader. Since lived now handles disk fallback,
we can preload much less (e.g., 4-64 MiB). The preload just needs to cover the
RedoxFS header and enough metadata for the initial mount.
---
## 5. Recommended Implementation Order
### Step 1: Fix lived to report full size + disk fallback (base patch)
Create `local/patches/base/P59-lived-disk-fallback.patch`:
1. Add `total_size: u64` field to LiveDisk
2. Parse RedoxFS header from preload buffer to determine total filesystem size
3. Report `total_size` from `size()` instead of `preload.len()`
4. For reads beyond preload: attempt to open and read from `/scheme/disk/0`
5. Keep overlay for writes
6. Keep block_size = 512 (from P6)
7. Add env var `DISK_PHYS_BLOCK` for the partition offset on the physical disk
### Step 2: Reduce bootloader preload cap (bootloader patch)
Create `local/patches/bootloader/P1-reduce-live-preload.patch`:
1. Change `max_preload` from 1024 MiB to a calculated minimum:
- Read the RedoxFS header to determine filesystem size
- Calculate the minimum preload needed: max(header + allocator extent, 4 MiB)
- Cap at 128 MiB (generous upper bound for metadata region)
2. Add `DISK_PHYS_BLOCK` env var so lived knows where the partition starts on disk
### Step 3: Verify
1. Build and test redbear-full ISO in QEMU with virtio-gpu
2. Verify RedoxFS mounts the full 4093 MiB filesystem
3. Verify login prompt appears
4. Verify KDE desktop loads (or at minimum, the greeter starts)
---
## 6. Risk Assessment
| Risk | Impact | Mitigation |
|------|--------|------------|
| RedoxFS header format changes between versions | lived parses header incorrectly | Use the same header parsing code as RedoxFS lib |
| ahcid not started when lived first needs disk | Read fails with ENOENT | Retry with backoff; RedoxFS mount retries automatically |
| Physical disk block offset wrong | Read corrupt data | Pass exact block offset from bootloader via env var |
| Preload too small for RedoxFS to find header | Mount fails immediately | Keep minimum preload at 4 MiB (covers any superblock) |
| Mini ISO regression | Small images broken | Test mini ISO after every change |
---
## 7. Alternative Approach: Mount From Physical Disk Directly
Instead of fixing lived, we could modify the init sequence to skip `disk.live` entirely
for the root filesystem mount:
1. Bootloader preloads just enough for kernel + initfs (no change needed)
2. lived starts but is only used for early boot I/O
3. `50_rootfs.service` is changed to explicitly mount from `/scheme/disk/0` (via ahcid)
instead of scanning all disk schemes
4. This requires passing the disk path and block offset from bootloader to init
**Pros:** Simpler lived (no disk fallback), cleaner architecture
**Cons:** Requires knowing which disk scheme serves the boot device; may not work if
the AHCI driver assigns a different number to the boot disk
**Verdict:** Fix 3 (lived with disk fallback) is more robust because it works regardless
of which disk scheme is assigned. The lived approach acts as a transparent cache layer.
---
## 8. Implementation Notes
### Bootloader env vars (current)
```
DISK_LIVE_ADDR=<hex phys addr of preload buffer>
DISK_LIVE_SIZE=<hex size of preload buffer>
REDOXFS_BLOCK=0 (always 0 for live mode)
REDOXFS_UUID=<uuid>
```
### Bootloader env vars (proposed additions)
```
DISK_PHYS_BLOCK=<hex block offset of partition on physical disk>
REDOXFS_FULL_SIZE=<hex total filesystem size>
```
### lived env vars (current)
```
DISK_LIVE_ADDR → phys addr to mmap
DISK_LIVE_SIZE → size to mmap (= preload size, NOT total filesystem size)
```
### lived env vars (proposed)
```
DISK_LIVE_ADDR → phys addr of preload buffer
DISK_LIVE_SIZE → size of preload buffer
DISK_PHYS_BLOCK → block offset for disk fallback reads
REDOXFS_FULL_SIZE → total filesystem size (for size() reporting)
```
+40 -2
View File
@@ -16,6 +16,44 @@ The code was recovered from git history, but this must never happen again.
## Rules
### 0. MEGA-PATCH + P-PATCH DISCIPLINE (HIGHEST PRIORITY)
Each patched component (base, kernel, relibc) uses a two-layer patch model:
- **`redox.patch`** (the mega-patch) — frozen baseline of all Red Bear work. Applied first.
- **`P<N>-<desc>.patch`** (individual patches) — new work done AFTER the mega-patch. Applied after.
**The single rule that prevents the most build failures:**
> **NEVER regenerate `redox.patch` from a source tree that includes P-patch changes.**
If the source tree has P-patches applied (visible in `git status --short` or in the recipe.toml
patches list after `redox.patch`), then `git diff` from that tree will absorb the P-patches into
the mega-patch. The P-patches will then fail to apply on the next clean build because their
changes are already in the mega-patch.
**Correct operations:**
| Operation | How |
|-----------|-----|
| Add new change | Create a new P-patch, add it after `redox.patch` in `recipe.toml` |
| Consolidate P-patches into mega-patch | Generate new `redox.patch` from full source tree, then **remove all P-patches from `recipe.toml`** in the same commit |
| Fix a bug in a P-patch | Create a new P-patch that fixes it (or regenerate just that P-patch) |
**Wrong operations (causes build failures):**
| Wrong operation | What breaks |
|-----------------|-------------|
| Regenerate mega-patch from tree with P-patches, leave P-patches in recipe.toml | P-patches try to re-apply changes already in mega-patch → "Reversed or previously applied" |
| Edit mega-patch by hand | Line counts go wrong, hunks fail, impossible to debug |
| Remove P-patches from recipe.toml without folding into mega-patch | Changes are lost entirely |
**Incident log:**
| Date | What happened | Root cause | Fix |
|------|---------------|-----------|-----|
| 2026-05-29 | `validate-patches base` showed mega-patch with 100+ hunk failures, P10/P11/P12 also failing | Mega-patch was regenerated from a source tree that included P-patch changes. Individual P-patches touched same files (`init/src/main.rs`) as mega-patch, causing overlap. | P10/P11/P12 are genuinely new work on top of the mega-patch (not absorbed). The validation failure was from the mega-patch itself being stale for some hunks. Fix: regenerate mega-patch from a clean mega-patch-only tree, then re-apply P-patches. |
### 1. Never remove patches to fix build failures
When a patch fails to apply:
@@ -101,5 +139,5 @@ After ANY change to the patches list or patch files:
| 2026-04-26 | Restored 8 removed patches | Agent deleted them to bypass conflicts; restored all from git HEAD |
| 2026-04-26 | Restored 9 BINS entries | Agent deleted i2cd, gpiod, ucsid, etc. to bypass missing sources |
| 2026-04-26 | Added EXISTING_BINS grep loop | Gracefully handles missing driver source instead of build failure |
| 2026-04-26 | Fixed grep/find variables | `${GREP}` and `${FIND}` are unset in redoxer env; use bare `grep`/`find` |
| 2026-04-26 | Fixed TOML escaping | `\"` in TOML triple-quotes becomes `"` in bash; use `\\\"` for literal `"` |
| 2026-05-29 | Added P12-init-fix-init-debug-import.patch | P11 introduced `init_debug` calls but forgot to import `init_debug``use crate::color::{init_error, init_warn, ...}` missing `init_debug`. Build error: `cannot find function init_debug in this scope`. |
| 2026-05-29 | Documented mega-patch discipline (Rule 0) | Recurring patch corruption caused by regenerating mega-patch from trees that include P-patches. Created Rule 0 in PATCH-GOVERNANCE.md and updated AGENTS.md. |
@@ -0,0 +1,7 @@
diff --git a/init.initfs.d/50_rootfs.service b/init.initfs.d/50_rootfs.service
index db7ba429..59f2c61c 100644
--- a/init.initfs.d/50_rootfs.service
+++ b/init.initfs.d/50_rootfs.service
@@ -7 +7 @@ cmd = "redoxfs"
-args = ["--uuid" ,"$REDOXFS_UUID", "file", "$REDOXFS_BLOCK"]
+args = ["--uuid" ,"$REDOXFS_UUID", "file"]
@@ -0,0 +1,13 @@
diff --git a/init/src/main.rs b/init/src/main.rs
index e7f6712f..6b9da2b2 100644
--- a/init/src/main.rs
+++ b/init/src/main.rs
@@ -169,0 +170 @@ fn main() {
+ if init_config.log_debug {
@@ -171 +172,2 @@ fn main() {
- init_warn(&format!("rootfs-file: {}", name));
+ init_debug(&format!("rootfs-file: {}", name));
+ }
@@ -180 +182 @@ fn main() {
- init_warn(&format!(
+ init_debug(&format!(
@@ -0,0 +1,7 @@
diff --git a/init/src/main.rs b/init/src/main.rs
index 45cb9f73..f3861e94 100644
--- a/init/src/main.rs
+++ b/init/src/main.rs
@@ -18 +18 @@ mod unit;
-use crate::color::{init_error, init_warn, status_fail, status_ok};
+use crate::color::{init_debug, init_error, init_warn, status_ok};
@@ -0,0 +1,248 @@
diff --git a/drivers/storage/lived/src/main.rs b/drivers/storage/lived/src/main.rs
index 2ca1ff27..8582e42a 100644
--- a/drivers/storage/lived/src/main.rs
+++ b/drivers/storage/lived/src/main.rs
@@ -1,0 +2,8 @@
+//!
+//! For live ISO boot: bootloader preloads the first N MiB of the filesystem into RAM.
+//! This daemon serves that preloaded region from RAM, and falls through to the physical
+//! disk (USB, AHCI, NVMe) for reads beyond the preload boundary.
+//!
+//! Boot order: lived(10) → drivers(40) → usbscsid(45) → rootfs(50)
+//! Since drivers load AFTER lived starts, the disk fallback is lazy: the physical disk
+//! handle is opened on the first out-of-range read, with retry backoff.
@@ -4,0 +13 @@
+use std::cell::RefCell;
@@ -7 +15,0 @@ use std::fs::File;
-
@@ -8,0 +17 @@ use std::os::fd::AsRawFd;
+use std::sync::atomic::{AtomicBool, Ordering};
@@ -19,0 +29,11 @@ use anyhow::{anyhow, Context};
+/// Block size must be 512 (redoxfs BLOCK_SIZE), not PAGE_SIZE.
+/// DiskWrapper::read rejects buffers not aligned to block_size, and redoxfs reads
+/// in 512-byte chunks.
+const BLOCK_SIZE: usize = 512;
+
+/// Maximum retries for opening the physical disk before giving up.
+/// Drivers (xhcid → usbscsid → /scheme/disk/) load between service 40-45,
+/// while lived starts at 10. Give plenty of retries.
+const DISK_OPEN_MAX_RETRIES: u32 = 60;
+const DISK_OPEN_RETRY_INTERVAL_MS: u64 = 500;
+
@@ -22 +41,0 @@ struct LiveDisk {
- //TODO: drop overlay blocks if they match the original
@@ -23,0 +43,4 @@ struct LiveDisk {
+ full_size: u64,
+ disk_phys_offset: u64,
+ disk_file: RefCell<Option<File>>,
+ logged_first_fallback: AtomicBool,
@@ -27 +50,6 @@ impl LiveDisk {
- fn new(phys: usize, size: usize) -> anyhow::Result<LiveDisk> {
+ fn new(
+ phys: usize,
+ preload_size: usize,
+ full_size: u64,
+ disk_phys_offset: u64,
+ ) -> anyhow::Result<LiveDisk> {
@@ -30,2 +58,2 @@ impl LiveDisk {
- .checked_add(size)
- .context("phys + size overflow")?
+ .checked_add(preload_size)
+ .context("phys + preload_size overflow")?
@@ -33 +61 @@ impl LiveDisk {
- let size = end - start;
+ let mapped_size = end - start;
@@ -41 +69 @@ impl LiveDisk {
- length: size,
+ length: mapped_size,
@@ -47 +75 @@ impl LiveDisk {
- std::slice::from_raw_parts_mut(base as *mut u8, size)
+ std::slice::from_raw_parts_mut(base as *mut u8, mapped_size)
@@ -49,0 +78,7 @@ impl LiveDisk {
+ eprintln!(
+ "lived: preload {} MiB, full filesystem {} MiB, disk_phys_offset {:#x}",
+ preload_size / (1024 * 1024),
+ full_size / (1024 * 1024),
+ disk_phys_offset,
+ );
+
@@ -52,0 +88,4 @@ impl LiveDisk {
+ full_size,
+ disk_phys_offset,
+ disk_file: RefCell::new(None),
+ logged_first_fallback: AtomicBool::new(false),
@@ -54,0 +94,69 @@ impl LiveDisk {
+
+ fn open_disk(&self) -> syscall::Result<()> {
+ if self.disk_file.borrow().is_some() {
+ return Ok(());
+ }
+ match self.try_open_disk() {
+ Ok(file) => {
+ *self.disk_file.borrow_mut() = Some(file);
+ Ok(())
+ }
+ Err(msg) => {
+ eprintln!("lived: disk fallback unavailable: {}", msg);
+ Err(syscall::Error::new(EIO))
+ }
+ }
+ }
+
+ fn try_open_disk(&self) -> Result<File, String> {
+ // Try common disk scheme paths. USB boot appears as /scheme/disk/0 or /scheme/usbscsi/0.
+ // AHCI boot appears as /scheme/disk/0. NVMe appears as /scheme/disk/0.
+ let candidates = [
+ "/scheme/disk/0",
+ "/scheme/usbscsi/0",
+ "/scheme/disk/1",
+ "/scheme/usbscsi/1",
+ ];
+
+ for attempt in 0..DISK_OPEN_MAX_RETRIES {
+ for path in &candidates {
+ if let Ok(file) = File::open(path) {
+ eprintln!("lived: opened physical disk at {} (attempt {})", path, attempt + 1);
+ return Ok(file);
+ }
+ }
+
+ if attempt < DISK_OPEN_MAX_RETRIES - 1 {
+ std::thread::sleep(std::time::Duration::from_millis(DISK_OPEN_RETRY_INTERVAL_MS));
+ }
+ }
+
+ Err(format!(
+ "no /scheme/disk/ found after {} retries",
+ DISK_OPEN_MAX_RETRIES
+ ))
+ }
+
+ fn read_from_disk(&self, offset: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
+ let disk_offset = self.disk_phys_offset + offset;
+
+ if !self.logged_first_fallback.swap(true, Ordering::Relaxed) {
+ eprintln!(
+ "lived: first disk fallback read at offset {} MiB (disk offset {:#x})",
+ offset / (1024 * 1024),
+ disk_offset,
+ );
+ }
+
+ self.open_disk()?;
+
+ use std::io::{Read, Seek, SeekFrom};
+ let mut disk = self.disk_file.borrow_mut();
+ let file = disk.as_mut().unwrap();
+ file.seek(SeekFrom::Start(disk_offset))
+ .map_err(|_| syscall::Error::new(EIO))?;
+ file.read_exact(buffer)
+ .map_err(|_| syscall::Error::new(EIO))?;
+
+ Ok(buffer.len())
+ }
@@ -59 +167 @@ impl Disk for LiveDisk {
- PAGE_SIZE as u32
+ BLOCK_SIZE as u32
@@ -63 +171 @@ impl Disk for LiveDisk {
- self.original.len() as u64
+ self.full_size
@@ -67,2 +175,4 @@ impl Disk for LiveDisk {
- let mut offset = (block as usize) * PAGE_SIZE;
- if offset + buffer.len() > self.original.len() {
+ let bs = self.block_size() as usize;
+ let mut offset = (block as usize) * bs;
+
+ if offset + buffer.len() > self.full_size as usize {
@@ -71 +181,28 @@ impl Disk for LiveDisk {
- for chunk in buffer.chunks_mut(PAGE_SIZE) {
+
+ let preload_len = self.original.len();
+
+ if offset + buffer.len() <= preload_len {
+ for chunk in buffer.chunks_mut(bs) {
+ match self.overlay.get(&block) {
+ Some(overlay) => {
+ chunk.copy_from_slice(&overlay[..chunk.len()]);
+ }
+ None => {
+ chunk.copy_from_slice(&self.original[offset..offset + chunk.len()]);
+ }
+ }
+ block += 1;
+ offset += bs;
+ }
+ return Ok(buffer.len());
+ }
+
+ if offset >= preload_len {
+ let fs_byte_offset = (block as u64) * bs as u64;
+ return self.read_from_disk(fs_byte_offset, buffer);
+ }
+
+ let preload_remaining = preload_len - offset;
+ let (ram_part, disk_part) = buffer.split_at_mut(preload_remaining);
+
+ for chunk in ram_part.chunks_mut(bs) {
@@ -81 +218 @@ impl Disk for LiveDisk {
- offset += PAGE_SIZE;
+ offset += bs;
@@ -82,0 +220,4 @@ impl Disk for LiveDisk {
+
+ let disk_fs_offset = (block as u64) * bs as u64;
+ self.read_from_disk(disk_fs_offset, disk_part)?;
+
@@ -87,2 +228,3 @@ impl Disk for LiveDisk {
- let mut offset = (block as usize) * PAGE_SIZE;
- if offset + buffer.len() > self.original.len() {
+ let bs = self.block_size() as usize;
+ let mut offset = (block as usize) * bs;
+ if offset + buffer.len() > self.full_size as usize {
@@ -91 +233 @@ impl Disk for LiveDisk {
- for chunk in buffer.chunks(PAGE_SIZE) {
+ for chunk in buffer.chunks(bs) {
@@ -93,2 +235,3 @@ impl Disk for LiveDisk {
- let offset = (block as usize) * PAGE_SIZE;
- self.original[offset..offset + PAGE_SIZE]
+ let off = (block as usize) * bs;
+ if off + bs <= self.original.len() {
+ self.original[off..off + bs]
@@ -96,0 +240,3 @@ impl Disk for LiveDisk {
+ } else {
+ vec![0u8; bs].into_boxed_slice()
+ }
@@ -100 +246 @@ impl Disk for LiveDisk {
- offset += PAGE_SIZE;
+ offset += bs;
@@ -112 +258,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- let mut size = 0;
+ let mut preload_size = 0;
+ let mut full_size = 0u64;
+ let mut disk_phys_offset = 0u64;
@@ -129 +277 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- size = usize::from_str_radix(value, 16).unwrap_or(0);
+ preload_size = usize::from_str_radix(value, 16).unwrap_or(0);
@@ -130,0 +279,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
+
+ if name == "REDOXFS_FULL_SIZE" {
+ full_size = u64::from_str_radix(value, 16).unwrap_or(0);
@@ -133,2 +284,6 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- if phys == 0 || size == 0 {
- // No live disk data, no need to say anything or exit with
+ if name == "DISK_PHYS_BLOCK" {
+ disk_phys_offset = u64::from_str_radix(value, 16).unwrap_or(0) * BLOCK_SIZE as u64;
+ }
+ }
+
+ if phys == 0 || preload_size == 0 {
@@ -138,0 +294,4 @@ fn daemon(daemon: daemon::Daemon) -> ! {
+ if full_size == 0 {
+ full_size = preload_size as u64;
+ }
+
@@ -152 +311 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- LiveDisk::new(phys, size).unwrap_or_else(|err| {
+ LiveDisk::new(phys, preload_size, full_size, disk_phys_offset).unwrap_or_else(|err| {
@@ -160 +319 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
+ libredox::call::setrens(0, 0).expect("lived: failed to enter null namespace");
@@ -0,0 +1,118 @@
--- a/drivers/storage/lived/src/main.rs
+++ b/drivers/storage/lived/src/main.rs
@@ -45 +45 @@
- disk_file: RefCell<Option<File>>,
+ disk_path: RefCell<Option<String>>,
@@ -90 +90 @@
- disk_file: RefCell::new(None),
+ disk_path: RefCell::new(None),
@@ -96 +96 @@
- if self.disk_file.borrow().is_some() {
+ if self.disk_path.borrow().is_some() {
@@ -100,2 +100,2 @@
- Ok(file) => {
- *self.disk_file.borrow_mut() = Some(file);
+ Ok(path) => {
+ *self.disk_path.borrow_mut() = Some(path);
@@ -111,10 +111 @@
- fn try_open_disk(&self) -> Result<File, String> {
- // Try common disk scheme paths. USB boot appears as /scheme/disk/0 or /scheme/usbscsi/0.
- // AHCI boot appears as /scheme/disk/0. NVMe appears as /scheme/disk/0.
- let candidates = [
- "/scheme/disk/0",
- "/scheme/usbscsi/0",
- "/scheme/disk/1",
- "/scheme/usbscsi/1",
- ];
-
+ fn try_open_disk(&self) -> Result<String, String> {
@@ -122,4 +113,70 @@
- for path in &candidates {
- if let Ok(file) = File::open(path) {
- eprintln!("lived: opened physical disk at {} (attempt {})", path, attempt + 1);
- return Ok(file);
+ if let Ok(entries) = std::fs::read_dir("/scheme") {
+ let all_schemes: Vec<String> = entries
+ .flatten()
+ .map(|e| e.file_name().to_string_lossy().to_string())
+ .collect();
+ let has_disk = all_schemes
+ .iter()
+ .any(|s| s.starts_with("disk.") && s != "disk.live");
+ if attempt == 0 || attempt == DISK_OPEN_MAX_RETRIES - 1 || has_disk {
+ eprintln!(
+ "lived: attempt {} /scheme/ = {:?} (has_disk={})",
+ attempt + 1,
+ all_schemes,
+ has_disk
+ );
+ }
+ for name_str in &all_schemes {
+ if name_str.starts_with("disk.") && name_str != "disk.live" {
+ for idx in [0u32, 2, 1, 3, 4, 5] {
+ let path = format!("/scheme/{}/{}", name_str, idx);
+ if let Ok(mut file) = File::open(&path) {
+ use std::io::{Read, Seek, SeekFrom};
+ let test_offset = self.disk_phys_offset + self.full_size - 4096;
+ let mut probe = vec![0u8; 4096];
+ if file.seek(SeekFrom::Start(test_offset)).is_err() {
+ eprintln!(
+ "lived: skipping {} — seek to {:#x} failed",
+ path, test_offset
+ );
+ continue;
+ }
+ match file.read_exact(&mut probe) {
+ Ok(()) => {
+ eprintln!(
+ "lived: validated physical disk at {} (attempt {}, verified at end {:#x})",
+ path,
+ attempt + 1,
+ test_offset
+ );
+ return Ok(path);
+ }
+ Err(e) => {
+ eprintln!(
+ "lived: skipping {} — read at {:#x}: {}",
+ path, test_offset, e
+ );
+ continue;
+ }
+ }
+ } else if attempt == 0 {
+ eprintln!("lived: {} not openable", path);
+ }
+ }
+ let path_root = format!("/scheme/{}", name_str);
+ if let Ok(mut file) = File::open(&path_root) {
+ use std::io::{Read, Seek, SeekFrom};
+ let test_offset = self.disk_phys_offset + self.full_size - 4096;
+ let mut probe = vec![0u8; 4096];
+ if file.seek(SeekFrom::Start(test_offset)).is_ok()
+ && file.read_exact(&mut probe).is_ok()
+ {
+ eprintln!(
+ "lived: validated physical disk at {} (attempt {})",
+ path_root,
+ attempt + 1
+ );
+ return Ok(path_root);
+ }
+ }
+ }
@@ -152,0 +210,4 @@
+ let path = self.disk_path.borrow();
+ let disk_path = path.as_ref().unwrap();
+ let mut file = File::open(disk_path).map_err(|_| syscall::Error::new(EIO))?;
+
@@ -154,2 +214,0 @@
- let mut disk = self.disk_file.borrow_mut();
- let file = disk.as_mut().unwrap();
@@ -319,2 +378,4 @@
- libredox::call::setrens(0, 0).expect("lived: failed to enter null namespace");
-
+ // Lived must NOT call setrens(0, 0). The null namespace only contains
+ // "memory" and "pipe" (see relibc redox_setrens_v1). Lived needs
+ // ongoing access to /scheme/ for disk scheme discovery after storage
+ // drivers register their schemes asynchronously.
@@ -0,0 +1,202 @@
diff --git a/logd/src/scheme.rs b/logd/src/scheme.rs
index 070de3d6..f5c1549a 100644
--- a/logd/src/scheme.rs
+++ b/logd/src/scheme.rs
@@ -1,2 +1,2 @@
-use std::collections::{BTreeMap, VecDeque};
-use std::fs::{File, OpenOptions};
+use std::collections::{BTreeMap, HashMap, VecDeque};
+use std::fs::{File, OpenOptions, rename};
@@ -5,0 +6 @@ use std::os::fd::{FromRawFd, RawFd};
+use std::path::PathBuf;
@@ -6,0 +8 @@ use std::sync::mpsc::{self, Sender};
+use std::time::{SystemTime, UNIX_EPOCH};
@@ -13,0 +16,5 @@ use syscall::schemev2::NewFdFlags;
+const LOG_DIR: &str = "/var/log";
+const MAX_LOG_SIZE: u64 = 10 * 1024 * 1024;
+const MAX_ROTATED_FILES: u32 = 5;
+const MEMORY_LOG_LIMIT: usize = 1000;
+
@@ -31 +38 @@ enum OutputCmd {
- Log(Vec<u8>),
+ Log { context: String, line: Vec<u8> },
@@ -34,0 +42,96 @@ enum OutputCmd {
+fn json_escape(s: &str) -> String {
+ let mut out = String::with_capacity(s.len());
+ for c in s.chars() {
+ match c {
+ '\\' => out.push_str("\\\\"),
+ '"' => out.push_str("\\\""),
+ '\n' => out.push_str("\\n"),
+ '\r' => out.push_str("\\r"),
+ '\t' => out.push_str("\\t"),
+ c if c.is_control() => out.push_str(&format!("\\u{:04x}", c as u32)),
+ c => out.push(c),
+ }
+ }
+ out
+}
+
+fn format_json_line(context: &str, line: &[u8]) -> Vec<u8> {
+ let now = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .unwrap_or_default();
+ let secs = now.as_secs();
+ let timestamp = format!(
+ "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
+ 1970 + secs / 31_557_600,
+ (secs % 31_557_600) / 2_592_000 + 1,
+ (secs % 2_592_000) / 86_400 + 1,
+ (secs % 86_400) / 3600,
+ (secs % 3600) / 60,
+ secs % 60
+ );
+ let text = String::from_utf8_lossy(line).trim_end_matches('\n').to_string();
+ let (source, message) = match text.split_once(": ") {
+ Some((s, m)) => (s, m),
+ None => (context, text.as_str()),
+ };
+ let json = format!(
+ "{{\"timestamp\":\"{}\",\"source\":\"{}\",\"message\":\"{}\"}}\n",
+ json_escape(&timestamp),
+ json_escape(source),
+ json_escape(message)
+ );
+ json.into_bytes()
+}
+
+struct LogFile {
+ file: File,
+ path: PathBuf,
+ bytes_written: u64,
+}
+
+impl LogFile {
+ fn open(path: PathBuf) -> std::io::Result<Self> {
+ let file = OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open(&path)?;
+ let metadata = file.metadata()?;
+ Ok(LogFile {
+ file,
+ path,
+ bytes_written: metadata.len(),
+ })
+ }
+
+ fn write(&mut self, data: &[u8]) -> std::io::Result<()> {
+ self.file.write_all(data)?;
+ self.file.flush()?;
+ self.bytes_written += data.len() as u64;
+ Ok(())
+ }
+
+ fn maybe_rotate(&mut self) -> std::io::Result<()> {
+ if self.bytes_written < MAX_LOG_SIZE {
+ return Ok(());
+ }
+
+ drop(std::mem::replace(&mut self.file, unsafe { File::from_raw_fd(-1) }));
+
+ for i in (1..MAX_ROTATED_FILES).rev() {
+ let old_path = self.path.with_extension(format!("log.{}", i));
+ let new_path = self.path.with_extension(format!("log.{}", i + 1));
+ let _ = rename(&old_path, &new_path);
+ }
+
+ let backup_path = self.path.with_extension("log.1");
+ let _ = rename(&self.path, &backup_path);
+
+ self.file = OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open(&self.path)?;
+ self.bytes_written = 0;
+ Ok(())
+ }
+}
+
@@ -43,0 +147,13 @@ impl<'sock> LogScheme<'sock> {
+ let _ = std::fs::create_dir_all("/var/log");
+ let persistent_log: Option<File> = OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open("/var/log/system.log")
+ .ok();
+ let mut service_logs: HashMap<String, LogFile> = HashMap::new();
+
+ let _ = std::fs::create_dir_all(LOG_DIR);
+
+
+ let json_format = std::env::var("LOGD_JSON").map_or(false, |v| v == "1");
+
@@ -48,0 +165,5 @@ impl<'sock> LogScheme<'sock> {
+ let mut persistent = persistent_log;
+ if let Some(ref mut f) = persistent {
+ let _ = f.write(b"--- logd started ---
+");
+ }
@@ -51 +172,32 @@ impl<'sock> LogScheme<'sock> {
- OutputCmd::Log(line) => {
+ OutputCmd::Log { context, line } => {
+ let out_line = if json_format {
+ format_json_line(&context, &line)
+ } else {
+ line.clone()
+ };
+ if let Some(ref mut f) = persistent {
+ let _ = f.write(&out_line);
+ let _ = f.flush();
+ }
+
+ let service_name = context.split(':').next().unwrap_or("unknown");
+ if !service_name.is_empty() {
+ let log_path = PathBuf::from(LOG_DIR).join(format!("{}.log", service_name));
+ let entry = service_logs.entry(service_name.to_string()).or_insert_with(|| {
+ LogFile::open(log_path).unwrap_or_else(|_| {
+ LogFile::open(PathBuf::from("/dev/null")).unwrap()
+ })
+ });
+ let _ = entry.write(&out_line);
+ let _ = entry.maybe_rotate();
+ }
+
+ let system_path = PathBuf::from(LOG_DIR).join("system.log");
+ let system_entry = service_logs.entry("system".to_string()).or_insert_with(|| {
+ LogFile::open(system_path).unwrap_or_else(|_| {
+ LogFile::open(PathBuf::from("/dev/null")).unwrap()
+ })
+ });
+ let _ = system_entry.write(&out_line);
+ let _ = system_entry.maybe_rotate();
+
@@ -53 +205 @@ impl<'sock> LogScheme<'sock> {
- let _ = file.write(&line);
+ let _ = file.write(&out_line);
@@ -56,3 +208,2 @@ impl<'sock> LogScheme<'sock> {
- logs.push_back(line);
- // Keep a limited amount of logs for backfilling to bound memory usage
- while logs.len() > 1000 {
+ logs.push_back(out_line);
+ while logs.len() > MEMORY_LOG_LIMIT {
@@ -68 +218,0 @@ impl<'sock> LogScheme<'sock> {
-
@@ -83 +232,0 @@ impl<'sock> LogScheme<'sock> {
- // FIXME currently possible as /scheme/log/kernel presents a snapshot of the log queue
@@ -118 +266,0 @@ impl<'sock> LogScheme<'sock> {
- // Writing to the kernel debug log never blocks
@@ -124 +272,4 @@ impl<'sock> LogScheme<'sock> {
- .send(OutputCmd::Log(mem::take(handle_buf)))
+ .send(OutputCmd::Log {
+ context: context.to_string(),
+ line: mem::take(handle_buf),
+ })
@@ -173,3 +323,0 @@ impl<'sock> SchemeSync for LogScheme<'sock> {
-
- // TODO
-
@@ -244,3 +391,0 @@ impl<'sock> SchemeSync for LogScheme<'sock> {
-
- //TODO: flush remaining data?
-
@@ -0,0 +1,48 @@
diff --git a/src/main.rs b/src/main.rs
index 78dabb0..a41086e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -557,0 +558 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
+ let disk_block = fs.block;
@@ -559 +560,13 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- print!("live: 0/{} MiB", size / MIBI as u64);
+ let max_preload: u64 = 128 * MIBI as u64;
+ let preload_size = if size > max_preload {
+ println!(
+ "live: filesystem is {} MiB, capping preload at {} MiB",
+ size / MIBI as u64,
+ max_preload / MIBI as u64
+ );
+ max_preload
+ } else {
+ size
+ };
+
+ print!("live: 0/{} MiB", preload_size / MIBI as u64);
@@ -561 +574 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- let live_size = match usize::try_from(size) {
+ let live_size = match usize::try_from(preload_size) {
@@ -593 +606 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
+ print!("\rlive: {}/{} MiB", i / MIBI as u64, preload_size / MIBI as u64);
@@ -600 +613,10 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
+ println!("\rlive: {}/{} MiB", i / MIBI as u64, preload_size / MIBI as u64);
+
+ if preload_size < size {
+ println!(
+ "live: preloaded {} MiB of {} MiB filesystem (remaining {} MiB from disk)",
+ preload_size / MIBI as u64,
+ size / MIBI as u64,
+ (size - preload_size) / MIBI as u64
+ );
+ }
@@ -613 +635 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- Some(live)
+ Some((disk_block, size, live))
@@ -672 +694 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- if let Some(live) = live_opt {
+ if let Some((disk_block, fs_size, live)) = live_opt {
@@ -674,0 +697,2 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
+ writeln!(w, "DISK_PHYS_BLOCK={:016x}", disk_block).unwrap();
+ writeln!(w, "REDOXFS_FULL_SIZE={:016x}", fs_size).unwrap();
@@ -0,0 +1,91 @@
diff --git a/src/arch/x86/x64.rs b/src/arch/x86/x64.rs
index a0a275a..cad4592 100644
--- a/src/arch/x86/x64.rs
+++ b/src/arch/x86/x64.rs
@@ -46 +46 @@ pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) ->
- for pdp_i in 0..8 {
+ for pdp_i in 0..64 {
@@ -103 +103 @@ pub unsafe fn paging_framebuffer(
- if framebuffer_phys + framebuffer_size <= 0x2_0000_0000 {
+ if framebuffer_phys + framebuffer_size <= 0x10_0000_0000 {
diff --git a/src/main.rs b/src/main.rs
index a41086e..1ce4bf3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -560,11 +560 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- let max_preload: u64 = 128 * MIBI as u64;
- let preload_size = if size > max_preload {
- println!(
- "live: filesystem is {} MiB, capping preload at {} MiB",
- size / MIBI as u64,
- max_preload / MIBI as u64
- );
- max_preload
- } else {
- size
- };
+ let mut preload_size: u64 = size;
@@ -572,4 +562,5 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- print!("live: 0/{} MiB", preload_size / MIBI as u64);
-
- let live_size = match usize::try_from(preload_size) {
- Ok(live_size) => live_size,
+ let mut ptr = ptr::null_mut();
+ if live {
+ ptr = os.alloc_zeroed_page_aligned(
+ match usize::try_from(preload_size) {
+ Ok(s) => s,
@@ -581 +572,19 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- };
+ }
+ );
+ if ptr.is_null() && live {
+ println!(
+ "\rlive: unable to allocate {} MiB, halving...",
+ preload_size / MIBI as u64
+ );
+ let floor: u64 = 128 * MIBI as u64;
+ while preload_size > floor {
+ preload_size /= 2;
+ if let Ok(smaller) = usize::try_from(preload_size) {
+ ptr = os.alloc_zeroed_page_aligned(smaller);
+ if !ptr.is_null() {
+ break;
+ }
+ }
+ }
+ }
+ }
@@ -583,6 +592 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- let ptr = if live {
- os.alloc_zeroed_page_aligned(live_size)
- } else {
- ptr::null_mut()
- };
- if live && ptr.is_null() {
+ if ptr.is_null() {
@@ -595,0 +600,11 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
+ print!("live: 0/{} MiB", preload_size / MIBI as u64);
+
+ let live_size = match usize::try_from(preload_size) {
+ Ok(live_size) => live_size,
+ Err(_) => {
+ println!("\rlive: disabled (image too large for bootloader address space)");
+ live = false;
+ 0
+ }
+ };
+
diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs
index d0b2cf1..dacead6 100644
--- a/src/os/uefi/mod.rs
+++ b/src/os/uefi/mod.rs
@@ -48,2 +48 @@ pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 {
- // Max address mapped by src/arch paging code (8 GiB)
- let mut ptr = 0x2_0000_0000;
+ let mut ptr = 0;
@@ -51,2 +50,2 @@ pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 {
- 1, // AllocateMaxAddress
- MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list
+ 0, // AllocateAnyPages
+ MemoryType::EfiLoaderData, // Valid until ExitBootServices
@@ -0,0 +1,7 @@
diff --git a/src/main.rs b/src/main.rs
index be5f3b7..531b167 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -514 +514 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- "Redox OS Bootloader {} on {}",
+ "RedBear OS Bootloader {} on {}",
@@ -0,0 +1,107 @@
diff --git a/src/devices/graphical_debug/debug.rs b/src/devices/graphical_debug/debug.rs
index 4b684c8a..e3fe0472 100644
--- a/src/devices/graphical_debug/debug.rs
+++ b/src/devices/graphical_debug/debug.rs
@@ -29,0 +30,3 @@ impl DebugDisplay {
+ const CHAR_WIDTH: usize = 8;
+ const CHAR_HEIGHT: usize = 16;
+
@@ -43,2 +46,2 @@ impl DebugDisplay {
- let w = display.width / 8;
- let h = display.height / 16;
+ let w = display.width / Self::CHAR_WIDTH;
+ let h = display.height / Self::CHAR_HEIGHT;
@@ -54,0 +58,21 @@ impl DebugDisplay {
+ fn scroll_up(&mut self) {
+ let stride = self.display.stride;
+ let width = self.display.width;
+ let total_height = self.display.height;
+ let scroll_px = Self::CHAR_HEIGHT;
+
+ unsafe {
+ let ptr = self.display.onscreen_ptr;
+ for row in 0..total_height - scroll_px {
+ ptr::copy(
+ ptr.add((row + scroll_px) * stride),
+ ptr.add(row * stride),
+ width,
+ );
+ }
+ for row in total_height - scroll_px..total_height {
+ ptr::write_bytes(ptr.add(row * stride), 0, width);
+ }
+ }
+ }
+
@@ -59 +83,5 @@ impl DebugDisplay {
- self.y = (self.y + 1) % self.h;
+ self.y += 1;
+ if self.y >= self.h {
+ self.scroll_up();
+ self.y = self.h - 1;
+ }
@@ -67 +94,0 @@ impl DebugDisplay {
- // Byte 0x1B starts ESC sequence
@@ -72 +98,0 @@ impl DebugDisplay {
- // Ignore other nonprintable characters
@@ -77 +102,0 @@ impl DebugDisplay {
- // '[' after ESC starts CSI sequence
@@ -82 +106,0 @@ impl DebugDisplay {
- // Capture any bytes after ESC
@@ -87 +110,0 @@ impl DebugDisplay {
- // Byte 0x40 to 0x7E ends CSI
@@ -92 +114,0 @@ impl DebugDisplay {
- // Capture any bytes after CSI
@@ -96 +117,0 @@ impl DebugDisplay {
- // Allow any other bytes
@@ -102 +122,0 @@ impl DebugDisplay {
- self.clear_row((self.y + 1) % self.h);
@@ -105 +125 @@ impl DebugDisplay {
- self.char(self.x * 8, self.y * 16, b as char, 0xFFFFFF);
+ self.char(self.x * Self::CHAR_WIDTH, self.y * Self::CHAR_HEIGHT, b as char, 0xFFFFFF);
@@ -112 +132 @@ impl DebugDisplay {
- for row in y * 16..(y + 1) * 16 {
+ for row in y * Self::CHAR_HEIGHT..(y + 1) * Self::CHAR_HEIGHT {
@@ -123 +142,0 @@ impl DebugDisplay {
- /// Draw a character
@@ -125,7 +144,8 @@ impl DebugDisplay {
- if x + 8 <= self.display.width && y + 16 <= self.display.height {
- let phys_y = y % self.display.height;
- let mut dst = unsafe {
- self.display
- .onscreen_ptr
- .add(phys_y * self.display.stride + x)
- };
+ if x + Self::CHAR_WIDTH > self.display.width || y + Self::CHAR_HEIGHT > self.display.height {
+ return;
+ }
+
+ let font_i = Self::CHAR_HEIGHT * (character as usize);
+ if font_i + Self::CHAR_HEIGHT > FONT.len() {
+ return;
+ }
@@ -133,6 +152,0 @@ impl DebugDisplay {
- let font_i = 16 * (character as usize);
- if font_i + 16 <= FONT.len() {
- for row in 0..16 {
- let row_data = FONT[font_i + row];
- for col in 0..8 {
- if (row_data >> (7 - col)) & 1 == 1 {
@@ -139,0 +154,7 @@ impl DebugDisplay {
+ let ptr = self.display.onscreen_ptr;
+ let stride = self.display.stride;
+ for row in 0..Self::CHAR_HEIGHT {
+ let row_data = FONT[font_i + row];
+ let dst = ptr.add((y + row) * stride + x);
+ for col in 0..Self::CHAR_WIDTH {
+ if (row_data >> (Self::CHAR_WIDTH - 1 - col)) & 1 == 1 {
@@ -144,9 +164,0 @@ impl DebugDisplay {
-
- let next_phys_y = (phys_y + row + 1) % self.display.height;
- dst = unsafe {
- self.display
- .onscreen_ptr
- .add(next_phys_y * self.display.stride + x)
- };
- }
- }
@@ -0,0 +1,140 @@
diff --git a/src/context/context.rs b/src/context/context.rs
index 6d723f4..836ce25 100644
--- a/src/context/context.rs
+++ b/src/context/context.rs
@@ -153,0 +154,3 @@ pub struct Context {
+ /// Capability bitmask — derived from euid by procmgr: euid==0 → CAP_ALL, else 0.
+ pub caps: u64,
+
@@ -159,0 +163,6 @@ pub struct Context {
+impl Context {
+ pub fn has_cap(&self, cap: u64) -> bool {
+ self.caps & cap == cap
+ }
+}
+
@@ -210,0 +220,2 @@ impl Context {
+ caps: 0,
+
@@ -485,0 +497 @@ impl Context {
+ caps: self.caps,
diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs
index 5d73469..7e1558a 100644
--- a/src/scheme/acpi.rs
+++ b/src/scheme/acpi.rs
@@ -142 +142 @@ impl KernelScheme for AcpiScheme {
- if ctx.uid != 0 {
+ if !ctx.has_cap(crate::scheme::caps::CAP_ACPI) {
diff --git a/src/scheme/caps.rs b/src/scheme/caps.rs
new file mode 100644
index 0000000..6886e88
--- /dev/null
+++ b/src/scheme/caps.rs
@@ -0,0 +1,29 @@
+//! Kernel capability bitmask for fine-grained privilege control.
+//!
+//! Each capability is a single bit in a `u64`. Processes with `euid == 0`
+//! (via procmgr SetResugid) receive `CAP_ALL`. Non-root processes receive `0`
+//! by default. Future work: explicit capability assignment via proc scheme.
+
+/// Register or unregister kernel schemes.
+pub const CAP_SCHEME_REGISTER: u64 = 1 << 0;
+/// Map physical memory (scheme:memory/physical).
+pub const CAP_PHYS_MEM: u64 = 1 << 1;
+/// Allocate IRQ vectors (scheme:irq).
+pub const CAP_IRQ: u64 = 1 << 2;
+/// Access ACPI tables (scheme:acpi).
+pub const CAP_ACPI: u64 = 1 << 3;
+/// Use kernel debugger (scheme:debug).
+pub const CAP_SYS_DEBUG: u64 = 1 << 4;
+/// Write to arbitrary files / sys:action (scheme:sys write).
+pub const CAP_SYS_WRITE: u64 = 1 << 5;
+/// Read/write model-specific registers (scheme:msr).
+pub const CAP_SYS_MSR: u64 = 1 << 6;
+/// Access PS/2 keyboard/mouse (scheme:serio).
+pub const CAP_SERIO: u64 = 1 << 7;
+/// Change file ownership (scheme:user chown).
+pub const CAP_CHOWN: u64 = 1 << 8;
+/// Modify process attributes: setuid/setgid, ptrace, signal to arbitrary procs.
+pub const CAP_PROC_ATTR: u64 = 1 << 9;
+
+/// All capabilities set — assigned to euid == 0 processes.
+pub const CAP_ALL: u64 = !0u64;
diff --git a/src/scheme/debug.rs b/src/scheme/debug.rs
index 4a23b3c..ae9a96a 100644
--- a/src/scheme/debug.rs
+++ b/src/scheme/debug.rs
@@ -76 +76 @@ impl KernelScheme for DebugScheme {
- if ctx.uid != 0 {
+ if !ctx.has_cap(crate::scheme::caps::CAP_SYS_DEBUG) {
diff --git a/src/scheme/irq.rs b/src/scheme/irq.rs
index 4222960..bf99a85 100644
--- a/src/scheme/irq.rs
+++ b/src/scheme/irq.rs
@@ -259 +259 @@ impl crate::scheme::KernelScheme for IrqScheme {
- if ctx.uid != 0 {
+ if !ctx.has_cap(crate::scheme::caps::CAP_IRQ) {
diff --git a/src/scheme/memory.rs b/src/scheme/memory.rs
index c2f9f47..146c461 100644
--- a/src/scheme/memory.rs
+++ b/src/scheme/memory.rs
@@ -235 +235 @@ impl KernelScheme for MemoryScheme {
- if ctx.uid != 0
+ if !ctx.has_cap(crate::scheme::caps::CAP_PHYS_MEM)
diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs
index 765e547..96826de 100644
--- a/src/scheme/mod.rs
+++ b/src/scheme/mod.rs
@@ -81,0 +82,2 @@ pub mod sys;
+pub mod caps;
+
@@ -356 +358 @@ impl KernelScheme for SchemeList {
- if caller.uid != 0 {
+ if !caller.has_cap(caps::CAP_SCHEME_REGISTER) {
@@ -813,0 +816 @@ pub struct CallerCtx {
+ pub caps: u64,
@@ -815,0 +819,3 @@ impl CallerCtx {
+ pub fn has_cap(&self, cap: u64) -> bool {
+ self.caps & cap == cap
+ }
@@ -822,0 +829 @@ impl CallerCtx {
+ caps: self.caps,
diff --git a/src/scheme/serio.rs b/src/scheme/serio.rs
index 2650502..8e32c98 100644
--- a/src/scheme/serio.rs
+++ b/src/scheme/serio.rs
@@ -82 +82 @@ impl KernelScheme for SerioScheme {
- if ctx.uid != 0 {
+ if !ctx.has_cap(crate::scheme::caps::CAP_SERIO) {
diff --git a/src/scheme/sys/mod.rs b/src/scheme/sys/mod.rs
index 9eb3564..902ab5a 100644
--- a/src/scheme/sys/mod.rs
+++ b/src/scheme/sys/mod.rs
@@ -144 +144 @@ impl KernelScheme for SysScheme {
- if ctx.uid != 0 {
+ if !ctx.has_cap(crate::scheme::caps::CAP_SYS_MSR) {
@@ -170 +170 @@ impl KernelScheme for SysScheme {
- if matches!(entry.1, Wr(_)) && ctx.uid != 0 {
+ if matches!(entry.1, Wr(_)) && !ctx.has_cap(crate::scheme::caps::CAP_SYS_WRITE) {
diff --git a/src/scheme/user.rs b/src/scheme/user.rs
index dfbf66b..e215b8d 100644
--- a/src/scheme/user.rs
+++ b/src/scheme/user.rs
@@ -1593 +1593 @@ impl KernelScheme for UserScheme {
- if cx.euid != 0 && (uid != cx.euid || gid != cx.egid) {
+ if !cx.has_cap(crate::scheme::caps::CAP_CHOWN) && (uid != cx.euid || gid != cx.egid) {
diff --git a/src/startup/mod.rs b/src/startup/mod.rs
index 86aabc2..4af65a3 100644
--- a/src/startup/mod.rs
+++ b/src/startup/mod.rs
@@ -190,0 +191 @@ pub(crate) fn kmain(bootstrap: Bootstrap) -> ! {
+ context.caps = crate::scheme::caps::CAP_ALL;
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
--- a/src/scheme/proc.rs
+++ b/src/scheme/proc.rs
@@ -1275,0 +1276,5 @@ impl ContextHandle {
+ guard.caps = if info.euid == 0 {
+ crate::scheme::caps::CAP_ALL
+ } else {
+ 0
+ };
@@ -0,0 +1,34 @@
diff --git a/xf86drm.c b/xf86drm.c
index 1b206ccd4..c3904caa3 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -1774,0 +1775,23 @@ drm_public drmVersionPtr drmGetVersion(int fd)
+
+ /* The scheme returns a NUL-terminated driver name in version.name.
+ * KWin drm_gpu.cpp dereferences version->name unconditionally
+ * (strstr checks for "i915", "amdgpu", "virtio" etc.) so it must
+ * never be NULL.
+ */
+ version.name[sizeof(version.name) - 1] = '\0';
+ if (version.name[0] != '\0') {
+ size_t len = strlen(version.name);
+ retval->name = drmMalloc(len + 1);
+ if (retval->name) {
+ memcpy(retval->name, version.name, len + 1);
+ retval->name_len = len;
+ }
+ } else {
+ const char fallback[] = "redox-drm";
+ retval->name = drmMalloc(sizeof(fallback));
+ if (retval->name) {
+ memcpy(retval->name, fallback, sizeof(fallback));
+ retval->name_len = sizeof(fallback) - 1;
+ }
+ }
+
diff --git a/xf86drm_redox.h b/xf86drm_redox.h
index c1abe8256..7f9d252fa 100644
--- a/xf86drm_redox.h
+++ b/xf86drm_redox.h
@@ -140,0 +141 @@ struct redox_drm_version_wire {
+ char name[64];
@@ -0,0 +1,68 @@
diff --git a/xf86drm.c b/xf86drm.c
index c3904caa3..306026e8b 100644
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -4096 +4096 @@ static int drmParseSubsystemType(int maj, int min)
-#elif defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD__)
+#elif defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__redox__)
@@ -5122,0 +5123,60 @@ drm_public int drmGetDeviceFromDevId(dev_t find_rdev, uint32_t flags, drmDeviceP
+ return 0;
+#elif defined(__redox__)
+ /* On Redox there is no /dev/dri/ directory to enumerate.
+ * Instead, open /scheme/drm/card0 and query PCI info to
+ * construct a single drmDevice that serves as both primary
+ * and render node. */
+ drmDevicePtr devp;
+ char *pptr;
+ int max_node_length = 64, i;
+ size_t extra, psize;
+ uint8_t pbuf[22];
+ size_t prsize = 0;
+ int pret, fd;
+
+ if (device == NULL)
+ return -EINVAL;
+ if (drm_device_validate_flags(flags))
+ return -EINVAL;
+
+ fd = open("/scheme/drm/card0", O_RDWR | O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ pret = redox_drm_exchange(fd, REDOX_DRM_IOCTL_GET_PCI_INFO, NULL, 0,
+ pbuf, sizeof(pbuf), &prsize);
+ close(fd);
+ if (pret != 0 || prsize < 17)
+ return -EIO;
+
+ extra = DRM_NODE_MAX * (sizeof(void *) + max_node_length);
+ psize = sizeof(drmDevice) + extra + sizeof(drmPciBusInfo) + sizeof(drmPciDeviceInfo);
+ devp = calloc(1, psize);
+ if (!devp)
+ return -ENOMEM;
+
+ devp->bustype = DRM_BUS_PCI;
+ /* Advertise both PRIMARY and RENDER — same path on Redox */
+ devp->available_nodes = (1 << DRM_NODE_PRIMARY) | (1 << DRM_NODE_RENDER);
+ pptr = (char *)devp + sizeof(drmDevice);
+ devp->nodes = (char **)pptr;
+ pptr += DRM_NODE_MAX * sizeof(void *);
+ for (i = 0; i < DRM_NODE_MAX; i++) { devp->nodes[i] = pptr; pptr += max_node_length; }
+ snprintf(devp->nodes[DRM_NODE_PRIMARY], max_node_length, "/scheme/drm/card0");
+ snprintf(devp->nodes[DRM_NODE_RENDER], max_node_length, "/scheme/drm/card0");
+
+ devp->businfo.pci = (drmPciBusInfoPtr)pptr;
+ pptr += sizeof(drmPciBusInfo);
+ devp->businfo.pci->domain = pbuf[10] | (pbuf[11] << 8) | (pbuf[12] << 16) | (pbuf[13] << 24);
+ devp->businfo.pci->bus = pbuf[14];
+ devp->businfo.pci->dev = pbuf[15];
+ devp->businfo.pci->func = pbuf[16];
+
+ devp->deviceinfo.pci = (drmPciDeviceInfoPtr)pptr;
+ devp->deviceinfo.pci->vendor_id = pbuf[0] | (pbuf[1] << 8);
+ devp->deviceinfo.pci->device_id = pbuf[2] | (pbuf[3] << 8);
+ devp->deviceinfo.pci->subvendor_id = pbuf[4] | (pbuf[5] << 8);
+ devp->deviceinfo.pci->subdevice_id = pbuf[6] | (pbuf[7] << 8);
+ devp->deviceinfo.pci->revision_id = pbuf[8];
+
+ *device = devp;
@@ -0,0 +1,25 @@
diff --git a/src/bin/mount.rs b/src/bin/mount.rs
index dba7f3c..70cf661 100644
--- a/src/bin/mount.rs
+++ b/src/bin/mount.rs
@@ -292 +292,19 @@ fn daemon(
- DiskId::Uuid(ref uuid) => filesystem_by_uuid(uuid, block_opt),
+ DiskId::Uuid(ref uuid) => {
+ let max_attempts = 20;
+ let mut attempt = 0;
+ loop {
+ attempt += 1;
+ if let Some(result) = filesystem_by_uuid(uuid, block_opt) {
+ break Some(result);
+ }
+ if attempt >= max_attempts {
+ log::error!("uuid search exhausted {} attempts", attempt);
+ break None;
+ }
+ #[cfg(target_os = "redox")]
+ std::thread::sleep(std::time::Duration::from_millis(200));
+ #[cfg(not(target_os = "redox"))]
+ std::thread::sleep(std::time::Duration::from_millis(200));
+ log::debug!("uuid search retry {}/{}", attempt, max_attempts);
+ }
+ }
+22
View File
@@ -0,0 +1,22 @@
diff --git a/src/bin/mount.rs b/src/bin/mount.rs
index dba7f3c..8f00a30 100644
--- a/src/bin/mount.rs
+++ b/src/bin/mount.rs
@@ -292 +292,16 @@ fn daemon(
- DiskId::Uuid(ref uuid) => filesystem_by_uuid(uuid, block_opt),
+ DiskId::Uuid(ref uuid) => {
+ let max_attempts = 20;
+ let mut attempt = 0;
+ loop {
+ attempt += 1;
+ if let Some(result) = filesystem_by_uuid(uuid, block_opt) {
+ break Some(result);
+ }
+ if attempt >= max_attempts {
+ log::error!("uuid search exhausted {} attempts", attempt);
+ break None;
+ }
+ std::thread::sleep(std::time::Duration::from_millis(200));
+ log::debug!("uuid search retry {}/{}", attempt, max_attempts);
+ }
+ }
@@ -0,0 +1,53 @@
diff --git a/src/bin/login.rs b/src/bin/login.rs
index 022fb47..6e1fda6 100644
--- a/src/bin/login.rs
+++ b/src/bin/login.rs
@@ -8,0 +9,3 @@ use std::str;
+#[cfg(target_os = "redox")]
+use redox_rt::sys::{posix_setresugid, Resugid};
+
@@ -38 +41 @@ const MOTD_FILE: &'static str = "/etc/motd";
-const DEFAULT_SCHEMES: [&'static str; 26] = [
+const DEFAULT_SCHEMES: [&'static str; 28] = [
@@ -65 +68,2 @@ const DEFAULT_SCHEMES: [&'static str; 26] = [
- // Display schemes
+ // Display schemes (DRM/KMS path for GPU drivers)
+ "drm",
@@ -67,0 +72,2 @@ const DEFAULT_SCHEMES: [&'static str; 26] = [
+ // Input schemes
+ "input",
@@ -92,0 +99,17 @@ pub fn apply_login_schemes(
+#[cfg(target_os = "redox")]
+fn drop_privileges(user: &User<redox_users::auth::Full>) -> Result<()> {
+ Ok(posix_setresugid(&Resugid {
+ ruid: Some(user.uid as u32),
+ euid: Some(user.uid as u32),
+ suid: Some(user.uid as u32),
+ rgid: Some(user.gid as u32),
+ egid: Some(user.gid as u32),
+ sgid: Some(user.gid as u32),
+ })?)
+}
+
+#[cfg(not(target_os = "redox"))]
+fn drop_privileges(_user: &User<redox_users::auth::Full>) -> Result<()> {
+ Ok(())
+}
+
@@ -177,0 +201 @@ pub fn main() {
+ drop_privileges(user).unwrap_or_exit(1);
@@ -200,0 +225,9 @@ pub fn main() {
+ let before_ns_fd =
+ apply_login_schemes(user, &DEFAULT_SCHEMES).unwrap_or_exit(1);
+
+ let _ = syscall::fcntl(
+ before_ns_fd.raw(),
+ syscall::F_SETFD,
+ syscall::O_CLOEXEC,
+ );
+ drop_privileges(user).unwrap_or_exit(1);
@@ -201,0 +235,4 @@ pub fn main() {
+ let _ = syscall::fcntl(before_ns_fd.raw(), syscall::F_SETFD, 0);
+ let _ = libredox::call::close(
+ libredox::call::setns(before_ns_fd.into_raw()).unwrap_or_exit(1),
+ );
@@ -0,0 +1,46 @@
diff --git a/src/bin/login.rs b/src/bin/login.rs
index 6e1fda6..e0878c9 100644
--- a/src/bin/login.rs
+++ b/src/bin/login.rs
@@ -193,2 +193,7 @@ pub fn main() {
- let before_ns_fd =
- apply_login_schemes(user, &DEFAULT_SCHEMES).unwrap_or_exit(1);
+ let before_ns_fd = match apply_login_schemes(user, &DEFAULT_SCHEMES) {
+ Ok(fd) => fd,
+ Err(err) => {
+ eprintln!("login: apply_login_schemes failed: {}", err);
+ std::process::exit(1);
+ }
+ };
@@ -201,2 +206,8 @@ pub fn main() {
- drop_privileges(user).unwrap_or_exit(1);
- spawn_shell(user).unwrap_or_exit(1);
+ if let Err(err) = drop_privileges(user) {
+ eprintln!("login: drop_privileges failed: {}", err);
+ std::process::exit(1);
+ }
+ if let Err(err) = spawn_shell(user) {
+ eprintln!("login: spawn_shell failed: {}", err);
+ std::process::exit(1);
+ }
@@ -225,2 +236,7 @@ pub fn main() {
- let before_ns_fd =
- apply_login_schemes(user, &DEFAULT_SCHEMES).unwrap_or_exit(1);
+ let before_ns_fd = match apply_login_schemes(user, &DEFAULT_SCHEMES) {
+ Ok(fd) => fd,
+ Err(err) => {
+ eprintln!("login: apply_login_schemes failed: {}", err);
+ std::process::exit(1);
+ }
+ };
@@ -233,2 +249,8 @@ pub fn main() {
- drop_privileges(user).unwrap_or_exit(1);
- spawn_shell(user).unwrap_or_exit(1);
+ if let Err(err) = drop_privileges(user) {
+ eprintln!("login: drop_privileges failed: {}", err);
+ std::process::exit(1);
+ }
+ if let Err(err) = spawn_shell(user) {
+ eprintln!("login: spawn_shell failed: {}", err);
+ std::process::exit(1);
+ }
@@ -0,0 +1,7 @@
diff --git a/Cargo.toml b/Cargo.toml
index abfc7ae..a05f4fd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -73 +73 @@ ioslice = "0.6"
-redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false }
+redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false, features = ["proc"] }
+249
View File
@@ -0,0 +1,249 @@
# Red Bear OS Custom Recipes — Catalog
All recipes under `local/recipes/` are Red Bear OS originals or patched forks of upstream
Redox recipes. They are symlinked into `recipes/<category>/` at build time via
`local/scripts/apply-patches.sh`.
**Convention**: recipe directories contain `recipe.toml` (build instructions) and optionally
`source/` (source code for Rust `cargo` or `custom` templates). Patches for upstream sources
live in `local/patches/<component>/`, never here.
## archives
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| uutils-tar | cargo | Rust | GNU tar compatible archiver from uutils, for creating and extracting tar archives |
## branding
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| redbear-release | custom | Shell | OS release metadata: `/etc/os-release`, hostname, and branding assets |
## core
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| ext4d | custom | Rust | ext4 filesystem driver daemon (userspace, scheme-based) |
| fatd | custom | Rust | FAT32 filesystem driver daemon (userspace, scheme-based) |
| grub | custom | Scripts | GRUB boot manager integration for live ISO and bare-metal installs |
| pcid-spawner | cargo | Rust | PCI device spawner — launches driver daemons on PCI device discovery |
## dev
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| binutils-native | custom | C | GNU binutils (ld, objdump, etc.) built for native Red Bear target |
| bison | custom | C | GNU bison parser generator |
| cub | custom | Rust | Red Bear build utility — build orchestration helper |
| flex | custom | C | Fast lexical analyzer generator |
| gcc-native | custom | C | GCC cross-compiler for native Red Bear target |
| gnu-make | custom | C | GNU Make build tool |
| libtool | custom | C | GNU libtool — generic library support script |
| llvm-native | custom | C++ | LLVM/Clang toolchain built for native Red Bear target |
| m4 | custom | C | GNU m4 macro processor |
| meson | custom | Python | Meson build system |
| ninja-build | custom | C | Ninja build system (small, fast build runner) |
| rust-native | custom | Rust | Rust toolchain for native Red Bear builds |
## drivers
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| ehcid | cargo | Rust | EHCI (USB 2.0) host controller driver daemon |
| linux-kpi | custom | C headers | Linux Kernel Programming Interface — C header shim translating Linux kernel APIs to redox-driver-sys. **GPU and Wi-Fi only** — no USB support |
| ohcid | cargo | Rust | OHCI (USB 1.1) host controller driver daemon |
| redbear-btusb | custom | Rust | Bluetooth USB transport driver — sends/receives HCI packets over USB |
| redbear-input-headers | custom | C headers | Linux-compatible input event headers (input.h, evdev constants) for driver and compositor use |
| redbear-iwlwifi | cargo | Rust | Intel Wi-Fi driver (iwlwifi port) — mac80211-based wireless networking |
| redox-driver-acpi | cargo | Rust | ACPI driver — parses RSDP/SDT/MADT/FADT tables, exposes scheme:acpi |
| redox-driver-core | cargo | Rust | Core driver traits and types shared across all redox-driver-* crates |
| redox-driver-pci | cargo | Rust | PCI bus driver — enumerates PCI devices, exposes scheme:pci, provides config space access |
| redox-driver-sys | custom | Rust | Safe Rust FFI wrappers for scheme:memory, scheme:irq, scheme:pci + hardware quirks system |
| uhcid | cargo | Rust | UHCI (USB 1.1) host controller driver daemon |
| usb-core | cargo | Rust | USB core stack — hub driver, device enumeration, transfer scheduling |
## gpu
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| amdgpu | custom | C | AMD GPU display core (DC) port with linux-kpi compat shim |
| redox-drm | cargo | Rust | DRM/KMS scheme daemon — GPU driver manager, supports virtio-gpu, Intel, AMD. Auto-detects hardware and loads correct driver |
## groups
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| build-essential-native | custom | Meta | Meta-package group: compiler, linker, make, and core build tools for native development |
## kde
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| breeze | custom | C++ | KDE Breeze widget style and window decoration theme |
| kde-cli-tools | custom | C++ | KDE command-line utilities (kde-open, kioclient, etc.) |
| kdecoration | custom | C++ | KDE window decoration library — decoration plugin API for KWin |
| kf6-attica | custom | C++ | KDE Frameworks 6 — Open Collaboration Services API (GHNS) |
| kf6-extra-cmake-modules | custom | CMake | KDE Frameworks 6 — Extra CMake Modules (build system extensions) |
| kf6-karchive | custom | C++ | KDE Frameworks 6 — archive handling (tar, zip, etc.) |
| kf6-kauth | custom | C++ | KDE Frameworks 6 — authorization framework (PolicyKit integration) |
| kf6-kbookmarks | custom | C++ | KDE Frameworks 6 — bookmark management (XBEL format) |
| kf6-kcmutils | custom | C++ | KDE Frameworks 6 — KCModule utilities for System Settings |
| kf6-kcodecs | custom | C++ | KDE Frameworks 6 — string encoding/decoding (base64, uuencode, etc.) |
| kf6-kcolorscheme | custom | C++ | KDE Frameworks 6 — color scheme management |
| kf6-kcompletion | custom | C++ | KDE Frameworks 6 — text completion widgets and utilities |
| kf6-kconfig | custom | C++ | KDE Frameworks 6 — configuration file framework (INI, JSON) |
| kf6-kconfigwidgets | custom | C++ | KDE Frameworks 6 — configuration-aware widgets |
| kf6-kcoreaddons | custom | C++ | KDE Frameworks 6 — core utilities (KAboutData, KJob, KProcess) |
| kf6-kcrash | custom | C++ | KDE Frameworks 6 — crash handler with DrKonqi integration |
| kf6-kdbusaddons | custom | C++ | KDE Frameworks 6 — D-Bus convenience classes |
| kf6-kdeclarative | custom | C++ | KDE Frameworks 6 — KDE QtQuick integration plugins |
| kf6-kded6 | custom | C++ | KDE Frameworks 6 — background service daemon (kded6) |
| kf6-kglobalaccel | custom | C++ | KDE Frameworks 6 — global keyboard shortcut registration |
| kf6-kguiaddons | custom | C++ | KDE Frameworks 6 — GUI utilities (color picker, key sequence) |
| kf6-ki18n | custom | C++ | KDE Frameworks 6 — internationalization (gettext integration) |
| kf6-kiconthemes | custom | C++ | KDE Frameworks 6 — icon theme management and rendering |
| kf6-kidletime | custom | C++ | KDE Frameworks 6 — idle time detection for screensaver/power |
| kf6-kio | custom | C++ | KDE Frameworks 6 — I/O framework (KIO slaves for network/FS access) |
| kf6-kitemmodels | custom | C++ | KDE Frameworks 6 — Qt model extensions (KRearrangeColumns, KSortFilter) |
| kf6-kitemviews | custom | C++ | KDE Frameworks 6 — item view widgets (KFilterProxy, KCategoryDrawer) |
| kf6-kjobwidgets | custom | C++ | KDE Frameworks 6 — async job tracking widgets |
| kf6-knewstuff | custom | C++ | KDE Frameworks 6 — Get Hot New Stuff (GHNS) download framework |
| kf6-knotifications | custom | C++ | KDE Frameworks 6 — system notification framework |
| kf6-kpackage | custom | C++ | KDE Frameworks 6 — package/installation framework |
| kf6-kservice | custom | C++ | KDE Frameworks 6 — service/plugin framework (mime type, .desktop parsing) |
| kf6-ksvg | custom | C++ | KDE Frameworks 6 — SVG rendering with theme support |
| kf6-ktextwidgets | custom | C++ | KDE Frameworks 6 — text editing widgets (KTextEdit, find/replace) |
| kf6-kwallet | custom | C++ | KDE Frameworks 6 — secure credential storage |
| kf6-kwayland | custom | C++ | KDE Frameworks 6 — Wayland protocol bindings for Qt/KDE |
| kf6-kwidgetsaddons | custom | C++ | KDE Frameworks 6 — extra Qt widgets (KComboBox, KPageWidget) |
| kf6-kwindowsystem | custom | C++ | KDE Frameworks 6 — window system integration (window info, stacking) |
| kf6-kxmlgui | custom | C++ | KDE Frameworks 6 — XML-defined GUI (menus, toolbars, actions) |
| kf6-notifyconfig | custom | C++ | KDE Frameworks 6 — notification configuration widgets |
| kf6-parts | custom | C++ | KDE Frameworks 6 — embeddable document/viewer parts (KParts) |
| kf6-prison | custom | C++ | KDE Frameworks 6 — barcode/QR code generation (QRencode wrapper) |
| kf6-pty | custom | C++ | KDE Frameworks 6 — pseudoterminal (PTY) management |
| kf6-solid | custom | C++ | KDE Frameworks 6 — hardware abstraction (storage, power, network) |
| kf6-sonnet | custom | C++ | KDE Frameworks 6 — spell checking framework |
| kf6-syntaxhighlighting | custom | C++ | KDE Frameworks 6 — syntax highlighting engine (Kate definitions) |
| kglobalacceld | custom | C++ | KDE global shortcut daemon — handles keyboard shortcut registration |
| kirigami | custom | C++ | KDE Kirigami — responsive QtQuick UI framework (convergent apps) |
| konsole | custom | C++ | KDE Konsole — terminal emulator |
| kwin | custom | C++ | KDE KWin — Wayland compositor and window manager |
| plasma-desktop | custom | C++ | KDE Plasma Desktop — panels, desktop containment, applets |
| plasma-framework | custom | C++ | KDE Plasma Framework — libplasma for Plasma shell and applets |
| plasma-wayland-protocols | custom | C++ | KDE Plasma Wayland protocol extensions |
| plasma-workspace | custom | C++ | KDE Plasma Workspace — plasma-shell, data engines, runners |
## libs
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| freetype2 | custom | C | FreeType 2 font rendering library |
| glib | custom | C | GLib — core event loop, type system, utility functions |
| icu | custom | C++ | ICU — Unicode and internationalization support |
| lcms2 | custom | C | Little CMS 2 — color management library |
| lcms2-stub | custom | C | Minimal lcms2 stub — provides symbols without implementation |
| libdisplay-info | custom | C | EDID and display descriptor parsing library |
| libdisplay-info-stub | custom | C | Minimal libdisplay-info stub for compile-time satisfaction |
| libdrm | meson | C | libdrm — DRM/KMS user-space library with Red Bear patches for render node and virtio-gpu support |
| libepoxy | custom | C | Epoxy — OpenGL function pointer manager (cross-platform GL loader) |
| libepoxy-stub | custom | C | Minimal libepoxy stub — provides symbols without implementation |
| libevdev | meson | C | libevdev — evdev device wrapper library for input handling |
| libinput | meson | C | libinput — input device management (keyboard, pointer, touch) |
| libqrencode | cmake | C | libqrencode — QR code encoding library |
| libudev-stub | custom | C | Minimal libudev stub — provides symbols for udev-dependent builds |
| libxcvt | custom | C | libxcvt — VESA CVT mode timing calculation |
| libxcvt-stub | custom | C | Minimal libxcvt stub — provides symbols without implementation |
| zbus | custom | Rust | zbus crate — Rust D-Bus message bus library (library-only, custom build) |
## qt
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| qt6-sensors | custom | C++ | Qt 6 Sensors module — hardware sensor access (accel, gyro, etc.) |
| qtbase | custom | C++ | Qt 6 Base — core, gui, widgets, network modules |
| qtdeclarative | custom | C++ | Qt 6 QML/QtQuick — declarative UI framework |
| qtsvg | custom | C++ | Qt 6 SVG — SVG rendering support for Qt |
| qtwayland | custom | C++ | Qt 6 Wayland — Wayland compositor/client integration |
## system
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| coretempd | cargo | Rust | CPU core temperature monitoring daemon |
| cpufreqd | cargo | Rust | CPU frequency scaling daemon |
| cub | custom | Rust | Red Bear build utility (same as dev/cub, system-installed copy) |
| dbus | meson | C | D-Bus reference implementation — system and session message bus |
| driver-manager | custom | Rust | Driver lifecycle manager — loads/unloads drivers based on hardware |
| driver-params | cargo | Rust | Driver parameter service — exposes driver configuration via scheme |
| evdevd | custom | Rust | evdev event daemon — translates input events to evdev protocol |
| firmware-loader | cargo | Rust | Firmware loading daemon — serves GPU/device firmware blobs via scheme:firmware |
| hwrngd | cargo | Rust | Hardware RNG daemon — exposes entropy from hardware random number generator |
| iommu | cargo | Rust | IOMMU daemon — manages I/O memory mapping for device DMA |
| numad | cargo | Rust | NUMA topology daemon — exposes NUMA node information |
| redbear-accessibility | custom | Rust | Accessibility service — screen reader and input assistance bridge |
| redbear-acmd | cargo | Rust | Red Bear admin CLI — system administration commands |
| redbear-authd | cargo | Rust | Authentication daemon — PAM-like auth with scheme:auth |
| redbear-btctl | custom | Rust | Bluetooth control utility — scan, pair, connect Bluetooth devices |
| redbear-dbus-services | custom | Config | D-Bus service activation files and XML policy files for system/session buses |
| redbear-ecmd | cargo | Rust | Extended command-line tool — system diagnostics and info |
| redbear-firmware | custom | Scripts | Firmware management utility — list, extract, verify firmware blobs |
| redbear-greeter | custom | Rust | Login greeter daemon — displays graphical login prompt |
| redbear-hwutils | cargo | Rust | Hardware utility library and CLI — PCI, USB, sensor information |
| redbear-ime | custom | Rust | Input method engine — multilingual text input framework |
| redbear-info | cargo | Rust | System information daemon — hardware and OS state queries |
| redbear-keymapd | custom | Rust | Keyboard layout daemon — manages keymaps and input layouts |
| redbear-login-protocol | cargo | Rust | Login protocol crate — shared types for auth session management |
| redbear-meta | custom | Meta | Meta-package — ensures core Red Bear system packages are installed |
| redbear-mtr | cargo | Rust | MTR network diagnostic tool — traceroute + ping combined |
| redbear-netctl | cargo | Rust | Network control daemon — manages network interfaces and connections |
| redbear-netctl-console | cargo | Rust | Network control console UI — TUI for redbear-netctl |
| redbear-netstat | cargo | Rust | Network statistics tool — socket, interface, and routing info |
| redbear-nmap | cargo | Rust | Network mapper — port scanning and host discovery |
| redbear-notifications | cargo | Rust | Notification daemon — freedesktop.org notification spec implementation |
| redbear-passwd | cargo | Rust | Password management utility — change passwords, manage shadow |
| redbear-polkit | cargo | Rust | PolicyKit daemon — authorization framework for privileged operations |
| redbear-quirks | custom | Config | Hardware quirks database — TOML quirk definitions for known issues |
| redbear-sessiond | cargo | Rust | Session manager daemon — D-Bus login1 subset for KWin/Wayland |
| redbear-session-launch | cargo | Rust | Session launcher — starts desktop session compositor and services |
| redbear-statusnotifierwatcher | cargo | Rust | Status Notifier Watcher — freedesktop.org system tray spec |
| redbear-traceroute | cargo | Rust | Traceroute utility — network path discovery |
| redbear-udisks | cargo | Rust | UDisks2 daemon — storage device management via D-Bus |
| redbear-upower | cargo | Rust | UPower daemon — power management and battery status via D-Bus |
| redbear-usbaudiod | cargo | Rust | USB audio daemon — USB audio class device driver |
| redbear-wayland-guard | custom | Rust | Wayland security guard — validates compositor/client permissions |
| redbear-wifictl | cargo | Rust | Wi-Fi control utility — scan, connect, manage wireless networks |
| seatd | meson | C | seatd — seat management daemon for DRM master access |
| thermald | cargo | Rust | Thermal management daemon — monitors and controls CPU temperature |
| udev-shim | cargo | Rust | udev compatibility shim — translates libudev calls to Red Bear schemes |
## tests
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| redox-drm-prime-test | custom | Rust | DRM PRIME buffer sharing test — validates GPU buffer import/export |
| relibc-phase1-tests | custom | C | relibc POSIX compliance tests — validates syscall wrappers and C library functions |
## tools
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| diffutils | custom | C | GNU diffutils — diff, diff3, sdiff, cmp |
## tui
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| mc | custom | C | GNU Midnight Commander — file manager with TUI |
## wayland
| Recipe | Template | Language | Description |
|--------|----------|----------|-------------|
| libwayland | custom | C | libwayland — Wayland protocol client/server library |
| qt6-wayland-smoke | custom | C++ | Qt 6 Wayland smoke test — minimal QML window on Wayland |
| redbear-compositor | cargo | Rust | Red Bear Wayland compositor — compositor shell for DRM/KMS output |
| seatd-redox | meson | C | seatd Redox backend — seat management for Red Bear's scheme system |
| smallvil | cargo | C | Smallvil — minimal wlroots-based Wayland compositor for testing |
| wayland-protocols | custom | C | Wayland protocol extensions — stable and staging protocol XML files |
@@ -1,12 +1,10 @@
_ _
| | (_)
| | ___ _ ___ _ __ _ _ ___
| |/ / || |/ _ \ | '_ \| | | / __|
| < | || | (_) || |_) | |_| \__ \
|_|\_\|_|/ |\___/ | .__/ \__,_|___/
|__/ | |
|_|
Red Bear OS v0.2.0 "Liliya" — Built on Redox OS
____ _ ____ ___ ____
| _ \ ___ __| | __ ) ___ __ _ _ __ / _ \/ ___|
| |_) / _ \ / _` | _ \ / _ \/ _` | '__| | | | \___ \
| _ < __/ (_| | |_) | __/ (_| | | | |_| |___) |
|_| \_\___|\__,_|____/ \___|\__,_|_| \___/|____/
v0.2.2 "Liliya" — Built on Redox OS
Type 'help' for available commands.
@@ -1,7 +1,7 @@
PRETTY_NAME="Red Bear OS 0.2.0 (Liliya)"
PRETTY_NAME="Red Bear OS 0.2.2 (Liliya)"
NAME="Red Bear OS"
VERSION_ID="0.2.0"
VERSION="0.2.0 (Liliya)"
VERSION_ID="0.2.2"
VERSION="0.2.2 (Liliya)"
VERSION_CODENAME="liliya"
ID="redbear-os"
ID_LIKE="redox-os"
@@ -0,0 +1,21 @@
diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs
--- a/src/drivers/mod.rs
+++ b/src/drivers/mod.rs
@@ -27 +27 @@ const INTEL_GEN12_MTL_IDS: &[u16] = &[
- 0x7D40, 0x7D41, 0x7D45, 0x7D51, 0x7D55, 0x7D60, 0x7D67, 0x7DD1, 0x7DD5,
+ 0x7D40, 0x7D45, 0x7D55, 0x7D60, 0x7DD5,
@@ -29,2 +29,2 @@ const INTEL_GEN12_MTL_IDS: &[u16] = &[
-const INTEL_GEN12_ARL_IDS: &[u16] = &[0x6420, 0x64A0, 0x64B0];
-const INTEL_GEN12_LNL_IDS: &[u16] = &[0xB640];
+const INTEL_GEN12_ARL_IDS: &[u16] = &[0x7D41, 0x7D51, 0x7DD1, 0x7D67, 0xB640];
+const INTEL_GEN12_LNL_IDS: &[u16] = &[0x6420, 0x64A0, 0x64B0];
diff --git a/src/main.rs b/src/main.rs
--- a/src/main.rs
+++ b/src/main.rs
@@ -297 +297,5 @@ fn intel_display_firmware_keys(device_id: u16) -> Option<&'static [&'static str]>
- 0x7D40 | 0x7D41 | 0x7D45 | 0x7D51 | 0x7D55 | 0x7D60 | 0x7D67 | 0x7DD1 | 0x7DD5 => {
+ 0x7D40 | 0x7D45 | 0x7D55 | 0x7D60 | 0x7DD5 => {
+ Some(INTEL_MTL_DMC_KEYS)
+ }
+ // Arrow Lake (ARL) — display IP 14.0, same DMC as Meteor Lake
+ 0x7D41 | 0x7D51 | 0x7DD1 | 0x7D67 | 0xB640 => {
+1 -1
View File
@@ -1,6 +1,6 @@
[source]
path = "source"
patches = ["P1-intel-gen-gate.patch", "P2-intel-display-fixes.patch", "P3-intel-gen8-gen9-firmware.patch", "P4-virtio-gpu-driver.patch", "P5-virtio-auto-probe.patch", "P6-pcid-coordinate-handoff.patch", "P7-unreachable-pattern-cleanup.patch", "P8-terminal-scheme-ebadf.patch", "P9-virtio-handoff-mmio-map.patch"]
patches = ["P1-intel-gen-gate.patch", "P2-intel-display-fixes.patch", "P3-intel-gen8-gen9-firmware.patch", "P4-virtio-gpu-driver.patch", "P5-virtio-auto-probe.patch", "P6-pcid-coordinate-handoff.patch", "P7-unreachable-pattern-cleanup.patch", "P8-terminal-scheme-ebadf.patch", "P9-virtio-handoff-mmio-map.patch", "P10-arrow-lake-device-ids.patch"]
[build]
template = "cargo"
@@ -138,36 +138,6 @@ impl VirtioDriver {
};
let (connectors, crtcs) = load_display_topology(&mut device)?;
// Probe 1: resource_unref for non-existent ID — tests VirtQueue mechanism
match device.resource_unref(1) {
Ok(()) => info!("redox-drm: VirtIO diag resource_unref(1) returned OK (unexpected — ID 1 should not exist)"),
Err(e) => info!("redox-drm: VirtIO diag resource_unref(1) returned {:?}", e),
}
// Probe 2: invalid format=0 — if QEMU returns ERR_INVALID_PARAMETER it reads the full
// command; if ERR_INVALID_RESOURCE_ID it's only seeing the header or resource_id field.
{
let probe = VirtioGpuResourceCreate2d {
hdr: VirtioGpuCtrlHeader::command(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D),
resource_id: 99,
format: 0,
width: 64,
height: 64,
};
let probe_bytes = bytes_of(&probe);
info!("redox-drm: VirtIO diag probe: resource_create_2d(rid=99, fmt=0, w=64, h=64)");
match device.submit_request(probe_bytes, core::mem::size_of::<VirtioGpuCtrlHeader>()) {
Ok(resp) => {
let hdr = read_struct::<VirtioGpuCtrlHeader>(&resp)?;
info!(
"redox-drm: VirtIO diag probe response: type={:#06x} ({})",
hdr.type_, response_type_name(hdr.type_)
);
}
Err(e) => info!("redox-drm: VirtIO diag probe submit_request error: {:?}", e),
}
}
info!(
"redox-drm: VirtIO GPU ready for {} with {} connector(s), {} CRTC(s), EDID={} VIRGL={} IRQ mode {}",
info.location,
@@ -895,59 +865,11 @@ impl VirtioGpuDevice {
})?;
let request_ptr = dma.as_mut_ptr();
// SAFETY: `dma` is at least `total` bytes, and `request`/response ranges do not overlap.
unsafe {
core::ptr::copy_nonoverlapping(request.as_ptr(), request_ptr, request.len());
core::ptr::write_bytes(request_ptr.add(request.len()), 0, response_len);
}
let mut verify_mismatch = false;
unsafe {
for (i, &expected) in request.iter().enumerate().take(8) {
if *request_ptr.add(i) != expected {
verify_mismatch = true;
break;
}
}
}
if verify_mismatch {
let readback: Vec<u8> = unsafe { core::slice::from_raw_parts(request_ptr, request.len().min(40)) }.to_vec();
warn!(
"redox-drm: VirtIO DMA VERIFY MISMATCH! written={:02x?} readback={:02x?}",
&request[..request.len().min(40)],
readback
);
}
info!(
"redox-drm: VirtIO submit_request dma_phys={:#x} req_len={} resp_addr={:#x} resp_len={}",
dma.physical_address(),
request.len(),
dma.physical_address() + request.len(),
response_len
);
// Full DMA buffer hex dump BEFORE push_avail — confirms physical memory contains
// correct bytes that QEMU should read. Dumps request area + first 8 bytes of
// response area (should be zeroed).
{
let full_len = total.min(128);
let dma_slice: &[u8] = unsafe { core::slice::from_raw_parts(request_ptr, full_len) };
info!(
"redox-drm: VirtIO DMA BUFFER BEFORE SUBMIT ({} bytes): {:02x?}",
full_len, dma_slice
);
// Also dump the raw descriptor that QEMU will see — addr, len, flags, next
// desc[0] = request (F_NEXT), desc[1] = response (F_WRITE)
info!(
"redox-drm: VirtIO DESC[0] addr={:#x} len={} flags=F_NEXT next=1 | DESC[1] addr={:#x} len={} flags=F_WRITE next=0",
dma.physical_address(),
request.len(),
dma.physical_address() + request.len(),
response_len,
);
}
self.ctrlq.submit_request(
&self.transport,
dma.physical_address() as u64,
@@ -957,15 +879,6 @@ impl VirtioGpuDevice {
COMMAND_TIMEOUT,
)?;
{
let full_len = total.min(128);
let dma_slice: &[u8] = unsafe { core::slice::from_raw_parts(request_ptr, full_len) };
info!(
"redox-drm: VirtIO DMA BUFFER AFTER RESPONSE ({} bytes): {:02x?}",
full_len, dma_slice
);
}
let mut response = vec![0u8; response_len];
unsafe {
core::ptr::copy_nonoverlapping(
@@ -976,14 +889,6 @@ impl VirtioGpuDevice {
}
if response_len >= core::mem::size_of::<VirtioGpuCtrlHeader>() {
let hdr = unsafe { (response.as_ptr() as *const VirtioGpuCtrlHeader).read_unaligned() };
info!(
"redox-drm: VirtIO submit_request response type={:#06x} ({}) flags={:#x} fence_id={} ctx_id={}",
hdr.type_,
response_type_name(hdr.type_),
hdr.flags,
hdr.fence_id,
hdr.ctx_id,
);
if hdr.type_ != VIRTIO_GPU_RESP_OK_NODATA
&& hdr.type_ != VIRTIO_GPU_RESP_OK_DISPLAY_INFO
&& hdr.type_ != VIRTIO_GPU_RESP_OK_EDID
@@ -992,7 +897,9 @@ impl VirtioGpuDevice {
&& hdr.type_ != VIRTIO_GPU_RESP_OK_MAP_INFO
{
warn!(
"redox-drm: VirtIO unexpected response header bytes: {:02x?}",
"redox-drm: VirtIO unexpected response type={:#06x} ({}) header: {:02x?}",
hdr.type_,
response_type_name(hdr.type_),
&response[..response_len.min(24)]
);
}
@@ -1047,23 +954,8 @@ impl VirtioGpuDevice {
height,
};
let request_bytes = bytes_of(&request);
info!(
"redox-drm: VirtIO resource_create_2d rid={} fmt={} w={} h={} cmd_bytes={:02x?}",
resource_id,
VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM,
width,
height,
&request_bytes[..request_bytes.len().min(48)]
);
let response = self.submit_request(request_bytes, core::mem::size_of::<VirtioGpuCtrlHeader>())?;
let response = self.submit_request(bytes_of(&request), core::mem::size_of::<VirtioGpuCtrlHeader>())?;
let header = read_struct::<VirtioGpuCtrlHeader>(&response)?;
info!(
"redox-drm: VirtIO resource_create_2d response type={:#06x} ({})",
header.type_,
response_type_name(header.type_)
);
validate_response_type(&header, VIRTIO_GPU_RESP_OK_NODATA)
}
@@ -1102,10 +994,7 @@ impl VirtioGpuDevice {
resource_id,
padding: 0,
};
info!("redox-drm: VirtIO resource_unref rid={} cmd_bytes={:02x?}", resource_id, bytes_of(&request));
let result = self.submit_nodata(&request);
info!("redox-drm: VirtIO resource_unref rid={} result={:?}", resource_id, &result);
result
self.submit_nodata(&request)
}
fn set_scanout(
@@ -3,7 +3,6 @@ use std::sync::atomic::{fence, Ordering};
use std::thread;
use std::time::{Duration, Instant};
use log::info;
use redox_driver_sys::dma::DmaBuffer;
use crate::driver::{DriverError, Result};
@@ -39,7 +38,6 @@ pub struct Virtqueue {
free_list: Vec<u16>,
pending: BTreeMap<u16, Vec<u16>>,
last_used_idx: u16,
failed: bool,
}
impl Virtqueue {
@@ -65,7 +63,6 @@ impl Virtqueue {
free_list: (0..size).rev().collect(),
pending: BTreeMap::new(),
last_used_idx: 0,
failed: false,
})
}
@@ -102,13 +99,6 @@ impl Virtqueue {
response_len: u32,
timeout: Duration,
) -> Result<()> {
if self.failed {
return Err(DriverError::Io(format!(
"VirtIO queue {} is failed and cannot accept more requests",
self.index
)));
}
let head = self.alloc_desc()?;
let tail = self.alloc_desc()?;
self.write_desc(
@@ -132,26 +122,6 @@ impl Virtqueue {
self.pending.insert(head, vec![head, tail]);
{
let desc_ptr = self.desc.as_ptr() as *const u8;
let desc0_bytes: &[u8] = unsafe {
core::slice::from_raw_parts(desc_ptr.add((head as usize) * 16), 16)
};
let desc1_bytes: &[u8] = unsafe {
core::slice::from_raw_parts(desc_ptr.add((tail as usize) * 16), 16)
};
let avail_ptr = self.avail.as_ptr();
let avail_idx_val = unsafe { (avail_ptr.add(2) as *const u16).read_unaligned() };
let used_idx_val = {
let used_ptr = self.used.as_ptr();
unsafe { (used_ptr.add(2) as *const u16).read_unaligned() }
};
info!(
"redox-drm: VirtQueue desc[{}]={:02x?} desc[{}]={:02x?} avail_idx={} used_idx={} last_used={}",
head, desc0_bytes, tail, desc1_bytes, avail_idx_val, used_idx_val, self.last_used_idx
);
}
fence(Ordering::SeqCst);
self.push_avail(head);
@@ -161,7 +131,6 @@ impl Virtqueue {
}
if let Err(error) = self.wait_used(head, timeout) {
self.failed = true;
return Err(error);
}
@@ -188,6 +157,10 @@ impl Virtqueue {
fn wait_used(&mut self, expected_head: u16, timeout: Duration) -> Result<()> {
let deadline = Instant::now() + timeout;
loop {
// Ensure we see QEMU's DMA writes to the used ring.
// Without this fence, the CPU may read a stale used_idx from
// its cache while QEMU has already written the completion via DMA.
fence(Ordering::SeqCst);
let used_idx = self.read_used_idx();
if used_idx != self.last_used_idx {
let slot = usize::from(self.last_used_idx % self.size);
@@ -195,7 +168,8 @@ impl Virtqueue {
self.last_used_idx = self.last_used_idx.wrapping_add(1);
self.free_chain(elem.id as u16)?;
if elem.id as u16 != expected_head {
self.failed = true;
// Out-of-order completion — log but don't permanently fail.
// The descriptor chain has been freed; the caller will retry or propagate.
return Err(DriverError::Io(format!(
"VirtIO queue {} completed descriptor head {} while waiting for {}",
self.index, elem.id, expected_head
@@ -205,6 +179,10 @@ impl Virtqueue {
}
if Instant::now() >= deadline {
// Timeout: reclaim the pending chain so the queue stays usable.
// Do NOT set self.failed — a single timeout should not permanently
// disable the entire queue.
self.reclaim_pending_chain(expected_head);
return Err(DriverError::Io(format!(
"VirtIO queue {} timed out waiting for descriptor head {}",
self.index, expected_head
+10 -2
View File
@@ -125,6 +125,8 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
daemon.ready();
let mut handler = ReadinessBased::new(&socket, 16);
let mut scheme_err_logged = false;
let mut response_err_logged = false;
loop {
match handler.read_requests() {
Ok(true) => {}
@@ -133,7 +135,10 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
break;
}
Err(e) => {
error!("redox-drm: failed to receive scheme request: {}", e);
if !scheme_err_logged {
error!("redox-drm: failed to receive scheme request: {} (suppressing further errors)", e);
scheme_err_logged = true;
}
continue;
}
}
@@ -141,7 +146,10 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
handler.process_requests(|| drm_scheme.lock().expect("DRM scheme state poisoned"));
if let Err(e) = handler.write_responses() {
error!("redox-drm: failed to write scheme responses: {}", e);
if !response_err_logged {
error!("redox-drm: failed to write scheme responses: {} (suppressing further errors)", e);
response_err_logged = true;
}
}
}
@@ -57,7 +57,7 @@ add_subdirectory(src)
# Enable unit testing
if (BUILD_TESTING)
############################################################ add_subdirectory(autotests)
################################################################## add_subdirectory(autotests)
add_subdirectory(tests)
endif ()
@@ -19,6 +19,7 @@ cmake "${COOKBOOK_SOURCE}" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
-DBUILD_TESTING=OFF \
-DBUILD_DOC=OFF \
-DBUILD_QCH=OFF \
-Wno-dev
@@ -144,6 +144,12 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
# shall we use DBus?
# enabled per default on Linux & BSD systems
@@ -122,6 +122,12 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
@@ -113,6 +113,12 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(KF6Codecs ${KF_DEP_VERSION} REQUIRED)
find_package(KF6Config ${KF_DEP_VERSION} REQUIRED)
@@ -116,6 +116,12 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
# shall we use DBus?
# enabled per default on Linux & BSD systems
@@ -32,7 +32,7 @@ find_package(KF6GuiAddons ${KF_DEP_VERSION} REQUIRED)
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT REDOX)
################################################################################### find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED)
######################################################################################### find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED)
set(HAVE_KGLOBALACCEL TRUE)
else()
set(HAVE_KGLOBALACCEL FALSE)
@@ -136,6 +136,12 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6Svg ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
# shall we use DBus?
@@ -38,7 +38,7 @@ set_package_properties(Qt6Qml PROPERTIES
)
if (TARGET Qt6::Qml)
############################################################## include(ECMQmlModule)
#################################################################### include(ECMQmlModule)
endif()
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
@@ -1,6 +1,6 @@
add_subdirectory(core)
if (TARGET Qt6::Qml)
############################################################# add_subdirectory(qml)
################################################################### add_subdirectory(qml)
endif()
ecm_qt_install_logging_categories(
@@ -102,6 +102,12 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
@@ -102,6 +102,12 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT HAIKU)
option(WITH_X11 "Build with support for QX11Info::appUserTime()" ON)
@@ -117,6 +117,12 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
if (WITH_TEXT_TO_SPEECH)
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED TextToSpeech)
@@ -122,6 +122,12 @@ find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
set_package_properties(Wayland PROPERTIES
TYPE REQUIRED
)
@@ -74,10 +74,10 @@ void initializeLanguages()
// Ideally setting the LANGUAGE would change the default QLocale too
// but unfortunately this is too late since the QCoreApplication constructor
// already created a QLocale at this stage so we need to set the reset it
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale
// this is highly dependent on Qt internals, so may break, but oh well
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy;
}
}
@@ -65,9 +65,9 @@ ecm_set_disabled_deprecation_versions(
)
add_subdirectory( src )
#################################if (BUILD_TESTING)
################################# add_subdirectory( autotests )
#################################endif()
#######################################if (BUILD_TESTING)
####################################### add_subdirectory( autotests )
#######################################endif()
if (BUILD_QCH)
ecm_install_qch_export(
@@ -78,7 +78,7 @@ set_package_properties(PList PROPERTIES
if (CMAKE_SYSTEM_NAME MATCHES Linux)
# Used by the UDisks backend on Linux
####################################################################################################find_package(LibMount)
##########################################################################################################find_package(LibMount)
set_package_properties(LibMount PROPERTIES
TYPE REQUIRED)
endif()
@@ -124,6 +124,14 @@
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include "config-kwin.h"
@@ -110,34 +110,12 @@ static bool activate(const QString &sessionPath)
std::unique_ptr<ConsoleKitSession> ConsoleKitSession::create()
{
if (!QDBusConnection::systemBus().interface()->isServiceRegistered(s_serviceName)) {
return nullptr;
}
const QString sessionPath = findProcessSessionPath();
if (sessionPath.isEmpty()) {
qCWarning(KWIN_CORE) << "Could not determine the active graphical session";
return nullptr;
}
if (!activate(sessionPath)) {
qCWarning(KWIN_CORE, "Failed to activate %s session. Maybe another compositor is running?",
qPrintable(sessionPath));
return nullptr;
}
if (!takeControl(sessionPath)) {
qCWarning(KWIN_CORE, "Failed to take control of %s session. Maybe another compositor is running?",
qPrintable(sessionPath));
return nullptr;
}
std::unique_ptr<ConsoleKitSession> session{new ConsoleKitSession(sessionPath)};
if (session->initialize()) {
return session;
} else {
return nullptr;
}
// Red Bear OS: No ConsoleKit. Skip ConsoleKitSession entirely so Session::create()
// falls through to NoopSession, which opens DRM devices directly via ::open().
// Connecting to the system bus to check isServiceRegistered() can block
// indefinitely if the D-Bus daemon accepts the connection but doesn't implement
// the org.freedesktop.DBus interface methods Qt expects.
return nullptr;
}
bool ConsoleKitSession::isActive() const
@@ -1,2 +1,2 @@
#####################################################################################################################add_subdirectory(killer) # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only
#############################################################################################################################add_subdirectory(killer) # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only
add_subdirectory(wayland_wrapper)
@@ -349,6 +349,30 @@
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
/*
KWin - the KDE window manager
This file is part of the KDE project.
@@ -272,9 +272,17 @@ XwaylandInterface *ApplicationWayland::xwayland() const
int main(int argc, char *argv[])
{
fprintf(stderr, "[REDBEAR-KWIN] main() entered, pid=%d\n", getpid());
fflush(stderr);
KWin::Application::setupMalloc();
fprintf(stderr, "[REDBEAR-KWIN] setupMalloc done\n"); fflush(stderr);
KWin::Application::setupLocalizedString();
fprintf(stderr, "[REDBEAR-KWIN] setupLocalizedString done\n"); fflush(stderr);
KWin::gainRealTime();
fprintf(stderr, "[REDBEAR-KWIN] gainRealTime done\n"); fflush(stderr);
signal(SIGPIPE, SIG_IGN);
@@ -285,6 +293,8 @@ int main(int argc, char *argv[])
pthread_atfork(nullptr, nullptr, KWin::restoreNofileLimit);
}
fprintf(stderr, "[REDBEAR-KWIN] nofile limit bumped\n"); fflush(stderr);
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
// enforce our internal qpa plugin, unfortunately command line switch has precedence
@@ -295,8 +305,13 @@ int main(int argc, char *argv[])
// The gains are minimal, disable until it's fixed
QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache);
fprintf(stderr, "[REDBEAR-KWIN] about to construct ApplicationWayland, QT_QPA_PLATFORM=%s\n", getenv("QT_QPA_PLATFORM"));
fflush(stderr);
KWin::ApplicationWayland a(argc, argv);
fprintf(stderr, "[REDBEAR-KWIN] ApplicationWayland constructed OK\n"); fflush(stderr);
// reset QT_QPA_PLATFORM so we don't propagate it to our children (e.g. apps launched from the overview effect)
qunsetenv("QT_QPA_PLATFORM");
@@ -512,14 +527,20 @@ int main(int argc, char *argv[])
outputCount = std::max(1, count);
}
fprintf(stderr, "[REDBEAR-KWIN] backend type selected: %d (Kms=0,X11=1,Wayland=2,Virtual=3)\n", (int)backendType);
fflush(stderr);
switch (backendType) {
case BackendType::Kms:
fprintf(stderr, "[REDBEAR-KWIN] creating session...\n"); fflush(stderr);
a.setSession(KWin::Session::create());
if (!a.session()) {
std::cerr << "FATAl ERROR: could not acquire a session" << std::endl;
return 1;
}
fprintf(stderr, "[REDBEAR-KWIN] session created, creating DrmBackend...\n"); fflush(stderr);
a.setOutputBackend(std::make_unique<KWin::DrmBackend>(a.session()));
fprintf(stderr, "[REDBEAR-KWIN] DrmBackend created\n"); fflush(stderr);
break;
case BackendType::Virtual: {
auto outputBackend = std::make_unique<KWin::VirtualBackend>();
@@ -636,7 +657,9 @@ int main(int argc, char *argv[])
a.setProcessStartupEnvironment(environment);
a.setApplicationsToStart(parser.positionalArguments());
a.setInputMethodServerToStart(parser.value(inputMethodOption));
fprintf(stderr, "[REDBEAR-KWIN] about to call a.start()\n"); fflush(stderr);
a.start();
fprintf(stderr, "[REDBEAR-KWIN] a.start() done, entering event loop\n"); fflush(stderr);
return a.exec();
}
@@ -10,5 +10,5 @@ target_link_libraries(systembell PRIVATE
KF6::GlobalAccel
KF6::I18n
$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,Canberra::Canberra,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>
$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,Canberra::Canberra,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>
)
@@ -124,6 +124,14 @@
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include "backends/libinput/device.h"
#include "core/inputdevice.h"
@@ -700,6 +700,54 @@
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
#define F_ADD_SEALS 1033
#define F_GET_SEALS 1034
#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
#define F_ADD_SEALS 1033
#define F_GET_SEALS 1034
#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
#define F_ADD_SEALS 1033
#define F_GET_SEALS 1034
#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
#define F_ADD_SEALS 1033
#define F_GET_SEALS 1034
#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
#define F_ADD_SEALS 1033
#define F_GET_SEALS 1034
#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
#define F_ADD_SEALS 1033
#define F_GET_SEALS 1034
#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
#define F_ADD_SEALS 1033
#define F_GET_SEALS 1034
#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
#define F_ADD_SEALS 1033
#define F_GET_SEALS 1034
#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
/*
KWin - the KDE window manager
This file is part of the KDE project.
+108
View File
@@ -0,0 +1,108 @@
#TODO: SDDM display manager — Wayland-only build. PAM provided by pam-redbear shim.
# X11/XCB/XAUTH fully excluded via wayland-patch.sh (NO_X11 ifdef guards).
# XcbKeyboardBackend removed from greeter; XorgDisplayServer/XorgUserDisplayServer/XAuth
# removed from daemon. Default display server fallback is Wayland.
[source]
git = "https://github.com/sddm/sddm.git"
rev = "bc9eee8280275723767213220e88f6b14157ba1f"
patches = []
[build]
template = "custom"
dependencies = [
"qtbase",
"qtdeclarative",
"qtwayland",
"qtsvg",
"kf6-extra-cmake-modules",
"kf6-kcoreaddons",
"kf6-ki18n",
"kf6-kcrash",
"kf6-kdbusaddons",
"kf6-kconfig",
"kf6-kwindowsystem",
"kf6-kguiaddons",
"wayland-protocols",
"libwayland",
"pam-redbear",
]
script = """
DYNAMIC_INIT
HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build"
STAGE="${COOKBOOK_STAGE}/usr"
source "${COOKBOOK_ROOT}/local/scripts/lib/qt-sysroot.sh"
redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules
if [ -d "${COOKBOOK_SYSROOT}/plugins" ] && [ ! -L "${COOKBOOK_SYSROOT}/plugins" ]; then
if [ -d "${COOKBOOK_SYSROOT}/usr/plugins" ]; then
cp -an "${COOKBOOK_SYSROOT}/plugins/." "${COOKBOOK_SYSROOT}/usr/plugins/" 2>/dev/null || true
rm -rf "${COOKBOOK_SYSROOT}/plugins"
ln -s usr/plugins "${COOKBOOK_SYSROOT}/plugins"
fi
fi
CROSS_PKGCONFIG="${COOKBOOK_ROOT}/bin/x86_64-unknown-redox-pkg-config"
sed -i 's/find_package(XCB REQUIRED)/find_package(XCB QUIET)/' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
sed -i 's/find_package(XKB REQUIRED)/find_package(XKB QUIET)/' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
sed -i 's/pkg_check_modules(LIBXAU REQUIRED "xau")/pkg_check_modules(LIBXAU QUIET "xau")/' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
sed -i 's/find_package(Qt${QT_MAJOR_VERSION}.*CONFIG REQUIRED.*/find_package(Qt${QT_MAJOR_VERSION} 5.15.0 CONFIG REQUIRED Core Network DBus Gui Qml Quick)/' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
sed -i 's/find_package(Qt${QT_MAJOR_VERSION}Test)/find_package(Qt${QT_MAJOR_VERSION}Test QUIET)/' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
sed -i '/add_subdirectory(test)/d' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
sed -i '/enable_testing/d' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
find "${COOKBOOK_SOURCE}" -name "CMakeLists.txt" -exec sed -i '/qt_add_translation/d' {} + 2>/dev/null || true
find "${COOKBOOK_SOURCE}" -name "CMakeLists.txt" -exec sed -i '/LIBXCB_INCLUDE_DIR/d' {} + 2>/dev/null || true
find "${COOKBOOK_SOURCE}" -name "CMakeLists.txt" -exec sed -i 's/${LIBXCB_LIBRARIES}//' {} + 2>/dev/null || true
find "${COOKBOOK_SOURCE}" -name "CMakeLists.txt" -exec sed -i 's/${LIBXKB_LIBRARIES}//' {} + 2>/dev/null || true
find "${COOKBOOK_SOURCE}" -name "CMakeLists.txt" -exec sed -i 's/${LIBXAU_LIBRARIES}//' {} + 2>/dev/null || true
find "${COOKBOOK_SOURCE}" -name "CMakeLists.txt" -exec sed -i 's/${LIBXAU_INCLUDE_DIRS}//' {} + 2>/dev/null || true
if grep -q 'find_package(LibJournald' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null; then
sed -i 's/find_package(LibJournald REQUIRED)/find_package(LibJournald QUIET)/' "${COOKBOOK_SOURCE}/CMakeLists.txt"
fi
sed -i '/XAuth\\.cpp/d' "${COOKBOOK_SOURCE}/src/helper/CMakeLists.txt" 2>/dev/null || true
sed -i 's/${LIBXAU_LINK_LIBRARIES}//' "${COOKBOOK_SOURCE}/src/helper/CMakeLists.txt" 2>/dev/null || true
sed -i 's/ioctl(STDIN_FILENO, TIOCSCTTY)/ioctl(STDIN_FILENO, TIOCSCTTY, NULL)/' "${COOKBOOK_SOURCE}/src/helper/UserSession.cpp" 2>/dev/null || true
sed -i 's/XAuth::writeCookieToFile(display, m_xauthFile.fileName(), cookie)/true/' "${COOKBOOK_SOURCE}/src/helper/UserSession.cpp" 2>/dev/null || true
sed -i '/#include "XAuth.h"/d' "${COOKBOOK_SOURCE}/src/helper/UserSession.cpp" 2>/dev/null || true
python3 "${COOKBOOK_RECIPE}/remove-x11user-helper.py" "${COOKBOOK_SOURCE}/src/helper/CMakeLists.txt"
cp -r "${COOKBOOK_RECIPE}/stubs/"* "${COOKBOOK_SYSROOT}/usr/include/"
chmod +x "${COOKBOOK_RECIPE}/wayland-patch.sh"
"${COOKBOOK_RECIPE}/wayland-patch.sh" "${COOKBOOK_SOURCE}"
mkdir -p build
cd build
rm -f CMakeCache.txt
rm -rf CMakeFiles
cmake "${COOKBOOK_SOURCE}" \
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
-DQT_HOST_PATH="${HOST_BUILD}" \
-DKF6_HOST_TOOLING="${HOST_BUILD}/lib/cmake" \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}/usr;${COOKBOOK_SYSROOT}" \
-DPKG_CONFIG_EXECUTABLE="${CROSS_PKGCONFIG}" \
-DBUILD_TESTING=OFF \
-DBUILD_WITH_QT6=ON \
-DNO_SYSTEMD=ON \
-DENABLE_JOURNALD=OFF \
-DENABLE_PAM=ON \
-DCMAKE_BUILD_WITH_INSTALL_RPATH=TRUE \
-DCMAKE_INSTALL_RPATH="/usr/lib" \
-Wno-dev
cmake --build . -j${COOKBOOK_MAKE_JOBS}
DESTDIR="${COOKBOOK_STAGE}" cmake --install . --prefix /usr
for bin in "${STAGE}/bin/"* "${STAGE}/lib/"lib*.so.*; do
[ -f "${bin}" ] || continue
patchelf --set-rpath "/usr/lib" "${bin}" 2>/dev/null || true
done
"""
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
import re, sys
path = sys.argv[1]
with open(path) as f:
c = f.read()
c = re.sub(
r'\nadd_executable\(sddm-helper-start-x11user.*?\ninstall\(TARGETS sddm-helper-start-x11user[^)]*\)',
'', c, flags=re.DOTALL
)
c = c.replace(
' target_link_libraries(sddm-helper-start-x11user ${JOURNALD_LIBRARIES})\n', ''
)
with open(path, 'w') as f:
f.write(c)
Submodule local/recipes/kde/sddm/source added at bc9eee8280
+19
View File
@@ -0,0 +1,19 @@
#ifndef _X11_XAUTH_H
#define _X11_XAUTH_H
typedef unsigned short Family;
#define FamilyLocal 256
typedef struct _Xauth {
unsigned short family;
unsigned short address_length;
char *address;
unsigned short number_length;
char *number;
unsigned short name_length;
char *name;
unsigned short data_length;
char *data;
} Xauth;
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef _LINUX_KD_H
#define _LINUX_KD_H
#define KDSETMODE 0x4B3A
#define KDGETMODE 0x4B3B
#define KD_TEXT 0x00
#define KD_GRAPHICS 0x01
#endif
+41
View File
@@ -0,0 +1,41 @@
#ifndef _LINUX_VT_H
#define _LINUX_VT_H
#include <sys/ioctl.h>
#define VT_OPENQRY 0x5600
#define VT_GETMODE 0x5601
#define VT_SETMODE 0x5602
#define VT_GETSTATE 0x5603
#define VT_SENDSIG 0x5604
#define VT_RELDISP 0x5605
#define VT_ACTIVATE 0x5606
#define VT_WAITACTIVE 0x5607
#define VT_DISALLOCATE 0x5608
#define VT_GETACTIVE 0x5609
#define VT_AUTO 0x00
#define VT_PROCESS 0x01
#define VT_ACKACQ 2
#define KDSETMODE 0x4B3A
#define KDGETMODE 0x4B3B
#define KD_TEXT 0x00
#define KD_GRAPHICS 0x01
struct vt_stat {
unsigned short v_active;
unsigned short v_signal;
unsigned short v_state;
};
struct vt_mode {
unsigned char mode;
unsigned char waitv;
unsigned short relsig;
unsigned short acqsig;
unsigned short frsig;
};
#endif
+41
View File
@@ -0,0 +1,41 @@
#ifndef _UTMPX_H
#define _UTMPX_H
#include <sys/types.h>
#include <time.h>
#define UTMPX_FILE "/var/run/utmpx"
#define EMPTY 0
#define RUN_LVL 1
#define BOOT_TIME 2
#define NEW_TIME 3
#define OLD_TIME 4
#define INIT_PROCESS 5
#define LOGIN_PROCESS 6
#define USER_PROCESS 7
#define DEAD_PROCESS 8
struct utmpx {
short ut_type;
pid_t ut_pid;
char ut_line[32];
char ut_id[4];
char ut_user[32];
char ut_host[256];
struct timeval ut_tv;
struct {
int32_t __e_termination;
int32_t __e_exit;
} ut_exit;
};
static inline void setutxent(void) {}
static inline void endutxent(void) {}
static inline struct utmpx *getutxent(void) { return NULL; }
static inline struct utmpx *getutxid(const struct utmpx *id) { (void)id; return NULL; }
static inline struct utmpx *getutxline(const struct utmpx *line) { (void)line; return NULL; }
static inline int pututxline(const struct utmpx *ut) { (void)ut; return -1; }
static inline int utmpxname(const char *file) { (void)file; return -1; }
#endif
+245
View File
@@ -0,0 +1,245 @@
#!/bin/bash
set -e
SRC="$1"
if [ -z "$SRC" ]; then
echo "Usage: $0 <source-directory>"
exit 1
fi
echo "=== Applying Wayland-only patches to SDDM ==="
# === CMakeLists.txt: add NO_X11 compile definition ===
sed -i 's/^add_definitions(-Wall/add_definitions(-DNO_X11 -Wall/' "${SRC}/CMakeLists.txt"
# === Daemon CMakeLists: remove X11-only source files ===
sed -i '/XAuth\.cpp/d' "${SRC}/src/daemon/CMakeLists.txt"
sed -i '/XorgDisplayServer\.cpp/d' "${SRC}/src/daemon/CMakeLists.txt"
sed -i '/XorgUserDisplayServer/d' "${SRC}/src/daemon/CMakeLists.txt"
sed -i '/LIBXAU_LINK_LIBRARIES/d' "${SRC}/src/daemon/CMakeLists.txt"
# === Greeter CMakeLists: remove XCB keyboard backend ===
sed -i '/XcbKeyboardBackend\.cpp/d' "${SRC}/src/greeter/CMakeLists.txt"
# === Greeter CMakeLists: add Qt6::Network for QLocalSocket ===
sed -i 's|Qt\${QT_MAJOR_VERSION}::Quick[[:space:]]*$|Qt${QT_MAJOR_VERSION}::Quick\n Qt${QT_MAJOR_VERSION}::Network|' \
"${SRC}/src/greeter/CMakeLists.txt"
# === Multiline patches via Python ===
SDDM_SRC="$SRC" python3 << 'PYEOF'
import re
import os
def patch_file(path, replacements):
with open(path, 'r') as f:
content = f.read()
for pattern, repl, desc in replacements:
new_content = re.sub(pattern, repl, content, flags=re.DOTALL)
if new_content == content:
print(f" WARNING: pattern not matched in {path}: {desc}")
content = new_content
with open(path, 'w') as f:
f.write(content)
src = os.environ.get('SDDM_SRC', '.')
# ---- KeyboardModel.cpp ----
patch_file(f"{src}/src/greeter/KeyboardModel.cpp", [
(r'#include "XcbKeyboardBackend\.h"',
'#ifndef NO_X11\n#include "XcbKeyboardBackend.h"\n#endif',
"XcbKeyboardBackend include"),
(r'(\s+)if \(QGuiApplication::platformName\(\) == QLatin1String\("xcb"\)\) \{\n'
r'\s+m_backend = new XcbKeyboardBackend\(d\);\n'
r'\s+m_backend->init\(\);\n'
r'\s+m_backend->connectEventsDispatcher\(this\);\n'
r'\s+\} else (if)',
r'\1#ifndef NO_X11\n'
r'\1 if (QGuiApplication::platformName() == QLatin1String("xcb")) {\n'
r'\1 m_backend = new XcbKeyboardBackend(d);\n'
r'\1 m_backend->init();\n'
r'\1 m_backend->connectEventsDispatcher(this);\n'
r'\1 } else\n'
r'\1#endif\n'
r'\1 \2',
"XCB branch in constructor"),
])
# ---- Display.cpp ----
patch_file(f"{src}/src/daemon/Display.cpp", [
(r'#include "XorgDisplayServer\.h"\n#include "XorgUserDisplayServer\.h"',
'#ifndef NO_X11\n#include "XorgDisplayServer.h"\n#include "XorgUserDisplayServer.h"\n#endif',
"Xorg includes"),
(r'(\s+)case X11DisplayServerType:\n'
r'(\s+)if \(seat\(\)->canTTY\(\)\) \{\n'
r'\s+m_terminalId = VirtualTerminal::setUpNewVt\(\);\n'
r'\s+\}\n'
r'\s+m_displayServer = new XorgDisplayServer\(this\);\n'
r'\s+break;\n'
r'(\s+)case X11UserDisplayServerType:\n'
r'\s+if \(seat\(\)->canTTY\(\)\) \{\n'
r'\s+m_terminalId = fetchAvailableVt\(\);\n'
r'\s+\}\n'
r'\s+m_displayServer = new XorgUserDisplayServer\(this\);\n'
r'\s+m_greeter->setDisplayServerCommand\(XorgUserDisplayServer::command\(this\)\);\n'
r'\s+break;',
r'\1#ifndef NO_X11\n'
r'\1 case X11DisplayServerType:\n'
r'\2 if (seat()->canTTY()) {\n'
r' m_terminalId = VirtualTerminal::setUpNewVt();\n'
r' }\n'
r' m_displayServer = new XorgDisplayServer(this);\n'
r' break;\n'
r'\3 case X11UserDisplayServerType:\n'
r' if (seat()->canTTY()) {\n'
r' m_terminalId = fetchAvailableVt();\n'
r' }\n'
r' m_displayServer = new XorgUserDisplayServer(this);\n'
r' m_greeter->setDisplayServerCommand(XorgUserDisplayServer::command(this));\n'
r' break;\n'
r'\1#endif',
"X11 cases in constructor switch"),
(r'(\s+)if \(session\.xdgSessionType\(\) == QLatin1String\("x11"\)\) \{\n'
r'(\s+)if \(m_displayServerType == X11DisplayServerType\)\n'
r'(\s+)env\.insert\(QStringLiteral\("DISPLAY"\), name\(\)\);\n'
r'(\s+)else\n'
r'(\s+)m_auth->setDisplayServerCommand\(XorgUserDisplayServer::command\(this\)\);\n'
r'(\s+)\} else \{',
r'\1if (session.xdgSessionType() == QLatin1String("x11")) {\n'
r'#ifndef NO_X11\n'
r'\2 if (m_displayServerType == X11DisplayServerType)\n'
r'\3 env.insert(QStringLiteral("DISPLAY"), name());\n'
r'\4 else\n'
r'\5 m_auth->setDisplayServerCommand(XorgUserDisplayServer::command(this));\n'
r'#endif\n'
r'\6} else {',
"XorgUserDisplayServer::command in startAuth"),
(r'(\s+)if \(qobject_cast<XorgDisplayServer \*>\(m_displayServer\)\)\n'
r'(\s+)m_auth->setCookie\(qobject_cast<XorgDisplayServer \*>\(m_displayServer\)->cookie\(\)\);',
r'\1#ifndef NO_X11\n'
r'\1 if (qobject_cast<XorgDisplayServer *>(m_displayServer))\n'
r'\2 m_auth->setCookie(qobject_cast<XorgDisplayServer *>(m_displayServer)->cookie());\n'
r'\1#endif',
"XorgDisplayServer cookie in slotAuthenticationFinished"),
(r'(\s+qPrintable\(displayServerType\)\));\n'
r'(\s+)\}\n'
r'(\s+)ret = X11DisplayServerType;',
r'\1;\n'
r'\2}\n'
r'#ifndef NO_X11\n'
r'\3ret = X11DisplayServerType;\n'
r'#else\n'
r'\3ret = WaylandDisplayServerType;\n'
r'#endif',
"defaultDisplayServerType fallback"),
])
# ---- Greeter.cpp ----
patch_file(f"{src}/src/daemon/Greeter.cpp", [
(r'#include "XorgDisplayServer\.h"\n#include "XorgUserDisplayServer\.h"',
'#ifndef NO_X11\n#include "XorgDisplayServer.h"\n#include "XorgUserDisplayServer.h"\n#endif',
"Xorg includes"),
(r'(\s+)if \(m_display->displayServerType\(\) == Display::X11DisplayServerType\) \{\n'
r'(\s+)// set process environment\n'
r'(\s+)QProcessEnvironment env = QProcessEnvironment::systemEnvironment\(\);\n'
r'(\s+)env\.insert\(QStringLiteral\("DISPLAY"\), m_display->name\(\)\);\n'
r'(\s+)env\.insert\(QStringLiteral\("XAUTHORITY"\), qobject_cast<XorgDisplayServer\*>\(displayServer\)->authPath\(\)\);\n'
r'(\s+)env\.insert\(QStringLiteral\("XCURSOR_THEME"\), xcursorTheme\);\n'
r'(\s+)if \(!xcursorSize\.isEmpty\(\)\)\n'
r'(\s+)env\.insert\(QStringLiteral\("XCURSOR_SIZE"\), xcursorSize\);\n'
r'(\s+)m_process->setProcessEnvironment\(env\);\n'
r'(\s+)\}',
r'\1#ifndef NO_X11\n'
r'\1 if (m_display->displayServerType() == Display::X11DisplayServerType) {\n'
r'\2 // set process environment\n'
r'\3 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();\n'
r'\4 env.insert(QStringLiteral("DISPLAY"), m_display->name());\n'
r'\5 env.insert(QStringLiteral("XAUTHORITY"), qobject_cast<XorgDisplayServer*>(displayServer)->authPath());\n'
r'\6 env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme);\n'
r'\7 if (!xcursorSize.isEmpty())\n'
r'\8 env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize);\n'
r'\9 m_process->setProcessEnvironment(env);\n'
r'\1 }\n'
r'\1#endif',
"X11 env in testing mode"),
(r'(\s+)if \(m_display->displayServerType\(\) == Display::X11DisplayServerType\) \{\n'
r'(\s+)env\.insert\(QStringLiteral\("DISPLAY"\), m_display->name\(\)\);\n'
r'(\s+)env\.insert\(QStringLiteral\("QT_QPA_PLATFORM"\), QStringLiteral\("xcb"\)\);\n'
r'(\s+)m_auth->setCookie\(qobject_cast<XorgDisplayServer\*>\(displayServer\)->cookie\(\)\);\n'
r'(\s+)\} else if',
r'\1#ifndef NO_X11\n'
r'\1 if (m_display->displayServerType() == Display::X11DisplayServerType) {\n'
r'\2 env.insert(QStringLiteral("DISPLAY"), m_display->name());\n'
r'\3 env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb"));\n'
r'\4 m_auth->setCookie(qobject_cast<XorgDisplayServer*>(displayServer)->cookie());\n'
r'\1 } else\n'
r'\1#endif\n'
r'\1 if',
"X11 env/cookie in non-testing mode"),
(r'(\s+)auto \*xorgUser = qobject_cast<XorgUserDisplayServer \*>\(displayServer\);\n'
r'(\s+)if \(xorgUser\)\n'
r'(\s+)xorgUser->setDisplayName\(displayName\);\n',
r'\1#ifndef NO_X11\n'
r'\1 auto *xorgUser = qobject_cast<XorgUserDisplayServer *>(displayServer);\n'
r'\2 if (xorgUser)\n'
r'\3 xorgUser->setDisplayName(displayName);\n'
r'\1#endif\n',
"XorgUserDisplayServer in onDisplayServerReady"),
])
# ---- Seat.cpp ----
patch_file(f"{src}/src/daemon/Seat.cpp", [
(r'#include "XorgDisplayServer\.h"\n',
'#ifndef NO_X11\n#include "XorgDisplayServer.h"\n#endif\n',
"Xorg include"),
(r'(\s+)// If we failed to create a display with wayland or rootful x11.*\n'
r'(\s+)// x11-user.*\n'
r'(\s+)// since.*\n'
r'(\s+)if \(display->displayServerType\(\) != Display::X11UserDisplayServerType\) \{\n'
r'(\s+)qWarning\(\) << "Failed to launch the display server, falling back to DisplayServer=x11-user";\n'
r'(\s+)createDisplay\(Display::X11UserDisplayServerType\);\n'
r'(\s+)\} else',
r'\1// Wayland-only: no X11 fallback available\n'
r'#ifndef NO_X11\n'
r'\1 // If we failed to create a display with wayland or rootful x11, try with\n'
r'\1 // x11-user. There\'s a chance it might work. It\'s a handy fallback\n'
r'\1 // since the alternative is a black screen\n'
r'\4 if (display->displayServerType() != Display::X11UserDisplayServerType) {\n'
r'\5 qWarning() << "Failed to launch the display server, falling back to DisplayServer=x11-user";\n'
r'\6 createDisplay(Display::X11UserDisplayServerType);\n'
r'\7 } else\n'
r'#endif\n',
"X11 fallback in Seat"),
])
# ---- UserSession.cpp ----
patch_file(f"{src}/src/helper/UserSession.cpp", [
(r'#include "XAuth\.h"',
'#ifndef NO_X11\n#include "XAuth.h"\n#endif',
"XAuth include"),
(r'(\s+)if \(!XAuth::writeCookieToFile\(display, m_xauthFile\.fileName\(\), cookie\)\) \{\n'
r'(\s+)const auto error = strerror\(errno\);\n'
r'(\s+)qCritical\(\) << .*;\n'
r'(\s+)_exit\(Auth::HELPER_AUTH_ERROR\);\n'
r'(\s+)\}',
r'\1#ifndef NO_X11\n'
r'\1 if (!XAuth::writeCookieToFile(display, m_xauthFile.fileName(), cookie)) {\n'
r'\2 const auto error = strerror(errno);\n'
r'\3 qCritical() << "Failed to write xauth cookie: " << error;\n'
r'\4 _exit(Auth::HELPER_AUTH_ERROR);\n'
r'\5 }\n'
r'\1#endif',
"XAuth cookie write"),
])
print("=== Wayland-only patches applied ===")
PYEOF
+1 -1
View File
@@ -1,7 +1,7 @@
[source]
tar = "https://gitlab.freedesktop.org/mesa/libdrm/-/archive/libdrm-2.4.125/libdrm-libdrm-2.4.125.tar.gz"
blake3 = "33e6448252639f4ff8a8cd30129b335c5d85356c1c93f8d77a79221003b14f66"
patches = ["redox.patch", "../../../patches/libdrm/P1-drm-ioctl-bridge.patch", "../../../patches/libdrm/P2-drm-get-pci-info.patch"]
patches = ["redox.patch", "../../../local/patches/libdrm/P1-drm-ioctl-bridge.patch", "../../../local/patches/libdrm/P2-drm-get-pci-info.patch", "../../../local/patches/libdrm/P3-drm-get-version-driver-name.patch", "../../../local/patches/libdrm/P4-drmGetDeviceFromDevId-redox.patch"]
[build]
template = "meson"
@@ -0,0 +1,132 @@
#ifndef _VIRTGPU_DRM_H_
#define _VIRTGPU_DRM_H_
#include <stdint.h>
#define DRM_VIRTGPU_MAP 0x41
#define DRM_VIRTGPU_EXECBUFFER 0x42
#define DRM_VIRTGPU_GETPARAM 0x43
#define DRM_VIRTGPU_RESOURCE_CREATE 0x44
#define DRM_VIRTGPU_RESOURCE_INFO 0x45
#define DRM_VIRTGPU_TRANSFER_FROM_HOST 0x46
#define DRM_VIRTGPU_TRANSFER_TO_HOST 0x47
#define DRM_VIRTGPU_WAIT 0x48
#define DRM_VIRTGPU_GET_CAPS 0x49
#define DRM_VIRTGPU_RESOURCE_CREATE_BLOB 0x4A
#define DRM_VIRTGPU_CONTEXT_INIT 0x4B
#define drm_virtgpu_resource_create drm_virtgpu_resource_create_3d
#define drm_virtgpu_3d_transfer_to_host drm_virtgpu_transfer_to_host
#define drm_virtgpu_3d_transfer_from_host drm_virtgpu_transfer_from_host
#define drm_virtgpu_3d_wait drm_virtgpu_wait_3d
#define ctx_set_params_ptr ctx_set_params
#define resource_id res_handle
struct drm_virtgpu_3d_box {
uint32_t x;
uint32_t y;
uint32_t z;
uint32_t w;
uint32_t h;
uint32_t d;
};
struct drm_virtgpu_execbuffer {
uint32_t flags;
uint32_t size;
uint64_t command;
uint64_t bo_handles;
uint32_t num_bo_handles;
int32_t fence_fd;
uint32_t ring_idx;
uint32_t syncobj_stride;
uint32_t num_in_syncobjs;
uint32_t num_out_syncobjs;
uint64_t in_syncobjs;
uint64_t out_syncobjs;
};
struct drm_virtgpu_getparam {
uint64_t param;
uint64_t value;
};
struct drm_virtgpu_resource_create_3d {
uint32_t target;
uint32_t format;
uint32_t bind;
uint32_t width;
uint32_t height;
uint32_t depth;
uint32_t array_size;
uint32_t last_level;
uint32_t nr_samples;
uint32_t flags;
uint32_t bo_handle;
uint32_t res_handle;
uint32_t size;
uint32_t stride;
};
struct drm_virtgpu_resource_info {
uint32_t bo_handle;
uint32_t res_handle;
uint32_t size;
uint32_t blob_mem;
};
struct drm_virtgpu_transfer_to_host {
uint32_t bo_handle;
struct drm_virtgpu_3d_box box;
uint32_t level;
uint32_t offset;
uint32_t stride;
uint32_t layer_stride;
};
struct drm_virtgpu_transfer_from_host {
uint32_t bo_handle;
struct drm_virtgpu_3d_box box;
uint32_t level;
uint32_t offset;
uint32_t stride;
uint32_t layer_stride;
};
struct drm_virtgpu_wait_3d {
uint32_t handle;
uint32_t flags;
};
struct drm_virtgpu_get_caps {
uint32_t cap_set_id;
uint32_t cap_set_ver;
uint64_t addr;
uint32_t size;
uint32_t pad;
};
struct drm_virtgpu_resource_create_blob {
uint32_t blob_mem;
uint32_t blob_flags;
uint32_t bo_handle;
uint32_t res_handle;
uint64_t size;
uint32_t pad;
uint32_t cmd_size;
uint64_t cmd;
uint64_t blob_id;
};
struct drm_virtgpu_context_set_param {
uint64_t param;
uint64_t value;
};
struct drm_virtgpu_context_init {
uint32_t num_params;
uint32_t pad;
uint64_t ctx_set_params;
};
#endif
+84 -1
View File
@@ -1772,6 +1772,29 @@ drm_public drmVersionPtr drmGetVersion(int fd)
retval->version_major = version.major;
retval->version_minor = version.minor;
retval->version_patchlevel = version.patch;
/* The scheme returns a NUL-terminated driver name in version.name.
* KWin drm_gpu.cpp dereferences version->name unconditionally
* (strstr checks for "i915", "amdgpu", "virtio" etc.) so it must
* never be NULL.
*/
version.name[sizeof(version.name) - 1] = '\0';
if (version.name[0] != '\0') {
size_t len = strlen(version.name);
retval->name = drmMalloc(len + 1);
if (retval->name) {
memcpy(retval->name, version.name, len + 1);
retval->name_len = len;
}
} else {
const char fallback[] = "redox-drm";
retval->name = drmMalloc(sizeof(fallback));
if (retval->name) {
memcpy(retval->name, fallback, sizeof(fallback));
retval->name_len = sizeof(fallback) - 1;
}
}
return retval;
#else
drmVersionPtr retval;
@@ -4070,7 +4093,7 @@ static int drmParseSubsystemType(int maj, int min)
return DRM_BUS_VIRTIO;
}
return subsystem_type;
#elif defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD__)
#elif defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__redox__)
return DRM_BUS_PCI;
#else
#warning "Missing implementation of drmParseSubsystemType"
@@ -5097,6 +5120,66 @@ drm_public int drmGetDeviceFromDevId(dev_t find_rdev, uint32_t flags, drmDeviceP
*device = d;
return 0;
#elif defined(__redox__)
/* On Redox there is no /dev/dri/ directory to enumerate.
* Instead, open /scheme/drm/card0 and query PCI info to
* construct a single drmDevice that serves as both primary
* and render node. */
drmDevicePtr devp;
char *pptr;
int max_node_length = 64, i;
size_t extra, psize;
uint8_t pbuf[22];
size_t prsize = 0;
int pret, fd;
if (device == NULL)
return -EINVAL;
if (drm_device_validate_flags(flags))
return -EINVAL;
fd = open("/scheme/drm/card0", O_RDWR | O_CLOEXEC);
if (fd < 0)
return -errno;
pret = redox_drm_exchange(fd, REDOX_DRM_IOCTL_GET_PCI_INFO, NULL, 0,
pbuf, sizeof(pbuf), &prsize);
close(fd);
if (pret != 0 || prsize < 17)
return -EIO;
extra = DRM_NODE_MAX * (sizeof(void *) + max_node_length);
psize = sizeof(drmDevice) + extra + sizeof(drmPciBusInfo) + sizeof(drmPciDeviceInfo);
devp = calloc(1, psize);
if (!devp)
return -ENOMEM;
devp->bustype = DRM_BUS_PCI;
/* Advertise both PRIMARY and RENDER — same path on Redox */
devp->available_nodes = (1 << DRM_NODE_PRIMARY) | (1 << DRM_NODE_RENDER);
pptr = (char *)devp + sizeof(drmDevice);
devp->nodes = (char **)pptr;
pptr += DRM_NODE_MAX * sizeof(void *);
for (i = 0; i < DRM_NODE_MAX; i++) { devp->nodes[i] = pptr; pptr += max_node_length; }
snprintf(devp->nodes[DRM_NODE_PRIMARY], max_node_length, "/scheme/drm/card0");
snprintf(devp->nodes[DRM_NODE_RENDER], max_node_length, "/scheme/drm/card0");
devp->businfo.pci = (drmPciBusInfoPtr)pptr;
pptr += sizeof(drmPciBusInfo);
devp->businfo.pci->domain = pbuf[10] | (pbuf[11] << 8) | (pbuf[12] << 16) | (pbuf[13] << 24);
devp->businfo.pci->bus = pbuf[14];
devp->businfo.pci->dev = pbuf[15];
devp->businfo.pci->func = pbuf[16];
devp->deviceinfo.pci = (drmPciDeviceInfoPtr)pptr;
devp->deviceinfo.pci->vendor_id = pbuf[0] | (pbuf[1] << 8);
devp->deviceinfo.pci->device_id = pbuf[2] | (pbuf[3] << 8);
devp->deviceinfo.pci->subvendor_id = pbuf[4] | (pbuf[5] << 8);
devp->deviceinfo.pci->subdevice_id = pbuf[6] | (pbuf[7] << 8);
devp->deviceinfo.pci->revision_id = pbuf[8];
*device = devp;
return 0;
#else
drmDevicePtr local_devices[MAX_DRM_NODES];
@@ -138,6 +138,7 @@ struct redox_drm_version_wire {
int32_t major;
int32_t minor;
int32_t patch;
char name[64];
};
struct redox_drm_prime_handle_to_fd_wire {
@@ -0,0 +1,48 @@
# PAM compatibility shim — implements the PAM API by delegating authentication
# to redbear-authd via Unix socket JSON protocol. Provides libpam.so and
# security/pam_appl.h for SDDM and other PAM-consuming software.
[source]
path = "source"
[build]
template = "custom"
dependencies = [
"redbear-authd",
]
script = """
DYNAMIC_INIT
mkdir -p "${COOKBOOK_STAGE}/usr/lib"
mkdir -p "${COOKBOOK_STAGE}/usr/include/security"
x86_64-unknown-redox-gcc \
-shared \
-fPIC \
-std=c11 \
-Wall \
-Wextra \
-Wl,-soname,libpam.so.0 \
-I"${COOKBOOK_SOURCE}" \
-o "${COOKBOOK_STAGE}/usr/lib/libpam.so.0.99.0" \
"${COOKBOOK_SOURCE}/pam_redbear.c"
ln -sf libpam.so.0.99.0 "${COOKBOOK_STAGE}/usr/lib/libpam.so.0"
ln -sf libpam.so.0 "${COOKBOOK_STAGE}/usr/lib/libpam.so"
cp "${COOKBOOK_SOURCE}/security/pam_appl.h" "${COOKBOOK_STAGE}/usr/include/security/pam_appl.h"
# pkg-config for downstream cmake/find_package lookups
mkdir -p "${COOKBOOK_STAGE}/usr/lib/pkgconfig"
cat > "${COOKBOOK_STAGE}/usr/lib/pkgconfig/pam.pc" << 'EOF'
prefix=/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: pam
Description: PAM compatibility shim (redbear-authd backend)
Version: 0.99.0
Libs: -L${libdir} -lpam
Cflags: -I${includedir}
EOF
"""
@@ -0,0 +1,342 @@
#include <security/pam_appl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#define AUTHD_SOCK_PATH "/run/redbear-authd.sock"
#define AUTHD_BUF_SIZE 4096
#define AUTHD_RESP_MAX 1024
struct pam_handle {
char *service;
char *user;
char *tty;
char *rhost;
char *ruser;
struct pam_conv conv;
};
static int connect_authd(void)
{
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) return -1;
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, AUTHD_SOCK_PATH, sizeof(addr.sun_path) - 1);
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
close(fd);
return -1;
}
return fd;
}
static ssize_t send_all(int fd, const char *buf, size_t len)
{
size_t sent = 0;
while (sent < len) {
ssize_t n = send(fd, buf + sent, len - sent, 0);
if (n < 0) return -1;
sent += (size_t)n;
}
return (ssize_t)sent;
}
static ssize_t recv_line(int fd, char *buf, size_t bufsize)
{
size_t total = 0;
while (total < bufsize - 1) {
ssize_t n = recv(fd, buf + total, 1, 0);
if (n <= 0) return n < 0 ? -1 : (ssize_t)total;
total += (size_t)n;
if (buf[total - 1] == '\n') break;
}
buf[total] = '\0';
return (ssize_t)total;
}
__attribute__((unused))
static char *json_extract_string(const char *json, const char *key)
{
char search[128];
snprintf(search, sizeof(search), "\"%s\":", key);
const char *pos = strstr(json, search);
if (!pos) return NULL;
pos += strlen(search);
while (*pos == ' ' || *pos == '\t') pos++;
if (*pos != '"') return NULL;
pos++;
size_t klen = strlen(key);
(void)klen;
const char *end = strchr(pos, '"');
if (!end) return NULL;
size_t vlen = (size_t)(end - pos);
char *val = malloc(vlen + 1);
if (!val) return NULL;
memcpy(val, pos, vlen);
val[vlen] = '\0';
return val;
}
static int json_extract_bool(const char *json, const char *key)
{
char search[128];
snprintf(search, sizeof(search), "\"%s\":", key);
const char *pos = strstr(json, search);
if (!pos) return -1;
pos += strlen(search);
while (*pos == ' ' || *pos == '\t') pos++;
if (strncmp(pos, "true", 4) == 0) return 1;
if (strncmp(pos, "false", 5) == 0) return 0;
return -1;
}
int pam_start(const char *service, const char *user,
const struct pam_conv *conv, pam_handle_t **pamh)
{
if (!pamh) return PAM_SYSTEM_ERR;
pam_handle_t *h = calloc(1, sizeof(*h));
if (!h) return PAM_BUF_ERR;
if (service) {
h->service = strdup(service);
if (!h->service) { free(h); return PAM_BUF_ERR; }
}
if (user) {
h->user = strdup(user);
if (!h->user) { free(h->service); free(h); return PAM_BUF_ERR; }
}
if (conv) {
h->conv = *conv;
}
*pamh = h;
return PAM_SUCCESS;
}
int pam_end(pam_handle_t *pamh, int pam_status)
{
(void)pam_status;
if (!pamh) return PAM_SYSTEM_ERR;
free(pamh->service);
free(pamh->user);
free(pamh->tty);
free(pamh->rhost);
free(pamh->ruser);
free(pamh);
return PAM_SUCCESS;
}
int pam_set_item(pam_handle_t *pamh, int item_type, const void *item)
{
if (!pamh) return PAM_SYSTEM_ERR;
char **target = NULL;
switch (item_type) {
case PAM_SERVICE: target = &pamh->service; break;
case PAM_USER: target = &pamh->user; break;
case PAM_TTY: target = &pamh->tty; break;
case PAM_RHOST: target = &pamh->rhost; break;
case PAM_RUSER: target = &pamh->ruser; break;
case PAM_CONV:
if (item) pamh->conv = *(const struct pam_conv *)item;
return PAM_SUCCESS;
default:
return PAM_BAD_ITEM;
}
if (!target) return PAM_BAD_ITEM;
free(*target);
*target = NULL;
if (item) {
*target = strdup((const char *)item);
if (!*target) return PAM_BUF_ERR;
}
return PAM_SUCCESS;
}
int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item)
{
if (!pamh || !item) return PAM_SYSTEM_ERR;
switch (item_type) {
case PAM_SERVICE: *item = pamh->service; break;
case PAM_USER: *item = pamh->user; break;
case PAM_TTY: *item = pamh->tty; break;
case PAM_RHOST: *item = pamh->rhost; break;
case PAM_RUSER: *item = pamh->ruser; break;
case PAM_CONV: *item = &pamh->conv; break;
default:
*item = NULL;
return PAM_BAD_ITEM;
}
return PAM_SUCCESS;
}
int pam_authenticate(pam_handle_t *pamh, int flags)
{
(void)flags;
if (!pamh) return PAM_SYSTEM_ERR;
if (!pamh->conv.conv) return PAM_SYSTEM_ERR;
const char *username = pamh->user;
if (!username) return PAM_AUTH_ERR;
const struct pam_message msg = {
.msg_style = PAM_PROMPT_ECHO_OFF,
.msg = "Password: "
};
const struct pam_message *msg_ptr = &msg;
struct pam_response *resp = NULL;
int rc = pamh->conv.conv(1, &msg_ptr, &resp, pamh->conv.appdata_ptr);
if (rc != PAM_SUCCESS || !resp || !resp[0].resp) return PAM_CONV_ERR;
const char *password = resp[0].resp;
int fd = connect_authd();
if (fd < 0) {
if (resp[0].resp) { memset(resp[0].resp, 0, strlen(resp[0].resp)); free(resp[0].resp); }
free(resp);
return PAM_AUTHINFO_UNAVAIL;
}
char request[AUTHD_BUF_SIZE];
int n = snprintf(request, sizeof(request),
"{\"type\":\"Authenticate\",\"username\":\"%s\",\"password\":\"",
username);
for (const char *p = password; *p && (size_t)n < sizeof(request) - 4; p++) {
if (*p == '"' || *p == '\\') {
request[n++] = '\\';
if ((size_t)n >= sizeof(request) - 3) break;
}
request[n++] = *p;
}
n += snprintf(request + n, sizeof(request) - (size_t)n, "\",\"vt\":0}\n");
memset((void *)password, 0, strlen(password));
free(resp[0].resp);
free(resp);
if (send_all(fd, request, (size_t)n) < 0) {
close(fd);
return PAM_AUTH_ERR;
}
char response[AUTHD_RESP_MAX];
ssize_t rlen = recv_line(fd, response, sizeof(response));
close(fd);
if (rlen <= 0) return PAM_AUTH_ERR;
int ok = json_extract_bool(response, "ok");
if (ok == 1) return PAM_SUCCESS;
return PAM_AUTH_ERR;
}
int pam_acct_mgmt(pam_handle_t *pamh, int flags)
{
(void)pamh;
(void)flags;
return PAM_SUCCESS;
}
int pam_open_session(pam_handle_t *pamh, int flags)
{
(void)pamh;
(void)flags;
return PAM_SUCCESS;
}
int pam_close_session(pam_handle_t *pamh, int flags)
{
(void)pamh;
(void)flags;
return PAM_SUCCESS;
}
const char *pam_strerror(pam_handle_t *pamh, int errnum)
{
(void)pamh;
switch (errnum) {
case PAM_SUCCESS: return "Success";
case PAM_OPEN_ERR: return "Failed to load module";
case PAM_SYMBOL_ERR: return "Invalid symbol";
case PAM_SERVICE_ERR: return "Error in service module";
case PAM_SYSTEM_ERR: return "System error";
case PAM_BUF_ERR: return "Memory buffer error";
case PAM_PERM_DENIED: return "Permission denied";
case PAM_AUTH_ERR: return "Authentication failure";
case PAM_CRED_INSUFFICIENT: return "Insufficient credentials";
case PAM_AUTHINFO_UNAVAIL: return "Authentication information unavailable";
case PAM_USER_UNKNOWN: return "User not known to the underlying module";
case PAM_MAXTRIES: return "Have exhausted maximum number of retries";
case PAM_NEW_AUTHTOK_REQD: return "Authentication token is no longer valid";
case PAM_ACCT_EXPIRED: return "Account expired";
case PAM_SESSION_ERR: return "Cannot make/remove an entry for the session";
case PAM_CRED_UNAVAIL: return "Authentication credentials unavailable";
case PAM_CRED_EXPIRED: return "Authentication credentials expired";
case PAM_CRED_ERR: return "Authentication credentials error";
case PAM_CONV_ERR: return "Conversation error";
case PAM_ABORT: return "Critical error - immediate abort";
default: return "Unknown PAM error";
}
}
int pam_setcred(pam_handle_t *pamh, int flags)
{
(void)pamh;
(void)flags;
return PAM_SUCCESS;
}
int pam_chauthtok(pam_handle_t *pamh, int flags)
{
(void)pamh;
(void)flags;
return PAM_AUTHTOK_ERR;
}
int pam_putenv(pam_handle_t *pamh, const char *name_value)
{
(void)pamh;
(void)name_value;
return PAM_SUCCESS;
}
char **pam_getenvlist(pam_handle_t *pamh)
{
(void)pamh;
char **envlist = malloc(sizeof(char *));
if (envlist) envlist[0] = NULL;
return envlist;
}
const char *pam_getenv(pam_handle_t *pamh, const char *name)
{
(void)pamh;
(void)name;
return NULL;
}
@@ -0,0 +1,116 @@
#ifndef PAM_APPL_H
#define PAM_APPL_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef int pam_item_type;
#define PAM_SUCCESS 0
#define PAM_OPEN_ERR 1
#define PAM_SYMBOL_ERR 2
#define PAM_SERVICE_ERR 3
#define PAM_SYSTEM_ERR 4
#define PAM_BUF_ERR 5
#define PAM_PERM_DENIED 6
#define PAM_AUTH_ERR 7
#define PAM_CRED_INSUFFICIENT 8
#define PAM_AUTHINFO_UNAVAIL 9
#define PAM_USER_UNKNOWN 10
#define PAM_MAXTRIES 11
#define PAM_NEW_AUTHTOK_REQD 12
#define PAM_ACCT_EXPIRED 13
#define PAM_SESSION_ERR 14
#define PAM_CRED_UNAVAIL 15
#define PAM_CRED_EXPIRED 16
#define PAM_CRED_ERR 17
#define PAM_NO_MODULE_DATA 18
#define PAM_CONV_ERR 19
#define PAM_AUTHTOK_ERR 20
#define PAM_ABORT 21
#define PAM_AUTHTOK_RECOVER_ERR 22
#define PAM_AUTHTOK_LOCK_BUSY 23
#define PAM_AUTHTOK_DISABLE_AGING 24
#define PAM_TRY_AGAIN 25
#define PAM_IGNORE 26
#define PAM_MODULE_UNKNOWN 27
#define PAM_AUTHTOK_EXPIRED 28
#define PAM_BAD_ITEM 29
#define PAM_SERVICE 1
#define PAM_USER 2
#define PAM_TTY 3
#define PAM_RHOST 4
#define PAM_CONV 5
#define PAM_RUSER 8
#define PAM_SILENT 0x8000
#define PAM_DISALLOW_NULL_AUTHTOK 0x0001
#define PAM_ESTABLISH_CRED 0x0002
#define PAM_DELETE_CRED 0x0004
#define PAM_REINITIALIZE_CRED 0x0008
#define PAM_REFRESH_CRED 0x0010
#define PAM_CHANGE_EXPIRED_AUTHTOK 0x0020
#define PAM_MAX_NUM_MSG 32
#define PAM_PROMPT_ECHO_OFF 1
#define PAM_PROMPT_ECHO_ON 2
#define PAM_ERROR_MSG 3
#define PAM_TEXT_INFO 4
struct pam_message {
int msg_style;
const char *msg;
};
struct pam_response {
char *resp;
int resp_retcode;
};
struct pam_conv {
int (*conv)(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr);
void *appdata_ptr;
};
typedef struct pam_handle pam_handle_t;
int pam_start(const char *service, const char *user,
const struct pam_conv *conv, pam_handle_t **pamh);
int pam_end(pam_handle_t *pamh, int pam_status);
int pam_set_item(pam_handle_t *pamh, int item_type, const void *item);
int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item);
int pam_authenticate(pam_handle_t *pamh, int flags);
int pam_acct_mgmt(pam_handle_t *pamh, int flags);
int pam_open_session(pam_handle_t *pamh, int flags);
int pam_close_session(pam_handle_t *pamh, int flags);
const char *pam_strerror(pam_handle_t *pamh, int errnum);
int pam_setcred(pam_handle_t *pamh, int flags);
int pam_chauthtok(pam_handle_t *pamh, int flags);
int pam_putenv(pam_handle_t *pamh, const char *name_value);
char **pam_getenvlist(pam_handle_t *pamh);
const char *pam_getenv(pam_handle_t *pamh, const char *name);
#ifdef __cplusplus
}
#endif
#endif
@@ -198,6 +198,54 @@
#endif
#endif
#ifdef Q_OS_REDOX
#undef QT_USE_XOPEN_LFS_EXTENSIONS
#undef QT_LARGEFILE_SUPPORT
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
#endif
#ifdef Q_OS_REDOX
#undef QT_USE_XOPEN_LFS_EXTENSIONS
#undef QT_LARGEFILE_SUPPORT
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
#endif
#ifdef Q_OS_REDOX
#undef QT_USE_XOPEN_LFS_EXTENSIONS
#undef QT_LARGEFILE_SUPPORT
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
#endif
#ifdef Q_OS_REDOX
#undef QT_USE_XOPEN_LFS_EXTENSIONS
#undef QT_LARGEFILE_SUPPORT
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
#endif
#ifdef Q_OS_REDOX
#undef QT_USE_XOPEN_LFS_EXTENSIONS
#undef QT_LARGEFILE_SUPPORT
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
#endif
#ifdef Q_OS_REDOX
#undef QT_USE_XOPEN_LFS_EXTENSIONS
#undef QT_LARGEFILE_SUPPORT
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
#endif
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
@@ -1375,6 +1375,48 @@ qt_internal_extend_target(Core CONDITION REDOX
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_cpp_winrt
SOURCES
platform/windows/qfactorycacheregistration_p.h
@@ -1578,6 +1620,48 @@ qt_internal_extend_target(Core CONDITION REDOX
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_itemmodel
SOURCES
itemmodels/qabstractitemmodel.cpp itemmodels/qabstractitemmodel.h itemmodels/qabstractitemmodel_p.h
@@ -202,6 +202,12 @@ static_assert(std::is_signed_v<qint128>,
#include <assert.h>
#include <assert.h>
#include <assert.h>
#include <assert.h>
#include <assert.h>
#include <assert.h>
#include <assert.h>
#include <assert.h>
#include <assert.h>
#ifndef static_assert
#define static_assert _Static_assert
#endif
@@ -1146,6 +1146,12 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
if (header.hopLimit != -1) {
msg.msg_controllen += CMSG_SPACE(sizeof(int));
@@ -1179,6 +1185,12 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
#endif
#endif
#endif
#endif
#endif
#endif
#endif
#endif
#endif
#endif
if (header.ifindex != 0 || !header.senderAddress.isNull()) {
struct in6_pktinfo *data = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cmsgptr));
@@ -46,6 +46,12 @@
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#if defined(Q_OS_VXWORKS)
@@ -76,6 +76,12 @@ public:
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
virtual QPlatformOpenGLContext *createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const = 0;
#endif /* QT_CONFIG(opengl) */
@@ -102,6 +108,12 @@ public:
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
virtual bool canCreatePlatformOffscreenSurface() const { return false; }
#if QT_CONFIG(opengl)
@@ -139,6 +151,12 @@ public:
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
virtual void *nativeResourceForContext(NativeResource /*resource*/, QPlatformOpenGLContext */*context*/) { return nullptr; }
#endif /* QT_CONFIG(opengl) */
@@ -166,6 +184,12 @@ public:
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
};
}
@@ -6,6 +6,7 @@ edition = "2024"
[dependencies]
redox-scheme = "0.11"
redox_syscall = "0.7"
libredox = "0.1"
libc = "0.2"
[profile.release]
+263 -69
View File
@@ -1,10 +1,17 @@
use std::env;
use std::fs;
use std::io::{Read, Write};
use std::os::unix::net::UnixListener;
use std::thread;
use std::time::Duration;
use redox_scheme::scheme::SchemeSync;
use redox_scheme::scheme::SchemeState;
use redox_scheme::{CallerCtx, OpenResult, SignalBehavior, Socket};
use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT};
use syscall::flag::O_ACCMODE;
use syscall::schemev2::NewFdFlags;
const POLL_MS: u64 = 2000;
const SCHEME_ROOT_ID: usize = 0;
const IA32_THERM_STATUS: u32 = 0x19c;
const IA32_TEMPERATURE_TARGET: u32 = 0x1a2;
@@ -19,7 +26,8 @@ enum Vendor {
fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
let path = format!("/scheme/sys/msr/{}/{:x}", cpu, msr);
fs::read_to_string(&path).ok()
fs::read_to_string(&path)
.ok()
.and_then(|s| u64::from_str_radix(s.trim(), 16).ok())
}
@@ -39,27 +47,34 @@ fn detect_cpus() -> Vec<u32> {
for l in d.lines() {
if let Some(id_str) = l.strip_prefix("CPU ") {
if let Some((num, _)) = id_str.split_once(':') {
if let Ok(id) = num.trim().parse() { v.push(id); }
if let Ok(id) = num.trim().parse::<u32>() {
v.push(id);
}
}
}
}
}
if v.is_empty() { v.push(0); }
if v.is_empty() {
v.push(0);
}
v
}
fn read_temperature_intel(cpu: u32, tjmax: u8) -> Option<i16> {
let raw = read_msr(cpu, IA32_THERM_STATUS)?;
let digital_readout = ((raw >> 16) & 0x7F) as u8;
if digital_readout == 0 { return None; }
let temp = tjmax.saturating_sub(digital_readout);
Some(temp as i16)
if digital_readout == 0 {
return None;
}
Some(tjmax.saturating_sub(digital_readout) as i16)
}
fn read_tjmax_intel(cpu: u32) -> u8 {
if let Some(raw) = read_msr(cpu, IA32_TEMPERATURE_TARGET) {
let tj = ((raw >> 16) & 0xFF) as u8;
if tj > 0 && tj < 150 { return tj; }
if tj > 0 && tj < 150 {
return tj;
}
}
100
}
@@ -67,9 +82,10 @@ fn read_tjmax_intel(cpu: u32) -> u8 {
fn read_temperature_amd(cpu: u32) -> Option<i16> {
let raw = read_msr(cpu, AMD_TCTL)?;
let tctl = ((raw >> 21) & 0x3FF) as u16;
if tctl == 0 { return None; }
let temp = (tctl as f32) / 8.0;
Some(temp as i16)
if tctl == 0 {
return None;
}
Some(((tctl as f32) / 8.0) as i16)
}
#[derive(Clone)]
@@ -79,78 +95,256 @@ struct CpuInfo {
tjmax: u8,
}
fn main() {
let scheme_path = ":coretemp";
let _ = fs::remove_file(scheme_path);
let listener = UnixListener::bind(scheme_path).expect("bind scheme");
eprintln!("[INFO] coretempd: starting");
#[derive(Clone)]
enum Handle {
Listing,
CpuTemp { cpu_id: u32 },
}
let cpus = detect_cpus();
eprintln!("[INFO] coretempd: detected {} CPU(s)", cpus.len());
struct CoretempScheme {
cpus: Vec<CpuInfo>,
next_handle: usize,
handles: Vec<Option<Handle>>,
}
let cpu_infos: Vec<CpuInfo> = cpus.iter().map(|&id| {
let vendor = detect_vendor(id);
let tjmax = if vendor == Vendor::Intel {
read_tjmax_intel(id)
impl CoretempScheme {
fn new(cpus: Vec<CpuInfo>) -> Self {
Self {
cpus,
next_handle: 1,
handles: vec![None],
}
}
fn alloc_handle(&mut self, h: Handle) -> usize {
let id = self.next_handle;
self.handles.push(Some(h));
self.next_handle += 1;
id
}
fn get_handle(&self, id: usize) -> Result<&Handle> {
self.handles
.get(id)
.and_then(|opt| opt.as_ref())
.ok_or_else(|| Error::new(EBADF))
}
fn format_listing(&self) -> String {
let mut out = String::new();
for info in &self.cpus {
let temp = match info.vendor {
Vendor::Intel => read_temperature_intel(info.id, info.tjmax),
Vendor::Amd => read_temperature_amd(info.id),
Vendor::Unknown => None,
};
match temp {
Some(t) => out.push_str(&format!("cpu{}: {}C\n", info.id, t)),
None => out.push_str(&format!("cpu{}: N/A\n", info.id)),
}
}
out
}
}
impl SchemeSync for CoretempScheme {
fn scheme_root(&mut self) -> Result<usize> {
Ok(SCHEME_ROOT_ID)
}
fn openat(
&mut self,
dirfd: usize,
path: &str,
flags: usize,
_fcntl_flags: u32,
_ctx: &CallerCtx,
) -> Result<OpenResult> {
if flags & O_ACCMODE != 0 {
return Err(Error::new(EINVAL));
}
let handle = if dirfd == SCHEME_ROOT_ID {
let trimmed = path.trim_start_matches('/');
if trimmed.is_empty() {
Handle::Listing
} else if let Some(cpu_str) = trimmed.strip_prefix("cpu") {
let cpu_id = cpu_str
.trim_end_matches('/')
.parse::<u32>()
.map_err(|_| Error::new(ENOENT))?;
if self.cpus.iter().any(|c| c.id == cpu_id) {
Handle::CpuTemp { cpu_id }
} else {
return Err(Error::new(ENOENT));
}
} else {
return Err(Error::new(ENOENT));
}
} else {
0
return Err(Error::new(ENOENT));
};
eprintln!("[INFO] coretempd: CPU {} = {:?}", id, vendor);
CpuInfo { id, vendor, tjmax }
}).collect();
let infos_clone = cpu_infos.clone();
thread::spawn(move || {
loop {
thread::sleep(Duration::from_millis(POLL_MS));
for info in &infos_clone {
Ok(OpenResult::ThisScheme {
number: self.alloc_handle(handle),
flags: NewFdFlags::empty(),
})
}
fn read(
&mut self,
id: usize,
buf: &mut [u8],
offset: u64,
_flags: u32,
_ctx: &CallerCtx,
) -> Result<usize> {
let data = match self.get_handle(id)? {
Handle::Listing => self.format_listing(),
Handle::CpuTemp { cpu_id } => {
let info = self
.cpus
.iter()
.find(|c| c.id == *cpu_id)
.ok_or_else(|| Error::new(ENOENT))?;
let temp = match info.vendor {
Vendor::Intel => read_temperature_intel(info.id, info.tjmax),
Vendor::Amd => read_temperature_amd(info.id),
Vendor::Unknown => None,
};
if let Some(t) = temp {
let _ = fs::write(format!("/tmp/coretemp_cpu{}", info.id), format!("{}\n", t));
match temp {
Some(t) => format!("{}\n", t),
None => "N/A\n".to_string(),
}
}
};
let bytes = data.as_bytes();
let off = usize::try_from(offset).map_err(|_| Error::new(EINVAL))?;
if off >= bytes.len() {
return Ok(0);
}
let count = (bytes.len() - off).min(buf.len());
buf[..count].copy_from_slice(&bytes[off..off + count]);
Ok(count)
}
fn write(
&mut self,
_id: usize,
_buf: &[u8],
_offset: u64,
_flags: u32,
_ctx: &CallerCtx,
) -> Result<usize> {
Err(Error::new(EINVAL))
}
fn on_close(&mut self, id: usize) {
if id < self.handles.len() {
self.handles[id] = None;
}
}
}
#[cfg(target_os = "redox")]
fn init_notify_fd() -> Option<i32> {
env::var("INIT_NOTIFY").ok()?.parse::<i32>().ok()
}
#[cfg(target_os = "redox")]
fn notify_scheme_ready(
notify_fd: i32,
socket: &Socket,
scheme: &mut CoretempScheme,
) -> Result<(), String> {
let cap_id = scheme
.scheme_root()
.map_err(|err| format!("coretempd: scheme_root failed: {err}"))?;
let cap_fd = socket
.create_this_scheme_fd(0, cap_id, 0, 0)
.map_err(|err| format!("coretempd: create_this_scheme_fd failed: {err}"))?;
syscall::call::write(
notify_fd as usize,
&libredox::Fd::new(cap_fd).into_raw().to_ne_bytes(),
)
.map_err(|err| format!("coretempd: failed to notify init: {err}"))?;
Ok(())
}
#[cfg(target_os = "redox")]
fn run_daemon() -> Result<(), String> {
let notify_fd = init_notify_fd();
let cpus = detect_cpus();
eprintln!("[INFO] coretempd: detected {} CPU(s)", cpus.len());
let cpu_infos: Vec<CpuInfo> = cpus
.iter()
.map(|&id| {
let vendor = detect_vendor(id);
let tjmax = if vendor == Vendor::Intel {
read_tjmax_intel(id)
} else {
0
};
eprintln!("[INFO] coretempd: CPU {} = {:?}", id, vendor);
CpuInfo { id, vendor, tjmax }
})
.collect();
let socket = Socket::create()
.map_err(|err| format!("coretempd: failed to create scheme socket: {err}"))?;
let mut state = redox_scheme::scheme::SchemeState::new();
let mut scheme = CoretempScheme::new(cpu_infos);
if let Some(fd) = notify_fd {
notify_scheme_ready(fd, &socket, &mut scheme)?;
}
eprintln!("[INFO] coretempd: registered scheme:coretemp");
let infos_clone = scheme.cpus.clone();
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(POLL_MS));
for info in &infos_clone {
let temp = match info.vendor {
Vendor::Intel => read_temperature_intel(info.id, info.tjmax),
Vendor::Amd => read_temperature_amd(info.id),
Vendor::Unknown => None,
};
if let Some(t) = temp {
let _ = fs::write(format!("/tmp/coretemp_cpu{}", info.id), format!("{}\n", t));
}
}
});
for stream in listener.incoming() {
if let Ok(mut stream) = stream {
let mut buf = [0u8; 64];
if let Ok(n) = stream.read(&mut buf) {
let req = String::from_utf8_lossy(&buf[..n]).trim().to_string();
let resp = if req == "/" {
let mut names = String::new();
for info in &cpu_infos {
names.push_str(&format!("cpu{}\n", info.id));
}
names
} else if let Some(cpu_str) = req.strip_prefix("/cpu") {
if let Ok(cpu) = cpu_str.parse::<u32>() {
if let Some(info) = cpu_infos.iter().find(|i| i.id == cpu) {
let temp = match info.vendor {
Vendor::Intel => read_temperature_intel(info.id, info.tjmax),
Vendor::Amd => read_temperature_amd(info.id),
Vendor::Unknown => None,
};
if let Some(t) = temp {
format!("{}\n", t)
} else {
"N/A\n".to_string()
}
} else {
"N/A\n".to_string()
}
} else {
"N/A\n".to_string()
}
} else {
"N/A\n".to_string()
};
let _ = stream.write_all(resp.as_bytes());
}
loop {
let request = socket
.next_request(SignalBehavior::Restart)
.map_err(|err| format!("coretempd: failed to read scheme request: {err}"))?;
let Some(request) = request else {
return Ok(());
};
if let redox_scheme::RequestKind::Call(request) = request.kind() {
let response = request.handle_sync(&mut scheme, &mut state);
socket
.write_response(response, SignalBehavior::Restart)
.map_err(|err| format!("coretempd: failed to write response: {err}"))?;
}
}
}
#[cfg(not(target_os = "redox"))]
fn run_daemon() -> Result<(), String> {
eprintln!("[INFO] coretempd: not running on Redox, exiting");
Ok(())
}
fn main() {
if let Err(e) = run_daemon() {
eprintln!("[ERROR] coretempd: {e}");
std::process::exit(1);
}
}
@@ -49,6 +49,42 @@ impl log::Log for StderrLogger {
fn flush(&self) {}
}
/// Wait for a scheme to become available by polling `/scheme/{name}`.
/// Returns true if the scheme appeared within the timeout, false otherwise.
///
/// In initfs mode, driver-manager starts before pcid and acpid. The PCI bus
/// scheme (`/scheme/pci`) is registered by pcid, which is spawned by hwd
/// (service 40). The ACPI bus scheme (`/scheme/acpi`) is registered by acpid
/// (service 41). Without waiting, enumeration fails with 0 devices and the
/// deferred retry loop (3 × 500ms) is too short.
fn wait_for_scheme(name: &str, timeout: Duration) -> bool {
let path = format!("/scheme/{}", name);
let start = Instant::now();
let poll_interval = Duration::from_millis(100);
while start.elapsed() < timeout {
// Use read_dir instead of Path::exists() — Redox scheme paths
// may not respond to exists()/metadata() while still being
// functional for directory enumeration.
if fs::read_dir(&path).is_ok() {
log::info!(
"scheme {} available after {}ms",
name,
start.elapsed().as_millis()
);
return true;
}
thread::sleep(poll_interval);
}
log::warn!(
"scheme {} not available after {}ms",
name,
timeout.as_millis()
);
false
}
fn run_enumeration(
manager: &Arc<Mutex<DeviceManager>>,
scheme: &DriverManagerScheme,
@@ -421,6 +457,14 @@ fn main() {
reset_timeline_log();
// In initfs mode, pcid (spawned by hwd at service 40) may not have
// registered /scheme/pci yet when driver-manager starts at service 00.
// Wait for required bus schemes to appear before enumerating.
if initfs {
wait_for_scheme("pci", Duration::from_secs(30));
wait_for_scheme("acpi", Duration::from_secs(10));
}
if manager_config.async_probe {
let handle = thread::spawn(move || {
let (bound, deferred) = run_enumeration(&mgr_clone, scheme_clone.as_ref(), initfs);
@@ -454,7 +498,7 @@ fn main() {
idle_forever();
}
let max_retries = 3u32;
let max_retries = if initfs { 10u32 } else { 3u32 };
for retry in 1..=max_retries {
if SHUTDOWN_REQUESTED.load(Ordering::SeqCst) {
log::info!("driver-manager: SIGTERM received during deferred retry, shutting down");
@@ -212,17 +212,6 @@ fn main() {
);
let firmware_dir_str = firmware_dir.to_string_lossy().into_owned();
match manifest::generate_manifest(&firmware_dir_str) {
Ok(()) => info!(
"firmware-loader: generated firmware manifest at {}/MANIFEST.txt",
firmware_dir.display()
),
Err(err) => warn!(
"firmware-loader: failed to generate firmware manifest for {}: {}",
firmware_dir.display(),
err
),
}
let registry = match FirmwareRegistry::new(&firmware_dir) {
Ok(registry) => registry,
@@ -245,6 +234,14 @@ fn main() {
firmware_dir.display()
);
// Manifest generation is a host-side verification tool — skip it at runtime.
// The background thread would produce thousands of ENODEV warnings after
// setrens(0,0) makes the filesystem inaccessible, and the manifest is not
// consumed by any runtime path.
// std::thread::spawn(move || {
// let _ = manifest::generate_manifest(&firmware_dir_str);
// });
if args.first().map(String::as_str) == Some("--probe") {
println!("count={}", registry.len());
let mut keys = registry.list_keys();
@@ -25,24 +25,18 @@ tar -xf "${ARCHIVE}" -C "${EXTRACTED}" --strip-components=1
mkdir -p "${COOKBOOK_STAGE}/lib/firmware"
mkdir -p "${COOKBOOK_STAGE}/lib/firmware/LICENSES"
# Install all firmware payloads from linux-firmware while keeping license metadata separate.
while IFS= read -r -d '' file; do
rel="${file#${EXTRACTED}/}"
case "$(basename "$rel")" in
README|README.md|Makefile|check_whence.py)
continue
;;
LICENCE*|LICENSE*)
install -Dm0644 "$file" "${COOKBOOK_STAGE}/lib/firmware/LICENSES/$(basename "$rel")"
continue
;;
WHENCE)
install -Dm0644 "$file" "${COOKBOOK_STAGE}/lib/firmware/LICENSES/WHENCE"
continue
;;
esac
install -Dm0644 "$file" "${COOKBOOK_STAGE}/lib/firmware/${rel}"
done < <(find "${EXTRACTED}" -type f -print0)
install -Dm0644 "${EXTRACTED}/WHENCE" "${COOKBOOK_STAGE}/lib/firmware/LICENSES/WHENCE"
for lic in "${EXTRACTED}"/LICENCE* "${EXTRACTED}"/LICENSE*; do
[ -f "$lic" ] && install -Dm0644 "$lic" "${COOKBOOK_STAGE}/lib/firmware/LICENSES/$(basename "$lic")"
done
FIRMWARE_DIRS="amdgpu radeon i915 iwlwifi"
for dir in ${FIRMWARE_DIRS}; do
if [ -d "${EXTRACTED}/${dir}" ]; then
mkdir -p "${COOKBOOK_STAGE}/lib/firmware/${dir}"
find "${EXTRACTED}/${dir}" -type f -exec install -Dm0644 {} "${COOKBOOK_STAGE}/lib/firmware/{}" \\;
fi
done
cat > "${COOKBOOK_STAGE}/lib/firmware/LICENSES/index.txt" <<'EOF'
Red Bear firmware bundle
@@ -30,10 +30,12 @@ fi
mkdir -p "$XDG_RUNTIME_DIR"
drm_scheme_ready() {
# Try to open /scheme/drm/card0 via read (head -c 1).
# On Redox, stat and test -e are unreliable for scheme paths,
# but opening the scheme file descriptor works.
( head -c 1 "/scheme/drm/card0" ) >/dev/null 2>&1
# Check if /scheme/drm/card0 can be opened. On Redox, stat and test -e
# are unreliable for scheme paths. Opening the scheme path with redir
# succeeds if the scheme daemon has registered the device, fails otherwise.
# Do NOT read from it (head -c 1) — DRM scheme fds are request/response,
# not streaming, so a read would block waiting for a request response.
( exec 3<"/scheme/drm/card0" && exec 3>&- ) >/dev/null 2>&1
}
wait_for_drm_scheme() {
@@ -90,7 +92,8 @@ if wait_for_drm_scheme; then
echo "redbear-greeter-compositor: env DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS:-unset} DBUS_SYSTEM_BUS_ADDRESS=${DBUS_SYSTEM_BUS_ADDRESS:-unset}" >&2
echo "redbear-greeter-compositor: launching $COMPOSITOR" >&2
QT_QPA_PLATFORM=offscreen "$COMPOSITOR"
unset QT_QPA_PLATFORM
"$COMPOSITOR"
EXIT_CODE=$?
echo "redbear-greeter-compositor: compositor exited with code $EXIT_CODE" >&2
exit $EXIT_CODE
@@ -93,7 +93,7 @@ kwin_mode="virtual"
# Redox scheme paths do not respond to stat()/test -e, but opening and
# reading a byte works. Use the same probe the compositor wrapper uses.
drm_scheme_ready() {
( head -c 1 "/scheme/drm/card0" ) >/dev/null 2>&1
( exec 3<"/scheme/drm/card0" && exec 3>&- ) >/dev/null 2>&1
}
set_kwin_mode() {
+4
View File
@@ -1,6 +1,10 @@
#!/usr/bin/env bash
# apply-patches.sh — Apply all Red Bear OS overlays on top of upstream Redox build system.
#
# DEPRECATION NOTICE: Patches are now applied atomically by 'repo fetch' via recipe.toml.
# This script is retained for: (1) build-system git patches, (2) recipe symlinks.
# Do NOT use this for recipe source patching — that is handled by the cookbook.
#
# Usage: ./local/scripts/apply-patches.sh [--force] [--dry-run]
#
# This script:
+2 -51
View File
@@ -124,57 +124,8 @@ stash_nested_repo_if_dirty() {
stash_nested_repo_if_dirty "$PROJECT_ROOT/recipes/core/relibc/source" "relibc"
if [ "$APPLY_PATCHES" = "1" ] && [ -z "${REDBEAR_RELEASE:-}" ]; then
echo ">>> Applying local patches..."
apply_patch_dir() {
local patch_dir="$1"
local target_dir="$2"
local label="$3"
if [ "$label" = "relibc" ] && [ -d "$target_dir/.git" ]; then
if ! git -C "$target_dir" diff --quiet || ! git -C "$target_dir" diff --cached --quiet || [ -n "$(git -C "$target_dir" ls-files --others --exclude-standard)" ]; then
echo " STASH relibc source (dirty nested checkout)"
rm -f "$target_dir/.git/index.lock"
git -C "$target_dir" stash push --all -m "build-redbear-auto-stash" > /dev/null 2>&1 || true
fi
fi
if [ ! -d "$patch_dir" ]; then
return 0
fi
for patch_file in "$patch_dir"/*.patch; do
[ -f "$patch_file" ] || continue
patch_name=$(basename "$patch_file")
if [ "$label" = "base" ] && [ "$patch_name" = "P0-acpid-power-methods.patch" ]; then
acpid_file="$target_dir/drivers/acpid/src/acpi.rs"
if [ -f "$acpid_file" ] && grep -q "pub fn evaluate_acpi_method(" "$acpid_file"; then
echo " SKIP $patch_name (ACPI power helper methods already present)"
continue
fi
fi
if [ ! -d "$target_dir" ]; then
echo " SKIP $patch_name ($label source not fetched yet)"
continue
fi
if patch --dry-run -p1 -d "$target_dir" < "$patch_file" > /dev/null 2>&1; then
patch -p1 -d "$target_dir" < "$patch_file" > /dev/null 2>&1
echo " OK $patch_name"
else
echo " SKIP $patch_name (already applied or won't apply)"
fi
done
}
apply_patch_dir "$PROJECT_ROOT/local/patches/kernel" "$PROJECT_ROOT/recipes/core/kernel/source" "kernel"
apply_patch_dir "$PROJECT_ROOT/local/patches/base" "$PROJECT_ROOT/recipes/core/base/source" "base"
apply_patch_dir "$PROJECT_ROOT/local/patches/relibc" "$PROJECT_ROOT/recipes/core/relibc/source" "relibc"
apply_patch_dir "$PROJECT_ROOT/local/patches/bootloader" "$PROJECT_ROOT/recipes/core/bootloader/source" "bootloader"
apply_patch_dir "$PROJECT_ROOT/local/patches/installer" "$PROJECT_ROOT/recipes/core/installer/source" "installer"
stash_nested_repo_if_dirty "$PROJECT_ROOT/recipes/core/relibc/source" "relibc"
echo ">>> Patches are applied by 'repo fetch' via recipe.toml (atomic mechanism)"
echo ">>> Skipping direct patch application (was bypassing cookbook atomicity)"
echo ""
elif [ -n "${REDBEAR_RELEASE:-}" ]; then
echo ">>> Release mode: skipping patch application (patches pre-applied in archived sources)"
+9
View File
@@ -12,6 +12,15 @@ VENDOR="amd"
SUBSET="all"
COPIED_COUNT=0
# Offline gate: this script downloads from the network.
# Block if REPO_OFFLINE=1 (the default during builds).
if [ "${REPO_OFFLINE:-1}" = "1" ] && [ -z "${REDBEAR_ALLOW_UPSTREAM:-}" ]; then
echo "ERROR: fetch-firmware.sh requires network access but REPO_OFFLINE=1." >&2
echo " Set REPO_OFFLINE=0 or pass REDBEAR_ALLOW_UPSTREAM=1 to override." >&2
echo " This script is manual-only — it is never called by 'make all' or 'make live'." >&2
exit 1
fi
usage() {
cat <<EOF
Usage: $(basename "$0") [--vendor amd|intel] [--subset all|rdna|dmc|wifi|bluetooth]

Some files were not shown because too many files have changed in this diff Show More