Bug #14668
openbhyve could better emulate pmtimer rollover
0%
Description
In the PIIX4 specification, the PM timer is stated to have certain behavior when its value rolls over:
When bit 23 of the timer transitions from high-to-low or low-to-high, the
TMROF_STS
bit is set. If theTMROF_EN
bit is set anSCI
interrupt is also generated.
The vpmtmr emulation in bhyve lacks any of that, acting simply as a 24-bit wide read-only timer. It would probably be useful to provide some sort of interface so the host software could properly emulate that roll-over behavior (in c-bhyve and/or propolis).
Updated by Greg Colombo about 1 year ago
One result of this is that when you run a Windows guest that decides to use the PM timer, its performance counter APIs like Win32's QueryPerformanceCounter will go backwards every few seconds. This isn't generally fatal to the system (in that it doesn't immediately BSOD), but user programs that rely on QPC increasing over time will be unhappy; notably, DWM (the desktop compositor) has a timing thread that will fastfail if it sees QPC moving backwards, so RDP/graphical console sessions will be unstable and difficult to use. Switching Windows to use a different time source like the HPET stabilizes these users.
Here's a small Rust program that exhibits this. In Cargo.toml add a dependency on the windows crate:
[dependencies.windows]
version = "0.36.1"
features = ["Win32_System_Performance", "Win32_Foundation"]
Then, in main.rs:
use windows::{
Win32::System::Performance::QueryPerformanceCounter,
Win32::System::Performance::QueryPerformanceFrequency,
};
fn main() {
let mut qpc: i64 = 0;
let mut qpc_freq: i64 = 0;
unsafe {
QueryPerformanceFrequency(&mut qpc_freq).unwrap();
}
println!("QPC frequency: {} / 0x{:x}", qpc_freq, qpc_freq);
let mut sample: usize = 1;
loop {
unsafe {
QueryPerformanceCounter(&mut qpc).unwrap();
}
println!("Sample {:>16} | {:#016x}", sample, qpc);
sample += 1;
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
Building this against the x86_64-pc-windows-msvc target and running it in a Windows guest will show that bits 0-23 frequently roll over, but that bits 24 and up don't increase at the frequency you would expect, as in this sample output:
QPC frequency: 3579545 / 0x369e99
Sample 1 | 0x0000000e4fc53f
Sample 2 | 0x0000000d8758a6
Sample 3 | 0x0000000dbed1fa
Sample 4 | 0x0000000df64c58
Sample 5 | 0x0000000e2dc547
Sample 6 | 0x0000000e6539f4
Sample 7 | 0x0000000d9d921c
Sample 8 | 0x0000000dd50f1c
Sample 9 | 0x0000000e0c8990
Sample 10 | 0x0000000e43fff6
Sample 11 | 0x0000000e7b7a93
Sample 12 | 0x0000000db3cb6a <-- rollover
Sample 13 | 0x0000000deb41c1
Sample 14 | 0x0000000e22bad4
Sample 15 | 0x0000000e5a3579
Sample 16 | 0x0000001d8356db <- rollover, but incremented at bit 28
Sample 17 | 0x0000001dbda283
Sample 18 | 0x0000001df519d9
Sample 19 | 0x0000001e2c9731
Sample 20 | 0x0000001e640eb7
Sample 21 | 0x0000001d9c6776 <- rollover, etc.
Sample 22 | 0x0000001dd3df8a
Sample 23 | 0x0000001e144438
Sample 24 | 0x0000001e4b58f0
Sample 25 | 0x0000001d83a975
Sample 26 | 0x0000001dc15afb
Sample 27 | 0x0000001df89434
Sample 28 | 0x0000001e300df4
Sample 29 | 0x0000001e678891
Sample 30 | 0x0000001d9fe245
Sample 31 | 0x0000001dd75767
Sample 32 | 0x0000001e0ed20b
Sample 33 | 0x0000001e464877
Sample 34 | 0x0000001e7dc48a