diff options
-rw-r--r-- | usr/src/pkg/manifests/system-bhyve-tests.p5m | 1 | ||||
-rw-r--r-- | usr/src/test/bhyve-tests/runfiles/default.run | 1 | ||||
-rw-r--r-- | usr/src/test/bhyve-tests/tests/vmm/Makefile | 3 | ||||
-rw-r--r-- | usr/src/test/bhyve-tests/tests/vmm/pause_resume.c | 84 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/io/vatpit.c | 52 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/io/vatpit.h | 5 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/io/vhpet.c | 88 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/io/vhpet.h | 3 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/io/vlapic.c | 36 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/io/vlapic.h | 4 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/io/vrtc.c | 22 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/io/vrtc.h | 3 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/sys/vmm_kernel.h | 4 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/vmm.c | 53 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmm/vmm_sol_dev.c | 11 | ||||
-rw-r--r-- | usr/src/uts/intel/sys/vmm_dev.h | 4 |
16 files changed, 338 insertions, 36 deletions
diff --git a/usr/src/pkg/manifests/system-bhyve-tests.p5m b/usr/src/pkg/manifests/system-bhyve-tests.p5m index d96fa52e80..0fd66e1f0a 100644 --- a/usr/src/pkg/manifests/system-bhyve-tests.p5m +++ b/usr/src/pkg/manifests/system-bhyve-tests.p5m @@ -66,6 +66,7 @@ file path=opt/bhyve-tests/tests/vmm/legacy_destruct mode=0555 file path=opt/bhyve-tests/tests/vmm/mem_devmem mode=0555 file path=opt/bhyve-tests/tests/vmm/mem_partial mode=0555 file path=opt/bhyve-tests/tests/vmm/mem_seg_map mode=0555 +file path=opt/bhyve-tests/tests/vmm/pause_resume mode=0555 file path=opt/bhyve-tests/tests/vmm/self_destruct mode=0555 file path=opt/bhyve-tests/tests/vmm/vmm_drv_test_fini mode=0555 file path=opt/bhyve-tests/tests/vmm/vmm_drv_test_init mode=0555 diff --git a/usr/src/test/bhyve-tests/runfiles/default.run b/usr/src/test/bhyve-tests/runfiles/default.run index f176fef66c..afa4b4d9ac 100644 --- a/usr/src/test/bhyve-tests/runfiles/default.run +++ b/usr/src/test/bhyve-tests/runfiles/default.run @@ -37,6 +37,7 @@ tests = [ 'mem_devmem', 'mem_partial', 'mem_seg_map', + 'pause_resume', 'self_destruct' ] post = vmm_drv_test_fini diff --git a/usr/src/test/bhyve-tests/tests/vmm/Makefile b/usr/src/test/bhyve-tests/tests/vmm/Makefile index dd5379add0..3b0528644c 100644 --- a/usr/src/test/bhyve-tests/tests/vmm/Makefile +++ b/usr/src/test/bhyve-tests/tests/vmm/Makefile @@ -30,7 +30,8 @@ PROG = mem_partial \ drv_hold \ cpuid_ioctl \ default_capabs \ - datarw_constraints + datarw_constraints \ + pause_resume SCRIPT = vmm_drv_test_fini vmm_drv_test_init diff --git a/usr/src/test/bhyve-tests/tests/vmm/pause_resume.c b/usr/src/test/bhyve-tests/tests/vmm/pause_resume.c new file mode 100644 index 0000000000..bf5458b6be --- /dev/null +++ b/usr/src/test/bhyve-tests/tests/vmm/pause_resume.c @@ -0,0 +1,84 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2022 Oxide Computer Company + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <libgen.h> +#include <sys/stat.h> +#include <errno.h> +#include <err.h> +#include <assert.h> +#include <sys/sysmacros.h> +#include <stdbool.h> + +#include <sys/vmm.h> +#include <sys/vmm_dev.h> +#include <sys/vmm_data.h> +#include <vmmapi.h> + +#include "common.h" + +int +main(int argc, char *argv[]) +{ + const char *suite_name = basename(argv[0]); + struct vmctx *ctx; + + ctx = create_test_vm(suite_name); + if (ctx == NULL) { + errx(EXIT_FAILURE, "could not open test VM"); + } + + if (vm_activate_cpu(ctx, 0) != 0) { + err(EXIT_FAILURE, "could not activate vcpu0"); + } + + const int vmfd = vm_get_device_fd(ctx); + int error; + + if (ioctl(vmfd, VM_PAUSE, 0) != 0) { + err(EXIT_FAILURE, "VM_PAUSE failed"); + } + + /* Pausing an already-paused instanced should result in EALREADY */ + if (ioctl(vmfd, VM_PAUSE, 0) == 0) { + errx(EXIT_FAILURE, "VM_PAUSE should have failed"); + } + error = errno; + if (error != EALREADY) { + errx(EXIT_FAILURE, "VM_PAUSE unexpected errno: %d != %d", + EALREADY, error); + } + + if (ioctl(vmfd, VM_RESUME, 0) != 0) { + err(EXIT_FAILURE, "VM_RESUME failed"); + } + + /* Resuming an already-running instanced should result in EALREADY */ + if (ioctl(vmfd, VM_RESUME, 0) == 0) { + errx(EXIT_FAILURE, "VM_RESUME should have failed"); + } + error = errno; + if (error != EALREADY) { + errx(EXIT_FAILURE, "VM_RESUME unexpected errno: %d != %d", + EALREADY, error); + } + + vm_destroy(ctx); + (void) printf("%s\tPASS\n", suite_name); + return (EXIT_SUCCESS); +} diff --git a/usr/src/uts/intel/io/vmm/io/vatpit.c b/usr/src/uts/intel/io/vmm/io/vatpit.c index b6218935db..90b10507b3 100644 --- a/usr/src/uts/intel/io/vmm/io/vatpit.c +++ b/usr/src/uts/intel/io/vmm/io/vatpit.c @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #define VATPIT_LOCK(vatpit) mutex_enter(&((vatpit)->lock)) #define VATPIT_UNLOCK(vatpit) mutex_exit(&((vatpit)->lock)) +#define VATPIT_LOCKED(vatpit) MUTEX_HELD(&((vatpit)->lock)) #define TIMER_SEL_MASK 0xc0 #define TIMER_RW_MASK 0x30 @@ -170,6 +171,13 @@ vatpit_callout_handler(void *a) if (c->mode == TIMER_RATEGEN || c->mode == TIMER_SQWAVE) { pit_timer_start_cntr0(vatpit); + } else { + /* + * For non-periodic timers, clear the time target to distinguish + * between a fired timer (thus a zero value) and a pending one + * awaiting VM resumption (holding a non-zero value). + */ + c->time_target = 0; } (void) vatpic_pulse_irq(vatpit->vm, 0); @@ -180,6 +188,16 @@ done: } static void +vatpit_callout_reset(struct vatpit *vatpit) +{ + struct channel *c = &vatpit->channel[0]; + + ASSERT(VATPIT_LOCKED(vatpit)); + callout_reset_hrtime(&c->callout, c->time_target, + vatpit_callout_handler, &c->callout_arg, C_ABSOLUTE); +} + +static void pit_timer_start_cntr0(struct vatpit *vatpit) { struct channel *c = &vatpit->channel[0]; @@ -206,8 +224,7 @@ pit_timer_start_cntr0(struct vatpit *vatpit) hrt_freq_interval(PIT_8254_FREQ, c->total_target); } - callout_reset_hrtime(&c->callout, c->time_target, - vatpit_callout_handler, &c->callout_arg, C_ABSOLUTE); + vatpit_callout_reset(vatpit); } static uint16_t @@ -497,6 +514,29 @@ vatpit_localize_resources(struct vatpit *vatpit) } } +void +vatpit_pause(struct vatpit *vatpit) +{ + struct channel *c = &vatpit->channel[0]; + + VATPIT_LOCK(vatpit); + callout_stop(&c->callout); + VATPIT_UNLOCK(vatpit); +} + +void +vatpit_resume(struct vatpit *vatpit) +{ + struct channel *c = &vatpit->channel[0]; + + VATPIT_LOCK(vatpit); + ASSERT(!callout_active(&c->callout)); + if (c->time_target != 0) { + vatpit_callout_reset(vatpit); + } + VATPIT_UNLOCK(vatpit); +} + static int vatpit_data_read(void *datap, const vmm_data_req_t *req) { @@ -526,7 +566,7 @@ vatpit_data_read(void *datap, const vmm_data_req_t *req) (src->ol_sel ? (1 << 3) : 0) | (src->fr_sel ? (1 << 4) : 0); /* Only channel 0 has the timer configured */ - if (i == 0) { + if (i == 0 && src->time_target != 0) { chan->vac_time_target = vm_normalize_hrtime(vatpit->vm, src->time_target); } else { @@ -605,8 +645,10 @@ vatpit_data_write(void *datap, const vmm_data_req_t *req) out->time_target = time_target; out->time_loaded = time_target - hrt_freq_interval(PIT_8254_FREQ, out->initial); - callout_reset_hrtime(callout, out->time_target, - vatpit_callout_handler, &out->callout_arg, C_ABSOLUTE); + + if (!vm_is_paused(vatpit->vm)) { + vatpit_callout_reset(vatpit); + } } VATPIT_UNLOCK(vatpit); diff --git a/usr/src/uts/intel/io/vmm/io/vatpit.h b/usr/src/uts/intel/io/vmm/io/vatpit.h index bee3a88293..ffa1587cc9 100644 --- a/usr/src/uts/intel/io/vmm/io/vatpit.h +++ b/usr/src/uts/intel/io/vmm/io/vatpit.h @@ -28,6 +28,9 @@ * * $FreeBSD$ */ +/* + * Copyright 2022 Oxide Computer Company + */ #ifndef _VATPIT_H_ #define _VATPIT_H_ @@ -45,5 +48,7 @@ int vatpit_nmisc_handler(void *arg, bool in, uint16_t port, uint8_t bytes, uint32_t *eax); void vatpit_localize_resources(struct vatpit *); +void vatpit_pause(struct vatpit *); +void vatpit_resume(struct vatpit *); #endif /* _VATPIT_H_ */ diff --git a/usr/src/uts/intel/io/vmm/io/vhpet.c b/usr/src/uts/intel/io/vmm/io/vhpet.c index d946dbe691..c34714c740 100644 --- a/usr/src/uts/intel/io/vmm/io/vhpet.c +++ b/usr/src/uts/intel/io/vmm/io/vhpet.c @@ -31,6 +31,7 @@ /* * Copyright 2018 Joyent, Inc. + * Copyright 2022 Oxide Computer Company */ #include <sys/cdefs.h> @@ -96,6 +97,7 @@ struct vhpet { #define VHPET_LOCK(vhp) mutex_enter(&((vhp)->lock)) #define VHPET_UNLOCK(vhp) mutex_exit(&((vhp)->lock)) +#define VHPET_LOCKED(vhp) MUTEX_HELD(&((vhp)->lock)) static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, hrtime_t now); @@ -271,43 +273,45 @@ vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) } static void -vhpet_handler(void *a) +vhpet_handler(void *arg) { - int n; - uint32_t counter; - hrtime_t now; - struct vhpet *vhpet; - struct callout *callout; - struct vhpet_callout_arg *arg; - - arg = a; - vhpet = arg->vhpet; - n = arg->timer_num; - callout = &vhpet->timer[n].callout; + const struct vhpet_callout_arg *vca = arg; + struct vhpet *vhpet = vca->vhpet; + const int n = vca->timer_num; + struct callout *callout = &vhpet->timer[n].callout; VHPET_LOCK(vhpet); - if (callout_pending(callout)) /* callout was reset */ - goto done; - - if (!callout_active(callout)) /* callout was stopped */ - goto done; + if (callout_pending(callout) || !callout_active(callout)) { + VHPET_UNLOCK(vhpet); + return; + } callout_deactivate(callout); + ASSERT(vhpet_counter_enabled(vhpet)); - if (!vhpet_counter_enabled(vhpet)) - panic("vhpet(%p) callout with counter disabled", vhpet); + if (vhpet_periodic_timer(vhpet, n)) { + hrtime_t now; + uint32_t counter = vhpet_counter(vhpet, &now); - counter = vhpet_counter(vhpet, &now); - vhpet_start_timer(vhpet, n, counter, now); + vhpet_start_timer(vhpet, n, counter, now); + } else { + /* + * Zero out the expiration time to distinguish a fired timer + * from one which is held due to a VM pause. + */ + vhpet->timer[n].callout_expire = 0; + } vhpet_timer_interrupt(vhpet, n); -done: + VHPET_UNLOCK(vhpet); } static void vhpet_stop_timer(struct vhpet *vhpet, int n, hrtime_t now) { + ASSERT(VHPET_LOCKED(vhpet)); + callout_stop(&vhpet->timer[n].callout); /* @@ -320,6 +324,7 @@ vhpet_stop_timer(struct vhpet *vhpet, int n, hrtime_t now) if (vhpet->timer[n].callout_expire < now) { vhpet_timer_interrupt(vhpet, n); } + vhpet->timer[n].callout_expire = 0; } static void @@ -327,6 +332,8 @@ vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, hrtime_t now) { struct vhpet_timer *timer = &vhpet->timer[n]; + ASSERT(VHPET_LOCKED(vhpet)); + if (timer->comprate != 0) vhpet_adjust_compval(vhpet, n, counter); else { @@ -739,6 +746,34 @@ vhpet_localize_resources(struct vhpet *vhpet) } } +void +vhpet_pause(struct vhpet *vhpet) +{ + VHPET_LOCK(vhpet); + for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) { + struct vhpet_timer *timer = &vhpet->timer[i]; + + callout_stop(&timer->callout); + } + VHPET_UNLOCK(vhpet); +} + +void +vhpet_resume(struct vhpet *vhpet) +{ + VHPET_LOCK(vhpet); + for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) { + struct vhpet_timer *timer = &vhpet->timer[i]; + + if (timer->callout_expire != 0) { + callout_reset_hrtime(&timer->callout, + timer->callout_expire, vhpet_handler, + &timer->arg, C_ABSOLUTE); + } + } + VHPET_UNLOCK(vhpet); +} + static int vhpet_data_read(void *datap, const vmm_data_req_t *req) { @@ -762,7 +797,7 @@ vhpet_data_read(void *datap, const vmm_data_req_t *req) timer_out->vht_msi = timer->msireg; timer_out->vht_comp_val = timer->compval; timer_out->vht_comp_rate = timer->comprate; - if (callout_pending(&timer->callout)) { + if (timer->callout_expire != 0) { timer_out->vht_time_target = vm_normalize_hrtime(vhpet->vm, timer->callout_expire); @@ -895,10 +930,15 @@ vhpet_data_write(void *datap, const vmm_data_req_t *req) * from the HPET would come along for the ride. */ - /* TODO: properly configure timer */ if (timer_src->vht_time_target != 0) { timer->callout_expire = vm_denormalize_hrtime(vhpet->vm, timer_src->vht_time_target); + + if (!vm_is_paused(vhpet->vm)) { + callout_reset_hrtime(&timer->callout, + timer->callout_expire, vhpet_handler, + &timer->arg, C_ABSOLUTE); + } } else { timer->callout_expire = 0; } diff --git a/usr/src/uts/intel/io/vmm/io/vhpet.h b/usr/src/uts/intel/io/vmm/io/vhpet.h index 0ea0a6b15a..8729b44424 100644 --- a/usr/src/uts/intel/io/vmm/io/vhpet.h +++ b/usr/src/uts/intel/io/vmm/io/vhpet.h @@ -31,6 +31,7 @@ /* * Copyright 2018 Joyent, Inc. + * Copyright 2022 Oxide Computer Company */ #ifndef _VHPET_H_ @@ -48,5 +49,7 @@ int vhpet_mmio_read(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t *val, int vhpet_getcap(struct vm_hpet_cap *cap); void vhpet_localize_resources(struct vhpet *vhpet); +void vhpet_pause(struct vhpet *); +void vhpet_resume(struct vhpet *); #endif /* _VHPET_H_ */ diff --git a/usr/src/uts/intel/io/vmm/io/vlapic.c b/usr/src/uts/intel/io/vmm/io/vlapic.c index e6b5f3be00..d4edbdd53b 100644 --- a/usr/src/uts/intel/io/vmm/io/vlapic.c +++ b/usr/src/uts/intel/io/vmm/io/vlapic.c @@ -772,6 +772,14 @@ vlapic_callout_handler(void *arg) vlapic->timer_fire_when += vlapic->timer_period; } vlapic_callout_reset(vlapic); + } else { + /* + * Clear the target time so that logic can distinguish from a + * timer which has fired (where the value is zero) from one + * which is held pending due to the instance being paused (where + * the value is non-zero, but the callout is not pending). + */ + vlapic->timer_fire_when = 0; } done: VLAPIC_TIMER_UNLOCK(vlapic); @@ -1708,6 +1716,25 @@ vlapic_localize_resources(struct vlapic *vlapic) vmm_glue_callout_localize(&vlapic->callout); } +void +vlapic_pause(struct vlapic *vlapic) +{ + VLAPIC_TIMER_LOCK(vlapic); + callout_stop(&vlapic->callout); + VLAPIC_TIMER_UNLOCK(vlapic); + +} + +void +vlapic_resume(struct vlapic *vlapic) +{ + VLAPIC_TIMER_LOCK(vlapic); + if (vlapic->timer_fire_when != 0) { + vlapic_callout_reset(vlapic); + } + VLAPIC_TIMER_UNLOCK(vlapic); +} + static int vlapic_data_read(void *datap, const vmm_data_req_t *req) { @@ -1726,7 +1753,7 @@ vlapic_data_read(void *datap, const vmm_data_req_t *req) out->vl_msr_apicbase = vlapic->msr_apicbase; out->vl_esr_pending = vlapic->esr_pending; - if (callout_pending(&vlapic->callout)) { + if (vlapic->timer_fire_when != 0) { out->vl_timer_target = vm_normalize_hrtime(vlapic->vm, vlapic->timer_fire_when); } else { @@ -1923,7 +1950,12 @@ vlapic_data_write(void *datap, const vmm_data_req_t *req) if (src->vl_timer_target != 0) { vlapic->timer_fire_when = vm_denormalize_hrtime(vlapic->vm, src->vl_timer_target); - vlapic_callout_reset(vlapic); + + if (!vm_is_paused(vlapic->vm)) { + vlapic_callout_reset(vlapic); + } + } else { + vlapic->timer_fire_when = 0; } if (vlapic->ops.sync_state) { diff --git a/usr/src/uts/intel/io/vmm/io/vlapic.h b/usr/src/uts/intel/io/vmm/io/vlapic.h index 4fe2d79c69..386c9a0922 100644 --- a/usr/src/uts/intel/io/vmm/io/vlapic.h +++ b/usr/src/uts/intel/io/vmm/io/vlapic.h @@ -30,7 +30,7 @@ /* * Copyright 2018 Joyent, Inc. - * Copyright 2020 Oxide Computer Company + * Copyright 2022 Oxide Computer Company */ #ifndef _VLAPIC_H_ @@ -104,5 +104,7 @@ void vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset); void vlapic_self_ipi_handler(struct vlapic *vlapic, uint32_t val); void vlapic_localize_resources(struct vlapic *vlapic); +void vlapic_pause(struct vlapic *); +void vlapic_resume(struct vlapic *); #endif /* _VLAPIC_H_ */ diff --git a/usr/src/uts/intel/io/vmm/io/vrtc.c b/usr/src/uts/intel/io/vmm/io/vrtc.c index 644532f077..fd4fafdff6 100644 --- a/usr/src/uts/intel/io/vmm/io/vrtc.c +++ b/usr/src/uts/intel/io/vmm/io/vrtc.c @@ -970,6 +970,23 @@ vrtc_localize_resources(struct vrtc *vrtc) vmm_glue_callout_localize(&vrtc->callout); } +void +vrtc_pause(struct vrtc *vrtc) +{ + VRTC_LOCK(vrtc); + callout_stop(&vrtc->callout); + VRTC_UNLOCK(vrtc); +} + +void +vrtc_resume(struct vrtc *vrtc) +{ + VRTC_LOCK(vrtc); + ASSERT(!callout_active(&vrtc->callout)); + vrtc_callout_reset(vrtc, vrtc_freq(vrtc)); + VRTC_UNLOCK(vrtc); +} + static int vrtc_data_read(void *datap, const vmm_data_req_t *req) { @@ -1020,8 +1037,9 @@ vrtc_data_write(void *datap, const vmm_data_req_t *req) time_t curtime = vrtc_curtime(vrtc, NULL); secs_to_rtc(curtime, vrtc, 1); - /* Make sure the callout is appropriately scheduled */ - vrtc_callout_reset(vrtc, vrtc_freq(vrtc)); + if (!vm_is_paused(vrtc->vm)) { + vrtc_callout_reset(vrtc, vrtc_freq(vrtc)); + } VRTC_UNLOCK(vrtc); return (0); diff --git a/usr/src/uts/intel/io/vmm/io/vrtc.h b/usr/src/uts/intel/io/vmm/io/vrtc.h index d3140c1308..e7869335eb 100644 --- a/usr/src/uts/intel/io/vmm/io/vrtc.h +++ b/usr/src/uts/intel/io/vmm/io/vrtc.h @@ -30,6 +30,7 @@ /* * Copyright 2018 Joyent, Inc. + * Copyright 2022 Oxide Computer Company */ #ifndef _VRTC_H_ @@ -54,5 +55,7 @@ int vrtc_data_handler(void *arg, bool in, uint16_t port, uint8_t bytes, uint32_t *val); void vrtc_localize_resources(struct vrtc *); +void vrtc_pause(struct vrtc *); +void vrtc_resume(struct vrtc *); #endif diff --git a/usr/src/uts/intel/io/vmm/sys/vmm_kernel.h b/usr/src/uts/intel/io/vmm/sys/vmm_kernel.h index 290044b438..c5c7d7889e 100644 --- a/usr/src/uts/intel/io/vmm/sys/vmm_kernel.h +++ b/usr/src/uts/intel/io/vmm/sys/vmm_kernel.h @@ -131,6 +131,10 @@ void vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores, int vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores, uint16_t threads, uint16_t maxcpus); +int vm_pause_instance(struct vm *); +int vm_resume_instance(struct vm *); +bool vm_is_paused(struct vm *); + /* * APIs that race against hardware. */ diff --git a/usr/src/uts/intel/io/vmm/vmm.c b/usr/src/uts/intel/io/vmm/vmm.c index 6f85f13be6..44f1ee5ca2 100644 --- a/usr/src/uts/intel/io/vmm/vmm.c +++ b/usr/src/uts/intel/io/vmm/vmm.c @@ -220,6 +220,7 @@ struct vm { struct ioport_config ioports; /* (o) ioport handling */ bool mem_transient; /* (o) alloc transient memory */ + bool is_paused; /* (i) instance is paused */ }; static int vmm_initialized; @@ -728,6 +729,58 @@ vm_reinit(struct vm *vm, uint64_t flags) return (0); } +bool +vm_is_paused(struct vm *vm) +{ + return (vm->is_paused); +} + +int +vm_pause_instance(struct vm *vm) +{ + if (vm->is_paused) { + return (EALREADY); + } + vm->is_paused = true; + + for (uint_t i = 0; i < vm->maxcpus; i++) { + struct vcpu *vcpu = &vm->vcpu[i]; + + if (!CPU_ISSET(i, &vm->active_cpus)) { + continue; + } + vlapic_pause(vcpu->vlapic); + } + vhpet_pause(vm->vhpet); + vatpit_pause(vm->vatpit); + vrtc_pause(vm->vrtc); + + return (0); +} + +int +vm_resume_instance(struct vm *vm) +{ + if (!vm->is_paused) { + return (EALREADY); + } + vm->is_paused = false; + + vrtc_resume(vm->vrtc); + vatpit_resume(vm->vatpit); + vhpet_resume(vm->vhpet); + for (uint_t i = 0; i < vm->maxcpus; i++) { + struct vcpu *vcpu = &vm->vcpu[i]; + + if (!CPU_ISSET(i, &vm->active_cpus)) { + continue; + } + vlapic_resume(vcpu->vlapic); + } + + return (0); +} + int vm_map_mmio(struct vm *vm, vm_paddr_t gpa, size_t len, vm_paddr_t hpa) { diff --git a/usr/src/uts/intel/io/vmm/vmm_sol_dev.c b/usr/src/uts/intel/io/vmm/vmm_sol_dev.c index 882d22b435..c66f1ce17a 100644 --- a/usr/src/uts/intel/io/vmm/vmm_sol_dev.c +++ b/usr/src/uts/intel/io/vmm/vmm_sol_dev.c @@ -481,6 +481,8 @@ vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md, case VM_MUNMAP_MEMSEG: case VM_WRLOCK_CYCLE: case VM_PMTMR_LOCATE: + case VM_PAUSE: + case VM_RESUME: vmm_write_lock(sc); lock_type = LOCK_WRITE_HOLD; break; @@ -1844,6 +1846,15 @@ vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md, break; } + case VM_PAUSE: { + error = vm_pause_instance(sc->vmm_vm); + break; + } + case VM_RESUME: { + error = vm_resume_instance(sc->vmm_vm); + break; + } + default: error = ENOTTY; break; diff --git a/usr/src/uts/intel/sys/vmm_dev.h b/usr/src/uts/intel/sys/vmm_dev.h index 3737686974..6fe04a633e 100644 --- a/usr/src/uts/intel/sys/vmm_dev.h +++ b/usr/src/uts/intel/sys/vmm_dev.h @@ -402,7 +402,7 @@ struct vm_legacy_cpuid { * best-effort activity. Nothing is to be inferred about the magnitude of a * change when the version is modified. It follows no rules like semver. */ -#define VMM_CURRENT_INTERFACE_VERSION 6 +#define VMM_CURRENT_INTERFACE_VERSION 7 #define VMMCTL_IOC_BASE (('V' << 16) | ('M' << 8)) @@ -462,6 +462,8 @@ struct vm_legacy_cpuid { #define VM_PMTMR_LOCATE (VMM_LOCK_IOC_BASE | 0x07) #define VM_MUNMAP_MEMSEG (VMM_LOCK_IOC_BASE | 0x08) #define VM_UNMAP_PPTDEV_MMIO (VMM_LOCK_IOC_BASE | 0x09) +#define VM_PAUSE (VMM_LOCK_IOC_BASE | 0x0a) +#define VM_RESUME (VMM_LOCK_IOC_BASE | 0x0b) #define VM_WRLOCK_CYCLE (VMM_LOCK_IOC_BASE | 0xff) |