On Mon, Oct 5, 2026 at 12:26 PM Mushahid Hussain <[email protected]> wrote:
>
> A nested Hyper-V that sees the frequency MSRs lets its parent own
> the partition reference time. After a resume from hibernation it
> issues HvCallRestorePartitionTime (0x103) with the TSC and the
> reference counter it saved, and expects both clocks to continue
> from there. KVM rejected the hypercall. The guest time base stayed
> inconsistent and the root partition did not run.
>
> Restore both clocks in one pvclock update, so no vCPU runs with the
> new TSC and the old reference time. Each vCPU computes and writes its
> own TSC offset from the recorded step, so no other vCPU writes its
> TSC state. kvm_synchronize_tsc() writes the offset of the calling
> vCPU only. Mark the TSC page as changed by the guest, so it is
> recomputed even when TSC emulation control is set.
>
> The TLFS marks the hypercall, its input and CPUID 0x40000004 EAX
> bit 20 (RestoreTimeOnResume) as reserved. Microsoft's hvdef crate
> defines them (hypercall::RestorePartitionTime), and Microsoft's
> OHCL kernel carries the same C definitions in hvgdk_mini.h.
>
> Advertise bit 20 and gate the hypercall on it under
> KVM_CAP_HYPERV_ENFORCE_CPUID. Hyper-V offers hibernation to its root
> partition only when the parent sets this bit.
>
> Assisted-by: Claude:claude-fable-5.1
> Signed-off-by: Mushahid Hussain <[email protected]>
> ---
Aside from what Sashiko and David have already said...
> +static u64 kvm_hv_restore_partition_time(struct kvm_vcpu *vcpu,
> + struct kvm_hv_hcall *hc)
> +{
> + struct kvm_hv *hv = to_kvm_hv(vcpu->kvm);
> + struct hv_input_restore_partition_time in;
> +
> + BUILD_BUG_ON(sizeof(in) != 32);
> +
> + if (kvm_vcpu_read_guest(vcpu, hc->ingpa, &in, sizeof(in)))
> + return HV_STATUS_INVALID_HYPERCALL_INPUT;
Nit: some validation checks are missing here. Per the TLFS, hypercall
input parameters must be 8-byte aligned and must not cross a page
boundary (cf. kvm_hvcall_signal_event()).
> + if (in.reserved)
> + return HV_STATUS_INVALID_HYPERCALL_INPUT;
> + if (in.partition_id != HV_PARTITION_ID_SELF)
> + return HV_STATUS_INVALID_PARTITION_ID;
> +
> + /* The guest asked for the step; recompute the TSC page as for its
> own write. */
> + mutex_lock(&hv->hv_lock);
> + if (hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE)
> + hv->hv_tsc_page_status = HV_TSC_PAGE_GUEST_CHANGED;
> + mutex_unlock(&hv->hv_lock);
I think there's a window here in which another vCPU can consume
HV_TSC_PAGE_GUEST_CHANGED before kvm_set_clock_and_tsc() steps the
clocks. If TSC emulation control is enabled,
kvm_hv_request_tsc_page_update() won't invalidate the resulting stale
TSC page.
> + kvm_set_clock_and_tsc(vcpu->kvm, in.reference_time_in_100ns * 100,
> in.tsc);
When stepping the reference time forward, don't we have to recalculate
the hrtimer deadlines associated with armed synthetic timers?
Otherwise, those hrtimers will fire late.