Files
RedBear-OS/local/recipes/system/firmware-loader/source/src/main.rs
T
vasilito ff5a132a9d fix: comprehensive boot hardening — crashes, warnings, sensors, bare-metal PS/2
- firmware-loader: handle missing INIT_NOTIFY gracefully (Option<RawFd>)
- driver-params: suppress ENODEV (19) on missing driver-manager scheme
- compositor: add wl_seat.name + pointer capabilities for Qt6 compat
- greeter: use redox QPA (libqredox.so) instead of broken Qt6 Wayland
- greeter: reduce DRM wait 10s→2s, kded6 offscreen QPA to avoid crash
- thermald: add CPU die temperature via MSR IA32_THERM_STATUS (Linux coretemp)
- pcid: diagnostic MCFG warning with Q35 guidance, address validation
- dhcpd: auto-interface detection (P0-dhcpd-auto-iface.patch wired)
- procmgr: SIGCHLD EPERM→debug (kernel limitation, not a bug)
- ps2d LED: warn→debug on modern hw without real PS/2 controller
- ps2d mouse: 10×1s→3×250ms retry, fast-fail on unknown response
- i2c RON: handle empty response from i2cd when no adapters present
- base recipe: wire 6 new/improved patches
- config: remove INIT_SKIP, enable all 14 previously-suppressed daemons
2026-05-06 11:16:18 +01:00

272 lines
8.2 KiB
Rust

