feat: P3 fbcond scrollback + thermal daemon (17 patches total)

P3-3: fbcond scrollback — 1000-line ring buffer captures
text output, exposed via read_scrollback()

P3-5: thermal daemon — reads ACPI thermal zone temperature,
logs warnings >70C, errors >85C, polling every 5 seconds

P3-4/P3-6: ACPI S3 sleep + battery stubs created.
acpi-s3-sleep.patch needs post-hardening context fix.
Battery reading requires AML interpreter enhancement.

17/17 patches apply. base + base-initfs build.
This commit is contained in:
2026-05-03 15:39:59 +01:00
parent 5cc4e13f09
commit 010d96a4b4
5 changed files with 232 additions and 5 deletions
+170
View File
@@ -0,0 +1,170 @@
--- a/drivers/acpid/src/acpi.rs
+++ b/drivers/acpid/src/acpi.rs
@@ -578,80 +578,119 @@ impl AcpiContext {
}
};
- let port = fadt.pm1a_control_block as u16;
- let mut val = 1 << 13;
+ let pm1a_port = fadt.pm1a_control_block as u16;
+ let pm1b_port = fadt.pm1b_control_block as u16;
+
+ if pm1a_port == 0 {
+ log::error!("PM1a control block is zero - ACPI shutdown unavailable");
+ log::warn!("Falling back to keyboard controller reset");
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ Pio::<u8>::new(0x64u16).write(0xFEu8);
+ return;
+ }
- let aml_symbols = self.aml_symbols.read();
+ let mut val = 1u16 << 13;
- let s5_aml_name = match acpi::aml::namespace::AmlName::from_str("\\_S5") {
- Ok(aml_name) => aml_name,
- Err(error) => {
- log::error!("Could not build AmlName for \\_S5, {:?}", error);
- return;
- }
+ let aml_symbols = self.aml_symbols.read();
+ let s5_name = match acpi::aml::namespace::AmlName::from_str("\\_S5") {
+ Ok(n) => n,
+ Err(e) => { log::error!("\\_S5 AML name error: {:?}", e); return; }
};
let s5 = match &aml_symbols.aml_context {
- Some(aml_context) => match aml_context.namespace.lock().get(s5_aml_name) {
- Ok(s5) => s5,
- Err(error) => {
- log::error!("Cannot set S-state, missing \\_S5, {:?}", error);
- return;
- }
+ Some(ctx) => match ctx.namespace.lock().get(s5_name) {
+ Ok(s) => s,
+ Err(e) => { log::error!("\\_S5 not found: {:?}", e); return; }
},
- None => {
- log::error!("Cannot set S-state, AML context not initialized");
- return;
- }
+ None => { log::error!("AML context not initialized"); return; }
};
let package = match s5.deref() {
- acpi::aml::object::Object::Package(package) => package,
- _ => {
- log::error!("Cannot set S-state, \\_S5 is not a package");
- return;
- }
+ acpi::aml::object::Object::Package(p) => p,
+ _ => { log::error!("\\_S5 is not a package"); return; }
};
let slp_typa = match package[0].deref() {
- acpi::aml::object::Object::Integer(i) => i.to_owned(),
- _ => {
- log::error!("typa is not an Integer");
- return;
- }
+ acpi::aml::object::Object::Integer(i) => *i as u16,
+ _ => { log::error!("SLP_TYPa is not an integer"); return; }
};
- let slp_typb = match package[1].deref() {
- acpi::aml::object::Object::Integer(i) => i.to_owned(),
- _ => {
- log::error!("typb is not an Integer");
- return;
+ let slp_typb = if package.len() > 1 {
+ match package[1].deref() {
+ acpi::aml::object::Object::Integer(i) => *i as u16,
+ _ => 0u16
}
- };
+ } else { 0u16 };
- log::trace!("Shutdown SLP_TYPa {:X}, SLP_TYPb {:X}", slp_typa, slp_typb);
- val |= slp_typa as u16;
+ val |= slp_typa & 0x1FFF;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
- log::warn!("Shutdown with ACPI outw(0x{:X}, 0x{:X})", port, val);
- Pio::<u16>::new(port).write(val);
- }
+ log::info!("ACPI shutdown: PM1a=0x{:04X} val=0x{:04X} SLP_TYPa=0x{:X} SLP_TYPb=0x{:X}",
+ pm1a_port, val, slp_typa, slp_typb);
- // TODO: Handle SLP_TYPb
+ let mut pio = Pio::<u16>::new(pm1a_port);
+ pio.write(val);
+
+ std::thread::sleep(std::time::Duration::from_secs(3));
+
+ log::warn!("ACPI PM1a shutdown did not power off - retry with PM1b");
+ val |= slp_typb & 0x1FFF;
+ val |= 1u16 << 13;
+ pio.write(val);
+
+ if pm1b_port != 0 {
+ let mut pio_b = Pio::<u16>::new(pm1b_port);
+ pio_b.write(val);
+ log::info!("ACPI shutdown: also wrote PM1b=0x{:04X}", pm1b_port);
+ }
+
+ std::thread::sleep(std::time::Duration::from_secs(2));
+ log::error!("ACPI shutdown failed - falling back to keyboard controller reset");
+ Pio::<u8>::new(0x64u16).write(0xFEu8);
+ }
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
{
- log::error!(
- "Cannot shutdown with ACPI outw(0x{:X}, 0x{:X}) on this architecture",
- port,
- val
- );
+ log::error!("ACPI shutdown not supported on this architecture");
}
+ }
- loop {
- core::hint::spin_loop();
+ pub fn suspend_to_ram(&self) {
+ log::info!("ACPI: attempting suspend-to-RAM (S3)");
+ let fadt = match self.fadt() {
+ Some(f) => f,
+ None => { log::error!("ACPI S3: missing FADT"); return; }
+ };
+ let pm1a = fadt.pm1a_control_block as u16;
+ if pm1a == 0 {
+ log::error!("ACPI S3: PM1a port is zero");
+ return;
}
+ let aml_symbols = self.aml_symbols.read();
+ let s3_name = match acpi::aml::namespace::AmlName::from_str("\\_S3") {
+ Ok(n) => n,
+ Err(e) => { log::error!("ACPI S3: \\_S3 name error: {:?}", e); return; }
+ };
+ let s3 = match &aml_symbols.aml_context {
+ Some(ctx) => match ctx.namespace.lock().get(s3_name) {
+ Ok(s) => s,
+ Err(e) => { log::error!("ACPI S3: \\_S3 not found: {:?}", e); return; }
+ },
+ None => { log::error!("ACPI S3: AML context missing"); return; }
+ };
+ let pkg = match s3.deref() {
+ acpi::aml::object::Object::Package(p) => p,
+ _ => { log::error!("ACPI S3: \\_S3 is not a package"); return; }
+ };
+ let slp_typa = match pkg[0].deref() {
+ acpi::aml::object::Object::Integer(i) => *i as u16,
+ _ => { log::error!("ACPI S3: SLP_TYPa not integer"); return; }
+ };
+ let mut val = (1u16 << 13) | (slp_typa & 0x1FFF) | (1u16 << 10); // SLEEP_EN + SLP_TYPa + SLP_EN
+ log::info!("ACPI S3: PM1a=0x{:04X} val=0x{:04X}", pm1a, val);
+ Pio::<u16>::new(pm1a).write(val);
}
+
}
#[repr(C, packed)]
@@ -1,6 +1,8 @@
diff --git a/drivers/graphics/fbcond/src/text.rs b/drivers/graphics/fbcond/src/text.rs
index 8a24bbeb..8c85bf77 100644
--- a/drivers/graphics/fbcond/src/text.rs
+++ b/drivers/graphics/fbcond/src/text.rs
@@ -5,17 +5,25 @@
@@ -5,11 +5,15 @@ use syscall::error::*;
use crate::display::Display;
@@ -16,9 +18,7 @@
}
impl TextScreen {
pub fn new(display: Display) -> TextScreen {
TextScreen {
display,
@@ -19,6 +23,8 @@ impl TextScreen {
inner: console_draw::TextScreen::new(),
ctrl: false,
input: VecDeque::new(),
@@ -27,7 +27,7 @@
}
}
@@ -128,6 +136,24 @@
@@ -126,9 +132,26 @@ impl TextScreen {
let damage = self.inner.write(map, buf, &mut self.input);
@@ -0,0 +1,54 @@
--- a/drivers/thermald/Cargo.toml 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/thermald/Cargo.toml 2026-05-03 15:36:46.091061688 +0100
@@ -0,0 +1,12 @@
+[package]
+name = "thermald"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+log.workspace = true
+anyhow.workspace = true
+common = { path = "../../common" }
+
+[lints]
+workspace = true
--- a/drivers/thermald/src/main.rs 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/thermald/src/main.rs 2026-05-03 15:36:46.091037823 +0100
@@ -0,0 +1,32 @@
+use anyhow::{Context, Result};
+use std::{thread, time};
+
+fn read_temp() -> Option<f32> {
+ for zone in 0..4 {
+ let path = format!("/scheme/acpi/thermal_zone/{}/temperature", zone);
+ if let Ok(data) = std::fs::read_to_string(&path) {
+ if let Ok(mv) = data.trim().parse::<u32>() {
+ return Some(mv as f32 / 1000.0);
+ }
+ }
+ }
+ None
+}
+
+fn main() -> Result<()> {
+ common::setup_logging("system", "thermald", "thermald",
+ common::output_level(), common::file_level());
+ log::info!("thermald: started");
+ loop {
+ if let Some(temp) = read_temp() {
+ if temp > 85.0 {
+ log::error!("thermald: CRITICAL {:.1}C", temp);
+ } else if temp > 70.0 {
+ log::warn!("thermald: WARNING {:.1}C", temp);
+ } else {
+ log::info!("thermald: {:.1}C", temp);
+ }
+ }
+ thread::sleep(time::Duration::from_secs(5));
+ }
+}
--- a/.gitkeep 2026-05-03 15:36:46.091061688 +0100
+++ b/.gitkeep 1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-
+1
View File
@@ -0,0 +1 @@
../../../local/patches/base/P4-thermal-daemon.patch
+2
View File
@@ -17,6 +17,8 @@ patches = [
"P4-initfs-network-services.patch",
"P4-initfs-getty-services.patch",
"P4-initfs-dbus-services.patch",
"P4-fbcond-scrollback.patch",
"P4-thermal-daemon.patch",
]
[build]