diff options
56 files changed, 2902 insertions, 1761 deletions
diff --git a/Makefile b/Makefile index 1bc3cb049a..d8e1f36229 100644 --- a/Makefile +++ b/Makefile @@ -173,6 +173,7 @@ qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(qapi-obj-y): $(GENERATED_HEADERS) qapi-dir := $(BUILD_DIR)/qapi-generated qemu-ga$(EXESUF): LIBS = $(LIBS_QGA) +qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir) gen-out-type = $(subst .,-,$(suffix $@)) @@ -347,28 +348,29 @@ QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") +POD2MAN = pod2man --utf8 qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi $(call quiet-command, \ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \ - pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \ + $(POD2MAN) --section=1 --center=" " --release=" " qemu.pod > $@, \ " GEN $@") qemu-img.1: qemu-img.texi qemu-img-cmds.texi $(call quiet-command, \ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \ - pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \ + $(POD2MAN) --section=1 --center=" " --release=" " qemu-img.pod > $@, \ " GEN $@") fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi $(call quiet-command, \ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< fsdev/virtfs-proxy-helper.pod && \ - pod2man --section=1 --center=" " --release=" " fsdev/virtfs-proxy-helper.pod > $@, \ + $(POD2MAN) --section=1 --center=" " --release=" " fsdev/virtfs-proxy-helper.pod > $@, \ " GEN $@") qemu-nbd.8: qemu-nbd.texi $(call quiet-command, \ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \ - pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ + $(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ " GEN $@") dvi: qemu-doc.dvi qemu-tech.dvi diff --git a/Makefile.target b/Makefile.target index 63cf769dc9..44b2e83e6f 100644 --- a/Makefile.target +++ b/Makefile.target @@ -81,8 +81,10 @@ libobj-y += tcg/tcg.o tcg/optimize.o libobj-$(CONFIG_TCG_INTERPRETER) += tci.o libobj-y += fpu/softfloat.o ifneq ($(TARGET_BASE_ARCH), sparc) +ifneq ($(TARGET_BASE_ARCH), alpha) libobj-y += op_helper.o endif +endif libobj-y += helper.o ifeq ($(TARGET_BASE_ARCH), i386) libobj-y += cpuid.o @@ -96,6 +98,7 @@ libobj-y += cpu_init.o endif libobj-$(TARGET_SPARC) += int32_helper.o libobj-$(TARGET_SPARC64) += int64_helper.o +libobj-$(TARGET_ALPHA) += int_helper.o fpu_helper.o sys_helper.o mem_helper.o libobj-y += disas.o libobj-$(CONFIG_TCI_DIS) += tci-dis.o diff --git a/QMP/qmp-spec.txt b/QMP/qmp-spec.txt index 9d30a8ce6e..1ba916c9f2 100644 --- a/QMP/qmp-spec.txt +++ b/QMP/qmp-spec.txt @@ -209,13 +209,27 @@ incompatible way are disabled by default and will be advertised by the capabilities array (section '2.2 Server Greeting'). Thus, Clients can check that array and enable the capabilities they support. -Additionally, Clients must not assume any particular: - -- Size of json-objects or length of json-arrays +The QMP Server performs a type check on the arguments to a command. It +generates an error if a value does not have the expected type for its +key, or if it does not understand a key that the Client included. The +strictness of the Server catches wrong assumptions of Clients about +the Server's schema. Clients can assume that, when such validation +errors occur, they will be reported before the command generated any +side effect. + +However, Clients must not assume any particular: + +- Length of json-arrays +- Size of json-objects; in particular, future versions of QEMU may add + new keys and Clients should be able to ignore them. - Order of json-object members or json-array elements - Amount of errors generated by a command, that is, new errors can be added to any existing command in newer versions of the Server +Of course, the Server does guarantee to send valid JSON. But apart from +this, a Client should be "conservative in what they send, and liberal in +what they accept". + 6. Downstream extension of QMP ------------------------------ diff --git a/configure b/configure index 8b4e3c1a02..14ef738b6c 100755 --- a/configure +++ b/configure @@ -3608,7 +3608,7 @@ case "$target_arch2" in esac case "$target_arch2" in - sparc*) + alpha | sparc*) echo "CONFIG_TCG_PASS_AREG0=y" >> $config_target_mak ;; esac diff --git a/def-helper.h b/def-helper.h index 0e70c31bc4..b98ff69e4e 100644 --- a/def-helper.h +++ b/def-helper.h @@ -39,6 +39,7 @@ #endif #define dh_alias_ptr ptr #define dh_alias_void void +#define dh_alias_noreturn noreturn #define dh_alias_env ptr #define dh_alias(t) glue(dh_alias_, t) @@ -52,36 +53,42 @@ #define dh_ctype_tl target_ulong #define dh_ctype_ptr void * #define dh_ctype_void void +#define dh_ctype_noreturn void QEMU_NORETURN #define dh_ctype_env CPUArchState * #define dh_ctype(t) dh_ctype_##t /* We can't use glue() here because it falls foul of C preprocessor recursive expansion rules. */ #define dh_retvar_decl0_void void +#define dh_retvar_decl0_noreturn void #define dh_retvar_decl0_i32 TCGv_i32 retval #define dh_retvar_decl0_i64 TCGv_i64 retval #define dh_retvar_decl0_ptr TCGv_ptr retval #define dh_retvar_decl0(t) glue(dh_retvar_decl0_, dh_alias(t)) #define dh_retvar_decl_void +#define dh_retvar_decl_noreturn #define dh_retvar_decl_i32 TCGv_i32 retval, #define dh_retvar_decl_i64 TCGv_i64 retval, #define dh_retvar_decl_ptr TCGv_ptr retval, #define dh_retvar_decl(t) glue(dh_retvar_decl_, dh_alias(t)) #define dh_retvar_void TCG_CALL_DUMMY_ARG +#define dh_retvar_noreturn TCG_CALL_DUMMY_ARG #define dh_retvar_i32 GET_TCGV_i32(retval) #define dh_retvar_i64 GET_TCGV_i64(retval) #define dh_retvar_ptr GET_TCGV_ptr(retval) #define dh_retvar(t) glue(dh_retvar_, dh_alias(t)) #define dh_is_64bit_void 0 +#define dh_is_64bit_noreturn 0 #define dh_is_64bit_i32 0 #define dh_is_64bit_i64 1 #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_noreturn 0 #define dh_is_signed_i32 0 #define dh_is_signed_s32 1 #define dh_is_signed_i64 0 diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 5831e371ea..ad11767a2f 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -194,11 +194,11 @@ Example: void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp) { - GenericList *i; + GenericList *i, **prev = (GenericList **)obj; visit_start_list(m, name, errp); - for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) { + for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) { UserDefOneList *native_i = (UserDefOneList *)i; visit_type_UserDefOne(m, &native_i->value, NULL, errp); } diff --git a/elf.h b/elf.h index 2e05d34620..36bcac4c11 100644 --- a/elf.h +++ b/elf.h @@ -216,6 +216,7 @@ typedef int64_t Elf64_Sxword; #define ELF_ST_BIND(x) ((x) >> 4) #define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf) +#define ELF_ST_INFO(bind, type) (((bind) << 4) | ((type) & 0xf)) #define ELF32_ST_BIND(x) ELF_ST_BIND(x) #define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) #define ELF64_ST_BIND(x) ELF_ST_BIND(x) @@ -345,6 +346,21 @@ typedef struct { #define R_MIPS_HIVENDOR 127 +/* SUN SPARC specific definitions. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + /* * Sparc ELF relocation types */ diff --git a/exec.c b/exec.c index 265e895603..6731ab8c4b 100644 --- a/exec.c +++ b/exec.c @@ -636,6 +636,7 @@ void tcg_exec_init(unsigned long tb_size) cpu_gen_init(); code_gen_alloc(tb_size); code_gen_ptr = code_gen_buffer; + tcg_register_jit(code_gen_buffer, code_gen_buffer_size); page_init(); #if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE) /* There's no guest base to take into account, so go ahead and diff --git a/gdbstub.c b/gdbstub.c index f4e97f7370..6a7e2c4934 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -284,7 +284,6 @@ enum RSState { RS_GETLINE, RS_CHKSUM1, RS_CHKSUM2, - RS_SYSCALL, }; typedef struct GDBState { CPUArchState *c_cpu; /* current CPU for step/continue ops */ @@ -304,6 +303,8 @@ typedef struct GDBState { CharDriverState *chr; CharDriverState *mon_chr; #endif + char syscall_buf[256]; + gdb_syscall_complete_cb current_syscall_cb; } GDBState; /* By default use no IRQs and no timers while single stepping so as to @@ -346,8 +347,6 @@ static int get_char(GDBState *s) } #endif -static gdb_syscall_complete_cb gdb_current_syscall_cb; - static enum { GDB_SYS_UNKNOWN, GDB_SYS_ENABLED, @@ -2097,8 +2096,10 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) if (*p == ',') p++; type = *p; - if (gdb_current_syscall_cb) - gdb_current_syscall_cb(s->c_cpu, ret, err); + if (s->current_syscall_cb) { + s->current_syscall_cb(s->c_cpu, ret, err); + s->current_syscall_cb = NULL; + } if (type == 'C') { put_packet(s, "T02"); } else { @@ -2398,7 +2399,12 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) const char *type; int ret; - if (running || s->state == RS_INACTIVE || s->state == RS_SYSCALL) { + if (running || s->state == RS_INACTIVE) { + return; + } + /* Is there a GDB syscall waiting to be sent? */ + if (s->current_syscall_cb) { + put_packet(s, s->syscall_buf); return; } switch (state) { @@ -2468,8 +2474,8 @@ send_packet: void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) { va_list va; - char buf[256]; char *p; + char *p_end; target_ulong addr; uint64_t i64; GDBState *s; @@ -2477,14 +2483,13 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) s = gdbserver_state; if (!s) return; - gdb_current_syscall_cb = cb; - s->state = RS_SYSCALL; + s->current_syscall_cb = cb; #ifndef CONFIG_USER_ONLY vm_stop(RUN_STATE_DEBUG); #endif - s->state = RS_IDLE; va_start(va, fmt); - p = buf; + p = s->syscall_buf; + p_end = &s->syscall_buf[sizeof(s->syscall_buf)]; *(p++) = 'F'; while (*fmt) { if (*fmt == '%') { @@ -2492,17 +2497,17 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) switch (*fmt++) { case 'x': addr = va_arg(va, target_ulong); - p += snprintf(p, &buf[sizeof(buf)] - p, TARGET_FMT_lx, addr); + p += snprintf(p, p_end - p, TARGET_FMT_lx, addr); break; case 'l': if (*(fmt++) != 'x') goto bad_format; i64 = va_arg(va, uint64_t); - p += snprintf(p, &buf[sizeof(buf)] - p, "%" PRIx64, i64); + p += snprintf(p, p_end - p, "%" PRIx64, i64); break; case 's': addr = va_arg(va, target_ulong); - p += snprintf(p, &buf[sizeof(buf)] - p, TARGET_FMT_lx "/%x", + p += snprintf(p, p_end - p, TARGET_FMT_lx "/%x", addr, va_arg(va, int)); break; default: @@ -2517,10 +2522,16 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) } *p = 0; va_end(va); - put_packet(s, buf); #ifdef CONFIG_USER_ONLY + put_packet(s, s->syscall_buf); gdb_handlesig(s->c_cpu, 0); #else + /* In this case wait to send the syscall packet until notification that + the CPU has stopped. This must be done because if the packet is sent + now the reply from the syscall request could be received while the CPU + is still in the running state, which can cause packets to be dropped + and state transition 'T' packets to be sent while the syscall is still + being processed. */ cpu_exit(s->c_cpu); #endif } @@ -2919,6 +2930,7 @@ int gdbserver_start(const char *device) s->chr = chr; s->state = chr ? RS_IDLE : RS_INACTIVE; s->mon_chr = mon_chr; + s->current_syscall_cb = NULL; return 0; } diff --git a/hw/blizzard.c b/hw/blizzard.c index c7d844d105..29074c4fa7 100644 --- a/hw/blizzard.c +++ b/hw/blizzard.c @@ -937,9 +937,7 @@ static void blizzard_screen_dump(void *opaque, const char *filename, { BlizzardState *s = (BlizzardState *) opaque; - if (cswitch) { - blizzard_update_display(opaque); - } + blizzard_update_display(opaque); if (s && ds_get_data(s->state)) ppm_save(filename, s->state->surface); } diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c index f172093876..4a08e9d002 100644 --- a/hw/omap_lcdc.c +++ b/hw/omap_lcdc.c @@ -267,9 +267,8 @@ static int ppm_save(const char *filename, uint8_t *data, static void omap_screen_dump(void *opaque, const char *filename, bool cswitch) { struct omap_lcd_panel_s *omap_lcd = opaque; - if (cswitch) { - omap_update_display(opaque); - } + + omap_update_display(opaque); if (omap_lcd && ds_get_data(omap_lcd->state)) ppm_save(filename, ds_get_data(omap_lcd->state), omap_lcd->width, omap_lcd->height, diff --git a/hw/vga.c b/hw/vga.c index 6dc98f6c2a..f80860caed 100644 --- a/hw/vga.c +++ b/hw/vga.c @@ -2415,7 +2415,7 @@ static void vga_screen_dump(void *opaque, const char *filename, bool cswitch) if (cswitch) { vga_invalidate_display(s); - vga_hw_update(); } + vga_hw_update(); ppm_save(filename, s->ds->surface); } diff --git a/hw/xen_console.c b/hw/xen_console.c index edcb31ce66..3794b1972d 100644 --- a/hw/xen_console.c +++ b/hw/xen_console.c @@ -248,6 +248,9 @@ static void con_disconnect(struct XenDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + if (!xendev->dev) { + return; + } if (con->chr) qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); xen_be_unbind_evtchn(&con->xendev); diff --git a/hw/xen_disk.c b/hw/xen_disk.c index 9cb0253719..9719395b09 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -726,6 +726,7 @@ static void blk_disconnect(struct XenDevice *xendev) if (!blkdev->dinfo) { /* close/delete only if we created it ourself */ bdrv_close(blkdev->bs); + bdrv_detach_dev(blkdev->bs, blkdev); bdrv_delete(blkdev->bs); } blkdev->bs = NULL; diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index bdc3c79462..802cae3a29 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -682,8 +682,19 @@ vcard_emul_event_thread(void *arg) SECMODModule *module = (SECMODModule *)arg; do { + /* + * XXX - the latency value doesn't matter one bit. you only get no + * blocking (flags |= CKF_DONT_BLOCK) or PKCS11_WAIT_LATENCY (==500), + * hard coded in coolkey. And it isn't coolkey's fault - the timeout + * value we pass get's dropped on the floor before C_WaitForSlotEvent + * is called. + */ slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500); if (slot == NULL) { + /* this could be just a no event indication */ + if (PORT_GetError() == SEC_ERROR_NO_EVENT) { + continue; + } break; } vreader = vcard_emul_find_vreader_from_slot(slot); @@ -994,10 +1005,10 @@ vcard_emul_init(const VCardEmulOptions *options) SECMOD_GetReadLock(module_lock); for (mlp = module_list; mlp; mlp = mlp->next) { SECMODModule *module = mlp->module; - PRBool has_emul_slots = PR_FALSE; - if (module == NULL) { - continue; + /* Ignore the internal module */ + if (module == NULL || module == SECMOD_GetInternalModule()) { + continue; } for (i = 0; i < module->slotCount; i++) { @@ -1007,15 +1018,22 @@ vcard_emul_init(const VCardEmulOptions *options) if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) { continue; } + if (strcmp("E-Gate 0 0", PK11_GetSlotName(slot)) == 0) { + /* + * coolkey <= 1.1.0-20 emulates this reader if it can't find + * any hardware readers. This causes problems, warn user of + * problems. + */ + fprintf(stderr, "known bad coolkey version - see " + "https://bugzilla.redhat.com/show_bug.cgi?id=802435\n"); + continue; + } vreader_emul = vreader_emul_new(slot, options->hw_card_type, options->hw_type_params); vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, vreader_emul_delete); vreader_add_reader(vreader); - has_readers = PR_TRUE; - has_emul_slots = PR_TRUE; - if (PK11_IsPresent(slot)) { VCard *vcard; vcard = vcard_emul_mirror_card(vreader); @@ -1024,12 +1042,10 @@ vcard_emul_init(const VCardEmulOptions *options) vcard_free(vcard); } } - if (has_emul_slots) { - vcard_emul_new_event_thread(module); - } + vcard_emul_new_event_thread(module); } SECMOD_ReleaseReadLock(module_lock); - nss_emul_init = has_readers; + nss_emul_init = PR_TRUE; return VCARD_EMUL_OK; } diff --git a/monitor.c b/monitor.c index 2ff1e0b4d6..8946a100c0 100644 --- a/monitor.c +++ b/monitor.c @@ -4157,6 +4157,9 @@ static int check_client_args_type(const QDict *client_args, case 'O': assert(flags & QMP_ACCEPT_UNKNOWNS); break; + case 'q': + /* Any QObject can be passed. */ + break; case '/': case '.': /* diff --git a/qapi-schema-test.json b/qapi-schema-test.json index 8c7f9f79f4..9eae3501d7 100644 --- a/qapi-schema-test.json +++ b/qapi-schema-test.json @@ -8,7 +8,7 @@ # for testing nested structs { 'type': 'UserDefOne', - 'data': { 'integer': 'int', 'string': 'str' } } + 'data': { 'integer': 'int', 'string': 'str', '*enum1': 'EnumOne' } } { 'type': 'UserDefTwo', 'data': { 'string': 'str', diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c index e6b6152e08..74386b9b1b 100644 --- a/qapi/qmp-input-visitor.c +++ b/qapi/qmp-input-visitor.c @@ -22,16 +22,17 @@ typedef struct StackObject { - const QObject *obj; - const QListEntry *entry; + QObject *obj; + const QListEntry *entry; + GHashTable *h; } StackObject; struct QmpInputVisitor { Visitor visitor; - QObject *obj; StackObject stack[QIV_STACK_SIZE]; int nb_stack; + bool strict; }; static QmpInputVisitor *to_qiv(Visitor *v) @@ -39,21 +40,18 @@ static QmpInputVisitor *to_qiv(Visitor *v) return container_of(v, QmpInputVisitor, visitor); } -static const QObject *qmp_input_get_object(QmpInputVisitor *qiv, - const char *name) +static QObject *qmp_input_get_object(QmpInputVisitor *qiv, + const char *name) { - const QObject *qobj; - - if (qiv->nb_stack == 0) { - qobj = qiv->obj; - } else { - qobj = qiv->stack[qiv->nb_stack - 1].obj; - } + QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj; if (qobj) { if (name && qobject_type(qobj) == QTYPE_QDICT) { + if (qiv->stack[qiv->nb_stack - 1].h) { + g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name); + } return qdict_get(qobject_to_qdict(qobj), name); - } else if (qiv->nb_stack > 0 && qobject_type(qobj) == QTYPE_QLIST) { + } else if (qiv->stack[qiv->nb_stack - 1].entry) { return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); } } @@ -61,34 +59,57 @@ static const QObject *qmp_input_get_object(QmpInputVisitor *qiv, return qobj; } -static void qmp_input_push(QmpInputVisitor *qiv, const QObject *obj, Error **errp) +static void qdict_add_key(const char *key, QObject *obj, void *opaque) { - qiv->stack[qiv->nb_stack].obj = obj; - if (qobject_type(obj) == QTYPE_QLIST) { - qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj)); - } - qiv->nb_stack++; + GHashTable *h = opaque; + g_hash_table_insert(h, (gpointer) key, NULL); +} + +static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp) +{ + GHashTable *h; if (qiv->nb_stack >= QIV_STACK_SIZE) { error_set(errp, QERR_BUFFER_OVERRUN); return; } + + qiv->stack[qiv->nb_stack].obj = obj; + qiv->stack[qiv->nb_stack].entry = NULL; + qiv->stack[qiv->nb_stack].h = NULL; + + if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) { + h = g_hash_table_new(g_str_hash, g_str_equal); + qdict_iter(qobject_to_qdict(obj), qdict_add_key, h); + qiv->stack[qiv->nb_stack].h = h; + } + + qiv->nb_stack++; } static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) { - qiv->nb_stack--; - if (qiv->nb_stack < 0) { - error_set(errp, QERR_BUFFER_OVERRUN); - return; + GHashTableIter iter; + gpointer key; + + if (qiv->strict && qiv->stack[qiv->nb_stack - 1].h) { + g_hash_table_iter_init(&iter, qiv->stack[qiv->nb_stack - 1].h); + if (g_hash_table_iter_next(&iter, &key, NULL)) { + error_set(errp, QERR_QMP_EXTRA_MEMBER, (char *) key); + } + g_hash_table_unref(qiv->stack[qiv->nb_stack - 1].h); } + + assert(qiv->nb_stack > 0); + qiv->nb_stack--; } static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind, const char *name, size_t size, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - const QObject *qobj = qmp_input_get_object(qiv, name); + QObject *qobj = qmp_input_get_object(qiv, name); + Error *err = NULL; if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", @@ -96,8 +117,9 @@ static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind, return; } - qmp_input_push(qiv, qobj, errp); - if (error_is_set(errp)) { + qmp_input_push(qiv, qobj, &err); + if (err) { + error_propagate(errp, err); return; } @@ -116,7 +138,7 @@ static void qmp_input_end_struct(Visitor *v, Error **errp) static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - const QObject *qobj = qmp_input_get_object(qiv, name); + QObject *qobj = qmp_input_get_object(qiv, name); if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", @@ -133,18 +155,24 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, QmpInputVisitor *qiv = to_qiv(v); GenericList *entry; StackObject *so = &qiv->stack[qiv->nb_stack - 1]; + bool first; + + if (so->entry == NULL) { + so->entry = qlist_first(qobject_to_qlist(so->obj)); + first = true; + } else { + so->entry = qlist_next(so->entry); + first = false; + } if (so->entry == NULL) { return NULL; } entry = g_malloc0(sizeof(*entry)); - if (*list) { - so->entry = qlist_next(so->entry); - if (so->entry == NULL) { - g_free(entry); - return NULL; - } + if (first) { + *list = entry; + } else { (*list)->next = entry; } @@ -162,7 +190,7 @@ static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - const QObject *qobj = qmp_input_get_object(qiv, name); + QObject *qobj = qmp_input_get_object(qiv, name); if (!qobj || qobject_type(qobj) != QTYPE_QINT) { error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", @@ -177,7 +205,7 @@ static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - const QObject *qobj = qmp_input_get_object(qiv, name); + QObject *qobj = qmp_input_get_object(qiv, name); if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) { error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", @@ -192,7 +220,7 @@ static void qmp_input_type_str(Visitor *v, char **obj, const char *name, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - const QObject *qobj = qmp_input_get_object(qiv, name); + QObject *qobj = qmp_input_get_object(qiv, name); if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) { error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", @@ -207,7 +235,7 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - const QObject *qobj = qmp_input_get_object(qiv, name); + QObject *qobj = qmp_input_get_object(qiv, name); if (!qobj || qobject_type(qobj) != QTYPE_QFLOAT) { error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", @@ -222,7 +250,7 @@ static void qmp_input_start_optional(Visitor *v, bool *present, const char *name, Error **errp) { QmpInputVisitor *qiv = to_qiv(v); - const QObject *qobj = qmp_input_get_object(qiv, name); + QObject *qobj = qmp_input_get_object(qiv, name); if (!qobj) { *present = false; @@ -239,7 +267,7 @@ Visitor *qmp_input_get_visitor(QmpInputVisitor *v) void qmp_input_visitor_cleanup(QmpInputVisitor *v) { - qobject_decref(v->obj); + qobject_decref(v->stack[0].obj); g_free(v); } @@ -261,8 +289,18 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj) v->visitor.type_number = qmp_input_type_number; v->visitor.start_optional = qmp_input_start_optional; - v->obj = obj; - qobject_incref(v->obj); + qmp_input_push(v, obj, NULL); + qobject_incref(obj); + + return v; +} + +QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj) +{ + QmpInputVisitor *v; + + v = qmp_input_visitor_new(obj); + v->strict = true; return v; } diff --git a/qapi/qmp-input-visitor.h b/qapi/qmp-input-visitor.h index 3f798f0335..e0a48a5f3b 100644 --- a/qapi/qmp-input-visitor.h +++ b/qapi/qmp-input-visitor.h @@ -20,6 +20,8 @@ typedef struct QmpInputVisitor QmpInputVisitor; QmpInputVisitor *qmp_input_visitor_new(QObject *obj); +QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj); + void qmp_input_visitor_cleanup(QmpInputVisitor *v); Visitor *qmp_input_get_visitor(QmpInputVisitor *v); diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c index e0697b0d0f..2bce9d5db1 100644 --- a/qapi/qmp-output-visitor.c +++ b/qapi/qmp-output-visitor.c @@ -199,14 +199,16 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v) { QStackEntry *e, *tmp; + /* The bottom QStackEntry, if any, owns the root QObject. See the + * qmp_output_push_obj() invocations in qmp_output_add_obj(). */ + QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v); + QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) { QTAILQ_REMOVE(&v->stack, e, node); - if (e->value) { - qobject_decref(e->value); - } g_free(e); } + qobject_decref(root); g_free(v); } diff --git a/qemu-options.hx b/qemu-options.hx index daefce3038..662f571527 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -288,13 +288,21 @@ TODO ETEXI DEF("global", HAS_ARG, QEMU_OPTION_global, - "-global driver.property=value\n" + "-global driver.prop=value\n" " set a global default for a driver property\n", QEMU_ARCH_ALL) STEXI -@item -global +@item -global @var{driver}.@var{prop}=@var{value} @findex -global -TODO +Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.: + +@example +qemu -global ide-drive.physical_block_size=4096 -drive file=file,if=ide,index=0,media=disk +@end example + +In particular, you can use this to set driver properties for devices which are +created automatically by the machine model. To create a device which is not +created automatically and set properties on it, use -@option{device}. ETEXI DEF("mtdblock", HAS_ARG, QEMU_OPTION_mtdblock, diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 7b2be2f936..faf970dff3 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -12,29 +12,30 @@ */ #include <glib.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include "qga/guest-agent-core.h" +#include "qga-qmp-commands.h" +#include "qerror.h" +#include "qemu-queue.h" +#include "host-utils.h" #if defined(__linux__) #include <mntent.h> #include <linux/fs.h> - -#if defined(__linux__) && defined(FIFREEZE) -#define CONFIG_FSFREEZE -#endif -#endif - -#include <sys/types.h> -#include <sys/ioctl.h> #include <ifaddrs.h> #include <arpa/inet.h> #include <sys/socket.h> #include <net/if.h> #include <sys/wait.h> -#include "qga/guest-agent-core.h" -#include "qga-qmp-commands.h" -#include "qerror.h" -#include "qemu-queue.h" -#include "host-utils.h" +#if defined(__linux__) && defined(FIFREEZE) +#define CONFIG_FSFREEZE +#endif +#endif + +#if defined(__linux__) +/* TODO: use this in place of all post-fork() fclose(std*) callers */ static void reopen_fd_to_null(int fd) { int nullfd; @@ -50,6 +51,7 @@ static void reopen_fd_to_null(int fd) close(nullfd); } } +#endif /* defined(__linux__) */ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) { @@ -309,7 +311,11 @@ static void guest_file_init(void) QTAILQ_INIT(&guest_file_state.filehandles); } +/* linux-specific implementations. avoid this if at all possible. */ +#if defined(__linux__) + #if defined(CONFIG_FSFREEZE) + static void disable_logging(void) { ga_disable_logging(ga_state); @@ -505,38 +511,7 @@ static void guest_fsfreeze_cleanup(void) } } } -#else -/* - * Return status of freeze/thaw - */ -GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) -{ - error_set(err, QERR_UNSUPPORTED); - - return 0; -} - -/* - * Walk list of mounted file systems in the guest, and freeze the ones which - * are real local file systems. - */ -int64_t qmp_guest_fsfreeze_freeze(Error **err) -{ - error_set(err, QERR_UNSUPPORTED); - - return 0; -} - -/* - * Walk list of frozen file systems in the guest, and thaw them. - */ -int64_t qmp_guest_fsfreeze_thaw(Error **err) -{ - error_set(err, QERR_UNSUPPORTED); - - return 0; -} -#endif +#endif /* CONFIG_FSFREEZE */ #define LINUX_SYS_STATE_FILE "/sys/power/state" #define SUSPEND_SUPPORTED 0 @@ -904,6 +879,52 @@ error: return NULL; } +#else /* defined(__linux__) */ + +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return 0; +} + +int64_t qmp_guest_fsfreeze_freeze(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return 0; +} + +int64_t qmp_guest_fsfreeze_thaw(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return 0; +} + +void qmp_guest_suspend_disk(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); +} + +void qmp_guest_suspend_ram(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); +} + +void qmp_guest_suspend_hybrid(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); +} + +GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +#endif + /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { diff --git a/qmp-commands.hx b/qmp-commands.hx index c626ba8d3d..944787161f 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -708,7 +708,7 @@ EQMP }, { .name = "transaction", - .args_type = "actions:O", + .args_type = "actions:q", .mhandler.cmd_new = qmp_marshal_input_transaction, }, @@ -2125,7 +2125,7 @@ EQMP { .name = "qom-set", - .args_type = "path:s,property:s,opts:O", + .args_type = "path:s,property:s,value:q", .mhandler.cmd_new = qmp_qom_set, }, diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 3aabf61491..0b4f0a0fe1 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -42,7 +42,7 @@ def generate_command_decl(name, args, ret_type): return mcgen(''' %(ret_type)s qmp_%(name)s(%(args)sError **errp); ''', - ret_type=c_type(ret_type), name=c_var(name), args=arglist).strip() + ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip() def gen_sync_call(name, args, ret_type, indent=0): ret = "" @@ -59,7 +59,7 @@ def gen_sync_call(name, args, ret_type, indent=0): %(retval)sqmp_%(name)s(%(args)serrp); ''', - name=c_var(name), args=arglist, retval=retval).rstrip() + name=c_fun(name), args=arglist, retval=retval).rstrip() if ret_type: ret += "\n" + mcgen('''' if (!error_is_set(errp)) { @@ -74,7 +74,7 @@ if (!error_is_set(errp)) { def gen_marshal_output_call(name, ret_type): if not ret_type: return "" - return "qmp_marshal_output_%s(retval, ret, errp);" % c_var(name) + return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name) def gen_visitor_output_containers_decl(ret_type): ret = "" @@ -140,7 +140,7 @@ v = qapi_dealloc_get_visitor(md); ''') else: ret += mcgen(''' -mi = qmp_input_visitor_new(%(obj)s); +mi = qmp_input_visitor_new_strict(%(obj)s); v = qmp_input_get_visitor(mi); ''', obj=obj) @@ -198,16 +198,16 @@ static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_o qapi_dealloc_visitor_cleanup(md); } ''', - c_ret_type=c_type(ret_type), c_name=c_var(name), + c_ret_type=c_type(ret_type), c_name=c_fun(name), visitor=type_visitor(ret_type)) return ret def gen_marshal_input_decl(name, args, ret_type, middle_mode): if middle_mode: - return 'int qmp_marshal_input_%s(Monitor *mon, const QDict *qdict, QObject **ret)' % c_var(name) + return 'int qmp_marshal_input_%s(Monitor *mon, const QDict *qdict, QObject **ret)' % c_fun(name) else: - return 'static void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_var(name) + return 'static void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_fun(name) @@ -298,7 +298,7 @@ def gen_registry(commands): registry += mcgen(''' qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s); ''', - name=cmd['command'], c_name=c_var(cmd['command'])) + name=cmd['command'], c_name=c_fun(cmd['command'])) pop_indent() ret = mcgen(''' static void qmp_init_marshal(void) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 727fb77266..4a734f58d5 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -100,7 +100,7 @@ typedef enum %(name)s %(abbrev)s_%(value)s = %(i)d, ''', abbrev=de_camel_case(name).upper(), - value=c_var(value).upper(), + value=c_fun(value).upper(), i=i) i += 1 @@ -126,7 +126,7 @@ struct %(name)s %(c_type)s %(c_name)s; ''', c_type=c_type(typeinfo[key]), - c_name=c_var(key)) + c_name=c_fun(key)) ret += mcgen(''' }; diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 54117d4d2b..8d4e94a45f 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -61,7 +61,13 @@ def generate_visit_struct(name, members): void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) { + if (error_is_set(errp)) { + return; + } visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), errp); + if (obj && !*obj) { + goto end; + } ''', name=name) push_indent() @@ -69,6 +75,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** pop_indent() ret += mcgen(''' +end: visit_end_struct(m, errp); } ''') @@ -79,11 +86,14 @@ def generate_visit_list(name, members): void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp) { - GenericList *i, **head = (GenericList **)obj; + GenericList *i, **prev = (GenericList **)obj; + if (error_is_set(errp)) { + return; + } visit_start_list(m, name, errp); - for (*head = i = visit_next_list(m, head, errp); i; i = visit_next_list(m, &i, errp)) { + for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) { %(name)sList *native_i = (%(name)sList *)i; visit_type_%(name)s(m, &native_i->value, NULL, errp); } @@ -112,7 +122,13 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** { Error *err = NULL; + if (error_is_set(errp)) { + return; + } visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); + if (obj && !*obj) { + goto end; + } visit_type_%(name)sKind(m, &(*obj)->kind, "type", &err); if (err) { error_propagate(errp, err); @@ -129,9 +145,9 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** break; ''', abbrev = de_camel_case(name).upper(), - enum = de_camel_case(key).upper(), + enum = c_fun(de_camel_case(key)).upper(), c_type=members[key], - c_name=c_var(key)) + c_name=c_fun(key)) ret += mcgen(''' default: diff --git a/scripts/qapi.py b/scripts/qapi.py index 6e05469e6d..e06233666b 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -131,7 +131,10 @@ def camel_case(name): return new_name def c_var(name): - return '_'.join(name.split('-')).lstrip("*") + return name.replace('-', '_').lstrip("*") + +def c_fun(name): + return c_var(name).replace('.', '_') def c_list_type(name): return '%sList' % name diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py new file mode 100644 index 0000000000..8a0f30534f --- /dev/null +++ b/scripts/qemu-gdb.py @@ -0,0 +1,89 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright 2012 Red Hat, Inc. and/or its affiliates +# +# Authors: +# Avi Kivity <avi@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Contributions after 2012-01-13 are licensed under the terms of the +# GNU GPL, version 2 or (at your option) any later version. + + +import gdb + +def isnull(ptr): + return ptr == gdb.Value(0).cast(ptr.type) + +def int128(p): + return long(p['lo']) + (long(p['hi']) << 64) + +class QemuCommand(gdb.Command): + '''Prefix for QEMU debug support commands''' + def __init__(self): + gdb.Command.__init__(self, 'qemu', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE, True) + +class MtreeCommand(gdb.Command): + '''Display the memory tree hierarchy''' + def __init__(self): + gdb.Command.__init__(self, 'qemu mtree', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + self.queue = [] + def invoke(self, arg, from_tty): + self.seen = set() + self.queue_root('address_space_memory') + self.queue_root('address_space_io') + self.process_queue() + def queue_root(self, varname): + ptr = gdb.parse_and_eval(varname)['root'] + self.queue.append(ptr) + def process_queue(self): + while self.queue: + ptr = self.queue.pop(0) + if long(ptr) in self.seen: + continue + self.print_item(ptr) + def print_item(self, ptr, offset = gdb.Value(0), level = 0): + self.seen.add(long(ptr)) + addr = ptr['addr'] + addr += offset + size = int128(ptr['size']) + alias = ptr['alias'] + klass = '' + if not isnull(alias): + klass = ' (alias)' + elif not isnull(ptr['ops']): + klass = ' (I/O)' + elif bool(ptr['ram']): + klass = ' (RAM)' + gdb.write('%s%016x-%016x %s%s (@ %s)\n' + % (' ' * level, + long(addr), + long(addr + (size - 1)), + ptr['name'].string(), + klass, + ptr, + ), + gdb.STDOUT) + if not isnull(alias): + gdb.write('%s alias: %s@%016x (@ %s)\n' % + (' ' * level, + alias['name'].string(), + ptr['alias_offset'], + alias, + ), + gdb.STDOUT) + self.queue.append(alias) + subregion = ptr['subregions']['tqh_first'] + level += 1 + while not isnull(subregion): + self.print_item(subregion, addr, level) + subregion = subregion['subregions_link']['tqe_next'] + +QemuCommand() +MtreeCommand() diff --git a/scripts/texi2pod.pl b/scripts/texi2pod.pl index 9ed056ad1c..94097fb065 100755 --- a/scripts/texi2pod.pl +++ b/scripts/texi2pod.pl @@ -36,6 +36,7 @@ $fnno = 1; $inf = ""; $ibase = ""; @ipath = (); +$encoding = undef; while ($_ = shift) { if (/^-D(.*)$/) { @@ -97,6 +98,12 @@ while(<$inf>) { /^\@setfilename\s+([^.]+)/ and $fn = $1, next; /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next; + # Look for document encoding + /^\@documentencoding\s+([^.]+)/ and do { + $encoding = $1 unless defined $encoding; + next; + }; + # Identify a man title but keep only the one we are interested in. /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do { if (exists $defs{$1}) { @@ -336,6 +343,8 @@ $inf = pop @instack; die "No filename or title\n" unless defined $fn && defined $tl; +print "=encoding $encoding\n\n" if defined $encoding; + $sects{NAME} = "$fn \- $tl\n"; $sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES}; diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index 48c0fdc188..6c5d28bae1 100644 --- a/target-alpha/cpu.h +++ b/target-alpha/cpu.h @@ -234,7 +234,6 @@ struct CPUAlphaState { uint8_t fpcr_exc_mask; uint8_t fpcr_dyn_round; uint8_t fpcr_flush_to_zero; - uint8_t fpcr_dnz; uint8_t fpcr_dnod; uint8_t fpcr_undz; @@ -434,6 +433,9 @@ int cpu_alpha_handle_mmu_fault (CPUAlphaState *env, uint64_t address, int rw, int mmu_idx); #define cpu_handle_mmu_fault cpu_alpha_handle_mmu_fault void do_interrupt (CPUAlphaState *env); +void do_restore_state(CPUAlphaState *, void *retaddr); +void QEMU_NORETURN dynamic_excp(CPUAlphaState *, void *, int, int); +void QEMU_NORETURN arith_excp(CPUAlphaState *, void *, int, uint64_t); uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env); void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val); diff --git a/target-alpha/fpu_helper.c b/target-alpha/fpu_helper.c new file mode 100644 index 0000000000..dda110352e --- /dev/null +++ b/target-alpha/fpu_helper.c @@ -0,0 +1,822 @@ +/* + * Helpers for floating point instructions. + * + * Copyright (c) 2007 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "helper.h" +#include "softfloat.h" + +#define FP_STATUS (env->fp_status) + + +void helper_setroundmode(CPUAlphaState *env, uint32_t val) +{ + set_float_rounding_mode(val, &FP_STATUS); +} + +void helper_setflushzero(CPUAlphaState *env, uint32_t val) +{ + set_flush_to_zero(val, &FP_STATUS); +} + +void helper_fp_exc_clear(CPUAlphaState *env) +{ + set_float_exception_flags(0, &FP_STATUS); +} + +uint32_t helper_fp_exc_get(CPUAlphaState *env) +{ + return get_float_exception_flags(&FP_STATUS); +} + +static inline void inline_fp_exc_raise(CPUAlphaState *env, void *retaddr, + uint32_t exc, uint32_t regno) +{ + if (exc) { + uint32_t hw_exc = 0; + + if (exc & float_flag_invalid) { + hw_exc |= EXC_M_INV; + } + if (exc & float_flag_divbyzero) { + hw_exc |= EXC_M_DZE; + } + if (exc & float_flag_overflow) { + hw_exc |= EXC_M_FOV; + } + if (exc & float_flag_underflow) { + hw_exc |= EXC_M_UNF; + } + if (exc & float_flag_inexact) { + hw_exc |= EXC_M_INE; + } + + arith_excp(env, retaddr, hw_exc, 1ull << regno); + } +} + +/* Raise exceptions for ieee fp insns without software completion. + In that case there are no exceptions that don't trap; the mask + doesn't apply. */ +void helper_fp_exc_raise(CPUAlphaState *env, uint32_t exc, uint32_t regno) +{ + inline_fp_exc_raise(env, GETPC(), exc, regno); +} + +/* Raise exceptions for ieee fp insns with software completion. */ +void helper_fp_exc_raise_s(CPUAlphaState *env, uint32_t exc, uint32_t regno) +{ + if (exc) { + env->fpcr_exc_status |= exc; + exc &= ~env->fpcr_exc_mask; + inline_fp_exc_raise(env, GETPC(), exc, regno); + } +} + +/* Input handing without software completion. Trap for all + non-finite numbers. */ +void helper_ieee_input(CPUAlphaState *env, uint64_t val) +{ + uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; + uint64_t frac = val & 0xfffffffffffffull; + + if (exp == 0) { + /* Denormals without DNZ set raise an exception. */ + if (frac != 0 && !env->fp_status.flush_inputs_to_zero) { + arith_excp(env, GETPC(), EXC_M_UNF, 0); + } + } else if (exp == 0x7ff) { + /* Infinity or NaN. */ + /* ??? I'm not sure these exception bit flags are correct. I do + know that the Linux kernel, at least, doesn't rely on them and + just emulates the insn to figure out what exception to use. */ + arith_excp(env, GETPC(), frac ? EXC_M_INV : EXC_M_FOV, 0); + } +} + +/* Similar, but does not trap for infinities. Used for comparisons. */ +void helper_ieee_input_cmp(CPUAlphaState *env, uint64_t val) +{ + uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; + uint64_t frac = val & 0xfffffffffffffull; + + if (exp == 0) { + /* Denormals without DNZ set raise an exception. */ + if (frac != 0 && !env->fp_status.flush_inputs_to_zero) { + arith_excp(env, GETPC(), EXC_M_UNF, 0); + } + } else if (exp == 0x7ff && frac) { + /* NaN. */ + arith_excp(env, GETPC(), EXC_M_INV, 0); + } +} + +/* F floating (VAX) */ +static uint64_t float32_to_f(float32 fa) +{ + uint64_t r, exp, mant, sig; + CPU_FloatU a; + + a.f = fa; + sig = ((uint64_t)a.l & 0x80000000) << 32; + exp = (a.l >> 23) & 0xff; + mant = ((uint64_t)a.l & 0x007fffff) << 29; + + if (exp == 255) { + /* NaN or infinity */ + r = 1; /* VAX dirty zero */ + } else if (exp == 0) { + if (mant == 0) { + /* Zero */ + r = 0; + } else { + /* Denormalized */ + r = sig | ((exp + 1) << 52) | mant; + } + } else { + if (exp >= 253) { + /* Overflow */ + r = 1; /* VAX dirty zero */ + } else { + r = sig | ((exp + 2) << 52); + } + } + + return r; +} + +static float32 f_to_float32(CPUAlphaState *env, void *retaddr, uint64_t a) +{ + uint32_t exp, mant_sig; + CPU_FloatU r; + + exp = ((a >> 55) & 0x80) | ((a >> 52) & 0x7f); + mant_sig = ((a >> 32) & 0x80000000) | ((a >> 29) & 0x007fffff); + + if (unlikely(!exp && mant_sig)) { + /* Reserved operands / Dirty zero */ + dynamic_excp(env, retaddr, EXCP_OPCDEC, 0); + } + + if (exp < 3) { + /* Underflow */ + r.l = 0; + } else { + r.l = ((exp - 2) << 23) | mant_sig; + } + + return r.f; +} + +uint32_t helper_f_to_memory(uint64_t a) +{ + uint32_t r; + r = (a & 0x00001fffe0000000ull) >> 13; + r |= (a & 0x07ffe00000000000ull) >> 45; + r |= (a & 0xc000000000000000ull) >> 48; + return r; +} + +uint64_t helper_memory_to_f(uint32_t a) +{ + uint64_t r; + r = ((uint64_t)(a & 0x0000c000)) << 48; + r |= ((uint64_t)(a & 0x003fffff)) << 45; + r |= ((uint64_t)(a & 0xffff0000)) << 13; + if (!(a & 0x00004000)) { + r |= 0x7ll << 59; + } + return r; +} + +/* ??? Emulating VAX arithmetic with IEEE arithmetic is wrong. We should + either implement VAX arithmetic properly or just signal invalid opcode. */ + +uint64_t helper_addf(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = f_to_float32(env, GETPC(), a); + fb = f_to_float32(env, GETPC(), b); + fr = float32_add(fa, fb, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_subf(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = f_to_float32(env, GETPC(), a); + fb = f_to_float32(env, GETPC(), b); + fr = float32_sub(fa, fb, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_mulf(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = f_to_float32(env, GETPC(), a); + fb = f_to_float32(env, GETPC(), b); + fr = float32_mul(fa, fb, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_divf(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = f_to_float32(env, GETPC(), a); + fb = f_to_float32(env, GETPC(), b); + fr = float32_div(fa, fb, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_sqrtf(CPUAlphaState *env, uint64_t t) +{ + float32 ft, fr; + + ft = f_to_float32(env, GETPC(), t); + fr = float32_sqrt(ft, &FP_STATUS); + return float32_to_f(fr); +} + + +/* G floating (VAX) */ +static uint64_t float64_to_g(float64 fa) +{ + uint64_t r, exp, mant, sig; + CPU_DoubleU a; + + a.d = fa; + sig = a.ll & 0x8000000000000000ull; + exp = (a.ll >> 52) & 0x7ff; + mant = a.ll & 0x000fffffffffffffull; + + if (exp == 2047) { + /* NaN or infinity */ + r = 1; /* VAX dirty zero */ + } else if (exp == 0) { + if (mant == 0) { + /* Zero */ + r = 0; + } else { + /* Denormalized */ + r = sig | ((exp + 1) << 52) | mant; + } + } else { + if (exp >= 2045) { + /* Overflow */ + r = 1; /* VAX dirty zero */ + } else { + r = sig | ((exp + 2) << 52); + } + } + + return r; +} + +static float64 g_to_float64(CPUAlphaState *env, void *retaddr, uint64_t a) +{ + uint64_t exp, mant_sig; + CPU_DoubleU r; + + exp = (a >> 52) & 0x7ff; + mant_sig = a & 0x800fffffffffffffull; + + if (!exp && mant_sig) { + /* Reserved operands / Dirty zero */ + dynamic_excp(env, retaddr, EXCP_OPCDEC, 0); + } + + if (exp < 3) { + /* Underflow */ + r.ll = 0; + } else { + r.ll = ((exp - 2) << 52) | mant_sig; + } + + return r.d; +} + +uint64_t helper_g_to_memory(uint64_t a) +{ + uint64_t r; + r = (a & 0x000000000000ffffull) << 48; + r |= (a & 0x00000000ffff0000ull) << 16; + r |= (a & 0x0000ffff00000000ull) >> 16; + r |= (a & 0xffff000000000000ull) >> 48; + return r; +} + +uint64_t helper_memory_to_g(uint64_t a) +{ + uint64_t r; + r = (a & 0x000000000000ffffull) << 48; + r |= (a & 0x00000000ffff0000ull) << 16; + r |= (a & 0x0000ffff00000000ull) >> 16; + r |= (a & 0xffff000000000000ull) >> 48; + return r; +} + +uint64_t helper_addg(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + fr = float64_add(fa, fb, &FP_STATUS); + return float64_to_g(fr); +} + +uint64_t helper_subg(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + fr = float64_sub(fa, fb, &FP_STATUS); + return float64_to_g(fr); +} + +uint64_t helper_mulg(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + fr = float64_mul(fa, fb, &FP_STATUS); + return float64_to_g(fr); +} + +uint64_t helper_divg(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + fr = float64_div(fa, fb, &FP_STATUS); + return float64_to_g(fr); +} + +uint64_t helper_sqrtg(CPUAlphaState *env, uint64_t a) +{ + float64 fa, fr; + + fa = g_to_float64(env, GETPC(), a); + fr = float64_sqrt(fa, &FP_STATUS); + return float64_to_g(fr); +} + + +/* S floating (single) */ + +/* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */ +static inline uint64_t float32_to_s_int(uint32_t fi) +{ + uint32_t frac = fi & 0x7fffff; + uint32_t sign = fi >> 31; + uint32_t exp_msb = (fi >> 30) & 1; + uint32_t exp_low = (fi >> 23) & 0x7f; + uint32_t exp; + + exp = (exp_msb << 10) | exp_low; + if (exp_msb) { + if (exp_low == 0x7f) { + exp = 0x7ff; + } + } else { + if (exp_low != 0x00) { + exp |= 0x380; + } + } + + return (((uint64_t)sign << 63) + | ((uint64_t)exp << 52) + | ((uint64_t)frac << 29)); +} + +static inline uint64_t float32_to_s(float32 fa) +{ + CPU_FloatU a; + a.f = fa; + return float32_to_s_int(a.l); +} + +static inline uint32_t s_to_float32_int(uint64_t a) +{ + return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff); +} + +static inline float32 s_to_float32(uint64_t a) +{ + CPU_FloatU r; + r.l = s_to_float32_int(a); + return r.f; +} + +uint32_t helper_s_to_memory(uint64_t a) +{ + return s_to_float32_int(a); +} + +uint64_t helper_memory_to_s(uint32_t a) +{ + return float32_to_s_int(a); +} + +uint64_t helper_adds(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_add(fa, fb, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_subs(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_sub(fa, fb, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_muls(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_mul(fa, fb, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_divs(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_div(fa, fb, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_sqrts(CPUAlphaState *env, uint64_t a) +{ + float32 fa, fr; + + fa = s_to_float32(a); + fr = float32_sqrt(fa, &FP_STATUS); + return float32_to_s(fr); +} + + +/* T floating (double) */ +static inline float64 t_to_float64(uint64_t a) +{ + /* Memory format is the same as float64 */ + CPU_DoubleU r; + r.ll = a; + return r.d; +} + +static inline uint64_t float64_to_t(float64 fa) +{ + /* Memory format is the same as float64 */ + CPU_DoubleU r; + r.d = fa; + return r.ll; +} + +uint64_t helper_addt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_add(fa, fb, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_subt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_sub(fa, fb, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_mult(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_mul(fa, fb, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_divt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_div(fa, fb, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_sqrtt(CPUAlphaState *env, uint64_t a) +{ + float64 fa, fr; + + fa = t_to_float64(a); + fr = float64_sqrt(fa, &FP_STATUS); + return float64_to_t(fr); +} + +/* Comparisons */ +uint64_t helper_cmptun(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_unordered_quiet(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmpteq(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_eq_quiet(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmptle(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_le(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmptlt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_lt(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmpgeq(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + + if (float64_eq_quiet(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmpgle(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + + if (float64_le(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmpglt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + + if (float64_lt(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +/* Floating point format conversion */ +uint64_t helper_cvtts(CPUAlphaState *env, uint64_t a) +{ + float64 fa; + float32 fr; + + fa = t_to_float64(a); + fr = float64_to_float32(fa, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a) +{ + float32 fa; + float64 fr; + + fa = s_to_float32(a); + fr = float32_to_float64(fa, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a) +{ + float32 fr = int64_to_float32(a, &FP_STATUS); + return float32_to_s(fr); +} + +/* Implement float64 to uint64 conversion without saturation -- we must + supply the truncated result. This behaviour is used by the compiler + to get unsigned conversion for free with the same instruction. + + The VI flag is set when overflow or inexact exceptions should be raised. */ + +static inline uint64_t inline_cvttq(CPUAlphaState *env, uint64_t a, + int roundmode, int VI) +{ + uint64_t frac, ret = 0; + uint32_t exp, sign, exc = 0; + int shift; + + sign = (a >> 63); + exp = (uint32_t)(a >> 52) & 0x7ff; + frac = a & 0xfffffffffffffull; + + if (exp == 0) { + if (unlikely(frac != 0)) { + goto do_underflow; + } + } else if (exp == 0x7ff) { + exc = (frac ? float_flag_invalid : VI ? float_flag_overflow : 0); + } else { + /* Restore implicit bit. */ + frac |= 0x10000000000000ull; + + shift = exp - 1023 - 52; + if (shift >= 0) { + /* In this case the number is so large that we must shift + the fraction left. There is no rounding to do. */ + if (shift < 63) { + ret = frac << shift; + if (VI && (ret >> shift) != frac) { + exc = float_flag_overflow; + } + } + } else { + uint64_t round; + + /* In this case the number is smaller than the fraction as + represented by the 52 bit number. Here we must think + about rounding the result. Handle this by shifting the + fractional part of the number into the high bits of ROUND. + This will let us efficiently handle round-to-nearest. */ + shift = -shift; + if (shift < 63) { + ret = frac >> shift; + round = frac << (64 - shift); + } else { + /* The exponent is so small we shift out everything. + Leave a sticky bit for proper rounding below. */ + do_underflow: + round = 1; + } + + if (round) { + exc = (VI ? float_flag_inexact : 0); + switch (roundmode) { + case float_round_nearest_even: + if (round == (1ull << 63)) { + /* Fraction is exactly 0.5; round to even. */ + ret += (ret & 1); + } else if (round > (1ull << 63)) { + ret += 1; + } + break; + case float_round_to_zero: + break; + case float_round_up: + ret += 1 - sign; + break; + case float_round_down: + ret += sign; + break; + } + } + } + if (sign) { + ret = -ret; + } + } + if (unlikely(exc)) { + float_raise(exc, &FP_STATUS); + } + + return ret; +} + +uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a) +{ + return inline_cvttq(env, a, FP_STATUS.float_rounding_mode, 1); +} + +uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a) +{ + return inline_cvttq(env, a, float_round_to_zero, 0); +} + +uint64_t helper_cvttq_svic(CPUAlphaState *env, uint64_t a) +{ + return inline_cvttq(env, a, float_round_to_zero, 1); +} + +uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a) +{ + float64 fr = int64_to_float64(a, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_cvtqf(CPUAlphaState *env, uint64_t a) +{ + float32 fr = int64_to_float32(a, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_cvtgf(CPUAlphaState *env, uint64_t a) +{ + float64 fa; + float32 fr; + + fa = g_to_float64(env, GETPC(), a); + fr = float64_to_float32(fa, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_cvtgq(CPUAlphaState *env, uint64_t a) +{ + float64 fa = g_to_float64(env, GETPC(), a); + return float64_to_int64_round_to_zero(fa, &FP_STATUS); +} + +uint64_t helper_cvtqg(CPUAlphaState *env, uint64_t a) +{ + float64 fr; + fr = int64_to_float64(a, &FP_STATUS); + return float64_to_g(fr); +} diff --git a/target-alpha/helper.c b/target-alpha/helper.c index 3f2e7c383d..765e650002 100644 --- a/target-alpha/helper.c +++ b/target-alpha/helper.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "softfloat.h" +#include "helper.h" uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env) { @@ -81,7 +82,7 @@ uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env) break; } - if (env->fpcr_dnz) { + if (env->fp_status.flush_inputs_to_zero) { r |= FPCR_DNZ; } if (env->fpcr_dnod) { @@ -150,17 +151,25 @@ void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val) } env->fpcr_dyn_round = t; - env->fpcr_flush_to_zero - = (val & (FPCR_UNDZ|FPCR_UNFD)) == (FPCR_UNDZ|FPCR_UNFD); - - env->fpcr_dnz = (val & FPCR_DNZ) != 0; env->fpcr_dnod = (val & FPCR_DNOD) != 0; env->fpcr_undz = (val & FPCR_UNDZ) != 0; + env->fpcr_flush_to_zero = env->fpcr_dnod & env->fpcr_undz; + env->fp_status.flush_inputs_to_zero = (val & FPCR_DNZ) != 0; +} + +uint64_t helper_load_fpcr(CPUAlphaState *env) +{ + return cpu_alpha_load_fpcr(env); +} + +void helper_store_fpcr(CPUAlphaState *env, uint64_t val) +{ + cpu_alpha_store_fpcr(env, val); } #if defined(CONFIG_USER_ONLY) -int cpu_alpha_handle_mmu_fault (CPUAlphaState *env, target_ulong address, int rw, - int mmu_idx) +int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong address, + int rw, int mmu_idx) { env->exception_index = EXCP_MMFAULT; env->trap_arg0 = address; @@ -484,3 +493,41 @@ void cpu_dump_state (CPUAlphaState *env, FILE *f, fprintf_function cpu_fprintf, } cpu_fprintf(f, "\n"); } + +void do_restore_state(CPUAlphaState *env, void *retaddr) +{ + uintptr_t pc = (uintptr_t)retaddr; + if (pc) { + TranslationBlock *tb = tb_find_pc(pc); + if (tb) { + cpu_restore_state(tb, env, pc); + } + } +} + +/* This should only be called from translate, via gen_excp. + We expect that ENV->PC has already been updated. */ +void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error) +{ + env->exception_index = excp; + env->error_code = error; + cpu_loop_exit(env); +} + +/* This may be called from any of the helpers to set up EXCEPTION_INDEX. */ +void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, void *retaddr, + int excp, int error) +{ + env->exception_index = excp; + env->error_code = error; + do_restore_state(env, retaddr); + cpu_loop_exit(env); +} + +void QEMU_NORETURN arith_excp(CPUAlphaState *env, void *retaddr, + int exc, uint64_t mask) +{ + env->trap_arg0 = exc; + env->trap_arg1 = mask; + dynamic_excp(env, retaddr, EXCP_ARITH, 0); +} diff --git a/target-alpha/helper.h b/target-alpha/helper.h index b693ceea97..a184def3e9 100644 --- a/target-alpha/helper.h +++ b/target-alpha/helper.h @@ -1,14 +1,14 @@ #include "def-helper.h" -DEF_HELPER_2(excp, void, int, int) -DEF_HELPER_FLAGS_0(load_pcc, TCG_CALL_CONST | TCG_CALL_PURE, i64) - -DEF_HELPER_2(addqv, i64, i64, i64) -DEF_HELPER_2(addlv, i64, i64, i64) -DEF_HELPER_2(subqv, i64, i64, i64) -DEF_HELPER_2(sublv, i64, i64, i64) -DEF_HELPER_2(mullv, i64, i64, i64) -DEF_HELPER_2(mulqv, i64, i64, i64) +DEF_HELPER_3(excp, noreturn, env, int, int) +DEF_HELPER_FLAGS_1(load_pcc, TCG_CALL_CONST | TCG_CALL_PURE, i64, env) + +DEF_HELPER_3(addqv, i64, env, i64, i64) +DEF_HELPER_3(addlv, i64, env, i64, i64) +DEF_HELPER_3(subqv, i64, env, i64, i64) +DEF_HELPER_3(sublv, i64, env, i64, i64) +DEF_HELPER_3(mullv, i64, env, i64, i64) +DEF_HELPER_3(mulqv, i64, env, i64, i64) DEF_HELPER_FLAGS_2(umulh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) DEF_HELPER_FLAGS_1(ctpop, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) @@ -34,90 +34,89 @@ DEF_HELPER_FLAGS_1(pkwb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) DEF_HELPER_FLAGS_1(unpkbl, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) DEF_HELPER_FLAGS_1(unpkbw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) -DEF_HELPER_FLAGS_0(load_fpcr, TCG_CALL_CONST | TCG_CALL_PURE, i64) -DEF_HELPER_FLAGS_1(store_fpcr, TCG_CALL_CONST, void, i64) +DEF_HELPER_FLAGS_1(load_fpcr, TCG_CALL_CONST | TCG_CALL_PURE, i64, env) +DEF_HELPER_FLAGS_2(store_fpcr, TCG_CALL_CONST, void, env, i64) DEF_HELPER_FLAGS_1(f_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64) DEF_HELPER_FLAGS_1(memory_to_f, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32) -DEF_HELPER_FLAGS_2(addf, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(subf, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(mulf, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(divf, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_1(sqrtf, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_3(addf, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(subf, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(mulf, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(divf, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(sqrtf, TCG_CALL_CONST, i64, env, i64) DEF_HELPER_FLAGS_1(g_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) DEF_HELPER_FLAGS_1(memory_to_g, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) -DEF_HELPER_FLAGS_2(addg, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(subg, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(mulg, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(divg, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_1(sqrtg, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_3(addg, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(subg, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(mulg, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(divg, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(sqrtg, TCG_CALL_CONST, i64, env, i64) DEF_HELPER_FLAGS_1(s_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64) DEF_HELPER_FLAGS_1(memory_to_s, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32) -DEF_HELPER_FLAGS_2(adds, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(subs, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(muls, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(divs, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_1(sqrts, TCG_CALL_CONST, i64, i64) - -DEF_HELPER_FLAGS_2(addt, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(subt, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(mult, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_2(divt, TCG_CALL_CONST, i64, i64, i64) -DEF_HELPER_FLAGS_1(sqrtt, TCG_CALL_CONST, i64, i64) - -DEF_HELPER_FLAGS_2(cmptun, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) -DEF_HELPER_FLAGS_2(cmpteq, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) -DEF_HELPER_FLAGS_2(cmptle, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) -DEF_HELPER_FLAGS_2(cmptlt, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) -DEF_HELPER_FLAGS_2(cmpgeq, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) -DEF_HELPER_FLAGS_2(cmpgle, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) -DEF_HELPER_FLAGS_2(cmpglt, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) - -DEF_HELPER_FLAGS_1(cvtts, TCG_CALL_CONST, i64, i64) -DEF_HELPER_FLAGS_1(cvtst, TCG_CALL_CONST, i64, i64) -DEF_HELPER_FLAGS_1(cvtqs, TCG_CALL_CONST, i64, i64) -DEF_HELPER_FLAGS_1(cvtqt, TCG_CALL_CONST, i64, i64) -DEF_HELPER_FLAGS_1(cvtqf, TCG_CALL_CONST, i64, i64) -DEF_HELPER_FLAGS_1(cvtgf, TCG_CALL_CONST, i64, i64) -DEF_HELPER_FLAGS_1(cvtgq, TCG_CALL_CONST, i64, i64) -DEF_HELPER_FLAGS_1(cvtqg, TCG_CALL_CONST, i64, i64) - -DEF_HELPER_FLAGS_1(cvttq, TCG_CALL_CONST, i64, i64) -DEF_HELPER_FLAGS_1(cvttq_c, TCG_CALL_CONST, i64, i64) -DEF_HELPER_FLAGS_1(cvttq_svic, TCG_CALL_CONST, i64, i64) - -DEF_HELPER_FLAGS_1(setroundmode, TCG_CALL_CONST, void, i32) -DEF_HELPER_FLAGS_1(setflushzero, TCG_CALL_CONST, void, i32) -DEF_HELPER_FLAGS_0(fp_exc_clear, TCG_CALL_CONST, void) -DEF_HELPER_FLAGS_0(fp_exc_get, TCG_CALL_CONST | TCG_CALL_PURE, i32) -DEF_HELPER_2(fp_exc_raise, void, i32, i32) -DEF_HELPER_2(fp_exc_raise_s, void, i32, i32) - -DEF_HELPER_1(ieee_input, i64, i64) -DEF_HELPER_1(ieee_input_cmp, i64, i64) -DEF_HELPER_1(ieee_input_s, i64, i64) +DEF_HELPER_FLAGS_3(adds, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(subs, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(muls, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(divs, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(sqrts, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_3(addt, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(subt, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(mult, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(divt, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(sqrtt, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_3(cmptun, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmpteq, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmptle, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmptlt, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmpgeq, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmpgle, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmpglt, TCG_CALL_CONST, i64, env, i64, i64) + +DEF_HELPER_FLAGS_2(cvtts, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtst, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtqs, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtqt, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtqf, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtgf, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtgq, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtqg, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_2(cvttq, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvttq_c, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvttq_svic, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_2(setroundmode, TCG_CALL_CONST, void, env, i32) +DEF_HELPER_FLAGS_2(setflushzero, TCG_CALL_CONST, void, env, i32) +DEF_HELPER_FLAGS_1(fp_exc_clear, TCG_CALL_CONST, void, env) +DEF_HELPER_FLAGS_1(fp_exc_get, TCG_CALL_CONST | TCG_CALL_PURE, i32, env) +DEF_HELPER_3(fp_exc_raise, void, env, i32, i32) +DEF_HELPER_3(fp_exc_raise_s, void, env, i32, i32) + +DEF_HELPER_2(ieee_input, void, env, i64) +DEF_HELPER_2(ieee_input_cmp, void, env, i64) #if !defined (CONFIG_USER_ONLY) -DEF_HELPER_1(hw_ret, void, i64) +DEF_HELPER_2(hw_ret, void, env, i64) DEF_HELPER_1(ldl_phys, i64, i64) DEF_HELPER_1(ldq_phys, i64, i64) -DEF_HELPER_1(ldl_l_phys, i64, i64) -DEF_HELPER_1(ldq_l_phys, i64, i64) +DEF_HELPER_2(ldl_l_phys, i64, env, i64) +DEF_HELPER_2(ldq_l_phys, i64, env, i64) DEF_HELPER_2(stl_phys, void, i64, i64) DEF_HELPER_2(stq_phys, void, i64, i64) -DEF_HELPER_2(stl_c_phys, i64, i64, i64) -DEF_HELPER_2(stq_c_phys, i64, i64, i64) +DEF_HELPER_3(stl_c_phys, i64, env, i64, i64) +DEF_HELPER_3(stq_c_phys, i64, env, i64, i64) -DEF_HELPER_FLAGS_0(tbia, TCG_CALL_CONST, void) -DEF_HELPER_FLAGS_1(tbis, TCG_CALL_CONST, void, i64) +DEF_HELPER_FLAGS_1(tbia, TCG_CALL_CONST, void, env) +DEF_HELPER_FLAGS_2(tbis, TCG_CALL_CONST, void, env, i64) DEF_HELPER_1(halt, void, i64); DEF_HELPER_FLAGS_0(get_time, TCG_CALL_CONST, i64) -DEF_HELPER_FLAGS_1(set_alarm, TCG_CALL_CONST, void, i64) +DEF_HELPER_FLAGS_2(set_alarm, TCG_CALL_CONST, void, env, i64) #endif #include "def-helper.h" diff --git a/target-alpha/int_helper.c b/target-alpha/int_helper.c new file mode 100644 index 0000000000..1d832f0b57 --- /dev/null +++ b/target-alpha/int_helper.c @@ -0,0 +1,319 @@ +/* + * Helpers for integer and multimedia instructions. + * + * Copyright (c) 2007 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "helper.h" +#include "host-utils.h" + + +uint64_t helper_umulh(uint64_t op1, uint64_t op2) +{ + uint64_t tl, th; + mulu64(&tl, &th, op1, op2); + return th; +} + +uint64_t helper_ctpop(uint64_t arg) +{ + return ctpop64(arg); +} + +uint64_t helper_ctlz(uint64_t arg) +{ + return clz64(arg); +} + +uint64_t helper_cttz(uint64_t arg) +{ + return ctz64(arg); +} + +static inline uint64_t byte_zap(uint64_t op, uint8_t mskb) +{ + uint64_t mask; + + mask = 0; + mask |= ((mskb >> 0) & 1) * 0x00000000000000FFULL; + mask |= ((mskb >> 1) & 1) * 0x000000000000FF00ULL; + mask |= ((mskb >> 2) & 1) * 0x0000000000FF0000ULL; + mask |= ((mskb >> 3) & 1) * 0x00000000FF000000ULL; + mask |= ((mskb >> 4) & 1) * 0x000000FF00000000ULL; + mask |= ((mskb >> 5) & 1) * 0x0000FF0000000000ULL; + mask |= ((mskb >> 6) & 1) * 0x00FF000000000000ULL; + mask |= ((mskb >> 7) & 1) * 0xFF00000000000000ULL; + + return op & ~mask; +} + +uint64_t helper_zap(uint64_t val, uint64_t mask) +{ + return byte_zap(val, mask); +} + +uint64_t helper_zapnot(uint64_t val, uint64_t mask) +{ + return byte_zap(val, ~mask); +} + +uint64_t helper_cmpbge(uint64_t op1, uint64_t op2) +{ + uint8_t opa, opb, res; + int i; + + res = 0; + for (i = 0; i < 8; i++) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + if (opa >= opb) { + res |= 1 << i; + } + } + return res; +} + +uint64_t helper_minub8(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint8_t opa, opb, opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; +} + +uint64_t helper_minsb8(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + int8_t opa, opb; + uint8_t opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; +} + +uint64_t helper_minuw4(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint16_t opa, opb, opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; +} + +uint64_t helper_minsw4(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + int16_t opa, opb; + uint16_t opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; +} + +uint64_t helper_maxub8(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint8_t opa, opb, opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; +} + +uint64_t helper_maxsb8(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + int8_t opa, opb; + uint8_t opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; +} + +uint64_t helper_maxuw4(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint16_t opa, opb, opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; +} + +uint64_t helper_maxsw4(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + int16_t opa, opb; + uint16_t opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; +} + +uint64_t helper_perr(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint8_t opa, opb, opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + if (opa >= opb) { + opr = opa - opb; + } else { + opr = opb - opa; + } + res += opr; + } + return res; +} + +uint64_t helper_pklb(uint64_t op1) +{ + return (op1 & 0xff) | ((op1 >> 24) & 0xff00); +} + +uint64_t helper_pkwb(uint64_t op1) +{ + return ((op1 & 0xff) + | ((op1 >> 8) & 0xff00) + | ((op1 >> 16) & 0xff0000) + | ((op1 >> 24) & 0xff000000)); +} + +uint64_t helper_unpkbl(uint64_t op1) +{ + return (op1 & 0xff) | ((op1 & 0xff00) << 24); +} + +uint64_t helper_unpkbw(uint64_t op1) +{ + return ((op1 & 0xff) + | ((op1 & 0xff00) << 8) + | ((op1 & 0xff0000) << 16) + | ((op1 & 0xff000000) << 24)); +} + +uint64_t helper_addqv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint64_t tmp = op1; + op1 += op2; + if (unlikely((tmp ^ op2 ^ (-1ULL)) & (tmp ^ op1) & (1ULL << 63))) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return op1; +} + +uint64_t helper_addlv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint64_t tmp = op1; + op1 = (uint32_t)(op1 + op2); + if (unlikely((tmp ^ op2 ^ (-1UL)) & (tmp ^ op1) & (1UL << 31))) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return op1; +} + +uint64_t helper_subqv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint64_t res; + res = op1 - op2; + if (unlikely((op1 ^ op2) & (res ^ op1) & (1ULL << 63))) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return res; +} + +uint64_t helper_sublv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint32_t res; + res = op1 - op2; + if (unlikely((op1 ^ op2) & (res ^ op1) & (1UL << 31))) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return res; +} + +uint64_t helper_mullv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + int64_t res = (int64_t)op1 * (int64_t)op2; + + if (unlikely((int32_t)res != res)) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return (int64_t)((int32_t)res); +} + +uint64_t helper_mulqv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint64_t tl, th; + + muls64(&tl, &th, op1, op2); + /* If th != 0 && th != -1, then we had an overflow */ + if (unlikely((th + 1) > 1)) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return tl; +} diff --git a/target-alpha/mem_helper.c b/target-alpha/mem_helper.c new file mode 100644 index 0000000000..dd5ca4933a --- /dev/null +++ b/target-alpha/mem_helper.c @@ -0,0 +1,151 @@ +/* + * Helpers for loads and stores + * + * Copyright (c) 2007 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "helper.h" + + +/* Softmmu support */ +#ifndef CONFIG_USER_ONLY + +uint64_t helper_ldl_phys(uint64_t p) +{ + return (int32_t)ldl_phys(p); +} + +uint64_t helper_ldq_phys(uint64_t p) +{ + return ldq_phys(p); +} + +uint64_t helper_ldl_l_phys(CPUAlphaState *env, uint64_t p) +{ + env->lock_addr = p; + return env->lock_value = (int32_t)ldl_phys(p); +} + +uint64_t helper_ldq_l_phys(CPUAlphaState *env, uint64_t p) +{ + env->lock_addr = p; + return env->lock_value = ldq_phys(p); +} + +void helper_stl_phys(uint64_t p, uint64_t v) +{ + stl_phys(p, v); +} + +void helper_stq_phys(uint64_t p, uint64_t v) +{ + stq_phys(p, v); +} + +uint64_t helper_stl_c_phys(CPUAlphaState *env, uint64_t p, uint64_t v) +{ + uint64_t ret = 0; + + if (p == env->lock_addr) { + int32_t old = ldl_phys(p); + if (old == (int32_t)env->lock_value) { + stl_phys(p, v); + ret = 1; + } + } + env->lock_addr = -1; + + return ret; +} + +uint64_t helper_stq_c_phys(CPUAlphaState *env, uint64_t p, uint64_t v) +{ + uint64_t ret = 0; + + if (p == env->lock_addr) { + uint64_t old = ldq_phys(p); + if (old == env->lock_value) { + stq_phys(p, v); + ret = 1; + } + } + env->lock_addr = -1; + + return ret; +} + +static void do_unaligned_access(CPUAlphaState *env, target_ulong addr, + int is_write, int is_user, void *retaddr) +{ + uint64_t pc; + uint32_t insn; + + do_restore_state(env, retaddr); + + pc = env->pc; + insn = cpu_ldl_code(env, pc); + + env->trap_arg0 = addr; + env->trap_arg1 = insn >> 26; /* opcode */ + env->trap_arg2 = (insn >> 21) & 31; /* dest regno */ + env->exception_index = EXCP_UNALIGN; + env->error_code = 0; + cpu_loop_exit(env); +} + +void cpu_unassigned_access(CPUAlphaState *env, target_phys_addr_t addr, + int is_write, int is_exec, int unused, int size) +{ + env->trap_arg0 = addr; + env->trap_arg1 = is_write; + dynamic_excp(env, NULL, EXCP_MCHK, 0); +} + +#include "softmmu_exec.h" + +#define MMUSUFFIX _mmu +#define ALIGNED_ONLY + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + +/* try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill(CPUAlphaState *env, target_ulong addr, int is_write, + int mmu_idx, void *retaddr) +{ + int ret; + + ret = cpu_alpha_handle_mmu_fault(env, addr, is_write, mmu_idx); + if (unlikely(ret != 0)) { + do_restore_state(env, retaddr); + /* Exception index and error code are already set */ + cpu_loop_exit(env); + } +} +#endif /* CONFIG_USER_ONLY */ diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c deleted file mode 100644 index c77f009b79..0000000000 --- a/target-alpha/op_helper.c +++ /dev/null @@ -1,1379 +0,0 @@ -/* - * Alpha emulation cpu micro-operations helpers for qemu. - * - * Copyright (c) 2007 Jocelyn Mayer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "cpu.h" -#include "dyngen-exec.h" -#include "host-utils.h" -#include "softfloat.h" -#include "helper.h" -#include "sysemu.h" -#include "qemu-timer.h" - -#define FP_STATUS (env->fp_status) - -/*****************************************************************************/ -/* Exceptions processing helpers */ - -/* This should only be called from translate, via gen_excp. - We expect that ENV->PC has already been updated. */ -void QEMU_NORETURN helper_excp(int excp, int error) -{ - env->exception_index = excp; - env->error_code = error; - cpu_loop_exit(env); -} - -static void do_restore_state(void *retaddr) -{ - unsigned long pc = (unsigned long)retaddr; - - if (pc) { - TranslationBlock *tb = tb_find_pc(pc); - if (tb) { - cpu_restore_state(tb, env, pc); - } - } -} - -/* This may be called from any of the helpers to set up EXCEPTION_INDEX. */ -static void QEMU_NORETURN dynamic_excp(int excp, int error) -{ - env->exception_index = excp; - env->error_code = error; - do_restore_state(GETPC()); - cpu_loop_exit(env); -} - -static void QEMU_NORETURN arith_excp(int exc, uint64_t mask) -{ - env->trap_arg0 = exc; - env->trap_arg1 = mask; - dynamic_excp(EXCP_ARITH, 0); -} - -uint64_t helper_load_pcc (void) -{ -#ifndef CONFIG_USER_ONLY - /* In system mode we have access to a decent high-resolution clock. - In order to make OS-level time accounting work with the RPCC, - present it with a well-timed clock fixed at 250MHz. */ - return (((uint64_t)env->pcc_ofs << 32) - | (uint32_t)(qemu_get_clock_ns(vm_clock) >> 2)); -#else - /* In user-mode, vm_clock doesn't exist. Just pass through the host cpu - clock ticks. Also, don't bother taking PCC_OFS into account. */ - return (uint32_t)cpu_get_real_ticks(); -#endif -} - -uint64_t helper_load_fpcr (void) -{ - return cpu_alpha_load_fpcr (env); -} - -void helper_store_fpcr (uint64_t val) -{ - cpu_alpha_store_fpcr (env, val); -} - -uint64_t helper_addqv (uint64_t op1, uint64_t op2) -{ - uint64_t tmp = op1; - op1 += op2; - if (unlikely((tmp ^ op2 ^ (-1ULL)) & (tmp ^ op1) & (1ULL << 63))) { - arith_excp(EXC_M_IOV, 0); - } - return op1; -} - -uint64_t helper_addlv (uint64_t op1, uint64_t op2) -{ - uint64_t tmp = op1; - op1 = (uint32_t)(op1 + op2); - if (unlikely((tmp ^ op2 ^ (-1UL)) & (tmp ^ op1) & (1UL << 31))) { - arith_excp(EXC_M_IOV, 0); - } - return op1; -} - -uint64_t helper_subqv (uint64_t op1, uint64_t op2) -{ - uint64_t res; - res = op1 - op2; - if (unlikely((op1 ^ op2) & (res ^ op1) & (1ULL << 63))) { - arith_excp(EXC_M_IOV, 0); - } - return res; -} - -uint64_t helper_sublv (uint64_t op1, uint64_t op2) -{ - uint32_t res; - res = op1 - op2; - if (unlikely((op1 ^ op2) & (res ^ op1) & (1UL << 31))) { - arith_excp(EXC_M_IOV, 0); - } - return res; -} - -uint64_t helper_mullv (uint64_t op1, uint64_t op2) -{ - int64_t res = (int64_t)op1 * (int64_t)op2; - - if (unlikely((int32_t)res != res)) { - arith_excp(EXC_M_IOV, 0); - } - return (int64_t)((int32_t)res); -} - -uint64_t helper_mulqv (uint64_t op1, uint64_t op2) -{ - uint64_t tl, th; - - muls64(&tl, &th, op1, op2); - /* If th != 0 && th != -1, then we had an overflow */ - if (unlikely((th + 1) > 1)) { - arith_excp(EXC_M_IOV, 0); - } - return tl; -} - -uint64_t helper_umulh (uint64_t op1, uint64_t op2) -{ - uint64_t tl, th; - - mulu64(&tl, &th, op1, op2); - return th; -} - -uint64_t helper_ctpop (uint64_t arg) -{ - return ctpop64(arg); -} - -uint64_t helper_ctlz (uint64_t arg) -{ - return clz64(arg); -} - -uint64_t helper_cttz (uint64_t arg) -{ - return ctz64(arg); -} - -static inline uint64_t byte_zap(uint64_t op, uint8_t mskb) -{ - uint64_t mask; - - mask = 0; - mask |= ((mskb >> 0) & 1) * 0x00000000000000FFULL; - mask |= ((mskb >> 1) & 1) * 0x000000000000FF00ULL; - mask |= ((mskb >> 2) & 1) * 0x0000000000FF0000ULL; - mask |= ((mskb >> 3) & 1) * 0x00000000FF000000ULL; - mask |= ((mskb >> 4) & 1) * 0x000000FF00000000ULL; - mask |= ((mskb >> 5) & 1) * 0x0000FF0000000000ULL; - mask |= ((mskb >> 6) & 1) * 0x00FF000000000000ULL; - mask |= ((mskb >> 7) & 1) * 0xFF00000000000000ULL; - - return op & ~mask; -} - -uint64_t helper_zap(uint64_t val, uint64_t mask) -{ - return byte_zap(val, mask); -} - -uint64_t helper_zapnot(uint64_t val, uint64_t mask) -{ - return byte_zap(val, ~mask); -} - -uint64_t helper_cmpbge (uint64_t op1, uint64_t op2) -{ - uint8_t opa, opb, res; - int i; - - res = 0; - for (i = 0; i < 8; i++) { - opa = op1 >> (i * 8); - opb = op2 >> (i * 8); - if (opa >= opb) - res |= 1 << i; - } - return res; -} - -uint64_t helper_minub8 (uint64_t op1, uint64_t op2) -{ - uint64_t res = 0; - uint8_t opa, opb, opr; - int i; - - for (i = 0; i < 8; ++i) { - opa = op1 >> (i * 8); - opb = op2 >> (i * 8); - opr = opa < opb ? opa : opb; - res |= (uint64_t)opr << (i * 8); - } - return res; -} - -uint64_t helper_minsb8 (uint64_t op1, uint64_t op2) -{ - uint64_t res = 0; - int8_t opa, opb; - uint8_t opr; - int i; - - for (i = 0; i < 8; ++i) { - opa = op1 >> (i * 8); - opb = op2 >> (i * 8); - opr = opa < opb ? opa : opb; - res |= (uint64_t)opr << (i * 8); - } - return res; -} - -uint64_t helper_minuw4 (uint64_t op1, uint64_t op2) -{ - uint64_t res = 0; - uint16_t opa, opb, opr; - int i; - - for (i = 0; i < 4; ++i) { - opa = op1 >> (i * 16); - opb = op2 >> (i * 16); - opr = opa < opb ? opa : opb; - res |= (uint64_t)opr << (i * 16); - } - return res; -} - -uint64_t helper_minsw4 (uint64_t op1, uint64_t op2) -{ - uint64_t res = 0; - int16_t opa, opb; - uint16_t opr; - int i; - - for (i = 0; i < 4; ++i) { - opa = op1 >> (i * 16); - opb = op2 >> (i * 16); - opr = opa < opb ? opa : opb; - res |= (uint64_t)opr << (i * 16); - } - return res; -} - -uint64_t helper_maxub8 (uint64_t op1, uint64_t op2) -{ - uint64_t res = 0; - uint8_t opa, opb, opr; - int i; - - for (i = 0; i < 8; ++i) { - opa = op1 >> (i * 8); - opb = op2 >> (i * 8); - opr = opa > opb ? opa : opb; - res |= (uint64_t)opr << (i * 8); - } - return res; -} - -uint64_t helper_maxsb8 (uint64_t op1, uint64_t op2) -{ - uint64_t res = 0; - int8_t opa, opb; - uint8_t opr; - int i; - - for (i = 0; i < 8; ++i) { - opa = op1 >> (i * 8); - opb = op2 >> (i * 8); - opr = opa > opb ? opa : opb; - res |= (uint64_t)opr << (i * 8); - } - return res; -} - -uint64_t helper_maxuw4 (uint64_t op1, uint64_t op2) -{ - uint64_t res = 0; - uint16_t opa, opb, opr; - int i; - - for (i = 0; i < 4; ++i) { - opa = op1 >> (i * 16); - opb = op2 >> (i * 16); - opr = opa > opb ? opa : opb; - res |= (uint64_t)opr << (i * 16); - } - return res; -} - -uint64_t helper_maxsw4 (uint64_t op1, uint64_t op2) -{ - uint64_t res = 0; - int16_t opa, opb; - uint16_t opr; - int i; - - for (i = 0; i < 4; ++i) { - opa = op1 >> (i * 16); - opb = op2 >> (i * 16); - opr = opa > opb ? opa : opb; - res |= (uint64_t)opr << (i * 16); - } - return res; -} - -uint64_t helper_perr (uint64_t op1, uint64_t op2) -{ - uint64_t res = 0; - uint8_t opa, opb, opr; - int i; - - for (i = 0; i < 8; ++i) { - opa = op1 >> (i * 8); - opb = op2 >> (i * 8); - if (opa >= opb) - opr = opa - opb; - else - opr = opb - opa; - res += opr; - } - return res; -} - -uint64_t helper_pklb (uint64_t op1) -{ - return (op1 & 0xff) | ((op1 >> 24) & 0xff00); -} - -uint64_t helper_pkwb (uint64_t op1) -{ - return ((op1 & 0xff) - | ((op1 >> 8) & 0xff00) - | ((op1 >> 16) & 0xff0000) - | ((op1 >> 24) & 0xff000000)); -} - -uint64_t helper_unpkbl (uint64_t op1) -{ - return (op1 & 0xff) | ((op1 & 0xff00) << 24); -} - -uint64_t helper_unpkbw (uint64_t op1) -{ - return ((op1 & 0xff) - | ((op1 & 0xff00) << 8) - | ((op1 & 0xff0000) << 16) - | ((op1 & 0xff000000) << 24)); -} - -/* Floating point helpers */ - -void helper_setroundmode (uint32_t val) -{ - set_float_rounding_mode(val, &FP_STATUS); -} - -void helper_setflushzero (uint32_t val) -{ - set_flush_to_zero(val, &FP_STATUS); -} - -void helper_fp_exc_clear (void) -{ - set_float_exception_flags(0, &FP_STATUS); -} - -uint32_t helper_fp_exc_get (void) -{ - return get_float_exception_flags(&FP_STATUS); -} - -/* Raise exceptions for ieee fp insns without software completion. - In that case there are no exceptions that don't trap; the mask - doesn't apply. */ -void helper_fp_exc_raise(uint32_t exc, uint32_t regno) -{ - if (exc) { - uint32_t hw_exc = 0; - - if (exc & float_flag_invalid) { - hw_exc |= EXC_M_INV; - } - if (exc & float_flag_divbyzero) { - hw_exc |= EXC_M_DZE; - } - if (exc & float_flag_overflow) { - hw_exc |= EXC_M_FOV; - } - if (exc & float_flag_underflow) { - hw_exc |= EXC_M_UNF; - } - if (exc & float_flag_inexact) { - hw_exc |= EXC_M_INE; - } - - arith_excp(hw_exc, 1ull << regno); - } -} - -/* Raise exceptions for ieee fp insns with software completion. */ -void helper_fp_exc_raise_s(uint32_t exc, uint32_t regno) -{ - if (exc) { - env->fpcr_exc_status |= exc; - - exc &= ~env->fpcr_exc_mask; - if (exc) { - helper_fp_exc_raise(exc, regno); - } - } -} - -/* Input remapping without software completion. Handle denormal-map-to-zero - and trap for all other non-finite numbers. */ -uint64_t helper_ieee_input(uint64_t val) -{ - uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; - uint64_t frac = val & 0xfffffffffffffull; - - if (exp == 0) { - if (frac != 0) { - /* If DNZ is set flush denormals to zero on input. */ - if (env->fpcr_dnz) { - val &= 1ull << 63; - } else { - arith_excp(EXC_M_UNF, 0); - } - } - } else if (exp == 0x7ff) { - /* Infinity or NaN. */ - /* ??? I'm not sure these exception bit flags are correct. I do - know that the Linux kernel, at least, doesn't rely on them and - just emulates the insn to figure out what exception to use. */ - arith_excp(frac ? EXC_M_INV : EXC_M_FOV, 0); - } - return val; -} - -/* Similar, but does not trap for infinities. Used for comparisons. */ -uint64_t helper_ieee_input_cmp(uint64_t val) -{ - uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; - uint64_t frac = val & 0xfffffffffffffull; - - if (exp == 0) { - if (frac != 0) { - /* If DNZ is set flush denormals to zero on input. */ - if (env->fpcr_dnz) { - val &= 1ull << 63; - } else { - arith_excp(EXC_M_UNF, 0); - } - } - } else if (exp == 0x7ff && frac) { - /* NaN. */ - arith_excp(EXC_M_INV, 0); - } - return val; -} - -/* Input remapping with software completion enabled. All we have to do - is handle denormal-map-to-zero; all other inputs get exceptions as - needed from the actual operation. */ -uint64_t helper_ieee_input_s(uint64_t val) -{ - if (env->fpcr_dnz) { - uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; - if (exp == 0) { - val &= 1ull << 63; - } - } - return val; -} - -/* F floating (VAX) */ -static inline uint64_t float32_to_f(float32 fa) -{ - uint64_t r, exp, mant, sig; - CPU_FloatU a; - - a.f = fa; - sig = ((uint64_t)a.l & 0x80000000) << 32; - exp = (a.l >> 23) & 0xff; - mant = ((uint64_t)a.l & 0x007fffff) << 29; - - if (exp == 255) { - /* NaN or infinity */ - r = 1; /* VAX dirty zero */ - } else if (exp == 0) { - if (mant == 0) { - /* Zero */ - r = 0; - } else { - /* Denormalized */ - r = sig | ((exp + 1) << 52) | mant; - } - } else { - if (exp >= 253) { - /* Overflow */ - r = 1; /* VAX dirty zero */ - } else { - r = sig | ((exp + 2) << 52); - } - } - - return r; -} - -static inline float32 f_to_float32(uint64_t a) -{ - uint32_t exp, mant_sig; - CPU_FloatU r; - - exp = ((a >> 55) & 0x80) | ((a >> 52) & 0x7f); - mant_sig = ((a >> 32) & 0x80000000) | ((a >> 29) & 0x007fffff); - - if (unlikely(!exp && mant_sig)) { - /* Reserved operands / Dirty zero */ - dynamic_excp(EXCP_OPCDEC, 0); - } - - if (exp < 3) { - /* Underflow */ - r.l = 0; - } else { - r.l = ((exp - 2) << 23) | mant_sig; - } - - return r.f; -} - -uint32_t helper_f_to_memory (uint64_t a) -{ - uint32_t r; - r = (a & 0x00001fffe0000000ull) >> 13; - r |= (a & 0x07ffe00000000000ull) >> 45; - r |= (a & 0xc000000000000000ull) >> 48; - return r; -} - -uint64_t helper_memory_to_f (uint32_t a) -{ - uint64_t r; - r = ((uint64_t)(a & 0x0000c000)) << 48; - r |= ((uint64_t)(a & 0x003fffff)) << 45; - r |= ((uint64_t)(a & 0xffff0000)) << 13; - if (!(a & 0x00004000)) - r |= 0x7ll << 59; - return r; -} - -/* ??? Emulating VAX arithmetic with IEEE arithmetic is wrong. We should - either implement VAX arithmetic properly or just signal invalid opcode. */ - -uint64_t helper_addf (uint64_t a, uint64_t b) -{ - float32 fa, fb, fr; - - fa = f_to_float32(a); - fb = f_to_float32(b); - fr = float32_add(fa, fb, &FP_STATUS); - return float32_to_f(fr); -} - -uint64_t helper_subf (uint64_t a, uint64_t b) -{ - float32 fa, fb, fr; - - fa = f_to_float32(a); - fb = f_to_float32(b); - fr = float32_sub(fa, fb, &FP_STATUS); - return float32_to_f(fr); -} - -uint64_t helper_mulf (uint64_t a, uint64_t b) -{ - float32 fa, fb, fr; - - fa = f_to_float32(a); - fb = f_to_float32(b); - fr = float32_mul(fa, fb, &FP_STATUS); - return float32_to_f(fr); -} - -uint64_t helper_divf (uint64_t a, uint64_t b) -{ - float32 fa, fb, fr; - - fa = f_to_float32(a); - fb = f_to_float32(b); - fr = float32_div(fa, fb, &FP_STATUS); - return float32_to_f(fr); -} - -uint64_t helper_sqrtf (uint64_t t) -{ - float32 ft, fr; - - ft = f_to_float32(t); - fr = float32_sqrt(ft, &FP_STATUS); - return float32_to_f(fr); -} - - -/* G floating (VAX) */ -static inline uint64_t float64_to_g(float64 fa) -{ - uint64_t r, exp, mant, sig; - CPU_DoubleU a; - - a.d = fa; - sig = a.ll & 0x8000000000000000ull; - exp = (a.ll >> 52) & 0x7ff; - mant = a.ll & 0x000fffffffffffffull; - - if (exp == 2047) { - /* NaN or infinity */ - r = 1; /* VAX dirty zero */ - } else if (exp == 0) { - if (mant == 0) { - /* Zero */ - r = 0; - } else { - /* Denormalized */ - r = sig | ((exp + 1) << 52) | mant; - } - } else { - if (exp >= 2045) { - /* Overflow */ - r = 1; /* VAX dirty zero */ - } else { - r = sig | ((exp + 2) << 52); - } - } - - return r; -} - -static inline float64 g_to_float64(uint64_t a) -{ - uint64_t exp, mant_sig; - CPU_DoubleU r; - - exp = (a >> 52) & 0x7ff; - mant_sig = a & 0x800fffffffffffffull; - - if (!exp && mant_sig) { - /* Reserved operands / Dirty zero */ - dynamic_excp(EXCP_OPCDEC, 0); - } - - if (exp < 3) { - /* Underflow */ - r.ll = 0; - } else { - r.ll = ((exp - 2) << 52) | mant_sig; - } - - return r.d; -} - -uint64_t helper_g_to_memory (uint64_t a) -{ - uint64_t r; - r = (a & 0x000000000000ffffull) << 48; - r |= (a & 0x00000000ffff0000ull) << 16; - r |= (a & 0x0000ffff00000000ull) >> 16; - r |= (a & 0xffff000000000000ull) >> 48; - return r; -} - -uint64_t helper_memory_to_g (uint64_t a) -{ - uint64_t r; - r = (a & 0x000000000000ffffull) << 48; - r |= (a & 0x00000000ffff0000ull) << 16; - r |= (a & 0x0000ffff00000000ull) >> 16; - r |= (a & 0xffff000000000000ull) >> 48; - return r; -} - -uint64_t helper_addg (uint64_t a, uint64_t b) -{ - float64 fa, fb, fr; - - fa = g_to_float64(a); - fb = g_to_float64(b); - fr = float64_add(fa, fb, &FP_STATUS); - return float64_to_g(fr); -} - -uint64_t helper_subg (uint64_t a, uint64_t b) -{ - float64 fa, fb, fr; - - fa = g_to_float64(a); - fb = g_to_float64(b); - fr = float64_sub(fa, fb, &FP_STATUS); - return float64_to_g(fr); -} - -uint64_t helper_mulg (uint64_t a, uint64_t b) -{ - float64 fa, fb, fr; - - fa = g_to_float64(a); - fb = g_to_float64(b); - fr = float64_mul(fa, fb, &FP_STATUS); - return float64_to_g(fr); -} - -uint64_t helper_divg (uint64_t a, uint64_t b) -{ - float64 fa, fb, fr; - - fa = g_to_float64(a); - fb = g_to_float64(b); - fr = float64_div(fa, fb, &FP_STATUS); - return float64_to_g(fr); -} - -uint64_t helper_sqrtg (uint64_t a) -{ - float64 fa, fr; - - fa = g_to_float64(a); - fr = float64_sqrt(fa, &FP_STATUS); - return float64_to_g(fr); -} - - -/* S floating (single) */ - -/* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */ -static inline uint64_t float32_to_s_int(uint32_t fi) -{ - uint32_t frac = fi & 0x7fffff; - uint32_t sign = fi >> 31; - uint32_t exp_msb = (fi >> 30) & 1; - uint32_t exp_low = (fi >> 23) & 0x7f; - uint32_t exp; - - exp = (exp_msb << 10) | exp_low; - if (exp_msb) { - if (exp_low == 0x7f) - exp = 0x7ff; - } else { - if (exp_low != 0x00) - exp |= 0x380; - } - - return (((uint64_t)sign << 63) - | ((uint64_t)exp << 52) - | ((uint64_t)frac << 29)); -} - -static inline uint64_t float32_to_s(float32 fa) -{ - CPU_FloatU a; - a.f = fa; - return float32_to_s_int(a.l); -} - -static inline uint32_t s_to_float32_int(uint64_t a) -{ - return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff); -} - -static inline float32 s_to_float32(uint64_t a) -{ - CPU_FloatU r; - r.l = s_to_float32_int(a); - return r.f; -} - -uint32_t helper_s_to_memory (uint64_t a) -{ - return s_to_float32_int(a); -} - -uint64_t helper_memory_to_s (uint32_t a) -{ - return float32_to_s_int(a); -} - -uint64_t helper_adds (uint64_t a, uint64_t b) -{ - float32 fa, fb, fr; - - fa = s_to_float32(a); - fb = s_to_float32(b); - fr = float32_add(fa, fb, &FP_STATUS); - return float32_to_s(fr); -} - -uint64_t helper_subs (uint64_t a, uint64_t b) -{ - float32 fa, fb, fr; - - fa = s_to_float32(a); - fb = s_to_float32(b); - fr = float32_sub(fa, fb, &FP_STATUS); - return float32_to_s(fr); -} - -uint64_t helper_muls (uint64_t a, uint64_t b) -{ - float32 fa, fb, fr; - - fa = s_to_float32(a); - fb = s_to_float32(b); - fr = float32_mul(fa, fb, &FP_STATUS); - return float32_to_s(fr); -} - -uint64_t helper_divs (uint64_t a, uint64_t b) -{ - float32 fa, fb, fr; - - fa = s_to_float32(a); - fb = s_to_float32(b); - fr = float32_div(fa, fb, &FP_STATUS); - return float32_to_s(fr); -} - -uint64_t helper_sqrts (uint64_t a) -{ - float32 fa, fr; - - fa = s_to_float32(a); - fr = float32_sqrt(fa, &FP_STATUS); - return float32_to_s(fr); -} - - -/* T floating (double) */ -static inline float64 t_to_float64(uint64_t a) -{ - /* Memory format is the same as float64 */ - CPU_DoubleU r; - r.ll = a; - return r.d; -} - -static inline uint64_t float64_to_t(float64 fa) -{ - /* Memory format is the same as float64 */ - CPU_DoubleU r; - r.d = fa; - return r.ll; -} - -uint64_t helper_addt (uint64_t a, uint64_t b) -{ - float64 fa, fb, fr; - - fa = t_to_float64(a); - fb = t_to_float64(b); - fr = float64_add(fa, fb, &FP_STATUS); - return float64_to_t(fr); -} - -uint64_t helper_subt (uint64_t a, uint64_t b) -{ - float64 fa, fb, fr; - - fa = t_to_float64(a); - fb = t_to_float64(b); - fr = float64_sub(fa, fb, &FP_STATUS); - return float64_to_t(fr); -} - -uint64_t helper_mult (uint64_t a, uint64_t b) -{ - float64 fa, fb, fr; - - fa = t_to_float64(a); - fb = t_to_float64(b); - fr = float64_mul(fa, fb, &FP_STATUS); - return float64_to_t(fr); -} - -uint64_t helper_divt (uint64_t a, uint64_t b) -{ - float64 fa, fb, fr; - - fa = t_to_float64(a); - fb = t_to_float64(b); - fr = float64_div(fa, fb, &FP_STATUS); - return float64_to_t(fr); -} - -uint64_t helper_sqrtt (uint64_t a) -{ - float64 fa, fr; - - fa = t_to_float64(a); - fr = float64_sqrt(fa, &FP_STATUS); - return float64_to_t(fr); -} - -/* Comparisons */ -uint64_t helper_cmptun (uint64_t a, uint64_t b) -{ - float64 fa, fb; - - fa = t_to_float64(a); - fb = t_to_float64(b); - - if (float64_unordered_quiet(fa, fb, &FP_STATUS)) { - return 0x4000000000000000ULL; - } else { - return 0; - } -} - -uint64_t helper_cmpteq(uint64_t a, uint64_t b) -{ - float64 fa, fb; - - fa = t_to_float64(a); - fb = t_to_float64(b); - - if (float64_eq_quiet(fa, fb, &FP_STATUS)) - return 0x4000000000000000ULL; - else - return 0; -} - -uint64_t helper_cmptle(uint64_t a, uint64_t b) -{ - float64 fa, fb; - - fa = t_to_float64(a); - fb = t_to_float64(b); - - if (float64_le(fa, fb, &FP_STATUS)) - return 0x4000000000000000ULL; - else - return 0; -} - -uint64_t helper_cmptlt(uint64_t a, uint64_t b) -{ - float64 fa, fb; - - fa = t_to_float64(a); - fb = t_to_float64(b); - - if (float64_lt(fa, fb, &FP_STATUS)) - return 0x4000000000000000ULL; - else - return 0; -} - -uint64_t helper_cmpgeq(uint64_t a, uint64_t b) -{ - float64 fa, fb; - - fa = g_to_float64(a); - fb = g_to_float64(b); - - if (float64_eq_quiet(fa, fb, &FP_STATUS)) - return 0x4000000000000000ULL; - else - return 0; -} - -uint64_t helper_cmpgle(uint64_t a, uint64_t b) -{ - float64 fa, fb; - - fa = g_to_float64(a); - fb = g_to_float64(b); - - if (float64_le(fa, fb, &FP_STATUS)) - return 0x4000000000000000ULL; - else - return 0; -} - -uint64_t helper_cmpglt(uint64_t a, uint64_t b) -{ - float64 fa, fb; - - fa = g_to_float64(a); - fb = g_to_float64(b); - - if (float64_lt(fa, fb, &FP_STATUS)) - return 0x4000000000000000ULL; - else - return 0; -} - -/* Floating point format conversion */ -uint64_t helper_cvtts (uint64_t a) -{ - float64 fa; - float32 fr; - - fa = t_to_float64(a); - fr = float64_to_float32(fa, &FP_STATUS); - return float32_to_s(fr); -} - -uint64_t helper_cvtst (uint64_t a) -{ - float32 fa; - float64 fr; - - fa = s_to_float32(a); - fr = float32_to_float64(fa, &FP_STATUS); - return float64_to_t(fr); -} - -uint64_t helper_cvtqs (uint64_t a) -{ - float32 fr = int64_to_float32(a, &FP_STATUS); - return float32_to_s(fr); -} - -/* Implement float64 to uint64 conversion without saturation -- we must - supply the truncated result. This behaviour is used by the compiler - to get unsigned conversion for free with the same instruction. - - The VI flag is set when overflow or inexact exceptions should be raised. */ - -static inline uint64_t helper_cvttq_internal(uint64_t a, int roundmode, int VI) -{ - uint64_t frac, ret = 0; - uint32_t exp, sign, exc = 0; - int shift; - - sign = (a >> 63); - exp = (uint32_t)(a >> 52) & 0x7ff; - frac = a & 0xfffffffffffffull; - - if (exp == 0) { - if (unlikely(frac != 0)) { - goto do_underflow; - } - } else if (exp == 0x7ff) { - exc = (frac ? float_flag_invalid : VI ? float_flag_overflow : 0); - } else { - /* Restore implicit bit. */ - frac |= 0x10000000000000ull; - - shift = exp - 1023 - 52; - if (shift >= 0) { - /* In this case the number is so large that we must shift - the fraction left. There is no rounding to do. */ - if (shift < 63) { - ret = frac << shift; - if (VI && (ret >> shift) != frac) { - exc = float_flag_overflow; - } - } - } else { - uint64_t round; - - /* In this case the number is smaller than the fraction as - represented by the 52 bit number. Here we must think - about rounding the result. Handle this by shifting the - fractional part of the number into the high bits of ROUND. - This will let us efficiently handle round-to-nearest. */ - shift = -shift; - if (shift < 63) { - ret = frac >> shift; - round = frac << (64 - shift); - } else { - /* The exponent is so small we shift out everything. - Leave a sticky bit for proper rounding below. */ - do_underflow: - round = 1; - } - - if (round) { - exc = (VI ? float_flag_inexact : 0); - switch (roundmode) { - case float_round_nearest_even: - if (round == (1ull << 63)) { - /* Fraction is exactly 0.5; round to even. */ - ret += (ret & 1); - } else if (round > (1ull << 63)) { - ret += 1; - } - break; - case float_round_to_zero: - break; - case float_round_up: - ret += 1 - sign; - break; - case float_round_down: - ret += sign; - break; - } - } - } - if (sign) { - ret = -ret; - } - } - if (unlikely(exc)) { - float_raise(exc, &FP_STATUS); - } - - return ret; -} - -uint64_t helper_cvttq(uint64_t a) -{ - return helper_cvttq_internal(a, FP_STATUS.float_rounding_mode, 1); -} - -uint64_t helper_cvttq_c(uint64_t a) -{ - return helper_cvttq_internal(a, float_round_to_zero, 0); -} - -uint64_t helper_cvttq_svic(uint64_t a) -{ - return helper_cvttq_internal(a, float_round_to_zero, 1); -} - -uint64_t helper_cvtqt (uint64_t a) -{ - float64 fr = int64_to_float64(a, &FP_STATUS); - return float64_to_t(fr); -} - -uint64_t helper_cvtqf (uint64_t a) -{ - float32 fr = int64_to_float32(a, &FP_STATUS); - return float32_to_f(fr); -} - -uint64_t helper_cvtgf (uint64_t a) -{ - float64 fa; - float32 fr; - - fa = g_to_float64(a); - fr = float64_to_float32(fa, &FP_STATUS); - return float32_to_f(fr); -} - -uint64_t helper_cvtgq (uint64_t a) -{ - float64 fa = g_to_float64(a); - return float64_to_int64_round_to_zero(fa, &FP_STATUS); -} - -uint64_t helper_cvtqg (uint64_t a) -{ - float64 fr; - fr = int64_to_float64(a, &FP_STATUS); - return float64_to_g(fr); -} - -/* PALcode support special instructions */ -#if !defined (CONFIG_USER_ONLY) -void helper_hw_ret (uint64_t a) -{ - env->pc = a & ~3; - env->intr_flag = 0; - env->lock_addr = -1; - if ((a & 1) == 0) { - env->pal_mode = 0; - swap_shadow_regs(env); - } -} - -void helper_tbia(void) -{ - tlb_flush(env, 1); -} - -void helper_tbis(uint64_t p) -{ - tlb_flush_page(env, p); -} - -void helper_halt(uint64_t restart) -{ - if (restart) { - qemu_system_reset_request(); - } else { - qemu_system_shutdown_request(); - } -} - -uint64_t helper_get_time(void) -{ - return qemu_get_clock_ns(rtc_clock); -} - -void helper_set_alarm(uint64_t expire) -{ - if (expire) { - env->alarm_expire = expire; - qemu_mod_timer(env->alarm_timer, expire); - } else { - qemu_del_timer(env->alarm_timer); - } -} -#endif - -/*****************************************************************************/ -/* Softmmu support */ -#if !defined (CONFIG_USER_ONLY) -uint64_t helper_ldl_phys(uint64_t p) -{ - return (int32_t)ldl_phys(p); -} - -uint64_t helper_ldq_phys(uint64_t p) -{ - return ldq_phys(p); -} - -uint64_t helper_ldl_l_phys(uint64_t p) -{ - env->lock_addr = p; - return env->lock_value = (int32_t)ldl_phys(p); -} - -uint64_t helper_ldq_l_phys(uint64_t p) -{ - env->lock_addr = p; - return env->lock_value = ldl_phys(p); -} - -void helper_stl_phys(uint64_t p, uint64_t v) -{ - stl_phys(p, v); -} - -void helper_stq_phys(uint64_t p, uint64_t v) -{ - stq_phys(p, v); -} - -uint64_t helper_stl_c_phys(uint64_t p, uint64_t v) -{ - uint64_t ret = 0; - - if (p == env->lock_addr) { - int32_t old = ldl_phys(p); - if (old == (int32_t)env->lock_value) { - stl_phys(p, v); - ret = 1; - } - } - env->lock_addr = -1; - - return ret; -} - -uint64_t helper_stq_c_phys(uint64_t p, uint64_t v) -{ - uint64_t ret = 0; - - if (p == env->lock_addr) { - uint64_t old = ldq_phys(p); - if (old == env->lock_value) { - stq_phys(p, v); - ret = 1; - } - } - env->lock_addr = -1; - - return ret; -} - -static void QEMU_NORETURN do_unaligned_access(target_ulong addr, int is_write, - int is_user, void *retaddr) -{ - uint64_t pc; - uint32_t insn; - - do_restore_state(retaddr); - - pc = env->pc; - insn = ldl_code(pc); - - env->trap_arg0 = addr; - env->trap_arg1 = insn >> 26; /* opcode */ - env->trap_arg2 = (insn >> 21) & 31; /* dest regno */ - helper_excp(EXCP_UNALIGN, 0); -} - -void QEMU_NORETURN cpu_unassigned_access(CPUAlphaState *env1, - target_phys_addr_t addr, int is_write, - int is_exec, int unused, int size) -{ - env = env1; - env->trap_arg0 = addr; - env->trap_arg1 = is_write; - dynamic_excp(EXCP_MCHK, 0); -} - -#include "softmmu_exec.h" - -#define MMUSUFFIX _mmu -#define ALIGNED_ONLY - -#define SHIFT 0 -#include "softmmu_template.h" - -#define SHIFT 1 -#include "softmmu_template.h" - -#define SHIFT 2 -#include "softmmu_template.h" - -#define SHIFT 3 -#include "softmmu_template.h" - -/* try to fill the TLB and return an exception if error. If retaddr is - NULL, it means that the function was called in C code (i.e. not - from generated code or from helper.c) */ -/* XXX: fix it to restore all registers */ -void tlb_fill(CPUAlphaState *env1, target_ulong addr, int is_write, int mmu_idx, - void *retaddr) -{ - CPUAlphaState *saved_env; - int ret; - - saved_env = env; - env = env1; - ret = cpu_alpha_handle_mmu_fault(env, addr, is_write, mmu_idx); - if (unlikely(ret != 0)) { - do_restore_state(retaddr); - /* Exception index and error code are already set */ - cpu_loop_exit(env); - } - env = saved_env; -} -#endif diff --git a/target-alpha/sys_helper.c b/target-alpha/sys_helper.c new file mode 100644 index 0000000000..40ca49c883 --- /dev/null +++ b/target-alpha/sys_helper.c @@ -0,0 +1,87 @@ +/* + * Helpers for system instructions. + * + * Copyright (c) 2007 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "helper.h" +#include "sysemu.h" +#include "qemu-timer.h" + + +uint64_t helper_load_pcc(CPUAlphaState *env) +{ +#ifndef CONFIG_USER_ONLY + /* In system mode we have access to a decent high-resolution clock. + In order to make OS-level time accounting work with the RPCC, + present it with a well-timed clock fixed at 250MHz. */ + return (((uint64_t)env->pcc_ofs << 32) + | (uint32_t)(qemu_get_clock_ns(vm_clock) >> 2)); +#else + /* In user-mode, vm_clock doesn't exist. Just pass through the host cpu + clock ticks. Also, don't bother taking PCC_OFS into account. */ + return (uint32_t)cpu_get_real_ticks(); +#endif +} + +/* PALcode support special instructions */ +#ifndef CONFIG_USER_ONLY +void helper_hw_ret(CPUAlphaState *env, uint64_t a) +{ + env->pc = a & ~3; + env->intr_flag = 0; + env->lock_addr = -1; + if ((a & 1) == 0) { + env->pal_mode = 0; + swap_shadow_regs(env); + } +} + +void helper_tbia(CPUAlphaState *env) +{ + tlb_flush(env, 1); +} + +void helper_tbis(CPUAlphaState *env, uint64_t p) +{ + tlb_flush_page(env, p); +} + +void helper_halt(uint64_t restart) +{ + if (restart) { + qemu_system_reset_request(); + } else { + qemu_system_shutdown_request(); + } +} + +uint64_t helper_get_time(void) +{ + return qemu_get_clock_ns(rtc_clock); +} + +void helper_set_alarm(CPUAlphaState *env, uint64_t expire) +{ + if (expire) { + env->alarm_expire = expire; + qemu_mod_timer(env->alarm_timer, expire); + } else { + qemu_del_timer(env->alarm_timer); + } +} +#endif /* CONFIG_USER_ONLY */ diff --git a/target-alpha/translate.c b/target-alpha/translate.c index b51fe5c8da..1f4565d794 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -149,7 +149,7 @@ static void gen_excp_1(int exception, int error_code) tmp1 = tcg_const_i32(exception); tmp2 = tcg_const_i32(error_code); - gen_helper_excp(tmp1, tmp2); + gen_helper_excp(cpu_env, tmp1, tmp2); tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp1); } @@ -611,7 +611,8 @@ static void gen_qual_roundmode(DisasContext *ctx, int fn11) tcg_gen_movi_i32(tmp, float_round_down); break; case QUAL_RM_D: - tcg_gen_ld8u_i32(tmp, cpu_env, offsetof(CPUAlphaState, fpcr_dyn_round)); + tcg_gen_ld8u_i32(tmp, cpu_env, + offsetof(CPUAlphaState, fpcr_dyn_round)); break; } @@ -641,7 +642,8 @@ static void gen_qual_flushzero(DisasContext *ctx, int fn11) tmp = tcg_temp_new_i32(); if (fn11) { /* Underflow is enabled, use the FPCR setting. */ - tcg_gen_ld8u_i32(tmp, cpu_env, offsetof(CPUAlphaState, fpcr_flush_to_zero)); + tcg_gen_ld8u_i32(tmp, cpu_env, + offsetof(CPUAlphaState, fpcr_flush_to_zero)); } else { /* Underflow is disabled, force flush-to-zero. */ tcg_gen_movi_i32(tmp, 1); @@ -659,15 +661,19 @@ static void gen_qual_flushzero(DisasContext *ctx, int fn11) static TCGv gen_ieee_input(int reg, int fn11, int is_cmp) { - TCGv val = tcg_temp_new(); + TCGv val; if (reg == 31) { - tcg_gen_movi_i64(val, 0); - } else if (fn11 & QUAL_S) { - gen_helper_ieee_input_s(val, cpu_fir[reg]); - } else if (is_cmp) { - gen_helper_ieee_input_cmp(val, cpu_fir[reg]); + val = tcg_const_i64(0); } else { - gen_helper_ieee_input(val, cpu_fir[reg]); + if ((fn11 & QUAL_S) == 0) { + if (is_cmp) { + gen_helper_ieee_input_cmp(cpu_env, cpu_fir[reg]); + } else { + gen_helper_ieee_input(cpu_env, cpu_fir[reg]); + } + } + val = tcg_temp_new(); + tcg_gen_mov_i64(val, cpu_fir[reg]); } return val; } @@ -680,7 +686,7 @@ static void gen_fp_exc_clear(void) offsetof(CPUAlphaState, fp_status.float_exception_flags)); tcg_temp_free_i32(zero); #else - gen_helper_fp_exc_clear(); + gen_helper_fp_exc_clear(cpu_env); #endif } @@ -698,7 +704,7 @@ static void gen_fp_exc_raise_ignore(int rc, int fn11, int ignore) tcg_gen_ld8u_i32(exc, cpu_env, offsetof(CPUAlphaState, fp_status.float_exception_flags)); #else - gen_helper_fp_exc_get(exc); + gen_helper_fp_exc_get(exc, cpu_env); #endif if (ignore) { @@ -713,9 +719,9 @@ static void gen_fp_exc_raise_ignore(int rc, int fn11, int ignore) reg = tcg_const_i32(rc + 32); if (fn11 & QUAL_S) { - gen_helper_fp_exc_raise_s(exc, reg); + gen_helper_fp_exc_raise_s(cpu_env, exc, reg); } else { - gen_helper_fp_exc_raise(exc, reg); + gen_helper_fp_exc_raise(cpu_env, exc, reg); } tcg_temp_free_i32(reg); @@ -784,20 +790,20 @@ static void gen_fcvtql_v(DisasContext *ctx, int rb, int rc) gen_fcvtql(rb, rc); } -#define FARITH2(name) \ -static inline void glue(gen_f, name)(int rb, int rc) \ -{ \ - if (unlikely(rc == 31)) { \ - return; \ - } \ - if (rb != 31) { \ - gen_helper_ ## name (cpu_fir[rc], cpu_fir[rb]); \ - } else { \ - TCGv tmp = tcg_const_i64(0); \ - gen_helper_ ## name (cpu_fir[rc], tmp); \ - tcg_temp_free(tmp); \ - } \ -} +#define FARITH2(name) \ + static inline void glue(gen_f, name)(int rb, int rc) \ + { \ + if (unlikely(rc == 31)) { \ + return; \ + } \ + if (rb != 31) { \ + gen_helper_ ## name(cpu_fir[rc], cpu_env, cpu_fir[rb]); \ + } else { \ + TCGv tmp = tcg_const_i64(0); \ + gen_helper_ ## name(cpu_fir[rc], cpu_env, tmp); \ + tcg_temp_free(tmp); \ + } \ + } /* ??? VAX instruction qualifiers ignored. */ FARITH2(sqrtf) @@ -807,7 +813,8 @@ FARITH2(cvtgq) FARITH2(cvtqf) FARITH2(cvtqg) -static void gen_ieee_arith2(DisasContext *ctx, void (*helper)(TCGv, TCGv), +static void gen_ieee_arith2(DisasContext *ctx, + void (*helper)(TCGv, TCGv_ptr, TCGv), int rb, int rc, int fn11) { TCGv vb; @@ -823,7 +830,7 @@ static void gen_ieee_arith2(DisasContext *ctx, void (*helper)(TCGv, TCGv), gen_fp_exc_clear(); vb = gen_ieee_input(rb, fn11, 0); - helper(cpu_fir[rc], vb); + helper(cpu_fir[rc], cpu_env, vb); tcg_temp_free(vb); gen_fp_exc_raise(rc, fn11); @@ -859,18 +866,18 @@ static void gen_fcvttq(DisasContext *ctx, int rb, int rc, int fn11) also do not have integer overflow enabled. Special case that. */ switch (fn11) { case QUAL_RM_C: - gen_helper_cvttq_c(cpu_fir[rc], vb); + gen_helper_cvttq_c(cpu_fir[rc], cpu_env, vb); break; case QUAL_V | QUAL_RM_C: case QUAL_S | QUAL_V | QUAL_RM_C: ignore = float_flag_inexact; /* FALLTHRU */ case QUAL_S | QUAL_V | QUAL_I | QUAL_RM_C: - gen_helper_cvttq_svic(cpu_fir[rc], vb); + gen_helper_cvttq_svic(cpu_fir[rc], cpu_env, vb); break; default: gen_qual_roundmode(ctx, fn11); - gen_helper_cvttq(cpu_fir[rc], vb); + gen_helper_cvttq(cpu_fir[rc], cpu_env, vb); ignore |= (fn11 & QUAL_V ? 0 : float_flag_overflow); ignore |= (fn11 & QUAL_I ? 0 : float_flag_inexact); break; @@ -880,7 +887,8 @@ static void gen_fcvttq(DisasContext *ctx, int rb, int rc, int fn11) gen_fp_exc_raise_ignore(rc, fn11, ignore); } -static void gen_ieee_intcvt(DisasContext *ctx, void (*helper)(TCGv, TCGv), +static void gen_ieee_intcvt(DisasContext *ctx, + void (*helper)(TCGv, TCGv_ptr, TCGv), int rb, int rc, int fn11) { TCGv vb; @@ -904,10 +912,10 @@ static void gen_ieee_intcvt(DisasContext *ctx, void (*helper)(TCGv, TCGv), inexact handling is requested. */ if (fn11 & QUAL_I) { gen_fp_exc_clear(); - helper(cpu_fir[rc], vb); + helper(cpu_fir[rc], cpu_env, vb); gen_fp_exc_raise(rc, fn11); } else { - helper(cpu_fir[rc], vb); + helper(cpu_fir[rc], cpu_env, vb); } if (rb == 31) { @@ -999,34 +1007,34 @@ static inline void gen_fcpyse(int ra, int rb, int rc) gen_cpys_internal(ra, rb, rc, 0, 0xFFF0000000000000ULL); } -#define FARITH3(name) \ -static inline void glue(gen_f, name)(int ra, int rb, int rc) \ -{ \ - TCGv va, vb; \ - \ - if (unlikely(rc == 31)) { \ - return; \ - } \ - if (ra == 31) { \ - va = tcg_const_i64(0); \ - } else { \ - va = cpu_fir[ra]; \ - } \ - if (rb == 31) { \ - vb = tcg_const_i64(0); \ - } else { \ - vb = cpu_fir[rb]; \ - } \ - \ - gen_helper_ ## name (cpu_fir[rc], va, vb); \ - \ - if (ra == 31) { \ - tcg_temp_free(va); \ - } \ - if (rb == 31) { \ - tcg_temp_free(vb); \ - } \ -} +#define FARITH3(name) \ + static inline void glue(gen_f, name)(int ra, int rb, int rc) \ + { \ + TCGv va, vb; \ + \ + if (unlikely(rc == 31)) { \ + return; \ + } \ + if (ra == 31) { \ + va = tcg_const_i64(0); \ + } else { \ + va = cpu_fir[ra]; \ + } \ + if (rb == 31) { \ + vb = tcg_const_i64(0); \ + } else { \ + vb = cpu_fir[rb]; \ + } \ + \ + gen_helper_ ## name(cpu_fir[rc], cpu_env, va, vb); \ + \ + if (ra == 31) { \ + tcg_temp_free(va); \ + } \ + if (rb == 31) { \ + tcg_temp_free(vb); \ + } \ + } /* ??? VAX instruction qualifiers ignored. */ FARITH3(addf) @@ -1042,7 +1050,7 @@ FARITH3(cmpglt) FARITH3(cmpgle) static void gen_ieee_arith3(DisasContext *ctx, - void (*helper)(TCGv, TCGv, TCGv), + void (*helper)(TCGv, TCGv_ptr, TCGv, TCGv), int ra, int rb, int rc, int fn11) { TCGv va, vb; @@ -1059,7 +1067,7 @@ static void gen_ieee_arith3(DisasContext *ctx, va = gen_ieee_input(ra, fn11, 0); vb = gen_ieee_input(rb, fn11, 0); - helper(cpu_fir[rc], va, vb); + helper(cpu_fir[rc], cpu_env, va, vb); tcg_temp_free(va); tcg_temp_free(vb); @@ -1082,7 +1090,7 @@ IEEE_ARITH3(mult) IEEE_ARITH3(divt) static void gen_ieee_compare(DisasContext *ctx, - void (*helper)(TCGv, TCGv, TCGv), + void (*helper)(TCGv, TCGv_ptr, TCGv, TCGv), int ra, int rb, int rc, int fn11) { TCGv va, vb; @@ -1097,7 +1105,7 @@ static void gen_ieee_compare(DisasContext *ctx, va = gen_ieee_input(ra, fn11, 1); vb = gen_ieee_input(rb, fn11, 1); - helper(cpu_fir[rc], va, vb); + helper(cpu_fir[rc], cpu_env, va, vb); tcg_temp_free(va); tcg_temp_free(vb); @@ -1388,14 +1396,8 @@ static inline void glue(gen_, name)(int ra, int rb, int rc, int islit,\ tcg_temp_free(tmp1); \ } \ } -ARITH3(cmpbge) -ARITH3(addlv) -ARITH3(sublv) -ARITH3(addqv) -ARITH3(subqv) ARITH3(umulh) -ARITH3(mullv) -ARITH3(mulqv) +ARITH3(cmpbge) ARITH3(minub8) ARITH3(minsb8) ARITH3(minuw4) @@ -1406,6 +1408,43 @@ ARITH3(maxuw4) ARITH3(maxsw4) ARITH3(perr) +/* Code to call arith3 helpers */ +#define ARITH3_EX(name) \ + static inline void glue(gen_, name)(int ra, int rb, int rc, \ + int islit, uint8_t lit) \ + { \ + if (unlikely(rc == 31)) { \ + return; \ + } \ + if (ra != 31) { \ + if (islit) { \ + TCGv tmp = tcg_const_i64(lit); \ + gen_helper_ ## name(cpu_ir[rc], cpu_env, \ + cpu_ir[ra], tmp); \ + tcg_temp_free(tmp); \ + } else { \ + gen_helper_ ## name(cpu_ir[rc], cpu_env, \ + cpu_ir[ra], cpu_ir[rb]); \ + } \ + } else { \ + TCGv tmp1 = tcg_const_i64(0); \ + if (islit) { \ + TCGv tmp2 = tcg_const_i64(lit); \ + gen_helper_ ## name(cpu_ir[rc], cpu_env, tmp1, tmp2); \ + tcg_temp_free(tmp2); \ + } else { \ + gen_helper_ ## name(cpu_ir[rc], cpu_env, tmp1, cpu_ir[rb]); \ + } \ + tcg_temp_free(tmp1); \ + } \ + } +ARITH3_EX(addlv) +ARITH3_EX(sublv) +ARITH3_EX(addqv) +ARITH3_EX(subqv) +ARITH3_EX(mullv) +ARITH3_EX(mulqv) + #define MVIOP2(name) \ static inline void glue(gen_, name)(int rb, int rc) \ { \ @@ -1643,12 +1682,12 @@ static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) switch (regno) { case 255: /* TBIA */ - gen_helper_tbia(); + gen_helper_tbia(cpu_env); break; case 254: /* TBIS */ - gen_helper_tbis(tmp); + gen_helper_tbis(cpu_env, tmp); break; case 253: @@ -1664,7 +1703,7 @@ static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) case 251: /* ALARM */ - gen_helper_set_alarm(tmp); + gen_helper_set_alarm(cpu_env, tmp); break; default: @@ -2674,17 +2713,17 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) case 0x024: /* MT_FPCR */ if (likely(ra != 31)) - gen_helper_store_fpcr(cpu_fir[ra]); + gen_helper_store_fpcr(cpu_env, cpu_fir[ra]); else { TCGv tmp = tcg_const_i64(0); - gen_helper_store_fpcr(tmp); + gen_helper_store_fpcr(cpu_env, tmp); tcg_temp_free(tmp); } break; case 0x025: /* MF_FPCR */ if (likely(ra != 31)) - gen_helper_load_fpcr(cpu_fir[ra]); + gen_helper_load_fpcr(cpu_fir[ra], cpu_env); break; case 0x02A: /* FCMOVEQ */ @@ -2758,11 +2797,11 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) if (ra != 31) { if (use_icount) { gen_io_start(); - gen_helper_load_pcc(cpu_ir[ra]); + gen_helper_load_pcc(cpu_ir[ra], cpu_env); gen_io_end(); ret = EXIT_PC_STALE; } else { - gen_helper_load_pcc(cpu_ir[ra]); + gen_helper_load_pcc(cpu_ir[ra], cpu_env); } } break; @@ -2832,11 +2871,11 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) break; case 0x2: /* Longword physical access with lock (hw_ldl_l/p) */ - gen_helper_ldl_l_phys(cpu_ir[ra], addr); + gen_helper_ldl_l_phys(cpu_ir[ra], cpu_env, addr); break; case 0x3: /* Quadword physical access with lock (hw_ldq_l/p) */ - gen_helper_ldq_l_phys(cpu_ir[ra], addr); + gen_helper_ldq_l_phys(cpu_ir[ra], cpu_env, addr); break; case 0x4: /* Longword virtual PTE fetch (hw_ldl/v) */ @@ -3108,10 +3147,10 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) emulation PALcode, so continue to accept it. */ TCGv tmp = tcg_temp_new(); tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUAlphaState, exc_addr)); - gen_helper_hw_ret(tmp); + gen_helper_hw_ret(cpu_env, tmp); tcg_temp_free(tmp); } else { - gen_helper_hw_ret(cpu_ir[rb]); + gen_helper_hw_ret(cpu_env, cpu_ir[rb]); } ret = EXIT_PC_UPDATED; break; @@ -3145,11 +3184,11 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) break; case 0x2: /* Longword physical access with lock */ - gen_helper_stl_c_phys(val, addr, val); + gen_helper_stl_c_phys(val, cpu_env, addr, val); break; case 0x3: /* Quadword physical access with lock */ - gen_helper_stq_c_phys(val, addr, val); + gen_helper_stq_c_phys(val, cpu_env, addr, val); break; case 0x4: /* Longword virtual access */ @@ -3385,7 +3424,7 @@ static inline void gen_intermediate_code_internal(CPUAlphaState *env, } if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); - insn = ldl_code(ctx.pc); + insn = cpu_ldl_code(env, ctx.pc); num_insns++; if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { @@ -3513,7 +3552,8 @@ CPUAlphaState * cpu_alpha_init (const char *cpu_model) #if defined (CONFIG_USER_ONLY) env->ps = PS_USER_MODE; cpu_alpha_store_fpcr(env, (FPCR_INVD | FPCR_DZED | FPCR_OVFD - | FPCR_UNFD | FPCR_INED | FPCR_DNOD)); + | FPCR_UNFD | FPCR_INED | FPCR_DNOD + | FPCR_DYN_NORMAL)); #endif env->lock_addr = -1; env->fen = 1; diff --git a/target-mips/helper.h b/target-mips/helper.h index 442f684697..76fb451e77 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -1,7 +1,7 @@ #include "def-helper.h" -DEF_HELPER_2(raise_exception_err, void, i32, int) -DEF_HELPER_1(raise_exception, void, i32) +DEF_HELPER_2(raise_exception_err, noreturn, i32, int) +DEF_HELPER_1(raise_exception, noreturn, i32) #ifdef TARGET_MIPS64 DEF_HELPER_3(ldl, tl, tl, tl, int) diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 3a207312a4..ce01225e6c 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -2292,7 +2292,8 @@ void helper_wait (void) #if !defined(CONFIG_USER_ONLY) -static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr); +static void QEMU_NORETURN do_unaligned_access(target_ulong addr, int is_write, + int is_user, void *retaddr); #define MMUSUFFIX _mmu #define ALIGNED_ONLY diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 1025752e8f..885ad45c3a 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -699,8 +699,9 @@ uint64_t cpu_tick_get_count(CPUTimer *timer); void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit); trap_state* cpu_tsptr(CPUSPARCState* env); #endif -void do_unaligned_access(CPUSPARCState *env, target_ulong addr, int is_write, - int is_user, void *retaddr); +void QEMU_NORETURN do_unaligned_access(CPUSPARCState *env, target_ulong addr, + int is_write, int is_user, + void *retaddr); #define TB_FLAG_FPU_ENABLED (1 << 4) #define TB_FLAG_AM_ENABLED (1 << 5) diff --git a/target-sparc/helper.h b/target-sparc/helper.h index c4d6225102..e3c7fddbe8 100644 --- a/target-sparc/helper.h +++ b/target-sparc/helper.h @@ -78,7 +78,7 @@ DEF_HELPER_1(fcmpeq_fcc1, void, env) DEF_HELPER_1(fcmpeq_fcc2, void, env) DEF_HELPER_1(fcmpeq_fcc3, void, env) #endif -DEF_HELPER_2(raise_exception, void, env, int) +DEF_HELPER_2(raise_exception, noreturn, env, int) #define F_HELPER_0_1(name) DEF_HELPER_1(f ## name, void, env) DEF_HELPER_3(faddd, f64, env, f64, f64) diff --git a/target-sparc/mmu_helper.c b/target-sparc/mmu_helper.c index 11fb9f5de6..cb73c449f0 100644 --- a/target-sparc/mmu_helper.c +++ b/target-sparc/mmu_helper.c @@ -150,18 +150,17 @@ static int get_physical_address(CPUSPARCState *env, target_phys_addr_t *physical case 3: /* Reserved */ return (3 << 8) | (4 << 2); case 2: /* L3 PTE */ - page_offset = (address & TARGET_PAGE_MASK) & - (TARGET_PAGE_SIZE - 1); + page_offset = 0; } *page_size = TARGET_PAGE_SIZE; break; case 2: /* L2 PTE */ - page_offset = address & 0x3ffff; + page_offset = address & 0x3f000; *page_size = 0x40000; } break; case 2: /* L1 PTE */ - page_offset = address & 0xffffff; + page_offset = address & 0xfff000; *page_size = 0x1000000; } } @@ -206,11 +205,11 @@ int cpu_sparc_handle_mmu_fault(CPUSPARCState *env, target_ulong address, int rw, target_ulong page_size; int error_code = 0, prot, access_index; + address &= TARGET_PAGE_MASK; error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, mmu_idx, &page_size); + vaddr = address; if (error_code == 0) { - vaddr = address & TARGET_PAGE_MASK; - paddr &= TARGET_PAGE_MASK; #ifdef DEBUG_MMU printf("Translate at " TARGET_FMT_lx " -> " TARGET_FMT_plx ", vaddr " TARGET_FMT_lx "\n", address, paddr, vaddr); @@ -230,7 +229,6 @@ int cpu_sparc_handle_mmu_fault(CPUSPARCState *env, target_ulong address, int rw, permissions. If no mapping is available, redirect accesses to neverland. Fake/overridden mappings will be flushed when switching to normal mode. */ - vaddr = address & TARGET_PAGE_MASK; prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; tlb_set_page(env, vaddr, paddr, prot, mmu_idx, TARGET_PAGE_SIZE); return 0; @@ -704,17 +702,16 @@ static int get_physical_address(CPUSPARCState *env, target_phys_addr_t *physical int cpu_sparc_handle_mmu_fault(CPUSPARCState *env, target_ulong address, int rw, int mmu_idx) { - target_ulong virt_addr, vaddr; + target_ulong vaddr; target_phys_addr_t paddr; target_ulong page_size; int error_code = 0, prot, access_index; + address &= TARGET_PAGE_MASK; error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, mmu_idx, &page_size); if (error_code == 0) { - virt_addr = address & TARGET_PAGE_MASK; - vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & - (TARGET_PAGE_SIZE - 1)); + vaddr = address; trace_mmu_helper_mmu_fault(address, paddr, mmu_idx, env->tl, env->dmmu.mmu_primary_context, diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c index e579ef06a1..2885212e3a 100644 --- a/tcg/hppa/tcg-target.c +++ b/tcg/hppa/tcg-target.c @@ -336,7 +336,7 @@ static int tcg_target_const_match(tcg_target_long val, #define INSN_COMIBF (INSN_OP(0x23)) /* supplied by libgcc */ -extern void *__canonicalize_funcptr_for_compare(void *); +extern void *__canonicalize_funcptr_for_compare(const void *); static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { @@ -628,7 +628,7 @@ static void tcg_out_bswap32(TCGContext *s, int ret, int arg, int temp) tcg_out_shd(s, ret, arg, temp, 8); /* ret = DCBA */ } -static void tcg_out_call(TCGContext *s, void *func) +static void tcg_out_call(TCGContext *s, const void *func) { tcg_target_long val, hi, lo, disp; @@ -1661,23 +1661,18 @@ static int tcg_target_callee_save_regs[] = { TCG_REG_R18 }; +#define FRAME_SIZE ((-TCG_TARGET_CALL_STACK_OFFSET \ + + TCG_TARGET_STATIC_CALL_ARGS_SIZE \ + + ARRAY_SIZE(tcg_target_callee_save_regs) * 4 \ + + CPU_TEMP_BUF_NLONGS * sizeof(long) \ + + TCG_TARGET_STACK_ALIGN - 1) \ + & -TCG_TARGET_STACK_ALIGN) + 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; - - /* Allocate space for the TCG temps. */ - frame_size += CPU_TEMP_BUF_NLONGS * sizeof(long); - - /* Align the allocated space. */ - frame_size = ((frame_size + TCG_TARGET_STACK_ALIGN - 1) - & -TCG_TARGET_STACK_ALIGN); + frame_size = FRAME_SIZE; /* The return address is stored in the caller's frame. */ tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_CALL_STACK, -20); @@ -1752,3 +1747,81 @@ static void tcg_target_init(TCGContext *s) tcg_add_target_add_op_defs(hppa_op_defs); } + +typedef struct { + uint32_t len __attribute__((aligned((sizeof(void *))))); + uint32_t id; + uint8_t version; + char augmentation[1]; + uint8_t code_align; + uint8_t data_align; + uint8_t return_column; +} DebugFrameCIE; + +typedef struct { + uint32_t len __attribute__((aligned((sizeof(void *))))); + uint32_t cie_offset; + tcg_target_long func_start __attribute__((packed)); + tcg_target_long func_len __attribute__((packed)); + uint8_t def_cfa[4]; + uint8_t ret_ofs[3]; + uint8_t reg_ofs[ARRAY_SIZE(tcg_target_callee_save_regs) * 2]; +} DebugFrameFDE; + +typedef struct { + DebugFrameCIE cie; + DebugFrameFDE fde; +} DebugFrame; + +#define ELF_HOST_MACHINE EM_PARISC +#define ELF_HOST_FLAGS EFA_PARISC_1_1 + +/* ??? BFD (and thus GDB) wants very much to distinguish between HPUX + and other extensions. We don't really care, but if we don't set this + to *something* then the object file won't be properly matched. */ +#define ELF_OSABI ELFOSABI_LINUX + +static DebugFrame debug_frame = { + .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .cie.id = -1, + .cie.version = 1, + .cie.code_align = 1, + .cie.data_align = 1, + .cie.return_column = 2, + + .fde.len = sizeof(DebugFrameFDE)-4, /* length after .len member */ + .fde.def_cfa = { + 0x12, 30, /* DW_CFA_def_cfa_sf sp, ... */ + (-FRAME_SIZE & 0x7f) | 0x80, /* ... sleb128 -FRAME_SIZE */ + (-FRAME_SIZE >> 7) & 0x7f + }, + .fde.ret_ofs = { + 0x11, 2, (-20 / 4) & 0x7f /* DW_CFA_offset_extended_sf r2, 20 */ + }, + .fde.reg_ofs = { + /* This must match the ordering in tcg_target_callee_save_regs. */ + 0x80 + 4, 0, /* DW_CFA_offset r4, 0 */ + 0x80 + 5, 4, /* DW_CFA_offset r5, 4 */ + 0x80 + 6, 8, /* DW_CFA_offset r6, 8 */ + 0x80 + 7, 12, /* ... */ + 0x80 + 8, 16, + 0x80 + 9, 20, + 0x80 + 10, 24, + 0x80 + 11, 28, + 0x80 + 12, 32, + 0x80 + 13, 36, + 0x80 + 14, 40, + 0x80 + 15, 44, + 0x80 + 16, 48, + 0x80 + 17, 52, + 0x80 + 18, 56, + } +}; + +void tcg_register_jit(void *buf, size_t buf_size) +{ + debug_frame.fde.func_start = (tcg_target_long) buf; + debug_frame.fde.func_len = buf_size; + + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c index 43a51a1c54..871a7e74f5 100644 --- a/tcg/i386/tcg-target.c +++ b/tcg/i386/tcg-target.c @@ -1989,22 +1989,29 @@ static int tcg_target_callee_save_regs[] = { #endif }; +/* Compute frame size via macros, to share between tcg_target_qemu_prologue + and tcg_register_jit. */ + +#define PUSH_SIZE \ + ((1 + ARRAY_SIZE(tcg_target_callee_save_regs)) \ + * (TCG_TARGET_REG_BITS / 8)) + +#define FRAME_SIZE \ + ((PUSH_SIZE \ + + TCG_STATIC_CALL_ARGS_SIZE \ + + CPU_TEMP_BUF_NLONGS * sizeof(long) \ + + TCG_TARGET_STACK_ALIGN - 1) \ + & ~(TCG_TARGET_STACK_ALIGN - 1)) + /* Generate global QEMU prologue and epilogue code */ static void tcg_target_qemu_prologue(TCGContext *s) { - int i, frame_size, push_size, stack_addend; + int i, stack_addend; /* TB prologue */ /* Reserve some stack space, also for TCG temps. */ - 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 + - CPU_TEMP_BUF_NLONGS * sizeof(long); - frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & - ~(TCG_TARGET_STACK_ALIGN - 1); - stack_addend = frame_size - push_size; + stack_addend = FRAME_SIZE - PUSH_SIZE; tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, CPU_TEMP_BUF_NLONGS * sizeof(long)); @@ -2070,3 +2077,92 @@ static void tcg_target_init(TCGContext *s) tcg_add_target_add_op_defs(x86_op_defs); } + +typedef struct { + uint32_t len __attribute__((aligned((sizeof(void *))))); + uint32_t id; + uint8_t version; + char augmentation[1]; + uint8_t code_align; + uint8_t data_align; + uint8_t return_column; +} DebugFrameCIE; + +typedef struct { + uint32_t len __attribute__((aligned((sizeof(void *))))); + uint32_t cie_offset; + tcg_target_long func_start __attribute__((packed)); + tcg_target_long func_len __attribute__((packed)); + uint8_t def_cfa[4]; + uint8_t reg_ofs[14]; +} DebugFrameFDE; + +typedef struct { + DebugFrameCIE cie; + DebugFrameFDE fde; +} DebugFrame; + +#if TCG_TARGET_REG_BITS == 64 +#define ELF_HOST_MACHINE EM_X86_64 +static DebugFrame debug_frame = { + .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .cie.id = -1, + .cie.version = 1, + .cie.code_align = 1, + .cie.data_align = 0x78, /* sleb128 -8 */ + .cie.return_column = 16, + + .fde.len = sizeof(DebugFrameFDE)-4, /* length after .len member */ + .fde.def_cfa = { + 12, 7, /* DW_CFA_def_cfa %rsp, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde.reg_ofs = { + 0x90, 1, /* DW_CFA_offset, %rip, -8 */ + /* The following ordering must match tcg_target_callee_save_regs. */ + 0x86, 2, /* DW_CFA_offset, %rbp, -16 */ + 0x83, 3, /* DW_CFA_offset, %rbx, -24 */ + 0x8c, 4, /* DW_CFA_offset, %r12, -32 */ + 0x8d, 5, /* DW_CFA_offset, %r13, -40 */ + 0x8e, 6, /* DW_CFA_offset, %r14, -48 */ + 0x8f, 7, /* DW_CFA_offset, %r15, -56 */ + } +}; +#else +#define ELF_HOST_MACHINE EM_386 +static DebugFrame debug_frame = { + .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .cie.id = -1, + .cie.version = 1, + .cie.code_align = 1, + .cie.data_align = 0x7c, /* sleb128 -4 */ + .cie.return_column = 8, + + .fde.len = sizeof(DebugFrameFDE)-4, /* length after .len member */ + .fde.def_cfa = { + 12, 4, /* DW_CFA_def_cfa %esp, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde.reg_ofs = { + 0x88, 1, /* DW_CFA_offset, %eip, -4 */ + /* The following ordering must match tcg_target_callee_save_regs. */ + 0x85, 2, /* DW_CFA_offset, %ebp, -8 */ + 0x83, 3, /* DW_CFA_offset, %ebx, -12 */ + 0x86, 4, /* DW_CFA_offset, %esi, -16 */ + 0x87, 5, /* DW_CFA_offset, %edi, -20 */ + } +}; +#endif + +void tcg_register_jit(void *buf, size_t buf_size) +{ + /* We're expecting a 2 byte uleb128 encoded value. */ + assert(FRAME_SIZE >> 14 == 0); + + debug_frame.fde.func_start = (tcg_target_long) buf; + debug_frame.fde.func_len = buf_size; + + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} diff --git a/tcg/sparc/tcg-target.c b/tcg/sparc/tcg-target.c index 491c9797d3..247a27814a 100644 --- a/tcg/sparc/tcg-target.c +++ b/tcg/sparc/tcg-target.c @@ -1624,3 +1624,66 @@ static void tcg_target_init(TCGContext *s) tcg_regset_set_reg(s->reserved_regs, TCG_REG_O7); tcg_add_target_add_op_defs(sparc_op_defs); } + +#if TCG_TARGET_REG_BITS == 64 +# define ELF_HOST_MACHINE EM_SPARCV9 +#elif defined(__sparc_v8plus__) +# define ELF_HOST_MACHINE EM_SPARC32PLUS +# define ELF_HOST_FLAGS EF_SPARC_32PLUS +#else +# define ELF_HOST_MACHINE EM_SPARC +#endif + +typedef struct { + uint32_t len __attribute__((aligned((sizeof(void *))))); + uint32_t id; + uint8_t version; + char augmentation[1]; + uint8_t code_align; + uint8_t data_align; + uint8_t return_column; +} DebugFrameCIE; + +typedef struct { + uint32_t len __attribute__((aligned((sizeof(void *))))); + uint32_t cie_offset; + tcg_target_long func_start __attribute__((packed)); + tcg_target_long func_len __attribute__((packed)); + uint8_t def_cfa[TCG_TARGET_REG_BITS == 64 ? 4 : 2]; + uint8_t win_save; + uint8_t ret_save[3]; +} DebugFrameFDE; + +typedef struct { + DebugFrameCIE cie; + DebugFrameFDE fde; +} DebugFrame; + +static DebugFrame debug_frame = { + .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .cie.id = -1, + .cie.version = 1, + .cie.code_align = 1, + .cie.data_align = -sizeof(void *) & 0x7f, + .cie.return_column = 15, /* o7 */ + + .fde.len = sizeof(DebugFrameFDE)-4, /* length after .len member */ + .fde.def_cfa = { +#if TCG_TARGET_REG_BITS == 64 + 12, 30, /* DW_CFA_def_cfa i6, 2047 */ + (2047 & 0x7f) | 0x80, (2047 >> 7) +#else + 13, 30 /* DW_CFA_def_cfa_register i6 */ +#endif + }, + .fde.win_save = 0x2d, /* DW_CFA_GNU_window_save */ + .fde.ret_save = { 9, 15, 31 }, /* DW_CFA_register o7, i7 */ +}; + +void tcg_register_jit(void *buf, size_t buf_size) +{ + debug_frame.fde.func_start = (tcg_target_long) buf; + debug_frame.fde.func_len = buf_size; + + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} diff --git a/tcg/tcg.c b/tcg/tcg.c index ccfcd1abe1..ab589c7ad2 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -28,6 +28,9 @@ #include "config.h" +/* Define to jump the ELF file used to communicate with GDB. */ +#undef DEBUG_JIT + #if !defined(CONFIG_DEBUG_TCG) && !defined(NDEBUG) /* define it to suppress various consistency checks (faster) */ #define NDEBUG @@ -45,6 +48,18 @@ #include "cpu.h" #include "tcg-op.h" + +#if TCG_TARGET_REG_BITS == 64 +# define ELF_CLASS ELFCLASS64 +#else +# define ELF_CLASS ELFCLASS32 +#endif +#ifdef HOST_WORDS_BIGENDIAN +# define ELF_DATA ELFDATA2MSB +#else +# define ELF_DATA ELFDATA2LSB +#endif + #include "elf.h" #if defined(CONFIG_USE_GUEST_BASE) && !defined(TCG_TARGET_HAS_GUEST_BASE) @@ -57,6 +72,10 @@ 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 void tcg_register_jit_int(void *buf, size_t size, + void *debug_frame, size_t debug_frame_size) + __attribute__((unused)); + /* Forward declarations for functions declared and used in tcg-target.c. */ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str); static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, @@ -2231,3 +2250,267 @@ void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) cpu_fprintf(f, "[TCG profiler not compiled]\n"); } #endif + +#ifdef ELF_HOST_MACHINE +/* In order to use this feature, the backend needs to do three things: + + (1) Define ELF_HOST_MACHINE to indicate both what value to + put into the ELF image and to indicate support for the feature. + + (2) Define tcg_register_jit. This should create a buffer containing + the contents of a .debug_frame section that describes the post- + prologue unwind info for the tcg machine. + + (3) Call tcg_register_jit_int, with the constructed .debug_frame. +*/ + +/* Begin GDB interface. THE FOLLOWING MUST MATCH GDB DOCS. */ +typedef enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN +} jit_actions_t; + +struct jit_code_entry { + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const void *symfile_addr; + uint64_t symfile_size; +}; + +struct jit_descriptor { + uint32_t version; + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; +}; + +void __jit_debug_register_code(void) __attribute__((noinline)); +void __jit_debug_register_code(void) +{ + asm(""); +} + +/* Must statically initialize the version, because GDB may check + the version before we can set it. */ +struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 }; + +/* End GDB interface. */ + +static int find_string(const char *strtab, const char *str) +{ + const char *p = strtab + 1; + + while (1) { + if (strcmp(p, str) == 0) { + return p - strtab; + } + p += strlen(p) + 1; + } +} + +static void tcg_register_jit_int(void *buf_ptr, size_t buf_size, + void *debug_frame, size_t debug_frame_size) +{ + struct __attribute__((packed)) DebugInfo { + uint32_t len; + uint16_t version; + uint32_t abbrev; + uint8_t ptr_size; + uint8_t cu_die; + uint16_t cu_lang; + uintptr_t cu_low_pc; + uintptr_t cu_high_pc; + uint8_t fn_die; + char fn_name[16]; + uintptr_t fn_low_pc; + uintptr_t fn_high_pc; + uint8_t cu_eoc; + }; + + struct ElfImage { + ElfW(Ehdr) ehdr; + ElfW(Phdr) phdr; + ElfW(Shdr) shdr[7]; + ElfW(Sym) sym[2]; + struct DebugInfo di; + uint8_t da[24]; + char str[80]; + }; + + struct ElfImage *img; + + static const struct ElfImage img_template = { + .ehdr = { + .e_ident[EI_MAG0] = ELFMAG0, + .e_ident[EI_MAG1] = ELFMAG1, + .e_ident[EI_MAG2] = ELFMAG2, + .e_ident[EI_MAG3] = ELFMAG3, + .e_ident[EI_CLASS] = ELF_CLASS, + .e_ident[EI_DATA] = ELF_DATA, + .e_ident[EI_VERSION] = EV_CURRENT, + .e_type = ET_EXEC, + .e_machine = ELF_HOST_MACHINE, + .e_version = EV_CURRENT, + .e_phoff = offsetof(struct ElfImage, phdr), + .e_shoff = offsetof(struct ElfImage, shdr), + .e_ehsize = sizeof(ElfW(Shdr)), + .e_phentsize = sizeof(ElfW(Phdr)), + .e_phnum = 1, + .e_shentsize = sizeof(ElfW(Shdr)), + .e_shnum = ARRAY_SIZE(img->shdr), + .e_shstrndx = ARRAY_SIZE(img->shdr) - 1, +#ifdef ELF_HOST_FLAGS + .e_flags = ELF_HOST_FLAGS, +#endif +#ifdef ELF_OSABI + .e_ident[EI_OSABI] = ELF_OSABI, +#endif + }, + .phdr = { + .p_type = PT_LOAD, + .p_flags = PF_X, + }, + .shdr = { + [0] = { .sh_type = SHT_NULL }, + /* Trick: The contents of code_gen_buffer are not present in + this fake ELF file; that got allocated elsewhere. Therefore + we mark .text as SHT_NOBITS (similar to .bss) so that readers + will not look for contents. We can record any address. */ + [1] = { /* .text */ + .sh_type = SHT_NOBITS, + .sh_flags = SHF_EXECINSTR | SHF_ALLOC, + }, + [2] = { /* .debug_info */ + .sh_type = SHT_PROGBITS, + .sh_offset = offsetof(struct ElfImage, di), + .sh_size = sizeof(struct DebugInfo), + }, + [3] = { /* .debug_abbrev */ + .sh_type = SHT_PROGBITS, + .sh_offset = offsetof(struct ElfImage, da), + .sh_size = sizeof(img->da), + }, + [4] = { /* .debug_frame */ + .sh_type = SHT_PROGBITS, + .sh_offset = sizeof(struct ElfImage), + }, + [5] = { /* .symtab */ + .sh_type = SHT_SYMTAB, + .sh_offset = offsetof(struct ElfImage, sym), + .sh_size = sizeof(img->sym), + .sh_info = 1, + .sh_link = ARRAY_SIZE(img->shdr) - 1, + .sh_entsize = sizeof(ElfW(Sym)), + }, + [6] = { /* .strtab */ + .sh_type = SHT_STRTAB, + .sh_offset = offsetof(struct ElfImage, str), + .sh_size = sizeof(img->str), + } + }, + .sym = { + [1] = { /* code_gen_buffer */ + .st_info = ELF_ST_INFO(STB_GLOBAL, STT_FUNC), + .st_shndx = 1, + } + }, + .di = { + .len = sizeof(struct DebugInfo) - 4, + .version = 2, + .ptr_size = sizeof(void *), + .cu_die = 1, + .cu_lang = 0x8001, /* DW_LANG_Mips_Assembler */ + .fn_die = 2, + .fn_name = "code_gen_buffer" + }, + .da = { + 1, /* abbrev number (the cu) */ + 0x11, 1, /* DW_TAG_compile_unit, has children */ + 0x13, 0x5, /* DW_AT_language, DW_FORM_data2 */ + 0x11, 0x1, /* DW_AT_low_pc, DW_FORM_addr */ + 0x12, 0x1, /* DW_AT_high_pc, DW_FORM_addr */ + 0, 0, /* end of abbrev */ + 2, /* abbrev number (the fn) */ + 0x2e, 0, /* DW_TAG_subprogram, no children */ + 0x3, 0x8, /* DW_AT_name, DW_FORM_string */ + 0x11, 0x1, /* DW_AT_low_pc, DW_FORM_addr */ + 0x12, 0x1, /* DW_AT_high_pc, DW_FORM_addr */ + 0, 0, /* end of abbrev */ + 0 /* no more abbrev */ + }, + .str = "\0" ".text\0" ".debug_info\0" ".debug_abbrev\0" + ".debug_frame\0" ".symtab\0" ".strtab\0" "code_gen_buffer", + }; + + /* We only need a single jit entry; statically allocate it. */ + static struct jit_code_entry one_entry; + + uintptr_t buf = (uintptr_t)buf_ptr; + size_t img_size = sizeof(struct ElfImage) + debug_frame_size; + + img = g_malloc(img_size); + *img = img_template; + memcpy(img + 1, debug_frame, debug_frame_size); + + img->phdr.p_vaddr = buf; + img->phdr.p_paddr = buf; + img->phdr.p_memsz = buf_size; + + img->shdr[1].sh_name = find_string(img->str, ".text"); + img->shdr[1].sh_addr = buf; + img->shdr[1].sh_size = buf_size; + + img->shdr[2].sh_name = find_string(img->str, ".debug_info"); + img->shdr[3].sh_name = find_string(img->str, ".debug_abbrev"); + + img->shdr[4].sh_name = find_string(img->str, ".debug_frame"); + img->shdr[4].sh_size = debug_frame_size; + + img->shdr[5].sh_name = find_string(img->str, ".symtab"); + img->shdr[6].sh_name = find_string(img->str, ".strtab"); + + img->sym[1].st_name = find_string(img->str, "code_gen_buffer"); + img->sym[1].st_value = buf; + img->sym[1].st_size = buf_size; + + img->di.cu_low_pc = buf; + img->di.cu_high_pc = buf_size; + img->di.fn_low_pc = buf; + img->di.fn_high_pc = buf_size; + +#ifdef DEBUG_JIT + /* Enable this block to be able to debug the ELF image file creation. + One can use readelf, objdump, or other inspection utilities. */ + { + FILE *f = fopen("/tmp/qemu.jit", "w+b"); + if (f) { + if (fwrite(img, img_size, 1, f) != img_size) { + /* Avoid stupid unused return value warning for fwrite. */ + } + fclose(f); + } + } +#endif + + one_entry.symfile_addr = img; + one_entry.symfile_size = img_size; + + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + __jit_debug_descriptor.relevant_entry = &one_entry; + __jit_debug_descriptor.first_entry = &one_entry; + __jit_debug_register_code(); +} +#else +/* No support for the feature. Provide the entry point expected by exec.c, + and implement the internal function we declared earlier. */ + +static void tcg_register_jit_int(void *buf, size_t size, + void *debug_frame, size_t debug_frame_size) +{ +} + +void tcg_register_jit(void *buf, size_t buf_size) +{ +} +#endif /* ELF_HOST_MACHINE */ diff --git a/tcg/tcg.h b/tcg/tcg.h index 5f6c647ea5..a83bdddba4 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -586,3 +586,5 @@ extern uint8_t code_gen_prologue[]; # define tcg_qemu_tb_exec(env, tb_ptr) \ ((tcg_target_ulong (*)(void *, void *))code_gen_prologue)(env, tb_ptr) #endif + +void tcg_register_jit(void *buf, size_t buf_size); diff --git a/test-qmp-commands.c b/test-qmp-commands.c index fa5a7bd8dc..60cbf019bb 100644 --- a/test-qmp-commands.c +++ b/test-qmp-commands.c @@ -46,7 +46,6 @@ static void test_dispatch_cmd(void) resp = qmp_dispatch(QOBJECT(req)); assert(resp != NULL); assert(!qdict_haskey(qobject_to_qdict(resp), "error")); - g_print("\nresp: %s\n", qstring_get_str(qobject_to_json(resp))); qobject_decref(resp); QDECREF(req); @@ -63,7 +62,6 @@ static void test_dispatch_cmd_error(void) resp = qmp_dispatch(QOBJECT(req)); assert(resp != NULL); assert(qdict_haskey(qobject_to_qdict(resp), "error")); - g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp))); qobject_decref(resp); QDECREF(req); @@ -92,7 +90,6 @@ static void test_dispatch_cmd_io(void) resp = qmp_dispatch(QOBJECT(req)); assert(resp != NULL); assert(!qdict_haskey(qobject_to_qdict(resp), "error")); - g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp))); qobject_decref(resp); QDECREF(req); diff --git a/test-qmp-input-strict.c b/test-qmp-input-strict.c new file mode 100644 index 0000000000..f6df8cbe1e --- /dev/null +++ b/test-qmp-input-strict.c @@ -0,0 +1,234 @@ +/* + * QMP Input Visitor unit-tests (strict mode). + * + * Copyright (C) 2011-2012 Red Hat Inc. + * + * Authors: + * Luiz Capitulino <lcapitulino@redhat.com> + * Paolo Bonzini <pbonzini@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <stdarg.h> + +#include "qapi/qmp-input-visitor.h" +#include "test-qapi-types.h" +#include "test-qapi-visit.h" +#include "qemu-objects.h" + +typedef struct TestInputVisitorData { + QObject *obj; + QmpInputVisitor *qiv; +} TestInputVisitorData; + +static void validate_teardown(TestInputVisitorData *data, + const void *unused) +{ + qobject_decref(data->obj); + data->obj = NULL; + + if (data->qiv) { + qmp_input_visitor_cleanup(data->qiv); + data->qiv = NULL; + } +} + +/* This is provided instead of a test setup function so that the JSON + string used by the tests are kept in the test functions (and not + int main()) */ +static GCC_FMT_ATTR(2, 3) +Visitor *validate_test_init(TestInputVisitorData *data, + const char *json_string, ...) +{ + Visitor *v; + va_list ap; + + va_start(ap, json_string); + data->obj = qobject_from_jsonv(json_string, &ap); + va_end(ap); + + g_assert(data->obj != NULL); + + data->qiv = qmp_input_visitor_new_strict(data->obj); + g_assert(data->qiv != NULL); + + v = qmp_input_get_visitor(data->qiv); + g_assert(v != NULL); + + return v; +} + +typedef struct TestStruct +{ + int64_t integer; + bool boolean; + char *string; +} TestStruct; + +static void visit_type_TestStruct(Visitor *v, TestStruct **obj, + const char *name, Error **errp) +{ + visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), + errp); + + visit_type_int(v, &(*obj)->integer, "integer", errp); + visit_type_bool(v, &(*obj)->boolean, "boolean", errp); + visit_type_str(v, &(*obj)->string, "string", errp); + + visit_end_struct(v, errp); +} + +static void test_validate_struct(TestInputVisitorData *data, + const void *unused) +{ + TestStruct *p = NULL; + Error *errp = NULL; + Visitor *v; + + v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); + + visit_type_TestStruct(v, &p, NULL, &errp); + g_assert(!error_is_set(&errp)); + g_free(p->string); + g_free(p); +} + +static void test_validate_struct_nested(TestInputVisitorData *data, + const void *unused) +{ + UserDefNested *udp = NULL; + Error *errp = NULL; + Visitor *v; + + v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string' }, 'string2': 'string2'}}}"); + + visit_type_UserDefNested(v, &udp, NULL, &errp); + g_assert(!error_is_set(&errp)); + qapi_free_UserDefNested(udp); +} + +static void test_validate_list(TestInputVisitorData *data, + const void *unused) +{ + UserDefOneList *head = NULL; + Error *errp = NULL; + Visitor *v; + + v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]"); + + visit_type_UserDefOneList(v, &head, NULL, &errp); + g_assert(!error_is_set(&errp)); + qapi_free_UserDefOneList(head); +} + +static void test_validate_union(TestInputVisitorData *data, + const void *unused) +{ + UserDefUnion *tmp = NULL; + Visitor *v; + Error *errp = NULL; + + v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }"); + + visit_type_UserDefUnion(v, &tmp, NULL, &errp); + g_assert(!error_is_set(&errp)); + qapi_free_UserDefUnion(tmp); +} + +static void test_validate_fail_struct(TestInputVisitorData *data, + const void *unused) +{ + TestStruct *p = NULL; + Error *errp = NULL; + Visitor *v; + + v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }"); + + visit_type_TestStruct(v, &p, NULL, &errp); + g_assert(error_is_set(&errp)); + if (p) { + g_free(p->string); + } + g_free(p); +} + +static void test_validate_fail_struct_nested(TestInputVisitorData *data, + const void *unused) +{ + UserDefNested *udp = NULL; + Error *errp = NULL; + Visitor *v; + + v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}"); + + visit_type_UserDefNested(v, &udp, NULL, &errp); + g_assert(error_is_set(&errp)); + qapi_free_UserDefNested(udp); +} + +static void test_validate_fail_list(TestInputVisitorData *data, + const void *unused) +{ + UserDefOneList *head = NULL; + Error *errp = NULL; + Visitor *v; + + v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]"); + + visit_type_UserDefOneList(v, &head, NULL, &errp); + g_assert(error_is_set(&errp)); + qapi_free_UserDefOneList(head); +} + +static void test_validate_fail_union(TestInputVisitorData *data, + const void *unused) +{ + UserDefUnion *tmp = NULL; + Error *errp = NULL; + Visitor *v; + + v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 }, 'extra': 'yyy' }"); + + visit_type_UserDefUnion(v, &tmp, NULL, &errp); + g_assert(error_is_set(&errp)); + qapi_free_UserDefUnion(tmp); +} + +static void validate_test_add(const char *testpath, + TestInputVisitorData *data, + void (*test_func)(TestInputVisitorData *data, const void *user_data)) +{ + g_test_add(testpath, TestInputVisitorData, data, NULL, test_func, + validate_teardown); +} + +int main(int argc, char **argv) +{ + TestInputVisitorData testdata; + + g_test_init(&argc, &argv, NULL); + + validate_test_add("/visitor/input-strict/pass/struct", + &testdata, test_validate_struct); + validate_test_add("/visitor/input-strict/pass/struct-nested", + &testdata, test_validate_struct_nested); + validate_test_add("/visitor/input-strict/pass/list", + &testdata, test_validate_list); + validate_test_add("/visitor/input-strict/pass/union", + &testdata, test_validate_union); + validate_test_add("/visitor/input-strict/fail/struct", + &testdata, test_validate_fail_struct); + validate_test_add("/visitor/input-strict/fail/struct-nested", + &testdata, test_validate_fail_struct_nested); + validate_test_add("/visitor/input-strict/fail/list", + &testdata, test_validate_fail_list); + validate_test_add("/visitor/input-strict/fail/union", + &testdata, test_validate_fail_union); + + g_test_run(); + + return 0; +} diff --git a/test-qmp-input-visitor.c b/test-qmp-input-visitor.c index 1996e49576..c30fdc4e59 100644 --- a/test-qmp-input-visitor.c +++ b/test-qmp-input-visitor.c @@ -258,6 +258,23 @@ static void input_visitor_test_add(const char *testpath, visitor_input_teardown); } +static void test_visitor_in_errors(TestInputVisitorData *data, + const void *unused) +{ + TestStruct *p = NULL; + Error *errp = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', 'string': -42 }"); + + visit_type_TestStruct(v, &p, NULL, &errp); + g_assert(error_is_set(&errp)); + g_assert(p->string == NULL); + + g_free(p->string); + g_free(p); +} + int main(int argc, char **argv) { TestInputVisitorData in_visitor_data; @@ -282,6 +299,8 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_list); input_visitor_test_add("/visitor/input/union", &in_visitor_data, test_visitor_in_union); + input_visitor_test_add("/visitor/input/errors", + &in_visitor_data, test_visitor_in_errors); g_test_run(); diff --git a/test-qmp-output-visitor.c b/test-qmp-output-visitor.c index 4d6c4d4420..24a6359504 100644 --- a/test-qmp-output-visitor.c +++ b/test-qmp-output-visitor.c @@ -274,6 +274,24 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, qapi_free_UserDefNested(ud2); } +static void test_visitor_out_struct_errors(TestOutputVisitorData *data, + const void *unused) +{ + EnumOne bad_values[] = { ENUM_ONE_MAX, -1 }; + UserDefOne u = { 0 }, *pu = &u; + Error *errp; + int i; + + for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) { + errp = NULL; + u.has_enum1 = true; + u.enum1 = bad_values[i]; + visit_type_UserDefOne(data->ov, &pu, "unused", &errp); + g_assert(error_is_set(&errp) == true); + error_free(errp); + } +} + typedef struct TestStructList { TestStruct *value; @@ -444,6 +462,8 @@ int main(int argc, char **argv) &out_visitor_data, test_visitor_out_struct); output_visitor_test_add("/visitor/output/struct-nested", &out_visitor_data, test_visitor_out_struct_nested); + output_visitor_test_add("/visitor/output/struct-errors", + &out_visitor_data, test_visitor_out_struct_errors); output_visitor_test_add("/visitor/output/list", &out_visitor_data, test_visitor_out_list); output_visitor_test_add("/visitor/output/list-qapi-free", diff --git a/tests/Makefile b/tests/Makefile index c78ade122e..2a2fff7c6d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,6 +3,7 @@ export SRC_PATH CHECKS = check-qdict check-qfloat check-qint check-qstring check-qlist CHECKS += check-qjson test-qmp-output-visitor test-qmp-input-visitor CHECKS += test-string-input-visitor test-string-output-visitor test-coroutine +CHECKS += test-qmp-commands CHECKS += $(SRC_PATH)/tests/qemu-iotests-quick.sh check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o test-coroutine.o: $(GENERATED_HEADERS) @@ -15,9 +16,9 @@ check-qfloat: check-qfloat.o qfloat.o $(tools-obj-y) check-qjson: check-qjson.o $(qobject-obj-y) $(tools-obj-y) test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(tools-obj-y) -test-qmp-input-visitor.o test-qmp-output-visitor.o \ +test-qmp-input-visitor.o test-qmp-output-visitor.o test-qmp-input-strict.o \ test-string-input-visitor.o test-string-output-visitor.o \ - test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir) + test-qmp-commands.o: QEMU_CFLAGS += -I $(qapi-dir) $(qapi-dir)/test-qapi-types.c $(qapi-dir)/test-qapi-types.h :\ $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py @@ -36,6 +37,9 @@ test-string-output-visitor: test-string-output-visitor.o $(qobject-obj-y) $(qapi test-string-input-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) test-string-input-visitor: test-string-input-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o +test-qmp-input-strict.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) +test-qmp-input-strict: test-qmp-input-strict.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o + test-qmp-output-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) test-qmp-output-visitor: test-qmp-output-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o diff --git a/trace-events b/trace-events index 70f059d9f6..db2cd39950 100644 --- a/trace-events +++ b/trace-events @@ -726,7 +726,7 @@ ppm_save(const char *filename, void *display_surface) "%s surface=%p" # hw/qxl.c disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d" disable qxl_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u" -qxl_create_guest_primary(int qid, uint32_t width, uint32_t height, uint64_t mem, uint32_t format, uint32_t position) "%d %dx%d mem=%lx %d,%d" +qxl_create_guest_primary(int qid, uint32_t width, uint32_t height, uint64_t mem, uint32_t format, uint32_t position) "%d %ux%u mem=%" PRIx64 " %u,%u" qxl_create_guest_primary_rest(int qid, int32_t stride, uint32_t type, uint32_t flags) "%d %d,%d,%d" qxl_destroy_primary(int qid) "%d" qxl_enter_vga_mode(int qid) "%d" diff --git a/ui/spice-display.c b/ui/spice-display.c index 28d6d4a6b3..cb8a7addfc 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -80,8 +80,8 @@ void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot, if (async != QXL_SYNC) { spice_qxl_add_memslot_async(&ssd->qxl, memslot, - (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_MEMSLOT_ADD_ASYNC)); + (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_MEMSLOT_ADD_ASYNC)); } else { ssd->worker->add_memslot(ssd->worker, memslot); } @@ -100,8 +100,8 @@ void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, trace_qemu_spice_create_primary_surface(ssd->qxl.id, id, surface, async); if (async != QXL_SYNC) { spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface, - (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_CREATE_PRIMARY_ASYNC)); + (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_CREATE_PRIMARY_ASYNC)); } else { ssd->worker->create_primary_surface(ssd->worker, id, surface); } @@ -113,8 +113,8 @@ void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, trace_qemu_spice_destroy_primary_surface(ssd->qxl.id, id, async); if (async != QXL_SYNC) { spice_qxl_destroy_primary_surface_async(&ssd->qxl, id, - (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_DESTROY_PRIMARY_ASYNC)); + (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_PRIMARY_ASYNC)); } else { ssd->worker->destroy_primary_surface(ssd->worker, id); } @@ -168,7 +168,7 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) drawable->bbox = ssd->dirty; drawable->clip.type = SPICE_CLIP_TYPE_NONE; drawable->effect = QXL_EFFECT_OPAQUE; - drawable->release_info.id = (intptr_t)update; + drawable->release_info.id = (uintptr_t)update; drawable->type = QXL_DRAW_COPY; drawable->surfaces_dest[0] = -1; drawable->surfaces_dest[1] = -1; @@ -179,7 +179,7 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) + time_space.tv_nsec / 1000 / 1000; drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; - drawable->u.copy.src_bitmap = (intptr_t)image; + drawable->u.copy.src_bitmap = (uintptr_t)image; drawable->u.copy.src_area.right = bw; drawable->u.copy.src_area.bottom = bh; @@ -189,7 +189,7 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) image->bitmap.stride = bw * 4; image->descriptor.width = image->bitmap.x = bw; image->descriptor.height = image->bitmap.y = bh; - image->bitmap.data = (intptr_t)(update->bitmap); + image->bitmap.data = (uintptr_t)(update->bitmap); image->bitmap.palette = 0; image->bitmap.format = SPICE_BITMAP_FMT_32BIT; @@ -210,7 +210,7 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) } cmd->type = QXL_CMD_DRAW; - cmd->data = (intptr_t)drawable; + cmd->data = (uintptr_t)drawable; memset(&ssd->dirty, 0, sizeof(ssd->dirty)); return update; @@ -254,7 +254,7 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) surface.mouse_mode = true; surface.flags = 0; surface.type = 0; - surface.mem = (intptr_t)ssd->buf; + surface.mem = (uintptr_t)ssd->buf; surface.group_id = MEMSLOT_GROUP_HOST; qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC); diff --git a/vl.c b/vl.c index 112b0e0af9..0fccf50a5d 100644 --- a/vl.c +++ b/vl.c @@ -3196,7 +3196,7 @@ int main(int argc, char **argv, char **envp) cpudef_init(); if (cpu_model && *cpu_model == '?') { - list_cpus(stdout, &fprintf, optarg); + list_cpus(stdout, &fprintf, cpu_model); exit(0); } |