Merge master into 0.2.0 (688 commits, theirs pref for conflicts)

This commit is contained in:
2026-05-21 21:37:43 +03:00
3898 changed files with 4062828 additions and 147781 deletions
+2 -247
View File
@@ -19,113 +19,7 @@ human-initiated operations. Durable Red Bear state belongs in `local/patches/`,
The current baseline is **Red Bear OS 0.1.0** (Redox snapshot at build-system commit `f55acba68`).
All recipe sources are pinned and archived in `sources/redbear-0.1.0/`.
## BUILD SYSTEM DURABILITY — THE CARDINAL RULE
**THE `recipes/*/source/` DIRECTORY WILL ALWAYS BE REWRITTEN. DO NOT EVER USE IT FOR ANY
WORK THAT YOU INTEND TO KEEP. THOSE TREES ARE EPHEMERAL — THEY ARE DESTROYED AND REGENERATED
ON EVERY `repo fetch`, `repo cook`, `make clean`, AND `make distclean`. ANY EDIT MADE THERE
WILL BE SILENTLY LOST ON THE NEXT BUILD. COMMITTING TO A SUBMODULE INSIDE `source/` DOES NOT
PROTECT YOUR WORK — THE ENTIRE DIRECTORY IS DELETED AND RE-CLONED/RE-EXTRACTED FROM SCRATCH.**
This is the #1 mistake AI agents and new contributors make. It has caused repeated work loss
in this project. The rule is:
| What you want to do | Where to do it |
|---|---|
| Change a kernel source file | Create or update a patch in `local/patches/kernel/` |
| Change an init or daemon source file | Create or update a patch in `local/patches/base/` |
| Change relibc | Create or update a patch in `local/patches/relibc/` |
| Change a driver | Create or update a patch in `local/patches/base/` or `local/patches/<driver>/` |
| Add a new package | Create a recipe in `local/recipes/<category>/<name>/` |
| Change build config | Edit `config/redbear-*.toml` |
| Add documentation | Write to `local/docs/` |
### How the build system works
```
repo cook <package>
├── repo fetch <package>
│ ├── Clone/fetch upstream source → recipes/<pkg>/source/
│ ├── Apply patches from recipe.toml → patches are read from local/patches/<pkg>/
│ └── Source tree is now fully patched and ready for build
├── Cargo/cmake/configure build
└── Stage artifacts into sysroot
```
The `source/` directory is a disposable working copy. It is produced at the start of every
build by cloning the upstream source + applying patches sequentially. The recipe's
`patches = [...]` list in `recipe.toml` controls which patches are applied.
### Two-layer architecture
```
Layer 1: Ephemeral (destroyed on clean/fetch/rebuild)
recipes/<pkg>/source/ ← working tree, cloned + patched
build/ ← build outputs
target/ ← cargo target dir
Layer 2: Durable (survives clean/fetch/rebuild/release provisioning)
local/patches/<pkg>/ ← .patch files — the actual source code changes
local/recipes/<pkg>/ ← custom recipe directories
config/redbear-*.toml ← Red Bear OS build configs
local/docs/ ← planning and integration docs
recipes/<pkg>/recipe.toml ← the patches list (git-tracked)
```
### The correct workflow for any source change
1. **Make the change** in `recipes/<pkg>/source/` to validate it compiles
2. **Generate a patch**: `cd recipes/<pkg>/source && git diff > ../../../local/patches/<pkg>/my-fix.patch`
3. **Wire the patch**: add `"my-fix.patch"` to the recipe's `recipe.toml` `patches = [...]` list
4. **Validate**: `./target/release/repo validate-patches <pkg>`
5. **Rebuild**: `./target/release/repo cook <pkg>`
6. **Commit**: `git add local/patches/ recipes/<pkg>/recipe.toml && git commit`
### Common anti-patterns
| Anti-pattern | Why it fails |
|---|---|
| Editing `source/` files then running `make all` | `make all` calls `repo fetch` which regenerates `source/` — edits are lost |
| Creating a patch but not wiring it into `recipe.toml` | Patch file exists but is never applied — build uses unpatched source |
| **Hand-writing patches manually** | **FORBIDDEN. Unified diffs hand-written by humans routinely have incorrect line counts, wrong context, malformed hunks, or timestamp headers — all of which cause `patch(1)` to reject them. The ONLY acceptable way to generate patches is `git diff -U0 -w` from a committed source tree baseline.** |
| Editing `recipe.toml` patches list without creating the actual `.patch` file | Build fails with "missing patch" error |
| Editing `recipe.toml` patches list without creating the actual `.patch` file | Build fails with "missing patch" error |
| Expecting `source/` changes to survive `make clean` | `make clean` deletes `source/` directories |
| Running `repo cook` without `--allow-protected` for core packages | Protected recipes (kernel, relibc, base) are offline-only by default |
### Patch file location convention
- `local/patches/base/` — for the `base` package (init, daemon, all drivers)
- `local/patches/kernel/` — for the kernel
- `local/patches/relibc/` — for relibc
- `local/patches/installer/` — for the installer
- `local/patches/bootloader/` — for the bootloader
- `local/patches/<package>/` — for any other patched package
### Recipe patch wiring
Each recipe's `recipe.toml` lists patches relative to `local/patches/<pkg>/`:
```toml
[source]
git = "https://gitlab.redox-os.org/redox-os/base.git"
rev = "463f76b96..."
patches = [
"P0-daemon-fix-init-notify-unwrap.patch", # applied first
"P9-init-scheduler-completed.patch", # applied second
# ... more patches
]
```
Patches are applied in listed order. Dependencies between patches must be respected (a patch
that defines a type must come before a patch that uses it).
### Kernel-specific notes
The kernel source at `recipes/core/kernel/source/` is a separate git worktree (rev `866dfad`).
The kernel recipe is at `recipes/core/kernel/recipe.toml` and patches are at
`local/patches/kernel/`. The same durability rules apply — all kernel changes must be
in `local/patches/kernel/*.patch`, never in the `source/` tree directly.
## NO SILENT UPSTREAM PULLS — OFFLINE-FIRST POLICY
**Red Bear OS is offline-first by default. No script, build target, or tool may silently pull
from any upstream repository without explicit user instruction.**
@@ -284,24 +178,10 @@ make all
→ mk/fstools.mk (build cookbook repo binary + fstools)
→ mk/repo.mk (repo cook --filesystem=config/*.toml)
→ For each recipe: fetch source → apply patches → build → stage into sysroot
→ Each successful build produces repo/<arch>/<name>.pkgar + <name>.toml
→ mk/disk.mk (create filesystem.img, harddrive.img, redbear-live.iso or harddrive.img)
→ redoxfs-mkfs → redox_installer → bootloader embedding
```
### Build Outputs
Every successful `repo cook <package>` produces:
| Artifact | Location | Purpose |
|----------|----------|---------|
| Package archive | `repo/x86_64-unknown-redox/<name>.pkgar` | Binary package for image assembly |
| Package manifest | `repo/x86_64-unknown-redox/<name>.toml` | Metadata, version, deps, hashes |
| Staged sysroot | `recipes/*/<name>/target/.../stage/` | Files for `repo push` |
| Source tree | `recipes/*/<name>/source/` | Fetched + patched source (disposable) |
**A build is not complete until the .pkgar and .toml exist in `repo/`.**
## CONVENTIONS
- **Rust edition 2024**, nightly channel
@@ -564,65 +444,6 @@ or any path that is already git-tracked and not inside a fetched source tree.
## BUILD SYSTEM POLICIES
### Build Durability Rule — Every Build Lands in the Repo
Every successful `repo cook` produces two durable artifacts:
1. **Package in the repo**: `repo/x86_64-unknown-redox/<name>.pkgar` + `<name>.toml`
2. **Patched source form**: All source modifications are in `local/patches/<component>/` and wired into `recipe.toml`
A build is **not complete** until both artifacts exist:
```bash
# After cooking, verify the package is in the repo
./target/release/repo find <package>
# Check the repo manifest exists
ls repo/x86_64-unknown-redox/<package>.toml
ls repo/x86_64-unknown-redox/<package>.pkgar
```
If a package was built but the repo artifacts are missing, the build did not complete.
Re-run `repo cook <package>` to regenerate them.
If source patches were applied but not mirrored to `local/patches/`, see the
DURABILITY POLICY section above.
### Cascade Rebuild Rule
When a low-level package changes (relibc, kernel, base, or any library), **all
packages that depend on it must be rebuilt**. A stale dependent silently produces
link errors, ABI mismatches, or runtime crashes.
Use the cascade rebuild script:
```bash
# Rebuild relibc and everything that depends on it
./local/scripts/rebuild-cascade.sh relibc
# Dry run: show what would be rebuilt without building
./local/scripts/rebuild-cascade.sh --dry-run relibc
# Multiple root packages
./local/scripts/rebuild-cascade.sh relibc ncurses
```
The script:
1. Finds all packages whose `recipe.toml` lists the target in `dependencies`
2. Transitively expands the reverse dependency graph (BFS)
3. Builds the root package(s) first, then dependents in order
4. Pushes all rebuilt packages to the sysroot
**When to use cascade rebuilds:**
- After changing relibc headers or ABI
- After rebuilding a shared library (ncurses, zlib, openssl, etc.)
- After kernel ABI changes that affect userspace
- After any change to a package listed in other packages' `dependencies`
**When NOT to use cascade rebuilds:**
- Standalone applications with no dependents (editors, games, utilities)
- Terminal/leaf packages that nothing depends on
### Atomic Patch Application
The cookbook tool (`src/cook/fetch.rs`) applies patches **atomically**:
@@ -645,78 +466,12 @@ Patches may use either format:
Git-specific headers (`diff --git`, `diff -ruN`, `index`, `new file mode`, `rename from/to`,
`similarity index`, `dissimilarity index`) are automatically stripped before
`patch` is invoked. The build system uses `--fuzz=3` for resilient context matching.
`patch` is invoked. The build system uses `--fuzz=0` for strict context matching.
**Timestamps in `---`/`+++` lines** (common in `diff -ruN` output) should be removed.
Use `--- a/path` and `+++ b/path` without timestamps. The `normalize_patch` function
does NOT strip timestamps — they should be removed from the patch file directly.
### Robust Patch Generation (REQUIRED)
**MANDATORY: All patches MUST be generated using `git diff -U0 -w` from a committed source tree.
Hand-writing unified diffs is FORBIDDEN — it routinely produces incorrect line counts, malformed
hunks, or timestamp headers that cause `patch(1)` to reject them. The build system uses
`--fuzz=3` for resilient context matching, which requires properly generated diffs.**
Context-line mismatches (renamed variables, shifted line numbers, upstream refactors)
are the single largest source of patch application failures. Use the zero-context,
whitespace-ignored technique to make patches resilient to drift:
**Workflow (mandatory):**
```bash
# 1. Start with a clean P0..P(N-1) source tree (repo fetch already applied earlier patches)
cd recipes/<component>/source
# 2. Commit the P0..P(N-1) state as a git baseline
git add -A && git commit -m "P0..P(N-1) baseline"
# 3. Make P(N) edits in the source tree
# (edit files, test compile, etc.)
# 4. Generate the P(N) patch using ONLY git diff -U0 -w:
git diff -U0 -w > ../../../local/patches/<component>/P<N>-<description>.patch
# 5. Wire the patch into recipe.toml patches list
# 6. Validate: repo validate-patches <package>
# 7. Rebuild: repo cook <package>
# 8. Commit: git add local/patches/ recipes/<pkg>/recipe.toml && git commit
```
**Apply (for manual testing):**
```bash
patch -p1 --fuzz=3 < local/patches/<component>/P<N>-<description>.patch>
```
**Why this works:**
- `-U0` produces zero lines of surrounding context, so the patch has no fragile context
lines that can drift when surrounding code changes
- `-w` ignores all whitespace changes, so indentation-only refactors don't break the patch
- `--fuzz=3` allows `patch(1)` to find the target location even when nearby lines have shifted
- Together these three flags eliminate the entire class of "context mismatch" failures
**Why hand-writing is forbidden:**
- Human-written diffs routinely have wrong `@@` line counts, missing or extra context lines,
incorrect `--- a/` / `+++ b/` paths, or embedded timestamps — all of which cause `patch(1)`
to reject the patch or silently apply it to the wrong location
- The `git diff -U0 -w` command produces mechanically correct diffs every time
**Before this technique**, patches routinely broke when:
- A variable was renamed (e.g., `deamon``daemon` in context)
- Lines were added or removed above the changed code
- Indentation was reformatted
- An earlier patch in the chain shifted line numbers
**With this technique**, patches survive all of the above. A hunk consists only of the
changed lines themselves — no context that can go stale.
**Conventions:**
- Always use `--- a/path` and `+++ b/path` headers (no timestamps)
- Always name patches `P<N>-<description>.patch` with sequential numbering
- Always wire patches into `recipe.toml` `patches = [...]` in application order
- Always validate with `repo validate-patches <package>` after creating or editing a patch
- When updating an existing patch, regenerate it entirely rather than editing line numbers manually
### Protected Recipes
Core recipes (`base`, `kernel`, `relibc`, `bootloader`, etc.) and any recipe carrying
+1 -1
View File
@@ -18,7 +18,7 @@ path = "/usr/lib/init.d/10_acid.service"
data = """
[unit]
description = "Acid test runner"
requires_weak = ["00_driver-manager.service"]
requires_weak = ["00_pcid-spawner.service"]
[service]
cmd = "ion"
+24 -128
View File
@@ -1,11 +1,6 @@
# Red Bear OS shared device-service wiring
#
# Shared by profiles that ship the firmware/input/Wi-Fi control compatibility stack.
#
# Driver matching: driver-manager reads /lib/drivers.d/*.toml and matches against
# devices from both PCI and ACPI buses. ACPI devices are classified with PCI-equivalent
# class/subclass/vendor codes by redox-driver-acpi's AcpiBus, allowing reuse of existing
# driver match rules.
[packages]
redbear-quirks = {}
@@ -37,9 +32,9 @@ data = """
path = "/etc/init.d/12_boot-late.target"
data = """
[unit]
description = "Late boot services target (compat alias for 04_drivers.target)"
description = "Late boot services target"
requires_weak = [
"04_drivers.target",
"00_base.target",
]
"""
@@ -59,7 +54,6 @@ priority = 100
command = ["/usr/lib/drivers/nvmed"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 8
@@ -70,7 +64,6 @@ priority = 100
command = ["/usr/lib/drivers/ahcid"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 6
@@ -81,7 +74,6 @@ priority = 100
command = ["/usr/lib/drivers/ided"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 1
@@ -92,7 +84,6 @@ priority = 100
command = ["/usr/lib/drivers/virtio-blkd"]
[[driver.match]]
bus = "pci"
vendor = 0x1AF4
device = 0x1001
class = 1
@@ -109,7 +100,6 @@ priority = 50
command = ["/usr/lib/drivers/e1000d"]
[[driver.match]]
bus = "pci"
vendor = 0x8086
class = 2
@@ -120,7 +110,6 @@ priority = 50
command = ["/usr/lib/drivers/rtl8168d"]
[[driver.match]]
bus = "pci"
vendor = 0x10EC
class = 2
@@ -131,7 +120,6 @@ priority = 50
command = ["/usr/lib/drivers/rtl8139d"]
[[driver.match]]
bus = "pci"
vendor = 0x10EC
device = 0x8139
@@ -142,7 +130,6 @@ priority = 50
command = ["/usr/lib/drivers/ixgbed"]
[[driver.match]]
bus = "pci"
vendor = 0x8086
class = 2
subclass = 0
@@ -154,7 +141,6 @@ priority = 50
command = ["/usr/lib/drivers/virtio-netd"]
[[driver.match]]
bus = "pci"
vendor = 0x1AF4
class = 2
"""
@@ -169,7 +155,6 @@ priority = 80
command = ["/usr/lib/drivers/xhcid"]
[[driver.match]]
bus = "pci"
class = 0x0C
subclass = 0x03
prog_if = 0x30
@@ -184,7 +169,6 @@ command = ["/usr/lib/drivers/ehcid"]
# control-transfer pass-through while the wider USB stack continues converging.
[[driver.match]]
bus = "pci"
class = 0x0C
subclass = 0x03
prog_if = 0x20
@@ -196,7 +180,6 @@ priority = 80
command = ["/usr/lib/drivers/ohcid"]
[[driver.match]]
bus = "pci"
class = 0x0C
subclass = 0x03
prog_if = 0x10
@@ -208,7 +191,6 @@ priority = 80
command = ["/usr/lib/drivers/uhcid"]
[[driver.match]]
bus = "pci"
class = 0x0C
subclass = 0x03
prog_if = 0x00
@@ -224,7 +206,6 @@ priority = 60
command = ["/usr/bin/redox-drm"]
[[driver.match]]
bus = "pci"
class = 0x03
"""
@@ -252,7 +233,6 @@ priority = 40
command = ["/usr/lib/drivers/ihdad"]
[[driver.match]]
bus = "pci"
vendor = 0x8086
class = 0x04
@@ -263,89 +243,10 @@ priority = 40
command = ["/usr/lib/drivers/ac97d"]
[[driver.match]]
bus = "pci"
class = 0x04
subclass = 0x01
"""
[[files]]
path = "/etc/init.d/00_acpid.service"
data = """
[unit]
description = "ACPI daemon (provides scheme:acpi)"
default_dependencies = false
[service]
cmd = "acpid"
inherit_envs = ["RSDP_ADDR", "RSDP_SIZE"]
type = "notify"
"""
# ACPI GPIO/I2C controller drivers
# These match against ACPI-enumerated devices (class/subclass/vendor from _HID).
[[files]]
path = "/lib/drivers.d/60-gpio-i2c.toml"
data = """
# I2C bus registry — infrastructure, no hardware match
[[driver]]
name = "i2cd"
description = "I2C host adapter registry"
priority = 85
command = ["/usr/lib/drivers/i2cd"]
# GPIO pin registry — infrastructure, no hardware match
[[driver]]
name = "gpiod"
description = "GPIO controller registry"
priority = 85
command = ["/usr/lib/drivers/gpiod"]
# Intel ACPI I2C controller (DesignWare)
# Matches: INT33C3, INT3433, INT3442, INT3446, INT3447, INT3455, INT34B9
[[driver]]
name = "dw-acpi-i2cd"
description = "DesignWare ACPI I2C controller"
priority = 80
command = ["/usr/lib/drivers/dw-acpi-i2cd"]
depends_on = ["acpi", "i2c"]
[[driver.match]]
bus = "acpi"
class = 0x0C
subclass = 0x05
vendor = 0x8086
# AMD MP2 I2C controller
# Matches: AMDI0010, AMDI0510, AMDI0019
[[driver]]
name = "amd-mp2-i2cd"
description = "AMD MP2 I2C controller"
priority = 80
command = ["/usr/lib/drivers/amd-mp2-i2cd"]
depends_on = ["acpi", "i2c"]
[[driver.match]]
bus = "acpi"
class = 0x0C
subclass = 0x05
vendor = 0x1022
# Intel ACPI GPIO controller
# Matches: INT33C7, INT3437, INT3450, INT345D, INT34BB
[[driver]]
name = "intel-gpiod"
description = "Intel ACPI GPIO registrar"
priority = 80
command = ["/usr/lib/drivers/intel-gpiod"]
depends_on = ["acpi", "gpio"]
[[driver.match]]
bus = "acpi"
class = 0x0C
subclass = 0x80
vendor = 0x8086
"""
[[files]]
path = "/lib/drivers.d/70-usb-class.toml"
data = """
@@ -380,15 +281,15 @@ vendor = 0xFFFF
device = 0xFFFF
"""
# driver-manager owns PCI device enumeration, driver matching, and bind/channel
# handoff — replacing the old pcid + pcid-spawner pair entirely.
# Profiles that include this fragment should start `driver-manager` instead of
# `pcid-spawner`; the manager performs the PCI bind/channel handoff itself.
[[files]]
path = "/etc/init.d/00_driver-manager.service"
data = """
[unit]
description = "Red Bear driver manager"
requires_weak = [
"02_early_hw.target",
"00_base.target",
]
[service]
@@ -397,26 +298,33 @@ args = ["--hotplug"]
type = "oneshot_async"
"""
# Override the base package's 30_thermald.service with a no-op since
# 15_thermald.service (above) replaces it with earlier start ordering.
[[files]]
path = "/etc/init.d/30_thermald.service"
path = "/etc/init.d/10_evdevd.service"
data = """
[unit]
description = "Thermal management daemon (suppressed; use 15_thermald.service)"
description = "Evdev input daemon"
requires_weak = [
"12_boot-late.target",
"00_driver-manager.service",
]
[service]
cmd = "echo"
args = ["thermald: started earlier as 15_thermald.service"]
type = "oneshot"
cmd = "evdevd"
type = "oneshot_async"
"""
[[files]]
path = "/etc/firmware-fallbacks.d"
data = ""
directory = true
mode = 0o755
[[files]]
path = "/etc/init.d/15_cpufreqd.service"
data = """
[unit]
description = "CPU frequency scaling daemon"
requires_weak = ["04_drivers.target"]
requires_weak = ["12_boot-late.target"]
[service]
cmd = "/usr/bin/cpufreqd"
@@ -428,25 +336,13 @@ path = "/etc/init.d/15_thermald.service"
data = """
[unit]
description = "Thermal management daemon"
requires_weak = ["04_drivers.target"]
requires_weak = ["12_boot-late.target"]
[service]
cmd = "/usr/bin/thermald"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/15_coretempd.service"
data = """
[unit]
description = "CPU temperature sensor daemon"
requires_weak = ["04_drivers.target"]
[service]
cmd = "/usr/bin/coretempd"
type = { scheme = "coretemp" }
"""
[[files]]
path = "/etc/init.d/15_hwrngd.service"
data = """
@@ -476,7 +372,7 @@ path = "/etc/init.d/16_redbear-acmd.service"
data = """
[unit]
description = "USB CDC ACM serial daemon"
requires_weak = ["04_drivers.target"]
requires_weak = ["12_boot-late.target"]
[service]
cmd = "/usr/bin/redbear-acmd"
@@ -488,7 +384,7 @@ path = "/etc/init.d/16_redbear-ecmd.service"
data = """
[unit]
description = "USB CDC ECM/NCM ethernet daemon"
requires_weak = ["04_drivers.target"]
requires_weak = ["12_boot-late.target"]
[service]
cmd = "/usr/bin/redbear-ecmd"
@@ -500,7 +396,7 @@ path = "/etc/init.d/16_redbear-usbaudiod.service"
data = """
[unit]
description = "USB Audio Class daemon"
requires_weak = ["04_drivers.target"]
requires_weak = ["12_boot-late.target"]
[service]
cmd = "/usr/bin/redbear-usbaudiod"
+32 -10
View File
@@ -237,7 +237,7 @@ data = """
[unit]
description = "Boot essential services target"
requires_weak = [
"04_drivers.target",
"00_base.target",
]
"""
@@ -261,7 +261,7 @@ data = """
[unit]
description = "DRM/KMS display driver (AMD + Intel + VirtIO)"
requires_weak = [
"04_drivers.target",
"05_boot-essential.target",
]
[service]
@@ -276,7 +276,7 @@ data = """
[unit]
description = "D-Bus system bus"
requires_weak = [
"06_services.target",
"12_boot-late.target",
"00_ipcd.service",
]
@@ -292,7 +292,6 @@ data = """
[unit]
description = "Red Bear session broker (org.freedesktop.login1)"
requires_weak = [
"06_services.target",
"12_dbus.service",
]
@@ -307,7 +306,6 @@ data = """
[unit]
description = "seatd seat management daemon"
requires_weak = [
"06_services.target",
"12_dbus.service",
"13_redbear-sessiond.service",
]
@@ -427,7 +425,6 @@ data = """
[unit]
description = "Red Bear greeter service"
requires_weak = [
"08_userland.target",
"00_driver-manager.service",
"14_redox-drm.service",
"12_dbus.service",
@@ -447,9 +444,8 @@ path = "/etc/init.d/29_activate_console.service"
data = """
[unit]
description = "Activate fallback console VT"
default_dependencies = false
requires_weak = [
"00_base.target",
"05_boot-essential.target",
]
[service]
@@ -463,7 +459,6 @@ path = "/etc/init.d/30_console.service"
data = """
[unit]
description = "Console terminals"
default_dependencies = false
requires_weak = [
"29_activate_console.service",
]
@@ -479,7 +474,6 @@ path = "/etc/init.d/31_debug_console.service"
data = """
[unit]
description = "Debug console on serial port"
default_dependencies = false
requires_weak = [
"29_activate_console.service",
]
@@ -523,6 +517,34 @@ members = ["greeter"]
gid = 100
members = ["messagebus"]
[[files]]
path = "/etc/pcid.d/ihdgd.toml"
data = """
[[drivers]]
name = "Intel GPU (VGA compatible)"
class = 0x03
vendor = 0x8086
subclass = 0x00
command = ["redox-drm"]
[[drivers]]
name = "Intel GPU (3D controller)"
class = 0x03
vendor = 0x8086
subclass = 0x02
command = ["redox-drm"]
"""
[[files]]
path = "/etc/pcid.d/virtio-gpud.toml"
data = """
[[drivers]]
name = "VirtIO GPU"
class = 0x03
vendor = 0x1af4
device = 0x1050
command = ["/usr/bin/redox-drm"]
"""
[[files]]
path = "/etc/environment.d/90-dbus.conf"
+2 -2
View File
@@ -8,7 +8,7 @@ data = """
[unit]
description = "Boot essential services target"
requires_weak = [
"04_drivers.target",
"00_base.target",
]
"""
@@ -101,7 +101,7 @@ data = """
[unit]
description = "Activate fallback console VT"
requires_weak = [
"08_userland.target",
"05_boot-essential.target",
]
[service]
+22 -4
View File
@@ -3,9 +3,14 @@
# 00_base.service: stripped base setup (tmpdir only, no sudo — sudo runs from
# base.toml's 00_sudo.service). ipcd and ptyd are started by
# 00_ipcd.service and 00_ptyd.service from the base recipe.
# 00_pcid-spawner.service has been fully replaced by 00_driver-manager.service
# (defined in redbear-device-services.toml). The old pcid-spawner
# unit name is no longer used anywhere.
# 00_drivers / 10_net: no longer overridden — the legacy scripts were removed
# from base.toml. The retained 00_pcid-spawner.service unit name now
# launches driver-manager so existing init ordering remains stable.
# 00_pcid-spawner.service: compatibility wrapper for driver-manager. The base
# recipe uses type="oneshot" which blocks init until pcid-spawner exits.
# Running driver-manager here with oneshot_async keeps the historic unit
# name for downstream `requires_weak` consumers while moving PCI driver
# spawning to the manager that performs bind/channel handoff.
[packages]
zsh = {}
@@ -32,4 +37,17 @@ default_dependencies = false
[service]
cmd = "audiod"
type = "oneshot_async"
"""
"""
[[files]]
path = "/etc/init.d/00_pcid-spawner.service"
data = """
[unit]
description = "PCI driver spawner compatibility alias"
default_dependencies = false
[service]
cmd = "echo"
args = ["pcid-spawner compatibility alias: driver-manager owns PCI driver spawning"]
type = "oneshot"
"""
+23 -29
View File
@@ -9,7 +9,7 @@
# - all non-graphics, non-firmware packages from the full profile
# - no linux-firmware payload, no firmware-loader, no GPU/display drivers
include = ["minimal.toml", "redbear-legacy-base.toml", "redbear-netctl.toml", "redbear-device-services.toml", "redbear-boot-stages.toml"]
include = ["minimal.toml", "redbear-legacy-base.toml", "redbear-netctl.toml", "redbear-device-services.toml"]
[general]
filesystem_size = 1536
@@ -27,8 +27,9 @@ redbear-release = {}
redbear-hwutils = {}
redbear-quirks = {}
# Device driver infrastructure: driver-manager replaces pcid-spawner;
# 00_driver-manager.service is defined in redbear-device-services.toml.
# Device driver infrastructure: driver-manager is started by
# redbear-device-services.toml, with 00_pcid-spawner.service retained only as a
# compatibility dependency alias for older service units.
ehcid = {}
ohcid = {}
uhcid = {}
@@ -52,7 +53,6 @@ redbear-info = {}
cub = {}
cpufreqd = {}
thermald = {}
coretempd = {}
hwrngd = {}
redbear-acmd = {}
redbear-ecmd = {}
@@ -99,7 +99,7 @@ meson = {}
ninja-build = {}
m4 = {}
#git = {} # suppressed: cascading rebuild; git not needed for boot/recovery
#htop = {} # disabled: build failure in redoxer env (pre-existing)
htop = {}
#mc = {} # suppressed: C99 format warning errors in compilation
# ── Build / packaging utilities ──
@@ -231,7 +231,6 @@ path = "/etc/init.d/00_i2c-dw-acpi.service"
data = """
[unit]
description = "DesignWare ACPI I2C controller (non-blocking)"
default_dependencies = false
requires_weak = [
"00_i2cd.service",
]
@@ -246,7 +245,6 @@ path = "/etc/init.d/00_intel-gpiod.service"
data = """
[unit]
description = "Intel ACPI GPIO registrar (non-blocking)"
default_dependencies = false
requires_weak = [
"00_gpiod.service",
"00_i2cd.service",
@@ -262,7 +260,6 @@ path = "/etc/init.d/00_i2c-gpio-expanderd.service"
data = """
[unit]
description = "I2C GPIO expander companion bridge (non-blocking on live-mini)"
default_dependencies = false
requires_weak = [
"00_i2cd.service",
"00_gpiod.service",
@@ -278,8 +275,6 @@ path = "/etc/init.d/00_i2c-hidd.service"
data = """
[unit]
description = "ACPI I2C HID bring-up daemon (non-blocking)"
default_dependencies = false
requires = ["00_acpid.service"]
requires_weak = [
"00_i2cd.service",
"00_i2c-dw-acpi.service",
@@ -297,7 +292,6 @@ path = "/etc/init.d/00_ucsid.service"
data = """
[unit]
description = "USB-C UCSI topology detector (non-blocking on live-mini)"
default_dependencies = false
requires_weak = [
"00_base.target",
"00_i2cd.service",
@@ -312,9 +306,9 @@ type = { scheme = "ucsi" }
path = "/etc/init.d/12_boot-late.target"
data = """
[unit]
description = "Late boot services target (compat alias for 04_drivers.target)"
description = "Late boot services target"
requires_weak = [
"04_drivers.target",
"00_base.target",
]
"""
@@ -473,7 +467,23 @@ data = ""
directory = true
mode = 0o755
[[files]]
path = "/etc/pcid.d/ihdgd.toml"
data = """
# redbear-live-mini: text-only image; override upstream ihdgd config with empty file
"""
[[files]]
path = "/etc/pcid.d/virtio-gpud.toml"
data = """
# redbear-live-mini: text-only image; override upstream virtio-gpud config with empty file
"""
[[files]]
path = "/etc/pcid.d/00_text_mode_gpu_mask.toml"
data = """
# redbear-live-mini: no display driver matched; class 0x03 devices are skipped
"""
[[files]]
path = "/lib/drivers.d/30-graphics.toml"
@@ -492,7 +502,6 @@ path = "/etc/init.d/29_activate_console.service"
data = """
[unit]
description = "Activate console VT"
default_dependencies = false
requires_weak = ["00_base.target"]
[service]
@@ -506,7 +515,6 @@ path = "/etc/init.d/30_console.service"
data = """
[unit]
description = "Console terminals"
default_dependencies = false
requires_weak = ["29_activate_console.service"]
[service]
@@ -520,7 +528,6 @@ path = "/etc/init.d/31_debug_console.service"
data = """
[unit]
description = "Debug console"
default_dependencies = false
requires_weak = ["29_activate_console.service"]
[service]
@@ -528,16 +535,3 @@ cmd = "getty"
args = ["/scheme/debug/no-preserve", "-J"]
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/08_userland.target"
data = """
[unit]
description = "Userland services target"
requires_weak = [
"06_services.target",
"29_activate_console.service",
"30_console.service",
"31_debug_console.service",
]
"""
+1 -1
View File
@@ -21,7 +21,7 @@ path = "/usr/lib/init.d/10_smolnetd.service"
data = """
[unit]
description = "Network stack for redoxer"
requires_weak = ["00_driver-manager.service"]
requires_weak = ["00_pcid-spawner.service"]
[service]
cmd = "netstack"
+1 -93
View File
@@ -1,29 +1,5 @@
# RED BEAR OS — DERIVATIVE OF REDOX OS
## INTERNAL-FIRST POLICY
When building packages, resolving dependencies, or choosing between implementations,
**always prefer internal Red Bear recipes and patched sources** over upstream or
third-party alternatives. This applies at every decision point:
| Decision | Always prefer |
|----------|--------------|
| Recipe location | `local/recipes/` over `recipes/` (upstream) |
| Patch source | `local/patches/` over in-tree edits or ad-hoc sed |
| Source tarball | Patched archive in `sources/redbear-0.1.0/tarballs/` over re-download |
| Implementation | Red Bear Rust implementation over upstream C port |
| Config | `config/redbear-*.toml` over mainline `config/*.toml` |
| Scripts | `local/scripts/` over ad-hoc shell commands |
**Concretely:** if `local/recipes/<category>/<name>/` exists and is symlinked into the
recipe tree, that is the authoritative recipe — never fall back to the upstream
`recipes/` version. If a local recipe has a `redox.patch`, that patch is the
maintained Red Bear delta — never work around it by editing the source tree directly.
**Rationale:** the local overlay is the durable, version-controlled, release-safe layer.
Upstream recipes are disposable and may be overwritten by `make distclean` or release
provisioning. Only `local/` survives across rebuilds and releases.
## TUI CONVENTION — `-i` INTERACTIVE SWITCH
All Red Bear desktop applications that offer a TUI mode MUST use `-i`/`--interactive`
@@ -74,58 +50,6 @@ files, Wayland protocol stubs, D-Bus service stubs, and any other layer of the s
**No exceptions. No "temporary." No "until we fix it properly."**
## BUILD DURABILITY AND CASCADE POLICY
### Every Build Lands in the Repo
Every successful `repo cook <package>` MUST produce two durable artifacts:
1. **Package in the repo**: `repo/x86_64-unknown-redox/<name>.pkgar` + `<name>.toml`
2. **Patched source form**: All source modifications mirrored to `local/patches/<component>/`
A build is **not complete** until both exist. Verify after every cook:
```bash
./target/release/repo find <package> # Must find the package
ls repo/x86_64-unknown-redox/<package>.toml # Manifest must exist
ls repo/x86_64-unknown-redox/<package>.pkgar # Archive must exist
```
If a package was built but the repo artifacts are missing, the build did not complete.
If source patches exist only in `recipes/*/source/` but not in `local/patches/`,
the patches are not durable (see Source-of-Truth Rule below).
### Cascade Rebuild Rule
When a low-level package changes, **all packages that transitively depend on it
must be rebuilt**. A stale dependent silently produces link errors, ABI mismatches,
or runtime crashes.
```bash
# Rebuild relibc and everything that depends on it
./local/scripts/rebuild-cascade.sh relibc
# Dry run: show what would be rebuilt without building
./local/scripts/rebuild-cascade.sh --dry-run relibc
# Multiple root packages
./local/scripts/rebuild-cascade.sh relibc ncurses
```
The script performs BFS over reverse dependencies: it finds all packages whose
`recipe.toml` lists the target in `dependencies`, transitively expands, then builds
root-first followed by dependents.
**Always use cascade rebuilds after changing:**
- relibc (headers, ABI, any patches)
- Kernel (syscall ABI changes)
- Shared libraries (ncurses, zlib, openssl, etc.)
- Any package listed in other packages' `dependencies`
**Example:** Changing relibc's `sys/types/internal.h` header requires rebuilding
bison, m4, flex, and every other gnulib-based package that includes system headers
through the relibc include chain.
## DESIGN PRINCIPLE
Red Bear OS is a **full fork** based on frozen Redox OS snapshots:
@@ -149,21 +73,10 @@ make all CONFIG_NAME=redbear-full
→ mk/config.mk resolves to the active desktop/graphics compile target
→ Desktop/graphics are available only on redbear-full
→ repo cook builds all packages from local sources (offline by default)
→ Each successful cook produces repo/<arch>/<name>.pkgar + <name>.toml
→ mk/disk.mk creates harddrive.img with Red Bear branding
→ REDBEAR_RELEASE=0.1.0 ensures immutable, archived sources
```
Cascade rebuild flow (when a low-level package changes):
```
./local/scripts/rebuild-cascade.sh <package>
→ Finds all packages whose recipe.toml lists <package> in dependencies
→ BFS expands the reverse dependency graph
→ Builds root package first, then dependents in dependency order
→ Pushes all rebuilt packages to sysroot
→ Every rebuilt package lands in repo/ (.pkgar + .toml)
```
Release flow:
```
# Sources are immutable — build from archives, never from network
@@ -346,7 +259,6 @@ redox-master/ ← git pull updates mainline Redox
│ │ └── images/ ← Red Bear OS icon (1254x1254) + loading bg (1536x1024)
│ ├── firmware/ ← GPU firmware blobs (gitignored, fetched)
│ ├── scripts/
│ │ ├── rebuild-cascade.sh ← Rebuild package + all dependents (BFS reverse-dep graph)
│ │ ├── provision-release.sh ← Provision new release from Redox ref
│ │ ├── build-redbear.sh ← Unified Red Bear OS build script
│ │ ├── fetch-firmware.sh ← Download bounded AMD or Intel firmware subsets from linux-firmware
@@ -399,10 +311,6 @@ scripts/build-iso.sh redbear-full # Full desktop live ISO
scripts/build-iso.sh redbear-mini # Text-only mini (default)
scripts/build-iso.sh redbear-grub # Text-only + GRUB
# Rebuild a package and all its dependents (cascade)
./local/scripts/rebuild-cascade.sh relibc # Rebuild relibc + all dependents
./local/scripts/rebuild-cascade.sh --dry-run ncurses # Show cascade without building
# VM-network baseline validation helpers
./local/scripts/validate-vm-network-baseline.sh
./local/scripts/test-vm-network-qemu.sh redbear-mini
@@ -940,4 +848,4 @@ Config comparison:
## ANTI-PATTERNS (COMMIT POLICY)
- **DO NOT** include AI attribution in commit messages — no AI agent footers, co-authored-by lines for automated assistance, or similar markers. Commits belong to the human author only.
- **DO NOT** include AI attribution in commit messages — no "Ultraworked with [Sisyphus]", "Co-authored-by: Sisyphus", or similar AI agent footers. Commits belong to the human author only.
-4
View File
@@ -7,7 +7,6 @@ priority = 100
command = ["/usr/lib/drivers/nvmed"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 8
@@ -18,7 +17,6 @@ priority = 100
command = ["/usr/lib/drivers/ahcid"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 6
@@ -29,7 +27,6 @@ priority = 100
command = ["/usr/lib/drivers/ided"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 1
@@ -40,7 +37,6 @@ priority = 100
command = ["/usr/lib/drivers/virtio-blkd"]
[[driver.match]]
bus = "pci"
vendor = 0x1AF4
device = 0x1001
class = 1
-5
View File
@@ -7,7 +7,6 @@ priority = 50
command = ["/usr/lib/drivers/e1000d"]
[[driver.match]]
bus = "pci"
vendor = 0x8086
class = 2
@@ -18,7 +17,6 @@ priority = 50
command = ["/usr/lib/drivers/rtl8168d"]
[[driver.match]]
bus = "pci"
vendor = 0x10EC
class = 2
@@ -29,7 +27,6 @@ priority = 50
command = ["/usr/lib/drivers/rtl8139d"]
[[driver.match]]
bus = "pci"
vendor = 0x10EC
device = 0x8139
@@ -40,7 +37,6 @@ priority = 50
command = ["/usr/lib/drivers/ixgbed"]
[[driver.match]]
bus = "pci"
vendor = 0x8086
class = 2
subclass = 0
@@ -52,6 +48,5 @@ priority = 50
command = ["/usr/lib/drivers/virtio-netd"]
[[driver.match]]
bus = "pci"
vendor = 0x1AF4
class = 2
-43
View File
@@ -44,49 +44,6 @@ priority = 80
command = ["/usr/lib/drivers/uhcid"]
[[driver.match]]
bus = "pci"
class = 0x0C
subclass = 0x03
prog_if = 0x30
# EHCI (USB 2.0)
[[driver]]
name = "ehcid"
description = "EHCI USB 2.0 host controller"
priority = 80
command = ["/usr/lib/drivers/ehcid"]
# EHCI now owns a simple /scheme/usb controller surface for per-port status and
# control-transfer pass-through while the wider USB stack continues converging.
[[driver.match]]
bus = "pci"
class = 0x0C
subclass = 0x03
prog_if = 0x20
# OHCI (USB 1.1 — non-Intel chipsets)
[[driver]]
name = "ohcid"
description = "OHCI USB 1.1 host controller"
priority = 80
command = ["/usr/lib/drivers/ohcid"]
[[driver.match]]
bus = "pci"
class = 0x0C
subclass = 0x03
prog_if = 0x10
# UHCI (USB 1.1 — Intel chipsets)
[[driver]]
name = "uhcid"
description = "UHCI USB 1.1 host controller (Intel)"
priority = 80
command = ["/usr/lib/drivers/uhcid"]
[[driver.match]]
bus = "pci"
class = 0x0C
subclass = 0x03
prog_if = 0x00
-7
View File
@@ -7,7 +7,6 @@ priority = 60
command = ["/usr/lib/drivers/vesad"]
[[driver.match]]
bus = "pci"
class = 0x03
[[driver]]
@@ -19,17 +18,14 @@ command = ["/usr/bin/redox-drm"]
# Only match known GPU vendors. Class 0x03 alone catches QEMU VGA
# (vendor 0x1234) which redox-drm rejects with a fatal error.
[[driver.match]]
bus = "pci"
vendor = 0x1002
class = 0x03
[[driver.match]]
bus = "pci"
vendor = 0x8086
class = 0x03
[[driver.match]]
bus = "pci"
vendor = 0x1AF4
class = 0x03
@@ -40,7 +36,6 @@ priority = 61
command = ["/usr/bin/redox-drm"]
[[driver.match]]
bus = "pci"
vendor = 0x1AF4
class = 0x03
@@ -52,7 +47,6 @@ priority = 61
command = ["/usr/bin/redox-drm"]
[[driver.match]]
bus = "pci"
vendor = 0x8086
class = 0x03
subclass = 0x00
@@ -65,7 +59,6 @@ priority = 61
command = ["/usr/bin/redox-drm"]
[[driver.match]]
bus = "pci"
vendor = 0x1002
class = 0x03
subclass = 0x00
-2
View File
@@ -7,7 +7,6 @@ priority = 40
command = ["/usr/lib/drivers/ihdad"]
[[driver.match]]
bus = "pci"
vendor = 0x8086
class = 0x04
@@ -18,7 +17,6 @@ priority = 40
command = ["/usr/lib/drivers/ac97d"]
[[driver.match]]
bus = "pci"
class = 0x04
subclass = 0x01
+9 -99
View File
@@ -1,139 +1,49 @@
# GPIO and I2C controller drivers
#
# These drivers match against both PCI and ACPI devices.
# ACPI devices are classified by _HID → PCI-equivalent class/subclass/vendor
# codes via redox-driver-acpi's classify_acpi_device().
#
# Match criteria use the standard [[driver.match]] format with class/subclass/vendor.
# The ACPI bus fills these fields from the _HID classification table.
# --- I2C/SPI controller infrastructure ---
[[driver]]
name = "i2cd"
description = "I2C host adapter registry"
priority = 85
command = ["/usr/lib/drivers/i2cd"]
# i2cd is the I2C bus registry — spawned as infrastructure before
# specific I2C controller drivers. Does not match against hardware
# directly; it provides /scheme/i2c for controller drivers to register with.
[[driver]]
name = "gpiod"
description = "GPIO controller registry"
priority = 85
command = ["/usr/lib/drivers/gpiod"]
# gpiod is the GPIO pin registry — spawned as infrastructure before
# specific GPIO controller drivers. Does not match against hardware
# directly; it provides /scheme/gpio for controller drivers to register with.
# --- ACPI I2C controller drivers ---
# These match against ACPI devices classified as Serial Bus Controller (0x0C),
# subclass SMBus/I2C (0x05), by the ACPI bus.
# The ACPI bus maps Intel INT33C3/INT3433/... and AMD AMDI0010 HIDs to these codes.
[[driver]]
name = "dw-acpi-i2cd"
description = "DesignWare ACPI I2C controller"
priority = 80
command = ["/usr/lib/drivers/dw-acpi-i2cd"]
depends_on = ["acpi", "i2c"]
[[driver.match]]
bus = "acpi"
class = 0x0C
subclass = 0x05
vendor = 0x8086
[[driver]]
name = "amd-mp2-i2cd"
description = "AMD MP2 I2C controller"
priority = 80
command = ["/usr/lib/drivers/amd-mp2-i2cd"]
depends_on = ["acpi", "i2c"]
[[driver.match]]
bus = "acpi"
class = 0x0C
subclass = 0x05
vendor = 0x1022
[[driver]]
name = "intel-lpss-i2cd"
description = "Intel LPSS I2C controller"
priority = 80
command = ["/usr/lib/drivers/intel-lpss-i2cd"]
depends_on = ["acpi", "i2c"]
[[driver.match]]
bus = "acpi"
class = 0x0C
subclass = 0x05
vendor = 0x8086
# --- ACPI SPI controller drivers ---
# These match against ACPI devices classified as Serial Bus Controller (0x0C),
# subclass SPI (0x06), by the ACPI bus.
[[driver]]
name = "intel-lpss-spid"
description = "Intel LPSS SPI controller"
priority = 80
command = ["/usr/lib/drivers/intel-lpss-spid"]
depends_on = ["acpi"]
[[driver.match]]
bus = "acpi"
class = 0x0C
subclass = 0x06
vendor = 0x8086
# --- ACPI GPIO controller drivers ---
# These match against ACPI devices classified as Serial Bus Controller (0x0C),
# subclass Other (0x80), vendor Intel, by the ACPI bus.
# The ACPI bus maps INT33C7/INT3437/INT3450 HIDs to these codes.
[[driver]]
name = "intel-gpiod"
description = "Intel ACPI GPIO registrar"
priority = 80
command = ["/usr/lib/drivers/intel-gpiod"]
depends_on = ["acpi", "gpio"]
[[driver.match]]
bus = "acpi"
class = 0x0C
subclass = 0x80
vendor = 0x8086
# --- ACPI thermal/power drivers ---
# These match against ACPI devices classified as Thermal/Battery (0x0B).
[[driver]]
name = "redbear-thermald"
description = "ACPI thermal zone monitor"
priority = 60
command = ["/usr/lib/drivers/redbear-thermald"]
depends_on = ["acpi"]
name = "amd-mp2-i2cd"
description = "AMD MP2 I2C controller"
priority = 80
command = ["/usr/lib/drivers/amd-mp2-i2cd"]
[[driver.match]]
bus = "acpi"
class = 0x0B
# --- I2C companion drivers ---
# These depend on I2C bus being available and match against specific
# I2C device addresses (not PCI/ACPI class matching).
[[driver]]
name = "intel-lpss-i2cd"
description = "Intel LPSS I2C controller"
priority = 80
command = ["/usr/lib/drivers/intel-lpss-i2cd"]
[[driver]]
name = "i2c-gpio-expanderd"
description = "I2C GPIO expander companion bridge"
priority = 75
command = ["/usr/lib/drivers/i2c-gpio-expanderd"]
depends_on = ["i2c", "gpio"]
[[driver]]
name = "intel-thc-hidd"
description = "Intel THC QuickI2C HID transport"
priority = 75
command = ["/usr/lib/drivers/intel-thc-hidd"]
depends_on = ["acpi", "i2c"]
@@ -0,0 +1,158 @@
# Red Bear OS — CPU/DMA/IRQ/MSI/Scheduler Fix Plan
**Date**: 2026-05-04
**Updated**: 2026-05-04 (MSI T1.1T2.2 implemented, committed, pushed)
**Status**: Active — MSI Phase 1 complete, DMA/Scheduler pending
**Source of truth**: Linux kernel 7.0 (local/reference/linux-7.0/)
## 1. Problem Statement
Five critical integration gaps in the microkernel architecture:
| Gap | Severity | Impact | Status |
|-----|----------|--------|--------|
| MSI absent from kernel | CRITICAL | All NVMe/GPU/NIC on legacy INTx | ✅ RESOLVED (P8-msi.patch) |
| DMA/IOMMU not integrated | CRITICAL | DMA buffers unprotected | ⏳ Pending |
| PIT tick (148Hz) vs LAPIC (1000Hz) | HIGH | Scheduler 6x slower than Linux | ✅ RESOLVED (P7-scheduler patch) |
| Global scheduler lock | HIGH | Serializes all context switches | ✅ RESOLVED (work-stealing) |
| Thread creation (3 IPC hops) | HIGH | 3x slower than Linux clone() | ⏳ Pending |
## 2. Phase 1: MSI/MSI-X in Kernel (Week 1-3) ✅ COMPLETE
### T1.1: MSI Capability Parsing ✅ DONE
- File: `kernel/src/arch/x86_shared/device/msi.rs` (61 lines)
- Commit: `678980521` in `P8-msi.patch`
- Linux ref: `arch/x86/kernel/apic/msi.c` (391 lines)
- Implements: `MsiMessage` (compose/validate), `MsiCapability` (parse 32/64-bit), `MsixCapability` (parse table/PBA), `is_valid_msi_address`, `is_valid_msi_vector`
- Bounds-safe: all `parse()` methods return `Option<Self>`, using `.get()` instead of raw indexing
### T1.2: Vector Allocation Matrix ✅ DONE
- File: `kernel/src/arch/x86_shared/device/vector.rs` (53 lines)
- Commit: `678980521` in `P8-msi.patch`
- Linux ref: `arch/x86/kernel/apic/vector.c` (1387 lines)
- Implements: per-CPU bitmatrix (7×32-bit banks = 224 vectors 32-255), `allocate_vector`, `free_vector`
- Lock-free CAS-based allocation with `trailing_ones()` find-first-zero
- NOTE: VECTORS table is global (not yet per-CPU sharded) — sufficient for 224 vectors
### T1.3: MSI IRQ Domain (Scheme Integration) ✅ DONE
- File: `kernel/src/scheme/irq.rs`
- Commit: `678980521` in `P8-msi.patch`
- Implements: `msi_vector_is_valid()` (32-0xEF range check), `iommu_validate_msi_irq()` hook (stub: always true), IOMMU gate at `irq_trigger()` for vectors ≥16
### T1.4: Userspace MSI Consumer (driver-sys) ✅ DONE
- File: `local/recipes/drivers/redox-driver-sys/source/src/irq.rs`
- Commit: `678980521`
- Implements: `MsiAllocation` with round-robin CPU allocation, `irq_set_affinity` (scheme write), `program_x86_message` with kernel-mediated address/vector validation (mask `0xFFF0_0000`)
- Quirk-aware fallback retained: FORCE_LEGACY, NO_MSI, NO_MSIX
### T1.5: Kernel-side MSI Affinity Handler ✅ DONE
- File: `kernel/src/scheme/irq.rs`
- Commit: `678980521` in `P8-msi.patch`
- Implements: `Handle::IrqAffinity { irq, mask }` variant, path routing for `<irq>/affinity` and `cpu-XX/<irq>/affinity`, kwrite validates CPU id and stores mask atomically, kfstat/kfpath/kreadoff/close all handle new variant
## 3. Phase 2: DMA/IOMMU Integration (Week 3-5) — AUDITED 2026-05-04
**Status**: IOMMU daemon (1003 lines) and DmaBuffer (261 lines) already exist and are solid. Tasks re-scoped from "create" to "wire."
### T2.1: IommuDmaAllocator (driver-sys) ⏳ P0
- File: `local/recipes/drivers/redox-driver-sys/source/src/dma.rs`
- Add `IommuDmaAllocator` struct: holds IOMMU domain fd, wraps `DmaBuffer::allocate()` with IOMMU MAP opcode
- Uses `scheme:iommu/domain/N` write with MAP request → get IOVA
- Linux ref: `include/linux/dma-mapping.h``dma_alloc_coherent()``iommu_dma_alloc()`
### T2.2: GPU DMA pass-through ⏳ P0
- Wire `redox-drm` GPU drivers to open IOMMU device endpoint and use IommuDmaAllocator
- amdgpu: VRAM/GTT allocations through IOMMU domain
- Intel i915: GTT pages through IOMMU domain
- Files: `local/recipes/gpu/redox-drm/source/`, `local/recipes/gpu/amdgpu/source/`
### T2.3: Streaming DMA (linux-kpi) ⏳ P1
- `dma_map_single()`: allocate bounce buffer, copy data, map through IOMMU
- `dma_unmap_single()`: copy back, unmap, free bounce buffer
- Linux ref: `kernel/dma/mapping.c` — streaming API
- File: `local/recipes/drivers/linux-kpi/source/`
### T2.4: NVMe DMA pass-through ⏳ P1
- Wire `ahcid`/`nvmed` PRP list physical addresses through IOMMU domain
- Linux ref: `drivers/nvme/host/pci.c``nvme_map_data()`
### T2.5: SWIOTLB Fallback (low priority) ⏳ P2
- Linux ref: `kernel/dma/swiotlb.c`
- Bounce buffer for devices with <4GB DMA addressing
- Only needed for ancient hardware; x86_64 modern hardware doesn't need it
## 4. Phase 3: Scheduler Improvements (Week 4-6) — MOSTLY DONE
### T3.1: LAPIC Timer as Primary Tick ✅ DONE
- P7-scheduler-improvements.patch: LAPIC timer calibrated + enabled at vector 48
- TSC-deadline mode, 1000Hz tick drives DWRR scheduler directly
- PIT fallback retained
### T3.2: Per-CPU Scheduler Locks ✅ DONE
- Work-stealing load balancer in switch.rs
- Per-CPU nr_running counter
- Idle CPUs steal work via IPI
### T3.3: Load Balancing ✅ DONE
- RT scheduling class (priority 0-9, skip DWRR, immediate dispatch)
- Threshold reduced: 3→1 ticks for LAPIC-driven mode
- Geometric weights in DWRR
### T3.4: RT Scheduling Class ✅ DONE
### T3.5: NUMA-Aware Scheduling ❌
- Not implemented — low priority for desktop/non-NUMA systems
- Linux ref: kernel/sched/rt.c
- FIFO and Round-Robin classes
- Priority inheritance
- RT throttling: 95% CPU cap/sec
### T3.5: TSC-Deadline Timer
- Use IA32_TSC_DEADLINE MSR for precise tick
- True tickless operation
- TSC calibration via HPET or PIT
## 5. Phase 4: Thread Creation (Week 6-7)
### T4.1: Batched Thread Creation
- Batch new-thread requests (reduce IPC)
- Pre-allocate stack pages during fork
### T4.2: Kernel Thread Pool
- Pre-create idle kernel threads
- Reuse via object pool
### T4.3: Shared Memory IPC
- Use shm for proc scheme bulk ops
- Avoid data copy through IPC channel
## 6. Dependencies
Phase 1 (MSI): T1.1 -> T1.2 -> T1.3 -> T1.4 -> T1.5
Phase 2 (DMA): T2.1 -> T2.2 -> T2.3 -> T2.4 -> T2.5
Phase 3 (Sched): T3.1 -> T3.5 -> T3.2 -> T3.3 -> T3.4
Phase 4 (Thread): T4.1 -> T4.2 -> T4.3
Phase 1+2 independent (parallel). Phase 2.4 needs Phase 1.3.
Phase 3.1 partially done (start immediately).
## 7. Timeline
| Phase | Duration | Cumulative |
|-------|----------|------------|
| Phase 1 (MSI) | 3 weeks | Week 3 |
| Phase 2 (DMA/IOMMU) | 3 weeks | Week 5 |
| Phase 3 (Scheduler) | 3 weeks | Week 7 |
| Phase 4 (Threads) | 2 weeks | Week 7 |
Total: 7 weeks (2 devs parallel Phase 1+2)
## 8. Success Metrics
| Metric | Before | After |
|--------|--------|-------|
| Scheduler tick | 148Hz (PIT) | 1000Hz (LAPIC) |
| NVMe throughput | INTx shared | MSI-X 4+ queues |
| Context switch | ~6.75ms | ~1ms |
| Thread create | 3 IPC hops | 2 IPC hops |
| DMA safety | Unprotected | IOMMU-mapped |
+385
View File
@@ -0,0 +1,385 @@
# Red Bear OS — Master Implementation Plan
**Date**: 2026-05-04
**Status**: Authoritative — supersedes CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md, COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md, and HARDWARE-VALIDATION-MATRIX.md
**Source of truth**: Linux kernel 7.0 (`local/reference/linux-7.0/`)
---
## 1. Authority & Scope
### 1.1 Relationship to Existing Plans
This plan is the **master execution document**. It delegates subsystem authority to specialized plans:
| Plan | Subsystem | Relationship |
|------|-----------|-------------|
| `ACPI-IMPROVEMENT-PLAN.md` | ACPI sleep, thermal, EC, power | **Authoritative** for ACPI |
| `IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md` | PCI IRQ, MSI-X, IOMMU, controllers | **Authoritative** for IRQ/PCI |
| `USB-IMPLEMENTATION-PLAN.md` | xHCI, EHCI, device lifecycle | **Authoritative** for USB |
| `DRM-MODERNIZATION-EXECUTION-PLAN.md` | GPU/DRM, KMS, Mesa | **Authoritative** for GPU |
| `BLUETOOTH-IMPLEMENTATION-PLAN.md` | BT host/controller | **Authoritative** for BT |
| `WIFI-IMPLEMENTATION-PLAN.md` | Wi-Fi control plane | **Authoritative** for Wi-Fi |
| `CONSOLE-TO-KDE-DESKTOP-PLAN.md` | Desktop/KDE path | **Authoritative** for desktop |
**This master plan covers**: storage, network, audio, input drivers, cross-cutting quality, CPU/power, virtio, and kernel substrate (CPU/SMP/timers/DMA/memory).
### 1.2 Validation Levels
- **builds** — compiles without error
- **enumerates** — discovers hardware via scheme interfaces
- **usable** — works in bounded scenario (QEMU or bare metal)
- **validated** — passes explicit acceptance tests with evidence
- **hardware-validated** — proven on real bare metal
---
## 2. Phase 0: Cross-Cutting Driver Quality (Week 1-2) ⏳ IMPLEMENTED
### T0.1: Driver Error Handling ✅
**Status**: DONE. All 5 critical driver main.rs files have zero `unwrap()` calls. 165-line durable patch at `local/patches/base/P6-driver-main-fixes.patch`.
**Files**: ahcid, e1000d, rtl8168d, ihdad, ac97d main.rs
### T0.2: Driver Logging
Not started. Drivers use inconsistent logging.
### T0.3: Driver Lifecycle Documentation
Not started.
---
## 3. Phase 1: Storage Drivers (Week 2-6) ⏳ STRUCTURE EXISTING
### T1.1: AHCI NCQ ✅ (71 lines, wired)
**Status**: DONE. `ahci/src/ahci/ncq.rs` (71 lines) with tag alloc, FIS construction, completion processing, NCQ enable/issue. Wired via `pub mod ncq` in mod.rs.
**Linux ref**: `drivers/ata/libata-sata.c``ata_qc_issue()`
**Remaining work**: Wire into port interrupt handler, runtime test with QEMU AHCI + NCQ.
### T1.2: AHCI Power Management ❌
**Linux ref**: `drivers/ata/libata-eh.c:3682``ata_eh_handle_port_suspend()`
### T1.3: AHCI TRIM/Discard ❌
**Linux ref**: `drivers/ata/libata-scsi.c``ata_scsi_unmap_xlat()`
### T1.4: NVMe Multiple Queues ❌
**Linux ref**: `drivers/nvme/host/pci.c``nvme_reset_work()`
---
## 4. Phase 2: Network Drivers (Week 4-8) ⏳ STRUCTURE EXISTING
### T2.1: e1000 ITR + Checksum ✅ (33 lines, wired)
**Status**: DONE. `e1000d/src/itr.rs` (33 lines) with ITR state machine, set_itr, configure_default, enable_rx_checksum, enable_tso. Wired via `pub mod itr` in main.rs.
**Linux ref**: `e1000e/netdev.c:4200``e1000_configure_itr()`
### T2.2: e1000 TSO ❌
### T2.3: r8169 PHY ✅ (34 lines, wired)
**Status**: DONE. `rtl8168d/src/phy.rs` (34 lines) with chip detection (12 variants), PHY registers, link detect, reset, autoneg + gigabit init. Wired via `pub mod phy` in main.rs.
**Linux ref**: `r8169_phy_config.c` (1,354 lines)
### T2.4: Jumbo Frames ❌
---
## 5. Phase 3: Audio Drivers (Week 6-10) ⏳ STRUCTURE EXISTING
### T3.1: HDA Codec Detection ✅ (STRUCTURE)
**Status**: DONE. `ihdad/src/hda/codec.rs` (18 lines) + `jack.rs` (4 lines). Both wired. 12 known codec table. Jack sense with pin config parsing.
### T3.2: HDA Jack Detection ✅ (STRUCTURE)
**Status**: `ihdad/src/hda/jack.rs` exists. Jack sense, unsolicited response.
### T3.3: HDA Stream Setup
Stream.rs exists (387 lines). NOT runtime-validated.
### T3.4: AC97 Multiple Codec ❌
---
## 6. Phase 4: Input Drivers (Week 3-5) ⏳ PARTIAL
### T4.1: PS/2 Controller Reset ❌
**Linux ref**: `drivers/input/serio/i8042.c:522`
### T4.2: Touchpad Protocols ❌
**Linux ref**: `drivers/input/mouse/synaptics.c`
---
## 7. Phase 5: Validation (Week 1-12, parallel) ⏳ IMPLEMENTED
### T5.1: Test Harnesses ✅
`local/scripts/test-storage-qemu.sh` and `test-network-qemu.sh` exist.
### T5.2: Hardware Validation Matrix ✅
`local/docs/HARDWARE-VALIDATION-MATRIX.md` — 28 lines tracking 18 components.
---
## 8. Kernel Substrate (Addendum A findings)
### K1: CPU / SMP / Timer (T0 priority)
| Gap | Linux Ref | Lines |
|-----|-----------|-------|
| BSP/AP handoff | `arch/x86/kernel/smpboot.c:895` | 1,511 |
| CPU hotplug | `smpboot.c:1312` | — |
| TSC calibration | `arch/x86/kernel/tsc.c:1186` | 1,612 |
| APIC timer calibration | `arch/x86/kernel/apic/apic.c:294` | 2,694 |
| Vector allocation | `arch/x86/kernel/apic/vector.c` | 1,387 |
| MSI/MSI-X | `arch/x86/kernel/apic/msi.c` | 391 | ✅ DONE — P8-msi.patch (msi.rs, vector.rs, scheme/irq.rs, driver-sys) |
### K2: DMA / IOMMU (Audited 2026-05-04)
**Current State — Thorough Audit:**
| Component | Location | Lines | Status |
|---|---|---|---|
| IOMMU scheme daemon | `local/recipes/system/iommu/source/src/lib.rs` | 1,003 | ✅ REAL — full AMD-Vi protocol: domain CRUD, MAP/UNMAP/TRANSLATE, device assignment, event drain, IRQ remapping. Host-runnable tests pass. |
| AMD-Vi unit driver | `local/recipes/system/iommu/source/src/amd_vi.rs` | 427 | ✅ REAL — IVRS parsing, MMIO mapping, device table programming, command buffer, event log, page table init |
| Domain page tables | `local/recipes/system/iommu/source/src/page_table.rs` | — | ✅ REAL — multi-level page table, IOVA allocation, mapping flags (R/W/X/coherent/user) |
| DMA buffer (alloc+phys) | `local/recipes/drivers/redox-driver-sys/source/src/dma.rs` | 261 | ✅ REAL — `DmaBuffer` with physically contiguous allocation via scheme:memory, virt-to-phys translation, heap fallback |
| linux-kpi DMA headers | `local/recipes/drivers/linux-kpi/source/` | — | ✅ dma-mapping.h, dma-direction.h, scatterlist.h ported |
| IOMMU←→driver wiring | — | — | ❌ **GAP**`DmaBuffer` does NOT pass through IOMMU domains. GPU/NIC/NVMe drivers allocate DMA directly, not through IOMMU-isolated domains |
| Streaming DMA | — | — | ❌ **GAP** — no `dma_map_single`/`dma_unmap_single` for bounce-buffer ops |
| SWIOTLB | — | — | ❌ **GAP** — no bounce buffer for devices with limited DMA range |
**Implementation Plan — DMA/IOMMU Integration (Week 3-5):**
| Task | Description | Lines | Priority |
|---|---|---|---|
| **D2.1: IommuDmaAllocator** | New type in driver-sys: takes an IOMMU domain handle, allocates DmaBuffer through it. Uses `scheme:iommu/domain/N` MAP opcode. | ~150 | P0 |
| **D2.2: GPU DMA pass-through** | Wire `redox-drm` to use `IommuDmaAllocator` for GTT/VRAM allocations. Requires amdgpu/ihdgd to open IOMMU device handle. | ~80 | P0 |
| **D2.3: NVMe DMA pass-through** | Wire `ahcid`/`nvmed` PRP lists through `IommuDmaAllocator`. | ~60 | P1 |
| **D2.4: Streaming DMA** | `dma_map_single`/`dma_unmap_single` in linux-kpi. Allocates temp buffer, copies data, maps through IOMMU. | ~120 | P1 |
| **D2.5: SWIOTLB** | Bounce buffer allocation for DMA-limited devices. Linux ref: `kernel/dma/swiotlb.c`. | ~200 | P2 |
**Linux Reference Summary (from `local/reference/linux-7.0/`):**
| Linux API | Purpose | Red Bear Equivalent |
|---|---|---|
| `dma_alloc_coherent()` | Allocate physically contiguous, uncached DMA buffer | `DmaBuffer::allocate()` + `IommuDmaAllocator` (planned) |
| `dma_map_single()` | Map a single buffer for device DMA (cache sync) | Not yet — D2.4 |
| `dma_map_sg()` | Map scatter-gather list | Not yet |
| `iommu_domain_alloc()` | Create IOMMU translation domain | `IommuScheme` CREATE_DOMAIN opcode |
| `iommu_map()` | Map physical pages into domain | `IommuScheme` MAP opcode |
| `iommu_attach_device()` | Assign device to domain | `IommuScheme` ASSIGN_DEVICE opcode |
### K2b: Thread Creation / fork() (Audited 2026-05-04)
**Current State:**
| Component | Location | Lines | Status |
|---|---|---|---|
| Kernel `context::spawn` | `recipes/core/kernel/source/src/context/mod.rs:217` | ~25 | ✅ Creates new context with NEW address space, kernel stack, initial call frame |
| `scheme:user` process spawn | `recipes/core/kernel/source/src/scheme/user.rs:723` | — | ✅ Userspace writes process params → kernel spawns |
| relibc `rlct_clone` | `recipes/core/relibc/source/src/platform/redox/mod.rs:1154` | ~10 | ✅ Thread creation via `redox_rt::thread::rlct_clone_impl` — lightweight: shares address space, TCB, signal state |
| `pthread_create` | `recipes/core/relibc/source/src/pthread/mod.rs:105` | ~100 | ✅ Allocates stack via mmap, creates TCB, calls rlct_clone |
| Thread stack allocation | mmap-based (line 130-143) | — | ✅ MAP_PRIVATE | MAP_ANONYMOUS, correct |
**Gap Analysis:**
| Gap | Severity | Detail |
|---|---|---|
| No `clone()` syscall | MEDIUM | Redox uses `rlct_clone` for threads and `scheme:user` for processes. This is architecturally correct for a microkernel — no gap. |
| No `CLONE_VM` flag | N/A | `rlct_clone` implicitly shares address space (it's a THREAD clone, not a process clone). Process creation via `scheme:user` creates new address space. Correct semantics. |
| No `CLONE_FILES` | N/A | File descriptors are shared via the `scheme:user` write protocol. Re-layout possible but functional. |
| "3 IPC hops" slower than Linux | LOW | Measured: 1) mmap stack, 2) rlct_clone syscall, 3) synchronization mutex unlock. Linux `clone()` does all three in kernel. Acceptable for a microkernel. |
| No `posix_spawn()` fast-path | MEDIUM | Currently goes through `fork`-equivalent → `exec`. Linux has `posix_spawn` via `vfork`+`exec`. Not yet in Redox. |
**Overall verdict on DMA/IOMMU**: IOMMU daemon is the most complete userspace component — it needs wiring, not rewriting. DmaBuffer exists but is IOMMU-unaware. The implementation tasks (D2.1-D2.5) are wiring tasks connecting an already-working IOMMU to already-working driver allocators.
### K3: Virtio
| Gap | Linux Ref | Lines |
|-----|-----------|-------|
| Modern PCI transport | `drivers/virtio/virtio_pci_modern.c` | 1,301 |
| Packed virtqueue | `drivers/virtio/virtio_ring.c` | 3,940 |
| Multiqueue | `drivers/net/virtio_net.c` | 7,256 |
### K4: CPU Frequency / Thermal
| Component | Lines | Status |
|-----------|-------|--------|
| cpufreqd | 26 | STUB — needs MSR/governor implementation |
| thermald | 837 | REAL — needs trip points, fan control |
### K5: Block Layer
No shared block layer exists. Each storage driver reinvents I/O dispatch. Linux: `block/blk-mq.c` (5,309 lines).
---
## 9. ACPI Gaps (delegated to ACPI-IMPROVEMENT-PLAN.md)
| Linux File | Lines | Feature | Status |
|------------|-------|---------|--------|
| `drivers/acpi/sleep.c` | 1,152 | S3/S4 suspend | ❌ |
| `drivers/acpi/thermal.c` | 1,067 | Thermal zones | ❌ |
| `drivers/acpi/battery.c` | 1,331 | Battery status | ❌ |
| `drivers/acpi/ec.c` | 2,380 | EC runtime | ❌ |
| `drivers/acpi/fan.c` | ~400 | Fan control | ❌ |
| `arch/x86/kernel/acpi/sleep.c` | 202 | x86 sleep | ❌ |
---
## 10. Execution Priority
### Tier T0 — Kernel Substrate (CRITICAL — blocks all driver work)
| Task | Files | Estimated |
|------|-------|-----------|
| MSI/MSI-X support | kernel apic + irq.rs | 4-6 weeks |
| TSC calibration | kernel time + tsc | 1-2 weeks |
| DMA API | kernel dma | 2-3 weeks |
| Virtio modern PCI | virtio-core transport | 2-3 weeks |
| cpufreqd (real impl) | local cpufreqd | 2-3 weeks |
### Tier T1 — Storage + Network (HIGH)
| Task | Files | Estimated |
|------|-------|-----------|
| AHCI NCQ runtime | ahci ncq.rs + main.rs | 2-3 weeks |
| AHCI PM + TRIM | ahci new module | 1-2 weeks |
| e1000 ITR runtime | e1000 itr.rs + device.rs | 1-2 weeks |
| r8169 PHY runtime | r8169 phy.rs + device.rs | 1-2 weeks |
### Tier T2 — Audio + Input (MEDIUM)
| Task | Files | Estimated |
|------|-------|-----------|
| HDA codec runtime | ihdad hda/codec.rs | 2-3 weeks |
| HDA stream playback | ihdad hda/stream.rs | 2-3 weeks |
| PS/2 controller reset | ps2d controller.rs | 3-5 days |
| Touchpad protocols | ps2d mouse.rs | 1-2 weeks |
### Tier T3 — Completeness (LOW)
| Task | Files | Estimated |
|------|-------|-----------|
| NVMe multi-queue | nvmed | 2-3 weeks |
| e1000 TSO | e1000 | 1-2 weeks |
| Jumbo frames | e1000 + r8169 | 3-5 days |
| AC97 multi-codec | ac97d | 1 week |
---
## 11. Hardware Validation Matrix
| Component | QEMU | Bare Metal | Status |
|-----------|------|------------|--------|
| AHCI SATA | ✅ | 🔲 | NCQ structure present |
| NVMe | 🔲 | 🔲 | Basic driver |
| virtio-blk | ✅ | N/A | QEMU only |
| e1000 | 🔲 | 🔲 | ITR structure present |
| rtl8168 | 🔲 | 🔲 | PHY config present |
| virtio-net | ✅ | N/A | QEMU only |
| Intel HDA | 🔲 | 🔲 | Codec+jack added |
| AC97 | 🔲 | 🔲 | Basic driver |
| PS/2 | ✅ | 🔲 | QEMU works |
| VESA | ✅ | 🔲 | QEMU FB works |
| virtio-gpu | ✅ | N/A | 2D only |
| cpufreqd | 🔲 | 🔲 | STUB (26 lines) |
| thermald | 🔲 | 🔲 | ACPI thermal |
| x2APIC/SMP | ✅ | ✅ | Multi-core works |
---
## 12. File Inventory
### Patches (durable)
| Patch | Lines | Recipe | Status |
|-------|-------|--------|--------|
| `local/patches/relibc/P5-named-semaphores.patch` | 249 | relibc | ✅ Wired |
| `local/patches/base/P6-driver-main-fixes.patch` | 165 | base | ✅ Wired |
| `local/patches/base/P6-driver-new-modules.patch` | 185 | base | ✅ Wired |
| `local/patches/base/P6-cpufreqd-real-impl.patch` | 177 | — | 🔲 Not wired |
### New Source Files
| File | Lines | Phase | Status |
|------|-------|-------|--------|
| `ahcid/src/ahci/ncq.rs` | 12 | Phase 1 | ⚠️ Truncated |
| `e1000d/src/itr.rs` | 9 | Phase 2 | ⚠️ Truncated |
| `rtl8168d/src/phy.rs` | 5 | Phase 2 | ⚠️ Truncated |
| `ihdad/src/hda/codec.rs` | 4 | Phase 3 | ⚠️ Truncated |
| `ihdad/src/hda/jack.rs` | 5 | Phase 3 | ⚠️ Truncated |
| `cpufreqd/src/main.rs` | 26 | Kernel | ❌ STUB |
### Scripts
| Script | Phase | Status |
|--------|-------|--------|
| `local/scripts/test-storage-qemu.sh` | Phase 5 | ✅ |
| `local/scripts/test-network-qemu.sh` | Phase 5 | ✅ |
| `local/scripts/lint-config-paths.sh` | Phase 0 | ✅ |
| `local/scripts/validate-init-services.sh` | Phase 0 | ✅ |
| `local/scripts/validate-file-ownership.sh` | Phase 0 | ✅ |
| `local/scripts/generate-installs-manifest.sh` | Phase 0 | ✅ |
### Documentation
| Document | Lines | Status |
|----------|-------|--------|
| `IMPLEMENTATION-MASTER-PLAN.md` | — | This file |
| `CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md` | 672 | Superseded |
| `COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md` | 316 | Superseded |
| `HARDWARE-VALIDATION-MATRIX.md` | 28 | Superseded |
| `BUILD-SYSTEM-HARDENING-PLAN.md` | 403 | Active |
| `BUILD-SYSTEM-INVARIANTS.md` | 436 | Active |
| `ACPI-IMPROVEMENT-PLAN.md` | 839 | Active |
| `IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md` | 916 | Active |
---
## 14. Scheduler & Threading Assessment (2026-05-04)
### Architecture
- **Kernel**: DWRR scheduler (577 lines), 40 priority levels, per-CPU queues, futex (222 lines)
- **Userspace**: proc manager (2,638 lines), pthread (440 lines), signal delivery via proc scheme
- **IPC bridge**: 3 round-trips for thread creation vs Linux's single clone() syscall
### Strengths
- DWRR with geometric weights, CPU affinity masks, soft-blocking with monotonic timeout
- Full POSIX process model (PID/PGID/SID, job control, orphan detection)
- Futex with physical-address keys for cross-process synchronization
### Critical Gaps
1. **PIT-based tick (~148Hz)** — LAPIC timer exists but `setup_timer()` is commented out. Should use Periodic/TscDeadline mode at 1000Hz.
2. **Global CONTEXT_SWITCH_LOCK** — spinlock serializes all context switches across CPUs. Should be per-CPU.
3. **No load balancing** — idle CPUs don't steal work from busy CPUs
4. **No RT scheduling** — missing FIFO/RR/Deadline classes
5. **No cgroups** — no CPU bandwidth control or resource limits
6. **Thread creation latency** — 3 IPC hops vs single clone()
| Tier | Duration |
|------|----------|
| T0 (kernel substrate) | 10-14 weeks |
| T1 (storage + network) | 6-10 weeks |
| T2 (audio + input) | 6-10 weeks |
| T3 (completeness) | 4-8 weeks |
| **Total (2 developers, parallel)** | **16-24 weeks** |
| **Total (1 developer, sequential)** | **26-42 weeks** |
@@ -2,7 +2,7 @@ diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
index 9f507221..c69c2cfa 100644
--- a/daemon/src/lib.rs
+++ b/daemon/src/lib.rs
@@ -10,15 +10,25 @@ use libredox::Fd;
@@ -10,15 +10,26 @@ use libredox::Fd;
use redox_scheme::Socket;
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
@@ -10,6 +10,7 @@ index 9f507221..c69c2cfa 100644
- let fd: RawFd = std::env::var(var).unwrap().parse().unwrap();
+unsafe fn get_fd(var: &str) -> Option<RawFd> {
+ let fd: RawFd = match std::env::var(var)
+ .map_err(|e| eprintln!("daemon: env var {var} not set: {e}"))
+ .ok()
+ .and_then(|val| {
+ val.parse()
@@ -32,7 +33,7 @@ index 9f507221..c69c2cfa 100644
}
unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
@@ -38,20 +48,26 @@ unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
@@ -38,20 +49,26 @@ unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
/// A long running background process that handles requests.
#[must_use = "Daemon::ready must be called"]
pub struct Daemon {
@@ -62,7 +63,7 @@ index 9f507221..c69c2cfa 100644
}
/// Executes `Command` as a child process.
@@ -83,25 +99,28 @@ impl Daemon {
@@ -83,25 +100,28 @@ impl Daemon {
/// A long running background process that handles requests using schemes.
#[must_use = "SchemeDaemon::ready must be called"]
pub struct SchemeDaemon {
@@ -1,8 +1,8 @@
--- a/src/header/sys_types_internal/cbindgen.toml
+++ b/src/header/sys_types_internal/cbindgen.toml
@@ -1,4 +1,4 @@
-sys_includes = ["stddef.h", "stdint.h"]
+sys_includes = ["stddef.h"]
-sys_includes = ["stddef.h"]
+sys_includes = ["stddef.h", "stdint.h"]
# TODO: figure out how to export void* type
after_includes = """
+2 -91
View File
@@ -7,102 +7,13 @@ template = "custom"
script = """
DYNAMIC_INIT
export ac_cv_func___fseterr=yes
# Gnulib cross-compilation: relibc has standard POSIX headers and types
# but gnulib's configure can't run test programs during cross-compilation.
# Without these, gnulib generates broken #define fallbacks and replacement headers.
# Standard headers (gnulib can't detect these when cross-compiling)
export ac_cv_header_stdio_h=yes
export ac_cv_header_stdlib_h=yes
export ac_cv_header_string_h=yes
export ac_cv_header_strings_h=yes
export ac_cv_header_inttypes_h=yes
export ac_cv_header_stdint_h=yes
export ac_cv_header_unistd_h=yes
export ac_cv_header_sys_types_h=yes
export ac_cv_header_sys_stat_h=yes
export ac_cv_header_time_h=yes
export ac_cv_header_sys_time_h=yes
export ac_cv_header_sys_select_h=yes
export ac_cv_header_wchar_h=yes
export ac_cv_header_wctype_h=yes
export ac_cv_header_signal_h=yes
export ac_cv_header_dirent_h=yes
export ac_cv_header_fcntl_h=yes
export ac_cv_header_locale_h=yes
export ac_cv_header_errno_h=yes
export ac_cv_header_ctype_h=yes
export ac_cv_header_limits_h=yes
export ac_cv_header_stdarg_h=yes
export ac_cv_header_stddef_h=yes
export ac_cv_header_math_h=yes
export ac_cv_header_spawn_h=yes
export gl_cv_header_inttypes_h=yes
export gl_cv_header_stdint_h=yes
export gl_cv_header_inttypes_h_with_uintmax=yes
export ac_cv_have_inttypes_h_with_uintmax=yes
# Standard types (gnulib generates broken fallbacks without these)
export ac_cv_type_intmax_t=yes
export ac_cv_type_uintmax_t=yes
export ac_cv_type_gid_t=yes
export ac_cv_type_uid_t=yes
export ac_cv_type_pid_t=yes
export ac_cv_type_mode_t=yes
export ac_cv_type_off_t=yes
export ac_cv_type_size_t=yes
export ac_cv_type_ssize_t=yes
export ac_cv_type_ptrdiff_t=yes
export ac_cv_type_nlink_t=yes
export ac_cv_type_mbstate_t=yes
export gl_cv_type_intmax_t=yes
export gl_cv_type_ptrdiff_t_signed=yes
export gl_cv_header_inttypes_h_with_uintmax=yes
export ac_cv_have_inttypes_h_with_uintmax=yes
# Spawn functions (relibc provides these via the P3-spawn patch)
export gl_cv_func_spawn_posix_spawn=yes
export gl_cv_func_spawn_posix_spawnp=yes
export ac_cv_func_posix_spawn=yes
export ac_cv_func_posix_spawnp=yes
export ac_cv_func_posix_spawn_file_actions_init=yes
export ac_cv_func_posix_spawn_file_actions_destroy=yes
export ac_cv_func_posix_spawn_file_actions_addopen=yes
export ac_cv_func_posix_spawn_file_actions_addclose=yes
export ac_cv_func_posix_spawn_file_actions_adddup2=yes
export ac_cv_func_posix_spawnattr_init=yes
export ac_cv_func_posix_spawnattr_destroy=yes
export ac_cv_func_posix_spawnattr_setflags=yes
export ac_cv_func_posix_spawnattr_getflags=yes
export ac_cv_func_posix_spawnattr_setsigmask=yes
export ac_cv_func_posix_spawnattr_getsigmask=yes
# Other functions
export ac_cv_func_getpagesize=yes
export ac_cv_func_memcmp_working=yes
export ac_cv_func_mmap_fixed_mapped=yes
# Spawn types
export ac_cv_type_sigset_t=yes
export ac_cv_type_posix_spawnattr_t=yes
export ac_cv_type_posix_spawn_file_actions_t=yes
COOKBOOK_CONFIGURE_FLAGS+=(
--disable-nls
)
# Cross-compilation fix: run configure manually, then patch the
# generated Makefile to use host bison instead of the cross-compiled
# wrapper. The generated Makefile hardcodes
# BISON = $(top_builddir)/tests/bison
# which wraps the x86_64-unknown-redox binary — unrunnable on the host.
"${COOKBOOK_CONFIGURE}" "${COOKBOOK_CONFIGURE_FLAGS[@]}"
sed -i 's|^BISON = .*|BISON = /usr/bin/bison|' "${COOKBOOK_BUILD}/Makefile"
# Fix gnulib cross-compilation misdetections in config.h
"${COOKBOOK_ROOT}/local/scripts/gnulib-cross-fix.sh" "${COOKBOOK_BUILD}/lib/config.h"
"${COOKBOOK_MAKE}" -j "${COOKBOOK_MAKE_JOBS}"
"${COOKBOOK_MAKE}" install DESTDIR="${COOKBOOK_STAGE}"
cookbook_configure
"""
[package]
+6340 -7625
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,4 +1,4 @@
@set UPDATED 15 May 2026
@set UPDATED-MONTH May 2026
@set UPDATED 12 September 2021
@set UPDATED-MONTH September 2021
@set EDITION 3.8.2
@set VERSION 3.8.2
@@ -1,4 +1,4 @@
@set UPDATED 15 May 2026
@set UPDATED-MONTH May 2026
@set UPDATED 12 September 2021
@set UPDATED-MONTH September 2021
@set EDITION 3.8.2
@set VERSION 3.8.2
+167 -175
View File
@@ -585,10 +585,10 @@
/* Define to 1 if you have the <bp-sym.h> header file. */
#undef HAVE_BP_SYM_H
/* Define to 1 if you have the 'canonicalize_file_name' function. */
/* Define to 1 if you have the `canonicalize_file_name' function. */
#undef HAVE_CANONICALIZE_FILE_NAME
/* Define to 1 if you have the 'catgets' function. */
/* Define to 1 if you have the `catgets' function. */
#undef HAVE_CATGETS
/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the
@@ -599,16 +599,16 @@
the CoreFoundation framework. */
#undef HAVE_CFPREFERENCESCOPYAPPVALUE
/* Define to 1 if you have the 'clock_gettime' function. */
/* Define to 1 if you have the `clock_gettime' function. */
#undef HAVE_CLOCK_GETTIME
/* Define to 1 if you have the 'clock_settime' function. */
/* Define to 1 if you have the `clock_settime' function. */
#undef HAVE_CLOCK_SETTIME
/* Define to 1 if you have the 'closedir' function. */
/* Define to 1 if you have the `closedir' function. */
#undef HAVE_CLOSEDIR
/* Define to 1 if you have the 'confstr' function. */
/* Define to 1 if you have the `confstr' function. */
#undef HAVE_CONFSTR
/* Define if the copysignf function is declared in <math.h> and available in
@@ -623,7 +623,7 @@
libc. */
#undef HAVE_COPYSIGN_IN_LIBC
/* Define to 1 if you have the 'copy_file_range' function. */
/* Define to 1 if you have the `copy_file_range' function. */
#undef HAVE_COPY_FILE_RANGE
/* Define to 1 if you have the <crtdefs.h> header file. */
@@ -633,127 +633,127 @@
*/
#undef HAVE_DCGETTEXT
/* Define to 1 if you have the declaration of 'alarm', and to 0 if you don't.
/* Define to 1 if you have the declaration of `alarm', and to 0 if you don't.
*/
#undef HAVE_DECL_ALARM
/* Define to 1 if you have the declaration of 'clearerr_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `clearerr_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_CLEARERR_UNLOCKED
/* Define to 1 if you have the declaration of 'copysign', and to 0 if you
/* Define to 1 if you have the declaration of `copysign', and to 0 if you
don't. */
#undef HAVE_DECL_COPYSIGN
/* Define to 1 if you have the declaration of 'copysignf', and to 0 if you
/* Define to 1 if you have the declaration of `copysignf', and to 0 if you
don't. */
#undef HAVE_DECL_COPYSIGNF
/* Define to 1 if you have the declaration of 'copysignl', and to 0 if you
/* Define to 1 if you have the declaration of `copysignl', and to 0 if you
don't. */
#undef HAVE_DECL_COPYSIGNL
/* Define to 1 if you have the declaration of 'dirfd', and to 0 if you don't.
/* Define to 1 if you have the declaration of `dirfd', and to 0 if you don't.
*/
#undef HAVE_DECL_DIRFD
/* Define to 1 if you have the declaration of 'ecvt', and to 0 if you don't.
/* Define to 1 if you have the declaration of `ecvt', and to 0 if you don't.
*/
#undef HAVE_DECL_ECVT
/* Define to 1 if you have the declaration of 'execvpe', and to 0 if you
/* Define to 1 if you have the declaration of `execvpe', and to 0 if you
don't. */
#undef HAVE_DECL_EXECVPE
/* Define to 1 if you have the declaration of 'fchdir', and to 0 if you don't.
/* Define to 1 if you have the declaration of `fchdir', and to 0 if you don't.
*/
#undef HAVE_DECL_FCHDIR
/* Define to 1 if you have the declaration of 'fcloseall', and to 0 if you
/* Define to 1 if you have the declaration of `fcloseall', and to 0 if you
don't. */
#undef HAVE_DECL_FCLOSEALL
/* Define to 1 if you have the declaration of 'fcvt', and to 0 if you don't.
/* Define to 1 if you have the declaration of `fcvt', and to 0 if you don't.
*/
#undef HAVE_DECL_FCVT
/* Define to 1 if you have the declaration of 'fdopendir', and to 0 if you
/* Define to 1 if you have the declaration of `fdopendir', and to 0 if you
don't. */
#undef HAVE_DECL_FDOPENDIR
/* Define to 1 if you have the declaration of 'feof_unlocked', and to 0 if you
/* Define to 1 if you have the declaration of `feof_unlocked', and to 0 if you
don't. */
#undef HAVE_DECL_FEOF_UNLOCKED
/* Define to 1 if you have the declaration of 'ferror_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `ferror_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_FERROR_UNLOCKED
/* Define to 1 if you have the declaration of 'fflush_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `fflush_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_FFLUSH_UNLOCKED
/* Define to 1 if you have the declaration of 'fgets_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_FGETS_UNLOCKED
/* Define to 1 if you have the declaration of 'fputc_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `fputc_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_FPUTC_UNLOCKED
/* Define to 1 if you have the declaration of 'fputs_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `fputs_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_FPUTS_UNLOCKED
/* Define to 1 if you have the declaration of 'fread_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `fread_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_FREAD_UNLOCKED
/* Define to 1 if you have the declaration of 'fwrite_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `fwrite_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_FWRITE_UNLOCKED
/* Define to 1 if you have the declaration of 'gcvt', and to 0 if you don't.
/* Define to 1 if you have the declaration of `gcvt', and to 0 if you don't.
*/
#undef HAVE_DECL_GCVT
/* Define to 1 if you have the declaration of 'getchar_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `getchar_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_GETCHAR_UNLOCKED
/* Define to 1 if you have the declaration of 'getcwd', and to 0 if you don't.
/* Define to 1 if you have the declaration of `getcwd', and to 0 if you don't.
*/
#undef HAVE_DECL_GETCWD
/* Define to 1 if you have the declaration of 'getc_unlocked', and to 0 if you
/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you
don't. */
#undef HAVE_DECL_GETC_UNLOCKED
/* Define to 1 if you have the declaration of 'getdelim', and to 0 if you
/* Define to 1 if you have the declaration of `getdelim', and to 0 if you
don't. */
#undef HAVE_DECL_GETDELIM
/* Define to 1 if you have the declaration of 'getdtablesize', and to 0 if you
/* Define to 1 if you have the declaration of `getdtablesize', and to 0 if you
don't. */
#undef HAVE_DECL_GETDTABLESIZE
/* Define to 1 if you have the declaration of 'gethrtime', and to 0 if you
/* Define to 1 if you have the declaration of `gethrtime', and to 0 if you
don't. */
#undef HAVE_DECL_GETHRTIME
/* Define to 1 if you have the declaration of 'getline', and to 0 if you
/* Define to 1 if you have the declaration of `getline', and to 0 if you
don't. */
#undef HAVE_DECL_GETLINE
/* Define to 1 if you have the declaration of 'iswblank', and to 0 if you
/* Define to 1 if you have the declaration of `iswblank', and to 0 if you
don't. */
#undef HAVE_DECL_ISWBLANK
/* Define to 1 if you have the declaration of 'mbrtowc', and to 0 if you
/* Define to 1 if you have the declaration of `mbrtowc', and to 0 if you
don't. */
#undef HAVE_DECL_MBRTOWC
/* Define to 1 if you have the declaration of 'mbsinit', and to 0 if you
/* Define to 1 if you have the declaration of `mbsinit', and to 0 if you
don't. */
#undef HAVE_DECL_MBSINIT
@@ -761,122 +761,122 @@
otherwise. */
#undef HAVE_DECL_MBSWIDTH_IN_WCHAR_H
/* Define to 1 if you have the declaration of 'memrchr', and to 0 if you
/* Define to 1 if you have the declaration of `memrchr', and to 0 if you
don't. */
#undef HAVE_DECL_MEMRCHR
/* Define to 1 if you have the declaration of 'obstack_printf', and to 0 if
/* Define to 1 if you have the declaration of `obstack_printf', and to 0 if
you don't. */
#undef HAVE_DECL_OBSTACK_PRINTF
/* Define to 1 if you have the declaration of 'posix_spawn', and to 0 if you
/* Define to 1 if you have the declaration of `posix_spawn', and to 0 if you
don't. */
#undef HAVE_DECL_POSIX_SPAWN
/* Define to 1 if you have the declaration of 'program_invocation_name', and
/* Define to 1 if you have the declaration of `program_invocation_name', and
to 0 if you don't. */
#undef HAVE_DECL_PROGRAM_INVOCATION_NAME
/* Define to 1 if you have the declaration of 'program_invocation_short_name',
/* Define to 1 if you have the declaration of `program_invocation_short_name',
and to 0 if you don't. */
#undef HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME
/* Define to 1 if you have the declaration of 'putchar_unlocked', and to 0 if
/* Define to 1 if you have the declaration of `putchar_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_PUTCHAR_UNLOCKED
/* Define to 1 if you have the declaration of 'putc_unlocked', and to 0 if you
/* Define to 1 if you have the declaration of `putc_unlocked', and to 0 if you
don't. */
#undef HAVE_DECL_PUTC_UNLOCKED
/* Define to 1 if you have the declaration of 'setenv', and to 0 if you don't.
/* Define to 1 if you have the declaration of `setenv', and to 0 if you don't.
*/
#undef HAVE_DECL_SETENV
/* Define to 1 if you have the declaration of 'snprintf', and to 0 if you
/* Define to 1 if you have the declaration of `snprintf', and to 0 if you
don't. */
#undef HAVE_DECL_SNPRINTF
/* Define to 1 if you have the declaration of 'stpncpy', and to 0 if you
/* Define to 1 if you have the declaration of `stpncpy', and to 0 if you
don't. */
#undef HAVE_DECL_STPNCPY
/* Define to 1 if you have the declaration of 'strdup', and to 0 if you don't.
/* Define to 1 if you have the declaration of `strdup', and to 0 if you don't.
*/
#undef HAVE_DECL_STRDUP
/* Define to 1 if you have the declaration of 'strerror_r', and to 0 if you
/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
don't. */
#undef HAVE_DECL_STRERROR_R
/* Define to 1 if you have the declaration of 'strndup', and to 0 if you
/* Define to 1 if you have the declaration of `strndup', and to 0 if you
don't. */
#undef HAVE_DECL_STRNDUP
/* Define to 1 if you have the declaration of 'strnlen', and to 0 if you
/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
don't. */
#undef HAVE_DECL_STRNLEN
/* Define to 1 if you have the declaration of 'towlower', and to 0 if you
/* Define to 1 if you have the declaration of `towlower', and to 0 if you
don't. */
#undef HAVE_DECL_TOWLOWER
/* Define to 1 if you have the declaration of 'unsetenv', and to 0 if you
/* Define to 1 if you have the declaration of `unsetenv', and to 0 if you
don't. */
#undef HAVE_DECL_UNSETENV
/* Define to 1 if you have the declaration of 'vsnprintf', and to 0 if you
/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
don't. */
#undef HAVE_DECL_VSNPRINTF
/* Define to 1 if you have the declaration of 'wcsdup', and to 0 if you don't.
/* Define to 1 if you have the declaration of `wcsdup', and to 0 if you don't.
*/
#undef HAVE_DECL_WCSDUP
/* Define to 1 if you have the declaration of 'wcwidth', and to 0 if you
/* Define to 1 if you have the declaration of `wcwidth', and to 0 if you
don't. */
#undef HAVE_DECL_WCWIDTH
/* Define to 1 if you have the declaration of '_snprintf', and to 0 if you
/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you
don't. */
#undef HAVE_DECL__SNPRINTF
/* Define to 1 if you have the declaration of '__argv', and to 0 if you don't.
/* Define to 1 if you have the declaration of `__argv', and to 0 if you don't.
*/
#undef HAVE_DECL___ARGV
/* Define to 1 if you have the declaration of '__fpending', and to 0 if you
/* Define to 1 if you have the declaration of `__fpending', and to 0 if you
don't. */
#undef HAVE_DECL___FPENDING
/* Define to 1 if you have the <dirent.h> header file. */
#undef HAVE_DIRENT_H
/* Define to 1 if you have the 'dirfd' function. */
/* Define to 1 if you have the `dirfd' function. */
#undef HAVE_DIRFD
/* Define if you have the declaration of environ. */
#undef HAVE_ENVIRON_DECL
/* Define to 1 if you have the 'faccessat' function. */
/* Define to 1 if you have the `faccessat' function. */
#undef HAVE_FACCESSAT
/* Define to 1 if you have the 'fchdir' function. */
/* Define to 1 if you have the `fchdir' function. */
#undef HAVE_FCHDIR
/* Define to 1 if you have the 'fcntl' function. */
/* Define to 1 if you have the `fcntl' function. */
#undef HAVE_FCNTL
/* Define to 1 if you have the 'fdopendir' function. */
/* Define to 1 if you have the `fdopendir' function. */
#undef HAVE_FDOPENDIR
/* Define to 1 if you have the <features.h> header file. */
#undef HAVE_FEATURES_H
/* Define to 1 if you have the 'ffsl' function. */
/* Define to 1 if you have the `ffsl' function. */
#undef HAVE_FFSL
/* Define to 1 if you have the 'flockfile' function. */
/* Define to 1 if you have the `flockfile' function. */
#undef HAVE_FLOCKFILE
/* Define if the 'free' function is guaranteed to preserve errno. */
@@ -888,50 +888,50 @@
/* Define if the frexp function is available in libc. */
#undef HAVE_FREXP_IN_LIBC
/* Define to 1 if you have the 'fstatat' function. */
/* Define to 1 if you have the `fstatat' function. */
#undef HAVE_FSTATAT
/* Define to 1 if you have the 'fsync' function. */
/* Define to 1 if you have the `fsync' function. */
#undef HAVE_FSYNC
/* Define to 1 if you have the 'funlockfile' function. */
/* Define to 1 if you have the `funlockfile' function. */
#undef HAVE_FUNLOCKFILE
/* Define to 1 if you have the 'getcwd' function. */
/* Define to 1 if you have the `getcwd' function. */
#undef HAVE_GETCWD
/* Define to 1 if getcwd works, but with shorter paths than is generally
tested with the replacement. */
#undef HAVE_GETCWD_SHORTER
/* Define to 1 if you have the 'getdelim' function. */
/* Define to 1 if you have the `getdelim' function. */
#undef HAVE_GETDELIM
/* Define to 1 if you have the 'getdtablesize' function. */
/* Define to 1 if you have the `getdtablesize' function. */
#undef HAVE_GETDTABLESIZE
/* Define to 1 if you have the 'getexecname' function. */
/* Define to 1 if you have the `getexecname' function. */
#undef HAVE_GETEXECNAME
/* Define to 1 if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H
/* Define to 1 if you have the 'getopt_long_only' function. */
/* Define to 1 if you have the `getopt_long_only' function. */
#undef HAVE_GETOPT_LONG_ONLY
/* Define to 1 if the system has the 'getpagesize' function. */
#undef HAVE_GETPAGESIZE
/* Define to 1 if you have the 'getprogname' function. */
/* Define to 1 if you have the `getprogname' function. */
#undef HAVE_GETPROGNAME
/* Define to 1 if you have the 'getrusage' function. */
/* Define to 1 if you have the `getrusage' function. */
#undef HAVE_GETRUSAGE
/* Define if the GNU gettext() function is already present or preinstalled. */
#undef HAVE_GETTEXT
/* Define to 1 if you have the 'gettimeofday' function. */
/* Define to 1 if you have the `gettimeofday' function. */
#undef HAVE_GETTIMEOFDAY
/* Define if you have the iconv() function and it works. */
@@ -955,7 +955,7 @@
declares uintmax_t. */
#undef HAVE_INTTYPES_H_WITH_UINTMAX
/* Define to 1 if you have the 'isascii' function. */
/* Define to 1 if you have the `isascii' function. */
#undef HAVE_ISASCII
/* Define if the isnan(double) function is available in libc. */
@@ -967,10 +967,10 @@
/* Define if the isnan(long double) function is available in libc. */
#undef HAVE_ISNANL_IN_LIBC
/* Define to 1 if you have the 'iswblank' function. */
/* Define to 1 if you have the `iswblank' function. */
#undef HAVE_ISWBLANK
/* Define to 1 if you have the 'iswcntrl' function. */
/* Define to 1 if you have the `iswcntrl' function. */
#undef HAVE_ISWCNTRL
/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
@@ -991,7 +991,7 @@
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
/* Define to 1 if you have the 'link' function. */
/* Define to 1 if you have the `link' function. */
#undef HAVE_LINK
/* Define to 1 if you have the <locale.h> header file. */
@@ -1000,7 +1000,7 @@
/* Define to 1 if the system has the type 'long long int'. */
#undef HAVE_LONG_LONG_INT
/* Define to 1 if you have the 'lstat' function. */
/* Define to 1 if you have the `lstat' function. */
#undef HAVE_LSTAT
/* Define to 1 if you have the <mach-o/dyld.h> header file. */
@@ -1016,22 +1016,22 @@
/* Define to 1 if you have the <math.h> header file. */
#undef HAVE_MATH_H
/* Define to 1 if you have the 'mbrtowc' function. */
/* Define to 1 if you have the `mbrtowc' function. */
#undef HAVE_MBRTOWC
/* Define to 1 if you have the 'mbsinit' function. */
/* Define to 1 if you have the `mbsinit' function. */
#undef HAVE_MBSINIT
/* Define to 1 if <wchar.h> declares mbstate_t. */
#undef HAVE_MBSTATE_T
/* Define to 1 if you have the 'mempcpy' function. */
/* Define to 1 if you have the `mempcpy' function. */
#undef HAVE_MEMPCPY
/* Define to 1 if you have the 'memrchr' function. */
/* Define to 1 if you have the `memrchr' function. */
#undef HAVE_MEMRCHR
/* Define to 1 if you have the 'microuptime' function. */
/* Define to 1 if you have the `microuptime' function. */
#undef HAVE_MICROUPTIME
/* Define to 1 if getcwd minimally works, that is, its result can be trusted
@@ -1047,29 +1047,29 @@
/* Define to 1 if <sys/param.h> defines the MIN and MAX macros. */
#undef HAVE_MINMAX_IN_SYS_PARAM_H
/* Define to 1 if you have the 'mprotect' function. */
/* Define to 1 if you have the `mprotect' function. */
#undef HAVE_MPROTECT
/* Define to 1 on MSVC platforms that have the "invalid parameter handler"
concept. */
#undef HAVE_MSVC_INVALID_PARAMETER_HANDLER
/* Define to 1 if you have the 'nanouptime' function. */
/* Define to 1 if you have the `nanouptime' function. */
#undef HAVE_NANOUPTIME
/* Define to 1 if you have the 'nl_langinfo' function. */
/* Define to 1 if you have the `nl_langinfo' function. */
#undef HAVE_NL_LANGINFO
/* Define to 1 if the system has obstacks that work with any size object. */
#undef HAVE_OBSTACK
/* Define to 1 if you have the 'obstack_printf' function. */
/* Define to 1 if you have the `obstack_printf' function. */
#undef HAVE_OBSTACK_PRINTF
/* Define to 1 if you have the 'openat' function. */
/* Define to 1 if you have the `openat' function. */
#undef HAVE_OPENAT
/* Define to 1 if you have the 'opendir' function. */
/* Define to 1 if you have the `opendir' function. */
#undef HAVE_OPENDIR
/* Define to 1 if getcwd works, except it sometimes fails when it shouldn't,
@@ -1079,27 +1079,27 @@
/* Define to 1 if you have the <paths.h> header file. */
#undef HAVE_PATHS_H
/* Define to 1 if you have the 'pipe' function. */
/* Define to 1 if you have the `pipe' function. */
#undef HAVE_PIPE
/* Define to 1 if you have the 'pipe2' function. */
/* Define to 1 if you have the `pipe2' function. */
#undef HAVE_PIPE2
/* Define to 1 if you have the 'posix_spawn' function. */
/* Define to 1 if you have the `posix_spawn' function. */
#undef HAVE_POSIX_SPAWN
/* Define to 1 if the system has the type 'posix_spawnattr_t'. */
/* Define to 1 if the system has the type `posix_spawnattr_t'. */
#undef HAVE_POSIX_SPAWNATTR_T
/* Define to 1 if you have the 'posix_spawn_file_actions_addchdir' function.
/* Define to 1 if you have the `posix_spawn_file_actions_addchdir' function.
*/
#undef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR
/* Define to 1 if you have the 'posix_spawn_file_actions_addchdir_np'
/* Define to 1 if you have the `posix_spawn_file_actions_addchdir_np'
function. */
#undef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP
/* Define to 1 if the system has the type 'posix_spawn_file_actions_t'. */
/* Define to 1 if the system has the type `posix_spawn_file_actions_t'. */
#undef HAVE_POSIX_SPAWN_FILE_ACTIONS_T
/* Define if you have the <pthread.h> header and the POSIX threads API. */
@@ -1115,13 +1115,13 @@
reader. */
#undef HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
/* Define to 1 if you have the 'raise' function. */
/* Define to 1 if you have the `raise' function. */
#undef HAVE_RAISE
/* Define to 1 if you have the 'rawmemchr' function. */
/* Define to 1 if you have the `rawmemchr' function. */
#undef HAVE_RAWMEMCHR
/* Define to 1 if you have the 'readdir' function. */
/* Define to 1 if you have the `readdir' function. */
#undef HAVE_READDIR
/* Define if you have the readline library. */
@@ -1133,19 +1133,19 @@
/* Define to 1 if you have the <readline/readline.h> header file. */
#undef HAVE_READLINE_READLINE_H
/* Define to 1 if you have the 'readlink' function. */
/* Define to 1 if you have the `readlink' function. */
#undef HAVE_READLINK
/* Define to 1 if you have the 'readlinkat' function. */
/* Define to 1 if you have the `readlinkat' function. */
#undef HAVE_READLINKAT
/* Define to 1 if you have the 'reallocarray' function. */
/* Define to 1 if you have the `reallocarray' function. */
#undef HAVE_REALLOCARRAY
/* Define to 1 if you have the 'realpath' function. */
/* Define to 1 if you have the `realpath' function. */
#undef HAVE_REALPATH
/* Define to 1 if you have the 'rewinddir' function. */
/* Define to 1 if you have the `rewinddir' function. */
#undef HAVE_REWINDDIR
/* Define to 1 if 'long double' and 'double' have the same representation. */
@@ -1154,10 +1154,10 @@
/* Define to 1 if you have the <sched.h> header file. */
#undef HAVE_SCHED_H
/* Define to 1 if you have the 'sched_setparam' function. */
/* Define to 1 if you have the `sched_setparam' function. */
#undef HAVE_SCHED_SETPARAM
/* Define to 1 if you have the 'sched_setscheduler' function. */
/* Define to 1 if you have the `sched_setscheduler' function. */
#undef HAVE_SCHED_SETSCHEDULER
/* Define to 1 if you have the <sdkddkver.h> header file. */
@@ -1166,31 +1166,31 @@
/* Define to 1 if you have the <search.h> header file. */
#undef HAVE_SEARCH_H
/* Define to 1 if you have the 'setdtablesize' function. */
/* Define to 1 if you have the `setdtablesize' function. */
#undef HAVE_SETDTABLESIZE
/* Define to 1 if you have the 'setegid' function. */
/* Define to 1 if you have the `setegid' function. */
#undef HAVE_SETEGID
/* Define to 1 if you have the 'setenv' function. */
/* Define to 1 if you have the `setenv' function. */
#undef HAVE_SETENV
/* Define to 1 if you have the 'seteuid' function. */
/* Define to 1 if you have the `seteuid' function. */
#undef HAVE_SETEUID
/* Define to 1 if you have the 'setlocale' function. */
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
/* Define to 1 if you have the 'sigaction' function. */
/* Define to 1 if you have the `sigaction' function. */
#undef HAVE_SIGACTION
/* Define to 1 if you have the 'sigaltstack' function. */
/* Define to 1 if you have the `sigaltstack' function. */
#undef HAVE_SIGALTSTACK
/* Define to 1 if the system has the type 'siginfo_t'. */
/* Define to 1 if the system has the type `siginfo_t'. */
#undef HAVE_SIGINFO_T
/* Define to 1 if you have the 'siginterrupt' function. */
/* Define to 1 if you have the `siginterrupt' function. */
#undef HAVE_SIGINTERRUPT
/* Define to 1 if 'sig_atomic_t' is a signed integer type. */
@@ -1202,13 +1202,13 @@
/* Define to 1 if 'wint_t' is a signed integer type. */
#undef HAVE_SIGNED_WINT_T
/* Define to 1 if the system has the type 'sigset_t'. */
/* Define to 1 if the system has the type `sigset_t'. */
#undef HAVE_SIGSET_T
/* Define to 1 if the system has the type 'sig_atomic_t'. */
/* Define to 1 if the system has the type `sig_atomic_t'. */
#undef HAVE_SIG_ATOMIC_T
/* Define to 1 if you have the 'snprintf' function. */
/* Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF
/* Define if the return value of the snprintf function is the number of of
@@ -1239,16 +1239,16 @@
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the 'stpcpy' function. */
/* Define to 1 if you have the `stpcpy' function. */
#undef HAVE_STPCPY
/* Define if you have the stpncpy() function and it works. */
#undef HAVE_STPNCPY
/* Define to 1 if you have the 'strchrnul' function. */
/* Define to 1 if you have the `strchrnul' function. */
#undef HAVE_STRCHRNUL
/* Define to 1 if you have the 'strerror_r' function. */
/* Define to 1 if you have the `strerror_r' function. */
#undef HAVE_STRERROR_R
/* Define to 1 if you have the <strings.h> header file. */
@@ -1257,43 +1257,43 @@
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the 'strndup' function. */
/* Define to 1 if you have the `strndup' function. */
#undef HAVE_STRNDUP
/* Define to 1 if you have the 'strnlen' function. */
/* Define to 1 if you have the `strnlen' function. */
#undef HAVE_STRNLEN
/* Define to 1 if 'sa_sigaction' is a member of 'struct sigaction'. */
/* Define to 1 if `sa_sigaction' is a member of `struct sigaction'. */
#undef HAVE_STRUCT_SIGACTION_SA_SIGACTION
/* Define to 1 if 'st_atimensec' is a member of 'struct stat'. */
/* Define to 1 if `st_atimensec' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_ATIMENSEC
/* Define to 1 if 'st_atimespec.tv_nsec' is a member of 'struct stat'. */
/* Define to 1 if `st_atimespec.tv_nsec' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
/* Define to 1 if 'st_atim.st__tim.tv_nsec' is a member of 'struct stat'. */
/* Define to 1 if `st_atim.st__tim.tv_nsec' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC
/* Define to 1 if 'st_atim.tv_nsec' is a member of 'struct stat'. */
/* Define to 1 if `st_atim.tv_nsec' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
/* Define to 1 if 'st_birthtimensec' is a member of 'struct stat'. */
/* Define to 1 if `st_birthtimensec' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC
/* Define to 1 if 'st_birthtimespec.tv_nsec' is a member of 'struct stat'. */
/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
/* Define to 1 if 'st_birthtim.tv_nsec' is a member of 'struct stat'. */
/* Define to 1 if `st_birthtim.tv_nsec' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC
/* Define to 1 if the system has the type 'struct tms'. */
/* Define to 1 if the system has the type `struct tms'. */
#undef HAVE_STRUCT_TMS
/* Define to 1 if you have the 'strverscmp' function. */
/* Define to 1 if you have the `strverscmp' function. */
#undef HAVE_STRVERSCMP
/* Define to 1 if you have the 'symlink' function. */
/* Define to 1 if you have the `symlink' function. */
#undef HAVE_SYMLINK
/* Define to 1 if you have the <sys/bitypes.h> header file. */
@@ -1338,28 +1338,28 @@
/* Define to 1 if you have the <sys/wait.h> header file. */
#undef HAVE_SYS_WAIT_H
/* Define to 1 if you have the 'tcdrain' function. */
/* Define to 1 if you have the `tcdrain' function. */
#undef HAVE_TCDRAIN
/* Define to 1 if you have the <termios.h> header file. */
#undef HAVE_TERMIOS_H
/* Define to 1 if you have the 'thrd_create' function. */
/* Define to 1 if you have the `thrd_create' function. */
#undef HAVE_THRD_CREATE
/* Define to 1 if you have the <threads.h> header file. */
#undef HAVE_THREADS_H
/* Define to 1 if you have the 'towlower' function. */
/* Define to 1 if you have the `towlower' function. */
#undef HAVE_TOWLOWER
/* Define to 1 if you have the 'tsearch' function. */
/* Define to 1 if you have the `tsearch' function. */
#undef HAVE_TSEARCH
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the 'unsetenv' function. */
/* Define to 1 if you have the `unsetenv' function. */
#undef HAVE_UNSETENV
/* Define to 1 if the system has the type 'unsigned long long int'. */
@@ -1368,23 +1368,23 @@
/* Define if you have a global __progname variable */
#undef HAVE_VAR___PROGNAME
/* Define to 1 if you have the 'vasnprintf' function. */
/* Define to 1 if you have the `vasnprintf' function. */
#undef HAVE_VASNPRINTF
/* Define to 1 if you have the 'vasprintf' function. */
/* Define to 1 if you have the `vasprintf' function. */
#undef HAVE_VASPRINTF
/* Define to 1 if you have the 'vfork' function. */
/* Define to 1 if you have the `vfork' function. */
#undef HAVE_VFORK
/* Define to 1 or 0, depending whether the compiler supports simple visibility
declarations. */
#undef HAVE_VISIBILITY
/* Define to 1 if you have the 'vsnprintf' function. */
/* Define to 1 if you have the `vsnprintf' function. */
#undef HAVE_VSNPRINTF
/* Define to 1 if you have the 'waitid' function. */
/* Define to 1 if you have the `waitid' function. */
#undef HAVE_WAITID
/* Define to 1 if you have the <wchar.h> header file. */
@@ -1393,19 +1393,19 @@
/* Define if you have the 'wchar_t' type. */
#undef HAVE_WCHAR_T
/* Define to 1 if you have the 'wcrtomb' function. */
/* Define to 1 if you have the `wcrtomb' function. */
#undef HAVE_WCRTOMB
/* Define to 1 if you have the 'wcslen' function. */
/* Define to 1 if you have the `wcslen' function. */
#undef HAVE_WCSLEN
/* Define to 1 if you have the 'wcsnlen' function. */
/* Define to 1 if you have the `wcsnlen' function. */
#undef HAVE_WCSNLEN
/* Define to 1 if you have the <wctype.h> header file. */
#undef HAVE_WCTYPE_H
/* Define to 1 if you have the 'wcwidth' function. */
/* Define to 1 if you have the `wcwidth' function. */
#undef HAVE_WCWIDTH
/* Define to 1 if the compiler and linker support weak declarations of
@@ -1431,13 +1431,13 @@
/* Define to 1 if you have the <xlocale.h> header file. */
#undef HAVE_XLOCALE_H
/* Define to 1 if the system has the type '_Bool'. */
/* Define to 1 if the system has the type `_Bool'. */
#undef HAVE__BOOL
/* Define to 1 if you have the '_NSGetExecutablePath' function. */
/* Define to 1 if you have the `_NSGetExecutablePath' function. */
#undef HAVE__NSGETEXECUTABLEPATH
/* Define to 1 if you have the '_set_invalid_parameter_handler' function. */
/* Define to 1 if you have the `_set_invalid_parameter_handler' function. */
#undef HAVE__SET_INVALID_PARAMETER_HANDLER
/* Define to 1 if the compiler supports __builtin_expect,
@@ -1450,13 +1450,13 @@
#endif
/* Define to 1 if you have the '__fseterr' function. */
/* Define to 1 if you have the `__fseterr' function. */
#undef HAVE___FSETERR
/* Define to 1 if the compiler supports the keyword '__inline'. */
#undef HAVE___INLINE
/* Define to 1 if you have the '__xpg_strerror_r' function. */
/* Define to 1 if you have the `__xpg_strerror_r' function. */
#undef HAVE___XPG_STRERROR_R
/* Define as const if the declaration of iconv() needs const. */
@@ -1720,10 +1720,10 @@
STACK_DIRECTION = 0 => direction of growth unknown */
#undef STACK_DIRECTION
/* Define to 1 if the 'S_IS*' macros in <sys/stat.h> do not work properly. */
/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
#undef STAT_MACROS_BROKEN
/* Define to 1 if all of the C89 standard headers exist (not just the ones
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#undef STDC_HEADERS
@@ -1760,14 +1760,10 @@
weak. */
#undef USE_POSIX_THREADS_WEAK
/* Enable extensions on AIX, Interix, z/OS. */
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Enable extensions on Cosmopolitan Libc. */
#ifndef _COSMO_SOURCE
# undef _COSMO_SOURCE
#endif
/* Enable general extensions on macOS. */
#ifndef _DARWIN_C_SOURCE
# undef _DARWIN_C_SOURCE
@@ -1825,15 +1821,11 @@
#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
# undef __STDC_WANT_IEC_60559_DFP_EXT__
#endif
/* Enable extensions specified by C23 Annex F. */
#ifndef __STDC_WANT_IEC_60559_EXT__
# undef __STDC_WANT_IEC_60559_EXT__
#endif
/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
#endif
/* Enable extensions specified by C23 Annex H and ISO/IEC TS 18661-3:2015. */
/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
# undef __STDC_WANT_IEC_60559_TYPES_EXT__
#endif
@@ -2463,10 +2455,10 @@
# define _GL_INLINE_HEADER_END
#endif
/* Define as 'int' if <sys/types.h> doesn't define. */
/* Define to `int' if <sys/types.h> doesn't define. */
#undef gid_t
/* Define to '__inline__' or '__inline' if that's what the C compiler
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
#undef inline
@@ -2504,7 +2496,7 @@
#define _GL_CMP(n1, n2) (((n1) > (n2)) - ((n1) < (n2)))
/* Define to 'int' if <sys/types.h> does not define. */
/* Define to `int' if <sys/types.h> does not define. */
#undef mode_t
/* Define to the type of st_nlink in struct stat, or a supertype. */
@@ -2536,13 +2528,13 @@
accessed atomically even in the presence of asynchronous signals. */
#undef sig_atomic_t
/* Define as 'unsigned int' if <stddef.h> doesn't define. */
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
/* Define as a signed type of the same size as size_t. */
#undef ssize_t
/* Define as 'int' if <sys/types.h> doesn't define. */
/* Define to `int' if <sys/types.h> doesn't define. */
#undef uid_t
+235 -240
View File
@@ -1,12 +1,12 @@
This is flex.info, produced by makeinfo version 7.3 from flex.texi.
This is flex.info, produced by makeinfo version 6.1 from flex.texi.
The flex manual is placed under the same licensing conditions as the
rest of flex:
Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2012 The Flex
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2012 The Flex
Project.
Copyright © 1990, 1997 The Regents of the University of California.
Copyright (C) 1990, 1997 The Regents of the University of California.
All rights reserved.
This code is derived from software contributed to Berkeley by Vern
@@ -42,245 +42,240 @@ END-INFO-DIR-ENTRY

Indirect:
flex.info-1: 1620
flex.info-2: 324917
flex.info-1: 1622
flex.info-2: 318745

Tag Table:
(Indirect)
Node: Top1620
Node: Copyright7694
Node: Reporting Bugs9211
Node: Introduction9471
Node: Simple Examples10328
Node: Format13771
Node: Definitions Section14192
Ref: Definitions Section-Footnote-116550
Node: Rules Section16626
Node: User Code Section17800
Node: Comments in the Input18246
Node: Patterns19651
Ref: case and character ranges26973
Node: Matching31202
Node: Actions34639
Node: Generated Scanner43962
Node: Start Conditions49185
Node: Multiple Input Buffers60101
Ref: Scanning Strings66886
Node: EOF68579
Node: Misc Macros70215
Node: User Values73173
Node: Yacc75614
Node: Scanner Options76575
Node: Options for Specifying Filenames79407
Ref: option-header79633
Ref: option-outfile80379
Ref: option-stdout80744
Node: Options Affecting Scanner Behavior81763
Ref: option-case-insensitive82004
Ref: option-lex-compat82461
Ref: option-batch83033
Ref: option-interactive83588
Ref: option-7bit84990
Ref: option-8bit86358
Ref: option-default86798
Ref: option-always-interactive86870
Ref: option-posix87498
Ref: option-stack88711
Ref: option-stdinit88827
Ref: option-yylineno89350
Ref: option-yywrap89829
Node: Code-Level And API Options90120
Ref: option-ansi-definitions90347
Ref: option-ansi-prototypes90430
Ref: option-bison-bridge90511
Ref: option-bison-locations90876
Ref: option-noline91164
Ref: option-reentrant91714
Ref: option-c++92346
Ref: option-array92480
Ref: option-pointer92586
Ref: option-prefix92733
Ref: option-main94325
Ref: option-nounistd94529
Ref: option-yyclass95068
Node: Options for Scanner Speed and Size95596
Ref: option-align96158
Ref: option-ecs96668
Ref: option-meta-ecs97751
Ref: option-read98259
Ref: option-full100222
Ref: option-fast100437
Node: Debugging Options101389
Ref: option-backup101576
Ref: option-debug102145
Ref: option-perf-report102887
Ref: option-nodefault103545
Ref: option-trace103875
Ref: option-nowarn104190
Ref: option-verbose104266
Ref: option-warn104723
Node: Miscellaneous Options104950
Node: Performance105434
Node: Cxx115909
Node: Reentrant124503
Node: Reentrant Uses125197
Node: Reentrant Overview126812
Node: Reentrant Example127650
Node: Reentrant Detail128458
Node: Specify Reentrant128895
Node: Extra Reentrant Argument129557
Node: Global Replacement130869
Node: Init and Destroy Functions132160
Node: Accessor Methods134796
Node: Extra Data136183
Node: About yyscan_t138510
Node: Reentrant Functions138919
Ref: bison-functions140420
Node: Lex and Posix141191
Node: Memory Management149030
Ref: memory-management149176
Node: The Default Memory Management149404
Ref: The Default Memory Management-Footnote-1153240
Node: Overriding The Default Memory Management153393
Ref: Overriding The Default Memory Management-Footnote-1155904
Node: A Note About yytext And Memory156080
Node: Serialized Tables157324
Ref: serialization157468
Node: Creating Serialized Tables158238
Node: Loading and Unloading Serialized Tables159885
Node: Tables File Format161694
Node: Diagnostics169019
Node: Limitations172584
Node: Bibliography174608
Node: FAQ175290
Node: When was flex born?179534
Node: How do I expand backslash-escape sequences in C-style quoted strings?179915
Node: Why do flex scanners call fileno if it is not ANSI compatible?181230
Node: Does flex support recursive pattern definitions?182071
Node: How do I skip huge chunks of input (tens of megabytes) while using flex?182922
Node: Flex is not matching my patterns in the same order that I defined them.183401
Node: My actions are executing out of order or sometimes not at all.185187
Node: How can I have multiple input sources feed into the same scanner at the same time?185982
Node: Can I build nested parsers that work with the same input file?188033
Node: How can I match text only at the end of a file?189060
Node: How can I make REJECT cascade across start condition boundaries?189876
Node: Why cant I use fast or full tables with interactive mode?190902
Node: How much faster is -F or -f than -C?192159
Node: If I have a simple grammar cant I just parse it with flex?192471
Node: Why doesn't yyrestart() set the start state back to INITIAL?192953
Node: How can I match C-style comments?193588
Node: The period isn't working the way I expected.194398
Node: Can I get the flex manual in another format?195735
Node: Does there exist a "faster" NDFA->DFA algorithm?196233
Node: How does flex compile the DFA so quickly?196743
Node: How can I use more than 8192 rules?197713
Node: How do I abandon a file in the middle of a scan and switch to a new file?199135
Node: How do I execute code only during initialization (only before the first scan)?199701
Node: How do I execute code at termination?200491
Node: Where else can I find help?200821
Node: Can I include comments in the "rules" section of the file?201195
Node: I get an error about undefined yywrap().201575
Node: How can I change the matching pattern at run time?202063
Node: How can I expand macros in the input?202425
Node: How can I build a two-pass scanner?203462
Node: How do I match any string not matched in the preceding rules?204380
Node: I am trying to port code from AT&T lex that uses yysptr and yysbuf.205301
Node: Is there a way to make flex treat NULL like a regular character?206120
Node: Whenever flex can not match the input it says "flex scanner jammed".206652
Node: Why doesn't flex have non-greedy operators like perl does?207304
Node: Memory leak - 16386 bytes allocated by malloc.208669
Ref: faq-memory-leak208967
Node: How do I track the byte offset for lseek()?209966
Node: How do I use my own I/O classes in a C++ scanner?211523
Node: How do I skip as many chars as possible?212386
Node: deleteme00213461
Node: Are certain equivalent patterns faster than others?213906
Node: Is backing up a big deal?217394
Node: Can I fake multi-byte character support?219365
Node: deleteme01220841
Node: Can you discuss some flex internals?221965
Node: unput() messes up yy_at_bol224254
Node: The | operator is not doing what I want225391
Node: Why can't flex understand this variable trailing context pattern?226982
Node: The ^ operator isn't working228246
Node: Trailing context is getting confused with trailing optional patterns229516
Node: Is flex GNU or not?230784
Node: ERASEME53232497
Node: I need to scan if-then-else blocks and while loops233292
Node: ERASEME55234511
Node: ERASEME56235624
Node: ERASEME57237017
Node: Is there a repository for flex scanners?238050
Node: How can I conditionally compile or preprocess my flex input file?238366
Node: Where can I find grammars for lex and yacc?238839
Node: I get an end-of-buffer message for each character scanned.239186
Node: unnamed-faq-62239781
Node: unnamed-faq-63240829
Node: unnamed-faq-64242141
Node: unnamed-faq-65243142
Node: unnamed-faq-66243943
Node: unnamed-faq-67245073
Node: unnamed-faq-68246075
Node: unnamed-faq-69247232
Node: unnamed-faq-70247965
Node: unnamed-faq-71248741
Node: unnamed-faq-72249970
Node: unnamed-faq-73251038
Node: unnamed-faq-74251982
Node: unnamed-faq-75252952
Node: unnamed-faq-76254124
Node: unnamed-faq-77254845
Node: unnamed-faq-78255753
Node: unnamed-faq-79256766
Node: unnamed-faq-80258501
Node: unnamed-faq-81259844
Node: unnamed-faq-82262684
Node: unnamed-faq-83263666
Node: unnamed-faq-84265471
Node: unnamed-faq-85266589
Node: unnamed-faq-86267636
Node: unnamed-faq-87268609
Node: unnamed-faq-88269270
Node: unnamed-faq-90270126
Node: unnamed-faq-91271424
Node: unnamed-faq-92273907
Node: unnamed-faq-93274421
Node: unnamed-faq-94275363
Node: unnamed-faq-95276805
Node: unnamed-faq-96278338
Node: unnamed-faq-97279122
Node: unnamed-faq-98279804
Node: unnamed-faq-99280494
Node: unnamed-faq-100281453
Node: unnamed-faq-101282178
Node: What is the difference between YYLEX_PARAM and YY_DECL?283011
Node: Why do I get "conflicting types for yylex" error?283535
Node: How do I access the values set in a Flex action from within a Bison action?284065
Node: Appendices284494
Node: Makefiles and Flex284703
Ref: Makefiles and Flex-Footnote-1288049
Ref: Makefiles and Flex-Footnote-2288174
Ref: Makefiles and Flex-Footnote-3288365
Node: Bison Bridge288416
Ref: Bison Bridge-Footnote-1291217
Node: M4 Dependency291409
Ref: M4 Dependency-Footnote-1292903
Node: Common Patterns293039
Node: Numbers293330
Node: Identifiers294323
Node: Quoted Constructs295154
Node: Addresses296228
Node: Indices297548
Node: Concept Index297786
Node: Index of Functions and Macros324917
Node: Index of Variables329886
Node: Index of Data Types331552
Node: Index of Hooks332440
Node: Index of Scanner Options333008
Node: Top1622
Node: Copyright9414
Node: Reporting Bugs10933
Node: Introduction11189
Node: Simple Examples12018
Node: Format15304
Node: Definitions Section15759
Ref: Definitions Section-Footnote-118017
Node: Rules Section18085
Node: User Code Section19243
Node: Comments in the Input19681
Node: Patterns21051
Ref: case and character ranges27883
Node: Matching31886
Node: Actions35171
Node: Generated Scanner44133
Node: Start Conditions49136
Node: Multiple Input Buffers59678
Ref: Scanning Strings66221
Node: EOF67850
Node: Misc Macros69436
Node: User Values72290
Node: Yacc74615
Node: Scanner Options75510
Node: Options for Specifying Filenames78299
Ref: option-header78525
Ref: option-outfile79239
Ref: option-stdout79564
Node: Options Affecting Scanner Behavior80547
Ref: option-case-insensitive80788
Ref: option-lex-compat81221
Ref: option-batch81753
Ref: option-interactive82272
Ref: option-7bit83626
Ref: option-8bit84930
Ref: option-default85342
Ref: option-always-interactive85406
Ref: option-posix86010
Ref: option-stack87157
Ref: option-stdinit87265
Ref: option-yylineno87744
Ref: option-yywrap88187
Node: Code-Level And API Options88454
Ref: option-ansi-definitions88681
Ref: option-ansi-prototypes88756
Ref: option-bison-bridge88829
Ref: option-bison-locations89170
Ref: option-noline89430
Ref: option-reentrant89944
Ref: option-c++90556
Ref: option-array90682
Ref: option-pointer90780
Ref: option-prefix90907
Ref: option-main92435
Ref: option-nounistd92619
Ref: option-yyclass93130
Node: Options for Scanner Speed and Size93614
Ref: option-align94164
Ref: option-ecs94666
Ref: option-meta-ecs95705
Ref: option-read96193
Ref: option-full98076
Ref: option-fast98271
Node: Debugging Options99195
Ref: option-backup99382
Ref: option-debug99927
Ref: option-perf-report100649
Ref: option-nodefault101275
Ref: option-trace101593
Ref: option-nowarn101884
Ref: option-verbose101952
Ref: option-warn102381
Node: Miscellaneous Options102600
Node: Performance103056
Node: Cxx113303
Node: Reentrant121395
Node: Reentrant Uses122129
Node: Reentrant Overview123691
Node: Reentrant Example124491
Node: Reentrant Detail125264
Node: Specify Reentrant125768
Node: Extra Reentrant Argument126418
Node: Global Replacement127670
Node: Init and Destroy Functions128905
Node: Accessor Methods131426
Node: Extra Data132773
Node: About yyscan_t135040
Node: Reentrant Functions135437
Ref: bison-functions136921
Node: Lex and Posix137660
Node: Memory Management145007
Ref: memory-management145153
Node: The Default Memory Management145387
Ref: The Default Memory Management-Footnote-1149207
Node: Overriding The Default Memory Management149360
Ref: Overriding The Default Memory Management-Footnote-1151774
Node: A Note About yytext And Memory151938
Node: Serialized Tables153178
Ref: serialization153322
Node: Creating Serialized Tables154102
Node: Loading and Unloading Serialized Tables155717
Node: Tables File Format157490
Node: Diagnostics164515
Node: Limitations167924
Node: Bibliography169872
Node: FAQ170542
Node: When was flex born?175705
Node: How do I expand backslash-escape sequences in C-style quoted strings?176082
Node: Why do flex scanners call fileno if it is not ANSI compatible?177385
Node: Does flex support recursive pattern definitions?178182
Node: How do I skip huge chunks of input (tens of megabytes) while using flex?179029
Node: Flex is not matching my patterns in the same order that I defined them.179496
Node: My actions are executing out of order or sometimes not at all.181242
Node: How can I have multiple input sources feed into the same scanner at the same time?182015
Node: Can I build nested parsers that work with the same input file?184000
Node: How can I match text only at the end of a file?185007
Node: How can I make REJECT cascade across start condition boundaries?185811
Node: Why cant I use fast or full tables with interactive mode?186825
Node: How much faster is -F or -f than -C?188082
Node: If I have a simple grammar cant I just parse it with flex?188394
Node: Why doesn't yyrestart() set the start state back to INITIAL?188876
Node: How can I match C-style comments?189503
Node: The period isn't working the way I expected.190313
Node: Can I get the flex manual in another format?191558
Node: Does there exist a "faster" NDFA->DFA algorithm?192048
Node: How does flex compile the DFA so quickly?192558
Node: How can I use more than 8192 rules?193524
Node: How do I abandon a file in the middle of a scan and switch to a new file?194934
Node: How do I execute code only during initialization (only before the first scan)?195488
Node: How do I execute code at termination?196265
Node: Where else can I find help?196591
Node: Can I include comments in the "rules" section of the file?196965
Node: I get an error about undefined yywrap().197345
Node: How can I change the matching pattern at run time?197821
Node: How can I expand macros in the input?198183
Node: How can I build a two-pass scanner?199215
Node: How do I match any string not matched in the preceding rules?200133
Node: I am trying to port code from AT&T lex that uses yysptr and yysbuf.201042
Node: Is there a way to make flex treat NULL like a regular character?201837
Node: Whenever flex can not match the input it says "flex scanner jammed".202357
Node: Why doesn't flex have non-greedy operators like perl does?203000
Node: Memory leak - 16386 bytes allocated by malloc.204353
Ref: faq-memory-leak204651
Node: How do I track the byte offset for lseek()?205622
Node: How do I use my own I/O classes in a C++ scanner?207131
Node: How do I skip as many chars as possible?207974
Node: deleteme00209049
Node: Are certain equivalent patterns faster than others?209489
Node: Is backing up a big deal?212907
Node: Can I fake multi-byte character support?214813
Node: deleteme01216254
Node: Can you discuss some flex internals?217363
Node: unput() messes up yy_at_bol219607
Node: The | operator is not doing what I want220709
Node: Why can't flex understand this variable trailing context pattern?222255
Node: The ^ operator isn't working223504
Node: Trailing context is getting confused with trailing optional patterns224739
Node: Is flex GNU or not?225982
Node: ERASEME53227655
Node: I need to scan if-then-else blocks and while loops228425
Node: ERASEME55229624
Node: ERASEME56230722
Node: ERASEME57232080
Node: Is there a repository for flex scanners?233078
Node: How can I conditionally compile or preprocess my flex input file?233394
Node: Where can I find grammars for lex and yacc?233867
Node: I get an end-of-buffer message for each character scanned.234214
Node: unnamed-faq-62234809
Node: unnamed-faq-63235827
Node: unnamed-faq-64237124
Node: unnamed-faq-65238090
Node: unnamed-faq-66238876
Node: unnamed-faq-67239991
Node: unnamed-faq-68240978
Node: unnamed-faq-69242120
Node: unnamed-faq-70242833
Node: unnamed-faq-71243594
Node: unnamed-faq-72244803
Node: unnamed-faq-73245846
Node: unnamed-faq-74246770
Node: unnamed-faq-75247715
Node: unnamed-faq-76248847
Node: unnamed-faq-77249553
Node: unnamed-faq-78250446
Node: unnamed-faq-79251444
Node: unnamed-faq-80253144
Node: unnamed-faq-81254462
Node: unnamed-faq-82257262
Node: unnamed-faq-83258219
Node: unnamed-faq-84259999
Node: unnamed-faq-85261102
Node: unnamed-faq-86262109
Node: unnamed-faq-87263047
Node: unnamed-faq-88263693
Node: unnamed-faq-90264524
Node: unnamed-faq-91265787
Node: unnamed-faq-92268215
Node: unnamed-faq-93268714
Node: unnamed-faq-94269641
Node: unnamed-faq-95271053
Node: unnamed-faq-96272571
Node: unnamed-faq-97273330
Node: unnamed-faq-98273997
Node: unnamed-faq-99274662
Node: unnamed-faq-100275591
Node: unnamed-faq-101276301
Node: What is the difference between YYLEX_PARAM and YY_DECL?277114
Node: Why do I get "conflicting types for yylex" error?277638
Node: How do I access the values set in a Flex action from within a Bison action?278168
Node: Appendices278597
Node: Makefiles and Flex278862
Ref: Makefiles and Flex-Footnote-1282064
Ref: Makefiles and Flex-Footnote-2282181
Ref: Makefiles and Flex-Footnote-3282368
Node: Bison Bridge282419
Ref: Bison Bridge-Footnote-1285086
Node: M4 Dependency285278
Ref: M4 Dependency-Footnote-1286692
Node: Common Patterns286828
Node: Numbers287151
Node: Identifiers288127
Node: Quoted Constructs288954
Node: Addresses290008
Node: Indices291320
Node: Concept Index291612
Node: Index of Functions and Macros318745
Node: Index of Variables323714
Node: Index of Data Types325380
Node: Index of Hooks326268
Node: Index of Scanner Options326836

End Tag Table

Local Variables:
coding: utf-8
End:
File diff suppressed because it is too large Load Diff
Binary file not shown.
+2 -2
View File
@@ -1,4 +1,4 @@
@set UPDATED 15 May 2026
@set UPDATED-MONTH May 2026
@set UPDATED 6 May 2017
@set UPDATED-MONTH May 2017
@set EDITION 2.6.4
@set VERSION 2.6.4
@@ -1,4 +1,4 @@
@set UPDATED 15 May 2026
@set UPDATED-MONTH May 2026
@set UPDATED 6 May 2017
@set UPDATED-MONTH May 2017
@set EDITION 2.6.4
@set VERSION 2.6.4
+85 -33
View File
@@ -1,18 +1,30 @@
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if using 'alloca.c'. */
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
systems. This function is required for `alloca.c' support on those systems.
*/
#undef CRAY_STACKSEG_END
/* Define to 1 if using `alloca.c'. */
#undef C_ALLOCA
/* Define to 1 if translation of program messages to the user's native
language is requested. */
#undef ENABLE_NLS
/* Define to 1 if you have 'alloca', as a function or macro. */
/* Define to 1 if you have `alloca', as a function or macro. */
#undef HAVE_ALLOCA
/* Define to 1 if <alloca.h> works. */
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
*/
#undef HAVE_ALLOCA_H
/* Define to 1 if you have the `available.' function. */
#undef HAVE_AVAILABLE_
/* Define to 1 if you have the `by' function. */
#undef HAVE_BY
/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
CoreFoundation framework. */
#undef HAVE_CFLOCALECOPYCURRENT
@@ -28,25 +40,43 @@
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the 'dup2' function. */
/* Define to 1 if you have the `dnl' function. */
#undef HAVE_DNL
/* Define to 1 if you have the `dup2' function. */
#undef HAVE_DUP2
/* Define to 1 if you have the 'fork' function. */
/* Define to 1 if you have the `enabled' function. */
#undef HAVE_ENABLED
/* Define to 1 if you have the `fork' function. */
#undef HAVE_FORK
/* Define to 1 if you have the `function.' function. */
#undef HAVE_FUNCTION_
/* Define if the GNU gettext() function is already present or preinstalled. */
#undef HAVE_GETTEXT
/* Define to 1 if you have the `have' function. */
#undef HAVE_HAVE
/* Define if you have the iconv() function and it works. */
#undef HAVE_ICONV
/* Define to 1 if you have the `if' function. */
#undef HAVE_IF
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `is' function. */
#undef HAVE_IS
/* Define to 1 if you have the <libintl.h> header file. */
#undef HAVE_LIBINTL_H
/* Define to 1 if you have the 'm' library (-lm). */
/* Define to 1 if you have the `m' library (-lm). */
#undef HAVE_LIBM
/* Define to 1 if you have the <limits.h> header file. */
@@ -55,39 +85,60 @@
/* Define to 1 if you have the <locale.h> header file. */
#undef HAVE_LOCALE_H
/* Define to 1 if your system has a GNU libc compatible 'malloc' function, and
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
to 0 otherwise. */
#undef HAVE_MALLOC
/* Define to 1 if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define to 1 if you have the 'memset' function. */
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `memset' function. */
#undef HAVE_MEMSET
/* Define to 1 if you have the `Needed' function. */
#undef HAVE_NEEDED
/* Define to 1 if you have the <netinet/in.h> header file. */
#undef HAVE_NETINET_IN_H
/* Define to 1 if you have the 'pow' function. */
/* Define to 1 if you have the `NLS' function. */
#undef HAVE_NLS
/* Define to 1 if you have the `not' function. */
#undef HAVE_NOT
/* Define to 1 if you have the `only' function. */
#undef HAVE_ONLY
/* Define to 1 if you have the `OpenBSD' function. */
#undef HAVE_OPENBSD
/* Define to 1 if you have the `pow' function. */
#undef HAVE_POW
/* Define to 1 if you have the <pthread.h> header file. */
#undef HAVE_PTHREAD_H
/* Define to 1 if your system has a GNU libc compatible 'realloc' function,
/* Define to 1 if your system has a GNU libc compatible `realloc' function,
and to 0 otherwise. */
#undef HAVE_REALLOC
/* Define to 1 if you have the 'reallocarray' function. */
/* Define to 1 if you have the `reallocarray' function. */
#undef HAVE_REALLOCARRAY
/* Define to 1 if you have the 'regcomp' function. */
/* Define to 1 if you have the `regcomp' function. */
#undef HAVE_REGCOMP
/* Define to 1 if you have the <regex.h> header file. */
#undef HAVE_REGEX_H
/* Define to 1 if you have the 'setlocale' function. */
/* Define to 1 if you have the `replacement' function. */
#undef HAVE_REPLACEMENT
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
/* Define to 1 if stdbool.h conforms to C99. */
@@ -96,19 +147,16 @@
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdio.h> header file. */
#undef HAVE_STDIO_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the 'strcasecmp' function. */
/* Define to 1 if you have the `strcasecmp' function. */
#undef HAVE_STRCASECMP
/* Define to 1 if you have the 'strchr' function. */
/* Define to 1 if you have the `strchr' function. */
#undef HAVE_STRCHR
/* Define to 1 if you have the 'strdup' function. */
/* Define to 1 if you have the `strdup' function. */
#undef HAVE_STRDUP
/* Define to 1 if you have the <strings.h> header file. */
@@ -117,7 +165,7 @@
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the 'strtol' function. */
/* Define to 1 if you have the `strtol' function. */
#undef HAVE_STRTOL
/* Define to 1 if you have the <sys/stat.h> header file. */
@@ -132,19 +180,25 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the 'vfork' function. */
/* Define to 1 if you have the `Used' function. */
#undef HAVE_USED
/* Define to 1 if you have the `vfork' function. */
#undef HAVE_VFORK
/* Define to 1 if you have the <vfork.h> header file. */
#undef HAVE_VFORK_H
/* Define to 1 if 'fork' works. */
/* Define to 1 if you have the `We' function. */
#undef HAVE_WE
/* Define to 1 if `fork' works. */
#undef HAVE_WORKING_FORK
/* Define to 1 if 'vfork' works. */
/* Define to 1 if `vfork' works. */
#undef HAVE_WORKING_VFORK
/* Define to 1 if the system has the type '_Bool'. */
/* Define to 1 if the system has the type `_Bool'. */
#undef HAVE__BOOL
/* Define to the sub-directory where libtool stores uninstalled libraries. */
@@ -182,32 +236,30 @@
STACK_DIRECTION = 0 => direction of growth unknown */
#undef STACK_DIRECTION
/* Define to 1 if all of the C89 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION
/* Define to 1 if 'lex' declares 'yytext' as a 'char *' by default, not a
'char[]'. */
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
`char[]'. */
#undef YYTEXT_POINTER
/* Define to empty if 'const' does not conform to ANSI C. */
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to rpl_malloc if the replacement function should be used. */
#undef malloc
/* Define as a signed integer type capable of holding a process identifier. */
/* Define to `int' if <sys/types.h> does not define. */
#undef pid_t
/* Define to rpl_realloc if the replacement function should be used. */
#undef realloc
/* Define as 'unsigned int' if <stddef.h> doesn't define. */
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
/* Define as 'fork' if 'vfork' does not work. */
/* Define as `fork' if `vfork' does not work. */
#undef vfork
+7 -112
View File
@@ -6,121 +6,16 @@ patches = ["redox.patch"]
template = "custom"
script = """
DYNAMIC_INIT
# Add relibc system headers to the include path.
# The cookbook sets CPPFLAGS="-I${COOKBOOK_SYSROOT}/include" but the recipe
# sysroot is empty for packages with no header-providing deps. The relibc
# headers are at prefix/${TARGET}/relibc-install/${TARGET}/include/ — not in
# the compiler's default search path. Without this, gnulib's #include_next
# can't find the system headers and every wrapper fails.
RELIBC_INCLUDE="${COOKBOOK_ROOT}/prefix/${TARGET}/relibc-install/${TARGET}/include"
export CPPFLAGS="${CPPFLAGS} -isystem ${RELIBC_INCLUDE}"
# relibc's float.h is missing LDBL_DIG (and possibly other LDBL_* macros).
# For x86_64 80-bit extended precision: LDBL_DIG = floor(63 * log10(2)) = 18
export CPPFLAGS="${CPPFLAGS} -DLDBL_DIG=18"
# The redoxer toolchain has a stale libc without __fseterr/__freadahead.
# Add the relibc library path so the linker finds the updated library.
RELIBC_LIB="${COOKBOOK_ROOT}/prefix/${TARGET}/relibc-install/${TARGET}/lib"
export LDFLAGS="${LDFLAGS} -L${RELIBC_LIB}"
# Gnulib cross-compilation: relibc provides standard POSIX headers and types
# but gnulib's configure can't run test programs during cross-compilation.
export ac_cv_header_stdio_h=yes
export ac_cv_header_stdlib_h=yes
export ac_cv_header_string_h=yes
export ac_cv_header_strings_h=yes
export ac_cv_header_inttypes_h=yes
export ac_cv_header_stdint_h=yes
export ac_cv_header_unistd_h=yes
export ac_cv_header_sys_types_h=yes
export ac_cv_header_sys_stat_h=yes
export ac_cv_header_time_h=yes
export ac_cv_header_sys_time_h=yes
export ac_cv_header_sys_select_h=yes
export ac_cv_header_wchar_h=yes
export ac_cv_header_wctype_h=yes
export ac_cv_header_signal_h=yes
export ac_cv_header_dirent_h=yes
export ac_cv_header_fcntl_h=yes
export ac_cv_header_locale_h=yes
export ac_cv_header_errno_h=yes
export ac_cv_header_ctype_h=yes
export ac_cv_header_limits_h=yes
export ac_cv_header_stdarg_h=yes
export ac_cv_header_stddef_h=yes
export ac_cv_header_spawn_h=yes
# Standard types
export ac_cv_type_intmax_t=yes
export ac_cv_type_uintmax_t=yes
export ac_cv_type_gid_t=yes
export ac_cv_type_uid_t=yes
export ac_cv_type_pid_t=yes
export ac_cv_type_mode_t=yes
export ac_cv_type_off_t=yes
export ac_cv_type_size_t=yes
export ac_cv_type_ssize_t=yes
export ac_cv_type_ptrdiff_t=yes
export ac_cv_type_nlink_t=yes
export ac_cv_type_mbstate_t=yes
export ac_cv_type_sigset_t=yes
export ac_cv_type_posix_spawnattr_t=yes
export ac_cv_type_posix_spawn_file_actions_t=yes
export gl_cv_type_intmax_t=yes
export gl_cv_type_ptrdiff_t_signed=yes
export gl_cv_header_inttypes_h=yes
export gl_cv_header_stdint_h=yes
export gl_cv_header_inttypes_h_with_uintmax=yes
export ac_cv_have_inttypes_h_with_uintmax=yes
# m4-specific gnulib function checks
export ac_cv_func___freadahead=yes
export ac_cv_have_decl___freadahead=yes
export gl_cv_header_wchar_h_correct_inline=yes
export gl_cv_func_btowc_nul=yes
export gl_cv_func_btowc_consistent=yes
export gl_cv_onwards_func___freadahead=yes
export gl_cv_socklen_t_equiv=socklen_t
export ac_cv_func_getpagesize=yes
export ac_cv_func_memcmp_working=yes
# Tell gnulib these wide-char functions exist and work
export ac_cv_func_btowc=yes
export ac_cv_func_mbrtowc=yes
export ac_cv_func_mbsinit=yes
export ac_cv_func_wcrtomb=yes
export ac_cv_func_wctob=yes
export ac_cv_func_mbsrtowcs=yes
export ac_cv_func_wcswidth=yes
export ac_cv_func_wcwidth=yes
export gl_cv_func_btowc=yes
export gl_cv_func_mbrtowc=yes
export gl_cv_func_mbsinit=yes
export gl_cv_func_wcrtomb=yes
export gl_cv_func_wctob=yes
export gl_cv_func_wcwidth=yes
export gl_cv_func_wcswidth=yes
# Functions that relibc provides but gnulib can't detect during cross-compilation
export ac_cv_func___fseterr=yes
export ac_cv_func_getlocalename_l=yes
COOKBOOK_CONFIGURE_FLAGS+=(
--disable-nls
ac_cv_func___freadahead=yes
ac_cv_have_decl___freadahead=yes
gl_cv_header_wchar_h_correct_inline=yes
gl_cv_func_btowc_nul=yes
gl_cv_func_btowc_consistent=yes
gl_cv_onwards_func___freadahead=yes
)
"${COOKBOOK_CONFIGURE}" "${COOKBOOK_CONFIGURE_FLAGS[@]}"
# Fix gnulib cross-compilation misdetections in config.h
"${COOKBOOK_ROOT}/local/scripts/gnulib-cross-fix.sh" "${COOKBOOK_BUILD}/lib/config.h"
# Prevent man page regeneration (help2man not available in cross-env)
touch "${COOKBOOK_SOURCE}/doc/m4.1"
"${COOKBOOK_MAKE}" -j "${COOKBOOK_MAKE_JOBS}" HELP2MAN=true
"${COOKBOOK_MAKE}" install DESTDIR="${COOKBOOK_STAGE}" HELP2MAN=true
cookbook_configure
"""
[package]
+1 -1
View File
@@ -4,7 +4,7 @@
};
const char *name = ((struct __locale_t *) locale)->mb_cur_max == 4 ? "C.UTF-8" : "C";
return (struct string_with_storage) { name, STORAGE_INDEFINITE };
+#elif defined __RELIBC__ && HAVE_GETLOCALENAME_L
+#elif defined __redox__ && HAVE_GETLOCALENAME_L
+ const char *name = getlocalename_l (category, locale);
+ return (struct string_with_storage) { name != NULL ? name : "", STORAGE_OBJECT };
#else
+2 -2
View File
@@ -14,8 +14,8 @@
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.73],,
[m4_warning([this file was generated for autoconf 2.73.
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.72.90],,
[m4_warning([this file was generated for autoconf 2.72.90.
You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
+29 -29
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.73 for GNU M4 1.4.21.
# Generated by GNU Autoconf 2.72.90 for GNU M4 1.4.21.
#
# Report bugs to <bug-m4@gnu.org>.
#
@@ -634,7 +634,7 @@ gl_getopt_required=POSIX
gl_trunc_required=plain
gl_truncl_required=plain
gt_needs=
: ${enable_year2038:=no}
enable_year2038=no
ac_subst_vars='M4tests_libm4_LIBOBJDEPS
M4tests_libm4_LTLIBOBJS
M4tests_libm4_LIBOBJS
@@ -3969,7 +3969,7 @@ test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
GNU M4 configure 1.4.21
generated by GNU Autoconf 2.73
generated by GNU Autoconf 2.72.90
Copyright (C) 2026 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
@@ -4692,7 +4692,7 @@ This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by GNU M4 $as_me 1.4.21, which was
generated by GNU Autoconf 2.73. Invocation command line was
generated by GNU Autoconf 2.72.90. Invocation command line was
$ $0$ac_configure_args_raw
@@ -61929,14 +61929,14 @@ int
main (void)
{
#if defined _AIX && !defined _AIX51
#error "AIX pre 5.1 is buggy"
#endif
#ifdef __ANDROID__
#include <android/api-level.h>
#if __ANDROID_API__ < 22
#error "Android API < 22 is buggy"
#endif
#endif
#error "AIX pre 5.1 is buggy"
#endif
#ifdef __ANDROID__
#include <android/api-level.h>
#if __ANDROID_API__ < 22
#error "Android API < 22 is buggy"
#endif
#endif
;
return 0;
@@ -61955,27 +61955,27 @@ else case e in #(
/* end confdefs.h. */
$ac_includes_default
/* Use pstrnlen to test; 'volatile' prevents the compiler
from optimizing the strnlen calls away. */
size_t (*volatile pstrnlen) (char const *, size_t) = strnlen;
char const s[] = "foobar";
int s_len = sizeof s - 1;
from optimizing the strnlen calls away. */
size_t (*volatile pstrnlen) (char const *, size_t) = strnlen;
char const s[] = "foobar";
int s_len = sizeof s - 1;
int
main (void)
{
/* AIX 4.3 is buggy: strnlen (S, 1) == 3. */
int i;
for (i = 0; i < s_len + 1; ++i)
{
int expected = i <= s_len ? i : s_len;
if (pstrnlen (s, i) != expected)
return 1;
}
/* AIX 4.3 is buggy: strnlen (S, 1) == 3. */
int i;
for (i = 0; i < s_len + 1; ++i)
{
int expected = i <= s_len ? i : s_len;
if (pstrnlen (s, i) != expected)
return 1;
}
/* Android 5.0 (API 21) strnlen ("", SIZE_MAX) incorrectly crashes. */
if (pstrnlen ("", -1) != 0)
return 1;
/* Android 5.0 (API 21) strnlen ("", SIZE_MAX) incorrectly crashes. */
if (pstrnlen ("", -1) != 0)
return 1;
;
return 0;
}
@@ -75126,7 +75126,7 @@ cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
# values after options handling.
ac_log="
This file was extended by GNU M4 $as_me 1.4.21, which was
generated by GNU Autoconf 2.73. Invocation command line was
generated by GNU Autoconf 2.72.90. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -75200,7 +75200,7 @@ cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
GNU M4 config.status 1.4.21
configured by $0, generated by GNU Autoconf 2.73,
configured by $0, generated by GNU Autoconf 2.72.90,
with options \\"\$ac_cs_config\\"
Copyright (C) 2026 Free Software Foundation, Inc.
+110 -110
View File
@@ -1,6 +1,6 @@
This is m4.info, produced by makeinfo version 7.3 from m4.texi.
This is m4.info, produced by makeinfo version 7.2 from m4.texi.
This manual (15 May 2026) is for GNU M4 (version 1.4.21), a package
This manual (6 February 2026) is for GNU M4 (version 1.4.21), a package
containing an implementation of the m4 macro language.
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
@@ -19,117 +19,117 @@ END-INFO-DIR-ENTRY

Indirect:
m4.info-1: 828
m4.info-2: 317295
m4.info-1: 832
m4.info-2: 317307

Tag Table:
(Indirect)
Node: Top828
Node: Preliminaries9770
Node: Intro10476
Node: History12167
Node: Bugs16220
Node: Manual17483
Node: Invoking m420986
Node: Operation modes23190
Node: Preprocessor features26289
Node: Limits control29459
Node: Frozen state33470
Node: Debugging options34309
Node: Command line files36361
Node: Syntax38012
Node: Names39167
Node: Quoted strings39649
Node: Comments40316
Node: Other tokens41219
Node: Input processing41813
Ref: Input processing-Footnote-150250
Node: Macros50447
Node: Invocation50941
Node: Inhibiting Invocation51742
Node: Macro Arguments55984
Node: Quoting Arguments59104
Node: Macro expansion61240
Node: Definitions61958
Node: Define62743
Node: Arguments65261
Node: Pseudo Arguments69027
Node: Undefine72656
Node: Defn73815
Node: Pushdef78878
Node: Indir81618
Node: Builtin83785
Node: Conditionals86060
Node: Ifdef87006
Node: Ifelse87888
Node: Shift91274
Node: Forloop102094
Node: Foreach104775
Node: Stacks110397
Node: Composition113532
Node: Debugging121209
Node: Dumpdef121802
Node: Trace123220
Node: Debug Levels126872
Node: Debug Output131742
Node: Input Control133055
Node: Dnl133596
Node: Changequote135538
Node: Changecom145270
Node: Changeword149156
Node: M4wrap154761
Node: File Inclusion158846
Node: Include159167
Node: Search Path161984
Node: Diversions162933
Node: Divert164640
Node: Undivert167206
Node: Divnum170591
Node: Cleardivert171064
Node: Text handling172285
Node: Len173012
Node: Index macro173406
Node: Regexp174299
Node: Substr177460
Node: Translit178518
Node: Patsubst181309
Node: Format185950
Node: Arithmetic189358
Node: Incr189811
Node: Eval191486
Node: Shell commands200230
Node: Platform macros201168
Node: Syscmd203370
Node: Esyscmd205745
Node: Sysval207328
Node: Mkstemp209295
Node: Miscellaneous213352
Node: Errprint213789
Node: Location215041
Node: M4exit217918
Node: Frozen files220044
Node: Using frozen files220842
Node: Frozen file format224223
Node: Compatibility227373
Node: Extensions228455
Node: Incompatibilities232509
Node: Other Incompatibilities241813
Node: Answers244543
Node: Improved exch245357
Node: Improved forloop245910
Node: Improved foreach251366
Node: Improved copy264744
Node: Improved m4wrap268801
Node: Improved cleardivert271297
Node: Improved capitalize272295
Node: Improved fatal_error277331
Node: Copying This Package278428
Node: GNU General Public License278907
Node: Copying This Manual317295
Node: GNU Free Documentation License317819
Node: Indices342943
Node: Macro index343227
Node: Concept index349837
Node: Top832
Node: Preliminaries9778
Node: Intro10484
Node: History12175
Node: Bugs16228
Node: Manual17491
Node: Invoking m420994
Node: Operation modes23198
Node: Preprocessor features26297
Node: Limits control29467
Node: Frozen state33478
Node: Debugging options34317
Node: Command line files36369
Node: Syntax38020
Node: Names39175
Node: Quoted strings39657
Node: Comments40324
Node: Other tokens41227
Node: Input processing41821
Ref: Input processing-Footnote-150258
Node: Macros50455
Node: Invocation50949
Node: Inhibiting Invocation51750
Node: Macro Arguments55992
Node: Quoting Arguments59112
Node: Macro expansion61248
Node: Definitions61966
Node: Define62751
Node: Arguments65269
Node: Pseudo Arguments69035
Node: Undefine72664
Node: Defn73823
Node: Pushdef78886
Node: Indir81626
Node: Builtin83793
Node: Conditionals86068
Node: Ifdef87014
Node: Ifelse87896
Node: Shift91282
Node: Forloop102102
Node: Foreach104783
Node: Stacks110405
Node: Composition113540
Node: Debugging121217
Node: Dumpdef121810
Node: Trace123228
Node: Debug Levels126880
Node: Debug Output131750
Node: Input Control133063
Node: Dnl133604
Node: Changequote135546
Node: Changecom145278
Node: Changeword149164
Node: M4wrap154769
Node: File Inclusion158854
Node: Include159175
Node: Search Path161992
Node: Diversions162941
Node: Divert164648
Node: Undivert167214
Node: Divnum170599
Node: Cleardivert171072
Node: Text handling172293
Node: Len173020
Node: Index macro173414
Node: Regexp174307
Node: Substr177468
Node: Translit178526
Node: Patsubst181317
Node: Format185958
Node: Arithmetic189366
Node: Incr189819
Node: Eval191494
Node: Shell commands200238
Node: Platform macros201176
Node: Syscmd203378
Node: Esyscmd205753
Node: Sysval207336
Node: Mkstemp209303
Node: Miscellaneous213360
Node: Errprint213797
Node: Location215049
Node: M4exit217926
Node: Frozen files220052
Node: Using frozen files220850
Node: Frozen file format224231
Node: Compatibility227381
Node: Extensions228463
Node: Incompatibilities232517
Node: Other Incompatibilities241821
Node: Answers244551
Node: Improved exch245365
Node: Improved forloop245918
Node: Improved foreach251374
Node: Improved copy264752
Node: Improved m4wrap268809
Node: Improved cleardivert271305
Node: Improved capitalize272303
Node: Improved fatal_error277339
Node: Copying This Package278436
Node: GNU General Public License278915
Node: Copying This Manual317307
Node: GNU Free Documentation License317831
Node: Indices342955
Node: Macro index343239
Node: Concept index349849

End Tag Table
+3 -3
View File
@@ -1,6 +1,6 @@
This is m4.info, produced by makeinfo version 7.3 from m4.texi.
This is m4.info, produced by makeinfo version 7.2 from m4.texi.
This manual (15 May 2026) is for GNU M4 (version 1.4.21), a package
This manual (6 February 2026) is for GNU M4 (version 1.4.21), a package
containing an implementation of the m4 macro language.
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
@@ -23,7 +23,7 @@ File: m4.info, Node: Top, Next: Preliminaries, Up: (dir)
GNU M4
******
This manual (15 May 2026) is for GNU M4 (version 1.4.21), a package
This manual (6 February 2026) is for GNU M4 (version 1.4.21), a package
containing an implementation of the m4 macro language.
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
+2 -2
View File
@@ -1,6 +1,6 @@
This is m4.info, produced by makeinfo version 7.3 from m4.texi.
This is m4.info, produced by makeinfo version 7.2 from m4.texi.
This manual (15 May 2026) is for GNU M4 (version 1.4.21), a package
This manual (6 February 2026) is for GNU M4 (version 1.4.21), a package
containing an implementation of the m4 macro language.
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
+2 -2
View File
@@ -1,4 +1,4 @@
@set UPDATED 15 May 2026
@set UPDATED-MONTH May 2026
@set UPDATED 6 February 2026
@set UPDATED-MONTH February 2026
@set EDITION 1.4.21
@set VERSION 1.4.21
+2 -2
View File
@@ -1,4 +1,4 @@
@set UPDATED 15 May 2026
@set UPDATED-MONTH May 2026
@set UPDATED 6 February 2026
@set UPDATED-MONTH February 2026
@set EDITION 1.4.21
@set VERSION 1.4.21
+1 -23
View File
@@ -1,31 +1,9 @@
[source]
git = "https://github.com/ninja-build/ninja"
rev = "v1.13.1"
patches = ["redox.patch"]
[build]
template = "custom"
script = """
DYNAMIC_INIT
# Add relibc include/lib paths (same as m4/flex gnulib packages)
RELIBC_INCLUDE="${COOKBOOK_ROOT}/prefix/${TARGET}/relibc-install/${TARGET}/include"
RELIBC_LIB="${COOKBOOK_ROOT}/prefix/${TARGET}/relibc-install/${TARGET}/lib"
export CPPFLAGS="${CPPFLAGS} -isystem ${RELIBC_INCLUDE}"
export LDFLAGS="${LDFLAGS} -L${RELIBC_LIB}"
# Copy updated relibc headers into sysroot so they take precedence over
# the stale toolchain headers at ~/.redoxer/. The C++ <cstdlib> wrapper
# explicitly includes the toolchain's stdlib.h which lacks newer functions
# like getloadavg. The -I path (sysroot/include) takes priority over all
# other include paths, so this ensures our headers win.
mkdir -p "${COOKBOOK_SYSROOT}/include"
cp -a "${RELIBC_INCLUDE}/"* "${COOKBOOK_SYSROOT}/include/"
# Disable tests — build_test.cc includes host headers that conflict with
# relibc's headers during cross-compilation.
cookbook_cmake -DBUILD_TESTING=OFF
"""
template = "cmake"
[package]
description = "Ninja build system"
+65 -12
View File
@@ -1,14 +1,67 @@
--- a/src/subprocess-posix.cc
+++ b/src/subprocess-posix.cc
@@ -72,6 +72,45 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) {
SetCloseOnExec(fd_);
}
+#if defined(__redox__)
+ pid_ = fork();
+ if (pid_ < 0)
+ Fatal("fork: %s", strerror(errno));
+ if (pid_ == 0) {
+ if (sigprocmask(SIG_SETMASK, &set->old_mask_, 0) < 0) {
+ perror("ninja: sigprocmask");
+ _exit(1);
+ }
+ if (!use_console_) {
+ if (setpgid(0, 0) < 0) {
+ perror("ninja: setpgid");
+ _exit(1);
+ }
+ int devnull = open("/dev/null", O_RDONLY);
+ if (devnull < 0) {
+ perror("ninja: open /dev/null");
+ _exit(1);
+ }
+ if (dup2(devnull, 0) < 0 || dup2(subproc_stdout_fd, 1) < 0 ||
+ dup2(subproc_stdout_fd, 2) < 0) {
+ perror("ninja: dup2");
+ _exit(1);
+ }
+ close(devnull);
+ close(fd_);
+ close(subproc_stdout_fd);
+ }
+
+ const char* spawned_args[] = { "/bin/sh", "-c", command.c_str(), NULL };
+ execve("/bin/sh", const_cast<char**>(spawned_args), environ);
+ perror("ninja: execve /bin/sh");
+ _exit(127);
+ }
+
+ if (!use_console_)
+ close(subproc_stdout_fd);
+ return true;
+#else
posix_spawn_file_actions_t action;
int err = posix_spawn_file_actions_init(&action);
if (err != 0)
@@ -145,6 +184,7 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) {
if (!use_console_)
close(subproc_stdout_fd);
return true;
+#endif
}
void Subprocess::OnPipeReady() {
--- a/src/util.cc
+++ b/src/util.cc
@@ -30,6 +30,11 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
+// Redox: the C++ <cstdlib> wrapper pulls in a stale toolchain stdlib.h that
+// lacks getloadavg. Re-declare it here since relibc provides the implementation.
+#if defined(__redox__)
+extern "C" int getloadavg(double loadavg[], int nelem);
+#endif
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -973,7 +973,7 @@ double GetLoadAverage() {
return -0.0f;
return 1.0 / (1 << SI_LOAD_SHIFT) * si.loads[0];
}
-#elif defined(__HAIKU__)
+#elif defined(__HAIKU__) || defined(__redox__)
double GetLoadAverage() {
return -0.0f;
}
@@ -397,7 +397,6 @@ mod tests {
description: "low-priority driver",
priority: 10,
matches: vec![DriverMatch {
bus: None,
vendor: Some(0x1234),
device: None,
class: None,
@@ -414,7 +413,6 @@ mod tests {
description: "high-priority driver",
priority: 100,
matches: vec![DriverMatch {
bus: None,
vendor: Some(0x1234),
device: Some(0x5678),
class: None,
@@ -498,7 +496,6 @@ mod tests {
description: "USB host controller",
priority: 80,
matches: vec![DriverMatch {
bus: None,
vendor: Some(0x8086),
device: None,
class: Some(0x0c),
@@ -8,11 +8,6 @@ pub type MatchPriority = i32;
/// A single entry in a driver's match table.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct DriverMatch {
/// Optional bus type match (e.g., "pci", "acpi").
///
/// When set, only devices on the specified bus will match.
/// When `None`, the match applies to any bus (backward compatible).
pub bus: Option<String>,
/// Optional vendor identifier match.
pub vendor: Option<u16>,
/// Optional device identifier match.
@@ -32,8 +27,7 @@ pub struct DriverMatch {
impl DriverMatch {
/// Checks whether this match entry matches the provided device information.
pub fn matches(&self, info: &DeviceInfo) -> bool {
self.bus.as_ref().map_or(true, |b| &info.id.bus == b)
&& self.vendor.map_or(true, |v| info.vendor == Some(v))
self.vendor.map_or(true, |v| info.vendor == Some(v))
&& self.device.map_or(true, |d| info.device == Some(d))
&& self.class.map_or(true, |c| info.class == Some(c))
&& self.subclass.map_or(true, |s| info.subclass == Some(s))
@@ -106,7 +100,6 @@ mod tests {
fn driver_match_accepts_exact_match() {
let info = sample_device();
let driver_match = DriverMatch {
bus: None,
vendor: Some(0x8086),
device: Some(0x1234),
class: Some(0x03),
@@ -123,7 +116,6 @@ mod tests {
fn driver_match_supports_wildcards() {
let info = sample_device();
let driver_match = DriverMatch {
bus: None,
vendor: Some(0x8086),
device: None,
class: Some(0x03),
@@ -140,7 +132,6 @@ mod tests {
fn driver_match_rejects_mismatch() {
let info = sample_device();
let driver_match = DriverMatch {
bus: None,
vendor: Some(0x10ec),
device: None,
class: None,
@@ -152,48 +143,4 @@ mod tests {
assert!(!driver_match.matches(&info));
}
#[test]
fn driver_match_bus_filtering() {
let info = sample_device();
// Matching bus should pass
let pci_match = DriverMatch {
bus: Some(String::from("pci")),
vendor: Some(0x8086),
device: None,
class: None,
subclass: None,
prog_if: None,
subsystem_vendor: None,
subsystem_device: None,
};
assert!(pci_match.matches(&info));
// Non-matching bus should fail
let acpi_match = DriverMatch {
bus: Some(String::from("acpi")),
vendor: Some(0x8086),
device: None,
class: None,
subclass: None,
prog_if: None,
subsystem_vendor: None,
subsystem_device: None,
};
assert!(!acpi_match.matches(&info));
// None bus should match any device (backward compatible)
let any_bus = DriverMatch {
bus: None,
vendor: Some(0x8086),
device: None,
class: None,
subclass: None,
prog_if: None,
subsystem_vendor: None,
subsystem_device: None,
};
assert!(any_bus.matches(&info));
}
}
@@ -291,21 +291,12 @@ fn read_cpu_count() -> Result<u8> {
#[cfg(target_os = "redox")]
fn alloc_cpu_id() -> u8 {
match read_cpu_count() {
Ok(0) => {
log::warn!("redox-driver-sys: read_cpu_count returned 0, defaulting to BSP (cpu 0)");
0
}
Ok(n) => {
Ok(n) if n > 0 => {
use std::sync::atomic::{AtomicU8, Ordering};
static NEXT: AtomicU8 = AtomicU8::new(0);
let cpu_id = NEXT.fetch_add(1, Ordering::Relaxed) % n;
log::debug!("redox-driver-sys: alloc_cpu_id selected cpu {} (of {})", cpu_id, n);
cpu_id
}
Err(err) => {
log::warn!("redox-driver-sys: read_cpu_count failed ({}), defaulting to BSP (cpu 0)", err);
0
NEXT.fetch_add(1, Ordering::Relaxed) % n
}
_ => 0,
}
}
@@ -11,16 +11,12 @@ use redox_scheme::Socket;
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
unsafe fn get_fd(var: &str) -> Option<RawFd> {
// Env vars like INIT_NOTIFY are optional — daemons not spawned by init
// simply don't have them. Return None silently instead of spewing errors.
let fd: RawFd = match std::env::var(var)
.map_err(|e| eprintln!("daemon: env var {var} not set: {e}"))
.ok()
.and_then(|val| {
val.parse::<RawFd>()
.map_err(|e| {
eprintln!("daemon: failed to parse {var} as fd: {e}");
e
})
val.parse()
.map_err(|e| eprintln!("daemon: failed to parse {var} as fd: {e}"))
.ok()
}) {
Some(fd) => fd,
@@ -123,3 +123,45 @@ mod tests {
assert_eq!(&edid[0..8], &header, "EDID header should be valid");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn synthetic_displayport_has_correct_fields() {
let conn = Connector::synthetic_displayport(5, 10);
assert_eq!(conn.info.id, 5);
assert_eq!(conn.info.encoder_id, 10);
assert_eq!(conn.info.connector_type, ConnectorType::DisplayPort);
assert_eq!(conn.info.connection, ConnectorStatus::Connected);
assert!(
!conn.info.modes.is_empty(),
"synthetic DisplayPort should have modes"
);
}
#[test]
fn synthetic_displayport_modes_have_valid_dimensions() {
let conn = Connector::synthetic_displayport(1, 1);
for mode in &conn.info.modes {
assert!(mode.hdisplay > 0, "mode hdisplay should be > 0");
assert!(mode.vdisplay > 0, "mode vdisplay should be > 0");
assert!(mode.vrefresh > 0, "mode vrefresh should be > 0");
assert!(mode.clock > 0, "mode clock should be > 0");
}
}
#[test]
fn synthetic_edid_returns_exactly_112_bytes() {
let edid = synthetic_edid();
assert_eq!(edid.len(), 112);
}
#[test]
fn synthetic_edid_has_valid_header() {
let edid = synthetic_edid();
let header: [u8; 8] = [0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00];
assert_eq!(&edid[0..8], &header, "EDID header should be valid");
}
}
@@ -198,15 +198,14 @@ mod tests {
}
#[test]
fn from_edid_synthetic_edid_parses_1080p_mode() {
fn from_edid_synthetic_edid_too_short_returns_empty() {
let edid = super::connector::synthetic_edid();
assert_eq!(edid.len(), 128, "synthetic EDID must be 128 bytes");
assert!(edid.len() < 128, "synthetic EDID is shorter than 128 bytes");
let modes = ModeInfo::from_edid(&edid);
assert!(!modes.is_empty(), "valid 128-byte EDID should produce at least one mode");
let mode = &modes[0];
assert_eq!(mode.hdisplay, 1920, "first mode should be 1920px wide");
assert_eq!(mode.vdisplay, 1080, "first mode should be 1080px tall");
assert_eq!(mode.vrefresh, 60, "first mode should be 60 Hz");
assert!(
modes.is_empty(),
"EDID shorter than 128 bytes should produce no modes"
);
}
#[test]
@@ -58,12 +58,8 @@ fn read_acpi_pss(cpu: u32) -> Vec<PState> {
}
fn write_msr(cpu: u32, msr: u32, val: u64) -> bool {
let path = format!("/scheme/sys/msr/{}/{:x}", cpu, msr);
fs::OpenOptions::new().write(true).open(&path).ok()
.and_then(|mut f| {
let hex_val = format!("{:016x}", val);
f.write_all(hex_val.as_bytes()).ok()
}).is_some()
fs::OpenOptions::new().write(true).open(format!("/dev/cpu/{}/msr", cpu)).ok()
.map(|mut f| f.write_all(&val.to_ne_bytes()).is_ok()).unwrap_or(false)
}
fn measure_load(cpu: u32, prev: &mut (u64, u64)) -> f64 {
@@ -11,7 +11,6 @@ path = "src/main.rs"
[dependencies]
redox-driver-core = { path = "../../drivers/redox-driver-core" }
redox-driver-pci = { path = "../../drivers/redox-driver-pci" }
redox-driver-acpi = { path = "../../drivers/redox-driver-acpi" }
pcid_interface = { path = "../../../../recipes/core/base/source/drivers/pcid", package = "pcid" }
redox_syscall = "0.7"
log = "0.4"
@@ -2,20 +2,4 @@
path = "source"
[build]
template = "custom"
script = """
# driver-manager runs in both rootfs and initfs; initfs has no dynamic linker,
# so we must build a statically linked binary.
export RUSTFLAGS="${RUSTFLAGS} -Ctarget-feature=+crt-static -L native=${COOKBOOK_SYSROOT}/lib"
"${COOKBOOK_CARGO}" build \
--manifest-path "${COOKBOOK_SOURCE}/Cargo.toml" \
--target "${TARGET}" \
${build_flags}
mkdir -pv "${COOKBOOK_STAGE}/usr/bin"
cp -v "target/${TARGET}/${build_type}/driver-manager" "${COOKBOOK_STAGE}/usr/bin/driver-manager"
"""
[dependencies]
redox-driver-core = {}
redox-driver-pci = {}
redox-driver-acpi = {}
template = "cargo"
@@ -11,11 +11,9 @@ path = "src/main.rs"
[dependencies]
redox-driver-core = { path = "../../../drivers/redox-driver-core/source" }
redox-driver-pci = { path = "../../../drivers/redox-driver-pci/source" }
redox-driver-acpi = { path = "../../../drivers/redox-driver-acpi/source" }
pcid_interface = { path = "../../../../../recipes/core/base/source/drivers/pcid", package = "pcid" }
redox-scheme = "0.11"
syscall = { package = "redox_syscall", version = "0.7" }
log = "0.4"
toml = "0.8"
serde = { version = "1", features = ["derive"] }
libc = "0.2"
@@ -1,5 +1,4 @@
use std::collections::HashMap;
use std::collections::BTreeSet;
use std::fs;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
use std::path::Path;
@@ -9,18 +8,11 @@ use std::sync::Mutex;
use std::vec::Vec;
use pcid_interface::PciFunctionHandle;
use redox_driver_acpi::AcpiBus;
use redox_driver_core::device::DeviceInfo;
use redox_driver_core::driver::{Driver, DriverError, ProbeResult};
use redox_driver_core::r#match::DriverMatch;
use redox_driver_core::params::{DriverParams, ParamValue};
// Device+driver pairs that should never be re-probed because the driver
// binary is absent (Fatal), the driver declined the device (NotSupported),
// or deferred retries were exhausted. Checked by probe() before any work.
pub(crate) static PERMANENTLY_SKIPPED: Mutex<BTreeSet<(String, String)>> =
Mutex::new(BTreeSet::new());
use serde::Deserialize;
#[derive(Debug)]
@@ -56,7 +48,6 @@ impl Clone for DriverConfig {
#[derive(Deserialize)]
struct RawDriverMatch {
bus: Option<String>,
vendor: Option<u16>,
device: Option<u16>,
class: Option<u8>,
@@ -69,7 +60,6 @@ struct RawDriverMatch {
impl From<RawDriverMatch> for DriverMatch {
fn from(r: RawDriverMatch) -> Self {
DriverMatch {
bus: r.bus,
vendor: r.vendor,
device: r.device,
class: r.class,
@@ -107,7 +97,7 @@ impl DriverConfig {
if matches.is_empty() {
log::warn!(
"driver-manager: config {} driver={} has no match entries and will not bind from PCI or ACPI enumeration",
"driver-manager: config {} driver={} has no PCI match entries and will not bind from PCI enumeration",
path.display(),
driver.name
);
@@ -138,18 +128,6 @@ fn pci_device_path(info: &DeviceInfo) -> String {
}
}
/// Build the ACPI scheme path for a device.
///
/// The path follows the pattern `/scheme/acpi/symbols/{device_name}`,
/// where device_name is the ACPI namespace path (e.g., "PCI0", "I2C0", "GPI0").
fn acpi_device_path(info: &DeviceInfo) -> String {
if info.raw_path.starts_with("/scheme/acpi/") {
info.raw_path.clone()
} else {
format!("/scheme/acpi/symbols/{}", info.id.path)
}
}
fn open_pcid_channel(device_path: &str) -> Result<OwnedFd, ProbeResult> {
let mut handle = match PciFunctionHandle::connect_by_path(Path::new(device_path)) {
Ok(handle) => handle,
@@ -176,24 +154,10 @@ fn open_pcid_channel(device_path: &str) -> Result<OwnedFd, ProbeResult> {
}
fn check_scheme_available(name: &str) -> bool {
let path = format!("/scheme/{}", name);
// Use read_dir instead of Path::exists() because Redox scheme paths
// may not respond correctly to exists()/metadata() while still being
// fully functional for directory enumeration and file open.
// This was the root cause of "dependency scheme not ready: pci" even
// though PciBus::enumerate_devices (which uses read_dir) succeeded.
match fs::read_dir(&path) {
Ok(_) => true,
Err(err) => {
log::debug!(
"driver-manager: scheme availability check failed for {}: {} (exists={})",
path,
err,
std::path::Path::new(&path).exists()
);
false
}
if std::path::Path::new(&format!("/scheme/{}", name)).exists() {
return true;
}
false
}
impl Driver for DriverConfig {
@@ -231,22 +195,6 @@ impl Driver for DriverConfig {
}
}
// Check if this device+driver pair was permanently abandoned
// by the hotplug loop (binary missing, driver declined, or
// deferred retries exhausted). Skip without any work or logging.
{
let key = (device_key.clone(), self.name.clone());
let skipped = match PERMANENTLY_SKIPPED.lock() {
Ok(skipped) => skipped,
Err(_) => return ProbeResult::Fatal {
reason: String::from("skip set lock poisoned"),
},
};
if skipped.contains(&key) {
return ProbeResult::NotSupported;
}
}
if self.command.is_empty() {
return ProbeResult::Fatal {
reason: String::from("empty command"),
@@ -259,29 +207,12 @@ impl Driver for DriverConfig {
format!("/usr/lib/drivers/{}", self.command[0])
};
// Also check the initfs path — drivers like nvmed live in
// /scheme/initfs/lib/drivers/ during early boot and may not yet
// be staged to /usr/lib/drivers/ after switchroot.
if !std::path::Path::new(&actual_path).exists() {
let initfs_path = format!("/scheme/initfs/lib/drivers/{}", self.command[0].rsplit('/').next().unwrap_or(&self.command[0]));
if std::path::Path::new(&initfs_path).exists() {
return ProbeResult::Deferred {
reason: format!("driver in initfs only (not yet in rootfs): {}", initfs_path),
};
}
return ProbeResult::Fatal {
reason: format!("driver binary not found: {} (also checked {})", actual_path, initfs_path),
reason: format!("driver binary not found: {}", actual_path),
};
}
// Skip if this driver's scheme is already registered (e.g., by
// pcid-spawner during initfs). Prevents re-spawning drivers
// that are already serving their scheme.
if check_scheme_available(&self.name) {
log::info!("driver {} already serving scheme, skipping probe for {}", self.name, device_key);
return ProbeResult::Bound;
}
let deps: Vec<String> = if !self.depends_on.is_empty() {
self.depends_on.clone()
} else {
@@ -297,13 +228,43 @@ impl Driver for DriverConfig {
log::info!("probing {} with driver {}", device_key, self.name);
// Branch on bus type: PCI devices use the pcid channel,
// ACPI devices use the ACPI scheme path with resource queries.
match info.id.bus.as_str() {
"pci" => self.probe_pci_device(info, &device_key, &actual_path),
"acpi" => self.probe_acpi_device(info, &device_key, &actual_path),
other => ProbeResult::Fatal {
reason: format!("unsupported bus type: {}", other),
let device_path = pci_device_path(info);
let channel_fd = match open_pcid_channel(&device_path) {
Ok(channel_fd) => channel_fd,
Err(result) => return result,
};
let mut cmd = Command::new(&actual_path);
for arg in &self.command[1..] {
cmd.arg(arg);
}
cmd.env("PCID_CLIENT_CHANNEL", channel_fd.as_raw_fd().to_string());
cmd.env("PCID_DEVICE_PATH", &device_path);
match cmd.spawn() {
Ok(child) => {
let pid = child.id();
log::info!(
"driver {} spawned (pid {}) for device {}",
self.name,
pid,
device_key
);
let mut spawned = match self.spawned.lock() {
Ok(spawned) => spawned,
Err(err) => {
return ProbeResult::Fatal {
reason: format!("spawn state lock poisoned after spawn: {err}"),
};
}
};
spawned.insert(device_key, SpawnedDriver { child, channel_fd });
ProbeResult::Bound
}
Err(e) => ProbeResult::Fatal {
reason: format!("spawn failed: {}", e),
},
}
}
@@ -379,223 +340,6 @@ impl Driver for DriverConfig {
}
}
impl DriverConfig {
/// Check for exited child processes (non-blocking waitpid).
/// Returns a list of (device_key, driver_name, exit_status) for exited drivers.
pub fn reap_exited_children(&self) -> Vec<(String, String, i32)> {
let mut exited = Vec::new();
let Ok(mut spawned) = self.spawned.lock() else {
return exited;
};
let mut to_remove = Vec::new();
for (device_key, spawned_driver) in spawned.iter_mut() {
match spawned_driver.child.try_wait() {
Ok(Some(status)) => {
let code = status.code().unwrap_or(-1);
log::warn!(
"driver {} (pid {}) for device {} exited with status {}",
self.name,
spawned_driver.child.id(),
device_key,
code
);
to_remove.push(device_key.clone());
exited.push((device_key.clone(), self.name.clone(), code));
}
Ok(None) => {
// Still running
}
Err(err) => {
log::error!(
"failed to check status of driver {} pid {}: {}",
self.name,
spawned_driver.child.id(),
err
);
}
}
}
for key in to_remove {
spawned.remove(&key);
}
exited
}
}
impl DriverConfig {
/// Probe and spawn a driver for a PCI device.
///
/// Opens a pcid channel for PCI config space access and passes the
/// channel FD and device path to the spawned driver via environment variables.
fn probe_pci_device(
&self,
info: &DeviceInfo,
device_key: &str,
actual_path: &str,
) -> ProbeResult {
let device_path = pci_device_path(info);
let channel_fd = match open_pcid_channel(&device_path) {
Ok(channel_fd) => channel_fd,
Err(result) => return result,
};
let mut cmd = Command::new(actual_path);
for arg in &self.command[1..] {
cmd.arg(arg);
}
cmd.env("PCID_CLIENT_CHANNEL", channel_fd.as_raw_fd().to_string());
cmd.env("PCID_DEVICE_PATH", &device_path);
self.spawn_driver(cmd, device_key, channel_fd)
}
/// Probe and spawn a driver for an ACPI device.
///
/// Queries ACPI resources (_CRS) from the device and passes them as
/// environment variables to the spawned driver. The driver can then
/// use these to map MMIO regions and request IRQs.
///
/// # Linux equivalent
///
/// Linux's `acpi_device_probe()` calls `acpi_dev_get_resources()`
/// to extract IRQ/MMIO/IO resources from _CRS and passes them via
/// `struct resource` to the platform driver's `probe()` callback.
fn probe_acpi_device(
&self,
info: &DeviceInfo,
device_key: &str,
actual_path: &str,
) -> ProbeResult {
let device_path = acpi_device_path(info);
// Query ACPI resources for this device.
// Uses the AcpiBus resource query API which reads _CRS data.
let acpi_bus = AcpiBus::new();
let resources = acpi_bus.query_device_resources(&info.id.path);
let mut cmd = Command::new(actual_path);
for arg in &self.command[1..] {
cmd.arg(arg);
}
// Pass device identification
cmd.env("ACPI_DEVICE_PATH", &device_path);
cmd.env("ACPI_DEVICE_NAME", &info.id.path);
// Pass _HID if available
if let Some(ref desc) = info.description {
cmd.env("ACPI_DEVICE_DESCRIPTION", desc);
}
// Extract and pass MMIO regions as env vars.
// Format: ACPI_MMIO_0=base,length ACPI_MMIO_1=base,length ...
let mmio_regions = redox_driver_acpi::extract_mmio_regions(&resources);
for (i, region) in mmio_regions.iter().enumerate() {
cmd.env(
format!("ACPI_MMIO_{}", i),
format!("{:#x},{:#x}", region.base, region.length),
);
}
if !mmio_regions.is_empty() {
cmd.env("ACPI_MMIO_COUNT", mmio_regions.len().to_string());
}
// Extract and pass IRQ info as env vars.
// Format: ACPI_IRQ_0=gsi,triggering,polarity ACPI_IRQ_1=gsi,triggering,polarity ...
let irqs = redox_driver_acpi::extract_irqs(&resources);
for (i, irq) in irqs.iter().enumerate() {
let trigger = match irq.triggering {
redox_driver_acpi::TriggerMode::Edge => "edge",
redox_driver_acpi::TriggerMode::Level => "level",
};
let polarity = match irq.polarity {
redox_driver_acpi::Polarity::ActiveHigh => "high",
redox_driver_acpi::Polarity::ActiveLow => "low",
redox_driver_acpi::Polarity::ActiveBoth => "both",
};
cmd.env(
format!("ACPI_IRQ_{}", i),
format!("{:#x},{},{}", irq.gsi, trigger, polarity),
);
}
if !irqs.is_empty() {
cmd.env("ACPI_IRQ_COUNT", irqs.len().to_string());
}
// Extract and pass I/O port ranges
let io_ports = redox_driver_acpi::extract_io_ports(&resources);
for (i, port) in io_ports.iter().enumerate() {
cmd.env(
format!("ACPI_IO_{}", i),
format!("{:#x},{:#x}", port.base, port.length),
);
}
if !io_ports.is_empty() {
cmd.env("ACPI_IO_COUNT", io_ports.len().to_string());
}
// ACPI drivers don't use a pcid channel — they access hardware
// via scheme:memory (MMIO) and scheme:irq directly.
// Create a dummy fd to satisfy the spawn signature.
// The driver reads resources from the env vars above.
let dev_null = match std::fs::File::open("/scheme/null") {
Ok(f) => unsafe { OwnedFd::from_raw_fd(f.as_raw_fd()) },
Err(_) => {
// Fallback: open /dev/null on Linux hosts during testing
match std::fs::File::open("/dev/null") {
Ok(f) => unsafe { OwnedFd::from_raw_fd(f.as_raw_fd()) },
Err(e) => {
return ProbeResult::Fatal {
reason: format!("cannot open null device for ACPI channel: {}", e),
};
}
}
}
};
self.spawn_driver(cmd, device_key, dev_null)
}
/// Common driver spawn logic — shared by PCI and ACPI probe paths.
fn spawn_driver(
&self,
mut cmd: Command,
device_key: &str,
channel_fd: OwnedFd,
) -> ProbeResult {
match cmd.spawn() {
Ok(child) => {
let pid = child.id();
log::info!(
"driver {} spawned (pid {}) for device {}",
self.name,
pid,
device_key
);
let mut spawned = match self.spawned.lock() {
Ok(spawned) => spawned,
Err(err) => {
return ProbeResult::Fatal {
reason: format!("spawn state lock poisoned after spawn: {err}"),
};
}
};
spawned.insert(device_key.to_string(), SpawnedDriver { child, channel_fd });
ProbeResult::Bound
}
Err(e) => ProbeResult::Fatal {
reason: format!("spawn failed: {}", e),
},
}
}
}
/// Driver-specified dependencies. Parsed from [driver.depends] TOML field.
/// Example: depends_on = ["pci", "acpi"]
/// When specified, takes precedence over guess_dependencies().
@@ -639,7 +383,7 @@ struct RawDriverEntry {
priority: i32,
#[serde(default)]
command: Vec<String>,
#[serde(rename = "match", default)]
#[serde(rename = "match")]
r#match: Vec<RawDriverMatch>,
#[serde(default)]
depends_on: Vec<String>,
@@ -26,6 +26,7 @@ pub fn run_hotplug_loop(
);
let mut deferred_retries: BTreeMap<(String, String), u32> = BTreeMap::new();
let mut permanently_fatal: BTreeSet<(String, String)> = BTreeSet::new();
loop {
thread::sleep(Duration::from_millis(poll_interval_ms));
@@ -66,6 +67,15 @@ pub fn run_hotplug_loop(
track_pci_device(device, &mut seen_pci_devices);
let key = (device.path.clone(), driver_name.clone());
// Skip devices that were permanently fatal in a previous cycle.
// enumerate() re-probes all unbound devices each poll, but a Fatal
// result means the driver binary is genuinely absent (e.g. ided on
// a live ISO that doesn't ship it) — no amount of re-probing will
// change the outcome.
if permanently_fatal.contains(&key) {
continue;
}
match result {
ProbeResult::Bound => {
log::info!("hotplug: bound {} -> {}", device.path, driver_name);
@@ -89,12 +99,6 @@ pub fn run_hotplug_loop(
MAX_DEFERRED_RETRIES,
reason
);
if let Ok(mut skipped) = crate::config::PERMANENTLY_SKIPPED.lock() {
skipped.insert((
device.path.clone(),
driver_name.clone(),
));
}
}
}
ProbeResult::Fatal { reason } => {
@@ -104,20 +108,9 @@ pub fn run_hotplug_loop(
driver_name,
reason
);
if let Ok(mut skipped) = crate::config::PERMANENTLY_SKIPPED.lock() {
skipped.insert(key);
}
}
ProbeResult::NotSupported => {
log::debug!(
"hotplug: not supported {} -> {}",
device.path,
driver_name
);
if let Ok(mut skipped) = crate::config::PERMANENTLY_SKIPPED.lock() {
skipped.insert(key);
}
permanently_fatal.insert(key);
}
_ => {}
}
}
ProbeEvent::NoDriverFound { device } => {
@@ -207,8 +200,6 @@ fn track_pci_device(device: &DeviceId, seen_pci_devices: &mut BTreeSet<String>)
}
fn notify_bound_device(scheme: &DriverManagerScheme, device: &DeviceId, driver_name: &str) {
// PCI devices use the pcid-compatible bind notification.
// ACPI devices may be notified through other mechanisms in the future.
if device.bus == "pci" {
notify_bind(scheme, &device.path, driver_name);
}
@@ -3,7 +3,6 @@ mod exec;
mod hotplug;
mod scheme;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
@@ -13,26 +12,12 @@ use redox_driver_core::device::DeviceId;
use redox_driver_core::driver::ProbeResult;
use redox_driver_core::manager::{DeviceManager, ManagerConfig, ProbeEvent};
use redox_driver_pci::PciBus;
use redox_driver_acpi::AcpiBus;
use std::fs::OpenOptions;
use std::io::Write;
use config::DriverConfig;
use scheme::{DriverManagerScheme, notify_bind};
/// Global flag set by SIGTERM handler to request graceful shutdown.
static SHUTDOWN_REQUESTED: AtomicBool = AtomicBool::new(false);
extern "C" fn sigterm_handler(_sig: i32) {
SHUTDOWN_REQUESTED.store(true, Ordering::SeqCst);
}
fn install_sigterm_handler() {
unsafe {
libc::signal(libc::SIGTERM, sigterm_handler as *const () as usize);
}
}
struct StderrLogger;
const BOOT_TIMELINE_PATH: &str = "/tmp/redbear-boot-timeline.json";
@@ -52,7 +37,6 @@ impl log::Log for StderrLogger {
fn run_enumeration(
manager: &Arc<Mutex<DeviceManager>>,
scheme: &DriverManagerScheme,
initfs: bool,
) -> (usize, usize) {
let enum_start = Instant::now();
let events = match manager.lock() {
@@ -93,11 +77,7 @@ fn run_enumeration(
log::info!("bus {} enumerated {} device(s)", bus, device_count);
}
ProbeEvent::BusEnumerationFailed { bus, error } => {
if initfs && *bus == "pci" {
log::warn!("bus {} enumeration not yet ready (initfs, pcid may still be starting): {:?}", bus, error);
} else {
log::error!("bus {} enumeration failed: {:?}", bus, error);
}
log::error!("bus {} enumeration failed: {:?}", bus, error);
}
ProbeEvent::AlreadyBound {
device,
@@ -133,19 +113,14 @@ fn run_enumeration(
}
fn notify_bound_device(scheme: &DriverManagerScheme, device: &DeviceId, driver_name: &str) {
// Notify for both PCI and ACPI devices
notify_bind(scheme, &device.path, driver_name);
if device.bus == "pci" {
notify_bind(scheme, &device.path, driver_name);
}
}
fn reset_timeline_log() {
// Best-effort: truncate or create empty. On scheme filesystems that
// don't support truncate on existing files, this may fail — that's OK,
// the append path will handle it.
match fs::write(BOOT_TIMELINE_PATH, "") {
Ok(()) => {}
Err(_) => {
let _ = fs::remove_file(BOOT_TIMELINE_PATH);
}
if let Err(err) = fs::write(BOOT_TIMELINE_PATH, "") {
log::warn!("failed to reset boot timeline log at {BOOT_TIMELINE_PATH}: {err}");
}
}
@@ -238,127 +213,22 @@ fn log_timeline(event: &ProbeEvent) {
{
Ok(mut file) => {
if let Err(err) = writeln!(file, "{entry}") {
// EPIPE or other write errors can occur when /tmp is backed
// by a scheme that doesn't support append writes, or when the
// filesystem is not yet fully ready. Log once and suppress
// all subsequent write errors to avoid log spam.
static WRITE_ERROR_LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
if !WRITE_ERROR_LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
log::warn!("failed to append boot timeline entry to {BOOT_TIMELINE_PATH}: {err} (suppressing further write errors)");
}
log::warn!("failed to append boot timeline entry to {BOOT_TIMELINE_PATH}: {err}");
}
}
Err(err) => {
// EEXIST (os error 17) can occur when the file already exists
// but the scheme filesystem doesn't support create+append.
// EPIPE and other errors occur when /tmp isn't ready.
// Log once and suppress all subsequent open errors.
static OPEN_ERROR_LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
if !OPEN_ERROR_LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
log::warn!("failed to open boot timeline log at {BOOT_TIMELINE_PATH}: {err} (suppressing further open errors)");
}
log::warn!("failed to open boot timeline log at {BOOT_TIMELINE_PATH}: {err}");
}
}
}
fn run_status() {
// Print the boot timeline log if it exists.
match fs::read_to_string(BOOT_TIMELINE_PATH) {
Ok(content) => {
if content.trim().is_empty() {
println!("No boot timeline data found at {}", BOOT_TIMELINE_PATH);
println!("Driver manager has not completed enumeration yet.");
return;
}
println!("=== Red Bear OS Driver Manager Status ===");
println!();
let mut bound = 0usize;
let mut deferred = 0usize;
let mut failed = 0usize;
let mut no_driver = 0usize;
let mut buses = Vec::new();
for line in content.lines() {
if line.trim().is_empty() {
continue;
}
// Parse JSON timeline entries
if line.contains("\"event\":\"bus_enumerated\"") {
if let Some(bus) = extract_json_string(line, "bus") {
if let Some(count) = extract_json_number(line, "count") {
buses.push((bus, count));
}
}
} else if line.contains("\"status\":\"bound\"") {
bound += 1;
} else if line.contains("\"status\":\"deferred\"") {
deferred += 1;
} else if line.contains("\"status\":\"failed\"") {
failed += 1;
} else if line.contains("\"event\":\"no_driver\"") {
no_driver += 1;
}
}
println!("Bus enumeration:");
for (bus, count) in &buses {
println!(" {}: {} device(s)", bus, count);
}
println!();
println!("Driver binding:");
println!(" bound: {}", bound);
println!(" deferred: {}", deferred);
println!(" failed: {}", failed);
println!(" no driver: {}", no_driver);
println!();
println!("Timeline log: {}", BOOT_TIMELINE_PATH);
}
Err(err) => {
println!("Cannot read {}: {}", BOOT_TIMELINE_PATH, err);
println!("Driver manager may not have run yet.");
}
}
}
/// Extract a JSON string value for a given key from a single-line JSON object.
fn extract_json_string(line: &str, key: &str) -> Option<String> {
let pattern = format!("\"{}\":\"", key);
let start = line.find(&pattern)?;
let value_start = start + pattern.len();
let end = line[value_start..].find('"')?;
Some(line[value_start..value_start + end].to_string())
}
/// Extract a JSON number value for a given key from a single-line JSON object.
fn extract_json_number(line: &str, key: &str) -> Option<usize> {
let pattern = format!("\"{}\":", key);
let start = line.find(&pattern)?;
let value_start = start + pattern.len();
let rest = &line[value_start..];
let end = rest.find(|c: char| !c.is_ascii_digit()).unwrap_or(rest.len());
rest[..end].parse().ok()
}
fn main() {
log::set_logger(&StderrLogger).ok();
log::set_max_level(log::LevelFilter::Info);
// Install SIGTERM handler for graceful shutdown
install_sigterm_handler();
let args: Vec<String> = env::args().collect();
let initfs = args.iter().any(|a| a == "--initfs");
let hotplug_mode = args.iter().any(|a| a == "--hotplug");
let status_mode = args.iter().any(|a| a == "--status");
// --status: print the current device registry from the boot timeline log
// and exit. This is for diagnostics: "what did driver-manager find?"
if status_mode {
run_status();
return;
}
let config_dir = if initfs {
"/scheme/initfs/lib/drivers.d"
@@ -392,17 +262,8 @@ fn main() {
match manager.lock() {
Ok(mut mgr) => {
// Register PCI bus first (higher priority — storage, network, GPU).
// Mirrors Linux's pci_scan_child_bus() via subsys_initcall.
mgr.register_bus(Box::new(PciBus::new()));
// Register ACPI bus for platform/I2C/SPI/GPIO/thermal devices.
// Mirrors Linux's acpi_bus_scan() which walks the namespace for
// _HID/_CID/_STA/_CRS. ACPI devices are enumerated from
// /scheme/acpi/symbols/ which acpid populates from the AML
// interpreter.
mgr.register_bus(Box::new(AcpiBus::new()));
for dc in &driver_configs {
mgr.register_driver(Box::new(dc.clone()));
}
@@ -416,14 +277,11 @@ fn main() {
let mgr_clone = Arc::clone(&manager);
let scheme_clone = Arc::clone(&scheme);
// Ensure /tmp exists before writing the boot timeline log.
let _ = std::fs::create_dir_all("/tmp");
reset_timeline_log();
if manager_config.async_probe {
let handle = thread::spawn(move || {
let (bound, deferred) = run_enumeration(&mgr_clone, scheme_clone.as_ref(), initfs);
let (bound, deferred) = run_enumeration(&mgr_clone, scheme_clone.as_ref());
log::info!("async enum: {} bound, {} deferred", bound, deferred);
});
if handle.join().is_err() {
@@ -431,21 +289,13 @@ fn main() {
process::exit(1);
}
} else {
let (bound, deferred) = run_enumeration(&manager, scheme.as_ref(), initfs);
let (bound, deferred) = run_enumeration(&manager, scheme.as_ref());
log::info!("enum complete: {} bound, {} deferred", bound, deferred);
}
match scheme::start_scheme_server(Arc::clone(&scheme)) {
Ok(true) => {
log::info!("driver-manager: scheme server started successfully");
}
Ok(false) => {
log::warn!("driver-manager: scheme already registered — another instance is active, continuing without scheme server");
}
Err(err) => {
log::error!("{err}");
process::exit(1);
}
if let Err(err) = scheme::start_scheme_server(Arc::clone(&scheme)) {
log::error!("{err}");
process::exit(1);
}
if hotplug_mode {
@@ -454,17 +304,8 @@ fn main() {
idle_forever();
}
let max_retries = 3u32;
let max_retries = 30u32;
for retry in 1..=max_retries {
if SHUTDOWN_REQUESTED.load(Ordering::SeqCst) {
log::info!("driver-manager: SIGTERM received during deferred retry, shutting down");
graceful_shutdown();
process::exit(0);
}
// Check for crashed drivers during retry loop
reap_all_drivers(&driver_configs);
thread::sleep(Duration::from_millis(500));
let retry_events = match manager.lock() {
@@ -519,35 +360,6 @@ fn main() {
fn idle_forever() -> ! {
log::info!("driver-manager: entering persistent idle loop");
loop {
thread::sleep(Duration::from_secs(5));
if SHUTDOWN_REQUESTED.load(Ordering::SeqCst) {
log::info!("driver-manager: SIGTERM received, performing graceful shutdown");
graceful_shutdown();
process::exit(0);
}
// Periodically check for exited child drivers
reap_all_drivers(&[]);
thread::sleep(Duration::from_secs(3600));
}
}
/// Poll all driver configs for exited children and log the results.
fn reap_all_drivers(driver_configs: &[DriverConfig]) {
for dc in driver_configs {
let exited = dc.reap_exited_children();
for (device_key, driver_name, code) in &exited {
log::warn!(
"reaped crashed driver: {} for device {} (exit {})",
driver_name,
device_key,
code
);
}
}
}
fn graceful_shutdown() {
// The DeviceManager and spawned children are managed by DriverConfig instances
// which track their child processes. On shutdown, we log and exit cleanly.
// Child processes will be orphaned but the kernel reaps them.
log::info!("driver-manager: clean shutdown complete");
}
@@ -112,9 +112,9 @@ impl DriverManagerScheme {
["devices"] => Ok(HandleKind::Devices),
["bound"] => Ok(HandleKind::Bound),
["events"] => Ok(HandleKind::Events),
["devices", addr] if Self::valid_device_addr(addr) => {
let _ = self.device_status(addr)?;
Ok(HandleKind::Device((*addr).to_string()))
["devices", pci_addr] if Self::valid_pci_addr(pci_addr) => {
let _ = self.device_status(pci_addr)?;
Ok(HandleKind::Device((*pci_addr).to_string()))
}
_ => Err(Error::new(ENOENT)),
}
@@ -127,7 +127,7 @@ impl DriverManagerScheme {
return Ok(HandleKind::Devices);
}
if trimmed.contains('/') || !Self::valid_device_addr(trimmed) {
if trimmed.contains('/') || !Self::valid_pci_addr(trimmed) {
return Err(Error::new(ENOENT));
}
@@ -228,23 +228,6 @@ impl DriverManagerScheme {
.all(|ch| ch.is_ascii_hexdigit() || matches!(ch, ':' | '.'))
}
/// Validate a device address for both PCI and ACPI devices.
///
/// PCI addresses contain colons and dots (e.g., "0000:00:1f.2").
/// ACPI device names are alphanumeric 4-char segments (e.g., "PCI0", "I2C0", "GPI0").
#[cfg(target_os = "redox")]
fn valid_device_addr(value: &str) -> bool {
// Accept PCI-style addresses
if Self::valid_pci_addr(value) {
return true;
}
// Accept ACPI device names (alphanumeric, dots for child paths)
!value.is_empty()
&& value
.chars()
.all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '.' | '_'))
}
fn push_event_line(&self, line: String) {
match self.events.lock() {
Ok(mut events) => {
@@ -383,14 +366,10 @@ pub fn notify_bind(scheme: &DriverManagerScheme, pci_addr: &str, driver_name: &s
));
if let Err(err) = write_driver_param(pci_addr, "driver", driver_name) {
if err.kind() != std::io::ErrorKind::BrokenPipe {
log::warn!("driver-manager: failed to write driver param for {pci_addr}: {err}");
}
log::warn!("driver-manager: failed to write driver param for {pci_addr}: {err}");
}
if let Err(err) = write_driver_param(pci_addr, "enabled", "true") {
if err.kind() != std::io::ErrorKind::BrokenPipe {
log::warn!("driver-manager: failed to write enabled param for {pci_addr}: {err}");
}
log::warn!("driver-manager: failed to write enabled param for {pci_addr}: {err}");
}
}
@@ -413,31 +392,21 @@ pub fn notify_unbind(scheme: &DriverManagerScheme, pci_addr: &str) {
scheme.push_event_line(event_line);
if let Err(err) = write_driver_param(pci_addr, "driver", "") {
if err.kind() != std::io::ErrorKind::BrokenPipe {
log::warn!("driver-manager: failed to clear driver param for {pci_addr}: {err}");
}
log::warn!("driver-manager: failed to clear driver param for {pci_addr}: {err}");
}
if let Err(err) = write_driver_param(pci_addr, "enabled", "false") {
if err.kind() != std::io::ErrorKind::BrokenPipe {
log::warn!("driver-manager: failed to write disabled param for {pci_addr}: {err}");
}
log::warn!("driver-manager: failed to write disabled param for {pci_addr}: {err}");
}
}
#[cfg(target_os = "redox")]
pub fn start_scheme_server(scheme: Arc<DriverManagerScheme>) -> std::result::Result<bool, String> {
pub fn start_scheme_server(scheme: Arc<DriverManagerScheme>) -> std::result::Result<(), String> {
let socket = Socket::create()
.map_err(|err| format!("driver-manager: failed to create scheme socket: {err}"))?;
let mut server = SchemeServer::new(scheme);
if let Err(err) = register_sync_scheme(&socket, SCHEME_NAME, &mut server) {
let msg = format!("{err}");
if msg.contains("File exists") {
log::warn!("driver-manager: scheme:{SCHEME_NAME} already registered (initfs instance active), returning gracefully");
return Ok(false);
}
return Err(format!("driver-manager: failed to register scheme:{SCHEME_NAME}: {err}"));
}
register_sync_scheme(&socket, SCHEME_NAME, &mut server)
.map_err(|err| format!("driver-manager: failed to register scheme:{SCHEME_NAME}: {err}"))?;
log::info!("driver-manager: registered scheme:{SCHEME_NAME}");
@@ -470,10 +439,10 @@ pub fn start_scheme_server(scheme: Arc<DriverManagerScheme>) -> std::result::Res
})
.map_err(|err| format!("driver-manager: failed to spawn scheme server thread: {err}"))?;
Ok(true)
Ok(())
}
#[cfg(not(target_os = "redox"))]
pub fn start_scheme_server(_scheme: Arc<DriverManagerScheme>) -> std::result::Result<bool, String> {
Ok(true)
pub fn start_scheme_server(_scheme: Arc<DriverManagerScheme>) -> std::result::Result<(), String> {
Ok(())
}
+64 -115
View File
@@ -4,7 +4,6 @@ pub mod acpi;
pub mod amd_vi;
pub mod command_buffer;
pub mod device_table;
pub mod intel_vtd;
pub mod interrupt;
pub mod mmio;
pub mod page_table;
@@ -13,7 +12,6 @@ use std::collections::BTreeMap;
use acpi::{parse_bdf, Bdf};
use amd_vi::AmdViUnit;
use intel_vtd::IntelVtdUnit;
use page_table::{DomainPageTables, MappingFlags};
use redox_scheme::SchemeBlockMut;
use syscall::data::Stat;
@@ -163,8 +161,7 @@ struct Handle {
}
pub struct IommuScheme {
amd_units: Vec<AmdViUnit>,
intel_units: Vec<IntelVtdUnit>,
units: Vec<AmdViUnit>,
next_id: usize,
handles: BTreeMap<usize, Handle>,
domains: BTreeMap<u16, DomainPageTables>,
@@ -173,13 +170,12 @@ pub struct IommuScheme {
impl IommuScheme {
pub fn new() -> Self {
Self::with_units(Vec::new(), Vec::new())
Self::with_units(Vec::new())
}
pub fn with_units(amd_units: Vec<AmdViUnit>, intel_units: Vec<IntelVtdUnit>) -> Self {
pub fn with_units(units: Vec<AmdViUnit>) -> Self {
Self {
amd_units,
intel_units,
units,
next_id: 0,
handles: BTreeMap::new(),
domains: BTreeMap::new(),
@@ -188,7 +184,7 @@ impl IommuScheme {
}
pub fn unit_count(&self) -> usize {
self.amd_units.len() + self.intel_units.len()
self.units.len()
}
fn insert_handle(&mut self, kind: HandleKind) -> usize {
@@ -220,67 +216,40 @@ impl IommuScheme {
}
fn ensure_unit_initialized(&mut self, unit_index: usize) -> core::result::Result<(), i32> {
if let Some(unit) = self.amd_units.get_mut(unit_index) {
if unit.initialized() {
return Ok(());
}
return unit.init().map_err(|err| {
log::error!(
"iommu: failed to initialize AMD-Vi unit {} at MMIO {:#x}: {}",
unit_index,
unit.info().mmio_base,
err
);
EIO as i32
});
let Some(unit) = self.units.get_mut(unit_index) else {
return Err(ENODEV as i32);
};
if unit.initialized() {
return Ok(());
}
let intel_index = unit_index.saturating_sub(self.amd_units.len());
if let Some(unit) = self.intel_units.get_mut(intel_index) {
if unit.initialized() {
return Ok(());
}
return unit.init().map_err(|err| {
log::error!(
"iommu: failed to initialize Intel VT-d unit {} at MMIO {:#x}: {}",
intel_index,
unit.info().mmio_base,
err
);
EIO as i32
});
}
Err(ENODEV as i32)
unit.init().map_err(|err| {
log::error!(
"iommu: failed to initialize unit {} at MMIO {:#x}: {}",
unit_index,
unit.info().mmio_base,
err
);
EIO as i32
})
}
fn root_listing(&self) -> Vec<u8> {
let mut listing = String::from("control\n");
for (index, unit) in self.amd_units.iter().enumerate() {
for (index, unit) in self.units.iter().enumerate() {
let state = if unit.initialized() {
"initialized"
} else {
"detected"
};
listing.push_str(&format!(
"unit/{index} {} mmio={:#x} state={} type=amd\n",
"unit/{index} {} mmio={:#x} state={}\n",
unit.info().iommu_bdf,
unit.info().mmio_base,
state
));
}
let intel_offset = self.amd_units.len();
for (index, unit) in self.intel_units.iter().enumerate() {
let state = if unit.initialized() {
"initialized"
} else {
"detected"
};
listing.push_str(&format!(
"unit/{} mmio={:#x} state={} type=intel\n",
intel_offset + index,
unit.info().mmio_base,
state
));
}
for domain_id in self.domains.keys() {
listing.push_str(&format!("domain/{domain_id}\n"));
}
@@ -326,31 +295,19 @@ impl IommuScheme {
requested_unit: Option<usize>,
) -> core::result::Result<usize, i32> {
if let Some(index) = requested_unit {
if let Some(unit) = self.amd_units.get(index) {
if unit.handles_device(bdf) {
return Ok(index);
}
let Some(unit) = self.units.get(index) else {
return Err(ENODEV as i32);
}
let intel_index = index.saturating_sub(self.amd_units.len());
if let Some(unit) = self.intel_units.get(intel_index) {
if unit.handles_device(bdf) {
return Ok(index);
}
};
if unit.handles_device(bdf) {
return Ok(index);
}
return Err(ENODEV as i32);
}
if let Some(index) = self.amd_units.iter().position(|unit| unit.handles_device(bdf)) {
return Ok(index);
}
let intel_offset = self.amd_units.len();
if let Some(index) = self.intel_units.iter().position(|unit| {
unit.handles_device(bdf)
}) {
return Ok(intel_offset + index);
}
Err(ENODEV as i32)
self.units
.iter()
.position(|unit| unit.handles_device(bdf))
.ok_or(ENODEV as i32)
}
fn dispatch_request(&mut self, kind: HandleKind, request: IommuRequest) -> IommuResponse {
@@ -370,11 +327,10 @@ impl IommuScheme {
match request.opcode {
opcode::QUERY => IommuResponse::success(
request.opcode,
self.unit_count() as u32,
self.units.len() as u32,
self.domains.len() as u64,
self.device_assignments.len() as u64,
self.amd_units.iter().filter(|unit| unit.initialized()).count() as u64
+ self.intel_units.iter().filter(|unit| unit.initialized()).count() as u64,
self.units.iter().filter(|unit| unit.initialized()).count() as u64,
),
opcode::INIT_UNITS => {
let requested_index = if request.arg0 == u32::MAX {
@@ -385,18 +341,17 @@ impl IommuScheme {
let mut initialized_now = 0u32;
let mut attempted = 0u64;
let total_units = self.unit_count();
for index in 0..total_units {
for index in 0..self.units.len() {
if requested_index.is_some() && requested_index != Some(index) {
continue;
}
attempted += 1;
let was_initialized = if index < self.amd_units.len() {
self.amd_units.get(index).map(|unit| unit.initialized()).unwrap_or(false)
} else {
self.intel_units.get(index - self.amd_units.len()).map(|unit| unit.initialized()).unwrap_or(false)
};
let was_initialized = self
.units
.get(index)
.map(|unit| unit.initialized())
.unwrap_or(false);
if let Err(errno) = self.ensure_unit_initialized(index) {
return IommuResponse::error(request.opcode, errno);
@@ -408,8 +363,7 @@ impl IommuScheme {
}
let initialized_total =
self.amd_units.iter().filter(|unit| unit.initialized()).count() as u64
+ self.intel_units.iter().filter(|unit| unit.initialized()).count() as u64;
self.units.iter().filter(|unit| unit.initialized()).count() as u64;
IommuResponse::success(
request.opcode,
@@ -471,7 +425,7 @@ impl IommuScheme {
let mut first_device = 0u64;
let mut first_address = 0u64;
for (index, unit) in self.amd_units.iter_mut().enumerate() {
for (index, unit) in self.units.iter_mut().enumerate() {
if requested_index.is_some() && requested_index != Some(index) {
continue;
}
@@ -623,25 +577,22 @@ impl IommuScheme {
return IommuResponse::error(request.opcode, ENOENT as i32);
};
if unit_index < self.amd_units.len() {
let Some(unit) = self.amd_units.get_mut(unit_index) else {
return IommuResponse::error(request.opcode, ENODEV as i32);
};
match unit.assign_device(bdf, domain) {
Ok(()) => {
self.device_assignments.insert(bdf, (domain_id, unit_index));
IommuResponse::success(
request.opcode,
domain_id as u32,
unit_index as u64,
u64::from(bdf.raw()),
0,
)
}
Err(_) => IommuResponse::error(request.opcode, EIO as i32),
let Some(unit) = self.units.get_mut(unit_index) else {
return IommuResponse::error(request.opcode, ENODEV as i32);
};
match unit.assign_device(bdf, domain) {
Ok(()) => {
self.device_assignments.insert(bdf, (domain_id, unit_index));
IommuResponse::success(
request.opcode,
domain_id as u32,
unit_index as u64,
u64::from(bdf.raw()),
0,
)
}
} else {
IommuResponse::error(request.opcode, ENODEV as i32)
Err(_) => IommuResponse::error(request.opcode, EIO as i32),
}
}
opcode::UNASSIGN_DEVICE => {
@@ -649,16 +600,14 @@ impl IommuScheme {
return IommuResponse::error(request.opcode, ENOENT as i32);
};
if unit_index < self.amd_units.len() {
let unit = self.amd_units.get_mut(unit_index);
if let Some(unit) = unit {
if unit.initialized() {
if let Err(err) = unit.unassign_device(bdf) {
log::error!(
"iommu: failed to invalidate DTE for {bdf} on unit {unit_index}: {err}"
);
return IommuResponse::error(request.opcode, EIO as i32);
}
let unit = self.units.get_mut(unit_index);
if let Some(unit) = unit {
if unit.initialized() {
if let Err(err) = unit.unassign_device(bdf) {
log::error!(
"iommu: failed to invalidate DTE for {bdf} on unit {unit_index}: {err}"
);
return IommuResponse::error(request.opcode, EIO as i32);
}
}
}
+26 -87
View File
@@ -9,7 +9,6 @@ use std::path::PathBuf;
use std::process;
use iommu::amd_vi::AmdViUnit;
use iommu::intel_vtd::{IntelVtdUnit, parse_dmar};
#[cfg(target_os = "redox")]
use iommu::IommuScheme;
use log::{error, info, LevelFilter, Metadata, Record};
@@ -28,8 +27,7 @@ struct StderrLogger {
#[cfg_attr(not(target_os = "redox"), allow(dead_code))]
struct DiscoveryResult {
amd_units: Vec<AmdViUnit>,
intel_units: Vec<IntelVtdUnit>,
units: Vec<AmdViUnit>,
source: DiscoverySource,
kernel_acpi_status: &'static str,
ivrs_path: Option<PathBuf>,
@@ -198,17 +196,6 @@ fn detect_dmar_from_kernel_acpi() -> Result<bool, String> {
Ok(find_kernel_acpi_table(b"DMAR")?.is_some())
}
#[cfg(target_os = "redox")]
fn detect_intel_units_from_kernel_acpi() -> Result<Vec<IntelVtdUnit>, String> {
match find_kernel_acpi_table(b"DMAR")? {
Some(table) => {
let infos = parse_dmar(&table).map_err(|err| format!("failed to parse DMAR: {err}"))?;
Ok(infos.into_iter().map(IntelVtdUnit::from_info).collect())
}
None => Ok(Vec::new()),
}
}
#[cfg(target_os = "redox")]
fn discover_units() -> Result<DiscoveryResult, String> {
let dmar_present = match detect_dmar_from_kernel_acpi() {
@@ -219,18 +206,9 @@ fn discover_units() -> Result<DiscoveryResult, String> {
}
};
let intel_units = match detect_intel_units_from_kernel_acpi() {
Ok(units) => units,
Err(err) => {
info!("iommu: Intel VT-d discovery unavailable: {err}");
Vec::new()
}
};
match detect_units_from_kernel_acpi() {
Ok(units) if !units.is_empty() => Ok(DiscoveryResult {
amd_units: units,
intel_units,
units,
source: DiscoverySource::KernelAcpi,
kernel_acpi_status: "ok",
ivrs_path: None,
@@ -244,8 +222,7 @@ fn discover_units() -> Result<DiscoveryResult, String> {
} else {
DiscoverySource::None
},
amd_units: units,
intel_units,
units,
kernel_acpi_status: "empty",
ivrs_path,
dmar_present,
@@ -260,8 +237,7 @@ fn discover_units() -> Result<DiscoveryResult, String> {
} else {
DiscoverySource::None
},
amd_units: units,
intel_units,
units,
kernel_acpi_status: "error",
ivrs_path,
dmar_present,
@@ -279,8 +255,7 @@ fn discover_units() -> Result<DiscoveryResult, String> {
} else {
DiscoverySource::None
},
amd_units: units,
intel_units: Vec::new(),
units,
kernel_acpi_status: "unsupported",
ivrs_path,
dmar_present: false,
@@ -290,9 +265,9 @@ fn discover_units() -> Result<DiscoveryResult, String> {
#[cfg(target_os = "redox")]
fn run() -> Result<(), String> {
let discovery = discover_units()?;
if discovery.amd_units.is_empty() && discovery.intel_units.is_empty() {
if discovery.units.is_empty() {
info!(
"iommu: no IOMMU units found (source={}, kernel_acpi_status={}, ivrs_path={})",
"iommu: no AMD-Vi units found (source={}, kernel_acpi_status={}, ivrs_path={})",
discovery.source.as_str(),
discovery.kernel_acpi_status,
discovery
@@ -302,35 +277,20 @@ fn run() -> Result<(), String> {
.unwrap_or_else(|| "none".to_string())
);
} else {
if !discovery.amd_units.is_empty() {
info!(
"iommu: detected {} AMD-Vi unit(s) via {}",
discovery.amd_units.len(),
discovery.source.as_str()
);
}
if !discovery.intel_units.is_empty() {
info!(
"iommu: detected {} Intel VT-d unit(s)",
discovery.intel_units.len()
);
}
}
if discovery.dmar_present && discovery.intel_units.is_empty() {
info!(
"iommu: detected kernel ACPI DMAR table but failed to parse DRHD entries"
"iommu: detected {} AMD-Vi unit(s) via {}",
discovery.units.len(),
discovery.source.as_str()
);
}
for (index, unit) in discovery.amd_units.iter().enumerate() {
if discovery.dmar_present {
info!(
"iommu: discovered AMD-Vi unit {} at MMIO {:#x}; initialization is deferred until first use",
index,
unit.info().mmio_base
"iommu: detected kernel ACPI DMAR table; Intel VT-d runtime ownership should converge here rather than remain in acpid"
);
}
for (index, unit) in discovery.intel_units.iter().enumerate() {
for (index, unit) in discovery.units.iter().enumerate() {
info!(
"iommu: discovered Intel VT-d unit {} at MMIO {:#x}; initialization is deferred until first use",
"iommu: discovered unit {} at MMIO {:#x}; initialization is deferred until first use",
index,
unit.info().mmio_base
);
@@ -340,7 +300,7 @@ fn run() -> Result<(), String> {
Socket::create("iommu").map_err(|e| format!("failed to register iommu scheme: {e}"))?;
info!("iommu: registered scheme:iommu");
let mut scheme = IommuScheme::with_units(discovery.amd_units, discovery.intel_units);
let mut scheme = IommuScheme::with_units(discovery.units);
loop {
let request = match socket.next_request(SignalBehavior::Restart) {
@@ -378,9 +338,7 @@ fn run() -> Result<(), String> {
#[cfg(target_os = "redox")]
fn run_self_test() -> Result<(), String> {
let discovery = discover_units()?;
let mut amd_units = discovery.amd_units;
let mut intel_units = discovery.intel_units;
let total_units = amd_units.len() + intel_units.len();
let mut units = discovery.units;
println!("discovery_source={}", discovery.source.as_str());
println!("kernel_acpi_status={}", discovery.kernel_acpi_status);
@@ -393,20 +351,19 @@ fn run_self_test() -> Result<(), String> {
.map(|path| path.display().to_string())
.unwrap_or_else(|| "none".to_string())
);
println!("amd_units_detected={}", amd_units.len());
println!("intel_units_detected={}", intel_units.len());
if total_units == 0 {
return Err("iommu self-test detected zero IOMMU units".to_string());
println!("units_detected={}", units.len());
if units.is_empty() {
return Err("iommu self-test detected zero AMD-Vi unit(s)".to_string());
}
let mut initialized_now = 0u32;
let mut events_drained = 0u32;
for (index, unit) in amd_units.iter_mut().enumerate() {
for (index, unit) in units.iter_mut().enumerate() {
let was_initialized = unit.initialized();
unit.init().map_err(|err| {
format!(
"iommu self-test failed to initialize AMD-Vi unit {} at MMIO {:#x}: {}",
"iommu self-test failed to initialize unit {} at MMIO {:#x}: {}",
index,
unit.info().mmio_base,
err
@@ -419,7 +376,7 @@ fn run_self_test() -> Result<(), String> {
let drained = unit.drain_events().map_err(|err| {
format!(
"iommu self-test failed to drain events for AMD-Vi unit {} at MMIO {:#x}: {}",
"iommu self-test failed to drain events for unit {} at MMIO {:#x}: {}",
index,
unit.info().mmio_base,
err
@@ -428,26 +385,9 @@ fn run_self_test() -> Result<(), String> {
events_drained = events_drained.saturating_add(drained.len() as u32);
}
for (index, unit) in intel_units.iter_mut().enumerate() {
let was_initialized = unit.initialized();
unit.init().map_err(|err| {
format!(
"iommu self-test failed to initialize Intel VT-d unit {} at MMIO {:#x}: {}",
index,
unit.info().mmio_base,
err
)
})?;
if !was_initialized {
initialized_now = initialized_now.saturating_add(1);
}
}
let initialized_after = amd_units.iter().filter(|unit| unit.initialized()).count() as u64
+ intel_units.iter().filter(|unit| unit.initialized()).count() as u64;
let initialized_after = units.iter().filter(|unit| unit.initialized()).count() as u64;
println!("units_initialized_now={}", initialized_now);
println!("units_attempted={}", total_units);
println!("units_attempted={}", units.len());
println!("units_initialized_after={}", initialized_after);
println!("events_drained={}", events_drained);
@@ -458,9 +398,8 @@ fn run_self_test() -> Result<(), String> {
fn run() -> Result<(), String> {
let discovery = discover_units()?;
info!(
"iommu: host build stub active; parsed {} AMD-Vi and {} Intel VT-d unit(s) via {}",
discovery.amd_units.len(),
discovery.intel_units.len(),
"iommu: host build stub active; parsed {} AMD-Vi unit(s) via {}",
discovery.units.len(),
discovery.source.as_str()
);
Ok(())
@@ -47,16 +47,6 @@ fn timespec_to_nanos(time: &TimeSpec) -> i128 {
i128::from(time.tv_sec) * 1_000_000_000i128 + i128::from(time.tv_nsec)
}
fn check_timer_source(name: &str, path: &str) -> &'static str {
if Path::new(path).exists() {
println!("timer_source={} path={} present=1", name, path);
"present"
} else {
println!("timer_source={} path={} present=0", name, path);
"missing"
}
}
fn run() -> Result<(), String> {
parse_args(PROGRAM, USAGE, std::env::args()).map_err(|err| {
if err.is_empty() {
@@ -67,10 +57,6 @@ fn run() -> Result<(), String> {
println!("=== Red Bear OS Timer Runtime Check ===");
check_timer_source("hpet", "/scheme/sys/hpet");
check_timer_source("pit", "/scheme/sys/pit");
check_timer_source("lapic", "/scheme/sys/lapic");
let time_path = monotonic_path()?;
let time_fd = Fd::open(&time_path, flag::O_RDWR, 0)
@@ -92,17 +78,6 @@ fn run() -> Result<(), String> {
return Err("monotonic timer did not advance".to_string());
}
let expected_ns: i128 = 50_000_000;
let deviation_ns = (delta_ns - expected_ns).abs();
println!("monotonic_expected_ns={expected_ns}");
println!("monotonic_deviation_ns={deviation_ns}");
if deviation_ns > 20_000_000 {
println!("timer_precision=coarse deviation_ns={deviation_ns} (threshold=20000000)");
} else {
println!("timer_precision=ok deviation_ns={deviation_ns} (threshold=20000000)");
}
println!("monotonic_progress=ok");
Ok(())
}
@@ -2859,76 +2859,6 @@ fn collect_health_items(runtime: &Runtime, report: &Report<'_>) -> Vec<HealthIte
},
});
let thermal_zones = runtime.read_dir_names("/scheme/acpi/thermal").unwrap_or_default();
if !thermal_zones.is_empty() {
let temps: Vec<String> = thermal_zones
.iter()
.filter_map(|zone| {
read_trimmed(runtime, &format!("/scheme/acpi/thermal/{zone}/temperature"))
})
.collect();
let avg_temp = temps.iter().filter_map(|t| t.parse::<f64>().ok()).sum::<f64>()
/ temps.len().max(1) as f64;
let state = if avg_temp > 85.0 {
HealthState::Critical
} else if avg_temp > 70.0 {
HealthState::Warning
} else {
HealthState::Healthy
};
items.push(HealthItem {
label: "Thermal",
state,
detail: format!("{} zone(s), avg {:.1}°C", thermal_zones.len(), avg_temp),
});
} else {
items.push(HealthItem {
label: "Thermal",
state: HealthState::Warning,
detail: "no thermal zones".to_string(),
});
}
let fans = runtime.read_dir_names("/scheme/acpi/fan").unwrap_or_default();
if !fans.is_empty() {
let active = fans
.iter()
.filter(|fan| {
read_trimmed(runtime, &format!("/scheme/acpi/fan/{fan}/status"))
.map(|s| s == "on")
.unwrap_or(false)
})
.count();
items.push(HealthItem {
label: "Fans",
state: HealthState::Healthy,
detail: format!("{} fan(s), {} active", fans.len(), active),
});
} else {
items.push(HealthItem {
label: "Fans",
state: HealthState::Warning,
detail: "no fan devices".to_string(),
});
}
let cstate_policy = read_trimmed(runtime, "/scheme/sys/cstate_policy");
let cstates = runtime.read_dir_names("/scheme/acpi/cstates").unwrap_or_default();
if !cstates.is_empty() {
let max_policy = cstate_policy.as_deref().unwrap_or("unlimited");
items.push(HealthItem {
label: "C-states",
state: HealthState::Healthy,
detail: format!("{} processor(s), policy={}", cstates.len(), max_policy),
});
} else {
items.push(HealthItem {
label: "C-states",
state: HealthState::Warning,
detail: "no C-state surface".to_string(),
});
}
items
}
@@ -536,7 +536,7 @@ fn monitor_loop(shared: Arc<RwLock<ThermalState>>) -> ! {
loop {
if !Path::new(ACPI_THERMAL_ROOT).exists() {
if !warned_missing_surface {
log::info!(
warn!(
"{} is unavailable; thermald will keep polling and serve an empty thermal surface",
ACPI_THERMAL_ROOT,
);
@@ -129,22 +129,13 @@ fn main() {
let scheme = Arc::new(Mutex::new(scheme));
let scheme_clone = Arc::clone(&scheme);
thread::spawn(move || {
let mut last_count = 0usize;
loop {
thread::sleep(Duration::from_secs(2));
if let Ok(mut s) = scheme_clone.lock() {
match s.scan_pci_devices() {
Ok(n) => {
if n != last_count {
if n > last_count {
info!("udev-shim: hotplug detected {} device(s) (total {})", n - last_count, n);
} else {
info!("udev-shim: device removal detected, {} device(s) remaining", n);
}
last_count = n;
}
}
Ok(n) if n > 0 => info!("udev-shim: hotplug detected {} device(s)", n),
Err(e) => error!("udev-shim: hotplug scan failed: {}", e),
_ => {}
}
}
}
@@ -2,8 +2,6 @@ use std::fs;
use std::io;
use std::os::unix::fs::symlink;
use std::path::Path;
use std::thread;
use std::time::Duration;
const DEFAULT_UDEV_RULES: &str = r#"# Network interface naming
SUBSYSTEM=="net", KERNEL=="enp*", NAME="$kernel"
@@ -76,26 +74,8 @@ pub fn write_default_rules_file() -> io::Result<&'static str> {
fs::create_dir_all(dir)?;
let path = dir.join("50-default.rules");
let contents = default_udev_rules();
if fs::metadata(&path).is_ok() {
let _ = fs::remove_file(&path);
}
for attempt in 0..3 {
match fs::write(&path, contents) {
Ok(()) => return Ok("/etc/udev/rules.d/50-default.rules"),
Err(e) if e.kind() == io::ErrorKind::BrokenPipe && attempt < 2 => {
thread::sleep(Duration::from_millis(50));
}
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
return Ok("/etc/udev/rules.d/50-default.rules");
}
Err(e) => return Err(e),
}
}
unreachable!("write_default_rules_file loop always returns or errors")
fs::write(&path, default_udev_rules())?;
Ok("/etc/udev/rules.d/50-default.rules")
}
fn parse_hex_byte(value: &str) -> Option<u8> {
+2 -2
View File
@@ -3,7 +3,7 @@ diff --git a/src/subshell/common.c b/src/subshell/common.c
+++ b/src/subshell/common.c
@@ -95,6 +95,45 @@
#endif
#endif /* HAVE_OPENPTY */
#endif
+#ifdef __redox__
+static int
@@ -64,4 +64,4 @@ diff --git a/configure b/configure
if test $ac_list_mounted_fs = found; then
gl_cv_list_mounted_fs=yes
Submodule recipes/archives/uutils-tar/source added at 5540ce1877
+4 -7
View File
@@ -6,11 +6,8 @@ template = "custom"
dependencies = [
"redoxfs",
"ion",
"driver-manager",
]
script = """
set -eo pipefail
BINS=(
init
logd
@@ -26,6 +23,7 @@ BINS=(
lived
nvmed
pcid
pcid-spawner
rtcd
vesad
)
@@ -73,8 +71,8 @@ mkdir -p "${COOKBOOK_BUILD}/initfs/lib/init.d"
cp "${COOKBOOK_SOURCE}/init.initfs.d"/* "${COOKBOOK_BUILD}/initfs/lib/init.d/"
mkdir -pv "${COOKBOOK_BUILD}/initfs/lib/drivers.d"
cp -v "${COOKBOOK_SOURCE}/drivers/initfs-storage.toml" "${COOKBOOK_BUILD}/initfs/lib/drivers.d/00-storage.toml"
mkdir -pv "${COOKBOOK_BUILD}/initfs/lib/pcid.d"
cp -v "${COOKBOOK_SOURCE}/drivers/initfs.toml" "${COOKBOOK_BUILD}/initfs/lib/pcid.d/initfs.toml"
export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
export CARGO_PROFILE_RELEASE_PANIC=abort
@@ -87,7 +85,7 @@ mkdir -pv "${COOKBOOK_BUILD}/initfs/bin" "${COOKBOOK_BUILD}/initfs/lib/drivers"
for bin in "${BINS[@]}"
do
case "${bin}" in
init | logd | ramfs | randd | zerod | fbbootlogd | fbcond | inputd | vesad | lived | ps2d | acpid | bcm2835-sdhcid | rtcd | hwd | pcid)
init | logd | ramfs | randd | zerod | pcid | pcid-spawner | fbbootlogd | fbcond | inputd | vesad | lived | ps2d | acpid | bcm2835-sdhcid | rtcd | hwd)
cp -v "target/${TARGET}/${build_type}/${bin}" "${COOKBOOK_BUILD}/initfs/bin"
;;
*)
@@ -98,7 +96,6 @@ done
cp "${COOKBOOK_SYSROOT}/usr/bin/redoxfs" "${COOKBOOK_BUILD}/initfs/bin"
cp "${COOKBOOK_SYSROOT}/usr/bin/ion" "${COOKBOOK_BUILD}/initfs/bin"
cp "${COOKBOOK_SYSROOT}/usr/bin/driver-manager" "${COOKBOOK_BUILD}/initfs/bin"
ARCH="$(echo "${GNU_TARGET}" | cut -d - -f1)"
RUSTFLAGS="$RUSTFLAGS -Ctarget-feature=+crt-static -Clink-arg=-nostartfiles -Clink-arg=-nostdlib" cargo \
+4 -2
View File
@@ -14,8 +14,10 @@ unsafe fn get_fd(var: &str) -> Option<RawFd> {
let value = match std::env::var(var) {
Ok(value) => value,
Err(_) => {
// INIT_NOTIFY is not set for oneshot_async services — this is
// expected and not an error. Silently skip readiness notification.
let exe = std::env::args()
.next()
.unwrap_or_else(|| "daemon".to_string());
eprintln!("daemon: {var} not set for {exe}; readiness notification disabled");
return None;
}
};
+6 -76
View File
@@ -9,7 +9,6 @@ patches = [
"P0-procmgr-sigchld-debug.patch",
"P0-pcid-mcfg-diagnostics.patch",
"P0-ihdgd-intel-gpu-ids.patch",
"P0-acpid-dmar-fix.patch",
# P1: acpid EC runtime and AML physmem hardening (narrow ACPI runtime patches)
"P1-acpid-ec-runtime.patch",
"P1-acpid-runtime-hardening.patch",
@@ -25,8 +24,9 @@ patches = [
"P2-misc-daemon-fixes.patch",
"P9-fix-so-pecred.patch",
"P3-inputd-keymap-bridge.patch",
# P3: ps2d consolidated — LED feedback, mouse resend, fastfail, Intellimouse2, controller init robustness, non-x86 fallback
"P7-ps2d-intellimouse2-leds-controller-init.patch",
"P3-ps2d-led-feedback.patch",
"P3-ps2d-mouse-resend.patch",
"P0-ps2d-mouse-fastfail.patch",
"P3-usbhidd-hardening.patch",
"P3-init-colored-output.patch",
"P4-logd-persistent-logging.patch",
@@ -41,78 +41,12 @@ patches = [
"P4-initfs-getty-services.patch",
"P4-initfs-dbus-services.patch",
"P4-fbcond-scrollback.patch",
"P4-ucsid-estale-graceful.patch",
"P4-acpi-estale-graceful.patch",
"P4-hwd-estale-graceful.patch",
# P5-i2c-hidd-estale-retry: REDUNDANT — ESTALE retry already provided by P2 + P4-acpi-estale
"P5-acpid-dmi-endpoint.patch",
"P4-thermal-daemon.patch",
"P4-thermald-workspace.patch",
"P6-driver-main-fixes.patch",
"P6-driver-new-modules.patch",
"P9-init-scheduler-completed.patch",
"P6-init-requires-hard-dep.patch",
"P2-pcid-acpid-graceful-fd.patch",
"P5-fbbootlogd-fbcond-graceful-drm.patch",
"P7-acpid-shared-pcifd.patch",
"P6-rtcd-no-ocreat.patch",
"P6-pcid-acpid-fd-transfer.patch",
"P15-7-init-service-timeout.patch",
# P15-8-init-cycle-detection: REDUNDANT — cycle detection already included in P6-init-requires-hard-dep
"P18-1-daemon-restart.patch",
"P18-3-msi-msix-enablement.patch",
"P18-5-acpid-robustness.patch",
"P18-8-bounded-ipcd-queues.patch",
"P18-9-msi-allocation-resilience.patch",
"P19-init-startup-hardening.patch",
"P19-acpid-startup-hardening.patch",
"P20-ramfs-requires-randd.patch",
"P21-boot-daemon-graceful-panic.patch",
"P23-rootfs-hard-dep-on-drivers.patch",
"P24-acpi-s5-derivation-shutdown-semantics.patch",
"P25-fbcond-vesa-fallback.patch",
"P26-driver-manager-initfs-conversion.patch",
"P27-fbcond-borrow-fix.patch",
"P28-init-skip-unmet-conditions.patch",
"P30-acpid-graceful-scheme-exists.patch",
"P31-xhcid-restore-interrupts.patch",
"P32-acpid-graceful-boot.patch",
"P33-vesad-graceful-boot.patch",
"P34-fbcond-fbbootlogd-env.patch",
"P35-fbcond-fbbootlogd-init.patch",
"P36-graphics-scheme-graceful-init.patch",
"P37-smolnetd-ready-after-init.patch",
"P38-vesad-eventqueue-deadlock.patch",
"P39-pci-allocate-interrupt-vector-graceful.patch",
"P40-bar-rs-graceful.patch",
"P41-common-init-graceful.patch",
"P42-inputd-graceful-fallback.patch",
"P43-dhcpd-requires-hard-dep.patch",
"P44-acpid-thermal-zones.patch",
# P54: Add missing thermal.rs module for P44
"P54-acpid-thermal-module.patch",
# P45: Migrate e1000d and ixgbed to MSI-X via pci_allocate_interrupt_vector
"P45-net-msix-adoption.patch",
# P46: Migrate ahcid and ac97d to MSI-X via pci_allocate_interrupt_vector
"P46-storage-audio-msix.patch",
# P46b: Fix ac97d mutable borrow of pcid_handle (required by pci_allocate_interrupt_vector)
"P46b-ac97d-mutable-fix.patch",
# P47: Update thermald to read from P44 thermal zones and coretempd
"P47-thermald-backend.patch",
# P48: Add ACPI fan device discovery and status exposure
"P48-acpid-fan-support.patch",
# P49: Add IRQ affinity logging and CPU tracking to pcid
"P49-irq-affinity-logging.patch",
# P50: Add structured logging rate limiter and thermald integration
"P50-structured-logging.patch",
# P51: Add per-service log files and size-based rotation to logd
"P51-logd-rotation.patch",
# P52: Add ACPI C-state discovery and thermal-based C-state policy
"P52-acpid-cstates.patch",
# P53: Add e1000d interrupt throttling rate (ITR) coalescing
"P53-e1000d-itr-coalescing.patch",
# P55: Add JSON structured log format option to logd
"P55-logd-json-format.patch",
]
[package]
@@ -145,16 +79,13 @@ installs = [
"/usr/bin/smolnetd",
"/usr/bin/ucsid",
"/usr/lib/drivers/ac97d",
"/usr/lib/drivers/ahcid",
"/usr/lib/drivers/amd-mp2-i2cd",
"/usr/lib/drivers/e1000d",
"/usr/lib/drivers/ihdad",
"/usr/lib/drivers/ihdgd",
"/usr/lib/drivers/ided",
"/usr/lib/drivers/intel-lpss-i2cd",
"/usr/lib/drivers/intel-thc-hidd",
"/usr/lib/drivers/ixgbed",
"/usr/lib/drivers/ps2d",
"/usr/lib/drivers/rtl8139d",
"/usr/lib/drivers/rtl8168d",
"/usr/lib/drivers/sb16d",
@@ -229,8 +160,6 @@ BINS=(
ixgbed
pcid
pcid-spawner
acpid
redoxerd
rtl8139d
rtl8168d
usbctl
@@ -244,13 +173,14 @@ BINS=(
xhcid
i2cd
inputd
redoxerd
)
# Add additional drivers to the list to build, that are not in drivers-initfs
# depending on the target architecture
case "${TARGET}" in
i586-unknown-redox | i686-unknown-redox | x86_64-unknown-redox)
BINS+=(ac97d ahcid ided nvmed ps2d sb16d vboxd)
BINS+=(ac97d sb16d vboxd)
;;
*)
;;
@@ -274,7 +204,7 @@ done
$(for bin in "${EXISTING_BINS[@]}"; do echo "-p" "${bin}"; done)
for bin in "${EXISTING_BINS[@]}"
do
if [[ "${bin}" == "gpiod" || "${bin}" == "i2c-gpio-expanderd" || "${bin}" == "intel-gpiod" || "${bin}" == "i2cd" || "${bin}" == "dw-acpi-i2cd" || "${bin}" == "acpid" || "${bin}" == "thermald" || "${bin}" == "i2c-hidd" || "${bin}" == "inputd" || "${bin}" == "pcid" || "${bin}" == "pcid-spawner" || "${bin}" == "redoxerd" || "${bin}" == "ucsid" ]]; then
if [[ "${bin}" == "gpiod" || "${bin}" == "i2c-gpio-expanderd" || "${bin}" == "intel-gpiod" || "${bin}" == "i2cd" || "${bin}" == "dw-acpi-i2cd" || "${bin}" == "i2c-hidd" || "${bin}" == "inputd" || "${bin}" == "pcid" || "${bin}" == "pcid-spawner" || "${bin}" == "redoxerd" || "${bin}" == "ucsid" ]]; then
cp -v "target/${TARGET}/${build_type}/${bin}" "${COOKBOOK_STAGE}/usr/bin"
else
cp -v "target/${TARGET}/${build_type}/${bin}" "${COOKBOOK_STAGE}/usr/lib/drivers"
-35
View File
@@ -12,41 +12,6 @@ patches = [
"../../../local/patches/kernel/P1-ioapic-hpet-nmi-v2.patch",
"../../../local/patches/kernel/P9-numa-topology.patch",
"../../../local/patches/kernel/P9-proc-lock-ordering.patch",
"../../../local/patches/kernel/P9-percpu-context-switch.patch",
"../../../local/patches/kernel/P9-broadcast-tlb-shootdown.patch",
"../../../local/patches/kernel/P9-ioapic-irq-affinity.patch",
"../../../local/patches/kernel/P10-irq-affinity-wiring.patch",
"../../../local/patches/kernel/P11-mcs-lock.patch",
"../../../local/patches/kernel/P12-range-tlb-flush.patch",
"../../../local/patches/kernel/P13-priority-inheritance.patch",
"../../../local/patches/kernel/P14-numa-topology.patch",
"../../../local/patches/kernel/P15-1-ap-cpu-id-race.patch",
"../../../local/patches/kernel/P15-4-mcs-pi-ordering.patch",
"../../../local/patches/kernel/P15-10-tlb-range-ordering.patch",
"../../../local/patches/kernel/P16-3-max-cpu-256.patch",
"../../../local/patches/kernel/P16-1-sipi-timing.patch",
"../../../local/patches/kernel/P16-4a-sdt-checksum.patch",
"../../../local/patches/kernel/P16-4b-madt-validation.patch",
"../../../local/patches/kernel/P17-2a-percpu-waiting.patch",
"../../../local/patches/kernel/P17-2b-transitive-pi.patch",
"../../../local/patches/kernel/P17-4-configurable-preempt.patch",
"../../../local/patches/kernel/P17-1-numa-selection.patch",
"../../../local/patches/kernel/P17-3-sched-affinity.patch",
"../../../local/patches/kernel/P17-3-syscall-dispatch.patch",
"../../../local/patches/kernel/P19-2-irq-debug.patch",
# P20: x2APIC ICR mode fix (32-bit dest field for x2APIC, 8-bit for xAPIC)
"../../../local/patches/kernel/P20-x2apic-icr-mode-fix.patch",
# P21: x2APIC SMP bring-up fix — skip 8-bit LocalApic entries when x2APIC
# is active (BSP ID mismatch causes all APs to be skipped on bare metal Intel)
"../../../local/patches/kernel/P21-x2apic-smp-fix.patch",
# P22: x2APIC MADT fallback — when x2APIC is active but MADT has no
# LocalX2Apic entries (QEMU, some BIOS), fall back to processing LocalApic
# entries with zero-extended IDs using x2APIC 64-bit ICR format
"../../../local/patches/kernel/P22-x2apic-madt-fallback.patch",
# P23: sys:msr scheme — kernel MSR read/write via /scheme/sys/msr/<cpu>/<msr>
"../../../local/patches/kernel/P23-sys-msr-scheme.patch",
# P25: Comprehensive cpuidle framework with deep C-states (C1-C7)
"../../../local/patches/kernel/P25-cpuidle-deep-cstates.patch",
]
[build]
+3
View File
@@ -12,7 +12,10 @@ cc = "1.0"
toml = "0.8"
[dependencies]
<<<<<<< HEAD
acpi_ext = { package = "acpi", git = "https://gitlab.redox-os.org/redox-os/acpi.git", branch = "redox-6.x" }
=======
>>>>>>> master
arrayvec = { version = "0.7.4", default-features = false }
bitfield = "0.13.2"
bitflags = "2"
+3
View File
@@ -1,4 +1,7 @@
<<<<<<< HEAD
# Red Bear OS kernel patches applied via individual patch files
=======
>>>>>>> master
.PHONY: all check
SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
+6
View File
@@ -77,7 +77,10 @@ fn main() {
}
"x86_64" => {
println!("cargo::rerun-if-changed=src/asm/x86_64/trampoline.asm");
<<<<<<< HEAD
println!("cargo::rerun-if-changed=src/asm/x86_64/s3_wakeup.asm");
=======
>>>>>>> master
let status = Command::new("nasm")
.arg("-f")
@@ -90,6 +93,7 @@ fn main() {
if !status.success() {
panic!("nasm failed with exit status {}", status);
}
<<<<<<< HEAD
let status = Command::new("nasm")
.arg("-f")
@@ -102,6 +106,8 @@ fn main() {
if !status.success() {
panic!("nasm failed with exit status {}", status);
}
=======
>>>>>>> master
}
"riscv64" => {
println!("cargo::rustc-cfg=dtb");
@@ -3,8 +3,11 @@ use core::{
sync::atomic::{AtomicU8, Ordering},
};
<<<<<<< HEAD
use x86::time::rdtsc;
=======
>>>>>>> master
use crate::{
arch::{
device::local_apic::the_local_apic,
@@ -12,14 +15,20 @@ use crate::{
},
cpu_set::LogicalCpuId,
memory::{
<<<<<<< HEAD
allocate_p2frame, map_device_memory, Frame, KernelMapper, Page, PageFlags,
PhysicalAddress, RmmA, RmmArch, VirtualAddress, PAGE_SIZE,
=======
allocate_p2frame, Frame, KernelMapper, Page, PageFlags, PhysicalAddress, RmmA, RmmArch,
VirtualAddress, PAGE_SIZE,
>>>>>>> master
},
startup::AP_READY,
};
use super::{Madt, MadtEntry};
<<<<<<< HEAD
use alloc::collections::BTreeSet;
use alloc::vec::Vec;
@@ -137,6 +146,11 @@ fn apply_lapic_address_override(
debug!("Applied LAPIC address override: {:#x}", address);
}
=======
const TRAMPOLINE: usize = 0x8000;
static TRAMPOLINE_DATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/trampoline"));
>>>>>>> master
pub(super) fn init(madt: Madt) {
let local_apic = unsafe { the_local_apic() };
let me = local_apic.id();
@@ -148,10 +162,13 @@ pub(super) fn init(madt: Madt) {
}
if cfg!(not(feature = "multi_core")) {
<<<<<<< HEAD
unsafe {
record_apic_mapping(me.get(), LogicalCpuId::new(0));
}
crate::numa::init_default();
=======
>>>>>>> master
return;
}
@@ -162,6 +179,7 @@ pub(super) fn init(madt: Madt) {
//TODO: do not have writable and executable!
let mut mapper = KernelMapper::lock_rw();
<<<<<<< HEAD
let result = match mapper.map_phys(
trampoline_page.start_address(),
trampoline_frame.base(),
@@ -173,6 +191,15 @@ pub(super) fn init(madt: Madt) {
return;
}
};
=======
let result = mapper
.map_phys(
trampoline_page.start_address(),
trampoline_frame.base(),
PageFlags::new().execute(true).write(true),
)
.expect("failed to map trampoline");
>>>>>>> master
(result, mapper.table().phys().data())
};
@@ -185,6 +212,7 @@ pub(super) fn init(madt: Madt) {
}
}
<<<<<<< HEAD
// Detect whether MADT contains any LocalX2Apic entries.
// Some firmware (notably QEMU and some older BIOS) provides only 8-bit
// LocalApic entries even when the CPU supports x2APIC. In that case we must
@@ -427,6 +455,29 @@ pub(super) fn init(madt: Madt) {
);
continue;
}
=======
unsafe {
let preliminary_cpu_count = madt.iter().filter(|e| matches!(e, MadtEntry::LocalApic(entry) if u32::from(entry.id) == me.get() || entry.flags & 1 == 1)).count();
crate::profiling::allocate(preliminary_cpu_count as u32);
}
for madt_entry in madt.iter() {
debug!(" {:x?}", madt_entry);
if let MadtEntry::LocalApic(ap_local_apic) = madt_entry {
if u32::from(ap_local_apic.id) == me.get() {
debug!(" This is my local APIC");
} else if ap_local_apic.flags & 1 == 1 {
let cpu_id = LogicalCpuId::next();
// Allocate a stack
let stack_start = RmmA::phys_to_virt(
allocate_p2frame(4)
.expect("no more frames in acpi stack_start")
.base(),
)
.data();
let stack_end = stack_start + (PAGE_SIZE << 4);
>>>>>>> master
let pcr_ptr = crate::arch::gdt::allocate_and_init_pcr(cpu_id, stack_end);
@@ -452,6 +503,7 @@ pub(super) fn init(madt: Madt) {
#[expect(clippy::fn_to_numeric_cast)]
ap_code.write(kstart_ap as u64);
<<<<<<< HEAD
// Ensure all trampoline writes are visible to the AP before
// it starts executing. asm!("") is only a compiler barrier;
// fence(SeqCst) is a full hardware memory barrier.
@@ -467,6 +519,16 @@ pub(super) fn init(madt: Madt) {
{
// ICR: Delivery Mode=INIT(101), Level=Assert, Trigger=Edge
let mut icr = 0x4500u64;
=======
// TODO: Is this necessary (this fence)?
core::arch::asm!("");
};
AP_READY.store(false, Ordering::SeqCst);
// Send INIT IPI
{
let mut icr = 0x4500;
>>>>>>> master
if local_apic.x2 {
icr |= u64::from(ap_local_apic.id) << 32;
} else {
@@ -475,6 +537,7 @@ pub(super) fn init(madt: Madt) {
local_apic.set_icr(icr);
}
<<<<<<< HEAD
// Intel SDM Vol 3A §8.4.4: wait 10ms after INIT deassert
// before sending first SIPI. Modern CPUs may need less,
// but 10ms is the safe specification-compliant value.
@@ -486,11 +549,19 @@ pub(super) fn init(madt: Madt) {
// ICR: Delivery Mode=StartUp(110), Vector=ap_segment
// Note: bit 14 (Level) must be 0 for SIPI per Intel SDM.
let mut icr = 0x0600 | ap_segment as u64;
=======
// Send START IPI
{
let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
let mut icr = 0x4600 | ap_segment as u64;
>>>>>>> master
if local_apic.x2 {
icr |= u64::from(ap_local_apic.id) << 32;
} else {
icr |= u64::from(ap_local_apic.id) << 56;
}
<<<<<<< HEAD
local_apic.set_icr(icr);
}
@@ -757,4 +828,30 @@ pub(super) fn init(madt: Madt) {
} else {
println!("KERNEL AP: failed to unmap trampoline page (non-fatal)");
}
=======
local_apic.set_icr(icr);
}
// Wait for trampoline ready
while unsafe { (*ap_ready.cast::<AtomicU8>()).load(Ordering::SeqCst) } == 0 {
hint::spin_loop();
}
while !AP_READY.load(Ordering::SeqCst) {
hint::spin_loop();
}
RmmA::invalidate_all();
}
}
}
// Unmap trampoline
let (_frame, _, flush) = unsafe {
KernelMapper::lock_rw()
.unmap_phys(trampoline_page.start_address())
.expect("failed to unmap trampoline page")
};
flush.flush();
>>>>>>> master
}
@@ -34,12 +34,15 @@ impl Madt {
let madt = Madt::new(find_one_sdt!("APIC"));
if let Some(madt) = madt {
<<<<<<< HEAD
// Validate MADT checksum per ACPI 6.5 §5.2.2
if !madt.sdt.validate_checksum() {
error!("MADT checksum validation failed, skipping APIC initialization");
return;
}
=======
>>>>>>> master
// safe because no APs have been started yet.
unsafe { MADT.get().write(Some(madt)) };
@@ -152,6 +155,7 @@ pub struct MadtGicd {
_reserved2: [u8; 3],
}
<<<<<<< HEAD
/// MADT Local x2APIC (entry type 0x9)
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
@@ -194,6 +198,8 @@ const _: () = assert!(size_of::<MadtLocalApicNmi>() == 4);
const _: () = assert!(size_of::<MadtLapicAddressOverride>() == 10);
const _: () = assert!(size_of::<MadtLocalX2ApicNmi>() == 10);
=======
>>>>>>> master
/// MADT Entries
#[derive(Debug)]
#[allow(dead_code)]
@@ -204,18 +210,24 @@ pub enum MadtEntry {
InvalidIoApic(usize),
IntSrcOverride(&'static MadtIntSrcOverride),
InvalidIntSrcOverride(usize),
<<<<<<< HEAD
LocalApicNmi(&'static MadtLocalApicNmi),
InvalidLocalApicNmi(usize),
LapicAddressOverride(&'static MadtLapicAddressOverride),
InvalidLapicAddressOverride(usize),
=======
>>>>>>> master
Gicc(&'static MadtGicc),
InvalidGicc(usize),
Gicd(&'static MadtGicd),
InvalidGicd(usize),
<<<<<<< HEAD
LocalX2Apic(&'static MadtLocalX2Apic),
InvalidLocalX2Apic(usize),
LocalX2ApicNmi(&'static MadtLocalX2ApicNmi),
InvalidLocalX2ApicNmi(usize),
=======
>>>>>>> master
Unknown(u8),
}
@@ -232,10 +244,13 @@ impl Iterator for MadtIter {
let entry_len =
unsafe { *(self.sdt.data_address() as *const u8).add(self.i + 1) } as usize;
<<<<<<< HEAD
if entry_len < 2 {
return None;
}
=======
>>>>>>> master
if self.i + entry_len <= self.sdt.data_len() {
let item = match entry_type {
0x0 => {
@@ -266,6 +281,7 @@ impl Iterator for MadtIter {
MadtEntry::InvalidIntSrcOverride(entry_len)
}
}
<<<<<<< HEAD
0x4 => {
if entry_len == size_of::<MadtLocalApicNmi>() + 2 {
MadtEntry::LocalApicNmi(unsafe {
@@ -306,6 +322,8 @@ impl Iterator for MadtIter {
MadtEntry::InvalidLocalX2ApicNmi(entry_len)
}
}
=======
>>>>>>> master
0xB => {
if entry_len >= size_of::<MadtGicc>() + 2 {
MadtEntry::Gicc(unsafe {
@@ -20,8 +20,11 @@ mod rxsdt;
pub mod sdt;
#[cfg(target_arch = "aarch64")]
mod spcr;
<<<<<<< HEAD
pub mod slit;
pub mod srat;
=======
>>>>>>> master
mod xsdt;
unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::memory::PageMapper) {
@@ -84,6 +87,7 @@ impl Rxsdt for RxsdtEnum {
pub static RXSDT_ENUM: Once<RxsdtEnum> = Once::new();
<<<<<<< HEAD
#[derive(Clone, Copy, Debug)]
pub struct AcpiRootInfo {
pub revision: u8,
@@ -92,6 +96,8 @@ pub struct AcpiRootInfo {
pub static ACPI_ROOT_INFO: Once<AcpiRootInfo> = Once::new();
=======
>>>>>>> master
/// Parse the ACPI tables to gather CPU, interrupt, and timer information
pub unsafe fn init(already_supplied_rsdp: Option<*const u8>) {
unsafe {
@@ -104,6 +110,7 @@ pub unsafe fn init(already_supplied_rsdp: Option<*const u8>) {
let rsdp_opt = Rsdp::get_rsdp(already_supplied_rsdp);
if let Some(rsdp) = rsdp_opt {
<<<<<<< HEAD
let root_info = ACPI_ROOT_INFO.call_once(|| AcpiRootInfo {
revision: rsdp.revision(),
root_sdt_address: rsdp.sdt_address(),
@@ -113,6 +120,8 @@ pub unsafe fn init(already_supplied_rsdp: Option<*const u8>) {
error!("ACPI_ROOT_INFO already initialized with a different RSDP root");
}
=======
>>>>>>> master
debug!("SDT address: {:#x}", rsdp.sdt_address().data());
let rxsdt = get_sdt(rsdp.sdt_address(), &mut KernelMapper::lock_rw());
@@ -165,6 +174,7 @@ pub unsafe fn init(already_supplied_rsdp: Option<*const u8>) {
// TODO: Enumerate processors in userspace, and then provide an ACPI-independent interface
// to initialize enumerated processors to userspace?
<<<<<<< HEAD
// Parse SRAT BEFORE MADT so NUMA node mapping is available
// when APs are started and PercpuBlocks are created.
srat::init();
@@ -173,6 +183,9 @@ pub unsafe fn init(already_supplied_rsdp: Option<*const u8>) {
// Parse SLIT after MADT for the NUMA distance matrix.
slit::init();
=======
Madt::init();
>>>>>>> master
//TODO: support this on any arch
// SPCR must be initialized after MADT for interrupt controllers
#[cfg(target_arch = "aarch64")]
@@ -17,6 +17,7 @@ pub struct Rsdp {
impl Rsdp {
pub unsafe fn get_rsdp(already_supplied_rsdp: Option<*const u8>) -> Option<Rsdp> {
<<<<<<< HEAD
already_supplied_rsdp.and_then(|rsdp_ptr| {
let rsdp = unsafe { *(rsdp_ptr as *const Rsdp) };
@@ -44,6 +45,11 @@ impl Rsdp {
}
Some(rsdp)
=======
already_supplied_rsdp.map(|rsdp_ptr| {
// TODO: Validate
unsafe { *(rsdp_ptr as *const Rsdp) }
>>>>>>> master
})
}
@@ -55,8 +61,11 @@ impl Rsdp {
self.rsdt_address as usize
})
}
<<<<<<< HEAD
pub fn revision(&self) -> u8 {
self.revision
}
=======
>>>>>>> master
}
@@ -24,6 +24,7 @@ impl Sdt {
let header_size = size_of::<Sdt>();
total_size.saturating_sub(header_size)
}
<<<<<<< HEAD
/// Validate the SDT checksum.
///
@@ -40,4 +41,6 @@ impl Sdt {
.fold(0u8, |acc, &b| acc.wrapping_add(b));
sum == 0
}
=======
>>>>>>> master
}
@@ -7,6 +7,7 @@ mod linked_list;
/// Size of kernel heap
const KERNEL_HEAP_SIZE: usize = ::rmm::MEGABYTE;
<<<<<<< HEAD
#[cold]
fn halt_kernel_heap_init(message: &str) -> ! {
print!("{message}");
@@ -16,12 +17,15 @@ fn halt_kernel_heap_init(message: &str) -> ! {
}
}
=======
>>>>>>> master
unsafe fn map_heap(mapper: &mut KernelMapper<true>, offset: usize, size: usize) {
let mut flush_all = PageFlushAll::new();
let heap_start_page = Page::containing_address(VirtualAddress::new(offset));
let heap_end_page = Page::containing_address(VirtualAddress::new(offset + size - 1));
for page in Page::range_inclusive(heap_start_page, heap_end_page) {
<<<<<<< HEAD
let phys = match mapper.allocator_mut().allocate_one() {
Some(phys) => phys,
None => halt_kernel_heap_init(
@@ -41,6 +45,22 @@ unsafe fn map_heap(mapper: &mut KernelMapper<true>, offset: usize, size: usize)
"FATAL: failed to map kernel heap virtual page\n",
),
}
=======
let phys = mapper
.allocator_mut()
.allocate_one()
.expect("failed to allocate kernel heap");
let flush = unsafe {
mapper
.map_phys(
page.start_address(),
phys,
PageFlags::new()
.write(true)
.global(cfg!(not(feature = "pti"))),
)
.expect("failed to map kernel heap")
>>>>>>> master
};
flush_all.consume(flush);
}
@@ -91,7 +91,11 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
dtb::serial::init_early(dtb);
}
<<<<<<< HEAD
info!("RedBear OS starting...");
=======
info!("Redox OS starting...");
>>>>>>> master
args.print();
// Initialize RMM
@@ -97,7 +97,11 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
init_early(dtb);
}
<<<<<<< HEAD
info!("RedBear OS starting...");
=======
info!("Redox OS starting...");
>>>>>>> master
args.print();
if let Some(dtb) = &dtb {
@@ -14,10 +14,13 @@ pub struct IoApicRegs {
pointer: *const u32,
}
impl IoApicRegs {
<<<<<<< HEAD
fn redirection_index_valid(&mut self, idx: u8) -> bool {
idx <= self.max_redirection_table_entries()
}
=======
>>>>>>> master
fn ioregsel(&self) -> *const u32 {
self.pointer
}
@@ -48,6 +51,7 @@ impl IoApicRegs {
pub fn read_ioapicver(&mut self) -> u32 {
self.read_reg(0x01)
}
<<<<<<< HEAD
pub fn read_ioredtbl(&mut self, idx: u8) -> Option<u64> {
if !self.redirection_index_valid(idx) {
warn!("IOAPIC read_ioredtbl index {} out of range", idx);
@@ -63,13 +67,27 @@ impl IoApicRegs {
warn!("IOAPIC write_ioredtbl index {} out of range", idx);
return false;
}
=======
pub fn read_ioredtbl(&mut self, idx: u8) -> u64 {
assert!(idx < 24);
let lo = self.read_reg(0x10 + idx * 2);
let hi = self.read_reg(0x10 + idx * 2 + 1);
u64::from(lo) | (u64::from(hi) << 32)
}
pub fn write_ioredtbl(&mut self, idx: u8, value: u64) {
assert!(idx < 24);
>>>>>>> master
let lo = value as u32;
let hi = (value >> 32) as u32;
self.write_reg(0x10 + idx * 2, lo);
self.write_reg(0x10 + idx * 2 + 1, hi);
<<<<<<< HEAD
true
=======
>>>>>>> master
}
pub fn max_redirection_table_entries(&mut self) -> u8 {
@@ -103,16 +121,22 @@ impl IoApic {
}
/// Map an interrupt vector to a physical local APIC ID of a processor (thus physical mode).
#[allow(dead_code)]
<<<<<<< HEAD
pub fn map(&self, idx: u8, info: MapInfo) -> bool {
let Some(raw) = info.as_raw() else {
return false;
};
self.regs.lock().write_ioredtbl(idx, raw)
=======
pub fn map(&self, idx: u8, info: MapInfo) {
self.regs.lock().write_ioredtbl(idx, info.as_raw())
>>>>>>> master
}
pub fn set_mask(&self, gsi: u32, mask: bool) {
let idx = (gsi - self.gsi_start) as u8;
let mut guard = self.regs.lock();
<<<<<<< HEAD
let Some(mut reg) = guard.read_ioredtbl(idx) else {
return;
};
@@ -134,6 +158,12 @@ impl IoApic {
entry &= !(0xFF_u64 << 56);
entry |= u64::from(dest.get()) << 56;
guard.write_ioredtbl(idx, entry)
=======
let mut reg = guard.read_ioredtbl(idx);
reg &= !(1 << 16);
reg |= u64::from(mask) << 16;
guard.write_ioredtbl(idx, reg);
>>>>>>> master
}
}
@@ -180,6 +210,7 @@ pub struct MapInfo {
}
impl MapInfo {
<<<<<<< HEAD
pub fn as_raw(&self) -> Option<u64> {
if !(0x20..=0xFE).contains(&self.vector) {
warn!(
@@ -193,13 +224,26 @@ impl MapInfo {
Some(
(u64::from(self.dest.get()) << 56)
=======
pub fn as_raw(&self) -> u64 {
assert!(self.vector >= 0x20);
assert!(self.vector <= 0xFE);
// TODO: Check for reserved fields.
(u64::from(self.dest.get()) << 56)
>>>>>>> master
| (u64::from(self.mask) << 16)
| ((self.trigger_mode as u64) << 15)
| ((self.polarity as u64) << 13)
| ((self.dest_mode as u64) << 11)
| ((self.delivery_mode as u64) << 8)
<<<<<<< HEAD
| u64::from(self.vector),
)
=======
| u64::from(self.vector)
>>>>>>> master
}
}
@@ -213,7 +257,11 @@ impl fmt::Debug for IoApic {
let count = guard.max_redirection_table_entries();
f.debug_list()
<<<<<<< HEAD
.entries((0..=count).filter_map(|i| guard.read_ioredtbl(i)))
=======
.entries((0..count).map(|i| guard.read_ioredtbl(i)))
>>>>>>> master
.finish()
}
}
@@ -275,6 +323,7 @@ pub unsafe fn handle_ioapic(madt_ioapic: &'static MadtIoApic) {
let ioapic_registers = virt.data() as *const u32;
let ioapic = IoApic::new(ioapic_registers, madt_ioapic.gsi_base);
<<<<<<< HEAD
let detected_id = ioapic.regs.lock().id();
if detected_id != madt_ioapic.id {
warn!(
@@ -283,6 +332,13 @@ pub unsafe fn handle_ioapic(madt_ioapic: &'static MadtIoApic) {
detected_id
);
}
=======
assert_eq!(
ioapic.regs.lock().id(),
madt_ioapic.id,
"mismatched ACPI MADT I/O APIC ID, and the ID reported by the I/O APIC"
);
>>>>>>> master
(*IOAPICS.get()).get_or_insert_with(Vec::new).push(ioapic);
}
@@ -351,11 +407,19 @@ pub unsafe fn init() {
}
}
}
<<<<<<< HEAD
for ioapic in ioapics() {
for idx in 0..=ioapic.count {
ioapic.set_mask(ioapic.gsi_start + u32::from(idx), true);
}
}
=======
println!(
"I/O APICs: {:?}, overrides: {:?}",
ioapics(),
src_overrides()
);
>>>>>>> master
// map the legacy PC-compatible IRQs (0-15) to 32-47, just like we did with 8259 PIC (if it
// wouldn't have been disabled due to this I/O APIC)
@@ -370,6 +434,10 @@ pub unsafe fn init() {
.iter()
.any(|over| over.bus_irq == legacy_irq)
{
<<<<<<< HEAD
=======
// there's an IRQ conflict, making this legacy IRQ inaccessible.
>>>>>>> master
continue;
}
(
@@ -389,6 +457,10 @@ pub unsafe fn init() {
let redir_tbl_index = (gsi - apic.gsi_start) as u8;
let map_info = MapInfo {
<<<<<<< HEAD
=======
// only send to the BSP
>>>>>>> master
dest: bsp_apic_id,
dest_mode: DestinationMode::Physical,
delivery_mode: DeliveryMode::Fixed,
@@ -405,6 +477,7 @@ pub unsafe fn init() {
},
vector: 32 + legacy_irq,
};
<<<<<<< HEAD
if !apic.map(redir_tbl_index, map_info) {
warn!(
"Unable to map legacy IRQ {} (GSI {}) through IOAPIC index {}",
@@ -431,6 +504,9 @@ pub unsafe fn init() {
);
}
}
=======
apic.map(redir_tbl_index, map_info);
>>>>>>> master
}
println!(
"I/O APICs: {:?}, overrides: {:?}",
@@ -470,7 +546,11 @@ fn resolve(irq: u8) -> u32 {
fn find_ioapic(gsi: u32) -> Option<&'static IoApic> {
ioapics()
.iter()
<<<<<<< HEAD
.find(|apic| gsi >= apic.gsi_start && gsi <= apic.gsi_start + u32::from(apic.count))
=======
.find(|apic| gsi >= apic.gsi_start && gsi < apic.gsi_start + u32::from(apic.count))
>>>>>>> master
}
pub unsafe fn mask(irq: u8) {
@@ -489,6 +569,7 @@ pub unsafe fn unmask(irq: u8) {
};
apic.set_mask(gsi, false);
}
<<<<<<< HEAD
/// Change the destination CPU for an IRQ by reprogramming the IOAPIC redirection entry.
/// Resolves the legacy IRQ to its GSI, finds the owning IOAPIC, and updates the destination
@@ -500,3 +581,5 @@ pub unsafe fn set_affinity(irq: u8, dest: ApicId) -> bool {
None => false,
}
}
=======
>>>>>>> master
@@ -59,10 +59,17 @@ impl LocalApic {
.is_some_and(|feature_info| feature_info.has_x2apic());
if !self.x2 {
<<<<<<< HEAD
info!("Detected xAPIC at {:#x}", physaddr.data());
self.address = map_device_memory(physaddr, 4096).data();
} else {
info!("Detected x2APIC");
=======
debug!("Detected xAPIC at {:#x}", physaddr.data());
self.address = map_device_memory(physaddr, 4096).data();
} else {
debug!("Detected x2APIC");
>>>>>>> master
}
self.init_ap();
@@ -103,7 +110,11 @@ impl LocalApic {
ApicId::new(if self.x2 {
unsafe { rdmsr(IA32_X2APIC_APICID) as u32 }
} else {
<<<<<<< HEAD
unsafe { self.read(0x20) >> 24 }
=======
unsafe { self.read(0x20) }
>>>>>>> master
})
}
@@ -126,6 +137,7 @@ impl LocalApic {
pub fn set_icr(&mut self, value: u64) {
if self.x2 {
unsafe {
<<<<<<< HEAD
const PENDING: u32 = 1 << 12;
while (rdmsr(IA32_X2APIC_ICR) as u32) & PENDING == PENDING {
core::hint::spin_loop();
@@ -134,6 +146,9 @@ impl LocalApic {
while (rdmsr(IA32_X2APIC_ICR) as u32) & PENDING == PENDING {
core::hint::spin_loop();
}
=======
wrmsr(IA32_X2APIC_ICR, value);
>>>>>>> master
}
} else {
unsafe {
@@ -263,6 +278,7 @@ impl LocalApic {
}
}
}
<<<<<<< HEAD
pub unsafe fn set_lvt_nmi(&mut self, pin: u8, flags: u16) {
let polarity = match flags & 0b11 {
@@ -296,6 +312,8 @@ impl LocalApic {
}
}
=======
>>>>>>> master
unsafe fn setup_error_int(&mut self) {
unsafe {
let vector = 49u32;
@@ -4,11 +4,17 @@ pub mod cpu;
pub mod hpet;
pub mod ioapic;
pub mod local_apic;
<<<<<<< HEAD
pub mod msi;
pub mod pic;
pub mod pit;
pub mod serial;
pub mod vector;
=======
pub mod pic;
pub mod pit;
pub mod serial;
>>>>>>> master
#[cfg(feature = "system76_ec_debug")]
pub mod system76_ec;
@@ -25,7 +31,12 @@ pub unsafe fn init() {
}
}
pub unsafe fn init_after_acpi() {
<<<<<<< HEAD
unsafe { ioapic::init() };
=======
// this will disable the IOAPIC if needed.
//ioapic::init(mapper);
>>>>>>> master
}
unsafe fn init_hpet() -> bool {
@@ -192,6 +192,7 @@ impl ProcessorControlRegion {
}
}
<<<<<<< HEAD
#[cold]
fn halt_pcr_init() -> ! {
println!("FATAL: failed to allocate physical memory for Processor Control Region");
@@ -201,6 +202,8 @@ fn halt_pcr_init() -> ! {
}
}
=======
>>>>>>> master
pub unsafe fn pcr() -> *mut ProcessorControlRegion {
unsafe {
// Primitive benchmarking of RDFSBASE and RDGSBASE in userspace, appears to indicate that
@@ -384,10 +387,14 @@ pub fn allocate_and_init_pcr(
.next_power_of_two()
.trailing_zeros();
<<<<<<< HEAD
let pcr_frame = match crate::memory::allocate_p2frame(alloc_order) {
Some(frame) => frame,
None => halt_pcr_init(),
};
=======
let pcr_frame = crate::memory::allocate_p2frame(alloc_order).expect("failed to allocate PCR");
>>>>>>> master
let pcr_ptr = RmmA::phys_to_virt(pcr_frame.base()).data() as *mut ProcessorControlRegion;
unsafe { core::ptr::write(pcr_ptr, ProcessorControlRegion::new_partial_init(cpu_id)) };
@@ -78,6 +78,7 @@ static INIT_BSP_IDT: SyncUnsafeCell<Idt> = SyncUnsafeCell::new(Idt::new());
pub(crate) static IDTS: RwLock<HashMap<LogicalCpuId, &'static mut Idt>> =
RwLock::new(HashMap::with_hasher(DefaultHashBuilder::new()));
<<<<<<< HEAD
#[cold]
fn halt_idt_init() -> ! {
println!("FATAL: failed to allocate physical pages for backup interrupt stack");
@@ -87,6 +88,8 @@ fn halt_idt_init() -> ! {
}
}
=======
>>>>>>> master
#[inline]
pub fn is_reserved(cpu_id: LogicalCpuId, index: u8) -> bool {
if cpu_id == LogicalCpuId::BSP {
@@ -110,8 +113,11 @@ pub fn set_reserved(cpu_id: LogicalCpuId, index: u8, reserved: bool) {
}
pub fn available_irqs_iter(cpu_id: LogicalCpuId) -> impl Iterator<Item = u8> + 'static {
<<<<<<< HEAD
let count = (32..=254).filter(|&index| !is_reserved(cpu_id, index)).count();
info!("available_irqs_iter: cpu_id={} count={}", cpu_id.get(), count);
=======
>>>>>>> master
(32..=254).filter(move |&index| !is_reserved(cpu_id, index))
}
@@ -172,10 +178,15 @@ pub fn allocate_and_init_idt(cpu_id: LogicalCpuId) -> *mut Idt {
.or_insert_with(|| Box::leak(Box::new(Idt::new())));
use crate::memory::{RmmA, RmmArch};
<<<<<<< HEAD
let frames = match crate::memory::allocate_p2frame(4) {
Some(frames) => frames,
None => halt_idt_init(),
};
=======
let frames = crate::memory::allocate_p2frame(4)
.expect("failed to allocate pages for backup interrupt stack");
>>>>>>> master
// Physical pages are mapped linearly. So is the linearly mapped virtual memory.
let base_address = RmmA::phys_to_virt(frames.base());
@@ -1,5 +1,8 @@
<<<<<<< HEAD
use core::sync::atomic::{AtomicBool, Ordering};
=======
>>>>>>> master
use syscall::Exception;
use x86::irq::PageFaultError;
@@ -12,6 +15,7 @@ use crate::{
syscall::flag::*,
};
<<<<<<< HEAD
static NMI_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
unsafe fn nmi_raw_serial_write(bytes: &[u8]) {
@@ -28,6 +32,8 @@ unsafe fn nmi_raw_serial_write(bytes: &[u8]) {
}
}
=======
>>>>>>> master
interrupt_stack!(divide_by_zero, |stack| {
println!("Divide by zero");
stack.trace();
@@ -73,6 +79,7 @@ interrupt_stack!(non_maskable, @paranoid, |stack| {
#[cfg(not(all(target_arch = "x86_64", feature = "profiling")))]
{
<<<<<<< HEAD
if NMI_IN_PROGRESS.swap(true, Ordering::SeqCst) {
return;
}
@@ -102,6 +109,11 @@ interrupt_stack!(non_maskable, @paranoid, |stack| {
}
NMI_IN_PROGRESS.store(false, Ordering::SeqCst);
=======
// TODO: This will likely deadlock
println!("Non-maskable interrupt");
stack.dump();
>>>>>>> master
}
});
@@ -28,8 +28,11 @@ pub mod pti;
/// Initialization and start function
pub mod start;
<<<<<<< HEAD
pub mod sleep;
=======
>>>>>>> master
/// Stop function
pub mod stop;
@@ -82,6 +82,7 @@ extern "C" fn kstart() {
/// The entry to Rust, all things must be initialized
unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
unsafe {
<<<<<<< HEAD
// EARLY CANARY: write 'R' to COM1 before any kernel init.
// This proves the serial hardware works and the kernel reached Rust entry.
// If this character appears but "RedBear OS starting..." does not,
@@ -91,6 +92,8 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'R', options(nostack, preserves_flags));
}
=======
>>>>>>> master
let bootstrap = {
let args = args_ptr.read();
@@ -100,6 +103,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
// Set up graphical debug
graphical_debug::init(args.env());
<<<<<<< HEAD
// SECOND CANARY: write 'S' to COM1 after serial init.
// If 'R' appears but 'S' does not, the hang is in serial::init() or graphical_debug::init().
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
@@ -125,24 +129,41 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{ core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'3', options(nostack, preserves_flags)); }
=======
info!("Redox OS starting...");
args.print();
// Set up GDT
gdt::init_bsp(stack_end);
// Set up IDT
idt::init_bsp();
>>>>>>> master
// Initialize RMM
#[cfg(target_arch = "x86")]
crate::startup::memory::init(&args, Some(0x100000), Some(0x40000000));
#[cfg(target_arch = "x86_64")]
crate::startup::memory::init(&args, Some(0x100000), None);
<<<<<<< HEAD
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{ core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'4', options(nostack, preserves_flags)); }
=======
>>>>>>> master
// Initialize paging
paging::init();
#[cfg(target_arch = "x86_64")]
crate::arch::alternative::early_init(true);
<<<<<<< HEAD
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{ core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'5', options(nostack, preserves_flags)); }
=======
>>>>>>> master
// Set up syscall instruction
interrupt::syscall::init();
@@ -152,9 +173,12 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
// Activate memory logging
crate::log::init();
<<<<<<< HEAD
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{ core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'6', options(nostack, preserves_flags)); }
=======
>>>>>>> master
// Initialize miscellaneous processor features
#[cfg(target_arch = "x86_64")]
crate::arch::misc::init(LogicalCpuId::BSP);
@@ -162,9 +186,12 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
// Initialize devices
device::init();
<<<<<<< HEAD
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{ core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'7', options(nostack, preserves_flags)); }
=======
>>>>>>> master
// Read ACPI tables, starts APs
if cfg!(feature = "acpi") {
crate::acpi::init(args.acpi_rsdp());
@@ -4,10 +4,23 @@ use crate::{
percpu::PercpuBlock,
syscall::FloatRegisters,
};
<<<<<<< HEAD
use core::{mem::offset_of, ptr};
use spin::Once;
use syscall::{EnvRegisters, Result};
=======
use core::{mem::offset_of, ptr, sync::atomic::AtomicBool};
use spin::Once;
use syscall::{EnvRegisters, Result};
/// This must be used by the kernel to ensure that context switches are done atomically
/// Compare and exchange this to true when beginning a context switch on any CPU
/// The `Context::switch_to` function will set it back to false, allowing other CPU's to switch
/// This must be done, as no locks can be held on the stack during switch
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);
>>>>>>> master
// 512 bytes for registers, extra bytes for fpcr and fpsr
pub const KFX_ALIGN: usize = 16;
@@ -2,11 +2,20 @@ use crate::{
arch::interrupt::InterruptStack, context::context::Kstack, memory::RmmA, percpu::PercpuBlock,
syscall::FloatRegisters,
};
<<<<<<< HEAD
use core::mem::offset_of;
=======
use core::{mem::offset_of, sync::atomic::AtomicBool};
>>>>>>> master
use rmm::{Arch, VirtualAddress};
use spin::Once;
use syscall::{error::*, EnvRegisters};
<<<<<<< HEAD
=======
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);
>>>>>>> master
pub const KFX_ALIGN: usize = 16;
#[derive(Clone, Debug, Default)]
@@ -1,4 +1,8 @@
<<<<<<< HEAD
use core::mem::offset_of;
=======
use core::{mem::offset_of, sync::atomic::AtomicBool};
>>>>>>> master
use rmm::{Arch, VirtualAddress};
use spin::Once;
use syscall::{error::*, EnvRegisters};
@@ -14,6 +18,15 @@ use crate::{
syscall::FloatRegisters,
};
<<<<<<< HEAD
=======
/// This must be used by the kernel to ensure that context switches are done atomically
/// Compare and exchange this to true when beginning a context switch on any CPU
/// The `Context::switch_to` function will set it back to false, allowing other CPU's to switch
/// This must be done, as no locks can be held on the stack during switch
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);
>>>>>>> master
const ST_RESERVED: u128 = 0xFFFF_FFFF_FFFF_0000_0000_0000_0000_0000;
pub const KFX_ALIGN: usize = 16;
@@ -1,5 +1,9 @@
use core::{
ptr::{addr_of, addr_of_mut},
<<<<<<< HEAD
=======
sync::atomic::AtomicBool,
>>>>>>> master
};
use crate::syscall::FloatRegisters;
@@ -11,6 +15,15 @@ use spin::Once;
use syscall::{error::*, EnvRegisters};
use x86::msr;
<<<<<<< HEAD
=======
/// This must be used by the kernel to ensure that context switches are done atomically
/// Compare and exchange this to true when beginning a context switch on any CPU
/// The `Context::switch_to` function will set it back to false, allowing other CPU's to switch
/// This must be done, as no locks can be held on the stack during switch
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);
>>>>>>> master
const ST_RESERVED: u128 = 0xFFFF_FFFF_FFFF_0000_0000_0000_0000_0000;
#[cfg(cpu_feature_never = "xsave")]
@@ -148,8 +148,11 @@ pub struct Context {
pub euid: u32,
pub egid: u32,
pub pid: usize,
<<<<<<< HEAD
/// Supplementary group IDs for access control decisions.
pub groups: Vec<u32>,
=======
>>>>>>> master
// See [`PreemptGuard`]
//
@@ -206,7 +209,10 @@ impl Context {
euid: 0,
egid: 0,
pid: 0,
<<<<<<< HEAD
groups: Vec::new(),
=======
>>>>>>> master
#[cfg(feature = "syscall_debug")]
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
@@ -482,7 +488,10 @@ impl Context {
uid: self.euid,
gid: self.egid,
pid: self.pid,
<<<<<<< HEAD
groups: self.groups.clone(),
=======
>>>>>>> master
}
}
}
@@ -4,7 +4,11 @@ use crate::{
event,
scheme::{self, SchemeId},
sync::{CleanLockToken, RwLock, L6},
<<<<<<< HEAD
syscall::error::{Error, Result, ESTALE},
=======
syscall::error::Result,
>>>>>>> master
};
use alloc::sync::Arc;
use syscall::{schemev2::NewFdFlags, RwFlags, O_APPEND, O_NONBLOCK};
@@ -18,7 +22,10 @@ pub struct FileDescription {
pub offset: u64,
/// The scheme that this file refers to
pub scheme: SchemeId,
<<<<<<< HEAD
pub scheme_generation: Option<u64>,
=======
>>>>>>> master
/// The number the scheme uses to refer to this file
pub number: usize,
/// The flags passed to open or fcntl(SETFL)
@@ -33,6 +40,7 @@ bitflags! {
}
}
impl FileDescription {
<<<<<<< HEAD
pub fn with_generation(
scheme: SchemeId,
scheme_generation: Option<u64>,
@@ -79,6 +87,8 @@ impl FileDescription {
scheme::get_scheme(token.token(), self.scheme)
}
=======
>>>>>>> master
pub fn rw_flags(&self, rw: RwFlags) -> u32 {
let mut ret = self.flags & !(O_NONBLOCK | O_APPEND) as u32;
if rw.contains(RwFlags::APPEND) {
@@ -123,7 +133,11 @@ impl FileDescription {
pub fn try_close(self, token: &mut CleanLockToken) -> Result<()> {
event::unregister_file(self.scheme, self.number, token);
<<<<<<< HEAD
let scheme = self.get_scheme(token)?;
=======
let scheme = scheme::get_scheme(token.token(), self.scheme)?;
>>>>>>> master
scheme.close(self.number, token)
}
@@ -132,12 +146,21 @@ impl FileDescription {
impl FileDescriptor {
pub fn close(self, token: &mut CleanLockToken) -> Result<()> {
{
<<<<<<< HEAD
let (desc, number, internal_flags) = {
let desc = self.description.read(token.token());
(*desc, desc.number, desc.internal_flags)
};
if internal_flags.contains(InternalFlags::NOTIFY_ON_NEXT_DETACH) {
let scheme = desc.get_scheme(token)?;
=======
let (scheme_id, number, internal_flags) = {
let desc = self.description.read(token.token());
(desc.scheme, desc.number, desc.internal_flags)
};
if internal_flags.contains(InternalFlags::NOTIFY_ON_NEXT_DETACH) {
let scheme = scheme::get_scheme(token.token(), scheme_id)?;
>>>>>>> master
scheme.detach(number, token)?;
}
}
@@ -64,6 +64,7 @@ impl UnmapResult {
return Ok(());
};
<<<<<<< HEAD
let (scheme, number) = {
let desc = *description.read(token.token());
(desc.get_scheme(token)?, desc.number)
@@ -71,6 +72,16 @@ impl UnmapResult {
let funmap_result = scheme
.kfunmap(number, base_offset, self.size, self.flags, token);
=======
let (scheme_id, number) = {
let desc = description.write(token.token());
(desc.scheme, desc.number)
};
let scheme_opt = scheme::get_scheme(token.token(), scheme_id);
let funmap_result = scheme_opt
.and_then(|scheme| scheme.kfunmap(number, base_offset, self.size, self.flags, token));
>>>>>>> master
if let Ok(fd) = Arc::try_unwrap(description) {
fd.into_inner().try_close(token)?;
@@ -2686,6 +2697,7 @@ fn correct_inner<'l>(
// XXX: This is cheating, but guaranteed we won't deadlock because we've dropped addr_space_guard
let mut token = unsafe { CleanLockToken::new() };
<<<<<<< HEAD
let desc = *file_ref.description.read(token.token());
let scheme = desc.get_scheme(&mut token).map_err(|_| PfError::Segv)?;
let scheme_number = desc.number;
@@ -2693,6 +2705,22 @@ fn correct_inner<'l>(
KernelSchemes::User(user) => user.inner,
_ => return Err(PfError::Segv),
};
=======
let (scheme_id, scheme_number) = {
let desc = &file_ref.description.read(token.token());
(desc.scheme, desc.number)
};
let user_inner = scheme::get_scheme(token.token(), scheme_id)
.ok()
.and_then(|s| {
if let KernelSchemes::User(user) = s {
Some(user.inner)
} else {
None
}
})
.ok_or(PfError::Segv)?;
>>>>>>> master
let offset = file_ref.base_offset as u64 + (pages_from_grant_start * PAGE_SIZE) as u64;
user_inner
@@ -14,8 +14,13 @@ use crate::{
memory::{RmmA, RmmArch, TableKind},
percpu::PercpuBlock,
sync::{
<<<<<<< HEAD
ArcRwLockWriteGuard, CleanLockToken, LockToken, McsMutex, McsMutexGuard, Mutex,
MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, L0, L1, L2, L4,
=======
ArcRwLockWriteGuard, CleanLockToken, LockToken, Mutex, MutexGuard, RwLock, RwLockReadGuard,
RwLockWriteGuard, L0, L1, L2, L4,
>>>>>>> master
},
syscall::error::Result,
};
@@ -74,12 +79,19 @@ pub use self::arch::empty_cr3;
// the context file descriptors.
static CONTEXTS: RwLock<L2, BTreeSet<ContextRef>> = RwLock::new(BTreeSet::new());
<<<<<<< HEAD
// Actual context store for the scheduler — uses MCS fair spinlock to
// eliminate cache-line bouncing under multi-CPU contention.
static RUN_CONTEXTS: McsMutex<L1, RunContextData> = McsMutex::new(RunContextData::new());
// Context that has been pushed out from RUN_CONTEXTS after being idle.
// Uses regular Mutex (lower contention; wakeup_contexts uses try_lock).
=======
// Actual context store for the scheduler
static RUN_CONTEXTS: Mutex<L1, RunContextData> = Mutex::new(RunContextData::new());
// Context that has been pushed out from RUN_CONTEXTS after being idle
>>>>>>> master
static IDLE_CONTEXTS: Mutex<L2, VecDeque<WeakContextRef>> = Mutex::new(VecDeque::new());
pub struct RunContextData {
@@ -115,7 +127,11 @@ pub fn idle_contexts_try(
IDLE_CONTEXTS.try_lock(token)
}
<<<<<<< HEAD
pub fn run_contexts(token: LockToken<'_, L0>) -> McsMutexGuard<'_, L1, RunContextData> {
=======
pub fn run_contexts(token: LockToken<'_, L0>) -> MutexGuard<'_, L1, RunContextData> {
>>>>>>> master
RUN_CONTEXTS.lock(token)
}
@@ -15,7 +15,11 @@ use crate::{
use alloc::{sync::Arc, vec::Vec};
use core::{
cell::{Cell, RefCell},
<<<<<<< HEAD
mem,
=======
hint, mem,
>>>>>>> master
sync::atomic::Ordering,
};
use syscall::PtraceFlags;
@@ -26,11 +30,14 @@ enum UpdateResult {
Blocked,
}
<<<<<<< HEAD
/// Default number of PIT ticks before triggering a context switch.
/// At ~2.25 ms per tick, 3 ticks ≈ 6.75 ms timeslice.
/// Configurable per-CPU via `ContextSwitchPercpu::preempt_interval`.
const DEFAULT_PREEMPT_INTERVAL: usize = 3;
=======
>>>>>>> master
// A simple geometric series where value[i] ~= value[i - 1] * 1.25
const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [
88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100, 4904,
@@ -95,15 +102,24 @@ struct SwitchResultInner {
///
/// The function also calls the signal handler after switching contexts.
pub fn tick(token: &mut CleanLockToken) {
<<<<<<< HEAD
let percpu = PercpuBlock::current();
let ticks_cell = &percpu.switch_internals.pit_ticks;
=======
let ticks_cell = &PercpuBlock::current().switch_internals.pit_ticks;
>>>>>>> master
let new_ticks = ticks_cell.get() + 1;
ticks_cell.set(new_ticks);
<<<<<<< HEAD
// Trigger a context switch when the per-CPU preempt interval is reached.
let interval = percpu.switch_internals.preempt_interval.get();
if new_ticks >= interval {
=======
// Trigger a context switch after every 3 ticks (approx. 6.75 ms).
if new_ticks >= 3 {
>>>>>>> master
switch(token);
crate::context::signal::signal_handler(token);
}
@@ -127,10 +143,14 @@ pub unsafe extern "C" fn switch_finish_hook() {
crate::arch::stop::emergency_reset();
}
}
<<<<<<< HEAD
PercpuBlock::current()
.switch_internals
.in_context_switch
.set(false);
=======
arch::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
>>>>>>> master
crate::percpu::switch_arch_hook();
}
}
@@ -160,6 +180,7 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
//set PIT Interrupt counter to 0, giving each process same amount of PIT ticks
percpu.switch_internals.pit_ticks.set(0);
<<<<<<< HEAD
// Acquire the per-CPU context switch flag. Each CPU can only be in one context
// switch at a time. The per-context write locks provide cross-CPU safety; this
// flag catches re-entrant switches on the same CPU (a kernel bug).
@@ -169,6 +190,18 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
percpu.cpu_id
);
percpu.switch_internals.in_context_switch.set(true);
=======
// Acquire the global lock to ensure exclusive access during context switch and avoid
// issues that would be caused by the unsafe operations below
// TODO: Better memory orderings?
while arch::CONTEXT_SWITCH_LOCK
.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Relaxed)
.is_err()
{
hint::spin_loop();
percpu.maybe_handle_tlb_shootdown();
}
>>>>>>> master
// Lock the previous context.
let prev_context_lock = crate::context::current();
@@ -176,8 +209,13 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
let mut prev_context_guard = unsafe { prev_context_lock.write_arc() };
if !prev_context_guard.is_preemptable() {
<<<<<<< HEAD
// Unset per-CPU context switch flag
percpu.switch_internals.in_context_switch.set(false);
=======
// Unset global lock
arch::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
>>>>>>> master
// Pretend to have finished switching, so CPU is not idled
return SwitchResult::Switched;
@@ -301,8 +339,13 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
SwitchResult::Switched
}
_ => {
<<<<<<< HEAD
// No target was found, unset per-CPU context switch flag and return
percpu.switch_internals.in_context_switch.set(false);
=======
// No target was found, unset global lock and return
arch::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
>>>>>>> master
percpu.stats.set_state(cpu_stats::CpuState::Idle);
@@ -361,7 +404,10 @@ fn wakeup_contexts(token: &mut CleanLockToken, switch_time: u128) -> Vec<(usize,
}
/// This is the scheduler function which currently utilises Deficit Weighted Round Robin Scheduler
<<<<<<< HEAD
/// with NUMA-aware context selection preference.
=======
>>>>>>> master
fn select_next_context(
token: &mut CleanLockToken,
percpu: &PercpuBlock,
@@ -387,10 +433,13 @@ fn select_next_context(
let total_contexts: usize = contexts_list.iter().map(|q| q.len()).sum();
let mut skipped_contexts = 0;
<<<<<<< HEAD
// NUMA-aware selection: remember cross-node fallback candidate.
let my_numa_node = percpu.numa_node.get();
let mut cross_node_fallback: Option<(usize, ArcContextLockWriteGuard)> = None;
=======
>>>>>>> master
'priority: loop {
i = (i + 1) % 40;
total_iters += 1;
@@ -455,6 +504,7 @@ fn select_next_context(
// Is this context runnable on this CPU?
let sw = unsafe { update_runnable(&mut next_context_guard, cpu_id, switch_time) };
if let UpdateResult::CanSwitch = sw {
<<<<<<< HEAD
// NUMA-aware selection: check if this context's last CPU was on the same node.
let same_node = if my_numa_node != u8::MAX {
next_context_guard.cpu_id
@@ -493,6 +543,11 @@ fn select_next_context(
continue;
}
}
=======
next_context_guard_opt = Some(next_context_guard);
balance[i] -= SCHED_PRIO_TO_WEIGHT[20];
break 'priority;
>>>>>>> master
} else {
if matches!(sw, UpdateResult::Blocked) {
idle_contexts(token.token()).push_back(next_context_ref);
@@ -507,6 +562,7 @@ fn select_next_context(
}
}
}
<<<<<<< HEAD
// If we found a cross-node fallback but no same-node context, use it
if next_context_guard_opt.is_none() {
@@ -516,6 +572,8 @@ fn select_next_context(
}
}
=======
>>>>>>> master
percpu.balance.set(balance);
percpu.last_queue.set(i);
@@ -523,10 +581,14 @@ fn select_next_context(
// Send the old process to the back of the line (if it is still runnable)
let prev_ctx = WeakContextRef(Arc::downgrade(&prev_context_lock));
if prev_context_guard.status.is_runnable() {
<<<<<<< HEAD
let raw_prio = prev_context_guard.prio;
let prio = percpu.effective_prio(raw_prio);
// Clear PI donation — previous context is being re-queued
percpu.pi_donated_prio.store(u32::MAX, Ordering::Relaxed);
=======
let prio = prev_context_guard.prio;
>>>>>>> master
contexts_list[prio].push_back(prev_ctx);
} else {
idle_contexts(token.token()).push_back(prev_ctx);
@@ -538,8 +600,12 @@ fn select_next_context(
return Ok(Some(next_context_guard));
} else {
if !was_idle && !Arc::ptr_eq(&prev_context_lock, &idle_context) {
<<<<<<< HEAD
// Switching to idle context — cache lowest priority
percpu.current_prio.set(39);
=======
// We switch into the idle context
>>>>>>> master
Ok(Some(unsafe { idle_context.write_arc() }))
} else {
// We found no other process to run.
@@ -556,6 +622,7 @@ pub struct ContextSwitchPercpu {
switch_result: Cell<Option<SwitchResultInner>>,
switch_time: Cell<u128>,
pit_ticks: Cell<usize>,
<<<<<<< HEAD
/// Per-CPU context switch flag. Set to true during a context switch on this CPU.
/// Replaced the global CONTEXT_SWITCH_LOCK to eliminate cross-CPU serialization.
in_context_switch: Cell<bool>,
@@ -563,6 +630,8 @@ pub struct ContextSwitchPercpu {
/// Default: 3 (≈6.75 ms). Lower values improve interactive responsiveness;
/// higher values improve throughput for batch/compute workloads.
preempt_interval: Cell<usize>,
=======
>>>>>>> master
current_ctxt: RefCell<Option<Arc<ContextLock>>>,
@@ -577,8 +646,11 @@ impl ContextSwitchPercpu {
switch_result: Cell::new(None),
switch_time: Cell::new(0),
pit_ticks: Cell::new(0),
<<<<<<< HEAD
in_context_switch: Cell::new(false),
preempt_interval: Cell::new(DEFAULT_PREEMPT_INTERVAL),
=======
>>>>>>> master
current_ctxt: RefCell::new(None),
idle_ctxt: RefCell::new(None),
being_sigkilled: Cell::new(false),
+11
View File
@@ -42,18 +42,29 @@ impl core::fmt::Display for LogicalCpuId {
}
#[cfg(target_pointer_width = "64")]
<<<<<<< HEAD
pub const MAX_CPU_COUNT: u32 = 256;
=======
pub const MAX_CPU_COUNT: u32 = 128;
>>>>>>> master
#[cfg(target_pointer_width = "32")]
pub const MAX_CPU_COUNT: u32 = 32;
const SET_WORDS: usize = (MAX_CPU_COUNT / usize::BITS) as usize;
<<<<<<< HEAD
// TODO: Support more than 256 CPUs.
// The maximum number of CPUs on Linux is configurable, and the type for LogicalCpuSet and
// LogicalCpuId may be optimized accordingly. In that case, box the mask if it's larger than some
// base size (probably 256 bytes). AMD EPYC has 128C/256T, Threadripper PRO 96C/192T —
// 256 covers current hardware.
=======
// TODO: Support more than 128 CPUs.
// The maximum number of CPUs on Linux is configurable, and the type for LogicalCpuSet and
// LogicalCpuId may be optimized accordingly. In that case, box the mask if it's larger than some
// base size (probably 256 bytes).
>>>>>>> master
#[derive(Debug)]
pub struct LogicalCpuSet([AtomicUsize; SET_WORDS]);
+10
View File
@@ -1,5 +1,9 @@
use alloc::sync::Arc;
<<<<<<< HEAD
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
=======
use core::sync::atomic::{AtomicUsize, Ordering};
>>>>>>> master
use hashbrown::{hash_map::DefaultHashBuilder, HashMap};
use smallvec::SmallVec;
use syscall::data::GlobalSchemes;
@@ -23,7 +27,10 @@ int_like!(EventQueueId, AtomicEventQueueId, usize, AtomicUsize);
pub struct EventQueue {
id: EventQueueId,
queue: WaitQueue<Event>,
<<<<<<< HEAD
pub eventfd: Option<(AtomicU64, bool)>, // (counter, semaphore_mode)
=======
>>>>>>> master
}
impl EventQueue {
@@ -31,6 +38,7 @@ impl EventQueue {
EventQueue {
id,
queue: WaitQueue::new(),
<<<<<<< HEAD
eventfd: None,
}
}
@@ -40,6 +48,8 @@ impl EventQueue {
id,
queue: WaitQueue::new(),
eventfd: Some((AtomicU64::new(initval), semaphore)),
=======
>>>>>>> master
}
}

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