From 88555c342dc15941baf826c1e5aae6b4599ae551 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Tue, 30 Jun 2026 12:57:04 +0300 Subject: [PATCH] redbear-power: add HWP MSR constants and accessors (Phase G.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../system/redbear-power/source/src/msr.rs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/local/recipes/system/redbear-power/source/src/msr.rs b/local/recipes/system/redbear-power/source/src/msr.rs index ae89d2984d..02634657f5 100644 --- a/local/recipes/system/redbear-power/source/src/msr.rs +++ b/local/recipes/system/redbear-power/source/src/msr.rs @@ -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 { 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 { + read_msr(cpu, IA32_HWP_REQUEST) +} + +/// Read the current HWP status value (MSR 0x777). +pub fn read_hwp_status(cpu: u32) -> Option { + 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)]