@@ -13,6 +13,7 @@ use kvm_ioctls::VcpuFd;
13
13
14
14
use super :: get_fdt_addr;
15
15
use super :: regs:: * ;
16
+ use crate :: vstate:: kvm:: OptionalCapabilities ;
16
17
use crate :: vstate:: memory:: GuestMemoryMmap ;
17
18
18
19
/// Errors thrown while setting aarch64 registers.
@@ -78,6 +79,7 @@ pub fn setup_boot_regs(
78
79
cpu_id : u8 ,
79
80
boot_ip : u64 ,
80
81
mem : & GuestMemoryMmap ,
82
+ optional_capabilities : & OptionalCapabilities ,
81
83
) -> Result < ( ) , VcpuError > {
82
84
let kreg_off = offset_of ! ( kvm_regs, regs) ;
83
85
@@ -106,6 +108,23 @@ pub fn setup_boot_regs(
106
108
vcpufd
107
109
. set_one_reg ( id, & get_fdt_addr ( mem) . to_le_bytes ( ) )
108
110
. 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
+ }
109
128
}
110
129
Ok ( ( ) )
111
130
}
@@ -226,8 +245,9 @@ mod tests {
226
245
let vm = kvm. fd . create_vm ( ) . unwrap ( ) ;
227
246
let vcpu = vm. create_vcpu ( 0 ) . unwrap ( ) ;
228
247
let mem = arch_mem ( layout:: FDT_MAX_SIZE + 0x1000 ) ;
248
+ let optional_capabilities = kvm. optional_capabilities ( ) ;
229
249
230
- let res = setup_boot_regs ( & vcpu, 0 , 0x0 , & mem) ;
250
+ let res = setup_boot_regs ( & vcpu, 0 , 0x0 , & mem, & optional_capabilities ) ;
231
251
assert ! ( matches!(
232
252
res. unwrap_err( ) ,
233
253
VcpuError :: SetOneReg ( 0x6030000000100042 , _)
@@ -237,7 +257,25 @@ mod tests {
237
257
vm. get_preferred_target ( & mut kvi) . unwrap ( ) ;
238
258
vcpu. vcpu_init ( & kvi) . unwrap ( ) ;
239
259
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 = 1000 ;
276
+
277
+ assert ! ( counter_value < max_value) ;
278
+ }
241
279
}
242
280
243
281
#[ test]
0 commit comments