summaryrefslogtreecommitdiff
path: root/usr/src/uts/intel/io/vmm
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/intel/io/vmm')
-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
11 files changed, 247 insertions, 34 deletions
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;