diff options
-rw-r--r-- | MAINTAINERS | 5 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | Makefile.objs | 2 | ||||
-rw-r--r-- | Makefile.target | 2 | ||||
-rw-r--r-- | QMP/qmp-spec.txt | 20 | ||||
-rwxr-xr-x | configure | 14 | ||||
-rw-r--r-- | cpu-exec.c | 1 | ||||
-rw-r--r-- | cpus.c | 82 | ||||
-rw-r--r-- | cpus.h | 2 | ||||
-rw-r--r-- | docs/qapi-code-gen.txt | 4 | ||||
-rw-r--r-- | hw/irq.c | 17 | ||||
-rw-r--r-- | hw/irq.h | 5 | ||||
-rw-r--r-- | hw/mc146818rtc.c | 33 | ||||
-rw-r--r-- | hw/mc146818rtc.h | 3 | ||||
-rw-r--r-- | hw/mc146818rtc_regs.h | 62 | ||||
-rw-r--r-- | hw/pc_piix.c | 3 | ||||
-rw-r--r-- | hw/xen_console.c | 3 | ||||
-rw-r--r-- | hw/xen_disk.c | 11 | ||||
-rw-r--r-- | libcacard/vcard_emul_nss.c | 36 | ||||
-rw-r--r-- | monitor.c | 3 | ||||
-rw-r--r-- | osdep.h | 2 | ||||
-rw-r--r-- | qapi-schema-test.json | 2 | ||||
-rw-r--r-- | qapi/qmp-input-visitor.c | 120 | ||||
-rw-r--r-- | qapi/qmp-input-visitor.h | 2 | ||||
-rw-r--r-- | qapi/qmp-output-visitor.c | 8 | ||||
-rw-r--r-- | qemu-common.h | 1 | ||||
-rw-r--r-- | qemu-options.hx | 8 | ||||
-rw-r--r-- | qemu-timer.c | 2 | ||||
-rw-r--r-- | qemu-timer.h | 1 | ||||
-rw-r--r-- | qmp-commands.hx | 4 | ||||
-rw-r--r-- | qtest.c | 443 | ||||
-rw-r--r-- | qtest.h | 35 | ||||
-rw-r--r-- | rules.mak | 2 | ||||
-rwxr-xr-x | scripts/create_config | 6 | ||||
-rwxr-xr-x | scripts/gtester-cat | 26 | ||||
-rw-r--r-- | scripts/qapi-commands.py | 2 | ||||
-rw-r--r-- | scripts/qapi-visit.py | 20 | ||||
-rwxr-xr-x | scripts/qtest | 5 | ||||
-rwxr-xr-x | scripts/tracetool | 4 | ||||
-rw-r--r-- | target-arm/cpu-qom.h | 71 | ||||
-rw-r--r-- | target-arm/cpu.c | 60 | ||||
-rw-r--r-- | target-arm/cpu.h | 3 | ||||
-rw-r--r-- | target-arm/helper.c | 14 | ||||
-rw-r--r-- | target-arm/translate.c | 2 | ||||
-rw-r--r-- | target-unicore32/cpu-qom.h | 59 | ||||
-rw-r--r-- | target-unicore32/cpu.c | 104 | ||||
-rw-r--r-- | target-unicore32/cpu.h | 4 | ||||
-rw-r--r-- | target-unicore32/helper.c | 65 | ||||
-rw-r--r-- | target-unicore32/helper.h | 3 | ||||
-rw-r--r-- | target-unicore32/op_helper.c | 3 | ||||
-rw-r--r-- | target-unicore32/translate.c | 3 | ||||
-rw-r--r-- | tests/Makefile | 167 | ||||
-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.c | 387 | ||||
-rw-r--r-- | tests/libqtest.h | 335 | ||||
-rw-r--r-- | tests/rtc-test.c | 263 | ||||
-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.c | 234 | ||||
-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.c | 10 |
69 files changed, 2596 insertions, 232 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index f83d07c2c4..922945c920 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -112,6 +112,11 @@ M: Blue Swirl <blauwirbel@gmail.com> S: Maintained F: target-sparc/ +UniCore32 +M: Guan Xuetao <gxt@mprc.pku.edu.cn> +S: Maintained +F: target-unicore32/ + X86 M: qemu-devel@nongnu.org S: Odd Fixes @@ -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 44b2e83e6f..9b0cf74c31 100644 --- a/Makefile.target +++ b/Makefile.target @@ -92,12 +92,14 @@ 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 endif libobj-$(TARGET_SPARC) += int32_helper.o libobj-$(TARGET_SPARC64) += int64_helper.o +libobj-$(TARGET_UNICORE32) += cpu.o libobj-$(TARGET_ALPHA) += int_helper.o fpu_helper.o sys_helper.o mem_helper.o libobj-y += disas.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 ------------------------------ @@ -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; @@ -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); } } @@ -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); } @@ -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); +} @@ -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; } @@ -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 '.': /* @@ -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 a6cd8e96fd..f72f9a0fdf 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2716,6 +2716,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 @@ -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/target-unicore32/cpu-qom.h b/target-unicore32/cpu-qom.h new file mode 100644 index 0000000000..342d85e39b --- /dev/null +++ b/target-unicore32/cpu-qom.h @@ -0,0 +1,59 @@ +/* + * QEMU UniCore32 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 version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ +#ifndef QEMU_UC32_CPU_QOM_H +#define QEMU_UC32_CPU_QOM_H + +#include "qemu/cpu.h" +#include "cpu.h" + +#define TYPE_UNICORE32_CPU "unicore32-cpu" + +#define UNICORE32_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(UniCore32CPUClass, (klass), TYPE_UNICORE32_CPU) +#define UNICORE32_CPU(obj) \ + OBJECT_CHECK(UniCore32CPU, (obj), TYPE_UNICORE32_CPU) +#define UNICORE32_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(UniCore32CPUClass, (obj), TYPE_UNICORE32_CPU) + +/** + * UniCore32CPUClass: + * + * A UniCore32 CPU model. + */ +typedef struct UniCore32CPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ +} UniCore32CPUClass; + +/** + * UniCore32CPU: + * @env: #CPUUniCore32State + * + * A UniCore32 CPU. + */ +typedef struct UniCore32CPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUUniCore32State env; +} UniCore32CPU; + +static inline UniCore32CPU *uc32_env_get_cpu(CPUUniCore32State *env) +{ + return UNICORE32_CPU(container_of(env, UniCore32CPU, env)); +} + +#define ENV_GET_CPU(e) CPU(uc32_env_get_cpu(e)) + + +#endif diff --git a/target-unicore32/cpu.c b/target-unicore32/cpu.c new file mode 100644 index 0000000000..de63f58dda --- /dev/null +++ b/target-unicore32/cpu.c @@ -0,0 +1,104 @@ +/* + * QEMU UniCore32 CPU + * + * Copyright (c) 2010-2011 GUAN Xue-tao + * 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 version 2 as + * published by the Free Software Foundation. + * + * Contributions from 2012-04-01 on are considered under GPL version 2, + * or (at your option) any later version. + */ + +#include "cpu-qom.h" +#include "qemu-common.h" + +static inline void set_feature(CPUUniCore32State *env, int feature) +{ + env->features |= feature; +} + +/* CPU models */ + +typedef struct UniCore32CPUInfo { + const char *name; + void (*instance_init)(Object *obj); +} UniCore32CPUInfo; + +static void unicore_ii_cpu_initfn(Object *obj) +{ + UniCore32CPU *cpu = UNICORE32_CPU(obj); + CPUUniCore32State *env = &cpu->env; + + env->cp0.c0_cpuid = 0x40010863; + + set_feature(env, UC32_HWCAP_CMOV); + set_feature(env, UC32_HWCAP_UCF64); + env->ucf64.xregs[UC32_UCF64_FPSCR] = 0; + env->cp0.c0_cachetype = 0x1dd20d2; + env->cp0.c1_sys = 0x00090078; +} + +static void uc32_any_cpu_initfn(Object *obj) +{ + UniCore32CPU *cpu = UNICORE32_CPU(obj); + CPUUniCore32State *env = &cpu->env; + + env->cp0.c0_cpuid = 0xffffffff; + + set_feature(env, UC32_HWCAP_CMOV); + set_feature(env, UC32_HWCAP_UCF64); +} + +static const UniCore32CPUInfo uc32_cpus[] = { + { .name = "UniCore-II", .instance_init = unicore_ii_cpu_initfn }, + { .name = "any", .instance_init = uc32_any_cpu_initfn }, +}; + +static void uc32_cpu_initfn(Object *obj) +{ + UniCore32CPU *cpu = UNICORE32_CPU(obj); + CPUUniCore32State *env = &cpu->env; + + cpu_exec_init(env); + env->cpu_model_str = object_get_typename(obj); + + env->uncached_asr = ASR_MODE_USER; + env->regs[31] = 0; + + tlb_flush(env, 1); +} + +static void uc32_register_cpu_type(const UniCore32CPUInfo *info) +{ + TypeInfo type_info = { + .name = info->name, + .parent = TYPE_UNICORE32_CPU, + .instance_init = info->instance_init, + }; + + type_register_static(&type_info); +} + +static const TypeInfo uc32_cpu_type_info = { + .name = TYPE_UNICORE32_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(UniCore32CPU), + .instance_init = uc32_cpu_initfn, + .abstract = true, + .class_size = sizeof(UniCore32CPUClass), +}; + +static void uc32_cpu_register_types(void) +{ + int i; + + type_register_static(&uc32_cpu_type_info); + for (i = 0; i < ARRAY_SIZE(uc32_cpus); i++) { + uc32_register_cpu_type(&uc32_cpus[i]); + } +} + +type_init(uc32_cpu_register_types) diff --git a/target-unicore32/cpu.h b/target-unicore32/cpu.h index a3f8589205..81c14ffd77 100644 --- a/target-unicore32/cpu.h +++ b/target-unicore32/cpu.h @@ -5,7 +5,8 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. */ #ifndef __CPU_UC32_H__ #define __CPU_UC32_H__ @@ -159,6 +160,7 @@ static inline void cpu_set_tls(CPUUniCore32State *env, target_ulong newtls) } #include "cpu-all.h" +#include "cpu-qom.h" #include "exec-all.h" static inline void cpu_pc_from_tb(CPUUniCore32State *env, TranslationBlock *tb) diff --git a/target-unicore32/helper.c b/target-unicore32/helper.c index 6af492daf5..9fe4a375e4 100644 --- a/target-unicore32/helper.c +++ b/target-unicore32/helper.c @@ -4,6 +4,9 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * Contributions from 2012-04-01 on are considered under GPL version 2, + * or (at your option) any later version. */ #include "cpu.h" @@ -11,75 +14,23 @@ #include "helper.h" #include "host-utils.h" -static inline void set_feature(CPUUniCore32State *env, int feature) -{ - env->features |= feature; -} - -struct uc32_cpu_t { - uint32_t id; - const char *name; -}; - -static const struct uc32_cpu_t uc32_cpu_names[] = { - { UC32_CPUID_UCV2, "UniCore-II"}, - { UC32_CPUID_ANY, "any"}, - { 0, NULL} -}; - -/* return 0 if not found */ -static uint32_t uc32_cpu_find_by_name(const char *name) -{ - int i; - uint32_t id; - - id = 0; - for (i = 0; uc32_cpu_names[i].name; i++) { - if (strcmp(name, uc32_cpu_names[i].name) == 0) { - id = uc32_cpu_names[i].id; - break; - } - } - return id; -} - CPUUniCore32State *uc32_cpu_init(const char *cpu_model) { + UniCore32CPU *cpu; CPUUniCore32State *env; - uint32_t id; static int inited = 1; - env = g_malloc0(sizeof(CPUUniCore32State)); - cpu_exec_init(env); - - id = uc32_cpu_find_by_name(cpu_model); - switch (id) { - case UC32_CPUID_UCV2: - set_feature(env, UC32_HWCAP_CMOV); - set_feature(env, UC32_HWCAP_UCF64); - env->ucf64.xregs[UC32_UCF64_FPSCR] = 0; - env->cp0.c0_cachetype = 0x1dd20d2; - env->cp0.c1_sys = 0x00090078; - break; - case UC32_CPUID_ANY: /* For userspace emulation. */ - set_feature(env, UC32_HWCAP_CMOV); - set_feature(env, UC32_HWCAP_UCF64); - break; - default: - cpu_abort(env, "Bad CPU ID: %x\n", id); + if (object_class_by_name(cpu_model) == NULL) { + return NULL; } - - env->cpu_model_str = cpu_model; - env->cp0.c0_cpuid = id; - env->uncached_asr = ASR_MODE_USER; - env->regs[31] = 0; + cpu = UNICORE32_CPU(object_new(cpu_model)); + env = &cpu->env; if (inited) { inited = 0; uc32_translate_init(); } - tlb_flush(env, 1); qemu_init_vcpu(env); return env; } diff --git a/target-unicore32/helper.h b/target-unicore32/helper.h index 615de2add9..5a3b8a41ea 100644 --- a/target-unicore32/helper.h +++ b/target-unicore32/helper.h @@ -3,7 +3,8 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. */ #include "def-helper.h" diff --git a/target-unicore32/op_helper.c b/target-unicore32/op_helper.c index 638a02097d..b954c30a84 100644 --- a/target-unicore32/op_helper.c +++ b/target-unicore32/op_helper.c @@ -5,7 +5,8 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. */ #include "cpu.h" #include "dyngen-exec.h" diff --git a/target-unicore32/translate.c b/target-unicore32/translate.c index 3b3ba163a6..9793d14c1b 100644 --- a/target-unicore32/translate.c +++ b/target-unicore32/translate.c @@ -5,7 +5,8 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. */ #include <stdarg.h> #include <stdlib.h> 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 @@ -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" @@ -1314,7 +1315,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 @@ -2100,6 +2101,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) @@ -3183,6 +3185,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); } |