Skip to content

time ABI: publish the host's clock correction with each time sample - #1018

Open
Pedro Henrique Penna (ppenna) wants to merge 5 commits into
devfrom
agents/issue-1009-host-clock-correction
Open

Pedro Henrique Penna (ppenna) wants to merge 5 commits into
devfrom
agents/issue-1009-host-clock-correction

Conversation

@ppenna

@ppenna Pedro Henrique Penna (ppenna) commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1009. Pins microsoft/openvmm#4809 at 090fbab88f2cc2e069147c8dca122e343c9363ed, which moves every microVM to time ABI 2: OpenVMM pairs each time sample's host UTC with the guest TSC on the host and publishes with it the host's clock correction, which the guest now applies instead of estimating it over minutes. The guest, nvx.py doctor's H4, the architecture guide, and a new microVM scenario move with the pin.

Based on dev at 78c626dac. microsoft/openvmm#4809 is six commits directly on bcfc47cff, the head of feature/microvm/dev since microsoft/openvmm#4818 merged, and the pin is that pull request's rebased head commit itself.

Change

Commit Files Change
edcce2828 openvmm openvmm, nvx-rs/, guest/common/nvx-snapshot, wording in scripts/ bcfc47cff -> 090fbab88, and the guest of time ABI 2 and restore packet 5, which must move with it: the version-2 sample's decoder, the pairing against the guest's own TSC bracket, the frequency correction from the host's correction, the discipline that holds it, the restore repair's sample, the time state's new fields, nvx-time sample, and the template fixtures' manifests, regenerated at the pin
d0491ef80 doc doc/design/{1.2,3.1,3.2,5.5}, doc/build.md Specifies the version-2 sample, the guest's pairing and frequency, the discipline, and restore packet 5
7a8d13b75 doctor scripts/nvx_tools/doctor.py, scripts/test_time_abi.py, doc/ci.md, validate-runner H4 keeps the 250 ppm rate budget and requires a synchronized host clock correction, read as OpenVMM reads it; the direct correction is evidence
2e3f894dc test-microvm scripts/nvx_tools/microvm_tests.py, its guest script and tests, microvm_shards.json, doc/ci.md, doc/usage.md The time-host-correction scenario
4aeff73a8 doc doc/openvmm-upstream-roadmap.md Records the pin's commits as positions 368-373 and moves the NVX pin

OpenVMM's control-session protocol vectors and built-in CPU profiles are unchanged at the pin.

Time ABI 2

