Skip to content

Commit 88c243e

Browse files
committed
feat: reset SYS_CNTPCT_EL0 on VM boot
Reset SYS_CNTPCT_EL0 performance counter register on VM boot to avoid passing through host performance counter. Note that resetting the register on VM boot does not guarantee that VM will see the counter value 0 at startup because there is a delta in time between register reset and VM boot during which counter continues to advance. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent a5ffb7a commit 88c243e

File tree

1 file changed

+23
-0
lines changed

1 file changed

+23
-0
lines changed

src/vmm/src/arch/aarch64/vcpu.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::path::PathBuf;
1010

1111
use kvm_bindings::*;
1212
use kvm_ioctls::VcpuFd;
13+
use log::warn;
1314

1415
use super::get_fdt_addr;
1516
use super::regs::*;
@@ -106,6 +107,19 @@ pub fn setup_boot_regs(
106107
vcpufd
107108
.set_one_reg(id, &get_fdt_addr(mem).to_le_bytes())
108109
.map_err(|err| VcpuError::SetOneReg(id, err))?;
110+
111+
// Reset the physical counter for the guest. This way we avoid guest reading
112+
// host physical counter.
113+
// Resetting SYS_CNTPCT_EL0 for singe vcpu is enough because there is only 1 performance
114+
// counter in the system.
115+
// Because the access to SYS_CNTPCT_EL0 is not present in all kernels, we don't
116+
// fail if ioctl returns an error.
117+
// Note: the value observed by the guest will still be above 0, because there is a delta
118+
// time between this resetting and first call to KVM_RUN
119+
let zero: u64 = 0;
120+
if let Err(_) = vcpufd.set_one_reg(SYS_CNTPCT_EL0, &zero.to_le_bytes()) {
121+
warn!("Unable to reset performance counter. VM will use host value instead.");
122+
}
109123
}
110124
Ok(())
111125
}
@@ -238,6 +252,15 @@ mod tests {
238252
vcpu.vcpu_init(&kvi).unwrap();
239253

240254
setup_boot_regs(&vcpu, 0, 0x0, &mem).unwrap();
255+
256+
// Check that the register is reset on compatible kernels.
257+
// Because there is a delta in time between we reset the register and time we
258+
// read it, we cannot compare with 0. Choose some meaningfully small value instead.
259+
let mut reg_bytes = [0_u8; 8];
260+
if let Ok(_) = vcpufd.get_one_reg(SYS_CNTPCT_EL0, &mut reg_bytes) {
261+
let counter_value = u64::from_le_bytes(reg_bytes);
262+
assert!(counter_value < 10_000);
263+
}
241264
}
242265

243266
#[test]

0 commit comments

Comments
 (0)