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:
2026-07-22 22:07:02 +09:00
parent bd1b251f70
commit 00da984779
2 changed files with 31 additions and 9 deletions
+5 -8
View File
@@ -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
View File
@@ -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)
}
}
}
}