Allow a virtual machine to be forcibly reset or powered off. This is done
by adding an argument to the VM_SUSPEND ioctl that specifies how the virtual machine should be suspended, viz. VM_SUSPEND_RESET or VM_SUSPEND_POWEROFF. The disposition of VM_SUSPEND is also made available to the exit handler via the 'u.suspended' member of 'struct vm_exit'. This capability is exposed via the '--force-reset' and '--force-poweroff' arguments to /usr/sbin/bhyvectl. Discussed with: grehan@
This commit is contained in:
parent
70b7e330c6
commit
f0fdcfe247
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=265062
@ -343,10 +343,13 @@ vm_run(struct vmctx *ctx, int vcpu, uint64_t rip, struct vm_exit *vmexit)
|
||||
}
|
||||
|
||||
int
|
||||
vm_suspend(struct vmctx *ctx)
|
||||
vm_suspend(struct vmctx *ctx, enum vm_suspend_how how)
|
||||
{
|
||||
struct vm_suspend vmsuspend;
|
||||
|
||||
return (ioctl(ctx->fd, VM_SUSPEND, 0));
|
||||
bzero(&vmsuspend, sizeof(vmsuspend));
|
||||
vmsuspend.how = how;
|
||||
return (ioctl(ctx->fd, VM_SUSPEND, &vmsuspend));
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -61,7 +61,7 @@ int vm_set_register(struct vmctx *ctx, int vcpu, int reg, uint64_t val);
|
||||
int vm_get_register(struct vmctx *ctx, int vcpu, int reg, uint64_t *retval);
|
||||
int vm_run(struct vmctx *ctx, int vcpu, uint64_t rip,
|
||||
struct vm_exit *ret_vmexit);
|
||||
int vm_suspend(struct vmctx *ctx);
|
||||
int vm_suspend(struct vmctx *ctx, enum vm_suspend_how how);
|
||||
int vm_apicid2vcpu(struct vmctx *ctx, int apicid);
|
||||
int vm_inject_exception(struct vmctx *ctx, int vcpu, int vec);
|
||||
int vm_inject_exception2(struct vmctx *ctx, int vcpu, int vec, int errcode);
|
||||
|
@ -29,6 +29,13 @@
|
||||
#ifndef _VMM_H_
|
||||
#define _VMM_H_
|
||||
|
||||
enum vm_suspend_how {
|
||||
VM_SUSPEND_NONE,
|
||||
VM_SUSPEND_RESET,
|
||||
VM_SUSPEND_POWEROFF,
|
||||
VM_SUSPEND_LAST
|
||||
};
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
#define VM_MAX_NAMELEN 32
|
||||
@ -115,7 +122,7 @@ int vm_get_seg_desc(struct vm *vm, int vcpu, int reg,
|
||||
int vm_set_seg_desc(struct vm *vm, int vcpu, int reg,
|
||||
struct seg_desc *desc);
|
||||
int vm_run(struct vm *vm, struct vm_run *vmrun);
|
||||
int vm_suspend(struct vm *vm);
|
||||
int vm_suspend(struct vm *vm, enum vm_suspend_how how);
|
||||
int vm_inject_nmi(struct vm *vm, int vcpu);
|
||||
int vm_nmi_pending(struct vm *vm, int vcpuid);
|
||||
void vm_nmi_clear(struct vm *vm, int vcpuid);
|
||||
@ -134,6 +141,7 @@ int vm_apicid2vcpuid(struct vm *vm, int apicid);
|
||||
void vm_activate_cpu(struct vm *vm, int vcpu);
|
||||
cpuset_t vm_active_cpus(struct vm *vm);
|
||||
struct vm_exit *vm_exitinfo(struct vm *vm, int vcpuid);
|
||||
void vm_exit_suspended(struct vm *vm, int vcpuid, uint64_t rip);
|
||||
|
||||
/*
|
||||
* Rendezvous all vcpus specified in 'dest' and execute 'func(arg)'.
|
||||
@ -382,6 +390,9 @@ struct vm_exit {
|
||||
struct {
|
||||
int vector;
|
||||
} ioapic_eoi;
|
||||
struct {
|
||||
enum vm_suspend_how how;
|
||||
} suspended;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
@ -159,6 +159,10 @@ struct vm_hpet_cap {
|
||||
uint32_t capabilities; /* lower 32 bits of HPET capabilities */
|
||||
};
|
||||
|
||||
struct vm_suspend {
|
||||
enum vm_suspend_how how;
|
||||
};
|
||||
|
||||
enum {
|
||||
/* general routines */
|
||||
IOCNUM_ABIVERS = 0,
|
||||
@ -214,7 +218,7 @@ enum {
|
||||
#define VM_RUN \
|
||||
_IOWR('v', IOCNUM_RUN, struct vm_run)
|
||||
#define VM_SUSPEND \
|
||||
_IO('v', IOCNUM_SUSPEND)
|
||||
_IOW('v', IOCNUM_SUSPEND, struct vm_suspend)
|
||||
#define VM_MAP_MEMORY \
|
||||
_IOWR('v', IOCNUM_MAP_MEMORY, struct vm_memory_segment)
|
||||
#define VM_GET_MEMORY_SEG \
|
||||
|
@ -2044,16 +2044,6 @@ vmx_exit_rendezvous(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
|
||||
return (UNHANDLED);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
vmx_exit_suspended(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
|
||||
{
|
||||
|
||||
vmexit->rip = vmcs_guest_rip();
|
||||
vmexit->inst_length = 0;
|
||||
vmexit->exitcode = VM_EXITCODE_SUSPENDED;
|
||||
return (UNHANDLED);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
vmx_exit_inst_error(struct vmxctx *vmxctx, int rc, struct vm_exit *vmexit)
|
||||
{
|
||||
@ -2173,7 +2163,8 @@ vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
disable_intr();
|
||||
if (vcpu_suspended(suspend_cookie)) {
|
||||
enable_intr();
|
||||
handled = vmx_exit_suspended(vmx, vcpu, vmexit);
|
||||
vm_exit_suspended(vmx->vm, vcpu, vmcs_guest_rip());
|
||||
handled = UNHANDLED;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1211,16 +1211,45 @@ vm_handle_suspend(struct vm *vm, int vcpuid, bool *retu)
|
||||
}
|
||||
|
||||
int
|
||||
vm_suspend(struct vm *vm)
|
||||
vm_suspend(struct vm *vm, enum vm_suspend_how how)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (atomic_cmpset_int(&vm->suspend, 0, 1)) {
|
||||
VM_CTR0(vm, "virtual machine suspended");
|
||||
return (0);
|
||||
} else {
|
||||
VM_CTR0(vm, "virtual machine already suspended");
|
||||
if (how <= VM_SUSPEND_NONE || how >= VM_SUSPEND_LAST)
|
||||
return (EINVAL);
|
||||
|
||||
if (atomic_cmpset_int(&vm->suspend, 0, how) == 0) {
|
||||
VM_CTR2(vm, "virtual machine already suspended %d/%d",
|
||||
vm->suspend, how);
|
||||
return (EALREADY);
|
||||
}
|
||||
|
||||
VM_CTR1(vm, "virtual machine successfully suspended %d", how);
|
||||
|
||||
/*
|
||||
* Notify all active vcpus that they are now suspended.
|
||||
*/
|
||||
for (i = 0; i < VM_MAXCPU; i++) {
|
||||
if (CPU_ISSET(i, &vm->active_cpus))
|
||||
vcpu_notify_event(vm, i, false);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
vm_exit_suspended(struct vm *vm, int vcpuid, uint64_t rip)
|
||||
{
|
||||
struct vm_exit *vmexit;
|
||||
|
||||
KASSERT(vm->suspend > VM_SUSPEND_NONE && vm->suspend < VM_SUSPEND_LAST,
|
||||
("vm_exit_suspended: invalid suspend type %d", vm->suspend));
|
||||
|
||||
vmexit = vm_exitinfo(vm, vcpuid);
|
||||
vmexit->rip = rip;
|
||||
vmexit->inst_length = 0;
|
||||
vmexit->exitcode = VM_EXITCODE_SUSPENDED;
|
||||
vmexit->u.suspended.how = vm->suspend;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -166,6 +166,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
struct vm_stat_desc *statdesc;
|
||||
struct vm_x2apic *x2apic;
|
||||
struct vm_gpa_pte *gpapte;
|
||||
struct vm_suspend *vmsuspend;
|
||||
|
||||
sc = vmmdev_lookup2(cdev);
|
||||
if (sc == NULL)
|
||||
@ -241,7 +242,8 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
error = vm_run(sc->vm, vmrun);
|
||||
break;
|
||||
case VM_SUSPEND:
|
||||
error = vm_suspend(sc->vm);
|
||||
vmsuspend = (struct vm_suspend *)data;
|
||||
error = vm_suspend(sc->vm, vmsuspend->how);
|
||||
break;
|
||||
case VM_STAT_DESC: {
|
||||
statdesc = (struct vm_stat_desc *)data;
|
||||
|
@ -461,17 +461,18 @@ vmexit_inst_emul(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
|
||||
|
||||
static pthread_mutex_t resetcpu_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t resetcpu_cond = PTHREAD_COND_INITIALIZER;
|
||||
static int resetcpu = -1;
|
||||
|
||||
static int
|
||||
vmexit_suspend(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
|
||||
{
|
||||
|
||||
assert(resetcpu != -1);
|
||||
enum vm_suspend_how how;
|
||||
|
||||
how = vmexit->u.suspended.how;
|
||||
assert(how == VM_SUSPEND_RESET || how == VM_SUSPEND_POWEROFF);
|
||||
|
||||
fbsdrun_deletecpu(ctx, *pvcpu);
|
||||
|
||||
if (*pvcpu != resetcpu) {
|
||||
if (*pvcpu != BSP) {
|
||||
pthread_mutex_lock(&resetcpu_mtx);
|
||||
pthread_cond_signal(&resetcpu_cond);
|
||||
pthread_mutex_unlock(&resetcpu_mtx);
|
||||
@ -483,7 +484,12 @@ vmexit_suspend(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
|
||||
pthread_cond_wait(&resetcpu_cond, &resetcpu_mtx);
|
||||
}
|
||||
pthread_mutex_unlock(&resetcpu_mtx);
|
||||
exit(0);
|
||||
|
||||
if (how == VM_SUSPEND_RESET)
|
||||
exit(0);
|
||||
if (how == VM_SUSPEND_POWEROFF)
|
||||
exit(1);
|
||||
return (0); /* NOTREACHED */
|
||||
}
|
||||
|
||||
static vmexit_handler_t handler[VM_EXITCODE_MAX] = {
|
||||
@ -505,6 +511,7 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip)
|
||||
cpuset_t mask;
|
||||
int error, rc, prevcpu;
|
||||
enum vm_exitcode exitcode;
|
||||
enum vm_suspend_how how;
|
||||
|
||||
if (pincpu >= 0) {
|
||||
CPU_ZERO(&mask);
|
||||
@ -538,10 +545,13 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip)
|
||||
rip = vmexit[vcpu].rip;
|
||||
break;
|
||||
case VMEXIT_RESET:
|
||||
if (vm_suspend(ctx) == 0) {
|
||||
assert(resetcpu == -1);
|
||||
resetcpu = vcpu;
|
||||
}
|
||||
case VMEXIT_POWEROFF:
|
||||
if (rc == VMEXIT_RESET)
|
||||
how = VM_SUSPEND_RESET;
|
||||
else
|
||||
how = VM_SUSPEND_POWEROFF;
|
||||
error = vm_suspend(ctx, how);
|
||||
assert(error == 0 || errno == EALREADY);
|
||||
rip = vmexit[vcpu].rip + vmexit[vcpu].inst_length;
|
||||
break;
|
||||
default:
|
||||
|
@ -191,13 +191,16 @@ usage(void)
|
||||
" [--get-highmem]\n"
|
||||
" [--get-gpa-pmap]\n"
|
||||
" [--assert-lapic-lvt=<pin>]\n"
|
||||
" [--inject-nmi]\n",
|
||||
" [--inject-nmi]\n"
|
||||
" [--force-reset]\n"
|
||||
" [--force-poweroff]\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int get_stats, getcap, setcap, capval, get_gpa_pmap;
|
||||
static int inject_nmi, assert_lapic_lvt;
|
||||
static int force_reset, force_poweroff;
|
||||
static const char *capname;
|
||||
static int create, destroy, get_lowmem, get_highmem;
|
||||
static uint64_t memsize;
|
||||
@ -565,6 +568,8 @@ main(int argc, char *argv[])
|
||||
{ "create", NO_ARG, &create, 1 },
|
||||
{ "destroy", NO_ARG, &destroy, 1 },
|
||||
{ "inject-nmi", NO_ARG, &inject_nmi, 1 },
|
||||
{ "force-reset", NO_ARG, &force_reset, 1 },
|
||||
{ "force-poweroff", NO_ARG, &force_poweroff, 1 },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
@ -1535,6 +1540,12 @@ main(int argc, char *argv[])
|
||||
printf("vm_run error %d\n", error);
|
||||
}
|
||||
|
||||
if (!error && force_reset)
|
||||
error = vm_suspend(ctx, VM_SUSPEND_RESET);
|
||||
|
||||
if (!error && force_poweroff)
|
||||
error = vm_suspend(ctx, VM_SUSPEND_POWEROFF);
|
||||
|
||||
if (error)
|
||||
printf("errno = %d\n", errno);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user