-/* $OpenBSD: main.c,v 1.29 2017/06/07 23:15:49 mlarkin Exp $ */
+/* $OpenBSD: main.c,v 1.30 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
int ctl_start(struct parse_result *, int, char *[]);
int ctl_status(struct parse_result *, int, char *[]);
int ctl_stop(struct parse_result *, int, char *[]);
+int ctl_pause(struct parse_result *, int, char *[]);
+int ctl_unpause(struct parse_result *, int, char *[]);
struct ctl_command ctl_commands[] = {
{ "console", CMD_CONSOLE, ctl_console, "id" },
"\t\t[-n switch] [-i count] [-d disk]*" },
{ "status", CMD_STATUS, ctl_status, "[id]" },
{ "stop", CMD_STOP, ctl_stop, "id" },
+ { "pause", CMD_PAUSE, ctl_pause, "id" },
+ { "unpause", CMD_UNPAUSE, ctl_unpause, "id" },
{ NULL }
};
imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1,
&res->mode, sizeof(res->mode));
break;
+ case CMD_PAUSE:
+ pause_vm(res->id, res->name);
+ break;
+ case CMD_UNPAUSE:
+ unpause_vm(res->id, res->name);
+ break;
case CMD_CREATE:
case NONE:
break;
case CMD_STATUS:
done = add_info(&imsg, &ret);
break;
+ case CMD_PAUSE:
+ done = pause_vm_complete(&imsg, &ret);
+ break;
+ case CMD_UNPAUSE:
+ done = unpause_vm_complete(&imsg, &ret);
+ break;
default:
done = 1;
break;
return (vmmaction(res));
}
+int
+ctl_pause(struct parse_result *res, int argc, char *argv[])
+{
+ if (argc == 2) {
+ if (parse_vmid(res, argv[1]) == -1)
+ errx(1, "invalid id: %s", argv[1]);
+ } else if (argc != 2)
+ ctl_usage(res->ctl);
+
+ return (vmmaction(res));
+}
+
+int
+ctl_unpause(struct parse_result *res, int argc, char *argv[])
+{
+ if (argc == 2) {
+ if (parse_vmid(res, argv[1]) == -1)
+ errx(1, "invalid id: %s", argv[1]);
+ } else if (argc != 2)
+ ctl_usage(res->ctl);
+
+ return (vmmaction(res));
+}
+
__dead void
ctl_openconsole(const char *name)
{
-/* $OpenBSD: vmctl.c,v 1.30 2017/04/19 15:38:32 reyk Exp $ */
+/* $OpenBSD: vmctl.c,v 1.31 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
return (1);
}
+void
+pause_vm(uint32_t pause_id, const char *name)
+{
+ struct vmop_id vid;
+
+ memset(&vid, 0, sizeof(vid));
+ vid.vid_id = pause_id;
+ if (name != NULL)
+ (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
+
+ imsg_compose(ibuf, IMSG_VMDOP_PAUSE_VM, 0, 0, -1,
+ &vid, sizeof(vid));
+}
+
+int
+pause_vm_complete(struct imsg *imsg, int *ret)
+{
+ struct vmop_result *vmr;
+ int res;
+
+ if (imsg->hdr.type == IMSG_VMDOP_PAUSE_VM_RESPONSE) {
+ vmr = (struct vmop_result *)imsg->data;
+ res = vmr->vmr_result;
+ if (res) {
+ errno = res;
+ warn("pause vm command failed");
+ *ret = EIO;
+ } else {
+ warnx("paused vm %d successfully", vmr->vmr_id);
+ *ret = 0;
+ }
+ } else {
+ warnx("unexpected response received from vmd");
+ *ret = EINVAL;
+ }
+
+ return (1);
+}
+
+void
+unpause_vm(uint32_t pause_id, const char *name)
+{
+ struct vmop_id vid;
+
+ memset(&vid, 0, sizeof(vid));
+ vid.vid_id = pause_id;
+ if (name != NULL)
+ (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
+
+ imsg_compose(ibuf, IMSG_VMDOP_UNPAUSE_VM, 0, 0, -1,
+ &vid, sizeof(vid));
+}
+
+int
+unpause_vm_complete(struct imsg *imsg, int *ret)
+{
+ struct vmop_result *vmr;
+ int res;
+
+ if (imsg->hdr.type == IMSG_VMDOP_UNPAUSE_VM_RESPONSE) {
+ vmr = (struct vmop_result *)imsg->data;
+ res = vmr->vmr_result;
+ if (res) {
+ errno = res;
+ warn("unpause vm command failed");
+ *ret = EIO;
+ } else {
+ warnx("unpaused vm %d successfully", vmr->vmr_id);
+ *ret = 0;
+ }
+ } else {
+ warnx("unexpected response received from vmd");
+ *ret = EINVAL;
+ }
+
+ return (1);
+}
+
/*
* terminate_vm
*
-/* $OpenBSD: vmctl.h,v 1.14 2017/04/06 18:07:13 reyk Exp $ */
+/* $OpenBSD: vmctl.h,v 1.15 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
CMD_START,
CMD_STATUS,
CMD_STOP,
+ CMD_PAUSE,
+ CMD_UNPAUSE,
};
struct ctl_command;
int vm_start_complete(struct imsg *, int *, int);
void terminate_vm(uint32_t, const char *);
int terminate_vm_complete(struct imsg *, int *);
+void pause_vm(uint32_t, const char *);
+int pause_vm_complete(struct imsg *, int *);
+void unpause_vm(uint32_t, const char *);
+int unpause_vm_complete(struct imsg *, int *);
int check_info_id(const char *, uint32_t);
void get_info_vm(uint32_t, const char *, int);
int add_info(struct imsg *, int *);
-/* $OpenBSD: control.c,v 1.19 2017/05/04 19:41:58 reyk Exp $ */
+/* $OpenBSD: control.c,v 1.20 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2010-2015 Reyk Floeter <reyk@openbsd.org>
switch (imsg->hdr.type) {
case IMSG_VMDOP_START_VM_RESPONSE:
+ case IMSG_VMDOP_PAUSE_VM_RESPONSE:
+ case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
case IMSG_VMDOP_GET_INFO_VM_DATA:
case IMSG_VMDOP_GET_INFO_VM_END_DATA:
log_setverbose(v);
/* FALLTHROUGH */
+ case IMSG_VMDOP_PAUSE_VM:
+ case IMSG_VMDOP_UNPAUSE_VM:
case IMSG_VMDOP_LOAD:
case IMSG_VMDOP_RELOAD:
case IMSG_CTL_RESET:
-/* $OpenBSD: i8253.c,v 1.15 2017/05/08 09:08:40 reyk Exp $ */
+/* $OpenBSD: i8253.c,v 1.16 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
*
evtimer_del(&i8253_channel[chn].timer);
timerclear(&tv);
+ i8253_channel[chn].in_use = 1;
tv.tv_usec = (i8253_channel[chn].start * NS_PER_TICK) / 1000;
evtimer_add(&i8253_channel[chn].timer, &tv);
}
int
i8253_restore(int fd, uint32_t vm_id)
{
+ int i;
log_debug("%s: restoring PIT", __func__);
if (atomicio(read, fd, &i8253_channel, sizeof(i8253_channel)) !=
sizeof(i8253_channel)) {
log_warnx("%s: error reading PIT from fd", __func__);
return (-1);
}
- memset(&i8253_channel[0].timer, 0, sizeof(struct event));
- memset(&i8253_channel[1].timer, 0, sizeof(struct event));
- memset(&i8253_channel[2].timer, 0, sizeof(struct event));
- i8253_channel[0].vm_id = vm_id;
- i8253_channel[1].vm_id = vm_id;
- i8253_channel[2].vm_id = vm_id;
- evtimer_set(&i8253_channel[0].timer, i8253_fire, &i8253_channel[0]);
- evtimer_set(&i8253_channel[1].timer, i8253_fire, &i8253_channel[1]);
- evtimer_set(&i8253_channel[2].timer, i8253_fire, &i8253_channel[2]);
- i8253_reset(0);
+ for (i = 0; i < 3; i++) {
+ memset(&i8253_channel[i].timer, 0, sizeof(struct event));
+ i8253_channel[i].vm_id = vm_id;
+ evtimer_set(&i8253_channel[i].timer, i8253_fire,
+ &i8253_channel[i]);
+ i8253_reset(i);
+ }
return (0);
}
void
i8253_stop()
{
- evtimer_del(&i8253_channel[0].timer);
- evtimer_del(&i8253_channel[1].timer);
- evtimer_del(&i8253_channel[2].timer);
+ int i;
+ for (i = 0; i < 3; i++)
+ evtimer_del(&i8253_channel[i].timer);
+}
+
+void
+i8253_start()
+{
+ int i;
+ for (i = 0; i < 3; i++)
+ if(i8253_channel[i].in_use)
+ i8253_reset(i);
}
-/* $OpenBSD: i8253.h,v 1.6 2017/05/08 09:08:40 reyk Exp $ */
+/* $OpenBSD: i8253.h,v 1.7 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
*
uint8_t rbs; /* channel is in readback status mode */
struct event timer; /* timer event for this counter */
uint32_t vm_id; /* owning VM id */
+ int in_use; /* denotes if this counter was ever used */
};
void i8253_init(uint32_t);
uint8_t vcpu_exit_i8253(struct vm_run_params *);
void i8253_do_readback(uint32_t);
void i8253_stop(void);
+void i8253_start(void);
-/* $OpenBSD: mc146818.c,v 1.14 2017/05/08 09:08:40 reyk Exp $ */
+/* $OpenBSD: mc146818.c,v 1.15 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
*
memset(&rtc.per, 0, sizeof(struct event));
evtimer_set(&rtc.sec, rtc_fire1, NULL);
evtimer_set(&rtc.per, rtc_fireper, (void *)(intptr_t)rtc.vm_id);
-
- evtimer_add(&rtc.per, &rtc.per_tv);
- evtimer_add(&rtc.sec, &rtc.sec_tv);
return (0);
}
evtimer_del(&rtc.per);
evtimer_del(&rtc.sec);
}
+
+void
+mc146818_start()
+{
+ evtimer_add(&rtc.per, &rtc.per_tv);
+ evtimer_add(&rtc.sec, &rtc.sec_tv);
+}
-/* $OpenBSD: mc146818.h,v 1.4 2017/05/08 09:08:40 reyk Exp $ */
+/* $OpenBSD: mc146818.h,v 1.5 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
*
int mc146818_dump(int);
int mc146818_restore(int, uint32_t);
void mc146818_stop(void);
+void mc146818_start(void);
-/* $OpenBSD: vm.c,v 1.20 2017/06/07 14:53:28 mlarkin Exp $ */
+/* $OpenBSD: vm.c,v 1.21 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
uint8_t vcpu_exit_pci(struct vm_run_params *);
int vcpu_pic_intr(uint32_t, uint32_t, uint8_t);
int loadfile_bios(FILE *, struct vcpu_reg_state *);
+void pause_vm(struct vm_create_params *);
+void unpause_vm(struct vm_create_params *);
static struct vm_mem_range *find_gpa_range(struct vm_create_params *, paddr_t,
size_t);
vm_dispatch_vmm(int fd, short event, void *arg)
{
struct vmd_vm *vm = arg;
+ struct vmop_result vmr;
struct imsgev *iev = &vm->vm_iev;
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
if (vmmci_ctl(VMMCI_REBOOT) == -1)
_exit(0);
break;
+ case IMSG_VMDOP_PAUSE_VM:
+ vmr.vmr_result = 0;
+ vmr.vmr_id = vm->vm_vmid;
+ pause_vm(&vm->vm_params.vmc_params);
+ imsg_compose_event(&vm->vm_iev,
+ IMSG_VMDOP_PAUSE_VM_RESPONSE,
+ imsg.hdr.peerid, imsg.hdr.pid, -1, &vmr,
+ sizeof(vmr));
+ break;
+ case IMSG_VMDOP_UNPAUSE_VM:
+ vmr.vmr_result = 0;
+ vmr.vmr_id = vm->vm_vmid;
+ unpause_vm(&vm->vm_params.vmc_params);
+ imsg_compose_event(&vm->vm_iev,
+ IMSG_VMDOP_UNPAUSE_VM_RESPONSE,
+ imsg.hdr.peerid, imsg.hdr.pid, -1, &vmr,
+ sizeof(vmr));
+ break;
default:
fatalx("%s: got invalid imsg %d from %s",
__func__, imsg.hdr.type,
_exit(0);
}
+void
+pause_vm(struct vm_create_params *vcp)
+{
+ if (current_vm->vm_paused)
+ return;
+
+ current_vm->vm_paused = 1;
+
+ /* XXX: vcpu_run_loop is running in another thread and we have to wait
+ * for the vm to exit before returning */
+ sleep(1);
+
+ i8253_stop();
+ mc146818_stop();
+}
+
+void
+unpause_vm(struct vm_create_params *vcp)
+{
+ unsigned int n;
+ if (!current_vm->vm_paused)
+ return;
+
+ current_vm->vm_paused = 0;
+
+ i8253_start();
+ mc146818_start();
+ for (n = 0; n <= vcp->vcp_ncpus; n++)
+ pthread_cond_broadcast(&vcpu_run_cond[n]);
+}
+
/*
* vcpu_reset
*
return ((void *)ret);
}
- /* If we are halted, wait */
+ /* If we are halted or paused, wait */
if (vcpu_hlt[n]) {
- ret = pthread_cond_wait(&vcpu_run_cond[n],
- &vcpu_run_mtx[n]);
-
- if (ret) {
- log_warnx("%s: can't wait on cond (%d)",
- __func__, (int)ret);
- (void)pthread_mutex_unlock(&vcpu_run_mtx[n]);
- break;
+ while (current_vm->vm_paused == 1) {
+ ret = pthread_cond_wait(&vcpu_run_cond[n],
+ &vcpu_run_mtx[n]);
+ if (ret) {
+ log_warnx(
+ "%s: can't wait on cond (%d)",
+ __func__, (int)ret);
+ (void)pthread_mutex_unlock(
+ &vcpu_run_mtx[n]);
+ break;
+ }
+ }
+ if (vcpu_hlt[n]) {
+ ret = pthread_cond_wait(&vcpu_run_cond[n],
+ &vcpu_run_mtx[n]);
+
+ if (ret) {
+ log_warnx(
+ "%s: can't wait on cond (%d)",
+ __func__, (int)ret);
+ (void)pthread_mutex_unlock(
+ &vcpu_run_mtx[n]);
+ break;
+ }
}
}
ret = pthread_mutex_unlock(&vcpu_run_mtx[n]);
+
if (ret) {
log_warnx("%s: can't unlock mutex on cond (%d)",
__func__, (int)ret);
-/* $OpenBSD: vmd.c,v 1.62 2017/05/29 07:15:22 mlarkin Exp $ */
+/* $OpenBSD: vmd.c,v 1.63 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
proc_forward_imsg(ps, imsg, PROC_PRIV, -1);
cmd = IMSG_CTL_OK;
break;
+ case IMSG_VMDOP_PAUSE_VM:
+ case IMSG_VMDOP_UNPAUSE_VM:
+ IMSG_SIZE_CHECK(imsg, &vid);
+ memcpy(&vid, imsg->data, sizeof(vid));
+ if (vid.vid_id == 0) {
+ if ((vm = vm_getbyname(vid.vid_name)) == NULL) {
+ res = ENOENT;
+ cmd = IMSG_VMDOP_PAUSE_VM_RESPONSE;
+ break;
+ } else {
+ vid.vid_id = vm->vm_vmid;
+ }
+ }
+ proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type,
+ imsg->hdr.peerid, -1, &vid, sizeof(vid));
+ break;
default:
return (-1);
}
struct vmop_info_result vir;
switch (imsg->hdr.type) {
+ case IMSG_VMDOP_PAUSE_VM_RESPONSE:
+ IMSG_SIZE_CHECK(imsg, &vmr);
+ memcpy(&vmr, imsg->data, sizeof(vmr));
+ if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
+ break;
+ proc_compose_imsg(ps, PROC_CONTROL, -1,
+ imsg->hdr.type, imsg->hdr.peerid, -1,
+ imsg->data, sizeof(imsg->data));
+ log_info("%s: paused vm %d successfully",
+ vm->vm_params.vmc_params.vcp_name,
+ vm->vm_vmid);
+ break;
+ case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
+ IMSG_SIZE_CHECK(imsg, &vmr);
+ memcpy(&vmr, imsg->data, sizeof(vmr));
+ if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
+ break;
+ proc_compose_imsg(ps, PROC_CONTROL, -1,
+ imsg->hdr.type, imsg->hdr.peerid, -1,
+ imsg->data, sizeof(imsg->data));
+ log_info("%s: unpaused vm %d successfully.",
+ vm->vm_params.vmc_params.vcp_name,
+ vm->vm_vmid);
+ break;
case IMSG_VMDOP_START_VM_RESPONSE:
IMSG_SIZE_CHECK(imsg, &vmr);
memcpy(&vmr, imsg->data, sizeof(vmr));
vcp = &vmc->vmc_params;
vm->vm_pid = -1;
vm->vm_tty = -1;
+ vm->vm_paused = 0;
for (i = 0; i < vcp->vcp_ndisks; i++)
vm->vm_disks[i] = -1;
-/* $OpenBSD: vmd.h,v 1.56 2017/06/12 13:41:24 deraadt Exp $ */
+/* $OpenBSD: vmd.h,v 1.57 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
IMSG_VMDOP_START_VM_IF,
IMSG_VMDOP_START_VM_END,
IMSG_VMDOP_START_VM_RESPONSE,
+ IMSG_VMDOP_PAUSE_VM,
+ IMSG_VMDOP_PAUSE_VM_RESPONSE,
+ IMSG_VMDOP_UNPAUSE_VM,
+ IMSG_VMDOP_UNPAUSE_VM_RESPONSE,
IMSG_VMDOP_TERMINATE_VM_REQUEST,
IMSG_VMDOP_TERMINATE_VM_RESPONSE,
IMSG_VMDOP_TERMINATE_VM_EVENT,
struct imsgev vm_iev;
int vm_shutdown;
uid_t vm_uid;
+ int vm_paused;
TAILQ_ENTRY(vmd_vm) vm_entry;
};
-/* $OpenBSD: vmm.c,v 1.69 2017/04/21 07:03:26 reyk Exp $ */
+/* $OpenBSD: vmm.c,v 1.70 2017/07/09 00:51:40 pd Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
int res = 0, cmd = 0, verbose;
struct vmd_vm *vm = NULL;
struct vm_terminate_params vtp;
+ struct vmop_id vid;
struct vmop_result vmr;
uint32_t id = 0;
unsigned int mode;
-1, &verbose, sizeof(verbose));
}
break;
+ case IMSG_VMDOP_PAUSE_VM:
+ IMSG_SIZE_CHECK(imsg, &vid);
+ memcpy(&vid, imsg->data, sizeof(vid));
+ id = vid.vid_id;
+ vm = vm_getbyvmid(id);
+ if ((vm = vm_getbyvmid(id)) == NULL) {
+ res = ENOENT;
+ cmd = IMSG_VMDOP_PAUSE_VM_RESPONSE;
+ break;
+ }
+ imsg_compose_event(&vm->vm_iev,
+ imsg->hdr.type, imsg->hdr.peerid, imsg->hdr.pid,
+ imsg->fd, &vid, sizeof(vid));
+ break;
+ case IMSG_VMDOP_UNPAUSE_VM:
+ IMSG_SIZE_CHECK(imsg, &vid);
+ memcpy(&vid, imsg->data, sizeof(vid));
+ id = vid.vid_id;
+ if ((vm = vm_getbyvmid(id)) == NULL) {
+ res = ENOENT;
+ cmd = IMSG_VMDOP_UNPAUSE_VM_RESPONSE;
+ break;
+ }
+ imsg_compose_event(&vm->vm_iev,
+ imsg->hdr.type, imsg->hdr.peerid, imsg->hdr.pid,
+ imsg->fd, &vid, sizeof(vid));
+ break;
default:
return (-1);
}
}
if (id == 0)
id = imsg->hdr.peerid;
+ case IMSG_VMDOP_PAUSE_VM_RESPONSE:
+ case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
memset(&vmr, 0, sizeof(vmr));
vmr.vmr_result = res;
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
ssize_t n;
+ unsigned int i;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
case IMSG_VMDOP_VM_REBOOT:
vm->vm_shutdown = 0;
break;
+ case IMSG_VMDOP_PAUSE_VM_RESPONSE:
+ case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
+ for (i = 0; i < sizeof(procs); i++) {
+ if (procs[i].p_id == PROC_PARENT) {
+ proc_forward_imsg(procs[i].p_ps,
+ &imsg, PROC_PARENT,
+ -1);
+ break;
+ }
+ }
+ break;
+
default:
fatalx("%s: got invalid imsg %d from %s",
__func__, imsg.hdr.type,