summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--Makefile.objs2
-rw-r--r--Makefile.target1
-rw-r--r--QMP/qmp-spec.txt20
-rwxr-xr-xconfigure14
-rw-r--r--cpu-exec.c1
-rw-r--r--cpus.c82
-rw-r--r--cpus.h2
-rw-r--r--docs/qapi-code-gen.txt4
-rw-r--r--hw/irq.c17
-rw-r--r--hw/irq.h5
-rw-r--r--hw/mc146818rtc.c33
-rw-r--r--hw/mc146818rtc.h3
-rw-r--r--hw/mc146818rtc_regs.h62
-rw-r--r--hw/pc_piix.c3
-rw-r--r--hw/xen_console.c3
-rw-r--r--hw/xen_disk.c11
-rw-r--r--libcacard/vcard_emul_nss.c36
-rw-r--r--monitor.c3
-rw-r--r--osdep.h2
-rw-r--r--qapi-schema-test.json2
-rw-r--r--qapi/qmp-input-visitor.c120
-rw-r--r--qapi/qmp-input-visitor.h2
-rw-r--r--qapi/qmp-output-visitor.c8
-rw-r--r--qemu-common.h1
-rw-r--r--qemu-options.hx8
-rw-r--r--qemu-timer.c2
-rw-r--r--qemu-timer.h1
-rw-r--r--qmp-commands.hx4
-rw-r--r--qtest.c443
-rw-r--r--qtest.h35
-rw-r--r--rules.mak2
-rwxr-xr-xscripts/create_config6
-rwxr-xr-xscripts/gtester-cat26
-rw-r--r--scripts/qapi-commands.py2
-rw-r--r--scripts/qapi-visit.py20
-rwxr-xr-xscripts/qtest5
-rwxr-xr-xscripts/tracetool4
-rw-r--r--target-arm/cpu-qom.h71
-rw-r--r--target-arm/cpu.c60
-rw-r--r--target-arm/cpu.h3
-rw-r--r--target-arm/helper.c14
-rw-r--r--target-arm/translate.c2
-rw-r--r--tests/Makefile167
-rw-r--r--tests/check-qdict.c (renamed from check-qdict.c)0
-rw-r--r--tests/check-qfloat.c (renamed from check-qfloat.c)0
-rw-r--r--tests/check-qint.c (renamed from check-qint.c)0
-rw-r--r--tests/check-qjson.c (renamed from check-qjson.c)0
-rw-r--r--tests/check-qlist.c (renamed from check-qlist.c)0
-rw-r--r--tests/check-qstring.c (renamed from check-qstring.c)0
-rw-r--r--tests/libqtest.c387
-rw-r--r--tests/libqtest.h335
-rw-r--r--tests/rtc-test.c263
-rw-r--r--tests/test-coroutine.c (renamed from test-coroutine.c)0
-rw-r--r--tests/test-qmp-commands.c (renamed from test-qmp-commands.c)0
-rw-r--r--tests/test-qmp-input-strict.c234
-rw-r--r--tests/test-qmp-input-visitor.c (renamed from test-qmp-input-visitor.c)19
-rw-r--r--tests/test-qmp-output-visitor.c (renamed from test-qmp-output-visitor.c)20
-rw-r--r--tests/test-string-input-visitor.c (renamed from test-string-input-visitor.c)0
-rw-r--r--tests/test-string-output-visitor.c (renamed from test-string-output-visitor.c)0
-rw-r--r--vl.c10
61 files changed, 2410 insertions, 171 deletions
diff --git a/Makefile b/Makefile
index d8e1f36229..35c7a2a551 100644
--- a/Makefile
+++ b/Makefile
@@ -348,7 +348,6 @@ QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
-POD2MAN = pod2man --utf8
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
diff --git a/Makefile.objs b/Makefile.objs
index 226b01df96..e9842b0538 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -322,6 +322,8 @@ hw-obj-$(CONFIG_DP8393X) += dp8393x.o
hw-obj-$(CONFIG_DS1225Y) += ds1225y.o
hw-obj-$(CONFIG_MIPSNET) += mipsnet.o
+hw-obj-y += qtest.o
+
# Sound
sound-obj-y =
sound-obj-$(CONFIG_SB16) += sb16.o
diff --git a/Makefile.target b/Makefile.target
index 7cff65f224..9b0cf74c31 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -92,6 +92,7 @@ endif
libobj-$(TARGET_SPARC64) += vis_helper.o
libobj-$(CONFIG_NEED_MMU) += mmu.o
libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
+libobj-$(TARGET_ARM) += cpu.o
ifeq ($(TARGET_BASE_ARCH), sparc)
libobj-y += fop_helper.o cc_helper.o win_helper.o mmu_helper.o ldst_helper.o
libobj-y += cpu_init.o
diff --git a/QMP/qmp-spec.txt b/QMP/qmp-spec.txt
index 9d30a8ce6e..1ba916c9f2 100644
--- a/QMP/qmp-spec.txt
+++ b/QMP/qmp-spec.txt
@@ -209,13 +209,27 @@ incompatible way are disabled by default and will be advertised by the
capabilities array (section '2.2 Server Greeting'). Thus, Clients can check
that array and enable the capabilities they support.
-Additionally, Clients must not assume any particular:
-
-- Size of json-objects or length of json-arrays
+The QMP Server performs a type check on the arguments to a command. It
+generates an error if a value does not have the expected type for its
+key, or if it does not understand a key that the Client included. The
+strictness of the Server catches wrong assumptions of Clients about
+the Server's schema. Clients can assume that, when such validation
+errors occur, they will be reported before the command generated any
+side effect.
+
+However, Clients must not assume any particular:
+
+- Length of json-arrays
+- Size of json-objects; in particular, future versions of QEMU may add
+ new keys and Clients should be able to ignore them.
- Order of json-object members or json-array elements
- Amount of errors generated by a command, that is, new errors can be added
to any existing command in newer versions of the Server
+Of course, the Server does guarantee to send valid JSON. But apart from
+this, a Client should be "conservative in what they send, and liberal in
+what they accept".
+
6. Downstream extension of QMP
------------------------------
diff --git a/configure b/configure
index 14ef738b6c..b51a749475 100755
--- a/configure
+++ b/configure
@@ -2821,6 +2821,13 @@ if test "$solaris" = "no" ; then
fi
fi
+# test if pod2man has --utf8 option
+if pod2man --help | grep -q utf8; then
+ POD2MAN="pod2man --utf8"
+else
+ POD2MAN="pod2man"
+fi
+
# Use ASLR, no-SEH and DEP if available
if test "$mingw32" = "yes" ; then
for flag in --dynamicbase --no-seh --nxcompat; do
@@ -3052,12 +3059,12 @@ if test "$cap_ng" = "yes" ; then
echo "CONFIG_LIBCAP=y" >> $config_host_mak
fi
for card in $audio_card_list; do
- def=CONFIG_`echo $card | tr '[:lower:]' '[:upper:]'`
+ def=CONFIG_`echo $card | LC_ALL=C tr '[a-z]' '[A-Z]'`
echo "$def=y" >> $config_host_mak
done
echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak
for drv in $audio_drv_list; do
- def=CONFIG_`echo $drv | tr '[:lower:]' '[:upper:]'`
+ def=CONFIG_`echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]'`
echo "$def=y" >> $config_host_mak
if test "$drv" = "fmod"; then
echo "FMOD_CFLAGS=-I$fmod_inc" >> $config_host_mak
@@ -3358,6 +3365,7 @@ echo "LIBS+=$LIBS" >> $config_host_mak
echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak
echo "EXESUF=$EXESUF" >> $config_host_mak
echo "LIBS_QGA+=$libs_qga" >> $config_host_mak
+echo "POD2MAN=$POD2MAN" >> $config_host_mak
# generate list of library paths for linker script
@@ -3618,7 +3626,7 @@ echo "TARGET_INT_ALIGNMENT=$target_int_alignment" >> $config_target_mak
echo "TARGET_LONG_ALIGNMENT=$target_long_alignment" >> $config_target_mak
echo "TARGET_LLONG_ALIGNMENT=$target_llong_alignment" >> $config_target_mak
echo "TARGET_ARCH=$TARGET_ARCH" >> $config_target_mak
-target_arch_name="`echo $TARGET_ARCH | tr '[:lower:]' '[:upper:]'`"
+target_arch_name="`echo $TARGET_ARCH | LC_ALL=C tr '[a-z]' '[A-Z]'`"
echo "TARGET_$target_arch_name=y" >> $config_target_mak
echo "TARGET_ARCH2=$target_arch2" >> $config_target_mak
# TARGET_BASE_ARCH needs to be defined after TARGET_ARCH
diff --git a/cpu-exec.c b/cpu-exec.c
index 0fa8325b27..d153f978e1 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -21,6 +21,7 @@
#include "disas.h"
#include "tcg.h"
#include "qemu-barrier.h"
+#include "qtest.h"
int tb_invalidated_flag;
diff --git a/cpus.c b/cpus.c
index 25ba621da5..eb22bd5df1 100644
--- a/cpus.c
+++ b/cpus.c
@@ -34,6 +34,7 @@
#include "qemu-thread.h"
#include "cpus.h"
+#include "qtest.h"
#include "main-loop.h"
#ifndef _WIN32
@@ -238,6 +239,20 @@ static void icount_warp_rt(void *opaque)
vm_clock_warp_start = -1;
}
+void qtest_clock_warp(int64_t dest)
+{
+ int64_t clock = qemu_get_clock_ns(vm_clock);
+ assert(qtest_enabled());
+ while (clock < dest) {
+ int64_t deadline = qemu_clock_deadline(vm_clock);
+ int64_t warp = MIN(dest - clock, deadline);
+ qemu_icount_bias += warp;
+ qemu_run_timers(vm_clock);
+ clock = qemu_get_clock_ns(vm_clock);
+ }
+ qemu_notify_event();
+}
+
void qemu_clock_warp(QEMUClock *clock)
{
int64_t deadline;
@@ -264,6 +279,11 @@ void qemu_clock_warp(QEMUClock *clock)
return;
}
+ if (qtest_enabled()) {
+ /* When testing, qtest commands advance icount. */
+ return;
+ }
+
vm_clock_warp_start = qemu_get_clock_ns(rt_clock);
deadline = qemu_clock_deadline(vm_clock);
if (deadline > 0) {
@@ -741,6 +761,48 @@ static void *qemu_kvm_cpu_thread_fn(void *arg)
return NULL;
}
+static void *qemu_dummy_cpu_thread_fn(void *arg)
+{
+#ifdef _WIN32
+ fprintf(stderr, "qtest is not supported under Windows\n");
+ exit(1);
+#else
+ CPUArchState *env = arg;
+ sigset_t waitset;
+ int r;
+
+ qemu_mutex_lock_iothread();
+ qemu_thread_get_self(env->thread);
+ env->thread_id = qemu_get_thread_id();
+
+ sigemptyset(&waitset);
+ sigaddset(&waitset, SIG_IPI);
+
+ /* signal CPU creation */
+ env->created = 1;
+ qemu_cond_signal(&qemu_cpu_cond);
+
+ cpu_single_env = env;
+ while (1) {
+ cpu_single_env = NULL;
+ qemu_mutex_unlock_iothread();
+ do {
+ int sig;
+ r = sigwait(&waitset, &sig);
+ } while (r == -1 && (errno == EAGAIN || errno == EINTR));
+ if (r == -1) {
+ perror("sigwait");
+ exit(1);
+ }
+ qemu_mutex_lock_iothread();
+ cpu_single_env = env;
+ qemu_wait_io_event_common(env);
+ }
+
+ return NULL;
+#endif
+}
+
static void tcg_exec_all(void);
static void *qemu_tcg_cpu_thread_fn(void *arg)
@@ -803,7 +865,7 @@ void qemu_cpu_kick(void *_env)
CPUArchState *env = _env;
qemu_cond_broadcast(env->halt_cond);
- if (kvm_enabled() && !env->thread_kicked) {
+ if (!tcg_enabled() && !env->thread_kicked) {
qemu_cpu_kick_thread(env);
env->thread_kicked = true;
}
@@ -832,7 +894,7 @@ int qemu_cpu_is_self(void *_env)
void qemu_mutex_lock_iothread(void)
{
- if (kvm_enabled()) {
+ if (!tcg_enabled()) {
qemu_mutex_lock(&qemu_global_mutex);
} else {
iothread_requesting_mutex = true;
@@ -947,6 +1009,18 @@ static void qemu_kvm_start_vcpu(CPUArchState *env)
}
}
+static void qemu_dummy_start_vcpu(CPUArchState *env)
+{
+ env->thread = g_malloc0(sizeof(QemuThread));
+ env->halt_cond = g_malloc0(sizeof(QemuCond));
+ qemu_cond_init(env->halt_cond);
+ qemu_thread_create(env->thread, qemu_dummy_cpu_thread_fn, env,
+ QEMU_THREAD_JOINABLE);
+ while (env->created == 0) {
+ qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
+ }
+}
+
void qemu_init_vcpu(void *_env)
{
CPUArchState *env = _env;
@@ -956,8 +1030,10 @@ void qemu_init_vcpu(void *_env)
env->stopped = 1;
if (kvm_enabled()) {
qemu_kvm_start_vcpu(env);
- } else {
+ } else if (tcg_enabled()) {
qemu_tcg_init_vcpu(env);
+ } else {
+ qemu_dummy_start_vcpu(env);
}
}
diff --git a/cpus.h b/cpus.h
index 4ea2fe2c22..81bd81773f 100644
--- a/cpus.h
+++ b/cpus.h
@@ -11,6 +11,8 @@ void cpu_synchronize_all_states(void);
void cpu_synchronize_all_post_reset(void);
void cpu_synchronize_all_post_init(void);
+void qtest_clock_warp(int64_t dest);
+
/* vl.c */
extern int smp_cores;
extern int smp_threads;
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 5831e371ea..ad11767a2f 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -194,11 +194,11 @@ Example:
void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp)
{
- GenericList *i;
+ GenericList *i, **prev = (GenericList **)obj;
visit_start_list(m, name, errp);
- for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) {
+ for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) {
UserDefOneList *native_i = (UserDefOneList *)i;
visit_type_UserDefOne(m, &native_i->value, NULL, errp);
}
diff --git a/hw/irq.c b/hw/irq.c
index 62f766eb6f..d413a0b235 100644
--- a/hw/irq.c
+++ b/hw/irq.c
@@ -104,3 +104,20 @@ qemu_irq *qemu_irq_proxy(qemu_irq **target, int n)
{
return qemu_allocate_irqs(proxy_irq_handler, target, n);
}
+
+void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n)
+{
+ int i;
+ qemu_irq *old_irqs = qemu_allocate_irqs(NULL, NULL, n);
+ for (i = 0; i < n; i++) {
+ *old_irqs[i] = *gpio_in[i];
+ gpio_in[i]->handler = handler;
+ gpio_in[i]->opaque = old_irqs;
+ }
+}
+
+void qemu_irq_intercept_out(qemu_irq **gpio_out, qemu_irq_handler handler, int n)
+{
+ qemu_irq *old_irqs = *gpio_out;
+ *gpio_out = qemu_allocate_irqs(handler, old_irqs, n);
+}
diff --git a/hw/irq.h b/hw/irq.h
index 64da2fd601..56c55f0c46 100644
--- a/hw/irq.h
+++ b/hw/irq.h
@@ -38,4 +38,9 @@ qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2);
*/
qemu_irq *qemu_irq_proxy(qemu_irq **target, int n);
+/* For internal use in qtest. Similar to qemu_irq_split, but operating
+ on an existing vector of qemu_irq. */
+void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n);
+void qemu_irq_intercept_out(qemu_irq **gpio_out, qemu_irq_handler handler, int n);
+
#endif
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 2b59c36ee6..9c64e0ae25 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -47,39 +47,6 @@
#define RTC_REINJECT_ON_ACK_COUNT 20
-#define RTC_SECONDS 0
-#define RTC_SECONDS_ALARM 1
-#define RTC_MINUTES 2
-#define RTC_MINUTES_ALARM 3
-#define RTC_HOURS 4
-#define RTC_HOURS_ALARM 5
-#define RTC_ALARM_DONT_CARE 0xC0
-
-#define RTC_DAY_OF_WEEK 6
-#define RTC_DAY_OF_MONTH 7
-#define RTC_MONTH 8
-#define RTC_YEAR 9
-
-#define RTC_REG_A 10
-#define RTC_REG_B 11
-#define RTC_REG_C 12
-#define RTC_REG_D 13
-
-#define REG_A_UIP 0x80
-
-#define REG_B_SET 0x80
-#define REG_B_PIE 0x40
-#define REG_B_AIE 0x20
-#define REG_B_UIE 0x10
-#define REG_B_SQWE 0x08
-#define REG_B_DM 0x04
-#define REG_B_24H 0x02
-
-#define REG_C_UF 0x10
-#define REG_C_IRQF 0x80
-#define REG_C_PF 0x40
-#define REG_C_AF 0x20
-
typedef struct RTCState {
ISADevice dev;
MemoryRegion io;
diff --git a/hw/mc146818rtc.h b/hw/mc146818rtc.h
index f1199300a4..f286b6a12a 100644
--- a/hw/mc146818rtc.h
+++ b/hw/mc146818rtc.h
@@ -2,8 +2,7 @@
#define MC146818RTC_H
#include "isa.h"
-
-#define RTC_ISA_IRQ 8
+#include "mc146818rtc_regs.h"
ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq);
void rtc_set_memory(ISADevice *dev, int addr, int val);
diff --git a/hw/mc146818rtc_regs.h b/hw/mc146818rtc_regs.h
new file mode 100644
index 0000000000..3ab37709f0
--- /dev/null
+++ b/hw/mc146818rtc_regs.h
@@ -0,0 +1,62 @@
+/*
+ * QEMU MC146818 RTC emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * 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.
+ */
+#ifndef RTC_REGS_H
+#define RTC_REGS_H
+
+#define RTC_ISA_IRQ 8
+
+#define RTC_SECONDS 0
+#define RTC_SECONDS_ALARM 1
+#define RTC_MINUTES 2
+#define RTC_MINUTES_ALARM 3
+#define RTC_HOURS 4
+#define RTC_HOURS_ALARM 5
+#define RTC_ALARM_DONT_CARE 0xC0
+
+#define RTC_DAY_OF_WEEK 6
+#define RTC_DAY_OF_MONTH 7
+#define RTC_MONTH 8
+#define RTC_YEAR 9
+
+#define RTC_REG_A 10
+#define RTC_REG_B 11
+#define RTC_REG_C 12
+#define RTC_REG_D 13
+
+#define REG_A_UIP 0x80
+
+#define REG_B_SET 0x80
+#define REG_B_PIE 0x40
+#define REG_B_AIE 0x20
+#define REG_B_UIE 0x10
+#define REG_B_SQWE 0x08
+#define REG_B_DM 0x04
+#define REG_B_24H 0x02
+
+#define REG_C_UF 0x10
+#define REG_C_IRQF 0x80
+#define REG_C_PF 0x40
+#define REG_C_AF 0x20
+
+#endif
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 3f99f9a7c2..ec5118f848 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -107,6 +107,9 @@ static void ioapic_init(GSIState *gsi_state)
} else {
dev = qdev_create(NULL, "ioapic");
}
+ /* FIXME: this should be under the piix3. */
+ object_property_add_child(object_resolve_path("i440fx", NULL),
+ "ioapic", OBJECT(dev), NULL);
qdev_init_nofail(dev);
d = sysbus_from_qdev(dev);
sysbus_mmio_map(d, 0, 0xfec00000);
diff --git a/hw/xen_console.c b/hw/xen_console.c
index edcb31ce66..3794b1972d 100644
--- a/hw/xen_console.c
+++ b/hw/xen_console.c
@@ -248,6 +248,9 @@ static void con_disconnect(struct XenDevice *xendev)
{
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+ if (!xendev->dev) {
+ return;
+ }
if (con->chr)
qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL);
xen_be_unbind_evtchn(&con->xendev);
diff --git a/hw/xen_disk.c b/hw/xen_disk.c
index 68fa36a1cf..9719395b09 100644
--- a/hw/xen_disk.c
+++ b/hw/xen_disk.c
@@ -330,6 +330,9 @@ static void qemu_aio_complete(void *opaque, int ret)
if (ioreq->aio_inflight > 0) {
return;
}
+ if (ioreq->postsync) {
+ bdrv_flush(ioreq->blkdev->bs);
+ }
ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
ioreq_unmap(ioreq);
@@ -376,9 +379,6 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
goto err;
}
- if (ioreq->postsync) {
- bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */
- }
qemu_aio_complete(ioreq, 0);
return 0;
@@ -584,10 +584,10 @@ static int blk_init(struct XenDevice *xendev)
}
/* read-only ? */
+ qflags = BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO;
if (strcmp(blkdev->mode, "w") == 0) {
- qflags = BDRV_O_RDWR;
+ qflags |= BDRV_O_RDWR;
} else {
- qflags = 0;
info |= VDISK_READONLY;
}
@@ -726,6 +726,7 @@ static void blk_disconnect(struct XenDevice *xendev)
if (!blkdev->dinfo) {
/* close/delete only if we created it ourself */
bdrv_close(blkdev->bs);
+ bdrv_detach_dev(blkdev->bs, blkdev);
bdrv_delete(blkdev->bs);
}
blkdev->bs = NULL;
diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
index bdc3c79462..802cae3a29 100644
--- a/libcacard/vcard_emul_nss.c
+++ b/libcacard/vcard_emul_nss.c
@@ -682,8 +682,19 @@ vcard_emul_event_thread(void *arg)
SECMODModule *module = (SECMODModule *)arg;
do {
+ /*
+ * XXX - the latency value doesn't matter one bit. you only get no
+ * blocking (flags |= CKF_DONT_BLOCK) or PKCS11_WAIT_LATENCY (==500),
+ * hard coded in coolkey. And it isn't coolkey's fault - the timeout
+ * value we pass get's dropped on the floor before C_WaitForSlotEvent
+ * is called.
+ */
slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500);
if (slot == NULL) {
+ /* this could be just a no event indication */
+ if (PORT_GetError() == SEC_ERROR_NO_EVENT) {
+ continue;
+ }
break;
}
vreader = vcard_emul_find_vreader_from_slot(slot);
@@ -994,10 +1005,10 @@ vcard_emul_init(const VCardEmulOptions *options)
SECMOD_GetReadLock(module_lock);
for (mlp = module_list; mlp; mlp = mlp->next) {
SECMODModule *module = mlp->module;
- PRBool has_emul_slots = PR_FALSE;
- if (module == NULL) {
- continue;
+ /* Ignore the internal module */
+ if (module == NULL || module == SECMOD_GetInternalModule()) {
+ continue;
}
for (i = 0; i < module->slotCount; i++) {
@@ -1007,15 +1018,22 @@ vcard_emul_init(const VCardEmulOptions *options)
if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) {
continue;
}
+ if (strcmp("E-Gate 0 0", PK11_GetSlotName(slot)) == 0) {
+ /*
+ * coolkey <= 1.1.0-20 emulates this reader if it can't find
+ * any hardware readers. This causes problems, warn user of
+ * problems.
+ */
+ fprintf(stderr, "known bad coolkey version - see "
+ "https://bugzilla.redhat.com/show_bug.cgi?id=802435\n");
+ continue;
+ }
vreader_emul = vreader_emul_new(slot, options->hw_card_type,
options->hw_type_params);
vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul,
vreader_emul_delete);
vreader_add_reader(vreader);
- has_readers = PR_TRUE;
- has_emul_slots = PR_TRUE;
-
if (PK11_IsPresent(slot)) {
VCard *vcard;
vcard = vcard_emul_mirror_card(vreader);
@@ -1024,12 +1042,10 @@ vcard_emul_init(const VCardEmulOptions *options)
vcard_free(vcard);
}
}
- if (has_emul_slots) {
- vcard_emul_new_event_thread(module);
- }
+ vcard_emul_new_event_thread(module);
}
SECMOD_ReleaseReadLock(module_lock);
- nss_emul_init = has_readers;
+ nss_emul_init = PR_TRUE;
return VCARD_EMUL_OK;
}
diff --git a/monitor.c b/monitor.c
index 2ff1e0b4d6..8946a100c0 100644
--- a/monitor.c
+++ b/monitor.c
@@ -4157,6 +4157,9 @@ static int check_client_args_type(const QDict *client_args,
case 'O':
assert(flags & QMP_ACCEPT_UNKNOWNS);
break;
+ case 'q':
+ /* Any QObject can be passed. */
+ break;
case '/':
case '.':
/*
diff --git a/osdep.h b/osdep.h
index 156666e0ee..428285c7e0 100644
--- a/osdep.h
+++ b/osdep.h
@@ -140,4 +140,6 @@ static inline void qemu_timersub(const struct timeval *val1,
#define qemu_timersub timersub
#endif
+void qemu_set_cloexec(int fd);
+
#endif
diff --git a/qapi-schema-test.json b/qapi-schema-test.json
index 8c7f9f79f4..9eae3501d7 100644
--- a/qapi-schema-test.json
+++ b/qapi-schema-test.json
@@ -8,7 +8,7 @@
# for testing nested structs
{ 'type': 'UserDefOne',
- 'data': { 'integer': 'int', 'string': 'str' } }
+ 'data': { 'integer': 'int', 'string': 'str', '*enum1': 'EnumOne' } }
{ 'type': 'UserDefTwo',
'data': { 'string': 'str',
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index e6b6152e08..74386b9b1b 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -22,16 +22,17 @@
typedef struct StackObject
{
- const QObject *obj;
- const QListEntry *entry;
+ QObject *obj;
+ const QListEntry *entry;
+ GHashTable *h;
} StackObject;
struct QmpInputVisitor
{
Visitor visitor;
- QObject *obj;
StackObject stack[QIV_STACK_SIZE];
int nb_stack;
+ bool strict;
};
static QmpInputVisitor *to_qiv(Visitor *v)
@@ -39,21 +40,18 @@ static QmpInputVisitor *to_qiv(Visitor *v)
return container_of(v, QmpInputVisitor, visitor);
}
-static const QObject *qmp_input_get_object(QmpInputVisitor *qiv,
- const char *name)
+static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
+ const char *name)
{
- const QObject *qobj;
-
- if (qiv->nb_stack == 0) {
- qobj = qiv->obj;
- } else {
- qobj = qiv->stack[qiv->nb_stack - 1].obj;
- }
+ QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj;
if (qobj) {
if (name && qobject_type(qobj) == QTYPE_QDICT) {
+ if (qiv->stack[qiv->nb_stack - 1].h) {
+ g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
+ }
return qdict_get(qobject_to_qdict(qobj), name);
- } else if (qiv->nb_stack > 0 && qobject_type(qobj) == QTYPE_QLIST) {
+ } else if (qiv->stack[qiv->nb_stack - 1].entry) {
return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
}
}
@@ -61,34 +59,57 @@ static const QObject *qmp_input_get_object(QmpInputVisitor *qiv,
return qobj;
}
-static void qmp_input_push(QmpInputVisitor *qiv, const QObject *obj, Error **errp)
+static void qdict_add_key(const char *key, QObject *obj, void *opaque)
{
- qiv->stack[qiv->nb_stack].obj = obj;
- if (qobject_type(obj) == QTYPE_QLIST) {
- qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj));
- }
- qiv->nb_stack++;
+ GHashTable *h = opaque;
+ g_hash_table_insert(h, (gpointer) key, NULL);
+}
+
+static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
+{
+ GHashTable *h;
if (qiv->nb_stack >= QIV_STACK_SIZE) {
error_set(errp, QERR_BUFFER_OVERRUN);
return;
}
+
+ qiv->stack[qiv->nb_stack].obj = obj;
+ qiv->stack[qiv->nb_stack].entry = NULL;
+ qiv->stack[qiv->nb_stack].h = NULL;
+
+ if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
+ h = g_hash_table_new(g_str_hash, g_str_equal);
+ qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
+ qiv->stack[qiv->nb_stack].h = h;
+ }
+
+ qiv->nb_stack++;
}
static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
{
- qiv->nb_stack--;
- if (qiv->nb_stack < 0) {
- error_set(errp, QERR_BUFFER_OVERRUN);
- return;
+ GHashTableIter iter;
+ gpointer key;
+
+ if (qiv->strict && qiv->stack[qiv->nb_stack - 1].h) {
+ g_hash_table_iter_init(&iter, qiv->stack[qiv->nb_stack - 1].h);
+ if (g_hash_table_iter_next(&iter, &key, NULL)) {
+ error_set(errp, QERR_QMP_EXTRA_MEMBER, (char *) key);
+ }
+ g_hash_table_unref(qiv->stack[qiv->nb_stack - 1].h);
}
+
+ assert(qiv->nb_stack > 0);
+ qiv->nb_stack--;
}
static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind,
const char *name, size_t size, Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
- const QObject *qobj = qmp_input_get_object(qiv, name);
+ QObject *qobj = qmp_input_get_object(qiv, name);
+ Error *err = NULL;
if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -96,8 +117,9 @@ static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind,
return;
}
- qmp_input_push(qiv, qobj, errp);
- if (error_is_set(errp)) {
+ qmp_input_push(qiv, qobj, &err);
+ if (err) {
+ error_propagate(errp, err);
return;
}
@@ -116,7 +138,7 @@ static void qmp_input_end_struct(Visitor *v, Error **errp)
static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
- const QObject *qobj = qmp_input_get_object(qiv, name);
+ QObject *qobj = qmp_input_get_object(qiv, name);
if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -133,18 +155,24 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
QmpInputVisitor *qiv = to_qiv(v);
GenericList *entry;
StackObject *so = &qiv->stack[qiv->nb_stack - 1];
+ bool first;
+
+ if (so->entry == NULL) {
+ so->entry = qlist_first(qobject_to_qlist(so->obj));
+ first = true;
+ } else {
+ so->entry = qlist_next(so->entry);
+ first = false;
+ }
if (so->entry == NULL) {
return NULL;
}
entry = g_malloc0(sizeof(*entry));
- if (*list) {
- so->entry = qlist_next(so->entry);
- if (so->entry == NULL) {
- g_free(entry);
- return NULL;
- }
+ if (first) {
+ *list = entry;
+ } else {
(*list)->next = entry;
}
@@ -162,7 +190,7 @@ static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
- const QObject *qobj = qmp_input_get_object(qiv, name);
+ QObject *qobj = qmp_input_get_object(qiv, name);
if (!qobj || qobject_type(qobj) != QTYPE_QINT) {
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -177,7 +205,7 @@ static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name,
Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
- const QObject *qobj = qmp_input_get_object(qiv, name);
+ QObject *qobj = qmp_input_get_object(qiv, name);
if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) {
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -192,7 +220,7 @@ static void qmp_input_type_str(Visitor *v, char **obj, const char *name,
Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
- const QObject *qobj = qmp_input_get_object(qiv, name);
+ QObject *qobj = qmp_input_get_object(qiv, name);
if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) {
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -207,7 +235,7 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
- const QObject *qobj = qmp_input_get_object(qiv, name);
+ QObject *qobj = qmp_input_get_object(qiv, name);
if (!qobj || qobject_type(qobj) != QTYPE_QFLOAT) {
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -222,7 +250,7 @@ static void qmp_input_start_optional(Visitor *v, bool *present,
const char *name, Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
- const QObject *qobj = qmp_input_get_object(qiv, name);
+ QObject *qobj = qmp_input_get_object(qiv, name);
if (!qobj) {
*present = false;
@@ -239,7 +267,7 @@ Visitor *qmp_input_get_visitor(QmpInputVisitor *v)
void qmp_input_visitor_cleanup(QmpInputVisitor *v)
{
- qobject_decref(v->obj);
+ qobject_decref(v->stack[0].obj);
g_free(v);
}
@@ -261,8 +289,18 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
v->visitor.type_number = qmp_input_type_number;
v->visitor.start_optional = qmp_input_start_optional;
- v->obj = obj;
- qobject_incref(v->obj);
+ qmp_input_push(v, obj, NULL);
+ qobject_incref(obj);
+
+ return v;
+}
+
+QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj)
+{
+ QmpInputVisitor *v;
+
+ v = qmp_input_visitor_new(obj);
+ v->strict = true;
return v;
}
diff --git a/qapi/qmp-input-visitor.h b/qapi/qmp-input-visitor.h
index 3f798f0335..e0a48a5f3b 100644
--- a/qapi/qmp-input-visitor.h
+++ b/qapi/qmp-input-visitor.h
@@ -20,6 +20,8 @@
typedef struct QmpInputVisitor QmpInputVisitor;
QmpInputVisitor *qmp_input_visitor_new(QObject *obj);
+QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj);
+
void qmp_input_visitor_cleanup(QmpInputVisitor *v);
Visitor *qmp_input_get_visitor(QmpInputVisitor *v);
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index e0697b0d0f..2bce9d5db1 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -199,14 +199,16 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
{
QStackEntry *e, *tmp;
+ /* The bottom QStackEntry, if any, owns the root QObject. See the
+ * qmp_output_push_obj() invocations in qmp_output_add_obj(). */
+ QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);
+
QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
QTAILQ_REMOVE(&v->stack, e, node);
- if (e->value) {
- qobject_decref(e->value);
- }
g_free(e);
}
+ qobject_decref(root);
g_free(v);
}
diff --git a/qemu-common.h b/qemu-common.h
index c9e96a808e..4647dd96e9 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -190,7 +190,6 @@ ssize_t qemu_send_full(int fd, const void *buf, size_t count, int flags)
QEMU_WARN_UNUSED_RESULT;
ssize_t qemu_recv_full(int fd, void *buf, size_t count, int flags)
QEMU_WARN_UNUSED_RESULT;
-void qemu_set_cloexec(int fd);
#ifndef _WIN32
int qemu_eventfd(int pipefd[2]);
diff --git a/qemu-options.hx b/qemu-options.hx
index 662f571527..fe88939805 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2715,6 +2715,14 @@ the @var{simple} tracing backend.
@end table
ETEXI
+DEF("qtest", HAS_ARG, QEMU_OPTION_qtest,
+ "-qtest CHR specify tracing options\n",
+ QEMU_ARCH_ALL)
+
+DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log,
+ "-qtest-log LOG specify tracing options\n",
+ QEMU_ARCH_ALL)
+
HXCOMM This is the last statement. Insert new options before this line!
STEXI
@end table
diff --git a/qemu-timer.c b/qemu-timer.c
index d7f56e55f9..80bcc563e0 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -397,7 +397,7 @@ int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time)
return qemu_timer_expired_ns(timer_head, current_time * timer_head->scale);
}
-static void qemu_run_timers(QEMUClock *clock)
+void qemu_run_timers(QEMUClock *clock)
{
QEMUTimer **ptimer_head, *ts;
int64_t current_time;
diff --git a/qemu-timer.h b/qemu-timer.h
index de17f3b1a1..661bbe76b2 100644
--- a/qemu-timer.h
+++ b/qemu-timer.h
@@ -59,6 +59,7 @@ int qemu_timer_pending(QEMUTimer *ts);
int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time);
uint64_t qemu_timer_expire_time_ns(QEMUTimer *ts);
+void qemu_run_timers(QEMUClock *clock);
void qemu_run_all_timers(void);
int qemu_alarm_pending(void);
void configure_alarms(char const *opt);
diff --git a/qmp-commands.hx b/qmp-commands.hx
index c626ba8d3d..944787161f 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -708,7 +708,7 @@ EQMP
},
{
.name = "transaction",
- .args_type = "actions:O",
+ .args_type = "actions:q",
.mhandler.cmd_new = qmp_marshal_input_transaction,
},
@@ -2125,7 +2125,7 @@ EQMP
{
.name = "qom-set",
- .args_type = "path:s,property:s,opts:O",
+ .args_type = "path:s,property:s,value:q",
.mhandler.cmd_new = qmp_qom_set,
},
diff --git a/qtest.c b/qtest.c
new file mode 100644
index 0000000000..cd7186c39c
--- /dev/null
+++ b/qtest.c
@@ -0,0 +1,443 @@
+/*
+ * Test Server
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qtest.h"
+#include "hw/qdev.h"
+#include "qemu-char.h"
+#include "ioport.h"
+#include "memory.h"
+#include "hw/irq.h"
+#include "sysemu.h"
+#include "cpus.h"
+
+#define MAX_IRQ 256
+
+const char *qtest_chrdev;
+const char *qtest_log;
+int qtest_allowed = 0;
+
+static DeviceState *irq_intercept_dev;
+static FILE *qtest_log_fp;
+static CharDriverState *qtest_chr;
+static GString *inbuf;
+static int irq_levels[MAX_IRQ];
+static struct timeval start_time;
+static bool qtest_opened;
+
+#define FMT_timeval "%ld.%06ld"
+
+/**
+ * QTest Protocol
+ *
+ * Line based protocol, request/response based. Server can send async messages
+ * so clients should always handle many async messages before the response
+ * comes in.
+ *
+ * Valid requests
+ *
+ * Clock management:
+ *
+ * The qtest client is completely in charge of the vm_clock. qtest commands
+ * let you adjust the value of the clock (monotonically). All the commands
+ * return the current value of the clock in nanoseconds.
+ *
+ * > clock_step
+ * < OK VALUE
+ *
+ * Advance the clock to the next deadline. Useful when waiting for
+ * asynchronous events.
+ *
+ * > clock_step NS
+ * < OK VALUE
+ *
+ * Advance the clock by NS nanoseconds.
+ *
+ * > clock_set NS
+ * < OK VALUE
+ *
+ * Advance the clock to NS nanoseconds (do nothing if it's already past).
+ *
+ * PIO and memory access:
+ *
+ * > outb ADDR VALUE
+ * < OK
+ *
+ * > outw ADDR VALUE
+ * < OK
+ *
+ * > outl ADDR VALUE
+ * < OK
+ *
+ * > inb ADDR
+ * < OK VALUE
+ *
+ * > inw ADDR
+ * < OK VALUE
+ *
+ * > inl ADDR
+ * < OK VALUE
+ *
+ * > read ADDR SIZE
+ * < OK DATA
+ *
+ * > write ADDR SIZE DATA
+ * < OK
+ *
+ * ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0.
+ *
+ * DATA is an arbitrarily long hex number prefixed with '0x'. If it's smaller
+ * than the expected size, the value will be zero filled at the end of the data
+ * sequence.
+ *
+ * IRQ management:
+ *
+ * > irq_intercept_in QOM-PATH
+ * < OK
+ *
+ * > irq_intercept_out QOM-PATH
+ * < OK
+ *
+ * Attach to the gpio-in (resp. gpio-out) pins exported by the device at
+ * QOM-PATH. When the pin is triggered, one of the following async messages
+ * will be printed to the qtest stream:
+ *
+ * IRQ raise NUM
+ * IRQ lower NUM
+ *
+ * where NUM is an IRQ number. For the PC, interrupts can be intercepted
+ * simply with "irq_intercept_in ioapic" (note that IRQ0 comes out with
+ * NUM=0 even though it is remapped to GSI 2).
+ */
+
+static int hex2nib(char ch)
+{
+ if (ch >= '0' && ch <= '9') {
+ return ch - '0';
+ } else if (ch >= 'a' && ch <= 'f') {
+ return 10 + (ch - 'a');
+ } else if (ch >= 'A' && ch <= 'F') {
+ return 10 + (ch - 'a');
+ } else {
+ return -1;
+ }
+}
+
+static void qtest_get_time(struct timeval *tv)
+{
+ gettimeofday(tv, NULL);
+ tv->tv_sec -= start_time.tv_sec;
+ tv->tv_usec -= start_time.tv_usec;
+ if (tv->tv_usec < 0) {
+ tv->tv_usec += 1000000;
+ tv->tv_sec -= 1;
+ }
+}
+
+static void qtest_send_prefix(CharDriverState *chr)
+{
+ struct timeval tv;
+
+ if (!qtest_log_fp || !qtest_opened) {
+ return;
+ }
+
+ qtest_get_time(&tv);
+ fprintf(qtest_log_fp, "[S +" FMT_timeval "] ",
+ tv.tv_sec, tv.tv_usec);
+}
+
+static void qtest_send(CharDriverState *chr, const char *fmt, ...)
+{
+ va_list ap;
+ char buffer[1024];
+ size_t len;
+
+ va_start(ap, fmt);
+ len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ va_end(ap);
+
+ qemu_chr_fe_write(chr, (uint8_t *)buffer, len);
+ if (qtest_log_fp && qtest_opened) {
+ fprintf(qtest_log_fp, "%s", buffer);
+ }
+}
+
+static void qtest_irq_handler(void *opaque, int n, int level)
+{
+ qemu_irq *old_irqs = opaque;
+ qemu_set_irq(old_irqs[n], level);
+
+ if (irq_levels[n] != level) {
+ CharDriverState *chr = qtest_chr;
+ irq_levels[n] = level;
+ qtest_send_prefix(chr);
+ qtest_send(chr, "IRQ %s %d\n",
+ level ? "raise" : "lower", n);
+ }
+}
+
+static void qtest_process_command(CharDriverState *chr, gchar **words)
+{
+ const gchar *command;
+
+ g_assert(words);
+
+ command = words[0];
+
+ if (qtest_log_fp) {
+ struct timeval tv;
+ int i;
+
+ qtest_get_time(&tv);
+ fprintf(qtest_log_fp, "[R +" FMT_timeval "]",
+ tv.tv_sec, tv.tv_usec);
+ for (i = 0; words[i]; i++) {
+ fprintf(qtest_log_fp, " %s", words[i]);
+ }
+ fprintf(qtest_log_fp, "\n");
+ }
+
+ g_assert(command);
+ if (strcmp(words[0], "irq_intercept_out") == 0
+ || strcmp(words[0], "irq_intercept_in") == 0) {
+ DeviceState *dev;
+
+ g_assert(words[1]);
+ dev = DEVICE(object_resolve_path(words[1], NULL));
+ if (!dev) {
+ qtest_send_prefix(chr);
+ qtest_send(chr, "FAIL Unknown device\n");
+ return;
+ }
+
+ if (irq_intercept_dev) {
+ qtest_send_prefix(chr);
+ if (irq_intercept_dev != dev) {
+ qtest_send(chr, "FAIL IRQ intercept already enabled\n");
+ } else {
+ qtest_send(chr, "OK\n");
+ }
+ return;
+ }
+
+ if (words[0][14] == 'o') {
+ qemu_irq_intercept_out(&dev->gpio_out, qtest_irq_handler, dev->num_gpio_out);
+ } else {
+ qemu_irq_intercept_in(dev->gpio_in, qtest_irq_handler, dev->num_gpio_in);
+ }
+ irq_intercept_dev = dev;
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+
+ } else if (strcmp(words[0], "outb") == 0 ||
+ strcmp(words[0], "outw") == 0 ||
+ strcmp(words[0], "outl") == 0) {
+ uint16_t addr;
+ uint32_t value;
+
+ g_assert(words[1] && words[2]);
+ addr = strtol(words[1], NULL, 0);
+ value = strtol(words[2], NULL, 0);
+
+ if (words[0][3] == 'b') {
+ cpu_outb(addr, value);
+ } else if (words[0][3] == 'w') {
+ cpu_outw(addr, value);
+ } else if (words[0][3] == 'l') {
+ cpu_outl(addr, value);
+ }
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+ } else if (strcmp(words[0], "inb") == 0 ||
+ strcmp(words[0], "inw") == 0 ||
+ strcmp(words[0], "inl") == 0) {
+ uint16_t addr;
+ uint32_t value = -1U;
+
+ g_assert(words[1]);
+ addr = strtol(words[1], NULL, 0);
+
+ if (words[0][2] == 'b') {
+ value = cpu_inb(addr);
+ } else if (words[0][2] == 'w') {
+ value = cpu_inw(addr);
+ } else if (words[0][2] == 'l') {
+ value = cpu_inl(addr);
+ }
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK 0x%04x\n", value);
+ } else if (strcmp(words[0], "read") == 0) {
+ uint64_t addr, len, i;
+ uint8_t *data;
+
+ g_assert(words[1] && words[2]);
+ addr = strtoul(words[1], NULL, 0);
+ len = strtoul(words[2], NULL, 0);
+
+ data = g_malloc(len);
+ cpu_physical_memory_read(addr, data, len);
+
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK 0x");
+ for (i = 0; i < len; i++) {
+ qtest_send(chr, "%02x", data[i]);
+ }
+ qtest_send(chr, "\n");
+
+ g_free(data);
+ } else if (strcmp(words[0], "write") == 0) {
+ uint64_t addr, len, i;
+ uint8_t *data;
+ size_t data_len;
+
+ g_assert(words[1] && words[2] && words[3]);
+ addr = strtoul(words[1], NULL, 0);
+ len = strtoul(words[2], NULL, 0);
+
+ data_len = strlen(words[3]);
+ if (data_len < 3) {
+ qtest_send(chr, "ERR invalid argument size\n");
+ return;
+ }
+
+ data = g_malloc(len);
+ for (i = 0; i < len; i++) {
+ if ((i * 2 + 4) <= data_len) {
+ data[i] = hex2nib(words[3][i * 2 + 2]) << 4;
+ data[i] |= hex2nib(words[3][i * 2 + 3]);
+ } else {
+ data[i] = 0;
+ }
+ }
+ cpu_physical_memory_write(addr, data, len);
+ g_free(data);
+
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+ } else if (strcmp(words[0], "clock_step") == 0) {
+ int64_t ns;
+
+ if (words[1]) {
+ ns = strtoll(words[1], NULL, 0);
+ } else {
+ ns = qemu_clock_deadline(vm_clock);
+ }
+ qtest_clock_warp(qemu_get_clock_ns(vm_clock) + ns);
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK %"PRIi64"\n", (int64_t)qemu_get_clock_ns(vm_clock));
+ } else if (strcmp(words[0], "clock_set") == 0) {
+ int64_t ns;
+
+ g_assert(words[1]);
+ ns = strtoll(words[1], NULL, 0);
+ qtest_clock_warp(ns);
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK %"PRIi64"\n", (int64_t)qemu_get_clock_ns(vm_clock));
+ } else {
+ qtest_send_prefix(chr);
+ qtest_send(chr, "FAIL Unknown command `%s'\n", words[0]);
+ }
+}
+
+static void qtest_process_inbuf(CharDriverState *chr, GString *inbuf)
+{
+ char *end;
+
+ while ((end = strchr(inbuf->str, '\n')) != NULL) {
+ size_t offset;
+ GString *cmd;
+ gchar **words;
+
+ offset = end - inbuf->str;
+
+ cmd = g_string_new_len(inbuf->str, offset);
+ g_string_erase(inbuf, 0, offset + 1);
+
+ words = g_strsplit(cmd->str, " ", 0);
+ qtest_process_command(chr, words);
+ g_strfreev(words);
+
+ g_string_free(cmd, TRUE);
+ }
+}
+
+static void qtest_read(void *opaque, const uint8_t *buf, int size)
+{
+ CharDriverState *chr = opaque;
+
+ g_string_append_len(inbuf, (const gchar *)buf, size);
+ qtest_process_inbuf(chr, inbuf);
+}
+
+static int qtest_can_read(void *opaque)
+{
+ return 1024;
+}
+
+static void qtest_event(void *opaque, int event)
+{
+ int i;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ qemu_system_reset(false);
+ for (i = 0; i < ARRAY_SIZE(irq_levels); i++) {
+ irq_levels[i] = 0;
+ }
+ gettimeofday(&start_time, NULL);
+ qtest_opened = true;
+ if (qtest_log_fp) {
+ fprintf(qtest_log_fp, "[I " FMT_timeval "] OPENED\n",
+ start_time.tv_sec, start_time.tv_usec);
+ }
+ break;
+ case CHR_EVENT_CLOSED:
+ qtest_opened = false;
+ if (qtest_log_fp) {
+ struct timeval tv;
+ qtest_get_time(&tv);
+ fprintf(qtest_log_fp, "[I +" FMT_timeval "] CLOSED\n",
+ tv.tv_sec, tv.tv_usec);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+int qtest_init(void)
+{
+ CharDriverState *chr;
+
+ g_assert(qtest_chrdev != NULL);
+
+ configure_icount("0");
+ chr = qemu_chr_new("qtest", qtest_chrdev, NULL);
+
+ qemu_chr_add_handlers(chr, qtest_can_read, qtest_read, qtest_event, chr);
+ qemu_chr_fe_set_echo(chr, true);
+
+ inbuf = g_string_new("");
+
+ if (qtest_log) {
+ if (strcmp(qtest_log, "none") != 0) {
+ qtest_log_fp = fopen(qtest_log, "w+");
+ }
+ } else {
+ qtest_log_fp = stderr;
+ }
+
+ qtest_chr = chr;
+
+ return 0;
+}
diff --git a/qtest.h b/qtest.h
new file mode 100644
index 0000000000..1478343ff0
--- /dev/null
+++ b/qtest.h
@@ -0,0 +1,35 @@
+/*
+ * Test Server
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QTEST_H
+#define QTEST_H
+
+#include "qemu-common.h"
+
+extern int qtest_allowed;
+extern const char *qtest_chrdev;
+extern const char *qtest_log;
+
+static inline bool qtest_enabled(void)
+{
+ return qtest_allowed;
+}
+
+static inline int qtest_available(void)
+{
+ return 1;
+}
+
+int qtest_init(void);
+
+#endif
diff --git a/rules.mak b/rules.mak
index 04a91983ec..c30093c116 100644
--- a/rules.mak
+++ b/rules.mak
@@ -47,7 +47,7 @@ quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1))
cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \
>/dev/null 2>&1 && echo OK), $2, $3)
-VPATH_SUFFIXES = %.c %.h %.S %.m %.mak %.texi
+VPATH_SUFFIXES = %.c %.h %.S %.m %.mak %.texi %.sh
set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1)))
# find-in-path
diff --git a/scripts/create_config b/scripts/create_config
index 0098e683e2..470e05e397 100755
--- a/scripts/create_config
+++ b/scripts/create_config
@@ -16,7 +16,7 @@ case $line in
prefix=* | [a-z]*dir=*) # directory configuration
name=${line%=*}
value=${line#*=}
- define_name=`echo $name | tr '[:lower:]' '[:upper:]'`
+ define_name=`echo $name | LC_ALL=C tr '[a-z]' '[A-Z]'`
eval "define_value=\"$value\""
echo "#define CONFIG_QEMU_$define_name \"$define_value\""
# save for the next definitions
@@ -48,7 +48,7 @@ case $line in
;;
ARCH=*) # configuration
arch=${line#*=}
- arch_name=`echo $arch | tr '[:lower:]' '[:upper:]'`
+ arch_name=`echo $arch | LC_ALL=C tr '[a-z]' '[A-Z]'`
echo "#define HOST_$arch_name 1"
;;
HOST_USB=*)
@@ -73,7 +73,7 @@ case $line in
TARGET_BASE_ARCH=*) # configuration
target_base_arch=${line#*=}
if [ "$target_base_arch" != "$target_arch" ]; then
- base_arch_name=`echo $target_base_arch | tr '[:lower:]' '[:upper:]'`
+ base_arch_name=`echo $target_base_arch | LC_ALL=C tr '[a-z]' '[A-Z]'`
echo "#define TARGET_$base_arch_name 1"
fi
;;
diff --git a/scripts/gtester-cat b/scripts/gtester-cat
new file mode 100755
index 0000000000..061a952cad
--- /dev/null
+++ b/scripts/gtester-cat
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# Copyright IBM, Corp. 2012
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2 or later.
+# See the COPYING file in the top-level directory.
+
+cat <<EOF
+<?xml version="1.0"?>
+<gtester>
+ <info>
+ <package>qemu</package>
+ <version>0.0</version>
+ <revision>rev</revision>
+ </info>
+EOF
+
+sed \
+ -e '/<?xml/d' \
+ -e '/^<gtester>$/d' \
+ -e '/<info>/,/<\/info>/d' \
+ -e '$b' \
+ -e '/^<\/gtester>$/d' "$@"
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 30a24d211b..0b4f0a0fe1 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -140,7 +140,7 @@ v = qapi_dealloc_get_visitor(md);
''')
else:
ret += mcgen('''
-mi = qmp_input_visitor_new(%(obj)s);
+mi = qmp_input_visitor_new_strict(%(obj)s);
v = qmp_input_get_visitor(mi);
''',
obj=obj)
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 78c947cd9c..8d4e94a45f 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -61,7 +61,13 @@ def generate_visit_struct(name, members):
void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
{
+ if (error_is_set(errp)) {
+ return;
+ }
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), errp);
+ if (obj && !*obj) {
+ goto end;
+ }
''',
name=name)
push_indent()
@@ -69,6 +75,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
pop_indent()
ret += mcgen('''
+end:
visit_end_struct(m, errp);
}
''')
@@ -79,11 +86,14 @@ def generate_visit_list(name, members):
void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp)
{
- GenericList *i, **head = (GenericList **)obj;
+ GenericList *i, **prev = (GenericList **)obj;
+ if (error_is_set(errp)) {
+ return;
+ }
visit_start_list(m, name, errp);
- for (*head = i = visit_next_list(m, head, errp); i; i = visit_next_list(m, &i, errp)) {
+ for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) {
%(name)sList *native_i = (%(name)sList *)i;
visit_type_%(name)s(m, &native_i->value, NULL, errp);
}
@@ -112,7 +122,13 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
{
Error *err = NULL;
+ if (error_is_set(errp)) {
+ return;
+ }
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
+ if (obj && !*obj) {
+ goto end;
+ }
visit_type_%(name)sKind(m, &(*obj)->kind, "type", &err);
if (err) {
error_propagate(errp, err);
diff --git a/scripts/qtest b/scripts/qtest
new file mode 100755
index 0000000000..4ef6c1c500
--- /dev/null
+++ b/scripts/qtest
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+export QTEST_QEMU_BINARY=$1
+shift
+"$@"
diff --git a/scripts/tracetool b/scripts/tracetool
index 65bd0a1b4c..d675daba2b 100755
--- a/scripts/tracetool
+++ b/scripts/tracetool
@@ -410,7 +410,7 @@ linetoh_dtrace()
args=$(get_args "$1")
argnames=$(get_argnames "$1", ",")
- nameupper=`echo $name | tr '[:lower:]' '[:upper:]'`
+ nameupper=`echo $name | LC_ALL=C tr '[a-z]' '[A-Z]'`
# Define an empty function for the trace event
cat <<EOF
@@ -539,7 +539,7 @@ convert()
fi
if [ "$1" = "h" ]; then
name=$(get_name "$str")
- NAME=$(echo $name | tr '[:lower:]' '[:upper:]')
+ NAME=$(echo $name | LC_ALL=C tr '[a-z]' '[A-Z]')
echo "#define TRACE_${NAME}_ENABLED ${enabled}"
fi
done
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
new file mode 100644
index 0000000000..42d2a6b63b
--- /dev/null
+++ b/target-arm/cpu-qom.h
@@ -0,0 +1,71 @@
+/*
+ * QEMU ARM CPU
+ *
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see
+ * <http://www.gnu.org/licenses/gpl-2.0.html>
+ */
+#ifndef QEMU_ARM_CPU_QOM_H
+#define QEMU_ARM_CPU_QOM_H
+
+#include "qemu/cpu.h"
+#include "cpu.h"
+
+#define TYPE_ARM_CPU "arm-cpu"
+
+#define ARM_CPU_CLASS(klass) \
+ OBJECT_CLASS_CHECK(ARMCPUClass, (klass), TYPE_ARM_CPU)
+#define ARM_CPU(obj) \
+ OBJECT_CHECK(ARMCPU, (obj), TYPE_ARM_CPU)
+#define ARM_CPU_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(ARMCPUClass, (obj), TYPE_ARM_CPU)
+
+/**
+ * ARMCPUClass:
+ * @parent_reset: The parent class' reset handler.
+ *
+ * An ARM CPU model.
+ */
+typedef struct ARMCPUClass {
+ /*< private >*/
+ CPUClass parent_class;
+ /*< public >*/
+
+ void (*parent_reset)(CPUState *cpu);
+} ARMCPUClass;
+
+/**
+ * ARMCPU:
+ * @env: #CPUARMState
+ *
+ * An ARM CPU core.
+ */
+typedef struct ARMCPU {
+ /*< private >*/
+ CPUState parent_obj;
+ /*< public >*/
+
+ CPUARMState env;
+} ARMCPU;
+
+static inline ARMCPU *arm_env_get_cpu(CPUARMState *env)
+{
+ return ARM_CPU(container_of(env, ARMCPU, env));
+}
+
+#define ENV_GET_CPU(e) CPU(arm_env_get_cpu(e))
+
+
+#endif
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
new file mode 100644
index 0000000000..c3ed45b0bc
--- /dev/null
+++ b/target-arm/cpu.c
@@ -0,0 +1,60 @@
+/*
+ * QEMU ARM CPU
+ *
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see
+ * <http://www.gnu.org/licenses/gpl-2.0.html>
+ */
+
+#include "cpu-qom.h"
+#include "qemu-common.h"
+
+/* CPUClass::reset() */
+static void arm_cpu_reset(CPUState *s)
+{
+ ARMCPU *cpu = ARM_CPU(s);
+ ARMCPUClass *acc = ARM_CPU_GET_CLASS(cpu);
+
+ acc->parent_reset(s);
+
+ /* TODO Inline the current contents of cpu_state_reset(),
+ once cpu_reset_model_id() is eliminated. */
+ cpu_state_reset(&cpu->env);
+}
+
+static void arm_cpu_class_init(ObjectClass *oc, void *data)
+{
+ ARMCPUClass *acc = ARM_CPU_CLASS(oc);
+ CPUClass *cc = CPU_CLASS(acc);
+
+ acc->parent_reset = cc->reset;
+ cc->reset = arm_cpu_reset;
+}
+
+static const TypeInfo arm_cpu_type_info = {
+ .name = TYPE_ARM_CPU,
+ .parent = TYPE_CPU,
+ .instance_size = sizeof(ARMCPU),
+ .abstract = false,
+ .class_size = sizeof(ARMCPUClass),
+ .class_init = arm_cpu_class_init,
+};
+
+static void arm_cpu_register_types(void)
+{
+ type_register_static(&arm_cpu_type_info);
+}
+
+type_init(arm_cpu_register_types)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 26c114b6e5..e176c5f65c 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -238,7 +238,6 @@ typedef struct CPUARMState {
CPUARMState *cpu_arm_init(const char *cpu_model);
void arm_translate_init(void);
int cpu_arm_exec(CPUARMState *s);
-void cpu_arm_close(CPUARMState *s);
void do_interrupt(CPUARMState *);
void switch_mode(CPUARMState *, int);
uint32_t do_arm_semihosting(CPUARMState *env);
@@ -383,6 +382,7 @@ enum arm_features {
ARM_FEATURE_ARM_DIV, /* divide supported in ARM encoding */
ARM_FEATURE_VFP4, /* VFPv4 (implies that NEON is v2) */
ARM_FEATURE_GENERIC_TIMER,
+ ARM_FEATURE_MVFR, /* Media and VFP Feature Registers 0 and 1 */
};
static inline int arm_feature(CPUARMState *env, int feature)
@@ -476,6 +476,7 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp)
#endif
#include "cpu-all.h"
+#include "cpu-qom.h"
/* Bit usage in the TB flags field: */
#define ARM_TBFLAG_THUMB_SHIFT 0
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 1314f23d59..d974b579dc 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -254,6 +254,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
}
if (arm_feature(env, ARM_FEATURE_V6K)) {
set_feature(env, ARM_FEATURE_V6);
+ set_feature(env, ARM_FEATURE_MVFR);
}
if (arm_feature(env, ARM_FEATURE_V6)) {
set_feature(env, ARM_FEATURE_V5);
@@ -278,6 +279,10 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
}
}
+/* TODO Move contents into arm_cpu_reset() in cpu.c,
+ * once cpu_reset_model_id() is eliminated,
+ * and then forward to cpu_reset() here.
+ */
void cpu_state_reset(CPUARMState *env)
{
uint32_t id;
@@ -400,6 +405,7 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
CPUARMState *cpu_arm_init(const char *cpu_model)
{
+ ARMCPU *cpu;
CPUARMState *env;
uint32_t id;
static int inited = 0;
@@ -407,7 +413,8 @@ CPUARMState *cpu_arm_init(const char *cpu_model)
id = cpu_arm_find_by_name(cpu_model);
if (id == 0)
return NULL;
- env = g_malloc0(sizeof(CPUARMState));
+ cpu = ARM_CPU(object_new(TYPE_ARM_CPU));
+ env = &cpu->env;
cpu_exec_init(env);
if (tcg_enabled() && !inited) {
inited = 1;
@@ -493,11 +500,6 @@ static uint32_t cpu_arm_find_by_name(const char *name)
return id;
}
-void cpu_arm_close(CPUARMState *env)
-{
- g_free(env);
-}
-
static int bad_mode_switch(CPUARMState *env, int mode)
{
/* Return true if it is not valid for us to switch to
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 81725d1687..46d1d3ef9f 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -2906,7 +2906,7 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
case ARM_VFP_MVFR0:
case ARM_VFP_MVFR1:
if (IS_USER(s)
- || !arm_feature(env, ARM_FEATURE_VFP3))
+ || !arm_feature(env, ARM_FEATURE_MVFR))
return 1;
tmp = load_cpu_field(vfp.xregs[rn]);
break;
diff --git a/tests/Makefile b/tests/Makefile
index 94ea3421ad..a98a848ec9 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,58 +1,141 @@
export SRC_PATH
-CHECKS = check-qdict check-qfloat check-qint check-qstring check-qlist
-CHECKS += check-qjson test-qmp-output-visitor test-qmp-input-visitor
-CHECKS += test-string-input-visitor test-string-output-visitor test-coroutine
-CHECKS += test-qmp-commands
-CHECKS += $(SRC_PATH)/tests/qemu-iotests-quick.sh
-
-check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o test-coroutine.o: $(GENERATED_HEADERS)
-
-check-qint: check-qint.o qint.o $(tools-obj-y)
-check-qstring: check-qstring.o qstring.o $(tools-obj-y)
-check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(tools-obj-y)
-check-qlist: check-qlist.o qlist.o qint.o $(tools-obj-y)
-check-qfloat: check-qfloat.o qfloat.o $(tools-obj-y)
-check-qjson: check-qjson.o $(qobject-obj-y) $(tools-obj-y)
-test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(tools-obj-y)
-
-test-qmp-input-visitor.o test-qmp-output-visitor.o \
-test-string-input-visitor.o test-string-output-visitor.o \
- test-qmp-commands.o: QEMU_CFLAGS += -I $(qapi-dir)
-
-$(qapi-dir)/test-qapi-types.c $(qapi-dir)/test-qapi-types.h :\
+check-unit-y = tests/check-qdict$(EXESUF)
+check-unit-y += tests/check-qfloat$(EXESUF)
+check-unit-y += tests/check-qint$(EXESUF)
+check-unit-y += tests/check-qstring$(EXESUF)
+check-unit-y += tests/check-qlist$(EXESUF)
+check-unit-y += tests/check-qjson$(EXESUF)
+check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
+check-unit-y += tests/test-qmp-input-visitor$(EXESUF)
+check-unit-y += tests/test-qmp-input-strict$(EXESUF)
+check-unit-y += tests/test-qmp-commands$(EXESUF)
+check-unit-y += tests/test-string-input-visitor$(EXESUF)
+check-unit-y += tests/test-string-output-visitor$(EXESUF)
+check-unit-y += tests/test-coroutine$(EXESUF)
+
+check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
+
+# All QTests for now are POSIX-only, but the dependencies are
+# really in libqtest, not in the testcases themselves.
+check-qtest-i386-y = tests/rtc-test
+check-qtest-x86_64-y = $(check-qtest-i386-y)
+
+GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
+
+test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
+ tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \
+ tests/test-coroutine.o tests/test-string-output-visitor.o \
+ tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \
+ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \
+ tests/test-qmp-commands.o
+
+test-qapi-obj-y = $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y)
+test-qapi-obj-y += tests/test-qapi-visit.o tests/test-qapi-types.o
+test-qapi-obj-y += module.o
+
+$(test-obj-y): $(GENERATED_HEADERS)
+$(test-obj-y): QEMU_INCLUDES += -Itests
+
+tests/check-qint$(EXESUF): tests/check-qint.o qint.o $(tools-obj-y)
+tests/check-qstring$(EXESUF): tests/check-qstring.o qstring.o $(tools-obj-y)
+tests/check-qdict$(EXESUF): tests/check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(tools-obj-y)
+tests/check-qlist$(EXESUF): tests/check-qlist.o qlist.o qint.o $(tools-obj-y)
+tests/check-qfloat$(EXESUF): tests/check-qfloat.o qfloat.o $(tools-obj-y)
+tests/check-qjson$(EXESUF): tests/check-qjson.o $(qobject-obj-y) $(tools-obj-y)
+tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(coroutine-obj-y) $(tools-obj-y)
+
+tests/test-qapi-types.c tests/test-qapi-types.h :\
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
- $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
-$(qapi-dir)/test-qapi-visit.c $(qapi-dir)/test-qapi-visit.h :\
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o tests -p "test-" < $<, " GEN $@")
+tests/test-qapi-visit.c tests/test-qapi-visit.h :\
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py
- $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
-$(qapi-dir)/test-qmp-commands.h $(qapi-dir)/test-qmp-marshal.c :\
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o tests -p "test-" < $<, " GEN $@")
+tests/test-qmp-commands.h tests/test-qmp-marshal.c :\
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
- $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o tests -p "test-" < $<, " GEN $@")
-test-string-output-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-string-output-visitor: test-string-output-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y)
+tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y)
+tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y)
+tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y)
+tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y)
+tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y)
-test-string-input-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-string-input-visitor: test-string-input-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+tests/rtc-test$(EXESUF): tests/rtc-test.o $(trace-obj-y)
-test-qmp-output-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-qmp-output-visitor: test-qmp-output-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+# QTest rules
-test-qmp-input-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-qmp-input-visitor: test-qmp-input-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
+QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TARGET),))
+check-qtest-$(CONFIG_POSIX)=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y))
-test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
-test-qmp-commands: test-qmp-commands.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
+qtest-obj-y = tests/libqtest.o $(oslib-obj-y)
+$(check-qtest-y): $(qtest-obj-y)
-$(SRC_PATH)/tests/qemu-iotests-quick.sh: qemu-img qemu-io
+.PHONY: check-help
+check-help:
+ @echo "Regression testing targets:"
+ @echo
+ @echo " make check Run all tests"
+ @echo " make check-qtest-TARGET Run qtest tests for given target"
+ @echo " make check-qtest Run qtest tests"
+ @echo " make check-unit Run qobject tests"
+ @echo " make check-block Run block tests"
+ @echo " make check-report.html Generates an HTML test report"
+ @echo
+ @echo "Please note that HTML reports do not regenerate if the unit tests"
+ @echo "has not changed."
+ @echo
+ @echo "The variable SPEED can be set to control the gtester speed setting."
+ @echo "Default options are -k and (for make V=1) --verbose; they can be"
+ @echo "changed with variable GTESTER_OPTIONS."
+.SECONDARY:
-.PHONY: check check-block
+SPEED = quick
+GTESTER_OPTIONS = -k $(if $(V),--verbose,-q)
-check: $(CHECKS)
- $(call quiet-command, gtester $(CHECKS), " CHECK")
+# gtester tests, possibly with verbose output
-check-block:
- $(call quiet-command, $(SHELL) $(SRC_PATH)/tests/check-block.sh , " CHECK")
+.PHONY: $(patsubst %, check-qtest-%, $(QTEST_TARGETS))
+$(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y)
+ $(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
+ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@")
+
+.PHONY: $(patsubst %, check-%, $(check-unit-y))
+$(patsubst %, check-%, $(check-unit-y)): check-%: %
+ $(call quiet-command,gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER $*")
+
+# gtester tests with XML output
+
+$(patsubst %, check-report-qtest-%.xml, $(QTEST_TARGETS)): check-report-qtest-%.xml: $(check-qtest-y)
+ $(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
+ gtester -q $(GTESTER_OPTIONS) -o $@ -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@")
+
+check-report-unit.xml: $(check-unit-y)
+ $(call quiet-command,gtester -q $(GTESTER_OPTIONS) -o $@ -m=$(SPEED) $^, "GTESTER $@")
+
+# Reports and overall runs
+
+check-report.xml: $(patsubst %,check-report-qtest-%.xml, $(QTEST_TARGETS)) check-report-unit.xml
+ $(call quiet-command,$(SRC_PATH)/scripts/gtester-cat $^ > $@, " GEN $@")
+
+check-report.html: check-report.xml
+ $(call quiet-command,gtester-report $< > $@, " GEN $@")
+
+
+# Other tests
+
+.PHONY: check-tests/qemu-iotests-quick.sh
+check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF)
+ $<
+
+# Consolidated targets
+
+.PHONY: check-qtest check-unit check
+check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
+check-unit: $(patsubst %,check-%, $(check-unit-y))
+check-block: $(patsubst %,check-%, $(check-block-y))
+check: check-unit check-qtest
diff --git a/check-qdict.c b/tests/check-qdict.c
index fc0d276538..fc0d276538 100644
--- a/check-qdict.c
+++ b/tests/check-qdict.c
diff --git a/check-qfloat.c b/tests/check-qfloat.c
index cdc66ea10b..cdc66ea10b 100644
--- a/check-qfloat.c
+++ b/tests/check-qfloat.c
diff --git a/check-qint.c b/tests/check-qint.c
index 5a27119ae2..5a27119ae2 100644
--- a/check-qint.c
+++ b/tests/check-qint.c
diff --git a/check-qjson.c b/tests/check-qjson.c
index 526e25ef6d..526e25ef6d 100644
--- a/check-qjson.c
+++ b/tests/check-qjson.c
diff --git a/check-qlist.c b/tests/check-qlist.c
index 501ba262da..501ba262da 100644
--- a/check-qlist.c
+++ b/tests/check-qlist.c
diff --git a/check-qstring.c b/tests/check-qstring.c
index addad6c673..addad6c673 100644
--- a/check-qstring.c
+++ b/tests/check-qstring.c
diff --git a/tests/libqtest.c b/tests/libqtest.c
new file mode 100644
index 0000000000..d47969eae5
--- /dev/null
+++ b/tests/libqtest.c
@@ -0,0 +1,387 @@
+/*
+ * QTest
+ *
+ * Copyright IBM, Corp. 2012
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include "libqtest.h"
+
+#include <glib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "osdep.h"
+
+#define MAX_IRQ 256
+
+QTestState *global_qtest;
+
+struct QTestState
+{
+ int fd;
+ bool irq_level[MAX_IRQ];
+ GString *rx;
+ gchar *pid_file;
+};
+
+#define g_assert_no_errno(ret) do { \
+ g_assert_cmpint(ret, !=, -1); \
+} while (0)
+
+QTestState *qtest_init(const char *extra_args)
+{
+ QTestState *s;
+ struct sockaddr_un addr;
+ int sock, ret, i;
+ gchar *socket_path;
+ gchar *pid_file;
+ gchar *command;
+ const char *qemu_binary;
+ pid_t pid;
+ socklen_t addrlen;
+
+ qemu_binary = getenv("QTEST_QEMU_BINARY");
+ g_assert(qemu_binary != NULL);
+
+ socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
+ pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
+
+ s = g_malloc(sizeof(*s));
+
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ g_assert_no_errno(sock);
+
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
+ qemu_set_cloexec(sock);
+
+ do {
+ ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
+ } while (ret == -1 && errno == EINTR);
+ g_assert_no_errno(ret);
+ listen(sock, 1);
+
+ pid = fork();
+ if (pid == 0) {
+ command = g_strdup_printf("%s "
+ "-qtest unix:%s,nowait "
+ "-qtest-log /dev/null "
+ "-pidfile %s "
+ "-machine accel=qtest "
+ "%s", qemu_binary, socket_path,
+ pid_file,
+ extra_args ?: "");
+
+ ret = system(command);
+ exit(ret);
+ g_free(command);
+ }
+
+ do {
+ ret = accept(sock, (struct sockaddr *)&addr, &addrlen);
+ } while (ret == -1 && errno == EINTR);
+ g_assert_no_errno(ret);
+ close(sock);
+
+ s->fd = ret;
+ s->rx = g_string_new("");
+ s->pid_file = pid_file;
+ for (i = 0; i < MAX_IRQ; i++) {
+ s->irq_level[i] = false;
+ }
+
+ g_free(socket_path);
+
+ return s;
+}
+
+void qtest_quit(QTestState *s)
+{
+ FILE *f;
+ char buffer[1024];
+
+ f = fopen(s->pid_file, "r");
+ if (f) {
+ if (fgets(buffer, sizeof(buffer), f)) {
+ pid_t pid = atoi(buffer);
+ int status = 0;
+
+ kill(pid, SIGTERM);
+ waitpid(pid, &status, 0);
+ }
+
+ fclose(f);
+ }
+}
+
+static void qtest_sendf(QTestState *s, const char *fmt, ...)
+{
+ va_list ap;
+ gchar *str;
+ size_t size, offset;
+
+ va_start(ap, fmt);
+ str = g_strdup_vprintf(fmt, ap);
+ va_end(ap);
+ size = strlen(str);
+
+ offset = 0;
+ while (offset < size) {
+ ssize_t len;
+
+ len = write(s->fd, str + offset, size - offset);
+ if (len == -1 && errno == EINTR) {
+ continue;
+ }
+
+ g_assert_no_errno(len);
+ g_assert_cmpint(len, >, 0);
+
+ offset += len;
+ }
+}
+
+static GString *qtest_recv_line(QTestState *s)
+{
+ GString *line;
+ size_t offset;
+ char *eol;
+
+ while ((eol = strchr(s->rx->str, '\n')) == NULL) {
+ ssize_t len;
+ char buffer[1024];
+
+ len = read(s->fd, buffer, sizeof(buffer));
+ if (len == -1 && errno == EINTR) {
+ continue;
+ }
+
+ if (len == -1 || len == 0) {
+ fprintf(stderr, "Broken pipe\n");
+ exit(1);
+ }
+
+ g_string_append_len(s->rx, buffer, len);
+ }
+
+ offset = eol - s->rx->str;
+ line = g_string_new_len(s->rx->str, offset);
+ g_string_erase(s->rx, 0, offset + 1);
+
+ return line;
+}
+
+static gchar **qtest_rsp(QTestState *s, int expected_args)
+{
+ GString *line;
+ gchar **words;
+ int i;
+
+redo:
+ line = qtest_recv_line(s);
+ words = g_strsplit(line->str, " ", 0);
+ g_string_free(line, TRUE);
+
+ if (strcmp(words[0], "IRQ") == 0) {
+ int irq;
+
+ g_assert(words[1] != NULL);
+ g_assert(words[2] != NULL);
+
+ irq = strtoul(words[2], NULL, 0);
+ g_assert_cmpint(irq, >=, 0);
+ g_assert_cmpint(irq, <, MAX_IRQ);
+
+ if (strcmp(words[1], "raise") == 0) {
+ s->irq_level[irq] = true;
+ } else {
+ s->irq_level[irq] = false;
+ }
+
+ g_strfreev(words);
+ goto redo;
+ }
+
+ g_assert(words[0] != NULL);
+ g_assert_cmpstr(words[0], ==, "OK");
+
+ if (expected_args) {
+ for (i = 0; i < expected_args; i++) {
+ g_assert(words[i] != NULL);
+ }
+ } else {
+ g_strfreev(words);
+ }
+
+ return words;
+}
+
+const char *qtest_get_arch(void)
+{
+ const char *qemu = getenv("QTEST_QEMU_BINARY");
+ const char *end = strrchr(qemu, '/');
+
+ return end + strlen("/qemu-system-");
+}
+
+bool qtest_get_irq(QTestState *s, int num)
+{
+ /* dummy operation in order to make sure irq is up to date */
+ qtest_inb(s, 0);
+
+ return s->irq_level[num];
+}
+
+static int64_t qtest_clock_rsp(QTestState *s)
+{
+ gchar **words;
+ int64_t clock;
+ words = qtest_rsp(s, 2);
+ clock = g_ascii_strtoll(words[1], NULL, 0);
+ g_strfreev(words);
+ return clock;
+}
+
+int64_t qtest_clock_step_next(QTestState *s)
+{
+ qtest_sendf(s, "clock_step\n");
+ return qtest_clock_rsp(s);
+}
+
+int64_t qtest_clock_step(QTestState *s, int64_t step)
+{
+ qtest_sendf(s, "clock_step %"PRIi64"\n", step);
+ return qtest_clock_rsp(s);
+}
+
+int64_t qtest_clock_set(QTestState *s, int64_t val)
+{
+ qtest_sendf(s, "clock_set %"PRIi64"\n", val);
+ return qtest_clock_rsp(s);
+}
+
+void qtest_irq_intercept_out(QTestState *s, const char *qom_path)
+{
+ qtest_sendf(s, "irq_intercept_out %s\n", qom_path);
+ qtest_rsp(s, 0);
+}
+
+void qtest_irq_intercept_in(QTestState *s, const char *qom_path)
+{
+ qtest_sendf(s, "irq_intercept_in %s\n", qom_path);
+ qtest_rsp(s, 0);
+}
+
+static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value)
+{
+ qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value);
+ qtest_rsp(s, 0);
+}
+
+void qtest_outb(QTestState *s, uint16_t addr, uint8_t value)
+{
+ qtest_out(s, "outb", addr, value);
+}
+
+void qtest_outw(QTestState *s, uint16_t addr, uint16_t value)
+{
+ qtest_out(s, "outw", addr, value);
+}
+
+void qtest_outl(QTestState *s, uint16_t addr, uint32_t value)
+{
+ qtest_out(s, "outl", addr, value);
+}
+
+static uint32_t qtest_in(QTestState *s, const char *cmd, uint16_t addr)
+{
+ gchar **args;
+ uint32_t value;
+
+ qtest_sendf(s, "%s 0x%x\n", cmd, addr);
+ args = qtest_rsp(s, 2);
+ value = strtoul(args[1], NULL, 0);
+ g_strfreev(args);
+
+ return value;
+}
+
+uint8_t qtest_inb(QTestState *s, uint16_t addr)
+{
+ return qtest_in(s, "inb", addr);
+}
+
+uint16_t qtest_inw(QTestState *s, uint16_t addr)
+{
+ return qtest_in(s, "inw", addr);
+}
+
+uint32_t qtest_inl(QTestState *s, uint16_t addr)
+{
+ return qtest_in(s, "inl", addr);
+}
+
+static int hex2nib(char ch)
+{
+ if (ch >= '0' && ch <= '9') {
+ return ch - '0';
+ } else if (ch >= 'a' && ch <= 'f') {
+ return 10 + (ch - 'a');
+ } else if (ch >= 'A' && ch <= 'F') {
+ return 10 + (ch - 'a');
+ } else {
+ return -1;
+ }
+}
+
+void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size)
+{
+ uint8_t *ptr = data;
+ gchar **args;
+ size_t i;
+
+ qtest_sendf(s, "read 0x%x 0x%x\n", addr, size);
+ args = qtest_rsp(s, 2);
+
+ for (i = 0; i < size; i++) {
+ ptr[i] = hex2nib(args[1][2 + (i * 2)]) << 4;
+ ptr[i] |= hex2nib(args[1][2 + (i * 2) + 1]);
+ }
+
+ g_strfreev(args);
+}
+
+void qtest_add_func(const char *str, void (*fn))
+{
+ gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str);
+ g_test_add_func(path, fn);
+}
+
+void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
+{
+ const uint8_t *ptr = data;
+ size_t i;
+
+ qtest_sendf(s, "write 0x%x 0x%x 0x", addr, size);
+ for (i = 0; i < size; i++) {
+ qtest_sendf(s, "%02x", ptr[i]);
+ }
+ qtest_sendf(s, "\n");
+ qtest_rsp(s, 0);
+}
diff --git a/tests/libqtest.h b/tests/libqtest.h
new file mode 100644
index 0000000000..2ca85a9ee9
--- /dev/null
+++ b/tests/libqtest.h
@@ -0,0 +1,335 @@
+/*
+ * QTest
+ *
+ * Copyright IBM, Corp. 2012
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#ifndef LIBQTEST_H
+#define LIBQTEST_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+typedef struct QTestState QTestState;
+
+extern QTestState *global_qtest;
+
+/**
+ * qtest_init:
+ * @extra_args: other arguments to pass to QEMU.
+ */
+QTestState *qtest_init(const char *extra_args);
+
+/**
+ * qtest_quit:
+ * @s: QTestState instance to operate on.
+ *
+ * Shut down the QEMU process associated to @s.
+ */
+void qtest_quit(QTestState *s);
+
+/**
+ * qtest_get_irq:
+ * @s: QTestState instance to operate on.
+ * @num: Interrupt to observe.
+ *
+ * Return the level of the @num interrupt.
+ */
+bool qtest_get_irq(QTestState *s, int num);
+
+/**
+ * qtest_irq_intercept_in:
+ * @s: QTestState instance to operate on.
+ * @string: QOM path of a device.
+ *
+ * Associate qtest irqs with the GPIO-in pins of the device
+ * whose path is specified by @string.
+ */
+void qtest_irq_intercept_in(QTestState *s, const char *string);
+
+/**
+ * qtest_irq_intercept_out:
+ * @s: QTestState instance to operate on.
+ * @string: QOM path of a device.
+ *
+ * Associate qtest irqs with the GPIO-out pins of the device
+ * whose path is specified by @string.
+ */
+void qtest_irq_intercept_out(QTestState *s, const char *string);
+
+/**
+ * qtest_outb:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write an 8-bit value to an I/O port.
+ */
+void qtest_outb(QTestState *s, uint16_t addr, uint8_t value);
+
+/**
+ * qtest_outw:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write a 16-bit value to an I/O port.
+ */
+void qtest_outw(QTestState *s, uint16_t addr, uint16_t value);
+
+/**
+ * qtest_outl:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write a 32-bit value to an I/O port.
+ */
+void qtest_outl(QTestState *s, uint16_t addr, uint32_t value);
+
+/**
+ * qtest_inb:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns an 8-bit value from an I/O port.
+ */
+uint8_t qtest_inb(QTestState *s, uint16_t addr);
+
+/**
+ * qtest_inw:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns a 16-bit value from an I/O port.
+ */
+uint16_t qtest_inw(QTestState *s, uint16_t addr);
+
+/**
+ * qtest_inl:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns a 32-bit value from an I/O port.
+ */
+uint32_t qtest_inl(QTestState *s, uint16_t addr);
+
+/**
+ * qtest_memread:
+ * @s: QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @data: Pointer to where memory contents will be stored.
+ * @size: Number of bytes to read.
+ *
+ * Read guest memory into a buffer.
+ */
+void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size);
+
+/**
+ * qtest_memwrite:
+ * @s: QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes that will be written to guest memory.
+ * @size: Number of bytes to write.
+ *
+ * Write a buffer to guest memory.
+ */
+void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size);
+
+/**
+ * qtest_clock_step_next:
+ * @s: QTestState instance to operate on.
+ *
+ * Advance the vm_clock to the next deadline. Return the current
+ * value of the vm_clock in nanoseconds.
+ */
+int64_t qtest_clock_step_next(QTestState *s);
+
+/**
+ * qtest_clock_step:
+ * @s: QTestState instance to operate on.
+ * @step: Number of nanoseconds to advance the clock by.
+ *
+ * Advance the vm_clock by @step nanoseconds. Return the current
+ * value of the vm_clock in nanoseconds.
+ */
+int64_t qtest_clock_step(QTestState *s, int64_t step);
+
+/**
+ * qtest_clock_set:
+ * @s: QTestState instance to operate on.
+ * @val: Nanoseconds value to advance the clock to.
+ *
+ * Advance the vm_clock to @val nanoseconds since the VM was launched.
+ * Return the current value of the vm_clock in nanoseconds.
+ */
+int64_t qtest_clock_set(QTestState *s, int64_t val);
+
+/**
+ * qtest_get_arch:
+ *
+ * Returns the architecture for the QEMU executable under test.
+ */
+const char *qtest_get_arch(void);
+
+/**
+ * qtest_add_func:
+ * @str: Test case path.
+ * @fn: Test case function
+ *
+ * Add a GTester testcase with the given name and function.
+ * The path is prefixed with the architecture under test, as
+ * returned by qtest_get_arch.
+ */
+void qtest_add_func(const char *str, void (*fn));
+
+/**
+ * qtest_start:
+ * @args: other arguments to pass to QEMU
+ *
+ * Start QEMU and assign the resulting QTestState to a global variable.
+ * The global variable is used by "shortcut" macros documented below.
+ */
+#define qtest_start(args) ( \
+ global_qtest = qtest_init((args)) \
+ )
+
+/**
+ * get_irq:
+ * @num: Interrupt to observe.
+ *
+ * Return the level of the @num interrupt.
+ */
+#define get_irq(num) qtest_get_irq(global_qtest, num)
+
+/**
+ * irq_intercept_in:
+ * @string: QOM path of a device.
+ *
+ * Associate qtest irqs with the GPIO-in pins of the device
+ * whose path is specified by @string.
+ */
+#define irq_intercept_in(string) qtest_irq_intercept_in(global_qtest, string)
+
+/**
+ * qtest_irq_intercept_out:
+ * @string: QOM path of a device.
+ *
+ * Associate qtest irqs with the GPIO-out pins of the device
+ * whose path is specified by @string.
+ */
+#define irq_intercept_out(string) qtest_irq_intercept_out(global_qtest, string)
+
+/**
+ * outb:
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write an 8-bit value to an I/O port.
+ */
+#define outb(addr, val) qtest_outb(global_qtest, addr, val)
+
+/**
+ * outw:
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write a 16-bit value to an I/O port.
+ */
+#define outw(addr, val) qtest_outw(global_qtest, addr, val)
+
+/**
+ * outl:
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write a 32-bit value to an I/O port.
+ */
+#define outl(addr, val) qtest_outl(global_qtest, addr, val)
+
+/**
+ * inb:
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns an 8-bit value from an I/O port.
+ */
+#define inb(addr) qtest_inb(global_qtest, addr)
+
+/**
+ * inw:
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns a 16-bit value from an I/O port.
+ */
+#define inw(addr) qtest_inw(global_qtest, addr)
+
+/**
+ * inl:
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns a 32-bit value from an I/O port.
+ */
+#define inl(addr) qtest_inl(global_qtest, addr)
+
+/**
+ * memread:
+ * @addr: Guest address to read from.
+ * @data: Pointer to where memory contents will be stored.
+ * @size: Number of bytes to read.
+ *
+ * Read guest memory into a buffer.
+ */
+#define memread(addr, data, size) qtest_memread(global_qtest, addr, data, size)
+
+/**
+ * memwrite:
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes that will be written to guest memory.
+ * @size: Number of bytes to write.
+ *
+ * Write a buffer to guest memory.
+ */
+#define memwrite(addr, data, size) qtest_memwrite(global_qtest, addr, data, size)
+
+/**
+ * clock_step_next:
+ *
+ * Advance the vm_clock to the next deadline. Return the current
+ * value of the vm_clock in nanoseconds.
+ */
+#define clock_step_next() qtest_clock_step_next(global_qtest)
+
+/**
+ * clock_step:
+ * @step: Number of nanoseconds to advance the clock by.
+ *
+ * Advance the vm_clock by @step nanoseconds. Return the current
+ * value of the vm_clock in nanoseconds.
+ */
+#define clock_step(step) qtest_clock_step(global_qtest, step)
+
+/**
+ * clock_set:
+ * @val: Nanoseconds value to advance the clock to.
+ *
+ * Advance the vm_clock to @val nanoseconds since the VM was launched.
+ * Return the current value of the vm_clock in nanoseconds.
+ */
+#define clock_set(val) qtest_clock_set(global_qtest, val)
+
+#endif
diff --git a/tests/rtc-test.c b/tests/rtc-test.c
new file mode 100644
index 0000000000..983a980bab
--- /dev/null
+++ b/tests/rtc-test.c
@@ -0,0 +1,263 @@
+/*
+ * QTest testcase for the MC146818 real-time clock
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include "libqtest.h"
+#include "hw/mc146818rtc_regs.h"
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static uint8_t base = 0x70;
+
+static int bcd2dec(int value)
+{
+ return (((value >> 4) & 0x0F) * 10) + (value & 0x0F);
+}
+
+static int dec2bcd(int value)
+{
+ return ((value / 10) << 4) | (value % 10);
+}
+
+static uint8_t cmos_read(uint8_t reg)
+{
+ outb(base + 0, reg);
+ return inb(base + 1);
+}
+
+static void cmos_write(uint8_t reg, uint8_t val)
+{
+ outb(base + 0, reg);
+ outb(base + 1, val);
+}
+
+static int tm_cmp(struct tm *lhs, struct tm *rhs)
+{
+ time_t a, b;
+ struct tm d1, d2;
+
+ memcpy(&d1, lhs, sizeof(d1));
+ memcpy(&d2, rhs, sizeof(d2));
+
+ a = mktime(&d1);
+ b = mktime(&d2);
+
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ }
+
+ return 0;
+}
+
+#if 0
+static void print_tm(struct tm *tm)
+{
+ printf("%04d-%02d-%02d %02d:%02d:%02d\n",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff);
+}
+#endif
+
+static void cmos_get_date_time(struct tm *date)
+{
+ int base_year = 2000, hour_offset;
+ int sec, min, hour, mday, mon, year;
+ time_t ts;
+ struct tm dummy;
+
+ sec = cmos_read(RTC_SECONDS);
+ min = cmos_read(RTC_MINUTES);
+ hour = cmos_read(RTC_HOURS);
+ mday = cmos_read(RTC_DAY_OF_MONTH);
+ mon = cmos_read(RTC_MONTH);
+ year = cmos_read(RTC_YEAR);
+
+ if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) {
+ sec = bcd2dec(sec);
+ min = bcd2dec(min);
+ hour = bcd2dec(hour);
+ mday = bcd2dec(mday);
+ mon = bcd2dec(mon);
+ year = bcd2dec(year);
+ hour_offset = 80;
+ } else {
+ hour_offset = 0x80;
+ }
+
+ if ((cmos_read(0x0B) & REG_B_24H) == 0) {
+ if (hour >= hour_offset) {
+ hour -= hour_offset;
+ hour += 12;
+ }
+ }
+
+ ts = time(NULL);
+ localtime_r(&ts, &dummy);
+
+ date->tm_isdst = dummy.tm_isdst;
+ date->tm_sec = sec;
+ date->tm_min = min;
+ date->tm_hour = hour;
+ date->tm_mday = mday;
+ date->tm_mon = mon - 1;
+ date->tm_year = base_year + year - 1900;
+ date->tm_gmtoff = 0;
+
+ ts = mktime(date);
+}
+
+static void check_time(int wiggle)
+{
+ struct tm start, date[4], end;
+ struct tm *datep;
+ time_t ts;
+
+ /*
+ * This check assumes a few things. First, we cannot guarantee that we get
+ * a consistent reading from the wall clock because we may hit an edge of
+ * the clock while reading. To work around this, we read four clock readings
+ * such that at least two of them should match. We need to assume that one
+ * reading is corrupt so we need four readings to ensure that we have at
+ * least two consecutive identical readings
+ *
+ * It's also possible that we'll cross an edge reading the host clock so
+ * simply check to make sure that the clock reading is within the period of
+ * when we expect it to be.
+ */
+
+ ts = time(NULL);
+ gmtime_r(&ts, &start);
+
+ cmos_get_date_time(&date[0]);
+ cmos_get_date_time(&date[1]);
+ cmos_get_date_time(&date[2]);
+ cmos_get_date_time(&date[3]);
+
+ ts = time(NULL);
+ gmtime_r(&ts, &end);
+
+ if (tm_cmp(&date[0], &date[1]) == 0) {
+ datep = &date[0];
+ } else if (tm_cmp(&date[1], &date[2]) == 0) {
+ datep = &date[1];
+ } else if (tm_cmp(&date[2], &date[3]) == 0) {
+ datep = &date[2];
+ } else {
+ g_assert_not_reached();
+ }
+
+ if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) {
+ long t, s;
+
+ start.tm_isdst = datep->tm_isdst;
+
+ t = (long)mktime(datep);
+ s = (long)mktime(&start);
+ if (t < s) {
+ g_test_message("RTC is %ld second(s) behind wall-clock\n", (s - t));
+ } else {
+ g_test_message("RTC is %ld second(s) ahead of wall-clock\n", (t - s));
+ }
+
+ g_assert_cmpint(ABS(t - s), <=, wiggle);
+ }
+}
+
+static int wiggle = 2;
+
+static void bcd_check_time(void)
+{
+ /* Set BCD mode */
+ cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM);
+ check_time(wiggle);
+}
+
+static void dec_check_time(void)
+{
+ /* Set DEC mode */
+ cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_DM);
+ check_time(wiggle);
+}
+
+static void set_alarm_time(struct tm *tm)
+{
+ int sec;
+
+ sec = tm->tm_sec;
+
+ if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) {
+ sec = dec2bcd(sec);
+ }
+
+ cmos_write(RTC_SECONDS_ALARM, sec);
+ cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
+ cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
+}
+
+static void alarm_time(void)
+{
+ struct tm now;
+ time_t ts;
+ int i;
+
+ ts = time(NULL);
+ gmtime_r(&ts, &now);
+
+ /* set DEC mode */
+ cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_DM);
+
+ g_assert(!get_irq(RTC_ISA_IRQ));
+ cmos_read(RTC_REG_C);
+
+ now.tm_sec = (now.tm_sec + 2) % 60;
+ set_alarm_time(&now);
+ cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_AIE);
+
+ for (i = 0; i < 2 + wiggle; i++) {
+ if (get_irq(RTC_ISA_IRQ)) {
+ break;
+ }
+
+ clock_step(1000000000);
+ }
+
+ g_assert(get_irq(RTC_ISA_IRQ));
+ g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
+ g_assert(cmos_read(RTC_REG_C) == 0);
+}
+
+int main(int argc, char **argv)
+{
+ QTestState *s = NULL;
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ s = qtest_start("-display none -rtc clock=vm");
+ qtest_irq_intercept_in(s, "ioapic");
+
+ qtest_add_func("/rtc/bcd/check-time", bcd_check_time);
+ qtest_add_func("/rtc/dec/check-time", dec_check_time);
+ qtest_add_func("/rtc/alarm-time", alarm_time);
+ ret = g_test_run();
+
+ if (s) {
+ qtest_quit(s);
+ }
+
+ return ret;
+}
diff --git a/test-coroutine.c b/tests/test-coroutine.c
index e5d14eb696..e5d14eb696 100644
--- a/test-coroutine.c
+++ b/tests/test-coroutine.c
diff --git a/test-qmp-commands.c b/tests/test-qmp-commands.c
index 60cbf019bb..60cbf019bb 100644
--- a/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
new file mode 100644
index 0000000000..f6df8cbe1e
--- /dev/null
+++ b/tests/test-qmp-input-strict.c
@@ -0,0 +1,234 @@
+/*
+ * QMP Input Visitor unit-tests (strict mode).
+ *
+ * Copyright (C) 2011-2012 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ * Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <glib.h>
+#include <stdarg.h>
+
+#include "qapi/qmp-input-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestInputVisitorData {
+ QObject *obj;
+ QmpInputVisitor *qiv;
+} TestInputVisitorData;
+
+static void validate_teardown(TestInputVisitorData *data,
+ const void *unused)
+{
+ qobject_decref(data->obj);
+ data->obj = NULL;
+
+ if (data->qiv) {
+ qmp_input_visitor_cleanup(data->qiv);
+ data->qiv = NULL;
+ }
+}
+
+/* This is provided instead of a test setup function so that the JSON
+ string used by the tests are kept in the test functions (and not
+ int main()) */
+static GCC_FMT_ATTR(2, 3)
+Visitor *validate_test_init(TestInputVisitorData *data,
+ const char *json_string, ...)
+{
+ Visitor *v;
+ va_list ap;
+
+ va_start(ap, json_string);
+ data->obj = qobject_from_jsonv(json_string, &ap);
+ va_end(ap);
+
+ g_assert(data->obj != NULL);
+
+ data->qiv = qmp_input_visitor_new_strict(data->obj);
+ g_assert(data->qiv != NULL);
+
+ v = qmp_input_get_visitor(data->qiv);
+ g_assert(v != NULL);
+
+ return v;
+}
+
+typedef struct TestStruct
+{
+ int64_t integer;
+ bool boolean;
+ char *string;
+} TestStruct;
+
+static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
+ const char *name, Error **errp)
+{
+ visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
+ errp);
+
+ visit_type_int(v, &(*obj)->integer, "integer", errp);
+ visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
+ visit_type_str(v, &(*obj)->string, "string", errp);
+
+ visit_end_struct(v, errp);
+}
+
+static void test_validate_struct(TestInputVisitorData *data,
+ const void *unused)
+{
+ TestStruct *p = NULL;
+ Error *errp = NULL;
+ Visitor *v;
+
+ v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
+
+ visit_type_TestStruct(v, &p, NULL, &errp);
+ g_assert(!error_is_set(&errp));
+ g_free(p->string);
+ g_free(p);
+}
+
+static void test_validate_struct_nested(TestInputVisitorData *data,
+ const void *unused)
+{
+ UserDefNested *udp = NULL;
+ Error *errp = NULL;
+ Visitor *v;
+
+ v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string' }, 'string2': 'string2'}}}");
+
+ visit_type_UserDefNested(v, &udp, NULL, &errp);
+ g_assert(!error_is_set(&errp));
+ qapi_free_UserDefNested(udp);
+}
+
+static void test_validate_list(TestInputVisitorData *data,
+ const void *unused)
+{
+ UserDefOneList *head = NULL;
+ Error *errp = NULL;
+ Visitor *v;
+
+ v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]");
+
+ visit_type_UserDefOneList(v, &head, NULL, &errp);
+ g_assert(!error_is_set(&errp));
+ qapi_free_UserDefOneList(head);
+}
+
+static void test_validate_union(TestInputVisitorData *data,
+ const void *unused)
+{
+ UserDefUnion *tmp = NULL;
+ Visitor *v;
+ Error *errp = NULL;
+
+ v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }");
+
+ visit_type_UserDefUnion(v, &tmp, NULL, &errp);
+ g_assert(!error_is_set(&errp));
+ qapi_free_UserDefUnion(tmp);
+}
+
+static void test_validate_fail_struct(TestInputVisitorData *data,
+ const void *unused)
+{
+ TestStruct *p = NULL;
+ Error *errp = NULL;
+ Visitor *v;
+
+ v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }");
+
+ visit_type_TestStruct(v, &p, NULL, &errp);
+ g_assert(error_is_set(&errp));
+ if (p) {
+ g_free(p->string);
+ }
+ g_free(p);
+}
+
+static void test_validate_fail_struct_nested(TestInputVisitorData *data,
+ const void *unused)
+{
+ UserDefNested *udp = NULL;
+ Error *errp = NULL;
+ Visitor *v;
+
+ v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}");
+
+ visit_type_UserDefNested(v, &udp, NULL, &errp);
+ g_assert(error_is_set(&errp));
+ qapi_free_UserDefNested(udp);
+}
+
+static void test_validate_fail_list(TestInputVisitorData *data,
+ const void *unused)
+{
+ UserDefOneList *head = NULL;
+ Error *errp = NULL;
+ Visitor *v;
+
+ v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]");
+
+ visit_type_UserDefOneList(v, &head, NULL, &errp);
+ g_assert(error_is_set(&errp));
+ qapi_free_UserDefOneList(head);
+}
+
+static void test_validate_fail_union(TestInputVisitorData *data,
+ const void *unused)
+{
+ UserDefUnion *tmp = NULL;
+ Error *errp = NULL;
+ Visitor *v;
+
+ v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 }, 'extra': 'yyy' }");
+
+ visit_type_UserDefUnion(v, &tmp, NULL, &errp);
+ g_assert(error_is_set(&errp));
+ qapi_free_UserDefUnion(tmp);
+}
+
+static void validate_test_add(const char *testpath,
+ TestInputVisitorData *data,
+ void (*test_func)(TestInputVisitorData *data, const void *user_data))
+{
+ g_test_add(testpath, TestInputVisitorData, data, NULL, test_func,
+ validate_teardown);
+}
+
+int main(int argc, char **argv)
+{
+ TestInputVisitorData testdata;
+
+ g_test_init(&argc, &argv, NULL);
+
+ validate_test_add("/visitor/input-strict/pass/struct",
+ &testdata, test_validate_struct);
+ validate_test_add("/visitor/input-strict/pass/struct-nested",
+ &testdata, test_validate_struct_nested);
+ validate_test_add("/visitor/input-strict/pass/list",
+ &testdata, test_validate_list);
+ validate_test_add("/visitor/input-strict/pass/union",
+ &testdata, test_validate_union);
+ validate_test_add("/visitor/input-strict/fail/struct",
+ &testdata, test_validate_fail_struct);
+ validate_test_add("/visitor/input-strict/fail/struct-nested",
+ &testdata, test_validate_fail_struct_nested);
+ validate_test_add("/visitor/input-strict/fail/list",
+ &testdata, test_validate_fail_list);
+ validate_test_add("/visitor/input-strict/fail/union",
+ &testdata, test_validate_fail_union);
+
+ g_test_run();
+
+ return 0;
+}
diff --git a/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 1996e49576..c30fdc4e59 100644
--- a/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -258,6 +258,23 @@ static void input_visitor_test_add(const char *testpath,
visitor_input_teardown);
}
+static void test_visitor_in_errors(TestInputVisitorData *data,
+ const void *unused)
+{
+ TestStruct *p = NULL;
+ Error *errp = NULL;
+ Visitor *v;
+
+ v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', 'string': -42 }");
+
+ visit_type_TestStruct(v, &p, NULL, &errp);
+ g_assert(error_is_set(&errp));
+ g_assert(p->string == NULL);
+
+ g_free(p->string);
+ g_free(p);
+}
+
int main(int argc, char **argv)
{
TestInputVisitorData in_visitor_data;
@@ -282,6 +299,8 @@ int main(int argc, char **argv)
&in_visitor_data, test_visitor_in_list);
input_visitor_test_add("/visitor/input/union",
&in_visitor_data, test_visitor_in_union);
+ input_visitor_test_add("/visitor/input/errors",
+ &in_visitor_data, test_visitor_in_errors);
g_test_run();
diff --git a/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 4d6c4d4420..24a6359504 100644
--- a/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -274,6 +274,24 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
qapi_free_UserDefNested(ud2);
}
+static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
+ const void *unused)
+{
+ EnumOne bad_values[] = { ENUM_ONE_MAX, -1 };
+ UserDefOne u = { 0 }, *pu = &u;
+ Error *errp;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
+ errp = NULL;
+ u.has_enum1 = true;
+ u.enum1 = bad_values[i];
+ visit_type_UserDefOne(data->ov, &pu, "unused", &errp);
+ g_assert(error_is_set(&errp) == true);
+ error_free(errp);
+ }
+}
+
typedef struct TestStructList
{
TestStruct *value;
@@ -444,6 +462,8 @@ int main(int argc, char **argv)
&out_visitor_data, test_visitor_out_struct);
output_visitor_test_add("/visitor/output/struct-nested",
&out_visitor_data, test_visitor_out_struct_nested);
+ output_visitor_test_add("/visitor/output/struct-errors",
+ &out_visitor_data, test_visitor_out_struct_errors);
output_visitor_test_add("/visitor/output/list",
&out_visitor_data, test_visitor_out_list);
output_visitor_test_add("/visitor/output/list-qapi-free",
diff --git a/test-string-input-visitor.c b/tests/test-string-input-visitor.c
index 5370e32041..5370e32041 100644
--- a/test-string-input-visitor.c
+++ b/tests/test-string-input-visitor.c
diff --git a/test-string-output-visitor.c b/tests/test-string-output-visitor.c
index 22909b84ef..22909b84ef 100644
--- a/test-string-output-visitor.c
+++ b/tests/test-string-output-visitor.c
diff --git a/vl.c b/vl.c
index 0fccf50a5d..e575401d29 100644
--- a/vl.c
+++ b/vl.c
@@ -152,6 +152,7 @@ int main(int argc, char **argv)
#ifdef CONFIG_VIRTFS
#include "fsdev/qemu-fsdev.h"
#endif
+#include "qtest.h"
#include "disas.h"
@@ -1312,7 +1313,7 @@ int qemu_shutdown_requested(void)
void qemu_kill_report(void)
{
- if (shutdown_signal != -1) {
+ if (!qtest_enabled() && shutdown_signal != -1) {
fprintf(stderr, "qemu: terminating on signal %d", shutdown_signal);
if (shutdown_pid == 0) {
/* This happens for eg ^C at the terminal, so it's worth
@@ -2098,6 +2099,7 @@ static struct {
{ "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed },
{ "xen", "Xen", xen_available, xen_init, &xen_allowed },
{ "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed },
+ { "qtest", "QTest", qtest_available, qtest_init, &qtest_allowed },
};
static int configure_accelerator(void)
@@ -3181,6 +3183,12 @@ int main(int argc, char **argv, char **envp)
fclose(fp);
break;
}
+ case QEMU_OPTION_qtest:
+ qtest_chrdev = optarg;
+ break;
+ case QEMU_OPTION_qtest_log:
+ qtest_log = optarg;
+ break;
default:
os_parse_cmd_args(popt->index, optarg);
}