milestone: desktop path Phases 1-5

Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests
Phase 2 (Wayland Compositor): bounded scaffold, zero warnings
Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick)
Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker
Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker

Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
2026-04-29 09:54:06 +01:00
parent b23714f542
commit 8acc73d774
508 changed files with 76526 additions and 396 deletions
@@ -0,0 +1,111 @@
use amlserde::{AmlSerde, AmlSerdeValue};
use std::{error::Error, fs, process::Command};
use super::Backend;
pub struct AcpiBackend {
rxsdt: Vec<u8>,
}
impl Backend for AcpiBackend {
fn new() -> Result<Self, Box<dyn Error>> {
let rxsdt = fs::read("/scheme/kernel.acpi/rxsdt")?;
// Spawn acpid
//TODO: pass rxsdt data to acpid?
#[allow(deprecated, reason = "we can't yet move this to init")]
daemon::Daemon::spawn(Command::new("acpid"));
Ok(Self { rxsdt })
}
fn probe(&mut self) -> Result<(), Box<dyn Error>> {
// Read symbols from acpi scheme
let entries = fs::read_dir("/scheme/acpi/symbols")?;
// TODO: Reimplement with getdents?
let symbols_fd = libredox::Fd::open(
"/scheme/acpi/symbols",
libredox::flag::O_DIRECTORY | libredox::flag::O_RDONLY,
0,
)?;
for entry_res in entries {
let entry = entry_res?;
if let Some(file_name) = entry.file_name().to_str() {
if file_name.ends_with("_CID") || file_name.ends_with("_HID") {
let symbol_fd = symbols_fd.openat(file_name, libredox::flag::O_RDONLY, 0)?;
let stat = symbol_fd.stat()?;
let mut buf: Vec<u8> = vec![0u8; stat.st_size as usize];
let count = symbol_fd.read(&mut buf)?;
buf.truncate(count);
let ron = String::from_utf8(buf)?;
let AmlSerde { name, value } = ron::from_str(&ron)?;
let id = match value {
AmlSerdeValue::Integer(integer) => {
let vendor = integer & 0xFFFF;
let device = (integer >> 16) & 0xFFFF;
let vendor_rev = ((vendor & 0xFF) << 8) | vendor >> 8;
let vendor_1 = (((vendor_rev >> 10) & 0x1f) as u8 + 64) as char;
let vendor_2 = (((vendor_rev >> 5) & 0x1f) as u8 + 64) as char;
let vendor_3 = (((vendor_rev >> 0) & 0x1f) as u8 + 64) as char;
//TODO: simplify this nibble swap
let device_1 = (device >> 4) & 0xF;
let device_2 = (device >> 0) & 0xF;
let device_3 = (device >> 12) & 0xF;
let device_4 = (device >> 8) & 0xF;
format!(
"{}{}{}{:01X}{:01X}{:01X}{:01X}",
vendor_1,
vendor_2,
vendor_3,
device_1,
device_2,
device_3,
device_4
)
}
AmlSerdeValue::String(string) => string,
_ => {
log::warn!("{}: unsupported value {:x?}", name, value);
continue;
}
};
let what = match id.as_str() {
// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html
"ACPI0003" => "Power source",
"ACPI0006" => "GPE block",
"ACPI0007" => "Processor",
"ACPI0010" => "Processor control",
// https://uefi.org/sites/default/files/resources/devids%20%285%29.txt
"PNP0000" => "AT interrupt controller",
"PNP0100" => "AT timer",
"PNP0103" => "HPET",
"PNP0200" => "AT DMA controller",
"PNP0303" => "IBM Enhanced (101/102-key, PS/2 mouse support)",
"PNP030B" => "PS/2 keyboard",
"PNP0400" => "Standard LPT printer port",
"PNP0501" => "16550A-compatible COM port",
"PNP0A03" | "PNP0A08" => "PCI bus",
"PNP0A05" => "Generic ACPI bus",
"PNP0A06" => "Generic ACPI Extended-IO bus (EIO bus)",
"PNP0B00" => "AT real-time clock",
"PNP0C01" => "System board",
"PNP0C02" => "Reserved resources",
"PNP0C04" => "Math coprocessor",
"PNP0C09" => "Embedded controller",
"PNP0C0A" => "Battery",
"PNP0C0B" => "Fan",
"PNP0C0C" => "Power button",
"PNP0C0D" => "Lid sensor",
"PNP0C0E" => "Sleep button",
"PNP0C0F" => "PCI interrupt link",
"PNP0C50" => "I2C HID",
"PNP0F13" => "PS/2 port for PS/2-style mouse",
_ => "?",
};
log::debug!("{}: {} ({})", name, id, what);
}
}
}
Ok(())
}
}
@@ -0,0 +1,45 @@
use std::{error::Error, fs};
use super::Backend;
pub struct DeviceTreeBackend {
dtb: Vec<u8>,
}
impl DeviceTreeBackend {
fn dump(node: &fdt::node::FdtNode<'_, '_>, level: usize) {
let mut line = String::new();
for _ in 0..level {
line.push_str(" ");
}
line.push_str(node.name);
if let Some(compatible) = node.compatible() {
line.push_str(":");
for id in compatible.all() {
line.push_str(" ");
line.push_str(id);
}
}
log::debug!("{}", line);
for child in node.children() {
Self::dump(&child, level + 1);
}
}
}
impl Backend for DeviceTreeBackend {
fn new() -> Result<Self, Box<dyn Error>> {
let dtb = fs::read("/scheme/kernel.dtb")?;
let dt = fdt::Fdt::new(&dtb).map_err(|err| format!("failed to parse dtb: {}", err))?;
Ok(Self { dtb })
}
fn probe(&mut self) -> Result<(), Box<dyn Error>> {
let dt = fdt::Fdt::new(&self.dtb).map_err(|err| format!("failed to parse dtb: {}", err))?;
let root = dt
.find_node("/")
.ok_or_else(|| format!("failed to find root node"))?;
Self::dump(&root, 0);
Ok(())
}
}
@@ -0,0 +1,16 @@
use std::error::Error;
use super::Backend;
pub struct LegacyBackend;
impl Backend for LegacyBackend {
fn new() -> Result<Self, Box<dyn Error>> {
Ok(Self)
}
fn probe(&mut self) -> Result<(), Box<dyn Error>> {
log::info!("TODO: handle driver spawning from legacy backend");
Ok(())
}
}
@@ -0,0 +1,14 @@
use std::error::Error;
mod acpi;
mod devicetree;
mod legacy;
pub use self::{acpi::AcpiBackend, devicetree::DeviceTreeBackend, legacy::LegacyBackend};
pub trait Backend {
fn new() -> Result<Self, Box<dyn Error>>
where
Self: Sized;
fn probe(&mut self) -> Result<(), Box<dyn Error>>;
}
+59
View File
@@ -0,0 +1,59 @@
use std::process;
mod backend;
use self::backend::{AcpiBackend, Backend, DeviceTreeBackend, LegacyBackend};
fn daemon(daemon: daemon::Daemon) -> ! {
common::setup_logging(
"misc",
"hwd",
"hwd",
common::output_level(),
common::file_level(),
);
// Prefer DTB if available (matches kernel preference)
let mut backend: Box<dyn Backend> = match DeviceTreeBackend::new() {
Ok(ok) => {
log::info!("using devicetree backend");
Box::new(ok)
}
Err(err) => {
log::debug!("cannot use devicetree backend: {}", err);
match AcpiBackend::new() {
Ok(ok) => {
log::info!("using ACPI backend");
Box::new(ok)
}
Err(err) => {
log::debug!("cannot use ACPI backend: {}", err);
log::info!("using legacy backend");
Box::new(LegacyBackend)
}
}
}
};
//TODO: launch pcid based on backend information?
// Must launch after acpid but before probe calls /scheme/acpi/symbols
#[allow(deprecated, reason = "we can't yet move this to init")]
daemon::Daemon::spawn(process::Command::new("pcid"));
daemon.ready();
//TODO: HWD is meant to locate PCI/XHCI/etc devices in ACPI and DeviceTree definitions and start their drivers
match backend.probe() {
Ok(()) => {
process::exit(0);
}
Err(err) => {
log::error!("failed to probe with error {}", err);
process::exit(1);
}
}
}
fn main() {
daemon::Daemon::new(daemon);
}