On Fri, Jul 10, 2026 at 12:15:25PM +0200, Magnus Kulke wrote: > This is a partition-wide state for which we use a dedicated hw clock > facility, similar to KVM. We have to freeze the time for a partition > before we are allowed to set it. We register a state change handler for > the clock device and a post-load handler for migration state. In the > post-load handler we toggle a flag that will set the reference time > state on next state to "running" on the partition. > > We can move the time freeze and reference-time ioctls/hvcalls to the > clock module. > > Signed-off-by: Magnus Kulke <[email protected]> > --- > MAINTAINERS | 1 + > accel/mshv/mshv-all.c | 67 ++------------ > accel/mshv/trace-events | 1 + > hw/i386/meson.build | 1 + > hw/i386/mshv/clock.c | 189 +++++++++++++++++++++++++++++++++++++++ > hw/i386/mshv/meson.build | 4 + > include/system/mshv.h | 3 + > 7 files changed, 208 insertions(+), 58 deletions(-) > create mode 100644 hw/i386/mshv/clock.c > create mode 100644 hw/i386/mshv/meson.build > > diff --git a/MAINTAINERS b/MAINTAINERS > index 6171cc7494..269876db3e 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -613,6 +613,7 @@ R: Wei Liu <[email protected]> > R: Doru Blânzeanu <[email protected]> > S: Supported > F: target/i386/mshv/ > +F: hw/i386/mshv/ > > X86 Instruction Emulator > M: Roman Bolshakov <[email protected]> > diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c > index b34fb34d05..7b3c13cf1e 100644 > --- a/accel/mshv/mshv-all.c > +++ b/accel/mshv/mshv-all.c > @@ -60,54 +60,6 @@ static int init_mshv(int *mshv_fd) > return 0; > } > > -/* freeze 1 to pause, 0 to resume */ > -static int set_time_freeze(int vm_fd, int freeze) > -{ > - int ret; > - struct hv_input_set_partition_property in = {0}; > - in.property_code = HV_PARTITION_PROPERTY_TIME_FREEZE; > - in.property_value = freeze; > - > - struct mshv_root_hvcall args = {0}; > - args.code = HVCALL_SET_PARTITION_PROPERTY; > - args.in_sz = sizeof(in); > - args.in_ptr = (uint64_t)∈ > - > - ret = mshv_hvcall(vm_fd, &args); > - if (ret < 0) { > - error_report("Failed to set time freeze"); > - return -1; > - } > - > - return 0; > -} > - > -static int pause_vm(int vm_fd) > -{ > - int ret; > - > - ret = set_time_freeze(vm_fd, 1); > - if (ret < 0) { > - error_report("Failed to pause partition: %s", strerror(errno)); > - return -1; > - } > - > - return 0; > -} > - > -static int resume_vm(int vm_fd) > -{ > - int ret; > - > - ret = set_time_freeze(vm_fd, 0); > - if (ret < 0) { > - error_report("Failed to resume partition: %s", strerror(errno)); > - return -1; > - } > - > - return 0; > -} > - > static int get_host_partition_property(int mshv_fd, uint32_t property_code, > uint64_t *value) > { > @@ -330,9 +282,6 @@ static int create_vm(int mshv_fd, int *vm_fd) > return -1; > } > > - /* Always create a frozen partition */ > - pause_vm(*vm_fd); > - > return 0; > } > > @@ -578,13 +527,6 @@ static int mshv_init(AccelState *as, MachineState *ms) > return -1; > } > > - ret = resume_vm(vm_fd); > - if (ret < 0) { > - close(mshv_fd); > - close(vm_fd); > - return -1; > - } > - > s->vm = vm_fd; > s->fd = mshv_fd; > > @@ -606,6 +548,8 @@ static int mshv_init(AccelState *as, MachineState *ms) > > register_savevm_live("mshv", 0, 1, &savevm_mshv, s); > > + mshv_clock_init(); > + > return 0; > } > > @@ -643,6 +587,13 @@ static int mshv_cpu_exec(CPUState *cpu) > cpu->vcpu_dirty = false; > } > > + /* Corresponding store-release is in cpu_exit. */ > + if (qatomic_load_acquire(&cpu->exit_request)) { > + trace_mshv_interrupt_exit_request(cpu->cpu_index); > + ret = EXCP_INTERRUPT; > + break; > + } > + > ret = mshv_run_vcpu(mshv_state->vm, cpu, &mshv_msg, &exit_reason); > if (ret < 0) { > error_report("Failed to run on vcpu %d", cpu->cpu_index); > diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events > index a4dffeb24a..859e8bfb0f 100644 > --- a/accel/mshv/trace-events > +++ b/accel/mshv/trace-events > @@ -4,6 +4,7 @@ > # SPDX-License-Identifier: GPL-2.0-or-later > > mshv_start_vcpu_thread(const char* thread, uint32_t cpu) "thread=%s > cpu_index=%d" > +mshv_interrupt_exit_request(uint32_t cpu) "cpu_index=%d" > > mshv_set_memory(bool add, uint64_t gpa, uint64_t size, uint64_t user_addr, > bool readonly, int ret) "add=%d gpa=0x%" PRIx64 " size=0x%" PRIx64 " > user=0x%" PRIx64 " readonly=%d result=%d" > mshv_mem_ioeventfd_add(uint64_t addr, uint32_t size, uint32_t data) > "addr=0x%" PRIx64 " size=%d data=0x%x" > diff --git a/hw/i386/meson.build b/hw/i386/meson.build > index b611fbb5a7..f4d3122863 100644 > --- a/hw/i386/meson.build > +++ b/hw/i386/meson.build > @@ -39,6 +39,7 @@ i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c', > 'tdvf-hob.c')) > > subdir('kvm') > subdir('xen') > +subdir('mshv') > > i386_ss.add_all(xenpv_ss) > > diff --git a/hw/i386/mshv/clock.c b/hw/i386/mshv/clock.c > new file mode 100644 > index 0000000000..02c586503a > --- /dev/null > +++ b/hw/i386/mshv/clock.c > @@ -0,0 +1,189 @@ > +/* > + * MSHV partition reference clock > + * > + * Copyright Microsoft, Corp. 2026 > + * > + * Authors: Magnus Kulke <[email protected]> > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#include "qemu/osdep.h" > +#include "qemu/error-report.h" > +#include "migration/vmstate.h" > +#include "system/runstate.h" > +#include "hw/hyperv/hvhdk.h" > +#include "hw/hyperv/hvhdk_mini.h" > +#include "hw/hyperv/hvgdk.h" > +#include "hw/hyperv/hvgdk_mini.h" > +#include "linux/mshv.h" > +#include "system/mshv.h" > +#include "system/mshv_int.h" > + > +/* > + * Partition reference clock (HV_PARTITION_PROPERTY_REFERENCE_TIME), captured > + * when the VM is stopped and re-applied when it resumes. > + * > + * Mirrors hw/i386/kvm/clock.c. > + */ > +typedef struct MshvClockState { > + uint64_t ref_time; > + bool ref_time_pending; > +} MshvClockState; > + > +static MshvClockState mshv_clock; > + > +static int mshv_get_reference_time(int vm_fd, uint64_t *ref_time) > +{ > + struct hv_input_get_partition_property in = { 0 }; > + struct hv_output_get_partition_property out = { 0 }; > + struct mshv_root_hvcall args = { 0 }; > + int ret; > + > + in.property_code = HV_PARTITION_PROPERTY_REFERENCE_TIME; > + > + args.code = HVCALL_GET_PARTITION_PROPERTY; > + args.in_sz = sizeof(in); > + args.in_ptr = (uint64_t)∈ > + args.out_sz = sizeof(out); > + args.out_ptr = (uint64_t)&out; > + > + ret = mshv_hvcall(vm_fd, &args); > + if (ret < 0) { > + error_report("Failed to get reference time"); > + return -1; > + } > + > + *ref_time = out.property_value; > + return 0; > +} > + > +static int mshv_set_reference_time(int vm_fd, uint64_t ref_time) > +{ > + struct hv_input_set_partition_property in = { 0 }; > + struct mshv_root_hvcall args = { 0 }; > + int ret; > + > + in.property_code = HV_PARTITION_PROPERTY_REFERENCE_TIME; > + in.property_value = ref_time; > + > + args.code = HVCALL_SET_PARTITION_PROPERTY; > + args.in_sz = sizeof(in); > + args.in_ptr = (uint64_t)∈ > + > + ret = mshv_hvcall(vm_fd, &args); > + if (ret < 0) { > + error_report("Failed to set reference time"); > + return -1; > + } > + > + return 0; > +} > + > +/* > + * Freeze (freeze=1) or unfreeze (freeze=0) time for a partition. This will > not > + * pause/eject vCPU execution. It is assumed that the caller already has > stopped > + * the partition's vCPUs. > + * > + * NB: a partition's reference clock can only be written while the time is > + * frozen. > + */ > +static int mshv_set_time_freeze(int vm_fd, int freeze) > +{ > + struct hv_input_set_partition_property in = { 0 }; > + struct mshv_root_hvcall args = { 0 }; > + int ret; > + > + in.property_code = HV_PARTITION_PROPERTY_TIME_FREEZE; > + in.property_value = freeze; > + > + args.code = HVCALL_SET_PARTITION_PROPERTY; > + args.in_sz = sizeof(in); > + args.in_ptr = (uint64_t)∈ > + > + ret = mshv_hvcall(vm_fd, &args); > + if (ret < 0) { > + error_report("Failed to set time freeze"); > + return -1; > + } > + > + return 0; > +} > + > +static void mshv_clock_vm_state_change(void *opaque, bool running, > + RunState state) > +{ > + MshvClockState *s = opaque; > + int vm_fd = mshv_state->vm; > + int ret; > + > + if (running) { > + /* Skip if we have nothing to restore, e.g. on initial boot. */ > + if (!s->ref_time_pending) { > + return; > + } > + > + ret = mshv_set_time_freeze(vm_fd, 1); > + if (ret < 0) { > + error_report("Failed to freeze partition time on resume"); > + abort(); > + } > + > + /* INVARIANT: reference time can only be written if time is frozen. > */ > + ret = mshv_set_reference_time(vm_fd, s->ref_time); > + if (ret < 0) { > + error_report("Failed to restore reference time on resume"); > + abort(); > + } > + > + if (mshv_set_time_freeze(vm_fd, 0) < 0) { > + error_report("Failed to unfreeze partition time on resume"); > + abort(); > + } > + > + s->ref_time_pending = false; > + } else { > + /* Skip if we already have a to-be-set ref time */ > + if (s->ref_time_pending) { > + return; > + } > + > + ret = mshv_get_reference_time(vm_fd, &s->ref_time); > + if (ret < 0) { > + error_report("Failed to capture reference time on stop"); > + abort(); > + } > + > + s->ref_time_pending = true; > + } > +} > + > +/* > + * The incoming reference time should be applied on the next resume before > + * vCPUs start executing. > + */ > +static int mshv_clock_post_load(void *opaque, int version_id) > +{ > + MshvClockState *s = opaque; > + > + s->ref_time_pending = true; > + > + return 0; > +} > + > +static const VMStateDescription vmstate_mshv_clock = { > + .name = "mshv-clock", > + .version_id = 1, > + .minimum_version_id = 1, > + .post_load = mshv_clock_post_load, > + .fields = (const VMStateField[]) { > + VMSTATE_UINT64(ref_time, MshvClockState), > + VMSTATE_END_OF_LIST() > + }, > +}; > + > +void mshv_clock_init(void) > +{ > + vmstate_register(NULL, 0, &vmstate_mshv_clock, &mshv_clock); > + qemu_add_vm_change_state_handler(mshv_clock_vm_state_change, > &mshv_clock); > +} > diff --git a/hw/i386/mshv/meson.build b/hw/i386/mshv/meson.build > new file mode 100644 > index 0000000000..0e556851b6 > --- /dev/null > +++ b/hw/i386/mshv/meson.build > @@ -0,0 +1,4 @@ > +i386_mshv_ss = ss.source_set() > +i386_mshv_ss.add(files('clock.c')) > + > +i386_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss) > diff --git a/include/system/mshv.h b/include/system/mshv.h > index 46fe3fcebc..a6f815b822 100644 > --- a/include/system/mshv.h > +++ b/include/system/mshv.h > @@ -55,6 +55,9 @@ DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, > > extern MshvState *mshv_state; > > +/* clock (partition reference time) */ > +void mshv_clock_init(void); > + > /* interrupt */ > int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, > uint32_t vector, > uint32_t vp_index, bool logical_destination_mode, > -- > 2.34.1
Reviewed-by: Doru Blânzeanu <[email protected]>
