cpufreqd: correct VM detection paths (Redox DMI + CPUID hypervisor bit)
The earlier commit (6d1b11726) read /sys/class/dmi/id/sys_vendor
and /sys/class/dmi/id/product_name. Those are the Linux paths.
Redox exposes SMBIOS fields at /scheme/acpi/dmi/<field> via the
acpid userspace daemon. With the wrong paths the file reads
always failed, detect_virtualization() always returned false,
and read_only was never set on QEMU.
Now we read /scheme/acpi/dmi/sys_vendor and product_name (the
Redox-correct paths), and as a fallback when SMBIOS is absent
or uninformative we check the CPUID hypervisor-present bit
(leaf 1 ECX bit 31) via inline assembly. The CPUID pattern
mirrors local/recipes/system/redbear-power/source/src/cpuid.rs:168
which already uses this bit for the same purpose.
When either signal indicates virtualization, every CpuInfo is
constructed with read_only = true and apply_pstate() short-
circuits at the top. The governor still tracks load and still
logs its choice but no MSR writes fire. On bare metal the
existing path is preserved exactly.
The companion kernel fix in local/sources/kernel (commit
c2312627 on master) corrects a path-strip bug in the sys
scheme dispatcher that was preventing every MSR open from
succeeding with ENOENT. With both fixes together, cpufreqd
on QEMU enters read-only mode and the Ondemand governor stops
the P0->P1->P0 oscillation.
This commit is contained in:
@@ -333,28 +333,72 @@ fn write_scheme_state(governor: Governor, cpus: &[CpuInfo]) {
|
||||
}
|
||||
|
||||
fn detect_virtualization() -> bool {
|
||||
// Detect a hypervisor / VM by reading DMI strings. On QEMU the
|
||||
// sys_vendor is "QEMU" or "KVM" or similar; on real hardware
|
||||
// (e.g. LG Gram 2025) it's "LG Electronics" or "Intel Corporation".
|
||||
// Returning true here means: "the cpufreqd governor's P-state
|
||||
// writes are not going to take effect because the emulator
|
||||
// doesn't model IA32_PERF_STATUS / IA32_PERF_CTL."
|
||||
if let Ok(s) = fs::read_to_string("/sys/class/dmi/id/sys_vendor") {
|
||||
// Detect a hypervisor / VM before applying P-state changes.
|
||||
//
|
||||
// Two signals are checked:
|
||||
//
|
||||
// 1. DMI strings at the Redox-correct path /scheme/acpi/dmi/<field>
|
||||
// (NOT /sys/class/dmi/id/<field> — that is the Linux path).
|
||||
// The Redox acpid exposes SMBIOS fields there. On QEMU with
|
||||
// OVMF the strings are "QEMU", "KVM", or similar; on real
|
||||
// hardware (e.g. LG Gram 2025) they are the OEM strings
|
||||
// ("LG Electronics", "16Z90TR"). When SMBIOS is absent (the
|
||||
// Redox acpid log line "SMBIOS: no entry point found" can
|
||||
// happen on some QEMU configurations), the read returns Err
|
||||
// and we fall through to the CPUID check.
|
||||
//
|
||||
// 2. The CPUID hypervisor-present bit (leaf 1, ECX bit 31) read
|
||||
// via inline assembly. This is the canonical x86 VM-detection
|
||||
// signal — it works regardless of whether SMBIOS is exposed.
|
||||
// On QEMU/KVM/VMware/VirtualBox/Hyper-V/Xen the bit is set;
|
||||
// on bare metal it is clear. The pattern mirrors
|
||||
// redbear-power/src/cpuid.rs:168 which already uses this bit.
|
||||
//
|
||||
// When either signal says "virtualized", cpufreqd is constructed
|
||||
// with read_only = true and apply_pstate short-circuits at the
|
||||
// top: load is still tracked, governor choice is still logged,
|
||||
// but no MSR writes fire. On real hardware the writes go to the
|
||||
// kernel scheme (/scheme/sys/msr/{cpu}/0x{msr_hex}) and the
|
||||
// governor progresses through P-states normally.
|
||||
if let Ok(s) = fs::read_to_string("/scheme/acpi/dmi/sys_vendor") {
|
||||
let s = s.to_ascii_lowercase();
|
||||
if s.contains("qemu") || s.contains("kvm") || s.contains("vmware")
|
||||
|| s.contains("virtualbox") || s.contains("hyper-v") || s.contains("xen")
|
||||
|| s.contains("microsoft corporation") || s.contains("amazon")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Ok(s) = fs::read_to_string("/sys/class/dmi/id/product_name") {
|
||||
if let Ok(s) = fs::read_to_string("/scheme/acpi/dmi/product_name") {
|
||||
let s = s.to_ascii_lowercase();
|
||||
if s.contains("virtual") || s.contains("kvm") || s.contains("qemu") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// No /sys/class/dmi on this system (Redox bare metal) — assume
|
||||
// real hardware. cpufreqd's P-state writes are meaningful.
|
||||
// SMBIOS absent or uninformative: fall back to the CPUID
|
||||
// hypervisor-present bit. Inline assembly because the Redox
|
||||
// kernel does not expose raw CPUID leaves via a scheme.
|
||||
if cpuid_hypervisor_bit() {
|
||||
return true;
|
||||
}
|
||||
// No SMBIOS, no CPUID bit: assume real hardware. cpufreqd's
|
||||
// P-state writes through /scheme/sys/msr/{cpu}/0x{msr_hex}
|
||||
// are meaningful.
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn cpuid_hypervisor_bit() -> bool {
|
||||
// CPUID leaf 1, subleaf 0, ECX bit 31 — the standard x86
|
||||
// hypervisor-present bit. Returns true when running under
|
||||
// any hypervisor that advertises itself (QEMU/KVM, VMware,
|
||||
// VirtualBox, Hyper-V, Xen, bhyve, etc.).
|
||||
let cpuid = unsafe { core::arch::x86_64::__cpuid_count(1, 0) };
|
||||
(cpuid.ecx & (1 << 31)) != 0
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
fn cpuid_hypervisor_bit() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
+1
-1
Submodule local/sources/kernel updated: a8042049ce...c231262700
Reference in New Issue
Block a user