use anyhow::Result;
#[cfg(target_os = "windows")]
use crate::pawnio::PawnIo;
#[cfg(target_os = "windows")]
const INTEL_MSR_BIN: &[u8] = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/res/pawnio/IntelMSR.bin"
));
pub const IA32_THERM_STATUS_MSR: u32 = 0x019C;
pub const IA32_PACKAGE_THERM_STATUS: u32 = 0x01B1;
pub const IA32_TEMPERATURE_TARGET: u32 = 0x01A2;
bitfield::bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Ia32ThermStatus(u64);
impl Debug;
pub thermal_status, _: 0;
pub thermal_status_log, set_thermal_status_log: 1;
pub prochot_event, _: 2;
pub prochot_log, set_prochot_log: 3;
pub critical_temp_status, _: 4;
pub critical_temp_log, set_critical_temp_log: 5;
pub thermal_threshold_1_status, _: 6;
pub thermal_threshold_1_log, set_thermal_threshold_1_log: 7;
pub thermal_threshold_2_status, _: 8;
pub thermal_threshold_2_log, set_thermal_threshold_2_log: 9;
pub power_limitation_status, _: 10;
pub power_limitation_log, set_power_limitation_log: 11;
pub current_limit_status, _: 12;
pub current_limit_log, set_current_limit_log: 13;
pub cross_domain_limit_status, _: 14;
pub cross_domain_limit_log, set_cross_domain_limit_log: 15;
pub digital_readout, _: 22, 16;
pub resolution, _: 30, 27;
pub reading_valid, _: 31;
}
bitfield::bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Ia32PackageThermStatus(u64);
impl Debug;
pub pkg_thermal_status, _: 0;
pub pkg_thermal_status_log, set_pkg_thermal_status_log: 1;
pub pkg_prochot_event, _: 2;
pub pkg_prochot_log, set_pkg_prochot_log: 3;
pub pkg_critical_temp_status, _: 4;
pub pkg_critical_temp_log, set_pkg_critical_temp_log: 5;
pub pkg_thermal_threshold_1_status, _: 6;
pub pkg_thermal_threshold_1_log, set_pkg_thermal_threshold_1_log: 7;
pub pkg_thermal_threshold_2_status, _: 8;
pub pkg_thermal_threshold_2_log, set_pkg_thermal_threshold_2_log: 9;
pub pkg_power_limitation_status, _: 10;
pub pkg_power_limitation_log, set_pkg_power_limitation_log: 11;
pub pkg_digital_readout, _: 22, 16;
pub hw_feedback_interface_change_status, _: 26;
}
bitfield::bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Ia32TemperatureTarget(u64);
impl Debug;
pub temperature_target, _: 23, 16;
pub target_offset, set_target_offset: 29, 24;
}
pub struct IntelMsr {
#[cfg(target_os = "linux")]
msr_file: std::fs::File,
#[cfg(target_os = "windows")]
pawn_io: Option<PawnIo>,
}
impl IntelMsr {
#[cfg(target_os = "linux")]
pub fn new(cpu_index: u32) -> Result<Self, std::io::Error> {
let path = format!("/dev/cpu/{}/msr", cpu_index);
let msr_file = std::fs::File::open(&path)?;
Ok(IntelMsr { msr_file })
}
#[cfg(target_os = "windows")]
pub fn new() -> Result<Self> {
let pawn_io = PawnIo::load_module(INTEL_MSR_BIN)
.map_err(|e| anyhow::anyhow!("Failed to load IntelMSR module: {}", e))?;
Ok(IntelMsr {
pawn_io: Some(pawn_io),
})
}
#[cfg(target_os = "windows")]
pub fn read_msr(&self, msr_index: u32) -> Result<u64> {
use anyhow::Context as _;
let pawn_io = self.pawn_io.as_ref().context("PawnIO module not loaded")?;
let input = [msr_index as u64];
let result = pawn_io.execute("ioctl_read_msr", &input, 1);
if !result.success || result.output.is_empty() {
anyhow::bail!("Failed to read MSR 0x{:08X}", msr_index);
}
Ok(result.output[0] as u64)
}
#[cfg(target_os = "linux")]
pub fn read_msr(&mut self, msr_index: u32) -> anyhow::Result<u64> {
use std::io::{Read, Seek, SeekFrom};
use std::mem::size_of;
self.msr_file.seek(SeekFrom::Start(
(msr_index as u64) * size_of::<u64>() as u64,
))?;
let mut buffer = [0u8; 8];
self.msr_file.read_exact(&mut buffer)?;
let value = u64::from_le_bytes(buffer);
let eax = value as u32;
let edx = (value >> 32) as u32;
Ok(value)
}
pub fn read_therm_status(&self) -> anyhow::Result<Ia32ThermStatus> {
let val = self.read_msr(IA32_THERM_STATUS_MSR)?;
Ok(Ia32ThermStatus(val))
}
pub fn read_package_therm_status(&self) -> anyhow::Result<Ia32PackageThermStatus> {
let val = self.read_msr(IA32_PACKAGE_THERM_STATUS)?;
Ok(Ia32PackageThermStatus(val))
}
pub fn read_temperature_target(&self) -> anyhow::Result<Ia32TemperatureTarget> {
let val = self.read_msr(IA32_TEMPERATURE_TARGET)?;
Ok(Ia32TemperatureTarget(val))
}
}