From a1c57196b3175ff0829b271a1619348b734fde7f Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Fri, 10 Jan 2025 11:27:02 +0000 Subject: [PATCH 1/3] feat: reset KVM_REG_ARM_PTIMER_CNT on VM boot Reset KVM_REG_ARM_PTIMER_CNT physical counter register on VM boot to avoid passing through host physical 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 --- src/vmm/src/arch/aarch64/regs.rs | 6 ++++++ src/vmm/src/arch/aarch64/vcpu.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/vmm/src/arch/aarch64/regs.rs b/src/vmm/src/arch/aarch64/regs.rs index 5238f58ba70..d844fbfb56b 100644 --- a/src/vmm/src/arch/aarch64/regs.rs +++ b/src/vmm/src/arch/aarch64/regs.rs @@ -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); diff --git a/src/vmm/src/arch/aarch64/vcpu.rs b/src/vmm/src/arch/aarch64/vcpu.rs index 80fc5a339df..fd17d213ee4 100644 --- a/src/vmm/src/arch/aarch64/vcpu.rs +++ b/src/vmm/src/arch/aarch64/vcpu.rs @@ -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::*; @@ -106,6 +107,24 @@ 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 single 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. + // Path series which introduced the needed changes: + // https://lore.kernel.org/all/20230330174800.2677007-1-maz@kernel.org/ + // 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 VM physical counter. VM will use host value instead."); + } } Ok(()) } @@ -238,6 +257,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] From 57b458b37062868e07a7bc88f4f09fd486079385 Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Fri, 10 Jan 2025 11:37:21 +0000 Subject: [PATCH 2/3] chore: add an entry to the CHANGELOG Add an entry about physical counter reset to the CHANGELOG. Signed-off-by: Egor Lazarchuk --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e402b85ad43..c624c6807d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ and this project adheres to ### Added +- [#4987](https://github.com/firecracker-microvm/firecracker/pull/4987): Reset + physical counter register (`CNTPCT_EL0`) on VM startup. This avoids VM reading + the host physical counter value. This is only possible on 6.4 and newer + kernels. For older kernels a warning will be printed to the logs. + ### Changed - [#4913](https://github.com/firecracker-microvm/firecracker/pull/4913): Removed From b5be9915f7b306b05cb8b6bb2b9e13ee6c971edf Mon Sep 17 00:00:00 2001 From: Egor Lazarchuk Date: Fri, 10 Jan 2025 17:19:39 +0000 Subject: [PATCH 3/3] chore: update prod-host-setup.md with arm physical counter info Update a note about physical counter on ARM being reset instead of directly passed through. Signed-off-by: Egor Lazarchuk --- docs/prod-host-setup.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/prod-host-setup.md b/docs/prod-host-setup.md index f046e31c735..3073af7dc0d 100644 --- a/docs/prod-host-setup.md +++ b/docs/prod-host-setup.md @@ -328,13 +328,13 @@ For vendor-specific recommendations, please consult the resources below: - ARM: [Speculative Processor Vulnerability](https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability) -##### [ARM only] Physical counter directly passed through to the guest +##### [ARM only] VM Physical counter reset on VM boot -On ARM, the physical counter (i.e `CNTPCT`) it is returning the -[actual EL1 physical counter value of the host][1]. From the discussions before -merging this change [upstream][2], this seems like a conscious design decision -of the ARM code contributors, giving precedence to performance over the ability -to trap and control this in the hypervisor. +On ARM, Firecracker tries to reset the `CNTPCT` physical counter on VM boot. +This is done in order to prevent VM from reading host physical counter value. +Because this is only possible in kernels containing [this](https://lore.kernel.org/all/20230330174800.2677007-1-maz@kernel.org/) +patch series (6.4 and newer) Firecracker does not fail if it cannot reset the counter +and instead prints a warning message. ##### Verification