diff --git a/drivers/acpi-rs/src/aml/mod.rs b/drivers/acpi-rs/src/aml/mod.rs index 693fff8467..a6c27b12db 100644 --- a/drivers/acpi-rs/src/aml/mod.rs +++ b/drivers/acpi-rs/src/aml/mod.rs @@ -255,7 +255,61 @@ where warn!("Invoking \\_SB._INI failed: {:?}", err); } - // TODO: run all _REGs for globally-installed handlers (this might need more bookkeeping) + // _REG opregion connect (ACPICA evrgnini.c): run \_SB._REG(space, 1) + // per installed handler — firmware gates EC access behind this. + let installed_spaces: Vec = + self.region_handlers.lock().keys().copied().collect(); + for space in &installed_spaces { + let space_id = u8::from(*space) as u64; + if let Err(err) = self.evaluate_if_present( + AmlName::from_str("\\_SB._REG").unwrap(), + vec![Object::Integer(space_id).wrap(), Object::Integer(1).wrap()], + ) { + warn!("\\_SB._REG({:?}, connect) failed: {:?}", space, err); + } + } + + // Device-level _REG: for each device holding an OpRegion of an + // installed space, run ._REG(space, 1). + { + let mut reg_namespace = self.namespace.lock().clone(); + let _ = reg_namespace.traverse(|path, level| { + match level.kind { + NamespaceLevelKind::Device + | NamespaceLevelKind::Processor + | NamespaceLevelKind::ThermalZone + | NamespaceLevelKind::PowerResource => { + let mut device_spaces: Vec = level + .values + .values() + .filter_map(|(_, obj)| match &**obj { + Object::OpRegion(region) + if installed_spaces.contains(®ion.space) => + { + Some(region.space) + } + _ => None, + }) + .collect(); + device_spaces.sort(); + device_spaces.dedup(); + for space in device_spaces { + let space_id = u8::from(space) as u64; + if let Ok(reg_path) = + AmlName::from_str("_REG").unwrap().resolve(path) + { + let _ = self.evaluate_if_present( + reg_path, + vec![Object::Integer(space_id).wrap(), Object::Integer(1).wrap()], + ); + } + } + Ok(true) + } + _ => Ok(true), + } + }); + } /* * We can now initialize each device in the namespace. For each device, we evaluate `_STA`, diff --git a/drivers/acpi-rs/src/aml/op_region.rs b/drivers/acpi-rs/src/aml/op_region.rs index 7eaa6b5b1a..3fd60f5527 100644 --- a/drivers/acpi-rs/src/aml/op_region.rs +++ b/drivers/acpi-rs/src/aml/op_region.rs @@ -54,3 +54,22 @@ impl From for RegionSpace { } } } + +impl From for u8 { + fn from(space: RegionSpace) -> u8 { + match space { + RegionSpace::SystemMemory => 0, + RegionSpace::SystemIO => 1, + RegionSpace::PciConfig => 2, + RegionSpace::EmbeddedControl => 3, + RegionSpace::SmBus => 4, + RegionSpace::SystemCmos => 5, + RegionSpace::PciBarTarget => 6, + RegionSpace::Ipmi => 7, + RegionSpace::GeneralPurposeIo => 8, + RegionSpace::GenericSerialBus => 9, + RegionSpace::Pcc => 10, + RegionSpace::Oem(value) => value, + } + } +} diff --git a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs index 6073977dd1..931a857f51 100644 --- a/drivers/acpid/src/acpi.rs +++ b/drivers/acpid/src/acpi.rs @@ -1841,6 +1841,10 @@ impl Fadt { pub fn supports_s0_idle(&self) -> bool { (self.flags & (1 << 21)) != 0 } + + pub fn is_hardware_reduced(&self) -> bool { + (self.flags & (1 << 20)) != 0 + } } impl Deref for Fadt { diff --git a/drivers/acpid/src/power_events.rs b/drivers/acpid/src/power_events.rs index 7e5bd680e7..08d8ea5361 100644 --- a/drivers/acpid/src/power_events.rs +++ b/drivers/acpid/src/power_events.rs @@ -60,6 +60,13 @@ pub fn init_power_events(context: &AcpiContext) -> Result<(), Box> { log::warn!("acpid: no FADT; GPE/PM1 power events unavailable"); return Ok(()); }; + if fadt.is_hardware_reduced() { + // HW-reduced ACPI (FADT bit 20): no PM1/GPE register blocks. Sleep and + // wake use the HW-reduced sleep control/status registers instead. + log::info!("acpid: hardware-reduced ACPI platform — PM1/GPE blocks absent"); + *context.gpe.write() = None; + return Ok(()); + } let blocks = GpeBlocks::from_fadt(&fadt); log::info!( "acpid: SCI irq {} gpe0={:?} gpe1={:?} pm1a_evt={:#x?} pm1_evt_len={}", diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs index 7ef1e2296e..a066f14e9d 100644 --- a/drivers/acpid/src/scheme.rs +++ b/drivers/acpid/src/scheme.rs @@ -127,6 +127,7 @@ enum ThermalFileKind { Temperature, Passive, Critical, + Active, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -430,6 +431,7 @@ impl SchemeSync for AcpiScheme<'_, '_> { "temperature" => ThermalFileKind::Temperature, "passive" => ThermalFileKind::Passive, "critical" => ThermalFileKind::Critical, + "active" => ThermalFileKind::Active, _ => return Err(Error::new(ENOENT)), }; HandleKind::ThermalZone { @@ -683,18 +685,40 @@ impl SchemeSync for AcpiScheme<'_, '_> { dmi_buf.as_bytes() } HandleKind::ThermalZone { zone, kind } => { - let method = match kind { - ThermalFileKind::Temperature => "_TMP", - ThermalFileKind::Passive => "_PSV", - ThermalFileKind::Critical => "_CRT", - }; - let values = self - .ctx - .evaluate_acpi_method(zone, method, &[]) - .map_err(|_| Error::new(EIO))?; - let raw = values.first().copied().unwrap_or(0); - dmi_buf = format!("{}\n", raw); - dmi_buf.as_bytes() + match kind { + ThermalFileKind::Active => { + // _AC0.._AC9 active cooling trip points (tenths of K); + // only the defined ones are returned, one per line. + let mut lines = String::new(); + for idx in 0..10u8 { + let method = format!("_AC{idx}"); + if let Ok(values) = + self.ctx.evaluate_acpi_method(zone, &method, &[]) + { + if let Some(v) = values.first() { + lines.push_str(&format!("{method}={v}\n")); + } + } + } + dmi_buf = lines; + dmi_buf.as_bytes() + } + _ => { + let method = match kind { + ThermalFileKind::Temperature => "_TMP", + ThermalFileKind::Passive => "_PSV", + ThermalFileKind::Critical => "_CRT", + ThermalFileKind::Active => unreachable!(), + }; + let values = self + .ctx + .evaluate_acpi_method(zone, method, &[]) + .map_err(|_| Error::new(EIO))?; + let raw = values.first().copied().unwrap_or(0); + dmi_buf = format!("{}\n", raw); + dmi_buf.as_bytes() + } + } } HandleKind::PmTimer => { let fadt = self.ctx.fadt().ok_or(Error::new(ENODEV))?; diff --git a/drivers/acpid/src/wake.rs b/drivers/acpid/src/wake.rs index 1064eb7d62..6968249123 100644 --- a/drivers/acpid/src/wake.rs +++ b/drivers/acpid/src/wake.rs @@ -6,10 +6,12 @@ use crate::acpi::AcpiContext; use crate::gpe::GpeBlocks; const LPIT_TYPE_NATIVE_CSTATE: u32 = 0x00; +const GAS_SPACE_FIXED_HARDWARE: u8 = 0x7F; #[derive(Clone, Debug)] pub struct LpiEntry { pub unique_id: u16, + pub entry_trigger_space_id: u8, pub entry_trigger_addr: u64, pub residency_us: u32, pub latency_us: u32, @@ -19,6 +21,16 @@ pub struct LpiEntry { pub no_counter: bool, } +impl LpiEntry { + pub fn mwait_hint(&self) -> Option { + if self.entry_trigger_space_id == GAS_SPACE_FIXED_HARDWARE { + Some(self.entry_trigger_addr as u32) + } else { + None + } + } +} + #[derive(Clone, Debug, Default)] pub struct LpitInfo { pub entries: Vec, @@ -57,40 +69,31 @@ impl LpitInfo { let disabled = (flags & 1) != 0; let no_counter = (flags & 2) != 0; - if type_ == LPIT_TYPE_NATIVE_CSTATE && length >= 48 { + if type_ == LPIT_TYPE_NATIVE_CSTATE && length >= 56 { + let entry_trigger_space_id = data[offset + 16]; let entry_trigger_addr = u64::from_le_bytes([ - data[offset + 16], - data[offset + 17], - data[offset + 18], - data[offset + 19], data[offset + 20], data[offset + 21], data[offset + 22], data[offset + 23], - ]); - let residency_us = u32::from_le_bytes([ data[offset + 24], data[offset + 25], data[offset + 26], data[offset + 27], ]); - let latency_us = u32::from_le_bytes([ + let residency_us = u32::from_le_bytes([ data[offset + 28], data[offset + 29], data[offset + 30], data[offset + 31], ]); - let residency_counter_addr = u64::from_le_bytes([ + let latency_us = u32::from_le_bytes([ data[offset + 32], data[offset + 33], data[offset + 34], data[offset + 35], - data[offset + 36], - data[offset + 37], - data[offset + 38], - data[offset + 39], ]); - let counter_frequency = u64::from_le_bytes([ + let residency_counter_addr = u64::from_le_bytes([ data[offset + 40], data[offset + 41], data[offset + 42], @@ -100,8 +103,19 @@ impl LpitInfo { data[offset + 46], data[offset + 47], ]); + let counter_frequency = u64::from_le_bytes([ + data[offset + 48], + data[offset + 49], + data[offset + 50], + data[offset + 51], + data[offset + 52], + data[offset + 53], + data[offset + 54], + data[offset + 55], + ]); entries.push(LpiEntry { unique_id, + entry_trigger_space_id, entry_trigger_addr, residency_us, latency_us, @@ -134,6 +148,10 @@ impl LpitInfo { .filter(|e| !e.disabled) .max_by_key(|e| e.residency_us) } + + pub fn best_mwait_hint(&self) -> Option { + self.deepest_lpi().and_then(|e| e.mwait_hint()) + } } #[derive(Clone, Debug)] @@ -323,26 +341,36 @@ impl SleepStates { mod tests { use super::*; - #[test] - fn lpit_parse_native_cstate() { + fn lpi_entry(unique_id: u16, residency: u32, space_id: u8, trigger: u64) -> Vec { let mut data = Vec::new(); data.extend_from_slice(&0u32.to_le_bytes()); - data.extend_from_slice(&48u32.to_le_bytes()); - data.extend_from_slice(&1u16.to_le_bytes()); + data.extend_from_slice(&56u32.to_le_bytes()); + data.extend_from_slice(&unique_id.to_le_bytes()); data.extend_from_slice(&0u16.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); - data.extend_from_slice(&0x1000u64.to_le_bytes()); - data.extend_from_slice(&300u32.to_le_bytes()); + data.push(space_id); + data.extend_from_slice(&[0u8; 3]); + data.extend_from_slice(&trigger.to_le_bytes()); + data.extend_from_slice(&residency.to_le_bytes()); data.extend_from_slice(&10u32.to_le_bytes()); + data.extend_from_slice(&[0u8; 4]); data.extend_from_slice(&0x2000u64.to_le_bytes()); data.extend_from_slice(&19200u64.to_le_bytes()); + data + } + + #[test] + fn lpit_parse_native_cstate() { + let data = lpi_entry(1, 300, GAS_SPACE_FIXED_HARDWARE, 0x60); let info = LpitInfo::parse(&data).unwrap(); assert_eq!(info.entries.len(), 1); assert_eq!(info.entries[0].unique_id, 1); assert_eq!(info.entries[0].residency_us, 300); assert_eq!(info.entries[0].latency_us, 10); + assert_eq!(info.entries[0].entry_trigger_addr, 0x60); assert!(!info.entries[0].disabled); assert!(!info.entries[0].no_counter); + assert_eq!(info.entries[0].mwait_hint(), Some(0x60)); } #[test] @@ -359,21 +387,21 @@ mod tests { #[test] fn lpit_deepest_entry() { let mut data = Vec::new(); - for (id, residency) in [(1u16, 100u32), (2, 500), (3, 50)] { - data.extend_from_slice(&0u32.to_le_bytes()); - data.extend_from_slice(&48u32.to_le_bytes()); - data.extend_from_slice(&id.to_le_bytes()); - data.extend_from_slice(&0u16.to_le_bytes()); - data.extend_from_slice(&0u32.to_le_bytes()); - data.extend_from_slice(&0x1000u64.to_le_bytes()); - data.extend_from_slice(&residency.to_le_bytes()); - data.extend_from_slice(&10u32.to_le_bytes()); - data.extend_from_slice(&0x2000u64.to_le_bytes()); - data.extend_from_slice(&19200u64.to_le_bytes()); - } + data.extend(lpi_entry(1, 100, GAS_SPACE_FIXED_HARDWARE, 0x20)); + data.extend(lpi_entry(2, 500, GAS_SPACE_FIXED_HARDWARE, 0x60)); + data.extend(lpi_entry(3, 50, GAS_SPACE_FIXED_HARDWARE, 0x10)); let info = LpitInfo::parse(&data).unwrap(); let deepest = info.deepest_lpi().unwrap(); assert_eq!(deepest.unique_id, 2); assert_eq!(deepest.residency_us, 500); + assert_eq!(info.best_mwait_hint(), Some(0x60)); + } + + #[test] + fn lpit_no_ffh_hint_when_not_fixed_hardware() { + let data = lpi_entry(1, 300, 0x00, 0x60); + let info = LpitInfo::parse(&data).unwrap(); + assert_eq!(info.entries[0].mwait_hint(), None); + assert_eq!(info.best_mwait_hint(), None); } }