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.

Reply via email to