#define main qemu_main
#endif /* CONFIG_COCOA */
+#include <glib.h>
+
#include "hw/hw.h"
#include "hw/boards.h"
#include "hw/usb.h"
#include "audio/audio.h"
#include "migration.h"
#include "kvm.h"
+#include "qjson.h"
#include "qemu-option.h"
#include "qemu-config.h"
-#include "qemu-objects.h"
#include "qemu-options.h"
+#include "qmp-commands.h"
+#include "main-loop.h"
#ifdef CONFIG_VIRTFS
#include "fsdev/qemu-fsdev.h"
#endif
#include "slirp/libslirp.h"
#include "trace.h"
-#include "trace/simple.h"
+#include "trace/control.h"
#include "qemu-queue.h"
#include "cpus.h"
#include "arch_init.h"
#endif
int nb_nics;
NICInfo nd_table[MAX_NICS];
-int vm_running;
int autostart;
-int incoming_expected; /* Started with -incoming and waiting for incoming */
static int rtc_utc = 1;
static int rtc_date_offset = -1; /* -1 means no change */
QEMUClock *rtc_clock;
uint64_t node_mem[MAX_NODES];
uint64_t node_cpumask[MAX_NODES];
-static QEMUTimer *nographic_timer;
-
uint8_t qemu_uuid[16];
static QEMUBootSetHandler *boot_set_handler;
return 0;
}
+/***********************************************************/
+/* QEMU state */
+
+static RunState current_run_state = RUN_STATE_PRELAUNCH;
+
+typedef struct {
+ RunState from;
+ RunState to;
+} RunStateTransition;
+
+static const RunStateTransition runstate_transitions_def[] = {
+ /* from -> to */
+ { RUN_STATE_DEBUG, RUN_STATE_RUNNING },
+
+ { RUN_STATE_INMIGRATE, RUN_STATE_RUNNING },
+ { RUN_STATE_INMIGRATE, RUN_STATE_PRELAUNCH },
+
+ { RUN_STATE_INTERNAL_ERROR, RUN_STATE_PAUSED },
+ { RUN_STATE_INTERNAL_ERROR, RUN_STATE_FINISH_MIGRATE },
+
+ { RUN_STATE_IO_ERROR, RUN_STATE_RUNNING },
+ { RUN_STATE_IO_ERROR, RUN_STATE_FINISH_MIGRATE },
+
+ { RUN_STATE_PAUSED, RUN_STATE_RUNNING },
+ { RUN_STATE_PAUSED, RUN_STATE_FINISH_MIGRATE },
+
+ { RUN_STATE_POSTMIGRATE, RUN_STATE_RUNNING },
+ { RUN_STATE_POSTMIGRATE, RUN_STATE_FINISH_MIGRATE },
+
+ { RUN_STATE_PRELAUNCH, RUN_STATE_RUNNING },
+ { RUN_STATE_PRELAUNCH, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_PRELAUNCH, RUN_STATE_INMIGRATE },
+
+ { RUN_STATE_FINISH_MIGRATE, RUN_STATE_RUNNING },
+ { RUN_STATE_FINISH_MIGRATE, RUN_STATE_POSTMIGRATE },
+
+ { RUN_STATE_RESTORE_VM, RUN_STATE_RUNNING },
+
+ { RUN_STATE_RUNNING, RUN_STATE_DEBUG },
+ { RUN_STATE_RUNNING, RUN_STATE_INTERNAL_ERROR },
+ { RUN_STATE_RUNNING, RUN_STATE_IO_ERROR },
+ { RUN_STATE_RUNNING, RUN_STATE_PAUSED },
+ { RUN_STATE_RUNNING, RUN_STATE_FINISH_MIGRATE },
+ { RUN_STATE_RUNNING, RUN_STATE_RESTORE_VM },
+ { RUN_STATE_RUNNING, RUN_STATE_SAVE_VM },
+ { RUN_STATE_RUNNING, RUN_STATE_SHUTDOWN },
+ { RUN_STATE_RUNNING, RUN_STATE_WATCHDOG },
+
+ { RUN_STATE_SAVE_VM, RUN_STATE_RUNNING },
+
+ { RUN_STATE_SHUTDOWN, RUN_STATE_PAUSED },
+ { RUN_STATE_SHUTDOWN, RUN_STATE_FINISH_MIGRATE },
+
+ { RUN_STATE_WATCHDOG, RUN_STATE_RUNNING },
+ { RUN_STATE_WATCHDOG, RUN_STATE_FINISH_MIGRATE },
+
+ { RUN_STATE_MAX, RUN_STATE_MAX },
+};
+
+static bool runstate_valid_transitions[RUN_STATE_MAX][RUN_STATE_MAX];
+
+bool runstate_check(RunState state)
+{
+ return current_run_state == state;
+}
+
+void runstate_init(void)
+{
+ const RunStateTransition *p;
+
+ memset(&runstate_valid_transitions, 0, sizeof(runstate_valid_transitions));
+
+ for (p = &runstate_transitions_def[0]; p->from != RUN_STATE_MAX; p++) {
+ runstate_valid_transitions[p->from][p->to] = true;
+ }
+}
+
+/* This function will abort() on invalid state transitions */
+void runstate_set(RunState new_state)
+{
+ assert(new_state < RUN_STATE_MAX);
+
+ if (!runstate_valid_transitions[current_run_state][new_state]) {
+ fprintf(stderr, "ERROR: invalid runstate transition: '%s' -> '%s'\n",
+ RunState_lookup[current_run_state],
+ RunState_lookup[new_state]);
+ abort();
+ }
+
+ current_run_state = new_state;
+}
+
+int runstate_is_running(void)
+{
+ return runstate_check(RUN_STATE_RUNNING);
+}
+
+StatusInfo *qmp_query_status(Error **errp)
+{
+ StatusInfo *info = g_malloc0(sizeof(*info));
+
+ info->running = runstate_is_running();
+ info->singlestep = singlestep;
+ info->status = current_run_state;
+
+ return info;
+}
+
/***********************************************************/
/* real time host monotonic timer */
if (rtc_date_offset == -1)
if (rtc_utc)
seconds = mktimegm(tm);
- else
- seconds = mktime(tm);
+ else {
+ struct tm tmp = *tm;
+ tmp.tm_isdst = -1; /* use timezone to figure it out */
+ seconds = mktime(&tmp);
+ }
else
seconds = mktimegm(tm) + rtc_date_offset;
/* Allowed boot devices are:
* a-b: floppy disk drives
* c-f: IDE disk drives
- * g-m: machine implementation dependant drives
+ * g-m: machine implementation dependent drives
* n-p: network devices
* It's up to each machine implementation to check if the given boot
* devices match the actual hardware implementation and firmware
} else if (devpath) {
bootpath = devpath;
} else {
+ assert(i->suffix);
bootpath = g_strdup(i->suffix);
- assert(bootpath);
}
if (total) {
node_mem[nodenr] = 0;
} else {
int64_t sval;
- sval = strtosz(option, NULL);
- if (sval < 0) {
+ sval = strtosz(option, &endptr);
+ if (sval < 0 || *endptr) {
fprintf(stderr, "qemu: invalid numa mem size: %s\n", optarg);
exit(1);
}
DisplayState *ds = opaque;
DisplayChangeListener *dcl = ds->listeners;
- qemu_flush_coalesced_mmio_buffer();
dpy_refresh(ds);
while (dcl != NULL) {
qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock));
}
-static void nographic_update(void *opaque)
-{
- uint64_t interval = GUI_REFRESH_INTERVAL;
-
- qemu_flush_coalesced_mmio_buffer();
- qemu_mod_timer(nographic_timer, interval + qemu_get_clock_ms(rt_clock));
-}
-
struct vm_change_state_entry {
VMChangeStateHandler *cb;
void *opaque;
g_free (e);
}
-void vm_state_notify(int running, int reason)
+void vm_state_notify(int running, RunState state)
{
VMChangeStateEntry *e;
- trace_vm_state_notify(running, reason);
+ trace_vm_state_notify(running, state);
for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) {
- e->cb(e->opaque, running, reason);
+ e->cb(e->opaque, running, state);
}
}
void vm_start(void)
{
- if (!vm_running) {
+ if (!runstate_is_running()) {
cpu_enable_ticks();
- vm_running = 1;
- vm_state_notify(1, 0);
+ runstate_set(RUN_STATE_RUNNING);
+ vm_state_notify(1, RUN_STATE_RUNNING);
resume_all_vcpus();
monitor_protocol_event(QEVENT_RESUME, NULL);
}
static pid_t shutdown_pid;
static int powerdown_requested;
static int debug_requested;
-static int vmstop_requested;
+static RunState vmstop_requested = RUN_STATE_MAX;
int qemu_shutdown_requested_get(void)
{
return r;
}
-static int qemu_vmstop_requested(void)
+/* We use RUN_STATE_MAX but any invalid value will do */
+static bool qemu_vmstop_requested(RunState *r)
{
- int r = vmstop_requested;
- vmstop_requested = 0;
- return r;
+ if (vmstop_requested < RUN_STATE_MAX) {
+ *r = vmstop_requested;
+ vmstop_requested = RUN_STATE_MAX;
+ return true;
+ }
+
+ return false;
}
void qemu_register_reset(QEMUResetHandler *func, void *opaque)
{
shutdown_signal = signal;
shutdown_pid = pid;
+ no_shutdown = 0;
qemu_system_shutdown_request();
}
qemu_notify_event();
}
-void qemu_system_vmstop_request(int reason)
+void qemu_system_vmstop_request(RunState state)
{
- vmstop_requested = reason;
+ vmstop_requested = state;
qemu_notify_event();
}
-int main_loop_wait(int nonblocking)
-{
- fd_set rfds, wfds, xfds;
- int ret, nfds;
- struct timeval tv;
- int timeout;
+qemu_irq qemu_system_powerdown;
- if (nonblocking)
- timeout = 0;
- else {
- timeout = qemu_calculate_timeout();
- qemu_bh_update_timeout(&timeout);
+static bool main_loop_should_exit(void)
+{
+ RunState r;
+ if (qemu_debug_requested()) {
+ vm_stop(RUN_STATE_DEBUG);
}
-
- os_host_main_loop_wait(&timeout);
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
-
- /* poll any events */
- /* XXX: separate device handlers from system ones */
- nfds = -1;
- FD_ZERO(&rfds);
- FD_ZERO(&wfds);
- FD_ZERO(&xfds);
- qemu_iohandler_fill(&nfds, &rfds, &wfds, &xfds);
- slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
-
- if (timeout > 0) {
- qemu_mutex_unlock_iothread();
+ if (qemu_shutdown_requested()) {
+ qemu_kill_report();
+ monitor_protocol_event(QEVENT_SHUTDOWN, NULL);
+ if (no_shutdown) {
+ vm_stop(RUN_STATE_SHUTDOWN);
+ } else {
+ return true;
+ }
}
-
- ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
-
- if (timeout > 0) {
- qemu_mutex_lock_iothread();
+ if (qemu_reset_requested()) {
+ pause_all_vcpus();
+ cpu_synchronize_all_states();
+ qemu_system_reset(VMRESET_REPORT);
+ resume_all_vcpus();
+ if (runstate_check(RUN_STATE_INTERNAL_ERROR) ||
+ runstate_check(RUN_STATE_SHUTDOWN)) {
+ runstate_set(RUN_STATE_PAUSED);
+ }
}
-
- qemu_iohandler_poll(&rfds, &wfds, &xfds, ret);
- slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));
-
- qemu_run_all_timers();
-
- /* Check bottom-halves last in case any of the earlier events triggered
- them. */
- qemu_bh_poll();
-
- return ret;
-}
-
-#ifndef CONFIG_IOTHREAD
-static int vm_request_pending(void)
-{
- return powerdown_requested ||
- reset_requested ||
- shutdown_requested ||
- debug_requested ||
- vmstop_requested;
+ if (qemu_powerdown_requested()) {
+ monitor_protocol_event(QEVENT_POWERDOWN, NULL);
+ qemu_irq_raise(qemu_system_powerdown);
+ }
+ if (qemu_vmstop_requested(&r)) {
+ vm_stop(r);
+ }
+ return false;
}
-#endif
-
-qemu_irq qemu_system_powerdown;
static void main_loop(void)
{
bool nonblocking;
- int last_io __attribute__ ((unused)) = 0;
+ int last_io = 0;
#ifdef CONFIG_PROFILER
int64_t ti;
#endif
- int r;
-
- qemu_main_loop_start();
-
- for (;;) {
-#ifdef CONFIG_IOTHREAD
+ do {
nonblocking = !kvm_enabled() && last_io > 0;
-#else
- nonblocking = cpu_exec_all();
- if (vm_request_pending()) {
- nonblocking = true;
- }
-#endif
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
#ifdef CONFIG_PROFILER
dev_time += profile_getclock() - ti;
#endif
-
- if (qemu_debug_requested()) {
- vm_stop(VMSTOP_DEBUG);
- }
- if (qemu_shutdown_requested()) {
- qemu_kill_report();
- monitor_protocol_event(QEVENT_SHUTDOWN, NULL);
- if (no_shutdown) {
- vm_stop(VMSTOP_SHUTDOWN);
- } else
- break;
- }
- if (qemu_reset_requested()) {
- pause_all_vcpus();
- cpu_synchronize_all_states();
- qemu_system_reset(VMRESET_REPORT);
- resume_all_vcpus();
- }
- if (qemu_powerdown_requested()) {
- monitor_protocol_event(QEVENT_POWERDOWN, NULL);
- qemu_irq_raise(qemu_system_powerdown);
- }
- if ((r = qemu_vmstop_requested())) {
- vm_stop(r);
- }
- }
- bdrv_close_all();
- pause_all_vcpus();
+ } while (!main_loop_should_exit());
}
static void version(void)
static void help(int exitcode)
{
- const char *options_help =
-#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \
- opt_help
-#define DEFHEADING(text) stringify(text) "\n"
-#include "qemu-options.def"
-#undef DEF
-#undef DEFHEADING
-#undef GEN_DOCS
- ;
version();
- printf("usage: %s [options] [disk_image]\n"
- "\n"
- "'disk_image' is a raw hard disk image for IDE hard disk 0\n"
- "\n"
- "%s\n"
- "During emulation, the following keys are useful:\n"
+ printf("usage: %s [options] [disk_image]\n\n"
+ "'disk_image' is a raw hard disk image for IDE hard disk 0\n\n",
+ error_get_progname());
+
+#define QEMU_OPTIONS_GENERATE_HELP
+#include "qemu-options-wrapper.h"
+
+ printf("\nDuring emulation, the following keys are useful:\n"
"ctrl-alt-f toggle full screen\n"
"ctrl-alt-n switch to virtual console 'n'\n"
"ctrl-alt toggle mouse and keyboard grab\n"
"\n"
- "When using -nographic, press 'ctrl-a h' to get some help.\n",
- "qemu",
- options_help);
+ "When using -nographic, press 'ctrl-a h' to get some help.\n");
+
exit(exitcode);
}
static const QEMUOption qemu_options[] = {
{ "h", 0, QEMU_OPTION_h, QEMU_ARCH_ALL },
-#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \
- { option, opt_arg, opt_enum, arch_mask },
-#define DEFHEADING(text)
-#include "qemu-options.def"
-#undef DEF
-#undef DEFHEADING
-#undef GEN_DOCS
+#define QEMU_OPTIONS_GENERATE_OPTIONS
+#include "qemu-options-wrapper.h"
{ NULL },
};
static void select_vgahw (const char *p)
static gpointer malloc_and_trace(gsize n_bytes)
{
void *ptr = malloc(n_bytes);
- trace_qemu_malloc(n_bytes, ptr);
+ trace_g_malloc(n_bytes, ptr);
return ptr;
}
static gpointer realloc_and_trace(gpointer mem, gsize n_bytes)
{
void *ptr = realloc(mem, n_bytes);
- trace_qemu_realloc(mem, n_bytes, ptr);
+ trace_g_realloc(mem, n_bytes, ptr);
return ptr;
}
static void free_and_trace(gpointer mem)
{
- trace_qemu_free(mem);
+ trace_g_free(mem);
free(mem);
}
int show_vnc_port = 0;
#endif
int defconfig = 1;
- const char *trace_file = NULL;
const char *log_mask = NULL;
const char *log_file = NULL;
GMemVTable mem_trace = {
.realloc = realloc_and_trace,
.free = free_and_trace,
};
+ const char *trace_events = NULL;
+ const char *trace_file = NULL;
atexit(qemu_run_exit_notifiers);
error_set_progname(argv[0]);
g_mem_set_vtable(&mem_trace);
+ if (!g_thread_supported()) {
+ g_thread_init(NULL);
+ }
+
+ runstate_init();
init_clocks();
+ rtc_clock = host_clock;
qemu_cache_utils_init(envp);
break;
case QEMU_OPTION_m: {
int64_t value;
+ char *end;
- value = strtosz(optarg, NULL);
- if (value < 0) {
+ value = strtosz(optarg, &end);
+ if (value < 0 || *end) {
fprintf(stderr, "qemu: invalid ram size: %s\n", optarg);
exit(1);
}
case QEMU_OPTION_virtfs: {
QemuOpts *fsdev;
QemuOpts *device;
+ const char *writeout;
olist = qemu_find_opts("virtfs");
if (!olist) {
exit(1);
}
- if (qemu_opt_get(opts, "fstype") == NULL ||
+ if (qemu_opt_get(opts, "fsdriver") == NULL ||
qemu_opt_get(opts, "mount_tag") == NULL ||
- qemu_opt_get(opts, "path") == NULL ||
- qemu_opt_get(opts, "security_model") == NULL) {
- fprintf(stderr, "Usage: -virtfs fstype,path=/share_path/,"
- "security_model=[mapped|passthrough|none],"
+ qemu_opt_get(opts, "path") == NULL) {
+ fprintf(stderr, "Usage: -virtfs fsdriver,path=/share_path/,"
+ "[security_model={mapped|passthrough|none}],"
"mount_tag=tag.\n");
exit(1);
}
-
fsdev = qemu_opts_create(qemu_find_opts("fsdev"),
qemu_opt_get(opts, "mount_tag"), 1);
if (!fsdev) {
qemu_opt_get(opts, "mount_tag"));
exit(1);
}
- qemu_opt_set(fsdev, "fstype", qemu_opt_get(opts, "fstype"));
+
+ writeout = qemu_opt_get(opts, "writeout");
+ if (writeout) {
+#ifdef CONFIG_SYNC_FILE_RANGE
+ qemu_opt_set(fsdev, "writeout", writeout);
+#else
+ fprintf(stderr, "writeout=immediate not supported on "
+ "this platform\n");
+ exit(1);
+#endif
+ }
+ qemu_opt_set(fsdev, "fsdriver", qemu_opt_get(opts, "fsdriver"));
qemu_opt_set(fsdev, "path", qemu_opt_get(opts, "path"));
qemu_opt_set(fsdev, "security_model",
qemu_opt_get(opts, "security_model"));
+ qemu_opt_set_bool(fsdev, "readonly",
+ qemu_opt_get_bool(opts, "readonly", 0));
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
qemu_opt_set(device, "driver", "virtio-9p-pci");
qemu_opt_set(device, "fsdev",
qemu_opt_get(opts, "mount_tag"));
break;
}
+ case QEMU_OPTION_virtfs_synth: {
+ QemuOpts *fsdev;
+ QemuOpts *device;
+
+ fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", 1);
+ if (!fsdev) {
+ fprintf(stderr, "duplicate option: %s\n", "virtfs_synth");
+ exit(1);
+ }
+ qemu_opt_set(fsdev, "fsdriver", "synth");
+ qemu_opt_set(fsdev, "path", "/"); /* ignored */
+
+ device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
+ qemu_opt_set(device, "driver", "virtio-9p-pci");
+ qemu_opt_set(device, "fsdev", "v_synth");
+ qemu_opt_set(device, "mount_tag", "v_synth");
+ break;
+ }
case QEMU_OPTION_serial:
add_device_config(DEV_SERIAL, optarg);
default_serial = 0;
break;
case QEMU_OPTION_incoming:
incoming = optarg;
- incoming_expected = true;
break;
case QEMU_OPTION_nodefaults:
default_serial = 0;
}
xen_mode = XEN_ATTACH;
break;
-#ifdef CONFIG_TRACE_SIMPLE
case QEMU_OPTION_trace:
+ {
opts = qemu_opts_parse(qemu_find_opts("trace"), optarg, 0);
- if (opts) {
- trace_file = qemu_opt_get(opts, "file");
+ if (!opts) {
+ exit(1);
}
+ trace_events = qemu_opt_get(opts, "events");
+ trace_file = qemu_opt_get(opts, "file");
break;
-#endif
+ }
case QEMU_OPTION_readconfig:
{
int ret = qemu_read_config_file(optarg);
set_cpu_log(log_mask);
}
- if (!st_init(trace_file)) {
- fprintf(stderr, "warning: unable to initialize simple trace backend\n");
+ if (!trace_backend_init(trace_events, trace_file)) {
+ exit(1);
}
/* If no data_dir is specified then try to find it relative to the
if (!data_dir) {
data_dir = os_find_datadir(argv[0]);
}
- /* If all else fails use the install patch specified when building. */
+ /* If all else fails use the install path specified when building. */
if (!data_dir) {
data_dir = CONFIG_QEMU_DATADIR;
}
+ if (machine == NULL) {
+ fprintf(stderr, "No machine found.\n");
+ exit(1);
+ }
+
/*
* Default to max_cpus = smp_cpus, in case the user doesn't
* specify a max_cpus value.
configure_accelerator();
+ qemu_init_cpu_loop();
if (qemu_init_main_loop()) {
fprintf(stderr, "qemu_init_main_loop failed\n");
exit(1);
fprintf(stderr, "could not initialize alarm timer\n");
exit(1);
}
+
+ if (icount_option && (kvm_enabled() || xen_enabled())) {
+ fprintf(stderr, "-icount is not allowed with kvm or xen\n");
+ exit(1);
+ }
configure_icount(icount_option);
if (net_init_clients() < 0) {
}
qemu_add_globals();
+ qdev_machine_init();
+
machine->init(ram_size, boot_devices,
kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
}
dcl = dcl->next;
}
- if (ds->gui_timer == NULL) {
- nographic_timer = qemu_new_timer_ms(rt_clock, nographic_update, NULL);
- qemu_mod_timer(nographic_timer, qemu_get_clock_ms(rt_clock));
- }
text_consoles_set_display(ds);
if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) {
}
if (incoming) {
+ runstate_set(RUN_STATE_INMIGRATE);
int ret = qemu_start_incoming_migration(incoming);
if (ret < 0) {
fprintf(stderr, "Migration failed. Exit code %s(%d), exiting.\n",
os_setup_post();
+ resume_all_vcpus();
main_loop();
- quit_timers();
+ bdrv_close_all();
+ pause_all_vcpus();
net_cleanup();
res_free();