diff options
160 files changed, 12672 insertions, 11611 deletions
diff --git a/Makefile.android b/Makefile.android index efc6fc3..4139226 100644 --- a/Makefile.android +++ b/Makefile.android @@ -148,15 +148,18 @@ start-emulator-library = \ $(eval LOCAL_AR := $(MY_AR)) \ $(eval LOCAL_LDLIBS := $(MY_LDLIBS)) \ $(eval LOCAL_MODULE_TAGS := debug) \ - $(eval LOCAL_MODULE := $1) + $(eval LOCAL_MODULE := $1) \ # Used with start-emulator-library end-emulator-library = \ - $(eval include $(BUILD_HOST_STATIC_LIBRARY)) + $(eval include $(BUILD_HOST_STATIC_LIBRARY)) \ + $(eval EMULATOR_MODULE_TYPE := STATIC_LIBRARY) # A variant of start-emulator-library to start the definition of a host # program instead. Use with end-emulator-program -start-emulator-program = $(call start-emulator-library,$1) +start-emulator-program = \ + $(call start-emulator-library,$1) \ + $(eval EMULATOR_MODULE_TYPE := EXECUTABLES) # A varient of end-emulator-library for host programs instead end-emulator-program = \ diff --git a/Makefile.common b/Makefile.common index 15d6801..222a1ae 100644 --- a/Makefile.common +++ b/Makefile.common @@ -348,14 +348,14 @@ CORE_MISC_SOURCES = \ cutils.c \ d3des.c \ input.c \ + iohandler.c \ ioport.c \ module.c \ net-android.c \ notify.c \ osdep.c \ - outputchannel.c \ path.c \ - qemu-char-android.c \ + qemu-char.c \ qemu-config.c \ qemu-error.c \ qemu-malloc.c \ @@ -592,9 +592,10 @@ $(call end-emulator-library) ### ### This defines a function that can be used inside a module definition ### -### $(call gen-hx-header,<input>,<source-files>) +### $(call gen-hx-header,<input>,<output>,<source-files>) ### ### Where: <input> is the input file, with a .hx suffix (e.g. foo.hx) +### <output> is the output file, with a .h or .def suffix ### <source-files> is a list of source files that include the header ### @@ -602,16 +603,17 @@ $(call end-emulator-library) gen-hx-header = $(eval $(call gen-hx-header-ev,$1,$2,$3)) define gen-hx-header-ev -intermediates := $$(call intermediates-dir-for,EXECUTABLES,$$(LOCAL_MODULE),true) +intermediates := $$(call intermediates-dir-for,$$(EMULATOR_MODULE_TYPE),$$(LOCAL_MODULE),true) -QEMU_HEADER_H := $$(intermediates)/$$(1:%.hx=%.h) +QEMU_HEADER_H := $$(intermediates)/$$2 $$(QEMU_HEADER_H): PRIVATE_PATH := $$(LOCAL_PATH) $$(QEMU_HEADER_H): PRIVATE_CUSTOM_TOOL = $$(PRIVATE_PATH)/hxtool -h < $$< > $$@ $$(QEMU_HEADER_H): $$(LOCAL_PATH)/$$1 $$(LOCAL_PATH)/hxtool $$(transform-generated-source) LOCAL_GENERATED_SOURCES += $$(QEMU_HEADER_H) -_objects := $$(patsubst %,$$(intermediates)/%,$$(2:.c=.o)) +LOCAL_C_INCLUDES += $$(intermediates) +_objects := $$(patsubst %,$$(intermediates)/%,$$(3:.c=.o)) $$(_objects): $$(QEMU_HEADER_H) endef diff --git a/Makefile.target b/Makefile.target index fb32582..5f7a8b2 100644 --- a/Makefile.target +++ b/Makefile.target @@ -29,7 +29,10 @@ EMULATOR_TARGET_CFLAGS := \ -DNEED_CPU_H \ TCG_TARGET := $(HOST_ARCH) -ifeq ($(TCG_TARGET),x86) +ifeq ($(HOST_ARCH),x86) + TCG_TARGET := i386 +endif +ifeq ($(HOST_ARCH),x86_64) TCG_TARGET := i386 endif @@ -132,12 +135,11 @@ endif LOCAL_SRC_FILES += $(HW_SOURCES:%=hw/%) LOCAL_SRC_FILES += \ - exec.c \ cpu-exec.c \ + exec.c \ translate-all.c \ trace.c \ varint.c \ - dcache.c \ softmmu_outside_jit.c \ ############################################################################## @@ -155,6 +157,7 @@ LOCAL_SRC_FILES += \ target-arm/iwmmxt_helper.c \ target-arm/neon_helper.c \ target-arm/helper.c \ + target-arm/helper-android.c \ target-arm/translate.c \ target-arm/machine.c \ hw/armv7m.c \ @@ -206,6 +209,18 @@ MCHK_SOURCES := \ LOCAL_SRC_FILES += $(MCHK_SOURCES:%=memcheck/%) +LOCAL_SRC_FILES += \ + cpus.c \ + arch_init.c + +# What a mess, os-posix.c depends on the exact values of options +# which are target specific. +ifeq ($(HOST_OS),windows) + LOCAL_SRC_FILES += os-win32.c oslib-win32.c +else + LOCAL_SRC_FILES += os-posix.c oslib-posix.c +endif +$(call gen-hx-header,qemu-options.hx,qemu-options.def,os-posix.c os-win32.c) $(call end-emulator-library) @@ -248,6 +263,7 @@ LOCAL_SRC_FILES := \ loader.c \ monitor.c \ qemu-timer.c \ + qemu-timer-common.c \ user-events-qemu.c \ vl-android.c \ android/console.c \ @@ -259,8 +275,8 @@ LOCAL_SRC_FILES := \ android/protocol/core-commands-impl.c \ android/protocol/core-commands-qemu.c \ -$(call gen-hx-header,qemu-monitor.hx,monitor.c) -$(call gen-hx-header,qemu-options.hx,vl-android.c) +$(call gen-hx-header,qemu-monitor.hx,qemu-monitor.h,monitor.c) +$(call gen-hx-header,qemu-options.hx,qemu-options.def,vl-android.c qemu-options.h) ifeq ($(HOST_OS),darwin) FRAMEWORKS := OpenGL Cocoa QuickTime ApplicationServices Carbon IOKit @@ -324,6 +340,7 @@ LOCAL_SRC_FILES := \ loader.c \ monitor.c \ qemu-timer.c \ + qemu-timer-common.c \ user-events-qemu.c \ vl-android.c \ android/cmdline-option.c \ @@ -337,8 +354,8 @@ LOCAL_SRC_FILES := \ android/protocol/ui-commands-qemu.c \ android/ -$(call gen-hx-header,qemu-monitor.hx,monitor.c) -$(call gen-hx-header,qemu-options.hx,vl-android.c) +$(call gen-hx-header,qemu-monitor.hx,qemu-monitor.h,monitor.c) +$(call gen-hx-header,qemu-options.hx,qemu-options.def,vl-android.c qemu-options.h) # The following files cannot be in static libraries because they contain # constructor functions that are otherwise stripped by the final linker @@ -1,23 +1,3 @@ -This package contains the sources to the Android emulator program. +Read the documentation in qemu-doc.html. -This program emulates a virtual ARM board that can be used to run Android -system images on a typical developer machine. To do so, you'll need additionnal -files provided with the public Android Software Development Kit (SDK). - -To download them, go to http://code.google.com/android/ - -Emulator-specific documentation is available at the following page: - - http://code.google.com/android/reference/emulator.html - -Please read the INSTALL file to see how you can rebuild the emulator, or -build a source distribution package tarball. - -Read the CHANGES.TXT file to see what important changes were added since -the last release. - -Note: This program is distributed under the terms of the GNU General Public - License, which exact licensing conditions are available in the COPYING - file found within this package. - -- Android Emulator Team +Fabrice Bellard. @@ -24,7 +24,6 @@ #include "qemu-common.h" -#include "sysemu.h" #include "acl.h" #ifdef CONFIG_FNMATCH diff --git a/android-configure.sh b/android-configure.sh index 5a1748c..4939323 100755 --- a/android-configure.sh +++ b/android-configure.sh @@ -482,6 +482,12 @@ case "$TARGET_OS" in ;; esac +case "$TARGET_OS" in + linux-*|darwin-*) + echo "#define CONFIG_MADVISE 1" >> $config_h + ;; +esac + # the -nand-limits options can only work on non-windows systems if [ "$TARGET_OS" != "windows" ] ; then echo "#define CONFIG_NAND_LIMITS 1" >> $config_h diff --git a/android-trace.h b/android-trace.h new file mode 100644 index 0000000..2d0100d --- /dev/null +++ b/android-trace.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2006-2007 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +*/ + +#ifndef TRACE_H +#define TRACE_H + +#include <inttypes.h> +#include "android-trace_common.h" + +extern uint64_t start_time, end_time; +extern uint64_t elapsed_usecs; +extern uint64_t Now(); + +struct TranslationBlock; + +// The simulated time, in clock ticks, starting with one. +extern uint64_t sim_time; +extern uint64_t trace_static_bb_num(void);; + +// This variable == 1 if we are currently tracing, otherwise == 0. +extern int tracing; +extern int trace_all_addr; +extern int trace_cache_miss; + +extern void start_tracing(); +extern void stop_tracing(); +extern void trace_init(const char *filename); +extern void trace_bb_start(uint32_t bb_addr); +extern void trace_add_insn(uint32_t insn, int is_thumb); +extern void trace_bb_end(); + +extern int get_insn_ticks_arm(uint32_t insn); +extern int get_insn_ticks_thumb(uint32_t insn); + +extern void trace_exception(uint32_t pc); +extern void trace_bb_helper(uint64_t bb_num, struct TranslationBlock *tb); +extern void trace_insn_helper(); +extern void sim_dcache_load(uint32_t addr); +extern void sim_dcache_store(uint32_t addr, uint32_t val); +extern void sim_dcache_swp(uint32_t addr); +extern void trace_interpreted_method(uint32_t addr, int call_type); + +extern const char *trace_filename; +extern int tracing; +extern int trace_cache_miss; +extern int trace_all_addr; + +// Trace process/thread operations +extern void trace_switch(int pid); +extern void trace_fork(int tgid, int pid); +extern void trace_clone(int tgid, int pid); +extern void trace_exit(int exitcode); +extern void trace_name(char *name); + +#endif /* TRACE_H */ diff --git a/android-trace_common.h b/android-trace_common.h new file mode 100644 index 0000000..fe84c1a --- /dev/null +++ b/android-trace_common.h @@ -0,0 +1,142 @@ +/* Copyright (C) 2006-2007 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +*/ + +#ifndef TRACE_COMMON_H +#define TRACE_COMMON_H + +#include <inttypes.h> + +// This should be the same as OPC_BUF_SIZE +#define kMaxInsnPerBB 512 + +#define kMaxNumBasicBlocks 1024 + +#define kMaxNumAddrs 1024 + +#define kInsnBufferSize 1024 + +#define kCompressedSize 8192 + +#define kMethodEnter 0 +#define kMethodExit 1 +#define kMethodException 2 +#define kNativeEnter 4 +#define kNativeExit 5 +#define kNativeException 6 + +// The trace identifier string must be less than 16 characters. +#define TRACE_IDENT "qemu_trace_file" +#define TRACE_VERSION 2 + +typedef struct TraceHeader { + char ident[16]; + int version; + uint32_t start_sec; + uint32_t start_usec; + uint32_t pdate; + uint32_t ptime; + uint32_t num_used_pids; // number of distinct process ids used + int first_unused_pid; // -1 if all 32,768 pids are used (unlikely) + uint8_t padding[4]; // next field is 8-byte aligned + uint64_t num_static_bb; + uint64_t num_static_insn; + uint64_t num_dynamic_bb; + uint64_t num_dynamic_insn; + uint64_t elapsed_usecs; +} TraceHeader; + +typedef struct BBRec { + uint64_t start_time; // time of first occurrence + uint64_t bb_num; // basic block number + uint32_t repeat; // repeat count (= 0 if just one occurrence) + uint64_t time_diff; // diff from previous time (if repeat > 0) +} BBRec; + +// Define a trace record for addresses that miss in the cache +typedef struct AddrRec { + uint64_t time; + uint32_t addr; +} AddrRec; + +// Define a trace record for the start time of each instruction +typedef struct InsnRec { + uint64_t time_diff; // time difference from last instruction + uint32_t repeat; // repeat count +} InsnRec; + +// Define record types for process id changes. +#define kPidEndOfFile 0 +#define kPidFork 1 +#define kPidClone 2 +#define kPidSwitch 3 +#define kPidExec 4 +#define kPidMmap 5 +#define kPidExit 6 +#define kPidKthreadName 7 +#define kPidSymbolAdd 8 +#define kPidSymbolRemove 9 +#define kPidMunmap 10 +#define kPidNoAction 11 +#define kPidName 12 + +#define bswap16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff)) + +#define bswap32(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) \ + | (((x) >> 8) & 0xff00) | (((x) >> 24) & 0xff)) + +#define bswap64(x) (((x) << 56) | (((x) & 0xff00) << 40) \ + | (((x) & 0xff0000) << 24) | (((x) & 0xff000000ull) << 8) \ + | (((x) >> 8) & 0xff000000ull) | (((x) >> 24) & 0xff0000) \ + | (((x) >> 40) & 0xff00) | ((x) >> 56)) + +#if BYTE_ORDER == LITTLE_ENDIAN +#define hostToLE16(x) (x) +#define hostToLE32(x) (x) +#define hostToLE64(x) (x) +#define LE16ToHost(x) (x) +#define LE32ToHost(x) (x) +#define LE64ToHost(x) (x) +#define convert16(x) +#define convert32(x) +#define convert64(x) +#else +#define hostToLE16(x) bswap16(x) +#define hostToLE32(x) bswap32(x) +#define hostToLE64(x) bswap64(x) +#define LE16ToHost(x) bswap16(x) +#define LE32ToHost(x) bswap32(x) +#define LE64ToHost(x) bswap64(x) +#define convert16(x) (x = bswap16(x)) +#define convert32(x) (x = bswap32(x)) +#define convert64(x) (x = bswap64(x)) +#endif + +/* XXX: we wrap 16-bit thumb instructions into 32-bit undefined ARM instructions + * for simplicity reasons. See section 3.13.1 section of the ARM ARM for details + * on the undefined instruction space we're using + */ +static __inline__ int insn_is_thumb(uint32_t insn) +{ + return ((insn & 0xfff000f0) == 0xf7f000f0); +} + +static __inline__ uint32_t insn_wrap_thumb(uint32_t insn) +{ + return 0xf7f000f0 | ((insn & 0xfff0) << 4) | (insn & 0x000f); +} + +static __inline__ uint32_t insn_unwrap_thumb(uint32_t insn) +{ + return ((insn >> 4) & 0xfff0) | (insn & 0x000f); +} + +#endif /* TRACE_COMMON_H */ diff --git a/android/config/darwin-x86/config-host.h b/android/config/darwin-x86/config-host.h index 93efda6..0395918 100644 --- a/android/config/darwin-x86/config-host.h +++ b/android/config/darwin-x86/config-host.h @@ -16,3 +16,5 @@ #define O_LARGEFILE 0 #define MAP_ANONYMOUS MAP_ANON #define CONFIG_ANDROID 1 +#define CONFIG_POSIX 1 +#define CONFIG_MADVISE 1 diff --git a/android/config/freebsd-x86/config-host.h b/android/config/freebsd-x86/config-host.h index 5518924..2e44a83 100644 --- a/android/config/freebsd-x86/config-host.h +++ b/android/config/freebsd-x86/config-host.h @@ -14,3 +14,5 @@ #define CONFIG_UNAME_RELEASE "" #define CONFIG_SKINS 1 #define CONFIG_ANDROID 1 +#define CONFIG_POSIX 1 +#define CONFIG_MADVISE 1 diff --git a/android/config/linux-ppc/config-host.h b/android/config/linux-ppc/config-host.h index 5f7a94f..9faf170 100644 --- a/android/config/linux-ppc/config-host.h +++ b/android/config/linux-ppc/config-host.h @@ -15,4 +15,6 @@ #define HOST_WORDS_BIGENDIAN 1 #define CONFIG_IOVEC 1 #define CONFIG_LINUX 1 +#define CONFIG_POSIX 1 #define CONFIG_ANDROID 1 +#define CONFIG_MADVISE 1 diff --git a/android/config/linux-x86/config-host.h b/android/config/linux-x86/config-host.h index 8cc2487..70a0eed 100644 --- a/android/config/linux-x86/config-host.h +++ b/android/config/linux-x86/config-host.h @@ -15,4 +15,6 @@ #define CONFIG_KVM_GS_RESTORE 1 #define CONFIG_IOVEC 1 #define CONFIG_LINUX 1 +#define CONFIG_POSIX 1 #define CONFIG_ANDROID 1 +#define CONFIG_MADVISE 1 diff --git a/android/console.c b/android/console.c index daae2d7..f4273f4 100644 --- a/android/console.c +++ b/android/console.c @@ -2058,33 +2058,33 @@ static const CommandDefRec event_commands[] = /********************************************************************************************/ static int -control_write_out_cb(void* opaque, const char* fmt, va_list ap) +control_write_out_cb(void* opaque, const char* str, int strsize) { ControlClient client = opaque; - int ret = control_vwrite(client, fmt, ap); - return ret; + control_control_write(client, str, strsize); + return strsize; } static int -control_write_err_cb(void* opaque, const char* fmt, va_list ap) +control_write_err_cb(void* opaque, const char* str, int strsize) { int ret = 0; ControlClient client = opaque; ret += control_write(client, "KO: "); - ret += control_vwrite(client, fmt, ap); - return ret; + control_control_write(client, str, strsize); + return ret + strsize; } static int do_snapshot_list( ControlClient client, char* args ) { - int ret; - OutputChannel *out = output_channel_alloc(client, control_write_out_cb); - OutputChannel *err = output_channel_alloc(client, control_write_err_cb); - do_info_snapshots_oc(out, err); - ret = output_channel_written(err); - output_channel_free(out); - output_channel_free(err); + int64_t ret; + Monitor *out = monitor_fake_new(client, control_write_out_cb); + Monitor *err = monitor_fake_new(client, control_write_err_cb); + do_info_snapshots(out, err); + ret = monitor_fake_get_bytes(err); + monitor_fake_free(err); + monitor_fake_free(out); return ret > 0; } @@ -2092,17 +2092,17 @@ do_snapshot_list( ControlClient client, char* args ) static int do_snapshot_save( ControlClient client, char* args ) { - int ret; + int64_t ret; if (args == NULL) { control_write(client, "KO: argument missing, try 'avd snapshot save <name>'\r\n"); return -1; } - OutputChannel *err = output_channel_alloc(client, control_write_err_cb); - do_savevm_oc(err, args); - ret = output_channel_written(err); - output_channel_free(err); + Monitor *err = monitor_fake_new(client, control_write_err_cb); + do_savevm(err, args); + ret = monitor_fake_get_bytes(err); + monitor_fake_free(err); return ret > 0; // no output on error channel indicates success } @@ -2110,17 +2110,17 @@ do_snapshot_save( ControlClient client, char* args ) static int do_snapshot_load( ControlClient client, char* args ) { - int ret; + int64_t ret; if (args == NULL) { control_write(client, "KO: argument missing, try 'avd snapshot load <name>'\r\n"); return -1; } - OutputChannel *err = output_channel_alloc(client, control_write_err_cb); - do_loadvm_oc(err, args); - ret = output_channel_written(err); - output_channel_free(err); + Monitor *err = monitor_fake_new(client, control_write_err_cb); + do_loadvm(err, args); + ret = monitor_fake_get_bytes(err); + monitor_fake_free(err); return ret > 0; } @@ -2128,17 +2128,17 @@ do_snapshot_load( ControlClient client, char* args ) static int do_snapshot_del( ControlClient client, char* args ) { - int ret; + int64_t ret; if (args == NULL) { control_write(client, "KO: argument missing, try 'avd snapshot del <name>'\r\n"); return -1; } - OutputChannel *err = output_channel_alloc(client, control_write_err_cb); - do_delvm_oc(err, args); - ret = output_channel_written(err); - output_channel_free(err); + Monitor *err = monitor_fake_new(client, control_write_err_cb); + do_delvm(err, args); + ret = monitor_fake_get_bytes(err); + monitor_fake_free(err); return ret > 0; } diff --git a/android/hw-sensors.c b/android/hw-sensors.c index c3ab12f..69447a2 100644 --- a/android/hw-sensors.c +++ b/android/hw-sensors.c @@ -212,7 +212,7 @@ _hwSensorClient_new( HwSensors* sensors ) cl->sensors = sensors; cl->enabledMask = 0; cl->delay_ms = 800; - cl->timer = qemu_new_timer(vm_clock, _hwSensorClient_tick, cl); + cl->timer = qemu_new_timer_ns(vm_clock, _hwSensorClient_tick, cl); cl->next = sensors->clients; sensors->clients = cl; @@ -316,7 +316,7 @@ _hwSensorClient_tick( void* opaque ) _hwSensorClient_send(cl, (uint8_t*) buffer, strlen(buffer)); } - now_ns = qemu_get_clock(vm_clock); + now_ns = qemu_get_clock_ns(vm_clock); snprintf(buffer, sizeof buffer, "sync:%lld", now_ns/1000); _hwSensorClient_send(cl, (uint8_t*)buffer, strlen(buffer)); diff --git a/android/looper-qemu.c b/android/looper-qemu.c index 5526f5b..714b48d 100644 --- a/android/looper-qemu.c +++ b/android/looper-qemu.c @@ -36,7 +36,7 @@ qlooptimer_startRelative(void* impl, Duration timeout_ms) if (timeout_ms == DURATION_INFINITE) qemu_del_timer(tt); else - qemu_mod_timer(tt, qemu_get_clock_ns(host_clock) + timeout_ms*1000000); + qemu_mod_timer(tt, qemu_get_clock_ms(host_clock) + timeout_ms); } static void @@ -85,7 +85,7 @@ qlooper_timer_init(Looper* looper, void* opaque) { timer->clazz = (LoopTimerClass*) &qlooptimer_class; - timer->impl = qemu_new_timer(host_clock, callback, opaque); + timer->impl = qemu_new_timer_ms(host_clock, callback, opaque); } /********************************************************************** @@ -370,7 +370,7 @@ qlooper_handle_io_bh(void* opaque) static Duration qlooper_now(Looper* ll) { - return qemu_get_clock_ns(host_clock)/1000000; + return qemu_get_clock_ms(host_clock); } extern void qemu_system_shutdown_request(void); diff --git a/android/protocol/core-commands-impl.c b/android/protocol/core-commands-impl.c index 4366529..bf6da15 100644 --- a/android/protocol/core-commands-impl.c +++ b/android/protocol/core-commands-impl.c @@ -19,7 +19,7 @@ #include "android/android.h" #include "android/globals.h" #include "telephony/modem_driver.h" -#include "trace.h" +#include "android-trace.h" #include "android/looper.h" #include "android/async-utils.h" #include "android/sync-utils.h" diff --git a/android/protocol/core-commands-qemu.c b/android/protocol/core-commands-qemu.c index 03fef64..76b3abb 100644 --- a/android/protocol/core-commands-qemu.c +++ b/android/protocol/core-commands-qemu.c @@ -19,7 +19,7 @@ #include "android/globals.h" #include "android/hw-sensors.h" #include "telephony/modem_driver.h" -#include "trace.h" +#include "android-trace.h" #include "audio/audio.h" #include "android/protocol/core-commands-api.h" diff --git a/arch_init.c b/arch_init.c new file mode 100644 index 0000000..b966d8a --- /dev/null +++ b/arch_init.c @@ -0,0 +1,732 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdint.h> +#include <stdarg.h> +#include <stdlib.h> +#ifndef _WIN32 +#include <sys/types.h> +#include <sys/mman.h> +#endif +#include "config.h" +#include "monitor.h" +#include "sysemu.h" +#include "arch_init.h" +#include "audio/audio.h" +#include "hw/irq.h" +#include "hw/pci.h" +#include "hw/audiodev.h" +#include "kvm.h" +#include "migration.h" +#include "net.h" +#include "gdbstub.h" +#include "hw/smbios.h" + +#ifdef TARGET_SPARC +int graphic_width = 1024; +int graphic_height = 768; +int graphic_depth = 8; +#else +int graphic_width = 800; +int graphic_height = 600; +int graphic_depth = 15; +#endif + +const char arch_config_name[] = CONFIG_QEMU_SHAREDIR "/target-" TARGET_ARCH ".conf"; + +#if defined(TARGET_ALPHA) +#define QEMU_ARCH QEMU_ARCH_ALPHA +#elif defined(TARGET_ARM) +#define QEMU_ARCH QEMU_ARCH_ARM +#elif defined(TARGET_CRIS) +#define QEMU_ARCH QEMU_ARCH_CRIS +#elif defined(TARGET_I386) +#define QEMU_ARCH QEMU_ARCH_I386 +#elif defined(TARGET_M68K) +#define QEMU_ARCH QEMU_ARCH_M68K +#elif defined(TARGET_LM32) +#define QEMU_ARCH QEMU_ARCH_LM32 +#elif defined(TARGET_MICROBLAZE) +#define QEMU_ARCH QEMU_ARCH_MICROBLAZE +#elif defined(TARGET_MIPS) +#define QEMU_ARCH QEMU_ARCH_MIPS +#elif defined(TARGET_PPC) +#define QEMU_ARCH QEMU_ARCH_PPC +#elif defined(TARGET_S390X) +#define QEMU_ARCH QEMU_ARCH_S390X +#elif defined(TARGET_SH4) +#define QEMU_ARCH QEMU_ARCH_SH4 +#elif defined(TARGET_SPARC) +#define QEMU_ARCH QEMU_ARCH_SPARC +#endif + +const uint32_t arch_type = QEMU_ARCH; + +#if 1 +/***********************************************************/ +/* ram save/restore */ + +#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */ +#define RAM_SAVE_FLAG_COMPRESS 0x02 +#define RAM_SAVE_FLAG_MEM_SIZE 0x04 +#define RAM_SAVE_FLAG_PAGE 0x08 +#define RAM_SAVE_FLAG_EOS 0x10 +#define RAM_SAVE_FLAG_CONTINUE 0x20 + +static int is_dup_page(uint8_t *page, uint8_t ch) +{ + uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch; + uint32_t *array = (uint32_t *)page; + int i; + + for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) { + if (array[i] != val) { + return 0; + } + } + + return 1; +} + +static RAMBlock *last_block; +static ram_addr_t last_offset; + +static int ram_save_block(QEMUFile *f) +{ + RAMBlock *block = last_block; + ram_addr_t offset = last_offset; + ram_addr_t current_addr; + int bytes_sent = 0; + + if (!block) + block = QLIST_FIRST(&ram_list.blocks); + + current_addr = block->offset + offset; + + do { + if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) { + uint8_t *p; + int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0; + + cpu_physical_memory_reset_dirty(current_addr, + current_addr + TARGET_PAGE_SIZE, + MIGRATION_DIRTY_FLAG); + + p = block->host + offset; + + if (is_dup_page(p, *p)) { + qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS); + if (!cont) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, + strlen(block->idstr)); + } + qemu_put_byte(f, *p); + bytes_sent = 1; + } else { + qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_PAGE); + if (!cont) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, + strlen(block->idstr)); + } + qemu_put_buffer(f, p, TARGET_PAGE_SIZE); + bytes_sent = TARGET_PAGE_SIZE; + } + + break; + } + + offset += TARGET_PAGE_SIZE; + if (offset >= block->length) { + offset = 0; + block = QLIST_NEXT(block, next); + if (!block) + block = QLIST_FIRST(&ram_list.blocks); + } + + current_addr = block->offset + offset; + + } while (current_addr != last_block->offset + last_offset); + + last_block = block; + last_offset = offset; + + return bytes_sent; +} + +static uint64_t bytes_transferred; + +static ram_addr_t ram_save_remaining(void) +{ + RAMBlock *block; + ram_addr_t count = 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + ram_addr_t addr; + for (addr = block->offset; addr < block->offset + block->length; + addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) { + count++; + } + } + } + + return count; +} + +uint64_t ram_bytes_remaining(void) +{ + return ram_save_remaining() * TARGET_PAGE_SIZE; +} + +uint64_t ram_bytes_transferred(void) +{ + return bytes_transferred; +} + +uint64_t ram_bytes_total(void) +{ + RAMBlock *block; + uint64_t total = 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) + total += block->length; + + return total; +} + +static int block_compar(const void *a, const void *b) +{ + RAMBlock * const *ablock = a; + RAMBlock * const *bblock = b; + if ((*ablock)->offset < (*bblock)->offset) { + return -1; + } else if ((*ablock)->offset > (*bblock)->offset) { + return 1; + } + return 0; +} + +static void sort_ram_list(void) +{ + RAMBlock *block, *nblock, **blocks; + int n; + n = 0; + QLIST_FOREACH(block, &ram_list.blocks, next) { + ++n; + } + blocks = qemu_malloc(n * sizeof *blocks); + n = 0; + QLIST_FOREACH_SAFE(block, &ram_list.blocks, next, nblock) { + blocks[n++] = block; + QLIST_REMOVE(block, next); + } + qsort(blocks, n, sizeof *blocks, block_compar); + while (--n >= 0) { + QLIST_INSERT_HEAD(&ram_list.blocks, blocks[n], next); + } + qemu_free(blocks); +} + +int ram_save_live(QEMUFile *f, int stage, void *opaque) +{ + ram_addr_t addr; + uint64_t bytes_transferred_last; + double bwidth = 0; + uint64_t expected_time = 0; + + if (stage < 0) { + cpu_physical_memory_set_dirty_tracking(0); + return 0; + } + + if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) { + qemu_file_set_error(f); + return 0; + } + + if (stage == 1) { + RAMBlock *block; + bytes_transferred = 0; + last_block = NULL; + last_offset = 0; + sort_ram_list(); + + /* Make sure all dirty bits are set */ + QLIST_FOREACH(block, &ram_list.blocks, next) { + for (addr = block->offset; addr < block->offset + block->length; + addr += TARGET_PAGE_SIZE) { + if (!cpu_physical_memory_get_dirty(addr, + MIGRATION_DIRTY_FLAG)) { + cpu_physical_memory_set_dirty(addr); + } + } + } + + /* Enable dirty memory tracking */ + cpu_physical_memory_set_dirty_tracking(1); + + qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); + + QLIST_FOREACH(block, &ram_list.blocks, next) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); + qemu_put_be64(f, block->length); + } + } + + bytes_transferred_last = bytes_transferred; + bwidth = qemu_get_clock_ns(rt_clock); + + while (!qemu_file_rate_limit(f)) { + int bytes_sent; + + bytes_sent = ram_save_block(f); + bytes_transferred += bytes_sent; + if (bytes_sent == 0) { /* no more blocks */ + break; + } + } + + bwidth = qemu_get_clock_ns(rt_clock) - bwidth; + bwidth = (bytes_transferred - bytes_transferred_last) / bwidth; + + /* if we haven't transferred anything this round, force expected_time to a + * a very high value, but without crashing */ + if (bwidth == 0) { + bwidth = 0.000001; + } + + /* try transferring iterative blocks of memory */ + if (stage == 3) { + int bytes_sent; + + /* flush all remaining blocks regardless of rate limiting */ + while ((bytes_sent = ram_save_block(f)) != 0) { + bytes_transferred += bytes_sent; + } + cpu_physical_memory_set_dirty_tracking(0); + } + + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + + expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth; + + return (stage == 2) && (expected_time <= migrate_max_downtime()); +} + +static inline void *host_from_stream_offset(QEMUFile *f, + ram_addr_t offset, + int flags) +{ + static RAMBlock *block = NULL; + char id[256]; + uint8_t len; + + if (flags & RAM_SAVE_FLAG_CONTINUE) { + if (!block) { + fprintf(stderr, "Ack, bad migration stream!\n"); + return NULL; + } + + return block->host + offset; + } + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id))) + return block->host + offset; + } + + fprintf(stderr, "Can't find block %s!\n", id); + return NULL; +} + +int ram_load(QEMUFile *f, void *opaque, int version_id) +{ + ram_addr_t addr; + int flags; + + if (version_id < 3 || version_id > 4) { + return -EINVAL; + } + + do { + addr = qemu_get_be64(f); + + flags = addr & ~TARGET_PAGE_MASK; + addr &= TARGET_PAGE_MASK; + + if (flags & RAM_SAVE_FLAG_MEM_SIZE) { + if (version_id == 3) { + if (addr != ram_bytes_total()) { + return -EINVAL; + } + } else { + /* Synchronize RAM block list */ + char id[256]; + ram_addr_t length; + ram_addr_t total_ram_bytes = addr; + + while (total_ram_bytes) { + RAMBlock *block; + uint8_t len; + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + length = qemu_get_be64(f); + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id))) { + if (block->length != length) + return -EINVAL; + break; + } + } + + if (!block) { + fprintf(stderr, "Unknown ramblock \"%s\", cannot " + "accept migration\n", id); + return -EINVAL; + } + + total_ram_bytes -= length; + } + } + } + + if (flags & RAM_SAVE_FLAG_COMPRESS) { + void *host; + uint8_t ch; + + if (version_id == 3) + host = qemu_get_ram_ptr(addr); + else + host = host_from_stream_offset(f, addr, flags); + if (!host) { + return -EINVAL; + } + + ch = qemu_get_byte(f); + memset(host, ch, TARGET_PAGE_SIZE); +#ifndef _WIN32 + if (ch == 0 && + (!kvm_enabled() || kvm_has_sync_mmu())) { + qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED); + } +#endif + } else if (flags & RAM_SAVE_FLAG_PAGE) { + void *host; + + if (version_id == 3) + host = qemu_get_ram_ptr(addr); + else + host = host_from_stream_offset(f, addr, flags); + + qemu_get_buffer(f, host, TARGET_PAGE_SIZE); + } + if (qemu_file_has_error(f)) { + return -EIO; + } + } while (!(flags & RAM_SAVE_FLAG_EOS)); + + return 0; +} +#endif + +void qemu_service_io(void) +{ + qemu_notify_event(); +} + +#ifdef HAS_AUDIO +struct soundhw { + const char *name; + const char *descr; + int enabled; + int isa; + union { + int (*init_isa) (qemu_irq *pic); + int (*init_pci) (PCIBus *bus); + } init; +}; + +static struct soundhw soundhw[] = { +#ifdef HAS_AUDIO_CHOICE +#if defined(TARGET_I386) || defined(TARGET_MIPS) + { + "pcspk", + "PC speaker", + 0, + 1, + { .init_isa = pcspk_audio_init } + }, +#endif + +#ifdef CONFIG_SB16 + { + "sb16", + "Creative Sound Blaster 16", + 0, + 1, + { .init_isa = SB16_init } + }, +#endif + +#ifdef CONFIG_CS4231A + { + "cs4231a", + "CS4231A", + 0, + 1, + { .init_isa = cs4231a_init } + }, +#endif + +#ifdef CONFIG_ADLIB + { + "adlib", +#ifdef HAS_YMF262 + "Yamaha YMF262 (OPL3)", +#else + "Yamaha YM3812 (OPL2)", +#endif + 0, + 1, + { .init_isa = Adlib_init } + }, +#endif + +#ifdef CONFIG_GUS + { + "gus", + "Gravis Ultrasound GF1", + 0, + 1, + { .init_isa = GUS_init } + }, +#endif + +#ifdef CONFIG_AC97 + { + "ac97", + "Intel 82801AA AC97 Audio", + 0, + 0, + { .init_pci = ac97_init } + }, +#endif + +#ifdef CONFIG_ES1370 + { + "es1370", + "ENSONIQ AudioPCI ES1370", + 0, + 0, + { .init_pci = es1370_init } + }, +#endif + +#ifdef CONFIG_HDA + { + "hda", + "Intel HD Audio", + 0, + 0, + { .init_pci = intel_hda_and_codec_init } + }, +#endif + +#endif /* HAS_AUDIO_CHOICE */ + + { NULL, NULL, 0, 0, { NULL } } +}; + +void select_soundhw(const char *optarg) +{ + struct soundhw *c; + + if (*optarg == '?') { + show_valid_cards: + + printf("Valid sound card names (comma separated):\n"); + for (c = soundhw; c->name; ++c) { + printf ("%-11s %s\n", c->name, c->descr); + } + printf("\n-soundhw all will enable all of the above\n"); + exit(*optarg != '?'); + } + else { + size_t l; + const char *p; + char *e; + int bad_card = 0; + + if (!strcmp(optarg, "all")) { + for (c = soundhw; c->name; ++c) { + c->enabled = 1; + } + return; + } + + p = optarg; + while (*p) { + e = strchr(p, ','); + l = !e ? strlen(p) : (size_t) (e - p); + + for (c = soundhw; c->name; ++c) { + if (!strncmp(c->name, p, l) && !c->name[l]) { + c->enabled = 1; + break; + } + } + + if (!c->name) { + if (l > 80) { + fprintf(stderr, + "Unknown sound card name (too big to show)\n"); + } + else { + fprintf(stderr, "Unknown sound card name `%.*s'\n", + (int) l, p); + } + bad_card = 1; + } + p += l + (e != NULL); + } + + if (bad_card) { + goto show_valid_cards; + } + } +} + +void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) +{ + struct soundhw *c; + + for (c = soundhw; c->name; ++c) { + if (c->enabled) { + if (c->isa) { + if (isa_pic) { + c->init.init_isa(isa_pic); + } + } else { + if (pci_bus) { + c->init.init_pci(pci_bus); + } + } + } + } +} +#else +void select_soundhw(const char *optarg) +{ +} +void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) +{ +} +#endif + +int qemu_uuid_parse(const char *str, uint8_t *uuid) +{ + int ret; + + if (strlen(str) != 36) { + return -1; + } + + ret = sscanf(str, UUID_FMT, &uuid[0], &uuid[1], &uuid[2], &uuid[3], + &uuid[4], &uuid[5], &uuid[6], &uuid[7], &uuid[8], &uuid[9], + &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14], + &uuid[15]); + + if (ret != 16) { + return -1; + } +#ifdef TARGET_I386 + smbios_add_field(1, offsetof(struct smbios_type_1, uuid), 16, uuid); +#endif + return 0; +} + +#if 0 +void do_acpitable_option(const char *optarg) +{ +#ifdef TARGET_I386 + if (acpi_table_add(optarg) < 0) { + fprintf(stderr, "Wrong acpi table provided\n"); + exit(1); + } +#endif +} +#endif + +void do_smbios_option(const char *optarg) +{ +#ifdef TARGET_I386 + if (smbios_entry_add(optarg) < 0) { + fprintf(stderr, "Wrong smbios provided\n"); + exit(1); + } +#endif +} + +void cpudef_init(void) +{ +#if defined(cpudef_setup) + cpudef_setup(); /* parse cpu definitions in target config file */ +#endif +} + +int audio_available(void) +{ +#ifdef HAS_AUDIO + return 1; +#else + return 0; +#endif +} + +int kvm_available(void) +{ +#ifdef CONFIG_KVM + return 1; +#else + return 0; +#endif +} + +int xen_available(void) +{ +#ifdef CONFIG_XEN + return 1; +#else + return 0; +#endif +} diff --git a/arch_init.h b/arch_init.h new file mode 100644 index 0000000..86ebc14 --- /dev/null +++ b/arch_init.h @@ -0,0 +1,33 @@ +#ifndef QEMU_ARCH_INIT_H +#define QEMU_ARCH_INIT_H + +extern const char arch_config_name[]; + +enum { + QEMU_ARCH_ALL = -1, + QEMU_ARCH_ALPHA = 1, + QEMU_ARCH_ARM = 2, + QEMU_ARCH_CRIS = 4, + QEMU_ARCH_I386 = 8, + QEMU_ARCH_M68K = 16, + QEMU_ARCH_LM32 = 32, + QEMU_ARCH_MICROBLAZE = 64, + QEMU_ARCH_MIPS = 128, + QEMU_ARCH_PPC = 256, + QEMU_ARCH_S390X = 512, + QEMU_ARCH_SH4 = 1024, + QEMU_ARCH_SPARC = 2048, +}; + +extern const uint32_t arch_type; + +void select_soundhw(const char *optarg); +void do_acpitable_option(const char *optarg); +void do_smbios_option(const char *optarg); +void cpudef_init(void); +int audio_available(void); +void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus); +int kvm_available(void); +int xen_available(void); + +#endif @@ -1587,7 +1587,7 @@ arm_decode_bitfield (const char *ptr, unsigned long insn, } static void -arm_decode_shift (long given, fprintf_ftype func, void *stream, +arm_decode_shift (long given, fprintf_function func, void *stream, int print_shift) { func (stream, "%s", arm_regnames[given & 0xf]); @@ -1633,7 +1633,7 @@ print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given, { const struct opcode32 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; unsigned long mask; unsigned long value; int cond; @@ -2127,7 +2127,7 @@ static void print_arm_address (bfd_vma pc, struct disassemble_info *info, long given) { void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; if (((given & 0x000f0000) == 0x000f0000) && ((given & 0x02000000) == 0)) @@ -2222,7 +2222,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb) { const struct opcode32 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; if (thumb) { @@ -2676,7 +2676,7 @@ print_insn_arm_internal (bfd_vma pc, struct disassemble_info *info, long given) { const struct opcode32 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; if (print_insn_coprocessor (pc, info, given, false)) return; @@ -3036,7 +3036,7 @@ print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given) { const struct opcode16 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; for (insn = thumb_opcodes; insn->assembler; insn++) if ((given & insn->mask) == insn->value) @@ -3312,7 +3312,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) { const struct opcode32 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; if (print_insn_coprocessor (pc, info, given, true)) return; @@ -4101,6 +4101,30 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) addresses, since the addend is not currently pc-relative. */ pc = 0; + /* We include the hexdump of the instruction. The format here + matches that used by objdump and the ARM ARM (in particular, + 32 bit Thumb instructions are displayed as pairs of halfwords, + not as a single word.) */ + if (is_thumb) + { + if (size == 2) + { + info->fprintf_func(info->stream, "%04lx ", + ((unsigned long)given) & 0xffff); + } + else + { + info->fprintf_func(info->stream, "%04lx %04lx ", + (((unsigned long)given) >> 16) & 0xffff, + ((unsigned long)given) & 0xffff); + } + } + else + { + info->fprintf_func(info->stream, "%08lx ", + ((unsigned long)given) & 0xffffffff); + } + printer (pc, info, given); if (is_thumb) @@ -33,7 +33,6 @@ #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024) #else #include "qemu-common.h" -#include "sysemu.h" #include "gdbstub.h" #endif @@ -373,45 +372,64 @@ uint32_t do_arm_semihosting(CPUState *env) #ifdef CONFIG_USER_ONLY /* Build a commandline from the original argv. */ { - char **arg = ts->info->host_argv; - int len = ARG(1); - /* lock the buffer on the ARM side */ - char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0); + char *arm_cmdline_buffer; + const char *host_cmdline_buffer; - if (!cmdline_buffer) - /* FIXME - should this error code be -TARGET_EFAULT ? */ - return (uint32_t)-1; + unsigned int i; + unsigned int arm_cmdline_len = ARG(1); + unsigned int host_cmdline_len = + ts->info->arg_end-ts->info->arg_start; + + if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) { + return -1; /* not enough space to store command line */ + } - s = cmdline_buffer; - while (*arg && len > 2) { - int n = strlen(*arg); + if (!host_cmdline_len) { + /* We special-case the "empty command line" case (argc==0). + Just provide the terminating 0. */ + arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0); + arm_cmdline_buffer[0] = 0; + unlock_user(arm_cmdline_buffer, ARG(0), 1); - if (s != cmdline_buffer) { - *(s++) = ' '; - len--; - } - if (n >= len) - n = len - 1; - memcpy(s, *arg, n); - s += n; - len -= n; - arg++; + /* Adjust the commandline length argument. */ + SET_ARG(1, 0); + return 0; } - /* Null terminate the string. */ - *s = 0; - len = s - cmdline_buffer; - /* Unlock the buffer on the ARM side. */ - unlock_user(cmdline_buffer, ARG(0), len); + /* lock the buffers on the ARM side */ + arm_cmdline_buffer = + lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0); + host_cmdline_buffer = + lock_user(VERIFY_READ, ts->info->arg_start, + host_cmdline_len, 1); - /* Adjust the commandline length argument. */ - SET_ARG(1, len); + if (arm_cmdline_buffer && host_cmdline_buffer) + { + /* the last argument is zero-terminated; + no need for additional termination */ + memcpy(arm_cmdline_buffer, host_cmdline_buffer, + host_cmdline_len); - /* Return success if commandline fit into buffer. */ - return *arg ? -1 : 0; + /* separate arguments by white spaces */ + for (i = 0; i < host_cmdline_len-1; i++) { + if (arm_cmdline_buffer[i] == 0) { + arm_cmdline_buffer[i] = ' '; + } + } + + /* Adjust the commandline length argument. */ + SET_ARG(1, host_cmdline_len-1); + } + + /* Unlock the buffers on the ARM side. */ + unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len); + unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0); + + /* Return success if we could return a commandline. */ + return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1; } #else - return -1; + return -1; #endif case SYS_HEAPINFO: { diff --git a/audio/audio.c b/audio/audio.c index 3e60c12..7acd3d7 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1198,7 +1198,7 @@ static void audio_timer (void *opaque) AudioState *s = opaque; #if 0 #define MAX_DIFFS 100 - int64_t now = qemu_get_clock(vm_clock); + int64_t now = qemu_get_clock_ms(vm_clock); static int64_t last = 0; static float diffs[MAX_DIFFS]; static int num_diffs; @@ -1227,7 +1227,7 @@ static void audio_timer (void *opaque) #endif audio_run ("timer"); - qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); + qemu_mod_timer (s->ts, qemu_get_clock_ns (vm_clock) + conf.period.ticks); } @@ -1250,7 +1250,7 @@ static void audio_reset_timer (void) AudioState *s = &glob_audio_state; if (audio_is_timer_needed ()) { - qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); + qemu_mod_timer (s->ts, qemu_get_clock_ns (vm_clock) + 1); } else { qemu_del_timer (s->ts); @@ -1964,7 +1964,7 @@ static void audio_init (void) QLIST_INIT (&s->cap_head); atexit (audio_atexit); - s->ts = qemu_new_timer (vm_clock, audio_timer, s); + s->ts = qemu_new_timer_ns (vm_clock, audio_timer, s); if (!s->ts) { dolog ("Could not create audio timer\n"); return; diff --git a/audio/audio_int.h b/audio/audio_int.h index c6afe81..2ec0bf0 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -240,13 +240,9 @@ static inline int audio_ring_dist (int dst, int src, int len) } #if defined __GNUC__ -#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2))) #define INIT_FIELD(f) . f -#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m))) #else -#define GCC_ATTR /**/ #define INIT_FIELD(f) /**/ -#define GCC_FMT_ATTR(n, m) #endif static void GCC_ATTR dolog (const char *fmt, ...) @@ -144,6 +144,7 @@ CPU_CONVERT(le, 64, uint64_t) #define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) #define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) +#define cpu_to_be64wu(p, v) cpu_to_be64w(p, v) #else @@ -201,6 +202,20 @@ static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) p1[3] = v & 0xff; } +static inline void cpu_to_be64wu(uint64_t *p, uint64_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v >> 56; + p1[1] = v >> 48; + p1[2] = v >> 40; + p1[3] = v >> 32; + p1[4] = v >> 24; + p1[5] = v >> 16; + p1[6] = v >> 8; + p1[7] = v & 0xff; +} + #endif #ifdef HOST_WORDS_BIGENDIAN @@ -19,7 +19,6 @@ #include "qemu-common.h" #include "qemu-char.h" -#include "sysemu.h" #include "net.h" #include "bt-host.h" @@ -19,7 +19,6 @@ #include "qemu-common.h" #include "qemu-char.h" -#include "sysemu.h" #include "net.h" #include "hw/bt.h" diff --git a/buffered_file.c b/buffered_file.c index 1836e7e..a661570 100644 --- a/buffered_file.c +++ b/buffered_file.c @@ -14,7 +14,6 @@ #include "qemu-common.h" #include "hw/hw.h" #include "qemu-timer.h" -#include "sysemu.h" #include "qemu-char.h" #include "buffered_file.h" @@ -206,23 +205,26 @@ static int buffered_rate_limit(void *opaque) return 0; } -static size_t buffered_set_rate_limit(void *opaque, size_t new_rate) +static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate) { QEMUFileBuffered *s = opaque; - if (s->has_error) goto out; + if (new_rate > SIZE_MAX) { + new_rate = SIZE_MAX; + } + s->xfer_limit = new_rate / 10; - + out: return s->xfer_limit; } -static size_t buffered_get_rate_limit(void *opaque) +static int64_t buffered_get_rate_limit(void *opaque) { QEMUFileBuffered *s = opaque; - + return s->xfer_limit; } @@ -235,7 +237,7 @@ static void buffered_rate_tick(void *opaque) return; } - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100); if (s->freeze_output) return; @@ -271,9 +273,9 @@ QEMUFile *qemu_fopen_ops_buffered(void *opaque, buffered_set_rate_limit, buffered_get_rate_limit); - s->timer = qemu_new_timer(rt_clock, buffered_rate_tick, s); + s->timer = qemu_new_timer_ms(rt_clock, buffered_rate_tick, s); - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100); return s->file; } diff --git a/cache-utils.h b/cache-utils.h index b45fde4..0b65907 100644 --- a/cache-utils.h +++ b/cache-utils.h @@ -9,7 +9,7 @@ struct qemu_cache_conf { extern struct qemu_cache_conf qemu_cache_conf; -extern void qemu_cache_utils_init(char **envp); +void qemu_cache_utils_init(char **envp); /* mildly adjusted code from tcg-dyngen.c */ static inline void flush_icache_range(unsigned long start, unsigned long stop) @@ -26,45 +26,45 @@ struct sigfd_compat_info static void *sigwait_compat(void *opaque) { struct sigfd_compat_info *info = opaque; - int err; sigset_t all; sigfillset(&all); sigprocmask(SIG_BLOCK, &all, NULL); - do { - siginfo_t siginfo; - - err = sigwaitinfo(&info->mask, &siginfo); - if (err == -1 && errno == EINTR) { - err = 0; - continue; + while (1) { + int sig; + int err; + + err = sigwait(&info->mask, &sig); + if (err != 0) { + if (errno == EINTR) { + continue; + } else { + return NULL; + } + } else { + struct qemu_signalfd_siginfo buffer; + size_t offset = 0; + + memset(&buffer, 0, sizeof(buffer)); + buffer.ssi_signo = sig; + + while (offset < sizeof(buffer)) { + ssize_t len; + + len = write(info->fd, (char *)&buffer + offset, + sizeof(buffer) - offset); + if (len == -1 && errno == EINTR) + continue; + + if (len <= 0) { + return NULL; + } + + offset += len; + } } - - if (err > 0) { - char buffer[128]; - size_t offset = 0; - - memcpy(buffer, &err, sizeof(err)); - while (offset < sizeof(buffer)) { - ssize_t len; - - len = write(info->fd, buffer + offset, - sizeof(buffer) - offset); - if (len == -1 && errno == EINTR) - continue; - - if (len <= 0) { - err = -1; - break; - } - - offset += len; - } - } - } while (err >= 0); - - return NULL; + } } static int qemu_signalfd_compat(const sigset_t *mask) @@ -76,15 +76,18 @@ static int qemu_signalfd_compat(const sigset_t *mask) info = malloc(sizeof(*info)); if (info == NULL) { - errno = ENOMEM; - return -1; + errno = ENOMEM; + return -1; } if (pipe(fds) == -1) { - free(info); - return -1; + free(info); + return -1; } + qemu_set_cloexec(fds[0]); + qemu_set_cloexec(fds[1]); + memcpy(&info->mask, mask, sizeof(*mask)); info->fd = fds[1]; @@ -100,29 +103,15 @@ static int qemu_signalfd_compat(const sigset_t *mask) int qemu_signalfd(const sigset_t *mask) { -#if defined(SYS_signalfd) +#if defined(CONFIG_SIGNALFD) int ret; ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8); - if (!(ret == -1 && errno == ENOSYS)) - return ret; + if (ret != -1) { + qemu_set_cloexec(ret); + return ret; + } #endif return qemu_signalfd_compat(mask); } - -int qemu_eventfd(int *fds) -{ -#if defined(SYS_eventfd) - int ret; - - ret = syscall(SYS_eventfd, 0); - if (ret >= 0) { - fds[0] = fds[1] = ret; - return 0; - } else if (!(ret == -1 && errno == ENOSYS)) - return ret; -#endif - - return pipe(fds); -} @@ -17,12 +17,27 @@ #include <signal.h> struct qemu_signalfd_siginfo { - uint32_t ssi_signo; - uint8_t pad[124]; + uint32_t ssi_signo; /* Signal number */ + int32_t ssi_errno; /* Error number (unused) */ + int32_t ssi_code; /* Signal code */ + uint32_t ssi_pid; /* PID of sender */ + uint32_t ssi_uid; /* Real UID of sender */ + int32_t ssi_fd; /* File descriptor (SIGIO) */ + uint32_t ssi_tid; /* Kernel timer ID (POSIX timers) */ + uint32_t ssi_band; /* Band event (SIGIO) */ + uint32_t ssi_overrun; /* POSIX timer overrun count */ + uint32_t ssi_trapno; /* Trap number that caused signal */ + int32_t ssi_status; /* Exit status or signal (SIGCHLD) */ + int32_t ssi_int; /* Integer sent by sigqueue(2) */ + uint64_t ssi_ptr; /* Pointer sent by sigqueue(2) */ + uint64_t ssi_utime; /* User CPU time consumed (SIGCHLD) */ + uint64_t ssi_stime; /* System CPU time consumed (SIGCHLD) */ + uint64_t ssi_addr; /* Address that generated signal + (for hardware-generated signals) */ + uint8_t pad[48]; /* Pad size to 128 bytes (allow for + additional fields in the future) */ }; int qemu_signalfd(const sigset_t *mask); -int qemu_eventfd(int *fds); - #endif @@ -32,6 +32,39 @@ #define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) #define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff) +#ifdef CONFIG_SKINNING +// Skinning overwrites these functions to put the skin in between +DisplayState *qemu_graphic_console_init(vga_hw_update_ptr update, + vga_hw_invalidate_ptr invalidate, + vga_hw_screen_dump_ptr screen_dump, + vga_hw_text_update_ptr text_update, + void *opaque); +#undef graphic_console_init +#define graphic_console_init qemu_graphic_console_init + +void original_qemu_console_resize(DisplayState *ds, int width, int height); +#undef qemu_console_resize +#define qemu_console_resize original_qemu_console_resize +#endif + +int multitouch_enabled = 0; +static QEMUDisplayCloseCallback *qemu_display_close_callback = NULL; +static void *qemu_display_close_callback_opaque = NULL; + +void qemu_set_display_close_handler(QEMUDisplayCloseCallback *cb, void *opaque) +{ + qemu_display_close_callback = cb; + qemu_display_close_callback_opaque = opaque; +} + +int qemu_run_display_close_handler(void) +{ + if (qemu_display_close_callback != NULL) { + return qemu_display_close_callback(qemu_display_close_callback_opaque); + } + return 1; +} + typedef struct TextAttributes { uint8_t fgcol:4; uint8_t bgcol:4; @@ -137,6 +170,7 @@ struct TextConsole { TextAttributes t_attrib; /* currently active text attributes */ TextCell *cells; int text_x[2], text_y[2], cursor_invalidate; + int echo; int update_x0; int update_y0; @@ -235,8 +269,8 @@ static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba) return color; } -static void vga_fill_rect (DisplayState *ds, - int posx, int posy, int width, int height, uint32_t color) +void vga_fill_rect(DisplayState *ds, int posx, int posy, + int width, int height, uint32_t color) { uint8_t *d, *d1; int x, y, bpp; @@ -1069,8 +1103,10 @@ void console_select(unsigned int index) if (index >= MAX_CONSOLES) return; + if (active_console) { active_console->g_width = ds_get_width(active_console->ds); active_console->g_height = ds_get_height(active_console->ds); + } s = consoles[index]; if (s) { DisplayState *ds = s->ds; @@ -1141,7 +1177,7 @@ static void kbd_send_chars(void *opaque) /* characters are pending: we send them a bit later (XXX: horrible, should change char device API) */ if (s->out_fifo.count > 0) { - qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1); + qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1); } } @@ -1184,9 +1220,15 @@ void kbd_put_keysym(int keysym) *q++ = '\033'; *q++ = '['; *q++ = keysym & 0xff; + } else if (s->echo && (keysym == '\r' || keysym == '\n')) { + console_puts(s->chr, (const uint8_t *) "\r", 1); + *q++ = '\n'; } else { *q++ = keysym; } + if (s->echo) { + console_puts(s->chr, buf, q - buf); + } if (s->chr->chr_read) { qemu_fifo_write(&s->out_fifo, buf, q - buf); kbd_send_chars(s); @@ -1278,38 +1320,40 @@ static DisplaySurface* defaultallocator_create_displaysurface(int width, int hei { DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface)); - surface->width = width; - surface->height = height; - surface->linesize = width * 4; - surface->pf = qemu_default_pixelformat(32); -#ifdef HOST_WORDS_BIGENDIAN - surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; -#else - surface->flags = QEMU_ALLOCATED_FLAG; -#endif - surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height); - + int linesize = width * 4; + qemu_alloc_display(surface, width, height, linesize, + qemu_default_pixelformat(32), 0); return surface; } static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface, int width, int height) { + int linesize = width * 4; + qemu_alloc_display(surface, width, height, linesize, + qemu_default_pixelformat(32), 0); + return surface; +} + +void qemu_alloc_display(DisplaySurface *surface, int width, int height, + int linesize, PixelFormat pf, int newflags) +{ + void *data; surface->width = width; surface->height = height; - surface->linesize = width * 4; - surface->pf = qemu_default_pixelformat(32); - if (surface->flags & QEMU_ALLOCATED_FLAG) - surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height); - else - surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height); + surface->linesize = linesize; + surface->pf = pf; + if (surface->flags & QEMU_ALLOCATED_FLAG) { + data = qemu_realloc(surface->data, + surface->linesize * surface->height); + } else { + data = qemu_malloc(surface->linesize * surface->height); + } + surface->data = (uint8_t *)data; + surface->flags = newflags | QEMU_ALLOCATED_FLAG; #ifdef HOST_WORDS_BIGENDIAN - surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; -#else - surface->flags = QEMU_ALLOCATED_FLAG; + surface->flags |= QEMU_BIG_ENDIAN_FLAG; #endif - - return surface; } DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp, @@ -1441,43 +1485,27 @@ void console_color_init(DisplayState *ds) static int n_text_consoles; static CharDriverState *text_consoles[128]; -static QemuOpts *text_console_opts[128]; -static void text_console_do_init(CharDriverState *chr, DisplayState *ds, QemuOpts *opts) +static void text_console_set_echo(CharDriverState *chr, bool echo) { - TextConsole *s; - unsigned width; - unsigned height; - static int color_inited; + TextConsole *s = chr->opaque; - width = qemu_opt_get_number(opts, "width", 0); - if (width == 0) - width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH; + s->echo = echo; +} - height = qemu_opt_get_number(opts, "height", 0); - if (height == 0) - height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT; +static void text_console_do_init(CharDriverState *chr, DisplayState *ds) +{ + TextConsole *s; + static int color_inited; - if (width == 0 || height == 0) { - s = new_console(ds, TEXT_CONSOLE); - width = ds_get_width(s->ds); - height = ds_get_height(s->ds); - } else { - s = new_console(ds, TEXT_CONSOLE_FIXED_SIZE); - } + s = chr->opaque; - if (!s) { - free(chr); - return; - } - chr->opaque = s; chr->chr_write = console_puts; chr->chr_send_event = console_send_event; - s->chr = chr; s->out_fifo.buf = s->out_fifo_buf; s->out_fifo.buf_size = sizeof(s->out_fifo_buf); - s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s); + s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s); s->ds = ds; if (!color_inited) { @@ -1489,8 +1517,10 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds, QemuOpt s->total_height = DEFAULT_BACKSCROLL; s->x = 0; s->y = 0; - s->g_width = width; - s->g_height = height; + if (s->console_type == TEXT_CONSOLE) { + s->g_width = ds_get_width(s->ds); + s->g_height = ds_get_height(s->ds); + } s->hw_invalidate = text_console_invalidate; s->hw_text_update = text_console_update; @@ -1526,6 +1556,9 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds, QemuOpt CharDriverState *text_console_init(QemuOpts *opts) { CharDriverState *chr; + TextConsole *s; + unsigned width; + unsigned height; chr = qemu_mallocz(sizeof(CharDriverState)); @@ -1534,9 +1567,32 @@ CharDriverState *text_console_init(QemuOpts *opts) exit(1); } text_consoles[n_text_consoles] = chr; - text_console_opts[n_text_consoles] = opts; n_text_consoles++; + width = qemu_opt_get_number(opts, "width", 0); + if (width == 0) + width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH; + + height = qemu_opt_get_number(opts, "height", 0); + if (height == 0) + height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT; + + if (width == 0 || height == 0) { + s = new_console(NULL, TEXT_CONSOLE); + } else { + s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE); + } + + if (!s) { + free(chr); + return NULL; + } + + s->chr = chr; + s->g_width = width; + s->g_height = height; + chr->opaque = s; + chr->chr_set_echo = text_console_set_echo; return chr; } @@ -1577,9 +1633,7 @@ void text_consoles_set_display(DisplayState *ds) int i; for (i = 0; i < n_text_consoles; i++) { - text_console_do_init(text_consoles[i], ds, text_console_opts[i]); - qemu_opts_del(text_console_opts[i]); - text_console_opts[i] = NULL; + text_console_do_init(text_consoles[i], ds); } n_text_consoles = 0; @@ -10,6 +10,7 @@ #define MOUSE_EVENT_LBUTTON 0x01 #define MOUSE_EVENT_RBUTTON 0x02 #define MOUSE_EVENT_MBUTTON 0x04 +extern int multitouch_enabled; /* identical to the ps/2 keyboard bits */ #define QEMU_SCROLL_LOCK_LED (1 << 0) @@ -23,6 +24,10 @@ #define GUI_REFRESH_INTERVAL 30 #endif +typedef int QEMUDisplayCloseCallback(void *opaque); +void qemu_set_display_close_handler(QEMUDisplayCloseCallback *cb, void *opaque); +int qemu_run_display_close_handler(void); + typedef void QEMUPutKBDEvent(void *opaque, int keycode); typedef void QEMUPutLEDEvent(void *opaque, int ledstate); typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state); @@ -39,6 +44,14 @@ typedef struct QEMUPutMouseEntry { QTAILQ_ENTRY(QEMUPutMouseEntry) node; } QEMUPutMouseEntry; +typedef struct QEMUPutKBDEntry { + QEMUPutKBDEvent *put_kbd_event; + void *opaque; + + /* used internally by qemu for handling keyboards */ + QTAILQ_ENTRY(QEMUPutKBDEntry) next; +} QEMUPutKBDEntry; + typedef struct QEMUPutLEDEntry { QEMUPutLEDEvent *put_led; void *opaque; @@ -46,7 +59,7 @@ typedef struct QEMUPutLEDEntry { } QEMUPutLEDEntry; void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque); -void qemu_remove_kbd_event_handler(void); +void qemu_remove_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque); QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute, const char *name); @@ -165,7 +178,13 @@ struct DisplayChangeListener { void (*dpy_fill)(struct DisplayState *s, int x, int y, int w, int h, uint32_t c); void (*dpy_text_cursor)(struct DisplayState *s, int x, int y); - +#ifdef CONFIG_GLES2 + void (*dpy_updatecaption)(void); +#endif +#ifdef CONFIG_SKINNING + void (*dpy_enablezoom)(struct DisplayState *s, int width, int height); + void (*dpy_getresolution)(int *width, int *height); +#endif struct DisplayChangeListener *next; }; @@ -204,10 +223,14 @@ struct DisplayState { struct DisplayState *next; }; +void vga_fill_rect (DisplayState *ds, int posx, int posy, + int width, int height, uint32_t color); void register_displaystate(DisplayState *ds); DisplayState *get_displaystate(void); DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp, int linesize, uint8_t *data); +void qemu_alloc_display(DisplaySurface *surface, int width, int height, + int linesize, PixelFormat pf, int newflags); PixelFormat qemu_different_endianness_pixelformat(int bpp); PixelFormat qemu_default_pixelformat(int bpp); @@ -256,7 +279,6 @@ static inline void register_displayupdatelistener(DisplayState *ds, DisplayUpdat } void unregister_displayupdatelistener(DisplayState *ds, DisplayUpdateListener *dul); - #endif static inline void dpy_update(DisplayState *s, int x, int y, int w, int h) @@ -275,6 +297,20 @@ static inline void dpy_update(DisplayState *s, int x, int y, int w, int h) #endif } +#ifdef CONFIG_GLES2 +static inline void dpy_updatecaption(DisplayState *s) +{ + struct DisplayChangeListener *dcl = s->listeners; + while (dcl != NULL) { + if(dcl->dpy_updatecaption != NULL) + { + dcl->dpy_updatecaption(); + } + dcl = dcl->next; + } +} +#endif + static inline void dpy_resize(DisplayState *s) { struct DisplayChangeListener *dcl = s->listeners; @@ -331,6 +367,26 @@ static inline void dpy_cursor(struct DisplayState *s, int x, int y) { } } +#ifdef CONFIG_SKINNING +static inline void dpy_enablezoom(struct DisplayState *s, int width, int height) +{ + struct DisplayChangeListener *dcl = s->listeners; + while (dcl != NULL) { + if (dcl->dpy_enablezoom) dcl->dpy_enablezoom(s, width, height); + dcl = dcl->next; + } +} + +static inline void dpy_getresolution(struct DisplayState *s, int *width, int *height) +{ + struct DisplayChangeListener *dcl = s->listeners; + while (dcl != NULL) { + if (dcl->dpy_getresolution) dcl->dpy_getresolution(width, height); + dcl = dcl->next; + } +} +#endif + static inline int ds_get_linesize(DisplayState *ds) { return ds->surface->linesize; @@ -366,7 +422,7 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch) { if (!(ch & 0xff)) ch |= ' '; - cpu_to_le32wu((uint32_t *) dest, ch); + *dest = ch; } typedef void (*vga_hw_update_ptr)(void *); @@ -765,15 +765,15 @@ int page_check_range(target_ulong start, target_ulong len, int flags); CPUState *cpu_copy(CPUState *env); CPUState *qemu_get_cpu(int cpu); -void cpu_dump_state(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +#define CPU_DUMP_CODE 0x00010000 + +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags); -void cpu_dump_statistics (CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_statistics(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags); void QEMU_NORETURN cpu_abort(CPUState *env, const char *fmt, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); + GCC_FMT_ATTR(2, 3); extern CPUState *first_cpu; extern CPUState *cpu_single_env; @@ -862,9 +862,31 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr); /* memory API */ extern int phys_ram_fd; -extern uint8_t *phys_ram_dirty; extern ram_addr_t ram_size; -extern ram_addr_t last_ram_offset; + +/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */ +#define RAM_PREALLOC_MASK (1 << 0) + +typedef struct RAMBlock { + uint8_t *host; + ram_addr_t offset; + ram_addr_t length; + uint32_t flags; + char idstr[256]; + QLIST_ENTRY(RAMBlock) next; +#if defined(__linux__) && !defined(TARGET_S390X) + int fd; +#endif +} RAMBlock; + +typedef struct RAMList { + uint8_t *phys_dirty; + QLIST_HEAD(ram, RAMBlock) blocks; +} RAMList; +extern RAMList ram_list; + +extern const char *mem_path; +extern int mem_prealloc; /* physical memory access */ @@ -891,18 +913,44 @@ extern ram_addr_t last_ram_offset; /* read dirty bit (return 0 or 1) */ static inline int cpu_physical_memory_is_dirty(ram_addr_t addr) { - return phys_ram_dirty[addr >> TARGET_PAGE_BITS] == 0xff; + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] == 0xff; +} + +static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr) +{ + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS]; } static inline int cpu_physical_memory_get_dirty(ram_addr_t addr, int dirty_flags) { - return phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags; + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags; } static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) { - phys_ram_dirty[addr >> TARGET_PAGE_BITS] = 0xff; + ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] = 0xff; +} + +static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr, + int dirty_flags) +{ + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags; +} + +static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start, + int length, + int dirty_flags) +{ + int i, mask, len; + uint8_t *p; + + len = length >> TARGET_PAGE_BITS; + mask = ~dirty_flags; + p = ram_list.phys_dirty + (start >> TARGET_PAGE_BITS); + for (i = 0; i < len; i++) { + p[i] &= mask; + } } void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, diff --git a/cpu-common.h b/cpu-common.h index a422689..9e32177 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -28,10 +28,21 @@ typedef unsigned long ram_addr_t; typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value); typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr); -void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, +void cpu_register_physical_memory_log(target_phys_addr_t start_addr, ram_addr_t size, ram_addr_t phys_offset, - ram_addr_t region_offset); + ram_addr_t region_offset, + bool log_dirty); + +static inline void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset, + ram_addr_t region_offset) +{ + cpu_register_physical_memory_log(start_addr, size, phys_offset, + region_offset, false); +} + static inline void cpu_register_physical_memory(target_phys_addr_t start_addr, ram_addr_t size, ram_addr_t phys_offset) @@ -40,12 +51,19 @@ static inline void cpu_register_physical_memory(target_phys_addr_t start_addr, } ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr); -ram_addr_t qemu_ram_alloc(ram_addr_t); +ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name, + ram_addr_t size, void *host); +ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size); void qemu_ram_free(ram_addr_t addr); +void qemu_ram_remap(ram_addr_t addr, ram_addr_t length); /* This should only be used for ram local to a device. */ void *qemu_get_ram_ptr(ram_addr_t addr); +/* Same but slower, to use for migration, where the order of + * RAMBlocks must not change. */ +void *qemu_safe_ram_ptr(ram_addr_t addr); /* This should not be used by devices. */ -ram_addr_t qemu_ram_addr_from_host(void *ptr); +int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr); +ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr); int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read, CPUWriteMemoryFunc * const *mem_write, @@ -203,12 +203,14 @@ static void cpu_handle_debug_exception(CPUState *env) { CPUWatchpoint *wp; - if (!env->watchpoint_hit) - QTAILQ_FOREACH(wp, &env->watchpoints, entry) + if (!env->watchpoint_hit) { + QTAILQ_FOREACH(wp, &env->watchpoints, entry) { wp->flags &= ~BP_WATCHPOINT_HIT; - - if (debug_excp_handler) + } + } + if (debug_excp_handler) { debug_excp_handler(env); + } } /* main execution loop */ @@ -255,7 +257,9 @@ int cpu_exec(CPUState *env1) env->cc_x = (env->sr >> 4) & 1; #elif defined(TARGET_ALPHA) #elif defined(TARGET_ARM) +#elif defined(TARGET_UNICORE32) #elif defined(TARGET_PPC) +#elif defined(TARGET_LM32) #elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) @@ -280,8 +284,9 @@ int cpu_exec(CPUState *env1) if (env->exception_index >= EXCP_INTERRUPT) { /* exit request from the cpu execution loop */ ret = env->exception_index; - if (ret == EXCP_DEBUG) + if (ret == EXCP_DEBUG) { cpu_handle_debug_exception(env); + } break; } else { #if defined(CONFIG_USER_ONLY) @@ -311,6 +316,8 @@ int cpu_exec(CPUState *env1) env->old_exception = -1; #elif defined(TARGET_PPC) do_interrupt(env); +#elif defined(TARGET_LM32) + do_interrupt(env); #elif defined(TARGET_MICROBLAZE) do_interrupt(env); #elif defined(TARGET_MIPS) @@ -319,6 +326,8 @@ int cpu_exec(CPUState *env1) do_interrupt(env); #elif defined(TARGET_ARM) do_interrupt(env); +#elif defined(TARGET_UNICORE32) + do_interrupt(env); #elif defined(TARGET_SH4) do_interrupt(env); #elif defined(TARGET_ALPHA) @@ -327,6 +336,8 @@ int cpu_exec(CPUState *env1) do_interrupt(env); #elif defined(TARGET_M68K) do_interrupt(0); +#elif defined(TARGET_S390X) + do_interrupt(env); #endif env->exception_index = -1; #endif @@ -356,7 +367,7 @@ int cpu_exec(CPUState *env1) } #if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_MIPS) || \ defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \ - defined(TARGET_MICROBLAZE) + defined(TARGET_MICROBLAZE) || defined(TARGET_LM32) || defined(TARGET_UNICORE32) if (interrupt_request & CPU_INTERRUPT_HALT) { env->interrupt_request &= ~CPU_INTERRUPT_HALT; env->halted = 1; @@ -390,10 +401,10 @@ int cpu_exec(CPUState *env1) do_interrupt(EXCP12_MCHK, 0, 0, 0, 0); next_tb = 0; } else if ((interrupt_request & CPU_INTERRUPT_HARD) && - (((env->hflags2 & HF2_VINTR_MASK) && + (((env->hflags2 & HF2_VINTR_MASK) && (env->hflags2 & HF2_HIF_MASK)) || - (!(env->hflags2 & HF2_VINTR_MASK) && - (env->eflags & IF_MASK && + (!(env->hflags2 & HF2_VINTR_MASK) && + (env->eflags & IF_MASK && !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { int intno; svm_check_intercept(SVM_EXIT_INTR); @@ -411,7 +422,7 @@ int cpu_exec(CPUState *env1) next_tb = 0; #if !defined(CONFIG_USER_ONLY) } else if ((interrupt_request & CPU_INTERRUPT_VIRQ) && - (env->eflags & IF_MASK) && + (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) { int intno; /* FIXME: this should respect TPR */ @@ -436,6 +447,13 @@ int cpu_exec(CPUState *env1) env->interrupt_request &= ~CPU_INTERRUPT_HARD; next_tb = 0; } +#elif defined(TARGET_LM32) + if ((interrupt_request & CPU_INTERRUPT_HARD) + && (env->ie & IE_IE)) { + env->exception_index = EXCP_IRQ; + do_interrupt(env); + next_tb = 0; + } #elif defined(TARGET_MICROBLAZE) if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->sregs[SR_MSR] & MSR_IE) @@ -447,11 +465,7 @@ int cpu_exec(CPUState *env1) } #elif defined(TARGET_MIPS) if ((interrupt_request & CPU_INTERRUPT_HARD) && - cpu_mips_hw_interrupts_pending(env) && - (env->CP0_Status & (1 << CP0St_IE)) && - !(env->CP0_Status & (1 << CP0St_EXL)) && - !(env->CP0_Status & (1 << CP0St_ERL)) && - !(env->hflags & MIPS_HFLAG_DM)) { + cpu_mips_hw_interrupts_pending(env)) { /* Raise it */ env->exception_index = EXCP_EXT_INTERRUPT; env->error_code = 0; @@ -473,9 +487,6 @@ int cpu_exec(CPUState *env1) next_tb = 0; } } - } else if (interrupt_request & CPU_INTERRUPT_TIMER) { - //do_interrupt(0, 0, 0, 0, 0); - env->interrupt_request &= ~CPU_INTERRUPT_TIMER; } #elif defined(TARGET_ARM) if (interrupt_request & CPU_INTERRUPT_FIQ @@ -500,6 +511,12 @@ int cpu_exec(CPUState *env1) do_interrupt(env); next_tb = 0; } +#elif defined(TARGET_UNICORE32) + if (interrupt_request & CPU_INTERRUPT_HARD + && !(env->uncached_asr & ASR_I)) { + do_interrupt(env); + next_tb = 0; + } #elif defined(TARGET_SH4) if (interrupt_request & CPU_INTERRUPT_HARD) { do_interrupt(env); @@ -537,6 +554,12 @@ int cpu_exec(CPUState *env1) do_interrupt(1); next_tb = 0; } +#elif defined(TARGET_S390X) && !defined(CONFIG_USER_ONLY) + if ((interrupt_request & CPU_INTERRUPT_HARD) && + (env->psw.mask & PSW_MASK_EXT)) { + do_interrupt(env); + next_tb = 0; + } #endif /* Don't use the cached interupt_request value, do_interrupt may have updated the EXITTB flag. */ @@ -650,8 +673,10 @@ int cpu_exec(CPUState *env1) env->eflags = env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK); #elif defined(TARGET_ARM) /* XXX: Save/restore host fpu exception state?. */ +#elif defined(TARGET_UNICORE32) #elif defined(TARGET_SPARC) #elif defined(TARGET_PPC) +#elif defined(TARGET_LM32) #elif defined(TARGET_M68K) cpu_m68k_flush_flags(env, env->cc_op); env->cc_op = CC_OP_FLAGS; @@ -775,7 +800,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, if (tb) { /* the PC is inside the translated code. It means that we have a virtual CPU fault */ - cpu_restore_state(tb, env, pc, puc); + cpu_restore_state(tb, env, pc); } /* we restore the process signal mask as the sigreturn should @@ -1255,7 +1280,7 @@ int cpu_signal_handler(int host_signum, void *pinfo, break; } - return handle_cpu_signal(pc, (unsigned long)info->si_addr, + return handle_cpu_signal(pc, (unsigned long)info->si_addr, is_write, &uc->uc_sigmask, puc); } @@ -0,0 +1,629 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "config-host.h" + +#include "monitor.h" +#include "sysemu.h" +#include "gdbstub.h" +#include "dma.h" +#include "kvm.h" + +#include "cpus.h" + +static CPUState *cur_cpu; +static CPUState *next_cpu; + +/***********************************************************/ +void hw_error(const char *fmt, ...) +{ + va_list ap; + CPUState *env; + + va_start(ap, fmt); + fprintf(stderr, "qemu: hardware error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + for(env = first_cpu; env != NULL; env = env->next_cpu) { + fprintf(stderr, "CPU #%d:\n", env->cpu_index); +#ifdef TARGET_I386 + cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); +#else + cpu_dump_state(env, stderr, fprintf, 0); +#endif + } + va_end(ap); + abort(); +} + +static void do_vm_stop(int reason) +{ + if (vm_running) { + cpu_disable_ticks(); + vm_running = 0; + pause_all_vcpus(); + vm_state_notify(0, reason); + } +} + +static int cpu_can_run(CPUState *env) +{ + if (env->stop) + return 0; + if (env->stopped) + return 0; + return 1; +} + +static int cpu_has_work(CPUState *env) +{ + if (env->stop) + return 1; + if (env->stopped) + return 0; + if (!env->halted) + return 1; + if (qemu_cpu_has_work(env)) + return 1; + return 0; +} + +int tcg_has_work(void) +{ + CPUState *env; + + for (env = first_cpu; env != NULL; env = env->next_cpu) + if (cpu_has_work(env)) + return 1; + return 0; +} + +#ifndef _WIN32 +static int io_thread_fd = -1; + +#if 0 +static void qemu_event_increment(void) +{ + static const char byte = 0; + + if (io_thread_fd == -1) + return; + + write(io_thread_fd, &byte, sizeof(byte)); +} +#endif + +static void qemu_event_read(void *opaque) +{ + int fd = (unsigned long)opaque; + ssize_t len; + + /* Drain the notify pipe */ + do { + char buffer[512]; + len = read(fd, buffer, sizeof(buffer)); + } while ((len == -1 && errno == EINTR) || len > 0); +} + +static int qemu_event_init(void) +{ + int err; + int fds[2]; + + err = pipe(fds); + if (err == -1) + return -errno; + + err = fcntl_setfl(fds[0], O_NONBLOCK); + if (err < 0) + goto fail; + + err = fcntl_setfl(fds[1], O_NONBLOCK); + if (err < 0) + goto fail; + + qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL, + (void *)(unsigned long)fds[0]); + + io_thread_fd = fds[1]; + return 0; + +fail: + close(fds[0]); + close(fds[1]); + return err; +} +#else +HANDLE qemu_event_handle; + +static void dummy_event_handler(void *opaque) +{ +} + +static int qemu_event_init(void) +{ + qemu_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!qemu_event_handle) { + perror("Failed CreateEvent"); + return -1; + } + qemu_add_wait_object(qemu_event_handle, dummy_event_handler, NULL); + return 0; +} + +#if 0 +static void qemu_event_increment(void) +{ + SetEvent(qemu_event_handle); +} +#endif +#endif + +#ifndef CONFIG_IOTHREAD +int qemu_init_main_loop(void) +{ + return qemu_event_init(); +} + +void qemu_init_vcpu(void *_env) +{ + CPUState *env = _env; + + if (kvm_enabled()) + kvm_init_vcpu(env); + return; +} + +int qemu_cpu_self(void *env) +{ + return 1; +} + +void resume_all_vcpus(void) +{ +} + +void pause_all_vcpus(void) +{ +} + +void qemu_cpu_kick(void *env) +{ + return; +} + +void qemu_notify_event(void) +{ + CPUState *env = cpu_single_env; + + if (env) { + cpu_exit(env); +#ifdef USE_KQEMU + if (env->kqemu_enabled) + kqemu_cpu_interrupt(env); +#endif + } +} + +void qemu_mutex_lock_iothread(void) +{ +} + +void qemu_mutex_unlock_iothread(void) +{ +} + +void vm_stop(int reason) +{ + do_vm_stop(reason); +} + +#else /* CONFIG_IOTHREAD */ + +#include "qemu-thread.h" + +QemuMutex qemu_global_mutex; +static QemuMutex qemu_fair_mutex; + +static QemuThread io_thread; + +static QemuThread *tcg_cpu_thread; +static QemuCond *tcg_halt_cond; + +static int qemu_system_ready; +/* cpu creation */ +static QemuCond qemu_cpu_cond; +/* system init */ +static QemuCond qemu_system_cond; +static QemuCond qemu_pause_cond; + +static void block_io_signals(void); +static void unblock_io_signals(void); +static int tcg_has_work(void); + +int qemu_init_main_loop(void) +{ + int ret; + + ret = qemu_event_init(); + if (ret) + return ret; + + qemu_cond_init(&qemu_pause_cond); + qemu_mutex_init(&qemu_fair_mutex); + qemu_mutex_init(&qemu_global_mutex); + qemu_mutex_lock(&qemu_global_mutex); + + unblock_io_signals(); + qemu_thread_self(&io_thread); + + return 0; +} + +static void qemu_wait_io_event(CPUState *env) +{ + while (!tcg_has_work()) + qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); + + qemu_mutex_unlock(&qemu_global_mutex); + + /* + * Users of qemu_global_mutex can be starved, having no chance + * to acquire it since this path will get to it first. + * So use another lock to provide fairness. + */ + qemu_mutex_lock(&qemu_fair_mutex); + qemu_mutex_unlock(&qemu_fair_mutex); + + qemu_mutex_lock(&qemu_global_mutex); + if (env->stop) { + env->stop = 0; + env->stopped = 1; + qemu_cond_signal(&qemu_pause_cond); + } +} + +static int qemu_cpu_exec(CPUState *env); + +static void *kvm_cpu_thread_fn(void *arg) +{ + CPUState *env = arg; + + block_io_signals(); + qemu_thread_self(env->thread); + + /* signal CPU creation */ + qemu_mutex_lock(&qemu_global_mutex); + env->created = 1; + qemu_cond_signal(&qemu_cpu_cond); + + /* and wait for machine initialization */ + while (!qemu_system_ready) + qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); + + while (1) { + if (cpu_can_run(env)) + qemu_cpu_exec(env); + qemu_wait_io_event(env); + } + + return NULL; +} + +static void tcg_cpu_exec(void); + +static void *tcg_cpu_thread_fn(void *arg) +{ + CPUState *env = arg; + + block_io_signals(); + qemu_thread_self(env->thread); + + /* signal CPU creation */ + qemu_mutex_lock(&qemu_global_mutex); + for (env = first_cpu; env != NULL; env = env->next_cpu) + env->created = 1; + qemu_cond_signal(&qemu_cpu_cond); + + /* and wait for machine initialization */ + while (!qemu_system_ready) + qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); + + while (1) { + tcg_cpu_exec(); + qemu_wait_io_event(cur_cpu); + } + + return NULL; +} + +void qemu_cpu_kick(void *_env) +{ + CPUState *env = _env; + qemu_cond_broadcast(env->halt_cond); + if (kvm_enabled()) + qemu_thread_signal(env->thread, SIGUSR1); +} + +int qemu_cpu_self(void *env) +{ + return (cpu_single_env != NULL); +} + +static void cpu_signal(int sig) +{ + if (cpu_single_env) + cpu_exit(cpu_single_env); +} + +static void block_io_signals(void) +{ + sigset_t set; + struct sigaction sigact; + + sigemptyset(&set); + sigaddset(&set, SIGUSR2); + sigaddset(&set, SIGIO); + sigaddset(&set, SIGALRM); + pthread_sigmask(SIG_BLOCK, &set, NULL); + + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); + + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = cpu_signal; + sigaction(SIGUSR1, &sigact, NULL); +} + +static void unblock_io_signals(void) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGUSR2); + sigaddset(&set, SIGIO); + sigaddset(&set, SIGALRM); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); + + sigemptyset(&set); + sigaddset(&set, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &set, NULL); +} + +static void qemu_signal_lock(unsigned int msecs) +{ + qemu_mutex_lock(&qemu_fair_mutex); + + while (qemu_mutex_trylock(&qemu_global_mutex)) { + qemu_thread_signal(tcg_cpu_thread, SIGUSR1); + if (!qemu_mutex_timedlock(&qemu_global_mutex, msecs)) + break; + } + qemu_mutex_unlock(&qemu_fair_mutex); +} + +void qemu_mutex_lock_iothread(void) +{ + if (kvm_enabled()) { + qemu_mutex_lock(&qemu_fair_mutex); + qemu_mutex_lock(&qemu_global_mutex); + qemu_mutex_unlock(&qemu_fair_mutex); + } else + qemu_signal_lock(100); +} + +void qemu_mutex_unlock_iothread(void) +{ + qemu_mutex_unlock(&qemu_global_mutex); +} + +static int all_vcpus_paused(void) +{ + CPUState *penv = first_cpu; + + while (penv) { + if (!penv->stopped) + return 0; + penv = (CPUState *)penv->next_cpu; + } + + return 1; +} + +void pause_all_vcpus(void) +{ + CPUState *penv = first_cpu; + + while (penv) { + penv->stop = 1; + qemu_thread_signal(penv->thread, SIGUSR1); + qemu_cpu_kick(penv); + penv = (CPUState *)penv->next_cpu; + } + + while (!all_vcpus_paused()) { + qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100); + penv = first_cpu; + while (penv) { + qemu_thread_signal(penv->thread, SIGUSR1); + penv = (CPUState *)penv->next_cpu; + } + } +} + +void resume_all_vcpus(void) +{ + CPUState *penv = first_cpu; + + while (penv) { + penv->stop = 0; + penv->stopped = 0; + qemu_thread_signal(penv->thread, SIGUSR1); + qemu_cpu_kick(penv); + penv = (CPUState *)penv->next_cpu; + } +} + +static void tcg_init_vcpu(void *_env) +{ + CPUState *env = _env; + /* share a single thread for all cpus with TCG */ + if (!tcg_cpu_thread) { + env->thread = qemu_mallocz(sizeof(QemuThread)); + env->halt_cond = qemu_mallocz(sizeof(QemuCond)); + qemu_cond_init(env->halt_cond); + qemu_thread_create(env->thread, tcg_cpu_thread_fn, env); + while (env->created == 0) + qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); + tcg_cpu_thread = env->thread; + tcg_halt_cond = env->halt_cond; + } else { + env->thread = tcg_cpu_thread; + env->halt_cond = tcg_halt_cond; + } +} + +static void kvm_start_vcpu(CPUState *env) +{ +#if 0 + kvm_init_vcpu(env); + env->thread = qemu_mallocz(sizeof(QemuThread)); + env->halt_cond = qemu_mallocz(sizeof(QemuCond)); + qemu_cond_init(env->halt_cond); + qemu_thread_create(env->thread, kvm_cpu_thread_fn, env); + while (env->created == 0) + qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); +#endif +} + +void qemu_init_vcpu(void *_env) +{ + CPUState *env = _env; + + if (kvm_enabled()) + kvm_start_vcpu(env); + else + tcg_init_vcpu(env); +} + +void qemu_notify_event(void) +{ + qemu_event_increment(); +} + +void vm_stop(int reason) +{ + QemuThread me; + qemu_thread_self(&me); + + if (!qemu_thread_equal(&me, &io_thread)) { + qemu_system_vmstop_request(reason); + /* + * FIXME: should not return to device code in case + * vm_stop() has been requested. + */ + if (cpu_single_env) { + cpu_exit(cpu_single_env); + cpu_single_env->stop = 1; + } + return; + } + do_vm_stop(reason); +} + +#endif + +static int qemu_cpu_exec(CPUState *env) +{ + int ret; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif + +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif + if (use_icount) { + int64_t count; + int decr; + qemu_icount -= (env->icount_decr.u16.low + env->icount_extra); + env->icount_decr.u16.low = 0; + env->icount_extra = 0; + count = qemu_next_icount_deadline(); + count = (count + (1 << icount_time_shift) - 1) + >> icount_time_shift; + qemu_icount += count; + decr = (count > 0xffff) ? 0xffff : count; + count -= decr; + env->icount_decr.u16.low = decr; + env->icount_extra = count; + } +#ifdef CONFIG_TRACE + if (tbflush_requested) { + tbflush_requested = 0; + tb_flush(env); + return EXCP_INTERRUPT; + } +#endif + + + ret = cpu_exec(env); +#ifdef CONFIG_PROFILER + qemu_time += profile_getclock() - ti; +#endif + if (use_icount) { + /* Fold pending instructions back into the + instruction counter, and clear the interrupt flag. */ + qemu_icount -= (env->icount_decr.u16.low + + env->icount_extra); + env->icount_decr.u32 = 0; + env->icount_extra = 0; + } + return ret; +} + +void tcg_cpu_exec(void) +{ + int ret = 0; + + if (next_cpu == NULL) + next_cpu = first_cpu; + for (; next_cpu != NULL; next_cpu = next_cpu->next_cpu) { + CPUState *env = cur_cpu = next_cpu; + + if (!vm_running) + break; + if (qemu_timer_alarm_pending()) { + break; + } + if (cpu_can_run(env)) + ret = qemu_cpu_exec(env); + if (ret == EXCP_DEBUG) { + gdb_set_stop_cpu(env); + debug_requested = 1; + break; + } + } +} + @@ -0,0 +1,36 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_CPUS_H +#define QEMU_CPUS_H + +void tcg_cpu_exec(void); +void vm_state_notify(int running, int reason); +extern int tbflush_requested; +extern int debug_requested; + +void resume_all_vcpus(void); +void pause_all_vcpus(void); +int qemu_init_main_loop(void); + +#endif /* QEMU_CPUS_H */ @@ -23,6 +23,7 @@ */ #include "qemu-common.h" #include "host-utils.h" +#include <math.h> void pstrcpy(char *buf, int buf_size, const char *str) { @@ -168,30 +169,50 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len) } /* - * Copies iovecs from src to the end dst until src is completely copied or the - * total size of the copied iovec reaches size. The size of the last copied - * iovec is changed in order to fit the specified total size if it isn't a - * perfect fit already. + * Copies iovecs from src to the end of dst. It starts copying after skipping + * the given number of bytes in src and copies until src is completely copied + * or the total size of the copied iovec reaches size.The size of the last + * copied iovec is changed in order to fit the specified total size if it isn't + * a perfect fit already. */ -void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size) +void qemu_iovec_copy(QEMUIOVector *dst, QEMUIOVector *src, uint64_t skip, + size_t size) { int i; size_t done; + void *iov_base; + uint64_t iov_len; assert(dst->nalloc != -1); done = 0; for (i = 0; (i < src->niov) && (done != size); i++) { - if (done + src->iov[i].iov_len > size) { - qemu_iovec_add(dst, src->iov[i].iov_base, size - done); + if (skip >= src->iov[i].iov_len) { + /* Skip the whole iov */ + skip -= src->iov[i].iov_len; + continue; + } else { + /* Skip only part (or nothing) of the iov */ + iov_base = (uint8_t*) src->iov[i].iov_base + skip; + iov_len = src->iov[i].iov_len - skip; + skip = 0; + } + + if (done + iov_len > size) { + qemu_iovec_add(dst, iov_base, size - done); break; } else { - qemu_iovec_add(dst, src->iov[i].iov_base, src->iov[i].iov_len); + qemu_iovec_add(dst, iov_base, iov_len); } - done += src->iov[i].iov_len; + done += iov_len; } } +void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size) +{ + qemu_iovec_copy(dst, src, 0, size); +} + void qemu_iovec_destroy(QEMUIOVector *qiov) { assert(qiov->nalloc != -1); @@ -234,6 +255,49 @@ void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count) } } +void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count) +{ + size_t n; + int i; + + for (i = 0; i < qiov->niov && count; ++i) { + n = MIN(count, qiov->iov[i].iov_len); + memset(qiov->iov[i].iov_base, c, n); + count -= n; + } +} + +void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count, + size_t skip) +{ + int i; + size_t done; + void *iov_base; + uint64_t iov_len; + + done = 0; + for (i = 0; (i < qiov->niov) && (done != count); i++) { + if (skip >= qiov->iov[i].iov_len) { + /* Skip the whole iov */ + skip -= qiov->iov[i].iov_len; + continue; + } else { + /* Skip only part (or nothing) of the iov */ + iov_base = (uint8_t*) qiov->iov[i].iov_base + skip; + iov_len = qiov->iov[i].iov_len - skip; + skip = 0; + } + + if (done + iov_len > count) { + memset(iov_base, c, count - done); + break; + } else { + memset(iov_base, c, iov_len); + } + done += iov_len; + } +} + #ifndef _WIN32 /* Sets a specific flag */ int fcntl_setfl(int fd, int flag) @@ -251,3 +315,97 @@ int fcntl_setfl(int fd, int flag) } #endif +/* + * Convert string to bytes, allowing either B/b for bytes, K/k for KB, + * M/m for MB, G/g for GB or T/t for TB. Default without any postfix + * is MB. End pointer will be returned in *end, if not NULL. A valid + * value must be terminated by whitespace, ',' or '\0'. Return -1 on + * error. + */ +int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix) +{ + int64_t retval = -1; + char *endptr; + unsigned char c, d; + int mul_required = 0; + double val, mul, integral, fraction; + + errno = 0; + val = strtod(nptr, &endptr); + if (isnan(val) || endptr == nptr || errno != 0) { + goto fail; + } + fraction = modf(val, &integral); + if (fraction != 0) { + mul_required = 1; + } + /* + * Any whitespace character is fine for terminating the number, + * in addition we accept ',' to handle strings where the size is + * part of a multi token argument. + */ + c = *endptr; + d = c; + if (qemu_isspace(c) || c == '\0' || c == ',') { + c = 0; + if (default_suffix) { + d = default_suffix; + } else { + d = c; + } + } + switch (qemu_toupper(d)) { + case STRTOSZ_DEFSUFFIX_B: + mul = 1; + if (mul_required) { + goto fail; + } + break; + case STRTOSZ_DEFSUFFIX_KB: + mul = 1 << 10; + break; + case 0: + if (mul_required) { + goto fail; + } + case STRTOSZ_DEFSUFFIX_MB: + mul = 1ULL << 20; + break; + case STRTOSZ_DEFSUFFIX_GB: + mul = 1ULL << 30; + break; + case STRTOSZ_DEFSUFFIX_TB: + mul = 1ULL << 40; + break; + default: + goto fail; + } + /* + * If not terminated by whitespace, ',', or \0, increment endptr + * to point to next character, then check that we are terminated + * by an appropriate separating character, ie. whitespace, ',', or + * \0. If not, we are seeing trailing garbage, thus fail. + */ + if (c != 0) { + endptr++; + if (!qemu_isspace(*endptr) && *endptr != ',' && *endptr != 0) { + goto fail; + } + } + if ((val * mul >= INT64_MAX) || val < 0) { + goto fail; + } + retval = val * mul; + +fail: + if (end) { + *end = endptr; + } + + return retval; +} + +int64_t strtosz(const char *nptr, char **end) +{ + return strtosz_suffix(nptr, end, STRTOSZ_DEFSUFFIX_MB); +} diff --git a/dcache.c b/dcache.c deleted file mode 100644 index 7cce505..0000000 --- a/dcache.c +++ /dev/null @@ -1,334 +0,0 @@ -/* Copyright (C) 2007-2008 The Android Open Source Project -** -** This software is licensed under the terms of the GNU General Public -** License version 2, as published by the Free Software Foundation, and -** may be copied, distributed, and modified under those terms. -** -** 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. -*/ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "dcache.h" -#include "cpu.h" -#include "exec-all.h" -#include "trace.h" -#include "varint.h" - -extern FILE *ftrace_debug; - -int dcache_size = 16 * 1024; -int dcache_ways = 4; -int dcache_line_size = 32; -int dcache_replace_policy = kPolicyRandom; -int dcache_load_miss_penalty = 30; -int dcache_store_miss_penalty = 5; - -typedef struct Dcache { - int size; - int ways; - int line_size; - int log_line_size; - int rows; - uint32_t addr_mask; - int replace_policy; - int next_way; - int extra_increment_counter; - int *replace; - uint32_t **table; - int load_miss_penalty; - int store_miss_penalty; - uint64_t load_hits; - uint64_t load_misses; - uint64_t store_hits; - uint64_t store_misses; -} Dcache; - -Dcache dcache; - -void dcache_cleanup(); - -// Returns the log2 of "num" rounded up to the nearest integer. -int log2_roundup(int num) -{ - int power2; - int exp; - - for (exp = 0, power2 = 1; power2 < num; power2 <<= 1) { - exp += 1; - } - return exp; -} - -void dcache_init(int size, int ways, int line_size, int replace_policy, - int load_miss_penalty, int store_miss_penalty) -{ - int ii; - - // Compute the logs of the params, rounded up - int log_size = log2_roundup(size); - int log_ways = log2_roundup(ways); - int log_line_size = log2_roundup(line_size); - - // The number of rows in the table = size / (line_size * ways) - int log_rows = log_size - log_line_size - log_ways; - - dcache.size = 1 << log_size; - dcache.ways = 1 << log_ways; - dcache.line_size = 1 << log_line_size; - dcache.log_line_size = log_line_size; - dcache.rows = 1 << log_rows; - dcache.addr_mask = (1 << log_rows) - 1; - - // Allocate an array of pointers, one for each row - uint32_t **table = malloc(sizeof(uint32_t *) << log_rows); - - // Allocate the data for the whole cache in one call to malloc() - int data_size = sizeof(uint32_t) << (log_rows + log_ways); - uint32_t *data = malloc(data_size); - - // Fill the cache with invalid addresses - memset(data, ~0, data_size); - - // Assign the pointers into the data array - int rows = dcache.rows; - for (ii = 0; ii < rows; ++ii) { - table[ii] = &data[ii << log_ways]; - } - dcache.table = table; - dcache.replace_policy = replace_policy; - dcache.next_way = 0; - dcache.extra_increment_counter = 0; - - dcache.replace = NULL; - if (replace_policy == kPolicyRoundRobin) { - dcache.replace = malloc(sizeof(int) << log_rows); - memset(dcache.replace, 0, sizeof(int) << log_rows); - } - dcache.load_miss_penalty = load_miss_penalty; - dcache.store_miss_penalty = store_miss_penalty; - dcache.load_hits = 0; - dcache.load_misses = 0; - dcache.store_hits = 0; - dcache.store_misses = 0; - - atexit(dcache_cleanup); -} - -void dcache_stats() -{ - uint64_t hits = dcache.load_hits + dcache.store_hits; - uint64_t misses = dcache.load_misses + dcache.store_misses; - uint64_t total = hits + misses; - double hit_per = 0; - double miss_per = 0; - if (total) { - hit_per = 100.0 * hits / total; - miss_per = 100.0 * misses / total; - } - printf("\n"); - printf("Dcache hits %10llu %6.2f%%\n", hits, hit_per); - printf("Dcache misses %10llu %6.2f%%\n", misses, miss_per); - printf("Dcache total %10llu\n", hits + misses); -} - -void dcache_free() -{ - free(dcache.table[0]); - free(dcache.table); - free(dcache.replace); - dcache.table = NULL; -} - -void dcache_cleanup() -{ - dcache_stats(); - dcache_free(); -} - -void compress_trace_addresses(TraceAddr *trace_addr) -{ - AddrRec *ptr; - char *comp_ptr = trace_addr->compressed_ptr; - uint32_t prev_addr = trace_addr->prev_addr; - uint64_t prev_time = trace_addr->prev_time; - AddrRec *last = &trace_addr->buffer[kMaxNumAddrs]; - for (ptr = trace_addr->buffer; ptr != last; ++ptr) { - if (comp_ptr >= trace_addr->high_water_ptr) { - uint32_t size = comp_ptr - trace_addr->compressed; - fwrite(trace_addr->compressed, sizeof(char), size, trace_addr->fstream); - comp_ptr = trace_addr->compressed; - } - - int addr_diff = ptr->addr - prev_addr; - uint64_t time_diff = ptr->time - prev_time; - prev_addr = ptr->addr; - prev_time = ptr->time; - - comp_ptr = varint_encode_signed(addr_diff, comp_ptr); - comp_ptr = varint_encode(time_diff, comp_ptr); - } - trace_addr->compressed_ptr = comp_ptr; - trace_addr->prev_addr = prev_addr; - trace_addr->prev_time = prev_time; -} - -// This function is called by the generated code to simulate -// a dcache load access. -void dcache_load(uint32_t addr) -{ - int ii; - int ways = dcache.ways; - uint32_t cache_addr = addr >> dcache.log_line_size; - int row = cache_addr & dcache.addr_mask; - //printf("ld %lld 0x%x\n", sim_time, addr); - for (ii = 0; ii < ways; ++ii) { - if (cache_addr == dcache.table[row][ii]) { - dcache.load_hits += 1; -#if 0 - printf("dcache load hit addr: 0x%x cache_addr: 0x%x row %d way %d\n", - addr, cache_addr, row, ii); -#endif - // If we are tracing all addresses, then include this in the trace. - if (trace_all_addr) { - AddrRec *next = trace_load.next; - next->addr = addr; - next->time = sim_time; - next += 1; - if (next == &trace_load.buffer[kMaxNumAddrs]) { - // Compress the trace - compress_trace_addresses(&trace_load); - next = &trace_load.buffer[0]; - } - trace_load.next = next; - } - return; - } - } - // This is a cache miss - -#if 0 - if (ftrace_debug) - fprintf(ftrace_debug, "t%lld %08x\n", sim_time, addr); -#endif - if (trace_load.fstream) { - AddrRec *next = trace_load.next; - next->addr = addr; - next->time = sim_time; - next += 1; - if (next == &trace_load.buffer[kMaxNumAddrs]) { - // Compress the trace - compress_trace_addresses(&trace_load); - next = &trace_load.buffer[0]; - } - trace_load.next = next; - } - - dcache.load_misses += 1; - sim_time += dcache.load_miss_penalty; - - // Pick a way to replace - int way; - if (dcache.replace_policy == kPolicyRoundRobin) { - // Round robin replacement policy - way = dcache.replace[row]; - int next_way = way + 1; - if (next_way == dcache.ways) - next_way = 0; - dcache.replace[row] = next_way; - } else { - // Random replacement policy - way = dcache.next_way; - dcache.next_way += 1; - if (dcache.next_way >= dcache.ways) - dcache.next_way = 0; - - // Every 13 replacements, add an extra increment to the next way - dcache.extra_increment_counter += 1; - if (dcache.extra_increment_counter == 13) { - dcache.extra_increment_counter = 0; - dcache.next_way += 1; - if (dcache.next_way >= dcache.ways) - dcache.next_way = 0; - } - } -#if 0 - printf("dcache load miss addr: 0x%x cache_addr: 0x%x row %d replacing way %d\n", - addr, cache_addr, row, way); -#endif - dcache.table[row][way] = cache_addr; -} - -// This function is called by the generated code to simulate -// a dcache store access. -void dcache_store(uint32_t addr, uint32_t val) -{ - //printf("st %lld 0x%08x val 0x%x\n", sim_time, addr, val); - - int ii; - int ways = dcache.ways; - uint32_t cache_addr = addr >> dcache.log_line_size; - int row = cache_addr & dcache.addr_mask; - for (ii = 0; ii < ways; ++ii) { - if (cache_addr == dcache.table[row][ii]) { - dcache.store_hits += 1; -#if 0 - printf("dcache store hit addr: 0x%x cache_addr: 0x%x row %d way %d\n", - addr, cache_addr, row, ii); -#endif - // If we are tracing all addresses, then include this in the trace. - if (trace_all_addr) { - AddrRec *next = trace_store.next; - next->addr = addr; - next->time = sim_time; - next += 1; - if (next == &trace_store.buffer[kMaxNumAddrs]) { - // Compress the trace - compress_trace_addresses(&trace_store); - next = &trace_store.buffer[0]; - } - trace_store.next = next; - } - return; - } - } - // This is a cache miss -#if 0 - printf("dcache store miss addr: 0x%x cache_addr: 0x%x row %d\n", - addr, cache_addr, row); -#endif - -#if 0 - if (ftrace_debug) - fprintf(ftrace_debug, "t%lld %08x\n", sim_time, addr); -#endif - - if (trace_store.fstream) { - AddrRec *next = trace_store.next; - next->addr = addr; - next->time = sim_time; - next += 1; - if (next == &trace_store.buffer[kMaxNumAddrs]) { - // Compress the trace - compress_trace_addresses(&trace_store); - next = &trace_store.buffer[0]; - } - trace_store.next = next; - } - - dcache.store_misses += 1; - sim_time += dcache.store_miss_penalty; - - // Assume no write-allocate for now -} - -// This function is called by the generated code to simulate -// a dcache load and store (swp) access. -void dcache_swp(uint32_t addr) -{ - dcache_load(addr); - dcache_store(addr, 0); -} diff --git a/dcache.h b/dcache.h deleted file mode 100644 index 8857600..0000000 --- a/dcache.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (C) 2007-2008 The Android Open Source Project -** -** This software is licensed under the terms of the GNU General Public -** License version 2, as published by the Free Software Foundation, and -** may be copied, distributed, and modified under those terms. -** -** 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. -*/ -#ifndef DCACHE_H -#define DCACHE_H - -#include <inttypes.h> - -// Define constants for the replacement policies -#define kPolicyRoundRobin 1 -#define kPolicyRandom 2 - -extern int dcache_size; -extern int dcache_ways; -extern int dcache_line_size; -extern int dcache_replace_policy; -extern int dcache_load_miss_penalty; -extern int dcache_store_miss_penalty; - -extern void dcache_init(int size, int ways, int line_size, int replace_policy, - int load_miss_penalty, int store_miss_penalty); - -#endif /* DCACHE_H */ diff --git a/def-helper.h b/def-helper.h index 8a88c5b..8a822c7 100644 --- a/def-helper.h +++ b/def-helper.h @@ -81,9 +81,29 @@ #define dh_is_64bit_ptr (TCG_TARGET_REG_BITS == 64) #define dh_is_64bit(t) glue(dh_is_64bit_, dh_alias(t)) +#define dh_is_signed_void 0 +#define dh_is_signed_i32 0 +#define dh_is_signed_s32 1 +#define dh_is_signed_i64 0 +#define dh_is_signed_s64 1 +#define dh_is_signed_f32 0 +#define dh_is_signed_f64 0 +#define dh_is_signed_tl 0 +#define dh_is_signed_int 1 +/* ??? This is highly specific to the host cpu. There are even special + extension instructions that may be required, e.g. ia64's addp4. But + for now we don't support any 64-bit targets with 32-bit pointers. */ +#define dh_is_signed_ptr 0 +#define dh_is_signed_env dh_is_signed_ptr +#define dh_is_signed(t) dh_is_signed_##t + +#define dh_sizemask(t, n) \ + sizemask |= dh_is_64bit(t) << (n*2); \ + sizemask |= dh_is_signed(t) << (n*2+1) + #define dh_arg(t, n) \ args[n - 1] = glue(GET_TCGV_, dh_alias(t))(glue(arg, n)); \ - sizemask |= dh_is_64bit(t) << n + dh_sizemask(t, n) #define dh_arg_decl(t, n) glue(TCGv_, dh_alias(t)) glue(arg, n) @@ -138,8 +158,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) dh_arg_decl(t1, 1)) \ { \ TCGArg args[1]; \ - int sizemask; \ - sizemask = dh_is_64bit(ret); \ + int sizemask = 0; \ + dh_sizemask(ret, 0); \ dh_arg(t1, 1); \ tcg_gen_helperN(HELPER(name), flags, sizemask, dh_retvar(ret), 1, args); \ } @@ -149,8 +169,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) dh_arg_decl(t1, 1 dh_arg_decl(t2, 2)) \ { \ TCGArg args[2]; \ - int sizemask; \ - sizemask = dh_is_64bit(ret); \ + int sizemask = 0; \ + dh_sizemask(ret, 0); \ dh_arg(t1, 1); \ dh_arg(t2, 2); \ tcg_gen_helperN(HELPER(name), flags, sizemask, dh_retvar(ret), 2, args); \ @@ -161,8 +181,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) dh_arg_decl(t1, 1 dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \ { \ TCGArg args[3]; \ - int sizemask; \ - sizemask = dh_is_64bit(ret); \ + int sizemask = 0; \ + dh_sizemask(ret, 0); \ dh_arg(t1, 1); \ dh_arg(t2, 2); \ dh_arg(t3, 3); \ @@ -174,8 +194,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) dh_arg_decl(t1, 1 dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \ { \ TCGArg args[4]; \ - int sizemask; \ - sizemask = dh_is_64bit(ret); \ + int sizemask = 0; \ + dh_sizemask(ret, 0); \ dh_arg(t1, 1); \ dh_arg(t2, 2); \ dh_arg(t3, 3); \ diff --git a/device_tree.c b/device_tree.c index 426a631..f5d5eb1 100644 --- a/device_tree.c +++ b/device_tree.c @@ -20,7 +20,6 @@ #include "config.h" #include "qemu-common.h" -#include "sysemu.h" #include "device_tree.h" #include "hw/loader.h" @@ -74,7 +73,7 @@ fail: } int qemu_devtree_setprop(void *fdt, const char *node_path, - const char *property, uint32_t *val_array, int size) + const char *property, void *val_array, int size) { int offset; diff --git a/device_tree.h b/device_tree.h index f05c4e7..cecd98f 100644 --- a/device_tree.h +++ b/device_tree.h @@ -17,7 +17,7 @@ void *load_device_tree(const char *filename_path, int *sizep); int qemu_devtree_setprop(void *fdt, const char *node_path, - const char *property, uint32_t *val_array, int size); + const char *property, void *val_array, int size); int qemu_devtree_setprop_cell(void *fdt, const char *node_path, const char *property, uint32_t val); int qemu_devtree_setprop_string(void *fdt, const char *node_path, @@ -9,11 +9,7 @@ #ifndef DIS_ASM_H #define DIS_ASM_H -#include <stdlib.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> -#include <inttypes.h> +#include "qemu-common.h" typedef void *PTR; typedef uint64_t bfd_vma; @@ -237,8 +233,6 @@ typedef struct symbol_cache_entry } udata; } asymbol; -typedef int (*fprintf_ftype) (FILE*, const char*, ...); - enum dis_insn_type { dis_noninsn, /* Not a valid instruction */ dis_nonbranch, /* Not a branch instruction */ @@ -261,7 +255,7 @@ enum dis_insn_type { by hand, or using one of the initialization macros below. */ typedef struct disassemble_info { - fprintf_ftype fprintf_func; + fprintf_function fprintf_func; FILE *stream; PTR application_data; @@ -368,47 +362,48 @@ typedef struct disassemble_info { target address. Return number of bytes processed. */ typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *); -extern int print_insn_big_mips (bfd_vma, disassemble_info*); -extern int print_insn_little_mips (bfd_vma, disassemble_info*); -extern int print_insn_i386 (bfd_vma, disassemble_info*); -extern int print_insn_m68k (bfd_vma, disassemble_info*); -extern int print_insn_z8001 (bfd_vma, disassemble_info*); -extern int print_insn_z8002 (bfd_vma, disassemble_info*); -extern int print_insn_h8300 (bfd_vma, disassemble_info*); -extern int print_insn_h8300h (bfd_vma, disassemble_info*); -extern int print_insn_h8300s (bfd_vma, disassemble_info*); -extern int print_insn_h8500 (bfd_vma, disassemble_info*); -extern int print_insn_alpha (bfd_vma, disassemble_info*); -extern disassembler_ftype arc_get_disassembler (int, int); -extern int print_insn_arm (bfd_vma, disassemble_info*); -extern int print_insn_sparc (bfd_vma, disassemble_info*); -extern int print_insn_big_a29k (bfd_vma, disassemble_info*); -extern int print_insn_little_a29k (bfd_vma, disassemble_info*); -extern int print_insn_i960 (bfd_vma, disassemble_info*); -extern int print_insn_sh (bfd_vma, disassemble_info*); -extern int print_insn_shl (bfd_vma, disassemble_info*); -extern int print_insn_hppa (bfd_vma, disassemble_info*); -extern int print_insn_m32r (bfd_vma, disassemble_info*); -extern int print_insn_m88k (bfd_vma, disassemble_info*); -extern int print_insn_mn10200 (bfd_vma, disassemble_info*); -extern int print_insn_mn10300 (bfd_vma, disassemble_info*); -extern int print_insn_ns32k (bfd_vma, disassemble_info*); -extern int print_insn_big_powerpc (bfd_vma, disassemble_info*); -extern int print_insn_little_powerpc (bfd_vma, disassemble_info*); -extern int print_insn_rs6000 (bfd_vma, disassemble_info*); -extern int print_insn_w65 (bfd_vma, disassemble_info*); -extern int print_insn_d10v (bfd_vma, disassemble_info*); -extern int print_insn_v850 (bfd_vma, disassemble_info*); -extern int print_insn_tic30 (bfd_vma, disassemble_info*); -extern int print_insn_ppc (bfd_vma, disassemble_info*); -extern int print_insn_s390 (bfd_vma, disassemble_info*); -extern int print_insn_crisv32 (bfd_vma, disassemble_info*); -extern int print_insn_microblaze (bfd_vma, disassemble_info*); -extern int print_insn_ia64 (bfd_vma, disassemble_info*); +int print_insn_big_mips (bfd_vma, disassemble_info*); +int print_insn_little_mips (bfd_vma, disassemble_info*); +int print_insn_i386 (bfd_vma, disassemble_info*); +int print_insn_m68k (bfd_vma, disassemble_info*); +int print_insn_z8001 (bfd_vma, disassemble_info*); +int print_insn_z8002 (bfd_vma, disassemble_info*); +int print_insn_h8300 (bfd_vma, disassemble_info*); +int print_insn_h8300h (bfd_vma, disassemble_info*); +int print_insn_h8300s (bfd_vma, disassemble_info*); +int print_insn_h8500 (bfd_vma, disassemble_info*); +int print_insn_alpha (bfd_vma, disassemble_info*); +disassembler_ftype arc_get_disassembler (int, int); +int print_insn_arm (bfd_vma, disassemble_info*); +int print_insn_sparc (bfd_vma, disassemble_info*); +int print_insn_big_a29k (bfd_vma, disassemble_info*); +int print_insn_little_a29k (bfd_vma, disassemble_info*); +int print_insn_i960 (bfd_vma, disassemble_info*); +int print_insn_sh (bfd_vma, disassemble_info*); +int print_insn_shl (bfd_vma, disassemble_info*); +int print_insn_hppa (bfd_vma, disassemble_info*); +int print_insn_m32r (bfd_vma, disassemble_info*); +int print_insn_m88k (bfd_vma, disassemble_info*); +int print_insn_mn10200 (bfd_vma, disassemble_info*); +int print_insn_mn10300 (bfd_vma, disassemble_info*); +int print_insn_ns32k (bfd_vma, disassemble_info*); +int print_insn_big_powerpc (bfd_vma, disassemble_info*); +int print_insn_little_powerpc (bfd_vma, disassemble_info*); +int print_insn_rs6000 (bfd_vma, disassemble_info*); +int print_insn_w65 (bfd_vma, disassemble_info*); +int print_insn_d10v (bfd_vma, disassemble_info*); +int print_insn_v850 (bfd_vma, disassemble_info*); +int print_insn_tic30 (bfd_vma, disassemble_info*); +int print_insn_ppc (bfd_vma, disassemble_info*); +int print_insn_s390 (bfd_vma, disassemble_info*); +int print_insn_crisv32 (bfd_vma, disassemble_info*); +int print_insn_crisv10 (bfd_vma, disassemble_info*); +int print_insn_microblaze (bfd_vma, disassemble_info*); +int print_insn_ia64 (bfd_vma, disassemble_info*); #if 0 /* Fetch the disassembler for a given BFD, if that support is available. */ -extern disassembler_ftype disassembler (bfd *); +disassembler_ftype disassembler(bfd *); #endif @@ -417,21 +412,20 @@ extern disassembler_ftype disassembler (bfd *); /* Here is a function which callers may wish to use for read_memory_func. It gets bytes from a buffer. */ -extern int buffer_read_memory - (bfd_vma, bfd_byte *, int, struct disassemble_info *); +int buffer_read_memory(bfd_vma, bfd_byte *, int, struct disassemble_info *); /* This function goes with buffer_read_memory. It prints a message using info->fprintf_func and info->stream. */ -extern void perror_memory (int, bfd_vma, struct disassemble_info *); +void perror_memory(int, bfd_vma, struct disassemble_info *); /* Just print the address in hex. This is included for completeness even though both GDB and objdump provide their own (to print symbolic addresses). */ -extern void generic_print_address (bfd_vma, struct disassemble_info *); +void generic_print_address(bfd_vma, struct disassemble_info *); /* Always true. */ -extern int generic_symbol_at_address (bfd_vma, struct disassemble_info *); +int generic_symbol_at_address(bfd_vma, struct disassemble_info *); /* Macro to initialize a disassemble_info struct. This should be called by all applications creating such a struct. */ @@ -8,10 +8,6 @@ #include "exec-all.h" #include "disas.h" -#ifdef TARGET_I386 -#include "kvm.h" -#endif - /* Filled in by elfload.c. Simplistic, but will do for now. */ struct syminfo *syminfos = NULL; @@ -37,10 +33,6 @@ target_read_memory (bfd_vma memaddr, int length, struct disassemble_info *info) { -#ifdef TARGET_I386 - if (kvm_enabled()) - cpu_synchronize_state(cpu_single_env, 0); -#endif cpu_memory_rw_debug(cpu_single_env, memaddr, myaddr, length, 0); return 0; } @@ -216,8 +208,16 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags) disasm_info.mach = bfd_mach_alpha; print_insn = print_insn_alpha; #elif defined(TARGET_CRIS) + if (flags != 32) { + disasm_info.mach = bfd_mach_cris_v0_v10; + print_insn = print_insn_crisv10; + } else { disasm_info.mach = bfd_mach_cris_v32; print_insn = print_insn_crisv32; + } +#elif defined(TARGET_S390X) + disasm_info.mach = bfd_mach_s390_64; + print_insn = print_insn_s390; #elif defined(TARGET_MICROBLAZE) disasm_info.mach = bfd_arch_microblaze; print_insn = print_insn_microblaze; @@ -310,11 +310,6 @@ void disas(FILE *out, void *code, unsigned long size) #endif for (pc = (unsigned long)code; size > 0; pc += count, size -= count) { fprintf(out, "0x%08lx: ", pc); -#ifdef __arm__ - /* since data is included in the code, it is better to - display code data too */ - fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc)); -#endif count = print_insn(pc, &disasm_info); fprintf(out, "\n"); if (count < 0) @@ -350,14 +345,15 @@ monitor_read_memory (bfd_vma memaddr, bfd_byte *myaddr, int length, struct disassemble_info *info) { if (monitor_disas_is_physical) { - cpu_physical_memory_rw(memaddr, myaddr, length, 0); + cpu_physical_memory_read(memaddr, myaddr, length); } else { cpu_memory_rw_debug(monitor_disas_env, memaddr,myaddr, length, 0); } return 0; } -static int monitor_fprintf(FILE *stream, const char *fmt, ...) +static int GCC_FMT_ATTR(2, 3) +monitor_fprintf(FILE *stream, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -421,6 +417,9 @@ void monitor_disas(Monitor *mon, CPUState *env, #elif defined(TARGET_SH4) disasm_info.mach = bfd_mach_sh4; print_insn = print_insn_sh; +#elif defined(TARGET_S390X) + disasm_info.mach = bfd_mach_s390_64; + print_insn = print_insn_s390; #else monitor_printf(mon, "0x" TARGET_FMT_lx ": Asm output not supported on this arch\n", pc); @@ -8,11 +8,8 @@ void disas(FILE *out, void *code, unsigned long size); void target_disas(FILE *out, target_ulong code, target_ulong size, int flags); -/* The usual mess... FIXME: Remove this condition once dyngen-exec.h is gone */ -#ifndef __DYNGEN_EXEC_H__ void monitor_disas(Monitor *mon, CPUState *env, target_ulong pc, int nb_insn, int is_physical, int flags); -#endif /* Look up symbol for debugging purpose. Returns "" if unknown. */ const char *lookup_symbol(target_ulong orig_addr); diff --git a/dyngen-exec.h b/dyngen-exec.h index 283c286..54fe3d6 100644 --- a/dyngen-exec.h +++ b/dyngen-exec.h @@ -19,19 +19,7 @@ #if !defined(__DYNGEN_EXEC_H__) #define __DYNGEN_EXEC_H__ -/* prevent Solaris from trying to typedef FILE in gcc's - include/floatingpoint.h which will conflict with the - definition down below */ -#ifdef __sun__ -#define _FILEDEFED -#endif - -/* NOTE: standard headers should be used with special care at this - point because host CPU registers are used as global variables. Some - host headers do not allow that. */ -#include <stddef.h> -#include <stdint.h> -#include <stdbool.h> +#include "qemu-common.h" #ifdef __OpenBSD__ #include <sys/types.h> @@ -40,15 +28,6 @@ /* XXX: This may be wrong for 64-bit ILP32 hosts. */ typedef void * host_reg_t; -#ifdef CONFIG_BSD -typedef struct __sFILE FILE; -#else -typedef struct FILE FILE; -#endif -extern int fprintf(FILE *, const char *, ...); -extern int fputs(const char *, FILE *); -extern int printf(const char *, ...); - #if defined(__i386__) #define AREG0 "ebp" #define AREG1 "ebx" @@ -104,6 +104,9 @@ typedef int64_t Elf64_Sxword; #define EM_H8_300H 47 /* Hitachi H8/300H */ #define EM_H8S 48 /* Hitachi H8S */ +#define EM_LATTICEMICO32 138 /* LatticeMico32 */ + +#define EM_UNICORE32 110 /* UniCore32 */ /* * This is an interim value that we will use until the committee comes @@ -1191,6 +1194,25 @@ typedef struct elf64_note { Elf64_Word n_type; /* Content type */ } Elf64_Nhdr; + +/* This data structure represents a PT_LOAD segment. */ +struct elf32_fdpic_loadseg { + /* Core address to which the segment is mapped. */ + Elf32_Addr addr; + /* VMA recorded in the program header. */ + Elf32_Addr p_vaddr; + /* Size of this segment in memory. */ + Elf32_Word p_memsz; +}; +struct elf32_fdpic_loadmap { + /* Protocol version number, must be zero. */ + Elf32_Half version; + /* Number of segments in this map. */ + Elf32_Half nsegs; + /* The actual memory map. */ + struct elf32_fdpic_loadseg segs[/*nsegs*/]; +}; + #ifdef ELF_CLASS #if ELF_CLASS == ELFCLASS32 @@ -7,13 +7,13 @@ extern "C" { typedef struct envlist envlist_t; -extern envlist_t *envlist_create(void); -extern void envlist_free(envlist_t *); -extern int envlist_setenv(envlist_t *, const char *); -extern int envlist_unsetenv(envlist_t *, const char *); -extern int envlist_parse_set(envlist_t *, const char *); -extern int envlist_parse_unset(envlist_t *, const char *); -extern char **envlist_to_environ(const envlist_t *, size_t *); +envlist_t *envlist_create(void); +void envlist_free(envlist_t *); +int envlist_setenv(envlist_t *, const char *); +int envlist_unsetenv(envlist_t *, const char *); +int envlist_parse_set(envlist_t *, const char *); +int envlist_parse_unset(envlist_t *, const char *); +char **envlist_to_environ(const envlist_t *, size_t *); #ifdef __cplusplus } @@ -25,6 +25,15 @@ /* allow to see translation results - the slowdown should be negligible, so we leave it */ #define DEBUG_DISAS +/* Page tracking code uses ram addresses in system mode, and virtual + addresses in userspace mode. Define tb_page_addr_t to be an appropriate + type. */ +#if defined(CONFIG_USER_ONLY) +typedef abi_ulong tb_page_addr_t; +#else +typedef ram_addr_t tb_page_addr_t; +#endif + /* is_jmp field values */ #define DISAS_NEXT 0 /* next instruction can be analyzed */ #define DISAS_JUMP 1 /* only pc was modified dynamically */ @@ -60,19 +69,14 @@ extern uint32_t gen_opc_hflags[OPC_BUF_SIZE]; void gen_intermediate_code(CPUState *env, struct TranslationBlock *tb); void gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb); -void gen_pc_load(CPUState *env, struct TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc); +void restore_state_to_opc(CPUState *env, struct TranslationBlock *tb, int pc_pos); unsigned long code_gen_max_block_size(void); void cpu_gen_init(void); int cpu_gen_code(CPUState *env, struct TranslationBlock *tb, int *gen_code_size_ptr); int cpu_restore_state(struct TranslationBlock *tb, - CPUState *env, unsigned long searched_pc, - void *puc); -int cpu_restore_state_copy(struct TranslationBlock *tb, - CPUState *env, unsigned long searched_pc, - void *puc); + CPUState *env, unsigned long searched_pc); void cpu_resume_from_signal(CPUState *env1, void *puc); void cpu_io_recompile(CPUState *env, void *retaddr); TranslationBlock *tb_gen_code(CPUState *env, @@ -187,7 +191,7 @@ static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc) static inline unsigned int tb_phys_hash_func(unsigned long pc) { - return pc & (CODE_GEN_PHYS_HASH_SIZE - 1); + return (pc >> 2) & (CODE_GEN_PHYS_HASH_SIZE - 1); } #ifdef CONFIG_MEMCHECK @@ -403,60 +407,9 @@ static inline target_ulong get_phys_addr_code(CPUState *env1, target_ulong addr) } p = (void *)(unsigned long)addr + env1->tlb_table[mmu_idx][page_index].addend; - return qemu_ram_addr_from_host(p); -} - -#if 0 -/* Deterministic execution requires that IO only be performed on the last - instruction of a TB so that interrupts take effect immediately. */ -static inline int can_do_io(CPUState *env) -{ - if (!use_icount) - return 1; - - /* If not executing code then assume we are ok. */ - if (!env->current_tb) - return 1; - - return env->can_do_io != 0; + return qemu_ram_addr_from_host_nofail(p); } #endif -#endif /* 0 */ - -#ifdef CONFIG_KQEMU -#define KQEMU_MODIFY_PAGE_MASK (0xff & ~(VGA_DIRTY_FLAG | CODE_DIRTY_FLAG)) - -#define MSR_QPI_COMMBASE 0xfabe0010 - -int kqemu_init(CPUState *env); -int kqemu_cpu_exec(CPUState *env); -void kqemu_flush_page(CPUState *env, target_ulong addr); -void kqemu_flush(CPUState *env, int global); -void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr); -void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr); -void kqemu_set_phys_mem(uint64_t start_addr, ram_addr_t size, - ram_addr_t phys_offset); -void kqemu_cpu_interrupt(CPUState *env); -void kqemu_record_dump(void); - -extern uint32_t kqemu_comm_base; - -extern ram_addr_t kqemu_phys_ram_size; -extern uint8_t *kqemu_phys_ram_base; - -static inline int kqemu_is_ok(CPUState *env) -{ - return(env->kqemu_enabled && - (env->cr[0] & CR0_PE_MASK) && - !(env->hflags & HF_INHIBIT_IRQ_MASK) && - (env->eflags & IF_MASK) && - !(env->eflags & VM_MASK) && - (env->kqemu_enabled == 2 || - ((env->hflags & HF_CPL_MASK) == 3 && - (env->eflags & IOPL_MASK) != IOPL_MASK))); -} - -#endif typedef void (CPUDebugExcpHandler)(CPUState *env); @@ -115,21 +115,9 @@ uint8_t *code_gen_ptr; #if !defined(CONFIG_USER_ONLY) int phys_ram_fd; -uint8_t *phys_ram_dirty; static int in_migration; -typedef struct RAMBlock { - uint8_t *host; - ram_addr_t offset; - ram_addr_t length; - struct RAMBlock *next; -} RAMBlock; - -static RAMBlock *ram_blocks; -/* TODO: When we implement (and use) ram deallocation (e.g. for hotplug) - then we can no longer assume contiguous ram offsets, and external uses - of this variable will break. */ -ram_addr_t last_ram_offset; +RAMList ram_list = { .blocks = QLIST_HEAD_INITIALIZER(ram_list) }; #endif CPUState *first_cpu; @@ -226,21 +214,21 @@ static void map_exec(void *addr, long size) DWORD old_protect; VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &old_protect); - + } #else static void map_exec(void *addr, long size) { unsigned long start, end, page_size; - + page_size = getpagesize(); start = (unsigned long)addr; start &= ~(page_size - 1); - + end = (unsigned long)addr + size; end += page_size - 1; end &= ~(page_size - 1); - + mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC); } @@ -440,7 +428,7 @@ static void code_gen_alloc(unsigned long tb_size) code_gen_buffer_size = MIN_CODE_GEN_BUFFER_SIZE; /* The code gen buffer location may have constraints depending on the host cpu and OS */ -#if defined(__linux__) +#if defined(__linux__) { int flags; void *start = NULL; @@ -479,7 +467,8 @@ static void code_gen_alloc(unsigned long tb_size) exit(1); } } -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \ + || defined(__DragonFly__) || defined(__OpenBSD__) { int flags; void *addr = NULL; @@ -492,9 +481,16 @@ static void code_gen_alloc(unsigned long tb_size) /* Cannot map more than that */ if (code_gen_buffer_size > (800 * 1024 * 1024)) code_gen_buffer_size = (800 * 1024 * 1024); +#elif defined(__sparc_v9__) + // Map the buffer below 2G, so we can use direct calls and branches + flags |= MAP_FIXED; + addr = (void *) 0x60000000UL; + if (code_gen_buffer_size > (512 * 1024 * 1024)) { + code_gen_buffer_size = (512 * 1024 * 1024); + } #endif code_gen_buffer = mmap(addr, code_gen_buffer_size, - PROT_WRITE | PROT_READ | PROT_EXEC, + PROT_WRITE | PROT_READ | PROT_EXEC, flags, -1, 0); if (code_gen_buffer == MAP_FAILED) { fprintf(stderr, "Could not allocate dynamic translator buffer\n"); @@ -525,6 +521,11 @@ void cpu_exec_init_all(unsigned long tb_size) #if !defined(CONFIG_USER_ONLY) io_mem_init(); #endif +#if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE) + /* There's no guest base to take into account, so go ahead and + initialize the prologue now. */ + tcg_prologue_init(&tcg_ctx); +#endif } #if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) @@ -1005,8 +1006,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t restore the CPU state */ current_tb_modified = 1; - cpu_restore_state(current_tb, env, - env->mem_io_pc, NULL); + cpu_restore_state(current_tb, env, env->mem_io_pc); cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, ¤t_flags); } @@ -1114,7 +1114,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr, restore the CPU state */ current_tb_modified = 1; - cpu_restore_state(current_tb, env, pc, puc); + cpu_restore_state(current_tb, env, pc); cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, ¤t_flags); } @@ -1565,14 +1565,15 @@ static void cpu_unlink_tb(CPUState *env) TranslationBlock *tb; static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED; + spin_lock(&interrupt_lock); tb = env->current_tb; /* if the cpu is currently executing code, we must unlink it and all the potentially executing TB */ - if (tb && !testandset(&interrupt_lock)) { + if (tb) { env->current_tb = NULL; tb_reset_jump_recursive(tb); - resetlock(&interrupt_lock); } + spin_unlock(&interrupt_lock); } /* mask must never be zero, except for A20 change call */ @@ -1773,11 +1774,11 @@ static inline void tlb_flush_jmp_cache(CPUState *env, target_ulong addr) /* Discard jump cache entries for any tb which might potentially overlap the flushed page. */ i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE); - memset (&env->tb_jmp_cache[i], 0, + memset (&env->tb_jmp_cache[i], 0, TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); i = tb_jmp_cache_hash_page(addr); - memset (&env->tb_jmp_cache[i], 0, + memset (&env->tb_jmp_cache[i], 0, TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); } @@ -1861,7 +1862,7 @@ static void tlb_protect_code(ram_addr_t ram_addr) static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr, target_ulong vaddr) { - phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] |= CODE_DIRTY_FLAG; + cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG); } static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, @@ -1882,8 +1883,7 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, { CPUState *env; unsigned long length, start1; - int i, mask, len; - uint8_t *p; + int i; start &= TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); @@ -1891,18 +1891,14 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, length = end - start; if (length == 0) return; - len = length >> TARGET_PAGE_BITS; - mask = ~dirty_flags; - p = phys_ram_dirty + (start >> TARGET_PAGE_BITS); - for(i = 0; i < len; i++) - p[i] &= mask; + cpu_physical_memory_mask_dirty_range(start, length, dirty_flags); /* we modify the TLB cache so that the dirty bit will be set again when accessing the range */ - start1 = (unsigned long)qemu_get_ram_ptr(start); + start1 = (unsigned long)qemu_safe_ram_ptr(start); /* Chek that we don't span multiple blocks - this breaks the address comparisons below. */ - if ((unsigned long)qemu_get_ram_ptr(end - 1) - start1 + if ((unsigned long)qemu_safe_ram_ptr(end - 1) - start1 != (end - 1) - start) { abort(); } @@ -1949,7 +1945,7 @@ static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry) if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) { p = (void *)(unsigned long)((tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend); - ram_addr = qemu_ram_addr_from_host(p); + ram_addr = qemu_ram_addr_from_host_nofail(p); if (!cpu_physical_memory_is_dirty(ram_addr)) { tlb_entry->addr_write |= TLB_NOTDIRTY; } @@ -2369,16 +2365,17 @@ static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys, start_addr and region_offset are rounded down to a page boundary before calculating this offset. This should not be a problem unless the low bits of start_addr and region_offset differ. */ -void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, +void cpu_register_physical_memory_log(target_phys_addr_t start_addr, ram_addr_t size, ram_addr_t phys_offset, - ram_addr_t region_offset) + ram_addr_t region_offset, + bool log_dirty) { target_phys_addr_t addr, end_addr; PhysPageDesc *p; CPUState *env; ram_addr_t orig_size = size; - void *subpage; + subpage_t *subpage; if (kvm_enabled()) kvm_set_phys_mem(start_addr, size, phys_offset); @@ -2389,7 +2386,9 @@ void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, region_offset &= TARGET_PAGE_MASK; size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; end_addr = start_addr + (target_phys_addr_t)size; - for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) { + + addr = start_addr; + do { p = phys_page_find(addr >> TARGET_PAGE_BITS); if (p && p->phys_offset != IO_MEM_UNASSIGNED) { ram_addr_t orig_memory = p->phys_offset; @@ -2398,7 +2397,7 @@ void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, need_subpage); - if (need_subpage || phys_offset & IO_MEM_SUBWIDTH) { + if (need_subpage) { if (!(orig_memory & IO_MEM_SUBPAGE)) { subpage = subpage_init((addr & TARGET_PAGE_MASK), &p->phys_offset, orig_memory, @@ -2430,7 +2429,7 @@ void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, need_subpage); - if (need_subpage || phys_offset & IO_MEM_SUBWIDTH) { + if (need_subpage) { subpage = subpage_init((addr & TARGET_PAGE_MASK), &p->phys_offset, IO_MEM_UNASSIGNED, addr & TARGET_PAGE_MASK); @@ -2441,7 +2440,8 @@ void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, } } region_offset += TARGET_PAGE_SIZE; - } + addr += TARGET_PAGE_SIZE; + } while (addr != end_addr); /* since each CPU stores ram addresses in its TLB cache, we must reset the modified entries */ @@ -2474,46 +2474,216 @@ void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size) kvm_uncoalesce_mmio_region(addr, size); } -ram_addr_t qemu_ram_alloc(ram_addr_t size) +static ram_addr_t find_ram_offset(ram_addr_t size) +{ + RAMBlock *block, *next_block; + ram_addr_t offset = 0, mingap = ULONG_MAX; + + if (QLIST_EMPTY(&ram_list.blocks)) + return 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + ram_addr_t end, next = ULONG_MAX; + + end = block->offset + block->length; + + QLIST_FOREACH(next_block, &ram_list.blocks, next) { + if (next_block->offset >= end) { + next = MIN(next, next_block->offset); + } + } + if (next - end >= size && next - end < mingap) { + offset = end; + mingap = next - end; + } + } + return offset; +} + +static ram_addr_t last_ram_offset(void) +{ + RAMBlock *block; + ram_addr_t last = 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) + last = MAX(last, block->offset + block->length); + + return last; +} + +ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name, + ram_addr_t size, void *host) { - RAMBlock *new_block; + RAMBlock *new_block, *block; size = TARGET_PAGE_ALIGN(size); - new_block = qemu_malloc(sizeof(*new_block)); + new_block = qemu_mallocz(sizeof(*new_block)); +#if 0 + if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { + char *id = dev->parent_bus->info->get_dev_path(dev); + if (id) { + snprintf(new_block->idstr, sizeof(new_block->idstr), "%s/", id); + qemu_free(id); + } + } +#endif + pstrcat(new_block->idstr, sizeof(new_block->idstr), name); + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (!strcmp(block->idstr, new_block->idstr)) { + fprintf(stderr, "RAMBlock \"%s\" already registered, abort!\n", + new_block->idstr); + abort(); + } + } + + if (host) { + new_block->host = host; + new_block->flags |= RAM_PREALLOC_MASK; + } else { + if (mem_path) { +#if 0 && defined (__linux__) && !defined(TARGET_S390X) + new_block->host = file_ram_alloc(new_block, size, mem_path); + if (!new_block->host) { + new_block->host = qemu_vmalloc(size); + qemu_madvise(new_block->host, size, QEMU_MADV_MERGEABLE); + } +#else + fprintf(stderr, "-mem-path option unsupported\n"); + exit(1); +#endif + } else { #if defined(TARGET_S390X) && defined(CONFIG_KVM) - /* XXX S390 KVM requires the topmost vma of the RAM to be < 256GB */ - new_block->host = mmap((void*)0x1000000, size, PROT_EXEC|PROT_READ|PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); + /* XXX S390 KVM requires the topmost vma of the RAM to be < 256GB */ + new_block->host = mmap((void*)0x1000000, size, + PROT_EXEC|PROT_READ|PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); #else - new_block->host = qemu_vmalloc(size); + new_block->host = qemu_vmalloc(size); #endif #ifdef MADV_MERGEABLE - madvise(new_block->host, size, MADV_MERGEABLE); + madvise(new_block->host, size, MADV_MERGEABLE); #endif - new_block->offset = last_ram_offset; + } + } + + new_block->offset = find_ram_offset(size); new_block->length = size; - new_block->next = ram_blocks; - ram_blocks = new_block; + QLIST_INSERT_HEAD(&ram_list.blocks, new_block, next); - phys_ram_dirty = qemu_realloc(phys_ram_dirty, - (last_ram_offset + size) >> TARGET_PAGE_BITS); - memset(phys_ram_dirty + (last_ram_offset >> TARGET_PAGE_BITS), + ram_list.phys_dirty = qemu_realloc(ram_list.phys_dirty, + last_ram_offset() >> TARGET_PAGE_BITS); + memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS), 0xff, size >> TARGET_PAGE_BITS); - last_ram_offset += size; - if (kvm_enabled()) kvm_setup_guest_memory(new_block->host, size); return new_block->offset; } +ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size) +{ + return qemu_ram_alloc_from_ptr(dev, name, size, NULL); +} + void qemu_ram_free(ram_addr_t addr) { - /* TODO: implement this. */ + RAMBlock *block; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (addr == block->offset) { + QLIST_REMOVE(block, next); + if (block->flags & RAM_PREALLOC_MASK) { + ; + } else if (mem_path) { +#if defined (__linux__) && !defined(TARGET_S390X) + if (block->fd) { + munmap(block->host, block->length); + close(block->fd); + } else { + qemu_vfree(block->host); + } +#else + abort(); +#endif + } else { +#if defined(TARGET_S390X) && defined(CONFIG_KVM) + munmap(block->host, block->length); +#else + qemu_vfree(block->host); +#endif + } + qemu_free(block); + return; + } + } + +} + +#ifndef _WIN32 +void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) +{ +#ifndef CONFIG_ANDROID + RAMBlock *block; + ram_addr_t offset; + int flags; + void *area, *vaddr; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + offset = addr - block->offset; + if (offset < block->length) { + vaddr = block->host + offset; + if (block->flags & RAM_PREALLOC_MASK) { + ; + } else { + flags = MAP_FIXED; + munmap(vaddr, length); + if (mem_path) { +#if defined(__linux__) && !defined(TARGET_S390X) + if (block->fd) { +#ifdef MAP_POPULATE + flags |= mem_prealloc ? MAP_POPULATE | MAP_SHARED : + MAP_PRIVATE; +#else + flags |= MAP_PRIVATE; +#endif + area = mmap(vaddr, length, PROT_READ | PROT_WRITE, + flags, block->fd, offset); + } else { + flags |= MAP_PRIVATE | MAP_ANONYMOUS; + area = mmap(vaddr, length, PROT_READ | PROT_WRITE, + flags, -1, 0); + } +#else + abort(); +#endif + } else { +#if defined(TARGET_S390X) && defined(CONFIG_KVM) + flags |= MAP_SHARED | MAP_ANONYMOUS; + area = mmap(vaddr, length, PROT_EXEC|PROT_READ|PROT_WRITE, + flags, -1, 0); +#else + flags |= MAP_PRIVATE | MAP_ANONYMOUS; + area = mmap(vaddr, length, PROT_READ | PROT_WRITE, + flags, -1, 0); +#endif + } + if (area != vaddr) { + fprintf(stderr, "Could not remap addr: %lx@%lx\n", + length, addr); + exit(1); + } + qemu_madvise(vaddr, length, QEMU_MADV_MERGEABLE); + } + return; + } + } +#endif /* !CONFIG_ANDROID */ } +#endif /* !_WIN32 */ /* Return a host pointer to ram allocated with qemu_ram_alloc. With the exception of the softmmu code in this file, this should @@ -2525,57 +2695,69 @@ void qemu_ram_free(ram_addr_t addr) */ void *qemu_get_ram_ptr(ram_addr_t addr) { - RAMBlock *prev; - RAMBlock **prevp; RAMBlock *block; - prev = NULL; - prevp = &ram_blocks; - block = ram_blocks; - while (block && (block->offset > addr - || block->offset + block->length <= addr)) { - if (prev) - prevp = &prev->next; - prev = block; - block = block->next; - } - if (!block) { + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (addr - block->offset < block->length) { + /* Move this entry to to start of the list. */ + if (block != QLIST_FIRST(&ram_list.blocks)) { + QLIST_REMOVE(block, next); + QLIST_INSERT_HEAD(&ram_list.blocks, block, next); + } + return block->host + (addr - block->offset); + } + } + fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); abort(); + + return NULL; } - /* Move this entry to to start of the list. */ - if (prev) { - prev->next = block->next; - block->next = *prevp; - *prevp = block; - } + +/* Return a host pointer to ram allocated with qemu_ram_alloc. + * Same as qemu_get_ram_ptr but avoid reordering ramblocks. + */ +void *qemu_safe_ram_ptr(ram_addr_t addr) +{ + RAMBlock *block; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (addr - block->offset < block->length) { return block->host + (addr - block->offset); } + } -/* Some of the softmmu routines need to translate from a host pointer - (typically a TLB entry) back to a ram offset. */ -ram_addr_t qemu_ram_addr_from_host(void *ptr) + fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); + abort(); + + return NULL; +} + +int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) { - RAMBlock *prev; - RAMBlock **prevp; RAMBlock *block; uint8_t *host = ptr; - prev = NULL; - prevp = &ram_blocks; - block = ram_blocks; - while (block && (block->host > host - || block->host + block->length <= host)) { - if (prev) - prevp = &prev->next; - prev = block; - block = block->next; - } - if (!block) { + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (host - block->host < block->length) { + *ram_addr = block->offset + (host - block->host); + return 0; + } + } + return -1; +} + +/* Some of the softmmu routines need to translate from a host pointer + (typically a TLB entry) back to a ram offset. */ +ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) +{ + ram_addr_t ram_addr; + + if (qemu_ram_addr_from_host(ptr, &ram_addr)) { fprintf(stderr, "Bad ram pointer %p\n", ptr); abort(); } - return block->offset + (host - block->host); + return ram_addr; } static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr) @@ -2657,16 +2839,16 @@ static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr, uint32_t val) { int dirty_flags; - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); if (!(dirty_flags & CODE_DIRTY_FLAG)) { #if !defined(CONFIG_USER_ONLY) tb_invalidate_phys_page_fast(ram_addr, 1); - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); #endif } stb_p(qemu_get_ram_ptr(ram_addr), val); dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); - phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags); /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) @@ -2677,16 +2859,16 @@ static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr, uint32_t val) { int dirty_flags; - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); if (!(dirty_flags & CODE_DIRTY_FLAG)) { #if !defined(CONFIG_USER_ONLY) tb_invalidate_phys_page_fast(ram_addr, 2); - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); #endif } stw_p(qemu_get_ram_ptr(ram_addr), val); dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); - phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags); /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) @@ -2697,16 +2879,16 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr, uint32_t val) { int dirty_flags; - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); if (!(dirty_flags & CODE_DIRTY_FLAG)) { #if !defined(CONFIG_USER_ONLY) tb_invalidate_phys_page_fast(ram_addr, 4); - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); #endif } stl_p(qemu_get_ram_ptr(ram_addr), val); dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); - phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags); /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) @@ -2754,7 +2936,7 @@ static void check_watchpoint(int offset, int len_mask, int flags) cpu_abort(env, "check_watchpoint: could not find TB for " "pc=%p", (void *)env->mem_io_pc); } - cpu_restore_state(tb, env, env->mem_io_pc, NULL); + cpu_restore_state(tb, env, env->mem_io_pc); tb_phys_invalidate(tb, -1); if (wp->flags & BP_STOP_BEFORE_ACCESS) { env->exception_index = EXCP_DEBUG; @@ -3158,8 +3340,8 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + l, 0); /* set dirty bit */ - phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= - (0xff & ~CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flags( + addr1, (0xff & ~CODE_DIRTY_FLAG)); } } } else { @@ -3355,7 +3537,7 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, { if (buffer != bounce.buffer) { if (is_write) { - ram_addr_t addr1 = qemu_ram_addr_from_host(buffer); + ram_addr_t addr1 = qemu_ram_addr_from_host_nofail(buffer); while (access_len) { unsigned l; l = TARGET_PAGE_SIZE; @@ -3365,8 +3547,8 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + l, 0); /* set dirty bit */ - phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= - (0xff & ~CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flags( + addr1, (0xff & ~CODE_DIRTY_FLAG)); } addr1 += l; access_len -= l; @@ -3377,7 +3559,7 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, if (is_write) { cpu_physical_memory_write(bounce.addr, bounce.buffer, access_len); } - qemu_free(bounce.buffer); + qemu_vfree(bounce.buffer); bounce.buffer = NULL; cpu_notify_map_clients(); } @@ -3500,8 +3682,8 @@ void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val) /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); /* set dirty bit */ - phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= - (0xff & ~CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flags( + addr1, (0xff & ~CODE_DIRTY_FLAG)); } } } @@ -3569,8 +3751,8 @@ void stl_phys(target_phys_addr_t addr, uint32_t val) /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); /* set dirty bit */ - phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= - (0xff & ~CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flags(addr1, + (0xff & ~CODE_DIRTY_FLAG)); } } } @@ -3640,11 +3822,11 @@ void cpu_io_recompile(CPUState *env, void *retaddr) tb = tb_find_pc((unsigned long)retaddr); if (!tb) { - cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", + cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", retaddr); } n = env->icount_decr.u16.low + tb->icount; - cpu_restore_state(tb, env, (unsigned long)retaddr, NULL); + cpu_restore_state(tb, env, (unsigned long)retaddr); /* Calculate how many instructions had been executed before the fault occurred. */ n = n - env->icount_decr.u16.low; @@ -3690,8 +3872,7 @@ void cpu_io_recompile(CPUState *env, void *retaddr) #if !defined(CONFIG_USER_ONLY) -void dump_exec_info(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) { int i, target_code_size, max_target_code_size; int direct_jmp_count, direct_jmp2_count, cross_page; @@ -3718,14 +3899,14 @@ void dump_exec_info(FILE *f, } /* XXX: avoid using doubles ? */ cpu_fprintf(f, "Translation buffer state:\n"); - cpu_fprintf(f, "gen code size %ld/%ld\n", + cpu_fprintf(f, "gen code size %td/%ld\n", code_gen_ptr - code_gen_buffer, code_gen_buffer_max_size); - cpu_fprintf(f, "TB count %d/%d\n", + cpu_fprintf(f, "TB count %d/%d\n", nb_tbs, code_gen_max_blocks); cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", nb_tbs ? target_code_size / nb_tbs : 0, max_target_code_size); - cpu_fprintf(f, "TB avg host size %d bytes (expansion ratio: %0.1f)\n", + cpu_fprintf(f, "TB avg host size %td bytes (expansion ratio: %0.1f)\n", nb_tbs ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0, target_code_size ? (double) (code_gen_ptr - code_gen_buffer) / target_code_size : 0); cpu_fprintf(f, "cross page TB count %d (%d%%)\n", diff --git a/fpu/softfloat-macros.h b/fpu/softfloat-macros.h index 7838228..e82ce23 100644 --- a/fpu/softfloat-macros.h +++ b/fpu/softfloat-macros.h @@ -1,3 +1,8 @@ +/* + * QEMU float support macros + * + * Derived from SoftFloat. + */ /*============================================================================ @@ -31,6 +36,17 @@ these four paragraphs for those parts of this code that are retained. =============================================================================*/ /*---------------------------------------------------------------------------- +| This macro tests for minimum version of the GNU C compiler. +*----------------------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +# define SOFTFLOAT_GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +# define SOFTFLOAT_GNUC_PREREQ(maj, min) 0 +#endif + + +/*---------------------------------------------------------------------------- | Shifts `a' right by the number of bits given in `count'. If any nonzero | bits are shifted off, they are ``jammed'' into the least significant bit of | the result by setting the least significant bit to 1. The value of `count' @@ -39,9 +55,9 @@ these four paragraphs for those parts of this code that are retained. | The result is stored in the location pointed to by `zPtr'. *----------------------------------------------------------------------------*/ -INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr ) +INLINE void shift32RightJamming( uint32_t a, int16 count, uint32_t *zPtr ) { - bits32 z; + uint32_t z; if ( count == 0 ) { z = a; @@ -65,9 +81,9 @@ INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr ) | The result is stored in the location pointed to by `zPtr'. *----------------------------------------------------------------------------*/ -INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr ) +INLINE void shift64RightJamming( uint64_t a, int16 count, uint64_t *zPtr ) { - bits64 z; + uint64_t z; if ( count == 0 ) { z = a; @@ -101,9 +117,9 @@ INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr ) INLINE void shift64ExtraRightJamming( - bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr ) { - bits64 z0, z1; + uint64_t z0, z1; int8 negCount = ( - count ) & 63; if ( count == 0 ) { @@ -138,9 +154,9 @@ INLINE void INLINE void shift128Right( - bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr ) { - bits64 z0, z1; + uint64_t z0, z1; int8 negCount = ( - count ) & 63; if ( count == 0 ) { @@ -173,9 +189,9 @@ INLINE void INLINE void shift128RightJamming( - bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr ) { - bits64 z0, z1; + uint64_t z0, z1; int8 negCount = ( - count ) & 63; if ( count == 0 ) { @@ -224,16 +240,16 @@ INLINE void INLINE void shift128ExtraRightJamming( - bits64 a0, - bits64 a1, - bits64 a2, + uint64_t a0, + uint64_t a1, + uint64_t a2, int16 count, - bits64 *z0Ptr, - bits64 *z1Ptr, - bits64 *z2Ptr + uint64_t *z0Ptr, + uint64_t *z1Ptr, + uint64_t *z2Ptr ) { - bits64 z0, z1, z2; + uint64_t z0, z1, z2; int8 negCount = ( - count ) & 63; if ( count == 0 ) { @@ -282,7 +298,7 @@ INLINE void INLINE void shortShift128Left( - bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr ) { *z1Ptr = a1<<count; @@ -301,16 +317,16 @@ INLINE void INLINE void shortShift192Left( - bits64 a0, - bits64 a1, - bits64 a2, + uint64_t a0, + uint64_t a1, + uint64_t a2, int16 count, - bits64 *z0Ptr, - bits64 *z1Ptr, - bits64 *z2Ptr + uint64_t *z0Ptr, + uint64_t *z1Ptr, + uint64_t *z2Ptr ) { - bits64 z0, z1, z2; + uint64_t z0, z1, z2; int8 negCount; z2 = a2<<count; @@ -336,9 +352,9 @@ INLINE void INLINE void add128( - bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, uint64_t *z0Ptr, uint64_t *z1Ptr ) { - bits64 z1; + uint64_t z1; z1 = a1 + b1; *z1Ptr = z1; @@ -356,18 +372,18 @@ INLINE void INLINE void add192( - bits64 a0, - bits64 a1, - bits64 a2, - bits64 b0, - bits64 b1, - bits64 b2, - bits64 *z0Ptr, - bits64 *z1Ptr, - bits64 *z2Ptr + uint64_t a0, + uint64_t a1, + uint64_t a2, + uint64_t b0, + uint64_t b1, + uint64_t b2, + uint64_t *z0Ptr, + uint64_t *z1Ptr, + uint64_t *z2Ptr ) { - bits64 z0, z1, z2; + uint64_t z0, z1, z2; int8 carry0, carry1; z2 = a2 + b2; @@ -394,7 +410,7 @@ INLINE void INLINE void sub128( - bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, uint64_t *z0Ptr, uint64_t *z1Ptr ) { *z1Ptr = a1 - b1; @@ -412,18 +428,18 @@ INLINE void INLINE void sub192( - bits64 a0, - bits64 a1, - bits64 a2, - bits64 b0, - bits64 b1, - bits64 b2, - bits64 *z0Ptr, - bits64 *z1Ptr, - bits64 *z2Ptr + uint64_t a0, + uint64_t a1, + uint64_t a2, + uint64_t b0, + uint64_t b1, + uint64_t b2, + uint64_t *z0Ptr, + uint64_t *z1Ptr, + uint64_t *z2Ptr ) { - bits64 z0, z1, z2; + uint64_t z0, z1, z2; int8 borrow0, borrow1; z2 = a2 - b2; @@ -446,21 +462,21 @@ INLINE void | `z0Ptr' and `z1Ptr'. *----------------------------------------------------------------------------*/ -INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr ) +INLINE void mul64To128( uint64_t a, uint64_t b, uint64_t *z0Ptr, uint64_t *z1Ptr ) { - bits32 aHigh, aLow, bHigh, bLow; - bits64 z0, zMiddleA, zMiddleB, z1; + uint32_t aHigh, aLow, bHigh, bLow; + uint64_t z0, zMiddleA, zMiddleB, z1; aLow = a; aHigh = a>>32; bLow = b; bHigh = b>>32; - z1 = ( (bits64) aLow ) * bLow; - zMiddleA = ( (bits64) aLow ) * bHigh; - zMiddleB = ( (bits64) aHigh ) * bLow; - z0 = ( (bits64) aHigh ) * bHigh; + z1 = ( (uint64_t) aLow ) * bLow; + zMiddleA = ( (uint64_t) aLow ) * bHigh; + zMiddleB = ( (uint64_t) aHigh ) * bLow; + z0 = ( (uint64_t) aHigh ) * bHigh; zMiddleA += zMiddleB; - z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 ); + z0 += ( ( (uint64_t) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 ); zMiddleA <<= 32; z1 += zMiddleA; z0 += ( z1 < zMiddleA ); @@ -478,15 +494,15 @@ INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr ) INLINE void mul128By64To192( - bits64 a0, - bits64 a1, - bits64 b, - bits64 *z0Ptr, - bits64 *z1Ptr, - bits64 *z2Ptr + uint64_t a0, + uint64_t a1, + uint64_t b, + uint64_t *z0Ptr, + uint64_t *z1Ptr, + uint64_t *z2Ptr ) { - bits64 z0, z1, z2, more1; + uint64_t z0, z1, z2, more1; mul64To128( a1, b, &z1, &z2 ); mul64To128( a0, b, &z0, &more1 ); @@ -506,18 +522,18 @@ INLINE void INLINE void mul128To256( - bits64 a0, - bits64 a1, - bits64 b0, - bits64 b1, - bits64 *z0Ptr, - bits64 *z1Ptr, - bits64 *z2Ptr, - bits64 *z3Ptr + uint64_t a0, + uint64_t a1, + uint64_t b0, + uint64_t b1, + uint64_t *z0Ptr, + uint64_t *z1Ptr, + uint64_t *z2Ptr, + uint64_t *z3Ptr ) { - bits64 z0, z1, z2, z3; - bits64 more1, more2; + uint64_t z0, z1, z2, z3; + uint64_t more1, more2; mul64To128( a1, b1, &z2, &z3 ); mul64To128( a1, b0, &z1, &more2 ); @@ -543,18 +559,18 @@ INLINE void | unsigned integer is returned. *----------------------------------------------------------------------------*/ -static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b ) +static uint64_t estimateDiv128To64( uint64_t a0, uint64_t a1, uint64_t b ) { - bits64 b0, b1; - bits64 rem0, rem1, term0, term1; - bits64 z; + uint64_t b0, b1; + uint64_t rem0, rem1, term0, term1; + uint64_t z; if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF ); b0 = b>>32; z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32; mul64To128( b, z, &term0, &term1 ); sub128( a0, a1, term0, term1, &rem0, &rem1 ); - while ( ( (sbits64) rem0 ) < 0 ) { + while ( ( (int64_t) rem0 ) < 0 ) { z -= LIT64( 0x100000000 ); b1 = b<<32; add128( rem0, rem1, b0, b1, &rem0, &rem1 ); @@ -575,18 +591,18 @@ static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b ) | value. *----------------------------------------------------------------------------*/ -static bits32 estimateSqrt32( int16 aExp, bits32 a ) +static uint32_t estimateSqrt32( int16 aExp, uint32_t a ) { - static const bits16 sqrtOddAdjustments[] = { + static const uint16_t sqrtOddAdjustments[] = { 0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0, 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67 }; - static const bits16 sqrtEvenAdjustments[] = { + static const uint16_t sqrtEvenAdjustments[] = { 0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E, 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002 }; int8 index; - bits32 z; + uint32_t z; index = ( a>>27 ) & 15; if ( aExp & 1 ) { @@ -598,9 +614,9 @@ static bits32 estimateSqrt32( int16 aExp, bits32 a ) z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ (int)index ]; z = a / z + z; z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 ); - if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 ); + if ( z <= a ) return (uint32_t) ( ( (int32_t) a )>>1 ); } - return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 ); + return ( (uint32_t) ( ( ( (uint64_t) a )<<31 ) / z ) ) + ( z>>1 ); } @@ -609,8 +625,15 @@ static bits32 estimateSqrt32( int16 aExp, bits32 a ) | `a'. If `a' is zero, 32 is returned. *----------------------------------------------------------------------------*/ -static int8 countLeadingZeros32( bits32 a ) +static int8 countLeadingZeros32( uint32_t a ) { +#if SOFTFLOAT_GNUC_PREREQ(3, 4) + if (a) { + return __builtin_clz(a); + } else { + return 32; + } +#else static const int8 countLeadingZerosHigh[] = { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -642,7 +665,7 @@ static int8 countLeadingZeros32( bits32 a ) } shiftCount += countLeadingZerosHigh[ a>>24 ]; return shiftCount; - +#endif } /*---------------------------------------------------------------------------- @@ -650,12 +673,19 @@ static int8 countLeadingZeros32( bits32 a ) | `a'. If `a' is zero, 64 is returned. *----------------------------------------------------------------------------*/ -static int8 countLeadingZeros64( bits64 a ) +static int8 countLeadingZeros64( uint64_t a ) { +#if SOFTFLOAT_GNUC_PREREQ(3, 4) + if (a) { + return __builtin_clzll(a); + } else { + return 64; + } +#else int8 shiftCount; shiftCount = 0; - if ( a < ( (bits64) 1 )<<32 ) { + if ( a < ( (uint64_t) 1 )<<32 ) { shiftCount += 32; } else { @@ -663,7 +693,7 @@ static int8 countLeadingZeros64( bits64 a ) } shiftCount += countLeadingZeros32( a ); return shiftCount; - +#endif } /*---------------------------------------------------------------------------- @@ -672,7 +702,7 @@ static int8 countLeadingZeros64( bits64 a ) | Otherwise, returns 0. *----------------------------------------------------------------------------*/ -INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +INLINE flag eq128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 == b0 ) && ( a1 == b1 ); @@ -685,7 +715,7 @@ INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) | Otherwise, returns 0. *----------------------------------------------------------------------------*/ -INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +INLINE flag le128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) ); @@ -698,7 +728,7 @@ INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) | returns 0. *----------------------------------------------------------------------------*/ -INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +INLINE flag lt128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) ); @@ -711,7 +741,7 @@ INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) | Otherwise, returns 0. *----------------------------------------------------------------------------*/ -INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +INLINE flag ne128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 != b0 ) || ( a1 != b1 ); diff --git a/fpu/softfloat-native.c b/fpu/softfloat-native.c index 049c830..8848651 100644 --- a/fpu/softfloat-native.c +++ b/fpu/softfloat-native.c @@ -254,7 +254,7 @@ int float32_is_signaling_nan( float32 a1) return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); } -int float32_is_nan( float32 a1 ) +int float32_is_quiet_nan( float32 a1 ) { float32u u; uint64_t a; @@ -263,6 +263,15 @@ int float32_is_nan( float32 a1 ) return ( 0xFF800000 < ( a<<1 ) ); } +int float32_is_any_nan( float32 a1 ) +{ + float32u u; + uint32_t a; + u.f = a1; + a = u.i; + return (a & ~(1 << 31)) > 0x7f800000U; +} + /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ @@ -411,15 +420,25 @@ int float64_is_signaling_nan( float64 a1) } -int float64_is_nan( float64 a1 ) +int float64_is_quiet_nan( float64 a1 ) { float64u u; uint64_t a; u.f = a1; a = u.i; - return ( LIT64( 0xFFF0000000000000 ) < (bits64) ( a<<1 ) ); + return ( LIT64( 0xFFF0000000000000 ) < (uint64_t) ( a<<1 ) ); + +} + +int float64_is_any_nan( float64 a1 ) +{ + float64u u; + uint64_t a; + u.f = a1; + a = u.i; + return (a & ~(1ULL << 63)) > LIT64 (0x7FF0000000000000 ); } #ifdef FLOATX80 @@ -500,15 +519,22 @@ int floatx80_is_signaling_nan( floatx80 a1) aLow = u.i.low & ~ LIT64( 0x4000000000000000 ); return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) - && (bits64) ( aLow<<1 ) + && (uint64_t) ( aLow<<1 ) && ( u.i.low == aLow ); } -int floatx80_is_nan( floatx80 a1 ) +int floatx80_is_quiet_nan( floatx80 a1 ) +{ + floatx80u u; + u.f = a1; + return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (uint64_t) ( u.i.low<<1 ); +} + +int floatx80_is_any_nan( floatx80 a1 ) { floatx80u u; u.f = a1; - return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( u.i.low<<1 ); + return ((u.i.high & 0x7FFF) == 0x7FFF) && ( u.i.low<<1 ); } #endif diff --git a/fpu/softfloat-native.h b/fpu/softfloat-native.h index 6da0bcb..6afb74a 100644 --- a/fpu/softfloat-native.h +++ b/fpu/softfloat-native.h @@ -172,6 +172,15 @@ float128 int64_to_float128( int64_t STATUS_PARAM); #endif /*---------------------------------------------------------------------------- +| Software IEC/IEEE single-precision conversion constants. +*----------------------------------------------------------------------------*/ +#define float32_zero (0.0) +#define float32_one (1.0) +#define float32_ln2 (0.6931471) +#define float32_pi (3.1415926) +#define float32_half (0.5) + +/*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ int float32_to_int32( float32 STATUS_PARAM); @@ -210,7 +219,7 @@ INLINE float32 float32_div( float32 a, float32 b STATUS_PARAM) } float32 float32_rem( float32, float32 STATUS_PARAM); float32 float32_sqrt( float32 STATUS_PARAM); -INLINE int float32_eq( float32 a, float32 b STATUS_PARAM) +INLINE int float32_eq_quiet( float32 a, float32 b STATUS_PARAM) { return a == b; } @@ -222,7 +231,7 @@ INLINE int float32_lt( float32 a, float32 b STATUS_PARAM) { return a < b; } -INLINE int float32_eq_signaling( float32 a, float32 b STATUS_PARAM) +INLINE int float32_eq( float32 a, float32 b STATUS_PARAM) { return a <= b && a >= b; } @@ -237,12 +246,16 @@ INLINE int float32_lt_quiet( float32 a, float32 b STATUS_PARAM) INLINE int float32_unordered( float32 a, float32 b STATUS_PARAM) { return isunordered(a, b); - +} +INLINE int float32_unordered_quiet( float32 a, float32 b STATUS_PARAM) +{ + return isunordered(a, b); } int float32_compare( float32, float32 STATUS_PARAM ); int float32_compare_quiet( float32, float32 STATUS_PARAM ); int float32_is_signaling_nan( float32 ); -int float32_is_nan( float32 ); +int float32_is_quiet_nan( float32 ); +int float32_is_any_nan( float32 ); INLINE float32 float32_abs(float32 a) { @@ -271,12 +284,21 @@ INLINE float32 float32_is_zero(float32 a) return fpclassify(a) == FP_ZERO; } -INLINE float32 float32_scalbn(float32 a, int n) +INLINE float32 float32_scalbn(float32 a, int n STATUS_PARAM) { return scalbnf(a, n); } /*---------------------------------------------------------------------------- +| Software IEC/IEEE double-precision conversion constants. +*----------------------------------------------------------------------------*/ +#define float64_zero (0.0) +#define float64_one (1.0) +#define float64_ln2 (0.693147180559945) +#define float64_pi (3.141592653589793) +#define float64_half (0.5) + +/*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ int float64_to_int32( float64 STATUS_PARAM ); @@ -318,7 +340,7 @@ INLINE float64 float64_div( float64 a, float64 b STATUS_PARAM) } float64 float64_rem( float64, float64 STATUS_PARAM ); float64 float64_sqrt( float64 STATUS_PARAM ); -INLINE int float64_eq( float64 a, float64 b STATUS_PARAM) +INLINE int float64_eq_quiet( float64 a, float64 b STATUS_PARAM) { return a == b; } @@ -330,7 +352,7 @@ INLINE int float64_lt( float64 a, float64 b STATUS_PARAM) { return a < b; } -INLINE int float64_eq_signaling( float64 a, float64 b STATUS_PARAM) +INLINE int float64_eq( float64 a, float64 b STATUS_PARAM) { return a <= b && a >= b; } @@ -346,12 +368,16 @@ INLINE int float64_lt_quiet( float64 a, float64 b STATUS_PARAM) INLINE int float64_unordered( float64 a, float64 b STATUS_PARAM) { return isunordered(a, b); - +} +INLINE int float64_unordered_quiet( float64 a, float64 b STATUS_PARAM) +{ + return isunordered(a, b); } int float64_compare( float64, float64 STATUS_PARAM ); int float64_compare_quiet( float64, float64 STATUS_PARAM ); int float64_is_signaling_nan( float64 ); -int float64_is_nan( float64 ); +int float64_is_any_nan( float64 ); +int float64_is_quiet_nan( float64 ); INLINE float64 float64_abs(float64 a) { @@ -380,7 +406,7 @@ INLINE float64 float64_is_zero(float64 a) return fpclassify(a) == FP_ZERO; } -INLINE float64 float64_scalbn(float64 a, int n) +INLINE float64 float64_scalbn(float64 a, int n STATUS_PARAM) { return scalbn(a, n); } @@ -388,6 +414,15 @@ INLINE float64 float64_scalbn(float64 a, int n) #ifdef FLOATX80 /*---------------------------------------------------------------------------- +| Software IEC/IEEE extended double-precision conversion constants. +*----------------------------------------------------------------------------*/ +#define floatx80_zero (0.0L) +#define floatx80_one (1.0L) +#define floatx80_ln2 (0.69314718055994530943L) +#define floatx80_pi (3.14159265358979323851L) +#define floatx80_half (0.5L) + +/*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision conversion routines. *----------------------------------------------------------------------------*/ int floatx80_to_int32( floatx80 STATUS_PARAM ); @@ -422,7 +457,7 @@ INLINE floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM) } floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM ); floatx80 floatx80_sqrt( floatx80 STATUS_PARAM ); -INLINE int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM) +INLINE int floatx80_eq_quiet( floatx80 a, floatx80 b STATUS_PARAM) { return a == b; } @@ -434,7 +469,7 @@ INLINE int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM) { return a < b; } -INLINE int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM) +INLINE int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM) { return a <= b && a >= b; } @@ -450,12 +485,16 @@ INLINE int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM) INLINE int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM) { return isunordered(a, b); - +} +INLINE int floatx80_unordered_quiet( floatx80 a, floatx80 b STATUS_PARAM) +{ + return isunordered(a, b); } int floatx80_compare( floatx80, floatx80 STATUS_PARAM ); int floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM ); int floatx80_is_signaling_nan( floatx80 ); -int floatx80_is_nan( floatx80 ); +int floatx80_is_quiet_nan( floatx80 ); +int floatx80_is_any_nan( floatx80 ); INLINE floatx80 floatx80_abs(floatx80 a) { @@ -484,7 +523,7 @@ INLINE floatx80 floatx80_is_zero(floatx80 a) return fpclassify(a) == FP_ZERO; } -INLINE floatx80 floatx80_scalbn(floatx80 a, int n) +INLINE floatx80 floatx80_scalbn(floatx80 a, int n STATUS_PARAM) { return scalbnl(a, n); } diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 8e6aceb..9d68aae 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -1,3 +1,8 @@ +/* + * QEMU float support + * + * Derived from SoftFloat. + */ /*============================================================================ @@ -30,12 +35,6 @@ these four paragraphs for those parts of this code that are retained. =============================================================================*/ -#if defined(TARGET_MIPS) || defined(TARGET_HPPA) -#define SNAN_BIT_IS_ONE 1 -#else -#define SNAN_BIT_IS_ONE 0 -#endif - /*---------------------------------------------------------------------------- | Raises the exceptions specified by `flags'. Floating-point traps can be | defined here if desired. It is currently not possible for such a trap @@ -53,36 +52,111 @@ void float_raise( int8 flags STATUS_PARAM ) *----------------------------------------------------------------------------*/ typedef struct { flag sign; - bits64 high, low; + uint64_t high, low; } commonNaNT; /*---------------------------------------------------------------------------- -| The pattern for a default generated single-precision NaN. +| Returns 1 if the half-precision floating-point value `a' is a quiet +| NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ -#if defined(TARGET_SPARC) -#define float32_default_nan make_float32(0x7FFFFFFF) -#elif defined(TARGET_POWERPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) -#define float32_default_nan make_float32(0x7FC00000) -#elif defined(TARGET_HPPA) -#define float32_default_nan make_float32(0x7FA00000) -#elif SNAN_BIT_IS_ONE -#define float32_default_nan make_float32(0x7FBFFFFF) + +int float16_is_quiet_nan(float16 a_) +{ + uint16_t a = float16_val(a_); +#if SNAN_BIT_IS_ONE + return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); #else -#define float32_default_nan make_float32(0xFFC00000) + return ((a & ~0x8000) >= 0x7c80); #endif +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the half-precision floating-point value `a' is a signaling +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float16_is_signaling_nan(float16 a_) +{ + uint16_t a = float16_val(a_); +#if SNAN_BIT_IS_ONE + return ((a & ~0x8000) >= 0x7c80); +#else + return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the half-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ +float16 float16_maybe_silence_nan(float16 a_) +{ + if (float16_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + return float16_default_nan; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + uint16_t a = float16_val(a_); + a |= (1 << 9); + return make_float16(a); +#endif + } + return a_; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the half-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float16ToCommonNaN( float16 a STATUS_PARAM ) +{ + commonNaNT z; + + if ( float16_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR ); + z.sign = float16_val(a) >> 15; + z.low = 0; + z.high = ((uint64_t) float16_val(a))<<54; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the half- +| precision floating-point format. +*----------------------------------------------------------------------------*/ + +static float16 commonNaNToFloat16(commonNaNT a STATUS_PARAM) +{ + uint16_t mantissa = a.high>>54; + + if (STATUS(default_nan_mode)) { + return float16_default_nan; + } + + if (mantissa) { + return make_float16(((((uint16_t) a.sign) << 15) + | (0x1F << 10) | mantissa)); + } else { + return float16_default_nan; + } +} /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ -int float32_is_nan( float32 a_ ) +int float32_is_quiet_nan( float32 a_ ) { uint32_t a = float32_val(a_); #if SNAN_BIT_IS_ONE return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); #else - return ( 0xFF800000 <= (bits32) ( a<<1 ) ); + return ( 0xFF800000 <= (uint32_t) ( a<<1 ) ); #endif } @@ -95,13 +169,36 @@ int float32_is_signaling_nan( float32 a_ ) { uint32_t a = float32_val(a_); #if SNAN_BIT_IS_ONE - return ( 0xFF800000 <= (bits32) ( a<<1 ) ); + return ( 0xFF800000 <= (uint32_t) ( a<<1 ) ); #else return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); #endif } /*---------------------------------------------------------------------------- +| Returns a quiet NaN if the single-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float32 float32_maybe_silence_nan( float32 a_ ) +{ + if (float32_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + return float32_default_nan; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + uint32_t a = float32_val(a_); + a |= (1 << 22); + return make_float32(a); +#endif + } + return a_; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid | exception is raised. @@ -114,7 +211,7 @@ static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM ) if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR ); z.sign = float32_val(a)>>31; z.low = 0; - z.high = ( (bits64) float32_val(a) )<<41; + z.high = ( (uint64_t) float32_val(a) )<<41; return z; } @@ -123,17 +220,134 @@ static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM ) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float32 commonNaNToFloat32( commonNaNT a ) +static float32 commonNaNToFloat32( commonNaNT a STATUS_PARAM) { - bits32 mantissa = a.high>>41; + uint32_t mantissa = a.high>>41; + + if ( STATUS(default_nan_mode) ) { + return float32_default_nan; + } + if ( mantissa ) return make_float32( - ( ( (bits32) a.sign )<<31 ) | 0x7F800000 | ( a.high>>41 ) ); + ( ( (uint32_t) a.sign )<<31 ) | 0x7F800000 | ( a.high>>41 ) ); else return float32_default_nan; } /*---------------------------------------------------------------------------- +| Select which NaN to propagate for a two-input operation. +| IEEE754 doesn't specify all the details of this, so the +| algorithm is target-specific. +| The routine is passed various bits of information about the +| two NaNs and should return 0 to select NaN a and 1 for NaN b. +| Note that signalling NaNs are always squashed to quiet NaNs +| by the caller, by calling floatXX_maybe_silence_nan() before +| returning them. +| +| aIsLargerSignificand is only valid if both a and b are NaNs +| of some kind, and is true if a has the larger significand, +| or if both a and b have the same significand but a is +| positive but b is negative. It is only needed for the x87 +| tie-break rule. +*----------------------------------------------------------------------------*/ + +#if defined(TARGET_ARM) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* ARM mandated NaN propagation rules: take the first of: + * 1. A if it is signaling + * 2. B if it is signaling + * 3. A (quiet) + * 4. B (quiet) + * A signaling NaN is always quietened before returning it. + */ + if (aIsSNaN) { + return 0; + } else if (bIsSNaN) { + return 1; + } else if (aIsQNaN) { + return 0; + } else { + return 1; + } +} +#elif defined(TARGET_MIPS) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* According to MIPS specifications, if one of the two operands is + * a sNaN, a new qNaN has to be generated. This is done in + * floatXX_maybe_silence_nan(). For qNaN inputs the specifications + * says: "When possible, this QNaN result is one of the operand QNaN + * values." In practice it seems that most implementations choose + * the first operand if both operands are qNaN. In short this gives + * the following rules: + * 1. A if it is signaling + * 2. B if it is signaling + * 3. A (quiet) + * 4. B (quiet) + * A signaling NaN is always silenced before returning it. + */ + if (aIsSNaN) { + return 0; + } else if (bIsSNaN) { + return 1; + } else if (aIsQNaN) { + return 0; + } else { + return 1; + } +} +#elif defined(TARGET_PPC) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* PowerPC propagation rules: + * 1. A if it sNaN or qNaN + * 2. B if it sNaN or qNaN + * A signaling NaN is always silenced before returning it. + */ + if (aIsSNaN || aIsQNaN) { + return 0; + } else { + return 1; + } +} +#else +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* This implements x87 NaN propagation rules: + * SNaN + QNaN => return the QNaN + * two SNaNs => return the one with the larger significand, silenced + * two QNaNs => return the one with the larger significand + * SNaN and a non-NaN => return the SNaN, silenced + * QNaN and a non-NaN => return the QNaN + * + * If we get down to comparing significands and they are the same, + * return the NaN with the positive sign bit (if any). + */ + if (aIsSNaN) { + if (bIsSNaN) { + return aIsLargerSignificand ? 0 : 1; + } + return bIsQNaN ? 1 : 0; + } + else if (aIsQNaN) { + if (bIsSNaN || !bIsQNaN) + return 0; + else { + return aIsLargerSignificand ? 0 : 1; + } + } else { + return 1; + } +} +#endif + +/*---------------------------------------------------------------------------- | Takes two single-precision floating-point values `a' and `b', one of which | is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a | signaling NaN, the invalid exception is raised. @@ -141,78 +355,52 @@ static float32 commonNaNToFloat32( commonNaNT a ) static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; - bits32 av, bv, res; + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + uint32_t av, bv; - if ( STATUS(default_nan_mode) ) - return float32_default_nan; - - aIsNaN = float32_is_nan( a ); + aIsQuietNaN = float32_is_quiet_nan( a ); aIsSignalingNaN = float32_is_signaling_nan( a ); - bIsNaN = float32_is_nan( b ); + bIsQuietNaN = float32_is_quiet_nan( b ); bIsSignalingNaN = float32_is_signaling_nan( b ); av = float32_val(a); bv = float32_val(b); -#if SNAN_BIT_IS_ONE - av &= ~0x00400000; - bv &= ~0x00400000; -#else - av |= 0x00400000; - bv |= 0x00400000; -#endif + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - res = bIsNaN ? bv : av; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) - res = av; - else { - returnLargerSignificand: - if ( (bits32) ( av<<1 ) < (bits32) ( bv<<1 ) ) - res = bv; - else if ( (bits32) ( bv<<1 ) < (bits32) ( av<<1 ) ) - res = av; - else - res = ( av < bv ) ? av : bv; - } + + if ( STATUS(default_nan_mode) ) + return float32_default_nan; + + if ((uint32_t)(av<<1) < (uint32_t)(bv<<1)) { + aIsLargerSignificand = 0; + } else if ((uint32_t)(bv<<1) < (uint32_t)(av<<1)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (av < bv) ? 1 : 0; } - else { - res = bv; + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, + aIsLargerSignificand)) { + return float32_maybe_silence_nan(b); + } else { + return float32_maybe_silence_nan(a); } - return make_float32(res); } /*---------------------------------------------------------------------------- -| The pattern for a default generated double-precision NaN. -*----------------------------------------------------------------------------*/ -#if defined(TARGET_SPARC) -#define float64_default_nan make_float64(LIT64( 0x7FFFFFFFFFFFFFFF )) -#elif defined(TARGET_POWERPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) -#define float64_default_nan make_float64(LIT64( 0x7FF8000000000000 )) -#elif defined(TARGET_HPPA) -#define float64_default_nan make_float64(LIT64( 0x7FF4000000000000 )) -#elif SNAN_BIT_IS_ONE -#define float64_default_nan make_float64(LIT64( 0x7FF7FFFFFFFFFFFF )) -#else -#define float64_default_nan make_float64(LIT64( 0xFFF8000000000000 )) -#endif - -/*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ -int float64_is_nan( float64 a_ ) +int float64_is_quiet_nan( float64 a_ ) { - bits64 a = float64_val(a_); + uint64_t a = float64_val(a_); #if SNAN_BIT_IS_ONE return ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) && ( a & LIT64( 0x0007FFFFFFFFFFFF ) ); #else - return ( LIT64( 0xFFF0000000000000 ) <= (bits64) ( a<<1 ) ); + return ( LIT64( 0xFFF0000000000000 ) <= (uint64_t) ( a<<1 ) ); #endif } @@ -223,9 +411,9 @@ int float64_is_nan( float64 a_ ) int float64_is_signaling_nan( float64 a_ ) { - bits64 a = float64_val(a_); + uint64_t a = float64_val(a_); #if SNAN_BIT_IS_ONE - return ( LIT64( 0xFFF0000000000000 ) <= (bits64) ( a<<1 ) ); + return ( LIT64( 0xFFF0000000000000 ) <= (uint64_t) ( a<<1 ) ); #else return ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) @@ -234,6 +422,29 @@ int float64_is_signaling_nan( float64 a_ ) } /*---------------------------------------------------------------------------- +| Returns a quiet NaN if the double-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float64 float64_maybe_silence_nan( float64 a_ ) +{ + if (float64_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + return float64_default_nan; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + uint64_t a = float64_val(a_); + a |= LIT64( 0x0008000000000000 ); + return make_float64(a); +#endif + } + return a_; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid | exception is raised. @@ -255,13 +466,17 @@ static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float64 commonNaNToFloat64( commonNaNT a ) +static float64 commonNaNToFloat64( commonNaNT a STATUS_PARAM) { - bits64 mantissa = a.high>>12; + uint64_t mantissa = a.high>>12; + + if ( STATUS(default_nan_mode) ) { + return float64_default_nan; + } if ( mantissa ) return make_float64( - ( ( (bits64) a.sign )<<63 ) + ( ( (uint64_t) a.sign )<<63 ) | LIT64( 0x7FF0000000000000 ) | ( a.high>>12 )); else @@ -276,105 +491,108 @@ static float64 commonNaNToFloat64( commonNaNT a ) static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; - bits64 av, bv, res; - - if ( STATUS(default_nan_mode) ) - return float64_default_nan; + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + uint64_t av, bv; - aIsNaN = float64_is_nan( a ); + aIsQuietNaN = float64_is_quiet_nan( a ); aIsSignalingNaN = float64_is_signaling_nan( a ); - bIsNaN = float64_is_nan( b ); + bIsQuietNaN = float64_is_quiet_nan( b ); bIsSignalingNaN = float64_is_signaling_nan( b ); av = float64_val(a); bv = float64_val(b); -#if SNAN_BIT_IS_ONE - av &= ~LIT64( 0x0008000000000000 ); - bv &= ~LIT64( 0x0008000000000000 ); -#else - av |= LIT64( 0x0008000000000000 ); - bv |= LIT64( 0x0008000000000000 ); -#endif + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - res = bIsNaN ? bv : av; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) - res = av; - else { - returnLargerSignificand: - if ( (bits64) ( av<<1 ) < (bits64) ( bv<<1 ) ) - res = bv; - else if ( (bits64) ( bv<<1 ) < (bits64) ( av<<1 ) ) - res = av; - else - res = ( av < bv ) ? av : bv; - } + + if ( STATUS(default_nan_mode) ) + return float64_default_nan; + + if ((uint64_t)(av<<1) < (uint64_t)(bv<<1)) { + aIsLargerSignificand = 0; + } else if ((uint64_t)(bv<<1) < (uint64_t)(av<<1)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (av < bv) ? 1 : 0; } - else { - res = bv; + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, + aIsLargerSignificand)) { + return float64_maybe_silence_nan(b); + } else { + return float64_maybe_silence_nan(a); } - return make_float64(res); } #ifdef FLOATX80 /*---------------------------------------------------------------------------- -| The pattern for a default generated extended double-precision NaN. The -| `high' and `low' values hold the most- and least-significant bits, -| respectively. -*----------------------------------------------------------------------------*/ -#if SNAN_BIT_IS_ONE -#define floatx80_default_nan_high 0x7FFF -#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF ) -#else -#define floatx80_default_nan_high 0xFFFF -#define floatx80_default_nan_low LIT64( 0xC000000000000000 ) -#endif - -/*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a -| quiet NaN; otherwise returns 0. +| quiet NaN; otherwise returns 0. This slightly differs from the same +| function for other types as floatx80 has an explicit bit. *----------------------------------------------------------------------------*/ -int floatx80_is_nan( floatx80 a ) +int floatx80_is_quiet_nan( floatx80 a ) { #if SNAN_BIT_IS_ONE - bits64 aLow; + uint64_t aLow; aLow = a.low & ~ LIT64( 0x4000000000000000 ); return ( ( a.high & 0x7FFF ) == 0x7FFF ) - && (bits64) ( aLow<<1 ) + && (uint64_t) ( aLow<<1 ) && ( a.low == aLow ); #else - return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 ); + return ( ( a.high & 0x7FFF ) == 0x7FFF ) + && (LIT64( 0x8000000000000000 ) <= ((uint64_t) ( a.low<<1 ))); #endif } /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a -| signaling NaN; otherwise returns 0. +| signaling NaN; otherwise returns 0. This slightly differs from the same +| function for other types as floatx80 has an explicit bit. *----------------------------------------------------------------------------*/ int floatx80_is_signaling_nan( floatx80 a ) { #if SNAN_BIT_IS_ONE - return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 ); + return ( ( a.high & 0x7FFF ) == 0x7FFF ) + && (LIT64( 0x8000000000000000 ) <= ((uint64_t) ( a.low<<1 ))); #else - bits64 aLow; + uint64_t aLow; aLow = a.low & ~ LIT64( 0x4000000000000000 ); return ( ( a.high & 0x7FFF ) == 0x7FFF ) - && (bits64) ( aLow<<1 ) + && (uint64_t) ( aLow<<1 ) && ( a.low == aLow ); #endif } /*---------------------------------------------------------------------------- +| Returns a quiet NaN if the extended double-precision floating point value +| `a' is a signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_maybe_silence_nan( floatx80 a ) +{ + if (floatx80_is_signaling_nan(a)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + a.low |= LIT64( 0xC000000000000000 ); + return a; +#endif + } + return a; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the extended double-precision floating- | point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the | invalid exception is raised. @@ -385,9 +603,15 @@ static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM) commonNaNT z; if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR); - z.sign = a.high>>15; - z.low = 0; - z.high = a.low; + if ( a.low >> 63 ) { + z.sign = a.high >> 15; + z.low = 0; + z.high = a.low << 1; + } else { + z.sign = floatx80_default_nan_high >> 15; + z.low = 0; + z.high = floatx80_default_nan_low << 1; + } return z; } @@ -396,15 +620,24 @@ static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM) | double-precision floating-point format. *----------------------------------------------------------------------------*/ -static floatx80 commonNaNToFloatx80( commonNaNT a ) +static floatx80 commonNaNToFloatx80( commonNaNT a STATUS_PARAM) { floatx80 z; - if (a.high) - z.low = a.high; - else + if ( STATUS(default_nan_mode) ) { z.low = floatx80_default_nan_low; - z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF; + z.high = floatx80_default_nan_high; + return z; + } + + if (a.high >> 1) { + z.low = LIT64( 0x8000000000000000 ) | a.high >> 1; + z.high = ( ( (uint16_t) a.sign )<<15 ) | 0x7FFF; + } else { + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + } + return z; } @@ -416,7 +649,15 @@ static floatx80 commonNaNToFloatx80( commonNaNT a ) static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + + aIsQuietNaN = floatx80_is_quiet_nan( a ); + aIsSignalingNaN = floatx80_is_signaling_nan( a ); + bIsQuietNaN = floatx80_is_quiet_nan( b ); + bIsSignalingNaN = floatx80_is_signaling_nan( b ); + + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); if ( STATUS(default_nan_mode) ) { a.low = floatx80_default_nan_low; @@ -424,31 +665,19 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) return a; } - aIsNaN = floatx80_is_nan( a ); - aIsSignalingNaN = floatx80_is_signaling_nan( a ); - bIsNaN = floatx80_is_nan( b ); - bIsSignalingNaN = floatx80_is_signaling_nan( b ); -#if SNAN_BIT_IS_ONE - a.low &= ~LIT64( 0xC000000000000000 ); - b.low &= ~LIT64( 0xC000000000000000 ); -#else - a.low |= LIT64( 0xC000000000000000 ); - b.low |= LIT64( 0xC000000000000000 ); -#endif - if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - return bIsNaN ? b : a; + if (a.low < b.low) { + aIsLargerSignificand = 0; + } else if (b.low < a.low) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) return a; - returnLargerSignificand: - if ( a.low < b.low ) return b; - if ( b.low < a.low ) return a; - return ( a.high < b.high ) ? a : b; - } - else { - return b; + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, + aIsLargerSignificand)) { + return floatx80_maybe_silence_nan(b); + } else { + return floatx80_maybe_silence_nan(a); } } @@ -457,23 +686,11 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) #ifdef FLOAT128 /*---------------------------------------------------------------------------- -| The pattern for a default generated quadruple-precision NaN. The `high' and -| `low' values hold the most- and least-significant bits, respectively. -*----------------------------------------------------------------------------*/ -#if SNAN_BIT_IS_ONE -#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF ) -#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) -#else -#define float128_default_nan_high LIT64( 0xFFFF800000000000 ) -#define float128_default_nan_low LIT64( 0x0000000000000000 ) -#endif - -/*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ -int float128_is_nan( float128 a ) +int float128_is_quiet_nan( float128 a ) { #if SNAN_BIT_IS_ONE return @@ -481,7 +698,7 @@ int float128_is_nan( float128 a ) && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) ); #else return - ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) ) + ( LIT64( 0xFFFE000000000000 ) <= (uint64_t) ( a.high<<1 ) ) && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) ); #endif } @@ -495,7 +712,7 @@ int float128_is_signaling_nan( float128 a ) { #if SNAN_BIT_IS_ONE return - ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) ) + ( LIT64( 0xFFFE000000000000 ) <= (uint64_t) ( a.high<<1 ) ) && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) ); #else return @@ -505,6 +722,29 @@ int float128_is_signaling_nan( float128 a ) } /*---------------------------------------------------------------------------- +| Returns a quiet NaN if the quadruple-precision floating point value `a' is +| a signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float128 float128_maybe_silence_nan( float128 a ) +{ + if (float128_is_signaling_nan(a)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + a.low = float128_default_nan_low; + a.high = float128_default_nan_high; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + a.high |= LIT64( 0x0000800000000000 ); + return a; +#endif + } + return a; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the quadruple-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid | exception is raised. @@ -525,12 +765,18 @@ static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float128 commonNaNToFloat128( commonNaNT a ) +static float128 commonNaNToFloat128( commonNaNT a STATUS_PARAM) { float128 z; + if ( STATUS(default_nan_mode) ) { + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + shift128Right( a.high, a.low, 16, &z.high, &z.low ); - z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF000000000000 ); + z.high |= ( ( (uint64_t) a.sign )<<63 ) | LIT64( 0x7FFF000000000000 ); return z; } @@ -542,7 +788,15 @@ static float128 commonNaNToFloat128( commonNaNT a ) static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + + aIsQuietNaN = float128_is_quiet_nan( a ); + aIsSignalingNaN = float128_is_signaling_nan( a ); + bIsQuietNaN = float128_is_quiet_nan( b ); + bIsSignalingNaN = float128_is_signaling_nan( b ); + + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); if ( STATUS(default_nan_mode) ) { a.low = float128_default_nan_low; @@ -550,31 +804,19 @@ static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) return a; } - aIsNaN = float128_is_nan( a ); - aIsSignalingNaN = float128_is_signaling_nan( a ); - bIsNaN = float128_is_nan( b ); - bIsSignalingNaN = float128_is_signaling_nan( b ); -#if SNAN_BIT_IS_ONE - a.high &= ~LIT64( 0x0000800000000000 ); - b.high &= ~LIT64( 0x0000800000000000 ); -#else - a.high |= LIT64( 0x0000800000000000 ); - b.high |= LIT64( 0x0000800000000000 ); -#endif - if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - return bIsNaN ? b : a; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) return a; - returnLargerSignificand: - if ( lt128( a.high<<1, a.low, b.high<<1, b.low ) ) return b; - if ( lt128( b.high<<1, b.low, a.high<<1, a.low ) ) return a; - return ( a.high < b.high ) ? a : b; + if (lt128(a.high<<1, a.low, b.high<<1, b.low)) { + aIsLargerSignificand = 0; + } else if (lt128(b.high<<1, b.low, a.high<<1, a.low)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - else { - return b; + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, + aIsLargerSignificand)) { + return float128_maybe_silence_nan(b); + } else { + return float128_maybe_silence_nan(a); } } diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 0b82797..baba1dc 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1,3 +1,8 @@ +/* + * QEMU float support + * + * Derived from SoftFloat. + */ /*============================================================================ @@ -30,8 +35,6 @@ these four paragraphs for those parts of this code that are retained. =============================================================================*/ -/* FIXME: Flush-To-Zero only effects results. Denormal inputs should also - be flushed to zero. */ #include "softfloat.h" /*---------------------------------------------------------------------------- @@ -69,6 +72,33 @@ void set_floatx80_rounding_precision(int val STATUS_PARAM) #endif /*---------------------------------------------------------------------------- +| Returns the fraction bits of the half-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE uint32_t extractFloat16Frac(float16 a) +{ + return float16_val(a) & 0x3ff; +} + +/*---------------------------------------------------------------------------- +| Returns the exponent bits of the half-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE int16 extractFloat16Exp(float16 a) +{ + return (float16_val(a) >> 10) & 0x1f; +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE flag extractFloat16Sign(float16 a) +{ + return float16_val(a)>>15; +} + +/*---------------------------------------------------------------------------- | Takes a 64-bit fixed-point value `absZ' with binary point between bits 6 | and 7, and returns the properly rounded 32-bit integer corresponding to the | input. If `zSign' is 1, the input is negated before being converted to an @@ -79,7 +109,7 @@ void set_floatx80_rounding_precision(int val STATUS_PARAM) | positive or negative integer is returned. *----------------------------------------------------------------------------*/ -static int32 roundAndPackInt32( flag zSign, bits64 absZ STATUS_PARAM) +static int32 roundAndPackInt32( flag zSign, uint64_t absZ STATUS_PARAM) { int8 roundingMode; flag roundNearestEven; @@ -110,7 +140,7 @@ static int32 roundAndPackInt32( flag zSign, bits64 absZ STATUS_PARAM) if ( zSign ) z = - z; if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) { float_raise( float_flag_invalid STATUS_VAR); - return zSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + return zSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; return z; @@ -129,7 +159,7 @@ static int32 roundAndPackInt32( flag zSign, bits64 absZ STATUS_PARAM) | returned. *----------------------------------------------------------------------------*/ -static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PARAM) +static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATUS_PARAM) { int8 roundingMode; flag roundNearestEven, increment; @@ -137,7 +167,7 @@ static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PA roundingMode = STATUS(float_rounding_mode); roundNearestEven = ( roundingMode == float_round_nearest_even ); - increment = ( (sbits64) absZ1 < 0 ); + increment = ( (int64_t) absZ1 < 0 ); if ( ! roundNearestEven ) { if ( roundingMode == float_round_to_zero ) { increment = 0; @@ -154,7 +184,7 @@ static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PA if ( increment ) { ++absZ0; if ( absZ0 == 0 ) goto overflow; - absZ0 &= ~ ( ( (bits64) ( absZ1<<1 ) == 0 ) & roundNearestEven ); + absZ0 &= ~ ( ( (uint64_t) ( absZ1<<1 ) == 0 ) & roundNearestEven ); } z = absZ0; if ( zSign ) z = - z; @@ -162,7 +192,7 @@ static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PA overflow: float_raise( float_flag_invalid STATUS_VAR); return - zSign ? (sbits64) LIT64( 0x8000000000000000 ) + zSign ? (int64_t) LIT64( 0x8000000000000000 ) : LIT64( 0x7FFFFFFFFFFFFFFF ); } if ( absZ1 ) STATUS(float_exception_flags) |= float_flag_inexact; @@ -174,7 +204,7 @@ static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PA | Returns the fraction bits of the single-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE bits32 extractFloat32Frac( float32 a ) +INLINE uint32_t extractFloat32Frac( float32 a ) { return float32_val(a) & 0x007FFFFF; @@ -204,6 +234,21 @@ INLINE flag extractFloat32Sign( float32 a ) } /*---------------------------------------------------------------------------- +| If `a' is denormal and we are in flush-to-zero mode then set the +| input-denormal exception and return zero. Otherwise just return the value. +*----------------------------------------------------------------------------*/ +static float32 float32_squash_input_denormal(float32 a STATUS_PARAM) +{ + if (STATUS(flush_inputs_to_zero)) { + if (extractFloat32Exp(a) == 0 && extractFloat32Frac(a) != 0) { + float_raise(float_flag_input_denormal STATUS_VAR); + return make_float32(float32_val(a) & 0x80000000); + } + } + return a; +} + +/*---------------------------------------------------------------------------- | Normalizes the subnormal single-precision floating-point value represented | by the denormalized significand `aSig'. The normalized exponent and | significand are stored at the locations pointed to by `zExpPtr' and @@ -211,7 +256,7 @@ INLINE flag extractFloat32Sign( float32 a ) *----------------------------------------------------------------------------*/ static void - normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr ) + normalizeFloat32Subnormal( uint32_t aSig, int16 *zExpPtr, uint32_t *zSigPtr ) { int8 shiftCount; @@ -232,11 +277,11 @@ static void | significand. *----------------------------------------------------------------------------*/ -INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig ) +INLINE float32 packFloat32( flag zSign, int16 zExp, uint32_t zSig ) { return make_float32( - ( ( (bits32) zSign )<<31 ) + ( ( (bits32) zExp )<<23 ) + zSig); + ( ( (uint32_t) zSign )<<31 ) + ( ( (uint32_t) zExp )<<23 ) + zSig); } @@ -262,7 +307,7 @@ INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig ) | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM) +static float32 roundAndPackFloat32( flag zSign, int16 zExp, uint32_t zSig STATUS_PARAM) { int8 roundingMode; flag roundNearestEven; @@ -287,10 +332,10 @@ static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_P } } roundBits = zSig & 0x7F; - if ( 0xFD <= (bits16) zExp ) { + if ( 0xFD <= (uint16_t) zExp ) { if ( ( 0xFD < zExp ) || ( ( zExp == 0xFD ) - && ( (sbits32) ( zSig + roundIncrement ) < 0 ) ) + && ( (int32_t) ( zSig + roundIncrement ) < 0 ) ) ) { float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); return packFloat32( zSign, 0xFF, - ( roundIncrement == 0 )); @@ -325,7 +370,7 @@ static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_P *----------------------------------------------------------------------------*/ static float32 - normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM) + normalizeRoundAndPackFloat32( flag zSign, int16 zExp, uint32_t zSig STATUS_PARAM) { int8 shiftCount; @@ -338,7 +383,7 @@ static float32 | Returns the fraction bits of the double-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE bits64 extractFloat64Frac( float64 a ) +INLINE uint64_t extractFloat64Frac( float64 a ) { return float64_val(a) & LIT64( 0x000FFFFFFFFFFFFF ); @@ -368,6 +413,21 @@ INLINE flag extractFloat64Sign( float64 a ) } /*---------------------------------------------------------------------------- +| If `a' is denormal and we are in flush-to-zero mode then set the +| input-denormal exception and return zero. Otherwise just return the value. +*----------------------------------------------------------------------------*/ +static float64 float64_squash_input_denormal(float64 a STATUS_PARAM) +{ + if (STATUS(flush_inputs_to_zero)) { + if (extractFloat64Exp(a) == 0 && extractFloat64Frac(a) != 0) { + float_raise(float_flag_input_denormal STATUS_VAR); + return make_float64(float64_val(a) & (1ULL << 63)); + } + } + return a; +} + +/*---------------------------------------------------------------------------- | Normalizes the subnormal double-precision floating-point value represented | by the denormalized significand `aSig'. The normalized exponent and | significand are stored at the locations pointed to by `zExpPtr' and @@ -375,7 +435,7 @@ INLINE flag extractFloat64Sign( float64 a ) *----------------------------------------------------------------------------*/ static void - normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr ) + normalizeFloat64Subnormal( uint64_t aSig, int16 *zExpPtr, uint64_t *zSigPtr ) { int8 shiftCount; @@ -396,11 +456,11 @@ static void | significand. *----------------------------------------------------------------------------*/ -INLINE float64 packFloat64( flag zSign, int16 zExp, bits64 zSig ) +INLINE float64 packFloat64( flag zSign, int16 zExp, uint64_t zSig ) { return make_float64( - ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<52 ) + zSig); + ( ( (uint64_t) zSign )<<63 ) + ( ( (uint64_t) zExp )<<52 ) + zSig); } @@ -426,7 +486,7 @@ INLINE float64 packFloat64( flag zSign, int16 zExp, bits64 zSig ) | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM) +static float64 roundAndPackFloat64( flag zSign, int16 zExp, uint64_t zSig STATUS_PARAM) { int8 roundingMode; flag roundNearestEven; @@ -451,10 +511,10 @@ static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_P } } roundBits = zSig & 0x3FF; - if ( 0x7FD <= (bits16) zExp ) { + if ( 0x7FD <= (uint16_t) zExp ) { if ( ( 0x7FD < zExp ) || ( ( zExp == 0x7FD ) - && ( (sbits64) ( zSig + roundIncrement ) < 0 ) ) + && ( (int64_t) ( zSig + roundIncrement ) < 0 ) ) ) { float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); return packFloat64( zSign, 0x7FF, - ( roundIncrement == 0 )); @@ -489,7 +549,7 @@ static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_P *----------------------------------------------------------------------------*/ static float64 - normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM) + normalizeRoundAndPackFloat64( flag zSign, int16 zExp, uint64_t zSig STATUS_PARAM) { int8 shiftCount; @@ -505,7 +565,7 @@ static float64 | value `a'. *----------------------------------------------------------------------------*/ -INLINE bits64 extractFloatx80Frac( floatx80 a ) +INLINE uint64_t extractFloatx80Frac( floatx80 a ) { return a.low; @@ -544,7 +604,7 @@ INLINE flag extractFloatx80Sign( floatx80 a ) *----------------------------------------------------------------------------*/ static void - normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr ) + normalizeFloatx80Subnormal( uint64_t aSig, int32 *zExpPtr, uint64_t *zSigPtr ) { int8 shiftCount; @@ -559,12 +619,12 @@ static void | extended double-precision floating-point value, returning the result. *----------------------------------------------------------------------------*/ -INLINE floatx80 packFloatx80( flag zSign, int32 zExp, bits64 zSig ) +INLINE floatx80 packFloatx80( flag zSign, int32 zExp, uint64_t zSig ) { floatx80 z; z.low = zSig; - z.high = ( ( (bits16) zSign )<<15 ) + zExp; + z.high = ( ( (uint16_t) zSign )<<15 ) + zExp; return z; } @@ -595,7 +655,7 @@ INLINE floatx80 packFloatx80( flag zSign, int32 zExp, bits64 zSig ) static floatx80 roundAndPackFloatx80( - int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 + int8 roundingPrecision, flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 STATUS_PARAM) { int8 roundingMode; @@ -632,7 +692,7 @@ static floatx80 } } roundBits = zSig0 & roundMask; - if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) { + if ( 0x7FFD <= (uint32_t) ( zExp - 1 ) ) { if ( ( 0x7FFE < zExp ) || ( ( zExp == 0x7FFE ) && ( zSig0 + roundIncrement < zSig0 ) ) ) { @@ -650,7 +710,7 @@ static floatx80 if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR); if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; zSig0 += roundIncrement; - if ( (sbits64) zSig0 < 0 ) zExp = 1; + if ( (int64_t) zSig0 < 0 ) zExp = 1; roundIncrement = roundMask + 1; if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) { roundMask |= roundIncrement; @@ -673,7 +733,7 @@ static floatx80 if ( zSig0 == 0 ) zExp = 0; return packFloatx80( zSign, zExp, zSig0 ); precision80: - increment = ( (sbits64) zSig1 < 0 ); + increment = ( (int64_t) zSig1 < 0 ); if ( ! roundNearestEven ) { if ( roundingMode == float_round_to_zero ) { increment = 0; @@ -687,7 +747,7 @@ static floatx80 } } } - if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) { + if ( 0x7FFD <= (uint32_t) ( zExp - 1 ) ) { if ( ( 0x7FFE < zExp ) || ( ( zExp == 0x7FFE ) && ( zSig0 == LIT64( 0xFFFFFFFFFFFFFFFF ) ) @@ -716,7 +776,7 @@ static floatx80 if ( isTiny && zSig1 ) float_raise( float_flag_underflow STATUS_VAR); if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact; if ( roundNearestEven ) { - increment = ( (sbits64) zSig1 < 0 ); + increment = ( (int64_t) zSig1 < 0 ); } else { if ( zSign ) { @@ -729,8 +789,8 @@ static floatx80 if ( increment ) { ++zSig0; zSig0 &= - ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven ); - if ( (sbits64) zSig0 < 0 ) zExp = 1; + ~ ( ( (uint64_t) ( zSig1<<1 ) == 0 ) & roundNearestEven ); + if ( (int64_t) zSig0 < 0 ) zExp = 1; } return packFloatx80( zSign, zExp, zSig0 ); } @@ -743,7 +803,7 @@ static floatx80 zSig0 = LIT64( 0x8000000000000000 ); } else { - zSig0 &= ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven ); + zSig0 &= ~ ( ( (uint64_t) ( zSig1<<1 ) == 0 ) & roundNearestEven ); } } else { @@ -764,7 +824,7 @@ static floatx80 static floatx80 normalizeRoundAndPackFloatx80( - int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 + int8 roundingPrecision, flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 STATUS_PARAM) { int8 shiftCount; @@ -791,7 +851,7 @@ static floatx80 | floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE bits64 extractFloat128Frac1( float128 a ) +INLINE uint64_t extractFloat128Frac1( float128 a ) { return a.low; @@ -803,7 +863,7 @@ INLINE bits64 extractFloat128Frac1( float128 a ) | floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE bits64 extractFloat128Frac0( float128 a ) +INLINE uint64_t extractFloat128Frac0( float128 a ) { return a.high & LIT64( 0x0000FFFFFFFFFFFF ); @@ -845,11 +905,11 @@ INLINE flag extractFloat128Sign( float128 a ) static void normalizeFloat128Subnormal( - bits64 aSig0, - bits64 aSig1, + uint64_t aSig0, + uint64_t aSig1, int32 *zExpPtr, - bits64 *zSig0Ptr, - bits64 *zSig1Ptr + uint64_t *zSig0Ptr, + uint64_t *zSig1Ptr ) { int8 shiftCount; @@ -888,12 +948,12 @@ static void *----------------------------------------------------------------------------*/ INLINE float128 - packFloat128( flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 ) + packFloat128( flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 ) { float128 z; z.low = zSig1; - z.high = ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<48 ) + zSig0; + z.high = ( ( (uint64_t) zSign )<<63 ) + ( ( (uint64_t) zExp )<<48 ) + zSig0; return z; } @@ -921,14 +981,14 @@ INLINE float128 static float128 roundAndPackFloat128( - flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1, bits64 zSig2 STATUS_PARAM) + flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1, uint64_t zSig2 STATUS_PARAM) { int8 roundingMode; flag roundNearestEven, increment, isTiny; roundingMode = STATUS(float_rounding_mode); roundNearestEven = ( roundingMode == float_round_nearest_even ); - increment = ( (sbits64) zSig2 < 0 ); + increment = ( (int64_t) zSig2 < 0 ); if ( ! roundNearestEven ) { if ( roundingMode == float_round_to_zero ) { increment = 0; @@ -942,7 +1002,7 @@ static float128 } } } - if ( 0x7FFD <= (bits32) zExp ) { + if ( 0x7FFD <= (uint32_t) zExp ) { if ( ( 0x7FFD < zExp ) || ( ( zExp == 0x7FFD ) && eq128( @@ -986,7 +1046,7 @@ static float128 zExp = 0; if ( isTiny && zSig2 ) float_raise( float_flag_underflow STATUS_VAR); if ( roundNearestEven ) { - increment = ( (sbits64) zSig2 < 0 ); + increment = ( (int64_t) zSig2 < 0 ); } else { if ( zSign ) { @@ -1022,10 +1082,10 @@ static float128 static float128 normalizeRoundAndPackFloat128( - flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 STATUS_PARAM) + flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 STATUS_PARAM) { int8 shiftCount; - bits64 zSig2; + uint64_t zSig2; if ( zSig0 == 0 ) { zSig0 = zSig1; @@ -1059,7 +1119,7 @@ float32 int32_to_float32( int32 a STATUS_PARAM ) flag zSign; if ( a == 0 ) return float32_zero; - if ( a == (sbits32) 0x80000000 ) return packFloat32( 1, 0x9E, 0 ); + if ( a == (int32_t) 0x80000000 ) return packFloat32( 1, 0x9E, 0 ); zSign = ( a < 0 ); return normalizeRoundAndPackFloat32( zSign, 0x9C, zSign ? - a : a STATUS_VAR ); @@ -1076,7 +1136,7 @@ float64 int32_to_float64( int32 a STATUS_PARAM ) flag zSign; uint32 absA; int8 shiftCount; - bits64 zSig; + uint64_t zSig; if ( a == 0 ) return float64_zero; zSign = ( a < 0 ); @@ -1101,7 +1161,7 @@ floatx80 int32_to_floatx80( int32 a STATUS_PARAM ) flag zSign; uint32 absA; int8 shiftCount; - bits64 zSig; + uint64_t zSig; if ( a == 0 ) return packFloatx80( 0, 0, 0 ); zSign = ( a < 0 ); @@ -1127,7 +1187,7 @@ float128 int32_to_float128( int32 a STATUS_PARAM ) flag zSign; uint32 absA; int8 shiftCount; - bits64 zSig0; + uint64_t zSig0; if ( a == 0 ) return packFloat128( 0, 0, 0, 0 ); zSign = ( a < 0 ); @@ -1204,7 +1264,7 @@ float64 int64_to_float64( int64 a STATUS_PARAM ) flag zSign; if ( a == 0 ) return float64_zero; - if ( a == (sbits64) LIT64( 0x8000000000000000 ) ) { + if ( a == (int64_t) LIT64( 0x8000000000000000 ) ) { return packFloat64( 1, 0x43E, 0 ); } zSign = ( a < 0 ); @@ -1258,7 +1318,7 @@ float128 int64_to_float128( int64 a STATUS_PARAM ) uint64 absA; int8 shiftCount; int32 zExp; - bits64 zSig0, zSig1; + uint64_t zSig0, zSig1; if ( a == 0 ) return packFloat128( 0, 0, 0, 0 ); zSign = ( a < 0 ); @@ -1295,9 +1355,10 @@ int32 float32_to_int32( float32 a STATUS_PARAM ) { flag aSign; int16 aExp, shiftCount; - bits32 aSig; - bits64 aSig64; + uint32_t aSig; + uint64_t aSig64; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -1325,8 +1386,9 @@ int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM ) { flag aSign; int16 aExp, shiftCount; - bits32 aSig; + uint32_t aSig; int32 z; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1337,7 +1399,7 @@ int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM ) float_raise( float_flag_invalid STATUS_VAR); if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) return 0x7FFFFFFF; } - return (sbits32) 0x80000000; + return (int32_t) 0x80000000; } else if ( aExp <= 0x7E ) { if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; @@ -1345,7 +1407,7 @@ int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM ) } aSig = ( aSig | 0x00800000 )<<8; z = aSig>>( - shiftCount ); - if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) { + if ( (uint32_t) ( aSig<<( shiftCount & 31 ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } if ( aSign ) z = - z; @@ -1355,6 +1417,55 @@ int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM ) /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point value +| `a' to the 16-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int16 float32_to_int16_round_to_zero( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + uint32_t aSig; + int32 z; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + shiftCount = aExp - 0x8E; + if ( 0 <= shiftCount ) { + if ( float32_val(a) != 0xC7000000 ) { + float_raise( float_flag_invalid STATUS_VAR); + if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { + return 0x7FFF; + } + } + return (int32_t) 0xffff8000; + } + else if ( aExp <= 0x7E ) { + if ( aExp | aSig ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + return 0; + } + shiftCount -= 0x10; + aSig = ( aSig | 0x00800000 )<<8; + z = aSig>>( - shiftCount ); + if ( (uint32_t) ( aSig<<( shiftCount & 31 ) ) ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + if ( aSign ) { + z = - z; + } + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value | `a' to the 64-bit two's complement integer format. The conversion is | performed according to the IEC/IEEE Standard for Binary Floating-Point | Arithmetic---which means in particular that the conversion is rounded @@ -1367,8 +1478,9 @@ int64 float32_to_int64( float32 a STATUS_PARAM ) { flag aSign; int16 aExp, shiftCount; - bits32 aSig; - bits64 aSig64, aSigExtra; + uint32_t aSig; + uint64_t aSig64, aSigExtra; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1379,7 +1491,7 @@ int64 float32_to_int64( float32 a STATUS_PARAM ) if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } if ( aExp ) aSig |= 0x00800000; aSig64 = aSig; @@ -1403,9 +1515,10 @@ int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM ) { flag aSign; int16 aExp, shiftCount; - bits32 aSig; - bits64 aSig64; + uint32_t aSig; + uint64_t aSig64; int64 z; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1418,7 +1531,7 @@ int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM ) return LIT64( 0x7FFFFFFFFFFFFFFF ); } } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } else if ( aExp <= 0x7E ) { if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; @@ -1427,7 +1540,7 @@ int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM ) aSig64 = aSig | 0x00800000; aSig64 <<= 40; z = aSig64>>( - shiftCount ); - if ( (bits64) ( aSig64<<( shiftCount & 63 ) ) ) { + if ( (uint64_t) ( aSig64<<( shiftCount & 63 ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } if ( aSign ) z = - z; @@ -1446,13 +1559,14 @@ float64 float32_to_float64( float32 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR )); + if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat64( aSign, 0x7FF, 0 ); } if ( aExp == 0 ) { @@ -1460,7 +1574,7 @@ float64 float32_to_float64( float32 a STATUS_PARAM ) normalizeFloat32Subnormal( aSig, &aExp, &aSig ); --aExp; } - return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 ); + return packFloat64( aSign, aExp + 0x380, ( (uint64_t) aSig )<<29 ); } @@ -1477,13 +1591,14 @@ floatx80 float32_to_floatx80( float32 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) { @@ -1491,7 +1606,7 @@ floatx80 float32_to_floatx80( float32 a STATUS_PARAM ) normalizeFloat32Subnormal( aSig, &aExp, &aSig ); } aSig |= 0x00800000; - return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 ); + return packFloatx80( aSign, aExp + 0x3F80, ( (uint64_t) aSig )<<40 ); } @@ -1510,13 +1625,14 @@ float128 float32_to_float128( float32 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat128( aSign, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -1524,7 +1640,7 @@ float128 float32_to_float128( float32 a STATUS_PARAM ) normalizeFloat32Subnormal( aSig, &aExp, &aSig ); --aExp; } - return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 ); + return packFloat128( aSign, aExp + 0x3F80, ( (uint64_t) aSig )<<25, 0 ); } @@ -1541,9 +1657,10 @@ float32 float32_round_to_int( float32 a STATUS_PARAM) { flag aSign; int16 aExp; - bits32 lastBitMask, roundBitsMask; + uint32_t lastBitMask, roundBitsMask; int8 roundingMode; - bits32 z; + uint32_t z; + a = float32_squash_input_denormal(a STATUS_VAR); aExp = extractFloat32Exp( a ); if ( 0x96 <= aExp ) { @@ -1553,7 +1670,7 @@ float32 float32_round_to_int( float32 a STATUS_PARAM) return a; } if ( aExp <= 0x7E ) { - if ( (bits32) ( float32_val(a)<<1 ) == 0 ) return a; + if ( (uint32_t) ( float32_val(a)<<1 ) == 0 ) return a; STATUS(float_exception_flags) |= float_flag_inexact; aSign = extractFloat32Sign( a ); switch ( STATUS(float_rounding_mode) ) { @@ -1600,7 +1717,7 @@ float32 float32_round_to_int( float32 a STATUS_PARAM) static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) { int16 aExp, bExp, zExp; - bits32 aSig, bSig, zSig; + uint32_t aSig, bSig, zSig; int16 expDiff; aSig = extractFloat32Frac( a ); @@ -1654,7 +1771,7 @@ static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) aSig |= 0x20000000; zSig = ( aSig + bSig )<<1; --zExp; - if ( (sbits32) zSig < 0 ) { + if ( (int32_t) zSig < 0 ) { zSig = aSig + bSig; ++zExp; } @@ -1674,7 +1791,7 @@ static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) { int16 aExp, bExp, zExp; - bits32 aSig, bSig, zSig; + uint32_t aSig, bSig, zSig; int16 expDiff; aSig = extractFloat32Frac( a ); @@ -1747,6 +1864,8 @@ static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) float32 float32_add( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); aSign = extractFloat32Sign( a ); bSign = extractFloat32Sign( b ); @@ -1768,6 +1887,8 @@ float32 float32_add( float32 a, float32 b STATUS_PARAM ) float32 float32_sub( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); aSign = extractFloat32Sign( a ); bSign = extractFloat32Sign( b ); @@ -1790,9 +1911,12 @@ float32 float32_mul( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign, zSign; int16 aExp, bExp, zExp; - bits32 aSig, bSig; - bits64 zSig64; - bits32 zSig; + uint32_t aSig, bSig; + uint64_t zSig64; + uint32_t zSig; + + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1830,9 +1954,9 @@ float32 float32_mul( float32 a, float32 b STATUS_PARAM ) zExp = aExp + bExp - 0x7F; aSig = ( aSig | 0x00800000 )<<7; bSig = ( bSig | 0x00800000 )<<8; - shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 ); + shift64RightJamming( ( (uint64_t) aSig ) * bSig, 32, &zSig64 ); zSig = zSig64; - if ( 0 <= (sbits32) ( zSig<<1 ) ) { + if ( 0 <= (int32_t) ( zSig<<1 ) ) { zSig <<= 1; --zExp; } @@ -1850,7 +1974,9 @@ float32 float32_div( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign, zSign; int16 aExp, bExp, zExp; - bits32 aSig, bSig, zSig; + uint32_t aSig, bSig, zSig; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1894,9 +2020,9 @@ float32 float32_div( float32 a, float32 b STATUS_PARAM ) aSig >>= 1; ++zExp; } - zSig = ( ( (bits64) aSig )<<32 ) / bSig; + zSig = ( ( (uint64_t) aSig )<<32 ) / bSig; if ( ( zSig & 0x3F ) == 0 ) { - zSig |= ( (bits64) bSig * zSig != ( (bits64) aSig )<<32 ); + zSig |= ( (uint64_t) bSig * zSig != ( (uint64_t) aSig )<<32 ); } return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); @@ -1912,11 +2038,13 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) { flag aSign, zSign; int16 aExp, bExp, expDiff; - bits32 aSig, bSig; - bits32 q; - bits64 aSig64, bSig64, q64; - bits32 alternateASig; - sbits32 sigMean; + uint32_t aSig, bSig; + uint32_t q; + uint64_t aSig64, bSig64, q64; + uint32_t alternateASig; + int32_t sigMean; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1958,7 +2086,7 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) q = ( bSig <= aSig ); if ( q ) aSig -= bSig; if ( 0 < expDiff ) { - q = ( ( (bits64) aSig )<<32 ) / bSig; + q = ( ( (uint64_t) aSig )<<32 ) / bSig; q >>= 32 - expDiff; bSig >>= 2; aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q; @@ -1970,8 +2098,8 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) } else { if ( bSig <= aSig ) aSig -= bSig; - aSig64 = ( (bits64) aSig )<<40; - bSig64 = ( (bits64) bSig )<<40; + aSig64 = ( (uint64_t) aSig )<<40; + bSig64 = ( (uint64_t) bSig )<<40; expDiff -= 64; while ( 0 < expDiff ) { q64 = estimateDiv128To64( aSig64, 0, bSig64 ); @@ -1990,12 +2118,12 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) alternateASig = aSig; ++q; aSig -= bSig; - } while ( 0 <= (sbits32) aSig ); + } while ( 0 <= (int32_t) aSig ); sigMean = aSig + alternateASig; if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) { aSig = alternateASig; } - zSign = ( (sbits32) aSig < 0 ); + zSign = ( (int32_t) aSig < 0 ); if ( zSign ) aSig = - aSig; return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig STATUS_VAR ); @@ -2011,8 +2139,9 @@ float32 float32_sqrt( float32 a STATUS_PARAM ) { flag aSign; int16 aExp, zExp; - bits32 aSig, zSig; - bits64 rem, term; + uint32_t aSig, zSig; + uint64_t rem, term; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -2041,11 +2170,11 @@ float32 float32_sqrt( float32 a STATUS_PARAM ) goto roundAndPack; } aSig >>= aExp & 1; - term = ( (bits64) zSig ) * zSig; - rem = ( ( (bits64) aSig )<<32 ) - term; - while ( (sbits64) rem < 0 ) { + term = ( (uint64_t) zSig ) * zSig; + rem = ( ( (uint64_t) aSig )<<32 ) - term; + while ( (int64_t) rem < 0 ) { --zSig; - rem += ( ( (bits64) zSig )<<1 ) | 1; + rem += ( ( (uint64_t) zSig )<<1 ) | 1; } zSig |= ( rem != 0 ); } @@ -2075,30 +2204,31 @@ float32 float32_sqrt( float32 a STATUS_PARAM ) static const float64 float32_exp2_coefficients[15] = { - make_float64( 0x3ff0000000000000ll ), /* 1 */ - make_float64( 0x3fe0000000000000ll ), /* 2 */ - make_float64( 0x3fc5555555555555ll ), /* 3 */ - make_float64( 0x3fa5555555555555ll ), /* 4 */ - make_float64( 0x3f81111111111111ll ), /* 5 */ - make_float64( 0x3f56c16c16c16c17ll ), /* 6 */ - make_float64( 0x3f2a01a01a01a01all ), /* 7 */ - make_float64( 0x3efa01a01a01a01all ), /* 8 */ - make_float64( 0x3ec71de3a556c734ll ), /* 9 */ - make_float64( 0x3e927e4fb7789f5cll ), /* 10 */ - make_float64( 0x3e5ae64567f544e4ll ), /* 11 */ - make_float64( 0x3e21eed8eff8d898ll ), /* 12 */ - make_float64( 0x3de6124613a86d09ll ), /* 13 */ - make_float64( 0x3da93974a8c07c9dll ), /* 14 */ - make_float64( 0x3d6ae7f3e733b81fll ), /* 15 */ + const_float64( 0x3ff0000000000000ll ), /* 1 */ + const_float64( 0x3fe0000000000000ll ), /* 2 */ + const_float64( 0x3fc5555555555555ll ), /* 3 */ + const_float64( 0x3fa5555555555555ll ), /* 4 */ + const_float64( 0x3f81111111111111ll ), /* 5 */ + const_float64( 0x3f56c16c16c16c17ll ), /* 6 */ + const_float64( 0x3f2a01a01a01a01all ), /* 7 */ + const_float64( 0x3efa01a01a01a01all ), /* 8 */ + const_float64( 0x3ec71de3a556c734ll ), /* 9 */ + const_float64( 0x3e927e4fb7789f5cll ), /* 10 */ + const_float64( 0x3e5ae64567f544e4ll ), /* 11 */ + const_float64( 0x3e21eed8eff8d898ll ), /* 12 */ + const_float64( 0x3de6124613a86d09ll ), /* 13 */ + const_float64( 0x3da93974a8c07c9dll ), /* 14 */ + const_float64( 0x3d6ae7f3e733b81fll ), /* 15 */ }; float32 float32_exp2( float32 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; float64 r, x, xn; int i; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -2143,8 +2273,9 @@ float32 float32_log2( float32 a STATUS_PARAM ) { flag aSign, zSign; int16 aExp; - bits32 aSig, zSig, i; + uint32_t aSig, zSig, i; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -2168,7 +2299,7 @@ float32 float32_log2( float32 a STATUS_PARAM ) zSig = aExp << 23; for (i = 1 << 22; i > 0; i >>= 1) { - aSig = ( (bits64)aSig * aSig ) >> 23; + aSig = ( (uint64_t)aSig * aSig ) >> 23; if ( aSig & 0x01000000 ) { aSig >>= 1; zSig |= i; @@ -2183,37 +2314,41 @@ float32 float32_log2( float32 a STATUS_PARAM ) /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is equal to -| the corresponding value `b', and 0 otherwise. The comparison is performed +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float32_eq( float32 a, float32 b STATUS_PARAM ) { + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { - if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); - } + float_raise( float_flag_invalid STATUS_VAR); return 0; } - return ( float32_val(a) == float32_val(b) ) || - ( (bits32) ( ( float32_val(a) | float32_val(b) )<<1 ) == 0 ); - + av = float32_val(a); + bv = float32_val(b); + return ( av == bv ) || ( (uint32_t) ( ( av | bv )<<1 ) == 0 ); } /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is less than -| or equal to the corresponding value `b', and 0 otherwise. The comparison -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| or equal to the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float32_le( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; - bits32 av, bv; + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2225,21 +2360,24 @@ int float32_le( float32 a, float32 b STATUS_PARAM ) bSign = extractFloat32Sign( b ); av = float32_val(a); bv = float32_val(b); - if ( aSign != bSign ) return aSign || ( (bits32) ( ( av | bv )<<1 ) == 0 ); + if ( aSign != bSign ) return aSign || ( (uint32_t) ( ( av | bv )<<1 ) == 0 ); return ( av == bv ) || ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is less than -| the corresponding value `b', and 0 otherwise. The comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. The comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float32_lt( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; - bits32 av, bv; + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2251,32 +2389,54 @@ int float32_lt( float32 a, float32 b STATUS_PARAM ) bSign = extractFloat32Sign( b ); av = float32_val(a); bv = float32_val(b); - if ( aSign != bSign ) return aSign && ( (bits32) ( ( av | bv )<<1 ) != 0 ); + if ( aSign != bSign ) return aSign && ( (uint32_t) ( ( av | bv )<<1 ) != 0 ); return ( av != bv ) && ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- -| Returns 1 if the single-precision floating-point value `a' is equal to -| the corresponding value `b', and 0 otherwise. The invalid exception is -| raised if either operand is a NaN. Otherwise, the comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| Returns 1 if the single-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. The invalid exception is raised if either +| operand is a NaN. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_eq_signaling( float32 a, float32 b STATUS_PARAM ) +int float32_unordered( float32 a, float32 b STATUS_PARAM ) { - bits32 av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { float_raise( float_flag_invalid STATUS_VAR); - return 0; + return 1; } - av = float32_val(a); - bv = float32_val(b); - return ( av == bv ) || ( (bits32) ( ( av | bv )<<1 ) == 0 ); + return 0; +} +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. The comparison is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_eq_quiet( float32 a, float32 b STATUS_PARAM ) +{ + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + return ( float32_val(a) == float32_val(b) ) || + ( (uint32_t) ( ( float32_val(a) | float32_val(b) )<<1 ) == 0 ); } /*---------------------------------------------------------------------------- @@ -2289,7 +2449,9 @@ int float32_eq_signaling( float32 a, float32 b STATUS_PARAM ) int float32_le_quiet( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; - bits32 av, bv; + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2303,7 +2465,7 @@ int float32_le_quiet( float32 a, float32 b STATUS_PARAM ) bSign = extractFloat32Sign( b ); av = float32_val(a); bv = float32_val(b); - if ( aSign != bSign ) return aSign || ( (bits32) ( ( av | bv )<<1 ) == 0 ); + if ( aSign != bSign ) return aSign || ( (uint32_t) ( ( av | bv )<<1 ) == 0 ); return ( av == bv ) || ( aSign ^ ( av < bv ) ); } @@ -2318,7 +2480,9 @@ int float32_le_quiet( float32 a, float32 b STATUS_PARAM ) int float32_lt_quiet( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; - bits32 av, bv; + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2332,12 +2496,35 @@ int float32_lt_quiet( float32 a, float32 b STATUS_PARAM ) bSign = extractFloat32Sign( b ); av = float32_val(a); bv = float32_val(b); - if ( aSign != bSign ) return aSign && ( (bits32) ( ( av | bv )<<1 ) != 0 ); + if ( aSign != bSign ) return aSign && ( (uint32_t) ( ( av | bv )<<1 ) != 0 ); return ( av != bv ) && ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. Quiet NaNs do not cause an exception. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_unordered_quiet( float32 a, float32 b STATUS_PARAM ) +{ + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point value | `a' to the 32-bit two's complement integer format. The conversion is | performed according to the IEC/IEEE Standard for Binary Floating-Point @@ -2351,7 +2538,8 @@ int32 float64_to_int32( float64 a STATUS_PARAM ) { flag aSign; int16 aExp, shiftCount; - bits64 aSig; + uint64_t aSig; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -2378,8 +2566,9 @@ int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM ) { flag aSign; int16 aExp, shiftCount; - bits64 aSig, savedASig; + uint64_t aSig, savedASig; int32 z; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -2401,7 +2590,7 @@ int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM ) if ( ( z < 0 ) ^ aSign ) { invalid: float_raise( float_flag_invalid STATUS_VAR); - return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig<<shiftCount ) != savedASig ) { STATUS(float_exception_flags) |= float_flag_inexact; @@ -2412,6 +2601,57 @@ int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM ) /*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point value +| `a' to the 16-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int16 float64_to_int16_round_to_zero( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + uint64_t aSig, savedASig; + int32 z; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( 0x40E < aExp ) { + if ( ( aExp == 0x7FF ) && aSig ) { + aSign = 0; + } + goto invalid; + } + else if ( aExp < 0x3FF ) { + if ( aExp || aSig ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + return 0; + } + aSig |= LIT64( 0x0010000000000000 ); + shiftCount = 0x433 - aExp; + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if ( aSign ) { + z = - z; + } + if ( ( (int16_t)z < 0 ) ^ aSign ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + return aSign ? (int32_t) 0xffff8000 : 0x7FFF; + } + if ( ( aSig<<shiftCount ) != savedASig ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value | `a' to the 64-bit two's complement integer format. The conversion is | performed according to the IEC/IEEE Standard for Binary Floating-Point | Arithmetic---which means in particular that the conversion is rounded @@ -2424,7 +2664,8 @@ int64 float64_to_int64( float64 a STATUS_PARAM ) { flag aSign; int16 aExp, shiftCount; - bits64 aSig, aSigExtra; + uint64_t aSig, aSigExtra; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -2440,7 +2681,7 @@ int64 float64_to_int64( float64 a STATUS_PARAM ) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } aSigExtra = 0; aSig <<= - shiftCount; @@ -2466,8 +2707,9 @@ int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM ) { flag aSign; int16 aExp, shiftCount; - bits64 aSig; + uint64_t aSig; int64 z; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -2485,7 +2727,7 @@ int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM ) return LIT64( 0x7FFFFFFFFFFFFFFF ); } } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } z = aSig<<shiftCount; } @@ -2495,7 +2737,7 @@ int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM ) return 0; } z = aSig>>( - shiftCount ); - if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) { + if ( (uint64_t) ( aSig<<( shiftCount & 63 ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } } @@ -2515,14 +2757,15 @@ float32 float64_to_float32( float64 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits64 aSig; - bits32 zSig; + uint64_t aSig; + uint32_t zSig; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat32( aSign, 0xFF, 0 ); } shift64RightJamming( aSig, 22, &aSig ); @@ -2546,29 +2789,28 @@ float32 float64_to_float32( float64 a STATUS_PARAM ) | than the desired result exponent whenever `zSig' is a complete, normalized | significand. *----------------------------------------------------------------------------*/ -static bits16 packFloat16(flag zSign, int16 zExp, bits16 zSig) +static float16 packFloat16(flag zSign, int16 zExp, uint16_t zSig) { - return (((bits32)zSign) << 15) + (((bits32)zExp) << 10) + zSig; + return make_float16( + (((uint32_t)zSign) << 15) + (((uint32_t)zExp) << 10) + zSig); } /* Half precision floats come in two formats: standard IEEE and "ARM" format. The latter gains extra exponent range by omitting the NaN/Inf encodings. */ - -float32 float16_to_float32( bits16 a, flag ieee STATUS_PARAM ) + +float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM) { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; - aSign = a >> 15; - aExp = (a >> 10) & 0x1f; - aSig = a & 0x3ff; + aSign = extractFloat16Sign(a); + aExp = extractFloat16Exp(a); + aSig = extractFloat16Frac(a); if (aExp == 0x1f && ieee) { if (aSig) { - /* Make sure correct exceptions are raised. */ - float32ToCommonNaN(a STATUS_VAR); - aSig |= 0x200; + return commonNaNToFloat32(float16ToCommonNaN(a STATUS_VAR) STATUS_VAR); } return packFloat32(aSign, 0xff, aSig << 13); } @@ -2586,38 +2828,45 @@ float32 float16_to_float32( bits16 a, flag ieee STATUS_PARAM ) return packFloat32( aSign, aExp + 0x70, aSig << 13); } -bits16 float32_to_float16( float32 a, flag ieee STATUS_PARAM) +float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM) { flag aSign; int16 aExp; - bits32 aSig; - bits32 mask; - bits32 increment; + uint32_t aSig; + uint32_t mask; + uint32_t increment; int8 roundingMode; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { if (aSig) { - /* Make sure correct exceptions are raised. */ - float32ToCommonNaN(a STATUS_VAR); - aSig |= 0x00400000; + /* Input is a NaN */ + float16 r = commonNaNToFloat16( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + if (!ieee) { + return packFloat16(aSign, 0, 0); + } + return r; } - return packFloat16(aSign, 0x1f, aSig >> 13); + /* Infinity */ + if (!ieee) { + float_raise(float_flag_invalid STATUS_VAR); + return packFloat16(aSign, 0x1f, 0x3ff); + } + return packFloat16(aSign, 0x1f, 0); } - if (aExp == 0 && aSign == 0) { + if (aExp == 0 && aSig == 0) { return packFloat16(aSign, 0, 0); } /* Decimal point between bits 22 and 23. */ aSig |= 0x00800000; aExp -= 0x7f; if (aExp < -14) { - mask = 0x007fffff; - if (aExp < -24) { - aExp = -25; - } else { - mask >>= 24 + aExp; + mask = 0x00ffffff; + if (aExp >= -24) { + mask >>= 25 + aExp; } } else { mask = 0x00001fff; @@ -2659,7 +2908,7 @@ bits16 float32_to_float16( float32 a, flag ieee STATUS_PARAM) } } else { if (aExp > 16) { - float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_invalid | float_flag_inexact STATUS_VAR); return packFloat16(aSign, 0x1f, 0x3ff); } } @@ -2686,13 +2935,14 @@ floatx80 float64_to_floatx80( float64 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits64 aSig; + uint64_t aSig; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) { @@ -2720,13 +2970,14 @@ float128 float64_to_float128( float64 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits64 aSig, zSig0, zSig1; + uint64_t aSig, zSig0, zSig1; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat128( aSign, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -2752,9 +3003,10 @@ float64 float64_round_to_int( float64 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits64 lastBitMask, roundBitsMask; + uint64_t lastBitMask, roundBitsMask; int8 roundingMode; - bits64 z; + uint64_t z; + a = float64_squash_input_denormal(a STATUS_VAR); aExp = extractFloat64Exp( a ); if ( 0x433 <= aExp ) { @@ -2764,7 +3016,7 @@ float64 float64_round_to_int( float64 a STATUS_PARAM ) return a; } if ( aExp < 0x3FF ) { - if ( (bits64) ( float64_val(a)<<1 ) == 0 ) return a; + if ( (uint64_t) ( float64_val(a)<<1 ) == 0 ) return a; STATUS(float_exception_flags) |= float_flag_inexact; aSign = extractFloat64Sign( a ); switch ( STATUS(float_rounding_mode) ) { @@ -2824,7 +3076,7 @@ float64 float64_trunc_to_int( float64 a STATUS_PARAM) static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) { int16 aExp, bExp, zExp; - bits64 aSig, bSig, zSig; + uint64_t aSig, bSig, zSig; int16 expDiff; aSig = extractFloat64Frac( a ); @@ -2878,7 +3130,7 @@ static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) aSig |= LIT64( 0x2000000000000000 ); zSig = ( aSig + bSig )<<1; --zExp; - if ( (sbits64) zSig < 0 ) { + if ( (int64_t) zSig < 0 ) { zSig = aSig + bSig; ++zExp; } @@ -2898,7 +3150,7 @@ static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) { int16 aExp, bExp, zExp; - bits64 aSig, bSig, zSig; + uint64_t aSig, bSig, zSig; int16 expDiff; aSig = extractFloat64Frac( a ); @@ -2971,6 +3223,8 @@ static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) float64 float64_add( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); aSign = extractFloat64Sign( a ); bSign = extractFloat64Sign( b ); @@ -2992,6 +3246,8 @@ float64 float64_add( float64 a, float64 b STATUS_PARAM ) float64 float64_sub( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); aSign = extractFloat64Sign( a ); bSign = extractFloat64Sign( b ); @@ -3014,7 +3270,10 @@ float64 float64_mul( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign, zSign; int16 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; + uint64_t aSig, bSig, zSig0, zSig1; + + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3054,7 +3313,7 @@ float64 float64_mul( float64 a, float64 b STATUS_PARAM ) bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11; mul64To128( aSig, bSig, &zSig0, &zSig1 ); zSig0 |= ( zSig1 != 0 ); - if ( 0 <= (sbits64) ( zSig0<<1 ) ) { + if ( 0 <= (int64_t) ( zSig0<<1 ) ) { zSig0 <<= 1; --zExp; } @@ -3072,9 +3331,11 @@ float64 float64_div( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign, zSign; int16 aExp, bExp, zExp; - bits64 aSig, bSig, zSig; - bits64 rem0, rem1; - bits64 term0, term1; + uint64_t aSig, bSig, zSig; + uint64_t rem0, rem1; + uint64_t term0, term1; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3122,7 +3383,7 @@ float64 float64_div( float64 a, float64 b STATUS_PARAM ) if ( ( zSig & 0x1FF ) <= 2 ) { mul64To128( bSig, zSig, &term0, &term1 ); sub128( aSig, 0, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig; add128( rem0, rem1, 0, bSig, &rem0, &rem1 ); } @@ -3142,10 +3403,12 @@ float64 float64_rem( float64 a, float64 b STATUS_PARAM ) { flag aSign, zSign; int16 aExp, bExp, expDiff; - bits64 aSig, bSig; - bits64 q, alternateASig; - sbits64 sigMean; + uint64_t aSig, bSig; + uint64_t q, alternateASig; + int64_t sigMean; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); @@ -3205,12 +3468,12 @@ float64 float64_rem( float64 a, float64 b STATUS_PARAM ) alternateASig = aSig; ++q; aSig -= bSig; - } while ( 0 <= (sbits64) aSig ); + } while ( 0 <= (int64_t) aSig ); sigMean = aSig + alternateASig; if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) { aSig = alternateASig; } - zSign = ( (sbits64) aSig < 0 ); + zSign = ( (int64_t) aSig < 0 ); if ( zSign ) aSig = - aSig; return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig STATUS_VAR ); @@ -3226,8 +3489,9 @@ float64 float64_sqrt( float64 a STATUS_PARAM ) { flag aSign; int16 aExp, zExp; - bits64 aSig, zSig, doubleZSig; - bits64 rem0, rem1, term0, term1; + uint64_t aSig, zSig, doubleZSig; + uint64_t rem0, rem1, term0, term1; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3256,7 +3520,7 @@ float64 float64_sqrt( float64 a STATUS_PARAM ) doubleZSig = zSig<<1; mul64To128( zSig, zSig, &term0, &term1 ); sub128( aSig, 0, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig; doubleZSig -= 2; add128( rem0, rem1, zSig>>63, doubleZSig | 1, &rem0, &rem1 ); @@ -3276,7 +3540,8 @@ float64 float64_log2( float64 a STATUS_PARAM ) { flag aSign, zSign; int16 aExp; - bits64 aSig, aSig0, aSig1, zSig, i; + uint64_t aSig, aSig0, aSig1, zSig, i; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3298,7 +3563,7 @@ float64 float64_log2( float64 a STATUS_PARAM ) aExp -= 0x3FF; aSig |= LIT64( 0x0010000000000000 ); zSign = aExp < 0; - zSig = (bits64)aExp << 52; + zSig = (uint64_t)aExp << 52; for (i = 1LL << 51; i > 0; i >>= 1) { mul64To128( aSig, aSig, &aSig0, &aSig1 ); aSig = ( aSig0 << 12 ) | ( aSig1 >> 52 ); @@ -3315,39 +3580,42 @@ float64 float64_log2( float64 a STATUS_PARAM ) /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is equal to the -| corresponding value `b', and 0 otherwise. The comparison is performed +| corresponding value `b', and 0 otherwise. The invalid exception is raised +| if either operand is a NaN. Otherwise, the comparison is performed | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float64_eq( float64 a, float64 b STATUS_PARAM ) { - bits64 av, bv; + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { - if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); - } + float_raise( float_flag_invalid STATUS_VAR); return 0; } av = float64_val(a); bv = float64_val(b); - return ( av == bv ) || ( (bits64) ( ( av | bv )<<1 ) == 0 ); + return ( av == bv ) || ( (uint64_t) ( ( av | bv )<<1 ) == 0 ); } /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is less than or -| equal to the corresponding value `b', and 0 otherwise. The comparison is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| equal to the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float64_le( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; - bits64 av, bv; + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) @@ -3359,22 +3627,25 @@ int float64_le( float64 a, float64 b STATUS_PARAM ) bSign = extractFloat64Sign( b ); av = float64_val(a); bv = float64_val(b); - if ( aSign != bSign ) return aSign || ( (bits64) ( ( av | bv )<<1 ) == 0 ); + if ( aSign != bSign ) return aSign || ( (uint64_t) ( ( av | bv )<<1 ) == 0 ); return ( av == bv ) || ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is less than -| the corresponding value `b', and 0 otherwise. The comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. The comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float64_lt( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; - bits64 av, bv; + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { @@ -3385,31 +3656,56 @@ int float64_lt( float64 a, float64 b STATUS_PARAM ) bSign = extractFloat64Sign( b ); av = float64_val(a); bv = float64_val(b); - if ( aSign != bSign ) return aSign && ( (bits64) ( ( av | bv )<<1 ) != 0 ); + if ( aSign != bSign ) return aSign && ( (uint64_t) ( ( av | bv )<<1 ) != 0 ); return ( av != bv ) && ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- -| Returns 1 if the double-precision floating-point value `a' is equal to the -| corresponding value `b', and 0 otherwise. The invalid exception is raised -| if either operand is a NaN. Otherwise, the comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| Returns 1 if the double-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. The invalid exception is raised if either +| operand is a NaN. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_eq_signaling( float64 a, float64 b STATUS_PARAM ) +int float64_unordered( float64 a, float64 b STATUS_PARAM ) { - bits64 av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { float_raise( float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is equal to the +| corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception.The comparison is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_eq_quiet( float64 a, float64 b STATUS_PARAM ) +{ + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } return 0; } av = float64_val(a); bv = float64_val(b); - return ( av == bv ) || ( (bits64) ( ( av | bv )<<1 ) == 0 ); + return ( av == bv ) || ( (uint64_t) ( ( av | bv )<<1 ) == 0 ); } @@ -3423,7 +3719,9 @@ int float64_eq_signaling( float64 a, float64 b STATUS_PARAM ) int float64_le_quiet( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; - bits64 av, bv; + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) @@ -3437,7 +3735,7 @@ int float64_le_quiet( float64 a, float64 b STATUS_PARAM ) bSign = extractFloat64Sign( b ); av = float64_val(a); bv = float64_val(b); - if ( aSign != bSign ) return aSign || ( (bits64) ( ( av | bv )<<1 ) == 0 ); + if ( aSign != bSign ) return aSign || ( (uint64_t) ( ( av | bv )<<1 ) == 0 ); return ( av == bv ) || ( aSign ^ ( av < bv ) ); } @@ -3452,7 +3750,9 @@ int float64_le_quiet( float64 a, float64 b STATUS_PARAM ) int float64_lt_quiet( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; - bits64 av, bv; + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) @@ -3466,11 +3766,34 @@ int float64_lt_quiet( float64 a, float64 b STATUS_PARAM ) bSign = extractFloat64Sign( b ); av = float64_val(a); bv = float64_val(b); - if ( aSign != bSign ) return aSign && ( (bits64) ( ( av | bv )<<1 ) != 0 ); + if ( aSign != bSign ) return aSign && ( (uint64_t) ( ( av | bv )<<1 ) != 0 ); return ( av != bv ) && ( aSign ^ ( av < bv ) ); } +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. Quiet NaNs do not cause an exception. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_unordered_quiet( float64 a, float64 b STATUS_PARAM ) +{ + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} + #ifdef FLOATX80 /*---------------------------------------------------------------------------- @@ -3487,12 +3810,12 @@ int32 floatx80_to_int32( floatx80 a STATUS_PARAM ) { flag aSign; int32 aExp, shiftCount; - bits64 aSig; + uint64_t aSig; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); - if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0; + if ( ( aExp == 0x7FFF ) && (uint64_t) ( aSig<<1 ) ) aSign = 0; shiftCount = 0x4037 - aExp; if ( shiftCount <= 0 ) shiftCount = 1; shift64RightJamming( aSig, shiftCount, &aSig ); @@ -3514,14 +3837,14 @@ int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM ) { flag aSign; int32 aExp, shiftCount; - bits64 aSig, savedASig; + uint64_t aSig, savedASig; int32 z; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( 0x401E < aExp ) { - if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0; + if ( ( aExp == 0x7FFF ) && (uint64_t) ( aSig<<1 ) ) aSign = 0; goto invalid; } else if ( aExp < 0x3FFF ) { @@ -3536,7 +3859,7 @@ int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM ) if ( ( z < 0 ) ^ aSign ) { invalid: float_raise( float_flag_invalid STATUS_VAR); - return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig<<shiftCount ) != savedASig ) { STATUS(float_exception_flags) |= float_flag_inexact; @@ -3559,7 +3882,7 @@ int64 floatx80_to_int64( floatx80 a STATUS_PARAM ) { flag aSign; int32 aExp, shiftCount; - bits64 aSig, aSigExtra; + uint64_t aSig, aSigExtra; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); @@ -3574,7 +3897,7 @@ int64 floatx80_to_int64( floatx80 a STATUS_PARAM ) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } aSigExtra = 0; } @@ -3599,7 +3922,7 @@ int64 floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM ) { flag aSign; int32 aExp, shiftCount; - bits64 aSig; + uint64_t aSig; int64 z; aSig = extractFloatx80Frac( a ); @@ -3614,14 +3937,14 @@ int64 floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM ) return LIT64( 0x7FFFFFFFFFFFFFFF ); } } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } else if ( aExp < 0x3FFF ) { if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; return 0; } z = aSig>>( - shiftCount ); - if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) { + if ( (uint64_t) ( aSig<<( shiftCount & 63 ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } if ( aSign ) z = - z; @@ -3640,14 +3963,14 @@ float32 floatx80_to_float32( floatx80 a STATUS_PARAM ) { flag aSign; int32 aExp; - bits64 aSig; + uint64_t aSig; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) { - return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) ); + if ( (uint64_t) ( aSig<<1 ) ) { + return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat32( aSign, 0xFF, 0 ); } @@ -3668,14 +3991,14 @@ float64 floatx80_to_float64( floatx80 a STATUS_PARAM ) { flag aSign; int32 aExp; - bits64 aSig, zSig; + uint64_t aSig, zSig; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) { - return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) ); + if ( (uint64_t) ( aSig<<1 ) ) { + return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat64( aSign, 0x7FF, 0 ); } @@ -3698,13 +4021,13 @@ float128 floatx80_to_float128( floatx80 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits64 aSig, zSig0, zSig1; + uint64_t aSig, zSig0, zSig1; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); - if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) { - return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) ); + if ( ( aExp == 0x7FFF ) && (uint64_t) ( aSig<<1 ) ) { + return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 ); return packFloat128( aSign, aExp, zSig0, zSig1 ); @@ -3724,27 +4047,27 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM ) { flag aSign; int32 aExp; - bits64 lastBitMask, roundBitsMask; + uint64_t lastBitMask, roundBitsMask; int8 roundingMode; floatx80 z; aExp = extractFloatx80Exp( a ); if ( 0x403E <= aExp ) { - if ( ( aExp == 0x7FFF ) && (bits64) ( extractFloatx80Frac( a )<<1 ) ) { + if ( ( aExp == 0x7FFF ) && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) { return propagateFloatx80NaN( a, a STATUS_VAR ); } return a; } if ( aExp < 0x3FFF ) { if ( ( aExp == 0 ) - && ( (bits64) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) { + && ( (uint64_t) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) { return a; } STATUS(float_exception_flags) |= float_flag_inexact; aSign = extractFloatx80Sign( a ); switch ( STATUS(float_rounding_mode) ) { case float_round_nearest_even: - if ( ( aExp == 0x3FFE ) && (bits64) ( extractFloatx80Frac( a )<<1 ) + if ( ( aExp == 0x3FFE ) && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) { return packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) ); @@ -3797,7 +4120,7 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM ) static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM) { int32 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; + uint64_t aSig, bSig, zSig0, zSig1; int32 expDiff; aSig = extractFloatx80Frac( a ); @@ -3807,7 +4130,7 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM expDiff = aExp - bExp; if ( 0 < expDiff ) { if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return a; } if ( bExp == 0 ) --expDiff; @@ -3816,7 +4139,7 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM } else if ( expDiff < 0 ) { if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) ++expDiff; @@ -3825,7 +4148,7 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM } else { if ( aExp == 0x7FFF ) { - if ( (bits64) ( ( aSig | bSig )<<1 ) ) { + if ( (uint64_t) ( ( aSig | bSig )<<1 ) ) { return propagateFloatx80NaN( a, b STATUS_VAR ); } return a; @@ -3840,7 +4163,7 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM goto shiftRight1; } zSig0 = aSig + bSig; - if ( (sbits64) zSig0 < 0 ) goto roundAndPack; + if ( (int64_t) zSig0 < 0 ) goto roundAndPack; shiftRight1: shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 ); zSig0 |= LIT64( 0x8000000000000000 ); @@ -3863,7 +4186,7 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM ) { int32 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; + uint64_t aSig, bSig, zSig0, zSig1; int32 expDiff; floatx80 z; @@ -3875,7 +4198,7 @@ static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM if ( 0 < expDiff ) goto aExpBigger; if ( expDiff < 0 ) goto bExpBigger; if ( aExp == 0x7FFF ) { - if ( (bits64) ( ( aSig | bSig )<<1 ) ) { + if ( (uint64_t) ( ( aSig | bSig )<<1 ) ) { return propagateFloatx80NaN( a, b STATUS_VAR ); } float_raise( float_flag_invalid STATUS_VAR); @@ -3893,7 +4216,7 @@ static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM return packFloatx80( STATUS(float_rounding_mode) == float_round_down, 0, 0 ); bExpBigger: if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) ++expDiff; @@ -3905,7 +4228,7 @@ static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM goto normalizeRoundAndPack; aExpBigger: if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return a; } if ( bExp == 0 ) --expDiff; @@ -3972,7 +4295,7 @@ floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM ) { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; + uint64_t aSig, bSig, zSig0, zSig1; floatx80 z; aSig = extractFloatx80Frac( a ); @@ -3983,15 +4306,15 @@ floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM ) bSign = extractFloatx80Sign( b ); zSign = aSign ^ bSign; if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) - || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) { + if ( (uint64_t) ( aSig<<1 ) + || ( ( bExp == 0x7FFF ) && (uint64_t) ( bSig<<1 ) ) ) { return propagateFloatx80NaN( a, b STATUS_VAR ); } if ( ( bExp | bSig ) == 0 ) goto invalid; return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); if ( ( aExp | aSig ) == 0 ) { invalid: float_raise( float_flag_invalid STATUS_VAR); @@ -4011,7 +4334,7 @@ floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM ) } zExp = aExp + bExp - 0x3FFE; mul64To128( aSig, bSig, &zSig0, &zSig1 ); - if ( 0 < (sbits64) zSig0 ) { + if ( 0 < (int64_t) zSig0 ) { shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 ); --zExp; } @@ -4031,8 +4354,8 @@ floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM ) { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; - bits64 rem0, rem1, rem2, term0, term1, term2; + uint64_t aSig, bSig, zSig0, zSig1; + uint64_t rem0, rem1, rem2, term0, term1, term2; floatx80 z; aSig = extractFloatx80Frac( a ); @@ -4043,15 +4366,15 @@ floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM ) bSign = extractFloatx80Sign( b ); zSign = aSign ^ bSign; if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); goto invalid; } return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return packFloatx80( zSign, 0, 0 ); } if ( bExp == 0 ) { @@ -4081,15 +4404,15 @@ floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM ) zSig0 = estimateDiv128To64( aSig, rem1, bSig ); mul64To128( bSig, zSig0, &term0, &term1 ); sub128( aSig, rem1, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig0; add128( rem0, rem1, 0, bSig, &rem0, &rem1 ); } zSig1 = estimateDiv128To64( rem1, 0, bSig ); - if ( (bits64) ( zSig1<<1 ) <= 8 ) { + if ( (uint64_t) ( zSig1<<1 ) <= 8 ) { mul64To128( bSig, zSig1, &term1, &term2 ); sub128( rem1, 0, term1, term2, &rem1, &rem2 ); - while ( (sbits64) rem1 < 0 ) { + while ( (int64_t) rem1 < 0 ) { --zSig1; add128( rem1, rem2, 0, bSig, &rem1, &rem2 ); } @@ -4111,8 +4434,8 @@ floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM ) { flag aSign, zSign; int32 aExp, bExp, expDiff; - bits64 aSig0, aSig1, bSig; - bits64 q, term0, term1, alternateASig0, alternateASig1; + uint64_t aSig0, aSig1, bSig; + uint64_t q, term0, term1, alternateASig0, alternateASig1; floatx80 z; aSig0 = extractFloatx80Frac( a ); @@ -4121,14 +4444,14 @@ floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM ) bSig = extractFloatx80Frac( b ); bExp = extractFloatx80Exp( b ); if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig0<<1 ) - || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) { + if ( (uint64_t) ( aSig0<<1 ) + || ( ( bExp == 0x7FFF ) && (uint64_t) ( bSig<<1 ) ) ) { return propagateFloatx80NaN( a, b STATUS_VAR ); } goto invalid; } if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return a; } if ( bExp == 0 ) { @@ -4142,7 +4465,7 @@ floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM ) normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); } if ( aExp == 0 ) { - if ( (bits64) ( aSig0<<1 ) == 0 ) return a; + if ( (uint64_t) ( aSig0<<1 ) == 0 ) return a; normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 ); } bSig |= LIT64( 0x8000000000000000 ); @@ -4207,15 +4530,15 @@ floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM ) { flag aSign; int32 aExp, zExp; - bits64 aSig0, aSig1, zSig0, zSig1, doubleZSig0; - bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + uint64_t aSig0, aSig1, zSig0, zSig1, doubleZSig0; + uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; floatx80 z; aSig0 = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a STATUS_VAR ); + if ( (uint64_t) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a STATUS_VAR ); if ( ! aSign ) return a; goto invalid; } @@ -4238,7 +4561,7 @@ floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM ) doubleZSig0 = zSig0<<1; mul64To128( zSig0, zSig0, &term0, &term1 ); sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig0; doubleZSig0 -= 2; add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 ); @@ -4250,7 +4573,7 @@ floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM ) sub128( rem1, 0, term1, term2, &rem1, &rem2 ); mul64To128( zSig1, zSig1, &term2, &term3 ); sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 ); - while ( (sbits64) rem1 < 0 ) { + while ( (int64_t) rem1 < 0 ) { --zSig1; shortShift128Left( 0, zSig1, 1, &term2, &term3 ); term3 |= 1; @@ -4268,31 +4591,28 @@ floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM ) } /*---------------------------------------------------------------------------- -| Returns 1 if the extended double-precision floating-point value `a' is -| equal to the corresponding value `b', and 0 otherwise. The comparison is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| Returns 1 if the extended double-precision floating-point value `a' is equal +| to the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM ) { if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { - if ( floatx80_is_signaling_nan( a ) - || floatx80_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); - } + float_raise( float_flag_invalid STATUS_VAR); return 0; } return ( a.low == b.low ) && ( ( a.high == b.high ) || ( ( a.low == 0 ) - && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) ) + && ( (uint16_t) ( ( a.high | b.high )<<1 ) == 0 ) ) ); } @@ -4300,8 +4620,9 @@ int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM ) /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is | less than or equal to the corresponding value `b', and 0 otherwise. The -| comparison is performed according to the IEC/IEEE Standard for Binary -| Floating-Point Arithmetic. +| invalid exception is raised if either operand is a NaN. The comparison is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. *----------------------------------------------------------------------------*/ int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM ) @@ -4309,9 +4630,9 @@ int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM ) flag aSign, bSign; if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { float_raise( float_flag_invalid STATUS_VAR); return 0; @@ -4321,7 +4642,7 @@ int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM ) if ( aSign != bSign ) { return aSign - || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + || ( ( ( (uint16_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) == 0 ); } return @@ -4332,9 +4653,9 @@ int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM ) /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is -| less than the corresponding value `b', and 0 otherwise. The comparison -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| less than the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM ) @@ -4342,9 +4663,9 @@ int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM ) flag aSign, bSign; if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { float_raise( float_flag_invalid STATUS_VAR); return 0; @@ -4354,7 +4675,7 @@ int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM ) if ( aSign != bSign ) { return aSign - && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + && ( ( ( (uint16_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) != 0 ); } return @@ -4364,28 +4685,50 @@ int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM ) } /*---------------------------------------------------------------------------- -| Returns 1 if the extended double-precision floating-point value `a' is equal -| to the corresponding value `b', and 0 otherwise. The invalid exception is -| raised if either operand is a NaN. Otherwise, the comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| Returns 1 if the extended double-precision floating-point values `a' and `b' +| cannot be compared, and 0 otherwise. The invalid exception is raised if +| either operand is a NaN. The comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ +int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM ) +{ + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} -int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM ) +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is +| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +| cause an exception. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int floatx80_eq_quiet( floatx80 a, floatx80 b STATUS_PARAM ) { if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } return 0; } return ( a.low == b.low ) && ( ( a.high == b.high ) || ( ( a.low == 0 ) - && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) ) + && ( (uint16_t) ( ( a.high | b.high )<<1 ) == 0 ) ) ); } @@ -4402,9 +4745,9 @@ int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM ) flag aSign, bSign; if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { if ( floatx80_is_signaling_nan( a ) || floatx80_is_signaling_nan( b ) ) { @@ -4417,7 +4760,7 @@ int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM ) if ( aSign != bSign ) { return aSign - || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + || ( ( ( (uint16_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) == 0 ); } return @@ -4438,9 +4781,9 @@ int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM ) flag aSign, bSign; if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { if ( floatx80_is_signaling_nan( a ) || floatx80_is_signaling_nan( b ) ) { @@ -4453,7 +4796,7 @@ int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM ) if ( aSign != bSign ) { return aSign - && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + && ( ( ( (uint16_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) != 0 ); } return @@ -4462,6 +4805,28 @@ int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM ) } +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point values `a' and `b' +| cannot be compared, and 0 otherwise. Quiet NaNs do not cause an exception. +| The comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ +int floatx80_unordered_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +{ + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) + ) { + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} + #endif #ifdef FLOAT128 @@ -4480,7 +4845,7 @@ int32 float128_to_int32( float128 a STATUS_PARAM ) { flag aSign; int32 aExp, shiftCount; - bits64 aSig0, aSig1; + uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -4509,7 +4874,7 @@ int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM ) { flag aSign; int32 aExp, shiftCount; - bits64 aSig0, aSig1, savedASig; + uint64_t aSig0, aSig1, savedASig; int32 z; aSig1 = extractFloat128Frac1( a ); @@ -4534,7 +4899,7 @@ int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM ) if ( ( z < 0 ) ^ aSign ) { invalid: float_raise( float_flag_invalid STATUS_VAR); - return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig0<<shiftCount ) != savedASig ) { STATUS(float_exception_flags) |= float_flag_inexact; @@ -4557,7 +4922,7 @@ int64 float128_to_int64( float128 a STATUS_PARAM ) { flag aSign; int32 aExp, shiftCount; - bits64 aSig0, aSig1; + uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -4575,7 +4940,7 @@ int64 float128_to_int64( float128 a STATUS_PARAM ) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } shortShift128Left( aSig0, aSig1, - shiftCount, &aSig0, &aSig1 ); } @@ -4600,7 +4965,7 @@ int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM ) { flag aSign; int32 aExp, shiftCount; - bits64 aSig0, aSig1; + uint64_t aSig0, aSig1; int64 z; aSig1 = extractFloat128Frac1( a ); @@ -4622,10 +4987,10 @@ int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM ) return LIT64( 0x7FFFFFFFFFFFFFFF ); } } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } z = ( aSig0<<shiftCount ) | ( aSig1>>( ( - shiftCount ) & 63 ) ); - if ( (bits64) ( aSig1<<shiftCount ) ) { + if ( (uint64_t) ( aSig1<<shiftCount ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } } @@ -4638,7 +5003,7 @@ int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM ) } z = aSig0>>( - shiftCount ); if ( aSig1 - || ( shiftCount && (bits64) ( aSig0<<( shiftCount & 63 ) ) ) ) { + || ( shiftCount && (uint64_t) ( aSig0<<( shiftCount & 63 ) ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } } @@ -4658,8 +5023,8 @@ float32 float128_to_float32( float128 a STATUS_PARAM ) { flag aSign; int32 aExp; - bits64 aSig0, aSig1; - bits32 zSig; + uint64_t aSig0, aSig1; + uint32_t zSig; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -4667,7 +5032,7 @@ float32 float128_to_float32( float128 a STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat32( aSign, 0xFF, 0 ); } @@ -4693,7 +5058,7 @@ float64 float128_to_float64( float128 a STATUS_PARAM ) { flag aSign; int32 aExp; - bits64 aSig0, aSig1; + uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -4701,7 +5066,7 @@ float64 float128_to_float64( float128 a STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat64( aSign, 0x7FF, 0 ); } @@ -4728,7 +5093,7 @@ floatx80 float128_to_floatx80( float128 a STATUS_PARAM ) { flag aSign; int32 aExp; - bits64 aSig0, aSig1; + uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -4736,7 +5101,7 @@ floatx80 float128_to_floatx80( float128 a STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } @@ -4765,7 +5130,7 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) { flag aSign; int32 aExp; - bits64 lastBitMask, roundBitsMask; + uint64_t lastBitMask, roundBitsMask; int8 roundingMode; float128 z; @@ -4790,9 +5155,9 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask; } else { - if ( (sbits64) z.low < 0 ) { + if ( (int64_t) z.low < 0 ) { ++z.high; - if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1; + if ( (uint64_t) ( z.low<<1 ) == 0 ) z.high &= ~1; } } } @@ -4806,7 +5171,7 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) } else { if ( aExp < 0x3FFF ) { - if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a; + if ( ( ( (uint64_t) ( a.high<<1 ) ) | a.low ) == 0 ) return a; STATUS(float_exception_flags) |= float_flag_inexact; aSign = extractFloat128Sign( a ); switch ( STATUS(float_rounding_mode) ) { @@ -4868,7 +5233,7 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM) { int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; int32 expDiff; aSig1 = extractFloat128Frac1( a ); @@ -4949,7 +5314,7 @@ static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM) { int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; int32 expDiff; float128 z; @@ -5074,7 +5439,7 @@ float128 float128_mul( float128 a, float128 b STATUS_PARAM ) { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; float128 z; aSig1 = extractFloat128Frac1( a ); @@ -5138,8 +5503,8 @@ float128 float128_div( float128 a, float128 b STATUS_PARAM ) { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; - bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; float128 z; aSig1 = extractFloat128Frac1( a ); @@ -5193,7 +5558,7 @@ float128 float128_div( float128 a, float128 b STATUS_PARAM ) zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 ); mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 ); sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig0; add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 ); } @@ -5201,7 +5566,7 @@ float128 float128_div( float128 a, float128 b STATUS_PARAM ) if ( ( zSig1 & 0x3FFF ) <= 4 ) { mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 ); sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 ); - while ( (sbits64) rem1 < 0 ) { + while ( (int64_t) rem1 < 0 ) { --zSig1; add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 ); } @@ -5222,9 +5587,9 @@ float128 float128_rem( float128 a, float128 b STATUS_PARAM ) { flag aSign, zSign; int32 aExp, bExp, expDiff; - bits64 aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2; - bits64 allZero, alternateASig0, alternateASig1, sigMean1; - sbits64 sigMean0; + uint64_t aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2; + uint64_t allZero, alternateASig0, alternateASig1, sigMean1; + int64_t sigMean0; float128 z; aSig1 = extractFloat128Frac1( a ); @@ -5306,15 +5671,15 @@ float128 float128_rem( float128 a, float128 b STATUS_PARAM ) alternateASig1 = aSig1; ++q; sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 ); - } while ( 0 <= (sbits64) aSig0 ); + } while ( 0 <= (int64_t) aSig0 ); add128( - aSig0, aSig1, alternateASig0, alternateASig1, (bits64 *)&sigMean0, &sigMean1 ); + aSig0, aSig1, alternateASig0, alternateASig1, (uint64_t *)&sigMean0, &sigMean1 ); if ( ( sigMean0 < 0 ) || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) { aSig0 = alternateASig0; aSig1 = alternateASig1; } - zSign = ( (sbits64) aSig0 < 0 ); + zSign = ( (int64_t) aSig0 < 0 ); if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 ); return normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 STATUS_VAR ); @@ -5331,8 +5696,8 @@ float128 float128_sqrt( float128 a STATUS_PARAM ) { flag aSign; int32 aExp, zExp; - bits64 aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0; - bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + uint64_t aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0; + uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; float128 z; aSig1 = extractFloat128Frac1( a ); @@ -5364,7 +5729,7 @@ float128 float128_sqrt( float128 a STATUS_PARAM ) doubleZSig0 = zSig0<<1; mul64To128( zSig0, zSig0, &term0, &term1 ); sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig0; doubleZSig0 -= 2; add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 ); @@ -5376,7 +5741,7 @@ float128 float128_sqrt( float128 a STATUS_PARAM ) sub128( rem1, 0, term1, term2, &rem1, &rem2 ); mul64To128( zSig1, zSig1, &term2, &term3 ); sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 ); - while ( (sbits64) rem1 < 0 ) { + while ( (int64_t) rem1 < 0 ) { --zSig1; shortShift128Left( 0, zSig1, 1, &term2, &term3 ); term3 |= 1; @@ -5392,7 +5757,8 @@ float128 float128_sqrt( float128 a STATUS_PARAM ) /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is equal to -| the corresponding value `b', and 0 otherwise. The comparison is performed +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ @@ -5404,26 +5770,23 @@ int float128_eq( float128 a, float128 b STATUS_PARAM ) || ( ( extractFloat128Exp( b ) == 0x7FFF ) && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) ) { - if ( float128_is_signaling_nan( a ) - || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); - } + float_raise( float_flag_invalid STATUS_VAR); return 0; } return ( a.low == b.low ) && ( ( a.high == b.high ) || ( ( a.low == 0 ) - && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) + && ( (uint64_t) ( ( a.high | b.high )<<1 ) == 0 ) ) ); } /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is less than -| or equal to the corresponding value `b', and 0 otherwise. The comparison -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| or equal to the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float128_le( float128 a, float128 b STATUS_PARAM ) @@ -5443,7 +5806,7 @@ int float128_le( float128 a, float128 b STATUS_PARAM ) if ( aSign != bSign ) { return aSign - || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + || ( ( ( (uint64_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) == 0 ); } return @@ -5454,8 +5817,9 @@ int float128_le( float128 a, float128 b STATUS_PARAM ) /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is less than -| the corresponding value `b', and 0 otherwise. The comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. The comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float128_lt( float128 a, float128 b STATUS_PARAM ) @@ -5475,7 +5839,7 @@ int float128_lt( float128 a, float128 b STATUS_PARAM ) if ( aSign != bSign ) { return aSign - && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + && ( ( ( (uint64_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) != 0 ); } return @@ -5485,13 +5849,33 @@ int float128_lt( float128 a, float128 b STATUS_PARAM ) } /*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. The invalid exception is raised if either +| operand is a NaN. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_unordered( float128 a, float128 b STATUS_PARAM ) +{ + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is equal to -| the corresponding value `b', and 0 otherwise. The invalid exception is -| raised if either operand is a NaN. Otherwise, the comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. The comparison is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_eq_signaling( float128 a, float128 b STATUS_PARAM ) +int float128_eq_quiet( float128 a, float128 b STATUS_PARAM ) { if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) @@ -5499,14 +5883,17 @@ int float128_eq_signaling( float128 a, float128 b STATUS_PARAM ) || ( ( extractFloat128Exp( b ) == 0x7FFF ) && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } return 0; } return ( a.low == b.low ) && ( ( a.high == b.high ) || ( ( a.low == 0 ) - && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) + && ( (uint64_t) ( ( a.high | b.high )<<1 ) == 0 ) ) ); } @@ -5538,7 +5925,7 @@ int float128_le_quiet( float128 a, float128 b STATUS_PARAM ) if ( aSign != bSign ) { return aSign - || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + || ( ( ( (uint64_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) == 0 ); } return @@ -5574,7 +5961,7 @@ int float128_lt_quiet( float128 a, float128 b STATUS_PARAM ) if ( aSign != bSign ) { return aSign - && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + && ( ( ( (uint64_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) != 0 ); } return @@ -5583,6 +5970,29 @@ int float128_lt_quiet( float128 a, float128 b STATUS_PARAM ) } +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. Quiet NaNs do not cause an exception. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_unordered_quiet( float128 a, float128 b STATUS_PARAM ) +{ + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} + #endif /* misc functions */ @@ -5632,6 +6042,24 @@ unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM ) return res; } +unsigned int float32_to_uint16_round_to_zero( float32 a STATUS_PARAM ) +{ + int64_t v; + unsigned int res; + + v = float32_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise( float_flag_invalid STATUS_VAR); + } else if (v > 0xffff) { + res = 0xffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + unsigned int float64_to_uint32( float64 a STATUS_PARAM ) { int64_t v; @@ -5668,6 +6096,24 @@ unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM ) return res; } +unsigned int float64_to_uint16_round_to_zero( float64 a STATUS_PARAM ) +{ + int64_t v; + unsigned int res; + + v = float64_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise( float_flag_invalid STATUS_VAR); + } else if (v > 0xffff) { + res = 0xffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + /* FIXME: This looks broken. */ uint64_t float64_to_uint64 (float64 a STATUS_PARAM) { @@ -5696,7 +6142,9 @@ INLINE int float ## s ## _compare_internal( float ## s a, float ## s b, \ int is_quiet STATUS_PARAM ) \ { \ flag aSign, bSign; \ - bits ## s av, bv; \ + uint ## s ## _t av, bv; \ + a = float ## s ## _squash_input_denormal(a STATUS_VAR); \ + b = float ## s ## _squash_input_denormal(b STATUS_VAR); \ \ if (( ( extractFloat ## s ## Exp( a ) == nan_exp ) && \ extractFloat ## s ## Frac( a ) ) || \ @@ -5714,7 +6162,7 @@ INLINE int float ## s ## _compare_internal( float ## s a, float ## s b, \ av = float ## s ## _val(a); \ bv = float ## s ## _val(b); \ if ( aSign != bSign ) { \ - if ( (bits ## s) ( ( av | bv )<<1 ) == 0 ) { \ + if ( (uint ## s ## _t) ( ( av | bv )<<1 ) == 0 ) { \ /* zero case */ \ return float_relation_equal; \ } else { \ @@ -5742,6 +6190,52 @@ int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \ COMPARE(32, 0xff) COMPARE(64, 0x7ff) +INLINE int floatx80_compare_internal( floatx80 a, floatx80 b, + int is_quiet STATUS_PARAM ) +{ + flag aSign, bSign; + + if (( ( extractFloatx80Exp( a ) == 0x7fff ) && + ( extractFloatx80Frac( a )<<1 ) ) || + ( ( extractFloatx80Exp( b ) == 0x7fff ) && + ( extractFloatx80Frac( b )<<1 ) )) { + if (!is_quiet || + floatx80_is_signaling_nan( a ) || + floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return float_relation_unordered; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + + if ( ( ( (uint16_t) ( ( a.high | b.high ) << 1 ) ) == 0) && + ( ( a.low | b.low ) == 0 ) ) { + /* zero case */ + return float_relation_equal; + } else { + return 1 - (2 * aSign); + } + } else { + if (a.low == b.low && a.high == b.high) { + return float_relation_equal; + } else { + return 1 - 2 * (aSign ^ ( lt128( a.high, a.low, b.high, b.low ) )); + } + } +} + +int floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM ) +{ + return floatx80_compare_internal(a, b, 0 STATUS_VAR); +} + +int floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +{ + return floatx80_compare_internal(a, b, 1 STATUS_VAR); +} + INLINE int float128_compare_internal( float128 a, float128 b, int is_quiet STATUS_PARAM ) { @@ -5786,18 +6280,71 @@ int float128_compare_quiet( float128 a, float128 b STATUS_PARAM ) return float128_compare_internal(a, b, 1 STATUS_VAR); } +/* min() and max() functions. These can't be implemented as + * 'compare and pick one input' because that would mishandle + * NaNs and +0 vs -0. + */ +#define MINMAX(s, nan_exp) \ +INLINE float ## s float ## s ## _minmax(float ## s a, float ## s b, \ + int ismin STATUS_PARAM ) \ +{ \ + flag aSign, bSign; \ + uint ## s ## _t av, bv; \ + a = float ## s ## _squash_input_denormal(a STATUS_VAR); \ + b = float ## s ## _squash_input_denormal(b STATUS_VAR); \ + if (float ## s ## _is_any_nan(a) || \ + float ## s ## _is_any_nan(b)) { \ + return propagateFloat ## s ## NaN(a, b STATUS_VAR); \ + } \ + aSign = extractFloat ## s ## Sign(a); \ + bSign = extractFloat ## s ## Sign(b); \ + av = float ## s ## _val(a); \ + bv = float ## s ## _val(b); \ + if (aSign != bSign) { \ + if (ismin) { \ + return aSign ? a : b; \ + } else { \ + return aSign ? b : a; \ + } \ + } else { \ + if (ismin) { \ + return (aSign ^ (av < bv)) ? a : b; \ + } else { \ + return (aSign ^ (av < bv)) ? b : a; \ + } \ + } \ +} \ + \ +float ## s float ## s ## _min(float ## s a, float ## s b STATUS_PARAM) \ +{ \ + return float ## s ## _minmax(a, b, 1 STATUS_VAR); \ +} \ + \ +float ## s float ## s ## _max(float ## s a, float ## s b STATUS_PARAM) \ +{ \ + return float ## s ## _minmax(a, b, 0 STATUS_VAR); \ +} + +MINMAX(32, 0xff) +MINMAX(64, 0x7ff) + + /* Multiply A by 2 raised to the power N. */ float32 float32_scalbn( float32 a, int n STATUS_PARAM ) { flag aSign; - int16 aExp; - bits32 aSig; + int16_t aExp; + uint32_t aSig; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { + if ( aSig ) { + return propagateFloat32NaN( a, a STATUS_VAR ); + } return a; } if ( aExp != 0 ) @@ -5805,6 +6352,12 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM ) else if ( aSig == 0 ) return a; + if (n > 0x200) { + n = 0x200; + } else if (n < -0x200) { + n = -0x200; + } + aExp += n - 1; aSig <<= 7; return normalizeRoundAndPackFloat32( aSign, aExp, aSig STATUS_VAR ); @@ -5813,14 +6366,18 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM ) float64 float64_scalbn( float64 a, int n STATUS_PARAM ) { flag aSign; - int16 aExp; - bits64 aSig; + int16_t aExp; + uint64_t aSig; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { + if ( aSig ) { + return propagateFloat64NaN( a, a STATUS_VAR ); + } return a; } if ( aExp != 0 ) @@ -5828,6 +6385,12 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM ) else if ( aSig == 0 ) return a; + if (n > 0x1000) { + n = 0x1000; + } else if (n < -0x1000) { + n = -0x1000; + } + aExp += n - 1; aSig <<= 10; return normalizeRoundAndPackFloat64( aSign, aExp, aSig STATUS_VAR ); @@ -5837,19 +6400,29 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM ) floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM ) { flag aSign; - int16 aExp; - bits64 aSig; + int32_t aExp; + uint64_t aSig; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); - if ( aExp == 0x7FF ) { + if ( aExp == 0x7FFF ) { + if ( aSig<<1 ) { + return propagateFloatx80NaN( a, a STATUS_VAR ); + } return a; } + if (aExp == 0 && aSig == 0) return a; + if (n > 0x10000) { + n = 0x10000; + } else if (n < -0x10000) { + n = -0x10000; + } + aExp += n; return normalizeRoundAndPackFloatx80( STATUS(floatx80_rounding_precision), aSign, aExp, aSig, 0 STATUS_VAR ); @@ -5860,14 +6433,17 @@ floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM ) float128 float128_scalbn( float128 a, int n STATUS_PARAM ) { flag aSign; - int32 aExp; - bits64 aSig0, aSig1; + int32_t aExp; + uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); aExp = extractFloat128Exp( a ); aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) { + return propagateFloat128NaN( a, a STATUS_VAR ); + } return a; } if ( aExp != 0 ) @@ -5875,6 +6451,12 @@ float128 float128_scalbn( float128 a, int n STATUS_PARAM ) else if ( aSig0 == 0 && aSig1 == 0 ) return a; + if (n > 0x10000) { + n = 0x10000; + } else if (n < -0x10000) { + n = -0x10000; + } + aExp += n - 1; return normalizeRoundAndPackFloat128( aSign, aExp, aSig0, aSig1 STATUS_VAR ); diff --git a/fpu/softfloat.h b/fpu/softfloat.h index 9528825..5eff085 100644 --- a/fpu/softfloat.h +++ b/fpu/softfloat.h @@ -1,3 +1,9 @@ +/* + * QEMU float support + * + * Derived from SoftFloat. + */ + /*============================================================================ This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic @@ -59,24 +65,15 @@ typedef signed int int32; typedef uint64_t uint64; typedef int64_t int64; -/*---------------------------------------------------------------------------- -| Each of the following `typedef's defines a type that holds integers -| of _exactly_ the number of bits specified. For instance, for most -| implementation of C, `bits16' and `sbits16' should be `typedef'ed to -| `unsigned short int' and `signed short int' (or `short int'), respectively. -*----------------------------------------------------------------------------*/ -typedef uint8_t bits8; -typedef int8_t sbits8; -typedef uint16_t bits16; -typedef int16_t sbits16; -typedef uint32_t bits32; -typedef int32_t sbits32; -typedef uint64_t bits64; -typedef int64_t sbits64; - #define LIT64( a ) a##LL #define INLINE static inline +#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) +#define SNAN_BIT_IS_ONE 1 +#else +#define SNAN_BIT_IS_ONE 0 +#endif + /*---------------------------------------------------------------------------- | The macro `FLOATX80' must be defined to enable the extended double-precision | floating-point format `floatx80'. If this macro is not defined, the @@ -120,29 +117,44 @@ enum { //#define USE_SOFTFLOAT_STRUCT_TYPES #ifdef USE_SOFTFLOAT_STRUCT_TYPES typedef struct { + uint16_t v; +} float16; +#define float16_val(x) (((float16)(x)).v) +#define make_float16(x) __extension__ ({ float16 f16_val = {x}; f16_val; }) +#define const_float16(x) { x } +typedef struct { uint32_t v; } float32; /* The cast ensures an error if the wrong type is passed. */ #define float32_val(x) (((float32)(x)).v) #define make_float32(x) __extension__ ({ float32 f32_val = {x}; f32_val; }) +#define const_float32(x) { x } typedef struct { uint64_t v; } float64; #define float64_val(x) (((float64)(x)).v) #define make_float64(x) __extension__ ({ float64 f64_val = {x}; f64_val; }) +#define const_float64(x) { x } #else +typedef uint16_t float16; typedef uint32_t float32; typedef uint64_t float64; +#define float16_val(x) (x) #define float32_val(x) (x) #define float64_val(x) (x) +#define make_float16(x) (x) #define make_float32(x) (x) #define make_float64(x) (x) +#define const_float16(x) (x) +#define const_float32(x) (x) +#define const_float64(x) (x) #endif #ifdef FLOATX80 typedef struct { uint64_t low; uint16_t high; } floatx80; +#define make_floatx80(exp, mant) ((floatx80) { mant, exp }) #endif #ifdef FLOAT128 typedef struct { @@ -180,7 +192,8 @@ enum { float_flag_divbyzero = 4, float_flag_overflow = 8, float_flag_underflow = 16, - float_flag_inexact = 32 + float_flag_inexact = 32, + float_flag_input_denormal = 64 }; typedef struct float_status { @@ -190,16 +203,27 @@ typedef struct float_status { #ifdef FLOATX80 signed char floatx80_rounding_precision; #endif + /* should denormalised results go to zero and set the inexact flag? */ flag flush_to_zero; + /* should denormalised inputs go to zero and set the input_denormal flag? */ + flag flush_inputs_to_zero; flag default_nan_mode; } float_status; void set_float_rounding_mode(int val STATUS_PARAM); void set_float_exception_flags(int val STATUS_PARAM); +INLINE void set_float_detect_tininess(int val STATUS_PARAM) +{ + STATUS(float_detect_tininess) = val; +} INLINE void set_flush_to_zero(flag val STATUS_PARAM) { STATUS(flush_to_zero) = val; } +INLINE void set_flush_inputs_to_zero(flag val STATUS_PARAM) +{ + STATUS(flush_inputs_to_zero) = val; +} INLINE void set_default_nan_mode(flag val STATUS_PARAM) { STATUS(default_nan_mode) = val; @@ -221,42 +245,62 @@ void float_raise( int8 flags STATUS_PARAM); /*---------------------------------------------------------------------------- | Software IEC/IEEE integer-to-floating-point conversion routines. *----------------------------------------------------------------------------*/ -float32 int32_to_float32( int STATUS_PARAM ); -float64 int32_to_float64( int STATUS_PARAM ); +float32 int32_to_float32( int32 STATUS_PARAM ); +float64 int32_to_float64( int32 STATUS_PARAM ); float32 uint32_to_float32( unsigned int STATUS_PARAM ); float64 uint32_to_float64( unsigned int STATUS_PARAM ); #ifdef FLOATX80 -floatx80 int32_to_floatx80( int STATUS_PARAM ); +floatx80 int32_to_floatx80( int32 STATUS_PARAM ); #endif #ifdef FLOAT128 -float128 int32_to_float128( int STATUS_PARAM ); +float128 int32_to_float128( int32 STATUS_PARAM ); #endif -float32 int64_to_float32( int64_t STATUS_PARAM ); -float32 uint64_to_float32( uint64_t STATUS_PARAM ); -float64 int64_to_float64( int64_t STATUS_PARAM ); -float64 uint64_to_float64( uint64_t STATUS_PARAM ); +float32 int64_to_float32( int64 STATUS_PARAM ); +float32 uint64_to_float32( uint64 STATUS_PARAM ); +float64 int64_to_float64( int64 STATUS_PARAM ); +float64 uint64_to_float64( uint64 STATUS_PARAM ); #ifdef FLOATX80 -floatx80 int64_to_floatx80( int64_t STATUS_PARAM ); +floatx80 int64_to_floatx80( int64 STATUS_PARAM ); #endif #ifdef FLOAT128 -float128 int64_to_float128( int64_t STATUS_PARAM ); +float128 int64_to_float128( int64 STATUS_PARAM ); #endif /*---------------------------------------------------------------------------- | Software half-precision conversion routines. *----------------------------------------------------------------------------*/ -bits16 float32_to_float16( float32, flag STATUS_PARAM ); -float32 float16_to_float32( bits16, flag STATUS_PARAM ); +float16 float32_to_float16( float32, flag STATUS_PARAM ); +float32 float16_to_float32( float16, flag STATUS_PARAM ); + +/*---------------------------------------------------------------------------- +| Software half-precision operations. +*----------------------------------------------------------------------------*/ +int float16_is_quiet_nan( float16 ); +int float16_is_signaling_nan( float16 ); +float16 float16_maybe_silence_nan( float16 ); + +/*---------------------------------------------------------------------------- +| The pattern for a default generated half-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_ARM) +#define float16_default_nan make_float16(0x7E00) +#elif SNAN_BIT_IS_ONE +#define float16_default_nan make_float16(0x7DFF) +#else +#define float16_default_nan make_float16(0xFE00) +#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ -int float32_to_int32( float32 STATUS_PARAM ); -int float32_to_int32_round_to_zero( float32 STATUS_PARAM ); -unsigned int float32_to_uint32( float32 STATUS_PARAM ); -unsigned int float32_to_uint32_round_to_zero( float32 STATUS_PARAM ); -int64_t float32_to_int64( float32 STATUS_PARAM ); -int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM ); +int16 float32_to_int16_round_to_zero( float32 STATUS_PARAM ); +unsigned int float32_to_uint16_round_to_zero( float32 STATUS_PARAM ); +int32 float32_to_int32( float32 STATUS_PARAM ); +int32 float32_to_int32_round_to_zero( float32 STATUS_PARAM ); +uint32 float32_to_uint32( float32 STATUS_PARAM ); +uint32 float32_to_uint32_round_to_zero( float32 STATUS_PARAM ); +int64 float32_to_int64( float32 STATUS_PARAM ); +int64 float32_to_int64_round_to_zero( float32 STATUS_PARAM ); float64 float32_to_float64( float32 STATUS_PARAM ); #ifdef FLOATX80 floatx80 float32_to_floatx80( float32 STATUS_PARAM ); @@ -280,22 +324,33 @@ float32 float32_log2( float32 STATUS_PARAM ); int float32_eq( float32, float32 STATUS_PARAM ); int float32_le( float32, float32 STATUS_PARAM ); int float32_lt( float32, float32 STATUS_PARAM ); -int float32_eq_signaling( float32, float32 STATUS_PARAM ); +int float32_unordered( float32, float32 STATUS_PARAM ); +int float32_eq_quiet( float32, float32 STATUS_PARAM ); int float32_le_quiet( float32, float32 STATUS_PARAM ); int float32_lt_quiet( float32, float32 STATUS_PARAM ); +int float32_unordered_quiet( float32, float32 STATUS_PARAM ); int float32_compare( float32, float32 STATUS_PARAM ); int float32_compare_quiet( float32, float32 STATUS_PARAM ); -int float32_is_nan( float32 ); +float32 float32_min(float32, float32 STATUS_PARAM); +float32 float32_max(float32, float32 STATUS_PARAM); +int float32_is_quiet_nan( float32 ); int float32_is_signaling_nan( float32 ); +float32 float32_maybe_silence_nan( float32 ); float32 float32_scalbn( float32, int STATUS_PARAM ); INLINE float32 float32_abs(float32 a) { + /* Note that abs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ return make_float32(float32_val(a) & 0x7fffffff); } INLINE float32 float32_chs(float32 a) { + /* Note that chs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ return make_float32(float32_val(a) ^ 0x80000000); } @@ -314,21 +369,55 @@ INLINE int float32_is_zero(float32 a) return (float32_val(a) & 0x7fffffff) == 0; } +INLINE int float32_is_any_nan(float32 a) +{ + return ((float32_val(a) & ~(1 << 31)) > 0x7f800000UL); +} + +INLINE int float32_is_zero_or_denormal(float32 a) +{ + return (float32_val(a) & 0x7f800000) == 0; +} + +INLINE float32 float32_set_sign(float32 a, int sign) +{ + return make_float32((float32_val(a) & 0x7fffffff) | (sign << 31)); +} + #define float32_zero make_float32(0) #define float32_one make_float32(0x3f800000) #define float32_ln2 make_float32(0x3f317218) +#define float32_pi make_float32(0x40490fdb) +#define float32_half make_float32(0x3f000000) +#define float32_infinity make_float32(0x7f800000) + + +/*---------------------------------------------------------------------------- +| The pattern for a default generated single-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_SPARC) +#define float32_default_nan make_float32(0x7FFFFFFF) +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) +#define float32_default_nan make_float32(0x7FC00000) +#elif SNAN_BIT_IS_ONE +#define float32_default_nan make_float32(0x7FBFFFFF) +#else +#define float32_default_nan make_float32(0xFFC00000) +#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ -int float64_to_int32( float64 STATUS_PARAM ); -int float64_to_int32_round_to_zero( float64 STATUS_PARAM ); -unsigned int float64_to_uint32( float64 STATUS_PARAM ); -unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM ); -int64_t float64_to_int64( float64 STATUS_PARAM ); -int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM ); -uint64_t float64_to_uint64 (float64 a STATUS_PARAM); -uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM); +int16 float64_to_int16_round_to_zero( float64 STATUS_PARAM ); +unsigned int float64_to_uint16_round_to_zero( float64 STATUS_PARAM ); +int32 float64_to_int32( float64 STATUS_PARAM ); +int32 float64_to_int32_round_to_zero( float64 STATUS_PARAM ); +uint32 float64_to_uint32( float64 STATUS_PARAM ); +uint32 float64_to_uint32_round_to_zero( float64 STATUS_PARAM ); +int64 float64_to_int64( float64 STATUS_PARAM ); +int64 float64_to_int64_round_to_zero( float64 STATUS_PARAM ); +uint64 float64_to_uint64 (float64 a STATUS_PARAM); +uint64 float64_to_uint64_round_to_zero (float64 a STATUS_PARAM); float32 float64_to_float32( float64 STATUS_PARAM ); #ifdef FLOATX80 floatx80 float64_to_floatx80( float64 STATUS_PARAM ); @@ -352,22 +441,33 @@ float64 float64_log2( float64 STATUS_PARAM ); int float64_eq( float64, float64 STATUS_PARAM ); int float64_le( float64, float64 STATUS_PARAM ); int float64_lt( float64, float64 STATUS_PARAM ); -int float64_eq_signaling( float64, float64 STATUS_PARAM ); +int float64_unordered( float64, float64 STATUS_PARAM ); +int float64_eq_quiet( float64, float64 STATUS_PARAM ); int float64_le_quiet( float64, float64 STATUS_PARAM ); int float64_lt_quiet( float64, float64 STATUS_PARAM ); +int float64_unordered_quiet( float64, float64 STATUS_PARAM ); int float64_compare( float64, float64 STATUS_PARAM ); int float64_compare_quiet( float64, float64 STATUS_PARAM ); -int float64_is_nan( float64 a ); +float64 float64_min(float64, float64 STATUS_PARAM); +float64 float64_max(float64, float64 STATUS_PARAM); +int float64_is_quiet_nan( float64 a ); int float64_is_signaling_nan( float64 ); +float64 float64_maybe_silence_nan( float64 ); float64 float64_scalbn( float64, int STATUS_PARAM ); INLINE float64 float64_abs(float64 a) { + /* Note that abs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ return make_float64(float64_val(a) & 0x7fffffffffffffffLL); } INLINE float64 float64_chs(float64 a) { + /* Note that chs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ return make_float64(float64_val(a) ^ 0x8000000000000000LL); } @@ -386,19 +486,46 @@ INLINE int float64_is_zero(float64 a) return (float64_val(a) & 0x7fffffffffffffffLL) == 0; } +INLINE int float64_is_any_nan(float64 a) +{ + return ((float64_val(a) & ~(1ULL << 63)) > 0x7ff0000000000000ULL); +} + +INLINE float64 float64_set_sign(float64 a, int sign) +{ + return make_float64((float64_val(a) & 0x7fffffffffffffffULL) + | ((int64_t)sign << 63)); +} + #define float64_zero make_float64(0) #define float64_one make_float64(0x3ff0000000000000LL) #define float64_ln2 make_float64(0x3fe62e42fefa39efLL) +#define float64_pi make_float64(0x400921fb54442d18LL) +#define float64_half make_float64(0x3fe0000000000000LL) +#define float64_infinity make_float64(0x7ff0000000000000LL) + +/*---------------------------------------------------------------------------- +| The pattern for a default generated double-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_SPARC) +#define float64_default_nan make_float64(LIT64( 0x7FFFFFFFFFFFFFFF )) +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) +#define float64_default_nan make_float64(LIT64( 0x7FF8000000000000 )) +#elif SNAN_BIT_IS_ONE +#define float64_default_nan make_float64(LIT64( 0x7FF7FFFFFFFFFFFF )) +#else +#define float64_default_nan make_float64(LIT64( 0xFFF8000000000000 )) +#endif #ifdef FLOATX80 /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision conversion routines. *----------------------------------------------------------------------------*/ -int floatx80_to_int32( floatx80 STATUS_PARAM ); -int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM ); -int64_t floatx80_to_int64( floatx80 STATUS_PARAM ); -int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM ); +int32 floatx80_to_int32( floatx80 STATUS_PARAM ); +int32 floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM ); +int64 floatx80_to_int64( floatx80 STATUS_PARAM ); +int64 floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM ); float32 floatx80_to_float32( floatx80 STATUS_PARAM ); float64 floatx80_to_float64( floatx80 STATUS_PARAM ); #ifdef FLOAT128 @@ -418,11 +545,16 @@ floatx80 floatx80_sqrt( floatx80 STATUS_PARAM ); int floatx80_eq( floatx80, floatx80 STATUS_PARAM ); int floatx80_le( floatx80, floatx80 STATUS_PARAM ); int floatx80_lt( floatx80, floatx80 STATUS_PARAM ); -int floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM ); +int floatx80_unordered( floatx80, floatx80 STATUS_PARAM ); +int floatx80_eq_quiet( floatx80, floatx80 STATUS_PARAM ); int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM ); int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM ); -int floatx80_is_nan( floatx80 ); +int floatx80_unordered_quiet( floatx80, floatx80 STATUS_PARAM ); +int floatx80_compare( floatx80, floatx80 STATUS_PARAM ); +int floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM ); +int floatx80_is_quiet_nan( floatx80 ); int floatx80_is_signaling_nan( floatx80 ); +floatx80 floatx80_maybe_silence_nan( floatx80 ); floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM ); INLINE floatx80 floatx80_abs(floatx80 a) @@ -439,7 +571,7 @@ INLINE floatx80 floatx80_chs(floatx80 a) INLINE int floatx80_is_infinity(floatx80 a) { - return (a.high & 0x7fff) == 0x7fff && a.low == 0; + return (a.high & 0x7fff) == 0x7fff && a.low == 0x8000000000000000LL; } INLINE int floatx80_is_neg(floatx80 a) @@ -452,6 +584,31 @@ INLINE int floatx80_is_zero(floatx80 a) return (a.high & 0x7fff) == 0 && a.low == 0; } +INLINE int floatx80_is_any_nan(floatx80 a) +{ + return ((a.high & 0x7fff) == 0x7fff) && (a.low<<1); +} + +#define floatx80_zero make_floatx80(0x0000, 0x0000000000000000LL) +#define floatx80_one make_floatx80(0x3fff, 0x8000000000000000LL) +#define floatx80_ln2 make_floatx80(0x3ffe, 0xb17217f7d1cf79acLL) +#define floatx80_pi make_floatx80(0x4000, 0xc90fdaa22168c235LL) +#define floatx80_half make_floatx80(0x3ffe, 0x8000000000000000LL) +#define floatx80_infinity make_floatx80(0x7fff, 0x8000000000000000LL) + +/*---------------------------------------------------------------------------- +| The pattern for a default generated extended double-precision NaN. The +| `high' and `low' values hold the most- and least-significant bits, +| respectively. +*----------------------------------------------------------------------------*/ +#if SNAN_BIT_IS_ONE +#define floatx80_default_nan_high 0x7FFF +#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF ) +#else +#define floatx80_default_nan_high 0xFFFF +#define floatx80_default_nan_low LIT64( 0xC000000000000000 ) +#endif + #endif #ifdef FLOAT128 @@ -459,10 +616,10 @@ INLINE int floatx80_is_zero(floatx80 a) /*---------------------------------------------------------------------------- | Software IEC/IEEE quadruple-precision conversion routines. *----------------------------------------------------------------------------*/ -int float128_to_int32( float128 STATUS_PARAM ); -int float128_to_int32_round_to_zero( float128 STATUS_PARAM ); -int64_t float128_to_int64( float128 STATUS_PARAM ); -int64_t float128_to_int64_round_to_zero( float128 STATUS_PARAM ); +int32 float128_to_int32( float128 STATUS_PARAM ); +int32 float128_to_int32_round_to_zero( float128 STATUS_PARAM ); +int64 float128_to_int64( float128 STATUS_PARAM ); +int64 float128_to_int64_round_to_zero( float128 STATUS_PARAM ); float32 float128_to_float32( float128 STATUS_PARAM ); float64 float128_to_float64( float128 STATUS_PARAM ); #ifdef FLOATX80 @@ -482,13 +639,16 @@ float128 float128_sqrt( float128 STATUS_PARAM ); int float128_eq( float128, float128 STATUS_PARAM ); int float128_le( float128, float128 STATUS_PARAM ); int float128_lt( float128, float128 STATUS_PARAM ); -int float128_eq_signaling( float128, float128 STATUS_PARAM ); +int float128_unordered( float128, float128 STATUS_PARAM ); +int float128_eq_quiet( float128, float128 STATUS_PARAM ); int float128_le_quiet( float128, float128 STATUS_PARAM ); int float128_lt_quiet( float128, float128 STATUS_PARAM ); +int float128_unordered_quiet( float128, float128 STATUS_PARAM ); int float128_compare( float128, float128 STATUS_PARAM ); int float128_compare_quiet( float128, float128 STATUS_PARAM ); -int float128_is_nan( float128 ); +int float128_is_quiet_nan( float128 ); int float128_is_signaling_nan( float128 ); +float128 float128_maybe_silence_nan( float128 ); float128 float128_scalbn( float128, int STATUS_PARAM ); INLINE float128 float128_abs(float128 a) @@ -518,6 +678,24 @@ INLINE int float128_is_zero(float128 a) return (a.high & 0x7fffffffffffffffLL) == 0 && a.low == 0; } +INLINE int float128_is_any_nan(float128 a) +{ + return ((a.high >> 48) & 0x7fff) == 0x7fff && + ((a.low != 0) || ((a.high & 0xffffffffffffLL) != 0)); +} + +/*---------------------------------------------------------------------------- +| The pattern for a default generated quadruple-precision NaN. The `high' and +| `low' values hold the most- and least-significant bits, respectively. +*----------------------------------------------------------------------------*/ +#if SNAN_BIT_IS_ONE +#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF ) +#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) +#else +#define float128_default_nan_high LIT64( 0xFFFF800000000000 ) +#define float128_default_nan_low LIT64( 0x0000000000000000 ) +#endif + #endif #else /* CONFIG_SOFTFLOAT */ diff --git a/gen-icount.h b/gen-icount.h index 8879da6..5fb3829 100644 --- a/gen-icount.h +++ b/gen-icount.h @@ -29,7 +29,7 @@ static void gen_icount_end(TranslationBlock *tb, int num_insns) if (use_icount) { *icount_arg = num_insns; gen_set_label(icount_label); - tcg_gen_exit_tb((long)tb + 2); + tcg_gen_exit_tb((tcg_target_long)tb + 2); } } diff --git a/hw/android_arm.c b/hw/android_arm.c index 3b9dc6d..188051b 100644 --- a/hw/android_arm.c +++ b/hw/android_arm.c @@ -79,7 +79,7 @@ static void android_arm_init_(ram_addr_t ram_size, env = cpu_init(cpu_model); register_savevm( "cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load, env ); - ram_offset = qemu_ram_alloc(ram_size); + ram_offset = qemu_ram_alloc(NULL,"android_arm",ram_size); cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); cpu_pic = arm_pic_init_cpu(env); @@ -599,7 +599,7 @@ static uint32_t apic_get_current_count(APICState *s) { int64_t d; uint32_t val; - d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >> + d = (qemu_get_clock_ns(vm_clock) - s->initial_count_load_time) >> s->count_shift; if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { /* periodic */ @@ -806,12 +806,12 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) int n = index - 0x32; s->lvt[n] = val; if (n == APIC_LVT_TIMER) - apic_timer_update(s, qemu_get_clock(vm_clock)); + apic_timer_update(s, qemu_get_clock_ns(vm_clock)); } break; case 0x38: s->initial_count = val; - s->initial_count_load_time = qemu_get_clock(vm_clock); + s->initial_count_load_time = qemu_get_clock_ns(vm_clock); apic_timer_update(s, s->initial_count_load_time); break; case 0x39: @@ -956,7 +956,7 @@ int apic_init(CPUState *env) cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, apic_io_memory); } - s->timer = qemu_new_timer(vm_clock, apic_timer, s); + s->timer = qemu_new_timer_ns(vm_clock, apic_timer, s); register_savevm("apic", s->idx, 2, apic_save, apic_load, s); qemu_register_reset(apic_reset, 0, s); diff --git a/hw/armv7m.c b/hw/armv7m.c index 297a3e1..f7636db 100644 --- a/hw/armv7m.c +++ b/hw/armv7m.c @@ -105,13 +105,13 @@ static void bitband_writel(void *opaque, target_phys_addr_t offset, cpu_physical_memory_write(addr, (uint8_t *)&v, 4); } -static CPUReadMemoryFunc *bitband_readfn[] = { +static CPUReadMemoryFunc * const bitband_readfn[] = { bitband_readb, bitband_readw, bitband_readl }; -static CPUWriteMemoryFunc *bitband_writefn[] = { +static CPUWriteMemoryFunc * const bitband_writefn[] = { bitband_writeb, bitband_writew, bitband_writel @@ -192,9 +192,11 @@ qemu_irq *armv7m_init(int flash_size, int sram_size, /* Flash programming is done via the SCU, so pretend it is ROM. */ cpu_register_physical_memory(0, flash_size, - qemu_ram_alloc(flash_size) | IO_MEM_ROM); + qemu_ram_alloc(NULL, "armv7m.flash", + flash_size) | IO_MEM_ROM); cpu_register_physical_memory(0x20000000, sram_size, - qemu_ram_alloc(sram_size) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "armv7m.sram", + sram_size) | IO_MEM_RAM); armv7m_bitband_init(); nvic = qdev_create(NULL, "armv7m_nvic"); @@ -233,8 +235,8 @@ qemu_irq *armv7m_init(int flash_size, int sram_size, space. This stops qemu complaining about executing code outside RAM when returning from an exception. */ cpu_register_physical_memory(0xfffff000, 0x1000, - qemu_ram_alloc(0x1000) | IO_MEM_RAM); - + qemu_ram_alloc(NULL, "armv7m.hack", + 0x1000) | IO_MEM_RAM); return pic; } diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index f789c78..f732011 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -64,7 +64,7 @@ static inline int64_t systick_scale(nvic_state *s) static void systick_reload(nvic_state *s, int reset) { if (reset) - s->systick.tick = qemu_get_clock(vm_clock); + s->systick.tick = qemu_get_clock_ns(vm_clock); s->systick.tick += (s->systick.reload + 1) * systick_scale(s); qemu_mod_timer(s->systick.timer, s->systick.tick); } @@ -396,7 +396,7 @@ static void armv7m_nvic_init(SysBusDevice *dev) gic_init(&s->gic); cpu_register_physical_memory(0xe000e000, 0x1000, s->gic.iomemtype); - s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s); + s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s); register_savevm("armv7m_nvic", -1, 1, nvic_save, nvic_load, s); } diff --git a/hw/bt-hci-csr.c b/hw/bt-hci-csr.c index 982577d..65ffa37 100644 --- a/hw/bt-hci-csr.c +++ b/hw/bt-hci-csr.c @@ -88,7 +88,7 @@ static inline void csrhci_fifo_wake(struct csrhci_s *s) } if (s->out_len) - qemu_mod_timer(s->out_tm, qemu_get_clock(vm_clock) + s->baud_delay); + qemu_mod_timer(s->out_tm, qemu_get_clock_ns(vm_clock) + s->baud_delay); } #define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len) @@ -446,7 +446,7 @@ CharDriverState *uart_hci_init(qemu_irq wakeup) s->hci->evt_recv = csrhci_out_hci_packet_event; s->hci->acl_recv = csrhci_out_hci_packet_acl; - s->out_tm = qemu_new_timer(vm_clock, csrhci_out_tick, s); + s->out_tm = qemu_new_timer_ns(vm_clock, csrhci_out_tick, s); s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins); csrhci_reset(s); diff --git a/hw/bt-hci.c b/hw/bt-hci.c index f1ee92c..41df24c 100644 --- a/hw/bt-hci.c +++ b/hw/bt-hci.c @@ -576,7 +576,7 @@ static void bt_hci_inquiry_result(struct bt_hci_s *hci, static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period) { - qemu_mod_timer(timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(timer, qemu_get_clock_ns(vm_clock) + muldiv64(period << 7, get_ticks_per_sec(), 100)); } @@ -657,7 +657,7 @@ static void bt_hci_lmp_link_establish(struct bt_hci_s *hci, if (master) { link->acl_mode = acl_active; hci->lm.handle[hci->lm.last_handle].acl_mode_timer = - qemu_new_timer(vm_clock, bt_hci_mode_tick, link); + qemu_new_timer_ns(vm_clock, bt_hci_mode_tick, link); } } @@ -1084,7 +1084,7 @@ static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle, bt_hci_event_status(hci, HCI_SUCCESS); - qemu_mod_timer(link->acl_mode_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(link->acl_mode_timer, qemu_get_clock_ns(vm_clock) + muldiv64(interval * 625, get_ticks_per_sec(), 1000000)); bt_hci_lmp_mode_change_master(hci, link->link, mode, interval); @@ -2145,10 +2145,10 @@ struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net) { struct bt_hci_s *s = qemu_mallocz(sizeof(struct bt_hci_s)); - s->lm.inquiry_done = qemu_new_timer(vm_clock, bt_hci_inquiry_done, s); - s->lm.inquiry_next = qemu_new_timer(vm_clock, bt_hci_inquiry_next, s); + s->lm.inquiry_done = qemu_new_timer_ns(vm_clock, bt_hci_inquiry_done, s); + s->lm.inquiry_next = qemu_new_timer_ns(vm_clock, bt_hci_inquiry_next, s); s->conn_accept_timer = - qemu_new_timer(vm_clock, bt_hci_conn_accept_timeout, s); + qemu_new_timer_ns(vm_clock, bt_hci_conn_accept_timeout, s); s->evt_packet = bt_hci_evt_packet; s->evt_submit = bt_hci_evt_submit; diff --git a/hw/goldfish_pipe.c b/hw/goldfish_pipe.c index 998ba49..fd31a2b 100644 --- a/hw/goldfish_pipe.c +++ b/hw/goldfish_pipe.c @@ -655,7 +655,7 @@ throttlePipe_init( void* hwpipe, void* svcOpaque, const char* args ) ANEW0(pipe); pingPongPipe_init0(&pipe->pingpong, hwpipe, svcOpaque); - pipe->timer = qemu_new_timer(vm_clock, throttlePipe_timerFunc, pipe); + pipe->timer = qemu_new_timer_ns(vm_clock, throttlePipe_timerFunc, pipe); /* For now, limit to 500 KB/s in both directions */ pipe->sendRate = 1e9 / (500*1024*8); pipe->recvRate = pipe->sendRate; diff --git a/hw/goldfish_timer.c b/hw/goldfish_timer.c index a714b22..567ce2e 100644 --- a/hw/goldfish_timer.c +++ b/hw/goldfish_timer.c @@ -34,16 +34,6 @@ struct timer_state { QEMUTimer *timer; }; -/* Converts nanoseconds into ticks */ -static int64_t ns2tks(int64_t ns) { - return muldiv64(ns, get_ticks_per_sec(), 1000000000); -} - -/* Converts ticks into nanoseconds */ -static int64_t tks2ns(int64_t tks) { - return muldiv64(tks, 1000000000, get_ticks_per_sec()); -} - #define GOLDFISH_TIMER_SAVE_VERSION 1 static void goldfish_timer_save(QEMUFile* f, void* opaque) @@ -53,9 +43,9 @@ static void goldfish_timer_save(QEMUFile* f, void* opaque) qemu_put_be64(f, s->now_ns); /* in case the kernel is in the middle of a timer read */ qemu_put_byte(f, s->armed); if (s->armed) { - int64_t now_tks = qemu_get_clock(vm_clock); - int64_t alarm_tks = ns2tks(s->alarm_low_ns | (int64_t)s->alarm_high_ns << 32); - qemu_put_be64(f, alarm_tks - now_tks); + int64_t now_ns = qemu_get_clock_ns(vm_clock); + int64_t alarm_ns = (s->alarm_low_ns | (int64_t)s->alarm_high_ns << 32); + qemu_put_be64(f, alarm_ns - now_ns); } } @@ -88,7 +78,7 @@ static uint32_t goldfish_timer_read(void *opaque, target_phys_addr_t offset) struct timer_state *s = (struct timer_state *)opaque; switch(offset) { case TIMER_TIME_LOW: - s->now_ns = tks2ns(qemu_get_clock(vm_clock)); + s->now_ns = qemu_get_clock_ns(vm_clock); return s->now_ns; case TIMER_TIME_HIGH: return s->now_ns >> 32; @@ -101,16 +91,16 @@ static uint32_t goldfish_timer_read(void *opaque, target_phys_addr_t offset) static void goldfish_timer_write(void *opaque, target_phys_addr_t offset, uint32_t value_ns) { struct timer_state *s = (struct timer_state *)opaque; - int64_t alarm_tks, now_tks; + int64_t alarm_ns, now_ns; switch(offset) { case TIMER_ALARM_LOW: s->alarm_low_ns = value_ns; - alarm_tks = ns2tks(s->alarm_low_ns | (int64_t)s->alarm_high_ns << 32); - now_tks = qemu_get_clock(vm_clock); - if (alarm_tks <= now_tks) { + alarm_ns = (s->alarm_low_ns | (int64_t)s->alarm_high_ns << 32); + now_ns = qemu_get_clock_ns(vm_clock); + if (alarm_ns <= now_ns) { goldfish_device_set_irq(&s->dev, 0, 1); } else { - qemu_mod_timer(s->timer, alarm_tks); + qemu_mod_timer(s->timer, alarm_ns); s->armed = 1; } break; @@ -251,7 +241,7 @@ void goldfish_timer_and_rtc_init(uint32_t timerbase, int timerirq) { timer_state.dev.base = timerbase; timer_state.dev.irq = timerirq; - timer_state.timer = qemu_new_timer(vm_clock, goldfish_timer_tick, &timer_state); + timer_state.timer = qemu_new_timer_ns(vm_clock, goldfish_timer_tick, &timer_state); goldfish_device_add(&timer_state.dev, goldfish_timer_readfn, goldfish_timer_writefn, &timer_state); register_savevm( "goldfish_timer", 0, GOLDFISH_TIMER_SAVE_VERSION, goldfish_timer_save, goldfish_timer_load, &timer_state); diff --git a/hw/goldfish_trace.c b/hw/goldfish_trace.c index 02f9b8d..9eeb2a4 100644 --- a/hw/goldfish_trace.c +++ b/hw/goldfish_trace.c @@ -16,7 +16,7 @@ #include "qemu_file.h" #include "goldfish_trace.h" #include "sysemu.h" -#include "trace.h" +#include "android-trace.h" #ifdef CONFIG_MEMCHECK #include "memcheck/memcheck.h" #include "memcheck/memcheck_util.h" @@ -37,8 +37,8 @@ typedef int (QEMUFileRateLimit)(void *opaque); * the new actual bandwidth. It should be new_rate if everything goes ok, and * the old rate otherwise */ -typedef size_t (QEMUFileSetRateLimit)(void *opaque, size_t new_rate); -typedef size_t (QEMUFileGetRateLimit)(void *opaque); +typedef int64_t (QEMUFileSetRateLimit)(void *opaque, int64_t new_rate); +typedef int64_t (QEMUFileGetRateLimit)(void *opaque); QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, QEMUFileGetBufferFunc *get_buffer, @@ -47,10 +47,11 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, QEMUFileSetRateLimit *set_rate_limit, QEMUFileGetRateLimit *get_rate_limit); QEMUFile *qemu_fopen(const char *filename, const char *mode); +QEMUFile *qemu_fdopen(int fd, const char *mode); QEMUFile *qemu_fopen_socket(int fd); QEMUFile *qemu_popen(FILE *popen_file, const char *mode); QEMUFile *qemu_popen_cmd(const char *command, const char *mode); -int qemu_popen_fd(QEMUFile *f); +int qemu_stdio_fd(QEMUFile *f); void qemu_fflush(QEMUFile *f); int qemu_fclose(QEMUFile *f); void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); @@ -66,7 +67,9 @@ static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v) void qemu_put_be16(QEMUFile *f, unsigned int v); void qemu_put_be32(QEMUFile *f, unsigned int v); void qemu_put_be64(QEMUFile *f, uint64_t v); +#ifdef CONFIG_ANDROID void qemu_put_float(QEMUFile *f, float v); +#endif int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size); int qemu_get_byte(QEMUFile *f); @@ -80,10 +83,12 @@ static inline unsigned int qemu_get_ubyte(QEMUFile *f) unsigned int qemu_get_be16(QEMUFile *f); unsigned int qemu_get_be32(QEMUFile *f); uint64_t qemu_get_be64(QEMUFile *f); +#ifdef CONFIG_ANDROID float qemu_get_float(QEMUFile *f); +#endif int qemu_file_rate_limit(QEMUFile *f); -size_t qemu_file_set_rate_limit(QEMUFile *f, size_t new_rate); -size_t qemu_file_get_rate_limit(QEMUFile *f); +int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); +int64_t qemu_file_get_rate_limit(QEMUFile *f); int qemu_file_has_error(QEMUFile *f); void qemu_file_set_error(QEMUFile *f); @@ -66,7 +66,7 @@ static int pit_get_count(PITChannelState *s) uint64_t d; int counter; - d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, get_ticks_per_sec()); + d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ, get_ticks_per_sec()); switch(s->mode) { case 0: case 1: @@ -189,7 +189,7 @@ void pit_set_gate(PITState *pit, int channel, int val) case 5: if (s->gate < val) { /* restart counting on rising edge */ - s->count_load_time = qemu_get_clock(vm_clock); + s->count_load_time = qemu_get_clock_ns(vm_clock); pit_irq_timer_update(s, s->count_load_time); } break; @@ -197,7 +197,7 @@ void pit_set_gate(PITState *pit, int channel, int val) case 3: if (s->gate < val) { /* restart counting on rising edge */ - s->count_load_time = qemu_get_clock(vm_clock); + s->count_load_time = qemu_get_clock_ns(vm_clock); pit_irq_timer_update(s, s->count_load_time); } /* XXX: disable/enable counting */ @@ -228,7 +228,7 @@ static inline void pit_load_count(PITChannelState *s, int val) { if (val == 0) val = 0x10000; - s->count_load_time = qemu_get_clock(vm_clock); + s->count_load_time = qemu_get_clock_ns(vm_clock); s->count = val; pit_irq_timer_update(s, s->count_load_time); } @@ -262,7 +262,7 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) if (!(val & 0x10) && !s->status_latched) { /* status latch */ /* XXX: add BCD and null count */ - s->status = (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) | + s->status = (pit_get_out1(s, qemu_get_clock_ns(vm_clock)) << 7) | (s->rw_mode << 4) | (s->mode << 1) | s->bcd; @@ -492,7 +492,7 @@ PITState *pit_init(int base, qemu_irq irq) s = &pit->channels[0]; /* the timer 0 is connected to an IRQ */ - s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s); + s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s); s->irq = irq; register_savevm("i8254", base, 1, pit_save, pit_load, pit); diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index f93a3cb..9cf880d 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -108,8 +108,8 @@ static void rtc_coalesced_timer_update(RTCState *s) qemu_del_timer(s->coalesced_timer); } else { /* divide each RTC interval to 2 - 8 smaller intervals */ - int c = MIN(s->irq_coalesced, 7) + 1; - int64_t next_clock = qemu_get_clock(vm_clock) + + int c = MIN(s->irq_coalesced, 7) + 1; + int64_t next_clock = qemu_get_clock_ns(vm_clock) + muldiv64(s->period / c, get_ticks_per_sec(), 32768); qemu_mod_timer(s->coalesced_timer, next_clock); } @@ -150,7 +150,7 @@ static void rtc_timer_update(RTCState *s, int64_t current_time) #endif #endif enable_pie = 1; - + if (period_code != 0 && (((s->cmos_data[RTC_REG_B] & REG_B_PIE) && enable_pie) || ((s->cmos_data[RTC_REG_B] & REG_B_SQWE) && s->sqw_irq))) { @@ -237,7 +237,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) /* UIP bit is read only */ s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) | (s->cmos_data[RTC_REG_A] & REG_A_UIP); - rtc_timer_update(s, qemu_get_clock(vm_clock)); + rtc_timer_update(s, qemu_get_clock_ns(vm_clock)); break; case RTC_REG_B: if (data & REG_B_SET) { @@ -251,7 +251,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) } } s->cmos_data[RTC_REG_B] = data; - rtc_timer_update(s, qemu_get_clock(vm_clock)); + rtc_timer_update(s, qemu_get_clock_ns(vm_clock)); break; case RTC_REG_C: case RTC_REG_D: @@ -610,18 +610,18 @@ RTCState *rtc_init_sqw(int base, qemu_irq irq, qemu_irq sqw_irq, int base_year) s->base_year = base_year; rtc_set_date_from_host(s); - s->periodic_timer = qemu_new_timer(vm_clock, + s->periodic_timer = qemu_new_timer_ns(vm_clock, rtc_periodic_timer, s); #ifdef TARGET_I386 if (rtc_td_hack) - s->coalesced_timer = qemu_new_timer(vm_clock, rtc_coalesced_timer, s); + s->coalesced_timer = qemu_new_timer_ns(vm_clock, rtc_coalesced_timer, s); #endif - s->second_timer = qemu_new_timer(vm_clock, + s->second_timer = qemu_new_timer_ns(vm_clock, rtc_update_second, s); - s->second_timer2 = qemu_new_timer(vm_clock, + s->second_timer2 = qemu_new_timer_ns(vm_clock, rtc_update_second2, s); - s->next_second_time = qemu_get_clock(vm_clock) + (get_ticks_per_sec() * 99) / 100; + s->next_second_time = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() * 99) / 100; qemu_mod_timer(s->second_timer2, s->next_second_time); register_ioport_write(base, 2, 1, cmos_ioport_write, s); @@ -731,14 +731,14 @@ RTCState *rtc_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, s->base_year = base_year; rtc_set_date_from_host(s); - s->periodic_timer = qemu_new_timer(vm_clock, + s->periodic_timer = qemu_new_timer_ns(vm_clock, rtc_periodic_timer, s); - s->second_timer = qemu_new_timer(vm_clock, + s->second_timer = qemu_new_timer_ns(vm_clock, rtc_update_second, s); - s->second_timer2 = qemu_new_timer(vm_clock, + s->second_timer2 = qemu_new_timer_ns(vm_clock, rtc_update_second2, s); - s->next_second_time = qemu_get_clock(vm_clock) + (get_ticks_per_sec() * 99) / 100; + s->next_second_time = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() * 99) / 100; qemu_mod_timer(s->second_timer2, s->next_second_time); io_memory = cpu_register_io_memory(rtc_mm_read, rtc_mm_write, s); diff --git a/hw/msmouse.c b/hw/msmouse.c index 69356a5..05f893c 100644 --- a/hw/msmouse.c +++ b/hw/msmouse.c @@ -64,7 +64,7 @@ static void msmouse_chr_close (struct CharDriverState *chr) qemu_free (chr); } -CharDriverState *qemu_chr_open_msmouse(void) +CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) { CharDriverState *chr; diff --git a/hw/msmouse.h b/hw/msmouse.h index 947afd9..456cb21 100644 --- a/hw/msmouse.h +++ b/hw/msmouse.h @@ -1,2 +1,2 @@ /* msmouse.c */ -CharDriverState *qemu_chr_open_msmouse(void); +CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts); @@ -550,7 +550,7 @@ static void generate_bootsect(target_phys_addr_t option_rom, *p++ = 0x1f; /* pop ds */ *p++ = 0x58; /* pop ax */ *p++ = 0xcb; /* lret */ - + /* Actual code */ *reloc = (p - rom); @@ -910,7 +910,7 @@ static void pc_init1(ram_addr_t ram_size, cpu_model = "qemu32"; #endif } - + for(i = 0; i < smp_cpus; i++) { env = cpu_init(cpu_model); if (!env) { @@ -927,25 +927,23 @@ static void pc_init1(ram_addr_t ram_size, vmport_init(); /* allocate RAM */ - ram_addr = qemu_ram_alloc(0xa0000); + ram_addr = qemu_ram_alloc(NULL, "pc.ram", + below_4g_mem_size + above_4g_mem_size); cpu_register_physical_memory(0, 0xa0000, ram_addr); - - /* Allocate, even though we won't register, so we don't break the - * phys_ram_base + PA assumption. This range includes vga (0xa0000 - 0xc0000), - * and some bios areas, which will be registered later - */ - ram_addr = qemu_ram_alloc(0x100000 - 0xa0000); - ram_addr = qemu_ram_alloc(below_4g_mem_size - 0x100000); cpu_register_physical_memory(0x100000, below_4g_mem_size - 0x100000, - ram_addr); + ram_addr + 0x100000); + if (above_4g_mem_size > 0) { + cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size, + ram_addr + below_4g_mem_size); + } #else /* * Allocate a single contiguous RAM so that the goldfish * framebuffer can work well especially when the frame buffer is * large. */ - ram_addr = qemu_ram_alloc(below_4g_mem_size); + ram_addr = qemu_ram_alloc(NULL, "pc.ram", below_4g_mem_size); cpu_register_physical_memory(0, below_4g_mem_size, ram_addr); #endif @@ -975,7 +973,7 @@ static void pc_init1(ram_addr_t ram_size, (bios_size % 65536) != 0) { goto bios_error; } - bios_offset = qemu_ram_alloc(bios_size); + bios_offset = qemu_ram_alloc(NULL, "bios.bin", bios_size); ret = load_image(filename, qemu_get_ram_ptr(bios_offset)); if (ret != bios_size) { bios_error: @@ -995,7 +993,7 @@ static void pc_init1(ram_addr_t ram_size, - option_rom_offset = qemu_ram_alloc(0x20000); + option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", 0x20000); oprom_area_size = 0; cpu_register_physical_memory(0xc0000, 0x20000, option_rom_offset); diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 7dd8ed3..d34b85d 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -626,7 +626,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, starting_frame = OHCI_BM(iso_td.flags, TD_SF); frame_count = OHCI_BM(iso_td.flags, TD_FC); - relative_frame_number = USUB(ohci->frame_number, starting_frame); + relative_frame_number = USUB(ohci->frame_number, starting_frame); #ifdef DEBUG_ISOCH printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n" @@ -640,8 +640,8 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, iso_td.flags, iso_td.bp, iso_td.next, iso_td.be, iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3], iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7], - ohci->frame_number, starting_frame, - frame_count, relative_frame_number, + ohci->frame_number, starting_frame, + frame_count, relative_frame_number, OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC)); #endif @@ -651,7 +651,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, } else if (relative_frame_number > frame_count) { /* ISO TD expired - retire the TD to the Done Queue and continue with the next ISO TD of the same ED */ - dprintf("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, + dprintf("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, frame_count); OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN); ed->head &= ~OHCI_DPTR_MASK; @@ -692,8 +692,8 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, start_offset = iso_td.offset[relative_frame_number]; next_offset = iso_td.offset[relative_frame_number + 1]; - if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || - ((relative_frame_number < frame_count) && + if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || + ((relative_frame_number < frame_count) && !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) { printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n", start_offset, next_offset); @@ -758,7 +758,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, if (ret != USB_RET_NODEV) break; } - + if (ret == USB_RET_ASYNC) { return 1; } @@ -1095,7 +1095,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) /* Generate a SOF event, and set a timer for EOF */ static void ohci_sof(OHCIState *ohci) { - ohci->sof_time = qemu_get_clock(vm_clock); + ohci->sof_time = qemu_get_clock_ns(vm_clock); qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time); ohci_set_interrupt(ohci, OHCI_INTR_SF); } @@ -1179,12 +1179,12 @@ static void ohci_frame_boundary(void *opaque) */ static int ohci_bus_start(OHCIState *ohci) { - ohci->eof_timer = qemu_new_timer(vm_clock, + ohci->eof_timer = qemu_new_timer_ns(vm_clock, ohci_frame_boundary, ohci); if (ohci->eof_timer == NULL) { - fprintf(stderr, "usb-ohci: %s: qemu_new_timer failed\n", ohci->name); + fprintf(stderr, "usb-ohci: %s: qemu_new_timer_ns failed\n", ohci->name); /* TODO: Signal unrecoverable error */ return 0; } @@ -1304,7 +1304,7 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci) /* Being in USB operational state guarnatees sof_time was * set already. */ - tks = qemu_get_clock(vm_clock) - ohci->sof_time; + tks = qemu_get_clock_ns(vm_clock) - ohci->sof_time; /* avoid muldiv if possible */ if (tks >= usb_frame_time) @@ -28,24 +28,45 @@ #include "console.h" #include "qjson.h" -static QEMUPutKBDEvent *qemu_put_kbd_event; -static void *qemu_put_kbd_event_opaque; +#ifdef CONFIG_SKINNING +QEMUPutMouseEntry *original_qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, + void *opaque, int absolute, + const char *name); +#undef qemu_add_mouse_event_handler +#define qemu_add_mouse_event_handler original_qemu_add_mouse_event_handler +#endif +static QTAILQ_HEAD(, QEMUPutKBDEntry) kbd_handlers = + QTAILQ_HEAD_INITIALIZER(kbd_handlers); static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = QTAILQ_HEAD_INITIALIZER(led_handlers); static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers = QTAILQ_HEAD_INITIALIZER(mouse_handlers); -static NotifierList mouse_mode_notifiers = +static NotifierList mouse_mode_notifiers = NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) { - qemu_put_kbd_event_opaque = opaque; - qemu_put_kbd_event = func; + QEMUPutKBDEntry *s; + + if (func != NULL) { + s = qemu_mallocz(sizeof(QEMUPutKBDEntry)); + + s->put_kbd_event = func; + s->opaque = opaque; + + QTAILQ_INSERT_TAIL(&kbd_handlers, s, next); + } } -void qemu_remove_kbd_event_handler(void) +void qemu_remove_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) { - qemu_put_kbd_event_opaque = NULL; - qemu_put_kbd_event = NULL; + QEMUPutKBDEntry *cursor, *cursor_next; + if (func != NULL) { + QTAILQ_FOREACH_SAFE(cursor, &kbd_handlers, next, cursor_next) { + if (cursor->put_kbd_event == func && cursor->opaque == opaque) { + QTAILQ_REMOVE(&kbd_handlers, cursor, next); + } + } + } } static void check_mode_change(void) @@ -129,8 +150,9 @@ void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry) void kbd_put_keycode(int keycode) { - if (qemu_put_kbd_event) { - qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode); + QEMUPutKBDEntry *cursor; + QTAILQ_FOREACH(cursor, &kbd_handlers, next) { + cursor->put_kbd_event(cursor->opaque, keycode); } } @@ -148,7 +170,9 @@ void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) QEMUPutMouseEntry *entry; QEMUPutMouseEvent *mouse_event; void *mouse_event_opaque; +#ifndef CONFIG_SKINNING int width; +#endif if (QTAILQ_EMPTY(&mouse_handlers)) { return; @@ -160,16 +184,20 @@ void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) mouse_event_opaque = entry->qemu_put_mouse_event_opaque; if (mouse_event) { +#ifndef CONFIG_SKINNING if (graphic_rotate) { - if (entry->qemu_put_mouse_event_absolute) + if (entry->qemu_put_mouse_event_absolute) { width = 0x7fff; - else + } else { width = graphic_width - 1; - mouse_event(mouse_event_opaque, - width - dy, dx, dz, buttons_state); - } else - mouse_event(mouse_event_opaque, - dx, dy, dz, buttons_state); + } + mouse_event(mouse_event_opaque, width - dy, dx, dz, buttons_state); + } else { + mouse_event(mouse_event_opaque, dx, dy, dz, buttons_state); + } +#else + mouse_event(mouse_event_opaque, dx, dy, dz, buttons_state); +#endif } } diff --git a/iohandler.c b/iohandler.c new file mode 100644 index 0000000..2b82421 --- /dev/null +++ b/iohandler.c @@ -0,0 +1,193 @@ +/* + * QEMU System Emulator - managing I/O handler + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config-host.h" +#include "qemu-common.h" +#include "qemu-char.h" +#include "qemu-queue.h" + +#ifndef _WIN32 +#include <sys/wait.h> +#endif + +typedef struct IOHandlerRecord { + int fd; + IOCanReadHandler *fd_read_poll; + IOHandler *fd_read; + IOHandler *fd_write; + int deleted; + void *opaque; + QLIST_ENTRY(IOHandlerRecord) next; +} IOHandlerRecord; + +static QLIST_HEAD(, IOHandlerRecord) io_handlers = + QLIST_HEAD_INITIALIZER(io_handlers); + + +/* XXX: fd_read_poll should be suppressed, but an API change is + necessary in the character devices to suppress fd_can_read(). */ +int qemu_set_fd_handler2(int fd, + IOCanReadHandler *fd_read_poll, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque) +{ + IOHandlerRecord *ioh; + + if (!fd_read && !fd_write) { + QLIST_FOREACH(ioh, &io_handlers, next) { + if (ioh->fd == fd) { + ioh->deleted = 1; + break; + } + } + } else { + QLIST_FOREACH(ioh, &io_handlers, next) { + if (ioh->fd == fd) + goto found; + } + ioh = qemu_mallocz(sizeof(IOHandlerRecord)); + QLIST_INSERT_HEAD(&io_handlers, ioh, next); + found: + ioh->fd = fd; + ioh->fd_read_poll = fd_read_poll; + ioh->fd_read = fd_read; + ioh->fd_write = fd_write; + ioh->opaque = opaque; + ioh->deleted = 0; + } + return 0; +} + +int qemu_set_fd_handler(int fd, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque) +{ + return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); +} + +void qemu_iohandler_fill(int *pnfds, fd_set *readfds, fd_set *writefds, fd_set *xfds) +{ + IOHandlerRecord *ioh; + + QLIST_FOREACH(ioh, &io_handlers, next) { + if (ioh->deleted) + continue; + if (ioh->fd_read && + (!ioh->fd_read_poll || + ioh->fd_read_poll(ioh->opaque) != 0)) { + FD_SET(ioh->fd, readfds); + if (ioh->fd > *pnfds) + *pnfds = ioh->fd; + } + if (ioh->fd_write) { + FD_SET(ioh->fd, writefds); + if (ioh->fd > *pnfds) + *pnfds = ioh->fd; + } + } +} + +void qemu_iohandler_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, int ret) +{ + if (ret > 0) { + IOHandlerRecord *pioh, *ioh; + + QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { + if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, readfds)) { + ioh->fd_read(ioh->opaque); + } + if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, writefds)) { + ioh->fd_write(ioh->opaque); + } + + /* Do this last in case read/write handlers marked it for deletion */ + if (ioh->deleted) { + QLIST_REMOVE(ioh, next); + qemu_free(ioh); + } + } + } +} + +/* reaping of zombies. right now we're not passing the status to + anyone, but it would be possible to add a callback. */ +#ifndef _WIN32 +typedef struct ChildProcessRecord { + int pid; + QLIST_ENTRY(ChildProcessRecord) next; +} ChildProcessRecord; + +static QLIST_HEAD(, ChildProcessRecord) child_watches = + QLIST_HEAD_INITIALIZER(child_watches); + +static QEMUBH *sigchld_bh; + +static void sigchld_handler(int signal) +{ + qemu_bh_schedule(sigchld_bh); +} + +static void sigchld_bh_handler(void *opaque) +{ + ChildProcessRecord *rec, *next; + + QLIST_FOREACH_SAFE(rec, &child_watches, next, next) { + if (waitpid(rec->pid, NULL, WNOHANG) == rec->pid) { + QLIST_REMOVE(rec, next); + qemu_free(rec); + } + } +} + +static void qemu_init_child_watch(void) +{ + struct sigaction act; + sigchld_bh = qemu_bh_new(sigchld_bh_handler, NULL); + + act.sa_handler = sigchld_handler; + act.sa_flags = SA_NOCLDSTOP; + sigaction(SIGCHLD, &act, NULL); +} + +int qemu_add_child_watch(pid_t pid) +{ + ChildProcessRecord *rec; + + if (!sigchld_bh) { + qemu_init_child_watch(); + } + + QLIST_FOREACH(rec, &child_watches, next) { + if (rec->pid == pid) { + return 1; + } + } + rec = qemu_mallocz(sizeof(ChildProcessRecord)); + rec->pid = pid; + QLIST_INSERT_HEAD(&child_watches, rec, next); + return 0; +} +#endif diff --git a/json-lexer.c b/json-lexer.c index 534fcf7..65c9720 100644 --- a/json-lexer.c +++ b/json-lexer.c @@ -27,12 +27,8 @@ * */ -/* Building with mingw results in an error because ERROR is defined as a - * macro in this environment. Undefined it */ -#undef ERROR - enum json_lexer_state { - ERROR = 0, + IN_ERROR = 0, IN_DQ_UCODE3, IN_DQ_UCODE2, IN_DQ_UCODE1, @@ -154,7 +150,7 @@ static const uint8_t json_lexer[][256] = { /* Zero */ [IN_ZERO] = { TERMINAL(JSON_INTEGER), - ['0' ... '9'] = ERROR, + ['0' ... '9'] = IN_ERROR, ['.'] = IN_MANTISSA, }, @@ -306,7 +302,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch) lexer->token = qstring_new(); new_state = IN_START; break; - case ERROR: + case IN_ERROR: return -EINVAL; default: break; diff --git a/json-parser.c b/json-parser.c index 70b9b6f..6c06ef9 100644 --- a/json-parser.c +++ b/json-parser.c @@ -91,7 +91,8 @@ static int token_is_escape(QObject *obj, const char *value) /** * Error handler */ -static void parse_error(JSONParserContext *ctxt, QObject *token, const char *msg, ...) +static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt, + QObject *token, const char *msg, ...) { va_list ap; va_start(ap, msg); diff --git a/migration-exec.c b/migration-exec.c index 0dd5aff..ab1dea2 100644 --- a/migration-exec.c +++ b/migration-exec.c @@ -20,14 +20,16 @@ #include "sysemu.h" #include "buffered_file.h" #include "block.h" +#include <sys/types.h> +#include <sys/wait.h> //#define DEBUG_MIGRATION_EXEC #ifdef DEBUG_MIGRATION_EXEC -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { printf("migration-exec: " fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif @@ -43,13 +45,21 @@ static int file_write(FdMigrationState *s, const void * buf, size_t size) static int exec_close(FdMigrationState *s) { - dprintf("exec_close\n"); + int ret = 0; + DPRINTF("exec_close\n"); if (s->opaque) { - qemu_fclose(s->opaque); + ret = qemu_fclose(s->opaque); s->opaque = NULL; s->fd = -1; + if (ret != -1 && + WIFEXITED(ret) + && WEXITSTATUS(ret) == 0) { + ret = 0; + } else { + ret = -1; } - return 0; + } + return ret; } MigrationState *exec_start_outgoing_migration(const char *command, @@ -63,18 +73,18 @@ MigrationState *exec_start_outgoing_migration(const char *command, f = popen(command, "w"); if (f == NULL) { - dprintf("Unable to popen exec target\n"); + DPRINTF("Unable to popen exec target\n"); goto err_after_alloc; } s->fd = fileno(f); if (s->fd == -1) { - dprintf("Unable to retrieve file descriptor for popen'd handle\n"); + DPRINTF("Unable to retrieve file descriptor for popen'd handle\n"); goto err_after_open; } if (fcntl(s->fd, F_SETFD, O_NONBLOCK) == -1) { - dprintf("Unable to set nonblocking mode on file descriptor\n"); + DPRINTF("Unable to set nonblocking mode on file descriptor\n"); goto err_after_open; } @@ -116,9 +126,9 @@ static void exec_accept_incoming_migration(void *opaque) goto err; } qemu_announce_self(); - dprintf("successfully loaded vm state\n"); + DPRINTF("successfully loaded vm state\n"); /* we've successfully migrated, close the fd */ - qemu_set_fd_handler2(qemu_popen_fd(f), NULL, NULL, NULL, NULL); + qemu_set_fd_handler2(qemu_stdio_fd(f), NULL, NULL, NULL, NULL); vm_start(); err: @@ -129,14 +139,14 @@ int exec_start_incoming_migration(const char *command) { QEMUFile *f; - dprintf("Attempting to start an incoming migration\n"); + DPRINTF("Attempting to start an incoming migration\n"); f = qemu_popen_cmd(command, "r"); if(f == NULL) { - dprintf("Unable to apply qemu wrapper to popen file\n"); + DPRINTF("Unable to apply qemu wrapper to popen file\n"); return -errno; } - qemu_set_fd_handler2(qemu_popen_fd(f), NULL, + qemu_set_fd_handler2(qemu_stdio_fd(f), NULL, exec_accept_incoming_migration, NULL, (void *)(unsigned long)f); diff --git a/migration.h b/migration.h index 37c7f8e..9d145b0 100644 --- a/migration.h +++ b/migration.h @@ -102,4 +102,11 @@ static inline FdMigrationState *migrate_to_fms(MigrationState *mig_state) return container_of(mig_state, FdMigrationState, mig_state); } +uint64_t ram_bytes_remaining(void); +uint64_t ram_bytes_transferred(void); +uint64_t ram_bytes_total(void); + +int ram_save_live(QEMUFile *f, int stage, void *opaque); +int ram_load(QEMUFile *f, void *opaque, int version_id); + #endif diff --git a/monitor-android.h b/monitor-android.h new file mode 100644 index 0000000..b9b0b37 --- /dev/null +++ b/monitor-android.h @@ -0,0 +1,49 @@ +/* This file is included from monitor.c, it's purpose is to hold as much + * Android-specific stuff as possible to ease upstream integrations. + */ + +Monitor* +monitor_fake_new(void* opaque, MonitorFakeFunc cb) +{ + Monitor* mon; + + assert(cb != NULL); + mon = qemu_mallocz(sizeof(*mon)); + mon->fake_opaque = opaque; + mon->fake_func = cb; + mon->fake_count = 0; + + return mon; +} + +int +monitor_fake_get_bytes(Monitor* mon) +{ + assert(mon->fake_func != NULL); + return mon->fake_count; +} + +void +monitor_fake_free(Monitor* mon) +{ + assert(mon->fake_func != NULL); + free(mon); +} + +/* This replaces the definition in monitor.c which is in a + * #ifndef CONFIG_ANDROID .. #endif block. + */ +void monitor_flush(Monitor *mon) +{ + if (!mon) + return; + + if (mon->fake_func != NULL) { + mon->fake_func(mon->fake_opaque, (void*)mon->outbuf, mon->outbuf_index); + mon->outbuf_index = 0; + mon->fake_count += mon->outbuf_index; + } else if (!mon->mux_out) { + qemu_chr_write(mon->chr, mon->outbuf, mon->outbuf_index); + mon->outbuf_index = 0; + } +} @@ -86,8 +86,18 @@ struct Monitor { void *password_opaque; QLIST_ENTRY(Monitor) entry; int has_quit; +#ifdef CONFIG_ANDROID + void* fake_opaque; + MonitorFakeFunc fake_func; + int64_t fake_count; + +#endif }; +#ifdef CONFIG_ANDROID +#include "monitor-android.h" +#endif + static QLIST_HEAD(mon_list, Monitor) mon_list; #if defined(TARGET_I386) @@ -158,6 +168,7 @@ static int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +#ifndef CONFIG_ANDROID /* See monitor-android.h */ void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 && !mon->mux_out) { @@ -165,6 +176,7 @@ void monitor_flush(Monitor *mon) mon->outbuf_index = 0; } } +#endif /* flush at every end of line or if the buffer is full */ static void monitor_puts(Monitor *mon, const char *str) @@ -187,13 +199,11 @@ static void monitor_puts(Monitor *mon, const char *str) } } -int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) +void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { char buf[4096]; - int ret = vsnprintf(buf, sizeof(buf), fmt, ap); + vsnprintf(buf, sizeof(buf), fmt, ap); monitor_puts(mon, buf); - - return ret; } void monitor_printf(Monitor *mon, const char *fmt, ...) @@ -231,7 +241,8 @@ void monitor_print_filename(Monitor *mon, const char *filename) } } -static int monitor_fprintf(FILE *stream, const char *fmt, ...) +static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream, + const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -1120,8 +1131,7 @@ static void do_sendkey(Monitor *mon, const char *string, int has_hold_time, kbd_put_keycode(keycode & 0x7f); } /* delayed key up events */ - qemu_mod_timer(key_timer, qemu_get_clock(vm_clock) + - muldiv64(get_ticks_per_sec(), hold_time, 1000)); + qemu_mod_timer(key_timer, qemu_get_clock_ms(vm_clock) + hold_time); } static int mouse_button_state; @@ -3072,7 +3082,7 @@ void monitor_init(CharDriverState *chr, int flags) Monitor *mon; if (is_first_init) { - key_timer = qemu_new_timer(vm_clock, release_keys, NULL); + key_timer = qemu_new_timer_ms(vm_clock, release_keys, NULL); is_first_init = 0; } @@ -14,7 +14,8 @@ extern Monitor *default_mon; #define MONITOR_IS_DEFAULT 0x01 #define MONITOR_USE_READLINE 0x02 #define MONITOR_USE_CONTROL 0x04 -#define MONITOR_QUIT_DOESNT_EXIT 0x08 /* prevent 'quit' from exiting the emulator */ +#define MONITOR_USE_PRETTY 0x08 +#define MONITOR_QUIT_DOESNT_EXIT 0x80 /* prevent 'quit' from exiting the emulator */ /* flags for monitor commands */ #define MONITOR_CMD_ASYNC 0x0001 @@ -33,6 +34,9 @@ typedef enum MonitorEvent { QEVENT_BLOCK_IO_ERROR, QEVENT_RTC_CHANGE, QEVENT_WATCHDOG, + QEVENT_SPICE_CONNECTED, + QEVENT_SPICE_INITIALIZED, + QEVENT_SPICE_DISCONNECTED, QEVENT_MAX, } MonitorEvent; @@ -50,9 +54,9 @@ int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, int monitor_get_fd(Monitor *mon, const char *fdname); -int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap); -void monitor_printf(Monitor *mon, const char *fmt, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); +void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) + GCC_FMT_ATTR(2, 0); +void monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void monitor_print_filename(Monitor *mon, const char *filename); void monitor_flush(Monitor *mon); @@ -60,4 +64,17 @@ typedef void (MonitorCompletion)(void *opaque, QObject *ret_data); void monitor_set_error(Monitor *mon, QError *qerror); +#ifdef CONFIG_ANDROID +typedef int (*MonitorFakeFunc)(void* opaque, const char* str, int strsize); + +/* Create a new fake Monitor object to send all output to an internal + * buffer. This is used to send snapshot save/load errors (produced in + * savevm.c) to the Android console when 'avd snapshot save' or + * 'avd snapshot load' are used. + */ +Monitor* monitor_fake_new(void* opaque, MonitorFakeFunc cb); +int monitor_fake_get_bytes(Monitor* mon); +void monitor_fake_free(Monitor* mon); +#endif + #endif /* !MONITOR_H */ diff --git a/os-posix.c b/os-posix.c new file mode 100644 index 0000000..324bf13 --- /dev/null +++ b/os-posix.c @@ -0,0 +1,398 @@ +/* + * os-posix.c + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +/*needed for MAP_POPULATE before including qemu-options.h */ +#include <sys/mman.h> +#include <pwd.h> +#include <libgen.h> + +/* Needed early for CONFIG_BSD etc. */ +#include "config-host.h" +#include "sysemu.h" +#include "net.h" +#include "qemu-options.h" + +#ifdef CONFIG_LINUX +#include <sys/prctl.h> +#include <sys/syscall.h> +#endif + +#ifdef CONFIG_EVENTFD +#include <sys/eventfd.h> +#endif + +static struct passwd *user_pwd; +static const char *chroot_dir; +static int daemonize; +static int fds[2]; + +void os_setup_early_signal_handling(void) +{ + struct sigaction act; + sigfillset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); +} + +static void termsig_handler(int signal) +{ + qemu_system_shutdown_request(); +} + +static void sigchld_handler(int signal) +{ + waitpid(-1, NULL, WNOHANG); +} + +void os_setup_signal_handling(void) +{ + struct sigaction act; + + memset(&act, 0, sizeof(act)); + act.sa_handler = termsig_handler; + sigaction(SIGINT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGTERM, &act, NULL); + + act.sa_handler = sigchld_handler; + act.sa_flags = SA_NOCLDSTOP; + sigaction(SIGCHLD, &act, NULL); +} + +/* Find a likely location for support files using the location of the binary. + For installed binaries this will be "$bindir/../share/qemu". When + running from the build tree this will be "$bindir/../pc-bios". */ +#define SHARE_SUFFIX "/share/qemu" +#define BUILD_SUFFIX "/pc-bios" +char *os_find_datadir(const char *argv0) +{ + char *dir; + char *p = NULL; + char *res; + char buf[PATH_MAX]; + size_t max_len; + +#if defined(__linux__) + { + int len; + len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); + if (len > 0) { + buf[len] = 0; + p = buf; + } + } +#elif defined(__FreeBSD__) + { + static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + size_t len = sizeof(buf) - 1; + + *buf = '\0'; + if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) && + *buf) { + buf[sizeof(buf) - 1] = '\0'; + p = buf; + } + } +#endif + /* If we don't have any way of figuring out the actual executable + location then try argv[0]. */ + if (!p) { + p = realpath(argv0, buf); + if (!p) { + return NULL; + } + } + dir = dirname(p); + dir = dirname(dir); + + max_len = strlen(dir) + + MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1; + res = qemu_mallocz(max_len); + snprintf(res, max_len, "%s%s", dir, SHARE_SUFFIX); + if (access(res, R_OK)) { + snprintf(res, max_len, "%s%s", dir, BUILD_SUFFIX); + if (access(res, R_OK)) { + qemu_free(res); + res = NULL; + } + } + + return res; +} +#undef SHARE_SUFFIX +#undef BUILD_SUFFIX + +void os_set_proc_name(const char *s) +{ +#if defined(PR_SET_NAME) + char name[16]; + if (!s) + return; + name[sizeof(name) - 1] = 0; + strncpy(name, s, sizeof(name)); + /* Could rewrite argv[0] too, but that's a bit more complicated. + This simple way is enough for `top'. */ + if (prctl(PR_SET_NAME, name)) { + perror("unable to change process name"); + exit(1); + } +#else + fprintf(stderr, "Change of process name not supported by your OS\n"); + exit(1); +#endif +} + +/* + * Parse OS specific command line options. + * return 0 if option handled, -1 otherwise + */ +void os_parse_cmd_args(int index, const char *optarg) +{ + switch (index) { +#ifdef CONFIG_SLIRP + case QEMU_OPTION_smb: +#if 1 + net_slirp_smb(optarg); +#else + if (net_slirp_smb(optarg) < 0) + exit(1); +#endif + break; +#endif + case QEMU_OPTION_runas: + user_pwd = getpwnam(optarg); + if (!user_pwd) { + fprintf(stderr, "User \"%s\" doesn't exist\n", optarg); + exit(1); + } + break; + case QEMU_OPTION_chroot: + chroot_dir = optarg; + break; + case QEMU_OPTION_daemonize: + daemonize = 1; + break; + } + return; +} + +static void change_process_uid(void) +{ + if (user_pwd) { + if (setgid(user_pwd->pw_gid) < 0) { + fprintf(stderr, "Failed to setgid(%d)\n", user_pwd->pw_gid); + exit(1); + } + if (setuid(user_pwd->pw_uid) < 0) { + fprintf(stderr, "Failed to setuid(%d)\n", user_pwd->pw_uid); + exit(1); + } + if (setuid(0) != -1) { + fprintf(stderr, "Dropping privileges failed\n"); + exit(1); + } + } +} + +static void change_root(void) +{ + if (chroot_dir) { + if (chroot(chroot_dir) < 0) { + fprintf(stderr, "chroot failed\n"); + exit(1); + } + if (chdir("/")) { + perror("not able to chdir to /"); + exit(1); + } + } + +} + +void os_daemonize(void) +{ + if (daemonize) { + pid_t pid; + + if (pipe(fds) == -1) + exit(1); + + pid = fork(); + if (pid > 0) { + uint8_t status; + ssize_t len; + + close(fds[1]); + + again: + len = read(fds[0], &status, 1); + if (len == -1 && (errno == EINTR)) + goto again; + + if (len != 1) + exit(1); + else if (status == 1) { + fprintf(stderr, "Could not acquire pidfile: %s\n", strerror(errno)); + exit(1); + } else + exit(0); + } else if (pid < 0) + exit(1); + + close(fds[0]); + qemu_set_cloexec(fds[1]); + + setsid(); + + pid = fork(); + if (pid > 0) + exit(0); + else if (pid < 0) + exit(1); + + umask(027); + + signal(SIGTSTP, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + } +} + +void os_setup_post(void) +{ + int fd = 0; + + if (daemonize) { + uint8_t status = 0; + ssize_t len; + + again1: + len = write(fds[1], &status, 1); + if (len == -1 && (errno == EINTR)) + goto again1; + + if (len != 1) + exit(1); + + if (chdir("/")) { + perror("not able to chdir to /"); + exit(1); + } + TFR(fd = qemu_open("/dev/null", O_RDWR)); + if (fd == -1) + exit(1); + } + + change_root(); + change_process_uid(); + + if (daemonize) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + + close(fd); + } +} + +void os_pidfile_error(void) +{ + if (daemonize) { + uint8_t status = 1; + if (write(fds[1], &status, 1) != 1) { + perror("daemonize. Writing to pipe\n"); + } + } else + fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno)); +} + +void os_set_line_buffering(void) +{ + setvbuf(stdout, NULL, _IOLBF, 0); +} + +/* + * Creates an eventfd that looks like a pipe and has EFD_CLOEXEC set. + */ +int qemu_eventfd(int fds[2]) +{ +#ifdef CONFIG_EVENTFD + int ret; + + ret = eventfd(0, 0); + if (ret >= 0) { + fds[0] = ret; + qemu_set_cloexec(ret); + if ((fds[1] = dup(ret)) == -1) { + close(ret); + return -1; + } + qemu_set_cloexec(fds[1]); + return 0; + } + + if (errno != ENOSYS) { + return -1; + } +#endif + + return qemu_pipe(fds); +} + +int qemu_create_pidfile(const char *filename) +{ + char buffer[128]; + int len; + int fd; + + fd = qemu_open(filename, O_RDWR | O_CREAT, 0600); + if (fd == -1) { + return -1; + } + if (lockf(fd, F_TLOCK, 0) == -1) { + return -1; + } + len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); + if (write(fd, buffer, len) != len) { + return -1; + } + + return 0; +} + +int qemu_get_thread_id(void) +{ +#if defined (__linux__) + return syscall(SYS_gettid); +#else + return getpid(); +#endif +} diff --git a/os-win32.c b/os-win32.c new file mode 100644 index 0000000..d6d54c6 --- /dev/null +++ b/os-win32.c @@ -0,0 +1,273 @@ +/* + * os-win32.c + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <windows.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#include <sys/time.h> +#include "config-host.h" +#include "sysemu.h" +#include "qemu-options.h" + +/***********************************************************/ +/* Functions missing in mingw */ + +int setenv(const char *name, const char *value, int overwrite) +{ + int result = 0; + if (overwrite || !getenv(name)) { + size_t length = strlen(name) + strlen(value) + 2; + char *string = qemu_malloc(length); + snprintf(string, length, "%s=%s", name, value); + result = putenv(string); + } + return result; +} + +/***********************************************************/ +/* Polling handling */ + +typedef struct PollingEntry { + PollingFunc *func; + void *opaque; + struct PollingEntry *next; +} PollingEntry; + +static PollingEntry *first_polling_entry; + +int qemu_add_polling_cb(PollingFunc *func, void *opaque) +{ + PollingEntry **ppe, *pe; + pe = qemu_mallocz(sizeof(PollingEntry)); + pe->func = func; + pe->opaque = opaque; + for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next); + *ppe = pe; + return 0; +} + +void qemu_del_polling_cb(PollingFunc *func, void *opaque) +{ + PollingEntry **ppe, *pe; + for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) { + pe = *ppe; + if (pe->func == func && pe->opaque == opaque) { + *ppe = pe->next; + qemu_free(pe); + break; + } + } +} + +/***********************************************************/ +/* Wait objects support */ +typedef struct WaitObjects { + int num; + HANDLE events[MAXIMUM_WAIT_OBJECTS + 1]; + WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1]; + void *opaque[MAXIMUM_WAIT_OBJECTS + 1]; +} WaitObjects; + +static WaitObjects wait_objects = {0}; + +int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) +{ + WaitObjects *w = &wait_objects; + + if (w->num >= MAXIMUM_WAIT_OBJECTS) + return -1; + w->events[w->num] = handle; + w->func[w->num] = func; + w->opaque[w->num] = opaque; + w->num++; + return 0; +} + +void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) +{ + int i, found; + WaitObjects *w = &wait_objects; + + found = 0; + for (i = 0; i < w->num; i++) { + if (w->events[i] == handle) + found = 1; + if (found) { + w->events[i] = w->events[i + 1]; + w->func[i] = w->func[i + 1]; + w->opaque[i] = w->opaque[i + 1]; + } + } + if (found) + w->num--; +} + +void os_host_main_loop_wait(int *timeout) +{ + int ret, ret2, i; + PollingEntry *pe; + + /* XXX: need to suppress polling by better using win32 events */ + ret = 0; + for(pe = first_polling_entry; pe != NULL; pe = pe->next) { + ret |= pe->func(pe->opaque); + } + if (ret == 0) { + int err; + WaitObjects *w = &wait_objects; + + qemu_mutex_unlock_iothread(); + ret = WaitForMultipleObjects(w->num, w->events, FALSE, *timeout); + qemu_mutex_lock_iothread(); + if (WAIT_OBJECT_0 + 0 <= ret && ret <= WAIT_OBJECT_0 + w->num - 1) { + if (w->func[ret - WAIT_OBJECT_0]) + w->func[ret - WAIT_OBJECT_0](w->opaque[ret - WAIT_OBJECT_0]); + + /* Check for additional signaled events */ + for(i = (ret - WAIT_OBJECT_0 + 1); i < w->num; i++) { + + /* Check if event is signaled */ + ret2 = WaitForSingleObject(w->events[i], 0); + if(ret2 == WAIT_OBJECT_0) { + if (w->func[i]) + w->func[i](w->opaque[i]); + } else if (ret2 == WAIT_TIMEOUT) { + } else { + err = GetLastError(); + fprintf(stderr, "WaitForSingleObject error %d %d\n", i, err); + } + } + } else if (ret == WAIT_TIMEOUT) { + } else { + err = GetLastError(); + fprintf(stderr, "WaitForMultipleObjects error %d %d\n", ret, err); + } + } + + *timeout = 0; +} + +static BOOL WINAPI qemu_ctrl_handler(DWORD type) +{ + exit(STATUS_CONTROL_C_EXIT); + return TRUE; +} + +void os_setup_early_signal_handling(void) +{ + /* Note: cpu_interrupt() is currently not SMP safe, so we force + QEMU to run on a single CPU */ + HANDLE h; + DWORD_PTR mask, smask; + int i; + + SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE); + + h = GetCurrentProcess(); + if (GetProcessAffinityMask(h, &mask, &smask)) { + for(i = 0; i < 32; i++) { + if (mask & (1 << i)) + break; + } + if (i != 32) { + mask = 1 << i; + SetProcessAffinityMask(h, mask); + } + } +} + +/* Look for support files in the same directory as the executable. */ +char *os_find_datadir(const char *argv0) +{ + char *p; + char buf[MAX_PATH]; + DWORD len; + + len = GetModuleFileName(NULL, buf, sizeof(buf) - 1); + if (len == 0) { + return NULL; + } + + buf[len] = 0; + p = buf + len - 1; + while (p != buf && *p != '\\') + p--; + *p = 0; + if (access(buf, R_OK) == 0) { + return qemu_strdup(buf); + } + return NULL; +} + +void os_set_line_buffering(void) +{ + setbuf(stdout, NULL); + setbuf(stderr, NULL); +} + +/* + * Parse OS specific command line options. + * return 0 if option handled, -1 otherwise + */ +void os_parse_cmd_args(int index, const char *optarg) +{ + return; +} + +void os_pidfile_error(void) +{ + fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno)); +} + +int qemu_create_pidfile(const char *filename) +{ + char buffer[128]; + int len; + HANDLE file; + OVERLAPPED overlap; + BOOL ret; + memset(&overlap, 0, sizeof(overlap)); + + file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file == INVALID_HANDLE_VALUE) { + return -1; + } + len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); + ret = WriteFileEx(file, (LPCVOID)buffer, (DWORD)len, + &overlap, NULL); + if (ret == 0) { + return -1; + } + return 0; +} + +int qemu_get_thread_id(void) +{ + return GetCurrentThreadId(); +} @@ -32,13 +32,16 @@ /* Needed early for CONFIG_BSD etc. */ #include "config-host.h" +#if defined(CONFIG_MADVISE) || defined(CONFIG_POSIX_MADVISE) +#include <sys/mman.h> +#endif + #ifdef CONFIG_SOLARIS #include <sys/types.h> #include <sys/statvfs.h> -#endif - -#ifdef CONFIG_EVENTFD -#include <sys/eventfd.h> +/* See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for + discussion about Solaris header problems */ +extern int madvise(caddr_t, size_t, int); #endif #ifdef _WIN32 @@ -63,191 +66,22 @@ typedef int32_t socklen_t; #include "sysemu.h" #include "qemu_socket.h" -#if !defined(_POSIX_C_SOURCE) || defined(_WIN32) || defined(__sun__) || defined(__APPLE__) -static void *oom_check(void *ptr) -{ - if (ptr == NULL) { -#if defined(_WIN32) - fprintf(stderr, "Failed to allocate memory: %lu\n", GetLastError()); -#else - fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno)); -#endif - abort(); - } - return ptr; -} -#endif - -#if defined(_WIN32) -void *qemu_memalign(size_t alignment, size_t size) -{ - if (!size) { - abort(); - } - return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); -} - -void *qemu_vmalloc(size_t size) -{ - /* FIXME: this is not exactly optimal solution since VirtualAlloc - has 64Kb granularity, but at least it guarantees us that the - memory is page aligned. */ - if (!size) { - abort(); - } - return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); -} - -void qemu_vfree(void *ptr) -{ - VirtualFree(ptr, 0, MEM_RELEASE); -} - -#else - -void *qemu_memalign(size_t alignment, size_t size) -{ -#if defined(_POSIX_C_SOURCE) && !defined(__sun__) && !defined(__APPLE__) - int ret; - void *ptr; - ret = posix_memalign(&ptr, alignment, size); - if (ret != 0) { - fprintf(stderr, "Failed to allocate %zu B: %s\n", - size, strerror(ret)); - abort(); - } - return ptr; -#elif defined(CONFIG_BSD) - return oom_check(valloc(size)); -#else - return oom_check(memalign(alignment, size)); -#endif -} - -/* alloc shared memory pages */ -void *qemu_vmalloc(size_t size) -{ - return qemu_memalign(getpagesize(), size); -} - -void qemu_vfree(void *ptr) +int qemu_madvise(void *addr, size_t len, int advice) { - free(ptr); -} - -#endif - -int qemu_create_pidfile(const char *filename) -{ - char buffer[128]; - int len; -#ifndef _WIN32 - int fd; - - fd = qemu_open(filename, O_RDWR | O_CREAT, 0600); - if (fd == -1) - return -1; - - if (lockf(fd, F_TLOCK, 0) == -1) - return -1; - - len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); - if (write(fd, buffer, len) != len) + if (advice == QEMU_MADV_INVALID) { + errno = EINVAL; return -1; -#else - HANDLE file; - OVERLAPPED overlap; - BOOL ret; - memset(&overlap, 0, sizeof(overlap)); - - file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if (file == INVALID_HANDLE_VALUE) - return -1; - - len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); - ret = WriteFileEx(file, (LPCVOID)buffer, (DWORD)len, - &overlap, NULL); - if (ret == 0) - return -1; -#endif - return 0; -} - -#ifdef _WIN32 - -/* mingw32 needs ffs for compilations without optimization. */ -int ffs(int i) -{ - /* Use gcc's builtin ffs. */ - return __builtin_ffs(i); -} - -/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */ -#define _W32_FT_OFFSET (116444736000000000ULL) - -int qemu_gettimeofday(qemu_timeval *tp) -{ - union { - unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */ - FILETIME ft; - } _now; - - if(tp) - { - GetSystemTimeAsFileTime (&_now.ft); - tp->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL ); - tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL); } - /* Always return 0 as per Open Group Base Specifications Issue 6. - Do not set errno on error. */ - return 0; -} -#endif /* _WIN32 */ - - -#ifdef _WIN32 -#ifndef CONFIG_ANDROID -void socket_set_nonblock(int fd) -{ - unsigned long opt = 1; - ioctlsocket(fd, FIONBIO, &opt); -} -#endif - -int inet_aton(const char *cp, struct in_addr *ia) -{ - uint32_t addr = inet_addr(cp); - if (addr == 0xffffffff) - return 0; - ia->s_addr = addr; - return 1; -} - -void qemu_set_cloexec(int fd) -{ -} - +#if defined(CONFIG_MADVISE) + return madvise(addr, len, advice); +#elif defined(CONFIG_POSIX_MADVISE) + return posix_madvise(addr, len, advice); #else - -#ifndef CONFIG_ANDROID -void socket_set_nonblock(int fd) -{ - int f; - f = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, f | O_NONBLOCK); -} + errno = EINVAL; + return -1; #endif - -void qemu_set_cloexec(int fd) -{ - int f; - f = fcntl(fd, F_GETFD); - fcntl(fd, F_SETFD, f | FD_CLOEXEC); } -#endif /* * Opens a file with FD_CLOEXEC set @@ -309,58 +143,6 @@ ssize_t qemu_write_full(int fd, const void *buf, size_t count) return total; } -#ifndef _WIN32 -/* - * Creates an eventfd that looks like a pipe and has EFD_CLOEXEC set. - */ -int qemu_eventfd(int fds[2]) -{ -#ifdef CONFIG_EVENTFD - int ret; - - ret = eventfd(0, 0); - if (ret >= 0) { - fds[0] = ret; - qemu_set_cloexec(ret); - if ((fds[1] = dup(ret)) == -1) { - close(ret); - return -1; - } - qemu_set_cloexec(fds[1]); - return 0; - } - - if (errno != ENOSYS) { - return -1; - } -#endif - - return qemu_pipe(fds); -} - -/* - * Creates a pipe with FD_CLOEXEC set on both file descriptors - */ -int qemu_pipe(int pipefd[2]) -{ - int ret; - -#ifdef CONFIG_PIPE2 - ret = pipe2(pipefd, O_CLOEXEC); - if (ret != -1 || errno != ENOSYS) { - return ret; - } -#endif - ret = pipe(pipefd); - if (ret == 0) { - qemu_set_cloexec(pipefd[0]); - qemu_set_cloexec(pipefd[1]); - } - - return ret; -} -#endif - /* * Opens a socket with FD_CLOEXEC set */ @@ -8,9 +8,7 @@ #include <sys/signal.h> #endif -#ifndef _WIN32 #include <sys/time.h> -#endif #ifndef glue #define xglue(x, y) x ## y @@ -57,6 +55,10 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#endif + #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #endif @@ -90,23 +92,60 @@ void *qemu_memalign(size_t alignment, size_t size); void *qemu_vmalloc(size_t size); void qemu_vfree(void *ptr); -int qemu_create_pidfile(const char *filename); +#define QEMU_MADV_INVALID -1 -#ifdef _WIN32 -int ffs(int i); +#if defined(CONFIG_MADVISE) + +#define QEMU_MADV_WILLNEED MADV_WILLNEED +#define QEMU_MADV_DONTNEED MADV_DONTNEED +#ifdef MADV_DONTFORK +#define QEMU_MADV_DONTFORK MADV_DONTFORK +#else +#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID +#endif +#ifdef MADV_MERGEABLE +#define QEMU_MADV_MERGEABLE MADV_MERGEABLE +#else +#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID +#endif -int setenv(const char *name, const char *value, int overwrite); -int asprintf(char **sptr, char *fmt, ...); -int vasprintf(char **sptr, char *fmt, va_list args); +#elif defined(CONFIG_POSIX_MADVISE) -typedef struct { - long tv_sec; - long tv_usec; -} qemu_timeval; -int qemu_gettimeofday(qemu_timeval *tp); +#define QEMU_MADV_WILLNEED POSIX_MADV_WILLNEED +#define QEMU_MADV_DONTNEED POSIX_MADV_DONTNEED +#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID +#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID + +#else /* no-op */ + +#define QEMU_MADV_WILLNEED QEMU_MADV_INVALID +#define QEMU_MADV_DONTNEED QEMU_MADV_INVALID +#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID +#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID + +#endif + +int qemu_madvise(void *addr, size_t len, int advice); + +int qemu_create_pidfile(const char *filename); +int qemu_get_thread_id(void); + +#ifdef _WIN32 +static inline void qemu_timersub(const struct timeval *val1, + const struct timeval *val2, + struct timeval *res) +{ + res->tv_sec = val1->tv_sec - val2->tv_sec; + if (val1->tv_usec < val2->tv_usec) { + res->tv_sec--; + res->tv_usec = val1->tv_usec - val2->tv_usec + 1000 * 1000; + } else { + res->tv_usec = val1->tv_usec - val2->tv_usec; + } +} #else -typedef struct timeval qemu_timeval; -#define qemu_gettimeofday(tp) gettimeofday(tp, NULL); -#endif /* !_WIN32 */ +#define qemu_timersub timersub +#define ffs __builtin_ffs +#endif #endif diff --git a/oslib-posix.c b/oslib-posix.c new file mode 100644 index 0000000..ba6e559 --- /dev/null +++ b/oslib-posix.c @@ -0,0 +1,159 @@ +/* + * os-posix-lib.c + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Red Hat, Inc. + * + * QEMU library functions on POSIX which are shared between QEMU and + * the QEMU tools. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config-host.h" +#include "sysemu.h" +#include "trace.h" +#include "qemu_socket.h" + +void *qemu_oom_check(void *ptr) +{ + if (ptr == NULL) { + fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno)); + abort(); + } + return ptr; +} + +void *qemu_memalign(size_t alignment, size_t size) +{ + void *ptr; +#if defined(_POSIX_C_SOURCE) && !defined(__sun__) + int ret; + ret = posix_memalign(&ptr, alignment, size); + if (ret != 0) { + fprintf(stderr, "Failed to allocate %zu B: %s\n", + size, strerror(ret)); + abort(); + } +#elif defined(CONFIG_BSD) + ptr = qemu_oom_check(valloc(size)); +#else + ptr = qemu_oom_check(memalign(alignment, size)); +#endif + //trace_qemu_memalign(alignment, size, ptr); + return ptr; +} + +/* alloc shared memory pages */ +void *qemu_vmalloc(size_t size) +{ + return qemu_memalign(getpagesize(), size); +} + +void qemu_vfree(void *ptr) +{ + //trace_qemu_vfree(ptr); + free(ptr); +} + +#if 0 /* in sockets.c */ +void socket_set_nonblock(int fd) +{ + int f; + f = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, f | O_NONBLOCK); +} +#endif + +void qemu_set_cloexec(int fd) +{ + int f; + f = fcntl(fd, F_GETFD); + fcntl(fd, F_SETFD, f | FD_CLOEXEC); +} + +/* + * Creates a pipe with FD_CLOEXEC set on both file descriptors + */ +int qemu_pipe(int pipefd[2]) +{ + int ret; + +#ifdef CONFIG_PIPE2 + ret = pipe2(pipefd, O_CLOEXEC); + if (ret != -1 || errno != ENOSYS) { + return ret; + } +#endif + ret = pipe(pipefd); + if (ret == 0) { + qemu_set_cloexec(pipefd[0]); + qemu_set_cloexec(pipefd[1]); + } + + return ret; +} + +int qemu_utimensat(int dirfd, const char *path, const struct timespec *times, + int flags) +{ + struct timeval tv[2], tv_now; + struct stat st; + int i; +#ifdef CONFIG_UTIMENSAT + int ret; + + ret = utimensat(dirfd, path, times, flags); + if (ret != -1 || errno != ENOSYS) { + return ret; + } +#endif + /* Fallback: use utimes() instead of utimensat() */ + + /* happy if special cases */ + if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) { + return 0; + } + if (times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) { + return utimes(path, NULL); + } + + /* prepare for hard cases */ + if (times[0].tv_nsec == UTIME_NOW || times[1].tv_nsec == UTIME_NOW) { + gettimeofday(&tv_now, NULL); + } + if (times[0].tv_nsec == UTIME_OMIT || times[1].tv_nsec == UTIME_OMIT) { + stat(path, &st); + } + + for (i = 0; i < 2; i++) { + if (times[i].tv_nsec == UTIME_NOW) { + tv[i].tv_sec = tv_now.tv_sec; + tv[i].tv_usec = tv_now.tv_usec; + } else if (times[i].tv_nsec == UTIME_OMIT) { + tv[i].tv_sec = (i == 0) ? st.st_atime : st.st_mtime; + tv[i].tv_usec = 0; + } else { + tv[i].tv_sec = times[i].tv_sec; + tv[i].tv_usec = times[i].tv_nsec / 1000; + } + } + + return utimes(path, &tv[0]); +} diff --git a/oslib-win32.c b/oslib-win32.c new file mode 100644 index 0000000..9d82abd --- /dev/null +++ b/oslib-win32.c @@ -0,0 +1,116 @@ +/* + * os-win32.c + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Red Hat, Inc. + * + * QEMU library functions for win32 which are shared between QEMU and + * the QEMU tools. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <windows.h> +#include "config-host.h" +#include "sysemu.h" +#include "trace.h" +#include "qemu_socket.h" + +void *qemu_oom_check(void *ptr) +{ + if (ptr == NULL) { + fprintf(stderr, "Failed to allocate memory: %lu\n", GetLastError()); + abort(); + } + return ptr; +} + +void *qemu_memalign(size_t alignment, size_t size) +{ + void *ptr; + + if (!size) { + abort(); + } + ptr = qemu_oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); + //trace_qemu_memalign(alignment, size, ptr); + return ptr; +} + +void *qemu_vmalloc(size_t size) +{ + 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. */ + if (!size) { + abort(); + } + ptr = qemu_oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); + //trace_qemu_vmalloc(size, ptr); + return ptr; +} + +void qemu_vfree(void *ptr) +{ + //trace_qemu_vfree(ptr); + VirtualFree(ptr, 0, MEM_RELEASE); +} + +#if 0 /* in sockets.c */ +void socket_set_nonblock(int fd) +{ + unsigned long opt = 1; + ioctlsocket(fd, FIONBIO, &opt); +} +#endif + +int inet_aton(const char *cp, struct in_addr *ia) +{ + uint32_t addr = inet_addr(cp); + if (addr == 0xffffffff) { + return 0; + } + ia->s_addr = addr; + return 1; +} + +void qemu_set_cloexec(int fd) +{ +} + +/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */ +#define _W32_FT_OFFSET (116444736000000000ULL) + +int qemu_gettimeofday(qemu_timeval *tp) +{ + union { + unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */ + FILETIME ft; + } _now; + + if(tp) { + GetSystemTimeAsFileTime (&_now.ft); + tp->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL ); + tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL); + } + /* Always return 0 as per Open Group Base Specifications Issue 6. + Do not set errno on error. */ + return 0; +} diff --git a/outputchannel.c b/outputchannel.c deleted file mode 100644 index 192b488..0000000 --- a/outputchannel.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "outputchannel.h" -#include "qemu-common.h" - -struct OutputChannel { - void* opaque; /* caller-specific information */ - OutputChannelPrintf printf; /* callback function to do the printing */ - unsigned int written; /* number of bytes written to the channel */ -}; - -OutputChannel* output_channel_alloc(void* opaque, OutputChannelPrintf cb) -{ - OutputChannel* oc = qemu_mallocz(sizeof(*oc)); - oc->printf = cb; - oc->opaque = opaque; - oc->written = 0; - - return oc; -} - -int output_channel_printf(OutputChannel* oc, const char* fmt, ...) -{ - int ret; - va_list ap; - va_start(ap, fmt); - ret = oc->printf(oc->opaque, fmt, ap); - va_end(ap); - - /* Don't count errors and no-ops towards number of bytes written */ - if (ret > 0) { - oc->written += ret; - } - - return ret; -} - -void output_channel_free(OutputChannel* oc) -{ - free(oc); -} - -unsigned int output_channel_written(OutputChannel* oc) -{ - return oc->written; -} diff --git a/outputchannel.h b/outputchannel.h deleted file mode 100644 index 4dece8d..0000000 --- a/outputchannel.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef _OUTPUTCHANNEL_H -#define _OUTPUTCHANNEL_H - -#include <stdarg.h> - -/* Callback function to print a printf-formatted string to a channel */ -typedef int (*OutputChannelPrintf) (void* opaque, const char* fmt, va_list ap); - -typedef struct OutputChannel OutputChannel; - -/* Allocates a new output channel */ -OutputChannel* output_channel_alloc(void* opaque, OutputChannelPrintf cb); - -/* Prints a printf-formatted string to the output channel */ -int output_channel_printf(OutputChannel* oc, const char* fmt, ...); - -/* Frees an output channel */ -void output_channel_free(OutputChannel* oc); - -/* Returns the number of bytes written to the channel */ -unsigned int output_channel_written(OutputChannel* oc); - -#endif /* _OUTPUTCHANNEL_H */ @@ -41,7 +41,8 @@ static int strneq(const char *s1, unsigned int n, const char *s2) return s2[i] == 0; } -static struct pathelem *add_entry(struct pathelem *root, const char *name); +static struct pathelem *add_entry(struct pathelem *root, const char *name, + unsigned char type); static struct pathelem *new_entry(const char *root, struct pathelem *parent, @@ -59,6 +60,15 @@ static struct pathelem *new_entry(const char *root, #define streq(a,b) (strcmp((a), (b)) == 0) +/* Not all systems provide this feature */ +#if defined(DT_DIR) && defined(DT_UNKNOWN) +# define dirent_type(dirent) ((dirent)->d_type) +# define is_dir_maybe(type) ((type) == DT_DIR || (type) == DT_UNKNOWN) +#else +# define dirent_type(dirent) (1) +# define is_dir_maybe(type) (type) +#endif + static struct pathelem *add_dir_maybe(struct pathelem *path) { DIR *dir; @@ -68,7 +78,7 @@ static struct pathelem *add_dir_maybe(struct pathelem *path) while ((dirent = readdir(dir)) != NULL) { if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){ - path = add_entry(path, dirent->d_name); + path = add_entry(path, dirent->d_name, dirent_type(dirent)); } } closedir(dir); @@ -76,16 +86,22 @@ static struct pathelem *add_dir_maybe(struct pathelem *path) return path; } -static struct pathelem *add_entry(struct pathelem *root, const char *name) +static struct pathelem *add_entry(struct pathelem *root, const char *name, + unsigned char type) { + struct pathelem **e; + root->num_entries++; root = realloc(root, sizeof(*root) + sizeof(root->entries[0])*root->num_entries); + e = &root->entries[root->num_entries-1]; + + *e = new_entry(root->pathname, root, name); + if (is_dir_maybe(type)) { + *e = add_dir_maybe(*e); + } - root->entries[root->num_entries-1] = new_entry(root->pathname, root, name); - root->entries[root->num_entries-1] - = add_dir_maybe(root->entries[root->num_entries-1]); return root; } @@ -10,6 +10,7 @@ #pragma GCC poison TARGET_ALPHA #pragma GCC poison TARGET_ARM #pragma GCC poison TARGET_CRIS +#pragma GCC poison TARGET_LM32 #pragma GCC poison TARGET_M68K #pragma GCC poison TARGET_MIPS #pragma GCC poison TARGET_MIPS64 @@ -45,6 +46,14 @@ #pragma GCC poison CPU_INTERRUPT_DEBUG #pragma GCC poison CPU_INTERRUPT_VIRQ #pragma GCC poison CPU_INTERRUPT_NMI +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_0 +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_1 +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_2 +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_3 +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_4 +#pragma GCC poison CPU_INTERRUPT_TGT_INT_0 +#pragma GCC poison CPU_INTERRUPT_TGT_INT_1 +#pragma GCC poison CPU_INTERRUPT_TGT_INT_2 #endif #endif diff --git a/posix-aio-compat.c b/posix-aio-compat.c index a67ffe3..e1c58d0 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -24,6 +24,7 @@ #include "qemu-queue.h" #include "osdep.h" +#include "sysemu.h" #include "qemu-common.h" #include "block_int.h" @@ -127,7 +128,7 @@ static ssize_t handle_aiocb_ioctl(struct qemu_paiocb *aiocb) /* * This looks weird, but the aio code only consideres a request - * successfull if it has written the number full number of bytes. + * successful if it has written the number full number of bytes. * * Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command, * so in fact we return the ioctl command here to make posix_aio_read() @@ -269,7 +270,7 @@ static ssize_t handle_aiocb_rw(struct qemu_paiocb *aiocb) * Ok, we have to do it the hard way, copy all segments into * a single aligned buffer. */ - buf = qemu_memalign(512, aiocb->aio_nbytes); + buf = qemu_blockalign(aiocb->common.bs, aiocb->aio_nbytes); if (aiocb->aio_type & QEMU_AIO_WRITE) { char *p = buf; int i; @@ -453,6 +454,9 @@ static int posix_aio_process_queue(void *opaque) } else { ret = -ret; } + + //trace_paio_complete(acb, acb->common.opaque, ret); + /* remove the request */ *pacb = acb->next; /* call the callback */ @@ -535,6 +539,8 @@ static void paio_cancel(BlockDriverAIOCB *blockacb) struct qemu_paiocb *acb = (struct qemu_paiocb *)blockacb; int active = 0; + //trace_paio_cancel(acb, acb->common.opaque); + mutex_lock(&lock); if (!acb->active) { QTAILQ_REMOVE(&request_list, acb, node); @@ -583,6 +589,7 @@ BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd, acb->next = posix_aio_state->first_aio; posix_aio_state->first_aio = acb; + //trace_paio_submit(acb, opaque, sector_num, nb_sectors, type); qemu_paio_submit(acb); return &acb->common; } @@ -599,6 +606,7 @@ BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd, acb->aio_type = QEMU_AIO_IOCTL; acb->aio_fildes = fd; acb->ev_signo = SIGUSR2; + acb->async_context_id = get_async_context_id(); acb->aio_offset = 0; acb->aio_ioctl_buf = buf; acb->aio_ioctl_cmd = req; diff --git a/qemu-char-android.c b/qemu-char-android.c deleted file mode 100644 index 0ecf01c..0000000 --- a/qemu-char-android.c +++ /dev/null @@ -1,2441 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu-common.h" -#include "sockets.h" -#include "net.h" -#include "monitor.h" -#include "console.h" -#include "sysemu.h" -#include "qemu-timer.h" -#include "qemu-char.h" -#include "block.h" -#include "hw/usb.h" -#include "hw/baum.h" -#include "hw/msmouse.h" - -#include <unistd.h> -#include <fcntl.h> -#include <signal.h> -#include <time.h> -#include <errno.h> -#include <sys/time.h> -#include <zlib.h> - -#ifndef _WIN32 -#include <sys/times.h> -#include <sys/wait.h> -#include <termios.h> -#include <sys/mman.h> -#include <sys/ioctl.h> -#include <sys/resource.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <net/if.h> -#ifdef __NetBSD__ -#include <net/if_tap.h> -#endif -#ifdef __linux__ -#include <linux/if_tun.h> -#endif -#include <arpa/inet.h> -#include <dirent.h> -#include <netdb.h> -#include <sys/select.h> -#ifdef CONFIG_BSD -#include <sys/stat.h> -#ifdef __FreeBSD__ -#include <libutil.h> -#include <dev/ppbus/ppi.h> -#include <dev/ppbus/ppbconf.h> -#elif defined(__DragonFly__) -#include <libutil.h> -#include <dev/misc/ppi/ppi.h> -#include <bus/ppbus/ppbconf.h> -#else -#include <util.h> -#endif -#elif defined (__GLIBC__) && defined (__FreeBSD_kernel__) -#include <freebsd/stdlib.h> -#else -#ifdef __linux__ -#include <pty.h> - -#include <linux/ppdev.h> -#include <linux/parport.h> -#endif -#ifdef __sun__ -#include <sys/stat.h> -#include <sys/ethernet.h> -#include <sys/sockio.h> -#include <netinet/arp.h> -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> // must come after ip.h -#include <netinet/udp.h> -#include <netinet/tcp.h> -#include <net/if.h> -#include <syslog.h> -#include <stropts.h> -#endif -#endif -#endif - -#include "qemu_socket.h" - -#define READ_BUF_LEN 4096 - -#ifdef CONFIG_ANDROID -#include "charpipe.h" -#include "modem_driver.h" -#include "android/gps.h" -#include "android/hw-kmsg.h" -#include "android/hw-qemud.h" -#endif /* CONFIG_ANDROID */ - -/***********************************************************/ -/* character device */ - -static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs = - QTAILQ_HEAD_INITIALIZER(chardevs); -static int initial_reset_issued; - -static void qemu_chr_event(CharDriverState *s, int event) -{ - /* Keep track if the char device is open */ - switch (event) { - case CHR_EVENT_OPENED: - s->opened = 1; - break; - case CHR_EVENT_CLOSED: - s->opened = 0; - break; - } - - if (!s->chr_event) - return; - s->chr_event(s->handler_opaque, event); -} - -static void qemu_chr_generic_open_bh(void *opaque) -{ - CharDriverState *s = opaque; - qemu_chr_event(s, CHR_EVENT_OPENED); - qemu_bh_delete(s->bh); - s->bh = NULL; -} - -void qemu_chr_generic_open(CharDriverState *s) -{ - if (s->bh == NULL) { - s->bh = qemu_bh_new(qemu_chr_generic_open_bh, s); - qemu_bh_schedule(s->bh); - } -} - -void qemu_chr_reset(CharDriverState *s) -{ - if (s->bh == NULL && initial_reset_issued) { - s->bh = qemu_bh_new(qemu_chr_generic_open_bh, s); - qemu_bh_schedule(s->bh); - } -} - -void qemu_chr_initial_reset(void) -{ - CharDriverState *chr; - - initial_reset_issued = 1; - - QTAILQ_FOREACH(chr, &chardevs, next) { - qemu_chr_reset(chr); - } -} - -int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len) -{ - return s->chr_write(s, buf, len); -} - -int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg) -{ - if (!s->chr_ioctl) - return -ENOTSUP; - return s->chr_ioctl(s, cmd, arg); -} - -int qemu_chr_can_read(CharDriverState *s) -{ - if (!s->chr_can_read) - return 0; - return s->chr_can_read(s->handler_opaque); -} - -void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len) -{ - s->chr_read(s->handler_opaque, buf, len); -} - -int qemu_chr_get_msgfd(CharDriverState *s) -{ - return s->get_msgfd ? s->get_msgfd(s) : -1; -} - -void qemu_chr_accept_input(CharDriverState *s) -{ - if (s->chr_accept_input) - s->chr_accept_input(s); -} - -void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) -{ - char buf[READ_BUF_LEN]; - va_list ap; - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - qemu_chr_write(s, (uint8_t *)buf, strlen(buf)); - va_end(ap); -} - -void qemu_chr_send_event(CharDriverState *s, int event) -{ - if (s->chr_send_event) - s->chr_send_event(s, event); -} - -void qemu_chr_add_handlers(CharDriverState *s, - IOCanReadHandler *fd_can_read, - IOReadHandler *fd_read, - IOEventHandler *fd_event, - void *opaque) -{ - s->chr_can_read = fd_can_read; - s->chr_read = fd_read; - s->chr_event = fd_event; - s->handler_opaque = opaque; - if (s->chr_update_read_handler) - s->chr_update_read_handler(s); - - /* We're connecting to an already opened device, so let's make sure we - also get the open event */ - if (s->opened) { - qemu_chr_generic_open(s); - } -} - -static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) -{ - return len; -} - -static CharDriverState *qemu_chr_open_null(void) -{ - CharDriverState *chr; - - chr = qemu_mallocz(sizeof(CharDriverState)); - chr->chr_write = null_chr_write; - return chr; -} - -/* MUX driver for serial I/O splitting */ -#define MAX_MUX 4 -#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ -#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) -typedef struct { - IOCanReadHandler *chr_can_read[MAX_MUX]; - IOReadHandler *chr_read[MAX_MUX]; - IOEventHandler *chr_event[MAX_MUX]; - void *ext_opaque[MAX_MUX]; - CharDriverState *drv; - int focus; - int mux_cnt; - int term_got_escape; - int max_size; - /* Intermediate input buffer allows to catch escape sequences even if the - currently active device is not accepting any input - but only until it - is full as well. */ - unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE]; - int prod[MAX_MUX]; - int cons[MAX_MUX]; - int timestamps; - int linestart; - int64_t timestamps_start; -} MuxDriver; - - -static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) -{ - MuxDriver *d = chr->opaque; - int ret; - if (!d->timestamps) { - ret = d->drv->chr_write(d->drv, buf, len); - } else { - int i; - - ret = 0; - for (i = 0; i < len; i++) { - if (d->linestart) { - char buf1[64]; - int64_t ti; - int secs; - - ti = qemu_get_clock(rt_clock); - if (d->timestamps_start == -1) - d->timestamps_start = ti; - ti -= d->timestamps_start; - secs = ti / 1000; - snprintf(buf1, sizeof(buf1), - "[%02d:%02d:%02d.%03d] ", - secs / 3600, - (secs / 60) % 60, - secs % 60, - (int)(ti % 1000)); - d->drv->chr_write(d->drv, (uint8_t *)buf1, strlen(buf1)); - d->linestart = 0; - } - ret += d->drv->chr_write(d->drv, buf+i, 1); - if (buf[i] == '\n') { - d->linestart = 1; - } - } - } - return ret; -} - -static const char * const mux_help[] = { - "% h print this help\n\r", - "% x exit emulator\n\r", - "% s save disk data back to file (if -snapshot)\n\r", - "% t toggle console timestamps\n\r" - "% b send break (magic sysrq)\n\r", - "% c switch between console and monitor\n\r", - "% % sends %\n\r", - NULL -}; - -int term_escape_char = 0x01; /* ctrl-a is used for escape */ -static void mux_print_help(CharDriverState *chr) -{ - int i, j; - char ebuf[15] = "Escape-Char"; - char cbuf[50] = "\n\r"; - - if (term_escape_char > 0 && term_escape_char < 26) { - snprintf(cbuf, sizeof(cbuf), "\n\r"); - snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a'); - } else { - snprintf(cbuf, sizeof(cbuf), - "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r", - term_escape_char); - } - chr->chr_write(chr, (uint8_t *)cbuf, strlen(cbuf)); - for (i = 0; mux_help[i] != NULL; i++) { - for (j=0; mux_help[i][j] != '\0'; j++) { - if (mux_help[i][j] == '%') - chr->chr_write(chr, (uint8_t *)ebuf, strlen(ebuf)); - else - chr->chr_write(chr, (uint8_t *)&mux_help[i][j], 1); - } - } -} - -static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event) -{ - if (d->chr_event[mux_nr]) - d->chr_event[mux_nr](d->ext_opaque[mux_nr], event); -} - -static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) -{ - if (d->term_got_escape) { - d->term_got_escape = 0; - if (ch == term_escape_char) - goto send_char; - switch(ch) { - case '?': - case 'h': - mux_print_help(chr); - break; - case 'x': - { - const char *term = "QEMU: Terminated\n\r"; - chr->chr_write(chr,(uint8_t *)term,strlen(term)); - exit(0); - break; - } - case 's': - bdrv_commit_all(); - break; - case 'b': - qemu_chr_event(chr, CHR_EVENT_BREAK); - break; - case 'c': - /* Switch to the next registered device */ - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); - d->focus++; - if (d->focus >= d->mux_cnt) - d->focus = 0; - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); - break; - case 't': - d->timestamps = !d->timestamps; - d->timestamps_start = -1; - d->linestart = 0; - break; - } - } else if (ch == term_escape_char) { - d->term_got_escape = 1; - } else { - send_char: - return 1; - } - return 0; -} - -static void mux_chr_accept_input(CharDriverState *chr) -{ - MuxDriver *d = chr->opaque; - int m = d->focus; - - while (d->prod[m] != d->cons[m] && - d->chr_can_read[m] && - d->chr_can_read[m](d->ext_opaque[m])) { - d->chr_read[m](d->ext_opaque[m], - &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1); - } -} - -static int mux_chr_can_read(void *opaque) -{ - CharDriverState *chr = opaque; - MuxDriver *d = chr->opaque; - int m = d->focus; - - if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) - return 1; - if (d->chr_can_read[m]) - return d->chr_can_read[m](d->ext_opaque[m]); - return 0; -} - -static void mux_chr_read(void *opaque, const uint8_t *buf, int size) -{ - CharDriverState *chr = opaque; - MuxDriver *d = chr->opaque; - int m = d->focus; - int i; - - mux_chr_accept_input (opaque); - - for(i = 0; i < size; i++) - if (mux_proc_byte(chr, d, buf[i])) { - if (d->prod[m] == d->cons[m] && - d->chr_can_read[m] && - d->chr_can_read[m](d->ext_opaque[m])) - d->chr_read[m](d->ext_opaque[m], &buf[i], 1); - else - d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i]; - } -} - -static void mux_chr_event(void *opaque, int event) -{ - CharDriverState *chr = opaque; - MuxDriver *d = chr->opaque; - int i; - - /* Send the event to all registered listeners */ - for (i = 0; i < d->mux_cnt; i++) - mux_chr_send_event(d, i, event); -} - -static void mux_chr_update_read_handler(CharDriverState *chr) -{ - MuxDriver *d = chr->opaque; - - if (d->mux_cnt >= MAX_MUX) { - fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n"); - return; - } - d->ext_opaque[d->mux_cnt] = chr->handler_opaque; - d->chr_can_read[d->mux_cnt] = chr->chr_can_read; - d->chr_read[d->mux_cnt] = chr->chr_read; - d->chr_event[d->mux_cnt] = chr->chr_event; - /* Fix up the real driver with mux routines */ - if (d->mux_cnt == 0) { - qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read, - mux_chr_event, chr); - } - if (d->focus != -1) { - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); - } - d->focus = d->mux_cnt; - d->mux_cnt++; - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); -} - -static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) -{ - CharDriverState *chr; - MuxDriver *d; - - chr = qemu_mallocz(sizeof(CharDriverState)); - d = qemu_mallocz(sizeof(MuxDriver)); - - chr->opaque = d; - d->drv = drv; - d->focus = -1; - chr->chr_write = mux_chr_write; - chr->chr_update_read_handler = mux_chr_update_read_handler; - chr->chr_accept_input = mux_chr_accept_input; - - /* Muxes are always open on creation */ - qemu_chr_generic_open(chr); - - return chr; -} - - -#ifdef _WIN32 -int send_all(int fd, const void *buf, int len1) -{ -#if 1 - return socket_send(fd, buf, len1); -#else - int ret, len; - - len = len1; - while (len > 0) { - ret = send(fd, buf, len, 0); - if (ret < 0) { - errno = WSAGetLastError(); - if (errno != WSAEWOULDBLOCK && errno != WSAEAGAIN) { - return -1; - } - } else if (ret == 0) { - break; - } else { - buf += ret; - len -= ret; - } - } - return len1 - len; -#endif -} - -#else - -static int unix_write(int fd, const uint8_t *buf, int len1) -{ - int ret, len; - - len = len1; - while (len > 0) { - ret = write(fd, buf, len); - if (ret < 0) { - if (errno != EINTR && errno != EAGAIN) - return -1; - } else if (ret == 0) { - break; - } else { - buf += ret; - len -= ret; - } - } - return len1 - len; -} - -int send_all(int fd, const void *buf, int len1) -{ - return unix_write(fd, buf, len1); -} -#endif /* !_WIN32 */ - -#ifndef _WIN32 - -typedef struct { - int fd_in, fd_out; - int max_size; -} FDCharDriver; - -#define STDIO_MAX_CLIENTS 1 -static int stdio_nb_clients = 0; - -static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) -{ - FDCharDriver *s = chr->opaque; - return send_all(s->fd_out, buf, len); -} - -static int fd_chr_read_poll(void *opaque) -{ - CharDriverState *chr = opaque; - FDCharDriver *s = chr->opaque; - - s->max_size = qemu_chr_can_read(chr); - return s->max_size; -} - -static void fd_chr_read(void *opaque) -{ - CharDriverState *chr = opaque; - FDCharDriver *s = chr->opaque; - int size, len; - uint8_t buf[READ_BUF_LEN]; - - len = sizeof(buf); - if (len > s->max_size) - len = s->max_size; - if (len == 0) - return; - size = read(s->fd_in, buf, len); - if (size == 0) { - /* FD has been closed. Remove it from the active list. */ - qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); - qemu_chr_event(chr, CHR_EVENT_CLOSED); - return; - } - if (size > 0) { - qemu_chr_read(chr, buf, size); - } -} - -static void fd_chr_update_read_handler(CharDriverState *chr) -{ - FDCharDriver *s = chr->opaque; - - if (s->fd_in >= 0) { - if (display_type == DT_NOGRAPHIC && s->fd_in == 0) { - } else { - qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll, - fd_chr_read, NULL, chr); - } - } -} - -static void fd_chr_close(struct CharDriverState *chr) -{ - FDCharDriver *s = chr->opaque; - - if (s->fd_in >= 0) { - if (display_type == DT_NOGRAPHIC && s->fd_in == 0) { - } else { - qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); - } - } - - qemu_free(s); - qemu_chr_event(chr, CHR_EVENT_CLOSED); -} - -/* open a character device to a unix fd */ -static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) -{ - CharDriverState *chr; - FDCharDriver *s; - - chr = qemu_mallocz(sizeof(CharDriverState)); - s = qemu_mallocz(sizeof(FDCharDriver)); - s->fd_in = fd_in; - s->fd_out = fd_out; - chr->opaque = s; - chr->chr_write = fd_chr_write; - chr->chr_update_read_handler = fd_chr_update_read_handler; - chr->chr_close = fd_chr_close; - - qemu_chr_generic_open(chr); - - return chr; -} - -static CharDriverState *qemu_chr_open_file_out(const char *file_out) -{ - int fd_out; - - TFR(fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); - if (fd_out < 0) - return NULL; - return qemu_chr_open_fd(-1, fd_out); -} - -static CharDriverState *qemu_chr_open_pipe(const char *filename) -{ - int fd_in, fd_out; - char filename_in[256], filename_out[256]; - - snprintf(filename_in, 256, "%s.in", filename); - snprintf(filename_out, 256, "%s.out", filename); - TFR(fd_in = open(filename_in, O_RDWR | O_BINARY)); - TFR(fd_out = open(filename_out, O_RDWR | O_BINARY)); - if (fd_in < 0 || fd_out < 0) { - if (fd_in >= 0) - close(fd_in); - if (fd_out >= 0) - close(fd_out); - TFR(fd_in = fd_out = open(filename, O_RDWR | O_BINARY)); - if (fd_in < 0) - return NULL; - } - return qemu_chr_open_fd(fd_in, fd_out); -} - -static CharDriverState *qemu_chr_open_fdpair(const char *fd_pair) -{ - int fd_in, fd_out; - char *endptr; - - /* fd_pair should contain two decimal fd values, separated by - * a colon. */ - endptr = NULL; - fd_in = strtol(fd_pair, &endptr, 10); - if (endptr == NULL || endptr == fd_pair || *endptr != ':') - return NULL; - endptr++; // skip colon - fd_pair = endptr; - endptr = NULL; - fd_out = strtol(fd_pair, &endptr, 10); - if (endptr == NULL || endptr == fd_pair || *endptr != '\0') - return NULL; - - return qemu_chr_open_fd(fd_in, fd_out); -} - -/* for STDIO, we handle the case where several clients use it - (nographic mode) */ - -#define TERM_FIFO_MAX_SIZE 1 - -static uint8_t term_fifo[TERM_FIFO_MAX_SIZE]; -static int term_fifo_size; - -static int stdio_read_poll(void *opaque) -{ - CharDriverState *chr = opaque; - - /* try to flush the queue if needed */ - if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) { - qemu_chr_read(chr, term_fifo, 1); - term_fifo_size = 0; - } - /* see if we can absorb more chars */ - if (term_fifo_size == 0) - return 1; - else - return 0; -} - -static void stdio_read(void *opaque) -{ - int size; - uint8_t buf[1]; - CharDriverState *chr = opaque; - - size = read(0, buf, 1); - if (size == 0) { - /* stdin has been closed. Remove it from the active list. */ - qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); - qemu_chr_event(chr, CHR_EVENT_CLOSED); - return; - } - if (size > 0) { - if (qemu_chr_can_read(chr) > 0) { - qemu_chr_read(chr, buf, 1); - } else if (term_fifo_size == 0) { - term_fifo[term_fifo_size++] = buf[0]; - } - } -} - -/* init terminal so that we can grab keys */ -static struct termios oldtty; -static int old_fd0_flags; -static int term_atexit_done; - -static void term_exit(void) -{ - tcsetattr (0, TCSANOW, &oldtty); - fcntl(0, F_SETFL, old_fd0_flags); -} - -static void term_init(void) -{ - struct termios tty; - - tcgetattr (0, &tty); - oldtty = tty; - old_fd0_flags = fcntl(0, F_GETFL); - - tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP - |INLCR|IGNCR|ICRNL|IXON); - tty.c_oflag |= OPOST; - tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); - /* if graphical mode, we allow Ctrl-C handling */ - if (display_type == DT_NOGRAPHIC) - tty.c_lflag &= ~ISIG; - tty.c_cflag &= ~(CSIZE|PARENB); - tty.c_cflag |= CS8; - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; - - tcsetattr (0, TCSANOW, &tty); - - if (!term_atexit_done++) - atexit(term_exit); - - fcntl(0, F_SETFL, O_NONBLOCK); -} - -static void qemu_chr_close_stdio(struct CharDriverState *chr) -{ - term_exit(); - stdio_nb_clients--; - qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); - fd_chr_close(chr); -} - -static CharDriverState *qemu_chr_open_stdio(void) -{ - CharDriverState *chr; - - if (stdio_nb_clients >= STDIO_MAX_CLIENTS) - return NULL; - chr = qemu_chr_open_fd(0, 1); - chr->chr_close = qemu_chr_close_stdio; - qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr); - stdio_nb_clients++; - term_init(); - - return chr; -} - -#ifdef __sun__ -/* Once Solaris has openpty(), this is going to be removed. */ -static int openpty(int *amaster, int *aslave, char *name, - struct termios *termp, struct winsize *winp) -{ - const char *slave; - int mfd = -1, sfd = -1; - - *amaster = *aslave = -1; - - mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY); - if (mfd < 0) - goto err; - - if (grantpt(mfd) == -1 || unlockpt(mfd) == -1) - goto err; - - if ((slave = ptsname(mfd)) == NULL) - goto err; - - if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1) - goto err; - - if (ioctl(sfd, I_PUSH, "ptem") == -1 || - (termp != NULL && tcgetattr(sfd, termp) < 0)) - goto err; - - if (amaster) - *amaster = mfd; - if (aslave) - *aslave = sfd; - if (winp) - ioctl(sfd, TIOCSWINSZ, winp); - - return 0; - -err: - if (sfd != -1) - close(sfd); - close(mfd); - return -1; -} - -static void cfmakeraw (struct termios *termios_p) -{ - termios_p->c_iflag &= - ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); - termios_p->c_oflag &= ~OPOST; - termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); - termios_p->c_cflag &= ~(CSIZE|PARENB); - termios_p->c_cflag |= CS8; - - termios_p->c_cc[VMIN] = 0; - termios_p->c_cc[VTIME] = 0; -} -#endif - -#ifndef _WIN32 - -typedef struct { - int fd; - int connected; - int polling; - int read_bytes; - QEMUTimer *timer; -} PtyCharDriver; - -static void pty_chr_update_read_handler(CharDriverState *chr); -static void pty_chr_state(CharDriverState *chr, int connected); - -static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) -{ - PtyCharDriver *s = chr->opaque; - - if (!s->connected) { - /* guest sends data, check for (re-)connect */ - pty_chr_update_read_handler(chr); - return 0; - } - return send_all(s->fd, buf, len); -} - -static int pty_chr_read_poll(void *opaque) -{ - CharDriverState *chr = opaque; - PtyCharDriver *s = chr->opaque; - - s->read_bytes = qemu_chr_can_read(chr); - return s->read_bytes; -} - -static void pty_chr_read(void *opaque) -{ - CharDriverState *chr = opaque; - PtyCharDriver *s = chr->opaque; - int size, len; - uint8_t buf[READ_BUF_LEN]; - - len = sizeof(buf); - if (len > s->read_bytes) - len = s->read_bytes; - if (len == 0) - return; - size = read(s->fd, buf, len); - if ((size == -1 && errno == EIO) || - (size == 0)) { - pty_chr_state(chr, 0); - return; - } - if (size > 0) { - pty_chr_state(chr, 1); - qemu_chr_read(chr, buf, size); - } -} - -static void pty_chr_update_read_handler(CharDriverState *chr) -{ - PtyCharDriver *s = chr->opaque; - - qemu_set_fd_handler2(s->fd, pty_chr_read_poll, - pty_chr_read, NULL, chr); - s->polling = 1; - /* - * Short timeout here: just need wait long enougth that qemu makes - * it through the poll loop once. When reconnected we want a - * short timeout so we notice it almost instantly. Otherwise - * read() gives us -EIO instantly, making pty_chr_state() reset the - * timeout to the normal (much longer) poll interval before the - * timer triggers. - */ - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10); -} - -static void pty_chr_state(CharDriverState *chr, int connected) -{ - PtyCharDriver *s = chr->opaque; - - if (!connected) { - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); - s->connected = 0; - s->polling = 0; - /* (re-)connect poll interval for idle guests: once per second. - * We check more frequently in case the guests sends data to - * the virtual device linked to our pty. */ - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000); - } else { - if (!s->connected) - qemu_chr_generic_open(chr); - s->connected = 1; - } -} - -static void pty_chr_timer(void *opaque) -{ - struct CharDriverState *chr = opaque; - PtyCharDriver *s = chr->opaque; - - if (s->connected) - return; - if (s->polling) { - /* If we arrive here without polling being cleared due - * read returning -EIO, then we are (re-)connected */ - pty_chr_state(chr, 1); - return; - } - - /* Next poll ... */ - pty_chr_update_read_handler(chr); -} - -static void pty_chr_close(struct CharDriverState *chr) -{ - PtyCharDriver *s = chr->opaque; - - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); - close(s->fd); - qemu_del_timer(s->timer); - qemu_free_timer(s->timer); - qemu_free(s); - qemu_chr_event(chr, CHR_EVENT_CLOSED); -} - -static CharDriverState *qemu_chr_open_pty(void) -{ - CharDriverState *chr; - PtyCharDriver *s; - struct termios tty; - int slave_fd, len; -#if defined(__OpenBSD__) || defined(__DragonFly__) - char pty_name[PATH_MAX]; -#define q_ptsname(x) pty_name -#else - char *pty_name = NULL; -#define q_ptsname(x) ptsname(x) - extern char* ptsname(int); -#endif - - chr = qemu_mallocz(sizeof(CharDriverState)); - s = qemu_mallocz(sizeof(PtyCharDriver)); - - if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) { - return NULL; - } - - /* Set raw attributes on the pty. */ - tcgetattr(slave_fd, &tty); - cfmakeraw(&tty); - tcsetattr(slave_fd, TCSAFLUSH, &tty); - close(slave_fd); - - len = strlen(q_ptsname(s->fd)) + 5; - chr->filename = qemu_malloc(len); - snprintf(chr->filename, len, "pty:%s", q_ptsname(s->fd)); - fprintf(stderr, "char device redirected to %s\n", q_ptsname(s->fd)); - - chr->opaque = s; - chr->chr_write = pty_chr_write; - chr->chr_update_read_handler = pty_chr_update_read_handler; - chr->chr_close = pty_chr_close; - - s->timer = qemu_new_timer(rt_clock, pty_chr_timer, chr); - - return chr; -} - -static void tty_serial_init(int fd, int speed, - int parity, int data_bits, int stop_bits) -{ - struct termios tty; - speed_t spd; - -#if 0 - printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n", - speed, parity, data_bits, stop_bits); -#endif - tcgetattr (fd, &tty); - if (!term_atexit_done) { - oldtty = tty; - } - -#define check_speed(val) if (speed <= val) { spd = B##val; break; } - speed = speed * 10 / 11; - do { - check_speed(50); - check_speed(75); - check_speed(110); - check_speed(134); - check_speed(150); - check_speed(200); - check_speed(300); - check_speed(600); - check_speed(1200); - check_speed(1800); - check_speed(2400); - check_speed(4800); - check_speed(9600); - check_speed(19200); - check_speed(38400); - /* Non-Posix values follow. They may be unsupported on some systems. */ - check_speed(57600); - check_speed(115200); -#ifdef B230400 - check_speed(230400); -#endif -#ifdef B460800 - check_speed(460800); -#endif -#ifdef B500000 - check_speed(500000); -#endif -#ifdef B576000 - check_speed(576000); -#endif -#ifdef B921600 - check_speed(921600); -#endif -#ifdef B1000000 - check_speed(1000000); -#endif -#ifdef B1152000 - check_speed(1152000); -#endif -#ifdef B1500000 - check_speed(1500000); -#endif -#ifdef B2000000 - check_speed(2000000); -#endif -#ifdef B2500000 - check_speed(2500000); -#endif -#ifdef B3000000 - check_speed(3000000); -#endif -#ifdef B3500000 - check_speed(3500000); -#endif -#ifdef B4000000 - check_speed(4000000); -#endif - spd = B115200; - } while (0); - - cfsetispeed(&tty, spd); - cfsetospeed(&tty, spd); - - tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP - |INLCR|IGNCR|ICRNL|IXON); - tty.c_oflag |= OPOST; - tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG); - tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB); - switch(data_bits) { - default: - case 8: - tty.c_cflag |= CS8; - break; - case 7: - tty.c_cflag |= CS7; - break; - case 6: - tty.c_cflag |= CS6; - break; - case 5: - tty.c_cflag |= CS5; - break; - } - switch(parity) { - default: - case 'N': - break; - case 'E': - tty.c_cflag |= PARENB; - break; - case 'O': - tty.c_cflag |= PARENB | PARODD; - break; - } - if (stop_bits == 2) - tty.c_cflag |= CSTOPB; - - tcsetattr (fd, TCSANOW, &tty); -} - -static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) -{ - FDCharDriver *s = chr->opaque; - - switch(cmd) { - case CHR_IOCTL_SERIAL_SET_PARAMS: - { - QEMUSerialSetParams *ssp = arg; - tty_serial_init(s->fd_in, ssp->speed, ssp->parity, - ssp->data_bits, ssp->stop_bits); - } - break; - case CHR_IOCTL_SERIAL_SET_BREAK: - { - int enable = *(int *)arg; - if (enable) - tcsendbreak(s->fd_in, 1); - } - break; - case CHR_IOCTL_SERIAL_GET_TIOCM: - { - int sarg = 0; - int *targ = (int *)arg; - ioctl(s->fd_in, TIOCMGET, &sarg); - *targ = 0; - if (sarg & TIOCM_CTS) - *targ |= CHR_TIOCM_CTS; - if (sarg & TIOCM_CAR) - *targ |= CHR_TIOCM_CAR; - if (sarg & TIOCM_DSR) - *targ |= CHR_TIOCM_DSR; - if (sarg & TIOCM_RI) - *targ |= CHR_TIOCM_RI; - if (sarg & TIOCM_DTR) - *targ |= CHR_TIOCM_DTR; - if (sarg & TIOCM_RTS) - *targ |= CHR_TIOCM_RTS; - } - break; - case CHR_IOCTL_SERIAL_SET_TIOCM: - { - int sarg = *(int *)arg; - int targ = 0; - ioctl(s->fd_in, TIOCMGET, &targ); - targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR - | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS); - if (sarg & CHR_TIOCM_CTS) - targ |= TIOCM_CTS; - if (sarg & CHR_TIOCM_CAR) - targ |= TIOCM_CAR; - if (sarg & CHR_TIOCM_DSR) - targ |= TIOCM_DSR; - if (sarg & CHR_TIOCM_RI) - targ |= TIOCM_RI; - if (sarg & CHR_TIOCM_DTR) - targ |= TIOCM_DTR; - if (sarg & CHR_TIOCM_RTS) - targ |= TIOCM_RTS; - ioctl(s->fd_in, TIOCMSET, &targ); - } - break; - default: - return -ENOTSUP; - } - return 0; -} - -static CharDriverState *qemu_chr_open_tty(const char *filename) -{ - CharDriverState *chr; - int fd; - - TFR(fd = open(filename, O_RDWR | O_NONBLOCK)); - tty_serial_init(fd, 115200, 'N', 8, 1); - chr = qemu_chr_open_fd(fd, fd); - if (!chr) { - close(fd); - return NULL; - } - chr->chr_ioctl = tty_serial_ioctl; - qemu_chr_reset(chr); - return chr; -} -#else /* _WIN32 */ -static CharDriverState *qemu_chr_open_pty(void) -{ - return NULL; -} -#endif /* _WIN32 */ - -#if defined(__linux__) -typedef struct { - int fd; - int mode; -} ParallelCharDriver; - -static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode) -{ - if (s->mode != mode) { - int m = mode; - if (ioctl(s->fd, PPSETMODE, &m) < 0) - return 0; - s->mode = mode; - } - return 1; -} - -static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) -{ - ParallelCharDriver *drv = chr->opaque; - int fd = drv->fd; - uint8_t b; - - switch(cmd) { - case CHR_IOCTL_PP_READ_DATA: - if (ioctl(fd, PPRDATA, &b) < 0) - return -ENOTSUP; - *(uint8_t *)arg = b; - break; - case CHR_IOCTL_PP_WRITE_DATA: - b = *(uint8_t *)arg; - if (ioctl(fd, PPWDATA, &b) < 0) - return -ENOTSUP; - break; - case CHR_IOCTL_PP_READ_CONTROL: - if (ioctl(fd, PPRCONTROL, &b) < 0) - return -ENOTSUP; - /* Linux gives only the lowest bits, and no way to know data - direction! For better compatibility set the fixed upper - bits. */ - *(uint8_t *)arg = b | 0xc0; - break; - case CHR_IOCTL_PP_WRITE_CONTROL: - b = *(uint8_t *)arg; - if (ioctl(fd, PPWCONTROL, &b) < 0) - return -ENOTSUP; - break; - case CHR_IOCTL_PP_READ_STATUS: - if (ioctl(fd, PPRSTATUS, &b) < 0) - return -ENOTSUP; - *(uint8_t *)arg = b; - break; - case CHR_IOCTL_PP_DATA_DIR: - if (ioctl(fd, PPDATADIR, (int *)arg) < 0) - return -ENOTSUP; - break; - case CHR_IOCTL_PP_EPP_READ_ADDR: - if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) { - struct ParallelIOArg *parg = arg; - int n = read(fd, parg->buffer, parg->count); - if (n != parg->count) { - return -EIO; - } - } - break; - case CHR_IOCTL_PP_EPP_READ: - if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) { - struct ParallelIOArg *parg = arg; - int n = read(fd, parg->buffer, parg->count); - if (n != parg->count) { - return -EIO; - } - } - break; - case CHR_IOCTL_PP_EPP_WRITE_ADDR: - if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) { - struct ParallelIOArg *parg = arg; - int n = write(fd, parg->buffer, parg->count); - if (n != parg->count) { - return -EIO; - } - } - break; - case CHR_IOCTL_PP_EPP_WRITE: - if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) { - struct ParallelIOArg *parg = arg; - int n = write(fd, parg->buffer, parg->count); - if (n != parg->count) { - return -EIO; - } - } - break; - default: - return -ENOTSUP; - } - return 0; -} - -static void pp_close(CharDriverState *chr) -{ - ParallelCharDriver *drv = chr->opaque; - int fd = drv->fd; - - pp_hw_mode(drv, IEEE1284_MODE_COMPAT); - ioctl(fd, PPRELEASE); - close(fd); - qemu_free(drv); -} - -static CharDriverState *qemu_chr_open_pp(const char *filename) -{ - CharDriverState *chr; - ParallelCharDriver *drv; - int fd; - - TFR(fd = open(filename, O_RDWR)); - if (fd < 0) - return NULL; - - if (ioctl(fd, PPCLAIM) < 0) { - close(fd); - return NULL; - } - - drv = qemu_mallocz(sizeof(ParallelCharDriver)); - drv->fd = fd; - drv->mode = IEEE1284_MODE_COMPAT; - - chr = qemu_mallocz(sizeof(CharDriverState)); - chr->chr_write = null_chr_write; - chr->chr_ioctl = pp_ioctl; - chr->chr_close = pp_close; - chr->opaque = drv; - - qemu_chr_reset(chr); - - return chr; -} -#endif /* __linux__ */ - -#if defined(__FreeBSD__) || defined(__DragonFly__) -static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) -{ - int fd = (int)chr->opaque; - uint8_t b; - - switch(cmd) { - case CHR_IOCTL_PP_READ_DATA: - if (ioctl(fd, PPIGDATA, &b) < 0) - return -ENOTSUP; - *(uint8_t *)arg = b; - break; - case CHR_IOCTL_PP_WRITE_DATA: - b = *(uint8_t *)arg; - if (ioctl(fd, PPISDATA, &b) < 0) - return -ENOTSUP; - break; - case CHR_IOCTL_PP_READ_CONTROL: - if (ioctl(fd, PPIGCTRL, &b) < 0) - return -ENOTSUP; - *(uint8_t *)arg = b; - break; - case CHR_IOCTL_PP_WRITE_CONTROL: - b = *(uint8_t *)arg; - if (ioctl(fd, PPISCTRL, &b) < 0) - return -ENOTSUP; - break; - case CHR_IOCTL_PP_READ_STATUS: - if (ioctl(fd, PPIGSTATUS, &b) < 0) - return -ENOTSUP; - *(uint8_t *)arg = b; - break; - default: - return -ENOTSUP; - } - return 0; -} - -static CharDriverState *qemu_chr_open_pp(const char *filename) -{ - CharDriverState *chr; - int fd; - - fd = open(filename, O_RDWR); - if (fd < 0) - return NULL; - - chr = qemu_mallocz(sizeof(CharDriverState)); - chr->opaque = (void *)fd; - chr->chr_write = null_chr_write; - chr->chr_ioctl = pp_ioctl; - return chr; -} -#endif - -#else /* _WIN32 */ - -typedef struct { - int max_size; - HANDLE hcom, hrecv, hsend; - OVERLAPPED orecv, osend; - BOOL fpipe; - DWORD len; -} WinCharState; - -#define NSENDBUF 2048 -#define NRECVBUF 2048 -#define MAXCONNECT 1 -#define NTIMEOUT 5000 - -static int win_chr_poll(void *opaque); -static int win_chr_pipe_poll(void *opaque); - -static void win_chr_close(CharDriverState *chr) -{ - WinCharState *s = chr->opaque; - - if (s->hsend) { - CloseHandle(s->hsend); - s->hsend = NULL; - } - if (s->hrecv) { - CloseHandle(s->hrecv); - s->hrecv = NULL; - } - if (s->hcom) { - CloseHandle(s->hcom); - s->hcom = NULL; - } - if (s->fpipe) - qemu_del_polling_cb(win_chr_pipe_poll, chr); - else - qemu_del_polling_cb(win_chr_poll, chr); -} - -static int win_chr_init(CharDriverState *chr, const char *filename) -{ - WinCharState *s = chr->opaque; - COMMCONFIG comcfg; - COMMTIMEOUTS cto = { 0, 0, 0, 0, 0}; - COMSTAT comstat; - DWORD size; - DWORD err; - - s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL); - if (!s->hsend) { - fprintf(stderr, "Failed CreateEvent\n"); - goto fail; - } - s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL); - if (!s->hrecv) { - fprintf(stderr, "Failed CreateEvent\n"); - goto fail; - } - - s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - if (s->hcom == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError()); - s->hcom = NULL; - goto fail; - } - - if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) { - fprintf(stderr, "Failed SetupComm\n"); - goto fail; - } - - ZeroMemory(&comcfg, sizeof(COMMCONFIG)); - size = sizeof(COMMCONFIG); - GetDefaultCommConfig(filename, &comcfg, &size); - comcfg.dcb.DCBlength = sizeof(DCB); - CommConfigDialog(filename, NULL, &comcfg); - - if (!SetCommState(s->hcom, &comcfg.dcb)) { - fprintf(stderr, "Failed SetCommState\n"); - goto fail; - } - - if (!SetCommMask(s->hcom, EV_ERR)) { - fprintf(stderr, "Failed SetCommMask\n"); - goto fail; - } - - cto.ReadIntervalTimeout = MAXDWORD; - if (!SetCommTimeouts(s->hcom, &cto)) { - fprintf(stderr, "Failed SetCommTimeouts\n"); - goto fail; - } - - if (!ClearCommError(s->hcom, &err, &comstat)) { - fprintf(stderr, "Failed ClearCommError\n"); - goto fail; - } - qemu_add_polling_cb(win_chr_poll, chr); - return 0; - - fail: - win_chr_close(chr); - return -1; -} - -static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1) -{ - WinCharState *s = chr->opaque; - DWORD len, ret, size, err; - - len = len1; - ZeroMemory(&s->osend, sizeof(s->osend)); - s->osend.hEvent = s->hsend; - while (len > 0) { - if (s->hsend) - ret = WriteFile(s->hcom, buf, len, &size, &s->osend); - else - ret = WriteFile(s->hcom, buf, len, &size, NULL); - if (!ret) { - err = GetLastError(); - if (err == ERROR_IO_PENDING) { - ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE); - if (ret) { - buf += size; - len -= size; - } else { - break; - } - } else { - break; - } - } else { - buf += size; - len -= size; - } - } - return len1 - len; -} - -static int win_chr_read_poll(CharDriverState *chr) -{ - WinCharState *s = chr->opaque; - - s->max_size = qemu_chr_can_read(chr); - return s->max_size; -} - -static void win_chr_readfile(CharDriverState *chr) -{ - WinCharState *s = chr->opaque; - int ret, err; - uint8_t buf[1024]; - DWORD size; - - ZeroMemory(&s->orecv, sizeof(s->orecv)); - s->orecv.hEvent = s->hrecv; - ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv); - if (!ret) { - err = GetLastError(); - if (err == ERROR_IO_PENDING) { - ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE); - } - } - - if (size > 0) { - qemu_chr_read(chr, buf, size); - } -} - -static void win_chr_read(CharDriverState *chr) -{ - WinCharState *s = chr->opaque; - - if (s->len > s->max_size) - s->len = s->max_size; - if (s->len == 0) - return; - - win_chr_readfile(chr); -} - -static int win_chr_poll(void *opaque) -{ - CharDriverState *chr = opaque; - WinCharState *s = chr->opaque; - COMSTAT status; - DWORD comerr; - - ClearCommError(s->hcom, &comerr, &status); - if (status.cbInQue > 0) { - s->len = status.cbInQue; - win_chr_read_poll(chr); - win_chr_read(chr); - return 1; - } - return 0; -} - -static CharDriverState *qemu_chr_open_win(const char *filename) -{ - CharDriverState *chr; - WinCharState *s; - - chr = qemu_mallocz(sizeof(CharDriverState)); - s = qemu_mallocz(sizeof(WinCharState)); - chr->opaque = s; - chr->chr_write = win_chr_write; - chr->chr_close = win_chr_close; - - if (win_chr_init(chr, filename) < 0) { - free(s); - free(chr); - return NULL; - } - qemu_chr_reset(chr); - return chr; -} - -static int win_chr_pipe_poll(void *opaque) -{ - CharDriverState *chr = opaque; - WinCharState *s = chr->opaque; - DWORD size; - - PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL); - if (size > 0) { - s->len = size; - win_chr_read_poll(chr); - win_chr_read(chr); - return 1; - } - return 0; -} - -static int win_chr_pipe_init(CharDriverState *chr, const char *filename) -{ - WinCharState *s = chr->opaque; - OVERLAPPED ov; - int ret; - DWORD size; - char openname[256]; - - s->fpipe = TRUE; - - s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL); - if (!s->hsend) { - fprintf(stderr, "Failed CreateEvent\n"); - goto fail; - } - s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL); - if (!s->hrecv) { - fprintf(stderr, "Failed CreateEvent\n"); - goto fail; - } - - snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename); - s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | - PIPE_WAIT, - MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL); - if (s->hcom == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError()); - s->hcom = NULL; - goto fail; - } - - ZeroMemory(&ov, sizeof(ov)); - ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - ret = ConnectNamedPipe(s->hcom, &ov); - if (ret) { - fprintf(stderr, "Failed ConnectNamedPipe\n"); - goto fail; - } - - ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE); - if (!ret) { - fprintf(stderr, "Failed GetOverlappedResult\n"); - if (ov.hEvent) { - CloseHandle(ov.hEvent); - ov.hEvent = NULL; - } - goto fail; - } - - if (ov.hEvent) { - CloseHandle(ov.hEvent); - ov.hEvent = NULL; - } - qemu_add_polling_cb(win_chr_pipe_poll, chr); - return 0; - - fail: - win_chr_close(chr); - return -1; -} - - -static CharDriverState *qemu_chr_open_win_pipe(const char *filename) -{ - CharDriverState *chr; - WinCharState *s; - - chr = qemu_mallocz(sizeof(CharDriverState)); - s = qemu_mallocz(sizeof(WinCharState)); - chr->opaque = s; - chr->chr_write = win_chr_write; - chr->chr_close = win_chr_close; - - if (win_chr_pipe_init(chr, filename) < 0) { - free(s); - free(chr); - return NULL; - } - qemu_chr_reset(chr); - return chr; -} - -static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) -{ - CharDriverState *chr; - WinCharState *s; - - chr = qemu_mallocz(sizeof(CharDriverState)); - s = qemu_mallocz(sizeof(WinCharState)); - s->hcom = fd_out; - chr->opaque = s; - chr->chr_write = win_chr_write; - qemu_chr_reset(chr); - return chr; -} - -static CharDriverState *qemu_chr_open_win_con(const char *filename) -{ - return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); -} - -static CharDriverState *qemu_chr_open_win_file_out(const char *file_out) -{ - HANDLE fd_out; - - fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (fd_out == INVALID_HANDLE_VALUE) - return NULL; - - return qemu_chr_open_win_file(fd_out); -} -#endif /* !_WIN32 */ - -/***********************************************************/ -/* UDP Net console */ - -typedef struct { - int fd; - SockAddress daddr; - uint8_t buf[1024]; - int bufcnt; - int bufptr; - int max_size; -} NetCharDriver; - -static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) -{ - NetCharDriver *s = chr->opaque; - - return socket_sendto(s->fd, (const void *)buf, len, &s->daddr); -} - -static int udp_chr_read_poll(void *opaque) -{ - CharDriverState *chr = opaque; - NetCharDriver *s = chr->opaque; - - s->max_size = qemu_chr_can_read(chr); - - /* If there were any stray characters in the queue process them - * first - */ - while (s->max_size > 0 && s->bufptr < s->bufcnt) { - qemu_chr_read(chr, &s->buf[s->bufptr], 1); - s->bufptr++; - s->max_size = qemu_chr_can_read(chr); - } - return s->max_size; -} - -static void udp_chr_read(void *opaque) -{ - CharDriverState *chr = opaque; - NetCharDriver *s = chr->opaque; - - if (s->max_size == 0) - return; - s->bufcnt = socket_recv(s->fd, (void *)s->buf, sizeof(s->buf)); - s->bufptr = s->bufcnt; - if (s->bufcnt <= 0) - return; - - s->bufptr = 0; - while (s->max_size > 0 && s->bufptr < s->bufcnt) { - qemu_chr_read(chr, &s->buf[s->bufptr], 1); - s->bufptr++; - s->max_size = qemu_chr_can_read(chr); - } -} - -static void udp_chr_update_read_handler(CharDriverState *chr) -{ - NetCharDriver *s = chr->opaque; - - if (s->fd >= 0) { - qemu_set_fd_handler2(s->fd, udp_chr_read_poll, - udp_chr_read, NULL, chr); - } -} - -static void udp_chr_close(CharDriverState *chr) -{ - NetCharDriver *s = chr->opaque; - if (s->fd >= 0) { - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); - socket_close(s->fd); - } - qemu_free(s); -} - -static CharDriverState *qemu_chr_open_udp(const char *def) -{ - CharDriverState *chr = NULL; - NetCharDriver *s = NULL; - int fd = -1; - SockAddress saddr; - - chr = qemu_mallocz(sizeof(CharDriverState)); - s = qemu_mallocz(sizeof(NetCharDriver)); - - fd = socket_create_inet(SOCKET_DGRAM); - if (fd < 0) { - perror("socket(PF_INET, SOCK_DGRAM)"); - goto return_err; - } - - if (parse_host_src_port(&s->daddr, &saddr, def) < 0) { - printf("Could not parse: %s\n", def); - goto return_err; - } - - if (socket_bind(fd, &saddr) < 0) { - perror("bind"); - goto return_err; - } - - s->fd = fd; - s->bufcnt = 0; - s->bufptr = 0; - chr->opaque = s; - chr->chr_write = udp_chr_write; - chr->chr_update_read_handler = udp_chr_update_read_handler; - chr->chr_close = udp_chr_close; - return chr; - -return_err: - if (chr) - free(chr); - if (s) - free(s); - if (fd >= 0) - closesocket(fd); - return NULL; -} - -/***********************************************************/ -/* TCP Net console */ - -typedef struct { - int fd, listen_fd; - int connected; - int max_size; - int do_telnetopt; - int do_nodelay; - int is_unix; - int msgfd; -} TCPCharDriver; - -static void tcp_chr_accept(void *opaque); - -static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) -{ - TCPCharDriver *s = chr->opaque; - if (s->connected) { - return send_all(s->fd, buf, len); - } else { - /* XXX: indicate an error ? */ - return len; - } -} - -static int tcp_chr_read_poll(void *opaque) -{ - CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; - if (!s->connected) - return 0; - s->max_size = qemu_chr_can_read(chr); - return s->max_size; -} - -#define IAC 255 -#define IAC_BREAK 243 -static void tcp_chr_process_IAC_bytes(CharDriverState *chr, - TCPCharDriver *s, - uint8_t *buf, int *size) -{ - /* Handle any telnet client's basic IAC options to satisfy char by - * char mode with no echo. All IAC options will be removed from - * the buf and the do_telnetopt variable will be used to track the - * state of the width of the IAC information. - * - * IAC commands come in sets of 3 bytes with the exception of the - * "IAC BREAK" command and the double IAC. - */ - - int i; - int j = 0; - - for (i = 0; i < *size; i++) { - if (s->do_telnetopt > 1) { - if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) { - /* Double IAC means send an IAC */ - if (j != i) - buf[j] = buf[i]; - j++; - s->do_telnetopt = 1; - } else { - if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) { - /* Handle IAC break commands by sending a serial break */ - qemu_chr_event(chr, CHR_EVENT_BREAK); - s->do_telnetopt++; - } - s->do_telnetopt++; - } - if (s->do_telnetopt >= 4) { - s->do_telnetopt = 1; - } - } else { - if ((unsigned char)buf[i] == IAC) { - s->do_telnetopt = 2; - } else { - if (j != i) - buf[j] = buf[i]; - j++; - } - } - } - *size = j; -} - -#if 0 -static int tcp_get_msgfd(CharDriverState *chr) -{ - TCPCharDriver *s = chr->opaque; - int fd = s->msgfd; - s->msgfd = -1; - return fd; -} -#endif - -#ifndef _WIN32 -static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg) -{ - TCPCharDriver *s = chr->opaque; - struct cmsghdr *cmsg; - - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { - int fd; - - if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) - continue; - - fd = *((int *)CMSG_DATA(cmsg)); - if (fd < 0) - continue; - - if (s->msgfd != -1) - close(s->msgfd); - s->msgfd = fd; - } -} - -static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) -{ - TCPCharDriver *s = chr->opaque; - struct msghdr msg = { NULL, }; - struct iovec iov[1]; - union { - struct cmsghdr cmsg; - char control[CMSG_SPACE(sizeof(int))]; - } msg_control; - ssize_t ret; - - iov[0].iov_base = buf; - iov[0].iov_len = len; - - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = &msg_control; - msg.msg_controllen = sizeof(msg_control); - - ret = recvmsg(s->fd, &msg, 0); - if (ret > 0 && s->is_unix) - unix_process_msgfd(chr, &msg); - - return ret; -} -#else -static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) -{ - TCPCharDriver *s = chr->opaque; - return recv(s->fd, buf, len, 0); -} -#endif - -static void tcp_chr_read(void *opaque) -{ - CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; - uint8_t buf[READ_BUF_LEN]; - int len, size; - - if (!s->connected || s->max_size <= 0) - return; - len = sizeof(buf); - if (len > s->max_size) - len = s->max_size; - size = tcp_chr_recv(chr, (void *)buf, len); - if (size == 0) { - /* connection closed */ - s->connected = 0; - if (s->listen_fd >= 0) { - qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); - } - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); - socket_close(s->fd); - s->fd = -1; - qemu_chr_event(chr, CHR_EVENT_CLOSED); - } else if (size > 0) { - if (s->do_telnetopt) - tcp_chr_process_IAC_bytes(chr, s, buf, &size); - if (size > 0) - qemu_chr_read(chr, buf, size); - } -} - -#ifndef _WIN32 -CharDriverState *qemu_chr_open_eventfd(int eventfd) -{ - return qemu_chr_open_fd(eventfd, eventfd); -} -#endif - -static void tcp_chr_connect(void *opaque) -{ - CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; - - s->connected = 1; - qemu_set_fd_handler2(s->fd, tcp_chr_read_poll, - tcp_chr_read, NULL, chr); - qemu_chr_generic_open(chr); -} - -#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c; -static void tcp_chr_telnet_init(int fd) -{ - char buf[3]; - /* Send the telnet negotion to put telnet in binary, no echo, single char mode */ - IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */ - socket_send(fd, (char *)buf, 3); - IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */ - socket_send(fd, (char *)buf, 3); - IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */ - socket_send(fd, (char *)buf, 3); - IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */ - socket_send(fd, (char *)buf, 3); -} - -static void tcp_chr_accept(void *opaque) -{ - CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; - int fd; - - for(;;) { - fd = socket_accept(s->listen_fd, NULL); - if (fd < 0) { - return; - } else if (fd >= 0) { - if (s->do_telnetopt) - tcp_chr_telnet_init(fd); - break; - } - } - socket_set_nonblock(fd); - if (s->do_nodelay) - socket_set_nodelay(fd); - s->fd = fd; - qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); - tcp_chr_connect(chr); -} - -static void tcp_chr_close(CharDriverState *chr) -{ - TCPCharDriver *s = chr->opaque; - if (s->fd >= 0) { - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); - closesocket(s->fd); - } - if (s->listen_fd >= 0) { - qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); - closesocket(s->listen_fd); - } - qemu_free(s); -} - -static CharDriverState *qemu_chr_open_tcp(const char *host_str, - int is_telnet, - int is_unix) -{ - CharDriverState *chr = NULL; - TCPCharDriver *s = NULL; - int fd = -1, offset = 0; - int is_listen = 0; - int is_waitconnect = 1; - int do_nodelay = 0; - const char *ptr; - - ptr = host_str; - while((ptr = strchr(ptr,','))) { - ptr++; - if (!strncmp(ptr,"server",6)) { - is_listen = 1; - } else if (!strncmp(ptr,"nowait",6)) { - is_waitconnect = 0; - } else if (!strncmp(ptr,"nodelay",6)) { - do_nodelay = 1; - } else if (!strncmp(ptr,"to=",3)) { - /* nothing, inet_listen() parses this one */; - } else if (!strncmp(ptr,"ipv4",4)) { - /* nothing, inet_connect() and inet_listen() parse this one */; - } else if (!strncmp(ptr,"ipv6",4)) { - /* nothing, inet_connect() and inet_listen() parse this one */; - } else { - printf("Unknown option: %s\n", ptr); - goto fail; - } - } - if (!is_listen) - is_waitconnect = 0; - - chr = qemu_mallocz(sizeof(CharDriverState)); - s = qemu_mallocz(sizeof(TCPCharDriver)); - - if (is_listen) { - chr->filename = qemu_malloc(256); - if (is_unix) { - pstrcpy(chr->filename, 256, "unix:"); - } else if (is_telnet) { - pstrcpy(chr->filename, 256, "telnet:"); - } else { - pstrcpy(chr->filename, 256, "tcp:"); - } - offset = strlen(chr->filename); - } - if (is_unix) { - if (is_listen) { - fd = unix_listen(host_str, chr->filename + offset, 256 - offset); - } else { - fd = unix_connect(host_str); - } - } else { -#ifdef CONFIG_ANDROID - if (!strncmp(host_str,"socket=",7)) { - char *end; - long val = strtol(host_str+7, &end, 10); - if (val <= 0 || end == host_str+7) { - printf("Invalid socket number: '%s'\n", host_str+7); - goto fail; - } - fd = (int) val; - } else -#endif - if (is_listen) { - fd = inet_listen(host_str, chr->filename + offset, 256 - offset, - SOCKET_STREAM, 0); - } else { - fd = inet_connect(host_str, SOCKET_STREAM); - } - } - if (fd < 0) - goto fail; - - if (!is_waitconnect) - socket_set_nonblock(fd); - - s->connected = 0; - s->fd = -1; - s->listen_fd = -1; - s->is_unix = is_unix; - s->do_nodelay = do_nodelay && !is_unix; - - chr->opaque = s; - chr->chr_write = tcp_chr_write; - chr->chr_close = tcp_chr_close; - - if (is_listen) { - s->listen_fd = fd; - qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); - if (is_telnet) - s->do_telnetopt = 1; - } else { - s->connected = 1; - s->fd = fd; - socket_set_nodelay(fd); - tcp_chr_connect(chr); - } - - if (is_listen && is_waitconnect) { - printf("QEMU waiting for connection on: %s\n", - chr->filename ? chr->filename : host_str); - tcp_chr_accept(chr); - socket_set_nonblock(s->listen_fd); - } - - return chr; - fail: - if (fd >= 0) - closesocket(fd); - qemu_free(s); - qemu_free(chr); - return NULL; -} - -CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s)) -{ - const char *p; - CharDriverState *chr; - - if (!strcmp(filename, "vc")) { - chr = text_console_init_compat(label, NULL); - } else - if (strstart(filename, "vc:", &p)) { - chr = text_console_init_compat(label, p); - } else - if (!strcmp(filename, "null")) { - chr = qemu_chr_open_null(); - } else - if (strstart(filename, "tcp:", &p)) { - chr = qemu_chr_open_tcp(p, 0, 0); - } else - if (strstart(filename, "telnet:", &p)) { - chr = qemu_chr_open_tcp(p, 1, 0); - } else - if (strstart(filename, "udp:", &p)) { - chr = qemu_chr_open_udp(p); - } else - if (strstart(filename, "mon:", &p)) { - chr = qemu_chr_open(label, p, NULL); - if (chr) { - chr = qemu_chr_open_mux(chr); - monitor_init(chr, MONITOR_USE_READLINE); - } else { - printf("Unable to open driver: %s\n", p); - } - } else if (!strcmp(filename, "msmouse")) { - chr = qemu_chr_open_msmouse(); - } else -#ifndef _WIN32 - if (strstart(filename, "unix:", &p)) { - chr = qemu_chr_open_tcp(p, 0, 1); - } else if (strstart(filename, "file:", &p)) { - chr = qemu_chr_open_file_out(p); - } else if (strstart(filename, "pipe:", &p)) { - chr = qemu_chr_open_pipe(p); - } else if (strstart(filename, "fdpair:", &p)) { - chr = qemu_chr_open_fdpair(p); - } else if (!strcmp(filename, "pty")) { - chr = qemu_chr_open_pty(); - } else if (!strcmp(filename, "stdio")) { - chr = qemu_chr_open_stdio(); - } else -#if defined(__linux__) - if (strstart(filename, "/dev/parport", NULL)) { - chr = qemu_chr_open_pp(filename); - } else -#elif defined(__FreeBSD__) || defined(__DragonFly__) - if (strstart(filename, "/dev/ppi", NULL)) { - chr = qemu_chr_open_pp(filename); - } else -#endif - if (strstart(filename, "/dev/", NULL)) { - chr = qemu_chr_open_tty(filename); - } else -#else /* !_WIN32 */ - if (strstart(filename, "COM", NULL)) { - chr = qemu_chr_open_win(filename); - } else - if (strstart(filename, "pipe:", &p)) { - chr = qemu_chr_open_win_pipe(p); - } else - if (strstart(filename, "con:", NULL)) { - chr = qemu_chr_open_win_con(filename); - } else - if (strstart(filename, "file:", &p)) { - chr = qemu_chr_open_win_file_out(p); - } else -#endif - if (!strcmp(filename, "android-modem")) { - CharDriverState* cs; - qemu_chr_open_charpipe( &cs, &android_modem_cs ); - return cs; - } else if (!strcmp(filename, "android-gps")) { - CharDriverState* cs; - qemu_chr_open_charpipe( &cs, &android_gps_cs ); - return cs; - } else if (!strcmp(filename, "android-kmsg")) { - return android_kmsg_get_cs(); - } else if (!strcmp(filename, "android-qemud")) { - return android_qemud_get_cs(); - } else -#ifdef CONFIG_BRLAPI - if (!strcmp(filename, "braille")) { - chr = chr_baum_init(); - } else -#endif - { - chr = NULL; - } - - if (chr) { - if (!chr->filename) - chr->filename = qemu_strdup(filename); - chr->init = init; - chr->label = qemu_strdup(label); - QTAILQ_INSERT_TAIL(&chardevs, chr, next); - } - return chr; -} - -void qemu_chr_close(CharDriverState *chr) -{ - QTAILQ_REMOVE(&chardevs, chr, next); - if (chr->chr_close) - chr->chr_close(chr); - qemu_free(chr->filename); - qemu_free(chr->label); - qemu_free(chr); -} - -void qemu_chr_info(Monitor *mon) -{ - CharDriverState *chr; - - QTAILQ_FOREACH(chr, &chardevs, next) { - monitor_printf(mon, "%s: filename=%s\n", chr->label, chr->filename); - } -} - -CharDriverState *qemu_chr_find(const char *name) -{ - CharDriverState *chr; - - QTAILQ_FOREACH(chr, &chardevs, next) { - if (strcmp(chr->label, name) != 0) - continue; - return chr; - } - return NULL; -} diff --git a/qemu-char.c b/qemu-char.c index f1834a6..46c15ce 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu-common.h" +#include "sockets.h" #include "net.h" #include "monitor.h" #include "console.h" @@ -52,22 +53,19 @@ #include <sys/socket.h> #include <netinet/in.h> #include <net/if.h> -#ifdef __NetBSD__ -#include <net/if_tap.h> -#endif -#ifdef __linux__ -#include <linux/if_tun.h> -#endif #include <arpa/inet.h> #include <dirent.h> #include <netdb.h> #include <sys/select.h> #ifdef CONFIG_BSD #include <sys/stat.h> -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include <libutil.h> #include <dev/ppbus/ppi.h> #include <dev/ppbus/ppbconf.h> +#if defined(__GLIBC__) +#include <pty.h> +#endif #elif defined(__DragonFly__) #include <libutil.h> #include <dev/misc/ppi/ppi.h> @@ -75,8 +73,6 @@ #else #include <util.h> #endif -#elif defined (__GLIBC__) && defined (__FreeBSD_kernel__) -#include <freebsd/stdlib.h> #else #ifdef __linux__ #include <pty.h> @@ -106,11 +102,20 @@ #define READ_BUF_LEN 4096 +#ifdef CONFIG_ANDROID +#include "charpipe.h" +#include "modem_driver.h" +#include "android/gps.h" +#include "android/hw-kmsg.h" +#include "android/hw-qemud.h" +#endif /* CONFIG_ANDROID */ + /***********************************************************/ /* character device */ static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs = QTAILQ_HEAD_INITIALIZER(chardevs); +static int initial_reset_issued; static void qemu_chr_event(CharDriverState *s, int event) { @@ -221,6 +226,10 @@ void qemu_chr_add_handlers(CharDriverState *s, IOEventHandler *fd_event, void *opaque) { + if (!opaque && !fd_can_read && !fd_read && !fd_event) { + /* chr driver being released. */ + ++s->avail_connections; + } s->chr_can_read = fd_can_read; s->chr_read = fd_read; s->chr_event = fd_event; @@ -291,7 +300,7 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) int64_t ti; int secs; - ti = qemu_get_clock(rt_clock); + ti = qemu_get_clock_ms(rt_clock); if (d->timestamps_start == -1) d->timestamps_start = ti; ti -= d->timestamps_start; @@ -376,12 +385,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) break; } case 's': - { - int i; - for (i = 0; i < nb_drives; i++) { - bdrv_commit(drives_table[i].bdrv); - } - } + bdrv_commit_all(); break; case 'b': qemu_chr_event(chr, CHR_EVENT_BREAK); @@ -505,6 +509,9 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) chr->chr_write = mux_chr_write; chr->chr_update_read_handler = mux_chr_update_read_handler; chr->chr_accept_input = mux_chr_accept_input; + /* Frontend guest-open / -close notification is not support with muxes */ + chr->chr_guest_open = NULL; + chr->chr_guest_close = NULL; /* Muxes are always open on creation */ qemu_chr_generic_open(chr); @@ -516,6 +523,9 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) #ifdef _WIN32 int send_all(int fd, const void *buf, int len1) { +#if 1 + return socket_send(fd, buf, len1); +#else int ret, len; len = len1; @@ -523,7 +533,7 @@ int send_all(int fd, const void *buf, int len1) ret = send(fd, buf, len, 0); if (ret < 0) { errno = WSAGetLastError(); - if (errno != WSAEWOULDBLOCK) { + if (errno != WSAEWOULDBLOCK && errno != WSAEAGAIN) { return -1; } } else if (ret == 0) { @@ -534,13 +544,15 @@ int send_all(int fd, const void *buf, int len1) } } return len1 - len; +#endif } #else -static int unix_write(int fd, const uint8_t *buf, int len1) +int send_all(int fd, const void *_buf, int len1) { int ret, len; + const uint8_t *buf = _buf; len = len1; while (len > 0) { @@ -557,12 +569,31 @@ static int unix_write(int fd, const uint8_t *buf, int len1) } return len1 - len; } +#endif /* !_WIN32 */ -int send_all(int fd, const void *buf, int len1) +static CharDriverState *qemu_chr_open_android_modem(QemuOpts* opts) { - return unix_write(fd, buf, len1); + CharDriverState* cs; + qemu_chr_open_charpipe( &cs, &android_modem_cs ); + return cs; +} +static CharDriverState *qemu_chr_open_android_gps(QemuOpts* opts) +{ + CharDriverState* cs; + qemu_chr_open_charpipe( &cs, &android_gps_cs ); + return cs; +} + +static CharDriverState *qemu_chr_open_android_kmsg(QemuOpts* opts) +{ + return android_kmsg_get_cs(); +} + +static CharDriverState *qemu_chr_open_android_qemud(QemuOpts* opts) +{ + return android_qemud_get_cs(); } -#endif /* !_WIN32 */ + #ifndef _WIN32 @@ -699,6 +730,18 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) return qemu_chr_open_fd(fd_in, fd_out); } +#ifdef CONFIG_ANDROID +static CharDriverState *qemu_chr_open_fdpair(QemuOpts* opts) +{ + int fd_in = qemu_opt_get_number(opts, "fdin",-1); + int fd_out = qemu_opt_get_number(opts, "fdout",-1); + + if (fd_in < 0 || fd_out < 0) + return NULL; + + return qemu_chr_open_fd(fd_in, fd_out); +} +#endif /* CONFIG_ANDROID */ /* for STDIO, we handle the case where several clients use it (nographic mode) */ @@ -749,7 +792,7 @@ static void stdio_read(void *opaque) /* init terminal so that we can grab keys */ static struct termios oldtty; static int old_fd0_flags; -static int term_atexit_done; +static bool stdio_allow_signal; static void term_exit(void) { @@ -757,32 +800,26 @@ static void term_exit(void) fcntl(0, F_SETFL, old_fd0_flags); } -static void term_init(QemuOpts *opts) +static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) { struct termios tty; - tcgetattr (0, &tty); - oldtty = tty; - old_fd0_flags = fcntl(0, F_GETFL); - + tty = oldtty; + if (!echo) { tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP |INLCR|IGNCR|ICRNL|IXON); tty.c_oflag |= OPOST; tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); - /* if graphical mode, we allow Ctrl-C handling */ - if (!qemu_opt_get_bool(opts, "signal", display_type != DT_NOGRAPHIC)) - tty.c_lflag &= ~ISIG; tty.c_cflag &= ~(CSIZE|PARENB); tty.c_cflag |= CS8; tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 0; + } + /* if graphical mode, we allow Ctrl-C handling */ + if (!stdio_allow_signal) + tty.c_lflag &= ~ISIG; tcsetattr (0, TCSANOW, &tty); - - if (!term_atexit_done++) - atexit(term_exit); - - fcntl(0, F_SETFL, O_NONBLOCK); } static void qemu_chr_close_stdio(struct CharDriverState *chr) @@ -799,11 +836,21 @@ static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) if (stdio_nb_clients >= STDIO_MAX_CLIENTS) return NULL; + if (stdio_nb_clients == 0) { + old_fd0_flags = fcntl(0, F_GETFL); + tcgetattr (0, &oldtty); + fcntl(0, F_SETFL, O_NONBLOCK); + atexit(term_exit); + } + chr = qemu_chr_open_fd(0, 1); chr->chr_close = qemu_chr_close_stdio; + chr->chr_set_echo = qemu_chr_set_echo_stdio; qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr); stdio_nb_clients++; - term_init(opts); + stdio_allow_signal = qemu_opt_get_bool(opts, "signal", + display_type != DT_NOGRAPHIC); + qemu_chr_set_echo(chr, false); return chr; } @@ -865,9 +912,7 @@ static void cfmakeraw (struct termios *termios_p) } #endif -#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ - || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ - || defined(__GLIBC__) +#ifndef _WIN32 typedef struct { int fd; @@ -940,7 +985,7 @@ static void pty_chr_update_read_handler(CharDriverState *chr) * timeout to the normal (much longer) poll interval before the * timer triggers. */ - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 10); } static void pty_chr_state(CharDriverState *chr, int connected) @@ -954,7 +999,7 @@ static void pty_chr_state(CharDriverState *chr, int connected) /* (re-)connect poll interval for idle guests: once per second. * We check more frequently in case the guests sends data to * the virtual device linked to our pty. */ - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 1000); } else { if (!s->connected) qemu_chr_generic_open(chr); @@ -1004,6 +1049,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) #else char *pty_name = NULL; #define q_ptsname(x) ptsname(x) + //extern char* ptsname(int); #endif chr = qemu_mallocz(sizeof(CharDriverState)); @@ -1030,7 +1076,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) chr->chr_update_read_handler = pty_chr_update_read_handler; chr->chr_close = pty_chr_close; - s->timer = qemu_new_timer(rt_clock, pty_chr_timer, chr); + s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr); return chr; } @@ -1046,9 +1092,6 @@ static void tty_serial_init(int fd, int speed, speed, parity, data_bits, stop_bits); #endif tcgetattr (fd, &tty); - if (!term_atexit_done) { - oldtty = tty; - } #define check_speed(val) if (speed <= val) { spd = B##val; break; } speed = speed * 10 / 11; @@ -1220,11 +1263,6 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static void tty_exit(void) -{ - tcsetattr(0, TCSANOW, &oldtty); -} - static void qemu_chr_close_tty(CharDriverState *chr) { FDCharDriver *s = chr->opaque; @@ -1259,16 +1297,14 @@ static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) } chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty; - if (!term_atexit_done++) - atexit(tty_exit); return chr; } -#else /* ! __linux__ && ! __sun__ */ +#else /* _WIN32 */ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) { return NULL; } -#endif /* __linux__ || __sun__ */ +#endif /* _WIN32 */ #if defined(__linux__) typedef struct { @@ -1415,7 +1451,7 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) { - int fd = (int)(long)chr->opaque; + int fd = (int)(intptr_t)chr->opaque; uint8_t b; switch(cmd) { @@ -1461,7 +1497,7 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) return NULL; chr = qemu_mallocz(sizeof(CharDriverState)); - chr->opaque = (void *)(long)fd; + chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; return chr; @@ -1829,6 +1865,7 @@ static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts) typedef struct { int fd; + SockAddress daddr; uint8_t buf[READ_BUF_LEN]; int bufcnt; int bufptr; @@ -1839,7 +1876,7 @@ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { NetCharDriver *s = chr->opaque; - return send(s->fd, (const void *)buf, len, 0); + return socket_sendto(s->fd, (const void *)buf, len, &s->daddr); } static int udp_chr_read_poll(void *opaque) @@ -1867,7 +1904,7 @@ static void udp_chr_read(void *opaque) if (s->max_size == 0) return; - s->bufcnt = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0); + s->bufcnt = socket_recv(s->fd, (void *)s->buf, sizeof(s->buf)); s->bufptr = s->bufcnt; if (s->bufcnt <= 0) return; @@ -1895,10 +1932,9 @@ static void udp_chr_close(CharDriverState *chr) NetCharDriver *s = chr->opaque; if (s->fd >= 0) { qemu_set_fd_handler(s->fd, NULL, NULL, NULL); - closesocket(s->fd); + socket_close(s->fd); } qemu_free(s); - qemu_chr_event(chr, CHR_EVENT_CLOSED); } static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) @@ -1908,7 +1944,7 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) int fd = -1; chr = qemu_mallocz(sizeof(CharDriverState)); - s = qemu_mallocz(sizeof(NetCharDriver)); + s = qemu_mallocz(sizeof(NetCharDriver)); fd = inet_dgram_opts(opts); if (fd < 0) { @@ -2106,7 +2142,7 @@ static void tcp_chr_read(void *opaque) qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); } qemu_set_fd_handler(s->fd, NULL, NULL, NULL); - closesocket(s->fd); + socket_close(s->fd); s->fd = -1; qemu_chr_event(chr, CHR_EVENT_CLOSED); } else if (size > 0) { @@ -2141,46 +2177,24 @@ static void tcp_chr_telnet_init(int fd) char buf[3]; /* Send the telnet negotion to put telnet in binary, no echo, single char mode */ IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */ - send(fd, (char *)buf, 3, 0); + socket_send(fd, (char *)buf, 3); IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */ - send(fd, (char *)buf, 3, 0); + socket_send(fd, (char *)buf, 3); IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */ - send(fd, (char *)buf, 3, 0); + socket_send(fd, (char *)buf, 3); IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */ - send(fd, (char *)buf, 3, 0); -} - -static void socket_set_nodelay(int fd) -{ - int val = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); + socket_send(fd, (char *)buf, 3); } static void tcp_chr_accept(void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; - struct sockaddr_in saddr; -#ifndef _WIN32 - struct sockaddr_un uaddr; -#endif - struct sockaddr *addr; - socklen_t len; int fd; for(;;) { -#ifndef _WIN32 - if (s->is_unix) { - len = sizeof(uaddr); - addr = (struct sockaddr *)&uaddr; - } else -#endif - { - len = sizeof(saddr); - addr = (struct sockaddr *)&saddr; - } - fd = qemu_accept(s->listen_fd, addr, &len); - if (fd < 0 && errno != EINTR) { + fd = socket_accept(s->listen_fd, NULL); + if (fd < 0) { return; } else if (fd >= 0) { if (s->do_telnetopt) @@ -2309,6 +2323,70 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) return NULL; } +/***********************************************************/ +/* Memory chardev */ +typedef struct { + size_t outbuf_size; + size_t outbuf_capacity; + uint8_t *outbuf; +} MemoryDriver; + +static int mem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + MemoryDriver *d = chr->opaque; + + /* TODO: the QString implementation has the same code, we should + * introduce a generic way to do this in cutils.c */ + if (d->outbuf_capacity < d->outbuf_size + len) { + /* grow outbuf */ + d->outbuf_capacity += len; + d->outbuf_capacity *= 2; + d->outbuf = qemu_realloc(d->outbuf, d->outbuf_capacity); + } + + memcpy(d->outbuf + d->outbuf_size, buf, len); + d->outbuf_size += len; + + return len; +} + +void qemu_chr_init_mem(CharDriverState *chr) +{ + MemoryDriver *d; + + d = qemu_malloc(sizeof(*d)); + d->outbuf_size = 0; + d->outbuf_capacity = 4096; + d->outbuf = qemu_mallocz(d->outbuf_capacity); + + memset(chr, 0, sizeof(*chr)); + chr->opaque = d; + chr->chr_write = mem_chr_write; +} + +QString *qemu_chr_mem_to_qs(CharDriverState *chr) +{ + MemoryDriver *d = chr->opaque; + return qstring_from_substr((char *) d->outbuf, 0, d->outbuf_size - 1); +} + +/* NOTE: this driver can not be closed with qemu_chr_close()! */ +void qemu_chr_close_mem(CharDriverState *chr) +{ + MemoryDriver *d = chr->opaque; + + qemu_free(d->outbuf); + qemu_free(chr->opaque); + chr->opaque = NULL; + chr->chr_write = NULL; +} + +size_t qemu_chr_mem_osize(const CharDriverState *chr) +{ + const MemoryDriver *d = chr->opaque; + return d->outbuf_size; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2427,6 +2505,40 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", filename); return opts; } +#ifdef CONFIG_ANDROID + if (strstart(filename, "fdpair:", &p)) { + int fdin, fdout; + char temp[8]; + qemu_opt_set(opts, "backend", "fdpair"); + if (sscanf(p, "%d,%d", &fdin, &fdout) != 2) { + goto fail; + } + if (fdin < 0 || fdout < 0) { + goto fail; + } + snprintf(temp, sizeof temp, "%d", fdin); + qemu_opt_set(opts, "fdin", temp); + snprintf(temp, sizeof temp, "%d", fdout); + qemu_opt_set(opts, "fdout", temp); + return opts; + } + if (!strcmp(filename, "android-kmsg")) { + qemu_opt_set(opts, "backend", "android-kmsg"); + return opts; + } + if (!strcmp(filename, "android-qemud")) { + qemu_opt_set(opts, "backend", "android-qemud"); + return opts; + } + if (!strcmp(filename, "android-modem")) { + qemu_opt_set(opts, "backend", "android-modem"); + return opts; + } + if (!strcmp(filename, "android-gps")) { + qemu_opt_set(opts, "backend", "android-gps"); + return opts; + } +#endif /* CONFIG_ANDROID */ fail: qemu_opts_del(opts); @@ -2441,7 +2553,7 @@ static const struct { { .name = "socket", .open = qemu_chr_open_socket }, { .name = "udp", .open = qemu_chr_open_udp }, { .name = "msmouse", .open = qemu_chr_open_msmouse }, - { .name = "vc", .open = text_console_init_compat }, + { .name = "vc", .open = text_console_init }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, { .name = "pipe", .open = qemu_chr_open_win_pipe }, @@ -2454,8 +2566,14 @@ static const struct { { .name = "stdio", .open = qemu_chr_open_stdio }, #endif #ifdef CONFIG_ANDROID +#ifndef _WIN32 { .name = "fdpair", .open = qemu_chr_open_fdpair }, #endif + { .name = "android-qemud", .open = qemu_chr_open_android_qemud }, + { .name = "android-kmsg", .open = qemu_chr_open_android_kmsg }, + { .name = "android-modem", .open = qemu_chr_open_android_modem }, + { .name = "android-gps", .open = qemu_chr_open_android_gps }, +#endif #ifdef CONFIG_BRLAPI { .name = "braille", .open = chr_baum_init }, #endif @@ -2481,6 +2599,11 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, return NULL; } + if (qemu_opt_get(opts, "backend") == NULL) { + fprintf(stderr, "chardev: \"%s\" missing backend\n", + qemu_opts_id(opts)); + return NULL; + } for (i = 0; i < ARRAY_SIZE(backend_table); i++) { if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0) break; @@ -2510,7 +2633,10 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, snprintf(base->label, len, "%s-base", qemu_opts_id(opts)); chr = qemu_chr_open_mux(base); chr->filename = base->filename; + chr->avail_connections = MAX_MUX; QTAILQ_INSERT_TAIL(&chardevs, chr, next); + } else { + chr->avail_connections = 1; } chr->label = qemu_strdup(qemu_opts_id(opts)); return chr; @@ -2534,9 +2660,31 @@ CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*i if (chr && qemu_opt_get_bool(opts, "mux", 0)) { monitor_init(chr, MONITOR_USE_READLINE); } + qemu_opts_del(opts); return chr; } +void qemu_chr_set_echo(struct CharDriverState *chr, bool echo) +{ + if (chr->chr_set_echo) { + chr->chr_set_echo(chr, echo); + } +} + +void qemu_chr_guest_open(struct CharDriverState *chr) +{ + if (chr->chr_guest_open) { + chr->chr_guest_open(chr); + } +} + +void qemu_chr_guest_close(struct CharDriverState *chr) +{ + if (chr->chr_guest_close) { + chr->chr_guest_close(chr); + } +} + void qemu_chr_close(CharDriverState *chr) { QTAILQ_REMOVE(&chardevs, chr, next); diff --git a/qemu-char.h b/qemu-char.h index 7671824..30ea925 100644 --- a/qemu-char.h +++ b/qemu-char.h @@ -6,6 +6,7 @@ #include "qemu-option.h" #include "qemu-config.h" #include "qobject.h" +#include "qstring.h" /* character device */ @@ -63,11 +64,15 @@ struct CharDriverState { void (*chr_send_event)(struct CharDriverState *chr, int event); void (*chr_close)(struct CharDriverState *chr); void (*chr_accept_input)(struct CharDriverState *chr); + void (*chr_set_echo)(struct CharDriverState *chr, bool echo); + void (*chr_guest_open)(struct CharDriverState *chr); + void (*chr_guest_close)(struct CharDriverState *chr); void *opaque; QEMUBH *bh; char *label; char *filename; int opened; + int avail_connections; QTAILQ_ENTRY(CharDriverState) next; }; @@ -75,8 +80,12 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); CharDriverState *qemu_chr_open_opts(QemuOpts *opts, void (*init)(struct CharDriverState *s)); CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s)); +void qemu_chr_set_echo(struct CharDriverState *chr, bool echo); +void qemu_chr_guest_open(struct CharDriverState *chr); +void qemu_chr_guest_close(struct CharDriverState *chr); void qemu_chr_close(CharDriverState *chr); -void qemu_chr_printf(CharDriverState *s, const char *fmt, ...); +void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len); void qemu_chr_send_event(CharDriverState *s, int event); void qemu_chr_add_handlers(CharDriverState *s, @@ -92,7 +101,7 @@ void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len); int qemu_chr_get_msgfd(CharDriverState *s); void qemu_chr_accept_input(CharDriverState *s); void qemu_chr_info_print(Monitor *mon, const QObject *ret_data); -void qemu_chr_info(Monitor *mon); +void qemu_chr_info(Monitor *mon, QObject **ret_data); CharDriverState *qemu_chr_find(const char *name); /* add an eventfd to the qemu devices that are polled */ @@ -100,6 +109,12 @@ CharDriverState *qemu_chr_open_eventfd(int eventfd); extern int term_escape_char; +/* memory chardev */ +void qemu_chr_init_mem(CharDriverState *chr); +void qemu_chr_close_mem(CharDriverState *chr); +QString *qemu_chr_mem_to_qs(CharDriverState *chr); +size_t qemu_chr_mem_osize(const CharDriverState *chr); + /* async I/O support */ int qemu_set_fd_handler2(int fd, diff --git a/qemu-common.h b/qemu-common.h index f8db5d4..897d510 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -18,10 +18,8 @@ typedef struct QEMUFile QEMUFile; typedef struct QEMUBH QEMUBH; typedef struct DeviceState DeviceState; -/* Hack around the mess dyngen-exec.h causes: We need QEMU_NORETURN in files that - cannot include the following headers without conflicts. This condition has - to be removed once dyngen is gone. */ -#ifndef __DYNGEN_EXEC_H__ +struct Monitor; +typedef struct Monitor Monitor; /* we put basic includes here to avoid repeating them in device drivers */ #include <stdlib.h> @@ -38,8 +36,17 @@ typedef struct DeviceState DeviceState; #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> +#include <sys/time.h> #include <assert.h> +#ifdef _WIN32 +#include "qemu-os-win32.h" +#endif + +#ifdef CONFIG_POSIX +#include "qemu-os-posix.h" +#endif + #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif @@ -55,6 +62,9 @@ typedef struct DeviceState DeviceState; #if !defined(ENOTSUP) #define ENOTSUP 4096 #endif +#ifndef TIME_MAX +#define TIME_MAX LONG_MAX +#endif #ifndef CONFIG_IOVEC #define CONFIG_IOVEC @@ -70,10 +80,29 @@ struct iovec { #include <sys/uio.h> #endif +#if defined __GNUC__ +# if (__GNUC__ < 4) || \ + defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4) + /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */ +# define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2))) +# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m))) +# else + /* Use gnu_printf when supported (qemu uses standard format strings). */ +# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2))) +# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) +# endif +#else +#define GCC_ATTR /**/ +#define GCC_FMT_ATTR(n, m) +#endif + +typedef int (*fprintf_function)(FILE *f, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + #ifdef _WIN32 #define fsync _commit #define lseek _lseeki64 -extern int qemu_ftruncate64(int, int64_t); +int qemu_ftruncate64(int, int64_t); #define ftruncate qemu_ftruncate64 static inline char *realpath(const char *path, char *resolved_path) @@ -122,8 +151,6 @@ void qemu_bh_delete(QEMUBH *bh); int qemu_bh_poll(void); void qemu_bh_update_timeout(int *timeout); -uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c); - void qemu_get_timedate(struct tm *tm, int offset); int qemu_timedate_diff(struct tm *tm); @@ -138,6 +165,21 @@ int qemu_fls(int i); int qemu_fdatasync(int fd); int fcntl_setfl(int fd, int flag); +/* + * strtosz() suffixes used to specify the default treatment of an + * argument passed to strtosz() without an explicit suffix. + * These should be defined using upper case characters in the range + * A-Z, as strtosz() will use qemu_toupper() on the given argument + * prior to comparison. + */ +#define STRTOSZ_DEFSUFFIX_TB 'T' +#define STRTOSZ_DEFSUFFIX_GB 'G' +#define STRTOSZ_DEFSUFFIX_MB 'M' +#define STRTOSZ_DEFSUFFIX_KB 'K' +#define STRTOSZ_DEFSUFFIX_B 'B' +int64_t strtosz(const char *nptr, char **end); +int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix); + /* path.c */ void init_paths(const char *prefix); const char *path(const char *pathname); @@ -158,6 +200,12 @@ const char *path(const char *pathname); #define qemu_isascii(c) isascii((unsigned char)(c)) #define qemu_toascii(c) toascii((unsigned char)(c)) +#ifdef _WIN32 +/* ffs() in oslib-win32.c for WIN32, strings.h for the rest of the world */ +int ffs(int i); +#endif + +void *qemu_oom_check(void *ptr); void *qemu_malloc(size_t size); void *qemu_realloc(void *ptr, size_t size); void *qemu_mallocz(size_t size); @@ -174,6 +222,7 @@ ssize_t qemu_write_full(int fd, const void *buf, size_t count) void qemu_set_cloexec(int fd); #ifndef _WIN32 +int qemu_add_child_watch(pid_t pid); int qemu_eventfd(int pipefd[2]); int qemu_pipe(int pipefd[2]); #endif @@ -183,14 +232,16 @@ void *get_mmap_addr(unsigned long size); /* Error handling. */ -void QEMU_NORETURN hw_error(const char *fmt, ...) - __attribute__ ((__format__ (__printf__, 1, 2))); +void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); /* IO callbacks. */ typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); typedef int IOCanReadHandler(void *opaque); typedef void IOHandler(void *opaque); +void qemu_iohandler_fill(int *pnfds, fd_set *readfds, fd_set *writefds, fd_set *xfds); +void qemu_iohandler_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, int rc); + struct ParallelIOArg { void *buffer; int count; @@ -281,14 +332,22 @@ typedef struct QEMUIOVector { void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); +void qemu_iovec_copy(QEMUIOVector *dst, QEMUIOVector *src, uint64_t skip, + size_t size); void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size); void qemu_iovec_destroy(QEMUIOVector *qiov); void qemu_iovec_reset(QEMUIOVector *qiov); void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf); void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count); +void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count); +void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count, + size_t skip); -struct Monitor; -typedef struct Monitor Monitor; +/* OS specific functions */ +void os_setup_early_signal_handling(void); +char *os_find_datadir(const char *argv0); +void os_parse_cmd_args(int index, const char *optarg); +void os_pidfile_error(void); /* Convert a byte between binary and BCD. */ static inline uint8_t to_bcd(uint8_t val) @@ -301,9 +360,31 @@ static inline uint8_t from_bcd(uint8_t val) return ((val >> 4) * 10) + (val & 0x0f); } -#include "module.h" +/* compute with 96 bit intermediate result: (a*b)/c */ +static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +{ + union { + uint64_t ll; + struct { +#ifdef HOST_WORDS_BIGENDIAN + uint32_t high, low; +#else + uint32_t low, high; +#endif + } l; + } u, res; + uint64_t rl, rh; + + u.ll = a; + rl = (uint64_t)u.l.low * (uint64_t)b; + rh = (uint64_t)u.l.high * (uint64_t)b; + rh += (rl >> 32); + res.l.high = rh / c; + res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; + return res.ll; +} -#endif /* dyngen-exec.h hack */ +#include "module.h" typedef enum DisplayType { diff --git a/qemu-config.c b/qemu-config.c index 1efdbec..14d3419 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -2,7 +2,6 @@ #include "qemu-error.h" #include "qemu-option.h" #include "qemu-config.h" -#include "sysemu.h" #include "hw/qdev.h" static QemuOptsList qemu_drive_opts = { @@ -146,6 +145,12 @@ static QemuOptsList qemu_chardev_opts = { },{ .name = "signal", .type = QEMU_OPT_BOOL, + },{ + .name = "name", + .type = QEMU_OPT_STRING, + },{ + .name = "debug", + .type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, @@ -283,11 +288,29 @@ static QemuOptsList qemu_mon_opts = { },{ .name = "default", .type = QEMU_OPT_BOOL, + },{ + .name = "pretty", + .type = QEMU_OPT_BOOL, }, { /* end of list */ } }, }; +#ifdef CONFIG_SIMPLE_TRACE +static QemuOptsList qemu_trace_opts = { + .name = "trace", + .implied_opt_name = "trace", + .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head), + .desc = { + { + .name = "file", + .type = QEMU_OPT_STRING, + }, + { /* end if list */ } + }, +}; +#endif + static QemuOptsList qemu_cpudef_opts = { .name = "cpudef", .head = QTAILQ_HEAD_INITIALIZER(qemu_cpudef_opts.head), @@ -336,6 +359,97 @@ static QemuOptsList qemu_cpudef_opts = { }, }; +QemuOptsList qemu_spice_opts = { + .name = "spice", + .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head), + .desc = { + { + .name = "port", + .type = QEMU_OPT_NUMBER, + },{ + .name = "tls-port", + .type = QEMU_OPT_NUMBER, + },{ + .name = "addr", + .type = QEMU_OPT_STRING, + },{ + .name = "ipv4", + .type = QEMU_OPT_BOOL, + },{ + .name = "ipv6", + .type = QEMU_OPT_BOOL, + },{ + .name = "password", + .type = QEMU_OPT_STRING, + },{ + .name = "disable-ticketing", + .type = QEMU_OPT_BOOL, + },{ + .name = "x509-dir", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-key-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-key-password", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-cert-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-cacert-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-dh-key-file", + .type = QEMU_OPT_STRING, + },{ + .name = "tls-ciphers", + .type = QEMU_OPT_STRING, + },{ + .name = "tls-channel", + .type = QEMU_OPT_STRING, + },{ + .name = "plaintext-channel", + .type = QEMU_OPT_STRING, + },{ + .name = "image-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "jpeg-wan-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "zlib-glz-wan-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "streaming-video", + .type = QEMU_OPT_STRING, + },{ + .name = "agent-mouse", + .type = QEMU_OPT_BOOL, + },{ + .name = "playback-compression", + .type = QEMU_OPT_BOOL, + }, + { /* end if list */ } + }, +}; + +QemuOptsList qemu_option_rom_opts = { + .name = "option-rom", + .implied_opt_name = "romfile", + .head = QTAILQ_HEAD_INITIALIZER(qemu_option_rom_opts.head), + .desc = { + { + .name = "bootindex", + .type = QEMU_OPT_NUMBER, + }, { + .name = "romfile", + .type = QEMU_OPT_STRING, + }, + { /* end if list */ } + }, +}; + static QemuOptsList *vm_config_groups[32] = { &qemu_drive_opts, &qemu_chardev_opts, @@ -346,6 +460,10 @@ static QemuOptsList *vm_config_groups[32] = { &qemu_global_opts, &qemu_mon_opts, &qemu_cpudef_opts, +#ifdef CONFIG_SIMPLE_TRACE + &qemu_trace_opts, +#endif + &qemu_option_rom_opts, NULL, }; diff --git a/qemu-config.h b/qemu-config.h index 533a049..20d707f 100644 --- a/qemu-config.h +++ b/qemu-config.h @@ -3,6 +3,7 @@ extern QemuOptsList qemu_fsdev_opts; extern QemuOptsList qemu_virtfs_opts; +extern QemuOptsList qemu_spice_opts; QemuOptsList *qemu_find_opts(const char *group); void qemu_add_opts(QemuOptsList *list); diff --git a/qemu-error.c b/qemu-error.c index 5a35e7c..41c191d 100644 --- a/qemu-error.c +++ b/qemu-error.c @@ -12,7 +12,6 @@ #include <stdio.h> #include "monitor.h" -#include "sysemu.h" /* * Print to current monitor if we have one, else to stderr. diff --git a/qemu-error.h b/qemu-error.h index a45609f..4d5c537 100644 --- a/qemu-error.h +++ b/qemu-error.h @@ -30,12 +30,11 @@ void loc_set_none(void); void loc_set_cmdline(char **argv, int idx, int cnt); void loc_set_file(const char *fname, int lno); -void error_vprintf(const char *fmt, va_list ap); -void error_printf(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); -void error_printf_unless_qmp(const char *fmt, ...) - __attribute__ ((format(printf, 1, 2))); +void error_vprintf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); +void error_printf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +void error_printf_unless_qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2); void error_print_loc(void); void error_set_progname(const char *argv0); -void error_report(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); +void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); #endif @@ -61,7 +61,7 @@ static void *qemu_io_alloc(size_t len, int pattern) if (misalign) len += MISALIGN_OFFSET; - buf = qemu_memalign(512, len); + buf = qemu_blockalign(bs, len); memset(buf, pattern, len); if (misalign) buf += MISALIGN_OFFSET; @@ -326,7 +326,7 @@ read_help(void) " -l, -- length for pattern verification (only with -P)\n" " -p, -- use bdrv_pread to read the file\n" " -P, -- use a pattern to verify read data\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" " -s, -- start offset for pattern verification (only with -P)\n" " -v, -- dump buffer to standard output\n" "\n"); @@ -509,7 +509,7 @@ readv_help(void) " -C, -- report statistics in a machine parsable format\n" " -P, -- use a pattern to verify read data\n" " -v, -- dump buffer to standard output\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -633,7 +633,7 @@ write_help(void) " -p, -- use bdrv_pwrite to write the file\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -765,7 +765,7 @@ writev_help(void) " filled with a set pattern (0xcdcdcdcd).\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -1100,7 +1100,7 @@ aio_read_help(void) " -C, -- report statistics in a machine parsable format\n" " -P, -- use a pattern to verify read data\n" " -v, -- dump buffer to standard output\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -1131,8 +1131,10 @@ aio_read_f(int argc, char **argv) case 'P': ctx->Pflag = 1; ctx->pattern = parse_pattern(optarg); - if (ctx->pattern < 0) + if (ctx->pattern < 0) { + free(ctx); return 0; + } break; case 'q': ctx->qflag = 1; @@ -1198,7 +1200,7 @@ aio_write_help(void) " used to ensure all outstanding aio requests have been completed\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -1394,6 +1396,93 @@ static const cmdinfo_t info_cmd = { .oneline = "prints information about the current file", }; +static void +discard_help(void) +{ + printf( +"\n" +" discards a range of bytes from the given offset\n" +"\n" +" Example:\n" +" 'discard 512 1k' - discards 1 kilobyte from 512 bytes into the file\n" +"\n" +" Discards a segment of the currently open file.\n" +" -C, -- report statistics in a machine parsable format\n" +" -q, -- quiet mode, do not show I/O statistics\n" +"\n"); +} + +static int discard_f(int argc, char **argv); + +static const cmdinfo_t discard_cmd = { + .name = "discard", + .altname = "d", + .cfunc = discard_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] off len", + .oneline = "discards a number of bytes at a specified offset", + .help = discard_help, +}; + +static int +discard_f(int argc, char **argv) +{ + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, ret; + int64_t offset; + int count; + + while ((c = getopt(argc, argv, "Cq")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'q': + qflag = 1; + break; + default: + return command_usage(&discard_cmd); + } + } + + if (optind != argc - 2) { + return command_usage(&discard_cmd); + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } + + optind++; + count = cvtnum(argv[optind]); + if (count < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } + + gettimeofday(&t1, NULL); + ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, count >> BDRV_SECTOR_BITS); + gettimeofday(&t2, NULL); + + if (ret < 0) { + printf("discard failed: %s\n", strerror(-ret)); + goto out; + } + + /* Finally, report back -- -C gives a parsable format */ + if (!qflag) { + t2 = tsub(t2, t1); + print_report("discard", &t2, offset, count, count, 1, Cflag); + } + +out: + return 0; +} + static int alloc_f(int argc, char **argv) { @@ -1446,6 +1535,44 @@ static const cmdinfo_t alloc_cmd = { }; static int +map_f(int argc, char **argv) +{ + int64_t offset; + int64_t nb_sectors; + char s1[64]; + int num, num_checked; + int ret; + const char *retstr; + + offset = 0; + nb_sectors = bs->total_sectors; + + do { + num_checked = MIN(nb_sectors, INT_MAX); + ret = bdrv_is_allocated(bs, offset, num_checked, &num); + retstr = ret ? " allocated" : "not allocated"; + cvtstr(offset << 9ULL, s1, sizeof(s1)); + printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n", + offset << 9ULL, num, num_checked, retstr, s1, ret); + + offset += num; + nb_sectors -= num; + } while(offset < bs->total_sectors); + + return 0; +} + +static const cmdinfo_t map_cmd = { + .name = "map", + .argmin = 0, + .argmax = 0, + .cfunc = map_f, + .args = "", + .oneline = "prints the allocated areas of a file", +}; + + +static int close_f(int argc, char **argv) { bdrv_close(bs); @@ -1682,7 +1809,9 @@ int main(int argc, char **argv) add_command(&truncate_cmd); add_command(&length_cmd); add_command(&info_cmd); + add_command(&discard_cmd); add_command(&alloc_cmd); + add_command(&map_cmd); add_args_command(init_args_command); add_check_command(init_check_command); diff --git a/qemu-lock.h b/qemu-lock.h index 9a3e6ac..a72edda 100644 --- a/qemu-lock.h +++ b/qemu-lock.h @@ -15,15 +15,11 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/> */ -/* Locking primitives. Most of this code should be redundant - - system emulation doesn't need/use locking, NPTL userspace uses - pthread mutexes, and non-NPTL userspace isn't threadsafe anyway. - In either case a spinlock is probably the wrong kind of lock. - Spinlocks are only good if you know annother CPU has the lock and is - likely to release it soon. In environments where you have more threads - than physical CPUs (the extreme case being a single CPU host) a spinlock - simply wastes CPU until the OS decides to preempt it. */ -#if defined(CONFIG_USE_NPTL) +/* configure guarantees us that we have pthreads on any host except + * mingw32, which doesn't support any of the user-only targets. + * So we can simply assume we have pthread mutexes here. + */ +#if defined(CONFIG_USER_ONLY) #include <pthread.h> #define spin_lock pthread_mutex_lock @@ -33,203 +29,15 @@ #else -#if defined(__hppa__) - -typedef int spinlock_t[4]; - -#define SPIN_LOCK_UNLOCKED { 1, 1, 1, 1 } - -static inline void resetlock (spinlock_t *p) -{ - (*p)[0] = (*p)[1] = (*p)[2] = (*p)[3] = 1; -} - -#else - +/* Empty implementations, on the theory that system mode emulation + * is single-threaded. This means that these functions should only + * be used from code run in the TCG cpu thread, and cannot protect + * data structures which might also be accessed from the IO thread + * or from signal handlers. + */ typedef int spinlock_t; - #define SPIN_LOCK_UNLOCKED 0 -static inline void resetlock (spinlock_t *p) -{ - *p = SPIN_LOCK_UNLOCKED; -} - -#endif - -#if defined(_ARCH_PPC) -static inline int testandset (int *p) -{ - int ret; - __asm__ __volatile__ ( - " lwarx %0,0,%1\n" - " xor. %0,%3,%0\n" - " bne $+12\n" - " stwcx. %2,0,%1\n" - " bne- $-16\n" - : "=&r" (ret) - : "r" (p), "r" (1), "r" (0) - : "cr0", "memory"); - return ret; -} -#elif defined(__i386__) -static inline int testandset (int *p) -{ - long int readval = 0; - - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" - : "+m" (*p), "+a" (readval) - : "r" (1) - : "cc"); - return readval; -} -#elif defined(__x86_64__) -static inline int testandset (int *p) -{ - long int readval = 0; - - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" - : "+m" (*p), "+a" (readval) - : "r" (1) - : "cc"); - return readval; -} -#elif defined(__s390__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n" - " jl 0b" - : "=&d" (ret) - : "r" (1), "a" (p), "0" (*p) - : "cc", "memory" ); - return ret; -} -#elif defined(__alpha__) -static inline int testandset (int *p) -{ - int ret; - unsigned long one; - - __asm__ __volatile__ ("0: mov 1,%2\n" - " ldl_l %0,%1\n" - " stl_c %2,%1\n" - " beq %2,1f\n" - ".subsection 2\n" - "1: br 0b\n" - ".previous" - : "=r" (ret), "=m" (*p), "=r" (one) - : "m" (*p)); - return ret; -} -#elif defined(__sparc__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__("ldstub [%1], %0" - : "=r" (ret) - : "r" (p) - : "memory"); - - return (ret ? 1 : 0); -} -#elif defined(__arm__) -static inline int testandset (int *spinlock) -{ - register unsigned int ret; - __asm__ __volatile__("swp %0, %1, [%2]" - : "=r"(ret) - : "0"(1), "r"(spinlock)); - - return ret; -} -#elif defined(__mc68000) -static inline int testandset (int *p) -{ - char ret; - __asm__ __volatile__("tas %1; sne %0" - : "=r" (ret) - : "m" (p) - : "cc","memory"); - return ret; -} -#elif defined(__hppa__) - -/* Because malloc only guarantees 8-byte alignment for malloc'd data, - and GCC only guarantees 8-byte alignment for stack locals, we can't - be assured of 16-byte alignment for atomic lock data even if we - specify "__attribute ((aligned(16)))" in the type declaration. So, - we use a struct containing an array of four ints for the atomic lock - type and dynamically select the 16-byte aligned int from the array - for the semaphore. */ -#define __PA_LDCW_ALIGNMENT 16 -static inline void *ldcw_align (void *p) { - unsigned long a = (unsigned long)p; - a = (a + __PA_LDCW_ALIGNMENT - 1) & ~(__PA_LDCW_ALIGNMENT - 1); - return (void *)a; -} - -static inline int testandset (spinlock_t *p) -{ - unsigned int ret; - p = ldcw_align(p); - __asm__ __volatile__("ldcw 0(%1),%0" - : "=r" (ret) - : "r" (p) - : "memory" ); - return !ret; -} - -#elif defined(__ia64) - -#include <ia64intrin.h> - -static inline int testandset (int *p) -{ - return __sync_lock_test_and_set (p, 1); -} -#elif defined(__mips__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__ ( - " .set push \n" - " .set noat \n" - " .set mips2 \n" - "1: li $1, 1 \n" - " ll %0, %1 \n" - " sc $1, %1 \n" - " beqz $1, 1b \n" - " .set pop " - : "=r" (ret), "+R" (*p) - : - : "memory"); - - return ret; -} -#else -#error unimplemented CPU support -#endif - -#if defined(CONFIG_USER_ONLY) -static inline void spin_lock(spinlock_t *lock) -{ - while (testandset(lock)); -} - -static inline void spin_unlock(spinlock_t *lock) -{ - resetlock(lock); -} - -static inline int spin_trylock(spinlock_t *lock) -{ - return !testandset(lock); -} -#else static inline void spin_lock(spinlock_t *lock) { } @@ -238,10 +46,4 @@ static inline void spin_unlock(spinlock_t *lock) { } -static inline int spin_trylock(spinlock_t *lock) -{ - return 1; -} -#endif - #endif diff --git a/qemu-malloc.c b/qemu-malloc.c index 36b0b36..8749fc8 100644 --- a/qemu-malloc.c +++ b/qemu-malloc.c @@ -24,16 +24,9 @@ #include "qemu-common.h" #include <stdlib.h> -static void *oom_check(void *ptr) -{ - if (ptr == NULL) { - abort(); - } - return ptr; -} - void qemu_free(void *ptr) { + //trace_qemu_free(ptr); free(ptr); } @@ -48,26 +41,35 @@ static int allow_zero_malloc(void) void *qemu_malloc(size_t size) { + void *ptr; if (!size && !allow_zero_malloc()) { abort(); } - return oom_check(malloc(size ? size : 1)); + ptr = qemu_oom_check(malloc(size ? size : 1)); + //trace_qemu_malloc(size, ptr); + return ptr; } void *qemu_realloc(void *ptr, size_t size) { + void *newptr; if (!size && !allow_zero_malloc()) { abort(); } - return oom_check(realloc(ptr, size ? size : 1)); + newptr = qemu_oom_check(realloc(ptr, size ? size : 1)); + //trace_qemu_realloc(ptr, size, newptr); + return newptr; } void *qemu_mallocz(size_t size) { + void *ptr; if (!size && !allow_zero_malloc()) { abort(); } - return oom_check(calloc(1, size ? size : 1)); + ptr = qemu_oom_check(calloc(1, size ? size : 1)); + //trace_qemu_malloc(size, ptr); + return ptr; } char *qemu_strdup(const char *str) diff --git a/qemu-option.c b/qemu-option.c index 1f8f41a..65db542 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -394,8 +394,8 @@ QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest, /* * Parses a parameter string (param) into an option list (dest). * - * list is the templace is. If dest is NULL, a new copy of list is created for - * it. If list is NULL, this function fails. + * list is the template option list. If dest is NULL, a new copy of list is + * created. If list is NULL, this function fails. * * A parameter string consists of one or more parameters, separated by commas. * Each parameter consists of its name and possibly of a value. In the latter @@ -416,20 +416,13 @@ QEMUOptionParameter *parse_option_parameters(const char *param, char value[256]; char *param_delim, *value_delim; char next_delim; - size_t num_options; if (list == NULL) { return NULL; } if (dest == NULL) { - // Count valid options - num_options = count_option_parameters(list); - - // Create a copy of the option list to fill in values - dest = qemu_mallocz((num_options + 1) * sizeof(QEMUOptionParameter)); - allocated = dest; - memcpy(dest, list, (num_options + 1) * sizeof(QEMUOptionParameter)); + dest = allocated = append_option_parameters(NULL, list); } while (*param) { diff --git a/qemu-options.h b/qemu-options.h new file mode 100644 index 0000000..d538f91 --- /dev/null +++ b/qemu-options.h @@ -0,0 +1,41 @@ +/* + * qemu-options.h + * + * Defines needed for command line argument processing. + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Jes Sorensen <Jes.Sorensen@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _QEMU_OPTIONS_H_ +#define _QEMU_OPTIONS_H_ + +enum { +#define DEF(option, opt_arg, opt_enum, opt_help) \ + opt_enum, +#define DEFHEADING(text) +#include "qemu-options.def" +#undef DEF +#undef DEFHEADING +#undef GEN_DOCS +}; + +#endif diff --git a/qemu-os-posix.h b/qemu-os-posix.h new file mode 100644 index 0000000..81fd9ab --- /dev/null +++ b/qemu-os-posix.h @@ -0,0 +1,54 @@ +/* + * posix specific declarations + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Jes Sorensen <Jes.Sorensen@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_OS_POSIX_H +#define QEMU_OS_POSIX_H + +static inline void os_host_main_loop_wait(int *timeout) +{ +} + +void os_set_line_buffering(void); +void os_set_proc_name(const char *s); +void os_setup_signal_handling(void); +void os_daemonize(void); +void os_setup_post(void); + +typedef struct timeval qemu_timeval; +#define qemu_gettimeofday(tp) gettimeofday(tp, NULL) + +#ifndef CONFIG_UTIMENSAT +#ifndef UTIME_NOW +# define UTIME_NOW ((1l << 30) - 1l) +#endif +#ifndef UTIME_OMIT +# define UTIME_OMIT ((1l << 30) - 2l) +#endif +#endif +typedef struct timespec qemu_timespec; +int qemu_utimensat(int dirfd, const char *path, const qemu_timespec *times, + int flags); + +#endif diff --git a/qemu-os-win32.h b/qemu-os-win32.h new file mode 100644 index 0000000..ed2753d --- /dev/null +++ b/qemu-os-win32.h @@ -0,0 +1,66 @@ +/* + * win32 specific declarations + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Jes Sorensen <Jes.Sorensen@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_OS_WIN32_H +#define QEMU_OS_WIN32_H + +#include <windows.h> +#include <winsock2.h> + +/* Polling handling */ + +/* return TRUE if no sleep should be done afterwards */ +typedef int PollingFunc(void *opaque); + +int qemu_add_polling_cb(PollingFunc *func, void *opaque); +void qemu_del_polling_cb(PollingFunc *func, void *opaque); + +/* Wait objects handling */ +typedef void WaitObjectFunc(void *opaque); + +int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); +void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); + +void os_host_main_loop_wait(int *timeout); + +static inline void os_setup_signal_handling(void) {} +static inline void os_daemonize(void) {} +static inline void os_setup_post(void) {} +void os_set_line_buffering(void); +static inline void os_set_proc_name(const char *dummy) {} + +#if !defined(EPROTONOSUPPORT) +# define EPROTONOSUPPORT EINVAL +#endif + +int setenv(const char *name, const char *value, int overwrite); + +typedef struct { + long tv_sec; + long tv_usec; +} qemu_timeval; +int qemu_gettimeofday(qemu_timeval *tp); + +#endif diff --git a/qemu-sockets.c b/qemu-sockets.c index c4c0f65..13c0b93 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -665,24 +665,28 @@ int unix_connect(const char *path) int unix_listen_opts(QemuOpts *opts) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } int unix_connect_opts(QemuOpts *opts) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } int unix_listen(const char *path, char *ostr, int olen) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } int unix_connect(const char *path) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } diff --git a/qemu-timer-common.c b/qemu-timer-common.c new file mode 100644 index 0000000..755e300 --- /dev/null +++ b/qemu-timer-common.c @@ -0,0 +1,63 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-timer.h" + +/***********************************************************/ +/* real time host monotonic timer */ + +#ifdef _WIN32 + +int64_t clock_freq; + +static void __attribute__((constructor)) init_get_clock(void) +{ + LARGE_INTEGER freq; + int ret; + ret = QueryPerformanceFrequency(&freq); + if (ret == 0) { + fprintf(stderr, "Could not calibrate ticks\n"); + exit(1); + } + clock_freq = freq.QuadPart; +} + +#else + +int use_rt_clock; + +static void __attribute__((constructor)) init_get_clock(void) +{ + use_rt_clock = 0; +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ + || defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ + || defined(__OpenBSD__) + { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + use_rt_clock = 1; + } + } +#endif +} +#endif diff --git a/qemu-timer.c b/qemu-timer.c index 82a5de3..4e51dd3 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -64,78 +64,6 @@ int64_t qemu_icount_bias; static QEMUTimer *icount_rt_timer; static QEMUTimer *icount_vm_timer; - -/***********************************************************/ -/* real time host monotonic timer */ - - -static int64_t get_clock_realtime(void) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000); -} - -#ifdef WIN32 - -static int64_t clock_freq; - -static void init_get_clock(void) -{ - LARGE_INTEGER freq; - int ret; - ret = QueryPerformanceFrequency(&freq); - if (ret == 0) { - fprintf(stderr, "Could not calibrate ticks\n"); - exit(1); - } - clock_freq = freq.QuadPart; -} - -static int64_t get_clock(void) -{ - LARGE_INTEGER ti; - QueryPerformanceCounter(&ti); - return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq); -} - -#else - -static int use_rt_clock; - -static void init_get_clock(void) -{ - use_rt_clock = 0; -#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ - || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - use_rt_clock = 1; - } - } -#endif -} - -static int64_t get_clock(void) -{ -#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ - || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - if (use_rt_clock) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return ts.tv_sec * 1000000000LL + ts.tv_nsec; - } else -#endif - { - /* XXX: using gettimeofday leads to problems if the date - changes, so it should be avoided. */ - return get_clock_realtime(); - } -} -#endif - /***********************************************************/ /* guest cycle counter */ @@ -259,12 +187,14 @@ void cpu_disable_ticks(void) struct QEMUClock { int type; int enabled; - /* XXX: add frequency */ + + QEMUTimer *warp_timer; }; struct QEMUTimer { QEMUClock *clock; - int64_t expire_time; + int64_t expire_time; /* in nanoseconds */ + int scale; QEMUTimerCB *cb; void *opaque; struct QEMUTimer *next; @@ -275,14 +205,23 @@ struct qemu_alarm_timer { int (*start)(struct qemu_alarm_timer *t); void (*stop)(struct qemu_alarm_timer *t); void (*rearm)(struct qemu_alarm_timer *t); - void *priv; - +#if defined(__linux__) + int fd; + timer_t timer; +#elif defined(_WIN32) + HANDLE timer; +#endif char expired; char pending; }; static struct qemu_alarm_timer *alarm_timer; +static bool qemu_timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) +{ + return timer_head && (timer_head->expire_time <= current_time); +} + int qemu_alarm_pending(void) { return alarm_timer->pending; @@ -301,15 +240,14 @@ static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) t->rearm(t); } -/* TODO: MIN_TIMER_REARM_US should be optimized */ -#define MIN_TIMER_REARM_US 250 +/* TODO: MIN_TIMER_REARM_NS should be optimized */ +#define MIN_TIMER_REARM_NS 250000 #ifdef _WIN32 -struct qemu_alarm_win32 { - MMRESULT timerId; - unsigned int period; -} alarm_win32_data = {0, 0}; +static int mm_start_timer(struct qemu_alarm_timer *t); +static void mm_stop_timer(struct qemu_alarm_timer *t); +static void mm_rearm_timer(struct qemu_alarm_timer *t); static int win32_start_timer(struct qemu_alarm_timer *t); static void win32_stop_timer(struct qemu_alarm_timer *t); @@ -353,7 +291,7 @@ static void icount_adjust(void) return; cur_time = cpu_get_clock(); - cur_icount = qemu_get_clock(vm_clock); + cur_icount = qemu_get_clock_ns(vm_clock); delta = cur_icount - cur_time; /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ if (delta > 0 @@ -375,14 +313,14 @@ static void icount_adjust(void) static void icount_adjust_rt(void * opaque) { qemu_mod_timer(icount_rt_timer, - qemu_get_clock(rt_clock) + 1000); + qemu_get_clock_ms(rt_clock) + 1000); icount_adjust(); } static void icount_adjust_vm(void * opaque) { qemu_mod_timer(icount_vm_timer, - qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); + qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10); icount_adjust(); } @@ -395,24 +333,24 @@ static struct qemu_alarm_timer alarm_timers[] = { #ifndef _WIN32 #ifdef __linux__ /* HPET - if available - is preferred */ - {"hpet", hpet_start_timer, hpet_stop_timer, NULL, NULL}, + {"hpet", hpet_start_timer, hpet_stop_timer, NULL}, /* ...otherwise try RTC */ - {"rtc", rtc_start_timer, rtc_stop_timer, NULL, NULL}, + {"rtc", rtc_start_timer, rtc_stop_timer, NULL}, #endif - {"unix", unix_start_timer, unix_stop_timer, NULL, NULL}, + {"unix", unix_start_timer, unix_stop_timer, NULL}, #ifdef __linux__ /* on Linux, the 'dynticks' clock sometimes doesn't work * properly. this results in the UI freezing while emulation * continues, for several seconds... So move it to the end * of the list. */ {"dynticks", dynticks_start_timer, - dynticks_stop_timer, dynticks_rearm_timer, NULL}, + dynticks_stop_timer, dynticks_rearm_timer}, #endif #else - {"dynticks", win32_start_timer, - win32_stop_timer, win32_rearm_timer, &alarm_win32_data}, - {"win32", win32_start_timer, - win32_stop_timer, NULL, &alarm_win32_data}, + {"mmtimer", mm_start_timer, mm_stop_timer, NULL}, + {"mmtimer2", mm_start_timer, mm_stop_timer, mm_rearm_timer}, + {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer}, + {"win32", win32_start_timer, win32_stop_timer, NULL}, #endif {NULL, } }; @@ -503,7 +441,92 @@ void qemu_clock_enable(QEMUClock *clock, int enabled) clock->enabled = enabled; } -QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) +static int64_t vm_clock_warp_start; + +static void icount_warp_rt(void *opaque) +{ + if (vm_clock_warp_start == -1) { + return; + } + + if (vm_running) { + int64_t clock = qemu_get_clock_ns(rt_clock); + int64_t warp_delta = clock - vm_clock_warp_start; + if (use_icount == 1) { + qemu_icount_bias += warp_delta; + } else { + /* + * In adaptive mode, do not let the vm_clock run too + * far ahead of real time. + */ + int64_t cur_time = cpu_get_clock(); + int64_t cur_icount = qemu_get_clock_ns(vm_clock); + int64_t delta = cur_time - cur_icount; + qemu_icount_bias += MIN(warp_delta, delta); + } + if (qemu_timer_expired(active_timers[QEMU_CLOCK_VIRTUAL], + qemu_get_clock_ns(vm_clock))) { + qemu_notify_event(); + } + } + vm_clock_warp_start = -1; +} + +void qemu_clock_warp(QEMUClock *clock) +{ + int64_t deadline; + + if (!clock->warp_timer) { + return; + } + + /* + * There are too many global variables to make the "warp" behavior + * applicable to other clocks. But a clock argument removes the + * need for if statements all over the place. + */ + assert(clock == vm_clock); + + /* + * If the CPUs have been sleeping, advance the vm_clock timer now. This + * ensures that the deadline for the timer is computed correctly below. + * This also makes sure that the insn counter is synchronized before the + * CPU starts running, in case the CPU is woken by an event other than + * the earliest vm_clock timer. + */ + icount_warp_rt(NULL); + if (qemu_cpu_has_work(cpu_single_env) || !active_timers[clock->type]) { + qemu_del_timer(clock->warp_timer); + return; + } + + vm_clock_warp_start = qemu_get_clock_ns(rt_clock); + deadline = qemu_next_icount_deadline(); + if (deadline > 0) { + /* + * Ensure the vm_clock proceeds even when the virtual CPU goes to + * sleep. Otherwise, the CPU might be waiting for a future timer + * interrupt to wake it up, but the interrupt never comes because + * the vCPU isn't running any insns and thus doesn't advance the + * vm_clock. + * + * An extreme solution for this problem would be to never let VCPUs + * sleep in icount mode if there is a pending vm_clock timer; rather + * time could just advance to the next vm_clock event. Instead, we + * do stop VCPUs and only advance vm_clock after some "real" time, + * (related to the time left until the next event) has passed. This + * rt_clock timer will do this. This avoids that the warps are too + * visible externally---for example, you will not be sending network + * packets continously instead of every 100ms. + */ + qemu_mod_timer(clock->warp_timer, vm_clock_warp_start + deadline); + } else { + qemu_notify_event(); + } +} + +QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, + QEMUTimerCB *cb, void *opaque) { QEMUTimer *ts; @@ -511,6 +534,7 @@ QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) ts->clock = clock; ts->cb = cb; ts->opaque = opaque; + ts->scale = scale; return ts; } @@ -541,7 +565,7 @@ void qemu_del_timer(QEMUTimer *ts) /* modify the current timer so that it will be fired when current_time >= expire_time. The corresponding callback will be called. */ -void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) +static void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) { QEMUTimer **pt, *t; @@ -553,10 +577,9 @@ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) pt = &active_timers[ts->clock->type]; for(;;) { t = *pt; - if (!t) - break; - if (t->expire_time > expire_time) + if (!qemu_timer_expired_ns(t, expire_time)) { break; + } pt = &t->next; } ts->expire_time = expire_time; @@ -569,9 +592,18 @@ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) qemu_rearm_alarm_timer(alarm_timer); } /* Interrupt execution to force deadline recalculation. */ - if (use_icount) + qemu_clock_warp(ts->clock); + if (use_icount) { qemu_notify_event(); } + } +} + +/* modify the current timer so that it will be fired when current_time + >= expire_time. The corresponding callback will be called. */ +void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) +{ + qemu_mod_timer_ns(ts, expire_time * ts->scale); } int qemu_timer_pending(QEMUTimer *ts) @@ -586,9 +618,7 @@ int qemu_timer_pending(QEMUTimer *ts) int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time) { - if (!timer_head) - return 0; - return (timer_head->expire_time <= current_time); + return qemu_timer_expired_ns(timer_head, current_time * timer_head->scale); } static void qemu_run_timers(QEMUClock *clock) @@ -599,12 +629,13 @@ static void qemu_run_timers(QEMUClock *clock) if (!clock->enabled) return; - current_time = qemu_get_clock (clock); + current_time = qemu_get_clock_ns(clock); ptimer_head = &active_timers[clock->type]; for(;;) { ts = *ptimer_head; - if (!ts || ts->expire_time > current_time) + if (!qemu_timer_expired_ns(ts, current_time)) { break; + } /* remove timer from the list before calling the callback */ *ptimer_head = ts->next; ts->next = NULL; @@ -650,7 +681,6 @@ int64_t qemu_get_clock_ns(QEMUClock *clock) void init_clocks(void) { - init_get_clock(); rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME); vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL); host_clock = qemu_new_clock(QEMU_CLOCK_HOST); @@ -677,7 +707,7 @@ void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) expire_time = qemu_get_be64(f); if (expire_time != -1) { - qemu_mod_timer(ts, expire_time); + qemu_mod_timer_ns(ts, expire_time); } else { qemu_del_timer(ts); } @@ -705,6 +735,10 @@ void configure_icount(const char *option) if (!option) return; +#ifdef CONFIG_IOTHREAD + vm_clock->warp_timer = qemu_new_timer_ns(rt_clock, icount_warp_rt, NULL); +#endif + if (strcmp(option, "auto") != 0) { icount_time_shift = strtol(option, NULL, 0); use_icount = 1; @@ -722,12 +756,12 @@ void configure_icount(const char *option) the virtual time trigger catches emulated time passing too fast. Realtime triggers occur even when idle, so use them less frequently than VM triggers. */ - icount_rt_timer = qemu_new_timer(rt_clock, icount_adjust_rt, NULL); + icount_rt_timer = qemu_new_timer_ms(rt_clock, icount_adjust_rt, NULL); qemu_mod_timer(icount_rt_timer, - qemu_get_clock(rt_clock) + 1000); - icount_vm_timer = qemu_new_timer(vm_clock, icount_adjust_vm, NULL); + qemu_get_clock_ms(rt_clock) + 1000); + icount_vm_timer = qemu_new_timer_ns(vm_clock, icount_adjust_vm, NULL); qemu_mod_timer(icount_vm_timer, - qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); + qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10); } void qemu_run_all_timers(void) @@ -758,10 +792,11 @@ int qemu_timer_alarm_pending(void) return ret; } + +static int64_t qemu_next_alarm_deadline(void); + #ifdef _WIN32 -static void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg, - DWORD_PTR dwUser, DWORD_PTR dw1, - DWORD_PTR dw2) +static void CALLBACK host_alarm_handler(PVOID lpParam, BOOLEAN unused) #else static void host_alarm_handler(int host_signum) #endif @@ -776,7 +811,7 @@ static void host_alarm_handler(int host_signum) static int64_t delta_min = INT64_MAX; static int64_t delta_max, delta_cum, last_clock, delta, ti; static int count; - ti = qemu_get_clock(vm_clock); + ti = qemu_get_clock_ns(vm_clock); if (last_clock != 0) { delta = ti - last_clock; if (delta < delta_min) @@ -800,14 +835,7 @@ static void host_alarm_handler(int host_signum) } #endif if (alarm_has_dynticks(t) || - (!use_icount && - qemu_timer_expired(active_timers[QEMU_CLOCK_VIRTUAL], - qemu_get_clock(vm_clock))) || - qemu_timer_expired(active_timers[QEMU_CLOCK_REALTIME], - qemu_get_clock(rt_clock)) || - qemu_timer_expired(active_timers[QEMU_CLOCK_HOST], - qemu_get_clock(host_clock))) { - + qemu_next_alarm_deadline () <= 0) { t->expired = alarm_has_dynticks(t); t->pending = 1; timer_alarm_pending = 1; @@ -815,20 +843,15 @@ static void host_alarm_handler(int host_signum) } } -int64_t qemu_next_deadline(void) +int64_t qemu_next_icount_deadline(void) { /* To avoid problems with overflow limit this to 2^32. */ int64_t delta = INT32_MAX; + assert(use_icount); if (active_timers[QEMU_CLOCK_VIRTUAL]) { delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time - - qemu_get_clock(vm_clock); - } - if (active_timers[QEMU_CLOCK_HOST]) { - int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time - - qemu_get_clock(host_clock); - if (hdelta < delta) - delta = hdelta; + qemu_get_clock_ns(vm_clock); } if (delta < 0) @@ -837,35 +860,37 @@ int64_t qemu_next_deadline(void) return delta; } -#ifndef _WIN32 - -#if defined(__linux__) - -#define RTC_FREQ 1024 - -static uint64_t qemu_next_deadline_dyntick(void) +static int64_t qemu_next_alarm_deadline(void) { int64_t delta; int64_t rtdelta; - if (use_icount) + if (!use_icount && active_timers[QEMU_CLOCK_VIRTUAL]) { + delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time - + qemu_get_clock_ns(vm_clock); + } else { delta = INT32_MAX; - else - delta = (qemu_next_deadline() + 999) / 1000; - + } + if (active_timers[QEMU_CLOCK_HOST]) { + int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time - + qemu_get_clock_ns(host_clock); + if (hdelta < delta) + delta = hdelta; + } if (active_timers[QEMU_CLOCK_REALTIME]) { rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time - - qemu_get_clock(rt_clock))*1000; + qemu_get_clock_ns(rt_clock)); if (rtdelta < delta) delta = rtdelta; } - if (delta < MIN_TIMER_REARM_US) - delta = MIN_TIMER_REARM_US; - return delta; } +#if defined(__linux__) + +#define RTC_FREQ 1024 + static void enable_sigio_timer(int fd) { struct sigaction act; @@ -914,7 +939,7 @@ static int hpet_start_timer(struct qemu_alarm_timer *t) goto fail; enable_sigio_timer(fd); - t->priv = (void *)(long)fd; + t->fd = fd; return 0; fail: @@ -924,7 +949,7 @@ fail: static void hpet_stop_timer(struct qemu_alarm_timer *t) { - int fd = (long)t->priv; + int fd = t->fd; close(fd); } @@ -953,14 +978,14 @@ static int rtc_start_timer(struct qemu_alarm_timer *t) enable_sigio_timer(rtc_fd); - t->priv = (void *)(long)rtc_fd; + t->fd = rtc_fd; return 0; } static void rtc_stop_timer(struct qemu_alarm_timer *t) { - int rtc_fd = (long)t->priv; + int rtc_fd = t->fd; close(rtc_fd); } @@ -995,24 +1020,24 @@ static int dynticks_start_timer(struct qemu_alarm_timer *t) return -1; } - t->priv = (void *)(long)host_timer; + t->timer = host_timer; return 0; } static void dynticks_stop_timer(struct qemu_alarm_timer *t) { - timer_t host_timer = (timer_t)(long)t->priv; + timer_t host_timer = t->timer; timer_delete(host_timer); } static void dynticks_rearm_timer(struct qemu_alarm_timer *t) { - timer_t host_timer = (timer_t)(long)t->priv; + timer_t host_timer = t->timer; struct itimerspec timeout; - int64_t nearest_delta_us = INT64_MAX; - int64_t current_us; + int64_t nearest_delta_ns = INT64_MAX; + int64_t current_ns; assert(alarm_has_dynticks(t)); if (!active_timers[QEMU_CLOCK_REALTIME] && @@ -1020,7 +1045,9 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t) !active_timers[QEMU_CLOCK_HOST]) return; - nearest_delta_us = qemu_next_deadline_dyntick(); + nearest_delta_ns = qemu_next_alarm_deadline(); + if (nearest_delta_ns < MIN_TIMER_REARM_NS) + nearest_delta_ns = MIN_TIMER_REARM_NS; /* check whether a timer is already running */ if (timer_gettime(host_timer, &timeout)) { @@ -1028,14 +1055,14 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t) fprintf(stderr, "Internal timer error: aborting\n"); exit(1); } - current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000; - if (current_us && current_us <= nearest_delta_us) + current_ns = timeout.it_value.tv_sec * 1000000000LL + timeout.it_value.tv_nsec; + if (current_ns && current_ns <= nearest_delta_ns) return; timeout.it_interval.tv_sec = 0; timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */ - timeout.it_value.tv_sec = nearest_delta_us / 1000000; - timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000; + timeout.it_value.tv_sec = nearest_delta_ns / 1000000000; + timeout.it_value.tv_nsec = nearest_delta_ns % 1000000000; if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) { perror("settime"); fprintf(stderr, "Internal timer error: aborting\n"); @@ -1045,6 +1072,8 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t) #endif /* defined(__linux__) */ +#if !defined(_WIN32) + static int unix_start_timer(struct qemu_alarm_timer *t) { struct sigaction act; @@ -1084,75 +1113,161 @@ static void unix_stop_timer(struct qemu_alarm_timer *t) #ifdef _WIN32 -static int win32_start_timer(struct qemu_alarm_timer *t) +static MMRESULT mm_timer; +static unsigned mm_period; + +static void CALLBACK mm_alarm_handler(UINT uTimerID, UINT uMsg, + DWORD_PTR dwUser, DWORD_PTR dw1, + DWORD_PTR dw2) +{ + struct qemu_alarm_timer *t = alarm_timer; + if (!t) { + return; + } + if (alarm_has_dynticks(t) || qemu_next_alarm_deadline() <= 0) { + t->expired = alarm_has_dynticks(t); + t->pending = 1; + qemu_notify_event(); + } +} + +static int mm_start_timer(struct qemu_alarm_timer *t) { TIMECAPS tc; - struct qemu_alarm_win32 *data = t->priv; UINT flags; memset(&tc, 0, sizeof(tc)); timeGetDevCaps(&tc, sizeof(tc)); - data->period = tc.wPeriodMin; - timeBeginPeriod(data->period); + mm_period = tc.wPeriodMin; + timeBeginPeriod(mm_period); flags = TIME_CALLBACK_FUNCTION; - if (alarm_has_dynticks(t)) + if (alarm_has_dynticks(t)) { flags |= TIME_ONESHOT; - else + } else { flags |= TIME_PERIODIC; + } - data->timerId = timeSetEvent(1, // interval (ms) - data->period, // resolution - host_alarm_handler, // function - (DWORD)t, // parameter + mm_timer = timeSetEvent(1, /* interval (ms) */ + mm_period, /* resolution */ + mm_alarm_handler, /* function */ + (DWORD_PTR)t, /* parameter */ flags); - if (!data->timerId) { + if (!mm_timer) { fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n", GetLastError()); - timeEndPeriod(data->period); + timeEndPeriod(mm_period); return -1; } return 0; } -static void win32_stop_timer(struct qemu_alarm_timer *t) +static void mm_stop_timer(struct qemu_alarm_timer *t) { - struct qemu_alarm_win32 *data = t->priv; - - timeKillEvent(data->timerId); - timeEndPeriod(data->period); + timeKillEvent(mm_timer); + timeEndPeriod(mm_period); } -static void win32_rearm_timer(struct qemu_alarm_timer *t) +static void mm_rearm_timer(struct qemu_alarm_timer *t) { - struct qemu_alarm_win32 *data = t->priv; + int nearest_delta_ms; assert(alarm_has_dynticks(t)); if (!active_timers[QEMU_CLOCK_REALTIME] && !active_timers[QEMU_CLOCK_VIRTUAL] && - !active_timers[QEMU_CLOCK_HOST]) + !active_timers[QEMU_CLOCK_HOST]) { return; + } - timeKillEvent(data->timerId); + timeKillEvent(mm_timer); - data->timerId = timeSetEvent(1, - data->period, - host_alarm_handler, - (DWORD)t, - TIME_ONESHOT | TIME_CALLBACK_FUNCTION); + nearest_delta_ms = (qemu_next_alarm_deadline() + 999999) / 1000000; + if (nearest_delta_ms < 1) { + nearest_delta_ms = 1; + } + mm_timer = timeSetEvent(nearest_delta_ms, + mm_period, + mm_alarm_handler, + (DWORD_PTR)t, + TIME_ONESHOT | TIME_CALLBACK_FUNCTION); - if (!data->timerId) { + if (!mm_timer) { fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n", GetLastError()); - timeEndPeriod(data->period); + timeEndPeriod(mm_period); exit(1); } } +static int win32_start_timer(struct qemu_alarm_timer *t) +{ + HANDLE hTimer; + BOOLEAN success; + + /* If you call ChangeTimerQueueTimer on a one-shot timer (its period + is zero) that has already expired, the timer is not updated. Since + creating a new timer is relatively expensive, set a bogus one-hour + interval in the dynticks case. */ + success = CreateTimerQueueTimer(&hTimer, + NULL, + host_alarm_handler, + t, + 1, + alarm_has_dynticks(t) ? 3600000 : 1, + WT_EXECUTEINTIMERTHREAD); + + if (!success) { + fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n", + GetLastError()); + return -1; + } + + t->timer = hTimer; + return 0; +} + +static void win32_stop_timer(struct qemu_alarm_timer *t) +{ + HANDLE hTimer = t->timer; + + if (hTimer) { + DeleteTimerQueueTimer(NULL, hTimer, NULL); + } +} + +static void win32_rearm_timer(struct qemu_alarm_timer *t) +{ + HANDLE hTimer = t->timer; + int nearest_delta_ms; + BOOLEAN success; + + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) + return; + + nearest_delta_ms = (qemu_next_alarm_deadline() + 999999) / 1000000; + if (nearest_delta_ms < 1) { + nearest_delta_ms = 1; + } + success = ChangeTimerQueueTimer(NULL, + hTimer, + nearest_delta_ms, + 3600000); + + if (!success) { + fprintf(stderr, "Failed to rearm win32 alarm timer: %ld\n", + GetLastError()); + exit(-1); + } + +} + #endif /* _WIN32 */ static void alarm_timer_on_change_state_rearm(void *opaque, int running, int reason) @@ -1236,7 +1351,7 @@ int qemu_calculate_timeout(void) } else { /* Wait for either IO to occur or the next timer event. */ - add = qemu_next_deadline(); + add = qemu_next_icount_deadline(); /* We advance the timer before checking for IO. Limit the amount we advance so that early IO activity won't get the guest too far ahead. */ diff --git a/qemu-timer.h b/qemu-timer.h index e175809..c84673a 100644 --- a/qemu-timer.h +++ b/qemu-timer.h @@ -2,9 +2,19 @@ #define QEMU_TIMER_H #include "qemu-common.h" +#include <time.h> +#include <sys/time.h> + +#ifdef _WIN32 +#include <windows.h> +#endif /* timers */ +#define SCALE_MS 1000000 +#define SCALE_US 1000 +#define SCALE_NS 1 + typedef struct QEMUClock QEMUClock; typedef void QEMUTimerCB(void *opaque); @@ -29,8 +39,11 @@ extern QEMUClock *host_clock; int64_t qemu_get_clock(QEMUClock *clock); int64_t qemu_get_clock_ns(QEMUClock *clock); void qemu_clock_enable(QEMUClock *clock, int enabled); +void qemu_clock_warp(QEMUClock *clock); + +QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, + QEMUTimerCB *cb, void *opaque); -QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque); void qemu_free_timer(QEMUTimer *ts); void qemu_del_timer(QEMUTimer *ts); void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time); @@ -40,6 +53,7 @@ int qemu_timer_alarm_pending(void); void qemu_run_all_timers(void); int qemu_alarm_pending(void); +int64_t qemu_next_icount_deadline(void); int64_t qemu_next_deadline(void); void configure_alarms(char const *opt); void configure_icount(const char *option); @@ -48,11 +62,75 @@ void init_clocks(void); int init_timer_alarm(void); void quit_timers(void); +int64_t cpu_get_ticks(void); +void cpu_enable_ticks(void); +void cpu_disable_ticks(void); + +static inline QEMUTimer *qemu_new_timer_ns(QEMUClock *clock, QEMUTimerCB *cb, + void *opaque) +{ + return qemu_new_timer(clock, SCALE_NS, cb, opaque); +} + +static inline QEMUTimer *qemu_new_timer_ms(QEMUClock *clock, QEMUTimerCB *cb, + void *opaque) +{ + return qemu_new_timer(clock, SCALE_MS, cb, opaque); +} + +static inline int64_t qemu_get_clock_ms(QEMUClock *clock) +{ + return qemu_get_clock_ns(clock) / SCALE_MS; +} + static inline int64_t get_ticks_per_sec(void) { return 1000000000LL; } +/* real time host monotonic timer */ +static inline int64_t get_clock_realtime(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000); +} + +/* Warning: don't insert tracepoints into these functions, they are + also used by simpletrace backend and tracepoints would cause + an infinite recursion! */ +#ifdef _WIN32 +extern int64_t clock_freq; + +static inline int64_t get_clock(void) +{ + LARGE_INTEGER ti; + QueryPerformanceCounter(&ti); + return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq); +} + +#else + +extern int use_rt_clock; + +static inline int64_t get_clock(void) +{ +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ + || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + if (use_rt_clock) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; + } else +#endif + { + /* XXX: using gettimeofday leads to problems if the date + changes, so it should be avoided. */ + return get_clock_realtime(); + } +} +#endif void qemu_get_timer(QEMUFile *f, QEMUTimer *ts); void qemu_put_timer(QEMUFile *f, QEMUTimer *ts); @@ -101,6 +101,10 @@ static const QErrorStringTable qerror_table[] = { .desc = "Device '%(device)' has no child bus", }, { + .error_fmt = QERR_DEVICE_NO_HOTPLUG, + .desc = "Device '%(device)' does not support hotplugging", + }, + { .error_fmt = QERR_DUPLICATE_ID, .desc = "Duplicate ID '%(id)' for %(object)", }, @@ -197,6 +201,11 @@ static const QErrorStringTable qerror_table[] = { .desc = "An undefined error has ocurred", }, { + .error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + .desc = "'%(device)' uses a %(format) feature which is not " + "supported by this qemu version: %(feature)", + }, + { .error_fmt = QERR_VNC_SERVER_FAILED, .desc = "Could not start VNC server on %(target)", }, @@ -218,7 +227,8 @@ QError *qerror_new(void) return qerr; } -static void qerror_abort(const QError *qerr, const char *fmt, ...) +static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr, + const char *fmt, ...) { va_list ap; @@ -233,7 +243,8 @@ static void qerror_abort(const QError *qerr, const char *fmt, ...) abort(); } -static void qerror_set_data(QError *qerr, const char *fmt, va_list *va) +static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr, + const char *fmt, va_list *va) { QObject *obj; @@ -34,12 +34,11 @@ typedef struct QError { QError *qerror_new(void); QError *qerror_from_info(const char *file, int linenr, const char *func, - const char *fmt, va_list *va); + const char *fmt, va_list *va) GCC_FMT_ATTR(4, 0); QString *qerror_human(const QError *qerror); void qerror_print(QError *qerror); void qerror_report_internal(const char *file, int linenr, const char *func, - const char *fmt, ...) - __attribute__ ((format(printf, 4, 5))); + const char *fmt, ...) GCC_FMT_ATTR(4, 5); #define qerror_report(fmt, ...) \ qerror_report_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__) QError *qobject_to_qerror(const QObject *obj); @@ -91,6 +90,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_DEVICE_NO_BUS \ "{ 'class': 'DeviceNoBus', 'data': { 'device': %s } }" +#define QERR_DEVICE_NO_HOTPLUG \ + "{ 'class': 'DeviceNoHotplug', 'data': { 'device': %s } }" + #define QERR_DUPLICATE_ID \ "{ 'class': 'DuplicateId', 'data': { 'id': %s, 'object': %s } }" @@ -163,7 +165,13 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_UNDEFINED_ERROR \ "{ 'class': 'UndefinedError', 'data': {} }" +#define QERR_UNKNOWN_BLOCK_FORMAT_FEATURE \ + "{ 'class': 'UnknownBlockFormatFeature', 'data': { 'device': %s, 'format': %s, 'feature': %s } }" + #define QERR_VNC_SERVER_FAILED \ "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }" +#define QERR_FEATURE_DISABLED \ + "{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }" + #endif /* QERROR_H */ @@ -72,43 +72,57 @@ QObject *qobject_from_jsonf(const char *string, ...) typedef struct ToJsonIterState { + int indent; + int pretty; int count; QString *str; } ToJsonIterState; -static void to_json(const QObject *obj, QString *str); +static void to_json(const QObject *obj, QString *str, int pretty, int indent); static void to_json_dict_iter(const char *key, QObject *obj, void *opaque) { ToJsonIterState *s = opaque; QString *qkey; + int j; - if (s->count) { + if (s->count) qstring_append(s->str, ", "); + + if (s->pretty) { + qstring_append(s->str, "\n"); + for (j = 0 ; j < s->indent ; j++) + qstring_append(s->str, " "); } qkey = qstring_from_str(key); - to_json(QOBJECT(qkey), s->str); + to_json(QOBJECT(qkey), s->str, s->pretty, s->indent); QDECREF(qkey); qstring_append(s->str, ": "); - to_json(obj, s->str); + to_json(obj, s->str, s->pretty, s->indent); s->count++; } static void to_json_list_iter(QObject *obj, void *opaque) { ToJsonIterState *s = opaque; + int j; - if (s->count) { + if (s->count) qstring_append(s->str, ", "); + + if (s->pretty) { + qstring_append(s->str, "\n"); + for (j = 0 ; j < s->indent ; j++) + qstring_append(s->str, " "); } - to_json(obj, s->str); + to_json(obj, s->str, s->pretty, s->indent); s->count++; } -static void to_json(const QObject *obj, QString *str) +static void to_json(const QObject *obj, QString *str, int pretty, int indent) { switch (qobject_type(obj)) { case QTYPE_QINT: { @@ -193,8 +207,16 @@ static void to_json(const QObject *obj, QString *str) s.count = 0; s.str = str; + s.indent = indent + 1; + s.pretty = pretty; qstring_append(str, "{"); qdict_iter(val, to_json_dict_iter, &s); + if (pretty) { + int j; + qstring_append(str, "\n"); + for (j = 0 ; j < indent ; j++) + qstring_append(str, " "); + } qstring_append(str, "}"); break; } @@ -204,8 +226,16 @@ static void to_json(const QObject *obj, QString *str) s.count = 0; s.str = str; + s.indent = indent + 1; + s.pretty = pretty; qstring_append(str, "["); qlist_iter(val, (void *)to_json_list_iter, &s); + if (pretty) { + int j; + qstring_append(str, "\n"); + for (j = 0 ; j < indent ; j++) + qstring_append(str, " "); + } qstring_append(str, "]"); break; } @@ -249,7 +279,16 @@ QString *qobject_to_json(const QObject *obj) { QString *str = qstring_new(); - to_json(obj, str); + to_json(obj, str, 0, 0); + + return str; +} + +QString *qobject_to_json_pretty(const QObject *obj) +{ + QString *str = qstring_new(); + + to_json(obj, str, 1, 0); return str; } @@ -18,11 +18,11 @@ #include "qobject.h" #include "qstring.h" -QObject *qobject_from_json(const char *string); -QObject *qobject_from_jsonf(const char *string, ...) - __attribute__((__format__ (__printf__, 1, 2))); -QObject *qobject_from_jsonv(const char *string, va_list *ap); +QObject *qobject_from_json(const char *string) GCC_FMT_ATTR(1, 0); +QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2); +QObject *qobject_from_jsonv(const char *string, va_list *ap) GCC_FMT_ATTR(1, 0); QString *qobject_to_json(const QObject *obj); +QString *qobject_to_json_pretty(const QObject *obj); #endif /* QJSON_H */ @@ -78,7 +78,6 @@ #include "qemu-timer.h" #include "qemu-char.h" #include "blockdev.h" -#include "outputchannel.h" #include "block.h" #include "audio/audio.h" #include "migration.h" @@ -89,25 +88,37 @@ #define SELF_ANNOUNCE_ROUNDS 5 -#define ETH_P_EXPERIMENTAL 0x01F1 /* just a number */ -//#define ETH_P_EXPERIMENTAL 0x0012 /* make it the size of the packet */ -#define EXPERIMENTAL_MAGIC 0xf1f23f4f -static int announce_self_create(uint8_t *buf, +#ifndef ETH_P_RARP +#define ETH_P_RARP 0x8035 +#endif +#define ARP_HTYPE_ETH 0x0001 +#define ARP_PTYPE_IP 0x0800 +#define ARP_OP_REQUEST_REV 0x3 + +static int announce_self_create(uint8_t *buf, uint8_t *mac_addr) { - uint32_t magic = EXPERIMENTAL_MAGIC; - uint16_t proto = htons(ETH_P_EXPERIMENTAL); + /* Ethernet header. */ + memset(buf, 0xff, 6); /* destination MAC addr */ + memcpy(buf + 6, mac_addr, 6); /* source MAC addr */ + *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */ - /* FIXME: should we send a different packet (arp/rarp/ping)? */ + /* RARP header. */ + *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */ + *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */ + *(buf + 18) = 6; /* hardware addr length (ethernet) */ + *(buf + 19) = 4; /* protocol addr length (IPv4) */ + *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */ + memcpy(buf + 22, mac_addr, 6); /* source hw addr */ + memset(buf + 28, 0x00, 4); /* source protocol addr */ + memcpy(buf + 32, mac_addr, 6); /* target hw addr */ + memset(buf + 38, 0x00, 4); /* target protocol addr */ - memset(buf, 0, 64); - memset(buf, 0xff, 6); /* h_dst */ - memcpy(buf + 6, mac_addr, 6); /* h_src */ - memcpy(buf + 12, &proto, 2); /* h_proto */ - memcpy(buf + 14, &magic, 4); /* magic */ + /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */ + memset(buf + 42, 0x00, 18); - return 64; /* len */ + return 60; /* len (FCS will be added by hardware) */ } static void qemu_announce_self_once(void *opaque) @@ -128,8 +139,10 @@ static void qemu_announce_self_once(void *opaque) vc->receive(vc, buf, len); } } - if (count--) { - qemu_mod_timer(timer, qemu_get_clock(rt_clock) + 100); + if (--count) { + /* delay 50ms, 150ms, 250ms, ... */ + qemu_mod_timer(timer, qemu_get_clock_ms(rt_clock) + + 50 + (SELF_ANNOUNCE_ROUNDS - count - 1) * 100); } else { qemu_del_timer(timer); qemu_free_timer(timer); @@ -139,7 +152,7 @@ static void qemu_announce_self_once(void *opaque) void qemu_announce_self(void) { static QEMUTimer *timer; - timer = qemu_new_timer(rt_clock, qemu_announce_self_once, &timer); + timer = qemu_new_timer_ms(rt_clock, qemu_announce_self_once, &timer); qemu_announce_self_once(&timer); } @@ -167,11 +180,11 @@ struct QEMUFile { int has_error; }; -typedef struct QEMUFilePopen +typedef struct QEMUFileStdio { - FILE *popen_file; + FILE *stdio_file; QEMUFile *file; -} QEMUFilePopen; +} QEMUFileStdio; typedef struct QEMUFileSocket { @@ -179,7 +192,7 @@ typedef struct QEMUFileSocket QEMUFile *file; } QEMUFileSocket; -static int file_socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) { QEMUFileSocket *s = opaque; ssize_t len; @@ -201,16 +214,16 @@ static int file_socket_close(void *opaque) return 0; } -static int popen_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) +static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { - QEMUFilePopen *s = opaque; - return fwrite(buf, 1, size, s->popen_file); + QEMUFileStdio *s = opaque; + return fwrite(buf, 1, size, s->stdio_file); } -static int popen_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) { - QEMUFilePopen *s = opaque; - FILE *fp = s->popen_file; + QEMUFileStdio *s = opaque; + FILE *fp = s->stdio_file; int bytes; do { @@ -220,31 +233,42 @@ static int popen_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) return bytes; } -static int popen_close(void *opaque) +static int stdio_pclose(void *opaque) { - QEMUFilePopen *s = opaque; - pclose(s->popen_file); + QEMUFileStdio *s = opaque; + int ret; + ret = pclose(s->stdio_file); + qemu_free(s); + return ret; +} + +static int stdio_fclose(void *opaque) +{ + QEMUFileStdio *s = opaque; + fclose(s->stdio_file); qemu_free(s); return 0; } -QEMUFile *qemu_popen(FILE *popen_file, const char *mode) +QEMUFile *qemu_popen(FILE *stdio_file, const char *mode) { - QEMUFilePopen *s; + QEMUFileStdio *s; - if (popen_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { + if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { fprintf(stderr, "qemu_popen: Argument validity check failed\n"); return NULL; } - s = qemu_mallocz(sizeof(QEMUFilePopen)); + s = qemu_mallocz(sizeof(QEMUFileStdio)); - s->popen_file = popen_file; + s->stdio_file = stdio_file; if(mode[0] == 'r') { - s->file = qemu_fopen_ops(s, NULL, popen_get_buffer, popen_close, NULL, NULL, NULL); + s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_pclose, + NULL, NULL, NULL); } else { - s->file = qemu_fopen_ops(s, popen_put_buffer, NULL, popen_close, NULL, NULL, NULL); + s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_pclose, + NULL, NULL, NULL); } return s->file; } @@ -261,93 +285,112 @@ QEMUFile *qemu_popen_cmd(const char *command, const char *mode) return qemu_popen(popen_file, mode); } -int qemu_popen_fd(QEMUFile *f) +int qemu_stdio_fd(QEMUFile *f) { - QEMUFilePopen *p; + QEMUFileStdio *p; int fd; - p = (QEMUFilePopen *)f->opaque; - fd = fileno(p->popen_file); + p = (QEMUFileStdio *)f->opaque; + fd = fileno(p->stdio_file); return fd; } +QEMUFile *qemu_fdopen(int fd, const char *mode) +{ + QEMUFileStdio *s; + + if (mode == NULL || + (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != 'b' || mode[2] != 0) { + fprintf(stderr, "qemu_fdopen: Argument validity check failed\n"); + return NULL; + } + + s = qemu_mallocz(sizeof(QEMUFileStdio)); + s->stdio_file = fdopen(fd, mode); + if (!s->stdio_file) + goto fail; + + if(mode[0] == 'r') { + s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_fclose, + NULL, NULL, NULL); + } else { + s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_fclose, + NULL, NULL, NULL); + } + return s->file; + +fail: + qemu_free(s); + return NULL; +} + QEMUFile *qemu_fopen_socket(int fd) { QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket)); s->fd = fd; - s->file = qemu_fopen_ops(s, NULL, file_socket_get_buffer, file_socket_close, NULL, NULL, NULL); + s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, file_socket_close, + NULL, NULL, NULL); return s->file; } -typedef struct QEMUFileStdio -{ - FILE *outfile; -} QEMUFileStdio; - static int file_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { QEMUFileStdio *s = opaque; - fseek(s->outfile, pos, SEEK_SET); - fwrite(buf, 1, size, s->outfile); - return size; + fseek(s->stdio_file, pos, SEEK_SET); + return fwrite(buf, 1, size, s->stdio_file); } static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) { QEMUFileStdio *s = opaque; - fseek(s->outfile, pos, SEEK_SET); - return fread(buf, 1, size, s->outfile); -} - -static int file_close(void *opaque) -{ - QEMUFileStdio *s = opaque; - fclose(s->outfile); - qemu_free(s); - return 0; + fseek(s->stdio_file, pos, SEEK_SET); + return fread(buf, 1, size, s->stdio_file); } QEMUFile *qemu_fopen(const char *filename, const char *mode) { QEMUFileStdio *s; + if (mode == NULL || + (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != 'b' || mode[2] != 0) { + fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); + return NULL; + } + s = qemu_mallocz(sizeof(QEMUFileStdio)); - s->outfile = fopen(filename, mode); - if (!s->outfile) + s->stdio_file = fopen(filename, mode); + if (!s->stdio_file) goto fail; - if (!strcmp(mode, "wb")) - return qemu_fopen_ops(s, file_put_buffer, NULL, file_close, NULL, NULL, NULL); - else if (!strcmp(mode, "rb")) - return qemu_fopen_ops(s, NULL, file_get_buffer, file_close, NULL, NULL, NULL); - + if(mode[0] == 'w') { + s->file = qemu_fopen_ops(s, file_put_buffer, NULL, stdio_fclose, + NULL, NULL, NULL); + } else { + s->file = qemu_fopen_ops(s, NULL, file_get_buffer, stdio_fclose, + NULL, NULL, NULL); + } + return s->file; fail: - if (s->outfile) - fclose(s->outfile); qemu_free(s); return NULL; } -typedef struct QEMUFileBdrv -{ - BlockDriverState *bs; - int64_t base_offset; -} QEMUFileBdrv; - static int block_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { - bdrv_save_vmstate(((QEMUFileBdrv*)opaque)->bs, buf, pos, size); + bdrv_save_vmstate(opaque, buf, pos, size); return size; } static int block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) { - return bdrv_load_vmstate(((QEMUFileBdrv*)opaque)->bs, buf, pos, size); + return bdrv_load_vmstate(opaque, buf, pos, size); } static int bdrv_fclose(void *opaque) @@ -355,19 +398,12 @@ static int bdrv_fclose(void *opaque) return 0; } -static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable) +static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) { - QEMUFileBdrv *s; - - s = qemu_mallocz(sizeof(QEMUFileBdrv)); - - s->bs = bs; - s->base_offset = offset; - if (is_writable) - return qemu_fopen_ops(s, block_put_buffer, NULL, bdrv_fclose, NULL, NULL, NULL); - - return qemu_fopen_ops(s, NULL, block_get_buffer, bdrv_fclose, NULL, NULL, NULL); + return qemu_fopen_ops(bs, block_put_buffer, NULL, bdrv_fclose, + NULL, NULL, NULL); + return qemu_fopen_ops(bs, NULL, block_get_buffer, bdrv_fclose, NULL, NULL, NULL); } QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, @@ -387,6 +423,7 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, f->close = close; f->rate_limit = rate_limit; f->set_rate_limit = set_rate_limit; + f->get_rate_limit = get_rate_limit; f->is_write = 0; return f; @@ -564,9 +601,19 @@ int qemu_file_rate_limit(QEMUFile *f) return 0; } -size_t qemu_file_set_rate_limit(QEMUFile *f, size_t new_rate) +int64_t qemu_file_get_rate_limit(QEMUFile *f) { - if (f->set_rate_limit) + if (f->get_rate_limit) + return f->get_rate_limit(f->opaque); + + return 0; +} + +int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate) +{ + /* any failed or completed migration keeps its state to allow probing of + * migration data, but has no associated file anymore */ + if (f && f->set_rate_limit) return f->set_rate_limit(f->opaque, new_rate); return 0; @@ -1151,21 +1198,7 @@ static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, return ret; } -static int -monitor_output_channel_cb(void* opaque, const char* fmt, va_list ap) -{ - return monitor_vprintf((Monitor*) opaque, fmt, ap); -} - - -void do_savevm(Monitor *mon, const char *name) -{ - OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb); - do_savevm_oc(oc, name); - output_channel_free(oc); -} - -void do_savevm_oc(OutputChannel *err, const char *name) +void do_savevm(Monitor *err, const char *name) { BlockDriverState *bs, *bs1; QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; @@ -1182,7 +1215,7 @@ void do_savevm_oc(OutputChannel *err, const char *name) bs = bdrv_snapshots(); if (!bs) { - output_channel_printf(err, "No block device can accept snapshots\n"); + monitor_printf(err, "No block device can accept snapshots\n"); return; } @@ -1218,25 +1251,25 @@ void do_savevm_oc(OutputChannel *err, const char *name) sn->date_sec = tv.tv_sec; sn->date_nsec = tv.tv_usec * 1000; #endif - sn->vm_clock_nsec = qemu_get_clock(vm_clock); + sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock); if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { - output_channel_printf(err, "Device %s does not support VM state snapshots\n", + monitor_printf(err, "Device %s does not support VM state snapshots\n", bdrv_get_device_name(bs)); goto the_end; } /* save the VM state */ - f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1); + f = qemu_fopen_bdrv(bs, 1); if (!f) { - output_channel_printf(err, "Could not open VM state file\n"); + monitor_printf(err, "Could not open VM state file\n"); goto the_end; } ret = qemu_savevm_state(f); vm_state_size = qemu_ftell(f); qemu_fclose(f); if (ret < 0) { - output_channel_printf(err, "Error %d while writing VM\n", ret); + monitor_printf(err, "Error %d while writing VM\n", ret); goto the_end; } @@ -1248,7 +1281,7 @@ void do_savevm_oc(OutputChannel *err, const char *name) if (must_delete) { ret = bdrv_snapshot_delete(bs1, old_sn->id_str); if (ret < 0) { - output_channel_printf(err, + monitor_printf(err, "Error while deleting snapshot on '%s'\n", bdrv_get_device_name(bs1)); } @@ -1257,7 +1290,7 @@ void do_savevm_oc(OutputChannel *err, const char *name) sn->vm_state_size = (bs == bs1 ? vm_state_size : 0); ret = bdrv_snapshot_create(bs1, sn); if (ret < 0) { - output_channel_printf(err, "Error while creating snapshot on '%s'\n", + monitor_printf(err, "Error while creating snapshot on '%s'\n", bdrv_get_device_name(bs1)); } } @@ -1268,15 +1301,7 @@ void do_savevm_oc(OutputChannel *err, const char *name) vm_start(); } -void do_loadvm(Monitor *mon, const char *name) -{ - OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb); - android_snapshot_update_time_request = 1; - do_loadvm_oc(oc, name); - output_channel_free(oc); -} - -void do_loadvm_oc(OutputChannel *err, const char *name) +void do_loadvm(Monitor *err, const char *name) { BlockDriverState *bs, *bs1; BlockDriverInfo bdi1, *bdi = &bdi1; @@ -1287,7 +1312,7 @@ void do_loadvm_oc(OutputChannel *err, const char *name) bs = bdrv_snapshots(); if (!bs) { - output_channel_printf(err, "No block device supports snapshots\n"); + monitor_printf(err, "No block device supports snapshots\n"); return; } @@ -1303,20 +1328,20 @@ void do_loadvm_oc(OutputChannel *err, const char *name) ret = bdrv_snapshot_goto(bs1, name); if (ret < 0) { if (bs != bs1) - output_channel_printf(err, "Warning: "); + monitor_printf(err, "Warning: "); switch(ret) { case -ENOTSUP: - output_channel_printf(err, + monitor_printf(err, "Snapshots not supported on device '%s'\n", bdrv_get_device_name(bs1)); break; case -ENOENT: - output_channel_printf(err, "Could not find snapshot '%s' on " + monitor_printf(err, "Could not find snapshot '%s' on " "device '%s'\n", name, bdrv_get_device_name(bs1)); break; default: - output_channel_printf(err, "Error %d while activating snapshot on" + monitor_printf(err, "Error %d while activating snapshot on" " '%s'\n", ret, bdrv_get_device_name(bs1)); break; } @@ -1328,7 +1353,7 @@ void do_loadvm_oc(OutputChannel *err, const char *name) } if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { - output_channel_printf(err, "Device %s does not support VM state snapshots\n", + monitor_printf(err, "Device %s does not support VM state snapshots\n", bdrv_get_device_name(bs)); return; } @@ -1339,35 +1364,29 @@ void do_loadvm_oc(OutputChannel *err, const char *name) goto the_end; /* restore the VM state */ - f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0); + f = qemu_fopen_bdrv(bs, 0); if (!f) { - output_channel_printf(err, "Could not open VM state file\n"); + monitor_printf(err, "Could not open VM state file\n"); goto the_end; } ret = qemu_loadvm_state(f); qemu_fclose(f); if (ret < 0) { - output_channel_printf(err, "Error %d while loading VM state\n", ret); + monitor_printf(err, "Error %d while loading VM state\n", ret); } the_end: if (saved_vm_running) vm_start(); } -void do_delvm(Monitor *mon, const char *name) -{ - OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb); - do_delvm_oc(oc, name); - output_channel_free(oc); -} -void do_delvm_oc(OutputChannel *err, const char *name) +void do_delvm(Monitor *err, const char *name) { BlockDriverState *bs, *bs1; int ret; bs = bdrv_snapshots(); if (!bs) { - output_channel_printf(err, "No block device supports snapshots\n"); + monitor_printf(err, "No block device supports snapshots\n"); return; } @@ -1377,25 +1396,18 @@ void do_delvm_oc(OutputChannel *err, const char *name) ret = bdrv_snapshot_delete(bs1, name); if (ret < 0) { if (ret == -ENOTSUP) - output_channel_printf(err, + monitor_printf(err, "Snapshots not supported on device '%s'\n", bdrv_get_device_name(bs1)); else - output_channel_printf(err, "Error %d while deleting snapshot on " + monitor_printf(err, "Error %d while deleting snapshot on " "'%s'\n", ret, bdrv_get_device_name(bs1)); } } } } -void do_info_snapshots(Monitor *mon) -{ - OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb); - do_info_snapshots_oc(oc, oc); - output_channel_free(oc); -} - -void do_info_snapshots_oc(OutputChannel *out, OutputChannel *err) +void do_info_snapshots(Monitor* out, Monitor* err) { BlockDriverState *bs, *bs1; QEMUSnapshotInfo *sn_tab, *sn; @@ -1404,30 +1416,30 @@ void do_info_snapshots_oc(OutputChannel *out, OutputChannel *err) bs = bdrv_snapshots(); if (!bs) { - output_channel_printf(err, "No available block device supports snapshots\n"); + monitor_printf(err, "No available block device supports snapshots\n"); return; } - output_channel_printf(out, "Snapshot devices:"); + monitor_printf(out, "Snapshot devices:"); bs1 = NULL; while ((bs1 = bdrv_next(bs1))) { if (bdrv_can_snapshot(bs1)) { if (bs == bs1) - output_channel_printf(out, " %s", bdrv_get_device_name(bs1)); + monitor_printf(out, " %s", bdrv_get_device_name(bs1)); } } - output_channel_printf(out, "\n"); + monitor_printf(out, "\n"); nb_sns = bdrv_snapshot_list(bs, &sn_tab); if (nb_sns < 0) { - output_channel_printf(err, "bdrv_snapshot_list: error %d\n", nb_sns); + monitor_printf(err, "bdrv_snapshot_list: error %d\n", nb_sns); return; } - output_channel_printf(out, "Snapshot list (from %s):\n", + monitor_printf(out, "Snapshot list (from %s):\n", bdrv_get_device_name(bs)); - output_channel_printf(out, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); + monitor_printf(out, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); for(i = 0; i < nb_sns; i++) { sn = &sn_tab[i]; - output_channel_printf(out, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); + monitor_printf(out, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); } qemu_free(sn_tab); } @@ -137,7 +137,7 @@ netshaper_expires( NetShaper shaper ) QueuedPacket packet; while ((packet = shaper->packets) != NULL) { - int64_t now = qemu_get_clock( SHAPER_CLOCK ); + int64_t now = qemu_get_clock_ms( SHAPER_CLOCK ); if (packet->expiration > now) break; @@ -167,9 +167,9 @@ netshaper_create( int do_copy, shaper->active = 0; shaper->packets = NULL; shaper->num_packets = 0; - shaper->timer = qemu_new_timer( SHAPER_CLOCK, - (QEMUTimerCB*) netshaper_expires, - shaper ); + shaper->timer = qemu_new_timer_ms( SHAPER_CLOCK, + (QEMUTimerCB*) netshaper_expires, + shaper ); shaper->send_func = send_func; shaper->max_rate = 1e6; shaper->inv_rate = 0.; @@ -216,7 +216,7 @@ netshaper_send_aux( NetShaper shaper, return; } - now = qemu_get_clock( SHAPER_CLOCK ); + now = qemu_get_clock_ms( SHAPER_CLOCK ); if (now >= shaper->block_until) { shaper->send_func( data, size, opaque ); shaper->block_until = now + size*shaper->inv_rate; @@ -274,7 +274,7 @@ netshaper_can_send( NetShaper shaper ) if (shaper->packets) return 0; - now = qemu_get_clock( SHAPER_CLOCK ); + now = qemu_get_clock_ms( SHAPER_CLOCK ); return (now >= shaper->block_until); } @@ -424,7 +424,7 @@ static void netdelay_expires( NetDelay delay ) { Session session; - int64_t now = qemu_get_clock( SHAPER_CLOCK ); + int64_t now = qemu_get_clock_ms( SHAPER_CLOCK ); int rearm = 0; int64_t rearm_time = 0; @@ -463,9 +463,9 @@ netdelay_create( NetShaperSendFunc send_func ) delay->sessions = NULL; delay->num_sessions = 0; - delay->timer = qemu_new_timer( SHAPER_CLOCK, - (QEMUTimerCB*) netdelay_expires, - delay ); + delay->timer = qemu_new_timer_ms( SHAPER_CLOCK, + (QEMUTimerCB*) netdelay_expires, + delay ); delay->active = 0; delay->min_ms = 0; delay->max_ms = 0; @@ -553,7 +553,7 @@ netdelay_send_aux( NetDelay delay, const void* data, size_t size, void* opaqu delay->sessions = session; delay->num_sessions += 1; - session->expiration = qemu_get_clock( SHAPER_CLOCK ) + latency; + session->expiration = qemu_get_clock_ms( SHAPER_CLOCK ) + latency; session->src_ip = info->src_ip; session->dst_ip = info->dst_ip; @@ -8,7 +8,6 @@ #include "qemu-timer.h" #include "qdict.h" #include "qerror.h" -#include "outputchannel.h" #ifdef _WIN32 #include <windows.h> @@ -51,6 +50,7 @@ void qemu_system_powerdown_request(void); int qemu_shutdown_requested(void); int qemu_reset_requested(void); int qemu_powerdown_requested(void); +void qemu_system_killed(int signal, pid_t pid); #ifdef NEED_CPU_H #if !defined(TARGET_SPARC) // Please implement a power failure function to signal the OS @@ -62,13 +62,9 @@ void qemu_system_powerdown(void); void qemu_system_reset(void); void do_savevm(Monitor *mon, const char *name); -void do_savevm_oc(OutputChannel *err, const char *name); void do_loadvm(Monitor *mon, const char *name); -void do_loadvm_oc(OutputChannel *err, const char *name); void do_delvm(Monitor *mon, const char *name); -void do_delvm_oc(OutputChannel *err, const char *name); -void do_info_snapshots(Monitor *mon); -void do_info_snapshots_oc(OutputChannel *out, OutputChannel *err); +void do_info_snapshots(Monitor *mon, Monitor* err); void qemu_announce_self(void); @@ -91,22 +87,6 @@ void qemu_error_internal(const char *file, int linenr, const char *func, #define qemu_error_new(fmt, ...) \ qemu_error_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__) -#ifdef _WIN32 -/* Polling handling */ - -/* return TRUE if no sleep should be done afterwards */ -typedef int PollingFunc(void *opaque); - -int qemu_add_polling_cb(PollingFunc *func, void *opaque); -void qemu_del_polling_cb(PollingFunc *func, void *opaque); - -/* Wait objects handling */ -typedef void WaitObjectFunc(void *opaque); - -int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); -void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); -#endif - /* TAP win32 */ int tap_win32_init(VLANState *vlan, const char *model, const char *name, const char *ifname); @@ -138,10 +118,6 @@ extern int semihosting_enabled; extern int old_param; extern QEMUClock *rtc_clock; -#ifdef CONFIG_KQEMU -extern int kqemu_allowed; -#endif - #define MAX_NODES 64 extern int nb_numa_nodes; extern uint64_t node_mem[MAX_NODES]; @@ -263,21 +239,6 @@ void pstrcpy_targphys(target_phys_addr_t dest, int buf_size, const char *source); #endif -#ifdef HAS_AUDIO -struct soundhw { - const char *name; - const char *descr; - int enabled; - int isa; - union { - int (*init_isa) (qemu_irq *pic); - int (*init_pci) (PCIBus *bus); - } init; -}; - -extern struct soundhw soundhw[]; -#endif - void do_usb_add(Monitor *mon, const char *devname); void do_usb_del(Monitor *mon, const char *devname); void usb_info(Monitor *mon); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 240be27..f16e391 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -25,6 +25,8 @@ #define CPUState struct CPUARMState +#include "config.h" +#include "qemu-common.h" #include "cpu-defs.h" #include "softfloat.h" diff --git a/target-arm/helper-android.c b/target-arm/helper-android.c new file mode 100644 index 0000000..8c203d4 --- /dev/null +++ b/target-arm/helper-android.c @@ -0,0 +1,51 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "cpu.h" +#include "exec-all.h" +#include "gdbstub.h" +#include "def-helper.h" +#include "helper-android.h" +#include "qemu-common.h" + +#ifdef CONFIG_TRACE +#include "android-trace.h" + +void HELPER(traceTicks)(uint32_t ticks) +{ + sim_time += ticks; +} + +void HELPER(traceInsn)(void) +{ + trace_insn_helper(); +} + +#if HOST_LONG_BITS == 32 +void HELPER(traceBB32)(uint64_t bb_num, uint32_t tb) +{ + trace_bb_helper(bb_num, (void*)tb); +} +#endif + +#if HOST_LONG_BITS == 64 +void HELPER(traceBB64)(uint64_t bb_num, uint64_t tb) +{ + trace_bb_helper(bb_num, (void*)tb); +} +#endif + +#endif /* CONFIG_TRACE */ + +#ifdef CONFIG_MEMCHECK +#include "memcheck/memcheck_api.h" + +void HELPER(on_call)(target_ulong pc, target_ulong ret) { + memcheck_on_call(pc, ret); +} + +void HELPER(on_ret)(target_ulong ret) { + memcheck_on_ret(ret); +} +#endif // CONFIG_MEMCHECK diff --git a/target-arm/helper-android.h b/target-arm/helper-android.h new file mode 100644 index 0000000..5342d1d --- /dev/null +++ b/target-arm/helper-android.h @@ -0,0 +1,28 @@ +/* This file must be included from helper.h */ +#ifdef CONFIG_TRACE +DEF_HELPER_1(traceTicks, void, i32) +DEF_HELPER_0(traceInsn, void) +#if HOST_LONG_BITS == 32 +DEF_HELPER_2(traceBB32, void, i64, i32) +#endif +#if HOST_LONG_BITS == 64 +DEF_HELPER_2(traceBB64, void, i64, i64) +#endif +#endif + +#ifdef CONFIG_MEMCHECK +/* Hooks to translated BL/BLX. This callback is used to build thread's + * calling stack. + * Param: + * First pointer contains guest PC where BL/BLX has been found. + * Second pointer contains guest PC where BL/BLX will return. + */ +DEF_HELPER_2(on_call, void, i32, i32) +/* Hooks to return from translated BL/BLX. This callback is used to build + * thread's calling stack. + * Param: + * Pointer contains guest PC where BL/BLX will return. + */ +DEF_HELPER_1(on_ret, void, i32) +#endif // CONFIG_MEMCHECK +#include "def-helper.h" diff --git a/target-arm/helper.c b/target-arm/helper.c index f595b2c..154aa46 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -8,11 +8,8 @@ #include "helpers.h" #include "qemu-common.h" #ifdef CONFIG_TRACE -#include "trace.h" +#include "android-trace.h" #endif -#ifdef CONFIG_MEMCHECK -#include "memcheck/memcheck_api.h" -#endif // CONFIG_MEMCHECK static uint32_t cortexa8_cp15_c0_c1[8] = { 0x1031, 0x11, 0x400, 0, 0x31100003, 0x20000000, 0x01202000, 0x11 }; @@ -2634,34 +2631,6 @@ uint32_t HELPER(rsqrte_u32)(uint32_t a, CPUState *env) return float32_to_int32(tmp, s); } -#ifdef CONFIG_TRACE -#include "trace.h" -void HELPER(traceTicks)(uint32_t ticks) -{ - sim_time += ticks; -} - -void HELPER(traceInsn)(void) -{ - trace_insn_helper(); -} - -#if HOST_LONG_BITS == 32 -void HELPER(traceBB32)(uint64_t bb_num, uint32_t tb) -{ - trace_bb_helper(bb_num, (void*)tb); -} -#endif - -#if HOST_LONG_BITS == 64 -void HELPER(traceBB64)(uint64_t bb_num, uint64_t tb) -{ - trace_bb_helper(bb_num, (void*)tb); -} -#endif - -#endif /* CONFIG_TRACE */ - void HELPER(set_teecr)(CPUState *env, uint32_t val) { val &= 1; @@ -2670,13 +2639,3 @@ void HELPER(set_teecr)(CPUState *env, uint32_t val) tb_flush(env); } } - -#ifdef CONFIG_MEMCHECK -void HELPER(on_call)(target_ulong pc, target_ulong ret) { - memcheck_on_call(pc, ret); -} - -void HELPER(on_ret)(target_ulong ret) { - memcheck_on_ret(ret); -} -#endif // CONFIG_MEMCHECK diff --git a/target-arm/helpers.h b/target-arm/helpers.h index 5dd4925..bf210fe 100644 --- a/target-arm/helpers.h +++ b/target-arm/helpers.h @@ -15,17 +15,6 @@ DEF_HELPER_2(udiv, i32, i32, i32) DEF_HELPER_1(rbit, i32, i32) DEF_HELPER_1(abs, i32, i32) -#ifdef CONFIG_TRACE -DEF_HELPER_1(traceTicks, void, i32) -DEF_HELPER_0(traceInsn, void) -#if HOST_LONG_BITS == 32 -DEF_HELPER_2(traceBB32, void, i64, i32) -#endif -#if HOST_LONG_BITS == 64 -DEF_HELPER_2(traceBB64, void, i64, i64) -#endif -#endif - #define PAS_OP(pfx) \ DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \ DEF_HELPER_3(pfx ## sub8, i32, i32, i32, ptr) \ @@ -466,19 +455,4 @@ DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) DEF_HELPER_2(set_teecr, void, env, i32) -#ifdef CONFIG_MEMCHECK -/* Hooks to translated BL/BLX. This callback is used to build thread's - * calling stack. - * Param: - * First pointer contains guest PC where BL/BLX has been found. - * Second pointer contains guest PC where BL/BLX will return. - */ -DEF_HELPER_2(on_call, void, i32, i32) -/* Hooks to return from translated BL/BLX. This callback is used to build - * thread's calling stack. - * Param: - * Pointer contains guest PC where BL/BLX will return. - */ -DEF_HELPER_1(on_ret, void, i32) -#endif // CONFIG_MEMCHECK -#include "def-helper.h" +#include "helper-android.h" diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 6d8db29..bfb6be5 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -126,7 +126,7 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr) if (tb) { /* the PC is inside the translated code. It means that we have a virtual CPU fault */ - cpu_restore_state(tb, env, pc, NULL); + cpu_restore_state(tb, env, pc); } } raise_exception(env->exception_index); diff --git a/target-arm/memcheck_arm_helpers.h b/target-arm/translate-android.h index d13b89d..274c876 100644 --- a/target-arm/memcheck_arm_helpers.h +++ b/target-arm/translate-android.h @@ -1,29 +1,31 @@ -/* Copyright (C) 2007-2010 The Android Open Source Project -** -** This software is licensed under the terms of the GNU General Public -** License version 2, as published by the Free Software Foundation, and -** may be copied, distributed, and modified under those terms. -** -** 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. -*/ +/* This file must be included from target-arm/translate.c */ -/* - * Contains implementation of memcheck helper routines used by ARM's translator. - */ +/***** + ***** + ***** + ***** C O N F I G _ M E M C H E C K + ***** + ***** + *****/ -#ifndef QEMU_TARGET_ARM_MEMCHECK_ARM_HELPERS_H -#define QEMU_TARGET_ARM_MEMCHECK_ARM_HELPERS_H +#ifdef CONFIG_MEMCHECK -/* This file should compile iff qemu is built with memory checking - * configuration turned on. */ -#ifndef CONFIG_MEMCHECK -#error CONFIG_MEMCHECK is not defined. -#endif // CONFIG_MEMCHECK +/* + * Memchecker addition in this module is intended to inject qemu callback into + * translated code for each BL/BLX, as well as BL/BLX returns. These callbacks + * are used to build calling stack of the thread in order to provide better + * reporting on memory access violations. Although this may seem as something + * that may gratly impact the performance, in reality it doesn't. Overhead that + * is added by setting up callbacks and by callbacks themselves is neglectable. + * On the other hand, maintaining calling stack can indeed add some perf. + * overhead (TODO: provide solid numbers here). + * One of the things to watch out with regards to injecting callbacks, is + * consistency between intermediate code generated for execution, and for guest + * PC address calculation. If code doesn't match, a segmentation fault is + * guaranteed. + */ -#include "helpers.h" +#include "memcheck/memcheck_proc_management.h" #include "memcheck/memcheck_api.h" /* Array of return addresses detected in gen_intermediate_code_internal. */ @@ -203,4 +205,183 @@ set_on_ret(target_ulong ret) tcg_temp_free_ptr(tmp_ret); } -#endif // QEMU_TARGET_ARM_MEMCHECK_ARM_HELPERS_H + +# define ANDROID_WATCH_CALLSTACK_ARM(s) \ + if (watch_call_stack(s)) { \ + if (is_ret_address(env, s->pc)) { \ + set_on_ret(s->pc); \ + } \ + if (is_arm_bl_or_blx(insn)) { \ + set_on_call(s->pc, s->pc + 4); \ + if (!s->search_pc) { \ + register_ret_address(env, s->pc + 4); \ + } \ + } \ + } + +# define ANDROID_WATCH_CALLSTACK_THUMB(s) \ + if (watch_call_stack(s)) { \ + target_ulong ret_off; \ + if (is_ret_address(env, s->pc)) { \ + set_on_ret(s->pc); \ + } \ + if (is_thumb_bl_or_blx(insn, s->pc, &ret_off)) { \ + set_on_call(s->pc, s->pc + ret_off); \ + if (!s->search_pc) { \ + register_ret_address(env, s->pc + ret_off); \ + } \ + } \ + } + +# define ANDROID_DISAS_CONTEXT_FIELDS \ + int search_pc; + +# define ANDROID_START_CODEGEN(search_pc) \ + dc->search_pc = search_pc + + /* When memchecker is enabled, we need to keep a match between + * translated PC and guest PCs, so memchecker can quickly covert + * one to another. Note that we do that only for user mode. */ +# define ANDROID_CHECK_CODEGEN_PC(search_pc) \ + ((search_pc) || (memcheck_enabled && dc->user)) + +# define ANDROID_END_CODEGEN() \ + do { \ + if (memcheck_enabled && dc->user) { \ + j = gen_opc_ptr - gen_opc_buf; \ + lj++; \ + while (lj <= j) \ + gen_opc_instr_start[lj++] = 0; \ + } \ + } while (0) + +#else /* !CONFIG_MEMCHECK */ + +# define ANDROID_WATCH_CALLSTACK_ARM ((void)0) +# define ANDROID_WATCH_CALLSTACK_THUMB ((void)0) +# define ANDROID_DISAS_CONTEXT_FIELDS /* nothing */ +# define ANDROID_START_CODEGEN(s) ((void)(s)) +# define ANDROID_CHECK_CODEGEN_PC(s) (s) +# define ANDROID_END_CODEGEN() ((void)0) + +#endif /* !CONFIG_MEMCHECK */ + + +/***** + ***** + ***** + ***** C O N F I G _ T R A C E + ***** + ***** + *****/ + +#ifdef CONFIG_TRACE + +#include "android-trace.h" +#define gen_traceInsn() gen_helper_traceInsn() + +static void +gen_traceTicks( int count ) +{ + TCGv tmp = tcg_temp_new_i32(); + tcg_gen_movi_i32(tmp, count); + gen_helper_traceTicks(tmp); + tcg_temp_free_i32(tmp); +} + +static void +gen_traceBB( uint64_t bbNum, void* tb ) +{ +#if HOST_LONG_BITS == 32 + TCGv_i64 tmpNum = tcg_temp_new_i64(); + TCGv_i32 tmpTb = tcg_temp_new_i32(); + + tcg_gen_movi_i64(tmpNum, (int64_t)bbNum); + tcg_gen_movi_i32(tmpTb, (int32_t)tb); + gen_helper_traceBB32(tmpNum, tmpTb); + tcg_temp_free_i32(tmpTb); + tcg_temp_free_i64(tmpNum); +#elif HOST_LONG_BITS == 64 + TCGv_i64 tmpNum = tcg_temp_new_i64(); + TCGv_i64 tmpTb = tcg_temp_new_i64(); + + tcg_gen_movi_i64(tmpNum, (int64_t)bbNum); + tcg_gen_movi_i64(tmpTb, (int64_t)tb); + gen_helper_traceBB64(tmpNum, tmpTb); + tcg_temp_free_i64(tmpTb); + tcg_temp_free_i64(tmpNum); +#endif +} + +# define ANDROID_TRACE_DECLS int ticks = 0; + +# define ANDROID_TRACE_START_ARM() \ + do { \ + if (tracing) { \ + trace_add_insn(insn, 0); \ + ticks = get_insn_ticks_arm(insn); \ + gen_traceInsn(); \ + } \ + } while (0) + +# define ANDROID_TRACE_START_THUMB() \ + do { \ + if (tracing) { \ + int ticks = get_insn_ticks_thumb(insn); \ + trace_add_insn( insn_wrap_thumb(insn), 1 ); \ + gen_traceInsn(); \ + gen_traceTicks(ticks); \ + } \ + } while (0) + +# define ANDROID_TRACE_GEN_TICKS() \ + do { \ + if (tracing) { \ + } \ + } while (0) + +# define ANDROID_TRACE_GEN_SINGLE_TICK() \ + do { \ + if (tracing) { \ + gen_traceTicks(1); \ + ticks -= 1; \ + } \ + } while (0) + +# define ANDROID_TRACE_GEN_OTHER_TICKS() \ + do { \ + if (tracing && ticks > 0) { \ + gen_traceTicks(ticks); \ + } \ + } while (0) + +# define ANDROID_TRACE_START_BB() \ + do { \ + if (tracing) { \ + gen_traceBB(trace_static_bb_num(), tb); \ + trace_bb_start(dc->pc); \ + } \ + } while (0) + +# define ANDROID_TRACE_END_BB() \ + do { \ + if (tracing) { \ + trace_bb_end(); \ + } \ + } while (0) + +#else /* !CONFIG_TRACE */ + +# define ANDROID_TRACE_DECLS /* nothing */ +# define ANDROID_TRACE_START_ARM() ((void)0) +# define ANDROID_TRACE_START_THUMB() ((void)0) + +# define ANDROID_TRACE_GEN_TICKS() ((void)0) +# define ANDROID_TRACE_GEN_SINGLE_TICK() ((void)0) +# define ANDROID_TRACE_GEN_OTHER_TICKS() ((void)0) + +# define ANDROID_TRACE_START_BB() ((void)0) +# define ANDROID_TRACE_END_BB() ((void)0) + +#endif /* !CONFIG_TRACE */ + diff --git a/target-arm/translate.c b/target-arm/translate.c index f93a0cd..1e189f8 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -30,10 +30,6 @@ #include "tcg-op.h" #include "qemu-log.h" -#ifdef CONFIG_TRACE -#include "trace.h" -#endif - #include "helpers.h" #define GEN_HELPER 1 #include "helpers.h" @@ -66,39 +62,17 @@ typedef struct DisasContext { #endif #ifdef CONFIG_MEMCHECK int search_pc; -#endif // CONFIG_MEMCHECK +#endif } DisasContext; +#include "translate-android.h" + #if defined(CONFIG_USER_ONLY) #define IS_USER(s) 1 #else #define IS_USER(s) (s->user) #endif -#ifdef CONFIG_TRACE -#include "helpers.h" -#endif /* CONFIG_TRACE */ - -#ifdef CONFIG_MEMCHECK -/* - * Memchecker addition in this module is intended to inject qemu callback into - * translated code for each BL/BLX, as well as BL/BLX returns. These callbacks - * are used to build calling stack of the thread in order to provide better - * reporting on memory access violations. Although this may seem as something - * that may gratly impact the performance, in reality it doesn't. Overhead that - * is added by setting up callbacks and by callbacks themselves is neglectable. - * On the other hand, maintaining calling stack can indeed add some perf. - * overhead (TODO: provide solid numbers here). - * One of the things to watch out with regards to injecting callbacks, is - * consistency between intermediate code generated for execution, and for guest - * PC address calculation. If code doesn't match, a segmentation fault is - * guaranteed. - */ - -#include "memcheck/memcheck_proc_management.h" -#include "memcheck_arm_helpers.h" -#endif // CONFIG_MEMCHECK - /* These instructions trap after executing, so defer them until after the conditional executions state has been updated. */ #define DISAS_WFI 4 @@ -5756,50 +5730,10 @@ static void gen_logicq_cc(TCGv_i64 val) } -#ifdef CONFIG_TRACE - -#define gen_traceInsn() gen_helper_traceInsn() - -static void -gen_traceTicks( int count ) -{ - TCGv tmp = tcg_temp_new_i32(); - tcg_gen_movi_i32(tmp, count); - gen_helper_traceTicks(tmp); - tcg_temp_free_i32(tmp); -} - -static void -gen_traceBB( uint64_t bbNum, void* tb ) -{ -#if HOST_LONG_BITS == 32 - TCGv_i64 tmpNum = tcg_temp_new_i64(); - TCGv_i32 tmpTb = tcg_temp_new_i32(); - - tcg_gen_movi_i64(tmpNum, (int64_t)bbNum); - tcg_gen_movi_i32(tmpTb, (int32_t)tb); - gen_helper_traceBB32(tmpNum, tmpTb); - tcg_temp_free_i32(tmpTb); - tcg_temp_free_i64(tmpNum); -#elif HOST_LONG_BITS == 64 - TCGv_i64 tmpNum = tcg_temp_new_i64(); - TCGv_i64 tmpTb = tcg_temp_new_i64(); - - tcg_gen_movi_i64(tmpNum, (int64_t)bbNum); - tcg_gen_movi_i64(tmpTb, (int64_t)tb); - gen_helper_traceBB64(tmpNum, tmpTb); - tcg_temp_free_i64(tmpTb); - tcg_temp_free_i64(tmpNum); -#endif -} -#endif /* CONFIG_TRACE */ - static void disas_arm_insn(CPUState * env, DisasContext *s) { unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh; -#ifdef CONFIG_TRACE - int ticks = 0; -#endif + ANDROID_TRACE_DECLS TCGv tmp; TCGv tmp2; TCGv tmp3; @@ -5807,27 +5741,9 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) TCGv_i64 tmp64; insn = ldl_code(s->pc); -#ifdef CONFIG_MEMCHECK - if (watch_call_stack(s)) { - if (is_ret_address(env, s->pc)) { - set_on_ret(s->pc); - } - if (is_arm_bl_or_blx(insn)) { - set_on_call(s->pc, s->pc + 4); - if (!s->search_pc) { - register_ret_address(env, s->pc + 4); - } - } - } -#endif // CONFIG_MEMCHECK + ANDROID_WATCH_CALLSTACK_ARM(s); -#ifdef CONFIG_TRACE - if (tracing) { - trace_add_insn(insn, 0); - ticks = get_insn_ticks_arm(insn); - gen_traceInsn(); - } -#endif + ANDROID_TRACE_START_ARM(); s->pc += 4; @@ -5836,11 +5752,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) goto illegal_op; cond = insn >> 28; if (cond == 0xf){ -#ifdef CONFIG_TRACE - if (tracing) { - gen_traceTicks(ticks); - } -#endif + ANDROID_TRACE_GEN_TICKS(); /* Unconditional instructions. */ if (((insn >> 25) & 7) == 1) { /* NEON Data processing. */ @@ -6028,25 +5940,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) goto illegal_op; } if (cond != 0xe) { -#ifdef CONFIG_TRACE - if (tracing) { - /* a non-executed conditional instruction takes */ - /* only 1 cycle */ - gen_traceTicks(1); - ticks -= 1; - } -#endif + ANDROID_TRACE_GEN_SINGLE_TICK(); /* if not always execute, we generate a conditional jump to next instruction */ s->condlabel = gen_new_label(); gen_test_cc(cond ^ 1, s->condlabel); s->condjmp = 1; } -#ifdef CONFIG_TRACE - if (tracing && ticks > 0) { - gen_traceTicks(ticks); - } -#endif + ANDROID_TRACE_GEN_OTHER_TICKS(); if ((insn & 0x0f900000) == 0x03000000) { if ((insn & (1 << 21)) == 0) { ARCH(6T2); @@ -7198,14 +7099,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1) } insn = lduw_code(s->pc); -#ifdef CONFIG_TRACE - if (tracing) { - int ticks = get_insn_ticks_thumb(insn); - trace_add_insn( insn_wrap_thumb(insn), 1 ); - gen_traceInsn(); - gen_traceTicks(ticks); - } -#endif + ANDROID_TRACE_START_THUMB(); insn |= (uint32_t)insn_hw1 << 16; @@ -8188,29 +8082,10 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s) insn = lduw_code(s->pc); -#ifdef CONFIG_MEMCHECK - if (watch_call_stack(s)) { - target_ulong ret_off; - if (is_ret_address(env, s->pc)) { - set_on_ret(s->pc); - } - if (is_thumb_bl_or_blx(insn, s->pc, &ret_off)) { - set_on_call(s->pc, s->pc + ret_off); - if (!s->search_pc) { - register_ret_address(env, s->pc + ret_off); - } - } - } -#endif // CONFIG_MEMCHECK - -#ifdef CONFIG_TRACE - if (tracing) { - int ticks = get_insn_ticks_thumb(insn); - trace_add_insn( insn_wrap_thumb(insn), 1 ); - gen_traceInsn(); - gen_traceTicks(ticks); - } -#endif + ANDROID_WATCH_CALLSTACK_THUMB(s); + + ANDROID_TRACE_START_THUMB(); + s->pc += 2; switch (insn >> 12) { @@ -8888,9 +8763,7 @@ static inline void gen_intermediate_code_internal(CPUState *env, dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR; } #endif -#ifdef CONFIG_MEMCHECK - dc->search_pc = search_pc; -#endif // CONFIG_MEMCHECK + ANDROID_START_CODEGEN(search_pc); cpu_F0s = tcg_temp_new_i32(); cpu_F1s = tcg_temp_new_i32(); cpu_F0d = tcg_temp_new_i64(); @@ -8907,12 +8780,7 @@ static inline void gen_intermediate_code_internal(CPUState *env, max_insns = CF_COUNT_MASK; gen_icount_start(); -#ifdef CONFIG_TRACE - if (tracing) { - gen_traceBB(trace_static.bb_num, tb); - trace_bb_start(dc->pc); - } -#endif + ANDROID_TRACE_START_BB(); do { #ifdef CONFIG_USER_ONLY @@ -8950,14 +8818,7 @@ static inline void gen_intermediate_code_internal(CPUState *env, } } -#ifdef CONFIG_MEMCHECK - /* When memchecker is enabled, we need to keep a match between - * translated PC and guest PCs, so memchecker can quickly covert - * one to another. Note that we do that only for user mode. */ - if (search_pc || (memcheck_enabled && dc->user)) { -#else // CONFIG_MEMCHECK - if (search_pc) { -#endif // CONFIG_MEMCHECK + if (ANDROID_CHECK_CODEGEN_PC(search_pc)) { j = gen_opc_ptr - gen_opc_buf; if (lj < j) { lj++; @@ -9007,11 +8868,7 @@ static inline void gen_intermediate_code_internal(CPUState *env, dc->pc < next_page_start && num_insns < max_insns); -#ifdef CONFIG_TRACE - if (tracing) { - trace_bb_end(); - } -#endif + ANDROID_TRACE_END_BB(); if (tb->cflags & CF_LAST_IO) { if (dc->condjmp) { @@ -9104,14 +8961,7 @@ done_generating: while (lj <= j) gen_opc_instr_start[lj++] = 0; } else { -#ifdef CONFIG_MEMCHECK - if (memcheck_enabled && dc->user) { - j = gen_opc_ptr - gen_opc_buf; - lj++; - while (lj <= j) - gen_opc_instr_start[lj++] = 0; - } -#endif // CONFIG_MEMCHECK + ANDROID_END_CODEGEN(); tb->size = dc->pc - pc_start; tb->icount = num_insns; } @@ -9185,8 +9035,7 @@ void cpu_dump_state(CPUState *env, FILE *f, #endif } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { env->regs[15] = gen_opc_pc[pc_pos]; } diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c index 7a7829a..0815482 100644 --- a/target-i386/op_helper.c +++ b/target-i386/op_helper.c @@ -1891,7 +1891,7 @@ void helper_cmpxchg8b(target_ulong a0) eflags |= CC_Z; } else { /* always do the store */ - stq(a0, d); + stq(a0, d); EDX = (uint32_t)(d >> 32); EAX = (uint32_t)d; eflags &= ~CC_Z; @@ -1916,8 +1916,8 @@ void helper_cmpxchg16b(target_ulong a0) eflags |= CC_Z; } else { /* always do the store */ - stq(a0, d0); - stq(a0 + 8, d1); + stq(a0, d0); + stq(a0 + 8, d1); EDX = d1; EAX = d0; eflags &= ~CC_Z; @@ -2309,7 +2309,7 @@ void helper_lcall_real(int new_cs, target_ulong new_eip1, } /* protected mode call */ -void helper_lcall_protected(int new_cs, target_ulong new_eip, +void helper_lcall_protected(int new_cs, target_ulong new_eip, int shift, int next_eip_addend) { int new_stack, i; @@ -3009,7 +3009,7 @@ void helper_rdpmc(void) raise_exception(EXCP0D_GPF); } helper_svm_check_intercept_param(SVM_EXIT_RDPMC, 0); - + /* currently unimplemented */ raise_exception_err(EXCP06_ILLOP, 0); } @@ -4393,7 +4393,7 @@ void helper_fxsave(target_ulong ptr, int data64) if (data64) { stq(ptr + 0x08, 0); /* rip */ stq(ptr + 0x10, 0); /* rdp */ - } else + } else #endif { stl(ptr + 0x08, 0); /* eip */ @@ -4688,7 +4688,7 @@ void helper_hlt(int next_eip_addend) { helper_svm_check_intercept_param(SVM_EXIT_HLT, 0); EIP += next_eip_addend; - + do_hlt(); } @@ -4848,7 +4848,7 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr) if (tb) { /* the PC is inside the translated code. It means that we have a virtual CPU fault */ - cpu_restore_state(tb, env, pc, NULL); + cpu_restore_state(tb, env, pc); } } raise_exception_err(env->exception_index, env->error_code); @@ -4862,16 +4862,16 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr) #if defined(CONFIG_USER_ONLY) void helper_vmrun(int aflag, int next_eip_addend) -{ +{ } -void helper_vmmcall(void) -{ +void helper_vmmcall(void) +{ } void helper_vmload(int aflag) -{ +{ } void helper_vmsave(int aflag) -{ +{ } void helper_stgi(void) { @@ -4879,20 +4879,20 @@ void helper_stgi(void) void helper_clgi(void) { } -void helper_skinit(void) -{ +void helper_skinit(void) +{ } void helper_invlpga(int aflag) -{ +{ } -void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1) -{ +void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1) +{ } void helper_svm_check_intercept_param(uint32_t type, uint64_t param) { } -void helper_svm_check_io(uint32_t port, uint32_t param, +void helper_svm_check_io(uint32_t port, uint32_t param, uint32_t next_eip_addend) { } @@ -4901,16 +4901,16 @@ void helper_svm_check_io(uint32_t port, uint32_t param, static inline void svm_save_seg(target_phys_addr_t addr, const SegmentCache *sc) { - stw_phys(addr + offsetof(struct vmcb_seg, selector), + stw_phys(addr + offsetof(struct vmcb_seg, selector), sc->selector); - stq_phys(addr + offsetof(struct vmcb_seg, base), + stq_phys(addr + offsetof(struct vmcb_seg, base), sc->base); - stl_phys(addr + offsetof(struct vmcb_seg, limit), + stl_phys(addr + offsetof(struct vmcb_seg, limit), sc->limit); - stw_phys(addr + offsetof(struct vmcb_seg, attrib), + stw_phys(addr + offsetof(struct vmcb_seg, attrib), ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00)); } - + static inline void svm_load_seg(target_phys_addr_t addr, SegmentCache *sc) { unsigned int flags; @@ -4922,7 +4922,7 @@ static inline void svm_load_seg(target_phys_addr_t addr, SegmentCache *sc) sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12); } -static inline void svm_load_seg_cache(target_phys_addr_t addr, +static inline void svm_load_seg_cache(target_phys_addr_t addr, CPUState *env, int seg_reg) { SegmentCache sc1, *sc = &sc1; @@ -4965,13 +4965,13 @@ void helper_vmrun(int aflag, int next_eip_addend) stq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer); stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags), compute_eflags()); - svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.es), + svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.es), &env->segs[R_ES]); - svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.cs), + svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.cs), &env->segs[R_CS]); - svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ss), + svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ss), &env->segs[R_SS]); - svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ds), + svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ds), &env->segs[R_DS]); stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rip), @@ -5015,7 +5015,7 @@ void helper_vmrun(int aflag, int next_eip_addend) env->hflags2 |= HF2_HIF_MASK; } - cpu_load_efer(env, + cpu_load_efer(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.efer))); env->eflags = 0; load_eflags(ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rflags)), @@ -5159,13 +5159,13 @@ void helper_vmsave(int aflag) addr, ldq_phys(addr + offsetof(struct vmcb, save.fs.base)), env->segs[R_FS].base); - svm_save_seg(addr + offsetof(struct vmcb, save.fs), + svm_save_seg(addr + offsetof(struct vmcb, save.fs), &env->segs[R_FS]); - svm_save_seg(addr + offsetof(struct vmcb, save.gs), + svm_save_seg(addr + offsetof(struct vmcb, save.gs), &env->segs[R_GS]); - svm_save_seg(addr + offsetof(struct vmcb, save.tr), + svm_save_seg(addr + offsetof(struct vmcb, save.tr), &env->tr); - svm_save_seg(addr + offsetof(struct vmcb, save.ldtr), + svm_save_seg(addr + offsetof(struct vmcb, save.ldtr), &env->ldt); #ifdef TARGET_X86_64 @@ -5203,7 +5203,7 @@ void helper_invlpga(int aflag) { target_ulong addr; helper_svm_check_intercept_param(SVM_EXIT_INVLPGA, 0); - + if (aflag == 2) addr = EAX; else @@ -5282,7 +5282,7 @@ void helper_svm_check_intercept_param(uint32_t type, uint64_t param) } } -void helper_svm_check_io(uint32_t port, uint32_t param, +void helper_svm_check_io(uint32_t port, uint32_t param, uint32_t next_eip_addend) { if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) { @@ -5291,7 +5291,7 @@ void helper_svm_check_io(uint32_t port, uint32_t param, uint16_t mask = (1 << ((param >> 4) & 7)) - 1; if(lduw_phys(addr + port / 8) & (mask << (port & 7))) { /* next EIP */ - stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), + stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), env->eip + next_eip_addend); helper_vmexit(SVM_EXIT_IOIO, param | (port << 16)); } @@ -5316,13 +5316,13 @@ void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1) } /* Save the VM state in the vmcb */ - svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.es), + svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.es), &env->segs[R_ES]); - svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.cs), + svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.cs), &env->segs[R_CS]); - svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ss), + svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ss), &env->segs[R_SS]); - svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ds), + svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ds), &env->segs[R_DS]); stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), env->gdt.base); @@ -5371,7 +5371,7 @@ void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1) cpu_x86_update_cr3(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr3))); /* we need to set the efer after the crs so the hidden flags get set properly */ - cpu_load_efer(env, + cpu_load_efer(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer))); env->eflags = 0; load_eflags(ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags)), @@ -5504,7 +5504,7 @@ target_ulong helper_bsr(target_ulong t0) { int count; target_ulong res, mask; - + res = t0; count = TARGET_LONG_BITS - 1; mask = (target_ulong)1 << (TARGET_LONG_BITS - 1); diff --git a/target-i386/ops_sse_header.h b/target-i386/ops_sse_header.h index 03e9b17..20d47bb 100644 --- a/target-i386/ops_sse_header.h +++ b/target-i386/ops_sse_header.h @@ -31,6 +31,9 @@ #define dh_ctype_Reg Reg * #define dh_ctype_XMMReg XMMReg * #define dh_ctype_MMXReg MMXReg * +#define dh_is_signed_Reg dh_is_signed_ptr +#define dh_is_signed_XMMReg dh_is_signed_ptr +#define dh_is_signed_MMXReg dh_is_signed_ptr DEF_HELPER_2(glue(psrlw, SUFFIX), void, Reg, Reg) DEF_HELPER_2(glue(psraw, SUFFIX), void, Reg, Reg) diff --git a/target-i386/translate.c b/target-i386/translate.c index b50f0a9..f4e295f 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -380,7 +380,7 @@ static inline void gen_op_addq_A0_im(int64_t val) tcg_gen_addi_tl(cpu_A0, cpu_A0, val); } #endif - + static void gen_add_A0_im(DisasContext *s, int val) { #ifdef TARGET_X86_64 @@ -461,7 +461,7 @@ static inline void gen_op_set_cc_op(int32_t val) static inline void gen_op_addl_A0_reg_sN(int shift, int reg) { tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUState, regs[reg])); - if (shift != 0) + if (shift != 0) tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, shift); tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); #ifdef TARGET_X86_64 @@ -503,7 +503,7 @@ static inline void gen_op_movq_A0_reg(int reg) static inline void gen_op_addq_A0_reg_sN(int shift, int reg) { tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUState, regs[reg])); - if (shift != 0) + if (shift != 0) tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, shift); tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); } @@ -660,7 +660,7 @@ static inline void gen_string_movl_A0_EDI(DisasContext *s) } } -static inline void gen_op_movl_T0_Dshift(int ot) +static inline void gen_op_movl_T0_Dshift(int ot) { tcg_gen_ld32s_tl(cpu_T[0], cpu_env, offsetof(CPUState, df)); tcg_gen_shli_tl(cpu_T[0], cpu_T[0], ot); @@ -952,7 +952,7 @@ static inline void gen_jcc1(DisasContext *s, int cc_op, int b, int l1) case CC_OP_SUBW: case CC_OP_SUBL: case CC_OP_SUBQ: - + size = cc_op - CC_OP_SUBB; switch(jcc_op) { case JCC_Z: @@ -983,28 +983,28 @@ static inline void gen_jcc1(DisasContext *s, int cc_op, int b, int l1) switch(size) { case 0: tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0x80); - tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0, + tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0, 0, l1); break; case 1: tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0x8000); - tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0, + tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0, 0, l1); break; #ifdef TARGET_X86_64 case 2: tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0x80000000); - tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0, + tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0, 0, l1); break; #endif default: - tcg_gen_brcondi_tl(inv ? TCG_COND_GE : TCG_COND_LT, cpu_cc_dst, + tcg_gen_brcondi_tl(inv ? TCG_COND_GE : TCG_COND_LT, cpu_cc_dst, 0, l1); break; } break; - + case JCC_B: cond = inv ? TCG_COND_GEU : TCG_COND_LTU; goto fast_jcc_b; @@ -1036,7 +1036,7 @@ static inline void gen_jcc1(DisasContext *s, int cc_op, int b, int l1) } tcg_gen_brcond_tl(cond, cpu_tmp4, t0, l1); break; - + case JCC_L: cond = inv ? TCG_COND_GE : TCG_COND_LT; goto fast_jcc_l; @@ -1068,48 +1068,48 @@ static inline void gen_jcc1(DisasContext *s, int cc_op, int b, int l1) } tcg_gen_brcond_tl(cond, cpu_tmp4, t0, l1); break; - + default: goto slow_jcc; } break; - + /* some jumps are easy to compute */ case CC_OP_ADDB: case CC_OP_ADDW: case CC_OP_ADDL: case CC_OP_ADDQ: - + case CC_OP_ADCB: case CC_OP_ADCW: case CC_OP_ADCL: case CC_OP_ADCQ: - + case CC_OP_SBBB: case CC_OP_SBBW: case CC_OP_SBBL: case CC_OP_SBBQ: - + case CC_OP_LOGICB: case CC_OP_LOGICW: case CC_OP_LOGICL: case CC_OP_LOGICQ: - + case CC_OP_INCB: case CC_OP_INCW: case CC_OP_INCL: case CC_OP_INCQ: - + case CC_OP_DECB: case CC_OP_DECW: case CC_OP_DECL: case CC_OP_DECQ: - + case CC_OP_SHLB: case CC_OP_SHLW: case CC_OP_SHLL: case CC_OP_SHLQ: - + case CC_OP_SARB: case CC_OP_SARW: case CC_OP_SARL: @@ -1128,7 +1128,7 @@ static inline void gen_jcc1(DisasContext *s, int cc_op, int b, int l1) default: slow_jcc: gen_setcc_slow_T0(s, jcc_op); - tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, + tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_T[0], 0, l1); break; } @@ -1420,7 +1420,7 @@ static void gen_inc(DisasContext *s1, int ot, int d, int c) tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); } -static void gen_shift_rm_T1(DisasContext *s, int ot, int op1, +static void gen_shift_rm_T1(DisasContext *s, int ot, int op1, int is_right, int is_arith) { target_ulong mask; @@ -1462,7 +1462,7 @@ static void gen_shift_rm_T1(DisasContext *s, int ot, int op1, gen_op_st_T0_A0(ot + s->mem_index); else gen_op_mov_reg_T0(ot, op1); - + /* update eflags if non zero shift */ if (s->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(s->cc_op); @@ -1483,7 +1483,7 @@ static void gen_shift_rm_T1(DisasContext *s, int ot, int op1, tcg_gen_movi_i32(cpu_cc_op, CC_OP_SARB + ot); else tcg_gen_movi_i32(cpu_cc_op, CC_OP_SHLB + ot); - + gen_set_label(shift_label); s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ @@ -1495,7 +1495,7 @@ static void gen_shift_rm_im(DisasContext *s, int ot, int op1, int op2, int is_right, int is_arith) { int mask; - + if (ot == OT_QUAD) mask = 0x3f; else @@ -1530,7 +1530,7 @@ static void gen_shift_rm_im(DisasContext *s, int ot, int op1, int op2, gen_op_st_T0_A0(ot + s->mem_index); else gen_op_mov_reg_T0(ot, op1); - + /* update eflags if non zero shift */ if (op2 != 0) { tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4); @@ -1550,7 +1550,7 @@ static inline void tcg_gen_lshift(TCGv ret, TCGv arg1, target_long arg2) tcg_gen_shri_tl(ret, arg1, -arg2); } -static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, +static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, int is_right) { target_ulong mask; @@ -1584,12 +1584,12 @@ static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, shifts. */ label1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, label1); - + if (ot <= OT_WORD) tcg_gen_andi_tl(cpu_tmp0, t1, (1 << (3 + ot)) - 1); else tcg_gen_mov_tl(cpu_tmp0, t1); - + gen_extu(ot, t0); tcg_gen_mov_tl(t2, t0); @@ -1614,7 +1614,7 @@ static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, } else { gen_op_mov_reg_v(ot, op1, t0); } - + /* update eflags */ if (s->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(s->cc_op); @@ -1633,10 +1633,10 @@ static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, } tcg_gen_andi_tl(t0, t0, CC_C); tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t0); - + tcg_gen_discard_tl(cpu_cc_dst); tcg_gen_movi_i32(cpu_cc_op, CC_OP_EFLAGS); - + gen_set_label(label2); s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ @@ -1724,7 +1724,7 @@ static void gen_rot_rm_im(DisasContext *s, int ot, int op1, int op2, } /* XXX: add faster immediate = 1 case */ -static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1, +static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1, int is_right) { int label1; @@ -1737,7 +1737,7 @@ static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1, gen_op_ld_T0_A0(ot + s->mem_index); else gen_op_mov_TN_reg(ot, 0, op1); - + if (is_right) { switch (ot) { case 0: gen_helper_rcrb(cpu_T[0], cpu_T[0], cpu_T[1]); break; @@ -1770,13 +1770,13 @@ static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1, tcg_gen_mov_tl(cpu_cc_src, cpu_cc_tmp); tcg_gen_discard_tl(cpu_cc_dst); tcg_gen_movi_i32(cpu_cc_op, CC_OP_EFLAGS); - + gen_set_label(label1); s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ } /* XXX: add faster immediate case */ -static void gen_shiftd_rm_T1_T3(DisasContext *s, int ot, int op1, +static void gen_shiftd_rm_T1_T3(DisasContext *s, int ot, int op1, int is_right) { int label1, label2, data_bits; @@ -1810,7 +1810,7 @@ static void gen_shiftd_rm_T1_T3(DisasContext *s, int ot, int op1, shifts. */ label1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, label1); - + tcg_gen_addi_tl(cpu_tmp5, t2, -1); if (ot == OT_WORD) { /* Note: we implement the Intel behaviour for shift count > 16 */ @@ -1821,7 +1821,7 @@ static void gen_shiftd_rm_T1_T3(DisasContext *s, int ot, int op1, tcg_gen_ext32u_tl(t0, t0); tcg_gen_shr_tl(cpu_tmp4, t0, cpu_tmp5); - + /* only needed if count > 16, but a test would complicate */ tcg_gen_sub_tl(cpu_tmp5, tcg_const_tl(32), t2); tcg_gen_shl_tl(cpu_tmp0, t0, cpu_tmp5); @@ -1835,7 +1835,7 @@ static void gen_shiftd_rm_T1_T3(DisasContext *s, int ot, int op1, tcg_gen_shli_tl(t1, t1, 16); tcg_gen_or_tl(t1, t1, t0); tcg_gen_ext32u_tl(t1, t1); - + tcg_gen_shl_tl(cpu_tmp4, t0, cpu_tmp5); tcg_gen_sub_tl(cpu_tmp0, tcg_const_tl(32), cpu_tmp5); tcg_gen_shr_tl(cpu_tmp6, t1, cpu_tmp0); @@ -1858,13 +1858,13 @@ static void gen_shiftd_rm_T1_T3(DisasContext *s, int ot, int op1, tcg_gen_sub_tl(cpu_tmp5, tcg_const_tl(data_bits), t2); tcg_gen_shl_tl(t1, t1, cpu_tmp5); tcg_gen_or_tl(t0, t0, t1); - + } else { if (ot == OT_LONG) tcg_gen_ext32u_tl(t1, t1); tcg_gen_shl_tl(cpu_tmp4, t0, cpu_tmp5); - + tcg_gen_shl_tl(t0, t0, t2); tcg_gen_sub_tl(cpu_tmp5, tcg_const_tl(data_bits), t2); tcg_gen_shr_tl(t1, t1, cpu_tmp5); @@ -1880,7 +1880,7 @@ static void gen_shiftd_rm_T1_T3(DisasContext *s, int ot, int op1, } else { gen_op_mov_reg_v(ot, op1, t0); } - + /* update eflags */ if (s->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(s->cc_op); @@ -2315,7 +2315,7 @@ static inline void gen_jcc(DisasContext *s, int b, if (s->jmp_opt) { l1 = gen_new_label(); gen_jcc1(s, cc_op, b, l1); - + gen_goto_tb(s, 0, next_eip); gen_set_label(l1); @@ -2368,17 +2368,17 @@ static void gen_setcc(DisasContext *s, int b) static inline void gen_op_movl_T0_seg(int seg_reg) { - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, + tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,segs[seg_reg].selector)); } static inline void gen_op_movl_seg_T0_vm(int seg_reg) { tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffff); - tcg_gen_st32_tl(cpu_T[0], cpu_env, + tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,segs[seg_reg].selector)); tcg_gen_shli_tl(cpu_T[0], cpu_T[0], 4); - tcg_gen_st_tl(cpu_T[0], cpu_env, + tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,segs[seg_reg].base)); } @@ -2681,7 +2681,7 @@ static void gen_interrupt(DisasContext *s, int intno, if (s->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(s->cc_op); gen_jmp_im(cur_eip); - gen_helper_raise_interrupt(tcg_const_i32(intno), + gen_helper_raise_interrupt(tcg_const_i32(intno), tcg_const_i32(next_eip - cur_eip)); s->is_jmp = 3; } @@ -3175,7 +3175,7 @@ static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r) #endif { gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 0); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_movl_mm_T0_mmx(cpu_ptr0, cpu_tmp2_i32); @@ -3185,14 +3185,14 @@ static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r) #ifdef TARGET_X86_64 if (s->dflag == 2) { gen_ldst_modrm(s, modrm, OT_QUAD, OR_TMP0, 0); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[reg])); gen_helper_movq_mm_T0_xmm(cpu_ptr0, cpu_T[0]); } else #endif { gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 0); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[reg])); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_movl_mm_T0_xmm(cpu_ptr0, cpu_tmp2_i32); @@ -3324,13 +3324,13 @@ static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r) case 0x7e: /* movd ea, mm */ #ifdef TARGET_X86_64 if (s->dflag == 2) { - tcg_gen_ld_i64(cpu_T[0], cpu_env, + tcg_gen_ld_i64(cpu_T[0], cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); gen_ldst_modrm(s, modrm, OT_QUAD, OR_TMP0, 1); } else #endif { - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, + tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,fpregs[reg].mmx.MMX_L(0))); gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 1); } @@ -3338,13 +3338,13 @@ static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r) case 0x17e: /* movd ea, xmm */ #ifdef TARGET_X86_64 if (s->dflag == 2) { - tcg_gen_ld_i64(cpu_T[0], cpu_env, + tcg_gen_ld_i64(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); gen_ldst_modrm(s, modrm, OT_QUAD, OR_TMP0, 1); } else #endif { - tcg_gen_ld32u_tl(cpu_T[0], cpu_env, + tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 1); } @@ -3460,7 +3460,7 @@ static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r) break; case 0x050: /* movmskps */ rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[rm])); gen_helper_movmskps(cpu_tmp2_i32, cpu_ptr0); tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); @@ -3468,7 +3468,7 @@ static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r) break; case 0x150: /* movmskpd */ rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[rm])); gen_helper_movmskpd(cpu_tmp2_i32, cpu_ptr0); tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); @@ -4605,12 +4605,12 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_jmp_im(pc_start - s->cs_base); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_lcall_protected(cpu_tmp2_i32, cpu_T[1], - tcg_const_i32(dflag), + tcg_const_i32(dflag), tcg_const_i32(s->pc - pc_start)); } else { tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_lcall_real(cpu_tmp2_i32, cpu_T[1], - tcg_const_i32(dflag), + tcg_const_i32(dflag), tcg_const_i32(s->pc - s->cs_base)); } gen_eob(s); @@ -4877,7 +4877,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_lea_modrm(s, modrm, ®_addr, &offset_addr); gen_helper_cmpxchg16b(cpu_A0); } else -#endif +#endif { if (!(s->cpuid_features & CPUID_CX8)) goto illegal_op; @@ -5334,7 +5334,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) case 0xc1: /* shift Ev,Ib */ shift = 2; - grp2: + GRP2: { if ((b & 1) == 0) ot = OT_BYTE; @@ -5370,12 +5370,12 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) case 0xd1: /* shift Ev,1 */ shift = 1; - goto grp2; + goto GRP2; case 0xd2: case 0xd3: /* shift Ev,cl */ shift = 0; - goto grp2; + goto GRP2; case 0x1a4: /* shld imm */ op = 0; @@ -5452,7 +5452,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_helper_fildl_FT0(cpu_tmp2_i32); break; case 2: - tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, (s->mem_index >> 2) - 1); gen_helper_fldl_FT0(cpu_tmp1_i64); break; @@ -5491,7 +5491,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_helper_fildl_ST0(cpu_tmp2_i32); break; case 2: - tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, (s->mem_index >> 2) - 1); gen_helper_fldl_ST0(cpu_tmp1_i64); break; @@ -5513,7 +5513,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) break; case 2: gen_helper_fisttll_ST0(cpu_tmp1_i64); - tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, (s->mem_index >> 2) - 1); break; case 3: @@ -5539,7 +5539,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) break; case 2: gen_helper_fstl_ST0(cpu_tmp1_i64); - tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, (s->mem_index >> 2) - 1); break; case 3: @@ -5621,13 +5621,13 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_helper_fpop(); break; case 0x3d: /* fildll */ - tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, (s->mem_index >> 2) - 1); gen_helper_fildll_ST0(cpu_tmp1_i64); break; case 0x3f: /* fistpll */ gen_helper_fistll_ST0(cpu_tmp1_i64); - tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, (s->mem_index >> 2) - 1); gen_helper_fpop(); break; @@ -6015,7 +6015,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) ot = dflag ? OT_LONG : OT_WORD; gen_op_mov_TN_reg(OT_WORD, 0, R_EDX); gen_op_andl_T0_ffff(); - gen_check_io(s, ot, pc_start - s->cs_base, + gen_check_io(s, ot, pc_start - s->cs_base, SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes) | 4); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); @@ -6206,7 +6206,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) if (s->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(s->cc_op); gen_jmp_im(pc_start - s->cs_base); - gen_helper_iret_protected(tcg_const_i32(s->dflag), + gen_helper_iret_protected(tcg_const_i32(s->dflag), tcg_const_i32(s->pc - s->cs_base)); s->cc_op = CC_OP_EFLAGS; } @@ -7090,7 +7090,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) break; case 4: /* STGI */ if ((!(s->flags & HF_SVME_MASK) && - !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) || + !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) || !s->pe) goto illegal_op; if (s->cpl != 0) { @@ -7111,8 +7111,8 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) } break; case 6: /* SKINIT */ - if ((!(s->flags & HF_SVME_MASK) && - !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) || + if ((!(s->flags & HF_SVME_MASK) && + !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) || !s->pe) goto illegal_op; gen_helper_skinit(); @@ -7787,8 +7787,7 @@ void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) gen_intermediate_code_internal(env, tb, 1); } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { int cc_op; #ifdef DEBUG_DISAS @@ -7800,8 +7799,8 @@ void gen_pc_load(CPUState *env, TranslationBlock *tb, qemu_log("0x%04x: " TARGET_FMT_lx "\n", i, gen_opc_pc[i]); } } - qemu_log("spc=0x%08lx pc_pos=0x%x eip=" TARGET_FMT_lx " cs_base=%x\n", - searched_pc, pc_pos, gen_opc_pc[pc_pos] - tb->cs_base, + qemu_log("pc_pos=0x%x eip=" TARGET_FMT_lx " cs_base=%x\n", + pc_pos, gen_opc_pc[pc_pos] - tb->cs_base, (uint32_t)tb->cs_base); } #endif @@ -75,10 +75,13 @@ destroyed, but local temporaries and globals are preserved. * Helpers: Using the tcg_gen_helper_x_y it is possible to call any function -taking i32, i64 or pointer types. Before calling an helper, all -globals are stored at their canonical location and it is assumed that -the function can modify them. In the future, function modifiers will -be allowed to tell that the helper does not read or write some globals. +taking i32, i64 or pointer types. By default, before calling a helper, +all globals are stored at their canonical location and it is assumed +that the function can modify them. This can be overridden by the +TCG_CALL_CONST function modifier. By default, the helper is allowed to +modify the CPU state or raise an exception. This can be overridden by +the TCG_CALL_PURE function modifier, in which case the call to the +function is removed if the return value is not used. On some TCG targets (e.g. x86), several calling conventions are supported. @@ -210,7 +213,7 @@ t0=t1&~t2 * eqv_i32/i64 t0, t1, t2 -t0=~(t1^t2) +t0=~(t1^t2), or equivalently, t0=t1^~t2 * nand_i32/i64 t0, t1, t2 @@ -265,13 +268,13 @@ ext32u_i64 t0, t1 * bswap16_i32/i64 t0, t1 -16 bit byte swap on a 32/64 bit value. The two/six high order bytes must be -set to zero. +16 bit byte swap on a 32/64 bit value. It assumes that the two/six high order +bytes are set to zero. * bswap32_i32/i64 t0, t1 -32 bit byte swap on a 32/64 bit value. With a 64 bit value, the four high -order bytes must be set to zero. +32 bit byte swap on a 32/64 bit value. With a 64 bit value, it assumes that +the four high order bytes are set to zero. * bswap64_i64 t0, t1 @@ -282,6 +285,28 @@ order bytes must be set to zero. Indicate that the value of t0 won't be used later. It is useful to force dead code elimination. +* deposit_i32/i64 dest, t1, t2, pos, len + +Deposit T2 as a bitfield into T1, placing the result in DEST. +The bitfield is described by POS/LEN, which are immediate values: + + LEN - the length of the bitfield + POS - the position of the first bit, counting from the LSB + +For example, pos=8, len=4 indicates a 4-bit field at bit 8. +This operation would be equivalent to + + dest = (t1 & ~0x0f00) | ((t2 << 8) & 0x0f00) + + +********* Conditional moves + +* setcond_i32/i64 cond, dest, t1, t2 + +dest = (t1 cond t2) + +Set DEST to 1 if (T1 cond T2) is true, otherwise set to 0. + ********* Type conversions * ext_i32_i64 t0, t1 @@ -323,9 +348,37 @@ st32_i64 t0, t1, offset write(t0, t1 + offset) Write 8, 16, 32 or 64 bits to host memory. +********* 64-bit target on 32-bit host support + +The following opcodes are internal to TCG. Thus they are to be implemented by +32-bit host code generators, but are not to be emitted by guest translators. +They are emitted as needed by inline functions within "tcg-op.h". + +* brcond2_i32 cond, t0_low, t0_high, t1_low, t1_high, label + +Similar to brcond, except that the 64-bit values T0 and T1 +are formed from two 32-bit arguments. + +* add2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high +* sub2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high + +Similar to add/sub, except that the 64-bit inputs T1 and T2 are +formed from two 32-bit arguments, and the 64-bit output T0 +is returned in two 32-bit outputs. + +* mulu2_i32 t0_low, t0_high, t1, t2 + +Similar to mul, except two 32-bit (unsigned) inputs T1 and T2 yielding +the full 64-bit product T0. The later is returned in two 32-bit outputs. + +* setcond2_i32 cond, dest, t1_low, t1_high, t2_low, t2_high + +Similar to setcond, except that the 64-bit values T1 and T2 are +formed from two 32-bit arguments. The result is a 32-bit value. + ********* QEMU specific operations -* tb_exit t0 +* exit_tb t0 Exit the current TB and return the value t0 (word type). @@ -339,13 +392,17 @@ instructions. qemu_ld8s t0, t1, flags qemu_ld16u t0, t1, flags qemu_ld16s t0, t1, flags +qemu_ld32 t0, t1, flags qemu_ld32u t0, t1, flags qemu_ld32s t0, t1, flags qemu_ld64 t0, t1, flags -Load data at the QEMU CPU address t1 into t0. t1 has the QEMU CPU -address type. 'flags' contains the QEMU memory index (selects user or -kernel access) for example. +Load data at the QEMU CPU address t1 into t0. t1 has the QEMU CPU address +type. 'flags' contains the QEMU memory index (selects user or kernel access) +for example. + +Note that "qemu_ld32" implies a 32-bit result, while "qemu_ld32u" and +"qemu_ld32s" imply a 64-bit result appropriately extended from 32 bits. * qemu_st8 t0, t1, flags qemu_st16 t0, t1, flags @@ -445,7 +502,7 @@ register. the speed of the translation. - Don't hesitate to use helpers for complicated or seldom used target - intructions. There is little performance advantage in using TCG to + instructions. There is little performance advantage in using TCG to implement target instructions taking more than about twenty TCG instructions. @@ -1,4 +1,4 @@ -- Add new instructions such as: setcond, clz, ctz, popcnt. +- Add new instructions such as: clz, ctz, popcnt. - See if it is worth exporting mul2, mulu2, div2, divu2. diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c index f8d626d..fb858d8 100644 --- a/tcg/arm/tcg-target.c +++ b/tcg/arm/tcg-target.c @@ -22,6 +22,51 @@ * THE SOFTWARE. */ +#if defined(__ARM_ARCH_7__) || \ + defined(__ARM_ARCH_7A__) || \ + defined(__ARM_ARCH_7EM__) || \ + defined(__ARM_ARCH_7M__) || \ + defined(__ARM_ARCH_7R__) +#define USE_ARMV7_INSTRUCTIONS +#endif + +#if defined(USE_ARMV7_INSTRUCTIONS) || \ + defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6K__) || \ + defined(__ARM_ARCH_6T2__) || \ + defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6ZK__) +#define USE_ARMV6_INSTRUCTIONS +#endif + +#if defined(USE_ARMV6_INSTRUCTIONS) || \ + defined(__ARM_ARCH_5T__) || \ + defined(__ARM_ARCH_5TE__) || \ + defined(__ARM_ARCH_5TEJ__) +#define USE_ARMV5_INSTRUCTIONS +#endif + +#ifdef USE_ARMV5_INSTRUCTIONS +static const int use_armv5_instructions = 1; +#else +static const int use_armv5_instructions = 0; +#endif +#undef USE_ARMV5_INSTRUCTIONS + +#ifdef USE_ARMV6_INSTRUCTIONS +static const int use_armv6_instructions = 1; +#else +static const int use_armv6_instructions = 0; +#endif +#undef USE_ARMV6_INSTRUCTIONS + +#ifdef USE_ARMV7_INSTRUCTIONS +static const int use_armv7_instructions = 1; +#else +static const int use_armv7_instructions = 0; +#endif +#undef USE_ARMV7_INSTRUCTIONS + #ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%r0", @@ -39,14 +84,11 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%r12", "%r13", "%r14", + "%pc", }; #endif static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R0, - TCG_REG_R1, - TCG_REG_R2, - TCG_REG_R3, TCG_REG_R4, TCG_REG_R5, TCG_REG_R6, @@ -55,8 +97,12 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_R9, TCG_REG_R10, TCG_REG_R11, - TCG_REG_R12, TCG_REG_R13, + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R12, TCG_REG_R14, }; @@ -67,12 +113,25 @@ static const int tcg_target_call_oarg_regs[2] = { TCG_REG_R0, TCG_REG_R1 }; +static inline void reloc_abs32(void *code_ptr, tcg_target_long target) +{ + *(uint32_t *) code_ptr = target; +} + +static inline void reloc_pc24(void *code_ptr, tcg_target_long target) +{ + uint32_t offset = ((target - ((tcg_target_long) code_ptr + 8)) >> 2); + + *(uint32_t *) code_ptr = ((*(uint32_t *) code_ptr) & ~0xffffff) + | (offset & 0xffffff); +} + static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend) { switch (type) { case R_ARM_ABS32: - *(uint32_t *) code_ptr = value; + reloc_abs32(code_ptr, value); break; case R_ARM_CALL: @@ -81,8 +140,7 @@ static void patch_reloc(uint8_t *code_ptr, int type, tcg_abort(); case R_ARM_PC24: - *(uint32_t *) code_ptr = ((*(uint32_t *) code_ptr) & 0xff000000) | - (((value - ((tcg_target_long) code_ptr + 8)) >> 2) & 0xffffff); + reloc_pc24(code_ptr, value); break; } } @@ -105,69 +163,54 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) break; case 'r': -#ifndef CONFIG_SOFTMMU - case 'd': - case 'D': - case 'x': - case 'X': -#endif ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); break; -#ifdef CONFIG_SOFTMMU - /* qemu_ld/st inputs (unless 'X', 'd' or 'D') */ - case 'x': + /* qemu_ld address */ + case 'l': ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); +#ifdef CONFIG_SOFTMMU + /* r0 and r1 will be overwritten when reading the tlb entry, + so don't use these. */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); +#endif break; - - /* qemu_ld64 data_reg */ - case 'd': + case 'L': ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - /* r1 is still needed to load data_reg2, so don't use it. */ +#ifdef CONFIG_SOFTMMU + /* r1 is still needed to load data_reg or data_reg2, + so don't use it. */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); +#endif break; - /* qemu_ld/st64 data_reg2 */ - case 'D': + /* qemu_st address & data_reg */ + case 's': ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - /* r0, r1 and optionally r2 will be overwritten by the address - * and the low word of data, so don't use these. */ + /* r0 and r1 will be overwritten when reading the tlb entry + (softmmu only) and doing the byte swapping, so don't + use these. */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); -# if TARGET_LONG_BITS == 64 - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); -# endif break; - -# if TARGET_LONG_BITS == 64 - /* qemu_ld/st addr_reg2 */ - case 'X': + /* qemu_st64 data_reg2 */ + case 'S': ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - /* r0 will be overwritten by the low word of base, so don't use it. */ + /* r0 and r1 will be overwritten when reading the tlb entry + (softmmu only) and doing the byte swapping, so don't + use these. */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); - break; -# endif +#ifdef CONFIG_SOFTMMU + /* r2 is still needed to load data_reg, so don't use it. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); #endif - - case '1': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); - break; - - case '2': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); break; default: @@ -309,6 +352,9 @@ static inline void tcg_out_b(TCGContext *s, int cond, int32_t offset) static inline void tcg_out_b_noaddr(TCGContext *s, int cond) { + /* We pay attention here to not modify the branch target by skipping + the corresponding bytes. This ensure that caches and memory are + kept coherent during retranslation. */ #ifdef HOST_WORDS_BIGENDIAN tcg_out8(s, (cond << 4) | 0x0a); s->code_ptr += 3; @@ -324,6 +370,17 @@ static inline void tcg_out_bl(TCGContext *s, int cond, int32_t offset) (((offset - 8) >> 2) & 0x00ffffff)); } +static inline void tcg_out_blx(TCGContext *s, int cond, int rn) +{ + tcg_out32(s, (cond << 28) | 0x012fff30 | rn); +} + +static inline void tcg_out_blx_imm(TCGContext *s, int32_t offset) +{ + tcg_out32(s, 0xfa000000 | ((offset & 2) << 23) | + (((offset - 8) >> 2) & 0x00ffffff)); +} + static inline void tcg_out_dat_reg(TCGContext *s, int cond, int opc, int rd, int rn, int rm, int shift) { @@ -358,42 +415,38 @@ static inline void tcg_out_dat_imm(TCGContext *s, } static inline void tcg_out_movi32(TCGContext *s, - int cond, int rd, int32_t arg) + int cond, int rd, uint32_t arg) { - int offset = (uint32_t) arg - ((uint32_t) s->code_ptr + 8); - /* TODO: This is very suboptimal, we can easily have a constant * pool somewhere after all the instructions. */ + if ((int)arg < 0 && (int)arg >= -0x100) { + tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, (~arg) & 0xff); + } else if (use_armv7_instructions) { + /* use movw/movt */ + /* movw */ + tcg_out32(s, (cond << 28) | 0x03000000 | (rd << 12) + | ((arg << 4) & 0x000f0000) | (arg & 0xfff)); + if (arg & 0xffff0000) { + /* movt */ + tcg_out32(s, (cond << 28) | 0x03400000 | (rd << 12) + | ((arg >> 12) & 0x000f0000) | ((arg >> 16) & 0xfff)); + } + } else { + int opc = ARITH_MOV; + int rn = 0; - if (arg < 0 && arg > -0x100) - return tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, (~arg) & 0xff); - - if (offset < 0x100 && offset > -0x100) - return offset >= 0 ? - tcg_out_dat_imm(s, cond, ARITH_ADD, rd, 15, offset) : - tcg_out_dat_imm(s, cond, ARITH_SUB, rd, 15, -offset); - -#ifdef __ARM_ARCH_7A__ - /* use movw/movt */ - /* movw */ - tcg_out32(s, (cond << 28) | 0x03000000 | (rd << 12) - | ((arg << 4) & 0x000f0000) | (arg & 0xfff)); - if (arg & 0xffff0000) - /* movt */ - tcg_out32(s, (cond << 28) | 0x03400000 | (rd << 12) - | ((arg >> 12) & 0x000f0000) | ((arg >> 16) & 0xfff)); -#else - tcg_out_dat_imm(s, cond, ARITH_MOV, rd, 0, arg & 0xff); - if (arg & 0x0000ff00) - tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd, - ((arg >> 8) & 0xff) | 0xc00); - if (arg & 0x00ff0000) - tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd, - ((arg >> 16) & 0xff) | 0x800); - if (arg & 0xff000000) - tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd, - ((arg >> 24) & 0xff) | 0x400); -#endif + do { + int i, rot; + + i = ctz32(arg) & ~1; + rot = ((32 - i) << 7) & 0xf00; + tcg_out_dat_imm(s, cond, opc, rd, rn, ((arg >> i) & 0xff) | rot); + arg &= ~(0xff << i); + + opc = ARITH_ORR; + rn = rd; + } while (arg); + } } static inline void tcg_out_mul32(TCGContext *s, @@ -409,7 +462,7 @@ static inline void tcg_out_mul32(TCGContext *s, tcg_out32(s, (cond << 28) | ( 8 << 16) | (0 << 12) | (rs << 8) | 0x90 | rm); tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, 8, SHIFT_IMM_LSL(0)); + rd, 0, TCG_REG_R8, SHIFT_IMM_LSL(0)); } } @@ -447,6 +500,101 @@ static inline void tcg_out_smull32(TCGContext *s, } } +static inline void tcg_out_ext8s(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* sxtb */ + tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_ASR(24)); + } +} + +static inline void tcg_out_ext8u(TCGContext *s, int cond, + int rd, int rn) +{ + tcg_out_dat_imm(s, cond, ARITH_AND, rd, rn, 0xff); +} + +static inline void tcg_out_ext16s(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* sxth */ + tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(16)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_ASR(16)); + } +} + +static inline void tcg_out_ext16u(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* uxth */ + tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(16)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_LSR(16)); + } +} + +static inline void tcg_out_bswap16s(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* revsh */ + tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, TCG_REG_R8, SHIFT_IMM_ASR(16)); + tcg_out_dat_reg(s, cond, ARITH_ORR, + rd, TCG_REG_R8, rn, SHIFT_IMM_LSR(8)); + } +} + +static inline void tcg_out_bswap16(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* rev16 */ + tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, TCG_REG_R8, SHIFT_IMM_LSR(16)); + tcg_out_dat_reg(s, cond, ARITH_ORR, + rd, TCG_REG_R8, rn, SHIFT_IMM_LSR(8)); + } +} + +static inline void tcg_out_bswap32(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* rev */ + tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_EOR, + TCG_REG_R8, rn, rn, SHIFT_IMM_ROR(16)); + tcg_out_dat_imm(s, cond, ARITH_BIC, + TCG_REG_R8, TCG_REG_R8, 0xff | 0x800); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_ROR(8)); + tcg_out_dat_reg(s, cond, ARITH_EOR, + rd, rd, TCG_REG_R8, SHIFT_IMM_LSR(8)); + } +} + static inline void tcg_out_ld32_12(TCGContext *s, int cond, int rd, int rn, tcg_target_long im) { @@ -511,7 +659,7 @@ static inline void tcg_out_ld16u_8(TCGContext *s, int cond, (((-im) & 0xf0) << 4) | ((-im) & 0xf)); } -static inline void tcg_out_st16u_8(TCGContext *s, int cond, +static inline void tcg_out_st16_8(TCGContext *s, int cond, int rd, int rn, tcg_target_long im) { if (im >= 0) @@ -531,7 +679,7 @@ static inline void tcg_out_ld16u_r(TCGContext *s, int cond, (rn << 16) | (rd << 12) | rm); } -static inline void tcg_out_st16u_r(TCGContext *s, int cond, +static inline void tcg_out_st16_r(TCGContext *s, int cond, int rd, int rn, int rm) { tcg_out32(s, (cond << 28) | 0x018000b0 | @@ -551,19 +699,6 @@ static inline void tcg_out_ld16s_8(TCGContext *s, int cond, (((-im) & 0xf0) << 4) | ((-im) & 0xf)); } -static inline void tcg_out_st16s_8(TCGContext *s, int cond, - int rd, int rn, tcg_target_long im) -{ - if (im >= 0) - tcg_out32(s, (cond << 28) | 0x01c000f0 | - (rn << 16) | (rd << 12) | - ((im & 0xf0) << 4) | (im & 0xf)); - else - tcg_out32(s, (cond << 28) | 0x014000f0 | - (rn << 16) | (rd << 12) | - (((-im) & 0xf0) << 4) | ((-im) & 0xf)); -} - static inline void tcg_out_ld16s_r(TCGContext *s, int cond, int rd, int rn, int rm) { @@ -571,13 +706,6 @@ static inline void tcg_out_ld16s_r(TCGContext *s, int cond, (rn << 16) | (rd << 12) | rm); } -static inline void tcg_out_st16s_r(TCGContext *s, int cond, - int rd, int rn, int rm) -{ - tcg_out32(s, (cond << 28) | 0x018000f0 | - (rn << 16) | (rd << 12) | rm); -} - static inline void tcg_out_ld8_12(TCGContext *s, int cond, int rd, int rn, tcg_target_long im) { @@ -627,19 +755,6 @@ static inline void tcg_out_ld8s_8(TCGContext *s, int cond, (((-im) & 0xf0) << 4) | ((-im) & 0xf)); } -static inline void tcg_out_st8s_8(TCGContext *s, int cond, - int rd, int rn, tcg_target_long im) -{ - if (im >= 0) - tcg_out32(s, (cond << 28) | 0x01c000d0 | - (rn << 16) | (rd << 12) | - ((im & 0xf0) << 4) | (im & 0xf)); - else - tcg_out32(s, (cond << 28) | 0x014000d0 | - (rn << 16) | (rd << 12) | - (((-im) & 0xf0) << 4) | ((-im) & 0xf)); -} - static inline void tcg_out_ld8s_r(TCGContext *s, int cond, int rd, int rn, int rm) { @@ -647,13 +762,6 @@ static inline void tcg_out_ld8s_r(TCGContext *s, int cond, (rn << 16) | (rd << 12) | rm); } -static inline void tcg_out_st8s_r(TCGContext *s, int cond, - int rd, int rn, int rm) -{ - tcg_out32(s, (cond << 28) | 0x018000d0 | - (rn << 16) | (rd << 12) | rm); -} - static inline void tcg_out_ld32u(TCGContext *s, int cond, int rd, int rn, int32_t offset) { @@ -694,14 +802,14 @@ static inline void tcg_out_ld16s(TCGContext *s, int cond, tcg_out_ld16s_8(s, cond, rd, rn, offset); } -static inline void tcg_out_st16u(TCGContext *s, int cond, +static inline void tcg_out_st16(TCGContext *s, int cond, int rd, int rn, int32_t offset) { if (offset > 0xff || offset < -0xff) { tcg_out_movi32(s, cond, TCG_REG_R8, offset); - tcg_out_st16u_r(s, cond, rd, rn, TCG_REG_R8); + tcg_out_st16_r(s, cond, rd, rn, TCG_REG_R8); } else - tcg_out_st16u_8(s, cond, rd, rn, offset); + tcg_out_st16_8(s, cond, rd, rn, offset); } static inline void tcg_out_ld8u(TCGContext *s, int cond, @@ -724,7 +832,7 @@ static inline void tcg_out_ld8s(TCGContext *s, int cond, tcg_out_ld8s_8(s, cond, rd, rn, offset); } -static inline void tcg_out_st8u(TCGContext *s, int cond, +static inline void tcg_out_st8(TCGContext *s, int cond, int rd, int rn, int32_t offset) { if (offset > 0xfff || offset < -0xfff) { @@ -738,6 +846,11 @@ static inline void tcg_out_goto(TCGContext *s, int cond, uint32_t addr) { int32_t val; + if (addr & 1) { + /* goto to a Thumb destination isn't supported */ + tcg_abort(); + } + val = addr - (tcg_target_long) s->code_ptr; if (val - 8 < 0x01fffffd && val - 8 > -0x01fffffd) tcg_out_b(s, cond, val); @@ -746,60 +859,60 @@ static inline void tcg_out_goto(TCGContext *s, int cond, uint32_t addr) tcg_abort(); #else if (cond == COND_AL) { - tcg_out_ld32_12(s, COND_AL, 15, 15, -4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */ } else { tcg_out_movi32(s, cond, TCG_REG_R8, val - 8); tcg_out_dat_reg(s, cond, ARITH_ADD, - 15, 15, TCG_REG_R8, SHIFT_IMM_LSL(0)); + TCG_REG_PC, TCG_REG_PC, + TCG_REG_R8, SHIFT_IMM_LSL(0)); } #endif } } -static inline void tcg_out_call(TCGContext *s, int cond, uint32_t addr) +static inline void tcg_out_call(TCGContext *s, uint32_t addr) { int32_t val; -#ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R8, 0, 14, SHIFT_IMM_LSL(0)); -#endif - val = addr - (tcg_target_long) s->code_ptr; - if (val < 0x01fffffd && val > -0x01fffffd) - tcg_out_bl(s, cond, val); - else { + if (val - 8 < 0x02000000 && val - 8 >= -0x02000000) { + if (addr & 1) { + /* Use BLX if the target is in Thumb mode */ + if (!use_armv5_instructions) { + tcg_abort(); + } + tcg_out_blx_imm(s, val); + } else { + tcg_out_bl(s, COND_AL, val); + } + } else { #if 1 tcg_abort(); #else if (cond == COND_AL) { - tcg_out_dat_imm(s, cond, ARITH_ADD, 14, 15, 4); - tcg_out_ld32_12(s, COND_AL, 15, 15, -4); + tcg_out_dat_imm(s, cond, ARITH_ADD, TCG_REG_R14, TCG_REG_PC, 4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */ } else { tcg_out_movi32(s, cond, TCG_REG_R9, addr); - tcg_out_dat_imm(s, cond, ARITH_MOV, 14, 0, 15); + tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R14, 0, + TCG_REG_PC, SHIFT_IMM_LSL(0)); tcg_out_bx(s, cond, TCG_REG_R9); } #endif } - -#ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, TCG_REG_R8, SHIFT_IMM_LSL(0)); -#endif } static inline void tcg_out_callr(TCGContext *s, int cond, int arg) { -#ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R8, 0, 14, SHIFT_IMM_LSL(0)); -#endif - /* TODO: on ARMv5 and ARMv6 replace with tcg_out_blx(s, cond, arg); */ - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, 15, SHIFT_IMM_LSL(0)); - tcg_out_bx(s, cond, arg); -#ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, TCG_REG_R8, SHIFT_IMM_LSL(0)); -#endif + if (use_armv5_instructions) { + tcg_out_blx(s, cond, arg); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R14, 0, + TCG_REG_PC, SHIFT_IMM_LSL(0)); + tcg_out_bx(s, cond, arg); + } } static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index) @@ -809,7 +922,7 @@ static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index) if (l->has_value) tcg_out_goto(s, cond, l->u.value); else if (cond == COND_AL) { - tcg_out_ld32_12(s, COND_AL, 15, 15, -4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); tcg_out_reloc(s, s->code_ptr, R_ARM_ABS32, label_index, 31337); s->code_ptr += 4; } else { @@ -819,57 +932,6 @@ static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index) } } -static void tcg_out_div_helper(TCGContext *s, int cond, const TCGArg *args, - void *helper_div, void *helper_rem, int shift) -{ - int div_reg = args[0]; - int rem_reg = args[1]; - - /* stmdb sp!, { r0 - r3, ip, lr } */ - /* (Note that we need an even number of registers as per EABI) */ - tcg_out32(s, (cond << 28) | 0x092d500f); - - tcg_out_dat_reg(s, cond, ARITH_MOV, 0, 0, args[2], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 1, 0, args[3], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 2, 0, args[4], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 3, 0, 2, shift); - - tcg_out_call(s, cond, (uint32_t) helper_div); - tcg_out_dat_reg(s, cond, ARITH_MOV, 8, 0, 0, SHIFT_IMM_LSL(0)); - - /* ldmia sp, { r0 - r3, fp, lr } */ - tcg_out32(s, (cond << 28) | 0x089d500f); - - tcg_out_dat_reg(s, cond, ARITH_MOV, 0, 0, args[2], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 1, 0, args[3], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 2, 0, args[4], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 3, 0, 2, shift); - - tcg_out_call(s, cond, (uint32_t) helper_rem); - - tcg_out_dat_reg(s, cond, ARITH_MOV, rem_reg, 0, 0, SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, div_reg, 0, 8, SHIFT_IMM_LSL(0)); - - /* ldr r0, [sp], #4 */ - if (rem_reg != 0 && div_reg != 0) - tcg_out32(s, (cond << 28) | 0x04bd0004); - /* ldr r1, [sp], #4 */ - if (rem_reg != 1 && div_reg != 1) - tcg_out32(s, (cond << 28) | 0x04bd1004); - /* ldr r2, [sp], #4 */ - if (rem_reg != 2 && div_reg != 2) - tcg_out32(s, (cond << 28) | 0x04bd2004); - /* ldr r3, [sp], #4 */ - if (rem_reg != 3 && div_reg != 3) - tcg_out32(s, (cond << 28) | 0x04bd3004); - /* ldr ip, [sp], #4 */ - if (rem_reg != 12 && div_reg != 12) - tcg_out32(s, (cond << 28) | 0x04bdc004); - /* ldr lr, [sp], #4 */ - if (rem_reg != 14 && div_reg != 14) - tcg_out32(s, (cond << 28) | 0x04bde004); -} - #ifdef CONFIG_SOFTMMU #include "../../softmmu_defs.h" @@ -891,10 +953,9 @@ static void *qemu_st_helpers[4] = { #define TLB_SHIFT (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS) -static inline void tcg_out_qemu_ld(TCGContext *s, int cond, - const TCGArg *args, int opc) +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, data_reg, data_reg2; + int addr_reg, data_reg, data_reg2, bswap; #ifdef CONFIG_SOFTMMU int mem_index, s_bits; # if TARGET_LONG_BITS == 64 @@ -903,6 +964,11 @@ static inline void tcg_out_qemu_ld(TCGContext *s, int cond, uint32_t *label_ptr; #endif +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif data_reg = *args++; if (opc == 3) data_reg2 = *args++; @@ -924,12 +990,12 @@ static inline void tcg_out_qemu_ld(TCGContext *s, int cond, # if CPU_TLB_BITS > 8 # error # endif - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - 8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_R8, + 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); tcg_out_dat_imm(s, COND_AL, ARITH_AND, - 0, 8, CPU_TLB_SIZE - 1); - tcg_out_dat_reg(s, COND_AL, ARITH_ADD, - 0, TCG_AREG0, 0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); + TCG_REG_R0, TCG_REG_R8, CPU_TLB_SIZE - 1); + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_AREG0, + TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); /* In the * ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_read))] * below, the offset is likely to exceed 12 bits if mem_index != 0 and @@ -938,13 +1004,13 @@ static inline void tcg_out_qemu_ld(TCGContext *s, int cond, * before. */ if (mem_index) - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 0, 0, + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0, (mem_index << (TLB_SHIFT & 1)) | ((16 - (TLB_SHIFT >> 1)) << 8)); - tcg_out_ld32_12(s, COND_AL, 1, 0, + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addr_read)); - tcg_out_dat_reg(s, COND_AL, ARITH_CMP, - 0, 1, 8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R1, + TCG_REG_R8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); /* Check alignment. */ if (s_bits) tcg_out_dat_imm(s, COND_EQ, ARITH_TST, @@ -952,95 +1018,101 @@ static inline void tcg_out_qemu_ld(TCGContext *s, int cond, # if TARGET_LONG_BITS == 64 /* XXX: possibly we could use a block data load or writeback in * the first access. */ - tcg_out_ld32_12(s, COND_EQ, 1, 0, + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addr_read) + 4); - tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, - 0, 1, addr_reg2, SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + TCG_REG_R1, addr_reg2, SHIFT_IMM_LSL(0)); # endif - tcg_out_ld32_12(s, COND_EQ, 1, 0, + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addend)); switch (opc) { case 0: - tcg_out_ld8_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_ld8_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); break; case 0 | 4: - tcg_out_ld8s_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_ld8s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); break; case 1: - tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + if (bswap) { + tcg_out_bswap16(s, COND_EQ, data_reg, data_reg); + } break; case 1 | 4: - tcg_out_ld16s_r(s, COND_EQ, data_reg, addr_reg, 1); + if (bswap) { + tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + tcg_out_bswap16s(s, COND_EQ, data_reg, data_reg); + } else { + tcg_out_ld16s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + } break; case 2: default: - tcg_out_ld32_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_ld32_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + if (bswap) { + tcg_out_bswap32(s, COND_EQ, data_reg, data_reg); + } break; case 3: - tcg_out_ld32_rwb(s, COND_EQ, data_reg, 1, addr_reg); - tcg_out_ld32_12(s, COND_EQ, data_reg2, 1, 4); + if (bswap) { + tcg_out_ld32_rwb(s, COND_EQ, data_reg2, TCG_REG_R1, addr_reg); + tcg_out_ld32_12(s, COND_EQ, data_reg, TCG_REG_R1, 4); + tcg_out_bswap32(s, COND_EQ, data_reg2, data_reg2); + tcg_out_bswap32(s, COND_EQ, data_reg, data_reg); + } else { + tcg_out_ld32_rwb(s, COND_EQ, data_reg, TCG_REG_R1, addr_reg); + tcg_out_ld32_12(s, COND_EQ, data_reg2, TCG_REG_R1, 4); + } break; } label_ptr = (void *) s->code_ptr; - tcg_out_b(s, COND_EQ, 8); - -# ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 8, 0, 14, SHIFT_IMM_LSL(0)); -# endif + tcg_out_b_noaddr(s, COND_EQ); /* TODO: move this code to where the constants pool will be */ - if (addr_reg) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 0, 0, addr_reg, SHIFT_IMM_LSL(0)); + if (addr_reg != TCG_REG_R0) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R0, 0, addr_reg, SHIFT_IMM_LSL(0)); + } # if TARGET_LONG_BITS == 32 - tcg_out_dat_imm(s, cond, ARITH_MOV, 1, 0, mem_index); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R1, 0, mem_index); # else - if (addr_reg2 != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, addr_reg2, SHIFT_IMM_LSL(0)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R1, 0, addr_reg2, SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); # endif - tcg_out_bl(s, cond, (tcg_target_long) qemu_ld_helpers[s_bits] - - (tcg_target_long) s->code_ptr); + tcg_out_call(s, (tcg_target_long) qemu_ld_helpers[s_bits]); switch (opc) { case 0 | 4: - tcg_out_dat_reg(s, cond, ARITH_MOV, - 0, 0, 0, SHIFT_IMM_LSL(24)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg, 0, 0, SHIFT_IMM_ASR(24)); + tcg_out_ext8s(s, COND_AL, data_reg, TCG_REG_R0); break; case 1 | 4: - tcg_out_dat_reg(s, cond, ARITH_MOV, - 0, 0, 0, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg, 0, 0, SHIFT_IMM_ASR(16)); + tcg_out_ext16s(s, COND_AL, data_reg, TCG_REG_R0); break; case 0: case 1: case 2: default: - if (data_reg) - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg, 0, 0, SHIFT_IMM_LSL(0)); + if (data_reg != TCG_REG_R0) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0)); + } break; case 3: - if (data_reg != 0) - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg, 0, 0, SHIFT_IMM_LSL(0)); - if (data_reg2 != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg2, 0, 1, SHIFT_IMM_LSL(0)); + if (data_reg != TCG_REG_R0) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0)); + } + if (data_reg2 != TCG_REG_R1) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + data_reg2, 0, TCG_REG_R1, SHIFT_IMM_LSL(0)); + } break; } -# ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, 8, SHIFT_IMM_LSL(0)); -# endif - - *label_ptr += ((void *) s->code_ptr - (void *) label_ptr - 8) >> 2; + reloc_pc24(label_ptr, (tcg_target_long)s->code_ptr); #else /* !CONFIG_SOFTMMU */ if (GUEST_BASE) { uint32_t offset = GUEST_BASE; @@ -1051,9 +1123,9 @@ static inline void tcg_out_qemu_ld(TCGContext *s, int cond, i = ctz32(offset) & ~1; rot = ((32 - i) << 7) & 0xf00; - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 8, addr_reg, + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R8, addr_reg, ((offset >> i) & 0xff) | rot); - addr_reg = 8; + addr_reg = TCG_REG_R8; offset &= ~(0xff << i); } } @@ -1066,33 +1138,47 @@ static inline void tcg_out_qemu_ld(TCGContext *s, int cond, break; case 1: tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap16(s, COND_AL, data_reg, data_reg); + } break; case 1 | 4: - tcg_out_ld16s_8(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0); + tcg_out_bswap16s(s, COND_AL, data_reg, data_reg); + } else { + tcg_out_ld16s_8(s, COND_AL, data_reg, addr_reg, 0); + } break; case 2: default: tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap32(s, COND_AL, data_reg, data_reg); + } break; case 3: /* TODO: use block load - * check that data_reg2 > data_reg or the other way */ if (data_reg == addr_reg) { - tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, 4); - tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0); + tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4); + tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0); } else { - tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0); - tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, 4); + tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0); + tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4); + } + if (bswap) { + tcg_out_bswap32(s, COND_AL, data_reg, data_reg); + tcg_out_bswap32(s, COND_AL, data_reg2, data_reg2); } break; } #endif } -static inline void tcg_out_qemu_st(TCGContext *s, int cond, - const TCGArg *args, int opc) +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, data_reg, data_reg2; + int addr_reg, data_reg, data_reg2, bswap; #ifdef CONFIG_SOFTMMU int mem_index, s_bits; # if TARGET_LONG_BITS == 64 @@ -1101,6 +1187,11 @@ static inline void tcg_out_qemu_st(TCGContext *s, int cond, uint32_t *label_ptr; #endif +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif data_reg = *args++; if (opc == 3) data_reg2 = *args++; @@ -1120,11 +1211,11 @@ static inline void tcg_out_qemu_st(TCGContext *s, int cond, * add r0, env, r0 lsl #CPU_TLB_ENTRY_BITS */ tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - 8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); + TCG_REG_R8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); tcg_out_dat_imm(s, COND_AL, ARITH_AND, - 0, 8, CPU_TLB_SIZE - 1); - tcg_out_dat_reg(s, COND_AL, ARITH_ADD, - 0, TCG_AREG0, 0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); + TCG_REG_R0, TCG_REG_R8, CPU_TLB_SIZE - 1); + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R0, + TCG_AREG0, TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); /* In the * ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_write))] * below, the offset is likely to exceed 12 bits if mem_index != 0 and @@ -1133,13 +1224,13 @@ static inline void tcg_out_qemu_st(TCGContext *s, int cond, * before. */ if (mem_index) - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 0, 0, + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0, (mem_index << (TLB_SHIFT & 1)) | ((16 - (TLB_SHIFT >> 1)) << 8)); - tcg_out_ld32_12(s, COND_AL, 1, 0, + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addr_write)); - tcg_out_dat_reg(s, COND_AL, ARITH_CMP, - 0, 1, 8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R1, + TCG_REG_R8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); /* Check alignment. */ if (s_bits) tcg_out_dat_imm(s, COND_EQ, ARITH_TST, @@ -1147,125 +1238,121 @@ static inline void tcg_out_qemu_st(TCGContext *s, int cond, # if TARGET_LONG_BITS == 64 /* XXX: possibly we could use a block data load or writeback in * the first access. */ - tcg_out_ld32_12(s, COND_EQ, 1, 0, - offsetof(CPUState, tlb_table[0][0].addr_write) - + 4); - tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, - 0, 1, addr_reg2, SHIFT_IMM_LSL(0)); + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, + offsetof(CPUState, tlb_table[0][0].addr_write) + 4); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + TCG_REG_R1, addr_reg2, SHIFT_IMM_LSL(0)); # endif - tcg_out_ld32_12(s, COND_EQ, 1, 0, + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addend)); switch (opc) { case 0: - tcg_out_st8_r(s, COND_EQ, data_reg, addr_reg, 1); - break; - case 0 | 4: - tcg_out_st8s_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_st8_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); break; case 1: - tcg_out_st16u_r(s, COND_EQ, data_reg, addr_reg, 1); - break; - case 1 | 4: - tcg_out_st16s_r(s, COND_EQ, data_reg, addr_reg, 1); + if (bswap) { + tcg_out_bswap16(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st16_r(s, COND_EQ, TCG_REG_R0, addr_reg, TCG_REG_R1); + } else { + tcg_out_st16_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + } break; case 2: default: - tcg_out_st32_r(s, COND_EQ, data_reg, addr_reg, 1); + if (bswap) { + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st32_r(s, COND_EQ, TCG_REG_R0, addr_reg, TCG_REG_R1); + } else { + tcg_out_st32_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + } break; case 3: - tcg_out_st32_rwb(s, COND_EQ, data_reg, 1, addr_reg); - tcg_out_st32_12(s, COND_EQ, data_reg2, 1, 4); + if (bswap) { + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg2); + tcg_out_st32_rwb(s, COND_EQ, TCG_REG_R0, TCG_REG_R1, addr_reg); + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_EQ, TCG_REG_R0, TCG_REG_R1, 4); + } else { + tcg_out_st32_rwb(s, COND_EQ, data_reg, TCG_REG_R1, addr_reg); + tcg_out_st32_12(s, COND_EQ, data_reg2, TCG_REG_R1, 4); + } break; } label_ptr = (void *) s->code_ptr; - tcg_out_b(s, COND_EQ, 8); + tcg_out_b_noaddr(s, COND_EQ); /* TODO: move this code to where the constants pool will be */ - if (addr_reg) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 0, 0, addr_reg, SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R0, 0, addr_reg, SHIFT_IMM_LSL(0)); # if TARGET_LONG_BITS == 32 switch (opc) { case 0: - tcg_out_dat_imm(s, cond, ARITH_AND, 1, data_reg, 0xff); - tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index); + tcg_out_ext8u(s, COND_AL, TCG_REG_R1, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); break; case 1: - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, data_reg, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, 1, SHIFT_IMM_LSR(16)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index); + tcg_out_ext16u(s, COND_AL, TCG_REG_R1, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); break; case 2: - if (data_reg != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, data_reg, SHIFT_IMM_LSL(0)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R1, 0, data_reg, SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); break; case 3: - if (data_reg != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, data_reg, SHIFT_IMM_LSL(0)); - if (data_reg2 != 2) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, data_reg2, SHIFT_IMM_LSL(0)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R8, 0, mem_index); + tcg_out32(s, (COND_AL << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */ + if (data_reg != TCG_REG_R2) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0)); + } + if (data_reg2 != TCG_REG_R3) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R3, 0, data_reg2, SHIFT_IMM_LSL(0)); + } break; } # else - if (addr_reg2 != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, addr_reg2, SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R1, 0, addr_reg2, SHIFT_IMM_LSL(0)); switch (opc) { case 0: - tcg_out_dat_imm(s, cond, ARITH_AND, 2, data_reg, 0xff); - tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index); + tcg_out_ext8u(s, COND_AL, TCG_REG_R2, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index); break; case 1: - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, data_reg, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, 2, SHIFT_IMM_LSR(16)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index); + tcg_out_ext16u(s, COND_AL, TCG_REG_R2, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index); break; case 2: - if (data_reg != 2) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, data_reg, SHIFT_IMM_LSL(0)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index); + if (data_reg != TCG_REG_R2) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0)); + } + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index); break; case 3: - tcg_out_dat_imm(s, cond, ARITH_MOV, 8, 0, mem_index); - tcg_out32(s, (cond << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */ - if (data_reg != 2) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, data_reg, SHIFT_IMM_LSL(0)); - if (data_reg2 != 3) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 3, 0, data_reg2, SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R8, 0, mem_index); + tcg_out32(s, (COND_AL << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */ + if (data_reg != TCG_REG_R2) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0)); + } + if (data_reg2 != TCG_REG_R3) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R3, 0, data_reg2, SHIFT_IMM_LSL(0)); + } break; } # endif -# ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 8, 0, 14, SHIFT_IMM_LSL(0)); -# endif - - tcg_out_bl(s, cond, (tcg_target_long) qemu_st_helpers[s_bits] - - (tcg_target_long) s->code_ptr); -# if TARGET_LONG_BITS == 64 + tcg_out_call(s, (tcg_target_long) qemu_st_helpers[s_bits]); if (opc == 3) - tcg_out_dat_imm(s, cond, ARITH_ADD, 13, 13, 0x10); -# endif + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R13, TCG_REG_R13, 0x10); -# ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, 8, SHIFT_IMM_LSL(0)); -# endif - - *label_ptr += ((void *) s->code_ptr - (void *) label_ptr - 8) >> 2; + reloc_pc24(label_ptr, (tcg_target_long)s->code_ptr); #else /* !CONFIG_SOFTMMU */ if (GUEST_BASE) { uint32_t offset = GUEST_BASE; @@ -1276,9 +1363,9 @@ static inline void tcg_out_qemu_st(TCGContext *s, int cond, i = ctz32(offset) & ~1; rot = ((32 - i) << 7) & 0xf00; - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 8, addr_reg, + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R1, addr_reg, ((offset >> i) & 0xff) | rot); - addr_reg = 8; + addr_reg = TCG_REG_R1; offset &= ~(0xff << i); } } @@ -1286,24 +1373,35 @@ static inline void tcg_out_qemu_st(TCGContext *s, int cond, case 0: tcg_out_st8_12(s, COND_AL, data_reg, addr_reg, 0); break; - case 0 | 4: - tcg_out_st8s_8(s, COND_AL, data_reg, addr_reg, 0); - break; case 1: - tcg_out_st16u_8(s, COND_AL, data_reg, addr_reg, 0); - break; - case 1 | 4: - tcg_out_st16s_8(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap16(s, COND_AL, TCG_REG_R0, data_reg); + tcg_out_st16_8(s, COND_AL, TCG_REG_R0, addr_reg, 0); + } else { + tcg_out_st16_8(s, COND_AL, data_reg, addr_reg, 0); + } break; case 2: default: - tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0); + } else { + tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); + } break; case 3: /* TODO: use block store - * check that data_reg2 > data_reg or the other way */ - tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); - tcg_out_st32_12(s, COND_AL, data_reg2, addr_reg, 4); + if (bswap) { + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg2); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0); + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 4); + } else { + tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); + tcg_out_st32_12(s, COND_AL, data_reg2, addr_reg, 4); + } break; } #endif @@ -1311,44 +1409,34 @@ static inline void tcg_out_qemu_st(TCGContext *s, int cond, static uint8_t *tb_ret_addr; -static inline void tcg_out_op(TCGContext *s, int opc, +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { int c; switch (opc) { case INDEX_op_exit_tb: -#ifdef SAVE_LR - if (args[0] >> 8) - tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, 15, 0); - else - tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R0, 0, args[0]); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, 15, 0, 14, SHIFT_IMM_LSL(0)); - if (args[0] >> 8) - tcg_out32(s, args[0]); -#else { uint8_t *ld_ptr = s->code_ptr; if (args[0] >> 8) - tcg_out_ld32_12(s, COND_AL, 0, 15, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_PC, 0); else - tcg_out_dat_imm(s, COND_AL, ARITH_MOV, 0, 0, args[0]); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R0, 0, args[0]); tcg_out_goto(s, COND_AL, (tcg_target_ulong) tb_ret_addr); if (args[0] >> 8) { *ld_ptr = (uint8_t) (s->code_ptr - ld_ptr) - 8; tcg_out32(s, args[0]); } } -#endif break; case INDEX_op_goto_tb: if (s->tb_jmp_offset) { /* Direct jump method */ #if defined(USE_DIRECT_JUMP) s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; - tcg_out_b(s, COND_AL, 8); + tcg_out_b_noaddr(s, COND_AL); #else - tcg_out_ld32_12(s, COND_AL, 15, 15, -4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; tcg_out32(s, 0); #endif @@ -1359,12 +1447,12 @@ static inline void tcg_out_op(TCGContext *s, int opc, if (c > 0xfff || c < -0xfff) { tcg_out_movi32(s, COND_AL, TCG_REG_R0, (tcg_target_long) (s->tb_next + args[0])); - tcg_out_ld32_12(s, COND_AL, 15, TCG_REG_R0, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, 0); } else - tcg_out_ld32_12(s, COND_AL, 15, 15, c); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, c); #else - tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, 15, 0); - tcg_out_ld32_12(s, COND_AL, 15, TCG_REG_R0, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_PC, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, 0); tcg_out32(s, (tcg_target_long) (s->tb_next + args[0])); #endif } @@ -1372,7 +1460,7 @@ static inline void tcg_out_op(TCGContext *s, int opc, break; case INDEX_op_call: if (const_args[0]) - tcg_out_call(s, COND_AL, args[0]); + tcg_out_call(s, args[0]); else tcg_out_callr(s, COND_AL, args[0]); break; @@ -1402,10 +1490,10 @@ static inline void tcg_out_op(TCGContext *s, int opc, tcg_out_ld32u(s, COND_AL, args[0], args[1], args[2]); break; case INDEX_op_st8_i32: - tcg_out_st8u(s, COND_AL, args[0], args[1], args[2]); + tcg_out_st8(s, COND_AL, args[0], args[1], args[2]); break; case INDEX_op_st16_i32: - tcg_out_st16u(s, COND_AL, args[0], args[1], args[2]); + tcg_out_st16(s, COND_AL, args[0], args[1], args[2]); break; case INDEX_op_st_i32: tcg_out_st32(s, COND_AL, args[0], args[1], args[2]); @@ -1427,6 +1515,9 @@ static inline void tcg_out_op(TCGContext *s, int opc, case INDEX_op_and_i32: c = ARITH_AND; goto gen_arith; + case INDEX_op_andc_i32: + c = ARITH_BIC; + goto gen_arith; case INDEX_op_or_i32: c = ARITH_ORR; goto gen_arith; @@ -1466,16 +1557,6 @@ static inline void tcg_out_op(TCGContext *s, int opc, case INDEX_op_mulu2_i32: tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; - case INDEX_op_div2_i32: - tcg_out_div_helper(s, COND_AL, args, - tcg_helper_div_i64, tcg_helper_rem_i64, - SHIFT_IMM_ASR(31)); - break; - case INDEX_op_divu2_i32: - tcg_out_div_helper(s, COND_AL, args, - tcg_helper_divu_i64, tcg_helper_remu_i64, - SHIFT_IMM_LSR(31)); - break; /* XXX: Perhaps args[2] & 0x1f is wrong */ case INDEX_op_shl_i32: c = const_args[2] ? @@ -1488,14 +1569,38 @@ static inline void tcg_out_op(TCGContext *s, int opc, case INDEX_op_sar_i32: c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) : SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]); + goto gen_shift32; + case INDEX_op_rotr_i32: + c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ROR(args[2] & 0x1f) : + SHIFT_IMM_LSL(0) : SHIFT_REG_ROR(args[2]); /* Fall through. */ gen_shift32: tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c); break; + case INDEX_op_rotl_i32: + if (const_args[2]) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], + ((0x20 - args[2]) & 0x1f) ? + SHIFT_IMM_ROR((0x20 - args[2]) & 0x1f) : + SHIFT_IMM_LSL(0)); + } else { + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_R8, args[1], 0x20); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], + SHIFT_REG_ROR(TCG_REG_R8)); + } + break; + case INDEX_op_brcond_i32: - tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, - args[0], args[1], SHIFT_IMM_LSL(0)); + if (const_args[1]) { + int rot; + rot = encode_imm(args[1]); + tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, + args[0], rotl(args[1], rot) | (rot << 7)); + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[0], args[1], SHIFT_IMM_LSL(0)); + } tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], args[3]); break; case INDEX_op_brcond2_i32: @@ -1513,60 +1618,80 @@ static inline void tcg_out_op(TCGContext *s, int opc, args[0], args[2], SHIFT_IMM_LSL(0)); tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]], args[5]); break; + case INDEX_op_setcond_i32: + if (const_args[2]) { + int rot; + rot = encode_imm(args[2]); + tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, + args[1], rotl(args[2], rot) | (rot << 7)); + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[1], args[2], SHIFT_IMM_LSL(0)); + } + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[3]], + ARITH_MOV, args[0], 0, 1); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])], + ARITH_MOV, args[0], 0, 0); + break; + case INDEX_op_setcond2_i32: + /* See brcond2_i32 comment */ + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[2], args[4], SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + args[1], args[3], SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[5]], + ARITH_MOV, args[0], 0, 1); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[5])], + ARITH_MOV, args[0], 0, 0); + break; case INDEX_op_qemu_ld8u: - tcg_out_qemu_ld(s, COND_AL, args, 0); + tcg_out_qemu_ld(s, args, 0); break; case INDEX_op_qemu_ld8s: - tcg_out_qemu_ld(s, COND_AL, args, 0 | 4); + tcg_out_qemu_ld(s, args, 0 | 4); break; case INDEX_op_qemu_ld16u: - tcg_out_qemu_ld(s, COND_AL, args, 1); + tcg_out_qemu_ld(s, args, 1); break; case INDEX_op_qemu_ld16s: - tcg_out_qemu_ld(s, COND_AL, args, 1 | 4); + tcg_out_qemu_ld(s, args, 1 | 4); break; - case INDEX_op_qemu_ld32u: - tcg_out_qemu_ld(s, COND_AL, args, 2); + case INDEX_op_qemu_ld32: + tcg_out_qemu_ld(s, args, 2); break; case INDEX_op_qemu_ld64: - tcg_out_qemu_ld(s, COND_AL, args, 3); + tcg_out_qemu_ld(s, args, 3); break; case INDEX_op_qemu_st8: - tcg_out_qemu_st(s, COND_AL, args, 0); + tcg_out_qemu_st(s, args, 0); break; case INDEX_op_qemu_st16: - tcg_out_qemu_st(s, COND_AL, args, 1); + tcg_out_qemu_st(s, args, 1); break; case INDEX_op_qemu_st32: - tcg_out_qemu_st(s, COND_AL, args, 2); + tcg_out_qemu_st(s, args, 2); break; case INDEX_op_qemu_st64: - tcg_out_qemu_st(s, COND_AL, args, 3); + tcg_out_qemu_st(s, args, 3); + break; + + case INDEX_op_bswap16_i32: + tcg_out_bswap16(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_bswap32_i32: + tcg_out_bswap32(s, COND_AL, args[0], args[1]); break; case INDEX_op_ext8s_i32: -#ifdef __ARM_ARCH_7A__ - /* sxtb */ - tcg_out32(s, 0xe6af0070 | (args[0] << 12) | args[1]); -#else - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - args[0], 0, args[1], SHIFT_IMM_LSL(24)); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - args[0], 0, args[0], SHIFT_IMM_ASR(24)); -#endif + tcg_out_ext8s(s, COND_AL, args[0], args[1]); break; case INDEX_op_ext16s_i32: -#ifdef __ARM_ARCH_7A__ - /* sxth */ - tcg_out32(s, 0xe6bf0070 | (args[0] << 12) | args[1]); -#else - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - args[0], 0, args[1], SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - args[0], 0, args[0], SHIFT_IMM_ASR(16)); -#endif + tcg_out_ext16s(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + tcg_out_ext16u(s, COND_AL, args[0], args[1]); break; default: @@ -1598,9 +1723,8 @@ static const TCGTargetOpDef arm_op_defs[] = { { INDEX_op_sub_i32, { "r", "r", "rI" } }, { INDEX_op_mul_i32, { "r", "r", "r" } }, { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, - { INDEX_op_div2_i32, { "r", "r", "r", "1", "2" } }, - { INDEX_op_divu2_i32, { "r", "r", "r", "1", "2" } }, { INDEX_op_and_i32, { "r", "r", "rI" } }, + { INDEX_op_andc_i32, { "r", "r", "rI" } }, { INDEX_op_or_i32, { "r", "r", "rI" } }, { INDEX_op_xor_i32, { "r", "r", "rI" } }, { INDEX_op_neg_i32, { "r", "r" } }, @@ -1609,50 +1733,75 @@ static const TCGTargetOpDef arm_op_defs[] = { { INDEX_op_shl_i32, { "r", "r", "ri" } }, { INDEX_op_shr_i32, { "r", "r", "ri" } }, { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, - { INDEX_op_brcond_i32, { "r", "r" } }, + { INDEX_op_brcond_i32, { "r", "rI" } }, + { INDEX_op_setcond_i32, { "r", "r", "rI" } }, /* TODO: "r", "r", "r", "r", "ri", "ri" */ { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } }, { INDEX_op_sub2_i32, { "r", "r", "r", "r", "r", "r" } }, { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "r", "r" } }, + +#if TARGET_LONG_BITS == 32 + { INDEX_op_qemu_ld8u, { "r", "l" } }, + { INDEX_op_qemu_ld8s, { "r", "l" } }, + { INDEX_op_qemu_ld16u, { "r", "l" } }, + { INDEX_op_qemu_ld16s, { "r", "l" } }, + { INDEX_op_qemu_ld32, { "r", "l" } }, + { INDEX_op_qemu_ld64, { "L", "L", "l" } }, + + { INDEX_op_qemu_st8, { "s", "s" } }, + { INDEX_op_qemu_st16, { "s", "s" } }, + { INDEX_op_qemu_st32, { "s", "s" } }, + { INDEX_op_qemu_st64, { "S", "S", "s" } }, +#else + { INDEX_op_qemu_ld8u, { "r", "l", "l" } }, + { INDEX_op_qemu_ld8s, { "r", "l", "l" } }, + { INDEX_op_qemu_ld16u, { "r", "l", "l" } }, + { INDEX_op_qemu_ld16s, { "r", "l", "l" } }, + { INDEX_op_qemu_ld32, { "r", "l", "l" } }, + { INDEX_op_qemu_ld64, { "L", "L", "l", "l" } }, + + { INDEX_op_qemu_st8, { "s", "s", "s" } }, + { INDEX_op_qemu_st16, { "s", "s", "s" } }, + { INDEX_op_qemu_st32, { "s", "s", "s" } }, + { INDEX_op_qemu_st64, { "S", "S", "s", "s" } }, +#endif - { INDEX_op_qemu_ld8u, { "r", "x", "X" } }, - { INDEX_op_qemu_ld8s, { "r", "x", "X" } }, - { INDEX_op_qemu_ld16u, { "r", "x", "X" } }, - { INDEX_op_qemu_ld16s, { "r", "x", "X" } }, - { INDEX_op_qemu_ld32u, { "r", "x", "X" } }, - { INDEX_op_qemu_ld64, { "d", "r", "x", "X" } }, - - { INDEX_op_qemu_st8, { "x", "x", "X" } }, - { INDEX_op_qemu_st16, { "x", "x", "X" } }, - { INDEX_op_qemu_st32, { "x", "x", "X" } }, - { INDEX_op_qemu_st64, { "x", "D", "x", "X" } }, + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, { INDEX_op_ext8s_i32, { "r", "r" } }, { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, { -1 }, }; -void tcg_target_init(TCGContext *s) +static void tcg_target_init(TCGContext *s) { +#if !defined(CONFIG_USER_ONLY) /* fail safe */ if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) tcg_abort(); +#endif - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, - ((2 << TCG_REG_R14) - 1) & ~(1 << TCG_REG_R8)); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); tcg_regset_set32(tcg_target_call_clobber_regs, 0, - ((2 << TCG_REG_R3) - 1) | - (1 << TCG_REG_R12) | (1 << TCG_REG_R14)); + (1 << TCG_REG_R0) | + (1 << TCG_REG_R1) | + (1 << TCG_REG_R2) | + (1 << TCG_REG_R3) | + (1 << TCG_REG_R12) | + (1 << TCG_REG_R14)); tcg_regset_clear(s->reserved_regs); -#ifdef SAVE_LR - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R14); -#endif tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); tcg_regset_set_reg(s->reserved_regs, TCG_REG_R8); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_PC); tcg_add_target_add_op_defs(arm_op_defs); } @@ -1684,7 +1833,7 @@ static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) } } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) { tcg_out_dat_reg(s, COND_AL, ARITH_MOV, ret, 0, arg, SHIFT_IMM_LSL(0)); } @@ -1695,14 +1844,17 @@ static inline void tcg_out_movi(TCGContext *s, TCGType type, tcg_out_movi32(s, COND_AL, ret, arg); } -void tcg_target_qemu_prologue(TCGContext *s) +static void tcg_target_qemu_prologue(TCGContext *s) { - /* stmdb sp!, { r9 - r11, lr } */ - tcg_out32(s, (COND_AL << 28) | 0x092d4e00); + /* There is no need to save r7, it is used to store the address + of the env structure and is not modified by GCC. */ + + /* stmdb sp!, { r4 - r6, r8 - r11, lr } */ + tcg_out32(s, (COND_AL << 28) | 0x092d4f70); tcg_out_bx(s, COND_AL, TCG_REG_R0); tb_ret_addr = s->code_ptr; - /* ldmia sp!, { r9 - r11, pc } */ - tcg_out32(s, (COND_AL << 28) | 0x08bd8e00); + /* ldmia sp!, { r4 - r6, r8 - r11, pc } */ + tcg_out32(s, (COND_AL << 28) | 0x08bd8f70); } diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 5eac7bf..d8d7d94 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -26,14 +26,6 @@ #define TCG_TARGET_REG_BITS 32 #undef TCG_TARGET_WORDS_BIGENDIAN -#undef TCG_TARGET_HAS_div_i32 -#undef TCG_TARGET_HAS_div_i64 -#undef TCG_TARGET_HAS_bswap32_i32 -#define TCG_TARGET_HAS_ext8s_i32 -#define TCG_TARGET_HAS_ext16s_i32 -#define TCG_TARGET_HAS_neg_i32 -#undef TCG_TARGET_HAS_neg_i64 -#define TCG_TARGET_HAS_not_i32 #undef TCG_TARGET_STACK_GROWSUP enum { @@ -52,38 +44,40 @@ enum { TCG_REG_R12, TCG_REG_R13, TCG_REG_R14, + TCG_REG_PC, }; -#define TCG_TARGET_NB_REGS 15 +#define TCG_TARGET_NB_REGS 16 #define TCG_CT_CONST_ARM 0x100 /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_R13 #define TCG_TARGET_STACK_ALIGN 8 +#define TCG_TARGET_CALL_ALIGN_ARGS 1 #define TCG_TARGET_CALL_STACK_OFFSET 0 /* optional instructions */ -// #define TCG_TARGET_HAS_div_i32 -// #define TCG_TARGET_HAS_rot_i32 -// #define TCG_TARGET_HAS_ext8s_i32 -// #define TCG_TARGET_HAS_ext16s_i32 -// #define TCG_TARGET_HAS_ext8u_i32 -// #define TCG_TARGET_HAS_ext16u_i32 -// #define TCG_TARGET_HAS_bswap16_i32 -// #define TCG_TARGET_HAS_bswap32_i32 -// #define TCG_TARGET_HAS_not_i32 -// #define TCG_TARGET_HAS_neg_i32 -// #define TCG_TARGET_HAS_andc_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#undef TCG_TARGET_HAS_ext8u_i32 /* and r0, r1, #0xff */ +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_neg_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_andc_i32 // #define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 #define TCG_TARGET_HAS_GUEST_BASE enum { /* Note: must be synced with dyngen-exec.h */ TCG_AREG0 = TCG_REG_R7, - TCG_AREG1 = TCG_REG_R4, - TCG_AREG2 = TCG_REG_R5, }; static inline void flush_icache_range(unsigned long start, unsigned long stop) diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c index 4677971..7f4653e 100644 --- a/tcg/hppa/tcg-target.c +++ b/tcg/hppa/tcg-target.c @@ -24,41 +24,22 @@ #ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%r0", - "%r1", - "%rp", - "%r3", - "%r4", - "%r5", - "%r6", - "%r7", - "%r8", - "%r9", - "%r10", - "%r11", - "%r12", - "%r13", - "%r14", - "%r15", - "%r16", - "%r17", - "%r18", - "%r19", - "%r20", - "%r21", - "%r22", - "%r23", - "%r24", - "%r25", - "%r26", - "%dp", - "%ret0", - "%ret1", - "%sp", - "%r31", + "%r0", "%r1", "%rp", "%r3", "%r4", "%r5", "%r6", "%r7", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", + "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23", + "%r24", "%r25", "%r26", "%dp", "%ret0", "%ret1", "%sp", "%r31", }; #endif +/* This is an 8 byte temp slot in the stack frame. */ +#define STACK_TEMP_OFS -16 + +#ifdef CONFIG_USE_GUEST_BASE +#define TCG_GUEST_BASE_REG TCG_REG_R16 +#else +#define TCG_GUEST_BASE_REG TCG_REG_R0 +#endif + static const int tcg_target_reg_alloc_order[] = { TCG_REG_R4, TCG_REG_R5, @@ -75,6 +56,14 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_R14, TCG_REG_R15, TCG_REG_R16, + + TCG_REG_R26, + TCG_REG_R25, + TCG_REG_R24, + TCG_REG_R23, + + TCG_REG_RET0, + TCG_REG_RET1, }; static const int tcg_target_call_iarg_regs[4] = { @@ -89,16 +78,101 @@ static const int tcg_target_call_oarg_regs[2] = { TCG_REG_RET1, }; +/* True iff val fits a signed field of width BITS. */ +static inline int check_fit_tl(tcg_target_long val, unsigned int bits) +{ + return (val << ((sizeof(tcg_target_long) * 8 - bits)) + >> (sizeof(tcg_target_long) * 8 - bits)) == val; +} + +/* True iff depi can be used to compute (reg | MASK). + Accept a bit pattern like: + 0....01....1 + 1....10....0 + 0..01..10..0 + Copied from gcc sources. */ +static inline int or_mask_p(tcg_target_ulong mask) +{ + if (mask == 0 || mask == -1) { + return 0; + } + mask += mask & -mask; + return (mask & (mask - 1)) == 0; +} + +/* True iff depi or extru can be used to compute (reg & mask). + Accept a bit pattern like these: + 0....01....1 + 1....10....0 + 1..10..01..1 + Copied from gcc sources. */ +static inline int and_mask_p(tcg_target_ulong mask) +{ + return or_mask_p(~mask); +} + +static int low_sign_ext(int val, int len) +{ + return (((val << 1) & ~(-1u << len)) | ((val >> (len - 1)) & 1)); +} + +static int reassemble_12(int as12) +{ + return (((as12 & 0x800) >> 11) | + ((as12 & 0x400) >> 8) | + ((as12 & 0x3ff) << 3)); +} + +static int reassemble_17(int as17) +{ + return (((as17 & 0x10000) >> 16) | + ((as17 & 0x0f800) << 5) | + ((as17 & 0x00400) >> 8) | + ((as17 & 0x003ff) << 3)); +} + +static int reassemble_21(int as21) +{ + return (((as21 & 0x100000) >> 20) | + ((as21 & 0x0ffe00) >> 8) | + ((as21 & 0x000180) << 7) | + ((as21 & 0x00007c) << 14) | + ((as21 & 0x000003) << 12)); +} + +/* ??? Bizzarely, there is no PCREL12F relocation type. I guess all + such relocations are simply fully handled by the assembler. */ +#define R_PARISC_PCREL12F R_PARISC_NONE + static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend) { + uint32_t *insn_ptr = (uint32_t *)code_ptr; + uint32_t insn = *insn_ptr; + tcg_target_long pcrel; + + value += addend; + pcrel = (value - ((tcg_target_long)code_ptr + 8)) >> 2; + switch (type) { + case R_PARISC_PCREL12F: + assert(check_fit_tl(pcrel, 12)); + /* ??? We assume all patches are forward. See tcg_out_brcond + re setting the NUL bit on the branch and eliding the nop. */ + assert(pcrel >= 0); + insn &= ~0x1ffdu; + insn |= reassemble_12(pcrel); + break; case R_PARISC_PCREL17F: - hppa_patch17f((uint32_t *)code_ptr, value, addend); + assert(check_fit_tl(pcrel, 17)); + insn &= ~0x1f1ffdu; + insn |= reassemble_17(pcrel); break; default: tcg_abort(); } + + *insn_ptr = insn; } /* maximum number of register used for input function arguments */ @@ -126,6 +200,24 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) tcg_regset_reset_reg(ct->u.regs, TCG_REG_R24); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R23); break; + case 'Z': + ct->ct |= TCG_CT_CONST_0; + break; + case 'I': + ct->ct |= TCG_CT_CONST_S11; + break; + case 'J': + ct->ct |= TCG_CT_CONST_S5; + break; + case 'K': + ct->ct |= TCG_CT_CONST_MS11; + break; + case 'M': + ct->ct |= TCG_CT_CONST_AND; + break; + case 'O': + ct->ct |= TCG_CT_CONST_OR; + break; default: return -1; } @@ -135,15 +227,25 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) } /* test if a constant matches the constraint */ -static inline int tcg_target_const_match(tcg_target_long val, - const TCGArgConstraint *arg_ct) +static int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) { - int ct; - - ct = arg_ct->ct; - - /* TODO */ - + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { + return 1; + } else if (ct & TCG_CT_CONST_0) { + return val == 0; + } else if (ct & TCG_CT_CONST_S5) { + return check_fit_tl(val, 5); + } else if (ct & TCG_CT_CONST_S11) { + return check_fit_tl(val, 11); + } else if (ct & TCG_CT_CONST_MS11) { + return check_fit_tl(-val, 11); + } else if (ct & TCG_CT_CONST_AND) { + return and_mask_p(val); + } else if (ct & TCG_CT_CONST_OR) { + return or_mask_p(val); + } return 0; } @@ -163,692 +265,1197 @@ static inline int tcg_target_const_match(tcg_target_long val, #define INSN_SHDEP_CP(x) ((31 - (x)) << 5) #define INSN_SHDEP_P(x) ((x) << 5) #define INSN_COND(x) ((x) << 13) +#define INSN_IM11(x) low_sign_ext(x, 11) +#define INSN_IM14(x) low_sign_ext(x, 14) +#define INSN_IM5(x) (low_sign_ext(x, 5) << 16) + +#define COND_NEVER 0 +#define COND_EQ 1 +#define COND_LT 2 +#define COND_LE 3 +#define COND_LTU 4 +#define COND_LEU 5 +#define COND_SV 6 +#define COND_OD 7 +#define COND_FALSE 8 + +#define INSN_ADD (INSN_OP(0x02) | INSN_EXT6(0x18)) +#define INSN_ADDC (INSN_OP(0x02) | INSN_EXT6(0x1c)) +#define INSN_ADDI (INSN_OP(0x2d)) +#define INSN_ADDIL (INSN_OP(0x0a)) +#define INSN_ADDL (INSN_OP(0x02) | INSN_EXT6(0x28)) +#define INSN_AND (INSN_OP(0x02) | INSN_EXT6(0x08)) +#define INSN_ANDCM (INSN_OP(0x02) | INSN_EXT6(0x00)) +#define INSN_COMCLR (INSN_OP(0x02) | INSN_EXT6(0x22)) +#define INSN_COMICLR (INSN_OP(0x24)) +#define INSN_DEP (INSN_OP(0x35) | INSN_EXT3SH(3)) +#define INSN_DEPI (INSN_OP(0x35) | INSN_EXT3SH(7)) +#define INSN_EXTRS (INSN_OP(0x34) | INSN_EXT3SH(7)) +#define INSN_EXTRU (INSN_OP(0x34) | INSN_EXT3SH(6)) +#define INSN_LDIL (INSN_OP(0x08)) +#define INSN_LDO (INSN_OP(0x0d)) +#define INSN_MTCTL (INSN_OP(0x00) | INSN_EXT8B(0xc2)) +#define INSN_OR (INSN_OP(0x02) | INSN_EXT6(0x09)) +#define INSN_SHD (INSN_OP(0x34) | INSN_EXT3SH(2)) +#define INSN_SUB (INSN_OP(0x02) | INSN_EXT6(0x10)) +#define INSN_SUBB (INSN_OP(0x02) | INSN_EXT6(0x14)) +#define INSN_SUBI (INSN_OP(0x25)) +#define INSN_VEXTRS (INSN_OP(0x34) | INSN_EXT3SH(5)) +#define INSN_VEXTRU (INSN_OP(0x34) | INSN_EXT3SH(4)) +#define INSN_VSHD (INSN_OP(0x34) | INSN_EXT3SH(0)) +#define INSN_XOR (INSN_OP(0x02) | INSN_EXT6(0x0a)) +#define INSN_ZDEP (INSN_OP(0x35) | INSN_EXT3SH(2)) +#define INSN_ZVDEP (INSN_OP(0x35) | INSN_EXT3SH(0)) + +#define INSN_BL (INSN_OP(0x3a) | INSN_EXT3BR(0)) +#define INSN_BL_N (INSN_OP(0x3a) | INSN_EXT3BR(0) | 2) +#define INSN_BLR (INSN_OP(0x3a) | INSN_EXT3BR(2)) +#define INSN_BV (INSN_OP(0x3a) | INSN_EXT3BR(6)) +#define INSN_BV_N (INSN_OP(0x3a) | INSN_EXT3BR(6) | 2) +#define INSN_BLE_SR4 (INSN_OP(0x39) | (1 << 13)) + +#define INSN_LDB (INSN_OP(0x10)) +#define INSN_LDH (INSN_OP(0x11)) +#define INSN_LDW (INSN_OP(0x12)) +#define INSN_LDWM (INSN_OP(0x13)) +#define INSN_FLDDS (INSN_OP(0x0b) | INSN_EXT4(0) | (1 << 12)) + +#define INSN_LDBX (INSN_OP(0x03) | INSN_EXT4(0)) +#define INSN_LDHX (INSN_OP(0x03) | INSN_EXT4(1)) +#define INSN_LDWX (INSN_OP(0x03) | INSN_EXT4(2)) + +#define INSN_STB (INSN_OP(0x18)) +#define INSN_STH (INSN_OP(0x19)) +#define INSN_STW (INSN_OP(0x1a)) +#define INSN_STWM (INSN_OP(0x1b)) +#define INSN_FSTDS (INSN_OP(0x0b) | INSN_EXT4(8) | (1 << 12)) + +#define INSN_COMBT (INSN_OP(0x20)) +#define INSN_COMBF (INSN_OP(0x22)) +#define INSN_COMIBT (INSN_OP(0x21)) +#define INSN_COMIBF (INSN_OP(0x23)) + +/* supplied by libgcc */ +extern void *__canonicalize_funcptr_for_compare(void *); + +static void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) +{ + /* PA1.1 defines COPY as OR r,0,t; PA2.0 defines COPY as LDO 0(r),t + but hppa-dis.c is unaware of this definition */ + if (ret != arg) { + tcg_out32(s, INSN_OR | INSN_T(ret) | INSN_R1(arg) + | INSN_R2(TCG_REG_R0)); + } +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + int ret, tcg_target_long arg) +{ + if (check_fit_tl(arg, 14)) { + tcg_out32(s, INSN_LDO | INSN_R1(ret) + | INSN_R2(TCG_REG_R0) | INSN_IM14(arg)); + } else { + uint32_t hi, lo; + hi = arg >> 11; + lo = arg & 0x7ff; + + tcg_out32(s, INSN_LDIL | INSN_R2(ret) | reassemble_21(hi)); + if (lo) { + tcg_out32(s, INSN_LDO | INSN_R1(ret) + | INSN_R2(ret) | INSN_IM14(lo)); + } + } +} + +static void tcg_out_ldst(TCGContext *s, int ret, int addr, + tcg_target_long offset, int op) +{ + if (!check_fit_tl(offset, 14)) { + uint32_t hi, lo, op; + + hi = offset >> 11; + lo = offset & 0x7ff; + + if (addr == TCG_REG_R0) { + op = INSN_LDIL | INSN_R2(TCG_REG_R1); + } else { + op = INSN_ADDIL | INSN_R2(addr); + } + tcg_out32(s, op | reassemble_21(hi)); -#define COND_NEVER 0 -#define COND_EQUAL 1 -#define COND_LT 2 -#define COND_LTEQ 3 -#define COND_LTU 4 -#define COND_LTUEQ 5 -#define COND_SV 6 -#define COND_OD 7 + addr = TCG_REG_R1; + offset = lo; + } + + if (ret != addr || offset != 0 || op != INSN_LDO) { + tcg_out32(s, op | INSN_R1(ret) | INSN_R2(addr) | INSN_IM14(offset)); + } +} +/* This function is required by tcg.c. */ +static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, + int arg1, tcg_target_long arg2) +{ + tcg_out_ldst(s, ret, arg1, arg2, INSN_LDW); +} -/* Logical ADD */ -#define ARITH_ADD (INSN_OP(0x02) | INSN_EXT6(0x28)) -#define ARITH_AND (INSN_OP(0x02) | INSN_EXT6(0x08)) -#define ARITH_OR (INSN_OP(0x02) | INSN_EXT6(0x09)) -#define ARITH_XOR (INSN_OP(0x02) | INSN_EXT6(0x0a)) -#define ARITH_SUB (INSN_OP(0x02) | INSN_EXT6(0x10)) +/* This function is required by tcg.c. */ +static inline void tcg_out_st(TCGContext *s, TCGType type, int ret, + int arg1, tcg_target_long arg2) +{ + tcg_out_ldst(s, ret, arg1, arg2, INSN_STW); +} -#define SHD (INSN_OP(0x34) | INSN_EXT3SH(2)) -#define VSHD (INSN_OP(0x34) | INSN_EXT3SH(0)) -#define DEP (INSN_OP(0x35) | INSN_EXT3SH(3)) -#define ZDEP (INSN_OP(0x35) | INSN_EXT3SH(2)) -#define ZVDEP (INSN_OP(0x35) | INSN_EXT3SH(0)) -#define EXTRU (INSN_OP(0x34) | INSN_EXT3SH(6)) -#define EXTRS (INSN_OP(0x34) | INSN_EXT3SH(7)) -#define VEXTRS (INSN_OP(0x34) | INSN_EXT3SH(5)) +static void tcg_out_ldst_index(TCGContext *s, int data, + int base, int index, int op) +{ + tcg_out32(s, op | INSN_T(data) | INSN_R1(index) | INSN_R2(base)); +} -#define SUBI (INSN_OP(0x25)) -#define MTCTL (INSN_OP(0x00) | INSN_EXT8B(0xc2)) +static inline void tcg_out_addi2(TCGContext *s, int ret, int arg1, + tcg_target_long val) +{ + tcg_out_ldst(s, ret, arg1, val, INSN_LDO); +} -#define BL (INSN_OP(0x3a) | INSN_EXT3BR(0)) -#define BLE_SR4 (INSN_OP(0x39) | (1 << 13)) -#define BV (INSN_OP(0x3a) | INSN_EXT3BR(6)) -#define BV_N (INSN_OP(0x3a) | INSN_EXT3BR(6) | 2) -#define LDIL (INSN_OP(0x08)) -#define LDO (INSN_OP(0x0d)) +/* This function is required by tcg.c. */ +static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + tcg_out_addi2(s, reg, reg, val); +} -#define LDB (INSN_OP(0x10)) -#define LDH (INSN_OP(0x11)) -#define LDW (INSN_OP(0x12)) -#define LDWM (INSN_OP(0x13)) +static inline void tcg_out_arith(TCGContext *s, int t, int r1, int r2, int op) +{ + tcg_out32(s, op | INSN_T(t) | INSN_R1(r1) | INSN_R2(r2)); +} -#define STB (INSN_OP(0x18)) -#define STH (INSN_OP(0x19)) -#define STW (INSN_OP(0x1a)) -#define STWM (INSN_OP(0x1b)) +static inline void tcg_out_arithi(TCGContext *s, int t, int r1, + tcg_target_long val, int op) +{ + assert(check_fit_tl(val, 11)); + tcg_out32(s, op | INSN_R1(t) | INSN_R2(r1) | INSN_IM11(val)); +} -#define COMBT (INSN_OP(0x20)) -#define COMBF (INSN_OP(0x22)) +static inline void tcg_out_nop(TCGContext *s) +{ + tcg_out_arith(s, TCG_REG_R0, TCG_REG_R0, TCG_REG_R0, INSN_OR); +} -static int lowsignext(uint32_t val, int start, int length) +static inline void tcg_out_mtctl_sar(TCGContext *s, int arg) { - return (((val << 1) & ~(~0 << length)) | - ((val >> (length - 1)) & 1)) << start; + tcg_out32(s, INSN_MTCTL | INSN_R2(11) | INSN_R1(arg)); } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +/* Extract LEN bits at position OFS from ARG and place in RET. + Note that here the bit ordering is reversed from the PA-RISC + standard, such that the right-most bit is 0. */ +static inline void tcg_out_extr(TCGContext *s, int ret, int arg, + unsigned ofs, unsigned len, int sign) { - /* PA1.1 defines COPY as OR r,0,t */ - tcg_out32(s, ARITH_OR | INSN_T(ret) | INSN_R1(arg) | INSN_R2(TCG_REG_R0)); + assert(ofs < 32 && len <= 32 - ofs); + tcg_out32(s, (sign ? INSN_EXTRS : INSN_EXTRU) + | INSN_R1(ret) | INSN_R2(arg) + | INSN_SHDEP_P(31 - ofs) | INSN_DEP_LEN(len)); +} - /* PA2.0 defines COPY as LDO 0(r),t - * but hppa-dis.c is unaware of this definition */ - /* tcg_out32(s, LDO | INSN_R1(ret) | INSN_R2(arg) | reassemble_14(0)); */ +/* Likewise with OFS interpreted little-endian. */ +static inline void tcg_out_dep(TCGContext *s, int ret, int arg, + unsigned ofs, unsigned len) +{ + assert(ofs < 32 && len <= 32 - ofs); + tcg_out32(s, INSN_DEP | INSN_R2(ret) | INSN_R1(arg) + | INSN_SHDEP_CP(31 - ofs) | INSN_DEP_LEN(len)); } -static inline void tcg_out_movi(TCGContext *s, TCGType type, - int ret, tcg_target_long arg) +static inline void tcg_out_shd(TCGContext *s, int ret, int hi, int lo, + unsigned count) { - if (arg == (arg & 0x1fff)) { - tcg_out32(s, LDO | INSN_R1(ret) | INSN_R2(TCG_REG_R0) | - reassemble_14(arg)); + assert(count < 32); + tcg_out32(s, INSN_SHD | INSN_R1(hi) | INSN_R2(lo) | INSN_T(ret) + | INSN_SHDEP_CP(count)); +} + +static void tcg_out_vshd(TCGContext *s, int ret, int hi, int lo, int creg) +{ + tcg_out_mtctl_sar(s, creg); + tcg_out32(s, INSN_VSHD | INSN_T(ret) | INSN_R1(hi) | INSN_R2(lo)); +} + +static void tcg_out_ori(TCGContext *s, int ret, int arg, tcg_target_ulong m) +{ + int bs0, bs1; + + /* Note that the argument is constrained to match or_mask_p. */ + for (bs0 = 0; bs0 < 32; bs0++) { + if ((m & (1u << bs0)) != 0) { + break; + } + } + for (bs1 = bs0; bs1 < 32; bs1++) { + if ((m & (1u << bs1)) == 0) { + break; + } + } + assert(bs1 == 32 || (1ul << bs1) > m); + + tcg_out_mov(s, TCG_TYPE_I32, ret, arg); + tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(-1) + | INSN_SHDEP_CP(31 - bs0) | INSN_DEP_LEN(bs1 - bs0)); +} + +static void tcg_out_andi(TCGContext *s, int ret, int arg, tcg_target_ulong m) +{ + int ls0, ls1, ms0; + + /* Note that the argument is constrained to match and_mask_p. */ + for (ls0 = 0; ls0 < 32; ls0++) { + if ((m & (1u << ls0)) == 0) { + break; + } + } + for (ls1 = ls0; ls1 < 32; ls1++) { + if ((m & (1u << ls1)) != 0) { + break; + } + } + for (ms0 = ls1; ms0 < 32; ms0++) { + if ((m & (1u << ms0)) == 0) { + break; + } + } + assert (ms0 == 32); + + if (ls1 == 32) { + tcg_out_extr(s, ret, arg, 0, ls0, 0); } else { - tcg_out32(s, LDIL | INSN_R2(ret) | - reassemble_21(lrsel((uint32_t)arg, 0))); - if (arg & 0x7ff) - tcg_out32(s, LDO | INSN_R1(ret) | INSN_R2(ret) | - reassemble_14(rrsel((uint32_t)arg, 0))); + tcg_out_mov(s, TCG_TYPE_I32, ret, arg); + tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(0) + | INSN_SHDEP_CP(31 - ls0) | INSN_DEP_LEN(ls1 - ls0)); } } -static inline void tcg_out_ld_raw(TCGContext *s, int ret, - tcg_target_long arg) +static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg) { - tcg_out32(s, LDIL | INSN_R2(ret) | - reassemble_21(lrsel((uint32_t)arg, 0))); - tcg_out32(s, LDW | INSN_R1(ret) | INSN_R2(ret) | - reassemble_14(rrsel((uint32_t)arg, 0))); + tcg_out_extr(s, ret, arg, 0, 8, 1); } -static inline void tcg_out_ld_ptr(TCGContext *s, int ret, - tcg_target_long arg) +static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg) { - tcg_out_ld_raw(s, ret, arg); + tcg_out_extr(s, ret, arg, 0, 16, 1); } -static inline void tcg_out_ldst(TCGContext *s, int ret, int addr, int offset, - int op) +static void tcg_out_shli(TCGContext *s, int ret, int arg, int count) { - if (offset == (offset & 0xfff)) - tcg_out32(s, op | INSN_R1(ret) | INSN_R2(addr) | - reassemble_14(offset)); - else { - fprintf(stderr, "unimplemented %s with offset %d\n", __func__, offset); - tcg_abort(); - } + count &= 31; + tcg_out32(s, INSN_ZDEP | INSN_R2(ret) | INSN_R1(arg) + | INSN_SHDEP_CP(31 - count) | INSN_DEP_LEN(32 - count)); } -static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, - int arg1, tcg_target_long arg2) +static void tcg_out_shl(TCGContext *s, int ret, int arg, int creg) { - fprintf(stderr, "unimplemented %s\n", __func__); - tcg_abort(); + tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI); + tcg_out_mtctl_sar(s, TCG_REG_R20); + tcg_out32(s, INSN_ZVDEP | INSN_R2(ret) | INSN_R1(arg) | INSN_DEP_LEN(32)); } -static inline void tcg_out_st(TCGContext *s, TCGType type, int ret, - int arg1, tcg_target_long arg2) +static void tcg_out_shri(TCGContext *s, int ret, int arg, int count) { - fprintf(stderr, "unimplemented %s\n", __func__); - tcg_abort(); + count &= 31; + tcg_out_extr(s, ret, arg, count, 32 - count, 0); } -static inline void tcg_out_arith(TCGContext *s, int t, int r1, int r2, int op) +static void tcg_out_shr(TCGContext *s, int ret, int arg, int creg) { - tcg_out32(s, op | INSN_T(t) | INSN_R1(r1) | INSN_R2(r2)); + tcg_out_vshd(s, ret, TCG_REG_R0, arg, creg); } -static inline void tcg_out_arithi(TCGContext *s, int t, int r1, - tcg_target_long val, int op) +static void tcg_out_sari(TCGContext *s, int ret, int arg, int count) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R20, val); - tcg_out_arith(s, t, r1, TCG_REG_R20, op); + count &= 31; + tcg_out_extr(s, ret, arg, count, 32 - count, 1); } -static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +static void tcg_out_sar(TCGContext *s, int ret, int arg, int creg) { - tcg_out_arithi(s, reg, reg, val, ARITH_ADD); + tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI); + tcg_out_mtctl_sar(s, TCG_REG_R20); + tcg_out32(s, INSN_VEXTRS | INSN_R1(ret) | INSN_R2(arg) | INSN_DEP_LEN(32)); } -static inline void tcg_out_nop(TCGContext *s) +static void tcg_out_rotli(TCGContext *s, int ret, int arg, int count) { - tcg_out32(s, ARITH_OR | INSN_T(TCG_REG_R0) | INSN_R1(TCG_REG_R0) | - INSN_R2(TCG_REG_R0)); + count &= 31; + tcg_out_shd(s, ret, arg, arg, 32 - count); } -static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg) { - tcg_out32(s, EXTRS | INSN_R1(ret) | INSN_R2(arg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(8)); +static void tcg_out_rotl(TCGContext *s, int ret, int arg, int creg) +{ + tcg_out_arithi(s, TCG_REG_R20, creg, 32, INSN_SUBI); + tcg_out_vshd(s, ret, arg, arg, TCG_REG_R20); } -static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg) { - tcg_out32(s, EXTRS | INSN_R1(ret) | INSN_R2(arg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(16)); +static void tcg_out_rotri(TCGContext *s, int ret, int arg, int count) +{ + count &= 31; + tcg_out_shd(s, ret, arg, arg, count); } -static inline void tcg_out_bswap16(TCGContext *s, int ret, int arg) { - if(ret != arg) - tcg_out_mov(s, ret, arg); - tcg_out32(s, DEP | INSN_R2(ret) | INSN_R1(ret) | - INSN_SHDEP_CP(15) | INSN_DEP_LEN(8)); - tcg_out32(s, SHD | INSN_T(ret) | INSN_R1(TCG_REG_R0) | - INSN_R2(ret) | INSN_SHDEP_CP(8)); +static void tcg_out_rotr(TCGContext *s, int ret, int arg, int creg) +{ + tcg_out_vshd(s, ret, arg, arg, creg); } -static inline void tcg_out_bswap32(TCGContext *s, int ret, int arg, int temp) { - tcg_out32(s, SHD | INSN_T(temp) | INSN_R1(arg) | - INSN_R2(arg) | INSN_SHDEP_CP(16)); - tcg_out32(s, DEP | INSN_R2(temp) | INSN_R1(temp) | - INSN_SHDEP_CP(15) | INSN_DEP_LEN(8)); - tcg_out32(s, SHD | INSN_T(ret) | INSN_R1(arg) | - INSN_R2(temp) | INSN_SHDEP_CP(8)); +static void tcg_out_bswap16(TCGContext *s, int ret, int arg, int sign) +{ + if (ret != arg) { + tcg_out_mov(s, TCG_TYPE_I32, ret, arg); /* arg = xxAB */ + } + tcg_out_dep(s, ret, ret, 16, 8); /* ret = xBAB */ + tcg_out_extr(s, ret, ret, 8, 16, sign); /* ret = ..BA */ } -static inline void tcg_out_call(TCGContext *s, void *func) +static void tcg_out_bswap32(TCGContext *s, int ret, int arg, int temp) { - uint32_t val = (uint32_t)__canonicalize_funcptr_for_compare(func); - tcg_out32(s, LDIL | INSN_R2(TCG_REG_R20) | - reassemble_21(lrsel(val, 0))); - tcg_out32(s, BLE_SR4 | INSN_R2(TCG_REG_R20) | - reassemble_17(rrsel(val, 0) >> 2)); - tcg_out_mov(s, TCG_REG_RP, TCG_REG_R31); + /* arg = ABCD */ + tcg_out_rotri(s, temp, arg, 16); /* temp = CDAB */ + tcg_out_dep(s, temp, temp, 16, 8); /* temp = CBAB */ + tcg_out_shd(s, ret, arg, temp, 8); /* ret = DCBA */ } -#if defined(CONFIG_SOFTMMU) +static void tcg_out_call(TCGContext *s, void *func) +{ + tcg_target_long val, hi, lo, disp; -#include "../../softmmu_defs.h" + val = (uint32_t)__canonicalize_funcptr_for_compare(func); + disp = (val - ((tcg_target_long)s->code_ptr + 8)) >> 2; -static void *qemu_ld_helpers[4] = { - __ldb_mmu, - __ldw_mmu, - __ldl_mmu, - __ldq_mmu, -}; + if (check_fit_tl(disp, 17)) { + tcg_out32(s, INSN_BL_N | INSN_R2(TCG_REG_RP) | reassemble_17(disp)); + } else { + hi = val >> 11; + lo = val & 0x7ff; -static void *qemu_st_helpers[4] = { - __stb_mmu, - __stw_mmu, - __stl_mmu, - __stq_mmu, -}; -#endif + tcg_out32(s, INSN_LDIL | INSN_R2(TCG_REG_R20) | reassemble_21(hi)); + tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R20) + | reassemble_17(lo >> 2)); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_RP, TCG_REG_R31); + } +} -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +static void tcg_out_xmpyu(TCGContext *s, int retl, int reth, + int arg1, int arg2) { - int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap; -#if defined(CONFIG_SOFTMMU) - uint32_t *label1_ptr, *label2_ptr; -#endif -#if TARGET_LONG_BITS == 64 -#if defined(CONFIG_SOFTMMU) - uint32_t *label3_ptr; -#endif - int addr_reg2; -#endif + /* Store both words into the stack for copy to the FPU. */ + tcg_out_ldst(s, arg1, TCG_REG_SP, STACK_TEMP_OFS, INSN_STW); + tcg_out_ldst(s, arg2, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_STW); + + /* Load both words into the FPU at the same time. We get away + with this because we can address the left and right half of the + FPU registers individually once loaded. */ + /* fldds stack_temp(sp),fr22 */ + tcg_out32(s, INSN_FLDDS | INSN_R2(TCG_REG_SP) + | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22)); + + /* xmpyu fr22r,fr22,fr22 */ + tcg_out32(s, 0x3ad64796); + + /* Store the 64-bit result back into the stack. */ + /* fstds stack_temp(sp),fr22 */ + tcg_out32(s, INSN_FSTDS | INSN_R2(TCG_REG_SP) + | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22)); + + /* Load the pieces of the result that the caller requested. */ + if (reth) { + tcg_out_ldst(s, reth, TCG_REG_SP, STACK_TEMP_OFS, INSN_LDW); + } + if (retl) { + tcg_out_ldst(s, retl, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_LDW); + } +} - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; /* suppress warning */ - addr_reg = *args++; -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#endif - mem_index = *args; - s_bits = opc & 3; +static void tcg_out_add2(TCGContext *s, int destl, int desth, + int al, int ah, int bl, int bh, int blconst) +{ + int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl); - r0 = TCG_REG_R26; - r1 = TCG_REG_R25; + if (blconst) { + tcg_out_arithi(s, tmp, al, bl, INSN_ADDI); + } else { + tcg_out_arith(s, tmp, al, bl, INSN_ADD); + } + tcg_out_arith(s, desth, ah, bh, INSN_ADDC); -#if defined(CONFIG_SOFTMMU) - tcg_out_mov(s, r1, addr_reg); + tcg_out_mov(s, TCG_TYPE_I32, destl, tmp); +} - tcg_out_mov(s, r0, addr_reg); +static void tcg_out_sub2(TCGContext *s, int destl, int desth, int al, int ah, + int bl, int bh, int alconst, int blconst) +{ + int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl); - tcg_out32(s, SHD | INSN_T(r1) | INSN_R1(TCG_REG_R0) | INSN_R2(r1) | - INSN_SHDEP_CP(TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)); + if (alconst) { + if (blconst) { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R20, bl); + bl = TCG_REG_R20; + } + tcg_out_arithi(s, tmp, bl, al, INSN_SUBI); + } else if (blconst) { + tcg_out_arithi(s, tmp, al, -bl, INSN_ADDI); + } else { + tcg_out_arith(s, tmp, al, bl, INSN_SUB); + } + tcg_out_arith(s, desth, ah, bh, INSN_SUBB); - tcg_out_arithi(s, r0, r0, TARGET_PAGE_MASK | ((1 << s_bits) - 1), - ARITH_AND); + tcg_out_mov(s, TCG_TYPE_I32, destl, tmp); +} - tcg_out_arithi(s, r1, r1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, - ARITH_AND); +static void tcg_out_branch(TCGContext *s, int label_index, int nul) +{ + TCGLabel *l = &s->labels[label_index]; + uint32_t op = nul ? INSN_BL_N : INSN_BL; - tcg_out_arith(s, r1, r1, TCG_AREG0, ARITH_ADD); - tcg_out_arithi(s, r1, r1, - offsetof(CPUState, tlb_table[mem_index][0].addr_read), - ARITH_ADD); + if (l->has_value) { + tcg_target_long val = l->u.value; - tcg_out_ldst(s, TCG_REG_R20, r1, 0, LDW); + val -= (tcg_target_long)s->code_ptr + 8; + val >>= 2; + assert(check_fit_tl(val, 17)); -#if TARGET_LONG_BITS == 32 - /* if equal, jump to label1 */ - label1_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(r0) | - INSN_COND(COND_EQUAL)); - tcg_out_mov(s, r0, addr_reg); /* delay slot */ -#else - /* if not equal, jump to label3 */ - label3_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBF | INSN_R1(TCG_REG_R20) | INSN_R2(r0) | - INSN_COND(COND_EQUAL)); - tcg_out_mov(s, r0, addr_reg); /* delay slot */ - - tcg_out_ldst(s, TCG_REG_R20, r1, 4, LDW); - - /* if equal, jump to label1 */ - label1_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(addr_reg2) | - INSN_COND(COND_EQUAL)); - tcg_out_nop(s); /* delay slot */ - - /* label3: */ - *label3_ptr |= reassemble_12((uint32_t *)s->code_ptr - label3_ptr - 2); -#endif + tcg_out32(s, op | reassemble_17(val)); + } else { + /* We need to keep the offset unchanged for retranslation. */ + uint32_t old_insn = *(uint32_t *)s->code_ptr; -#if TARGET_LONG_BITS == 32 - tcg_out_mov(s, TCG_REG_R26, addr_reg); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R25, mem_index); -#else - tcg_out_mov(s, TCG_REG_R26, addr_reg); - tcg_out_mov(s, TCG_REG_R25, addr_reg2); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R24, mem_index); -#endif + tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL17F, label_index, 0); + tcg_out32(s, op | (old_insn & 0x1f1ffdu)); + } +} - tcg_out_call(s, qemu_ld_helpers[s_bits]); +static const uint8_t tcg_cond_to_cmp_cond[10] = +{ + [TCG_COND_EQ] = COND_EQ, + [TCG_COND_NE] = COND_EQ | COND_FALSE, + [TCG_COND_LT] = COND_LT, + [TCG_COND_GE] = COND_LT | COND_FALSE, + [TCG_COND_LE] = COND_LE, + [TCG_COND_GT] = COND_LE | COND_FALSE, + [TCG_COND_LTU] = COND_LTU, + [TCG_COND_GEU] = COND_LTU | COND_FALSE, + [TCG_COND_LEU] = COND_LEU, + [TCG_COND_GTU] = COND_LEU | COND_FALSE, +}; - switch(opc) { - case 0 | 4: - tcg_out_ext8s(s, data_reg, TCG_REG_RET0); - break; - case 1 | 4: - tcg_out_ext16s(s, data_reg, TCG_REG_RET0); - break; - case 0: - case 1: - case 2: - default: - tcg_out_mov(s, data_reg, TCG_REG_RET0); - break; - case 3: - tcg_abort(); - tcg_out_mov(s, data_reg, TCG_REG_RET0); - tcg_out_mov(s, data_reg2, TCG_REG_RET1); - break; +static void tcg_out_brcond(TCGContext *s, int cond, TCGArg c1, + TCGArg c2, int c2const, int label_index) +{ + TCGLabel *l = &s->labels[label_index]; + int op, pacond; + + /* Note that COMIB operates as if the immediate is the first + operand. We model brcond with the immediate in the second + to better match what targets are likely to give us. For + consistency, model COMB with reversed operands as well. */ + pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)]; + + if (c2const) { + op = (pacond & COND_FALSE ? INSN_COMIBF : INSN_COMIBT); + op |= INSN_IM5(c2); + } else { + op = (pacond & COND_FALSE ? INSN_COMBF : INSN_COMBT); + op |= INSN_R1(c2); } + op |= INSN_R2(c1); + op |= INSN_COND(pacond & 7); - /* jump to label2 */ - label2_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, BL | INSN_R2(TCG_REG_R0) | 2); + if (l->has_value) { + tcg_target_long val = l->u.value; - /* label1: */ - *label1_ptr |= reassemble_12((uint32_t *)s->code_ptr - label1_ptr - 2); + val -= (tcg_target_long)s->code_ptr + 8; + val >>= 2; + assert(check_fit_tl(val, 12)); - tcg_out_arithi(s, TCG_REG_R20, r1, - offsetof(CPUTLBEntry, addend) - offsetof(CPUTLBEntry, addr_read), - ARITH_ADD); - tcg_out_ldst(s, TCG_REG_R20, TCG_REG_R20, 0, LDW); - tcg_out_arith(s, r0, r0, TCG_REG_R20, ARITH_ADD); -#else - r0 = addr_reg; -#endif + /* ??? Assume that all branches to defined labels are backward. + Which means that if the nul bit is set, the delay slot is + executed if the branch is taken, and not executed in fallthru. */ + tcg_out32(s, op | reassemble_12(val)); + tcg_out_nop(s); + } else { + /* We need to keep the offset unchanged for retranslation. */ + uint32_t old_insn = *(uint32_t *)s->code_ptr; + + tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL12F, label_index, 0); + /* ??? Assume that all branches to undefined labels are forward. + Which means that if the nul bit is set, the delay slot is + not executed if the branch is taken, which is what we want. */ + tcg_out32(s, op | 2 | (old_insn & 0x1ffdu)); + } +} -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 0; -#else - bswap = 1; -#endif - switch (opc) { - case 0: - tcg_out_ldst(s, data_reg, r0, 0, LDB); - break; - case 0 | 4: - tcg_out_ldst(s, data_reg, r0, 0, LDB); - tcg_out_ext8s(s, data_reg, data_reg); - break; - case 1: - tcg_out_ldst(s, data_reg, r0, 0, LDH); - if (bswap) - tcg_out_bswap16(s, data_reg, data_reg); - break; - case 1 | 4: - tcg_out_ldst(s, data_reg, r0, 0, LDH); - if (bswap) - tcg_out_bswap16(s, data_reg, data_reg); - tcg_out_ext16s(s, data_reg, data_reg); - break; - case 2: - tcg_out_ldst(s, data_reg, r0, 0, LDW); - if (bswap) - tcg_out_bswap32(s, data_reg, data_reg, TCG_REG_R20); - break; - case 3: - tcg_abort(); - if (!bswap) { - tcg_out_ldst(s, data_reg, r0, 0, LDW); - tcg_out_ldst(s, data_reg2, r0, 4, LDW); - } else { - tcg_out_ldst(s, data_reg, r0, 4, LDW); - tcg_out_bswap32(s, data_reg, data_reg, TCG_REG_R20); - tcg_out_ldst(s, data_reg2, r0, 0, LDW); - tcg_out_bswap32(s, data_reg2, data_reg2, TCG_REG_R20); - } - break; - default: - tcg_abort(); +static void tcg_out_comclr(TCGContext *s, int cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + int op, pacond; + + /* Note that COMICLR operates as if the immediate is the first + operand. We model setcond with the immediate in the second + to better match what targets are likely to give us. For + consistency, model COMCLR with reversed operands as well. */ + pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)]; + + if (c2const) { + op = INSN_COMICLR | INSN_R2(c1) | INSN_R1(ret) | INSN_IM11(c2); + } else { + op = INSN_COMCLR | INSN_R2(c1) | INSN_R1(c2) | INSN_T(ret); } + op |= INSN_COND(pacond & 7); + op |= pacond & COND_FALSE ? 1 << 12 : 0; -#if defined(CONFIG_SOFTMMU) - /* label2: */ - *label2_ptr |= reassemble_17((uint32_t *)s->code_ptr - label2_ptr - 2); -#endif + tcg_out32(s, op); } -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +static void tcg_out_brcond2(TCGContext *s, int cond, TCGArg al, TCGArg ah, + TCGArg bl, int blconst, TCGArg bh, int bhconst, + int label_index) { - int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap; -#if defined(CONFIG_SOFTMMU) - uint32_t *label1_ptr, *label2_ptr; -#endif -#if TARGET_LONG_BITS == 64 -#if defined(CONFIG_SOFTMMU) - uint32_t *label3_ptr; -#endif - int addr_reg2; -#endif + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + tcg_out_comclr(s, tcg_invert_cond(cond), TCG_REG_R0, al, bl, blconst); + tcg_out_brcond(s, cond, ah, bh, bhconst, label_index); + break; - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; /* suppress warning */ - addr_reg = *args++; -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#endif - mem_index = *args; + default: + tcg_out_brcond(s, cond, ah, bh, bhconst, label_index); + tcg_out_comclr(s, TCG_COND_NE, TCG_REG_R0, ah, bh, bhconst); + tcg_out_brcond(s, tcg_unsigned_cond(cond), + al, bl, blconst, label_index); + break; + } +} + +static void tcg_out_setcond(TCGContext *s, int cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + tcg_out_comclr(s, tcg_invert_cond(cond), ret, c1, c2, c2const); + tcg_out_movi(s, TCG_TYPE_I32, ret, 1); +} - s_bits = opc; +static void tcg_out_setcond2(TCGContext *s, int cond, TCGArg ret, + TCGArg al, TCGArg ah, TCGArg bl, int blconst, + TCGArg bh, int bhconst) +{ + int scratch = TCG_REG_R20; + + if (ret != al && ret != ah + && (blconst || ret != bl) + && (bhconst || ret != bh)) { + scratch = ret; + } - r0 = TCG_REG_R26; - r1 = TCG_REG_R25; + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + tcg_out_setcond(s, cond, scratch, al, bl, blconst); + tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst); + tcg_out_movi(s, TCG_TYPE_I32, scratch, cond == TCG_COND_NE); + break; + + default: + tcg_out_setcond(s, tcg_unsigned_cond(cond), scratch, al, bl, blconst); + tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst); + tcg_out_movi(s, TCG_TYPE_I32, scratch, 0); + tcg_out_comclr(s, cond, TCG_REG_R0, ah, bh, bhconst); + tcg_out_movi(s, TCG_TYPE_I32, scratch, 1); + break; + } + + tcg_out_mov(s, TCG_TYPE_I32, ret, scratch); +} #if defined(CONFIG_SOFTMMU) - tcg_out_mov(s, r1, addr_reg); +#include "../../softmmu_defs.h" - tcg_out_mov(s, r0, addr_reg); +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; + +/* Load and compare a TLB entry, and branch if TLB miss. OFFSET is set to + the offset of the first ADDR_READ or ADDR_WRITE member of the appropriate + TLB for the memory index. The return value is the offset from ENV + contained in R1 afterward (to be used when loading ADDEND); if the + return value is 0, R1 is not used. */ - tcg_out32(s, SHD | INSN_T(r1) | INSN_R1(TCG_REG_R0) | INSN_R2(r1) | - INSN_SHDEP_CP(TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)); +static int tcg_out_tlb_read(TCGContext *s, int r0, int r1, int addrlo, + int addrhi, int s_bits, int lab_miss, int offset) +{ + int ret; + + /* Extracting the index into the TLB. The "normal C operation" is + r1 = addr_reg >> TARGET_PAGE_BITS; + r1 &= CPU_TLB_SIZE - 1; + r1 <<= CPU_TLB_ENTRY_BITS; + What this does is extract CPU_TLB_BITS beginning at TARGET_PAGE_BITS + and place them at CPU_TLB_ENTRY_BITS. We can combine the first two + operations with an EXTRU. Unfortunately, the current value of + CPU_TLB_ENTRY_BITS is > 3, so we can't merge that shift with the + add that follows. */ + tcg_out_extr(s, r1, addrlo, TARGET_PAGE_BITS, CPU_TLB_BITS, 0); + tcg_out_shli(s, r1, r1, CPU_TLB_ENTRY_BITS); + tcg_out_arith(s, r1, r1, TCG_AREG0, INSN_ADDL); + + /* Make sure that both the addr_{read,write} and addend can be + read with a 14-bit offset from the same base register. */ + if (check_fit_tl(offset + CPU_TLB_SIZE, 14)) { + ret = 0; + } else { + ret = (offset + 0x400) & ~0x7ff; + offset = ret - offset; + tcg_out_addi2(s, TCG_REG_R1, r1, ret); + r1 = TCG_REG_R1; + } - tcg_out_arithi(s, r0, r0, TARGET_PAGE_MASK | ((1 << s_bits) - 1), - ARITH_AND); + /* Load the entry from the computed slot. */ + if (TARGET_LONG_BITS == 64) { + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R23, r1, offset); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset + 4); + } else { + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset); + } - tcg_out_arithi(s, r1, r1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, - ARITH_AND); + /* Compute the value that ought to appear in the TLB for a hit, namely, the page + of the address. We include the low N bits of the address to catch unaligned + accesses and force them onto the slow path. Do this computation after having + issued the load from the TLB slot to give the load time to complete. */ + tcg_out_andi(s, r0, addrlo, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - tcg_out_arith(s, r1, r1, TCG_AREG0, ARITH_ADD); - tcg_out_arithi(s, r1, r1, - offsetof(CPUState, tlb_table[mem_index][0].addr_write), - ARITH_ADD); + /* If not equal, jump to lab_miss. */ + if (TARGET_LONG_BITS == 64) { + tcg_out_brcond2(s, TCG_COND_NE, TCG_REG_R20, TCG_REG_R23, + r0, 0, addrhi, 0, lab_miss); + } else { + tcg_out_brcond(s, TCG_COND_NE, TCG_REG_R20, r0, 0, lab_miss); + } - tcg_out_ldst(s, TCG_REG_R20, r1, 0, LDW); + return ret; +} +#endif -#if TARGET_LONG_BITS == 32 - /* if equal, jump to label1 */ - label1_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(r0) | - INSN_COND(COND_EQUAL)); - tcg_out_mov(s, r0, addr_reg); /* delay slot */ +static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo_reg, int datahi_reg, + int addr_reg, int addend_reg, int opc) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 0; #else - /* if not equal, jump to label3 */ - label3_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBF | INSN_R1(TCG_REG_R20) | INSN_R2(r0) | - INSN_COND(COND_EQUAL)); - tcg_out_mov(s, r0, addr_reg); /* delay slot */ - - tcg_out_ldst(s, TCG_REG_R20, r1, 4, LDW); - - /* if equal, jump to label1 */ - label1_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(addr_reg2) | - INSN_COND(COND_EQUAL)); - tcg_out_nop(s); /* delay slot */ - - /* label3: */ - *label3_ptr |= reassemble_12((uint32_t *)s->code_ptr - label3_ptr - 2); + const int bswap = 1; #endif - tcg_out_mov(s, TCG_REG_R26, addr_reg); -#if TARGET_LONG_BITS == 64 - tcg_out_mov(s, TCG_REG_R25, addr_reg2); - if (opc == 3) { - tcg_abort(); - tcg_out_mov(s, TCG_REG_R24, data_reg); - tcg_out_mov(s, TCG_REG_R23, data_reg2); - /* TODO: push mem_index */ - tcg_abort(); - } else { - switch(opc) { - case 0: - tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R24) | INSN_R2(data_reg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(8)); - break; - case 1: - tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R24) | INSN_R2(data_reg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(16)); - break; - case 2: - tcg_out_mov(s, TCG_REG_R24, data_reg); - break; + switch (opc) { + case 0: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX); + break; + case 0 | 4: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX); + tcg_out_ext8s(s, datalo_reg, datalo_reg); + break; + case 1: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX); + if (bswap) { + tcg_out_bswap16(s, datalo_reg, datalo_reg, 0); } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R23, mem_index); - } -#else - if (opc == 3) { - tcg_abort(); - tcg_out_mov(s, TCG_REG_R25, data_reg); - tcg_out_mov(s, TCG_REG_R24, data_reg2); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R23, mem_index); - } else { - switch(opc) { - case 0: - tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R25) | INSN_R2(data_reg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(8)); - break; - case 1: - tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R25) | INSN_R2(data_reg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(16)); - break; - case 2: - tcg_out_mov(s, TCG_REG_R25, data_reg); - break; + break; + case 1 | 4: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX); + if (bswap) { + tcg_out_bswap16(s, datalo_reg, datalo_reg, 1); + } else { + tcg_out_ext16s(s, datalo_reg, datalo_reg); } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R24, mem_index); + break; + case 2: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDWX); + if (bswap) { + tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20); + } + break; + case 3: + if (bswap) { + int t = datahi_reg; + datahi_reg = datalo_reg; + datalo_reg = t; + } + /* We can't access the low-part with a reg+reg addressing mode, + so perform the addition now and use reg_ofs addressing mode. */ + if (addend_reg != TCG_REG_R0) { + tcg_out_arith(s, TCG_REG_R20, addr_reg, addend_reg, INSN_ADD); + addr_reg = TCG_REG_R20; + } + /* Make sure not to clobber the base register. */ + if (datahi_reg == addr_reg) { + tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW); + tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW); + } else { + tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW); + tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW); + } + if (bswap) { + tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20); + tcg_out_bswap32(s, datahi_reg, datahi_reg, TCG_REG_R20); + } + break; + default: + tcg_abort(); } -#endif - tcg_out_call(s, qemu_st_helpers[s_bits]); +} + +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +{ + int datalo_reg = *args++; + /* Note that datahi_reg is only used for 64-bit loads. */ + int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0); + int addrlo_reg = *args++; + +#if defined(CONFIG_SOFTMMU) + /* Note that addrhi_reg is only used for 64-bit guests. */ + int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0); + int mem_index = *args; + int lab1, lab2, argreg, offset; + + lab1 = gen_new_label(); + lab2 = gen_new_label(); - /* jump to label2 */ - label2_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, BL | INSN_R2(TCG_REG_R0) | 2); + offset = offsetof(CPUState, tlb_table[mem_index][0].addr_read); + offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg, addrhi_reg, + opc & 3, lab1, offset); + /* TLB Hit. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, (offset ? TCG_REG_R1 : TCG_REG_R25), + offsetof(CPUState, tlb_table[mem_index][0].addend) - offset); + tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg, TCG_REG_R20, opc); + tcg_out_branch(s, lab2, 1); + + /* TLB Miss. */ /* label1: */ - *label1_ptr |= reassemble_12((uint32_t *)s->code_ptr - label1_ptr - 2); + tcg_out_label(s, lab1, (tcg_target_long)s->code_ptr); + + argreg = TCG_REG_R26; + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrlo_reg); + if (TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrhi_reg); + } + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + + tcg_out_call(s, qemu_ld_helpers[opc & 3]); + + switch (opc) { + case 0: + tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xff); + break; + case 0 | 4: + tcg_out_ext8s(s, datalo_reg, TCG_REG_RET0); + break; + case 1: + tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xffff); + break; + case 1 | 4: + tcg_out_ext16s(s, datalo_reg, TCG_REG_RET0); + break; + case 2: + case 2 | 4: + tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET0); + break; + case 3: + tcg_out_mov(s, TCG_TYPE_I32, datahi_reg, TCG_REG_RET0); + tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET1); + break; + default: + tcg_abort(); + } - tcg_out_arithi(s, TCG_REG_R20, r1, - offsetof(CPUTLBEntry, addend) - offsetof(CPUTLBEntry, addr_write), - ARITH_ADD); - tcg_out_ldst(s, TCG_REG_R20, TCG_REG_R20, 0, LDW); - tcg_out_arith(s, r0, r0, TCG_REG_R20, ARITH_ADD); + /* label2: */ + tcg_out_label(s, lab2, (tcg_target_long)s->code_ptr); #else - r0 = addr_reg; + tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg, + (GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_R0), opc); #endif +} +static void tcg_out_qemu_st_direct(TCGContext *s, int datalo_reg, int datahi_reg, + int addr_reg, int opc) +{ #ifdef TARGET_WORDS_BIGENDIAN - bswap = 0; + const int bswap = 0; #else - bswap = 1; + const int bswap = 1; #endif + switch (opc) { case 0: - tcg_out_ldst(s, data_reg, r0, 0, STB); + tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STB); break; case 1: if (bswap) { - tcg_out_bswap16(s, TCG_REG_R20, data_reg); - data_reg = TCG_REG_R20; + tcg_out_bswap16(s, TCG_REG_R20, datalo_reg, 0); + datalo_reg = TCG_REG_R20; } - tcg_out_ldst(s, data_reg, r0, 0, STH); + tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STH); break; case 2: if (bswap) { - tcg_out_bswap32(s, TCG_REG_R20, data_reg, TCG_REG_R20); - data_reg = TCG_REG_R20; + tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20); + datalo_reg = TCG_REG_R20; } - tcg_out_ldst(s, data_reg, r0, 0, STW); + tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STW); break; case 3: + if (bswap) { + tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20); + tcg_out_bswap32(s, TCG_REG_R23, datahi_reg, TCG_REG_R23); + datahi_reg = TCG_REG_R20; + datalo_reg = TCG_REG_R23; + } + tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_STW); + tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_STW); + break; + default: tcg_abort(); - if (!bswap) { - tcg_out_ldst(s, data_reg, r0, 0, STW); - tcg_out_ldst(s, data_reg2, r0, 4, STW); + } + +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +{ + int datalo_reg = *args++; + /* Note that datahi_reg is only used for 64-bit loads. */ + int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0); + int addrlo_reg = *args++; + +#if defined(CONFIG_SOFTMMU) + /* Note that addrhi_reg is only used for 64-bit guests. */ + int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0); + int mem_index = *args; + int lab1, lab2, argreg, offset; + + lab1 = gen_new_label(); + lab2 = gen_new_label(); + + offset = offsetof(CPUState, tlb_table[mem_index][0].addr_write); + offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg, addrhi_reg, + opc, lab1, offset); + + /* TLB Hit. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, (offset ? TCG_REG_R1 : TCG_REG_R25), + offsetof(CPUState, tlb_table[mem_index][0].addend) - offset); + + /* There are no indexed stores, so we must do this addition explitly. + Careful to avoid R20, which is used for the bswaps to follow. */ + tcg_out_arith(s, TCG_REG_R31, addrlo_reg, TCG_REG_R20, INSN_ADDL); + tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, TCG_REG_R31, opc); + tcg_out_branch(s, lab2, 1); + + /* TLB Miss. */ + /* label1: */ + tcg_out_label(s, lab1, (tcg_target_long)s->code_ptr); + + argreg = TCG_REG_R26; + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrlo_reg); + if (TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrhi_reg); + } + + switch(opc) { + case 0: + tcg_out_andi(s, argreg--, datalo_reg, 0xff); + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + break; + case 1: + tcg_out_andi(s, argreg--, datalo_reg, 0xffff); + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, argreg--, datalo_reg); + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + break; + case 3: + /* Because of the alignment required by the 64-bit data argument, + we will always use R23/R24. Also, we will always run out of + argument registers for storing mem_index, so that will have + to go on the stack. */ + if (mem_index == 0) { + argreg = TCG_REG_R0; } else { - tcg_out_bswap32(s, TCG_REG_R20, data_reg, TCG_REG_R20); - tcg_out_ldst(s, TCG_REG_R20, r0, 4, STW); - tcg_out_bswap32(s, TCG_REG_R20, data_reg2, TCG_REG_R20); - tcg_out_ldst(s, TCG_REG_R20, r0, 0, STW); + argreg = TCG_REG_R20; + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); } + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R23, datahi_reg); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R24, datalo_reg); + tcg_out_st(s, TCG_TYPE_I32, argreg, TCG_REG_SP, + TCG_TARGET_CALL_STACK_OFFSET - 4); break; default: tcg_abort(); } -#if defined(CONFIG_SOFTMMU) + tcg_out_call(s, qemu_st_helpers[opc]); + /* label2: */ - *label2_ptr |= reassemble_17((uint32_t *)s->code_ptr - label2_ptr - 2); + tcg_out_label(s, lab2, (tcg_target_long)s->code_ptr); +#else + /* There are no indexed stores, so if GUEST_BASE is set we must do the add + explicitly. Careful to avoid R20, which is used for the bswaps to follow. */ + if (GUEST_BASE != 0) { + tcg_out_arith(s, TCG_REG_R31, addrlo_reg, TCG_GUEST_BASE_REG, INSN_ADDL); + addrlo_reg = TCG_REG_R31; + } + tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, addrlo_reg, opc); #endif } -static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, - const int *const_args) +static void tcg_out_exit_tb(TCGContext *s, TCGArg arg) +{ + if (!check_fit_tl(arg, 14)) { + uint32_t hi, lo; + hi = arg & ~0x7ff; + lo = arg & 0x7ff; + if (lo) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, hi); + tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18)); + tcg_out_addi(s, TCG_REG_RET0, lo); + return; + } + arg = hi; + } + tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18)); + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, arg); +} + +static void tcg_out_goto_tb(TCGContext *s, TCGArg arg) { - int c; + if (s->tb_jmp_offset) { + /* direct jump method */ + fprintf(stderr, "goto_tb direct\n"); + tcg_abort(); + } else { + /* indirect jump method */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, TCG_REG_R0, + (tcg_target_long)(s->tb_next + arg)); + tcg_out32(s, INSN_BV_N | INSN_R2(TCG_REG_R20)); + } + s->tb_next_offset[arg] = s->code_ptr - s->code_buf; +} +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) +{ switch (opc) { case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, args[0]); - tcg_out32(s, BV_N | INSN_R2(TCG_REG_R18)); + tcg_out_exit_tb(s, args[0]); break; case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* direct jump method */ - fprintf(stderr, "goto_tb direct\n"); - tcg_abort(); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R20, args[0]); - tcg_out32(s, BV_N | INSN_R2(TCG_REG_R20)); - s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; - } else { - /* indirect jump method */ - tcg_out_ld_ptr(s, TCG_REG_R20, - (tcg_target_long)(s->tb_next + args[0])); - tcg_out32(s, BV_N | INSN_R2(TCG_REG_R20)); - } - s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + tcg_out_goto_tb(s, args[0]); break; + case INDEX_op_call: - tcg_out32(s, BLE_SR4 | INSN_R2(args[0])); - tcg_out_mov(s, TCG_REG_RP, TCG_REG_R31); + if (const_args[0]) { + tcg_out_call(s, (void *)args[0]); + } else { + /* ??? FIXME: the value in the register in args[0] is almost + certainly a procedure descriptor, not a code address. We + probably need to use the millicode $$dyncall routine. */ + tcg_abort(); + } break; + case INDEX_op_jmp: fprintf(stderr, "unimplemented jmp\n"); tcg_abort(); break; + case INDEX_op_br: - fprintf(stderr, "unimplemented br\n"); - tcg_abort(); + tcg_out_branch(s, args[0], 1); break; + case INDEX_op_movi_i32: tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]); break; case INDEX_op_ld8u_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDB); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB); break; case INDEX_op_ld8s_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDB); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB); tcg_out_ext8s(s, args[0], args[0]); break; case INDEX_op_ld16u_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDH); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH); break; case INDEX_op_ld16s_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDH); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH); tcg_out_ext16s(s, args[0], args[0]); break; case INDEX_op_ld_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDW); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDW); break; case INDEX_op_st8_i32: - tcg_out_ldst(s, args[0], args[1], args[2], STB); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_STB); break; case INDEX_op_st16_i32: - tcg_out_ldst(s, args[0], args[1], args[2], STH); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_STH); break; case INDEX_op_st_i32: - tcg_out_ldst(s, args[0], args[1], args[2], STW); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_STW); + break; + + case INDEX_op_add_i32: + if (const_args[2]) { + tcg_out_addi2(s, args[0], args[1], args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_ADDL); + } break; case INDEX_op_sub_i32: - c = ARITH_SUB; - goto gen_arith; + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1] - args[2]); + } else { + /* Recall that SUBI is a reversed subtract. */ + tcg_out_arithi(s, args[0], args[2], args[1], INSN_SUBI); + } + } else if (const_args[2]) { + tcg_out_addi2(s, args[0], args[1], -args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_SUB); + } + break; + case INDEX_op_and_i32: - c = ARITH_AND; - goto gen_arith; + if (const_args[2]) { + tcg_out_andi(s, args[0], args[1], args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_AND); + } + break; + case INDEX_op_or_i32: - c = ARITH_OR; - goto gen_arith; + if (const_args[2]) { + tcg_out_ori(s, args[0], args[1], args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_OR); + } + break; + case INDEX_op_xor_i32: - c = ARITH_XOR; - goto gen_arith; - case INDEX_op_add_i32: - c = ARITH_ADD; - goto gen_arith; + tcg_out_arith(s, args[0], args[1], args[2], INSN_XOR); + break; + + case INDEX_op_andc_i32: + if (const_args[2]) { + tcg_out_andi(s, args[0], args[1], ~args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_ANDCM); + } + break; case INDEX_op_shl_i32: - tcg_out32(s, SUBI | INSN_R1(TCG_REG_R20) | INSN_R2(args[2]) | - lowsignext(0x1f, 0, 11)); - tcg_out32(s, MTCTL | INSN_R2(11) | INSN_R1(TCG_REG_R20)); - tcg_out32(s, ZVDEP | INSN_R2(args[0]) | INSN_R1(args[1]) | - INSN_DEP_LEN(32)); + if (const_args[2]) { + tcg_out_shli(s, args[0], args[1], args[2]); + } else { + tcg_out_shl(s, args[0], args[1], args[2]); + } break; + case INDEX_op_shr_i32: - tcg_out32(s, MTCTL | INSN_R2(11) | INSN_R1(args[2])); - tcg_out32(s, VSHD | INSN_T(args[0]) | INSN_R1(TCG_REG_R0) | - INSN_R2(args[1])); + if (const_args[2]) { + tcg_out_shri(s, args[0], args[1], args[2]); + } else { + tcg_out_shr(s, args[0], args[1], args[2]); + } break; + case INDEX_op_sar_i32: - tcg_out32(s, SUBI | INSN_R1(TCG_REG_R20) | INSN_R2(args[2]) | - lowsignext(0x1f, 0, 11)); - tcg_out32(s, MTCTL | INSN_R2(11) | INSN_R1(TCG_REG_R20)); - tcg_out32(s, VEXTRS | INSN_R1(args[0]) | INSN_R2(args[1]) | - INSN_DEP_LEN(32)); + if (const_args[2]) { + tcg_out_sari(s, args[0], args[1], args[2]); + } else { + tcg_out_sar(s, args[0], args[1], args[2]); + } + break; + + case INDEX_op_rotl_i32: + if (const_args[2]) { + tcg_out_rotli(s, args[0], args[1], args[2]); + } else { + tcg_out_rotl(s, args[0], args[1], args[2]); + } + break; + + case INDEX_op_rotr_i32: + if (const_args[2]) { + tcg_out_rotri(s, args[0], args[1], args[2]); + } else { + tcg_out_rotr(s, args[0], args[1], args[2]); + } break; case INDEX_op_mul_i32: - fprintf(stderr, "unimplemented mul\n"); - tcg_abort(); + tcg_out_xmpyu(s, args[0], TCG_REG_R0, args[1], args[2]); break; case INDEX_op_mulu2_i32: - fprintf(stderr, "unimplemented mulu2\n"); - tcg_abort(); + tcg_out_xmpyu(s, args[0], args[1], args[2], args[3]); break; - case INDEX_op_div2_i32: - fprintf(stderr, "unimplemented div2\n"); - tcg_abort(); + + case INDEX_op_bswap16_i32: + tcg_out_bswap16(s, args[0], args[1], 0); break; - case INDEX_op_divu2_i32: - fprintf(stderr, "unimplemented divu2\n"); - tcg_abort(); + case INDEX_op_bswap32_i32: + tcg_out_bswap32(s, args[0], args[1], TCG_REG_R20); + break; + + case INDEX_op_not_i32: + tcg_out_arithi(s, args[0], args[1], -1, INSN_SUBI); + break; + case INDEX_op_ext8s_i32: + tcg_out_ext8s(s, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tcg_out_ext16s(s, args[0], args[1]); break; case INDEX_op_brcond_i32: - fprintf(stderr, "unimplemented brcond\n"); - tcg_abort(); + tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], args[3]); + break; + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args[4], args[0], args[1], + args[2], const_args[2], + args[3], const_args[3], args[5]); + break; + + case INDEX_op_setcond_i32: + tcg_out_setcond(s, args[3], args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args[5], args[0], args[1], args[2], + args[3], const_args[3], args[4], const_args[4]); + break; + + case INDEX_op_add2_i32: + tcg_out_add2(s, args[0], args[1], args[2], args[3], + args[4], args[5], const_args[4]); + break; + + case INDEX_op_sub2_i32: + tcg_out_sub2(s, args[0], args[1], args[2], args[3], + args[4], args[5], const_args[2], const_args[4]); break; case INDEX_op_qemu_ld8u: @@ -863,9 +1470,12 @@ static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, case INDEX_op_qemu_ld16s: tcg_out_qemu_ld(s, args, 1 | 4); break; - case INDEX_op_qemu_ld32u: + case INDEX_op_qemu_ld32: tcg_out_qemu_ld(s, args, 2); break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; case INDEX_op_qemu_st8: tcg_out_qemu_st(s, args, 0); @@ -876,87 +1486,186 @@ static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, case INDEX_op_qemu_st32: tcg_out_qemu_st(s, args, 2); break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; default: fprintf(stderr, "unknown opcode 0x%x\n", opc); tcg_abort(); } - return; - -gen_arith: - tcg_out_arith(s, args[0], args[1], args[2], c); } static const TCGTargetOpDef hppa_op_defs[] = { { INDEX_op_exit_tb, { } }, { INDEX_op_goto_tb, { } }, - { INDEX_op_call, { "r" } }, + { INDEX_op_call, { "ri" } }, { INDEX_op_jmp, { "r" } }, { INDEX_op_br, { } }, { INDEX_op_mov_i32, { "r", "r" } }, { INDEX_op_movi_i32, { "r" } }, + { INDEX_op_ld8u_i32, { "r", "r" } }, { INDEX_op_ld8s_i32, { "r", "r" } }, { INDEX_op_ld16u_i32, { "r", "r" } }, { INDEX_op_ld16s_i32, { "r", "r" } }, { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_st8_i32, { "r", "r" } }, - { INDEX_op_st16_i32, { "r", "r" } }, - { INDEX_op_st_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, - { INDEX_op_add_i32, { "r", "r", "r" } }, - { INDEX_op_sub_i32, { "r", "r", "r" } }, - { INDEX_op_and_i32, { "r", "r", "r" } }, - { INDEX_op_or_i32, { "r", "r", "r" } }, - { INDEX_op_xor_i32, { "r", "r", "r" } }, + { INDEX_op_add_i32, { "r", "rZ", "ri" } }, + { INDEX_op_sub_i32, { "r", "rI", "ri" } }, + { INDEX_op_and_i32, { "r", "rZ", "rM" } }, + { INDEX_op_or_i32, { "r", "rZ", "rO" } }, + { INDEX_op_xor_i32, { "r", "rZ", "rZ" } }, + /* Note that the second argument will be inverted, which means + we want a constant whose inversion matches M, and that O = ~M. + See the implementation of and_mask_p. */ + { INDEX_op_andc_i32, { "r", "rZ", "rO" } }, - { INDEX_op_shl_i32, { "r", "r", "r" } }, - { INDEX_op_shr_i32, { "r", "r", "r" } }, - { INDEX_op_sar_i32, { "r", "r", "r" } }, + { INDEX_op_mul_i32, { "r", "r", "r" } }, + { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, - { INDEX_op_brcond_i32, { "r", "r" } }, + { INDEX_op_shl_i32, { "r", "r", "ri" } }, + { INDEX_op_shr_i32, { "r", "r", "ri" } }, + { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + { INDEX_op_not_i32, { "r", "r" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + + { INDEX_op_brcond_i32, { "rZ", "rJ" } }, + { INDEX_op_brcond2_i32, { "rZ", "rZ", "rJ", "rJ" } }, + + { INDEX_op_setcond_i32, { "r", "rZ", "rI" } }, + { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rI", "rI" } }, + + { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rI", "rZ" } }, + { INDEX_op_sub2_i32, { "r", "r", "rI", "rZ", "rK", "rZ" } }, #if TARGET_LONG_BITS == 32 { INDEX_op_qemu_ld8u, { "r", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L" } }, - { INDEX_op_qemu_st8, { "L", "L" } }, - { INDEX_op_qemu_st16, { "L", "L" } }, - { INDEX_op_qemu_st32, { "L", "L" } }, - { INDEX_op_qemu_st64, { "L", "L", "L" } }, + { INDEX_op_qemu_st8, { "LZ", "L" } }, + { INDEX_op_qemu_st16, { "LZ", "L" } }, + { INDEX_op_qemu_st32, { "LZ", "L" } }, + { INDEX_op_qemu_st64, { "LZ", "LZ", "L" } }, #else { INDEX_op_qemu_ld8u, { "r", "L", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } }, - { INDEX_op_qemu_st8, { "L", "L", "L" } }, - { INDEX_op_qemu_st16, { "L", "L", "L" } }, - { INDEX_op_qemu_st32, { "L", "L", "L" } }, - { INDEX_op_qemu_st64, { "L", "L", "L", "L" } }, + { INDEX_op_qemu_st8, { "LZ", "L", "L" } }, + { INDEX_op_qemu_st16, { "LZ", "L", "L" } }, + { INDEX_op_qemu_st32, { "LZ", "L", "L" } }, + { INDEX_op_qemu_st64, { "LZ", "LZ", "L", "L" } }, #endif { -1 }, }; -void tcg_target_init(TCGContext *s) +static int tcg_target_callee_save_regs[] = { + /* R2, the return address register, is saved specially + in the caller's frame. */ + /* R3, the frame pointer, is not currently modified. */ + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + /* R17 is the global env, so no need to save. */ + TCG_REG_R18 +}; + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int frame_size, i; + + /* Allocate space for the fixed frame marker. */ + frame_size = -TCG_TARGET_CALL_STACK_OFFSET; + frame_size += TCG_TARGET_STATIC_CALL_ARGS_SIZE; + + /* Allocate space for the saved registers. */ + frame_size += ARRAY_SIZE(tcg_target_callee_save_regs) * 4; + + /* Align the allocated space. */ + frame_size = ((frame_size + TCG_TARGET_STACK_ALIGN - 1) + & -TCG_TARGET_STACK_ALIGN); + + /* The return address is stored in the caller's frame. */ + tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -20); + + /* Allocate stack frame, saving the first register at the same time. */ + tcg_out_ldst(s, tcg_target_callee_save_regs[0], + TCG_REG_SP, frame_size, INSN_STWM); + + /* Save all callee saved registers. */ + for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + tcg_out_st(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i], + TCG_REG_SP, -frame_size + i * 4); + } + +#ifdef CONFIG_USE_GUEST_BASE + if (GUEST_BASE != 0) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } +#endif + + /* Jump to TB, and adjust R18 to be the return address. */ + tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R26)); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R18, TCG_REG_R31); + + /* Restore callee saved registers. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -frame_size - 20); + for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + tcg_out_ld(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i], + TCG_REG_SP, -frame_size + i * 4); + } + + /* Deallocate stack frame and return. */ + tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_RP)); + tcg_out_ldst(s, tcg_target_callee_save_regs[0], + TCG_REG_SP, -frame_size, INSN_LDWM); +} + +static void tcg_target_init(TCGContext *s) { tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_R20) | - (1 << TCG_REG_R21) | - (1 << TCG_REG_R22) | - (1 << TCG_REG_R23) | - (1 << TCG_REG_R24) | - (1 << TCG_REG_R25) | - (1 << TCG_REG_R26)); + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET0); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET1); tcg_regset_clear(s->reserved_regs); tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* hardwired to zero */ diff --git a/tcg/hppa/tcg-target.h b/tcg/hppa/tcg-target.h index 7ab6f0c..a5cc440 100644 --- a/tcg/hppa/tcg-target.h +++ b/tcg/hppa/tcg-target.h @@ -69,135 +69,51 @@ enum { TCG_REG_R31, }; +#define TCG_CT_CONST_0 0x0100 +#define TCG_CT_CONST_S5 0x0200 +#define TCG_CT_CONST_S11 0x0400 +#define TCG_CT_CONST_MS11 0x0800 +#define TCG_CT_CONST_AND 0x1000 +#define TCG_CT_CONST_OR 0x2000 + /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_SP -#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_STACK_ALIGN 64 +#define TCG_TARGET_CALL_STACK_OFFSET -48 +#define TCG_TARGET_STATIC_CALL_ARGS_SIZE 8*4 +#define TCG_TARGET_CALL_ALIGN_ARGS 1 #define TCG_TARGET_STACK_GROWSUP /* optional instructions */ -//#define TCG_TARGET_HAS_ext8s_i32 -//#define TCG_TARGET_HAS_ext16s_i32 -//#define TCG_TARGET_HAS_bswap16_i32 -//#define TCG_TARGET_HAS_bswap32_i32 +// #define TCG_TARGET_HAS_div_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_andc_i32 +// #define TCG_TARGET_HAS_orc_i32 + +/* optional instructions automatically implemented */ +#undef TCG_TARGET_HAS_neg_i32 /* sub rd, 0, rs */ +#undef TCG_TARGET_HAS_ext8u_i32 /* and rd, rs, 0xff */ +#undef TCG_TARGET_HAS_ext16u_i32 /* and rd, rs, 0xffff */ + +#define TCG_TARGET_HAS_GUEST_BASE /* Note: must be synced with dyngen-exec.h */ #define TCG_AREG0 TCG_REG_R17 -#define TCG_AREG1 TCG_REG_R14 -#define TCG_AREG2 TCG_REG_R15 static inline void flush_icache_range(unsigned long start, unsigned long stop) { start &= ~31; - while (start <= stop) - { - asm volatile ("fdc 0(%0)\n" - "sync\n" - "fic 0(%%sr4, %0)\n" - "sync\n" + while (start <= stop) { + asm volatile ("fdc 0(%0)\n\t" + "sync\n\t" + "fic 0(%%sr4, %0)\n\t" + "sync" : : "r"(start) : "memory"); start += 32; } } - -/* supplied by libgcc */ -extern void *__canonicalize_funcptr_for_compare(void *); - -/* Field selection types defined by hppa */ -#define rnd(x) (((x)+0x1000)&~0x1fff) -/* lsel: select left 21 bits */ -#define lsel(v,a) (((v)+(a))>>11) -/* rsel: select right 11 bits */ -#define rsel(v,a) (((v)+(a))&0x7ff) -/* lrsel with rounding of addend to nearest 8k */ -#define lrsel(v,a) (((v)+rnd(a))>>11) -/* rrsel with rounding of addend to nearest 8k */ -#define rrsel(v,a) ((((v)+rnd(a))&0x7ff)+((a)-rnd(a))) - -#define mask(x,sz) ((x) & ~((1<<(sz))-1)) - -static inline int reassemble_12(int as12) -{ - return (((as12 & 0x800) >> 11) | - ((as12 & 0x400) >> 8) | - ((as12 & 0x3ff) << 3)); -} - -static inline int reassemble_14(int as14) -{ - return (((as14 & 0x1fff) << 1) | - ((as14 & 0x2000) >> 13)); -} - -static inline int reassemble_17(int as17) -{ - return (((as17 & 0x10000) >> 16) | - ((as17 & 0x0f800) << 5) | - ((as17 & 0x00400) >> 8) | - ((as17 & 0x003ff) << 3)); -} - -static inline int reassemble_21(int as21) -{ - return (((as21 & 0x100000) >> 20) | - ((as21 & 0x0ffe00) >> 8) | - ((as21 & 0x000180) << 7) | - ((as21 & 0x00007c) << 14) | - ((as21 & 0x000003) << 12)); -} - -static inline void hppa_patch21l(uint32_t *insn, int val, int addend) -{ - val = lrsel(val, addend); - *insn = mask(*insn, 21) | reassemble_21(val); -} - -static inline void hppa_patch14r(uint32_t *insn, int val, int addend) -{ - val = rrsel(val, addend); - *insn = mask(*insn, 14) | reassemble_14(val); -} - -static inline void hppa_patch17r(uint32_t *insn, int val, int addend) -{ - val = rrsel(val, addend); - *insn = (*insn & ~0x1f1ffd) | reassemble_17(val); -} - - -static inline void hppa_patch21l_dprel(uint32_t *insn, int val, int addend) -{ - register unsigned int dp asm("r27"); - hppa_patch21l(insn, val - dp, addend); -} - -static inline void hppa_patch14r_dprel(uint32_t *insn, int val, int addend) -{ - register unsigned int dp asm("r27"); - hppa_patch14r(insn, val - dp, addend); -} - -static inline void hppa_patch17f(uint32_t *insn, int val, int addend) -{ - int dot = (int)insn & ~0x3; - int v = ((val + addend) - dot - 8) / 4; - if (v > (1 << 16) || v < -(1 << 16)) { - printf("cannot fit branch to offset %d [%08x->%08x]\n", v, dot, val); - abort(); - } - *insn = (*insn & ~0x1f1ffd) | reassemble_17(v); -} - -static inline void hppa_load_imm21l(uint32_t *insn, int val, int addend) -{ - /* Transform addil L'sym(%dp) to ldil L'val, %r1 */ - *insn = 0x20200000 | reassemble_21(lrsel(val, 0)); -} - -static inline void hppa_load_imm14r(uint32_t *insn, int val, int addend) -{ - /* Transform ldw R'sym(%r1), %rN to ldo R'sym(%r1), %rN */ - hppa_patch14r(insn, val, addend); - /* HACK */ - if (addend == 0) - *insn = (*insn & ~0xfc000000) | (0x0d << 26); -} diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c index 972b102..09a5c55 100644 --- a/tcg/i386/tcg-target.c +++ b/tcg/i386/tcg-target.c @@ -24,18 +24,33 @@ #ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%eax", - "%ecx", - "%edx", - "%ebx", - "%esp", - "%ebp", - "%esi", - "%edi", +#if TCG_TARGET_REG_BITS == 64 + "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", +#else + "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", +#endif }; #endif static const int tcg_target_reg_alloc_order[] = { +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RBP, + TCG_REG_RBX, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_RCX, + TCG_REG_RDX, + TCG_REG_RSI, + TCG_REG_RDI, + TCG_REG_RAX, +#else TCG_REG_EAX, TCG_REG_EDX, TCG_REG_ECX, @@ -43,23 +58,49 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_ESI, TCG_REG_EDI, TCG_REG_EBP, +#endif +}; + +static const int tcg_target_call_iarg_regs[] = { +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RDI, + TCG_REG_RSI, + TCG_REG_RDX, + TCG_REG_RCX, + TCG_REG_R8, + TCG_REG_R9, +#else + TCG_REG_EAX, + TCG_REG_EDX, + TCG_REG_ECX +#endif }; -static const int tcg_target_call_iarg_regs[3] = { TCG_REG_EAX, TCG_REG_EDX, TCG_REG_ECX }; -static const int tcg_target_call_oarg_regs[2] = { TCG_REG_EAX, TCG_REG_EDX }; +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_EAX, + TCG_REG_EDX +}; static uint8_t *tb_ret_addr; -static void patch_reloc(uint8_t *code_ptr, int type, +static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend) { value += addend; switch(type) { - case R_386_32: + case R_386_PC32: + value -= (uintptr_t)code_ptr; + if (value != (int32_t)value) { + tcg_abort(); + } *(uint32_t *)code_ptr = value; break; - case R_386_PC32: - *(uint32_t *)code_ptr = value - (long)code_ptr; + case R_386_PC8: + value -= (uintptr_t)code_ptr; + if (value != (int8_t)value) { + tcg_abort(); + } + *(uint8_t *)code_ptr = value; break; default: tcg_abort(); @@ -69,6 +110,10 @@ static void patch_reloc(uint8_t *code_ptr, int type, /* maximum number of register used for input function arguments */ static inline int tcg_target_get_call_iarg_regs_count(int flags) { + if (TCG_TARGET_REG_BITS == 64) { + return 6; + } + flags &= TCG_CALL_TYPE_MASK; switch(flags) { case TCG_CALL_TYPE_STD: @@ -115,20 +160,42 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) break; case 'q': ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xf); + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xf); + } break; case 'r': ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xff); + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xff); + } break; /* qemu_ld/st address constraint */ case 'L': ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_EAX); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_EDX); + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_RSI); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_RDI); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_EAX); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_EDX); + } break; + + case 'e': + ct->ct |= TCG_CT_CONST_S32; + break; + case 'Z': + ct->ct |= TCG_CT_CONST_U32; + break; + default: return -1; } @@ -141,14 +208,83 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) static inline int tcg_target_const_match(tcg_target_long val, const TCGArgConstraint *arg_ct) { - int ct; - ct = arg_ct->ct; - if (ct & TCG_CT_CONST) + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { return 1; - else - return 0; + } + if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + return 1; + } + if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + return 1; + } + return 0; } +#if TCG_TARGET_REG_BITS == 64 +# define LOWREGMASK(x) ((x) & 7) +#else +# define LOWREGMASK(x) (x) +#endif + +#define P_EXT 0x100 /* 0x0f opcode prefix */ +#define P_DATA16 0x200 /* 0x66 opcode prefix */ +#if TCG_TARGET_REG_BITS == 64 +# define P_ADDR32 0x400 /* 0x67 opcode prefix */ +# define P_REXW 0x800 /* Set REX.W = 1 */ +# define P_REXB_R 0x1000 /* REG field as byte register */ +# define P_REXB_RM 0x2000 /* R/M field as byte register */ +#else +# define P_ADDR32 0 +# define P_REXW 0 +# define P_REXB_R 0 +# define P_REXB_RM 0 +#endif + +#define OPC_ARITH_EvIz (0x81) +#define OPC_ARITH_EvIb (0x83) +#define OPC_ARITH_GvEv (0x03) /* ... plus (ARITH_FOO << 3) */ +#define OPC_ADD_GvEv (OPC_ARITH_GvEv | (ARITH_ADD << 3)) +#define OPC_BSWAP (0xc8 | P_EXT) +#define OPC_CALL_Jz (0xe8) +#define OPC_CMP_GvEv (OPC_ARITH_GvEv | (ARITH_CMP << 3)) +#define OPC_DEC_r32 (0x48) +#define OPC_IMUL_GvEv (0xaf | P_EXT) +#define OPC_IMUL_GvEvIb (0x6b) +#define OPC_IMUL_GvEvIz (0x69) +#define OPC_INC_r32 (0x40) +#define OPC_JCC_long (0x80 | P_EXT) /* ... plus condition code */ +#define OPC_JCC_short (0x70) /* ... plus condition code */ +#define OPC_JMP_long (0xe9) +#define OPC_JMP_short (0xeb) +#define OPC_LEA (0x8d) +#define OPC_MOVB_EvGv (0x88) /* stores, more or less */ +#define OPC_MOVL_EvGv (0x89) /* stores, more or less */ +#define OPC_MOVL_GvEv (0x8b) /* loads, more or less */ +#define OPC_MOVL_EvIz (0xc7) +#define OPC_MOVL_Iv (0xb8) +#define OPC_MOVSBL (0xbe | P_EXT) +#define OPC_MOVSWL (0xbf | P_EXT) +#define OPC_MOVSLQ (0x63 | P_REXW) +#define OPC_MOVZBL (0xb6 | P_EXT) +#define OPC_MOVZWL (0xb7 | P_EXT) +#define OPC_POP_r32 (0x58) +#define OPC_PUSH_r32 (0x50) +#define OPC_PUSH_Iv (0x68) +#define OPC_PUSH_Ib (0x6a) +#define OPC_RET (0xc3) +#define OPC_SETCC (0x90 | P_EXT | P_REXB_RM) /* ... plus cc */ +#define OPC_SHIFT_1 (0xd1) +#define OPC_SHIFT_Ib (0xc1) +#define OPC_SHIFT_cl (0xd3) +#define OPC_TESTL (0x85) +#define OPC_XCHG_ax_r32 (0x90) + +#define OPC_GRP3_Ev (0xf7) +#define OPC_GRP5 (0xff) + +/* Group 1 opcode extensions for 0x80-0x83. + These are also used as modifiers for OPC_ARITH. */ #define ARITH_ADD 0 #define ARITH_OR 1 #define ARITH_ADC 2 @@ -158,12 +294,28 @@ static inline int tcg_target_const_match(tcg_target_long val, #define ARITH_XOR 6 #define ARITH_CMP 7 +/* Group 2 opcode extensions for 0xc0, 0xc1, 0xd0-0xd3. */ #define SHIFT_ROL 0 #define SHIFT_ROR 1 #define SHIFT_SHL 4 #define SHIFT_SHR 5 #define SHIFT_SAR 7 +/* Group 3 opcode extensions for 0xf6, 0xf7. To be used with OPC_GRP3. */ +#define EXT3_NOT 2 +#define EXT3_NEG 3 +#define EXT3_MUL 4 +#define EXT3_IMUL 5 +#define EXT3_DIV 6 +#define EXT3_IDIV 7 + +/* Group 5 opcode extensions for 0xff. To be used with OPC_GRP5. */ +#define EXT5_INC_Ev 0 +#define EXT5_DEC_Ev 1 +#define EXT5_CALLN_Ev 2 +#define EXT5_JMPN_Ev 4 + +/* Condition codes to be added to OPC_JCC_{long,short}. */ #define JCC_JMP (-1) #define JCC_JO 0x0 #define JCC_JNO 0x1 @@ -182,8 +334,6 @@ static inline int tcg_target_const_match(tcg_target_long val, #define JCC_JLE 0xe #define JCC_JG 0xf -#define P_EXT 0x100 /* 0x0f opcode prefix */ - static const uint8_t tcg_cond_to_jcc[10] = { [TCG_COND_EQ] = JCC_JE, [TCG_COND_NE] = JCC_JNE, @@ -197,228 +347,616 @@ static const uint8_t tcg_cond_to_jcc[10] = { [TCG_COND_GTU] = JCC_JA, }; -static inline void tcg_out_opc(TCGContext *s, int opc) +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x) +{ + int rex; + + if (opc & P_DATA16) { + /* We should never be asking for both 16 and 64-bit operation. */ + assert((opc & P_REXW) == 0); + tcg_out8(s, 0x66); + } + if (opc & P_ADDR32) { + tcg_out8(s, 0x67); + } + + rex = 0; + rex |= (opc & P_REXW) >> 8; /* REX.W */ + rex |= (r & 8) >> 1; /* REX.R */ + rex |= (x & 8) >> 2; /* REX.X */ + rex |= (rm & 8) >> 3; /* REX.B */ + + /* P_REXB_{R,RM} indicates that the given register is the low byte. + For %[abcd]l we need no REX prefix, but for %{si,di,bp,sp}l we do, + as otherwise the encoding indicates %[abcd]h. Note that the values + that are ORed in merely indicate that the REX byte must be present; + those bits get discarded in output. */ + rex |= opc & (r >= 4 ? P_REXB_R : 0); + rex |= opc & (rm >= 4 ? P_REXB_RM : 0); + + if (rex) { + tcg_out8(s, (uint8_t)(rex | 0x40)); + } + + if (opc & P_EXT) { + tcg_out8(s, 0x0f); + } + tcg_out8(s, opc); +} +#else +static void tcg_out_opc(TCGContext *s, int opc) { - if (opc & P_EXT) + if (opc & P_DATA16) { + tcg_out8(s, 0x66); + } + if (opc & P_EXT) { tcg_out8(s, 0x0f); + } tcg_out8(s, opc); } +/* Discard the register arguments to tcg_out_opc early, so as not to penalize + the 32-bit compilation paths. This method works with all versions of gcc, + whereas relying on optimization may not be able to exclude them. */ +#define tcg_out_opc(s, opc, r, rm, x) (tcg_out_opc)(s, opc) +#endif -static inline void tcg_out_modrm(TCGContext *s, int opc, int r, int rm) +static void tcg_out_modrm(TCGContext *s, int opc, int r, int rm) { - tcg_out_opc(s, opc); - tcg_out8(s, 0xc0 | (r << 3) | rm); + tcg_out_opc(s, opc, r, rm, 0); + tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); } -/* rm == -1 means no register index */ -static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, int rm, - int32_t offset) +/* Output an opcode with a full "rm + (index<<shift) + offset" address mode. + We handle either RM and INDEX missing with a negative value. In 64-bit + mode for absolute addresses, ~RM is the size of the immediate operand + that will follow the instruction. */ + +static void tcg_out_modrm_sib_offset(TCGContext *s, int opc, int r, int rm, + int index, int shift, + tcg_target_long offset) { - tcg_out_opc(s, opc); - if (rm == -1) { - tcg_out8(s, 0x05 | (r << 3)); - tcg_out32(s, offset); - } else if (offset == 0 && rm != TCG_REG_EBP) { - if (rm == TCG_REG_ESP) { - tcg_out8(s, 0x04 | (r << 3)); - tcg_out8(s, 0x24); - } else { - tcg_out8(s, 0x00 | (r << 3) | rm); - } - } else if ((int8_t)offset == offset) { - if (rm == TCG_REG_ESP) { - tcg_out8(s, 0x44 | (r << 3)); - tcg_out8(s, 0x24); + int mod, len; + + if (index < 0 && rm < 0) { + if (TCG_TARGET_REG_BITS == 64) { + /* Try for a rip-relative addressing mode. This has replaced + the 32-bit-mode absolute addressing encoding. */ + tcg_target_long pc = (tcg_target_long)s->code_ptr + 5 + ~rm; + tcg_target_long disp = offset - pc; + if (disp == (int32_t)disp) { + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (LOWREGMASK(r) << 3) | 5); + tcg_out32(s, disp); + return; + } + + /* Try for an absolute address encoding. This requires the + use of the MODRM+SIB encoding and is therefore larger than + rip-relative addressing. */ + if (offset == (int32_t)offset) { + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (LOWREGMASK(r) << 3) | 4); + tcg_out8(s, (4 << 3) | 5); + tcg_out32(s, offset); + return; + } + + /* ??? The memory isn't directly addressable. */ + tcg_abort(); } else { - tcg_out8(s, 0x40 | (r << 3) | rm); + /* Absolute address. */ + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (r << 3) | 5); + tcg_out32(s, offset); + return; } - tcg_out8(s, offset); + } + + /* Find the length of the immediate addend. Note that the encoding + that would be used for (%ebp) indicates absolute addressing. */ + if (rm < 0) { + mod = 0, len = 4, rm = 5; + } else if (offset == 0 && LOWREGMASK(rm) != TCG_REG_EBP) { + mod = 0, len = 0; + } else if (offset == (int8_t)offset) { + mod = 0x40, len = 1; + } else { + mod = 0x80, len = 4; + } + + /* Use a single byte MODRM format if possible. Note that the encoding + that would be used for %esp is the escape to the two byte form. */ + if (index < 0 && LOWREGMASK(rm) != TCG_REG_ESP) { + /* Single byte MODRM format. */ + tcg_out_opc(s, opc, r, rm, 0); + tcg_out8(s, mod | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); } else { - if (rm == TCG_REG_ESP) { - tcg_out8(s, 0x84 | (r << 3)); - tcg_out8(s, 0x24); + /* Two byte MODRM+SIB format. */ + + /* Note that the encoding that would place %esp into the index + field indicates no index register. In 64-bit mode, the REX.X + bit counts, so %r12 can be used as the index. */ + if (index < 0) { + index = 4; } else { - tcg_out8(s, 0x80 | (r << 3) | rm); + assert(index != TCG_REG_ESP); } + + tcg_out_opc(s, opc, r, rm, index); + tcg_out8(s, mod | (LOWREGMASK(r) << 3) | 4); + tcg_out8(s, (shift << 6) | (LOWREGMASK(index) << 3) | LOWREGMASK(rm)); + } + + if (len == 1) { + tcg_out8(s, offset); + } else if (len == 4) { tcg_out32(s, offset); } } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +/* A simplification of the above with no index or shift. */ +static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, + int rm, tcg_target_long offset) +{ + tcg_out_modrm_sib_offset(s, opc, r, rm, -1, 0, offset); +} + +/* Generate dest op= src. Uses the same ARITH_* codes as tgen_arithi. */ +static inline void tgen_arithr(TCGContext *s, int subop, int dest, int src) +{ + /* Propagate an opcode prefix, such as P_REXW. */ + int ext = subop & ~0x7; + subop &= 0x7; + + tcg_out_modrm(s, OPC_ARITH_GvEv + (subop << 3) + ext, dest, src); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) { - if (arg != ret) - tcg_out_modrm(s, 0x8b, ret, arg); + if (arg != ret) { + int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm(s, opc, ret, arg); + } } -static inline void tcg_out_movi(TCGContext *s, TCGType type, - int ret, int32_t arg) +static void tcg_out_movi(TCGContext *s, TCGType type, + int ret, tcg_target_long arg) { if (arg == 0) { - /* xor r0,r0 */ - tcg_out_modrm(s, 0x01 | (ARITH_XOR << 3), ret, ret); + tgen_arithr(s, ARITH_XOR, ret, ret); + return; + } else if (arg == (uint32_t)arg || type == TCG_TYPE_I32) { + tcg_out_opc(s, OPC_MOVL_Iv + LOWREGMASK(ret), 0, ret, 0); + tcg_out32(s, arg); + } else if (arg == (int32_t)arg) { + tcg_out_modrm(s, OPC_MOVL_EvIz + P_REXW, 0, ret); + tcg_out32(s, arg); } else { - tcg_out8(s, 0xb8 + ret); + tcg_out_opc(s, OPC_MOVL_Iv + P_REXW + LOWREGMASK(ret), 0, ret, 0); tcg_out32(s, arg); + tcg_out32(s, arg >> 31 >> 1); + } +} + +static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val) +{ + if (val == (int8_t)val) { + tcg_out_opc(s, OPC_PUSH_Ib, 0, 0, 0); + tcg_out8(s, val); + } else if (val == (int32_t)val) { + tcg_out_opc(s, OPC_PUSH_Iv, 0, 0, 0); + tcg_out32(s, val); + } else { + tcg_abort(); } } +static inline void tcg_out_push(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_PUSH_r32 + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_pop(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_POP_r32 + LOWREGMASK(reg), 0, reg, 0); +} + static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, int arg1, tcg_target_long arg2) { - /* movl */ - tcg_out_modrm_offset(s, 0x8b, ret, arg1, arg2); + int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm_offset(s, opc, ret, arg1, arg2); } static inline void tcg_out_st(TCGContext *s, TCGType type, int arg, int arg1, tcg_target_long arg2) { - /* movl */ - tcg_out_modrm_offset(s, 0x89, arg, arg1, arg2); + int opc = OPC_MOVL_EvGv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm_offset(s, opc, arg, arg1, arg2); } -static inline void tgen_arithi(TCGContext *s, int c, int r0, int32_t val, int cf) +static void tcg_out_shifti(TCGContext *s, int subopc, int reg, int count) { - if (!cf && ((c == ARITH_ADD && val == 1) || (c == ARITH_SUB && val == -1))) { - /* inc */ - tcg_out_opc(s, 0x40 + r0); - } else if (!cf && ((c == ARITH_ADD && val == -1) || (c == ARITH_SUB && val == 1))) { - /* dec */ - tcg_out_opc(s, 0x48 + r0); - } else if (val == (int8_t)val) { - tcg_out_modrm(s, 0x83, c, r0); - tcg_out8(s, val); - } else if (c == ARITH_AND && val == 0xffu && r0 < 4) { - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT, r0, r0); - } else if (c == ARITH_AND && val == 0xffffu) { - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, r0, r0); + /* Propagate an opcode prefix, such as P_DATA16. */ + int ext = subopc & ~0x7; + subopc &= 0x7; + + if (count == 1) { + tcg_out_modrm(s, OPC_SHIFT_1 + ext, subopc, reg); } else { - tcg_out_modrm(s, 0x81, c, r0); + tcg_out_modrm(s, OPC_SHIFT_Ib + ext, subopc, reg); + tcg_out8(s, count); + } +} + +static inline void tcg_out_bswap32(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_BSWAP + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_rolw_8(TCGContext *s, int reg) +{ + tcg_out_shifti(s, SHIFT_ROL + P_DATA16, reg, 8); +} + +static inline void tcg_out_ext8u(TCGContext *s, int dest, int src) +{ + /* movzbl */ + assert(src < 4 || TCG_TARGET_REG_BITS == 64); + tcg_out_modrm(s, OPC_MOVZBL + P_REXB_RM, dest, src); +} + +static void tcg_out_ext8s(TCGContext *s, int dest, int src, int rexw) +{ + /* movsbl */ + assert(src < 4 || TCG_TARGET_REG_BITS == 64); + tcg_out_modrm(s, OPC_MOVSBL + P_REXB_RM + rexw, dest, src); +} + +static inline void tcg_out_ext16u(TCGContext *s, int dest, int src) +{ + /* movzwl */ + tcg_out_modrm(s, OPC_MOVZWL, dest, src); +} + +static inline void tcg_out_ext16s(TCGContext *s, int dest, int src, int rexw) +{ + /* movsw[lq] */ + tcg_out_modrm(s, OPC_MOVSWL + rexw, dest, src); +} + +static inline void tcg_out_ext32u(TCGContext *s, int dest, int src) +{ + /* 32-bit mov zero extends. */ + tcg_out_modrm(s, OPC_MOVL_GvEv, dest, src); +} + +static inline void tcg_out_ext32s(TCGContext *s, int dest, int src) +{ + tcg_out_modrm(s, OPC_MOVSLQ, dest, src); +} + +static inline void tcg_out_bswap64(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_BSWAP + P_REXW + LOWREGMASK(reg), 0, reg, 0); +} + +static void tgen_arithi(TCGContext *s, int c, int r0, + tcg_target_long val, int cf) +{ + int rexw = 0; + + if (TCG_TARGET_REG_BITS == 64) { + rexw = c & -8; + c &= 7; + } + + /* ??? While INC is 2 bytes shorter than ADDL $1, they also induce + partial flags update stalls on Pentium4 and are not recommended + by current Intel optimization manuals. */ + if (!cf && (c == ARITH_ADD || c == ARITH_SUB) && (val == 1 || val == -1)) { + int is_inc = (c == ARITH_ADD) ^ (val < 0); + if (TCG_TARGET_REG_BITS == 64) { + /* The single-byte increment encodings are re-tasked as the + REX prefixes. Use the MODRM encoding. */ + tcg_out_modrm(s, OPC_GRP5 + rexw, + (is_inc ? EXT5_INC_Ev : EXT5_DEC_Ev), r0); + } else { + tcg_out8(s, (is_inc ? OPC_INC_r32 : OPC_DEC_r32) + r0); + } + return; + } + + if (c == ARITH_AND) { + if (TCG_TARGET_REG_BITS == 64) { + if (val == 0xffffffffu) { + tcg_out_ext32u(s, r0, r0); + return; + } + if (val == (uint32_t)val) { + /* AND with no high bits set can use a 32-bit operation. */ + rexw = 0; + } + } + if (val == 0xffu && (r0 < 4 || TCG_TARGET_REG_BITS == 64)) { + tcg_out_ext8u(s, r0, r0); + return; + } + if (val == 0xffffu) { + tcg_out_ext16u(s, r0, r0); + return; + } + } + + if (val == (int8_t)val) { + tcg_out_modrm(s, OPC_ARITH_EvIb + rexw, c, r0); + tcg_out8(s, val); + return; + } + if (rexw == 0 || val == (int32_t)val) { + tcg_out_modrm(s, OPC_ARITH_EvIz + rexw, c, r0); tcg_out32(s, val); + return; } + + tcg_abort(); } static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) { - if (val != 0) - tgen_arithi(s, ARITH_ADD, reg, val, 0); + if (val != 0) { + tgen_arithi(s, ARITH_ADD + P_REXW, reg, val, 0); + } } -static void tcg_out_jxx(TCGContext *s, int opc, int label_index) +#undef small /* for mingw build */ + +/* Use SMALL != 0 to force a short forward branch. */ +static void tcg_out_jxx(TCGContext *s, int opc, int label_index, int small) { int32_t val, val1; TCGLabel *l = &s->labels[label_index]; - + if (l->has_value) { val = l->u.value - (tcg_target_long)s->code_ptr; val1 = val - 2; if ((int8_t)val1 == val1) { - if (opc == -1) - tcg_out8(s, 0xeb); - else - tcg_out8(s, 0x70 + opc); + if (opc == -1) { + tcg_out8(s, OPC_JMP_short); + } else { + tcg_out8(s, OPC_JCC_short + opc); + } tcg_out8(s, val1); } else { + if (small) { + tcg_abort(); + } if (opc == -1) { - tcg_out8(s, 0xe9); + tcg_out8(s, OPC_JMP_long); tcg_out32(s, val - 5); } else { - tcg_out8(s, 0x0f); - tcg_out8(s, 0x80 + opc); + tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); tcg_out32(s, val - 6); } } + } else if (small) { + if (opc == -1) { + tcg_out8(s, OPC_JMP_short); + } else { + tcg_out8(s, OPC_JCC_short + opc); + } + tcg_out_reloc(s, s->code_ptr, R_386_PC8, label_index, -1); + s->code_ptr += 1; } else { if (opc == -1) { - tcg_out8(s, 0xe9); + tcg_out8(s, OPC_JMP_long); } else { - tcg_out8(s, 0x0f); - tcg_out8(s, 0x80 + opc); + tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); } tcg_out_reloc(s, s->code_ptr, R_386_PC32, label_index, -4); s->code_ptr += 4; } } -static void tcg_out_brcond(TCGContext *s, int cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index) +static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2, + int const_arg2, int rexw) { if (const_arg2) { if (arg2 == 0) { /* test r, r */ - tcg_out_modrm(s, 0x85, arg1, arg1); + tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1); } else { - tgen_arithi(s, ARITH_CMP, arg1, arg2, 0); + tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0); } } else { - tcg_out_modrm(s, 0x01 | (ARITH_CMP << 3), arg2, arg1); + tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2); } - tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index); } +static void tcg_out_brcond32(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index, int small) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, 0); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_brcond64(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index, int small) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small); +} +#else /* XXX: we implement it at the target level to avoid having to handle cross basic blocks temporaries */ -static void tcg_out_brcond2(TCGContext *s, - const TCGArg *args, const int *const_args) +static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, + const int *const_args, int small) { int label_next; label_next = gen_new_label(); switch(args[4]) { case TCG_COND_EQ: - tcg_out_brcond(s, TCG_COND_NE, args[0], args[2], const_args[2], label_next); - tcg_out_brcond(s, TCG_COND_EQ, args[1], args[3], const_args[3], args[5]); + tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], + label_next, 1); + tcg_out_brcond32(s, TCG_COND_EQ, args[1], args[3], const_args[3], + args[5], small); break; case TCG_COND_NE: - tcg_out_brcond(s, TCG_COND_NE, args[0], args[2], const_args[2], args[5]); - tcg_out_brcond(s, TCG_COND_NE, args[1], args[3], const_args[3], args[5]); + tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], + args[5], small); + tcg_out_brcond32(s, TCG_COND_NE, args[1], args[3], const_args[3], + args[5], small); break; case TCG_COND_LT: - tcg_out_brcond(s, TCG_COND_LT, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_LTU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_LE: - tcg_out_brcond(s, TCG_COND_LT, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_LEU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_GT: - tcg_out_brcond(s, TCG_COND_GT, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_GTU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_GE: - tcg_out_brcond(s, TCG_COND_GT, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_GEU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_LTU: - tcg_out_brcond(s, TCG_COND_LTU, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_LTU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_LEU: - tcg_out_brcond(s, TCG_COND_LTU, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_LEU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_GTU: - tcg_out_brcond(s, TCG_COND_GTU, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_GTU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_GEU: - tcg_out_brcond(s, TCG_COND_GTU, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_GEU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], + args[5], small); break; default: tcg_abort(); } tcg_out_label(s, label_next, (tcg_target_long)s->code_ptr); } +#endif + +static void tcg_out_setcond32(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, 0); + tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); + tcg_out_ext8u(s, dest, dest); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_setcond64(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); + tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); + tcg_out_ext8u(s, dest, dest); +} +#else +static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, + const int *const_args) +{ + TCGArg new_args[6]; + int label_true, label_over; + + memcpy(new_args, args+1, 5*sizeof(TCGArg)); + + if (args[0] == args[1] || args[0] == args[2] + || (!const_args[3] && args[0] == args[3]) + || (!const_args[4] && args[0] == args[4])) { + /* When the destination overlaps with one of the argument + registers, don't do anything tricky. */ + label_true = gen_new_label(); + label_over = gen_new_label(); + + new_args[5] = label_true; + tcg_out_brcond2(s, new_args, const_args+1, 1); + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + tcg_out_jxx(s, JCC_JMP, label_over, 1); + tcg_out_label(s, label_true, (tcg_target_long)s->code_ptr); + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 1); + tcg_out_label(s, label_over, (tcg_target_long)s->code_ptr); + } else { + /* When the destination does not overlap one of the arguments, + clear the destination first, jump if cond false, and emit an + increment in the true case. This results in smaller code. */ + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + + label_over = gen_new_label(); + new_args[4] = tcg_invert_cond(new_args[4]); + new_args[5] = label_over; + tcg_out_brcond2(s, new_args, const_args+1, 1); + + tgen_arithi(s, ARITH_ADD, args[0], 1, 0); + tcg_out_label(s, label_over, (tcg_target_long)s->code_ptr); + } +} +#endif + +static void tcg_out_branch(TCGContext *s, int call, tcg_target_long dest) +{ + tcg_target_long disp = dest - (tcg_target_long)s->code_ptr - 5; + + if (disp == (int32_t)disp) { + tcg_out_opc(s, call ? OPC_CALL_Jz : OPC_JMP_long, 0, 0, 0); + tcg_out32(s, disp); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R10, dest); + tcg_out_modrm(s, OPC_GRP5, + call ? EXT5_CALLN_Ev : EXT5_JMPN_Ev, TCG_REG_R10); + } +} + +static inline void tcg_out_calli(TCGContext *s, tcg_target_long dest) +{ + tcg_out_branch(s, 1, dest); +} + +static void tcg_out_jmp(TCGContext *s, tcg_target_long dest) +{ + tcg_out_branch(s, 0, dest); +} #if defined(CONFIG_SOFTMMU) @@ -437,647 +975,670 @@ static void *qemu_st_helpers[4] = { __stl_mmu, __stq_mmu, }; -#endif -#ifndef CONFIG_USER_ONLY -#define GUEST_BASE 0 -#endif +/* Perform the TLB load and compare. -/* XXX: qemu_ld and qemu_st could be modified to clobber only EDX and - EAX. It will be useful once fixed registers globals are less - common. */ -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, - int opc) + Inputs: + ADDRLO_IDX contains the index into ARGS of the low part of the + address; the high part of the address is at ADDR_LOW_IDX+1. + + MEM_INDEX and S_BITS are the memory context and log2 size of the load. + + WHICH is the offset into the CPUTLBEntry structure of the slot to read. + This should be offsetof addr_read or addr_write. + + Outputs: + LABEL_PTRS is filled with 1 (32-bit addresses) or 2 (64-bit addresses) + positions of the displacements of forward jumps to the TLB miss case. + + First argument register is loaded with the low part of the address. + In the TLB hit case, it has been adjusted as indicated by the TLB + and so is a host address. In the TLB miss case, it continues to + hold a guest address. + + Second argument register is clobbered. */ + +static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx, + int mem_index, int s_bits, + const TCGArg *args, + uint8_t **label_ptr, int which) { - int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap; -#if defined(CONFIG_SOFTMMU) - uint8_t *label1_ptr, *label2_ptr; -#endif -#if TARGET_LONG_BITS == 64 -#if defined(CONFIG_SOFTMMU) - uint8_t *label3_ptr; -#endif - int addr_reg2; -#endif + const int addrlo = args[addrlo_idx]; + const int r0 = tcg_target_call_iarg_regs[0]; + const int r1 = tcg_target_call_iarg_regs[1]; + TCGType type = TCG_TYPE_I32; + int rexw = 0; + + if (TCG_TARGET_REG_BITS == 64 && TARGET_LONG_BITS == 64) { + type = TCG_TYPE_I64; + rexw = P_REXW; + } - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; - addr_reg = *args++; -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#endif - mem_index = *args; - s_bits = opc & 3; + tcg_out_mov(s, type, r1, addrlo); + tcg_out_mov(s, type, r0, addrlo); - r0 = TCG_REG_EAX; - r1 = TCG_REG_EDX; + tcg_out_shifti(s, SHIFT_SHR + rexw, r1, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); -#if defined(CONFIG_SOFTMMU) - tcg_out_mov(s, r1, addr_reg); - - tcg_out_mov(s, r0, addr_reg); - - tcg_out_modrm(s, 0xc1, 5, r1); /* shr $x, r1 */ - tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - - tcg_out_modrm(s, 0x81, 4, r0); /* andl $x, r0 */ - tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - - tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */ - tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); - - tcg_out_opc(s, 0x8d); /* lea offset(r1, %ebp), r1 */ - tcg_out8(s, 0x80 | (r1 << 3) | 0x04); - tcg_out8(s, (5 << 3) | r1); - tcg_out32(s, offsetof(CPUState, tlb_table[mem_index][0].addr_read)); + tgen_arithi(s, ARITH_AND + rexw, r0, + TARGET_PAGE_MASK | ((1 << s_bits) - 1), 0); + tgen_arithi(s, ARITH_AND + rexw, r1, + (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, 0); + + tcg_out_modrm_sib_offset(s, OPC_LEA + P_REXW, r1, TCG_AREG0, r1, 0, + offsetof(CPUState, tlb_table[mem_index][0]) + + which); /* cmp 0(r1), r0 */ - tcg_out_modrm_offset(s, 0x3b, r0, r1, 0); - - tcg_out_mov(s, r0, addr_reg); - -#if TARGET_LONG_BITS == 32 - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; - s->code_ptr++; -#else - /* jne label3 */ - tcg_out8(s, 0x70 + JCC_JNE); - label3_ptr = s->code_ptr; - s->code_ptr++; - - /* cmp 4(r1), addr_reg2 */ - tcg_out_modrm_offset(s, 0x3b, addr_reg2, r1, 4); + tcg_out_modrm_offset(s, OPC_CMP_GvEv + rexw, r0, r1, 0); + + tcg_out_mov(s, type, r0, addrlo); - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; + /* jne label1 */ + tcg_out8(s, OPC_JCC_short + JCC_JNE); + label_ptr[0] = s->code_ptr; s->code_ptr++; - - /* label3: */ - *label3_ptr = s->code_ptr - label3_ptr - 1; -#endif - /* XXX: move that code at the end of the TB */ -#if TARGET_LONG_BITS == 32 - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_EDX, mem_index); -#else - tcg_out_mov(s, TCG_REG_EDX, addr_reg2); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index); -#endif - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_ld_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + /* cmp 4(r1), addrhi */ + tcg_out_modrm_offset(s, OPC_CMP_GvEv, args[addrlo_idx+1], r1, 4); - switch(opc) { - case 0 | 4: - /* movsbl */ - tcg_out_modrm(s, 0xbe | P_EXT, data_reg, TCG_REG_EAX); - break; - case 1 | 4: - /* movswl */ - tcg_out_modrm(s, 0xbf | P_EXT, data_reg, TCG_REG_EAX); - break; - case 0: - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT, data_reg, TCG_REG_EAX); - break; - case 1: - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, data_reg, TCG_REG_EAX); - break; - case 2: - default: - tcg_out_mov(s, data_reg, TCG_REG_EAX); - break; - case 3: - if (data_reg == TCG_REG_EDX) { - tcg_out_opc(s, 0x90 + TCG_REG_EDX); /* xchg %edx, %eax */ - tcg_out_mov(s, data_reg2, TCG_REG_EAX); - } else { - tcg_out_mov(s, data_reg, TCG_REG_EAX); - tcg_out_mov(s, data_reg2, TCG_REG_EDX); - } - break; + /* jne label1 */ + tcg_out8(s, OPC_JCC_short + JCC_JNE); + label_ptr[1] = s->code_ptr; + s->code_ptr++; } - /* jmp label2 */ - tcg_out8(s, 0xeb); - label2_ptr = s->code_ptr; - s->code_ptr++; - - /* label1: */ - *label1_ptr = s->code_ptr - label1_ptr - 1; + /* TLB Hit. */ - /* add x(r1), r0 */ - tcg_out_modrm_offset(s, 0x03, r0, r1, offsetof(CPUTLBEntry, addend) - - offsetof(CPUTLBEntry, addr_read)); -#else - r0 = addr_reg; + /* add addend(r1), r0 */ + tcg_out_modrm_offset(s, OPC_ADD_GvEv + P_REXW, r0, r1, + offsetof(CPUTLBEntry, addend) - which); +} #endif +static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi, + int base, tcg_target_long ofs, int sizeop) +{ #ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; + const int bswap = 1; #else - bswap = 0; + const int bswap = 0; #endif - switch(opc) { + switch (sizeop) { case 0: - /* movzbl */ - tcg_out_modrm_offset(s, 0xb6 | P_EXT, data_reg, r0, GUEST_BASE); + tcg_out_modrm_offset(s, OPC_MOVZBL, datalo, base, ofs); break; case 0 | 4: - /* movsbl */ - tcg_out_modrm_offset(s, 0xbe | P_EXT, data_reg, r0, GUEST_BASE); + tcg_out_modrm_offset(s, OPC_MOVSBL + P_REXW, datalo, base, ofs); break; case 1: - /* movzwl */ - tcg_out_modrm_offset(s, 0xb7 | P_EXT, data_reg, r0, GUEST_BASE); + tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs); if (bswap) { - /* rolw $8, data_reg */ - tcg_out8(s, 0x66); - tcg_out_modrm(s, 0xc1, 0, data_reg); - tcg_out8(s, 8); + tcg_out_rolw_8(s, datalo); } break; case 1 | 4: - /* movswl */ - tcg_out_modrm_offset(s, 0xbf | P_EXT, data_reg, r0, GUEST_BASE); if (bswap) { - /* rolw $8, data_reg */ - tcg_out8(s, 0x66); - tcg_out_modrm(s, 0xc1, 0, data_reg); - tcg_out8(s, 8); - - /* movswl data_reg, data_reg */ - tcg_out_modrm(s, 0xbf | P_EXT, data_reg, data_reg); + tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs); + tcg_out_rolw_8(s, datalo); + tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo); + } else { + tcg_out_modrm_offset(s, OPC_MOVSWL + P_REXW, datalo, base, ofs); } break; case 2: - /* movl (r0), data_reg */ - tcg_out_modrm_offset(s, 0x8b, data_reg, r0, GUEST_BASE); + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); if (bswap) { - /* bswap */ - tcg_out_opc(s, (0xc8 + data_reg) | P_EXT); + tcg_out_bswap32(s, datalo); } break; - case 3: - /* XXX: could be nicer */ - if (r0 == data_reg) { - r1 = TCG_REG_EDX; - if (r1 == data_reg) - r1 = TCG_REG_EAX; - tcg_out_mov(s, r1, r0); - r0 = r1; +#if TCG_TARGET_REG_BITS == 64 + case 2 | 4: + if (bswap) { + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + tcg_out_bswap32(s, datalo); + tcg_out_ext32s(s, datalo, datalo); + } else { + tcg_out_modrm_offset(s, OPC_MOVSLQ, datalo, base, ofs); } - if (!bswap) { - tcg_out_modrm_offset(s, 0x8b, data_reg, r0, GUEST_BASE); - tcg_out_modrm_offset(s, 0x8b, data_reg2, r0, GUEST_BASE + 4); + break; +#endif + case 3: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_ld(s, TCG_TYPE_I64, datalo, base, ofs); + if (bswap) { + tcg_out_bswap64(s, datalo); + } } else { - tcg_out_modrm_offset(s, 0x8b, data_reg, r0, GUEST_BASE + 4); - tcg_out_opc(s, (0xc8 + data_reg) | P_EXT); - - tcg_out_modrm_offset(s, 0x8b, data_reg2, r0, GUEST_BASE); - /* bswap */ - tcg_out_opc(s, (0xc8 + data_reg2) | P_EXT); + if (bswap) { + int t = datalo; + datalo = datahi; + datahi = t; + } + if (base != datalo) { + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4); + } else { + tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4); + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + } + if (bswap) { + tcg_out_bswap32(s, datalo); + tcg_out_bswap32(s, datahi); + } } break; default: tcg_abort(); } - -#if defined(CONFIG_SOFTMMU) - /* label2: */ - *label2_ptr = s->code_ptr - label2_ptr - 1; -#endif } - -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, +/* XXX: qemu_ld and qemu_st could be modified to clobber only EDX and + EAX. It will be useful once fixed registers globals are less + common. */ +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap; + int data_reg, data_reg2 = 0; + int addrlo_idx; #if defined(CONFIG_SOFTMMU) - uint8_t *label1_ptr, *label2_ptr; -#endif -#if TARGET_LONG_BITS == 64 -#if defined(CONFIG_SOFTMMU) - uint8_t *label3_ptr; -#endif - int addr_reg2; + int mem_index, s_bits, arg_idx; + uint8_t *label_ptr[3]; #endif - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; - addr_reg = *args++; -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#endif - mem_index = *args; + data_reg = args[0]; + addrlo_idx = 1; + if (TCG_TARGET_REG_BITS == 32 && opc == 3) { + data_reg2 = args[1]; + addrlo_idx = 2; + } - s_bits = opc; +#if defined(CONFIG_SOFTMMU) + mem_index = args[addrlo_idx + 1 + (TARGET_LONG_BITS > TCG_TARGET_REG_BITS)]; + s_bits = opc & 3; - r0 = TCG_REG_EAX; - r1 = TCG_REG_EDX; + tcg_out_tlb_load(s, addrlo_idx, mem_index, s_bits, args, + label_ptr, offsetof(CPUTLBEntry, addr_read)); -#if defined(CONFIG_SOFTMMU) - tcg_out_mov(s, r1, addr_reg); - - tcg_out_mov(s, r0, addr_reg); - - tcg_out_modrm(s, 0xc1, 5, r1); /* shr $x, r1 */ - tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - - tcg_out_modrm(s, 0x81, 4, r0); /* andl $x, r0 */ - tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - - tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */ - tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); - - tcg_out_opc(s, 0x8d); /* lea offset(r1, %ebp), r1 */ - tcg_out8(s, 0x80 | (r1 << 3) | 0x04); - tcg_out8(s, (5 << 3) | r1); - tcg_out32(s, offsetof(CPUState, tlb_table[mem_index][0].addr_write)); + /* TLB Hit. */ + tcg_out_qemu_ld_direct(s, data_reg, data_reg2, + tcg_target_call_iarg_regs[0], 0, opc); - /* cmp 0(r1), r0 */ - tcg_out_modrm_offset(s, 0x3b, r0, r1, 0); - - tcg_out_mov(s, r0, addr_reg); - -#if TARGET_LONG_BITS == 32 - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; - s->code_ptr++; -#else - /* jne label3 */ - tcg_out8(s, 0x70 + JCC_JNE); - label3_ptr = s->code_ptr; + /* jmp label2 */ + tcg_out8(s, OPC_JMP_short); + label_ptr[2] = s->code_ptr; s->code_ptr++; - - /* cmp 4(r1), addr_reg2 */ - tcg_out_modrm_offset(s, 0x3b, addr_reg2, r1, 4); - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; - s->code_ptr++; - - /* label3: */ - *label3_ptr = s->code_ptr - label3_ptr - 1; -#endif + /* TLB Miss. */ + + /* label1: */ + *label_ptr[0] = s->code_ptr - label_ptr[0] - 1; + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + *label_ptr[1] = s->code_ptr - label_ptr[1] - 1; + } /* XXX: move that code at the end of the TB */ -#if TARGET_LONG_BITS == 32 - if (opc == 3) { - tcg_out_mov(s, TCG_REG_EDX, data_reg); - tcg_out_mov(s, TCG_REG_ECX, data_reg2); - tcg_out8(s, 0x6a); /* push Ib */ - tcg_out8(s, mem_index); - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); - tcg_out_addi(s, TCG_REG_ESP, 4); - } else { - switch(opc) { - case 0: - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT, TCG_REG_EDX, data_reg); - break; - case 1: - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, TCG_REG_EDX, data_reg); - break; - case 2: - tcg_out_mov(s, TCG_REG_EDX, data_reg); - break; - } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index); - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); + /* The first argument is already loaded with addrlo. */ + arg_idx = 1; + if (TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[arg_idx++], + args[addrlo_idx + 1]); } -#else - if (opc == 3) { - tcg_out_mov(s, TCG_REG_EDX, addr_reg2); - tcg_out8(s, 0x6a); /* push Ib */ - tcg_out8(s, mem_index); - tcg_out_opc(s, 0x50 + data_reg2); /* push */ - tcg_out_opc(s, 0x50 + data_reg); /* push */ - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); - tcg_out_addi(s, TCG_REG_ESP, 12); - } else { - tcg_out_mov(s, TCG_REG_EDX, addr_reg2); - switch(opc) { - case 0: - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT, TCG_REG_ECX, data_reg); - break; - case 1: - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, TCG_REG_ECX, data_reg); - break; - case 2: - tcg_out_mov(s, TCG_REG_ECX, data_reg); - break; + tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[arg_idx], + mem_index); + tcg_out_calli(s, (tcg_target_long)qemu_ld_helpers[s_bits]); + + switch(opc) { + case 0 | 4: + tcg_out_ext8s(s, data_reg, TCG_REG_EAX, P_REXW); + break; + case 1 | 4: + tcg_out_ext16s(s, data_reg, TCG_REG_EAX, P_REXW); + break; + case 0: + tcg_out_ext8u(s, data_reg, TCG_REG_EAX); + break; + case 1: + tcg_out_ext16u(s, data_reg, TCG_REG_EAX); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); + break; +#if TCG_TARGET_REG_BITS == 64 + case 2 | 4: + tcg_out_ext32s(s, data_reg, TCG_REG_EAX); + break; +#endif + case 3: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_RAX); + } else if (data_reg == TCG_REG_EDX) { + /* xchg %edx, %eax */ + tcg_out_opc(s, OPC_XCHG_ax_r32 + TCG_REG_EDX, 0, 0, 0); + tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_EAX); + } else { + tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); + tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_EDX); } - tcg_out8(s, 0x6a); /* push Ib */ - tcg_out8(s, mem_index); - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); - tcg_out_addi(s, TCG_REG_ESP, 4); + break; + default: + tcg_abort(); } -#endif - - /* jmp label2 */ - tcg_out8(s, 0xeb); - label2_ptr = s->code_ptr; - s->code_ptr++; - - /* label1: */ - *label1_ptr = s->code_ptr - label1_ptr - 1; - /* add x(r1), r0 */ - tcg_out_modrm_offset(s, 0x03, r0, r1, offsetof(CPUTLBEntry, addend) - - offsetof(CPUTLBEntry, addr_write)); + /* label2: */ + *label_ptr[2] = s->code_ptr - label_ptr[2] - 1; #else - r0 = addr_reg; + { + int32_t offset = GUEST_BASE; + int base = args[addrlo_idx]; + + if (TCG_TARGET_REG_BITS == 64) { + /* ??? We assume all operations have left us with register + contents that are zero extended. So far this appears to + be true. If we want to enforce this, we can either do + an explicit zero-extension here, or (if GUEST_BASE == 0) + use the ADDR32 prefix. For now, do nothing. */ + + if (offset != GUEST_BASE) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_RDI, GUEST_BASE); + tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_RDI, base); + base = TCG_REG_RDI, offset = 0; + } + } + + tcg_out_qemu_ld_direct(s, data_reg, data_reg2, base, offset, opc); + } #endif +} +static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi, + int base, tcg_target_long ofs, int sizeop) +{ #ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; + const int bswap = 1; #else - bswap = 0; + const int bswap = 0; #endif - switch(opc) { + /* ??? Ideally we wouldn't need a scratch register. For user-only, + we could perform the bswap twice to restore the original value + instead of moving to the scratch. But as it is, the L constraint + means that the second argument reg is definitely free here. */ + int scratch = tcg_target_call_iarg_regs[1]; + + switch (sizeop) { case 0: - /* movb */ - tcg_out_modrm_offset(s, 0x88, data_reg, r0, GUEST_BASE); + tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R, datalo, base, ofs); break; case 1: if (bswap) { - tcg_out_mov(s, r1, data_reg); - tcg_out8(s, 0x66); /* rolw $8, %ecx */ - tcg_out_modrm(s, 0xc1, 0, r1); - tcg_out8(s, 8); - data_reg = r1; + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_rolw_8(s, scratch); + datalo = scratch; } - /* movw */ - tcg_out8(s, 0x66); - tcg_out_modrm_offset(s, 0x89, data_reg, r0, GUEST_BASE); + tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_DATA16, datalo, base, ofs); break; case 2: if (bswap) { - tcg_out_mov(s, r1, data_reg); - /* bswap data_reg */ - tcg_out_opc(s, (0xc8 + r1) | P_EXT); - data_reg = r1; + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_bswap32(s, scratch); + datalo = scratch; } - /* movl */ - tcg_out_modrm_offset(s, 0x89, data_reg, r0, GUEST_BASE); + tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs); break; case 3: - if (bswap) { - tcg_out_mov(s, r1, data_reg2); - /* bswap data_reg */ - tcg_out_opc(s, (0xc8 + r1) | P_EXT); - tcg_out_modrm_offset(s, 0x89, r1, r0, GUEST_BASE); - tcg_out_mov(s, r1, data_reg); - /* bswap data_reg */ - tcg_out_opc(s, (0xc8 + r1) | P_EXT); - tcg_out_modrm_offset(s, 0x89, r1, r0, GUEST_BASE + 4); + if (TCG_TARGET_REG_BITS == 64) { + if (bswap) { + tcg_out_mov(s, TCG_TYPE_I64, scratch, datalo); + tcg_out_bswap64(s, scratch); + datalo = scratch; + } + tcg_out_st(s, TCG_TYPE_I64, datalo, base, ofs); + } else if (bswap) { + tcg_out_mov(s, TCG_TYPE_I32, scratch, datahi); + tcg_out_bswap32(s, scratch); + tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs); + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_bswap32(s, scratch); + tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs + 4); } else { - tcg_out_modrm_offset(s, 0x89, data_reg, r0, GUEST_BASE); - tcg_out_modrm_offset(s, 0x89, data_reg2, r0, GUEST_BASE + 4); + tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs); + tcg_out_st(s, TCG_TYPE_I32, datahi, base, ofs + 4); } break; default: tcg_abort(); } +} +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, + int opc) +{ + int data_reg, data_reg2 = 0; + int addrlo_idx; #if defined(CONFIG_SOFTMMU) + int mem_index, s_bits; + int stack_adjust; + uint8_t *label_ptr[3]; +#endif + + data_reg = args[0]; + addrlo_idx = 1; + if (TCG_TARGET_REG_BITS == 32 && opc == 3) { + data_reg2 = args[1]; + addrlo_idx = 2; + } + +#if defined(CONFIG_SOFTMMU) + mem_index = args[addrlo_idx + 1 + (TARGET_LONG_BITS > TCG_TARGET_REG_BITS)]; + s_bits = opc; + + tcg_out_tlb_load(s, addrlo_idx, mem_index, s_bits, args, + label_ptr, offsetof(CPUTLBEntry, addr_write)); + + /* TLB Hit. */ + tcg_out_qemu_st_direct(s, data_reg, data_reg2, + tcg_target_call_iarg_regs[0], 0, opc); + + /* jmp label2 */ + tcg_out8(s, OPC_JMP_short); + label_ptr[2] = s->code_ptr; + s->code_ptr++; + + /* TLB Miss. */ + + /* label1: */ + *label_ptr[0] = s->code_ptr - label_ptr[0] - 1; + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + *label_ptr[1] = s->code_ptr - label_ptr[1] - 1; + } + + /* XXX: move that code at the end of the TB */ + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, (opc == 3 ? TCG_TYPE_I64 : TCG_TYPE_I32), + TCG_REG_RSI, data_reg); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_RDX, mem_index); + stack_adjust = 0; + } else if (TARGET_LONG_BITS == 32) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, data_reg); + if (opc == 3) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_ECX, data_reg2); + tcg_out_pushi(s, mem_index); + stack_adjust = 4; + } else { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index); + stack_adjust = 0; + } + } else { + if (opc == 3) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, args[addrlo_idx + 1]); + tcg_out_pushi(s, mem_index); + tcg_out_push(s, data_reg2); + tcg_out_push(s, data_reg); + stack_adjust = 12; + } else { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, args[addrlo_idx + 1]); + switch(opc) { + case 0: + tcg_out_ext8u(s, TCG_REG_ECX, data_reg); + break; + case 1: + tcg_out_ext16u(s, TCG_REG_ECX, data_reg); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_ECX, data_reg); + break; + } + tcg_out_pushi(s, mem_index); + stack_adjust = 4; + } + } + + tcg_out_calli(s, (tcg_target_long)qemu_st_helpers[s_bits]); + + if (stack_adjust == (TCG_TARGET_REG_BITS / 8)) { + /* Pop and discard. This is 2 bytes smaller than the add. */ + tcg_out_pop(s, TCG_REG_ECX); + } else if (stack_adjust != 0) { + tcg_out_addi(s, TCG_REG_ESP, stack_adjust); + } + /* label2: */ - *label2_ptr = s->code_ptr - label2_ptr - 1; + *label_ptr[2] = s->code_ptr - label_ptr[2] - 1; +#else + { + int32_t offset = GUEST_BASE; + int base = args[addrlo_idx]; + + if (TCG_TARGET_REG_BITS == 64) { + /* ??? We assume all operations have left us with register + contents that are zero extended. So far this appears to + be true. If we want to enforce this, we can either do + an explicit zero-extension here, or (if GUEST_BASE == 0) + use the ADDR32 prefix. For now, do nothing. */ + + if (offset != GUEST_BASE) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_RDI, GUEST_BASE); + tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_RDI, base); + base = TCG_REG_RDI, offset = 0; + } + } + + tcg_out_qemu_st_direct(s, data_reg, data_reg2, base, offset, opc); + } #endif } -static inline void tcg_out_op(TCGContext *s, int opc, +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { - int c; - + int c, rexw = 0; + +#if TCG_TARGET_REG_BITS == 64 +# define OP_32_64(x) \ + case glue(glue(INDEX_op_, x), _i64): \ + rexw = P_REXW; /* FALLTHRU */ \ + case glue(glue(INDEX_op_, x), _i32) +#else +# define OP_32_64(x) \ + case glue(glue(INDEX_op_, x), _i32) +#endif + switch(opc) { case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_EAX, args[0]); - tcg_out8(s, 0xe9); /* jmp tb_ret_addr */ - tcg_out32(s, tb_ret_addr - s->code_ptr - 4); + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_EAX, args[0]); + tcg_out_jmp(s, (tcg_target_long) tb_ret_addr); break; case INDEX_op_goto_tb: if (s->tb_jmp_offset) { /* direct jump method */ - tcg_out8(s, 0xe9); /* jmp im */ + tcg_out8(s, OPC_JMP_long); /* jmp im */ s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; tcg_out32(s, 0); } else { /* indirect jump method */ - /* jmp Ev */ - tcg_out_modrm_offset(s, 0xff, 4, -1, + tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1, (tcg_target_long)(s->tb_next + args[0])); } s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; break; case INDEX_op_call: if (const_args[0]) { - tcg_out8(s, 0xe8); - tcg_out32(s, args[0] - (tcg_target_long)s->code_ptr - 4); + tcg_out_calli(s, args[0]); } else { - tcg_out_modrm(s, 0xff, 2, args[0]); + /* call *reg */ + tcg_out_modrm(s, OPC_GRP5, EXT5_CALLN_Ev, args[0]); } break; case INDEX_op_jmp: if (const_args[0]) { - tcg_out8(s, 0xe9); - tcg_out32(s, args[0] - (tcg_target_long)s->code_ptr - 4); + tcg_out_jmp(s, args[0]); } else { - tcg_out_modrm(s, 0xff, 4, args[0]); + /* jmp *reg */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, args[0]); } break; case INDEX_op_br: - tcg_out_jxx(s, JCC_JMP, args[0]); + tcg_out_jxx(s, JCC_JMP, args[0], 0); break; case INDEX_op_movi_i32: tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); break; - case INDEX_op_ld8u_i32: - /* movzbl */ - tcg_out_modrm_offset(s, 0xb6 | P_EXT, args[0], args[1], args[2]); + OP_32_64(ld8u): + /* Note that we can ignore REXW for the zero-extend to 64-bit. */ + tcg_out_modrm_offset(s, OPC_MOVZBL, args[0], args[1], args[2]); break; - case INDEX_op_ld8s_i32: - /* movsbl */ - tcg_out_modrm_offset(s, 0xbe | P_EXT, args[0], args[1], args[2]); + OP_32_64(ld8s): + tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, args[0], args[1], args[2]); break; - case INDEX_op_ld16u_i32: - /* movzwl */ - tcg_out_modrm_offset(s, 0xb7 | P_EXT, args[0], args[1], args[2]); + OP_32_64(ld16u): + /* Note that we can ignore REXW for the zero-extend to 64-bit. */ + tcg_out_modrm_offset(s, OPC_MOVZWL, args[0], args[1], args[2]); break; - case INDEX_op_ld16s_i32: - /* movswl */ - tcg_out_modrm_offset(s, 0xbf | P_EXT, args[0], args[1], args[2]); + OP_32_64(ld16s): + tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, args[0], args[1], args[2]); break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_ld32u_i64: +#endif case INDEX_op_ld_i32: - /* movl */ - tcg_out_modrm_offset(s, 0x8b, args[0], args[1], args[2]); + tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_st8_i32: - /* movb */ - tcg_out_modrm_offset(s, 0x88, args[0], args[1], args[2]); + + OP_32_64(st8): + tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R, + args[0], args[1], args[2]); break; - case INDEX_op_st16_i32: - /* movw */ - tcg_out8(s, 0x66); - tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]); + OP_32_64(st16): + tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16, + args[0], args[1], args[2]); break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_st32_i64: +#endif case INDEX_op_st_i32: - /* movl */ - tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]); + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_sub_i32: + + OP_32_64(add): + /* For 3-operand addition, use LEA. */ + if (args[0] != args[1]) { + TCGArg a0 = args[0], a1 = args[1], a2 = args[2], c3 = 0; + + if (const_args[2]) { + c3 = a2, a2 = -1; + } else if (a0 == a2) { + /* Watch out for dest = src + dest, since we've removed + the matching constraint on the add. */ + tgen_arithr(s, ARITH_ADD + rexw, a0, a1); + break; + } + + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, c3); + break; + } + c = ARITH_ADD; + goto gen_arith; + OP_32_64(sub): c = ARITH_SUB; goto gen_arith; - case INDEX_op_and_i32: + OP_32_64(and): c = ARITH_AND; goto gen_arith; - case INDEX_op_or_i32: + OP_32_64(or): c = ARITH_OR; goto gen_arith; - case INDEX_op_xor_i32: + OP_32_64(xor): c = ARITH_XOR; goto gen_arith; - case INDEX_op_add_i32: - c = ARITH_ADD; gen_arith: if (const_args[2]) { - tgen_arithi(s, c, args[0], args[2], 0); + tgen_arithi(s, c + rexw, args[0], args[2], 0); } else { - tcg_out_modrm(s, 0x01 | (c << 3), args[2], args[0]); + tgen_arithr(s, c + rexw, args[0], args[2]); } break; - case INDEX_op_mul_i32: + + OP_32_64(mul): if (const_args[2]) { int32_t val; val = args[2]; if (val == (int8_t)val) { - tcg_out_modrm(s, 0x6b, args[0], args[0]); + tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, args[0], args[0]); tcg_out8(s, val); } else { - tcg_out_modrm(s, 0x69, args[0], args[0]); + tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, args[0], args[0]); tcg_out32(s, val); } } else { - tcg_out_modrm(s, 0xaf | P_EXT, args[0], args[2]); + tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, args[0], args[2]); } break; - case INDEX_op_mulu2_i32: - tcg_out_modrm(s, 0xf7, 4, args[3]); - break; - case INDEX_op_div2_i32: - tcg_out_modrm(s, 0xf7, 7, args[4]); + + OP_32_64(div2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, args[4]); break; - case INDEX_op_divu2_i32: - tcg_out_modrm(s, 0xf7, 6, args[4]); + OP_32_64(divu2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, args[4]); break; - case INDEX_op_shl_i32: + + OP_32_64(shl): c = SHIFT_SHL; - gen_shift32: - if (const_args[2]) { - if (args[2] == 1) { - tcg_out_modrm(s, 0xd1, c, args[0]); - } else { - tcg_out_modrm(s, 0xc1, c, args[0]); - tcg_out8(s, args[2]); - } - } else { - tcg_out_modrm(s, 0xd3, c, args[0]); - } - break; - case INDEX_op_shr_i32: + goto gen_shift; + OP_32_64(shr): c = SHIFT_SHR; - goto gen_shift32; - case INDEX_op_sar_i32: + goto gen_shift; + OP_32_64(sar): c = SHIFT_SAR; - goto gen_shift32; - case INDEX_op_rotl_i32: + goto gen_shift; + OP_32_64(rotl): c = SHIFT_ROL; - goto gen_shift32; - case INDEX_op_rotr_i32: + goto gen_shift; + OP_32_64(rotr): c = SHIFT_ROR; - goto gen_shift32; - - case INDEX_op_add2_i32: - if (const_args[4]) - tgen_arithi(s, ARITH_ADD, args[0], args[4], 1); - else - tcg_out_modrm(s, 0x01 | (ARITH_ADD << 3), args[4], args[0]); - if (const_args[5]) - tgen_arithi(s, ARITH_ADC, args[1], args[5], 1); - else - tcg_out_modrm(s, 0x01 | (ARITH_ADC << 3), args[5], args[1]); - break; - case INDEX_op_sub2_i32: - if (const_args[4]) - tgen_arithi(s, ARITH_SUB, args[0], args[4], 1); - else - tcg_out_modrm(s, 0x01 | (ARITH_SUB << 3), args[4], args[0]); - if (const_args[5]) - tgen_arithi(s, ARITH_SBB, args[1], args[5], 1); - else - tcg_out_modrm(s, 0x01 | (ARITH_SBB << 3), args[5], args[1]); + goto gen_shift; + gen_shift: + if (const_args[2]) { + tcg_out_shifti(s, c + rexw, args[0], args[2]); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, c, args[0]); + } break; + case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], args[3]); + tcg_out_brcond32(s, args[2], args[0], args[1], const_args[1], + args[3], 0); break; - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args, const_args); + case INDEX_op_setcond_i32: + tcg_out_setcond32(s, args[3], args[0], args[1], + args[2], const_args[2]); break; - case INDEX_op_bswap16_i32: - tcg_out8(s, 0x66); - tcg_out_modrm(s, 0xc1, SHIFT_ROL, args[0]); - tcg_out8(s, 8); + OP_32_64(bswap16): + tcg_out_rolw_8(s, args[0]); break; - case INDEX_op_bswap32_i32: - tcg_out_opc(s, (0xc8 + args[0]) | P_EXT); + OP_32_64(bswap32): + tcg_out_bswap32(s, args[0]); break; - case INDEX_op_neg_i32: - tcg_out_modrm(s, 0xf7, 3, args[0]); + OP_32_64(neg): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, args[0]); break; - - case INDEX_op_not_i32: - tcg_out_modrm(s, 0xf7, 2, args[0]); + OP_32_64(not): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, args[0]); break; - case INDEX_op_ext8s_i32: - tcg_out_modrm(s, 0xbe | P_EXT, args[0], args[1]); + OP_32_64(ext8s): + tcg_out_ext8s(s, args[0], args[1], rexw); break; - case INDEX_op_ext16s_i32: - tcg_out_modrm(s, 0xbf | P_EXT, args[0], args[1]); + OP_32_64(ext16s): + tcg_out_ext16s(s, args[0], args[1], rexw); break; - case INDEX_op_ext8u_i32: - tcg_out_modrm(s, 0xb6 | P_EXT, args[0], args[1]); + OP_32_64(ext8u): + tcg_out_ext8u(s, args[0], args[1]); break; - case INDEX_op_ext16u_i32: - tcg_out_modrm(s, 0xb7 | P_EXT, args[0], args[1]); + OP_32_64(ext16u): + tcg_out_ext16u(s, args[0], args[1]); break; case INDEX_op_qemu_ld8u: @@ -1092,13 +1653,16 @@ static inline void tcg_out_op(TCGContext *s, int opc, case INDEX_op_qemu_ld16s: tcg_out_qemu_ld(s, args, 1 | 4); break; +#if TCG_TARGET_REG_BITS == 64 case INDEX_op_qemu_ld32u: +#endif + case INDEX_op_qemu_ld32: tcg_out_qemu_ld(s, args, 2); break; case INDEX_op_qemu_ld64: tcg_out_qemu_ld(s, args, 3); break; - + case INDEX_op_qemu_st8: tcg_out_qemu_st(s, args, 0); break; @@ -1112,9 +1676,82 @@ static inline void tcg_out_op(TCGContext *s, int opc, tcg_out_qemu_st(s, args, 3); break; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args, const_args, 0); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args, const_args); + break; + case INDEX_op_mulu2_i32: + tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_MUL, args[3]); + break; + case INDEX_op_add2_i32: + if (const_args[4]) { + tgen_arithi(s, ARITH_ADD, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_ADD, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_ADC, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_ADC, args[1], args[5]); + } + break; + case INDEX_op_sub2_i32: + if (const_args[4]) { + tgen_arithi(s, ARITH_SUB, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_SUB, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_SBB, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_SBB, args[1], args[5]); + } + break; +#else /* TCG_TARGET_REG_BITS == 64 */ + case INDEX_op_movi_i64: + tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ld32s_i64: + tcg_out_modrm_offset(s, OPC_MOVSLQ, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i64: + tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_qemu_ld32s: + tcg_out_qemu_ld(s, args, 2 | 4); + break; + + case INDEX_op_brcond_i64: + tcg_out_brcond64(s, args[2], args[0], args[1], const_args[1], + args[3], 0); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond64(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + + case INDEX_op_bswap64_i64: + tcg_out_bswap64(s, args[0]); + break; + case INDEX_op_ext32u_i64: + tcg_out_ext32u(s, args[0], args[1]); + break; + case INDEX_op_ext32s_i64: + tcg_out_ext32s(s, args[0], args[1]); + break; +#endif + default: tcg_abort(); } + +#undef OP_32_64 } static const TCGTargetOpDef x86_op_defs[] = { @@ -1134,10 +1771,9 @@ static const TCGTargetOpDef x86_op_defs[] = { { INDEX_op_st16_i32, { "r", "r" } }, { INDEX_op_st_i32, { "r", "r" } }, - { INDEX_op_add_i32, { "r", "0", "ri" } }, + { INDEX_op_add_i32, { "r", "r", "ri" } }, { INDEX_op_sub_i32, { "r", "0", "ri" } }, { INDEX_op_mul_i32, { "r", "0", "ri" } }, - { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } }, { INDEX_op_div2_i32, { "a", "d", "0", "1", "r" } }, { INDEX_op_divu2_i32, { "a", "d", "0", "1", "r" } }, { INDEX_op_and_i32, { "r", "0", "ri" } }, @@ -1152,10 +1788,6 @@ static const TCGTargetOpDef x86_op_defs[] = { { INDEX_op_brcond_i32, { "r", "ri" } }, - { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } }, - { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } }, - { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, - { INDEX_op_bswap16_i32, { "r", "0" } }, { INDEX_op_bswap32_i32, { "r", "0" } }, @@ -1165,15 +1797,84 @@ static const TCGTargetOpDef x86_op_defs[] = { { INDEX_op_ext8s_i32, { "r", "q" } }, { INDEX_op_ext16s_i32, { "r", "r" } }, - { INDEX_op_ext8u_i32, { "r", "q"} }, - { INDEX_op_ext16u_i32, { "r", "r"} }, + { INDEX_op_ext8u_i32, { "r", "q" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { INDEX_op_setcond_i32, { "q", "r", "ri" } }, -#if TARGET_LONG_BITS == 32 +#if TCG_TARGET_REG_BITS == 32 + { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } }, + { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } }, + { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } }, + { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, +#else + { INDEX_op_mov_i64, { "r", "r" } }, + { INDEX_op_movi_i64, { "r" } }, + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + + { INDEX_op_add_i64, { "r", "0", "re" } }, + { INDEX_op_mul_i64, { "r", "0", "re" } }, + { INDEX_op_div2_i64, { "a", "d", "0", "1", "r" } }, + { INDEX_op_divu2_i64, { "a", "d", "0", "1", "r" } }, + { INDEX_op_sub_i64, { "r", "0", "re" } }, + { INDEX_op_and_i64, { "r", "0", "reZ" } }, + { INDEX_op_or_i64, { "r", "0", "re" } }, + { INDEX_op_xor_i64, { "r", "0", "re" } }, + + { INDEX_op_shl_i64, { "r", "0", "ci" } }, + { INDEX_op_shr_i64, { "r", "0", "ci" } }, + { INDEX_op_sar_i64, { "r", "0", "ci" } }, + { INDEX_op_rotl_i64, { "r", "0", "ci" } }, + { INDEX_op_rotr_i64, { "r", "0", "ci" } }, + + { INDEX_op_brcond_i64, { "r", "re" } }, + { INDEX_op_setcond_i64, { "r", "r", "re" } }, + + { INDEX_op_bswap16_i64, { "r", "0" } }, + { INDEX_op_bswap32_i64, { "r", "0" } }, + { INDEX_op_bswap64_i64, { "r", "0" } }, + { INDEX_op_neg_i64, { "r", "0" } }, + { INDEX_op_not_i64, { "r", "0" } }, + + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_ext8u_i64, { "r", "r" } }, + { INDEX_op_ext16u_i64, { "r", "r" } }, + { INDEX_op_ext32u_i64, { "r", "r" } }, +#endif + +#if TCG_TARGET_REG_BITS == 64 { INDEX_op_qemu_ld8u, { "r", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32s, { "r", "L" } }, + { INDEX_op_qemu_ld64, { "r", "L" } }, + + { INDEX_op_qemu_st8, { "L", "L" } }, + { INDEX_op_qemu_st16, { "L", "L" } }, + { INDEX_op_qemu_st32, { "L", "L" } }, + { INDEX_op_qemu_st64, { "L", "L" } }, +#elif TARGET_LONG_BITS <= TCG_TARGET_REG_BITS + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L" } }, { INDEX_op_qemu_st8, { "cb", "L" } }, @@ -1185,7 +1886,7 @@ static const TCGTargetOpDef x86_op_defs[] = { { INDEX_op_qemu_ld8s, { "r", "L", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } }, { INDEX_op_qemu_st8, { "cb", "L", "L" } }, @@ -1197,64 +1898,85 @@ static const TCGTargetOpDef x86_op_defs[] = { }; static int tcg_target_callee_save_regs[] = { - /* TCG_REG_EBP, */ /* currently used for the global env, so no - need to save */ +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RBP, + TCG_REG_RBX, + TCG_REG_R12, + TCG_REG_R13, + /* TCG_REG_R14, */ /* Currently used for the global env. */ + TCG_REG_R15, +#else + /* TCG_REG_EBP, */ /* Currently used for the global env. */ TCG_REG_EBX, TCG_REG_ESI, TCG_REG_EDI, +#endif }; -static inline void tcg_out_push(TCGContext *s, int reg) -{ - tcg_out_opc(s, 0x50 + reg); -} - -static inline void tcg_out_pop(TCGContext *s, int reg) -{ - tcg_out_opc(s, 0x58 + reg); -} - /* Generate global QEMU prologue and epilogue code */ -void tcg_target_qemu_prologue(TCGContext *s) +static void tcg_target_qemu_prologue(TCGContext *s) { int i, frame_size, push_size, stack_addend; - + /* TB prologue */ - /* save all callee saved registers */ - for(i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + + /* Save all callee saved registers. */ + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { tcg_out_push(s, tcg_target_callee_save_regs[i]); } - /* reserve some stack space */ - push_size = 4 + ARRAY_SIZE(tcg_target_callee_save_regs) * 4; + + /* Reserve some stack space. */ + push_size = 1 + ARRAY_SIZE(tcg_target_callee_save_regs); + push_size *= TCG_TARGET_REG_BITS / 8; + frame_size = push_size + TCG_STATIC_CALL_ARGS_SIZE; - frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & + frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & ~(TCG_TARGET_STACK_ALIGN - 1); stack_addend = frame_size - push_size; tcg_out_addi(s, TCG_REG_ESP, -stack_addend); - tcg_out_modrm(s, 0xff, 4, TCG_REG_EAX); /* jmp *%eax */ - + /* jmp *tb. */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[0]); + /* TB epilogue */ tb_ret_addr = s->code_ptr; + tcg_out_addi(s, TCG_REG_ESP, stack_addend); - for(i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) { + + for (i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) { tcg_out_pop(s, tcg_target_callee_save_regs[i]); } - tcg_out8(s, 0xc3); /* ret */ + tcg_out_opc(s, OPC_RET, 0, 0, 0); } -void tcg_target_init(TCGContext *s) +static void tcg_target_init(TCGContext *s) { +#if !defined(CONFIG_USER_ONLY) /* fail safe */ if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) tcg_abort(); +#endif + + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); + } else { + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xff); + } + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EAX); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EDX); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_ECX); + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RDI); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RSI); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11); + } - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xff); - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_EAX) | - (1 << TCG_REG_EDX) | - (1 << TCG_REG_ECX)); - tcg_regset_clear(s->reserved_regs); tcg_regset_set_reg(s->reserved_regs, TCG_REG_ESP); diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index f97034c..a869cf5 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -23,10 +23,18 @@ */ #define TCG_TARGET_I386 1 +#if defined(__x86_64__) +# define TCG_TARGET_REG_BITS 64 +#else #define TCG_TARGET_REG_BITS 32 +#endif //#define TCG_TARGET_WORDS_BIGENDIAN +#if TCG_TARGET_REG_BITS == 64 +# define TCG_TARGET_NB_REGS 16 +#else #define TCG_TARGET_NB_REGS 8 +#endif enum { TCG_REG_EAX = 0, @@ -37,14 +45,37 @@ enum { TCG_REG_EBP, TCG_REG_ESI, TCG_REG_EDI, + + /* 64-bit registers; always define the symbols to avoid + too much if-deffing. */ + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_RAX = TCG_REG_EAX, + TCG_REG_RCX = TCG_REG_ECX, + TCG_REG_RDX = TCG_REG_EDX, + TCG_REG_RBX = TCG_REG_EBX, + TCG_REG_RSP = TCG_REG_ESP, + TCG_REG_RBP = TCG_REG_EBP, + TCG_REG_RSI = TCG_REG_ESI, + TCG_REG_RDI = TCG_REG_EDI, }; +#define TCG_CT_CONST_S32 0x100 +#define TCG_CT_CONST_U32 0x200 + /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_ESP #define TCG_TARGET_STACK_ALIGN 16 #define TCG_TARGET_CALL_STACK_OFFSET 0 /* optional instructions */ +#define TCG_TARGET_HAS_div2_i32 #define TCG_TARGET_HAS_rot_i32 #define TCG_TARGET_HAS_ext8s_i32 #define TCG_TARGET_HAS_ext16s_i32 @@ -56,13 +87,43 @@ enum { #define TCG_TARGET_HAS_not_i32 // #define TCG_TARGET_HAS_andc_i32 // #define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_div2_i64 +#define TCG_TARGET_HAS_rot_i64 +#define TCG_TARGET_HAS_ext8s_i64 +#define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +#define TCG_TARGET_HAS_ext8u_i64 +#define TCG_TARGET_HAS_ext16u_i64 +#define TCG_TARGET_HAS_ext32u_i64 +#define TCG_TARGET_HAS_bswap16_i64 +#define TCG_TARGET_HAS_bswap32_i64 +#define TCG_TARGET_HAS_bswap64_i64 +#define TCG_TARGET_HAS_neg_i64 +#define TCG_TARGET_HAS_not_i64 +// #define TCG_TARGET_HAS_andc_i64 +// #define TCG_TARGET_HAS_orc_i64 +// #define TCG_TARGET_HAS_eqv_i64 +// #define TCG_TARGET_HAS_nand_i64 +// #define TCG_TARGET_HAS_nor_i64 +#endif #define TCG_TARGET_HAS_GUEST_BASE /* Note: must be synced with dyngen-exec.h */ +#if TCG_TARGET_REG_BITS == 64 +#define TCG_AREG0 TCG_REG_R14 +#define TCG_AREG1 TCG_REG_R15 +#define TCG_AREG2 TCG_REG_R12 +#else #define TCG_AREG0 TCG_REG_EBP #define TCG_AREG1 TCG_REG_EBX #define TCG_AREG2 TCG_REG_ESI +#endif static inline void flush_icache_range(unsigned long start, unsigned long stop) { diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c index 96cc461..7970268 100644 --- a/tcg/ppc/tcg-target.c +++ b/tcg/ppc/tcg-target.c @@ -36,11 +36,6 @@ static uint8_t *tb_ret_addr; #endif #define FAST_PATH -#if TARGET_PHYS_ADDR_BITS <= 32 -#define ADDEND_OFFSET 0 -#else -#define ADDEND_OFFSET 4 -#endif #ifndef GUEST_BASE #define GUEST_BASE 0 @@ -56,7 +51,7 @@ static uint8_t *tb_ret_addr; static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "r0", "r1", - "rp", + "r2", "r3", "r4", "r5", @@ -328,10 +323,12 @@ static int tcg_target_const_match(tcg_target_long val, #define MULLI OPCD( 7) #define CMPLI OPCD(10) #define CMPI OPCD(11) +#define SUBFIC OPCD( 8) #define LWZU OPCD(33) #define STWU OPCD(37) +#define RLWIMI OPCD(20) #define RLWINM OPCD(21) #define RLWNM OPCD(23) @@ -373,6 +370,8 @@ static int tcg_target_const_match(tcg_target_long val, #define NOR XO31(124) #define ANDC XO31( 60) #define ORC XO31(412) +#define EQV XO31(284) +#define NAND XO31(476) #define LBZX XO31( 87) #define LHZX XO31(279) @@ -438,7 +437,7 @@ static const uint32_t tcg_to_bc[10] = { [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE, }; -static void tcg_out_mov(TCGContext *s, int ret, int arg) +static void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) { tcg_out32 (s, OR | SAB (arg, ret, arg)); } @@ -592,11 +591,11 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) /* slow path */ #if TARGET_LONG_BITS == 32 - tcg_out_mov (s, 3, addr_reg); + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg); tcg_out_movi (s, TCG_TYPE_I32, 4, mem_index); #else - tcg_out_mov (s, 3, addr_reg2); - tcg_out_mov (s, 4, addr_reg); + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg2); + tcg_out_mov (s, TCG_TYPE_I32, 4, addr_reg); tcg_out_movi (s, TCG_TYPE_I32, 5, mem_index); #endif @@ -612,23 +611,23 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) case 1: case 2: if (data_reg != 3) - tcg_out_mov (s, data_reg, 3); + tcg_out_mov (s, TCG_TYPE_I32, data_reg, 3); break; case 3: if (data_reg == 3) { if (data_reg2 == 4) { - tcg_out_mov (s, 0, 4); - tcg_out_mov (s, 4, 3); - tcg_out_mov (s, 3, 0); + tcg_out_mov (s, TCG_TYPE_I32, 0, 4); + tcg_out_mov (s, TCG_TYPE_I32, 4, 3); + tcg_out_mov (s, TCG_TYPE_I32, 3, 0); } else { - tcg_out_mov (s, data_reg2, 3); - tcg_out_mov (s, 3, 4); + tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3); + tcg_out_mov (s, TCG_TYPE_I32, 3, 4); } } else { - if (data_reg != 4) tcg_out_mov (s, data_reg, 4); - if (data_reg2 != 3) tcg_out_mov (s, data_reg2, 3); + if (data_reg != 4) tcg_out_mov (s, TCG_TYPE_I32, data_reg, 4); + if (data_reg2 != 3) tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3); } break; } @@ -644,7 +643,7 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) tcg_out32 (s, (LWZ | RT (r0) | RA (r0) - | (ADDEND_OFFSET + offsetof (CPUTLBEntry, addend) + | (offsetof (CPUTLBEntry, addend) - offsetof (CPUTLBEntry, addr_read)) )); /* r0 = env->tlb_table[mem_index][index].addend */ @@ -706,7 +705,7 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) if (r0 == data_reg2) { tcg_out32 (s, LWZ | RT (0) | RA (r0)); tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4); - tcg_out_mov (s, data_reg2, 0); + tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 0); } else { tcg_out32 (s, LWZ | RT (data_reg2) | RA (r0)); @@ -788,11 +787,11 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) /* slow path */ #if TARGET_LONG_BITS == 32 - tcg_out_mov (s, 3, addr_reg); + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg); ir = 4; #else - tcg_out_mov (s, 3, addr_reg2); - tcg_out_mov (s, 4, addr_reg); + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg2); + tcg_out_mov (s, TCG_TYPE_I32, 4, addr_reg); #ifdef TCG_TARGET_CALL_ALIGN_ARGS ir = 5; #else @@ -818,14 +817,14 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) | ME (31))); break; case 2: - tcg_out_mov (s, ir, data_reg); + tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg); break; case 3: #ifdef TCG_TARGET_CALL_ALIGN_ARGS ir = 5; #endif - tcg_out_mov (s, ir++, data_reg2); - tcg_out_mov (s, ir, data_reg); + tcg_out_mov (s, TCG_TYPE_I32, ir++, data_reg2); + tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg); break; } ir++; @@ -843,7 +842,7 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) tcg_out32 (s, (LWZ | RT (r0) | RA (r0) - | (ADDEND_OFFSET + offsetof (CPUTLBEntry, addend) + | (offsetof (CPUTLBEntry, addend) - offsetof (CPUTLBEntry, addr_write)) )); /* r0 = env->tlb_table[mem_index][index].addend */ @@ -901,7 +900,7 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) #endif } -void tcg_target_qemu_prologue (TCGContext *s) +static void tcg_target_qemu_prologue (TCGContext *s) { int i, frame_size; @@ -934,7 +933,10 @@ void tcg_target_qemu_prologue (TCGContext *s) tcg_out32 (s, STW | RS (0) | RA (1) | (frame_size + LR_OFFSET)); #ifdef CONFIG_USE_GUEST_BASE - tcg_out_movi (s, TCG_TYPE_I32, TCG_GUEST_BASE_REG, GUEST_BASE); + if (GUEST_BASE) { + tcg_out_movi (s, TCG_TYPE_I32, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } #endif tcg_out32 (s, MTSPR | RS (3) | CTR); @@ -1076,7 +1078,8 @@ static void tcg_out_bc (TCGContext *s, int bc, int label_index) static void tcg_out_cr7eq_from_cond (TCGContext *s, const TCGArg *args, const int *const_args) { - int cond = args[4], op; + TCGCond cond = args[4]; + int op; struct { int bit1; int bit2; int cond2; } bits[] = { [TCG_COND_LT ] = { CR_LT, CR_LT, TCG_COND_LT }, [TCG_COND_LE ] = { CR_LT, CR_GT, TCG_COND_LT }, @@ -1106,9 +1109,9 @@ static void tcg_out_cr7eq_from_cond (TCGContext *s, const TCGArg *args, case TCG_COND_GEU: op = (b->bit1 != b->bit2) ? CRANDC : CRAND; tcg_out_cmp (s, b->cond2, args[1], args[3], const_args[3], 5); - tcg_out_cmp (s, TCG_COND_EQ, args[1], args[3], const_args[3], 6); - tcg_out_cmp (s, cond, args[0], args[2], const_args[2], 7); - tcg_out32 (s, op | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, b->bit2)); + tcg_out_cmp (s, tcg_unsigned_cond (cond), args[0], args[2], + const_args[2], 7); + tcg_out32 (s, op | BT (7, CR_EQ) | BA (5, CR_EQ) | BB (7, b->bit2)); tcg_out32 (s, CROR | BT (7, CR_EQ) | BA (5, b->bit1) | BB (7, CR_EQ)); break; default: @@ -1116,7 +1119,7 @@ static void tcg_out_cr7eq_from_cond (TCGContext *s, const TCGArg *args, } } -static void tcg_out_setcond (TCGContext *s, int cond, TCGArg arg0, +static void tcg_out_setcond (TCGContext *s, TCGCond cond, TCGArg arg0, TCGArg arg1, TCGArg arg2, int const_arg2) { int crop, sh, arg; @@ -1240,7 +1243,7 @@ static void tcg_out_setcond2 (TCGContext *s, const TCGArg *args, ); } -static void tcg_out_brcond (TCGContext *s, int cond, +static void tcg_out_brcond (TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, int label_index) { @@ -1288,7 +1291,7 @@ void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr) flush_icache_range(jmp_addr, jmp_addr + patch_size); } -static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { switch (opc) { @@ -1478,6 +1481,15 @@ static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, case INDEX_op_orc_i32: tcg_out32 (s, ORC | SAB (args[1], args[0], args[2])); break; + case INDEX_op_eqv_i32: + tcg_out32 (s, EQV | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_nand_i32: + tcg_out32 (s, NAND | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_nor_i32: + tcg_out32 (s, NOR | SAB (args[1], args[0], args[2])); + break; case INDEX_op_mul_i32: if (const_args[2]) { @@ -1517,7 +1529,7 @@ static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, if (args[0] == args[2] || args[0] == args[3]) { tcg_out32 (s, MULLW | TAB (0, args[2], args[3])); tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3])); - tcg_out_mov (s, args[0], 0); + tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); } else { tcg_out32 (s, MULLW | TAB (args[0], args[2], args[3])); @@ -1575,7 +1587,7 @@ static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, case INDEX_op_rotr_i32: if (const_args[2]) { if (!args[2]) { - tcg_out_mov (s, args[0], args[1]); + tcg_out_mov (s, TCG_TYPE_I32, args[0], args[1]); } else { tcg_out32 (s, RLWINM @@ -1588,7 +1600,7 @@ static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, } } else { - tcg_out32 (s, ADDI | RT (0) | RA (args[2]) | 0xffe0); + tcg_out32 (s, SUBFIC | RT (0) | RA (args[2]) | 32); tcg_out32 (s, RLWNM | RA (args[0]) | RS (args[1]) @@ -1603,7 +1615,7 @@ static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, if (args[0] == args[3] || args[0] == args[5]) { tcg_out32 (s, ADDC | TAB (0, args[2], args[4])); tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5])); - tcg_out_mov (s, args[0], 0); + tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); } else { tcg_out32 (s, ADDC | TAB (args[0], args[2], args[4])); @@ -1614,7 +1626,7 @@ static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, if (args[0] == args[3] || args[0] == args[5]) { tcg_out32 (s, SUBFC | TAB (0, args[4], args[2])); tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3])); - tcg_out_mov (s, args[0], 0); + tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); } else { tcg_out32 (s, SUBFC | TAB (args[0], args[4], args[2])); @@ -1641,7 +1653,7 @@ static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, break; case INDEX_op_not_i32: - tcg_out32 (s, NOR | SAB (args[1], args[0], args[0])); + tcg_out32 (s, NOR | SAB (args[1], args[0], args[1])); break; case INDEX_op_qemu_ld8u: @@ -1656,7 +1668,7 @@ static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, case INDEX_op_qemu_ld16s: tcg_out_qemu_ld(s, args, 1 | 4); break; - case INDEX_op_qemu_ld32u: + case INDEX_op_qemu_ld32: tcg_out_qemu_ld(s, args, 2); break; case INDEX_op_qemu_ld64: @@ -1707,6 +1719,77 @@ static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, tcg_out_setcond2 (s, args, const_args); break; + case INDEX_op_bswap16_i32: + /* Stolen from gcc's builtin_bswap16 */ + + /* a1 = abcd */ + + /* r0 = (a1 << 8) & 0xff00 # 00d0 */ + tcg_out32 (s, RLWINM + | RA (0) + | RS (args[1]) + | SH (8) + | MB (16) + | ME (23) + ); + + /* a0 = rotate_left (a1, 24) & 0xff # 000c */ + tcg_out32 (s, RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (24) + | MB (24) + | ME (31) + ); + + /* a0 = a0 | r0 # 00dc */ + tcg_out32 (s, OR | SAB (0, args[0], args[0])); + break; + + case INDEX_op_bswap32_i32: + /* Stolen from gcc's builtin_bswap32 */ + { + int a0 = args[0]; + + /* a1 = args[1] # abcd */ + + if (a0 == args[1]) { + a0 = 0; + } + + /* a0 = rotate_left (a1, 8) # bcda */ + tcg_out32 (s, RLWINM + | RA (a0) + | RS (args[1]) + | SH (8) + | MB (0) + | ME (31) + ); + + /* a0 = (a0 & ~0xff000000) | ((a1 << 24) & 0xff000000) # dcda */ + tcg_out32 (s, RLWIMI + | RA (a0) + | RS (args[1]) + | SH (24) + | MB (0) + | ME (7) + ); + + /* a0 = (a0 & ~0x0000ff00) | ((a1 << 24) & 0x0000ff00) # dcba */ + tcg_out32 (s, RLWIMI + | RA (a0) + | RS (args[1]) + | SH (24) + | MB (16) + | ME (23) + ); + + if (!a0) { + tcg_out_mov (s, TCG_TYPE_I32, args[0], a0); + } + } + break; + default: tcg_dump_ops (s, stderr); tcg_abort (); @@ -1761,16 +1844,22 @@ static const TCGTargetOpDef ppc_op_defs[] = { { INDEX_op_andc_i32, { "r", "r", "r" } }, { INDEX_op_orc_i32, { "r", "r", "r" } }, + { INDEX_op_eqv_i32, { "r", "r", "r" } }, + { INDEX_op_nand_i32, { "r", "r", "r" } }, + { INDEX_op_nor_i32, { "r", "r", "r" } }, { INDEX_op_setcond_i32, { "r", "r", "ri" } }, { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + #if TARGET_LONG_BITS == 32 { INDEX_op_qemu_ld8u, { "r", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L" } }, { INDEX_op_qemu_st8, { "K", "K" } }, @@ -1782,7 +1871,7 @@ static const TCGTargetOpDef ppc_op_defs[] = { { INDEX_op_qemu_ld8s, { "r", "L", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L", "L" } }, { INDEX_op_qemu_ld64, { "r", "L", "L", "L" } }, { INDEX_op_qemu_st8, { "K", "K", "K" } }, @@ -1799,7 +1888,7 @@ static const TCGTargetOpDef ppc_op_defs[] = { { -1 }, }; -void tcg_target_init(TCGContext *s) +static void tcg_target_init(TCGContext *s) { tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); tcg_regset_set32(tcg_target_call_clobber_regs, 0, @@ -1828,9 +1917,6 @@ void tcg_target_init(TCGContext *s) #ifdef _CALL_SYSV tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); #endif -#ifdef CONFIG_USE_GUEST_BASE - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); -#endif tcg_add_target_add_op_defs(ppc_op_defs); } diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 0c71a11..a1f8599 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -65,7 +65,7 @@ enum { /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_R1 #define TCG_TARGET_STACK_ALIGN 16 -#if defined _CALL_DARWIN +#if defined _CALL_DARWIN || defined __APPLE__ #define TCG_TARGET_CALL_STACK_OFFSET 24 #elif defined _CALL_AIX #define TCG_TARGET_CALL_STACK_OFFSET 52 @@ -83,15 +83,16 @@ enum { #define TCG_TARGET_HAS_ext16s_i32 #define TCG_TARGET_HAS_ext8u_i32 #define TCG_TARGET_HAS_ext16u_i32 -/* #define TCG_TARGET_HAS_bswap16_i32 */ -/* #define TCG_TARGET_HAS_bswap32_i32 */ +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 #define TCG_TARGET_HAS_not_i32 #define TCG_TARGET_HAS_neg_i32 #define TCG_TARGET_HAS_andc_i32 #define TCG_TARGET_HAS_orc_i32 +#define TCG_TARGET_HAS_eqv_i32 +#define TCG_TARGET_HAS_nand_i32 +#define TCG_TARGET_HAS_nor_i32 #define TCG_AREG0 TCG_REG_R27 -#define TCG_AREG1 TCG_REG_R24 -#define TCG_AREG2 TCG_REG_R25 #define TCG_TARGET_HAS_GUEST_BASE diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c index fed179c..ebbee34 100644 --- a/tcg/ppc64/tcg-target.c +++ b/tcg/ppc64/tcg-target.c @@ -28,12 +28,6 @@ static uint8_t *tb_ret_addr; #define FAST_PATH -#if TARGET_PHYS_ADDR_BITS == 32 -#define LD_ADDEND LWZ -#else -#define LD_ADDEND LD -#endif - #if TARGET_LONG_BITS == 32 #define LD_ADDR LWZU #define CMP_L 0 @@ -56,7 +50,7 @@ static uint8_t *tb_ret_addr; static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "r0", "r1", - "rp", + "r2", "r3", "r4", "r5", @@ -441,7 +435,7 @@ static const uint32_t tcg_to_bc[10] = { [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE, }; -static void tcg_out_mov (TCGContext *s, int ret, int arg) +static void tcg_out_mov (TCGContext *s, TCGType type, int ret, int arg) { tcg_out32 (s, OR | SAB (arg, ret, arg)); } @@ -650,7 +644,7 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) #endif /* slow path */ - tcg_out_mov (s, 3, addr_reg); + tcg_out_mov (s, TCG_TYPE_I64, 3, addr_reg); tcg_out_movi (s, TCG_TYPE_I64, 4, mem_index); tcg_out_call (s, (tcg_target_long) qemu_ld_helpers[s_bits], 1); @@ -670,7 +664,7 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) case 2: case 3: if (data_reg != 3) - tcg_out_mov (s, data_reg, 3); + tcg_out_mov (s, TCG_TYPE_I64, data_reg, 3); break; } label2_ptr = s->code_ptr; @@ -682,7 +676,7 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) #endif /* r0 now contains &env->tlb_table[mem_index][index].addr_read */ - tcg_out32 (s, (LD_ADDEND + tcg_out32 (s, (LD | RT (r0) | RA (r0) | (offsetof (CPUTLBEntry, addend) @@ -796,7 +790,7 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) #endif /* slow path */ - tcg_out_mov (s, 3, addr_reg); + tcg_out_mov (s, TCG_TYPE_I64, 3, addr_reg); tcg_out_rld (s, RLDICL, 4, data_reg, 0, 64 - (1 << (3 + opc))); tcg_out_movi (s, TCG_TYPE_I64, 5, mem_index); @@ -810,7 +804,7 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr); #endif - tcg_out32 (s, (LD_ADDEND + tcg_out32 (s, (LD | RT (r0) | RA (r0) | (offsetof (CPUTLBEntry, addend) @@ -866,7 +860,7 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) #endif } -void tcg_target_qemu_prologue (TCGContext *s) +static void tcg_target_qemu_prologue (TCGContext *s) { int i, frame_size; #ifndef __APPLE__ @@ -905,7 +899,10 @@ void tcg_target_qemu_prologue (TCGContext *s) tcg_out32 (s, STD | RS (0) | RA (1) | (frame_size + 16)); #ifdef CONFIG_USE_GUEST_BASE - tcg_out_movi (s, TCG_TYPE_I64, TCG_GUEST_BASE_REG, GUEST_BASE); + if (GUEST_BASE) { + tcg_out_movi (s, TCG_TYPE_I64, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } #endif tcg_out32 (s, MTSPR | RS (3) | CTR); @@ -1049,8 +1046,9 @@ static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, } -static void tcg_out_setcond (TCGContext *s, TCGType type, int cond, TCGArg arg0, - TCGArg arg1, TCGArg arg2, int const_arg2) +static void tcg_out_setcond (TCGContext *s, TCGType type, TCGCond cond, + TCGArg arg0, TCGArg arg1, TCGArg arg2, + int const_arg2) { int crop, sh, arg; @@ -1180,7 +1178,7 @@ static void tcg_out_bc (TCGContext *s, int bc, int label_index) } } -static void tcg_out_brcond (TCGContext *s, int cond, +static void tcg_out_brcond (TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, int label_index, int arch64) { @@ -1199,7 +1197,7 @@ void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr) flush_icache_range (jmp_addr, jmp_addr + patch_size); } -static void tcg_out_op (TCGContext *s, int opc, const TCGArg *args, +static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { int c; @@ -1517,6 +1515,7 @@ static void tcg_out_op (TCGContext *s, int opc, const TCGArg *args, case INDEX_op_qemu_ld16s: tcg_out_qemu_ld (s, args, 1 | 4); break; + case INDEX_op_qemu_ld32: case INDEX_op_qemu_ld32u: tcg_out_qemu_ld (s, args, 2); break; @@ -1645,6 +1644,7 @@ static const TCGTargetOpDef ppc_op_defs[] = { { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld32u, { "r", "L" } }, { INDEX_op_qemu_ld32s, { "r", "L" } }, { INDEX_op_qemu_ld64, { "r", "L" } }, @@ -1666,7 +1666,7 @@ static const TCGTargetOpDef ppc_op_defs[] = { { -1 }, }; -void tcg_target_init (TCGContext *s) +static void tcg_target_init (TCGContext *s) { tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); @@ -1695,9 +1695,5 @@ void tcg_target_init (TCGContext *s) #endif tcg_regset_set_reg (s->reserved_regs, TCG_REG_R13); -#ifdef CONFIG_USE_GUEST_BASE - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); -#endif - tcg_add_target_add_op_defs (ppc_op_defs); } diff --git a/tcg/ppc64/tcg-target.h b/tcg/ppc64/tcg-target.h index f5de642..8a6db11 100644 --- a/tcg/ppc64/tcg-target.h +++ b/tcg/ppc64/tcg-target.h @@ -80,6 +80,9 @@ enum { #define TCG_TARGET_HAS_neg_i32 /* #define TCG_TARGET_HAS_andc_i32 */ /* #define TCG_TARGET_HAS_orc_i32 */ +/* #define TCG_TARGET_HAS_eqv_i32 */ +/* #define TCG_TARGET_HAS_nand_i32 */ +/* #define TCG_TARGET_HAS_nor_i32 */ #define TCG_TARGET_HAS_div_i64 /* #define TCG_TARGET_HAS_rot_i64 */ @@ -96,9 +99,11 @@ enum { #define TCG_TARGET_HAS_neg_i64 /* #define TCG_TARGET_HAS_andc_i64 */ /* #define TCG_TARGET_HAS_orc_i64 */ +/* #define TCG_TARGET_HAS_eqv_i64 */ +/* #define TCG_TARGET_HAS_nand_i64 */ +/* #define TCG_TARGET_HAS_nor_i64 */ #define TCG_AREG0 TCG_REG_R27 -#define TCG_AREG1 TCG_REG_R24 -#define TCG_AREG2 TCG_REG_R25 #define TCG_TARGET_HAS_GUEST_BASE +#define TCG_TARGET_EXTEND_ARGS 1 diff --git a/tcg/sparc/tcg-target.c b/tcg/sparc/tcg-target.c index d4ddaa7..5f1353a 100644 --- a/tcg/sparc/tcg-target.c +++ b/tcg/sparc/tcg-target.c @@ -304,7 +304,7 @@ static void tcg_out_arithc(TCGContext *s, int rd, int rs1, | (val2const ? INSN_IMM13(val2) : INSN_RS2(val2))); } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) { tcg_out_arith(s, ret, arg, TCG_REG_G0, ARITH_OR); } @@ -520,7 +520,7 @@ static void tcg_out_cmp(TCGContext *s, TCGArg c1, TCGArg c2, int c2const) tcg_out_arithc(s, TCG_REG_G0, c1, c2, c2const, ARITH_SUBCC); } -static void tcg_out_brcond_i32(TCGContext *s, int cond, +static void tcg_out_brcond_i32(TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, int label_index) { @@ -530,7 +530,7 @@ static void tcg_out_brcond_i32(TCGContext *s, int cond, } #if TCG_TARGET_REG_BITS == 64 -static void tcg_out_brcond_i64(TCGContext *s, int cond, +static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, int label_index) { @@ -539,7 +539,7 @@ static void tcg_out_brcond_i64(TCGContext *s, int cond, tcg_out_nop(s); } #else -static void tcg_out_brcond2_i32(TCGContext *s, int cond, +static void tcg_out_brcond2_i32(TCGContext *s, TCGCond cond, TCGArg al, TCGArg ah, TCGArg bl, int blconst, TCGArg bh, int bhconst, int label_dest) @@ -587,7 +587,7 @@ static void tcg_out_brcond2_i32(TCGContext *s, int cond, } #endif -static void tcg_out_setcond_i32(TCGContext *s, int cond, TCGArg ret, +static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGArg ret, TCGArg c1, TCGArg c2, int c2const) { TCGArg t; @@ -643,7 +643,7 @@ static void tcg_out_setcond_i32(TCGContext *s, int cond, TCGArg ret, } #if TCG_TARGET_REG_BITS == 64 -static void tcg_out_setcond_i64(TCGContext *s, int cond, TCGArg ret, +static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGArg ret, TCGArg c1, TCGArg c2, int c2const) { tcg_out_cmp(s, c1, c2, c2const); @@ -653,7 +653,7 @@ static void tcg_out_setcond_i64(TCGContext *s, int cond, TCGArg ret, | MOVCC_XCC | INSN_IMM11(1)); } #else -static void tcg_out_setcond2_i32(TCGContext *s, int cond, TCGArg ret, +static void tcg_out_setcond2_i32(TCGContext *s, TCGCond cond, TCGArg ret, TCGArg al, TCGArg ah, TCGArg bl, int blconst, TCGArg bh, int bhconst) @@ -691,7 +691,7 @@ static void tcg_out_setcond2_i32(TCGContext *s, int cond, TCGArg ret, #endif /* Generate global QEMU prologue and epilogue code */ -void tcg_target_qemu_prologue(TCGContext *s) +static void tcg_target_qemu_prologue(TCGContext *s) { tcg_out32(s, SAVE | INSN_RD(TCG_REG_O6) | INSN_RS1(TCG_REG_O6) | INSN_IMM13(-TCG_TARGET_STACK_MINFRAME)); @@ -725,11 +725,13 @@ static const void * const qemu_st_helpers[4] = { #define TARGET_LD_OP LDX #endif -#if TARGET_PHYS_ADDR_BITS == 32 +#if defined(CONFIG_SOFTMMU) +#if HOST_LONG_BITS == 32 #define TARGET_ADDEND_LD_OP LDUW #else #define TARGET_ADDEND_LD_OP LDX #endif +#endif #ifdef __arch64__ #define HOST_LD_OP LDX @@ -793,7 +795,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, tcg_out32(s, 0); /* mov (delay slot) */ - tcg_out_mov(s, arg0, addr_reg); + tcg_out_mov(s, TCG_TYPE_PTR, arg0, addr_reg); /* mov */ tcg_out_movi(s, TCG_TYPE_I32, arg1, mem_index); @@ -843,7 +845,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, case 3: default: /* mov */ - tcg_out_mov(s, data_reg, arg0); + tcg_out_mov(s, TCG_TYPE_REG, data_reg, arg0); break; } @@ -1005,10 +1007,10 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, tcg_out32(s, 0); /* mov (delay slot) */ - tcg_out_mov(s, arg0, addr_reg); + tcg_out_mov(s, TCG_TYPE_PTR, arg0, addr_reg); /* mov */ - tcg_out_mov(s, arg1, data_reg); + tcg_out_mov(s, TCG_TYPE_REG, arg1, data_reg); /* mov */ tcg_out_movi(s, TCG_TYPE_I32, arg2, mem_index); @@ -1111,7 +1113,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, #endif } -static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { int c; @@ -1316,7 +1318,10 @@ static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, case INDEX_op_qemu_ld16s: tcg_out_qemu_ld(s, args, 1 | 4); break; + case INDEX_op_qemu_ld32: +#if TCG_TARGET_REG_BITS == 64 case INDEX_op_qemu_ld32u: +#endif tcg_out_qemu_ld(s, args, 2); break; #if TCG_TARGET_REG_BITS == 64 @@ -1472,8 +1477,9 @@ static const TCGTargetOpDef sparc_op_defs[] = { { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, #if TCG_TARGET_REG_BITS == 64 + { INDEX_op_qemu_ld32u, { "r", "L" } }, { INDEX_op_qemu_ld32s, { "r", "L" } }, #endif @@ -1527,7 +1533,7 @@ static const TCGTargetOpDef sparc_op_defs[] = { { -1 }, }; -void tcg_target_init(TCGContext *s) +static void tcg_target_init(TCGContext *s) { tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); #if TCG_TARGET_REG_BITS == 64 diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h index dbc574d..df0785e 100644 --- a/tcg/sparc/tcg-target.h +++ b/tcg/sparc/tcg-target.h @@ -87,6 +87,10 @@ enum { #define TCG_TARGET_STACK_ALIGN 8 #endif +#ifdef __arch64__ +#define TCG_TARGET_EXTEND_ARGS 1 +#endif + /* optional instructions */ #define TCG_TARGET_HAS_div_i32 // #define TCG_TARGET_HAS_rot_i32 @@ -100,6 +104,9 @@ enum { #define TCG_TARGET_HAS_not_i32 #define TCG_TARGET_HAS_andc_i32 #define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_div_i64 @@ -117,21 +124,18 @@ enum { #define TCG_TARGET_HAS_not_i64 #define TCG_TARGET_HAS_andc_i64 #define TCG_TARGET_HAS_orc_i64 +// #define TCG_TARGET_HAS_eqv_i64 +// #define TCG_TARGET_HAS_nand_i64 +// #define TCG_TARGET_HAS_nor_i64 #endif -/* Note: must be synced with dyngen-exec.h and Makefile.target */ +/* Note: must be synced with dyngen-exec.h */ #ifdef CONFIG_SOLARIS #define TCG_AREG0 TCG_REG_G2 -#define TCG_AREG1 TCG_REG_G3 -#define TCG_AREG2 TCG_REG_G4 #elif defined(__sparc_v9__) #define TCG_AREG0 TCG_REG_G5 -#define TCG_AREG1 TCG_REG_G6 -#define TCG_AREG2 TCG_REG_G7 #else #define TCG_AREG0 TCG_REG_G6 -#define TCG_AREG1 TCG_REG_G1 -#define TCG_AREG2 TCG_REG_G2 #endif static inline void flush_icache_range(unsigned long start, unsigned long stop) diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 6ae1760..207a89f 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -25,60 +25,60 @@ int gen_new_label(void); -static inline void tcg_gen_op1_i32(int opc, TCGv_i32 arg1) +static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 arg1) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); } -static inline void tcg_gen_op1_i64(int opc, TCGv_i64 arg1) +static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 arg1) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); } -static inline void tcg_gen_op1i(int opc, TCGArg arg1) +static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg arg1) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = arg1; } -static inline void tcg_gen_op2_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2) +static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); *gen_opparam_ptr++ = GET_TCGV_I32(arg2); } -static inline void tcg_gen_op2_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2) +static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); *gen_opparam_ptr++ = GET_TCGV_I64(arg2); } -static inline void tcg_gen_op2i_i32(int opc, TCGv_i32 arg1, TCGArg arg2) +static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGArg arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); *gen_opparam_ptr++ = arg2; } -static inline void tcg_gen_op2i_i64(int opc, TCGv_i64 arg1, TCGArg arg2) +static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGArg arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); *gen_opparam_ptr++ = arg2; } -static inline void tcg_gen_op2ii(int opc, TCGArg arg1, TCGArg arg2) +static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg arg1, TCGArg arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = arg1; *gen_opparam_ptr++ = arg2; } -static inline void tcg_gen_op3_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3) { *gen_opc_ptr++ = opc; @@ -87,7 +87,7 @@ static inline void tcg_gen_op3_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = GET_TCGV_I32(arg3); } -static inline void tcg_gen_op3_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3) { *gen_opc_ptr++ = opc; @@ -96,8 +96,8 @@ static inline void tcg_gen_op3_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = GET_TCGV_I64(arg3); } -static inline void tcg_gen_op3i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGArg arg3) +static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGArg arg3) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); @@ -105,8 +105,8 @@ static inline void tcg_gen_op3i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = arg3; } -static inline void tcg_gen_op3i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGArg arg3) +static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGArg arg3) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); @@ -114,8 +114,8 @@ static inline void tcg_gen_op3i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = arg3; } -static inline void tcg_gen_ldst_op_i32(int opc, TCGv_i32 val, TCGv_ptr base, - TCGArg offset) +static inline void tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, + TCGv_ptr base, TCGArg offset) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(val); @@ -123,8 +123,8 @@ static inline void tcg_gen_ldst_op_i32(int opc, TCGv_i32 val, TCGv_ptr base, *gen_opparam_ptr++ = offset; } -static inline void tcg_gen_ldst_op_i64(int opc, TCGv_i64 val, TCGv_ptr base, - TCGArg offset) +static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_ptr base, TCGArg offset) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(val); @@ -132,8 +132,8 @@ static inline void tcg_gen_ldst_op_i64(int opc, TCGv_i64 val, TCGv_ptr base, *gen_opparam_ptr++ = offset; } -static inline void tcg_gen_qemu_ldst_op_i64_i32(int opc, TCGv_i64 val, TCGv_i32 addr, - TCGArg mem_index) +static inline void tcg_gen_qemu_ldst_op_i64_i32(TCGOpcode opc, TCGv_i64 val, + TCGv_i32 addr, TCGArg mem_index) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(val); @@ -141,8 +141,8 @@ static inline void tcg_gen_qemu_ldst_op_i64_i32(int opc, TCGv_i64 val, TCGv_i32 *gen_opparam_ptr++ = mem_index; } -static inline void tcg_gen_qemu_ldst_op_i64_i64(int opc, TCGv_i64 val, TCGv_i64 addr, - TCGArg mem_index) +static inline void tcg_gen_qemu_ldst_op_i64_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_i64 addr, TCGArg mem_index) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(val); @@ -150,7 +150,7 @@ static inline void tcg_gen_qemu_ldst_op_i64_i64(int opc, TCGv_i64 val, TCGv_i64 *gen_opparam_ptr++ = mem_index; } -static inline void tcg_gen_op4_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4) { *gen_opc_ptr++ = opc; @@ -160,7 +160,7 @@ static inline void tcg_gen_op4_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = GET_TCGV_I32(arg4); } -static inline void tcg_gen_op4_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGv_i64 arg4) { *gen_opc_ptr++ = opc; @@ -170,7 +170,7 @@ static inline void tcg_gen_op4_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = GET_TCGV_I64(arg4); } -static inline void tcg_gen_op4i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGArg arg4) { *gen_opc_ptr++ = opc; @@ -180,7 +180,7 @@ static inline void tcg_gen_op4i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = arg4; } -static inline void tcg_gen_op4i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGArg arg4) { *gen_opc_ptr++ = opc; @@ -190,7 +190,7 @@ static inline void tcg_gen_op4i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = arg4; } -static inline void tcg_gen_op4ii_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGArg arg3, TCGArg arg4) { *gen_opc_ptr++ = opc; @@ -200,7 +200,7 @@ static inline void tcg_gen_op4ii_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = arg4; } -static inline void tcg_gen_op4ii_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGArg arg3, TCGArg arg4) { *gen_opc_ptr++ = opc; @@ -210,7 +210,7 @@ static inline void tcg_gen_op4ii_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = arg4; } -static inline void tcg_gen_op5_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5) { *gen_opc_ptr++ = opc; @@ -221,7 +221,7 @@ static inline void tcg_gen_op5_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = GET_TCGV_I32(arg5); } -static inline void tcg_gen_op5_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5) { *gen_opc_ptr++ = opc; @@ -232,7 +232,7 @@ static inline void tcg_gen_op5_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = GET_TCGV_I64(arg5); } -static inline void tcg_gen_op5i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4, TCGArg arg5) { *gen_opc_ptr++ = opc; @@ -243,7 +243,7 @@ static inline void tcg_gen_op5i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = arg5; } -static inline void tcg_gen_op5i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGv_i64 arg4, TCGArg arg5) { *gen_opc_ptr++ = opc; @@ -254,7 +254,31 @@ static inline void tcg_gen_op5i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = arg5; } -static inline void tcg_gen_op6_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGv_i32 arg3, + TCGArg arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = arg4; + *gen_opparam_ptr++ = arg5; +} + +static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGv_i64 arg3, + TCGArg arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = arg4; + *gen_opparam_ptr++ = arg5; +} + +static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5, TCGv_i32 arg6) { @@ -267,7 +291,7 @@ static inline void tcg_gen_op6_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = GET_TCGV_I32(arg6); } -static inline void tcg_gen_op6_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5, TCGv_i64 arg6) { @@ -280,7 +304,7 @@ static inline void tcg_gen_op6_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = GET_TCGV_I64(arg6); } -static inline void tcg_gen_op6i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5, TCGArg arg6) { @@ -293,7 +317,7 @@ static inline void tcg_gen_op6i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = arg6; } -static inline void tcg_gen_op6i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5, TCGArg arg6) { @@ -306,9 +330,9 @@ static inline void tcg_gen_op6i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = arg6; } -static inline void tcg_gen_op6ii_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGv_i32 arg3, TCGv_i32 arg4, TCGArg arg5, - TCGArg arg6) +static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGv_i32 arg3, + TCGv_i32 arg4, TCGArg arg5, TCGArg arg6) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); @@ -319,9 +343,9 @@ static inline void tcg_gen_op6ii_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, *gen_opparam_ptr++ = arg6; } -static inline void tcg_gen_op6ii_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGv_i64 arg3, TCGv_i64 arg4, TCGArg arg5, - TCGArg arg6) +static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGv_i64 arg3, + TCGv_i64 arg4, TCGArg arg5, TCGArg arg6) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); @@ -353,6 +377,13 @@ static inline void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg) tcg_gen_op2i_i32(INDEX_op_movi_i32, ret, arg); } +/* A version of dh_sizemask from def-helper.h that doesn't rely on + preprocessor magic. */ +static inline int tcg_gen_sizemask(int n, int is_64bit, int is_signed) +{ + return (is_64bit << n*2) | (is_signed << (n*2 + 1)); +} + /* helper calls */ static inline void tcg_gen_helperN(void *func, int flags, int sizemask, TCGArg ret, int nargs, TCGArg *args) @@ -364,8 +395,25 @@ static inline void tcg_gen_helperN(void *func, int flags, int sizemask, tcg_temp_free_ptr(fn); } -/* FIXME: Should this be pure? */ -static inline void tcg_gen_helper64(void *func, TCGv_i64 ret, +/* Note: Both tcg_gen_helper32() and tcg_gen_helper64() are currently + reserved for helpers in tcg-runtime.c. These helpers are all const + and pure, hence the call to tcg_gen_callN() with TCG_CALL_CONST | + TCG_CALL_PURE. This may need to be adjusted if these functions + start to be used with other helpers. */ +static inline void tcg_gen_helper32(void *func, int sizemask, TCGv_i32 ret, + TCGv_i32 a, TCGv_i32 b) +{ + TCGv_ptr fn; + TCGArg args[2]; + fn = tcg_const_ptr((tcg_target_long)func); + args[0] = GET_TCGV_I32(a); + args[1] = GET_TCGV_I32(b); + tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask, + GET_TCGV_I32(ret), 2, args); + tcg_temp_free_ptr(fn); +} + +static inline void tcg_gen_helper64(void *func, int sizemask, TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { TCGv_ptr fn; @@ -373,7 +421,8 @@ static inline void tcg_gen_helper64(void *func, TCGv_i64 ret, fn = tcg_const_ptr((tcg_target_long)func); args[0] = GET_TCGV_I64(a); args[1] = GET_TCGV_I64(b); - tcg_gen_callN(&tcg_ctx, fn, 0, 7, GET_TCGV_I64(ret), 2, args); + tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask, + GET_TCGV_I64(ret), 2, args); tcg_temp_free_ptr(fn); } @@ -575,28 +624,28 @@ static inline void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } -static inline void tcg_gen_brcond_i32(int cond, TCGv_i32 arg1, TCGv_i32 arg2, - int label_index) +static inline void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, + TCGv_i32 arg2, int label_index) { tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_index); } -static inline void tcg_gen_brcondi_i32(int cond, TCGv_i32 arg1, int32_t arg2, - int label_index) +static inline void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, + int32_t arg2, int label_index) { TCGv_i32 t0 = tcg_const_i32(arg2); tcg_gen_brcond_i32(cond, arg1, t0, label_index); tcg_temp_free_i32(t0); } -static inline void tcg_gen_setcond_i32(int cond, TCGv_i32 ret, +static inline void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_op4i_i32(INDEX_op_setcond_i32, ret, arg1, arg2, cond); } -static inline void tcg_gen_setcondi_i32(int cond, TCGv_i32 ret, TCGv_i32 arg1, - int32_t arg2) +static inline void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, int32_t arg2) { TCGv_i32 t0 = tcg_const_i32(arg2); tcg_gen_setcond_i32(cond, ret, arg1, t0); @@ -635,7 +684,7 @@ static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); } -#else +#elif defined(TCG_TARGET_HAS_div2_i32) static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { TCGv_i32 t0; @@ -671,6 +720,50 @@ static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, t0, arg2); tcg_temp_free_i32(t0); } +#else +static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 0, 1); + sizemask |= tcg_gen_sizemask(1, 0, 1); + sizemask |= tcg_gen_sizemask(2, 0, 1); + + tcg_gen_helper32(tcg_helper_div_i32, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 0, 1); + sizemask |= tcg_gen_sizemask(1, 0, 1); + sizemask |= tcg_gen_sizemask(2, 0, 1); + + tcg_gen_helper32(tcg_helper_rem_i32, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 0, 0); + sizemask |= tcg_gen_sizemask(1, 0, 0); + sizemask |= tcg_gen_sizemask(2, 0, 0); + + tcg_gen_helper32(tcg_helper_divu_i32, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 0, 0); + sizemask |= tcg_gen_sizemask(1, 0, 0); + sizemask |= tcg_gen_sizemask(2, 0, 0); + + tcg_gen_helper32(tcg_helper_remu_i32, sizemask, ret, arg1, arg2); +} #endif #if TCG_TARGET_REG_BITS == 32 @@ -829,7 +922,13 @@ static inline void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) specific code (x86) */ static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_shl_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_shl_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) @@ -839,7 +938,13 @@ static inline void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_shr_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_shr_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) @@ -849,7 +954,13 @@ static inline void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_sar_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_sar_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) @@ -857,15 +968,15 @@ static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) tcg_gen_shifti_i64(ret, arg1, arg2, 1, 1); } -static inline void tcg_gen_brcond_i64(int cond, TCGv_i64 arg1, TCGv_i64 arg2, - int label_index) +static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, + TCGv_i64 arg2, int label_index) { tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2), cond, label_index); } -static inline void tcg_gen_setcond_i64(int cond, TCGv_i64 ret, +static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { tcg_gen_op6i_i32(INDEX_op_setcond2_i32, TCGV_LOW(ret), @@ -897,22 +1008,46 @@ static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_div_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_div_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_rem_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_rem_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_divu_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_divu_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_remu_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_remu_i64, sizemask, ret, arg1, arg2); } #else @@ -1098,13 +1233,13 @@ static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) } } -static inline void tcg_gen_brcond_i64(int cond, TCGv_i64 arg1, TCGv_i64 arg2, - int label_index) +static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, + TCGv_i64 arg2, int label_index) { tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, label_index); } -static inline void tcg_gen_setcond_i64(int cond, TCGv_i64 ret, +static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { tcg_gen_op4i_i64(INDEX_op_setcond_i64, ret, arg1, arg2, cond); @@ -1135,7 +1270,7 @@ static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); } -#else +#elif defined(TCG_TARGET_HAS_div2_i64) static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { TCGv_i64 t0; @@ -1171,6 +1306,50 @@ static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, t0, arg2); tcg_temp_free_i64(t0); } +#else +static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_div_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_rem_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_divu_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_remu_i64, sizemask, ret, arg1, arg2); +} #endif #endif @@ -1205,16 +1384,16 @@ static inline void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) tcg_temp_free_i64(t0); } } -static inline void tcg_gen_brcondi_i64(int cond, TCGv_i64 arg1, int64_t arg2, - int label_index) +static inline void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, + int64_t arg2, int label_index) { TCGv_i64 t0 = tcg_const_i64(arg2); tcg_gen_brcond_i64(cond, arg1, t0, label_index); tcg_temp_free_i64(t0); } -static inline void tcg_gen_setcondi_i64(int cond, TCGv_i64 ret, TCGv_i64 arg1, - int64_t arg2) +static inline void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, int64_t arg2) { TCGv_i64 t0 = tcg_const_i64(arg2); tcg_gen_setcond_i64(cond, ret, arg1, t0); @@ -1595,6 +1774,9 @@ static inline void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) { #ifdef TCG_TARGET_HAS_not_i64 tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg); +#elif defined(TCG_TARGET_HAS_not_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_not_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_not_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); #else tcg_gen_xori_i64(ret, arg, -1); #endif @@ -1679,38 +1861,71 @@ static inline void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) static inline void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { +#ifdef TCG_TARGET_HAS_eqv_i32 + tcg_gen_op3_i32(INDEX_op_eqv_i32, ret, arg1, arg2); +#else tcg_gen_xor_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); +#endif } static inline void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { +#ifdef TCG_TARGET_HAS_eqv_i64 + tcg_gen_op3_i64(INDEX_op_eqv_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_eqv_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_eqv_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_eqv_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else tcg_gen_xor_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); +#endif } static inline void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { +#ifdef TCG_TARGET_HAS_nand_i32 + tcg_gen_op3_i32(INDEX_op_nand_i32, ret, arg1, arg2); +#else tcg_gen_and_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); +#endif } static inline void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { +#ifdef TCG_TARGET_HAS_nand_i64 + tcg_gen_op3_i64(INDEX_op_nand_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_nand_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_nand_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_nand_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else tcg_gen_and_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); +#endif } static inline void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { +#ifdef TCG_TARGET_HAS_nor_i32 + tcg_gen_op3_i32(INDEX_op_nor_i32, ret, arg1, arg2); +#else tcg_gen_or_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); +#endif } static inline void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { +#ifdef TCG_TARGET_HAS_nor_i64 + tcg_gen_op3_i64(INDEX_op_nor_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_nor_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_nor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_nor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else tcg_gen_or_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); +#endif } static inline void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) @@ -1880,6 +2095,44 @@ static inline void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) } } +static inline void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, + TCGv_i32 arg2, unsigned int ofs, + unsigned int len) +{ +#ifdef TCG_TARGET_HAS_deposit_i32 + tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len); +#else + uint32_t mask = (1u << len) - 1; + TCGv_i32 t1 = tcg_temp_new_i32 (); + + tcg_gen_andi_i32(t1, arg2, mask); + tcg_gen_shli_i32(t1, t1, ofs); + tcg_gen_andi_i32(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i32(ret, ret, t1); + + tcg_temp_free_i32(t1); +#endif +} + +static inline void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, + TCGv_i64 arg2, unsigned int ofs, + unsigned int len) +{ +#ifdef TCG_TARGET_HAS_deposit_i64 + tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); +#else + uint64_t mask = (1ull << len) - 1; + TCGv_i64 t1 = tcg_temp_new_i64 (); + + tcg_gen_andi_i64(t1, arg2, mask); + tcg_gen_shli_i64(t1, t1, ofs); + tcg_gen_andi_i64(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i64(ret, ret, t1); + + tcg_temp_free_i64(t1); +#endif +} + /***************************************/ /* QEMU specific operations. Their type depend on the QEMU CPU type. */ @@ -1981,9 +2234,9 @@ static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index) static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index) { #if TARGET_LONG_BITS == 32 - tcg_gen_op3i_i32(INDEX_op_qemu_ld32u, ret, addr, mem_index); + tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index); #else - tcg_gen_op4i_i32(INDEX_op_qemu_ld32u, TCGV_LOW(ret), TCGV_LOW(addr), + tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr), TCGV_HIGH(addr), mem_index); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); #endif @@ -1992,9 +2245,9 @@ static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index) static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index) { #if TARGET_LONG_BITS == 32 - tcg_gen_op3i_i32(INDEX_op_qemu_ld32u, ret, addr, mem_index); + tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index); #else - tcg_gen_op4i_i32(INDEX_op_qemu_ld32u, TCGV_LOW(ret), TCGV_LOW(addr), + tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr), TCGV_HIGH(addr), mem_index); tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); #endif @@ -2078,12 +2331,20 @@ static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index) static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index) { +#if TARGET_LONG_BITS == 32 + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index); +#else tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32u, ret, addr, mem_index); +#endif } static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index) { +#if TARGET_LONG_BITS == 32 + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index); +#else tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32s, ret, addr, mem_index); +#endif } static inline void tcg_gen_qemu_ld64(TCGv_i64 ret, TCGv addr, int mem_index) @@ -2117,7 +2378,6 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #endif /* TCG_TARGET_REG_BITS != 32 */ #if TARGET_LONG_BITS == 64 -#define TCG_TYPE_TL TCG_TYPE_I64 #define tcg_gen_movi_tl tcg_gen_movi_i64 #define tcg_gen_mov_tl tcg_gen_mov_i64 #define tcg_gen_ld8u_tl tcg_gen_ld8u_i64 @@ -2186,10 +2446,10 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_rotli_tl tcg_gen_rotli_i64 #define tcg_gen_rotr_tl tcg_gen_rotr_i64 #define tcg_gen_rotri_tl tcg_gen_rotri_i64 +#define tcg_gen_deposit_tl tcg_gen_deposit_i64 #define tcg_const_tl tcg_const_i64 #define tcg_const_local_tl tcg_const_local_i64 #else -#define TCG_TYPE_TL TCG_TYPE_I32 #define tcg_gen_movi_tl tcg_gen_movi_i32 #define tcg_gen_mov_tl tcg_gen_mov_i32 #define tcg_gen_ld8u_tl tcg_gen_ld8u_i32 @@ -2257,6 +2517,7 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_rotli_tl tcg_gen_rotli_i32 #define tcg_gen_rotr_tl tcg_gen_rotr_i32 #define tcg_gen_rotri_tl tcg_gen_rotri_i32 +#define tcg_gen_deposit_tl tcg_gen_deposit_i32 #define tcg_const_tl tcg_const_i32 #define tcg_const_local_tl tcg_const_local_i32 #endif diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h index 34cdba5..1b8a6e4 100644 --- a/tcg/tcg-opc.h +++ b/tcg/tcg-opc.h @@ -21,267 +21,295 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef DEF2 -#define DEF2(name, oargs, iargs, cargs, flags) DEF(name, oargs + iargs + cargs, 0) -#endif + +/* + * DEF(name, oargs, iargs, cargs, flags) + */ /* predefined ops */ -DEF2(end, 0, 0, 0, 0) /* must be kept first */ -DEF2(nop, 0, 0, 0, 0) -DEF2(nop1, 0, 0, 1, 0) -DEF2(nop2, 0, 0, 2, 0) -DEF2(nop3, 0, 0, 3, 0) -DEF2(nopn, 0, 0, 1, 0) /* variable number of parameters */ +DEF(end, 0, 0, 0, 0) /* must be kept first */ +DEF(nop, 0, 0, 0, 0) +DEF(nop1, 0, 0, 1, 0) +DEF(nop2, 0, 0, 2, 0) +DEF(nop3, 0, 0, 3, 0) +DEF(nopn, 0, 0, 1, 0) /* variable number of parameters */ -DEF2(discard, 1, 0, 0, 0) +DEF(discard, 1, 0, 0, 0) -DEF2(set_label, 0, 0, 1, 0) -DEF2(call, 0, 1, 2, TCG_OPF_SIDE_EFFECTS) /* variable number of parameters */ -DEF2(jmp, 0, 1, 0, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) -DEF2(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(set_label, 0, 0, 1, 0) +DEF(call, 0, 1, 2, TCG_OPF_SIDE_EFFECTS) /* variable number of parameters */ +DEF(jmp, 0, 1, 0, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) -DEF2(mov_i32, 1, 1, 0, 0) -DEF2(movi_i32, 1, 0, 1, 0) -DEF2(setcond_i32, 1, 2, 1, 0) +DEF(mov_i32, 1, 1, 0, 0) +DEF(movi_i32, 1, 0, 1, 0) +DEF(setcond_i32, 1, 2, 1, 0) /* load/store */ -DEF2(ld8u_i32, 1, 1, 1, 0) -DEF2(ld8s_i32, 1, 1, 1, 0) -DEF2(ld16u_i32, 1, 1, 1, 0) -DEF2(ld16s_i32, 1, 1, 1, 0) -DEF2(ld_i32, 1, 1, 1, 0) -DEF2(st8_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st16_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(ld8u_i32, 1, 1, 1, 0) +DEF(ld8s_i32, 1, 1, 1, 0) +DEF(ld16u_i32, 1, 1, 1, 0) +DEF(ld16s_i32, 1, 1, 1, 0) +DEF(ld_i32, 1, 1, 1, 0) +DEF(st8_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st16_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) /* arith */ -DEF2(add_i32, 1, 2, 0, 0) -DEF2(sub_i32, 1, 2, 0, 0) -DEF2(mul_i32, 1, 2, 0, 0) +DEF(add_i32, 1, 2, 0, 0) +DEF(sub_i32, 1, 2, 0, 0) +DEF(mul_i32, 1, 2, 0, 0) #ifdef TCG_TARGET_HAS_div_i32 -DEF2(div_i32, 1, 2, 0, 0) -DEF2(divu_i32, 1, 2, 0, 0) -DEF2(rem_i32, 1, 2, 0, 0) -DEF2(remu_i32, 1, 2, 0, 0) -#else -DEF2(div2_i32, 2, 3, 0, 0) -DEF2(divu2_i32, 2, 3, 0, 0) -#endif -DEF2(and_i32, 1, 2, 0, 0) -DEF2(or_i32, 1, 2, 0, 0) -DEF2(xor_i32, 1, 2, 0, 0) +DEF(div_i32, 1, 2, 0, 0) +DEF(divu_i32, 1, 2, 0, 0) +DEF(rem_i32, 1, 2, 0, 0) +DEF(remu_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_div2_i32 +DEF(div2_i32, 2, 3, 0, 0) +DEF(divu2_i32, 2, 3, 0, 0) +#endif +DEF(and_i32, 1, 2, 0, 0) +DEF(or_i32, 1, 2, 0, 0) +DEF(xor_i32, 1, 2, 0, 0) /* shifts/rotates */ -DEF2(shl_i32, 1, 2, 0, 0) -DEF2(shr_i32, 1, 2, 0, 0) -DEF2(sar_i32, 1, 2, 0, 0) +DEF(shl_i32, 1, 2, 0, 0) +DEF(shr_i32, 1, 2, 0, 0) +DEF(sar_i32, 1, 2, 0, 0) #ifdef TCG_TARGET_HAS_rot_i32 -DEF2(rotl_i32, 1, 2, 0, 0) -DEF2(rotr_i32, 1, 2, 0, 0) +DEF(rotl_i32, 1, 2, 0, 0) +DEF(rotr_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_deposit_i32 +DEF(deposit_i32, 1, 2, 2, 0) #endif -DEF2(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) #if TCG_TARGET_REG_BITS == 32 -DEF2(add2_i32, 2, 4, 0, 0) -DEF2(sub2_i32, 2, 4, 0, 0) -DEF2(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) -DEF2(mulu2_i32, 2, 2, 0, 0) -DEF2(setcond2_i32, 1, 4, 1, 0) +DEF(add2_i32, 2, 4, 0, 0) +DEF(sub2_i32, 2, 4, 0, 0) +DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(mulu2_i32, 2, 2, 0, 0) +DEF(setcond2_i32, 1, 4, 1, 0) #endif #ifdef TCG_TARGET_HAS_ext8s_i32 -DEF2(ext8s_i32, 1, 1, 0, 0) +DEF(ext8s_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext16s_i32 -DEF2(ext16s_i32, 1, 1, 0, 0) +DEF(ext16s_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext8u_i32 -DEF2(ext8u_i32, 1, 1, 0, 0) +DEF(ext8u_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext16u_i32 -DEF2(ext16u_i32, 1, 1, 0, 0) +DEF(ext16u_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap16_i32 -DEF2(bswap16_i32, 1, 1, 0, 0) +DEF(bswap16_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap32_i32 -DEF2(bswap32_i32, 1, 1, 0, 0) +DEF(bswap32_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_not_i32 -DEF2(not_i32, 1, 1, 0, 0) +DEF(not_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_neg_i32 -DEF2(neg_i32, 1, 1, 0, 0) +DEF(neg_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_andc_i32 -DEF2(andc_i32, 1, 2, 0, 0) +DEF(andc_i32, 1, 2, 0, 0) #endif #ifdef TCG_TARGET_HAS_orc_i32 -DEF2(orc_i32, 1, 2, 0, 0) +DEF(orc_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_eqv_i32 +DEF(eqv_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nand_i32 +DEF(nand_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nor_i32 +DEF(nor_i32, 1, 2, 0, 0) #endif #if TCG_TARGET_REG_BITS == 64 -DEF2(mov_i64, 1, 1, 0, 0) -DEF2(movi_i64, 1, 0, 1, 0) -DEF2(setcond_i64, 1, 2, 1, 0) +DEF(mov_i64, 1, 1, 0, 0) +DEF(movi_i64, 1, 0, 1, 0) +DEF(setcond_i64, 1, 2, 1, 0) /* load/store */ -DEF2(ld8u_i64, 1, 1, 1, 0) -DEF2(ld8s_i64, 1, 1, 1, 0) -DEF2(ld16u_i64, 1, 1, 1, 0) -DEF2(ld16s_i64, 1, 1, 1, 0) -DEF2(ld32u_i64, 1, 1, 1, 0) -DEF2(ld32s_i64, 1, 1, 1, 0) -DEF2(ld_i64, 1, 1, 1, 0) -DEF2(st8_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st16_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st32_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(ld8u_i64, 1, 1, 1, 0) +DEF(ld8s_i64, 1, 1, 1, 0) +DEF(ld16u_i64, 1, 1, 1, 0) +DEF(ld16s_i64, 1, 1, 1, 0) +DEF(ld32u_i64, 1, 1, 1, 0) +DEF(ld32s_i64, 1, 1, 1, 0) +DEF(ld_i64, 1, 1, 1, 0) +DEF(st8_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st16_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st32_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) /* arith */ -DEF2(add_i64, 1, 2, 0, 0) -DEF2(sub_i64, 1, 2, 0, 0) -DEF2(mul_i64, 1, 2, 0, 0) +DEF(add_i64, 1, 2, 0, 0) +DEF(sub_i64, 1, 2, 0, 0) +DEF(mul_i64, 1, 2, 0, 0) #ifdef TCG_TARGET_HAS_div_i64 -DEF2(div_i64, 1, 2, 0, 0) -DEF2(divu_i64, 1, 2, 0, 0) -DEF2(rem_i64, 1, 2, 0, 0) -DEF2(remu_i64, 1, 2, 0, 0) -#else -DEF2(div2_i64, 2, 3, 0, 0) -DEF2(divu2_i64, 2, 3, 0, 0) -#endif -DEF2(and_i64, 1, 2, 0, 0) -DEF2(or_i64, 1, 2, 0, 0) -DEF2(xor_i64, 1, 2, 0, 0) +DEF(div_i64, 1, 2, 0, 0) +DEF(divu_i64, 1, 2, 0, 0) +DEF(rem_i64, 1, 2, 0, 0) +DEF(remu_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_div2_i64 +DEF(div2_i64, 2, 3, 0, 0) +DEF(divu2_i64, 2, 3, 0, 0) +#endif +DEF(and_i64, 1, 2, 0, 0) +DEF(or_i64, 1, 2, 0, 0) +DEF(xor_i64, 1, 2, 0, 0) /* shifts/rotates */ -DEF2(shl_i64, 1, 2, 0, 0) -DEF2(shr_i64, 1, 2, 0, 0) -DEF2(sar_i64, 1, 2, 0, 0) +DEF(shl_i64, 1, 2, 0, 0) +DEF(shr_i64, 1, 2, 0, 0) +DEF(sar_i64, 1, 2, 0, 0) #ifdef TCG_TARGET_HAS_rot_i64 -DEF2(rotl_i64, 1, 2, 0, 0) -DEF2(rotr_i64, 1, 2, 0, 0) +DEF(rotl_i64, 1, 2, 0, 0) +DEF(rotr_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_deposit_i64 +DEF(deposit_i64, 1, 2, 2, 0) #endif -DEF2(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) #ifdef TCG_TARGET_HAS_ext8s_i64 -DEF2(ext8s_i64, 1, 1, 0, 0) +DEF(ext8s_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext16s_i64 -DEF2(ext16s_i64, 1, 1, 0, 0) +DEF(ext16s_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext32s_i64 -DEF2(ext32s_i64, 1, 1, 0, 0) +DEF(ext32s_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext8u_i64 -DEF2(ext8u_i64, 1, 1, 0, 0) +DEF(ext8u_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext16u_i64 -DEF2(ext16u_i64, 1, 1, 0, 0) +DEF(ext16u_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext32u_i64 -DEF2(ext32u_i64, 1, 1, 0, 0) +DEF(ext32u_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap16_i64 -DEF2(bswap16_i64, 1, 1, 0, 0) +DEF(bswap16_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap32_i64 -DEF2(bswap32_i64, 1, 1, 0, 0) +DEF(bswap32_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap64_i64 -DEF2(bswap64_i64, 1, 1, 0, 0) +DEF(bswap64_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_not_i64 -DEF2(not_i64, 1, 1, 0, 0) +DEF(not_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_neg_i64 -DEF2(neg_i64, 1, 1, 0, 0) +DEF(neg_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_andc_i64 -DEF2(andc_i64, 1, 2, 0, 0) +DEF(andc_i64, 1, 2, 0, 0) #endif #ifdef TCG_TARGET_HAS_orc_i64 -DEF2(orc_i64, 1, 2, 0, 0) +DEF(orc_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_eqv_i64 +DEF(eqv_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nand_i64 +DEF(nand_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nor_i64 +DEF(nor_i64, 1, 2, 0, 0) #endif #endif /* QEMU specific */ #if TARGET_LONG_BITS > TCG_TARGET_REG_BITS -DEF2(debug_insn_start, 0, 0, 2, 0) +DEF(debug_insn_start, 0, 0, 2, 0) #else -DEF2(debug_insn_start, 0, 0, 1, 0) +DEF(debug_insn_start, 0, 0, 1, 0) #endif -DEF2(exit_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) -DEF2(goto_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) /* Note: even if TARGET_LONG_BITS is not defined, the INDEX_op constants must be defined */ #if TCG_TARGET_REG_BITS == 32 #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld8u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld8s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld16u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld16s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld32u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld32s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld64, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld64, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld64, 2, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld64, 2, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_st8, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st8, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_st16, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st16, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_st32, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st32, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_st64, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st64, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_st64, 0, 4, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st64, 0, 4, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #else /* TCG_TARGET_REG_BITS == 32 */ -DEF2(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld64, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld64, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_st64, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st64, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif /* TCG_TARGET_REG_BITS != 32 */ -#undef DEF2 +#undef DEF diff --git a/tcg/tcg-runtime.h b/tcg/tcg-runtime.h index e750cc1..5615b13 100644 --- a/tcg/tcg-runtime.h +++ b/tcg/tcg-runtime.h @@ -2,6 +2,11 @@ #define TCG_RUNTIME_H /* tcg-runtime.c */ +int32_t tcg_helper_div_i32(int32_t arg1, int32_t arg2); +int32_t tcg_helper_rem_i32(int32_t arg1, int32_t arg2); +uint32_t tcg_helper_divu_i32(uint32_t arg1, uint32_t arg2); +uint32_t tcg_helper_remu_i32(uint32_t arg1, uint32_t arg2); + int64_t tcg_helper_shl_i64(int64_t arg1, int64_t arg2); int64_t tcg_helper_shr_i64(int64_t arg1, int64_t arg2); int64_t tcg_helper_sar_i64(int64_t arg1, int64_t arg2); @@ -63,15 +63,15 @@ #error GUEST_BASE not supported on this host. #endif +static void tcg_target_init(TCGContext *s); +static void tcg_target_qemu_prologue(TCGContext *s); static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend); static TCGOpDef tcg_op_defs[] = { -#define DEF(s, n, copy_size) { #s, 0, 0, n, n, 0, copy_size }, -#define DEF2(s, oargs, iargs, cargs, flags) { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags, 0 }, +#define DEF(s, oargs, iargs, cargs, flags) { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, #include "tcg-opc.h" #undef DEF -#undef DEF2 }; static TCGRegSet tcg_target_available_regs[2]; @@ -110,7 +110,7 @@ static inline void tcg_out32(TCGContext *s, uint32_t v) /* label relocation processing */ -void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, +static void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, int label_index, long addend) { TCGLabel *l; @@ -250,7 +250,10 @@ void tcg_context_init(TCGContext *s) } tcg_target_init(s); +} +void tcg_prologue_init(TCGContext *s) +{ /* init global prologue and epilogue */ s->code_buf = code_gen_prologue; s->code_ptr = s->code_buf; @@ -457,6 +460,10 @@ static inline int tcg_temp_new_internal(TCGType type, int temp_local) s->nb_temps++; } } + +#if defined(CONFIG_DEBUG_TCG) + s->temps_in_use++; +#endif return idx; } @@ -482,6 +489,13 @@ static inline void tcg_temp_free_internal(int idx) TCGTemp *ts; int k; +#if defined(CONFIG_DEBUG_TCG) + s->temps_in_use--; + if (s->temps_in_use < 0) { + fprintf(stderr, "More temporaries freed than allocated!\n"); + } +#endif + assert(idx >= s->nb_globals && idx < s->nb_temps); ts = &s->temps[idx]; assert(ts->temp_allocated != 0); @@ -535,6 +549,27 @@ TCGv_i64 tcg_const_local_i64(int64_t val) return t0; } +#if defined(CONFIG_DEBUG_TCG) +void tcg_clear_temp_count(void) +{ + TCGContext *s = &tcg_ctx; + s->temps_in_use = 0; +} + +int tcg_check_temp_count(void) +{ + TCGContext *s = &tcg_ctx; + if (s->temps_in_use) { + /* Clear the count so that we don't give another + * warning immediately next time around. + */ + s->temps_in_use = 0; + return 1; + } + return 0; +} +#endif + void tcg_register_helper(void *func, const char *name) { TCGContext *s = &tcg_ctx; @@ -560,14 +595,36 @@ void tcg_register_helper(void *func, const char *name) void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, int sizemask, TCGArg ret, int nargs, TCGArg *args) { +#ifdef TCG_TARGET_I386 int call_type; +#endif int i; int real_args; int nb_rets; TCGArg *nparam; + +#if defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64 + for (i = 0; i < nargs; ++i) { + int is_64bit = sizemask & (1 << (i+1)*2); + int is_signed = sizemask & (2 << (i+1)*2); + if (!is_64bit) { + TCGv_i64 temp = tcg_temp_new_i64(); + TCGv_i64 orig = MAKE_TCGV_I64(args[i]); + if (is_signed) { + tcg_gen_ext32s_i64(temp, orig); + } else { + tcg_gen_ext32u_i64(temp, orig); + } + args[i] = GET_TCGV_I64(temp); + } + } +#endif /* TCG_TARGET_EXTEND_ARGS */ + *gen_opc_ptr++ = INDEX_op_call; nparam = gen_opparam_ptr++; +#ifdef TCG_TARGET_I386 call_type = (flags & TCG_CALL_TYPE_MASK); +#endif if (ret != TCG_CALL_DUMMY_ARG) { #if TCG_TARGET_REG_BITS < 64 if (sizemask & 1) { @@ -591,7 +648,8 @@ void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, real_args = 0; for (i = 0; i < nargs; i++) { #if TCG_TARGET_REG_BITS < 64 - if (sizemask & (2 << i)) { + int is_64bit = sizemask & (1 << (i+1)*2); + if (is_64bit) { #ifdef TCG_TARGET_I386 /* REGPARM case: if the third parameter is 64 bit, it is allocated on the stack */ @@ -607,7 +665,17 @@ void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, real_args++; } #endif -#ifdef TCG_TARGET_WORDS_BIGENDIAN + /* If stack grows up, then we will be placing successive + arguments at lower addresses, which means we need to + reverse the order compared to how we would normally + treat either big or little-endian. For those arguments + that will wind up in registers, this still works for + HPPA (the only current STACK_GROWSUP target) since the + argument registers are *also* allocated in decreasing + order. If another such target is added, this logic may + have to get more complicated to differentiate between + stack arguments and register arguments. */ +#if defined(TCG_TARGET_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP) *gen_opparam_ptr++ = args[i] + 1; *gen_opparam_ptr++ = args[i]; #else @@ -615,13 +683,13 @@ void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, *gen_opparam_ptr++ = args[i] + 1; #endif real_args += 2; - } else -#endif - { + continue; + } +#endif /* TCG_TARGET_REG_BITS < 64 */ + *gen_opparam_ptr++ = args[i]; real_args++; } - } *gen_opparam_ptr++ = GET_TCGV_PTR(func); *gen_opparam_ptr++ = flags; @@ -630,6 +698,16 @@ void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, /* total parameters, needed to go backward in the instruction stream */ *gen_opparam_ptr++ = 1 + nb_rets + real_args + 3; + +#if defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64 + for (i = 0; i < nargs; ++i) { + int is_64bit = sizemask & (1 << (i+1)*2); + if (!is_64bit) { + TCGv_i64 temp = MAKE_TCGV_I64(args[i]); + tcg_temp_free_i64(temp); + } + } +#endif /* TCG_TARGET_EXTEND_ARGS */ } #if TCG_TARGET_REG_BITS == 32 @@ -681,6 +759,7 @@ void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, } #endif + static void tcg_reg_alloc_start(TCGContext *s) { int i; @@ -793,7 +872,8 @@ void tcg_dump_ops(TCGContext *s, FILE *outfile) const uint16_t *opc_ptr; const TCGArg *args; TCGArg arg; - int c, i, k, nb_oargs, nb_iargs, nb_cargs, first_insn; + TCGOpcode c; + int i, k, nb_oargs, nb_iargs, nb_cargs, first_insn; const TCGOpDef *def; char buf[128]; @@ -980,16 +1060,16 @@ static void sort_constraints(TCGOpDef *def, int start, int n) void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) { - int op; + TCGOpcode op; TCGOpDef *def; const char *ct_str; int i, nb_args; for(;;) { - if (tdefs->op < 0) + if (tdefs->op == (TCGOpcode)-1) break; op = tdefs->op; - assert(op >= 0 && op < NB_OPS); + assert((unsigned)op < NB_OPS); def = &tcg_op_defs[op]; #if defined(CONFIG_DEBUG_TCG) /* Duplicate entry in op definitions? */ @@ -1056,15 +1136,27 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) } #if defined(CONFIG_DEBUG_TCG) + i = 0; for (op = 0; op < ARRAY_SIZE(tcg_op_defs); op++) { if (op < INDEX_op_call || op == INDEX_op_debug_insn_start) { /* Wrong entry in op definitions? */ - assert(!tcg_op_defs[op].used); + if (tcg_op_defs[op].used) { + fprintf(stderr, "Invalid op definition for %s\n", + tcg_op_defs[op].name); + i = 1; + } } else { /* Missing entry in op definitions? */ - assert(tcg_op_defs[op].used); + if (!tcg_op_defs[op].used) { + fprintf(stderr, "Missing op definition for %s\n", + tcg_op_defs[op].name); + i = 1; + } } } + if (i == 1) { + tcg_abort(); + } #endif } @@ -1116,7 +1208,8 @@ static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps) temporaries are removed. */ static void tcg_liveness_analysis(TCGContext *s) { - int i, op_index, op, nb_args, nb_iargs, nb_oargs, arg, nb_ops; + int i, op_index, nb_args, nb_iargs, nb_oargs, arg, nb_ops; + TCGOpcode op; TCGArg *args; const TCGOpDef *def; uint8_t *dead_temps; @@ -1271,7 +1364,7 @@ static void tcg_liveness_analysis(TCGContext *s) } #else /* dummy liveness analysis */ -void tcg_liveness_analysis(TCGContext *s) +static void tcg_liveness_analysis(TCGContext *s) { int nb_ops; nb_ops = gen_opc_ptr - gen_opc_buf; @@ -1532,7 +1625,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs); } if (ts->reg != reg) { - tcg_out_mov(s, reg, ts->reg); + tcg_out_mov(s, ots->type, reg, ts->reg); } } } else if (ts->val_type == TEMP_VAL_MEM) { @@ -1564,7 +1657,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, } static void tcg_reg_alloc_op(TCGContext *s, - const TCGOpDef *def, int opc, + const TCGOpDef *def, TCGOpcode opc, const TCGArg *args, unsigned int dead_iargs) { @@ -1637,7 +1730,7 @@ static void tcg_reg_alloc_op(TCGContext *s, /* allocate a new register matching the constraint and move the temporary register into it */ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); - tcg_out_mov(s, reg, ts->reg); + tcg_out_mov(s, ts->type, reg, ts->reg); } new_args[i] = reg; const_args[i] = 0; @@ -1719,7 +1812,7 @@ static void tcg_reg_alloc_op(TCGContext *s, ts = &s->temps[args[i]]; reg = new_args[i]; if (ts->fixed_reg && ts->reg != reg) { - tcg_out_mov(s, ts->reg, reg); + tcg_out_mov(s, ts->type, ts->reg, reg); } } } @@ -1731,7 +1824,7 @@ static void tcg_reg_alloc_op(TCGContext *s, #endif static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, - int opc, const TCGArg *args, + TCGOpcode opc, const TCGArg *args, unsigned int dead_iargs) { int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params; @@ -1805,7 +1898,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, tcg_reg_free(s, reg); if (ts->val_type == TEMP_VAL_REG) { if (ts->reg != reg) { - tcg_out_mov(s, reg, ts->reg); + tcg_out_mov(s, ts->type, reg, ts->reg); } } else if (ts->val_type == TEMP_VAL_MEM) { tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); @@ -1834,7 +1927,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, reg = ts->reg; if (!tcg_regset_test_reg(arg_ct->u.regs, reg)) { reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); - tcg_out_mov(s, reg, ts->reg); + tcg_out_mov(s, ts->type, reg, ts->reg); } func_arg = reg; tcg_regset_set_reg(allocated_regs, reg); @@ -1893,7 +1986,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, assert(s->reg_to_temp[reg] == -1); if (ts->fixed_reg) { if (ts->reg != reg) { - tcg_out_mov(s, ts->reg, reg); + tcg_out_mov(s, ts->type, ts->reg, reg); } } else { if (ts->val_type == TEMP_VAL_REG) @@ -1912,7 +2005,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, static int64_t tcg_table_op_count[NB_OPS]; -void dump_op_count(void) +static void dump_op_count(void) { int i; FILE *f; @@ -1928,7 +2021,8 @@ void dump_op_count(void) static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf, long search_pc) { - int opc, op_index; + TCGOpcode opc; + int op_index; const TCGOpDef *def; unsigned int dead_iargs; const TCGArg *args; @@ -2109,8 +2203,7 @@ int tcg_gen_code_search_pc(TCGContext *s, uint8_t *gen_code_buf, long offset) } #ifdef CONFIG_PROFILER -void tcg_dump_info(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) { TCGContext *s = &tcg_ctx; int64_t tot; @@ -2154,8 +2247,7 @@ void tcg_dump_info(FILE *f, dump_op_count(); } #else -void tcg_dump_info(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) { cpu_fprintf(f, "[TCG profiler not compiled]\n"); } @@ -47,12 +47,12 @@ typedef uint64_t TCGRegSet; #error unsupported #endif -enum { -#define DEF(s, n, copy_size) INDEX_op_ ## s, +typedef enum TCGOpcode { +#define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, #include "tcg-opc.h" #undef DEF NB_OPS, -}; +} TCGOpcode; #define tcg_regset_clear(d) (d) = 0 #define tcg_regset_set(d, s) (d) = (s) @@ -96,17 +96,29 @@ typedef struct TCGPool { this value, they are statically allocated in the TB stack frame */ #define TCG_STATIC_CALL_ARGS_SIZE 128 -typedef int TCGType; - -#define TCG_TYPE_I32 0 -#define TCG_TYPE_I64 1 -#define TCG_TYPE_COUNT 2 /* number of different types */ +typedef enum TCGType { + TCG_TYPE_I32, + TCG_TYPE_I64, + TCG_TYPE_COUNT, /* number of different types */ + /* An alias for the size of the host register. */ #if TCG_TARGET_REG_BITS == 32 -#define TCG_TYPE_PTR TCG_TYPE_I32 + TCG_TYPE_REG = TCG_TYPE_I32, +#else + TCG_TYPE_REG = TCG_TYPE_I64, +#endif + + /* An alias for the size of the native pointer. We don't currently + support any hosts with 64-bit registers and 32-bit pointers. */ + TCG_TYPE_PTR = TCG_TYPE_REG, + + /* An alias for the size of the target "long", aka register. */ +#if TARGET_LONG_BITS == 64 + TCG_TYPE_TL = TCG_TYPE_I64, #else -#define TCG_TYPE_PTR TCG_TYPE_I64 + TCG_TYPE_TL = TCG_TYPE_I32, #endif +} TCGType; typedef tcg_target_ulong TCGArg; @@ -311,6 +323,10 @@ struct TCGContext { int64_t restore_count; int64_t restore_time; #endif + +#ifdef CONFIG_DEBUG_TCG + int temps_in_use; +#endif }; extern TCGContext tcg_ctx; @@ -341,6 +357,7 @@ static inline void *tcg_malloc(int size) } void tcg_context_init(TCGContext *s); +void tcg_prologue_init(TCGContext *s); void tcg_func_start(TCGContext *s); int tcg_gen_code(TCGContext *s, uint8_t *gen_code_buf); @@ -379,8 +396,20 @@ static inline TCGv_i64 tcg_temp_local_new_i64(void) void tcg_temp_free_i64(TCGv_i64 arg); char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg); -void tcg_dump_info(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +#if defined(CONFIG_DEBUG_TCG) +/* If you call tcg_clear_temp_count() at the start of a section of + * code which is not supposed to leak any TCG temporaries, then + * calling tcg_check_temp_count() at the end of the section will + * return 1 if the section did in fact leak a temporary. + */ +void tcg_clear_temp_count(void); +int tcg_check_temp_count(void); +#else +#define tcg_clear_temp_count() do { } while (0) +#define tcg_check_temp_count() 0 +#endif + +void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf); #define TCG_CT_ALIAS 0x80 #define TCG_CT_IALIAS 0x40 @@ -409,7 +438,6 @@ typedef struct TCGOpDef { const char *name; uint8_t nb_oargs, nb_iargs, nb_cargs, nb_args; uint8_t flags; - uint16_t copy_size; TCGArgConstraint *args_ct; int *sorted_args; #if defined(CONFIG_DEBUG_TCG) @@ -418,13 +446,10 @@ typedef struct TCGOpDef { } TCGOpDef; typedef struct TCGTargetOpDef { - int op; + TCGOpcode op; const char *args_ct_str[TCG_MAX_OP_ARGS]; } TCGTargetOpDef; -void tcg_target_init(TCGContext *s); -void tcg_target_qemu_prologue(TCGContext *s); - #define tcg_abort() \ do {\ fprintf(stderr, "%s:%d: tcg fatal error\n", __FILE__, __LINE__);\ @@ -472,9 +497,6 @@ TCGv_i64 tcg_const_i64(int64_t val); TCGv_i32 tcg_const_local_i32(int32_t val); TCGv_i64 tcg_const_local_i64(int64_t val); -void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, - int label_index, long addend); - extern uint8_t code_gen_prologue[]; #if defined(_ARCH_PPC) && !defined(_ARCH_PPC64) #define tcg_qemu_tb_exec(tb_ptr) \ diff --git a/telephony/sysdeps_qemu.c b/telephony/sysdeps_qemu.c index e464597..6aa5038 100644 --- a/telephony/sysdeps_qemu.c +++ b/telephony/sysdeps_qemu.c @@ -41,7 +41,7 @@ SysTime sys_time_ms( void ) { - return qemu_get_clock( rt_clock ); + return qemu_get_clock_ms(rt_clock); } /** TIMERS @@ -126,7 +126,7 @@ sys_timer_set( SysTimer timer, SysTime when, SysCallback _callback, void* o qemu_free_timer( timer->timer ); } - timer->timer = qemu_new_timer( rt_clock, callback, opaque ); + timer->timer = qemu_new_timer_ms( rt_clock, callback, opaque ); timer->callback = callback; timer->opaque = opaque; @@ -320,7 +320,7 @@ sys_channel_create_tcp_server( int port ) channel->fd = socket_anyaddr_server( port, SOCKET_STREAM ); if (channel->fd < 0) { - D( "%s: failed to created network socket on TCP:%d\n", + D( "%s: failed to created network socket on TCP:%d\n", __FUNCTION__, port ); sys_channel_free( channel ); return NULL; @@ -22,10 +22,106 @@ #include <time.h> #include "cpu.h" #include "exec-all.h" -#include "trace.h" +#include "android-trace.h" #include "varint.h" #include "android/utils/path.h" +// For tracing dynamic execution of basic blocks +typedef struct TraceBB { + char *filename; + FILE *fstream; + BBRec buffer[kMaxNumBasicBlocks]; + BBRec *next; // points to next record in buffer + uint64_t flush_time; // time of last buffer flush + char compressed[kCompressedSize]; + char *compressed_ptr; + char *high_water_ptr; + int64_t prev_bb_num; + uint64_t prev_bb_time; + uint64_t current_bb_num; + uint64_t current_bb_start_time; + uint64_t recnum; // counts number of trace records + uint32_t current_bb_addr; + int num_insns; +} TraceBB; + +// For tracing simuation start times of instructions +typedef struct TraceInsn { + char *filename; + FILE *fstream; + InsnRec dummy; // this is here so we can use buffer[-1] + InsnRec buffer[kInsnBufferSize]; + InsnRec *current; + uint64_t prev_time; // time of last instruction start + char compressed[kCompressedSize]; + char *compressed_ptr; + char *high_water_ptr; +} TraceInsn; + +// For tracing the static information about a basic block +typedef struct TraceStatic { + char *filename; + FILE *fstream; + uint32_t insns[kMaxInsnPerBB]; + int next_insn; + uint64_t bb_num; + uint32_t bb_addr; + int is_thumb; +} TraceStatic; + +// For tracing load and store addresses +typedef struct TraceAddr { + char *filename; + FILE *fstream; + AddrRec buffer[kMaxNumAddrs]; + AddrRec *next; + char compressed[kCompressedSize]; + char *compressed_ptr; + char *high_water_ptr; + uint32_t prev_addr; + uint64_t prev_time; +} TraceAddr; + +// For tracing exceptions +typedef struct TraceExc { + char *filename; + FILE *fstream; + char compressed[kCompressedSize]; + char *compressed_ptr; + char *high_water_ptr; + uint64_t prev_time; + uint64_t prev_bb_recnum; +} TraceExc; + +// For tracing process id changes +typedef struct TracePid { + char *filename; + FILE *fstream; + char compressed[kCompressedSize]; + char *compressed_ptr; + uint64_t prev_time; +} TracePid; + +// For tracing Dalvik VM method enter and exit +typedef struct TraceMethod { + char *filename; + FILE *fstream; + char compressed[kCompressedSize]; + char *compressed_ptr; + uint64_t prev_time; + uint32_t prev_addr; + int32_t prev_pid; +} TraceMethod; + +extern TraceBB trace_bb; +extern TraceInsn trace_insn; +extern TraceStatic trace_static; +extern TraceAddr trace_load; +extern TraceAddr trace_store; +extern TraceExc trace_exc; +extern TracePid trace_pid; +extern TraceMethod trace_method; + TraceBB trace_bb; TraceInsn trace_insn; TraceStatic trace_static; @@ -1878,3 +1974,8 @@ void trace_interpreted_method(uint32_t addr, int call_type) comp_ptr = varint_encode(call_type, comp_ptr); trace_method.compressed_ptr = comp_ptr; } + +uint64_t trace_static_bb_num(void) +{ + return trace_static.bb_num; +} @@ -1,159 +0,0 @@ -/* Copyright (C) 2006-2007 The Android Open Source Project -** -** This software is licensed under the terms of the GNU General Public -** License version 2, as published by the Free Software Foundation, and -** may be copied, distributed, and modified under those terms. -** -** 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. -*/ - -#ifndef TRACE_H -#define TRACE_H - -#include <inttypes.h> -#include "trace_common.h" - -extern uint64_t start_time, end_time; -extern uint64_t elapsed_usecs; -extern uint64 Now(); - -struct TranslationBlock; - -// For tracing dynamic execution of basic blocks -typedef struct TraceBB { - char *filename; - FILE *fstream; - BBRec buffer[kMaxNumBasicBlocks]; - BBRec *next; // points to next record in buffer - uint64_t flush_time; // time of last buffer flush - char compressed[kCompressedSize]; - char *compressed_ptr; - char *high_water_ptr; - int64_t prev_bb_num; - uint64_t prev_bb_time; - uint64_t current_bb_num; - uint64_t current_bb_start_time; - uint64_t recnum; // counts number of trace records - uint32_t current_bb_addr; - int num_insns; -} TraceBB; - -// For tracing simuation start times of instructions -typedef struct TraceInsn { - char *filename; - FILE *fstream; - InsnRec dummy; // this is here so we can use buffer[-1] - InsnRec buffer[kInsnBufferSize]; - InsnRec *current; - uint64_t prev_time; // time of last instruction start - char compressed[kCompressedSize]; - char *compressed_ptr; - char *high_water_ptr; -} TraceInsn; - -// For tracing the static information about a basic block -typedef struct TraceStatic { - char *filename; - FILE *fstream; - uint32_t insns[kMaxInsnPerBB]; - int next_insn; - uint64_t bb_num; - uint32_t bb_addr; - int is_thumb; -} TraceStatic; - -// For tracing load and store addresses -typedef struct TraceAddr { - char *filename; - FILE *fstream; - AddrRec buffer[kMaxNumAddrs]; - AddrRec *next; - char compressed[kCompressedSize]; - char *compressed_ptr; - char *high_water_ptr; - uint32_t prev_addr; - uint64_t prev_time; -} TraceAddr; - -// For tracing exceptions -typedef struct TraceExc { - char *filename; - FILE *fstream; - char compressed[kCompressedSize]; - char *compressed_ptr; - char *high_water_ptr; - uint64_t prev_time; - uint64_t prev_bb_recnum; -} TraceExc; - -// For tracing process id changes -typedef struct TracePid { - char *filename; - FILE *fstream; - char compressed[kCompressedSize]; - char *compressed_ptr; - uint64_t prev_time; -} TracePid; - -// For tracing Dalvik VM method enter and exit -typedef struct TraceMethod { - char *filename; - FILE *fstream; - char compressed[kCompressedSize]; - char *compressed_ptr; - uint64_t prev_time; - uint32_t prev_addr; - int32_t prev_pid; -} TraceMethod; - -extern TraceBB trace_bb; -extern TraceInsn trace_insn; -extern TraceStatic trace_static; -extern TraceAddr trace_load; -extern TraceAddr trace_store; -extern TraceExc trace_exc; -extern TracePid trace_pid; -extern TraceMethod trace_method; - -// The simulated time, in clock ticks, starting with one. -extern uint64_t sim_time; - -// This variable == 1 if we are currently tracing, otherwise == 0. -extern int tracing; -extern int trace_all_addr; -extern int trace_cache_miss; - -extern void start_tracing(); -extern void stop_tracing(); -extern void trace_init(const char *filename); -extern void trace_bb_start(uint32_t bb_addr); -extern void trace_add_insn(uint32_t insn, int is_thumb); -extern void trace_bb_end(); - -extern int get_insn_ticks_arm(uint32_t insn); -extern int get_insn_ticks_thumb(uint32_t insn); - -extern void trace_exception(uint32 pc); -extern void trace_bb_helper(uint64_t bb_num, TranslationBlock *tb); -extern void trace_insn_helper(); -extern void sim_dcache_load(uint32_t addr); -extern void sim_dcache_store(uint32_t addr, uint32_t val); -extern void sim_dcache_swp(uint32_t addr); -extern void trace_interpreted_method(uint32_t addr, int call_type); - -extern const char *trace_filename; -extern int tracing; -extern int trace_cache_miss; -extern int trace_all_addr; - -// Trace process/thread operations -extern void trace_switch(int pid); -extern void trace_fork(int tgid, int pid); -extern void trace_clone(int tgid, int pid); -extern void trace_exit(int exitcode); -extern void trace_name(char *name); - -#endif /* TRACE_H */ diff --git a/trace_common.h b/trace_common.h index fe84c1a..b2a2188 100644 --- a/trace_common.h +++ b/trace_common.h @@ -1,142 +1,4 @@ -/* Copyright (C) 2006-2007 The Android Open Source Project -** -** This software is licensed under the terms of the GNU General Public -** License version 2, as published by the Free Software Foundation, and -** may be copied, distributed, and modified under those terms. -** -** 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. -*/ - -#ifndef TRACE_COMMON_H -#define TRACE_COMMON_H - -#include <inttypes.h> - -// This should be the same as OPC_BUF_SIZE -#define kMaxInsnPerBB 512 - -#define kMaxNumBasicBlocks 1024 - -#define kMaxNumAddrs 1024 - -#define kInsnBufferSize 1024 - -#define kCompressedSize 8192 - -#define kMethodEnter 0 -#define kMethodExit 1 -#define kMethodException 2 -#define kNativeEnter 4 -#define kNativeExit 5 -#define kNativeException 6 - -// The trace identifier string must be less than 16 characters. -#define TRACE_IDENT "qemu_trace_file" -#define TRACE_VERSION 2 - -typedef struct TraceHeader { - char ident[16]; - int version; - uint32_t start_sec; - uint32_t start_usec; - uint32_t pdate; - uint32_t ptime; - uint32_t num_used_pids; // number of distinct process ids used - int first_unused_pid; // -1 if all 32,768 pids are used (unlikely) - uint8_t padding[4]; // next field is 8-byte aligned - uint64_t num_static_bb; - uint64_t num_static_insn; - uint64_t num_dynamic_bb; - uint64_t num_dynamic_insn; - uint64_t elapsed_usecs; -} TraceHeader; - -typedef struct BBRec { - uint64_t start_time; // time of first occurrence - uint64_t bb_num; // basic block number - uint32_t repeat; // repeat count (= 0 if just one occurrence) - uint64_t time_diff; // diff from previous time (if repeat > 0) -} BBRec; - -// Define a trace record for addresses that miss in the cache -typedef struct AddrRec { - uint64_t time; - uint32_t addr; -} AddrRec; - -// Define a trace record for the start time of each instruction -typedef struct InsnRec { - uint64_t time_diff; // time difference from last instruction - uint32_t repeat; // repeat count -} InsnRec; - -// Define record types for process id changes. -#define kPidEndOfFile 0 -#define kPidFork 1 -#define kPidClone 2 -#define kPidSwitch 3 -#define kPidExec 4 -#define kPidMmap 5 -#define kPidExit 6 -#define kPidKthreadName 7 -#define kPidSymbolAdd 8 -#define kPidSymbolRemove 9 -#define kPidMunmap 10 -#define kPidNoAction 11 -#define kPidName 12 - -#define bswap16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff)) - -#define bswap32(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) \ - | (((x) >> 8) & 0xff00) | (((x) >> 24) & 0xff)) - -#define bswap64(x) (((x) << 56) | (((x) & 0xff00) << 40) \ - | (((x) & 0xff0000) << 24) | (((x) & 0xff000000ull) << 8) \ - | (((x) >> 8) & 0xff000000ull) | (((x) >> 24) & 0xff0000) \ - | (((x) >> 40) & 0xff00) | ((x) >> 56)) - -#if BYTE_ORDER == LITTLE_ENDIAN -#define hostToLE16(x) (x) -#define hostToLE32(x) (x) -#define hostToLE64(x) (x) -#define LE16ToHost(x) (x) -#define LE32ToHost(x) (x) -#define LE64ToHost(x) (x) -#define convert16(x) -#define convert32(x) -#define convert64(x) -#else -#define hostToLE16(x) bswap16(x) -#define hostToLE32(x) bswap32(x) -#define hostToLE64(x) bswap64(x) -#define LE16ToHost(x) bswap16(x) -#define LE32ToHost(x) bswap32(x) -#define LE64ToHost(x) bswap64(x) -#define convert16(x) (x = bswap16(x)) -#define convert32(x) (x = bswap32(x)) -#define convert64(x) (x = bswap64(x)) -#endif - -/* XXX: we wrap 16-bit thumb instructions into 32-bit undefined ARM instructions - * for simplicity reasons. See section 3.13.1 section of the ARM ARM for details - * on the undefined instruction space we're using +/* This file should be removed once we update the Android qtools to + * include 'android-trace_common.h' directly. */ -static __inline__ int insn_is_thumb(uint32_t insn) -{ - return ((insn & 0xfff000f0) == 0xf7f000f0); -} - -static __inline__ uint32_t insn_wrap_thumb(uint32_t insn) -{ - return 0xf7f000f0 | ((insn & 0xfff0) << 4) | (insn & 0x000f); -} - -static __inline__ uint32_t insn_unwrap_thumb(uint32_t insn) -{ - return ((insn >> 4) & 0xfff0) | (insn & 0x000f); -} - -#endif /* TRACE_COMMON_H */ +#include "android-trace_common.h" diff --git a/translate-all.c b/translate-all.c index 3830007..ed174e7 100644 --- a/translate-all.c +++ b/translate-all.c @@ -72,9 +72,11 @@ unsigned long code_gen_max_block_size(void) if (max == 0) { max = TCG_MAX_OP_SIZE; -#define DEF(s, n, copy_size) max = copy_size > max? copy_size : max; +#define DEF(name, iarg, oarg, carg, flags) DEF2((iarg) + (oarg) + (carg)) +#define DEF2(copy_size) max = (copy_size > max) ? copy_size : max; #include "tcg-opc.h" #undef DEF +#undef DEF2 max *= OPC_MAX_SIZE; } @@ -169,8 +171,7 @@ int cpu_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr) /* The cpu state corresponding to 'searched_pc' is restored. */ int cpu_restore_state(TranslationBlock *tb, - CPUState *env, unsigned long searched_pc, - void *puc) + CPUState *env, unsigned long searched_pc) { TCGContext *s = &tcg_ctx; int j; @@ -214,7 +215,7 @@ int cpu_restore_state(TranslationBlock *tb, j--; env->icount_decr.u16.low -= gen_opc_icount[j]; - gen_pc_load(env, tb, searched_pc, j, puc); + restore_state_to_opc(env, tb, j); #ifdef CONFIG_PROFILER s->restore_time += profile_getclock() - ti; diff --git a/usb-linux.c b/usb-linux.c index d8610e8..51aee0c 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1374,7 +1374,7 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, static void usb_host_auto_timer(void *unused) { usb_host_scan(NULL, usb_host_auto_scan); - qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000); + qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000); } /* @@ -1452,7 +1452,7 @@ static int usb_host_auto_add(const char *spec) * If this turns out to be too expensive we can move that into a * separate thread. */ - usb_auto_timer = qemu_new_timer(rt_clock, usb_host_auto_timer, NULL); + usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_timer, NULL); if (!usb_auto_timer) { fprintf(stderr, "husb: failed to allocate auto scan timer\n"); qemu_free(f); @@ -1460,7 +1460,7 @@ static int usb_host_auto_add(const char *spec) } /* Check for new devices every two seconds */ - qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000); + qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000); } dprintf("husb: added auto filter: bus_num %d addr %d vid %d pid %d\n", diff --git a/vl-android.c b/vl-android.c index c240a44..8436d68 100644 --- a/vl-android.c +++ b/vl-android.c @@ -85,7 +85,6 @@ #ifndef _WIN32 #include <libgen.h> -#include <pwd.h> #include <sys/times.h> #include <sys/wait.h> #include <termios.h> @@ -163,8 +162,11 @@ #define memalign(align, size) malloc(size) #endif +#include "cpus.h" +#include "arch_init.h" #ifdef CONFIG_COCOA +int qemu_main(int argc, char **argv, char **envp); #undef main #define main qemu_main #endif /* CONFIG_COCOA */ @@ -174,7 +176,6 @@ #include "hw/usb.h" #include "hw/pcmcia.h" #include "hw/pc.h" -#include "hw/audiodev.h" #include "hw/isa.h" #include "hw/baum.h" #include "hw/bt.h" @@ -222,11 +223,8 @@ extern void android_emulator_set_base_port(int port); #include "disas.h" -#include "exec-all.h" - #ifdef CONFIG_TRACE -#include "trace.h" -#include "dcache.h" +#include "android-trace.h" #endif #include "qemu_socket.h" @@ -263,6 +261,10 @@ DisplayType display_type = DT_DEFAULT; const char* keyboard_layout = NULL; int64_t ticks_per_sec; ram_addr_t ram_size; +const char *mem_path = NULL; +#ifdef MAP_POPULATE +int mem_prealloc = 0; /* force preallocation of physical target memory */ +#endif int nb_nics; NICInfo nd_table[MAX_NICS]; int vm_running; @@ -274,15 +276,6 @@ int std_vga_enabled = 0; int vmsvga_enabled = 0; int xenfb_enabled = 0; QEMUClock *rtc_clock; -#ifdef TARGET_SPARC -int graphic_width = 1024; -int graphic_height = 768; -int graphic_depth = 8; -#else -int graphic_width = 800; -int graphic_height = 600; -int graphic_depth = 15; -#endif static int full_screen = 0; #ifdef CONFIG_SDL static int no_frame = 0; @@ -309,9 +302,6 @@ int no_reboot = 0; int no_shutdown = 0; int cursor_hide = 1; int graphic_rotate = 0; -#ifndef _WIN32 -int daemonize = 0; -#endif WatchdogTimerModel *watchdog = NULL; int watchdog_action = WDT_RESET; const char *option_rom[MAX_OPTION_ROMS]; @@ -334,8 +324,6 @@ int nb_numa_nodes; uint64_t node_mem[MAX_NODES]; uint64_t node_cpumask[MAX_NODES]; -static CPUState *cur_cpu; -static CPUState *next_cpu; static QEMUTimer *nographic_timer; uint8_t qemu_uuid[16]; @@ -567,28 +555,6 @@ FILE* rotate_qemu_log(FILE* old_log_fd, const char* filename) { return new_log_fd; } -/***********************************************************/ -void hw_error(const char *fmt, ...) -{ - va_list ap; - CPUState *env; - - va_start(ap, fmt); - fprintf(stderr, "qemu: hardware error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - for(env = first_cpu; env != NULL; env = env->next_cpu) { - fprintf(stderr, "CPU #%d:\n", env->cpu_index); -#ifdef TARGET_I386 - cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); -#else - cpu_dump_state(env, stderr, fprintf, 0); -#endif - } - va_end(ap); - abort(); -} - /***************/ /* ballooning */ @@ -615,33 +581,6 @@ ram_addr_t qemu_balloon_status(void) } /***********************************************************/ -/* real time host monotonic timer */ - -/* compute with 96 bit intermediate result: (a*b)/c */ -uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) -{ - union { - uint64_t ll; - struct { -#ifdef HOST_WORDS_BIGENDIAN - uint32_t high, low; -#else - uint32_t low, high; -#endif - } l; - } u, res; - uint64_t rl, rh; - - u.ll = a; - rl = (uint64_t)u.l.low * (uint64_t)b; - rh = (uint64_t)u.l.high * (uint64_t)b; - rh += (rl >> 32); - res.l.high = rh / c; - res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; - return res.ll; -} - -/***********************************************************/ /* host time/date access */ void qemu_get_timedate(struct tm *tm, int offset) { @@ -680,7 +619,7 @@ int qemu_timedate_diff(struct tm *tm) #ifdef CONFIG_TRACE -static int tbflush_requested; +int tbflush_requested; static int exit_requested; void start_tracing() @@ -1750,468 +1689,6 @@ void pcmcia_info(Monitor *mon) } /***********************************************************/ -/* I/O handling */ - -typedef struct IOHandlerRecord { - int fd; - IOCanReadHandler *fd_read_poll; - IOHandler *fd_read; - IOHandler *fd_write; - int deleted; - void *opaque; - /* temporary data */ - struct pollfd *ufd; - struct IOHandlerRecord *next; -} IOHandlerRecord; - -static IOHandlerRecord *first_io_handler; - -/* XXX: fd_read_poll should be suppressed, but an API change is - necessary in the character devices to suppress fd_can_read(). */ -int qemu_set_fd_handler2(int fd, - IOCanReadHandler *fd_read_poll, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque) -{ - IOHandlerRecord **pioh, *ioh; - - if (!fd_read && !fd_write) { - pioh = &first_io_handler; - for(;;) { - ioh = *pioh; - if (ioh == NULL) - break; - if (ioh->fd == fd) { - ioh->deleted = 1; - break; - } - pioh = &ioh->next; - } - } else { - for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { - if (ioh->fd == fd) - goto found; - } - ioh = qemu_mallocz(sizeof(IOHandlerRecord)); - ioh->next = first_io_handler; - first_io_handler = ioh; - found: - ioh->fd = fd; - ioh->fd_read_poll = fd_read_poll; - ioh->fd_read = fd_read; - ioh->fd_write = fd_write; - ioh->opaque = opaque; - ioh->deleted = 0; - } - return 0; -} - -int qemu_set_fd_handler(int fd, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque) -{ - return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); -} - -#ifdef _WIN32 -/***********************************************************/ -/* Polling handling */ - -typedef struct PollingEntry { - PollingFunc *func; - void *opaque; - struct PollingEntry *next; -} PollingEntry; - -static PollingEntry *first_polling_entry; - -int qemu_add_polling_cb(PollingFunc *func, void *opaque) -{ - PollingEntry **ppe, *pe; - pe = qemu_mallocz(sizeof(PollingEntry)); - pe->func = func; - pe->opaque = opaque; - for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next); - *ppe = pe; - return 0; -} - -void qemu_del_polling_cb(PollingFunc *func, void *opaque) -{ - PollingEntry **ppe, *pe; - for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) { - pe = *ppe; - if (pe->func == func && pe->opaque == opaque) { - *ppe = pe->next; - qemu_free(pe); - break; - } - } -} - -/***********************************************************/ -/* Wait objects support */ -typedef struct WaitObjects { - int num; - HANDLE events[MAXIMUM_WAIT_OBJECTS + 1]; - WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1]; - void *opaque[MAXIMUM_WAIT_OBJECTS + 1]; -} WaitObjects; - -static WaitObjects wait_objects = {0}; - -int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) -{ - WaitObjects *w = &wait_objects; - - if (w->num >= MAXIMUM_WAIT_OBJECTS) - return -1; - w->events[w->num] = handle; - w->func[w->num] = func; - w->opaque[w->num] = opaque; - w->num++; - return 0; -} - -void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) -{ - int i, found; - WaitObjects *w = &wait_objects; - - found = 0; - for (i = 0; i < w->num; i++) { - if (w->events[i] == handle) - found = 1; - if (found) { - w->events[i] = w->events[i + 1]; - w->func[i] = w->func[i + 1]; - w->opaque[i] = w->opaque[i + 1]; - } - } - if (found) - w->num--; -} -#endif - -/***********************************************************/ -/* ram save/restore */ - -static int ram_get_page(QEMUFile *f, uint8_t *buf, int len) -{ - int v; - - v = qemu_get_byte(f); - switch(v) { - case 0: - if (qemu_get_buffer(f, buf, len) != len) - return -EIO; - break; - case 1: - v = qemu_get_byte(f); - memset(buf, v, len); - break; - default: - return -EINVAL; - } - - if (qemu_file_has_error(f)) - return -EIO; - - return 0; -} - -static int ram_load_v1(QEMUFile *f, void *opaque) -{ - int ret; - ram_addr_t i; - - if (qemu_get_be32(f) != last_ram_offset) - return -EINVAL; - for(i = 0; i < last_ram_offset; i+= TARGET_PAGE_SIZE) { - ret = ram_get_page(f, qemu_get_ram_ptr(i), TARGET_PAGE_SIZE); - if (ret) - return ret; - } - return 0; -} - -#define BDRV_HASH_BLOCK_SIZE 1024 -#define IOBUF_SIZE 4096 -#define RAM_CBLOCK_MAGIC 0xfabe - -typedef struct RamDecompressState { - z_stream zstream; - QEMUFile *f; - uint8_t buf[IOBUF_SIZE]; -} RamDecompressState; - -static int ram_decompress_open(RamDecompressState *s, QEMUFile *f) -{ - int ret; - memset(s, 0, sizeof(*s)); - s->f = f; - ret = inflateInit(&s->zstream); - if (ret != Z_OK) - return -1; - return 0; -} - -static int ram_decompress_buf(RamDecompressState *s, uint8_t *buf, int len) -{ - int ret, clen; - - s->zstream.avail_out = len; - s->zstream.next_out = buf; - while (s->zstream.avail_out > 0) { - if (s->zstream.avail_in == 0) { - if (qemu_get_be16(s->f) != RAM_CBLOCK_MAGIC) - return -1; - clen = qemu_get_be16(s->f); - if (clen > IOBUF_SIZE) - return -1; - qemu_get_buffer(s->f, s->buf, clen); - s->zstream.avail_in = clen; - s->zstream.next_in = s->buf; - } - ret = inflate(&s->zstream, Z_PARTIAL_FLUSH); - if (ret != Z_OK && ret != Z_STREAM_END) { - return -1; - } - } - return 0; -} - -static void ram_decompress_close(RamDecompressState *s) -{ - inflateEnd(&s->zstream); -} - -#define RAM_SAVE_FLAG_FULL 0x01 -#define RAM_SAVE_FLAG_COMPRESS 0x02 -#define RAM_SAVE_FLAG_MEM_SIZE 0x04 -#define RAM_SAVE_FLAG_PAGE 0x08 -#define RAM_SAVE_FLAG_EOS 0x10 - -static int is_dup_page(uint8_t *page, uint8_t ch) -{ - uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch; - uint32_t *array = (uint32_t *)page; - int i; - - for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) { - if (array[i] != val) - return 0; - } - - return 1; -} - -static int ram_save_block(QEMUFile *f) -{ - static ram_addr_t current_addr = 0; - ram_addr_t saved_addr = current_addr; - ram_addr_t addr = 0; - int found = 0; - - while (addr < last_ram_offset) { - if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) { - uint8_t *p; - - cpu_physical_memory_reset_dirty(current_addr, - current_addr + TARGET_PAGE_SIZE, - MIGRATION_DIRTY_FLAG); - - p = qemu_get_ram_ptr(current_addr); - - if (is_dup_page(p, *p)) { - qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_COMPRESS); - qemu_put_byte(f, *p); - } else { - qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_PAGE); - qemu_put_buffer(f, p, TARGET_PAGE_SIZE); - } - - found = 1; - break; - } - addr += TARGET_PAGE_SIZE; - current_addr = (saved_addr + addr) % last_ram_offset; - } - - return found; -} - -static uint64_t bytes_transferred = 0; - -static ram_addr_t ram_save_remaining(void) -{ - ram_addr_t addr; - ram_addr_t count = 0; - - for (addr = 0; addr < last_ram_offset; addr += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) - count++; - } - - return count; -} - -uint64_t ram_bytes_remaining(void) -{ - return ram_save_remaining() * TARGET_PAGE_SIZE; -} - -uint64_t ram_bytes_transferred(void) -{ - return bytes_transferred; -} - -uint64_t ram_bytes_total(void) -{ - return last_ram_offset; -} - -static int ram_save_live(QEMUFile *f, int stage, void *opaque) -{ - ram_addr_t addr; - uint64_t bytes_transferred_last; - double bwidth = 0; - uint64_t expected_time = 0; - - cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX); - - if (stage == 1) { - /* Make sure all dirty bits are set */ - for (addr = 0; addr < last_ram_offset; addr += TARGET_PAGE_SIZE) { - if (!cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) - cpu_physical_memory_set_dirty(addr); - } - - /* Enable dirty memory tracking */ - cpu_physical_memory_set_dirty_tracking(1); - - qemu_put_be64(f, last_ram_offset | RAM_SAVE_FLAG_MEM_SIZE); - } - - bytes_transferred_last = bytes_transferred; - bwidth = qemu_get_clock_ns(rt_clock); - - while (!qemu_file_rate_limit(f)) { - int ret; - - ret = ram_save_block(f); - bytes_transferred += ret * TARGET_PAGE_SIZE; - if (ret == 0) /* no more blocks */ - break; - } - - bwidth = qemu_get_clock_ns(rt_clock) - bwidth; - bwidth = (bytes_transferred - bytes_transferred_last) / bwidth; - - /* if we haven't transferred anything this round, force expected_time to a - * a very high value, but without crashing */ - if (bwidth == 0) - bwidth = 0.000001; - - /* try transferring iterative blocks of memory */ - - if (stage == 3) { - - /* flush all remaining blocks regardless of rate limiting */ - while (ram_save_block(f) != 0) { - bytes_transferred += TARGET_PAGE_SIZE; - } - cpu_physical_memory_set_dirty_tracking(0); - } - - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - - expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth; - - return (stage == 2) && (expected_time <= migrate_max_downtime()); -} - -static int ram_load_dead(QEMUFile *f, void *opaque) -{ - RamDecompressState s1, *s = &s1; - uint8_t buf[10]; - ram_addr_t i; - - if (ram_decompress_open(s, f) < 0) - return -EINVAL; - for(i = 0; i < last_ram_offset; i+= BDRV_HASH_BLOCK_SIZE) { - if (ram_decompress_buf(s, buf, 1) < 0) { - fprintf(stderr, "Error while reading ram block header\n"); - goto error; - } - if (buf[0] == 0) { - if (ram_decompress_buf(s, qemu_get_ram_ptr(i), - BDRV_HASH_BLOCK_SIZE) < 0) { - fprintf(stderr, "Error while reading ram block address=0x%08" PRIx64, (uint64_t)i); - goto error; - } - } else { - error: - printf("Error block header\n"); - return -EINVAL; - } - } - ram_decompress_close(s); - - return 0; -} - -static int ram_load(QEMUFile *f, void *opaque, int version_id) -{ - ram_addr_t addr; - int flags; - - if (version_id == 1) - return ram_load_v1(f, opaque); - - if (version_id == 2) { - if (qemu_get_be32(f) != last_ram_offset) - return -EINVAL; - return ram_load_dead(f, opaque); - } - - if (version_id != 3) - return -EINVAL; - - do { - addr = qemu_get_be64(f); - - flags = addr & ~TARGET_PAGE_MASK; - addr &= TARGET_PAGE_MASK; - - if (flags & RAM_SAVE_FLAG_MEM_SIZE) { - if (addr != last_ram_offset) - return -EINVAL; - } - - if (flags & RAM_SAVE_FLAG_FULL) { - if (ram_load_dead(f, opaque) < 0) - return -EINVAL; - } - - if (flags & RAM_SAVE_FLAG_COMPRESS) { - uint8_t ch = qemu_get_byte(f); - memset(qemu_get_ram_ptr(addr), ch, TARGET_PAGE_SIZE); - } else if (flags & RAM_SAVE_FLAG_PAGE) - qemu_get_buffer(f, qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE); - } while (!(flags & RAM_SAVE_FLAG_EOS)); - - return 0; -} - -void qemu_service_io(void) -{ - qemu_notify_event(); -} - -/***********************************************************/ /* machine registration */ static QEMUMachine *first_machine = NULL; @@ -2268,14 +1745,14 @@ static void gui_update(void *opaque) interval = dcl->gui_timer_interval; dcl = dcl->next; } - qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock(rt_clock)); + qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock)); } static void nographic_update(void *opaque) { uint64_t interval = GUI_REFRESH_INTERVAL; - qemu_mod_timer(nographic_timer, interval + qemu_get_clock(rt_clock)); + qemu_mod_timer(nographic_timer, interval + qemu_get_clock_ms(rt_clock)); } struct vm_change_state_entry { @@ -2305,7 +1782,7 @@ void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) qemu_free (e); } -static void vm_state_notify(int running, int reason) +void vm_state_notify(int running, int reason) { VMChangeStateEntry *e; @@ -2314,9 +1791,6 @@ static void vm_state_notify(int running, int reason) } } -static void resume_all_vcpus(void); -static void pause_all_vcpus(void); - void vm_start(void) { if (!vm_running) { @@ -2339,9 +1813,10 @@ typedef struct QEMUResetEntry { static QEMUResetEntry *first_reset_entry; static int reset_requested; -static int shutdown_requested; +static int shutdown_requested, shutdown_signal = -1; +static pid_t shutdown_pid; static int powerdown_requested; -static int debug_requested; +int debug_requested; static int vmstop_requested; int qemu_shutdown_requested(void) @@ -2379,16 +1854,6 @@ static int qemu_vmstop_requested(void) return r; } -static void do_vm_stop(int reason) -{ - if (vm_running) { - cpu_disable_ticks(); - vm_running = 0; - pause_all_vcpus(); - vm_state_notify(0, reason); - } -} - void qemu_register_reset(QEMUResetHandler *func, int order, void *opaque) { QEMUResetEntry **pre, *re; @@ -2425,6 +1890,13 @@ void qemu_system_reset_request(void) qemu_notify_event(); } +void qemu_system_killed(int signal, pid_t pid) +{ + shutdown_signal = signal; + shutdown_pid = pid; + qemu_system_shutdown_request(); +} + void qemu_system_shutdown_request(void) { shutdown_requested = 1; @@ -2445,586 +1917,36 @@ static void qemu_system_vmstop_request(int reason) } #endif -#ifndef _WIN32 -static int io_thread_fd = -1; - -#if 0 -static void qemu_event_increment(void) -{ - static const char byte = 0; - - if (io_thread_fd == -1) - return; - - write(io_thread_fd, &byte, sizeof(byte)); -} -#endif - -static void qemu_event_read(void *opaque) -{ - int fd = (unsigned long)opaque; - ssize_t len; - - /* Drain the notify pipe */ - do { - char buffer[512]; - len = read(fd, buffer, sizeof(buffer)); - } while ((len == -1 && errno == EINTR) || len > 0); -} - -static int qemu_event_init(void) -{ - int err; - int fds[2]; - - err = pipe(fds); - if (err == -1) - return -errno; - - err = fcntl_setfl(fds[0], O_NONBLOCK); - if (err < 0) - goto fail; - - err = fcntl_setfl(fds[1], O_NONBLOCK); - if (err < 0) - goto fail; - - qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL, - (void *)(unsigned long)fds[0]); - - io_thread_fd = fds[1]; - return 0; - -fail: - close(fds[0]); - close(fds[1]); - return err; -} -#else -HANDLE qemu_event_handle; - -static void dummy_event_handler(void *opaque) -{ -} - -static int qemu_event_init(void) -{ - qemu_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!qemu_event_handle) { - perror("Failed CreateEvent"); - return -1; - } - qemu_add_wait_object(qemu_event_handle, dummy_event_handler, NULL); - return 0; -} - -#if 0 -static void qemu_event_increment(void) -{ - SetEvent(qemu_event_handle); -} -#endif -#endif - -static int cpu_can_run(CPUState *env) -{ - if (env->stop) - return 0; - if (env->stopped) - return 0; - return 1; -} - -#ifndef CONFIG_IOTHREAD -static int qemu_init_main_loop(void) -{ - return qemu_event_init(); -} - -void qemu_init_vcpu(void *_env) -{ - CPUState *env = _env; - - if (kvm_enabled()) - kvm_init_vcpu(env); - return; -} - -int qemu_cpu_self(void *env) -{ - return 1; -} - -static void resume_all_vcpus(void) -{ -} - -static void pause_all_vcpus(void) -{ -} - -void qemu_cpu_kick(void *env) -{ - return; -} - -void qemu_notify_event(void) -{ - CPUState *env = cpu_single_env; - - if (env) { - cpu_exit(env); -#ifdef USE_KQEMU - if (env->kqemu_enabled) - kqemu_cpu_interrupt(env); -#endif - } -} - -#define qemu_mutex_lock_iothread() do { } while (0) -#define qemu_mutex_unlock_iothread() do { } while (0) - -void vm_stop(int reason) -{ - do_vm_stop(reason); -} - -#else /* CONFIG_IOTHREAD */ - -#include "qemu-thread.h" - -QemuMutex qemu_global_mutex; -static QemuMutex qemu_fair_mutex; - -static QemuThread io_thread; - -static QemuThread *tcg_cpu_thread; -static QemuCond *tcg_halt_cond; - -static int qemu_system_ready; -/* cpu creation */ -static QemuCond qemu_cpu_cond; -/* system init */ -static QemuCond qemu_system_cond; -static QemuCond qemu_pause_cond; - -static void block_io_signals(void); -static void unblock_io_signals(void); -static int tcg_has_work(void); - -static int qemu_init_main_loop(void) -{ - int ret; - - ret = qemu_event_init(); - if (ret) - return ret; - - qemu_cond_init(&qemu_pause_cond); - qemu_mutex_init(&qemu_fair_mutex); - qemu_mutex_init(&qemu_global_mutex); - qemu_mutex_lock(&qemu_global_mutex); - - unblock_io_signals(); - qemu_thread_self(&io_thread); - - return 0; -} - -static void qemu_wait_io_event(CPUState *env) -{ - while (!tcg_has_work()) - qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); - - qemu_mutex_unlock(&qemu_global_mutex); - - /* - * Users of qemu_global_mutex can be starved, having no chance - * to acquire it since this path will get to it first. - * So use another lock to provide fairness. - */ - qemu_mutex_lock(&qemu_fair_mutex); - qemu_mutex_unlock(&qemu_fair_mutex); - - qemu_mutex_lock(&qemu_global_mutex); - if (env->stop) { - env->stop = 0; - env->stopped = 1; - qemu_cond_signal(&qemu_pause_cond); - } -} - -static int qemu_cpu_exec(CPUState *env); - -static void *kvm_cpu_thread_fn(void *arg) -{ - CPUState *env = arg; - - block_io_signals(); - qemu_thread_self(env->thread); - - /* signal CPU creation */ - qemu_mutex_lock(&qemu_global_mutex); - env->created = 1; - qemu_cond_signal(&qemu_cpu_cond); - - /* and wait for machine initialization */ - while (!qemu_system_ready) - qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); - - while (1) { - if (cpu_can_run(env)) - qemu_cpu_exec(env); - qemu_wait_io_event(env); - } - - return NULL; -} - -static void tcg_cpu_exec(void); - -static void *tcg_cpu_thread_fn(void *arg) -{ - CPUState *env = arg; - - block_io_signals(); - qemu_thread_self(env->thread); - - /* signal CPU creation */ - qemu_mutex_lock(&qemu_global_mutex); - for (env = first_cpu; env != NULL; env = env->next_cpu) - env->created = 1; - qemu_cond_signal(&qemu_cpu_cond); - - /* and wait for machine initialization */ - while (!qemu_system_ready) - qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); - - while (1) { - tcg_cpu_exec(); - qemu_wait_io_event(cur_cpu); - } - - return NULL; -} - -void qemu_cpu_kick(void *_env) -{ - CPUState *env = _env; - qemu_cond_broadcast(env->halt_cond); - if (kvm_enabled()) - qemu_thread_signal(env->thread, SIGUSR1); -} - -int qemu_cpu_self(void *env) -{ - return (cpu_single_env != NULL); -} - -static void cpu_signal(int sig) -{ - if (cpu_single_env) - cpu_exit(cpu_single_env); -} - -static void block_io_signals(void) -{ - sigset_t set; - struct sigaction sigact; - - sigemptyset(&set); - sigaddset(&set, SIGUSR2); - sigaddset(&set, SIGIO); - sigaddset(&set, SIGALRM); - pthread_sigmask(SIG_BLOCK, &set, NULL); - - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); - - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = cpu_signal; - sigaction(SIGUSR1, &sigact, NULL); -} - -static void unblock_io_signals(void) -{ - sigset_t set; - - sigemptyset(&set); - sigaddset(&set, SIGUSR2); - sigaddset(&set, SIGIO); - sigaddset(&set, SIGALRM); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); - - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_BLOCK, &set, NULL); -} - -static void qemu_signal_lock(unsigned int msecs) -{ - qemu_mutex_lock(&qemu_fair_mutex); - - while (qemu_mutex_trylock(&qemu_global_mutex)) { - qemu_thread_signal(tcg_cpu_thread, SIGUSR1); - if (!qemu_mutex_timedlock(&qemu_global_mutex, msecs)) - break; - } - qemu_mutex_unlock(&qemu_fair_mutex); -} - -void qemu_mutex_lock_iothread(void) -{ - if (kvm_enabled()) { - qemu_mutex_lock(&qemu_fair_mutex); - qemu_mutex_lock(&qemu_global_mutex); - qemu_mutex_unlock(&qemu_fair_mutex); - } else - qemu_signal_lock(100); -} - -void qemu_mutex_unlock_iothread(void) -{ - qemu_mutex_unlock(&qemu_global_mutex); -} - -static int all_vcpus_paused(void) -{ - CPUState *penv = first_cpu; - - while (penv) { - if (!penv->stopped) - return 0; - penv = (CPUState *)penv->next_cpu; - } - - return 1; -} - -static void pause_all_vcpus(void) -{ - CPUState *penv = first_cpu; - - while (penv) { - penv->stop = 1; - qemu_thread_signal(penv->thread, SIGUSR1); - qemu_cpu_kick(penv); - penv = (CPUState *)penv->next_cpu; - } - - while (!all_vcpus_paused()) { - qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100); - penv = first_cpu; - while (penv) { - qemu_thread_signal(penv->thread, SIGUSR1); - penv = (CPUState *)penv->next_cpu; - } - } -} - -static void resume_all_vcpus(void) -{ - CPUState *penv = first_cpu; - - while (penv) { - penv->stop = 0; - penv->stopped = 0; - qemu_thread_signal(penv->thread, SIGUSR1); - qemu_cpu_kick(penv); - penv = (CPUState *)penv->next_cpu; - } -} - -static void tcg_init_vcpu(void *_env) -{ - CPUState *env = _env; - /* share a single thread for all cpus with TCG */ - if (!tcg_cpu_thread) { - env->thread = qemu_mallocz(sizeof(QemuThread)); - env->halt_cond = qemu_mallocz(sizeof(QemuCond)); - qemu_cond_init(env->halt_cond); - qemu_thread_create(env->thread, tcg_cpu_thread_fn, env); - while (env->created == 0) - qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); - tcg_cpu_thread = env->thread; - tcg_halt_cond = env->halt_cond; - } else { - env->thread = tcg_cpu_thread; - env->halt_cond = tcg_halt_cond; - } -} - -static void kvm_start_vcpu(CPUState *env) -{ -#if 0 - kvm_init_vcpu(env); - env->thread = qemu_mallocz(sizeof(QemuThread)); - env->halt_cond = qemu_mallocz(sizeof(QemuCond)); - qemu_cond_init(env->halt_cond); - qemu_thread_create(env->thread, kvm_cpu_thread_fn, env); - while (env->created == 0) - qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); -#endif -} - -void qemu_init_vcpu(void *_env) -{ - CPUState *env = _env; - - if (kvm_enabled()) - kvm_start_vcpu(env); - else - tcg_init_vcpu(env); -} - -void qemu_notify_event(void) -{ - qemu_event_increment(); -} - -void vm_stop(int reason) -{ - QemuThread me; - qemu_thread_self(&me); - - if (!qemu_thread_equal(&me, &io_thread)) { - qemu_system_vmstop_request(reason); - /* - * FIXME: should not return to device code in case - * vm_stop() has been requested. - */ - if (cpu_single_env) { - cpu_exit(cpu_single_env); - cpu_single_env->stop = 1; - } - return; - } - do_vm_stop(reason); -} - -#endif - - -#ifdef _WIN32 -static void host_main_loop_wait(int *timeout) -{ - int ret, ret2, i; - PollingEntry *pe; - - - /* XXX: need to suppress polling by better using win32 events */ - ret = 0; - for(pe = first_polling_entry; pe != NULL; pe = pe->next) { - ret |= pe->func(pe->opaque); - } - if (ret == 0) { - int err; - WaitObjects *w = &wait_objects; - - ret = WaitForMultipleObjects(w->num, w->events, FALSE, *timeout); - if (WAIT_OBJECT_0 + 0 <= ret && ret <= WAIT_OBJECT_0 + w->num - 1) { - if (w->func[ret - WAIT_OBJECT_0]) - w->func[ret - WAIT_OBJECT_0](w->opaque[ret - WAIT_OBJECT_0]); - - /* Check for additional signaled events */ - for(i = (ret - WAIT_OBJECT_0 + 1); i < w->num; i++) { - - /* Check if event is signaled */ - ret2 = WaitForSingleObject(w->events[i], 0); - if(ret2 == WAIT_OBJECT_0) { - if (w->func[i]) - w->func[i](w->opaque[i]); - } else if (ret2 == WAIT_TIMEOUT) { - } else { - err = GetLastError(); - fprintf(stderr, "WaitForSingleObject error %d %d\n", i, err); - } - } - } else if (ret == WAIT_TIMEOUT) { - } else { - err = GetLastError(); - fprintf(stderr, "WaitForMultipleObjects error %d %d\n", ret, err); - } - } - - *timeout = 0; -} -#else -static void host_main_loop_wait(int *timeout) -{ -} -#endif - void main_loop_wait(int timeout) { - IOHandlerRecord *ioh; fd_set rfds, wfds, xfds; int ret, nfds; struct timeval tv; qemu_bh_update_timeout(&timeout); - host_main_loop_wait(&timeout); + os_host_main_loop_wait(&timeout); + + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; /* poll any events */ + /* XXX: separate device handlers from system ones */ nfds = -1; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&xfds); - for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { - if (ioh->deleted) - continue; - if (ioh->fd_read && - (!ioh->fd_read_poll || - ioh->fd_read_poll(ioh->opaque) != 0)) { - FD_SET(ioh->fd, &rfds); - if (ioh->fd > nfds) - nfds = ioh->fd; - } - if (ioh->fd_write) { - FD_SET(ioh->fd, &wfds); - if (ioh->fd > nfds) - nfds = ioh->fd; - } - } - - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - -#if defined(CONFIG_SLIRP) + qemu_iohandler_fill(&nfds, &rfds, &wfds, &xfds); if (slirp_is_inited()) { slirp_select_fill(&nfds, &rfds, &wfds, &xfds); } -#endif + qemu_mutex_unlock_iothread(); ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); qemu_mutex_lock_iothread(); - if (ret > 0) { - IOHandlerRecord **pioh; - - for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { - if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) { - ioh->fd_read(ioh->opaque); - } - if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) { - ioh->fd_write(ioh->opaque); - } - } - - /* remove deleted IO handlers */ - pioh = &first_io_handler; - while (*pioh) { - ioh = *pioh; - if (ioh->deleted) { - *pioh = ioh->next; - qemu_free(ioh); - } else - pioh = &ioh->next; - } - } -#if defined(CONFIG_SLIRP) + qemu_iohandler_poll(&rfds, &wfds, &xfds, ret); if (slirp_is_inited()) { if (ret < 0) { FD_ZERO(&rfds); @@ -3033,7 +1955,6 @@ void main_loop_wait(int timeout) } slirp_select_poll(&rfds, &wfds, &xfds); } -#endif charpipe_poll(); qemu_run_all_timers(); @@ -3044,102 +1965,6 @@ void main_loop_wait(int timeout) } -static int qemu_cpu_exec(CPUState *env) -{ - int ret; -#ifdef CONFIG_PROFILER - int64_t ti; -#endif - -#ifdef CONFIG_PROFILER - ti = profile_getclock(); -#endif - if (use_icount) { - int64_t count; - int decr; - qemu_icount -= (env->icount_decr.u16.low + env->icount_extra); - env->icount_decr.u16.low = 0; - env->icount_extra = 0; - count = qemu_next_deadline(); - count = (count + (1 << icount_time_shift) - 1) - >> icount_time_shift; - qemu_icount += count; - decr = (count > 0xffff) ? 0xffff : count; - count -= decr; - env->icount_decr.u16.low = decr; - env->icount_extra = count; - } -#ifdef CONFIG_TRACE - if (tbflush_requested) { - tbflush_requested = 0; - tb_flush(env); - return EXCP_INTERRUPT; - } -#endif - - - ret = cpu_exec(env); -#ifdef CONFIG_PROFILER - qemu_time += profile_getclock() - ti; -#endif - if (use_icount) { - /* Fold pending instructions back into the - instruction counter, and clear the interrupt flag. */ - qemu_icount -= (env->icount_decr.u16.low - + env->icount_extra); - env->icount_decr.u32 = 0; - env->icount_extra = 0; - } - return ret; -} - -static void tcg_cpu_exec(void) -{ - int ret = 0; - - if (next_cpu == NULL) - next_cpu = first_cpu; - for (; next_cpu != NULL; next_cpu = next_cpu->next_cpu) { - CPUState *env = cur_cpu = next_cpu; - - if (!vm_running) - break; - if (qemu_timer_alarm_pending()) { - break; - } - if (cpu_can_run(env)) - ret = qemu_cpu_exec(env); - if (ret == EXCP_DEBUG) { - gdb_set_stop_cpu(env); - debug_requested = 1; - break; - } - } -} - -static int cpu_has_work(CPUState *env) -{ - if (env->stop) - return 1; - if (env->stopped) - return 0; - if (!env->halted) - return 1; - if (qemu_cpu_has_work(env)) - return 1; - return 0; -} - -int tcg_has_work(void) -{ - CPUState *env; - - for (env = first_cpu; env != NULL; env = env->next_cpu) - if (cpu_has_work(env)) - return 1; - return 0; -} - static int vm_can_run(void) { if (powerdown_requested) @@ -3231,7 +2056,7 @@ void qemu_help(int exitcode) #define DEF(option, opt_arg, opt_enum, opt_help) \ opt_help #define DEFHEADING(text) stringify(text) "\n" -#include "qemu-options.h" +#include "qemu-options.def" #undef DEF #undef DEFHEADING #undef GEN_DOCS @@ -3260,7 +2085,7 @@ enum { #define DEF(option, opt_arg, opt_enum, opt_help) \ opt_enum, #define DEFHEADING(text) -#include "qemu-options.h" +#include "qemu-options.def" #undef DEF #undef DEFHEADING #undef GEN_DOCS @@ -3277,159 +2102,13 @@ static const QEMUOption qemu_options[] = { #define DEF(option, opt_arg, opt_enum, opt_help) \ { option, opt_arg, opt_enum }, #define DEFHEADING(text) -#include "qemu-options.h" +#include "qemu-options.def" #undef DEF #undef DEFHEADING #undef GEN_DOCS { NULL, 0, 0 }, }; -#ifdef HAS_AUDIO -struct soundhw soundhw[] = { -#ifdef HAS_AUDIO_CHOICE -#if defined(TARGET_I386) || defined(TARGET_MIPS) - { - "pcspk", - "PC speaker", - 0, - 1, - { .init_isa = pcspk_audio_init } - }, -#endif - -#ifdef CONFIG_SB16 - { - "sb16", - "Creative Sound Blaster 16", - 0, - 1, - { .init_isa = SB16_init } - }, -#endif - -#ifdef CONFIG_CS4231A - { - "cs4231a", - "CS4231A", - 0, - 1, - { .init_isa = cs4231a_init } - }, -#endif - -#ifdef CONFIG_ADLIB - { - "adlib", -#ifdef HAS_YMF262 - "Yamaha YMF262 (OPL3)", -#else - "Yamaha YM3812 (OPL2)", -#endif - 0, - 1, - { .init_isa = Adlib_init } - }, -#endif - -#ifdef CONFIG_GUS - { - "gus", - "Gravis Ultrasound GF1", - 0, - 1, - { .init_isa = GUS_init } - }, -#endif - -#ifdef CONFIG_AC97 - { - "ac97", - "Intel 82801AA AC97 Audio", - 0, - 0, - { .init_pci = ac97_init } - }, -#endif - -#ifdef CONFIG_ES1370 - { - "es1370", - "ENSONIQ AudioPCI ES1370", - 0, - 0, - { .init_pci = es1370_init } - }, -#endif - -#endif /* HAS_AUDIO_CHOICE */ - - { NULL, NULL, 0, 0, { NULL } } -}; - -static void select_soundhw (const char *optarg) -{ - struct soundhw *c; - - if (*optarg == '?') { - show_valid_cards: - - printf ("Valid sound card names (comma separated):\n"); - for (c = soundhw; c->name; ++c) { - printf ("%-11s %s\n", c->name, c->descr); - } - printf ("\n-soundhw all will enable all of the above\n"); - if (*optarg != '?') { - PANIC("Unknown sound card name: %s", optarg); - } else { - QEMU_EXIT(0); - } - } - else { - size_t l; - const char *p; - char *e; - int bad_card = 0; - - if (!strcmp (optarg, "all")) { - for (c = soundhw; c->name; ++c) { - c->enabled = 1; - } - return; - } - - p = optarg; - while (*p) { - e = strchr (p, ','); - l = !e ? strlen (p) : (size_t) (e - p); - - for (c = soundhw; c->name; ++c) { - if (!strncmp (c->name, p, l)) { - c->enabled = 1; - break; - } - } - - if (!c->name) { -#ifndef CONFIG_ANDROID - if (l > 80) { - fprintf (stderr, - "Unknown sound card name (too big to show)\n"); - } else { - fprintf (stderr, "Unknown sound card name `%.*s'\n", - (int) l, p); - } -#endif // !CONFIG_ANDROID - bad_card = 1; - } - p += l + (e != NULL); - } - - if (bad_card) - goto show_valid_cards; - } -} -#endif - static void select_vgahw (const char *p) { const char *opts; @@ -3465,66 +2144,8 @@ static void select_vgahw (const char *p) } } -#ifdef _WIN32 -static BOOL WINAPI qemu_ctrl_handler(DWORD type) -{ - exit(STATUS_CONTROL_C_EXIT); - return TRUE; -} -#endif - -int qemu_uuid_parse(const char *str, uint8_t *uuid) -{ - int ret; - - if(strlen(str) != 36) - return -1; - - ret = sscanf(str, UUID_FMT, &uuid[0], &uuid[1], &uuid[2], &uuid[3], - &uuid[4], &uuid[5], &uuid[6], &uuid[7], &uuid[8], &uuid[9], - &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14], &uuid[15]); - - if(ret != 16) - return -1; - -#ifdef TARGET_I386 - smbios_add_field(1, offsetof(struct smbios_type_1, uuid), 16, uuid); -#endif - - return 0; -} - #define MAX_NET_CLIENTS 32 -#ifndef _WIN32 - -static void termsig_handler(int signal) -{ - qemu_system_shutdown_request(); -} - -static void sigchld_handler(int signal) -{ - waitpid(-1, NULL, WNOHANG); -} - -static void sighandler_setup(void) -{ - struct sigaction act; - - memset(&act, 0, sizeof(act)); - act.sa_handler = termsig_handler; - sigaction(SIGINT, &act, NULL); - sigaction(SIGHUP, &act, NULL); - sigaction(SIGTERM, &act, NULL); - - act.sa_handler = sigchld_handler; - act.sa_flags = SA_NOCLDSTOP; - sigaction(SIGCHLD, &act, NULL); -} - -#endif - #ifdef _WIN32 /* Look for support files in the same directory as the executable. */ static char *find_datadir(const char *argv0) @@ -3920,18 +2541,9 @@ int main(int argc, char **argv, char **envp) const char *cpu_model; const char *usb_devices[MAX_USB_CMDLINE]; int usb_devices_index; -#ifndef _WIN32 - int fds[2]; -#endif int tb_size; const char *pid_file = NULL; const char *incoming = NULL; -#ifndef _WIN32 - int fd = 0; - struct passwd *pwd = NULL; - const char *chroot_dir = NULL; - const char *run_as = NULL; -#endif CPUState *env; int show_vnc_port = 0; IniFile* hw_ini = NULL; @@ -3950,35 +2562,7 @@ int main(int argc, char **argv, char **envp) qemu_cache_utils_init(envp); QLIST_INIT (&vm_change_state_head); -#ifndef _WIN32 - { - struct sigaction act; - sigfillset(&act.sa_mask); - act.sa_flags = 0; - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, NULL); - } -#else - SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE); - /* Note: cpu_interrupt() is currently not SMP safe, so we force - QEMU to run on a single CPU */ - { - HANDLE h; - DWORD mask, smask; - int i; - h = GetCurrentProcess(); - if (GetProcessAffinityMask(h, &mask, &smask)) { - for(i = 0; i < 32; i++) { - if (mask & (1 << i)) - break; - } - if (i != 32) { - mask = 1 << i; - SetProcessAffinityMask(h, mask); - } - } - } -#endif + os_setup_early_signal_handling(); module_call_init(MODULE_INIT_MACHINE); machine = find_default_machine(); @@ -4268,13 +2852,6 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_bootp: bootp_filename = optarg; break; -#if 0 /* ANDROID disabled */ -#ifndef _WIN32 - case QEMU_OPTION_smb: - net_slirp_smb(optarg); - break; -#endif -#endif /* ANDROID */ case QEMU_OPTION_redir: net_slirp_redir(NULL, optarg, NULL); break; @@ -4363,9 +2940,6 @@ int main(int argc, char **argv, char **envp) singlestep = 1; break; case QEMU_OPTION_S: -#if 0 /* ANDROID */ - PANIC("Sorry, stopped launch is not supported in the Android emulator" ); -#endif autostart = 0; break; #ifndef _WIN32 @@ -4503,17 +3077,7 @@ int main(int argc, char **argv, char **envp) break; #endif case QEMU_OPTION_smbios: - if(smbios_entry_add(optarg) < 0) { - PANIC("Wrong smbios provided"); - } - break; -#endif -#ifdef CONFIG_KQEMU - case QEMU_OPTION_no_kqemu: - kqemu_allowed = 0; - break; - case QEMU_OPTION_kernel_kqemu: - kqemu_allowed = 2; + do_smbios_option(optarg); break; #endif #ifdef CONFIG_KVM @@ -4570,11 +3134,6 @@ int main(int argc, char **argv, char **envp) PANIC("Fail to parse UUID string. Wrong format."); } break; -#ifndef _WIN32 - case QEMU_OPTION_daemonize: - daemonize = 1; - break; -#endif case QEMU_OPTION_option_rom: if (nb_option_roms >= MAX_OPTION_ROMS) { PANIC("Too many option ROMs"); @@ -4740,14 +3299,6 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_incoming: incoming = optarg; break; -#ifndef _WIN32 - case QEMU_OPTION_chroot: - chroot_dir = optarg; - break; - case QEMU_OPTION_runas: - run_as = optarg; - break; -#endif #ifdef CONFIG_XEN case QEMU_OPTION_xen_domid: xen_domid = atoi(optarg); @@ -4914,6 +3465,8 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_snapshot_no_time_update: android_snapshot_update_time = 0; break; + default: + os_parse_cmd_args(popt->index, optarg); } } } @@ -5275,6 +3828,11 @@ int main(int argc, char **argv, char **envp) serial_hds_add_at(1, "android-qemud"); stralloc_add_str(kernel_params, " android.qemud=ttyS1"); + if (pid_file && qemu_create_pidfile(pid_file) != 0) { + os_pidfile_error(); + exit(1); + } + #if defined(CONFIG_KVM) if (kvm_allowed < 0) { kvm_allowed = kvm_check_allowed(); @@ -5304,68 +3862,6 @@ int main(int argc, char **argv, char **envp) monitor_device = "stdio"; } -#ifndef _WIN32 - if (daemonize) { - pid_t pid; - - if (pipe(fds) == -1) { - PANIC("Unable to aquire pidfile"); - } - - pid = fork(); - if (pid > 0) { - uint8_t status; - ssize_t len; - - close(fds[1]); - - again: - len = read(fds[0], &status, 1); - if (len == -1 && (errno == EINTR)) - goto again; - - if (len != 1) { - PANIC("Error when aquiring pidfile"); - } - else if (status == 1) { - PANIC("Could not acquire pidfile"); - } else { - QEMU_EXIT(0); - } - } else if (pid < 0) { - PANIC("Unable to daemonize"); - } - - setsid(); - - pid = fork(); - if (pid > 0) { - QEMU_EXIT(0); - } else if (pid < 0) { - PANIC("Could not acquire pid file"); - } - - umask(027); - - signal(SIGTSTP, SIG_IGN); - signal(SIGTTOU, SIG_IGN); - signal(SIGTTIN, SIG_IGN); - } - - if (pid_file && qemu_create_pidfile(pid_file) != 0) { - if (daemonize) { - uint8_t status = 1; - int ret; - do { - ret = write(fds[1], &status, 1); - } while (ret < 0 && errno == EINTR); - PANIC("Could not acquire pid file"); - } else { - PANIC("Could not acquire pid file"); - } - } -#endif - #ifdef CONFIG_KQEMU if (smp_cpus > 1) kqemu_allowed = 0; @@ -5396,7 +3892,7 @@ int main(int argc, char **argv, char **envp) if (!boot_devices[0]) { boot_devices = "cad"; } - setvbuf(stdout, NULL, _IOLBF, 0); + os_set_line_buffering(); if (init_timer_alarm() < 0) { PANIC("could not initialize alarm timer"); @@ -5511,10 +4007,8 @@ int main(int argc, char **argv, char **envp) //register_savevm("timer", 0, 2, timer_save, timer_load, &timers_state); register_savevm_live("ram", 0, 3, ram_save_live, NULL, ram_load, NULL); -#ifndef _WIN32 /* must be after terminal init, SDL library changes signal handlers */ - sighandler_setup(); -#endif + os_setup_signal_handling(); /* Maintain compatibility with multiple stdio monitors */ if (!strcmp(monitor_device,"stdio")) { @@ -5626,13 +4120,6 @@ int main(int argc, char **argv, char **envp) #ifdef CONFIG_TRACE if (trace_filename) { trace_init(trace_filename); -#if 0 - // We don't need the dcache code until we can get load and store tracing - // working again. - dcache_init(dcache_size, dcache_ways, dcache_line_size, - dcache_replace_policy, dcache_load_miss_penalty, - dcache_store_miss_penalty); -#endif fprintf(stderr, "-- When done tracing, exit the emulator. --\n"); } #endif @@ -5791,15 +4278,15 @@ int main(int argc, char **argv, char **envp) dcl = ds->listeners; while (dcl != NULL) { if (dcl->dpy_refresh != NULL) { - ds->gui_timer = qemu_new_timer(rt_clock, gui_update, ds); - qemu_mod_timer(ds->gui_timer, qemu_get_clock(rt_clock)); + ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds); + qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock)); } dcl = dcl->next; } if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) { - nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL); - qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock)); + nographic_timer = qemu_new_timer_ms(rt_clock, nographic_update, NULL); + qemu_mod_timer(nographic_timer, qemu_get_clock_ms(rt_clock)); } text_consoles_set_display(ds); @@ -5857,66 +4344,7 @@ int main(int argc, char **argv, char **envp) if (autostart) vm_start(); -#ifndef _WIN32 - if (daemonize) { - uint8_t status = 0; - ssize_t len; - - again1: - len = write(fds[1], &status, 1); - if (len == -1 && (errno == EINTR)) - goto again1; - - if (len != 1) { - PANIC("Unable to daemonize"); - } - - if (chdir("/")) { - perror("not able to chdir to /"); - PANIC("not able to chdir to /"); - } - TFR(fd = open("/dev/null", O_RDWR)); - if (fd == -1) - PANIC("open(\"/dev/null\") failed: %s", errno_str); - } - - if (run_as) { - pwd = getpwnam(run_as); - if (!pwd) { - PANIC("User \"%s\" doesn't exist", run_as); - } - } - - if (chroot_dir) { - if (chroot(chroot_dir) < 0) { - PANIC("chroot failed"); - } - if (chdir("/")) { - perror("not able to chdir to /"); - PANIC("not able to chdir to /"); - } - } - - if (run_as) { - if (setgid(pwd->pw_gid) < 0) { - PANIC("Failed to setgid(%d)", pwd->pw_gid); - } - if (setuid(pwd->pw_uid) < 0) { - PANIC("Failed to setuid(%d)", pwd->pw_uid); - } - if (setuid(0) != -1) { - PANIC("Dropping privileges failed"); - } - } - - if (daemonize) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - - close(fd); - } -#endif + os_setup_post(); #ifdef CONFIG_ANDROID // This will notify the UI that the core is successfuly initialized @@ -34,7 +34,6 @@ #ifndef _WIN32 #include <libgen.h> -#include <pwd.h> #include <sys/times.h> #include <sys/wait.h> #include <termios.h> @@ -112,6 +111,9 @@ #define memalign(align, size) malloc(size) #endif +#include "cpus.h" +#include "arch_init.h" + #ifdef CONFIG_SDL #ifdef __APPLE__ #include <SDL.h> @@ -126,6 +128,7 @@ int main(int argc, char **argv) #endif /* CONFIG_SDL */ #ifdef CONFIG_COCOA +int qemu_main(int argc, char **argv, char **envp); #undef main #define main qemu_main #endif /* CONFIG_COCOA */ @@ -135,7 +138,6 @@ int main(int argc, char **argv) #include "hw/usb.h" #include "hw/pcmcia.h" #include "hw/pc.h" -#include "hw/audiodev.h" #include "hw/isa.h" #include "hw/baum.h" #include "hw/bt.h" @@ -192,10 +194,13 @@ static IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; DriveInfo drives_table[MAX_DRIVES+1]; int nb_drives; enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; -static DisplayState *display_state; DisplayType display_type = DT_DEFAULT; const char* keyboard_layout = NULL; ram_addr_t ram_size; +const char *mem_path = NULL; +#ifdef MAP_POPULATE +int mem_prealloc = 0; /* force preallocation of physical target memory */ +#endif int nb_nics; NICInfo nd_table[MAX_NICS]; int vm_running; @@ -207,15 +212,6 @@ int std_vga_enabled = 0; int vmsvga_enabled = 0; int xenfb_enabled = 0; QEMUClock *rtc_clock; -#ifdef TARGET_SPARC -int graphic_width = 1024; -int graphic_height = 768; -int graphic_depth = 8; -#else -int graphic_width = 800; -int graphic_height = 600; -int graphic_depth = 15; -#endif static int full_screen = 0; #ifdef CONFIG_SDL static int no_frame = 0; @@ -240,9 +236,6 @@ int no_reboot = 0; int no_shutdown = 0; int cursor_hide = 1; int graphic_rotate = 0; -#ifndef _WIN32 -int daemonize = 0; -#endif WatchdogTimerModel *watchdog = NULL; int watchdog_action = WDT_RESET; const char *option_rom[MAX_OPTION_ROMS]; @@ -264,9 +257,6 @@ int nb_numa_nodes; uint64_t node_mem[MAX_NODES]; uint64_t node_cpumask[MAX_NODES]; -static CPUState *cur_cpu; -static CPUState *next_cpu; -static int timer_alarm_pending = 1; static QEMUTimer *nographic_timer; uint8_t qemu_uuid[16]; @@ -354,28 +344,6 @@ static void default_ioport_writel(void *opaque, uint32_t address, uint32_t data) } -/***********************************************************/ -void hw_error(const char *fmt, ...) -{ - va_list ap; - CPUState *env; - - va_start(ap, fmt); - fprintf(stderr, "qemu: hardware error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - for(env = first_cpu; env != NULL; env = env->next_cpu) { - fprintf(stderr, "CPU #%d:\n", env->cpu_index); -#ifdef TARGET_I386 - cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); -#else - cpu_dump_state(env, stderr, fprintf, 0); -#endif - } - va_end(ap); - abort(); -} - /***************/ /* ballooning */ @@ -402,33 +370,6 @@ ram_addr_t qemu_balloon_status(void) } /***********************************************************/ -/* real time host monotonic timer */ - -/* compute with 96 bit intermediate result: (a*b)/c */ -uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) -{ - union { - uint64_t ll; - struct { -#ifdef HOST_WORDS_BIGENDIAN - uint32_t high, low; -#else - uint32_t low, high; -#endif - } l; - } u, res; - uint64_t rl, rh; - - u.ll = a; - rl = (uint64_t)u.l.low * (uint64_t)b; - rh = (uint64_t)u.l.high * (uint64_t)b; - rh += (rl >> 32); - res.l.high = rh / c; - res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; - return res.ll; -} - -/***********************************************************/ /* host time/date access */ void qemu_get_timedate(struct tm *tm, int offset) { @@ -1550,507 +1491,6 @@ void pcmcia_info(Monitor *mon) } /***********************************************************/ -/* register display */ - -struct DisplayAllocator default_allocator = { - defaultallocator_create_displaysurface, - defaultallocator_resize_displaysurface, - defaultallocator_free_displaysurface -}; - -void register_displaystate(DisplayState *ds) -{ - DisplayState **s; - s = &display_state; - while (*s != NULL) - s = &(*s)->next; - ds->next = NULL; - *s = ds; -} - -DisplayState *get_displaystate(void) -{ - return display_state; -} - -DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da) -{ - if(ds->allocator == &default_allocator) ds->allocator = da; - return ds->allocator; -} - -/* dumb display */ - -static void dumb_display_init(void) -{ - DisplayState *ds = qemu_mallocz(sizeof(DisplayState)); - ds->allocator = &default_allocator; - ds->surface = qemu_create_displaysurface(ds, 640, 480); - register_displaystate(ds); -} - -/***********************************************************/ -/* I/O handling */ - -typedef struct IOHandlerRecord { - int fd; - IOCanReadHandler *fd_read_poll; - IOHandler *fd_read; - IOHandler *fd_write; - int deleted; - void *opaque; - /* temporary data */ - struct pollfd *ufd; - QLIST_ENTRY(IOHandlerRecord) next; -} IOHandlerRecord; - -static QLIST_HEAD(, IOHandlerRecord) io_handlers = - QLIST_HEAD_INITIALIZER(io_handlers); - - -/* XXX: fd_read_poll should be suppressed, but an API change is - necessary in the character devices to suppress fd_can_read(). */ -int qemu_set_fd_handler2(int fd, - IOCanReadHandler *fd_read_poll, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque) -{ - IOHandlerRecord *ioh; - - if (!fd_read && !fd_write) { - QLIST_FOREACH(ioh, &io_handlers, next) { - if (ioh->fd == fd) { - ioh->deleted = 1; - break; - } - } - } else { - QLIST_FOREACH(ioh, &io_handlers, next) { - if (ioh->fd == fd) - goto found; - } - ioh = qemu_mallocz(sizeof(IOHandlerRecord)); - QLIST_INSERT_HEAD(&io_handlers, ioh, next); - found: - ioh->fd = fd; - ioh->fd_read_poll = fd_read_poll; - ioh->fd_read = fd_read; - ioh->fd_write = fd_write; - ioh->opaque = opaque; - ioh->deleted = 0; - } - return 0; -} - -int qemu_set_fd_handler(int fd, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque) -{ - return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); -} - -#ifdef _WIN32 -/***********************************************************/ -/* Polling handling */ - -typedef struct PollingEntry { - PollingFunc *func; - void *opaque; - struct PollingEntry *next; -} PollingEntry; - -static PollingEntry *first_polling_entry; - -int qemu_add_polling_cb(PollingFunc *func, void *opaque) -{ - PollingEntry **ppe, *pe; - pe = qemu_mallocz(sizeof(PollingEntry)); - pe->func = func; - pe->opaque = opaque; - for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next); - *ppe = pe; - return 0; -} - -void qemu_del_polling_cb(PollingFunc *func, void *opaque) -{ - PollingEntry **ppe, *pe; - for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) { - pe = *ppe; - if (pe->func == func && pe->opaque == opaque) { - *ppe = pe->next; - qemu_free(pe); - break; - } - } -} - -/***********************************************************/ -/* Wait objects support */ -typedef struct WaitObjects { - int num; - HANDLE events[MAXIMUM_WAIT_OBJECTS + 1]; - WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1]; - void *opaque[MAXIMUM_WAIT_OBJECTS + 1]; -} WaitObjects; - -static WaitObjects wait_objects = {0}; - -int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) -{ - WaitObjects *w = &wait_objects; - - if (w->num >= MAXIMUM_WAIT_OBJECTS) - return -1; - w->events[w->num] = handle; - w->func[w->num] = func; - w->opaque[w->num] = opaque; - w->num++; - return 0; -} - -void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) -{ - int i, found; - WaitObjects *w = &wait_objects; - - found = 0; - for (i = 0; i < w->num; i++) { - if (w->events[i] == handle) - found = 1; - if (found) { - w->events[i] = w->events[i + 1]; - w->func[i] = w->func[i + 1]; - w->opaque[i] = w->opaque[i + 1]; - } - } - if (found) - w->num--; -} -#endif - -/***********************************************************/ -/* ram save/restore */ - -static int ram_get_page(QEMUFile *f, uint8_t *buf, int len) -{ - int v; - - v = qemu_get_byte(f); - switch(v) { - case 0: - if (qemu_get_buffer(f, buf, len) != len) - return -EIO; - break; - case 1: - v = qemu_get_byte(f); - memset(buf, v, len); - break; - default: - return -EINVAL; - } - - if (qemu_file_has_error(f)) - return -EIO; - - return 0; -} - -static int ram_load_v1(QEMUFile *f, void *opaque) -{ - int ret; - ram_addr_t i; - - if (qemu_get_be32(f) != last_ram_offset) - return -EINVAL; - for(i = 0; i < last_ram_offset; i+= TARGET_PAGE_SIZE) { - ret = ram_get_page(f, qemu_get_ram_ptr(i), TARGET_PAGE_SIZE); - if (ret) - return ret; - } - return 0; -} - -#define BDRV_HASH_BLOCK_SIZE 1024 -#define IOBUF_SIZE 4096 -#define RAM_CBLOCK_MAGIC 0xfabe - -typedef struct RamDecompressState { - z_stream zstream; - QEMUFile *f; - uint8_t buf[IOBUF_SIZE]; -} RamDecompressState; - -static int ram_decompress_open(RamDecompressState *s, QEMUFile *f) -{ - int ret; - memset(s, 0, sizeof(*s)); - s->f = f; - ret = inflateInit(&s->zstream); - if (ret != Z_OK) - return -1; - return 0; -} - -static int ram_decompress_buf(RamDecompressState *s, uint8_t *buf, int len) -{ - int ret, clen; - - s->zstream.avail_out = len; - s->zstream.next_out = buf; - while (s->zstream.avail_out > 0) { - if (s->zstream.avail_in == 0) { - if (qemu_get_be16(s->f) != RAM_CBLOCK_MAGIC) - return -1; - clen = qemu_get_be16(s->f); - if (clen > IOBUF_SIZE) - return -1; - qemu_get_buffer(s->f, s->buf, clen); - s->zstream.avail_in = clen; - s->zstream.next_in = s->buf; - } - ret = inflate(&s->zstream, Z_PARTIAL_FLUSH); - if (ret != Z_OK && ret != Z_STREAM_END) { - return -1; - } - } - return 0; -} - -static void ram_decompress_close(RamDecompressState *s) -{ - inflateEnd(&s->zstream); -} - -#define RAM_SAVE_FLAG_FULL 0x01 -#define RAM_SAVE_FLAG_COMPRESS 0x02 -#define RAM_SAVE_FLAG_MEM_SIZE 0x04 -#define RAM_SAVE_FLAG_PAGE 0x08 -#define RAM_SAVE_FLAG_EOS 0x10 - -static int is_dup_page(uint8_t *page, uint8_t ch) -{ - uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch; - uint32_t *array = (uint32_t *)page; - int i; - - for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) { - if (array[i] != val) - return 0; - } - - return 1; -} - -static int ram_save_block(QEMUFile *f) -{ - static ram_addr_t current_addr = 0; - ram_addr_t saved_addr = current_addr; - ram_addr_t addr = 0; - int found = 0; - - while (addr < last_ram_offset) { - if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) { - uint8_t *p; - - cpu_physical_memory_reset_dirty(current_addr, - current_addr + TARGET_PAGE_SIZE, - MIGRATION_DIRTY_FLAG); - - p = qemu_get_ram_ptr(current_addr); - - if (is_dup_page(p, *p)) { - qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_COMPRESS); - qemu_put_byte(f, *p); - } else { - qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_PAGE); - qemu_put_buffer(f, p, TARGET_PAGE_SIZE); - } - - found = 1; - break; - } - addr += TARGET_PAGE_SIZE; - current_addr = (saved_addr + addr) % last_ram_offset; - } - - return found; -} - -static uint64_t bytes_transferred; - -static ram_addr_t ram_save_remaining(void) -{ - ram_addr_t addr; - ram_addr_t count = 0; - - for (addr = 0; addr < last_ram_offset; addr += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) - count++; - } - - return count; -} - -uint64_t ram_bytes_remaining(void) -{ - return ram_save_remaining() * TARGET_PAGE_SIZE; -} - -uint64_t ram_bytes_transferred(void) -{ - return bytes_transferred; -} - -uint64_t ram_bytes_total(void) -{ - return last_ram_offset; -} - -static int ram_save_live(QEMUFile *f, int stage, void *opaque) -{ - ram_addr_t addr; - uint64_t bytes_transferred_last; - double bwidth = 0; - uint64_t expected_time = 0; - - if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) { - qemu_file_set_error(f); - return 0; - } - - if (stage == 1) { - bytes_transferred = 0; - - /* Make sure all dirty bits are set */ - for (addr = 0; addr < last_ram_offset; addr += TARGET_PAGE_SIZE) { - if (!cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) - cpu_physical_memory_set_dirty(addr); - } - - /* Enable dirty memory tracking */ - cpu_physical_memory_set_dirty_tracking(1); - - qemu_put_be64(f, last_ram_offset | RAM_SAVE_FLAG_MEM_SIZE); - } - - bytes_transferred_last = bytes_transferred; - bwidth = qemu_get_clock_ns(rt_clock); - - while (!qemu_file_rate_limit(f)) { - int ret; - - ret = ram_save_block(f); - bytes_transferred += ret * TARGET_PAGE_SIZE; - if (ret == 0) /* no more blocks */ - break; - } - - bwidth = qemu_get_clock_ns(rt_clock) - bwidth; - bwidth = (bytes_transferred - bytes_transferred_last) / bwidth; - - /* if we haven't transferred anything this round, force expected_time to a - * a very high value, but without crashing */ - if (bwidth == 0) - bwidth = 0.000001; - - /* try transferring iterative blocks of memory */ - if (stage == 3) { - /* flush all remaining blocks regardless of rate limiting */ - while (ram_save_block(f) != 0) { - bytes_transferred += TARGET_PAGE_SIZE; - } - cpu_physical_memory_set_dirty_tracking(0); - } - - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - - expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth; - - return (stage == 2) && (expected_time <= migrate_max_downtime()); -} - -static int ram_load_dead(QEMUFile *f, void *opaque) -{ - RamDecompressState s1, *s = &s1; - uint8_t buf[10]; - ram_addr_t i; - - if (ram_decompress_open(s, f) < 0) - return -EINVAL; - for(i = 0; i < last_ram_offset; i+= BDRV_HASH_BLOCK_SIZE) { - if (ram_decompress_buf(s, buf, 1) < 0) { - fprintf(stderr, "Error while reading ram block header\n"); - goto error; - } - if (buf[0] == 0) { - if (ram_decompress_buf(s, qemu_get_ram_ptr(i), - BDRV_HASH_BLOCK_SIZE) < 0) { - fprintf(stderr, "Error while reading ram block address=0x%08" PRIx64, (uint64_t)i); - goto error; - } - } else { - error: - printf("Error block header\n"); - return -EINVAL; - } - } - ram_decompress_close(s); - - return 0; -} - -static int ram_load(QEMUFile *f, void *opaque, int version_id) -{ - ram_addr_t addr; - int flags; - - if (version_id == 1) - return ram_load_v1(f, opaque); - - if (version_id == 2) { - if (qemu_get_be32(f) != last_ram_offset) - return -EINVAL; - return ram_load_dead(f, opaque); - } - - if (version_id != 3) - return -EINVAL; - - do { - addr = qemu_get_be64(f); - - flags = addr & ~TARGET_PAGE_MASK; - addr &= TARGET_PAGE_MASK; - - if (flags & RAM_SAVE_FLAG_MEM_SIZE) { - if (addr != last_ram_offset) - return -EINVAL; - } - - if (flags & RAM_SAVE_FLAG_FULL) { - if (ram_load_dead(f, opaque) < 0) - return -EINVAL; - } - - if (flags & RAM_SAVE_FLAG_COMPRESS) { - uint8_t ch = qemu_get_byte(f); - memset(qemu_get_ram_ptr(addr), ch, TARGET_PAGE_SIZE); - } else if (flags & RAM_SAVE_FLAG_PAGE) - qemu_get_buffer(f, qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE); - } while (!(flags & RAM_SAVE_FLAG_EOS)); - - return 0; -} - -void qemu_service_io(void) -{ - qemu_notify_event(); -} - -/***********************************************************/ /* machine registration */ static QEMUMachine *first_machine = NULL; @@ -2107,14 +1547,14 @@ static void gui_update(void *opaque) interval = dcl->gui_timer_interval; dcl = dcl->next; } - qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock(rt_clock)); + qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock)); } static void nographic_update(void *opaque) { uint64_t interval = GUI_REFRESH_INTERVAL; - qemu_mod_timer(nographic_timer, interval + qemu_get_clock(rt_clock)); + qemu_mod_timer(nographic_timer, interval + qemu_get_clock_ms(rt_clock)); } struct vm_change_state_entry { @@ -2144,7 +1584,7 @@ void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) qemu_free (e); } -static void vm_state_notify(int running, int reason) +void vm_state_notify(int running, int reason) { VMChangeStateEntry *e; @@ -2153,9 +1593,6 @@ static void vm_state_notify(int running, int reason) } } -static void resume_all_vcpus(void); -static void pause_all_vcpus(void); - void vm_start(void) { if (!vm_running) { @@ -2178,9 +1615,10 @@ typedef struct QEMUResetEntry { static QTAILQ_HEAD(reset_handlers, QEMUResetEntry) reset_handlers = QTAILQ_HEAD_INITIALIZER(reset_handlers); static int reset_requested; -static int shutdown_requested; +static int shutdown_requested, shutdown_signal = -1; +static pid_t shutdown_pid; static int powerdown_requested; -static int debug_requested; +int debug_requested; static int vmstop_requested; int qemu_shutdown_requested(void) @@ -2218,38 +1656,6 @@ static int qemu_vmstop_requested(void) return r; } -void qemu_register_reset(QEMUResetHandler *func, void *opaque) -{ - QEMUResetEntry *re = qemu_mallocz(sizeof(QEMUResetEntry)); - - re->func = func; - re->opaque = opaque; - QTAILQ_INSERT_TAIL(&reset_handlers, re, entry); -} - -void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) -{ - QEMUResetEntry *re; - - QTAILQ_FOREACH(re, &reset_handlers, entry) { - if (re->func == func && re->opaque == opaque) { - QTAILQ_REMOVE(&reset_handlers, re, entry); - qemu_free(re); - return; - } - } -} - -static void do_vm_stop(int reason) -{ - if (vm_running) { - cpu_disable_ticks(); - vm_running = 0; - pause_all_vcpus(); - vm_state_notify(0, reason); - } -} - void qemu_register_reset(QEMUResetHandler *func, int order, void *opaque) { QEMUResetEntry **pre, *re; @@ -2286,6 +1692,13 @@ void qemu_system_reset_request(void) qemu_notify_event(); } +void qemu_system_killed(int signal, pid_t pid) +{ + shutdown_signal = signal; + shutdown_pid = pid; + qemu_system_shutdown_request(); +} + void qemu_system_shutdown_request(void) { shutdown_requested = 1; @@ -2306,572 +1719,36 @@ static void qemu_system_vmstop_request(int reason) } #endif -#ifndef _WIN32 -static int io_thread_fd = -1; - -static void qemu_event_increment(void) -{ - static const char byte = 0; - - if (io_thread_fd == -1) - return; - - write(io_thread_fd, &byte, sizeof(byte)); -} - -static void qemu_event_read(void *opaque) -{ - int fd = (unsigned long)opaque; - ssize_t len; - - /* Drain the notify pipe */ - do { - char buffer[512]; - len = read(fd, buffer, sizeof(buffer)); - } while ((len == -1 && errno == EINTR) || len > 0); -} - -static int qemu_event_init(void) -{ - int err; - int fds[2]; - - err = pipe(fds); - if (err == -1) - return -errno; - - err = fcntl_setfl(fds[0], O_NONBLOCK); - if (err < 0) - goto fail; - - err = fcntl_setfl(fds[1], O_NONBLOCK); - if (err < 0) - goto fail; - - qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL, - (void *)(unsigned long)fds[0]); - - io_thread_fd = fds[1]; - return 0; - -fail: - close(fds[0]); - close(fds[1]); - return err; -} -#else -HANDLE qemu_event_handle; - -static void dummy_event_handler(void *opaque) -{ -} - -static int qemu_event_init(void) -{ - qemu_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!qemu_event_handle) { - perror("Failed CreateEvent"); - return -1; - } - qemu_add_wait_object(qemu_event_handle, dummy_event_handler, NULL); - return 0; -} - -static void qemu_event_increment(void) -{ - SetEvent(qemu_event_handle); -} -#endif - -static int cpu_can_run(CPUState *env) -{ - if (env->stop) - return 0; - if (env->stopped) - return 0; - if (!vm_running) - return 0; - return 1; -} - -#ifndef CONFIG_IOTHREAD -static int qemu_init_main_loop(void) -{ - return qemu_event_init(); -} - -void qemu_init_vcpu(void *_env) -{ - CPUState *env = _env; - - if (kvm_enabled()) - kvm_init_vcpu(env); - return; -} - -int qemu_cpu_self(void *env) -{ - return 1; -} - -static void resume_all_vcpus(void) -{ -} - -static void pause_all_vcpus(void) -{ -} - -void qemu_cpu_kick(void *env) -{ - return; -} - -void qemu_notify_event(void) -{ - CPUState *env = cpu_single_env; - - if (env) { - cpu_exit(env); - } -} - -#define qemu_mutex_lock_iothread() do { } while (0) -#define qemu_mutex_unlock_iothread() do { } while (0) - -void vm_stop(int reason) -{ - do_vm_stop(reason); -} - -#else /* CONFIG_IOTHREAD */ - -#include "qemu-thread.h" - -QemuMutex qemu_global_mutex; -static QemuMutex qemu_fair_mutex; - -static QemuThread io_thread; - -static QemuThread *tcg_cpu_thread; -static QemuCond *tcg_halt_cond; - -static int qemu_system_ready; -/* cpu creation */ -static QemuCond qemu_cpu_cond; -/* system init */ -static QemuCond qemu_system_cond; -static QemuCond qemu_pause_cond; - -static void block_io_signals(void); -static void unblock_io_signals(void); -static int tcg_has_work(void); - -static int qemu_init_main_loop(void) -{ - int ret; - - ret = qemu_event_init(); - if (ret) - return ret; - - qemu_cond_init(&qemu_pause_cond); - qemu_mutex_init(&qemu_fair_mutex); - qemu_mutex_init(&qemu_global_mutex); - qemu_mutex_lock(&qemu_global_mutex); - - unblock_io_signals(); - qemu_thread_self(&io_thread); - - return 0; -} - -static void qemu_wait_io_event(CPUState *env) -{ - while (!tcg_has_work()) - qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); - - qemu_mutex_unlock(&qemu_global_mutex); - - /* - * Users of qemu_global_mutex can be starved, having no chance - * to acquire it since this path will get to it first. - * So use another lock to provide fairness. - */ - qemu_mutex_lock(&qemu_fair_mutex); - qemu_mutex_unlock(&qemu_fair_mutex); - - qemu_mutex_lock(&qemu_global_mutex); - if (env->stop) { - env->stop = 0; - env->stopped = 1; - qemu_cond_signal(&qemu_pause_cond); - } -} - -static int qemu_cpu_exec(CPUState *env); - -static void *kvm_cpu_thread_fn(void *arg) -{ - CPUState *env = arg; - - block_io_signals(); - qemu_thread_self(env->thread); - - /* signal CPU creation */ - qemu_mutex_lock(&qemu_global_mutex); - env->created = 1; - qemu_cond_signal(&qemu_cpu_cond); - - /* and wait for machine initialization */ - while (!qemu_system_ready) - qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); - - while (1) { - if (cpu_can_run(env)) - qemu_cpu_exec(env); - qemu_wait_io_event(env); - } - - return NULL; -} - -static void tcg_cpu_exec(void); - -static void *tcg_cpu_thread_fn(void *arg) -{ - CPUState *env = arg; - - block_io_signals(); - qemu_thread_self(env->thread); - - /* signal CPU creation */ - qemu_mutex_lock(&qemu_global_mutex); - for (env = first_cpu; env != NULL; env = env->next_cpu) - env->created = 1; - qemu_cond_signal(&qemu_cpu_cond); - - /* and wait for machine initialization */ - while (!qemu_system_ready) - qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); - - while (1) { - tcg_cpu_exec(); - qemu_wait_io_event(cur_cpu); - } - - return NULL; -} - -void qemu_cpu_kick(void *_env) -{ - CPUState *env = _env; - qemu_cond_broadcast(env->halt_cond); - if (kvm_enabled()) - qemu_thread_signal(env->thread, SIGUSR1); -} - -int qemu_cpu_self(void *env) -{ - return (cpu_single_env != NULL); -} - -static void cpu_signal(int sig) -{ - if (cpu_single_env) - cpu_exit(cpu_single_env); -} - -static void block_io_signals(void) -{ - sigset_t set; - struct sigaction sigact; - - sigemptyset(&set); - sigaddset(&set, SIGUSR2); - sigaddset(&set, SIGIO); - sigaddset(&set, SIGALRM); - pthread_sigmask(SIG_BLOCK, &set, NULL); - - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); - - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = cpu_signal; - sigaction(SIGUSR1, &sigact, NULL); -} - -static void unblock_io_signals(void) -{ - sigset_t set; - - sigemptyset(&set); - sigaddset(&set, SIGUSR2); - sigaddset(&set, SIGIO); - sigaddset(&set, SIGALRM); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); - - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_BLOCK, &set, NULL); -} - -static void qemu_signal_lock(unsigned int msecs) -{ - qemu_mutex_lock(&qemu_fair_mutex); - - while (qemu_mutex_trylock(&qemu_global_mutex)) { - qemu_thread_signal(tcg_cpu_thread, SIGUSR1); - if (!qemu_mutex_timedlock(&qemu_global_mutex, msecs)) - break; - } - qemu_mutex_unlock(&qemu_fair_mutex); -} - -void qemu_mutex_lock_iothread(void) -{ - if (kvm_enabled()) { - qemu_mutex_lock(&qemu_fair_mutex); - qemu_mutex_lock(&qemu_global_mutex); - qemu_mutex_unlock(&qemu_fair_mutex); - } else - qemu_signal_lock(100); -} - -void qemu_mutex_unlock_iothread(void) -{ - qemu_mutex_unlock(&qemu_global_mutex); -} - -static int all_vcpus_paused(void) -{ - CPUState *penv = first_cpu; - - while (penv) { - if (!penv->stopped) - return 0; - penv = (CPUState *)penv->next_cpu; - } - - return 1; -} - -static void pause_all_vcpus(void) -{ - CPUState *penv = first_cpu; - - while (penv) { - penv->stop = 1; - qemu_thread_signal(penv->thread, SIGUSR1); - qemu_cpu_kick(penv); - penv = (CPUState *)penv->next_cpu; - } - - while (!all_vcpus_paused()) { - qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100); - penv = first_cpu; - while (penv) { - qemu_thread_signal(penv->thread, SIGUSR1); - penv = (CPUState *)penv->next_cpu; - } - } -} - -static void resume_all_vcpus(void) -{ - CPUState *penv = first_cpu; - - while (penv) { - penv->stop = 0; - penv->stopped = 0; - qemu_thread_signal(penv->thread, SIGUSR1); - qemu_cpu_kick(penv); - penv = (CPUState *)penv->next_cpu; - } -} - -static void tcg_init_vcpu(void *_env) -{ - CPUState *env = _env; - /* share a single thread for all cpus with TCG */ - if (!tcg_cpu_thread) { - env->thread = qemu_mallocz(sizeof(QemuThread)); - env->halt_cond = qemu_mallocz(sizeof(QemuCond)); - qemu_cond_init(env->halt_cond); - qemu_thread_create(env->thread, tcg_cpu_thread_fn, env); - while (env->created == 0) - qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); - tcg_cpu_thread = env->thread; - tcg_halt_cond = env->halt_cond; - } else { - env->thread = tcg_cpu_thread; - env->halt_cond = tcg_halt_cond; - } -} - -static void kvm_start_vcpu(CPUState *env) -{ - kvm_init_vcpu(env); - env->thread = qemu_mallocz(sizeof(QemuThread)); - env->halt_cond = qemu_mallocz(sizeof(QemuCond)); - qemu_cond_init(env->halt_cond); - qemu_thread_create(env->thread, kvm_cpu_thread_fn, env); - while (env->created == 0) - qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); -} - -void qemu_init_vcpu(void *_env) -{ - CPUState *env = _env; - - if (kvm_enabled()) - kvm_start_vcpu(env); - else - tcg_init_vcpu(env); -} - -void qemu_notify_event(void) -{ - qemu_event_increment(); -} - -void vm_stop(int reason) -{ - QemuThread me; - qemu_thread_self(&me); - - if (!qemu_thread_equal(&me, &io_thread)) { - qemu_system_vmstop_request(reason); - /* - * FIXME: should not return to device code in case - * vm_stop() has been requested. - */ - if (cpu_single_env) { - cpu_exit(cpu_single_env); - cpu_single_env->stop = 1; - } - return; - } - do_vm_stop(reason); -} - -#endif - - -#ifdef _WIN32 -static void host_main_loop_wait(int *timeout) -{ - int ret, ret2, i; - PollingEntry *pe; - - - /* XXX: need to suppress polling by better using win32 events */ - ret = 0; - for(pe = first_polling_entry; pe != NULL; pe = pe->next) { - ret |= pe->func(pe->opaque); - } - if (ret == 0) { - int err; - WaitObjects *w = &wait_objects; - - ret = WaitForMultipleObjects(w->num, w->events, FALSE, *timeout); - if (WAIT_OBJECT_0 + 0 <= ret && ret <= WAIT_OBJECT_0 + w->num - 1) { - if (w->func[ret - WAIT_OBJECT_0]) - w->func[ret - WAIT_OBJECT_0](w->opaque[ret - WAIT_OBJECT_0]); - - /* Check for additional signaled events */ - for(i = (ret - WAIT_OBJECT_0 + 1); i < w->num; i++) { - - /* Check if event is signaled */ - ret2 = WaitForSingleObject(w->events[i], 0); - if(ret2 == WAIT_OBJECT_0) { - if (w->func[i]) - w->func[i](w->opaque[i]); - } else if (ret2 == WAIT_TIMEOUT) { - } else { - err = GetLastError(); - fprintf(stderr, "WaitForSingleObject error %d %d\n", i, err); - } - } - } else if (ret == WAIT_TIMEOUT) { - } else { - err = GetLastError(); - fprintf(stderr, "WaitForMultipleObjects error %d %d\n", ret, err); - } - } - - *timeout = 0; -} -#else -static void host_main_loop_wait(int *timeout) -{ -} -#endif - void main_loop_wait(int timeout) { - IOHandlerRecord *ioh; fd_set rfds, wfds, xfds; int ret, nfds; struct timeval tv; qemu_bh_update_timeout(&timeout); - host_main_loop_wait(&timeout); + os_host_main_loop_wait(&timeout); + + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; /* poll any events */ + /* XXX: separate device handlers from system ones */ nfds = -1; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&xfds); - QLIST_FOREACH(ioh, &io_handlers, next) { - if (ioh->deleted) - continue; - if (ioh->fd_read && - (!ioh->fd_read_poll || - ioh->fd_read_poll(ioh->opaque) != 0)) { - FD_SET(ioh->fd, &rfds); - if (ioh->fd > nfds) - nfds = ioh->fd; - } - if (ioh->fd_write) { - FD_SET(ioh->fd, &wfds); - if (ioh->fd > nfds) - nfds = ioh->fd; - } - } - - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - -#if defined(CONFIG_SLIRP) + qemu_iohandler_fill(&nfds, &rfds, &wfds, &xfds); if (slirp_is_inited()) { slirp_select_fill(&nfds, &rfds, &wfds, &xfds); } -#endif + qemu_mutex_unlock_iothread(); ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); qemu_mutex_lock_iothread(); - if (ret > 0) { - IOHandlerRecord *pioh; - - QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { - if (ioh->deleted) { - QLIST_REMOVE(ioh, next); - qemu_free(ioh); - continue; - } - if (ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) { - ioh->fd_read(ioh->opaque); - } - if (ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) { - ioh->fd_write(ioh->opaque); - } - } - -#if defined(CONFIG_SLIRP) + qemu_iohandler_poll(&rfds, &wfds, &xfds, ret); if (slirp_is_inited()) { if (ret < 0) { FD_ZERO(&rfds); @@ -2880,7 +1757,6 @@ void main_loop_wait(int timeout) } slirp_select_poll(&rfds, &wfds, &xfds); } -#endif qemu_run_all_timers(); @@ -2890,97 +1766,6 @@ void main_loop_wait(int timeout) } -static int qemu_cpu_exec(CPUState *env) -{ - int ret; -#ifdef CONFIG_PROFILER - int64_t ti; -#endif - -#ifdef CONFIG_PROFILER - ti = profile_getclock(); -#endif - if (use_icount) { - int64_t count; - int decr; - qemu_icount -= (env->icount_decr.u16.low + env->icount_extra); - env->icount_decr.u16.low = 0; - env->icount_extra = 0; - count = qemu_next_deadline(); - count = (count + (1 << icount_time_shift) - 1) - >> icount_time_shift; - qemu_icount += count; - decr = (count > 0xffff) ? 0xffff : count; - count -= decr; - env->icount_decr.u16.low = decr; - env->icount_extra = count; - } - ret = cpu_exec(env); -#ifdef CONFIG_PROFILER - qemu_time += profile_getclock() - ti; -#endif - if (use_icount) { - /* Fold pending instructions back into the - instruction counter, and clear the interrupt flag. */ - qemu_icount -= (env->icount_decr.u16.low - + env->icount_extra); - env->icount_decr.u32 = 0; - env->icount_extra = 0; - } - return ret; -} - -static void tcg_cpu_exec(void) -{ - int ret = 0; - - if (next_cpu == NULL) - next_cpu = first_cpu; - for (; next_cpu != NULL; next_cpu = next_cpu->next_cpu) { - CPUState *env = cur_cpu = next_cpu; - - if (timer_alarm_pending) { - timer_alarm_pending = 0; - break; - } - if (cpu_can_run(env)) - ret = qemu_cpu_exec(env); - else if (env->stop) - break; - - if (ret == EXCP_DEBUG) { - gdb_set_stop_cpu(env); - debug_requested = 1; - break; - } - } -} - -#if 0 -static int cpu_has_work(CPUState *env) -{ - if (env->stop) - return 1; - if (env->stopped) - return 0; - if (!env->halted) - return 1; - if (qemu_cpu_has_work(env)) - return 1; - return 0; -} - -static int tcg_has_work(void) -{ - CPUState *env; - - for (env = first_cpu; env != NULL; env = env->next_cpu) - if (cpu_has_work(env)) - return 1; - return 0; -} -#endif - static int vm_can_run(void) { if (powerdown_requested) @@ -3057,7 +1842,7 @@ static void help(int exitcode) #define DEF(option, opt_arg, opt_enum, opt_help) \ opt_help #define DEFHEADING(text) stringify(text) "\n" -#include "qemu-options.h" +#include "qemu-options.def" #undef DEF #undef DEFHEADING #undef GEN_DOCS @@ -3086,7 +1871,7 @@ enum { #define DEF(option, opt_arg, opt_enum, opt_help) \ opt_enum, #define DEFHEADING(text) -#include "qemu-options.h" +#include "qemu-options.def" #undef DEF #undef DEFHEADING #undef GEN_DOCS @@ -3103,154 +1888,13 @@ static const QEMUOption qemu_options[] = { #define DEF(option, opt_arg, opt_enum, opt_help) \ { option, opt_arg, opt_enum }, #define DEFHEADING(text) -#include "qemu-options.h" +#include "qemu-options.def" #undef DEF #undef DEFHEADING #undef GEN_DOCS - { NULL }, + { NULL, 0, 0 }, }; -#ifdef HAS_AUDIO -struct soundhw soundhw[] = { -#ifdef HAS_AUDIO_CHOICE -#if defined(TARGET_I386) || defined(TARGET_MIPS) - { - "pcspk", - "PC speaker", - 0, - 1, - { .init_isa = pcspk_audio_init } - }, -#endif - -#ifdef CONFIG_SB16 - { - "sb16", - "Creative Sound Blaster 16", - 0, - 1, - { .init_isa = SB16_init } - }, -#endif - -#ifdef CONFIG_CS4231A - { - "cs4231a", - "CS4231A", - 0, - 1, - { .init_isa = cs4231a_init } - }, -#endif - -#ifdef CONFIG_ADLIB - { - "adlib", -#ifdef HAS_YMF262 - "Yamaha YMF262 (OPL3)", -#else - "Yamaha YM3812 (OPL2)", -#endif - 0, - 1, - { .init_isa = Adlib_init } - }, -#endif - -#ifdef CONFIG_GUS - { - "gus", - "Gravis Ultrasound GF1", - 0, - 1, - { .init_isa = GUS_init } - }, -#endif - -#ifdef CONFIG_AC97 - { - "ac97", - "Intel 82801AA AC97 Audio", - 0, - 0, - { .init_pci = ac97_init } - }, -#endif - -#ifdef CONFIG_ES1370 - { - "es1370", - "ENSONIQ AudioPCI ES1370", - 0, - 0, - { .init_pci = es1370_init } - }, -#endif - -#endif /* HAS_AUDIO_CHOICE */ - - { NULL, NULL, 0, 0, { NULL } } -}; - -static void select_soundhw (const char *optarg) -{ - struct soundhw *c; - - if (*optarg == '?') { - show_valid_cards: - - printf ("Valid sound card names (comma separated):\n"); - for (c = soundhw; c->name; ++c) { - printf ("%-11s %s\n", c->name, c->descr); - } - printf ("\n-soundhw all will enable all of the above\n"); - exit (*optarg != '?'); - } - else { - size_t l; - const char *p; - char *e; - int bad_card = 0; - - if (!strcmp (optarg, "all")) { - for (c = soundhw; c->name; ++c) { - c->enabled = 1; - } - return; - } - - p = optarg; - while (*p) { - e = strchr (p, ','); - l = !e ? strlen (p) : (size_t) (e - p); - - for (c = soundhw; c->name; ++c) { - if (!strncmp (c->name, p, l)) { - c->enabled = 1; - break; - } - } - - if (!c->name) { - if (l > 80) { - fprintf (stderr, - "Unknown sound card name (too big to show)\n"); - } - else { - fprintf (stderr, "Unknown sound card name `%.*s'\n", - (int) l, p); - } - bad_card = 1; - } - p += l + (e != NULL); - } - - if (bad_card) - goto show_valid_cards; - } -} -#endif - static void select_vgahw (const char *p) { const char *opts; @@ -3287,66 +1931,8 @@ static void select_vgahw (const char *p) } } -#ifdef _WIN32 -static BOOL WINAPI qemu_ctrl_handler(DWORD type) -{ - exit(STATUS_CONTROL_C_EXIT); - return TRUE; -} -#endif - -int qemu_uuid_parse(const char *str, uint8_t *uuid) -{ - int ret; - - if(strlen(str) != 36) - return -1; - - ret = sscanf(str, UUID_FMT, &uuid[0], &uuid[1], &uuid[2], &uuid[3], - &uuid[4], &uuid[5], &uuid[6], &uuid[7], &uuid[8], &uuid[9], - &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14], &uuid[15]); - - if(ret != 16) - return -1; - -#ifdef TARGET_I386 - smbios_add_field(1, offsetof(struct smbios_type_1, uuid), 16, uuid); -#endif - - return 0; -} - #define MAX_NET_CLIENTS 32 -#ifndef _WIN32 - -static void termsig_handler(int signal) -{ - qemu_system_shutdown_request(); -} - -static void sigchld_handler(int signal) -{ - waitpid(-1, NULL, WNOHANG); -} - -static void sighandler_setup(void) -{ - struct sigaction act; - - memset(&act, 0, sizeof(act)); - act.sa_handler = termsig_handler; - sigaction(SIGINT, &act, NULL); - sigaction(SIGHUP, &act, NULL); - sigaction(SIGTERM, &act, NULL); - - act.sa_handler = sigchld_handler; - act.sa_flags = SA_NOCLDSTOP; - sigaction(SIGCHLD, &act, NULL); -} - -#endif - #ifdef _WIN32 /* Look for support files in the same directory as the executable. */ static char *find_datadir(const char *argv0) @@ -3504,18 +2090,9 @@ int main(int argc, char **argv, char **envp) const char *cpu_model; const char *usb_devices[MAX_USB_CMDLINE]; int usb_devices_index; -#ifndef _WIN32 - int fds[2]; -#endif int tb_size; const char *pid_file = NULL; const char *incoming = NULL; -#ifndef _WIN32 - int fd = 0; - struct passwd *pwd = NULL; - const char *chroot_dir = NULL; - const char *run_as = NULL; -#endif CPUState *env; int show_vnc_port = 0; @@ -3524,35 +2101,7 @@ int main(int argc, char **argv, char **envp) qemu_cache_utils_init(envp); QLIST_INIT (&vm_change_state_head); -#ifndef _WIN32 - { - struct sigaction act; - sigfillset(&act.sa_mask); - act.sa_flags = 0; - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, NULL); - } -#else - SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE); - /* Note: cpu_interrupt() is currently not SMP safe, so we force - QEMU to run on a single CPU */ - { - HANDLE h; - DWORD mask, smask; - int i; - h = GetCurrentProcess(); - if (GetProcessAffinityMask(h, &mask, &smask)) { - for(i = 0; i < 32; i++) { - if (mask & (1 << i)) - break; - } - if (i != 32) { - mask = 1 << i; - SetProcessAffinityMask(h, mask); - } - } - } -#endif + os_setup_early_signal_handling(); module_call_init(MODULE_INIT_MACHINE); machine = find_default_machine(); @@ -3825,11 +2374,6 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_bootp: bootp_filename = optarg; break; -#ifndef _WIN32 - case QEMU_OPTION_smb: - net_slirp_smb(optarg); - break; -#endif case QEMU_OPTION_redir: net_slirp_redir(NULL, optarg, NULL); break; @@ -4122,11 +2666,6 @@ int main(int argc, char **argv, char **envp) exit(1); } break; -#ifndef _WIN32 - case QEMU_OPTION_daemonize: - daemonize = 1; - break; -#endif case QEMU_OPTION_option_rom: if (nb_option_roms >= MAX_OPTION_ROMS) { fprintf(stderr, "Too many option ROMs\n"); @@ -4164,7 +2703,7 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_startdate: { struct tm tm; - time_t rtc_start_date; + time_t rtc_start_date = 0; if (!strcmp(optarg, "now")) { rtc_date_offset = -1; } else { @@ -4210,14 +2749,6 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_incoming: incoming = optarg; break; -#ifndef _WIN32 - case QEMU_OPTION_chroot: - chroot_dir = optarg; - break; - case QEMU_OPTION_runas: - run_as = optarg; - break; -#endif #ifdef CONFIG_XEN case QEMU_OPTION_xen_domid: xen_domid = atoi(optarg); @@ -4243,6 +2774,11 @@ int main(int argc, char **argv, char **envp) data_dir = CONFIG_QEMU_SHAREDIR; } + if (pid_file && qemu_create_pidfile(pid_file) != 0) { + os_pidfile_error(); + exit(1); + } + #if defined(CONFIG_KVM) && defined(CONFIG_KQEMU) if (kvm_allowed && kqemu_allowed) { fprintf(stderr, @@ -4268,60 +2804,6 @@ int main(int argc, char **argv, char **envp) monitor_device = "stdio"; } -#ifndef _WIN32 - if (daemonize) { - pid_t pid; - - if (pipe(fds) == -1) - exit(1); - - pid = fork(); - if (pid > 0) { - uint8_t status; - ssize_t len; - - close(fds[1]); - - again: - len = read(fds[0], &status, 1); - if (len == -1 && (errno == EINTR)) - goto again; - - if (len != 1) - exit(1); - else if (status == 1) { - fprintf(stderr, "Could not acquire pidfile\n"); - exit(1); - } else - exit(0); - } else if (pid < 0) - exit(1); - - setsid(); - - pid = fork(); - if (pid > 0) - exit(0); - else if (pid < 0) - exit(1); - - umask(027); - - signal(SIGTSTP, SIG_IGN); - signal(SIGTTOU, SIG_IGN); - signal(SIGTTIN, SIG_IGN); - } - - if (pid_file && qemu_create_pidfile(pid_file) != 0) { - if (daemonize) { - uint8_t status = 1; - write(fds[1], &status, 1); - } else - fprintf(stderr, "Could not acquire pid file\n"); - exit(1); - } -#endif - #ifdef CONFIG_KQEMU if (smp_cpus > 1) kqemu_allowed = 0; @@ -4347,7 +2829,7 @@ int main(int argc, char **argv, char **envp) if (!boot_devices[0]) { boot_devices = "cad"; } - setvbuf(stdout, NULL, _IOLBF, 0); + os_set_line_buffering(); if (init_timer_alarm() < 0) { fprintf(stderr, "could not initialize alarm timer\n"); @@ -4460,10 +2942,8 @@ int main(int argc, char **argv, char **envp) //register_savevm("timer", 0, 2, timer_save, timer_load, NULL); register_savevm_live("ram", 0, 3, ram_save_live, NULL, ram_load, NULL); -#ifndef _WIN32 /* must be after terminal init, SDL library changes signal handlers */ - sighandler_setup(); -#endif + os_setup_signal_handling(); /* Maintain compatibility with multiple stdio monitors */ if (!strcmp(monitor_device,"stdio")) { @@ -4669,15 +3149,15 @@ int main(int argc, char **argv, char **envp) dcl = ds->listeners; while (dcl != NULL) { if (dcl->dpy_refresh != NULL) { - ds->gui_timer = qemu_new_timer(rt_clock, gui_update, ds); - qemu_mod_timer(ds->gui_timer, qemu_get_clock(rt_clock)); + ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds); + qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock)); } dcl = dcl->next; } if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) { - nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL); - qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock)); + nographic_timer = qemu_new_timer_ms(rt_clock, nographic_update, NULL); + qemu_mod_timer(nographic_timer, qemu_get_clock_ms(rt_clock)); } text_consoles_set_display(display_state); @@ -4727,70 +3207,7 @@ int main(int argc, char **argv, char **envp) if (autostart) vm_start(); -#ifndef _WIN32 - if (daemonize) { - uint8_t status = 0; - ssize_t len; - - again1: - len = write(fds[1], &status, 1); - if (len == -1 && (errno == EINTR)) - goto again1; - - if (len != 1) - exit(1); - - if (chdir("/")) { - perror("not able to chdir to /"); - exit(1); - } - TFR(fd = qemu_open("/dev/null", O_RDWR)); - if (fd == -1) - exit(1); - } - - if (run_as) { - pwd = getpwnam(run_as); - if (!pwd) { - fprintf(stderr, "User \"%s\" doesn't exist\n", run_as); - exit(1); - } - } - - if (chroot_dir) { - if (chroot(chroot_dir) < 0) { - fprintf(stderr, "chroot failed\n"); - exit(1); - } - if (chdir("/")) { - perror("not able to chdir to /"); - exit(1); - } - } - - if (run_as) { - if (setgid(pwd->pw_gid) < 0) { - fprintf(stderr, "Failed to setgid(%d)\n", pwd->pw_gid); - exit(1); - } - if (setuid(pwd->pw_uid) < 0) { - fprintf(stderr, "Failed to setuid(%d)\n", pwd->pw_uid); - exit(1); - } - if (setuid(0) != -1) { - fprintf(stderr, "Dropping privileges failed\n"); - exit(1); - } - } - - if (daemonize) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - - close(fd); - } -#endif + os_setup_post(); main_loop(); quit_timers(); diff --git a/vnc-android.c b/vnc-android.c index af39dd2..9b259b2 100644 --- a/vnc-android.c +++ b/vnc-android.c @@ -722,7 +722,7 @@ static void vnc_update_client(void *opaque) if (vs->output.offset && !vs->audio_cap && !vs->force_update) { /* kernel send buffers are full -> drop frames to throttle */ - qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); + qemu_mod_timer(vs->timer, qemu_get_clock_ms(rt_clock) + VNC_REFRESH_INTERVAL); return; } @@ -763,7 +763,7 @@ static void vnc_update_client(void *opaque) } if (!has_dirty && !vs->audio_cap && !vs->force_update) { - qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); + qemu_mod_timer(vs->timer, qemu_get_clock_ms(rt_clock) + VNC_REFRESH_INTERVAL); return; } @@ -811,7 +811,7 @@ static void vnc_update_client(void *opaque) } if (vs->csock != -1) { - qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); + qemu_mod_timer(vs->timer, qemu_get_clock_ms(rt_clock) + VNC_REFRESH_INTERVAL); } else { vnc_disconnect_finish(vs); } @@ -2073,7 +2073,7 @@ static void vnc_connect(VncDisplay *vd, int csock) vs->vd = vd; vs->ds = vd->ds; - vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs); + vs->timer = qemu_new_timer_ms(rt_clock, vnc_update_client, vs); vs->last_x = -1; vs->last_y = -1; @@ -716,7 +716,7 @@ static void vnc_update_client(void *opaque) if (vs->output.offset && !vs->audio_cap && !vs->force_update) { /* kernel send buffers are full -> drop frames to throttle */ - qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); + qemu_mod_timer(vs->timer, qemu_get_clock_ms(rt_clock) + VNC_REFRESH_INTERVAL); return; } @@ -757,7 +757,7 @@ static void vnc_update_client(void *opaque) } if (!has_dirty && !vs->audio_cap && !vs->force_update) { - qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); + qemu_mod_timer(vs->timer, qemu_get_clock_ms(rt_clock) + VNC_REFRESH_INTERVAL); return; } @@ -805,7 +805,7 @@ static void vnc_update_client(void *opaque) } if (vs->csock != -1) { - qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); + qemu_mod_timer(vs->timer, qemu_get_clock_ms(rt_clock) + VNC_REFRESH_INTERVAL); } else { vnc_disconnect_finish(vs); } @@ -1585,7 +1585,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) static void set_pixel_conversion(VncState *vs) { if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == - (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && + (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) { vs->write_pixels = vnc_write_pixels_copy; switch (vs->ds->surface->pf.bits_per_pixel) { @@ -1693,7 +1693,7 @@ static void vnc_colordepth(VncState *vs) vnc_write_u8(vs, 0); /* msg id */ vnc_write_u8(vs, 0); vnc_write_u16(vs, 1); /* number of rects */ - vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), + vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds), VNC_ENCODING_WMVi); pixel_format_message(vs); vnc_flush(vs); @@ -2067,7 +2067,7 @@ static void vnc_connect(VncDisplay *vd, int csock) vs->vd = vd; vs->ds = vd->ds; - vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs); + vs->timer = qemu_new_timer_ms(rt_clock, vnc_update_client, vs); vs->last_x = -1; vs->last_y = -1; @@ -2175,7 +2175,7 @@ int vnc_display_password(DisplayState *ds, const char *password) char *vnc_display_local_addr(DisplayState *ds) { VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; - + return vnc_socket_local_addr("%s:%s", vs->lsock); } |