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();
|
enable_and_halt();
|
||||||
} else {
|
} else {
|
||||||
// MWAIT supported. Enter the deepest substate, break on any
|
// MWAIT supported. Enter the deepest substate, break on any
|
||||||
// interrupt (ecx=0).
|
// interrupt (ecx=0). Prefer the LPIT entry_trigger hint (set by
|
||||||
//
|
// acpid via AcpiVerb::SetLpiHint); fall back to CPUID leaf-5 max
|
||||||
// The hint we pass in EAX is 0x20 | max_substate, where
|
// substate when no LPIT hint is available.
|
||||||
// bit 5 means "treat sub-state field as data, not flags".
|
let eax_hint: u32 = crate::scheme::acpi::lpi_mwait_hint()
|
||||||
// On Arrow Lake-H, BIOS-set sub-state hints in the FADT's
|
.unwrap_or(0x20 | (max_substate as u32));
|
||||||
// _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);
|
|
||||||
enable_and_halt(); // interrupts must be enabled first
|
enable_and_halt(); // interrupts must be enabled first
|
||||||
mwait_loop(eax_hint, 0);
|
mwait_loop(eax_hint, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-1
@@ -1,5 +1,5 @@
|
|||||||
use alloc::boxed::Box;
|
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 crate::sync::ordered::{Mutex, L4};
|
||||||
use spin::Once;
|
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);
|
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.
|
/// Set by the kstop handler when acpid requests s2idle entry.
|
||||||
/// Idempotent.
|
/// Idempotent.
|
||||||
pub fn s2idle_request_set() {
|
pub fn s2idle_request_set() {
|
||||||
@@ -362,6 +376,17 @@ impl KernelScheme for AcpiScheme {
|
|||||||
// press).
|
// press).
|
||||||
Ok(0)
|
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