coretempd: Add AMD Zen temperature sensor support
Detect CPU vendor by probing MSRs (Intel IA32_THERM_STATUS vs AMD TCTL MSR C0010293). Support both Intel Tjmax-based and AMD direct temperature reading. Log detected vendor per CPU at startup.
This commit is contained in:
@@ -1,13 +1,21 @@
|
||||
use std::fs;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::path::Path;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
const POLL_MS: u64 = 2000;
|
||||
|
||||
const IA32_THERM_STATUS: u32 = 0x19c;
|
||||
const IA32_TEMPERATURE_TARGET: u32 = 0x1a2;
|
||||
const POLL_MS: u64 = 2000;
|
||||
const AMD_TCTL: u32 = 0xc0010293;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum Vendor {
|
||||
Intel,
|
||||
Amd,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
|
||||
let path = format!("/scheme/sys/msr/{}/{:x}", cpu, msr);
|
||||
@@ -15,6 +23,16 @@ fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
|
||||
.and_then(|s| u64::from_str_radix(s.trim(), 16).ok())
|
||||
}
|
||||
|
||||
fn detect_vendor(cpu: u32) -> Vendor {
|
||||
if read_msr(cpu, IA32_THERM_STATUS).is_some() {
|
||||
Vendor::Intel
|
||||
} else if read_msr(cpu, AMD_TCTL).is_some() {
|
||||
Vendor::Amd
|
||||
} else {
|
||||
Vendor::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_cpus() -> Vec<u32> {
|
||||
let mut v = Vec::new();
|
||||
if let Ok(d) = fs::read_to_string("/scheme/sys/cpu") {
|
||||
@@ -30,7 +48,7 @@ fn detect_cpus() -> Vec<u32> {
|
||||
v
|
||||
}
|
||||
|
||||
fn read_temperature(cpu: u32, tjmax: u8) -> Option<i16> {
|
||||
fn read_temperature_intel(cpu: u32, tjmax: u8) -> Option<i16> {
|
||||
let raw = read_msr(cpu, IA32_THERM_STATUS)?;
|
||||
let digital_readout = ((raw >> 16) & 0x7F) as u8;
|
||||
if digital_readout == 0 { return None; }
|
||||
@@ -38,7 +56,7 @@ fn read_temperature(cpu: u32, tjmax: u8) -> Option<i16> {
|
||||
Some(temp as i16)
|
||||
}
|
||||
|
||||
fn read_tjmax(cpu: u32) -> u8 {
|
||||
fn read_tjmax_intel(cpu: u32) -> u8 {
|
||||
if let Some(raw) = read_msr(cpu, IA32_TEMPERATURE_TARGET) {
|
||||
let tj = ((raw >> 16) & 0xFF) as u8;
|
||||
if tj > 0 && tj < 150 { return tj; }
|
||||
@@ -46,6 +64,21 @@ fn read_tjmax(cpu: u32) -> u8 {
|
||||
100
|
||||
}
|
||||
|
||||
fn read_temperature_amd(cpu: u32) -> Option<i16> {
|
||||
let raw = read_msr(cpu, AMD_TCTL)?;
|
||||
let tctl = ((raw >> 21) & 0x3FF) as u16;
|
||||
if tctl == 0 { return None; }
|
||||
let temp = (tctl as f32) / 8.0;
|
||||
Some(temp as i16)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct CpuInfo {
|
||||
id: u32,
|
||||
vendor: Vendor,
|
||||
tjmax: u8,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let scheme_path = ":coretemp";
|
||||
let _ = fs::remove_file(scheme_path);
|
||||
@@ -55,16 +88,29 @@ fn main() {
|
||||
let cpus = detect_cpus();
|
||||
eprintln!("[INFO] coretempd: detected {} CPU(s)", cpus.len());
|
||||
|
||||
let tjmax_values: Vec<u8> = cpus.iter().map(|&c| read_tjmax(c)).collect();
|
||||
let cpu_infos: Vec<CpuInfo> = cpus.iter().map(|&id| {
|
||||
let vendor = detect_vendor(id);
|
||||
let tjmax = if vendor == Vendor::Intel {
|
||||
read_tjmax_intel(id)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
eprintln!("[INFO] coretempd: CPU {} = {:?}", id, vendor);
|
||||
CpuInfo { id, vendor, tjmax }
|
||||
}).collect();
|
||||
|
||||
let cpus_clone = cpus.clone();
|
||||
let tjmax_clone = tjmax_values.clone();
|
||||
let infos_clone = cpu_infos.clone();
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
thread::sleep(Duration::from_millis(POLL_MS));
|
||||
for (&cpu, &tjmax) in cpus_clone.iter().zip(&tjmax_clone) {
|
||||
if let Some(temp) = read_temperature(cpu, tjmax) {
|
||||
let _ = fs::write(format!("/tmp/coretemp_cpu{}", cpu), format!("{}\n", temp));
|
||||
for info in &infos_clone {
|
||||
let temp = match info.vendor {
|
||||
Vendor::Intel => read_temperature_intel(info.id, info.tjmax),
|
||||
Vendor::Amd => read_temperature_amd(info.id),
|
||||
Vendor::Unknown => None,
|
||||
};
|
||||
if let Some(t) = temp {
|
||||
let _ = fs::write(format!("/tmp/coretemp_cpu{}", info.id), format!("{}\n", t));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,15 +123,20 @@ fn main() {
|
||||
let req = String::from_utf8_lossy(&buf[..n]).trim().to_string();
|
||||
let resp = if req == "/" {
|
||||
let mut names = String::new();
|
||||
for &cpu in &cpus {
|
||||
names.push_str(&format!("cpu{}\n", cpu));
|
||||
for info in &cpu_infos {
|
||||
names.push_str(&format!("cpu{}\n", info.id));
|
||||
}
|
||||
names
|
||||
} else if let Some(cpu_str) = req.strip_prefix("/cpu") {
|
||||
if let Ok(cpu) = cpu_str.parse::<u32>() {
|
||||
if let Some(idx) = cpus.iter().position(|&c| c == cpu) {
|
||||
if let Some(temp) = read_temperature(cpu, tjmax_values[idx]) {
|
||||
format!("{}\n", temp)
|
||||
if let Some(info) = cpu_infos.iter().find(|i| i.id == cpu) {
|
||||
let temp = match info.vendor {
|
||||
Vendor::Intel => read_temperature_intel(info.id, info.tjmax),
|
||||
Vendor::Amd => read_temperature_amd(info.id),
|
||||
Vendor::Unknown => None,
|
||||
};
|
||||
if let Some(t) = temp {
|
||||
format!("{}\n", t)
|
||||
} else {
|
||||
"N/A\n".to_string()
|
||||
}
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../local/recipes/system/coretempd
|
||||
Reference in New Issue
Block a user