kernel: consume LPIT MWAIT hint in idle_loop via AcpiVerb::SetLpiHint
- acpi.rs: LPI_MWAIT_HINT/LPI_MWAIT_HINT_SET atomics + set/get accessors; AcpiVerb::SetLpiHint handler reads the 4-byte LE payload and stores the firmware-recommended MWAIT hint. - interrupt/mod.rs idle_loop(): prefer the LPIT entry_trigger hint (set by acpid) for mwait_loop(); fall back to CPUID leaf-5 max substate when no LPIT hint is available (no LPIT table, or pre-acpid boot). This is the full Modern Standby integration: the CPU now enters the firmware-optimal deepest LPI state during s2idle, not merely the CPUID-reported deepest state.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
+26
-1
@@ -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<u32> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user