fix: redbear-mini boot to login prompt + daemon hardening

- 29_activate_console.service: oneshot -> oneshot_async (unblocks init
  scheduler, enabling getty 2 -> login)
- 15_coretempd.service: oneshot_async -> {scheme="coretemp"} (init
  now correctly registers the scheme fd)
- cpufreqd: child-process MSR probe detects QEMU's lack of MSR 0x199
  and gracefully degrades to monitoring-only mode
- coretempd: notification failure is now non-fatal (WARN instead of ?)
- driver-manager: "no match entries" downgraded from warn to debug
  (infrastructure daemons intentionally have no hw match)
This commit is contained in:
2026-06-01 09:02:42 +03:00
parent 3be97a964a
commit 77795cfa18
5 changed files with 59 additions and 5 deletions
+1 -1
View File
@@ -484,7 +484,7 @@ requires_weak = ["04_drivers.target"]
[service]
cmd = "/usr/bin/coretempd"
type = "oneshot_async"
type = { scheme = "coretemp" }
"""
[[files]]
+1 -1
View File
@@ -501,7 +501,7 @@ requires_weak = ["00_base.target"]
[service]
cmd = "inputd"
args = ["-A", "2"]
type = "oneshot"
type = "oneshot_async"
"""
[[files]]
@@ -298,7 +298,9 @@ fn run_daemon() -> Result<(), String> {
let mut scheme = CoretempScheme::new(cpu_infos);
if let Some(fd) = notify_fd {
notify_scheme_ready(fd, &socket, &mut scheme)?;
if let Err(e) = notify_scheme_ready(fd, &socket, &mut scheme) {
eprintln!("[WARN] coretempd: init notification failed ({}), continuing without notify", e);
}
}
eprintln!("[INFO] coretempd: registered scheme:coretemp");
@@ -1,6 +1,8 @@
use std::env;
use std::fs;
use std::io::{Read, Write};
use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
const IA32_PERF_CTL: u32 = 0x199;
@@ -10,6 +12,8 @@ const STATE_WRITE_INTERVAL_S: u64 = 1;
const MSR_ERROR_SUPPRESS_COUNT: u32 = 1;
const THERMAL_CACHE_MS: u64 = 1000;
static MSR_AVAILABLE: AtomicBool = AtomicBool::new(true);
#[derive(Clone, Copy, PartialEq, Debug)]
enum Governor { Performance, Powersave, Ondemand, Conservative, Schedutil }
@@ -57,7 +61,7 @@ fn read_acpi_pss(cpu: u32) -> Vec<PState> {
]
}
fn write_msr(cpu: u32, msr: u32, val: u64) -> bool {
fn write_msr_raw(cpu: u32, msr: u32, val: u64) -> bool {
let path = format!("/scheme/sys/msr/{}/{:x}", cpu, msr);
fs::OpenOptions::new().write(true).open(&path).ok()
.and_then(|mut f| {
@@ -66,6 +70,33 @@ fn write_msr(cpu: u32, msr: u32, val: u64) -> bool {
}).is_some()
}
fn write_msr(cpu: u32, msr: u32, val: u64) -> bool {
if !MSR_AVAILABLE.load(Ordering::Relaxed) {
return false;
}
let path = format!("/scheme/sys/msr/{}/{:x}", cpu, msr);
fs::OpenOptions::new().write(true).open(&path).ok()
.and_then(|mut f| {
let hex_val = format!("{:016x}", val);
f.write_all(hex_val.as_bytes()).ok()
}).is_some()
}
fn probe_msr_available(cpu: u32) -> bool {
let exe = match env::current_exe() {
Ok(p) => p,
Err(_) => return false,
};
match Command::new(exe)
.arg("--probe-msr")
.arg(cpu.to_string())
.status()
{
Ok(s) => s.success(),
Err(_) => false,
}
}
fn measure_load(cpu: u32, prev: &mut (u64, u64)) -> f64 {
if let Ok(d) = fs::read_to_string(format!("/scheme/sys/cpu/{}/stat", cpu)) {
let p: Vec<u64> = d.split_whitespace().filter_map(|s| s.parse().ok()).collect();
@@ -116,6 +147,15 @@ fn write_scheme_state(governor: Governor, cpus: &[CpuInfo]) {
}
fn main() {
let args: Vec<String> = env::args().collect();
// Probe mode: child process tests MSR write and exits with status.
if args.len() >= 3 && args[1] == "--probe-msr" {
let cpu: u32 = args[2].parse().unwrap_or(0);
let ok = write_msr_raw(cpu, IA32_PERF_CTL, 0x1a00);
std::process::exit(if ok { 0 } else { 1 });
}
let governor = match env::var("CPUFREQ_GOVERNOR").unwrap_or_default().as_str() {
"performance" => Governor::Performance, "powersave" => Governor::Powersave,
"conservative" => Governor::Conservative, "schedutil" => Governor::Schedutil,
@@ -123,6 +163,18 @@ fn main() {
};
let cpus = detect_cpus();
eprintln!("[INFO] cpufreqd: detected {} CPU(s), governor={:?}", cpus.len(), governor);
// Probe MSR availability before attempting any writes.
// In environments without MSR 0x199 (QEMU default, some hypervisors),
// wrmsr causes a kernel #GP that kills the process. By spawning a child
// to test the write first, we detect this safely and degrade to
// monitoring-only mode.
let msr_ok = cpus.iter().all(|&id| probe_msr_available(id));
if !msr_ok {
MSR_AVAILABLE.store(false, Ordering::Relaxed);
eprintln!("[INFO] cpufreqd: MSR 0x199 writes unavailable — running in monitoring-only mode");
}
let mut ci: Vec<CpuInfo> = cpus.iter().map(|&id| {
let ps = read_acpi_pss(id);
eprintln!("[INFO] cpufreqd: CPU{}: {} P-states ({} - {} kHz)", id, ps.len(), ps.first().map_or(0, |p| p.freq_khz), ps.last().map_or(0, |p| p.freq_khz));
@@ -106,7 +106,7 @@ impl DriverConfig {
driver.r#match.into_iter().map(DriverMatch::from).collect();
if matches.is_empty() {
log::warn!(
log::debug!(
"driver-manager: config {} driver={} has no match entries and will not bind from PCI or ACPI enumeration",
path.display(),
driver.name