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:
Red Bear OS
2026-06-30 12:57:04 +03:00
committed by vasilito
parent d24d0e2174
commit 88555c342d
@@ -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)]