OpenVMM (microsoft/openvmm#4809). Writing 0xa7 to the portb status port latches a 48-byte version-2 sample: host UTC and the guest TSC at the same instant, paired on the host through the guest TSC's offset, which every backend measures before any VP runs (KVM's KVM_VCPU_TSC_OFFSET; VP 0's TSC between two host TSC reads on MSHV and WHP), with the pairing's bound; the status of host UTC; a disruption counter, which changes on a host suspension, a step of host UTC, or a change of the offset; and the host's correction, how fast host UTC runs against the guest TSC counted at the declared rate, with the bounds of its error and of host UTC's. OpenVMM reads the correction from the host OS, whatever time service runs there: adjtimex on Linux, and on Windows GetSystemTimeAdjustmentPrecise, whose increment is the performance counter's frequency and whose adjustment is the counter's ticks in each second of UTC, so that UTC runs at the increment over the adjustment of the counter's rate. After a restore the correction includes the restore's rate deviation. Restore packet 5 drops host UTC; CPUID leaf 0x40000002 reports time ABI 2.0, and snapshots record time ABI version 2.

Guest (nvx-rs). nvx-time reads its TSC around the selector write and its realtime clock between two more TSC reads, accepts a sample only if its TSC lies in that bracket, widened by the pairing bound and the 1 µs skew bound, and takes the offset from host UTC at the sample's TSC. It sets the kernel's frequency correction so that its realtime clock, which Linux counts at the TSC rate rounded down to the kHz, runs at host UTC's published rate, within Linux's ±500 ppm, and reports frequency_limited beyond it. Boot steps to host UTC and holds that frequency with STA_FREQHOLD, so that the kernel's PLL corrects only the phase; every poll holds the latest correction, and a changed disruption counter returns the discipline to its fast cadence. Boot, restore, and polls take up to three samples within 100 µs, where time ABI 1 accepted 1 ms at boot and restore. A restore's repair takes its sample in the packet's port transaction, so a clone runs at the destination host's correction from its repair on. The time state reports the host's status, correction and its bound, host UTC's maximum error, and the disruption counter.

The issue's next steps

  • Specification. 3.2 specifies the published values, their transport, and the discipline's use of them, and says that they are no paravirtual clock; 1.2 names VMClock among the excluded ones.
  • OpenVMM. [DO NOT REVIEW] microVM: publish the host's clock correction with each time sample openvmm#4809 reads the correction on Linux and Windows and publishes it with each sample.
  • nvx-rs. The discipline and the restore repair apply it.
  • doctor. H4 keeps dev's 250 ppm guest-budget gate, chronyd re-reads, warning, kernel-PLL fallback, and average-since-boot evidence, and additionally requires the host to publish a synchronized correction. The directly read status, correction, and maximum error are evidence; the independent rate gate still protects the guest's correction budget.
  • Tests. time-host-correction reads the host's own correction from an unhooked cold boot. OpenVMM's host-rate-offset-ppm hook then selects ordinary corrections below Linux's limit and a 900 ppm limit phase. The ordinary hooks solve OpenVMM's multiplicative rate composition and stay within its +/-10,000 ppm parser range and fail explicitly if no accepted hook can avoid saturation; the frequency allowance is derived from the reported TSC rate down to the ABI's 500 MHz minimum. It runs on KVM, MSHV, and WHP.

The open questions:

  • Transport. Version 2 of the 0xa7 sample, 48 bytes, rather than a page that the guest maps.
  • Refresh. OpenVMM reads the host's correction for every sample, and the guest holds the correction of each sample that it accepts.
  • Time ABI 1 snapshots. A time ABI 2 OpenVMM refuses them (E_MANIFEST_TIME); snapshots and templates must be recaptured.
  • Host sleep. A host suspension changes the disruption counter.

Compatibility

  • The guest and OpenVMM must both implement time ABI 2: a guest of either version fails the other's conformance check, so the guest artifacts, the release, and nvx-rs move together with the pin.
  • Snapshots and templates of time ABI 1 must be recaptured.
  • H4 fails a host whose clock no time service synchronizes, or that reports it unreliable. On the test VMs, chronyd synchronizes the Linux hosts' clocks and the Windows Time service the Windows host's; each CI job's Validate runner step reports its runner's.

Validation

This branch was rebuilt on dev four times while dev moved, each time carrying the same patches:

  • At 565d1ebe5, on 67f395e54, pinning OpenVMM b27be7e6a.
  • At cf7453276, on f8469b80a, pinning 93b2c4d91.
  • At 013c960c8, on d89d37090, pinning a417f946a.
  • At the rebased head 4aeff73a8, on 78c626dac, pinning 090fbab88. This rebuild preserves dev's newer H4 budget, chronyd, PLL, warning, and average-correction work; folds in the current review fixes; and carries the Windows correction fix below.

The Windows correction's sign. CI's run of 032774707 failed the second WHP shard on azure-windows-3, in time-host-correction's hooked cold boot. There, host UTC ran at +151.4 ppm against the guest TSC while OpenVMM published +248.7 ppm. Without the hook, OpenVMM published +48.7 ppm as the host's own correction while host UTC ran at -48.6 ppm.

  • Cause. OpenVMM read GetSystemTimeAdjustmentPrecise as the legacy GetSystemTimeAdjustment, which adds its adjustment once per increment. The precise API instead reports frequencies: the increment is the performance counter's, and Windows counts a second of UTC for every adjustment's worth of the counter's ticks. A larger adjustment therefore slows UTC.
  • Evidence. A 20 s comparison of UTC with QueryPerformanceCounter showed this on both Windows hosts measured:
    • On azure-windows-3, whose time service moved its adjustment from 10,000,501 to 10,000,588 for an increment of 10,000,000, UTC ran 53.1 ppm slow.
    • On the Windows test VM, at an adjustment of 10,000,005, UTC ran 0.447 ppm slow.
  • Fix. Commit 2 of [DO NOT REVIEW] microVM: publish the host's clock correction with each time sample openvmm#4809 and the doctor now take the increment over the adjustment, minus one, and their unit tests check that direction with these readings.
  • Consequence. The Windows runners' corrections in the H4 probe below have the opposite sign of those that this pull request first reported.

The validation:

  • OpenVMM (see [DO NOT REVIEW] microVM: publish the host's clock correction with each time sample openvmm#4809):
    • Before the Windows fix: clippy, rustdoc, nextest, and the time ABI hardware tests on Windows/WHP, Linux/KVM, and Linux/MSHV.
    • At 090fbab88: formatting; package-scoped check, clippy with -D warnings, rustdoc, and unit tests for every changed crate on Windows and Linux; 18 WHP, 12 KVM, and 13 MSHV ignored hardware tests; and the two affected KVM VMM tests all pass.
    • Its CI found petri still setting the portb device's removed utc_offset_ms, and a VMM test showed the ttrpc microVM test's guest reading restore packet 4. The pin fixes both, and both microVM VMM tests passed on Linux/KVM.
  • NVX on Windows:
    • At 4aeff73a8: nvx.py verify; the time ABI (75), microVM (305), and nvx_tools (566) suites; ruff, Linux and Windows pyright, Markdown links; and the OpenVMM vectors and CPU profiles (3 and 1).
    • At 990e4b425, before the final base-only refresh: shell formatting and linting; the nvx-rs feature matrix, rustdoc, MSRV, and runtime-adapter protocol, host, and Linux-musl guest clippy checks.
    • At the earlier d17c209d5, before a review corrected design doc 3.2's pairing equations: nvx.py verify; the time ABI, scenario, shard, and Markdown tests (185) and test_nvx_tools.py (566); ruff, pyright, and scripts/check_markdown_links.py; and the openvmm_vectors and openvmm_cpu_profiles comparisons, ignored tests included (3 and 1).
    • At cf7453276: nvx-rs and runtime adapter fmt and clippy, for the host and for x86_64-unknown-linux-musl.
  • NVX on Linux, on an Azure MSHV VM:
    • At 990e4b425: the full validate-nvx suite (1,503 tests), adversarial (84), host-connect (4), Specula (25), and Markdown links; every check-nvx-rs step, including guest binaries and the runtime adapter's protocol, host, and guest configurations.
    • At 990e4b425 with OpenVMM 090fbab88 and freshly built guest artifacts: time-host-correction and doctor --checks H3 H4 --ci-schedule pass on Linux/KVM and Linux/MSHV.
    • At 032774707 with the scenario's probe: the full validate-nvx Python suite (1,467 tests), plus the adversarial (84) and host-connect (4) tests.
    • At 032774707: the runtime adapter's VM scenarios (154), including dev's new clone and template scenarios.
    • At cf7453276, every step of check-nvx-rs:
      • fmt and clippy;
      • the tests with all, default, and no features;
      • the bundling check and rustdoc;
      • the MSRV (1.89) and macOS checks;
      • the guest binaries' checks;
      • the runtime adapter's protocol, host, and guest configurations.
  • Template fixtures: the fixtures regenerated at 75765b660 remain byte-identical at 090fbab88; the exact-pin tests pass.
  • End to end on three Azure test VMs, with OpenVMM, the guest artifacts (built with Docker), and nvx-rs built from the branch:
    • At d17c209d5 on Windows/WHP, with OpenVMM 75765b660:
      • time-host-correction ran twice. Its published corrections matched host UTC's measured rate against the guest TSC within 0.04 to 0.12 ppm; the old reading left gaps of 0.24 to 0.93 ppm, twice the host's correction.
      • nvx.py doctor --checks H3 H4 --ci-schedule passed. H4 read -0.600 ppm for an adjustment of 10,000,006, and host UTC measured -0.612 ppm against the counter.
    • At 032774707 with the scenario's probe: time-host-correction passed 3 times on each backend.
      • Duration: 13.4 to 13.6 s on Linux/KVM and Linux/MSHV, and 17.5 to 20 s on Windows/WHP.
      • The probe read the host's own correction as -17 ppm on KVM, -44 to -69 ppm on MSHV, and about 0 ppm on WHP.
      • The hooks then published about 200 and 150 ppm against that sign, negative on WHP.
      • The limit phase held 500000 ppb in the host correction's direction with frequency_limited=1.
    • At 565d1ebe5:
      • nvx.py doctor --checks H1 H2 H3 H4 H6 on the qualification schedule passed on Linux/KVM, Linux/MSHV, and Windows/WHP.
      • The default microVM suite passed on all three backends.
      • The runtime adapter's VM scenarios passed on Linux/KVM and Linux/MSHV.
    • At cf7453276 and ea808fa91 on Linux/MSHV:
      • time-host-correction, time-abi-conformance, snapshot-core, restore-downtime, smp-snapshot, and cpu-profile-fallback passed.
      • nvx.py doctor --checks H1 H2 H3 H4 --ci-schedule passed.
    • Throughout: the guest held the host's correction to the kernel's kHz rounding (0, 207, and 70 ppb on KVM, MSHV, and WHP), and its clock ran within 0.85 ppm of it.
  • Host UTC slews. While validating, host UTC ran up to 24 ppm away from its own correction over a 5 s window whenever the host's time service updated it in between; otherwise it follows adjtimex to 0.03 ppm.
    • A check of the guest's clock directly against host UTC failed 1 of 12 runs on such a slew.
    • The scenario therefore checks the guest's clock against the correction that it holds, through the samples' pairing of that clock with the guest TSC.
    • It checks host UTC against its published correction within 75 ppm.
  • All 20 CI runners report synchronized host clocks, so the new H4 gate passes on each of them. The Linux runners report neither STA_UNSYNC nor STA_CLOCKERR, the Windows ones an applied time adjustment, and their corrections range from -76 to +6.9 ppm.
  • CI:

Merge microsoft/openvmm#4809 into feature/microvm/dev, by fast-forwarding that branch to 090fbab88, before merging this pull request.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The checker can accept stale clock corrections, and the restore-limit phase can pass without exercising Linux’s frequency limit, leaving both scenarios insufficiently validated.

2 open issues
What changed in this PR

Moves NVX to time ABI 2 so guests apply the host’s published clock correction rather than estimating it. This coordinates guest boot and restore behavior with the OpenVMM pin and addresses the host-clock qualification problems described in #1009 and #1008.

Changes:

  • Updates time samples, restore packets, guest frequency control, and reported clock state.
  • Changes doctor H4 to check TSC stability and synchronized host correction without gating on correction size.
  • Adds a host-correction microVM scenario, regression coverage, and updated architecture documentation.
File Description
scripts/​test_time_abi.py Updates ABI diagnostics and host-clock qualification tests.
scripts/​test_nvx_tools.py Updates comments describing qualification and time ABI behavior.
scripts/​test_microvm_tests.py Adds host-correction scenario and report-checker tests.
scripts/​nvx_tools/​time_abi.py Updates harness descriptions and errors to ABI v2.
scripts/​nvx_tools/​microvm_tests.py Adds host-correction boot and restore validation.
scripts/​nvx_tools/​microvm_test_scripts/​time-host-correction.sh.in Reports guest state and paired time samples.
scripts/​nvx_tools/​microvm_test_scripts/​snapshot-core.sh Removes an outdated packet-version comment.
scripts/​nvx_tools/​microvm_shards.json Adds the scenario and rebalances test shards.
scripts/​nvx_tools/​doctor.py Reads host clock corrections and revises H4 qualification.
nvx-rs/​tests/​guest_tools.rs Updates packet, sample, and command-output tests.
nvx-rs/​tests/​fixtures/​template/​README.md Documents regenerated ABI v2 fixture manifests.
nvx-rs/​src/​host/​template/​test_support.rs Uses the current time ABI in manifest fixtures.
nvx-rs/​src/​host/​template/​manifest/​tests.rs Tests rejection of incompatible time ABI versions.
nvx-rs/​src/​host/​launch.rs Removes version-specific wording from a comment.
nvx-rs/​src/​guest/​time/​test_mode.rs Updates decoder and clock-arithmetic test hooks.
nvx-rs/​src/​guest/​time/​system.rs Pairs samples with guest clocks and applies host corrections.
nvx-rs/​src/​guest/​time/​state.rs Records host status, correction, bounds, and disruptions.
nvx-rs/​src/​guest/​time/​mod.rs Identifies the guest implementation as ABI v2.
nvx-rs/​src/​guest/​time/​daemon.rs Holds published frequency corrections and handles disruptions.
nvx-rs/​src/​guest/​time/​cpu_mask.rs Removes version-specific wording from a comment.
nvx-rs/​src/​guest/​time/​commands.rs Expands nvx-time sample output and pairing validation.
nvx-rs/​src/​guest/​time/​checks.rs Checks the ABI v2 CPUID identity.
nvx-rs/​src/​guest/​time/​api.rs Applies sampled host corrections during boot and restore.
nvx-rs/​src/​guest/​sys/​mod.rs Exposes the frequency-hold type internally.
nvx-rs/​src/​guest/​sys/​discipline.rs Holds frequency while allowing kernel phase correction.
nvx-rs/​src/​contract.rs Sets time ABI 2 and restore packet 5.
nvx-rs/​README.md Updates guest API and tool descriptions to ABI v2.
guest/​common/​nvx-snapshot Updates comments for packet v5 and sampled restore time.
doc/​usage.md Describes the host-correction scenario.
doc/​openvmm-upstream-roadmap.md Records the new pin and its six commits.
doc/​design/​5.5-restore-protocol.md Specifies packet v5 and sample-based clock repair.
doc/​design/​3.2-clock-and-time.md Specifies ABI v2 samples, corrections, and discipline.
doc/​design/​3.1-platform-control-device.md Describes host-paired time samples.
doc/​design/​1.2-non-goals.md Clarifies the distinction from paravirtual clocks.
doc/​ci.md Documents revised H4 checks and scenario expectations.
doc/​build.md Updates guest-tool and boot-time behavior descriptions.
.github/​actions/​validate-runner/​action.yml Updates comments describing host qualification.

Review effort: Balanced

Comment thread scripts/nvx_tools/microvm_tests.py Outdated
Comment thread scripts/nvx_tools/microvm_tests.py
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-1009-host-clock-correction branch from ea808fa to 013c960 Compare October 29, 2026 20:45
Copilot AI balanced review requested due to automatic review settings October 29, 2026 20:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Fixed boot and restore offsets can push host corrections beyond Linux’s ±500 ppm limit, causing the non-limit checks to reject guests that correctly clamp their corrections.

1 open issue
2 resolved since last review

Review effort: Balanced

Comment thread scripts/nvx_tools/microvm_tests.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The ABI equations mix TSC-derived seconds with nanosecond timestamps, requiring a seconds-to-nanoseconds conversion to avoid incorrect extrapolation and uncertainty calculations.

2 open issues

Review effort: Balanced

Comment thread doc/design/3.2-clock-and-time.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The coordinated guest time-ABI and snapshot-format changes require maintainer confirmation of exact-pin compatibility, artifact deployment, and pending cross-platform validation across KVM, MSHV, and WHP.

1 open issue
1 resolved since last review

Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-1009-host-clock-correction branch from d17c209 to a91e5e5 Compare October 29, 2026 22:11
Copilot AI balanced review requested due to automatic review settings October 29, 2026 22:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The breaking time-ABI and restore-protocol migration needs maintainer review and exact-pin KVM/MSHV/WHP validation, with the coupled OpenVMM PR still open and its full CI unfinished.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The default time-correction scenario generates offsets outside OpenVMM’s accepted range, and the rounding allowance can reject valid sub-GHz guests.

2 open issues

Review effort: Max

Comment thread scripts/nvx_tools/microvm_tests.py Outdated
Comment thread scripts/nvx_tools/microvm_tests.py Outdated
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-1009-host-clock-correction branch from a91e5e5 to 990e4b4 Compare October 30, 2026 07:49
Copilot AI max review requested due to automatic review settings October 30, 2026 07:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

H4 still rejects stable, synchronized hosts when H3 supplies native_tsc_hz and correction-derived rate deviation exceeds 250 ppm, preserving the qualification failure the change intends to remove.

1 open issue
2 resolved since last review

Review effort: Max

Comment thread scripts/nvx_tools/doctor.py
Pin microsoft/openvmm#4809 at 090fbab88f2cc2e069147c8dca122e343c9363ed,
six commits on bcfc47cff6606afadffef97551e538962895b598, the previous pin
and the head of feature/microvm/dev.

Under time ABI 1 the guest paired host UTC with its own clock: nvx-time
read CLOCK_REALTIME around the port write that latches a time sample and
accepted the pairing only within 1 ms at boot and restore, or 50 us for
its discipline, so a port write that a loaded host served late cost a
sample, and three cost a boot or a restore. It then estimated how fast
host UTC runs against its TSC by steering the kernel's PLL over minutes,
although the host's time service already knows that rate.

Time ABI 2 has OpenVMM pair each sample's host UTC with the guest TSC on
the host and publish with it the host's clock correction, which the host
OS reports (adjtimex on Linux, GetSystemTimeAdjustmentPrecise on
Windows), the bound of the correction's error, the status and maximum
error of host UTC, and a disruption counter, which changes on a host
suspension, a step of host UTC, or a change of the guest TSC's offset.
The version-2 sample is 48 bytes. CPUID leaf 0x40000002 reports time ABI
2.0 (EBX 0x00020000). Restore packet 5 drops host UTC, so its header
shrinks to 24 bytes, and the guest's repair takes a time sample instead.

The guest moves with the pin, since a guest of one time ABI fails the
other's conformance check:

- nvx::contract: TIME_ABI_VERSION 2 and RESTORE_PACKET_VERSION 5.
- Pairing: the guest reads its TSC before and after the selector write,
  and CLOCK_REALTIME between those reads, and accepts a sample only if
  its TSC lies within that bracket, widened by the published pairing
  bound and 1 us of skew. It compares host UTC with CLOCK_REALTIME at
  the sample's TSC, moving its CLOCK_REALTIME read there at the rate at
  which Linux counts it; the offset's uncertainty is the pairing bound,
  half the bracket, and 2,000 ppm of that move.
- Rate: from the host's correction and the TSC rate, which the guest
  reads once from the Hyper-V TSC frequency MSR (0x40000022) and keeps
  in its time state, the guest computes the adjtimex frequency that
  makes CLOCK_REALTIME, which Linux counts at its kHz rounding of the
  TSC rate, run at host UTC's rate. It clamps that frequency to Linux's
  500 ppm limit and reports whether it did.
- Discipline: boot steps the clock to host UTC and holds that frequency
  with STA_FREQHOLD, so that the kernel's PLL corrects only the phase.
  Every poll holds the latest correction and slews the measured offset,
  or steps it from 128 ms on. A changed disruption counter returns the
  daemon to its fast cadence. Boot, restore, and polls take up to three
  samples within 100 us; a boot that gets none fails its check, and a
  restore fails its repair. The maximum and estimated errors that the
  guest sets include host UTC's maximum error only when the host
  reports a synchronized clock; the guest never marks its clock
  unsynchronized on the host's behalf.
- Restore: the repair reads packet 5 and takes its time sample in the
  same port transaction, then steps the clock and holds the frequency.
- State: the time state records the host's status, correction, and
  correction bound, host UTC's maximum error, the disruption counter,
  and whether the frequency was limited. nvx-time sample prints a
  sample's fields and the guest's pairing, and the test hooks packet,
  sample, pair, frequency, and correction take the new fields.

Templates record time ABI version 2, so the manifest reader refuses a
template of time ABI 1, which must be recaptured. The fixtures'
manifests, which OpenVMM's generator regenerated at the pin, differ from
the previous ones only in that version. The template-key vectors keep
the contract versions that their test fixes. Comments, the nvx-rs
README, and nvx-snapshot name the new versions.

OpenVMM's protocol vectors and CPU profiles are unchanged at the pin.

Part of #1009

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The architecture guide described time ABI 1: a 16-byte time sample that
the guest pairs with its own clock within 1 ms, or 50 us for its
discipline, a guest that estimates host UTC's rate itself, and restore
packet 4, which carries host UTC latched at its first selection.
Describe time ABI 2, which the previous commit pins.

3.2, Clock and time:

- Table 3.5: CPUID leaf 0x40000002 reports time ABI 2.0 in EBX.
- 3.2.4: the host clock correction of every time sample includes a
  restore's rate deviation, so that the guest compensates it from its
  repair on.
- 3.2.6, Backend obligations: every backend knows the guest TSC's
  offset from the host TSC before any processor runs, at cold boot and
  at every restore, so that the VMM pairs each sample's host UTC with
  the guest TSC.
- 3.2.9, Time samples, rewritten: Figure 3.1 lays out the 48-byte
  version-2 sample field by field; the text gives the guest's pairing,
  the bracket check and the uncertainty of the offset, how it computes
  the adjtimex frequency that makes CLOCK_REALTIME run at host UTC's
  rate from the correction and the TSC rate, and the 500 ppm limit that
  it reports. Table 3.10 bounds every use, boot, restore, and the
  discipline, at 100 us and three attempts. The samples are no
  paravirtual clock: they feed only the wall-clock discipline, and the
  TSC stays the guest's clocksource.
- 3.2.10, Guest obligations: the time state's new fields, a boot that
  takes a sample within the bound before readiness, steps to host UTC,
  and holds the frequency that the host's correction gives, and a
  discipline that holds that frequency at every accepted sample, so
  that the kernel's PLL only slews the phase, and returns to its fast
  cadence when the disruption counter changes.

5.5, Restore protocol: Figure 5.7 lays out restore packet 5, whose
header is 24 bytes and carries no UTC, 88 bytes without memory ranges,
and flag 3 also covers the host clock rate's test hook. The guest's
repair steps take a time sample in the packet's port transaction and
set the clock and its rate from it.

3.1, Platform control device, says that time samples pair host UTC with
the guest TSC and carry the host's correction, and that the guest checks
the pairing against TSC reads around the port write. 1.2, Non-goals,
names kvmclock, Hyper-V's reference TSC page, and VMClock among the
excluded paravirtual clocks, and says what the time samples feed
instead. The build guide's description of nvx-time names restore packet
5 and the version-2 sample parsers and the correction that the
discipline applies.

Part of #1009

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Since this branch split from dev, H4's rate qualification gained the
250 ppm budget that time ABI 2 leaves for the host's clock correction,
a retry when chronyd's skew straddles that limit, a warning above the
old 100 ppm bound, the kernel PLL's estimate when chronyd is absent, and
the average correction since boot as evidence (#1008). Keep all of
those checks and their evidence.

Time ABI 2 also changes what H4 must learn directly from the host. Every
time sample publishes the host's clock correction, which the guest
holds, so a qualified host must have a time service maintaining that
correction. H4 now additionally:

- Fails unless the host publishes a synchronized correction, read as
  OpenVMM reads it: on Linux, adjtimex reports neither STA_UNSYNC nor
  STA_CLOCKERR; on Windows, GetSystemTimeAdjustmentPrecise reports an
  applied adjustment.
- Reports the status, correction, and host UTC's maximum error in its
  detail and as host_clock_status and host_correction_ppm summary facts.
  The direct correction is evidence; the independent 250 ppm rate gate
  continues to protect the guest's correction budget.
- Keeps the chronyd estimate and its skew, conclusive re-reads, the
  kernel PLL fallback, the warning, and the average since boot, instead
  of replacing them with the instantaneous correction.

The Linux reader shares the typed struct timex reader with the PLL and
H7. The Windows reader uses the precise API's direction: the performance
counter ticks increment times per second, and host UTC counts a second
for every adjustment ticks, so a larger adjustment slows UTC.

The H4 tests cover synchronized, free-running, unreliable, and unknown
host clocks; Linux frequency, tick, and maximum-error mapping; Windows'
correction direction; the summary facts; and the interaction with the
rate budget, warnings, chronyd re-reads, PLL fallback, and boot average.
The CI guide, validate-runner, and its test describe the combined gate.

Part of #1009
Refs #1008

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-1009-host-clock-correction branch from 990e4b4 to 7603877 Compare October 30, 2026 14:39
Copilot AI max review requested due to automatic review settings October 30, 2026 14:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The hook-rate calculation adds rates instead of composing them multiplicatively, allowing saturating phases and rejecting valid ones, with the same error reflected in the tests.

1 open issue
1 resolved since last review

Review effort: Max

Comment thread scripts/nvx_tools/microvm_tests.py Outdated
Time ABI 2 publishes the host's clock correction with every sample, and
the guest holds it as its frequency correction. Add the
`time-host-correction` microVM scenario to check that behavior when host
UTC runs at a deliberately different rate from the guest TSC.

An unhooked cold boot first reads the host's own correction. OpenVMM's
`host-rate-offset-ppm` hook then drives:

- a hooked cold boot whose published correction stays below Linux's
  500 ppm frequency limit;
- a restore at another correction below the limit; and
- a restore in the host correction's direction that exceeds the limit,
  where the guest must hold +/-500 ppm and report frequency_limited=1.

The ordinary hooks solve OpenVMM's multiplicative composition of the
host and hook rates for targets of 200 and 150 ppm against the host
correction's sign. They are clamped to OpenVMM's accepted +/-10,000 ppm
hook range; at its edge they use the closest composed correction below
Linux's limit, and a host correction that cannot be brought below that
limit fails before OpenVMM starts with a diagnostic naming both bounds.
The limit hook is 900 ppm in the host correction's direction.

Each phase requires the test-hook flag and checks that the boot step or
restore repair did not retain a stale correction. Two samples pair host
UTC and the guest's realtime clock with the guest TSC, so the checker
verifies that both clocks run at the published correction, within their
pairing uncertainty and the allowance for host slews. The limit phase
requires the saturated frequency and flag explicitly.

Linux counts the declared TSC rate rounded down to the kHz. The checker
derives the permitted difference between the host and kernel frequency
corrections from each report's tsc_hz and the worst 999 Hz loss, instead
of assuming a rate of at least 1 GHz. The regression at 500,000,999 Hz
accepts the valid 1,998 ppb difference; the hook tests cover both
+/-10,000 ppm boundaries and reject corrections that cannot be offset.

The shard plan runs the scenario on KVM, MSHV, and WHP. Unit tests cover
the successful phases, stale and ignored corrections, the clock-rate
checks, host slews and changing corrections, saturation, missing or
unhooked samples, the full 500 MHz ABI range, OpenVMM's hook range, and
the probe, boot, and restore command sequence.

Part of #1009

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Record the six commits that the first commit of this series pins as
positions 368-373 of the microVM chain, and move the NVX pin to
090fbab88.

Positions 368-373 move NVX's time ABI to version 2: the shared host TSC
bracket, the host OS's discipline of host UTC, the publication of host
UTC paired with the guest TSC and its correction, the guest TSC's
offset on every backend, the version-2 time sample and restore packet 5,
and the Guide's description. They change the time-ABI backends, portb
device, restore packet, and VM worker that positions 159-269 added.
Position 372 also adapts petri's microVM configuration from PR15 and the
ttrpc microVM test from PR25.

The six commits are directly on bcfc47cff, position 367 and the current
head of feature/microvm/dev after microsoft/openvmm#4818 merged
positions 366-367. Update the scope, physical DAG, after-tip count and
table, and remarks. The pin is 216 commits past the mapped tip.

Part of #1009

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-1009-host-clock-correction branch from 7603877 to 4aeff73 Compare October 30, 2026 15:04
Copilot AI max review requested due to automatic review settings October 30, 2026 15:04

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

time ABI: publish the host's clock correction with each time sample instead of having guests estimate it

3 participants