diff --git a/local/recipes/system/coretempd/source/src/main.rs b/local/recipes/system/coretempd/source/src/main.rs index 0a15652f84..e7d48ade29 100644 --- a/local/recipes/system/coretempd/source/src/main.rs +++ b/local/recipes/system/coretempd/source/src/main.rs @@ -53,6 +53,17 @@ fn probe_msr_available() -> bool { } fn detect_vendor(cpu: u32) -> Vendor { + // Prefer platform info from /scheme/sys/cpu — avoids MSR access + if let Ok(d) = fs::read_to_string("/scheme/sys/cpu") { + let lower = d.to_lowercase(); + if lower.contains("intel") || lower.contains("genuineintel") { + return Vendor::Intel; + } + if lower.contains("amd") || lower.contains("authenticamd") { + return Vendor::Amd; + } + } + // Fall back to MSR probing if platform info unavailable if read_msr(cpu, IA32_THERM_STATUS).is_some() { Vendor::Intel } else if read_msr(cpu, AMD_TCTL).is_some() { @@ -63,21 +74,27 @@ fn detect_vendor(cpu: u32) -> Vendor { } fn detect_cpus() -> Vec { - let mut v = Vec::new(); + // Try /scheme/sys/cpu first for CPU count if let Ok(d) = fs::read_to_string("/scheme/sys/cpu") { - for l in d.lines() { - if let Some(id_str) = l.strip_prefix("CPU ") { - if let Some((num, _)) = id_str.split_once(':') { - if let Ok(id) = num.trim().parse::() { - v.push(id); - } + let mut v = Vec::new(); + for line in d.lines() { + if line.starts_with("CPUs: ") { + if let Ok(count) = line[6..].trim().parse::() { + return (0..count).collect(); } } } } - if v.is_empty() { - v.push(0); + // Fall back to /dev/cpu + let mut v = Vec::new(); + if let Ok(e) = fs::read_dir("/dev/cpu") { + for x in e.flatten() { + if let Ok(n) = x.file_name().into_string() { + if let Ok(id) = n.parse() { v.push(id); } + } + } } + if v.is_empty() { v.push(0); } v } diff --git a/local/recipes/system/cpufreqd/source/src/main.rs b/local/recipes/system/cpufreqd/source/src/main.rs index d81bbdadf0..08a2a47cad 100644 --- a/local/recipes/system/cpufreqd/source/src/main.rs +++ b/local/recipes/system/cpufreqd/source/src/main.rs @@ -39,6 +39,53 @@ fn detect_cpus() -> Vec { v } +fn read_sys_cpu_info() -> (String, u32, u32) { + let mut vendor = String::new(); + let mut base_mhz: u32 = 0; + let mut max_mhz: u32 = 0; + if let Ok(d) = fs::read_to_string("/scheme/sys/cpu") { + for line in d.lines() { + if let Some(v) = line.strip_prefix("Vendor: ") { + vendor = v.to_string(); + } else if let Some(b) = line.strip_prefix("CPU Base MHz: ") { + base_mhz = b.parse().unwrap_or(0); + } else if let Some(m) = line.strip_prefix("CPU Max MHz: ") { + max_mhz = m.parse().unwrap_or(0); + } + } + } + (vendor, base_mhz, max_mhz) +} + +fn generate_pstates_from_freq(base_mhz: u32, max_mhz: u32, is_intel: bool) -> Vec { + if max_mhz == 0 || base_mhz == 0 { + return vec![]; + } + let bus_mhz = if base_mhz > 0 { base_mhz } else { 100 }; + let max_ratio = max_mhz * 1000 / bus_mhz; // convert to kHz ratio + let min_ratio = (max_ratio / 2).max(8); // reasonable minimum + + let mut states = Vec::new(); + let steps = 4; // generate 4 P-states + for i in 0..steps { + let ratio = max_ratio - (i * (max_ratio - min_ratio) / (steps - 1)); + let freq_khz = ratio * bus_mhz; + // Intel SpeedStep encoding: ratio in bits 15:8 + let ctl = if is_intel { + ((ratio as u64) << 8) & 0xFF00 + } else { + ratio as u64 // AMD uses raw ratio in lower bits + }; + states.push(PState { + freq_khz, + power_mw: (35000 * ratio / max_ratio) as u32, + latency_us: 10, + ctl, + }); + } + states +} + fn read_acpi_pss(cpu: u32) -> Vec { let path = format!("/scheme/acpi/processor/CPU{}/pss", cpu); if let Ok(d) = fs::read_to_string(&path) { @@ -53,12 +100,7 @@ fn read_acpi_pss(cpu: u32) -> Vec { } if !s.is_empty() { return s; } } - vec![ - PState { freq_khz: 2400, power_mw: 35000, latency_us: 10, ctl: 0x1a00 }, - PState { freq_khz: 2000, power_mw: 25000, latency_us: 10, ctl: 0x1600 }, - PState { freq_khz: 1600, power_mw: 18000, latency_us: 10, ctl: 0x1200 }, - PState { freq_khz: 1200, power_mw: 12000, latency_us: 10, ctl: 0x0e00 }, - ] + vec![] } fn write_msr_raw(cpu: u32, msr: u32, val: u64) -> bool { @@ -156,6 +198,10 @@ fn main() { std::process::exit(if ok { 0 } else { 1 }); } + let (vendor, base_mhz, max_mhz) = read_sys_cpu_info(); + let is_intel = vendor.to_lowercase().contains("intel"); + eprintln!("[INFO] cpufreqd: platform {} base={}MHz max={}MHz", vendor, base_mhz, max_mhz); + let governor = match env::var("CPUFREQ_GOVERNOR").unwrap_or_default().as_str() { "performance" => Governor::Performance, "powersave" => Governor::Powersave, "conservative" => Governor::Conservative, "schedutil" => Governor::Schedutil, @@ -164,11 +210,6 @@ fn main() { let cpus = detect_cpus(); eprintln!("[INFO] cpufreqd: detected {} CPU(s), governor={:?}", cpus.len(), governor); - // Probe MSR availability before attempting any writes. - // In environments without MSR 0x199 (QEMU default, some hypervisors), - // wrmsr causes a kernel #GP that kills the process. By spawning a child - // to test the write first, we detect this safely and degrade to - // monitoring-only mode. let msr_ok = cpus.iter().all(|&id| probe_msr_available(id)); if !msr_ok { MSR_AVAILABLE.store(false, Ordering::Relaxed); @@ -176,7 +217,10 @@ fn main() { } let mut ci: Vec = cpus.iter().map(|&id| { - let ps = read_acpi_pss(id); + let mut ps = read_acpi_pss(id); + if ps.is_empty() { + ps = generate_pstates_from_freq(base_mhz, max_mhz, is_intel); + } eprintln!("[INFO] cpufreqd: CPU{}: {} P-states ({} - {} kHz)", id, ps.len(), ps.first().map_or(0, |p| p.freq_khz), ps.last().map_or(0, |p| p.freq_khz)); CpuInfo { id, pstates: ps, current_idx: 0, load_history: [0.0; SAMPLE_WINDOW], load_idx: 0, throttle: false, msr_errors: 0, msr_suppressed: false } }).collect();