Skip to content

Commit c7cd11d

Browse files
committed
feat: reset KVM_REG_ARM_PTIMER_CNT on VM boot
Reset KVM_REG_ARM_PTIMER_CNT 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 c7cd11d

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

src/vmm/src/arch/aarch64/regs.rs

+6
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ arm64_sys_reg!(SYS_CNTV_CVAL_EL0, 3, 3, 14, 3, 2);
9999
// https://elixir.bootlin.com/linux/v6.8/source/arch/arm64/include/asm/sysreg.h#L459
100100
arm64_sys_reg!(SYS_CNTPCT_EL0, 3, 3, 14, 0, 1);
101101

102+
// Physical Timer EL0 count Register
103+
// The id of this register is same as SYS_CNTPCT_EL0, but KVM defines it
104+
// separately, so we do as well.
105+
// https://elixir.bootlin.com/linux/v6.12.6/source/arch/arm64/include/uapi/asm/kvm.h#L259
106+
arm64_sys_reg!(KVM_REG_ARM_PTIMER_CNT, 3, 3, 14, 0, 1);
107+
102108
// Translation Table Base Register
103109
// https://developer.arm.com/documentation/ddi0595/2021-03/AArch64-Registers/TTBR1-EL1--Translation-Table-Base-Register-1--EL1-
104110
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);

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

+23
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 KVM_REG_ARM_PTIMER_CNT for singe vcpu is enough because there is only
114+
// one timer struct with offsets per VM.
115+
// Because the access to KVM_REG_ARM_PTIMER_CNT is only present starting 6.4 kernel,
116+
// we don't 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(KVM_REG_ARM_PTIMER_CNT, &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)