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
This commit is contained in:
@@ -112,7 +112,8 @@ impl DriverParamsScheme {
|
||||
}
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(err) => log::warn!(
|
||||
Err(err) if err.raw_os_error() == Some(19) => {} // ENODEV: scheme not present
|
||||
Err(err) => log::debug!(
|
||||
"driver-params: failed to read {}: {err}",
|
||||
self.bound_path.display()
|
||||
),
|
||||
|
||||
@@ -49,17 +49,25 @@ fn default_firmware_dir() -> PathBuf {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
unsafe fn get_init_notify_fd() -> RawFd {
|
||||
let fd: RawFd = env::var("INIT_NOTIFY")
|
||||
.expect("firmware-loader: INIT_NOTIFY not set")
|
||||
.parse()
|
||||
.expect("firmware-loader: INIT_NOTIFY is not a valid fd");
|
||||
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);
|
||||
fd
|
||||
Some(fd)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn notify_scheme_ready(notify_fd: RawFd, socket: &Socket, scheme: &mut FirmwareScheme) {
|
||||
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");
|
||||
@@ -77,7 +85,7 @@ fn notify_scheme_ready(notify_fd: RawFd, socket: &Socket, scheme: &mut FirmwareS
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn run_daemon(notify_fd: RawFd, registry: FirmwareRegistry) -> ! {
|
||||
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);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ drm_devices_ready() {
|
||||
|
||||
wait_for_drm_devices() {
|
||||
local devices="${1:-}"
|
||||
local wait_seconds="${REDBEAR_DRM_WAIT_SECONDS:-10}"
|
||||
local wait_seconds="${REDBEAR_DRM_WAIT_SECONDS:-2}"
|
||||
local attempts=0
|
||||
|
||||
if [ -z "$devices" ]; then
|
||||
|
||||
@@ -209,7 +209,13 @@ launch_optional_component() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
"$program" &
|
||||
# kded6 is a D-Bus daemon with no GUI requirement.
|
||||
# Force offscreen QPA to avoid the Qt6 Wayland page-fault crash at null+8.
|
||||
if [ "$program" = "kded6" ]; then
|
||||
QT_QPA_PLATFORM=offscreen "$program" &
|
||||
else
|
||||
"$program" &
|
||||
fi
|
||||
local pid=$!
|
||||
optional_pids+=("$pid")
|
||||
|
||||
|
||||
@@ -12,6 +12,13 @@ int main(int argc, char *argv[]) {
|
||||
qputenv("QT_QUICK_BACKEND", QByteArrayLiteral("software"));
|
||||
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
|
||||
|
||||
// Use the Redox-native QPA plugin (libqredox.so) instead of Wayland.
|
||||
// The Qt6 Wayland plugin crashes at null+8 during wl_registry init on Redox.
|
||||
// The redox QPA plugin renders directly to the framebuffer via scheme:display.
|
||||
if (qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) {
|
||||
qputenv("QT_QPA_PLATFORM", "redox");
|
||||
}
|
||||
|
||||
QGuiApplication app(argc, argv);
|
||||
QQuickStyle::setStyle(QStringLiteral("Basic"));
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// thermald — ACPI thermal zone manager
|
||||
// thermald — ACPI thermal zone manager + CPU die temperature
|
||||
// Reads thermal zone data from /scheme/acpi/thermal/
|
||||
// Reads CPU temperature via MSR IA32_THERM_STATUS (0x19C)
|
||||
// Provides /scheme/thermal for temperature queries
|
||||
//
|
||||
// Cross-referenced with Linux drivers/hwmon/coretemp.c:
|
||||
// MSR 0x19C bits 16-22 = digital readout (offset from TjMax)
|
||||
// MSR 0x1A2 bits 16-23 = temperature target (TjMax)
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
@@ -34,6 +39,9 @@ const CPUFREQ_GOVERNOR_PATHS: [&str; 2] = ["/scheme/cpufreq/governor", "/scheme/
|
||||
const THERMAL_POLL_INTERVAL: Duration = Duration::from_secs(2);
|
||||
const PASSIVE_HYSTERESIS_C: f64 = 2.0;
|
||||
const ACTIVE_MARGIN_C: f64 = 5.0;
|
||||
const MSR_THERM_STATUS: u32 = 0x19C;
|
||||
const MSR_TEMPERATURE_TARGET: u32 = 0x1A2;
|
||||
const TJMAX_DEFAULT_C: f64 = 100.0;
|
||||
|
||||
struct StderrLogger {
|
||||
level: LevelFilter,
|
||||
@@ -180,6 +188,53 @@ fn zone_name_for_entry(entry: &fs::DirEntry) -> Option<String> {
|
||||
entry.file_name().into_string().ok()
|
||||
}
|
||||
|
||||
fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
|
||||
let path = format!("/dev/cpu/{cpu}/msr");
|
||||
let mut file = fs::File::open(&path).ok()?;
|
||||
let mut buf = [0u8; 8];
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
file.seek(SeekFrom::Start(msr as u64)).ok()?;
|
||||
file.read_exact(&mut buf).ok()?;
|
||||
Some(u64::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
fn cpu_count() -> u32 {
|
||||
let mut count = 0u32;
|
||||
if let Ok(entries) = fs::read_dir("/dev/cpu") {
|
||||
for entry in entries.filter_map(Result::ok) {
|
||||
if let Ok(name) = entry.file_name().into_string() {
|
||||
if name.parse::<u32>().is_ok() {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
count.max(1)
|
||||
}
|
||||
|
||||
fn read_cpu_temperature(cpu: u32) -> Option<f64> {
|
||||
let tjmax = read_msr(cpu, MSR_TEMPERATURE_TARGET)
|
||||
.map(|v| ((v >> 16) & 0xFF) as f64)
|
||||
.unwrap_or(TJMAX_DEFAULT_C);
|
||||
let status = read_msr(cpu, MSR_THERM_STATUS)?;
|
||||
if status & (1 << 31) == 0 {
|
||||
return None;
|
||||
}
|
||||
let digital_readout = ((status >> 16) & 0x7F) as f64;
|
||||
Some(tjmax - digital_readout)
|
||||
}
|
||||
|
||||
fn discover_cpu_zones() -> Vec<(String, PathBuf)> {
|
||||
let mut zones = Vec::new();
|
||||
let count = cpu_count();
|
||||
for cpu in 0..count {
|
||||
if read_cpu_temperature(cpu).is_some() {
|
||||
zones.push((format!("cpu{cpu}"), PathBuf::from(format!("/dev/cpu/{cpu}/msr"))));
|
||||
}
|
||||
}
|
||||
zones
|
||||
}
|
||||
|
||||
fn discover_zone_dirs() -> Vec<(String, PathBuf)> {
|
||||
let mut zones = Vec::new();
|
||||
let Ok(entries) = fs::read_dir(ACPI_THERMAL_ROOT) else {
|
||||
@@ -207,7 +262,12 @@ fn discover_zone_dirs() -> Vec<(String, PathBuf)> {
|
||||
}
|
||||
|
||||
fn read_zone_runtime(name: String, dir: PathBuf, previous: Option<&ZoneRuntime>) -> Option<ZoneRuntime> {
|
||||
let temperature = normalize_temperature_celsius(read_scalar(&dir, &["_TMP", "tmp", "temperature"])?);
|
||||
let temperature = if name.starts_with("cpu") {
|
||||
let cpu = name.strip_prefix("cpu")?.parse::<u32>().ok()?;
|
||||
read_cpu_temperature(cpu)?
|
||||
} else {
|
||||
normalize_temperature_celsius(read_scalar(&dir, &["_TMP", "tmp", "temperature"])?)
|
||||
};
|
||||
let passive_threshold =
|
||||
read_scalar(&dir, &["_PSV", "psv", "passive_threshold"]).map(normalize_temperature_celsius);
|
||||
let critical_threshold =
|
||||
@@ -244,6 +304,15 @@ fn refresh_zones(previous: &[ZoneRuntime]) -> Vec<ZoneRuntime> {
|
||||
refreshed.push(zone);
|
||||
}
|
||||
}
|
||||
for (name, dir) in discover_cpu_zones() {
|
||||
if refreshed.iter().any(|z| z.zone.name == name) {
|
||||
continue;
|
||||
}
|
||||
let previous_zone = previous_by_name.get(name.as_str()).copied();
|
||||
if let Some(zone) = read_zone_runtime(name, dir, previous_zone) {
|
||||
refreshed.push(zone);
|
||||
}
|
||||
}
|
||||
|
||||
refreshed
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user