summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--Makefile.objs10
-rw-r--r--arch_init.c4
-rw-r--r--backends/Makefile.objs2
-rw-r--r--backends/testdev.c131
-rwxr-xr-xconfigure1
-rw-r--r--cpu-exec.c116
-rw-r--r--cpus.c118
-rw-r--r--hw/misc/vfio.c46
-rw-r--r--hw/timer/imx_epit.c16
-rw-r--r--hw/timer/imx_gpt.c32
-rw-r--r--hw/timer/tusb6010.c3
-rw-r--r--hw/virtio/vhost-backend.c2
-rw-r--r--include/hw/usb.h7
-rw-r--r--include/hw/virtio/vhost-backend.h2
-rw-r--r--include/qemu-common.h8
-rw-r--r--include/qemu/timer.h2
-rw-r--r--include/sysemu/char.h3
-rw-r--r--monitor.c1
-rw-r--r--po/zh_CN.po86
-rw-r--r--qapi-schema.json3
-rw-r--r--qemu-char.c4
-rw-r--r--qemu-img.c9
-rw-r--r--qemu-options.hx17
-rw-r--r--qga/commands-posix.c508
-rw-r--r--qga/commands-win32.c47
-rw-r--r--qga/guest-agent-core.h1
-rw-r--r--qga/main.c1
-rw-r--r--qga/qapi-schema.json96
-rw-r--r--qtest.c13
-rw-r--r--stubs/Makefile.objs1
-rw-r--r--stubs/chr-testdev.c7
-rw-r--r--target-i386/cpu.c10
-rw-r--r--target-mips/op_helper.c11
-rw-r--r--tests/Makefile2
-rw-r--r--vl.c39
36 files changed, 1262 insertions, 99 deletions
diff --git a/Makefile b/Makefile
index d6b9dc1ebe..c5751b879a 100644
--- a/Makefile
+++ b/Makefile
@@ -202,7 +202,7 @@ Makefile: $(version-obj-y) $(version-lobj-y)
# Build libraries
libqemustub.a: $(stub-obj-y)
-libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o qapi-event.o
+libqemuutil.a: $(util-obj-y)
block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL
util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
diff --git a/Makefile.objs b/Makefile.objs
index 1f76cea569..efd9768e86 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -1,7 +1,7 @@
#######################################################################
# Common libraries for tools and emulators
stub-obj-y = stubs/
-util-obj-y = util/ qobject/ qapi/ trace/
+util-obj-y = util/ qobject/ qapi/ trace/ qapi-types.o qapi-visit.o qapi-event.o
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
@@ -12,7 +12,6 @@ block-obj-y += main-loop.o iohandler.o qemu-timer.o
block-obj-$(CONFIG_POSIX) += aio-posix.o
block-obj-$(CONFIG_WIN32) += aio-win32.o
block-obj-y += block/
-block-obj-y += qapi-types.o qapi-visit.o qapi-event.o
block-obj-y += qemu-io-cmds.o
block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
@@ -88,11 +87,6 @@ common-obj-y += qmp-marshal.o
common-obj-y += qmp.o hmp.o
endif
-######################################################################
-# some qapi visitors are used by both system and user emulation:
-
-common-obj-y += qapi-visit.o qapi-types.o
-
#######################################################################
# Target-independent parts used in system and user emulation
common-obj-y += qemu-log.o
@@ -111,5 +105,5 @@ version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo
# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed
# by libqemuutil.a. These should be moved to a separate .json schema.
-qga-obj-y = qga/ qapi-types.o qapi-visit.o
+qga-obj-y = qga/
qga-vss-dll-obj-y = qga/
diff --git a/arch_init.c b/arch_init.c
index 8ddaf35191..28ece769d8 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -1072,8 +1072,8 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
if (!strncmp(id, block->idstr, sizeof(id))) {
if (block->length != length) {
- error_report("Length mismatch: %s: " RAM_ADDR_FMT
- " in != " RAM_ADDR_FMT, id, length,
+ error_report("Length mismatch: %s: 0x" RAM_ADDR_FMT
+ " in != 0x" RAM_ADDR_FMT, id, length,
block->length);
ret = -EINVAL;
}
diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 506a46c33b..31a3a894f5 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -1,7 +1,7 @@
common-obj-y += rng.o rng-egd.o
common-obj-$(CONFIG_POSIX) += rng-random.o
-common-obj-y += msmouse.o
+common-obj-y += msmouse.o testdev.o
common-obj-$(CONFIG_BRLAPI) += baum.o
baum.o-cflags := $(SDL_CFLAGS)
diff --git a/backends/testdev.c b/backends/testdev.c
new file mode 100644
index 0000000000..70d63b3b8d
--- /dev/null
+++ b/backends/testdev.c
@@ -0,0 +1,131 @@
+/*
+ * QEMU Char Device for testsuite control
+ *
+ * Copyright (c) 2014 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "sysemu/char.h"
+
+#define BUF_SIZE 32
+
+typedef struct {
+ CharDriverState *chr;
+ uint8_t in_buf[32];
+ int in_buf_used;
+} TestdevCharState;
+
+/* Try to interpret a whole incoming packet */
+static int testdev_eat_packet(TestdevCharState *testdev)
+{
+ const uint8_t *cur = testdev->in_buf;
+ int len = testdev->in_buf_used;
+ uint8_t c;
+ int arg;
+
+#define EAT(c) do { \
+ if (!len--) { \
+ return 0; \
+ } \
+ c = *cur++; \
+} while (0)
+
+ EAT(c);
+
+ while (isspace(c)) {
+ EAT(c);
+ }
+
+ arg = 0;
+ while (isdigit(c)) {
+ arg = arg * 10 + c - '0';
+ EAT(c);
+ }
+
+ while (isspace(c)) {
+ EAT(c);
+ }
+
+ switch (c) {
+ case 'q':
+ exit((arg << 1) | 1);
+ break;
+ default:
+ break;
+ }
+ return cur - testdev->in_buf;
+}
+
+/* The other end is writing some data. Store it and try to interpret */
+static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ TestdevCharState *testdev = chr->opaque;
+ int tocopy, eaten, orig_len = len;
+
+ while (len) {
+ /* Complete our buffer as much as possible */
+ tocopy = MIN(len, BUF_SIZE - testdev->in_buf_used);
+
+ memcpy(testdev->in_buf + testdev->in_buf_used, buf, tocopy);
+ testdev->in_buf_used += tocopy;
+ buf += tocopy;
+ len -= tocopy;
+
+ /* Interpret it as much as possible */
+ while (testdev->in_buf_used > 0 &&
+ (eaten = testdev_eat_packet(testdev)) > 0) {
+ memmove(testdev->in_buf, testdev->in_buf + eaten,
+ testdev->in_buf_used - eaten);
+ testdev->in_buf_used -= eaten;
+ }
+ }
+ return orig_len;
+}
+
+static void testdev_close(struct CharDriverState *chr)
+{
+ TestdevCharState *testdev = chr->opaque;
+
+ g_free(testdev);
+}
+
+CharDriverState *chr_testdev_init(void)
+{
+ TestdevCharState *testdev;
+ CharDriverState *chr;
+
+ testdev = g_malloc0(sizeof(TestdevCharState));
+ testdev->chr = chr = g_malloc0(sizeof(CharDriverState));
+
+ chr->opaque = testdev;
+ chr->chr_write = testdev_write;
+ chr->chr_close = testdev_close;
+
+ return chr;
+}
+
+static void register_types(void)
+{
+ register_char_driver_qapi("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL);
+}
+
+type_init(register_types);
diff --git a/configure b/configure
index 09e4f98322..283c71cb7a 100755
--- a/configure
+++ b/configure
@@ -1730,6 +1730,7 @@ fi
cat > $TMPC <<EOF
#include <sys/socket.h>
+#include <linux/ip.h>
int main(void) { return sizeof(struct mmsghdr); }
EOF
if compile_prog "" "" ; then
diff --git a/cpu-exec.c b/cpu-exec.c
index 38e5f02a30..cbc8067b37 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -22,6 +22,109 @@
#include "tcg.h"
#include "qemu/atomic.h"
#include "sysemu/qtest.h"
+#include "qemu/timer.h"
+
+/* -icount align implementation. */
+
+typedef struct SyncClocks {
+ int64_t diff_clk;
+ int64_t last_cpu_icount;
+ int64_t realtime_clock;
+} SyncClocks;
+
+#if !defined(CONFIG_USER_ONLY)
+/* Allow the guest to have a max 3ms advance.
+ * The difference between the 2 clocks could therefore
+ * oscillate around 0.
+ */
+#define VM_CLOCK_ADVANCE 3000000
+#define THRESHOLD_REDUCE 1.5
+#define MAX_DELAY_PRINT_RATE 2000000000LL
+#define MAX_NB_PRINTS 100
+
+static void align_clocks(SyncClocks *sc, const CPUState *cpu)
+{
+ int64_t cpu_icount;
+
+ if (!icount_align_option) {
+ return;
+ }
+
+ cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
+ sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount);
+ sc->last_cpu_icount = cpu_icount;
+
+ if (sc->diff_clk > VM_CLOCK_ADVANCE) {
+#ifndef _WIN32
+ struct timespec sleep_delay, rem_delay;
+ sleep_delay.tv_sec = sc->diff_clk / 1000000000LL;
+ sleep_delay.tv_nsec = sc->diff_clk % 1000000000LL;
+ if (nanosleep(&sleep_delay, &rem_delay) < 0) {
+ sc->diff_clk -= (sleep_delay.tv_sec - rem_delay.tv_sec) * 1000000000LL;
+ sc->diff_clk -= sleep_delay.tv_nsec - rem_delay.tv_nsec;
+ } else {
+ sc->diff_clk = 0;
+ }
+#else
+ Sleep(sc->diff_clk / SCALE_MS);
+ sc->diff_clk = 0;
+#endif
+ }
+}
+
+static void print_delay(const SyncClocks *sc)
+{
+ static float threshold_delay;
+ static int64_t last_realtime_clock;
+ static int nb_prints;
+
+ if (icount_align_option &&
+ sc->realtime_clock - last_realtime_clock >= MAX_DELAY_PRINT_RATE &&
+ nb_prints < MAX_NB_PRINTS) {
+ if ((-sc->diff_clk / (float)1000000000LL > threshold_delay) ||
+ (-sc->diff_clk / (float)1000000000LL <
+ (threshold_delay - THRESHOLD_REDUCE))) {
+ threshold_delay = (-sc->diff_clk / 1000000000LL) + 1;
+ printf("Warning: The guest is now late by %.1f to %.1f seconds\n",
+ threshold_delay - 1,
+ threshold_delay);
+ nb_prints++;
+ last_realtime_clock = sc->realtime_clock;
+ }
+ }
+}
+
+static void init_delay_params(SyncClocks *sc,
+ const CPUState *cpu)
+{
+ if (!icount_align_option) {
+ return;
+ }
+ sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
+ sc->realtime_clock +
+ cpu_get_clock_offset();
+ sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
+ if (sc->diff_clk < max_delay) {
+ max_delay = sc->diff_clk;
+ }
+ if (sc->diff_clk > max_advance) {
+ max_advance = sc->diff_clk;
+ }
+
+ /* Print every 2s max if the guest is late. We limit the number
+ of printed messages to NB_PRINT_MAX(currently 100) */
+ print_delay(sc);
+}
+#else
+static void align_clocks(SyncClocks *sc, const CPUState *cpu)
+{
+}
+
+static void init_delay_params(SyncClocks *sc, const CPUState *cpu)
+{
+}
+#endif /* CONFIG USER ONLY */
void cpu_loop_exit(CPUState *cpu)
{
@@ -227,6 +330,8 @@ int cpu_exec(CPUArchState *env)
TranslationBlock *tb;
uint8_t *tc_ptr;
uintptr_t next_tb;
+ SyncClocks sc;
+
/* This must be volatile so it is not trashed by longjmp() */
volatile bool have_tb_lock = false;
@@ -283,6 +388,13 @@ int cpu_exec(CPUArchState *env)
#endif
cpu->exception_index = -1;
+ /* Calculate difference between guest clock and host clock.
+ * This delay includes the delay of the last cycle, so
+ * what we have to do is sleep until it is 0. As for the
+ * advance/delay we gain here, we try to fix it next time.
+ */
+ init_delay_params(&sc, cpu);
+
/* prepare setjmp context for exception handling */
for(;;) {
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
@@ -672,6 +784,7 @@ int cpu_exec(CPUArchState *env)
if (insns_left > 0) {
/* Execute remaining instructions. */
cpu_exec_nocache(env, insns_left, tb);
+ align_clocks(&sc, cpu);
}
cpu->exception_index = EXCP_INTERRUPT;
next_tb = 0;
@@ -684,6 +797,9 @@ int cpu_exec(CPUArchState *env)
}
}
cpu->current_tb = NULL;
+ /* Try to align the host and virtual clocks
+ if the guest is in advance */
+ align_clocks(&sc, cpu);
/* reset soft MMU for next block (it can currently
only be set by a memory fault) */
} /* for(;;) */
diff --git a/cpus.c b/cpus.c
index 5e7f2cf3cf..2b5c0bd7c7 100644
--- a/cpus.c
+++ b/cpus.c
@@ -64,6 +64,8 @@
#endif /* CONFIG_LINUX */
static CPUState *next_cpu;
+int64_t max_delay;
+int64_t max_advance;
bool cpu_is_stopped(CPUState *cpu)
{
@@ -102,17 +104,12 @@ static bool all_cpu_threads_idle(void)
/* Protected by TimersState seqlock */
-/* Compensate for varying guest execution speed. */
-static int64_t qemu_icount_bias;
-static int64_t vm_clock_warp_start;
+static int64_t vm_clock_warp_start = -1;
/* Conversion factor from emulated instructions to virtual clock ticks. */
static int icount_time_shift;
/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
#define MAX_ICOUNT_SHIFT 10
-/* Only written by TCG thread */
-static int64_t qemu_icount;
-
static QEMUTimer *icount_rt_timer;
static QEMUTimer *icount_vm_timer;
static QEMUTimer *icount_warp_timer;
@@ -129,6 +126,11 @@ typedef struct TimersState {
int64_t cpu_clock_offset;
int32_t cpu_ticks_enabled;
int64_t dummy;
+
+ /* Compensate for varying guest execution speed. */
+ int64_t qemu_icount_bias;
+ /* Only written by TCG thread */
+ int64_t qemu_icount;
} TimersState;
static TimersState timers_state;
@@ -139,14 +141,14 @@ static int64_t cpu_get_icount_locked(void)
int64_t icount;
CPUState *cpu = current_cpu;
- icount = qemu_icount;
+ icount = timers_state.qemu_icount;
if (cpu) {
if (!cpu_can_do_io(cpu)) {
fprintf(stderr, "Bad clock read\n");
}
icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
}
- return qemu_icount_bias + (icount << icount_time_shift);
+ return timers_state.qemu_icount_bias + cpu_icount_to_ns(icount);
}
int64_t cpu_get_icount(void)
@@ -162,6 +164,11 @@ int64_t cpu_get_icount(void)
return icount;
}
+int64_t cpu_icount_to_ns(int64_t icount)
+{
+ return icount << icount_time_shift;
+}
+
/* return the host CPU cycle counter and handle stop/restart */
/* Caller must hold the BQL */
int64_t cpu_get_ticks(void)
@@ -214,6 +221,23 @@ int64_t cpu_get_clock(void)
return ti;
}
+/* return the offset between the host clock and virtual CPU clock */
+int64_t cpu_get_clock_offset(void)
+{
+ int64_t ti;
+ unsigned start;
+
+ do {
+ start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
+ ti = timers_state.cpu_clock_offset;
+ if (!timers_state.cpu_ticks_enabled) {
+ ti -= get_clock();
+ }
+ } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
+
+ return -ti;
+}
+
/* enable cpu_get_ticks()
* Caller must hold BQL which server as mutex for vm_clock_seqlock.
*/
@@ -284,7 +308,8 @@ static void icount_adjust(void)
icount_time_shift++;
}
last_delta = delta;
- qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift);
+ timers_state.qemu_icount_bias = cur_icount
+ - (timers_state.qemu_icount << icount_time_shift);
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
}
@@ -333,7 +358,7 @@ static void icount_warp_rt(void *opaque)
int64_t delta = cur_time - cur_icount;
warp_delta = MIN(warp_delta, delta);
}
- qemu_icount_bias += warp_delta;
+ timers_state.qemu_icount_bias += warp_delta;
}
vm_clock_warp_start = -1;
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
@@ -351,7 +376,7 @@ void qtest_clock_warp(int64_t dest)
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
seqlock_write_lock(&timers_state.vm_clock_seqlock);
- qemu_icount_bias += warp;
+ timers_state.qemu_icount_bias += warp;
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
@@ -428,6 +453,25 @@ void qemu_clock_warp(QEMUClockType type)
}
}
+static bool icount_state_needed(void *opaque)
+{
+ return use_icount;
+}
+
+/*
+ * This is a subsection for icount migration.
+ */
+static const VMStateDescription icount_vmstate_timers = {
+ .name = "timer/icount",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(qemu_icount_bias, TimersState),
+ VMSTATE_INT64(qemu_icount, TimersState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_timers = {
.name = "timer",
.version_id = 2,
@@ -437,23 +481,44 @@ static const VMStateDescription vmstate_timers = {
VMSTATE_INT64(dummy, TimersState),
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection[]) {
+ {
+ .vmsd = &icount_vmstate_timers,
+ .needed = icount_state_needed,
+ }, {
+ /* empty */
+ }
}
};
-void configure_icount(const char *option)
+void configure_icount(QemuOpts *opts, Error **errp)
{
+ const char *option;
+ char *rem_str = NULL;
+
seqlock_init(&timers_state.vm_clock_seqlock, NULL);
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
+ option = qemu_opt_get(opts, "shift");
if (!option) {
+ if (qemu_opt_get(opts, "align") != NULL) {
+ error_setg(errp, "Please specify shift option when using align");
+ }
return;
}
-
+ icount_align_option = qemu_opt_get_bool(opts, "align", false);
icount_warp_timer = timer_new_ns(QEMU_CLOCK_REALTIME,
icount_warp_rt, NULL);
if (strcmp(option, "auto") != 0) {
- icount_time_shift = strtol(option, NULL, 0);
+ errno = 0;
+ icount_time_shift = strtol(option, &rem_str, 0);
+ if (errno != 0 || *rem_str != '\0' || !strlen(option)) {
+ error_setg(errp, "icount: Invalid shift value");
+ }
use_icount = 1;
return;
+ } else if (icount_align_option) {
+ error_setg(errp, "shift=auto and align=on are incompatible");
}
use_icount = 2;
@@ -1250,7 +1315,8 @@ static int tcg_cpu_exec(CPUArchState *env)
int64_t count;
int64_t deadline;
int decr;
- qemu_icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
+ timers_state.qemu_icount -= (cpu->icount_decr.u16.low
+ + cpu->icount_extra);
cpu->icount_decr.u16.low = 0;
cpu->icount_extra = 0;
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
@@ -1265,7 +1331,7 @@ static int tcg_cpu_exec(CPUArchState *env)
}
count = qemu_icount_round(deadline);
- qemu_icount += count;
+ timers_state.qemu_icount += count;
decr = (count > 0xffff) ? 0xffff : count;
count -= decr;
cpu->icount_decr.u16.low = decr;
@@ -1278,7 +1344,8 @@ static int tcg_cpu_exec(CPUArchState *env)
if (use_icount) {
/* Fold pending instructions back into the
instruction counter, and clear the interrupt flag. */
- qemu_icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
+ timers_state.qemu_icount -= (cpu->icount_decr.u16.low
+ + cpu->icount_extra);
cpu->icount_decr.u32 = 0;
cpu->icount_extra = 0;
}
@@ -1487,3 +1554,20 @@ void qmp_inject_nmi(Error **errp)
error_set(errp, QERR_UNSUPPORTED);
#endif
}
+
+void dump_drift_info(FILE *f, fprintf_function cpu_fprintf)
+{
+ if (!use_icount) {
+ return;
+ }
+
+ cpu_fprintf(f, "Host - Guest clock %"PRIi64" ms\n",
+ (cpu_get_clock() - cpu_get_icount())/SCALE_MS);
+ if (icount_align_option) {
+ cpu_fprintf(f, "Max guest delay %"PRIi64" ms\n", -max_delay/SCALE_MS);
+ cpu_fprintf(f, "Max guest advance %"PRIi64" ms\n", max_advance/SCALE_MS);
+ } else {
+ cpu_fprintf(f, "Max guest delay NA\n");
+ cpu_fprintf(f, "Max guest advance NA\n");
+ }
+}
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
index 0b9eba0c84..ba08adbd99 100644
--- a/hw/misc/vfio.c
+++ b/hw/misc/vfio.c
@@ -120,11 +120,19 @@ typedef struct VFIOINTx {
} VFIOINTx;
typedef struct VFIOMSIVector {
- EventNotifier interrupt; /* eventfd triggered on interrupt */
- EventNotifier kvm_interrupt; /* eventfd triggered for KVM irqfd bypass */
+ /*
+ * Two interrupt paths are configured per vector. The first, is only used
+ * for interrupts injected via QEMU. This is typically the non-accel path,
+ * but may also be used when we want QEMU to handle masking and pending
+ * bits. The KVM path bypasses QEMU and is therefore higher performance,
+ * but requires masking at the device. virq is used to track the MSI route
+ * through KVM, thus kvm_interrupt is only available when virq is set to a
+ * valid (>= 0) value.
+ */
+ EventNotifier interrupt;
+ EventNotifier kvm_interrupt;
struct VFIODevice *vdev; /* back pointer to device */
- MSIMessage msg; /* cache the MSI message so we know when it changes */
- int virq; /* KVM irqchip route for QEMU bypass */
+ int virq;
bool use;
} VFIOMSIVector;
@@ -681,13 +689,24 @@ static int vfio_enable_vectors(VFIODevice *vdev, bool msix)
fds = (int32_t *)&irq_set->data;
for (i = 0; i < vdev->nr_vectors; i++) {
- if (!vdev->msi_vectors[i].use) {
- fds[i] = -1;
- } else if (vdev->msi_vectors[i].virq >= 0) {
- fds[i] = event_notifier_get_fd(&vdev->msi_vectors[i].kvm_interrupt);
- } else {
- fds[i] = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt);
+ int fd = -1;
+
+ /*
+ * MSI vs MSI-X - The guest has direct access to MSI mask and pending
+ * bits, therefore we always use the KVM signaling path when setup.
+ * MSI-X mask and pending bits are emulated, so we want to use the
+ * KVM signaling path only when configured and unmasked.
+ */
+ if (vdev->msi_vectors[i].use) {
+ if (vdev->msi_vectors[i].virq < 0 ||
+ (msix && msix_is_masked(&vdev->pdev, i))) {
+ fd = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt);
+ } else {
+ fd = event_notifier_get_fd(&vdev->msi_vectors[i].kvm_interrupt);
+ }
}
+
+ fds[i] = fd;
}
ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
@@ -724,7 +743,6 @@ static void vfio_add_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage *msg,
return;
}
- vector->msg = *msg;
vector->virq = virq;
}
@@ -740,7 +758,6 @@ static void vfio_remove_kvm_msi_virq(VFIOMSIVector *vector)
static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg)
{
kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg);
- vector->msg = msg;
}
static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
@@ -919,6 +936,7 @@ retry:
for (i = 0; i < vdev->nr_vectors; i++) {
VFIOMSIVector *vector = &vdev->msi_vectors[i];
+ MSIMessage msg = msi_get_message(&vdev->pdev, i);
vector->vdev = vdev;
vector->virq = -1;
@@ -931,13 +949,11 @@ retry:
qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt),
vfio_msi_interrupt, NULL, vector);
- vector->msg = msi_get_message(&vdev->pdev, i);
-
/*
* Attempt to enable route through KVM irqchip,
* default to userspace handling if unavailable.
*/
- vfio_add_kvm_msi_virq(vector, &vector->msg, false);
+ vfio_add_kvm_msi_virq(vector, &msg, false);
}
/* Set interrupt type prior to possible interrupts */
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index c855eba43c..ffefc22f43 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -83,7 +83,7 @@ static char const *imx_epit_reg_name(uint32_t reg)
#define CR_CLKSRC_SHIFT (24)
#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT)
-#define TIMER_MAX 0XFFFFFFFFUL
+#define EPIT_TIMER_MAX 0XFFFFFFFFUL
/*
* Exact clock frequencies vary from board to board.
@@ -155,7 +155,7 @@ static void imx_epit_reset(DeviceState *dev)
*/
s->cr &= (CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN);
s->sr = 0;
- s->lr = TIMER_MAX;
+ s->lr = EPIT_TIMER_MAX;
s->cmp = 0;
s->cnt = 0;
/* stop both timers */
@@ -163,9 +163,9 @@ static void imx_epit_reset(DeviceState *dev)
ptimer_stop(s->timer_reload);
/* compute new frequency */
imx_epit_set_freq(s);
- /* init both timers to TIMER_MAX */
- ptimer_set_limit(s->timer_cmp, TIMER_MAX, 1);
- ptimer_set_limit(s->timer_reload, TIMER_MAX, 1);
+ /* init both timers to EPIT_TIMER_MAX */
+ ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1);
+ ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1);
if (s->freq && (s->cr & CR_EN)) {
/* if the timer is still enabled, restart it */
ptimer_run(s->timer_reload, 0);
@@ -227,7 +227,7 @@ static void imx_epit_reload_compare_timer(IMXEPITState *s)
/* It'll fire in this round of the timer */
next = tmp - s->cmp;
} else { /* catch it next time around */
- next = tmp - s->cmp + ((s->cr & CR_RLD) ? TIMER_MAX : s->lr);
+ next = tmp - s->cmp + ((s->cr & CR_RLD) ? EPIT_TIMER_MAX : s->lr);
}
ptimer_set_count(s->timer_cmp, next);
}
@@ -260,8 +260,8 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
ptimer_set_limit(s->timer_reload, s->lr, 1);
ptimer_set_limit(s->timer_cmp, s->lr, 1);
} else {
- ptimer_set_limit(s->timer_reload, TIMER_MAX, 1);
- ptimer_set_limit(s->timer_cmp, TIMER_MAX, 1);
+ ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1);
+ ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1);
}
}
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 56ee4db991..3b3101084b 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -80,11 +80,11 @@ static char const *imx_gpt_reg_name(uint32_t reg)
* GPT : General purpose timer
*
* This timer counts up continuously while it is enabled, resetting itself
- * to 0 when it reaches TIMER_MAX (in freerun mode) or when it
+ * to 0 when it reaches GPT_TIMER_MAX (in freerun mode) or when it
* reaches the value of one of the ocrX (in periodic mode).
*/
-#define TIMER_MAX 0XFFFFFFFFUL
+#define GPT_TIMER_MAX 0XFFFFFFFFUL
/* Control register. Not all of these bits have any effect (yet) */
#define GPT_CR_EN (1 << 0) /* GPT Enable */
@@ -218,7 +218,7 @@ static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg,
static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
{
- uint32_t timeout = TIMER_MAX;
+ uint32_t timeout = GPT_TIMER_MAX;
uint32_t count = 0;
long long limit;
@@ -230,10 +230,10 @@ static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
if (event) {
/* This is a timer event */
- if ((s->cr & GPT_CR_FRR) && (s->next_timeout != TIMER_MAX)) {
+ if ((s->cr & GPT_CR_FRR) && (s->next_timeout != GPT_TIMER_MAX)) {
/*
* if we are in free running mode and we have not reached
- * the TIMER_MAX limit, then update the count
+ * the GPT_TIMER_MAX limit, then update the count
*/
count = imx_gpt_update_count(s);
}
@@ -267,7 +267,7 @@ static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
if ((s->ir & GPT_IR_OF3IE) && (timeout == s->ocr3)) {
s->next_int |= GPT_SR_OF3;
}
- if ((s->ir & GPT_IR_ROVIE) && (timeout == TIMER_MAX)) {
+ if ((s->ir & GPT_IR_ROVIE) && (timeout == GPT_TIMER_MAX)) {
s->next_int |= GPT_SR_ROV;
}
@@ -370,20 +370,20 @@ static void imx_gpt_reset(DeviceState *dev)
s->pr = 0;
s->ir = 0;
s->cnt = 0;
- s->ocr1 = TIMER_MAX;
- s->ocr2 = TIMER_MAX;
- s->ocr3 = TIMER_MAX;
+ s->ocr1 = GPT_TIMER_MAX;
+ s->ocr2 = GPT_TIMER_MAX;
+ s->ocr3 = GPT_TIMER_MAX;
s->icr1 = 0;
s->icr2 = 0;
- s->next_timeout = TIMER_MAX;
+ s->next_timeout = GPT_TIMER_MAX;
s->next_int = 0;
/* compute new freq */
imx_gpt_set_freq(s);
- /* reset the limit to TIMER_MAX */
- ptimer_set_limit(s->timer, TIMER_MAX, 1);
+ /* reset the limit to GPT_TIMER_MAX */
+ ptimer_set_limit(s->timer, GPT_TIMER_MAX, 1);
/* if the timer is still enabled, restart it */
if (s->freq && (s->cr & GPT_CR_EN)) {
@@ -415,8 +415,8 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
if ((oldreg ^ s->cr) & GPT_CR_EN) {
if (s->cr & GPT_CR_EN) {
if (s->cr & GPT_CR_ENMOD) {
- s->next_timeout = TIMER_MAX;
- ptimer_set_count(s->timer, TIMER_MAX);
+ s->next_timeout = GPT_TIMER_MAX;
+ ptimer_set_count(s->timer, GPT_TIMER_MAX);
imx_gpt_compute_next_timeout(s, false);
}
ptimer_run(s->timer, 1);
@@ -451,8 +451,8 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
/* In non-freerun mode, reset count when this register is written */
if (!(s->cr & GPT_CR_FRR)) {
- s->next_timeout = TIMER_MAX;
- ptimer_set_limit(s->timer, TIMER_MAX, 1);
+ s->next_timeout = GPT_TIMER_MAX;
+ ptimer_set_limit(s->timer, GPT_TIMER_MAX, 1);
}
/* compute the new timeout */
diff --git a/hw/timer/tusb6010.c b/hw/timer/tusb6010.c
index bd2a89e020..459c748e1b 100644
--- a/hw/timer/tusb6010.c
+++ b/hw/timer/tusb6010.c
@@ -282,9 +282,6 @@ static void tusb_gpio_intr_update(TUSBState *s)
/* TODO: How is this signalled? */
}
-extern CPUReadMemoryFunc * const musb_read[];
-extern CPUWriteMemoryFunc * const musb_write[];
-
static uint32_t tusb_async_readb(void *opaque, hwaddr addr)
{
TUSBState *s = (TUSBState *) opaque;
diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c
index 35316c40d9..ff4f2001bb 100644
--- a/hw/virtio/vhost-backend.c
+++ b/hw/virtio/vhost-backend.c
@@ -14,8 +14,6 @@
#include <sys/ioctl.h>
-extern const VhostOps user_ops;
-
static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request,
void *arg)
{
diff --git a/include/hw/usb.h b/include/hw/usb.h
index 8bcab48d29..223a5aef8f 100644
--- a/include/hw/usb.h
+++ b/include/hw/usb.h
@@ -475,7 +475,8 @@ void usb_host_info(Monitor *mon, const QDict *qdict);
#define VM_USB_HUB_SIZE 8
-/* usb-musb.c */
+/* hw/usb/hdc-musb.c */
+
enum musb_irq_source_e {
musb_irq_suspend = 0,
musb_irq_resume,
@@ -494,6 +495,10 @@ enum musb_irq_source_e {
};
typedef struct MUSBState MUSBState;
+
+extern CPUReadMemoryFunc * const musb_read[];
+extern CPUWriteMemoryFunc * const musb_write[];
+
MUSBState *musb_init(DeviceState *parent_device, int gpio_base);
void musb_reset(MUSBState *s);
uint32_t musb_core_intr_get(MUSBState *s);
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index d31768a1d4..e472f29714 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -32,6 +32,8 @@ typedef struct VhostOps {
vhost_backend_cleanup vhost_backend_cleanup;
} VhostOps;
+extern const VhostOps user_ops;
+
int vhost_set_backend_type(struct vhost_dev *dev,
VhostBackendType backend_type);
diff --git a/include/qemu-common.h b/include/qemu-common.h
index 6ef8282234..bcf7a6ad43 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -41,6 +41,7 @@
#include <assert.h>
#include <signal.h>
#include "glib-compat.h"
+#include "qemu/option.h"
#ifdef _WIN32
#include "sysemu/os-win32.h"
@@ -105,8 +106,13 @@ static inline char *realpath(const char *path, char *resolved_path)
#endif
/* icount */
-void configure_icount(const char *option);
+void configure_icount(QemuOpts *opts, Error **errp);
extern int use_icount;
+extern int icount_align_option;
+/* drift information for info jit command */
+extern int64_t max_delay;
+extern int64_t max_advance;
+void dump_drift_info(FILE *f, fprintf_function cpu_fprintf);
#include "qemu/osdep.h"
#include "qemu/bswap.h"
diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index 7f9a074c2a..5f5210d543 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -745,6 +745,8 @@ static inline int64_t get_clock(void)
/* icount */
int64_t cpu_get_icount(void);
int64_t cpu_get_clock(void);
+int64_t cpu_get_clock_offset(void);
+int64_t cpu_icount_to_ns(int64_t icount);
/*******************************************/
/* host CPU ticks (if available) */
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0bbd631e72..98cd4c958e 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -358,6 +358,9 @@ CharDriverState *qemu_char_get_next_serial(void);
/* msmouse */
CharDriverState *qemu_chr_open_msmouse(void);
+/* testdev.c */
+CharDriverState *chr_testdev_init(void);
+
/* baum.c */
CharDriverState *chr_baum_init(void);
diff --git a/monitor.c b/monitor.c
index 5bc70a642d..cdbaa60f98 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1047,6 +1047,7 @@ static void do_info_registers(Monitor *mon, const QDict *qdict)
static void do_info_jit(Monitor *mon, const QDict *qdict)
{
dump_exec_info((FILE *)mon, monitor_fprintf);
+ dump_drift_info((FILE *)mon, monitor_fprintf);
}
static void do_info_history(Monitor *mon, const QDict *qdict)
diff --git a/po/zh_CN.po b/po/zh_CN.po
new file mode 100644
index 0000000000..2b1d42e97b
--- /dev/null
+++ b/po/zh_CN.po
@@ -0,0 +1,86 @@
+# Chinese translation for QEMU.
+# This file is put in the public domain.
+#
+# Fam Zheng <famz@redhat.com>, 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: QEMU 2.2\n"
+"Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n"
+"POT-Creation-Date: 2014-07-31 10:03+0800\n"
+"PO-Revision-Date: 2014-07-31 10:00+0800\n"
+"Last-Translator: Fam Zheng <famz@redhat.com>\n"
+"Language-Team: Chinese <zh@li.org>\n"
+"Language: zh\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 1.4\n"
+
+#: ui/gtk.c:321
+msgid " - Press Ctrl+Alt+G to release grab"
+msgstr " - 按下 Ctrl+Alt+G 取消捕获"
+
+#: ui/gtk.c:325
+msgid " [Paused]"
+msgstr " [已暂停]"
+
+#: ui/gtk.c:1601
+msgid "_Pause"
+msgstr "暂停(_P)"
+
+#: ui/gtk.c:1607
+msgid "_Reset"
+msgstr "重置(_R)"
+
+#: ui/gtk.c:1610
+msgid "Power _Down"
+msgstr "关闭电源(_D)"
+
+#: ui/gtk.c:1616
+msgid "_Quit"
+msgstr "退出(_Q)"
+
+#: ui/gtk.c:1692
+msgid "_Fullscreen"
+msgstr "全屏(_F)"
+
+#: ui/gtk.c:1702
+msgid "Zoom _In"
+msgstr "放大(_I)"
+
+#: ui/gtk.c:1709
+msgid "Zoom _Out"
+msgstr "缩小(_O)"
+
+#: ui/gtk.c:1716
+msgid "Best _Fit"
+msgstr "最合适大小(_F)"
+
+#: ui/gtk.c:1723
+msgid "Zoom To _Fit"
+msgstr "缩放以适应大小(_F)"
+
+#: ui/gtk.c:1729
+msgid "Grab On _Hover"
+msgstr "鼠标经过时捕获(_H)"
+
+#: ui/gtk.c:1732
+msgid "_Grab Input"
+msgstr "捕获输入(_G)"
+
+#: ui/gtk.c:1761
+msgid "Show _Tabs"
+msgstr "显示标签页(_T)"
+
+#: ui/gtk.c:1764
+msgid "Detach Tab"
+msgstr "分离标签页"
+
+#: ui/gtk.c:1778
+msgid "_Machine"
+msgstr "虚拟机(_M)"
+
+#: ui/gtk.c:1783
+msgid "_View"
+msgstr "视图(_V)"
diff --git a/qapi-schema.json b/qapi-schema.json
index b11aad2068..341f417a5f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2749,7 +2749,7 @@
#
# Configuration info for the new chardev backend.
#
-# Since: 1.4
+# Since: 1.4 (testdev since 2.2)
##
{ 'type': 'ChardevDummy', 'data': { } }
@@ -2764,6 +2764,7 @@
'mux' : 'ChardevMux',
'msmouse': 'ChardevDummy',
'braille': 'ChardevDummy',
+ 'testdev': 'ChardevDummy',
'stdio' : 'ChardevStdio',
'console': 'ChardevDummy',
'spicevmc' : 'ChardevSpiceChannel',
diff --git a/qemu-char.c b/qemu-char.c
index 956be49ecd..6964a2d9fd 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3256,6 +3256,7 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
strcmp(filename, "pty") == 0 ||
strcmp(filename, "msmouse") == 0 ||
strcmp(filename, "braille") == 0 ||
+ strcmp(filename, "testdev") == 0 ||
strcmp(filename, "stdio") == 0) {
qemu_opt_set(opts, "backend", filename);
return opts;
@@ -4057,6 +4058,9 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
chr = chr_baum_init();
break;
#endif
+ case CHARDEV_BACKEND_KIND_TESTDEV:
+ chr = chr_testdev_init();
+ break;
case CHARDEV_BACKEND_KIND_STDIO:
chr = qemu_chr_open_stdio(backend->stdio);
break;
diff --git a/qemu-img.c b/qemu-img.c
index 9566faeb56..18caa4b450 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -185,15 +185,20 @@ static int GCC_FMT_ATTR(2, 3) qprintf(bool quiet, const char *fmt, ...)
static int read_password(char *buf, int buf_size)
{
int c, i;
+
printf("Password: ");
fflush(stdout);
i = 0;
for(;;) {
c = getchar();
- if (c == '\n')
+ if (c < 0) {
+ buf[i] = '\0';
+ return -1;
+ } else if (c == '\n') {
break;
- if (i < (buf_size - 1))
+ } else if (i < (buf_size - 1)) {
buf[i++] = c;
+ }
}
buf[i] = '\0';
return 0;
diff --git a/qemu-options.hx b/qemu-options.hx
index 154962558b..96516c1e23 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3011,11 +3011,11 @@ re-inject them.
ETEXI
DEF("icount", HAS_ARG, QEMU_OPTION_icount, \
- "-icount [N|auto]\n" \
+ "-icount [shift=N|auto][,align=on|off]\n" \
" enable virtual instruction counter with 2^N clock ticks per\n" \
- " instruction\n", QEMU_ARCH_ALL)
+ " instruction and enable aligning the host and virtual clocks\n", QEMU_ARCH_ALL)
STEXI
-@item -icount [@var{N}|auto]
+@item -icount [shift=@var{N}|auto]
@findex -icount
Enable virtual instruction counter. The virtual cpu will execute one
instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified
@@ -3026,6 +3026,17 @@ Note that while this option can give deterministic behavior, it does not
provide cycle accurate emulation. Modern CPUs contain superscalar out of
order cores with complex cache hierarchies. The number of instructions
executed often has little or no correlation with actual performance.
+
+@option{align=on} will activate the delay algorithm which will try to
+to synchronise the host clock and the virtual clock. The goal is to
+have a guest running at the real frequency imposed by the shift option.
+Whenever the guest clock is behind the host clock and if
+@option{align=on} is specified then we print a messsage to the user
+to inform about the delay.
+Currently this option does not work when @option{shift} is @code{auto}.
+Note: The sync algorithm will work for those shift values for which
+the guest clock runs ahead of the host clock. Typically this happens
+when the shift value is high (how high depends on the host machine).
ETEXI
DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 8e6272c5a2..7eed7f4592 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -18,6 +18,7 @@
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
+#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
@@ -575,6 +576,7 @@ static void guest_file_init(void)
typedef struct FsMount {
char *dirname;
char *devtype;
+ unsigned int devmajor, devminor;
QTAILQ_ENTRY(FsMount) next;
} FsMount;
@@ -596,15 +598,40 @@ static void free_fs_mount_list(FsMountList *mounts)
}
}
+static int dev_major_minor(const char *devpath,
+ unsigned int *devmajor, unsigned int *devminor)
+{
+ struct stat st;
+
+ *devmajor = 0;
+ *devminor = 0;
+
+ if (stat(devpath, &st) < 0) {
+ slog("failed to stat device file '%s': %s", devpath, strerror(errno));
+ return -1;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ /* It is bind mount */
+ return -2;
+ }
+ if (S_ISBLK(st.st_mode)) {
+ *devmajor = major(st.st_rdev);
+ *devminor = minor(st.st_rdev);
+ return 0;
+ }
+ return -1;
+}
+
/*
* Walk the mount table and build a list of local file systems
*/
-static void build_fs_mount_list(FsMountList *mounts, Error **errp)
+static void build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp)
{
struct mntent *ment;
FsMount *mount;
char const *mtab = "/proc/self/mounts";
FILE *fp;
+ unsigned int devmajor, devminor;
fp = setmntent(mtab, "r");
if (!fp) {
@@ -624,20 +651,423 @@ static void build_fs_mount_list(FsMountList *mounts, Error **errp)
(strcmp(ment->mnt_type, "cifs") == 0)) {
continue;
}
+ if (dev_major_minor(ment->mnt_fsname, &devmajor, &devminor) == -2) {
+ /* Skip bind mounts */
+ continue;
+ }
mount = g_malloc0(sizeof(FsMount));
mount->dirname = g_strdup(ment->mnt_dir);
mount->devtype = g_strdup(ment->mnt_type);
+ mount->devmajor = devmajor;
+ mount->devminor = devminor;
QTAILQ_INSERT_TAIL(mounts, mount, next);
}
endmntent(fp);
}
+
+static void decode_mntname(char *name, int len)
+{
+ int i, j = 0;
+ for (i = 0; i <= len; i++) {
+ if (name[i] != '\\') {
+ name[j++] = name[i];
+ } else if (name[i + 1] == '\\') {
+ name[j++] = '\\';
+ i++;
+ } else if (name[i + 1] >= '0' && name[i + 1] <= '3' &&
+ name[i + 2] >= '0' && name[i + 2] <= '7' &&
+ name[i + 3] >= '0' && name[i + 3] <= '7') {
+ name[j++] = (name[i + 1] - '0') * 64 +
+ (name[i + 2] - '0') * 8 +
+ (name[i + 3] - '0');
+ i += 3;
+ } else {
+ name[j++] = name[i];
+ }
+ }
+}
+
+static void build_fs_mount_list(FsMountList *mounts, Error **errp)
+{
+ FsMount *mount;
+ char const *mountinfo = "/proc/self/mountinfo";
+ FILE *fp;
+ char *line = NULL, *dash;
+ size_t n;
+ char check;
+ unsigned int devmajor, devminor;
+ int ret, dir_s, dir_e, type_s, type_e, dev_s, dev_e;
+
+ fp = fopen(mountinfo, "r");
+ if (!fp) {
+ build_fs_mount_list_from_mtab(mounts, errp);
+ return;
+ }
+
+ while (getline(&line, &n, fp) != -1) {
+ ret = sscanf(line, "%*u %*u %u:%u %*s %n%*s%n%c",
+ &devmajor, &devminor, &dir_s, &dir_e, &check);
+ if (ret < 3) {
+ continue;
+ }
+ dash = strstr(line + dir_e, " - ");
+ if (!dash) {
+ continue;
+ }
+ ret = sscanf(dash, " - %n%*s%n %n%*s%n%c",
+ &type_s, &type_e, &dev_s, &dev_e, &check);
+ if (ret < 1) {
+ continue;
+ }
+ line[dir_e] = 0;
+ dash[type_e] = 0;
+ dash[dev_e] = 0;
+ decode_mntname(line + dir_s, dir_e - dir_s);
+ decode_mntname(dash + dev_s, dev_e - dev_s);
+ if (devmajor == 0) {
+ /* btrfs reports major number = 0 */
+ if (strcmp("btrfs", dash + type_s) != 0 ||
+ dev_major_minor(dash + dev_s, &devmajor, &devminor) < 0) {
+ continue;
+ }
+ }
+
+ mount = g_malloc0(sizeof(FsMount));
+ mount->dirname = g_strdup(line + dir_s);
+ mount->devtype = g_strdup(dash + type_s);
+ mount->devmajor = devmajor;
+ mount->devminor = devminor;
+
+ QTAILQ_INSERT_TAIL(mounts, mount, next);
+ }
+ free(line);
+
+ fclose(fp);
+}
#endif
#if defined(CONFIG_FSFREEZE)
+static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
+{
+ char *path;
+ char *dpath;
+ char *driver = NULL;
+ char buf[PATH_MAX];
+ ssize_t len;
+
+ path = g_strndup(syspath, pathlen);
+ dpath = g_strdup_printf("%s/driver", path);
+ len = readlink(dpath, buf, sizeof(buf) - 1);
+ if (len != -1) {
+ buf[len] = 0;
+ driver = g_strdup(basename(buf));
+ }
+ g_free(dpath);
+ g_free(path);
+ return driver;
+}
+
+static int compare_uint(const void *_a, const void *_b)
+{
+ unsigned int a = *(unsigned int *)_a;
+ unsigned int b = *(unsigned int *)_b;
+
+ return a < b ? -1 : a > b ? 1 : 0;
+}
+
+/* Walk the specified sysfs and build a sorted list of host or ata numbers */
+static int build_hosts(char const *syspath, char const *host, bool ata,
+ unsigned int *hosts, int hosts_max, Error **errp)
+{
+ char *path;
+ DIR *dir;
+ struct dirent *entry;
+ int i = 0;
+
+ path = g_strndup(syspath, host - syspath);
+ dir = opendir(path);
+ if (!dir) {
+ error_setg_errno(errp, errno, "opendir(\"%s\")", path);
+ g_free(path);
+ return -1;
+ }
+
+ while (i < hosts_max) {
+ entry = readdir(dir);
+ if (!entry) {
+ break;
+ }
+ if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
+ ++i;
+ } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) {
+ ++i;
+ }
+ }
+
+ qsort(hosts, i, sizeof(hosts[0]), compare_uint);
+
+ g_free(path);
+ closedir(dir);
+ return i;
+}
+
+/* Store disk device info specified by @sysfs into @fs */
+static void build_guest_fsinfo_for_real_device(char const *syspath,
+ GuestFilesystemInfo *fs,
+ Error **errp)
+{
+ unsigned int pci[4], host, hosts[8], tgt[3];
+ int i, nhosts = 0, pcilen;
+ GuestDiskAddress *disk;
+ GuestPCIAddress *pciaddr;
+ GuestDiskAddressList *list = NULL;
+ bool has_ata = false, has_host = false, has_tgt = false;
+ char *p, *q, *driver = NULL;
+
+ p = strstr(syspath, "/devices/pci");
+ if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
+ pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
+ g_debug("only pci device is supported: sysfs path \"%s\"", syspath);
+ return;
+ }
+
+ driver = get_pci_driver(syspath, (p + 12 + pcilen) - syspath, errp);
+ if (!driver) {
+ goto cleanup;
+ }
+
+ p = strstr(syspath, "/target");
+ if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
+ tgt, tgt + 1, tgt + 2) == 3) {
+ has_tgt = true;
+ }
+
+ p = strstr(syspath, "/ata");
+ if (p) {
+ q = p + 4;
+ has_ata = true;
+ } else {
+ p = strstr(syspath, "/host");
+ q = p + 5;
+ }
+ if (p && sscanf(q, "%u", &host) == 1) {
+ has_host = true;
+ nhosts = build_hosts(syspath, p, has_ata, hosts,
+ sizeof(hosts) / sizeof(hosts[0]), errp);
+ if (nhosts < 0) {
+ goto cleanup;
+ }
+ }
+
+ pciaddr = g_malloc0(sizeof(*pciaddr));
+ pciaddr->domain = pci[0];
+ pciaddr->bus = pci[1];
+ pciaddr->slot = pci[2];
+ pciaddr->function = pci[3];
+
+ disk = g_malloc0(sizeof(*disk));
+ disk->pci_controller = pciaddr;
+
+ list = g_malloc0(sizeof(*list));
+ list->value = disk;
+
+ if (strcmp(driver, "ata_piix") == 0) {
+ /* a host per ide bus, target*:0:<unit>:0 */
+ if (!has_host || !has_tgt) {
+ g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ for (i = 0; i < nhosts; i++) {
+ if (host == hosts[i]) {
+ disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
+ disk->bus = i;
+ disk->unit = tgt[1];
+ break;
+ }
+ }
+ if (i >= nhosts) {
+ g_debug("no host for '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ } else if (strcmp(driver, "sym53c8xx") == 0) {
+ /* scsi(LSI Logic): target*:0:<unit>:0 */
+ if (!has_tgt) {
+ g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
+ disk->unit = tgt[1];
+ } else if (strcmp(driver, "virtio-pci") == 0) {
+ if (has_tgt) {
+ /* virtio-scsi: target*:0:0:<unit> */
+ disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
+ disk->unit = tgt[2];
+ } else {
+ /* virtio-blk: 1 disk per 1 device */
+ disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
+ }
+ } else if (strcmp(driver, "ahci") == 0) {
+ /* ahci: 1 host per 1 unit */
+ if (!has_host || !has_tgt) {
+ g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ for (i = 0; i < nhosts; i++) {
+ if (host == hosts[i]) {
+ disk->unit = i;
+ disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
+ break;
+ }
+ }
+ if (i >= nhosts) {
+ g_debug("no host for '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ } else {
+ g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
+ goto cleanup;
+ }
+
+ list->next = fs->disk;
+ fs->disk = list;
+ g_free(driver);
+ return;
+
+cleanup:
+ if (list) {
+ qapi_free_GuestDiskAddressList(list);
+ }
+ g_free(driver);
+}
+
+static void build_guest_fsinfo_for_device(char const *devpath,
+ GuestFilesystemInfo *fs,
+ Error **errp);
+
+/* Store a list of slave devices of virtual volume specified by @syspath into
+ * @fs */
+static void build_guest_fsinfo_for_virtual_device(char const *syspath,
+ GuestFilesystemInfo *fs,
+ Error **errp)
+{
+ DIR *dir;
+ char *dirpath;
+ struct dirent entry, *result;
+
+ dirpath = g_strdup_printf("%s/slaves", syspath);
+ dir = opendir(dirpath);
+ if (!dir) {
+ error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
+ g_free(dirpath);
+ return;
+ }
+ g_free(dirpath);
+
+ for (;;) {
+ if (readdir_r(dir, &entry, &result) != 0) {
+ error_setg_errno(errp, errno, "readdir_r(\"%s\")", dirpath);
+ break;
+ }
+ if (!result) {
+ break;
+ }
+
+ if (entry.d_type == DT_LNK) {
+ g_debug(" slave device '%s'", entry.d_name);
+ dirpath = g_strdup_printf("%s/slaves/%s", syspath, entry.d_name);
+ build_guest_fsinfo_for_device(dirpath, fs, errp);
+ g_free(dirpath);
+
+ if (*errp) {
+ break;
+ }
+ }
+ }
+
+ closedir(dir);
+}
+
+/* Dispatch to functions for virtual/real device */
+static void build_guest_fsinfo_for_device(char const *devpath,
+ GuestFilesystemInfo *fs,
+ Error **errp)
+{
+ char *syspath = realpath(devpath, NULL);
+
+ if (!syspath) {
+ error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
+ return;
+ }
+
+ if (!fs->name) {
+ fs->name = g_strdup(basename(syspath));
+ }
+
+ g_debug(" parse sysfs path '%s'", syspath);
+
+ if (strstr(syspath, "/devices/virtual/block/")) {
+ build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
+ } else {
+ build_guest_fsinfo_for_real_device(syspath, fs, errp);
+ }
+
+ free(syspath);
+}
+
+/* Return a list of the disk device(s)' info which @mount lies on */
+static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
+ Error **errp)
+{
+ GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
+ char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
+ mount->devmajor, mount->devminor);
+
+ fs->mountpoint = g_strdup(mount->dirname);
+ fs->type = g_strdup(mount->devtype);
+ build_guest_fsinfo_for_device(devpath, fs, errp);
+
+ g_free(devpath);
+ return fs;
+}
+
+GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
+{
+ FsMountList mounts;
+ struct FsMount *mount;
+ GuestFilesystemInfoList *new, *ret = NULL;
+ Error *local_err = NULL;
+
+ QTAILQ_INIT(&mounts);
+ build_fs_mount_list(&mounts, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ QTAILQ_FOREACH(mount, &mounts, next) {
+ g_debug("Building guest fsinfo for '%s'", mount->dirname);
+
+ new = g_malloc0(sizeof(*ret));
+ new->value = build_guest_fsinfo(mount, &local_err);
+ new->next = ret;
+ ret = new;
+ if (local_err) {
+ error_propagate(errp, local_err);
+ qapi_free_GuestFilesystemInfoList(ret);
+ ret = NULL;
+ break;
+ }
+ }
+
+ free_fs_mount_list(&mounts);
+ return ret;
+}
+
+
typedef enum {
FSFREEZE_HOOK_THAW = 0,
FSFREEZE_HOOK_FREEZE,
@@ -710,13 +1140,21 @@ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
return GUEST_FSFREEZE_STATUS_THAWED;
}
+int64_t qmp_guest_fsfreeze_freeze(Error **errp)
+{
+ return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
+}
+
/*
* Walk list of mounted file systems in the guest, and freeze the ones which
* are real local file systems.
*/
-int64_t qmp_guest_fsfreeze_freeze(Error **errp)
+int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
+ strList *mountpoints,
+ Error **errp)
{
int ret = 0, i = 0;
+ strList *list;
FsMountList mounts;
struct FsMount *mount;
Error *local_err = NULL;
@@ -741,6 +1179,19 @@ int64_t qmp_guest_fsfreeze_freeze(Error **errp)
ga_set_frozen(ga_state);
QTAILQ_FOREACH_REVERSE(mount, &mounts, FsMountList, next) {
+ /* To issue fsfreeze in the reverse order of mounts, check if the
+ * mount is listed in the list here */
+ if (has_mountpoints) {
+ for (list = mountpoints; list; list = list->next) {
+ if (strcmp(list->value, mount->dirname) == 0) {
+ break;
+ }
+ }
+ if (!list) {
+ continue;
+ }
+ }
+
fd = qemu_open(mount->dirname, O_RDONLY);
if (fd == -1) {
error_setg_errno(errp, errno, "failed to open %s", mount->dirname);
@@ -1460,6 +1911,12 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
#if !defined(CONFIG_FSFREEZE)
+GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
+{
+ error_set(errp, QERR_UNSUPPORTED);
+ return NULL;
+}
+
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
{
error_set(errp, QERR_UNSUPPORTED);
@@ -1474,6 +1931,15 @@ int64_t qmp_guest_fsfreeze_freeze(Error **errp)
return 0;
}
+int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
+ strList *mountpoints,
+ Error **errp)
+{
+ error_set(errp, QERR_UNSUPPORTED);
+
+ return 0;
+}
+
int64_t qmp_guest_fsfreeze_thaw(Error **errp)
{
error_set(errp, QERR_UNSUPPORTED);
@@ -1489,6 +1955,44 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
}
#endif
+/* add unsupported commands to the blacklist */
+GList *ga_command_blacklist_init(GList *blacklist)
+{
+#if !defined(__linux__)
+ {
+ const char *list[] = {
+ "guest-suspend-disk", "guest-suspend-ram",
+ "guest-suspend-hybrid", "guest-network-get-interfaces",
+ "guest-get-vcpus", "guest-set-vcpus", NULL};
+ char **p = (char **)list;
+
+ while (*p) {
+ blacklist = g_list_append(blacklist, *p++);
+ }
+ }
+#endif
+
+#if !defined(CONFIG_FSFREEZE)
+ {
+ const char *list[] = {
+ "guest-get-fsinfo", "guest-fsfreeze-status",
+ "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
+ "guest-fsfreeze-thaw", "guest-get-fsinfo", NULL};
+ char **p = (char **)list;
+
+ while (*p) {
+ blacklist = g_list_append(blacklist, *p++);
+ }
+ }
+#endif
+
+#if !defined(CONFIG_FSTRIM)
+ blacklist = g_list_append(blacklist, (char *)"guest-fstrim");
+#endif
+
+ return blacklist;
+}
+
/* register init/cleanup routines for stateful command groups */
void ga_command_state_init(GAState *s, GACommandState *cs)
{
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index e76939651a..3bcbeae8ff 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -152,6 +152,12 @@ void qmp_guest_file_flush(int64_t handle, Error **errp)
error_set(errp, QERR_UNSUPPORTED);
}
+GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
+{
+ error_set(errp, QERR_UNSUPPORTED);
+ return NULL;
+}
+
/*
* Return status of freeze/thaw
*/
@@ -206,6 +212,15 @@ error:
return 0;
}
+int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
+ strList *mountpoints,
+ Error **errp)
+{
+ error_set(errp, QERR_UNSUPPORTED);
+
+ return 0;
+}
+
/*
* Thaw local file systems using Volume Shadow-copy Service.
*/
@@ -431,10 +446,40 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
return -1;
}
+/* add unsupported commands to the blacklist */
+GList *ga_command_blacklist_init(GList *blacklist)
+{
+ const char *list_unsupported[] = {
+ "guest-file-open", "guest-file-close", "guest-file-read",
+ "guest-file-write", "guest-file-seek", "guest-file-flush",
+ "guest-suspend-hybrid", "guest-network-get-interfaces",
+ "guest-get-vcpus", "guest-set-vcpus",
+ "guest-fsfreeze-freeze-list", "guest-get-fsinfo",
+ "guest-fstrim", NULL};
+ char **p = (char **)list_unsupported;
+
+ while (*p) {
+ blacklist = g_list_append(blacklist, *p++);
+ }
+
+ if (!vss_init(true)) {
+ const char *list[] = {
+ "guest-get-fsinfo", "guest-fsfreeze-status",
+ "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
+ p = (char **)list;
+
+ while (*p) {
+ blacklist = g_list_append(blacklist, *p++);
+ }
+ }
+
+ return blacklist;
+}
+
/* register init/cleanup routines for stateful command groups */
void ga_command_state_init(GAState *s, GACommandState *cs)
{
- if (vss_init(true)) {
+ if (!vss_initialized()) {
ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
}
}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index e422208b4e..e92c6abafb 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -19,6 +19,7 @@ typedef struct GAState GAState;
typedef struct GACommandState GACommandState;
extern GAState *ga_state;
+GList *ga_command_blacklist_init(GList *blacklist);
void ga_command_state_init(GAState *s, GACommandState *cs);
void ga_command_state_add(GACommandState *cs,
void (*init)(void),
diff --git a/qga/main.c b/qga/main.c
index 8b927c9db7..227f2bdb88 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1144,6 +1144,7 @@ int main(int argc, char **argv)
goto out_bad;
}
+ blacklist = ga_command_blacklist_init(blacklist);
if (blacklist) {
s->blacklist = blacklist;
do {
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a8cdcb35c4..376e79f583 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -387,6 +387,23 @@
'returns': 'int' }
##
+# @guest-fsfreeze-freeze-list:
+#
+# Sync and freeze specified guest filesystems
+#
+# @mountpoints: #optional an array of mountpoints of filesystems to be frozen.
+# If omitted, every mounted filesystem is frozen.
+#
+# Returns: Number of file systems currently frozen. On error, all filesystems
+# will be thawed.
+#
+# Since: 2.2
+##
+{ 'command': 'guest-fsfreeze-freeze-list',
+ 'data': { '*mountpoints': ['str'] },
+ 'returns': 'int' }
+
+##
# @guest-fsfreeze-thaw:
#
# Unfreeze all frozen guest filesystems
@@ -642,3 +659,82 @@
{ 'command': 'guest-set-vcpus',
'data': {'vcpus': ['GuestLogicalProcessor'] },
'returns': 'int' }
+
+##
+# @GuestDiskBusType
+#
+# An enumeration of bus type of disks
+#
+# @ide: IDE disks
+# @fdc: floppy disks
+# @scsi: SCSI disks
+# @virtio: virtio disks
+# @xen: Xen disks
+# @usb: USB disks
+# @uml: UML disks
+# @sata: SATA disks
+# @sd: SD cards
+#
+# Since: 2.2
+##
+{ 'enum': 'GuestDiskBusType',
+ 'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata',
+ 'sd' ] }
+
+##
+# @GuestPCIAddress:
+#
+# @domain: domain id
+# @bus: bus id
+# @slot: slot id
+# @function: function id
+#
+# Since: 2.2
+##
+{ 'type': 'GuestPCIAddress',
+ 'data': {'domain': 'int', 'bus': 'int',
+ 'slot': 'int', 'function': 'int'} }
+
+##
+# @GuestDiskAddress:
+#
+# @pci-controller: controller's PCI address
+# @type: bus type
+# @bus: bus id
+# @target: target id
+# @unit: unit id
+#
+# Since: 2.2
+##
+{ 'type': 'GuestDiskAddress',
+ 'data': {'pci-controller': 'GuestPCIAddress',
+ 'bus-type': 'GuestDiskBusType',
+ 'bus': 'int', 'target': 'int', 'unit': 'int'} }
+
+##
+# @GuestFilesystemInfo
+#
+# @name: disk name
+# @mountpoint: mount point path
+# @type: file system type string
+# @disk: an array of disk hardware information that the volume lies on,
+# which may be empty if the disk type is not supported
+#
+# Since: 2.2
+##
+{ 'type': 'GuestFilesystemInfo',
+ 'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
+ 'disk': ['GuestDiskAddress']} }
+
+##
+# @guest-get-fsinfo:
+#
+# Returns: The list of filesystems information mounted in the guest.
+# The returned mountpoints may be specified to
+# @guest-fsfreeze-freeze-list.
+# Network filesystems (such as CIFS and NFS) are not listed.
+#
+# Since: 2.2
+##
+{ 'command': 'guest-get-fsinfo',
+ 'returns': ['GuestFilesystemInfo'] }
diff --git a/qtest.c b/qtest.c
index 04a6dc1f0f..ef0d99191c 100644
--- a/qtest.c
+++ b/qtest.c
@@ -19,6 +19,9 @@
#include "hw/irq.h"
#include "sysemu/sysemu.h"
#include "sysemu/cpus.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
+#include "qemu/error-report.h"
#define MAX_IRQ 256
@@ -509,10 +512,16 @@ static void qtest_event(void *opaque, int event)
}
}
-int qtest_init_accel(MachineClass *mc)
+static void configure_qtest_icount(const char *options)
{
- configure_icount("0");
+ QemuOpts *opts = qemu_opts_parse(qemu_find_opts("icount"), options, 1);
+ configure_icount(opts, &error_abort);
+ qemu_opts_del(opts);
+}
+int qtest_init_accel(MachineClass *mc)
+{
+ configure_qtest_icount("0");
return 0;
}
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index 528e1617b3..5e347d04bd 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -2,6 +2,7 @@ stub-obj-y += arch-query-cpu-def.o
stub-obj-y += bdrv-commit-all.o
stub-obj-y += chr-baum-init.o
stub-obj-y += chr-msmouse.o
+stub-obj-y += chr-testdev.o
stub-obj-y += clock-warp.o
stub-obj-y += cpu-get-clock.o
stub-obj-y += cpu-get-icount.o
diff --git a/stubs/chr-testdev.c b/stubs/chr-testdev.c
new file mode 100644
index 0000000000..23112a2c07
--- /dev/null
+++ b/stubs/chr-testdev.c
@@ -0,0 +1,7 @@
+#include "qemu-common.h"
+#include "sysemu/char.h"
+
+CharDriverState *chr_testdev_init(void)
+{
+ return 0;
+}
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 6d008ab5ee..217500c735 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1716,9 +1716,9 @@ static void x86_set_hv_spinlocks(Object *obj, Visitor *v, void *opaque,
if (value < min || value > max) {
error_setg(errp, "Property %s.%s doesn't take value %" PRId64
- " (minimum: %" PRId64 ", maximum: %" PRId64 ")",
- object_get_typename(obj), name ? name : "null",
- value, min, max);
+ " (minimum: %" PRId64 ", maximum: %" PRId64 ")",
+ object_get_typename(obj), name ? name : "null",
+ value, min, max);
return;
}
cpu->hyperv_spinlock_attempts = value;
@@ -1808,8 +1808,8 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
}
if (numvalue < min) {
error_report("hv-spinlocks value shall always be >= 0x%x"
- ", fixup will be removed in future versions",
- min);
+ ", fixup will be removed in future versions",
+ min);
numvalue = min;
}
snprintf(num, sizeof(num), "%" PRId32, numvalue);
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 27651a4a00..df97b35f87 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -21,6 +21,7 @@
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
#include "exec/cpu_ldst.h"
+#include "sysemu/kvm.h"
#ifndef CONFIG_USER_ONLY
static inline void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global);
@@ -2168,6 +2169,16 @@ void mips_cpu_unassigned_access(CPUState *cs, hwaddr addr,
MIPSCPU *cpu = MIPS_CPU(cs);
CPUMIPSState *env = &cpu->env;
+ /*
+ * Raising an exception with KVM enabled will crash because it won't be from
+ * the main execution loop so the longjmp won't have a matching setjmp.
+ * Until we can trigger a bus error exception through KVM lets just ignore
+ * the access.
+ */
+ if (kvm_enabled()) {
+ return;
+ }
+
if (is_exec) {
helper_raise_exception(env, EXCP_IBE);
} else {
diff --git a/tests/Makefile b/tests/Makefile
index 4b2e1bbea9..837e9c89e7 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -282,7 +282,7 @@ tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) libqemu
tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) qapi-types.o qapi-visit.o libqemuutil.a libqemustub.a
+tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
diff --git a/vl.c b/vl.c
index fe451aaf15..a8029d59d7 100644
--- a/vl.c
+++ b/vl.c
@@ -183,6 +183,7 @@ uint8_t *boot_splash_filedata;
size_t boot_splash_filedata_size;
uint8_t qemu_extra_params_fw[2];
+int icount_align_option;
typedef struct FWBootEntry FWBootEntry;
struct FWBootEntry {
@@ -537,6 +538,23 @@ static QemuOptsList qemu_mem_opts = {
},
};
+static QemuOptsList qemu_icount_opts = {
+ .name = "icount",
+ .implied_opt_name = "shift",
+ .merge_lists = true,
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_icount_opts.head),
+ .desc = {
+ {
+ .name = "shift",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "align",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end of list */ }
+ },
+};
+
/**
* Get machine options
*
@@ -2908,13 +2926,12 @@ int main(int argc, char **argv, char **envp)
{
int i;
int snapshot, linux_boot;
- const char *icount_option = NULL;
const char *initrd_filename;
const char *kernel_filename, *kernel_cmdline;
const char *boot_order;
DisplayState *ds;
int cyls, heads, secs, translation;
- QemuOpts *hda_opts = NULL, *opts, *machine_opts;
+ QemuOpts *hda_opts = NULL, *opts, *machine_opts, *icount_opts = NULL;
QemuOptsList *olist;
int optind;
const char *optarg;
@@ -2979,6 +2996,7 @@ int main(int argc, char **argv, char **envp)
qemu_add_opts(&qemu_msg_opts);
qemu_add_opts(&qemu_name_opts);
qemu_add_opts(&qemu_numa_opts);
+ qemu_add_opts(&qemu_icount_opts);
runstate_init();
@@ -3830,7 +3848,11 @@ int main(int argc, char **argv, char **envp)
}
break;
case QEMU_OPTION_icount:
- icount_option = optarg;
+ icount_opts = qemu_opts_parse(qemu_find_opts("icount"),
+ optarg, 1);
+ if (!icount_opts) {
+ exit(1);
+ }
break;
case QEMU_OPTION_incoming:
incoming = optarg;
@@ -4306,11 +4328,14 @@ int main(int argc, char **argv, char **envp)
qemu_spice_init();
#endif
- if (icount_option && (kvm_enabled() || xen_enabled())) {
- fprintf(stderr, "-icount is not allowed with kvm or xen\n");
- exit(1);
+ if (icount_opts) {
+ if (kvm_enabled() || xen_enabled()) {
+ fprintf(stderr, "-icount is not allowed with kvm or xen\n");
+ exit(1);
+ }
+ configure_icount(icount_opts, &error_abort);
+ qemu_opts_del(icount_opts);
}
- configure_icount(icount_option);
/* clean up network at qemu process termination */
atexit(&net_cleanup);