redbear-power: add HWP MSR constants and accessors (Phase G.2)
The MSR library was missing the HWP (Hardware P-states / Intel Speed
Shift) MSR set. Arrow Lake-H exposes HWP via:
IA32_PM_ENABLE (0x770) bit 0: HWP_ENABLE
IA32_HWP_CAPABILITIES (0x771) [31:0]: HWP range
IA32_HWP_REQUEST (0x774) [42:0]: min/max/desired/EPP/activity
IA32_HWP_STATUS (0x777): current operating point
IA32_PERF_STATUS (0x198): legacy current P-state
IA32_PLATFORM_INFO (0xCE): max non-turbo / min ratios
MSR_TURBO_RATIO_LIMIT (0x1AD): per-core turbo ratios
IA32_ENERGY_PERF_BIAS (0x1B0): power-perf hint
Add:
- hwp_enabled(cpu) → reads MSR 0x770 bit 0
- hwp_capabilities(cpu) → reads MSR 0x771, returns
(lowest, most_efficient, guaranteed, highest)
- read_hwp_request(cpu) → reads MSR 0x774
- read_hwp_status(cpu) → reads MSR 0x777
The TUI can now show a live "HWP active" indicator, the HWP range
percentages, and the current HWP request value. The HWP range
is computed once at startup; updates need only a re-read of
MSR 0x774 (8 bytes, ~microseconds).
The phase-G.1 kernel MSR scheme (commit 8cd4f69) provides the
in-memory storage for these reads. On real hardware, the kernel
will write the actual MSR values; on QEMU they default to zero
which makes the TUI display "HWP inactive" without erroring.
This commit is contained in:
@@ -22,6 +22,15 @@ use std::path::Path;
|
||||
pub const IA32_THERM_STATUS: u32 = 0x19c;
|
||||
pub const IA32_PACKAGE_THERM_STATUS: u32 = 0x1b1;
|
||||
pub const IA32_PERF_CTL: u32 = 0x199;
|
||||
pub const IA32_PERF_STATUS: u32 = 0x198;
|
||||
pub const IA32_PLATFORM_INFO: u32 = 0xCE;
|
||||
pub const IA32_PM_ENABLE: u32 = 0x770;
|
||||
pub const IA32_HWP_CAPABILITIES: u32 = 0x771;
|
||||
pub const IA32_HWP_REQUEST: u32 = 0x774;
|
||||
pub const IA32_HWP_REQUEST_PKG: u32 = 0x772;
|
||||
pub const IA32_HWP_STATUS: u32 = 0x777;
|
||||
pub const MSR_TURBO_RATIO_LIMIT: u32 = 0x1AD;
|
||||
pub const IA32_ENERGY_PERF_BIAS: u32 = 0x1B0;
|
||||
|
||||
// --- RAPL (Running Average Power Limit) MSRs ---
|
||||
// Intel SDM Vol.4 + Linux arch/x86/include/asm/msr-index.h
|
||||
@@ -179,6 +188,39 @@ pub fn read_current_perf_ctl(cpu: u32) -> Option<u64> {
|
||||
read_msr(cpu, IA32_PERF_CTL)
|
||||
}
|
||||
|
||||
/// Check whether HWP (Hardware P-states / Intel Speed Shift) is enabled.
|
||||
/// Returns true if bit 0 of IA32_PM_ENABLE (MSR 0x770) is set.
|
||||
pub fn hwp_enabled(cpu: u32) -> bool {
|
||||
read_msr(cpu, IA32_PM_ENABLE)
|
||||
.map(|pm| pm & 1 != 0)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Read HWP range from IA32_HWP_CAPABILITIES (MSR 0x771).
|
||||
/// Returns (lowest, guaranteed, most_efficient, highest) performance
|
||||
/// values, all as u8 percentages of the maximum. The values come from
|
||||
/// the HWP cap register: [7:0] highest, [15:8] guaranteed, [23:16]
|
||||
/// most efficient, [31:24] lowest (per Intel SDM Vol 3B §14.4.3).
|
||||
pub fn hwp_capabilities(cpu: u32) -> Option<(u8, u8, u8, u8)> {
|
||||
let raw = read_msr(cpu, IA32_HWP_CAPABILITIES)?;
|
||||
let lowest = ((raw >> 24) & 0xFF) as u8;
|
||||
let efficient = ((raw >> 16) & 0xFF) as u8;
|
||||
let guaranteed = ((raw >> 8) & 0xFF) as u8;
|
||||
let highest = (raw & 0xFF) as u8;
|
||||
Some((lowest, efficient, guaranteed, highest))
|
||||
}
|
||||
|
||||
/// Read the current HWP request value (MSR 0x774). Returns the u64
|
||||
/// value as-is; the caller can decode the min/max/desired/EPP fields.
|
||||
pub fn read_hwp_request(cpu: u32) -> Option<u64> {
|
||||
read_msr(cpu, IA32_HWP_REQUEST)
|
||||
}
|
||||
|
||||
/// Read the current HWP status value (MSR 0x777).
|
||||
pub fn read_hwp_status(cpu: u32) -> Option<u64> {
|
||||
read_msr(cpu, IA32_HWP_STATUS)
|
||||
}
|
||||
|
||||
/// Decoded view of `IA32_PACKAGE_THERM_STATUS`. Built from the raw MSR
|
||||
/// value in `app.rs:refresh()`. Empty/default when the MSR is unreadable.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
|
||||
Reference in New Issue
Block a user