diff options
202 files changed, 13523 insertions, 2987 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 6902a568f4..089b5af4d6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -451,10 +451,14 @@ F: hw/gpio/pl061.c F: hw/input/pl050.c F: hw/intc/pl190.c F: hw/sd/pl181.c +F: hw/ssi/pl022.c +F: include/hw/ssi/pl022.h F: hw/timer/pl031.c F: include/hw/arm/primecell.h F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h +F: hw/timer/cmsdk-apb-dualtimer.c +F: include/hw/timer/cmsdk-apb-dualtimer.h F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h F: hw/watchdog/cmsdk-apb-watchdog.c @@ -463,6 +467,8 @@ F: hw/misc/tz-ppc.c F: include/hw/misc/tz-ppc.h F: hw/misc/tz-mpc.c F: include/hw/misc/tz-mpc.h +F: hw/misc/tz-msc.c +F: include/hw/misc/tz-msc.h ARM cores M: Peter Maydell <peter.maydell@linaro.org> @@ -537,6 +543,10 @@ F: hw/misc/mps2-*.c F: include/hw/misc/mps2-*.h F: hw/arm/iotkit.c F: include/hw/arm/iotkit.h +F: hw/misc/iotkit-sysctl.c +F: include/hw/misc/iotkit-sysctl.h +F: hw/misc/iotkit-sysinfo.c +F: include/hw/misc/iotkit-sysinfo.h Musicpal M: Jan Kiszka <jan.kiszka@web.de> @@ -1696,7 +1706,6 @@ F: qom/ X: qom/cpu.c F: tests/check-qom-interface.c F: tests/check-qom-proplist.c -F: tests/qom-test.c QMP M: Markus Armbruster <armbru@redhat.com> @@ -1706,8 +1715,19 @@ F: monitor.c F: docs/devel/*qmp-* F: scripts/qmp/ F: tests/qmp-test.c +F: tests/qmp-cmd-test.c T: git git://repo.or.cz/qemu/armbru.git qapi-next +qtest +M: Paolo Bonzini <pbonzini@redhat.com> +M: Thomas Huth <thuth@redhat.com> +M: Laurent Vivier <lvivier@redhat.com> +S: Maintained +F: qtest.c +F: tests/libqtest.* +F: tests/libqos/ +F: tests/*-test.c + Register API M: Alistair Francis <alistair@alistair23.me> S: Maintained diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 38f468d8e2..de12f78eb8 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -1639,10 +1639,8 @@ static int kvm_init(MachineState *ms) s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; } -#ifdef KVM_CAP_READONLY_MEM kvm_readonly_mem_allowed = (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); -#endif kvm_eventfds_allowed = (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); @@ -1478,11 +1478,6 @@ static QDict *parse_json_filename(const char *filename, Error **errp) options_obj = qobject_from_json(filename, errp); if (!options_obj) { - /* Work around qobject_from_json() lossage TODO fix that */ - if (errp && !*errp) { - error_setg(errp, "Could not parse the JSON options"); - return NULL; - } error_prepend(errp, "Could not parse the JSON options: "); return NULL; } @@ -3612,6 +3612,7 @@ fi # libmpathpersist probe if test "$mpath" != "no" ; then + # probe for the new API cat > $TMPC <<EOF #include <libudev.h> #include <mpath_persist.h> @@ -3633,8 +3634,26 @@ int main(void) { EOF if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then mpathpersist=yes + mpathpersist_new_api=yes else - mpathpersist=no + # probe for the old API + cat > $TMPC <<EOF +#include <libudev.h> +#include <mpath_persist.h> +unsigned mpath_mx_alloc_len = 1024; +int logsink; +int main(void) { + struct udev *udev = udev_new(); + mpath_lib_init(udev); + return 0; +} +EOF + if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then + mpathpersist=yes + mpathpersist_new_api=no + else + mpathpersist=no + fi fi else mpathpersist=no @@ -6409,9 +6428,6 @@ if test "$bluez" = "yes" ; then echo "CONFIG_BLUEZ=y" >> $config_host_mak echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak fi -if test "$glib_subprocess" = "yes" ; then - echo "CONFIG_HAS_GLIB_SUBPROCESS_TESTS=y" >> $config_host_mak -fi if test "$gtk" = "yes" ; then echo "CONFIG_GTK=m" >> $config_host_mak echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak @@ -6495,6 +6511,9 @@ if test "$virtfs" = "yes" ; then fi if test "$mpath" = "yes" ; then echo "CONFIG_MPATH=y" >> $config_host_mak + if test "$mpathpersist_new_api" = "yes"; then + echo "CONFIG_MPATH_NEW_API=y" >> $config_host_mak + fi fi if test "$vhost_scsi" = "yes" ; then echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak diff --git a/cpus-common.c b/cpus-common.c index 59f751ecf9..98dd8c6ff1 100644 --- a/cpus-common.c +++ b/cpus-common.c @@ -84,7 +84,7 @@ void cpu_list_add(CPUState *cpu) } else { assert(!cpu_index_auto_assigned); } - QTAILQ_INSERT_TAIL(&cpus, cpu, node); + QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node); qemu_mutex_unlock(&qemu_cpu_list_lock); finish_safe_work(cpu); @@ -101,7 +101,7 @@ void cpu_list_remove(CPUState *cpu) assert(!(cpu_index_auto_assigned && cpu != QTAILQ_LAST(&cpus, CPUTailQ))); - QTAILQ_REMOVE(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus, cpu, node); cpu->cpu_index = UNASSIGNED_CPU_INDEX; qemu_mutex_unlock(&qemu_cpu_list_lock); } @@ -121,8 +121,6 @@ static bool all_cpu_threads_idle(void) /* Protected by TimersState seqlock */ static bool icount_sleep = true; -/* Conversion factor from emulated instructions to virtual clock ticks. */ -static int icount_time_shift; /* Arbitrarily pick 1MIPS as the minimum allowable speed. */ #define MAX_ICOUNT_SHIFT 10 @@ -131,20 +129,27 @@ typedef struct TimersState { int64_t cpu_ticks_prev; int64_t cpu_ticks_offset; - /* cpu_clock_offset can be read out of BQL, so protect it with - * this lock. + /* Protect fields that can be respectively read outside the + * BQL, and written from multiple threads. */ QemuSeqLock vm_clock_seqlock; - int64_t cpu_clock_offset; - int32_t cpu_ticks_enabled; - int64_t dummy; + QemuSpin vm_clock_lock; + + int16_t cpu_ticks_enabled; + + /* Conversion factor from emulated instructions to virtual clock ticks. */ + int16_t icount_time_shift; /* Compensate for varying guest execution speed. */ int64_t qemu_icount_bias; + + int64_t vm_clock_warp_start; + int64_t cpu_clock_offset; + /* Only written by TCG thread */ int64_t qemu_icount; + /* for adjusting icount */ - int64_t vm_clock_warp_start; QEMUTimer *icount_rt_timer; QEMUTimer *icount_vm_timer; QEMUTimer *icount_warp_timer; @@ -245,16 +250,19 @@ void cpu_update_icount(CPUState *cpu) int64_t executed = cpu_get_icount_executed(cpu); cpu->icount_budget -= executed; -#ifdef CONFIG_ATOMIC64 +#ifndef CONFIG_ATOMIC64 + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); +#endif atomic_set__nocheck(&timers_state.qemu_icount, - atomic_read__nocheck(&timers_state.qemu_icount) + - executed); -#else /* FIXME: we need 64bit atomics to do this safely */ - timers_state.qemu_icount += executed; + timers_state.qemu_icount + executed); +#ifndef CONFIG_ATOMIC64 + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); #endif } -int64_t cpu_get_icount_raw(void) +static int64_t cpu_get_icount_raw_locked(void) { CPUState *cpu = current_cpu; @@ -266,20 +274,30 @@ int64_t cpu_get_icount_raw(void) /* Take into account what has run */ cpu_update_icount(cpu); } -#ifdef CONFIG_ATOMIC64 + /* The read is protected by the seqlock, so __nocheck is okay. */ return atomic_read__nocheck(&timers_state.qemu_icount); -#else /* FIXME: we need 64bit atomics to do this safely */ - return timers_state.qemu_icount; -#endif } -/* Return the virtual CPU time, based on the instruction counter. */ static int64_t cpu_get_icount_locked(void) { - int64_t icount = cpu_get_icount_raw(); - return timers_state.qemu_icount_bias + cpu_icount_to_ns(icount); + int64_t icount = cpu_get_icount_raw_locked(); + return atomic_read__nocheck(&timers_state.qemu_icount_bias) + cpu_icount_to_ns(icount); } +int64_t cpu_get_icount_raw(void) +{ + int64_t icount; + unsigned start; + + do { + start = seqlock_read_begin(&timers_state.vm_clock_seqlock); + icount = cpu_get_icount_raw_locked(); + } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); + + return icount; +} + +/* Return the virtual CPU time, based on the instruction counter. */ int64_t cpu_get_icount(void) { int64_t icount; @@ -295,14 +313,29 @@ int64_t cpu_get_icount(void) int64_t cpu_icount_to_ns(int64_t icount) { - return icount << icount_time_shift; + return icount << atomic_read(&timers_state.icount_time_shift); +} + +static int64_t cpu_get_ticks_locked(void) +{ + int64_t ticks = timers_state.cpu_ticks_offset; + if (timers_state.cpu_ticks_enabled) { + ticks += cpu_get_host_ticks(); + } + + if (timers_state.cpu_ticks_prev > ticks) { + /* Non increasing ticks may happen if the host uses software suspend. */ + timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; + ticks = timers_state.cpu_ticks_prev; + } + + timers_state.cpu_ticks_prev = ticks; + return ticks; } /* return the time elapsed in VM between vm_start and vm_stop. Unless * icount is active, cpu_get_ticks() uses units of the host CPU cycle * counter. - * - * Caller must hold the BQL */ int64_t cpu_get_ticks(void) { @@ -312,19 +345,9 @@ int64_t cpu_get_ticks(void) return cpu_get_icount(); } - ticks = timers_state.cpu_ticks_offset; - if (timers_state.cpu_ticks_enabled) { - ticks += cpu_get_host_ticks(); - } - - if (timers_state.cpu_ticks_prev > ticks) { - /* Note: non increasing ticks may happen if the host uses - software suspend */ - timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; - ticks = timers_state.cpu_ticks_prev; - } - - timers_state.cpu_ticks_prev = ticks; + qemu_spin_lock(&timers_state.vm_clock_lock); + ticks = cpu_get_ticks_locked(); + qemu_spin_unlock(&timers_state.vm_clock_lock); return ticks; } @@ -361,14 +384,15 @@ int64_t cpu_get_clock(void) */ void cpu_enable_ticks(void) { - /* Here, the really thing protected by seqlock is cpu_clock_offset. */ - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (!timers_state.cpu_ticks_enabled) { timers_state.cpu_ticks_offset -= cpu_get_host_ticks(); timers_state.cpu_clock_offset -= get_clock(); timers_state.cpu_ticks_enabled = 1; } - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); } /* disable cpu_get_ticks() : the clock is stopped. You must not call @@ -377,14 +401,15 @@ void cpu_enable_ticks(void) */ void cpu_disable_ticks(void) { - /* Here, the really thing protected by seqlock is cpu_clock_offset. */ - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (timers_state.cpu_ticks_enabled) { timers_state.cpu_ticks_offset += cpu_get_host_ticks(); timers_state.cpu_clock_offset = cpu_get_clock_locked(); timers_state.cpu_ticks_enabled = 0; } - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); } /* Correlation between real and virtual time is always going to be @@ -407,7 +432,8 @@ static void icount_adjust(void) return; } - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); cur_time = cpu_get_clock_locked(); cur_icount = cpu_get_icount_locked(); @@ -415,20 +441,24 @@ static void icount_adjust(void) /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ if (delta > 0 && last_delta + ICOUNT_WOBBLE < delta * 2 - && icount_time_shift > 0) { + && timers_state.icount_time_shift > 0) { /* The guest is getting too far ahead. Slow time down. */ - icount_time_shift--; + atomic_set(&timers_state.icount_time_shift, + timers_state.icount_time_shift - 1); } if (delta < 0 && last_delta - ICOUNT_WOBBLE > delta * 2 - && icount_time_shift < MAX_ICOUNT_SHIFT) { + && timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) { /* The guest is getting too far behind. Speed time up. */ - icount_time_shift++; + atomic_set(&timers_state.icount_time_shift, + timers_state.icount_time_shift + 1); } last_delta = delta; - timers_state.qemu_icount_bias = cur_icount - - (timers_state.qemu_icount << icount_time_shift); - seqlock_write_end(&timers_state.vm_clock_seqlock); + atomic_set__nocheck(&timers_state.qemu_icount_bias, + cur_icount - (timers_state.qemu_icount + << timers_state.icount_time_shift)); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); } static void icount_adjust_rt(void *opaque) @@ -448,7 +478,8 @@ static void icount_adjust_vm(void *opaque) static int64_t qemu_icount_round(int64_t count) { - return (count + (1 << icount_time_shift) - 1) >> icount_time_shift; + int shift = atomic_read(&timers_state.icount_time_shift); + return (count + (1 << shift) - 1) >> shift; } static void icount_warp_rt(void) @@ -468,7 +499,8 @@ static void icount_warp_rt(void) return; } - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (runstate_is_running()) { int64_t clock = REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock_locked()); @@ -484,10 +516,12 @@ static void icount_warp_rt(void) int64_t delta = clock - cur_icount; warp_delta = MIN(warp_delta, delta); } - timers_state.qemu_icount_bias += warp_delta; + atomic_set__nocheck(&timers_state.qemu_icount_bias, + timers_state.qemu_icount_bias + warp_delta); } timers_state.vm_clock_warp_start = -1; - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) { qemu_clock_notify(QEMU_CLOCK_VIRTUAL); @@ -512,9 +546,12 @@ void qtest_clock_warp(int64_t dest) int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); int64_t warp = qemu_soonest_timeout(dest - clock, deadline); - seqlock_write_begin(&timers_state.vm_clock_seqlock); - timers_state.qemu_icount_bias += warp; - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + atomic_set__nocheck(&timers_state.qemu_icount_bias, + timers_state.qemu_icount_bias + warp); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); @@ -581,9 +618,12 @@ void qemu_start_warp_timer(void) * It is useful when we want a deterministic execution time, * isolated from host latencies. */ - seqlock_write_begin(&timers_state.vm_clock_seqlock); - timers_state.qemu_icount_bias += deadline; - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + atomic_set__nocheck(&timers_state.qemu_icount_bias, + timers_state.qemu_icount_bias + deadline); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); qemu_clock_notify(QEMU_CLOCK_VIRTUAL); } else { /* @@ -594,12 +634,14 @@ void qemu_start_warp_timer(void) * you will not be sending network packets continuously instead of * every 100ms. */ - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (timers_state.vm_clock_warp_start == -1 || timers_state.vm_clock_warp_start > clock) { timers_state.vm_clock_warp_start = clock; } - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); timer_mod_anticipate(timers_state.icount_warp_timer, clock + deadline); } @@ -700,7 +742,7 @@ static const VMStateDescription vmstate_timers = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_INT64(cpu_ticks_offset, TimersState), - VMSTATE_INT64(dummy, TimersState), + VMSTATE_UNUSED(8), VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), VMSTATE_END_OF_LIST() }, @@ -812,7 +854,7 @@ void configure_icount(QemuOpts *opts, Error **errp) } if (strcmp(option, "auto") != 0) { errno = 0; - icount_time_shift = strtol(option, &rem_str, 0); + timers_state.icount_time_shift = strtol(option, &rem_str, 0); if (errno != 0 || *rem_str != '\0' || !strlen(option)) { error_setg(errp, "icount: Invalid shift value"); } @@ -828,7 +870,7 @@ void configure_icount(QemuOpts *opts, Error **errp) /* 125MIPS seems a reasonable initial guess at the guest speed. It will be corrected fairly quickly anyway. */ - icount_time_shift = 3; + timers_state.icount_time_shift = 3; /* Have both realtime and virtual time triggers for speed adjustment. The realtime trigger catches emulated time passing too slowly, @@ -1491,7 +1533,7 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) atomic_mb_set(&cpu->exit_request, 0); } - qemu_tcg_rr_wait_io_event(cpu ? cpu : QTAILQ_FIRST(&cpus)); + qemu_tcg_rr_wait_io_event(cpu ? cpu : first_cpu); deal_with_unplugged_cpus(); } @@ -1762,10 +1804,16 @@ bool qemu_mutex_iothread_locked(void) return iothread_locked; } -void qemu_mutex_lock_iothread(void) +/* + * The BQL is taken from so many places that it is worth profiling the + * callers directly, instead of funneling them all through a single function. + */ +void qemu_mutex_lock_iothread_impl(const char *file, int line) { + QemuMutexLockFunc bql_lock = atomic_read(&qemu_bql_mutex_lock_func); + g_assert(!qemu_mutex_iothread_locked()); - qemu_mutex_lock(&qemu_global_mutex); + bql_lock(&qemu_global_mutex, file, line); iothread_locked = true; } diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index a92bc34fb2..0483d548d9 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -103,6 +103,7 @@ CONFIG_STM32F2XX_SPI=y CONFIG_STM32F205_SOC=y CONFIG_CMSDK_APB_TIMER=y +CONFIG_CMSDK_APB_DUALTIMER=y CONFIG_CMSDK_APB_UART=y CONFIG_CMSDK_APB_WATCHDOG=y @@ -110,9 +111,12 @@ CONFIG_MPS2_FPGAIO=y CONFIG_MPS2_SCC=y CONFIG_TZ_MPC=y +CONFIG_TZ_MSC=y CONFIG_TZ_PPC=y CONFIG_IOTKIT=y CONFIG_IOTKIT_SECCTL=y +CONFIG_IOTKIT_SYSCTL=y +CONFIG_IOTKIT_SYSINFO=y CONFIG_VERSATILE=y CONFIG_VERSATILE_PCI=y diff --git a/disas/m68k.c b/disas/m68k.c index a687df437c..0dc8aa1a3c 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -1623,6 +1623,7 @@ print_insn_arg (const char *d, case 'X': place = '8'; + /* fall through */ case 'Y': case 'Z': case 'W': diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 6ed3fce061..687570754d 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -240,10 +240,13 @@ should succeed even with the data missing. To support this the subsection can be connected to a device property and from there to a versioned machine type. -One important note is that the post_load() function is called "after" -loading all subsections, because a newer subsection could change same -value that it uses. A flag, and the combination of pre_load and post_load -can be used to detect whether a subsection was loaded, and to +The 'pre_load' and 'post_load' functions on subsections are only +called if the subsection is loaded. + +One important note is that the outer post_load() function is called "after" +loading all subsections, because a newer subsection could change the same +value that it uses. A flag, and the combination of outer pre_load and +post_load can be used to detect whether a subsection was loaded, and to fall back on default behaviour when the subsection isn't present. Example: @@ -315,8 +318,8 @@ For example: the property to false. c) Add a static bool support_foo function that tests the property. d) Add a subsection with a .needed set to the support_foo function - e) (potentially) Add a pre_load that sets up a default value for 'foo' - to be used if the subsection isn't loaded. + e) (potentially) Add an outer pre_load that sets up a default value + for 'foo' to be used if the subsection isn't loaded. Now that subsection will not be generated when using an older machine type and the migration stream will be accepted by older diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt index 1566b8ae5e..8f7da0245d 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.txt @@ -20,9 +20,9 @@ operating system. 2. Protocol Specification ========================= -This section details the protocol format. For the purpose of this document -"Client" is any application which is using QMP to communicate with QEMU and -"Server" is QEMU itself. +This section details the protocol format. For the purpose of this +document, "Server" is either QEMU or the QEMU Guest Agent, and +"Client" is any application communicating with it via QMP. JSON data structures, when mentioned in this document, are always in the following format: @@ -34,9 +34,8 @@ by the JSON standard: http://www.ietf.org/rfc/rfc7159.txt -The protocol is always encoded in UTF-8 except for synchronization -bytes (documented below); although thanks to json-string escape -sequences, the server will reply using only the strict ASCII subset. +The server expects its input to be encoded in UTF-8, and sends its +output encoded in ASCII. For convenience, json-object members mentioned in this document will be in a certain order. However, in real protocol usage they can be in @@ -215,16 +214,31 @@ Some events are rate-limited to at most one per second. If additional dropped, and the last one is delayed. "Similar" normally means same event type. See qmp-events.txt for details. -2.6 QGA Synchronization +2.6 Forcing the JSON parser into known-good state +------------------------------------------------- + +Incomplete or invalid input can leave the server's JSON parser in a +state where it can't parse additional commands. To get it back into +known-good state, the client should provoke a lexical error. + +The cleanest way to do that is sending an ASCII control character +other than '\t' (horizontal tab), '\r' (carriage return), or '\n' (new +line). + +Sadly, older versions of QEMU can fail to flag this as an error. If a +client needs to deal with them, it should send a 0xFF byte. + +2.7 QGA Synchronization ----------------------- -When using QGA, an additional synchronization feature is built into -the protocol. If the Client sends a raw 0xFF sentinel byte (not valid -JSON), then the Server will reset its state and discard all pending -data prior to the sentinel. Conversely, if the Client makes use of -the 'guest-sync-delimited' command, the Server will send a raw 0xFF -sentinel byte prior to its response, to aid the Client in discarding -any data prior to the sentinel. +When a client connects to QGA over a transport lacking proper +connection semantics such as virtio-serial, QGA may have read partial +input from a previous client. The client needs to force QGA's parser +into known-good state using the previous section's technique. +Moreover, the client may receive output a previous client didn't read. +To help with skipping that output, QGA provides the +'guest-sync-delimited' command. Refer to its documentation for +details. 3. QMP Examples @@ -1742,7 +1742,7 @@ static void dump_init(DumpState *s, int fd, bool has_format, warn_report("guest note is not present"); } else if (size < note_head_size || size > MAX_GUEST_NOTE_SIZE) { warn_report("guest note size is invalid: %" PRIu32, size); - } else if (format != VMCOREINFO_FORMAT_ELF) { + } else if (format != FW_CFG_VMCOREINFO_FORMAT_ELF) { warn_report("guest note format is unsupported: %" PRIu16, format); } else { s->guest_note = g_malloc(size + 1); /* +1 for adding \0 */ diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 7d63cffdeb..59ca356d0e 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1293,19 +1293,23 @@ float32 float64_to_float32(float64 a, float_status *s) * Arithmetic. */ -static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) +static FloatParts round_to_int(FloatParts a, int rmode, + int scale, float_status *s) { - if (is_nan(a.cls)) { + switch (a.cls) { + case float_class_qnan: + case float_class_snan: return return_nan(a, s); - } - switch (a.cls) { case float_class_zero: case float_class_inf: - case float_class_qnan: /* already "integral" */ break; + case float_class_normal: + scale = MIN(MAX(scale, -0x10000), 0x10000); + a.exp += scale; + if (a.exp >= DECOMPOSED_BINARY_POINT) { /* already integral */ break; @@ -1314,7 +1318,7 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) bool one; /* all fractional */ s->float_exception_flags |= float_flag_inexact; - switch (rounding_mode) { + switch (rmode) { case float_round_nearest_even: one = a.exp == -1 && a.frac > DECOMPOSED_IMPLICIT_BIT; break; @@ -1347,7 +1351,7 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) uint64_t rnd_mask = rnd_even_mask >> 1; uint64_t inc; - switch (rounding_mode) { + switch (rmode) { case float_round_nearest_even: inc = ((a.frac & rnd_even_mask) != frac_lsbm1 ? frac_lsbm1 : 0); break; @@ -1387,28 +1391,28 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) float16 float16_round_to_int(float16 a, float_status *s) { FloatParts pa = float16_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, s); + FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); return float16_round_pack_canonical(pr, s); } float32 float32_round_to_int(float32 a, float_status *s) { FloatParts pa = float32_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, s); + FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); return float32_round_pack_canonical(pr, s); } float64 float64_round_to_int(float64 a, float_status *s) { FloatParts pa = float64_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, s); + FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); return float64_round_pack_canonical(pr, s); } float64 float64_trunc_to_int(float64 a, float_status *s) { FloatParts pa = float64_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, float_round_to_zero, s); + FloatParts pr = round_to_int(pa, float_round_to_zero, 0, s); return float64_round_pack_canonical(pr, s); } @@ -1423,13 +1427,13 @@ float64 float64_trunc_to_int(float64 a, float_status *s) * is returned. */ -static int64_t round_to_int_and_pack(FloatParts in, int rmode, +static int64_t round_to_int_and_pack(FloatParts in, int rmode, int scale, int64_t min, int64_t max, float_status *s) { uint64_t r; int orig_flags = get_float_exception_flags(s); - FloatParts p = round_to_int(in, rmode, s); + FloatParts p = round_to_int(in, rmode, scale, s); switch (p.cls) { case float_class_snan: @@ -1469,38 +1473,158 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode, } } -#define FLOAT_TO_INT(fsz, isz) \ -int ## isz ## _t float ## fsz ## _to_int ## isz(float ## fsz a, \ - float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_int_and_pack(p, s->float_rounding_mode, \ - INT ## isz ## _MIN, INT ## isz ## _MAX,\ - s); \ -} \ - \ -int ## isz ## _t float ## fsz ## _to_int ## isz ## _round_to_zero \ - (float ## fsz a, float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_int_and_pack(p, float_round_to_zero, \ - INT ## isz ## _MIN, INT ## isz ## _MAX,\ - s); \ +int16_t float16_to_int16_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float16_unpack_canonical(a, s), + rmode, scale, INT16_MIN, INT16_MAX, s); +} + +int32_t float16_to_int32_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float16_unpack_canonical(a, s), + rmode, scale, INT32_MIN, INT32_MAX, s); +} + +int64_t float16_to_int64_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float16_unpack_canonical(a, s), + rmode, scale, INT64_MIN, INT64_MAX, s); +} + +int16_t float32_to_int16_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float32_unpack_canonical(a, s), + rmode, scale, INT16_MIN, INT16_MAX, s); +} + +int32_t float32_to_int32_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float32_unpack_canonical(a, s), + rmode, scale, INT32_MIN, INT32_MAX, s); +} + +int64_t float32_to_int64_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float32_unpack_canonical(a, s), + rmode, scale, INT64_MIN, INT64_MAX, s); +} + +int16_t float64_to_int16_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float64_unpack_canonical(a, s), + rmode, scale, INT16_MIN, INT16_MAX, s); +} + +int32_t float64_to_int32_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float64_unpack_canonical(a, s), + rmode, scale, INT32_MIN, INT32_MAX, s); +} + +int64_t float64_to_int64_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float64_unpack_canonical(a, s), + rmode, scale, INT64_MIN, INT64_MAX, s); +} + +int16_t float16_to_int16(float16 a, float_status *s) +{ + return float16_to_int16_scalbn(a, s->float_rounding_mode, 0, s); +} + +int32_t float16_to_int32(float16 a, float_status *s) +{ + return float16_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float16_to_int64(float16 a, float_status *s) +{ + return float16_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + +int16_t float32_to_int16(float32 a, float_status *s) +{ + return float32_to_int16_scalbn(a, s->float_rounding_mode, 0, s); +} + +int32_t float32_to_int32(float32 a, float_status *s) +{ + return float32_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float32_to_int64(float32 a, float_status *s) +{ + return float32_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + +int16_t float64_to_int16(float64 a, float_status *s) +{ + return float64_to_int16_scalbn(a, s->float_rounding_mode, 0, s); +} + +int32_t float64_to_int32(float64 a, float_status *s) +{ + return float64_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float64_to_int64(float64 a, float_status *s) +{ + return float64_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + +int16_t float16_to_int16_round_to_zero(float16 a, float_status *s) +{ + return float16_to_int16_scalbn(a, float_round_to_zero, 0, s); +} + +int32_t float16_to_int32_round_to_zero(float16 a, float_status *s) +{ + return float16_to_int32_scalbn(a, float_round_to_zero, 0, s); +} + +int64_t float16_to_int64_round_to_zero(float16 a, float_status *s) +{ + return float16_to_int64_scalbn(a, float_round_to_zero, 0, s); } -FLOAT_TO_INT(16, 16) -FLOAT_TO_INT(16, 32) -FLOAT_TO_INT(16, 64) +int16_t float32_to_int16_round_to_zero(float32 a, float_status *s) +{ + return float32_to_int16_scalbn(a, float_round_to_zero, 0, s); +} + +int32_t float32_to_int32_round_to_zero(float32 a, float_status *s) +{ + return float32_to_int32_scalbn(a, float_round_to_zero, 0, s); +} -FLOAT_TO_INT(32, 16) -FLOAT_TO_INT(32, 32) -FLOAT_TO_INT(32, 64) +int64_t float32_to_int64_round_to_zero(float32 a, float_status *s) +{ + return float32_to_int64_scalbn(a, float_round_to_zero, 0, s); +} -FLOAT_TO_INT(64, 16) -FLOAT_TO_INT(64, 32) -FLOAT_TO_INT(64, 64) +int16_t float64_to_int16_round_to_zero(float64 a, float_status *s) +{ + return float64_to_int16_scalbn(a, float_round_to_zero, 0, s); +} -#undef FLOAT_TO_INT +int32_t float64_to_int32_round_to_zero(float64 a, float_status *s) +{ + return float64_to_int32_scalbn(a, float_round_to_zero, 0, s); +} + +int64_t float64_to_int64_round_to_zero(float64 a, float_status *s) +{ + return float64_to_int64_scalbn(a, float_round_to_zero, 0, s); +} /* * Returns the result of converting the floating-point value `a' to @@ -1515,11 +1639,12 @@ FLOAT_TO_INT(64, 64) * flag. */ -static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, - float_status *s) +static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, int scale, + uint64_t max, float_status *s) { int orig_flags = get_float_exception_flags(s); - FloatParts p = round_to_int(in, rmode, s); + FloatParts p = round_to_int(in, rmode, scale, s); + uint64_t r; switch (p.cls) { case float_class_snan: @@ -1532,8 +1657,6 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, case float_class_zero: return 0; case float_class_normal: - { - uint64_t r; if (p.sign) { s->float_exception_flags = orig_flags | float_flag_invalid; return 0; @@ -1555,45 +1678,165 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, if (r > max) { s->float_exception_flags = orig_flags | float_flag_invalid; return max; - } else { - return r; } - } + return r; default: g_assert_not_reached(); } } -#define FLOAT_TO_UINT(fsz, isz) \ -uint ## isz ## _t float ## fsz ## _to_uint ## isz(float ## fsz a, \ - float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_uint_and_pack(p, s->float_rounding_mode, \ - UINT ## isz ## _MAX, s); \ -} \ - \ -uint ## isz ## _t float ## fsz ## _to_uint ## isz ## _round_to_zero \ - (float ## fsz a, float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_uint_and_pack(p, float_round_to_zero, \ - UINT ## isz ## _MAX, s); \ +uint16_t float16_to_uint16_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float16_unpack_canonical(a, s), + rmode, scale, UINT16_MAX, s); +} + +uint32_t float16_to_uint32_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float16_unpack_canonical(a, s), + rmode, scale, UINT32_MAX, s); +} + +uint64_t float16_to_uint64_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float16_unpack_canonical(a, s), + rmode, scale, UINT64_MAX, s); +} + +uint16_t float32_to_uint16_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float32_unpack_canonical(a, s), + rmode, scale, UINT16_MAX, s); +} + +uint32_t float32_to_uint32_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float32_unpack_canonical(a, s), + rmode, scale, UINT32_MAX, s); +} + +uint64_t float32_to_uint64_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float32_unpack_canonical(a, s), + rmode, scale, UINT64_MAX, s); +} + +uint16_t float64_to_uint16_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float64_unpack_canonical(a, s), + rmode, scale, UINT16_MAX, s); +} + +uint32_t float64_to_uint32_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float64_unpack_canonical(a, s), + rmode, scale, UINT32_MAX, s); } -FLOAT_TO_UINT(16, 16) -FLOAT_TO_UINT(16, 32) -FLOAT_TO_UINT(16, 64) +uint64_t float64_to_uint64_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float64_unpack_canonical(a, s), + rmode, scale, UINT64_MAX, s); +} + +uint16_t float16_to_uint16(float16 a, float_status *s) +{ + return float16_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint32_t float16_to_uint32(float16 a, float_status *s) +{ + return float16_to_uint32_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint64_t float16_to_uint64(float16 a, float_status *s) +{ + return float16_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint16_t float32_to_uint16(float32 a, float_status *s) +{ + return float32_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint32_t float32_to_uint32(float32 a, float_status *s) +{ + return float32_to_uint32_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint64_t float32_to_uint64(float32 a, float_status *s) +{ + return float32_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint16_t float64_to_uint16(float64 a, float_status *s) +{ + return float64_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint32_t float64_to_uint32(float64 a, float_status *s) +{ + return float64_to_uint32_scalbn(a, s->float_rounding_mode, 0, s); +} -FLOAT_TO_UINT(32, 16) -FLOAT_TO_UINT(32, 32) -FLOAT_TO_UINT(32, 64) +uint64_t float64_to_uint64(float64 a, float_status *s) +{ + return float64_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); +} -FLOAT_TO_UINT(64, 16) -FLOAT_TO_UINT(64, 32) -FLOAT_TO_UINT(64, 64) +uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *s) +{ + return float16_to_uint16_scalbn(a, float_round_to_zero, 0, s); +} -#undef FLOAT_TO_UINT +uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *s) +{ + return float16_to_uint32_scalbn(a, float_round_to_zero, 0, s); +} + +uint64_t float16_to_uint64_round_to_zero(float16 a, float_status *s) +{ + return float16_to_uint64_scalbn(a, float_round_to_zero, 0, s); +} + +uint16_t float32_to_uint16_round_to_zero(float32 a, float_status *s) +{ + return float32_to_uint16_scalbn(a, float_round_to_zero, 0, s); +} + +uint32_t float32_to_uint32_round_to_zero(float32 a, float_status *s) +{ + return float32_to_uint32_scalbn(a, float_round_to_zero, 0, s); +} + +uint64_t float32_to_uint64_round_to_zero(float32 a, float_status *s) +{ + return float32_to_uint64_scalbn(a, float_round_to_zero, 0, s); +} + +uint16_t float64_to_uint16_round_to_zero(float64 a, float_status *s) +{ + return float64_to_uint16_scalbn(a, float_round_to_zero, 0, s); +} + +uint32_t float64_to_uint32_round_to_zero(float64 a, float_status *s) +{ + return float64_to_uint32_scalbn(a, float_round_to_zero, 0, s); +} + +uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *s) +{ + return float64_to_uint64_scalbn(a, float_round_to_zero, 0, s); +} /* * Integer to float conversions @@ -1603,81 +1846,122 @@ FLOAT_TO_UINT(64, 64) * to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. */ -static FloatParts int_to_float(int64_t a, float_status *status) +static FloatParts int_to_float(int64_t a, int scale, float_status *status) { - FloatParts r = {}; + FloatParts r = { .sign = false }; + if (a == 0) { r.cls = float_class_zero; - r.sign = false; - } else if (a == (1ULL << 63)) { - r.cls = float_class_normal; - r.sign = true; - r.frac = DECOMPOSED_IMPLICIT_BIT; - r.exp = 63; } else { - uint64_t f; + uint64_t f = a; + int shift; + + r.cls = float_class_normal; if (a < 0) { - f = -a; + f = -f; r.sign = true; - } else { - f = a; - r.sign = false; } - int shift = clz64(f) - 1; - r.cls = float_class_normal; - r.exp = (DECOMPOSED_BINARY_POINT - shift); - r.frac = f << shift; + shift = clz64(f) - 1; + scale = MIN(MAX(scale, -0x10000), 0x10000); + + r.exp = DECOMPOSED_BINARY_POINT - shift + scale; + r.frac = (shift < 0 ? DECOMPOSED_IMPLICIT_BIT : f << shift); } return r; } -float16 int64_to_float16(int64_t a, float_status *status) +float16 int64_to_float16_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, status); + FloatParts pa = int_to_float(a, scale, status); return float16_round_pack_canonical(pa, status); } +float16 int32_to_float16_scalbn(int32_t a, int scale, float_status *status) +{ + return int64_to_float16_scalbn(a, scale, status); +} + +float16 int16_to_float16_scalbn(int16_t a, int scale, float_status *status) +{ + return int64_to_float16_scalbn(a, scale, status); +} + +float16 int64_to_float16(int64_t a, float_status *status) +{ + return int64_to_float16_scalbn(a, 0, status); +} + float16 int32_to_float16(int32_t a, float_status *status) { - return int64_to_float16(a, status); + return int64_to_float16_scalbn(a, 0, status); } float16 int16_to_float16(int16_t a, float_status *status) { - return int64_to_float16(a, status); + return int64_to_float16_scalbn(a, 0, status); } -float32 int64_to_float32(int64_t a, float_status *status) +float32 int64_to_float32_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, status); + FloatParts pa = int_to_float(a, scale, status); return float32_round_pack_canonical(pa, status); } +float32 int32_to_float32_scalbn(int32_t a, int scale, float_status *status) +{ + return int64_to_float32_scalbn(a, scale, status); +} + +float32 int16_to_float32_scalbn(int16_t a, int scale, float_status *status) +{ + return int64_to_float32_scalbn(a, scale, status); +} + +float32 int64_to_float32(int64_t a, float_status *status) +{ + return int64_to_float32_scalbn(a, 0, status); +} + float32 int32_to_float32(int32_t a, float_status *status) { - return int64_to_float32(a, status); + return int64_to_float32_scalbn(a, 0, status); } float32 int16_to_float32(int16_t a, float_status *status) { - return int64_to_float32(a, status); + return int64_to_float32_scalbn(a, 0, status); } -float64 int64_to_float64(int64_t a, float_status *status) +float64 int64_to_float64_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, status); + FloatParts pa = int_to_float(a, scale, status); return float64_round_pack_canonical(pa, status); } +float64 int32_to_float64_scalbn(int32_t a, int scale, float_status *status) +{ + return int64_to_float64_scalbn(a, scale, status); +} + +float64 int16_to_float64_scalbn(int16_t a, int scale, float_status *status) +{ + return int64_to_float64_scalbn(a, scale, status); +} + +float64 int64_to_float64(int64_t a, float_status *status) +{ + return int64_to_float64_scalbn(a, 0, status); +} + float64 int32_to_float64(int32_t a, float_status *status) { - return int64_to_float64(a, status); + return int64_to_float64_scalbn(a, 0, status); } float64 int16_to_float64(int16_t a, float_status *status) { - return int64_to_float64(a, status); + return int64_to_float64_scalbn(a, 0, status); } @@ -1689,73 +1973,120 @@ float64 int16_to_float64(int16_t a, float_status *status) * IEC/IEEE Standard for Binary Floating-Point Arithmetic. */ -static FloatParts uint_to_float(uint64_t a, float_status *status) +static FloatParts uint_to_float(uint64_t a, int scale, float_status *status) { - FloatParts r = { .sign = false}; + FloatParts r = { .sign = false }; if (a == 0) { r.cls = float_class_zero; } else { - int spare_bits = clz64(a) - 1; + scale = MIN(MAX(scale, -0x10000), 0x10000); r.cls = float_class_normal; - r.exp = DECOMPOSED_BINARY_POINT - spare_bits; - if (spare_bits < 0) { - shift64RightJamming(a, -spare_bits, &a); + if ((int64_t)a < 0) { + r.exp = DECOMPOSED_BINARY_POINT + 1 + scale; + shift64RightJamming(a, 1, &a); r.frac = a; } else { - r.frac = a << spare_bits; + int shift = clz64(a) - 1; + r.exp = DECOMPOSED_BINARY_POINT - shift + scale; + r.frac = a << shift; } } return r; } -float16 uint64_to_float16(uint64_t a, float_status *status) +float16 uint64_to_float16_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, status); + FloatParts pa = uint_to_float(a, scale, status); return float16_round_pack_canonical(pa, status); } +float16 uint32_to_float16_scalbn(uint32_t a, int scale, float_status *status) +{ + return uint64_to_float16_scalbn(a, scale, status); +} + +float16 uint16_to_float16_scalbn(uint16_t a, int scale, float_status *status) +{ + return uint64_to_float16_scalbn(a, scale, status); +} + +float16 uint64_to_float16(uint64_t a, float_status *status) +{ + return uint64_to_float16_scalbn(a, 0, status); +} + float16 uint32_to_float16(uint32_t a, float_status *status) { - return uint64_to_float16(a, status); + return uint64_to_float16_scalbn(a, 0, status); } float16 uint16_to_float16(uint16_t a, float_status *status) { - return uint64_to_float16(a, status); + return uint64_to_float16_scalbn(a, 0, status); } -float32 uint64_to_float32(uint64_t a, float_status *status) +float32 uint64_to_float32_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, status); + FloatParts pa = uint_to_float(a, scale, status); return float32_round_pack_canonical(pa, status); } +float32 uint32_to_float32_scalbn(uint32_t a, int scale, float_status *status) +{ + return uint64_to_float32_scalbn(a, scale, status); +} + +float32 uint16_to_float32_scalbn(uint16_t a, int scale, float_status *status) +{ + return uint64_to_float32_scalbn(a, scale, status); +} + +float32 uint64_to_float32(uint64_t a, float_status *status) +{ + return uint64_to_float32_scalbn(a, 0, status); +} + float32 uint32_to_float32(uint32_t a, float_status *status) { - return uint64_to_float32(a, status); + return uint64_to_float32_scalbn(a, 0, status); } float32 uint16_to_float32(uint16_t a, float_status *status) { - return uint64_to_float32(a, status); + return uint64_to_float32_scalbn(a, 0, status); } -float64 uint64_to_float64(uint64_t a, float_status *status) +float64 uint64_to_float64_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, status); + FloatParts pa = uint_to_float(a, scale, status); return float64_round_pack_canonical(pa, status); } +float64 uint32_to_float64_scalbn(uint32_t a, int scale, float_status *status) +{ + return uint64_to_float64_scalbn(a, scale, status); +} + +float64 uint16_to_float64_scalbn(uint16_t a, int scale, float_status *status) +{ + return uint64_to_float64_scalbn(a, scale, status); +} + +float64 uint64_to_float64(uint64_t a, float_status *status) +{ + return uint64_to_float64_scalbn(a, 0, status); +} + float64 uint32_to_float64(uint32_t a, float_status *status) { - return uint64_to_float64(a, status); + return uint64_to_float64_scalbn(a, 0, status); } float64 uint16_to_float64(uint16_t a, float_status *status) { - return uint64_to_float64(a, status); + return uint64_to_float64_scalbn(a, 0, status); } /* Float Min/Max */ diff --git a/fsdev/Makefile.objs b/fsdev/Makefile.objs index fb38017c0b..24bbb3e75c 100644 --- a/fsdev/Makefile.objs +++ b/fsdev/Makefile.objs @@ -1,7 +1,10 @@ # Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add. # only pull in the actual 9p backend if we also enabled virtio or xen. -common-obj-$(call land,$(CONFIG_VIRTFS),$(call lor,$(CONFIG_VIRTIO),$(CONFIG_XEN))) = qemu-fsdev.o 9p-marshal.o 9p-iov-marshal.o -common-obj-$(call lnot,$(call land,$(CONFIG_VIRTFS),$(call lor,$(CONFIG_VIRTIO),$(CONFIG_XEN)))) = qemu-fsdev-dummy.o +ifeq ($(call land,$(CONFIG_VIRTFS),$(call lor,$(CONFIG_VIRTIO_9P),$(CONFIG_XEN))),y) +common-obj-y = qemu-fsdev.o 9p-marshal.o 9p-iov-marshal.o +else +common-obj-y = qemu-fsdev-dummy.o +endif common-obj-y += qemu-fsdev-opts.o qemu-fsdev-throttle.o # Toplevel always builds this; targets without virtio will put it in diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 70639f656a..cbee8b944d 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -300,6 +300,28 @@ Show dynamic compiler opcode counters ETEXI { + .name = "sync-profile", + .args_type = "mean:-m,no_coalesce:-n,max:i?", + .params = "[-m] [-n] [max]", + .help = "show synchronization profiling info, up to max entries " + "(default: 10), sorted by total wait time. (-m: sort by " + "mean wait time; -n: do not coalesce objects with the " + "same call site)", + .cmd = hmp_info_sync_profile, + }, + +STEXI +@item info sync-profile [-m|-n] [@var{max}] +@findex info sync-profile +Show synchronization profiling info, up to @var{max} entries (default: 10), +sorted by total wait time. + -m: sort by mean wait time + -n: do not coalesce objects with the same call site +When different objects that share the same call site are coalesced, the "Object" +field shows---enclosed in brackets---the number of objects being coalesced. +ETEXI + + { .name = "kvm", .args_type = "", .params = "", diff --git a/hmp-commands.hx b/hmp-commands.hx index c1fc747403..db0c681f74 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -644,6 +644,21 @@ sendkey ctrl-alt-f1 This command is useful to send keys that your graphical user interface intercepts at low level, such as @code{ctrl-alt-f1} in X Window. ETEXI + { + .name = "sync-profile", + .args_type = "op:s?", + .params = "[on|off|reset]", + .help = "enable, disable or reset synchronization profiling. " + "With no arguments, prints whether profiling is on or off.", + .cmd = hmp_sync_profile, + }, + +STEXI +@item sync-profile [on|off|reset] +@findex sync-profile +Enable, disable or reset synchronization profiling. With no arguments, prints +whether profiling is on or off. +ETEXI { .name = "system_reset", @@ -327,6 +327,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS), params->compress_threads); + assert(params->has_compress_wait_thread); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD), + params->compress_wait_thread ? "on" : "off"); assert(params->has_decompress_threads); monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), @@ -339,6 +343,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT), params->cpu_throttle_increment); + assert(params->has_max_cpu_throttle); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE), + params->max_cpu_throttle); assert(params->has_tls_creds); monitor_printf(mon, "%s: '%s'\n", MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS), @@ -1062,6 +1070,30 @@ void hmp_stop(Monitor *mon, const QDict *qdict) qmp_stop(NULL); } +void hmp_sync_profile(Monitor *mon, const QDict *qdict) +{ + const char *op = qdict_get_try_str(qdict, "op"); + + if (op == NULL) { + bool on = qsp_is_enabled(); + + monitor_printf(mon, "sync-profile is %s\n", on ? "on" : "off"); + return; + } + if (!strcmp(op, "on")) { + qsp_enable(); + } else if (!strcmp(op, "off")) { + qsp_disable(); + } else if (!strcmp(op, "reset")) { + qsp_reset(); + } else { + Error *err = NULL; + + error_setg(&err, QERR_INVALID_PARAMETER, op); + hmp_handle_error(mon, &err); + } +} + void hmp_system_reset(Monitor *mon, const QDict *qdict) { qmp_system_reset(NULL); @@ -1623,6 +1655,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_compress_threads = true; visit_type_int(v, param, &p->compress_threads, &err); break; + case MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD: + p->has_compress_wait_thread = true; + visit_type_bool(v, param, &p->compress_wait_thread, &err); + break; case MIGRATION_PARAMETER_DECOMPRESS_THREADS: p->has_decompress_threads = true; visit_type_int(v, param, &p->decompress_threads, &err); @@ -1635,6 +1671,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_cpu_throttle_increment = true; visit_type_int(v, param, &p->cpu_throttle_increment, &err); break; + case MIGRATION_PARAMETER_MAX_CPU_THROTTLE: + p->has_max_cpu_throttle = true; + visit_type_int(v, param, &p->max_cpu_throttle, &err); + break; case MIGRATION_PARAMETER_TLS_CREDS: p->has_tls_creds = true; p->tls_creds = g_new0(StrOrNull, 1); @@ -42,6 +42,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict); void hmp_info_iothreads(Monitor *mon, const QDict *qdict); void hmp_quit(Monitor *mon, const QDict *qdict); void hmp_stop(Monitor *mon, const QDict *qdict); +void hmp_sync_profile(Monitor *mon, const QDict *qdict); void hmp_system_reset(Monitor *mon, const QDict *qdict); void hmp_system_powerdown(Monitor *mon, const QDict *qdict); void hmp_exit_preconfig(Monitor *mon, const QDict *qdict); diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 6404af5f33..e330f24c71 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -512,7 +512,7 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp) pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x09; - pm_smbus_init(DEVICE(dev), &s->smb); + pm_smbus_init(DEVICE(dev), &s->smb, true); memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1); memory_region_add_subregion(pci_address_space_io(dev), s->smb_io_base, &s->smb.io); diff --git a/hw/arm/boot.c b/hw/arm/boot.c index ca9467e583..20c71d7d96 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -736,6 +736,17 @@ static void do_cpu_reset(void *opaque) } } + if (!env->aarch64 && !info->secure_boot && + arm_feature(env, ARM_FEATURE_EL2)) { + /* + * This is an AArch32 boot not to Secure state, and + * we have Hyp mode available, so boot the kernel into + * Hyp mode. This is not how the CPU comes out of reset, + * so we need to manually put it there. + */ + cpsr_write(env, ARM_CPU_MODE_HYP, CPSR_M, CPSRWriteRaw); + } + if (cs == first_cpu) { AddressSpace *as = arm_boot_address_space(cpu, info); diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 258f470623..4b56bfa8d1 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -207,6 +207,10 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); sysbus_connect_irq(sbd, i, irq); sysbus_connect_irq(sbd, i + smp_cpus, qdev_get_gpio_in(d, ARM_CPU_FIQ)); + sysbus_connect_irq(sbd, i + 2 * smp_cpus, + qdev_get_gpio_in(d, ARM_CPU_VIRQ)); + sysbus_connect_irq(sbd, i + 3 * smp_cpus, + qdev_get_gpio_in(d, ARM_CPU_VFIQ)); } /* diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index d5e26855a5..7663ad6861 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -209,6 +209,10 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sbd, i, irq); irq = qdev_get_gpio_in(d, ARM_CPU_FIQ); sysbus_connect_irq(sbd, i + smp_cpus, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_VIRQ); + sysbus_connect_irq(sbd, i + 2 * smp_cpus, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_VFIQ); + sysbus_connect_irq(sbd, i + 3 * smp_cpus, irq); } /* diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 6d42fce2c3..fb9efa02c3 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -243,6 +243,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) int n; qemu_irq cpu_irq[4]; qemu_irq cpu_fiq[4]; + qemu_irq cpu_virq[4]; + qemu_irq cpu_vfiq[4]; MemoryRegion *sysram; MemoryRegion *dram; MemoryRegion *sysmem; @@ -282,6 +284,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) object_property_set_bool(cpuobj, true, "realized", &error_fatal); cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ); cpu_fiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ); + cpu_virq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VIRQ); + cpu_vfiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VFIQ); } sysmem = get_system_memory(); @@ -329,6 +333,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); sysbus_connect_irq(busdev, n + smp_cpus, cpu_fiq[n]); + sysbus_connect_irq(busdev, n + 2 * smp_cpus, cpu_virq[n]); + sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]); } for (n = 0; n < 128; n++) { diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 8cadc8b160..8742200fb4 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -16,9 +16,11 @@ #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/arm/iotkit.h" -#include "hw/misc/unimp.h" #include "hw/arm/arm.h" +/* Clock frequency in HZ of the 32KHz "slow clock" */ +#define S32KCLK (32 * 1000) + /* Create an alias region of @size bytes starting at @base * which mirrors the memory starting at @orig. */ @@ -138,8 +140,23 @@ static void iotkit_init(Object *obj) TYPE_CMSDK_APB_TIMER); sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), + TYPE_CMSDK_APB_TIMER); sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), - TYPE_UNIMPLEMENTED_DEVICE); + TYPE_CMSDK_APB_DUALTIMER); + sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog, + sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog, + sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog, + sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "iotkit-sysctl", &s->sysctl, + sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL); + sysbus_init_child_obj(obj, "iotkit-sysinfo", &s->sysinfo, + sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO); + object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, + sizeof(s->nmi_orgate), TYPE_OR_IRQ, + &error_abort, NULL); object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, &error_abort, NULL); @@ -154,8 +171,6 @@ static void iotkit_init(Object *obj) TYPE_SPLIT_IRQ, &error_abort, NULL); g_free(name); } - sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), - TYPE_UNIMPLEMENTED_DEVICE); } static void iotkit_exp_irq(void *opaque, int n, int level) @@ -390,13 +405,15 @@ static void iotkit_realize(DeviceState *dev, Error **errp) return; } - qdev_prop_set_string(DEVICE(&s->dualtimer), "name", "Dual timer"); - qdev_prop_set_uint64(DEVICE(&s->dualtimer), "size", 0x1000); + + qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq); object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); if (err) { error_propagate(errp, err); return; } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 5)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); if (err) { @@ -462,13 +479,14 @@ static void iotkit_realize(DeviceState *dev, Error **errp) /* Devices behind APB PPC1: * 0x4002f000: S32K timer */ - qdev_prop_set_string(DEVICE(&s->s32ktimer), "name", "S32KTIMER"); - qdev_prop_set_uint64(DEVICE(&s->s32ktimer), "size", 0x1000); + qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK); object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); if (err) { error_propagate(errp, err); return; } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 2)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); if (err) { @@ -501,19 +519,66 @@ static void iotkit_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in_named(dev_apb_ppc1, "cfg_sec_resp", 0)); - /* Using create_unimplemented_device() maps the stub into the - * system address space rather than into our container, but the - * overall effect to the guest is the same. - */ - create_unimplemented_device("SYSINFO", 0x40020000, 0x1000); + object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* System information registers */ + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000); + /* System control registers */ + object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000); - create_unimplemented_device("SYSCONTROL", 0x50021000, 0x1000); - create_unimplemented_device("S32KWATCHDOG", 0x5002e000, 0x1000); + /* This OR gate wires together outputs from the secure watchdogs to NMI */ + object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0, + qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0)); + + qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK); + object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000); /* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */ - create_unimplemented_device("NS watchdog", 0x40081000, 0x1000); - create_unimplemented_device("S watchdog", 0x50081000, 0x1000); + qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 1)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000); + + qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000); for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); @@ -602,6 +667,21 @@ static void iotkit_realize(DeviceState *dev, Error **errp) iotkit_forward_sec_resp_cfg(s); + /* Forward the MSC related signals */ + qdev_pass_gpios(dev_secctl, dev, "mscexp_status"); + qdev_pass_gpios(dev_secctl, dev, "mscexp_clear"); + qdev_pass_gpios(dev_secctl, dev, "mscexp_ns"); + qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 11)); + + /* + * Expose our container region to the board model; this corresponds + * to the AHB Slave Expansion ports which allow bus master devices + * (eg DMA controllers) in the board model to make transactions into + * devices in the IoTKit. + */ + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container); + system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; } diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index dc0f34abe5..6dd02ae47e 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -45,7 +45,10 @@ #include "hw/misc/mps2-scc.h" #include "hw/misc/mps2-fpgaio.h" #include "hw/misc/tz-mpc.h" +#include "hw/misc/tz-msc.h" #include "hw/arm/iotkit.h" +#include "hw/dma/pl080.h" +#include "hw/ssi/pl022.h" #include "hw/devices.h" #include "net/net.h" #include "hw/core/split-irq.h" @@ -71,12 +74,13 @@ typedef struct { MPS2FPGAIO fpgaio; TZPPC ppc[5]; TZMPC ssram_mpc[3]; - UnimplementedDeviceState spi[5]; + PL022State spi[5]; UnimplementedDeviceState i2c[4]; UnimplementedDeviceState i2s_audio; UnimplementedDeviceState gpio[4]; - UnimplementedDeviceState dma[4]; UnimplementedDeviceState gfx; + PL080State dma[4]; + TZMSC msc[4]; CMSDKAPBUART uart[5]; SplitIRQ sec_resp_splitter; qemu_or_irq uart_irq_orgate; @@ -188,7 +192,7 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, sccdev = DEVICE(scc); qdev_set_parent_bus(sccdev, sysbus_get_default()); qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); - qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008); + qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); object_property_set_bool(OBJECT(scc), true, "realized", &error_fatal); return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0); @@ -263,6 +267,89 @@ static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0); } +static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + PL080State *dma = opaque; + int i = dma - &mms->dma[0]; + SysBusDevice *s; + char *mscname = g_strdup_printf("%s-msc", name); + TZMSC *msc = &mms->msc[i]; + DeviceState *iotkitdev = DEVICE(&mms->iotkit); + MemoryRegion *msc_upstream; + MemoryRegion *msc_downstream; + + /* + * Each DMA device is a PL081 whose transaction master interface + * is guarded by a Master Security Controller. The downstream end of + * the MSC connects to the IoTKit AHB Slave Expansion port, so the + * DMA devices can see all devices and memory that the CPU does. + */ + sysbus_init_child_obj(OBJECT(mms), mscname, msc, sizeof(*msc), TYPE_TZ_MSC); + msc_downstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(&mms->iotkit), 0); + object_property_set_link(OBJECT(msc), OBJECT(msc_downstream), + "downstream", &error_fatal); + object_property_set_link(OBJECT(msc), OBJECT(mms), + "idau", &error_fatal); + object_property_set_bool(OBJECT(msc), true, "realized", &error_fatal); + + qdev_connect_gpio_out_named(DEVICE(msc), "irq", 0, + qdev_get_gpio_in_named(iotkitdev, + "mscexp_status", i)); + qdev_connect_gpio_out_named(iotkitdev, "mscexp_clear", i, + qdev_get_gpio_in_named(DEVICE(msc), + "irq_clear", 0)); + qdev_connect_gpio_out_named(iotkitdev, "mscexp_ns", i, + qdev_get_gpio_in_named(DEVICE(msc), + "cfg_nonsec", 0)); + qdev_connect_gpio_out(DEVICE(&mms->sec_resp_splitter), + ARRAY_SIZE(mms->ppc) + i, + qdev_get_gpio_in_named(DEVICE(msc), + "cfg_sec_resp", 0)); + msc_upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(msc), 0); + + sysbus_init_child_obj(OBJECT(mms), name, dma, sizeof(*dma), TYPE_PL081); + object_property_set_link(OBJECT(dma), OBJECT(msc_upstream), + "downstream", &error_fatal); + object_property_set_bool(OBJECT(dma), true, "realized", &error_fatal); + + s = SYS_BUS_DEVICE(dma); + /* Wire up DMACINTR, DMACINTERR, DMACINTTC */ + sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, + "EXP_IRQ", 58 + i * 3)); + sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev, + "EXP_IRQ", 56 + i * 3)); + sysbus_connect_irq(s, 2, qdev_get_gpio_in_named(iotkitdev, + "EXP_IRQ", 57 + i * 3)); + + return sysbus_mmio_get_region(s, 0); +} + +static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + /* + * The AN505 has five PL022 SPI controllers. + * One of these should have the LCD controller behind it; the others + * are connected only to the FPGA's "general purpose SPI connector" + * or "shield" expansion connectors. + * Note that if we do implement devices behind SPI, the chip select + * lines are set via the "MISC" register in the MPS2 FPGAIO device. + */ + PL022State *spi = opaque; + int i = spi - &mms->spi[0]; + DeviceState *iotkitdev = DEVICE(&mms->iotkit); + SysBusDevice *s; + + sysbus_init_child_obj(OBJECT(mms), name, spi, sizeof(mms->spi[0]), + TYPE_PL022); + object_property_set_bool(OBJECT(spi), true, "realized", &error_fatal); + s = SYS_BUS_DEVICE(spi); + sysbus_connect_irq(s, 0, + qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 51 + i)); + return sysbus_mmio_get_region(s, 0); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -289,13 +376,14 @@ static void mps2tz_common_init(MachineState *machine) &error_fatal); /* The sec_resp_cfg output from the IoTKit must be split into multiple - * lines, one for each of the PPCs we create here. + * lines, one for each of the PPCs we create here, plus one per MSC. */ object_initialize(&mms->sec_resp_splitter, sizeof(mms->sec_resp_splitter), TYPE_SPLIT_IRQ); object_property_add_child(OBJECT(machine), "sec-resp-splitter", OBJECT(&mms->sec_resp_splitter), &error_abort); - object_property_set_int(OBJECT(&mms->sec_resp_splitter), 5, + object_property_set_int(OBJECT(&mms->sec_resp_splitter), + ARRAY_SIZE(mms->ppc) + ARRAY_SIZE(mms->msc), "num-lines", &error_fatal); object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true, "realized", &error_fatal); @@ -360,11 +448,11 @@ static void mps2tz_common_init(MachineState *machine) }, { .name = "apb_ppcexp1", .ports = { - { "spi0", make_unimp_dev, &mms->spi[0], 0x40205000, 0x1000 }, - { "spi1", make_unimp_dev, &mms->spi[1], 0x40206000, 0x1000 }, - { "spi2", make_unimp_dev, &mms->spi[2], 0x40209000, 0x1000 }, - { "spi3", make_unimp_dev, &mms->spi[3], 0x4020a000, 0x1000 }, - { "spi4", make_unimp_dev, &mms->spi[4], 0x4020b000, 0x1000 }, + { "spi0", make_spi, &mms->spi[0], 0x40205000, 0x1000 }, + { "spi1", make_spi, &mms->spi[1], 0x40206000, 0x1000 }, + { "spi2", make_spi, &mms->spi[2], 0x40209000, 0x1000 }, + { "spi3", make_spi, &mms->spi[3], 0x4020a000, 0x1000 }, + { "spi4", make_spi, &mms->spi[4], 0x4020b000, 0x1000 }, { "uart0", make_uart, &mms->uart[0], 0x40200000, 0x1000 }, { "uart1", make_uart, &mms->uart[1], 0x40201000, 0x1000 }, { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 }, @@ -396,10 +484,10 @@ static void mps2tz_common_init(MachineState *machine) }, { .name = "ahb_ppcexp1", .ports = { - { "dma0", make_unimp_dev, &mms->dma[0], 0x40110000, 0x1000 }, - { "dma1", make_unimp_dev, &mms->dma[1], 0x40111000, 0x1000 }, - { "dma2", make_unimp_dev, &mms->dma[2], 0x40112000, 0x1000 }, - { "dma3", make_unimp_dev, &mms->dma[3], 0x40113000, 0x1000 }, + { "dma0", make_dma, &mms->dma[0], 0x40110000, 0x1000 }, + { "dma1", make_dma, &mms->dma[1], 0x40111000, 0x1000 }, + { "dma2", make_dma, &mms->dma[2], 0x40112000, 0x1000 }, + { "dma3", make_dma, &mms->dma[3], 0x40113000, 0x1000 }, }, }, }; @@ -480,12 +568,32 @@ static void mps2tz_common_init(MachineState *machine) armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000); } +static void mps2_tz_idau_check(IDAUInterface *ii, uint32_t address, + int *iregion, bool *exempt, bool *ns, bool *nsc) +{ + /* + * The MPS2 TZ FPGA images have IDAUs in them which are connected to + * the Master Security Controllers. Thes have the same logic as + * is used by the IoTKit for the IDAU connected to the CPU, except + * that MSCs don't care about the NSC attribute. + */ + int region = extract32(address, 28, 4); + + *ns = !(region & 1); + *nsc = false; + /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ + *exempt = (address & 0xeff00000) == 0xe0000000; + *iregion = region; +} + static void mps2tz_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc); mc->init = mps2tz_common_init; mc->max_cpus = 1; + iic->check = mps2_tz_idau_check; } static void mps2tz_an505_class_init(ObjectClass *oc, void *data) @@ -496,7 +604,7 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33"; mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); - mmc->scc_id = 0x41040000 | (505 << 4); + mmc->scc_id = 0x41045050; } static const TypeInfo mps2tz_info = { @@ -506,6 +614,10 @@ static const TypeInfo mps2tz_info = { .instance_size = sizeof(MPS2TZMachineState), .class_size = sizeof(MPS2TZMachineClass), .class_init = mps2tz_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_IDAU_INTERFACE }, + { } + }, }; static const TypeInfo mps2tz_an505_info = { diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 0a0ae867d9..e3d698ba6c 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -34,6 +34,7 @@ #include "hw/misc/unimp.h" #include "hw/char/cmsdk-apb-uart.h" #include "hw/timer/cmsdk-apb-timer.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" #include "hw/misc/mps2-scc.h" #include "hw/devices.h" #include "net/net.h" @@ -64,6 +65,7 @@ typedef struct { MemoryRegion blockram_m3; MemoryRegion sram; MPS2SCC scc; + CMSDKAPBDualTimer dualtimer; } MPS2MachineState; #define TYPE_MPS2_MACHINE "mps2" @@ -297,11 +299,20 @@ static void mps2_common_init(MachineState *machine) cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ); cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ); + sysbus_init_child_obj(OBJECT(mms), "dualtimer", &mms->dualtimer, + sizeof(mms->dualtimer), TYPE_CMSDK_APB_DUALTIMER); + qdev_prop_set_uint32(DEVICE(&mms->dualtimer), "pclk-frq", SYSCLK_FRQ); + object_property_set_bool(OBJECT(&mms->dualtimer), true, "realized", + &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 0, + qdev_get_gpio_in(armv7m, 10)); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->dualtimer), 0, 0x40002000); + object_initialize(&mms->scc, sizeof(mms->scc), TYPE_MPS2_SCC); sccdev = DEVICE(&mms->scc); qdev_set_parent_bus(sccdev, sysbus_get_default()); qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); - qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008); + qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); object_property_set_bool(OBJECT(&mms->scc), true, "realized", &error_fatal); @@ -336,7 +347,7 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data) mc->desc = "ARM MPS2 with AN385 FPGA image for Cortex-M3"; mmc->fpga_type = FPGA_AN385; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); - mmc->scc_id = 0x41040000 | (385 << 4); + mmc->scc_id = 0x41043850; } static void mps2_an511_class_init(ObjectClass *oc, void *data) @@ -347,7 +358,7 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data) mc->desc = "ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3"; mmc->fpga_type = FPGA_AN511; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); - mmc->scc_id = 0x4104000 | (511 << 4); + mmc->scc_id = 0x41045110; } static const TypeInfo mps2_info = { diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index b67b0cefb6..f598a1c053 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -409,7 +409,7 @@ static uint64_t pxa2xx_mm_read(void *opaque, hwaddr addr, case MDCNFG ... SA1110: if ((addr & 3) == 0) return s->mm_regs[addr >> 2]; - + /* fall through */ default: printf("%s: Bad register " REG_FMT "\n", __func__, addr); break; diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 5bfe2e4348..c02d18ee61 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -172,6 +172,7 @@ typedef struct { typedef struct { MachineState parent; bool secure; + bool virt; } VexpressMachineState; #define TYPE_VEXPRESS_MACHINE "vexpress" @@ -203,7 +204,7 @@ struct VEDBoardInfo { }; static void init_cpus(const char *cpu_type, const char *privdev, - hwaddr periphbase, qemu_irq *pic, bool secure) + hwaddr periphbase, qemu_irq *pic, bool secure, bool virt) { DeviceState *dev; SysBusDevice *busdev; @@ -216,6 +217,11 @@ static void init_cpus(const char *cpu_type, const char *privdev, if (!secure) { object_property_set_bool(cpuobj, false, "has_el3", NULL); } + if (!virt) { + if (object_property_find(cpuobj, "has_el2", NULL)) { + object_property_set_bool(cpuobj, false, "has_el2", NULL); + } + } if (object_property_find(cpuobj, "reset-cbar", NULL)) { object_property_set_int(cpuobj, periphbase, @@ -251,6 +257,10 @@ static void init_cpus(const char *cpu_type, const char *privdev, sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); sysbus_connect_irq(busdev, n + smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(busdev, n + 2 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(busdev, n + 3 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); } } @@ -285,7 +295,8 @@ static void a9_daughterboard_init(const VexpressMachineState *vms, memory_region_add_subregion(sysmem, 0x60000000, ram); /* 0x1e000000 A9MPCore (SCU) private memory region */ - init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic, vms->secure); + init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic, + vms->secure, vms->virt); /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */ @@ -366,7 +377,8 @@ static void a15_daughterboard_init(const VexpressMachineState *vms, memory_region_add_subregion(sysmem, 0x80000000, ram); /* 0x2c000000 A15MPCore private memory region (GIC) */ - init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure); + init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure, + vms->virt); /* A15 daughterboard peripherals: */ @@ -701,8 +713,8 @@ static void vexpress_common_init(MachineState *machine) daughterboard->bootinfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30; daughterboard->bootinfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr; daughterboard->bootinfo.modify_dtb = vexpress_modify_dtb; - /* Indicate that when booting Linux we should be in secure state */ - daughterboard->bootinfo.secure_boot = true; + /* When booting Linux we should be in secure state if the CPU has one. */ + daughterboard->bootinfo.secure_boot = vms->secure; arm_load_kernel(ARM_CPU(first_cpu), &daughterboard->bootinfo); } @@ -720,6 +732,20 @@ static void vexpress_set_secure(Object *obj, bool value, Error **errp) vms->secure = value; } +static bool vexpress_get_virt(Object *obj, Error **errp) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + return vms->virt; +} + +static void vexpress_set_virt(Object *obj, bool value, Error **errp) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + vms->virt = value; +} + static void vexpress_instance_init(Object *obj) { VexpressMachineState *vms = VEXPRESS_MACHINE(obj); @@ -734,6 +760,32 @@ static void vexpress_instance_init(Object *obj) NULL); } +static void vexpress_a15_instance_init(Object *obj) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + /* + * For the vexpress-a15, EL2 is by default enabled if EL3 is, + * but can also be specifically set to on or off. + */ + vms->virt = true; + object_property_add_bool(obj, "virtualization", vexpress_get_virt, + vexpress_set_virt, NULL); + object_property_set_description(obj, "virtualization", + "Set on/off to enable/disable the ARM " + "Virtualization Extensions " + "(defaults to same as 'secure')", + NULL); +} + +static void vexpress_a9_instance_init(Object *obj) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + /* The A9 doesn't have the virt extensions */ + vms->virt = false; +} + static void vexpress_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -780,12 +832,14 @@ static const TypeInfo vexpress_a9_info = { .name = TYPE_VEXPRESS_A9_MACHINE, .parent = TYPE_VEXPRESS_MACHINE, .class_init = vexpress_a9_class_init, + .instance_init = vexpress_a9_instance_init, }; static const TypeInfo vexpress_a15_info = { .name = TYPE_VEXPRESS_A15_MACHINE, .parent = TYPE_VEXPRESS_MACHINE, .class_init = vexpress_a15_class_init, + .instance_init = vexpress_a15_instance_init, }; static void vexpress_machine_init(void) diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index aaebec1839..9089dcb47e 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -305,6 +305,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) case 6: as.endianness = 1; + /* fall through */ case 2: as.fmt = AUD_FMT_S16; s->shift = as.nchannels; diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 59cf252754..dd75c9e8f5 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -474,82 +474,7 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr) return addr; } -static void es1370_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - ES1370State *s = opaque; - uint32_t shift, mask; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case ES1370_REG_CONTROL: - case ES1370_REG_CONTROL + 1: - case ES1370_REG_CONTROL + 2: - case ES1370_REG_CONTROL + 3: - shift = (addr - ES1370_REG_CONTROL) << 3; - mask = 0xff << shift; - val = (s->ctl & ~mask) | ((val & 0xff) << shift); - es1370_update_voices (s, val, s->sctl); - print_ctl (val); - break; - case ES1370_REG_MEMPAGE: - s->mempage = val; - break; - case ES1370_REG_SERIAL_CONTROL: - case ES1370_REG_SERIAL_CONTROL + 1: - case ES1370_REG_SERIAL_CONTROL + 2: - case ES1370_REG_SERIAL_CONTROL + 3: - shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3; - mask = 0xff << shift; - val = (s->sctl & ~mask) | ((val & 0xff) << shift); - es1370_maybe_lower_irq (s, val); - es1370_update_voices (s, s->ctl, val); - print_sctl (val); - break; - default: - lwarn ("writeb %#x <- %#x\n", addr, val); - break; - } -} - -static void es1370_writew(void *opaque, uint32_t addr, uint32_t val) -{ - ES1370State *s = opaque; - addr = es1370_fixup (s, addr); - uint32_t shift, mask; - struct chan *d = &s->chan[0]; - - switch (addr) { - case ES1370_REG_CODEC: - dolog ("ignored codec write address %#x, data %#x\n", - (val >> 8) & 0xff, val & 0xff); - s->codec = val; - break; - - case ES1370_REG_CONTROL: - case ES1370_REG_CONTROL + 2: - shift = (addr != ES1370_REG_CONTROL) << 4; - mask = 0xffff << shift; - val = (s->ctl & ~mask) | ((val & 0xffff) << shift); - es1370_update_voices (s, val, s->sctl); - print_ctl (val); - break; - - case ES1370_REG_ADC_SCOUNT: - d++; - case ES1370_REG_DAC2_SCOUNT: - d++; - case ES1370_REG_DAC1_SCOUNT: - d->scount = (d->scount & ~0xffff) | (val & 0xffff); - break; - - default: - lwarn ("writew %#x <- %#x\n", addr, val); - break; - } -} - -static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) +static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { ES1370State *s = opaque; struct chan *d = &s->chan[0]; @@ -572,21 +497,19 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) print_sctl (val); break; - case ES1370_REG_ADC_SCOUNT: - d++; - case ES1370_REG_DAC2_SCOUNT: - d++; case ES1370_REG_DAC1_SCOUNT: + case ES1370_REG_DAC2_SCOUNT: + case ES1370_REG_ADC_SCOUNT: + d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2; d->scount = (val & 0xffff) | (d->scount & ~0xffff); ldebug ("chan %td CURR_SAMP_CT %d, SAMP_CT %d\n", d - &s->chan[0], val >> 16, (val & 0xffff)); break; - case ES1370_REG_ADC_FRAMEADR: - d++; - case ES1370_REG_DAC2_FRAMEADR: - d++; case ES1370_REG_DAC1_FRAMEADR: + case ES1370_REG_DAC2_FRAMEADR: + case ES1370_REG_ADC_FRAMEADR: + d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3; d->frame_addr = val; ldebug ("chan %td frame address %#x\n", d - &s->chan[0], val); break; @@ -598,11 +521,10 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) lwarn ("writing to phantom frame address %#x\n", val); break; - case ES1370_REG_ADC_FRAMECNT: - d++; - case ES1370_REG_DAC2_FRAMECNT: - d++; case ES1370_REG_DAC1_FRAMECNT: + case ES1370_REG_DAC2_FRAMECNT: + case ES1370_REG_ADC_FRAMECNT: + d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3; d->frame_cnt = val; d->leftover = 0; ldebug ("chan %td frame count %d, buffer size %d\n", @@ -615,84 +537,7 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) } } -static uint32_t es1370_readb(void *opaque, uint32_t addr) -{ - ES1370State *s = opaque; - uint32_t val; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case 0x1b: /* Legacy */ - lwarn ("Attempt to read from legacy register\n"); - val = 5; - break; - case ES1370_REG_MEMPAGE: - val = s->mempage; - break; - case ES1370_REG_CONTROL + 0: - case ES1370_REG_CONTROL + 1: - case ES1370_REG_CONTROL + 2: - case ES1370_REG_CONTROL + 3: - val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3); - break; - case ES1370_REG_STATUS + 0: - case ES1370_REG_STATUS + 1: - case ES1370_REG_STATUS + 2: - case ES1370_REG_STATUS + 3: - val = s->status >> ((addr - ES1370_REG_STATUS) << 3); - break; - default: - val = ~0; - lwarn ("readb %#x -> %#x\n", addr, val); - break; - } - return val; -} - -static uint32_t es1370_readw(void *opaque, uint32_t addr) -{ - ES1370State *s = opaque; - struct chan *d = &s->chan[0]; - uint32_t val; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case ES1370_REG_ADC_SCOUNT + 2: - d++; - case ES1370_REG_DAC2_SCOUNT + 2: - d++; - case ES1370_REG_DAC1_SCOUNT + 2: - val = d->scount >> 16; - break; - - case ES1370_REG_ADC_FRAMECNT: - d++; - case ES1370_REG_DAC2_FRAMECNT: - d++; - case ES1370_REG_DAC1_FRAMECNT: - val = d->frame_cnt & 0xffff; - break; - - case ES1370_REG_ADC_FRAMECNT + 2: - d++; - case ES1370_REG_DAC2_FRAMECNT + 2: - d++; - case ES1370_REG_DAC1_FRAMECNT + 2: - val = d->frame_cnt >> 16; - break; - - default: - val = ~0; - lwarn ("readw %#x -> %#x\n", addr, val); - break; - } - - return val; -} - -static uint32_t es1370_readl(void *opaque, uint32_t addr) +static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size) { ES1370State *s = opaque; uint32_t val; @@ -717,11 +562,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr) val = s->sctl; break; - case ES1370_REG_ADC_SCOUNT: - d++; - case ES1370_REG_DAC2_SCOUNT: - d++; case ES1370_REG_DAC1_SCOUNT: + case ES1370_REG_DAC2_SCOUNT: + case ES1370_REG_ADC_SCOUNT: + d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2; val = d->scount; #ifdef DEBUG_ES1370 { @@ -735,11 +579,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr) #endif break; - case ES1370_REG_ADC_FRAMECNT: - d++; - case ES1370_REG_DAC2_FRAMECNT: - d++; case ES1370_REG_DAC1_FRAMECNT: + case ES1370_REG_DAC2_FRAMECNT: + case ES1370_REG_ADC_FRAMECNT: + d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3; val = d->frame_cnt; #ifdef DEBUG_ES1370 { @@ -753,11 +596,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr) #endif break; - case ES1370_REG_ADC_FRAMEADR: - d++; - case ES1370_REG_DAC2_FRAMEADR: - d++; case ES1370_REG_DAC1_FRAMEADR: + case ES1370_REG_DAC2_FRAMEADR: + case ES1370_REG_ADC_FRAMEADR: + d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3; val = d->frame_addr; break; @@ -908,44 +750,17 @@ static void es1370_adc_callback (void *opaque, int avail) es1370_run_channel (s, ADC_CHANNEL, avail); } -static uint64_t es1370_read(void *opaque, hwaddr addr, - unsigned size) -{ - switch (size) { - case 1: - return es1370_readb(opaque, addr); - case 2: - return es1370_readw(opaque, addr); - case 4: - return es1370_readl(opaque, addr); - default: - return -1; - } -} - -static void es1370_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - switch (size) { - case 1: - es1370_writeb(opaque, addr, val); - break; - case 2: - es1370_writew(opaque, addr, val); - break; - case 4: - es1370_writel(opaque, addr, val); - break; - } -} - static const MemoryRegionOps es1370_io_ops = { .read = es1370_read, .write = es1370_write, - .impl = { + .valid = { .min_access_size = 1, .max_access_size = 4, }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, .endianness = DEVICE_LITTLE_ENDIAN, }; diff --git a/hw/audio/gusemu_hal.c b/hw/audio/gusemu_hal.c index 1150fc4426..ae40ca341c 100644 --- a/hw/audio/gusemu_hal.c +++ b/hw/audio/gusemu_hal.c @@ -261,6 +261,7 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data) GUSregb(IRQStatReg2x6) = 0x10; GUS_irqrequest(state, state->gusirq, 1); } + /* fall through */ case 0x20D: /* SB2xCd no IRQ */ GUSregb(SB2xCd) = (uint8_t) data; break; diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 5a4d32364e..c5b9bf79e8 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -741,10 +741,15 @@ static void complete (SB16State *s) ldebug ("set time const %d\n", s->time_const); break; - case 0x42: /* FT2 sets output freq with this, go figure */ - qemu_log_mask(LOG_UNIMP, "cmd 0x42 might not do what it think it" - " should\n"); case 0x41: + case 0x42: + /* + * 0x41 is documented as setting the output sample rate, + * and 0x42 the input sample rate, but in fact SB16 hardware + * seems to have only a single sample rate under the hood, + * and FT2 sets output freq with this (go figure). Compare: + * http://homepages.cae.wisc.edu/~brodskye/sb16doc/sb16doc.html#SamplingRate + */ s->freq = dsp_get_hilo (s); ldebug ("set freq %d\n", s->freq); break; diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 43c1079493..5649843cd8 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -53,6 +53,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) int i; Error *err = NULL; bool has_el3; + bool has_el2 = false; Object *cpuobj; gicdev = DEVICE(&s->gic); @@ -67,6 +68,10 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) has_el3 = object_property_find(cpuobj, "has_el3", NULL) && object_property_get_bool(cpuobj, "has_el3", &error_abort); qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3); + /* Similarly for virtualization support */ + has_el2 = object_property_find(cpuobj, "has_el2", NULL) && + object_property_get_bool(cpuobj, "has_el2", &error_abort); + qdev_prop_set_bit(gicdev, "has-virtualization-extensions", has_el2); } object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); @@ -103,20 +108,40 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, ppibase + timer_irq[irq])); } + if (has_el2) { + /* Connect the GIC maintenance interrupt to PPI ID 25 */ + sysbus_connect_irq(SYS_BUS_DEVICE(gicdev), i + 4 * s->num_cpu, + qdev_get_gpio_in(gicdev, ppibase + 25)); + } } /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x0fff -- reserved * 0x1000-0x1fff -- GIC Distributor * 0x2000-0x3fff -- GIC CPU interface - * 0x4000-0x4fff -- GIC virtual interface control (not modelled) - * 0x5000-0x5fff -- GIC virtual interface control (not modelled) - * 0x6000-0x7fff -- GIC virtual CPU interface (not modelled) + * 0x4000-0x4fff -- GIC virtual interface control for this CPU + * 0x5000-0x51ff -- GIC virtual interface control for CPU 0 + * 0x5200-0x53ff -- GIC virtual interface control for CPU 1 + * 0x5400-0x55ff -- GIC virtual interface control for CPU 2 + * 0x5600-0x57ff -- GIC virtual interface control for CPU 3 + * 0x6000-0x7fff -- GIC virtual CPU interface */ memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(busdev, 0)); memory_region_add_subregion(&s->container, 0x2000, sysbus_mmio_get_region(busdev, 1)); + if (has_el2) { + memory_region_add_subregion(&s->container, 0x4000, + sysbus_mmio_get_region(busdev, 2)); + memory_region_add_subregion(&s->container, 0x6000, + sysbus_mmio_get_region(busdev, 3)); + for (i = 0; i < s->num_cpu; i++) { + hwaddr base = 0x5000 + i * 0x200; + MemoryRegion *mr = sysbus_mmio_get_region(busdev, + 4 + s->num_cpu + i); + memory_region_add_subregion(&s->container, base, mr); + } + } } static Property a15mp_priv_properties[] = { diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 3355f4c131..d534d00a65 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -34,6 +34,13 @@ #define DEFAULT_VCRAM_SIZE 0x4000000 #define BCM2835_FB_OFFSET 0x00100000 +/* Maximum permitted framebuffer size; experimentally determined on an rpi2 */ +#define XRES_MAX 3840 +#define YRES_MAX 2560 +/* Framebuffer size used if guest requests zero size */ +#define XRES_SMALL 592 +#define YRES_SMALL 488 + static void fb_invalidate_display(void *opaque) { BCM2835FBState *s = BCM2835_FB(opaque); @@ -52,7 +59,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, int bpp = surface_bits_per_pixel(surface); while (width--) { - switch (s->bpp) { + switch (s->config.bpp) { case 8: /* lookup palette starting at video ram base * TODO: cache translation, rather than doing this each time! @@ -91,7 +98,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, break; } - if (s->pixo == 0) { + if (s->config.pixo == 0) { /* swap to BGR pixel format */ uint8_t tmp = r; r = b; @@ -126,6 +133,18 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, } } +static bool fb_use_offsets(BCM2835FBConfig *config) +{ + /* + * Return true if we should use the viewport offsets. + * Experimentally, the hardware seems to do this only if the + * viewport size is larger than the physical screen. (It doesn't + * prevent the guest setting this silly viewport setting, though...) + */ + return config->xres_virtual > config->xres && + config->yres_virtual > config->yres; +} + static void fb_update_display(void *opaque) { BCM2835FBState *s = opaque; @@ -134,13 +153,19 @@ static void fb_update_display(void *opaque) int last = 0; int src_width = 0; int dest_width = 0; + uint32_t xoff = 0, yoff = 0; - if (s->lock || !s->xres) { + if (s->lock || !s->config.xres) { return; } - src_width = s->xres * (s->bpp >> 3); - dest_width = s->xres; + src_width = bcm2835_fb_get_pitch(&s->config); + if (fb_use_offsets(&s->config)) { + xoff = s->config.xoffset; + yoff = s->config.yoffset; + } + + dest_width = s->config.xres; switch (surface_bits_per_pixel(surface)) { case 0: @@ -165,89 +190,104 @@ static void fb_update_display(void *opaque) } if (s->invalidate) { - framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base, - s->yres, src_width); + hwaddr base = s->config.base + xoff + yoff * src_width; + framebuffer_update_memory_section(&s->fbsection, s->dma_mr, + base, + s->config.yres, src_width); } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, + framebuffer_update_display(surface, &s->fbsection, + s->config.xres, s->config.yres, src_width, dest_width, 0, s->invalidate, draw_line_src16, s, &first, &last); if (first >= 0) { - dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1); + dpy_gfx_update(s->con, 0, first, s->config.xres, + last - first + 1); } s->invalidate = false; } -static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) +void bcm2835_fb_validate_config(BCM2835FBConfig *config) { - value &= ~0xf; - - s->lock = true; - - s->xres = ldl_le_phys(&s->dma_as, value); - s->yres = ldl_le_phys(&s->dma_as, value + 4); - s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8); - s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12); - s->bpp = ldl_le_phys(&s->dma_as, value + 20); - s->xoffset = ldl_le_phys(&s->dma_as, value + 24); - s->yoffset = ldl_le_phys(&s->dma_as, value + 28); - - s->base = s->vcram_base | (value & 0xc0000000); - s->base += BCM2835_FB_OFFSET; + /* + * Validate the config, and clip any bogus values into range, + * as the hardware does. Note that fb_update_display() relies on + * this happening to prevent it from performing out-of-range + * accesses on redraw. + */ + config->xres = MIN(config->xres, XRES_MAX); + config->xres_virtual = MIN(config->xres_virtual, XRES_MAX); + config->yres = MIN(config->yres, YRES_MAX); + config->yres_virtual = MIN(config->yres_virtual, YRES_MAX); + + /* + * These are not minima: a 40x40 framebuffer will be accepted. + * They're only used as defaults if the guest asks for zero size. + */ + if (config->xres == 0) { + config->xres = XRES_SMALL; + } + if (config->yres == 0) { + config->yres = YRES_SMALL; + } + if (config->xres_virtual == 0) { + config->xres_virtual = config->xres; + } + if (config->yres_virtual == 0) { + config->yres_virtual = config->yres; + } - /* TODO - Manage properly virtual resolution */ + if (fb_use_offsets(config)) { + /* Clip the offsets so the viewport is within the physical screen */ + config->xoffset = MIN(config->xoffset, + config->xres_virtual - config->xres); + config->yoffset = MIN(config->yoffset, + config->yres_virtual - config->yres); + } +} - s->pitch = s->xres * (s->bpp >> 3); - s->size = s->yres * s->pitch; +void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig) +{ + s->lock = true; - stl_le_phys(&s->dma_as, value + 16, s->pitch); - stl_le_phys(&s->dma_as, value + 32, s->base); - stl_le_phys(&s->dma_as, value + 36, s->size); + s->config = *newconfig; s->invalidate = true; - qemu_console_resize(s->con, s->xres, s->yres); + qemu_console_resize(s->con, s->config.xres, s->config.yres); s->lock = false; } -void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, - uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, - uint32_t *pixo, uint32_t *alpha) +static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) { - s->lock = true; + uint32_t pitch; + uint32_t size; + BCM2835FBConfig newconf; - /* TODO: input validation! */ - if (xres) { - s->xres = *xres; - } - if (yres) { - s->yres = *yres; - } - if (xoffset) { - s->xoffset = *xoffset; - } - if (yoffset) { - s->yoffset = *yoffset; - } - if (bpp) { - s->bpp = *bpp; - } - if (pixo) { - s->pixo = *pixo; - } - if (alpha) { - s->alpha = *alpha; - } + value &= ~0xf; - /* TODO - Manage properly virtual resolution */ + newconf.xres = ldl_le_phys(&s->dma_as, value); + newconf.yres = ldl_le_phys(&s->dma_as, value + 4); + newconf.xres_virtual = ldl_le_phys(&s->dma_as, value + 8); + newconf.yres_virtual = ldl_le_phys(&s->dma_as, value + 12); + newconf.bpp = ldl_le_phys(&s->dma_as, value + 20); + newconf.xoffset = ldl_le_phys(&s->dma_as, value + 24); + newconf.yoffset = ldl_le_phys(&s->dma_as, value + 28); - s->pitch = s->xres * (s->bpp >> 3); - s->size = s->yres * s->pitch; + newconf.base = s->vcram_base | (value & 0xc0000000); + newconf.base += BCM2835_FB_OFFSET; - s->invalidate = true; - qemu_console_resize(s->con, s->xres, s->yres); - s->lock = false; + bcm2835_fb_validate_config(&newconf); + + pitch = bcm2835_fb_get_pitch(&newconf); + size = bcm2835_fb_get_size(&newconf); + + stl_le_phys(&s->dma_as, value + 16, pitch); + stl_le_phys(&s->dma_as, value + 32, newconf.base); + stl_le_phys(&s->dma_as, value + 36, size); + + bcm2835_fb_reconfigure(s, &newconf); } static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size) @@ -312,18 +352,17 @@ static const VMStateDescription vmstate_bcm2835_fb = { VMSTATE_BOOL(lock, BCM2835FBState), VMSTATE_BOOL(invalidate, BCM2835FBState), VMSTATE_BOOL(pending, BCM2835FBState), - VMSTATE_UINT32(xres, BCM2835FBState), - VMSTATE_UINT32(yres, BCM2835FBState), - VMSTATE_UINT32(xres_virtual, BCM2835FBState), - VMSTATE_UINT32(yres_virtual, BCM2835FBState), - VMSTATE_UINT32(xoffset, BCM2835FBState), - VMSTATE_UINT32(yoffset, BCM2835FBState), - VMSTATE_UINT32(bpp, BCM2835FBState), - VMSTATE_UINT32(base, BCM2835FBState), - VMSTATE_UINT32(pitch, BCM2835FBState), - VMSTATE_UINT32(size, BCM2835FBState), - VMSTATE_UINT32(pixo, BCM2835FBState), - VMSTATE_UINT32(alpha, BCM2835FBState), + VMSTATE_UINT32(config.xres, BCM2835FBState), + VMSTATE_UINT32(config.yres, BCM2835FBState), + VMSTATE_UINT32(config.xres_virtual, BCM2835FBState), + VMSTATE_UINT32(config.yres_virtual, BCM2835FBState), + VMSTATE_UINT32(config.xoffset, BCM2835FBState), + VMSTATE_UINT32(config.yoffset, BCM2835FBState), + VMSTATE_UINT32(config.bpp, BCM2835FBState), + VMSTATE_UINT32(config.base, BCM2835FBState), + VMSTATE_UNUSED(8), /* Was pitch and size */ + VMSTATE_UINT32(config.pixo, BCM2835FBState), + VMSTATE_UINT32(config.alpha, BCM2835FBState), VMSTATE_END_OF_LIST() } }; @@ -349,13 +388,7 @@ static void bcm2835_fb_reset(DeviceState *dev) s->pending = false; - s->xres_virtual = s->xres; - s->yres_virtual = s->yres; - s->xoffset = 0; - s->yoffset = 0; - s->base = s->vcram_base + BCM2835_FB_OFFSET; - s->pitch = s->xres * (s->bpp >> 3); - s->size = s->yres * s->pitch; + s->config = s->initial_config; s->invalidate = true; s->lock = false; @@ -379,24 +412,33 @@ static void bcm2835_fb_realize(DeviceState *dev, Error **errp) return; } + /* Fill in the parts of initial_config that are not set by QOM properties */ + s->initial_config.xres_virtual = s->initial_config.xres; + s->initial_config.yres_virtual = s->initial_config.yres; + s->initial_config.xoffset = 0; + s->initial_config.yoffset = 0; + s->initial_config.base = s->vcram_base + BCM2835_FB_OFFSET; + s->dma_mr = MEMORY_REGION(obj); address_space_init(&s->dma_as, s->dma_mr, NULL); bcm2835_fb_reset(dev); s->con = graphic_console_init(dev, 0, &vgafb_ops, s); - qemu_console_resize(s->con, s->xres, s->yres); + qemu_console_resize(s->con, s->config.xres, s->config.yres); } static Property bcm2835_fb_props[] = { DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/ DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size, DEFAULT_VCRAM_SIZE), - DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640), - DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480), - DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16), - DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */ - DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */ + DEFINE_PROP_UINT32("xres", BCM2835FBState, initial_config.xres, 640), + DEFINE_PROP_UINT32("yres", BCM2835FBState, initial_config.yres, 480), + DEFINE_PROP_UINT32("bpp", BCM2835FBState, initial_config.bpp, 16), + DEFINE_PROP_UINT32("pixo", BCM2835FBState, + initial_config.pixo, 1), /* 1=RGB, 0=BGR */ + DEFINE_PROP_UINT32("alpha", BCM2835FBState, + initial_config.alpha, 2), /* alpha ignored */ DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 6fff4852c5..1c199ab369 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -232,6 +232,7 @@ static void cg3_reg_write(void *opaque, hwaddr addr, uint64_t val, s->b[s->dac_index] = regval; /* Index autoincrement */ s->dac_index = (s->dac_index + 1) & 0xff; + /* fall through */ default: s->dac_state = 0; break; diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 7583b18c29..04c87c8e8d 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -1426,7 +1426,8 @@ static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) s->vga.hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5); break; case 0x07: // Extended Sequencer Mode - cirrus_update_memory_access(s); + cirrus_update_memory_access(s); + /* fall through */ case 0x08: // EEPROM Control case 0x09: // Scratch Register 0 case 0x0a: // Scratch Register 1 diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 0d26e0f6b5..685a2378ed 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -22,8 +22,6 @@ #include "hw/i2c/pm_smbus.h" #include "hw/i2c/smbus.h" -/* no save/load? */ - #define SMBHSTSTS 0x00 #define SMBHSTCNT 0x02 #define SMBHSTCMD 0x03 @@ -31,20 +29,40 @@ #define SMBHSTDAT0 0x05 #define SMBHSTDAT1 0x06 #define SMBBLKDAT 0x07 +#define SMBAUXCTL 0x0d -#define STS_HOST_BUSY (1) -#define STS_INTR (1<<1) -#define STS_DEV_ERR (1<<2) -#define STS_BUS_ERR (1<<3) -#define STS_FAILED (1<<4) -#define STS_SMBALERT (1<<5) -#define STS_INUSE_STS (1<<6) -#define STS_BYTE_DONE (1<<7) +#define STS_HOST_BUSY (1 << 0) +#define STS_INTR (1 << 1) +#define STS_DEV_ERR (1 << 2) +#define STS_BUS_ERR (1 << 3) +#define STS_FAILED (1 << 4) +#define STS_SMBALERT (1 << 5) +#define STS_INUSE_STS (1 << 6) +#define STS_BYTE_DONE (1 << 7) /* Signs of successfully transaction end : * ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR ) */ -//#define DEBUG +#define CTL_INTREN (1 << 0) +#define CTL_KILL (1 << 1) +#define CTL_LAST_BYTE (1 << 5) +#define CTL_START (1 << 6) +#define CTL_PEC_EN (1 << 7) +#define CTL_RETURN_MASK 0x1f + +#define PROT_QUICK 0 +#define PROT_BYTE 1 +#define PROT_BYTE_DATA 2 +#define PROT_WORD_DATA 3 +#define PROT_PROC_CALL 4 +#define PROT_BLOCK_DATA 5 +#define PROT_I2C_BLOCK_READ 6 + +#define AUX_PEC (1 << 0) +#define AUX_BLK (1 << 1) +#define AUX_MASK 0x3 + +/*#define DEBUG*/ #ifdef DEBUG # define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) @@ -62,19 +80,17 @@ static void smb_transaction(PMSMBus *s) I2CBus *bus = s->smbus; int ret; - assert(s->smb_stat & STS_HOST_BUSY); - s->smb_stat &= ~STS_HOST_BUSY; - SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); /* Transaction isn't exec if STS_DEV_ERR bit set */ if ((s->smb_stat & STS_DEV_ERR) != 0) { goto error; } + switch(prot) { - case 0x0: + case PROT_QUICK: ret = smbus_quick_command(bus, addr, read); goto done; - case 0x1: + case PROT_BYTE: if (read) { ret = smbus_receive_byte(bus, addr); goto data8; @@ -82,7 +98,7 @@ static void smb_transaction(PMSMBus *s) ret = smbus_send_byte(bus, addr, cmd); goto done; } - case 0x2: + case PROT_BYTE_DATA: if (read) { ret = smbus_read_byte(bus, addr, cmd); goto data8; @@ -91,22 +107,73 @@ static void smb_transaction(PMSMBus *s) goto done; } break; - case 0x3: + case PROT_WORD_DATA: if (read) { ret = smbus_read_word(bus, addr, cmd); goto data16; } else { - ret = smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); + ret = smbus_write_word(bus, addr, cmd, + (s->smb_data1 << 8) | s->smb_data0); goto done; } break; - case 0x5: + case PROT_I2C_BLOCK_READ: if (read) { - ret = smbus_read_block(bus, addr, cmd, s->smb_data); + int xfersize = s->smb_data0; + if (xfersize > sizeof(s->smb_data)) { + xfersize = sizeof(s->smb_data); + } + ret = smbus_read_block(bus, addr, s->smb_data1, s->smb_data, + xfersize, false, true); goto data8; } else { - ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); - goto done; + /* The manual says the behavior is undefined, just set DEV_ERR. */ + goto error; + } + break; + case PROT_BLOCK_DATA: + if (read) { + ret = smbus_read_block(bus, addr, cmd, s->smb_data, + sizeof(s->smb_data), !s->i2c_enable, + !s->i2c_enable); + if (ret < 0) { + goto error; + } + s->smb_index = 0; + s->op_done = false; + if (s->smb_auxctl & AUX_BLK) { + s->smb_stat |= STS_INTR; + } else { + s->smb_blkdata = s->smb_data[0]; + s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; + } + s->smb_data0 = ret; + goto out; + } else { + if (s->smb_auxctl & AUX_BLK) { + if (s->smb_index != s->smb_data0) { + s->smb_index = 0; + goto error; + } + /* Data is already all written to the queue, just do + the operation. */ + s->smb_index = 0; + ret = smbus_write_block(bus, addr, cmd, s->smb_data, + s->smb_data0, !s->i2c_enable); + if (ret < 0) { + goto error; + } + s->op_done = true; + s->smb_stat |= STS_INTR; + s->smb_stat &= ~STS_HOST_BUSY; + } else { + s->op_done = false; + s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; + s->smb_data[0] = s->smb_blkdata; + s->smb_index = 0; + ret = 0; + } + goto out; } break; default: @@ -128,20 +195,35 @@ done: if (ret < 0) { goto error; } - s->smb_stat |= STS_BYTE_DONE | STS_INTR; + s->smb_stat |= STS_INTR; +out: return; error: s->smb_stat |= STS_DEV_ERR; return; - } static void smb_transaction_start(PMSMBus *s) { - /* Do not execute immediately the command ; it will be - * executed when guest will read SMB_STAT register */ - s->smb_stat |= STS_HOST_BUSY; + if (s->smb_ctl & CTL_INTREN) { + smb_transaction(s); + } else { + /* Do not execute immediately the command; it will be + * executed when guest will read SMB_STAT register. This + * is to work around a bug in AMIBIOS (that is working + * around another bug in some specific hardware) where + * it waits for STS_HOST_BUSY to be set before waiting + * checking for status. If STS_HOST_BUSY doesn't get + * set, it gets stuck. */ + s->smb_stat |= STS_HOST_BUSY; + } +} + +static bool +smb_irq_value(PMSMBus *s) +{ + return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN); } static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, @@ -153,13 +235,61 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, " val=0x%02" PRIx64 "\n", addr, val); switch(addr) { case SMBHSTSTS: - s->smb_stat = (~(val & 0xff)) & s->smb_stat; - s->smb_index = 0; + s->smb_stat &= ~(val & ~STS_HOST_BUSY); + if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) { + uint8_t read = s->smb_addr & 0x01; + + s->smb_index++; + if (!read && s->smb_index == s->smb_data0) { + uint8_t prot = (s->smb_ctl >> 2) & 0x07; + uint8_t cmd = s->smb_cmd; + uint8_t addr = s->smb_addr >> 1; + int ret; + + if (prot == PROT_I2C_BLOCK_READ) { + s->smb_stat |= STS_DEV_ERR; + goto out; + } + + ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data, + s->smb_data0, !s->i2c_enable); + if (ret < 0) { + s->smb_stat |= STS_DEV_ERR; + goto out; + } + s->op_done = true; + s->smb_stat |= STS_INTR; + s->smb_stat &= ~STS_HOST_BUSY; + } else if (!read) { + s->smb_data[s->smb_index] = s->smb_blkdata; + s->smb_stat |= STS_BYTE_DONE; + } else if (s->smb_ctl & CTL_LAST_BYTE) { + s->op_done = true; + s->smb_blkdata = s->smb_data[s->smb_index]; + s->smb_index = 0; + s->smb_stat |= STS_INTR; + s->smb_stat &= ~STS_HOST_BUSY; + } else { + s->smb_blkdata = s->smb_data[s->smb_index]; + s->smb_stat |= STS_BYTE_DONE; + } + } break; case SMBHSTCNT: - s->smb_ctl = val; - if (val & 0x40) + s->smb_ctl = val & ~CTL_START; /* CTL_START always reads 0 */ + if (val & CTL_START) { + if (!s->op_done) { + s->smb_index = 0; + s->op_done = true; + } smb_transaction_start(s); + } + if (s->smb_ctl & CTL_KILL) { + s->op_done = true; + s->smb_index = 0; + s->smb_stat |= STS_FAILED; + s->smb_stat &= ~STS_HOST_BUSY; + } break; case SMBHSTCMD: s->smb_cmd = val; @@ -174,13 +304,26 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, s->smb_data1 = val; break; case SMBBLKDAT: - s->smb_data[s->smb_index++] = val; - if (s->smb_index > 31) + if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { s->smb_index = 0; + } + if (s->smb_auxctl & AUX_BLK) { + s->smb_data[s->smb_index++] = val; + } else { + s->smb_blkdata = val; + } + break; + case SMBAUXCTL: + s->smb_auxctl = val & AUX_MASK; break; default: break; } + + out: + if (s->set_irq) { + s->set_irq(s, smb_irq_value(s)); + } } static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) @@ -193,12 +336,12 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = s->smb_stat; if (s->smb_stat & STS_HOST_BUSY) { /* execute command now */ + s->smb_stat &= ~STS_HOST_BUSY; smb_transaction(s); } break; case SMBHSTCNT: - s->smb_index = 0; - val = s->smb_ctl & 0x1f; + val = s->smb_ctl & CTL_RETURN_MASK; break; case SMBHSTCMD: val = s->smb_cmd; @@ -213,18 +356,44 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = s->smb_data1; break; case SMBBLKDAT: - val = s->smb_data[s->smb_index++]; - if (s->smb_index > 31) + if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { s->smb_index = 0; + } + if (s->smb_auxctl & AUX_BLK) { + val = s->smb_data[s->smb_index++]; + if (!s->op_done && s->smb_index == s->smb_data0) { + s->op_done = true; + s->smb_index = 0; + s->smb_stat &= ~STS_HOST_BUSY; + } + } else { + val = s->smb_blkdata; + } + break; + case SMBAUXCTL: + val = s->smb_auxctl; break; default: val = 0; break; } - SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", addr, val); + SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", + addr, val); + + if (s->set_irq) { + s->set_irq(s, smb_irq_value(s)); + } + return val; } +static void pm_smbus_reset(PMSMBus *s) +{ + s->op_done = true; + s->smb_index = 0; + s->smb_stat = 0; +} + static const MemoryRegionOps pm_smbus_ops = { .read = smb_ioport_readb, .write = smb_ioport_writeb, @@ -233,9 +402,14 @@ static const MemoryRegionOps pm_smbus_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -void pm_smbus_init(DeviceState *parent, PMSMBus *smb) +void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk) { + smb->op_done = true; + smb->reset = pm_smbus_reset; smb->smbus = i2c_init_bus(parent, "i2c"); + if (force_aux_blk) { + smb->smb_auxctl |= AUX_BLK; + } memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb, "pm-smbus", 64); } diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c index 587ce1ab7f..6ff77c582f 100644 --- a/hw/i2c/smbus.c +++ b/hw/i2c/smbus.c @@ -293,33 +293,42 @@ int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data) return 0; } -int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data) +int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool recv_len, bool send_cmd) { - int len; + int rlen; int i; - if (i2c_start_transfer(bus, addr, 0)) { - return -1; + if (send_cmd) { + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); } - i2c_send(bus, command); if (i2c_start_transfer(bus, addr, 1)) { - i2c_end_transfer(bus); + if (send_cmd) { + i2c_end_transfer(bus); + } return -1; } - len = i2c_recv(bus); - if (len > 32) { - len = 0; + if (recv_len) { + rlen = i2c_recv(bus); + } else { + rlen = len; } - for (i = 0; i < len; i++) { + if (rlen > len) { + rlen = 0; + } + for (i = 0; i < rlen; i++) { data[i] = i2c_recv(bus); } i2c_nack(bus); i2c_end_transfer(bus); - return len; + return rlen; } int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, - int len) + int len, bool send_len) { int i; @@ -330,7 +339,9 @@ int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, return -1; } i2c_send(bus, command); - i2c_send(bus, len); + if (send_len) { + i2c_send(bus, len); + } for (i = 0; i < len; i++) { i2c_send(bus, data[i]); } diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 007cb6701d..2a8b49e02f 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -40,6 +40,8 @@ typedef struct ICH9SMBState { PCIDevice dev; + bool irq_enabled; + PMSMBus smb; } ICH9SMBState; @@ -61,12 +63,16 @@ static void ich9_smbus_write_config(PCIDevice *d, uint32_t address, pci_default_write_config(d, address, val, len); if (range_covers_byte(address, len, ICH9_SMB_HOSTC)) { uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC]; - if ((hostc & ICH9_SMB_HOSTC_HST_EN) && - !(hostc & ICH9_SMB_HOSTC_I2C_EN)) { + if (hostc & ICH9_SMB_HOSTC_HST_EN) { memory_region_set_enabled(&s->smb.io, true); } else { memory_region_set_enabled(&s->smb.io, false); } + s->smb.i2c_enable = (hostc & ICH9_SMB_HOSTC_I2C_EN) != 0; + if (hostc & ICH9_SMB_HOSTC_SSRESET) { + s->smb.reset(&s->smb); + s->dev.config[ICH9_SMB_HOSTC] &= ~ICH9_SMB_HOSTC_SSRESET; + } } } @@ -80,7 +86,7 @@ static void ich9_smbus_realize(PCIDevice *d, Error **errp) pci_set_byte(d->config + ICH9_SMB_HOSTC, 0); /* TODO bar0, bar1: 64bit BAR support*/ - pm_smbus_init(&d->qdev, &s->smb); + pm_smbus_init(&d->qdev, &s->smb, false); pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO, &s->smb.io); } @@ -105,11 +111,25 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } +static void ich9_smb_set_irq(PMSMBus *pmsmb, bool enabled) +{ + ICH9SMBState *s = pmsmb->opaque; + + if (enabled == s->irq_enabled) { + return; + } + + s->irq_enabled = enabled; + pci_set_irq(&s->dev, enabled); +} + I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base) { PCIDevice *d = pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE); ICH9SMBState *s = ICH9_SMB_DEVICE(d); + s->smb.set_irq = ich9_smb_set_irq; + s->smb.opaque = s; return s->smb.smbus; } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 83a444472b..03148450c8 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1679,7 +1679,9 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { const PCMachineState *pcms = PC_MACHINE(hotplug_dev); + const PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); + const uint64_t legacy_align = TARGET_PAGE_SIZE; /* * When -no-acpi is used with Q35 machine type, no ACPI is built, @@ -1696,6 +1698,9 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, error_setg(errp, "nvdimm is not enabled: missing 'nvdimm' in '-M'"); return; } + + pc_dimm_pre_plug(dev, MACHINE(hotplug_dev), + pcmc->enforce_aligned_dimm ? NULL : &legacy_align, errp); } static void pc_memory_plug(HotplugHandler *hotplug_dev, @@ -1704,18 +1709,9 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev, HotplugHandlerClass *hhc; Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); - uint64_t align = TARGET_PAGE_SIZE; bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) { - align = memory_region_get_alignment(mr); - } - - pc_dimm_plug(dev, MACHINE(pcms), align, &local_err); + pc_dimm_plug(dev, MACHINE(pcms), &local_err); if (local_err) { goto out; } diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 6fda52b86c..97ffdd820f 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -650,31 +650,17 @@ static void apic_timer(void *opaque) apic_timer_update(s, s->next_time); } -static uint32_t apic_mem_readb(void *opaque, hwaddr addr) -{ - return 0; -} - -static uint32_t apic_mem_readw(void *opaque, hwaddr addr) -{ - return 0; -} - -static void apic_mem_writeb(void *opaque, hwaddr addr, uint32_t val) -{ -} - -static void apic_mem_writew(void *opaque, hwaddr addr, uint32_t val) -{ -} - -static uint32_t apic_mem_readl(void *opaque, hwaddr addr) +static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) { DeviceState *dev; APICCommonState *s; uint32_t val; int index; + if (size < 4) { + return 0; + } + dev = cpu_get_current_apic(); if (!dev) { return 0; @@ -765,11 +751,17 @@ static void apic_send_msi(MSIMessage *msi) apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode); } -static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val) +static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) { DeviceState *dev; APICCommonState *s; int index = (addr >> 4) & 0xff; + + if (size < 4) { + return; + } + if (addr > 0xfff || !index) { /* MSI and MMIO APIC are at the same memory location, * but actually not on the global bus: MSI is on PCI bus @@ -880,10 +872,12 @@ static void apic_post_load(APICCommonState *s) } static const MemoryRegionOps apic_io_ops = { - .old_mmio = { - .read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, }, - .write = { apic_mem_writeb, apic_mem_writew, apic_mem_writel, }, - }, + .read = apic_mem_read, + .write = apic_mem_write, + .impl.min_access_size = 1, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index c1b35fc1ee..542b4b93ea 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -2084,7 +2084,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_cpu; i++) { memory_region_init_io(&s->vifaceiomem[i + 1], OBJECT(s), &gic_viface_ops, &s->backref[i], - "gic_viface", 0x1000); + "gic_viface", 0x200); sysbus_init_mmio(sbd, &s->vifaceiomem[i + 1]); } } diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index e946030e84..8bbb1fa785 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/ipmi/ipmi.h" @@ -450,22 +451,63 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); } -static const VMStateDescription vmstate_ISAIPMIBTDevice = { - .name = TYPE_IPMI_INTERFACE, +static int ipmi_bt_vmstate_post_load(void *opaque, int version) +{ + IPMIBT *ib = opaque; + + /* Make sure all the values are sane. */ + if (ib->outpos >= MAX_IPMI_MSG_SIZE || ib->outlen >= MAX_IPMI_MSG_SIZE || + ib->outpos >= ib->outlen) { + qemu_log_mask(LOG_GUEST_ERROR, + "ipmi:bt: vmstate transfer received bad out values: %d %d\n", + ib->outpos, ib->outlen); + ib->outpos = 0; + ib->outlen = 0; + } + + if (ib->inlen >= MAX_IPMI_MSG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "ipmi:bt: vmstate transfer received bad in value: %d\n", + ib->inlen); + ib->inlen = 0; + } + + return 0; +} + +const VMStateDescription vmstate_IPMIBT = { + .name = TYPE_IPMI_INTERFACE_PREFIX "bt", .version_id = 1, .minimum_version_id = 1, + .post_load = ipmi_bt_vmstate_post_load, + .fields = (VMStateField[]) { + VMSTATE_BOOL(obf_irq_set, IPMIBT), + VMSTATE_BOOL(atn_irq_set, IPMIBT), + VMSTATE_BOOL(irqs_enabled, IPMIBT), + VMSTATE_UINT32(outpos, IPMIBT), + VMSTATE_UINT32(outlen, IPMIBT), + VMSTATE_UINT8_ARRAY(outmsg, IPMIBT, MAX_IPMI_MSG_SIZE), + VMSTATE_UINT32(inlen, IPMIBT), + VMSTATE_UINT8_ARRAY(inmsg, IPMIBT, MAX_IPMI_MSG_SIZE), + VMSTATE_UINT8(control_reg, IPMIBT), + VMSTATE_UINT8(mask_reg, IPMIBT), + VMSTATE_UINT8(waiting_rsp, IPMIBT), + VMSTATE_UINT8(waiting_seq, IPMIBT), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ISAIPMIBTDevice = { + .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt", + .version_id = 2, + .minimum_version_id = 2, + /* + * Version 1 had messed up the array transfer, it's not even usable + * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer + * the buffer length, so random things would happen. + */ .fields = (VMStateField[]) { - VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice), - VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice), - VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice), - VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice), - VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice), - VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, bt.outlen), - VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, bt.inlen), - VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice), - VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice), - VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice), - VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice), + VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), VMSTATE_END_OF_LIST() } }; diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index cff1946232..7302f6d74b 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -370,7 +370,7 @@ static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp) pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x90; - pm_smbus_init(&s->dev.qdev, &s->smb); + pm_smbus_init(&s->dev.qdev, &s->smb, false); memory_region_add_subregion(get_system_io(), s->smb_io_base, &s->smb.io); apm_init(dev, &s->apm, NULL, s); diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 65843bc52a..fb6bcaedc4 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -29,57 +29,60 @@ static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); -void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, - Error **errp) +void pc_dimm_pre_plug(DeviceState *dev, MachineState *machine, + const uint64_t *legacy_align, Error **errp) { - int slot; PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, - &error_abort); - MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); Error *local_err = NULL; - uint64_t addr; + MemoryRegion *mr; + uint64_t addr, align; + int slot; - addr = object_property_get_uint(OBJECT(dimm), - PC_DIMM_ADDR_PROP, &local_err); + slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + &error_abort); + slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot, + machine->ram_slots, &local_err); if (local_err) { goto out; } + object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &error_abort); + trace_mhp_pc_dimm_assigned_slot(slot); - addr = memory_device_get_free_addr(machine, !addr ? NULL : &addr, align, - memory_region_size(mr), &local_err); + mr = ddc->get_memory_region(dimm, &local_err); if (local_err) { goto out; } - object_property_set_uint(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); + align = legacy_align ? *legacy_align : memory_region_get_alignment(mr); + addr = object_property_get_uint(OBJECT(dev), PC_DIMM_ADDR_PROP, + &error_abort); + addr = memory_device_get_free_addr(machine, !addr ? NULL : &addr, align, + memory_region_size(mr), &local_err); if (local_err) { goto out; } trace_mhp_pc_dimm_assigned_address(addr); + object_property_set_uint(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, + &error_abort); +out: + error_propagate(errp, local_err); +} - slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, &local_err); - if (local_err) { - goto out; - } +void pc_dimm_plug(DeviceState *dev, MachineState *machine, Error **errp) +{ + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, + &error_abort); + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); + uint64_t addr; - slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot, - machine->ram_slots, &local_err); - if (local_err) { - goto out; - } - object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &local_err); - if (local_err) { - goto out; - } - trace_mhp_pc_dimm_assigned_slot(slot); + addr = object_property_get_uint(OBJECT(dev), PC_DIMM_ADDR_PROP, + &error_abort); memory_device_plug_region(machine, mr, addr); vmstate_register_ram(vmstate_mr, dev); - -out: - error_propagate(errp, local_err); } void pc_dimm_unplug(DeviceState *dev, MachineState *machine) diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 3467451482..40041d5ec0 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -599,6 +599,208 @@ static void network_init(PCIBus *pci_bus) } } +static void write_bootloader_nanomips(uint8_t *base, int64_t run_addr, + int64_t kernel_entry) +{ + uint16_t *p; + + /* Small bootloader */ + p = (uint16_t *)base; + +#define NM_HI1(VAL) (((VAL) >> 16) & 0x1f) +#define NM_HI2(VAL) \ + (((VAL) & 0xf000) | (((VAL) >> 19) & 0xffc) | (((VAL) >> 31) & 0x1)) +#define NM_LO(VAL) ((VAL) & 0xfff) + + stw_p(p++, 0x2800); stw_p(p++, 0x001c); + /* bc to_here */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + + /* to_here: */ + if (semihosting_get_argc()) { + /* Preserve a0 content as arguments have been passed */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + } else { + stw_p(p++, 0x0080); stw_p(p++, 0x0002); + /* li a0,2 */ + } + + stw_p(p++, 0xe3a0 | NM_HI1(ENVP_ADDR - 64)); + + stw_p(p++, NM_HI2(ENVP_ADDR - 64)); + /* lui sp,%hi(ENVP_ADDR - 64) */ + + stw_p(p++, 0x83bd); stw_p(p++, NM_LO(ENVP_ADDR - 64)); + /* ori sp,sp,%lo(ENVP_ADDR - 64) */ + + stw_p(p++, 0xe0a0 | NM_HI1(ENVP_ADDR)); + + stw_p(p++, NM_HI2(ENVP_ADDR)); + /* lui a1,%hi(ENVP_ADDR) */ + + stw_p(p++, 0x80a5); stw_p(p++, NM_LO(ENVP_ADDR)); + /* ori a1,a1,%lo(ENVP_ADDR) */ + + stw_p(p++, 0xe0c0 | NM_HI1(ENVP_ADDR + 8)); + + stw_p(p++, NM_HI2(ENVP_ADDR + 8)); + /* lui a2,%hi(ENVP_ADDR + 8) */ + + stw_p(p++, 0x80c6); stw_p(p++, NM_LO(ENVP_ADDR + 8)); + /* ori a2,a2,%lo(ENVP_ADDR + 8) */ + + stw_p(p++, 0xe0e0 | NM_HI1(loaderparams.ram_low_size)); + + stw_p(p++, NM_HI2(loaderparams.ram_low_size)); + /* lui a3,%hi(loaderparams.ram_low_size) */ + + stw_p(p++, 0x80e7); stw_p(p++, NM_LO(loaderparams.ram_low_size)); + /* ori a3,a3,%lo(loaderparams.ram_low_size) */ + + /* + * Load BAR registers as done by YAMON: + * + * - set up PCI0 I/O BARs from 0x18000000 to 0x181fffff + * - set up PCI0 MEM0 at 0x10000000, size 0x8000000 + * - set up PCI0 MEM1 at 0x18200000, size 0xbe00000 + * + */ + stw_p(p++, 0xe040); stw_p(p++, 0x0681); + /* lui t1, %hi(0xb4000000) */ + +#ifdef TARGET_WORDS_BIGENDIAN + + stw_p(p++, 0xe020); stw_p(p++, 0x0be1); + /* lui t0, %hi(0xdf000000) */ + + /* 0x68 corresponds to GT_ISD (from hw/mips/gt64xxx_pci.c) */ + stw_p(p++, 0x8422); stw_p(p++, 0x9068); + /* sw t0, 0x68(t1) */ + + stw_p(p++, 0xe040); stw_p(p++, 0x077d); + /* lui t1, %hi(0xbbe00000) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0801); + /* lui t0, %hi(0xc0000000) */ + + /* 0x48 corresponds to GT_PCI0IOLD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9048); + /* sw t0, 0x48(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0800); + /* lui t0, %hi(0x40000000) */ + + /* 0x50 corresponds to GT_PCI0IOHD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9050); + /* sw t0, 0x50(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0001); + /* lui t0, %hi(0x80000000) */ + + /* 0x58 corresponds to GT_PCI0M0LD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9058); + /* sw t0, 0x58(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x07e0); + /* lui t0, %hi(0x3f000000) */ + + /* 0x60 corresponds to GT_PCI0M0HD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9060); + /* sw t0, 0x60(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0821); + /* lui t0, %hi(0xc1000000) */ + + /* 0x80 corresponds to GT_PCI0M1LD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9080); + /* sw t0, 0x80(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0bc0); + /* lui t0, %hi(0x5e000000) */ + +#else + + stw_p(p++, 0x0020); stw_p(p++, 0x00df); + /* addiu[32] t0, $0, 0xdf */ + + /* 0x68 corresponds to GT_ISD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9068); + /* sw t0, 0x68(t1) */ + + /* Use kseg2 remapped address 0x1be00000 */ + stw_p(p++, 0xe040); stw_p(p++, 0x077d); + /* lui t1, %hi(0xbbe00000) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x00c0); + /* addiu[32] t0, $0, 0xc0 */ + + /* 0x48 corresponds to GT_PCI0IOLD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9048); + /* sw t0, 0x48(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x0040); + /* addiu[32] t0, $0, 0x40 */ + + /* 0x50 corresponds to GT_PCI0IOHD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9050); + /* sw t0, 0x50(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x0080); + /* addiu[32] t0, $0, 0x80 */ + + /* 0x58 corresponds to GT_PCI0M0LD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9058); + /* sw t0, 0x58(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x003f); + /* addiu[32] t0, $0, 0x3f */ + + /* 0x60 corresponds to GT_PCI0M0HD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9060); + /* sw t0, 0x60(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x00c1); + /* addiu[32] t0, $0, 0xc1 */ + + /* 0x80 corresponds to GT_PCI0M1LD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9080); + /* sw t0, 0x80(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x005e); + /* addiu[32] t0, $0, 0x5e */ + +#endif + + /* 0x88 corresponds to GT_PCI0M1HD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9088); + /* sw t0, 0x88(t1) */ + + stw_p(p++, 0xe320 | NM_HI1(kernel_entry)); + + stw_p(p++, NM_HI2(kernel_entry)); + /* lui t9,%hi(kernel_entry) */ + + stw_p(p++, 0x8339); stw_p(p++, NM_LO(kernel_entry)); + /* ori t9,t9,%lo(kernel_entry) */ + + stw_p(p++, 0x4bf9); stw_p(p++, 0x0000); + /* jalrc t8 */ +} + /* ROM and pseudo bootloader The following code implements a very very simple bootloader. It first @@ -620,7 +822,6 @@ static void network_init(PCIBus *pci_bus) a2 - 32-bit address of the environment variables table a3 - RAM size in bytes */ - static void write_bootloader(uint8_t *base, int64_t run_addr, int64_t kernel_entry) { @@ -1096,8 +1297,13 @@ void mips_malta_init(MachineState *machine) loaderparams.initrd_filename = initrd_filename; kernel_entry = load_kernel(); - write_bootloader(memory_region_get_ram_ptr(bios), - bootloader_run_addr, kernel_entry); + if (!cpu_supports_isa(machine->cpu_type, ISA_NANOMIPS32)) { + write_bootloader(memory_region_get_ram_ptr(bios), + bootloader_run_addr, kernel_entry); + } else { + write_bootloader_nanomips(memory_region_get_ram_ptr(bios), + bootloader_run_addr, kernel_entry); + } if (kvm_enabled()) { /* Write the bootloader code @ the end of RAM, 1MB reserved */ write_bootloader(memory_region_get_ram_ptr(ram_low_preio) + diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 22714b0851..6d50b03cfd 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -64,8 +64,11 @@ obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o obj-$(CONFIG_MPS2_SCC) += mps2-scc.o obj-$(CONFIG_TZ_MPC) += tz-mpc.o +obj-$(CONFIG_TZ_MSC) += tz-msc.o obj-$(CONFIG_TZ_PPC) += tz-ppc.o obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o +obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o +obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 70eaafd325..145427ae0f 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -21,11 +21,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) uint32_t tmp; int n; uint32_t offset, length, color; - uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; - uint32_t tmp_xres, tmp_yres, tmp_xoffset, tmp_yoffset; - uint32_t tmp_bpp, tmp_pixo, tmp_alpha; - uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, - *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; + + /* + * Copy the current state of the framebuffer config; we will update + * this copy as we process tags and then ask the framebuffer to use + * it at the end. + */ + BCM2835FBConfig fbconfig = s->fbdev->config; + bool fbconfig_updated = false; value &= ~0xf; @@ -141,12 +144,9 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* Frame buffer */ case 0x00040001: /* Allocate buffer */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); - tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; - tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; - tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 12, fbconfig.base); stl_le_phys(&s->dma_as, value + 16, - tmp_xres * tmp_yres * tmp_bpp / 8); + bcm2835_fb_get_size(&fbconfig)); resplen = 8; break; case 0x00048001: /* Release buffer */ @@ -155,86 +155,85 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00040002: /* Blank screen */ resplen = 4; break; - case 0x00040003: /* Get display width/height */ - case 0x00040004: - tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; - tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; - stl_le_phys(&s->dma_as, value + 12, tmp_xres); - stl_le_phys(&s->dma_as, value + 16, tmp_yres); + case 0x00044003: /* Test physical display width/height */ + case 0x00044004: /* Test virtual display width/height */ resplen = 8; break; - case 0x00044003: /* Test display width/height */ - case 0x00044004: + case 0x00048003: /* Set physical display width/height */ + fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040003: /* Get physical display width/height */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xres); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yres); resplen = 8; break; - case 0x00048003: /* Set display width/height */ - case 0x00048004: - xres = ldl_le_phys(&s->dma_as, value + 12); - newxres = &xres; - yres = ldl_le_phys(&s->dma_as, value + 16); - newyres = &yres; + case 0x00048004: /* Set virtual display width/height */ + fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040004: /* Get virtual display width/height */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual); resplen = 8; break; - case 0x00040005: /* Get depth */ - tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; - stl_le_phys(&s->dma_as, value + 12, tmp_bpp); - resplen = 4; - break; case 0x00044005: /* Test depth */ resplen = 4; break; case 0x00048005: /* Set depth */ - bpp = ldl_le_phys(&s->dma_as, value + 12); - newbpp = &bpp; - resplen = 4; - break; - case 0x00040006: /* Get pixel order */ - tmp_pixo = newpixo != NULL ? *newpixo : s->fbdev->pixo; - stl_le_phys(&s->dma_as, value + 12, tmp_pixo); + fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040005: /* Get depth */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp); resplen = 4; break; case 0x00044006: /* Test pixel order */ resplen = 4; break; case 0x00048006: /* Set pixel order */ - pixo = ldl_le_phys(&s->dma_as, value + 12); - newpixo = &pixo; - resplen = 4; - break; - case 0x00040007: /* Get alpha */ - tmp_alpha = newalpha != NULL ? *newalpha : s->fbdev->alpha; - stl_le_phys(&s->dma_as, value + 12, tmp_alpha); + fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040006: /* Get pixel order */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo); resplen = 4; break; case 0x00044007: /* Test pixel alpha */ resplen = 4; break; case 0x00048007: /* Set alpha */ - alpha = ldl_le_phys(&s->dma_as, value + 12); - newalpha = α + fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040007: /* Get alpha */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha); resplen = 4; break; case 0x00040008: /* Get pitch */ - tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; - tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; - stl_le_phys(&s->dma_as, value + 12, tmp_xres * tmp_bpp / 8); + stl_le_phys(&s->dma_as, value + 12, + bcm2835_fb_get_pitch(&fbconfig)); resplen = 4; break; - case 0x00040009: /* Get virtual offset */ - tmp_xoffset = newxoffset != NULL ? *newxoffset : s->fbdev->xoffset; - tmp_yoffset = newyoffset != NULL ? *newyoffset : s->fbdev->yoffset; - stl_le_phys(&s->dma_as, value + 12, tmp_xoffset); - stl_le_phys(&s->dma_as, value + 16, tmp_yoffset); - resplen = 8; - break; case 0x00044009: /* Test virtual offset */ resplen = 8; break; case 0x00048009: /* Set virtual offset */ - xoffset = ldl_le_phys(&s->dma_as, value + 12); - newxoffset = &xoffset; - yoffset = ldl_le_phys(&s->dma_as, value + 16); - newyoffset = &yoffset; + fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040009: /* Get virtual offset */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset); resplen = 8; break; case 0x0004000a: /* Get/Test/Set overscan */ @@ -285,10 +284,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) } /* Reconfigure framebuffer if required */ - if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo - || newalpha) { - bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset, - newyoffset, newbpp, newpixo, newalpha); + if (fbconfig_updated) { + bcm2835_fb_reconfigure(s->fbdev, &fbconfig); } /* Buffer response code */ diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index de4fd8e36d..2222b3e147 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -190,12 +190,13 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, r = s->apbexp[offset_to_ppc_idx(offset)].sp; break; case A_SECMSCINTSTAT: + r = s->secmscintstat; + break; case A_SECMSCINTEN: + r = s->secmscinten; + break; case A_NSMSCEXP: - qemu_log_mask(LOG_UNIMP, - "IoTKit SecCtl S block read: " - "unimplemented offset 0x%x\n", offset); - r = 0; + r = s->nsmscexp; break; case A_PID4: case A_PID5: @@ -291,6 +292,23 @@ static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc) qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1)); } +static void iotkit_secctl_update_mscexp_irqs(qemu_irq *msc_irqs, uint32_t value) +{ + int i; + + for (i = 0; i < IOTS_NUM_EXP_MSC; i++) { + qemu_set_irq(msc_irqs[i], extract32(value, i + 16, 1)); + } +} + +static void iotkit_secctl_update_msc_irq(IoTKitSecCtl *s) +{ + /* Update the combined MSC IRQ, based on S_MSCEXP_STATUS and S_MSCEXP_EN */ + bool level = s->secmscintstat & s->secmscinten; + + qemu_set_irq(s->msc_irq, level); +} + static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) @@ -370,10 +388,15 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, iotkit_secctl_ppc_sp_write(ppc, value); break; case A_SECMSCINTCLR: + iotkit_secctl_update_mscexp_irqs(s->mscexp_clear, value); + break; case A_SECMSCINTEN: - qemu_log_mask(LOG_UNIMP, - "IoTKit SecCtl S block write: " - "unimplemented offset 0x%x\n", offset); + s->secmscinten = value; + iotkit_secctl_update_msc_irq(s); + break; + case A_NSMSCEXP: + s->nsmscexp = value; + iotkit_secctl_update_mscexp_irqs(s->mscexp_ns, value); break; case A_SECMPCINTSTATUS: case A_SECPPCINTSTAT: @@ -381,7 +404,6 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, case A_BRGINTSTAT: case A_AHBNSPPC0: case A_AHBSPPPC0: - case A_NSMSCEXP: case A_PID4: case A_PID5: case A_PID6: @@ -588,6 +610,14 @@ static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level); } +static void iotkit_secctl_mscexp_status(void *opaque, int n, int level) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + s->secmscintstat = deposit32(s->secmscintstat, n + 16, 1, !!level); + iotkit_secctl_update_msc_irq(s); +} + static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level) { IoTKitSecCtlPPC *ppc = opaque; @@ -660,6 +690,14 @@ static void iotkit_secctl_init(Object *obj) qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status, "mpcexp_status", IOTS_NUM_EXP_MPC); + qdev_init_gpio_in_named(dev, iotkit_secctl_mscexp_status, + "mscexp_status", IOTS_NUM_EXP_MSC); + qdev_init_gpio_out_named(dev, s->mscexp_clear, "mscexp_clear", + IOTS_NUM_EXP_MSC); + qdev_init_gpio_out_named(dev, s->mscexp_ns, "mscexp_ns", + IOTS_NUM_EXP_MSC); + qdev_init_gpio_out_named(dev, &s->msc_irq, "msc_irq", 1); + memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops, s, "iotkit-secctl-s-regs", 0x1000); memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops, @@ -690,6 +728,24 @@ static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = { } }; +static bool needed_always(void *opaque) +{ + return true; +} + +static const VMStateDescription iotkit_secctl_msc_vmstate = { + .name = "iotkit-secctl/msc", + .version_id = 1, + .minimum_version_id = 1, + .needed = needed_always, + .fields = (VMStateField[]) { + VMSTATE_UINT32(secmscintstat, IoTKitSecCtl), + VMSTATE_UINT32(secmscinten, IoTKitSecCtl), + VMSTATE_UINT32(nsmscexp, IoTKitSecCtl), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription iotkit_secctl_vmstate = { .name = "iotkit-secctl", .version_id = 1, @@ -710,6 +766,7 @@ static const VMStateDescription iotkit_secctl_vmstate = { }, .subsections = (const VMStateDescription*[]) { &iotkit_secctl_mpcintstatus_vmstate, + &iotkit_secctl_msc_vmstate, NULL }, }; diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c new file mode 100644 index 0000000000..a21d8bd678 --- /dev/null +++ b/hw/misc/iotkit-sysctl.c @@ -0,0 +1,261 @@ +/* + * ARM IoTKit system control element + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system control element" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * Specifically, it implements the "system control register" blocks. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/iotkit-sysctl.h" + +REG32(SECDBGSTAT, 0x0) +REG32(SECDBGSET, 0x4) +REG32(SECDBGCLR, 0x8) +REG32(RESET_SYNDROME, 0x100) +REG32(RESET_MASK, 0x104) +REG32(SWRESET, 0x108) + FIELD(SWRESET, SWRESETREQ, 9, 1) +REG32(GRETREG, 0x10c) +REG32(INITSVRTOR0, 0x110) +REG32(CPUWAIT, 0x118) +REG32(BUSWAIT, 0x11c) +REG32(WICCTRL, 0x120) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int sysctl_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x54, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset, + unsigned size) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque); + uint64_t r; + + switch (offset) { + case A_SECDBGSTAT: + r = s->secure_debug; + break; + case A_RESET_SYNDROME: + r = s->reset_syndrome; + break; + case A_RESET_MASK: + r = s->reset_mask; + break; + case A_GRETREG: + r = s->gretreg; + break; + case A_INITSVRTOR0: + r = s->initsvrtor0; + break; + case A_CPUWAIT: + r = s->cpuwait; + break; + case A_BUSWAIT: + /* In IoTKit BUSWAIT is reserved, R/O, zero */ + r = 0; + break; + case A_WICCTRL: + r = s->wicctrl; + break; + case A_PID4 ... A_CID3: + r = sysctl_id[(offset - A_PID4) / 4]; + break; + case A_SECDBGSET: + case A_SECDBGCLR: + case A_SWRESET: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl read: read of WO offset %x\n", + (int)offset); + r = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl read: bad offset %x\n", (int)offset); + r = 0; + break; + } + trace_iotkit_sysctl_read(offset, r, size); + return r; +} + +static void iotkit_sysctl_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque); + + trace_iotkit_sysctl_write(offset, value, size); + + /* + * Most of the state here has to do with control of reset and + * similar kinds of power up -- for instance the guest can ask + * what the reason for the last reset was, or forbid reset for + * some causes (like the non-secure watchdog). Most of this is + * not relevant to QEMU, which doesn't really model anything other + * than a full power-on reset. + * We just model the registers as reads-as-written. + */ + + switch (offset) { + case A_RESET_SYNDROME: + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl RESET_SYNDROME unimplemented\n"); + s->reset_syndrome = value; + break; + case A_RESET_MASK: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl RESET_MASK unimplemented\n"); + s->reset_mask = value; + break; + case A_GRETREG: + /* + * General retention register, which is only reset by a power-on + * reset. Technically this implementation is complete, since + * QEMU only supports power-on resets... + */ + s->gretreg = value; + break; + case A_INITSVRTOR0: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl INITSVRTOR0 unimplemented\n"); + s->initsvrtor0 = value; + break; + case A_CPUWAIT: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CPUWAIT unimplemented\n"); + s->cpuwait = value; + break; + case A_WICCTRL: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl WICCTRL unimplemented\n"); + s->wicctrl = value; + break; + case A_SECDBGSET: + /* write-1-to-set */ + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SECDBGSET unimplemented\n"); + s->secure_debug |= value; + break; + case A_SECDBGCLR: + /* write-1-to-clear */ + s->secure_debug &= ~value; + break; + case A_SWRESET: + /* One w/o bit to request a reset; all other bits reserved */ + if (value & R_SWRESET_SWRESETREQ_MASK) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + break; + case A_BUSWAIT: /* In IoTKit BUSWAIT is reserved, R/O, zero */ + case A_SECDBGSTAT: + case A_PID4 ... A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl write: write of RO offset %x\n", + (int)offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl write: bad offset %x\n", (int)offset); + break; + } +} + +static const MemoryRegionOps iotkit_sysctl_ops = { + .read = iotkit_sysctl_read, + .write = iotkit_sysctl_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void iotkit_sysctl_reset(DeviceState *dev) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(dev); + + trace_iotkit_sysctl_reset(); + s->secure_debug = 0; + s->reset_syndrome = 1; + s->reset_mask = 0; + s->gretreg = 0; + s->initsvrtor0 = 0x10000000; + s->cpuwait = 0; + s->wicctrl = 0; +} + +static void iotkit_sysctl_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + IoTKitSysCtl *s = IOTKIT_SYSCTL(obj); + + memory_region_init_io(&s->iomem, obj, &iotkit_sysctl_ops, + s, "iotkit-sysctl", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription iotkit_sysctl_vmstate = { + .name = "iotkit-sysctl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(secure_debug, IoTKitSysCtl), + VMSTATE_UINT32(reset_syndrome, IoTKitSysCtl), + VMSTATE_UINT32(reset_mask, IoTKitSysCtl), + VMSTATE_UINT32(gretreg, IoTKitSysCtl), + VMSTATE_UINT32(initsvrtor0, IoTKitSysCtl), + VMSTATE_UINT32(cpuwait, IoTKitSysCtl), + VMSTATE_UINT32(wicctrl, IoTKitSysCtl), + VMSTATE_END_OF_LIST() + } +}; + +static void iotkit_sysctl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &iotkit_sysctl_vmstate; + dc->reset = iotkit_sysctl_reset; +} + +static const TypeInfo iotkit_sysctl_info = { + .name = TYPE_IOTKIT_SYSCTL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IoTKitSysCtl), + .instance_init = iotkit_sysctl_init, + .class_init = iotkit_sysctl_class_init, +}; + +static void iotkit_sysctl_register_types(void) +{ + type_register_static(&iotkit_sysctl_info); +} + +type_init(iotkit_sysctl_register_types); diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c new file mode 100644 index 0000000000..78955bc45f --- /dev/null +++ b/hw/misc/iotkit-sysinfo.c @@ -0,0 +1,128 @@ +/* + * ARM IoTKit system information block + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system information block" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * It consists of 2 read-only version/config registers, plus the + * usual ID registers. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/iotkit-sysinfo.h" + +REG32(SYS_VERSION, 0x0) +REG32(SYS_CONFIG, 0x4) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int sysinfo_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint64_t r; + + switch (offset) { + case A_SYS_VERSION: + r = 0x41743; + break; + + case A_SYS_CONFIG: + r = 0x31; + break; + case A_PID4 ... A_CID3: + r = sysinfo_id[(offset - A_PID4) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysInfo read: bad offset %x\n", (int)offset); + r = 0; + break; + } + trace_iotkit_sysinfo_read(offset, r, size); + return r; +} + +static void iotkit_sysinfo_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + trace_iotkit_sysinfo_write(offset, value, size); + + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysInfo: write to RO offset 0x%x\n", (int)offset); +} + +static const MemoryRegionOps iotkit_sysinfo_ops = { + .read = iotkit_sysinfo_read, + .write = iotkit_sysinfo_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void iotkit_sysinfo_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + IoTKitSysInfo *s = IOTKIT_SYSINFO(obj); + + memory_region_init_io(&s->iomem, obj, &iotkit_sysinfo_ops, + s, "iotkit-sysinfo", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data) +{ + /* + * This device has no guest-modifiable state and so it + * does not need a reset function or VMState. + */ +} + +static const TypeInfo iotkit_sysinfo_info = { + .name = TYPE_IOTKIT_SYSINFO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IoTKitSysInfo), + .instance_init = iotkit_sysinfo_init, + .class_init = iotkit_sysinfo_class_init, +}; + +static void iotkit_sysinfo_register_types(void) +{ + type_register_static(&iotkit_sysinfo_info); +} + +type_init(iotkit_sysinfo_register_types); diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 7394a057d8..5cf10ebd66 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -22,6 +22,7 @@ #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/misc/mps2-fpgaio.h" +#include "qemu/timer.h" REG32(LED0, 0) REG32(BUTTON, 8) @@ -32,10 +33,92 @@ REG32(PRESCALE, 0x1c) REG32(PSCNTR, 0x20) REG32(MISC, 0x4c) +static uint32_t counter_from_tickoff(int64_t now, int64_t tick_offset, int frq) +{ + return muldiv64(now - tick_offset, frq, NANOSECONDS_PER_SECOND); +} + +static int64_t tickoff_from_counter(int64_t now, uint32_t count, int frq) +{ + return now - muldiv64(count, NANOSECONDS_PER_SECOND, frq); +} + +static void resync_counter(MPS2FPGAIO *s) +{ + /* + * Update s->counter and s->pscntr to their true current values + * by calculating how many times PSCNTR has ticked since the + * last time we did a resync. + */ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t elapsed = now - s->pscntr_sync_ticks; + + /* + * Round elapsed down to a whole number of PSCNTR ticks, so we don't + * lose time if we do multiple resyncs in a single tick. + */ + uint64_t ticks = muldiv64(elapsed, s->prescale_clk, NANOSECONDS_PER_SECOND); + + /* + * Work out what PSCNTR and COUNTER have moved to. We assume that + * PSCNTR reloads from PRESCALE one tick-period after it hits zero, + * and that COUNTER increments at the same moment. + */ + if (ticks == 0) { + /* We haven't ticked since the last time we were asked */ + return; + } else if (ticks < s->pscntr) { + /* We haven't yet reached zero, just reduce the PSCNTR */ + s->pscntr -= ticks; + } else { + if (s->prescale == 0) { + /* + * If the reload value is zero then the PSCNTR will stick + * at zero once it reaches it, and so we will increment + * COUNTER every tick after that. + */ + s->counter += ticks - s->pscntr; + s->pscntr = 0; + } else { + /* + * This is the complicated bit. This ASCII art diagram gives an + * example with PRESCALE==5 PSCNTR==7: + * + * ticks 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + * PSCNTR 7 6 5 4 3 2 1 0 5 4 3 2 1 0 5 + * cinc 1 2 + * y 0 1 2 3 4 5 6 7 8 9 10 11 12 + * x 0 1 2 3 4 5 0 1 2 3 4 5 0 + * + * where x = y % (s->prescale + 1) + * and so PSCNTR = s->prescale - x + * and COUNTER is incremented by y / (s->prescale + 1) + * + * The case where PSCNTR < PRESCALE works out the same, + * though we must be careful to calculate y as 64-bit unsigned + * for all parts of the expression. + * y < 0 is not possible because that implies ticks < s->pscntr. + */ + uint64_t y = ticks - s->pscntr + s->prescale; + s->pscntr = s->prescale - (y % (s->prescale + 1)); + s->counter += y / (s->prescale + 1); + } + } + + /* + * Only advance the sync time to the timestamp of the last PSCNTR tick, + * not all the way to 'now', so we don't lose time if we do multiple + * resyncs in a single tick. + */ + s->pscntr_sync_ticks += muldiv64(ticks, NANOSECONDS_PER_SECOND, + s->prescale_clk); +} + static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size) { MPS2FPGAIO *s = MPS2_FPGAIO(opaque); uint64_t r; + int64_t now; switch (offset) { case A_LED0: @@ -54,12 +137,20 @@ static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size) r = s->misc; break; case A_CLK1HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r = counter_from_tickoff(now, s->clk1hz_tick_offset, 1); + break; case A_CLK100HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r = counter_from_tickoff(now, s->clk100hz_tick_offset, 100); + break; case A_COUNTER: + resync_counter(s); + r = s->counter; + break; case A_PSCNTR: - /* These are all upcounters of various frequencies. */ - qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n"); - r = 0; + resync_counter(s); + r = s->pscntr; break; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -76,6 +167,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { MPS2FPGAIO *s = MPS2_FPGAIO(opaque); + int64_t now; trace_mps2_fpgaio_write(offset, value, size); @@ -89,6 +181,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, s->led0 = value & 0x3; break; case A_PRESCALE: + resync_counter(s); s->prescale = value; break; case A_MISC: @@ -100,6 +193,22 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, "MPS2 FPGAIO: MISC control bits unimplemented\n"); s->misc = value; break; + case A_CLK1HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->clk1hz_tick_offset = tickoff_from_counter(now, value, 1); + break; + case A_CLK100HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->clk100hz_tick_offset = tickoff_from_counter(now, value, 100); + break; + case A_COUNTER: + resync_counter(s); + s->counter = value; + break; + case A_PSCNTR: + resync_counter(s); + s->pscntr = value; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset); @@ -116,11 +225,17 @@ static const MemoryRegionOps mps2_fpgaio_ops = { static void mps2_fpgaio_reset(DeviceState *dev) { MPS2FPGAIO *s = MPS2_FPGAIO(dev); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); trace_mps2_fpgaio_reset(); s->led0 = 0; s->prescale = 0; s->misc = 0; + s->clk1hz_tick_offset = tickoff_from_counter(now, 0, 1); + s->clk100hz_tick_offset = tickoff_from_counter(now, 0, 100); + s->counter = 0; + s->pscntr = 0; + s->pscntr_sync_ticks = now; } static void mps2_fpgaio_init(Object *obj) @@ -133,6 +248,27 @@ static void mps2_fpgaio_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } +static bool mps2_fpgaio_counters_needed(void *opaque) +{ + /* Currently vmstate.c insists all subsections have a 'needed' function */ + return true; +} + +static const VMStateDescription mps2_fpgaio_counters_vmstate = { + .name = "mps2-fpgaio/counters", + .version_id = 2, + .minimum_version_id = 2, + .needed = mps2_fpgaio_counters_needed, + .fields = (VMStateField[]) { + VMSTATE_INT64(clk1hz_tick_offset, MPS2FPGAIO), + VMSTATE_INT64(clk100hz_tick_offset, MPS2FPGAIO), + VMSTATE_UINT32(counter, MPS2FPGAIO), + VMSTATE_UINT32(pscntr, MPS2FPGAIO), + VMSTATE_INT64(pscntr_sync_ticks, MPS2FPGAIO), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription mps2_fpgaio_vmstate = { .name = "mps2-fpgaio", .version_id = 1, @@ -142,6 +278,10 @@ static const VMStateDescription mps2_fpgaio_vmstate = { VMSTATE_UINT32(prescale, MPS2FPGAIO), VMSTATE_UINT32(misc, MPS2FPGAIO), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &mps2_fpgaio_counters_vmstate, + NULL } }; diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index b26250dec9..9d8961ba0c 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -99,17 +99,6 @@ static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp) isa_register_ioport(d, &s->io, s->ioport); } -#define PVPANIC_IOPORT_PROP "ioport" - -uint16_t pvpanic_port(void) -{ - Object *o = object_resolve_path_type("", TYPE_PVPANIC, NULL); - if (!o) { - return 0; - } - return object_property_get_uint(o, PVPANIC_IOPORT_PROP, NULL); -} - static Property pvpanic_isa_properties[] = { DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicState, ioport, 0x505), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 1341508b33..52466c77c4 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -92,6 +92,15 @@ tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secur tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s" tz_mpc_iommu_notify(uint64_t addr) "TZ MPC iommu: notifying UNMAP/MAP for 0x%" PRIx64 +# hw/misc/tz-msc.c +tz_msc_reset(void) "TZ MSC: reset" +tz_msc_cfg_nonsec(int level) "TZ MSC: cfg_nonsec = %d" +tz_msc_cfg_sec_resp(int level) "TZ MSC: cfg_sec_resp = %d" +tz_msc_irq_enable(int level) "TZ MSC: int_enable = %d" +tz_msc_irq_clear(int level) "TZ MSC: int_clear = %d" +tz_msc_update_irq(int level) "TZ MSC: setting irq line to %d" +tz_msc_access_blocked(uint64_t offset) "TZ MSC: offset 0x%" PRIx64 " access blocked" + # hw/misc/tz-ppc.c tz_ppc_reset(void) "TZ PPC: reset" tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d" @@ -116,3 +125,10 @@ ccm_freq(uint32_t freq) "freq = %d\n" ccm_clock_freq(uint32_t clock, uint32_t freq) "(Clock = %d) = %d\n" ccm_read_reg(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 "\n" ccm_write_reg(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 "\n" + +# hw/misc/iotkit-sysctl.c +iotkit_sysinfo_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysinfo_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysctl_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysctl_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysctl_reset(void) "IoTKit SysCtl: reset" diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c new file mode 100644 index 0000000000..9e352044ea --- /dev/null +++ b/hw/misc/tz-msc.c @@ -0,0 +1,308 @@ +/* + * ARM TrustZone master security controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/tz-msc.h" + +static void tz_msc_update_irq(TZMSC *s) +{ + bool level = s->irq_status; + + trace_tz_msc_update_irq(level); + qemu_set_irq(s->irq, level); +} + +static void tz_msc_cfg_nonsec(void *opaque, int n, int level) +{ + TZMSC *s = TZ_MSC(opaque); + + trace_tz_msc_cfg_nonsec(level); + s->cfg_nonsec = level; +} + +static void tz_msc_cfg_sec_resp(void *opaque, int n, int level) +{ + TZMSC *s = TZ_MSC(opaque); + + trace_tz_msc_cfg_sec_resp(level); + s->cfg_sec_resp = level; +} + +static void tz_msc_irq_clear(void *opaque, int n, int level) +{ + TZMSC *s = TZ_MSC(opaque); + + trace_tz_msc_irq_clear(level); + + s->irq_clear = level; + if (level) { + s->irq_status = false; + tz_msc_update_irq(s); + } +} + +/* The MSC may either block a transaction by aborting it, block a + * transaction by making it RAZ/WI, allow it through with + * MemTxAttrs indicating a secure transaction, or allow it with + * MemTxAttrs indicating a non-secure transaction. + */ +typedef enum MSCAction { + MSCBlockAbort, + MSCBlockRAZWI, + MSCAllowSecure, + MSCAllowNonSecure, +} MSCAction; + +static MSCAction tz_msc_check(TZMSC *s, hwaddr addr) +{ + /* + * Check whether to allow an access from the bus master, returning + * an MSCAction indicating the required behaviour. If the transaction + * is blocked, the caller must check cfg_sec_resp to determine + * whether to abort or RAZ/WI the transaction. + */ + IDAUInterfaceClass *iic = IDAU_INTERFACE_GET_CLASS(s->idau); + IDAUInterface *ii = IDAU_INTERFACE(s->idau); + bool idau_exempt = false, idau_ns = true, idau_nsc = true; + int idau_region = IREGION_NOTVALID; + + iic->check(ii, addr, &idau_region, &idau_exempt, &idau_ns, &idau_nsc); + + if (idau_exempt) { + /* + * Uncheck region -- OK, transaction type depends on + * whether bus master is configured as Secure or NonSecure + */ + return s->cfg_nonsec ? MSCAllowNonSecure : MSCAllowSecure; + } + + if (idau_ns) { + /* NonSecure region -- always forward as NS transaction */ + return MSCAllowNonSecure; + } + + if (!s->cfg_nonsec) { + /* Access to Secure region by Secure bus master: OK */ + return MSCAllowSecure; + } + + /* Attempted access to Secure region by NS bus master: block */ + trace_tz_msc_access_blocked(addr); + if (!s->cfg_sec_resp) { + return MSCBlockRAZWI; + } + + /* + * The TRM isn't clear on behaviour if irq_clear is high when a + * transaction is blocked. We assume that the MSC behaves like the + * PPC, where holding irq_clear high suppresses the interrupt. + */ + if (!s->irq_clear) { + s->irq_status = true; + tz_msc_update_irq(s); + } + return MSCBlockAbort; +} + +static MemTxResult tz_msc_read(void *opaque, hwaddr addr, uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + TZMSC *s = opaque; + AddressSpace *as = &s->downstream_as; + uint64_t data; + MemTxResult res; + + switch (tz_msc_check(s, addr)) { + case MSCBlockAbort: + return MEMTX_ERROR; + case MSCBlockRAZWI: + *pdata = 0; + return MEMTX_OK; + case MSCAllowSecure: + attrs.secure = 1; + attrs.unspecified = 0; + break; + case MSCAllowNonSecure: + attrs.secure = 0; + attrs.unspecified = 0; + break; + } + + switch (size) { + case 1: + data = address_space_ldub(as, addr, attrs, &res); + break; + case 2: + data = address_space_lduw_le(as, addr, attrs, &res); + break; + case 4: + data = address_space_ldl_le(as, addr, attrs, &res); + break; + case 8: + data = address_space_ldq_le(as, addr, attrs, &res); + break; + default: + g_assert_not_reached(); + } + *pdata = data; + return res; +} + +static MemTxResult tz_msc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + TZMSC *s = opaque; + AddressSpace *as = &s->downstream_as; + MemTxResult res; + + switch (tz_msc_check(s, addr)) { + case MSCBlockAbort: + return MEMTX_ERROR; + case MSCBlockRAZWI: + return MEMTX_OK; + case MSCAllowSecure: + attrs.secure = 1; + attrs.unspecified = 0; + break; + case MSCAllowNonSecure: + attrs.secure = 0; + attrs.unspecified = 0; + break; + } + + switch (size) { + case 1: + address_space_stb(as, addr, val, attrs, &res); + break; + case 2: + address_space_stw_le(as, addr, val, attrs, &res); + break; + case 4: + address_space_stl_le(as, addr, val, attrs, &res); + break; + case 8: + address_space_stq_le(as, addr, val, attrs, &res); + break; + default: + g_assert_not_reached(); + } + return res; +} + +static const MemoryRegionOps tz_msc_ops = { + .read_with_attrs = tz_msc_read, + .write_with_attrs = tz_msc_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void tz_msc_reset(DeviceState *dev) +{ + TZMSC *s = TZ_MSC(dev); + + trace_tz_msc_reset(); + s->cfg_sec_resp = false; + s->cfg_nonsec = false; + s->irq_clear = 0; + s->irq_status = 0; +} + +static void tz_msc_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + TZMSC *s = TZ_MSC(obj); + + qdev_init_gpio_in_named(dev, tz_msc_cfg_nonsec, "cfg_nonsec", 1); + qdev_init_gpio_in_named(dev, tz_msc_cfg_sec_resp, "cfg_sec_resp", 1); + qdev_init_gpio_in_named(dev, tz_msc_irq_clear, "irq_clear", 1); + qdev_init_gpio_out_named(dev, &s->irq, "irq", 1); +} + +static void tz_msc_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + TZMSC *s = TZ_MSC(dev); + const char *name = "tz-msc-downstream"; + uint64_t size; + + /* + * We can't create the upstream end of the port until realize, + * as we don't know the size of the MR used as the downstream until then. + * We insist on having a downstream, to avoid complicating the + * code with handling the "don't know how big this is" case. It's easy + * enough for the user to create an unimplemented_device as downstream + * if they have nothing else to plug into this. + */ + if (!s->downstream) { + error_setg(errp, "MSC 'downstream' link not set"); + return; + } + if (!s->idau) { + error_setg(errp, "MSC 'idau' link not set"); + return; + } + + size = memory_region_size(s->downstream); + address_space_init(&s->downstream_as, s->downstream, name); + memory_region_init_io(&s->upstream, obj, &tz_msc_ops, s, name, size); + sysbus_init_mmio(sbd, &s->upstream); +} + +static const VMStateDescription tz_msc_vmstate = { + .name = "tz-msc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(cfg_nonsec, TZMSC), + VMSTATE_BOOL(cfg_sec_resp, TZMSC), + VMSTATE_BOOL(irq_clear, TZMSC), + VMSTATE_BOOL(irq_status, TZMSC), + VMSTATE_END_OF_LIST() + } +}; + +static Property tz_msc_properties[] = { + DEFINE_PROP_LINK("downstream", TZMSC, downstream, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_LINK("idau", TZMSC, idau, + TYPE_IDAU_INTERFACE, IDAUInterface *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tz_msc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = tz_msc_realize; + dc->vmsd = &tz_msc_vmstate; + dc->reset = tz_msc_reset; + dc->props = tz_msc_properties; +} + +static const TypeInfo tz_msc_info = { + .name = TYPE_TZ_MSC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TZMSC), + .instance_init = tz_msc_init, + .class_init = tz_msc_class_init, +}; + +static void tz_msc_register_types(void) +{ + type_register_static(&tz_msc_info); +} + +type_init(tz_msc_register_types); diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c index a2805527cb..304c6287c7 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -19,7 +19,7 @@ static void fw_cfg_vmci_write(void *dev, off_t offset, size_t len) VMCoreInfoState *s = VMCOREINFO(dev); s->has_vmcoreinfo = offset == 0 && len == sizeof(s->vmcoreinfo) - && s->vmcoreinfo.guest_format != VMCOREINFO_FORMAT_NONE; + && s->vmcoreinfo.guest_format != FW_CFG_VMCOREINFO_FORMAT_NONE; } static void vmcoreinfo_reset(void *dev) @@ -28,7 +28,7 @@ static void vmcoreinfo_reset(void *dev) s->has_vmcoreinfo = false; memset(&s->vmcoreinfo, 0, sizeof(s->vmcoreinfo)); - s->vmcoreinfo.host_format = cpu_to_le16(VMCOREINFO_FORMAT_ELF); + s->vmcoreinfo.host_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF); } static void vmcoreinfo_realize(DeviceState *dev, Error **errp) @@ -53,7 +53,7 @@ static void vmcoreinfo_realize(DeviceState *dev, Error **errp) return; } - fw_cfg_add_file_callback(fw_cfg, "etc/vmcoreinfo", + fw_cfg_add_file_callback(fw_cfg, FW_CFG_VMCOREINFO_FILENAME, NULL, fw_cfg_vmci_write, s, &s->vmcoreinfo, sizeof(s->vmcoreinfo), false); diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 47146ba12a..162b27a3b8 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -608,6 +608,9 @@ static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) rtc_set_memory(rtc, 0x3e, checksum & 0xff); rtc_set_memory(rtc, 0x2f, checksum >> 8); rtc_set_memory(rtc, 0x3f, checksum >> 8); + + object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(rtc), + "date", NULL); } return 0; } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ddd4478a34..4edb6c7d16 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -562,9 +562,12 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) { + CPUState **rev; CPUState *cs; + int n_cpus; int cpus_offset; char *nodename; + int i; cpus_offset = fdt_add_subnode(fdt, 0, "cpus"); _FDT(cpus_offset); @@ -575,8 +578,19 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) * We walk the CPUs in reverse order to ensure that CPU DT nodes * created by fdt_add_subnode() end up in the right order in FDT * for the guest kernel the enumerate the CPUs correctly. + * + * The CPU list cannot be traversed in reverse order, so we need + * to do extra work. */ - CPU_FOREACH_REVERSE(cs) { + n_cpus = 0; + rev = NULL; + CPU_FOREACH(cs) { + rev = g_renew(CPUState *, rev, n_cpus + 1); + rev[n_cpus++] = cs; + } + + for (i = n_cpus - 1; i >= 0; i--) { + CPUState *cs = rev[i]; PowerPCCPU *cpu = POWERPC_CPU(cs); int index = spapr_get_vcpu_id(cpu); DeviceClass *dc = DEVICE_GET_CLASS(cs); @@ -3113,13 +3127,12 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); - uint64_t align, size, addr; + uint64_t size, addr; uint32_t node; - align = memory_region_get_alignment(mr); size = memory_region_size(mr); - pc_dimm_plug(dev, MACHINE(ms), align, &local_err); + pc_dimm_plug(dev, MACHINE(ms), &local_err); if (local_err) { goto out; } @@ -3154,6 +3167,7 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + Error *local_err = NULL; MemoryRegion *mr; uint64_t size; Object *memdev; @@ -3179,7 +3193,13 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, memdev = object_property_get_link(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, &error_abort); pagesize = host_memory_backend_pagesize(MEMORY_BACKEND(memdev)); - spapr_check_pagesize(spapr, pagesize, errp); + spapr_check_pagesize(spapr, pagesize, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pc_dimm_pre_plug(dev, MACHINE(hotplug_dev), NULL, errp); } struct sPAPRDIMMState { diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 160657f4b9..955ba94800 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -959,6 +959,10 @@ static void lsi_do_msgout(LSIState *s) DPRINTF("WDTR (ignored)\n"); lsi_skip_msgbytes(s, 1); break; + case 4: + DPRINTF("PPR (ignored)\n"); + lsi_skip_msgbytes(s, 5); + break; default: goto bad; } diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index ba1afa3c1e..a56317e026 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -464,6 +464,7 @@ static void megasas_unmap_frame(MegasasState *s, MegasasCmd *cmd) cmd->frame = NULL; cmd->pa = 0; cmd->pa_size = 0; + qemu_sglist_destroy(&cmd->qsg); clear_bit(cmd->index, s->frame_map); } @@ -580,7 +581,6 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) static void megasas_complete_command(MegasasCmd *cmd) { - qemu_sglist_destroy(&cmd->qsg); cmd->iov_size = 0; cmd->iov_offset = 0; diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 4176e871e1..929404fb48 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1431,6 +1431,7 @@ static void mptsas1068_class_init(ObjectClass *oc, void *data) dc->reset = mptsas_reset; dc->vmsd = &vmstate_mptsas; dc->desc = "LSI SAS 1068"; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } static const TypeInfo mptsas_info = { diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index e2a5828af1..b7fbab65dd 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c @@ -96,6 +96,9 @@ uint64_t vhost_scsi_common_get_features(VirtIODevice *vdev, uint64_t features, { VHostSCSICommon *vsc = VHOST_SCSI_COMMON(vdev); + /* Turn on predefined features supported by this device */ + features |= vsc->host_features; + return vhost_get_features(&vsc->dev, vsc->feature_bits, features); } diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 9c1bea8ff3..becf550085 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -238,6 +238,9 @@ static Property vhost_scsi_properties[] = { DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors, 0xFFFF), DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128), + DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_T10_PI, + false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 9355cfdf07..2e1ba4a87b 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -137,17 +137,6 @@ static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp) } } -static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev, - uint64_t features, Error **errp) -{ - VHostUserSCSI *s = VHOST_USER_SCSI(vdev); - - /* Turn on predefined features supported by this device */ - features |= s->host_features; - - return vhost_scsi_common_get_features(vdev, features, errp); -} - static Property vhost_user_scsi_properties[] = { DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev), DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0), @@ -157,12 +146,15 @@ static Property vhost_user_scsi_properties[] = { DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors, 0xFFFF), DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128), - DEFINE_PROP_BIT64("hotplug", VHostUserSCSI, host_features, - VIRTIO_SCSI_F_HOTPLUG, - true), - DEFINE_PROP_BIT64("param_change", VHostUserSCSI, host_features, - VIRTIO_SCSI_F_CHANGE, - true), + DEFINE_PROP_BIT64("hotplug", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_HOTPLUG, + true), + DEFINE_PROP_BIT64("param_change", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_CHANGE, + true), + DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_T10_PI, + false), DEFINE_PROP_END_OF_LIST(), }; @@ -187,7 +179,7 @@ static void vhost_user_scsi_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = vhost_user_scsi_realize; vdc->unrealize = vhost_user_scsi_unrealize; - vdc->get_features = vhost_user_scsi_get_features; + vdc->get_features = vhost_scsi_common_get_features; vdc->set_config = vhost_scsi_common_set_config; vdc->set_status = vhost_user_scsi_set_status; fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path; diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c index c1368018ee..e58247554c 100644 --- a/hw/ssi/pl022.c +++ b/hw/ssi/pl022.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" +#include "hw/ssi/pl022.h" #include "hw/ssi/ssi.h" #include "qemu/log.h" @@ -37,35 +38,10 @@ do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0) #define PL022_SR_BSY 0x10 #define PL022_INT_ROR 0x01 -#define PL022_INT_RT 0x04 +#define PL022_INT_RT 0x02 #define PL022_INT_RX 0x04 #define PL022_INT_TX 0x08 -#define TYPE_PL022 "pl022" -#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022) - -typedef struct PL022State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t cr0; - uint32_t cr1; - uint32_t bitmask; - uint32_t sr; - uint32_t cpsr; - uint32_t is; - uint32_t im; - /* The FIFO head points to the next empty entry. */ - int tx_fifo_head; - int rx_fifo_head; - int tx_fifo_len; - int rx_fifo_len; - uint16_t tx_fifo[8]; - uint16_t rx_fifo[8]; - qemu_irq irq; - SSIBus *ssi; -} PL022State; - static const unsigned char pl022_id[8] = { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; @@ -170,7 +146,7 @@ static uint64_t pl022_read(void *opaque, hwaddr offset, return s->is; case 0x1c: /* MIS */ return s->im & s->is; - case 0x20: /* DMACR */ + case 0x24: /* DMACR */ /* Not implemented. */ return 0; default: @@ -216,7 +192,15 @@ static void pl022_write(void *opaque, hwaddr offset, s->im = value; pl022_update(s); break; - case 0x20: /* DMACR */ + case 0x20: /* ICR */ + /* + * write-1-to-clear: bit 0 clears ROR, bit 1 clears RT; + * RX and TX interrupts cannot be cleared this way. + */ + value &= PL022_INT_ROR | PL022_INT_RT; + s->is &= ~value; + break; + case 0x24: /* DMACR */ if (value) { qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n"); } @@ -227,8 +211,10 @@ static void pl022_write(void *opaque, hwaddr offset, } } -static void pl022_reset(PL022State *s) +static void pl022_reset(DeviceState *dev) { + PL022State *s = PL022(dev); + s->rx_fifo_len = 0; s->tx_fifo_len = 0; s->im = 0; @@ -292,25 +278,24 @@ static const VMStateDescription vmstate_pl022 = { } }; -static int pl022_init(SysBusDevice *sbd) +static void pl022_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); PL022State *s = PL022(dev); memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); s->ssi = ssi_create_bus(dev, "ssi"); - pl022_reset(s); - vmstate_register(dev, -1, &vmstate_pl022, s); - return 0; } static void pl022_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = pl022_init; + dc->reset = pl022_reset; + dc->vmsd = &vmstate_pl022; + dc->realize = pl022_realize; } static const TypeInfo pl022_info = { diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index e16b2b913c..b32194d153 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -44,4 +44,5 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o +common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o common-obj-$(CONFIG_MSF2) += mss-timer.o diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c new file mode 100644 index 0000000000..ccd49753b7 --- /dev/null +++ b/hw/timer/cmsdk-apb-dualtimer.c @@ -0,0 +1,515 @@ +/* + * ARM CMSDK APB dual-timer emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB dual-input timer" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" + +REG32(TIMER1LOAD, 0x0) +REG32(TIMER1VALUE, 0x4) +REG32(TIMER1CONTROL, 0x8) + FIELD(CONTROL, ONESHOT, 0, 1) + FIELD(CONTROL, SIZE, 1, 1) + FIELD(CONTROL, PRESCALE, 2, 2) + FIELD(CONTROL, INTEN, 5, 1) + FIELD(CONTROL, MODE, 6, 1) + FIELD(CONTROL, ENABLE, 7, 1) +#define R_CONTROL_VALID_MASK (R_CONTROL_ONESHOT_MASK | R_CONTROL_SIZE_MASK | \ + R_CONTROL_PRESCALE_MASK | R_CONTROL_INTEN_MASK | \ + R_CONTROL_MODE_MASK | R_CONTROL_ENABLE_MASK) +REG32(TIMER1INTCLR, 0xc) +REG32(TIMER1RIS, 0x10) +REG32(TIMER1MIS, 0x14) +REG32(TIMER1BGLOAD, 0x18) +REG32(TIMER2LOAD, 0x20) +REG32(TIMER2VALUE, 0x24) +REG32(TIMER2CONTROL, 0x28) +REG32(TIMER2INTCLR, 0x2c) +REG32(TIMER2RIS, 0x30) +REG32(TIMER2MIS, 0x34) +REG32(TIMER2BGLOAD, 0x38) +REG32(TIMERITCR, 0xf00) + FIELD(TIMERITCR, ENABLE, 0, 1) +#define R_TIMERITCR_VALID_MASK R_TIMERITCR_ENABLE_MASK +REG32(TIMERITOP, 0xf04) + FIELD(TIMERITOP, TIMINT1, 0, 1) + FIELD(TIMERITOP, TIMINT2, 1, 1) +#define R_TIMERITOP_VALID_MASK (R_TIMERITOP_TIMINT1_MASK | \ + R_TIMERITOP_TIMINT2_MASK) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int timer_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x23, 0xb8, 0x1b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static bool cmsdk_dualtimermod_intstatus(CMSDKAPBDualTimerModule *m) +{ + /* Return masked interrupt status for the timer module */ + return m->intstatus && (m->control & R_CONTROL_INTEN_MASK); +} + +static void cmsdk_apb_dualtimer_update(CMSDKAPBDualTimer *s) +{ + bool timint1, timint2, timintc; + + if (s->timeritcr) { + /* Integration test mode: outputs driven directly from TIMERITOP bits */ + timint1 = s->timeritop & R_TIMERITOP_TIMINT1_MASK; + timint2 = s->timeritop & R_TIMERITOP_TIMINT2_MASK; + } else { + timint1 = cmsdk_dualtimermod_intstatus(&s->timermod[0]); + timint2 = cmsdk_dualtimermod_intstatus(&s->timermod[1]); + } + + timintc = timint1 || timint2; + + qemu_set_irq(s->timermod[0].timerint, timint1); + qemu_set_irq(s->timermod[1].timerint, timint2); + qemu_set_irq(s->timerintc, timintc); +} + +static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m, + uint32_t newctrl) +{ + /* Handle a write to the CONTROL register */ + uint32_t changed; + + newctrl &= R_CONTROL_VALID_MASK; + + changed = m->control ^ newctrl; + + if (changed & ~newctrl & R_CONTROL_ENABLE_MASK) { + /* ENABLE cleared, stop timer before any further changes */ + ptimer_stop(m->timer); + } + + if (changed & R_CONTROL_PRESCALE_MASK) { + int divisor; + + switch (FIELD_EX32(newctrl, CONTROL, PRESCALE)) { + case 0: + divisor = 1; + break; + case 1: + divisor = 16; + break; + case 2: + divisor = 256; + break; + case 3: + /* UNDEFINED; complain, and arbitrarily treat like 2 */ + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB dual-timer: CONTROL.PRESCALE==0b11" + " is undefined behaviour\n"); + divisor = 256; + break; + default: + g_assert_not_reached(); + } + ptimer_set_freq(m->timer, m->parent->pclk_frq / divisor); + } + + if (changed & R_CONTROL_MODE_MASK) { + uint32_t load; + if (newctrl & R_CONTROL_MODE_MASK) { + /* Periodic: the limit is the LOAD register value */ + load = m->load; + } else { + /* Free-running: counter wraps around */ + load = ptimer_get_limit(m->timer); + if (!(m->control & R_CONTROL_SIZE_MASK)) { + load = deposit32(m->load, 0, 16, load); + } + m->load = load; + load = 0xffffffff; + } + if (!(m->control & R_CONTROL_SIZE_MASK)) { + load &= 0xffff; + } + ptimer_set_limit(m->timer, load, 0); + } + + if (changed & R_CONTROL_SIZE_MASK) { + /* Timer switched between 16 and 32 bit count */ + uint32_t value, load; + + value = ptimer_get_count(m->timer); + load = ptimer_get_limit(m->timer); + if (newctrl & R_CONTROL_SIZE_MASK) { + /* 16 -> 32, top half of VALUE is in struct field */ + value = deposit32(m->value, 0, 16, value); + } else { + /* 32 -> 16: save top half to struct field and truncate */ + m->value = value; + value &= 0xffff; + } + + if (newctrl & R_CONTROL_MODE_MASK) { + /* Periodic, timer limit has LOAD value */ + if (newctrl & R_CONTROL_SIZE_MASK) { + load = deposit32(m->load, 0, 16, load); + } else { + m->load = load; + load &= 0xffff; + } + } else { + /* Free-running, timer limit is set to give wraparound */ + if (newctrl & R_CONTROL_SIZE_MASK) { + load = 0xffffffff; + } else { + load = 0xffff; + } + } + ptimer_set_count(m->timer, value); + ptimer_set_limit(m->timer, load, 0); + } + + if (newctrl & R_CONTROL_ENABLE_MASK) { + /* + * ENABLE is set; start the timer after all other changes. + * We start it even if the ENABLE bit didn't actually change, + * in case the timer was an expired one-shot timer that has + * now been changed into a free-running or periodic timer. + */ + ptimer_run(m->timer, !!(newctrl & R_CONTROL_ONESHOT_MASK)); + } + + m->control = newctrl; +} + +static uint64_t cmsdk_apb_dualtimer_read(void *opaque, hwaddr offset, + unsigned size) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque); + uint64_t r; + + if (offset >= A_TIMERITCR) { + switch (offset) { + case A_TIMERITCR: + r = s->timeritcr; + break; + case A_PID4 ... A_CID3: + r = timer_id[(offset - A_PID4) / 4]; + break; + default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB dual-timer read: bad offset %x\n", + (int) offset); + r = 0; + break; + } + } else { + int timer = offset >> 5; + CMSDKAPBDualTimerModule *m; + + if (timer >= ARRAY_SIZE(s->timermod)) { + goto bad_offset; + } + + m = &s->timermod[timer]; + + switch (offset & 0x1F) { + case A_TIMER1LOAD: + case A_TIMER1BGLOAD: + if (m->control & R_CONTROL_MODE_MASK) { + /* + * Periodic: the ptimer limit is the LOAD register value, (or + * just the low 16 bits of it if the timer is in 16-bit mode) + */ + r = ptimer_get_limit(m->timer); + if (!(m->control & R_CONTROL_SIZE_MASK)) { + r = deposit32(m->load, 0, 16, r); + } + } else { + /* Free-running: LOAD register value is just in m->load */ + r = m->load; + } + break; + case A_TIMER1VALUE: + r = ptimer_get_count(m->timer); + if (!(m->control & R_CONTROL_SIZE_MASK)) { + r = deposit32(m->value, 0, 16, r); + } + break; + case A_TIMER1CONTROL: + r = m->control; + break; + case A_TIMER1RIS: + r = m->intstatus; + break; + case A_TIMER1MIS: + r = cmsdk_dualtimermod_intstatus(m); + break; + default: + goto bad_offset; + } + } + + trace_cmsdk_apb_dualtimer_read(offset, r, size); + return r; +} + +static void cmsdk_apb_dualtimer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque); + + trace_cmsdk_apb_dualtimer_write(offset, value, size); + + if (offset >= A_TIMERITCR) { + switch (offset) { + case A_TIMERITCR: + s->timeritcr = value & R_TIMERITCR_VALID_MASK; + cmsdk_apb_dualtimer_update(s); + case A_TIMERITOP: + s->timeritop = value & R_TIMERITOP_VALID_MASK; + cmsdk_apb_dualtimer_update(s); + default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB dual-timer write: bad offset %x\n", + (int) offset); + break; + } + } else { + int timer = offset >> 5; + CMSDKAPBDualTimerModule *m; + + if (timer >= ARRAY_SIZE(s->timermod)) { + goto bad_offset; + } + + m = &s->timermod[timer]; + + switch (offset & 0x1F) { + case A_TIMER1LOAD: + /* Set the limit, and immediately reload the count from it */ + m->load = value; + m->value = value; + if (!(m->control & R_CONTROL_SIZE_MASK)) { + value &= 0xffff; + } + if (!(m->control & R_CONTROL_MODE_MASK)) { + /* + * In free-running mode this won't set the limit but will + * still change the current count value. + */ + ptimer_set_count(m->timer, value); + } else { + if (!value) { + ptimer_stop(m->timer); + } + ptimer_set_limit(m->timer, value, 1); + if (value && (m->control & R_CONTROL_ENABLE_MASK)) { + /* Force possibly-expired oneshot timer to restart */ + ptimer_run(m->timer, 1); + } + } + break; + case A_TIMER1BGLOAD: + /* Set the limit, but not the current count */ + m->load = value; + if (!(m->control & R_CONTROL_MODE_MASK)) { + /* In free-running mode there is no limit */ + break; + } + if (!(m->control & R_CONTROL_SIZE_MASK)) { + value &= 0xffff; + } + ptimer_set_limit(m->timer, value, 0); + break; + case A_TIMER1CONTROL: + cmsdk_dualtimermod_write_control(m, value); + cmsdk_apb_dualtimer_update(s); + break; + case A_TIMER1INTCLR: + m->intstatus = 0; + cmsdk_apb_dualtimer_update(s); + break; + default: + goto bad_offset; + } + } +} + +static const MemoryRegionOps cmsdk_apb_dualtimer_ops = { + .read = cmsdk_apb_dualtimer_read, + .write = cmsdk_apb_dualtimer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void cmsdk_dualtimermod_tick(void *opaque) +{ + CMSDKAPBDualTimerModule *m = opaque; + + m->intstatus = 1; + cmsdk_apb_dualtimer_update(m->parent); +} + +static void cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule *m) +{ + m->control = R_CONTROL_INTEN_MASK; + m->intstatus = 0; + m->load = 0; + m->value = 0xffffffff; + ptimer_stop(m->timer); + /* + * We start in free-running mode, with VALUE at 0xffffffff, and + * in 16-bit counter mode. This means that the ptimer count and + * limit must both be set to 0xffff, so we wrap at 16 bits. + */ + ptimer_set_limit(m->timer, 0xffff, 1); + ptimer_set_freq(m->timer, m->parent->pclk_frq); +} + +static void cmsdk_apb_dualtimer_reset(DeviceState *dev) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev); + int i; + + trace_cmsdk_apb_dualtimer_reset(); + + for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { + cmsdk_dualtimermod_reset(&s->timermod[i]); + } + s->timeritcr = 0; + s->timeritop = 0; +} + +static void cmsdk_apb_dualtimer_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(obj); + int i; + + memory_region_init_io(&s->iomem, obj, &cmsdk_apb_dualtimer_ops, + s, "cmsdk-apb-dualtimer", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->timerintc); + + for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { + sysbus_init_irq(sbd, &s->timermod[i].timerint); + } +} + +static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev); + int i; + + if (s->pclk_frq == 0) { + error_setg(errp, "CMSDK APB timer: pclk-frq property must be set"); + return; + } + + for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { + CMSDKAPBDualTimerModule *m = &s->timermod[i]; + QEMUBH *bh = qemu_bh_new(cmsdk_dualtimermod_tick, m); + + m->parent = s; + m->timer = ptimer_init(bh, + PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + } +} + +static const VMStateDescription cmsdk_dualtimermod_vmstate = { + .name = "cmsdk-apb-dualtimer-module", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(timer, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(load, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(value, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(control, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(intstatus, CMSDKAPBDualTimerModule), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription cmsdk_apb_dualtimer_vmstate = { + .name = "cmsdk-apb-dualtimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(timermod, CMSDKAPBDualTimer, + CMSDK_APB_DUALTIMER_NUM_MODULES, + 1, cmsdk_dualtimermod_vmstate, + CMSDKAPBDualTimerModule), + VMSTATE_UINT32(timeritcr, CMSDKAPBDualTimer), + VMSTATE_UINT32(timeritop, CMSDKAPBDualTimer), + VMSTATE_END_OF_LIST() + } +}; + +static Property cmsdk_apb_dualtimer_properties[] = { + DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBDualTimer, pclk_frq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cmsdk_apb_dualtimer_realize; + dc->vmsd = &cmsdk_apb_dualtimer_vmstate; + dc->reset = cmsdk_apb_dualtimer_reset; + dc->props = cmsdk_apb_dualtimer_properties; +} + +static const TypeInfo cmsdk_apb_dualtimer_info = { + .name = TYPE_CMSDK_APB_DUALTIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CMSDKAPBDualTimer), + .instance_init = cmsdk_apb_dualtimer_init, + .class_init = cmsdk_apb_dualtimer_class_init, +}; + +static void cmsdk_apb_dualtimer_register_types(void) +{ + type_register_static(&cmsdk_apb_dualtimer_info); +} + +type_init(cmsdk_apb_dualtimer_register_types); diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 6f1f723b1f..a504f0308d 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -120,7 +120,7 @@ static void rtc_coalesced_timer_update(RTCState *s) timer_del(s->coalesced_timer); } else { /* divide each RTC interval to 2 - 8 smaller intervals */ - int c = MIN(s->irq_coalesced, 7) + 1; + int c = MIN(s->irq_coalesced, 7) + 1; int64_t next_clock = qemu_clock_get_ns(rtc_clock) + periodic_clock_to_ns(s->period / c); timer_mod(s->coalesced_timer, next_clock); @@ -485,7 +485,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, s->cmos_data[s->cmos_index] = data; check_update_timer(s); break; - case RTC_IBM_PS2_CENTURY_BYTE: + case RTC_IBM_PS2_CENTURY_BYTE: s->cmos_index = RTC_CENTURY; /* fall through */ case RTC_CENTURY: @@ -713,7 +713,7 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, return 0xff; } else { switch(s->cmos_index) { - case RTC_IBM_PS2_CENTURY_BYTE: + case RTC_IBM_PS2_CENTURY_BYTE: s->cmos_index = RTC_CENTURY; /* fall through */ case RTC_CENTURY: @@ -915,7 +915,7 @@ static void rtc_reset(void *opaque) if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { s->irq_coalesced = 0; - s->irq_reinject_on_ack_count = 0; + s->irq_reinject_on_ack_count = 0; } } @@ -995,9 +995,6 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) object_property_add_tm(OBJECT(s), "date", rtc_get_date, NULL); - object_property_add_alias(qdev_get_machine(), "rtc-time", - OBJECT(s), "date", NULL); - qdev_init_gpio_out(dev, &s->irq, 1); } @@ -1019,6 +1016,9 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) } QLIST_INSERT_HEAD(&rtc_devices, s, link); + object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(s), + "date", NULL); + return isadev; } @@ -1052,17 +1052,11 @@ static void rtc_class_initfn(ObjectClass *klass, void *data) dc->user_creatable = false; } -static void rtc_finalize(Object *obj) -{ - object_property_del(qdev_get_machine(), "rtc", NULL); -} - static const TypeInfo mc146818rtc_info = { .name = TYPE_MC146818_RTC, .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(RTCState), .class_init = rtc_class_initfn, - .instance_finalize = rtc_finalize, }; static void mc146818rtc_register_types(void) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 5f8736cf10..91b18ba312 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -74,6 +74,7 @@ static uint32_t sh_timer_read(void *opaque, hwaddr offset) case OFFSET_TCPR: if (s->feat & TIMER_FEAT_CAPT) return s->tcpr; + /* fall through */ default: hw_error("sh_timer_read: Bad offset %x\n", (int)offset); return 0; diff --git a/hw/timer/trace-events b/hw/timer/trace-events index e6e042fddb..fa4213df5b 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -61,5 +61,10 @@ cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB t cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset" +# hw/timer/cmsdk_apb_dualtimer.c +cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset" + # hw/timer/xlnx-zynqmp-rtc.c xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d" diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 71cd069478..c67271f1ba 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -113,7 +113,7 @@ void qemu_chr_fe_accept_input(CharBackend *be); /** * @qemu_chr_fe_disconnect: * - * Close a fd accpeted by character backend. + * Close a fd accepted by character backend. * Without associated Chardev, do nothing. */ void qemu_chr_fe_disconnect(CharBackend *be); @@ -122,7 +122,7 @@ void qemu_chr_fe_disconnect(CharBackend *be); * @qemu_chr_fe_wait_connected: * * Wait for characted backend to be connected, return < 0 on error or - * if no assicated Chardev. + * if no associated Chardev. */ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp); @@ -186,7 +186,7 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, * @buf the data * @len the number of bytes to send * - * Returns: the number of bytes consumed (0 if no assicated Chardev) + * Returns: the number of bytes consumed (0 if no associated Chardev) */ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len); @@ -201,7 +201,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len); * @buf the data * @len the number of bytes to send * - * Returns: the number of bytes consumed (0 if no assicated Chardev) + * Returns: the number of bytes consumed (0 if no associated Chardev) */ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len); @@ -213,7 +213,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len); * @buf the data buffer * @len the number of bytes to read * - * Returns: the number of bytes read (0 if no assicated Chardev) + * Returns: the number of bytes read (0 if no associated Chardev) */ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len); diff --git a/include/elf.h b/include/elf.h index 28a5a638e0..312f68af81 100644 --- a/include/elf.h +++ b/include/elf.h @@ -143,6 +143,8 @@ typedef int64_t Elf64_Sxword; #define EM_RISCV 243 /* RISC-V */ +#define EM_NANOMIPS 249 /* Wave Computing nanoMIPS */ + /* * This is an interim value that we will use until the committee comes * up with a final number. diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 69f4dbc4db..cc1b58b029 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -190,49 +190,88 @@ enum { /*---------------------------------------------------------------------------- | Software IEC/IEEE integer-to-floating-point conversion routines. *----------------------------------------------------------------------------*/ + +float16 int16_to_float16_scalbn(int16_t a, int, float_status *status); +float16 int32_to_float16_scalbn(int32_t a, int, float_status *status); +float16 int64_to_float16_scalbn(int64_t a, int, float_status *status); +float16 uint16_to_float16_scalbn(uint16_t a, int, float_status *status); +float16 uint32_to_float16_scalbn(uint32_t a, int, float_status *status); +float16 uint64_to_float16_scalbn(uint64_t a, int, float_status *status); + +float16 int16_to_float16(int16_t a, float_status *status); +float16 int32_to_float16(int32_t a, float_status *status); +float16 int64_to_float16(int64_t a, float_status *status); +float16 uint16_to_float16(uint16_t a, float_status *status); +float16 uint32_to_float16(uint32_t a, float_status *status); +float16 uint64_to_float16(uint64_t a, float_status *status); + +float32 int16_to_float32_scalbn(int16_t, int, float_status *status); +float32 int32_to_float32_scalbn(int32_t, int, float_status *status); +float32 int64_to_float32_scalbn(int64_t, int, float_status *status); +float32 uint16_to_float32_scalbn(uint16_t, int, float_status *status); +float32 uint32_to_float32_scalbn(uint32_t, int, float_status *status); +float32 uint64_to_float32_scalbn(uint64_t, int, float_status *status); + float32 int16_to_float32(int16_t, float_status *status); float32 int32_to_float32(int32_t, float_status *status); -float64 int16_to_float64(int16_t, float_status *status); -float64 int32_to_float64(int32_t, float_status *status); +float32 int64_to_float32(int64_t, float_status *status); float32 uint16_to_float32(uint16_t, float_status *status); float32 uint32_to_float32(uint32_t, float_status *status); +float32 uint64_to_float32(uint64_t, float_status *status); + +float64 int16_to_float64_scalbn(int16_t, int, float_status *status); +float64 int32_to_float64_scalbn(int32_t, int, float_status *status); +float64 int64_to_float64_scalbn(int64_t, int, float_status *status); +float64 uint16_to_float64_scalbn(uint16_t, int, float_status *status); +float64 uint32_to_float64_scalbn(uint32_t, int, float_status *status); +float64 uint64_to_float64_scalbn(uint64_t, int, float_status *status); + +float64 int16_to_float64(int16_t, float_status *status); +float64 int32_to_float64(int32_t, float_status *status); +float64 int64_to_float64(int64_t, float_status *status); float64 uint16_to_float64(uint16_t, float_status *status); float64 uint32_to_float64(uint32_t, float_status *status); +float64 uint64_to_float64(uint64_t, float_status *status); + floatx80 int32_to_floatx80(int32_t, float_status *status); -float128 int32_to_float128(int32_t, float_status *status); -float32 int64_to_float32(int64_t, float_status *status); -float64 int64_to_float64(int64_t, float_status *status); floatx80 int64_to_floatx80(int64_t, float_status *status); + +float128 int32_to_float128(int32_t, float_status *status); float128 int64_to_float128(int64_t, float_status *status); -float32 uint64_to_float32(uint64_t, float_status *status); -float64 uint64_to_float64(uint64_t, float_status *status); float128 uint64_to_float128(uint64_t, float_status *status); /*---------------------------------------------------------------------------- | Software half-precision conversion routines. *----------------------------------------------------------------------------*/ + float16 float32_to_float16(float32, bool ieee, float_status *status); float32 float16_to_float32(float16, bool ieee, float_status *status); float16 float64_to_float16(float64 a, bool ieee, float_status *status); float64 float16_to_float64(float16 a, bool ieee, float_status *status); + +int16_t float16_to_int16_scalbn(float16, int, int, float_status *status); +int32_t float16_to_int32_scalbn(float16, int, int, float_status *status); +int64_t float16_to_int64_scalbn(float16, int, int, float_status *status); + int16_t float16_to_int16(float16, float_status *status); -uint16_t float16_to_uint16(float16 a, float_status *status); -int16_t float16_to_int16_round_to_zero(float16, float_status *status); -uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *status); int32_t float16_to_int32(float16, float_status *status); -uint32_t float16_to_uint32(float16 a, float_status *status); -int32_t float16_to_int32_round_to_zero(float16, float_status *status); -uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *status); int64_t float16_to_int64(float16, float_status *status); -uint64_t float16_to_uint64(float16 a, float_status *status); + +int16_t float16_to_int16_round_to_zero(float16, float_status *status); +int32_t float16_to_int32_round_to_zero(float16, float_status *status); int64_t float16_to_int64_round_to_zero(float16, float_status *status); + +uint16_t float16_to_uint16_scalbn(float16 a, int, int, float_status *status); +uint32_t float16_to_uint32_scalbn(float16 a, int, int, float_status *status); +uint64_t float16_to_uint64_scalbn(float16 a, int, int, float_status *status); + +uint16_t float16_to_uint16(float16 a, float_status *status); +uint32_t float16_to_uint32(float16 a, float_status *status); +uint64_t float16_to_uint64(float16 a, float_status *status); + +uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *status); +uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *status); uint64_t float16_to_uint64_round_to_zero(float16 a, float_status *status); -float16 int16_to_float16(int16_t a, float_status *status); -float16 int32_to_float16(int32_t a, float_status *status); -float16 int64_to_float16(int64_t a, float_status *status); -float16 uint16_to_float16(uint16_t a, float_status *status); -float16 uint32_to_float16(uint32_t a, float_status *status); -float16 uint64_to_float16(uint64_t a, float_status *status); /*---------------------------------------------------------------------------- | Software half-precision operations. @@ -321,18 +360,31 @@ float16 float16_default_nan(float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ + +int16_t float32_to_int16_scalbn(float32, int, int, float_status *status); +int32_t float32_to_int32_scalbn(float32, int, int, float_status *status); +int64_t float32_to_int64_scalbn(float32, int, int, float_status *status); + int16_t float32_to_int16(float32, float_status *status); -uint16_t float32_to_uint16(float32, float_status *status); -int16_t float32_to_int16_round_to_zero(float32, float_status *status); -uint16_t float32_to_uint16_round_to_zero(float32, float_status *status); int32_t float32_to_int32(float32, float_status *status); +int64_t float32_to_int64(float32, float_status *status); + +int16_t float32_to_int16_round_to_zero(float32, float_status *status); int32_t float32_to_int32_round_to_zero(float32, float_status *status); +int64_t float32_to_int64_round_to_zero(float32, float_status *status); + +uint16_t float32_to_uint16_scalbn(float32, int, int, float_status *status); +uint32_t float32_to_uint32_scalbn(float32, int, int, float_status *status); +uint64_t float32_to_uint64_scalbn(float32, int, int, float_status *status); + +uint16_t float32_to_uint16(float32, float_status *status); uint32_t float32_to_uint32(float32, float_status *status); -uint32_t float32_to_uint32_round_to_zero(float32, float_status *status); -int64_t float32_to_int64(float32, float_status *status); uint64_t float32_to_uint64(float32, float_status *status); + +uint16_t float32_to_uint16_round_to_zero(float32, float_status *status); +uint32_t float32_to_uint32_round_to_zero(float32, float_status *status); uint64_t float32_to_uint64_round_to_zero(float32, float_status *status); -int64_t float32_to_int64_round_to_zero(float32, float_status *status); + float64 float32_to_float64(float32, float_status *status); floatx80 float32_to_floatx80(float32, float_status *status); float128 float32_to_float128(float32, float_status *status); @@ -450,18 +502,31 @@ float32 float32_default_nan(float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ + +int16_t float64_to_int16_scalbn(float64, int, int, float_status *status); +int32_t float64_to_int32_scalbn(float64, int, int, float_status *status); +int64_t float64_to_int64_scalbn(float64, int, int, float_status *status); + int16_t float64_to_int16(float64, float_status *status); -uint16_t float64_to_uint16(float64, float_status *status); -int16_t float64_to_int16_round_to_zero(float64, float_status *status); -uint16_t float64_to_uint16_round_to_zero(float64, float_status *status); int32_t float64_to_int32(float64, float_status *status); +int64_t float64_to_int64(float64, float_status *status); + +int16_t float64_to_int16_round_to_zero(float64, float_status *status); int32_t float64_to_int32_round_to_zero(float64, float_status *status); +int64_t float64_to_int64_round_to_zero(float64, float_status *status); + +uint16_t float64_to_uint16_scalbn(float64, int, int, float_status *status); +uint32_t float64_to_uint32_scalbn(float64, int, int, float_status *status); +uint64_t float64_to_uint64_scalbn(float64, int, int, float_status *status); + +uint16_t float64_to_uint16(float64, float_status *status); uint32_t float64_to_uint32(float64, float_status *status); +uint64_t float64_to_uint64(float64, float_status *status); + +uint16_t float64_to_uint16_round_to_zero(float64, float_status *status); uint32_t float64_to_uint32_round_to_zero(float64, float_status *status); -int64_t float64_to_int64(float64, float_status *status); -int64_t float64_to_int64_round_to_zero(float64, float_status *status); -uint64_t float64_to_uint64(float64 a, float_status *status); -uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *status); +uint64_t float64_to_uint64_round_to_zero(float64, float_status *status); + float32 float64_to_float32(float64, float_status *status); floatx80 float64_to_floatx80(float64, float_status *status); float128 float64_to_float128(float64, float_status *status); diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h index 2cddde55dd..3a8ee63908 100644 --- a/include/hw/arm/iotkit.h +++ b/include/hw/arm/iotkit.h @@ -28,6 +28,9 @@ * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which * are wired to the NVIC lines 32 .. n+32 + * + sysbus MMIO region 0 is the "AHB Slave Expansion" which allows + * bus master devices in the board model to make transactions into + * all the devices and memory areas in the IoTKit * Controlling up to 4 AHB expansion PPBs which a system using the IoTKit * might provide: * + named GPIO outputs apb_ppcexp{0,1,2,3}_nonsec[0..15] @@ -45,6 +48,11 @@ * Controlling each of the 16 expansion MPCs which a system using the IoTKit * might provide: * + named GPIO inputs mpcexp_status[0..15] + * Controlling each of the 16 expansion MSCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mscexp_status[0..15] + * + named GPIO outputs mscexp_clear[0..15] + * + named GPIO outputs mscexp_ns[0..15] */ #ifndef IOTKIT_H @@ -56,7 +64,10 @@ #include "hw/misc/tz-ppc.h" #include "hw/misc/tz-mpc.h" #include "hw/timer/cmsdk-apb-timer.h" -#include "hw/misc/unimp.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" +#include "hw/misc/iotkit-sysctl.h" +#include "hw/misc/iotkit-sysinfo.h" #include "hw/or-irq.h" #include "hw/core/split-irq.h" @@ -81,14 +92,22 @@ typedef struct IoTKit { TZMPC mpc; CMSDKAPBTIMER timer0; CMSDKAPBTIMER timer1; + CMSDKAPBTIMER s32ktimer; qemu_or_irq ppc_irq_orgate; SplitIRQ sec_resp_splitter; SplitIRQ ppc_irq_splitter[NUM_PPCS]; SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; qemu_or_irq mpc_irq_orgate; + qemu_or_irq nmi_orgate; + + CMSDKAPBDualTimer dualtimer; + + CMSDKAPBWatchdog s32kwatchdog; + CMSDKAPBWatchdog nswatchdog; + CMSDKAPBWatchdog swatchdog; - UnimplementedDeviceState dualtimer; - UnimplementedDeviceState s32ktimer; + IoTKitSysCtl sysctl; + IoTKitSysCtl sysinfo; MemoryRegion container; MemoryRegion alias1; diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index ae0a3807f2..228988ba05 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -17,6 +17,20 @@ #define TYPE_BCM2835_FB "bcm2835-fb" #define BCM2835_FB(obj) OBJECT_CHECK(BCM2835FBState, (obj), TYPE_BCM2835_FB) +/* + * Configuration information about the fb which the guest can program + * via the mailbox property interface. + */ +typedef struct { + uint32_t xres, yres; + uint32_t xres_virtual, yres_virtual; + uint32_t xoffset, yoffset; + uint32_t bpp; + uint32_t base; + uint32_t pixo; + uint32_t alpha; +} BCM2835FBConfig; + typedef struct { /*< private >*/ SysBusDevice busdev; @@ -31,16 +45,43 @@ typedef struct { qemu_irq mbox_irq; bool lock, invalidate, pending; - uint32_t xres, yres; - uint32_t xres_virtual, yres_virtual; - uint32_t xoffset, yoffset; - uint32_t bpp; - uint32_t base, pitch, size; - uint32_t pixo, alpha; + + BCM2835FBConfig config; + BCM2835FBConfig initial_config; } BCM2835FBState; -void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, - uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, - uint32_t *pixo, uint32_t *alpha); +void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig); + +/** + * bcm2835_fb_get_pitch: return number of bytes per line of the framebuffer + * @config: configuration info for the framebuffer + * + * Return the number of bytes per line of the framebuffer, ie the number + * that must be added to a pixel address to get the address of the pixel + * directly below it on screen. + */ +static inline uint32_t bcm2835_fb_get_pitch(BCM2835FBConfig *config) +{ + uint32_t xres = MAX(config->xres, config->xres_virtual); + return xres * (config->bpp >> 3); +} + +/** + * bcm2835_fb_get_size: return total size of framebuffer in bytes + * @config: configuration info for the framebuffer + */ +static inline uint32_t bcm2835_fb_get_size(BCM2835FBConfig *config) +{ + uint32_t yres = MAX(config->yres, config->yres_virtual); + return yres * bcm2835_fb_get_pitch(config); +} + +/** + * bcm2835_fb_validate_config: check provided config + * + * Validates the configuration information provided by the guest and + * adjusts it if necessary. + */ +void bcm2835_fb_validate_config(BCM2835FBConfig *config); #endif diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index b6e19e35d0..81cecaf27e 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -327,6 +327,14 @@ static int glue(load_elf, SZ)(const char *name, int fd, } } break; + case EM_MIPS: + case EM_NANOMIPS: + if ((ehdr.e_machine != EM_MIPS) && + (ehdr.e_machine != EM_NANOMIPS)) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + break; default: if (elf_machine != ehdr.e_machine) { ret = ELF_LOAD_WRONG_ARCH; diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h index 2a837afdcb..060d3c6ac0 100644 --- a/include/hw/i2c/pm_smbus.h +++ b/include/hw/i2c/pm_smbus.h @@ -1,6 +1,8 @@ #ifndef PM_SMBUS_H #define PM_SMBUS_H +#define PM_SMBUS_MAX_MSG_SIZE 32 + typedef struct PMSMBus { I2CBus *smbus; MemoryRegion io; @@ -11,10 +13,26 @@ typedef struct PMSMBus { uint8_t smb_addr; uint8_t smb_data0; uint8_t smb_data1; - uint8_t smb_data[32]; - uint8_t smb_index; + uint8_t smb_data[PM_SMBUS_MAX_MSG_SIZE]; + uint8_t smb_blkdata; + uint8_t smb_auxctl; + uint32_t smb_index; + + /* Set by pm_smbus.c */ + void (*reset)(struct PMSMBus *s); + + /* Set by the user. */ + bool i2c_enable; + void (*set_irq)(struct PMSMBus *s, bool enabled); + void *opaque; + + /* Internally used by pm_smbus. */ + + /* Set on block transfers after the last byte has been read, so the + INTR bit can be set at the right time. */ + bool op_done; } PMSMBus; -void pm_smbus_init(DeviceState *parent, PMSMBus *smb); +void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk); #endif /* PM_SMBUS_H */ diff --git a/include/hw/i2c/smbus.h b/include/hw/i2c/smbus.h index 4fdba022c1..d8b1b9ee81 100644 --- a/include/hw/i2c/smbus.h +++ b/include/hw/i2c/smbus.h @@ -72,9 +72,22 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command); int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data); int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command); int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data); -int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data); + +/* + * Do a block transfer from an I2C device. If recv_len is set, then the + * first received byte is a length field and is used to know how much data + * to receive. Otherwise receive "len" bytes. If send_cmd is set, send + * the command byte first before receiving the data. + */ +int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool recv_len, bool send_cmd); + +/* + * Do a block transfer to an I2C device. If send_len is set, send the + * "len" value before the data. + */ int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, - int len); + int len, bool send_len); void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf); void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 26ebb7d5e9..b382eb4303 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -79,7 +79,8 @@ typedef struct PCDIMMDeviceClass { Error **errp); } PCDIMMDeviceClass; -void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, - Error **errp); +void pc_dimm_pre_plug(DeviceState *dev, MachineState *machine, + const uint64_t *legacy_align, Error **errp); +void pc_dimm_plug(DeviceState *dev, MachineState *machine, Error **errp); void pc_dimm_unplug(DeviceState *dev, MachineState *machine); #endif diff --git a/include/hw/misc/iotkit-secctl.h b/include/hw/misc/iotkit-secctl.h index 082c14c925..1a193b306f 100644 --- a/include/hw/misc/iotkit-secctl.h +++ b/include/hw/misc/iotkit-secctl.h @@ -19,6 +19,7 @@ * + named GPIO output "sec_resp_cfg" indicating whether blocked accesses * should RAZ/WI or bus error * + named GPIO output "nsc_cfg" whose value tracks the NSCCFG register value + * + named GPIO output "msc_irq" for the combined IRQ line from the MSCs * Controlling the 2 APB PPCs in the IoTKit: * + named GPIO outputs apb_ppc0_nonsec[0..2] and apb_ppc1_nonsec * + named GPIO outputs apb_ppc0_ap[0..2] and apb_ppc1_ap @@ -44,6 +45,11 @@ * Controlling each of the 16 expansion MPCs which a system using the IoTKit * might provide: * + named GPIO inputs mpcexp_status[0..15] + * Controlling each of the 16 expansion MSCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mscexp_status[0..15] + * + named GPIO outputs mscexp_clear[0..15] + * + named GPIO outputs mscexp_ns[0..15] */ #ifndef IOTKIT_SECCTL_H @@ -62,6 +68,7 @@ #define IOTS_NUM_AHB_EXP_PPC 4 #define IOTS_NUM_EXP_MPC 16 #define IOTS_NUM_MPC 1 +#define IOTS_NUM_EXP_MSC 16 typedef struct IoTKitSecCtl IoTKitSecCtl; @@ -103,6 +110,13 @@ struct IoTKitSecCtl { uint32_t brginten; uint32_t mpcintstatus; + uint32_t secmscintstat; + uint32_t secmscinten; + uint32_t nsmscexp; + qemu_irq mscexp_clear[IOTS_NUM_EXP_MSC]; + qemu_irq mscexp_ns[IOTS_NUM_EXP_MSC]; + qemu_irq msc_irq; + IoTKitSecCtlPPC apb[IOTS_NUM_APB_PPC]; IoTKitSecCtlPPC apbexp[IOTS_NUM_APB_EXP_PPC]; IoTKitSecCtlPPC ahbexp[IOTS_NUM_APB_EXP_PPC]; diff --git a/include/hw/misc/iotkit-sysctl.h b/include/hw/misc/iotkit-sysctl.h new file mode 100644 index 0000000000..e36613cb5e --- /dev/null +++ b/include/hw/misc/iotkit-sysctl.h @@ -0,0 +1,49 @@ +/* + * ARM IoTKit system control element + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system control element" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * Specifically, it implements the "system information block" and + * "system control register" blocks. + * + * QEMU interface: + * + sysbus MMIO region 0: the system information register bank + * + sysbus MMIO region 1: the system control register bank + */ + +#ifndef HW_MISC_IOTKIT_SYSCTL_H +#define HW_MISC_IOTKIT_SYSCTL_H + +#include "hw/sysbus.h" + +#define TYPE_IOTKIT_SYSCTL "iotkit-sysctl" +#define IOTKIT_SYSCTL(obj) OBJECT_CHECK(IoTKitSysCtl, (obj), \ + TYPE_IOTKIT_SYSCTL) + +typedef struct IoTKitSysCtl { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + uint32_t secure_debug; + uint32_t reset_syndrome; + uint32_t reset_mask; + uint32_t gretreg; + uint32_t initsvrtor0; + uint32_t cpuwait; + uint32_t wicctrl; +} IoTKitSysCtl; + +#endif diff --git a/include/hw/misc/iotkit-sysinfo.h b/include/hw/misc/iotkit-sysinfo.h new file mode 100644 index 0000000000..7b2e1a5e48 --- /dev/null +++ b/include/hw/misc/iotkit-sysinfo.h @@ -0,0 +1,37 @@ +/* + * ARM IoTKit system information block + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system information block" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * QEMU interface: + * + sysbus MMIO region 0: the system information register bank + */ + +#ifndef HW_MISC_IOTKIT_SYSINFO_H +#define HW_MISC_IOTKIT_SYSINFO_H + +#include "hw/sysbus.h" + +#define TYPE_IOTKIT_SYSINFO "iotkit-sysinfo" +#define IOTKIT_SYSINFO(obj) OBJECT_CHECK(IoTKitSysInfo, (obj), \ + TYPE_IOTKIT_SYSINFO) + +typedef struct IoTKitSysInfo { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; +} IoTKitSysInfo; + +#endif diff --git a/include/hw/misc/mps2-fpgaio.h b/include/hw/misc/mps2-fpgaio.h index eedf17ebc6..69e265cd4b 100644 --- a/include/hw/misc/mps2-fpgaio.h +++ b/include/hw/misc/mps2-fpgaio.h @@ -37,7 +37,17 @@ typedef struct { uint32_t prescale; uint32_t misc; + /* QEMU_CLOCK_VIRTUAL time at which counter and pscntr were last synced */ + int64_t pscntr_sync_ticks; + /* Values of COUNTER and PSCNTR at time pscntr_sync_ticks */ + uint32_t counter; + uint32_t pscntr; + uint32_t prescale_clk; + + /* These hold the CLOCK_VIRTUAL ns tick when the CLK1HZ/CLK100HZ was zero */ + int64_t clk1hz_tick_offset; + int64_t clk100hz_tick_offset; } MPS2FPGAIO; #endif diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h index 36a54e270c..1ee071a703 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -16,6 +16,15 @@ #define TYPE_PVPANIC "pvpanic" -uint16_t pvpanic_port(void); +#define PVPANIC_IOPORT_PROP "ioport" + +static inline uint16_t pvpanic_port(void) +{ + Object *o = object_resolve_path_type("", TYPE_PVPANIC, NULL); + if (!o) { + return 0; + } + return object_property_get_uint(o, PVPANIC_IOPORT_PROP, NULL); +} #endif diff --git a/include/hw/misc/tz-msc.h b/include/hw/misc/tz-msc.h new file mode 100644 index 0000000000..116b96ae9b --- /dev/null +++ b/include/hw/misc/tz-msc.h @@ -0,0 +1,79 @@ +/* + * ARM TrustZone master security controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the TrustZone master security controller (MSC). + * It is documented in the ARM CoreLink SIE-200 System IP for Embedded TRM + * (DDI 0571G): + * https://developer.arm.com/products/architecture/m-profile/docs/ddi0571/g + * + * The MSC sits in front of a device which can be a bus master (such as + * a DMA controller) and allows secure software to configure it to either + * pass through or reject transactions made by that bus master. + * Rejected transactions may be configured to either be aborted, or to + * behave as RAZ/WI. An interrupt can be signalled for a rejected transaction. + * + * The MSC has no register interface -- it is configured purely by a + * collection of input signals from other hardware in the system. Typically + * they are either hardwired or exposed in an ad-hoc register interface by + * the SoC that uses the MSC. + * + * We don't currently implement the irq_enable GPIO input, because on + * the MPS2 FPGA images it is always tied high, which is awkward to + * implement in QEMU. + * + * QEMU interface: + * + Named GPIO input "cfg_nonsec": set to 1 if the bus master should be + * treated as nonsecure, or 0 for secure + * + Named GPIO input "cfg_sec_resp": set to 1 if a rejected transaction should + * result in a transaction error, or 0 for the transaction to RAZ/WI + * + Named GPIO input "irq_clear": set to 1 to clear a pending interrupt + * + Named GPIO output "irq": set for a transaction-failed interrupt + * + Property "downstream": MemoryRegion defining where bus master transactions + * are made if they are not blocked + * + Property "idau": an object implementing IDAUInterface, which defines which + * addresses should be treated as secure and which as non-secure. + * This need not be the same IDAU as the one used by the CPU. + * + sysbus MMIO region 0: MemoryRegion defining the upstream end of the MSC; + * this should be passed to the bus master device as the region it should + * make memory transactions to + */ + +#ifndef TZ_MSC_H +#define TZ_MSC_H + +#include "hw/sysbus.h" +#include "target/arm/idau.h" + +#define TYPE_TZ_MSC "tz-msc" +#define TZ_MSC(obj) OBJECT_CHECK(TZMSC, (obj), TYPE_TZ_MSC) + +typedef struct TZMSC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + + /* State: these just track the values of our input signals */ + bool cfg_nonsec; + bool cfg_sec_resp; + bool irq_clear; + /* State: are we asserting irq ? */ + bool irq_status; + + qemu_irq irq; + MemoryRegion *downstream; + AddressSpace downstream_as; + MemoryRegion upstream; + IDAUInterface *idau; +} TZMSC; + +#endif diff --git a/include/hw/misc/vmcoreinfo.h b/include/hw/misc/vmcoreinfo.h index c3aa856545..0d11578059 100644 --- a/include/hw/misc/vmcoreinfo.h +++ b/include/hw/misc/vmcoreinfo.h @@ -13,20 +13,12 @@ #define VMCOREINFO_H #include "hw/qdev.h" +#include "standard-headers/linux/qemu_fw_cfg.h" #define VMCOREINFO_DEVICE "vmcoreinfo" #define VMCOREINFO(obj) OBJECT_CHECK(VMCoreInfoState, (obj), VMCOREINFO_DEVICE) -#define VMCOREINFO_FORMAT_NONE 0x0 -#define VMCOREINFO_FORMAT_ELF 0x1 - -/* all fields are little-endian */ -typedef struct FWCfgVMCoreInfo { - uint16_t host_format; /* set on reset */ - uint16_t guest_format; - uint32_t size; - uint64_t paddr; -} QEMU_PACKED FWCfgVMCoreInfo; +typedef struct fw_cfg_vmcoreinfo FWCfgVMCoreInfo; typedef struct VMCoreInfoState { DeviceClass parent_obj; diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index b2259cc4a3..f5a6895a74 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -2,7 +2,7 @@ #define FW_CFG_H #include "exec/hwaddr.h" -#include "hw/nvram/fw_cfg_keys.h" +#include "standard-headers/linux/qemu_fw_cfg.h" #include "hw/sysbus.h" #include "sysemu/dma.h" @@ -14,12 +14,7 @@ #define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO) #define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM) -typedef struct FWCfgFile { - uint32_t size; /* file size */ - uint16_t select; /* write this to 0x510 to read it */ - uint16_t reserved; - char name[FW_CFG_MAX_FILE_PATH]; -} FWCfgFile; +typedef struct fw_cfg_file FWCfgFile; #define FW_CFG_ORDER_OVERRIDE_VGA 70 #define FW_CFG_ORDER_OVERRIDE_NIC 80 @@ -34,14 +29,7 @@ typedef struct FWCfgFiles { FWCfgFile f[]; } FWCfgFiles; -/* Control as first field allows for different structures selected by this - * field, which might be useful in the future - */ -typedef struct FWCfgDmaAccess { - uint32_t control; - uint32_t length; - uint64_t address; -} QEMU_PACKED FWCfgDmaAccess; +typedef struct fw_cfg_dma_access FWCfgDmaAccess; typedef void (*FWCfgCallback)(void *opaque); typedef void (*FWCfgWriteCallback)(void *opaque, off_t start, size_t len); diff --git a/include/hw/nvram/fw_cfg_keys.h b/include/hw/nvram/fw_cfg_keys.h deleted file mode 100644 index b6919451f5..0000000000 --- a/include/hw/nvram/fw_cfg_keys.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef FW_CFG_KEYS_H -#define FW_CFG_KEYS_H - -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_UUID 0x02 -#define FW_CFG_RAM_SIZE 0x03 -#define FW_CFG_NOGRAPHIC 0x04 -#define FW_CFG_NB_CPUS 0x05 -#define FW_CFG_MACHINE_ID 0x06 -#define FW_CFG_KERNEL_ADDR 0x07 -#define FW_CFG_KERNEL_SIZE 0x08 -#define FW_CFG_KERNEL_CMDLINE 0x09 -#define FW_CFG_INITRD_ADDR 0x0a -#define FW_CFG_INITRD_SIZE 0x0b -#define FW_CFG_BOOT_DEVICE 0x0c -#define FW_CFG_NUMA 0x0d -#define FW_CFG_BOOT_MENU 0x0e -#define FW_CFG_MAX_CPUS 0x0f -#define FW_CFG_KERNEL_ENTRY 0x10 -#define FW_CFG_KERNEL_DATA 0x11 -#define FW_CFG_INITRD_DATA 0x12 -#define FW_CFG_CMDLINE_ADDR 0x13 -#define FW_CFG_CMDLINE_SIZE 0x14 -#define FW_CFG_CMDLINE_DATA 0x15 -#define FW_CFG_SETUP_ADDR 0x16 -#define FW_CFG_SETUP_SIZE 0x17 -#define FW_CFG_SETUP_DATA 0x18 -#define FW_CFG_FILE_DIR 0x19 - -#define FW_CFG_FILE_FIRST 0x20 -#define FW_CFG_FILE_SLOTS_MIN 0x10 - -#define FW_CFG_WRITE_CHANNEL 0x4000 -#define FW_CFG_ARCH_LOCAL 0x8000 -#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) - -#define FW_CFG_INVALID 0xffff - -/* width in bytes of fw_cfg control register */ -#define FW_CFG_CTL_SIZE 0x02 - -#define FW_CFG_MAX_FILE_PATH 56 - -#endif diff --git a/include/hw/ssi/pl022.h b/include/hw/ssi/pl022.h new file mode 100644 index 0000000000..a080519366 --- /dev/null +++ b/include/hw/ssi/pl022.h @@ -0,0 +1,51 @@ +/* + * ARM PrimeCell PL022 Synchronous Serial Port + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the Arm PrimeCell PL022 synchronous serial port. + * The PL022 TRM is: + * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0194h/DDI0194H_ssp_pl022_trm.pdf + * + * QEMU interface: + * + sysbus IRQ: SSPINTR combined interrupt line + * + sysbus MMIO region 0: MemoryRegion for the device's registers + */ + +#ifndef HW_SSI_PL022_H +#define HW_SSI_PL022_H + +#include "hw/sysbus.h" + +#define TYPE_PL022 "pl022" +#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022) + +typedef struct PL022State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint32_t cr0; + uint32_t cr1; + uint32_t bitmask; + uint32_t sr; + uint32_t cpsr; + uint32_t is; + uint32_t im; + /* The FIFO head points to the next empty entry. */ + int tx_fifo_head; + int rx_fifo_head; + int tx_fifo_len; + int rx_fifo_len; + uint16_t tx_fifo[8]; + uint16_t rx_fifo[8]; + qemu_irq irq; + SSIBus *ssi; +} PL022State; + +#endif diff --git a/include/hw/timer/cmsdk-apb-dualtimer.h b/include/hw/timer/cmsdk-apb-dualtimer.h new file mode 100644 index 0000000000..9843a9dbb1 --- /dev/null +++ b/include/hw/timer/cmsdk-apb-dualtimer.h @@ -0,0 +1,72 @@ +/* + * ARM CMSDK APB dual-timer emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB dual-input timer" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + * + * QEMU interface: + * + QOM property "pclk-frq": frequency at which the timer is clocked + * + sysbus MMIO region 0: the register bank + * + sysbus IRQ 0: combined timer interrupt TIMINTC + * + sysbus IRO 1: timer block 1 interrupt TIMINT1 + * + sysbus IRQ 2: timer block 2 interrupt TIMINT2 + */ + +#ifndef CMSDK_APB_DUALTIMER_H +#define CMSDK_APB_DUALTIMER_H + +#include "hw/sysbus.h" +#include "hw/ptimer.h" + +#define TYPE_CMSDK_APB_DUALTIMER "cmsdk-apb-dualtimer" +#define CMSDK_APB_DUALTIMER(obj) OBJECT_CHECK(CMSDKAPBDualTimer, (obj), \ + TYPE_CMSDK_APB_DUALTIMER) + +typedef struct CMSDKAPBDualTimer CMSDKAPBDualTimer; + +/* One of the two identical timer modules in the dual-timer module */ +typedef struct CMSDKAPBDualTimerModule { + CMSDKAPBDualTimer *parent; + struct ptimer_state *timer; + qemu_irq timerint; + /* + * We must track the guest LOAD and VALUE register state by hand + * rather than leaving this state only in the ptimer limit/count, + * because if CONTROL.SIZE is 0 then only the low 16 bits of the + * counter actually counts, but the high half is still guest + * accessible. + */ + uint32_t load; + uint32_t value; + uint32_t control; + uint32_t intstatus; +} CMSDKAPBDualTimerModule; + +#define CMSDK_APB_DUALTIMER_NUM_MODULES 2 + +struct CMSDKAPBDualTimer { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + qemu_irq timerintc; + uint32_t pclk_frq; + + CMSDKAPBDualTimerModule timermod[CMSDK_APB_DUALTIMER_NUM_MODULES]; + uint32_t timeritcr; + uint32_t timeritop; +}; + +#endif diff --git a/include/hw/virtio/vhost-scsi-common.h b/include/hw/virtio/vhost-scsi-common.h index 4553be4bc3..57fb1d87b5 100644 --- a/include/hw/virtio/vhost-scsi-common.h +++ b/include/hw/virtio/vhost-scsi-common.h @@ -35,6 +35,7 @@ typedef struct VHostSCSICommon { int channel; int target; int lun; + uint64_t host_features; } VHostSCSICommon; int vhost_scsi_common_start(VHostSCSICommon *vsc); diff --git a/include/hw/virtio/vhost-user-scsi.h b/include/hw/virtio/vhost-user-scsi.h index 3ec34ae867..e429cacd8e 100644 --- a/include/hw/virtio/vhost-user-scsi.h +++ b/include/hw/virtio/vhost-user-scsi.h @@ -30,7 +30,6 @@ typedef struct VHostUserSCSI { VHostSCSICommon parent_obj; - uint64_t host_features; VhostUserState *vhost_user; } VHostUserSCSI; diff --git a/include/qapi/qmp/json-lexer.h b/include/qapi/qmp/json-lexer.h deleted file mode 100644 index afee7828cd..0000000000 --- a/include/qapi/qmp/json-lexer.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * JSON lexer - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#ifndef QEMU_JSON_LEXER_H -#define QEMU_JSON_LEXER_H - - -typedef enum json_token_type { - JSON_MIN = 100, - JSON_LCURLY = JSON_MIN, - JSON_RCURLY, - JSON_LSQUARE, - JSON_RSQUARE, - JSON_COLON, - JSON_COMMA, - JSON_INTEGER, - JSON_FLOAT, - JSON_KEYWORD, - JSON_STRING, - JSON_ESCAPE, - JSON_SKIP, - JSON_ERROR, -} JSONTokenType; - -typedef struct JSONLexer JSONLexer; - -typedef void (JSONLexerEmitter)(JSONLexer *, GString *, - JSONTokenType, int x, int y); - -struct JSONLexer -{ - JSONLexerEmitter *emit; - int state; - GString *token; - int x, y; -}; - -void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func); - -int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); - -int json_lexer_flush(JSONLexer *lexer); - -void json_lexer_destroy(JSONLexer *lexer); - -#endif diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h index 102f5c0068..7345a9bd5c 100644 --- a/include/qapi/qmp/json-parser.h +++ b/include/qapi/qmp/json-parser.h @@ -1,5 +1,5 @@ /* - * JSON Parser + * JSON Parser * * Copyright IBM, Corp. 2009 * @@ -11,12 +11,36 @@ * */ -#ifndef QEMU_JSON_PARSER_H -#define QEMU_JSON_PARSER_H +#ifndef QAPI_QMP_JSON_PARSER_H +#define QAPI_QMP_JSON_PARSER_H -#include "qemu-common.h" +typedef struct JSONLexer { + int start_state, state; + GString *token; + int x, y; +} JSONLexer; -QObject *json_parser_parse(GQueue *tokens, va_list *ap); -QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp); +typedef struct JSONMessageParser { + void (*emit)(void *opaque, QObject *json, Error *err); + void *opaque; + va_list *ap; + JSONLexer lexer; + int brace_count; + int bracket_count; + GQueue tokens; + uint64_t token_size; +} JSONMessageParser; + +void json_message_parser_init(JSONMessageParser *parser, + void (*emit)(void *opaque, QObject *json, + Error *err), + void *opaque, va_list *ap); + +void json_message_parser_feed(JSONMessageParser *parser, + const char *buffer, size_t size); + +void json_message_parser_flush(JSONMessageParser *parser); + +void json_message_parser_destroy(JSONMessageParser *parser); #endif diff --git a/include/qapi/qmp/json-streamer.h b/include/qapi/qmp/json-streamer.h deleted file mode 100644 index 00d8a23af8..0000000000 --- a/include/qapi/qmp/json-streamer.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * JSON streaming support - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#ifndef QEMU_JSON_STREAMER_H -#define QEMU_JSON_STREAMER_H - -#include "qapi/qmp/json-lexer.h" - -typedef struct JSONToken { - int type; - int x; - int y; - char str[]; -} JSONToken; - -typedef struct JSONMessageParser -{ - void (*emit)(struct JSONMessageParser *parser, GQueue *tokens); - JSONLexer lexer; - int brace_count; - int bracket_count; - GQueue *tokens; - uint64_t token_size; -} JSONMessageParser; - -void json_message_parser_init(JSONMessageParser *parser, - void (*func)(JSONMessageParser *, GQueue *)); - -int json_message_parser_feed(JSONMessageParser *parser, - const char *buffer, size_t size); - -int json_message_parser_flush(JSONMessageParser *parser); - -void json_message_parser_destroy(JSONMessageParser *parser); - -#endif diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index c82360f429..145571f618 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -61,9 +61,6 @@ #define QERR_IO_ERROR \ "An IO error has occurred" -#define QERR_JSON_PARSING \ - "Invalid JSON syntax" - #define QERR_MIGRATION_ACTIVE \ "There's a migration process in progress" diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h index 45bf02a036..bbae0a5ec8 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qapi/qmp/qnum.h @@ -25,7 +25,7 @@ typedef enum { /* * QNum encapsulates how our dialect of JSON fills in the blanks left - * by the JSON specification (RFC 7159) regarding numbers. + * by the JSON specification (RFC 8259) regarding numbers. * * Conceptually, we treat number as an abstract type with three * concrete subtypes: floating-point, signed integer, unsigned diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 721aa2416a..e59f9ae1e9 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -276,7 +276,9 @@ bool qemu_mutex_iothread_locked(void); * NOTE: tools currently are single-threaded and qemu_mutex_lock_iothread * is a no-op there. */ -void qemu_mutex_lock_iothread(void); +#define qemu_mutex_lock_iothread() \ + qemu_mutex_lock_iothread_impl(__FILE__, __LINE__) +void qemu_mutex_lock_iothread_impl(const char *file, int line); /** * qemu_mutex_unlock_iothread: Unlock the main loop mutex. diff --git a/include/qemu/qht.h b/include/qemu/qht.h index 1fb9116fa0..c9a11cc29a 100644 --- a/include/qemu/qht.h +++ b/include/qemu/qht.h @@ -46,6 +46,7 @@ typedef bool (*qht_lookup_func_t)(const void *obj, const void *userp); typedef void (*qht_iter_func_t)(struct qht *ht, void *p, uint32_t h, void *up); #define QHT_MODE_AUTO_RESIZE 0x1 /* auto-resize when heavily loaded */ +#define QHT_MODE_RAW_MUTEXES 0x2 /* bypass the profiler (QSP) */ /** * qht_init - Initialize a QHT diff --git a/include/qemu/qsp.h b/include/qemu/qsp.h new file mode 100644 index 0000000000..a94c464f90 --- /dev/null +++ b/include/qemu/qsp.h @@ -0,0 +1,29 @@ +/* + * qsp.c - QEMU Synchronization Profiler + * + * Copyright (C) 2018, Emilio G. Cota <cota@braap.org> + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Note: this header file can *only* be included from thread.h. + */ +#ifndef QEMU_QSP_H +#define QEMU_QSP_H + +#include "qemu/fprintf-fn.h" + +enum QSPSortBy { + QSP_SORT_BY_TOTAL_WAIT_TIME, + QSP_SORT_BY_AVG_WAIT_TIME, +}; + +void qsp_report(FILE *f, fprintf_function cpu_fprintf, size_t max, + enum QSPSortBy sort_by, bool callsite_coalesce); + +bool qsp_is_enabled(void); +void qsp_enable(void); +void qsp_disable(void); +void qsp_reset(void); + +#endif /* QEMU_QSP_H */ diff --git a/include/qemu/queue.h b/include/qemu/queue.h index 59fd1203a1..ac418efc43 100644 --- a/include/qemu/queue.h +++ b/include/qemu/queue.h @@ -341,6 +341,7 @@ struct { \ /* * Simple queue access methods. */ +#define QSIMPLEQ_EMPTY_ATOMIC(head) (atomic_read(&((head)->sqh_first)) == NULL) #define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) #define QSIMPLEQ_FIRST(head) ((head)->sqh_first) #define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) diff --git a/include/qemu/rcu_queue.h b/include/qemu/rcu_queue.h index 01be77407b..904b3372dc 100644 --- a/include/qemu/rcu_queue.h +++ b/include/qemu/rcu_queue.h @@ -36,7 +36,7 @@ extern "C" { /* * List access methods. */ -#define QLIST_EMPTY_RCU(head) (atomic_rcu_read(&(head)->lh_first) == NULL) +#define QLIST_EMPTY_RCU(head) (atomic_read(&(head)->lh_first) == NULL) #define QLIST_FIRST_RCU(head) (atomic_rcu_read(&(head)->lh_first)) #define QLIST_NEXT_RCU(elm, field) (atomic_rcu_read(&(elm)->field.le_next)) @@ -112,7 +112,7 @@ extern "C" { (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ } \ - *(elm)->field.le_prev = (elm)->field.le_next; \ + atomic_set((elm)->field.le_prev, (elm)->field.le_next); \ } while (/*CONSTCOND*/0) /* List traversal must occur within an RCU critical section. */ @@ -128,6 +128,137 @@ extern "C" { ((next_var) = atomic_rcu_read(&(var)->field.le_next), 1); \ (var) = (next_var)) +/* + * RCU simple queue + */ + +/* Simple queue access methods */ +#define QSIMPLEQ_EMPTY_RCU(head) (atomic_read(&(head)->sqh_first) == NULL) +#define QSIMPLEQ_FIRST_RCU(head) atomic_rcu_read(&(head)->sqh_first) +#define QSIMPLEQ_NEXT_RCU(elm, field) atomic_rcu_read(&(elm)->field.sqe_next) + +/* Simple queue functions */ +#define QSIMPLEQ_INSERT_HEAD_RCU(head, elm, field) do { \ + (elm)->field.sqe_next = (head)->sqh_first; \ + if ((elm)->field.sqe_next == NULL) { \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + } \ + atomic_rcu_set(&(head)->sqh_first, (elm)); \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_INSERT_TAIL_RCU(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + atomic_rcu_set((head)->sqh_last, (elm)); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ + (elm)->field.sqe_next = (listelm)->field.sqe_next; \ + if ((elm)->field.sqe_next == NULL) { \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + } \ + atomic_rcu_set(&(listelm)->field.sqe_next, (elm)); \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_REMOVE_HEAD_RCU(head, field) do { \ + atomic_set(&(head)->sqh_first, (head)->sqh_first->field.sqe_next); \ + if ((head)->sqh_first == NULL) { \ + (head)->sqh_last = &(head)->sqh_first; \ + } \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_REMOVE_RCU(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + QSIMPLEQ_REMOVE_HEAD_RCU((head), field); \ + } else { \ + struct type *curr = (head)->sqh_first; \ + while (curr->field.sqe_next != (elm)) { \ + curr = curr->field.sqe_next; \ + } \ + atomic_set(&curr->field.sqe_next, \ + curr->field.sqe_next->field.sqe_next); \ + if (curr->field.sqe_next == NULL) { \ + (head)->sqh_last = &(curr)->field.sqe_next; \ + } \ + } \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_FOREACH_RCU(var, head, field) \ + for ((var) = atomic_rcu_read(&(head)->sqh_first); \ + (var); \ + (var) = atomic_rcu_read(&(var)->field.sqe_next)) + +#define QSIMPLEQ_FOREACH_SAFE_RCU(var, head, field, next) \ + for ((var) = atomic_rcu_read(&(head)->sqh_first); \ + (var) && ((next) = atomic_rcu_read(&(var)->field.sqe_next), 1); \ + (var) = (next)) + +/* + * RCU tail queue + */ + +/* Tail queue access methods */ +#define QTAILQ_EMPTY_RCU(head) (atomic_read(&(head)->tqh_first) == NULL) +#define QTAILQ_FIRST_RCU(head) atomic_rcu_read(&(head)->tqh_first) +#define QTAILQ_NEXT_RCU(elm, field) atomic_rcu_read(&(elm)->field.tqe_next) + +/* Tail queue functions */ +#define QTAILQ_INSERT_HEAD_RCU(head, elm, field) do { \ + (elm)->field.tqe_next = (head)->tqh_first; \ + if ((elm)->field.tqe_next != NULL) { \ + (head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \ + } else { \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + } \ + atomic_rcu_set(&(head)->tqh_first, (elm)); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_TAIL_RCU(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + atomic_rcu_set((head)->tqh_last, (elm)); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ + (elm)->field.tqe_next = (listelm)->field.tqe_next; \ + if ((elm)->field.tqe_next != NULL) { \ + (elm)->field.tqe_next->field.tqe_prev = &(elm)->field.tqe_next; \ + } else { \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + } \ + atomic_rcu_set(&(listelm)->field.tqe_next, (elm)); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_BEFORE_RCU(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + atomic_rcu_set((listelm)->field.tqe_prev, (elm)); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ + } while (/*CONSTCOND*/0) + +#define QTAILQ_REMOVE_RCU(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) { \ + (elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \ + } else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + } \ + atomic_set((elm)->field.tqe_prev, (elm)->field.tqe_next); \ + (elm)->field.tqe_prev = NULL; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_FOREACH_RCU(var, head, field) \ + for ((var) = atomic_rcu_read(&(head)->tqh_first); \ + (var); \ + (var) = atomic_rcu_read(&(var)->field.tqe_next)) + +#define QTAILQ_FOREACH_SAFE_RCU(var, head, field, next) \ + for ((var) = atomic_rcu_read(&(head)->tqh_first); \ + (var) && ((next) = atomic_rcu_read(&(var)->field.tqe_next), 1); \ + (var) = (next)) + #ifdef __cplusplus } #endif diff --git a/include/qemu/seqlock.h b/include/qemu/seqlock.h index 8dee11d101..fd408b7ec5 100644 --- a/include/qemu/seqlock.h +++ b/include/qemu/seqlock.h @@ -16,6 +16,7 @@ #include "qemu/atomic.h" #include "qemu/thread.h" +#include "qemu/lockable.h" typedef struct QemuSeqLock QemuSeqLock; @@ -45,7 +46,26 @@ static inline void seqlock_write_end(QemuSeqLock *sl) atomic_set(&sl->sequence, sl->sequence + 1); } -static inline unsigned seqlock_read_begin(QemuSeqLock *sl) +/* Lock out other writers and update the count. */ +static inline void seqlock_write_lock_impl(QemuSeqLock *sl, QemuLockable *lock) +{ + qemu_lockable_lock(lock); + seqlock_write_begin(sl); +} +#define seqlock_write_lock(sl, lock) \ + seqlock_write_lock_impl(sl, QEMU_MAKE_LOCKABLE(lock)) + +/* Lock out other writers and update the count. */ +static inline void seqlock_write_unlock_impl(QemuSeqLock *sl, QemuLockable *lock) +{ + qemu_lockable_unlock(lock); + seqlock_write_begin(sl); +} +#define seqlock_write_unlock(sl, lock) \ + seqlock_write_unlock_impl(sl, QEMU_MAKE_LOCKABLE(lock)) + + +static inline unsigned seqlock_read_begin(const QemuSeqLock *sl) { /* Always fail if a write is in progress. */ unsigned ret = atomic_read(&sl->sequence); diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index fd27b34128..c903525062 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -6,8 +6,8 @@ typedef QemuMutex QemuRecMutex; #define qemu_rec_mutex_destroy qemu_mutex_destroy -#define qemu_rec_mutex_lock qemu_mutex_lock -#define qemu_rec_mutex_trylock qemu_mutex_trylock +#define qemu_rec_mutex_lock_impl qemu_mutex_lock_impl +#define qemu_rec_mutex_trylock_impl qemu_mutex_trylock_impl #define qemu_rec_mutex_unlock qemu_mutex_unlock struct QemuMutex { diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h index d668d789b4..50af5dd7ab 100644 --- a/include/qemu/thread-win32.h +++ b/include/qemu/thread-win32.h @@ -19,8 +19,9 @@ struct QemuRecMutex { }; void qemu_rec_mutex_destroy(QemuRecMutex *mutex); -void qemu_rec_mutex_lock(QemuRecMutex *mutex); -int qemu_rec_mutex_trylock(QemuRecMutex *mutex); +void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line); +int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, + int line); void qemu_rec_mutex_unlock(QemuRecMutex *mutex); struct QemuCond { diff --git a/include/qemu/thread.h b/include/qemu/thread.h index ef7bd16123..dacebcfff0 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -16,6 +16,9 @@ typedef struct QemuThread QemuThread; #include "qemu/thread-posix.h" #endif +/* include QSP header once QemuMutex, QemuCond etc. are defined */ +#include "qemu/qsp.h" + #define QEMU_THREAD_JOINABLE 0 #define QEMU_THREAD_DETACHED 1 @@ -25,10 +28,52 @@ int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line); void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line); void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line); -#define qemu_mutex_lock(mutex) \ - qemu_mutex_lock_impl(mutex, __FILE__, __LINE__) -#define qemu_mutex_trylock(mutex) \ - qemu_mutex_trylock_impl(mutex, __FILE__, __LINE__) +typedef void (*QemuMutexLockFunc)(QemuMutex *m, const char *f, int l); +typedef int (*QemuMutexTrylockFunc)(QemuMutex *m, const char *f, int l); +typedef void (*QemuRecMutexLockFunc)(QemuRecMutex *m, const char *f, int l); +typedef int (*QemuRecMutexTrylockFunc)(QemuRecMutex *m, const char *f, int l); +typedef void (*QemuCondWaitFunc)(QemuCond *c, QemuMutex *m, const char *f, + int l); + +extern QemuMutexLockFunc qemu_bql_mutex_lock_func; +extern QemuMutexLockFunc qemu_mutex_lock_func; +extern QemuMutexTrylockFunc qemu_mutex_trylock_func; +extern QemuRecMutexLockFunc qemu_rec_mutex_lock_func; +extern QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func; +extern QemuCondWaitFunc qemu_cond_wait_func; + +/* convenience macros to bypass the profiler */ +#define qemu_mutex_lock__raw(m) \ + qemu_mutex_lock_impl(m, __FILE__, __LINE__) +#define qemu_mutex_trylock__raw(m) \ + qemu_mutex_trylock_impl(m, __FILE__, __LINE__) + +#define qemu_mutex_lock(m) ({ \ + QemuMutexLockFunc _f = atomic_read(&qemu_mutex_lock_func); \ + _f(m, __FILE__, __LINE__); \ + }) + +#define qemu_mutex_trylock(m) ({ \ + QemuMutexTrylockFunc _f = atomic_read(&qemu_mutex_trylock_func); \ + _f(m, __FILE__, __LINE__); \ + }) + +#define qemu_rec_mutex_lock(m) ({ \ + QemuRecMutexLockFunc _f = atomic_read(&qemu_rec_mutex_lock_func); \ + _f(m, __FILE__, __LINE__); \ + }) + +#define qemu_rec_mutex_trylock(m) ({ \ + QemuRecMutexTrylockFunc _f; \ + _f = atomic_read(&qemu_rec_mutex_trylock_func); \ + _f(m, __FILE__, __LINE__); \ + }) + +#define qemu_cond_wait(c, m) ({ \ + QemuCondWaitFunc _f = atomic_read(&qemu_cond_wait_func); \ + _f(c, m, __FILE__, __LINE__); \ + }) + #define qemu_mutex_unlock(mutex) \ qemu_mutex_unlock_impl(mutex, __FILE__, __LINE__) @@ -47,6 +92,16 @@ static inline void (qemu_mutex_unlock)(QemuMutex *mutex) qemu_mutex_unlock(mutex); } +static inline void (qemu_rec_mutex_lock)(QemuRecMutex *mutex) +{ + qemu_rec_mutex_lock(mutex); +} + +static inline int (qemu_rec_mutex_trylock)(QemuRecMutex *mutex) +{ + return qemu_rec_mutex_trylock(mutex); +} + /* Prototypes for other functions are in thread-posix.h/thread-win32.h. */ void qemu_rec_mutex_init(QemuRecMutex *mutex); @@ -63,9 +118,6 @@ void qemu_cond_broadcast(QemuCond *cond); void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, const int line); -#define qemu_cond_wait(cond, mutex) \ - qemu_cond_wait_impl(cond, mutex, __FILE__, __LINE__) - static inline void (qemu_cond_wait)(QemuCond *cond, QemuMutex *mutex) { qemu_cond_wait(cond, mutex); diff --git a/include/qemu/unicode.h b/include/qemu/unicode.h index 71c72db461..7fa10b8e60 100644 --- a/include/qemu/unicode.h +++ b/include/qemu/unicode.h @@ -2,5 +2,6 @@ #define QEMU_UNICODE_H int mod_utf8_codepoint(const char *s, size_t n, char **end); +ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint); #endif diff --git a/include/qom/cpu.h b/include/qom/cpu.h index ecf6ed556a..dc130cd307 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -26,6 +26,7 @@ #include "exec/memattrs.h" #include "qapi/qapi-types-run-state.h" #include "qemu/bitmap.h" +#include "qemu/rcu_queue.h" #include "qemu/queue.h" #include "qemu/thread.h" @@ -442,13 +443,11 @@ struct CPUState { QTAILQ_HEAD(CPUTailQ, CPUState); extern struct CPUTailQ cpus; -#define CPU_NEXT(cpu) QTAILQ_NEXT(cpu, node) -#define CPU_FOREACH(cpu) QTAILQ_FOREACH(cpu, &cpus, node) +#define first_cpu QTAILQ_FIRST_RCU(&cpus) +#define CPU_NEXT(cpu) QTAILQ_NEXT_RCU(cpu, node) +#define CPU_FOREACH(cpu) QTAILQ_FOREACH_RCU(cpu, &cpus, node) #define CPU_FOREACH_SAFE(cpu, next_cpu) \ - QTAILQ_FOREACH_SAFE(cpu, &cpus, node, next_cpu) -#define CPU_FOREACH_REVERSE(cpu) \ - QTAILQ_FOREACH_REVERSE(cpu, &cpus, CPUTailQ, node) -#define first_cpu QTAILQ_FIRST(&cpus) + QTAILQ_FOREACH_SAFE_RCU(cpu, &cpus, node, next_cpu) extern __thread CPUState *current_cpu; diff --git a/include/standard-headers/linux/qemu_fw_cfg.h b/include/standard-headers/linux/qemu_fw_cfg.h new file mode 100644 index 0000000000..cb93f6678d --- /dev/null +++ b/include/standard-headers/linux/qemu_fw_cfg.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef _LINUX_FW_CFG_H +#define _LINUX_FW_CFG_H + +#include "standard-headers/linux/types.h" + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID 0x02 +#define FW_CFG_RAM_SIZE 0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA 0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS 0x0f +#define FW_CFG_KERNEL_ENTRY 0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR 0x13 +#define FW_CFG_CMDLINE_SIZE 0x14 +#define FW_CFG_CMDLINE_DATA 0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR 0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS_MIN 0x10 + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK (~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)) + +#define FW_CFG_INVALID 0xffff + +/* width in bytes of fw_cfg control register */ +#define FW_CFG_CTL_SIZE 0x02 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 + +/* fw_cfg file directory entry type */ +struct fw_cfg_file { + uint32_t size; + uint16_t select; + uint16_t reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +/* FW_CFG_DMA_CONTROL bits */ +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ 0x02 +#define FW_CFG_DMA_CTL_SKIP 0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 +#define FW_CFG_DMA_CTL_WRITE 0x10 + +#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */ + +/* Control as first field allows for different structures selected by this + * field, which might be useful in the future + */ +struct fw_cfg_dma_access { + uint32_t control; + uint32_t length; + uint64_t address; +}; + +#define FW_CFG_VMCOREINFO_FILENAME "etc/vmcoreinfo" + +#define FW_CFG_VMCOREINFO_FORMAT_NONE 0x0 +#define FW_CFG_VMCOREINFO_FORMAT_ELF 0x1 + +struct fw_cfg_vmcoreinfo { + uint16_t host_format; + uint16_t guest_format; + uint32_t size; + uint64_t paddr; +}; + +#endif @@ -732,10 +732,10 @@ static void job_cancel_async(Job *job, bool force) { if (job->user_paused) { /* Do not call job_enter here, the caller will handle it. */ - job->user_paused = false; if (job->driver->user_resume) { job->driver->user_resume(job); } + job->user_paused = false; assert(job->pause_count > 0); job->pause_count--; } diff --git a/linux-user/elfload.c b/linux-user/elfload.c index df07055361..8638612aec 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -853,6 +853,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #endif #define ELF_ARCH EM_MIPS +#define elf_check_arch(x) ((x) == EM_MIPS || (x) == EM_NANOMIPS) + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { diff --git a/linux-user/main.c b/linux-user/main.c index ea00dd9057..923cbb753a 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -126,7 +126,7 @@ void fork_end(int child) Discard information about the parent threads. */ CPU_FOREACH_SAFE(cpu, next_cpu) { if (cpu != thread_cpu) { - QTAILQ_REMOVE(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus, cpu, node); } } qemu_init_cpu_list(); diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 084ad6a041..c9c20cf8b7 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -397,10 +397,13 @@ static int do_store_exclusive(CPUMIPSState *env) target_ulong addr; target_ulong page_addr; target_ulong val; + uint32_t val_wp = 0; + uint32_t llnewval_wp = 0; int flags; int segv = 0; int reg; int d; + int wp; addr = env->lladdr; page_addr = addr & TARGET_PAGE_MASK; @@ -412,19 +415,31 @@ static int do_store_exclusive(CPUMIPSState *env) } else { reg = env->llreg & 0x1f; d = (env->llreg & 0x20) != 0; - if (d) { - segv = get_user_s64(val, addr); + wp = (env->llreg & 0x40) != 0; + if (!wp) { + if (d) { + segv = get_user_s64(val, addr); + } else { + segv = get_user_s32(val, addr); + } } else { segv = get_user_s32(val, addr); + segv |= get_user_s32(val_wp, addr); + llnewval_wp = env->llnewval_wp; } if (!segv) { - if (val != env->llval) { + if (val != env->llval && val_wp == llnewval_wp) { env->active_tc.gpr[reg] = 0; } else { - if (d) { - segv = put_user_u64(env->llnewval, addr); + if (!wp) { + if (d) { + segv = put_user_u64(env->llnewval, addr); + } else { + segv = put_user_u32(env->llnewval, addr); + } } else { segv = put_user_u32(env->llnewval, addr); + segv |= put_user_u32(env->llnewval_wp, addr + 4); } if (!segv) { env->active_tc.gpr[reg] = 1; @@ -732,6 +747,9 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) if (regs->cp0_epc & 1) { env->hflags |= MIPS_HFLAG_M16; } + if (env->insn_flags & ISA_NANOMIPS32) { + return; + } if (((info->elf_flags & EF_MIPS_NAN2008) != 0) != ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) { if ((env->active_fpu.fcr31_rw_bitmask & diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 02fba7606d..850b72a0c7 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8132,7 +8132,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, TaskState *ts; /* Remove the CPU from the list. */ - QTAILQ_REMOVE(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus, cpu, node); cpu_list_unlock(); diff --git a/migration/colo.c b/migration/colo.c index 4381067ed4..88936f5962 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -534,6 +534,7 @@ void *colo_process_incoming_thread(void *opaque) uint64_t value; Error *local_err = NULL; + rcu_register_thread(); qemu_sem_init(&mis->colo_incoming_sem, 0); migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, @@ -666,5 +667,6 @@ out: } migration_incoming_exit_colo(); + rcu_unregister_thread(); return NULL; } diff --git a/migration/migration.c b/migration/migration.c index b7d9854bda..4b316ec343 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -71,6 +71,7 @@ /* Define default autoconverge cpu throttle migration parameters */ #define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20 #define DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT 10 +#define DEFAULT_MIGRATE_MAX_CPU_THROTTLE 99 /* Migration XBZRLE default cache size */ #define DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE (64 * 1024 * 1024) @@ -389,6 +390,7 @@ static void process_incoming_migration_co(void *opaque) int ret; assert(mis->from_src_file); + mis->migration_incoming_co = qemu_coroutine_self(); mis->largest_page_size = qemu_ram_pagesize_largest(); postcopy_state_set(POSTCOPY_INCOMING_NONE); migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, @@ -418,7 +420,6 @@ static void process_incoming_migration_co(void *opaque) /* we get COLO info, and know if we are in COLO mode */ if (!ret && migration_incoming_enable_colo()) { - mis->migration_incoming_co = qemu_coroutine_self(); qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming", colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE); mis->have_colo_incoming_thread = true; @@ -442,6 +443,7 @@ static void process_incoming_migration_co(void *opaque) } mis->bh = qemu_bh_new(process_incoming_migration_bh, mis); qemu_bh_schedule(mis->bh); + mis->migration_incoming_co = NULL; } static void migration_incoming_setup(QEMUFile *f) @@ -671,6 +673,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->compress_level = s->parameters.compress_level; params->has_compress_threads = true; params->compress_threads = s->parameters.compress_threads; + params->has_compress_wait_thread = true; + params->compress_wait_thread = s->parameters.compress_wait_thread; params->has_decompress_threads = true; params->decompress_threads = s->parameters.decompress_threads; params->has_cpu_throttle_initial = true; @@ -697,6 +701,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->xbzrle_cache_size = s->parameters.xbzrle_cache_size; params->has_max_postcopy_bandwidth = true; params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth; + params->has_max_cpu_throttle = true; + params->max_cpu_throttle = s->parameters.max_cpu_throttle; return params; } @@ -1043,6 +1049,15 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } + if (params->has_max_cpu_throttle && + (params->max_cpu_throttle < params->cpu_throttle_initial || + params->max_cpu_throttle > 99)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "max_cpu_throttle", + "an integer in the range of cpu_throttle_initial to 99"); + return false; + } + return true; } @@ -1061,6 +1076,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, dest->compress_threads = params->compress_threads; } + if (params->has_compress_wait_thread) { + dest->compress_wait_thread = params->compress_wait_thread; + } + if (params->has_decompress_threads) { dest->decompress_threads = params->decompress_threads; } @@ -1110,6 +1129,9 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_max_postcopy_bandwidth) { dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth; } + if (params->has_max_cpu_throttle) { + dest->max_cpu_throttle = params->max_cpu_throttle; + } } static void migrate_params_apply(MigrateSetParameters *params, Error **errp) @@ -1126,6 +1148,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) s->parameters.compress_threads = params->compress_threads; } + if (params->has_compress_wait_thread) { + s->parameters.compress_wait_thread = params->compress_wait_thread; + } + if (params->has_decompress_threads) { s->parameters.decompress_threads = params->decompress_threads; } @@ -1185,6 +1211,9 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_max_postcopy_bandwidth) { s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth; } + if (params->has_max_cpu_throttle) { + s->parameters.max_cpu_throttle = params->max_cpu_throttle; + } } void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) @@ -1871,6 +1900,15 @@ int migrate_compress_threads(void) return s->parameters.compress_threads; } +int migrate_compress_wait_thread(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->parameters.compress_wait_thread; +} + int migrate_decompress_threads(void) { MigrationState *s; @@ -1962,7 +2000,6 @@ static int64_t migrate_max_postcopy_bandwidth(void) return s->parameters.max_postcopy_bandwidth; } - bool migrate_use_block(void) { MigrationState *s; @@ -2104,6 +2141,7 @@ static void *source_return_path_thread(void *opaque) int res; trace_source_return_path_thread_entry(); + rcu_register_thread(); retry: while (!ms->rp_state.error && !qemu_file_get_error(rp) && @@ -2243,6 +2281,7 @@ out: trace_source_return_path_thread_end(); ms->rp_state.from_dst_file = NULL; qemu_fclose(rp); + rcu_unregister_thread(); return NULL; } @@ -3131,6 +3170,8 @@ static Property migration_properties[] = { DEFINE_PROP_UINT8("x-compress-threads", MigrationState, parameters.compress_threads, DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT), + DEFINE_PROP_BOOL("x-compress-wait-thread", MigrationState, + parameters.compress_wait_thread, true), DEFINE_PROP_UINT8("x-decompress-threads", MigrationState, parameters.decompress_threads, DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT), @@ -3160,6 +3201,9 @@ static Property migration_properties[] = { DEFINE_PROP_SIZE("max-postcopy-bandwidth", MigrationState, parameters.max_postcopy_bandwidth, DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH), + DEFINE_PROP_UINT8("max-cpu-throttle", MigrationState, + parameters.max_cpu_throttle, + DEFAULT_MIGRATE_MAX_CPU_THROTTLE), /* Migration capabilities */ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), @@ -3230,6 +3274,7 @@ static void migration_instance_init(Object *obj) params->has_x_multifd_page_count = true; params->has_xbzrle_cache_size = true; params->has_max_postcopy_bandwidth = true; + params->has_max_cpu_throttle = true; qemu_sem_init(&ms->postcopy_pause_sem, 0); qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); diff --git a/migration/migration.h b/migration/migration.h index 64a7b33735..f7813f8261 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -266,11 +266,13 @@ bool migrate_colo_enabled(void); bool migrate_use_block(void); bool migrate_use_block_incremental(void); +int migrate_max_cpu_throttle(void); bool migrate_use_return_path(void); bool migrate_use_compression(void); int migrate_compress_level(void); int migrate_compress_threads(void); +int migrate_compress_wait_thread(void); int migrate_decompress_threads(void); bool migrate_use_events(void); bool migrate_postcopy_blocktime(void); diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index c2e387ed44..853d8b32ca 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -867,6 +867,7 @@ static void *postcopy_ram_fault_thread(void *opaque) RAMBlock *rb = NULL; trace_postcopy_ram_fault_thread_entry(); + rcu_register_thread(); mis->last_rb = NULL; /* last RAMBlock we sent part of */ qemu_sem_post(&mis->fault_thread_sem); @@ -1073,6 +1074,7 @@ retry: } } } + rcu_unregister_thread(); trace_postcopy_ram_fault_thread_exit(); g_free(pfd); return NULL; diff --git a/migration/qemu-file-channel.c b/migration/qemu-file-channel.c index e202d73834..8e639eb496 100644 --- a/migration/qemu-file-channel.c +++ b/migration/qemu-file-channel.c @@ -49,7 +49,11 @@ static ssize_t channel_writev_buffer(void *opaque, ssize_t len; len = qio_channel_writev(ioc, local_iov, nlocal_iov, NULL); if (len == QIO_CHANNEL_ERR_BLOCK) { - qio_channel_wait(ioc, G_IO_OUT); + if (qemu_in_coroutine()) { + qio_channel_yield(ioc, G_IO_OUT); + } else { + qio_channel_wait(ioc, G_IO_OUT); + } continue; } if (len < 0) { @@ -80,7 +84,11 @@ static ssize_t channel_get_buffer(void *opaque, ret = qio_channel_read(ioc, (char *)buf, size, NULL); if (ret < 0) { if (ret == QIO_CHANNEL_ERR_BLOCK) { - qio_channel_yield(ioc, G_IO_IN); + if (qemu_in_coroutine()) { + qio_channel_yield(ioc, G_IO_IN); + } else { + qio_channel_wait(ioc, G_IO_IN); + } } else { /* XXX handle Error * object */ return -EIO; diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 0463f4c321..977b9ae07c 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -253,8 +253,12 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, if (f->hooks && f->hooks->save_page) { int ret = f->hooks->save_page(f, f->opaque, block_offset, offset, size, bytes_sent); - f->bytes_xfer += size; - if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { + f->bytes_xfer += size; + } + + if (ret != RAM_SAVE_CONTROL_DELAYED && + ret != RAM_SAVE_CONTROL_NOT_SUPP) { if (bytes_sent && *bytes_sent > 0) { qemu_update_position(f, *bytes_sent); } else if (ret < 0) { diff --git a/migration/ram.c b/migration/ram.c index fa79d0a5b9..79c89425a3 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -341,6 +341,7 @@ typedef struct PageSearchStatus PageSearchStatus; struct CompressParam { bool done; bool quit; + bool zero_page; QEMUFile *file; QemuMutex mutex; QemuCond cond; @@ -382,14 +383,15 @@ static QemuThread *decompress_threads; static QemuMutex decomp_done_lock; static QemuCond decomp_done_cond; -static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset, uint8_t *source_buf); +static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, + ram_addr_t offset, uint8_t *source_buf); static void *do_data_compress(void *opaque) { CompressParam *param = opaque; RAMBlock *block; ram_addr_t offset; + bool zero_page; qemu_mutex_lock(¶m->mutex); while (!param->quit) { @@ -399,11 +401,12 @@ static void *do_data_compress(void *opaque) param->block = NULL; qemu_mutex_unlock(¶m->mutex); - do_compress_ram_page(param->file, ¶m->stream, block, offset, - param->originbuf); + zero_page = do_compress_ram_page(param->file, ¶m->stream, + block, offset, param->originbuf); qemu_mutex_lock(&comp_done_lock); param->done = true; + param->zero_page = zero_page; qemu_cond_signal(&comp_done_cond); qemu_mutex_unlock(&comp_done_lock); @@ -989,6 +992,7 @@ static void *multifd_send_thread(void *opaque) int ret; trace_multifd_send_thread_start(p->id); + rcu_register_thread(); if (multifd_send_initial_packet(p, &local_err) < 0) { goto out; @@ -1051,6 +1055,7 @@ out: p->running = false; qemu_mutex_unlock(&p->mutex); + rcu_unregister_thread(); trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages); return NULL; @@ -1220,6 +1225,7 @@ static void *multifd_recv_thread(void *opaque) int ret; trace_multifd_recv_thread_start(p->id); + rcu_register_thread(); while (true) { uint32_t used; @@ -1266,6 +1272,7 @@ static void *multifd_recv_thread(void *opaque) p->running = false; qemu_mutex_unlock(&p->mutex); + rcu_unregister_thread(); trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages); return NULL; @@ -1391,13 +1398,15 @@ static void mig_throttle_guest_down(void) MigrationState *s = migrate_get_current(); uint64_t pct_initial = s->parameters.cpu_throttle_initial; uint64_t pct_icrement = s->parameters.cpu_throttle_increment; + int pct_max = s->parameters.max_cpu_throttle; /* We have not started throttling yet. Let's start it. */ if (!cpu_throttle_active()) { cpu_throttle_set(pct_initial); } else { /* Throttling already on, just increase the rate */ - cpu_throttle_set(cpu_throttle_get_percentage() + pct_icrement); + cpu_throttle_set(MIN(cpu_throttle_get_percentage() + pct_icrement, + pct_max)); } } @@ -1666,6 +1675,31 @@ static void migration_bitmap_sync(RAMState *rs) } /** + * save_zero_page_to_file: send the zero page to the file + * + * Returns the size of data written to the file, 0 means the page is not + * a zero page + * + * @rs: current RAM state + * @file: the file where the data is saved + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + */ +static int save_zero_page_to_file(RAMState *rs, QEMUFile *file, + RAMBlock *block, ram_addr_t offset) +{ + uint8_t *p = block->host + offset; + int len = 0; + + if (is_zero_range(p, TARGET_PAGE_SIZE)) { + len += save_page_header(rs, file, block, offset | RAM_SAVE_FLAG_ZERO); + qemu_put_byte(file, 0); + len += 1; + } + return len; +} + +/** * save_zero_page: send the zero page to the stream * * Returns the number of pages written. @@ -1676,19 +1710,14 @@ static void migration_bitmap_sync(RAMState *rs) */ static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) { - uint8_t *p = block->host + offset; - int pages = -1; + int len = save_zero_page_to_file(rs, rs->f, block, offset); - if (is_zero_range(p, TARGET_PAGE_SIZE)) { + if (len) { ram_counters.duplicate++; - ram_counters.transferred += - save_page_header(rs, rs->f, block, offset | RAM_SAVE_FLAG_ZERO); - qemu_put_byte(rs->f, 0); - ram_counters.transferred += 1; - pages = 1; + ram_counters.transferred += len; + return 1; } - - return pages; + return -1; } static void ram_release_pages(const char *rbname, uint64_t offset, int pages) @@ -1823,15 +1852,20 @@ static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, return 1; } -static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset, uint8_t *source_buf) +static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, + ram_addr_t offset, uint8_t *source_buf) { RAMState *rs = ram_state; - int bytes_sent, blen; uint8_t *p = block->host + (offset & TARGET_PAGE_MASK); + bool zero_page = false; + int ret; + + if (save_zero_page_to_file(rs, f, block, offset)) { + zero_page = true; + goto exit; + } - bytes_sent = save_page_header(rs, f, block, offset | - RAM_SAVE_FLAG_COMPRESS_PAGE); + save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); /* * copy it to a internal buffer to avoid it being modified by VM @@ -1839,17 +1873,25 @@ static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, * decompression */ memcpy(source_buf, p, TARGET_PAGE_SIZE); - blen = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); - if (blen < 0) { - bytes_sent = 0; - qemu_file_set_error(migrate_get_current()->to_dst_file, blen); + ret = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); + if (ret < 0) { + qemu_file_set_error(migrate_get_current()->to_dst_file, ret); error_report("compressed data failed!"); - } else { - bytes_sent += blen; - ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1); + return false; } - return bytes_sent; +exit: + ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1); + return zero_page; +} + +static void +update_compress_thread_counts(const CompressParam *param, int bytes_xmit) +{ + if (param->zero_page) { + ram_counters.duplicate++; + } + ram_counters.transferred += bytes_xmit; } static void flush_compressed_data(RAMState *rs) @@ -1873,7 +1915,12 @@ static void flush_compressed_data(RAMState *rs) qemu_mutex_lock(&comp_param[idx].mutex); if (!comp_param[idx].quit) { len = qemu_put_qemu_file(rs->f, comp_param[idx].file); - ram_counters.transferred += len; + /* + * it's safe to fetch zero_page without holding comp_done_lock + * as there is no further request submitted to the thread, + * i.e, the thread should be waiting for a request at this point. + */ + update_compress_thread_counts(&comp_param[idx], len); } qemu_mutex_unlock(&comp_param[idx].mutex); } @@ -1890,30 +1937,33 @@ static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block, ram_addr_t offset) { int idx, thread_count, bytes_xmit = -1, pages = -1; + bool wait = migrate_compress_wait_thread(); thread_count = migrate_compress_threads(); qemu_mutex_lock(&comp_done_lock); - while (true) { - for (idx = 0; idx < thread_count; idx++) { - if (comp_param[idx].done) { - comp_param[idx].done = false; - bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file); - qemu_mutex_lock(&comp_param[idx].mutex); - set_compress_params(&comp_param[idx], block, offset); - qemu_cond_signal(&comp_param[idx].cond); - qemu_mutex_unlock(&comp_param[idx].mutex); - pages = 1; - ram_counters.normal++; - ram_counters.transferred += bytes_xmit; - break; - } - } - if (pages > 0) { +retry: + for (idx = 0; idx < thread_count; idx++) { + if (comp_param[idx].done) { + comp_param[idx].done = false; + bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file); + qemu_mutex_lock(&comp_param[idx].mutex); + set_compress_params(&comp_param[idx], block, offset); + qemu_cond_signal(&comp_param[idx].cond); + qemu_mutex_unlock(&comp_param[idx].mutex); + pages = 1; + update_compress_thread_counts(&comp_param[idx], bytes_xmit); break; - } else { - qemu_cond_wait(&comp_done_cond, &comp_done_lock); } } + + /* + * wait for the free thread if the user specifies 'compress-wait-thread', + * otherwise we will post the page out in the main thread as normal page. + */ + if (pages < 0 && wait) { + qemu_cond_wait(&comp_done_cond, &comp_done_lock); + goto retry; + } qemu_mutex_unlock(&comp_done_lock); return pages; @@ -1983,6 +2033,10 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) { RAMBlock *block = NULL; + if (QSIMPLEQ_EMPTY_ATOMIC(&rs->src_page_requests)) { + return NULL; + } + qemu_mutex_lock(&rs->src_page_req_mutex); if (!QSIMPLEQ_EMPTY(&rs->src_page_requests)) { struct RAMSrcPageRequest *entry = @@ -2175,6 +2229,39 @@ static bool save_page_use_compression(RAMState *rs) return false; } +/* + * try to compress the page before posting it out, return true if the page + * has been properly handled by compression, otherwise needs other + * paths to handle it + */ +static bool save_compress_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) +{ + if (!save_page_use_compression(rs)) { + return false; + } + + /* + * When starting the process of a new block, the first page of + * the block should be sent out before other pages in the same + * block, and all the pages in last block should have been sent + * out, keeping this order is important, because the 'cont' flag + * is used to avoid resending the block name. + * + * We post the fist page as normal page as compression will take + * much CPU resource. + */ + if (block != rs->last_sent_block) { + flush_compressed_data(rs); + return false; + } + + if (compress_page_with_multi_thread(rs, block, offset) > 0) { + return true; + } + + return false; +} + /** * ram_save_target_page: save one target page * @@ -2195,15 +2282,8 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, return res; } - /* - * When starting the process of a new block, the first page of - * the block should be sent out before other pages in the same - * block, and all the pages in last block should have been sent - * out, keeping this order is important, because the 'cont' flag - * is used to avoid resending the block name. - */ - if (block != rs->last_sent_block && save_page_use_compression(rs)) { - flush_compressed_data(rs); + if (save_compress_page(rs, block, offset)) { + return 1; } res = save_zero_page(rs, block, offset); @@ -2221,14 +2301,10 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, } /* - * Make sure the first page is sent out before other pages. - * - * we post it as normal page as compression will take much - * CPU resource. + * do not use multifd for compression as the first page in the new + * block should be posted out before sending the compressed page */ - if (block == rs->last_sent_block && save_page_use_compression(rs)) { - return compress_page_with_multi_thread(rs, block, offset); - } else if (migrate_use_multifd()) { + if (!save_page_use_compression(rs) && migrate_use_multifd()) { return ram_save_multifd_page(rs, block, offset); } diff --git a/migration/rdma.c b/migration/rdma.c index 8bd7159059..ae07515e83 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -86,6 +86,7 @@ static uint32_t known_capabilities = RDMA_CAPABILITY_PIN_ALL; " to abort!"); \ rdma->error_reported = 1; \ } \ + rcu_read_unlock(); \ return rdma->error_state; \ } \ } while (0) @@ -387,6 +388,10 @@ typedef struct RDMAContext { uint64_t unregistrations[RDMA_SIGNALED_SEND_MAX]; GHashTable *blockmap; + + /* the RDMAContext for return path */ + struct RDMAContext *return_path; + bool is_return_path; } RDMAContext; #define TYPE_QIO_CHANNEL_RDMA "qio-channel-rdma" @@ -398,7 +403,8 @@ typedef struct QIOChannelRDMA QIOChannelRDMA; struct QIOChannelRDMA { QIOChannel parent; - RDMAContext *rdma; + RDMAContext *rdmain; + RDMAContext *rdmaout; QEMUFile *file; bool blocking; /* XXX we don't actually honour this yet */ }; @@ -1483,27 +1489,56 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out, */ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma) { + struct rdma_cm_event *cm_event; + int ret = -1; + /* * Coroutine doesn't start until migration_fd_process_incoming() * so don't yield unless we know we're running inside of a coroutine. */ - if (rdma->migration_started_on_destination) { + if (rdma->migration_started_on_destination && + migration_incoming_get_current()->state == MIGRATION_STATUS_ACTIVE) { yield_until_fd_readable(rdma->comp_channel->fd); } else { /* This is the source side, we're in a separate thread * or destination prior to migration_fd_process_incoming() + * after postcopy, the destination also in a seprate thread. * we can't yield; so we have to poll the fd. * But we need to be able to handle 'cancel' or an error * without hanging forever. */ while (!rdma->error_state && !rdma->received_error) { - GPollFD pfds[1]; + GPollFD pfds[2]; pfds[0].fd = rdma->comp_channel->fd; pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR; + pfds[0].revents = 0; + + pfds[1].fd = rdma->channel->fd; + pfds[1].events = G_IO_IN | G_IO_HUP | G_IO_ERR; + pfds[1].revents = 0; + /* 0.1s timeout, should be fine for a 'cancel' */ - switch (qemu_poll_ns(pfds, 1, 100 * 1000 * 1000)) { + switch (qemu_poll_ns(pfds, 2, 100 * 1000 * 1000)) { + case 2: case 1: /* fd active */ - return 0; + if (pfds[0].revents) { + return 0; + } + + if (pfds[1].revents) { + ret = rdma_get_cm_event(rdma->channel, &cm_event); + if (!ret) { + rdma_ack_cm_event(cm_event); + } + + error_report("receive cm event while wait comp channel," + "cm event is %d", cm_event->event); + if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED || + cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) { + return -EPIPE; + } + } + break; case 0: /* Timeout, go around again */ break; @@ -2323,10 +2358,22 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) rdma_destroy_id(rdma->cm_id); rdma->cm_id = NULL; } + + /* the destination side, listen_id and channel is shared */ if (rdma->listen_id) { - rdma_destroy_id(rdma->listen_id); + if (!rdma->is_return_path) { + rdma_destroy_id(rdma->listen_id); + } rdma->listen_id = NULL; + + if (rdma->channel) { + if (!rdma->is_return_path) { + rdma_destroy_event_channel(rdma->channel); + } + rdma->channel = NULL; + } } + if (rdma->channel) { rdma_destroy_event_channel(rdma->channel); rdma->channel = NULL; @@ -2555,6 +2602,25 @@ err_dest_init_create_listen_id: } +static void qemu_rdma_return_path_dest_init(RDMAContext *rdma_return_path, + RDMAContext *rdma) +{ + int idx; + + for (idx = 0; idx < RDMA_WRID_MAX; idx++) { + rdma_return_path->wr_data[idx].control_len = 0; + rdma_return_path->wr_data[idx].control_curr = NULL; + } + + /*the CM channel and CM id is shared*/ + rdma_return_path->channel = rdma->channel; + rdma_return_path->listen_id = rdma->listen_id; + + rdma->return_path = rdma_return_path; + rdma_return_path->return_path = rdma; + rdma_return_path->is_return_path = true; +} + static void *qemu_rdma_data_init(const char *host_port, Error **errp) { RDMAContext *rdma = NULL; @@ -2595,12 +2661,20 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); QEMUFile *f = rioc->file; - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; int ret; ssize_t done = 0; size_t i; size_t len = 0; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmaout); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); /* @@ -2610,6 +2684,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, ret = qemu_rdma_write_flush(f, rdma); if (ret < 0) { rdma->error_state = ret; + rcu_read_unlock(); return ret; } @@ -2629,6 +2704,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, if (ret < 0) { rdma->error_state = ret; + rcu_read_unlock(); return ret; } @@ -2637,6 +2713,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, } } + rcu_read_unlock(); return done; } @@ -2670,12 +2747,20 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, Error **errp) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; RDMAControlHeader head; int ret = 0; ssize_t i; size_t done = 0; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmain); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); for (i = 0; i < niov; i++) { @@ -2687,7 +2772,7 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, * were given and dish out the bytes until we run * out of bytes. */ - ret = qemu_rdma_fill(rioc->rdma, data, want, 0); + ret = qemu_rdma_fill(rdma, data, want, 0); done += ret; want -= ret; /* Got what we needed, so go to next iovec */ @@ -2709,25 +2794,28 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, if (ret < 0) { rdma->error_state = ret; + rcu_read_unlock(); return ret; } /* * SEND was received with new bytes, now try again. */ - ret = qemu_rdma_fill(rioc->rdma, data, want, 0); + ret = qemu_rdma_fill(rdma, data, want, 0); done += ret; want -= ret; /* Still didn't get enough, so lets just return */ if (want) { if (done == 0) { + rcu_read_unlock(); return QIO_CHANNEL_ERR_BLOCK; } else { break; } } } + rcu_read_unlock(); return done; } @@ -2779,15 +2867,29 @@ qio_channel_rdma_source_prepare(GSource *source, gint *timeout) { QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source; - RDMAContext *rdma = rsource->rioc->rdma; + RDMAContext *rdma; GIOCondition cond = 0; *timeout = -1; + rcu_read_lock(); + if (rsource->condition == G_IO_IN) { + rdma = atomic_rcu_read(&rsource->rioc->rdmain); + } else { + rdma = atomic_rcu_read(&rsource->rioc->rdmaout); + } + + if (!rdma) { + error_report("RDMAContext is NULL when prepare Gsource"); + rcu_read_unlock(); + return FALSE; + } + if (rdma->wr_data[0].control_len) { cond |= G_IO_IN; } cond |= G_IO_OUT; + rcu_read_unlock(); return cond & rsource->condition; } @@ -2795,14 +2897,28 @@ static gboolean qio_channel_rdma_source_check(GSource *source) { QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source; - RDMAContext *rdma = rsource->rioc->rdma; + RDMAContext *rdma; GIOCondition cond = 0; + rcu_read_lock(); + if (rsource->condition == G_IO_IN) { + rdma = atomic_rcu_read(&rsource->rioc->rdmain); + } else { + rdma = atomic_rcu_read(&rsource->rioc->rdmaout); + } + + if (!rdma) { + error_report("RDMAContext is NULL when check Gsource"); + rcu_read_unlock(); + return FALSE; + } + if (rdma->wr_data[0].control_len) { cond |= G_IO_IN; } cond |= G_IO_OUT; + rcu_read_unlock(); return cond & rsource->condition; } @@ -2813,14 +2929,28 @@ qio_channel_rdma_source_dispatch(GSource *source, { QIOChannelFunc func = (QIOChannelFunc)callback; QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source; - RDMAContext *rdma = rsource->rioc->rdma; + RDMAContext *rdma; GIOCondition cond = 0; + rcu_read_lock(); + if (rsource->condition == G_IO_IN) { + rdma = atomic_rcu_read(&rsource->rioc->rdmain); + } else { + rdma = atomic_rcu_read(&rsource->rioc->rdmaout); + } + + if (!rdma) { + error_report("RDMAContext is NULL when dispatch Gsource"); + rcu_read_unlock(); + return FALSE; + } + if (rdma->wr_data[0].control_len) { cond |= G_IO_IN; } cond |= G_IO_OUT; + rcu_read_unlock(); return (*func)(QIO_CHANNEL(rsource->rioc), (cond & rsource->condition), user_data); @@ -2860,20 +2990,91 @@ static GSource *qio_channel_rdma_create_watch(QIOChannel *ioc, return source; } +static void qio_channel_rdma_set_aio_fd_handler(QIOChannel *ioc, + AioContext *ctx, + IOHandler *io_read, + IOHandler *io_write, + void *opaque) +{ + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); + if (io_read) { + aio_set_fd_handler(ctx, rioc->rdmain->comp_channel->fd, + false, io_read, io_write, NULL, opaque); + } else { + aio_set_fd_handler(ctx, rioc->rdmaout->comp_channel->fd, + false, io_read, io_write, NULL, opaque); + } +} static int qio_channel_rdma_close(QIOChannel *ioc, Error **errp) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); + RDMAContext *rdmain, *rdmaout; trace_qemu_rdma_close(); - if (rioc->rdma) { - if (!rioc->rdma->error_state) { - rioc->rdma->error_state = qemu_file_get_error(rioc->file); + + rdmain = rioc->rdmain; + if (rdmain) { + atomic_rcu_set(&rioc->rdmain, NULL); + } + + rdmaout = rioc->rdmaout; + if (rdmaout) { + atomic_rcu_set(&rioc->rdmaout, NULL); + } + + synchronize_rcu(); + + if (rdmain) { + qemu_rdma_cleanup(rdmain); + } + + if (rdmaout) { + qemu_rdma_cleanup(rdmaout); + } + + g_free(rdmain); + g_free(rdmaout); + + return 0; +} + +static int +qio_channel_rdma_shutdown(QIOChannel *ioc, + QIOChannelShutdown how, + Error **errp) +{ + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); + RDMAContext *rdmain, *rdmaout; + + rcu_read_lock(); + + rdmain = atomic_rcu_read(&rioc->rdmain); + rdmaout = atomic_rcu_read(&rioc->rdmain); + + switch (how) { + case QIO_CHANNEL_SHUTDOWN_READ: + if (rdmain) { + rdmain->error_state = -1; + } + break; + case QIO_CHANNEL_SHUTDOWN_WRITE: + if (rdmaout) { + rdmaout->error_state = -1; } - qemu_rdma_cleanup(rioc->rdma); - g_free(rioc->rdma); - rioc->rdma = NULL; + break; + case QIO_CHANNEL_SHUTDOWN_BOTH: + default: + if (rdmain) { + rdmain->error_state = -1; + } + if (rdmaout) { + rdmaout->error_state = -1; + } + break; } + + rcu_read_unlock(); return 0; } @@ -2916,11 +3117,24 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, size_t size, uint64_t *bytes_sent) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; int ret; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmaout); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); + if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + rcu_read_unlock(); + return RAM_SAVE_CONTROL_NOT_SUPP; + } + qemu_fflush(f); if (size > 0) { @@ -3002,12 +3216,45 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, } } + rcu_read_unlock(); return RAM_SAVE_CONTROL_DELAYED; err: rdma->error_state = ret; + rcu_read_unlock(); return ret; } +static void rdma_accept_incoming_migration(void *opaque); + +static void rdma_cm_poll_handler(void *opaque) +{ + RDMAContext *rdma = opaque; + int ret; + struct rdma_cm_event *cm_event; + MigrationIncomingState *mis = migration_incoming_get_current(); + + ret = rdma_get_cm_event(rdma->channel, &cm_event); + if (ret) { + error_report("get_cm_event failed %d", errno); + return; + } + rdma_ack_cm_event(cm_event); + + if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED || + cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) { + error_report("receive cm event, cm event is %d", cm_event->event); + rdma->error_state = -EPIPE; + if (rdma->return_path) { + rdma->return_path->error_state = -EPIPE; + } + + if (mis->migration_incoming_co) { + qemu_coroutine_enter(mis->migration_incoming_co); + } + return; + } +} + static int qemu_rdma_accept(RDMAContext *rdma) { RDMACapabilities cap; @@ -3102,7 +3349,15 @@ static int qemu_rdma_accept(RDMAContext *rdma) } } - qemu_set_fd_handler(rdma->channel->fd, NULL, NULL, NULL); + /* Accept the second connection request for return path */ + if (migrate_postcopy() && !rdma->is_return_path) { + qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration, + NULL, + (void *)(intptr_t)rdma->return_path); + } else { + qemu_set_fd_handler(rdma->channel->fd, rdma_cm_poll_handler, + NULL, rdma); + } ret = rdma_accept(rdma->cm_id, &conn_param); if (ret) { @@ -3171,8 +3426,8 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) RDMAControlHeader blocks = { .type = RDMA_CONTROL_RAM_BLOCKS_RESULT, .repeat = 1 }; QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); - RDMAContext *rdma = rioc->rdma; - RDMALocalBlocks *local = &rdma->local_ram_blocks; + RDMAContext *rdma; + RDMALocalBlocks *local; RDMAControlHeader head; RDMARegister *reg, *registers; RDMACompress *comp; @@ -3185,8 +3440,17 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) int count = 0; int i = 0; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmain); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); + local = &rdma->local_ram_blocks; do { trace_qemu_rdma_registration_handle_wait(); @@ -3420,6 +3684,7 @@ out: if (ret < 0) { rdma->error_state = ret; } + rcu_read_unlock(); return ret; } @@ -3433,10 +3698,18 @@ out: static int rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) { - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; int curr; int found = -1; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmain); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + /* Find the matching RAMBlock in our local list */ for (curr = 0; curr < rdma->local_ram_blocks.nb_blocks; curr++) { if (!strcmp(rdma->local_ram_blocks.block[curr].block_name, name)) { @@ -3447,6 +3720,7 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) if (found == -1) { error_report("RAMBlock '%s' not found on destination", name); + rcu_read_unlock(); return -ENOENT; } @@ -3454,6 +3728,7 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) trace_rdma_block_notification_handle(name, rdma->next_src_index); rdma->next_src_index++; + rcu_read_unlock(); return 0; } @@ -3476,14 +3751,27 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque, uint64_t flags, void *data) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; + + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmaout); + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } CHECK_ERROR_STATE(); + if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + rcu_read_unlock(); + return 0; + } + trace_qemu_rdma_registration_start(flags); qemu_put_be64(f, RAM_SAVE_FLAG_HOOK); qemu_fflush(f); + rcu_read_unlock(); return 0; } @@ -3496,12 +3784,24 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, { Error *local_err = NULL, **errp = &local_err; QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; RDMAControlHeader head = { .len = 0, .repeat = 1 }; int ret = 0; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmaout); + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); + if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + rcu_read_unlock(); + return 0; + } + qemu_fflush(f); ret = qemu_rdma_drain_cq(f, rdma); @@ -3530,6 +3830,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, qemu_rdma_reg_whole_ram_blocks : NULL); if (ret < 0) { ERROR(errp, "receiving remote info!"); + rcu_read_unlock(); return ret; } @@ -3553,6 +3854,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, "not identical on both the source and destination.", local->nb_blocks, nb_dest_blocks); rdma->error_state = -EINVAL; + rcu_read_unlock(); return -EINVAL; } @@ -3569,6 +3871,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, local->block[i].length, rdma->dest_blocks[i].length); rdma->error_state = -EINVAL; + rcu_read_unlock(); return -EINVAL; } local->block[i].remote_host_addr = @@ -3586,9 +3889,11 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, goto err; } + rcu_read_unlock(); return 0; err: rdma->error_state = ret; + rcu_read_unlock(); return ret; } @@ -3606,10 +3911,15 @@ static const QEMUFileHooks rdma_write_hooks = { static void qio_channel_rdma_finalize(Object *obj) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(obj); - if (rioc->rdma) { - qemu_rdma_cleanup(rioc->rdma); - g_free(rioc->rdma); - rioc->rdma = NULL; + if (rioc->rdmain) { + qemu_rdma_cleanup(rioc->rdmain); + g_free(rioc->rdmain); + rioc->rdmain = NULL; + } + if (rioc->rdmaout) { + qemu_rdma_cleanup(rioc->rdmaout); + g_free(rioc->rdmaout); + rioc->rdmaout = NULL; } } @@ -3623,6 +3933,8 @@ static void qio_channel_rdma_class_init(ObjectClass *klass, ioc_klass->io_set_blocking = qio_channel_rdma_set_blocking; ioc_klass->io_close = qio_channel_rdma_close; ioc_klass->io_create_watch = qio_channel_rdma_create_watch; + ioc_klass->io_set_aio_fd_handler = qio_channel_rdma_set_aio_fd_handler; + ioc_klass->io_shutdown = qio_channel_rdma_shutdown; } static const TypeInfo qio_channel_rdma_info = { @@ -3649,13 +3961,16 @@ static QEMUFile *qemu_fopen_rdma(RDMAContext *rdma, const char *mode) } rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA)); - rioc->rdma = rdma; if (mode[0] == 'w') { rioc->file = qemu_fopen_channel_output(QIO_CHANNEL(rioc)); + rioc->rdmaout = rdma; + rioc->rdmain = rdma->return_path; qemu_file_set_hooks(rioc->file, &rdma_write_hooks); } else { rioc->file = qemu_fopen_channel_input(QIO_CHANNEL(rioc)); + rioc->rdmain = rdma; + rioc->rdmaout = rdma->return_path; qemu_file_set_hooks(rioc->file, &rdma_read_hooks); } @@ -3679,6 +3994,10 @@ static void rdma_accept_incoming_migration(void *opaque) trace_qemu_rdma_accept_incoming_migration_accepted(); + if (rdma->is_return_path) { + return; + } + f = qemu_fopen_rdma(rdma, "rb"); if (f == NULL) { ERROR(errp, "could not qemu_fopen_rdma!"); @@ -3693,7 +4012,7 @@ static void rdma_accept_incoming_migration(void *opaque) void rdma_start_incoming_migration(const char *host_port, Error **errp) { int ret; - RDMAContext *rdma; + RDMAContext *rdma, *rdma_return_path; Error *local_err = NULL; trace_rdma_start_incoming_migration(); @@ -3720,12 +4039,24 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) trace_rdma_start_incoming_migration_after_rdma_listen(); + /* initialize the RDMAContext for return path */ + if (migrate_postcopy()) { + rdma_return_path = qemu_rdma_data_init(host_port, &local_err); + + if (rdma_return_path == NULL) { + goto err; + } + + qemu_rdma_return_path_dest_init(rdma_return_path, rdma); + } + qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration, NULL, (void *)(intptr_t)rdma); return; err: error_propagate(errp, local_err); g_free(rdma); + g_free(rdma_return_path); } void rdma_start_outgoing_migration(void *opaque, @@ -3733,6 +4064,7 @@ void rdma_start_outgoing_migration(void *opaque, { MigrationState *s = opaque; RDMAContext *rdma = qemu_rdma_data_init(host_port, errp); + RDMAContext *rdma_return_path = NULL; int ret = 0; if (rdma == NULL) { @@ -3753,6 +4085,32 @@ void rdma_start_outgoing_migration(void *opaque, goto err; } + /* RDMA postcopy need a seprate queue pair for return path */ + if (migrate_postcopy()) { + rdma_return_path = qemu_rdma_data_init(host_port, errp); + + if (rdma_return_path == NULL) { + goto err; + } + + ret = qemu_rdma_source_init(rdma_return_path, + s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL], errp); + + if (ret) { + goto err; + } + + ret = qemu_rdma_connect(rdma_return_path, errp); + + if (ret) { + goto err; + } + + rdma->return_path = rdma_return_path; + rdma_return_path->return_path = rdma; + rdma_return_path->is_return_path = true; + } + trace_rdma_start_outgoing_migration_after_rdma_connect(); s->to_dst_file = qemu_fopen_rdma(rdma, "wb"); @@ -3760,4 +4118,5 @@ void rdma_start_outgoing_migration(void *opaque, return; err: g_free(rdma); + g_free(rdma_return_path); } diff --git a/migration/savevm.c b/migration/savevm.c index 7f92567a10..13e51f0e34 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1622,6 +1622,7 @@ static void *postcopy_ram_listen_thread(void *opaque) qemu_sem_post(&mis->listen_thread_sem); trace_postcopy_ram_listen_thread_start(); + rcu_register_thread(); /* * Because we're a thread and not a coroutine we can't yield * in qemu_file, and thus we must be blocking now. @@ -1662,6 +1663,7 @@ static void *postcopy_ram_listen_thread(void *opaque) * to leave the guest running and fire MCEs for pages that never * arrived as a desperate recovery step. */ + rcu_unregister_thread(); exit(EXIT_FAILURE); } @@ -1676,6 +1678,7 @@ static void *postcopy_ram_listen_thread(void *opaque) migration_incoming_state_destroy(); qemu_loadvm_state_cleanup(); + rcu_unregister_thread(); return NULL; } diff --git a/migration/vmstate.c b/migration/vmstate.c index 6b9079bb51..0bc240a317 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -418,7 +418,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, static const VMStateDescription * vmstate_get_subsection(const VMStateDescription **sub, char *idstr) { - while (sub && *sub && (*sub)->needed) { + while (sub && *sub) { if (strcmp(idstr, (*sub)->name) == 0) { return *sub; } @@ -486,8 +486,8 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, int ret = 0; trace_vmstate_subsection_save_top(vmsd->name); - while (sub && *sub && (*sub)->needed) { - if ((*sub)->needed(opaque)) { + while (sub && *sub) { + if (vmstate_save_needed(*sub, opaque)) { const VMStateDescription *vmsdsub = *sub; uint8_t len; @@ -58,7 +58,6 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qjson.h" -#include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/qlist.h" #include "qom/object_interfaces.h" @@ -1454,6 +1453,17 @@ static void hmp_info_opcount(Monitor *mon, const QDict *qdict) } #endif +static void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) +{ + int64_t max = qdict_get_try_int(qdict, "max", 10); + bool mean = qdict_get_try_bool(qdict, "mean", false); + bool coalesce = !qdict_get_try_bool(qdict, "no_coalesce", false); + enum QSPSortBy sort_by; + + sort_by = mean ? QSP_SORT_BY_AVG_WAIT_TIME : QSP_SORT_BY_TOTAL_WAIT_TIME; + qsp_report((FILE *)mon, monitor_fprintf, max, sort_by, coalesce); +} + static void hmp_info_history(Monitor *mon, const QDict *qdict) { int i; @@ -4245,20 +4255,14 @@ static void monitor_qmp_bh_dispatcher(void *data) #define QMP_REQ_QUEUE_LEN_MAX (8) -static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) +static void handle_qmp_command(void *opaque, QObject *req, Error *err) { - QObject *req, *id = NULL; + Monitor *mon = opaque; + QObject *id = NULL; QDict *qdict; - MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser); - Monitor *mon = container_of(mon_qmp, Monitor, qmp); - Error *err = NULL; QMPRequest *req_obj; - req = json_parser_parse_err(tokens, NULL, &err); - if (!req && !err) { - /* json_parser_parse_err() sucks: can fail without setting @err */ - error_setg(&err, QERR_JSON_PARSING); - } + assert(!req != !err); qdict = qobject_to(QDict, req); if (qdict) { @@ -4454,7 +4458,8 @@ static void monitor_qmp_event(void *opaque, int event) monitor_qmp_response_flush(mon); monitor_qmp_cleanup_queues(mon); json_message_parser_destroy(&mon->qmp.parser); - json_message_parser_init(&mon->qmp.parser, handle_qmp_command); + json_message_parser_init(&mon->qmp.parser, handle_qmp_command, + mon, NULL); mon_refcount--; monitor_fdsets_cleanup(); break; @@ -4672,7 +4677,8 @@ void monitor_init(Chardev *chr, int flags) if (monitor_is_qmp(mon)) { qemu_chr_fe_set_echo(&mon->chr, true); - json_message_parser_init(&mon->qmp.parser, handle_qmp_command); + json_message_parser_init(&mon->qmp.parser, handle_qmp_command, + mon, NULL); if (mon->use_io_thread) { /* * Make sure the old iowatch is gone. It's possible when diff --git a/pc-bios/optionrom/linuxboot_dma.c b/pc-bios/optionrom/linuxboot_dma.c index 4754282ad7..d856d41b55 100644 --- a/pc-bios/optionrom/linuxboot_dma.c +++ b/pc-bios/optionrom/linuxboot_dma.c @@ -58,8 +58,6 @@ asm( " jmp load_kernel\n" ); -#include "../../include/hw/nvram/fw_cfg_keys.h" - /* QEMU_CFG_DMA_CONTROL bits */ #define BIOS_CFG_DMA_CTL_ERROR 0x01 #define BIOS_CFG_DMA_CTL_READ 0x02 @@ -73,6 +71,8 @@ asm( #define uint32_t unsigned int #define uint16_t unsigned short +#include "../../include/standard-headers/linux/qemu_fw_cfg.h" + #define barrier() asm("" : : : "memory") typedef struct FWCfgDmaAccess { diff --git a/pc-bios/optionrom/optionrom.h b/pc-bios/optionrom/optionrom.h index 6c4c2c82f4..a2b612f1a7 100644 --- a/pc-bios/optionrom/optionrom.h +++ b/pc-bios/optionrom/optionrom.h @@ -19,7 +19,20 @@ */ -#include "../../include/hw/nvram/fw_cfg_keys.h" +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_KERNEL_ENTRY 0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR 0x13 +#define FW_CFG_CMDLINE_SIZE 0x14 +#define FW_CFG_CMDLINE_DATA 0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 #define BIOS_CFG_IOPORT_CFG 0x510 #define BIOS_CFG_IOPORT_DATA 0x511 diff --git a/qapi/introspect.json b/qapi/introspect.json index 137b39b992..3d22166b2b 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -120,7 +120,7 @@ ## # @JSONType: # -# The four primitive and two structured types according to RFC 7159 +# The four primitive and two structured types according to RFC 8259 # section 1, plus 'int' (split off 'number'), plus the obvious top # type 'value'. # diff --git a/qapi/migration.json b/qapi/migration.json index 186e8a7303..f62d3f9a4b 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -212,13 +212,13 @@ # -> { "execute": "query-migrate" } # <- { "return": { # "status": "completed", +# "total-time":12345, +# "setup-time":12345, +# "downtime":12345, # "ram":{ # "transferred":123, # "remaining":123, # "total":246, -# "total-time":12345, -# "setup-time":12345, -# "downtime":12345, # "duplicate":123, # "normal":123, # "normal-bytes":123456, @@ -238,13 +238,13 @@ # <- { # "return":{ # "status":"active", +# "total-time":12345, +# "setup-time":12345, +# "expected-downtime":12345, # "ram":{ # "transferred":123, # "remaining":123, # "total":246, -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, # "duplicate":123, # "normal":123, # "normal-bytes":123456, @@ -259,13 +259,13 @@ # <- { # "return":{ # "status":"active", +# "total-time":12345, +# "setup-time":12345, +# "expected-downtime":12345, # "ram":{ # "total":1057024, # "remaining":1053304, # "transferred":3720, -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, # "duplicate":123, # "normal":123, # "normal-bytes":123456, @@ -285,14 +285,13 @@ # <- { # "return":{ # "status":"active", -# "capabilities" : [ { "capability": "xbzrle", "state" : true } ], +# "total-time":12345, +# "setup-time":12345, +# "expected-downtime":12345, # "ram":{ # "total":1057024, # "remaining":1053304, # "transferred":3720, -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, # "duplicate":10, # "normal":3333, # "normal-bytes":3412992, @@ -462,6 +461,11 @@ # @compress-threads: Set compression thread count to be used in live migration, # the compression thread count is an integer between 1 and 255. # +# @compress-wait-thread: Controls behavior when all compression threads are +# currently busy. If true (default), wait for a free +# compression thread to become available; otherwise, +# send the page uncompressed. (Since 3.1) +# # @decompress-threads: Set decompression thread count to be used in live # migration, the decompression thread count is an integer between 1 # and 255. Usually, decompression is at least 4 times as fast as @@ -523,15 +527,20 @@ # @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. # Defaults to 0 (unlimited). In bytes per second. # (Since 3.0) +# +# @max-cpu-throttle: maximum cpu throttle percentage. +# Defaults to 99. (Since 3.1) # Since: 2.4 ## { 'enum': 'MigrationParameter', 'data': ['compress-level', 'compress-threads', 'decompress-threads', + 'compress-wait-thread', 'cpu-throttle-initial', 'cpu-throttle-increment', 'tls-creds', 'tls-hostname', 'max-bandwidth', 'downtime-limit', 'x-checkpoint-delay', 'block-incremental', 'x-multifd-channels', 'x-multifd-page-count', - 'xbzrle-cache-size', 'max-postcopy-bandwidth' ] } + 'xbzrle-cache-size', 'max-postcopy-bandwidth', + 'max-cpu-throttle' ] } ## # @MigrateSetParameters: @@ -540,6 +549,11 @@ # # @compress-threads: compression thread count # +# @compress-wait-thread: Controls behavior when all compression threads are +# currently busy. If true (default), wait for a free +# compression thread to become available; otherwise, +# send the page uncompressed. (Since 3.1) +# # @decompress-threads: decompression thread count # # @cpu-throttle-initial: Initial percentage of time guest cpus are @@ -603,6 +617,10 @@ # @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. # Defaults to 0 (unlimited). In bytes per second. # (Since 3.0) +# +# @max-cpu-throttle: maximum cpu throttle percentage. +# The default value is 99. (Since 3.1) +# # Since: 2.4 ## # TODO either fuse back into MigrationParameters, or make @@ -610,6 +628,7 @@ { 'struct': 'MigrateSetParameters', 'data': { '*compress-level': 'int', '*compress-threads': 'int', + '*compress-wait-thread': 'bool', '*decompress-threads': 'int', '*cpu-throttle-initial': 'int', '*cpu-throttle-increment': 'int', @@ -622,7 +641,8 @@ '*x-multifd-channels': 'int', '*x-multifd-page-count': 'int', '*xbzrle-cache-size': 'size', - '*max-postcopy-bandwidth': 'size' } } + '*max-postcopy-bandwidth': 'size', + '*max-cpu-throttle': 'int' } } ## # @migrate-set-parameters: @@ -649,6 +669,11 @@ # # @compress-threads: compression thread count # +# @compress-wait-thread: Controls behavior when all compression threads are +# currently busy. If true (default), wait for a free +# compression thread to become available; otherwise, +# send the page uncompressed. (Since 3.1) +# # @decompress-threads: decompression thread count # # @cpu-throttle-initial: Initial percentage of time guest cpus are @@ -709,11 +734,17 @@ # @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. # Defaults to 0 (unlimited). In bytes per second. # (Since 3.0) +# +# @max-cpu-throttle: maximum cpu throttle percentage. +# Defaults to 99. +# (Since 3.1) +# # Since: 2.4 ## { 'struct': 'MigrationParameters', 'data': { '*compress-level': 'uint8', '*compress-threads': 'uint8', + '*compress-wait-thread': 'bool', '*decompress-threads': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', @@ -726,7 +757,8 @@ '*x-multifd-channels': 'uint8', '*x-multifd-page-count': 'uint32', '*xbzrle-cache-size': 'size', - '*max-postcopy-bandwidth': 'size' } } + '*max-postcopy-bandwidth': 'size', + '*max-cpu-throttle':'uint8'} } ## # @query-migrate-parameters: diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 6f2d466596..d8da1a62de 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qmp/dispatch.h" -#include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qbool.h" diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index da57f4cc24..3e88b27f9e 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -725,11 +725,6 @@ Visitor *qobject_input_visitor_new_str(const char *str, if (is_json) { obj = qobject_from_json(str, errp); if (!obj) { - /* Work around qobject_from_json() lossage TODO fix that */ - if (errp && !*errp) { - error_setg(errp, "JSON parse error"); - return NULL; - } return NULL; } args = qobject_to(QDict, obj); diff --git a/qemu-options.hx b/qemu-options.hx index 5515dfaba5..d66ab1bddb 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3953,6 +3953,16 @@ Dump json-encoded vmstate information for current machine type to file in @var{file} ETEXI +DEF("enable-sync-profile", 0, QEMU_OPTION_enable_sync_profile, + "-enable-sync-profile\n" + " enable synchronization profiling\n", + QEMU_ARCH_ALL) +STEXI +@item -enable-sync-profile +@findex -enable-sync-profile +Enable synchronization profiling. +ETEXI + STEXI @end table ETEXI diff --git a/qga/main.c b/qga/main.c index 87372d40ef..6d70242d05 100644 --- a/qga/main.c +++ b/qga/main.c @@ -18,7 +18,6 @@ #include <syslog.h> #include <sys/wait.h> #endif -#include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" @@ -597,24 +596,20 @@ static void process_command(GAState *s, QDict *req) } /* handle requests/control events coming in over the channel */ -static void process_event(JSONMessageParser *parser, GQueue *tokens) +static void process_event(void *opaque, QObject *obj, Error *err) { - GAState *s = container_of(parser, GAState, parser); - QObject *obj; + GAState *s = opaque; QDict *req, *rsp; - Error *err = NULL; int ret; - g_assert(s && parser); - g_debug("process_event: called"); - obj = json_parser_parse_err(tokens, NULL, &err); + assert(!obj != !err); if (err) { goto err; } req = qobject_to(QDict, obj); if (!req) { - error_setg(&err, QERR_JSON_PARSING); + error_setg(&err, "Input must be a JSON object"); goto err; } if (!qdict_haskey(req, "execute")) { @@ -1320,7 +1315,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation) s->command_state = ga_command_state_new(); ga_command_state_init(s, s->command_state); ga_command_state_init_all(s->command_state); - json_message_parser_init(&s->parser, process_event); + json_message_parser_init(&s->parser, process_event, s, NULL); #ifndef _WIN32 if (!register_signal_handlers()) { diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 980ba159d6..e1745a3d95 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -12,63 +12,116 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" -#include "qapi/qmp/json-lexer.h" +#include "json-parser-int.h" #define MAX_TOKEN_SIZE (64ULL << 20) /* - * Required by JSON (RFC 7159): + * From RFC 8259 "The JavaScript Object Notation (JSON) Data + * Interchange Format", with [comments in brackets]: * - * \"([^\\\"]|\\[\"'\\/bfnrt]|\\u[0-9a-fA-F]{4})*\" - * -?(0|[1-9][0-9]*)(.[0-9]+)?([eE][-+]?[0-9]+)? - * [{}\[\],:] - * [a-z]+ # covers null, true, false + * The set of tokens includes six structural characters, strings, + * numbers, and three literal names. * - * Extension of '' strings: + * These are the six structural characters: * - * '([^\\']|\\[\"'\\/bfnrt]|\\u[0-9a-fA-F]{4})*' + * begin-array = ws %x5B ws ; [ left square bracket + * begin-object = ws %x7B ws ; { left curly bracket + * end-array = ws %x5D ws ; ] right square bracket + * end-object = ws %x7D ws ; } right curly bracket + * name-separator = ws %x3A ws ; : colon + * value-separator = ws %x2C ws ; , comma * - * Extension for vararg handling in JSON construction: + * Insignificant whitespace is allowed before or after any of the six + * structural characters. + * [This lexer accepts it before or after any token, which is actually + * the same, as the grammar always has structural characters between + * other tokens.] * - * %((l|ll|I64)?d|[ipsf]) + * ws = *( + * %x20 / ; Space + * %x09 / ; Horizontal tab + * %x0A / ; Line feed or New line + * %x0D ) ; Carriage return * + * [...] three literal names: + * false null true + * [This lexer accepts [a-z]+, and leaves rejecting unknown literal + * names to the parser.] + * + * [Numbers:] + * + * number = [ minus ] int [ frac ] [ exp ] + * decimal-point = %x2E ; . + * digit1-9 = %x31-39 ; 1-9 + * e = %x65 / %x45 ; e E + * exp = e [ minus / plus ] 1*DIGIT + * frac = decimal-point 1*DIGIT + * int = zero / ( digit1-9 *DIGIT ) + * minus = %x2D ; - + * plus = %x2B ; + + * zero = %x30 ; 0 + * + * [Strings:] + * string = quotation-mark *char quotation-mark + * + * char = unescaped / + * escape ( + * %x22 / ; " quotation mark U+0022 + * %x5C / ; \ reverse solidus U+005C + * %x2F / ; / solidus U+002F + * %x62 / ; b backspace U+0008 + * %x66 / ; f form feed U+000C + * %x6E / ; n line feed U+000A + * %x72 / ; r carriage return U+000D + * %x74 / ; t tab U+0009 + * %x75 4HEXDIG ) ; uXXXX U+XXXX + * escape = %x5C ; \ + * quotation-mark = %x22 ; " + * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + * [This lexer accepts any non-control character after escape, and + * leaves rejecting invalid ones to the parser.] + * + * + * Extensions over RFC 8259: + * - Extra escape sequence in strings: + * 0x27 (apostrophe) is recognized after escape, too + * - Single-quoted strings: + * Like double-quoted strings, except they're delimited by %x27 + * (apostrophe) instead of %x22 (quotation mark), and can't contain + * unescaped apostrophe, but can contain unescaped quotation mark. + * - Interpolation, if enabled: + * The lexer accepts %[A-Za-z0-9]*, and leaves rejecting invalid + * ones to the parser. + * + * Note: + * - Input must be encoded in modified UTF-8. + * - Decoding and validating is left to the parser. */ enum json_lexer_state { IN_ERROR = 0, /* must really be 0, see json_lexer[] */ - IN_DQ_UCODE3, - IN_DQ_UCODE2, - IN_DQ_UCODE1, - IN_DQ_UCODE0, IN_DQ_STRING_ESCAPE, IN_DQ_STRING, - IN_SQ_UCODE3, - IN_SQ_UCODE2, - IN_SQ_UCODE1, - IN_SQ_UCODE0, IN_SQ_STRING_ESCAPE, IN_SQ_STRING, IN_ZERO, - IN_DIGITS, - IN_DIGIT, + IN_EXP_DIGITS, + IN_EXP_SIGN, IN_EXP_E, IN_MANTISSA, IN_MANTISSA_DIGITS, - IN_NONZERO_NUMBER, - IN_NEG_NONZERO_NUMBER, + IN_DIGITS, + IN_SIGN, IN_KEYWORD, - IN_ESCAPE, - IN_ESCAPE_L, - IN_ESCAPE_LL, - IN_ESCAPE_I, - IN_ESCAPE_I6, - IN_ESCAPE_I64, + IN_INTERP, IN_WHITESPACE, IN_START, + IN_START_INTERP, /* must be IN_START + 1 */ }; -QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START); +QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START_INTERP); +QEMU_BUILD_BUG_ON(IN_START_INTERP != IN_START + 1); #define TERMINAL(state) [0 ... 0x7F] = (state) @@ -76,87 +129,27 @@ QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START); from OLD_STATE required lookahead. This happens whenever the table below uses the TERMINAL macro. */ #define TERMINAL_NEEDED_LOOKAHEAD(old_state, terminal) \ - (json_lexer[(old_state)][0] == (terminal)) + (terminal != IN_ERROR && json_lexer[(old_state)][0] == (terminal)) static const uint8_t json_lexer[][256] = { /* Relies on default initialization to IN_ERROR! */ /* double quote string */ - [IN_DQ_UCODE3] = { - ['0' ... '9'] = IN_DQ_STRING, - ['a' ... 'f'] = IN_DQ_STRING, - ['A' ... 'F'] = IN_DQ_STRING, - }, - [IN_DQ_UCODE2] = { - ['0' ... '9'] = IN_DQ_UCODE3, - ['a' ... 'f'] = IN_DQ_UCODE3, - ['A' ... 'F'] = IN_DQ_UCODE3, - }, - [IN_DQ_UCODE1] = { - ['0' ... '9'] = IN_DQ_UCODE2, - ['a' ... 'f'] = IN_DQ_UCODE2, - ['A' ... 'F'] = IN_DQ_UCODE2, - }, - [IN_DQ_UCODE0] = { - ['0' ... '9'] = IN_DQ_UCODE1, - ['a' ... 'f'] = IN_DQ_UCODE1, - ['A' ... 'F'] = IN_DQ_UCODE1, - }, [IN_DQ_STRING_ESCAPE] = { - ['b'] = IN_DQ_STRING, - ['f'] = IN_DQ_STRING, - ['n'] = IN_DQ_STRING, - ['r'] = IN_DQ_STRING, - ['t'] = IN_DQ_STRING, - ['/'] = IN_DQ_STRING, - ['\\'] = IN_DQ_STRING, - ['\''] = IN_DQ_STRING, - ['\"'] = IN_DQ_STRING, - ['u'] = IN_DQ_UCODE0, + [0x20 ... 0xFD] = IN_DQ_STRING, }, [IN_DQ_STRING] = { - [1 ... 0xBF] = IN_DQ_STRING, - [0xC2 ... 0xF4] = IN_DQ_STRING, + [0x20 ... 0xFD] = IN_DQ_STRING, ['\\'] = IN_DQ_STRING_ESCAPE, ['"'] = JSON_STRING, }, /* single quote string */ - [IN_SQ_UCODE3] = { - ['0' ... '9'] = IN_SQ_STRING, - ['a' ... 'f'] = IN_SQ_STRING, - ['A' ... 'F'] = IN_SQ_STRING, - }, - [IN_SQ_UCODE2] = { - ['0' ... '9'] = IN_SQ_UCODE3, - ['a' ... 'f'] = IN_SQ_UCODE3, - ['A' ... 'F'] = IN_SQ_UCODE3, - }, - [IN_SQ_UCODE1] = { - ['0' ... '9'] = IN_SQ_UCODE2, - ['a' ... 'f'] = IN_SQ_UCODE2, - ['A' ... 'F'] = IN_SQ_UCODE2, - }, - [IN_SQ_UCODE0] = { - ['0' ... '9'] = IN_SQ_UCODE1, - ['a' ... 'f'] = IN_SQ_UCODE1, - ['A' ... 'F'] = IN_SQ_UCODE1, - }, [IN_SQ_STRING_ESCAPE] = { - ['b'] = IN_SQ_STRING, - ['f'] = IN_SQ_STRING, - ['n'] = IN_SQ_STRING, - ['r'] = IN_SQ_STRING, - ['t'] = IN_SQ_STRING, - ['/'] = IN_SQ_STRING, - ['\\'] = IN_SQ_STRING, - ['\''] = IN_SQ_STRING, - ['\"'] = IN_SQ_STRING, - ['u'] = IN_SQ_UCODE0, + [0x20 ... 0xFD] = IN_SQ_STRING, }, [IN_SQ_STRING] = { - [1 ... 0xBF] = IN_SQ_STRING, - [0xC2 ... 0xF4] = IN_SQ_STRING, + [0x20 ... 0xFD] = IN_SQ_STRING, ['\\'] = IN_SQ_STRING_ESCAPE, ['\''] = JSON_STRING, }, @@ -169,19 +162,19 @@ static const uint8_t json_lexer[][256] = { }, /* Float */ - [IN_DIGITS] = { + [IN_EXP_DIGITS] = { TERMINAL(JSON_FLOAT), - ['0' ... '9'] = IN_DIGITS, + ['0' ... '9'] = IN_EXP_DIGITS, }, - [IN_DIGIT] = { - ['0' ... '9'] = IN_DIGITS, + [IN_EXP_SIGN] = { + ['0' ... '9'] = IN_EXP_DIGITS, }, [IN_EXP_E] = { - ['-'] = IN_DIGIT, - ['+'] = IN_DIGIT, - ['0' ... '9'] = IN_DIGITS, + ['-'] = IN_EXP_SIGN, + ['+'] = IN_EXP_SIGN, + ['0' ... '9'] = IN_EXP_DIGITS, }, [IN_MANTISSA_DIGITS] = { @@ -196,17 +189,17 @@ static const uint8_t json_lexer[][256] = { }, /* Number */ - [IN_NONZERO_NUMBER] = { + [IN_DIGITS] = { TERMINAL(JSON_INTEGER), - ['0' ... '9'] = IN_NONZERO_NUMBER, + ['0' ... '9'] = IN_DIGITS, ['e'] = IN_EXP_E, ['E'] = IN_EXP_E, ['.'] = IN_MANTISSA, }, - [IN_NEG_NONZERO_NUMBER] = { + [IN_SIGN] = { ['0'] = IN_ZERO, - ['1' ... '9'] = IN_NONZERO_NUMBER, + ['1' ... '9'] = IN_DIGITS, }, /* keywords */ @@ -224,49 +217,25 @@ static const uint8_t json_lexer[][256] = { ['\n'] = IN_WHITESPACE, }, - /* escape */ - [IN_ESCAPE_LL] = { - ['d'] = JSON_ESCAPE, - ['u'] = JSON_ESCAPE, - }, - - [IN_ESCAPE_L] = { - ['d'] = JSON_ESCAPE, - ['l'] = IN_ESCAPE_LL, - ['u'] = JSON_ESCAPE, - }, - - [IN_ESCAPE_I64] = { - ['d'] = JSON_ESCAPE, - ['u'] = JSON_ESCAPE, - }, - - [IN_ESCAPE_I6] = { - ['4'] = IN_ESCAPE_I64, + /* interpolation */ + [IN_INTERP] = { + TERMINAL(JSON_INTERP), + ['A' ... 'Z'] = IN_INTERP, + ['a' ... 'z'] = IN_INTERP, + ['0' ... '9'] = IN_INTERP, }, - [IN_ESCAPE_I] = { - ['6'] = IN_ESCAPE_I6, - }, - - [IN_ESCAPE] = { - ['d'] = JSON_ESCAPE, - ['i'] = JSON_ESCAPE, - ['p'] = JSON_ESCAPE, - ['s'] = JSON_ESCAPE, - ['u'] = JSON_ESCAPE, - ['f'] = JSON_ESCAPE, - ['l'] = IN_ESCAPE_L, - ['I'] = IN_ESCAPE_I, - }, - - /* top level rule */ - [IN_START] = { + /* + * Two start states: + * - IN_START recognizes JSON tokens with our string extensions + * - IN_START_INTERP additionally recognizes interpolation. + */ + [IN_START ... IN_START_INTERP] = { ['"'] = IN_DQ_STRING, ['\''] = IN_SQ_STRING, ['0'] = IN_ZERO, - ['1' ... '9'] = IN_NONZERO_NUMBER, - ['-'] = IN_NEG_NONZERO_NUMBER, + ['1' ... '9'] = IN_DIGITS, + ['-'] = IN_SIGN, ['{'] = JSON_LCURLY, ['}'] = JSON_RCURLY, ['['] = JSON_LSQUARE, @@ -274,23 +243,23 @@ static const uint8_t json_lexer[][256] = { [','] = JSON_COMMA, [':'] = JSON_COLON, ['a' ... 'z'] = IN_KEYWORD, - ['%'] = IN_ESCAPE, [' '] = IN_WHITESPACE, ['\t'] = IN_WHITESPACE, ['\r'] = IN_WHITESPACE, ['\n'] = IN_WHITESPACE, }, + [IN_START_INTERP]['%'] = IN_INTERP, }; -void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func) +void json_lexer_init(JSONLexer *lexer, bool enable_interpolation) { - lexer->emit = func; - lexer->state = IN_START; + lexer->start_state = lexer->state = enable_interpolation + ? IN_START_INTERP : IN_START; lexer->token = g_string_sized_new(3); lexer->x = lexer->y = 0; } -static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) +static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) { int char_consumed, new_state; @@ -304,7 +273,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) assert(lexer->state <= ARRAY_SIZE(json_lexer)); new_state = json_lexer[lexer->state][(uint8_t)ch]; char_consumed = !TERMINAL_NEEDED_LOOKAHEAD(lexer->state, new_state); - if (char_consumed) { + if (char_consumed && !flush) { g_string_append_c(lexer->token, ch); } @@ -315,23 +284,23 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) case JSON_RSQUARE: case JSON_COLON: case JSON_COMMA: - case JSON_ESCAPE: + case JSON_INTERP: case JSON_INTEGER: case JSON_FLOAT: case JSON_KEYWORD: case JSON_STRING: - lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y); + json_message_process_token(lexer, lexer->token, new_state, + lexer->x, lexer->y); /* fall through */ case JSON_SKIP: g_string_truncate(lexer->token, 0); - new_state = IN_START; + new_state = lexer->start_state; break; case IN_ERROR: /* XXX: To avoid having previous bad input leaving the parser in an * unresponsive state where we consume unpredictable amounts of * subsequent "good" input, percolate this error state up to the - * tokenizer/parser by forcing a NULL object to be emitted, then - * reset state. + * parser by emitting a JSON_ERROR token, then reset lexer state. * * Also note that this handling is required for reliable channel * negotiation between QMP and the guest agent, since chr(0xFF) @@ -340,11 +309,11 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) * never a valid ASCII/UTF-8 sequence, so this should reliably * induce an error/flush state. */ - lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y); + json_message_process_token(lexer, lexer->token, JSON_ERROR, + lexer->x, lexer->y); g_string_truncate(lexer->token, 0); - new_state = IN_START; - lexer->state = new_state; - return 0; + lexer->state = lexer->start_state; + return; default: break; } @@ -355,33 +324,29 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) * this is a security consideration. */ if (lexer->token->len > MAX_TOKEN_SIZE) { - lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y); + json_message_process_token(lexer, lexer->token, lexer->state, + lexer->x, lexer->y); g_string_truncate(lexer->token, 0); - lexer->state = IN_START; + lexer->state = lexer->start_state; } - - return 0; } -int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size) +void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size) { size_t i; for (i = 0; i < size; i++) { - int err; - - err = json_lexer_feed_char(lexer, buffer[i], false); - if (err < 0) { - return err; - } + json_lexer_feed_char(lexer, buffer[i], false); } - - return 0; } -int json_lexer_flush(JSONLexer *lexer) +void json_lexer_flush(JSONLexer *lexer) { - return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0, true); + if (lexer->state != lexer->start_state) { + json_lexer_feed_char(lexer, 0, true); + } + json_message_process_token(lexer, lexer->token, JSON_END_OF_INPUT, + lexer->x, lexer->y); } void json_lexer_destroy(JSONLexer *lexer) diff --git a/qobject/json-parser-int.h b/qobject/json-parser-int.h new file mode 100644 index 0000000000..ceaa890ec6 --- /dev/null +++ b/qobject/json-parser-int.h @@ -0,0 +1,54 @@ +/* + * JSON Parser + * + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef JSON_PARSER_INT_H +#define JSON_PARSER_INT_H + +#include "qapi/qmp/json-parser.h" + + +typedef enum json_token_type { + JSON_MIN = 100, + JSON_LCURLY = JSON_MIN, + JSON_RCURLY, + JSON_LSQUARE, + JSON_RSQUARE, + JSON_COLON, + JSON_COMMA, + JSON_INTEGER, + JSON_FLOAT, + JSON_KEYWORD, + JSON_STRING, + JSON_INTERP, + JSON_SKIP, + JSON_ERROR, + JSON_END_OF_INPUT, +} JSONTokenType; + +typedef struct JSONToken JSONToken; + +/* json-lexer.c */ +void json_lexer_init(JSONLexer *lexer, bool enable_interpolation); +void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); +void json_lexer_flush(JSONLexer *lexer); +void json_lexer_destroy(JSONLexer *lexer); + +/* json-streamer.c */ +void json_message_process_token(JSONLexer *lexer, GString *input, + JSONTokenType type, int x, int y); + +/* json-parser.c */ +JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr); +QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp); + +#endif diff --git a/qobject/json-parser.c b/qobject/json-parser.c index a5aa790d62..5a840dfd86 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/unicode.h" #include "qapi/error.h" #include "qemu-common.h" #include "qapi/qmp/qbool.h" @@ -21,15 +22,21 @@ #include "qapi/qmp/qnull.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-lexer.h" -#include "qapi/qmp/json-streamer.h" +#include "json-parser-int.h" + +struct JSONToken { + JSONTokenType type; + int x; + int y; + char str[]; +}; typedef struct JSONParserContext { Error *err; JSONToken *current; GQueue *buf; + va_list *ap; } JSONParserContext; #define BUG_ON(cond) assert(!(cond)) @@ -43,7 +50,7 @@ typedef struct JSONParserContext * 4) deal with premature EOI */ -static QObject *parse_value(JSONParserContext *ctxt, va_list *ap); +static QObject *parse_value(JSONParserContext *ctxt); /** * Error handler @@ -53,169 +60,170 @@ static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt, { va_list ap; char message[1024]; + + if (ctxt->err) { + return; + } va_start(ap, msg); vsnprintf(message, sizeof(message), msg, ap); va_end(ap); - if (ctxt->err) { - error_free(ctxt->err); - ctxt->err = NULL; - } error_setg(&ctxt->err, "JSON parse error, %s", message); } -/** - * String helpers - * - * These helpers are used to unescape strings. - */ -static void wchar_to_utf8(uint16_t wchar, char *buffer, size_t buffer_length) +static int cvt4hex(const char *s) { - if (wchar <= 0x007F) { - BUG_ON(buffer_length < 2); - - buffer[0] = wchar & 0x7F; - buffer[1] = 0; - } else if (wchar <= 0x07FF) { - BUG_ON(buffer_length < 3); - - buffer[0] = 0xC0 | ((wchar >> 6) & 0x1F); - buffer[1] = 0x80 | (wchar & 0x3F); - buffer[2] = 0; - } else { - BUG_ON(buffer_length < 4); + int cp, i; - buffer[0] = 0xE0 | ((wchar >> 12) & 0x0F); - buffer[1] = 0x80 | ((wchar >> 6) & 0x3F); - buffer[2] = 0x80 | (wchar & 0x3F); - buffer[3] = 0; - } -} - -static int hex2decimal(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'); + cp = 0; + for (i = 0; i < 4; i++) { + if (!qemu_isxdigit(s[i])) { + return -1; + } + cp <<= 4; + if (s[i] >= '0' && s[i] <= '9') { + cp |= s[i] - '0'; + } else if (s[i] >= 'a' && s[i] <= 'f') { + cp |= 10 + s[i] - 'a'; + } else if (s[i] >= 'A' && s[i] <= 'F') { + cp |= 10 + s[i] - 'A'; + } else { + return -1; + } } - - return -1; + return cp; } /** - * parse_string(): Parse a json string and return a QObject + * parse_string(): Parse a JSON string + * + * From RFC 8259 "The JavaScript Object Notation (JSON) Data + * Interchange Format": + * + * char = unescaped / + * escape ( + * %x22 / ; " quotation mark U+0022 + * %x5C / ; \ reverse solidus U+005C + * %x2F / ; / solidus U+002F + * %x62 / ; b backspace U+0008 + * %x66 / ; f form feed U+000C + * %x6E / ; n line feed U+000A + * %x72 / ; r carriage return U+000D + * %x74 / ; t tab U+0009 + * %x75 4HEXDIG ) ; uXXXX U+XXXX + * escape = %x5C ; \ + * quotation-mark = %x22 ; " + * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + * + * Extensions over RFC 8259: + * - Extra escape sequence in strings: + * 0x27 (apostrophe) is recognized after escape, too + * - Single-quoted strings: + * Like double-quoted strings, except they're delimited by %x27 + * (apostrophe) instead of %x22 (quotation mark), and can't contain + * unescaped apostrophe, but can contain unescaped quotation mark. * - * string - * "" - * " chars " - * chars - * char - * char chars - * char - * any-Unicode-character- - * except-"-or-\-or- - * control-character - * \" - * \\ - * \/ - * \b - * \f - * \n - * \r - * \t - * \u four-hex-digits + * Note: + * - Encoding is modified UTF-8. + * - Invalid Unicode characters are rejected. + * - Control characters \x00..\x1F are rejected by the lexer. */ -static QString *qstring_from_escaped_str(JSONParserContext *ctxt, - JSONToken *token) +static QString *parse_string(JSONParserContext *ctxt, JSONToken *token) { const char *ptr = token->str; QString *str; - int double_quote = 1; - - if (*ptr == '"') { - double_quote = 1; - } else { - double_quote = 0; - } - ptr++; - + char quote; + const char *beg; + int cp, trailing; + char *end; + ssize_t len; + char utf8_buf[5]; + + assert(*ptr == '"' || *ptr == '\''); + quote = *ptr++; str = qstring_new(); - while (*ptr && - ((double_quote && *ptr != '"') || (!double_quote && *ptr != '\''))) { - if (*ptr == '\\') { - ptr++; - switch (*ptr) { + while (*ptr != quote) { + assert(*ptr); + switch (*ptr) { + case '\\': + beg = ptr++; + switch (*ptr++) { case '"': - qstring_append(str, "\""); - ptr++; + qstring_append_chr(str, '"'); break; case '\'': - qstring_append(str, "'"); - ptr++; + qstring_append_chr(str, '\''); break; case '\\': - qstring_append(str, "\\"); - ptr++; + qstring_append_chr(str, '\\'); break; case '/': - qstring_append(str, "/"); - ptr++; + qstring_append_chr(str, '/'); break; case 'b': - qstring_append(str, "\b"); - ptr++; + qstring_append_chr(str, '\b'); break; case 'f': - qstring_append(str, "\f"); - ptr++; + qstring_append_chr(str, '\f'); break; case 'n': - qstring_append(str, "\n"); - ptr++; + qstring_append_chr(str, '\n'); break; case 'r': - qstring_append(str, "\r"); - ptr++; + qstring_append_chr(str, '\r'); break; case 't': - qstring_append(str, "\t"); - ptr++; + qstring_append_chr(str, '\t'); break; - case 'u': { - uint16_t unicode_char = 0; - char utf8_char[4]; - int i = 0; - - ptr++; - - for (i = 0; i < 4; i++) { - if (qemu_isxdigit(*ptr)) { - unicode_char |= hex2decimal(*ptr) << ((3 - i) * 4); + case 'u': + cp = cvt4hex(ptr); + ptr += 4; + + /* handle surrogate pairs */ + if (cp >= 0xD800 && cp <= 0xDBFF + && ptr[0] == '\\' && ptr[1] == 'u') { + /* leading surrogate followed by \u */ + cp = 0x10000 + ((cp & 0x3FF) << 10); + trailing = cvt4hex(ptr + 2); + if (trailing >= 0xDC00 && trailing <= 0xDFFF) { + /* followed by trailing surrogate */ + cp |= trailing & 0x3FF; + ptr += 6; } else { - parse_error(ctxt, token, - "invalid hex escape sequence in string"); - goto out; + cp = -1; /* invalid */ } - ptr++; } - wchar_to_utf8(unicode_char, utf8_char, sizeof(utf8_char)); - qstring_append(str, utf8_char); - } break; + if (mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp) < 0) { + parse_error(ctxt, token, + "%.*s is not a valid Unicode character", + (int)(ptr - beg), beg); + goto out; + } + qstring_append(str, utf8_buf); + break; default: parse_error(ctxt, token, "invalid escape sequence in string"); goto out; } - } else { - char dummy[2]; - - dummy[0] = *ptr++; - dummy[1] = 0; - - qstring_append(str, dummy); + break; + case '%': + if (ctxt->ap && ptr[1] != '%') { + parse_error(ctxt, token, "can't interpolate into string"); + goto out; + } + ptr++; + /* fall through */ + default: + cp = mod_utf8_codepoint(ptr, 6, &end); + if (cp < 0) { + parse_error(ctxt, token, "invalid UTF-8 sequence in string"); + goto out; + } + ptr = end; + len = mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp); + assert(len >= 0); + qstring_append(str, utf8_buf); } } @@ -233,48 +241,19 @@ out: static JSONToken *parser_context_pop_token(JSONParserContext *ctxt) { g_free(ctxt->current); - assert(!g_queue_is_empty(ctxt->buf)); ctxt->current = g_queue_pop_head(ctxt->buf); return ctxt->current; } static JSONToken *parser_context_peek_token(JSONParserContext *ctxt) { - assert(!g_queue_is_empty(ctxt->buf)); return g_queue_peek_head(ctxt->buf); } -static JSONParserContext *parser_context_new(GQueue *tokens) -{ - JSONParserContext *ctxt; - - if (!tokens) { - return NULL; - } - - ctxt = g_malloc0(sizeof(JSONParserContext)); - ctxt->buf = tokens; - - return ctxt; -} - -/* to support error propagation, ctxt->err must be freed separately */ -static void parser_context_free(JSONParserContext *ctxt) -{ - if (ctxt) { - while (!g_queue_is_empty(ctxt->buf)) { - parser_context_pop_token(ctxt); - } - g_free(ctxt->current); - g_queue_free(ctxt->buf); - g_free(ctxt); - } -} - /** * Parsing rules */ -static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) +static int parse_pair(JSONParserContext *ctxt, QDict *dict) { QObject *value; QString *key = NULL; @@ -286,7 +265,7 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) goto out; } - key = qobject_to(QString, parse_value(ctxt, ap)); + key = qobject_to(QString, parse_value(ctxt)); if (!key) { parse_error(ctxt, peek, "key is not a string in object"); goto out; @@ -303,7 +282,7 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) goto out; } - value = parse_value(ctxt, ap); + value = parse_value(ctxt); if (value == NULL) { parse_error(ctxt, token, "Missing value in dict"); goto out; @@ -321,7 +300,7 @@ out: return -1; } -static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) +static QObject *parse_object(JSONParserContext *ctxt) { QDict *dict = NULL; JSONToken *token, *peek; @@ -338,7 +317,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) } if (peek->type != JSON_RCURLY) { - if (parse_pair(ctxt, dict, ap) == -1) { + if (parse_pair(ctxt, dict) == -1) { goto out; } @@ -354,7 +333,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) goto out; } - if (parse_pair(ctxt, dict, ap) == -1) { + if (parse_pair(ctxt, dict) == -1) { goto out; } @@ -375,7 +354,7 @@ out: return NULL; } -static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) +static QObject *parse_array(JSONParserContext *ctxt) { QList *list = NULL; JSONToken *token, *peek; @@ -394,7 +373,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) if (peek->type != JSON_RSQUARE) { QObject *obj; - obj = parse_value(ctxt, ap); + obj = parse_value(ctxt); if (obj == NULL) { parse_error(ctxt, token, "expecting value"); goto out; @@ -414,7 +393,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) goto out; } - obj = parse_value(ctxt, ap); + obj = parse_value(ctxt); if (obj == NULL) { parse_error(ctxt, token, "expecting value"); goto out; @@ -457,40 +436,39 @@ static QObject *parse_keyword(JSONParserContext *ctxt) return NULL; } -static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap) +static QObject *parse_interpolation(JSONParserContext *ctxt) { JSONToken *token; - if (ap == NULL) { - return NULL; - } - token = parser_context_pop_token(ctxt); - assert(token && token->type == JSON_ESCAPE); + assert(token && token->type == JSON_INTERP); if (!strcmp(token->str, "%p")) { - return va_arg(*ap, QObject *); + return va_arg(*ctxt->ap, QObject *); } else if (!strcmp(token->str, "%i")) { - return QOBJECT(qbool_from_bool(va_arg(*ap, int))); + return QOBJECT(qbool_from_bool(va_arg(*ctxt->ap, int))); } else if (!strcmp(token->str, "%d")) { - return QOBJECT(qnum_from_int(va_arg(*ap, int))); + return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int))); } else if (!strcmp(token->str, "%ld")) { - return QOBJECT(qnum_from_int(va_arg(*ap, long))); - } else if (!strcmp(token->str, "%lld") || - !strcmp(token->str, "%I64d")) { - return QOBJECT(qnum_from_int(va_arg(*ap, long long))); + return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long))); + } else if (!strcmp(token->str, "%lld")) { + return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long long))); + } else if (!strcmp(token->str, "%" PRId64)) { + return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int64_t))); } else if (!strcmp(token->str, "%u")) { - return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned int))); + return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned int))); } else if (!strcmp(token->str, "%lu")) { - return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned long))); - } else if (!strcmp(token->str, "%llu") || - !strcmp(token->str, "%I64u")) { - return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned long long))); + return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long))); + } else if (!strcmp(token->str, "%llu")) { + return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long long))); + } else if (!strcmp(token->str, "%" PRIu64)) { + return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, uint64_t))); } else if (!strcmp(token->str, "%s")) { - return QOBJECT(qstring_from_str(va_arg(*ap, const char *))); + return QOBJECT(qstring_from_str(va_arg(*ctxt->ap, const char *))); } else if (!strcmp(token->str, "%f")) { - return QOBJECT(qnum_from_double(va_arg(*ap, double))); + return QOBJECT(qnum_from_double(va_arg(*ctxt->ap, double))); } + parse_error(ctxt, token, "invalid interpolation '%s'", token->str); return NULL; } @@ -503,7 +481,7 @@ static QObject *parse_literal(JSONParserContext *ctxt) switch (token->type) { case JSON_STRING: - return QOBJECT(qstring_from_escaped_str(ctxt, token)); + return QOBJECT(parse_string(ctxt, token)); case JSON_INTEGER: { /* * Represent JSON_INTEGER as QNUM_I64 if possible, else as @@ -538,7 +516,7 @@ static QObject *parse_literal(JSONParserContext *ctxt) } case JSON_FLOAT: /* FIXME dependent on locale; a pervasive issue in QEMU */ - /* FIXME our lexer matches RFC 7159 in forbidding Inf or NaN, + /* FIXME our lexer matches RFC 8259 in forbidding Inf or NaN, * but those might be useful extensions beyond JSON */ return QOBJECT(qnum_from_double(strtod(token->str, NULL))); default: @@ -546,7 +524,7 @@ static QObject *parse_literal(JSONParserContext *ctxt) } } -static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) +static QObject *parse_value(JSONParserContext *ctxt) { JSONToken *token; @@ -558,11 +536,11 @@ static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) switch (token->type) { case JSON_LCURLY: - return parse_object(ctxt, ap); + return parse_object(ctxt); case JSON_LSQUARE: - return parse_array(ctxt, ap); - case JSON_ESCAPE: - return parse_escape(ctxt, ap); + return parse_array(ctxt); + case JSON_INTERP: + return parse_interpolation(ctxt); case JSON_INTEGER: case JSON_FLOAT: case JSON_STRING: @@ -575,25 +553,32 @@ static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) } } -QObject *json_parser_parse(GQueue *tokens, va_list *ap) +JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr) { - return json_parser_parse_err(tokens, ap, NULL); + JSONToken *token = g_malloc(sizeof(JSONToken) + tokstr->len + 1); + + token->type = type; + memcpy(token->str, tokstr->str, tokstr->len); + token->str[tokstr->len] = 0; + token->x = x; + token->y = y; + return token; } -QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp) +QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp) { - JSONParserContext *ctxt = parser_context_new(tokens); + JSONParserContext ctxt = { .buf = tokens, .ap = ap }; QObject *result; - if (!ctxt) { - return NULL; - } - - result = parse_value(ctxt, ap); + result = parse_value(&ctxt); + assert(ctxt.err || g_queue_is_empty(ctxt.buf)); - error_propagate(errp, ctxt->err); + error_propagate(errp, ctxt.err); - parser_context_free(ctxt); + while (!g_queue_is_empty(ctxt.buf)) { + parser_context_pop_token(&ctxt); + } + g_free(ctxt.current); return result; } diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index c51c2021f9..47dd7ea576 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -12,34 +12,29 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" -#include "qapi/qmp/json-lexer.h" -#include "qapi/qmp/json-streamer.h" +#include "qapi/error.h" +#include "json-parser-int.h" #define MAX_TOKEN_SIZE (64ULL << 20) #define MAX_TOKEN_COUNT (2ULL << 20) -#define MAX_NESTING (1ULL << 10) - -static void json_message_free_token(void *token, void *opaque) -{ - g_free(token); -} +#define MAX_NESTING (1 << 10) static void json_message_free_tokens(JSONMessageParser *parser) { - if (parser->tokens) { - g_queue_foreach(parser->tokens, json_message_free_token, NULL); - g_queue_free(parser->tokens); - parser->tokens = NULL; + JSONToken *token; + + while ((token = g_queue_pop_head(&parser->tokens))) { + g_free(token); } } -static void json_message_process_token(JSONLexer *lexer, GString *input, - JSONTokenType type, int x, int y) +void json_message_process_token(JSONLexer *lexer, GString *input, + JSONTokenType type, int x, int y) { JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); + QObject *json = NULL; + Error *err = NULL; JSONToken *token; - GQueue *tokens; switch (type) { case JSON_LCURLY: @@ -54,79 +49,82 @@ static void json_message_process_token(JSONLexer *lexer, GString *input, case JSON_RSQUARE: parser->bracket_count--; break; + case JSON_ERROR: + error_setg(&err, "JSON parse error, stray '%s'", input->str); + goto out_emit; + case JSON_END_OF_INPUT: + if (g_queue_is_empty(&parser->tokens)) { + return; + } + json = json_parser_parse(&parser->tokens, parser->ap, &err); + goto out_emit; default: break; } - token = g_malloc(sizeof(JSONToken) + input->len + 1); - token->type = type; - memcpy(token->str, input->str, input->len); - token->str[input->len] = 0; - token->x = x; - token->y = y; + /* + * Security consideration, we limit total memory allocated per object + * and the maximum recursion depth that a message can force. + */ + if (parser->token_size + input->len + 1 > MAX_TOKEN_SIZE) { + error_setg(&err, "JSON token size limit exceeded"); + goto out_emit; + } + if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) { + error_setg(&err, "JSON token count limit exceeded"); + goto out_emit; + } + if (parser->bracket_count + parser->brace_count > MAX_NESTING) { + error_setg(&err, "JSON nesting depth limit exceeded"); + goto out_emit; + } + token = json_token(type, x, y, input); parser->token_size += input->len; - g_queue_push_tail(parser->tokens, token); + g_queue_push_tail(&parser->tokens, token); - if (type == JSON_ERROR) { - goto out_emit_bad; - } else if (parser->brace_count < 0 || - parser->bracket_count < 0 || - (parser->brace_count == 0 && - parser->bracket_count == 0)) { - goto out_emit; - } else if (parser->token_size > MAX_TOKEN_SIZE || - g_queue_get_length(parser->tokens) > MAX_TOKEN_COUNT || - parser->bracket_count + parser->brace_count > MAX_NESTING) { - /* Security consideration, we limit total memory allocated per object - * and the maximum recursion depth that a message can force. - */ - goto out_emit_bad; + if ((parser->brace_count > 0 || parser->bracket_count > 0) + && parser->bracket_count >= 0 && parser->bracket_count >= 0) { + return; } - return; + json = json_parser_parse(&parser->tokens, parser->ap, &err); -out_emit_bad: - /* - * Clear out token list and tell the parser to emit an error - * indication by passing it a NULL list - */ - json_message_free_tokens(parser); out_emit: - /* send current list of tokens to parser and reset tokenizer */ parser->brace_count = 0; parser->bracket_count = 0; - /* parser->emit takes ownership of parser->tokens. Remove our own - * reference to parser->tokens before handing it out to parser->emit. - */ - tokens = parser->tokens; - parser->tokens = g_queue_new(); - parser->emit(parser, tokens); + json_message_free_tokens(parser); parser->token_size = 0; + parser->emit(parser->opaque, json, err); } void json_message_parser_init(JSONMessageParser *parser, - void (*func)(JSONMessageParser *, GQueue *)) + void (*emit)(void *opaque, QObject *json, + Error *err), + void *opaque, va_list *ap) { - parser->emit = func; + parser->emit = emit; + parser->opaque = opaque; + parser->ap = ap; parser->brace_count = 0; parser->bracket_count = 0; - parser->tokens = g_queue_new(); + g_queue_init(&parser->tokens); parser->token_size = 0; - json_lexer_init(&parser->lexer, json_message_process_token); + json_lexer_init(&parser->lexer, !!ap); } -int json_message_parser_feed(JSONMessageParser *parser, +void json_message_parser_feed(JSONMessageParser *parser, const char *buffer, size_t size) { - return json_lexer_feed(&parser->lexer, buffer, size); + json_lexer_feed(&parser->lexer, buffer, size); } -int json_message_parser_flush(JSONMessageParser *parser) +void json_message_parser_flush(JSONMessageParser *parser) { - return json_lexer_flush(&parser->lexer); + json_lexer_flush(&parser->lexer); + assert(g_queue_is_empty(&parser->tokens)); } void json_message_parser_destroy(JSONMessageParser *parser) diff --git a/qobject/qbool.c b/qobject/qbool.c index b58249925c..06dfc43498 100644 --- a/qobject/qbool.c +++ b/qobject/qbool.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qbool.h" -#include "qemu-common.h" /** * qbool_from_bool(): Create a new QBool from a bool diff --git a/qobject/qjson.c b/qobject/qjson.c index ab4040f235..db36101f3b 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -13,9 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-streamer.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" @@ -27,16 +25,29 @@ typedef struct JSONParsingState { JSONMessageParser parser; - va_list *ap; QObject *result; Error *err; } JSONParsingState; -static void parse_json(JSONMessageParser *parser, GQueue *tokens) +static void consume_json(void *opaque, QObject *json, Error *err) { - JSONParsingState *s = container_of(parser, JSONParsingState, parser); + JSONParsingState *s = opaque; - s->result = json_parser_parse_err(tokens, s->ap, &s->err); + assert(!json != !err); + assert(!s->result || !s->err); + + if (s->result) { + qobject_unref(s->result); + s->result = NULL; + error_setg(&s->err, "Expecting at most one JSON value"); + } + if (s->err) { + qobject_unref(json); + error_free(err); + return; + } + s->result = json; + s->err = err; } /* @@ -54,13 +65,15 @@ static QObject *qobject_from_jsonv(const char *string, va_list *ap, { JSONParsingState state = {}; - state.ap = ap; - - json_message_parser_init(&state.parser, parse_json); + json_message_parser_init(&state.parser, consume_json, &state, ap); json_message_parser_feed(&state.parser, string, strlen(string)); json_message_parser_flush(&state.parser); json_message_parser_destroy(&state.parser); + if (!state.result && !state.err) { + error_setg(&state.err, "Expecting a JSON value"); + } + error_propagate(errp, state.err); return state.result; } diff --git a/qobject/qlist.c b/qobject/qlist.c index 37c1c167f1..b3274af88b 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -17,7 +17,6 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/queue.h" -#include "qemu-common.h" /** * qlist_new(): Create a new QList diff --git a/qobject/qnull.c b/qobject/qnull.c index f6f55f11ea..00870a1824 100644 --- a/qobject/qnull.c +++ b/qobject/qnull.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/qnull.h" QNull qnull_ = { diff --git a/qobject/qnum.c b/qobject/qnum.c index 1501c82832..7012fc57f2 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qnum.h" -#include "qemu-common.h" /** * qnum_from_int(): Create a new QNum from an int64_t diff --git a/qobject/qobject.c b/qobject/qobject.c index cf4b7e229e..878dd76e79 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qnull.h" #include "qapi/qmp/qnum.h" diff --git a/qobject/qstring.c b/qobject/qstring.c index 0f1510e792..1c6897df00 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qstring.h" -#include "qemu-common.h" /** * qstring_new(): Create a new empty QString diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 42e1c50dd8..3765b0e35e 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1367,10 +1367,10 @@ sub process { # extract the filename as it passes if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; - $realfile =~ s@^([^/]*)/@@; + $realfile =~ s@^([^/]*)/@@ if (!$file); } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; - $realfile =~ s@^([^/]*)/@@; + $realfile =~ s@^([^/]*)/@@ if (!$file); $p1_prefix = $1; if (!$file && $tree && $p1_prefix ne '' && @@ -1929,9 +1929,8 @@ sub process { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /{\s+$/ && $prefix !~ /\#\s*define[^(]*\([^)]*\)\s+$/ && - $prefix !~ /,\s+$/) { + $prefix !~ /[,{:]\s+$/) { ERROR("space prohibited before open square bracket '['\n" . $herecurr); } } diff --git a/scripts/qemu-guest-agent/fsfreeze-hook b/scripts/qemu-guest-agent/fsfreeze-hook index c27b29f282..13aafd4845 100755 --- a/scripts/qemu-guest-agent/fsfreeze-hook +++ b/scripts/qemu-guest-agent/fsfreeze-hook @@ -13,7 +13,7 @@ FSFREEZE_D=$(dirname -- "$0")/fsfreeze-hook.d # Check whether file $1 is a backup or rpm-generated file and should be ignored is_ignored_file() { case "$1" in - *~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave | *.sample) + *~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave | *.sample | *.dpkg-old | *.dpkg-new | *.dpkg-tmp | *.dpkg-dist | *.dpkg-bak | *.dpkg-backup | *.dpkg-remove) return 0 ;; esac return 1 diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index feb75390aa..0a964fe240 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -165,7 +165,9 @@ EOF rm -rf "$output/include/standard-headers/linux" mkdir -p "$output/include/standard-headers/linux" -for i in "$tmpdir"/include/linux/*virtio*.h "$tmpdir/include/linux/input.h" \ +for i in "$tmpdir"/include/linux/*virtio*.h \ + "$tmpdir/include/linux/qemu_fw_cfg.h" \ + "$tmpdir/include/linux/input.h" \ "$tmpdir/include/linux/input-event-codes.h" \ "$tmpdir/include/linux/pci_regs.h" \ "$tmpdir/include/linux/ethtool.h" "$tmpdir/include/linux/kernel.h" \ diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 1528a712a0..ed037aabee 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -301,7 +301,11 @@ void put_multipath_config(struct config *conf) static void multipath_pr_init(void) { udev = udev_new(); +#ifdef CONFIG_MPATH_NEW_API multipath_conf = mpath_lib_init(); +#else + mpath_lib_init(udev); +#endif } static int is_mpath(int fd) diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c index 9b6db2e740..eb745d7d6a 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -7,7 +7,7 @@ bool qemu_mutex_iothread_locked(void) return true; } -void qemu_mutex_lock_iothread(void) +void qemu_mutex_lock_iothread_impl(const char *file, int line) { } diff --git a/target/arm/arm-semi.c b/target/arm/arm-semi.c index 7cac8734c7..b2b22d231e 100644 --- a/target/arm/arm-semi.c +++ b/target/arm/arm-semi.c @@ -136,7 +136,7 @@ static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) #ifdef CONFIG_USER_ONLY ts->swi_errno = err; #else - syscall_err = err; + syscall_err = err; #endif reg0 = ret; } else { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 62c36b4150..65c0fa0a65 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1320,14 +1320,14 @@ enum arm_cpu_mode { #define ARM_VFP_FPINST2 10 /* iwMMXt coprocessor control registers. */ -#define ARM_IWMMXT_wCID 0 -#define ARM_IWMMXT_wCon 1 -#define ARM_IWMMXT_wCSSF 2 -#define ARM_IWMMXT_wCASF 3 -#define ARM_IWMMXT_wCGR0 8 -#define ARM_IWMMXT_wCGR1 9 -#define ARM_IWMMXT_wCGR2 10 -#define ARM_IWMMXT_wCGR3 11 +#define ARM_IWMMXT_wCID 0 +#define ARM_IWMMXT_wCon 1 +#define ARM_IWMMXT_wCSSF 2 +#define ARM_IWMMXT_wCASF 3 +#define ARM_IWMMXT_wCGR0 8 +#define ARM_IWMMXT_wCGR1 9 +#define ARM_IWMMXT_wCGR2 10 +#define ARM_IWMMXT_wCGR3 11 /* V7M CCR bits */ FIELD(V7M_CCR, NONBASETHRDENA, 0, 1) diff --git a/target/arm/helper.c b/target/arm/helper.c index c9bce1efcb..088f452716 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3754,11 +3754,11 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, - { .name = "HCR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "HCR_EL2", .state = ARM_CP_STATE_BOTH, .type = ARM_CP_NO_RAW, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, - .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, @@ -3857,6 +3857,15 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { REGINFO_SENTINEL }; +/* Ditto, but for registers which exist in ARMv8 but not v7 */ +static const ARMCPRegInfo el3_no_el2_v8_cp_reginfo[] = { + { .name = "HCR2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL2_RW, + .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -3883,10 +3892,26 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) * HCR_PTW forbids certain page-table setups * HCR_DC Disables stage1 and enables stage2 translation */ - if ((raw_read(env, ri) ^ value) & (HCR_VM | HCR_PTW | HCR_DC)) { + if ((env->cp15.hcr_el2 ^ value) & (HCR_VM | HCR_PTW | HCR_DC)) { tlb_flush(CPU(cpu)); } - raw_write(env, ri, value); + env->cp15.hcr_el2 = value; +} + +static void hcr_writehigh(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Handle HCR2 write, i.e. write to high half of HCR_EL2 */ + value = deposit64(env->cp15.hcr_el2, 32, 32, value); + hcr_write(env, NULL, value); +} + +static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Handle HCR write, i.e. write to low half of HCR_EL2 */ + value = deposit64(env->cp15.hcr_el2, 0, 32, value); + hcr_write(env, NULL, value); } static const ARMCPRegInfo el2_cp_reginfo[] = { @@ -3894,6 +3919,11 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), .writefn = hcr_write }, + { .name = "HCR", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, + .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), + .writefn = hcr_writelow }, { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, @@ -4128,6 +4158,16 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { REGINFO_SENTINEL }; +static const ARMCPRegInfo el2_v8_cp_reginfo[] = { + { .name = "HCR2", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL2_RW, + .fieldoffset = offsetofhigh32(CPUARMState, cp15.hcr_el2), + .writefn = hcr_writehigh }, + REGINFO_SENTINEL +}; + static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -5179,6 +5219,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) }; define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el2_cp_reginfo); + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, el2_v8_cp_reginfo); + } /* RVBAR_EL2 is only implemented if EL2 is the highest EL */ if (!arm_feature(env, ARM_FEATURE_EL3)) { ARMCPRegInfo rvbar = { @@ -5211,6 +5254,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) }; define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el3_no_el2_cp_reginfo); + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, el3_no_el2_v8_cp_reginfo); + } } } if (arm_feature(env, ARM_FEATURE_EL3)) { @@ -5459,6 +5505,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) REGINFO_SENTINEL }; define_arm_cp_regs(cpu, auxcr_reginfo); + if (arm_feature(env, ARM_FEATURE_V8)) { + /* HACTLR2 maps to ACTLR_EL2[63:32] and is not in ARMv7 */ + ARMCPRegInfo hactlr2_reginfo = { + .name = "HACTLR2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 3, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 + }; + define_one_arm_cp_reg(cpu, &hactlr2_reginfo); + } } if (arm_feature(env, ARM_FEATURE_CBAR)) { @@ -7977,6 +8033,125 @@ void aarch64_sync_64_to_32(CPUARMState *env) env->regs[15] = env->pc; } +static void take_aarch32_exception(CPUARMState *env, int new_mode, + uint32_t mask, uint32_t offset, + uint32_t newpc) +{ + /* Change the CPU state so as to actually take the exception. */ + switch_mode(env, new_mode); + /* + * For exceptions taken to AArch32 we must clear the SS bit in both + * PSTATE and in the old-state value we save to SPSR_<mode>, so zero it now. + */ + env->uncached_cpsr &= ~PSTATE_SS; + env->spsr = cpsr_read(env); + /* Clear IT bits. */ + env->condexec_bits = 0; + /* Switch to the new mode, and to the correct instruction set. */ + env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; + /* Set new mode endianness */ + env->uncached_cpsr &= ~CPSR_E; + if (env->cp15.sctlr_el[arm_current_el(env)] & SCTLR_EE) { + env->uncached_cpsr |= CPSR_E; + } + /* J and IL must always be cleared for exception entry */ + env->uncached_cpsr &= ~(CPSR_IL | CPSR_J); + env->daif |= mask; + + if (new_mode == ARM_CPU_MODE_HYP) { + env->thumb = (env->cp15.sctlr_el[2] & SCTLR_TE) != 0; + env->elr_el[2] = env->regs[15]; + } else { + /* + * this is a lie, as there was no c1_sys on V4T/V5, but who cares + * and we should just guard the thumb mode on V4 + */ + if (arm_feature(env, ARM_FEATURE_V4T)) { + env->thumb = + (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_TE) != 0; + } + env->regs[14] = env->regs[15] + offset; + } + env->regs[15] = newpc; +} + +static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) +{ + /* + * Handle exception entry to Hyp mode; this is sufficiently + * different to entry to other AArch32 modes that we handle it + * separately here. + * + * The vector table entry used is always the 0x14 Hyp mode entry point, + * unless this is an UNDEF/HVC/abort taken from Hyp to Hyp. + * The offset applied to the preferred return address is always zero + * (see DDI0487C.a section G1.12.3). + * PSTATE A/I/F masks are set based only on the SCR.EA/IRQ/FIQ values. + */ + uint32_t addr, mask; + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + switch (cs->exception_index) { + case EXCP_UDEF: + addr = 0x04; + break; + case EXCP_SWI: + addr = 0x14; + break; + case EXCP_BKPT: + /* Fall through to prefetch abort. */ + case EXCP_PREFETCH_ABORT: + env->cp15.ifar_s = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, "...with HIFAR 0x%x\n", + (uint32_t)env->exception.vaddress); + addr = 0x0c; + break; + case EXCP_DATA_ABORT: + env->cp15.dfar_s = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, "...with HDFAR 0x%x\n", + (uint32_t)env->exception.vaddress); + addr = 0x10; + break; + case EXCP_IRQ: + addr = 0x18; + break; + case EXCP_FIQ: + addr = 0x1c; + break; + case EXCP_HVC: + addr = 0x08; + break; + case EXCP_HYP_TRAP: + addr = 0x14; + default: + cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); + } + + if (cs->exception_index != EXCP_IRQ && cs->exception_index != EXCP_FIQ) { + env->cp15.esr_el[2] = env->exception.syndrome; + } + + if (arm_current_el(env) != 2 && addr < 0x14) { + addr = 0x14; + } + + mask = 0; + if (!(env->cp15.scr_el3 & SCR_EA)) { + mask |= CPSR_A; + } + if (!(env->cp15.scr_el3 & SCR_IRQ)) { + mask |= CPSR_I; + } + if (!(env->cp15.scr_el3 & SCR_FIQ)) { + mask |= CPSR_F; + } + + addr += env->cp15.hvbar; + + take_aarch32_exception(env, ARM_CPU_MODE_HYP, mask, 0, addr); +} + static void arm_cpu_do_interrupt_aarch32(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -8012,6 +8187,11 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) env->cp15.mdscr_el1 = deposit64(env->cp15.mdscr_el1, 2, 4, moe); } + if (env->exception.target_el == 2) { + arm_cpu_do_interrupt_aarch32_hyp(cs); + return; + } + /* TODO: Vectored interrupt controller. */ switch (cs->exception_index) { case EXCP_UDEF: @@ -8119,29 +8299,7 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) env->cp15.scr_el3 &= ~SCR_NS; } - switch_mode (env, new_mode); - /* For exceptions taken to AArch32 we must clear the SS bit in both - * PSTATE and in the old-state value we save to SPSR_<mode>, so zero it now. - */ - env->uncached_cpsr &= ~PSTATE_SS; - env->spsr = cpsr_read(env); - /* Clear IT bits. */ - env->condexec_bits = 0; - /* Switch to the new mode, and to the correct instruction set. */ - env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; - /* Set new mode endianness */ - env->uncached_cpsr &= ~CPSR_E; - if (env->cp15.sctlr_el[arm_current_el(env)] & SCTLR_EE) { - env->uncached_cpsr |= CPSR_E; - } - env->daif |= mask; - /* this is a lie, as the was no c1_sys on V4T/V5, but who cares - * and we should just guard the thumb mode on V4 */ - if (arm_feature(env, ARM_FEATURE_V4T)) { - env->thumb = (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_TE) != 0; - } - env->regs[14] = env->regs[15] + offset; - env->regs[15] = addr; + take_aarch32_exception(env, new_mode, mask, offset, addr); } /* Handle exception entry to a target EL which is using AArch64 */ @@ -11564,45 +11722,30 @@ float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env) #define VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ float##fsz HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \ void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - float##fsz tmp; \ - tmp = itype##_to_##float##fsz(x, fpst); \ - return float##fsz##_scalbn(tmp, -(int)shift, fpst); \ -} +{ return itype##_to_##float##fsz##_scalbn(x, -shift, fpstp); } -/* Notice that we want only input-denormal exception flags from the - * scalbn operation: the other possible flags (overflow+inexact if - * we overflow to infinity, output-denormal) aren't correct for the - * complete scale-and-convert operation. - */ -#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, round) \ -uint##isz##_t HELPER(vfp_to##name##p##round)(float##fsz x, \ - uint32_t shift, \ - void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - int old_exc_flags = get_float_exception_flags(fpst); \ - float##fsz tmp; \ - if (float##fsz##_is_any_nan(x)) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - tmp = float##fsz##_scalbn(x, shift, fpst); \ - old_exc_flags |= get_float_exception_flags(fpst) \ - & float_flag_input_denormal; \ - set_float_exception_flags(old_exc_flags, fpst); \ - return float##fsz##_to_##itype##round(tmp, fpst); \ +#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, ROUND, suff) \ +uint##isz##_t HELPER(vfp_to##name##p##suff)(float##fsz x, uint32_t shift, \ + void *fpst) \ +{ \ + if (unlikely(float##fsz##_is_any_nan(x))) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##itype##_scalbn(x, ROUND, shift, fpst); \ } #define VFP_CONV_FIX(name, p, fsz, isz, itype) \ VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, _round_to_zero) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, ) +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, \ + float_round_to_zero, _round_to_zero) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, \ + get_float_rounding_mode(fpst), ) #define VFP_CONV_FIX_A64(name, p, fsz, isz, itype) \ VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, ) +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, \ + get_float_rounding_mode(fpst), ) VFP_CONV_FIX(sh, d, 64, 64, int16) VFP_CONV_FIX(sl, d, 64, 64, int32) @@ -11622,87 +11765,84 @@ VFP_CONV_FIX_A64(uq, s, 32, 64, uint64) #undef VFP_CONV_FLOAT_FIX_ROUND #undef VFP_CONV_FIX_A64 -/* Conversion to/from f16 can overflow to infinity before/after scaling. - * Therefore we convert to f64, scale, and then convert f64 to f16; or - * vice versa for conversion to integer. - * - * For 16- and 32-bit integers, the conversion to f64 never rounds. - * For 64-bit integers, any integer that would cause rounding will also - * overflow to f16 infinity, so there is no double rounding problem. - */ - -static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst) -{ - return float64_to_float16(float64_scalbn(f, -shift, fpst), true, fpst); -} - uint32_t HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) { - return do_postscale_fp16(int32_to_float64(x, fpst), shift, fpst); + return int32_to_float16_scalbn(x, -shift, fpst); } uint32_t HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) { - return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst); + return uint32_to_float16_scalbn(x, -shift, fpst); } uint32_t HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) { - return do_postscale_fp16(int64_to_float64(x, fpst), shift, fpst); + return int64_to_float16_scalbn(x, -shift, fpst); } uint32_t HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) { - return do_postscale_fp16(uint64_to_float64(x, fpst), shift, fpst); + return uint64_to_float16_scalbn(x, -shift, fpst); } -static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst) +uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst) { - if (unlikely(float16_is_any_nan(f))) { + if (unlikely(float16_is_any_nan(x))) { float_raise(float_flag_invalid, fpst); return 0; - } else { - int old_exc_flags = get_float_exception_flags(fpst); - float64 ret; - - ret = float16_to_float64(f, true, fpst); - ret = float64_scalbn(ret, shift, fpst); - old_exc_flags |= get_float_exception_flags(fpst) - & float_flag_input_denormal; - set_float_exception_flags(old_exc_flags, fpst); - - return ret; } -} - -uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst) -{ - return float64_to_int16(do_prescale_fp16(x, shift, fpst), fpst); + return float16_to_int16_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint32_t HELPER(vfp_touhh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint16_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint32_t HELPER(vfp_toslh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_int32(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int32_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint32_t HELPER(vfp_toulh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_uint32(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint32_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint64_t HELPER(vfp_tosqh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_int64(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int64_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint64_t HELPER(vfp_touqh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_uint64(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint64_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } /* Set the current fp rounding mode and return the old one. @@ -12331,6 +12471,7 @@ int arm_rmode_to_sf(int rmode) /* FIXME: add support for TIEAWAY and ODD */ qemu_log_mask(LOG_UNIMP, "arm: unimplemented rounding mode: %d\n", rmode); + /* fall through for now */ case FPROUNDING_TIEEVEN: default: rmode = float_round_nearest_even; diff --git a/target/arm/iwmmxt_helper.c b/target/arm/iwmmxt_helper.c index f6a4fc5b7f..24244d012c 100644 --- a/target/arm/iwmmxt_helper.c +++ b/target/arm/iwmmxt_helper.c @@ -27,30 +27,30 @@ /* iwMMXt macros extracted from GNU gdb. */ /* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations. */ -#define SIMD8_SET( v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n))) -#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n))) -#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n))) -#define SIMD64_SET(v, n) ((v != 0) << (32 + (n))) +#define SIMD8_SET(v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n))) +#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n))) +#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n))) +#define SIMD64_SET(v, n) ((v != 0) << (32 + (n))) /* Flags to pass as "n" above. */ -#define SIMD_NBIT -1 -#define SIMD_ZBIT -2 -#define SIMD_CBIT -3 -#define SIMD_VBIT -4 +#define SIMD_NBIT -1 +#define SIMD_ZBIT -2 +#define SIMD_CBIT -3 +#define SIMD_VBIT -4 /* Various status bit macros. */ -#define NBIT8(x) ((x) & 0x80) -#define NBIT16(x) ((x) & 0x8000) -#define NBIT32(x) ((x) & 0x80000000) -#define NBIT64(x) ((x) & 0x8000000000000000ULL) -#define ZBIT8(x) (((x) & 0xff) == 0) -#define ZBIT16(x) (((x) & 0xffff) == 0) -#define ZBIT32(x) (((x) & 0xffffffff) == 0) -#define ZBIT64(x) (x == 0) +#define NBIT8(x) ((x) & 0x80) +#define NBIT16(x) ((x) & 0x8000) +#define NBIT32(x) ((x) & 0x80000000) +#define NBIT64(x) ((x) & 0x8000000000000000ULL) +#define ZBIT8(x) (((x) & 0xff) == 0) +#define ZBIT16(x) (((x) & 0xffff) == 0) +#define ZBIT32(x) (((x) & 0xffffffff) == 0) +#define ZBIT64(x) (x == 0) /* Sign extension macros. */ -#define EXTEND8H(a) ((uint16_t) (int8_t) (a)) -#define EXTEND8(a) ((uint32_t) (int8_t) (a)) -#define EXTEND16(a) ((uint32_t) (int16_t) (a)) -#define EXTEND16S(a) ((int32_t) (int16_t) (a)) -#define EXTEND32(a) ((uint64_t) (int32_t) (a)) +#define EXTEND8H(a) ((uint16_t) (int8_t) (a)) +#define EXTEND8(a) ((uint32_t) (int8_t) (a)) +#define EXTEND16(a) ((uint32_t) (int16_t) (a)) +#define EXTEND16S(a) ((int32_t) (int16_t) (a)) +#define EXTEND32(a) ((uint64_t) (int32_t) (a)) uint64_t HELPER(iwmmxt_maddsq)(uint64_t a, uint64_t b) { @@ -159,141 +159,141 @@ uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b) #define NZBIT64(x) \ SIMD64_SET(NBIT64(x), SIMD_NBIT) | \ SIMD64_SET(ZBIT64(x), SIMD_ZBIT) -#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ +#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \ - (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) | \ - (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) | \ - (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ +{ \ + a = \ + (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \ + (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) | \ + (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) | \ + (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ + NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ + NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ + NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffff) << 0) | \ - (((b >> SH0) & 0xffff) << 16) | \ - (((a >> SH2) & 0xffff) << 32) | \ - (((b >> SH2) & 0xffff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) | \ - NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \ +{ \ + a = \ + (((a >> SH0) & 0xffff) << 0) | \ + (((b >> SH0) & 0xffff) << 16) | \ + (((a >> SH2) & 0xffff) << 32) | \ + (((b >> SH2) & 0xffff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) | \ + NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffffffff) << 0) | \ - (((b >> SH0) & 0xffffffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ +{ \ + a = \ + (((a >> SH0) & 0xffffffff) << 0) | \ + (((b >> SH0) & 0xffffffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xff) << 0) | \ - (((x >> SH1) & 0xff) << 16) | \ - (((x >> SH2) & 0xff) << 32) | \ - (((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ +{ \ + x = \ + (((x >> SH0) & 0xff) << 0) | \ + (((x >> SH1) & 0xff) << 16) | \ + (((x >> SH2) & 0xff) << 32) | \ + (((x >> SH3) & 0xff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ + NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xffff) << 0) | \ - (((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ +{ \ + x = \ + (((x >> SH0) & 0xffff) << 0) | \ + (((x >> SH2) & 0xffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = (((x >> SH0) & 0xffffffff) << 0); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ +{ \ + x = (((x >> SH0) & 0xffffffff) << 0); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \ - ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) | \ - ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) | \ - ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ +{ \ + x = \ + ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \ + ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) | \ + ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) | \ + ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ + NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \ - ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ +{ \ + x = \ + ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \ + ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = EXTEND32((x >> SH0) & 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ +{ \ + x = EXTEND32((x >> SH0) & 0xffffffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ return x; \ } IWMMXT_OP_UNPACK(l, 0, 8, 16, 24) IWMMXT_OP_UNPACK(h, 32, 40, 48, 56) -#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ +#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ - CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \ - CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \ - CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ +{ \ + a = \ + CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ + CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \ + CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \ + CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ + NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ + NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ + NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ - CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | \ - NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \ +{ \ + a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ + CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | \ + NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tl, O, 0xffffffff) | \ - CMP(32, Tl, O, 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ +{ \ + a = CMP(0, Tl, O, 0xffffffff) | \ + CMP(32, Tl, O, 0xffffffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ return a; \ } #define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \ diff --git a/target/arm/translate.c b/target/arm/translate.c index bcfc29c5a6..c6a5d2ac44 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -1627,7 +1627,7 @@ static inline void gen_mov_vreg_F0(int dp, int reg) tcg_gen_st_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg)); } -#define ARM_CP_RW_BIT (1 << 20) +#define ARM_CP_RW_BIT (1 << 20) static inline void iwmmxt_load_reg(TCGv_i64 var, int reg) { @@ -1861,12 +1861,12 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) wrd = insn & 0xf; rdlo = (insn >> 12) & 0xf; rdhi = (insn >> 16) & 0xf; - if (insn & ARM_CP_RW_BIT) { /* TMRRC */ + if (insn & ARM_CP_RW_BIT) { /* TMRRC */ iwmmxt_load_reg(cpu_V0, wrd); tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); tcg_gen_extrl_i64_i32(cpu_R[rdhi], cpu_V0); - } else { /* TMCRR */ + } else { /* TMCRR */ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); iwmmxt_store_reg(cpu_V0, wrd); gen_op_iwmmxt_set_mup(); @@ -1881,25 +1881,25 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) return 1; } if (insn & ARM_CP_RW_BIT) { - if ((insn >> 28) == 0xf) { /* WLDRW wCx */ + if ((insn >> 28) == 0xf) { /* WLDRW wCx */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); iwmmxt_store_creg(wrd, tmp); } else { i = 1; if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WLDRD */ + if (insn & (1 << 22)) { /* WLDRD */ gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); i = 0; - } else { /* WLDRW wRd */ + } else { /* WLDRW wRd */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); } } else { tmp = tcg_temp_new_i32(); - if (insn & (1 << 22)) { /* WLDRH */ + if (insn & (1 << 22)) { /* WLDRH */ gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); - } else { /* WLDRB */ + } else { /* WLDRB */ gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); } } @@ -1910,24 +1910,24 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); } } else { - if ((insn >> 28) == 0xf) { /* WSTRW wCx */ + if ((insn >> 28) == 0xf) { /* WSTRW wCx */ tmp = iwmmxt_load_creg(wrd); gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } else { gen_op_iwmmxt_movq_M0_wRn(wrd); tmp = tcg_temp_new_i32(); if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WSTRD */ + if (insn & (1 << 22)) { /* WSTRD */ gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); - } else { /* WSTRW wRd */ + } else { /* WSTRW wRd */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } } else { - if (insn & (1 << 22)) { /* WSTRH */ + if (insn & (1 << 22)) { /* WSTRH */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); gen_aa32_st16(s, tmp, addr, get_mem_index(s)); - } else { /* WSTRB */ + } else { /* WSTRB */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); gen_aa32_st8(s, tmp, addr, get_mem_index(s)); } @@ -1943,7 +1943,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) return 1; switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) { - case 0x000: /* WOR */ + case 0x000: /* WOR */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -1954,7 +1954,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x011: /* TMCR */ + case 0x011: /* TMCR */ if (insn & 0xf) return 1; rd = (insn >> 12) & 0xf; @@ -1985,7 +1985,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) return 1; } break; - case 0x100: /* WXOR */ + case 0x100: /* WXOR */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -1996,7 +1996,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x111: /* TMRC */ + case 0x111: /* TMRC */ if (insn & 0xf) return 1; rd = (insn >> 12) & 0xf; @@ -2004,7 +2004,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tmp = iwmmxt_load_creg(wrd); store_reg(s, rd, tmp); break; - case 0x300: /* WANDN */ + case 0x300: /* WANDN */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -2016,7 +2016,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x200: /* WAND */ + case 0x200: /* WAND */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -2027,7 +2027,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x810: case 0xa10: /* WMADD */ + case 0x810: case 0xa10: /* WMADD */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -2039,7 +2039,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ + case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2061,7 +2061,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ + case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2083,7 +2083,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ + case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2097,7 +2097,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ + case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2116,7 +2116,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ + case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2132,7 +2132,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ + case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2154,7 +2154,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ + case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2174,7 +2174,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ + case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2187,7 +2187,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ + case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ if (((insn >> 6) & 3) == 3) return 1; rd = (insn >> 12) & 0xf; @@ -2218,7 +2218,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ + case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ rd = (insn >> 12) & 0xf; wrd = (insn >> 16) & 0xf; if (rd == 15 || ((insn >> 22) & 3) == 3) @@ -2251,7 +2251,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } store_reg(s, rd, tmp); break; - case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ + case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ if ((insn & 0x000ff008) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); @@ -2270,7 +2270,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_set_nzcv(tmp); tcg_temp_free_i32(tmp); break; - case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ + case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ if (((insn >> 6) & 3) == 3) return 1; rd = (insn >> 12) & 0xf; @@ -2291,7 +2291,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ + case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); @@ -2319,7 +2319,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp); break; - case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ + case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); @@ -2339,7 +2339,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ + case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); @@ -2367,7 +2367,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp); break; - case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ + case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ rd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3) @@ -2387,7 +2387,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } store_reg(s, rd, tmp); break; - case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ + case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ case 0x906: case 0xb06: case 0xd06: case 0xf06: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2419,7 +2419,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ + case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2450,7 +2450,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ + case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2481,7 +2481,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ + case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ case 0x214: case 0x614: case 0xa14: case 0xe14: if (((insn >> 22) & 3) == 0) return 1; @@ -2509,7 +2509,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ + case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ case 0x014: case 0x414: case 0x814: case 0xc14: if (((insn >> 22) & 3) == 0) return 1; @@ -2537,7 +2537,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ + case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ case 0x114: case 0x514: case 0x914: case 0xd14: if (((insn >> 22) & 3) == 0) return 1; @@ -2565,7 +2565,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ + case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ case 0x314: case 0x714: case 0xb14: case 0xf14: if (((insn >> 22) & 3) == 0) return 1; @@ -2601,7 +2601,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ + case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ case 0x916: case 0xb16: case 0xd16: case 0xf16: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2632,7 +2632,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ + case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ case 0x816: case 0xa16: case 0xc16: case 0xe16: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2663,7 +2663,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ + case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ case 0x402: case 0x502: case 0x602: case 0x702: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2676,7 +2676,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ + case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ case 0x41a: case 0x51a: case 0x61a: case 0x71a: case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: @@ -2719,7 +2719,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ + case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ case 0x41e: case 0x51e: case 0x61e: case 0x71e: case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: @@ -2733,7 +2733,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ + case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ case 0x418: case 0x518: case 0x618: case 0x718: case 0x818: case 0x918: case 0xa18: case 0xb18: case 0xc18: case 0xd18: case 0xe18: case 0xf18: @@ -2776,7 +2776,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ + case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ case 0x408: case 0x508: case 0x608: case 0x708: case 0x808: case 0x908: case 0xa08: case 0xb08: case 0xc08: case 0xd08: case 0xe08: case 0xf08: @@ -2823,13 +2823,13 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tmp = load_reg(s, rd0); tmp2 = load_reg(s, rd1); switch ((insn >> 16) & 0xf) { - case 0x0: /* TMIA */ + case 0x0: /* TMIA */ gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0x8: /* TMIAPH */ + case 0x8: /* TMIAPH */ gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ + case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ if (insn & (1 << 16)) tcg_gen_shri_i32(tmp, tmp, 16); if (insn & (1 << 17)) @@ -2872,16 +2872,16 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) tmp = load_reg(s, rd0); tmp2 = load_reg(s, rd1); switch ((insn >> 16) & 0xf) { - case 0x0: /* MIA */ + case 0x0: /* MIA */ gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0x8: /* MIAPH */ + case 0x8: /* MIAPH */ gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0xc: /* MIABB */ - case 0xd: /* MIABT */ - case 0xe: /* MIATB */ - case 0xf: /* MIATT */ + case 0xc: /* MIABB */ + case 0xd: /* MIABT */ + case 0xe: /* MIATB */ + case 0xf: /* MIATT */ if (insn & (1 << 16)) tcg_gen_shri_i32(tmp, tmp, 16); if (insn & (1 << 17)) @@ -2907,13 +2907,13 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) if (acc != 0) return 1; - if (insn & ARM_CP_RW_BIT) { /* MRA */ + if (insn & ARM_CP_RW_BIT) { /* MRA */ iwmmxt_load_reg(cpu_V0, acc); tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); tcg_gen_extrl_i64_i32(cpu_R[rdhi], cpu_V0); tcg_gen_andi_i32(cpu_R[rdhi], cpu_R[rdhi], (1 << (40 - 32)) - 1); - } else { /* MAR */ + } else { /* MAR */ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); iwmmxt_store_reg(cpu_V0, acc); } diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4e4fe8fa8b..f24295e6e4 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3880,6 +3880,9 @@ arch_query_cpu_model_expansion(CpuModelExpansionType type, } props = qdict_new(); + ret->model = g_new0(CpuModelInfo, 1); + ret->model->props = QOBJECT(props); + ret->model->has_props = true; switch (type) { case CPU_MODEL_EXPANSION_TYPE_STATIC: @@ -3900,15 +3903,9 @@ arch_query_cpu_model_expansion(CpuModelExpansionType type, goto out; } - if (!props) { - props = qdict_new(); - } x86_cpu_to_dict(xc, props); - ret->model = g_new0(CpuModelInfo, 1); ret->model->name = g_strdup(base_name); - ret->model->props = QOBJECT(props); - ret->model->has_props = true; out: object_unref(OBJECT(xc)); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 9cad5812cd..b572a8e4aa 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1515,6 +1515,8 @@ int cpu_x86_support_mca_broadcast(CPUX86State *env); int cpu_get_pic_interrupt(CPUX86State *s); /* MSDOS compatibility mode FPU exception support */ void cpu_set_ferr(CPUX86State *s); +/* mpx_helper.c */ +void cpu_sync_bndcs_hflags(CPUX86State *env); /* this function must always be used to load data in the segment cache: it synchronizes the hflags with the segment cache values */ @@ -1557,6 +1559,8 @@ static inline void cpu_x86_load_seg_cache(CPUX86State *env, #error HF_CPL_MASK is hardcoded #endif env->hflags = (env->hflags & ~HF_CPL_MASK) | cpl; + /* Possibly switch between BNDCFGS and BNDCFGU */ + cpu_sync_bndcs_hflags(env); } new_hflags = (env->segs[R_SS].flags & DESC_B_MASK) >> (DESC_B_SHIFT - HF_SS32_SHIFT); @@ -1889,9 +1893,6 @@ void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, */ void x86_cpu_change_kvm_default(const char *prop, const char *value); -/* mpx_helper.c */ -void cpu_sync_bndcs_hflags(CPUX86State *env); - /* Return name of 32-bit register, from a R_* constant */ const char *get_register_name_32(unsigned int reg); diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 9313602d3d..0b2a07d3a4 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -1381,17 +1381,9 @@ int kvm_arch_init(MachineState *ms, KVMState *s) int ret; struct utsname utsname; -#ifdef KVM_CAP_XSAVE has_xsave = kvm_check_extension(s, KVM_CAP_XSAVE); -#endif - -#ifdef KVM_CAP_XCRS has_xcrs = kvm_check_extension(s, KVM_CAP_XCRS); -#endif - -#ifdef KVM_CAP_PIT_STATE2 has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); -#endif hv_vpindex_settable = kvm_check_extension(s, KVM_CAP_HYPERV_VP_INDEX); diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c index 00301a0c04..d1cbc6ebf0 100644 --- a/target/i386/seg_helper.c +++ b/target/i386/seg_helper.c @@ -518,6 +518,11 @@ static void switch_tss(CPUX86State *env, int tss_selector, static inline unsigned int get_sp_mask(unsigned int e2) { +#ifdef TARGET_X86_64 + if (e2 & DESC_L_MASK) { + return 0; + } else +#endif if (e2 & DESC_B_MASK) { return 0xffffffff; } else { @@ -1628,8 +1633,8 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, } limit = get_seg_limit(e1, e2); if (new_eip > limit && - !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, get_seg_base(e1, e2), limit, e2); @@ -1640,6 +1645,14 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, rpl = new_cs & 3; cpl = env->hflags & HF_CPL_MASK; type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (type != 12) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } +#endif switch (type) { case 1: /* 286 TSS */ case 9: /* 386 TSS */ @@ -1662,6 +1675,23 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, if (type == 12) { new_eip |= (e2 & 0xffff0000); } + +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + /* load the upper 8 bytes of the 64-bit call gate */ + if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + if (type != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + new_eip |= ((target_ulong)e1) << 32; + } +#endif + if (load_segment_ra(env, &e1, &e2, gate_cs, GETPC()) != 0) { raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } @@ -1675,11 +1705,22 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, (!(e2 & DESC_C_MASK) && (dpl != cpl))) { raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (!(e2 & DESC_L_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } + if (e2 & DESC_B_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } + } +#endif if (!(e2 & DESC_P_MASK)) { raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } limit = get_seg_limit(e1, e2); - if (new_eip > limit) { + if (new_eip > limit && + (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) { raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl, @@ -1724,12 +1765,12 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, int shift, target_ulong next_eip) { int new_stack, i; - uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count; - uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask; + uint32_t e1, e2, cpl, dpl, rpl, selector, param_count; + uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl, sp_mask; uint32_t val, limit, old_sp_mask; - target_ulong ssp, old_ssp; + target_ulong ssp, old_ssp, offset, sp; - LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift); + LOG_PCALL("lcall %04x:" TARGET_FMT_lx " s=%d\n", new_cs, new_eip, shift); LOG_PCALL_STATE(CPU(x86_env_get_cpu(env))); if ((new_cs & 0xfffc) == 0) { raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); @@ -1807,6 +1848,15 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; dpl = (e2 >> DESC_DPL_SHIFT) & 3; rpl = new_cs & 3; + +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (type != 12) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } +#endif + switch (type) { case 1: /* available 286 TSS */ case 9: /* available 386 TSS */ @@ -1833,8 +1883,23 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } selector = e1 >> 16; - offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); param_count = e2 & 0x1f; + offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + /* load the upper 8 bytes of the 64-bit call gate */ + if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + if (type != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + offset |= ((target_ulong)e1) << 32; + } +#endif if ((selector & 0xfffc) == 0) { raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } @@ -1849,46 +1914,80 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, if (dpl > cpl) { raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (!(e2 & DESC_L_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + if (e2 & DESC_B_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + shift++; + } +#endif if (!(e2 & DESC_P_MASK)) { raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_C_MASK) && dpl < cpl) { /* to inner privilege */ - get_ss_esp_from_tss(env, &ss, &sp, dpl, GETPC()); - LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" - TARGET_FMT_lx "\n", ss, sp, param_count, - env->regs[R_ESP]); - if ((ss & 0xfffc) == 0) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if ((ss & 3) != dpl) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if (load_segment_ra(env, &ss_e1, &ss_e2, ss, GETPC()) != 0) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; - if (ss_dpl != dpl) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if (!(ss_e2 & DESC_S_MASK) || - (ss_e2 & DESC_CS_MASK) || - !(ss_e2 & DESC_W_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if (!(ss_e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); +#ifdef TARGET_X86_64 + if (shift == 2) { + sp = get_rsp_from_tss(env, dpl); + ss = dpl; /* SS = NULL selector with RPL = new CPL */ + new_stack = 1; + sp_mask = 0; + ssp = 0; /* SS base is always zero in IA-32e mode */ + LOG_PCALL("new ss:rsp=%04x:%016llx env->regs[R_ESP]=" + TARGET_FMT_lx "\n", ss, sp, env->regs[R_ESP]); + } else +#endif + { + uint32_t sp32; + get_ss_esp_from_tss(env, &ss, &sp32, dpl, GETPC()); + LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" + TARGET_FMT_lx "\n", ss, sp32, param_count, + env->regs[R_ESP]); + sp = sp32; + if ((ss & 0xfffc) == 0) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if ((ss & 3) != dpl) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if (load_segment_ra(env, &ss_e1, &ss_e2, ss, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; + if (ss_dpl != dpl) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if (!(ss_e2 & DESC_S_MASK) || + (ss_e2 & DESC_CS_MASK) || + !(ss_e2 & DESC_W_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if (!(ss_e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + + sp_mask = get_sp_mask(ss_e2); + ssp = get_seg_base(ss_e1, ss_e2); } /* push_size = ((param_count * 2) + 8) << shift; */ old_sp_mask = get_sp_mask(env->segs[R_SS].flags); old_ssp = env->segs[R_SS].base; - - sp_mask = get_sp_mask(ss_e2); - ssp = get_seg_base(ss_e1, ss_e2); - if (shift) { +#ifdef TARGET_X86_64 + if (shift == 2) { + /* XXX: verify if new stack address is canonical */ + PUSHQ_RA(sp, env->segs[R_SS].selector, GETPC()); + PUSHQ_RA(sp, env->regs[R_ESP], GETPC()); + /* parameters aren't supported for 64-bit call gates */ + } else +#endif + if (shift == 1) { PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); for (i = param_count - 1; i >= 0; i--) { @@ -1917,7 +2016,13 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, new_stack = 0; } - if (shift) { +#ifdef TARGET_X86_64 + if (shift == 2) { + PUSHQ_RA(sp, env->segs[R_CS].selector, GETPC()); + PUSHQ_RA(sp, next_eip, GETPC()); + } else +#endif + if (shift == 1) { PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); } else { @@ -1928,11 +2033,18 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, /* from this point, not restartable */ if (new_stack) { - ss = (ss & ~3) | dpl; - cpu_x86_load_seg_cache(env, R_SS, ss, - ssp, - get_seg_limit(ss_e1, ss_e2), - ss_e2); +#ifdef TARGET_X86_64 + if (shift == 2) { + cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0); + } else +#endif + { + ss = (ss & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_SS, ss, + ssp, + get_seg_limit(ss_e1, ss_e2), + ss_e2); + } } selector = (selector & ~3) | dpl; diff --git a/target/i386/translate.c b/target/i386/translate.c index 07d185e7b6..1f9d1d9b24 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -4689,6 +4689,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x82: if (CODE64(s)) goto illegal_op; + /* fall through */ case 0x80: /* GRP1 */ case 0x81: case 0x83: @@ -8292,6 +8293,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x10e ... 0x10f: /* 3DNow! instructions, ignore prefixes */ s->prefix &= ~(PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA); + /* fall through */ case 0x110 ... 0x117: case 0x128 ... 0x12f: case 0x138 ... 0x13a: diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 009202cf64..28af4d191c 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -506,6 +506,8 @@ struct CPUMIPSState { uint64_t lladdr; target_ulong llval; target_ulong llnewval; + uint64_t llval_wp; + uint32_t llnewval_wp; target_ulong llreg; uint64_t CP0_LLAddr_rw_bitmask; int CP0_LLAddr_shift; diff --git a/target/mips/helper.c b/target/mips/helper.c index e215af9a41..f0c268b83c 100644 --- a/target/mips/helper.c +++ b/target/mips/helper.c @@ -682,6 +682,22 @@ static void set_hflags_for_handler (CPUMIPSState *env) static inline void set_badinstr_registers(CPUMIPSState *env) { + if (env->insn_flags & ISA_NANOMIPS32) { + if (env->CP0_Config3 & (1 << CP0C3_BI)) { + uint32_t instr = (cpu_lduw_code(env, env->active_tc.PC)) << 16; + if ((instr & 0x10000000) == 0) { + instr |= cpu_lduw_code(env, env->active_tc.PC + 2); + } + env->CP0_BadInstr = instr; + + if ((instr & 0xFC000000) == 0x60000000) { + instr = cpu_lduw_code(env, env->active_tc.PC + 4) << 16; + env->CP0_BadInstrX = instr; + } + } + return; + } + if (env->hflags & MIPS_HFLAG_M16) { /* TODO: add BadInstr support for microMIPS */ return; diff --git a/target/mips/helper.h b/target/mips/helper.h index 5f492348dd..b2a780a6f2 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -40,6 +40,8 @@ DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(dbitswap, TCG_CALL_NO_RWG_SE, tl, tl) #endif +DEF_HELPER_FLAGS_4(rotx, TCG_CALL_NO_RWG_SE, tl, tl, i32, i32, i32) + #ifndef CONFIG_USER_ONLY /* CP0 helpers */ DEF_HELPER_1(mfc0_mvpcontrol, tl, env) diff --git a/target/mips/mips-defs.h b/target/mips/mips-defs.h index d239069975..c8e99791ad 100644 --- a/target/mips/mips-defs.h +++ b/target/mips/mips-defs.h @@ -39,6 +39,7 @@ #define ISA_MIPS64R5 0x00001000 #define ISA_MIPS32R6 0x00002000 #define ISA_MIPS64R6 0x00004000 +#define ISA_NANOMIPS32 0x00008000 /* MIPS ASEs. */ #define ASE_MIPS16 0x00010000 @@ -87,6 +88,9 @@ #define CPU_MIPS32R6 (CPU_MIPS32R5 | ISA_MIPS32R6) #define CPU_MIPS64R6 (CPU_MIPS64R5 | CPU_MIPS32R6 | ISA_MIPS64R6) +/* Wave Computing: "nanoMIPS" */ +#define CPU_NANOMIPS32 (CPU_MIPS32R6 | ISA_NANOMIPS32) + /* Strictly follow the architecture standard: - Disallow "special" instruction handling for PMON/SPIM. Note that we still maintain Count/Compare to match the host clock. */ diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c index 0b2663b73a..c148b310cd 100644 --- a/target/mips/op_helper.c +++ b/target/mips/op_helper.c @@ -249,6 +249,100 @@ target_ulong helper_bitswap(target_ulong rt) return (int32_t)bitswap(rt); } +target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx, + uint32_t stripe) +{ + int i; + uint64_t tmp0 = ((uint64_t)rs) << 32 | ((uint64_t)rs & 0xffffffff); + uint64_t tmp1 = tmp0; + for (i = 0; i <= 46; i++) { + int s; + if (i & 0x8) { + s = shift; + } else { + s = shiftx; + } + + if (stripe != 0 && !(i & 0x4)) { + s = ~s; + } + if (s & 0x10) { + if (tmp0 & (1LL << (i + 16))) { + tmp1 |= 1LL << i; + } else { + tmp1 &= ~(1LL << i); + } + } + } + + uint64_t tmp2 = tmp1; + for (i = 0; i <= 38; i++) { + int s; + if (i & 0x4) { + s = shift; + } else { + s = shiftx; + } + + if (s & 0x8) { + if (tmp1 & (1LL << (i + 8))) { + tmp2 |= 1LL << i; + } else { + tmp2 &= ~(1LL << i); + } + } + } + + uint64_t tmp3 = tmp2; + for (i = 0; i <= 34; i++) { + int s; + if (i & 0x2) { + s = shift; + } else { + s = shiftx; + } + if (s & 0x4) { + if (tmp2 & (1LL << (i + 4))) { + tmp3 |= 1LL << i; + } else { + tmp3 &= ~(1LL << i); + } + } + } + + uint64_t tmp4 = tmp3; + for (i = 0; i <= 32; i++) { + int s; + if (i & 0x1) { + s = shift; + } else { + s = shiftx; + } + if (s & 0x2) { + if (tmp3 & (1LL << (i + 2))) { + tmp4 |= 1LL << i; + } else { + tmp4 &= ~(1LL << i); + } + } + } + + uint64_t tmp5 = tmp4; + for (i = 0; i <= 31; i++) { + int s; + s = shift; + if (s & 0x1) { + if (tmp4 & (1LL << (i + 1))) { + tmp5 |= 1LL << i; + } else { + tmp5 &= ~(1LL << i); + } + } + } + + return (int64_t)(int32_t)(uint32_t)tmp5; +} + #ifndef CONFIG_USER_ONLY static inline hwaddr do_translate_address(CPUMIPSState *env, @@ -2333,10 +2427,12 @@ void helper_eretnc(CPUMIPSState *env) void helper_deret(CPUMIPSState *env) { debug_pre_eret(env); - set_pc(env, env->CP0_DEPC); env->hflags &= ~MIPS_HFLAG_DM; compute_hflags(env); + + set_pc(env, env->CP0_DEPC); + debug_post_eret(env); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/mips/translate.c b/target/mips/translate.c index bdd880bb77..ab16cdb911 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -1449,6 +1449,8 @@ typedef struct DisasContext { uint32_t opcode; int insn_flags; int32_t CP0_Config1; + int32_t CP0_Config3; + int32_t CP0_Config5; /* Routine used to access memory */ int mem_idx; TCGMemOp default_tcg_memop_mask; @@ -1746,6 +1748,18 @@ static inline void gen_op_addr_add (DisasContext *ctx, TCGv ret, TCGv arg0, TCGv #endif } +static inline void gen_op_addr_addi(DisasContext *ctx, TCGv ret, TCGv base, + target_long ofs) +{ + tcg_gen_addi_tl(ret, base, ofs); + +#if defined(TARGET_MIPS64) + if (ctx->hflags & MIPS_HFLAG_AWRAP) { + tcg_gen_ext32s_i64(ret, ret); + } +#endif +} + /* Addresses computation (translation time) */ static target_long addr_add(DisasContext *ctx, target_long base, target_long offset) @@ -1902,6 +1916,59 @@ static inline void check_mvh(DisasContext *ctx) } #endif +/* + * This code generates a "reserved instruction" exception if the + * Config5 XNP bit is set. + */ +static inline void check_xnp(DisasContext *ctx) +{ + if (unlikely(ctx->CP0_Config5 & (1 << CP0C5_XNP))) { + generate_exception_end(ctx, EXCP_RI); + } +} + +/* + * This code generates a "reserved instruction" exception if the + * Config3 MT bit is NOT set. + */ +static inline void check_mt(DisasContext *ctx) +{ + if (unlikely(!(ctx->CP0_Config3 & (1 << CP0C3_MT)))) { + generate_exception_end(ctx, EXCP_RI); + } +} + +#ifndef CONFIG_USER_ONLY +/* + * This code generates a "coprocessor unusable" exception if CP0 is not + * available, and, if that is not the case, generates a "reserved instruction" + * exception if the Config5 MT bit is NOT set. This is needed for availability + * control of some of MT ASE instructions. + */ +static inline void check_cp0_mt(DisasContext *ctx) +{ + if (unlikely(!(ctx->hflags & MIPS_HFLAG_CP0))) { + generate_exception_err(ctx, EXCP_CpU, 0); + } else { + if (unlikely(!(ctx->CP0_Config3 & (1 << CP0C3_MT)))) { + generate_exception_err(ctx, EXCP_RI, 0); + } + } +} +#endif + +/* + * This code generates a "reserved instruction" exception if the + * Config5 NMS bit is set. + */ +static inline void check_nms(DisasContext *ctx) +{ + if (unlikely(ctx->CP0_Config5 & (1 << CP0C5_NMS))) { + generate_exception_end(ctx, EXCP_RI); + } +} + + /* Define small wrappers for gen_load_fpr* so that we have a uniform calling interface for 32 and 64-bit FPRs. No sense in changing all callers for gen_load_fpr32 when we need the CTX parameter for @@ -2348,6 +2415,31 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, tcg_temp_free(t0); } +static void gen_llwp(DisasContext *ctx, uint32_t base, int16_t offset, + uint32_t reg1, uint32_t reg2) +{ + TCGv taddr = tcg_temp_new(); + TCGv_i64 tval = tcg_temp_new_i64(); + TCGv tmp1 = tcg_temp_new(); + TCGv tmp2 = tcg_temp_new(); + + gen_base_offset_addr(ctx, taddr, base, offset); + tcg_gen_qemu_ld64(tval, taddr, ctx->mem_idx); +#ifdef TARGET_WORDS_BIGENDIAN + tcg_gen_extr_i64_tl(tmp2, tmp1, tval); +#else + tcg_gen_extr_i64_tl(tmp1, tmp2, tval); +#endif + gen_store_gpr(tmp1, reg1); + tcg_temp_free(tmp1); + gen_store_gpr(tmp2, reg2); + tcg_temp_free(tmp2); + tcg_gen_st_i64(tval, cpu_env, offsetof(CPUMIPSState, llval_wp)); + tcg_temp_free_i64(tval); + tcg_gen_st_tl(taddr, cpu_env, offsetof(CPUMIPSState, lladdr)); + tcg_temp_free(taddr); +} + /* Store */ static void gen_st (DisasContext *ctx, uint32_t opc, int rt, int base, int offset) @@ -2444,6 +2536,51 @@ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, tcg_temp_free(t0); } +static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset, + uint32_t reg1, uint32_t reg2) +{ + TCGv taddr = tcg_temp_local_new(); + TCGv lladdr = tcg_temp_local_new(); + TCGv_i64 tval = tcg_temp_new_i64(); + TCGv_i64 llval = tcg_temp_new_i64(); + TCGv_i64 val = tcg_temp_new_i64(); + TCGv tmp1 = tcg_temp_new(); + TCGv tmp2 = tcg_temp_new(); + TCGLabel *lab_fail = gen_new_label(); + TCGLabel *lab_done = gen_new_label(); + + gen_base_offset_addr(ctx, taddr, base, offset); + + tcg_gen_ld_tl(lladdr, cpu_env, offsetof(CPUMIPSState, lladdr)); + tcg_gen_brcond_tl(TCG_COND_NE, taddr, lladdr, lab_fail); + + gen_load_gpr(tmp1, reg1); + gen_load_gpr(tmp2, reg2); + +#ifdef TARGET_WORDS_BIGENDIAN + tcg_gen_concat_tl_i64(tval, tmp2, tmp1); +#else + tcg_gen_concat_tl_i64(tval, tmp1, tmp2); +#endif + + tcg_gen_ld_i64(llval, cpu_env, offsetof(CPUMIPSState, llval_wp)); + tcg_gen_atomic_cmpxchg_i64(val, taddr, llval, tval, + ctx->mem_idx, MO_64); + if (reg1 != 0) { + tcg_gen_movi_tl(cpu_gpr[reg1], 1); + } + tcg_gen_brcond_i64(TCG_COND_EQ, val, llval, lab_done); + + gen_set_label(lab_fail); + + if (reg1 != 0) { + tcg_gen_movi_tl(cpu_gpr[reg1], 0); + } + gen_set_label(lab_done); + tcg_gen_movi_tl(lladdr, -1); + tcg_gen_st_tl(lladdr, cpu_env, offsetof(CPUMIPSState, lladdr)); +} + /* Load and store */ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, TCGv t0) @@ -4564,6 +4701,128 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); } + +/* nanoMIPS Branches */ +static void gen_compute_branch_nm(DisasContext *ctx, uint32_t opc, + int insn_bytes, + int rs, int rt, int32_t offset) +{ + target_ulong btgt = -1; + int bcond_compute = 0; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* Load needed operands */ + switch (opc) { + case OPC_BEQ: + case OPC_BNE: + /* Compare two registers */ + if (rs != rt) { + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + } + btgt = ctx->base.pc_next + insn_bytes + offset; + break; + case OPC_BGEZAL: + /* Compare to zero */ + if (rs != 0) { + gen_load_gpr(t0, rs); + bcond_compute = 1; + } + btgt = ctx->base.pc_next + insn_bytes + offset; + break; + case OPC_BPOSGE32: + tcg_gen_andi_tl(t0, cpu_dspctrl, 0x3F); + bcond_compute = 1; + btgt = ctx->base.pc_next + insn_bytes + offset; + break; + case OPC_JR: + case OPC_JALR: + /* Jump to register */ + if (offset != 0 && offset != 16) { + /* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the + others are reserved. */ + MIPS_INVAL("jump hint"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + gen_load_gpr(btarget, rs); + break; + default: + MIPS_INVAL("branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + if (bcond_compute == 0) { + /* No condition to be computed */ + switch (opc) { + case OPC_BEQ: /* rx == rx */ + /* Always take */ + ctx->hflags |= MIPS_HFLAG_B; + break; + case OPC_BGEZAL: /* 0 >= 0 */ + /* Always take and link */ + tcg_gen_movi_tl(cpu_gpr[31], + ctx->base.pc_next + insn_bytes); + ctx->hflags |= MIPS_HFLAG_B; + break; + case OPC_BNE: /* rx != rx */ + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 8); + /* Skip the instruction in the delay slot */ + ctx->base.pc_next += 4; + goto out; + case OPC_JR: + ctx->hflags |= MIPS_HFLAG_BR; + break; + case OPC_JALR: + if (rt > 0) { + tcg_gen_movi_tl(cpu_gpr[rt], + ctx->base.pc_next + insn_bytes); + } + ctx->hflags |= MIPS_HFLAG_BR; + break; + default: + MIPS_INVAL("branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + } else { + switch (opc) { + case OPC_BEQ: + tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1); + goto not_likely; + case OPC_BNE: + tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1); + goto not_likely; + case OPC_BGEZAL: + tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); + tcg_gen_movi_tl(cpu_gpr[31], + ctx->base.pc_next + insn_bytes); + goto not_likely; + case OPC_BPOSGE32: + tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 32); + not_likely: + ctx->hflags |= MIPS_HFLAG_BC; + break; + default: + MIPS_INVAL("conditional branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + } + + ctx->btarget = btgt; + + out: + if (insn_bytes == 2) { + ctx->hflags |= MIPS_HFLAG_B16; + } + tcg_temp_free(t0); + tcg_temp_free(t1); +} + + /* special3 bitfield operations */ static void gen_bitops (DisasContext *ctx, uint32_t opc, int rt, int rs, int lsb, int msb) @@ -4736,8 +4995,8 @@ static void gen_lsa(DisasContext *ctx, int opc, int rd, int rs, int rt, return; } -static void gen_align(DisasContext *ctx, int opc, int rd, int rs, int rt, - int bp) +static void gen_align_bits(DisasContext *ctx, int wordsz, int rd, int rs, + int rt, int bits) { TCGv t0; if (rd == 0) { @@ -4745,35 +5004,40 @@ static void gen_align(DisasContext *ctx, int opc, int rd, int rs, int rt, return; } t0 = tcg_temp_new(); - gen_load_gpr(t0, rt); - if (bp == 0) { - switch (opc) { - case OPC_ALIGN: + if (bits == 0 || bits == wordsz) { + if (bits == 0) { + gen_load_gpr(t0, rt); + } else { + gen_load_gpr(t0, rs); + } + switch (wordsz) { + case 32: tcg_gen_ext32s_tl(cpu_gpr[rd], t0); break; #if defined(TARGET_MIPS64) - case OPC_DALIGN: + case 64: tcg_gen_mov_tl(cpu_gpr[rd], t0); break; #endif } } else { TCGv t1 = tcg_temp_new(); + gen_load_gpr(t0, rt); gen_load_gpr(t1, rs); - switch (opc) { - case OPC_ALIGN: + switch (wordsz) { + case 32: { TCGv_i64 t2 = tcg_temp_new_i64(); tcg_gen_concat_tl_i64(t2, t1, t0); - tcg_gen_shri_i64(t2, t2, 8 * (4 - bp)); + tcg_gen_shri_i64(t2, t2, 32 - bits); gen_move_low32(cpu_gpr[rd], t2); tcg_temp_free_i64(t2); } break; #if defined(TARGET_MIPS64) - case OPC_DALIGN: - tcg_gen_shli_tl(t0, t0, 8 * bp); - tcg_gen_shri_tl(t1, t1, 8 * (8 - bp)); + case 64: + tcg_gen_shli_tl(t0, t0, bits); + tcg_gen_shri_tl(t1, t1, 64 - bits); tcg_gen_or_tl(cpu_gpr[rd], t1, t0); break; #endif @@ -4784,6 +5048,18 @@ static void gen_align(DisasContext *ctx, int opc, int rd, int rs, int rt, tcg_temp_free(t0); } +static void gen_align(DisasContext *ctx, int wordsz, int rd, int rs, int rt, + int bp) +{ + gen_align_bits(ctx, wordsz, rd, rs, rt, bp * 8); +} + +static void gen_ext(DisasContext *ctx, int wordsz, int rd, int rs, int rt, + int shift) +{ + gen_align_bits(ctx, wordsz, rd, rs, rt, wordsz - shift); +} + static void gen_bitswap(DisasContext *ctx, int opc, int rd, int rt) { TCGv t0; @@ -8360,7 +8636,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, opn = "mthc0"; break; case OPC_MFTR: - check_insn(ctx, ASE_MT); + check_cp0_enabled(ctx); if (rd == 0) { /* Treat as NOP. */ return; @@ -8370,7 +8646,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, opn = "mftr"; break; case OPC_MTTR: - check_insn(ctx, ASE_MT); + check_cp0_enabled(ctx); gen_mttr(env, ctx, rd, rt, (ctx->opcode >> 5) & 1, ctx->opcode & 0x7, (ctx->opcode >> 4) & 1); opn = "mttr"; @@ -14276,8 +14552,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) break; case ALIGN: check_insn(ctx, ISA_MIPS32R6); - gen_align(ctx, OPC_ALIGN, rd, rs, rt, - extract32(ctx->opcode, 9, 2)); + gen_align(ctx, 32, rd, rs, rt, extract32(ctx->opcode, 9, 2)); break; case EXT: gen_bitops(ctx, OPC_EXT, rt, rs, rr, rd); @@ -15701,6 +15976,4569 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) return 2; } +/* + * + * nanoMIPS opcodes + * + */ + +/* MAJOR, P16, and P32 pools opcodes */ +enum { + NM_P_ADDIU = 0x00, + NM_ADDIUPC = 0x01, + NM_MOVE_BALC = 0x02, + NM_P16_MV = 0x04, + NM_LW16 = 0x05, + NM_BC16 = 0x06, + NM_P16_SR = 0x07, + + NM_POOL32A = 0x08, + NM_P_BAL = 0x0a, + NM_P16_SHIFT = 0x0c, + NM_LWSP16 = 0x0d, + NM_BALC16 = 0x0e, + NM_P16_4X4 = 0x0f, + + NM_P_GP_W = 0x10, + NM_P_GP_BH = 0x11, + NM_P_J = 0x12, + NM_P16C = 0x14, + NM_LWGP16 = 0x15, + NM_P16_LB = 0x17, + + NM_P48I = 0x18, + NM_P16_A1 = 0x1c, + NM_LW4X4 = 0x1d, + NM_P16_LH = 0x1f, + + NM_P_U12 = 0x20, + NM_P_LS_U12 = 0x21, + NM_P_BR1 = 0x22, + NM_P16_A2 = 0x24, + NM_SW16 = 0x25, + NM_BEQZC16 = 0x26, + + NM_POOL32F = 0x28, + NM_P_LS_S9 = 0x29, + NM_P_BR2 = 0x2a, + + NM_P16_ADDU = 0x2c, + NM_SWSP16 = 0x2d, + NM_BNEZC16 = 0x2e, + NM_MOVEP = 0x2f, + + NM_POOL32S = 0x30, + NM_P_BRI = 0x32, + NM_LI16 = 0x34, + NM_SWGP16 = 0x35, + NM_P16_BR = 0x36, + + NM_P_LUI = 0x38, + NM_ANDI16 = 0x3c, + NM_SW4X4 = 0x3d, + NM_MOVEPREV = 0x3f, +}; + +/* POOL32A instruction pool */ +enum { + NM_POOL32A0 = 0x00, + NM_SPECIAL2 = 0x01, + NM_COP2_1 = 0x02, + NM_UDI = 0x03, + NM_POOL32A5 = 0x05, + NM_POOL32A7 = 0x07, +}; + +/* P.GP.W instruction pool */ +enum { + NM_ADDIUGP_W = 0x00, + NM_LWGP = 0x02, + NM_SWGP = 0x03, +}; + +/* P48I instruction pool */ +enum { + NM_LI48 = 0x00, + NM_ADDIU48 = 0x01, + NM_ADDIUGP48 = 0x02, + NM_ADDIUPC48 = 0x03, + NM_LWPC48 = 0x0b, + NM_SWPC48 = 0x0f, +}; + +/* P.U12 instruction pool */ +enum { + NM_ORI = 0x00, + NM_XORI = 0x01, + NM_ANDI = 0x02, + NM_P_SR = 0x03, + NM_SLTI = 0x04, + NM_SLTIU = 0x05, + NM_SEQI = 0x06, + NM_ADDIUNEG = 0x08, + NM_P_SHIFT = 0x0c, + NM_P_ROTX = 0x0d, + NM_P_INS = 0x0e, + NM_P_EXT = 0x0f, +}; + +/* POOL32F instruction pool */ +enum { + NM_POOL32F_0 = 0x00, + NM_POOL32F_3 = 0x03, + NM_POOL32F_5 = 0x05, +}; + +/* POOL32S instruction pool */ +enum { + NM_POOL32S_0 = 0x00, + NM_POOL32S_4 = 0x04, +}; + +/* P.LUI instruction pool */ +enum { + NM_LUI = 0x00, + NM_ALUIPC = 0x01, +}; + +/* P.GP.BH instruction pool */ +enum { + NM_LBGP = 0x00, + NM_SBGP = 0x01, + NM_LBUGP = 0x02, + NM_ADDIUGP_B = 0x03, + NM_P_GP_LH = 0x04, + NM_P_GP_SH = 0x05, + NM_P_GP_CP1 = 0x06, +}; + +/* P.LS.U12 instruction pool */ +enum { + NM_LB = 0x00, + NM_SB = 0x01, + NM_LBU = 0x02, + NM_P_PREFU12 = 0x03, + NM_LH = 0x04, + NM_SH = 0x05, + NM_LHU = 0x06, + NM_LWU = 0x07, + NM_LW = 0x08, + NM_SW = 0x09, + NM_LWC1 = 0x0a, + NM_SWC1 = 0x0b, + NM_LDC1 = 0x0e, + NM_SDC1 = 0x0f, +}; + +/* P.LS.S9 instruction pool */ +enum { + NM_P_LS_S0 = 0x00, + NM_P_LS_S1 = 0x01, + NM_P_LS_E0 = 0x02, + NM_P_LS_WM = 0x04, + NM_P_LS_UAWM = 0x05, +}; + +/* P.BAL instruction pool */ +enum { + NM_BC = 0x00, + NM_BALC = 0x01, +}; + +/* P.J instruction pool */ +enum { + NM_JALRC = 0x00, + NM_JALRC_HB = 0x01, + NM_P_BALRSC = 0x08, +}; + +/* P.BR1 instruction pool */ +enum { + NM_BEQC = 0x00, + NM_P_BR3A = 0x01, + NM_BGEC = 0x02, + NM_BGEUC = 0x03, +}; + +/* P.BR2 instruction pool */ +enum { + NM_BNEC = 0x00, + NM_BLTC = 0x02, + NM_BLTUC = 0x03, +}; + +/* P.BRI instruction pool */ +enum { + NM_BEQIC = 0x00, + NM_BBEQZC = 0x01, + NM_BGEIC = 0x02, + NM_BGEIUC = 0x03, + NM_BNEIC = 0x04, + NM_BBNEZC = 0x05, + NM_BLTIC = 0x06, + NM_BLTIUC = 0x07, +}; + +/* P16.SHIFT instruction pool */ +enum { + NM_SLL16 = 0x00, + NM_SRL16 = 0x01, +}; + +/* POOL16C instruction pool */ +enum { + NM_POOL16C_0 = 0x00, + NM_LWXS16 = 0x01, +}; + +/* P16.A1 instruction pool */ +enum { + NM_ADDIUR1SP = 0x01, +}; + +/* P16.A2 instruction pool */ +enum { + NM_ADDIUR2 = 0x00, + NM_P_ADDIURS5 = 0x01, +}; + +/* P16.ADDU instruction pool */ +enum { + NM_ADDU16 = 0x00, + NM_SUBU16 = 0x01, +}; + +/* P16.SR instruction pool */ +enum { + NM_SAVE16 = 0x00, + NM_RESTORE_JRC16 = 0x01, +}; + +/* P16.4X4 instruction pool */ +enum { + NM_ADDU4X4 = 0x00, + NM_MUL4X4 = 0x01, +}; + +/* P16.LB instruction pool */ +enum { + NM_LB16 = 0x00, + NM_SB16 = 0x01, + NM_LBU16 = 0x02, +}; + +/* P16.LH instruction pool */ +enum { + NM_LH16 = 0x00, + NM_SH16 = 0x01, + NM_LHU16 = 0x02, +}; + +/* P.RI instruction pool */ +enum { + NM_SIGRIE = 0x00, + NM_P_SYSCALL = 0x01, + NM_BREAK = 0x02, + NM_SDBBP = 0x03, +}; + +/* POOL32A0 instruction pool */ +enum { + NM_P_TRAP = 0x00, + NM_SEB = 0x01, + NM_SLLV = 0x02, + NM_MUL = 0x03, + NM_MFC0 = 0x06, + NM_MFHC0 = 0x07, + NM_SEH = 0x09, + NM_SRLV = 0x0a, + NM_MUH = 0x0b, + NM_MTC0 = 0x0e, + NM_MTHC0 = 0x0f, + NM_SRAV = 0x12, + NM_MULU = 0x13, + NM_ROTRV = 0x1a, + NM_MUHU = 0x1b, + NM_ADD = 0x22, + NM_DIV = 0x23, + NM_ADDU = 0x2a, + NM_MOD = 0x2b, + NM_SUB = 0x32, + NM_DIVU = 0x33, + NM_RDHWR = 0x38, + NM_SUBU = 0x3a, + NM_MODU = 0x3b, + NM_P_CMOVE = 0x42, + NM_FORK = 0x45, + NM_MFTR = 0x46, + NM_MFHTR = 0x47, + NM_AND = 0x4a, + NM_YIELD = 0x4d, + NM_MTTR = 0x4e, + NM_MTHTR = 0x4f, + NM_OR = 0x52, + NM_D_E_MT_VPE = 0x56, + NM_NOR = 0x5a, + NM_XOR = 0x62, + NM_SLT = 0x6a, + NM_P_SLTU = 0x72, + NM_SOV = 0x7a, +}; + +/* POOL32A5 instruction pool */ +enum { + NM_CMP_EQ_PH = 0x00, + NM_CMP_LT_PH = 0x08, + NM_CMP_LE_PH = 0x10, + NM_CMPGU_EQ_QB = 0x18, + NM_CMPGU_LT_QB = 0x20, + NM_CMPGU_LE_QB = 0x28, + NM_CMPGDU_EQ_QB = 0x30, + NM_CMPGDU_LT_QB = 0x38, + NM_CMPGDU_LE_QB = 0x40, + NM_CMPU_EQ_QB = 0x48, + NM_CMPU_LT_QB = 0x50, + NM_CMPU_LE_QB = 0x58, + NM_ADDQ_S_W = 0x60, + NM_SUBQ_S_W = 0x68, + NM_ADDSC = 0x70, + NM_ADDWC = 0x78, + + NM_ADDQ_S_PH = 0x01, + NM_ADDQH_R_PH = 0x09, + NM_ADDQH_R_W = 0x11, + NM_ADDU_S_QB = 0x19, + NM_ADDU_S_PH = 0x21, + NM_ADDUH_R_QB = 0x29, + NM_SHRAV_R_PH = 0x31, + NM_SHRAV_R_QB = 0x39, + NM_SUBQ_S_PH = 0x41, + NM_SUBQH_R_PH = 0x49, + NM_SUBQH_R_W = 0x51, + NM_SUBU_S_QB = 0x59, + NM_SUBU_S_PH = 0x61, + NM_SUBUH_R_QB = 0x69, + NM_SHLLV_S_PH = 0x71, + NM_PRECR_SRA_R_PH_W = 0x79, + + NM_MULEU_S_PH_QBL = 0x12, + NM_MULEU_S_PH_QBR = 0x1a, + NM_MULQ_RS_PH = 0x22, + NM_MULQ_S_PH = 0x2a, + NM_MULQ_RS_W = 0x32, + NM_MULQ_S_W = 0x3a, + NM_APPEND = 0x42, + NM_MODSUB = 0x52, + NM_SHRAV_R_W = 0x5a, + NM_SHRLV_PH = 0x62, + NM_SHRLV_QB = 0x6a, + NM_SHLLV_QB = 0x72, + NM_SHLLV_S_W = 0x7a, + + NM_SHILO = 0x03, + + NM_MULEQ_S_W_PHL = 0x04, + NM_MULEQ_S_W_PHR = 0x0c, + + NM_MUL_S_PH = 0x05, + NM_PRECR_QB_PH = 0x0d, + NM_PRECRQ_QB_PH = 0x15, + NM_PRECRQ_PH_W = 0x1d, + NM_PRECRQ_RS_PH_W = 0x25, + NM_PRECRQU_S_QB_PH = 0x2d, + NM_PACKRL_PH = 0x35, + NM_PICK_QB = 0x3d, + NM_PICK_PH = 0x45, + + NM_SHRA_R_W = 0x5e, + NM_SHRA_R_PH = 0x66, + NM_SHLL_S_PH = 0x76, + NM_SHLL_S_W = 0x7e, + + NM_REPL_PH = 0x07 +}; + +/* POOL32A7 instruction pool */ +enum { + NM_P_LSX = 0x00, + NM_LSA = 0x01, + NM_EXTW = 0x03, + NM_POOL32AXF = 0x07, +}; + +/* P.SR instruction pool */ +enum { + NM_PP_SR = 0x00, + NM_P_SR_F = 0x01, +}; + +/* P.SHIFT instruction pool */ +enum { + NM_P_SLL = 0x00, + NM_SRL = 0x02, + NM_SRA = 0x04, + NM_ROTR = 0x06, +}; + +/* P.ROTX instruction pool */ +enum { + NM_ROTX = 0x00, +}; + +/* P.INS instruction pool */ +enum { + NM_INS = 0x00, +}; + +/* P.EXT instruction pool */ +enum { + NM_EXT = 0x00, +}; + +/* POOL32F_0 (fmt) instruction pool */ +enum { + NM_RINT_S = 0x04, + NM_RINT_D = 0x44, + NM_ADD_S = 0x06, + NM_SELEQZ_S = 0x07, + NM_SELEQZ_D = 0x47, + NM_CLASS_S = 0x0c, + NM_CLASS_D = 0x4c, + NM_SUB_S = 0x0e, + NM_SELNEZ_S = 0x0f, + NM_SELNEZ_D = 0x4f, + NM_MUL_S = 0x16, + NM_SEL_S = 0x17, + NM_SEL_D = 0x57, + NM_DIV_S = 0x1e, + NM_ADD_D = 0x26, + NM_SUB_D = 0x2e, + NM_MUL_D = 0x36, + NM_MADDF_S = 0x37, + NM_MADDF_D = 0x77, + NM_DIV_D = 0x3e, + NM_MSUBF_S = 0x3f, + NM_MSUBF_D = 0x7f, +}; + +/* POOL32F_3 instruction pool */ +enum { + NM_MIN_FMT = 0x00, + NM_MAX_FMT = 0x01, + NM_MINA_FMT = 0x04, + NM_MAXA_FMT = 0x05, + NM_POOL32FXF = 0x07, +}; + +/* POOL32F_5 instruction pool */ +enum { + NM_CMP_CONDN_S = 0x00, + NM_CMP_CONDN_D = 0x02, +}; + +/* P.GP.LH instruction pool */ +enum { + NM_LHGP = 0x00, + NM_LHUGP = 0x01, +}; + +/* P.GP.SH instruction pool */ +enum { + NM_SHGP = 0x00, +}; + +/* P.GP.CP1 instruction pool */ +enum { + NM_LWC1GP = 0x00, + NM_SWC1GP = 0x01, + NM_LDC1GP = 0x02, + NM_SDC1GP = 0x03, +}; + +/* P.LS.S0 instruction pool */ +enum { + NM_LBS9 = 0x00, + NM_LHS9 = 0x04, + NM_LWS9 = 0x08, + NM_LDS9 = 0x0c, + + NM_SBS9 = 0x01, + NM_SHS9 = 0x05, + NM_SWS9 = 0x09, + NM_SDS9 = 0x0d, + + NM_LBUS9 = 0x02, + NM_LHUS9 = 0x06, + NM_LWC1S9 = 0x0a, + NM_LDC1S9 = 0x0e, + + NM_P_PREFS9 = 0x03, + NM_LWUS9 = 0x07, + NM_SWC1S9 = 0x0b, + NM_SDC1S9 = 0x0f, +}; + +/* P.LS.S1 instruction pool */ +enum { + NM_ASET_ACLR = 0x02, + NM_UALH = 0x04, + NM_UASH = 0x05, + NM_CACHE = 0x07, + NM_P_LL = 0x0a, + NM_P_SC = 0x0b, +}; + +/* P.LS.WM instruction pool */ +enum { + NM_LWM = 0x00, + NM_SWM = 0x01, +}; + +/* P.LS.UAWM instruction pool */ +enum { + NM_UALWM = 0x00, + NM_UASWM = 0x01, +}; + +/* P.BR3A instruction pool */ +enum { + NM_BC1EQZC = 0x00, + NM_BC1NEZC = 0x01, + NM_BC2EQZC = 0x02, + NM_BC2NEZC = 0x03, + NM_BPOSGE32C = 0x04, +}; + +/* P16.RI instruction pool */ +enum { + NM_P16_SYSCALL = 0x01, + NM_BREAK16 = 0x02, + NM_SDBBP16 = 0x03, +}; + +/* POOL16C_0 instruction pool */ +enum { + NM_POOL16C_00 = 0x00, +}; + +/* P16.JRC instruction pool */ +enum { + NM_JRC = 0x00, + NM_JALRC16 = 0x01, +}; + +/* P.SYSCALL instruction pool */ +enum { + NM_SYSCALL = 0x00, + NM_HYPCALL = 0x01, +}; + +/* P.TRAP instruction pool */ +enum { + NM_TEQ = 0x00, + NM_TNE = 0x01, +}; + +/* P.CMOVE instruction pool */ +enum { + NM_MOVZ = 0x00, + NM_MOVN = 0x01, +}; + +/* POOL32Axf instruction pool */ +enum { + NM_POOL32AXF_1 = 0x01, + NM_POOL32AXF_2 = 0x02, + NM_POOL32AXF_4 = 0x04, + NM_POOL32AXF_5 = 0x05, + NM_POOL32AXF_7 = 0x07, +}; + +/* POOL32Axf_1 instruction pool */ +enum { + NM_POOL32AXF_1_0 = 0x00, + NM_POOL32AXF_1_1 = 0x01, + NM_POOL32AXF_1_3 = 0x03, + NM_POOL32AXF_1_4 = 0x04, + NM_POOL32AXF_1_5 = 0x05, + NM_POOL32AXF_1_7 = 0x07, +}; + +/* POOL32Axf_2 instruction pool */ +enum { + NM_POOL32AXF_2_0_7 = 0x00, + NM_POOL32AXF_2_8_15 = 0x01, + NM_POOL32AXF_2_16_23 = 0x02, + NM_POOL32AXF_2_24_31 = 0x03, +}; + +/* POOL32Axf_7 instruction pool */ +enum { + NM_SHRA_R_QB = 0x0, + NM_SHRL_PH = 0x1, + NM_REPL_QB = 0x2, +}; + +/* POOL32Axf_1_0 instruction pool */ +enum { + NM_MFHI = 0x0, + NM_MFLO = 0x1, + NM_MTHI = 0x2, + NM_MTLO = 0x3, +}; + +/* POOL32Axf_1_1 instruction pool */ +enum { + NM_MTHLIP = 0x0, + NM_SHILOV = 0x1, +}; + +/* POOL32Axf_1_3 instruction pool */ +enum { + NM_RDDSP = 0x0, + NM_WRDSP = 0x1, + NM_EXTP = 0x2, + NM_EXTPDP = 0x3, +}; + +/* POOL32Axf_1_4 instruction pool */ +enum { + NM_SHLL_QB = 0x0, + NM_SHRL_QB = 0x1, +}; + +/* POOL32Axf_1_5 instruction pool */ +enum { + NM_MAQ_S_W_PHR = 0x0, + NM_MAQ_S_W_PHL = 0x1, + NM_MAQ_SA_W_PHR = 0x2, + NM_MAQ_SA_W_PHL = 0x3, +}; + +/* POOL32Axf_1_7 instruction pool */ +enum { + NM_EXTR_W = 0x0, + NM_EXTR_R_W = 0x1, + NM_EXTR_RS_W = 0x2, + NM_EXTR_S_H = 0x3, +}; + +/* POOL32Axf_2_0_7 instruction pool */ +enum { + NM_DPA_W_PH = 0x0, + NM_DPAQ_S_W_PH = 0x1, + NM_DPS_W_PH = 0x2, + NM_DPSQ_S_W_PH = 0x3, + NM_BALIGN = 0x4, + NM_MADD = 0x5, + NM_MULT = 0x6, + NM_EXTRV_W = 0x7, +}; + +/* POOL32Axf_2_8_15 instruction pool */ +enum { + NM_DPAX_W_PH = 0x0, + NM_DPAQ_SA_L_W = 0x1, + NM_DPSX_W_PH = 0x2, + NM_DPSQ_SA_L_W = 0x3, + NM_MADDU = 0x5, + NM_MULTU = 0x6, + NM_EXTRV_R_W = 0x7, +}; + +/* POOL32Axf_2_16_23 instruction pool */ +enum { + NM_DPAU_H_QBL = 0x0, + NM_DPAQX_S_W_PH = 0x1, + NM_DPSU_H_QBL = 0x2, + NM_DPSQX_S_W_PH = 0x3, + NM_EXTPV = 0x4, + NM_MSUB = 0x5, + NM_MULSA_W_PH = 0x6, + NM_EXTRV_RS_W = 0x7, +}; + +/* POOL32Axf_2_24_31 instruction pool */ +enum { + NM_DPAU_H_QBR = 0x0, + NM_DPAQX_SA_W_PH = 0x1, + NM_DPSU_H_QBR = 0x2, + NM_DPSQX_SA_W_PH = 0x3, + NM_EXTPDPV = 0x4, + NM_MSUBU = 0x5, + NM_MULSAQ_S_W_PH = 0x6, + NM_EXTRV_S_H = 0x7, +}; + +/* POOL32Axf_{4, 5} instruction pool */ +enum { + NM_CLO = 0x25, + NM_CLZ = 0x2d, + + NM_TLBP = 0x01, + NM_TLBR = 0x09, + NM_TLBWI = 0x11, + NM_TLBWR = 0x19, + NM_TLBINV = 0x03, + NM_TLBINVF = 0x0b, + NM_DI = 0x23, + NM_EI = 0x2b, + NM_RDPGPR = 0x70, + NM_WRPGPR = 0x78, + NM_WAIT = 0x61, + NM_DERET = 0x71, + NM_ERETX = 0x79, + + /* nanoMIPS DSP instructions */ + NM_ABSQ_S_QB = 0x00, + NM_ABSQ_S_PH = 0x08, + NM_ABSQ_S_W = 0x10, + NM_PRECEQ_W_PHL = 0x28, + NM_PRECEQ_W_PHR = 0x30, + NM_PRECEQU_PH_QBL = 0x38, + NM_PRECEQU_PH_QBR = 0x48, + NM_PRECEU_PH_QBL = 0x58, + NM_PRECEU_PH_QBR = 0x68, + NM_PRECEQU_PH_QBLA = 0x39, + NM_PRECEQU_PH_QBRA = 0x49, + NM_PRECEU_PH_QBLA = 0x59, + NM_PRECEU_PH_QBRA = 0x69, + NM_REPLV_PH = 0x01, + NM_REPLV_QB = 0x09, + NM_BITREV = 0x18, + NM_INSV = 0x20, + NM_RADDU_W_QB = 0x78, + + NM_BITSWAP = 0x05, + NM_WSBH = 0x3d, +}; + +/* PP.SR instruction pool */ +enum { + NM_SAVE = 0x00, + NM_RESTORE = 0x02, + NM_RESTORE_JRC = 0x03, +}; + +/* P.SR.F instruction pool */ +enum { + NM_SAVEF = 0x00, + NM_RESTOREF = 0x01, +}; + +/* P16.SYSCALL instruction pool */ +enum { + NM_SYSCALL16 = 0x00, + NM_HYPCALL16 = 0x01, +}; + +/* POOL16C_00 instruction pool */ +enum { + NM_NOT16 = 0x00, + NM_XOR16 = 0x01, + NM_AND16 = 0x02, + NM_OR16 = 0x03, +}; + +/* PP.LSX and PP.LSXS instruction pool */ +enum { + NM_LBX = 0x00, + NM_LHX = 0x04, + NM_LWX = 0x08, + NM_LDX = 0x0c, + + NM_SBX = 0x01, + NM_SHX = 0x05, + NM_SWX = 0x09, + NM_SDX = 0x0d, + + NM_LBUX = 0x02, + NM_LHUX = 0x06, + NM_LWC1X = 0x0a, + NM_LDC1X = 0x0e, + + NM_LWUX = 0x07, + NM_SWC1X = 0x0b, + NM_SDC1X = 0x0f, + + NM_LHXS = 0x04, + NM_LWXS = 0x08, + NM_LDXS = 0x0c, + + NM_SHXS = 0x05, + NM_SWXS = 0x09, + NM_SDXS = 0x0d, + + NM_LHUXS = 0x06, + NM_LWC1XS = 0x0a, + NM_LDC1XS = 0x0e, + + NM_LWUXS = 0x07, + NM_SWC1XS = 0x0b, + NM_SDC1XS = 0x0f, +}; + +/* ERETx instruction pool */ +enum { + NM_ERET = 0x00, + NM_ERETNC = 0x01, +}; + +/* POOL32FxF_{0, 1} insturction pool */ +enum { + NM_CFC1 = 0x40, + NM_CTC1 = 0x60, + NM_MFC1 = 0x80, + NM_MTC1 = 0xa0, + NM_MFHC1 = 0xc0, + NM_MTHC1 = 0xe0, + + NM_CVT_S_PL = 0x84, + NM_CVT_S_PU = 0xa4, + + NM_CVT_L_S = 0x004, + NM_CVT_L_D = 0x104, + NM_CVT_W_S = 0x024, + NM_CVT_W_D = 0x124, + + NM_RSQRT_S = 0x008, + NM_RSQRT_D = 0x108, + + NM_SQRT_S = 0x028, + NM_SQRT_D = 0x128, + + NM_RECIP_S = 0x048, + NM_RECIP_D = 0x148, + + NM_FLOOR_L_S = 0x00c, + NM_FLOOR_L_D = 0x10c, + + NM_FLOOR_W_S = 0x02c, + NM_FLOOR_W_D = 0x12c, + + NM_CEIL_L_S = 0x04c, + NM_CEIL_L_D = 0x14c, + NM_CEIL_W_S = 0x06c, + NM_CEIL_W_D = 0x16c, + NM_TRUNC_L_S = 0x08c, + NM_TRUNC_L_D = 0x18c, + NM_TRUNC_W_S = 0x0ac, + NM_TRUNC_W_D = 0x1ac, + NM_ROUND_L_S = 0x0cc, + NM_ROUND_L_D = 0x1cc, + NM_ROUND_W_S = 0x0ec, + NM_ROUND_W_D = 0x1ec, + + NM_MOV_S = 0x01, + NM_MOV_D = 0x81, + NM_ABS_S = 0x0d, + NM_ABS_D = 0x8d, + NM_NEG_S = 0x2d, + NM_NEG_D = 0xad, + NM_CVT_D_S = 0x04d, + NM_CVT_D_W = 0x0cd, + NM_CVT_D_L = 0x14d, + NM_CVT_S_D = 0x06d, + NM_CVT_S_W = 0x0ed, + NM_CVT_S_L = 0x16d, +}; + +/* P.LL instruction pool */ +enum { + NM_LL = 0x00, + NM_LLWP = 0x01, +}; + +/* P.SC instruction pool */ +enum { + NM_SC = 0x00, + NM_SCWP = 0x01, +}; + +/* P.DVP instruction pool */ +enum { + NM_DVP = 0x00, + NM_EVP = 0x01, +}; + + +/* + * + * nanoMIPS decoding engine + * + */ + + +/* extraction utilities */ + +#define NANOMIPS_EXTRACT_RD(op) ((op >> 7) & 0x7) +#define NANOMIPS_EXTRACT_RS(op) ((op >> 4) & 0x7) +#define NANOMIPS_EXTRACT_RS2(op) uMIPS_RS(op) +#define NANOMIPS_EXTRACT_RS1(op) ((op >> 1) & 0x7) +#define NANOMIPS_EXTRACT_RD5(op) ((op >> 5) & 0x1f) +#define NANOMIPS_EXTRACT_RS5(op) (op & 0x1f) + +/* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr3'). */ +static inline int decode_gpr_gpr3(int r) +{ + static const int map[] = { 16, 17, 18, 19, 4, 5, 6, 7 }; + + return map[r & 0x7]; +} + +/* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr3.src.store'). */ +static inline int decode_gpr_gpr3_src_store(int r) +{ + static const int map[] = { 0, 17, 18, 19, 4, 5, 6, 7 }; + + return map[r & 0x7]; +} + +/* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr4'). */ +static inline int decode_gpr_gpr4(int r) +{ + static const int map[] = { 8, 9, 10, 11, 4, 5, 6, 7, + 16, 17, 18, 19, 20, 21, 22, 23 }; + + return map[r & 0xf]; +} + +/* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr4.zero'). */ +static inline int decode_gpr_gpr4_zero(int r) +{ + static const int map[] = { 8, 9, 10, 0, 4, 5, 6, 7, + 16, 17, 18, 19, 20, 21, 22, 23 }; + + return map[r & 0xf]; +} + + +/* extraction utilities */ + +#define NANOMIPS_EXTRACT_RD(op) ((op >> 7) & 0x7) +#define NANOMIPS_EXTRACT_RS(op) ((op >> 4) & 0x7) +#define NANOMIPS_EXTRACT_RS2(op) uMIPS_RS(op) +#define NANOMIPS_EXTRACT_RS1(op) ((op >> 1) & 0x7) +#define NANOMIPS_EXTRACT_RD5(op) ((op >> 5) & 0x1f) +#define NANOMIPS_EXTRACT_RS5(op) (op & 0x1f) + + +static void gen_adjust_sp(DisasContext *ctx, int u) +{ + gen_op_addr_addi(ctx, cpu_gpr[29], cpu_gpr[29], u); +} + +static void gen_save(DisasContext *ctx, uint8_t rt, uint8_t count, + uint8_t gp, uint16_t u) +{ + int counter = 0; + TCGv va = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); + + while (counter != count) { + bool use_gp = gp && (counter == count - 1); + int this_rt = use_gp ? 28 : (rt & 0x10) | ((rt + counter) & 0x1f); + int this_offset = -((counter + 1) << 2); + gen_base_offset_addr(ctx, va, 29, this_offset); + gen_load_gpr(t0, this_rt); + tcg_gen_qemu_st_tl(t0, va, ctx->mem_idx, + (MO_TEUL | ctx->default_tcg_memop_mask)); + counter++; + } + + /* adjust stack pointer */ + gen_adjust_sp(ctx, -u); + + tcg_temp_free(t0); + tcg_temp_free(va); +} + +static void gen_restore(DisasContext *ctx, uint8_t rt, uint8_t count, + uint8_t gp, uint16_t u) +{ + int counter = 0; + TCGv va = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); + + while (counter != count) { + bool use_gp = gp && (counter == count - 1); + int this_rt = use_gp ? 28 : (rt & 0x10) | ((rt + counter) & 0x1f); + int this_offset = u - ((counter + 1) << 2); + gen_base_offset_addr(ctx, va, 29, this_offset); + tcg_gen_qemu_ld_tl(t0, va, ctx->mem_idx, MO_TESL | + ctx->default_tcg_memop_mask); + tcg_gen_ext32s_tl(t0, t0); + gen_store_gpr(t0, this_rt); + counter++; + } + + /* adjust stack pointer */ + gen_adjust_sp(ctx, u); + + tcg_temp_free(t0); + tcg_temp_free(va); +} + +static void gen_pool16c_nanomips_insn(DisasContext *ctx) +{ + int rt = decode_gpr_gpr3(NANOMIPS_EXTRACT_RD(ctx->opcode)); + int rs = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS(ctx->opcode)); + + switch (extract32(ctx->opcode, 2, 2)) { + case NM_NOT16: + gen_logic(ctx, OPC_NOR, rt, rs, 0); + break; + case NM_AND16: + gen_logic(ctx, OPC_AND, rt, rt, rs); + break; + case NM_XOR16: + gen_logic(ctx, OPC_XOR, rt, rt, rs); + break; + case NM_OR16: + gen_logic(ctx, OPC_OR, rt, rt, rs); + break; + } +} + +static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) +{ + int rt = extract32(ctx->opcode, 21, 5); + int rs = extract32(ctx->opcode, 16, 5); + int rd = extract32(ctx->opcode, 11, 5); + + switch (extract32(ctx->opcode, 3, 7)) { + case NM_P_TRAP: + switch (extract32(ctx->opcode, 10, 1)) { + case NM_TEQ: + check_nms(ctx); + gen_trap(ctx, OPC_TEQ, rs, rt, -1); + break; + case NM_TNE: + check_nms(ctx); + gen_trap(ctx, OPC_TNE, rs, rt, -1); + break; + } + break; + case NM_RDHWR: + check_nms(ctx); + gen_rdhwr(ctx, rt, rs, extract32(ctx->opcode, 11, 3)); + break; + case NM_SEB: + check_nms(ctx); + gen_bshfl(ctx, OPC_SEB, rs, rt); + break; + case NM_SEH: + gen_bshfl(ctx, OPC_SEH, rs, rt); + break; + case NM_SLLV: + gen_shift(ctx, OPC_SLLV, rd, rt, rs); + break; + case NM_SRLV: + gen_shift(ctx, OPC_SRLV, rd, rt, rs); + break; + case NM_SRAV: + gen_shift(ctx, OPC_SRAV, rd, rt, rs); + break; + case NM_ROTRV: + gen_shift(ctx, OPC_ROTRV, rd, rt, rs); + break; + case NM_ADD: + gen_arith(ctx, OPC_ADD, rd, rs, rt); + break; + case NM_ADDU: + gen_arith(ctx, OPC_ADDU, rd, rs, rt); + break; + case NM_SUB: + check_nms(ctx); + gen_arith(ctx, OPC_SUB, rd, rs, rt); + break; + case NM_SUBU: + gen_arith(ctx, OPC_SUBU, rd, rs, rt); + break; + case NM_P_CMOVE: + switch (extract32(ctx->opcode, 10, 1)) { + case NM_MOVZ: + gen_cond_move(ctx, OPC_MOVZ, rd, rs, rt); + break; + case NM_MOVN: + gen_cond_move(ctx, OPC_MOVN, rd, rs, rt); + break; + } + break; + case NM_AND: + gen_logic(ctx, OPC_AND, rd, rs, rt); + break; + case NM_OR: + gen_logic(ctx, OPC_OR, rd, rs, rt); + break; + case NM_NOR: + gen_logic(ctx, OPC_NOR, rd, rs, rt); + break; + case NM_XOR: + gen_logic(ctx, OPC_XOR, rd, rs, rt); + break; + case NM_SLT: + gen_slt(ctx, OPC_SLT, rd, rs, rt); + break; + case NM_P_SLTU: + if (rd == 0) { + /* P_DVP */ +#ifndef CONFIG_USER_ONLY + TCGv t0 = tcg_temp_new(); + switch (extract32(ctx->opcode, 10, 1)) { + case NM_DVP: + if (ctx->vp) { + check_cp0_enabled(ctx); + gen_helper_dvp(t0, cpu_env); + gen_store_gpr(t0, rt); + } + break; + case NM_EVP: + if (ctx->vp) { + check_cp0_enabled(ctx); + gen_helper_evp(t0, cpu_env); + gen_store_gpr(t0, rt); + } + break; + } + tcg_temp_free(t0); +#endif + } else { + gen_slt(ctx, OPC_SLTU, rd, rs, rt); + } + break; + case NM_SOV: + { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + gen_load_gpr(t1, rs); + gen_load_gpr(t2, rt); + tcg_gen_add_tl(t0, t1, t2); + tcg_gen_ext32s_tl(t0, t0); + tcg_gen_xor_tl(t1, t1, t2); + tcg_gen_xor_tl(t2, t0, t2); + tcg_gen_andc_tl(t1, t2, t1); + + /* operands of same sign, result different sign */ + tcg_gen_setcondi_tl(TCG_COND_LT, t0, t1, 0); + gen_store_gpr(t0, rd); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + } + break; + case NM_MUL: + gen_r6_muldiv(ctx, R6_OPC_MUL, rd, rs, rt); + break; + case NM_MUH: + gen_r6_muldiv(ctx, R6_OPC_MUH, rd, rs, rt); + break; + case NM_MULU: + gen_r6_muldiv(ctx, R6_OPC_MULU, rd, rs, rt); + break; + case NM_MUHU: + gen_r6_muldiv(ctx, R6_OPC_MUHU, rd, rs, rt); + break; + case NM_DIV: + gen_r6_muldiv(ctx, R6_OPC_DIV, rd, rs, rt); + break; + case NM_MOD: + gen_r6_muldiv(ctx, R6_OPC_MOD, rd, rs, rt); + break; + case NM_DIVU: + gen_r6_muldiv(ctx, R6_OPC_DIVU, rd, rs, rt); + break; + case NM_MODU: + gen_r6_muldiv(ctx, R6_OPC_MODU, rd, rs, rt); + break; +#ifndef CONFIG_USER_ONLY + case NM_MFC0: + check_cp0_enabled(ctx); + if (rt == 0) { + /* Treat as NOP. */ + break; + } + gen_mfc0(ctx, cpu_gpr[rt], rs, extract32(ctx->opcode, 11, 3)); + break; + case NM_MTC0: + check_cp0_enabled(ctx); + { + TCGv t0 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + gen_mtc0(ctx, t0, rs, extract32(ctx->opcode, 11, 3)); + tcg_temp_free(t0); + } + break; + case NM_D_E_MT_VPE: + { + uint8_t sc = extract32(ctx->opcode, 10, 1); + TCGv t0 = tcg_temp_new(); + + switch (sc) { + case 0: + if (rs == 1) { + /* DMT */ + check_cp0_mt(ctx); + gen_helper_dmt(t0); + gen_store_gpr(t0, rt); + } else if (rs == 0) { + /* DVPE */ + check_cp0_mt(ctx); + gen_helper_dvpe(t0, cpu_env); + gen_store_gpr(t0, rt); + } else { + generate_exception_end(ctx, EXCP_RI); + } + break; + case 1: + if (rs == 1) { + /* EMT */ + check_cp0_mt(ctx); + gen_helper_emt(t0); + gen_store_gpr(t0, rt); + } else if (rs == 0) { + /* EVPE */ + check_cp0_mt(ctx); + gen_helper_evpe(t0, cpu_env); + gen_store_gpr(t0, rt); + } else { + generate_exception_end(ctx, EXCP_RI); + } + break; + } + + tcg_temp_free(t0); + } + break; + case NM_FORK: + check_mt(ctx); + { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + gen_load_gpr(t1, rs); + gen_helper_fork(t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + case NM_MFTR: + case NM_MFHTR: + check_cp0_enabled(ctx); + if (rd == 0) { + /* Treat as NOP. */ + return; + } + gen_mftr(env, ctx, rs, rt, extract32(ctx->opcode, 10, 1), + extract32(ctx->opcode, 11, 5), extract32(ctx->opcode, 3, 1)); + break; + case NM_MTTR: + case NM_MTHTR: + check_cp0_enabled(ctx); + gen_mttr(env, ctx, rs, rt, extract32(ctx->opcode, 10, 1), + extract32(ctx->opcode, 11, 5), extract32(ctx->opcode, 3, 1)); + break; + case NM_YIELD: + check_mt(ctx); + { + TCGv t0 = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_helper_yield(t0, cpu_env, t0); + gen_store_gpr(t0, rt); + tcg_temp_free(t0); + } + break; +#endif + default: + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* dsp */ +static void gen_pool32axf_1_5_nanomips_insn(DisasContext *ctx, uint32_t opc, + int ret, int v1, int v2) +{ + TCGv_i32 t0; + TCGv v0_t; + TCGv v1_t; + + t0 = tcg_temp_new_i32(); + + v0_t = tcg_temp_new(); + v1_t = tcg_temp_new(); + + tcg_gen_movi_i32(t0, v2 >> 3); + + gen_load_gpr(v0_t, ret); + gen_load_gpr(v1_t, v1); + + switch (opc) { + case NM_MAQ_S_W_PHR: + check_dsp(ctx); + gen_helper_maq_s_w_phr(t0, v1_t, v0_t, cpu_env); + break; + case NM_MAQ_S_W_PHL: + check_dsp(ctx); + gen_helper_maq_s_w_phl(t0, v1_t, v0_t, cpu_env); + break; + case NM_MAQ_SA_W_PHR: + check_dsp(ctx); + gen_helper_maq_sa_w_phr(t0, v1_t, v0_t, cpu_env); + break; + case NM_MAQ_SA_W_PHL: + check_dsp(ctx); + gen_helper_maq_sa_w_phl(t0, v1_t, v0_t, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(t0); + + tcg_temp_free(v0_t); + tcg_temp_free(v1_t); +} + + +static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, + int ret, int v1, int v2) +{ + int16_t imm; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv v0_t = tcg_temp_new(); + + gen_load_gpr(v0_t, v1); + + switch (opc) { + case NM_POOL32AXF_1_0: + check_dsp(ctx); + switch (extract32(ctx->opcode, 12, 2)) { + case NM_MFHI: + gen_HILO(ctx, OPC_MFHI, v2 >> 3, ret); + break; + case NM_MFLO: + gen_HILO(ctx, OPC_MFLO, v2 >> 3, ret); + break; + case NM_MTHI: + gen_HILO(ctx, OPC_MTHI, v2 >> 3, v1); + break; + case NM_MTLO: + gen_HILO(ctx, OPC_MTLO, v2 >> 3, v1); + break; + } + break; + case NM_POOL32AXF_1_1: + check_dsp(ctx); + switch (extract32(ctx->opcode, 12, 2)) { + case NM_MTHLIP: + tcg_gen_movi_tl(t0, v2); + gen_helper_mthlip(t0, v0_t, cpu_env); + break; + case NM_SHILOV: + tcg_gen_movi_tl(t0, v2 >> 3); + gen_helper_shilo(t0, v0_t, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_1_3: + check_dsp(ctx); + imm = extract32(ctx->opcode, 14, 7); + switch (extract32(ctx->opcode, 12, 2)) { + case NM_RDDSP: + tcg_gen_movi_tl(t0, imm); + gen_helper_rddsp(t0, t0, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_WRDSP: + gen_load_gpr(t0, ret); + tcg_gen_movi_tl(t1, imm); + gen_helper_wrdsp(t0, t1, cpu_env); + break; + case NM_EXTP: + tcg_gen_movi_tl(t0, v2 >> 3); + tcg_gen_movi_tl(t1, v1); + gen_helper_extp(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_EXTPDP: + tcg_gen_movi_tl(t0, v2 >> 3); + tcg_gen_movi_tl(t1, v1); + gen_helper_extpdp(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + case NM_POOL32AXF_1_4: + check_dsp(ctx); + tcg_gen_movi_tl(t0, v2 >> 2); + switch (extract32(ctx->opcode, 12, 1)) { + case NM_SHLL_QB: + gen_helper_shll_qb(t0, t0, v0_t, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_SHRL_QB: + gen_helper_shrl_qb(t0, t0, v0_t); + gen_store_gpr(t0, ret); + break; + } + break; + case NM_POOL32AXF_1_5: + opc = extract32(ctx->opcode, 12, 2); + gen_pool32axf_1_5_nanomips_insn(ctx, opc, ret, v1, v2); + break; + case NM_POOL32AXF_1_7: + check_dsp(ctx); + tcg_gen_movi_tl(t0, v2 >> 3); + tcg_gen_movi_tl(t1, v1); + switch (extract32(ctx->opcode, 12, 2)) { + case NM_EXTR_W: + gen_helper_extr_w(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_EXTR_R_W: + gen_helper_extr_r_w(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_EXTR_RS_W: + gen_helper_extr_rs_w(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_EXTR_S_H: + gen_helper_extr_s_h(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(v0_t); +} + +static void gen_pool32axf_2_multiply(DisasContext *ctx, uint32_t opc, + TCGv v0, TCGv v1, int rd) +{ + TCGv_i32 t0; + + t0 = tcg_temp_new_i32(); + + tcg_gen_movi_i32(t0, rd >> 3); + + switch (opc) { + case NM_POOL32AXF_2_0_7: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPA_W_PH: + check_dspr2(ctx); + gen_helper_dpa_w_ph(t0, v1, v0, cpu_env); + break; + case NM_DPAQ_S_W_PH: + check_dsp(ctx); + gen_helper_dpaq_s_w_ph(t0, v1, v0, cpu_env); + break; + case NM_DPS_W_PH: + check_dspr2(ctx); + gen_helper_dps_w_ph(t0, v1, v0, cpu_env); + break; + case NM_DPSQ_S_W_PH: + check_dsp(ctx); + gen_helper_dpsq_s_w_ph(t0, v1, v0, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_2_8_15: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAX_W_PH: + check_dspr2(ctx); + gen_helper_dpax_w_ph(t0, v0, v1, cpu_env); + break; + case NM_DPAQ_SA_L_W: + check_dsp(ctx); + gen_helper_dpaq_sa_l_w(t0, v0, v1, cpu_env); + break; + case NM_DPSX_W_PH: + check_dspr2(ctx); + gen_helper_dpsx_w_ph(t0, v0, v1, cpu_env); + break; + case NM_DPSQ_SA_L_W: + check_dsp(ctx); + gen_helper_dpsq_sa_l_w(t0, v0, v1, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_2_16_23: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAU_H_QBL: + check_dsp(ctx); + gen_helper_dpau_h_qbl(t0, v0, v1, cpu_env); + break; + case NM_DPAQX_S_W_PH: + check_dspr2(ctx); + gen_helper_dpaqx_s_w_ph(t0, v0, v1, cpu_env); + break; + case NM_DPSU_H_QBL: + check_dsp(ctx); + gen_helper_dpsu_h_qbl(t0, v0, v1, cpu_env); + break; + case NM_DPSQX_S_W_PH: + check_dspr2(ctx); + gen_helper_dpsqx_s_w_ph(t0, v0, v1, cpu_env); + break; + case NM_MULSA_W_PH: + check_dspr2(ctx); + gen_helper_mulsa_w_ph(t0, v0, v1, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_2_24_31: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAU_H_QBR: + check_dsp(ctx); + gen_helper_dpau_h_qbr(t0, v1, v0, cpu_env); + break; + case NM_DPAQX_SA_W_PH: + check_dspr2(ctx); + gen_helper_dpaqx_sa_w_ph(t0, v1, v0, cpu_env); + break; + case NM_DPSU_H_QBR: + check_dsp(ctx); + gen_helper_dpsu_h_qbr(t0, v1, v0, cpu_env); + break; + case NM_DPSQX_SA_W_PH: + check_dspr2(ctx); + gen_helper_dpsqx_sa_w_ph(t0, v1, v0, cpu_env); + break; + case NM_MULSAQ_S_W_PH: + check_dsp(ctx); + gen_helper_mulsaq_s_w_ph(t0, v1, v0, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(t0); +} + +static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, + int rt, int rs, int rd) +{ + int ret = rt; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv v0_t = tcg_temp_new(); + TCGv v1_t = tcg_temp_new(); + + gen_load_gpr(v0_t, rt); + gen_load_gpr(v1_t, rs); + + switch (opc) { + case NM_POOL32AXF_2_0_7: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPA_W_PH: + case NM_DPAQ_S_W_PH: + case NM_DPS_W_PH: + case NM_DPSQ_S_W_PH: + gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); + break; + case NM_BALIGN: + check_dspr2(ctx); + if (rt != 0) { + gen_load_gpr(t0, rs); + rd &= 3; + if (rd != 0 && rd != 2) { + tcg_gen_shli_tl(cpu_gpr[ret], cpu_gpr[ret], 8 * rd); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_shri_tl(t0, t0, 8 * (4 - rd)); + tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); + } + tcg_gen_ext32s_tl(cpu_gpr[ret], cpu_gpr[ret]); + } + break; + case NM_MADD: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + gen_load_gpr(t0, rt); + gen_load_gpr(t1, rs); + tcg_gen_ext_tl_i64(t2, t0); + tcg_gen_ext_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_add_i64(t2, t2, t3); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + tcg_temp_free_i64(t2); + } + break; + case NM_MULT: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_muls2_i32(t2, t3, t2, t3); + tcg_gen_ext_i32_tl(cpu_LO[acc], t2); + tcg_gen_ext_i32_tl(cpu_HI[acc], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + break; + case NM_EXTRV_W: + check_dsp(ctx); + gen_load_gpr(v1_t, rs); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extr_w(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + case NM_POOL32AXF_2_8_15: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAX_W_PH: + case NM_DPAQ_SA_L_W: + case NM_DPSX_W_PH: + case NM_DPSQ_SA_L_W: + gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); + break; + case NM_MADDU: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_extu_tl_i64(t2, t0); + tcg_gen_extu_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_add_i64(t2, t2, t3); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + tcg_temp_free_i64(t2); + } + break; + case NM_MULTU: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_mulu2_i32(t2, t3, t2, t3); + tcg_gen_ext_i32_tl(cpu_LO[acc], t2); + tcg_gen_ext_i32_tl(cpu_HI[acc], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + break; + case NM_EXTRV_R_W: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extr_r_w(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_2_16_23: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAU_H_QBL: + case NM_DPAQX_S_W_PH: + case NM_DPSU_H_QBL: + case NM_DPSQX_S_W_PH: + case NM_MULSA_W_PH: + gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); + break; + case NM_EXTPV: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extp(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_MSUB: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_ext_tl_i64(t2, t0); + tcg_gen_ext_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_sub_i64(t2, t3, t2); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + tcg_temp_free_i64(t2); + } + break; + case NM_EXTRV_RS_W: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extr_rs_w(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + case NM_POOL32AXF_2_24_31: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAU_H_QBR: + case NM_DPAQX_SA_W_PH: + case NM_DPSU_H_QBR: + case NM_DPSQX_SA_W_PH: + case NM_MULSAQ_S_W_PH: + gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); + break; + case NM_EXTPDPV: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extpdp(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_MSUBU: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_extu_tl_i64(t2, t0); + tcg_gen_extu_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_sub_i64(t2, t3, t2); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + tcg_temp_free_i64(t2); + } + break; + case NM_EXTRV_S_H: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extr_s_h(t0, t0, v0_t, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free(t0); + tcg_temp_free(t1); + + tcg_temp_free(v0_t); + tcg_temp_free(v1_t); +} + +static void gen_pool32axf_4_nanomips_insn(DisasContext *ctx, uint32_t opc, + int rt, int rs) +{ + int ret = rt; + TCGv t0 = tcg_temp_new(); + TCGv v0_t = tcg_temp_new(); + + gen_load_gpr(v0_t, rs); + + switch (opc) { + case NM_ABSQ_S_QB: + check_dspr2(ctx); + gen_helper_absq_s_qb(v0_t, v0_t, cpu_env); + gen_store_gpr(v0_t, ret); + break; + case NM_ABSQ_S_PH: + check_dsp(ctx); + gen_helper_absq_s_ph(v0_t, v0_t, cpu_env); + gen_store_gpr(v0_t, ret); + break; + case NM_ABSQ_S_W: + check_dsp(ctx); + gen_helper_absq_s_w(v0_t, v0_t, cpu_env); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQ_W_PHL: + check_dsp(ctx); + tcg_gen_andi_tl(v0_t, v0_t, 0xFFFF0000); + tcg_gen_ext32s_tl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQ_W_PHR: + check_dsp(ctx); + tcg_gen_andi_tl(v0_t, v0_t, 0x0000FFFF); + tcg_gen_shli_tl(v0_t, v0_t, 16); + tcg_gen_ext32s_tl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQU_PH_QBL: + check_dsp(ctx); + gen_helper_precequ_ph_qbl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQU_PH_QBR: + check_dsp(ctx); + gen_helper_precequ_ph_qbr(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQU_PH_QBLA: + check_dsp(ctx); + gen_helper_precequ_ph_qbla(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQU_PH_QBRA: + check_dsp(ctx); + gen_helper_precequ_ph_qbra(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEU_PH_QBL: + check_dsp(ctx); + gen_helper_preceu_ph_qbl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEU_PH_QBR: + check_dsp(ctx); + gen_helper_preceu_ph_qbr(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEU_PH_QBLA: + check_dsp(ctx); + gen_helper_preceu_ph_qbla(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEU_PH_QBRA: + check_dsp(ctx); + gen_helper_preceu_ph_qbra(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_REPLV_PH: + check_dsp(ctx); + tcg_gen_ext16u_tl(v0_t, v0_t); + tcg_gen_shli_tl(t0, v0_t, 16); + tcg_gen_or_tl(v0_t, v0_t, t0); + tcg_gen_ext32s_tl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_REPLV_QB: + check_dsp(ctx); + tcg_gen_ext8u_tl(v0_t, v0_t); + tcg_gen_shli_tl(t0, v0_t, 8); + tcg_gen_or_tl(v0_t, v0_t, t0); + tcg_gen_shli_tl(t0, v0_t, 16); + tcg_gen_or_tl(v0_t, v0_t, t0); + tcg_gen_ext32s_tl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_BITREV: + check_dsp(ctx); + gen_helper_bitrev(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_INSV: + check_dsp(ctx); + { + TCGv tv0 = tcg_temp_new(); + + gen_load_gpr(tv0, rt); + gen_helper_insv(v0_t, cpu_env, v0_t, tv0); + gen_store_gpr(v0_t, ret); + tcg_temp_free(tv0); + } + break; + case NM_RADDU_W_QB: + check_dsp(ctx); + gen_helper_raddu_w_qb(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_BITSWAP: + gen_bitswap(ctx, OPC_BITSWAP, ret, rs); + break; + case NM_CLO: + check_nms(ctx); + gen_cl(ctx, OPC_CLO, ret, rs); + break; + case NM_CLZ: + check_nms(ctx); + gen_cl(ctx, OPC_CLZ, ret, rs); + break; + case NM_WSBH: + gen_bshfl(ctx, OPC_WSBH, ret, rs); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free(v0_t); + tcg_temp_free(t0); +} + +static void gen_pool32axf_7_nanomips_insn(DisasContext *ctx, uint32_t opc, + int rt, int rs, int rd) +{ + TCGv t0 = tcg_temp_new(); + TCGv rs_t = tcg_temp_new(); + + gen_load_gpr(rs_t, rs); + + switch (opc) { + case NM_SHRA_R_QB: + check_dspr2(ctx); + tcg_gen_movi_tl(t0, rd >> 2); + switch (extract32(ctx->opcode, 12, 1)) { + case 0: + /* NM_SHRA_QB */ + gen_helper_shra_qb(t0, t0, rs_t); + gen_store_gpr(t0, rt); + break; + case 1: + /* NM_SHRA_R_QB */ + gen_helper_shra_r_qb(t0, t0, rs_t); + gen_store_gpr(t0, rt); + break; + } + break; + case NM_SHRL_PH: + check_dspr2(ctx); + tcg_gen_movi_tl(t0, rd >> 1); + gen_helper_shrl_ph(t0, t0, rs_t); + gen_store_gpr(t0, rt); + break; + case NM_REPL_QB: + check_dsp(ctx); + { + int16_t imm; + target_long result; + imm = extract32(ctx->opcode, 13, 8); + result = (uint32_t)imm << 24 | + (uint32_t)imm << 16 | + (uint32_t)imm << 8 | + (uint32_t)imm; + result = (int32_t)result; + tcg_gen_movi_tl(t0, result); + gen_store_gpr(t0, rt); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + tcg_temp_free(t0); + tcg_temp_free(rs_t); +} + + +static void gen_pool32axf_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) +{ + int rt = extract32(ctx->opcode, 21, 5); + int rs = extract32(ctx->opcode, 16, 5); + int rd = extract32(ctx->opcode, 11, 5); + + switch (extract32(ctx->opcode, 6, 3)) { + case NM_POOL32AXF_1: + { + int32_t op1 = extract32(ctx->opcode, 9, 3); + gen_pool32axf_1_nanomips_insn(ctx, op1, rt, rs, rd); + } + break; + case NM_POOL32AXF_2: + { + int32_t op1 = extract32(ctx->opcode, 12, 2); + gen_pool32axf_2_nanomips_insn(ctx, op1, rt, rs, rd); + } + break; + case NM_POOL32AXF_4: + { + int32_t op1 = extract32(ctx->opcode, 9, 7); + gen_pool32axf_4_nanomips_insn(ctx, op1, rt, rs); + } + break; + case NM_POOL32AXF_5: + switch (extract32(ctx->opcode, 9, 7)) { +#ifndef CONFIG_USER_ONLY + case NM_TLBP: + gen_cp0(env, ctx, OPC_TLBP, 0, 0); + break; + case NM_TLBR: + gen_cp0(env, ctx, OPC_TLBR, 0, 0); + break; + case NM_TLBWI: + gen_cp0(env, ctx, OPC_TLBWI, 0, 0); + break; + case NM_TLBWR: + gen_cp0(env, ctx, OPC_TLBWR, 0, 0); + break; + case NM_TLBINV: + gen_cp0(env, ctx, OPC_TLBINV, 0, 0); + break; + case NM_TLBINVF: + gen_cp0(env, ctx, OPC_TLBINVF, 0, 0); + break; + case NM_DI: + check_cp0_enabled(ctx); + { + TCGv t0 = tcg_temp_new(); + + save_cpu_state(ctx, 1); + gen_helper_di(t0, cpu_env); + gen_store_gpr(t0, rt); + /* Stop translation as we may have switched the execution mode */ + ctx->base.is_jmp = DISAS_STOP; + tcg_temp_free(t0); + } + break; + case NM_EI: + check_cp0_enabled(ctx); + { + TCGv t0 = tcg_temp_new(); + + save_cpu_state(ctx, 1); + gen_helper_ei(t0, cpu_env); + gen_store_gpr(t0, rt); + /* Stop translation as we may have switched the execution mode */ + ctx->base.is_jmp = DISAS_STOP; + tcg_temp_free(t0); + } + break; + case NM_RDPGPR: + gen_load_srsgpr(rs, rt); + break; + case NM_WRPGPR: + gen_store_srsgpr(rs, rt); + break; + case NM_WAIT: + gen_cp0(env, ctx, OPC_WAIT, 0, 0); + break; + case NM_DERET: + gen_cp0(env, ctx, OPC_DERET, 0, 0); + break; + case NM_ERETX: + gen_cp0(env, ctx, OPC_ERET, 0, 0); + break; +#endif + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_7: + { + int32_t op1 = extract32(ctx->opcode, 9, 3); + gen_pool32axf_7_nanomips_insn(ctx, op1, rt, rs, rd); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* Immediate Value Compact Branches */ +static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, + int rt, int32_t imm, int32_t offset) +{ + TCGCond cond; + int bcond_compute = 0; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + tcg_gen_movi_tl(t1, imm); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + + /* Load needed operands and calculate btarget */ + switch (opc) { + case NM_BEQIC: + if (rt == 0 && imm == 0) { + /* Unconditional branch */ + } else if (rt == 0 && imm != 0) { + /* Treat as NOP */ + goto out; + } else { + bcond_compute = 1; + cond = TCG_COND_EQ; + } + break; + case NM_BBEQZC: + case NM_BBNEZC: + check_nms(ctx); + if (imm >= 32 && !(ctx->hflags & MIPS_HFLAG_64)) { + generate_exception_end(ctx, EXCP_RI); + goto out; + } else if (rt == 0 && opc == NM_BBEQZC) { + /* Unconditional branch */ + } else if (rt == 0 && opc == NM_BBNEZC) { + /* Treat as NOP */ + goto out; + } else { + tcg_gen_shri_tl(t0, t0, imm); + tcg_gen_andi_tl(t0, t0, 1); + tcg_gen_movi_tl(t1, 0); + bcond_compute = 1; + if (opc == NM_BBEQZC) { + cond = TCG_COND_EQ; + } else { + cond = TCG_COND_NE; + } + } + break; + case NM_BNEIC: + if (rt == 0 && imm == 0) { + /* Treat as NOP */ + goto out; + } else if (rt == 0 && imm != 0) { + /* Unconditional branch */ + } else { + bcond_compute = 1; + cond = TCG_COND_NE; + } + break; + case NM_BGEIC: + if (rt == 0 && imm == 0) { + /* Unconditional branch */ + } else { + bcond_compute = 1; + cond = TCG_COND_GE; + } + break; + case NM_BLTIC: + bcond_compute = 1; + cond = TCG_COND_LT; + break; + case NM_BGEIUC: + if (rt == 0 && imm == 0) { + /* Unconditional branch */ + } else { + bcond_compute = 1; + cond = TCG_COND_GEU; + } + break; + case NM_BLTIUC: + bcond_compute = 1; + cond = TCG_COND_LTU; + break; + default: + MIPS_INVAL("Immediate Value Compact branch"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + if (bcond_compute == 0) { + /* Uncoditional compact branch */ + gen_goto_tb(ctx, 0, ctx->btarget); + } else { + /* Conditional compact branch */ + TCGLabel *fs = gen_new_label(); + + tcg_gen_brcond_tl(tcg_invert_cond(cond), t0, t1, fs); + + gen_goto_tb(ctx, 1, ctx->btarget); + gen_set_label(fs); + + gen_goto_tb(ctx, 0, ctx->base.pc_next + 4); + } + +out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +/* P.BALRSC type nanoMIPS R6 branches: BALRSC and BRSC */ +static void gen_compute_nanomips_pbalrsc_branch(DisasContext *ctx, int rs, + int rt) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* load rs */ + gen_load_gpr(t0, rs); + + /* link */ + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], ctx->base.pc_next + 4); + } + + /* calculate btarget */ + tcg_gen_shli_tl(t0, t0, 1); + tcg_gen_movi_tl(t1, ctx->base.pc_next + 4); + gen_op_addr_add(ctx, btarget, t1, t0); + + /* unconditional branch to register */ + tcg_gen_mov_tl(cpu_PC, btarget); + tcg_gen_lookup_and_goto_ptr(); + + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +/* nanoMIPS Branches */ +static void gen_compute_compact_branch_nm(DisasContext *ctx, uint32_t opc, + int rs, int rt, int32_t offset) +{ + int bcond_compute = 0; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* Load needed operands and calculate btarget */ + switch (opc) { + /* compact branch */ + case OPC_BGEC: + case OPC_BLTC: + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + break; + case OPC_BGEUC: + case OPC_BLTUC: + if (rs == 0 || rs == rt) { + /* OPC_BLEZALC, OPC_BGEZALC */ + /* OPC_BGTZALC, OPC_BLTZALC */ + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4); + } + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + break; + case OPC_BC: + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + break; + case OPC_BEQZC: + if (rs != 0) { + /* OPC_BEQZC, OPC_BNEZC */ + gen_load_gpr(t0, rs); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + } else { + /* OPC_JIC, OPC_JIALC */ + TCGv tbase = tcg_temp_new(); + TCGv toffset = tcg_temp_new(); + + gen_load_gpr(tbase, rt); + tcg_gen_movi_tl(toffset, offset); + gen_op_addr_add(ctx, btarget, tbase, toffset); + tcg_temp_free(tbase); + tcg_temp_free(toffset); + } + break; + default: + MIPS_INVAL("Compact branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + if (bcond_compute == 0) { + /* Uncoditional compact branch */ + switch (opc) { + case OPC_BC: + gen_goto_tb(ctx, 0, ctx->btarget); + break; + default: + MIPS_INVAL("Compact branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + } else { + /* Conditional compact branch */ + TCGLabel *fs = gen_new_label(); + + switch (opc) { + case OPC_BGEUC: + if (rs == 0 && rt != 0) { + /* OPC_BLEZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BGEZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); + } else { + /* OPC_BGEUC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GEU), t0, t1, fs); + } + break; + case OPC_BLTUC: + if (rs == 0 && rt != 0) { + /* OPC_BGTZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BLTZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); + } else { + /* OPC_BLTUC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LTU), t0, t1, fs); + } + break; + case OPC_BGEC: + if (rs == 0 && rt != 0) { + /* OPC_BLEZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BGEZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); + } else { + /* OPC_BGEC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GE), t0, t1, fs); + } + break; + case OPC_BLTC: + if (rs == 0 && rt != 0) { + /* OPC_BGTZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BLTZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); + } else { + /* OPC_BLTC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LT), t0, t1, fs); + } + break; + case OPC_BEQZC: + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t0, 0, fs); + break; + default: + MIPS_INVAL("Compact conditional branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + /* Generating branch here as compact branches don't have delay slot */ + gen_goto_tb(ctx, 1, ctx->btarget); + gen_set_label(fs); + + gen_goto_tb(ctx, 0, ctx->base.pc_next + 4); + } + +out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} + + +/* nanoMIPS CP1 Branches */ +static void gen_compute_branch_cp1_nm(DisasContext *ctx, uint32_t op, + int32_t ft, int32_t offset) +{ + target_ulong btarget; + TCGv_i64 t0 = tcg_temp_new_i64(); + + gen_load_fpr64(ctx, t0, ft); + tcg_gen_andi_i64(t0, t0, 1); + + btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + + switch (op) { + case NM_BC1EQZC: + tcg_gen_xori_i64(t0, t0, 1); + ctx->hflags |= MIPS_HFLAG_BC; + break; + case NM_BC1NEZC: + /* t0 already set */ + ctx->hflags |= MIPS_HFLAG_BC; + break; + default: + MIPS_INVAL("cp1 cond branch"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + tcg_gen_trunc_i64_tl(bcond, t0); + + ctx->btarget = btarget; + +out: + tcg_temp_free_i64(t0); +} + + +static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) +{ + TCGv t0, t1; + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + if ((extract32(ctx->opcode, 6, 1)) == 1) { + /* PP.LSXS instructions require shifting */ + switch (extract32(ctx->opcode, 7, 4)) { + case NM_SHXS: + check_nms(ctx); + case NM_LHXS: + case NM_LHUXS: + tcg_gen_shli_tl(t0, t0, 1); + break; + case NM_SWXS: + check_nms(ctx); + case NM_LWXS: + case NM_LWC1XS: + case NM_SWC1XS: + tcg_gen_shli_tl(t0, t0, 2); + break; + case NM_LDC1XS: + case NM_SDC1XS: + tcg_gen_shli_tl(t0, t0, 3); + break; + } + } + gen_op_addr_add(ctx, t0, t0, t1); + + switch (extract32(ctx->opcode, 7, 4)) { + case NM_LBX: + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_SB); + gen_store_gpr(t0, rd); + break; + case NM_LHX: + /*case NM_LHXS:*/ + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_TESW); + gen_store_gpr(t0, rd); + break; + case NM_LWX: + /*case NM_LWXS:*/ + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_TESL); + gen_store_gpr(t0, rd); + break; + case NM_LBUX: + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_UB); + gen_store_gpr(t0, rd); + break; + case NM_LHUX: + /*case NM_LHUXS:*/ + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_TEUW); + gen_store_gpr(t0, rd); + break; + case NM_SBX: + check_nms(ctx); + gen_load_gpr(t1, rd); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + MO_8); + break; + case NM_SHX: + /*case NM_SHXS:*/ + check_nms(ctx); + gen_load_gpr(t1, rd); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + MO_TEUW); + break; + case NM_SWX: + /*case NM_SWXS:*/ + check_nms(ctx); + gen_load_gpr(t1, rd); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + MO_TEUL); + break; + case NM_LWC1X: + /*case NM_LWC1XS:*/ + case NM_LDC1X: + /*case NM_LDC1XS:*/ + case NM_SWC1X: + /*case NM_SWC1XS:*/ + case NM_SDC1X: + /*case NM_SDC1XS:*/ + if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { + check_cp1_enabled(ctx); + switch (extract32(ctx->opcode, 7, 4)) { + case NM_LWC1X: + /*case NM_LWC1XS:*/ + gen_flt_ldst(ctx, OPC_LWC1, rd, t0); + break; + case NM_LDC1X: + /*case NM_LDC1XS:*/ + gen_flt_ldst(ctx, OPC_LDC1, rd, t0); + break; + case NM_SWC1X: + /*case NM_SWC1XS:*/ + gen_flt_ldst(ctx, OPC_SWC1, rd, t0); + break; + case NM_SDC1X: + /*case NM_SDC1XS:*/ + gen_flt_ldst(ctx, OPC_SDC1, rd, t0); + break; + } + } else { + generate_exception_err(ctx, EXCP_CpU, 1); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +static void gen_pool32f_nanomips_insn(DisasContext *ctx) +{ + int rt, rs, rd; + + rt = extract32(ctx->opcode, 21, 5); + rs = extract32(ctx->opcode, 16, 5); + rd = extract32(ctx->opcode, 11, 5); + + if (!(ctx->CP0_Config1 & (1 << CP0C1_FP))) { + generate_exception_end(ctx, EXCP_RI); + return; + } + check_cp1_enabled(ctx); + switch (extract32(ctx->opcode, 0, 3)) { + case NM_POOL32F_0: + switch (extract32(ctx->opcode, 3, 7)) { + case NM_RINT_S: + gen_farith(ctx, OPC_RINT_S, 0, rt, rs, 0); + break; + case NM_RINT_D: + gen_farith(ctx, OPC_RINT_D, 0, rt, rs, 0); + break; + case NM_CLASS_S: + gen_farith(ctx, OPC_CLASS_S, 0, rt, rs, 0); + break; + case NM_CLASS_D: + gen_farith(ctx, OPC_CLASS_D, 0, rt, rs, 0); + break; + case NM_ADD_S: + gen_farith(ctx, OPC_ADD_S, rt, rs, rd, 0); + break; + case NM_ADD_D: + gen_farith(ctx, OPC_ADD_D, rt, rs, rd, 0); + break; + case NM_SUB_S: + gen_farith(ctx, OPC_SUB_S, rt, rs, rd, 0); + break; + case NM_SUB_D: + gen_farith(ctx, OPC_SUB_D, rt, rs, rd, 0); + break; + case NM_MUL_S: + gen_farith(ctx, OPC_MUL_S, rt, rs, rd, 0); + break; + case NM_MUL_D: + gen_farith(ctx, OPC_MUL_D, rt, rs, rd, 0); + break; + case NM_DIV_S: + gen_farith(ctx, OPC_DIV_S, rt, rs, rd, 0); + break; + case NM_DIV_D: + gen_farith(ctx, OPC_DIV_D, rt, rs, rd, 0); + break; + case NM_SELEQZ_S: + gen_sel_s(ctx, OPC_SELEQZ_S, rd, rt, rs); + break; + case NM_SELEQZ_D: + gen_sel_d(ctx, OPC_SELEQZ_D, rd, rt, rs); + break; + case NM_SELNEZ_S: + gen_sel_s(ctx, OPC_SELNEZ_S, rd, rt, rs); + break; + case NM_SELNEZ_D: + gen_sel_d(ctx, OPC_SELNEZ_D, rd, rt, rs); + break; + case NM_SEL_S: + gen_sel_s(ctx, OPC_SEL_S, rd, rt, rs); + break; + case NM_SEL_D: + gen_sel_d(ctx, OPC_SEL_D, rd, rt, rs); + break; + case NM_MADDF_S: + gen_farith(ctx, OPC_MADDF_S, rt, rs, rd, 0); + break; + case NM_MADDF_D: + gen_farith(ctx, OPC_MADDF_D, rt, rs, rd, 0); + break; + case NM_MSUBF_S: + gen_farith(ctx, OPC_MSUBF_S, rt, rs, rd, 0); + break; + case NM_MSUBF_D: + gen_farith(ctx, OPC_MSUBF_D, rt, rs, rd, 0); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32F_3: + switch (extract32(ctx->opcode, 3, 3)) { + case NM_MIN_FMT: + switch (extract32(ctx->opcode, 9, 1)) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MIN_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MIN_D, rt, rs, rd, 0); + break; + } + break; + case NM_MAX_FMT: + switch (extract32(ctx->opcode, 9, 1)) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MAX_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MAX_D, rt, rs, rd, 0); + break; + } + break; + case NM_MINA_FMT: + switch (extract32(ctx->opcode, 9, 1)) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MINA_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MINA_D, rt, rs, rd, 0); + break; + } + break; + case NM_MAXA_FMT: + switch (extract32(ctx->opcode, 9, 1)) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MAXA_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MAXA_D, rt, rs, rd, 0); + break; + } + break; + case NM_POOL32FXF: + switch (extract32(ctx->opcode, 6, 8)) { + case NM_CFC1: + gen_cp1(ctx, OPC_CFC1, rt, rs); + break; + case NM_CTC1: + gen_cp1(ctx, OPC_CTC1, rt, rs); + break; + case NM_MFC1: + gen_cp1(ctx, OPC_MFC1, rt, rs); + break; + case NM_MTC1: + gen_cp1(ctx, OPC_MTC1, rt, rs); + break; + case NM_MFHC1: + gen_cp1(ctx, OPC_MFHC1, rt, rs); + break; + case NM_MTHC1: + gen_cp1(ctx, OPC_MTHC1, rt, rs); + break; + case NM_CVT_S_PL: + gen_farith(ctx, OPC_CVT_S_PL, -1, rs, rt, 0); + break; + case NM_CVT_S_PU: + gen_farith(ctx, OPC_CVT_S_PU, -1, rs, rt, 0); + break; + default: + switch (extract32(ctx->opcode, 6, 9)) { + case NM_CVT_L_S: + gen_farith(ctx, OPC_CVT_L_S, -1, rs, rt, 0); + break; + case NM_CVT_L_D: + gen_farith(ctx, OPC_CVT_L_D, -1, rs, rt, 0); + break; + case NM_CVT_W_S: + gen_farith(ctx, OPC_CVT_W_S, -1, rs, rt, 0); + break; + case NM_CVT_W_D: + gen_farith(ctx, OPC_CVT_W_D, -1, rs, rt, 0); + break; + case NM_RSQRT_S: + gen_farith(ctx, OPC_RSQRT_S, -1, rs, rt, 0); + break; + case NM_RSQRT_D: + gen_farith(ctx, OPC_RSQRT_D, -1, rs, rt, 0); + break; + case NM_SQRT_S: + gen_farith(ctx, OPC_SQRT_S, -1, rs, rt, 0); + break; + case NM_SQRT_D: + gen_farith(ctx, OPC_SQRT_D, -1, rs, rt, 0); + break; + case NM_RECIP_S: + gen_farith(ctx, OPC_RECIP_S, -1, rs, rt, 0); + break; + case NM_RECIP_D: + gen_farith(ctx, OPC_RECIP_D, -1, rs, rt, 0); + break; + case NM_FLOOR_L_S: + gen_farith(ctx, OPC_FLOOR_L_S, -1, rs, rt, 0); + break; + case NM_FLOOR_L_D: + gen_farith(ctx, OPC_FLOOR_L_D, -1, rs, rt, 0); + break; + case NM_FLOOR_W_S: + gen_farith(ctx, OPC_FLOOR_W_S, -1, rs, rt, 0); + break; + case NM_FLOOR_W_D: + gen_farith(ctx, OPC_FLOOR_W_D, -1, rs, rt, 0); + break; + case NM_CEIL_L_S: + gen_farith(ctx, OPC_CEIL_L_S, -1, rs, rt, 0); + break; + case NM_CEIL_L_D: + gen_farith(ctx, OPC_CEIL_L_D, -1, rs, rt, 0); + break; + case NM_CEIL_W_S: + gen_farith(ctx, OPC_CEIL_W_S, -1, rs, rt, 0); + break; + case NM_CEIL_W_D: + gen_farith(ctx, OPC_CEIL_W_D, -1, rs, rt, 0); + break; + case NM_TRUNC_L_S: + gen_farith(ctx, OPC_TRUNC_L_S, -1, rs, rt, 0); + break; + case NM_TRUNC_L_D: + gen_farith(ctx, OPC_TRUNC_L_D, -1, rs, rt, 0); + break; + case NM_TRUNC_W_S: + gen_farith(ctx, OPC_TRUNC_W_S, -1, rs, rt, 0); + break; + case NM_TRUNC_W_D: + gen_farith(ctx, OPC_TRUNC_W_D, -1, rs, rt, 0); + break; + case NM_ROUND_L_S: + gen_farith(ctx, OPC_ROUND_L_S, -1, rs, rt, 0); + break; + case NM_ROUND_L_D: + gen_farith(ctx, OPC_ROUND_L_D, -1, rs, rt, 0); + break; + case NM_ROUND_W_S: + gen_farith(ctx, OPC_ROUND_W_S, -1, rs, rt, 0); + break; + case NM_ROUND_W_D: + gen_farith(ctx, OPC_ROUND_W_D, -1, rs, rt, 0); + break; + case NM_MOV_S: + gen_farith(ctx, OPC_MOV_S, -1, rs, rt, 0); + break; + case NM_MOV_D: + gen_farith(ctx, OPC_MOV_D, -1, rs, rt, 0); + break; + case NM_ABS_S: + gen_farith(ctx, OPC_ABS_S, -1, rs, rt, 0); + break; + case NM_ABS_D: + gen_farith(ctx, OPC_ABS_D, -1, rs, rt, 0); + break; + case NM_NEG_S: + gen_farith(ctx, OPC_NEG_S, -1, rs, rt, 0); + break; + case NM_NEG_D: + gen_farith(ctx, OPC_NEG_D, -1, rs, rt, 0); + break; + case NM_CVT_D_S: + gen_farith(ctx, OPC_CVT_D_S, -1, rs, rt, 0); + break; + case NM_CVT_D_W: + gen_farith(ctx, OPC_CVT_D_W, -1, rs, rt, 0); + break; + case NM_CVT_D_L: + gen_farith(ctx, OPC_CVT_D_L, -1, rs, rt, 0); + break; + case NM_CVT_S_D: + gen_farith(ctx, OPC_CVT_S_D, -1, rs, rt, 0); + break; + case NM_CVT_S_W: + gen_farith(ctx, OPC_CVT_S_W, -1, rs, rt, 0); + break; + case NM_CVT_S_L: + gen_farith(ctx, OPC_CVT_S_L, -1, rs, rt, 0); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + } + break; + } + break; + case NM_POOL32F_5: + switch (extract32(ctx->opcode, 3, 3)) { + case NM_CMP_CONDN_S: + gen_r6_cmp_s(ctx, extract32(ctx->opcode, 6, 5), rt, rs, rd); + break; + case NM_CMP_CONDN_D: + gen_r6_cmp_d(ctx, extract32(ctx->opcode, 6, 5), rt, rs, rd); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, + int rd, int rs, int rt) +{ + int ret = rd; + TCGv t0 = tcg_temp_new(); + TCGv v1_t = tcg_temp_new(); + TCGv v2_t = tcg_temp_new(); + + gen_load_gpr(v1_t, rs); + gen_load_gpr(v2_t, rt); + + switch (opc) { + case NM_CMP_EQ_PH: + check_dsp(ctx); + gen_helper_cmp_eq_ph(v1_t, v2_t, cpu_env); + break; + case NM_CMP_LT_PH: + check_dsp(ctx); + gen_helper_cmp_lt_ph(v1_t, v2_t, cpu_env); + break; + case NM_CMP_LE_PH: + check_dsp(ctx); + gen_helper_cmp_le_ph(v1_t, v2_t, cpu_env); + break; + case NM_CMPU_EQ_QB: + check_dsp(ctx); + gen_helper_cmpu_eq_qb(v1_t, v2_t, cpu_env); + break; + case NM_CMPU_LT_QB: + check_dsp(ctx); + gen_helper_cmpu_lt_qb(v1_t, v2_t, cpu_env); + break; + case NM_CMPU_LE_QB: + check_dsp(ctx); + gen_helper_cmpu_le_qb(v1_t, v2_t, cpu_env); + break; + case NM_CMPGU_EQ_QB: + check_dsp(ctx); + gen_helper_cmpgu_eq_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGU_LT_QB: + check_dsp(ctx); + gen_helper_cmpgu_lt_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGU_LE_QB: + check_dsp(ctx); + gen_helper_cmpgu_le_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGDU_EQ_QB: + check_dspr2(ctx); + gen_helper_cmpgu_eq_qb(v1_t, v1_t, v2_t); + tcg_gen_deposit_tl(cpu_dspctrl, cpu_dspctrl, v1_t, 24, 4); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGDU_LT_QB: + check_dspr2(ctx); + gen_helper_cmpgu_lt_qb(v1_t, v1_t, v2_t); + tcg_gen_deposit_tl(cpu_dspctrl, cpu_dspctrl, v1_t, 24, 4); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGDU_LE_QB: + check_dspr2(ctx); + gen_helper_cmpgu_le_qb(v1_t, v1_t, v2_t); + tcg_gen_deposit_tl(cpu_dspctrl, cpu_dspctrl, v1_t, 24, 4); + gen_store_gpr(v1_t, ret); + break; + case NM_PACKRL_PH: + check_dsp(ctx); + gen_helper_packrl_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_PICK_QB: + check_dsp(ctx); + gen_helper_pick_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_PICK_PH: + check_dsp(ctx); + gen_helper_pick_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_ADDQ_S_W: + check_dsp(ctx); + gen_helper_addq_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_SUBQ_S_W: + check_dsp(ctx); + gen_helper_subq_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_ADDSC: + check_dsp(ctx); + gen_helper_addsc(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_ADDWC: + check_dsp(ctx); + gen_helper_addwc(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_ADDQ_S_PH: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDQ_PH */ + gen_helper_addq_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDQ_S_PH */ + gen_helper_addq_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDQH_R_PH: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDQH_PH */ + gen_helper_addqh_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDQH_R_PH */ + gen_helper_addqh_r_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDQH_R_W: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDQH_W */ + gen_helper_addqh_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDQH_R_W */ + gen_helper_addqh_r_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDU_S_QB: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDU_QB */ + gen_helper_addu_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDU_S_QB */ + gen_helper_addu_s_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDU_S_PH: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDU_PH */ + gen_helper_addu_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDU_S_PH */ + gen_helper_addu_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDUH_R_QB: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDUH_QB */ + gen_helper_adduh_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDUH_R_QB */ + gen_helper_adduh_r_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SHRAV_R_PH: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SHRAV_PH */ + gen_helper_shra_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SHRAV_R_PH */ + gen_helper_shra_r_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SHRAV_R_QB: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SHRAV_QB */ + gen_helper_shra_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SHRAV_R_QB */ + gen_helper_shra_r_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBQ_S_PH: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBQ_PH */ + gen_helper_subq_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBQ_S_PH */ + gen_helper_subq_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBQH_R_PH: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBQH_PH */ + gen_helper_subqh_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBQH_R_PH */ + gen_helper_subqh_r_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBQH_R_W: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBQH_W */ + gen_helper_subqh_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBQH_R_W */ + gen_helper_subqh_r_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBU_S_QB: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBU_QB */ + gen_helper_subu_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBU_S_QB */ + gen_helper_subu_s_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBU_S_PH: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBU_PH */ + gen_helper_subu_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBU_S_PH */ + gen_helper_subu_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBUH_R_QB: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBUH_QB */ + gen_helper_subuh_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBUH_R_QB */ + gen_helper_subuh_r_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SHLLV_S_PH: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SHLLV_PH */ + gen_helper_shll_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SHLLV_S_PH */ + gen_helper_shll_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_PRECR_SRA_R_PH_W: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* PRECR_SRA_PH_W */ + { + TCGv_i32 sa_t = tcg_const_i32(rd); + gen_helper_precr_sra_ph_w(v1_t, sa_t, v1_t, + cpu_gpr[rt]); + gen_store_gpr(v1_t, rt); + tcg_temp_free_i32(sa_t); + } + break; + case 1: + /* PRECR_SRA_R_PH_W */ + { + TCGv_i32 sa_t = tcg_const_i32(rd); + gen_helper_precr_sra_r_ph_w(v1_t, sa_t, v1_t, + cpu_gpr[rt]); + gen_store_gpr(v1_t, rt); + tcg_temp_free_i32(sa_t); + } + break; + } + break; + case NM_MULEU_S_PH_QBL: + check_dsp(ctx); + gen_helper_muleu_s_ph_qbl(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULEU_S_PH_QBR: + check_dsp(ctx); + gen_helper_muleu_s_ph_qbr(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULQ_RS_PH: + check_dsp(ctx); + gen_helper_mulq_rs_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULQ_S_PH: + check_dspr2(ctx); + gen_helper_mulq_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULQ_RS_W: + check_dspr2(ctx); + gen_helper_mulq_rs_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULQ_S_W: + check_dspr2(ctx); + gen_helper_mulq_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_APPEND: + check_dspr2(ctx); + gen_load_gpr(t0, rs); + if (rd != 0) { + tcg_gen_deposit_tl(cpu_gpr[rt], t0, cpu_gpr[rt], rd, 32 - rd); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + break; + case NM_MODSUB: + check_dsp(ctx); + gen_helper_modsub(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_SHRAV_R_W: + check_dsp(ctx); + gen_helper_shra_r_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_SHRLV_PH: + check_dspr2(ctx); + gen_helper_shrl_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_SHRLV_QB: + check_dsp(ctx); + gen_helper_shrl_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_SHLLV_QB: + check_dsp(ctx); + gen_helper_shll_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_SHLLV_S_W: + check_dsp(ctx); + gen_helper_shll_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_SHILO: + check_dsp(ctx); + { + TCGv tv0 = tcg_temp_new(); + TCGv tv1 = tcg_temp_new(); + int16_t imm = extract32(ctx->opcode, 16, 7); + + tcg_gen_movi_tl(tv0, rd >> 3); + tcg_gen_movi_tl(tv1, imm); + gen_helper_shilo(tv0, tv1, cpu_env); + } + break; + case NM_MULEQ_S_W_PHL: + check_dsp(ctx); + gen_helper_muleq_s_w_phl(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULEQ_S_W_PHR: + check_dsp(ctx); + gen_helper_muleq_s_w_phr(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MUL_S_PH: + check_dspr2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* MUL_PH */ + gen_helper_mul_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* MUL_S_PH */ + gen_helper_mul_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_PRECR_QB_PH: + check_dspr2(ctx); + gen_helper_precr_qb_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_PRECRQ_QB_PH: + check_dsp(ctx); + gen_helper_precrq_qb_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_PRECRQ_PH_W: + check_dsp(ctx); + gen_helper_precrq_ph_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_PRECRQ_RS_PH_W: + check_dsp(ctx); + gen_helper_precrq_rs_ph_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_PRECRQU_S_QB_PH: + check_dsp(ctx); + gen_helper_precrqu_s_qb_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_SHRA_R_W: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd); + gen_helper_shra_r_w(v1_t, t0, v1_t); + gen_store_gpr(v1_t, rt); + break; + case NM_SHRA_R_PH: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 1); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SHRA_PH */ + gen_helper_shra_ph(v1_t, t0, v1_t); + break; + gen_store_gpr(v1_t, rt); + case 1: + /* SHRA_R_PH */ + gen_helper_shra_r_ph(v1_t, t0, v1_t); + gen_store_gpr(v1_t, rt); + break; + } + break; + case NM_SHLL_S_PH: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 1); + switch (extract32(ctx->opcode, 10, 2)) { + case 0: + /* SHLL_PH */ + gen_helper_shll_ph(v1_t, t0, v1_t, cpu_env); + gen_store_gpr(v1_t, rt); + break; + case 2: + /* SHLL_S_PH */ + gen_helper_shll_s_ph(v1_t, t0, v1_t, cpu_env); + gen_store_gpr(v1_t, rt); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_SHLL_S_W: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd); + gen_helper_shll_s_w(v1_t, t0, v1_t, cpu_env); + gen_store_gpr(v1_t, rt); + break; + case NM_REPL_PH: + check_dsp(ctx); + { + int16_t imm; + imm = sextract32(ctx->opcode, 11, 11); + imm = (int16_t)(imm << 6) >> 6; + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], dup_const(MO_16, imm)); + } + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) +{ + uint16_t insn; + uint32_t op; + int rt, rs, rd; + int offset; + int imm; + + insn = cpu_lduw_code(env, ctx->base.pc_next + 2); + ctx->opcode = (ctx->opcode << 16) | insn; + + rt = extract32(ctx->opcode, 21, 5); + rs = extract32(ctx->opcode, 16, 5); + rd = extract32(ctx->opcode, 11, 5); + + op = extract32(ctx->opcode, 26, 6); + switch (op) { + case NM_P_ADDIU: + if (rt == 0) { + /* P.RI */ + switch (extract32(ctx->opcode, 19, 2)) { + case NM_SIGRIE: + default: + generate_exception_end(ctx, EXCP_RI); + break; + case NM_P_SYSCALL: + if ((extract32(ctx->opcode, 18, 1)) == NM_SYSCALL) { + generate_exception_end(ctx, EXCP_SYSCALL); + } else { + generate_exception_end(ctx, EXCP_RI); + } + break; + case NM_BREAK: + generate_exception_end(ctx, EXCP_BREAK); + break; + case NM_SDBBP: + if (is_uhi(extract32(ctx->opcode, 0, 19))) { + gen_helper_do_semihosting(cpu_env); + } else { + if (ctx->hflags & MIPS_HFLAG_SBRI) { + generate_exception_end(ctx, EXCP_RI); + } else { + generate_exception_end(ctx, EXCP_DBp); + } + } + break; + } + } else { + /* NM_ADDIU */ + imm = extract32(ctx->opcode, 0, 16); + if (rs != 0) { + tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], imm); + } else { + tcg_gen_movi_tl(cpu_gpr[rt], imm); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + } + break; + case NM_ADDIUPC: + if (rt != 0) { + offset = sextract32(ctx->opcode, 0, 1) << 21 | + extract32(ctx->opcode, 1, 20) << 1; + target_long addr = addr_add(ctx, ctx->base.pc_next + 4, offset); + tcg_gen_movi_tl(cpu_gpr[rt], addr); + } + break; + case NM_POOL32A: + switch (ctx->opcode & 0x07) { + case NM_POOL32A0: + gen_pool32a0_nanomips_insn(env, ctx); + break; + case NM_POOL32A5: + { + int32_t op1 = extract32(ctx->opcode, 3, 7); + gen_pool32a5_nanomips_insn(ctx, op1, rd, rs, rt); + } + break; + case NM_POOL32A7: + switch (extract32(ctx->opcode, 3, 3)) { + case NM_P_LSX: + gen_p_lsx(ctx, rd, rs, rt); + break; + case NM_LSA: + /* In nanoMIPS, the shift field directly encodes the shift + * amount, meaning that the supported shift values are in + * the range 0 to 3 (instead of 1 to 4 in MIPSR6). */ + gen_lsa(ctx, OPC_LSA, rd, rs, rt, + extract32(ctx->opcode, 9, 2) - 1); + break; + case NM_EXTW: + gen_ext(ctx, 32, rd, rs, rt, extract32(ctx->opcode, 6, 5)); + break; + case NM_POOL32AXF: + gen_pool32axf_nanomips_insn(env, ctx); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_GP_W: + switch (ctx->opcode & 0x03) { + case NM_ADDIUGP_W: + if (rt != 0) { + offset = extract32(ctx->opcode, 0, 21); + gen_op_addr_addi(ctx, cpu_gpr[rt], cpu_gpr[28], offset); + } + break; + case NM_LWGP: + gen_ld(ctx, OPC_LW, rt, 28, extract32(ctx->opcode, 2, 19) << 2); + break; + case NM_SWGP: + gen_st(ctx, OPC_SW, rt, 28, extract32(ctx->opcode, 2, 19) << 2); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P48I: + { + insn = cpu_lduw_code(env, ctx->base.pc_next + 4); + target_long addr_off = extract32(ctx->opcode, 0, 16) | insn << 16; + switch (extract32(ctx->opcode, 16, 5)) { + case NM_LI48: + check_nms(ctx); + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], addr_off); + } + break; + case NM_ADDIU48: + check_nms(ctx); + if (rt != 0) { + tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rt], addr_off); + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + } + break; + case NM_ADDIUGP48: + check_nms(ctx); + if (rt != 0) { + gen_op_addr_addi(ctx, cpu_gpr[rt], cpu_gpr[28], addr_off); + } + break; + case NM_ADDIUPC48: + check_nms(ctx); + if (rt != 0) { + target_long addr = addr_add(ctx, ctx->base.pc_next + 6, + addr_off); + + tcg_gen_movi_tl(cpu_gpr[rt], addr); + } + break; + case NM_LWPC48: + check_nms(ctx); + if (rt != 0) { + TCGv t0; + t0 = tcg_temp_new(); + + target_long addr = addr_add(ctx, ctx->base.pc_next + 6, + addr_off); + + tcg_gen_movi_tl(t0, addr); + tcg_gen_qemu_ld_tl(cpu_gpr[rt], t0, ctx->mem_idx, MO_TESL); + tcg_temp_free(t0); + } + break; + case NM_SWPC48: + check_nms(ctx); + { + TCGv t0, t1; + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + target_long addr = addr_add(ctx, ctx->base.pc_next + 6, + addr_off); + + tcg_gen_movi_tl(t0, addr); + gen_load_gpr(t1, rt); + + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); + + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + return 6; + } + case NM_P_U12: + switch (extract32(ctx->opcode, 12, 4)) { + case NM_ORI: + gen_logic_imm(ctx, OPC_ORI, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_XORI: + gen_logic_imm(ctx, OPC_XORI, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_ANDI: + gen_logic_imm(ctx, OPC_ANDI, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_P_SR: + switch (extract32(ctx->opcode, 20, 1)) { + case NM_PP_SR: + switch (ctx->opcode & 3) { + case NM_SAVE: + gen_save(ctx, rt, extract32(ctx->opcode, 16, 4), + extract32(ctx->opcode, 2, 1), + extract32(ctx->opcode, 3, 9) << 3); + break; + case NM_RESTORE: + case NM_RESTORE_JRC: + gen_restore(ctx, rt, extract32(ctx->opcode, 16, 4), + extract32(ctx->opcode, 2, 1), + extract32(ctx->opcode, 3, 9) << 3); + if ((ctx->opcode & 3) == NM_RESTORE_JRC) { + gen_compute_branch_nm(ctx, OPC_JR, 2, 31, 0, 0); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_SR_F: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_SLTI: + gen_slt_imm(ctx, OPC_SLTI, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_SLTIU: + gen_slt_imm(ctx, OPC_SLTIU, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_SEQI: + { + TCGv t0 = tcg_temp_new(); + + imm = extract32(ctx->opcode, 0, 12); + gen_load_gpr(t0, rs); + tcg_gen_setcondi_tl(TCG_COND_EQ, t0, t0, imm); + gen_store_gpr(t0, rt); + + tcg_temp_free(t0); + } + break; + case NM_ADDIUNEG: + imm = (int16_t) extract32(ctx->opcode, 0, 12); + gen_arith_imm(ctx, OPC_ADDIU, rt, rs, -imm); + break; + case NM_P_SHIFT: + { + int shift = extract32(ctx->opcode, 0, 5); + switch (extract32(ctx->opcode, 5, 4)) { + case NM_P_SLL: + if (rt == 0 && shift == 0) { + /* NOP */ + } else if (rt == 0 && shift == 3) { + /* EHB - treat as NOP */ + } else if (rt == 0 && shift == 5) { + /* PAUSE - treat as NOP */ + } else if (rt == 0 && shift == 6) { + /* SYNC */ + gen_sync(extract32(ctx->opcode, 16, 5)); + } else { + /* SLL */ + gen_shift_imm(ctx, OPC_SLL, rt, rs, + extract32(ctx->opcode, 0, 5)); + } + break; + case NM_SRL: + gen_shift_imm(ctx, OPC_SRL, rt, rs, + extract32(ctx->opcode, 0, 5)); + break; + case NM_SRA: + gen_shift_imm(ctx, OPC_SRA, rt, rs, + extract32(ctx->opcode, 0, 5)); + break; + case NM_ROTR: + gen_shift_imm(ctx, OPC_ROTR, rt, rs, + extract32(ctx->opcode, 0, 5)); + break; + } + } + break; + case NM_P_ROTX: + check_nms(ctx); + if (rt != 0) { + TCGv t0 = tcg_temp_new(); + TCGv_i32 shift = tcg_const_i32(extract32(ctx->opcode, 0, 5)); + TCGv_i32 shiftx = tcg_const_i32(extract32(ctx->opcode, 7, 4) + << 1); + TCGv_i32 stripe = tcg_const_i32(extract32(ctx->opcode, 6, 1)); + + gen_load_gpr(t0, rs); + gen_helper_rotx(cpu_gpr[rt], t0, shift, shiftx, stripe); + tcg_temp_free(t0); + + tcg_temp_free_i32(shift); + tcg_temp_free_i32(shiftx); + tcg_temp_free_i32(stripe); + } + break; + case NM_P_INS: + switch (((ctx->opcode >> 10) & 2) | + (extract32(ctx->opcode, 5, 1))) { + case NM_INS: + check_nms(ctx); + gen_bitops(ctx, OPC_INS, rt, rs, extract32(ctx->opcode, 0, 5), + extract32(ctx->opcode, 6, 5)); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_EXT: + switch (((ctx->opcode >> 10) & 2) | + (extract32(ctx->opcode, 5, 1))) { + case NM_EXT: + check_nms(ctx); + gen_bitops(ctx, OPC_EXT, rt, rs, extract32(ctx->opcode, 0, 5), + extract32(ctx->opcode, 6, 5)); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32F: + gen_pool32f_nanomips_insn(ctx); + break; + case NM_POOL32S: + break; + case NM_P_LUI: + switch (extract32(ctx->opcode, 1, 1)) { + case NM_LUI: + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], + sextract32(ctx->opcode, 0, 1) << 31 | + extract32(ctx->opcode, 2, 10) << 21 | + extract32(ctx->opcode, 12, 9) << 12); + } + break; + case NM_ALUIPC: + if (rt != 0) { + offset = sextract32(ctx->opcode, 0, 1) << 31 | + extract32(ctx->opcode, 2, 10) << 21 | + extract32(ctx->opcode, 12, 9) << 12; + target_long addr; + addr = ~0xFFF & addr_add(ctx, ctx->base.pc_next + 4, offset); + tcg_gen_movi_tl(cpu_gpr[rt], addr); + } + break; + } + break; + case NM_P_GP_BH: + { + uint32_t u = extract32(ctx->opcode, 0, 18); + + switch (extract32(ctx->opcode, 18, 3)) { + case NM_LBGP: + gen_ld(ctx, OPC_LB, rt, 28, u); + break; + case NM_SBGP: + gen_st(ctx, OPC_SB, rt, 28, u); + break; + case NM_LBUGP: + gen_ld(ctx, OPC_LBU, rt, 28, u); + break; + case NM_ADDIUGP_B: + if (rt != 0) { + gen_op_addr_addi(ctx, cpu_gpr[rt], cpu_gpr[28], u); + } + break; + case NM_P_GP_LH: + u &= ~1; + switch (ctx->opcode & 1) { + case NM_LHGP: + gen_ld(ctx, OPC_LH, rt, 28, u); + break; + case NM_LHUGP: + gen_ld(ctx, OPC_LHU, rt, 28, u); + break; + } + break; + case NM_P_GP_SH: + u &= ~1; + switch (ctx->opcode & 1) { + case NM_SHGP: + gen_st(ctx, OPC_SH, rt, 28, u); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_GP_CP1: + u &= ~0x3; + switch (ctx->opcode & 0x3) { + case NM_LWC1GP: + gen_cop1_ldst(ctx, OPC_LWC1, rt, 28, u); + break; + case NM_LDC1GP: + gen_cop1_ldst(ctx, OPC_LDC1, rt, 28, u); + break; + case NM_SWC1GP: + gen_cop1_ldst(ctx, OPC_SWC1, rt, 28, u); + break; + case NM_SDC1GP: + gen_cop1_ldst(ctx, OPC_SDC1, rt, 28, u); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_P_LS_U12: + { + uint32_t u = extract32(ctx->opcode, 0, 12); + + switch (extract32(ctx->opcode, 12, 4)) { + case NM_P_PREFU12: + if (rt == 31) { + /* SYNCI */ + /* Break the TB to be able to sync copied instructions + immediately */ + ctx->base.is_jmp = DISAS_STOP; + } else { + /* PREF */ + /* Treat as NOP. */ + } + break; + case NM_LB: + gen_ld(ctx, OPC_LB, rt, rs, u); + break; + case NM_LH: + gen_ld(ctx, OPC_LH, rt, rs, u); + break; + case NM_LW: + gen_ld(ctx, OPC_LW, rt, rs, u); + break; + case NM_LBU: + gen_ld(ctx, OPC_LBU, rt, rs, u); + break; + case NM_LHU: + gen_ld(ctx, OPC_LHU, rt, rs, u); + break; + case NM_SB: + gen_st(ctx, OPC_SB, rt, rs, u); + break; + case NM_SH: + gen_st(ctx, OPC_SH, rt, rs, u); + break; + case NM_SW: + gen_st(ctx, OPC_SW, rt, rs, u); + break; + case NM_LWC1: + gen_cop1_ldst(ctx, OPC_LWC1, rt, rs, u); + break; + case NM_LDC1: + gen_cop1_ldst(ctx, OPC_LDC1, rt, rs, u); + break; + case NM_SWC1: + gen_cop1_ldst(ctx, OPC_SWC1, rt, rs, u); + break; + case NM_SDC1: + gen_cop1_ldst(ctx, OPC_SDC1, rt, rs, u); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_P_LS_S9: + { + int32_t s = (sextract32(ctx->opcode, 15, 1) << 8) | + extract32(ctx->opcode, 0, 8); + + switch (extract32(ctx->opcode, 8, 3)) { + case NM_P_LS_S0: + switch (extract32(ctx->opcode, 11, 4)) { + case NM_LBS9: + gen_ld(ctx, OPC_LB, rt, rs, s); + break; + case NM_LHS9: + gen_ld(ctx, OPC_LH, rt, rs, s); + break; + case NM_LWS9: + gen_ld(ctx, OPC_LW, rt, rs, s); + break; + case NM_LBUS9: + gen_ld(ctx, OPC_LBU, rt, rs, s); + break; + case NM_LHUS9: + gen_ld(ctx, OPC_LHU, rt, rs, s); + break; + case NM_SBS9: + gen_st(ctx, OPC_SB, rt, rs, s); + break; + case NM_SHS9: + gen_st(ctx, OPC_SH, rt, rs, s); + break; + case NM_SWS9: + gen_st(ctx, OPC_SW, rt, rs, s); + break; + case NM_LWC1S9: + gen_cop1_ldst(ctx, OPC_LWC1, rt, rs, s); + break; + case NM_LDC1S9: + gen_cop1_ldst(ctx, OPC_LDC1, rt, rs, s); + break; + case NM_SWC1S9: + gen_cop1_ldst(ctx, OPC_SWC1, rt, rs, s); + break; + case NM_SDC1S9: + gen_cop1_ldst(ctx, OPC_SDC1, rt, rs, s); + break; + case NM_P_PREFS9: + if (rt == 31) { + /* SYNCI */ + /* Break the TB to be able to sync copied instructions + immediately */ + ctx->base.is_jmp = DISAS_STOP; + } else { + /* PREF */ + /* Treat as NOP. */ + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_LS_S1: + switch (extract32(ctx->opcode, 11, 4)) { + case NM_UALH: + case NM_UASH: + check_nms(ctx); + { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_base_offset_addr(ctx, t0, rs, s); + + switch (extract32(ctx->opcode, 11, 4)) { + case NM_UALH: + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW | + MO_UNALN); + gen_store_gpr(t0, rt); + break; + case NM_UASH: + gen_load_gpr(t1, rt); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW | + MO_UNALN); + break; + } + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + case NM_P_LL: + switch (ctx->opcode & 0x03) { + case NM_LL: + gen_ld(ctx, OPC_LL, rt, rs, s); + break; + case NM_LLWP: + check_xnp(ctx); + gen_llwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5)); + break; + } + break; + case NM_P_SC: + switch (ctx->opcode & 0x03) { + case NM_SC: + gen_st_cond(ctx, OPC_SC, rt, rs, s); + break; + case NM_SCWP: + check_xnp(ctx); + gen_scwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5)); + break; + } + break; + case NM_CACHE: + check_cp0_enabled(ctx); + if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) { + gen_cache_operation(ctx, rt, rs, s); + } + break; + } + break; + case NM_P_LS_WM: + case NM_P_LS_UAWM: + check_nms(ctx); + { + int count = extract32(ctx->opcode, 12, 3); + int counter = 0; + + offset = sextract32(ctx->opcode, 15, 1) << 8 | + extract32(ctx->opcode, 0, 8); + TCGv va = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGMemOp memop = (extract32(ctx->opcode, 8, 3)) == + NM_P_LS_UAWM ? MO_UNALN : 0; + + count = (count == 0) ? 8 : count; + while (counter != count) { + int this_rt = ((rt + counter) & 0x1f) | (rt & 0x10); + int this_offset = offset + (counter << 2); + + gen_base_offset_addr(ctx, va, rs, this_offset); + + switch (extract32(ctx->opcode, 11, 1)) { + case NM_LWM: + tcg_gen_qemu_ld_tl(t1, va, ctx->mem_idx, + memop | MO_TESL); + gen_store_gpr(t1, this_rt); + if ((this_rt == rs) && + (counter != (count - 1))) { + /* UNPREDICTABLE */ + } + break; + case NM_SWM: + this_rt = (rt == 0) ? 0 : this_rt; + gen_load_gpr(t1, this_rt); + tcg_gen_qemu_st_tl(t1, va, ctx->mem_idx, + memop | MO_TEUL); + break; + } + counter++; + } + tcg_temp_free(va); + tcg_temp_free(t1); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_MOVE_BALC: + check_nms(ctx); + { + TCGv t0 = tcg_temp_new(); + int32_t s = sextract32(ctx->opcode, 0, 1) << 21 | + extract32(ctx->opcode, 1, 20) << 1; + rd = (extract32(ctx->opcode, 24, 1)) == 0 ? 4 : 5; + rt = decode_gpr_gpr4_zero(extract32(ctx->opcode, 25, 1) << 3 | + extract32(ctx->opcode, 21, 3)); + gen_load_gpr(t0, rt); + tcg_gen_mov_tl(cpu_gpr[rd], t0); + gen_compute_branch_nm(ctx, OPC_BGEZAL, 4, 0, 0, s); + tcg_temp_free(t0); + } + break; + case NM_P_BAL: + { + int32_t s = sextract32(ctx->opcode, 0, 1) << 25 | + extract32(ctx->opcode, 1, 24) << 1; + + if ((extract32(ctx->opcode, 25, 1)) == 0) { + /* BC */ + gen_compute_branch_nm(ctx, OPC_BEQ, 4, 0, 0, s); + } else { + /* BALC */ + gen_compute_branch_nm(ctx, OPC_BGEZAL, 4, 0, 0, s); + } + } + break; + case NM_P_J: + switch (extract32(ctx->opcode, 12, 4)) { + case NM_JALRC: + case NM_JALRC_HB: + gen_compute_branch_nm(ctx, OPC_JALR, 4, rs, rt, 0); + break; + case NM_P_BALRSC: + gen_compute_nanomips_pbalrsc_branch(ctx, rs, rt); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_BR1: + { + int32_t s = sextract32(ctx->opcode, 0, 1) << 14 | + extract32(ctx->opcode, 1, 13) << 1; + switch (extract32(ctx->opcode, 14, 2)) { + case NM_BEQC: + check_nms(ctx); + gen_compute_branch_nm(ctx, OPC_BEQ, 4, rs, rt, s); + break; + case NM_P_BR3A: + s = sextract32(ctx->opcode, 0, 1) << 14 | + extract32(ctx->opcode, 1, 13) << 1; + check_cp1_enabled(ctx); + switch (extract32(ctx->opcode, 16, 5)) { + case NM_BC1EQZC: + gen_compute_branch_cp1_nm(ctx, OPC_BC1EQZ, rt, s); + break; + case NM_BC1NEZC: + gen_compute_branch_cp1_nm(ctx, OPC_BC1NEZ, rt, s); + break; + case NM_BPOSGE32C: + check_dspr2(ctx); + { + int32_t imm = extract32(ctx->opcode, 1, 13) | + extract32(ctx->opcode, 0, 1) << 13; + + gen_compute_branch_nm(ctx, OPC_BPOSGE32, 4, -1, -2, + imm); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_BGEC: + if (rs == rt) { + gen_compute_compact_branch_nm(ctx, OPC_BC, rs, rt, s); + } else { + gen_compute_compact_branch_nm(ctx, OPC_BGEC, rs, rt, s); + } + break; + case NM_BGEUC: + if (rs == rt || rt == 0) { + gen_compute_compact_branch_nm(ctx, OPC_BC, 0, 0, s); + } else if (rs == 0) { + gen_compute_compact_branch_nm(ctx, OPC_BEQZC, rt, 0, s); + } else { + gen_compute_compact_branch_nm(ctx, OPC_BGEUC, rs, rt, s); + } + break; + } + } + break; + case NM_P_BR2: + { + int32_t s = sextract32(ctx->opcode, 0, 1) << 14 | + extract32(ctx->opcode, 1, 13) << 1; + switch (extract32(ctx->opcode, 14, 2)) { + case NM_BNEC: + check_nms(ctx); + gen_compute_branch_nm(ctx, OPC_BNE, 4, rs, rt, s); + break; + case NM_BLTC: + if (rs != 0 && rt != 0 && rs == rt) { + /* NOP */ + ctx->hflags |= MIPS_HFLAG_FBNSLOT; + } else { + gen_compute_compact_branch_nm(ctx, OPC_BLTC, rs, rt, s); + } + break; + case NM_BLTUC: + if (rs == 0 || rs == rt) { + /* NOP */ + ctx->hflags |= MIPS_HFLAG_FBNSLOT; + } else { + gen_compute_compact_branch_nm(ctx, OPC_BLTUC, rs, rt, s); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_P_BRI: + { + int32_t s = sextract32(ctx->opcode, 0, 1) << 11 | + extract32(ctx->opcode, 1, 10) << 1; + uint32_t u = extract32(ctx->opcode, 11, 7); + + gen_compute_imm_branch(ctx, extract32(ctx->opcode, 18, 3), + rt, u, s); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + return 4; +} + +static int decode_nanomips_opc(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t op; + int rt = decode_gpr_gpr3(NANOMIPS_EXTRACT_RD(ctx->opcode)); + int rs = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS(ctx->opcode)); + int rd = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS1(ctx->opcode)); + int offset; + int imm; + + /* make sure instructions are on a halfword boundary */ + if (ctx->base.pc_next & 0x1) { + TCGv tmp = tcg_const_tl(ctx->base.pc_next); + tcg_gen_st_tl(tmp, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); + tcg_temp_free(tmp); + generate_exception_end(ctx, EXCP_AdEL); + return 2; + } + + op = extract32(ctx->opcode, 10, 6); + switch (op) { + case NM_P16_MV: + rt = NANOMIPS_EXTRACT_RD5(ctx->opcode); + if (rt != 0) { + /* MOVE */ + rs = NANOMIPS_EXTRACT_RS5(ctx->opcode); + gen_arith(ctx, OPC_ADDU, rt, rs, 0); + } else { + /* P16.RI */ + switch (extract32(ctx->opcode, 3, 2)) { + case NM_P16_SYSCALL: + if (extract32(ctx->opcode, 2, 1) == 0) { + generate_exception_end(ctx, EXCP_SYSCALL); + } else { + generate_exception_end(ctx, EXCP_RI); + } + break; + case NM_BREAK16: + generate_exception_end(ctx, EXCP_BREAK); + break; + case NM_SDBBP16: + if (is_uhi(extract32(ctx->opcode, 0, 3))) { + gen_helper_do_semihosting(cpu_env); + } else { + if (ctx->hflags & MIPS_HFLAG_SBRI) { + generate_exception_end(ctx, EXCP_RI); + } else { + generate_exception_end(ctx, EXCP_DBp); + } + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_P16_SHIFT: + { + int shift = extract32(ctx->opcode, 0, 3); + uint32_t opc = 0; + shift = (shift == 0) ? 8 : shift; + + switch (extract32(ctx->opcode, 3, 1)) { + case NM_SLL16: + opc = OPC_SLL; + break; + case NM_SRL16: + opc = OPC_SRL; + break; + } + gen_shift_imm(ctx, opc, rt, rs, shift); + } + break; + case NM_P16C: + switch (ctx->opcode & 1) { + case NM_POOL16C_0: + gen_pool16c_nanomips_insn(ctx); + break; + case NM_LWXS16: + gen_ldxs(ctx, rt, rs, rd); + break; + } + break; + case NM_P16_A1: + switch (extract32(ctx->opcode, 6, 1)) { + case NM_ADDIUR1SP: + imm = extract32(ctx->opcode, 0, 6) << 2; + gen_arith_imm(ctx, OPC_ADDIU, rt, 29, imm); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P16_A2: + switch (extract32(ctx->opcode, 3, 1)) { + case NM_ADDIUR2: + imm = extract32(ctx->opcode, 0, 3) << 2; + gen_arith_imm(ctx, OPC_ADDIU, rt, rs, imm); + break; + case NM_P_ADDIURS5: + rt = extract32(ctx->opcode, 5, 5); + if (rt != 0) { + /* imm = sign_extend(s[3] . s[2:0] , from_nbits = 4) */ + imm = (sextract32(ctx->opcode, 4, 1) << 3) | + (extract32(ctx->opcode, 0, 3)); + gen_arith_imm(ctx, OPC_ADDIU, rt, rt, imm); + } + break; + } + break; + case NM_P16_ADDU: + switch (ctx->opcode & 0x1) { + case NM_ADDU16: + gen_arith(ctx, OPC_ADDU, rd, rs, rt); + break; + case NM_SUBU16: + gen_arith(ctx, OPC_SUBU, rd, rs, rt); + break; + } + break; + case NM_P16_4X4: + rt = (extract32(ctx->opcode, 9, 1) << 3) | + extract32(ctx->opcode, 5, 3); + rs = (extract32(ctx->opcode, 4, 1) << 3) | + extract32(ctx->opcode, 0, 3); + rt = decode_gpr_gpr4(rt); + rs = decode_gpr_gpr4(rs); + switch ((extract32(ctx->opcode, 7, 2) & 0x2) | + (extract32(ctx->opcode, 3, 1))) { + case NM_ADDU4X4: + check_nms(ctx); + gen_arith(ctx, OPC_ADDU, rt, rs, rt); + break; + case NM_MUL4X4: + check_nms(ctx); + gen_r6_muldiv(ctx, R6_OPC_MUL, rt, rs, rt); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_LI16: + { + int imm = extract32(ctx->opcode, 0, 7); + imm = (imm == 0x7f ? -1 : imm); + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], imm); + } + } + break; + case NM_ANDI16: + { + uint32_t u = extract32(ctx->opcode, 0, 4); + u = (u == 12) ? 0xff : + (u == 13) ? 0xffff : u; + gen_logic_imm(ctx, OPC_ANDI, rt, rs, u); + } + break; + case NM_P16_LB: + offset = extract32(ctx->opcode, 0, 2); + switch (extract32(ctx->opcode, 2, 2)) { + case NM_LB16: + gen_ld(ctx, OPC_LB, rt, rs, offset); + break; + case NM_SB16: + rt = decode_gpr_gpr3_src_store( + NANOMIPS_EXTRACT_RD(ctx->opcode)); + gen_st(ctx, OPC_SB, rt, rs, offset); + break; + case NM_LBU16: + gen_ld(ctx, OPC_LBU, rt, rs, offset); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P16_LH: + offset = extract32(ctx->opcode, 1, 2) << 1; + switch ((extract32(ctx->opcode, 3, 1) << 1) | (ctx->opcode & 1)) { + case NM_LH16: + gen_ld(ctx, OPC_LH, rt, rs, offset); + break; + case NM_SH16: + rt = decode_gpr_gpr3_src_store( + NANOMIPS_EXTRACT_RD(ctx->opcode)); + gen_st(ctx, OPC_SH, rt, rs, offset); + break; + case NM_LHU16: + gen_ld(ctx, OPC_LHU, rt, rs, offset); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_LW16: + offset = extract32(ctx->opcode, 0, 4) << 2; + gen_ld(ctx, OPC_LW, rt, rs, offset); + break; + case NM_LWSP16: + rt = NANOMIPS_EXTRACT_RD5(ctx->opcode); + offset = extract32(ctx->opcode, 0, 5) << 2; + gen_ld(ctx, OPC_LW, rt, 29, offset); + break; + case NM_LW4X4: + check_nms(ctx); + rt = (extract32(ctx->opcode, 9, 1) << 3) | + extract32(ctx->opcode, 5, 3); + rs = (extract32(ctx->opcode, 4, 1) << 3) | + extract32(ctx->opcode, 0, 3); + offset = (extract32(ctx->opcode, 3, 1) << 3) | + (extract32(ctx->opcode, 8, 1) << 2); + rt = decode_gpr_gpr4(rt); + rs = decode_gpr_gpr4(rs); + gen_ld(ctx, OPC_LW, rt, rs, offset); + break; + case NM_SW4X4: + check_nms(ctx); + rt = (extract32(ctx->opcode, 9, 1) << 3) | + extract32(ctx->opcode, 5, 3); + rs = (extract32(ctx->opcode, 4, 1) << 3) | + extract32(ctx->opcode, 0, 3); + offset = (extract32(ctx->opcode, 3, 1) << 3) | + (extract32(ctx->opcode, 8, 1) << 2); + rt = decode_gpr_gpr4_zero(rt); + rs = decode_gpr_gpr4(rs); + gen_st(ctx, OPC_SW, rt, rs, offset); + break; + case NM_LWGP16: + offset = extract32(ctx->opcode, 0, 7) << 2; + gen_ld(ctx, OPC_LW, rt, 28, offset); + break; + case NM_SWSP16: + rt = NANOMIPS_EXTRACT_RD5(ctx->opcode); + offset = extract32(ctx->opcode, 0, 5) << 2; + gen_st(ctx, OPC_SW, rt, 29, offset); + break; + case NM_SW16: + rt = decode_gpr_gpr3_src_store( + NANOMIPS_EXTRACT_RD(ctx->opcode)); + rs = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS(ctx->opcode)); + offset = extract32(ctx->opcode, 0, 4) << 2; + gen_st(ctx, OPC_SW, rt, rs, offset); + break; + case NM_SWGP16: + rt = decode_gpr_gpr3_src_store( + NANOMIPS_EXTRACT_RD(ctx->opcode)); + offset = extract32(ctx->opcode, 0, 7) << 2; + gen_st(ctx, OPC_SW, rt, 28, offset); + break; + case NM_BC16: + gen_compute_branch_nm(ctx, OPC_BEQ, 2, 0, 0, + (sextract32(ctx->opcode, 0, 1) << 10) | + (extract32(ctx->opcode, 1, 9) << 1)); + break; + case NM_BALC16: + gen_compute_branch_nm(ctx, OPC_BGEZAL, 2, 0, 0, + (sextract32(ctx->opcode, 0, 1) << 10) | + (extract32(ctx->opcode, 1, 9) << 1)); + break; + case NM_BEQZC16: + gen_compute_branch_nm(ctx, OPC_BEQ, 2, rt, 0, + (sextract32(ctx->opcode, 0, 1) << 7) | + (extract32(ctx->opcode, 1, 6) << 1)); + break; + case NM_BNEZC16: + gen_compute_branch_nm(ctx, OPC_BNE, 2, rt, 0, + (sextract32(ctx->opcode, 0, 1) << 7) | + (extract32(ctx->opcode, 1, 6) << 1)); + break; + case NM_P16_BR: + switch (ctx->opcode & 0xf) { + case 0: + /* P16.JRC */ + switch (extract32(ctx->opcode, 4, 1)) { + case NM_JRC: + gen_compute_branch_nm(ctx, OPC_JR, 2, + extract32(ctx->opcode, 5, 5), 0, 0); + break; + case NM_JALRC16: + gen_compute_branch_nm(ctx, OPC_JALR, 2, + extract32(ctx->opcode, 5, 5), 31, 0); + break; + } + break; + default: + { + /* P16.BRI */ + uint32_t opc = extract32(ctx->opcode, 4, 3) < + extract32(ctx->opcode, 7, 3) ? OPC_BEQ : OPC_BNE; + gen_compute_branch_nm(ctx, opc, 2, rs, rt, + extract32(ctx->opcode, 0, 4) << 1); + } + break; + } + break; + case NM_P16_SR: + { + int count = extract32(ctx->opcode, 0, 4); + int u = extract32(ctx->opcode, 4, 4) << 4; + + rt = 30 + extract32(ctx->opcode, 9, 1); + switch (extract32(ctx->opcode, 8, 1)) { + case NM_SAVE16: + gen_save(ctx, rt, count, 0, u); + break; + case NM_RESTORE_JRC16: + gen_restore(ctx, rt, count, 0, u); + gen_compute_branch_nm(ctx, OPC_JR, 2, 31, 0, 0); + break; + } + } + break; + case NM_MOVEP: + case NM_MOVEPREV: + check_nms(ctx); + { + static const int gpr2reg1[] = {4, 5, 6, 7}; + static const int gpr2reg2[] = {5, 6, 7, 8}; + int re; + int rd2 = extract32(ctx->opcode, 3, 1) << 1 | + extract32(ctx->opcode, 8, 1); + int r1 = gpr2reg1[rd2]; + int r2 = gpr2reg2[rd2]; + int r3 = extract32(ctx->opcode, 4, 1) << 3 | + extract32(ctx->opcode, 0, 3); + int r4 = extract32(ctx->opcode, 9, 1) << 3 | + extract32(ctx->opcode, 5, 3); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + if (op == NM_MOVEP) { + rd = r1; + re = r2; + rs = decode_gpr_gpr4_zero(r3); + rt = decode_gpr_gpr4_zero(r4); + } else { + rd = decode_gpr_gpr4(r3); + re = decode_gpr_gpr4(r4); + rs = r1; + rt = r2; + } + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_mov_tl(cpu_gpr[rd], t0); + tcg_gen_mov_tl(cpu_gpr[re], t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + default: + return decode_nanomips_32_48_opc(env, ctx); + } + + return 2; +} + + /* SmartMIPS extension to MIPS32 */ #if defined(TARGET_MIPS64) @@ -17901,7 +22739,7 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) switch (op2) { case OPC_ALIGN: case OPC_ALIGN_END: - gen_align(ctx, OPC_ALIGN, rd, rs, rt, sa & 3); + gen_align(ctx, 32, rd, rs, rt, sa & 3); break; case OPC_BITSWAP: gen_bitswap(ctx, op2, rd, rt); @@ -17927,7 +22765,7 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) switch (op2) { case OPC_DALIGN: case OPC_DALIGN_END: - gen_align(ctx, OPC_DALIGN, rd, rs, rt, sa & 7); + gen_align(ctx, 64, rd, rs, rt, sa & 7); break; case OPC_DBITSWAP: gen_bitswap(ctx, op2, rd, rt); @@ -18586,7 +23424,7 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) gen_rdhwr(ctx, rt, rd, extract32(ctx->opcode, 6, 3)); break; case OPC_FORK: - check_insn(ctx, ASE_MT); + check_mt(ctx); { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -18599,7 +23437,7 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) } break; case OPC_YIELD: - check_insn(ctx, ASE_MT); + check_mt(ctx); { TCGv t0 = tcg_temp_new(); @@ -19896,22 +24734,22 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) op2 = MASK_MFMC0(ctx->opcode); switch (op2) { case OPC_DMT: - check_insn(ctx, ASE_MT); + check_cp0_mt(ctx); gen_helper_dmt(t0); gen_store_gpr(t0, rt); break; case OPC_EMT: - check_insn(ctx, ASE_MT); + check_cp0_mt(ctx); gen_helper_emt(t0); gen_store_gpr(t0, rt); break; case OPC_DVPE: - check_insn(ctx, ASE_MT); + check_cp0_mt(ctx); gen_helper_dvpe(t0, cpu_env); gen_store_gpr(t0, rt); break; case OPC_EVPE: - check_insn(ctx, ASE_MT); + check_cp0_mt(ctx); gen_helper_evpe(t0, cpu_env); gen_store_gpr(t0, rt); break; @@ -20447,6 +25285,8 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->saved_pc = -1; ctx->insn_flags = env->insn_flags; ctx->CP0_Config1 = env->CP0_Config1; + ctx->CP0_Config3 = env->CP0_Config3; + ctx->CP0_Config5 = env->CP0_Config5; ctx->btarget = 0; ctx->kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; ctx->rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; @@ -20517,7 +25357,10 @@ static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) int is_slot; is_slot = ctx->hflags & MIPS_HFLAG_BMASK; - if (!(ctx->hflags & MIPS_HFLAG_M16)) { + if (ctx->insn_flags & ISA_NANOMIPS32) { + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + insn_bytes = decode_nanomips_opc(env, ctx); + } else if (!(ctx->hflags & MIPS_HFLAG_M16)) { ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); insn_bytes = 4; decode_opc(env, ctx); @@ -20956,8 +25799,8 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Status |= (1 << CP0St_FR); } - if (env->CP0_Config3 & (1 << CP0C3_ISA)) { - /* microMIPS on reset when Config3.ISA == {1, 3} */ + if (env->CP0_Config3 & (1 << CP0C3_ISA) & (1 << (CP0C3_ISA + 1))) { + /* microMIPS on reset when Config3.ISA is 3 */ env->hflags |= MIPS_HFLAG_M16; } diff --git a/target/mips/translate_init.inc.c b/target/mips/translate_init.inc.c index c7ba6ee5f9..b3320b9dc7 100644 --- a/target/mips/translate_init.inc.c +++ b/target/mips/translate_init.inc.c @@ -449,6 +449,45 @@ const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS32R6 | ASE_MICROMIPS, .mmu_type = MMU_TYPE_R4000, }, + { + .name = "I7200", + .CP0_PRid = 0x00010000, + .CP0_Config0 = MIPS_CONFIG0 | (1 << CP0C0_MM) | (0x2 << CP0C0_AR) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = (1U << CP0C1_M) | (15 << CP0C1_MMU) | (2 << CP0C1_IS) | + (4 << CP0C1_IL) | (3 << CP0C1_IA) | (2 << CP0C1_DS) | + (4 << CP0C1_DL) | (3 << CP0C1_DA) | (1 << CP0C1_PC) | + (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_CMGCR) | + (1 << CP0C3_BI) | (1 << CP0C3_SC) | (3 << CP0C3_MMAR) | + (1 << CP0C3_ISA_ON_EXC) | (1 << CP0C3_ISA) | + (1 << CP0C3_ULRI) | (1 << CP0C3_RXI) | + (1 << CP0C3_DSP2P) | (1 << CP0C3_DSPP) | + (1 << CP0C3_CTXTC) | (1 << CP0C3_VInt) | + (1 << CP0C3_CDMM) | (1 << CP0C3_MT) | (1 << CP0C3_TL), + .CP0_Config4 = MIPS_CONFIG4 | (0xfc << CP0C4_KScrExist) | + (2 << CP0C4_IE) | (1U << CP0C4_M), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_MVH) | (1 << CP0C5_LLB), + .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | + (1 << CP0C5_UFE), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 0, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x3158FF1F, + .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | + (1U << CP0PG_RIE), + .CP0_PageGrain_rw_bitmask = 0, + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_HAS2008) | (1 << FCR0_F64) | + (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | + (1 << FCR0_S) | (0x02 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), + .SEGBITS = 32, + .PABITS = 32, + .insn_flags = CPU_NANOMIPS32 | ASE_DSP | ASE_DSPR2 | ASE_MT, + .mmu_type = MMU_TYPE_R4000, + }, #if defined(TARGET_MIPS64) { .name = "R4000", diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 12e765ba1f..265d25c937 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -1096,7 +1096,7 @@ void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, const S390CPUDef *def = s390_find_cpu_def(type, gen, ec_ga, NULL); g_assert(def); - g_assert(QTAILQ_EMPTY(&cpus)); + g_assert(QTAILQ_EMPTY_RCU(&cpus)); /* TCG emulates some features that can usually not be enabled with * the emulated machine generation. Make sure they can be enabled diff --git a/tests/Makefile.include b/tests/Makefile.include index 760a0f18b6..636b3d7ff8 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -116,6 +116,10 @@ check-unit-y += tests/rcutorture$(EXESUF) gcov-files-rcutorture-y = util/rcu.c check-unit-y += tests/test-rcu-list$(EXESUF) gcov-files-test-rcu-list-y = util/rcu.c +check-unit-y += tests/test-rcu-simpleq$(EXESUF) +gcov-files-test-rcu-simpleq-y = util/rcu.c +check-unit-y += tests/test-rcu-tailq$(EXESUF) +gcov-files-test-rcu-tailq-y = util/rcu.c check-unit-y += tests/test-qdist$(EXESUF) gcov-files-test-qdist-y = util/qdist.c check-unit-y += tests/test-qht$(EXESUF) @@ -124,7 +128,7 @@ check-unit-y += tests/test-qht-par$(EXESUF) gcov-files-test-qht-par-y = util/qht.c check-unit-y += tests/test-bitops$(EXESUF) check-unit-y += tests/test-bitcnt$(EXESUF) -check-unit-$(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) += tests/test-qdev-global-props$(EXESUF) +check-unit-y += tests/test-qdev-global-props$(EXESUF) check-unit-y += tests/check-qom-interface$(EXESUF) gcov-files-check-qom-interface-y = qom/object.c check-unit-y += tests/check-qom-proplist$(EXESUF) @@ -179,6 +183,8 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh check-qtest-generic-y = tests/qmp-test$(EXESUF) gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c +check-qtest-generic-y += tests/qmp-cmd-test$(EXESUF) + check-qtest-generic-y += tests/device-introspect-test$(EXESUF) gcov-files-generic-y = qdev-monitor.c qmp.c check-qtest-generic-y += tests/cdrom-test$(EXESUF) @@ -215,27 +221,27 @@ check-qtest-pci-y += tests/e1000-test$(EXESUF) gcov-files-pci-y += hw/net/e1000.c check-qtest-pci-y += tests/e1000e-test$(EXESUF) gcov-files-pci-y += hw/net/e1000e.c hw/net/e1000e_core.c -check-qtest-pci-y += tests/rtl8139-test$(EXESUF) -gcov-files-pci-y += hw/net/rtl8139.c -check-qtest-pci-y += tests/pcnet-test$(EXESUF) -gcov-files-pci-y += hw/net/pcnet.c -gcov-files-pci-y += hw/net/pcnet-pci.c -check-qtest-pci-y += tests/eepro100-test$(EXESUF) -gcov-files-pci-y += hw/net/eepro100.c -check-qtest-pci-y += tests/ne2000-test$(EXESUF) -gcov-files-pci-y += hw/net/ne2000.c -check-qtest-pci-y += tests/nvme-test$(EXESUF) -gcov-files-pci-y += hw/block/nvme.c -check-qtest-pci-y += tests/ac97-test$(EXESUF) -gcov-files-pci-y += hw/audio/ac97.c -check-qtest-pci-y += tests/es1370-test$(EXESUF) -gcov-files-pci-y += hw/audio/es1370.c +check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) +gcov-files-pci-$(CONFIG_RTL8139_PCI) += hw/net/rtl8139.c +check-qtest-pci-$(CONFIG_PCNET_PCI) += tests/pcnet-test$(EXESUF) +gcov-files-pci-$(CONFIG_PCNET_PCI) += hw/net/pcnet.c +gcov-files-pci-$(CONFIG_PCNET_PCI) += hw/net/pcnet-pci.c +check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF) +gcov-files-pci-$(CONFIG_EEPRO100_PCI) += hw/net/eepro100.c +check-qtest-pci-$(CONFIG_NE2000_PCI) += tests/ne2000-test$(EXESUF) +gcov-files-pci-$(CONFIG_NE2000_PCI) += hw/net/ne2000.c +check-qtest-pci-$(CONFIG_NVME_PCI) += tests/nvme-test$(EXESUF) +gcov-files-pci-$(CONFIG_NVME_PCI) += hw/block/nvme.c +check-qtest-pci-$(CONFIG_AC97) += tests/ac97-test$(EXESUF) +gcov-files-pci-$(CONFIG_AC97) += hw/audio/ac97.c +check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF) +gcov-files-pci-$(CONFIG_ES1370) += hw/audio/es1370.c check-qtest-pci-y += $(check-qtest-virtio-y) gcov-files-pci-y += $(gcov-files-virtio-y) hw/virtio/virtio-pci.c -check-qtest-pci-y += tests/tpci200-test$(EXESUF) -gcov-files-pci-y += hw/ipack/tpci200.c -check-qtest-pci-y += $(check-qtest-ipack-y) -gcov-files-pci-y += $(gcov-files-ipack-y) +check-qtest-pci-$(CONFIG_IPACK) += tests/tpci200-test$(EXESUF) +gcov-files-pci-$(CONFIG_IPACK) += hw/ipack/tpci200.c +check-qtest-pci-$(CONFIG_IPACK) += $(check-qtest-ipack-y) +gcov-files-pci-$(CONFIG_IPACK) += $(gcov-files-ipack-y) check-qtest-pci-y += tests/display-vga-test$(EXESUF) gcov-files-pci-y += hw/display/vga.c gcov-files-pci-y += hw/display/cirrus_vga.c @@ -243,8 +249,8 @@ gcov-files-pci-y += hw/display/vga-pci.c gcov-files-pci-y += hw/display/virtio-gpu.c gcov-files-pci-y += hw/display/virtio-gpu-pci.c gcov-files-pci-$(CONFIG_VIRTIO_VGA) += hw/display/virtio-vga.c -check-qtest-pci-y += tests/intel-hda-test$(EXESUF) -gcov-files-pci-y += hw/audio/intel-hda.c hw/audio/hda-codec.c +check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) +gcov-files-pci-$(CONFIG_HDA) += hw/audio/intel-hda.c hw/audio/hda-codec.c check-qtest-pci-$(CONFIG_IVSHMEM) += tests/ivshmem-test$(EXESUF) gcov-files-pci-y += hw/misc/ivshmem.c check-qtest-pci-y += tests/megasas-test$(EXESUF) @@ -267,27 +273,29 @@ check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF) check-qtest-i386-y += tests/i440fx-test$(EXESUF) check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) check-qtest-i386-y += tests/drive_del-test$(EXESUF) -check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF) +check-qtest-i386-$(CONFIG_WDT_IB700) += tests/wdt_ib700-test$(EXESUF) +gcov-files-i386-$(CONFIG_WDT_IB700) += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c check-qtest-i386-y += tests/tco-test$(EXESUF) -gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c check-qtest-i386-y += $(check-qtest-pci-y) gcov-files-i386-y += $(gcov-files-pci-y) -check-qtest-i386-y += tests/vmxnet3-test$(EXESUF) -gcov-files-i386-y += hw/net/vmxnet3.c +check-qtest-i386-$(CONFIG_VMXNET3_PCI) += tests/vmxnet3-test$(EXESUF) +gcov-files-i386-$(CONFIG_VMXNET3_PCI) += hw/net/vmxnet3.c gcov-files-i386-y += hw/net/net_rx_pkt.c gcov-files-i386-y += hw/net/net_tx_pkt.c -check-qtest-i386-y += tests/pvpanic-test$(EXESUF) -gcov-files-i386-y += i386-softmmu/hw/misc/pvpanic.c -check-qtest-i386-y += tests/i82801b11-test$(EXESUF) -gcov-files-i386-y += hw/pci-bridge/i82801b11.c -check-qtest-i386-y += tests/ioh3420-test$(EXESUF) -gcov-files-i386-y += hw/pci-bridge/ioh3420.c -check-qtest-i386-y += tests/usb-hcd-ohci-test$(EXESUF) -gcov-files-i386-y += hw/usb/hcd-ohci.c -check-qtest-i386-y += tests/usb-hcd-uhci-test$(EXESUF) -gcov-files-i386-y += hw/usb/hcd-uhci.c +check-qtest-i386-$(CONFIG_PVPANIC) += tests/pvpanic-test$(EXESUF) +gcov-files-i386-$(CONFIG_PVPANIC) += i386-softmmu/hw/misc/pvpanic.c +check-qtest-i386-$(CONFIG_I82801B11) += tests/i82801b11-test$(EXESUF) +gcov-files-i386-$(CONFIG_I82801B11) += hw/pci-bridge/i82801b11.c +check-qtest-i386-$(CONFIG_IOH3420) += tests/ioh3420-test$(EXESUF) +gcov-files-i386-$(CONFIG_IOH3420) += hw/pci-bridge/ioh3420.c +check-qtest-i386-$(CONFIG_USB_OHCI) += tests/usb-hcd-ohci-test$(EXESUF) +gcov-files-i386-$(CONFIG_USB_OHCI) += hw/usb/hcd-ohci.c +check-qtest-i386-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF) +gcov-files-i386-$(CONFIG_USB_UHCI) += hw/usb/hcd-uhci.c +ifeq ($(CONFIG_USB_ECHI)$(CONFIG_USB_UHCI),yy) check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF) -gcov-files-i386-y += hw/usb/hcd-ehci.c +endif +gcov-files-i386-$(CONFIG_USB_EHCI) += hw/usb/hcd-ehci.c gcov-files-i386-y += hw/usb/dev-hid.c gcov-files-i386-y += hw/usb/dev-storage.c check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF) @@ -300,18 +308,18 @@ check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EX ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),) check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF) endif -check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-swtpm-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-swtpm-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-swtpm-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM_TIS) += tests/tpm-tis-swtpm-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM_TIS) += tests/tpm-tis-test$(EXESUF) check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) -check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) +check-qtest-i386-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF) check-qtest-i386-y += tests/migration-test$(EXESUF) check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF) check-qtest-i386-y += tests/numa-test$(EXESUF) check-qtest-x86_64-y += $(check-qtest-i386-y) -check-qtest-x86_64-y += tests/sdhci-test$(EXESUF) +check-qtest-x86_64-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF) gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)) @@ -347,16 +355,16 @@ check-qtest-ppc64-y += tests/pnv-xscom-test$(EXESUF) check-qtest-ppc64-y += tests/migration-test$(EXESUF) check-qtest-ppc64-y += tests/rtas-test$(EXESUF) check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) -check-qtest-ppc64-y += tests/usb-hcd-ohci-test$(EXESUF) -gcov-files-ppc64-y += hw/usb/hcd-ohci.c -check-qtest-ppc64-y += tests/usb-hcd-uhci-test$(EXESUF) -gcov-files-ppc64-y += hw/usb/hcd-uhci.c +check-qtest-ppc64-$(CONFIG_USB_OHCI) += tests/usb-hcd-ohci-test$(EXESUF) +gcov-files-ppc64-$(CONFIG_USB_OHCI) += hw/usb/hcd-ohci.c +check-qtest-ppc64-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF) +gcov-files-ppc64-$(CONFIG_USB_UHCI) += hw/usb/hcd-uhci.c check-qtest-ppc64-y += tests/usb-hcd-xhci-test$(EXESUF) gcov-files-ppc64-y += hw/usb/hcd-xhci.c check-qtest-ppc64-y += $(check-qtest-virtio-y) check-qtest-ppc64-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) -check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) +check-qtest-ppc64-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF) check-qtest-ppc64-y += tests/display-vga-test$(EXESUF) check-qtest-ppc64-y += tests/numa-test$(EXESUF) check-qtest-ppc64-$(CONFIG_IVSHMEM) += tests/ivshmem-test$(EXESUF) @@ -385,11 +393,11 @@ gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF) gcov-files-arm-y += hw/timer/arm_mptimer.c check-qtest-arm-y += tests/boot-serial-test$(EXESUF) -check-qtest-arm-y += tests/sdhci-test$(EXESUF) +check-qtest-arm-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF) check-qtest-arm-y += tests/hexloader-test$(EXESUF) check-qtest-aarch64-y = tests/numa-test$(EXESUF) -check-qtest-aarch64-y += tests/sdhci-test$(EXESUF) +check-qtest-aarch64-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF) check-qtest-aarch64-y += tests/boot-serial-test$(EXESUF) check-qtest-microblazeel-y = $(check-qtest-microblaze-y) @@ -402,9 +410,7 @@ check-qtest-s390x-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) check-qtest-s390x-y += tests/drive_del-test$(EXESUF) -check-qtest-s390x-y += tests/virtio-balloon-test$(EXESUF) -check-qtest-s390x-y += tests/virtio-console-test$(EXESUF) -check-qtest-s390x-y += tests/virtio-serial-test$(EXESUF) +check-qtest-s390x-y += tests/virtio-ccw-test$(EXESUF) check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF) check-qtest-generic-y += tests/machine-none-test$(EXESUF) @@ -600,6 +606,8 @@ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ tests/test-opts-visitor.o tests/test-qmp-event.o \ tests/rcutorture.o tests/test-rcu-list.o \ + tests/test-rcu-simpleq.o \ + tests/test-rcu-tailq.o \ tests/test-qdist.o tests/test-shift128.o \ tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \ tests/atomic_add-bench.o @@ -649,6 +657,8 @@ tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o $(test-util-obj-y) tests/test-int128$(EXESUF): tests/test-int128.o tests/rcutorture$(EXESUF): tests/rcutorture.o $(test-util-obj-y) tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o $(test-util-obj-y) +tests/test-rcu-simpleq$(EXESUF): tests/test-rcu-simpleq.o $(test-util-obj-y) +tests/test-rcu-tailq$(EXESUF): tests/test-rcu-tailq.o $(test-util-obj-y) tests/test-qdist$(EXESUF): tests/test-qdist.o $(test-util-obj-y) tests/test-qht$(EXESUF): tests/test-qht.o $(test-util-obj-y) tests/test-qht-par$(EXESUF): tests/test-qht-par.o tests/qht-bench$(EXESUF) $(test-util-obj-y) @@ -771,6 +781,7 @@ libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o tests/qmp-test$(EXESUF): tests/qmp-test.o +tests/qmp-cmd-test$(EXESUF): tests/qmp-cmd-test.o tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o tests/rtc-test$(EXESUF): tests/rtc-test.o tests/m48t59-test$(EXESUF): tests/m48t59-test.o @@ -809,6 +820,7 @@ tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o $(libqos-virtio-obj-y) tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y) +tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) $(libqos-virtio-obj-y) tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y) tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o $(libqos-virtio-obj-y) diff --git a/tests/atomic_add-bench.c b/tests/atomic_add-bench.c index f96d448f77..2f6c72f63a 100644 --- a/tests/atomic_add-bench.c +++ b/tests/atomic_add-bench.c @@ -26,6 +26,7 @@ static bool test_stop; static const char commands_string[] = " -n = number of threads\n" " -m = use mutexes instead of atomic increments\n" + " -p = enable sync profiler\n" " -d = duration in seconds\n" " -r = range (will be rounded up to pow2)"; @@ -143,7 +144,7 @@ static void parse_args(int argc, char *argv[]) int c; for (;;) { - c = getopt(argc, argv, "hd:n:mr:"); + c = getopt(argc, argv, "hd:n:mpr:"); if (c < 0) { break; } @@ -160,6 +161,9 @@ static void parse_args(int argc, char *argv[]) case 'm': use_mutex = true; break; + case 'p': + qsp_enable(); + break; case 'r': range = pow2ceil(atoi(optarg)); break; diff --git a/tests/boot-order-test.c b/tests/boot-order-test.c index 9d98c48a3d..c60ebcf9d9 100644 --- a/tests/boot-order-test.c +++ b/tests/boot-order-test.c @@ -14,7 +14,7 @@ #include "libqos/fw_cfg.h" #include "libqtest.h" #include "qapi/qmp/qdict.h" -#include "hw/nvram/fw_cfg_keys.h" +#include "standard-headers/linux/qemu_fw_cfg.h" /* TODO actually test the results and get rid of this */ #define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) diff --git a/tests/check-qjson.c b/tests/check-qjson.c index eaf5d20663..cc13f3d41e 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -20,113 +20,111 @@ #include "qapi/qmp/qnull.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" +#include "qemu/unicode.h" #include "qemu-common.h" -static void escaped_string(void) +static QString *from_json_str(const char *jstr, bool single, Error **errp) { - int i; - struct { - const char *encoded; - const char *decoded; - int skip; - } test_cases[] = { - { "\"\\b\"", "\b" }, - { "\"\\f\"", "\f" }, - { "\"\\n\"", "\n" }, - { "\"\\r\"", "\r" }, - { "\"\\t\"", "\t" }, - { "\"/\"", "/" }, - { "\"\\/\"", "/", .skip = 1 }, - { "\"\\\\\"", "\\" }, - { "\"\\\"\"", "\"" }, - { "\"hello world \\\"embedded string\\\"\"", - "hello world \"embedded string\"" }, - { "\"hello world\\nwith new line\"", "hello world\nwith new line" }, - { "\"single byte utf-8 \\u0020\"", "single byte utf-8 ", .skip = 1 }, - { "\"double byte utf-8 \\u00A2\"", "double byte utf-8 \xc2\xa2" }, - { "\"triple byte utf-8 \\u20AC\"", "triple byte utf-8 \xe2\x82\xac" }, - { "'\\b'", "\b", .skip = 1 }, - { "'\\f'", "\f", .skip = 1 }, - { "'\\n'", "\n", .skip = 1 }, - { "'\\r'", "\r", .skip = 1 }, - { "'\\t'", "\t", .skip = 1 }, - { "'\\/'", "/", .skip = 1 }, - { "'\\\\'", "\\", .skip = 1 }, - {} - }; + char quote = single ? '\'' : '"'; + char *qjstr = g_strdup_printf("%c%s%c", quote, jstr, quote); + QString *ret = qobject_to(QString, qobject_from_json(qjstr, errp)); - for (i = 0; test_cases[i].encoded; i++) { - QObject *obj; - QString *str; - - obj = qobject_from_json(test_cases[i].encoded, &error_abort); - str = qobject_to(QString, obj); - g_assert(str); - g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded); + g_free(qjstr); + return ret; +} - if (test_cases[i].skip == 0) { - str = qobject_to_json(obj); - g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded); - qobject_unref(obj); - } +static char *to_json_str(QString *str) +{ + QString *json = qobject_to_json(QOBJECT(str)); + char *jstr; - qobject_unref(str); + if (!json) { + return NULL; } + /* peel off double quotes */ + jstr = g_strndup(qstring_get_str(json) + 1, + qstring_get_length(json) - 2); + qobject_unref(json); + return jstr; } -static void simple_string(void) +static void escaped_string(void) { - int i; struct { - const char *encoded; - const char *decoded; + /* Content of JSON string to parse with qobject_from_json() */ + const char *json_in; + /* Expected parse output; to unparse with qobject_to_json() */ + const char *utf8_out; + int skip; } test_cases[] = { - { "\"hello world\"", "hello world" }, - { "\"the quick brown fox jumped over the fence\"", - "the quick brown fox jumped over the fence" }, + { "\\b\\f\\n\\r\\t\\\\\\\"", "\b\f\n\r\t\\\"" }, + { "\\/\\'", "/'", .skip = 1 }, + { "single byte utf-8 \\u0020", "single byte utf-8 ", .skip = 1 }, + { "double byte utf-8 \\u00A2", "double byte utf-8 \xc2\xa2" }, + { "triple byte utf-8 \\u20AC", "triple byte utf-8 \xe2\x82\xac" }, + { "quadruple byte utf-8 \\uD834\\uDD1E", /* U+1D11E */ + "quadruple byte utf-8 \xF0\x9D\x84\x9E" }, + { "\\", NULL }, + { "\\z", NULL }, + { "\\ux", NULL }, + { "\\u1x", NULL }, + { "\\u12x", NULL }, + { "\\u123x", NULL }, + { "\\u12345", "\341\210\2645" }, + { "\\u0000x", "\xC0\x80x" }, + { "unpaired leading surrogate \\uD800", NULL }, + { "unpaired leading surrogate \\uD800\\uCAFE", NULL }, + { "unpaired leading surrogate \\uD800\\uD801\\uDC02", NULL }, + { "unpaired trailing surrogate \\uDC00", NULL }, + { "backward surrogate pair \\uDC00\\uD800", NULL }, + { "noncharacter U+FDD0 \\uFDD0", NULL }, + { "noncharacter U+FDEF \\uFDEF", NULL }, + { "noncharacter U+1FFFE \\uD87F\\uDFFE", NULL }, + { "noncharacter U+10FFFF \\uDC3F\\uDFFF", NULL }, {} }; + int i, j; + QString *cstr; + char *jstr; - for (i = 0; test_cases[i].encoded; i++) { - QObject *obj; - QString *str; - - obj = qobject_from_json(test_cases[i].encoded, &error_abort); - str = qobject_to(QString, obj); - g_assert(str); - g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - - str = qobject_to_json(obj); - g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); - - qobject_unref(obj); - - qobject_unref(str); + for (i = 0; test_cases[i].json_in; i++) { + for (j = 0; j < 2; j++) { + if (test_cases[i].utf8_out) { + cstr = from_json_str(test_cases[i].json_in, j, &error_abort); + g_assert_cmpstr(qstring_get_try_str(cstr), + ==, test_cases[i].utf8_out); + if (!test_cases[i].skip) { + jstr = to_json_str(cstr); + g_assert_cmpstr(jstr, ==, test_cases[i].json_in); + g_free(jstr); + } + qobject_unref(cstr); + } else { + cstr = from_json_str(test_cases[i].json_in, j, NULL); + g_assert(!cstr); + } + } } } -static void single_quote_string(void) +static void string_with_quotes(void) { - int i; - struct { - const char *encoded; - const char *decoded; - } test_cases[] = { - { "'hello world'", "hello world" }, - { "'the quick brown fox \\' jumped over the fence'", - "the quick brown fox ' jumped over the fence" }, - {} + const char *test_cases[] = { + "\"the bee's knees\"", + "'double quote \"'", + NULL }; + int i; + QString *str; + char *cstr; - for (i = 0; test_cases[i].encoded; i++) { - QObject *obj; - QString *str; - - obj = qobject_from_json(test_cases[i].encoded, &error_abort); - str = qobject_to(QString, obj); + for (i = 0; test_cases[i]; i++) { + str = qobject_to(QString, + qobject_from_json(test_cases[i], &error_abort)); g_assert(str); - g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - + cstr = g_strndup(test_cases[i] + 1, strlen(test_cases[i]) - 2); + g_assert_cmpstr(qstring_get_str(str), ==, cstr); + g_free(cstr); qobject_unref(str); } } @@ -134,117 +132,104 @@ static void single_quote_string(void) static void utf8_string(void) { /* - * FIXME Current behavior for invalid UTF-8 sequences is - * incorrect. This test expects current, incorrect results. - * They're all marked "bug:" below, and are to be replaced by - * correct ones as the bugs get fixed. - * - * The JSON parser rejects some invalid sequences, but accepts - * others without correcting the problem. - * - * We should either reject all invalid sequences, or minimize - * overlong sequences and replace all other invalid sequences by a - * suitable replacement character. A common choice for - * replacement is U+FFFD. - * - * Problem: we can't easily deal with embedded U+0000. Parsing - * the JSON string "this \\u0000" is fun" yields "this \0 is fun", - * which gets misinterpreted as NUL-terminated "this ". We should - * consider using overlong encoding \xC0\x80 for U+0000 ("modified - * UTF-8"). - * * Most test cases are scraped from Markus Kuhn's UTF-8 decoder * capability and stress test at * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt */ static const struct { + /* Content of JSON string to parse with qobject_from_json() */ const char *json_in; + /* Expected parse output */ const char *utf8_out; - const char *json_out; /* defaults to @json_in */ - const char *utf8_in; /* defaults to @utf8_out */ + /* Expected unparse output, defaults to @json_in */ + const char *json_out; } test_cases[] = { - /* - * Bug markers used here: - * - bug: not corrected - * JSON parser fails to correct invalid sequence(s) - * - bug: rejected - * JSON parser rejects invalid sequence(s) - * We may choose to define this as feature - * - bug: want "..." - * JSON parser produces incorrect result, this is the - * correct one, assuming replacement character U+FFFF - * We may choose to reject instead of replace - */ - + /* 0 Control characters */ + { + /* + * Note: \x00 is impossible, other representations of + * U+0000 are covered under 4.3 + */ + "\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", + NULL, + "\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" + "\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F" + "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" + "\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F", + }, /* 1 Some correct UTF-8 text */ { /* a bit of German */ - "\"Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" - " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.\"", "Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.", - "\"Falsches \\u00DCben von Xylophonmusik qu\\u00E4lt" - " jeden gr\\u00F6\\u00DFeren Zwerg.\"", + "Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" + " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.", + "Falsches \\u00DCben von Xylophonmusik qu\\u00E4lt" + " jeden gr\\u00F6\\u00DFeren Zwerg.", }, { /* a bit of Greek */ - "\"\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5\"", "\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5", - "\"\\u03BA\\u1F79\\u03C3\\u03BC\\u03B5\"", + "\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5", + "\\u03BA\\u1F79\\u03C3\\u03BC\\u03B5", }, /* 2 Boundary condition test cases */ /* 2.1 First possible sequence of a certain length */ - /* 2.1.1 1 byte U+0000 */ + /* + * 2.1.1 1 byte U+0020 + * Control characters are already covered by their own test + * case under 0. Test the first 1 byte non-control character + * here. + */ { - "\"\\u0000\"", - "", /* bug: want overlong "\xC0\x80" */ - "\"\\u0000\"", - "\xC0\x80", + " ", + " ", }, /* 2.1.2 2 bytes U+0080 */ { - "\"\xC2\x80\"", "\xC2\x80", - "\"\\u0080\"", + "\xC2\x80", + "\\u0080", }, /* 2.1.3 3 bytes U+0800 */ { - "\"\xE0\xA0\x80\"", "\xE0\xA0\x80", - "\"\\u0800\"", + "\xE0\xA0\x80", + "\\u0800", }, /* 2.1.4 4 bytes U+10000 */ { - "\"\xF0\x90\x80\x80\"", "\xF0\x90\x80\x80", - "\"\\uD800\\uDC00\"", + "\xF0\x90\x80\x80", + "\\uD800\\uDC00", }, /* 2.1.5 5 bytes U+200000 */ { - "\"\xF8\x88\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x88\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 2.1.6 6 bytes U+4000000 */ { - "\"\xFC\x84\x80\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x84\x80\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 2.2 Last possible sequence of a certain length */ /* 2.2.1 1 byte U+007F */ { - "\"\x7F\"", "\x7F", - "\"\\u007F\"", + "\x7F", + "\\u007F", }, /* 2.2.2 2 bytes U+07FF */ { - "\"\xDF\xBF\"", "\xDF\xBF", - "\"\\u07FF\"", + "\xDF\xBF", + "\\u07FF", }, /* * 2.2.3 3 bytes U+FFFC @@ -256,123 +241,111 @@ static void utf8_string(void) * U+FFFC here. */ { - "\"\xEF\xBF\xBC\"", "\xEF\xBF\xBC", - "\"\\uFFFC\"", + "\xEF\xBF\xBC", + "\\uFFFC", }, /* 2.2.4 4 bytes U+1FFFFF */ { - "\"\xF7\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF7\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 2.2.5 5 bytes U+3FFFFFF */ { - "\"\xFB\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFB\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 2.2.6 6 bytes U+7FFFFFFF */ { - "\"\xFD\xBF\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFD\xBF\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 2.3 Other boundary conditions */ { /* last one before surrogate range: U+D7FF */ - "\"\xED\x9F\xBF\"", "\xED\x9F\xBF", - "\"\\uD7FF\"", + "\xED\x9F\xBF", + "\\uD7FF", }, { /* first one after surrogate range: U+E000 */ - "\"\xEE\x80\x80\"", "\xEE\x80\x80", - "\"\\uE000\"", + "\xEE\x80\x80", + "\\uE000", }, { /* last one in BMP: U+FFFD */ - "\"\xEF\xBF\xBD\"", "\xEF\xBF\xBD", - "\"\\uFFFD\"", + "\xEF\xBF\xBD", + "\\uFFFD", }, { /* last one in last plane: U+10FFFD */ - "\"\xF4\x8F\xBF\xBD\"", "\xF4\x8F\xBF\xBD", - "\"\\uDBFF\\uDFFD\"" + "\xF4\x8F\xBF\xBD", + "\\uDBFF\\uDFFD" }, { /* first one beyond Unicode range: U+110000 */ - "\"\xF4\x90\x80\x80\"", "\xF4\x90\x80\x80", - "\"\\uFFFD\"", + NULL, + "\\uFFFD", }, /* 3 Malformed sequences */ /* 3.1 Unexpected continuation bytes */ /* 3.1.1 First continuation byte */ { - "\"\x80\"", - "\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\x80", + NULL, + "\\uFFFD", }, /* 3.1.2 Last continuation byte */ { - "\"\xBF\"", - "\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xBF", + NULL, + "\\uFFFD", }, /* 3.1.3 2 continuation bytes */ { - "\"\x80\xBF\"", - "\x80\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\x80\xBF", + NULL, + "\\uFFFD\\uFFFD", }, /* 3.1.4 3 continuation bytes */ { - "\"\x80\xBF\x80\"", - "\x80\xBF\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80", + NULL, + "\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.5 4 continuation bytes */ { - "\"\x80\xBF\x80\xBF\"", - "\x80\xBF\x80\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.6 5 continuation bytes */ { - "\"\x80\xBF\x80\xBF\x80\"", - "\x80\xBF\x80\xBF\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80\xBF\x80", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.7 6 continuation bytes */ { - "\"\x80\xBF\x80\xBF\x80\xBF\"", - "\x80\xBF\x80\xBF\x80\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80\xBF\x80\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.8 7 continuation bytes */ { - "\"\x80\xBF\x80\xBF\x80\xBF\x80\"", - "\x80\xBF\x80\xBF\x80\xBF\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80\xBF\x80\xBF\x80", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.9 Sequence of all 64 possible continuation bytes */ { - "\"\x80\x81\x82\x83\x84\x85\x86\x87" - "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" - "\x90\x91\x92\x93\x94\x95\x96\x97" - "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" - "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7" - "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" - "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7" - "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\"", - /* bug: not corrected */ "\x80\x81\x82\x83\x84\x85\x86\x87" "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" "\x90\x91\x92\x93\x94\x95\x96\x97" @@ -381,188 +354,166 @@ static void utf8_string(void) "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7" "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + NULL, "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.2 Lonely start characters */ /* 3.2.1 All 32 first bytes of 2-byte sequences, followed by space */ { - "\"\xC0 \xC1 \xC2 \xC3 \xC4 \xC5 \xC6 \xC7 " - "\xC8 \xC9 \xCA \xCB \xCC \xCD \xCE \xCF " - "\xD0 \xD1 \xD2 \xD3 \xD4 \xD5 \xD6 \xD7 " - "\xD8 \xD9 \xDA \xDB \xDC \xDD \xDE \xDF \"", - NULL, /* bug: rejected */ - "\"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \"", "\xC0 \xC1 \xC2 \xC3 \xC4 \xC5 \xC6 \xC7 " "\xC8 \xC9 \xCA \xCB \xCC \xCD \xCE \xCF " "\xD0 \xD1 \xD2 \xD3 \xD4 \xD5 \xD6 \xD7 " "\xD8 \xD9 \xDA \xDB \xDC \xDD \xDE \xDF ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", }, /* 3.2.2 All 16 first bytes of 3-byte sequences, followed by space */ { - "\"\xE0 \xE1 \xE2 \xE3 \xE4 \xE5 \xE6 \xE7 " - "\xE8 \xE9 \xEA \xEB \xEC \xED \xEE \xEF \"", - /* bug: not corrected */ "\xE0 \xE1 \xE2 \xE3 \xE4 \xE5 \xE6 \xE7 " "\xE8 \xE9 \xEA \xEB \xEC \xED \xEE \xEF ", - "\"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \"", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", }, /* 3.2.3 All 8 first bytes of 4-byte sequences, followed by space */ { - "\"\xF0 \xF1 \xF2 \xF3 \xF4 \xF5 \xF6 \xF7 \"", - NULL, /* bug: rejected */ - "\"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \"", "\xF0 \xF1 \xF2 \xF3 \xF4 \xF5 \xF6 \xF7 ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", }, /* 3.2.4 All 4 first bytes of 5-byte sequences, followed by space */ { - "\"\xF8 \xF9 \xFA \xFB \"", - NULL, /* bug: rejected */ - "\"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \"", "\xF8 \xF9 \xFA \xFB ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD ", }, /* 3.2.5 All 2 first bytes of 6-byte sequences, followed by space */ { - "\"\xFC \xFD \"", - NULL, /* bug: rejected */ - "\"\\uFFFD \\uFFFD \"", "\xFC \xFD ", + NULL, + "\\uFFFD \\uFFFD ", }, /* 3.3 Sequences with last continuation byte missing */ /* 3.3.1 2-byte sequence with last byte missing (U+0000) */ { - "\"\xC0\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xC0", + NULL, + "\\uFFFD", }, /* 3.3.2 3-byte sequence with last byte missing (U+0000) */ { - "\"\xE0\x80\"", - "\xE0\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xE0\x80", + NULL, + "\\uFFFD", }, /* 3.3.3 4-byte sequence with last byte missing (U+0000) */ { - "\"\xF0\x80\x80\"", - "\xF0\x80\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xF0\x80\x80", + NULL, + "\\uFFFD", }, /* 3.3.4 5-byte sequence with last byte missing (U+0000) */ { - "\"\xF8\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 3.3.5 6-byte sequence with last byte missing (U+0000) */ { - "\"\xFC\x80\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x80\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 3.3.6 2-byte sequence with last byte missing (U+07FF) */ { - "\"\xDF\"", - "\xDF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xDF", + NULL, + "\\uFFFD", }, /* 3.3.7 3-byte sequence with last byte missing (U+FFFF) */ { - "\"\xEF\xBF\"", - "\xEF\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xBF", + NULL, + "\\uFFFD", }, /* 3.3.8 4-byte sequence with last byte missing (U+1FFFFF) */ { - "\"\xF7\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF7\xBF\xBF", + NULL, + "\\uFFFD", }, /* 3.3.9 5-byte sequence with last byte missing (U+3FFFFFF) */ { - "\"\xFB\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFB\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 3.3.10 6-byte sequence with last byte missing (U+7FFFFFFF) */ { - "\"\xFD\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFD\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 3.4 Concatenation of incomplete sequences */ { - "\"\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80" - "\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", "\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80" "\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.5 Impossible bytes */ { - "\"\xFE\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFE", + NULL, + "\\uFFFD", }, { - "\"\xFF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFF", + NULL, + "\\uFFFD", }, { - "\"\xFE\xFE\xFF\xFF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", "\xFE\xFE\xFF\xFF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 4 Overlong sequences */ /* 4.1 Overlong '/' */ { - "\"\xC0\xAF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xC0\xAF", + NULL, + "\\uFFFD", }, { - "\"\xE0\x80\xAF\"", - "\xE0\x80\xAF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xE0\x80\xAF", + NULL, + "\\uFFFD", }, { - "\"\xF0\x80\x80\xAF\"", - "\xF0\x80\x80\xAF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xF0\x80\x80\xAF", + NULL, + "\\uFFFD", }, { - "\"\xF8\x80\x80\x80\xAF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x80\x80\x80\xAF", + NULL, + "\\uFFFD", }, { - "\"\xFC\x80\x80\x80\x80\xAF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x80\x80\x80\x80\xAF", + NULL, + "\\uFFFD", }, /* * 4.2 Maximum overlong sequences @@ -572,16 +523,15 @@ static void utf8_string(void) */ { /* \U+007F */ - "\"\xC1\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xC1\xBF", + NULL, + "\\uFFFD", }, { /* \U+07FF */ - "\"\xE0\x9F\xBF\"", - "\xE0\x9F\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xE0\x9F\xBF", + NULL, + "\\uFFFD", }, { /* @@ -590,197 +540,175 @@ static void utf8_string(void) * noncharacter. Testing U+FFFC seems more useful. See * also 2.2.3 */ - "\"\xF0\x8F\xBF\xBC\"", - "\xF0\x8F\xBF\xBC", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xF0\x8F\xBF\xBC", + NULL, + "\\uFFFD", }, { /* \U+1FFFFF */ - "\"\xF8\x87\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x87\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, { /* \U+3FFFFFF */ - "\"\xFC\x83\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x83\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 4.3 Overlong representation of the NUL character */ { /* \U+0000 */ - "\"\xC0\x80\"", - NULL, /* bug: rejected */ - "\"\\u0000\"", "\xC0\x80", + "\xC0\x80", + "\\u0000", }, { /* \U+0000 */ - "\"\xE0\x80\x80\"", - "\xE0\x80\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xE0\x80\x80", + NULL, + "\\uFFFD", }, { /* \U+0000 */ - "\"\xF0\x80\x80\x80\"", - "\xF0\x80\x80\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xF0\x80\x80\x80", + NULL, + "\\uFFFD", }, { /* \U+0000 */ - "\"\xF8\x80\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x80\x80\x80\x80", + NULL, + "\\uFFFD", }, { /* \U+0000 */ - "\"\xFC\x80\x80\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x80\x80\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 5 Illegal code positions */ /* 5.1 Single UTF-16 surrogates */ { /* \U+D800 */ - "\"\xED\xA0\x80\"", - "\xED\xA0\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xA0\x80", + NULL, + "\\uFFFD", }, { /* \U+DB7F */ - "\"\xED\xAD\xBF\"", - "\xED\xAD\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xAD\xBF", + NULL, + "\\uFFFD", }, { /* \U+DB80 */ - "\"\xED\xAE\x80\"", - "\xED\xAE\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xAE\x80", + NULL, + "\\uFFFD", }, { /* \U+DBFF */ - "\"\xED\xAF\xBF\"", - "\xED\xAF\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xAF\xBF", + NULL, + "\\uFFFD", }, { /* \U+DC00 */ - "\"\xED\xB0\x80\"", - "\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xB0\x80", + NULL, + "\\uFFFD", }, { /* \U+DF80 */ - "\"\xED\xBE\x80\"", - "\xED\xBE\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xBE\x80", + NULL, + "\\uFFFD", }, { /* \U+DFFF */ - "\"\xED\xBF\xBF\"", - "\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xBF\xBF", + NULL, + "\\uFFFD", }, /* 5.2 Paired UTF-16 surrogates */ { /* \U+D800\U+DC00 */ - "\"\xED\xA0\x80\xED\xB0\x80\"", - "\xED\xA0\x80\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xA0\x80\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+D800\U+DFFF */ - "\"\xED\xA0\x80\xED\xBF\xBF\"", - "\xED\xA0\x80\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xA0\x80\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DB7F\U+DC00 */ - "\"\xED\xAD\xBF\xED\xB0\x80\"", - "\xED\xAD\xBF\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAD\xBF\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DB7F\U+DFFF */ - "\"\xED\xAD\xBF\xED\xBF\xBF\"", - "\xED\xAD\xBF\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAD\xBF\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DB80\U+DC00 */ - "\"\xED\xAE\x80\xED\xB0\x80\"", - "\xED\xAE\x80\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAE\x80\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DB80\U+DFFF */ - "\"\xED\xAE\x80\xED\xBF\xBF\"", - "\xED\xAE\x80\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAE\x80\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DBFF\U+DC00 */ - "\"\xED\xAF\xBF\xED\xB0\x80\"", - "\xED\xAF\xBF\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAF\xBF\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DBFF\U+DFFF */ - "\"\xED\xAF\xBF\xED\xBF\xBF\"", - "\xED\xAF\xBF\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAF\xBF\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", }, /* 5.3 Other illegal code positions */ /* BMP noncharacters */ { /* \U+FFFE */ - "\"\xEF\xBF\xBE\"", - "\xEF\xBF\xBE", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xBF\xBE", + NULL, + "\\uFFFD", }, { /* \U+FFFF */ - "\"\xEF\xBF\xBF\"", - "\xEF\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xBF\xBF", + NULL, + "\\uFFFD", }, { /* U+FDD0 */ - "\"\xEF\xB7\x90\"", - "\xEF\xB7\x90", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xB7\x90", + NULL, + "\\uFFFD", }, { /* U+FDEF */ - "\"\xEF\xB7\xAF\"", - "\xEF\xB7\xAF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xB7\xAF", + NULL, + "\\uFFFD", }, /* Plane 1 .. 16 noncharacters */ { /* U+1FFFE U+1FFFF U+2FFFE U+2FFFF ... U+10FFFE U+10FFFF */ - "\"\xF0\x9F\xBF\xBE\xF0\x9F\xBF\xBF" - "\xF0\xAF\xBF\xBE\xF0\xAF\xBF\xBF" - "\xF0\xBF\xBF\xBE\xF0\xBF\xBF\xBF" - "\xF1\x8F\xBF\xBE\xF1\x8F\xBF\xBF" - "\xF1\x9F\xBF\xBE\xF1\x9F\xBF\xBF" - "\xF1\xAF\xBF\xBE\xF1\xAF\xBF\xBF" - "\xF1\xBF\xBF\xBE\xF1\xBF\xBF\xBF" - "\xF2\x8F\xBF\xBE\xF2\x8F\xBF\xBF" - "\xF2\x9F\xBF\xBE\xF2\x9F\xBF\xBF" - "\xF2\xAF\xBF\xBE\xF2\xAF\xBF\xBF" - "\xF2\xBF\xBF\xBE\xF2\xBF\xBF\xBF" - "\xF3\x8F\xBF\xBE\xF3\x8F\xBF\xBF" - "\xF3\x9F\xBF\xBE\xF3\x9F\xBF\xBF" - "\xF3\xAF\xBF\xBE\xF3\xAF\xBF\xBF" - "\xF3\xBF\xBF\xBE\xF3\xBF\xBF\xBF" - "\xF4\x8F\xBF\xBE\xF4\x8F\xBF\xBF\"", - /* bug: not corrected */ "\xF0\x9F\xBF\xBE\xF0\x9F\xBF\xBF" "\xF0\xAF\xBF\xBE\xF0\xAF\xBF\xBF" "\xF0\xBF\xBF\xBE\xF0\xBF\xBF\xBF" @@ -797,83 +725,66 @@ static void utf8_string(void) "\xF3\xAF\xBF\xBE\xF3\xAF\xBF\xBF" "\xF3\xBF\xBF\xBE\xF3\xBF\xBF\xBF" "\xF4\x8F\xBF\xBE\xF4\x8F\xBF\xBF", - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + NULL, "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, {} }; - int i; - QObject *obj; + int i, j; QString *str; - const char *json_in, *utf8_out, *utf8_in, *json_out; + const char *json_in, *utf8_out, *utf8_in, *json_out, *tail; + char *end, *in, *jstr; for (i = 0; test_cases[i].json_in; i++) { - json_in = test_cases[i].json_in; - utf8_out = test_cases[i].utf8_out; - utf8_in = test_cases[i].utf8_in ?: test_cases[i].utf8_out; - json_out = test_cases[i].json_out ?: test_cases[i].json_in; - - obj = qobject_from_json(json_in, utf8_out ? &error_abort : NULL); - if (utf8_out) { - str = qobject_to(QString, obj); - g_assert(str); - g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); - } else { - g_assert(!obj); - } - qobject_unref(obj); + for (j = 0; j < 2; j++) { + json_in = test_cases[i].json_in; + utf8_out = test_cases[i].utf8_out; + utf8_in = test_cases[i].utf8_out ?: test_cases[i].json_in; + json_out = test_cases[i].json_out ?: test_cases[i].json_in; + + /* Parse @json_in, expect @utf8_out */ + if (utf8_out) { + str = from_json_str(json_in, j, &error_abort); + g_assert_cmpstr(qstring_get_try_str(str), ==, utf8_out); + qobject_unref(str); + } else { + str = from_json_str(json_in, j, NULL); + g_assert(!str); + /* + * Failure may be due to any sequence, but *all* sequences + * are expected to fail. Test each one in isolation. + */ + for (tail = json_in; *tail; tail = end) { + mod_utf8_codepoint(tail, 6, &end); + if (*end == ' ') { + end++; + } + in = strndup(tail, end - tail); + str = from_json_str(in, j, NULL); + g_assert(!str); + g_free(in); + } + } - obj = QOBJECT(qstring_from_str(utf8_in)); - str = qobject_to_json(obj); - if (json_out) { - g_assert(str); - g_assert_cmpstr(qstring_get_str(str), ==, json_out); - } else { - g_assert(!str); - } - qobject_unref(str); - qobject_unref(obj); + /* Unparse @utf8_in, expect @json_out */ + str = qstring_from_str(utf8_in); + jstr = to_json_str(str); + g_assert_cmpstr(jstr, ==, json_out); + qobject_unref(str); + g_free(jstr); - /* - * Disabled, because qobject_from_json() is buggy, and I can't - * be bothered to add the expected incorrect results. - * FIXME Enable once these bugs have been fixed. - */ - if (0 && json_out != json_in) { - obj = qobject_from_json(json_out, &error_abort); - str = qobject_to(QString, obj); - g_assert(str); - g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); + /* Parse @json_out right back, unless it has replacements */ + if (!strstr(json_out, "\\uFFFD")) { + str = from_json_str(json_out, j, &error_abort); + g_assert_cmpstr(qstring_get_try_str(str), ==, utf8_in); + } } } } -static void vararg_string(void) -{ - int i; - struct { - const char *decoded; - } test_cases[] = { - { "hello world" }, - { "the quick brown fox jumped over the fence" }, - {} - }; - - for (i = 0; test_cases[i].decoded; i++) { - QString *str; - - str = qobject_to(QString, - qobject_from_jsonf_nofail("%s", - test_cases[i].decoded)); - g_assert(str); - g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - - qobject_unref(str); - } -} - static void simple_number(void) { int i; @@ -991,29 +902,6 @@ static void float_number(void) } } -static void vararg_number(void) -{ - QNum *qnum; - int value = 0x2342; - long long value_ll = 0x2342342343LL; - double valuef = 2.323423423; - int64_t val; - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%d", value)); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, value); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lld", value_ll)); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, value_ll); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%f", valuef)); - g_assert(qnum_get_double(qnum) == valuef); - qobject_unref(qnum); -} - static void keyword_literal(void) { QObject *obj; @@ -1043,6 +931,37 @@ static void keyword_literal(void) qobject_unref(qbool); + obj = qobject_from_json("null", &error_abort); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QNULL); + + null = qnull(); + g_assert(QOBJECT(null) == obj); + + qobject_unref(obj); + qobject_unref(null); +} + +static void interpolation_valid(void) +{ + long long value_lld = 0x123456789abcdefLL; + int64_t value_d64 = value_lld; + long value_ld = (long)value_lld; + int value_d = (int)value_lld; + unsigned long long value_llu = 0xfedcba9876543210ULL; + uint64_t value_u64 = value_llu; + unsigned long value_lu = (unsigned long)value_llu; + unsigned value_u = (unsigned)value_llu; + double value_f = 2.323423423; + const char *value_s = "hello world"; + QObject *value_p = QOBJECT(qnull()); + QBool *qbool; + QNum *qnum; + QString *qstr; + QObject *qobj; + + /* bool */ + qbool = qobject_to(QBool, qobject_from_jsonf_nofail("%i", false)); g_assert(qbool); g_assert(qbool_get_bool(qbool) == false); @@ -1054,15 +973,77 @@ static void keyword_literal(void) g_assert(qbool_get_bool(qbool) == true); qobject_unref(qbool); - obj = qobject_from_json("null", &error_abort); - g_assert(obj != NULL); - g_assert(qobject_type(obj) == QTYPE_QNULL); + /* number */ - null = qnull(); - g_assert(QOBJECT(null) == obj); + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%d", value_d)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_d); + qobject_unref(qnum); - qobject_unref(obj); - qobject_unref(null); + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%ld", value_ld)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_ld); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lld", value_lld)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_lld); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRId64, value_d64)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_lld); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%u", value_u)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_u); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lu", value_lu)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_lu); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%llu", value_llu)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRIu64, value_u64)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%f", value_f)); + g_assert(qnum_get_double(qnum) == value_f); + qobject_unref(qnum); + + /* string */ + + qstr = qobject_to(QString, + qobject_from_jsonf_nofail("%s", value_s)); + g_assert_cmpstr(qstring_get_try_str(qstr), ==, value_s); + qobject_unref(qstr); + + /* object */ + + qobj = qobject_from_jsonf_nofail("%p", value_p); + g_assert(qobj == value_p); +} + +static void interpolation_unknown(void) +{ + if (g_test_subprocess()) { + qobject_from_jsonf_nofail("%x", 666); + } + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*Unexpected error*" + "invalid interpolation '%x'*"); +} + +static void interpolation_string(void) +{ + if (g_test_subprocess()) { + qobject_from_jsonf_nofail("['%s', %s]", "eins", "zwei"); + } + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*Unexpected error*" + "can't interpolate into string*"); } static void simple_dict(void) @@ -1236,7 +1217,7 @@ static void simple_whitespace(void) })), }, { - .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]", + .encoded = "\t[ 43 , { 'h' : 'b' },\r\n\t[ ], 42 ]\n", .decoded = QLIT_QLIST(((QLitObject[]){ QLIT_QNUM(43), QLIT_QDICT(((QLitDictEntry[]){ @@ -1283,13 +1264,13 @@ static void simple_whitespace(void) } } -static void simple_varargs(void) +static void simple_interpolation(void) { QObject *embedded_obj; QObject *obj; QLitObject decoded = QLIT_QLIST(((QLitObject[]){ QLIT_QNUM(1), - QLIT_QNUM(2), + QLIT_QSTR("100%"), QLIT_QLIST(((QLitObject[]){ QLIT_QNUM(32), QLIT_QNUM(42), @@ -1299,7 +1280,7 @@ static void simple_varargs(void) embedded_obj = qobject_from_json("[32, 42]", &error_abort); g_assert(embedded_obj != NULL); - obj = qobject_from_jsonf_nofail("[%d, 2, %p]", 1, embedded_obj); + obj = qobject_from_jsonf_nofail("[%d, '100%%', %p]", 1, embedded_obj); g_assert(qlit_equal_qobject(&decoded, obj)); qobject_unref(obj); @@ -1307,8 +1288,52 @@ static void simple_varargs(void) static void empty_input(void) { - const char *empty = ""; - QObject *obj = qobject_from_json(empty, &error_abort); + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void blank_input(void) +{ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("\n ", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void junk_input(void) +{ + /* Note: junk within strings is covered elsewhere */ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("@", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("{\x01", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("[0\xFF]", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("00", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("[1e", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("truer", &err); + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1316,7 +1341,7 @@ static void unterminated_string(void) { Error *err = NULL; QObject *obj = qobject_from_json("\"abc", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1324,7 +1349,7 @@ static void unterminated_sq_string(void) { Error *err = NULL; QObject *obj = qobject_from_json("'abc", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1332,7 +1357,7 @@ static void unterminated_escape(void) { Error *err = NULL; QObject *obj = qobject_from_json("\"abc\\\"", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1340,7 +1365,7 @@ static void unterminated_array(void) { Error *err = NULL; QObject *obj = qobject_from_json("[32", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1348,7 +1373,7 @@ static void unterminated_array_comma(void) { Error *err = NULL; QObject *obj = qobject_from_json("[32,", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1364,7 +1389,7 @@ static void unterminated_dict(void) { Error *err = NULL; QObject *obj = qobject_from_json("{'abc':32", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1372,7 +1397,7 @@ static void unterminated_dict_comma(void) { Error *err = NULL; QObject *obj = qobject_from_json("{'abc':32,", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1418,32 +1443,48 @@ static void limits_nesting(void) g_assert(obj == NULL); } +static void multiple_values(void) +{ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("false true", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("} true", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - g_test_add_func("/literals/string/simple", simple_string); g_test_add_func("/literals/string/escaped", escaped_string); + g_test_add_func("/literals/string/quotes", string_with_quotes); g_test_add_func("/literals/string/utf8", utf8_string); - g_test_add_func("/literals/string/single_quote", single_quote_string); - g_test_add_func("/literals/string/vararg", vararg_string); g_test_add_func("/literals/number/simple", simple_number); g_test_add_func("/literals/number/large", large_number); g_test_add_func("/literals/number/float", float_number); - g_test_add_func("/literals/number/vararg", vararg_number); g_test_add_func("/literals/keyword", keyword_literal); + g_test_add_func("/literals/interpolation/valid", interpolation_valid); + g_test_add_func("/literals/interpolation/unkown", interpolation_unknown); + g_test_add_func("/literals/interpolation/string", interpolation_string); + g_test_add_func("/dicts/simple_dict", simple_dict); g_test_add_func("/dicts/large_dict", large_dict); g_test_add_func("/lists/simple_list", simple_list); - g_test_add_func("/whitespace/simple_whitespace", simple_whitespace); - - g_test_add_func("/varargs/simple_varargs", simple_varargs); + g_test_add_func("/mixed/simple_whitespace", simple_whitespace); + g_test_add_func("/mixed/interpolation", simple_interpolation); - g_test_add_func("/errors/empty_input", empty_input); + g_test_add_func("/errors/empty", empty_input); + g_test_add_func("/errors/blank", blank_input); + g_test_add_func("/errors/junk", junk_input); g_test_add_func("/errors/unterminated/string", unterminated_string); g_test_add_func("/errors/unterminated/escape", unterminated_escape); g_test_add_func("/errors/unterminated/sq_string", unterminated_sq_string); @@ -1455,6 +1496,7 @@ int main(int argc, char **argv) g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma); g_test_add_func("/errors/unterminated/literal", unterminated_literal); g_test_add_func("/errors/limits/nesting", limits_nesting); + g_test_add_func("/errors/multiple_values", multiple_values); return g_test_run(); } diff --git a/tests/cpu-plug-test.c b/tests/cpu-plug-test.c index f5d57da60e..3e93c8e096 100644 --- a/tests/cpu-plug-test.c +++ b/tests/cpu-plug-test.c @@ -257,11 +257,11 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_cb_for_every_machine(add_pc_test_case); + qtest_cb_for_every_machine(add_pc_test_case, g_test_quick()); } else if (g_str_equal(arch, "ppc64")) { - qtest_cb_for_every_machine(add_pseries_test_case); + qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick()); } else if (g_str_equal(arch, "s390x")) { - qtest_cb_for_every_machine(add_s390x_test_case); + qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick()); } return g_test_run(); diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index 0b4f221c29..a25092dfaa 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -103,7 +103,14 @@ static QList *device_type_list(bool abstract) static void test_one_device(const char *type) { QDict *resp; - char *help, *qom_tree; + char *help; + char *qom_tree_start, *qom_tree_end; + char *qtree_start, *qtree_end; + + g_test_message("Testing device '%s'", type); + + qom_tree_start = hmp("info qom-tree"); + qtree_start = hmp("info qtree"); resp = qmp("{'execute': 'device-list-properties'," " 'arguments': {'typename': %s}}", @@ -115,10 +122,18 @@ static void test_one_device(const char *type) /* * Some devices leave dangling pointers in QOM behind. - * "info qom-tree" has a good chance at crashing then + * "info qom-tree" or "info qtree" have a good chance at crashing then. + * Also make sure that the tree did not change. */ - qom_tree = hmp("info qom-tree"); - g_free(qom_tree); + qom_tree_end = hmp("info qom-tree"); + g_assert_cmpstr(qom_tree_start, ==, qom_tree_end); + g_free(qom_tree_start); + g_free(qom_tree_end); + + qtree_end = hmp("info qtree"); + g_assert_cmpstr(qtree_start, ==, qtree_end); + g_free(qtree_start); + g_free(qtree_end); } static void test_device_intro_list(void) @@ -206,13 +221,13 @@ static void test_device_intro_abstract(void) qtest_end(); } -static void test_device_intro_concrete(void) +static void test_device_intro_concrete(const void *args) { QList *types; QListEntry *entry; const char *type; - qtest_start(common_args); + qtest_start(args); types = device_type_list(false); QLIST_FOREACH_ENTRY(types, entry) { @@ -224,6 +239,7 @@ static void test_device_intro_concrete(void) qobject_unref(types); qtest_end(); + g_free((void *)args); } static void test_abstract_interfaces(void) @@ -260,6 +276,26 @@ static void test_abstract_interfaces(void) qtest_end(); } +static void add_machine_test_case(const char *mname) +{ + char *path, *args; + + /* Ignore blacklisted machines */ + if (g_str_equal("xenfv", mname) || g_str_equal("xenpv", mname)) { + return; + } + + path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname); + args = g_strdup_printf("-M %s", mname); + qtest_add_data_func(path, args, test_device_intro_concrete); + g_free(path); + + path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname); + args = g_strdup_printf("-nodefaults -M %s", mname); + qtest_add_data_func(path, args, test_device_intro_concrete); + g_free(path); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -268,8 +304,13 @@ int main(int argc, char **argv) qtest_add_func("device/introspect/list-fields", test_qom_list_fields); qtest_add_func("device/introspect/none", test_device_intro_none); qtest_add_func("device/introspect/abstract", test_device_intro_abstract); - qtest_add_func("device/introspect/concrete", test_device_intro_concrete); qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces); + if (g_test_quick()) { + qtest_add_data_func("device/introspect/concrete/defaults/none", + g_strdup(common_args), test_device_intro_concrete); + } else { + qtest_cb_for_every_machine(add_machine_test_case, true); + } return g_test_run(); } diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c index 2d0b176b36..673c10140f 100644 --- a/tests/drive_del-test.c +++ b/tests/drive_del-test.c @@ -65,9 +65,13 @@ static void test_drive_without_dev(void) static void test_after_failed_device_add(void) { + char driver[32]; QDict *response; QDict *error; + snprintf(driver, sizeof(driver), "virtio-blk-%s", + qvirtio_get_dev_type()); + qtest_start("-drive if=none,id=drive0"); /* Make device_add fail. If this leaks the virtio-blk device then a @@ -75,9 +79,9 @@ static void test_after_failed_device_add(void) */ response = qmp("{'execute': 'device_add'," " 'arguments': {" - " 'driver': 'virtio-blk-%s'," + " 'driver': %s," " 'drive': 'drive0'" - "}}", qvirtio_get_dev_type()); + "}}", driver); g_assert(response); error = qdict_get_qdict(response, "error"); g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, "GenericError"); diff --git a/tests/fw_cfg-test.c b/tests/fw_cfg-test.c index 1548bf14b2..1c5103fe1c 100644 --- a/tests/fw_cfg-test.c +++ b/tests/fw_cfg-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "hw/nvram/fw_cfg_keys.h" +#include "standard-headers/linux/qemu_fw_cfg.h" #include "libqos/fw_cfg.h" static uint64_t ram_size = 128 << 20; diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c index 634b9c288a..b83cb8f0af 100644 --- a/tests/libqos/malloc-pc.c +++ b/tests/libqos/malloc-pc.c @@ -14,7 +14,7 @@ #include "libqos/malloc-pc.h" #include "libqos/fw_cfg.h" -#include "hw/nvram/fw_cfg_keys.h" +#include "standard-headers/linux/qemu_fw_cfg.h" #include "qemu-common.h" diff --git a/tests/libqtest.c b/tests/libqtest.c index 852ccff1ce..d635c5bea0 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -21,10 +21,10 @@ #include <sys/un.h> #include "libqtest.h" +#include "qemu-common.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-streamer.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" @@ -446,14 +446,15 @@ typedef struct { QDict *response; } QMPResponseParser; -static void qmp_response(JSONMessageParser *parser, GQueue *tokens) +static void qmp_response(void *opaque, QObject *obj, Error *err) { - QMPResponseParser *qmp = container_of(parser, QMPResponseParser, parser); - QObject *obj; + QMPResponseParser *qmp = opaque; - obj = json_parser_parse(tokens, NULL); - if (!obj) { - fprintf(stderr, "QMP JSON response parsing failed\n"); + assert(!obj != !err); + + if (err) { + error_prepend(&err, "QMP JSON response parsing failed: "); + error_report_err(err); abort(); } @@ -468,7 +469,7 @@ QDict *qmp_fd_receive(int fd) bool log = getenv("QTEST_LOG") != NULL; qmp.response = NULL; - json_message_parser_init(&qmp.parser, qmp_response); + json_message_parser_init(&qmp.parser, qmp_response, &qmp, NULL); while (!qmp.response) { ssize_t len; char c; @@ -507,16 +508,6 @@ void qmp_fd_vsend(int fd, const char *fmt, va_list ap) { QObject *qobj; - /* - * qobject_from_vjsonf_nofail() chokes on leading 0xff as invalid - * JSON, but tests/test-qga.c needs to send that to test QGA - * synchronization - */ - if (*fmt == '\377') { - socket_send(fd, fmt, 1); - fmt++; - } - /* Going through qobject ensures we escape strings properly */ qobj = qobject_from_vjsonf_nofail(fmt, ap); @@ -604,6 +595,36 @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...) va_end(ap); } +void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) +{ + bool log = getenv("QTEST_LOG") != NULL; + char *str = g_strdup_vprintf(fmt, ap); + + if (log) { + fprintf(stderr, "%s", str); + } + socket_send(fd, str, strlen(str)); + g_free(str); +} + +void qmp_fd_send_raw(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_vsend_raw(fd, fmt, ap); + va_end(ap); +} + +void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_vsend_raw(s->qmp_fd, fmt, ap); + va_end(ap); +} + QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) { QDict *response; @@ -991,7 +1012,53 @@ bool qtest_big_endian(QTestState *s) return s->big_endian; } -void qtest_cb_for_every_machine(void (*cb)(const char *machine)) +static bool qtest_check_machine_version(const char *mname, const char *basename, + int major, int minor) +{ + char *newname; + bool is_equal; + + newname = g_strdup_printf("%s-%i.%i", basename, major, minor); + is_equal = g_str_equal(mname, newname); + g_free(newname); + + return is_equal; +} + +static bool qtest_is_old_versioned_machine(const char *mname) +{ + const char *dash = strrchr(mname, '-'); + const char *dot = strrchr(mname, '.'); + const char *chr; + char *bname; + const int major = QEMU_VERSION_MAJOR; + const int minor = QEMU_VERSION_MINOR; + bool res = false; + + if (dash && dot && dot > dash) { + for (chr = dash + 1; *chr; chr++) { + if (!qemu_isdigit(*chr) && *chr != '.') { + return false; + } + } + /* + * Now check if it is one of the latest versions. Check major + 1 + * and minor + 1 versions as well, since they might already exist + * in the development branch. + */ + bname = g_strdup(mname); + bname[dash - mname] = 0; + res = !qtest_check_machine_version(mname, bname, major + 1, 0) && + !qtest_check_machine_version(mname, bname, major, minor + 1) && + !qtest_check_machine_version(mname, bname, major, minor); + g_free(bname); + } + + return res; +} + +void qtest_cb_for_every_machine(void (*cb)(const char *machine), + bool skip_old_versioned) { QDict *response, *minfo; QList *list; @@ -1014,7 +1081,9 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine)) qstr = qobject_to(QString, qobj); g_assert(qstr); mname = qstring_get_str(qstr); - cb(mname); + if (!skip_old_versioned || !qtest_is_old_versioned_machine(mname)) { + cb(mname); + } } qtest_end(); diff --git a/tests/libqtest.h b/tests/libqtest.h index def1edaafa..36d5caecd4 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -97,6 +97,17 @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /** + * qtest_qmp_send_raw: + * @s: #QTestState instance to operate on. + * @fmt...: text to send, formatted like sprintf() + * + * Sends text to the QMP monitor verbatim. Need not be valid JSON; + * this is useful for negative tests. + */ +void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + +/** * qtest_qmpv: * @s: #QTestState instance to operate on. * @fmt: QMP message to send to QEMU, formatted like @@ -948,16 +959,20 @@ static inline int64_t clock_set(int64_t val) QDict *qmp_fd_receive(int fd); void qmp_fd_vsend(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); void qmp_fd_send(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void qmp_fd_send_raw(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); QDict *qmp_fdv(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); QDict *qmp_fd(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /** * qtest_cb_for_every_machine: * @cb: Pointer to the callback function + * @skip_old_versioned: true if versioned old machine types should be skipped * * Call a callback function for every name of all available machines. */ -void qtest_cb_for_every_machine(void (*cb)(const char *machine)); +void qtest_cb_for_every_machine(void (*cb)(const char *machine), + bool skip_old_versioned); /** * qtest_qmp_device_add: diff --git a/tests/migration-test.c b/tests/migration-test.c index eb58d0a48e..0e687b7512 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -438,15 +438,6 @@ static int test_migrate_start(QTestState **from, QTestState **to, " -incoming %s", accel, tmpfs, bootpath, uri); } else if (strcmp(arch, "ppc64") == 0) { - - /* On ppc64, the test only works with kvm-hv, but not with kvm-pr - * and TCG is touchy due to race conditions on dirty bits - * (especially on PPC for some reason) - */ - if (access("/sys/module/kvm_hv", F_OK)) { - g_print("Skipping test: kvm_hv not available "); - return -1; - } cmd_src = g_strdup_printf("-machine accel=%s -m 256M" " -name source,debug-threads=on" " -serial file:%s/src_serial" @@ -750,6 +741,17 @@ int main(int argc, char **argv) return 0; } + /* + * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG + * is touchy due to race conditions on dirty bits (especially on PPC for + * some reason) + */ + if (g_str_equal(qtest_get_arch(), "ppc64") && + access("/sys/module/kvm_hv", F_OK)) { + g_test_message("Skipping test: kvm_hv not available"); + return 0; + } + tmpfs = mkdtemp(template); if (!tmpfs) { g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno)); diff --git a/tests/qemu-iotests/229 b/tests/qemu-iotests/229 new file mode 100755 index 0000000000..ff851ec431 --- /dev/null +++ b/tests/qemu-iotests/229 @@ -0,0 +1,95 @@ +#!/bin/bash +# +# Test for force canceling a running blockjob that is paused in +# an error state. +# +# Copyright (C) 2018 Red Hat, Inc. +# +# 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/>. +# + +# creator +owner=jcody@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _cleanup_test_img + rm -f "$TEST_IMG" "$DEST_IMG" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +# Needs backing file and backing format support +_supported_fmt qcow2 qed +_supported_proto file +_supported_os Linux + + +DEST_IMG="$TEST_DIR/d.$IMGFMT" +TEST_IMG="$TEST_DIR/b.$IMGFMT" + +_make_test_img 2M + +# destination for mirror will be too small, causing error +TEST_IMG=$DEST_IMG _make_test_img 1M + +$QEMU_IO -c 'write 0 2M' "$TEST_IMG" | _filter_qemu_io + +_launch_qemu -drive id=testdisk,file="$TEST_IMG",format="$IMGFMT" + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + +echo +echo '=== Starting drive-mirror, causing error & stop ===' +echo + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'drive-mirror', + 'arguments': {'device': 'testdisk', + 'mode': 'absolute-paths', + 'format': '$IMGFMT', + 'target': '$DEST_IMG', + 'sync': 'full', + 'mode': 'existing', + 'on-source-error': 'stop', + 'on-target-error': 'stop' }}" \ + "JOB_STATUS_CHANGE.*pause" + +echo +echo '=== Force cancel job paused in error state ===' +echo + +success_or_failure="y" _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'block-job-cancel', + 'arguments': { 'device': 'testdisk', + 'force': true}}" \ + "BLOCK_JOB_CANCELLED" "Assertion" + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/229.out b/tests/qemu-iotests/229.out new file mode 100644 index 0000000000..4c4112805f --- /dev/null +++ b/tests/qemu-iotests/229.out @@ -0,0 +1,23 @@ +QA output created by 229 +Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=2097152 +Formatting 'TEST_DIR/d.IMGFMT', fmt=IMGFMT size=1048576 +wrote 2097152/2097152 bytes at offset 0 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} + +=== Starting drive-mirror, causing error & stop === + +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "testdisk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "testdisk", "operation": "write", "action": "stop"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "testdisk"}} + +=== Force cancel job paused in error state === + +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "testdisk", "operation": "write", "action": "stop"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "testdisk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "testdisk", "len": 2097152, "offset": 1048576, "speed": 0, "type": "mirror"}} +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index b973dc842d..743790745b 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -225,3 +225,4 @@ 225 rw auto quick 226 auto quick 227 auto quick +229 auto quick diff --git a/tests/qmp-cmd-test.c b/tests/qmp-cmd-test.c new file mode 100644 index 0000000000..c5b70df974 --- /dev/null +++ b/tests/qmp-cmd-test.c @@ -0,0 +1,213 @@ +/* + * QMP command test cases + * + * Copyright (c) 2017 Red Hat Inc. + * + * Authors: + * Markus Armbruster <armbru@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 "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-introspect.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qobject-input-visitor.h" + +const char common_args[] = "-nodefaults -machine none"; + +/* Query smoke tests */ + +static int query_error_class(const char *cmd) +{ + static struct { + const char *cmd; + int err_class; + } fails[] = { + /* Success depends on build configuration: */ +#ifndef CONFIG_SPICE + { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, +#endif +#ifndef CONFIG_VNC + { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, + { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, +#endif +#ifndef CONFIG_REPLICATION + { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, +#endif + /* Likewise, and require special QEMU command-line arguments: */ + { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, + { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, + { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, + { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, + { NULL, -1 } + }; + int i; + + for (i = 0; fails[i].cmd; i++) { + if (!strcmp(cmd, fails[i].cmd)) { + return fails[i].err_class; + } + } + return -1; +} + +static void test_query(const void *data) +{ + const char *cmd = data; + int expected_error_class = query_error_class(cmd); + QDict *resp, *error; + const char *error_class; + + qtest_start(common_args); + + resp = qmp("{ 'execute': %s }", cmd); + error = qdict_get_qdict(resp, "error"); + error_class = error ? qdict_get_str(error, "class") : NULL; + + if (expected_error_class < 0) { + g_assert(qdict_haskey(resp, "return")); + } else { + g_assert(error); + g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, + -1, &error_abort), + ==, expected_error_class); + } + qobject_unref(resp); + + qtest_end(); +} + +static bool query_is_blacklisted(const char *cmd) +{ + const char *blacklist[] = { + /* Not actually queries: */ + "add-fd", + /* Success depends on target arch: */ + "query-cpu-definitions", /* arm, i386, ppc, s390x */ + "query-gic-capabilities", /* arm */ + /* Success depends on target-specific build configuration: */ + "query-pci", /* CONFIG_PCI */ + /* Success depends on launching SEV guest */ + "query-sev-launch-measure", + /* Success depends on Host or Hypervisor SEV support */ + "query-sev", + "query-sev-capabilities", + NULL + }; + int i; + + for (i = 0; blacklist[i]; i++) { + if (!strcmp(cmd, blacklist[i])) { + return true; + } + } + return false; +} + +typedef struct { + SchemaInfoList *list; + GHashTable *hash; +} QmpSchema; + +static void qmp_schema_init(QmpSchema *schema) +{ + QDict *resp; + Visitor *qiv; + SchemaInfoList *tail; + + qtest_start(common_args); + resp = qmp("{ 'execute': 'query-qmp-schema' }"); + + qiv = qobject_input_visitor_new(qdict_get(resp, "return")); + visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); + visit_free(qiv); + + qobject_unref(resp); + qtest_end(); + + schema->hash = g_hash_table_new(g_str_hash, g_str_equal); + + /* Build @schema: hash table mapping entity name to SchemaInfo */ + for (tail = schema->list; tail; tail = tail->next) { + g_hash_table_insert(schema->hash, tail->value->name, tail->value); + } +} + +static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) +{ + return g_hash_table_lookup(schema->hash, name); +} + +static void qmp_schema_cleanup(QmpSchema *schema) +{ + qapi_free_SchemaInfoList(schema->list); + g_hash_table_destroy(schema->hash); +} + +static bool object_type_has_mandatory_members(SchemaInfo *type) +{ + SchemaInfoObjectMemberList *tail; + + g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); + + for (tail = type->u.object.members; tail; tail = tail->next) { + if (!tail->value->has_q_default) { + return true; + } + } + + return false; +} + +static void add_query_tests(QmpSchema *schema) +{ + SchemaInfoList *tail; + SchemaInfo *si, *arg_type, *ret_type; + char *test_name; + + /* Test the query-like commands */ + for (tail = schema->list; tail; tail = tail->next) { + si = tail->value; + if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { + continue; + } + + if (query_is_blacklisted(si->name)) { + continue; + } + + arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); + if (object_type_has_mandatory_members(arg_type)) { + continue; + } + + ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); + if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT + && !ret_type->u.object.members) { + continue; + } + + test_name = g_strdup_printf("qmp/%s", si->name); + qtest_add_data_func(test_name, si->name, test_query); + g_free(test_name); + } +} + +int main(int argc, char *argv[]) +{ + QmpSchema schema; + int ret; + + g_test_init(&argc, &argv, NULL); + + qmp_schema_init(&schema); + add_query_tests(&schema); + ret = g_test_run(); + + qmp_schema_cleanup(&schema); + return ret; +} diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 487ef946ed..4ae2245484 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -1,10 +1,10 @@ /* * QMP protocol test cases * - * Copyright (c) 2017 Red Hat Inc. + * Copyright (c) 2017-2018 Red Hat Inc. * * Authors: - * Markus Armbruster <armbru@redhat.com>, + * Markus Armbruster <armbru@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. @@ -13,13 +13,10 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qapi/error.h" -#include "qapi/qapi-visit-introspect.h" #include "qapi/qapi-visit-misc.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/util.h" -#include "qapi/visitor.h" #include "qapi/qmp/qstring.h" const char common_args[] = "-nodefaults -machine none"; @@ -45,10 +42,67 @@ static void test_version(QObject *version) visit_free(v); } +static bool recovered(QTestState *qts) +{ + QDict *resp; + bool ret; + + resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd' }"); + ret = !strcmp(get_error_class(resp), "CommandNotFound"); + qobject_unref(resp); + return ret; +} + static void test_malformed(QTestState *qts) { QDict *resp; + /* syntax error */ + qtest_qmp_send_raw(qts, "{]\n"); + resp = qtest_qmp_receive(qts); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); + qobject_unref(resp); + g_assert(recovered(qts)); + + /* lexical error: impossible byte outside string */ + qtest_qmp_send_raw(qts, "{\xFF"); + resp = qtest_qmp_receive(qts); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); + qobject_unref(resp); + g_assert(recovered(qts)); + + /* lexical error: funny control character outside string */ + qtest_qmp_send_raw(qts, "{\x01"); + resp = qtest_qmp_receive(qts); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); + qobject_unref(resp); + g_assert(recovered(qts)); + + /* lexical error: impossible byte in string */ + qtest_qmp_send_raw(qts, "{'bad \xFF"); + resp = qtest_qmp_receive(qts); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); + qobject_unref(resp); + g_assert(recovered(qts)); + + /* lexical error: control character in string */ + qtest_qmp_send_raw(qts, "{'execute': 'nonexistent', 'id':'\n"); + resp = qtest_qmp_receive(qts); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); + qobject_unref(resp); + g_assert(recovered(qts)); + + /* lexical error: interpolation */ + qtest_qmp_send_raw(qts, "%%p\n"); + /* two errors, one for "%", one for "p" */ + resp = qtest_qmp_receive(qts); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); + qobject_unref(resp); + resp = qtest_qmp_receive(qts); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); + qobject_unref(resp); + g_assert(recovered(qts)); + /* Not even a dictionary */ resp = qtest_qmp(qts, "null"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); @@ -253,184 +307,6 @@ static void test_qmp_oob(void) qtest_quit(qts); } -/* Query smoke tests */ - -static int query_error_class(const char *cmd) -{ - static struct { - const char *cmd; - int err_class; - } fails[] = { - /* Success depends on build configuration: */ -#ifndef CONFIG_SPICE - { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, -#endif -#ifndef CONFIG_VNC - { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, - { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, -#endif -#ifndef CONFIG_REPLICATION - { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, -#endif - /* Likewise, and require special QEMU command-line arguments: */ - { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, - { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, - { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, - { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, - { NULL, -1 } - }; - int i; - - for (i = 0; fails[i].cmd; i++) { - if (!strcmp(cmd, fails[i].cmd)) { - return fails[i].err_class; - } - } - return -1; -} - -static void test_query(const void *data) -{ - const char *cmd = data; - int expected_error_class = query_error_class(cmd); - QDict *resp, *error; - const char *error_class; - - qtest_start(common_args); - - resp = qmp("{ 'execute': %s }", cmd); - error = qdict_get_qdict(resp, "error"); - error_class = error ? qdict_get_str(error, "class") : NULL; - - if (expected_error_class < 0) { - g_assert(qdict_haskey(resp, "return")); - } else { - g_assert(error); - g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, - -1, &error_abort), - ==, expected_error_class); - } - qobject_unref(resp); - - qtest_end(); -} - -static bool query_is_blacklisted(const char *cmd) -{ - const char *blacklist[] = { - /* Not actually queries: */ - "add-fd", - /* Success depends on target arch: */ - "query-cpu-definitions", /* arm, i386, ppc, s390x */ - "query-gic-capabilities", /* arm */ - /* Success depends on target-specific build configuration: */ - "query-pci", /* CONFIG_PCI */ - /* Success depends on launching SEV guest */ - "query-sev-launch-measure", - /* Success depends on Host or Hypervisor SEV support */ - "query-sev", - "query-sev-capabilities", - NULL - }; - int i; - - for (i = 0; blacklist[i]; i++) { - if (!strcmp(cmd, blacklist[i])) { - return true; - } - } - return false; -} - -typedef struct { - SchemaInfoList *list; - GHashTable *hash; -} QmpSchema; - -static void qmp_schema_init(QmpSchema *schema) -{ - QDict *resp; - Visitor *qiv; - SchemaInfoList *tail; - - qtest_start(common_args); - resp = qmp("{ 'execute': 'query-qmp-schema' }"); - - qiv = qobject_input_visitor_new(qdict_get(resp, "return")); - visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); - visit_free(qiv); - - qobject_unref(resp); - qtest_end(); - - schema->hash = g_hash_table_new(g_str_hash, g_str_equal); - - /* Build @schema: hash table mapping entity name to SchemaInfo */ - for (tail = schema->list; tail; tail = tail->next) { - g_hash_table_insert(schema->hash, tail->value->name, tail->value); - } -} - -static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) -{ - return g_hash_table_lookup(schema->hash, name); -} - -static void qmp_schema_cleanup(QmpSchema *schema) -{ - qapi_free_SchemaInfoList(schema->list); - g_hash_table_destroy(schema->hash); -} - -static bool object_type_has_mandatory_members(SchemaInfo *type) -{ - SchemaInfoObjectMemberList *tail; - - g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); - - for (tail = type->u.object.members; tail; tail = tail->next) { - if (!tail->value->has_q_default) { - return true; - } - } - - return false; -} - -static void add_query_tests(QmpSchema *schema) -{ - SchemaInfoList *tail; - SchemaInfo *si, *arg_type, *ret_type; - char *test_name; - - /* Test the query-like commands */ - for (tail = schema->list; tail; tail = tail->next) { - si = tail->value; - if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { - continue; - } - - if (query_is_blacklisted(si->name)) { - continue; - } - - arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); - if (object_type_has_mandatory_members(arg_type)) { - continue; - } - - ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); - if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT - && !ret_type->u.object.members) { - continue; - } - - test_name = g_strdup_printf("qmp/%s", si->name); - qtest_add_data_func(test_name, si->name, test_query); - g_free(test_name); - } -} - /* Preconfig tests */ static void test_qmp_preconfig(void) @@ -474,19 +350,11 @@ static void test_qmp_preconfig(void) int main(int argc, char *argv[]) { - QmpSchema schema; - int ret; - g_test_init(&argc, &argv, NULL); qtest_add_func("qmp/protocol", test_qmp_protocol); qtest_add_func("qmp/oob", test_qmp_oob); - qmp_schema_init(&schema); - add_query_tests(&schema); qtest_add_func("qmp/preconfig", test_qmp_preconfig); - ret = g_test_run(); - - qmp_schema_cleanup(&schema); - return ret; + return g_test_run(); } diff --git a/tests/qom-test.c b/tests/qom-test.c index e6f712cbd3..73c52af3bb 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -123,7 +123,7 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - qtest_cb_for_every_machine(add_machine_test_case); + qtest_cb_for_every_machine(add_machine_test_case, g_test_quick()); return g_test_run(); } diff --git a/tests/test-char.c b/tests/test-char.c index 5905d31441..2a2ff32904 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -56,7 +56,6 @@ static void fe_event(void *opaque, int event) } } -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS #ifdef _WIN32 static void char_console_test_subprocess(void) { @@ -106,7 +105,6 @@ static void char_stdio_test(void) g_test_trap_assert_passed(); g_test_trap_assert_stdout("buf"); } -#endif static void char_ringbuf_test(void) { @@ -807,14 +805,12 @@ int main(int argc, char **argv) g_test_add_func("/char/invalid", char_invalid_test); g_test_add_func("/char/ringbuf", char_ringbuf_test); g_test_add_func("/char/mux", char_mux_test); -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS #ifdef _WIN32 g_test_add_func("/char/console/subprocess", char_console_test_subprocess); g_test_add_func("/char/console", char_console_test); #endif g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess); g_test_add_func("/char/stdio", char_stdio_test); -#endif #ifndef _WIN32 g_test_add_func("/char/pipe", char_pipe_test); #endif diff --git a/tests/test-hmp.c b/tests/test-hmp.c index 5352c9c088..1a3a9c5099 100644 --- a/tests/test-hmp.c +++ b/tests/test-hmp.c @@ -158,7 +158,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - qtest_cb_for_every_machine(add_machine_test_case); + qtest_cb_for_every_machine(add_machine_test_case, g_test_quick()); /* as none machine has no memory by default, add a test case with memory */ qtest_add_data_func("hmp/none+2MB", g_strdup("none -m 2"), test_machine); diff --git a/tests/test-qga.c b/tests/test-qga.c index c552cc0125..f69cdf6c03 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -147,8 +147,9 @@ static void test_qga_sync_delimited(gconstpointer fix) unsigned char c; QDict *ret; + qmp_fd_send_raw(fixture->fd, "\xff"); qmp_fd_send(fixture->fd, - "\xff{'execute': 'guest-sync-delimited'," + "{'execute': 'guest-sync-delimited'," " 'arguments': {'id': %u } }", r); diff --git a/tests/test-rcu-list.c b/tests/test-rcu-list.c index 1514d7ec97..192bfbf02e 100644 --- a/tests/test-rcu-list.c +++ b/tests/test-rcu-list.c @@ -44,7 +44,7 @@ static int nthreadsrunning; #define GOFLAG_RUN 1 #define GOFLAG_STOP 2 -static volatile int goflag = GOFLAG_INIT; +static int goflag = GOFLAG_INIT; #define RCU_READ_RUN 1000 #define RCU_UPDATE_RUN 10 @@ -82,9 +82,20 @@ static void wait_all_threads(void) n_threads = 0; } +#ifndef TEST_LIST_TYPE +#define TEST_LIST_TYPE 1 +#endif struct list_element { +#if TEST_LIST_TYPE == 1 QLIST_ENTRY(list_element) entry; +#elif TEST_LIST_TYPE == 2 + QSIMPLEQ_ENTRY(list_element) entry; +#elif TEST_LIST_TYPE == 3 + QTAILQ_ENTRY(list_element) entry; +#else +#error Invalid TEST_LIST_TYPE +#endif struct rcu_head rcu; }; @@ -96,8 +107,47 @@ static void reclaim_list_el(struct rcu_head *prcu) n_reclaims++; } +#if TEST_LIST_TYPE == 1 static QLIST_HEAD(q_list_head, list_element) Q_list_head; +#define TEST_NAME "qlist" +#define TEST_LIST_REMOVE_RCU QLIST_REMOVE_RCU +#define TEST_LIST_INSERT_AFTER_RCU QLIST_INSERT_AFTER_RCU +#define TEST_LIST_INSERT_HEAD_RCU QLIST_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QLIST_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QLIST_FOREACH_SAFE_RCU + +#elif TEST_LIST_TYPE == 2 +static QSIMPLEQ_HEAD(, list_element) Q_list_head = + QSIMPLEQ_HEAD_INITIALIZER(Q_list_head); + +#define TEST_NAME "qsimpleq" +#define TEST_LIST_REMOVE_RCU(el, f) \ + QSIMPLEQ_REMOVE_RCU(&Q_list_head, el, list_element, f) + +#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ + QSIMPLEQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) + +#define TEST_LIST_INSERT_HEAD_RCU QSIMPLEQ_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QSIMPLEQ_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QSIMPLEQ_FOREACH_SAFE_RCU + +#elif TEST_LIST_TYPE == 3 +static QTAILQ_HEAD(, list_element) Q_list_head; + +#define TEST_NAME "qtailq" +#define TEST_LIST_REMOVE_RCU(el, f) QTAILQ_REMOVE_RCU(&Q_list_head, el, f) + +#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ + QTAILQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) + +#define TEST_LIST_INSERT_HEAD_RCU QTAILQ_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QTAILQ_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QTAILQ_FOREACH_SAFE_RCU +#else +#error Invalid TEST_LIST_TYPE +#endif + static void *rcu_q_reader(void *arg) { long long n_reads_local = 0; @@ -107,15 +157,15 @@ static void *rcu_q_reader(void *arg) *(struct rcu_reader_data **)arg = &rcu_reader; atomic_inc(&nthreadsrunning); - while (goflag == GOFLAG_INIT) { + while (atomic_read(&goflag) == GOFLAG_INIT) { g_usleep(1000); } - while (goflag == GOFLAG_RUN) { + while (atomic_read(&goflag) == GOFLAG_RUN) { rcu_read_lock(); - QLIST_FOREACH_RCU(el, &Q_list_head, entry) { + TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) { n_reads_local++; - if (goflag == GOFLAG_STOP) { + if (atomic_read(&goflag) == GOFLAG_STOP) { break; } } @@ -142,35 +192,35 @@ static void *rcu_q_updater(void *arg) *(struct rcu_reader_data **)arg = &rcu_reader; atomic_inc(&nthreadsrunning); - while (goflag == GOFLAG_INIT) { + while (atomic_read(&goflag) == GOFLAG_INIT) { g_usleep(1000); } - while (goflag == GOFLAG_RUN) { + while (atomic_read(&goflag) == GOFLAG_RUN) { target_el = select_random_el(RCU_Q_LEN); j = 0; /* FOREACH_RCU could work here but let's use both macros */ - QLIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { j++; if (target_el == j) { - QLIST_REMOVE_RCU(prev_el, entry); + TEST_LIST_REMOVE_RCU(prev_el, entry); /* may be more than one updater in the future */ call_rcu1(&prev_el->rcu, reclaim_list_el); n_removed_local++; break; } } - if (goflag == GOFLAG_STOP) { + if (atomic_read(&goflag) == GOFLAG_STOP) { break; } target_el = select_random_el(RCU_Q_LEN); j = 0; - QLIST_FOREACH_RCU(el, &Q_list_head, entry) { + TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) { j++; if (target_el == j) { - prev_el = g_new(struct list_element, 1); + struct list_element *new_el = g_new(struct list_element, 1); n_nodes += n_nodes_local; - QLIST_INSERT_BEFORE_RCU(el, prev_el, entry); + TEST_LIST_INSERT_AFTER_RCU(el, new_el, entry); break; } } @@ -195,7 +245,7 @@ static void rcu_qtest_init(void) srand(time(0)); for (i = 0; i < RCU_Q_LEN; i++) { new_el = g_new(struct list_element, 1); - QLIST_INSERT_HEAD_RCU(&Q_list_head, new_el, entry); + TEST_LIST_INSERT_HEAD_RCU(&Q_list_head, new_el, entry); } qemu_mutex_lock(&counts_mutex); n_nodes += RCU_Q_LEN; @@ -209,9 +259,9 @@ static void rcu_qtest_run(int duration, int nreaders) g_usleep(1000); } - goflag = GOFLAG_RUN; + atomic_set(&goflag, GOFLAG_RUN); sleep(duration); - goflag = GOFLAG_STOP; + atomic_set(&goflag, GOFLAG_STOP); wait_all_threads(); } @@ -230,8 +280,8 @@ static void rcu_qtest(const char *test, int duration, int nreaders) create_thread(rcu_q_updater); rcu_qtest_run(duration, nreaders); - QLIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { - QLIST_REMOVE_RCU(prev_el, entry); + TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + TEST_LIST_REMOVE_RCU(prev_el, entry); call_rcu1(&prev_el->rcu, reclaim_list_el); n_removed_local++; } @@ -290,9 +340,9 @@ int main(int argc, char *argv[]) } else { gtest_seconds = 20; } - g_test_add_func("/rcu/qlist/single-threaded", gtest_rcuq_one); - g_test_add_func("/rcu/qlist/short-few", gtest_rcuq_few); - g_test_add_func("/rcu/qlist/long-many", gtest_rcuq_many); + g_test_add_func("/rcu/"TEST_NAME"/single-threaded", gtest_rcuq_one); + g_test_add_func("/rcu/"TEST_NAME"/short-few", gtest_rcuq_few); + g_test_add_func("/rcu/"TEST_NAME"/long-many", gtest_rcuq_many); g_test_in_charge = 1; return g_test_run(); } diff --git a/tests/test-rcu-simpleq.c b/tests/test-rcu-simpleq.c new file mode 100644 index 0000000000..057f7d33f7 --- /dev/null +++ b/tests/test-rcu-simpleq.c @@ -0,0 +1,2 @@ +#define TEST_LIST_TYPE 2 +#include "test-rcu-list.c" diff --git a/tests/test-rcu-tailq.c b/tests/test-rcu-tailq.c new file mode 100644 index 0000000000..8d487e0ee0 --- /dev/null +++ b/tests/test-rcu-tailq.c @@ -0,0 +1,2 @@ +#define TEST_LIST_TYPE 3 +#include "test-rcu-list.c" diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c index 84ce9c71ae..e75b959950 100644 --- a/tests/test-x86-cpuid-compat.c +++ b/tests/test-x86-cpuid-compat.c @@ -35,7 +35,6 @@ static QObject *qom_get(const char *path, const char *prop) return ret; } -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS static bool qom_get_bool(const char *path, const char *prop) { QBool *value = qobject_to(QBool, qom_get(path, prop)); @@ -44,7 +43,6 @@ static bool qom_get_bool(const char *path, const char *prop) qobject_unref(value); return b; } -#endif typedef struct CpuidTestArgs { const char *cmdline; @@ -168,7 +166,6 @@ static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline, return args; } -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS static void test_plus_minus_subprocess(void) { char *path; @@ -210,17 +207,14 @@ static void test_plus_minus(void) "Don't mix both \"+cx8\" and \"cx8=off\"*"); g_test_trap_assert_stdout(""); } -#endif int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess", test_plus_minus_subprocess); g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus); -#endif /* Original level values for CPU models: */ add_cpuid_test("x86/cpuid/phenom/level", diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index ca6251f5f8..716aff7153 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -768,7 +768,6 @@ static void wait_for_rings_started(TestServer *s, size_t count) g_mutex_unlock(&s->data_mutex); } -#if defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) static inline void test_server_connect(TestServer *server) { test_server_create_chr(server, ",reconnect=1"); @@ -893,7 +892,6 @@ static void test_flags_mismatch(void) g_free(path); } -#endif static void test_multiqueue(void) { @@ -975,7 +973,6 @@ int main(int argc, char **argv) qtest_add_func("/vhost-user/migrate", test_migrate); qtest_add_func("/vhost-user/multiqueue", test_multiqueue); -#if defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) /* keeps failing on build-system since Aug 15 2017 */ if (getenv("QTEST_VHOST_USER_FIXME")) { qtest_add_func("/vhost-user/reconnect/subprocess", @@ -988,7 +985,6 @@ int main(int argc, char **argv) test_flags_mismatch_subprocess); qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch); } -#endif ret = g_test_run(); diff --git a/tests/virtio-ccw-test.c b/tests/virtio-ccw-test.c new file mode 100644 index 0000000000..48c714d84c --- /dev/null +++ b/tests/virtio-ccw-test.c @@ -0,0 +1,110 @@ +/* + * QTest testcase for VirtIO CCW + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* Until we have a full libqos implementation of virtio-ccw (which requires + * also to add support for I/O channels to qtest), we can only do simple + * tests that initialize the devices. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/virtio.h" + +static void virtio_balloon_nop(void) +{ + global_qtest = qtest_initf("-device virtio-balloon-ccw"); + qtest_end(); +} + +static void virtconsole_nop(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw,id=vser0 " + "-device virtconsole,bus=vser0.0"); + qtest_end(); +} + +static void virtserialport_nop(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw,id=vser0 " + "-device virtserialport,bus=vser0.0"); + qtest_end(); +} + +static void virtio_serial_nop(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw"); + qtest_end(); +} + +static void virtio_serial_hotplug(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw"); + qtest_qmp_device_add("virtserialport", "hp-port", "{}"); + qtest_qmp_device_del("hp-port"); + qtest_end(); +} + +static void virtio_blk_nop(void) +{ + global_qtest = qtest_initf("-drive if=none,id=drv0,file=null-co://,format=raw " + "-device virtio-blk-ccw,drive=drv0"); + qtest_end(); +} + +static void virtio_net_nop(void) +{ + global_qtest = qtest_initf("-device virtio-net-ccw"); + qtest_end(); +} + +static void virtio_rng_nop(void) +{ + global_qtest = qtest_initf("-device virtio-rng-ccw"); + qtest_end(); +} + +static void virtio_scsi_nop(void) +{ + global_qtest = qtest_initf("-device virtio-scsi-ccw"); + qtest_end(); +} + +static void virtio_scsi_hotplug(void) +{ + global_qtest = qtest_initf("-drive if=none,id=drv0,file=null-co://,format=raw " + "-drive if=none,id=drv1,file=null-co://,format=raw " + "-device virtio-scsi-ccw " + "-device scsi-hd,drive=drv0"); + qtest_qmp_device_add("scsi-hd", "scsihd", "{'drive': 'drv1'}"); + qtest_qmp_device_del("scsihd"); + + qtest_end(); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/virtio/balloon/nop", virtio_balloon_nop); + qtest_add_func("/virtio/console/nop", virtconsole_nop); + qtest_add_func("/virtio/serialport/nop", virtserialport_nop); + qtest_add_func("/virtio/serial/nop", virtio_serial_nop); + qtest_add_func("/virtio/serial/hotplug", virtio_serial_hotplug); + qtest_add_func("/virtio/block/nop", virtio_blk_nop); + qtest_add_func("/virtio/net/nop", virtio_net_nop); + qtest_add_func("/virtio/rng/nop", virtio_rng_nop); + qtest_add_func("/virtio/scsi/nop", virtio_scsi_nop); + qtest_add_func("/virtio/scsi/hotplug", virtio_scsi_hotplug); + + ret = g_test_run(); + + return ret; +} diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index d7149dea7d..7e58d9e0ca 100755 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -176,7 +176,7 @@ class BaseVM(object): raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \ usernet_info) - def wait_ssh(self, seconds=120): + def wait_ssh(self, seconds=300): starttime = datetime.datetime.now() guest_up = False while (datetime.datetime.now() - starttime).total_seconds() < seconds: diff --git a/util/Makefile.objs b/util/Makefile.objs index e1c3fed4dc..e958116c86 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -44,6 +44,7 @@ util-obj-y += log.o util-obj-y += pagesize.o util-obj-y += qdist.o util-obj-y += qht.o +util-obj-y += qsp.o util-obj-y += range.o util-obj-y += stats64.o util-obj-y += systemd.o diff --git a/util/module.c b/util/module.c index c90973721f..1259dd3686 100644 --- a/util/module.c +++ b/util/module.c @@ -162,9 +162,10 @@ void module_load_one(const char *prefix, const char *lib_name) #ifdef CONFIG_MODULES char *fname = NULL; char *exec_dir; - char *dirs[3]; + const char *search_dir; + char *dirs[4]; char *module_name; - int i = 0; + int i = 0, n_dirs = 0; int ret; static GHashTable *loaded_modules; @@ -186,14 +187,19 @@ void module_load_one(const char *prefix, const char *lib_name) g_hash_table_insert(loaded_modules, module_name, module_name); exec_dir = qemu_get_exec_dir(); - dirs[i++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); - dirs[i++] = g_strdup_printf("%s/..", exec_dir ? : ""); - dirs[i++] = g_strdup_printf("%s", exec_dir ? : ""); - assert(i == ARRAY_SIZE(dirs)); + search_dir = getenv("QEMU_MODULE_DIR"); + if (search_dir != NULL) { + dirs[n_dirs++] = g_strdup_printf("%s", search_dir); + } + dirs[n_dirs++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); + dirs[n_dirs++] = g_strdup_printf("%s/..", exec_dir ? : ""); + dirs[n_dirs++] = g_strdup_printf("%s", exec_dir ? : ""); + assert(n_dirs <= ARRAY_SIZE(dirs)); + g_free(exec_dir); exec_dir = NULL; - for (i = 0; i < ARRAY_SIZE(dirs); i++) { + for (i = 0; i < n_dirs; i++) { fname = g_strdup_printf("%s/%s%s", dirs[i], module_name, HOST_DSOSUF); ret = module_load_file(fname); @@ -205,7 +211,7 @@ void module_load_one(const char *prefix, const char *lib_name) } } - for (i = 0; i < ARRAY_SIZE(dirs); i++) { + for (i = 0; i < n_dirs; i++) { g_free(dirs[i]); } diff --git a/util/oslib-win32.c b/util/oslib-win32.c index bb5ad28bd3..25dd1595ad 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -67,15 +67,24 @@ void *qemu_memalign(size_t alignment, size_t size) return qemu_oom_check(qemu_try_memalign(alignment, size)); } +static int get_allocation_granularity(void) +{ + SYSTEM_INFO system_info; + + GetSystemInfo(&system_info); + return system_info.dwAllocationGranularity; +} + void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared) { void *ptr; - /* FIXME: this is not exactly optimal solution since VirtualAlloc - has 64Kb granularity, but at least it guarantees us that the - memory is page aligned. */ ptr = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); trace_qemu_anon_ram_alloc(size, ptr); + + if (ptr && align) { + *align = MAX(get_allocation_granularity(), getpagesize()); + } return ptr; } diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index b303188a36..4a363ca675 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -97,13 +97,13 @@ void qemu_rec_mutex_destroy(QemuRecMutex *mutex) DeleteCriticalSection(&mutex->lock); } -void qemu_rec_mutex_lock(QemuRecMutex *mutex) +void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line) { assert(mutex->initialized); EnterCriticalSection(&mutex->lock); } -int qemu_rec_mutex_trylock(QemuRecMutex *mutex) +int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line) { assert(mutex->initialized); return !TryEnterCriticalSection(&mutex->lock); diff --git a/util/qht.c b/util/qht.c index c138777a9c..1e3a072e25 100644 --- a/util/qht.c +++ b/util/qht.c @@ -90,6 +90,33 @@ #endif /* + * Do _not_ use qemu_mutex_[try]lock directly! Use these macros, otherwise + * the profiler (QSP) will deadlock. + */ +static inline void qht_lock(struct qht *ht) +{ + if (ht->mode & QHT_MODE_RAW_MUTEXES) { + qemu_mutex_lock__raw(&ht->lock); + } else { + qemu_mutex_lock(&ht->lock); + } +} + +static inline int qht_trylock(struct qht *ht) +{ + if (ht->mode & QHT_MODE_RAW_MUTEXES) { + return qemu_mutex_trylock__raw(&(ht)->lock); + } + return qemu_mutex_trylock(&(ht)->lock); +} + +/* this inline is not really necessary, but it helps keep code consistent */ +static inline void qht_unlock(struct qht *ht) +{ + qemu_mutex_unlock(&ht->lock); +} + +/* * Note: reading partially-updated pointers in @pointers could lead to * segfaults. We thus access them with atomic_read/set; this guarantees * that the compiler makes all those accesses atomic. We also need the @@ -254,10 +281,10 @@ void qht_map_lock_buckets__no_stale(struct qht *ht, struct qht_map **pmap) qht_map_unlock_buckets(map); /* we raced with a resize; acquire ht->lock to see the updated ht->map */ - qemu_mutex_lock(&ht->lock); + qht_lock(ht); map = ht->map; qht_map_lock_buckets(map); - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); *pmap = map; return; } @@ -288,11 +315,11 @@ struct qht_bucket *qht_bucket_lock__no_stale(struct qht *ht, uint32_t hash, qemu_spin_unlock(&b->lock); /* we raced with a resize; acquire ht->lock to see the updated ht->map */ - qemu_mutex_lock(&ht->lock); + qht_lock(ht); map = ht->map; b = qht_map_to_bucket(map, hash); qemu_spin_lock(&b->lock); - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); *pmap = map; return b; } @@ -430,13 +457,13 @@ bool qht_reset_size(struct qht *ht, size_t n_elems) n_buckets = qht_elems_to_buckets(n_elems); - qemu_mutex_lock(&ht->lock); + qht_lock(ht); map = ht->map; if (n_buckets != map->n_buckets) { new = qht_map_create(n_buckets); } qht_do_resize_and_reset(ht, new); - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); return !!new; } @@ -565,7 +592,7 @@ static __attribute__((noinline)) void qht_grow_maybe(struct qht *ht) * If the lock is taken it probably means there's an ongoing resize, * so bail out. */ - if (qemu_mutex_trylock(&ht->lock)) { + if (qht_trylock(ht)) { return; } map = ht->map; @@ -575,7 +602,7 @@ static __attribute__((noinline)) void qht_grow_maybe(struct qht *ht) qht_do_resize(ht, new); } - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); } bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing) @@ -788,7 +815,7 @@ bool qht_resize(struct qht *ht, size_t n_elems) size_t n_buckets = qht_elems_to_buckets(n_elems); size_t ret = false; - qemu_mutex_lock(&ht->lock); + qht_lock(ht); if (n_buckets != ht->map->n_buckets) { struct qht_map *new; @@ -796,7 +823,7 @@ bool qht_resize(struct qht *ht, size_t n_elems) qht_do_resize(ht, new); ret = true; } - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); return ret; } diff --git a/util/qsp.c b/util/qsp.c new file mode 100644 index 0000000000..b0c2575d10 --- /dev/null +++ b/util/qsp.c @@ -0,0 +1,828 @@ +/* + * qsp.c - QEMU Synchronization Profiler + * + * Copyright (C) 2018, Emilio G. Cota <cota@braap.org> + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * QSP profiles the time spent in synchronization primitives, which can + * help diagnose performance problems, e.g. scalability issues when + * contention is high. + * + * The primitives currently supported are mutexes, recursive mutexes and + * condition variables. Note that not all related functions are intercepted; + * instead we profile only those functions that can have a performance impact, + * either due to blocking (e.g. cond_wait, mutex_lock) or cache line + * contention (e.g. mutex_lock, mutex_trylock). + * + * QSP's design focuses on speed and scalability. This is achieved + * by having threads do their profiling entirely on thread-local data. + * The appropriate thread-local data is found via a QHT, i.e. a concurrent hash + * table. To aggregate data in order to generate a report, we iterate over + * all entries in the hash table. Depending on the number of threads and + * synchronization objects this might be expensive, but note that it is + * very rarely called -- reports are generated only when requested by users. + * + * Reports are generated as a table where each row represents a call site. A + * call site is the triplet formed by the __file__ and __LINE__ of the caller + * as well as the address of the "object" (i.e. mutex, rec. mutex or condvar) + * being operated on. Optionally, call sites that operate on different objects + * of the same type can be coalesced, which can be particularly useful when + * profiling dynamically-allocated objects. + * + * Alternative designs considered: + * + * - Use an off-the-shelf profiler such as mutrace. This is not a viable option + * for us because QEMU has __malloc_hook set (by one of the libraries it + * uses); leaving this hook unset is required to avoid deadlock in mutrace. + * + * - Use a glib HT for each thread, protecting each HT with its own lock. + * This isn't simpler than the current design, and is 10% slower in the + * atomic_add-bench microbenchmark (-m option). + * + * - For reports, just use a binary tree as we aggregate data, instead of having + * an intermediate hash table. This would simplify the code only slightly, but + * would perform badly if there were many threads and objects to track. + * + * - Wrap operations on qsp entries with RCU read-side critical sections, so + * that qsp_reset() can delete entries. Unfortunately, the overhead of calling + * rcu_read_lock/unlock slows down atomic_add-bench -m by 24%. Having + * a snapshot that is updated on qsp_reset() avoids this overhead. + * + * Related Work: + * - Lennart Poettering's mutrace: http://0pointer.de/blog/projects/mutrace.html + * - Lozi, David, Thomas, Lawall and Muller. "Remote Core Locking: Migrating + * Critical-Section Execution to Improve the Performance of Multithreaded + * Applications", USENIX ATC'12. + */ +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/timer.h" +#include "qemu/qht.h" +#include "qemu/rcu.h" +#include "exec/tb-hash-xx.h" + +enum QSPType { + QSP_MUTEX, + QSP_BQL_MUTEX, + QSP_REC_MUTEX, + QSP_CONDVAR, +}; + +struct QSPCallSite { + const void *obj; + const char *file; /* i.e. __FILE__; shortened later */ + int line; + enum QSPType type; +}; +typedef struct QSPCallSite QSPCallSite; + +struct QSPEntry { + void *thread_ptr; + const QSPCallSite *callsite; + uint64_t n_acqs; + uint64_t ns; + unsigned int n_objs; /* count of coalesced objs; only used for reporting */ +#ifndef CONFIG_ATOMIC64 + /* + * If we cannot update the counts atomically, then use a seqlock. + * We don't need an associated lock because the updates are thread-local. + */ + QemuSeqLock sequence; +#endif +}; +typedef struct QSPEntry QSPEntry; + +struct QSPSnapshot { + struct rcu_head rcu; + struct qht ht; +}; +typedef struct QSPSnapshot QSPSnapshot; + +/* initial sizing for hash tables */ +#define QSP_INITIAL_SIZE 64 + +/* If this file is moved, QSP_REL_PATH should be updated accordingly */ +#define QSP_REL_PATH "util/qsp.c" + +/* this file's full path. Used to present all call sites with relative paths */ +static size_t qsp_qemu_path_len; + +/* the address of qsp_thread gives us a unique 'thread ID' */ +static __thread int qsp_thread; + +/* + * Call sites are the same for all threads, so we track them in a separate hash + * table to save memory. + */ +static struct qht qsp_callsite_ht; + +static struct qht qsp_ht; +static QSPSnapshot *qsp_snapshot; +static bool qsp_initialized, qsp_initializing; + +static const char * const qsp_typenames[] = { + [QSP_MUTEX] = "mutex", + [QSP_BQL_MUTEX] = "BQL mutex", + [QSP_REC_MUTEX] = "rec_mutex", + [QSP_CONDVAR] = "condvar", +}; + +QemuMutexLockFunc qemu_bql_mutex_lock_func = qemu_mutex_lock_impl; +QemuMutexLockFunc qemu_mutex_lock_func = qemu_mutex_lock_impl; +QemuMutexTrylockFunc qemu_mutex_trylock_func = qemu_mutex_trylock_impl; +QemuRecMutexLockFunc qemu_rec_mutex_lock_func = qemu_rec_mutex_lock_impl; +QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func = + qemu_rec_mutex_trylock_impl; +QemuCondWaitFunc qemu_cond_wait_func = qemu_cond_wait_impl; + +/* + * It pays off to _not_ hash callsite->file; hashing a string is slow, and + * without it we still get a pretty unique hash. + */ +static inline +uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t a) +{ + uint64_t b = (uint64_t)(uintptr_t)callsite->obj; + uint32_t e = callsite->line; + uint32_t f = callsite->type; + + return tb_hash_func7(a, b, e, f, 0); +} + +static inline +uint32_t qsp_callsite_hash(const QSPCallSite *callsite) +{ + return do_qsp_callsite_hash(callsite, 0); +} + +static inline uint32_t do_qsp_entry_hash(const QSPEntry *entry, uint64_t a) +{ + return do_qsp_callsite_hash(entry->callsite, a); +} + +static uint32_t qsp_entry_hash(const QSPEntry *entry) +{ + return do_qsp_entry_hash(entry, (uint64_t)(uintptr_t)entry->thread_ptr); +} + +static uint32_t qsp_entry_no_thread_hash(const QSPEntry *entry) +{ + return do_qsp_entry_hash(entry, 0); +} + +/* without the objects we need to hash the file name to get a decent hash */ +static uint32_t qsp_entry_no_thread_obj_hash(const QSPEntry *entry) +{ + const QSPCallSite *callsite = entry->callsite; + uint64_t a = g_str_hash(callsite->file); + uint64_t b = callsite->line; + uint32_t e = callsite->type; + + return tb_hash_func7(a, b, e, 0, 0); +} + +static bool qsp_callsite_cmp(const void *ap, const void *bp) +{ + const QSPCallSite *a = ap; + const QSPCallSite *b = bp; + + return a == b || + (a->obj == b->obj && + a->line == b->line && + a->type == b->type && + (a->file == b->file || !strcmp(a->file, b->file))); +} + +static bool qsp_callsite_no_obj_cmp(const void *ap, const void *bp) +{ + const QSPCallSite *a = ap; + const QSPCallSite *b = bp; + + return a == b || + (a->line == b->line && + a->type == b->type && + (a->file == b->file || !strcmp(a->file, b->file))); +} + +static bool qsp_entry_no_thread_cmp(const void *ap, const void *bp) +{ + const QSPEntry *a = ap; + const QSPEntry *b = bp; + + return qsp_callsite_cmp(a->callsite, b->callsite); +} + +static bool qsp_entry_no_thread_obj_cmp(const void *ap, const void *bp) +{ + const QSPEntry *a = ap; + const QSPEntry *b = bp; + + return qsp_callsite_no_obj_cmp(a->callsite, b->callsite); +} + +static bool qsp_entry_cmp(const void *ap, const void *bp) +{ + const QSPEntry *a = ap; + const QSPEntry *b = bp; + + return a->thread_ptr == b->thread_ptr && + qsp_callsite_cmp(a->callsite, b->callsite); +} + +/* + * Normally we'd call this from a constructor function, but we want it to work + * via libutil as well. + */ +static void qsp_do_init(void) +{ + /* make sure this file's path in the tree is up to date with QSP_REL_PATH */ + g_assert(strstr(__FILE__, QSP_REL_PATH)); + qsp_qemu_path_len = strlen(__FILE__) - strlen(QSP_REL_PATH); + + qht_init(&qsp_ht, qsp_entry_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); + qht_init(&qsp_callsite_ht, qsp_callsite_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); +} + +static __attribute__((noinline)) void qsp_init__slowpath(void) +{ + if (atomic_cmpxchg(&qsp_initializing, false, true) == false) { + qsp_do_init(); + atomic_set(&qsp_initialized, true); + } else { + while (!atomic_read(&qsp_initialized)) { + cpu_relax(); + } + } +} + +/* qsp_init() must be called from _all_ exported functions */ +static inline void qsp_init(void) +{ + if (likely(atomic_read(&qsp_initialized))) { + return; + } + qsp_init__slowpath(); +} + +static QSPCallSite *qsp_callsite_find(const QSPCallSite *orig) +{ + QSPCallSite *callsite; + uint32_t hash; + + hash = qsp_callsite_hash(orig); + callsite = qht_lookup(&qsp_callsite_ht, orig, hash); + if (callsite == NULL) { + void *existing = NULL; + + callsite = g_new(QSPCallSite, 1); + memcpy(callsite, orig, sizeof(*callsite)); + qht_insert(&qsp_callsite_ht, callsite, hash, &existing); + if (unlikely(existing)) { + g_free(callsite); + callsite = existing; + } + } + return callsite; +} + +static QSPEntry * +qsp_entry_create(struct qht *ht, const QSPEntry *entry, uint32_t hash) +{ + QSPEntry *e; + void *existing = NULL; + + e = g_new0(QSPEntry, 1); + e->thread_ptr = entry->thread_ptr; + e->callsite = qsp_callsite_find(entry->callsite); + + qht_insert(ht, e, hash, &existing); + if (unlikely(existing)) { + g_free(e); + e = existing; + } + return e; +} + +static QSPEntry * +qsp_entry_find(struct qht *ht, const QSPEntry *entry, uint32_t hash) +{ + QSPEntry *e; + + e = qht_lookup(ht, entry, hash); + if (e == NULL) { + e = qsp_entry_create(ht, entry, hash); + } + return e; +} + +/* + * Note: Entries are never removed, so callers do not have to be in an RCU + * read-side critical section. + */ +static QSPEntry *qsp_entry_get(const void *obj, const char *file, int line, + enum QSPType type) +{ + QSPCallSite callsite = { + .obj = obj, + .file = file, + .line = line, + .type = type, + }; + QSPEntry orig; + uint32_t hash; + + qsp_init(); + + orig.thread_ptr = &qsp_thread; + orig.callsite = &callsite; + + hash = qsp_entry_hash(&orig); + return qsp_entry_find(&qsp_ht, &orig, hash); +} + +/* + * @from is in the global hash table; read it atomically if the host + * supports it, otherwise use the seqlock. + */ +static void qsp_entry_aggregate(QSPEntry *to, const QSPEntry *from) +{ +#ifdef CONFIG_ATOMIC64 + to->ns += atomic_read__nocheck(&from->ns); + to->n_acqs += atomic_read__nocheck(&from->n_acqs); +#else + unsigned int version; + uint64_t ns, n_acqs; + + do { + version = seqlock_read_begin(&from->sequence); + ns = atomic_read__nocheck(&from->ns); + n_acqs = atomic_read__nocheck(&from->n_acqs); + } while (seqlock_read_retry(&from->sequence, version)); + + to->ns += ns; + to->n_acqs += n_acqs; +#endif +} + +/* + * @e is in the global hash table; it is only written to by the current thread, + * so we write to it atomically (as in "write once") to prevent torn reads. + * If the host doesn't support u64 atomics, use the seqlock. + */ +static inline void do_qsp_entry_record(QSPEntry *e, int64_t delta, bool acq) +{ +#ifndef CONFIG_ATOMIC64 + seqlock_write_begin(&e->sequence); +#endif + atomic_set__nocheck(&e->ns, e->ns + delta); + if (acq) { + atomic_set__nocheck(&e->n_acqs, e->n_acqs + 1); + } +#ifndef CONFIG_ATOMIC64 + seqlock_write_end(&e->sequence); +#endif +} + +static inline void qsp_entry_record(QSPEntry *e, int64_t delta) +{ + do_qsp_entry_record(e, delta, true); +} + +#define QSP_GEN_VOID(type_, qsp_t_, func_, impl_) \ + static void func_(type_ *obj, const char *file, int line) \ + { \ + QSPEntry *e; \ + int64_t t0, t1; \ + \ + t0 = get_clock(); \ + impl_(obj, file, line); \ + t1 = get_clock(); \ + \ + e = qsp_entry_get(obj, file, line, qsp_t_); \ + qsp_entry_record(e, t1 - t0); \ + } + +#define QSP_GEN_RET1(type_, qsp_t_, func_, impl_) \ + static int func_(type_ *obj, const char *file, int line) \ + { \ + QSPEntry *e; \ + int64_t t0, t1; \ + int err; \ + \ + t0 = get_clock(); \ + err = impl_(obj, file, line); \ + t1 = get_clock(); \ + \ + e = qsp_entry_get(obj, file, line, qsp_t_); \ + do_qsp_entry_record(e, t1 - t0, !err); \ + return err; \ + } + +QSP_GEN_VOID(QemuMutex, QSP_BQL_MUTEX, qsp_bql_mutex_lock, qemu_mutex_lock_impl) +QSP_GEN_VOID(QemuMutex, QSP_MUTEX, qsp_mutex_lock, qemu_mutex_lock_impl) +QSP_GEN_RET1(QemuMutex, QSP_MUTEX, qsp_mutex_trylock, qemu_mutex_trylock_impl) + +QSP_GEN_VOID(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_lock, + qemu_rec_mutex_lock_impl) +QSP_GEN_RET1(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_trylock, + qemu_rec_mutex_trylock_impl) + +#undef QSP_GEN_RET1 +#undef QSP_GEN_VOID + +static void +qsp_cond_wait(QemuCond *cond, QemuMutex *mutex, const char *file, int line) +{ + QSPEntry *e; + int64_t t0, t1; + + t0 = get_clock(); + qemu_cond_wait_impl(cond, mutex, file, line); + t1 = get_clock(); + + e = qsp_entry_get(cond, file, line, QSP_CONDVAR); + qsp_entry_record(e, t1 - t0); +} + +bool qsp_is_enabled(void) +{ + return atomic_read(&qemu_mutex_lock_func) == qsp_mutex_lock; +} + +void qsp_enable(void) +{ + atomic_set(&qemu_mutex_lock_func, qsp_mutex_lock); + atomic_set(&qemu_mutex_trylock_func, qsp_mutex_trylock); + atomic_set(&qemu_bql_mutex_lock_func, qsp_bql_mutex_lock); + atomic_set(&qemu_rec_mutex_lock_func, qsp_rec_mutex_lock); + atomic_set(&qemu_rec_mutex_trylock_func, qsp_rec_mutex_trylock); + atomic_set(&qemu_cond_wait_func, qsp_cond_wait); +} + +void qsp_disable(void) +{ + atomic_set(&qemu_mutex_lock_func, qemu_mutex_lock_impl); + atomic_set(&qemu_mutex_trylock_func, qemu_mutex_trylock_impl); + atomic_set(&qemu_bql_mutex_lock_func, qemu_mutex_lock_impl); + atomic_set(&qemu_rec_mutex_lock_func, qemu_rec_mutex_lock_impl); + atomic_set(&qemu_rec_mutex_trylock_func, qemu_rec_mutex_trylock_impl); + atomic_set(&qemu_cond_wait_func, qemu_cond_wait_impl); +} + +static gint qsp_tree_cmp(gconstpointer ap, gconstpointer bp, gpointer up) +{ + const QSPEntry *a = ap; + const QSPEntry *b = bp; + enum QSPSortBy sort_by = *(enum QSPSortBy *)up; + const QSPCallSite *ca; + const QSPCallSite *cb; + + switch (sort_by) { + case QSP_SORT_BY_TOTAL_WAIT_TIME: + if (a->ns > b->ns) { + return -1; + } else if (a->ns < b->ns) { + return 1; + } + break; + case QSP_SORT_BY_AVG_WAIT_TIME: + { + double avg_a = a->n_acqs ? a->ns / a->n_acqs : 0; + double avg_b = b->n_acqs ? b->ns / b->n_acqs : 0; + + if (avg_a > avg_b) { + return -1; + } else if (avg_a < avg_b) { + return 1; + } + break; + } + default: + g_assert_not_reached(); + } + + ca = a->callsite; + cb = b->callsite; + /* Break the tie with the object's address */ + if (ca->obj < cb->obj) { + return -1; + } else if (ca->obj > cb->obj) { + return 1; + } else { + int cmp; + + /* same obj. Break the tie with the callsite's file */ + cmp = strcmp(ca->file, cb->file); + if (cmp) { + return cmp; + } + /* same callsite file. Break the tie with the callsite's line */ + g_assert(ca->line != cb->line); + if (ca->line < cb->line) { + return -1; + } else if (ca->line > cb->line) { + return 1; + } else { + /* break the tie with the callsite's type */ + return cb->type - ca->type; + } + } +} + +static void qsp_sort(struct qht *ht, void *p, uint32_t h, void *userp) +{ + QSPEntry *e = p; + GTree *tree = userp; + + g_tree_insert(tree, e, NULL); +} + +static void qsp_aggregate(struct qht *global_ht, void *p, uint32_t h, void *up) +{ + struct qht *ht = up; + const QSPEntry *e = p; + QSPEntry *agg; + uint32_t hash; + + hash = qsp_entry_no_thread_hash(e); + agg = qsp_entry_find(ht, e, hash); + qsp_entry_aggregate(agg, e); +} + +static void qsp_iter_diff(struct qht *orig, void *p, uint32_t hash, void *htp) +{ + struct qht *ht = htp; + QSPEntry *old = p; + QSPEntry *new; + + new = qht_lookup(ht, old, hash); + /* entries are never deleted, so we must have this one */ + g_assert(new != NULL); + /* our reading of the stats happened after the snapshot was taken */ + g_assert(new->n_acqs >= old->n_acqs); + g_assert(new->ns >= old->ns); + + new->n_acqs -= old->n_acqs; + new->ns -= old->ns; + + /* No point in reporting an empty entry */ + if (new->n_acqs == 0 && new->ns == 0) { + bool removed = qht_remove(ht, new, hash); + + g_assert(removed); + g_free(new); + } +} + +static void qsp_diff(struct qht *orig, struct qht *new) +{ + qht_iter(orig, qsp_iter_diff, new); +} + +static void +qsp_iter_callsite_coalesce(struct qht *orig, void *p, uint32_t h, void *htp) +{ + struct qht *ht = htp; + QSPEntry *old = p; + QSPEntry *e; + uint32_t hash; + + hash = qsp_entry_no_thread_obj_hash(old); + e = qht_lookup(ht, old, hash); + if (e == NULL) { + e = qsp_entry_create(ht, old, hash); + e->n_objs = 1; + } else if (e->callsite->obj != old->callsite->obj) { + e->n_objs++; + } + e->ns += old->ns; + e->n_acqs += old->n_acqs; +} + +static void qsp_ht_delete(struct qht *ht, void *p, uint32_t h, void *htp) +{ + g_free(p); +} + +static void qsp_mktree(GTree *tree, bool callsite_coalesce) +{ + QSPSnapshot *snap; + struct qht ht, coalesce_ht; + struct qht *htp; + + /* + * First, see if there's a prior snapshot, so that we read the global hash + * table _after_ the snapshot has been created, which guarantees that + * the entries we'll read will be a superset of the snapshot's entries. + * + * We must remain in an RCU read-side critical section until we're done + * with the snapshot. + */ + rcu_read_lock(); + snap = atomic_rcu_read(&qsp_snapshot); + + /* Aggregate all results from the global hash table into a local one */ + qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); + qht_iter(&qsp_ht, qsp_aggregate, &ht); + + /* compute the difference wrt the snapshot, if any */ + if (snap) { + qsp_diff(&snap->ht, &ht); + } + /* done with the snapshot; RCU can reclaim it */ + rcu_read_unlock(); + + htp = &ht; + if (callsite_coalesce) { + qht_init(&coalesce_ht, qsp_entry_no_thread_obj_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); + qht_iter(&ht, qsp_iter_callsite_coalesce, &coalesce_ht); + + /* free the previous hash table, and point htp to coalesce_ht */ + qht_iter(&ht, qsp_ht_delete, NULL); + qht_destroy(&ht); + htp = &coalesce_ht; + } + + /* sort the hash table elements by using a tree */ + qht_iter(htp, qsp_sort, tree); + + /* free the hash table, but keep the elements (those are in the tree now) */ + qht_destroy(htp); +} + +/* free string with g_free */ +static char *qsp_at(const QSPCallSite *callsite) +{ + GString *s = g_string_new(NULL); + const char *shortened; + + /* remove the absolute path to qemu */ + if (unlikely(strlen(callsite->file) < qsp_qemu_path_len)) { + shortened = callsite->file; + } else { + shortened = callsite->file + qsp_qemu_path_len; + } + g_string_append_printf(s, "%s:%u", shortened, callsite->line); + return g_string_free(s, FALSE); +} + +struct QSPReportEntry { + const void *obj; + char *callsite_at; + const char *typename; + double time_s; + double ns_avg; + uint64_t n_acqs; + unsigned int n_objs; +}; +typedef struct QSPReportEntry QSPReportEntry; + +struct QSPReport { + QSPReportEntry *entries; + size_t n_entries; + size_t max_n_entries; +}; +typedef struct QSPReport QSPReport; + +static gboolean qsp_tree_report(gpointer key, gpointer value, gpointer udata) +{ + const QSPEntry *e = key; + QSPReport *report = udata; + QSPReportEntry *entry; + + if (report->n_entries == report->max_n_entries) { + return TRUE; + } + entry = &report->entries[report->n_entries]; + report->n_entries++; + + entry->obj = e->callsite->obj; + entry->n_objs = e->n_objs; + entry->callsite_at = qsp_at(e->callsite); + entry->typename = qsp_typenames[e->callsite->type]; + entry->time_s = e->ns * 1e-9; + entry->n_acqs = e->n_acqs; + entry->ns_avg = e->n_acqs ? e->ns / e->n_acqs : 0; + return FALSE; +} + +static void +pr_report(const QSPReport *rep, FILE *f, fprintf_function pr) +{ + char *dashes; + size_t max_len = 0; + int callsite_len = 0; + int callsite_rspace; + int n_dashes; + size_t i; + + /* find out the maximum length of all 'callsite' fields */ + for (i = 0; i < rep->n_entries; i++) { + const QSPReportEntry *e = &rep->entries[i]; + size_t len = strlen(e->callsite_at); + + if (len > max_len) { + max_len = len; + } + } + + callsite_len = MAX(max_len, strlen("Call site")); + /* white space to leave to the right of "Call site" */ + callsite_rspace = callsite_len - strlen("Call site"); + + pr(f, "Type Object Call site%*s Wait Time (s) " + " Count Average (us)\n", callsite_rspace, ""); + + /* build a horizontal rule with dashes */ + n_dashes = 79 + callsite_rspace; + dashes = g_malloc(n_dashes + 1); + memset(dashes, '-', n_dashes); + dashes[n_dashes] = '\0'; + pr(f, "%s\n", dashes); + + for (i = 0; i < rep->n_entries; i++) { + const QSPReportEntry *e = &rep->entries[i]; + GString *s = g_string_new(NULL); + + g_string_append_printf(s, "%-9s ", e->typename); + if (e->n_objs > 1) { + g_string_append_printf(s, "[%12u]", e->n_objs); + } else { + g_string_append_printf(s, "%14p", e->obj); + } + g_string_append_printf(s, " %s%*s %13.5f %12" PRIu64 " %12.2f\n", + e->callsite_at, + callsite_len - (int)strlen(e->callsite_at), "", + e->time_s, e->n_acqs, e->ns_avg * 1e-3); + pr(f, "%s", s->str); + g_string_free(s, TRUE); + } + + pr(f, "%s\n", dashes); + g_free(dashes); +} + +static void report_destroy(QSPReport *rep) +{ + size_t i; + + for (i = 0; i < rep->n_entries; i++) { + QSPReportEntry *e = &rep->entries[i]; + + g_free(e->callsite_at); + } + g_free(rep->entries); +} + +void qsp_report(FILE *f, fprintf_function cpu_fprintf, size_t max, + enum QSPSortBy sort_by, bool callsite_coalesce) +{ + GTree *tree = g_tree_new_full(qsp_tree_cmp, &sort_by, g_free, NULL); + QSPReport rep; + + qsp_init(); + + rep.entries = g_new0(QSPReportEntry, max); + rep.n_entries = 0; + rep.max_n_entries = max; + + qsp_mktree(tree, callsite_coalesce); + g_tree_foreach(tree, qsp_tree_report, &rep); + g_tree_destroy(tree); + + pr_report(&rep, f, cpu_fprintf); + report_destroy(&rep); +} + +static void qsp_snapshot_destroy(QSPSnapshot *snap) +{ + qht_iter(&snap->ht, qsp_ht_delete, NULL); + qht_destroy(&snap->ht); + g_free(snap); +} + +void qsp_reset(void) +{ + QSPSnapshot *new = g_new(QSPSnapshot, 1); + QSPSnapshot *old; + + qsp_init(); + + qht_init(&new->ht, qsp_entry_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); + + /* take a snapshot of the current state */ + qht_iter(&qsp_ht, qsp_aggregate, &new->ht); + + /* replace the previous snapshot, if any */ + old = atomic_xchg(&qsp_snapshot, new); + if (old) { + call_rcu(old, qsp_snapshot_destroy, rcu); + } +} diff --git a/util/unicode.c b/util/unicode.c index a812a35171..8580bc598b 100644 --- a/util/unicode.c +++ b/util/unicode.c @@ -13,6 +13,21 @@ #include "qemu/osdep.h" #include "qemu/unicode.h" +static bool is_valid_codepoint(int codepoint) +{ + if (codepoint > 0x10FFFFu) { + return false; /* beyond Unicode range */ + } + if ((codepoint >= 0xFDD0 && codepoint <= 0xFDEF) + || (codepoint & 0xFFFE) == 0xFFFE) { + return false; /* noncharacter */ + } + if (codepoint >= 0xD800 && codepoint <= 0xDFFF) { + return false; /* surrogate code point */ + } + return true; +} + /** * mod_utf8_codepoint: * @s: string encoded in modified UTF-8 @@ -83,13 +98,8 @@ int mod_utf8_codepoint(const char *s, size_t n, char **end) cp <<= 6; cp |= byte & 0x3F; } - if (cp > 0x10FFFF) { - cp = -1; /* beyond Unicode range */ - } else if ((cp >= 0xFDD0 && cp <= 0xFDEF) - || (cp & 0xFFFE) == 0xFFFE) { - cp = -1; /* noncharacter */ - } else if (cp >= 0xD800 && cp <= 0xDFFF) { - cp = -1; /* surrogate code point */ + if (!is_valid_codepoint(cp)) { + cp = -1; } else if (cp < min_cp[len - 2] && !(cp == 0 && len == 2)) { cp = -1; /* overlong, not \xC0\x80 */ } @@ -99,3 +109,48 @@ out: *end = (char *)p; return cp; } + +/** + * mod_utf8_encode: + * @buf: Destination buffer + * @bufsz: size of @buf, at least 5. + * @codepoint: Unicode codepoint to encode + * + * Convert Unicode codepoint @codepoint to modified UTF-8. + * + * Returns: the length of the UTF-8 sequence on success, -1 when + * @codepoint is invalid. + */ +ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint) +{ + assert(bufsz >= 5); + + if (!is_valid_codepoint(codepoint)) { + return -1; + } + + if (codepoint > 0 && codepoint <= 0x7F) { + buf[0] = codepoint & 0x7F; + buf[1] = 0; + return 1; + } + if (codepoint <= 0x7FF) { + buf[0] = 0xC0 | ((codepoint >> 6) & 0x1F); + buf[1] = 0x80 | (codepoint & 0x3F); + buf[2] = 0; + return 2; + } + if (codepoint <= 0xFFFF) { + buf[0] = 0xE0 | ((codepoint >> 12) & 0x0F); + buf[1] = 0x80 | ((codepoint >> 6) & 0x3F); + buf[2] = 0x80 | (codepoint & 0x3F); + buf[3] = 0; + return 3; + } + buf[0] = 0xF0 | ((codepoint >> 18) & 0x07); + buf[1] = 0x80 | ((codepoint >> 12) & 0x3F); + buf[2] = 0x80 | ((codepoint >> 6) & 0x3F); + buf[3] = 0x80 | (codepoint & 0x3F); + buf[4] = 0; + return 4; +} @@ -2987,6 +2987,7 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_object_opts); qemu_add_opts(&qemu_tpmdev_opts); qemu_add_opts(&qemu_realtime_opts); + qemu_add_opts(&qemu_overcommit_opts); qemu_add_opts(&qemu_msg_opts); qemu_add_opts(&qemu_name_opts); qemu_add_opts(&qemu_numa_opts); @@ -3959,6 +3960,9 @@ int main(int argc, char **argv, char **envp) exit(1); } break; + case QEMU_OPTION_enable_sync_profile: + qsp_enable(); + break; case QEMU_OPTION_nodefconfig: case QEMU_OPTION_nouserconfig: /* Nothing to be parsed here. Especially, do not error out below. */ @@ -4559,11 +4563,10 @@ int main(int argc, char **argv, char **envp) * (2) CONFIG_SLIRP not set, in which case the implicit "-net nic" * sets up a nic that isn't connected to anything. */ - if (!default_net) { + if (!default_net && (!qtest_enabled() || has_defaults)) { net_check_clients(); } - if (boot_once) { qemu_boot_set(boot_once, &error_fatal); qemu_register_reset(restore_boot_order, g_strdup(boot_order)); |