Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 520e92cad8 | |||
| aa9d14a90e | |||
| 44bcf2b75a | |||
| 7cd5bfbb83 | |||
| 5987fffde7 | |||
| 706050482b | |||
| daf131d435 | |||
| 0ccc233131 | |||
| c0a93e5cfa | |||
| 0a4a77a56b | |||
| d2c761a56c | |||
| f40b751bca | |||
| ce9ff8aebd | |||
| bb3ae6e63f | |||
| 61135b0cce | |||
| 9db9c3bdc9 | |||
| 845ae99f9d | |||
| 5c5f853192 | |||
| ece9837d15 | |||
| d26675708e | |||
| 2d11c98428 | |||
| 5c127bf6f4 | |||
| a0244075e7 | |||
| 2b11b20a2f | |||
| cb50169517 | |||
| 328d1abbcd |
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 = """
|
||||
|
||||
@@ -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
@@ -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,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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 0–3 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
@@ -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)
|
||||
```
|
||||
@@ -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(×tamp),
|
||||
+ 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);
|
||||
+ }
|
||||
+ }
|
||||
@@ -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"] }
|
||||
@@ -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,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
+1
Submodule local/recipes/kde/sddm/source added at bc9eee8280
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Executable
+245
@@ -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,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
|
||||
@@ -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)
|
||||
|
||||
+24
@@ -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]
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user