rate-limit scheme error spam to prevent serial log flood

This commit is contained in:
2026-05-28 00:36:17 +03:00
parent 3583ee0186
commit 328d1abbcd
59 changed files with 10063 additions and 25957 deletions
+43
View File
@@ -457,6 +457,49 @@ See `local/docs/BUILD-SYSTEM-HARDENING-PLAN.md` for the full plan.
- **DO NOT** skip warnings — investigate, diagnose, and fix the root cause; suppressing or ignoring warnings is not acceptable when a fix is feasible
- **DO NOT** remove patches from `recipe.toml` to fix build failures — rebase the patch instead (see `local/docs/PATCH-GOVERNANCE.md`)
- **DO NOT** remove BINS entries to fix build failures — fix the source or use EXISTING_BINS filtering
- **DO NOT** use the VESA display driver (`vesad`) as the primary display surface after GPU detection. vesad is only for early-boot framebuffer handoff — after redox-drm loads, the display path is `/scheme/drm/card0`. See **NO VESA POLICY** below.
## NO VESA POLICY
Red Bear OS does not use the VESA display driver as the primary display surface. All display
output goes through the DRM/KMS path via real GPU drivers:
| Environment | GPU Driver | 3D Support |
|---|---|---|
| QEMU | virtio-gpu (via redox-drm) | ✅ virgl |
| Intel hardware | Intel i915-like (via redox-drm) | ✅ Mesa i965/iris |
| AMD hardware | amdgpu (via redox-drm + linux-kpi) | ✅ Mesa radeonsi |
| Future | nouveau reimplementation (Rust, via redox-drm) | ✅ Mesa nouveau |
**vesad is allowed ONLY as an early-boot framebuffer handoff.** The bootloader sets up a linear
framebuffer before the kernel starts. vesad takes over this framebuffer so the initfs has console
output (fbcond, fbbootlogd) before real GPU drivers are available. Once redox-drm initializes and
registers `scheme:drm/card0`, vesad must hand off and NOT register `scheme:display.vesa` as the
primary display surface.
The display path for redbear-full:
```
Bootloader linear framebuffer
→ vesad (initfs, service 20): temporary FB handoff for text console
→ redox-drm (initfs, service 30): detects GPU hardware, takes over via DRM/KMS
→ redox-drm (rootfs, service 14): full DRM driver with 3D (Mesa)
→ KWin compositor: DRM/KMS master, composites desktop via /scheme/drm/card0
```
For redbear-mini: vesad handles the bootloader framebuffer for the text-only console. No GPU
driver loads — mini is text-only by design.
**After GPU detection, any code that opens `/scheme/display.vesa/` is incorrect.** The correct
display path is `/scheme/drm/card0` via the DRM scheme.
Rationale: VESA is a legacy BIOS-era standard with no hardware acceleration, no mode setting
beyond what the bootloader provides, no 3D, and no future. Red Bear OS targets real GPU
hardware with full DRM/KMS and Mesa support. vesad serves only as a bridge between bootloader
FB and the real GPU driver — it is never the final display path.
This policy also covers future GPU driver work: any new GPU support (nouveau Rust reimplementation,
ARM Mali, etc.) must go through the redox-drm + DRM/KMS path, never through VESA fallback.
## ZERO TOLERANCE FOR STUBS
+1 -1
View File
@@ -444,7 +444,7 @@ requires_weak = ["04_drivers.target"]
[service]
cmd = "/usr/bin/coretempd"
type = { scheme = "coretemp" }
type = "oneshot_async"
"""
[[files]]
+8 -1
View File
@@ -6,6 +6,13 @@
#
# Extends redbear-mini with the full desktop/graphics stack:
# Wayland, Qt6, KF6, KWin, Mesa, DRM drivers, firmware, greeter.
#
# GPU/display policy: DRM/KMS ONLY. No VESA. Real GPU drivers:
# QEMU → virtio-gpu via redox-drm (virgl 3D)
# Intel → i915-like via redox-drm (Mesa i965/iris)
# AMD → amdgpu via redox-drm + linux-kpi (Mesa radeonsi)
# Display path: bootloader FB → redox-drm → DRM/KMS → KWin compositor
# Consult local/reference/linux-7.0/ for driver behavior reference.
include = ["redbear-mini.toml"]
@@ -57,7 +64,7 @@ fontconfig = {}
libwayland = {}
wayland-protocols = {}
plasma-wayland-protocols = {}
redbear-compositor = "ignore" # replaced by kwin
redbear-compositor = {}
# Keyboard/input
libxkbcommon = {}
@@ -0,0 +1,363 @@
# Driver Discovery and Dynamic Hardware Mapping Plan
**Status**: Draft — implementation pending
**Date**: 2026-05-27
**Supersedes**: Ad-hoc pcid-spawner + hardcoded lived disk paths
**Author**: Red Bear OS team
---
## 1. Problem Statement
Red Bear OS has two critical gaps in hardware discovery:
1. **lived's disk fallback is broken**: The live ISO boot daemon (`lived`) tries hardcoded paths `/scheme/disk/0` and `/scheme/usbscsi/0` to find the physical boot disk. But no disk driver registers those exact scheme names — they register `disk.pci-00-1F-2_ahci`, `disk.usb-xhci+1-scsi`, etc. The fallback **never works**.
2. **No dynamic hardware mapping**: The system does not distinguish between "hardware present" and "driver needed." On bare metal with no virtio devices, the system should not try to load `virtio-blkd`. On QEMU with no real AHCI controller, the system should not try to load `ahcid`. Today, the driver-manager loads whatever matches its static config files regardless of whether the hardware exists.
Linux solves both problems with a two-stage model:
- **Stage 1 (initramfs)**: Enumerate PCI bus, load ONLY the storage driver matching the boot controller, mount rootfs.
- **Stage 2 (rootfs)**: Full enumeration, udev + modprobe dynamically load all remaining drivers based on actual hardware.
---
## 2. Current Architecture
### 2.1 Boot Sequence (Initfs Phase)
```
Bootstrap (PID 1) → init → services start in dependency order:
00_runtime.target randd, nulld, zerod, rtcd, logd
10_inputd.service VT input multiplexer
10_lived.service Live disk daemon (RAM preload + disk fallback)
20_graphics.target vesad (FB handoff), fbcond, fbbootlogd
41_acpid.service ACPI interpreter → scheme:acpi
40_hwd.service Hardware manager → spawns pcid internally
pcid → enumerates PCI bus → registers scheme:pci
00_driver-manager-initfs.service (if P26 applied)
Loads /scheme/initfs/lib/drivers.d/00-storage.toml
Only: ahcid, ided, nvmed, virtio-blkd
40_drivers.target All initfs drivers
50_rootfs.service Mount rootfs (hard dep on drivers.target)
90_initfs.target Trigger switchroot
```
### 2.2 Driver Registration Contract
All disk drivers using `driver_block::DiskScheme` register schemes starting with `"disk"`:
| Driver | Scheme Name Pattern | Match Criteria |
|--------|---------------------|----------------|
| ided | `disk.pci-XX-XX-X_ide` | PCI class 0x01, subclass 0x01 |
| ahcid | `disk.pci-XX-XX-X_ahci` | PCI class 0x01, subclass 0x06 |
| nvmed | `disk.pci-XX-XX-X-nvme` | PCI class 0x01, subclass 0x08 |
| virtio-blkd | `disk.pci-XX-XX-X_virtio_blk` | PCI vendor 0x1AF4, device 0x1001 |
| usbscsid | `disk.usb-xhci+PORT-scsi` | USB SCSI transport |
| lived | `disk.live` | RAM-backed (our daemon) |
The `DiskScheme::new()` assertion (`assert!(scheme_name.starts_with("disk"))`) is the **contract** that enables dynamic discovery: any consumer can find all disk schemes by listing `/scheme/` and filtering for the `"disk"` prefix.
### 2.3 The Two Driver-Loading Paths
| Path | Mechanism | Config Source | Drivers |
|------|-----------|---------------|---------|
| **Initfs** | `driver-manager --initfs` | `/scheme/initfs/lib/drivers.d/00-storage.toml` | Storage only (4 drivers) |
| **Rootfs** | `driver-manager --hotplug` | `/lib/drivers.d/*.toml` | All categories (40+ drivers) |
### 2.4 How Linux Does It (Reference)
Linux uses a two-tier ordering:
**Tier 1 — Initcall levels** (include/linux/init.h):
```
Level 0: pure_initcall (architecture setup)
Level 2: postcore_initcall (PCI subsystem registers here)
Level 4: subsys_initcall (SCSI, networking subsystems)
Level 6: device_initcall (module_init → all built-in drivers)
Level 7: late_initcall (late-stage platform drivers)
```
**Tier 2 — Link order** within device_initcall (drivers/Makefile):
```
Line 49: obj-y += virtio/ # VirtIO before block
Line 76: obj-y += block/ # Block devices (storage)
Line 84: obj-y += nvme/ # NVMe
Line 85: obj-y += ata/ # ATA/AHCI
Line 92: obj-y += net/ # Network
Line 68: obj-y += gpu/ # GPU comes AFTER storage
```
**The critical principle**: Storage must load before GPU not because of PCI ordering, but because GPU drivers need firmware blobs from `/lib/firmware/` — which requires a mounted filesystem. Storage drivers are needed to mount that filesystem.
**Dynamic loading** (after rootfs mount): `MODULE_DEVICE_TABLE` entries in every driver generate `modules.alias` patterns. udev receives kernel uevents with `MODALIAS=pci:v00001AF4d00001001...`, calls `modprobe`, which looks up the alias and loads the matching `.ko` module.
---
## 3. Design: Two-Stage Dynamic Hardware Discovery
### 3.1 Stage 1 — Initfs Boot (Storage-Only)
**Goal**: Load exactly the storage driver(s) needed to mount the root filesystem. No more, no less.
**Mechanism**: driver-manager `--initfs` already exists and does PCI class/vendor matching. The missing piece is that the P26 patch (which creates `00_driver-manager-initfs.service` and `initfs-storage.toml`) is wired in `recipe.toml` but needs to be applied.
**Initfs driver config** (`initfs-storage.toml`):
```toml
# Only storage drivers — needed to mount rootfs
# GPU/display deliberately excluded (handled by rootfs DRM/KMS stack)
[[driver]]
name = "nvmed"
description = "NVMe storage driver"
priority = 100
command = ["/scheme/initfs/lib/drivers/nvmed"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 8
[[driver]]
name = "ahcid"
description = "AHCI SATA driver"
priority = 100
command = ["/scheme/initfs/lib/drivers/ahcid"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 6
[[driver]]
name = "ided"
description = "PATA IDE driver"
priority = 100
command = ["/scheme/initfs/lib/drivers/ided"]
[[driver.match]]
bus = "pci"
class = 1
subclass = 1
[[driver]]
name = "virtio-blkd"
description = "VirtIO block device driver"
priority = 100
command = ["/scheme/initfs/lib/drivers/virtio-blkd"]
[[driver.match]]
bus = "pci"
vendor = 0x1AF4
device = 0x1001
```
**How this is already dynamic**: The driver-manager only spawns a driver when the PCI bus actually reports a matching device. If QEMU has no AHCI controller, `ahcid` is never spawned. If bare metal has no VirtIO devices, `virtio-blkd` is never spawned. The TOML match table is a **candidate list**, not a **must-load list**.
**What's needed**: Ensure P26 is applied, ensure `virtio-blkd` is in the BINS list, and ensure the initfs binary staging includes all 4 storage drivers.
### 3.2 Stage 2 — Rootfs (Full Hardware Discovery)
**Goal**: After rootfs is mounted, dynamically discover and load ALL remaining drivers based on actual hardware.
**Mechanism**: `driver-manager --hotplug` already reads `/lib/drivers.d/*.toml` (8 config files, 40+ drivers), enumerates PCI + ACPI buses, and spawns matching drivers. It also runs a hotplug loop for device add/remove.
**The existing driver configs are already data-driven and dynamic**:
| Config File | Category | Priority | Matching |
|-------------|----------|----------|----------|
| `00-storage.toml` | Storage | 100 | PCI class-based |
| `10-network.toml` | Network | 50 | PCI vendor + class |
| `20-usb.toml` | USB | 80 | PCI class + prog_if |
| `30-graphics.toml` | GPU/Display | 60 | PCI class 0x03 |
| `40-input.toml` | Input | 40 | Sentinel (vendor=0xFFFF) |
| `50-audio.toml` | Audio | 40 | PCI vendor + class |
| `60-gpio-i2c.toml` | GPIO/I2C | 30 | ACPI bus matching |
| `70-usb-class.toml` | USB class | 20 | Sentinel (vendor=0xFFFF) |
**Key property**: Priority ordering ensures storage (100) > USB (80) > GPU (60) > network (50) > audio (40). This mirrors Linux's link-order principle.
### 3.3 lived Disk Fallback Fix
**Current bug**: `lived` tries `/scheme/disk/0` — but real schemes are named `disk.pci-00-1F-2_ahci`, never just `disk`.
**Fix**: Replace hardcoded paths with RedoxFS-style dynamic scheme discovery (same pattern as `filesystem_by_uuid` in `redoxfs/src/bin/mount.rs`):
```rust
fn try_open_disk(&self) -> Result<File, String> {
for attempt in 0..DISK_OPEN_MAX_RETRIES {
// List /scheme/ to find all registered disk schemes
if let Ok(entries) = std::fs::read_dir("/scheme") {
for entry in entries.flatten() {
let name = entry.file_name();
let name_str = name.to_string_lossy();
// All disk schemes start with "disk." (driver-block contract)
// Skip our own "disk.live" scheme
if name_str.starts_with("disk.") && name_str != "disk.live" {
// Try opening disk 0 on this scheme
let path = format!("/scheme/{}/0", name_str);
if let Ok(file) = File::open(&path) {
eprintln!("lived: opened physical disk at {} (attempt {})",
path, attempt + 1);
return Ok(file);
}
}
}
}
if attempt < DISK_OPEN_MAX_RETRIES - 1 {
std::thread::sleep(std::time::Duration::from_millis(
DISK_OPEN_RETRY_INTERVAL_MS
));
}
}
Err(format!("no disk scheme found after {} retries", DISK_OPEN_MAX_RETRIES))
}
```
**This is the exact pattern RedoxFS uses** in `filesystem_by_uuid()`. It:
1. Lists `/scheme/` (all registered schemes)
2. Filters to names starting with `"disk."` (the `driver-block` contract)
3. Skips `disk.live` (our own RAM-backed scheme)
4. Tries opening disk 0 on each discovered scheme
**Boot timing**: lived starts at service 10, before disk drivers. The retry loop (60 × 500ms = 30s) gives driver-manager and storage drivers time to load and register their schemes. As soon as ANY storage driver registers `disk.*`, lived finds it.
---
## 4. What Needs to Change
### 4.1 Patches Required
| Component | Patch | What It Does |
|-----------|-------|--------------|
| **base** | P60 (new) | Add `virtio-blkd` to BINS + staged files; update lived's `try_open_disk()` with dynamic scheme discovery |
| **kernel** | P26 (existing) | DebugDisplay scrolling fix (already done) |
| **base** | P26-driver-manager-initfs-conversion.patch (existing, wired but needs application verification) | Replaces pcid-spawner with driver-manager in initfs |
### 4.2 Changes to `recipes/core/base/recipe.toml`
1. **Add `virtio-blkd` to BINS** (already done in working tree)
2. **Add `virtio-blkd` to staged files list** (already done in working tree)
3. **No changes to driver configs**`initfs-storage.toml` already lists all 4 storage drivers
### 4.3 Changes to `recipes/core/base/source/drivers/storage/lived/src/main.rs`
Replace the hardcoded `candidates` array in `try_open_disk()` with `/scheme/` directory enumeration that discovers disk schemes dynamically.
### 4.4 No Changes Needed
- **driver-manager** — already does dynamic PCI matching
- **initfs-storage.toml** — already has the right 4 storage drivers
- **Driver configs** (`/lib/drivers.d/*.toml`) — already data-driven with vendor/class matching
- **pcid** — already enumerates PCI bus correctly
- **Boot service order** — already correct (lived at 10, driver-manager-initfs at 00, rootfs at 50)
---
## 5. Verification Plan
### 5.1 QEMU with IDE (default)
```bash
timeout 60 qemu-system-x86_64 \
-drive file=build/x86_64/redbear-full.iso,format=raw \
-m 4G -smp 4 -serial stdio -no-reboot
```
Expected: lived finds `disk.pci-00-01-1_ide` scheme from `ided`, mounts rootfs.
### 5.2 QEMU with virtio-blk
```bash
timeout 60 qemu-system-x86_64 \
-device virtio-blk-pci,drive=drive0 \
-drive id=drive0,file=build/x86_64/redbear-full.iso,format=raw,if=none \
-m 4G -smp 4 -serial stdio -no-reboot
```
Expected: lived finds `disk.pci-00-XX-X_virtio_blk` scheme from `virtio-blkd`, mounts rootfs.
### 5.3 Bare Metal USB Boot
Expected: lived finds `disk.usb-xhci+PORT-scsi` scheme from `usbscsid`, mounts rootfs.
### 5.4 No Unnecessary Drivers
On QEMU with only virtio-blk (no AHCI), `ahcid` should NOT be spawned. Verify via boot log:
```
driver-manager: no driver found for pci 0000:00:01.1 # IDE controller — no match
driver-manager: bound: 0000:00:04.0 -> virtio-blkd # VirtIO block — matched
```
---
## 6. PCI Class Code Reference
From Linux `include/linux/pci_ids.h` and our driver configs:
| Class | Subclass | Prog IF | Device Type | Red Bear Driver |
|-------|----------|---------|-------------|-----------------|
| 0x01 | 0x01 | — | IDE/PATA | `ided` |
| 0x01 | 0x06 | 0x01 | AHCI SATA | `ahcid` |
| 0x01 | 0x08 | 0x02 | NVMe | `nvmed` |
| 0x01 | 0x00 | — | VirtIO Block (vendor 0x1AF4, device 0x1001) | `virtio-blkd` |
| 0x02 | — | — | Ethernet | `e1000d`, `rtl8168d`, etc. |
| 0x03 | — | — | Display/GPU | `redox-drm` |
| 0x04 | 0x03 | — | Audio (HDA) | `ihdad` |
| 0x0C | 0x03 | 0x30 | xHCI USB | `xhcid` |
| 0x0C | 0x03 | 0x00 | UHCI USB | `uhcid` |
| 0x0C | 0x03 | 0x10 | OHCI USB | `ohcid` |
| 0x0C | 0x03 | 0x20 | EHCI USB | `ehcid` |
---
## 7. Boot Timeline (Target State)
```
T+0ms Bootstrap starts, creates initfs/procmgr/namespace schemes
T+50ms init starts, launches 00_randd → 00_logd → 00_runtime.target
T+200ms lived starts (service 10), loads 128 MiB preload
T+300ms vesad starts (FB handoff for text console)
T+400ms acpid starts → ACPI interpreter → scheme:acpi
T+500ms hwd starts → spawns pcid → PCI bus scan → scheme:pci
driver-manager --initfs starts:
Loads 00-storage.toml (4 storage drivers)
Enumerates PCI bus via /scheme/pci/
QEMU: finds 8086:7010 (IDE) → spawns ided
finds 1234:1111 (virtio-gpu) → no storage match, skipped
finds 1AF4:1050 (virtio-net) → no storage match, skipped
T+1500ms ided registers disk.pci-00-01-1_ide
lived discovers disk.pci-00-01-1_ide via /scheme/ enumeration
lived disk fallback succeeds
T+2000ms redoxfs mounts rootfs from lived
T+2500ms switchroot → rootfs init starts
T+3000ms driver-manager --hotplug starts (rootfs):
Loads all /lib/drivers.d/*.toml configs
Detects ided already bound → skips
Finds 1234:1111 (display class 0x03) → spawns redox-drm
Finds 8086:100E (network class 0x02) → spawns e1000d
Finds 1AF4:1050 (virtio-net) → spawns virtio-netd
T+5000ms All drivers bound, system fully operational
```
---
## 8. Principles
1. **Data-driven, not hardcoded**: Driver matching via TOML configs with vendor/device/class fields. No binary name hardcoding, no path guessing.
2. **Enumerate first, match second**: PCI bus scan produces ALL devices. Driver matching filters to supported ones. Unknown hardware is logged but doesn't block boot.
3. **Priority ordering**: Storage (100) before USB (80) before GPU (60) before network (50) before audio (40). Mirrors Linux's link-order principle.
4. **Stage 1 = minimum viable set**: Initfs loads ONLY storage drivers. Everything else waits for rootfs.
5. **Dynamic scheme discovery**: lived discovers disk schemes by reading `/scheme/` and filtering for the `"disk."` prefix — the same contract that `driver-block` enforces.
6. **No unnecessary drivers**: If hardware doesn't exist, the driver is never spawned. `driver-manager` only calls `probe()` for devices that actually exist on the PCI/ACPI bus.
7. **Deferred retry for timing**: Drivers that start before their dependencies are ready get retried (3 times in initfs, 5 times in hotplug). After max retries, the device is permanently skipped with a logged reason.
+483
View File
@@ -0,0 +1,483 @@
# Live ISO Mount — Architecture, Failure Analysis, and Fix Plan
**Date:** 2026-05-27
**Status:** Draft — fixes not yet implemented
**Scope:** Bootloader live preload, lived daemon, RedoxFS mount chain
---
## 1. Current Architecture
### 1.1 Boot Flow (Live ISO)
```
UEFI firmware
→ Bootloader (recipes/core/bootloader/source/src/main.rs)
1. Find RedoxFS partition on disk
2. Read filesystem header → get total filesystem size (e.g., 4093 MiB)
3. Live preload: read first N MiB of filesystem into RAM
- Cap: max_preload = 1024 MiB (line 559)
- Set env: DISK_LIVE_ADDR=<phys addr>, DISK_LIVE_SIZE=<preload size>
- Set env: REDOXFS_BLOCK=0 (start of partition)
4. Load kernel from RedoxFS into memory
5. Load initfs from RedoxFS into memory
6. Set up paging, pass env to kernel
7. Jump to kernel entry point
Kernel
→ bootstrap (initfs)
→ init daemon
→ lived daemon (10_lived.service)
- Reads DISK_LIVE_ADDR + DISK_LIVE_SIZE from env
- Maps preloaded RAM as LiveDisk via /scheme/memory/physical
- Registers scheme:disk.live
- LiveDisk.size() = preloaded size (1024 MiB)
- LiveDisk.block_size() = PAGE_SIZE (4096) [P6 patch changes to 512]
→ redoxfs daemon (50_rootfs.service)
- Opens /scheme/disk.live/0 as DiskFile
- Calls FileSystem::open(disk, password, block=0, cleanup=true)
- Reads header at block 0 (inside preloaded region → works)
- Calls fs.reset_allocator() → walks the allocation tree
- Calls fs.cleanup() → may read blocks across the entire filesystem
- FAILURE: any read beyond preloaded size returns EINVAL
```
### 1.2 Component Map
| Component | Source | Role |
|-----------|--------|------|
| **Bootloader** | `recipes/core/bootloader/source/src/main.rs` | Preloads filesystem into RAM, passes env vars |
| **lived** | `recipes/core/base/source/drivers/storage/lived/src/main.rs` | Maps preloaded RAM as `scheme:disk.live` |
| **RedoxFS mount** | `recipes/core/redoxfs/source/src/bin/mount.rs` | Opens disk scheme, calls FileSystem::open |
| **RedoxFS lib** | `recipes/core/redoxfs/source/src/filesystem.rs` | Reads header, walks allocator tree |
| **driver-block** | `recipes/core/base/source/drivers/storage/driver-block/src/lib.rs` | DiskWrapper with block_size alignment checks |
| **P6 patch** | `local/patches/base/P6-lived-block-size-512.patch` | Changes block_size from PAGE_SIZE to 512 |
### 1.3 The Preload Cap
```rust
// bootloader/src/main.rs:559
let max_preload: u64 = 1024 * MIBI as u64; // 1 GiB hard cap
let preload_size = if size > max_preload {
max_preload // Cap at 1 GiB
} else {
size // Preload entire filesystem if ≤ 1 GiB
};
```
For redbear-full (4093 MiB filesystem): preloads 1024 MiB, 3069 MiB must come from disk.
For redbear-mini (1533 MiB filesystem): preloads 1024 MiB, 509 MiB must come from disk.
### 1.4 The lived Disk
```rust
// lived/src/main.rs - LiveDisk::read (CURRENT, unpatched source)
fn block_size(&self) -> u32 {
PAGE_SIZE as u32 // P6 changes this to 512
}
fn size(&self) -> u64 {
self.original.len() as u64 // This is the PRELOADED size, not total filesystem size
}
async fn read(&mut self, mut block: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
let mut offset = (block as usize) * PAGE_SIZE;
if offset + buffer.len() > self.original.len() {
return Err(syscall::Error::new(EINVAL)); // ← THIS IS THE FAILURE POINT
}
// ... read from preloaded buffer
}
```
**The fundamental problem:** `lived` only has the preloaded buffer (1024 MiB). It has no
access to the remaining filesystem data on the physical disk. When RedoxFS tries to read
beyond 1024 MiB, lived returns EINVAL.
---
## 2. Failure Analysis
### 2.1 Why Does the Mini ISO Work?
The mini ISO (1533 MiB) also has 509 MiB beyond the preload. However:
1. RedoxFS `FileSystem::open` reads the header at block 0 (within preload) → OK
2. `reset_allocator` walks the free block tree. For a 1533 MiB filesystem with minimal
contents, the allocator metadata is concentrated near the start → likely within 1024 MiB
3. `cleanup` reads extent nodes — for a small filesystem, these are also near the start
For the full ISO (4093 MiB) with hundreds of packages:
- The allocator tree and extent nodes span the entire 4093 MiB range
- RedoxFS needs to read blocks at offsets > 1024 MiB during `FileSystem::open`
- lived rejects those reads → mount fails
**The mini ISO works by luck** — its metadata happens to fit within the preload window.
This is not a reliable design.
### 2.2 The Exact Error Chain
```
RedoxFS FileSystem::open
→ disk.read_at(block_N, &mut header)
→ DiskFile::read_at(buffer, block_N * BLOCK_SIZE)
→ syscall::read(scheme:disk.live/0, offset=block_N * 512)
→ lived::LiveDisk::read(block_N, buffer)
→ offset = block_N * PAGE_SIZE // or 512 with P6
→ if offset + buffer.len() > self.original.len():
return Err(EINVAL) // ← HERE
```
The error propagates:
- lived → EINVAL
- DiskFile → "RedoxFS: IO ERROR: Invalid argument (os error 22)"
- FileSystem::open → Err(EINVAL)
- mount.rs → "not able to mount uuid ..."
### 2.3 The P6 Block Size Patch
The P6 patch (`local/patches/base/P6-lived-block-size-512.patch`) fixes a different but
related issue: the original `block_size()` returned `PAGE_SIZE` (4096), but RedoxFS reads
in 512-byte chunks (`BLOCK_SIZE = 4096` but individual reads may be 512). The `DiskWrapper`
in `driver-block` rejects misaligned reads. Changing to 512 fixes alignment but does NOT
fix the size/out-of-bounds problem.
**Note:** The current source tree (`recipes/core/base/source/drivers/storage/lived/src/main.rs`)
does NOT have the P6 patch applied — it still shows `PAGE_SIZE as u32`. The P6 patch is
applied during `repo fetch base` and only exists durably in `local/patches/base/`.
---
## 3. Fix Strategy
### 3.1 Design Principle
> Preload the minimum needed to boot the kernel + initfs. Once the OS is running, mount
> the filesystem from the actual disk device, not from the RAM preload.
The bootloader already loads kernel + initfs from RedoxFS before switching to live mode.
After that, the running OS has access to the AHCI driver (ahcid) and can mount the
filesystem directly from the physical disk.
### 3.2 Two-Phase Approach
**Phase A: Bootloader Changes** (bootloader is UEFI code, runs before the OS)
1. **Reduce preload to the minimum needed for kernel + initfs discovery**
- The bootloader needs to read the RedoxFS superblock + directory tree to find
`usr/lib/boot/kernel` and `usr/lib/boot/initfs`. This requires reading the header,
the root node, and walking directory entries.
- Instead of preloading a fixed 1024 MiB, preload only what's needed to locate and
read these two files. In practice, this is the first few MiB of the filesystem.
- Fallback: if the filesystem is small enough (≤ 64 MiB?), preload everything.
2. **Pass the physical disk location to the kernel**
- Set `DISK_PHYS_ADDR` and `DISK_PHYS_SIZE` env vars with the full disk geometry
- Keep `DISK_LIVE_ADDR` / `DISK_LIVE_SIZE` for the minimal preload
- Add `REDOXFS_FULL_SIZE` so the OS knows the true filesystem extent
**Phase B: lived Daemon Changes** (OS-level, patchable via `local/patches/base/`)
1. **Accept the full filesystem size as an additional env var**
- Read `REDOXFS_FULL_SIZE` or derive from the RedoxFS header
- Report `LiveDisk::size()` as the FULL filesystem size, not just the preload
2. **Fall through to the physical disk for reads beyond the preload**
- When `read(block, buffer)` is called with an offset beyond `self.original.len()`:
- Open the underlying block device (e.g., `/scheme/disk/0` after ahcid starts)
- Read the data from the physical disk
- Cache the result in the overlay HashMap
- This makes lived act as a write-through cache: preload in RAM, fallback to disk
3. **Alternative simpler approach: bypass lived entirely for large images**
- After ahcid starts and registers `/scheme/disk/0`, the init system could mount
RedoxFS directly from `/scheme/disk/0` instead of `/scheme/disk.live/0`
- The preload would only be used by the bootloader to load kernel + initfs
- Once the OS boots, lived is unnecessary — mount from the real disk
---
## 4. Concrete Fix Plan
### 4.1 Fix 1: Reduce Bootloader Preload (bootloader patch)
**File:** `recipes/core/bootloader/source/src/main.rs`
**Current:**
```rust
let max_preload: u64 = 1024 * MIBI as u64;
```
**Proposed change:**
```rust
// Only preload what the bootloader actually needs:
// - RedoxFS header + allocator (first ~1 MiB)
// - Root directory tree (typically first 32-64 MiB)
// - kernel and initfs files (loaded separately after preload)
// 64 MiB is generous for the metadata region of any reasonable filesystem.
// The kernel and initfs are loaded separately via fs.disk.read_at() directly
// from the physical disk, so they don't need to be in the preload.
let max_preload: u64 = 64 * MIBI as u64;
```
Wait — this doesn't work. The bootloader reads kernel and initfs from the RedoxFS
filesystem using `load_to_memory(os, &mut fs, "usr/lib/boot/kernel", ...)`. After the
preload, the bootloader has already switched the disk to the live buffer. So the kernel
and initfs must be within the preload, OR the bootloader must load them before switching
to live mode.
**Looking at the actual bootloader flow:**
```
1. Open RedoxFS from physical disk → fs
2. Preload first N MiB into RAM buffer
3. Set LIVE_OPT = Some((fs.block, buffer))
4. Load kernel from fs (still using physical disk? or from buffer?)
5. Load initfs from fs
6. Pass LIVE_OPT to kernel env
```
The live buffer is set in `LIVE_OPT` at line 625, but the kernel and initfs are loaded
at lines 642-663, AFTER the live preload. The `load_to_memory` function uses `fs` which
still uses the original disk handle. So the kernel and initfs are read from the physical
disk, not from the live buffer.
**This means the preload doesn't need to include kernel or initfs at all.** The preload
exists solely so that `lived` can serve the filesystem to the running OS via `scheme:disk.live`.
**Revised Fix 1:** Reduce max_preload to a small value (e.g., 4-64 MiB) that covers just
the RedoxFS metadata needed for initial mount, then rely on the disk fallback for the rest.
BUT: this only works if `lived` can fall through to the physical disk for out-of-bounds
reads. Without the fallback, reducing preload makes the problem worse.
### 4.2 Fix 2: lived Disk Fallback (base patch)
**File:** `recipes/core/base/source/drivers/storage/lived/src/main.rs`
This is the core fix. Make `lived` aware of the full filesystem and able to read from
the physical disk when the preload doesn't cover the requested region.
**Design:**
```rust
struct LiveDisk {
// Preloaded RAM buffer (may be smaller than total filesystem)
preload: &'static [u8],
// Full filesystem size (from RedoxFS header or env var)
total_size: u64,
// Physical disk offset where the filesystem starts
disk_block: u64,
// Handle to the physical disk (opened after ahcid starts)
disk_handle: Option<File>,
// Write overlay (same as before)
overlay: HashMap<u64, Box<[u8]>>,
}
impl Disk for LiveDisk {
fn block_size(&self) -> u32 { 512 }
fn size(&self) -> u64 { self.total_size }
async fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
let bs = self.block_size() as usize;
let offset = (block as usize) * bs;
if offset + buffer.len() > self.total_size as usize {
return Err(syscall::Error::new(EINVAL));
}
let preload_bytes = self.preload.len();
for (i, chunk) in buffer.chunks_mut(bs).enumerate() {
let block_i = block + i as u64;
let offset_i = offset + i * bs;
// Check overlay first
if let Some(overlay) = self.overlay.get(&block_i) {
chunk.copy_from_slice(&overlay[..chunk.len()]);
continue;
}
if offset_i + chunk.len() <= preload_bytes {
// Within preload → read from RAM
chunk.copy_from_slice(&self.preload[offset_i..offset_i + chunk.len()]);
} else {
// Beyond preload → read from physical disk
self.read_from_disk(block_i, chunk)?;
}
}
Ok(buffer.len())
}
fn read_from_disk(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<()> {
// Try to open the physical disk if not already open
if self.disk_handle.is_none() {
// Try common disk scheme paths
for path in &["/scheme/disk/0", "/scheme/disk/1"] {
if let Ok(file) = OpenOptions::new().read(true).open(path) {
self.disk_handle = Some(file);
break;
}
}
}
if let Some(ref mut disk) = self.disk_handle {
// Seek to the correct block (accounting for partition offset)
let abs_block = self.disk_block + block;
disk.read_at(buffer, abs_block * self.block_size() as u64)
.map_err(|_| syscall::Error::new(EIO))?;
Ok(())
} else {
// No disk available yet — return what we have from preload
// (fill with zeros for regions not in preload)
buffer.fill(0);
Err(syscall::Error::new(EIO))
}
}
}
```
**Problem with this approach:** `lived` starts before `ahcid` (it's at priority 10 in
init.initfs.d, while ahcid is at priority 40). So when lived first starts, there IS no
`/scheme/disk/0` to fall back to. The disk fallback would only work after ahcid initializes.
### 4.3 Fix 3: Two-Stage Mount (Recommended)
The cleanest fix is to change the init sequence:
**Stage 1: lived serves the preloaded buffer (for early boot)**
- lived starts as before, serves the preload via `scheme:disk.live`
- RedoxFS does NOT mount from `disk.live` for the root filesystem
- The initfs has everything needed for early boot (lived, ahcid, basic tools)
**Stage 2: Mount from physical disk (after drivers start)**
- After `40_drivers.target` completes, ahcid has registered `/scheme/disk/0`
- Init runs `redoxfs --uuid $REDOXFS_UUID file $REDOXFS_BLOCK` pointing to `/scheme/disk/0`
- This reads the full filesystem from the physical disk
**Current init flow:**
```
10_lived.service → starts lived (scheme:disk.live)
40_drivers.target → starts ahcid (scheme:disk/0)
50_rootfs.service → redoxfs mounts from... whichever disk scheme it finds first
(scans all /scheme/disk/* for matching UUID)
```
**The issue is timing:** `50_rootfs.service` requires `40_drivers.target`, so it should
wait for ahcid. But `redoxfs` scans ALL disk schemes, and `disk.live` matches first
(since lived starts earlier). RedoxFS finds the UUID in `disk.live` and tries to mount
from it, but the disk is too small.
**Proposed fix:**
1. **Make lived report the full filesystem size** by reading the RedoxFS header from
the preload buffer to determine `total_size`. Report that as `size()`.
2. **Make lived fall through to disk reads** for out-of-bounds regions. Use a lazy-open
approach: when a read goes beyond the preload and no disk handle is open yet, try
to open `/scheme/disk/0`. If it fails, return EIO (which RedoxFS will retry).
3. **Reduce the preload** in the bootloader. Since lived now handles disk fallback,
we can preload much less (e.g., 4-64 MiB). The preload just needs to cover the
RedoxFS header and enough metadata for the initial mount.
---
## 5. Recommended Implementation Order
### Step 1: Fix lived to report full size + disk fallback (base patch)
Create `local/patches/base/P59-lived-disk-fallback.patch`:
1. Add `total_size: u64` field to LiveDisk
2. Parse RedoxFS header from preload buffer to determine total filesystem size
3. Report `total_size` from `size()` instead of `preload.len()`
4. For reads beyond preload: attempt to open and read from `/scheme/disk/0`
5. Keep overlay for writes
6. Keep block_size = 512 (from P6)
7. Add env var `DISK_PHYS_BLOCK` for the partition offset on the physical disk
### Step 2: Reduce bootloader preload cap (bootloader patch)
Create `local/patches/bootloader/P1-reduce-live-preload.patch`:
1. Change `max_preload` from 1024 MiB to a calculated minimum:
- Read the RedoxFS header to determine filesystem size
- Calculate the minimum preload needed: max(header + allocator extent, 4 MiB)
- Cap at 128 MiB (generous upper bound for metadata region)
2. Add `DISK_PHYS_BLOCK` env var so lived knows where the partition starts on disk
### Step 3: Verify
1. Build and test redbear-full ISO in QEMU with virtio-gpu
2. Verify RedoxFS mounts the full 4093 MiB filesystem
3. Verify login prompt appears
4. Verify KDE desktop loads (or at minimum, the greeter starts)
---
## 6. Risk Assessment
| Risk | Impact | Mitigation |
|------|--------|------------|
| RedoxFS header format changes between versions | lived parses header incorrectly | Use the same header parsing code as RedoxFS lib |
| ahcid not started when lived first needs disk | Read fails with ENOENT | Retry with backoff; RedoxFS mount retries automatically |
| Physical disk block offset wrong | Read corrupt data | Pass exact block offset from bootloader via env var |
| Preload too small for RedoxFS to find header | Mount fails immediately | Keep minimum preload at 4 MiB (covers any superblock) |
| Mini ISO regression | Small images broken | Test mini ISO after every change |
---
## 7. Alternative Approach: Mount From Physical Disk Directly
Instead of fixing lived, we could modify the init sequence to skip `disk.live` entirely
for the root filesystem mount:
1. Bootloader preloads just enough for kernel + initfs (no change needed)
2. lived starts but is only used for early boot I/O
3. `50_rootfs.service` is changed to explicitly mount from `/scheme/disk/0` (via ahcid)
instead of scanning all disk schemes
4. This requires passing the disk path and block offset from bootloader to init
**Pros:** Simpler lived (no disk fallback), cleaner architecture
**Cons:** Requires knowing which disk scheme serves the boot device; may not work if
the AHCI driver assigns a different number to the boot disk
**Verdict:** Fix 3 (lived with disk fallback) is more robust because it works regardless
of which disk scheme is assigned. The lived approach acts as a transparent cache layer.
---
## 8. Implementation Notes
### Bootloader env vars (current)
```
DISK_LIVE_ADDR=<hex phys addr of preload buffer>
DISK_LIVE_SIZE=<hex size of preload buffer>
REDOXFS_BLOCK=0 (always 0 for live mode)
REDOXFS_UUID=<uuid>
```
### Bootloader env vars (proposed additions)
```
DISK_PHYS_BLOCK=<hex block offset of partition on physical disk>
REDOXFS_FULL_SIZE=<hex total filesystem size>
```
### lived env vars (current)
```
DISK_LIVE_ADDR → phys addr to mmap
DISK_LIVE_SIZE → size to mmap (= preload size, NOT total filesystem size)
```
### lived env vars (proposed)
```
DISK_LIVE_ADDR → phys addr of preload buffer
DISK_LIVE_SIZE → size of preload buffer
DISK_PHYS_BLOCK → block offset for disk fallback reads
REDOXFS_FULL_SIZE → total filesystem size (for size() reporting)
```
@@ -0,0 +1,248 @@
diff --git a/drivers/storage/lived/src/main.rs b/drivers/storage/lived/src/main.rs
index 2ca1ff27..8582e42a 100644
--- a/drivers/storage/lived/src/main.rs
+++ b/drivers/storage/lived/src/main.rs
@@ -1,0 +2,8 @@
+//!
+//! For live ISO boot: bootloader preloads the first N MiB of the filesystem into RAM.
+//! This daemon serves that preloaded region from RAM, and falls through to the physical
+//! disk (USB, AHCI, NVMe) for reads beyond the preload boundary.
+//!
+//! Boot order: lived(10) → drivers(40) → usbscsid(45) → rootfs(50)
+//! Since drivers load AFTER lived starts, the disk fallback is lazy: the physical disk
+//! handle is opened on the first out-of-range read, with retry backoff.
@@ -4,0 +13 @@
+use std::cell::RefCell;
@@ -7 +15,0 @@ use std::fs::File;
-
@@ -8,0 +17 @@ use std::os::fd::AsRawFd;
+use std::sync::atomic::{AtomicBool, Ordering};
@@ -19,0 +29,11 @@ use anyhow::{anyhow, Context};
+/// Block size must be 512 (redoxfs BLOCK_SIZE), not PAGE_SIZE.
+/// DiskWrapper::read rejects buffers not aligned to block_size, and redoxfs reads
+/// in 512-byte chunks.
+const BLOCK_SIZE: usize = 512;
+
+/// Maximum retries for opening the physical disk before giving up.
+/// Drivers (xhcid → usbscsid → /scheme/disk/) load between service 40-45,
+/// while lived starts at 10. Give plenty of retries.
+const DISK_OPEN_MAX_RETRIES: u32 = 60;
+const DISK_OPEN_RETRY_INTERVAL_MS: u64 = 500;
+
@@ -22 +41,0 @@ struct LiveDisk {
- //TODO: drop overlay blocks if they match the original
@@ -23,0 +43,4 @@ struct LiveDisk {
+ full_size: u64,
+ disk_phys_offset: u64,
+ disk_file: RefCell<Option<File>>,
+ logged_first_fallback: AtomicBool,
@@ -27 +50,6 @@ impl LiveDisk {
- fn new(phys: usize, size: usize) -> anyhow::Result<LiveDisk> {
+ fn new(
+ phys: usize,
+ preload_size: usize,
+ full_size: u64,
+ disk_phys_offset: u64,
+ ) -> anyhow::Result<LiveDisk> {
@@ -30,2 +58,2 @@ impl LiveDisk {
- .checked_add(size)
- .context("phys + size overflow")?
+ .checked_add(preload_size)
+ .context("phys + preload_size overflow")?
@@ -33 +61 @@ impl LiveDisk {
- let size = end - start;
+ let mapped_size = end - start;
@@ -41 +69 @@ impl LiveDisk {
- length: size,
+ length: mapped_size,
@@ -47 +75 @@ impl LiveDisk {
- std::slice::from_raw_parts_mut(base as *mut u8, size)
+ std::slice::from_raw_parts_mut(base as *mut u8, mapped_size)
@@ -49,0 +78,7 @@ impl LiveDisk {
+ eprintln!(
+ "lived: preload {} MiB, full filesystem {} MiB, disk_phys_offset {:#x}",
+ preload_size / (1024 * 1024),
+ full_size / (1024 * 1024),
+ disk_phys_offset,
+ );
+
@@ -52,0 +88,4 @@ impl LiveDisk {
+ full_size,
+ disk_phys_offset,
+ disk_file: RefCell::new(None),
+ logged_first_fallback: AtomicBool::new(false),
@@ -54,0 +94,69 @@ impl LiveDisk {
+
+ fn open_disk(&self) -> syscall::Result<()> {
+ if self.disk_file.borrow().is_some() {
+ return Ok(());
+ }
+ match self.try_open_disk() {
+ Ok(file) => {
+ *self.disk_file.borrow_mut() = Some(file);
+ Ok(())
+ }
+ Err(msg) => {
+ eprintln!("lived: disk fallback unavailable: {}", msg);
+ Err(syscall::Error::new(EIO))
+ }
+ }
+ }
+
+ fn try_open_disk(&self) -> Result<File, String> {
+ // Try common disk scheme paths. USB boot appears as /scheme/disk/0 or /scheme/usbscsi/0.
+ // AHCI boot appears as /scheme/disk/0. NVMe appears as /scheme/disk/0.
+ let candidates = [
+ "/scheme/disk/0",
+ "/scheme/usbscsi/0",
+ "/scheme/disk/1",
+ "/scheme/usbscsi/1",
+ ];
+
+ for attempt in 0..DISK_OPEN_MAX_RETRIES {
+ for path in &candidates {
+ if let Ok(file) = File::open(path) {
+ eprintln!("lived: opened physical disk at {} (attempt {})", path, attempt + 1);
+ return Ok(file);
+ }
+ }
+
+ if attempt < DISK_OPEN_MAX_RETRIES - 1 {
+ std::thread::sleep(std::time::Duration::from_millis(DISK_OPEN_RETRY_INTERVAL_MS));
+ }
+ }
+
+ Err(format!(
+ "no /scheme/disk/ found after {} retries",
+ DISK_OPEN_MAX_RETRIES
+ ))
+ }
+
+ fn read_from_disk(&self, offset: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
+ let disk_offset = self.disk_phys_offset + offset;
+
+ if !self.logged_first_fallback.swap(true, Ordering::Relaxed) {
+ eprintln!(
+ "lived: first disk fallback read at offset {} MiB (disk offset {:#x})",
+ offset / (1024 * 1024),
+ disk_offset,
+ );
+ }
+
+ self.open_disk()?;
+
+ use std::io::{Read, Seek, SeekFrom};
+ let mut disk = self.disk_file.borrow_mut();
+ let file = disk.as_mut().unwrap();
+ file.seek(SeekFrom::Start(disk_offset))
+ .map_err(|_| syscall::Error::new(EIO))?;
+ file.read_exact(buffer)
+ .map_err(|_| syscall::Error::new(EIO))?;
+
+ Ok(buffer.len())
+ }
@@ -59 +167 @@ impl Disk for LiveDisk {
- PAGE_SIZE as u32
+ BLOCK_SIZE as u32
@@ -63 +171 @@ impl Disk for LiveDisk {
- self.original.len() as u64
+ self.full_size
@@ -67,2 +175,4 @@ impl Disk for LiveDisk {
- let mut offset = (block as usize) * PAGE_SIZE;
- if offset + buffer.len() > self.original.len() {
+ let bs = self.block_size() as usize;
+ let mut offset = (block as usize) * bs;
+
+ if offset + buffer.len() > self.full_size as usize {
@@ -71 +181,28 @@ impl Disk for LiveDisk {
- for chunk in buffer.chunks_mut(PAGE_SIZE) {
+
+ let preload_len = self.original.len();
+
+ if offset + buffer.len() <= preload_len {
+ for chunk in buffer.chunks_mut(bs) {
+ match self.overlay.get(&block) {
+ Some(overlay) => {
+ chunk.copy_from_slice(&overlay[..chunk.len()]);
+ }
+ None => {
+ chunk.copy_from_slice(&self.original[offset..offset + chunk.len()]);
+ }
+ }
+ block += 1;
+ offset += bs;
+ }
+ return Ok(buffer.len());
+ }
+
+ if offset >= preload_len {
+ let fs_byte_offset = (block as u64) * bs as u64;
+ return self.read_from_disk(fs_byte_offset, buffer);
+ }
+
+ let preload_remaining = preload_len - offset;
+ let (ram_part, disk_part) = buffer.split_at_mut(preload_remaining);
+
+ for chunk in ram_part.chunks_mut(bs) {
@@ -81 +218 @@ impl Disk for LiveDisk {
- offset += PAGE_SIZE;
+ offset += bs;
@@ -82,0 +220,4 @@ impl Disk for LiveDisk {
+
+ let disk_fs_offset = (block as u64) * bs as u64;
+ self.read_from_disk(disk_fs_offset, disk_part)?;
+
@@ -87,2 +228,3 @@ impl Disk for LiveDisk {
- let mut offset = (block as usize) * PAGE_SIZE;
- if offset + buffer.len() > self.original.len() {
+ let bs = self.block_size() as usize;
+ let mut offset = (block as usize) * bs;
+ if offset + buffer.len() > self.full_size as usize {
@@ -91 +233 @@ impl Disk for LiveDisk {
- for chunk in buffer.chunks(PAGE_SIZE) {
+ for chunk in buffer.chunks(bs) {
@@ -93,2 +235,3 @@ impl Disk for LiveDisk {
- let offset = (block as usize) * PAGE_SIZE;
- self.original[offset..offset + PAGE_SIZE]
+ let off = (block as usize) * bs;
+ if off + bs <= self.original.len() {
+ self.original[off..off + bs]
@@ -96,0 +240,3 @@ impl Disk for LiveDisk {
+ } else {
+ vec![0u8; bs].into_boxed_slice()
+ }
@@ -100 +246 @@ impl Disk for LiveDisk {
- offset += PAGE_SIZE;
+ offset += bs;
@@ -112 +258,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- let mut size = 0;
+ let mut preload_size = 0;
+ let mut full_size = 0u64;
+ let mut disk_phys_offset = 0u64;
@@ -129 +277 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- size = usize::from_str_radix(value, 16).unwrap_or(0);
+ preload_size = usize::from_str_radix(value, 16).unwrap_or(0);
@@ -130,0 +279,3 @@ fn daemon(daemon: daemon::Daemon) -> ! {
+
+ if name == "REDOXFS_FULL_SIZE" {
+ full_size = u64::from_str_radix(value, 16).unwrap_or(0);
@@ -133,2 +284,6 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- if phys == 0 || size == 0 {
- // No live disk data, no need to say anything or exit with
+ if name == "DISK_PHYS_BLOCK" {
+ disk_phys_offset = u64::from_str_radix(value, 16).unwrap_or(0) * BLOCK_SIZE as u64;
+ }
+ }
+
+ if phys == 0 || preload_size == 0 {
@@ -138,0 +294,4 @@ fn daemon(daemon: daemon::Daemon) -> ! {
+ if full_size == 0 {
+ full_size = preload_size as u64;
+ }
+
@@ -152 +311 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- LiveDisk::new(phys, size).unwrap_or_else(|err| {
+ LiveDisk::new(phys, preload_size, full_size, disk_phys_offset).unwrap_or_else(|err| {
@@ -160 +319 @@ fn daemon(daemon: daemon::Daemon) -> ! {
- libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
+ libredox::call::setrens(0, 0).expect("lived: failed to enter null namespace");
@@ -0,0 +1,118 @@
--- a/drivers/storage/lived/src/main.rs
+++ b/drivers/storage/lived/src/main.rs
@@ -45 +45 @@
- disk_file: RefCell<Option<File>>,
+ disk_path: RefCell<Option<String>>,
@@ -90 +90 @@
- disk_file: RefCell::new(None),
+ disk_path: RefCell::new(None),
@@ -96 +96 @@
- if self.disk_file.borrow().is_some() {
+ if self.disk_path.borrow().is_some() {
@@ -100,2 +100,2 @@
- Ok(file) => {
- *self.disk_file.borrow_mut() = Some(file);
+ Ok(path) => {
+ *self.disk_path.borrow_mut() = Some(path);
@@ -111,10 +111 @@
- fn try_open_disk(&self) -> Result<File, String> {
- // Try common disk scheme paths. USB boot appears as /scheme/disk/0 or /scheme/usbscsi/0.
- // AHCI boot appears as /scheme/disk/0. NVMe appears as /scheme/disk/0.
- let candidates = [
- "/scheme/disk/0",
- "/scheme/usbscsi/0",
- "/scheme/disk/1",
- "/scheme/usbscsi/1",
- ];
-
+ fn try_open_disk(&self) -> Result<String, String> {
@@ -122,4 +113,70 @@
- for path in &candidates {
- if let Ok(file) = File::open(path) {
- eprintln!("lived: opened physical disk at {} (attempt {})", path, attempt + 1);
- return Ok(file);
+ if let Ok(entries) = std::fs::read_dir("/scheme") {
+ let all_schemes: Vec<String> = entries
+ .flatten()
+ .map(|e| e.file_name().to_string_lossy().to_string())
+ .collect();
+ let has_disk = all_schemes
+ .iter()
+ .any(|s| s.starts_with("disk.") && s != "disk.live");
+ if attempt == 0 || attempt == DISK_OPEN_MAX_RETRIES - 1 || has_disk {
+ eprintln!(
+ "lived: attempt {} /scheme/ = {:?} (has_disk={})",
+ attempt + 1,
+ all_schemes,
+ has_disk
+ );
+ }
+ for name_str in &all_schemes {
+ if name_str.starts_with("disk.") && name_str != "disk.live" {
+ for idx in [0u32, 2, 1, 3, 4, 5] {
+ let path = format!("/scheme/{}/{}", name_str, idx);
+ if let Ok(mut file) = File::open(&path) {
+ use std::io::{Read, Seek, SeekFrom};
+ let test_offset = self.disk_phys_offset + self.full_size - 4096;
+ let mut probe = vec![0u8; 4096];
+ if file.seek(SeekFrom::Start(test_offset)).is_err() {
+ eprintln!(
+ "lived: skipping {} — seek to {:#x} failed",
+ path, test_offset
+ );
+ continue;
+ }
+ match file.read_exact(&mut probe) {
+ Ok(()) => {
+ eprintln!(
+ "lived: validated physical disk at {} (attempt {}, verified at end {:#x})",
+ path,
+ attempt + 1,
+ test_offset
+ );
+ return Ok(path);
+ }
+ Err(e) => {
+ eprintln!(
+ "lived: skipping {} — read at {:#x}: {}",
+ path, test_offset, e
+ );
+ continue;
+ }
+ }
+ } else if attempt == 0 {
+ eprintln!("lived: {} not openable", path);
+ }
+ }
+ let path_root = format!("/scheme/{}", name_str);
+ if let Ok(mut file) = File::open(&path_root) {
+ use std::io::{Read, Seek, SeekFrom};
+ let test_offset = self.disk_phys_offset + self.full_size - 4096;
+ let mut probe = vec![0u8; 4096];
+ if file.seek(SeekFrom::Start(test_offset)).is_ok()
+ && file.read_exact(&mut probe).is_ok()
+ {
+ eprintln!(
+ "lived: validated physical disk at {} (attempt {})",
+ path_root,
+ attempt + 1
+ );
+ return Ok(path_root);
+ }
+ }
+ }
@@ -152,0 +210,4 @@
+ let path = self.disk_path.borrow();
+ let disk_path = path.as_ref().unwrap();
+ let mut file = File::open(disk_path).map_err(|_| syscall::Error::new(EIO))?;
+
@@ -154,2 +214,0 @@
- let mut disk = self.disk_file.borrow_mut();
- let file = disk.as_mut().unwrap();
@@ -319,2 +378,4 @@
- libredox::call::setrens(0, 0).expect("lived: failed to enter null namespace");
-
+ // Lived must NOT call setrens(0, 0). The null namespace only contains
+ // "memory" and "pipe" (see relibc redox_setrens_v1). Lived needs
+ // ongoing access to /scheme/ for disk scheme discovery after storage
+ // drivers register their schemes asynchronously.
@@ -0,0 +1,202 @@
diff --git a/logd/src/scheme.rs b/logd/src/scheme.rs
index 070de3d6..f5c1549a 100644
--- a/logd/src/scheme.rs
+++ b/logd/src/scheme.rs
@@ -1,2 +1,2 @@
-use std::collections::{BTreeMap, VecDeque};
-use std::fs::{File, OpenOptions};
+use std::collections::{BTreeMap, HashMap, VecDeque};
+use std::fs::{File, OpenOptions, rename};
@@ -5,0 +6 @@ use std::os::fd::{FromRawFd, RawFd};
+use std::path::PathBuf;
@@ -6,0 +8 @@ use std::sync::mpsc::{self, Sender};
+use std::time::{SystemTime, UNIX_EPOCH};
@@ -13,0 +16,5 @@ use syscall::schemev2::NewFdFlags;
+const LOG_DIR: &str = "/var/log";
+const MAX_LOG_SIZE: u64 = 10 * 1024 * 1024;
+const MAX_ROTATED_FILES: u32 = 5;
+const MEMORY_LOG_LIMIT: usize = 1000;
+
@@ -31 +38 @@ enum OutputCmd {
- Log(Vec<u8>),
+ Log { context: String, line: Vec<u8> },
@@ -34,0 +42,96 @@ enum OutputCmd {
+fn json_escape(s: &str) -> String {
+ let mut out = String::with_capacity(s.len());
+ for c in s.chars() {
+ match c {
+ '\\' => out.push_str("\\\\"),
+ '"' => out.push_str("\\\""),
+ '\n' => out.push_str("\\n"),
+ '\r' => out.push_str("\\r"),
+ '\t' => out.push_str("\\t"),
+ c if c.is_control() => out.push_str(&format!("\\u{:04x}", c as u32)),
+ c => out.push(c),
+ }
+ }
+ out
+}
+
+fn format_json_line(context: &str, line: &[u8]) -> Vec<u8> {
+ let now = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .unwrap_or_default();
+ let secs = now.as_secs();
+ let timestamp = format!(
+ "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
+ 1970 + secs / 31_557_600,
+ (secs % 31_557_600) / 2_592_000 + 1,
+ (secs % 2_592_000) / 86_400 + 1,
+ (secs % 86_400) / 3600,
+ (secs % 3600) / 60,
+ secs % 60
+ );
+ let text = String::from_utf8_lossy(line).trim_end_matches('\n').to_string();
+ let (source, message) = match text.split_once(": ") {
+ Some((s, m)) => (s, m),
+ None => (context, text.as_str()),
+ };
+ let json = format!(
+ "{{\"timestamp\":\"{}\",\"source\":\"{}\",\"message\":\"{}\"}}\n",
+ json_escape(&timestamp),
+ json_escape(source),
+ json_escape(message)
+ );
+ json.into_bytes()
+}
+
+struct LogFile {
+ file: File,
+ path: PathBuf,
+ bytes_written: u64,
+}
+
+impl LogFile {
+ fn open(path: PathBuf) -> std::io::Result<Self> {
+ let file = OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open(&path)?;
+ let metadata = file.metadata()?;
+ Ok(LogFile {
+ file,
+ path,
+ bytes_written: metadata.len(),
+ })
+ }
+
+ fn write(&mut self, data: &[u8]) -> std::io::Result<()> {
+ self.file.write_all(data)?;
+ self.file.flush()?;
+ self.bytes_written += data.len() as u64;
+ Ok(())
+ }
+
+ fn maybe_rotate(&mut self) -> std::io::Result<()> {
+ if self.bytes_written < MAX_LOG_SIZE {
+ return Ok(());
+ }
+
+ drop(std::mem::replace(&mut self.file, unsafe { File::from_raw_fd(-1) }));
+
+ for i in (1..MAX_ROTATED_FILES).rev() {
+ let old_path = self.path.with_extension(format!("log.{}", i));
+ let new_path = self.path.with_extension(format!("log.{}", i + 1));
+ let _ = rename(&old_path, &new_path);
+ }
+
+ let backup_path = self.path.with_extension("log.1");
+ let _ = rename(&self.path, &backup_path);
+
+ self.file = OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open(&self.path)?;
+ self.bytes_written = 0;
+ Ok(())
+ }
+}
+
@@ -43,0 +147,13 @@ impl<'sock> LogScheme<'sock> {
+ let _ = std::fs::create_dir_all("/var/log");
+ let persistent_log: Option<File> = OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open("/var/log/system.log")
+ .ok();
+ let mut service_logs: HashMap<String, LogFile> = HashMap::new();
+
+ let _ = std::fs::create_dir_all(LOG_DIR);
+
+
+ let json_format = std::env::var("LOGD_JSON").map_or(false, |v| v == "1");
+
@@ -48,0 +165,5 @@ impl<'sock> LogScheme<'sock> {
+ let mut persistent = persistent_log;
+ if let Some(ref mut f) = persistent {
+ let _ = f.write(b"--- logd started ---
+");
+ }
@@ -51 +172,32 @@ impl<'sock> LogScheme<'sock> {
- OutputCmd::Log(line) => {
+ OutputCmd::Log { context, line } => {
+ let out_line = if json_format {
+ format_json_line(&context, &line)
+ } else {
+ line.clone()
+ };
+ if let Some(ref mut f) = persistent {
+ let _ = f.write(&out_line);
+ let _ = f.flush();
+ }
+
+ let service_name = context.split(':').next().unwrap_or("unknown");
+ if !service_name.is_empty() {
+ let log_path = PathBuf::from(LOG_DIR).join(format!("{}.log", service_name));
+ let entry = service_logs.entry(service_name.to_string()).or_insert_with(|| {
+ LogFile::open(log_path).unwrap_or_else(|_| {
+ LogFile::open(PathBuf::from("/dev/null")).unwrap()
+ })
+ });
+ let _ = entry.write(&out_line);
+ let _ = entry.maybe_rotate();
+ }
+
+ let system_path = PathBuf::from(LOG_DIR).join("system.log");
+ let system_entry = service_logs.entry("system".to_string()).or_insert_with(|| {
+ LogFile::open(system_path).unwrap_or_else(|_| {
+ LogFile::open(PathBuf::from("/dev/null")).unwrap()
+ })
+ });
+ let _ = system_entry.write(&out_line);
+ let _ = system_entry.maybe_rotate();
+
@@ -53 +205 @@ impl<'sock> LogScheme<'sock> {
- let _ = file.write(&line);
+ let _ = file.write(&out_line);
@@ -56,3 +208,2 @@ impl<'sock> LogScheme<'sock> {
- logs.push_back(line);
- // Keep a limited amount of logs for backfilling to bound memory usage
- while logs.len() > 1000 {
+ logs.push_back(out_line);
+ while logs.len() > MEMORY_LOG_LIMIT {
@@ -68 +218,0 @@ impl<'sock> LogScheme<'sock> {
-
@@ -83 +232,0 @@ impl<'sock> LogScheme<'sock> {
- // FIXME currently possible as /scheme/log/kernel presents a snapshot of the log queue
@@ -118 +266,0 @@ impl<'sock> LogScheme<'sock> {
- // Writing to the kernel debug log never blocks
@@ -124 +272,4 @@ impl<'sock> LogScheme<'sock> {
- .send(OutputCmd::Log(mem::take(handle_buf)))
+ .send(OutputCmd::Log {
+ context: context.to_string(),
+ line: mem::take(handle_buf),
+ })
@@ -173,3 +323,0 @@ impl<'sock> SchemeSync for LogScheme<'sock> {
-
- // TODO
-
@@ -244,3 +391,0 @@ impl<'sock> SchemeSync for LogScheme<'sock> {
-
- //TODO: flush remaining data?
-
@@ -0,0 +1,48 @@
diff --git a/src/main.rs b/src/main.rs
index 78dabb0..a41086e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -557,0 +558 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
+ let disk_block = fs.block;
@@ -559 +560,13 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- print!("live: 0/{} MiB", size / MIBI as u64);
+ let max_preload: u64 = 128 * MIBI as u64;
+ let preload_size = if size > max_preload {
+ println!(
+ "live: filesystem is {} MiB, capping preload at {} MiB",
+ size / MIBI as u64,
+ max_preload / MIBI as u64
+ );
+ max_preload
+ } else {
+ size
+ };
+
+ print!("live: 0/{} MiB", preload_size / MIBI as u64);
@@ -561 +574 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- let live_size = match usize::try_from(size) {
+ let live_size = match usize::try_from(preload_size) {
@@ -593 +606 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
+ print!("\rlive: {}/{} MiB", i / MIBI as u64, preload_size / MIBI as u64);
@@ -600 +613,10 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
+ println!("\rlive: {}/{} MiB", i / MIBI as u64, preload_size / MIBI as u64);
+
+ if preload_size < size {
+ println!(
+ "live: preloaded {} MiB of {} MiB filesystem (remaining {} MiB from disk)",
+ preload_size / MIBI as u64,
+ size / MIBI as u64,
+ (size - preload_size) / MIBI as u64
+ );
+ }
@@ -613 +635 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- Some(live)
+ Some((disk_block, size, live))
@@ -672 +694 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- if let Some(live) = live_opt {
+ if let Some((disk_block, fs_size, live)) = live_opt {
@@ -674,0 +697,2 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
+ writeln!(w, "DISK_PHYS_BLOCK={:016x}", disk_block).unwrap();
+ writeln!(w, "REDOXFS_FULL_SIZE={:016x}", fs_size).unwrap();
@@ -0,0 +1,91 @@
diff --git a/src/arch/x86/x64.rs b/src/arch/x86/x64.rs
index a0a275a..cad4592 100644
--- a/src/arch/x86/x64.rs
+++ b/src/arch/x86/x64.rs
@@ -46 +46 @@ pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) ->
- for pdp_i in 0..8 {
+ for pdp_i in 0..64 {
@@ -103 +103 @@ pub unsafe fn paging_framebuffer(
- if framebuffer_phys + framebuffer_size <= 0x2_0000_0000 {
+ if framebuffer_phys + framebuffer_size <= 0x10_0000_0000 {
diff --git a/src/main.rs b/src/main.rs
index a41086e..1ce4bf3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -560,11 +560 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- let max_preload: u64 = 128 * MIBI as u64;
- let preload_size = if size > max_preload {
- println!(
- "live: filesystem is {} MiB, capping preload at {} MiB",
- size / MIBI as u64,
- max_preload / MIBI as u64
- );
- max_preload
- } else {
- size
- };
+ let mut preload_size: u64 = size;
@@ -572,4 +562,5 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- print!("live: 0/{} MiB", preload_size / MIBI as u64);
-
- let live_size = match usize::try_from(preload_size) {
- Ok(live_size) => live_size,
+ let mut ptr = ptr::null_mut();
+ if live {
+ ptr = os.alloc_zeroed_page_aligned(
+ match usize::try_from(preload_size) {
+ Ok(s) => s,
@@ -581 +572,19 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- };
+ }
+ );
+ if ptr.is_null() && live {
+ println!(
+ "\rlive: unable to allocate {} MiB, halving...",
+ preload_size / MIBI as u64
+ );
+ let floor: u64 = 128 * MIBI as u64;
+ while preload_size > floor {
+ preload_size /= 2;
+ if let Ok(smaller) = usize::try_from(preload_size) {
+ ptr = os.alloc_zeroed_page_aligned(smaller);
+ if !ptr.is_null() {
+ break;
+ }
+ }
+ }
+ }
+ }
@@ -583,6 +592 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
- let ptr = if live {
- os.alloc_zeroed_page_aligned(live_size)
- } else {
- ptr::null_mut()
- };
- if live && ptr.is_null() {
+ if ptr.is_null() {
@@ -595,0 +600,11 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
+ print!("live: 0/{} MiB", preload_size / MIBI as u64);
+
+ let live_size = match usize::try_from(preload_size) {
+ Ok(live_size) => live_size,
+ Err(_) => {
+ println!("\rlive: disabled (image too large for bootloader address space)");
+ live = false;
+ 0
+ }
+ };
+
diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs
index d0b2cf1..dacead6 100644
--- a/src/os/uefi/mod.rs
+++ b/src/os/uefi/mod.rs
@@ -48,2 +48 @@ pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 {
- // Max address mapped by src/arch paging code (8 GiB)
- let mut ptr = 0x2_0000_0000;
+ let mut ptr = 0;
@@ -51,2 +50,2 @@ pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 {
- 1, // AllocateMaxAddress
- MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list
+ 0, // AllocateAnyPages
+ MemoryType::EfiLoaderData, // Valid until ExitBootServices
@@ -0,0 +1,107 @@
diff --git a/src/devices/graphical_debug/debug.rs b/src/devices/graphical_debug/debug.rs
index 4b684c8a..e3fe0472 100644
--- a/src/devices/graphical_debug/debug.rs
+++ b/src/devices/graphical_debug/debug.rs
@@ -29,0 +30,3 @@ impl DebugDisplay {
+ const CHAR_WIDTH: usize = 8;
+ const CHAR_HEIGHT: usize = 16;
+
@@ -43,2 +46,2 @@ impl DebugDisplay {
- let w = display.width / 8;
- let h = display.height / 16;
+ let w = display.width / Self::CHAR_WIDTH;
+ let h = display.height / Self::CHAR_HEIGHT;
@@ -54,0 +58,21 @@ impl DebugDisplay {
+ fn scroll_up(&mut self) {
+ let stride = self.display.stride;
+ let width = self.display.width;
+ let total_height = self.display.height;
+ let scroll_px = Self::CHAR_HEIGHT;
+
+ unsafe {
+ let ptr = self.display.onscreen_ptr;
+ for row in 0..total_height - scroll_px {
+ ptr::copy(
+ ptr.add((row + scroll_px) * stride),
+ ptr.add(row * stride),
+ width,
+ );
+ }
+ for row in total_height - scroll_px..total_height {
+ ptr::write_bytes(ptr.add(row * stride), 0, width);
+ }
+ }
+ }
+
@@ -59 +83,5 @@ impl DebugDisplay {
- self.y = (self.y + 1) % self.h;
+ self.y += 1;
+ if self.y >= self.h {
+ self.scroll_up();
+ self.y = self.h - 1;
+ }
@@ -67 +94,0 @@ impl DebugDisplay {
- // Byte 0x1B starts ESC sequence
@@ -72 +98,0 @@ impl DebugDisplay {
- // Ignore other nonprintable characters
@@ -77 +102,0 @@ impl DebugDisplay {
- // '[' after ESC starts CSI sequence
@@ -82 +106,0 @@ impl DebugDisplay {
- // Capture any bytes after ESC
@@ -87 +110,0 @@ impl DebugDisplay {
- // Byte 0x40 to 0x7E ends CSI
@@ -92 +114,0 @@ impl DebugDisplay {
- // Capture any bytes after CSI
@@ -96 +117,0 @@ impl DebugDisplay {
- // Allow any other bytes
@@ -102 +122,0 @@ impl DebugDisplay {
- self.clear_row((self.y + 1) % self.h);
@@ -105 +125 @@ impl DebugDisplay {
- self.char(self.x * 8, self.y * 16, b as char, 0xFFFFFF);
+ self.char(self.x * Self::CHAR_WIDTH, self.y * Self::CHAR_HEIGHT, b as char, 0xFFFFFF);
@@ -112 +132 @@ impl DebugDisplay {
- for row in y * 16..(y + 1) * 16 {
+ for row in y * Self::CHAR_HEIGHT..(y + 1) * Self::CHAR_HEIGHT {
@@ -123 +142,0 @@ impl DebugDisplay {
- /// Draw a character
@@ -125,7 +144,8 @@ impl DebugDisplay {
- if x + 8 <= self.display.width && y + 16 <= self.display.height {
- let phys_y = y % self.display.height;
- let mut dst = unsafe {
- self.display
- .onscreen_ptr
- .add(phys_y * self.display.stride + x)
- };
+ if x + Self::CHAR_WIDTH > self.display.width || y + Self::CHAR_HEIGHT > self.display.height {
+ return;
+ }
+
+ let font_i = Self::CHAR_HEIGHT * (character as usize);
+ if font_i + Self::CHAR_HEIGHT > FONT.len() {
+ return;
+ }
@@ -133,6 +152,0 @@ impl DebugDisplay {
- let font_i = 16 * (character as usize);
- if font_i + 16 <= FONT.len() {
- for row in 0..16 {
- let row_data = FONT[font_i + row];
- for col in 0..8 {
- if (row_data >> (7 - col)) & 1 == 1 {
@@ -139,0 +154,7 @@ impl DebugDisplay {
+ let ptr = self.display.onscreen_ptr;
+ let stride = self.display.stride;
+ for row in 0..Self::CHAR_HEIGHT {
+ let row_data = FONT[font_i + row];
+ let dst = ptr.add((y + row) * stride + x);
+ for col in 0..Self::CHAR_WIDTH {
+ if (row_data >> (Self::CHAR_WIDTH - 1 - col)) & 1 == 1 {
@@ -144,9 +164,0 @@ impl DebugDisplay {
-
- let next_phys_y = (phys_y + row + 1) % self.display.height;
- dst = unsafe {
- self.display
- .onscreen_ptr
- .add(next_phys_y * self.display.stride + x)
- };
- }
- }
+10 -2
View File
@@ -125,6 +125,8 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
daemon.ready();
let mut handler = ReadinessBased::new(&socket, 16);
let mut scheme_err_logged = false;
let mut response_err_logged = false;
loop {
match handler.read_requests() {
Ok(true) => {}
@@ -133,7 +135,10 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
break;
}
Err(e) => {
error!("redox-drm: failed to receive scheme request: {}", e);
if !scheme_err_logged {
error!("redox-drm: failed to receive scheme request: {} (suppressing further errors)", e);
scheme_err_logged = true;
}
continue;
}
}
@@ -141,7 +146,10 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
handler.process_requests(|| drm_scheme.lock().expect("DRM scheme state poisoned"));
if let Err(e) = handler.write_responses() {
error!("redox-drm: failed to write scheme responses: {}", e);
if !response_err_logged {
error!("redox-drm: failed to write scheme responses: {} (suppressing further errors)", e);
response_err_logged = true;
}
}
}
@@ -57,7 +57,7 @@ add_subdirectory(src)
# Enable unit testing
if (BUILD_TESTING)
############################################################ add_subdirectory(autotests)
############################################################# add_subdirectory(autotests)
add_subdirectory(tests)
endif ()
@@ -19,6 +19,7 @@ cmake "${COOKBOOK_SOURCE}" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
-DBUILD_TESTING=OFF \
-DBUILD_DOC=OFF \
-DBUILD_QCH=OFF \
-Wno-dev
@@ -144,6 +144,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
# shall we use DBus?
# enabled per default on Linux & BSD systems
@@ -122,6 +122,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
@@ -113,6 +113,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(KF6Codecs ${KF_DEP_VERSION} REQUIRED)
find_package(KF6Config ${KF_DEP_VERSION} REQUIRED)
@@ -116,6 +116,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
# shall we use DBus?
# enabled per default on Linux & BSD systems
@@ -32,7 +32,7 @@ find_package(KF6GuiAddons ${KF_DEP_VERSION} REQUIRED)
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT REDOX)
################################################################################### find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED)
#################################################################################### find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED)
set(HAVE_KGLOBALACCEL TRUE)
else()
set(HAVE_KGLOBALACCEL FALSE)
@@ -136,6 +136,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6Svg ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
# shall we use DBus?
@@ -38,7 +38,7 @@ set_package_properties(Qt6Qml PROPERTIES
)
if (TARGET Qt6::Qml)
############################################################## include(ECMQmlModule)
############################################################### include(ECMQmlModule)
endif()
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
@@ -1,6 +1,6 @@
add_subdirectory(core)
if (TARGET Qt6::Qml)
############################################################# add_subdirectory(qml)
############################################################## add_subdirectory(qml)
endif()
ecm_qt_install_logging_categories(
@@ -102,6 +102,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
@@ -102,6 +102,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT HAIKU)
option(WITH_X11 "Build with support for QX11Info::appUserTime()" ON)
@@ -117,6 +117,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
if (WITH_TEXT_TO_SPEECH)
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED TextToSpeech)
@@ -122,6 +122,7 @@ find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
find_package(Qt6WaylandClientPrivate REQUIRED)
set_package_properties(Wayland PROPERTIES
TYPE REQUIRED
)
@@ -74,10 +74,10 @@ void initializeLanguages()
// Ideally setting the LANGUAGE would change the default QLocale too
// but unfortunately this is too late since the QCoreApplication constructor
// already created a QLocale at this stage so we need to set the reset it
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale
// this is highly dependent on Qt internals, so may break, but oh well
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy;
}
}
@@ -65,9 +65,9 @@ ecm_set_disabled_deprecation_versions(
)
add_subdirectory( src )
#################################if (BUILD_TESTING)
################################# add_subdirectory( autotests )
#################################endif()
##################################if (BUILD_TESTING)
################################## add_subdirectory( autotests )
##################################endif()
if (BUILD_QCH)
ecm_install_qch_export(
@@ -78,7 +78,7 @@ set_package_properties(PList PROPERTIES
if (CMAKE_SYSTEM_NAME MATCHES Linux)
# Used by the UDisks backend on Linux
####################################################################################################find_package(LibMount)
#####################################################################################################find_package(LibMount)
set_package_properties(LibMount PROPERTIES
TYPE REQUIRED)
endif()
@@ -124,6 +124,7 @@
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include "config-kwin.h"
@@ -1,2 +1,2 @@
#####################################################################################################################add_subdirectory(killer) # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only
######################################################################################################################add_subdirectory(killer) # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only # disabled: X11-only
add_subdirectory(wayland_wrapper)
@@ -349,6 +349,9 @@
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
#ifndef SUN_LEN
#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))
#endif
/*
KWin - the KDE window manager
This file is part of the KDE project.
@@ -10,5 +10,5 @@ target_link_libraries(systembell PRIVATE
KF6::GlobalAccel
KF6::I18n
$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,Canberra::Canberra,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>
$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,$<IF:$<BOOL:${Canberra_FOUND}>,Canberra::Canberra,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>
)
@@ -124,6 +124,7 @@
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include <libudev.h>
#include "backends/libinput/device.h"
#include "core/inputdevice.h"
@@ -700,6 +700,12 @@
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
#define F_ADD_SEALS 1033
#define F_GET_SEALS 1034
#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008
/*
KWin - the KDE window manager
This file is part of the KDE project.
@@ -198,6 +198,14 @@
#endif
#endif
#ifdef Q_OS_REDOX
#undef QT_USE_XOPEN_LFS_EXTENSIONS
#undef QT_LARGEFILE_SUPPORT
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
#endif
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
@@ -1375,6 +1375,13 @@ qt_internal_extend_target(Core CONDITION REDOX
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_cpp_winrt
SOURCES
platform/windows/qfactorycacheregistration_p.h
@@ -1578,6 +1585,13 @@ qt_internal_extend_target(Core CONDITION REDOX
io/qstorageinfo_unix.cpp
)
# Redox: POSIX statvfs, not Linux statfs
qt_internal_extend_target(Core CONDITION REDOX
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_itemmodel
SOURCES
itemmodels/qabstractitemmodel.cpp itemmodels/qabstractitemmodel.h itemmodels/qabstractitemmodel_p.h
@@ -202,6 +202,7 @@ static_assert(std::is_signed_v<qint128>,
#include <assert.h>
#include <assert.h>
#include <assert.h>
#include <assert.h>
#ifndef static_assert
#define static_assert _Static_assert
#endif
@@ -1146,6 +1146,7 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
#ifdef IPV6_HOPLIMIT
if (header.hopLimit != -1) {
msg.msg_controllen += CMSG_SPACE(sizeof(int));
@@ -1179,6 +1180,7 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
#endif
#endif
#endif
#endif
#endif
if (header.ifindex != 0 || !header.senderAddress.isNull()) {
struct in6_pktinfo *data = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cmsgptr));
@@ -46,6 +46,7 @@
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#if defined(Q_OS_VXWORKS)
@@ -76,6 +76,7 @@ public:
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
virtual QPlatformOpenGLContext *createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const = 0;
#endif /* QT_CONFIG(opengl) */
@@ -102,6 +103,7 @@ public:
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
virtual bool canCreatePlatformOffscreenSurface() const { return false; }
#if QT_CONFIG(opengl)
@@ -139,6 +141,7 @@ public:
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
#if QT_CONFIG(opengl)
virtual void *nativeResourceForContext(NativeResource /*resource*/, QPlatformOpenGLContext */*context*/) { return nullptr; }
#endif /* QT_CONFIG(opengl) */
@@ -166,6 +169,7 @@ public:
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
#endif /* QT_CONFIG(opengl) */
};
}
@@ -49,6 +49,42 @@ impl log::Log for StderrLogger {
fn flush(&self) {}
}
/// Wait for a scheme to become available by polling `/scheme/{name}`.
/// Returns true if the scheme appeared within the timeout, false otherwise.
///
/// In initfs mode, driver-manager starts before pcid and acpid. The PCI bus
/// scheme (`/scheme/pci`) is registered by pcid, which is spawned by hwd
/// (service 40). The ACPI bus scheme (`/scheme/acpi`) is registered by acpid
/// (service 41). Without waiting, enumeration fails with 0 devices and the
/// deferred retry loop (3 × 500ms) is too short.
fn wait_for_scheme(name: &str, timeout: Duration) -> bool {
let path = format!("/scheme/{}", name);
let start = Instant::now();
let poll_interval = Duration::from_millis(100);
while start.elapsed() < timeout {
// Use read_dir instead of Path::exists() — Redox scheme paths
// may not respond to exists()/metadata() while still being
// functional for directory enumeration.
if fs::read_dir(&path).is_ok() {
log::info!(
"scheme {} available after {}ms",
name,
start.elapsed().as_millis()
);
return true;
}
thread::sleep(poll_interval);
}
log::warn!(
"scheme {} not available after {}ms",
name,
timeout.as_millis()
);
false
}
fn run_enumeration(
manager: &Arc<Mutex<DeviceManager>>,
scheme: &DriverManagerScheme,
@@ -421,6 +457,14 @@ fn main() {
reset_timeline_log();
// In initfs mode, pcid (spawned by hwd at service 40) may not have
// registered /scheme/pci yet when driver-manager starts at service 00.
// Wait for required bus schemes to appear before enumerating.
if initfs {
wait_for_scheme("pci", Duration::from_secs(30));
wait_for_scheme("acpi", Duration::from_secs(10));
}
if manager_config.async_probe {
let handle = thread::spawn(move || {
let (bound, deferred) = run_enumeration(&mgr_clone, scheme_clone.as_ref(), initfs);
@@ -454,7 +498,7 @@ fn main() {
idle_forever();
}
let max_retries = 3u32;
let max_retries = if initfs { 10u32 } else { 3u32 };
for retry in 1..=max_retries {
if SHUTDOWN_REQUESTED.load(Ordering::SeqCst) {
log::info!("driver-manager: SIGTERM received during deferred retry, shutting down");
@@ -212,17 +212,6 @@ fn main() {
);
let firmware_dir_str = firmware_dir.to_string_lossy().into_owned();
match manifest::generate_manifest(&firmware_dir_str) {
Ok(()) => info!(
"firmware-loader: generated firmware manifest at {}/MANIFEST.txt",
firmware_dir.display()
),
Err(err) => warn!(
"firmware-loader: failed to generate firmware manifest for {}: {}",
firmware_dir.display(),
err
),
}
let registry = match FirmwareRegistry::new(&firmware_dir) {
Ok(registry) => registry,
@@ -245,6 +234,14 @@ fn main() {
firmware_dir.display()
);
// Manifest generation is a host-side verification tool — skip it at runtime.
// The background thread would produce thousands of ENODEV warnings after
// setrens(0,0) makes the filesystem inaccessible, and the manifest is not
// consumed by any runtime path.
// std::thread::spawn(move || {
// let _ = manifest::generate_manifest(&firmware_dir_str);
// });
if args.first().map(String::as_str) == Some("--probe") {
println!("count={}", registry.len());
let mut keys = registry.list_keys();
+6 -115
View File
@@ -2,118 +2,7 @@
git = "https://gitlab.redox-os.org/redox-os/base.git"
rev = "463f76b9608a896e6f6c9f63457f57f6409873c7"
patches = [
"P0-daemon-fix-init-notify-unwrap.patch",
"P0-workspace-add-bootstrap.patch",
"P0-init-continuous-scheduling.patch",
"P0-dhcpd-auto-iface.patch",
"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",
# Stale patches needing recreation: P1-pcid-uevent-surface, P2-boot-runtime-fixes,
# P2-hwd-misc, P2-pcid-cfg-access, P3-xhci-device-hardening, P6-cpufreqd-real-impl
"P2-i2c-gpio-ucsi-drivers.patch",
"P0-i2c-control-response-empty.patch",
"P2-ihdad-graceful-init.patch",
"P2-boot-logging.patch",
"P2-init-acpid-wiring.patch",
"P2-hwd-remove-acpid-spawn.patch",
"P2-initfs-pcid-service.patch",
"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-usbhidd-hardening.patch",
"P3-init-colored-output.patch",
"P4-logd-persistent-logging.patch",
"P4-acpi-shutdown-hardening.patch",
"P4-acpi-s3-sleep.patch",
"P4-pcid-public-client-channel.patch",
"P4-pcid-config-scheme.patch",
"P4-pcid-spawner-pci-coordinate-env.patch",
"P4-initfs-usb-drm-services.patch",
"P4-initfs-release-virtio-gpu.patch",
"P4-initfs-network-services.patch",
"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",
"P58-logd-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",
"redox.patch",
]
[package]
@@ -165,6 +54,7 @@ installs = [
"/usr/lib/drivers/usbhubd",
"/usr/lib/drivers/usbscsid",
"/usr/lib/drivers/vboxd",
"/usr/lib/drivers/virtio-blkd",
"/usr/lib/drivers/virtio-gpud",
"/usr/lib/drivers/virtio-netd",
"/usr/lib/drivers/xhcid",
@@ -240,9 +130,10 @@ BINS=(
usbhubd
ucsid
usbscsid
virtio-gpud
virtio-netd
xhcid
virtio-blkd
virtio-gpud
virtio-netd
xhcid
i2cd
inputd
)
Submodule recipes/core/base/source updated: 463f76b960...cb0cd1f850
+1 -1
View File
@@ -1,6 +1,6 @@
[source]
git = "https://gitlab.redox-os.org/redox-os/bootloader.git"
patches = ["redox.patch", "fix-uefi-alloc-panic.patch", "P0-gpt-partition-offset.patch", "P5-live-preload-cap-1gib.patch"]
patches = ["redox.patch", "fix-uefi-alloc-panic.patch", "P0-gpt-partition-offset.patch", "P5-live-preload-cap-128mib.patch", "P6-full-ramdisk-preload.patch"]
[build]
template = "custom"
Submodule recipes/core/bootloader/source updated: b22a35c467...2a718991b3
+2
View File
@@ -47,6 +47,8 @@ patches = [
"../../../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",
# P26: DebugDisplay proper scrolling — ptr::copy rows up instead of wrapping to top
"../../../local/patches/kernel/P26-debug-display-scroll.patch",
]
[build]
@@ -27,6 +27,9 @@ pub struct DebugDisplay {
}
impl DebugDisplay {
const CHAR_WIDTH: usize = 8;
const CHAR_HEIGHT: usize = 16;
pub(super) fn new(
width: usize,
height: usize,
@@ -40,8 +43,8 @@ impl DebugDisplay {
onscreen_ptr,
};
let w = display.width / 8;
let h = display.height / 16;
let w = display.width / Self::CHAR_WIDTH;
let h = display.height / Self::CHAR_HEIGHT;
DebugDisplay {
display,
x: 0,
@@ -52,11 +55,36 @@ impl DebugDisplay {
}
}
fn scroll_up(&mut self) {
let stride = self.display.stride;
let width = self.display.width;
let total_height = self.display.height;
let scroll_px = Self::CHAR_HEIGHT;
unsafe {
let ptr = self.display.onscreen_ptr;
for row in 0..total_height - scroll_px {
ptr::copy(
ptr.add((row + scroll_px) * stride),
ptr.add(row * stride),
width,
);
}
for row in total_height - scroll_px..total_height {
ptr::write_bytes(ptr.add(row * stride), 0, width);
}
}
}
pub fn write(&mut self, buf: &[u8]) {
for &b in buf {
if self.x >= self.w || b == b'\n' {
self.x = 0;
self.y = (self.y + 1) % self.h;
self.y += 1;
if self.y >= self.h {
self.scroll_up();
self.y = self.h - 1;
}
}
if b == b'\r' {
@@ -64,52 +92,44 @@ impl DebugDisplay {
}
match (b, &self.mode) {
// Byte 0x1B starts ESC sequence
(0x1B, _) => {
self.mode = Mode::Esc;
continue;
}
// Ignore other nonprintable characters
(0x00..=0x1F | 0x80..=0xFF, _) => {
self.mode = Mode::Plain;
continue;
}
// '[' after ESC starts CSI sequence
(b'[', Mode::Esc) => {
self.mode = Mode::Csi;
continue;
}
// Capture any bytes after ESC
(_, Mode::Esc) => {
self.mode = Mode::Plain;
continue;
}
// Byte 0x40 to 0x7E ends CSI
(0x40..=0x7E, Mode::Csi) => {
self.mode = Mode::Plain;
continue;
}
// Capture any bytes after CSI
(_, Mode::Csi) => {
continue;
}
// Allow any other bytes
(_, Mode::Plain) => {}
}
if self.x == 0 {
self.clear_row(self.y);
self.clear_row((self.y + 1) % self.h);
}
self.char(self.x * 8, self.y * 16, b as char, 0xFFFFFF);
self.char(self.x * Self::CHAR_WIDTH, self.y * Self::CHAR_HEIGHT, b as char, 0xFFFFFF);
self.x += 1;
}
}
fn clear_row(&mut self, y: usize) {
for row in y * 16..(y + 1) * 16 {
for row in y * Self::CHAR_HEIGHT..(y + 1) * Self::CHAR_HEIGHT {
unsafe {
ptr::write_bytes(
self.display.onscreen_ptr.add(row * self.display.stride),
@@ -120,36 +140,28 @@ impl DebugDisplay {
}
}
/// Draw a character
fn char(&mut self, x: usize, y: usize, character: char, color: u32) {
if x + 8 <= self.display.width && y + 16 <= self.display.height {
let phys_y = y % self.display.height;
let mut dst = unsafe {
self.display
.onscreen_ptr
.add(phys_y * self.display.stride + x)
};
if x + Self::CHAR_WIDTH > self.display.width || y + Self::CHAR_HEIGHT > self.display.height {
return;
}
let font_i = Self::CHAR_HEIGHT * (character as usize);
if font_i + Self::CHAR_HEIGHT > FONT.len() {
return;
}
let font_i = 16 * (character as usize);
if font_i + 16 <= FONT.len() {
for row in 0..16 {
let row_data = FONT[font_i + row];
for col in 0..8 {
if (row_data >> (7 - col)) & 1 == 1 {
unsafe {
let ptr = self.display.onscreen_ptr;
let stride = self.display.stride;
for row in 0..Self::CHAR_HEIGHT {
let row_data = FONT[font_i + row];
let dst = ptr.add((y + row) * stride + x);
for col in 0..Self::CHAR_WIDTH {
if (row_data >> (Self::CHAR_WIDTH - 1 - col)) & 1 == 1 {
*dst.add(col) = color;
}
}
}
let next_phys_y = (phys_y + row + 1) % self.display.height;
dst = unsafe {
self.display
.onscreen_ptr
.add(next_phys_y * self.display.stride + x)
};
}
}
}
}
}
+2 -3
View File
@@ -31,9 +31,8 @@ patches = [
# sys/types/internal.h → stdint.h → cycle
# Headers needing stdint.h include it directly (signal.h, signalfd.h, eventfd.h).
# open_memstream: temporarily disabled — module file and wiring need recreation
# "P3-open-memstream-create.patch",
# "P3-stdio-open-memstream-mod.patch",
# open_memstream: single-patch implementation in stdio/mod.rs
"P3-open-memstream.patch",
# F_DUPFD_CLOEXEC fallback (try CLOEXEC, fall back to DUPFD + SETFD)
"P3-fcntl-dupfd-cloexec.patch",
# Non-conflicting individual patches
+4 -4
View File
@@ -100,10 +100,10 @@ for relative in ('configure', 'libcharset/configure'):
path = Path(os.environ['COOKBOOK_SOURCE']) / relative
lines = path.read_text().splitlines()
for i, line in enumerate(lines):
if "macro_version='2.4.7'" in line or "macro_version='2.5.4-redox-9510'" in line:
lines[i] = "macro_version='2.6.0'"
if "macro_revision='2.4.7'" in line or "macro_revision='2.5.4-redox-9510'" in line:
lines[i] = "macro_revision='2.6.0'"
if "macro_version='2.4.7'" in line or "macro_version='2.5.4-redox-9510'" in line or "macro_version='2.6.0'" in line:
lines[i] = "macro_version='2.6.0.23-b08cb'"
if "macro_revision='2.4.7'" in line or "macro_revision='2.5.4-redox-9510'" in line or "macro_revision='2.6.0'" in line:
lines[i] = "macro_revision='2.6.0.23'"
if "grep -v '^ *+' conftest.err >conftest.er1" in line:
lines[i] = "test -f conftest.err && grep -v '^ *+' conftest.err > conftest.er1.tmp && mv -f conftest.er1.tmp conftest.er1 || :"
if 'cat conftest.er1 >&5' in line:
+2 -2
View File
@@ -31490,8 +31490,8 @@ AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
sed_quote_subst='$sed_quote_subst'
double_quote_subst='$double_quote_subst'
delay_variable_subst='$delay_variable_subst'
macro_version='2.6.0.23-b08cb'
macro_revision='2.6.0.23'
macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
AS='`$ECHO "$AS" | $SED "$delay_single_quote_subst"`'
DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
+2 -2
View File
@@ -13903,8 +13903,8 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
sed_quote_subst='$sed_quote_subst'
double_quote_subst='$double_quote_subst'
delay_variable_subst='$delay_variable_subst'
macro_version='2.6.0.23-b08cb'
macro_revision='2.6.0.23'
macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
AS='`$ECHO "$AS" | $SED "$delay_single_quote_subst"`'
DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
@@ -344,11 +344,12 @@ am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
$(top_srcdir)/../build-aux/missing \
$(top_srcdir)/../build-aux/mkinstalldirs ../build-aux/compile \
../build-aux/config.guess ../build-aux/config.rpath \
../build-aux/config.sub ../build-aux/install-sh \
../build-aux/ltmain.sh ../build-aux/mdate-sh \
../build-aux/missing ../build-aux/mkinstalldirs \
../build-aux/texinfo.tex ../build-aux/ylwrap ABOUT-NLS AUTHORS \
COPYING ChangeLog INSTALL NEWS README
../build-aux/config.sub ../build-aux/depcomp \
../build-aux/install-sh ../build-aux/ltmain.sh \
../build-aux/mdate-sh ../build-aux/missing \
../build-aux/mkinstalldirs ../build-aux/texinfo.tex \
../build-aux/ylwrap ABOUT-NLS AUTHORS COPYING ChangeLog \
INSTALL NEWS README
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
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
View File