Skip to content

Commit

Permalink
feat: reset KVM_REG_ARM_PTIMER_CNT on VM boot
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
ShadowCurse committed Jan 10, 2025
1 parent a5ffb7a commit 772dc49
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/vmm/src/arch/aarch64/regs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ arm64_sys_reg!(SYS_CNTV_CVAL_EL0, 3, 3, 14, 3, 2);
// https://elixir.bootlin.com/linux/v6.8/source/arch/arm64/include/asm/sysreg.h#L459
arm64_sys_reg!(SYS_CNTPCT_EL0, 3, 3, 14, 0, 1);

// Physical Timer EL0 count Register
// The id of this register is same as SYS_CNTPCT_EL0, but KVM defines it
// separately, so we do as well.
// https://elixir.bootlin.com/linux/v6.12.6/source/arch/arm64/include/uapi/asm/kvm.h#L259
arm64_sys_reg!(KVM_REG_ARM_PTIMER_CNT, 3, 3, 14, 0, 1);

// Translation Table Base Register
// https://developer.arm.com/documentation/ddi0595/2021-03/AArch64-Registers/TTBR1-EL1--Translation-Table-Base-Register-1--EL1-
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);
Expand Down
26 changes: 26 additions & 0 deletions src/vmm/src/arch/aarch64/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::path::PathBuf;

use kvm_bindings::*;
use kvm_ioctls::VcpuFd;
use log::warn;

use super::get_fdt_addr;
use super::regs::*;
Expand Down Expand Up @@ -106,6 +107,22 @@ pub fn setup_boot_regs(
vcpufd
.set_one_reg(id, &get_fdt_addr(mem).to_le_bytes())
.map_err(|err| VcpuError::SetOneReg(id, err))?;

// Reset the physical counter for the guest. This way we avoid guest reading
// host physical counter.
// Resetting KVM_REG_ARM_PTIMER_CNT for singe vcpu is enough because there is only
// one timer struct with offsets per VM.
// Because the access to KVM_REG_ARM_PTIMER_CNT is only present starting 6.4 kernel,
// we don't fail if ioctl returns an error.
// Note: the value observed by the guest will still be above 0, because there is a delta
// time between this resetting and first call to KVM_RUN
let zero: u64 = 0;
if vcpufd
.set_one_reg(KVM_REG_ARM_PTIMER_CNT, &zero.to_le_bytes())
.is_err()
{
warn!("Unable to reset performance counter. VM will use host value instead.");
}
}
Ok(())
}
Expand Down Expand Up @@ -238,6 +255,15 @@ mod tests {
vcpu.vcpu_init(&kvi).unwrap();

setup_boot_regs(&vcpu, 0, 0x0, &mem).unwrap();

// Check that the register is reset on compatible kernels.
// Because there is a delta in time between we reset the register and time we
// read it, we cannot compare with 0. Choose some meaningfully small value instead.
let mut reg_bytes = [0_u8; 8];
if vcpu.get_one_reg(SYS_CNTPCT_EL0, &mut reg_bytes).is_ok() {
let counter_value = u64::from_le_bytes(reg_bytes);
assert!(counter_value < 10_000);
}
}

#[test]
Expand Down

0 comments on commit 772dc49

Please sign in to comment.