summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/pkg/manifests/system-bhyve-tests.p5m1
-rw-r--r--usr/src/test/bhyve-tests/runfiles/default.run1
-rw-r--r--usr/src/test/bhyve-tests/tests/vmm/Makefile3
-rw-r--r--usr/src/test/bhyve-tests/tests/vmm/pause_resume.c84
-rw-r--r--usr/src/uts/intel/io/vmm/io/vatpit.c52
-rw-r--r--usr/src/uts/intel/io/vmm/io/vatpit.h5
-rw-r--r--usr/src/uts/intel/io/vmm/io/vhpet.c88
-rw-r--r--usr/src/uts/intel/io/vmm/io/vhpet.h3
-rw-r--r--usr/src/uts/intel/io/vmm/io/vlapic.c36
-rw-r--r--usr/src/uts/intel/io/vmm/io/vlapic.h4
-rw-r--r--usr/src/uts/intel/io/vmm/io/vrtc.c22
-rw-r--r--usr/src/uts/intel/io/vmm/io/vrtc.h3
-rw-r--r--usr/src/uts/intel/io/vmm/sys/vmm_kernel.h4
-rw-r--r--usr/src/uts/intel/io/vmm/vmm.c53
-rw-r--r--usr/src/uts/intel/io/vmm/vmm_sol_dev.c11
-rw-r--r--usr/src/uts/intel/sys/vmm_dev.h4
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)