diff options
116 files changed, 6042 insertions, 1981 deletions
diff --git a/Makefile.objs b/Makefile.objs index add83755be..18fd35cf15 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -50,7 +50,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/ common-obj-y += migration.o migration-tcp.o common-obj-y += vmstate.o -common-obj-y += qemu-file.o +common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o common-obj-$(CONFIG_RDMA) += migration-rdma.o common-obj-y += qemu-char.o #aio.o common-obj-y += block-migration.o diff --git a/Makefile.target b/Makefile.target index 1e8d7abcb3..e9ff1eed7b 100644 --- a/Makefile.target +++ b/Makefile.target @@ -127,7 +127,7 @@ endif #CONFIG_BSD_USER # System emulator target ifdef CONFIG_SOFTMMU obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o -obj-y += qtest.o +obj-y += qtest.o bootdevice.o obj-y += hw/ obj-$(CONFIG_FDT) += device_tree.o obj-$(CONFIG_KVM) += kvm-all.o diff --git a/arch_init.c b/arch_init.c index 9b3e25d805..88a5ba0837 100644 --- a/arch_init.c +++ b/arch_init.c @@ -1040,8 +1040,7 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) static int ram_load(QEMUFile *f, void *opaque, int version_id) { - ram_addr_t addr; - int flags, ret = 0; + int flags = 0, ret = 0; static uint64_t seq_iter; seq_iter++; @@ -1050,21 +1049,24 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) ret = -EINVAL; } - while (!ret) { - addr = qemu_get_be64(f); + while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { + ram_addr_t addr, total_ram_bytes; + void *host; + uint8_t ch; + addr = qemu_get_be64(f); flags = addr & ~TARGET_PAGE_MASK; addr &= TARGET_PAGE_MASK; - if (flags & RAM_SAVE_FLAG_MEM_SIZE) { + switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { + case RAM_SAVE_FLAG_MEM_SIZE: /* Synchronize RAM block list */ - char id[256]; - ram_addr_t length; - ram_addr_t total_ram_bytes = addr; - - while (total_ram_bytes) { + total_ram_bytes = addr; + while (!ret && total_ram_bytes) { RAMBlock *block; uint8_t len; + char id[256]; + ram_addr_t length; len = qemu_get_byte(f); qemu_get_buffer(f, (uint8_t *)id, len); @@ -1088,16 +1090,11 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) "accept migration", id); ret = -EINVAL; } - if (ret) { - break; - } total_ram_bytes -= length; } - } else if (flags & RAM_SAVE_FLAG_COMPRESS) { - void *host; - uint8_t ch; - + break; + case RAM_SAVE_FLAG_COMPRESS: host = host_from_stream_offset(f, addr, flags); if (!host) { error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); @@ -1107,9 +1104,8 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) ch = qemu_get_byte(f); ram_handle_compressed(host, ch, TARGET_PAGE_SIZE); - } else if (flags & RAM_SAVE_FLAG_PAGE) { - void *host; - + break; + case RAM_SAVE_FLAG_PAGE: host = host_from_stream_offset(f, addr, flags); if (!host) { error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); @@ -1118,8 +1114,9 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) } qemu_get_buffer(f, host, TARGET_PAGE_SIZE); - } else if (flags & RAM_SAVE_FLAG_XBZRLE) { - void *host = host_from_stream_offset(f, addr, flags); + break; + case RAM_SAVE_FLAG_XBZRLE: + host = host_from_stream_offset(f, addr, flags); if (!host) { error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); ret = -EINVAL; @@ -1132,17 +1129,22 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) ret = -EINVAL; break; } - } else if (flags & RAM_SAVE_FLAG_HOOK) { - ram_control_load_hook(f, flags); - } else if (flags & RAM_SAVE_FLAG_EOS) { - /* normal exit */ break; - } else { - error_report("Unknown migration flags: %#x", flags); - ret = -EINVAL; + case RAM_SAVE_FLAG_EOS: + /* normal exit */ break; + default: + if (flags & RAM_SAVE_FLAG_HOOK) { + ram_control_load_hook(f, flags); + } else { + error_report("Unknown combination of migration flags: %#x", + flags); + ret = -EINVAL; + } + } + if (!ret) { + ret = qemu_file_get_error(f); } - ret = qemu_file_get_error(f); } DPRINTF("Completed load of VM with exit code %d seq iteration " @@ -5043,6 +5043,11 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) return; } + if (!(bs->open_flags & BDRV_O_INCOMING)) { + return; + } + bs->open_flags &= ~BDRV_O_INCOMING; + if (bs->drv->bdrv_invalidate_cache) { bs->drv->bdrv_invalidate_cache(bs, &local_err); } else if (bs->file) { @@ -5078,19 +5083,6 @@ void bdrv_invalidate_cache_all(Error **errp) } } -void bdrv_clear_incoming_migration_all(void) -{ - BlockDriverState *bs; - - QTAILQ_FOREACH(bs, &bdrv_states, device_list) { - AioContext *aio_context = bdrv_get_aio_context(bs); - - aio_context_acquire(aio_context); - bs->open_flags = bs->open_flags & ~(BDRV_O_INCOMING); - aio_context_release(aio_context); - } -} - int bdrv_flush(BlockDriverState *bs) { Coroutine *co; diff --git a/bootdevice.c b/bootdevice.c new file mode 100644 index 0000000000..b29970c7a3 --- /dev/null +++ b/bootdevice.c @@ -0,0 +1,258 @@ +/* + * QEMU Boot Device Implement + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysemu/sysemu.h" +#include "qapi/visitor.h" +#include "qemu/error-report.h" + +typedef struct FWBootEntry FWBootEntry; + +struct FWBootEntry { + QTAILQ_ENTRY(FWBootEntry) link; + int32_t bootindex; + DeviceState *dev; + char *suffix; +}; + +static QTAILQ_HEAD(, FWBootEntry) fw_boot_order = + QTAILQ_HEAD_INITIALIZER(fw_boot_order); + +void check_boot_index(int32_t bootindex, Error **errp) +{ + FWBootEntry *i; + + if (bootindex >= 0) { + QTAILQ_FOREACH(i, &fw_boot_order, link) { + if (i->bootindex == bootindex) { + error_setg(errp, "The bootindex %d has already been used", + bootindex); + return; + } + } + } +} + +void del_boot_device_path(DeviceState *dev, const char *suffix) +{ + FWBootEntry *i; + + if (dev == NULL) { + return; + } + + QTAILQ_FOREACH(i, &fw_boot_order, link) { + if ((!suffix || !g_strcmp0(i->suffix, suffix)) && + i->dev == dev) { + QTAILQ_REMOVE(&fw_boot_order, i, link); + g_free(i->suffix); + g_free(i); + + break; + } + } +} + +void add_boot_device_path(int32_t bootindex, DeviceState *dev, + const char *suffix) +{ + FWBootEntry *node, *i; + + if (bootindex < 0) { + del_boot_device_path(dev, suffix); + return; + } + + assert(dev != NULL || suffix != NULL); + + del_boot_device_path(dev, suffix); + + node = g_malloc0(sizeof(FWBootEntry)); + node->bootindex = bootindex; + node->suffix = g_strdup(suffix); + node->dev = dev; + + QTAILQ_FOREACH(i, &fw_boot_order, link) { + if (i->bootindex == bootindex) { + error_report("Two devices with same boot index %d", bootindex); + exit(1); + } else if (i->bootindex < bootindex) { + continue; + } + QTAILQ_INSERT_BEFORE(i, node, link); + return; + } + QTAILQ_INSERT_TAIL(&fw_boot_order, node, link); +} + +DeviceState *get_boot_device(uint32_t position) +{ + uint32_t counter = 0; + FWBootEntry *i = NULL; + DeviceState *res = NULL; + + if (!QTAILQ_EMPTY(&fw_boot_order)) { + QTAILQ_FOREACH(i, &fw_boot_order, link) { + if (counter == position) { + res = i->dev; + break; + } + counter++; + } + } + return res; +} + +/* + * This function returns null terminated string that consist of new line + * separated device paths. + * + * memory pointed by "size" is assigned total length of the array in bytes + * + */ +char *get_boot_devices_list(size_t *size, bool ignore_suffixes) +{ + FWBootEntry *i; + size_t total = 0; + char *list = NULL; + + QTAILQ_FOREACH(i, &fw_boot_order, link) { + char *devpath = NULL, *bootpath; + size_t len; + + if (i->dev) { + devpath = qdev_get_fw_dev_path(i->dev); + assert(devpath); + } + + if (i->suffix && !ignore_suffixes && devpath) { + size_t bootpathlen = strlen(devpath) + strlen(i->suffix) + 1; + + bootpath = g_malloc(bootpathlen); + snprintf(bootpath, bootpathlen, "%s%s", devpath, i->suffix); + g_free(devpath); + } else if (devpath) { + bootpath = devpath; + } else if (!ignore_suffixes) { + assert(i->suffix); + bootpath = g_strdup(i->suffix); + } else { + bootpath = g_strdup(""); + } + + if (total) { + list[total-1] = '\n'; + } + len = strlen(bootpath) + 1; + list = g_realloc(list, total + len); + memcpy(&list[total], bootpath, len); + total += len; + g_free(bootpath); + } + + *size = total; + + if (boot_strict && *size > 0) { + list[total-1] = '\n'; + list = g_realloc(list, total + 5); + memcpy(&list[total], "HALT", 5); + *size = total + 5; + } + return list; +} + +typedef struct { + int32_t *bootindex; + const char *suffix; + DeviceState *dev; +} BootIndexProperty; + +static void device_get_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + BootIndexProperty *prop = opaque; + visit_type_int32(v, prop->bootindex, name, errp); +} + +static void device_set_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + BootIndexProperty *prop = opaque; + int32_t boot_index; + Error *local_err = NULL; + + visit_type_int32(v, &boot_index, name, &local_err); + if (local_err) { + goto out; + } + /* check whether bootindex is present in fw_boot_order list */ + check_boot_index(boot_index, &local_err); + if (local_err) { + goto out; + } + /* change bootindex to a new one */ + *prop->bootindex = boot_index; + + add_boot_device_path(*prop->bootindex, prop->dev, prop->suffix); + +out: + if (local_err) { + error_propagate(errp, local_err); + } +} + +static void property_release_bootindex(Object *obj, const char *name, + void *opaque) + +{ + BootIndexProperty *prop = opaque; + + del_boot_device_path(prop->dev, prop->suffix); + g_free(prop); +} + +void device_add_bootindex_property(Object *obj, int32_t *bootindex, + const char *name, const char *suffix, + DeviceState *dev, Error **errp) +{ + Error *local_err = NULL; + BootIndexProperty *prop = g_malloc0(sizeof(*prop)); + + prop->bootindex = bootindex; + prop->suffix = suffix; + prop->dev = dev; + + object_property_add(obj, name, "int32", + device_get_bootindex, + device_set_bootindex, + property_release_bootindex, + prop, &local_err); + + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + return; + } + /* initialize devices' bootindex property to -1 */ + object_property_set_int(obj, -1, name, NULL); +} @@ -4211,9 +4211,9 @@ EOF fi fi -# add pixman flags after all config tests are done -QEMU_CFLAGS="$QEMU_CFLAGS $pixman_cflags $fdt_cflags" -libs_softmmu="$libs_softmmu $pixman_libs" +# prepend pixman and ftd flags after all config tests are done +QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS" +libs_softmmu="$pixman_libs $libs_softmmu" echo "Install prefix $prefix" echo "BIOS directory `eval echo $qemu_datadir`" diff --git a/disas/mips.c b/disas/mips.c index 2106b574cb..7297825138 100644 --- a/disas/mips.c +++ b/disas/mips.c @@ -119,6 +119,8 @@ see <http://www.gnu.org/licenses/>. */ #define OP_SH_IMMEDIATE 0 #define OP_MASK_DELTA 0xffff #define OP_SH_DELTA 0 +#define OP_MASK_DELTA_R6 0x1ff +#define OP_SH_DELTA_R6 7 #define OP_MASK_FUNCT 0x3f #define OP_SH_FUNCT 0 #define OP_MASK_SPEC 0x3f @@ -405,6 +407,12 @@ struct mips_opcode "+3" UDI immediate bits 6-20 "+4" UDI immediate bits 6-25 + R6 immediates/displacements : + (adding suffix to 'o' to avoid adding new characters) + "+o" 9 bits immediate/displacement (shift = 7) + "+o1" 18 bits immediate/displacement (shift = 0) + "+o2" 19 bits immediate/displacement (shift = 0) + Other: "()" parens surrounding optional value "," separates operands @@ -521,6 +529,8 @@ struct mips_opcode #define INSN_ISA64 0x00000040 #define INSN_ISA32R2 0x00000080 #define INSN_ISA64R2 0x00000100 +#define INSN_ISA32R6 0x00000200 +#define INSN_ISA64R6 0x00000400 /* Masks used for MIPS-defined ASEs. */ #define INSN_ASE_MASK 0x0000f000 @@ -585,6 +595,8 @@ struct mips_opcode #define ISA_MIPS32R2 (ISA_MIPS32 | INSN_ISA32R2) #define ISA_MIPS64R2 (ISA_MIPS64 | INSN_ISA32R2 | INSN_ISA64R2) +#define ISA_MIPS32R6 (ISA_MIPS32R2 | INSN_ISA32R6) +#define ISA_MIPS64R6 (ISA_MIPS64R2 | INSN_ISA32R6 | INSN_ISA64R6) /* CPU defines, use instead of hardcoding processor number. Keep this in sync with bfd/archures.c in order for machine selection to work. */ @@ -1121,6 +1133,8 @@ extern const int bfd_mips16_num_opcodes; #define I64 INSN_ISA64 #define I33 INSN_ISA32R2 #define I65 INSN_ISA64R2 +#define I32R6 INSN_ISA32R6 +#define I64R6 INSN_ISA64R6 /* MIPS64 MIPS-3D ASE support. */ #define I16 INSN_MIPS16 @@ -1209,6 +1223,146 @@ const struct mips_opcode mips_builtin_opcodes[] = them first. The assemblers uses a hash table based on the instruction name anyhow. */ /* name, args, match, mask, pinfo, membership */ +{"lwpc", "s,+o2", 0xec080000, 0xfc180000, WR_d, 0, I32R6}, +{"lwupc", "s,+o2", 0xec100000, 0xfc180000, WR_d, 0, I64R6}, +{"ldpc", "s,+o1", 0xec180000, 0xfc1c0000, WR_d, 0, I64R6}, +{"addiupc", "s,+o2", 0xec000000, 0xfc180000, WR_d, 0, I32R6}, +{"auipc", "s,u", 0xec1e0000, 0xfc1f0000, WR_d, 0, I32R6}, +{"aluipc", "s,u", 0xec1f0000, 0xfc1f0000, WR_d, 0, I32R6}, +{"daui", "s,t,u", 0x74000000, 0xfc000000, RD_s|WR_t, 0, I64R6}, +{"dahi", "s,u", 0x04060000, 0xfc1f0000, RD_s, 0, I64R6}, +{"dati", "s,u", 0x041e0000, 0xfc1f0000, RD_s, 0, I64R6}, +{"lsa", "d,s,t", 0x00000005, 0xfc00073f, WR_d|RD_s|RD_t, 0, I32R6}, +{"dlsa", "d,s,t", 0x00000015, 0xfc00073f, WR_d|RD_s|RD_t, 0, I64R6}, +{"clz", "U,s", 0x00000050, 0xfc1f07ff, WR_d|RD_s, 0, I32R6}, +{"clo", "U,s", 0x00000051, 0xfc1f07ff, WR_d|RD_s, 0, I32R6}, +{"dclz", "U,s", 0x00000052, 0xfc1f07ff, WR_d|RD_s, 0, I64R6}, +{"dclo", "U,s", 0x00000053, 0xfc1f07ff, WR_d|RD_s, 0, I64R6}, +{"sdbbp", "B", 0x0000000e, 0xfc00003f, TRAP, 0, I32R6}, +{"mul", "d,s,t", 0x00000098, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"muh", "d,s,t", 0x000000d8, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"mulu", "d,s,t", 0x00000099, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"muhu", "d,s,t", 0x000000d9, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"div", "d,s,t", 0x0000009a, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"mod", "d,s,t", 0x000000da, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"divu", "d,s,t", 0x0000009b, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"modu", "d,s,t", 0x000000db, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"dmul", "d,s,t", 0x0000009c, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmuh", "d,s,t", 0x000000dc, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmulu", "d,s,t", 0x0000009d, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmuhu", "d,s,t", 0x000000dd, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"ddiv", "d,s,t", 0x0000009e, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmod", "d,s,t", 0x000000de, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"ddivu", "d,s,t", 0x0000009f, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"dmodu", "d,s,t", 0x000000df, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I64R6}, +{"ll", "t,o(b)", 0x7c000036, 0xfc00007f, LDD|RD_b|WR_t, 0, I32R6}, +{"sc", "t,o(b)", 0x7c000026, 0xfc00007f, LDD|RD_b|WR_t, 0, I32R6}, +{"lld", "t,o(b)", 0x7c000037, 0xfc00007f, LDD|RD_b|WR_t, 0, I64R6}, +{"scd", "t,o(b)", 0x7c000027, 0xfc00007f, LDD|RD_b|WR_t, 0, I64R6}, +{"pref", "h,o(b)", 0x7c000035, 0xfc00007f, RD_b, 0, I32R6}, +{"cache", "k,o(b)", 0x7c000025, 0xfc00007f, RD_b, 0, I32R6}, +{"seleqz", "d,v,t", 0x00000035, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"selnez", "d,v,t", 0x00000037, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I32R6}, +{"maddf.s", "D,S,T", 0x46000018, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"maddf.d", "D,S,T", 0x46200018, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"msubf.s", "D,S,T", 0x46000019, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"msubf.d", "D,S,T", 0x46200019, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"max.s", "D,S,T", 0x4600001e, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"max.d", "D,S,T", 0x4620001e, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"maxa.s", "D,S,T", 0x4600001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"maxa.d", "D,S,T", 0x4620001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"rint.s", "D,S", 0x4600001a, 0xffff003f, WR_D|RD_S|FP_S, 0, I32R6}, +{"rint.d", "D,S", 0x4620001a, 0xffff003f, WR_D|RD_S|FP_D, 0, I32R6}, +{"class.s", "D,S", 0x4600001b, 0xffff003f, WR_D|RD_S|FP_S, 0, I32R6}, +{"class.d", "D,S", 0x4620001b, 0xffff003f, WR_D|RD_S|FP_D, 0, I32R6}, +{"min.s", "D,S,T", 0x4600001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"min.d", "D,S,T", 0x4620001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"mina.s", "D,S,T", 0x4600001d, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"mina.d", "D,S,T", 0x4620001d, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"sel.s", "D,S,T", 0x46000010, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"sel.d", "D,S,T", 0x46200010, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"seleqz.s", "D,S,T", 0x46000014, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"seleqz.d", "D,S,T", 0x46200014, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"selnez.s", "D,S,T", 0x46000017, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I32R6}, +{"selnez.d", "D,S,T", 0x46200017, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I32R6}, +{"align", "d,v,t", 0x7c000220, 0xfc00073f, WR_d|RD_s|RD_t, 0, I32R6}, +{"dalign", "d,v,t", 0x7c000224, 0xfc00063f, WR_d|RD_s|RD_t, 0, I64R6}, +{"bitswap", "d,w", 0x7c000020, 0xffe007ff, WR_d|RD_t, 0, I32R6}, +{"dbitswap","d,w", 0x7c000024, 0xffe007ff, WR_d|RD_t, 0, I64R6}, +{"balc", "+p", 0xe8000000, 0xfc000000, UBD|WR_31, 0, I32R6}, +{"bc", "+p", 0xc8000000, 0xfc000000, UBD|WR_31, 0, I32R6}, +{"jic", "t,o", 0xd8000000, 0xffe00000, UBD|RD_t, 0, I32R6}, +{"beqzc", "s,+p", 0xd8000000, 0xfc000000, CBD|RD_s, 0, I32R6}, +{"jialc", "t,o", 0xf8000000, 0xffe00000, UBD|RD_t, 0, I32R6}, +{"bnezc", "s,+p", 0xf8000000, 0xfc000000, CBD|RD_s, 0, I32R6}, +{"beqzalc", "s,t,p", 0x20000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bovc", "s,t,p", 0x20000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"beqc", "s,t,p", 0x20000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bnezalc", "s,t,p", 0x60000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bnvc", "s,t,p", 0x60000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bnec", "s,t,p", 0x60000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"blezc", "s,t,p", 0x58000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgezc", "s,t,p", 0x58000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgec", "s,t,p", 0x58000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgtzc", "s,t,p", 0x5c000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bltzc", "s,t,p", 0x5c000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bltc", "s,t,p", 0x5c000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"blezalc", "s,t,p", 0x18000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgezalc", "s,t,p", 0x18000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgeuc", "s,t,p", 0x18000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bgtzalc", "s,t,p", 0x1c000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, +{"bltzalc", "s,t,p", 0x1c000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"bltuc", "s,t,p", 0x1c000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, +{"nal", "p", 0x04100000, 0xffff0000, WR_31, 0, I32R6}, +{"bal", "p", 0x04110000, 0xffff0000, UBD|WR_31, 0, I32R6}, +{"bc1eqz", "T,p", 0x45200000, 0xffe00000, CBD|RD_T|FP_S|FP_D, 0, I32R6}, +{"bc1nez", "T,p", 0x45a00000, 0xffe00000, CBD|RD_T|FP_S|FP_D, 0, I32R6}, +{"bc2eqz", "E,p", 0x49200000, 0xffe00000, CBD|RD_C2, 0, I32R6}, +{"bc2nez", "E,p", 0x49a00000, 0xffe00000, CBD|RD_C2, 0, I32R6}, +{"cmp.af.s", "D,S,T", 0x46800000, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.un.s", "D,S,T", 0x46800001, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.eq.s", "D,S,T", 0x46800002, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.ueq.s", "D,S,T", 0x46800003, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.lt.s", "D,S,T", 0x46800004, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.ult.s", "D,S,T", 0x46800005, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.le.s", "D,S,T", 0x46800006, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.ule.s", "D,S,T", 0x46800007, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.saf.s", "D,S,T", 0x46800008, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sun.s", "D,S,T", 0x46800009, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.seq.s", "D,S,T", 0x4680000a, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sueq.s", "D,S,T", 0x4680000b, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.slt.s", "D,S,T", 0x4680000c, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sult.s", "D,S,T", 0x4680000d, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sle.s", "D,S,T", 0x4680000e, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sule.s", "D,S,T", 0x4680000f, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.or.s", "D,S,T", 0x46800011, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.une.s", "D,S,T", 0x46800012, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.ne.s", "D,S,T", 0x46800013, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sor.s", "D,S,T", 0x46800019, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sune.s", "D,S,T", 0x4680001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.sne.s", "D,S,T", 0x4680001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_S, 0, I32R6}, +{"cmp.af.d", "D,S,T", 0x46a00000, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.un.d", "D,S,T", 0x46a00001, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.eq.d", "D,S,T", 0x46a00002, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.ueq.d", "D,S,T", 0x46a00003, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.lt.d", "D,S,T", 0x46a00004, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.ult.d", "D,S,T", 0x46a00005, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.le.d", "D,S,T", 0x46a00006, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.ule.d", "D,S,T", 0x46a00007, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.saf.d", "D,S,T", 0x46a00008, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sun.d", "D,S,T", 0x46a00009, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.seq.d", "D,S,T", 0x46a0000a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sueq.d", "D,S,T", 0x46a0000b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.slt.d", "D,S,T", 0x46a0000c, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sult.d", "D,S,T", 0x46a0000d, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sle.d", "D,S,T", 0x46a0000e, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sule.d", "D,S,T", 0x46a0000f, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.or.d", "D,S,T", 0x46a00011, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.une.d", "D,S,T", 0x46a00012, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.ne.d", "D,S,T", 0x46a00013, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sor.d", "D,S,T", 0x46a00019, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sune.d", "D,S,T", 0x46a0001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, +{"cmp.sne.d", "D,S,T", 0x46a0001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, {"pref", "k,o(b)", 0xcc000000, 0xfc000000, RD_b, 0, I4|I32|G3 }, {"prefx", "h,t(b)", 0x4c00000f, 0xfc0007ff, RD_b|RD_t, 0, I4|I33 }, {"nop", "", 0x00000000, 0xffffffff, 0, INSN2_ALIAS, I1 }, /* sll */ @@ -1753,6 +1907,7 @@ const struct mips_opcode mips_builtin_opcodes[] = {"lld", "t,o(b)", 0xd0000000, 0xfc000000, LDD|RD_b|WR_t, 0, I3 }, {"lld", "t,A(b)", 0, (int) M_LLD_AB, INSN_MACRO, 0, I3 }, {"lui", "t,u", 0x3c000000, 0xffe00000, WR_t, 0, I1 }, +{"aui", "s,t,u", 0x3c000000, 0xfc000000, RD_s|WR_t, 0, I32R6}, {"luxc1", "D,t(b)", 0x4c000005, 0xfc00f83f, LDD|WR_D|RD_t|RD_b|FP_D, 0, I5|I33|N55}, {"lw", "t,o(b)", 0x8c000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 }, {"lw", "t,A(b)", 0, (int) M_LW_AB, INSN_MACRO, 0, I1 }, @@ -3575,6 +3730,42 @@ print_insn_args (const char *d, (*info->fprintf_func) (info->stream, "0x%x", msbd + 1); break; + case 'o': + switch (*(d+1)) { + case '1': + d++; + delta = l & ((1 << 18) - 1); + if (delta & 0x20000) { + delta |= ~0x1ffff; + } + break; + case '2': + d++; + delta = l & ((1 << 19) - 1); + if (delta & 0x40000) { + delta |= ~0x3ffff; + } + break; + default: + delta = (l >> OP_SH_DELTA_R6) & OP_MASK_DELTA_R6; + if (delta & 0x8000) { + delta |= ~0xffff; + } + } + + (*info->fprintf_func) (info->stream, "%d", delta); + break; + + case 'p': + /* Sign extend the displacement with 26 bits. */ + delta = (l >> OP_SH_DELTA) & OP_MASK_TARGET; + if (delta & 0x2000000) { + delta |= ~0x3FFFFFF; + } + info->target = (delta << 2) + pc + INSNLEN; + (*info->print_address_func) (info->target, info); + break; + case 't': /* Coprocessor 0 reg name */ (*info->fprintf_func) (info->stream, "%s", mips_cp0_names[(l >> OP_SH_RT) & @@ -3726,7 +3917,8 @@ print_insn_args (const char *d, case 'j': /* Same as i, but sign-extended. */ case 'o': - delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; + delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; + if (delta & 0x8000) delta |= ~0xffff; (*info->fprintf_func) (info->stream, "%d", @@ -4052,6 +4244,23 @@ print_insn_mips (bfd_vma memaddr, && strcmp (op->name, "jalx")) continue; + if (strcmp(op->name, "bovc") == 0 + || strcmp(op->name, "bnvc") == 0) { + if (((word >> OP_SH_RS) & OP_MASK_RS) < + ((word >> OP_SH_RT) & OP_MASK_RT)) { + continue; + } + } + if (strcmp(op->name, "bgezc") == 0 + || strcmp(op->name, "bltzc") == 0 + || strcmp(op->name, "bgezalc") == 0 + || strcmp(op->name, "bltzalc") == 0) { + if (((word >> OP_SH_RS) & OP_MASK_RS) != + ((word >> OP_SH_RT) & OP_MASK_RT)) { + continue; + } + } + /* Figure out instruction type and branch delay information. */ if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) { diff --git a/docs/specs/standard-vga.txt b/docs/specs/standard-vga.txt index f82773e677..19d2a74509 100644 --- a/docs/specs/standard-vga.txt +++ b/docs/specs/standard-vga.txt @@ -70,3 +70,12 @@ Likewise applies to the pci variant only for obvious reasons. 0500 - 0515 : bochs dispi interface registers, mapped flat without index/data ports. Use (index << 1) as offset for (16bit) register access. + +0600 - 0607 : qemu extended registers. qemu 2.2+ only. + The pci revision is 2 (or greater) when + these registers are present. The registers + are 32bit. + 0600 : qemu extended register region size, in bytes. + 0604 : framebuffer endianness register. + - 0xbebebebe indicates big endian. + - 0x1e1e1e1e indicates little endian. diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 9274ebf101..16b21ebe61 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -7240,13 +7240,17 @@ int float128_compare_quiet( float128 a, float128 b STATUS_PARAM ) * minnum() and maxnum correspond to the IEEE 754-2008 minNum() * and maxNum() operations. min() and max() are the typical min/max * semantics provided by many CPUs which predate that specification. + * + * minnummag() and maxnummag() functions correspond to minNumMag() + * and minNumMag() from the IEEE-754 2008. */ #define MINMAX(s) \ static inline float ## s float ## s ## _minmax(float ## s a, float ## s b, \ - int ismin, int isieee STATUS_PARAM) \ + int ismin, int isieee, \ + int ismag STATUS_PARAM) \ { \ flag aSign, bSign; \ - uint ## s ## _t av, bv; \ + uint ## s ## _t av, bv, aav, abv; \ a = float ## s ## _squash_input_denormal(a STATUS_VAR); \ b = float ## s ## _squash_input_denormal(b STATUS_VAR); \ if (float ## s ## _is_any_nan(a) || \ @@ -7266,6 +7270,17 @@ static inline float ## s float ## s ## _minmax(float ## s a, float ## s b, \ bSign = extractFloat ## s ## Sign(b); \ av = float ## s ## _val(a); \ bv = float ## s ## _val(b); \ + if (ismag) { \ + aav = float ## s ## _abs(av); \ + abv = float ## s ## _abs(bv); \ + if (aav != abv) { \ + if (ismin) { \ + return (aav < abv) ? a : b; \ + } else { \ + return (aav < abv) ? b : a; \ + } \ + } \ + } \ if (aSign != bSign) { \ if (ismin) { \ return aSign ? a : b; \ @@ -7283,22 +7298,32 @@ static inline float ## s float ## s ## _minmax(float ## s a, float ## s b, \ \ float ## s float ## s ## _min(float ## s a, float ## s b STATUS_PARAM) \ { \ - return float ## s ## _minmax(a, b, 1, 0 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 1, 0, 0 STATUS_VAR); \ } \ \ float ## s float ## s ## _max(float ## s a, float ## s b STATUS_PARAM) \ { \ - return float ## s ## _minmax(a, b, 0, 0 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 0, 0, 0 STATUS_VAR); \ } \ \ float ## s float ## s ## _minnum(float ## s a, float ## s b STATUS_PARAM) \ { \ - return float ## s ## _minmax(a, b, 1, 1 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 1, 1, 0 STATUS_VAR); \ } \ \ float ## s float ## s ## _maxnum(float ## s a, float ## s b STATUS_PARAM) \ { \ - return float ## s ## _minmax(a, b, 0, 1 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 0, 1, 0 STATUS_VAR); \ +} \ + \ +float ## s float ## s ## _minnummag(float ## s a, float ## s b STATUS_PARAM) \ +{ \ + return float ## s ## _minmax(a, b, 1, 1, 1 STATUS_VAR); \ +} \ + \ +float ## s float ## s ## _maxnummag(float ## s a, float ## s b STATUS_PARAM) \ +{ \ + return float ## s ## _minmax(a, b, 0, 1, 1 STATUS_VAR); \ } MINMAX(32) diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index b72b34e5c9..0bfa814f71 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -354,8 +354,8 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, } } -static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { PIIX4PMState *s = PIIX4_PM(hotplug_dev); @@ -615,7 +615,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) dc->cannot_instantiate_with_device_add_yet = true; dc->hotpluggable = false; hc->plug = piix4_device_plug_cb; - hc->unplug = piix4_device_unplug_cb; + hc->unplug_request = piix4_device_unplug_request_cb; adevc->ospm_status = piix4_ospm_status; } diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 6c86a6b59e..34c1d8f1c9 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -2216,9 +2216,6 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - - add_boot_device_path(isa->bootindexA, dev, "/floppy@0"); - add_boot_device_path(isa->bootindexB, dev, "/floppy@1"); } static void sysbus_fdc_initfn(Object *obj) @@ -2291,8 +2288,6 @@ static Property isa_fdc_properties[] = { DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].bs), DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs), - DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1), - DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1), DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate, 0, true), DEFINE_PROP_END_OF_LIST(), @@ -2310,11 +2305,24 @@ static void isabus_fdc_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } +static void isabus_fdc_instance_init(Object *obj) +{ + FDCtrlISABus *isa = ISA_FDC(obj); + + device_add_bootindex_property(obj, &isa->bootindexA, + "bootindexA", "/floppy@0", + DEVICE(obj), NULL); + device_add_bootindex_property(obj, &isa->bootindexB, + "bootindexB", "/floppy@1", + DEVICE(obj), NULL); +} + static const TypeInfo isa_fdc_info = { .name = TYPE_ISA_FDC, .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(FDCtrlISABus), .class_init = isabus_fdc_class_init, + .instance_init = isabus_fdc_instance_init, }; static const VMStateDescription vmstate_sysbus_fdc ={ diff --git a/hw/block/nvme.c b/hw/block/nvme.c index b010c9b00f..9a95f7530b 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -24,6 +24,8 @@ #include <hw/hw.h> #include <hw/pci/msix.h> #include <hw/pci/pci.h> +#include "sysemu/sysemu.h" +#include "qapi/visitor.h" #include "nvme.h" @@ -871,11 +873,53 @@ static void nvme_class_init(ObjectClass *oc, void *data) dc->vmsd = &nvme_vmstate; } +static void nvme_get_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + NvmeCtrl *s = NVME(obj); + + visit_type_int32(v, &s->conf.bootindex, name, errp); +} + +static void nvme_set_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + NvmeCtrl *s = NVME(obj); + int32_t boot_index; + Error *local_err = NULL; + + visit_type_int32(v, &boot_index, name, &local_err); + if (local_err) { + goto out; + } + /* check whether bootindex is present in fw_boot_order list */ + check_boot_index(boot_index, &local_err); + if (local_err) { + goto out; + } + /* change bootindex to a new one */ + s->conf.bootindex = boot_index; + +out: + if (local_err) { + error_propagate(errp, local_err); + } +} + +static void nvme_instance_init(Object *obj) +{ + object_property_add(obj, "bootindex", "int32", + nvme_get_bootindex, + nvme_set_bootindex, NULL, NULL, NULL); + object_property_set_int(obj, -1, "bootindex", NULL); +} + static const TypeInfo nvme_info = { .name = "nvme", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(NvmeCtrl), .class_init = nvme_class_init, + .instance_init = nvme_instance_init, }; static void nvme_register_types(void) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 45e0c8f6e9..1fa97709c8 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -768,8 +768,6 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) bdrv_set_guest_block_size(s->bs, s->conf->logical_block_size); bdrv_iostatus_enable(s->bs); - - add_boot_device_path(s->conf->bootindex, dev, "/disk@0,0"); } static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp) @@ -794,6 +792,9 @@ static void virtio_blk_instance_init(Object *obj) (Object **)&s->blk.iothread, qdev_prop_allow_set_link_before_realize, OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); + device_add_bootindex_property(obj, &s->blk.conf.bootindex, + "bootindex", "/disk@0,0", + DEVICE(obj), NULL); } static Property virtio_blk_properties[] = { diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 3931085983..c6870f19e1 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -904,6 +904,12 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) } port->elem.out_num = 0; +} + +static void virtser_port_device_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); port->ivq = port->vser->ivqs[port->id]; @@ -912,7 +918,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) add_port(port->vser, port->id); /* Send an update to the guest about this new port added */ - virtio_notify_config(vdev); + virtio_notify_config(VIRTIO_DEVICE(hotplug_dev)); } static void virtser_port_device_unrealize(DeviceState *dev, Error **errp) @@ -935,7 +941,6 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSerial *vser = VIRTIO_SERIAL(dev); - BusState *bus; uint32_t i, max_supported_ports; if (!vser->serial.max_virtserial_ports) { @@ -957,8 +962,7 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) /* Spawn a new virtio-serial bus on which the ports will ride as devices */ qbus_create_inplace(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS, dev, vdev->bus_name); - bus = BUS(&vser->bus); - bus->allow_hotplug = 1; + qbus_set_hotplug_handler(BUS(&vser->bus), DEVICE(vser), errp); vser->bus.vser = vser; QTAILQ_INIT(&vser->ports); @@ -1021,7 +1025,6 @@ static void virtio_serial_port_class_init(ObjectClass *klass, void *data) k->bus_type = TYPE_VIRTIO_SERIAL_BUS; k->realize = virtser_port_device_realize; k->unrealize = virtser_port_device_unrealize; - k->unplug = qdev_simple_unplug_cb; k->props = virtser_props; } @@ -1064,6 +1067,7 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); QLIST_INIT(&vserdevices.devices); @@ -1077,6 +1081,8 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data) vdc->reset = vser_reset; vdc->save = virtio_serial_save_device; vdc->load = virtio_serial_load_device; + hc->plug = virtser_port_device_plug; + hc->unplug = qdev_simple_device_unplug_cb; } static const TypeInfo virtio_device_info = { @@ -1084,6 +1090,10 @@ static const TypeInfo virtio_device_info = { .parent = TYPE_VIRTIO_DEVICE, .instance_size = sizeof(VirtIOSerial), .class_init = virtio_serial_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static void virtio_serial_register_types(void) diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c index 5573d9d2d9..4e01074557 100644 --- a/hw/core/hotplug.c +++ b/hw/core/hotplug.c @@ -23,6 +23,17 @@ void hotplug_handler_plug(HotplugHandler *plug_handler, } } +void hotplug_handler_unplug_request(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->unplug_request) { + hdc->unplug_request(plug_handler, plugged_dev, errp); + } +} + void hotplug_handler_unplug(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 84caa1d694..f2bd954be5 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -112,7 +112,7 @@ static void set_drive(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_drive = { .name = "str", - .legacy_name = "drive", + .description = "ID of a drive to use as a backend", .get = get_drive, .set = set_drive, .release = release_drive, @@ -169,7 +169,7 @@ static void set_chr(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_chr = { .name = "str", - .legacy_name = "chr", + .description = "ID of a chardev to use as a backend", .get = get_chr, .set = set_chr, .release = release_chr, @@ -248,7 +248,7 @@ static void set_netdev(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_netdev = { .name = "str", - .legacy_name = "netdev", + .description = "ID of a netdev to use as a backend", .get = get_netdev, .set = set_netdev, }; @@ -328,7 +328,7 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_vlan = { .name = "int32", - .legacy_name = "vlan", + .description = "Integer VLAN id to connect to", .print = print_vlan, .get = get_vlan, .set = set_vlan, diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 66556d3bf9..2ed995f2b7 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -120,7 +120,7 @@ static void prop_set_bit(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_bit = { .name = "bool", - .legacy_name = "on/off", + .description = "on/off", .get = prop_get_bit, .set = prop_set_bit, }; @@ -455,7 +455,7 @@ inval: PropertyInfo qdev_prop_macaddr = { .name = "str", - .legacy_name = "macaddr", + .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", .get = get_mac, .set = set_mac, }; @@ -477,7 +477,8 @@ QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); PropertyInfo qdev_prop_bios_chs_trans = { .name = "BiosAtaTranslation", - .legacy_name = "bios-chs-trans", + .description = "Logical CHS translation algorithm, " + "auto/none/lba/large/rechs", .enum_table = BiosAtaTranslation_lookup, .get = get_enum, .set = set_enum, @@ -551,7 +552,7 @@ static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, PropertyInfo qdev_prop_pci_devfn = { .name = "int32", - .legacy_name = "pci-devfn", + .description = "Slot and optional function number, example: 06.0 or 06", .print = print_pci_devfn, .get = get_int32, .set = set_pci_devfn, @@ -598,7 +599,7 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_blocksize = { .name = "uint16", - .legacy_name = "blocksize", + .description = "A power of two between 512 and 32768", .get = get_uint16, .set = set_blocksize, }; @@ -706,7 +707,8 @@ inval: PropertyInfo qdev_prop_pci_host_devaddr = { .name = "str", - .legacy_name = "pci-host-devaddr", + .description = "Address (bus/device/function) of " + "the host device, example: 04:10.0", .get = get_pci_host_devaddr, .set = set_pci_host_devaddr, }; diff --git a/hw/core/qdev.c b/hw/core/qdev.c index fcb16383a1..a1e9247772 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -85,10 +85,6 @@ static void bus_add_child(BusState *bus, DeviceState *child) char name[32]; BusChild *kid = g_malloc0(sizeof(*kid)); - if (qdev_hotplug) { - assert(bus->allow_hotplug); - } - kid->index = bus->max_index++; kid->child = child; object_ref(OBJECT(kid->child)); @@ -112,6 +108,24 @@ void qdev_set_parent_bus(DeviceState *dev, BusState *bus) bus_add_child(bus, dev); } +static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler, + Error **errp) +{ + + object_property_set_link(OBJECT(bus), OBJECT(handler), + QDEV_HOTPLUG_HANDLER_PROPERTY, errp); +} + +void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp) +{ + qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp); +} + +void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp) +{ + qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp); +} + /* Create a new device. This only initializes the device state structure and allows properties to be set. qdev_init should be called to initialize the actual device emulation. */ @@ -209,11 +223,30 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, dev->alias_required_for_version = required_for_version; } +static HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) +{ + HotplugHandler *hotplug_ctrl = NULL; + + if (dev->parent_bus && dev->parent_bus->hotplug_handler) { + hotplug_ctrl = dev->parent_bus->hotplug_handler; + } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) { + MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + + if (mc->get_hotplug_handler) { + hotplug_ctrl = mc->get_hotplug_handler(machine, dev); + } + } + return hotplug_ctrl; +} + void qdev_unplug(DeviceState *dev, Error **errp) { DeviceClass *dc = DEVICE_GET_CLASS(dev); + HotplugHandler *hotplug_ctrl; + HotplugHandlerClass *hdc; - if (dev->parent_bus && !dev->parent_bus->allow_hotplug) { + if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); return; } @@ -226,13 +259,18 @@ void qdev_unplug(DeviceState *dev, Error **errp) qdev_hot_removed = true; - if (dev->parent_bus && dev->parent_bus->hotplug_handler) { - hotplug_handler_unplug(dev->parent_bus->hotplug_handler, dev, errp); + hotplug_ctrl = qdev_get_hotplug_handler(dev); + /* hotpluggable device MUST have HotplugHandler, if it doesn't + * then something is very wrong with it */ + g_assert(hotplug_ctrl); + + /* If device supports async unplug just request it to be done, + * otherwise just remove it synchronously */ + hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl); + if (hdc->unplug_request) { + hotplug_handler_unplug_request(hotplug_ctrl, dev, errp); } else { - assert(dc->unplug != NULL); - if (dc->unplug(dev) < 0) { /* legacy handler */ - error_set(errp, QERR_UNDEFINED_ERROR); - } + hotplug_handler_unplug(hotplug_ctrl, dev, errp); } } @@ -269,14 +307,13 @@ void qbus_reset_all_fn(void *opaque) } /* can be used as ->unplug() callback for the simple cases */ -int qdev_simple_unplug_cb(DeviceState *dev) +void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { /* just zap it */ object_unparent(OBJECT(dev)); - return 0; } - /* Like qdev_init(), but terminate program via error_report() instead of returning an error value. This is okay during machine creation. Don't use for hotplug, because there callers need to recover from @@ -337,10 +374,20 @@ static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev, void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler, const char *name, int n) { + int i; NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); + char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-in"); + assert(gpio_list->num_out == 0 || !name); gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler, dev, n); + + for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) { + object_property_add_child(OBJECT(dev), propname, + OBJECT(gpio_list->in[i]), &error_abort); + } + g_free(propname); + gpio_list->num_in += n; } @@ -352,11 +399,24 @@ void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n) void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, const char *name, int n) { + int i; NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); + char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-out"); + assert(gpio_list->num_in == 0 || !name); assert(gpio_list->num_out == 0); gpio_list->num_out = n; gpio_list->out = pins; + + for (i = 0; i < n; ++i) { + memset(&pins[i], 0, sizeof(*pins)); + object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, + (Object **)&pins[i], + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); + } + g_free(propname); } void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n) @@ -766,6 +826,11 @@ void qdev_property_add_static(DeviceState *dev, Property *prop, error_propagate(errp, local_err); return; } + + object_property_set_description(obj, prop->name, + prop->info->description, + &error_abort); + if (prop->qtype == QTYPE_NONE) { return; } @@ -811,6 +876,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) { DeviceState *dev = DEVICE(obj); DeviceClass *dc = DEVICE_GET_CLASS(dev); + HotplugHandler *hotplug_ctrl; BusState *bus; Error *local_err = NULL; @@ -838,20 +904,9 @@ static void device_set_realized(Object *obj, bool value, Error **errp) goto fail; } - if (dev->parent_bus && dev->parent_bus->hotplug_handler) { - hotplug_handler_plug(dev->parent_bus->hotplug_handler, - dev, &local_err); - } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) { - HotplugHandler *hotplug_ctrl; - MachineState *machine = MACHINE(qdev_get_machine()); - MachineClass *mc = MACHINE_GET_CLASS(machine); - - if (mc->get_hotplug_handler) { - hotplug_ctrl = mc->get_hotplug_handler(machine, dev); - if (hotplug_ctrl) { - hotplug_handler_plug(hotplug_ctrl, dev, &local_err); - } - } + hotplug_ctrl = qdev_get_hotplug_handler(dev); + if (hotplug_ctrl) { + hotplug_handler_plug(hotplug_ctrl, dev, &local_err); } if (local_err != NULL) { @@ -925,7 +980,7 @@ static bool device_get_hotpluggable(Object *obj, Error **errp) DeviceState *dev = DEVICE(obj); return dc->hotpluggable && (dev->parent_bus == NULL || - dev->parent_bus->allow_hotplug); + qbus_is_hotpluggable(dev->parent_bus)); } static bool device_get_hotplugged(Object *obj, Error **err) diff --git a/hw/cpu/icc_bus.c b/hw/cpu/icc_bus.c index 7f44c59b25..9575fd6a42 100644 --- a/hw/cpu/icc_bus.c +++ b/hw/cpu/icc_bus.c @@ -24,18 +24,10 @@ /* icc-bridge implementation */ -static void icc_bus_init(Object *obj) -{ - BusState *b = BUS(obj); - - b->allow_hotplug = true; -} - static const TypeInfo icc_bus_info = { .name = TYPE_ICC_BUS, .parent = TYPE_BUS, .instance_size = sizeof(ICCBus), - .instance_init = icc_bus_init, }; diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 93b3518b21..b540dd656c 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -1591,6 +1591,11 @@ async_common: qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n", update.left, update.top, update.right, update.bottom); + if (update.left == update.right || update.top == update.bottom) { + /* old drivers may provide empty area, keep going */ + qxl_clear_guest_bug(d); + goto cancel_async; + } break; } if (async == QXL_ASYNC) { diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index 0351d94707..db922f1843 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -35,10 +35,18 @@ #define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) #define PCI_VGA_BOCHS_OFFSET 0x500 #define PCI_VGA_BOCHS_SIZE (0x0b * 2) +#define PCI_VGA_QEXT_OFFSET 0x600 +#define PCI_VGA_QEXT_SIZE (2 * 4) #define PCI_VGA_MMIO_SIZE 0x1000 +#define PCI_VGA_QEXT_REG_SIZE (0 * 4) +#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) +#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e +#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe + enum vga_pci_flags { PCI_VGA_FLAG_ENABLE_MMIO = 1, + PCI_VGA_FLAG_ENABLE_QEXT = 2, }; typedef struct PCIVGAState { @@ -48,6 +56,7 @@ typedef struct PCIVGAState { MemoryRegion mmio; MemoryRegion ioport; MemoryRegion bochs; + MemoryRegion qext; } PCIVGAState; static const VMStateDescription vmstate_vga_pci = { @@ -140,6 +149,46 @@ static const MemoryRegionOps pci_vga_bochs_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static uint64_t pci_vga_qext_read(void *ptr, hwaddr addr, unsigned size) +{ + PCIVGAState *d = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_SIZE: + return PCI_VGA_QEXT_SIZE; + case PCI_VGA_QEXT_REG_BYTEORDER: + return d->vga.big_endian_fb ? + PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN; + default: + return 0; + } +} + +static void pci_vga_qext_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + PCIVGAState *d = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_BYTEORDER: + if (val == PCI_VGA_QEXT_BIG_ENDIAN) { + d->vga.big_endian_fb = true; + } + if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) { + d->vga.big_endian_fb = false; + } + break; + } +} + +static const MemoryRegionOps pci_vga_qext_ops = { + .read = pci_vga_qext_read, + .write = pci_vga_qext_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static int pci_std_vga_initfn(PCIDevice *dev) { PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev); @@ -167,6 +216,15 @@ static int pci_std_vga_initfn(PCIDevice *dev) &d->ioport); memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET, &d->bochs); + + if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { + memory_region_init_io(&d->qext, NULL, &pci_vga_qext_ops, d, + "qemu extended regs", PCI_VGA_QEXT_SIZE); + memory_region_add_subregion(&d->mmio, PCI_VGA_QEXT_OFFSET, + &d->qext); + pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); + } + pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); } @@ -199,6 +257,14 @@ static int pci_secondary_vga_initfn(PCIDevice *dev) memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET, &d->bochs); + if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { + memory_region_init_io(&d->qext, NULL, &pci_vga_qext_ops, d, + "qemu extended regs", PCI_VGA_QEXT_SIZE); + memory_region_add_subregion(&d->mmio, PCI_VGA_QEXT_OFFSET, + &d->qext); + pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); + } + pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); @@ -215,11 +281,15 @@ static void pci_secondary_vga_reset(DeviceState *dev) static Property vga_pci_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16), DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true), + DEFINE_PROP_BIT("qemu-extended-regs", + PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), DEFINE_PROP_END_OF_LIST(), }; static Property secondary_pci_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16), + DEFINE_PROP_BIT("qemu-extended-regs", + PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga.c b/hw/display/vga.c index 19e7f23bd3..52eaf05659 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -136,32 +136,6 @@ static const uint32_t mask16[16] = { #define PAT(x) cbswap_32(x) #endif -static const uint32_t dmask16[16] = { - PAT(0x00000000), - PAT(0x000000ff), - PAT(0x0000ff00), - PAT(0x0000ffff), - PAT(0x00ff0000), - PAT(0x00ff00ff), - PAT(0x00ffff00), - PAT(0x00ffffff), - PAT(0xff000000), - PAT(0xff0000ff), - PAT(0xff00ff00), - PAT(0xff00ffff), - PAT(0xffff0000), - PAT(0xffff00ff), - PAT(0xffffff00), - PAT(0xffffffff), -}; - -static const uint32_t dmask4[4] = { - PAT(0x00000000), - PAT(0x0000ffff), - PAT(0xffff0000), - PAT(0xffffffff), -}; - static uint32_t expand4[256]; static uint16_t expand2[256]; static uint8_t expand4to8[16]; diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index a3133211a8..00be4bb12a 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -774,7 +774,7 @@ static void *acpi_set_bsel(PCIBus *bus, void *opaque) unsigned *bsel_alloc = opaque; unsigned *bus_bsel; - if (bus->qbus.allow_hotplug) { + if (qbus_is_hotpluggable(BUS(bus))) { bus_bsel = g_malloc(sizeof *bus_bsel); *bus_bsel = (*bsel_alloc)++; diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c index 13b9de08f1..bb206da05f 100644 --- a/hw/i386/kvm/pci-assign.c +++ b/hw/i386/kvm/pci-assign.c @@ -1825,8 +1825,6 @@ static int assigned_initfn(struct PCIDevice *pci_dev) assigned_dev_load_option_rom(dev); - add_boot_device_path(dev->bootindex, &pci_dev->qdev, NULL); - return 0; assigned_out: @@ -1850,13 +1848,22 @@ static void assigned_exitfn(struct PCIDevice *pci_dev) free_assigned_device(dev); } +static void assigned_dev_instance_init(Object *obj) +{ + PCIDevice *pci_dev = PCI_DEVICE(obj); + AssignedDevice *d = DO_UPCAST(AssignedDevice, dev, PCI_DEVICE(obj)); + + device_add_bootindex_property(obj, &d->bootindex, + "bootindex", NULL, + &pci_dev->qdev, NULL); +} + static Property assigned_dev_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", AssignedDevice, host), DEFINE_PROP_BIT("prefer_msi", AssignedDevice, features, ASSIGNED_DEVICE_PREFER_MSI_BIT, false), DEFINE_PROP_BIT("share_intx", AssignedDevice, features, ASSIGNED_DEVICE_SHARE_INTX_BIT, true), - DEFINE_PROP_INT32("bootindex", AssignedDevice, bootindex, -1), DEFINE_PROP_STRING("configfd", AssignedDevice, configfd_name), DEFINE_PROP_END_OF_LIST(), }; @@ -1882,6 +1889,7 @@ static const TypeInfo assign_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AssignedDevice), .class_init = assign_class_init, + .instance_init = assigned_dev_instance_init, }; static void assign_register_types(void) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index bb0dc8e08a..e225c6d110 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -254,7 +254,7 @@ static void pc_q35_init(MachineState *machine) true, "ich9-ahci"); idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); - g_assert_cmpint(MAX_SATA_PORTS, ==, ICH_AHCI(ahci)->ahci.ports); + g_assert(MAX_SATA_PORTS == ICH_AHCI(ahci)->ahci.ports); ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); ahci_ide_create_devs(ahci, hd); diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index efab95b320..9814ef0d24 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -23,6 +23,7 @@ #include "sysemu/blockdev.h" #include "hw/block/block.h" #include "sysemu/sysemu.h" +#include "qapi/visitor.h" /* --------------------------------- */ @@ -191,6 +192,51 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) return 0; } +static void ide_dev_get_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + IDEDevice *d = IDE_DEVICE(obj); + + visit_type_int32(v, &d->conf.bootindex, name, errp); +} + +static void ide_dev_set_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + IDEDevice *d = IDE_DEVICE(obj); + int32_t boot_index; + Error *local_err = NULL; + + visit_type_int32(v, &boot_index, name, &local_err); + if (local_err) { + goto out; + } + /* check whether bootindex is present in fw_boot_order list */ + check_boot_index(boot_index, &local_err); + if (local_err) { + goto out; + } + /* change bootindex to a new one */ + d->conf.bootindex = boot_index; + + if (d->unit != -1) { + add_boot_device_path(d->conf.bootindex, &d->qdev, + d->unit ? "/disk@1" : "/disk@0"); + } +out: + if (local_err) { + error_propagate(errp, local_err); + } +} + +static void ide_dev_instance_init(Object *obj) +{ + object_property_add(obj, "bootindex", "int32", + ide_dev_get_bootindex, + ide_dev_set_bootindex, NULL, NULL, NULL); + object_property_set_int(obj, -1, "bootindex", NULL); +} + static int ide_hd_initfn(IDEDevice *dev) { return ide_dev_initfn(dev, IDE_HD); @@ -300,6 +346,7 @@ static const TypeInfo ide_device_type_info = { .abstract = true, .class_size = sizeof(IDEDeviceClass), .class_init = ide_device_class_init, + .instance_init = ide_dev_instance_init, }; static void ide_register_types(void) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 177023bcaf..530b074551 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -607,8 +607,8 @@ static void ich9_device_plug_cb(HotplugHandler *hotplug_dev, ich9_pm_device_plug_cb(&lpc->pm, dev, errp); } -static void ich9_device_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void ich9_device_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { error_setg(errp, "acpi: device unplug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -676,7 +676,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) */ dc->cannot_instantiate_with_device_add_yet = true; hc->plug = ich9_device_plug_cb; - hc->unplug = ich9_device_unplug_cb; + hc->unplug_request = ich9_device_unplug_request_cb; adevc->ospm_status = ich9_pm_ospm_status; } diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index b20807c4e4..e8e075c600 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -697,12 +697,12 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base, /* Jump to kernel code */ stl_p(p++, 0x3c1f0000 | ((kernel_entry >> 16) & 0xffff)); /* lui ra, high(kernel_entry) */ stl_p(p++, 0x37ff0000 | (kernel_entry & 0xffff)); /* ori ra, ra, low(kernel_entry) */ - stl_p(p++, 0x03e00008); /* jr ra */ + stl_p(p++, 0x03e00009); /* jalr ra */ stl_p(p++, 0x00000000); /* nop */ /* YAMON subroutines */ p = (uint32_t *) (base + 0x800); - stl_p(p++, 0x03e00008); /* jr ra */ + stl_p(p++, 0x03e00009); /* jalr ra */ stl_p(p++, 0x24020000); /* li v0,0 */ /* 808 YAMON print */ stl_p(p++, 0x03e06821); /* move t5,ra */ @@ -716,7 +716,7 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base, stl_p(p++, 0x00000000); /* nop */ stl_p(p++, 0x08000205); /* j 814 */ stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x01a00008); /* jr t5 */ + stl_p(p++, 0x01a00009); /* jalr t5 */ stl_p(p++, 0x01602021); /* move a0,t3 */ /* 0x83c YAMON print_count */ stl_p(p++, 0x03e06821); /* move t5,ra */ @@ -730,7 +730,7 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base, stl_p(p++, 0x258cffff); /* addiu t4,t4,-1 */ stl_p(p++, 0x1580fffa); /* bnez t4,84c */ stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x01a00008); /* jr t5 */ + stl_p(p++, 0x01a00009); /* jalr t5 */ stl_p(p++, 0x01602021); /* move a0,t3 */ /* 0x870 */ stl_p(p++, 0x3c08b800); /* lui t0,0xb400 */ @@ -740,7 +740,7 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base, stl_p(p++, 0x31290040); /* andi t1,t1,0x40 */ stl_p(p++, 0x1120fffc); /* beqz t1,878 <outch+0x8> */ stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x03e00008); /* jr ra */ + stl_p(p++, 0x03e00009); /* jalr ra */ stl_p(p++, 0xa1040000); /* sb a0,0(t0) */ } diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c index d66f3d2425..b5e798173b 100644 --- a/hw/misc/vfio.c +++ b/hw/misc/vfio.c @@ -4296,7 +4296,6 @@ static int vfio_initfn(PCIDevice *pdev) } } - add_boot_device_path(vdev->bootindex, &pdev->qdev, NULL); vfio_register_err_notifier(vdev); return 0; @@ -4365,13 +4364,22 @@ post_reset: vfio_pci_post_reset(vdev); } +static void vfio_instance_init(Object *obj) +{ + PCIDevice *pci_dev = PCI_DEVICE(obj); + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, PCI_DEVICE(obj)); + + device_add_bootindex_property(obj, &vdev->bootindex, + "bootindex", NULL, + &pci_dev->qdev, NULL); +} + static Property vfio_pci_dev_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIODevice, host), DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIODevice, intx.mmap_timeout, 1100), DEFINE_PROP_BIT("x-vga", VFIODevice, features, VFIO_FEATURE_ENABLE_VGA_BIT, false), - DEFINE_PROP_INT32("bootindex", VFIODevice, bootindex, -1), /* * TODO - support passed fds... is this necessary? * DEFINE_PROP_STRING("vfiofd", VFIODevice, vfiofd_name), @@ -4407,6 +4415,7 @@ static const TypeInfo vfio_pci_dev_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VFIODevice), .class_init = vfio_pci_dev_class_init, + .instance_init = vfio_instance_init, }; static void register_vfio_pci_dev_type(void) diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 272df00f4a..e33a4da9fa 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1569,8 +1569,6 @@ static int pci_e1000_init(PCIDevice *pci_dev) qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr); - add_boot_device_path(d->conf.bootindex, dev, "/ethernet-phy@0"); - d->autoneg_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, e1000_autoneg_timer, d); d->mit_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000_mit_timer, d); @@ -1621,10 +1619,19 @@ static void e1000_class_init(ObjectClass *klass, void *data) dc->props = e1000_properties; } +static void e1000_instance_init(Object *obj) +{ + E1000State *n = E1000(obj); + device_add_bootindex_property(obj, &n->conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(n), NULL); +} + static const TypeInfo e1000_base_info = { .name = TYPE_E1000_BASE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(E1000State), + .instance_init = e1000_instance_init, .class_size = sizeof(E1000BaseClass), .abstract = true, }; @@ -1668,6 +1675,7 @@ static void e1000_register_types(void) type_info.parent = TYPE_E1000_BASE; type_info.class_data = (void *)info; type_info.class_init = e1000_class_init; + type_info.instance_init = e1000_instance_init; type_register(&type_info); } diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 3cd826accc..4877bfd4d3 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -1901,11 +1901,17 @@ static int e100_nic_init(PCIDevice *pci_dev) s->vmstate->name = qemu_get_queue(s->nic)->model; vmstate_register(&pci_dev->qdev, -1, s->vmstate, s); - add_boot_device_path(s->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); - return 0; } +static void eepro100_instance_init(Object *obj) +{ + EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, PCI_DEVICE(obj)); + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(s), NULL); +} + static E100PCIDeviceInfo e100_devices[] = { { .name = "i82550", @@ -2104,7 +2110,8 @@ static void eepro100_register_types(void) type_info.parent = TYPE_PCI_DEVICE; type_info.class_init = eepro100_class_init; type_info.instance_size = sizeof(EEPRO100State); - + type_info.instance_init = eepro100_instance_init; + type_register(&type_info); } } diff --git a/hw/net/lance.c b/hw/net/lance.c index 7811a9edc2..a1c49f1b97 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -42,6 +42,7 @@ #include "hw/sparc/sun4m.h" #include "pcnet.h" #include "trace.h" +#include "sysemu/sysemu.h" #define TYPE_LANCE "lance" #define SYSBUS_PCNET(obj) \ @@ -143,6 +144,16 @@ static void lance_reset(DeviceState *dev) pcnet_h_reset(&d->state); } +static void lance_instance_init(Object *obj) +{ + SysBusPCNetState *d = SYSBUS_PCNET(obj); + PCNetState *s = &d->state; + + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(obj), NULL); +} + static Property lance_properties[] = { DEFINE_PROP_PTR("dma", SysBusPCNetState, state.dma_opaque), DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf), @@ -169,6 +180,7 @@ static const TypeInfo lance_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusPCNetState), .class_init = lance_class_init, + .instance_init = lance_instance_init, }; static void lance_register_types(void) diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 6eb1dac8dc..82e2ba17c1 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -28,6 +28,7 @@ #include "net/net.h" #include "ne2000.h" #include "exec/address-spaces.h" +#include "qapi/visitor.h" #define TYPE_ISA_NE2000 "ne2k_isa" #define ISA_NE2000(obj) OBJECT_CHECK(ISANE2000State, (obj), TYPE_ISA_NE2000) @@ -101,11 +102,54 @@ static void isa_ne2000_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } +static void isa_ne2000_get_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + ISANE2000State *isa = ISA_NE2000(obj); + NE2000State *s = &isa->ne2000; + + visit_type_int32(v, &s->c.bootindex, name, errp); +} + +static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + ISANE2000State *isa = ISA_NE2000(obj); + NE2000State *s = &isa->ne2000; + int32_t boot_index; + Error *local_err = NULL; + + visit_type_int32(v, &boot_index, name, &local_err); + if (local_err) { + goto out; + } + /* check whether bootindex is present in fw_boot_order list */ + check_boot_index(boot_index, &local_err); + if (local_err) { + goto out; + } + /* change bootindex to a new one */ + s->c.bootindex = boot_index; + +out: + if (local_err) { + error_propagate(errp, local_err); + } +} + +static void isa_ne2000_instance_init(Object *obj) +{ + object_property_add(obj, "bootindex", "int32", + isa_ne2000_get_bootindex, + isa_ne2000_set_bootindex, NULL, NULL, NULL); + object_property_set_int(obj, -1, "bootindex", NULL); +} static const TypeInfo ne2000_isa_info = { .name = TYPE_ISA_NE2000, .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISANE2000State), .class_init = isa_ne2000_class_initfn, + .instance_init = isa_ne2000_instance_init, }; static void ne2000_isa_register_types(void) diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index a62d12d92d..3ab2d03696 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -738,8 +738,6 @@ static int pci_ne2000_init(PCIDevice *pci_dev) object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); - add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); - return 0; } @@ -752,6 +750,17 @@ static void pci_ne2000_exit(PCIDevice *pci_dev) qemu_free_irq(s->irq); } +static void ne2000_instance_init(Object *obj) +{ + PCIDevice *pci_dev = PCI_DEVICE(obj); + PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); + NE2000State *s = &d->ne2000; + + device_add_bootindex_property(obj, &s->c.bootindex, + "bootindex", "/ethernet-phy@0", + &pci_dev->qdev, NULL); +} + static Property ne2000_properties[] = { DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c), DEFINE_PROP_END_OF_LIST(), @@ -778,6 +787,7 @@ static const TypeInfo ne2000_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCINE2000State), .class_init = ne2000_class_init, + .instance_init = ne2000_instance_init, }; static void ne2000_register_types(void) diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 50ffe914e0..fb5f5d6237 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -32,6 +32,7 @@ #include "hw/loader.h" #include "qemu/timer.h" #include "sysemu/dma.h" +#include "sysemu/sysemu.h" #include "pcnet.h" @@ -344,6 +345,16 @@ static void pci_reset(DeviceState *dev) pcnet_h_reset(&d->state); } +static void pcnet_instance_init(Object *obj) +{ + PCIPCNetState *d = PCI_PCNET(obj); + PCNetState *s = &d->state; + + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(obj), NULL); +} + static Property pcnet_properties[] = { DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), DEFINE_PROP_END_OF_LIST(), @@ -372,6 +383,7 @@ static const TypeInfo pcnet_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIPCNetState), .class_init = pcnet_class_init, + .instance_init = pcnet_instance_init, }; static void pci_pcnet_register_types(void) diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index 5299d52a8f..d344c15da0 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -1735,8 +1735,6 @@ int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0"); - /* Initialize the PROM */ /* diff --git a/hw/net/pcnet.h b/hw/net/pcnet.h index 9dee6f3e2c..f8e8a6f6ba 100644 --- a/hw/net/pcnet.h +++ b/hw/net/pcnet.h @@ -66,5 +66,4 @@ void pcnet_set_link_status(NetClientState *nc); void pcnet_common_cleanup(PCNetState *d); int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info); extern const VMStateDescription vmstate_pcnet; - #endif diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 6e59f3819b..8b8a1b18af 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -3538,11 +3538,18 @@ static int pci_rtl8139_init(PCIDevice *dev) s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rtl8139_timer, s); rtl8139_set_next_tctr_time(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - add_boot_device_path(s->conf.bootindex, d, "/ethernet-phy@0"); - return 0; } +static void rtl8139_instance_init(Object *obj) +{ + RTL8139State *s = RTL8139(obj); + + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(obj), NULL); +} + static Property rtl8139_properties[] = { DEFINE_NIC_PROPERTIES(RTL8139State, conf), DEFINE_PROP_END_OF_LIST(), @@ -3571,6 +3578,7 @@ static const TypeInfo rtl8139_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(RTL8139State), .class_init = rtl8139_class_init, + .instance_init = rtl8139_instance_init, }; static void rtl8139_register_types(void) diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 23c47d397c..2c8b038227 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -221,11 +221,18 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev) object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev); qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); - add_boot_device_path(dev->nicconf.bootindex, DEVICE(dev), ""); - return 0; } +static void spapr_vlan_instance_init(Object *obj) +{ + VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); + + device_add_bootindex_property(obj, &dev->nicconf.bootindex, + "bootindex", "", + DEVICE(dev), NULL); +} + void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd) { DeviceState *dev; @@ -553,6 +560,7 @@ static const TypeInfo spapr_vlan_info = { .parent = TYPE_VIO_SPAPR_DEVICE, .instance_size = sizeof(VIOsPAPRVLANDevice), .class_init = spapr_vlan_class_init, + .instance_init = spapr_vlan_instance_init, }; static void spapr_vlan_register_types(void) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 2040eac9a1..9b88775fac 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1661,8 +1661,6 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->qdev = dev; register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, virtio_net_save, virtio_net_load, n); - - add_boot_device_path(n->nic_conf.bootindex, dev, "/ethernet-phy@0"); } static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) @@ -1714,6 +1712,9 @@ static void virtio_net_instance_init(Object *obj) * Can be overriden with virtio_net_set_config_size. */ n->config_size = sizeof(struct virtio_net_config); + device_add_bootindex_property(obj, &n->nic_conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(n), NULL); } static Property virtio_net_properties[] = { diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index f246fa1c45..8eea58989b 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2172,11 +2172,16 @@ static int vmxnet3_pci_init(PCIDevice *pci_dev) register_savevm(dev, "vmxnet3-msix", -1, 1, vmxnet3_msix_save, vmxnet3_msix_load, s); - add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0"); - return 0; } +static void vmxnet3_instance_init(Object *obj) +{ + VMXNET3State *s = VMXNET3(obj); + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(obj), NULL); +} static void vmxnet3_pci_uninit(PCIDevice *pci_dev) { @@ -2524,6 +2529,7 @@ static const TypeInfo vmxnet3_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VMXNET3State), .class_init = vmxnet3_class_init, + .instance_init = vmxnet3_instance_init, }; static void vmxnet3_register_types(void) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index b71d251568..e7ed27e242 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -402,6 +402,26 @@ static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key, s->entries[arch][key].callback_opaque = callback_opaque; } +static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key, + void *data, size_t len) +{ + void *ptr; + int arch = !!(key & FW_CFG_ARCH_LOCAL); + + key &= FW_CFG_ENTRY_MASK; + + assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX); + + /* return the old data to the function caller, avoid memory leak */ + ptr = s->entries[arch][key].data; + s->entries[arch][key].data = data; + s->entries[arch][key].len = len; + s->entries[arch][key].callback_opaque = NULL; + s->entries[arch][key].callback = NULL; + + return ptr; +} + void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) { fw_cfg_add_bytes_read_callback(s, key, NULL, NULL, data, len); @@ -499,13 +519,42 @@ void fw_cfg_add_file(FWCfgState *s, const char *filename, fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len); } -static void fw_cfg_machine_ready(struct Notifier *n, void *data) +void *fw_cfg_modify_file(FWCfgState *s, const char *filename, + void *data, size_t len) +{ + int i, index; + + assert(s->files); + + index = be32_to_cpu(s->files->count); + assert(index < FW_CFG_FILE_SLOTS); + + for (i = 0; i < index; i++) { + if (strcmp(filename, s->files->f[i].name) == 0) { + return fw_cfg_modify_bytes_read(s, FW_CFG_FILE_FIRST + i, + data, len); + } + } + /* add new one */ + fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len); + return NULL; +} + +static void fw_cfg_machine_reset(void *opaque) { + void *ptr; size_t len; - FWCfgState *s = container_of(n, FWCfgState, machine_ready); + FWCfgState *s = opaque; char *bootindex = get_boot_devices_list(&len, false); - fw_cfg_add_file(s, "bootorder", (uint8_t*)bootindex, len); + ptr = fw_cfg_modify_file(s, "bootorder", (uint8_t *)bootindex, len); + g_free(ptr); +} + +static void fw_cfg_machine_ready(struct Notifier *n, void *data) +{ + FWCfgState *s = container_of(n, FWCfgState, machine_ready); + qemu_register_reset(fw_cfg_machine_reset, s); } FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 92799d08ef..252ea5eb53 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -150,7 +150,7 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) dc->vmsd = &pci_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); hc->plug = shpc_device_hotplug_cb; - hc->unplug = shpc_device_hot_unplug_cb; + hc->unplug_request = shpc_device_hot_unplug_request_cb; } static const TypeInfo pci_bridge_dev_info = { diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c index d87c469096..6ab28b703c 100644 --- a/hw/pci/pci-hotplug-old.c +++ b/hw/pci/pci-hotplug-old.c @@ -77,7 +77,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); return NULL; } - if (!((BusState*)bus)->allow_hotplug) { + if (!qbus_is_hotpluggable(BUS(bus))) { monitor_printf(mon, "PCI bus doesn't support hotplug\n"); return NULL; } @@ -227,7 +227,7 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); return NULL; } - if (!((BusState*)bus)->allow_hotplug) { + if (!qbus_is_hotpluggable(BUS(bus))) { monitor_printf(mon, "PCI bus doesn't support hotplug\n"); return NULL; } diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 1babddff4d..b64a004631 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -262,8 +262,8 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP); } -void pcie_cap_slot_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { uint8_t *exp_cap; diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index fa24877955..40ca8d5d17 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -154,7 +154,7 @@ static void pcie_slot_class_init(ObjectClass *oc, void *data) dc->props = pcie_slot_props; hc->plug = pcie_cap_slot_hotplug_cb; - hc->unplug = pcie_cap_slot_hot_unplug_cb; + hc->unplug_request = pcie_cap_slot_hot_unplug_request_cb; } static const TypeInfo pcie_slot_type_info = { diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index 1fcb8c4d85..65b2f5103f 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -549,8 +549,8 @@ void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, shpc_interrupt_update(pci_hotplug_dev); } -void shpc_device_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { Error *local_err = NULL; PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 597db34019..78da718362 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -333,7 +333,6 @@ static int init_event_facility(SCLPEventFacility *event_facility) /* Spawn a new bus for SCLP events */ qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus), TYPE_SCLP_EVENTS_BUS, sdev, NULL); - event_facility->sbus.qbus.allow_hotplug = 0; quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce"); if (!quiesce) { @@ -408,7 +407,6 @@ static void event_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->bus_type = TYPE_SCLP_EVENTS_BUS; - dc->unplug = qdev_simple_unplug_cb; dc->realize = event_realize; dc->unrealize = event_unrealize; } diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c index f451ca1ed3..29ecdb4194 100644 --- a/hw/s390x/s390-virtio-bus.c +++ b/hw/s390x/s390-virtio-bus.c @@ -102,7 +102,7 @@ VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) bus->next_ring = bus->dev_page + TARGET_PAGE_SIZE; /* Enable hotplugging */ - _bus->allow_hotplug = 1; + qbus_set_hotplug_handler(_bus, dev, &error_abort); /* Allocate RAM for VirtIO device pages (descriptors, queues, rings) */ *ram_size += S390_DEVICE_PAGES * TARGET_PAGE_SIZE; @@ -162,6 +162,8 @@ static void s390_virtio_net_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_NET); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); } static int s390_virtio_blk_init(VirtIOS390Device *s390_dev) @@ -183,6 +185,8 @@ static void s390_virtio_blk_instance_init(Object *obj) TYPE_VIRTIO_BLK); object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", &error_abort); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); } static int s390_virtio_serial_init(VirtIOS390Device *s390_dev) @@ -600,7 +604,6 @@ static void virtio_s390_device_class_init(ObjectClass *klass, void *data) dc->init = s390_virtio_busdev_init; dc->bus_type = TYPE_S390_VIRTIO_BUS; - dc->unplug = qdev_simple_unplug_cb; dc->reset = s390_virtio_busdev_reset; } @@ -681,6 +684,10 @@ static const TypeInfo s390_virtio_bridge_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusDevice), .class_init = s390_virtio_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; /* virtio-s390-bus */ @@ -689,13 +696,10 @@ static void virtio_s390_bus_new(VirtioBusState *bus, size_t bus_size, VirtIOS390Device *dev) { DeviceState *qdev = DEVICE(dev); - BusState *qbus; char virtio_bus_name[] = "virtio-bus"; qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_S390_BUS, qdev, virtio_bus_name); - qbus = BUS(bus); - qbus->allow_hotplug = 1; } static void virtio_s390_bus_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 18ba29fa14..f5ec94657f 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -230,7 +230,7 @@ VirtualCssBus *virtual_css_bus_init(void) cbus = VIRTUAL_CSS_BUS(bus); /* Enable hotplugging */ - bus->allow_hotplug = 1; + qbus_set_hotplug_handler(bus, dev, &error_abort); return cbus; } @@ -795,6 +795,8 @@ static void virtio_ccw_net_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_NET); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); } static int virtio_ccw_blk_init(VirtioCcwDevice *ccw_dev) @@ -817,6 +819,8 @@ static void virtio_ccw_blk_instance_init(Object *obj) TYPE_VIRTIO_BLK); object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", &error_abort); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); } static int virtio_ccw_serial_init(VirtioCcwDevice *ccw_dev) @@ -1590,7 +1594,8 @@ static int virtio_ccw_busdev_exit(DeviceState *dev) return _info->exit(_dev); } -static int virtio_ccw_busdev_unplug(DeviceState *dev) +static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; SubchDev *sch = _dev->sch; @@ -1609,7 +1614,6 @@ static int virtio_ccw_busdev_unplug(DeviceState *dev) css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); object_unparent(OBJECT(dev)); - return 0; } static Property virtio_ccw_properties[] = { @@ -1624,9 +1628,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) dc->props = virtio_ccw_properties; dc->init = virtio_ccw_busdev_init; dc->exit = virtio_ccw_busdev_exit; - dc->unplug = virtio_ccw_busdev_unplug; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; - } static const TypeInfo virtio_ccw_device_info = { @@ -1650,8 +1652,10 @@ static int virtual_css_bridge_init(SysBusDevice *dev) static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); k->init = virtual_css_bridge_init; + hc->unplug = virtio_ccw_busdev_unplug; } static const TypeInfo virtual_css_bridge_info = { @@ -1659,6 +1663,10 @@ static const TypeInfo virtual_css_bridge_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusDevice), .class_init = virtual_css_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; /* virtio-ccw-bus */ @@ -1667,13 +1675,10 @@ static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, VirtioCcwDevice *dev) { DeviceState *qdev = DEVICE(dev); - BusState *qbus; char virtio_bus_name[] = "virtio-bus"; qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_CCW_BUS, qdev, virtio_bus_name); - qbus = BUS(bus); - qbus->allow_hotplug = 1; } static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 0f3e0395f5..022a524074 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -23,9 +23,11 @@ static Property scsi_props[] = { static void scsi_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); k->get_dev_path = scsibus_get_dev_path; k->get_fw_dev_path = scsibus_get_fw_dev_path; + hc->unplug = qdev_simple_device_unplug_cb; } static const TypeInfo scsi_bus_info = { @@ -33,6 +35,10 @@ static const TypeInfo scsi_bus_info = { .parent = TYPE_BUS, .instance_size = sizeof(SCSIBus), .class_init = scsi_bus_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static int next_scsi_bus; @@ -92,7 +98,7 @@ void scsi_bus_new(SCSIBus *bus, size_t bus_size, DeviceState *host, qbus_create_inplace(bus, bus_size, TYPE_SCSI_BUS, host, bus_name); bus->busnr = next_scsi_bus++; bus->info = info; - bus->qbus.allow_hotplug = 1; + qbus_set_bus_hotplug_handler(BUS(bus), &error_abort); } static void scsi_dma_restart_bh(void *opaque) @@ -202,10 +208,6 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp) } dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb, dev); - - if (bus->info->hotplug) { - bus->info->hotplug(bus, dev); - } } static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp) @@ -231,7 +233,8 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, dev = qdev_create(&bus->qbus, driver); qdev_prop_set_uint32(dev, "scsi-id", unit); if (bootindex >= 0) { - qdev_prop_set_int32(dev, "bootindex", bootindex); + object_property_set_int(OBJECT(dev), bootindex, "bootindex", + &error_abort); } if (object_property_find(OBJECT(dev), "removable", NULL)) { qdev_prop_set_bit(dev, "removable", removable); @@ -1937,17 +1940,6 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) return 0; } -static int scsi_qdev_unplug(DeviceState *qdev) -{ - SCSIDevice *dev = SCSI_DEVICE(qdev); - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); - - if (bus->info->hot_unplug) { - bus->info->hot_unplug(bus, dev); - } - return qdev_simple_unplug_cb(qdev); -} - static const VMStateInfo vmstate_info_scsi_requests = { .name = "scsi-requests", .get = get_scsi_requests, @@ -2011,11 +2003,20 @@ static void scsi_device_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, k->categories); k->bus_type = TYPE_SCSI_BUS; k->realize = scsi_qdev_realize; - k->unplug = scsi_qdev_unplug; k->unrealize = scsi_qdev_unrealize; k->props = scsi_props; } +static void scsi_dev_instance_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + SCSIDevice *s = DO_UPCAST(SCSIDevice, qdev, dev); + + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", NULL, + &s->qdev, NULL); +} + static const TypeInfo scsi_device_type_info = { .name = TYPE_SCSI_DEVICE, .parent = TYPE_DEVICE, @@ -2023,6 +2024,7 @@ static const TypeInfo scsi_device_type_info = { .abstract = true, .class_size = sizeof(SCSIDeviceClass), .class_init = scsi_device_class_init, + .instance_init = scsi_dev_instance_init, }; static void scsi_register_types(void) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 7a7938a5bf..ae9e08dd13 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2269,7 +2269,6 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) bdrv_set_guest_block_size(s->qdev.conf.bs, s->qdev.blocksize); bdrv_iostatus_enable(s->qdev.conf.bs); - add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, NULL); } static void scsi_hd_realize(SCSIDevice *dev, Error **errp) @@ -2662,7 +2661,6 @@ static const TypeInfo scsi_cd_info = { #ifdef __linux__ static Property scsi_block_properties[] = { DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.bs), - DEFINE_PROP_INT32("bootindex", SCSIDiskState, qdev.conf.bootindex, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 01bca084e6..84a1d5bfe3 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -413,9 +413,6 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) /* define device state */ s->type = scsiid.scsi_type; DPRINTF("device type %d\n", s->type); - if (s->type == TYPE_DISK || s->type == TYPE_ROM) { - add_boot_device_path(s->conf.bootindex, &s->qdev, NULL); - } switch (s->type) { case TYPE_TAPE: @@ -463,7 +460,6 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, static Property scsi_generic_properties[] = { DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.bs), - DEFINE_PROP_INT32("bootindex", SCSIDevice, conf.bootindex, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 6c02fe2b9a..8547ea0475 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -737,26 +737,29 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) } } -static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev) +static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) { - VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); - VirtIODevice *vdev = VIRTIO_DEVICE(s); + VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { - virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET, + virtio_scsi_push_event(VIRTIO_SCSI(hotplug_dev), SCSI_DEVICE(dev), + VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN); } } -static void virtio_scsi_hot_unplug(SCSIBus *bus, SCSIDevice *dev) +static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) { - VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); - VirtIODevice *vdev = VIRTIO_DEVICE(s); + VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { - virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET, + virtio_scsi_push_event(VIRTIO_SCSI(hotplug_dev), SCSI_DEVICE(dev), + VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_REMOVED); } + qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); } static struct SCSIBusInfo virtio_scsi_scsi_info = { @@ -768,8 +771,6 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = { .complete = virtio_scsi_command_complete, .cancel = virtio_scsi_request_cancelled, .change = virtio_scsi_change, - .hotplug = virtio_scsi_hotplug, - .hot_unplug = virtio_scsi_hot_unplug, .parse_cdb = virtio_scsi_parse_cdb, .get_sg_list = virtio_scsi_get_sg_list, .save_request = virtio_scsi_save_request, @@ -853,6 +854,8 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) scsi_bus_new(&s->bus, sizeof(s->bus), dev, &virtio_scsi_scsi_info, vdev->bus_name); + /* override default SCSI bus hotplug-handler, with virtio-scsi's one */ + qbus_set_hotplug_handler(BUS(&s->bus), dev, &error_abort); if (!dev->hotplugged) { scsi_bus_legacy_handle_cmdline(&s->bus, &err); @@ -915,6 +918,7 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); dc->props = virtio_scsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); @@ -923,6 +927,8 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) vdc->set_config = virtio_scsi_set_config; vdc->get_features = virtio_scsi_get_features; vdc->reset = virtio_scsi_reset; + hc->plug = virtio_scsi_hotplug; + hc->unplug = virtio_scsi_hotunplug; } static const TypeInfo virtio_scsi_common_info = { @@ -939,6 +945,10 @@ static const TypeInfo virtio_scsi_info = { .instance_size = sizeof(VirtIOSCSI), .instance_init = virtio_scsi_instance_init, .class_init = virtio_scsi_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static void virtio_register_types(void) diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 5734d19789..d3a92fbabf 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -524,17 +524,20 @@ pvscsi_send_msg(PVSCSIState *s, SCSIDevice *dev, uint32_t msg_type) } static void -pvscsi_hotplug(SCSIBus *bus, SCSIDevice *dev) +pvscsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - PVSCSIState *s = container_of(bus, PVSCSIState, bus); - pvscsi_send_msg(s, dev, PVSCSI_MSG_DEV_ADDED); + PVSCSIState *s = PVSCSI(hotplug_dev); + + pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_ADDED); } static void -pvscsi_hot_unplug(SCSIBus *bus, SCSIDevice *dev) +pvscsi_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - PVSCSIState *s = container_of(bus, PVSCSIState, bus); - pvscsi_send_msg(s, dev, PVSCSI_MSG_DEV_REMOVED); + PVSCSIState *s = PVSCSI(hotplug_dev); + + pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_REMOVED); + qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); } static void @@ -1057,8 +1060,6 @@ static const struct SCSIBusInfo pvscsi_scsi_info = { .get_sg_list = pvscsi_get_sg_list, .complete = pvscsi_command_complete, .cancel = pvscsi_request_cancelled, - .hotplug = pvscsi_hotplug, - .hot_unplug = pvscsi_hot_unplug, }; static int @@ -1092,6 +1093,8 @@ pvscsi_init(PCIDevice *pci_dev) scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(pci_dev), &pvscsi_scsi_info, NULL); + /* override default SCSI bus hotplug-handler, with pvscsi's one */ + qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(s), &error_abort); pvscsi_reset_state(s); return 0; @@ -1187,6 +1190,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); k->init = pvscsi_init; k->exit = pvscsi_uninit; @@ -1199,6 +1203,8 @@ static void pvscsi_class_init(ObjectClass *klass, void *data) dc->props = pvscsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); k->config_write = pvscsi_write_config; + hc->unplug = pvscsi_hot_unplug; + hc->plug = pvscsi_hotplug; } static const TypeInfo pvscsi_info = { @@ -1206,6 +1212,10 @@ static const TypeInfo pvscsi_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PVSCSIState), .class_init = pvscsi_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static void diff --git a/hw/usb/bus.c b/hw/usb/bus.c index da1eba9fbd..986b2d8da8 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -24,10 +24,12 @@ static Property usb_props[] = { static void usb_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); k->print_dev = usb_bus_dev_print; k->get_dev_path = usb_get_dev_path; k->get_fw_dev_path = usb_get_fw_dev_path; + hc->unplug = qdev_simple_device_unplug_cb; } static const TypeInfo usb_bus_info = { @@ -35,6 +37,10 @@ static const TypeInfo usb_bus_info = { .parent = TYPE_BUS, .instance_size = sizeof(USBBus), .class_init = usb_bus_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static int next_usb_bus = 0; @@ -79,9 +85,9 @@ void usb_bus_new(USBBus *bus, size_t bus_size, USBBusOps *ops, DeviceState *host) { qbus_create_inplace(bus, bus_size, TYPE_USB_BUS, host, NULL); + qbus_set_bus_hotplug_handler(BUS(bus), &error_abort); bus->ops = ops; bus->busnr = next_usb_bus++; - bus->qbus.allow_hotplug = 1; /* Yes, we can */ QTAILQ_INIT(&bus->free); QTAILQ_INIT(&bus->used); QTAILQ_INSERT_TAIL(&busses, bus, next); @@ -701,7 +707,6 @@ static void usb_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->bus_type = TYPE_USB_BUS; - k->unplug = qdev_simple_unplug_cb; k->realize = usb_qdev_realize; k->unrealize = usb_qdev_unrealize; k->props = usb_props; diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 23e3c45b5f..5b95d5c382 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1371,8 +1371,16 @@ static void usb_net_realize(USBDevice *dev, Error **errrp) s->conf.macaddr.a[4], s->conf.macaddr.a[5]); usb_desc_set_string(dev, STRING_ETHADDR, s->usbstring_mac); +} + +static void usb_net_instance_init(Object *obj) +{ + USBDevice *dev = USB_DEVICE(obj); + USBNetState *s = DO_UPCAST(USBNetState, dev, dev); - add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet@0"); + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/ethernet-phy@0", + &dev->qdev, NULL); } static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) @@ -1438,6 +1446,7 @@ static const TypeInfo net_info = { .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBNetState), .class_init = usb_net_class_initfn, + .instance_init = usb_net_instance_init, }; static void usb_net_register_types(void) diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index d37ed02d2e..78ce681671 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1312,8 +1312,8 @@ static void ccid_realize(USBDevice *dev, Error **errp) usb_desc_init(dev); qbus_create_inplace(&s->bus, sizeof(s->bus), TYPE_CCID_BUS, DEVICE(dev), NULL); + qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(dev), &error_abort); s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP); - s->bus.qbus.allow_hotplug = 1; s->card = NULL; s->migration_state = MIGRATION_NONE; s->migration_target_ip = 0; @@ -1439,6 +1439,7 @@ static void ccid_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); uc->realize = ccid_realize; uc->product_desc = "QEMU USB CCID"; @@ -1451,6 +1452,7 @@ static void ccid_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &ccid_vmstate; dc->props = ccid_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + hc->unplug = qdev_simple_device_unplug_cb; } static const TypeInfo ccid_info = { @@ -1458,6 +1460,10 @@ static const TypeInfo ccid_info = { .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBCCIDState), .class_init = ccid_class_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static void ccid_card_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index bd7cc53e07..5bfc72ca45 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -17,6 +17,7 @@ #include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "sysemu/blockdev.h" +#include "qapi/visitor.h" //#define DEBUG_MSD @@ -59,6 +60,7 @@ typedef struct { /* usb-storage only */ BlockConf conf; uint32_t removable; + SCSIDevice *scsi_dev; } MSDState; struct usb_msd_cbw { @@ -632,8 +634,8 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) error_propagate(errp, err); return; } - s->bus.qbus.allow_hotplug = 0; usb_msd_handle_reset(dev); + s->scsi_dev = scsi_dev; if (bdrv_key_required(bs)) { if (cur_mon) { @@ -653,7 +655,6 @@ static void usb_msd_realize_bot(USBDevice *dev, Error **errp) usb_desc_init(dev); scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), &usb_msd_scsi_info_bot, NULL); - s->bus.qbus.allow_hotplug = 0; usb_msd_handle_reset(dev); } @@ -767,12 +768,62 @@ static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data) usb_msd_class_initfn_common(klass); } +static void usb_msd_get_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + USBDevice *dev = USB_DEVICE(obj); + MSDState *s = DO_UPCAST(MSDState, dev, dev); + + visit_type_int32(v, &s->conf.bootindex, name, errp); +} + +static void usb_msd_set_bootindex(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + USBDevice *dev = USB_DEVICE(obj); + MSDState *s = DO_UPCAST(MSDState, dev, dev); + int32_t boot_index; + Error *local_err = NULL; + + visit_type_int32(v, &boot_index, name, &local_err); + if (local_err) { + goto out; + } + /* check whether bootindex is present in fw_boot_order list */ + check_boot_index(boot_index, &local_err); + if (local_err) { + goto out; + } + /* change bootindex to a new one */ + s->conf.bootindex = boot_index; + + if (s->scsi_dev) { + object_property_set_int(OBJECT(s->scsi_dev), boot_index, "bootindex", + &error_abort); + } + +out: + if (local_err) { + error_propagate(errp, local_err); + } +} + +static void usb_msd_instance_init(Object *obj) +{ + object_property_add(obj, "bootindex", "int32", + usb_msd_get_bootindex, + usb_msd_set_bootindex, NULL, NULL, NULL); + object_property_set_int(obj, -1, "bootindex", NULL); +} + static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); uc->realize = usb_msd_realize_bot; usb_msd_class_initfn_common(klass); + dc->hotpluggable = false; } static const TypeInfo msd_info = { @@ -780,6 +831,7 @@ static const TypeInfo msd_info = { .parent = TYPE_USB_DEVICE, .instance_size = sizeof(MSDState), .class_init = usb_msd_class_initfn_storage, + .instance_init = usb_msd_instance_init, }; static const TypeInfo bot_info = { diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 45b74e5307..d2d161bc6e 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -978,10 +978,19 @@ static void usb_host_realize(USBDevice *udev, Error **errp) qemu_add_exit_notifier(&s->exit); QTAILQ_INSERT_TAIL(&hostdevs, s, next); - add_boot_device_path(s->bootindex, &udev->qdev, NULL); usb_host_auto_check(NULL); } +static void usb_host_instance_init(Object *obj) +{ + USBDevice *udev = USB_DEVICE(obj); + USBHostDevice *s = USB_HOST_DEVICE(udev); + + device_add_bootindex_property(obj, &s->bootindex, + "bootindex", NULL, + &udev->qdev, NULL); +} + static void usb_host_handle_destroy(USBDevice *udev) { USBHostDevice *s = USB_HOST_DEVICE(udev); @@ -1465,7 +1474,6 @@ static Property usb_host_dev_properties[] = { DEFINE_PROP_UINT32("productid", USBHostDevice, match.product_id, 0), DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), DEFINE_PROP_UINT32("isobsize", USBHostDevice, iso_urb_frames, 32), - DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex, -1), DEFINE_PROP_UINT32("loglevel", USBHostDevice, loglevel, LIBUSB_LOG_LEVEL_WARNING), DEFINE_PROP_BIT("pipeline", USBHostDevice, options, @@ -1498,6 +1506,7 @@ static TypeInfo usb_host_dev_info = { .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBHostDevice), .class_init = usb_host_class_initfn, + .instance_init = usb_host_instance_init, }; static void usb_host_register_types(void) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index e2c98962a2..9fbd59e5ee 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1401,7 +1401,6 @@ static void usbredir_realize(USBDevice *udev, Error **errp) usbredir_chardev_read, usbredir_chardev_event, dev); qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev); - add_boot_device_path(dev->bootindex, &udev->qdev, NULL); } static void usbredir_cleanup_device_queues(USBRedirDevice *dev) @@ -2471,7 +2470,6 @@ static Property usbredir_properties[] = { DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning), DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), - DEFINE_PROP_INT32("bootindex", USBRedirDevice, bootindex, -1), DEFINE_PROP_END_OF_LIST(), }; @@ -2496,11 +2494,22 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); } +static void usbredir_instance_init(Object *obj) +{ + USBDevice *udev = USB_DEVICE(obj); + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + device_add_bootindex_property(obj, &dev->bootindex, + "bootindex", NULL, + &udev->qdev, NULL); +} + static const TypeInfo usbredir_dev_info = { .name = "usb-redir", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBRedirDevice), .class_init = usbredir_class_initfn, + .instance_init = usbredir_instance_init, }; static void usbredir_register_types(void) diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 18c6e5b55c..2450c13747 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -89,9 +89,6 @@ typedef struct { VirtioBusState bus; } VirtIOMMIOProxy; -static void virtio_mmio_bus_new(VirtioBusState *bus, size_t bus_size, - VirtIOMMIOProxy *dev); - static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) { VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; @@ -362,7 +359,8 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp) VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); SysBusDevice *sbd = SYS_BUS_DEVICE(d); - virtio_mmio_bus_new(&proxy->bus, sizeof(proxy->bus), proxy); + qbus_create_inplace(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, + d, NULL); sysbus_init_irq(sbd, &proxy->irq); memory_region_init_io(&proxy->iomem, OBJECT(d), &virtio_mem_ops, proxy, TYPE_VIRTIO_MMIO, 0x200); @@ -393,17 +391,6 @@ static const TypeInfo virtio_mmio_info = { /* virtio-mmio-bus. */ -static void virtio_mmio_bus_new(VirtioBusState *bus, size_t bus_size, - VirtIOMMIOProxy *dev) -{ - DeviceState *qdev = DEVICE(dev); - BusState *qbus; - - qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_MMIO_BUS, qdev, NULL); - qbus = BUS(bus); - qbus->allow_hotplug = 0; -} - static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data) { BusClass *bus_class = BUS_CLASS(klass); diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 390f8244f3..ae7fef96d5 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1115,6 +1115,8 @@ static void virtio_blk_pci_instance_init(Object *obj) TYPE_VIRTIO_BLK); object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", &error_abort); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); } static const TypeInfo virtio_blk_pci_info = { @@ -1466,6 +1468,8 @@ static void virtio_net_pci_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_NET); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); } static const TypeInfo virtio_net_pci_info = { @@ -1542,13 +1546,10 @@ static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, VirtIOPCIProxy *dev) { DeviceState *qdev = DEVICE(dev); - BusState *qbus; char virtio_bus_name[] = "virtio-bus"; qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_PCI_BUS, qdev, virtio_bus_name); - qbus = BUS(bus); - qbus->allow_hotplug = 1; } static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 77177c5317..e32e25d547 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -374,6 +374,8 @@ float32 float32_min(float32, float32 STATUS_PARAM); float32 float32_max(float32, float32 STATUS_PARAM); float32 float32_minnum(float32, float32 STATUS_PARAM); float32 float32_maxnum(float32, float32 STATUS_PARAM); +float32 float32_minnummag(float32, float32 STATUS_PARAM); +float32 float32_maxnummag(float32, float32 STATUS_PARAM); int float32_is_quiet_nan( float32 ); int float32_is_signaling_nan( float32 ); float32 float32_maybe_silence_nan( float32 ); @@ -484,6 +486,8 @@ float64 float64_min(float64, float64 STATUS_PARAM); float64 float64_max(float64, float64 STATUS_PARAM); float64 float64_minnum(float64, float64 STATUS_PARAM); float64 float64_maxnum(float64, float64 STATUS_PARAM); +float64 float64_minnummag(float64, float64 STATUS_PARAM); +float64 float64_maxnummag(float64, float64 STATUS_PARAM); int float64_is_quiet_nan( float64 a ); int float64_is_signaling_nan( float64 ); float64 float64_maybe_silence_nan( float64 ); diff --git a/include/glib-compat.h b/include/glib-compat.h index 4ae0671a8e..f0615c99c2 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -18,6 +18,11 @@ #include <glib.h> +/* GLIB version compatibility flags */ +#if !GLIB_CHECK_VERSION(2, 26, 0) +#define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000)) +#endif + #if !GLIB_CHECK_VERSION(2, 14, 0) static inline guint g_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data) @@ -26,6 +31,37 @@ static inline guint g_timeout_add_seconds(guint interval, GSourceFunc function, } #endif +#if !GLIB_CHECK_VERSION(2, 28, 0) +static inline gint64 g_get_monotonic_time(void) +{ + /* g_get_monotonic_time() is best-effort so we can use the wall clock as a + * fallback. + */ + + GTimeVal time; + g_get_current_time(&time); + + return time.tv_sec * G_TIME_SPAN_SECOND + time.tv_usec; +} +#endif + +#if !GLIB_CHECK_VERSION(2, 16, 0) +static inline int g_strcmp0(const char *str1, const char *str2) +{ + int result; + + if (!str1) { + result = -(str1 != str2); + } else if (!str2) { + result = (str1 != str2); + } else { + result = strcmp(str1, str2); + } + + return result; +} +#endif + #ifdef _WIN32 /* * g_poll has a problem on Windows when using diff --git a/include/hw/block/block.h b/include/hw/block/block.h index 3a0148848b..867a226643 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -49,7 +49,6 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) _conf.physical_block_size, 512), \ DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \ DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \ - DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \ DEFINE_PROP_UINT32("discard_granularity", _state, \ _conf.discard_granularity, -1) diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h index a6533cb0b1..050d2f0530 100644 --- a/include/hw/hotplug.h +++ b/include/hw/hotplug.h @@ -47,7 +47,12 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler, * * @parent: Opaque parent interface. * @plug: plug callback. + * @unplug_request: unplug request callback. + * Used as a means to initiate device unplug for devices that + * require asynchronous unplug handling. * @unplug: unplug callback. + * Used for device removal with devices that implement + * asynchronous and synchronous (suprise) removal. */ typedef struct HotplugHandlerClass { /* <private> */ @@ -55,6 +60,7 @@ typedef struct HotplugHandlerClass { /* <public> */ hotplug_fn plug; + hotplug_fn unplug_request; hotplug_fn unplug; } HotplugHandlerClass; @@ -68,9 +74,17 @@ void hotplug_handler_plug(HotplugHandler *plug_handler, Error **errp); /** + * hotplug_handler_unplug_request: + * + * Calls #HotplugHandlerClass.unplug_request callback of @plug_handler. + */ +void hotplug_handler_unplug_request(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp); +/** * hotplug_handler_unplug: * - * Call #HotplugHandlerClass.unplug callback of @plug_handler. + * Calls #HotplugHandlerClass.unplug callback of @plug_handler. */ void hotplug_handler_unplug(HotplugHandler *plug_handler, DeviceState *plugged_dev, diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index db21a6197e..c4ee520e20 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -307,6 +307,14 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .property = "old_msi_addr",\ .value = "on",\ },{\ + .driver = "VGA",\ + .property = "qemu-extended-regs",\ + .value = "off",\ + },{\ + .driver = "secondary-vga",\ + .property = "qemu-extended-regs",\ + .value = "off",\ + },{\ .driver = "usb-mouse",\ .property = "usb_version",\ .value = stringify(1),\ diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 72b1549dc4..56e1ed7122 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -76,6 +76,8 @@ void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgReadCallback callback, void *callback_opaque, void *data, size_t len); +void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, + size_t len); FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, hwaddr crl_addr, hwaddr data_addr); diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index d139d588f6..b48a7a2c5a 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -128,6 +128,6 @@ extern const VMStateDescription vmstate_pcie_device; void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void pcie_cap_slot_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp); +void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); #endif /* QEMU_PCIE_H */ diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index eef1a1ad6e..025bc5b268 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h @@ -46,8 +46,8 @@ void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len); void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void shpc_device_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp); +void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); extern VMStateInfo shpc_vmstate_info; #define SHPC_VMSTATE(_field, _type) \ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 178fee2ef6..1fca75c591 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -126,7 +126,6 @@ typedef struct DeviceClass { /* Private to qdev / bus. */ qdev_initfn init; /* TODO remove, once users are converted to realize */ - qdev_event unplug; qdev_event exit; /* TODO remove, once users are converted to unrealize */ const char *bus_type; } DeviceClass; @@ -210,7 +209,6 @@ struct BusState { Object obj; DeviceState *parent; const char *name; - int allow_hotplug; HotplugHandler *hotplug_handler; int max_index; bool realized; @@ -232,7 +230,7 @@ struct Property { struct PropertyInfo { const char *name; - const char *legacy_name; + const char *description; const char **enum_table; int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len); ObjectPropertyAccessor *get; @@ -264,7 +262,8 @@ void qdev_init_nofail(DeviceState *dev); void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version); void qdev_unplug(DeviceState *dev, Error **errp); -int qdev_simple_unplug_cb(DeviceState *dev); +void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); void qdev_machine_creation_done(void); bool qdev_machine_modified(void); @@ -361,11 +360,13 @@ extern int qdev_hotplug; char *qdev_get_dev_path(DeviceState *dev); -static inline void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, - Error **errp) +void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, + Error **errp); + +void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp); + +static inline bool qbus_is_hotpluggable(BusState *bus) { - object_property_set_link(OBJECT(bus), OBJECT(handler), - QDEV_HOTPLUG_HANDLER_PROPERTY, errp); - bus->allow_hotplug = 1; + return bus->hotplug_handler; } #endif diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index b61bedb28c..caaa3201ce 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -146,8 +146,6 @@ struct SCSIBusInfo { void (*transfer_data)(SCSIRequest *req, uint32_t arg); void (*complete)(SCSIRequest *req, uint32_t arg, size_t resid); void (*cancel)(SCSIRequest *req); - void (*hotplug)(SCSIBus *bus, SCSIDevice *dev); - void (*hot_unplug)(SCSIBus *bus, SCSIDevice *dev); void (*change)(SCSIBus *bus, SCSIDevice *dev, SCSISense sense); QEMUSGList *(*get_sg_list)(SCSIRequest *req); diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h index c90f5298ab..401676bf4d 100644 --- a/include/migration/qemu-file.h +++ b/include/migration/qemu-file.h @@ -25,6 +25,8 @@ #define QEMU_FILE_H 1 #include "exec/cpu-common.h" +#include <stdint.h> + /* This function writes a chunk of data to a file at the given position. * The pos argument can be ignored if the file is only being used for * streaming. The handler should try to write all of the data it can. @@ -94,11 +96,19 @@ typedef struct QEMUFileOps { QEMURamSaveFunc *save_page; } QEMUFileOps; +struct QEMUSizedBuffer { + struct iovec *iov; + size_t n_iov; + size_t size; /* total allocated size in all iov's */ + size_t used; /* number of used bytes */ +}; + QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops); QEMUFile *qemu_fopen(const char *filename, const char *mode); QEMUFile *qemu_fdopen(int fd, const char *mode); QEMUFile *qemu_fopen_socket(int fd, const char *mode); QEMUFile *qemu_popen_cmd(const char *command, const char *mode); +QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input); int qemu_get_fd(QEMUFile *f); int qemu_fclose(QEMUFile *f); int64_t qemu_ftell(QEMUFile *f); @@ -110,6 +120,23 @@ void qemu_put_byte(QEMUFile *f, int v); */ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size); bool qemu_file_mode_is_not_valid(const char *mode); +bool qemu_file_is_writable(QEMUFile *f); + +QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len); +QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *); +void qsb_free(QEMUSizedBuffer *); +size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t length); +size_t qsb_get_length(const QEMUSizedBuffer *qsb); +ssize_t qsb_get_buffer(const QEMUSizedBuffer *, off_t start, size_t count, + uint8_t *buf); +ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf, + off_t pos, size_t count); + + +/* + * For use on files opened with qemu_bufopen + */ +const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f); static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v) { diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 9a001bd28f..e45fc49cb1 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -484,6 +484,17 @@ extern const VMStateInfo vmstate_info_bitmap; .start = (_start), \ } +#define VMSTATE_VBUFFER_ALLOC_UINT32(_field, _state, _version, _test, _start, _field_size) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .field_exists = (_test), \ + .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\ + .info = &vmstate_info_buffer, \ + .flags = VMS_VBUFFER|VMS_POINTER|VMS_ALLOC, \ + .offset = offsetof(_state, _field), \ + .start = (_start), \ +} + #define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \ .name = (stringify(_field)), \ .version_id = (_version), \ diff --git a/include/net/net.h b/include/net/net.h index ed594f9bdb..008d610046 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -36,8 +36,7 @@ typedef struct NICConf { #define DEFINE_NIC_PROPERTIES(_state, _conf) \ DEFINE_PROP_MACADDR("mac", _state, _conf.macaddr), \ DEFINE_PROP_VLAN("vlan", _state, _conf.peers), \ - DEFINE_PROP_NETDEV("netdev", _state, _conf.peers), \ - DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1) + DEFINE_PROP_NETDEV("netdev", _state, _conf.peers) /* Net clients */ diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 04df51b6fc..168a4086d8 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -71,6 +71,7 @@ typedef struct SSIBus SSIBus; typedef struct EventNotifier EventNotifier; typedef struct VirtIODevice VirtIODevice; typedef struct QEMUSGList QEMUSGList; +typedef struct QEMUSizedBuffer QEMUSizedBuffer; typedef struct SHPCDevice SHPCDevice; typedef struct FWCfgState FWCfgState; typedef struct PcGuestInfo PcGuestInfo; diff --git a/include/qom/object.h b/include/qom/object.h index 8a05a81a99..89c3092967 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -338,6 +338,7 @@ typedef struct ObjectProperty { gchar *name; gchar *type; + gchar *description; ObjectPropertyAccessor *get; ObjectPropertyAccessor *set; ObjectPropertyResolve *resolve; @@ -1275,6 +1276,19 @@ void object_property_add_alias(Object *obj, const char *name, Error **errp); /** + * object_property_set_description: + * @obj: the object owning the property + * @name: the name of the property + * @description: the description of the property on the object + * @errp: if an error occurs, a pointer to an area to store the error + * + * Set an object property's description. + * + */ +void object_property_set_description(Object *obj, const char *name, + const char *description, Error **errp); + +/** * object_child_foreach: * @obj: the object whose children will be navigated * @fn: the iterator function to be called diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index d8539fd602..0037a695c1 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -130,6 +130,7 @@ extern int no_shutdown; extern int semihosting_enabled; extern int old_param; extern int boot_menu; +extern bool boot_strict; extern uint8_t *boot_splash_filedata; extern size_t boot_splash_filedata_size; extern uint8_t qemu_extra_params_fw[2]; @@ -212,6 +213,11 @@ void add_boot_device_path(int32_t bootindex, DeviceState *dev, char *get_boot_devices_list(size_t *size, bool ignore_suffixes); DeviceState *get_boot_device(uint32_t position); +void check_boot_index(int32_t bootindex, Error **errp); +void del_boot_device_path(DeviceState *dev, const char *suffix); +void device_add_bootindex_property(Object *obj, int32_t *bootindex, + const char *name, const char *suffix, + DeviceState *dev, Error **errp); QemuOpts *qemu_get_machine_opts(void); diff --git a/migration.c b/migration.c index 8d675b31a1..c49a05a165 100644 --- a/migration.c +++ b/migration.c @@ -103,7 +103,6 @@ static void process_incoming_migration_co(void *opaque) } qemu_announce_self(); - bdrv_clear_incoming_migration_all(); /* Make sure all file formats flush their mutable metadata */ bdrv_invalidate_cache_all(&local_err); if (local_err) { @@ -972,6 +972,12 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, exp->ctx = bdrv_get_aio_context(bs); bdrv_ref(bs); bdrv_add_aio_context_notifier(bs, bs_aio_attached, bs_aio_detach, exp); + /* + * NBD exports are used for non-shared storage migration. Make sure + * that BDRV_O_INCOMING is cleared and the image is ready for write + * access since the export could be available before migration handover. + */ + bdrv_invalidate_cache(bs, NULL); return exp; } diff --git a/qapi-schema.json b/qapi-schema.json index 4f0d7e3250..24379ab3af 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1615,11 +1615,13 @@ # # @name: the name of the property # @type: the typename of the property +# @description: #optional if specified, the description of the property. +# (since 2.2) # # Since: 1.2 ## { 'type': 'DevicePropertyInfo', - 'data': { 'name': 'str', 'type': 'str' } } + 'data': { 'name': 'str', 'type': 'str', '*description': 'str' } } ## # @device-list-properties: diff --git a/qdev-monitor.c b/qdev-monitor.c index 5ec66067f5..fac7d179fe 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -213,9 +213,14 @@ int qdev_device_help(QemuOpts *opts) } for (prop = prop_list; prop; prop = prop->next) { - error_printf("%s.%s=%s\n", driver, + error_printf("%s.%s=%s", driver, prop->value->name, prop->value->type); + if (prop->value->has_description) { + error_printf(" (%s)\n", prop->value->description); + } else { + error_printf("\n"); + } } qapi_free_DevicePropertyInfoList(prop_list); @@ -487,7 +492,8 @@ DeviceState *qdev_device_add(QemuOpts *opts) } dc = DEVICE_CLASS(oc); - if (dc->cannot_instantiate_with_device_add_yet) { + if (dc->cannot_instantiate_with_device_add_yet || + (qdev_hotplug && !dc->hotpluggable)) { qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", "pluggable device type"); return NULL; @@ -515,7 +521,7 @@ DeviceState *qdev_device_add(QemuOpts *opts) return NULL; } } - if (qdev_hotplug && bus && !bus->allow_hotplug) { + if (qdev_hotplug && bus && !qbus_is_hotpluggable(bus)) { qerror_report(QERR_BUS_NO_HOTPLUG, bus->name); return NULL; } @@ -685,15 +691,20 @@ int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data) void qmp_device_del(const char *id, Error **errp) { - DeviceState *dev; + Object *obj; + char *root_path = object_get_canonical_path(qdev_get_peripheral()); + char *path = g_strdup_printf("%s/%s", root_path, id); - dev = qdev_find_recursive(sysbus_get_default(), id); - if (!dev) { + g_free(root_path); + obj = object_resolve_path_type(path, TYPE_DEVICE, NULL); + g_free(path); + + if (!obj) { error_set(errp, QERR_DEVICE_NOT_FOUND, id); return; } - qdev_unplug(dev, errp); + qdev_unplug(DEVICE(obj), errp); } void qdev_machine_init(void) diff --git a/qemu-file-stdio.c b/qemu-file-stdio.c new file mode 100644 index 0000000000..285068b303 --- /dev/null +++ b/qemu-file-stdio.c @@ -0,0 +1,194 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "block/coroutine.h" +#include "migration/qemu-file.h" + +typedef struct QEMUFileStdio { + FILE *stdio_file; + QEMUFile *file; +} QEMUFileStdio; + +static int stdio_get_fd(void *opaque) +{ + QEMUFileStdio *s = opaque; + + return fileno(s->stdio_file); +} + +static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, + int size) +{ + QEMUFileStdio *s = opaque; + int res; + + res = fwrite(buf, 1, size, s->stdio_file); + + if (res != size) { + return -errno; + } + return res; +} + +static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUFileStdio *s = opaque; + FILE *fp = s->stdio_file; + int bytes; + + for (;;) { + clearerr(fp); + bytes = fread(buf, 1, size, fp); + if (bytes != 0 || !ferror(fp)) { + break; + } + if (errno == EAGAIN) { + yield_until_fd_readable(fileno(fp)); + } else if (errno != EINTR) { + break; + } + } + return bytes; +} + +static int stdio_pclose(void *opaque) +{ + QEMUFileStdio *s = opaque; + int ret; + ret = pclose(s->stdio_file); + if (ret == -1) { + ret = -errno; + } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) { + /* close succeeded, but non-zero exit code: */ + ret = -EIO; /* fake errno value */ + } + g_free(s); + return ret; +} + +static int stdio_fclose(void *opaque) +{ + QEMUFileStdio *s = opaque; + int ret = 0; + + if (qemu_file_is_writable(s->file)) { + int fd = fileno(s->stdio_file); + struct stat st; + + ret = fstat(fd, &st); + if (ret == 0 && S_ISREG(st.st_mode)) { + /* + * If the file handle is a regular file make sure the + * data is flushed to disk before signaling success. + */ + ret = fsync(fd); + if (ret != 0) { + ret = -errno; + return ret; + } + } + } + if (fclose(s->stdio_file) == EOF) { + ret = -errno; + } + g_free(s); + return ret; +} + +static const QEMUFileOps stdio_pipe_read_ops = { + .get_fd = stdio_get_fd, + .get_buffer = stdio_get_buffer, + .close = stdio_pclose +}; + +static const QEMUFileOps stdio_pipe_write_ops = { + .get_fd = stdio_get_fd, + .put_buffer = stdio_put_buffer, + .close = stdio_pclose +}; + +QEMUFile *qemu_popen_cmd(const char *command, const char *mode) +{ + FILE *stdio_file; + QEMUFileStdio *s; + + if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { + fprintf(stderr, "qemu_popen: Argument validity check failed\n"); + return NULL; + } + + stdio_file = popen(command, mode); + if (stdio_file == NULL) { + return NULL; + } + + s = g_malloc0(sizeof(QEMUFileStdio)); + + s->stdio_file = stdio_file; + + if (mode[0] == 'r') { + s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops); + } else { + s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops); + } + return s->file; +} + +static const QEMUFileOps stdio_file_read_ops = { + .get_fd = stdio_get_fd, + .get_buffer = stdio_get_buffer, + .close = stdio_fclose +}; + +static const QEMUFileOps stdio_file_write_ops = { + .get_fd = stdio_get_fd, + .put_buffer = stdio_put_buffer, + .close = stdio_fclose +}; + +QEMUFile *qemu_fopen(const char *filename, const char *mode) +{ + QEMUFileStdio *s; + + if (qemu_file_mode_is_not_valid(mode)) { + return NULL; + } + + s = g_malloc0(sizeof(QEMUFileStdio)); + + s->stdio_file = fopen(filename, mode); + if (!s->stdio_file) { + goto fail; + } + + if (mode[0] == 'w') { + s->file = qemu_fopen_ops(s, &stdio_file_write_ops); + } else { + s->file = qemu_fopen_ops(s, &stdio_file_read_ops); + } + return s->file; +fail: + g_free(s); + return NULL; +} diff --git a/qemu-file-unix.c b/qemu-file-unix.c new file mode 100644 index 0000000000..9682396d97 --- /dev/null +++ b/qemu-file-unix.c @@ -0,0 +1,223 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" +#include "block/coroutine.h" +#include "migration/qemu-file.h" + +typedef struct QEMUFileSocket { + int fd; + QEMUFile *file; +} QEMUFileSocket; + +static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + ssize_t size = iov_size(iov, iovcnt); + + len = iov_send(s->fd, iov, iovcnt, 0, size); + if (len < size) { + len = -socket_error(); + } + return len; +} + +static int socket_get_fd(void *opaque) +{ + QEMUFileSocket *s = opaque; + + return s->fd; +} + +static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + + for (;;) { + len = qemu_recv(s->fd, buf, size, 0); + if (len != -1) { + break; + } + if (socket_error() == EAGAIN) { + yield_until_fd_readable(s->fd); + } else if (socket_error() != EINTR) { + break; + } + } + + if (len == -1) { + len = -socket_error(); + } + return len; +} + +static int socket_close(void *opaque) +{ + QEMUFileSocket *s = opaque; + closesocket(s->fd); + g_free(s); + return 0; +} + +static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) +{ + QEMUFileSocket *s = opaque; + ssize_t len, offset; + ssize_t size = iov_size(iov, iovcnt); + ssize_t total = 0; + + assert(iovcnt > 0); + offset = 0; + while (size > 0) { + /* Find the next start position; skip all full-sized vector elements */ + while (offset >= iov[0].iov_len) { + offset -= iov[0].iov_len; + iov++, iovcnt--; + } + + /* skip `offset' bytes from the (now) first element, undo it on exit */ + assert(iovcnt > 0); + iov[0].iov_base += offset; + iov[0].iov_len -= offset; + + do { + len = writev(s->fd, iov, iovcnt); + } while (len == -1 && errno == EINTR); + if (len == -1) { + return -errno; + } + + /* Undo the changes above */ + iov[0].iov_base -= offset; + iov[0].iov_len += offset; + + /* Prepare for the next iteration */ + offset += len; + total += len; + size -= len; + } + + return total; +} + +static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + + for (;;) { + len = read(s->fd, buf, size); + if (len != -1) { + break; + } + if (errno == EAGAIN) { + yield_until_fd_readable(s->fd); + } else if (errno != EINTR) { + break; + } + } + + if (len == -1) { + len = -errno; + } + return len; +} + +static int unix_close(void *opaque) +{ + QEMUFileSocket *s = opaque; + close(s->fd); + g_free(s); + return 0; +} + +static const QEMUFileOps unix_read_ops = { + .get_fd = socket_get_fd, + .get_buffer = unix_get_buffer, + .close = unix_close +}; + +static const QEMUFileOps unix_write_ops = { + .get_fd = socket_get_fd, + .writev_buffer = unix_writev_buffer, + .close = unix_close +}; + +QEMUFile *qemu_fdopen(int fd, const char *mode) +{ + QEMUFileSocket *s; + + if (mode == NULL || + (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != 'b' || mode[2] != 0) { + fprintf(stderr, "qemu_fdopen: Argument validity check failed\n"); + return NULL; + } + + s = g_malloc0(sizeof(QEMUFileSocket)); + s->fd = fd; + + if (mode[0] == 'r') { + s->file = qemu_fopen_ops(s, &unix_read_ops); + } else { + s->file = qemu_fopen_ops(s, &unix_write_ops); + } + return s->file; +} + +static const QEMUFileOps socket_read_ops = { + .get_fd = socket_get_fd, + .get_buffer = socket_get_buffer, + .close = socket_close +}; + +static const QEMUFileOps socket_write_ops = { + .get_fd = socket_get_fd, + .writev_buffer = socket_writev_buffer, + .close = socket_close +}; + +QEMUFile *qemu_fopen_socket(int fd, const char *mode) +{ + QEMUFileSocket *s; + + if (qemu_file_mode_is_not_valid(mode)) { + return NULL; + } + + s = g_malloc0(sizeof(QEMUFileSocket)); + s->fd = fd; + if (mode[0] == 'w') { + qemu_set_block(s->fd); + s->file = qemu_fopen_ops(s, &socket_write_ops); + } else { + s->file = qemu_fopen_ops(s, &socket_read_ops); + } + return s->file; +} diff --git a/qemu-file.c b/qemu-file.c index a8e39127f2..f938e36fe8 100644 --- a/qemu-file.c +++ b/qemu-file.c @@ -1,3 +1,26 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ #include "qemu-common.h" #include "qemu/iov.h" #include "qemu/sockets.h" @@ -28,324 +51,6 @@ struct QEMUFile { int last_error; }; -typedef struct QEMUFileStdio { - FILE *stdio_file; - QEMUFile *file; -} QEMUFileStdio; - -typedef struct QEMUFileSocket { - int fd; - QEMUFile *file; -} QEMUFileSocket; - -static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, - int64_t pos) -{ - QEMUFileSocket *s = opaque; - ssize_t len; - ssize_t size = iov_size(iov, iovcnt); - - len = iov_send(s->fd, iov, iovcnt, 0, size); - if (len < size) { - len = -socket_error(); - } - return len; -} - -static int socket_get_fd(void *opaque) -{ - QEMUFileSocket *s = opaque; - - return s->fd; -} - -static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) -{ - QEMUFileSocket *s = opaque; - ssize_t len; - - for (;;) { - len = qemu_recv(s->fd, buf, size, 0); - if (len != -1) { - break; - } - if (socket_error() == EAGAIN) { - yield_until_fd_readable(s->fd); - } else if (socket_error() != EINTR) { - break; - } - } - - if (len == -1) { - len = -socket_error(); - } - return len; -} - -static int socket_close(void *opaque) -{ - QEMUFileSocket *s = opaque; - closesocket(s->fd); - g_free(s); - return 0; -} - -static int stdio_get_fd(void *opaque) -{ - QEMUFileStdio *s = opaque; - - return fileno(s->stdio_file); -} - -static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, - int size) -{ - QEMUFileStdio *s = opaque; - int res; - - res = fwrite(buf, 1, size, s->stdio_file); - - if (res != size) { - return -errno; - } - return res; -} - -static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) -{ - QEMUFileStdio *s = opaque; - FILE *fp = s->stdio_file; - int bytes; - - for (;;) { - clearerr(fp); - bytes = fread(buf, 1, size, fp); - if (bytes != 0 || !ferror(fp)) { - break; - } - if (errno == EAGAIN) { - yield_until_fd_readable(fileno(fp)); - } else if (errno != EINTR) { - break; - } - } - return bytes; -} - -static int stdio_pclose(void *opaque) -{ - QEMUFileStdio *s = opaque; - int ret; - ret = pclose(s->stdio_file); - if (ret == -1) { - ret = -errno; - } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) { - /* close succeeded, but non-zero exit code: */ - ret = -EIO; /* fake errno value */ - } - g_free(s); - return ret; -} - -static int stdio_fclose(void *opaque) -{ - QEMUFileStdio *s = opaque; - int ret = 0; - - if (s->file->ops->put_buffer || s->file->ops->writev_buffer) { - int fd = fileno(s->stdio_file); - struct stat st; - - ret = fstat(fd, &st); - if (ret == 0 && S_ISREG(st.st_mode)) { - /* - * If the file handle is a regular file make sure the - * data is flushed to disk before signaling success. - */ - ret = fsync(fd); - if (ret != 0) { - ret = -errno; - return ret; - } - } - } - if (fclose(s->stdio_file) == EOF) { - ret = -errno; - } - g_free(s); - return ret; -} - -static const QEMUFileOps stdio_pipe_read_ops = { - .get_fd = stdio_get_fd, - .get_buffer = stdio_get_buffer, - .close = stdio_pclose -}; - -static const QEMUFileOps stdio_pipe_write_ops = { - .get_fd = stdio_get_fd, - .put_buffer = stdio_put_buffer, - .close = stdio_pclose -}; - -QEMUFile *qemu_popen_cmd(const char *command, const char *mode) -{ - FILE *stdio_file; - QEMUFileStdio *s; - - if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { - fprintf(stderr, "qemu_popen: Argument validity check failed\n"); - return NULL; - } - - stdio_file = popen(command, mode); - if (stdio_file == NULL) { - return NULL; - } - - s = g_malloc0(sizeof(QEMUFileStdio)); - - s->stdio_file = stdio_file; - - if (mode[0] == 'r') { - s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops); - } else { - s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops); - } - return s->file; -} - -static const QEMUFileOps stdio_file_read_ops = { - .get_fd = stdio_get_fd, - .get_buffer = stdio_get_buffer, - .close = stdio_fclose -}; - -static const QEMUFileOps stdio_file_write_ops = { - .get_fd = stdio_get_fd, - .put_buffer = stdio_put_buffer, - .close = stdio_fclose -}; - -static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, - int64_t pos) -{ - QEMUFileSocket *s = opaque; - ssize_t len, offset; - ssize_t size = iov_size(iov, iovcnt); - ssize_t total = 0; - - assert(iovcnt > 0); - offset = 0; - while (size > 0) { - /* Find the next start position; skip all full-sized vector elements */ - while (offset >= iov[0].iov_len) { - offset -= iov[0].iov_len; - iov++, iovcnt--; - } - - /* skip `offset' bytes from the (now) first element, undo it on exit */ - assert(iovcnt > 0); - iov[0].iov_base += offset; - iov[0].iov_len -= offset; - - do { - len = writev(s->fd, iov, iovcnt); - } while (len == -1 && errno == EINTR); - if (len == -1) { - return -errno; - } - - /* Undo the changes above */ - iov[0].iov_base -= offset; - iov[0].iov_len += offset; - - /* Prepare for the next iteration */ - offset += len; - total += len; - size -= len; - } - - return total; -} - -static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) -{ - QEMUFileSocket *s = opaque; - ssize_t len; - - for (;;) { - len = read(s->fd, buf, size); - if (len != -1) { - break; - } - if (errno == EAGAIN) { - yield_until_fd_readable(s->fd); - } else if (errno != EINTR) { - break; - } - } - - if (len == -1) { - len = -errno; - } - return len; -} - -static int unix_close(void *opaque) -{ - QEMUFileSocket *s = opaque; - close(s->fd); - g_free(s); - return 0; -} - -static const QEMUFileOps unix_read_ops = { - .get_fd = socket_get_fd, - .get_buffer = unix_get_buffer, - .close = unix_close -}; - -static const QEMUFileOps unix_write_ops = { - .get_fd = socket_get_fd, - .writev_buffer = unix_writev_buffer, - .close = unix_close -}; - -QEMUFile *qemu_fdopen(int fd, const char *mode) -{ - QEMUFileSocket *s; - - if (mode == NULL || - (mode[0] != 'r' && mode[0] != 'w') || - mode[1] != 'b' || mode[2] != 0) { - fprintf(stderr, "qemu_fdopen: Argument validity check failed\n"); - return NULL; - } - - s = g_malloc0(sizeof(QEMUFileSocket)); - s->fd = fd; - - if (mode[0] == 'r') { - s->file = qemu_fopen_ops(s, &unix_read_ops); - } else { - s->file = qemu_fopen_ops(s, &unix_write_ops); - } - return s->file; -} - -static const QEMUFileOps socket_read_ops = { - .get_fd = socket_get_fd, - .get_buffer = socket_get_buffer, - .close = socket_close -}; - -static const QEMUFileOps socket_write_ops = { - .get_fd = socket_get_fd, - .writev_buffer = socket_writev_buffer, - .close = socket_close -}; - bool qemu_file_mode_is_not_valid(const char *mode) { if (mode == NULL || @@ -358,51 +63,6 @@ bool qemu_file_mode_is_not_valid(const char *mode) return false; } -QEMUFile *qemu_fopen_socket(int fd, const char *mode) -{ - QEMUFileSocket *s; - - if (qemu_file_mode_is_not_valid(mode)) { - return NULL; - } - - s = g_malloc0(sizeof(QEMUFileSocket)); - s->fd = fd; - if (mode[0] == 'w') { - qemu_set_block(s->fd); - s->file = qemu_fopen_ops(s, &socket_write_ops); - } else { - s->file = qemu_fopen_ops(s, &socket_read_ops); - } - return s->file; -} - -QEMUFile *qemu_fopen(const char *filename, const char *mode) -{ - QEMUFileStdio *s; - - if (qemu_file_mode_is_not_valid(mode)) { - return NULL; - } - - s = g_malloc0(sizeof(QEMUFileStdio)); - - s->stdio_file = fopen(filename, mode); - if (!s->stdio_file) { - goto fail; - } - - if (mode[0] == 'w') { - s->file = qemu_fopen_ops(s, &stdio_file_write_ops); - } else { - s->file = qemu_fopen_ops(s, &stdio_file_read_ops); - } - return s->file; -fail: - g_free(s); - return NULL; -} - QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) { QEMUFile *f; @@ -433,7 +93,7 @@ void qemu_file_set_error(QEMUFile *f, int ret) } } -static inline bool qemu_file_is_writable(QEMUFile *f) +bool qemu_file_is_writable(QEMUFile *f) { return f->ops->writev_buffer || f->ops->put_buffer; } @@ -878,3 +538,458 @@ uint64_t qemu_get_be64(QEMUFile *f) v |= qemu_get_be32(f); return v; } + +#define QSB_CHUNK_SIZE (1 << 10) +#define QSB_MAX_CHUNK_SIZE (16 * QSB_CHUNK_SIZE) + +/** + * Create a QEMUSizedBuffer + * This type of buffer uses scatter-gather lists internally and + * can grow to any size. Any data array in the scatter-gather list + * can hold different amount of bytes. + * + * @buffer: Optional buffer to copy into the QSB + * @len: size of initial buffer; if @buffer is given, buffer must + * hold at least len bytes + * + * Returns a pointer to a QEMUSizedBuffer or NULL on allocation failure + */ +QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len) +{ + QEMUSizedBuffer *qsb; + size_t alloc_len, num_chunks, i, to_copy; + size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE) + ? QSB_MAX_CHUNK_SIZE + : QSB_CHUNK_SIZE; + + num_chunks = DIV_ROUND_UP(len ? len : QSB_CHUNK_SIZE, chunk_size); + alloc_len = num_chunks * chunk_size; + + qsb = g_try_new0(QEMUSizedBuffer, 1); + if (!qsb) { + return NULL; + } + + qsb->iov = g_try_new0(struct iovec, num_chunks); + if (!qsb->iov) { + g_free(qsb); + return NULL; + } + + qsb->n_iov = num_chunks; + + for (i = 0; i < num_chunks; i++) { + qsb->iov[i].iov_base = g_try_malloc0(chunk_size); + if (!qsb->iov[i].iov_base) { + /* qsb_free is safe since g_free can cope with NULL */ + qsb_free(qsb); + return NULL; + } + + qsb->iov[i].iov_len = chunk_size; + if (buffer) { + to_copy = (len - qsb->used) > chunk_size + ? chunk_size : (len - qsb->used); + memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy); + qsb->used += to_copy; + } + } + + qsb->size = alloc_len; + + return qsb; +} + +/** + * Free the QEMUSizedBuffer + * + * @qsb: The QEMUSizedBuffer to free + */ +void qsb_free(QEMUSizedBuffer *qsb) +{ + size_t i; + + if (!qsb) { + return; + } + + for (i = 0; i < qsb->n_iov; i++) { + g_free(qsb->iov[i].iov_base); + } + g_free(qsb->iov); + g_free(qsb); +} + +/** + * Get the number of used bytes in the QEMUSizedBuffer + * + * @qsb: A QEMUSizedBuffer + * + * Returns the number of bytes currently used in this buffer + */ +size_t qsb_get_length(const QEMUSizedBuffer *qsb) +{ + return qsb->used; +} + +/** + * Set the length of the buffer; the primary usage of this + * function is to truncate the number of used bytes in the buffer. + * The size will not be extended beyond the current number of + * allocated bytes in the QEMUSizedBuffer. + * + * @qsb: A QEMUSizedBuffer + * @new_len: The new length of bytes in the buffer + * + * Returns the number of bytes the buffer was truncated or extended + * to. + */ +size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len) +{ + if (new_len <= qsb->size) { + qsb->used = new_len; + } else { + qsb->used = qsb->size; + } + return qsb->used; +} + +/** + * Get the iovec that holds the data for a given position @pos. + * + * @qsb: A QEMUSizedBuffer + * @pos: The index of a byte in the buffer + * @d_off: Pointer to an offset that this function will indicate + * at what position within the returned iovec the byte + * is to be found + * + * Returns the index of the iovec that holds the byte at the given + * index @pos in the byte stream; a negative number if the iovec + * for the given position @pos does not exist. + */ +static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb, + off_t pos, off_t *d_off) +{ + ssize_t i; + off_t curr = 0; + + if (pos > qsb->used) { + return -1; + } + + for (i = 0; i < qsb->n_iov; i++) { + if (curr + qsb->iov[i].iov_len > pos) { + *d_off = pos - curr; + return i; + } + curr += qsb->iov[i].iov_len; + } + return -1; +} + +/* + * Convert the QEMUSizedBuffer into a flat buffer. + * + * Note: If at all possible, try to avoid this function since it + * may unnecessarily copy memory around. + * + * @qsb: pointer to QEMUSizedBuffer + * @start: offset to start at + * @count: number of bytes to copy + * @buf: a pointer to a buffer to write into (at least @count bytes) + * + * Returns the number of bytes copied into the output buffer + */ +ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start, + size_t count, uint8_t *buffer) +{ + const struct iovec *iov; + size_t to_copy, all_copy; + ssize_t index; + off_t s_off; + off_t d_off = 0; + char *s; + + if (start > qsb->used) { + return 0; + } + + all_copy = qsb->used - start; + if (all_copy > count) { + all_copy = count; + } else { + count = all_copy; + } + + index = qsb_get_iovec(qsb, start, &s_off); + if (index < 0) { + return 0; + } + + while (all_copy > 0) { + iov = &qsb->iov[index]; + + s = iov->iov_base; + + to_copy = iov->iov_len - s_off; + if (to_copy > all_copy) { + to_copy = all_copy; + } + memcpy(&buffer[d_off], &s[s_off], to_copy); + + d_off += to_copy; + all_copy -= to_copy; + + s_off = 0; + index++; + } + + return count; +} + +/** + * Grow the QEMUSizedBuffer to the given size and allocate + * memory for it. + * + * @qsb: A QEMUSizedBuffer + * @new_size: The new size of the buffer + * + * Return: + * a negative error code in case of memory allocation failure + * or + * the new size of the buffer. The returned size may be greater or equal + * to @new_size. + */ +static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size) +{ + size_t needed_chunks, i; + + if (qsb->size < new_size) { + struct iovec *new_iov; + size_t size_diff = new_size - qsb->size; + size_t chunk_size = (size_diff > QSB_MAX_CHUNK_SIZE) + ? QSB_MAX_CHUNK_SIZE : QSB_CHUNK_SIZE; + + needed_chunks = DIV_ROUND_UP(size_diff, chunk_size); + + new_iov = g_try_new(struct iovec, qsb->n_iov + needed_chunks); + if (new_iov == NULL) { + return -ENOMEM; + } + + /* Allocate new chunks as needed into new_iov */ + for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) { + new_iov[i].iov_base = g_try_malloc0(chunk_size); + new_iov[i].iov_len = chunk_size; + if (!new_iov[i].iov_base) { + size_t j; + + /* Free previously allocated new chunks */ + for (j = qsb->n_iov; j < i; j++) { + g_free(new_iov[j].iov_base); + } + g_free(new_iov); + + return -ENOMEM; + } + } + + /* + * Now we can't get any allocation errors, copy over to new iov + * and switch. + */ + for (i = 0; i < qsb->n_iov; i++) { + new_iov[i] = qsb->iov[i]; + } + + qsb->n_iov += needed_chunks; + g_free(qsb->iov); + qsb->iov = new_iov; + qsb->size += (needed_chunks * chunk_size); + } + + return qsb->size; +} + +/** + * Write into the QEMUSizedBuffer at a given position and a given + * number of bytes. This function will automatically grow the + * QEMUSizedBuffer. + * + * @qsb: A QEMUSizedBuffer + * @source: A byte array to copy data from + * @pos: The position within the @qsb to write data to + * @size: The number of bytes to copy into the @qsb + * + * Returns @size or a negative error code in case of memory allocation failure, + * or with an invalid 'pos' + */ +ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source, + off_t pos, size_t count) +{ + ssize_t rc = qsb_grow(qsb, pos + count); + size_t to_copy; + size_t all_copy = count; + const struct iovec *iov; + ssize_t index; + char *dest; + off_t d_off, s_off = 0; + + if (rc < 0) { + return rc; + } + + if (pos + count > qsb->used) { + qsb->used = pos + count; + } + + index = qsb_get_iovec(qsb, pos, &d_off); + if (index < 0) { + return -EINVAL; + } + + while (all_copy > 0) { + iov = &qsb->iov[index]; + + dest = iov->iov_base; + + to_copy = iov->iov_len - d_off; + if (to_copy > all_copy) { + to_copy = all_copy; + } + + memcpy(&dest[d_off], &source[s_off], to_copy); + + s_off += to_copy; + all_copy -= to_copy; + + d_off = 0; + index++; + } + + return count; +} + +/** + * Create a deep copy of the given QEMUSizedBuffer. + * + * @qsb: A QEMUSizedBuffer + * + * Returns a clone of @qsb or NULL on allocation failure + */ +QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb) +{ + QEMUSizedBuffer *out = qsb_create(NULL, qsb_get_length(qsb)); + size_t i; + ssize_t res; + off_t pos = 0; + + if (!out) { + return NULL; + } + + for (i = 0; i < qsb->n_iov; i++) { + res = qsb_write_at(out, qsb->iov[i].iov_base, + pos, qsb->iov[i].iov_len); + if (res < 0) { + qsb_free(out); + return NULL; + } + pos += res; + } + + return out; +} + +typedef struct QEMUBuffer { + QEMUSizedBuffer *qsb; + QEMUFile *file; +} QEMUBuffer; + +static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUBuffer *s = opaque; + ssize_t len = qsb_get_length(s->qsb) - pos; + + if (len <= 0) { + return 0; + } + + if (len > size) { + len = size; + } + return qsb_get_buffer(s->qsb, pos, len, buf); +} + +static int buf_put_buffer(void *opaque, const uint8_t *buf, + int64_t pos, int size) +{ + QEMUBuffer *s = opaque; + + return qsb_write_at(s->qsb, buf, pos, size); +} + +static int buf_close(void *opaque) +{ + QEMUBuffer *s = opaque; + + qsb_free(s->qsb); + + g_free(s); + + return 0; +} + +const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f) +{ + QEMUBuffer *p; + + qemu_fflush(f); + + p = f->opaque; + + return p->qsb; +} + +static const QEMUFileOps buf_read_ops = { + .get_buffer = buf_get_buffer, + .close = buf_close, +}; + +static const QEMUFileOps buf_write_ops = { + .put_buffer = buf_put_buffer, + .close = buf_close, +}; + +QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input) +{ + QEMUBuffer *s; + + if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != '\0') { + error_report("qemu_bufopen: Argument validity check failed"); + return NULL; + } + + s = g_malloc0(sizeof(QEMUBuffer)); + if (mode[0] == 'r') { + s->qsb = input; + } + + if (s->qsb == NULL) { + s->qsb = qsb_create(NULL, 0); + } + if (!s->qsb) { + g_free(s); + error_report("qemu_bufopen: qsb_create failed"); + return NULL; + } + + + if (mode[0] == 'r') { + s->file = qemu_fopen_ops(s, &buf_read_ops); + } else { + s->file = qemu_fopen_ops(s, &buf_write_ops); + } + return s->file; +} @@ -442,7 +442,8 @@ ObjectTypeInfoList *qmp_qom_list_types(bool has_implements, */ static DevicePropertyInfo *make_device_property_info(ObjectClass *klass, const char *name, - const char *default_type) + const char *default_type, + const char *description) { DevicePropertyInfo *info; Property *prop; @@ -465,7 +466,9 @@ static DevicePropertyInfo *make_device_property_info(ObjectClass *klass, info = g_malloc0(sizeof(*info)); info->name = g_strdup(prop->name); - info->type = g_strdup(prop->info->legacy_name ?: prop->info->name); + info->type = g_strdup(prop->info->name); + info->has_description = !!prop->info->description; + info->description = g_strdup(prop->info->description); return info; } klass = object_class_get_parent(klass); @@ -475,6 +478,9 @@ static DevicePropertyInfo *make_device_property_info(ObjectClass *klass, info = g_malloc0(sizeof(*info)); info->name = g_strdup(name); info->type = g_strdup(default_type); + info->has_description = !!description; + info->description = g_strdup(description); + return info; } @@ -521,7 +527,8 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename, continue; } - info = make_device_property_info(klass, prop->name, prop->type); + info = make_device_property_info(klass, prop->name, prop->type, + prop->description); if (!info) { continue; } diff --git a/qom/object.c b/qom/object.c index da0919a3dd..a751367e61 100644 --- a/qom/object.c +++ b/qom/object.c @@ -369,6 +369,7 @@ static void object_property_del_all(Object *obj) g_free(prop->name); g_free(prop->type); + g_free(prop->description); g_free(prop); } } @@ -803,6 +804,7 @@ void object_property_del(Object *obj, const char *name, Error **errp) g_free(prop->name); g_free(prop->type); + g_free(prop->description); g_free(prop); } @@ -1010,11 +1012,19 @@ char *object_property_print(Object *obj, const char *name, bool human, Error **errp) { StringOutputVisitor *mo; - char *string; + char *string = NULL; + Error *local_err = NULL; mo = string_output_visitor_new(human); - object_property_get(obj, string_output_get_visitor(mo), name, errp); + object_property_get(obj, string_output_get_visitor(mo), name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + string = string_output_get_string(mo); + +out: string_output_visitor_cleanup(mo); return string; } @@ -1634,6 +1644,7 @@ void object_property_add_alias(Object *obj, const char *name, ObjectProperty *op; ObjectProperty *target_prop; gchar *prop_type; + Error *local_err = NULL; target_prop = object_property_find(target_obj, target_name, errp); if (!target_prop) { @@ -1655,12 +1666,36 @@ void object_property_add_alias(Object *obj, const char *name, property_get_alias, property_set_alias, property_release_alias, - prop, errp); + prop, &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + goto out; + } op->resolve = property_resolve_alias; + object_property_set_description(obj, name, + target_prop->description, + &error_abort); + +out: g_free(prop_type); } +void object_property_set_description(Object *obj, const char *name, + const char *description, Error **errp) +{ + ObjectProperty *op; + + op = object_property_find(obj, name, errp); + if (!op) { + return; + } + + g_free(op->description); + op->description = g_strdup(description); +} + static void object_instance_init(Object *obj) { object_property_add_str(obj, "type", qdev_get_type, NULL, NULL); diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 8b9a92ebdc..26e7894eaf 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -431,7 +431,7 @@ struct CPUMIPSState { int error_code; uint32_t hflags; /* CPU State */ /* TMASK defines different execution modes */ -#define MIPS_HFLAG_TMASK 0xC07FF +#define MIPS_HFLAG_TMASK 0x1807FF #define MIPS_HFLAG_MODE 0x00007 /* execution modes */ /* The KSU flags must be the lowest bits in hflags. The flag order must be the same as defined for CP0 Status. This allows to use @@ -450,7 +450,7 @@ struct CPUMIPSState { and RSQRT.D. */ #define MIPS_HFLAG_COP1X 0x00080 /* COP1X instructions enabled */ #define MIPS_HFLAG_RE 0x00100 /* Reversed endianness */ -#define MIPS_HFLAG_UX 0x00200 /* 64-bit user mode */ +#define MIPS_HFLAG_AWRAP 0x00200 /* 32-bit compatibility address wrapping */ #define MIPS_HFLAG_M16 0x00400 /* MIPS16 mode flag */ #define MIPS_HFLAG_M16_SHIFT 10 /* If translation is interrupted between the branch instruction and @@ -463,17 +463,18 @@ struct CPUMIPSState { #define MIPS_HFLAG_BL 0x01800 /* Likely branch */ #define MIPS_HFLAG_BR 0x02000 /* branch to register (can't link TB) */ /* Extra flags about the current pending branch. */ -#define MIPS_HFLAG_BMASK_EXT 0x3C000 +#define MIPS_HFLAG_BMASK_EXT 0x7C000 #define MIPS_HFLAG_B16 0x04000 /* branch instruction was 16 bits */ #define MIPS_HFLAG_BDS16 0x08000 /* branch requires 16-bit delay slot */ #define MIPS_HFLAG_BDS32 0x10000 /* branch requires 32-bit delay slot */ -#define MIPS_HFLAG_BX 0x20000 /* branch exchanges execution mode */ +#define MIPS_HFLAG_BDS_STRICT 0x20000 /* Strict delay slot size */ +#define MIPS_HFLAG_BX 0x40000 /* branch exchanges execution mode */ #define MIPS_HFLAG_BMASK (MIPS_HFLAG_BMASK_BASE | MIPS_HFLAG_BMASK_EXT) /* MIPS DSP resources access. */ -#define MIPS_HFLAG_DSP 0x40000 /* Enable access to MIPS DSP resources. */ -#define MIPS_HFLAG_DSPR2 0x80000 /* Enable access to MIPS DSPR2 resources. */ +#define MIPS_HFLAG_DSP 0x080000 /* Enable access to MIPS DSP resources. */ +#define MIPS_HFLAG_DSPR2 0x100000 /* Enable access to MIPS DSPR2 resources. */ /* Extra flag about HWREna register. */ -#define MIPS_HFLAG_HWRENA_ULR 0x100000 /* ULR bit from HWREna is set. */ +#define MIPS_HFLAG_HWRENA_ULR 0x200000 /* ULR bit from HWREna is set. */ target_ulong btarget; /* Jump / branch target */ target_ulong bcond; /* Branch condition (if needed) */ @@ -725,7 +726,7 @@ static inline void compute_hflags(CPUMIPSState *env) { env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU | - MIPS_HFLAG_UX | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2); + MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2); if (!(env->CP0_Status & (1 << CP0St_EXL)) && !(env->CP0_Status & (1 << CP0St_ERL)) && !(env->hflags & MIPS_HFLAG_DM)) { @@ -737,8 +738,18 @@ static inline void compute_hflags(CPUMIPSState *env) (env->CP0_Status & (1 << CP0St_UX))) { env->hflags |= MIPS_HFLAG_64; } - if (env->CP0_Status & (1 << CP0St_UX)) { - env->hflags |= MIPS_HFLAG_UX; + + if (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) && + !(env->CP0_Status & (1 << CP0St_UX))) { + env->hflags |= MIPS_HFLAG_AWRAP; + } else if (env->insn_flags & ISA_MIPS32R6) { + /* Address wrapping for Supervisor and Kernel is specified in R6 */ + if ((((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_SM) && + !(env->CP0_Status & (1 << CP0St_SX))) || + (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_KM) && + !(env->CP0_Status & (1 << CP0St_KX)))) { + env->hflags |= MIPS_HFLAG_AWRAP; + } } #endif if ((env->CP0_Status & (1 << CP0St_CU0)) || diff --git a/target-mips/dsp_helper.c b/target-mips/dsp_helper.c index 94083fb424..349f2a0033 100644 --- a/target-mips/dsp_helper.c +++ b/target-mips/dsp_helper.c @@ -76,15 +76,6 @@ static inline void set_DSPControl_24(uint32_t flag, int len, CPUMIPSState *env) env->active_tc.DSPControl |= (target_ulong)flag << 24; } -static inline uint32_t get_DSPControl_24(int len, CPUMIPSState *env) -{ - uint32_t filter; - - filter = (0x01 << len) - 1; - - return (env->active_tc.DSPControl >> 24) & filter; -} - static inline void set_DSPControl_pos(uint32_t pos, CPUMIPSState *env) { target_ulong dspc; @@ -283,6 +274,7 @@ static inline int32_t mipsdsp_sat32_acc_q31(int32_t acc, int32_t a, return result; } +#ifdef TARGET_MIPS64 /* a[0] is LO, a[1] is HI. */ static inline void mipsdsp_sat64_acc_add_q63(int64_t *ret, int32_t ac, @@ -336,6 +328,7 @@ static inline void mipsdsp_sat64_acc_sub_q63(int64_t *ret, set_DSPControl_overflow_flag(1, 16 + ac, env); } } +#endif static inline int32_t mipsdsp_mul_i16_i16(int16_t a, int16_t b, CPUMIPSState *env) @@ -357,10 +350,12 @@ static inline int32_t mipsdsp_mul_u16_u16(int32_t a, int32_t b) return a * b; } +#ifdef TARGET_MIPS64 static inline int32_t mipsdsp_mul_i32_i32(int32_t a, int32_t b) { return a * b; } +#endif static inline int32_t mipsdsp_sat16_mul_i16_i16(int16_t a, int16_t b, CPUMIPSState *env) @@ -417,10 +412,12 @@ static inline int16_t mipsdsp_rashift16(int16_t a, target_ulong mov) return a >> mov; } +#ifdef TARGET_MIPS64 static inline int32_t mipsdsp_rashift32(int32_t a, target_ulong mov) { return a >> mov; } +#endif static inline int16_t mipsdsp_rshift1_add_q16(int16_t a, int16_t b) { @@ -479,6 +476,7 @@ static inline uint8_t mipsdsp_rrshift1_add_u8(uint8_t a, uint8_t b) return (temp >> 1) & 0x00FF; } +#ifdef TARGET_MIPS64 static inline uint8_t mipsdsp_rshift1_sub_u8(uint8_t a, uint8_t b) { uint16_t temp; @@ -496,6 +494,7 @@ static inline uint8_t mipsdsp_rrshift1_sub_u8(uint8_t a, uint8_t b) return (temp >> 1) & 0x00FF; } +#endif /* 128 bits long. p[0] is LO, p[1] is HI. */ static inline void mipsdsp_rndrashift_short_acc(int64_t *p, @@ -511,6 +510,7 @@ static inline void mipsdsp_rndrashift_short_acc(int64_t *p, p[1] = (acc >> 63) & 0x01; } +#ifdef TARGET_MIPS64 /* 128 bits long. p[0] is LO, p[1] is HI */ static inline void mipsdsp_rashift_acc(uint64_t *p, uint32_t ac, @@ -558,6 +558,7 @@ static inline void mipsdsp_rndrashift_acc(uint64_t *p, } } } +#endif static inline int32_t mipsdsp_mul_q15_q15(int32_t ac, uint16_t a, uint16_t b, CPUMIPSState *env) @@ -608,10 +609,12 @@ static inline uint16_t mipsdsp_mul_u8_u16(uint8_t a, uint16_t b, return tempI & 0x0000FFFF; } +#ifdef TARGET_MIPS64 static inline uint64_t mipsdsp_mul_u32_u32(uint32_t a, uint32_t b) { return (uint64_t)a * (uint64_t)b; } +#endif static inline int16_t mipsdsp_rndq15_mul_q15_q15(uint16_t a, uint16_t b, CPUMIPSState *env) @@ -717,7 +720,7 @@ static inline uint16_t mipsdsp_lshift16(uint16_t a, uint8_t s, return a << s; } - +#ifdef TARGET_MIPS64 static inline uint32_t mipsdsp_lshift32(uint32_t a, uint8_t s, CPUMIPSState *env) { @@ -734,6 +737,7 @@ static inline uint32_t mipsdsp_lshift32(uint32_t a, uint8_t s, return a << s; } } +#endif static inline uint16_t mipsdsp_sat16_lshift(uint16_t a, uint8_t s, CPUMIPSState *env) @@ -973,6 +977,7 @@ static inline uint8_t mipsdsp_satu8_sub(uint8_t a, uint8_t b, CPUMIPSState *env) return temp & 0x00FF; } +#ifdef TARGET_MIPS64 static inline uint32_t mipsdsp_sub32(int32_t a, int32_t b, CPUMIPSState *env) { int32_t temp; @@ -997,6 +1002,7 @@ static inline int32_t mipsdsp_add_i32(int32_t a, int32_t b, CPUMIPSState *env) return temp; } +#endif static inline int32_t mipsdsp_cmp_eq(int32_t a, int32_t b) { diff --git a/target-mips/helper.h b/target-mips/helper.h index 74ef09485f..a127db55c2 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -39,6 +39,11 @@ DEF_HELPER_3(macchiu, tl, env, tl, tl) DEF_HELPER_3(msachi, tl, env, tl, tl) DEF_HELPER_3(msachiu, tl, env, tl, tl) +DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) +#ifdef TARGET_MIPS64 +DEF_HELPER_FLAGS_1(dbitswap, TCG_CALL_NO_RWG_SE, tl, tl) +#endif + #ifndef CONFIG_USER_ONLY /* CP0 helpers */ DEF_HELPER_1(mfc0_mvpcontrol, tl, env) @@ -197,6 +202,25 @@ DEF_HELPER_2(float_cvtw_d, i32, env, i64) DEF_HELPER_3(float_addr_ps, i64, env, i64, i64) DEF_HELPER_3(float_mulr_ps, i64, env, i64, i64) +DEF_HELPER_FLAGS_1(float_class_s, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(float_class_d, TCG_CALL_NO_RWG_SE, i64, i64) + +#define FOP_PROTO(op) \ +DEF_HELPER_4(float_ ## op ## _s, i32, env, i32, i32, i32) \ +DEF_HELPER_4(float_ ## op ## _d, i64, env, i64, i64, i64) +FOP_PROTO(maddf) +FOP_PROTO(msubf) +#undef FOP_PROTO + +#define FOP_PROTO(op) \ +DEF_HELPER_3(float_ ## op ## _s, i32, env, i32, i32) \ +DEF_HELPER_3(float_ ## op ## _d, i64, env, i64, i64) +FOP_PROTO(max) +FOP_PROTO(maxa) +FOP_PROTO(min) +FOP_PROTO(mina) +#undef FOP_PROTO + #define FOP_PROTO(op) \ DEF_HELPER_2(float_ ## op ## l_s, i64, env, i32) \ DEF_HELPER_2(float_ ## op ## l_d, i64, env, i64) \ @@ -214,6 +238,7 @@ DEF_HELPER_2(float_ ## op ## _d, i64, env, i64) FOP_PROTO(sqrt) FOP_PROTO(rsqrt) FOP_PROTO(recip) +FOP_PROTO(rint) #undef FOP_PROTO #define FOP_PROTO(op) \ @@ -279,6 +304,33 @@ FOP_PROTO(le) FOP_PROTO(ngt) #undef FOP_PROTO +#define FOP_PROTO(op) \ +DEF_HELPER_3(r6_cmp_d_ ## op, i64, env, i64, i64) \ +DEF_HELPER_3(r6_cmp_s_ ## op, i32, env, i32, i32) +FOP_PROTO(af) +FOP_PROTO(un) +FOP_PROTO(eq) +FOP_PROTO(ueq) +FOP_PROTO(lt) +FOP_PROTO(ult) +FOP_PROTO(le) +FOP_PROTO(ule) +FOP_PROTO(saf) +FOP_PROTO(sun) +FOP_PROTO(seq) +FOP_PROTO(sueq) +FOP_PROTO(slt) +FOP_PROTO(sult) +FOP_PROTO(sle) +FOP_PROTO(sule) +FOP_PROTO(or) +FOP_PROTO(une) +FOP_PROTO(ne) +FOP_PROTO(sor) +FOP_PROTO(sune) +FOP_PROTO(sne) +#undef FOP_PROTO + /* Special functions */ #ifndef CONFIG_USER_ONLY DEF_HELPER_1(tlbwi, void, env) diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h index 9dfa5168da..6cb62b2df0 100644 --- a/target-mips/mips-defs.h +++ b/target-mips/mips-defs.h @@ -30,17 +30,21 @@ #define ISA_MIPS64 0x00000080 #define ISA_MIPS64R2 0x00000100 #define ISA_MIPS32R3 0x00000200 -#define ISA_MIPS32R5 0x00000400 +#define ISA_MIPS64R3 0x00000400 +#define ISA_MIPS32R5 0x00000800 +#define ISA_MIPS64R5 0x00001000 +#define ISA_MIPS32R6 0x00002000 +#define ISA_MIPS64R6 0x00004000 /* MIPS ASEs. */ -#define ASE_MIPS16 0x00001000 -#define ASE_MIPS3D 0x00002000 -#define ASE_MDMX 0x00004000 -#define ASE_DSP 0x00008000 -#define ASE_DSPR2 0x00010000 -#define ASE_MT 0x00020000 -#define ASE_SMARTMIPS 0x00040000 -#define ASE_MICROMIPS 0x00080000 +#define ASE_MIPS16 0x00010000 +#define ASE_MIPS3D 0x00020000 +#define ASE_MDMX 0x00040000 +#define ASE_DSP 0x00080000 +#define ASE_DSPR2 0x00100000 +#define ASE_MT 0x00200000 +#define ASE_SMARTMIPS 0x00400000 +#define ASE_MICROMIPS 0x00800000 /* Chip specific instructions. */ #define INSN_LOONGSON2E 0x20000000 @@ -68,9 +72,15 @@ /* MIPS Technologies "Release 3" */ #define CPU_MIPS32R3 (CPU_MIPS32R2 | ISA_MIPS32R3) +#define CPU_MIPS64R3 (CPU_MIPS64R2 | CPU_MIPS32R3 | ISA_MIPS64R3) /* MIPS Technologies "Release 5" */ #define CPU_MIPS32R5 (CPU_MIPS32R3 | ISA_MIPS32R5) +#define CPU_MIPS64R5 (CPU_MIPS64R3 | CPU_MIPS32R5 | ISA_MIPS64R5) + +/* MIPS Technologies "Release 6" */ +#define CPU_MIPS32R6 (CPU_MIPS32R5 | ISA_MIPS32R6) +#define CPU_MIPS64R6 (CPU_MIPS64R5 | CPU_MIPS32R6 | ISA_MIPS64R6) /* Strictly follow the architecture standard: - Disallow "special" instruction handling for PMON/SPIM. diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index df97b35f87..5204ed87ca 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -90,7 +90,6 @@ static inline type do_##name(CPUMIPSState *env, target_ulong addr, \ } \ } #endif -HELPER_LD(lbu, ldub, uint8_t) HELPER_LD(lw, ldl, int32_t) #ifdef TARGET_MIPS64 HELPER_LD(ld, ldq, int64_t) @@ -266,6 +265,29 @@ target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1, (uint64_t)(uint32_t)arg2); } +static inline target_ulong bitswap(target_ulong v) +{ + v = ((v >> 1) & (target_ulong)0x5555555555555555) | + ((v & (target_ulong)0x5555555555555555) << 1); + v = ((v >> 2) & (target_ulong)0x3333333333333333) | + ((v & (target_ulong)0x3333333333333333) << 2); + v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0F) | + ((v & (target_ulong)0x0F0F0F0F0F0F0F0F) << 4); + return v; +} + +#ifdef TARGET_MIPS64 +target_ulong helper_dbitswap(target_ulong rt) +{ + return bitswap(rt); +} +#endif + +target_ulong helper_bitswap(target_ulong rt) +{ + return (int32_t)bitswap(rt); +} + #ifndef CONFIG_USER_ONLY static inline hwaddr do_translate_address(CPUMIPSState *env, @@ -2786,6 +2808,110 @@ FLOAT_UNOP(abs) FLOAT_UNOP(chs) #undef FLOAT_UNOP +#define FLOAT_FMADDSUB(name, bits, muladd_arg) \ +uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env, \ + uint ## bits ## _t fs, \ + uint ## bits ## _t ft, \ + uint ## bits ## _t fd) \ +{ \ + uint ## bits ## _t fdret; \ + \ + fdret = float ## bits ## _muladd(fs, ft, fd, muladd_arg, \ + &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return fdret; \ +} + +FLOAT_FMADDSUB(maddf_s, 32, 0) +FLOAT_FMADDSUB(maddf_d, 64, 0) +FLOAT_FMADDSUB(msubf_s, 32, float_muladd_negate_product) +FLOAT_FMADDSUB(msubf_d, 64, float_muladd_negate_product) +#undef FLOAT_FMADDSUB + +#define FLOAT_MINMAX(name, bits, minmaxfunc) \ +uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env, \ + uint ## bits ## _t fs, \ + uint ## bits ## _t ft) \ +{ \ + uint ## bits ## _t fdret; \ + \ + fdret = float ## bits ## _ ## minmaxfunc(fs, ft, \ + &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return fdret; \ +} + +FLOAT_MINMAX(max_s, 32, maxnum) +FLOAT_MINMAX(max_d, 64, maxnum) +FLOAT_MINMAX(maxa_s, 32, maxnummag) +FLOAT_MINMAX(maxa_d, 64, maxnummag) + +FLOAT_MINMAX(min_s, 32, minnum) +FLOAT_MINMAX(min_d, 64, minnum) +FLOAT_MINMAX(mina_s, 32, minnummag) +FLOAT_MINMAX(mina_d, 64, minnummag) +#undef FLOAT_MINMAX + +#define FLOAT_RINT(name, bits) \ +uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env, \ + uint ## bits ## _t fs) \ +{ \ + uint ## bits ## _t fdret; \ + \ + fdret = float ## bits ## _round_to_int(fs, &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return fdret; \ +} + +FLOAT_RINT(rint_s, 32) +FLOAT_RINT(rint_d, 64) +#undef FLOAT_RINT + +#define FLOAT_CLASS_SIGNALING_NAN 0x001 +#define FLOAT_CLASS_QUIET_NAN 0x002 +#define FLOAT_CLASS_NEGATIVE_INFINITY 0x004 +#define FLOAT_CLASS_NEGATIVE_NORMAL 0x008 +#define FLOAT_CLASS_NEGATIVE_SUBNORMAL 0x010 +#define FLOAT_CLASS_NEGATIVE_ZERO 0x020 +#define FLOAT_CLASS_POSITIVE_INFINITY 0x040 +#define FLOAT_CLASS_POSITIVE_NORMAL 0x080 +#define FLOAT_CLASS_POSITIVE_SUBNORMAL 0x100 +#define FLOAT_CLASS_POSITIVE_ZERO 0x200 + +#define FLOAT_CLASS(name, bits) \ +uint ## bits ## _t helper_float_ ## name (uint ## bits ## _t arg) \ +{ \ + if (float ## bits ## _is_signaling_nan(arg)) { \ + return FLOAT_CLASS_SIGNALING_NAN; \ + } else if (float ## bits ## _is_quiet_nan(arg)) { \ + return FLOAT_CLASS_QUIET_NAN; \ + } else if (float ## bits ## _is_neg(arg)) { \ + if (float ## bits ## _is_infinity(arg)) { \ + return FLOAT_CLASS_NEGATIVE_INFINITY; \ + } else if (float ## bits ## _is_zero(arg)) { \ + return FLOAT_CLASS_NEGATIVE_ZERO; \ + } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ + return FLOAT_CLASS_NEGATIVE_SUBNORMAL; \ + } else { \ + return FLOAT_CLASS_NEGATIVE_NORMAL; \ + } \ + } else { \ + if (float ## bits ## _is_infinity(arg)) { \ + return FLOAT_CLASS_POSITIVE_INFINITY; \ + } else if (float ## bits ## _is_zero(arg)) { \ + return FLOAT_CLASS_POSITIVE_ZERO; \ + } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ + return FLOAT_CLASS_POSITIVE_SUBNORMAL; \ + } else { \ + return FLOAT_CLASS_POSITIVE_NORMAL; \ + } \ + } \ +} + +FLOAT_CLASS(class_s, 32) +FLOAT_CLASS(class_d, 64) +#undef FLOAT_CLASS + /* MIPS specific unary operations */ uint64_t helper_float_recip_d(CPUMIPSState *env, uint64_t fdt0) { @@ -3261,3 +3387,114 @@ FOP_COND_PS(le, float32_le(fst0, fst1, &env->active_fpu.fp_status), float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) FOP_COND_PS(ngt, float32_unordered(fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status), float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status) || float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) + +/* R6 compare operations */ +#define FOP_CONDN_D(op, cond) \ +uint64_t helper_r6_cmp_d_ ## op(CPUMIPSState * env, uint64_t fdt0, \ + uint64_t fdt1) \ +{ \ + uint64_t c; \ + c = cond; \ + update_fcr31(env, GETPC()); \ + if (c) { \ + return -1; \ + } else { \ + return 0; \ + } \ +} + +/* NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered_quiet() is still called. */ +FOP_CONDN_D(af, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status), 0)) +FOP_CONDN_D(un, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status))) +FOP_CONDN_D(eq, (float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(ueq, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(lt, (float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(ult, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(le, (float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(ule, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +/* NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered() is still called. */ +FOP_CONDN_D(saf, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status), 0)) +FOP_CONDN_D(sun, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status))) +FOP_CONDN_D(seq, (float64_eq(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sueq, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_eq(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(slt, (float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sult, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sle, (float64_le(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sule, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_le(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(or, (float64_le_quiet(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(une, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_lt_quiet(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(ne, (float64_lt_quiet(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sor, (float64_le(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_le(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sune, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_lt(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sne, (float64_lt(fdt1, fdt0, &env->active_fpu.fp_status) + || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))) + +#define FOP_CONDN_S(op, cond) \ +uint32_t helper_r6_cmp_s_ ## op(CPUMIPSState * env, uint32_t fst0, \ + uint32_t fst1) \ +{ \ + uint64_t c; \ + c = cond; \ + update_fcr31(env, GETPC()); \ + if (c) { \ + return -1; \ + } else { \ + return 0; \ + } \ +} + +/* NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered_quiet() is still called. */ +FOP_CONDN_S(af, (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status), 0)) +FOP_CONDN_S(un, (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status))) +FOP_CONDN_S(eq, (float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(ueq, (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) + || float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(lt, (float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(ult, (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) + || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(le, (float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(ule, (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) + || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status))) +/* NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered() is still called. */ +FOP_CONDN_S(saf, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status), 0)) +FOP_CONDN_S(sun, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status))) +FOP_CONDN_S(seq, (float32_eq(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sueq, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status) + || float32_eq(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(slt, (float32_lt(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sult, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status) + || float32_lt(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sle, (float32_le(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sule, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status) + || float32_le(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(or, (float32_le_quiet(fst1, fst0, &env->active_fpu.fp_status) + || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(une, (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) + || float32_lt_quiet(fst1, fst0, &env->active_fpu.fp_status) + || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(ne, (float32_lt_quiet(fst1, fst0, &env->active_fpu.fp_status) + || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sor, (float32_le(fst1, fst0, &env->active_fpu.fp_status) + || float32_le(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sune, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status) + || float32_lt(fst1, fst0, &env->active_fpu.fp_status) + || float32_lt(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sne, (float32_lt(fst1, fst0, &env->active_fpu.fp_status) + || float32_lt(fst0, fst1, &env->active_fpu.fp_status))) diff --git a/target-mips/translate.c b/target-mips/translate.c index 06db150325..446eb8a20f 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -65,7 +65,6 @@ enum { /* Jump and branches */ OPC_J = (0x02 << 26), OPC_JAL = (0x03 << 26), - OPC_JALS = OPC_JAL | 0x5, OPC_BEQ = (0x04 << 26), /* Unconditional if rs = rt = 0 (B) */ OPC_BEQL = (0x14 << 26), OPC_BNE = (0x05 << 26), @@ -74,8 +73,8 @@ enum { OPC_BLEZL = (0x16 << 26), OPC_BGTZ = (0x07 << 26), OPC_BGTZL = (0x17 << 26), - OPC_JALX = (0x1D << 26), /* MIPS 16 only */ - OPC_JALXS = OPC_JALX | 0x5, + OPC_JALX = (0x1D << 26), + OPC_DAUI = (0x1D << 26), /* Load and stores */ OPC_LDL = (0x1A << 26), OPC_LDR = (0x1B << 26), @@ -111,13 +110,55 @@ enum { OPC_SWC2 = (0x3A << 26), OPC_SDC1 = (0x3D << 26), OPC_SDC2 = (0x3E << 26), + /* Compact Branches */ + OPC_BLEZALC = (0x06 << 26), + OPC_BGEZALC = (0x06 << 26), + OPC_BGEUC = (0x06 << 26), + OPC_BGTZALC = (0x07 << 26), + OPC_BLTZALC = (0x07 << 26), + OPC_BLTUC = (0x07 << 26), + OPC_BOVC = (0x08 << 26), + OPC_BEQZALC = (0x08 << 26), + OPC_BEQC = (0x08 << 26), + OPC_BLEZC = (0x16 << 26), + OPC_BGEZC = (0x16 << 26), + OPC_BGEC = (0x16 << 26), + OPC_BGTZC = (0x17 << 26), + OPC_BLTZC = (0x17 << 26), + OPC_BLTC = (0x17 << 26), + OPC_BNVC = (0x18 << 26), + OPC_BNEZALC = (0x18 << 26), + OPC_BNEC = (0x18 << 26), + OPC_BC = (0x32 << 26), + OPC_BEQZC = (0x36 << 26), + OPC_JIC = (0x36 << 26), + OPC_BALC = (0x3A << 26), + OPC_BNEZC = (0x3E << 26), + OPC_JIALC = (0x3E << 26), /* MDMX ASE specific */ OPC_MDMX = (0x1E << 26), /* Cache and prefetch */ OPC_CACHE = (0x2F << 26), OPC_PREF = (0x33 << 26), - /* Reserved major opcode */ - OPC_MAJOR3B_RESERVED = (0x3B << 26), + /* PC-relative address computation / loads */ + OPC_PCREL = (0x3B << 26), +}; + +/* PC-relative address computation / loads */ +#define MASK_OPC_PCREL_TOP2BITS(op) (MASK_OP_MAJOR(op) | (op & (3 << 19))) +#define MASK_OPC_PCREL_TOP5BITS(op) (MASK_OP_MAJOR(op) | (op & (0x1f << 16))) +enum { + /* Instructions determined by bits 19 and 20 */ + OPC_ADDIUPC = OPC_PCREL | (0 << 19), + R6_OPC_LWPC = OPC_PCREL | (1 << 19), + OPC_LWUPC = OPC_PCREL | (2 << 19), + + /* Instructions determined by bits 16 ... 20 */ + OPC_AUIPC = OPC_PCREL | (0x1e << 16), + OPC_ALUIPC = OPC_PCREL | (0x1f << 16), + + /* Other */ + R6_OPC_LDPC = OPC_PCREL | (6 << 18), }; /* MIPS special opcodes */ @@ -157,6 +198,7 @@ enum { OPC_DMULTU = 0x1D | OPC_SPECIAL, OPC_DDIV = 0x1E | OPC_SPECIAL, OPC_DDIVU = 0x1F | OPC_SPECIAL, + /* 2 registers arithmetic / logic */ OPC_ADD = 0x20 | OPC_SPECIAL, OPC_ADDU = 0x21 | OPC_SPECIAL, @@ -175,8 +217,6 @@ enum { /* Jumps */ OPC_JR = 0x08 | OPC_SPECIAL, /* Also JR.HB */ OPC_JALR = 0x09 | OPC_SPECIAL, /* Also JALR.HB */ - OPC_JALRC = OPC_JALR | (0x5 << 6), - OPC_JALRS = 0x10 | OPC_SPECIAL | (0x5 << 6), /* Traps */ OPC_TGE = 0x30 | OPC_SPECIAL, OPC_TGEU = 0x31 | OPC_SPECIAL, @@ -193,6 +233,9 @@ enum { OPC_MOVZ = 0x0A | OPC_SPECIAL, OPC_MOVN = 0x0B | OPC_SPECIAL, + OPC_SELEQZ = 0x35 | OPC_SPECIAL, + OPC_SELNEZ = 0x37 | OPC_SPECIAL, + OPC_MOVCI = 0x01 | OPC_SPECIAL, /* Special */ @@ -202,15 +245,45 @@ enum { OPC_SPIM = 0x0E | OPC_SPECIAL, /* unofficial */ OPC_SYNC = 0x0F | OPC_SPECIAL, - OPC_SPECIAL15_RESERVED = 0x15 | OPC_SPECIAL, OPC_SPECIAL28_RESERVED = 0x28 | OPC_SPECIAL, OPC_SPECIAL29_RESERVED = 0x29 | OPC_SPECIAL, - OPC_SPECIAL35_RESERVED = 0x35 | OPC_SPECIAL, - OPC_SPECIAL37_RESERVED = 0x37 | OPC_SPECIAL, OPC_SPECIAL39_RESERVED = 0x39 | OPC_SPECIAL, OPC_SPECIAL3D_RESERVED = 0x3D | OPC_SPECIAL, }; +/* R6 Multiply and Divide instructions have the same Opcode + and function field as legacy OPC_MULT[U]/OPC_DIV[U] */ +#define MASK_R6_MULDIV(op) (MASK_SPECIAL(op) | (op & (0x7ff))) + +enum { + R6_OPC_MUL = OPC_MULT | (2 << 6), + R6_OPC_MUH = OPC_MULT | (3 << 6), + R6_OPC_MULU = OPC_MULTU | (2 << 6), + R6_OPC_MUHU = OPC_MULTU | (3 << 6), + R6_OPC_DIV = OPC_DIV | (2 << 6), + R6_OPC_MOD = OPC_DIV | (3 << 6), + R6_OPC_DIVU = OPC_DIVU | (2 << 6), + R6_OPC_MODU = OPC_DIVU | (3 << 6), + + R6_OPC_DMUL = OPC_DMULT | (2 << 6), + R6_OPC_DMUH = OPC_DMULT | (3 << 6), + R6_OPC_DMULU = OPC_DMULTU | (2 << 6), + R6_OPC_DMUHU = OPC_DMULTU | (3 << 6), + R6_OPC_DDIV = OPC_DDIV | (2 << 6), + R6_OPC_DMOD = OPC_DDIV | (3 << 6), + R6_OPC_DDIVU = OPC_DDIVU | (2 << 6), + R6_OPC_DMODU = OPC_DDIVU | (3 << 6), + + R6_OPC_CLZ = 0x10 | OPC_SPECIAL, + R6_OPC_CLO = 0x11 | OPC_SPECIAL, + R6_OPC_DCLZ = 0x12 | OPC_SPECIAL, + R6_OPC_DCLO = 0x13 | OPC_SPECIAL, + R6_OPC_SDBBP = 0x0e | OPC_SPECIAL, + + OPC_LSA = 0x05 | OPC_SPECIAL, + OPC_DLSA = 0x15 | OPC_SPECIAL, +}; + /* Multiplication variants of the vr54xx. */ #define MASK_MUL_VR54XX(op) MASK_SPECIAL(op) | (op & (0x1F << 6)) @@ -240,10 +313,8 @@ enum { OPC_BGEZ = (0x01 << 16) | OPC_REGIMM, OPC_BGEZL = (0x03 << 16) | OPC_REGIMM, OPC_BLTZAL = (0x10 << 16) | OPC_REGIMM, - OPC_BLTZALS = OPC_BLTZAL | 0x5, /* microMIPS */ OPC_BLTZALL = (0x12 << 16) | OPC_REGIMM, OPC_BGEZAL = (0x11 << 16) | OPC_REGIMM, - OPC_BGEZALS = OPC_BGEZAL | 0x5, /* microMIPS */ OPC_BGEZALL = (0x13 << 16) | OPC_REGIMM, OPC_TGEI = (0x08 << 16) | OPC_REGIMM, OPC_TGEIU = (0x09 << 16) | OPC_REGIMM, @@ -252,6 +323,9 @@ enum { OPC_TEQI = (0x0C << 16) | OPC_REGIMM, OPC_TNEI = (0x0E << 16) | OPC_REGIMM, OPC_SYNCI = (0x1F << 16) | OPC_REGIMM, + + OPC_DAHI = (0x06 << 16) | OPC_REGIMM, + OPC_DATI = (0x1e << 16) | OPC_REGIMM, }; /* Special2 opcodes */ @@ -346,23 +420,37 @@ enum { /* MIPS DSP Accumulator and DSPControl Access Sub-class */ OPC_EXTR_W_DSP = 0x38 | OPC_SPECIAL3, OPC_DEXTR_W_DSP = 0x3C | OPC_SPECIAL3, + + /* R6 */ + R6_OPC_PREF = 0x35 | OPC_SPECIAL3, + R6_OPC_CACHE = 0x25 | OPC_SPECIAL3, + R6_OPC_LL = 0x36 | OPC_SPECIAL3, + R6_OPC_SC = 0x26 | OPC_SPECIAL3, + R6_OPC_LLD = 0x37 | OPC_SPECIAL3, + R6_OPC_SCD = 0x27 | OPC_SPECIAL3, }; /* BSHFL opcodes */ #define MASK_BSHFL(op) MASK_SPECIAL3(op) | (op & (0x1F << 6)) enum { - OPC_WSBH = (0x02 << 6) | OPC_BSHFL, - OPC_SEB = (0x10 << 6) | OPC_BSHFL, - OPC_SEH = (0x18 << 6) | OPC_BSHFL, + OPC_WSBH = (0x02 << 6) | OPC_BSHFL, + OPC_SEB = (0x10 << 6) | OPC_BSHFL, + OPC_SEH = (0x18 << 6) | OPC_BSHFL, + OPC_ALIGN = (0x08 << 6) | OPC_BSHFL, /* 010.bp */ + OPC_ALIGN_END = (0x0B << 6) | OPC_BSHFL, /* 010.00 to 010.11 */ + OPC_BITSWAP = (0x00 << 6) | OPC_BSHFL /* 00000 */ }; /* DBSHFL opcodes */ #define MASK_DBSHFL(op) MASK_SPECIAL3(op) | (op & (0x1F << 6)) enum { - OPC_DSBH = (0x02 << 6) | OPC_DBSHFL, - OPC_DSHD = (0x05 << 6) | OPC_DBSHFL, + OPC_DSBH = (0x02 << 6) | OPC_DBSHFL, + OPC_DSHD = (0x05 << 6) | OPC_DBSHFL, + OPC_DALIGN = (0x08 << 6) | OPC_DBSHFL, /* 01.bp */ + OPC_DALIGN_END = (0x0F << 6) | OPC_DBSHFL, /* 01.000 to 01.111 */ + OPC_DBITSWAP = (0x00 << 6) | OPC_DBSHFL, /* 00000 */ }; /* MIPS DSP REGIMM opcodes */ @@ -851,6 +939,8 @@ enum { OPC_W_FMT = (FMT_W << 21) | OPC_CP1, OPC_L_FMT = (FMT_L << 21) | OPC_CP1, OPC_PS_FMT = (FMT_PS << 21) | OPC_CP1, + OPC_BC1EQZ = (0x09 << 21) | OPC_CP1, + OPC_BC1NEZ = (0x0D << 21) | OPC_CP1, }; #define MASK_CP1_FUNC(op) MASK_CP1(op) | (op & 0x3F) @@ -885,6 +975,8 @@ enum { OPC_CTC2 = (0x06 << 21) | OPC_CP2, OPC_MTHC2 = (0x07 << 21) | OPC_CP2, OPC_BC2 = (0x08 << 21) | OPC_CP2, + OPC_BC2EQZ = (0x09 << 21) | OPC_CP2, + OPC_BC2NEZ = (0x0D << 21) | OPC_CP2, }; #define MASK_LMI(op) (MASK_OP_MAJOR(op) | (op & (0x1F << 21)) | (op & 0x1F)) @@ -1012,7 +1104,7 @@ enum { /* global register indices */ static TCGv_ptr cpu_env; static TCGv cpu_gpr[32], cpu_PC; -static TCGv cpu_HI[MIPS_DSP_ACC], cpu_LO[MIPS_DSP_ACC], cpu_ACX[MIPS_DSP_ACC]; +static TCGv cpu_HI[MIPS_DSP_ACC], cpu_LO[MIPS_DSP_ACC]; static TCGv cpu_dspctrl, btarget, bcond; static TCGv_i32 hflags; static TCGv_i32 fpu_fcr0, fpu_fcr31; @@ -1103,10 +1195,6 @@ static const char * const regnames_LO[] = { "LO0", "LO1", "LO2", "LO3", }; -static const char * const regnames_ACX[] = { - "ACX0", "ACX1", "ACX2", "ACX3", -}; - static const char * const fregnames[] = { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", @@ -1149,17 +1237,6 @@ static inline void gen_store_gpr (TCGv t, int reg) tcg_gen_mov_tl(cpu_gpr[reg], t); } -/* Moves to/from ACX register. */ -static inline void gen_load_ACX (TCGv t, int reg) -{ - tcg_gen_mov_tl(t, cpu_ACX[reg]); -} - -static inline void gen_store_ACX (TCGv t, int reg) -{ - tcg_gen_mov_tl(cpu_ACX[reg], t); -} - /* Moves to/from shadow registers. */ static inline void gen_load_srsgpr (int from, int to) { @@ -1343,16 +1420,26 @@ static inline void gen_op_addr_add (DisasContext *ctx, TCGv ret, TCGv arg0, TCGv tcg_gen_add_tl(ret, arg0, arg1); #if defined(TARGET_MIPS64) - /* For compatibility with 32-bit code, data reference in user mode - with Status_UX = 0 should be casted to 32-bit and sign extended. - See the MIPS64 PRA manual, section 4.10. */ - if (((ctx->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) && - !(ctx->hflags & MIPS_HFLAG_UX)) { + if (ctx->hflags & MIPS_HFLAG_AWRAP) { tcg_gen_ext32s_i64(ret, ret); } #endif } +/* Addresses computation (translation time) */ +static target_long addr_add(DisasContext *ctx, target_long base, + target_long offset) +{ + target_long sum = base + offset; + +#if defined(TARGET_MIPS64) + if (ctx->hflags & MIPS_HFLAG_AWRAP) { + sum = (int32_t)sum; + } +#endif + return sum; +} + static inline void check_cp0_enabled(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_CP0))) @@ -1436,6 +1523,17 @@ static inline void check_insn(DisasContext *ctx, int flags) } } +/* This code generates a "reserved instruction" exception if the + CPU has corresponding flag set which indicates that the instruction + has been removed. */ +static inline void check_insn_opc_removed(DisasContext *ctx, int flags) +{ + if (unlikely(ctx->insn_flags & flags)) { + generate_exception(ctx, EXCP_RI); + } +} + +#ifdef TARGET_MIPS64 /* This code generates a "reserved instruction" exception if 64-bit instructions are not enabled. */ static inline void check_mips_64(DisasContext *ctx) @@ -1443,6 +1541,7 @@ static inline void check_mips_64(DisasContext *ctx) if (unlikely(!(ctx->hflags & MIPS_HFLAG_64))) generate_exception(ctx, EXCP_RI); } +#endif /* Define small wrappers for gen_load_fpr* so that we have a uniform calling interface for 32 and 64-bit FPRs. No sense in changing @@ -1504,6 +1603,98 @@ FOP_CONDS(abs, 1, s, FMT_S, 32) FOP_CONDS(, 0, ps, FMT_PS, 64) FOP_CONDS(abs, 1, ps, FMT_PS, 64) #undef FOP_CONDS + +#define FOP_CONDNS(fmt, ifmt, bits, STORE) \ +static inline void gen_r6_cmp_ ## fmt(DisasContext * ctx, int n, \ + int ft, int fs, int fd) \ +{ \ + TCGv_i ## bits fp0 = tcg_temp_new_i ## bits(); \ + TCGv_i ## bits fp1 = tcg_temp_new_i ## bits(); \ + switch (ifmt) { \ + case FMT_D: \ + check_cp1_registers(ctx, fs | ft | fd); \ + break; \ + } \ + gen_ldcmp_fpr ## bits(ctx, fp0, fs); \ + gen_ldcmp_fpr ## bits(ctx, fp1, ft); \ + switch (n) { \ + case 0: \ + gen_helper_r6_cmp_ ## fmt ## _af(fp0, cpu_env, fp0, fp1); \ + break; \ + case 1: \ + gen_helper_r6_cmp_ ## fmt ## _un(fp0, cpu_env, fp0, fp1); \ + break; \ + case 2: \ + gen_helper_r6_cmp_ ## fmt ## _eq(fp0, cpu_env, fp0, fp1); \ + break; \ + case 3: \ + gen_helper_r6_cmp_ ## fmt ## _ueq(fp0, cpu_env, fp0, fp1); \ + break; \ + case 4: \ + gen_helper_r6_cmp_ ## fmt ## _lt(fp0, cpu_env, fp0, fp1); \ + break; \ + case 5: \ + gen_helper_r6_cmp_ ## fmt ## _ult(fp0, cpu_env, fp0, fp1); \ + break; \ + case 6: \ + gen_helper_r6_cmp_ ## fmt ## _le(fp0, cpu_env, fp0, fp1); \ + break; \ + case 7: \ + gen_helper_r6_cmp_ ## fmt ## _ule(fp0, cpu_env, fp0, fp1); \ + break; \ + case 8: \ + gen_helper_r6_cmp_ ## fmt ## _saf(fp0, cpu_env, fp0, fp1); \ + break; \ + case 9: \ + gen_helper_r6_cmp_ ## fmt ## _sun(fp0, cpu_env, fp0, fp1); \ + break; \ + case 10: \ + gen_helper_r6_cmp_ ## fmt ## _seq(fp0, cpu_env, fp0, fp1); \ + break; \ + case 11: \ + gen_helper_r6_cmp_ ## fmt ## _sueq(fp0, cpu_env, fp0, fp1); \ + break; \ + case 12: \ + gen_helper_r6_cmp_ ## fmt ## _slt(fp0, cpu_env, fp0, fp1); \ + break; \ + case 13: \ + gen_helper_r6_cmp_ ## fmt ## _sult(fp0, cpu_env, fp0, fp1); \ + break; \ + case 14: \ + gen_helper_r6_cmp_ ## fmt ## _sle(fp0, cpu_env, fp0, fp1); \ + break; \ + case 15: \ + gen_helper_r6_cmp_ ## fmt ## _sule(fp0, cpu_env, fp0, fp1); \ + break; \ + case 17: \ + gen_helper_r6_cmp_ ## fmt ## _or(fp0, cpu_env, fp0, fp1); \ + break; \ + case 18: \ + gen_helper_r6_cmp_ ## fmt ## _une(fp0, cpu_env, fp0, fp1); \ + break; \ + case 19: \ + gen_helper_r6_cmp_ ## fmt ## _ne(fp0, cpu_env, fp0, fp1); \ + break; \ + case 25: \ + gen_helper_r6_cmp_ ## fmt ## _sor(fp0, cpu_env, fp0, fp1); \ + break; \ + case 26: \ + gen_helper_r6_cmp_ ## fmt ## _sune(fp0, cpu_env, fp0, fp1); \ + break; \ + case 27: \ + gen_helper_r6_cmp_ ## fmt ## _sne(fp0, cpu_env, fp0, fp1); \ + break; \ + default: \ + abort(); \ + } \ + STORE; \ + tcg_temp_free_i ## bits (fp0); \ + tcg_temp_free_i ## bits (fp1); \ +} + +FOP_CONDNS(d, FMT_D, 64, gen_store_fpr64(ctx, fp0, fd)) +FOP_CONDNS(s, FMT_S, 32, gen_store_fpr32(fp0, fd)) +#undef FOP_CONDNS #undef gen_ldcmp_fpr32 #undef gen_ldcmp_fpr64 @@ -1630,6 +1821,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, opn = "ld"; break; case OPC_LLD: + case R6_OPC_LLD: save_cpu_state(ctx, 1); op_ld_lld(t0, t0, ctx); gen_store_gpr(t0, rt); @@ -1764,6 +1956,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, opn = "lwr"; break; case OPC_LL: + case R6_OPC_LL: save_cpu_state(ctx, 1); op_ld_ll(t0, t0, ctx); gen_store_gpr(t0, rt); @@ -1851,12 +2044,14 @@ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, switch (opc) { #if defined(TARGET_MIPS64) case OPC_SCD: + case R6_OPC_SCD: save_cpu_state(ctx, 1); op_st_scd(t1, t0, rt, ctx); opn = "scd"; break; #endif case OPC_SC: + case R6_OPC_SC: save_cpu_state(ctx, 1); op_st_sc(t1, t0, rt, ctx); opn = "sc"; @@ -2063,8 +2258,15 @@ static void gen_logic_imm(DisasContext *ctx, uint32_t opc, regnames[rs], uimm); break; case OPC_LUI: - tcg_gen_movi_tl(cpu_gpr[rt], imm << 16); - MIPS_DEBUG("lui %s, " TARGET_FMT_lx, regnames[rt], uimm); + if (rs != 0 && (ctx->insn_flags & ISA_MIPS32R6)) { + /* OPC_AUI */ + tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], imm << 16); + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + MIPS_DEBUG("aui %s, %s, %04x", regnames[rt], regnames[rs], imm); + } else { + tcg_gen_movi_tl(cpu_gpr[rt], imm << 16); + MIPS_DEBUG("lui %s, " TARGET_FMT_lx, regnames[rt], uimm); + } break; default: @@ -2402,6 +2604,14 @@ static void gen_cond_move(DisasContext *ctx, uint32_t opc, tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr[rd], t0, t1, t2, cpu_gpr[rd]); opn = "movz"; break; + case OPC_SELNEZ: + tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rd], t0, t1, t2, t1); + opn = "selnez"; + break; + case OPC_SELEQZ: + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr[rd], t0, t1, t2, t1); + opn = "seleqz"; + break; } tcg_temp_free(t2); tcg_temp_free(t1); @@ -2660,6 +2870,309 @@ static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) MIPS_DEBUG("%s %s", opn, regnames[reg]); } +static inline void gen_r6_ld(target_long addr, int reg, int memidx, + TCGMemOp memop) +{ + TCGv t0 = tcg_const_tl(addr); + tcg_gen_qemu_ld_tl(t0, t0, memidx, memop); + gen_store_gpr(t0, reg); + tcg_temp_free(t0); +} + +static inline void gen_pcrel(DisasContext *ctx, int rs, int16_t imm) +{ + target_long offset; + target_long addr; + + switch (MASK_OPC_PCREL_TOP2BITS(ctx->opcode)) { + case OPC_ADDIUPC: + if (rs != 0) { + offset = sextract32(ctx->opcode << 2, 0, 21); + addr = addr_add(ctx, ctx->pc, offset); + tcg_gen_movi_tl(cpu_gpr[rs], addr); + } + break; + case R6_OPC_LWPC: + offset = sextract32(ctx->opcode << 2, 0, 21); + addr = addr_add(ctx, ctx->pc, offset); + gen_r6_ld(addr, rs, ctx->mem_idx, MO_TESL); + break; +#if defined(TARGET_MIPS64) + case OPC_LWUPC: + check_mips_64(ctx); + offset = sextract32(ctx->opcode << 2, 0, 21); + addr = addr_add(ctx, ctx->pc, offset); + gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEUL); + break; +#endif + default: + switch (MASK_OPC_PCREL_TOP5BITS(ctx->opcode)) { + case OPC_AUIPC: + if (rs != 0) { + offset = imm << 16; + addr = addr_add(ctx, ctx->pc, offset); + tcg_gen_movi_tl(cpu_gpr[rs], addr); + } + break; + case OPC_ALUIPC: + if (rs != 0) { + offset = imm << 16; + addr = ~0xFFFF & addr_add(ctx, ctx->pc, offset); + tcg_gen_movi_tl(cpu_gpr[rs], addr); + } + break; +#if defined(TARGET_MIPS64) + case R6_OPC_LDPC: /* bits 16 and 17 are part of immediate */ + case R6_OPC_LDPC + (1 << 16): + case R6_OPC_LDPC + (2 << 16): + case R6_OPC_LDPC + (3 << 16): + check_mips_64(ctx); + offset = sextract32(ctx->opcode << 3, 0, 21); + addr = addr_add(ctx, (ctx->pc & ~0x7), offset); + gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEQ); + break; +#endif + default: + MIPS_INVAL("OPC_PCREL"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + } +} + +static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) +{ + const char *opn = "r6 mul/div"; + TCGv t0, t1; + + if (rd == 0) { + /* Treat as NOP. */ + MIPS_DEBUG("NOP"); + return; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + switch (opc) { + case R6_OPC_DIV: + { + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + tcg_gen_ext32s_tl(t0, t0); + tcg_gen_ext32s_tl(t1, t1); + tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); + tcg_gen_and_tl(t2, t2, t3); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_tl(t2, t2, t3); + tcg_gen_movi_tl(t3, 0); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_div_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + opn = "div"; + break; + case R6_OPC_MOD: + { + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + tcg_gen_ext32s_tl(t0, t0); + tcg_gen_ext32s_tl(t1, t1); + tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); + tcg_gen_and_tl(t2, t2, t3); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_tl(t2, t2, t3); + tcg_gen_movi_tl(t3, 0); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + opn = "mod"; + break; + case R6_OPC_DIVU: + { + TCGv t2 = tcg_const_tl(0); + TCGv t3 = tcg_const_tl(1); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + opn = "divu"; + break; + case R6_OPC_MODU: + { + TCGv t2 = tcg_const_tl(0); + TCGv t3 = tcg_const_tl(1); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + opn = "modu"; + break; + case R6_OPC_MUL: + { + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_mul_i32(t2, t2, t3); + tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + opn = "mul"; + break; + case R6_OPC_MUH: + { + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_muls2_i32(t2, t3, t2, t3); + tcg_gen_ext_i32_tl(cpu_gpr[rd], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + opn = "muh"; + break; + case R6_OPC_MULU: + { + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_mul_i32(t2, t2, t3); + tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + opn = "mulu"; + break; + case R6_OPC_MUHU: + { + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_mulu2_i32(t2, t3, t2, t3); + tcg_gen_ext_i32_tl(cpu_gpr[rd], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + opn = "muhu"; + break; +#if defined(TARGET_MIPS64) + case R6_OPC_DDIV: + { + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, -1LL << 63); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1LL); + tcg_gen_and_tl(t2, t2, t3); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_tl(t2, t2, t3); + tcg_gen_movi_tl(t3, 0); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_div_tl(cpu_gpr[rd], t0, t1); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + opn = "ddiv"; + break; + case R6_OPC_DMOD: + { + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, -1LL << 63); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1LL); + tcg_gen_and_tl(t2, t2, t3); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_tl(t2, t2, t3); + tcg_gen_movi_tl(t3, 0); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + opn = "dmod"; + break; + case R6_OPC_DDIVU: + { + TCGv t2 = tcg_const_tl(0); + TCGv t3 = tcg_const_tl(1); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_divu_i64(cpu_gpr[rd], t0, t1); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + opn = "ddivu"; + break; + case R6_OPC_DMODU: + { + TCGv t2 = tcg_const_tl(0); + TCGv t3 = tcg_const_tl(1); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_remu_i64(cpu_gpr[rd], t0, t1); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + opn = "dmodu"; + break; + case R6_OPC_DMUL: + tcg_gen_mul_i64(cpu_gpr[rd], t0, t1); + opn = "dmul"; + break; + case R6_OPC_DMUH: + { + TCGv t2 = tcg_temp_new(); + tcg_gen_muls2_i64(t2, cpu_gpr[rd], t0, t1); + tcg_temp_free(t2); + } + opn = "dmuh"; + break; + case R6_OPC_DMULU: + tcg_gen_mul_i64(cpu_gpr[rd], t0, t1); + opn = "dmulu"; + break; + case R6_OPC_DMUHU: + { + TCGv t2 = tcg_temp_new(); + tcg_gen_mulu2_i64(t2, cpu_gpr[rd], t0, t1); + tcg_temp_free(t2); + } + opn = "dmuhu"; + break; +#endif + default: + MIPS_INVAL(opn); + generate_exception(ctx, EXCP_RI); + goto out; + } + (void)opn; /* avoid a compiler warning */ + MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]); + out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} + static void gen_muldiv(DisasContext *ctx, uint32_t opc, int acc, int rs, int rt) { @@ -2975,19 +3488,23 @@ static void gen_cl (DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rs); switch (opc) { case OPC_CLO: + case R6_OPC_CLO: gen_helper_clo(cpu_gpr[rd], t0); opn = "clo"; break; case OPC_CLZ: + case R6_OPC_CLZ: gen_helper_clz(cpu_gpr[rd], t0); opn = "clz"; break; #if defined(TARGET_MIPS64) case OPC_DCLO: + case R6_OPC_DCLO: gen_helper_dclo(cpu_gpr[rd], t0); opn = "dclo"; break; case OPC_DCLZ: + case R6_OPC_DCLZ: gen_helper_dclz(cpu_gpr[rd], t0); opn = "dclz"; break; @@ -3601,7 +4118,8 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) /* Branches (before delay slot) */ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, int insn_bytes, - int rs, int rt, int32_t offset) + int rs, int rt, int32_t offset, + int delayslot_size) { target_ulong btgt = -1; int blink = 0; @@ -3633,7 +4151,6 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, break; case OPC_BGEZ: case OPC_BGEZAL: - case OPC_BGEZALS: case OPC_BGEZALL: case OPC_BGEZL: case OPC_BGTZ: @@ -3642,7 +4159,6 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, case OPC_BLEZL: case OPC_BLTZ: case OPC_BLTZAL: - case OPC_BLTZALS: case OPC_BLTZALL: case OPC_BLTZL: /* Compare to zero */ @@ -3665,15 +4181,11 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, case OPC_J: case OPC_JAL: case OPC_JALX: - case OPC_JALS: - case OPC_JALXS: /* Jump to immediate */ btgt = ((ctx->pc + insn_bytes) & (int32_t)0xF0000000) | (uint32_t)offset; break; case OPC_JR: case OPC_JALR: - case OPC_JALRC: - case OPC_JALRS: /* Jump to register */ if (offset != 0 && offset != 16) { /* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the @@ -3702,12 +4214,8 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, ctx->hflags |= MIPS_HFLAG_B; MIPS_DEBUG("balways"); break; - case OPC_BGEZALS: case OPC_BGEZAL: /* 0 >= 0 */ case OPC_BGEZALL: /* 0 >= 0 likely */ - ctx->hflags |= (opc == OPC_BGEZALS - ? MIPS_HFLAG_BDS16 - : MIPS_HFLAG_BDS32); /* Always take and link */ blink = 31; ctx->hflags |= MIPS_HFLAG_B; @@ -3719,15 +4227,11 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, /* Treat as NOP. */ MIPS_DEBUG("bnever (NOP)"); goto out; - case OPC_BLTZALS: case OPC_BLTZAL: /* 0 < 0 */ - ctx->hflags |= (opc == OPC_BLTZALS - ? MIPS_HFLAG_BDS16 - : MIPS_HFLAG_BDS32); /* Handle as an unconditional branch to get correct delay slot checking. */ blink = 31; - btgt = ctx->pc + (opc == OPC_BLTZALS ? 6 : 8); + btgt = ctx->pc + insn_bytes + delayslot_size; ctx->hflags |= MIPS_HFLAG_B; MIPS_DEBUG("bnever and link"); break; @@ -3748,33 +4252,21 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, ctx->hflags |= MIPS_HFLAG_B; MIPS_DEBUG("j " TARGET_FMT_lx, btgt); break; - case OPC_JALXS: case OPC_JALX: ctx->hflags |= MIPS_HFLAG_BX; /* Fallthrough */ - case OPC_JALS: case OPC_JAL: blink = 31; ctx->hflags |= MIPS_HFLAG_B; - ctx->hflags |= ((opc == OPC_JALS || opc == OPC_JALXS) - ? MIPS_HFLAG_BDS16 - : MIPS_HFLAG_BDS32); MIPS_DEBUG("jal " TARGET_FMT_lx, btgt); break; case OPC_JR: ctx->hflags |= MIPS_HFLAG_BR; - if (insn_bytes == 4) - ctx->hflags |= MIPS_HFLAG_BDS32; MIPS_DEBUG("jr %s", regnames[rs]); break; - case OPC_JALRS: case OPC_JALR: - case OPC_JALRC: blink = rt; ctx->hflags |= MIPS_HFLAG_BR; - ctx->hflags |= (opc == OPC_JALRS - ? MIPS_HFLAG_BDS16 - : MIPS_HFLAG_BDS32); MIPS_DEBUG("jalr %s, %s", regnames[rt], regnames[rs]); break; default: @@ -3812,11 +4304,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); MIPS_DEBUG("bgezl %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; - case OPC_BGEZALS: case OPC_BGEZAL: - ctx->hflags |= (opc == OPC_BGEZALS - ? MIPS_HFLAG_BDS16 - : MIPS_HFLAG_BDS32); tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); MIPS_DEBUG("bgezal %s, " TARGET_FMT_lx, regnames[rs], btgt); blink = 31; @@ -3860,11 +4348,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, MIPS_DEBUG("bposge64 " TARGET_FMT_lx, btgt); goto not_likely; #endif - case OPC_BLTZALS: case OPC_BLTZAL: - ctx->hflags |= (opc == OPC_BLTZALS - ? MIPS_HFLAG_BDS16 - : MIPS_HFLAG_BDS32); tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); blink = 31; MIPS_DEBUG("bltzal %s, " TARGET_FMT_lx, regnames[rs], btgt); @@ -3888,13 +4372,20 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, blink, ctx->hflags, btgt); ctx->btarget = btgt; + + switch (delayslot_size) { + case 2: + ctx->hflags |= MIPS_HFLAG_BDS16; + break; + case 4: + ctx->hflags |= MIPS_HFLAG_BDS32; + break; + } + if (blink > 0) { - int post_delay = insn_bytes; + int post_delay = insn_bytes + delayslot_size; int lowbit = !!(ctx->hflags & MIPS_HFLAG_M16); - if (opc != OPC_JALRC) - post_delay += ((ctx->hflags & MIPS_HFLAG_BDS16) ? 2 : 4); - tcg_gen_movi_tl(cpu_gpr[blink], ctx->pc + post_delay + lowbit); } @@ -7128,11 +7619,60 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn, ctx->hflags, btarget); ctx->btarget = btarget; - + ctx->hflags |= MIPS_HFLAG_BDS32; out: tcg_temp_free_i32(t0); } +/* R6 CP1 Branches */ +static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, + int32_t ft, int32_t offset) +{ + target_ulong btarget; + const char *opn = "cp1 cond branch"; + TCGv_i64 t0 = tcg_temp_new_i64(); + + if (ctx->hflags & MIPS_HFLAG_BMASK) { +#ifdef MIPS_DEBUG_DISAS + LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); +#endif + generate_exception(ctx, EXCP_RI); + goto out; + } + + gen_load_fpr64(ctx, t0, ft); + tcg_gen_andi_i64(t0, t0, 1); + + btarget = addr_add(ctx, ctx->pc + 4, offset); + + switch (op) { + case OPC_BC1EQZ: + tcg_gen_xori_i64(t0, t0, 1); + opn = "bc1eqz"; + ctx->hflags |= MIPS_HFLAG_BC; + break; + case OPC_BC1NEZ: + /* t0 already set */ + opn = "bc1nez"; + ctx->hflags |= MIPS_HFLAG_BC; + break; + default: + MIPS_INVAL(opn); + generate_exception(ctx, EXCP_RI); + goto out; + } + + tcg_gen_trunc_i64_tl(bcond, t0); + + (void)opn; /* avoid a compiler warning */ + MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn, + ctx->hflags, btarget); + ctx->btarget = btarget; + +out: + tcg_temp_free_i64(t0); +} + /* Coprocessor 1 (FPU) */ #define FOP(func, fmt) (((fmt) << 21) | (func)) @@ -7154,14 +7694,25 @@ enum fopcode { OPC_TRUNC_W_S = FOP(13, FMT_S), OPC_CEIL_W_S = FOP(14, FMT_S), OPC_FLOOR_W_S = FOP(15, FMT_S), + OPC_SEL_S = FOP(16, FMT_S), OPC_MOVCF_S = FOP(17, FMT_S), OPC_MOVZ_S = FOP(18, FMT_S), OPC_MOVN_S = FOP(19, FMT_S), + OPC_SELEQZ_S = FOP(20, FMT_S), OPC_RECIP_S = FOP(21, FMT_S), OPC_RSQRT_S = FOP(22, FMT_S), + OPC_SELNEZ_S = FOP(23, FMT_S), + OPC_MADDF_S = FOP(24, FMT_S), + OPC_MSUBF_S = FOP(25, FMT_S), + OPC_RINT_S = FOP(26, FMT_S), + OPC_CLASS_S = FOP(27, FMT_S), + OPC_MIN_S = FOP(28, FMT_S), OPC_RECIP2_S = FOP(28, FMT_S), + OPC_MINA_S = FOP(29, FMT_S), OPC_RECIP1_S = FOP(29, FMT_S), + OPC_MAX_S = FOP(30, FMT_S), OPC_RSQRT1_S = FOP(30, FMT_S), + OPC_MAXA_S = FOP(31, FMT_S), OPC_RSQRT2_S = FOP(31, FMT_S), OPC_CVT_D_S = FOP(33, FMT_S), OPC_CVT_W_S = FOP(36, FMT_S), @@ -7200,14 +7751,25 @@ enum fopcode { OPC_TRUNC_W_D = FOP(13, FMT_D), OPC_CEIL_W_D = FOP(14, FMT_D), OPC_FLOOR_W_D = FOP(15, FMT_D), + OPC_SEL_D = FOP(16, FMT_D), OPC_MOVCF_D = FOP(17, FMT_D), OPC_MOVZ_D = FOP(18, FMT_D), OPC_MOVN_D = FOP(19, FMT_D), + OPC_SELEQZ_D = FOP(20, FMT_D), OPC_RECIP_D = FOP(21, FMT_D), OPC_RSQRT_D = FOP(22, FMT_D), + OPC_SELNEZ_D = FOP(23, FMT_D), + OPC_MADDF_D = FOP(24, FMT_D), + OPC_MSUBF_D = FOP(25, FMT_D), + OPC_RINT_D = FOP(26, FMT_D), + OPC_CLASS_D = FOP(27, FMT_D), + OPC_MIN_D = FOP(28, FMT_D), OPC_RECIP2_D = FOP(28, FMT_D), + OPC_MINA_D = FOP(29, FMT_D), OPC_RECIP1_D = FOP(29, FMT_D), + OPC_MAX_D = FOP(30, FMT_D), OPC_RSQRT1_D = FOP(30, FMT_D), + OPC_MAXA_D = FOP(31, FMT_D), OPC_RSQRT2_D = FOP(31, FMT_D), OPC_CVT_S_D = FOP(32, FMT_D), OPC_CVT_W_D = FOP(36, FMT_D), @@ -7277,6 +7839,53 @@ enum fopcode { OPC_CMP_NGT_PS = FOP (63, FMT_PS), }; +enum r6_f_cmp_op { + R6_OPC_CMP_AF_S = FOP(0, FMT_W), + R6_OPC_CMP_UN_S = FOP(1, FMT_W), + R6_OPC_CMP_EQ_S = FOP(2, FMT_W), + R6_OPC_CMP_UEQ_S = FOP(3, FMT_W), + R6_OPC_CMP_LT_S = FOP(4, FMT_W), + R6_OPC_CMP_ULT_S = FOP(5, FMT_W), + R6_OPC_CMP_LE_S = FOP(6, FMT_W), + R6_OPC_CMP_ULE_S = FOP(7, FMT_W), + R6_OPC_CMP_SAF_S = FOP(8, FMT_W), + R6_OPC_CMP_SUN_S = FOP(9, FMT_W), + R6_OPC_CMP_SEQ_S = FOP(10, FMT_W), + R6_OPC_CMP_SEUQ_S = FOP(11, FMT_W), + R6_OPC_CMP_SLT_S = FOP(12, FMT_W), + R6_OPC_CMP_SULT_S = FOP(13, FMT_W), + R6_OPC_CMP_SLE_S = FOP(14, FMT_W), + R6_OPC_CMP_SULE_S = FOP(15, FMT_W), + R6_OPC_CMP_OR_S = FOP(17, FMT_W), + R6_OPC_CMP_UNE_S = FOP(18, FMT_W), + R6_OPC_CMP_NE_S = FOP(19, FMT_W), + R6_OPC_CMP_SOR_S = FOP(25, FMT_W), + R6_OPC_CMP_SUNE_S = FOP(26, FMT_W), + R6_OPC_CMP_SNE_S = FOP(27, FMT_W), + + R6_OPC_CMP_AF_D = FOP(0, FMT_L), + R6_OPC_CMP_UN_D = FOP(1, FMT_L), + R6_OPC_CMP_EQ_D = FOP(2, FMT_L), + R6_OPC_CMP_UEQ_D = FOP(3, FMT_L), + R6_OPC_CMP_LT_D = FOP(4, FMT_L), + R6_OPC_CMP_ULT_D = FOP(5, FMT_L), + R6_OPC_CMP_LE_D = FOP(6, FMT_L), + R6_OPC_CMP_ULE_D = FOP(7, FMT_L), + R6_OPC_CMP_SAF_D = FOP(8, FMT_L), + R6_OPC_CMP_SUN_D = FOP(9, FMT_L), + R6_OPC_CMP_SEQ_D = FOP(10, FMT_L), + R6_OPC_CMP_SEUQ_D = FOP(11, FMT_L), + R6_OPC_CMP_SLT_D = FOP(12, FMT_L), + R6_OPC_CMP_SULT_D = FOP(13, FMT_L), + R6_OPC_CMP_SLE_D = FOP(14, FMT_L), + R6_OPC_CMP_SULE_D = FOP(15, FMT_L), + R6_OPC_CMP_OR_D = FOP(17, FMT_L), + R6_OPC_CMP_UNE_D = FOP(18, FMT_L), + R6_OPC_CMP_NE_D = FOP(19, FMT_L), + R6_OPC_CMP_SOR_D = FOP(25, FMT_L), + R6_OPC_CMP_SUNE_D = FOP(26, FMT_L), + R6_OPC_CMP_SNE_D = FOP(27, FMT_L), +}; static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) { const char *opn = "cp1 move"; @@ -7463,6 +8072,79 @@ static inline void gen_movcf_ps(DisasContext *ctx, int fs, int fd, gen_set_label(l2); } +static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft, + int fs) +{ + TCGv_i32 t1 = tcg_const_i32(0); + TCGv_i32 fp0 = tcg_temp_new_i32(); + TCGv_i32 fp1 = tcg_temp_new_i32(); + TCGv_i32 fp2 = tcg_temp_new_i32(); + gen_load_fpr32(fp0, fd); + gen_load_fpr32(fp1, ft); + gen_load_fpr32(fp2, fs); + + switch (op1) { + case OPC_SEL_S: + tcg_gen_andi_i32(fp0, fp0, 1); + tcg_gen_movcond_i32(TCG_COND_NE, fp0, fp0, t1, fp1, fp2); + break; + case OPC_SELEQZ_S: + tcg_gen_andi_i32(fp1, fp1, 1); + tcg_gen_movcond_i32(TCG_COND_EQ, fp0, fp1, t1, fp2, t1); + break; + case OPC_SELNEZ_S: + tcg_gen_andi_i32(fp1, fp1, 1); + tcg_gen_movcond_i32(TCG_COND_NE, fp0, fp1, t1, fp2, t1); + break; + default: + MIPS_INVAL("gen_sel_s"); + generate_exception (ctx, EXCP_RI); + break; + } + + gen_store_fpr32(fp0, fd); + tcg_temp_free_i32(fp2); + tcg_temp_free_i32(fp1); + tcg_temp_free_i32(fp0); + tcg_temp_free_i32(t1); +} + +static void gen_sel_d(DisasContext *ctx, enum fopcode op1, int fd, int ft, + int fs) +{ + TCGv_i64 t1 = tcg_const_i64(0); + TCGv_i64 fp0 = tcg_temp_new_i64(); + TCGv_i64 fp1 = tcg_temp_new_i64(); + TCGv_i64 fp2 = tcg_temp_new_i64(); + gen_load_fpr64(ctx, fp0, fd); + gen_load_fpr64(ctx, fp1, ft); + gen_load_fpr64(ctx, fp2, fs); + + switch (op1) { + case OPC_SEL_D: + tcg_gen_andi_i64(fp0, fp0, 1); + tcg_gen_movcond_i64(TCG_COND_NE, fp0, fp0, t1, fp1, fp2); + break; + case OPC_SELEQZ_D: + tcg_gen_andi_i64(fp1, fp1, 1); + tcg_gen_movcond_i64(TCG_COND_EQ, fp0, fp1, t1, fp2, t1); + break; + case OPC_SELNEZ_D: + tcg_gen_andi_i64(fp1, fp1, 1); + tcg_gen_movcond_i64(TCG_COND_NE, fp0, fp1, t1, fp2, t1); + break; + default: + MIPS_INVAL("gen_sel_d"); + generate_exception (ctx, EXCP_RI); + break; + } + + gen_store_fpr64(ctx, fp0, fd); + tcg_temp_free_i64(fp2); + tcg_temp_free_i64(fp1); + tcg_temp_free_i64(fp0); + tcg_temp_free_i64(t1); +} static void gen_farith (DisasContext *ctx, enum fopcode op1, int ft, int fs, int fd, int cc) @@ -7711,11 +8393,28 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, } opn = "floor.w.s"; break; + case OPC_SEL_S: + check_insn(ctx, ISA_MIPS32R6); + gen_sel_s(ctx, op1, fd, ft, fs); + opn = "sel.s"; + break; + case OPC_SELEQZ_S: + check_insn(ctx, ISA_MIPS32R6); + gen_sel_s(ctx, op1, fd, ft, fs); + opn = "seleqz.s"; + break; + case OPC_SELNEZ_S: + check_insn(ctx, ISA_MIPS32R6); + gen_sel_s(ctx, op1, fd, ft, fs); + opn = "selnez.s"; + break; case OPC_MOVCF_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_movcf_s(fs, fd, (ft >> 2) & 0x7, ft & 0x1); opn = "movcf.s"; break; case OPC_MOVZ_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); { int l1 = gen_new_label(); TCGv_i32 fp0; @@ -7732,6 +8431,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "movz.s"; break; case OPC_MOVN_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); { int l1 = gen_new_label(); TCGv_i32 fp0; @@ -7771,59 +8471,175 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, } opn = "rsqrt.s"; break; - case OPC_RECIP2_S: - check_cp1_64bitmode(ctx); + case OPC_MADDF_S: + check_insn(ctx, ISA_MIPS32R6); { TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - + TCGv_i32 fp2 = tcg_temp_new_i32(); gen_load_fpr32(fp0, fs); gen_load_fpr32(fp1, ft); - gen_helper_float_recip2_s(fp0, cpu_env, fp0, fp1); + gen_load_fpr32(fp2, fd); + gen_helper_float_maddf_s(fp2, cpu_env, fp0, fp1, fp2); + gen_store_fpr32(fp2, fd); + tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp0, fd); tcg_temp_free_i32(fp0); + opn = "maddf.s"; } - opn = "recip2.s"; break; - case OPC_RECIP1_S: - check_cp1_64bitmode(ctx); + case OPC_MSUBF_S: + check_insn(ctx, ISA_MIPS32R6); { TCGv_i32 fp0 = tcg_temp_new_i32(); - + TCGv_i32 fp1 = tcg_temp_new_i32(); + TCGv_i32 fp2 = tcg_temp_new_i32(); gen_load_fpr32(fp0, fs); - gen_helper_float_recip1_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_load_fpr32(fp1, ft); + gen_load_fpr32(fp2, fd); + gen_helper_float_msubf_s(fp2, cpu_env, fp0, fp1, fp2); + gen_store_fpr32(fp2, fd); + tcg_temp_free_i32(fp2); + tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); + opn = "msubf.s"; } - opn = "recip1.s"; break; - case OPC_RSQRT1_S: - check_cp1_64bitmode(ctx); + case OPC_RINT_S: + check_insn(ctx, ISA_MIPS32R6); { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_helper_float_rsqrt1_s(fp0, cpu_env, fp0); + gen_helper_float_rint_s(fp0, cpu_env, fp0); gen_store_fpr32(fp0, fd); tcg_temp_free_i32(fp0); + opn = "rint.s"; } - opn = "rsqrt1.s"; break; - case OPC_RSQRT2_S: - check_cp1_64bitmode(ctx); + case OPC_CLASS_S: + check_insn(ctx, ISA_MIPS32R6); { TCGv_i32 fp0 = tcg_temp_new_i32(); + gen_load_fpr32(fp0, fs); + gen_helper_float_class_s(fp0, fp0); + gen_store_fpr32(fp0, fd); + tcg_temp_free_i32(fp0); + opn = "class.s"; + } + break; + case OPC_MIN_S: /* OPC_RECIP2_S */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_MIN_S */ + TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); + TCGv_i32 fp2 = tcg_temp_new_i32(); + gen_load_fpr32(fp0, fs); + gen_load_fpr32(fp1, ft); + gen_helper_float_min_s(fp2, cpu_env, fp0, fp1); + gen_store_fpr32(fp2, fd); + tcg_temp_free_i32(fp2); + tcg_temp_free_i32(fp1); + tcg_temp_free_i32(fp0); + opn = "min.s"; + } else { + /* OPC_RECIP2_S */ + check_cp1_64bitmode(ctx); + { + TCGv_i32 fp0 = tcg_temp_new_i32(); + TCGv_i32 fp1 = tcg_temp_new_i32(); + gen_load_fpr32(fp0, fs); + gen_load_fpr32(fp1, ft); + gen_helper_float_recip2_s(fp0, cpu_env, fp0, fp1); + tcg_temp_free_i32(fp1); + gen_store_fpr32(fp0, fd); + tcg_temp_free_i32(fp0); + } + opn = "recip2.s"; + } + break; + case OPC_MINA_S: /* OPC_RECIP1_S */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_MINA_S */ + TCGv_i32 fp0 = tcg_temp_new_i32(); + TCGv_i32 fp1 = tcg_temp_new_i32(); + TCGv_i32 fp2 = tcg_temp_new_i32(); gen_load_fpr32(fp0, fs); gen_load_fpr32(fp1, ft); - gen_helper_float_rsqrt2_s(fp0, cpu_env, fp0, fp1); + gen_helper_float_mina_s(fp2, cpu_env, fp0, fp1); + gen_store_fpr32(fp2, fd); + tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp0, fd); tcg_temp_free_i32(fp0); + opn = "mina.s"; + } else { + /* OPC_RECIP1_S */ + check_cp1_64bitmode(ctx); + { + TCGv_i32 fp0 = tcg_temp_new_i32(); + + gen_load_fpr32(fp0, fs); + gen_helper_float_recip1_s(fp0, cpu_env, fp0); + gen_store_fpr32(fp0, fd); + tcg_temp_free_i32(fp0); + } + opn = "recip1.s"; + } + break; + case OPC_MAX_S: /* OPC_RSQRT1_S */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_MAX_S */ + TCGv_i32 fp0 = tcg_temp_new_i32(); + TCGv_i32 fp1 = tcg_temp_new_i32(); + gen_load_fpr32(fp0, fs); + gen_load_fpr32(fp1, ft); + gen_helper_float_max_s(fp1, cpu_env, fp0, fp1); + gen_store_fpr32(fp1, fd); + tcg_temp_free_i32(fp1); + tcg_temp_free_i32(fp0); + opn = "max.s"; + } else { + /* OPC_RSQRT1_S */ + check_cp1_64bitmode(ctx); + { + TCGv_i32 fp0 = tcg_temp_new_i32(); + + gen_load_fpr32(fp0, fs); + gen_helper_float_rsqrt1_s(fp0, cpu_env, fp0); + gen_store_fpr32(fp0, fd); + tcg_temp_free_i32(fp0); + } + opn = "rsqrt1.s"; + } + break; + case OPC_MAXA_S: /* OPC_RSQRT2_S */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_MAXA_S */ + TCGv_i32 fp0 = tcg_temp_new_i32(); + TCGv_i32 fp1 = tcg_temp_new_i32(); + gen_load_fpr32(fp0, fs); + gen_load_fpr32(fp1, ft); + gen_helper_float_maxa_s(fp1, cpu_env, fp0, fp1); + gen_store_fpr32(fp1, fd); + tcg_temp_free_i32(fp1); + tcg_temp_free_i32(fp0); + opn = "maxa.s"; + } else { + /* OPC_RSQRT2_S */ + check_cp1_64bitmode(ctx); + { + TCGv_i32 fp0 = tcg_temp_new_i32(); + TCGv_i32 fp1 = tcg_temp_new_i32(); + + gen_load_fpr32(fp0, fs); + gen_load_fpr32(fp1, ft); + gen_helper_float_rsqrt2_s(fp0, cpu_env, fp0, fp1); + tcg_temp_free_i32(fp1); + gen_store_fpr32(fp0, fd); + tcg_temp_free_i32(fp0); + } + opn = "rsqrt2.s"; } - opn = "rsqrt2.s"; break; case OPC_CVT_D_S: check_cp1_registers(ctx, fd); @@ -7865,6 +8681,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "cvt.l.s"; break; case OPC_CVT_PS_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); check_cp1_64bitmode(ctx); { TCGv_i64 fp64 = tcg_temp_new_i64(); @@ -7897,6 +8714,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, case OPC_CMP_NGE_S: case OPC_CMP_LE_S: case OPC_CMP_NGT_S: + check_insn_opc_removed(ctx, ISA_MIPS32R6); if (ctx->opcode & (1 << 6)) { gen_cmpabs_s(ctx, func-48, ft, fs, cc); opn = condnames_abs[func-48]; @@ -8120,11 +8938,28 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, } opn = "floor.w.d"; break; + case OPC_SEL_D: + check_insn(ctx, ISA_MIPS32R6); + gen_sel_d(ctx, op1, fd, ft, fs); + opn = "sel.d"; + break; + case OPC_SELEQZ_D: + check_insn(ctx, ISA_MIPS32R6); + gen_sel_d(ctx, op1, fd, ft, fs); + opn = "seleqz.d"; + break; + case OPC_SELNEZ_D: + check_insn(ctx, ISA_MIPS32R6); + gen_sel_d(ctx, op1, fd, ft, fs); + opn = "selnez.d"; + break; case OPC_MOVCF_D: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_movcf_d(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); opn = "movcf.d"; break; case OPC_MOVZ_D: + check_insn_opc_removed(ctx, ISA_MIPS32R6); { int l1 = gen_new_label(); TCGv_i64 fp0; @@ -8141,6 +8976,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "movz.d"; break; case OPC_MOVN_D: + check_insn_opc_removed(ctx, ISA_MIPS32R6); { int l1 = gen_new_label(); TCGv_i64 fp0; @@ -8180,59 +9016,171 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, } opn = "rsqrt.d"; break; - case OPC_RECIP2_D: - check_cp1_64bitmode(ctx); + case OPC_MADDF_D: + check_insn(ctx, ISA_MIPS32R6); { TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); - + TCGv_i64 fp2 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_recip2_d(fp0, cpu_env, fp0, fp1); + gen_load_fpr64(ctx, fp2, fd); + gen_helper_float_maddf_d(fp2, cpu_env, fp0, fp1, fp2); + gen_store_fpr64(ctx, fp2, fd); + tcg_temp_free_i64(fp2); tcg_temp_free_i64(fp1); - gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); + opn = "maddf.d"; } - opn = "recip2.d"; break; - case OPC_RECIP1_D: - check_cp1_64bitmode(ctx); + case OPC_MSUBF_D: + check_insn(ctx, ISA_MIPS32R6); { TCGv_i64 fp0 = tcg_temp_new_i64(); - + TCGv_i64 fp1 = tcg_temp_new_i64(); + TCGv_i64 fp2 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_recip1_d(fp0, cpu_env, fp0); - gen_store_fpr64(ctx, fp0, fd); + gen_load_fpr64(ctx, fp1, ft); + gen_load_fpr64(ctx, fp2, fd); + gen_helper_float_msubf_d(fp2, cpu_env, fp0, fp1, fp2); + gen_store_fpr64(ctx, fp2, fd); + tcg_temp_free_i64(fp2); + tcg_temp_free_i64(fp1); tcg_temp_free_i64(fp0); + opn = "msubf.d"; } - opn = "recip1.d"; break; - case OPC_RSQRT1_D: - check_cp1_64bitmode(ctx); + case OPC_RINT_D: + check_insn(ctx, ISA_MIPS32R6); { TCGv_i64 fp0 = tcg_temp_new_i64(); - gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_rsqrt1_d(fp0, cpu_env, fp0); + gen_helper_float_rint_d(fp0, cpu_env, fp0); gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); + opn = "rint.d"; } - opn = "rsqrt1.d"; break; - case OPC_RSQRT2_D: - check_cp1_64bitmode(ctx); + case OPC_CLASS_D: + check_insn(ctx, ISA_MIPS32R6); { TCGv_i64 fp0 = tcg_temp_new_i64(); + gen_load_fpr64(ctx, fp0, fs); + gen_helper_float_class_d(fp0, fp0); + gen_store_fpr64(ctx, fp0, fd); + tcg_temp_free_i64(fp0); + opn = "class.d"; + } + break; + case OPC_MIN_D: /* OPC_RECIP2_D */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_MIN_D */ + TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); + gen_load_fpr64(ctx, fp0, fs); + gen_load_fpr64(ctx, fp1, ft); + gen_helper_float_min_d(fp1, cpu_env, fp0, fp1); + gen_store_fpr64(ctx, fp1, fd); + tcg_temp_free_i64(fp1); + tcg_temp_free_i64(fp0); + opn = "min.d"; + } else { + /* OPC_RECIP2_D */ + check_cp1_64bitmode(ctx); + { + TCGv_i64 fp0 = tcg_temp_new_i64(); + TCGv_i64 fp1 = tcg_temp_new_i64(); + gen_load_fpr64(ctx, fp0, fs); + gen_load_fpr64(ctx, fp1, ft); + gen_helper_float_recip2_d(fp0, cpu_env, fp0, fp1); + tcg_temp_free_i64(fp1); + gen_store_fpr64(ctx, fp0, fd); + tcg_temp_free_i64(fp0); + } + opn = "recip2.d"; + } + break; + case OPC_MINA_D: /* OPC_RECIP1_D */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_MINA_D */ + TCGv_i64 fp0 = tcg_temp_new_i64(); + TCGv_i64 fp1 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_rsqrt2_d(fp0, cpu_env, fp0, fp1); + gen_helper_float_mina_d(fp1, cpu_env, fp0, fp1); + gen_store_fpr64(ctx, fp1, fd); + tcg_temp_free_i64(fp1); + tcg_temp_free_i64(fp0); + opn = "mina.d"; + } else { + /* OPC_RECIP1_D */ + check_cp1_64bitmode(ctx); + { + TCGv_i64 fp0 = tcg_temp_new_i64(); + + gen_load_fpr64(ctx, fp0, fs); + gen_helper_float_recip1_d(fp0, cpu_env, fp0); + gen_store_fpr64(ctx, fp0, fd); + tcg_temp_free_i64(fp0); + } + opn = "recip1.d"; + } + break; + case OPC_MAX_D: /* OPC_RSQRT1_D */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_MAX_D */ + TCGv_i64 fp0 = tcg_temp_new_i64(); + TCGv_i64 fp1 = tcg_temp_new_i64(); + gen_load_fpr64(ctx, fp0, fs); + gen_load_fpr64(ctx, fp1, ft); + gen_helper_float_max_d(fp1, cpu_env, fp0, fp1); + gen_store_fpr64(ctx, fp1, fd); + tcg_temp_free_i64(fp1); + tcg_temp_free_i64(fp0); + opn = "max.d"; + } else { + /* OPC_RSQRT1_D */ + check_cp1_64bitmode(ctx); + { + TCGv_i64 fp0 = tcg_temp_new_i64(); + + gen_load_fpr64(ctx, fp0, fs); + gen_helper_float_rsqrt1_d(fp0, cpu_env, fp0); + gen_store_fpr64(ctx, fp0, fd); + tcg_temp_free_i64(fp0); + } + opn = "rsqrt1.d"; + } + break; + case OPC_MAXA_D: /* OPC_RSQRT2_D */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_MAXA_D */ + TCGv_i64 fp0 = tcg_temp_new_i64(); + TCGv_i64 fp1 = tcg_temp_new_i64(); + gen_load_fpr64(ctx, fp0, fs); + gen_load_fpr64(ctx, fp1, ft); + gen_helper_float_maxa_d(fp1, cpu_env, fp0, fp1); + gen_store_fpr64(ctx, fp1, fd); tcg_temp_free_i64(fp1); - gen_store_fpr64(ctx, fp0, fd); tcg_temp_free_i64(fp0); + opn = "maxa.d"; + } else { + /* OPC_RSQRT2_D */ + check_cp1_64bitmode(ctx); + { + TCGv_i64 fp0 = tcg_temp_new_i64(); + TCGv_i64 fp1 = tcg_temp_new_i64(); + + gen_load_fpr64(ctx, fp0, fs); + gen_load_fpr64(ctx, fp1, ft); + gen_helper_float_rsqrt2_d(fp0, cpu_env, fp0, fp1); + tcg_temp_free_i64(fp1); + gen_store_fpr64(ctx, fp0, fd); + tcg_temp_free_i64(fp0); + } + opn = "rsqrt2.d"; } - opn = "rsqrt2.d"; break; case OPC_CMP_F_D: case OPC_CMP_UN_D: @@ -8250,6 +9198,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, case OPC_CMP_NGE_D: case OPC_CMP_LE_D: case OPC_CMP_NGT_D: + check_insn_opc_removed(ctx, ISA_MIPS32R6); if (ctx->opcode & (1 << 6)) { gen_cmpabs_d(ctx, func-48, ft, fs, cc); opn = condnames_abs[func-48]; @@ -8350,6 +9299,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, opn = "cvt.d.l"; break; case OPC_CVT_PS_PW: + check_insn_opc_removed(ctx, ISA_MIPS32R6); check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -9128,7 +10078,7 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd) tcg_temp_free(t0); } -static void handle_delay_slot(DisasContext *ctx, int insn_bytes) +static void gen_branch(DisasContext *ctx, int insn_bytes) { if (ctx->hflags & MIPS_HFLAG_BMASK) { int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK; @@ -9671,15 +10621,15 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) gen_addiupc(ctx, rx, imm, 0, 1); break; case M16_OPC_B: - gen_compute_branch(ctx, OPC_BEQ, 4, 0, 0, offset << 1); + gen_compute_branch(ctx, OPC_BEQ, 4, 0, 0, offset << 1, 0); /* No delay slot, so just process as a normal instruction */ break; case M16_OPC_BEQZ: - gen_compute_branch(ctx, OPC_BEQ, 4, rx, 0, offset << 1); + gen_compute_branch(ctx, OPC_BEQ, 4, rx, 0, offset << 1, 0); /* No delay slot, so just process as a normal instruction */ break; case M16_OPC_BNEQZ: - gen_compute_branch(ctx, OPC_BNE, 4, rx, 0, offset << 1); + gen_compute_branch(ctx, OPC_BNE, 4, rx, 0, offset << 1, 0); /* No delay slot, so just process as a normal instruction */ break; case M16_OPC_SHIFT: @@ -9737,10 +10687,10 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) case M16_OPC_I8: switch (funct) { case I8_BTEQZ: - gen_compute_branch(ctx, OPC_BEQ, 4, 24, 0, offset << 1); + gen_compute_branch(ctx, OPC_BEQ, 4, 24, 0, offset << 1, 0); break; case I8_BTNEZ: - gen_compute_branch(ctx, OPC_BNE, 4, 24, 0, offset << 1); + gen_compute_branch(ctx, OPC_BNE, 4, 24, 0, offset << 1, 0); break; case I8_SWRASP: gen_st(ctx, OPC_SW, 31, 29, imm); @@ -9868,7 +10818,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) case M16_OPC_B: offset = (ctx->opcode & 0x7ff) << 1; offset = (int16_t)(offset << 4) >> 4; - gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, offset); + gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, offset, 0); /* No delay slot, so just process as a normal instruction */ break; case M16_OPC_JAL: @@ -9876,16 +10826,18 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) offset = (((ctx->opcode & 0x1f) << 21) | ((ctx->opcode >> 5) & 0x1f) << 16 | offset) << 2; - op = ((ctx->opcode >> 10) & 0x1) ? OPC_JALXS : OPC_JALS; - gen_compute_branch(ctx, op, 4, rx, ry, offset); + op = ((ctx->opcode >> 10) & 0x1) ? OPC_JALX : OPC_JAL; + gen_compute_branch(ctx, op, 4, rx, ry, offset, 2); n_bytes = 4; break; case M16_OPC_BEQZ: - gen_compute_branch(ctx, OPC_BEQ, 2, rx, 0, ((int8_t)ctx->opcode) << 1); + gen_compute_branch(ctx, OPC_BEQ, 2, rx, 0, + ((int8_t)ctx->opcode) << 1, 0); /* No delay slot, so just process as a normal instruction */ break; case M16_OPC_BNEQZ: - gen_compute_branch(ctx, OPC_BNE, 2, rx, 0, ((int8_t)ctx->opcode) << 1); + gen_compute_branch(ctx, OPC_BNE, 2, rx, 0, + ((int8_t)ctx->opcode) << 1, 0); /* No delay slot, so just process as a normal instruction */ break; case M16_OPC_SHIFT: @@ -9958,11 +10910,11 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) switch (funct) { case I8_BTEQZ: gen_compute_branch(ctx, OPC_BEQ, 2, 24, 0, - ((int8_t)ctx->opcode) << 1); + ((int8_t)ctx->opcode) << 1, 0); break; case I8_BTNEZ: gen_compute_branch(ctx, OPC_BNE, 2, 24, 0, - ((int8_t)ctx->opcode) << 1); + ((int8_t)ctx->opcode) << 1, 0); break; case I8_SWRASP: gen_st(ctx, OPC_SW, 31, 29, (ctx->opcode & 0xff) << 2); @@ -10111,12 +11063,13 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) int ra = (ctx->opcode >> 5) & 0x1; if (link) { - op = nd ? OPC_JALRC : OPC_JALRS; + op = OPC_JALR; } else { op = OPC_JR; } - gen_compute_branch(ctx, op, 2, ra ? 31 : rx, 31, 0); + gen_compute_branch(ctx, op, 2, ra ? 31 : rx, 31, 0, + (nd ? 0 : 2)); } break; case RR_SDBBP: @@ -10874,7 +11827,6 @@ static void gen_pool16c_insn(DisasContext *ctx) { int rd = mmreg((ctx->opcode >> 3) & 0x7); int rs = mmreg(ctx->opcode & 0x7); - int opc; switch (((ctx->opcode) >> 4) & 0x3f) { case NOT16 + 0: @@ -10930,32 +11882,27 @@ static void gen_pool16c_insn(DisasContext *ctx) { int reg = ctx->opcode & 0x1f; - gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0); + gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0, 4); } break; case JRC16 + 0: case JRC16 + 1: { int reg = ctx->opcode & 0x1f; - - gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0); + gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0, 0); /* Let normal delay slot handling in our caller take us to the branch target. */ } break; case JALR16 + 0: case JALR16 + 1: - opc = OPC_JALR; - goto do_jalr; + gen_compute_branch(ctx, OPC_JALR, 2, ctx->opcode & 0x1f, 31, 0, 4); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + break; case JALR16S + 0: case JALR16S + 1: - opc = OPC_JALRS; - do_jalr: - { - int reg = ctx->opcode & 0x1f; - - gen_compute_branch(ctx, opc, 2, reg, 31, 0); - } + gen_compute_branch(ctx, OPC_JALR, 2, ctx->opcode & 0x1f, 31, 0, 2); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case MFHI16 + 0: case MFHI16 + 1: @@ -10983,8 +11930,7 @@ static void gen_pool16c_insn(DisasContext *ctx) case JRADDIUSP + 1: { int imm = ZIMM(ctx->opcode, 0, 5); - - gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0); + gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0, 0); gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2); /* Let normal delay slot handling in our caller take us to the branch target. */ @@ -11241,11 +12187,13 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) switch (minor) { case JALR: case JALR_HB: - gen_compute_branch (ctx, OPC_JALR, 4, rs, rt, 0); + gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 4); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case JALRS: case JALRS_HB: - gen_compute_branch (ctx, OPC_JALRS, 4, rs, rt, 0); + gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 2); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; default: goto pool32axf_invalid; @@ -12135,30 +13083,32 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, minor = (ctx->opcode >> 21) & 0x1f; switch (minor) { case BLTZ: - mips32_op = OPC_BLTZ; - goto do_branch; + gen_compute_branch(ctx, OPC_BLTZ, 4, rs, -1, imm << 1, 4); + break; case BLTZAL: - mips32_op = OPC_BLTZAL; - goto do_branch; + gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 4); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + break; case BLTZALS: - mips32_op = OPC_BLTZALS; - goto do_branch; + gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 2); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + break; case BGEZ: - mips32_op = OPC_BGEZ; - goto do_branch; + gen_compute_branch(ctx, OPC_BGEZ, 4, rs, -1, imm << 1, 4); + break; case BGEZAL: - mips32_op = OPC_BGEZAL; - goto do_branch; + gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 4); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + break; case BGEZALS: - mips32_op = OPC_BGEZALS; - goto do_branch; + gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 2); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; + break; case BLEZ: - mips32_op = OPC_BLEZ; - goto do_branch; + gen_compute_branch(ctx, OPC_BLEZ, 4, rs, -1, imm << 1, 4); + break; case BGTZ: - mips32_op = OPC_BGTZ; - do_branch: - gen_compute_branch(ctx, mips32_op, 4, rs, -1, imm << 1); + gen_compute_branch(ctx, OPC_BGTZ, 4, rs, -1, imm << 1, 4); break; /* Traps */ @@ -12186,7 +13136,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case BNEZC: case BEQZC: gen_compute_branch(ctx, minor == BNEZC ? OPC_BNE : OPC_BEQ, - 4, rs, 0, imm << 1); + 4, rs, 0, imm << 1, 0); /* Compact branches don't have a delay slot, so just let the normal delay slot handling take us to the branch target. */ @@ -12195,6 +13145,9 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, gen_logic_imm(ctx, OPC_LUI, rs, -1, imm); break; case SYNCI: + /* Break the TB to be able to sync copied instructions + immediately */ + ctx->bstate = BS_STOP; break; case BC2F: case BC2T: @@ -12324,25 +13277,28 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, break; case JALX32: offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; - gen_compute_branch(ctx, OPC_JALX, 4, rt, rs, offset); + gen_compute_branch(ctx, OPC_JALX, 4, rt, rs, offset, 4); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case JALS32: offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 1; - gen_compute_branch(ctx, OPC_JALS, 4, rt, rs, offset); + gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, offset, 2); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case BEQ32: - gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1); + gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1, 4); break; case BNE32: - gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1); + gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1, 4); break; case J32: gen_compute_branch(ctx, OPC_J, 4, rt, rs, - (int32_t)(ctx->opcode & 0x3FFFFFF) << 1); + (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); break; case JAL32: gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, - (int32_t)(ctx->opcode & 0x3FFFFFF) << 1); + (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); + ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; /* Floating point (COP1) */ case LWC132: @@ -12426,84 +13382,41 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) op = (ctx->opcode >> 10) & 0x3f; /* Enforce properly-sized instructions in a delay slot */ - if (ctx->hflags & MIPS_HFLAG_BMASK) { - int bits = ctx->hflags & MIPS_HFLAG_BMASK_EXT; - - switch (op) { - case POOL32A: - case POOL32B: - case POOL32I: - case POOL32C: - case ADDI32: - case ADDIU32: - case ORI32: - case XORI32: - case SLTI32: - case SLTIU32: - case ANDI32: - case JALX32: - case LBU32: - case LHU32: - case POOL32F: - case JALS32: - case BEQ32: - case BNE32: - case J32: - case JAL32: - case SB32: - case SH32: - case POOL32S: - case ADDIUPC: - case SWC132: - case SDC132: - case SD32: - case SW32: - case LB32: - case LH32: - case DADDIU32: - case LWC132: - case LDC132: - case LD32: - case LW32: - if (bits & MIPS_HFLAG_BDS16) { + if (ctx->hflags & MIPS_HFLAG_BDS_STRICT) { + switch (op & 0x7) { /* MSB-3..MSB-5 */ + case 0: + /* POOL32A, POOL32B, POOL32I, POOL32C */ + case 4: + /* ADDI32, ADDIU32, ORI32, XORI32, SLTI32, SLTIU32, ANDI32, JALX32 */ + case 5: + /* LBU32, LHU32, POOL32F, JALS32, BEQ32, BNE32, J32, JAL32 */ + case 6: + /* SB32, SH32, ADDIUPC, SWC132, SDC132, SW32 */ + case 7: + /* LB32, LH32, LWC132, LDC132, LW32 */ + if (ctx->hflags & MIPS_HFLAG_BDS16) { generate_exception(ctx, EXCP_RI); /* Just stop translation; the user is confused. */ ctx->bstate = BS_STOP; return 2; } break; - case POOL16A: - case POOL16B: - case POOL16C: - case LWGP16: - case POOL16F: - case LBU16: - case LHU16: - case LWSP16: - case LW16: - case SB16: - case SH16: - case SWSP16: - case SW16: - case MOVE16: - case ANDI16: - case POOL16D: - case POOL16E: - case BEQZ16: - case BNEZ16: - case B16: - case LI16: - if (bits & MIPS_HFLAG_BDS32) { + case 1: + /* POOL16A, POOL16B, POOL16C, LWGP16, POOL16F */ + case 2: + /* LBU16, LHU16, LWSP16, LW16, SB16, SH16, SWSP16, SW16 */ + case 3: + /* MOVE16, ANDI16, POOL16D, POOL16E, BEQZ16, BNEZ16, B16, LI16 */ + if (ctx->hflags & MIPS_HFLAG_BDS32) { generate_exception(ctx, EXCP_RI); /* Just stop translation; the user is confused. */ ctx->bstate = BS_STOP; return 2; } break; - default: - break; } } + switch (op) { case POOL16A: { @@ -12684,13 +13597,13 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) break; case B16: gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, - SIMM(ctx->opcode, 0, 10) << 1); + SIMM(ctx->opcode, 0, 10) << 1, 4); break; case BNEZ16: case BEQZ16: gen_compute_branch(ctx, op == BNEZ16 ? OPC_BNE : OPC_BEQ, 2, mmreg(uMIPS_RD(ctx->opcode)), - 0, SIMM(ctx->opcode, 0, 7) << 1); + 0, SIMM(ctx->opcode, 0, 7) << 1, 4); break; case LI16: { @@ -14447,905 +15360,1521 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, /* End MIPSDSP functions. */ -static void decode_opc (CPUMIPSState *env, DisasContext *ctx) +/* Compact Branches */ +static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, + int rs, int rt, int32_t offset) { - int32_t offset; - int rs, rt, rd, sa; - uint32_t op, op1, op2; - int16_t imm; + int bcond_compute = 0; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); - /* make sure instructions are on a word boundary */ - if (ctx->pc & 0x3) { - env->CP0_BadVAddr = ctx->pc; - generate_exception(ctx, EXCP_AdEL); - return; + if (ctx->hflags & MIPS_HFLAG_BMASK) { +#ifdef MIPS_DEBUG_DISAS + LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); +#endif + generate_exception(ctx, EXCP_RI); + goto out; } - /* Handle blikely not taken case */ - if ((ctx->hflags & MIPS_HFLAG_BMASK_BASE) == MIPS_HFLAG_BL) { - int l1 = gen_new_label(); - - MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4); - tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); - tcg_gen_movi_i32(hflags, ctx->hflags & ~MIPS_HFLAG_BMASK); - gen_goto_tb(ctx, 1, ctx->pc + 4); - gen_set_label(l1); - } + /* Load needed operands and calculate btarget */ + switch (opc) { + /* compact branch */ + case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ + case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + if (rs <= rt && rs == 0) { + /* OPC_BEQZALC, OPC_BNEZALC */ + tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4); + } + break; + case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ + case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + break; + case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ + case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ + if (rs == 0 || rs == rt) { + /* OPC_BLEZALC, OPC_BGEZALC */ + /* OPC_BGTZALC, OPC_BLTZALC */ + tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4); + } + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + break; + case OPC_BC: + case OPC_BALC: + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + break; + case OPC_BEQZC: + case OPC_BNEZC: + if (rs != 0) { + /* OPC_BEQZC, OPC_BNEZC */ + gen_load_gpr(t0, rs); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + } else { + /* OPC_JIC, OPC_JIALC */ + TCGv tbase = tcg_temp_new(); + TCGv toffset = tcg_temp_new(); - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { - tcg_gen_debug_insn_start(ctx->pc); + gen_load_gpr(tbase, rt); + tcg_gen_movi_tl(toffset, offset); + gen_op_addr_add(ctx, btarget, tbase, toffset); + tcg_temp_free(tbase); + tcg_temp_free(toffset); + } + break; + default: + MIPS_INVAL("Compact branch/jump"); + generate_exception(ctx, EXCP_RI); + goto out; } - op = MASK_OP_MAJOR(ctx->opcode); - rs = (ctx->opcode >> 21) & 0x1f; - rt = (ctx->opcode >> 16) & 0x1f; - rd = (ctx->opcode >> 11) & 0x1f; - sa = (ctx->opcode >> 6) & 0x1f; - imm = (int16_t)ctx->opcode; - switch (op) { - case OPC_SPECIAL: - op1 = MASK_SPECIAL(ctx->opcode); - switch (op1) { - case OPC_SLL: /* Shift with immediate */ - case OPC_SRA: - gen_shift_imm(ctx, op1, rd, rt, sa); + if (bcond_compute == 0) { + /* Uncoditional compact branch */ + switch (opc) { + case OPC_JIALC: + tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4); + /* Fallthrough */ + case OPC_JIC: + ctx->hflags |= MIPS_HFLAG_BR; break; - case OPC_SRL: - switch ((ctx->opcode >> 21) & 0x1f) { - case 1: - /* rotr is decoded as srl on non-R2 CPUs */ - if (ctx->insn_flags & ISA_MIPS32R2) { - op1 = OPC_ROTR; - } - /* Fallthrough */ - case 0: - gen_shift_imm(ctx, op1, rd, rt, sa); - break; - default: - generate_exception(ctx, EXCP_RI); - break; - } + case OPC_BALC: + tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4); + /* Fallthrough */ + case OPC_BC: + ctx->hflags |= MIPS_HFLAG_B; break; - case OPC_MOVN: /* Conditional move */ - case OPC_MOVZ: - check_insn(ctx, ISA_MIPS4 | ISA_MIPS32 | - INSN_LOONGSON2E | INSN_LOONGSON2F); - gen_cond_move(ctx, op1, rd, rs, rt); + default: + MIPS_INVAL("Compact branch/jump"); + generate_exception(ctx, EXCP_RI); + goto out; + } + + /* Generating branch here as compact branches don't have delay slot */ + gen_branch(ctx, 4); + } else { + /* Conditional compact branch */ + int l1 = gen_new_label(); + save_cpu_state(ctx, 0); + + switch (opc) { + case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ + if (rs == 0 && rt != 0) { + /* OPC_BLEZALC */ + tcg_gen_brcondi_tl(TCG_COND_LE, t1, 0, l1); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BGEZALC */ + tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); + } else { + /* OPC_BGEUC */ + tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1); + } break; - case OPC_ADD ... OPC_SUBU: - gen_arith(ctx, op1, rd, rs, rt); + case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ + if (rs == 0 && rt != 0) { + /* OPC_BGTZALC */ + tcg_gen_brcondi_tl(TCG_COND_GT, t1, 0, l1); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BLTZALC */ + tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1); + } else { + /* OPC_BLTUC */ + tcg_gen_brcond_tl(TCG_COND_LTU, t0, t1, l1); + } break; - case OPC_SLLV: /* Shifts */ - case OPC_SRAV: - gen_shift(ctx, op1, rd, rs, rt); + case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ + if (rs == 0 && rt != 0) { + /* OPC_BLEZC */ + tcg_gen_brcondi_tl(TCG_COND_LE, t1, 0, l1); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BGEZC */ + tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); + } else { + /* OPC_BGEC */ + tcg_gen_brcond_tl(TCG_COND_GE, t0, t1, l1); + } break; - case OPC_SRLV: - switch ((ctx->opcode >> 6) & 0x1f) { - case 1: - /* rotrv is decoded as srlv on non-R2 CPUs */ - if (ctx->insn_flags & ISA_MIPS32R2) { - op1 = OPC_ROTRV; - } - /* Fallthrough */ - case 0: - gen_shift(ctx, op1, rd, rs, rt); - break; - default: - generate_exception(ctx, EXCP_RI); - break; + case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ + if (rs == 0 && rt != 0) { + /* OPC_BGTZC */ + tcg_gen_brcondi_tl(TCG_COND_GT, t1, 0, l1); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BLTZC */ + tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1); + } else { + /* OPC_BLTC */ + tcg_gen_brcond_tl(TCG_COND_LT, t0, t1, l1); } break; - case OPC_SLT: /* Set on less than */ - case OPC_SLTU: - gen_slt(ctx, op1, rd, rs, rt); - break; - case OPC_AND: /* Logic*/ - case OPC_OR: - case OPC_NOR: - case OPC_XOR: - gen_logic(ctx, op1, rd, rs, rt); - break; - case OPC_MULT: - case OPC_MULTU: - if (sa) { - check_insn(ctx, INSN_VR54XX); - op1 = MASK_MUL_VR54XX(ctx->opcode); - gen_mul_vr54xx(ctx, op1, rd, rs, rt); + case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ + case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ + if (rs >= rt) { + /* OPC_BOVC, OPC_BNVC */ + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + TCGv input_overflow = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_ext32s_tl(t2, t0); + tcg_gen_setcond_tl(TCG_COND_NE, input_overflow, t2, t0); + tcg_gen_ext32s_tl(t3, t1); + tcg_gen_setcond_tl(TCG_COND_NE, t4, t3, t1); + tcg_gen_or_tl(input_overflow, input_overflow, t4); + + tcg_gen_add_tl(t4, t2, t3); + tcg_gen_ext32s_tl(t4, t4); + tcg_gen_xor_tl(t2, t2, t3); + tcg_gen_xor_tl(t3, t4, t3); + tcg_gen_andc_tl(t2, t3, t2); + tcg_gen_setcondi_tl(TCG_COND_LT, t4, t2, 0); + tcg_gen_or_tl(t4, t4, input_overflow); + if (opc == OPC_BOVC) { + /* OPC_BOVC */ + tcg_gen_brcondi_tl(TCG_COND_NE, t4, 0, l1); + } else { + /* OPC_BNVC */ + tcg_gen_brcondi_tl(TCG_COND_EQ, t4, 0, l1); + } + tcg_temp_free(input_overflow); + tcg_temp_free(t4); + tcg_temp_free(t3); + tcg_temp_free(t2); + } else if (rs < rt && rs == 0) { + /* OPC_BEQZALC, OPC_BNEZALC */ + if (opc == OPC_BEQZALC) { + /* OPC_BEQZALC */ + tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); + } else { + /* OPC_BNEZALC */ + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + } } else { - gen_muldiv(ctx, op1, rd & 3, rs, rt); + /* OPC_BEQC, OPC_BNEC */ + if (opc == OPC_BEQC) { + /* OPC_BEQC */ + tcg_gen_brcond_tl(TCG_COND_EQ, t0, t1, l1); + } else { + /* OPC_BNEC */ + tcg_gen_brcond_tl(TCG_COND_NE, t0, t1, l1); + } } break; - case OPC_DIV: - case OPC_DIVU: - gen_muldiv(ctx, op1, 0, rs, rt); + case OPC_BEQZC: + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); break; - case OPC_JR ... OPC_JALR: - gen_compute_branch(ctx, op1, 4, rs, rd, sa); + case OPC_BNEZC: + tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0, l1); break; - case OPC_TGE ... OPC_TEQ: /* Traps */ - case OPC_TNE: - gen_trap(ctx, op1, rs, rt, -1); + default: + MIPS_INVAL("Compact conditional branch/jump"); + generate_exception(ctx, EXCP_RI); + goto out; + } + + /* Generating branch here as compact branches don't have delay slot */ + /* TODO: implement forbidden slot */ + gen_goto_tb(ctx, 1, ctx->pc + 4); + gen_set_label(l1); + gen_goto_tb(ctx, 0, ctx->btarget); + MIPS_DEBUG("Compact conditional branch"); + ctx->bstate = BS_BRANCH; + } + +out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) +{ + int rs, rt, rd, sa; + uint32_t op1, op2; + + rs = (ctx->opcode >> 21) & 0x1f; + rt = (ctx->opcode >> 16) & 0x1f; + rd = (ctx->opcode >> 11) & 0x1f; + sa = (ctx->opcode >> 6) & 0x1f; + + op1 = MASK_SPECIAL(ctx->opcode); + switch (op1) { + case OPC_LSA: + if (rd != 0) { + int imm2 = extract32(ctx->opcode, 6, 3); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_shli_tl(t0, t0, imm2 + 1); + tcg_gen_add_tl(t0, t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], t0); + tcg_temp_free(t1); + tcg_temp_free(t0); + } + break; + case OPC_MULT ... OPC_DIVU: + op2 = MASK_R6_MULDIV(ctx->opcode); + switch (op2) { + case R6_OPC_MUL: + case R6_OPC_MUH: + case R6_OPC_MULU: + case R6_OPC_MUHU: + case R6_OPC_DIV: + case R6_OPC_MOD: + case R6_OPC_DIVU: + case R6_OPC_MODU: + gen_r6_muldiv(ctx, op2, rd, rs, rt); break; - case OPC_MFHI: /* Move from HI/LO */ - case OPC_MFLO: - gen_HILO(ctx, op1, rs & 3, rd); + default: + MIPS_INVAL("special_r6 muldiv"); + generate_exception(ctx, EXCP_RI); break; - case OPC_MTHI: - case OPC_MTLO: /* Move to HI/LO */ - gen_HILO(ctx, op1, rd & 3, rs); + } + break; + case OPC_SELEQZ: + case OPC_SELNEZ: + gen_cond_move(ctx, op1, rd, rs, rt); + break; + case R6_OPC_CLO: + case R6_OPC_CLZ: + if (rt == 0 && sa == 1) { + /* Major opcode and function field is shared with preR6 MFHI/MTHI. + We need additionally to check other fields */ + gen_cl(ctx, op1, rd, rs); + } else { + generate_exception(ctx, EXCP_RI); + } + break; + case R6_OPC_SDBBP: + generate_exception(ctx, EXCP_DBp); + break; +#if defined(TARGET_MIPS64) + case OPC_DLSA: + check_mips_64(ctx); + if (rd != 0) { + int imm2 = extract32(ctx->opcode, 6, 3); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_shli_tl(t0, t0, imm2 + 1); + tcg_gen_add_tl(cpu_gpr[rd], t0, t1); + tcg_temp_free(t1); + tcg_temp_free(t0); + } + break; + case R6_OPC_DCLO: + case R6_OPC_DCLZ: + if (rt == 0 && sa == 1) { + /* Major opcode and function field is shared with preR6 MFHI/MTHI. + We need additionally to check other fields */ + check_mips_64(ctx); + gen_cl(ctx, op1, rd, rs); + } else { + generate_exception(ctx, EXCP_RI); + } + break; + case OPC_DMULT ... OPC_DDIVU: + op2 = MASK_R6_MULDIV(ctx->opcode); + switch (op2) { + case R6_OPC_DMUL: + case R6_OPC_DMUH: + case R6_OPC_DMULU: + case R6_OPC_DMUHU: + case R6_OPC_DDIV: + case R6_OPC_DMOD: + case R6_OPC_DDIVU: + case R6_OPC_DMODU: + check_mips_64(ctx); + gen_r6_muldiv(ctx, op2, rd, rs, rt); break; - case OPC_PMON: /* Pmon entry point, also R4010 selsl */ -#ifdef MIPS_STRICT_STANDARD - MIPS_INVAL("PMON / selsl"); + default: + MIPS_INVAL("special_r6 muldiv"); generate_exception(ctx, EXCP_RI); + break; + } + break; +#endif + default: /* Invalid */ + MIPS_INVAL("special_r6"); + generate_exception(ctx, EXCP_RI); + break; + } +} + +static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) +{ + int rs, rt, rd, sa; + uint32_t op1; + + rs = (ctx->opcode >> 21) & 0x1f; + rt = (ctx->opcode >> 16) & 0x1f; + rd = (ctx->opcode >> 11) & 0x1f; + sa = (ctx->opcode >> 6) & 0x1f; + + op1 = MASK_SPECIAL(ctx->opcode); + switch (op1) { + case OPC_MOVN: /* Conditional move */ + case OPC_MOVZ: + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32 | + INSN_LOONGSON2E | INSN_LOONGSON2F); + gen_cond_move(ctx, op1, rd, rs, rt); + break; + case OPC_MFHI: /* Move from HI/LO */ + case OPC_MFLO: + gen_HILO(ctx, op1, rs & 3, rd); + break; + case OPC_MTHI: + case OPC_MTLO: /* Move to HI/LO */ + gen_HILO(ctx, op1, rd & 3, rs); + break; + case OPC_MOVCI: + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); + if (env->CP0_Config1 & (1 << CP0C1_FP)) { + check_cp1_enabled(ctx); + gen_movci(ctx, rd, rs, (ctx->opcode >> 18) & 0x7, + (ctx->opcode >> 16) & 1); + } else { + generate_exception_err(ctx, EXCP_CpU, 1); + } + break; + case OPC_MULT: + case OPC_MULTU: + if (sa) { + check_insn(ctx, INSN_VR54XX); + op1 = MASK_MUL_VR54XX(ctx->opcode); + gen_mul_vr54xx(ctx, op1, rd, rs, rt); + } else { + gen_muldiv(ctx, op1, rd & 3, rs, rt); + } + break; + case OPC_DIV: + case OPC_DIVU: + gen_muldiv(ctx, op1, 0, rs, rt); + break; +#if defined(TARGET_MIPS64) + case OPC_DMULT ... OPC_DDIVU: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); + gen_muldiv(ctx, op1, 0, rs, rt); + break; +#endif + case OPC_JR: + gen_compute_branch(ctx, op1, 4, rs, rd, sa, 4); + break; + case OPC_SPIM: +#ifdef MIPS_STRICT_STANDARD + MIPS_INVAL("SPIM"); + generate_exception(ctx, EXCP_RI); #else - gen_helper_0e0i(pmon, sa); + /* Implemented as RI exception for now. */ + MIPS_INVAL("spim (unofficial)"); + generate_exception(ctx, EXCP_RI); #endif + break; + default: /* Invalid */ + MIPS_INVAL("special_legacy"); + generate_exception(ctx, EXCP_RI); + break; + } +} + +static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) +{ + int rs, rt, rd, sa; + uint32_t op1; + + rs = (ctx->opcode >> 21) & 0x1f; + rt = (ctx->opcode >> 16) & 0x1f; + rd = (ctx->opcode >> 11) & 0x1f; + sa = (ctx->opcode >> 6) & 0x1f; + + op1 = MASK_SPECIAL(ctx->opcode); + switch (op1) { + case OPC_SLL: /* Shift with immediate */ + case OPC_SRA: + gen_shift_imm(ctx, op1, rd, rt, sa); + break; + case OPC_SRL: + switch ((ctx->opcode >> 21) & 0x1f) { + case 1: + /* rotr is decoded as srl on non-R2 CPUs */ + if (ctx->insn_flags & ISA_MIPS32R2) { + op1 = OPC_ROTR; + } + /* Fallthrough */ + case 0: + gen_shift_imm(ctx, op1, rd, rt, sa); break; - case OPC_SYSCALL: - generate_exception(ctx, EXCP_SYSCALL); - ctx->bstate = BS_STOP; + default: + generate_exception(ctx, EXCP_RI); break; - case OPC_BREAK: - generate_exception(ctx, EXCP_BREAK); + } + break; + case OPC_ADD ... OPC_SUBU: + gen_arith(ctx, op1, rd, rs, rt); + break; + case OPC_SLLV: /* Shifts */ + case OPC_SRAV: + gen_shift(ctx, op1, rd, rs, rt); + break; + case OPC_SRLV: + switch ((ctx->opcode >> 6) & 0x1f) { + case 1: + /* rotrv is decoded as srlv on non-R2 CPUs */ + if (ctx->insn_flags & ISA_MIPS32R2) { + op1 = OPC_ROTRV; + } + /* Fallthrough */ + case 0: + gen_shift(ctx, op1, rd, rs, rt); + break; + default: + generate_exception(ctx, EXCP_RI); break; - case OPC_SPIM: + } + break; + case OPC_SLT: /* Set on less than */ + case OPC_SLTU: + gen_slt(ctx, op1, rd, rs, rt); + break; + case OPC_AND: /* Logic*/ + case OPC_OR: + case OPC_NOR: + case OPC_XOR: + gen_logic(ctx, op1, rd, rs, rt); + break; + case OPC_JALR: + gen_compute_branch(ctx, op1, 4, rs, rd, sa, 4); + break; + case OPC_TGE ... OPC_TEQ: /* Traps */ + case OPC_TNE: + gen_trap(ctx, op1, rs, rt, -1); + break; + case OPC_LSA: /* OPC_PMON */ + if (ctx->insn_flags & ISA_MIPS32R6) { + decode_opc_special_r6(env, ctx); + } else { + /* Pmon entry point, also R4010 selsl */ #ifdef MIPS_STRICT_STANDARD - MIPS_INVAL("SPIM"); + MIPS_INVAL("PMON / selsl"); generate_exception(ctx, EXCP_RI); #else - /* Implemented as RI exception for now. */ - MIPS_INVAL("spim (unofficial)"); - generate_exception(ctx, EXCP_RI); + gen_helper_0e0i(pmon, sa); #endif - break; - case OPC_SYNC: - /* Treat as NOP. */ - break; - - case OPC_MOVCI: - check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); - if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { - check_cp1_enabled(ctx); - gen_movci(ctx, rd, rs, (ctx->opcode >> 18) & 0x7, - (ctx->opcode >> 16) & 1); - } else { - generate_exception_err(ctx, EXCP_CpU, 1); - } - break; + } + break; + case OPC_SYSCALL: + generate_exception(ctx, EXCP_SYSCALL); + ctx->bstate = BS_STOP; + break; + case OPC_BREAK: + generate_exception(ctx, EXCP_BREAK); + break; + case OPC_SYNC: + /* Treat as NOP. */ + break; #if defined(TARGET_MIPS64) - /* MIPS64 specific opcodes */ - case OPC_DSLL: - case OPC_DSRA: - case OPC_DSLL32: - case OPC_DSRA32: + /* MIPS64 specific opcodes */ + case OPC_DSLL: + case OPC_DSRA: + case OPC_DSLL32: + case OPC_DSRA32: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); + gen_shift_imm(ctx, op1, rd, rt, sa); + break; + case OPC_DSRL: + switch ((ctx->opcode >> 21) & 0x1f) { + case 1: + /* drotr is decoded as dsrl on non-R2 CPUs */ + if (ctx->insn_flags & ISA_MIPS32R2) { + op1 = OPC_DROTR; + } + /* Fallthrough */ + case 0: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_shift_imm(ctx, op1, rd, rt, sa); break; - case OPC_DSRL: - switch ((ctx->opcode >> 21) & 0x1f) { - case 1: - /* drotr is decoded as dsrl on non-R2 CPUs */ - if (ctx->insn_flags & ISA_MIPS32R2) { - op1 = OPC_DROTR; - } - /* Fallthrough */ - case 0: - check_insn(ctx, ISA_MIPS3); - check_mips_64(ctx); - gen_shift_imm(ctx, op1, rd, rt, sa); - break; - default: - generate_exception(ctx, EXCP_RI); - break; - } + default: + generate_exception(ctx, EXCP_RI); break; - case OPC_DSRL32: - switch ((ctx->opcode >> 21) & 0x1f) { - case 1: - /* drotr32 is decoded as dsrl32 on non-R2 CPUs */ - if (ctx->insn_flags & ISA_MIPS32R2) { - op1 = OPC_DROTR32; - } - /* Fallthrough */ - case 0: - check_insn(ctx, ISA_MIPS3); - check_mips_64(ctx); - gen_shift_imm(ctx, op1, rd, rt, sa); - break; - default: - generate_exception(ctx, EXCP_RI); - break; + } + break; + case OPC_DSRL32: + switch ((ctx->opcode >> 21) & 0x1f) { + case 1: + /* drotr32 is decoded as dsrl32 on non-R2 CPUs */ + if (ctx->insn_flags & ISA_MIPS32R2) { + op1 = OPC_DROTR32; } - break; - case OPC_DADD ... OPC_DSUBU: + /* Fallthrough */ + case 0: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_arith(ctx, op1, rd, rs, rt); + gen_shift_imm(ctx, op1, rd, rt, sa); break; - case OPC_DSLLV: - case OPC_DSRAV: - check_insn(ctx, ISA_MIPS3); - check_mips_64(ctx); - gen_shift(ctx, op1, rd, rs, rt); + default: + generate_exception(ctx, EXCP_RI); break; - case OPC_DSRLV: - switch ((ctx->opcode >> 6) & 0x1f) { - case 1: - /* drotrv is decoded as dsrlv on non-R2 CPUs */ - if (ctx->insn_flags & ISA_MIPS32R2) { - op1 = OPC_DROTRV; - } - /* Fallthrough */ - case 0: - check_insn(ctx, ISA_MIPS3); - check_mips_64(ctx); - gen_shift(ctx, op1, rd, rs, rt); - break; - default: - generate_exception(ctx, EXCP_RI); - break; + } + break; + case OPC_DADD ... OPC_DSUBU: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); + gen_arith(ctx, op1, rd, rs, rt); + break; + case OPC_DSLLV: + case OPC_DSRAV: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); + gen_shift(ctx, op1, rd, rs, rt); + break; + case OPC_DSRLV: + switch ((ctx->opcode >> 6) & 0x1f) { + case 1: + /* drotrv is decoded as dsrlv on non-R2 CPUs */ + if (ctx->insn_flags & ISA_MIPS32R2) { + op1 = OPC_DROTRV; } - break; - case OPC_DMULT ... OPC_DDIVU: + /* Fallthrough */ + case 0: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_muldiv(ctx, op1, 0, rs, rt); + gen_shift(ctx, op1, rd, rs, rt); break; -#endif - default: /* Invalid */ - MIPS_INVAL("special"); + default: generate_exception(ctx, EXCP_RI); break; } break; - case OPC_SPECIAL2: - op1 = MASK_SPECIAL2(ctx->opcode); - switch (op1) { - case OPC_MADD ... OPC_MADDU: /* Multiply and add/sub */ - case OPC_MSUB ... OPC_MSUBU: - check_insn(ctx, ISA_MIPS32); - gen_muldiv(ctx, op1, rd & 3, rs, rt); - break; - case OPC_MUL: - gen_arith(ctx, op1, rd, rs, rt); - break; - case OPC_CLO: - case OPC_CLZ: - check_insn(ctx, ISA_MIPS32); - gen_cl(ctx, op1, rd, rs); - break; - case OPC_SDBBP: - /* XXX: not clear which exception should be raised - * when in debug mode... - */ - check_insn(ctx, ISA_MIPS32); - if (!(ctx->hflags & MIPS_HFLAG_DM)) { - generate_exception(ctx, EXCP_DBp); - } else { - generate_exception(ctx, EXCP_DBp); - } - /* Treat as NOP. */ - break; - case OPC_DIV_G_2F: - case OPC_DIVU_G_2F: - case OPC_MULT_G_2F: - case OPC_MULTU_G_2F: - case OPC_MOD_G_2F: - case OPC_MODU_G_2F: - check_insn(ctx, INSN_LOONGSON2F); - gen_loongson_integer(ctx, op1, rd, rs, rt); - break; +#endif + default: + if (ctx->insn_flags & ISA_MIPS32R6) { + decode_opc_special_r6(env, ctx); + } else { + decode_opc_special_legacy(env, ctx); + } + } +} + +static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) +{ + int rs, rt, rd; + uint32_t op1; + + check_insn_opc_removed(ctx, ISA_MIPS32R6); + + rs = (ctx->opcode >> 21) & 0x1f; + rt = (ctx->opcode >> 16) & 0x1f; + rd = (ctx->opcode >> 11) & 0x1f; + + op1 = MASK_SPECIAL2(ctx->opcode); + switch (op1) { + case OPC_MADD ... OPC_MADDU: /* Multiply and add/sub */ + case OPC_MSUB ... OPC_MSUBU: + check_insn(ctx, ISA_MIPS32); + gen_muldiv(ctx, op1, rd & 3, rs, rt); + break; + case OPC_MUL: + gen_arith(ctx, op1, rd, rs, rt); + break; + case OPC_DIV_G_2F: + case OPC_DIVU_G_2F: + case OPC_MULT_G_2F: + case OPC_MULTU_G_2F: + case OPC_MOD_G_2F: + case OPC_MODU_G_2F: + check_insn(ctx, INSN_LOONGSON2F); + gen_loongson_integer(ctx, op1, rd, rs, rt); + break; + case OPC_CLO: + case OPC_CLZ: + check_insn(ctx, ISA_MIPS32); + gen_cl(ctx, op1, rd, rs); + break; + case OPC_SDBBP: + /* XXX: not clear which exception should be raised + * when in debug mode... + */ + check_insn(ctx, ISA_MIPS32); + if (!(ctx->hflags & MIPS_HFLAG_DM)) { + generate_exception(ctx, EXCP_DBp); + } else { + generate_exception(ctx, EXCP_DBp); + } + /* Treat as NOP. */ + break; #if defined(TARGET_MIPS64) - case OPC_DCLO: - case OPC_DCLZ: - check_insn(ctx, ISA_MIPS64); - check_mips_64(ctx); - gen_cl(ctx, op1, rd, rs); - break; - case OPC_DMULT_G_2F: - case OPC_DMULTU_G_2F: - case OPC_DDIV_G_2F: - case OPC_DDIVU_G_2F: - case OPC_DMOD_G_2F: - case OPC_DMODU_G_2F: - check_insn(ctx, INSN_LOONGSON2F); - gen_loongson_integer(ctx, op1, rd, rs, rt); - break; + case OPC_DCLO: + case OPC_DCLZ: + check_insn(ctx, ISA_MIPS64); + check_mips_64(ctx); + gen_cl(ctx, op1, rd, rs); + break; + case OPC_DMULT_G_2F: + case OPC_DMULTU_G_2F: + case OPC_DDIV_G_2F: + case OPC_DDIVU_G_2F: + case OPC_DMOD_G_2F: + case OPC_DMODU_G_2F: + check_insn(ctx, INSN_LOONGSON2F); + gen_loongson_integer(ctx, op1, rd, rs, rt); + break; #endif - default: /* Invalid */ - MIPS_INVAL("special2"); + default: /* Invalid */ + MIPS_INVAL("special2_legacy"); + generate_exception(ctx, EXCP_RI); + break; + } +} + +static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) +{ + int rs, rt, rd, sa; + uint32_t op1, op2; + int16_t imm; + + rs = (ctx->opcode >> 21) & 0x1f; + rt = (ctx->opcode >> 16) & 0x1f; + rd = (ctx->opcode >> 11) & 0x1f; + sa = (ctx->opcode >> 6) & 0x1f; + imm = (int16_t)ctx->opcode >> 7; + + op1 = MASK_SPECIAL3(ctx->opcode); + switch (op1) { + case R6_OPC_PREF: + if (rt >= 24) { + /* hint codes 24-31 are reserved and signal RI */ generate_exception(ctx, EXCP_RI); - break; } + /* Treat as NOP. */ break; - case OPC_SPECIAL3: - op1 = MASK_SPECIAL3(ctx->opcode); - switch (op1) { - case OPC_EXT: - case OPC_INS: - check_insn(ctx, ISA_MIPS32R2); - gen_bitops(ctx, op1, rt, rs, sa, rd); - break; - case OPC_BSHFL: - check_insn(ctx, ISA_MIPS32R2); - op2 = MASK_BSHFL(ctx->opcode); - gen_bshfl(ctx, op2, rt, rd); - break; - case OPC_RDHWR: - gen_rdhwr(ctx, rt, rd); - break; - case OPC_FORK: - check_insn(ctx, ASE_MT); - { - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - - gen_load_gpr(t0, rt); - gen_load_gpr(t1, rs); - gen_helper_fork(t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); + case R6_OPC_CACHE: + /* Treat as NOP. */ + break; + case R6_OPC_SC: + gen_st_cond(ctx, op1, rt, rs, imm); + break; + case R6_OPC_LL: + gen_ld(ctx, op1, rt, rs, imm); + break; + case OPC_BSHFL: + { + if (rd == 0) { + /* Treat as NOP. */ + break; } - break; - case OPC_YIELD: - check_insn(ctx, ASE_MT); - { - TCGv t0 = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); + gen_load_gpr(t0, rt); - save_cpu_state(ctx, 1); - gen_load_gpr(t0, rs); - gen_helper_yield(t0, cpu_env, t0); - gen_store_gpr(t0, rd); - tcg_temp_free(t0); - } - break; - case OPC_DIV_G_2E ... OPC_DIVU_G_2E: - case OPC_MOD_G_2E ... OPC_MODU_G_2E: - case OPC_MULT_G_2E ... OPC_MULTU_G_2E: - /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have - * the same mask and op1. */ - if ((ctx->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) { - op2 = MASK_ADDUH_QB(ctx->opcode); - switch (op2) { - case OPC_ADDUH_QB: - case OPC_ADDUH_R_QB: - case OPC_ADDQH_PH: - case OPC_ADDQH_R_PH: - case OPC_ADDQH_W: - case OPC_ADDQH_R_W: - case OPC_SUBUH_QB: - case OPC_SUBUH_R_QB: - case OPC_SUBQH_PH: - case OPC_SUBQH_R_PH: - case OPC_SUBQH_W: - case OPC_SUBQH_R_W: - gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); - break; - case OPC_MUL_PH: - case OPC_MUL_S_PH: - case OPC_MULQ_S_W: - case OPC_MULQ_RS_W: - gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1); - break; - default: - MIPS_INVAL("MASK ADDUH.QB"); - generate_exception(ctx, EXCP_RI); - break; - } - } else if (ctx->insn_flags & INSN_LOONGSON2E) { - gen_loongson_integer(ctx, op1, rd, rs, rt); - } else { - generate_exception(ctx, EXCP_RI); - } - break; - case OPC_LX_DSP: - op2 = MASK_LX(ctx->opcode); + op2 = MASK_BSHFL(ctx->opcode); switch (op2) { + case OPC_ALIGN ... OPC_ALIGN_END: + sa &= 3; + if (sa == 0) { + tcg_gen_mov_tl(cpu_gpr[rd], t0); + } else { + TCGv t1 = tcg_temp_new(); + TCGv_i64 t2 = tcg_temp_new_i64(); + gen_load_gpr(t1, rs); + tcg_gen_concat_tl_i64(t2, t1, t0); + tcg_gen_shri_i64(t2, t2, 8 * (4 - sa)); #if defined(TARGET_MIPS64) - case OPC_LDX: + tcg_gen_ext32s_i64(cpu_gpr[rd], t2); +#else + tcg_gen_trunc_i64_i32(cpu_gpr[rd], t2); #endif - case OPC_LBUX: - case OPC_LHX: - case OPC_LWX: - gen_mipsdsp_ld(ctx, op2, rd, rs, rt); + tcg_temp_free_i64(t2); + tcg_temp_free(t1); + } break; - default: /* Invalid */ - MIPS_INVAL("MASK LX"); - generate_exception(ctx, EXCP_RI); + case OPC_BITSWAP: + gen_helper_bitswap(cpu_gpr[rd], t0); break; } - break; - case OPC_ABSQ_S_PH_DSP: - op2 = MASK_ABSQ_S_PH(ctx->opcode); - switch (op2) { - case OPC_ABSQ_S_QB: - case OPC_ABSQ_S_PH: - case OPC_ABSQ_S_W: - case OPC_PRECEQ_W_PHL: - case OPC_PRECEQ_W_PHR: - case OPC_PRECEQU_PH_QBL: - case OPC_PRECEQU_PH_QBR: - case OPC_PRECEQU_PH_QBLA: - case OPC_PRECEQU_PH_QBRA: - case OPC_PRECEU_PH_QBL: - case OPC_PRECEU_PH_QBR: - case OPC_PRECEU_PH_QBLA: - case OPC_PRECEU_PH_QBRA: - gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); - break; - case OPC_BITREV: - case OPC_REPL_QB: - case OPC_REPLV_QB: - case OPC_REPL_PH: - case OPC_REPLV_PH: - gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); - break; - default: - MIPS_INVAL("MASK ABSQ_S.PH"); - generate_exception(ctx, EXCP_RI); + tcg_temp_free(t0); + } + break; +#if defined(TARGET_MIPS64) + case R6_OPC_SCD: + gen_st_cond(ctx, op1, rt, rs, imm); + break; + case R6_OPC_LLD: + gen_ld(ctx, op1, rt, rs, imm); + break; + case OPC_DBSHFL: + check_mips_64(ctx); + { + if (rd == 0) { + /* Treat as NOP. */ break; } - break; - case OPC_ADDU_QB_DSP: - op2 = MASK_ADDU_QB(ctx->opcode); + TCGv t0 = tcg_temp_new(); + gen_load_gpr(t0, rt); + + op2 = MASK_DBSHFL(ctx->opcode); switch (op2) { - case OPC_ADDQ_PH: - case OPC_ADDQ_S_PH: - case OPC_ADDQ_S_W: - case OPC_ADDU_QB: - case OPC_ADDU_S_QB: - case OPC_ADDU_PH: - case OPC_ADDU_S_PH: - case OPC_SUBQ_PH: - case OPC_SUBQ_S_PH: - case OPC_SUBQ_S_W: - case OPC_SUBU_QB: - case OPC_SUBU_S_QB: - case OPC_SUBU_PH: - case OPC_SUBU_S_PH: - case OPC_ADDSC: - case OPC_ADDWC: - case OPC_MODSUB: - case OPC_RADDU_W_QB: - gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); - break; - case OPC_MULEU_S_PH_QBL: - case OPC_MULEU_S_PH_QBR: - case OPC_MULQ_RS_PH: - case OPC_MULEQ_S_W_PHL: - case OPC_MULEQ_S_W_PHR: - case OPC_MULQ_S_PH: - gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1); + case OPC_DALIGN ... OPC_DALIGN_END: + sa &= 7; + if (sa == 0) { + tcg_gen_mov_tl(cpu_gpr[rd], t0); + } else { + TCGv t1 = tcg_temp_new(); + gen_load_gpr(t1, rs); + tcg_gen_shli_tl(t0, t0, 8 * sa); + tcg_gen_shri_tl(t1, t1, 8 * (8 - sa)); + tcg_gen_or_tl(cpu_gpr[rd], t1, t0); + tcg_temp_free(t1); + } break; - default: /* Invalid */ - MIPS_INVAL("MASK ADDU.QB"); - generate_exception(ctx, EXCP_RI); + case OPC_DBITSWAP: + gen_helper_dbitswap(cpu_gpr[rd], t0); break; - } - break; - case OPC_CMPU_EQ_QB_DSP: - op2 = MASK_CMPU_EQ_QB(ctx->opcode); + tcg_temp_free(t0); + } + break; +#endif + default: /* Invalid */ + MIPS_INVAL("special3_r6"); + generate_exception(ctx, EXCP_RI); + break; + } +} + +static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) +{ + int rs, rt, rd; + uint32_t op1, op2; + + rs = (ctx->opcode >> 21) & 0x1f; + rt = (ctx->opcode >> 16) & 0x1f; + rd = (ctx->opcode >> 11) & 0x1f; + + op1 = MASK_SPECIAL3(ctx->opcode); + switch (op1) { + case OPC_DIV_G_2E ... OPC_DIVU_G_2E: + case OPC_MOD_G_2E ... OPC_MODU_G_2E: + case OPC_MULT_G_2E ... OPC_MULTU_G_2E: + /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have + * the same mask and op1. */ + if ((ctx->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) { + op2 = MASK_ADDUH_QB(ctx->opcode); switch (op2) { - case OPC_PRECR_SRA_PH_W: - case OPC_PRECR_SRA_R_PH_W: - gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd); - break; - case OPC_PRECR_QB_PH: - case OPC_PRECRQ_QB_PH: - case OPC_PRECRQ_PH_W: - case OPC_PRECRQ_RS_PH_W: - case OPC_PRECRQU_S_QB_PH: + case OPC_ADDUH_QB: + case OPC_ADDUH_R_QB: + case OPC_ADDQH_PH: + case OPC_ADDQH_R_PH: + case OPC_ADDQH_W: + case OPC_ADDQH_R_W: + case OPC_SUBUH_QB: + case OPC_SUBUH_R_QB: + case OPC_SUBQH_PH: + case OPC_SUBQH_R_PH: + case OPC_SUBQH_W: + case OPC_SUBQH_R_W: gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); break; - case OPC_CMPU_EQ_QB: - case OPC_CMPU_LT_QB: - case OPC_CMPU_LE_QB: - case OPC_CMP_EQ_PH: - case OPC_CMP_LT_PH: - case OPC_CMP_LE_PH: - gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0); - break; - case OPC_CMPGU_EQ_QB: - case OPC_CMPGU_LT_QB: - case OPC_CMPGU_LE_QB: - case OPC_CMPGDU_EQ_QB: - case OPC_CMPGDU_LT_QB: - case OPC_CMPGDU_LE_QB: - case OPC_PICK_QB: - case OPC_PICK_PH: - case OPC_PACKRL_PH: - gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1); + case OPC_MUL_PH: + case OPC_MUL_S_PH: + case OPC_MULQ_S_W: + case OPC_MULQ_RS_W: + gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1); break; - default: /* Invalid */ - MIPS_INVAL("MASK CMPU.EQ.QB"); + default: + MIPS_INVAL("MASK ADDUH.QB"); generate_exception(ctx, EXCP_RI); break; } + } else if (ctx->insn_flags & INSN_LOONGSON2E) { + gen_loongson_integer(ctx, op1, rd, rs, rt); + } else { + generate_exception(ctx, EXCP_RI); + } + break; + case OPC_LX_DSP: + op2 = MASK_LX(ctx->opcode); + switch (op2) { +#if defined(TARGET_MIPS64) + case OPC_LDX: +#endif + case OPC_LBUX: + case OPC_LHX: + case OPC_LWX: + gen_mipsdsp_ld(ctx, op2, rd, rs, rt); break; - case OPC_SHLL_QB_DSP: - gen_mipsdsp_shift(ctx, op1, rd, rs, rt); + default: /* Invalid */ + MIPS_INVAL("MASK LX"); + generate_exception(ctx, EXCP_RI); break; - case OPC_DPA_W_PH_DSP: - op2 = MASK_DPA_W_PH(ctx->opcode); - switch (op2) { - case OPC_DPAU_H_QBL: - case OPC_DPAU_H_QBR: - case OPC_DPSU_H_QBL: - case OPC_DPSU_H_QBR: - case OPC_DPA_W_PH: - case OPC_DPAX_W_PH: - case OPC_DPAQ_S_W_PH: - case OPC_DPAQX_S_W_PH: - case OPC_DPAQX_SA_W_PH: - case OPC_DPS_W_PH: - case OPC_DPSX_W_PH: - case OPC_DPSQ_S_W_PH: - case OPC_DPSQX_S_W_PH: - case OPC_DPSQX_SA_W_PH: - case OPC_MULSAQ_S_W_PH: - case OPC_DPAQ_SA_L_W: - case OPC_DPSQ_SA_L_W: - case OPC_MAQ_S_W_PHL: - case OPC_MAQ_S_W_PHR: - case OPC_MAQ_SA_W_PHL: - case OPC_MAQ_SA_W_PHR: - case OPC_MULSA_W_PH: - gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0); - break; - default: /* Invalid */ - MIPS_INVAL("MASK DPAW.PH"); - generate_exception(ctx, EXCP_RI); - break; - } + } + break; + case OPC_ABSQ_S_PH_DSP: + op2 = MASK_ABSQ_S_PH(ctx->opcode); + switch (op2) { + case OPC_ABSQ_S_QB: + case OPC_ABSQ_S_PH: + case OPC_ABSQ_S_W: + case OPC_PRECEQ_W_PHL: + case OPC_PRECEQ_W_PHR: + case OPC_PRECEQU_PH_QBL: + case OPC_PRECEQU_PH_QBR: + case OPC_PRECEQU_PH_QBLA: + case OPC_PRECEQU_PH_QBRA: + case OPC_PRECEU_PH_QBL: + case OPC_PRECEU_PH_QBR: + case OPC_PRECEU_PH_QBLA: + case OPC_PRECEU_PH_QBRA: + gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); + break; + case OPC_BITREV: + case OPC_REPL_QB: + case OPC_REPLV_QB: + case OPC_REPL_PH: + case OPC_REPLV_PH: + gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); + break; + default: + MIPS_INVAL("MASK ABSQ_S.PH"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case OPC_ADDU_QB_DSP: + op2 = MASK_ADDU_QB(ctx->opcode); + switch (op2) { + case OPC_ADDQ_PH: + case OPC_ADDQ_S_PH: + case OPC_ADDQ_S_W: + case OPC_ADDU_QB: + case OPC_ADDU_S_QB: + case OPC_ADDU_PH: + case OPC_ADDU_S_PH: + case OPC_SUBQ_PH: + case OPC_SUBQ_S_PH: + case OPC_SUBQ_S_W: + case OPC_SUBU_QB: + case OPC_SUBU_S_QB: + case OPC_SUBU_PH: + case OPC_SUBU_S_PH: + case OPC_ADDSC: + case OPC_ADDWC: + case OPC_MODSUB: + case OPC_RADDU_W_QB: + gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); + break; + case OPC_MULEU_S_PH_QBL: + case OPC_MULEU_S_PH_QBR: + case OPC_MULQ_RS_PH: + case OPC_MULEQ_S_W_PHL: + case OPC_MULEQ_S_W_PHR: + case OPC_MULQ_S_PH: + gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1); + break; + default: /* Invalid */ + MIPS_INVAL("MASK ADDU.QB"); + generate_exception(ctx, EXCP_RI); break; - case OPC_INSV_DSP: - op2 = MASK_INSV(ctx->opcode); - switch (op2) { - case OPC_INSV: - check_dsp(ctx); - { - TCGv t0, t1; - if (rt == 0) { - MIPS_DEBUG("NOP"); - break; - } + } + break; + case OPC_CMPU_EQ_QB_DSP: + op2 = MASK_CMPU_EQ_QB(ctx->opcode); + switch (op2) { + case OPC_PRECR_SRA_PH_W: + case OPC_PRECR_SRA_R_PH_W: + gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd); + break; + case OPC_PRECR_QB_PH: + case OPC_PRECRQ_QB_PH: + case OPC_PRECRQ_PH_W: + case OPC_PRECRQ_RS_PH_W: + case OPC_PRECRQU_S_QB_PH: + gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); + break; + case OPC_CMPU_EQ_QB: + case OPC_CMPU_LT_QB: + case OPC_CMPU_LE_QB: + case OPC_CMP_EQ_PH: + case OPC_CMP_LT_PH: + case OPC_CMP_LE_PH: + gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0); + break; + case OPC_CMPGU_EQ_QB: + case OPC_CMPGU_LT_QB: + case OPC_CMPGU_LE_QB: + case OPC_CMPGDU_EQ_QB: + case OPC_CMPGDU_LT_QB: + case OPC_CMPGDU_LE_QB: + case OPC_PICK_QB: + case OPC_PICK_PH: + case OPC_PACKRL_PH: + gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1); + break; + default: /* Invalid */ + MIPS_INVAL("MASK CMPU.EQ.QB"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case OPC_SHLL_QB_DSP: + gen_mipsdsp_shift(ctx, op1, rd, rs, rt); + break; + case OPC_DPA_W_PH_DSP: + op2 = MASK_DPA_W_PH(ctx->opcode); + switch (op2) { + case OPC_DPAU_H_QBL: + case OPC_DPAU_H_QBR: + case OPC_DPSU_H_QBL: + case OPC_DPSU_H_QBR: + case OPC_DPA_W_PH: + case OPC_DPAX_W_PH: + case OPC_DPAQ_S_W_PH: + case OPC_DPAQX_S_W_PH: + case OPC_DPAQX_SA_W_PH: + case OPC_DPS_W_PH: + case OPC_DPSX_W_PH: + case OPC_DPSQ_S_W_PH: + case OPC_DPSQX_S_W_PH: + case OPC_DPSQX_SA_W_PH: + case OPC_MULSAQ_S_W_PH: + case OPC_DPAQ_SA_L_W: + case OPC_DPSQ_SA_L_W: + case OPC_MAQ_S_W_PHL: + case OPC_MAQ_S_W_PHR: + case OPC_MAQ_SA_W_PHL: + case OPC_MAQ_SA_W_PHR: + case OPC_MULSA_W_PH: + gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0); + break; + default: /* Invalid */ + MIPS_INVAL("MASK DPAW.PH"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case OPC_INSV_DSP: + op2 = MASK_INSV(ctx->opcode); + switch (op2) { + case OPC_INSV: + check_dsp(ctx); + { + TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); + if (rt == 0) { + MIPS_DEBUG("NOP"); + break; + } - gen_load_gpr(t0, rt); - gen_load_gpr(t1, rs); + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); - gen_helper_insv(cpu_gpr[rt], cpu_env, t1, t0); + gen_load_gpr(t0, rt); + gen_load_gpr(t1, rs); - tcg_temp_free(t0); - tcg_temp_free(t1); - break; - } - default: /* Invalid */ - MIPS_INVAL("MASK INSV"); - generate_exception(ctx, EXCP_RI); + gen_helper_insv(cpu_gpr[rt], cpu_env, t1, t0); + + tcg_temp_free(t0); + tcg_temp_free(t1); break; } + default: /* Invalid */ + MIPS_INVAL("MASK INSV"); + generate_exception(ctx, EXCP_RI); break; - case OPC_APPEND_DSP: - gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); + } + break; + case OPC_APPEND_DSP: + gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); + break; + case OPC_EXTR_W_DSP: + op2 = MASK_EXTR_W(ctx->opcode); + switch (op2) { + case OPC_EXTR_W: + case OPC_EXTR_R_W: + case OPC_EXTR_RS_W: + case OPC_EXTR_S_H: + case OPC_EXTRV_S_H: + case OPC_EXTRV_W: + case OPC_EXTRV_R_W: + case OPC_EXTRV_RS_W: + case OPC_EXTP: + case OPC_EXTPV: + case OPC_EXTPDP: + case OPC_EXTPDPV: + gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1); break; - case OPC_EXTR_W_DSP: - op2 = MASK_EXTR_W(ctx->opcode); - switch (op2) { - case OPC_EXTR_W: - case OPC_EXTR_R_W: - case OPC_EXTR_RS_W: - case OPC_EXTR_S_H: - case OPC_EXTRV_S_H: - case OPC_EXTRV_W: - case OPC_EXTRV_R_W: - case OPC_EXTRV_RS_W: - case OPC_EXTP: - case OPC_EXTPV: - case OPC_EXTPDP: - case OPC_EXTPDPV: - gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1); - break; - case OPC_RDDSP: - gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 1); - break; - case OPC_SHILO: - case OPC_SHILOV: - case OPC_MTHLIP: - case OPC_WRDSP: - gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0); - break; - default: /* Invalid */ - MIPS_INVAL("MASK EXTR.W"); - generate_exception(ctx, EXCP_RI); - break; - } + case OPC_RDDSP: + gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 1); break; + case OPC_SHILO: + case OPC_SHILOV: + case OPC_MTHLIP: + case OPC_WRDSP: + gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0); + break; + default: /* Invalid */ + MIPS_INVAL("MASK EXTR.W"); + generate_exception(ctx, EXCP_RI); + break; + } + break; #if defined(TARGET_MIPS64) - case OPC_DEXTM ... OPC_DEXT: - case OPC_DINSM ... OPC_DINS: - check_insn(ctx, ISA_MIPS64R2); - check_mips_64(ctx); - gen_bitops(ctx, op1, rt, rs, sa, rd); + case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E: + case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E: + case OPC_DMOD_G_2E ... OPC_DMODU_G_2E: + check_insn(ctx, INSN_LOONGSON2E); + gen_loongson_integer(ctx, op1, rd, rs, rt); + break; + case OPC_ABSQ_S_QH_DSP: + op2 = MASK_ABSQ_S_QH(ctx->opcode); + switch (op2) { + case OPC_PRECEQ_L_PWL: + case OPC_PRECEQ_L_PWR: + case OPC_PRECEQ_PW_QHL: + case OPC_PRECEQ_PW_QHR: + case OPC_PRECEQ_PW_QHLA: + case OPC_PRECEQ_PW_QHRA: + case OPC_PRECEQU_QH_OBL: + case OPC_PRECEQU_QH_OBR: + case OPC_PRECEQU_QH_OBLA: + case OPC_PRECEQU_QH_OBRA: + case OPC_PRECEU_QH_OBL: + case OPC_PRECEU_QH_OBR: + case OPC_PRECEU_QH_OBLA: + case OPC_PRECEU_QH_OBRA: + case OPC_ABSQ_S_OB: + case OPC_ABSQ_S_PW: + case OPC_ABSQ_S_QH: + gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); break; - case OPC_DBSHFL: - check_insn(ctx, ISA_MIPS64R2); - check_mips_64(ctx); - op2 = MASK_DBSHFL(ctx->opcode); - gen_bshfl(ctx, op2, rt, rd); + case OPC_REPL_OB: + case OPC_REPL_PW: + case OPC_REPL_QH: + case OPC_REPLV_OB: + case OPC_REPLV_PW: + case OPC_REPLV_QH: + gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); break; - case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E: - case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E: - case OPC_DMOD_G_2E ... OPC_DMODU_G_2E: - check_insn(ctx, INSN_LOONGSON2E); - gen_loongson_integer(ctx, op1, rd, rs, rt); + default: /* Invalid */ + MIPS_INVAL("MASK ABSQ_S.QH"); + generate_exception(ctx, EXCP_RI); break; - case OPC_ABSQ_S_QH_DSP: - op2 = MASK_ABSQ_S_QH(ctx->opcode); - switch (op2) { - case OPC_PRECEQ_L_PWL: - case OPC_PRECEQ_L_PWR: - case OPC_PRECEQ_PW_QHL: - case OPC_PRECEQ_PW_QHR: - case OPC_PRECEQ_PW_QHLA: - case OPC_PRECEQ_PW_QHRA: - case OPC_PRECEQU_QH_OBL: - case OPC_PRECEQU_QH_OBR: - case OPC_PRECEQU_QH_OBLA: - case OPC_PRECEQU_QH_OBRA: - case OPC_PRECEU_QH_OBL: - case OPC_PRECEU_QH_OBR: - case OPC_PRECEU_QH_OBLA: - case OPC_PRECEU_QH_OBRA: - case OPC_ABSQ_S_OB: - case OPC_ABSQ_S_PW: - case OPC_ABSQ_S_QH: - gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); - break; - case OPC_REPL_OB: - case OPC_REPL_PW: - case OPC_REPL_QH: - case OPC_REPLV_OB: - case OPC_REPLV_PW: - case OPC_REPLV_QH: - gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); - break; - default: /* Invalid */ - MIPS_INVAL("MASK ABSQ_S.QH"); - generate_exception(ctx, EXCP_RI); - break; - } + } + break; + case OPC_ADDU_OB_DSP: + op2 = MASK_ADDU_OB(ctx->opcode); + switch (op2) { + case OPC_RADDU_L_OB: + case OPC_SUBQ_PW: + case OPC_SUBQ_S_PW: + case OPC_SUBQ_QH: + case OPC_SUBQ_S_QH: + case OPC_SUBU_OB: + case OPC_SUBU_S_OB: + case OPC_SUBU_QH: + case OPC_SUBU_S_QH: + case OPC_SUBUH_OB: + case OPC_SUBUH_R_OB: + case OPC_ADDQ_PW: + case OPC_ADDQ_S_PW: + case OPC_ADDQ_QH: + case OPC_ADDQ_S_QH: + case OPC_ADDU_OB: + case OPC_ADDU_S_OB: + case OPC_ADDU_QH: + case OPC_ADDU_S_QH: + case OPC_ADDUH_OB: + case OPC_ADDUH_R_OB: + gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); break; - case OPC_ADDU_OB_DSP: - op2 = MASK_ADDU_OB(ctx->opcode); - switch (op2) { - case OPC_RADDU_L_OB: - case OPC_SUBQ_PW: - case OPC_SUBQ_S_PW: - case OPC_SUBQ_QH: - case OPC_SUBQ_S_QH: - case OPC_SUBU_OB: - case OPC_SUBU_S_OB: - case OPC_SUBU_QH: - case OPC_SUBU_S_QH: - case OPC_SUBUH_OB: - case OPC_SUBUH_R_OB: - case OPC_ADDQ_PW: - case OPC_ADDQ_S_PW: - case OPC_ADDQ_QH: - case OPC_ADDQ_S_QH: - case OPC_ADDU_OB: - case OPC_ADDU_S_OB: - case OPC_ADDU_QH: - case OPC_ADDU_S_QH: - case OPC_ADDUH_OB: - case OPC_ADDUH_R_OB: - gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); - break; - case OPC_MULEQ_S_PW_QHL: - case OPC_MULEQ_S_PW_QHR: - case OPC_MULEU_S_QH_OBL: - case OPC_MULEU_S_QH_OBR: - case OPC_MULQ_RS_QH: - gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1); - break; - default: /* Invalid */ - MIPS_INVAL("MASK ADDU.OB"); - generate_exception(ctx, EXCP_RI); - break; - } + case OPC_MULEQ_S_PW_QHL: + case OPC_MULEQ_S_PW_QHR: + case OPC_MULEU_S_QH_OBL: + case OPC_MULEU_S_QH_OBR: + case OPC_MULQ_RS_QH: + gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1); break; - case OPC_CMPU_EQ_OB_DSP: - op2 = MASK_CMPU_EQ_OB(ctx->opcode); - switch (op2) { - case OPC_PRECR_SRA_QH_PW: - case OPC_PRECR_SRA_R_QH_PW: - /* Return value is rt. */ - gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd); - break; - case OPC_PRECR_OB_QH: - case OPC_PRECRQ_OB_QH: - case OPC_PRECRQ_PW_L: - case OPC_PRECRQ_QH_PW: - case OPC_PRECRQ_RS_QH_PW: - case OPC_PRECRQU_S_OB_QH: - gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); - break; - case OPC_CMPU_EQ_OB: - case OPC_CMPU_LT_OB: - case OPC_CMPU_LE_OB: - case OPC_CMP_EQ_QH: - case OPC_CMP_LT_QH: - case OPC_CMP_LE_QH: - case OPC_CMP_EQ_PW: - case OPC_CMP_LT_PW: - case OPC_CMP_LE_PW: - gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0); - break; - case OPC_CMPGDU_EQ_OB: - case OPC_CMPGDU_LT_OB: - case OPC_CMPGDU_LE_OB: - case OPC_CMPGU_EQ_OB: - case OPC_CMPGU_LT_OB: - case OPC_CMPGU_LE_OB: - case OPC_PACKRL_PW: - case OPC_PICK_OB: - case OPC_PICK_PW: - case OPC_PICK_QH: - gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1); - break; - default: /* Invalid */ - MIPS_INVAL("MASK CMPU_EQ.OB"); - generate_exception(ctx, EXCP_RI); - break; - } + default: /* Invalid */ + MIPS_INVAL("MASK ADDU.OB"); + generate_exception(ctx, EXCP_RI); break; - case OPC_DAPPEND_DSP: - gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); + } + break; + case OPC_CMPU_EQ_OB_DSP: + op2 = MASK_CMPU_EQ_OB(ctx->opcode); + switch (op2) { + case OPC_PRECR_SRA_QH_PW: + case OPC_PRECR_SRA_R_QH_PW: + /* Return value is rt. */ + gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd); break; - case OPC_DEXTR_W_DSP: - op2 = MASK_DEXTR_W(ctx->opcode); - switch (op2) { - case OPC_DEXTP: - case OPC_DEXTPDP: - case OPC_DEXTPDPV: - case OPC_DEXTPV: - case OPC_DEXTR_L: - case OPC_DEXTR_R_L: - case OPC_DEXTR_RS_L: - case OPC_DEXTR_W: - case OPC_DEXTR_R_W: - case OPC_DEXTR_RS_W: - case OPC_DEXTR_S_H: - case OPC_DEXTRV_L: - case OPC_DEXTRV_R_L: - case OPC_DEXTRV_RS_L: - case OPC_DEXTRV_S_H: - case OPC_DEXTRV_W: - case OPC_DEXTRV_R_W: - case OPC_DEXTRV_RS_W: - gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1); - break; - case OPC_DMTHLIP: - case OPC_DSHILO: - case OPC_DSHILOV: - gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0); - break; - default: /* Invalid */ - MIPS_INVAL("MASK EXTR.W"); - generate_exception(ctx, EXCP_RI); - break; - } + case OPC_PRECR_OB_QH: + case OPC_PRECRQ_OB_QH: + case OPC_PRECRQ_PW_L: + case OPC_PRECRQ_QH_PW: + case OPC_PRECRQ_RS_QH_PW: + case OPC_PRECRQU_S_OB_QH: + gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); break; - case OPC_DPAQ_W_QH_DSP: - op2 = MASK_DPAQ_W_QH(ctx->opcode); - switch (op2) { - case OPC_DPAU_H_OBL: - case OPC_DPAU_H_OBR: - case OPC_DPSU_H_OBL: - case OPC_DPSU_H_OBR: - case OPC_DPA_W_QH: - case OPC_DPAQ_S_W_QH: - case OPC_DPS_W_QH: - case OPC_DPSQ_S_W_QH: - case OPC_MULSAQ_S_W_QH: - case OPC_DPAQ_SA_L_PW: - case OPC_DPSQ_SA_L_PW: - case OPC_MULSAQ_S_L_PW: - gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0); - break; - case OPC_MAQ_S_W_QHLL: - case OPC_MAQ_S_W_QHLR: - case OPC_MAQ_S_W_QHRL: - case OPC_MAQ_S_W_QHRR: - case OPC_MAQ_SA_W_QHLL: - case OPC_MAQ_SA_W_QHLR: - case OPC_MAQ_SA_W_QHRL: - case OPC_MAQ_SA_W_QHRR: - case OPC_MAQ_S_L_PWL: - case OPC_MAQ_S_L_PWR: - case OPC_DMADD: - case OPC_DMADDU: - case OPC_DMSUB: - case OPC_DMSUBU: - gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0); - break; - default: /* Invalid */ - MIPS_INVAL("MASK DPAQ.W.QH"); - generate_exception(ctx, EXCP_RI); - break; - } + case OPC_CMPU_EQ_OB: + case OPC_CMPU_LT_OB: + case OPC_CMPU_LE_OB: + case OPC_CMP_EQ_QH: + case OPC_CMP_LT_QH: + case OPC_CMP_LE_QH: + case OPC_CMP_EQ_PW: + case OPC_CMP_LT_PW: + case OPC_CMP_LE_PW: + gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0); break; - case OPC_DINSV_DSP: - op2 = MASK_INSV(ctx->opcode); - switch (op2) { - case OPC_DINSV: - { - TCGv t0, t1; + case OPC_CMPGDU_EQ_OB: + case OPC_CMPGDU_LT_OB: + case OPC_CMPGDU_LE_OB: + case OPC_CMPGU_EQ_OB: + case OPC_CMPGU_LT_OB: + case OPC_CMPGU_LE_OB: + case OPC_PACKRL_PW: + case OPC_PICK_OB: + case OPC_PICK_PW: + case OPC_PICK_QH: + gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1); + break; + default: /* Invalid */ + MIPS_INVAL("MASK CMPU_EQ.OB"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case OPC_DAPPEND_DSP: + gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); + break; + case OPC_DEXTR_W_DSP: + op2 = MASK_DEXTR_W(ctx->opcode); + switch (op2) { + case OPC_DEXTP: + case OPC_DEXTPDP: + case OPC_DEXTPDPV: + case OPC_DEXTPV: + case OPC_DEXTR_L: + case OPC_DEXTR_R_L: + case OPC_DEXTR_RS_L: + case OPC_DEXTR_W: + case OPC_DEXTR_R_W: + case OPC_DEXTR_RS_W: + case OPC_DEXTR_S_H: + case OPC_DEXTRV_L: + case OPC_DEXTRV_R_L: + case OPC_DEXTRV_RS_L: + case OPC_DEXTRV_S_H: + case OPC_DEXTRV_W: + case OPC_DEXTRV_R_W: + case OPC_DEXTRV_RS_W: + gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1); + break; + case OPC_DMTHLIP: + case OPC_DSHILO: + case OPC_DSHILOV: + gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0); + break; + default: /* Invalid */ + MIPS_INVAL("MASK EXTR.W"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case OPC_DPAQ_W_QH_DSP: + op2 = MASK_DPAQ_W_QH(ctx->opcode); + switch (op2) { + case OPC_DPAU_H_OBL: + case OPC_DPAU_H_OBR: + case OPC_DPSU_H_OBL: + case OPC_DPSU_H_OBR: + case OPC_DPA_W_QH: + case OPC_DPAQ_S_W_QH: + case OPC_DPS_W_QH: + case OPC_DPSQ_S_W_QH: + case OPC_MULSAQ_S_W_QH: + case OPC_DPAQ_SA_L_PW: + case OPC_DPSQ_SA_L_PW: + case OPC_MULSAQ_S_L_PW: + gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0); + break; + case OPC_MAQ_S_W_QHLL: + case OPC_MAQ_S_W_QHLR: + case OPC_MAQ_S_W_QHRL: + case OPC_MAQ_S_W_QHRR: + case OPC_MAQ_SA_W_QHLL: + case OPC_MAQ_SA_W_QHLR: + case OPC_MAQ_SA_W_QHRL: + case OPC_MAQ_SA_W_QHRR: + case OPC_MAQ_S_L_PWL: + case OPC_MAQ_S_L_PWR: + case OPC_DMADD: + case OPC_DMADDU: + case OPC_DMSUB: + case OPC_DMSUBU: + gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0); + break; + default: /* Invalid */ + MIPS_INVAL("MASK DPAQ.W.QH"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case OPC_DINSV_DSP: + op2 = MASK_INSV(ctx->opcode); + switch (op2) { + case OPC_DINSV: + { + TCGv t0, t1; - if (rt == 0) { - MIPS_DEBUG("NOP"); - break; - } - check_dsp(ctx); + if (rt == 0) { + MIPS_DEBUG("NOP"); + break; + } + check_dsp(ctx); - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); - gen_load_gpr(t0, rt); - gen_load_gpr(t1, rs); + gen_load_gpr(t0, rt); + gen_load_gpr(t1, rs); - gen_helper_dinsv(cpu_gpr[rt], cpu_env, t1, t0); + gen_helper_dinsv(cpu_gpr[rt], cpu_env, t1, t0); - tcg_temp_free(t0); - tcg_temp_free(t1); - break; - } - default: /* Invalid */ - MIPS_INVAL("MASK DINSV"); - generate_exception(ctx, EXCP_RI); - break; - } - break; - case OPC_SHLL_OB_DSP: - gen_mipsdsp_shift(ctx, op1, rd, rs, rt); + tcg_temp_free(t0); + tcg_temp_free(t1); break; -#endif + } default: /* Invalid */ - MIPS_INVAL("special3"); + MIPS_INVAL("MASK DINSV"); generate_exception(ctx, EXCP_RI); break; } break; + case OPC_SHLL_OB_DSP: + gen_mipsdsp_shift(ctx, op1, rd, rs, rt); + break; +#endif + default: /* Invalid */ + MIPS_INVAL("special3_legacy"); + generate_exception(ctx, EXCP_RI); + break; + } +} + +static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) +{ + int rs, rt, rd, sa; + uint32_t op1, op2; + + rs = (ctx->opcode >> 21) & 0x1f; + rt = (ctx->opcode >> 16) & 0x1f; + rd = (ctx->opcode >> 11) & 0x1f; + sa = (ctx->opcode >> 6) & 0x1f; + + op1 = MASK_SPECIAL3(ctx->opcode); + switch (op1) { + case OPC_EXT: + case OPC_INS: + check_insn(ctx, ISA_MIPS32R2); + gen_bitops(ctx, op1, rt, rs, sa, rd); + break; + case OPC_BSHFL: + op2 = MASK_BSHFL(ctx->opcode); + switch (op2) { + case OPC_ALIGN ... OPC_ALIGN_END: + case OPC_BITSWAP: + check_insn(ctx, ISA_MIPS32R6); + decode_opc_special3_r6(env, ctx); + break; + default: + check_insn(ctx, ISA_MIPS32R2); + gen_bshfl(ctx, op2, rt, rd); + break; + } + break; +#if defined(TARGET_MIPS64) + case OPC_DEXTM ... OPC_DEXT: + case OPC_DINSM ... OPC_DINS: + check_insn(ctx, ISA_MIPS64R2); + check_mips_64(ctx); + gen_bitops(ctx, op1, rt, rs, sa, rd); + break; + case OPC_DBSHFL: + op2 = MASK_DBSHFL(ctx->opcode); + switch (op2) { + case OPC_DALIGN ... OPC_DALIGN_END: + case OPC_DBITSWAP: + check_insn(ctx, ISA_MIPS32R6); + decode_opc_special3_r6(env, ctx); + break; + default: + check_insn(ctx, ISA_MIPS64R2); + check_mips_64(ctx); + op2 = MASK_DBSHFL(ctx->opcode); + gen_bshfl(ctx, op2, rt, rd); + break; + } + break; +#endif + case OPC_RDHWR: + gen_rdhwr(ctx, rt, rd); + break; + case OPC_FORK: + check_insn(ctx, ASE_MT); + { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + gen_load_gpr(t1, rs); + gen_helper_fork(t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + case OPC_YIELD: + check_insn(ctx, ASE_MT); + { + TCGv t0 = tcg_temp_new(); + + save_cpu_state(ctx, 1); + gen_load_gpr(t0, rs); + gen_helper_yield(t0, cpu_env, t0); + gen_store_gpr(t0, rd); + tcg_temp_free(t0); + } + break; + default: + if (ctx->insn_flags & ISA_MIPS32R6) { + decode_opc_special3_r6(env, ctx); + } else { + decode_opc_special3_legacy(env, ctx); + } + } +} + +static void decode_opc (CPUMIPSState *env, DisasContext *ctx) +{ + int32_t offset; + int rs, rt, rd, sa; + uint32_t op, op1; + int16_t imm; + + /* make sure instructions are on a word boundary */ + if (ctx->pc & 0x3) { + env->CP0_BadVAddr = ctx->pc; + generate_exception(ctx, EXCP_AdEL); + return; + } + + /* Handle blikely not taken case */ + if ((ctx->hflags & MIPS_HFLAG_BMASK_BASE) == MIPS_HFLAG_BL) { + int l1 = gen_new_label(); + + MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4); + tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); + tcg_gen_movi_i32(hflags, ctx->hflags & ~MIPS_HFLAG_BMASK); + gen_goto_tb(ctx, 1, ctx->pc + 4); + gen_set_label(l1); + } + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { + tcg_gen_debug_insn_start(ctx->pc); + } + + op = MASK_OP_MAJOR(ctx->opcode); + rs = (ctx->opcode >> 21) & 0x1f; + rt = (ctx->opcode >> 16) & 0x1f; + rd = (ctx->opcode >> 11) & 0x1f; + sa = (ctx->opcode >> 6) & 0x1f; + imm = (int16_t)ctx->opcode; + switch (op) { + case OPC_SPECIAL: + decode_opc_special(env, ctx); + break; + case OPC_SPECIAL2: + decode_opc_special2_legacy(env, ctx); + break; + case OPC_SPECIAL3: + decode_opc_special3(env, ctx); + break; case OPC_REGIMM: op1 = MASK_REGIMM(ctx->opcode); switch (op1) { - case OPC_BLTZ ... OPC_BGEZL: /* REGIMM branches */ - case OPC_BLTZAL ... OPC_BGEZALL: - gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2); + case OPC_BLTZL: /* REGIMM branches */ + case OPC_BGEZL: + case OPC_BLTZALL: + case OPC_BGEZALL: + check_insn_opc_removed(ctx, ISA_MIPS32R6); + case OPC_BLTZ: + case OPC_BGEZ: + gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2, 4); + break; + case OPC_BLTZAL: + case OPC_BGEZAL: + if (ctx->insn_flags & ISA_MIPS32R6) { + if (rs == 0) { + /* OPC_NAL, OPC_BAL */ + gen_compute_branch(ctx, op1, 4, 0, -1, imm << 2, 4); + } else { + generate_exception(ctx, EXCP_RI); + } + } else { + gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2, 4); + } break; case OPC_TGEI ... OPC_TEQI: /* REGIMM traps */ case OPC_TNEI: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_trap(ctx, op1, rs, -1, imm); break; case OPC_SYNCI: check_insn(ctx, ISA_MIPS32R2); - /* Treat as NOP. */ + /* Break the TB to be able to sync copied instructions + immediately */ + ctx->bstate = BS_STOP; break; case OPC_BPOSGE32: /* MIPS DSP branch */ #if defined(TARGET_MIPS64) case OPC_BPOSGE64: #endif check_dsp(ctx); - gen_compute_branch(ctx, op1, 4, -1, -2, (int32_t)imm << 2); + gen_compute_branch(ctx, op1, 4, -1, -2, (int32_t)imm << 2, 4); + break; +#if defined(TARGET_MIPS64) + case OPC_DAHI: + check_insn(ctx, ISA_MIPS32R6); + check_mips_64(ctx); + if (rs != 0) { + tcg_gen_addi_tl(cpu_gpr[rs], cpu_gpr[rs], (int64_t)imm << 32); + } + MIPS_DEBUG("dahi %s, %04x", regnames[rs], imm); break; + case OPC_DATI: + check_insn(ctx, ISA_MIPS32R6); + check_mips_64(ctx); + if (rs != 0) { + tcg_gen_addi_tl(cpu_gpr[rs], cpu_gpr[rs], (int64_t)imm << 48); + } + MIPS_DEBUG("dati %s, %04x", regnames[rs], imm); + break; +#endif default: /* Invalid */ MIPS_INVAL("regimm"); generate_exception(ctx, EXCP_RI); @@ -15376,6 +16905,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) case OPC_MFMC0: #ifndef CONFIG_USER_ONLY { + uint32_t op2; TCGv t0 = tcg_temp_new(); op2 = MASK_MFMC0(ctx->opcode); @@ -15439,7 +16969,16 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) break; } break; - case OPC_ADDI: /* Arithmetic with immediate opcode */ + case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC, OPC_ADDI */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_BOVC, OPC_BEQZALC, OPC_BEQC */ + gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); + } else { + /* OPC_ADDI */ + /* Arithmetic with immediate opcode */ + gen_arith_imm(ctx, op, rt, rs, imm); + } + break; case OPC_ADDIU: gen_arith_imm(ctx, op, rt, rs, imm); break; @@ -15448,36 +16987,96 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) gen_slt_imm(ctx, op, rt, rs, imm); break; case OPC_ANDI: /* Arithmetic with immediate opcode */ - case OPC_LUI: + case OPC_LUI: /* OPC_AUI */ case OPC_ORI: case OPC_XORI: gen_logic_imm(ctx, op, rt, rs, imm); break; case OPC_J ... OPC_JAL: /* Jump */ offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; - gen_compute_branch(ctx, op, 4, rs, rt, offset); + gen_compute_branch(ctx, op, 4, rs, rt, offset, 4); break; - case OPC_BEQ ... OPC_BGTZ: /* Branch */ - case OPC_BEQL ... OPC_BGTZL: - gen_compute_branch(ctx, op, 4, rs, rt, imm << 2); + /* Branch */ + case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC, OPC_BLEZL */ + if (ctx->insn_flags & ISA_MIPS32R6) { + if (rt == 0) { + generate_exception(ctx, EXCP_RI); + break; + } + /* OPC_BLEZC, OPC_BGEZC, OPC_BGEC */ + gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); + } else { + /* OPC_BLEZL */ + gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); + } + break; + case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC, OPC_BGTZL */ + if (ctx->insn_flags & ISA_MIPS32R6) { + if (rt == 0) { + generate_exception(ctx, EXCP_RI); + break; + } + /* OPC_BGTZC, OPC_BLTZC, OPC_BLTC */ + gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); + } else { + /* OPC_BGTZL */ + gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); + } + break; + case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC, OPC_BLEZ */ + if (rt == 0) { + /* OPC_BLEZ */ + gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); + } else { + check_insn(ctx, ISA_MIPS32R6); + /* OPC_BLEZALC, OPC_BGEZALC, OPC_BGEUC */ + gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); + } + break; + case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC, OPC_BGTZ */ + if (rt == 0) { + /* OPC_BGTZ */ + gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); + } else { + check_insn(ctx, ISA_MIPS32R6); + /* OPC_BGTZALC, OPC_BLTZALC, OPC_BLTUC */ + gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); + } + break; + case OPC_BEQL: + case OPC_BNEL: + check_insn_opc_removed(ctx, ISA_MIPS32R6); + case OPC_BEQ: + case OPC_BNE: + gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); break; - case OPC_LB ... OPC_LWR: /* Load and stores */ + case OPC_LWL: /* Load and stores */ + case OPC_LWR: case OPC_LL: + check_insn_opc_removed(ctx, ISA_MIPS32R6); + case OPC_LB ... OPC_LH: + case OPC_LW ... OPC_LHU: gen_ld(ctx, op, rt, rs, imm); break; - case OPC_SB ... OPC_SW: + case OPC_SWL: case OPC_SWR: + check_insn_opc_removed(ctx, ISA_MIPS32R6); + case OPC_SB ... OPC_SH: + case OPC_SW: gen_st(ctx, op, rt, rs, imm); break; case OPC_SC: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_st_cond(ctx, op, rt, rs, imm); break; case OPC_CACHE: + check_insn_opc_removed(ctx, ISA_MIPS32R6); check_cp0_enabled(ctx); check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); /* Treat as NOP. */ break; case OPC_PREF: + check_insn_opc_removed(ctx, ISA_MIPS32R6); check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); /* Treat as NOP. */ break; @@ -15511,23 +17110,106 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) gen_cp1(ctx, op1, rt, rd); break; #endif - case OPC_BC1ANY2: + case OPC_BC1EQZ: /* OPC_BC1ANY2 */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_BC1EQZ */ + gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), + rt, imm << 2); + } else { + /* OPC_BC1ANY2 */ + check_cop1x(ctx); + check_insn(ctx, ASE_MIPS3D); + gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), + (rt >> 2) & 0x7, imm << 2); + } + break; + case OPC_BC1NEZ: + check_insn(ctx, ISA_MIPS32R6); + gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), + rt, imm << 2); + break; case OPC_BC1ANY4: + check_insn_opc_removed(ctx, ISA_MIPS32R6); check_cop1x(ctx); check_insn(ctx, ASE_MIPS3D); /* fall through */ case OPC_BC1: + check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), (rt >> 2) & 0x7, imm << 2); break; + case OPC_PS_FMT: + check_insn_opc_removed(ctx, ISA_MIPS32R6); case OPC_S_FMT: case OPC_D_FMT: - case OPC_W_FMT: - case OPC_L_FMT: - case OPC_PS_FMT: gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, (imm >> 8) & 0x7); break; + case OPC_W_FMT: + case OPC_L_FMT: + { + int r6_op = ctx->opcode & FOP(0x3f, 0x1f); + if (ctx->insn_flags & ISA_MIPS32R6) { + switch (r6_op) { + case R6_OPC_CMP_AF_S: + case R6_OPC_CMP_UN_S: + case R6_OPC_CMP_EQ_S: + case R6_OPC_CMP_UEQ_S: + case R6_OPC_CMP_LT_S: + case R6_OPC_CMP_ULT_S: + case R6_OPC_CMP_LE_S: + case R6_OPC_CMP_ULE_S: + case R6_OPC_CMP_SAF_S: + case R6_OPC_CMP_SUN_S: + case R6_OPC_CMP_SEQ_S: + case R6_OPC_CMP_SEUQ_S: + case R6_OPC_CMP_SLT_S: + case R6_OPC_CMP_SULT_S: + case R6_OPC_CMP_SLE_S: + case R6_OPC_CMP_SULE_S: + case R6_OPC_CMP_OR_S: + case R6_OPC_CMP_UNE_S: + case R6_OPC_CMP_NE_S: + case R6_OPC_CMP_SOR_S: + case R6_OPC_CMP_SUNE_S: + case R6_OPC_CMP_SNE_S: + gen_r6_cmp_s(ctx, ctx->opcode & 0x1f, rt, rd, sa); + break; + case R6_OPC_CMP_AF_D: + case R6_OPC_CMP_UN_D: + case R6_OPC_CMP_EQ_D: + case R6_OPC_CMP_UEQ_D: + case R6_OPC_CMP_LT_D: + case R6_OPC_CMP_ULT_D: + case R6_OPC_CMP_LE_D: + case R6_OPC_CMP_ULE_D: + case R6_OPC_CMP_SAF_D: + case R6_OPC_CMP_SUN_D: + case R6_OPC_CMP_SEQ_D: + case R6_OPC_CMP_SEUQ_D: + case R6_OPC_CMP_SLT_D: + case R6_OPC_CMP_SULT_D: + case R6_OPC_CMP_SLE_D: + case R6_OPC_CMP_SULE_D: + case R6_OPC_CMP_OR_D: + case R6_OPC_CMP_UNE_D: + case R6_OPC_CMP_NE_D: + case R6_OPC_CMP_SOR_D: + case R6_OPC_CMP_SUNE_D: + case R6_OPC_CMP_SNE_D: + gen_r6_cmp_d(ctx, ctx->opcode & 0x1f, rt, rd, sa); + break; + default: + gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, + (imm >> 8) & 0x7); + break; + } + } else { + gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, + (imm >> 8) & 0x7); + } + break; + } default: MIPS_INVAL("cp1"); generate_exception (ctx, EXCP_RI); @@ -15538,13 +17220,35 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) } break; - /* COP2. */ - case OPC_LWC2: - case OPC_LDC2: - case OPC_SWC2: - case OPC_SDC2: - /* COP2: Not implemented. */ - generate_exception_err(ctx, EXCP_CpU, 2); + /* Compact branches [R6] and COP2 [non-R6] */ + case OPC_BC: /* OPC_LWC2 */ + case OPC_BALC: /* OPC_SWC2 */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_BC, OPC_BALC */ + gen_compute_compact_branch(ctx, op, 0, 0, + sextract32(ctx->opcode << 2, 0, 28)); + } else { + /* OPC_LWC2, OPC_SWC2 */ + /* COP2: Not implemented. */ + generate_exception_err(ctx, EXCP_CpU, 2); + } + break; + case OPC_BEQZC: /* OPC_JIC, OPC_LDC2 */ + case OPC_BNEZC: /* OPC_JIALC, OPC_SDC2 */ + if (ctx->insn_flags & ISA_MIPS32R6) { + if (rs != 0) { + /* OPC_BEQZC, OPC_BNEZC */ + gen_compute_compact_branch(ctx, op, rs, 0, + sextract32(ctx->opcode << 2, 0, 23)); + } else { + /* OPC_JIC, OPC_JIALC */ + gen_compute_compact_branch(ctx, op, 0, rt, imm); + } + } else { + /* OPC_LWC2, OPC_SWC2 */ + /* COP2: Not implemented. */ + generate_exception_err(ctx, EXCP_CpU, 2); + } break; case OPC_CP2: check_insn(ctx, INSN_LOONGSON2F); @@ -15553,6 +17257,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) break; case OPC_CP3: + check_insn_opc_removed(ctx, ISA_MIPS32R6); if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { check_cp1_enabled(ctx); op1 = MASK_CP3(ctx->opcode); @@ -15595,40 +17300,85 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) #if defined(TARGET_MIPS64) /* MIPS64 opcodes */ - case OPC_LWU: case OPC_LDL ... OPC_LDR: case OPC_LLD: + check_insn_opc_removed(ctx, ISA_MIPS32R6); + case OPC_LWU: case OPC_LD: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_ld(ctx, op, rt, rs, imm); break; case OPC_SDL ... OPC_SDR: + check_insn_opc_removed(ctx, ISA_MIPS32R6); case OPC_SD: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_st(ctx, op, rt, rs, imm); break; case OPC_SCD: + check_insn_opc_removed(ctx, ISA_MIPS32R6); check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_st_cond(ctx, op, rt, rs, imm); break; - case OPC_DADDI: + case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC, OPC_DADDI */ + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_BNVC, OPC_BNEZALC, OPC_BNEC */ + gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); + } else { + /* OPC_DADDI */ + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); + gen_arith_imm(ctx, op, rt, rs, imm); + } + break; case OPC_DADDIU: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_arith_imm(ctx, op, rt, rs, imm); break; +#else + case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ + if (ctx->insn_flags & ISA_MIPS32R6) { + gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); + } else { + MIPS_INVAL("major opcode"); + generate_exception(ctx, EXCP_RI); + } + break; #endif - case OPC_JALX: - check_insn(ctx, ASE_MIPS16 | ASE_MICROMIPS); - offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; - gen_compute_branch(ctx, op, 4, rs, rt, offset); + case OPC_DAUI: /* OPC_JALX */ + if (ctx->insn_flags & ISA_MIPS32R6) { +#if defined(TARGET_MIPS64) + /* OPC_DAUI */ + check_mips_64(ctx); + if (rt != 0) { + TCGv t0 = tcg_temp_new(); + gen_load_gpr(t0, rs); + tcg_gen_addi_tl(cpu_gpr[rt], t0, imm << 16); + tcg_temp_free(t0); + } + MIPS_DEBUG("daui %s, %s, %04x", regnames[rt], regnames[rs], imm); +#else + generate_exception(ctx, EXCP_RI); + MIPS_INVAL("major opcode"); +#endif + } else { + /* OPC_JALX */ + check_insn(ctx, ASE_MIPS16 | ASE_MICROMIPS); + offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; + gen_compute_branch(ctx, op, 4, rs, rt, offset, 4); + } break; case OPC_MDMX: check_insn(ctx, ASE_MDMX); /* MDMX: Not implemented. */ + break; + case OPC_PCREL: + check_insn(ctx, ISA_MIPS32R6); + gen_pcrel(ctx, rs, imm); + break; default: /* Invalid */ MIPS_INVAL("major opcode"); generate_exception(ctx, EXCP_RI); @@ -15726,8 +17476,15 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, ctx.bstate = BS_STOP; break; } + + if (ctx.hflags & MIPS_HFLAG_BMASK) { + if (!(ctx.hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32))) { + is_delay = 1; + /* force to generate branch as no delay slot is required */ + } + } if (is_delay) { - handle_delay_slot(&ctx, insn_bytes); + gen_branch(&ctx, insn_bytes); } ctx.pc += insn_bytes; @@ -15944,9 +17701,6 @@ void mips_tcg_init(void) cpu_LO[i] = tcg_global_mem_new(TCG_AREG0, offsetof(CPUMIPSState, active_tc.LO[i]), regnames_LO[i]); - cpu_ACX[i] = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUMIPSState, active_tc.ACX[i]), - regnames_ACX[i]); } cpu_dspctrl = tcg_global_mem_new(TCG_AREG0, offsetof(CPUMIPSState, active_tc.DSPControl), @@ -16134,6 +17888,12 @@ void cpu_state_reset(CPUMIPSState *env) } } #endif + if ((env->insn_flags & ISA_MIPS32R6) && + (env->active_fpu.fcr0 & (1 << FCR0_F64))) { + /* Status.FR = 0 mode in 64-bit FPU not allowed in R6 */ + env->CP0_Status |= (1 << CP0St_FR); + } + compute_hflags(env); cs->exception_index = EXCP_NONE; } diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index 29dc2ef738..67b7837750 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -516,6 +516,36 @@ static const mips_def_t mips_defs[] = .mmu_type = MMU_TYPE_R4000, }, { + /* A generic CPU supporting MIPS64 Release 6 ISA. + FIXME: It does not support all the MIPS64R6 features yet. + Eventually this should be replaced by a real CPU model. */ + .name = "MIPS64R6-generic", + .CP0_PRid = 0x00010000, + .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AR) | (0x2 << CP0C0_AT) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) | + (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | + (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | + (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3, + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 0, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x30D8FFFF, + .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | + (1 << FCR0_D) | (1 << FCR0_S) | (0x00 << FCR0_PRID) | + (0x0 << FCR0_REV), + .SEGBITS = 42, + /* The architectural limit is 59, but we have hardcoded 36 bit + in some places... + .PABITS = 59, */ /* the architectural limit */ + .PABITS = 36, + .insn_flags = CPU_MIPS64R6, + .mmu_type = MMU_TYPE_R4000, + }, + { .name = "Loongson-2E", .CP0_PRid = 0x6302, /*64KB I-cache and d-cache. 4 way with 32 bit cache line size*/ diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 65b840da03..33fb4cc9c3 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8044,7 +8044,7 @@ static void powerpc_set_compat(Object *obj, Visitor *v, static PropertyInfo powerpc_compat_propinfo = { .name = "str", - .legacy_name = "powerpc-server-compat", + .description = "compatibility mode, power6/power7/power8", .get = powerpc_get_compat, .set = powerpc_set_compat, }; diff --git a/tests/Makefile b/tests/Makefile index ffa8312eb5..16f0e4c805 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -258,8 +258,8 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ $(test-qapi-obj-y) \ libqemuutil.a libqemustub.a tests/test-vmstate$(EXESUF): tests/test-vmstate.o \ - vmstate.o qemu-file.o \ - libqemuutil.a + vmstate.o qemu-file.o qemu-file-unix.o \ + libqemuutil.a libqemustub.a tests/test-qapi-types.c tests/test-qapi-types.h :\ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py @@ -301,6 +301,7 @@ libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o libqos-pc-obj-y += tests/libqos/malloc-pc.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o libqos-virtio-obj-y = $(libqos-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o +libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o tests/rtc-test$(EXESUF): tests/rtc-test.o tests/m48t59-test$(EXESUF): tests/m48t59-test.o @@ -324,8 +325,8 @@ tests/ne2000-test$(EXESUF): tests/ne2000-test.o tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y) -tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o -tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o +tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) +tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y) tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o tests/virtio-9p-test$(EXESUF): tests/virtio-9p-test.o tests/virtio-serial-test$(EXESUF): tests/virtio-serial-test.o @@ -343,10 +344,10 @@ tests/ac97-test$(EXESUF): tests/ac97-test.o tests/es1370-test$(EXESUF): tests/es1370-test.o tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o -tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o -tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o -tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-pc-obj-y) -tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o +tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y) +tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y) +tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y) +tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index 0609294af0..6dba0db00a 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -20,6 +20,9 @@ #include <glib.h> +#define ACPI_PCIHP_ADDR 0xae00 +#define PCI_EJ_BASE 0x0008 + typedef struct QPCIBusPC { QPCIBus bus; @@ -247,3 +250,49 @@ void qpci_free_pc(QPCIBus *bus) g_free(s); } + +void qpci_plug_device_test(const char *driver, const char *id, + uint8_t slot, const char *opts) +{ + QDict *response; + char *cmd; + + cmd = g_strdup_printf("{'execute': 'device_add'," + " 'arguments': {" + " 'driver': '%s'," + " 'addr': '%d'," + " %s%s" + " 'id': '%s'" + "}}", driver, slot, + opts ? opts : "", opts ? "," : "", + id); + response = qmp(cmd); + g_free(cmd); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); +} + +void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) +{ + QDict *response; + char *cmd; + + cmd = g_strdup_printf("{'execute': 'device_del'," + " 'arguments': {" + " 'id': '%s'" + "}}", id); + response = qmp(cmd); + g_free(cmd); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); + + response = qmp(""); + g_assert(response); + g_assert(qdict_haskey(response, "event")); + g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); + QDECREF(response); +} diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index d51eb9e219..dfaee9ec37 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -87,4 +87,7 @@ void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value); void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr); void qpci_iounmap(QPCIDevice *dev, void *data); +void qpci_plug_device_test(const char *driver, const char *id, + uint8_t slot, const char *opts); +void qpci_unplug_acpi_device_test(const char *id, uint8_t slot); #endif diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c new file mode 100644 index 0000000000..41d89b8487 --- /dev/null +++ b/tests/libqos/usb.c @@ -0,0 +1,71 @@ +/* + * common code shared by usb tests + * + * Copyright (c) 2014 Red Hat, Inc + * + * Authors: + * Gerd Hoffmann <kraxel@redhat.com> + * John Snow <jsnow@redhat.com> + * Igor Mammedov <imammedo@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 <string.h> +#include "libqtest.h" +#include "qemu/osdep.h" +#include "hw/usb/uhci-regs.h" +#include "libqos/usb.h" + +void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, uint32_t devfn, int bar) +{ + hc->dev = qpci_device_find(pcibus, devfn); + g_assert(hc->dev != NULL); + qpci_device_enable(hc->dev); + hc->base = qpci_iomap(hc->dev, bar, NULL); + g_assert(hc->base != NULL); +} + +void uhci_port_test(struct qhc *hc, int port, uint16_t expect) +{ + void *addr = hc->base + 0x10 + 2 * port; + uint16_t value = qpci_io_readw(hc->dev, addr); + uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1); + + g_assert((value & mask) == (expect & mask)); +} + +void usb_test_hotplug(const char *hcd_id, const int port, + void (*port_check)(void)) +{ + QDict *response; + char *cmd; + + cmd = g_strdup_printf("{'execute': 'device_add'," + " 'arguments': {" + " 'driver': 'usb-tablet'," + " 'port': '%d'," + " 'bus': '%s.0'," + " 'id': 'usbdev%d'" + "}}", port, hcd_id, port); + response = qmp(cmd); + g_free(cmd); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + if (port_check) { + port_check(); + } + + cmd = g_strdup_printf("{'execute': 'device_del'," + " 'arguments': {" + " 'id': 'usbdev%d'" + "}}", port); + response = qmp(cmd); + g_free(cmd); + g_assert(response); + g_assert(qdict_haskey(response, "event")); + g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); +} diff --git a/tests/libqos/usb.h b/tests/libqos/usb.h new file mode 100644 index 0000000000..8fe56872b7 --- /dev/null +++ b/tests/libqos/usb.h @@ -0,0 +1,17 @@ +#ifndef LIBQOS_USB_H +#define LIBQOS_USB_H + +#include "libqos/pci-pc.h" + +struct qhc { + QPCIDevice *dev; + void *base; +}; + +void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, + uint32_t devfn, int bar); +void uhci_port_test(struct qhc *hc, int port, uint16_t expect); + +void usb_test_hotplug(const char *bus_name, const int port, + void (*port_check)(void)); +#endif diff --git a/tests/libqtest.h b/tests/libqtest.h index 3e12cab2f2..e7413d52dc 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -23,6 +23,7 @@ #include <stdarg.h> #include <sys/types.h> #include "qapi/qmp/qdict.h" +#include "glib-compat.h" typedef struct QTestState QTestState; diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index d72c64c90b..5e0fd13cc4 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -43,6 +43,12 @@ void yield_until_fd_readable(int fd) select(fd + 1, &fds, NULL, NULL, NULL); } +/* + * Some tests use 'open_test_file' to work on a real fd, some use + * an in memory file (QEMUSizedBuffer+qemu_bufopen); we could pick one + * but this way we test both. + */ + /* Duplicate temp_fd and seek to the beginning of the file */ static QEMUFile *open_test_file(bool write) { @@ -54,6 +60,30 @@ static QEMUFile *open_test_file(bool write) return qemu_fdopen(fd, write ? "wb" : "rb"); } +/* Open a read-only qemu-file from an existing memory block */ +static QEMUFile *open_mem_file_read(const void *data, size_t len) +{ + /* The qsb gets freed by qemu_fclose */ + QEMUSizedBuffer *qsb = qsb_create(data, len); + g_assert(qsb); + + return qemu_bufopen("r", qsb); +} + +/* + * Check that the contents of the memory-buffered file f match + * the given size/data. + */ +static void check_mem_file(QEMUFile *f, void *data, size_t size) +{ + uint8_t *result = g_malloc(size); + const QEMUSizedBuffer *qsb = qemu_buf_get(f); + g_assert_cmpint(qsb_get_length(qsb), ==, size); + g_assert_cmpint(qsb_get_buffer(qsb, 0, size, result), ==, size); + g_assert_cmpint(memcmp(result, data, size), ==, 0); + g_free(result); +} + #define SUCCESS(val) \ g_assert_cmpint((val), ==, 0) @@ -371,14 +401,12 @@ static const VMStateDescription vmstate_skipping = { static void test_save_noskip(void) { - QEMUFile *fsave = open_test_file(true); + QEMUFile *fsave = qemu_bufopen("w", NULL); TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, .skip_c_e = false }; vmstate_save_state(fsave, &vmstate_skipping, &obj); g_assert(!qemu_file_get_error(fsave)); - qemu_fclose(fsave); - QEMUFile *loading = open_test_file(false); uint8_t expected[] = { 0, 0, 0, 1, /* a */ 0, 0, 0, 2, /* b */ @@ -387,52 +415,31 @@ static void test_save_noskip(void) 0, 0, 0, 5, /* e */ 0, 0, 0, 0, 0, 0, 0, 6, /* f */ }; - uint8_t result[sizeof(expected)]; - g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==, - sizeof(result)); - g_assert(!qemu_file_get_error(loading)); - g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0); - - /* Must reach EOF */ - qemu_get_byte(loading); - g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO); - - qemu_fclose(loading); + check_mem_file(fsave, expected, sizeof(expected)); + qemu_fclose(fsave); } static void test_save_skip(void) { - QEMUFile *fsave = open_test_file(true); + QEMUFile *fsave = qemu_bufopen("w", NULL); TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, .skip_c_e = true }; vmstate_save_state(fsave, &vmstate_skipping, &obj); g_assert(!qemu_file_get_error(fsave)); - qemu_fclose(fsave); - QEMUFile *loading = open_test_file(false); uint8_t expected[] = { 0, 0, 0, 1, /* a */ 0, 0, 0, 2, /* b */ 0, 0, 0, 0, 0, 0, 0, 4, /* d */ 0, 0, 0, 0, 0, 0, 0, 6, /* f */ }; - uint8_t result[sizeof(expected)]; - g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==, - sizeof(result)); - g_assert(!qemu_file_get_error(loading)); - g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0); - - - /* Must reach EOF */ - qemu_get_byte(loading); - g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO); + check_mem_file(fsave, expected, sizeof(expected)); - qemu_fclose(loading); + qemu_fclose(fsave); } static void test_load_noskip(void) { - QEMUFile *fsave = open_test_file(true); uint8_t buf[] = { 0, 0, 0, 10, /* a */ 0, 0, 0, 20, /* b */ @@ -442,10 +449,8 @@ static void test_load_noskip(void) 0, 0, 0, 0, 0, 0, 0, 60, /* f */ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ }; - qemu_put_buffer(fsave, buf, sizeof(buf)); - qemu_fclose(fsave); - QEMUFile *loading = open_test_file(false); + QEMUFile *loading = open_mem_file_read(buf, sizeof(buf)); TestStruct obj = { .skip_c_e = false }; vmstate_load_state(loading, &vmstate_skipping, &obj, 2); g_assert(!qemu_file_get_error(loading)); @@ -460,7 +465,6 @@ static void test_load_noskip(void) static void test_load_skip(void) { - QEMUFile *fsave = open_test_file(true); uint8_t buf[] = { 0, 0, 0, 10, /* a */ 0, 0, 0, 20, /* b */ @@ -468,10 +472,8 @@ static void test_load_skip(void) 0, 0, 0, 0, 0, 0, 0, 60, /* f */ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ }; - qemu_put_buffer(fsave, buf, sizeof(buf)); - qemu_fclose(fsave); - QEMUFile *loading = open_test_file(false); + QEMUFile *loading = open_mem_file_read(buf, sizeof(buf)); TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; vmstate_load_state(loading, &vmstate_skipping, &obj, 2); g_assert(!qemu_file_get_error(loading)); diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c index c990492835..75073bf24e 100644 --- a/tests/usb-hcd-ehci-test.c +++ b/tests/usb-hcd-ehci-test.c @@ -15,11 +15,7 @@ #include "qemu/osdep.h" #include "hw/usb/uhci-regs.h" #include "hw/usb/ehci-regs.h" - -struct qhc { - QPCIDevice *dev; - void *base; -}; +#include "libqos/usb.h" static QPCIBus *pcibus; static struct qhc uhci1; @@ -29,15 +25,6 @@ static struct qhc ehci1; /* helpers */ -static void pci_init_one(struct qhc *hc, uint32_t devfn, int bar) -{ - hc->dev = qpci_device_find(pcibus, devfn); - g_assert(hc->dev != NULL); - qpci_device_enable(hc->dev); - hc->base = qpci_iomap(hc->dev, bar, NULL); - g_assert(hc->base != NULL); -} - #if 0 static void uhci_port_update(struct qhc *hc, int port, uint16_t set, uint16_t clear) @@ -52,19 +39,6 @@ static void uhci_port_update(struct qhc *hc, int port, } #endif -static void uhci_port_test(struct qhc *hc, int port, uint16_t expect) -{ - void *addr = hc->base + 0x10 + 2 * port; - uint16_t value = qpci_io_readw(hc->dev, addr); - uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1); - -#if 0 - fprintf(stderr, "%s: %d, have 0x%04x, want 0x%04x\n", - __func__, port, value & mask, expect & mask); -#endif - g_assert((value & mask) == (expect & mask)); -} - static void ehci_port_test(struct qhc *hc, int port, uint32_t expect) { void *addr = hc->base + 0x64 + 4 * port; @@ -88,10 +62,10 @@ static void pci_init(void) pcibus = qpci_init_pc(); g_assert(pcibus != NULL); - pci_init_one(&uhci1, QPCI_DEVFN(0x1d, 0), 4); - pci_init_one(&uhci2, QPCI_DEVFN(0x1d, 1), 4); - pci_init_one(&uhci3, QPCI_DEVFN(0x1d, 2), 4); - pci_init_one(&ehci1, QPCI_DEVFN(0x1d, 7), 0); + qusb_pci_init_one(pcibus, &uhci1, QPCI_DEVFN(0x1d, 0), 4); + qusb_pci_init_one(pcibus, &uhci2, QPCI_DEVFN(0x1d, 1), 4); + qusb_pci_init_one(pcibus, &uhci3, QPCI_DEVFN(0x1d, 2), 4); + qusb_pci_init_one(pcibus, &ehci1, QPCI_DEVFN(0x1d, 7), 0); } static void pci_uhci_port_1(void) @@ -154,6 +128,19 @@ static void pci_ehci_port_2(void) } } +static void pci_ehci_port_3_hotplug(void) +{ + /* check for presence of hotplugged usb-tablet */ + g_assert(pcibus != NULL); + ehci_port_test(&ehci1, 2, PORTSC_PPOWER | PORTSC_CONNECT); +} + +static void pci_ehci_port_hotplug(void) +{ + usb_test_hotplug("ich9-ehci-1", 3, pci_ehci_port_3_hotplug); +} + + int main(int argc, char **argv) { int ret; @@ -165,6 +152,7 @@ int main(int argc, char **argv) qtest_add_func("/ehci/pci/ehci-config", pci_ehci_config); qtest_add_func("/ehci/pci/uhci-port-2", pci_uhci_port_2); qtest_add_func("/ehci/pci/ehci-port-2", pci_ehci_port_2); + qtest_add_func("/ehci/pci/ehci-port-3-hotplug", pci_ehci_port_hotplug); qtest_start("-machine q35 -device ich9-usb-ehci1,bus=pcie.0,addr=1d.7," "multifunction=on,id=ich9-ehci-1 " diff --git a/tests/usb-hcd-ohci-test.c b/tests/usb-hcd-ohci-test.c index fbc3ffeebd..1160bde840 100644 --- a/tests/usb-hcd-ohci-test.c +++ b/tests/usb-hcd-ohci-test.c @@ -11,15 +11,18 @@ #include <string.h> #include "libqtest.h" #include "qemu/osdep.h" +#include "libqos/usb.h" static void test_ohci_init(void) { - qtest_start("-device pci-ohci,id=ohci"); - qtest_end(); } +static void test_ohci_hotplug(void) +{ + usb_test_hotplug("ohci", 1, NULL); +} int main(int argc, char **argv) { @@ -28,8 +31,11 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/ohci/pci/init", test_ohci_init); + qtest_add_func("/ohci/pci/hotplug", test_ohci_hotplug); + qtest_start("-device pci-ohci,id=ohci"); ret = g_test_run(); + qtest_end(); return ret; } diff --git a/tests/usb-hcd-uhci-test.c b/tests/usb-hcd-uhci-test.c index 94e858f4c6..8cf2c5bcaa 100644 --- a/tests/usb-hcd-uhci-test.c +++ b/tests/usb-hcd-uhci-test.c @@ -11,15 +11,69 @@ #include <string.h> #include "libqtest.h" #include "qemu/osdep.h" +#include "libqos/usb.h" +#include "hw/usb/uhci-regs.h" static void test_uhci_init(void) { - qtest_start("-device piix3-usb-uhci,id=uhci"); +} - qtest_end(); +static void test_port(int port) +{ + QPCIBus *pcibus; + struct qhc uhci; + + g_assert(port > 0); + pcibus = qpci_init_pc(); + g_assert(pcibus != NULL); + qusb_pci_init_one(pcibus, &uhci, QPCI_DEVFN(0x1d, 0), 4); + uhci_port_test(&uhci, port - 1, UHCI_PORT_CCS); +} + +static void test_port_1(void) +{ + test_port(1); } +static void test_port_2(void) +{ + test_port(2); +} + +static void test_uhci_hotplug(void) +{ + usb_test_hotplug("uhci", 2, test_port_2); +} + +static void test_usb_storage_hotplug(void) +{ + QDict *response; + + response = qmp("{'execute': 'device_add'," + " 'arguments': {" + " 'driver': 'usb-storage'," + " 'drive': 'drive0'," + " 'id': 'usbdev0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{'execute': 'device_del'," + " 'arguments': {" + " 'id': 'usbdev0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp(""); + g_assert(response); + g_assert(qdict_haskey(response, "event")); + g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); + QDECREF(response); +} int main(int argc, char **argv) { @@ -28,8 +82,15 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/uhci/pci/init", test_uhci_init); + qtest_add_func("/uhci/pci/port1", test_port_1); + qtest_add_func("/uhci/pci/hotplug", test_uhci_hotplug); + qtest_add_func("/uhci/pci/hotplug/usb-storage", test_usb_storage_hotplug); + qtest_start("-device piix3-usb-uhci,id=uhci,addr=1d.0" + " -drive id=drive0,if=none,file=/dev/null" + " -device usb-tablet,bus=uhci.0,port=1"); ret = g_test_run(); + qtest_end(); return ret; } diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c index 743e9798cd..b1a7dec5bb 100644 --- a/tests/usb-hcd-xhci-test.c +++ b/tests/usb-hcd-xhci-test.c @@ -11,15 +11,74 @@ #include <string.h> #include "libqtest.h" #include "qemu/osdep.h" +#include "libqos/usb.h" static void test_xhci_init(void) { - qtest_start("-device nec-usb-xhci,id=xhci"); +} - qtest_end(); +static void test_xhci_hotplug(void) +{ + usb_test_hotplug("xhci", 1, NULL); } +static void test_usb_uas_hotplug(void) +{ + QDict *response; + + response = qmp("{'execute': 'device_add'," + " 'arguments': {" + " 'driver': 'usb-uas'," + " 'id': 'uas'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{'execute': 'device_add'," + " 'arguments': {" + " 'driver': 'scsi-hd'," + " 'drive': 'drive0'," + " 'id': 'scsi-hd'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + /* TODO: + UAS HBA driver in libqos, to check that + added disk is visible after BUS rescan + */ + + response = qmp("{'execute': 'device_del'," + " 'arguments': {" + " 'id': 'scsi-hd'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp(""); + g_assert(qdict_haskey(response, "event")); + g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); + QDECREF(response); + + + response = qmp("{'execute': 'device_del'," + " 'arguments': {" + " 'id': 'uas'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp(""); + g_assert(response); + g_assert(qdict_haskey(response, "event")); + g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); + QDECREF(response); +} int main(int argc, char **argv) { @@ -28,8 +87,13 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/xhci/pci/init", test_xhci_init); + qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); + qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); + qtest_start("-device nec-usb-xhci,id=xhci" + " -drive id=drive0,if=none,file=/dev/null"); ret = g_test_run(); + qtest_end(); return ret; } diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 75fedf0977..fdf91e7897 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -21,15 +21,6 @@ #include <sys/vfs.h> #include <qemu/sockets.h> -/* GLIB version compatibility flags */ -#if !GLIB_CHECK_VERSION(2, 26, 0) -#define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000)) -#endif - -#if GLIB_CHECK_VERSION(2, 28, 0) -#define HAVE_MONOTONIC_TIME -#endif - #if GLIB_CHECK_VERSION(2, 32, 0) #define HAVE_MUTEX_INIT #define HAVE_COND_INIT @@ -116,18 +107,6 @@ static VhostUserMemory memory; static GMutex *data_mutex; static GCond *data_cond; -static gint64 _get_time(void) -{ -#ifdef HAVE_MONOTONIC_TIME - return g_get_monotonic_time(); -#else - GTimeVal time; - g_get_current_time(&time); - - return time.tv_sec * G_TIME_SPAN_SECOND + time.tv_usec; -#endif -} - static GMutex *_mutex_new(void) { GMutex *mutex; @@ -210,7 +189,7 @@ static void read_guest_mem(void) g_mutex_lock(data_mutex); - end_time = _get_time() + 5 * G_TIME_SPAN_SECOND; + end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; while (!fds_num) { if (!_cond_wait_until(data_cond, data_mutex, end_time)) { /* timeout has passed */ diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 5ce6e79757..ead3911e28 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -45,6 +45,8 @@ #define PCI_SLOT 0x04 #define PCI_FN 0x00 +#define PCI_SLOT_HP 0x06 + typedef struct QVirtioBlkReq { uint32_t type; uint32_t ioprio; @@ -55,7 +57,7 @@ typedef struct QVirtioBlkReq { static QPCIBus *test_start(void) { - char cmdline[100]; + char *cmdline; char tmp_path[] = "/tmp/qtest.XXXXXX"; int fd, ret; @@ -66,11 +68,14 @@ static QPCIBus *test_start(void) g_assert_cmpint(ret, ==, 0); close(fd); - snprintf(cmdline, 100, "-drive if=none,id=drive0,file=%s " - "-device virtio-blk-pci,drive=drive0,addr=%x.%x", - tmp_path, PCI_SLOT, PCI_FN); + cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s " + "-drive if=none,id=drive1,file=/dev/null " + "-device virtio-blk-pci,id=drv0,drive=drive0," + "addr=%x.%x", + tmp_path, PCI_SLOT, PCI_FN); qtest_start(cmdline); unlink(tmp_path); + g_free(cmdline); return qpci_init_pc(); } @@ -80,14 +85,14 @@ static void test_end(void) qtest_end(); } -static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus) +static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus, int slot) { QVirtioPCIDevice *dev; dev = qvirtio_pci_device_find(bus, QVIRTIO_BLK_DEVICE_ID); g_assert(dev != NULL); g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID); - g_assert_cmphex(dev->pdev->devfn, ==, ((PCI_SLOT << 3) | PCI_FN)); + g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN)); qvirtio_pci_device_enable(dev); qvirtio_reset(&qvirtio_pci, &dev->vdev); @@ -147,7 +152,7 @@ static void pci_basic(void) bus = test_start(); - dev = virtio_blk_init(bus); + dev = virtio_blk_init(bus, PCI_SLOT); /* MSI-X is not enabled */ addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; @@ -293,7 +298,7 @@ static void pci_indirect(void) bus = test_start(); - dev = virtio_blk_init(bus); + dev = virtio_blk_init(bus, PCI_SLOT); /* MSI-X is not enabled */ addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; @@ -384,7 +389,7 @@ static void pci_config(void) bus = test_start(); - dev = virtio_blk_init(bus); + dev = virtio_blk_init(bus, PCI_SLOT); /* MSI-X is not enabled */ addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; @@ -425,7 +430,7 @@ static void pci_msix(void) bus = test_start(); alloc = pc_alloc_init(); - dev = virtio_blk_init(bus); + dev = virtio_blk_init(bus, PCI_SLOT); qpci_msix_enable(dev->pdev); qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); @@ -534,7 +539,7 @@ static void pci_idx(void) bus = test_start(); alloc = pc_alloc_init(); - dev = virtio_blk_init(bus); + dev = virtio_blk_init(bus, PCI_SLOT); qpci_msix_enable(dev->pdev); qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); @@ -637,6 +642,27 @@ static void pci_idx(void) test_end(); } +static void hotplug(void) +{ + QPCIBus *bus; + QVirtioPCIDevice *dev; + + bus = test_start(); + + /* plug secondary disk */ + qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, + "'drive': 'drive1'"); + + dev = virtio_blk_init(bus, PCI_SLOT_HP); + g_assert(dev); + qvirtio_pci_device_disable(dev); + g_free(dev); + + /* unplug secondary disk */ + qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); + test_end(); +} + int main(int argc, char **argv) { int ret; @@ -648,6 +674,7 @@ int main(int argc, char **argv) g_test_add_func("/virtio/blk/pci/config", pci_config); g_test_add_func("/virtio/blk/pci/msix", pci_msix); g_test_add_func("/virtio/blk/pci/idx", pci_idx); + g_test_add_func("/virtio/blk/pci/hotplug", hotplug); ret = g_test_run(); diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index df99343238..ea7478c278 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -11,18 +11,28 @@ #include <string.h> #include "libqtest.h" #include "qemu/osdep.h" +#include "libqos/pci.h" + +#define PCI_SLOT_HP 0x06 /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) { } +static void hotplug(void) +{ + qpci_plug_device_test("virtio-net-pci", "net1", PCI_SLOT_HP, NULL); + qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP); +} + int main(int argc, char **argv) { int ret; g_test_init(&argc, &argv, NULL); qtest_add_func("/virtio/net/pci/nop", pci_nop); + qtest_add_func("/virtio/net/pci/hotplug", hotplug); qtest_start("-device virtio-net-pci"); ret = g_test_run(); diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c index 402c2060da..41c1cdb1aa 100644 --- a/tests/virtio-rng-test.c +++ b/tests/virtio-rng-test.c @@ -11,18 +11,28 @@ #include <string.h> #include "libqtest.h" #include "qemu/osdep.h" +#include "libqos/pci.h" + +#define PCI_SLOT_HP 0x06 /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) { } +static void hotplug(void) +{ + qpci_plug_device_test("virtio-rng-pci", "rng1", PCI_SLOT_HP, NULL); + qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP); +} + int main(int argc, char **argv) { int ret; g_test_init(&argc, &argv, NULL); qtest_add_func("/virtio/rng/pci/nop", pci_nop); + qtest_add_func("/virtio/rng/pci/hotplug", hotplug); qtest_start("-device virtio-rng-pci"); ret = g_test_run(); diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 3230908b98..41f9602e44 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -17,14 +17,43 @@ static void pci_nop(void) { } +static void hotplug(void) +{ + QDict *response; + + response = qmp("{\"execute\": \"device_add\"," + " \"arguments\": {" + " \"driver\": \"scsi-hd\"," + " \"id\": \"scsi-hd\"," + " \"drive\": \"drv1\"" + "}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{\"execute\": \"device_del\"," + " \"arguments\": {" + " \"id\": \"scsi-hd\"" + "}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + g_assert(qdict_haskey(response, "event")); + g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); + QDECREF(response); +} + int main(int argc, char **argv) { int ret; g_test_init(&argc, &argv, NULL); qtest_add_func("/virtio/scsi/pci/nop", pci_nop); + qtest_add_func("/virtio/scsi/pci/hotplug", hotplug); qtest_start("-drive id=drv0,if=none,file=/dev/null " + "-drive id=drv1,if=none,file=/dev/null " "-device virtio-scsi-pci,id=vscsi0 " "-device scsi-hd,bus=vscsi0.0,drive=drv0"); ret = g_test_run(); diff --git a/tests/virtio-serial-test.c b/tests/virtio-serial-test.c index e7438751ea..bf030a6162 100644 --- a/tests/virtio-serial-test.c +++ b/tests/virtio-serial-test.c @@ -17,12 +17,39 @@ static void pci_nop(void) { } +static void hotplug(void) +{ + QDict *response; + + response = qmp("{\"execute\": \"device_add\"," + " \"arguments\": {" + " \"driver\": \"virtserialport\"," + " \"id\": \"hp-port\"" + "}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + + response = qmp("{\"execute\": \"device_del\"," + " \"arguments\": {" + " \"id\": \"hp-port\"" + "}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + g_assert(qdict_haskey(response, "event")); + g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); + QDECREF(response); +} + int main(int argc, char **argv) { int ret; g_test_init(&argc, &argv, NULL); qtest_add_func("/virtio/serial/pci/nop", pci_nop); + qtest_add_func("/virtio/serial/pci/hotplug", hotplug); qtest_start("-device virtio-serial-pci"); ret = g_test_run(); @@ -931,6 +931,12 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) int qemu_keycode; int i; + if (key->keyval == GDK_KEY_Pause) { + qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE, + key->type == GDK_KEY_PRESS); + return TRUE; + } + qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget), gdk_keycode); @@ -1810,6 +1816,13 @@ static void gd_set_keycode_type(GtkDisplayState *s) fprintf(stderr, "unknown keycodes `%s', please report to " "qemu-devel@nongnu.org\n", keycodes); } + + if (desc) { + XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True); + } + if (keycodes) { + XFree(keycodes); + } } #endif } @@ -180,23 +180,12 @@ int ctrl_grab = 0; unsigned int nb_prom_envs = 0; const char *prom_envs[MAX_PROM_ENVS]; int boot_menu; -static bool boot_strict; +bool boot_strict; uint8_t *boot_splash_filedata; size_t boot_splash_filedata_size; uint8_t qemu_extra_params_fw[2]; int icount_align_option; -typedef struct FWBootEntry FWBootEntry; - -struct FWBootEntry { - QTAILQ_ENTRY(FWBootEntry) link; - int32_t bootindex; - DeviceState *dev; - char *suffix; -}; - -static QTAILQ_HEAD(, FWBootEntry) fw_boot_order = - QTAILQ_HEAD_INITIALIZER(fw_boot_order); int nb_numa_nodes; int max_numa_nodeid; @@ -1246,111 +1235,6 @@ static void restore_boot_order(void *opaque) g_free(normal_boot_order); } -void add_boot_device_path(int32_t bootindex, DeviceState *dev, - const char *suffix) -{ - FWBootEntry *node, *i; - - if (bootindex < 0) { - return; - } - - assert(dev != NULL || suffix != NULL); - - node = g_malloc0(sizeof(FWBootEntry)); - node->bootindex = bootindex; - node->suffix = g_strdup(suffix); - node->dev = dev; - - QTAILQ_FOREACH(i, &fw_boot_order, link) { - if (i->bootindex == bootindex) { - fprintf(stderr, "Two devices with same boot index %d\n", bootindex); - exit(1); - } else if (i->bootindex < bootindex) { - continue; - } - QTAILQ_INSERT_BEFORE(i, node, link); - return; - } - QTAILQ_INSERT_TAIL(&fw_boot_order, node, link); -} - -DeviceState *get_boot_device(uint32_t position) -{ - uint32_t counter = 0; - FWBootEntry *i = NULL; - DeviceState *res = NULL; - - if (!QTAILQ_EMPTY(&fw_boot_order)) { - QTAILQ_FOREACH(i, &fw_boot_order, link) { - if (counter == position) { - res = i->dev; - break; - } - counter++; - } - } - return res; -} - -/* - * This function returns null terminated string that consist of new line - * separated device paths. - * - * memory pointed by "size" is assigned total length of the array in bytes - * - */ -char *get_boot_devices_list(size_t *size, bool ignore_suffixes) -{ - FWBootEntry *i; - size_t total = 0; - char *list = NULL; - - QTAILQ_FOREACH(i, &fw_boot_order, link) { - char *devpath = NULL, *bootpath; - size_t len; - - if (i->dev) { - devpath = qdev_get_fw_dev_path(i->dev); - assert(devpath); - } - - if (i->suffix && !ignore_suffixes && devpath) { - size_t bootpathlen = strlen(devpath) + strlen(i->suffix) + 1; - - bootpath = g_malloc(bootpathlen); - snprintf(bootpath, bootpathlen, "%s%s", devpath, i->suffix); - g_free(devpath); - } else if (devpath) { - bootpath = devpath; - } else if (!ignore_suffixes) { - assert(i->suffix); - bootpath = g_strdup(i->suffix); - } else { - bootpath = g_strdup(""); - } - - if (total) { - list[total-1] = '\n'; - } - len = strlen(bootpath) + 1; - list = g_realloc(list, total + len); - memcpy(&list[total], bootpath, len); - total += len; - g_free(bootpath); - } - - *size = total; - - if (boot_strict && *size > 0) { - list[total-1] = '\n'; - list = g_realloc(list, total + 5); - memcpy(&list[total], "HALT", 5); - *size = total + 5; - } - return list; -} - static QemuOptsList qemu_smp_opts = { .name = "smp-opts", .implied_opt_name = "cpus", @@ -49,9 +49,16 @@ static void *vmstate_base_addr(void *opaque, VMStateField *field, bool alloc) if (field->flags & VMS_POINTER) { if (alloc && (field->flags & VMS_ALLOC)) { - int n_elems = vmstate_n_elems(opaque, field); - if (n_elems) { - gsize size = n_elems * field->size; + gsize size = 0; + if (field->flags & VMS_VBUFFER) { + size = vmstate_size(opaque, field); + } else { + int n_elems = vmstate_n_elems(opaque, field); + if (n_elems) { + size = n_elems * field->size; + } + } + if (size) { *((void **)base_addr + field->start) = g_malloc(size); } } |