mod r#async;
mod blob;
mod manifest;
mod scheme;
use std::env;
#[cfg(target_os = "redox")]
use std::os::fd::RawFd;
use std::path::PathBuf;
use std::process;
use std::sync::mpsc;
use std::time::Duration;
use log::{error, info, warn, LevelFilter, Metadata, Record};
#[cfg(target_os = "redox")]
use redox_scheme::{scheme::SchemeSync, SignalBehavior, Socket};
use blob::FirmwareRegistry;
#[cfg(target_os = "redox")]
use scheme::FirmwareScheme;
struct StderrLogger {
level: LevelFilter,
}
impl log::Log for StderrLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= self.level
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("[{}] {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
fn init_logging(level: LevelFilter) {
if log::set_boxed_logger(Box::new(StderrLogger { level })).is_err() {
return;
}
log::set_max_level(level);
}
fn default_firmware_dir() -> PathBuf {
PathBuf::from("/lib/firmware/")
}
#[cfg(target_os = "redox")]
unsafe fn get_init_notify_fd() -> Option<RawFd> {
let Ok(value) = env::var("INIT_NOTIFY") else {
eprintln!("firmware-loader: INIT_NOTIFY not set; readiness notification disabled");
return None;
};
let Ok(fd) = value.parse::<RawFd>() else {
eprintln!("firmware-loader: INIT_NOTIFY is not a valid fd; readiness notification disabled");
return None;
};
libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC);
Some(fd)
}
#[cfg(target_os = "redox")]
fn notify_scheme_ready(notify_fd: Option<RawFd>, socket: &Socket, scheme: &mut FirmwareScheme) {
let Some(notify_fd) = notify_fd else {
info!("firmware-loader: no INIT_NOTIFY fd, skipping readiness notification");
return;
};
let cap_id = scheme
.scheme_root()
.expect("firmware-loader: scheme_root failed");
let cap_fd = socket
.create_this_scheme_fd(0, cap_id, 0, 0)
.expect("firmware-loader: create_this_scheme_fd failed");
syscall::call_wo(
notify_fd as usize,
&libredox::Fd::new(cap_fd).into_raw().to_ne_bytes(),
syscall::CallFlags::FD,
&[],
)
.expect("firmware-loader: failed to notify init that scheme is ready");
}
#[cfg(target_os = "redox")]
fn run_daemon(notify_fd: Option<RawFd>, registry: FirmwareRegistry) -> ! {
let socket = Socket::create().expect("firmware-loader: failed to create scheme socket");
let mut scheme = FirmwareScheme::new(registry);
notify_scheme_ready(notify_fd, &socket, &mut scheme);
info!("firmware-loader: registered scheme:firmware");
libredox::call::setrens(0, 0).expect("firmware-loader: failed to enter null namespace");
while let Some(request) = socket
.next_request(SignalBehavior::Restart)
.expect("firmware-loader: failed to read scheme request")
{
match request.kind() {
redox_scheme::RequestKind::Call(request) => {
let mut state = redox_scheme::scheme::SchemeState::new();
let response = request.handle_sync(&mut scheme, &mut state);
socket
.write_response(response, SignalBehavior::Restart)
.expect("firmware-loader: failed to write response");
}
_ => (),
}
}
process::exit(0);
}
fn main() {
let log_level = match env::var("FIRMWARE_LOADER_LOG").as_deref() {
Ok("debug") => LevelFilter::Debug,
Ok("trace") => LevelFilter::Trace,
Ok("warn") => LevelFilter::Warn,
Ok("error") => LevelFilter::Error,
_ => LevelFilter::Info,
};
init_logging(log_level);
let args: Vec<String> = env::args().skip(1).collect();
if args.first().map(String::as_str) == Some("--generate-manifest") {
let Some(path) = args.get(1) else {
error!("firmware-loader: --generate-manifest requires a directory path");
process::exit(2);
};
if args.len() != 2 {
error!("firmware-loader: --generate-manifest accepts exactly one directory path");
process::exit(2);
}
match manifest::generate_manifest(path) {
Ok(()) => {
println!("generated {}/MANIFEST.txt", path.trim_end_matches('/'));
return;
}
Err(err) => {
error!(
"firmware-loader: failed to generate manifest for {}: {}",
path, err
);
process::exit(1);
}
}
}
if args.first().map(String::as_str) == Some("--request-nowait") {
let Some(name) = args.get(1) else {
error!("firmware-loader: --request-nowait requires a firmware name");
process::exit(2);
};
if args.len() > 3 {
error!(
"firmware-loader: --request-nowait accepts a firmware name and optional timeout_ms"
);
process::exit(2);
}
let timeout_ms = match args.get(2) {
Some(value) => match value.parse::<u64>() {
Ok(timeout_ms) => timeout_ms,
Err(err) => {
error!(
"firmware-loader: invalid timeout for --request-nowait ({}): {}",
value, err
);
process::exit(2);
}
},
None => 5000,
};
let (tx, rx) = mpsc::channel();
r#async::request_firmware_nowait(name, timeout_ms, move |result| {
let _ = tx.send(result);
});
match rx.recv_timeout(Duration::from_millis(timeout_ms.saturating_add(1000))) {
Ok(Ok(bytes)) => {
println!("loaded={} bytes={}", name, bytes.len());
return;
}
Ok(Err(err)) => {
error!("firmware-loader: async firmware request failed for {}: {}", name, err);
process::exit(1);
}
Err(err) => {
error!(
"firmware-loader: async firmware request channel failed for {}: {}",
name, err
);
process::exit(1);
}
}
}
let firmware_dir = env::var("FIRMWARE_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| default_firmware_dir());
info!(
"firmware-loader: starting with directory {}",
firmware_dir.display()
);
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,
Err(blob::BlobError::DirNotFound(_)) => {
error!(
"firmware-loader: firmware directory not found, starting with an empty registry: {}",
firmware_dir.display()
);
FirmwareRegistry::empty(&firmware_dir)
}
Err(e) => {
error!("firmware-loader: fatal error: failed to initialize firmware registry: {e}");
process::exit(1);
}
};
info!(
"firmware-loader: indexed {} firmware blob(s) from {}",
registry.len(),
firmware_dir.display()
);
if args.first().map(String::as_str) == Some("--probe") {
println!("count={}", registry.len());
let mut keys = registry.list_keys();
keys.sort_unstable();
for key in keys.into_iter().take(16) {
println!("firmware={key}");
}
return;
}
#[cfg(not(target_os = "redox"))]
{
eprintln!("firmware-loader: daemon mode is only supported on Redox; use --probe on host");
process::exit(1);
}
#[cfg(target_os = "redox")]
{
let notify_fd = unsafe { get_init_notify_fd() };
run_daemon(notify_fd, registry);
}
}