HVF virtual counter diverges from CLOCK_VIRTUAL when the host sleeps
Host environment
- Operating system: macOS
- OS/kernel version:
Darwin aki-MacBook-Air.local 21.3.0 Darwin Kernel Version 21.3.0: Wed Jan 5 21:37:58 PST 2022; root:xnu-8019.80.24~20/RELEASE_ARM64_T8101 arm64 - Architecture: arm64
- QEMU flavor: qemu-system-aarch64
- QEMU version: 6621441d
- QEMU command line:
./qemu-system-aarch64 -machine virt,accel=hvf,highmem=off -cpu cortex-a72 -drive if=pflash,file=edk2-aarch64-code.fd -drive if=pflash,file=edk2-arm-vars.fd -drive if=virtio,file=fedora.raw
Emulated/Virtualized environment
- Operating system: Fedora 35
- OS/kernel version:
Linux fedora-qemu 5.15.17-200.fc35.aarch64 #1 SMP Thu Jan 27 16:12:44 UTC 2022 aarch64 aarch64 aarch64 GNU/Linux - Architecture: aarch64
Description of problem
HVF's virtual counter diverges from CLOCK_VIRTUAL when the host sleeps and causes the inconsistency between Linux's system counter and everything else.
HVF's virtual counter apparently relies on something similar to mach_absolute_time, which stops when the host sleeps and resumes after it wakes up. However, CLOCK_VIRTUAL is implemented with mach_continuous_time, which continues even while the host sleeps. Linux uses the virtual counter as the source of the system counter and sees inconsistencies between the system counter and the other devices.
Steps to reproduce
- Launch Fedora.
- Compare the time shown at the top of the guest display and one at the top of the host display. The difference should be less than 2 minutes.
- Let the host sleep for 3 minutes.
- Compare the times again. The difference is now greater than 2 minutes.
Additional information
Here are solutions I've came up with so far. There are trade-offs but any of them should be better than the current situation. I'm happy to implement one if the maintainers have decided which one is the best or figure out a superior alternative.
- Implement
cpus_get_virtual_clockofAccelOpsClasswithmach_absolute_time. It would make HVF inconsistent with the other accelerators. Linux also expects the virtual clock is "continuous" and it leaves the divergence from the real time. - Request XNU
HOST_NOTIFY_CALENDAR_CHANGEto update the virtual clock with the continuous time. The interface is undocumented. - Use
IORegisterForSystemPowerto update the virtual clock with the continuous time. It is undocumented that the interface handles every cases wheremach_absolute_timeandmach_continuous_time, but it actually does if I read XNU's source code correctly.