diff --git a/src/arch/x86_shared/interrupt/mod.rs b/src/arch/x86_shared/interrupt/mod.rs index ad1ba0367f..efb4081c77 100644 --- a/src/arch/x86_shared/interrupt/mod.rs +++ b/src/arch/x86_shared/interrupt/mod.rs @@ -144,14 +144,11 @@ pub unsafe fn idle_loop() { enable_and_halt(); } else { // MWAIT supported. Enter the deepest substate, break on any - // interrupt (ecx=0). - // - // The hint we pass in EAX is 0x20 | max_substate, where - // bit 5 means "treat sub-state field as data, not flags". - // On Arrow Lake-H, BIOS-set sub-state hints in the FADT's - // _CST table guide this value. The kernel doesn't pick - // the state — that's the BIOS/firmware's job. - let eax_hint: u32 = 0x20 | (max_substate as u32); + // interrupt (ecx=0). Prefer the LPIT entry_trigger hint (set by + // acpid via AcpiVerb::SetLpiHint); fall back to CPUID leaf-5 max + // substate when no LPIT hint is available. + let eax_hint: u32 = crate::scheme::acpi::lpi_mwait_hint() + .unwrap_or(0x20 | (max_substate as u32)); enable_and_halt(); // interrupts must be enabled first mwait_loop(eax_hint, 0); } diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs index 9997b7e0e6..342558eca2 100644 --- a/src/scheme/acpi.rs +++ b/src/scheme/acpi.rs @@ -1,5 +1,5 @@ use alloc::boxed::Box; -use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering}; use crate::sync::ordered::{Mutex, L4}; use spin::Once; @@ -108,6 +108,20 @@ pub fn kstop_set_s3_slp_typ(slp_typ: u8) { S3_SLP_TYP.store(slp_typ, Ordering::Release); } +static LPI_MWAIT_HINT: AtomicU32 = AtomicU32::new(0); +static LPI_MWAIT_HINT_SET: AtomicBool = AtomicBool::new(false); + +pub fn set_lpi_mwait_hint(hint: u32) { + LPI_MWAIT_HINT.store(hint, Ordering::Release); + LPI_MWAIT_HINT_SET.store(true, Ordering::Release); +} + +pub fn lpi_mwait_hint() -> Option { + LPI_MWAIT_HINT_SET + .load(Ordering::Acquire) + .then(|| LPI_MWAIT_HINT.load(Ordering::Acquire)) +} + /// Set by the kstop handler when acpid requests s2idle entry. /// Idempotent. pub fn s2idle_request_set() { @@ -362,6 +376,17 @@ impl KernelScheme for AcpiScheme { // press). Ok(0) } + AcpiVerb::SetLpiHint => { + // Firmware-recommended MWAIT hint from LPIT entry_trigger + // (FFH GAS address). 4-byte LE payload. + let mut buf = [0u8; 4]; + let copied = payload.copy_common_bytes_to_slice(&mut buf)?; + if copied != 4 { + return Err(Error::new(EINVAL)); + } + set_lpi_mwait_hint(u32::from_le_bytes(buf)); + Ok(0) + } } } } \ No newline at end of file