Repository navigation
openvmm: start each PIT low/high count with its low byte - #1551
Merged
Pedro Henrique Penna (ppenna) merged 2 commits intoNov 12, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues remain for the documentation update to the OpenVMM pin and PIT fix mapping.
No open issues 🎉
What changed in this PR
This PR documents NVX’s OpenVMM pin for the PIT low/high-byte write fix addressing #1063 and #1146. The roadmap records the fix’s place in the commit chain and its suitability for independent upstream submission.
Changes:
- Updates the documented pin to
5d656d854, position 263, and adjusts chain counts and ranges. - Adds the PIT fix’s commit mapping and notes its conflict-free replay and passing checks on upstream main.
| File | Description |
|---|---|
| doc/openvmm-upstream-roadmap.md | Updates the pin, chain accounting, and upstream mapping for the PIT count-write fix. |
Review effort: Balanced
In its low-then-high access mode (RW = 3), OpenVMM's emulated 8254 PIT kept the low byte that it had buffered once the high byte completed a count, so after the first count it took every byte as a high byte with that stale low byte. A guest that wrote a count without a new control word so programmed a wrong one: control word 0x30, then the counts 0 and 16, left counter 0 counting 65536 (#1063), and control word 0x34, then the counts 0x1234 and 0x5678, left the count register at 0x5634 (#1146). Pin 5d656d854a8daca0f23833666b0b71229fe2ab73, the head of microsoft/openvmm#5061, one commit on c2c21ddb2, the head of feature/microvm/dev: - 5d656d854 chipset: start each PIT low/high count with its low byte Timer::write now takes the buffered low byte with the high byte, which returns the write flip-flop to the low byte of the next count. The saved state, which already holds that byte, keeps its format. Evidence: on Windows, the revision's three new chipset tests, two of which program counter 0 through the I/O ports as the issues do, fail without the fix, with a count register of 0x5634, with IRQ0 still low after 17 ticks of a count of 65536, and with a latched count of 0x5634, and pass at the pin, with the 101 other tests of chipset. The test attached to #1063 fails at c2c21ddb2 with the latched count 0xff9d of a count of 65536, and passes at the pin. NVX's guests disable the PIT at boot and never program it again, so they never wrote the second count that the defect corrupted. NVX needs no coordinated change: the control-session vectors, the source excerpts, the contract versions, and the CPU profiles that nvx-rs copies from OpenVMM match the revision's. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The NVX pin moves to 5d656d854, position 263, 132 commits past the mapped tip. Position 263, which microsoft/openvmm#5061 proposes for feature/microvm/dev on its head, c2c21ddb2, is unassigned. The Scope also says that microsoft/openvmm#5056 merged position 262. It makes the PIT's count writes in the low-then-high access mode take each count's low byte first, in Timer::write, code that upstream main has, and adds three tests, in a file that PR10's position 022 and position 139 also change, neither of them in the code that it changes. It changes nothing that a mapped PR adds. Replaying it on upstream main applies without conflicts, and chipset type-checks there with its tests, which pass, so it can go upstream on its own. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/issue-1063-pit-low-high-reload
branch
from
November 12, 2026 18:46
a159c4a to
86c885d
Compare
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Nov 12, 2026
Pedro Henrique Penna (ppenna)
deleted the
agents/issue-1063-pit-low-high-reload
branch
November 12, 2026 20:50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1063
Fixes #1146
What and why
In its low-then-high access mode (control word RW = 3), the 8254 PIT takes a count as two bytes, the low byte first, through a write flip-flop that a control word for the counter resets to the low byte, so a guest can write one count after another without a new control word, as Linux's PIT one-shot clock event device does for each event after its control word
0x38. OpenVMM's emulated PIT kept the low byte that it had buffered once the high byte completed a count, so after the first count it took every byte as a high byte with that stale low byte: control word0x30, then the counts 0 and 16, left counter 0 counting 65536 rather than 16 (#1063), and control word0x34, then the counts0x1234and0x5678, left the count register at0x5634(#1146). Such a guest programmed a wrong period or one-shot length, and IRQ0 at the wrong rate. #1146 duplicates #1063, so this closes both.Root cause, in OpenVMM: the
RwMode::LOW_HIGHarm ofTimer::write(vm/devices/chipset/src/pit/mod.rs) read the buffered low byte withif let Some(low) = self.state.cr_low, which left it set; onlyset_controlcleared it.The pin, microsoft/openvmm#5061 at
5d656d854a8daca0f23833666b0b71229fe2ab73, adds one commit toc2c21ddb2, the head offeature/microvm/dev(#1375's pin):5d656d854chipset: start each PIT low/high count with its low byte.self.state.cr_low.take(): the high byte takes the buffered low byte, which returns the flip-flop to the low byte of the next count. Three tests: one that writes two more counts without a control word after the first in each of the six modes, and two that program counter 0 through the I/O ports as chipset: PIT LSB/MSB access mode keeps the buffered LSB after the MSB write #1063 and openvmm: consecutive PIT low/high writes reuse the preceding count's low byte #1146 reproduce it, with VM time set tick by tick.The NVX commits:
ce76a5fadf9openvmm: pin the PIT low/high count fix. It changes only the gitlink, fromc2c21ddb2to5d656d854.86c885dd8a2doc: map the PIT low/high count fix in the OpenVMM roadmap. Position 263, unassigned.Decisions:
take(), and nothing else: the low-byte-only and high-byte-only access modes, the reset by a control word, the counter latch and read-back commands, and the read flip-flop (read_high) are unchanged.cr_low). A state that OpenVMM saved without this change after such a count holds the stale byte, which a restore keeps, since it cannot tell it from a low byte that the guest wrote; that guest's next counts then go wrong until its next control word for the counter, as they did before. NVX's guests disable the PIT at boot and never program it again (doc/design/3.2-clock-and-time.md, section 3.2.5), so they never wrote the second count that the defect corrupts, and nothing changes for them.Timer::writeacts only on a complete count, so the previous count keeps running until the high byte arrives. That predates this change, which does not alter what a first byte does, and is a separate behavior that no issue reports.Validation
Before and after the fix, on Windows, with Rust 1.97.1:
c2c21ddb2, with the new tests)5d656d854)low_high_access_takes_each_count_low_byte_first0x5634, expected0x5678(mode 0, the first checked)low_high_access_reloads_a_count_without_a_control_word, #1063's port sequence0xfff0of a count of 65536low_high_access_reprograms_a_running_rate_generator, #1146's port sequence0x5634, expected0x5678cargo test -p chipset --test pit_lsb_msb_reprogramand not committedOpenVMM package checks:
5d656d854, on Windows, forchipset:cargo check,cargo clippy --all-targets(clean apart from the environment's LNK4199 linker notices),cargo doc --no-deps, andcargo nextest run --profile agent(104 passed), thencargo xtask fmt --fix(clean).ba105dd7fwith this commit, the merged tree thatopenvmm_pin.py roadmapreports, in a temporary worktree:cargo check -p chipset --all-targetspassed, and the 14 PIT tests passed.NVX checks at this head, rebased onto
cc12bae1b84for #1537's test fixes (#1554):python scripts/openvmm_pin.py source 5d656d854a8daca0f23833666b0b71229fe2ab73 --pr 5061: good signature, and the revision is the pull request's head.python scripts/openvmm_pin.py check-branch --topic: passed (2 signed commits, no coordinated files).python scripts/openvmm_pin.py check-roadmap --worktree: passed (263 positions, 272 references).python scripts/nvx.py verify: passed.python scripts/local_gates.py --base origin/dev --only test-changes markdown-links test-layout openvmm-vectors openvmm-cpu-profiles --run: passed. That ischeck_test_changes.py(0 changes to existing tests),check_markdown_links.py,check_test_layout.py, and the ignoredopenvmm_vectors(4) andopenvmm_cpu_profiles(1) tests ofnvx-rs.nvx-cligate,python -m unittest discoveroverscripts/, which takes over two hours on this shared machine and whose code this change does not touch, is left to CI'sNVX CLI / LinuxandNVX CLI / Windowsjobs; and theopenvmm-cigate, which waits for OpenVMM's CI of the pin.Hosts: none. The PIT is OpenVMM's own on every backend, so the defect and the fix are backend-independent, and the crate-level tests reproduce them deterministically. No host or microVM run would show the changed behavior: NVX's guests never write a PIT count after boot, and a sandbox's unprivileged workload cannot reach the PIT's ports. Left to CI: the microVM tests on the KVM, MSHV, and WHP runners, and OpenVMM's unit tests on Linux and Windows and VMM tests on KVM, MSHV, and WHP hosts, which
openvmm-cirequires for the pin.CI at this head,
86c885dd8a2, in run 43283378192: passed, on its second attempt.NVX microVM tests / Linux / MSHV / Shard 1 of 2failed in the long-downtime restore scenario onazure-mshv-2. Across a simulated one-year downtime, the guest's frequency-corrected clocks,monotonicandboottime, gained about 186 s (5.9 ppm) more over realtime than the scenario expects from the guest's frequency correction, whilemonotonic_rawgained what it expects; that runner's TSC runs 9.81 ppm off the backend's declared rate. The same scenario passed onazure-mshv-1with this same change in run 43269651949, and the PIT plays no part in it, as NVX's guests disable the PIT at boot, so I reran the failed job once: attempt 2 passed, onazure-mshv-2again.OpenVMM CI of the pinned revision: passed. OpenVMM's required jobs passed for5d656d854in its run 43267707236, with no retried tests:unit tests [x64-linux],unit tests [x64-windows], andrun vmm-testsonx64-linux-amd-kvm(106 passed),x64-linux-intel-mshv(102),x64-windows-intel(275), andx64-windows-amd(274). Of the jobs that NVX does not require,run vmm-tests [x64-windows-amd-snp]failed; NVX does not use SEV-SNP. OpenVMM's results bot counts 29 failed Petri tests in the run, 28 of them unstable, none of them in the required jobs.NVX CLI / Linux,NVX CLI / Windows,nvx-rs / ubuntu-latest, andnvx-rs / windows-latest: passed.a159c4a0b0a, run 43269651949 passed every job butnvx-rs / ubuntu-latestandNVX CLI / Linux, which failed in CI: two tests on dev assume GNU coreutils, which ubuntu-latest (now 26.04) no longer has #1537's tests on the newubuntu-26.04image, which tests: keep the Linux tests true with Ubuntu 26.04's coreutils #1554 fixed ondev.No existing test changes, so no
tests-modifiedlabel.5d656d854a8daca0f23833666b0b71229fe2ab73.c2c21ddb2, the head offeature/microvm/devafter [DO NOT REVIEW] microVM: saturate the time ABI restore's logged phase durations openvmm#5056 (openvmm: time-ABI restore panics in a log line if the host clock steps back #1375). There are no other prerequisites.d4ee4cfd2; the checks of the fix above ran on exactly that revision.feature/microvm/devfromc2c21ddb2to exactly5d656d854a8daca0f23833666b0b71229fe2ab73. OpenVMM's merge button would squash the commit and leave the pin off the branch. This pull request then merges through the queue.