diff options
Diffstat (limited to 'usr/src/uts/intel/io/vmm')
-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 |
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; |