@@ -13,6 +13,7 @@ use kvm_ioctls::VcpuFd;
1313
1414use super :: get_fdt_addr;
1515use super :: regs:: * ;
16+ use crate :: vstate:: kvm:: OptionalCapabilities ;
1617use crate :: vstate:: memory:: GuestMemoryMmap ;
1718
1819/// Errors thrown while setting aarch64 registers.
@@ -78,6 +79,7 @@ pub fn setup_boot_regs(
7879 cpu_id : u8 ,
7980 boot_ip : u64 ,
8081 mem : & GuestMemoryMmap ,
82+ optional_capabilities : & OptionalCapabilities ,
8183) -> Result < ( ) , VcpuError > {
8284 let kreg_off = offset_of ! ( kvm_regs, regs) ;
8385
@@ -106,6 +108,23 @@ pub fn setup_boot_regs(
106108 vcpufd
107109 . set_one_reg ( id, & get_fdt_addr ( mem) . to_le_bytes ( ) )
108110 . map_err ( |err| VcpuError :: SetOneReg ( id, err) ) ?;
111+
112+ // Reset the physical counter for the guest. This way we avoid guest reading
113+ // host physical counter.
114+ // Resetting KVM_REG_ARM_PTIMER_CNT for single vcpu is enough because there is only
115+ // one timer struct with offsets per VM.
116+ // Because the access to KVM_REG_ARM_PTIMER_CNT is only present starting 6.4 kernel,
117+ // we only do the reset if KVM_CAP_COUNTER_OFFSET is present as it was added
118+ // in the same patch series as the ability to set the KVM_REG_ARM_PTIMER_CNT register.
119+ // Path series which introduced the needed changes:
120+ // https://lore.kernel.org/all/[email protected] / 121+ // Note: the value observed by the guest will still be above 0, because there is a delta
122+ // time between this resetting and first call to KVM_RUN.
123+ if optional_capabilities. counter_offset {
124+ vcpufd
125+ . set_one_reg ( KVM_REG_ARM_PTIMER_CNT , & [ 0 ; 8 ] )
126+ . map_err ( |err| VcpuError :: SetOneReg ( id, err) ) ?;
127+ }
109128 }
110129 Ok ( ( ) )
111130}
@@ -226,8 +245,9 @@ mod tests {
226245 let vm = kvm. fd . create_vm ( ) . unwrap ( ) ;
227246 let vcpu = vm. create_vcpu ( 0 ) . unwrap ( ) ;
228247 let mem = arch_mem ( layout:: FDT_MAX_SIZE + 0x1000 ) ;
248+ let optional_capabilities = kvm. optional_capabilities ( ) ;
229249
230- let res = setup_boot_regs ( & vcpu, 0 , 0x0 , & mem) ;
250+ let res = setup_boot_regs ( & vcpu, 0 , 0x0 , & mem, & optional_capabilities ) ;
231251 assert ! ( matches!(
232252 res. unwrap_err( ) ,
233253 VcpuError :: SetOneReg ( 0x6030000000100042 , _)
@@ -237,7 +257,25 @@ mod tests {
237257 vm. get_preferred_target ( & mut kvi) . unwrap ( ) ;
238258 vcpu. vcpu_init ( & kvi) . unwrap ( ) ;
239259
240- setup_boot_regs ( & vcpu, 0 , 0x0 , & mem) . unwrap ( ) ;
260+ setup_boot_regs ( & vcpu, 0 , 0x0 , & mem, & optional_capabilities) . unwrap ( ) ;
261+
262+ // Check that the register is reset on compatible kernels.
263+ // Because there is a delta in time between we reset the register and time we
264+ // read it, we cannot compare with 0. Instead we compare it with meaningfully
265+ // small value.
266+ if optional_capabilities. counter_offset {
267+ let mut reg_bytes = [ 0_u8 ; 8 ] ;
268+ vcpu. get_one_reg ( SYS_CNTPCT_EL0 , & mut reg_bytes) . unwrap ( ) ;
269+ let counter_value = u64:: from_le_bytes ( reg_bytes) ;
270+
271+ // We are reading the SYS_CNTPCT_EL0 right after resetting it.
272+ // If reset did happen successfully, the value should be quite small when we read it.
273+ // If the reset did not happen, the value will be same as on the host and it surely
274+ // will be more that MAX_VALUE.
275+ let MAX_VALUE = 500_000_000 ;
276+
277+ assert ! ( counter_value < MAX_VALUE ) ;
278+ }
241279 }
242280
243281 #[ test]
0 commit comments