use anyhow::{Context, Result, ensure};
use kvm_bindings::{Msrs, kvm_msr_entry};
use kvm_ioctls::VcpuFd;
pub(crate) fn read_one_msr(vcpu: &VcpuFd, msr_index: u32) -> Result<Option<u64>> {
let mut msrs = Msrs::from_entries(&[kvm_msr_entry {
index: msr_index,
..Default::default()
}])
.with_context(|| format!("Msrs::from_entries for MSR {msr_index:#x}"))?;
let n = vcpu
.get_msrs(&mut msrs)
.with_context(|| format!("KVM_GET_MSRS for MSR {msr_index:#x}"))?;
if n == 0 {
return Ok(None);
}
ensure!(
n == 1,
"KVM_GET_MSRS returned {n} entries for 1-MSR batch \
(MSR {msr_index:#x}) — KVM ABI violation"
);
Ok(Some(msrs.as_slice()[0].data))
}
#[cfg(test)]
pub(crate) fn read_one_msr_required(vcpu: &VcpuFd, msr_index: u32, label: &str) -> u64 {
read_one_msr(vcpu, msr_index)
.unwrap_or_else(|e| panic!("read_one_msr({msr_index:#x}) [{label}]: {e}"))
.unwrap_or_else(|| panic!("KVM did not expose MSR {msr_index:#x} ({label})"))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::vmm::x86_64::msr_indices::MSR_IA32_MISC_ENABLE;
use crate::vmm::x86_64::test_helpers::single_vcpu_kvm;
#[test]
fn read_one_msr_returns_some_on_supported_msr() {
let vm = single_vcpu_kvm();
let value = read_one_msr(&vm.vcpus[0], MSR_IA32_MISC_ENABLE).unwrap();
assert!(
value.is_some(),
"MSR_IA32_MISC_ENABLE must be exposed by KVM"
);
}
#[test]
fn read_one_msr_returns_none_on_unsupported_msr() {
let vm = single_vcpu_kvm();
let value = read_one_msr(&vm.vcpus[0], 0x2).unwrap();
assert!(value.is_none(), "MSR 0x2 must not be exposed by KVM");
}
#[test]
fn read_one_msr_matches_raw_get_msrs_value() {
let vm = single_vcpu_kvm();
let via_helper = read_one_msr(&vm.vcpus[0], MSR_IA32_MISC_ENABLE).unwrap();
let mut raw_msrs = Msrs::from_entries(&[kvm_msr_entry {
index: MSR_IA32_MISC_ENABLE,
..Default::default()
}])
.unwrap();
let n = vm.vcpus[0].get_msrs(&mut raw_msrs).unwrap();
assert_eq!(n, 1);
let via_raw = raw_msrs.as_slice()[0].data;
assert_eq!(via_helper, Some(via_raw));
}
}