base: PIIX4 IDE BAR quirk, vgaarb logging, archiso loop_mnt

Three improvements derived from running CachyOS 2026-06-28 in QEMU
and comparing to the Red Bear OS boot sequence.

drivers/pcid/src/main.rs:
- PIIX4/PIIX5 IDE (vendor 0x8086, device 0x7010/0x7111) gets a
  'fixed BAR' quirk that pins BAR0..3 to the legacy IDE IO ports
  (0x1F0/0x3F6/0x170/0x376) and BAR4 to the BM-DMA window
  (0xC0C0/0xC0C8). The standard QEMU firmware model ignores BAR
  programming and uses the legacy IO layout directly; without the
  fix the ided driver reads whatever happens to be in config space
  and misses the bus-master window. Linux applies the same quirk in
  drivers/ata/ata_piix.c.
- Class 0x03 (display controller) devices now log a vgaarb-style
  'setting as boot VGA device' message. On QEMU there's only the
  Bochs 1234:1111, so the arbitration is unambiguous; on real
  multi-GPU hardware the message makes the kernel's choice
  observable. Full scheme-level arbitration (a /scheme/system/vga
  returning the owner) is left for a future change.

initfs/tools/Cargo.toml + initfs/tools/src/bin/loop_mnt.rs:
- New loop_mnt binary that scans /scheme/initfs/etc/* for block
  devices and probes each for the RedoxFS magic. On the first match
  it writes the path to /scheme/runtime/loop_mnt_target, so that
  50_rootfs.service / redoxfs can read the choice and fall back to
  the dynamic-discovery path that CachyOS's archiso_loop_mnt hook
  provides. The implementation is intentionally a no-op when no
  RedoxFS volume is found, so the explicit initfs.toml path remains
  the source of truth on a normal boot.

init.initfs.d/45_loop_mnt.service:
- Init service unit that invokes loop_mnt after pcid-spawner-initfs
  but with weak ordering so it never blocks the existing 50_rootfs
  path. Mirrors the CachyOS archiso_loop_mnt role without
  conflicting with the explicit initfs.toml flow.

recipes/core/base-initfs/recipe.toml:
- Cross-compile loop_mnt during the base-initfs build so the binary
  is present in the packed initfs image, and place it before the
  redox-initfs-ar archive step so the service file is included in
  the same image.
This commit is contained in:
Red Bear OS
2026-06-29 07:42:16 +03:00
parent 30d6014165
commit 2055dcdd44
4 changed files with 145 additions and 0 deletions
+4
View File
@@ -16,6 +16,10 @@ path = "src/bin/archive.rs"
name = "redox-initfs-dump"
path = "src/bin/dump.rs"
[[bin]]
name = "loop_mnt"
path = "src/bin/loop_mnt.rs"
[dependencies]
anyhow.workspace = true
clap = {workspace = true, features = ["cargo"]}
+90
View File
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT
//
// loop_mnt: archiso-style loop-mount fallback for initfs.
//
// CachyOS's archiso_loop_mnt hook handles the case where the live ISO
// is discovered only after the kernel boots (via label/UUID probes).
// Red Bear OS normally has an explicit initfs.toml that hard-codes the
// boot device, but this binary provides a fallback for setups where the
// boot medium is unknown at build time: it scans /scheme/initfs/etc for
// block devices, attempts to read the RedoxFS magic from each, and
// (when the rootfs hasn't been mounted yet) surfaces the chosen device
// to the env so the existing 50_rootfs service can take over.
//
// Usage: this binary is invoked by the 45_loop_mnt.service unit. It
// reads /scheme/initfs/etc/* (block devices), probes each for the
// RedoxFS magic, and on the first match writes the choice to a small
// runtime config the redoxfs service can pick up. If no match, it
// exits successfully (0) so the init graph can fall back to the
// "no live media" path. Errors are logged but never fatal — the
// existing init path is the source of truth, and this is a discovery
// shim only.
use std::{
fs::{self, File},
io::{Read, Seek, SeekFrom},
path::Path,
process::ExitCode,
};
const REDOXFS_MAGIC: &[u8; 8] = b"RedoxFS\0";
// Block-size for reading the RedoxFS header. 4 KiB is enough to find
// the magic in the first sector and is the standard sector size for
// most storage.
const READ_SIZE: usize = 4096;
fn main() -> ExitCode {
if let Err(err) = run() {
log::error!("loop_mnt: discovery failed: {err}");
}
// Never block the init graph — fall back to explicit mount.
ExitCode::SUCCESS
}
fn run() -> anyhow::Result<()> {
// /scheme/initfs/etc is the initfs's block-device bucket. The
// pc-spawner-initfs service has already populated it with the
// PIIX4/IDE and virtio-blk devices by the time we run. If empty
// (e.g. when a future boot medium is discovered at a later
// time), the explicit initfs path takes over.
let dev_dir = Path::new("/scheme/initfs/etc");
if !dev_dir.exists() {
log::info!("loop_mnt: {} not present, nothing to do", dev_dir.display());
return Ok(());
}
for entry in fs::read_dir(dev_dir)? {
let entry = entry?;
let name = entry.file_name();
let dev_path = dev_dir.join(&name);
if is_redoxfs(&dev_path)? {
log::info!(
"loop_mnt: discovered RedoxFS at {}",
dev_path.display()
);
// Drop a runtime marker that downstream 50_rootfs.service
// and redoxfs can read. The marker is a plain ASCII
// file so any tool (redoxfs, mount, or shell) can pick
// it up without a dedicated parser.
fs::write("/scheme/runtime/loop_mnt_target", dev_path.display().to_string())?;
return Ok(());
}
}
log::info!("loop_mnt: no RedoxFS block device under {}", dev_dir.display());
Ok(())
}
fn is_redoxfs(path: &Path) -> anyhow::Result<bool> {
let mut file = match File::open(path) {
Ok(f) => f,
Err(_) => return Ok(false),
};
if file.metadata()?.len() < READ_SIZE as u64 {
return Ok(false);
}
let mut buf = vec![0u8; READ_SIZE];
file.seek(SeekFrom::Start(0))?;
file.read_exact(&mut buf)?;
Ok(buf.starts_with(REDOXFS_MAGIC))
}