cpu-temp 0.1.0

An Intel CPU temperature monitoring library for Windows and Linux using MSR access
Documentation
use anyhow::Result;

#[cfg(target_os = "windows")]
use crate::pawnio::PawnIo;

// Embedded IntelMSR binary module
#[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;

    // 格式:方法名, setter名: 高位, 低位;

    // Bit 0: Thermal Status
    pub thermal_status, _: 0;
    // Bit 1: Thermal Status Log
    pub thermal_status_log, set_thermal_status_log: 1;
    // Bit 2: PROCHOT # or FORCEPR# event
    pub prochot_event, _: 2;
    // Bit 3: PROCHOT # or FORCEPR# log
    pub prochot_log, set_prochot_log: 3;
    // Bit 4: Critical Temperature Status
    pub critical_temp_status, _: 4;
    // Bit 5: Critical Temperature Status log
    pub critical_temp_log, set_critical_temp_log: 5;
    // Bit 6: Thermal Threshold #1 Status
    pub thermal_threshold_1_status, _: 6;
    // Bit 7: Thermal Threshold #1 log
    pub thermal_threshold_1_log, set_thermal_threshold_1_log: 7;
    // Bit 8: Thermal Threshold #2 Status
    pub thermal_threshold_2_status, _: 8;
    // Bit 9: Thermal Threshold #2 log
    pub thermal_threshold_2_log, set_thermal_threshold_2_log: 9;
    // Bit 10: Power Limitation Status
    pub power_limitation_status, _: 10;
    // Bit 11: Power Limitation log
    pub power_limitation_log, set_power_limitation_log: 11;
    // Bit 12: Current Limit Status
    pub current_limit_status, _: 12;
    // Bit 13: Current Limit log
    pub current_limit_log, set_current_limit_log: 13;
    // Bit 14: Cross Domain Limit Status
    pub cross_domain_limit_status, _: 14;
    // Bit 15: Cross Domain Limit log
    pub cross_domain_limit_log, set_cross_domain_limit_log: 15;
    // Bits 22-16: Digital Readout
    pub digital_readout, _: 22, 16;
    // Bits 30-27: Resolution in Degrees Celsius
    pub resolution, _: 30, 27;
    // Bit 31: Reading Valid
    pub reading_valid, _: 31;
}

bitfield::bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq)]
    pub struct Ia32PackageThermStatus(u64);
    impl Debug;

    // Bit 0: Pkg Thermal Status (R/O)
    pub pkg_thermal_status, _: 0;
    // Bit 1: Pkg Thermal Status Log (R/W)
    pub pkg_thermal_status_log, set_pkg_thermal_status_log: 1;
    // Bit 2: Pkg PROCHOT # event (R/O)
    pub pkg_prochot_event, _: 2;
    // Bit 3: Pkg PROCHOT # log (R/WCO)
    pub pkg_prochot_log, set_pkg_prochot_log: 3;
    // Bit 4: Pkg Critical Temperature Status (R/O)
    pub pkg_critical_temp_status, _: 4;
    // Bit 5: Pkg Critical Temperature Status Log (R/WCO)
    pub pkg_critical_temp_log, set_pkg_critical_temp_log: 5;
    // Bit 6: Pkg Thermal Threshold #1 Status (R/O)
    pub pkg_thermal_threshold_1_status, _: 6;
    // Bit 7: Pkg Thermal Threshold #1 Log (R/WCO)
    pub pkg_thermal_threshold_1_log, set_pkg_thermal_threshold_1_log: 7;
    // Bit 8: Pkg Thermal Threshold #2 Status (R/O)
    pub pkg_thermal_threshold_2_status, _: 8;
    // Bit 9: Pkg Thermal Threshold #2 Log (R/WCO) - Corrected index based on context
    pub pkg_thermal_threshold_2_log, set_pkg_thermal_threshold_2_log: 9;
    // Bit 10: Pkg Power Limitation Status (R/O)
    pub pkg_power_limitation_status, _: 10;
    // Bit 11: Pkg Power Limitation Log (R/WCO)
    pub pkg_power_limitation_log, set_pkg_power_limitation_log: 11;
    // Bits 22-16: Pkg Digital Readout (R/O)
    pub pkg_digital_readout, _: 22, 16;
    // Bit 26: Hardware Feedback Interface Structure Change Status
    pub hw_feedback_interface_change_status, _: 26;
}

bitfield::bitfield! {
    #[derive(Clone, Copy, PartialEq, Eq)]
    pub struct Ia32TemperatureTarget(u64);
    impl Debug;

    // Bits 23-16: Temperature Target (R)
    pub temperature_target, _: 23, 16;
    // Bits 29-24: Target Offset (R/W)
    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 {
    /// Creates a new MSR accessor for the given CPU index (Linux only)
    #[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 })
    }

    /// Creates a new MSR accessor (Windows only - uses PawnIO)
    #[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")?;

        // The input buffer contains the MSR index as a single i64 value
        let input = [msr_index as u64];
        // The output will be an 8-byte value (u64) containing both EAX (low 32 bits) and EDX (high 32 bits)
        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;

        // 定位到MSR寄存器
        self.msr_file.seek(SeekFrom::Start(
            (msr_index as u64) * size_of::<u64>() as u64,
        ))?;

        // 读取MSR值(8字节)
        let mut buffer = [0u8; 8];
        self.msr_file.read_exact(&mut buffer)?;

        // MSR的低32位存储在EAX中,高32位存储在EDX中
        let value = u64::from_le_bytes(buffer);
        let eax = value as u32;
        let edx = (value >> 32) as u32;

        Ok(value)
    }

    /// 读取核心温度状态MSR
    pub fn read_therm_status(&self) -> anyhow::Result<Ia32ThermStatus> {
        let val = self.read_msr(IA32_THERM_STATUS_MSR)?;
        Ok(Ia32ThermStatus(val))
    }

    /// 读取封装温度状态MSR
    pub fn read_package_therm_status(&self) -> anyhow::Result<Ia32PackageThermStatus> {
        let val = self.read_msr(IA32_PACKAGE_THERM_STATUS)?;
        Ok(Ia32PackageThermStatus(val))
    }

    /// 读取温度目标值(TjMax)
    pub fn read_temperature_target(&self) -> anyhow::Result<Ia32TemperatureTarget> {
        let val = self.read_msr(IA32_TEMPERATURE_TARGET)?;
        Ok(Ia32TemperatureTarget(val))
    }
}