#include "config-host.h"
#ifndef _WIN32
+#include <libgen.h>
#include <pwd.h>
#include <sys/times.h>
#include <sys/wait.h>
#endif
#ifdef CONFIG_SDL
-#ifdef __APPLE__
-#include <SDL/SDL.h>
+#if defined(__APPLE__) || defined(main)
+#include <SDL.h>
int qemu_main(int argc, char **argv, char **envp);
int main(int argc, char **argv)
{
- qemu_main(argc, argv, NULL);
+ return qemu_main(argc, argv, NULL);
}
#undef main
#define main qemu_main
#include "hw/isa.h"
#include "hw/baum.h"
#include "hw/bt.h"
+#include "hw/watchdog.h"
#include "hw/smbios.h"
#include "hw/xen.h"
#include "bt-host.h"
#include "migration.h"
#include "kvm.h"
#include "balloon.h"
+#include "qemu-option.h"
#include "disas.h"
#include "qemu_socket.h"
-#if defined(CONFIG_SLIRP)
-#include "libslirp.h"
-#endif
+#include "slirp/libslirp.h"
//#define DEBUG_UNUSED_IOPORT
//#define DEBUG_IOPORT
/* XXX: use a two level table to limit memory usage */
#define MAX_IOPORTS 65536
-const char *bios_dir = CONFIG_QEMU_SHAREDIR;
+static const char *data_dir;
const char *bios_name = NULL;
static void *ioport_opaque[MAX_IOPORTS];
static IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS];
to store the VM snapshots */
DriveInfo drives_table[MAX_DRIVES+1];
int nb_drives;
-static int vga_ram_size;
enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
static DisplayState *display_state;
-int nographic;
-static int curses;
-static int sdl;
+DisplayType display_type = DT_DEFAULT;
const char* keyboard_layout = NULL;
int64_t ticks_per_sec;
ram_addr_t ram_size;
const char *vnc_display;
int acpi_enabled = 1;
int no_hpet = 0;
+int no_virtio_balloon = 0;
int fd_bootchk = 1;
int no_reboot = 0;
int no_shutdown = 0;
#ifndef _WIN32
int daemonize = 0;
#endif
+WatchdogTimerModel *watchdog = NULL;
+int watchdog_action = WDT_RESET;
const char *option_rom[MAX_OPTION_ROMS];
int nb_option_roms;
int semihosting_enabled = 0;
static inline int alarm_has_dynticks(struct qemu_alarm_timer *t)
{
- return t->flags & ALARM_FLAG_DYNTICKS;
+ return t && (t->flags & ALARM_FLAG_DYNTICKS);
}
static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t)
struct qemu_alarm_win32 {
MMRESULT timerId;
unsigned int period;
-} alarm_win32_data = {0, NULL, -1};
+} alarm_win32_data = {0, -1};
static int win32_start_timer(struct qemu_alarm_timer *t);
static void win32_stop_timer(struct qemu_alarm_timer *t);
qemu_get_clock(vm_clock))) ||
qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
qemu_get_clock(rt_clock))) {
- CPUState *env = next_cpu;
-
qemu_event_increment();
- alarm_timer->flags |= ALARM_FLAG_EXPIRED;
+ if (alarm_timer) alarm_timer->flags |= ALARM_FLAG_EXPIRED;
- if (env) {
+#ifndef CONFIG_IOTHREAD
+ if (next_cpu) {
/* stop the currently executing cpu because a timer occured */
- cpu_exit(env);
+ cpu_exit(next_cpu);
#ifdef CONFIG_KQEMU
- if (env->kqemu_enabled) {
- kqemu_cpu_interrupt(env);
+ if (next_cpu->kqemu_enabled) {
+ kqemu_cpu_interrupt(next_cpu);
}
#endif
}
+#endif
timer_alarm_pending = 1;
qemu_notify_event();
}
sigaction(SIGALRM, &act, NULL);
+ /*
+ * Initialize ev struct to 0 to avoid valgrind complaining
+ * about uninitialized data in timer_create call
+ */
+ memset(&ev, 0, sizeof(ev));
ev.sigev_value.sival_int = 0;
ev.sigev_notify = SIGEV_SIGNAL;
ev.sigev_signo = SIGALRM;
}
#endif
-const char *get_opt_name(char *buf, int buf_size, const char *p, char delim)
-{
- char *q;
-
- q = buf;
- while (*p != '\0' && *p != delim) {
- if (q && (q - buf) < buf_size - 1)
- *q++ = *p;
- p++;
- }
- if (q)
- *q = '\0';
-
- return p;
-}
-
-const char *get_opt_value(char *buf, int buf_size, const char *p)
-{
- char *q;
-
- q = buf;
- while (*p != '\0') {
- if (*p == ',') {
- if (*(p + 1) != ',')
- break;
- p++;
- }
- if (q && (q - buf) < buf_size - 1)
- *q++ = *p;
- p++;
- }
- if (q)
- *q = '\0';
-
- return p;
-}
-
-int get_param_value(char *buf, int buf_size,
- const char *tag, const char *str)
+int get_next_param_value(char *buf, int buf_size,
+ const char *tag, const char **pstr)
{
const char *p;
char option[128];
- p = str;
+ p = *pstr;
for(;;) {
p = get_opt_name(option, sizeof(option), p, '=');
if (*p != '=')
break;
p++;
if (!strcmp(tag, option)) {
- (void)get_opt_value(buf, buf_size, p);
+ *pstr = get_opt_value(buf, buf_size, p);
+ if (**pstr == ',') {
+ (*pstr)++;
+ }
return strlen(buf);
} else {
p = get_opt_value(NULL, 0, p);
return 0;
}
+int get_param_value(char *buf, int buf_size,
+ const char *tag, const char *str)
+{
+ return get_next_param_value(buf, buf_size, tag, &str);
+}
+
int check_params(char *buf, int buf_size,
const char * const *params, const char *str)
{
p = str;
while (*p != '\0') {
p = get_opt_name(buf, buf_size, p, '=');
- if (*p != '=')
+ if (*p != '=') {
return -1;
+ }
p++;
- for(i = 0; params[i] != NULL; i++)
- if (!strcmp(params[i], buf))
+ for (i = 0; params[i] != NULL; i++) {
+ if (!strcmp(params[i], buf)) {
break;
- if (params[i] == NULL)
+ }
+ }
+ if (params[i] == NULL) {
return -1;
+ }
p = get_opt_value(NULL, 0, p);
- if (*p != ',')
+ if (*p != ',') {
break;
+ }
p++;
}
return 0;
int index;
int cache;
int bdrv_flags, onerror;
+ const char *devaddr;
int drives_table_idx;
char *str = arg->opt;
static const char * const params[] = { "bus", "unit", "if", "index",
"cyls", "heads", "secs", "trans",
"media", "snapshot", "file",
- "cache", "format", "serial", "werror",
+ "cache", "format", "serial",
+ "werror", "addr",
NULL };
if (check_params(buf, sizeof(buf), params, str) < 0) {
}
}
+ devaddr = NULL;
+ if (get_param_value(buf, sizeof(buf), "addr", str)) {
+ if (type != IF_VIRTIO) {
+ fprintf(stderr, "addr is not supported by in '%s'\n", str);
+ return -1;
+ }
+ devaddr = strdup(buf);
+ }
+
/* compute bus and unit according index */
if (index != -1) {
bdrv = bdrv_new(buf);
drives_table_idx = drive_get_free_idx();
drives_table[drives_table_idx].bdrv = bdrv;
+ drives_table[drives_table_idx].devaddr = devaddr;
drives_table[drives_table_idx].type = type;
drives_table[drives_table_idx].bus = bus_id;
drives_table[drives_table_idx].unit = unit_id;
drives_table[drives_table_idx].onerror = onerror;
drives_table[drives_table_idx].drive_opt_idx = arg - drives_opt;
- strncpy(drives_table[nb_drives].serial, serial, sizeof(serial));
+ strncpy(drives_table[drives_table_idx].serial, serial, sizeof(serial));
nb_drives++;
switch(type) {
case IF_MTD:
case IF_VIRTIO:
break;
+ case IF_COUNT:
+ abort();
}
if (!file[0])
return -2;
} else if (strstart(devname, "net:", &p)) {
int nic = nb_nics;
- if (net_client_init("nic", p) < 0)
+ if (net_client_init(NULL, "nic", p) < 0)
return -1;
nd_table[nic].model = "usb";
dev = usb_net_init(&nd_table[nic]);
/* PCMCIA/Cardbus */
static struct pcmcia_socket_entry_s {
- struct pcmcia_socket_s *socket;
+ PCMCIASocket *socket;
struct pcmcia_socket_entry_s *next;
} *pcmcia_sockets = 0;
-void pcmcia_socket_register(struct pcmcia_socket_s *socket)
+void pcmcia_socket_register(PCMCIASocket *socket)
{
struct pcmcia_socket_entry_s *entry;
pcmcia_sockets = entry;
}
-void pcmcia_socket_unregister(struct pcmcia_socket_s *socket)
+void pcmcia_socket_unregister(PCMCIASocket *socket)
{
struct pcmcia_socket_entry_s *entry, **ptr;
return found;
}
-static ram_addr_t ram_save_threshold = 10;
+static uint64_t bytes_transferred = 0;
static ram_addr_t ram_save_remaining(void)
{
return count;
}
+uint64_t ram_bytes_remaining(void)
+{
+ return ram_save_remaining() * TARGET_PAGE_SIZE;
+}
+
+uint64_t ram_bytes_transferred(void)
+{
+ return bytes_transferred;
+}
+
+uint64_t ram_bytes_total(void)
+{
+ return last_ram_offset;
+}
+
static int ram_save_live(QEMUFile *f, int stage, void *opaque)
{
ram_addr_t addr;
+ uint64_t bytes_transferred_last;
+ double bwidth = 0;
+ uint64_t expected_time = 0;
+
+ if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) {
+ qemu_file_set_error(f);
+ return 0;
+ }
if (stage == 1) {
/* Make sure all dirty bits are set */
if (!cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG))
cpu_physical_memory_set_dirty(addr);
}
-
+
/* Enable dirty memory tracking */
cpu_physical_memory_set_dirty_tracking(1);
qemu_put_be64(f, last_ram_offset | RAM_SAVE_FLAG_MEM_SIZE);
}
+ bytes_transferred_last = bytes_transferred;
+ bwidth = get_clock();
+
while (!qemu_file_rate_limit(f)) {
int ret;
ret = ram_save_block(f);
+ bytes_transferred += ret * TARGET_PAGE_SIZE;
if (ret == 0) /* no more blocks */
break;
}
+ bwidth = get_clock() - bwidth;
+ bwidth = (bytes_transferred - bytes_transferred_last) / bwidth;
+
+ /* if we haven't transferred anything this round, force expected_time to a
+ * a very high value, but without crashing */
+ if (bwidth == 0)
+ bwidth = 0.000001;
+
/* try transferring iterative blocks of memory */
if (stage == 3) {
/* flush all remaining blocks regardless of rate limiting */
- while (ram_save_block(f) != 0);
+ while (ram_save_block(f) != 0) {
+ bytes_transferred += TARGET_PAGE_SIZE;
+ }
cpu_physical_memory_set_dirty_tracking(0);
}
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
- return (stage == 2) && (ram_save_remaining() < ram_save_threshold);
+ expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
+
+ return (stage == 2) && (expected_time <= migrate_max_downtime());
}
static int ram_load_dead(QEMUFile *f, void *opaque)
if (flags & RAM_SAVE_FLAG_COMPRESS) {
uint8_t ch = qemu_get_byte(f);
memset(qemu_get_ram_ptr(addr), ch, TARGET_PAGE_SIZE);
+#ifndef _WIN32
+ if (ch == 0 &&
+ (!kvm_enabled() || kvm_has_sync_mmu())) {
+ madvise(qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE, MADV_DONTNEED);
+ }
+#endif
} else if (flags & RAM_SAVE_FLAG_PAGE)
qemu_get_buffer(f, qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE);
} while (!(flags & RAM_SAVE_FLAG_EOS));
return NULL;
}
+static QEMUMachine *find_default_machine(void)
+{
+ QEMUMachine *m;
+
+ for(m = first_machine; m != NULL; m = m->next) {
+ if (m->is_default) {
+ return m;
+ }
+ }
+ return NULL;
+}
+
/***********************************************************/
/* main execution loop */
}
}
+static void resume_all_vcpus(void);
+static void pause_all_vcpus(void);
+
void vm_start(void)
{
if (!vm_running) {
vm_running = 1;
vm_state_notify(1, 0);
qemu_rearm_alarm_timer(alarm_timer);
- }
-}
-
-void vm_stop(int reason)
-{
- if (vm_running) {
- cpu_disable_ticks();
- vm_running = 0;
- vm_state_notify(0, reason);
+ resume_all_vcpus();
}
}
typedef struct QEMUResetEntry {
QEMUResetHandler *func;
void *opaque;
+ int order;
struct QEMUResetEntry *next;
} QEMUResetEntry;
static int shutdown_requested;
static int powerdown_requested;
static int debug_requested;
+static int vmstop_requested;
int qemu_shutdown_requested(void)
{
return r;
}
-void qemu_register_reset(QEMUResetHandler *func, void *opaque)
+static int qemu_vmstop_requested(void)
+{
+ int r = vmstop_requested;
+ vmstop_requested = 0;
+ return r;
+}
+
+static void do_vm_stop(int reason)
+{
+ if (vm_running) {
+ cpu_disable_ticks();
+ vm_running = 0;
+ pause_all_vcpus();
+ vm_state_notify(0, reason);
+ }
+}
+
+void qemu_register_reset(QEMUResetHandler *func, int order, void *opaque)
{
QEMUResetEntry **pre, *re;
pre = &first_reset_entry;
- while (*pre != NULL)
+ while (*pre != NULL && (*pre)->order >= order) {
pre = &(*pre)->next;
+ }
re = qemu_mallocz(sizeof(QEMUResetEntry));
re->func = func;
re->opaque = opaque;
+ re->order = order;
re->next = NULL;
*pre = re;
}
for(re = first_reset_entry; re != NULL; re = re->next) {
re->func(re->opaque);
}
- if (kvm_enabled())
- kvm_sync_vcpus();
}
void qemu_system_reset_request(void)
qemu_notify_event();
}
-void qemu_notify_event(void)
+#ifdef CONFIG_IOTHREAD
+static void qemu_system_vmstop_request(int reason)
{
- CPUState *env = cpu_single_env;
-
- if (env) {
- cpu_exit(env);
-#ifdef USE_KQEMU
- if (env->kqemu_enabled)
- kqemu_cpu_interrupt(env);
-#endif
- }
+ vmstop_requested = reason;
+ qemu_notify_event();
}
+#endif
#ifndef _WIN32
static int io_thread_fd = -1;
(void *)(unsigned long)fds[0]);
io_thread_fd = fds[1];
+ return 0;
+
fail:
close(fds[0]);
close(fds[1]);
}
#endif
+static int cpu_can_run(CPUState *env)
+{
+ if (env->stop)
+ return 0;
+ if (env->stopped)
+ return 0;
+ return 1;
+}
+
+#ifndef CONFIG_IOTHREAD
static int qemu_init_main_loop(void)
{
return qemu_event_init();
return 1;
}
+static void resume_all_vcpus(void)
+{
+}
+
+static void pause_all_vcpus(void)
+{
+}
+
void qemu_cpu_kick(void *env)
{
return;
}
+void qemu_notify_event(void)
+{
+ CPUState *env = cpu_single_env;
+
+ if (env) {
+ cpu_exit(env);
+#ifdef USE_KQEMU
+ if (env->kqemu_enabled)
+ kqemu_cpu_interrupt(env);
+#endif
+ }
+}
+
#define qemu_mutex_lock_iothread() do { } while (0)
#define qemu_mutex_unlock_iothread() do { } while (0)
+void vm_stop(int reason)
+{
+ do_vm_stop(reason);
+}
+
+#else /* CONFIG_IOTHREAD */
+
+#include "qemu-thread.h"
+
+QemuMutex qemu_global_mutex;
+static QemuMutex qemu_fair_mutex;
+
+static QemuThread io_thread;
+
+static QemuThread *tcg_cpu_thread;
+static QemuCond *tcg_halt_cond;
+
+static int qemu_system_ready;
+/* cpu creation */
+static QemuCond qemu_cpu_cond;
+/* system init */
+static QemuCond qemu_system_cond;
+static QemuCond qemu_pause_cond;
+
+static void block_io_signals(void);
+static void unblock_io_signals(void);
+static int tcg_has_work(void);
+
+static int qemu_init_main_loop(void)
+{
+ int ret;
+
+ ret = qemu_event_init();
+ if (ret)
+ return ret;
+
+ qemu_cond_init(&qemu_pause_cond);
+ qemu_mutex_init(&qemu_fair_mutex);
+ qemu_mutex_init(&qemu_global_mutex);
+ qemu_mutex_lock(&qemu_global_mutex);
+
+ unblock_io_signals();
+ qemu_thread_self(&io_thread);
+
+ return 0;
+}
+
+static void qemu_wait_io_event(CPUState *env)
+{
+ while (!tcg_has_work())
+ qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000);
+
+ qemu_mutex_unlock(&qemu_global_mutex);
+
+ /*
+ * Users of qemu_global_mutex can be starved, having no chance
+ * to acquire it since this path will get to it first.
+ * So use another lock to provide fairness.
+ */
+ qemu_mutex_lock(&qemu_fair_mutex);
+ qemu_mutex_unlock(&qemu_fair_mutex);
+
+ qemu_mutex_lock(&qemu_global_mutex);
+ if (env->stop) {
+ env->stop = 0;
+ env->stopped = 1;
+ qemu_cond_signal(&qemu_pause_cond);
+ }
+}
+
+static int qemu_cpu_exec(CPUState *env);
+
+static void *kvm_cpu_thread_fn(void *arg)
+{
+ CPUState *env = arg;
+
+ block_io_signals();
+ qemu_thread_self(env->thread);
+
+ /* signal CPU creation */
+ qemu_mutex_lock(&qemu_global_mutex);
+ env->created = 1;
+ qemu_cond_signal(&qemu_cpu_cond);
+
+ /* and wait for machine initialization */
+ while (!qemu_system_ready)
+ qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100);
+
+ while (1) {
+ if (cpu_can_run(env))
+ qemu_cpu_exec(env);
+ qemu_wait_io_event(env);
+ }
+
+ return NULL;
+}
+
+static void tcg_cpu_exec(void);
+
+static void *tcg_cpu_thread_fn(void *arg)
+{
+ CPUState *env = arg;
+
+ block_io_signals();
+ qemu_thread_self(env->thread);
+
+ /* signal CPU creation */
+ qemu_mutex_lock(&qemu_global_mutex);
+ for (env = first_cpu; env != NULL; env = env->next_cpu)
+ env->created = 1;
+ qemu_cond_signal(&qemu_cpu_cond);
+
+ /* and wait for machine initialization */
+ while (!qemu_system_ready)
+ qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100);
+
+ while (1) {
+ tcg_cpu_exec();
+ qemu_wait_io_event(cur_cpu);
+ }
+
+ return NULL;
+}
+
+void qemu_cpu_kick(void *_env)
+{
+ CPUState *env = _env;
+ qemu_cond_broadcast(env->halt_cond);
+ if (kvm_enabled())
+ qemu_thread_signal(env->thread, SIGUSR1);
+}
+
+int qemu_cpu_self(void *env)
+{
+ return (cpu_single_env != NULL);
+}
+
+static void cpu_signal(int sig)
+{
+ if (cpu_single_env)
+ cpu_exit(cpu_single_env);
+}
+
+static void block_io_signals(void)
+{
+ sigset_t set;
+ struct sigaction sigact;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR2);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR1);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = cpu_signal;
+ sigaction(SIGUSR1, &sigact, NULL);
+}
+
+static void unblock_io_signals(void)
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR2);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR1);
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+}
+
+static void qemu_signal_lock(unsigned int msecs)
+{
+ qemu_mutex_lock(&qemu_fair_mutex);
+
+ while (qemu_mutex_trylock(&qemu_global_mutex)) {
+ qemu_thread_signal(tcg_cpu_thread, SIGUSR1);
+ if (!qemu_mutex_timedlock(&qemu_global_mutex, msecs))
+ break;
+ }
+ qemu_mutex_unlock(&qemu_fair_mutex);
+}
+
+static void qemu_mutex_lock_iothread(void)
+{
+ if (kvm_enabled()) {
+ qemu_mutex_lock(&qemu_fair_mutex);
+ qemu_mutex_lock(&qemu_global_mutex);
+ qemu_mutex_unlock(&qemu_fair_mutex);
+ } else
+ qemu_signal_lock(100);
+}
+
+static void qemu_mutex_unlock_iothread(void)
+{
+ qemu_mutex_unlock(&qemu_global_mutex);
+}
+
+static int all_vcpus_paused(void)
+{
+ CPUState *penv = first_cpu;
+
+ while (penv) {
+ if (!penv->stopped)
+ return 0;
+ penv = (CPUState *)penv->next_cpu;
+ }
+
+ return 1;
+}
+
+static void pause_all_vcpus(void)
+{
+ CPUState *penv = first_cpu;
+
+ while (penv) {
+ penv->stop = 1;
+ qemu_thread_signal(penv->thread, SIGUSR1);
+ qemu_cpu_kick(penv);
+ penv = (CPUState *)penv->next_cpu;
+ }
+
+ while (!all_vcpus_paused()) {
+ qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100);
+ penv = first_cpu;
+ while (penv) {
+ qemu_thread_signal(penv->thread, SIGUSR1);
+ penv = (CPUState *)penv->next_cpu;
+ }
+ }
+}
+
+static void resume_all_vcpus(void)
+{
+ CPUState *penv = first_cpu;
+
+ while (penv) {
+ penv->stop = 0;
+ penv->stopped = 0;
+ qemu_thread_signal(penv->thread, SIGUSR1);
+ qemu_cpu_kick(penv);
+ penv = (CPUState *)penv->next_cpu;
+ }
+}
+
+static void tcg_init_vcpu(void *_env)
+{
+ CPUState *env = _env;
+ /* share a single thread for all cpus with TCG */
+ if (!tcg_cpu_thread) {
+ env->thread = qemu_mallocz(sizeof(QemuThread));
+ env->halt_cond = qemu_mallocz(sizeof(QemuCond));
+ qemu_cond_init(env->halt_cond);
+ qemu_thread_create(env->thread, tcg_cpu_thread_fn, env);
+ while (env->created == 0)
+ qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100);
+ tcg_cpu_thread = env->thread;
+ tcg_halt_cond = env->halt_cond;
+ } else {
+ env->thread = tcg_cpu_thread;
+ env->halt_cond = tcg_halt_cond;
+ }
+}
+
+static void kvm_start_vcpu(CPUState *env)
+{
+ kvm_init_vcpu(env);
+ env->thread = qemu_mallocz(sizeof(QemuThread));
+ env->halt_cond = qemu_mallocz(sizeof(QemuCond));
+ qemu_cond_init(env->halt_cond);
+ qemu_thread_create(env->thread, kvm_cpu_thread_fn, env);
+ while (env->created == 0)
+ qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100);
+}
+
+void qemu_init_vcpu(void *_env)
+{
+ CPUState *env = _env;
+
+ if (kvm_enabled())
+ kvm_start_vcpu(env);
+ else
+ tcg_init_vcpu(env);
+}
+
+void qemu_notify_event(void)
+{
+ qemu_event_increment();
+}
+
+void vm_stop(int reason)
+{
+ QemuThread me;
+ qemu_thread_self(&me);
+
+ if (!qemu_thread_equal(&me, &io_thread)) {
+ qemu_system_vmstop_request(reason);
+ /*
+ * FIXME: should not return to device code in case
+ * vm_stop() has been requested.
+ */
+ if (cpu_single_env) {
+ cpu_exit(cpu_single_env);
+ cpu_single_env->stop = 1;
+ }
+ return;
+ }
+ do_vm_stop(reason);
+}
+
+#endif
+
+
#ifdef _WIN32
static void host_main_loop_wait(int *timeout)
{
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
-#if defined(CONFIG_SLIRP)
- if (slirp_is_inited()) {
- slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
- }
-#endif
+ slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
+
qemu_mutex_unlock_iothread();
ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
qemu_mutex_lock_iothread();
pioh = &ioh->next;
}
}
-#if defined(CONFIG_SLIRP)
- if (slirp_is_inited()) {
- if (ret < 0) {
- FD_ZERO(&rfds);
- FD_ZERO(&wfds);
- FD_ZERO(&xfds);
- }
- slirp_select_poll(&rfds, &wfds, &xfds);
- }
-#endif
+
+ slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));
/* rearm timer, if not periodic */
if (alarm_timer->flags & ALARM_FLAG_EXPIRED) {
}
/* vm time timers */
- if (vm_running && likely(!(cur_cpu->singlestep_enabled & SSTEP_NOTIMER)))
- qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL],
- qemu_get_clock(vm_clock));
+ if (vm_running) {
+ if (!cur_cpu || likely(!(cur_cpu->singlestep_enabled & SSTEP_NOTIMER)))
+ qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL],
+ qemu_get_clock(vm_clock));
+ }
/* real time timers */
qemu_run_timers(&active_timers[QEMU_TIMER_REALTIME],
static void tcg_cpu_exec(void)
{
- int ret;
+ int ret = 0;
if (next_cpu == NULL)
next_cpu = first_cpu;
timer_alarm_pending = 0;
break;
}
- ret = qemu_cpu_exec(env);
+ if (cpu_can_run(env))
+ ret = qemu_cpu_exec(env);
if (ret == EXCP_DEBUG) {
gdb_set_stop_cpu(env);
debug_requested = 1;
static int cpu_has_work(CPUState *env)
{
+ if (env->stop)
+ return 1;
+ if (env->stopped)
+ return 0;
if (!env->halted)
return 1;
if (qemu_cpu_has_work(env))
static int qemu_calculate_timeout(void)
{
+#ifndef CONFIG_IOTHREAD
int timeout;
if (!vm_running)
}
return timeout;
+#else /* CONFIG_IOTHREAD */
+ return 1000;
+#endif
}
static int vm_can_run(void)
static void main_loop(void)
{
- for (;;) {
+ int r;
+
+#ifdef CONFIG_IOTHREAD
+ qemu_system_ready = 1;
+ qemu_cond_broadcast(&qemu_system_cond);
+#endif
+ for (;;) {
do {
#ifdef CONFIG_PROFILER
int64_t ti;
#endif
+#ifndef CONFIG_IOTHREAD
tcg_cpu_exec();
+#endif
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
} else
break;
}
- if (qemu_reset_requested())
+ if (qemu_reset_requested()) {
+ pause_all_vcpus();
qemu_system_reset();
+ resume_all_vcpus();
+ }
if (qemu_powerdown_requested())
qemu_system_powerdown();
+ if ((r = qemu_vmstop_requested()))
+ vm_stop(r);
}
+ pause_all_vcpus();
}
static void version(void)
qemu_system_shutdown_request();
}
-static void termsig_setup(void)
+static void sigchld_handler(int signal)
+{
+ waitpid(-1, NULL, WNOHANG);
+}
+
+static void sighandler_setup(void)
{
struct sigaction act;
sigaction(SIGINT, &act, NULL);
sigaction(SIGHUP, &act, NULL);
sigaction(SIGTERM, &act, NULL);
+
+ act.sa_handler = sigchld_handler;
+ act.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &act, NULL);
}
#endif
+#ifdef _WIN32
+/* Look for support files in the same directory as the executable. */
+static char *find_datadir(const char *argv0)
+{
+ char *p;
+ char buf[MAX_PATH];
+ DWORD len;
+
+ len = GetModuleFileName(NULL, buf, sizeof(buf) - 1);
+ if (len == 0) {
+ return NULL;
+ }
+
+ buf[len] = 0;
+ p = buf + len - 1;
+ while (p != buf && *p != '\\')
+ p--;
+ *p = 0;
+ if (access(buf, R_OK) == 0) {
+ return qemu_strdup(buf);
+ }
+ return NULL;
+}
+#else /* !_WIN32 */
+
+/* Find a likely location for support files using the location of the binary.
+ For installed binaries this will be "$bindir/../share/qemu". When
+ running from the build tree this will be "$bindir/../pc-bios". */
+#define SHARE_SUFFIX "/share/qemu"
+#define BUILD_SUFFIX "/pc-bios"
+static char *find_datadir(const char *argv0)
+{
+ char *dir;
+ char *p = NULL;
+ char *res;
+#ifdef PATH_MAX
+ char buf[PATH_MAX];
+#endif
+ size_t max_len;
+
+#if defined(__linux__)
+ {
+ int len;
+ len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
+ if (len > 0) {
+ buf[len] = 0;
+ p = buf;
+ }
+ }
+#elif defined(__FreeBSD__)
+ {
+ int len;
+ len = readlink("/proc/curproc/file", buf, sizeof(buf) - 1);
+ if (len > 0) {
+ buf[len] = 0;
+ p = buf;
+ }
+ }
+#endif
+ /* If we don't have any way of figuring out the actual executable
+ location then try argv[0]. */
+ if (!p) {
+#ifdef PATH_MAX
+ p = buf;
+#endif
+ p = realpath(argv0, p);
+ if (!p) {
+ return NULL;
+ }
+ }
+ dir = dirname(p);
+ dir = dirname(dir);
+
+ max_len = strlen(dir) +
+ MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1;
+ res = qemu_mallocz(max_len);
+ snprintf(res, max_len, "%s%s", dir, SHARE_SUFFIX);
+ if (access(res, R_OK)) {
+ snprintf(res, max_len, "%s%s", dir, BUILD_SUFFIX);
+ if (access(res, R_OK)) {
+ qemu_free(res);
+ res = NULL;
+ }
+ }
+#ifndef PATH_MAX
+ free(p);
+#endif
+ return res;
+}
+#undef SHARE_SUFFIX
+#undef BUILD_SUFFIX
+#endif
+
+char *qemu_find_file(int type, const char *name)
+{
+ int len;
+ const char *subdir;
+ char *buf;
+
+ /* If name contains path separators then try it as a straight path. */
+ if ((strchr(name, '/') || strchr(name, '\\'))
+ && access(name, R_OK) == 0) {
+ return strdup(name);
+ }
+ switch (type) {
+ case QEMU_FILE_TYPE_BIOS:
+ subdir = "";
+ break;
+ case QEMU_FILE_TYPE_KEYMAP:
+ subdir = "keymaps/";
+ break;
+ default:
+ abort();
+ }
+ len = strlen(data_dir) + strlen(name) + strlen(subdir) + 2;
+ buf = qemu_mallocz(len);
+ snprintf(buf, len, "%s/%s%s", data_dir, subdir, name);
+ if (access(buf, R_OK)) {
+ qemu_free(buf);
+ return NULL;
+ }
+ return buf;
+}
+
int main(int argc, char **argv, char **envp)
{
-#ifdef CONFIG_GDBSTUB
const char *gdbstub_dev = NULL;
-#endif
uint32_t boot_devices_bitmap = 0;
int i;
int snapshot, linux_boot, net_boot;
const char *run_as = NULL;
#endif
CPUState *env;
+ int show_vnc_port = 0;
qemu_cache_utils_init(envp);
}
#endif
- register_machines();
- machine = first_machine;
+ module_call_init(MODULE_INIT_MACHINE);
+ machine = find_default_machine();
cpu_model = NULL;
initrd_filename = NULL;
ram_size = 0;
- vga_ram_size = VGA_RAM_SIZE;
snapshot = 0;
- nographic = 0;
- curses = 0;
kernel_filename = NULL;
kernel_cmdline = "";
cyls = heads = secs = 0;
tb_size = 0;
autostart= 1;
+ register_watchdogs();
+
optind = 1;
for(;;) {
if (optind >= argc)
for(m = first_machine; m != NULL; m = m->next) {
printf("%-10s %s%s\n",
m->name, m->desc,
- m == first_machine ? " (default)" : "");
+ m->is_default ? " (default)" : "");
}
exit(*optarg != '?');
}
numa_add(optarg);
break;
case QEMU_OPTION_nographic:
- nographic = 1;
+ display_type = DT_NOGRAPHIC;
break;
#ifdef CONFIG_CURSES
case QEMU_OPTION_curses:
- curses = 1;
+ display_type = DT_CURSES;
break;
#endif
case QEMU_OPTION_portrait:
break;
#ifdef CONFIG_SLIRP
case QEMU_OPTION_tftp:
- tftp_prefix = optarg;
+ legacy_tftp_prefix = optarg;
break;
case QEMU_OPTION_bootp:
- bootp_filename = optarg;
+ legacy_bootp_filename = optarg;
break;
#ifndef _WIN32
case QEMU_OPTION_smb:
- net_slirp_smb(optarg);
+ net_slirp_smb(optarg);
break;
#endif
case QEMU_OPTION_redir:
- net_slirp_redir(NULL, optarg);
+ net_slirp_redir(optarg);
break;
#endif
case QEMU_OPTION_bt:
cpu_set_log(mask);
}
break;
-#ifdef CONFIG_GDBSTUB
case QEMU_OPTION_s:
gdbstub_dev = "tcp::" DEFAULT_GDBSTUB_PORT;
break;
case QEMU_OPTION_gdb:
gdbstub_dev = optarg;
break;
-#endif
case QEMU_OPTION_L:
- bios_dir = optarg;
+ data_dir = optarg;
break;
case QEMU_OPTION_bios:
bios_name = optarg;
serial_devices[serial_device_index] = optarg;
serial_device_index++;
break;
+ case QEMU_OPTION_watchdog:
+ i = select_watchdog(optarg);
+ if (i > 0)
+ exit (i == 1 ? 1 : 0);
+ break;
+ case QEMU_OPTION_watchdog_action:
+ if (select_watchdog_action(optarg) == -1) {
+ fprintf(stderr, "Unknown -watchdog-action parameter\n");
+ exit(1);
+ }
+ break;
case QEMU_OPTION_virtiocon:
if (virtio_console_index >= MAX_VIRTIO_CONSOLES) {
fprintf(stderr, "qemu: too many virtio consoles\n");
no_quit = 1;
break;
case QEMU_OPTION_sdl:
- sdl = 1;
+ display_type = DT_SDL;
break;
#endif
case QEMU_OPTION_pidfile:
}
break;
case QEMU_OPTION_vnc:
+ display_type = DT_VNC;
vnc_display = optarg;
break;
#ifdef TARGET_I386
case QEMU_OPTION_no_hpet:
no_hpet = 1;
break;
+ case QEMU_OPTION_no_virtio_balloon:
+ no_virtio_balloon = 1;
+ break;
#endif
case QEMU_OPTION_no_reboot:
no_reboot = 1;
}
}
+ /* If no data_dir is specified then try to find it relative to the
+ executable path. */
+ if (!data_dir) {
+ data_dir = find_datadir(argv[0]);
+ }
+ /* If all else fails use the install patch specified when building. */
+ if (!data_dir) {
+ data_dir = CONFIG_QEMU_SHAREDIR;
+ }
+
#if defined(CONFIG_KVM) && defined(CONFIG_KQEMU)
if (kvm_allowed && kqemu_allowed) {
fprintf(stderr,
exit(1);
}
- if (nographic) {
+ if (display_type == DT_NOGRAPHIC) {
if (serial_device_index == 0)
serial_devices[0] = "stdio";
if (parallel_device_index == 0)
exit(1);
}
linux_boot = (kernel_filename != NULL);
- net_boot = (boot_devices_bitmap >> ('n' - 'a')) & 0xF;
if (!linux_boot && *kernel_cmdline != '\0') {
fprintf(stderr, "-append only allowed with -kernel option\n");
if (net_client_parse(net_clients[i]) < 0)
exit(1);
}
- net_client_check();
-#ifdef TARGET_I386
- /* XXX: this should be moved in the PC machine instantiation code */
- if (net_boot != 0) {
- int netroms = 0;
- for (i = 0; i < nb_nics && i < 4; i++) {
- const char *model = nd_table[i].model;
- char buf[1024];
- if (net_boot & (1 << i)) {
- if (model == NULL)
- model = "ne2k_pci";
- snprintf(buf, sizeof(buf), "%s/pxe-%s.bin", bios_dir, model);
- if (get_image_size(buf) > 0) {
- if (nb_option_roms >= MAX_OPTION_ROMS) {
- fprintf(stderr, "Too many option ROMs\n");
- exit(1);
- }
- option_rom[nb_option_roms] = strdup(buf);
- nb_option_roms++;
- netroms++;
- }
- }
- }
- if (netroms == 0) {
- fprintf(stderr, "No valid PXE rom found for network device\n");
- exit(1);
- }
- }
-#endif
+ net_boot = (boot_devices_bitmap >> ('n' - 'a')) & 0xF;
+ net_set_boot_mask(net_boot);
+
+ net_client_check();
/* init the bluetooth world */
for (i = 0; i < nb_bt_opts; i++)
/* FIXME: This is a nasty hack because kqemu can't cope with dynamic
guest ram allocation. It needs to go away. */
if (kqemu_allowed) {
- kqemu_phys_ram_size = ram_size + VGA_RAM_SIZE + 4 * 1024 * 1024;
+ kqemu_phys_ram_size = ram_size + 8 * 1024 * 1024 + 4 * 1024 * 1024;
kqemu_phys_ram_base = qemu_vmalloc(kqemu_phys_ram_size);
if (!kqemu_phys_ram_base) {
fprintf(stderr, "Could not allocate physical memory\n");
cpu_exec_init_all(tb_size * 1024 * 1024);
bdrv_init();
- dma_helper_init();
/* we always create the cdrom drive, even if no disk is there */
#ifndef _WIN32
/* must be after terminal init, SDL library changes signal handlers */
- termsig_setup();
+ sighandler_setup();
#endif
/* Maintain compatibility with multiple stdio monitors */
}
}
- machine->init(ram_size, vga_ram_size, boot_devices,
+ module_call_init(MODULE_INIT_DEVICE);
+
+ machine->init(ram_size, boot_devices,
kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
dumb_display_init();
/* just use the first displaystate for the moment */
ds = display_state;
- /* terminal init */
- if (nographic) {
- if (curses) {
- fprintf(stderr, "fatal: -nographic can't be used with -curses\n");
- exit(1);
- }
- } else {
+
+ if (display_type == DT_DEFAULT) {
+#if defined(CONFIG_SDL) || defined(CONFIG_COCOA)
+ display_type = DT_SDL;
+#else
+ display_type = DT_VNC;
+ vnc_display = "localhost:0,to=99";
+ show_vnc_port = 1;
+#endif
+ }
+
+
+ switch (display_type) {
+ case DT_NOGRAPHIC:
+ break;
#if defined(CONFIG_CURSES)
- if (curses) {
- /* At the moment curses cannot be used with other displays */
- curses_display_init(ds, full_screen);
- } else
+ case DT_CURSES:
+ curses_display_init(ds, full_screen);
+ break;
#endif
- {
- if (vnc_display != NULL) {
- vnc_display_init(ds);
- if (vnc_display_open(ds, vnc_display) < 0)
- exit(1);
- }
#if defined(CONFIG_SDL)
- if (sdl || !vnc_display)
- sdl_display_init(ds, full_screen, no_frame);
+ case DT_SDL:
+ sdl_display_init(ds, full_screen, no_frame);
+ break;
#elif defined(CONFIG_COCOA)
- if (sdl || !vnc_display)
- cocoa_display_init(ds, full_screen);
+ case DT_SDL:
+ cocoa_display_init(ds, full_screen);
+ break;
#endif
- }
+ case DT_VNC:
+ vnc_display_init(ds);
+ if (vnc_display_open(ds, vnc_display) < 0)
+ exit(1);
+
+ if (show_vnc_port) {
+ printf("VNC server running on `%s'\n", vnc_display_local_addr(ds));
+ }
+ break;
+ default:
+ break;
}
dpy_resize(ds);
dcl = dcl->next;
}
- if (nographic || (vnc_display && !sdl)) {
+ if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) {
nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL);
qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock));
}
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
const char *devname = serial_devices[i];
if (devname && strcmp(devname, "none")) {
- char label[32];
- snprintf(label, sizeof(label), "serial%d", i);
if (strstart(devname, "vc", 0))
qemu_chr_printf(serial_hds[i], "serial%d console\r\n", i);
}
for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
const char *devname = parallel_devices[i];
if (devname && strcmp(devname, "none")) {
- char label[32];
- snprintf(label, sizeof(label), "parallel%d", i);
if (strstart(devname, "vc", 0))
qemu_chr_printf(parallel_hds[i], "parallel%d console\r\n", i);
}
for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
const char *devname = virtio_consoles[i];
if (virtcon_hds[i] && devname) {
- char label[32];
- snprintf(label, sizeof(label), "virtcon%d", i);
if (strstart(devname, "vc", 0))
qemu_chr_printf(virtcon_hds[i], "virtio console%d\r\n", i);
}
}
-#ifdef CONFIG_GDBSTUB
if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) {
fprintf(stderr, "qemu: could not open gdbserver on device '%s'\n",
gdbstub_dev);
exit(1);
}
-#endif
if (loadvm)
do_loadvm(cur_mon, loadvm);