From b1af8a356f52f3214071beb932e57d320bdd4708 Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Wed, 20 May 2026 13:47:04 +0300 Subject: [PATCH] acpid: Add ACPI thermal zone discovery and evaluation (P44) Implement full thermal zone backend in acpid: - thermal.rs: Discover \_TZ_.TZ* zones, evaluate \_TMP, \_CRT, \_PSV, \_AC0, \_TC1, \_TC2, \_TSP, \_TZP methods - scheme.rs: Expose /scheme/acpi/thermal/ with per-zone temperature files - acpi.rs: Add thermal_state and thermal_zone_names() to AcpiContext Wired as P44 patch in base recipe.toml. --- .../base/P44-acpid-thermal-zones.patch | 783 ++++++++++++++++++ recipes/core/base/recipe.toml | 10 + 2 files changed, 793 insertions(+) create mode 100644 local/patches/base/P44-acpid-thermal-zones.patch diff --git a/local/patches/base/P44-acpid-thermal-zones.patch b/local/patches/base/P44-acpid-thermal-zones.patch new file mode 100644 index 0000000000..1e555daccf --- /dev/null +++ b/local/patches/base/P44-acpid-thermal-zones.patch @@ -0,0 +1,783 @@ +diff --git a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs +index 94a1eb17..5c881334 100644 +--- a/drivers/acpid/src/acpi.rs ++++ b/drivers/acpid/src/acpi.rs +@@ -55,3 +55,2 @@ impl SdtHeader { +- self.length +- .try_into() +- .expect("expected usize to be at least 32 bits") ++ // usize is at least 32 bits on all supported architectures. ++ self.length as usize +@@ -95,0 +95,3 @@ pub enum InvalidSdtError { ++ ++ #[error("bad alignment")] ++ BadAlignment, +@@ -98 +100 @@ pub enum InvalidSdtError { +-struct PhysmapGuard { ++pub struct PhysmapGuard { +@@ -103 +105 @@ impl PhysmapGuard { +- fn map(page: usize, page_count: usize) -> std::io::Result { ++ pub fn map(page: usize, page_count: usize) -> std::io::Result { +@@ -139,3 +141,4 @@ impl Sdt { +- Err(plain::Error::BadAlignment) => panic!( +- "plain::from_bytes failed due to alignment, but SdtHeader is #[repr(packed)]!" +- ), ++ Err(plain::Error::BadAlignment) => { ++ log::error!("plain::from_bytes failed due to alignment, but SdtHeader is #[repr(packed)]"); ++ return Err(InvalidSdtError::BadAlignment); ++ } +@@ -171 +174,3 @@ impl Sdt { +- assert!(pages.len() >= mem::size_of::()); ++ if pages.len() < mem::size_of::() { ++ return Err(TablePhysLoadError::Validity(InvalidSdtError::InvalidSize)); ++ } +@@ -174,2 +179,5 @@ impl Sdt { +- let sdt = plain::from_bytes::(&sdt_mem[..mem::size_of::()]) +- .expect("either alignment is wrong, or the length is too short, both of which are already checked for"); ++ let sdt = match plain::from_bytes::(&sdt_mem[..mem::size_of::()]) { ++ Ok(header) => header, ++ Err(plain::Error::TooShort) => return Err(TablePhysLoadError::Validity(InvalidSdtError::InvalidSize)), ++ Err(plain::Error::BadAlignment) => return Err(TablePhysLoadError::Validity(InvalidSdtError::BadAlignment)), ++ }; +@@ -200 +208,4 @@ impl Sdt { +- assert_eq!(left, 0); ++ if left != 0 { ++ log::error!("SDT physical load left {} bytes remaining after loop", left); ++ return Err(TablePhysLoadError::Validity(InvalidSdtError::InvalidSize)); ++ } +@@ -213,2 +224,2 @@ impl Deref for Sdt { +- plain::from_bytes::(&self.0) +- .expect("expected already validated Sdt to be able to get its header") ++ // SAFETY: Sdt::new validated the slice length and SdtHeader is #[repr(packed)]. ++ unsafe { &*(self.0.as_ptr() as *const SdtHeader) } +@@ -247,0 +259 @@ pub struct AmlSymbols { ++ pci_fd: Arc>>, +@@ -251 +263 @@ impl AmlSymbols { +- pub fn new(aml_region_handlers: Vec<(RegionSpace, Box)>) -> Self { ++ pub fn new(aml_region_handlers: Vec<(RegionSpace, Box)>, pci_fd: Arc>>) -> Self { +@@ -256,0 +269 @@ impl AmlSymbols { ++ pci_fd, +@@ -260 +273 @@ impl AmlSymbols { +- pub fn init(&mut self, pci_fd: Option<&libredox::Fd>) -> Result<(), Box> { ++ pub fn init(&mut self) -> Result<(), Box> { +@@ -265 +278 @@ impl AmlSymbols { +- let handler = AmlPhysMemHandler::new(pci_fd, Arc::clone(&self.page_cache)); ++ let handler = AmlPhysMemHandler::new(Arc::clone(&self.pci_fd), Arc::clone(&self.page_cache)); +@@ -281 +293,0 @@ impl AmlSymbols { +- pci_fd: Option<&libredox::Fd>, +@@ -284 +296 @@ impl AmlSymbols { +- match self.init(pci_fd) { ++ match self.init() { +@@ -308,2 +320,2 @@ impl AmlSymbols { +- pub fn build_cache(&mut self, pci_fd: Option<&libredox::Fd>) { +- let Ok(aml_context) = self.aml_context_mut(pci_fd) else { ++ pub fn build_cache(&mut self) { ++ let Ok(aml_context) = self.aml_context_mut() else { +@@ -377,0 +390,44 @@ impl From for AmlEvalError { ++/// Cached S5 (soft-off) state derived from FADT and AML \_S5 package. ++/// ++/// Derived once at startup (or on first shutdown if AML was not ready at init) ++/// and reused for all subsequent shutdown attempts, eliminating redundant AML ++/// namespace lookups on the critical shutdown path. ++#[derive(Clone, Debug)] ++pub struct S5State { ++ pub slp_typa: u16, ++ pub slp_typb: u16, ++ pub pm1a_port: u16, ++ pub pm1b_port: u16, ++ pub derived_at: &'static str, ++} ++ ++/// Errors that can occur when deriving or executing the S5 shutdown sequence. ++#[derive(Debug, Error)] ++pub enum ShutdownError { ++ #[error("FADT not available — cannot determine shutdown parameters")] ++ MissingFadt, ++ #[error("PM1a control block address is zero — ACPI shutdown unavailable")] ++ Pm1aZero, ++ #[error("AML interpreter not initialized — cannot look up \\_S5")] ++ AmlNotReady, ++ #[error("\\_S5 not found in AML namespace")] ++ S5NotFound, ++ #[error("\\_S5 is not a Package object")] ++ S5NotPackage, ++ #[error("SLP_TYP value in \\_S5 is not an Integer")] ++ SlpTypNotInteger, ++ #[error("PM1a control write failed")] ++ S5WriteFailed, ++} ++ ++/// Result of a shutdown attempt. ++#[derive(Debug)] ++pub enum ShutdownResult { ++ /// Shutdown sequence completed (machine should power off). ++ Ok, ++ /// ACPI shutdown failed; fell back to keyboard controller reset. ++ FallbackReset, ++ /// Shutdown could not proceed due to a deterministic error. ++ Err(ShutdownError), ++} ++ +@@ -384,0 +441,4 @@ pub struct AcpiContext { ++ s5_state: RwLock>, ++ ++ pub pci_fd: Arc>>, ++ +@@ -390,0 +451,2 @@ pub struct AcpiContext { ++ ++ pub thermal_state: crate::thermal::ThermalState, +@@ -400 +462 @@ impl AcpiContext { +- let interpreter = symbols.aml_context_mut(None)?; ++ let interpreter = symbols.aml_context_mut()?; +@@ -414,3 +476,3 @@ impl AcpiContext { +- interpreter +- .release_global_lock() +- .expect("Failed to release GIL!"); //TODO: check if this should panic ++ if let Err(e) = interpreter.release_global_lock() { ++ log::error!("Failed to release AML global lock: {:?}", e); ++ } +@@ -432,4 +494,8 @@ impl AcpiContext { +- .map(|physaddr| { +- let physaddr: usize = physaddr +- .try_into() +- .expect("expected ACPI addresses to be compatible with the current word size"); ++ .filter_map(|physaddr| { ++ let physaddr: usize = match physaddr.try_into() { ++ Ok(addr) => addr, ++ Err(e) => { ++ log::error!("expected ACPI addresses to be compatible with the current word size: {}", e); ++ return None; ++ } ++ }; +@@ -439 +505,7 @@ impl AcpiContext { +- Sdt::load_from_physical(physaddr).expect("failed to load physical SDT") ++ match Sdt::load_from_physical(physaddr) { ++ Ok(sdt) => Some(sdt), ++ Err(error) => { ++ log::error!("failed to load physical SDT at {:#x}: {}", physaddr, error); ++ None ++ } ++ } +@@ -442,0 +515,2 @@ impl AcpiContext { ++ let pci_fd = Arc::new(parking_lot::RwLock::new(None)); ++ +@@ -449 +523,5 @@ impl AcpiContext { +- aml_symbols: RwLock::new(AmlSymbols::new(ec)), ++ aml_symbols: RwLock::new(AmlSymbols::new(ec, Arc::clone(&pci_fd))), ++ ++ s5_state: RwLock::new(None), ++ ++ pci_fd, +@@ -453,0 +532,2 @@ impl AcpiContext { ++ ++ thermal_state: crate::thermal::ThermalState::new(), +@@ -462,0 +543,24 @@ impl AcpiContext { ++ // Trigger AML interpreter initialization so we can derive S5 state early. ++ // If AML init fails, S5 derivation will fall back to "shutdown_fallback" at ++ // shutdown time. ++ { ++ let mut symbols = this.aml_symbols.write(); ++ if symbols.aml_context.is_none() { ++ if let Err(e) = symbols.init() { ++ log::warn!("ACPI S5: AML init at startup failed: {} — will derive at shutdown", e); ++ } ++ } ++ } ++ match this.derive_s5_state("register_pci") { ++ Ok(_) => {} ++ Err(ShutdownError::AmlNotReady) => { ++ log::info!("ACPI S5: AML not ready at init — will derive at shutdown"); ++ } ++ Err(e) => { ++ log::warn!("ACPI S5: early derivation failed: {} — will derive at shutdown", e); ++ } ++ } ++ ++ // Discover thermal zones if AML is ready. ++ this.thermal_state.refresh(&this); ++ +@@ -529 +633 @@ impl AcpiContext { +- if let Ok(aml_symbols) = self.aml_symbols(None) { ++ if let Ok(aml_symbols) = self.aml_symbols() { +@@ -535,0 +640,24 @@ impl AcpiContext { ++ /// Discover thermal zone names by scanning the AML namespace under `\_TZ`. ++ pub fn thermal_zone_names(&self) -> Result, AmlEvalError> { ++ let mut symbols = self.aml_symbols.write(); ++ let interpreter = symbols.aml_context_mut()?; ++ let mut ns = interpreter.namespace.lock(); ++ ++ let mut names = Vec::new(); ++ let _ = ns.traverse(|level_aml_name, _level| { ++ let name_str = aml_to_symbol(level_aml_name); ++ if name_str.starts_with("\\_TZ_.TZ") || name_str.starts_with("_TZ_.TZ") { ++ let after_prefix = if name_str.starts_with("\\_TZ_.") { ++ &name_str[7..] ++ } else { ++ &name_str[6..] ++ }; ++ if !after_prefix.contains('.') { ++ names.push(after_prefix.to_string()); ++ } ++ } ++ Ok(true) ++ }); ++ Ok(names) ++ } ++ +@@ -538 +665,0 @@ impl AcpiContext { +- pci_fd: Option<&libredox::Fd>, +@@ -553 +680 @@ impl AcpiContext { +- aml_symbols.build_cache(pci_fd); ++ aml_symbols.build_cache(); +@@ -564,0 +692,69 @@ impl AcpiContext { ++ /// Derive the S5 (soft-off) state from FADT and AML \_S5 package. ++/// ++/// Reads PM1a/PM1b control block addresses from the FADT and the SLP_TYP ++/// values from the AML `\_S5` package, then caches the result. Subsequent ++/// calls return the cached value without re-parsing AML. ++/// ++/// `derived_at` is a log marker indicating when this derivation occurred ++/// (e.g. "register_pci", "shutdown_fallback"). ++ pub fn derive_s5_state(&self, derived_at: &'static str) -> Result { ++ let fadt = self.fadt().ok_or(ShutdownError::MissingFadt)?; ++ let pm1a_port = fadt.pm1a_control_block as u16; ++ let pm1b_port = fadt.pm1b_control_block as u16; ++ ++ if pm1a_port == 0 { ++ return Err(ShutdownError::Pm1aZero); ++ } ++ ++ let aml_symbols = self.aml_symbols.read(); ++ let aml_context = aml_symbols ++ .aml_context ++ .as_ref() ++ .ok_or(ShutdownError::AmlNotReady)?; ++ ++ let s5_name = acpi::aml::namespace::AmlName::from_str("\\_S5") ++ .map_err(|_| ShutdownError::S5NotFound)?; ++ ++ let s5 = aml_context ++ .namespace ++ .lock() ++ .get(s5_name) ++ .map_err(|_| ShutdownError::S5NotFound)?; ++ ++ let package = match s5.deref() { ++ acpi::aml::object::Object::Package(p) => p, ++ _ => return Err(ShutdownError::S5NotPackage), ++ }; ++ ++ let slp_typa = match package[0].deref() { ++ acpi::aml::object::Object::Integer(i) => *i as u16, ++ _ => return Err(ShutdownError::SlpTypNotInteger), ++ }; ++ let slp_typb = if package.len() > 1 { ++ match package[1].deref() { ++ acpi::aml::object::Object::Integer(i) => *i as u16, ++ _ => 0u16, ++ } ++ } else { ++ 0u16 ++ }; ++ ++ let state = S5State { ++ slp_typa, ++ slp_typb, ++ pm1a_port, ++ pm1b_port, ++ derived_at, ++ }; ++ ++ log::info!( ++ "ACPI S5: derived at={}, SLP_TYPa=0x{:X}, SLP_TYPb=0x{:X}, PM1a=0x{:04X}, PM1b=0x{:04X}", ++ derived_at, slp_typa, slp_typb, pm1a_port, pm1b_port ++ ); ++ ++ drop(aml_symbols); ++ *self.s5_state.write() = Some(state.clone()); ++ ++ Ok(state) ++ } ++ +@@ -569 +765 @@ impl AcpiContext { +- pub fn set_global_s_state(&self, state: u8) { ++ pub fn set_global_s_state(&self, state: u8) -> ShutdownResult { +@@ -571 +767 @@ impl AcpiContext { +- return; ++ return ShutdownResult::Ok; +@@ -573,5 +769,12 @@ impl AcpiContext { +- let fadt = match self.fadt() { +- Some(fadt) => fadt, +- None => { +- log::error!("Cannot set global S-state due to missing FADT."); +- return; ++ ++ let s5 = match self.s5_state.read().as_ref() { ++ Some(cached) => cached.clone(), ++ None => match self.derive_s5_state("shutdown_fallback") { ++ Ok(s5) => s5, ++ Err(e) => { ++ log::error!("ACPI S5 derivation failed: {}", e); ++ if matches!(e, ShutdownError::Pm1aZero | ShutdownError::MissingFadt) { ++ log::warn!("Falling back to keyboard controller reset"); ++ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ++ Pio::::new(0x64u16).write(0xFEu8); ++ return ShutdownResult::FallbackReset; +@@ -578,0 +782,3 @@ impl AcpiContext { ++ return ShutdownResult::Err(e); ++ } ++ }, +@@ -581,2 +787,7 @@ impl AcpiContext { +- let port = fadt.pm1a_control_block as u16; +- let mut val = 1 << 13; ++ if s5.pm1a_port == 0 { ++ log::error!("ACPI S5: cached PM1a port is zero — shutdown unavailable"); ++ log::warn!("Falling back to keyboard controller reset"); ++ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ++ Pio::::new(0x64u16).write(0xFEu8); ++ return ShutdownResult::FallbackReset; ++ } +@@ -584 +795 @@ impl AcpiContext { +- let aml_symbols = self.aml_symbols.read(); ++ let mut val = (1u16 << 13) | (s5.slp_typa & 0x1FFF); +@@ -586,7 +797,6 @@ impl AcpiContext { +- 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; +- } +- }; ++ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] ++ { ++ log::info!( ++ "ACPI shutdown: writing PM1a=0x{:04X} val=0x{:04X} (SLP_TYPa=0x{:X}, SLP_TYPb=0x{:X})", ++ s5.pm1a_port, val, s5.slp_typa, s5.slp_typb ++ ); +@@ -594,11 +804,13 @@ impl AcpiContext { +- 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; +- } +- }, +- None => { +- log::error!("Cannot set S-state, AML context not initialized"); +- return; ++ let mut pio = Pio::::new(s5.pm1a_port); ++ pio.write(val); ++ ++ std::thread::sleep(std::time::Duration::from_secs(3)); ++ ++ log::warn!("ACPI PM1a shutdown did not power off — retrying with PM1b"); ++ val = (1u16 << 13) | (s5.slp_typb & 0x1FFF); ++ pio.write(val); ++ ++ if s5.pm1b_port != 0 { ++ let mut pio_b = Pio::::new(s5.pm1b_port); ++ pio_b.write(val); ++ log::info!("ACPI shutdown: also wrote PM1b=0x{:04X}", s5.pm1b_port); +@@ -606 +817,0 @@ impl AcpiContext { +- }; +@@ -608,5 +819,4 @@ impl AcpiContext { +- let package = match s5.deref() { +- acpi::aml::object::Object::Package(package) => package, +- _ => { +- log::error!("Cannot set S-state, \\_S5 is not a package"); +- return; ++ std::thread::sleep(std::time::Duration::from_secs(2)); ++ log::error!("ACPI shutdown failed — falling back to keyboard controller reset"); ++ Pio::::new(0x64u16).write(0xFEu8); ++ return ShutdownResult::FallbackReset; +@@ -614 +823,0 @@ impl AcpiContext { +- }; +@@ -616,5 +825,5 @@ impl AcpiContext { +- let slp_typa = match package[0].deref() { +- acpi::aml::object::Object::Integer(i) => i.to_owned(), +- _ => { +- log::error!("typa is not an Integer"); +- return; ++ #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] ++ { ++ log::error!("ACPI shutdown not supported on this architecture"); ++ ShutdownResult::Err(ShutdownError::S5WriteFailed) ++ } +@@ -621,0 +831,8 @@ impl AcpiContext { ++ ++ /// Suspend-to-RAM (S3 sleep state) ++ /// See ACPI 6.1 spec for SLP_TYPa/SLP_TYPb encoding ++ 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; } +@@ -623,4 +840,3 @@ impl AcpiContext { +- let slp_typb = match package[1].deref() { +- acpi::aml::object::Object::Integer(i) => i.to_owned(), +- _ => { +- log::error!("typb is not an Integer"); ++ let pm1a = fadt.pm1a_control_block as u16; ++ if pm1a == 0 { ++ log::error!("ACPI S3: PM1a port is zero"); +@@ -628,0 +845,4 @@ impl AcpiContext { ++ 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; } +@@ -630,4 +850,17 @@ impl AcpiContext { +- +- log::trace!("Shutdown SLP_TYPa {:X}, SLP_TYPb {:X}", slp_typa, slp_typb); +- val |= slp_typa as u16; +- ++ 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 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); ++ log::info!("ACPI S3: writing PM1a=0x{:04X} val=0x{:04X}", pm1a, val); +@@ -635,3 +868 @@ impl AcpiContext { +- { +- log::warn!("Shutdown with ACPI outw(0x{:X}, 0x{:X})", port, val); +- Pio::::new(port).write(val); ++ { Pio::::new(pm1a).write(val); } +@@ -640 +870,0 @@ impl AcpiContext { +- // TODO: Handle SLP_TYPb +@@ -642,7 +872,16 @@ impl AcpiContext { +- #[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 +- ); ++ /// Query ACPI battery via \\_SB_.BAT0._BST ++ /// Returns (remaining_capacity_mAh, present_voltage_mV) if available ++ pub fn read_battery_status(&self) -> Option<(u32, u32)> { ++ let aml_symbols = self.aml_symbols.read(); ++ let ctx = aml_symbols.aml_context.as_ref()?; ++ let mut ns = ctx.namespace.lock(); ++ let bst_name = acpi::aml::namespace::AmlName::from_str("\\_SB_.BAT0._BST").ok()?; ++ let bst = ns.get(bst_name).ok()?; ++ match bst.deref() { ++ acpi::aml::object::Object::Package(p) if p.len() >= 4 => { ++ let cap = match p[1].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None }; ++ let volt = match p[2].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None }; ++ Some((cap, volt)) ++ } ++ _ => { log::warn!("ACPI: _BST not a valid battery package"); None } ++ } +@@ -651,2 +890,14 @@ impl AcpiContext { +- loop { +- core::hint::spin_loop(); ++ /// Query ACPI battery info via \\_SB_.BAT0._BIF ++ /// Returns (design_capacity_mAh, last_full_capacity_mAh, design_voltage_mV) if available ++ pub fn read_battery_info(&self) -> Option<(u32, u32, u32)> { ++ let aml_symbols = self.aml_symbols.read(); ++ let ctx = aml_symbols.aml_context.as_ref()?; ++ let mut ns = ctx.namespace.lock(); ++ let bif_name = acpi::aml::namespace::AmlName::from_str("\\_SB_.BAT0._BIF").ok()?; ++ let bif = ns.get(bif_name).ok()?; ++ match bif.deref() { ++ acpi::aml::object::Object::Package(p) if p.len() >= 13 => { ++ let design = match p[1].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None }; ++ let last = match p[2].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None }; ++ let volt = match p[4].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None }; ++ Some((design, last, volt)) +@@ -653,0 +905 @@ impl AcpiContext { ++ _ => { log::warn!("ACPI: _BIF not a valid battery package"); None } +@@ -656,0 +909,2 @@ impl AcpiContext { ++} ++ +@@ -752,3 +1006,4 @@ impl Fadt { +- Err(plain::Error::BadAlignment) => unreachable!( +- "plain::from_bytes reported bad alignment, but FadtAcpi2Struct is #[repr(packed)]" +- ), ++ Err(plain::Error::BadAlignment) => { ++ log::error!("plain::from_bytes reported bad alignment for FadtAcpi2Struct, but it is #[repr(packed)]"); ++ None ++ } +@@ -763,2 +1018,2 @@ impl Deref for Fadt { +- plain::from_bytes::(&self.0 .0) +- .expect("expected FADT struct to already be validated in Deref impl") ++ // SAFETY: Fadt::new validated the slice length and FadtStruct is #[repr(packed)]. ++ unsafe { &*(self.0 .0.as_ptr() as *const FadtStruct) } +@@ -777,3 +1032,7 @@ impl Fadt { +- let fadt_sdt = context +- .take_single_sdt(*b"FACP") +- .expect("expected ACPI to always have a FADT"); ++ let fadt_sdt = match context.take_single_sdt(*b"FACP") { ++ Some(sdt) => sdt, ++ None => { ++ log::error!("expected ACPI to always have a FADT"); ++ return; ++ } ++ }; +@@ -790,4 +1049,2 @@ impl Fadt { +- Some(fadt2) => usize::try_from(fadt2.x_dsdt).unwrap_or_else(|_| { +- usize::try_from(fadt.dsdt).expect("expected any given u32 to fit within usize") +- }), +- None => usize::try_from(fadt.dsdt).expect("expected any given u32 to fit within usize"), ++ Some(fadt2) => fadt2.x_dsdt as usize, ++ None => fadt.dsdt as usize, +diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs +index 059254b3..91336ba7 100644 +--- a/drivers/acpid/src/main.rs ++++ b/drivers/acpid/src/main.rs +@@ -16,0 +17,2 @@ mod ec; ++mod dmi; ++mod thermal; +@@ -31,3 +33,8 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- let rxsdt_raw_data: Arc<[u8]> = std::fs::read("/scheme/kernel.acpi/rxsdt") +- .expect("acpid: failed to read `/scheme/kernel.acpi/rxsdt`") +- .into(); ++ let rxsdt_raw_data: Arc<[u8]> = match std::fs::read("/scheme/kernel.acpi/rxsdt") { ++ Ok(data) => data.into(), ++ Err(e) => { ++ log::warn!("acpid: failed to read `/scheme/kernel.acpi/rxsdt`: {} — no ACPI", e); ++ daemon.ready(); ++ std::process::exit(0); ++ } ++ }; +@@ -41 +48,8 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- let sdt = self::acpi::Sdt::new(rxsdt_raw_data).expect("acpid: failed to parse [RX]SDT"); ++ let sdt = match self::acpi::Sdt::new(rxsdt_raw_data) { ++ Ok(sdt) => sdt, ++ Err(e) => { ++ log::warn!("acpid: failed to parse [RX]SDT: {} — booting without ACPI", e); ++ daemon.ready(); ++ std::process::exit(0); ++ } ++ }; +@@ -67 +81,5 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- _ => panic!("acpid: expected [RX]SDT from kernel to be either of those"), ++ _ => { ++ log::warn!("acpid: expected [RX]SDT from kernel to be RSDT or XSDT, got {:?} — booting without ACPI", String::from_utf8_lossy(&sdt.signature)); ++ daemon.ready(); ++ std::process::exit(0); ++ } +@@ -75,0 +94,8 @@ fn daemon(daemon: daemon::Daemon) -> ! { ++ let dmi_strings = match self::dmi::read_smbios_dmi() { ++ Ok(strings) => Some(strings), ++ Err(e) => { ++ log::warn!("Failed to read SMBIOS DMI: {}", e); ++ None ++ } ++ }; ++ +@@ -78 +104,3 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- common::acquire_port_io_rights().expect("acpid: failed to set I/O privilege level to Ring 3"); ++ if let Err(e) = common::acquire_port_io_rights() { ++ log::warn!("acpid: failed to set I/O privilege level to Ring 3: {} — continuing without port I/O", e); ++ } +@@ -80,2 +108,8 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- let shutdown_pipe = File::open("/scheme/kernel.acpi/kstop") +- .expect("acpid: failed to open `/scheme/kernel.acpi/kstop`"); ++ let shutdown_pipe = match File::open("/scheme/kernel.acpi/kstop") { ++ Ok(f) => f, ++ Err(e) => { ++ log::warn!("acpid: failed to open `/scheme/kernel.acpi/kstop`: {} — booting without ACPI shutdown", e); ++ daemon.ready(); ++ std::process::exit(0); ++ } ++ }; +@@ -83,2 +117,17 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- let mut event_queue = RawEventQueue::new().expect("acpid: failed to create event queue"); +- let socket = Socket::nonblock().expect("acpid: failed to create disk scheme"); ++ let mut event_queue = match RawEventQueue::new() { ++ Ok(q) => q, ++ Err(e) => { ++ log::warn!("acpid: failed to create event queue: {} — booting without ACPI", e); ++ daemon.ready(); ++ std::process::exit(0); ++ } ++ }; ++ ++ let socket = match Socket::nonblock() { ++ Ok(s) => s, ++ Err(e) => { ++ log::warn!("acpid: failed to create scheme socket: {} — booting without ACPI", e); ++ daemon.ready(); ++ std::process::exit(0); ++ } ++ }; +@@ -86 +135 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- let mut scheme = self::scheme::AcpiScheme::new(&acpi_context, &socket); ++ let mut scheme = self::scheme::AcpiScheme::new(&acpi_context, &socket, dmi_strings); +@@ -89,6 +138,12 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- event_queue +- .subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ) +- .expect("acpid: failed to register shutdown pipe for event queue"); +- event_queue +- .subscribe(socket.inner().raw(), 1, EventFlags::READ) +- .expect("acpid: failed to register scheme socket for event queue"); ++ if let Err(e) = event_queue ++ .subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ) { ++ log::warn!("acpid: failed to register shutdown pipe for event queue: {} — booting without ACPI", e); ++ daemon.ready(); ++ std::process::exit(0); ++ } ++ if let Err(e) = event_queue ++ .subscribe(socket.inner().raw(), 1, EventFlags::READ) { ++ log::warn!("acpid: failed to register scheme socket for event queue: {} — booting without ACPI", e); ++ daemon.ready(); ++ std::process::exit(0); ++ } +@@ -96,2 +151,8 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- register_sync_scheme(&socket, "acpi", &mut scheme) +- .expect("acpid: failed to register acpi scheme to namespace"); ++ if let Err(err) = register_sync_scheme(&socket, "acpi", &mut scheme) { ++ log::warn!( ++ "acpid: failed to register acpi scheme (error: {}). Another acpid instance may already own it.", ++ err ++ ); ++ daemon.ready(); ++ std::process::exit(0); ++ } +@@ -101 +162,3 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- libredox::call::setrens(0, 0).expect("acpid: failed to enter null namespace"); ++ if let Err(e) = libredox::call::setrens(0, 0) { ++ log::warn!("acpid: failed to enter null namespace: {} — continuing", e); ++ } +@@ -105,6 +168,7 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- let Some(event) = event_queue +- .next() +- .transpose() +- .expect("acpid: failed to read event file") +- else { +- break; ++ let event = match event_queue.next().transpose() { ++ Ok(Some(e)) => e, ++ Ok(None) => break, ++ Err(e) => { ++ log::error!("acpid: failed to read event file: {} — continuing", e); ++ continue; ++ } +@@ -115,6 +179,7 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- match handler +- .process_requests_nonblocking(&mut scheme) +- .expect("acpid: failed to process requests") +- { +- ControlFlow::Continue(()) => {} +- ControlFlow::Break(()) => break, ++ match handler.process_requests_nonblocking(&mut scheme) { ++ Ok(ControlFlow::Continue(())) => {} ++ Ok(ControlFlow::Break(())) => break, ++ Err(e) => { ++ log::error!("acpid: failed to process requests: {} — continuing", e); ++ continue; ++ } +@@ -135 +200,2 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- acpi_context.set_global_s_state(5); ++ let result = acpi_context.set_global_s_state(5); ++ log::info!("ACPI shutdown result: {:?}", result); +@@ -137 +203,2 @@ fn daemon(daemon: daemon::Daemon) -> ! { +- unreachable!("System should have shut down before this is entered"); ++ log::error!("System should have shut down before this was reached"); ++ std::process::exit(1); +@@ -141 +208 @@ fn main() { +- common::init(); ++ if let Err(err) = common::init() { eprintln!("acpid: failed to initialize common: {err}"); std::process::exit(1); } +diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs +index 5a5040c3..b92327be 100644 +--- a/drivers/acpid/src/scheme.rs ++++ b/drivers/acpid/src/scheme.rs +@@ -24,0 +25 @@ use crate::acpi::{AcpiContext, AmlSymbols, SdtSignature}; ++use crate::dmi::DmiStrings; +@@ -29 +29,0 @@ pub struct AcpiScheme<'acpi, 'sock> { +- pci_fd: Option, +@@ -30,0 +31 @@ pub struct AcpiScheme<'acpi, 'sock> { ++ dmi_text: Option, +@@ -45,0 +47,3 @@ enum HandleKind<'a> { ++ Dmi(String), ++ Thermal, ++ ThermalZone(String), +@@ -57,0 +62,3 @@ impl HandleKind<'_> { ++ Self::Dmi(_) => false, ++ Self::Thermal => true, ++ Self::ThermalZone(_) => false, +@@ -67,0 +75 @@ impl HandleKind<'_> { ++ Self::Dmi(text) => text.len(), +@@ -70,0 +79,2 @@ impl HandleKind<'_> { ++ Self::Thermal => 0, ++ Self::ThermalZone(ref text) => text.len(), +@@ -76 +86 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> { +- pub fn new(ctx: &'acpi AcpiContext, socket: &'sock Socket) -> Self { ++ pub fn new(ctx: &'acpi AcpiContext, socket: &'sock Socket, dmi: Option) -> Self { +@@ -80 +89,0 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> { +- pci_fd: None, +@@ -81,0 +91 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> { ++ dmi_text: dmi.map(|d| d.to_text()), +@@ -198,0 +209 @@ impl SchemeSync for AcpiScheme<'_, '_> { ++ ["dmi"] => HandleKind::Dmi(self.dmi_text.clone().unwrap_or_default()), +@@ -207 +218 @@ impl SchemeSync for AcpiScheme<'_, '_> { +- if let Ok(aml_symbols) = self.ctx.aml_symbols(self.pci_fd.as_ref()) { ++ if let Ok(aml_symbols) = self.ctx.aml_symbols() { +@@ -224,0 +236,9 @@ impl SchemeSync for AcpiScheme<'_, '_> { ++ ["thermal"] => HandleKind::Thermal, ++ ["thermal", zone] => { ++ if let Some(tz) = self.ctx.thermal_state.zone_by_name(zone) { ++ HandleKind::ThermalZone(tz.to_text()) ++ } else { ++ return Err(Error::new(ENOENT)); ++ } ++ } ++ +@@ -311,0 +332,2 @@ impl SchemeSync for AcpiScheme<'_, '_> { ++ HandleKind::Dmi(ref text) => text.as_bytes(), ++ HandleKind::ThermalZone(ref text) => text.as_bytes(), +@@ -335,3 +357,8 @@ impl SchemeSync for AcpiScheme<'_, '_> { +- const TOPLEVEL_ENTRIES: &[&str] = &["tables", "symbols"]; +- +- for (idx, name) in TOPLEVEL_ENTRIES ++ const TOPLEVEL_ENTRIES: &[(DirentKind, &str)] = &[ ++ (DirentKind::Regular, "dmi"), ++ (DirentKind::Directory, "tables"), ++ (DirentKind::Directory, "symbols"), ++ (DirentKind::Directory, "thermal"), ++ ]; ++ ++ for (idx, (kind, name)) in TOPLEVEL_ENTRIES +@@ -346 +373 @@ impl SchemeSync for AcpiScheme<'_, '_> { +- kind: DirentKind::Directory, ++ kind: *kind, +@@ -393,0 +421,17 @@ impl SchemeSync for AcpiScheme<'_, '_> { ++ HandleKind::Thermal => { ++ for (idx, zone) in self ++ .ctx ++ .thermal_state ++ .zones() ++ .iter() ++ .enumerate() ++ .skip(opaque_offset as usize) ++ { ++ buf.entry(DirEntry { ++ inode: 0, ++ next_opaque_id: idx as u64 + 1, ++ name: &zone.name, ++ kind: DirentKind::Regular, ++ })?; ++ } ++ } +@@ -473 +517,3 @@ impl SchemeSync for AcpiScheme<'_, '_> { +- if self.pci_fd.is_some() { ++ { ++ let mut pci_fd = self.ctx.pci_fd.write(); ++ if pci_fd.is_some() { +@@ -475,2 +521,2 @@ impl SchemeSync for AcpiScheme<'_, '_> { +- } else { +- self.pci_fd = Some(new_fd); ++ } ++ *pci_fd = Some(new_fd); diff --git a/recipes/core/base/recipe.toml b/recipes/core/base/recipe.toml index aa915fbe66..ee6d281838 100644 --- a/recipes/core/base/recipe.toml +++ b/recipes/core/base/recipe.toml @@ -79,6 +79,16 @@ patches = [ "P32-acpid-graceful-boot.patch", "P33-vesad-graceful-boot.patch", "P34-fbcond-fbbootlogd-env.patch", + "P35-fbcond-fbbootlogd-init.patch", + "P36-graphics-scheme-graceful-init.patch", + "P37-smolnetd-ready-after-init.patch", + "P38-vesad-eventqueue-deadlock.patch", + "P39-pci-allocate-interrupt-vector-graceful.patch", + "P40-bar-rs-graceful.patch", + "P41-common-init-graceful.patch", + "P42-inputd-graceful-fallback.patch", + "P43-dhcpd-requires-hard-dep.patch", + "P44-acpid-thermal-zones.patch", ] [package]