diff options
68 files changed, 1733 insertions, 804 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index de0727cb09..ce7850a320 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -7,7 +7,7 @@ freebsd_12_task: cpu: 8 memory: 8G install_script: ASSUME_ALWAYS_YES=yes pkg bootstrap -f ; pkg install -y - bash bison curl cyrus-sasl git glib gmake gnutls gsed + bash curl cyrus-sasl git glib gmake gnutls gsed nettle perl5 pixman pkgconf png usbredir script: - mkdir build diff --git a/.mailmap b/.mailmap index d6bd1b2fec..926cac6bb8 100644 --- a/.mailmap +++ b/.mailmap @@ -45,6 +45,7 @@ Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com> Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com> Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com> Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> +Filip Bozuta <filip.bozuta@syrmia.com> <filip.bozuta@rt-rk.com.com> Frederic Konrad <konrad@adacore.com> <fred.konrad@greensocs.com> James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> Leif Lindholm <leif@nuviainc.com> <leif.lindholm@linaro.org> @@ -52,6 +53,7 @@ Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com> Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com> Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org> Philippe Mathieu-Daudé <philmd@redhat.com> <f4bug@amsat.org> +Stefan Brankovic <stefan.brankovic@syrmia.com> <stefan.brankovic@rt-rk.com.com> Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com> # Also list preferred name forms where people have changed their diff --git a/.shippable.yml b/.shippable.yml index 2cce7b5689..10cf219bff 100644 --- a/.shippable.yml +++ b/.shippable.yml @@ -5,8 +5,8 @@ env: global: - LC_ALL=C matrix: - - IMAGE=debian-amd64 - TARGET_LIST=x86_64-softmmu,x86_64-linux-user + # - IMAGE=debian-amd64 + # TARGET_LIST=x86_64-softmmu,x86_64-linux-user - IMAGE=debian-win32-cross TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu - IMAGE=debian-win64-cross @@ -19,10 +19,10 @@ env: TARGET_LIST=aarch64-softmmu,aarch64-linux-user - IMAGE=debian-s390x-cross TARGET_LIST=s390x-softmmu,s390x-linux-user - - IMAGE=debian-mips-cross - TARGET_LIST=mips-softmmu,mipsel-linux-user - - IMAGE=debian-mips64el-cross - TARGET_LIST=mips64el-softmmu,mips64el-linux-user + # - IMAGE=debian-mips-cross + # TARGET_LIST=mips-softmmu,mipsel-linux-user + # - IMAGE=debian-mips64el-cross + # TARGET_LIST=mips64el-softmmu,mips64el-linux-user - IMAGE=debian-ppc64el-cross TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user build: diff --git a/.travis.yml b/.travis.yml index 564be50a3c..ec6367af1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -429,6 +429,7 @@ jobs: env: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS}" + - UNRELIABLE=true - name: "[ppc64] GCC check-tcg" arch: ppc64le @@ -493,6 +494,7 @@ jobs: env: - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user" + - UNRELIABLE=true script: - ( cd ${SRC_DIR} ; git submodule update --init roms/SLOF ) - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? @@ -535,6 +537,7 @@ jobs: - TEST_CMD="make check-unit" - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools --host-cc=clang --cxx=clang++" + - UNRELIABLE=true # Release builds # The make-release script expect a QEMU version, so our tag must start with a 'v'. @@ -556,3 +559,5 @@ jobs: - mkdir -p release-build && cd release-build - ../configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } - make install + allow_failures: + - env: UNRELIABLE=true diff --git a/MAINTAINERS b/MAINTAINERS index 6e7890ce82..3abe3faa4e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1864,6 +1864,13 @@ S: Maintained F: include/hw/misc/unimp.h F: hw/misc/unimp.c +Empty slot +M: Artyom Tarasenko <atar4qemu@gmail.com> +R: Philippe Mathieu-Daudé <f4bug@amsat.org> +S: Maintained +F: include/hw/misc/empty_slot.h +F: hw/misc/empty_slot.c + Standard VGA M: Gerd Hoffmann <kraxel@redhat.com> S: Maintained diff --git a/exec.c b/exec.c index be4be2df3a..a0bf9d61c8 100644 --- a/exec.c +++ b/exec.c @@ -1038,6 +1038,7 @@ int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, int flags, CPUWatchpoint **watchpoint) { CPUWatchpoint *wp; + vaddr in_page; /* forbid ranges which are empty or run off the end of the address space */ if (len == 0 || (addr + len - 1) < addr) { @@ -1058,7 +1059,12 @@ int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry); } - tlb_flush_page(cpu, addr); + in_page = -(addr | TARGET_PAGE_MASK); + if (len <= in_page) { + tlb_flush_page(cpu, addr); + } else { + tlb_flush(cpu); + } if (watchpoint) *watchpoint = wp; diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 1d540ed6e7..d8fee8effe 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -24,7 +24,6 @@ common-obj-$(CONFIG_SOFTMMU) += numa.o common-obj-$(CONFIG_SOFTMMU) += clock-vmstate.o obj-$(CONFIG_SOFTMMU) += machine-qmp-cmds.o -common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o common-obj-$(CONFIG_XILINX_AXI) += stream.o common-obj-$(CONFIG_PTIMER) += ptimer.o common-obj-$(CONFIG_FITLOADER) += loader-fit.o diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 636c95d1fe..62063b2305 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -52,7 +52,7 @@ #include "sysemu/runstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "hw/empty_slot.h" +#include "hw/misc/empty_slot.h" #include "sysemu/kvm.h" #include "hw/semihosting/semihost.h" #include "hw/mips/cps.h" @@ -1241,7 +1241,7 @@ void mips_malta_init(MachineState *machine) * exception when accessing invalid memory. Create an empty slot to * emulate this feature. */ - empty_slot_init(0, 0x20000000); + empty_slot_init("GT64120", 0, 0x20000000); qdev_init_nofail(dev); diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 60a9d80b74..5aaca8a039 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -10,6 +10,7 @@ common-obj-$(CONFIG_EDU) += edu.o common-obj-$(CONFIG_PCA9552) += pca9552.o common-obj-$(CONFIG_UNIMP) += unimp.o +common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o common-obj-$(CONFIG_FW_CFG_DMA) += vmcoreinfo.o # ARM devices diff --git a/hw/core/empty_slot.c b/hw/misc/empty_slot.c index 3ba450e1ca..b568ae202b 100644 --- a/hw/core/empty_slot.c +++ b/hw/misc/empty_slot.c @@ -11,17 +11,9 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "qemu/module.h" -#include "hw/empty_slot.h" - -//#define DEBUG_EMPTY_SLOT - -#ifdef DEBUG_EMPTY_SLOT -#define DPRINTF(fmt, ...) \ - do { printf("empty_slot: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif +#include "hw/qdev-properties.h" +#include "hw/misc/empty_slot.h" +#include "trace.h" #define TYPE_EMPTY_SLOT "empty_slot" #define EMPTY_SLOT(obj) OBJECT_CHECK(EmptySlot, (obj), TYPE_EMPTY_SLOT) @@ -30,20 +22,26 @@ typedef struct EmptySlot { SysBusDevice parent_obj; MemoryRegion iomem; + char *name; uint64_t size; } EmptySlot; static uint64_t empty_slot_read(void *opaque, hwaddr addr, unsigned size) { - DPRINTF("read from " TARGET_FMT_plx "\n", addr); + EmptySlot *s = EMPTY_SLOT(opaque); + + trace_empty_slot_write(addr, size << 1, 0, size, s->name); + return 0; } static void empty_slot_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", (unsigned)val, addr); + EmptySlot *s = EMPTY_SLOT(opaque); + + trace_empty_slot_write(addr, size << 1, val, size, s->name); } static const MemoryRegionOps empty_slot_ops = { @@ -52,22 +50,18 @@ static const MemoryRegionOps empty_slot_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -void empty_slot_init(hwaddr addr, uint64_t slot_size) +void empty_slot_init(const char *name, hwaddr addr, uint64_t slot_size) { if (slot_size > 0) { /* Only empty slots larger than 0 byte need handling. */ DeviceState *dev; - SysBusDevice *s; - EmptySlot *e; dev = qdev_create(NULL, TYPE_EMPTY_SLOT); - s = SYS_BUS_DEVICE(dev); - e = EMPTY_SLOT(dev); - e->size = slot_size; + qdev_prop_set_uint64(dev, "size", slot_size); qdev_init_nofail(dev); - sysbus_mmio_map(s, 0, addr); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, addr, -10000); } } @@ -75,16 +69,27 @@ static void empty_slot_realize(DeviceState *dev, Error **errp) { EmptySlot *s = EMPTY_SLOT(dev); + if (s->name == NULL) { + s->name = g_strdup("empty-slot"); + } memory_region_init_io(&s->iomem, OBJECT(s), &empty_slot_ops, s, - "empty-slot", s->size); + s->name, s->size); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } +static Property empty_slot_properties[] = { + DEFINE_PROP_UINT64("size", EmptySlot, size, 0), + DEFINE_PROP_STRING("name", EmptySlot, name), + DEFINE_PROP_END_OF_LIST(), +}; + static void empty_slot_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = empty_slot_realize; + device_class_set_props(dc, empty_slot_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo empty_slot_info = { diff --git a/hw/misc/grlib_ahb_apb_pnp.c b/hw/misc/grlib_ahb_apb_pnp.c index e230e25363..43e001c3c7 100644 --- a/hw/misc/grlib_ahb_apb_pnp.c +++ b/hw/misc/grlib_ahb_apb_pnp.c @@ -25,6 +25,7 @@ #include "qemu/log.h" #include "hw/sysbus.h" #include "hw/misc/grlib_ahb_apb_pnp.h" +#include "trace.h" #define GRLIB_PNP_VENDOR_SHIFT (24) #define GRLIB_PNP_VENDOR_SIZE (8) @@ -132,13 +133,28 @@ void grlib_ahb_pnp_add_entry(AHBPnp *dev, uint32_t address, uint32_t mask, static uint64_t grlib_ahb_pnp_read(void *opaque, hwaddr offset, unsigned size) { AHBPnp *ahb_pnp = GRLIB_AHB_PNP(opaque); + uint32_t val; - return ahb_pnp->regs[offset >> 2]; + val = ahb_pnp->regs[offset >> 2]; + trace_grlib_ahb_pnp_read(offset, val); + + return val; +} + +static void grlib_ahb_pnp_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s not implemented\n", __func__); } static const MemoryRegionOps grlib_ahb_pnp_ops = { .read = grlib_ahb_pnp_read, + .write = grlib_ahb_pnp_write, .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, }; static void grlib_ahb_pnp_realize(DeviceState *dev, Error **errp) @@ -228,8 +244,12 @@ void grlib_apb_pnp_add_entry(APBPnp *dev, uint32_t address, uint32_t mask, static uint64_t grlib_apb_pnp_read(void *opaque, hwaddr offset, unsigned size) { APBPnp *apb_pnp = GRLIB_APB_PNP(opaque); + uint32_t val; + + val = apb_pnp->regs[offset >> 2]; + trace_grlib_apb_pnp_read(offset, val); - return apb_pnp->regs[offset >> 2]; + return val; } static void grlib_apb_pnp_write(void *opaque, hwaddr addr, diff --git a/hw/misc/trace-events b/hw/misc/trace-events index a5862b2bed..5561746866 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -39,6 +39,10 @@ ecc_mem_readl_ecr1(uint32_t ret) "Read event count 2 0x%08x" ecc_diag_mem_writeb(uint64_t addr, uint32_t val) "Write diagnostic %"PRId64" = 0x%02x" ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= 0x%02x" +# empty_slot.c +empty_slot_read(uint64_t addr, unsigned width, uint64_t value, unsigned size, const char *name) "rd addr:0x%04"PRIx64" data:0x%0*"PRIx64" size %u [%s]" +empty_slot_write(uint64_t addr, unsigned width, uint64_t value, unsigned size, const char *name) "wr addr:0x%04"PRIx64" data:0x%0*"PRIx64" size %u [%s]" + # slavio_misc.c slavio_misc_update_irq_raise(void) "Raise IRQ" slavio_misc_update_irq_lower(void) "Lower IRQ" @@ -198,3 +202,7 @@ via1_rtc_cmd_pram_read(int addr, int value) "addr=%u value=0x%02x" via1_rtc_cmd_pram_write(int addr, int value) "addr=%u value=0x%02x" via1_rtc_cmd_pram_sect_read(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" + +# grlib_ahb_apb_pnp.c +grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" data:0x%08x" +grlib_apb_pnp_read(uint64_t addr, uint32_t value) "APB PnP read addr:0x%03"PRIx64" data:0x%08x" diff --git a/hw/sparc/Kconfig b/hw/sparc/Kconfig index 22aff2f5b7..91805afab6 100644 --- a/hw/sparc/Kconfig +++ b/hw/sparc/Kconfig @@ -5,6 +5,7 @@ config SUN4M select CS4231 select ECCMEMCTL select EMPTY_SLOT + select UNIMP select ESCC select ESP select FDC diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 8f024dab7b..cc55117dec 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -339,16 +339,14 @@ static void leon3_generic_hw_init(MachineState *machine) 0, LEON3_TIMER_IRQ, GRLIB_APBIO_AREA); /* Allocate uart */ - if (serial_hd(0)) { - dev = qdev_create(NULL, TYPE_GRLIB_APB_UART); - qdev_prop_set_chr(dev, "chrdev", serial_hd(0)); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, LEON3_UART_OFFSET); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irqs[LEON3_UART_IRQ]); - grlib_apb_pnp_add_entry(apb_pnp, LEON3_UART_OFFSET, 0xFFF, - GRLIB_VENDOR_GAISLER, GRLIB_APBUART_DEV, 1, - LEON3_UART_IRQ, GRLIB_APBIO_AREA); - } + dev = qdev_create(NULL, TYPE_GRLIB_APB_UART); + qdev_prop_set_chr(dev, "chrdev", serial_hd(0)); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, LEON3_UART_OFFSET); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irqs[LEON3_UART_IRQ]); + grlib_apb_pnp_add_entry(apb_pnp, LEON3_UART_OFFSET, 0xFFF, + GRLIB_VENDOR_GAISLER, GRLIB_APBUART_DEV, 1, + LEON3_UART_IRQ, GRLIB_APBIO_AREA); } static void leon3_generic_machine_init(MachineClass *mc) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 8dda3f7292..249f7ba7ea 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -46,7 +46,8 @@ #include "hw/nvram/chrp_nvram.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" -#include "hw/empty_slot.h" +#include "hw/misc/empty_slot.h" +#include "hw/misc/unimp.h" #include "hw/irq.h" #include "hw/loader.h" #include "elf.h" @@ -883,7 +884,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, /* models without ECC don't trap when missing ram is accessed */ if (!hwdef->ecc_base) { - empty_slot_init(machine->ram_size, hwdef->max_mem - machine->ram_size); + empty_slot_init("ecc", machine->ram_size, + hwdef->max_mem - machine->ram_size); } prom_init(hwdef->slavio_base, bios_name); @@ -914,7 +916,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, Software shouldn't use aliased addresses, neither should it crash when does. Using empty_slot instead of aliasing can help with debugging such accesses */ - empty_slot_init(hwdef->iommu_pad_base,hwdef->iommu_pad_len); + empty_slot_init("iommu.alias", + hwdef->iommu_pad_base, hwdef->iommu_pad_len); } sparc32_dma_init(hwdef->dma_base, @@ -963,12 +966,14 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, for (i = 0; i < MAX_VSIMMS; i++) { /* vsimm registers probed by OBP */ if (hwdef->vsimm[i].reg_base) { - empty_slot_init(hwdef->vsimm[i].reg_base, 0x2000); + char *name = g_strdup_printf("vsimm[%d]", i); + empty_slot_init(name, hwdef->vsimm[i].reg_base, 0x2000); + g_free(name); } } if (hwdef->sx_base) { - empty_slot_init(hwdef->sx_base, 0x2000); + create_unimplemented_device("SUNW,sx", hwdef->sx_base, 0x2000); } nvram = m48t59_init(slavio_irq[0], hwdef->nvram_base, 0, 0x2000, 1968, 8); @@ -1031,14 +1036,16 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, if (hwdef->dbri_base) { /* ISDN chip with attached CS4215 audio codec */ /* prom space */ - empty_slot_init(hwdef->dbri_base+0x1000, 0x30); + create_unimplemented_device("SUNW,DBRI.prom", + hwdef->dbri_base + 0x1000, 0x30); /* reg space */ - empty_slot_init(hwdef->dbri_base+0x10000, 0x100); + create_unimplemented_device("SUNW,DBRI", + hwdef->dbri_base + 0x10000, 0x100); } if (hwdef->bpp_base) { /* parallel port */ - empty_slot_init(hwdef->bpp_base, 0x20); + create_unimplemented_device("SUNW,bpp", hwdef->bpp_base, 0x20); } initrd_size = 0; diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index ab5ef8c5b3..a87d55f6bb 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -68,7 +68,6 @@ typedef struct NiagaraBoardState { #define NIAGARA_VDISK_BASE 0x1f40000000ULL #define NIAGARA_RTC_BASE 0xfff0c1fff8ULL -#define NIAGARA_UART_BASE 0x1f10000000ULL /* Firmware layout * @@ -152,10 +151,8 @@ static void niagara_init(MachineState *machine) exit(1); } } - if (serial_hd(0)) { - serial_mm_init(sysmem, NIAGARA_UART_BASE, 0, NULL, 115200, - serial_hd(0), DEVICE_BIG_ENDIAN); - } + serial_mm_init(sysmem, NIAGARA_UART_BASE, 0, NULL, + 115200, serial_hd(0), DEVICE_BIG_ENDIAN); create_unimplemented_device("sun4v-iob", NIAGARA_IOBBASE, NIAGARA_IOBSIZE); sun4v_rtc_init(NIAGARA_RTC_BASE); } diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 80ea197594..866c9f546a 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -19,7 +19,7 @@ slavio_timer_mem_writel_invalid(uint64_t addr) "invalid write address 0x%"PRIx64 grlib_gptimer_enable(int id, uint32_t count) "timer:%d set count 0x%x and run" grlib_gptimer_disabled(int id, uint32_t config) "timer:%d Timer disable config 0x%x" grlib_gptimer_restart(int id, uint32_t reload) "timer:%d reload val: 0x%x" -grlib_gptimer_set_scaler(uint32_t scaler, uint32_t freq) "scaler:0x%x freq: 0x%x" +grlib_gptimer_set_scaler(uint32_t scaler, uint32_t freq) "scaler:0x%x freq:%uHz" grlib_gptimer_hit(int id) "timer:%d HIT" grlib_gptimer_readl(int id, uint64_t addr, uint32_t val) "timer:%d addr 0x%"PRIx64" 0x%x" grlib_gptimer_writel(int id, uint64_t addr, uint32_t val) "timer:%d addr 0x%"PRIx64" 0x%x" diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index e83500bee9..6427a0047d 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -5,7 +5,8 @@ vhost_commit(bool started, bool changed) "Started: %d Changed: %d" vhost_region_add_section(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64 vhost_region_add_section_merge(const char *name, uint64_t new_size, uint64_t gpa, uint64_t owr) "%s: size: 0x%"PRIx64 " gpa: 0x%"PRIx64 " owr: 0x%"PRIx64 vhost_region_add_section_aligned(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64 -vhost_section(const char *name, int r) "%s:%d" +vhost_section(const char *name) "%s" +vhost_reject_section(const char *name, int d) "%s:%d" vhost_iotlb_miss(void *dev, int step) "%p step %d" # vhost-user.c diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index aa06a36919..5fd25fe520 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -27,6 +27,7 @@ #include "migration/blocker.h" #include "migration/qemu-file-types.h" #include "sysemu/dma.h" +#include "sysemu/tcg.h" #include "trace.h" /* enabled until disconnected backend stabilizes */ @@ -403,26 +404,50 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, return r; } +/* + * vhost_section: identify sections needed for vhost access + * + * We only care about RAM sections here (where virtqueue and guest + * internals accessed by virtio might live). If we find one we still + * allow the backend to potentially filter it out of our list. + */ static bool vhost_section(struct vhost_dev *dev, MemoryRegionSection *section) { - bool result; - bool log_dirty = memory_region_get_dirty_log_mask(section->mr) & - ~(1 << DIRTY_MEMORY_MIGRATION); - result = memory_region_is_ram(section->mr) && - !memory_region_is_rom(section->mr); - - /* Vhost doesn't handle any block which is doing dirty-tracking other - * than migration; this typically fires on VGA areas. - */ - result &= !log_dirty; + MemoryRegion *mr = section->mr; + + if (memory_region_is_ram(mr) && !memory_region_is_rom(mr)) { + uint8_t dirty_mask = memory_region_get_dirty_log_mask(mr); + uint8_t handled_dirty; + + /* + * Kernel based vhost doesn't handle any block which is doing + * dirty-tracking other than migration for which it has + * specific logging support. However for TCG the kernel never + * gets involved anyway so we can also ignore it's + * self-modiying code detection flags. However a vhost-user + * client could still confuse a TCG guest if it re-writes + * executable memory that has already been translated. + */ + handled_dirty = (1 << DIRTY_MEMORY_MIGRATION) | + (1 << DIRTY_MEMORY_CODE); - if (result && dev->vhost_ops->vhost_backend_mem_section_filter) { - result &= - dev->vhost_ops->vhost_backend_mem_section_filter(dev, section); - } + if (dirty_mask & ~handled_dirty) { + trace_vhost_reject_section(mr->name, 1); + return false; + } + + if (dev->vhost_ops->vhost_backend_mem_section_filter && + !dev->vhost_ops->vhost_backend_mem_section_filter(dev, section)) { + trace_vhost_reject_section(mr->name, 2); + return false; + } - trace_vhost_section(section->mr->name, result); - return result; + trace_vhost_section(mr->name); + return true; + } else { + trace_vhost_reject_section(mr->name, 3); + return false; + } } static void vhost_begin(MemoryListener *listener) diff --git a/include/hw/empty_slot.h b/include/hw/empty_slot.h deleted file mode 100644 index cb9a221aa6..0000000000 --- a/include/hw/empty_slot.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef HW_EMPTY_SLOT_H -#define HW_EMPTY_SLOT_H - -#include "exec/hwaddr.h" - -/* empty_slot.c */ -void empty_slot_init(hwaddr addr, uint64_t slot_size); - -#endif diff --git a/include/hw/misc/empty_slot.h b/include/hw/misc/empty_slot.h new file mode 100644 index 0000000000..dec56e56ae --- /dev/null +++ b/include/hw/misc/empty_slot.h @@ -0,0 +1,19 @@ +/* + * QEMU Empty Slot + * + * The empty_slot device emulates known to a bus but not connected devices. + * + * Copyright (c) 2010 Artyom Tarasenko + * + * This code is licensed under the GNU GPL v2 or (at your option) any later + * version. + */ + +#ifndef HW_EMPTY_SLOT_H +#define HW_EMPTY_SLOT_H + +#include "exec/hwaddr.h" + +void empty_slot_init(const char *name, hwaddr addr, uint64_t slot_size); + +#endif diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 89ed579f55..bab8b0d4b3 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -12,6 +12,7 @@ #include <inttypes.h> #include <stdbool.h> +#include <stddef.h> /* * For best performance, build the plugin with -fvisibility=hidden so that diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ebc663ea0b..b5cb21384a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -389,7 +389,7 @@ static bool init_guest_commpage(void) { void *want = g2h(ARM_COMMPAGE & -qemu_host_page_size); void *addr = mmap(want, qemu_host_page_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); if (addr == MAP_FAILED) { perror("Allocating guest commpage"); @@ -2101,9 +2101,54 @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr, } } +/** + * pgd_find_hole_fallback: potential mmap address + * @guest_size: size of available space + * @brk: location of break + * @align: memory alignment + * + * This is a fallback method for finding a hole in the host address + * space if we don't have the benefit of being able to access + * /proc/self/map. It can potentially take a very long time as we can + * only dumbly iterate up the host address space seeing if the + * allocation would work. + */ +static uintptr_t pgd_find_hole_fallback(uintptr_t guest_size, uintptr_t brk, + long align, uintptr_t offset) +{ + uintptr_t base; + + /* Start (aligned) at the bottom and work our way up */ + base = ROUND_UP(mmap_min_addr, align); + + while (true) { + uintptr_t align_start, end; + align_start = ROUND_UP(base, align); + end = align_start + guest_size + offset; + + /* if brk is anywhere in the range give ourselves some room to grow. */ + if (align_start <= brk && brk < end) { + base = brk + (16 * MiB); + continue; + } else if (align_start + guest_size < align_start) { + /* we have run out of space */ + return -1; + } else { + int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE | MAP_FIXED; + void * mmap_start = mmap((void *) align_start, guest_size, + PROT_NONE, flags, -1, 0); + if (mmap_start != MAP_FAILED) { + munmap((void *) align_start, guest_size); + return (uintptr_t) mmap_start + offset; + } + base += qemu_host_page_size; + } + } +} + /* Return value for guest_base, or -1 if no hole found. */ static uintptr_t pgb_find_hole(uintptr_t guest_loaddr, uintptr_t guest_size, - long align) + long align, uintptr_t offset) { GSList *maps, *iter; uintptr_t this_start, this_end, next_start, brk; @@ -2116,6 +2161,10 @@ static uintptr_t pgb_find_hole(uintptr_t guest_loaddr, uintptr_t guest_size, /* Read brk after we've read the maps, which will malloc. */ brk = (uintptr_t)sbrk(0); + if (!maps) { + return pgd_find_hole_fallback(guest_size, brk, align, offset); + } + /* The first hole is before the first map entry. */ this_start = mmap_min_addr; @@ -2125,7 +2174,7 @@ static uintptr_t pgb_find_hole(uintptr_t guest_loaddr, uintptr_t guest_size, this_end = ((MapInfo *)iter->data)->start; next_start = ((MapInfo *)iter->data)->end; - align_start = ROUND_UP(this_start, align); + align_start = ROUND_UP(this_start + offset, align); /* Skip holes that are too small. */ if (align_start >= this_end) { @@ -2175,6 +2224,7 @@ static void pgb_static(const char *image_name, abi_ulong orig_loaddr, { uintptr_t loaddr = orig_loaddr; uintptr_t hiaddr = orig_hiaddr; + uintptr_t offset = 0; uintptr_t addr; if (hiaddr != orig_hiaddr) { @@ -2188,18 +2238,19 @@ static void pgb_static(const char *image_name, abi_ulong orig_loaddr, if (ARM_COMMPAGE) { /* * Extend the allocation to include the commpage. - * For a 64-bit host, this is just 4GiB; for a 32-bit host, - * the address arithmetic will wrap around, but the difference - * will produce the correct allocation size. + * For a 64-bit host, this is just 4GiB; for a 32-bit host we + * need to ensure there is space bellow the guest_base so we + * can map the commpage in the place needed when the address + * arithmetic wraps around. */ if (sizeof(uintptr_t) == 8 || loaddr >= 0x80000000u) { - hiaddr = (uintptr_t)4 << 30; + hiaddr = (uintptr_t) 4 << 30; } else { - loaddr = ARM_COMMPAGE & -align; + offset = -(ARM_COMMPAGE & -align); } } - addr = pgb_find_hole(loaddr, hiaddr - loaddr, align); + addr = pgb_find_hole(loaddr, hiaddr - loaddr, align, offset); if (addr == -1) { /* * If ARM_COMMPAGE, there *might* be a non-consecutive allocation @@ -2234,7 +2285,7 @@ static void pgb_dynamic(const char *image_name, long align) * just above that, and maximises the positive guest addresses. */ commpage = ARM_COMMPAGE & -align; - addr = pgb_find_hole(commpage, -commpage, align); + addr = pgb_find_hole(commpage, -commpage, align, 0); assert(addr != -1); guest_base = addr; } diff --git a/linux-user/mmap.c b/linux-user/mmap.c index caab62909e..0019447892 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -467,7 +467,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, * It can fail only on 64-bit host with 32-bit target. * On any other target/host host mmap() handles this error correctly. */ - if (!guest_range_valid(start, len)) { + if (end < start || !guest_range_valid(start, len)) { errno = ENOMEM; goto fail; } diff --git a/scripts/clean-includes b/scripts/clean-includes index dd938daa3e..795b3bea31 100755 --- a/scripts/clean-includes +++ b/scripts/clean-includes @@ -123,6 +123,7 @@ for f in "$@"; do ;; *include/qemu/osdep.h | \ *include/qemu/compiler.h | \ + *include/qemu/qemu-plugin.h | \ *include/glib-compat.h | \ *include/sysemu/os-posix.h | \ *include/sysemu/os-win32.h | \ diff --git a/scripts/coverity-scan/coverity-scan.docker b/scripts/coverity-scan/coverity-scan.docker index a4f64d1283..ad4d64c0f8 100644 --- a/scripts/coverity-scan/coverity-scan.docker +++ b/scripts/coverity-scan/coverity-scan.docker @@ -19,7 +19,6 @@ FROM fedora:30 ENV PACKAGES \ alsa-lib-devel \ bc \ - bison \ brlapi-devel \ bzip2 \ bzip2-devel \ @@ -30,7 +29,6 @@ ENV PACKAGES \ dbus-daemon \ device-mapper-multipath-devel \ findutils \ - flex \ gcc \ gcc-c++ \ gettext \ diff --git a/scripts/decodetree.py b/scripts/decodetree.py index f9d204aa36..530d41ca62 100755 --- a/scripts/decodetree.py +++ b/scripts/decodetree.py @@ -31,7 +31,6 @@ variablewidth = False fields = {} arguments = {} formats = {} -patterns = [] allpatterns = [] anyextern = False @@ -51,23 +50,27 @@ def error_with_file(file, lineno, *args): global output_file global output_fd + prefix = '' + if file: + prefix += '{0}:'.format(file) if lineno: - r = '{0}:{1}: error:'.format(file, lineno) - elif input_file: - r = '{0}: error:'.format(file) - else: - r = 'error:' - for a in args: - r += ' ' + str(a) - r += '\n' - sys.stderr.write(r) + prefix += '{0}:'.format(lineno) + if prefix: + prefix += ' ' + print(prefix, end='error: ', file=sys.stderr) + print(*args, file=sys.stderr) + if output_file and output_fd: output_fd.close() os.remove(output_file) exit(1) +# end error_with_file + def error(lineno, *args): - error_with_file(input_file, lineno, args) + error_with_file(input_file, lineno, *args) +# end error + def output(*args): global output_fd @@ -120,6 +123,7 @@ def is_pow2(x): def ctz(x): """Return the number of times 2 factors into X.""" + assert x != 0 r = 0 while ((x >> r) & 1) == 0: r += 1 @@ -127,6 +131,8 @@ def ctz(x): def is_contiguous(bits): + if bits == 0: + return -1 shift = ctz(bits) if is_pow2((bits >> shift) + 1): return shift @@ -364,32 +370,99 @@ class Pattern(General): output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n') output(ind, 'if (', translate_prefix, '_', self.name, '(ctx, &u.f_', arg, ')) return true;\n') + + # Normal patterns do not have children. + def build_tree(self): + return + def prop_masks(self): + return + def prop_format(self): + return + def prop_width(self): + return + # end Pattern class MultiPattern(General): - """Class representing an overlapping set of instruction patterns""" + """Class representing a set of instruction patterns""" - def __init__(self, lineno, pats, fixb, fixm, udfm, w): + def __init__(self, lineno): self.file = input_file self.lineno = lineno - self.pats = pats + self.pats = [] self.base = None - self.fixedbits = fixb - self.fixedmask = fixm - self.undefmask = udfm - self.width = w + self.fixedbits = 0 + self.fixedmask = 0 + self.undefmask = 0 + self.width = None def __str__(self): - r = "{" - for p in self.pats: - r = r + ' ' + str(p) - return r + "}" + r = 'group' + if self.fixedbits is not None: + r += ' ' + str_match_bits(self.fixedbits, self.fixedmask) + return r def output_decl(self): for p in self.pats: p.output_decl() + def prop_masks(self): + global insnmask + + fixedmask = insnmask + undefmask = insnmask + + # Collect fixedmask/undefmask for all of the children. + for p in self.pats: + p.prop_masks() + fixedmask &= p.fixedmask + undefmask &= p.undefmask + + # Widen fixedmask until all fixedbits match + repeat = True + fixedbits = 0 + while repeat and fixedmask != 0: + fixedbits = None + for p in self.pats: + thisbits = p.fixedbits & fixedmask + if fixedbits is None: + fixedbits = thisbits + elif fixedbits != thisbits: + fixedmask &= ~(fixedbits ^ thisbits) + break + else: + repeat = False + + self.fixedbits = fixedbits + self.fixedmask = fixedmask + self.undefmask = undefmask + + def build_tree(self): + for p in self.pats: + p.build_tree() + + def prop_format(self): + for p in self.pats: + p.build_tree() + + def prop_width(self): + width = None + for p in self.pats: + p.prop_width() + if width is None: + width = p.width + elif width != p.width: + error_with_file(self.file, self.lineno, + 'width mismatch in patterns within braces') + self.width = width + +# end MultiPattern + + +class IncMultiPattern(MultiPattern): + """Class representing an overlapping set of instruction patterns""" + def output_code(self, i, extracted, outerbits, outermask): global translate_prefix ind = str_indent(i) @@ -406,7 +479,154 @@ class MultiPattern(General): output(ind, '}\n') else: p.output_code(i, extracted, p.fixedbits, p.fixedmask) -#end MultiPattern +#end IncMultiPattern + + +class Tree: + """Class representing a node in a decode tree""" + + def __init__(self, fm, tm): + self.fixedmask = fm + self.thismask = tm + self.subs = [] + self.base = None + + def str1(self, i): + ind = str_indent(i) + r = '{0}{1:08x}'.format(ind, self.fixedmask) + if self.format: + r += ' ' + self.format.name + r += ' [\n' + for (b, s) in self.subs: + r += '{0} {1:08x}:\n'.format(ind, b) + r += s.str1(i + 4) + '\n' + r += ind + ']' + return r + + def __str__(self): + return self.str1(0) + + def output_code(self, i, extracted, outerbits, outermask): + ind = str_indent(i) + + # If we identified all nodes below have the same format, + # extract the fields now. + if not extracted and self.base: + output(ind, self.base.extract_name(), + '(ctx, &u.f_', self.base.base.name, ', insn);\n') + extracted = True + + # Attempt to aid the compiler in producing compact switch statements. + # If the bits in the mask are contiguous, extract them. + sh = is_contiguous(self.thismask) + if sh > 0: + # Propagate SH down into the local functions. + def str_switch(b, sh=sh): + return '(insn >> {0}) & 0x{1:x}'.format(sh, b >> sh) + + def str_case(b, sh=sh): + return '0x{0:x}'.format(b >> sh) + else: + def str_switch(b): + return 'insn & 0x{0:08x}'.format(b) + + def str_case(b): + return '0x{0:08x}'.format(b) + + output(ind, 'switch (', str_switch(self.thismask), ') {\n') + for b, s in sorted(self.subs): + assert (self.thismask & ~s.fixedmask) == 0 + innermask = outermask | self.thismask + innerbits = outerbits | b + output(ind, 'case ', str_case(b), ':\n') + output(ind, ' /* ', + str_match_bits(innerbits, innermask), ' */\n') + s.output_code(i + 4, extracted, innerbits, innermask) + output(ind, ' return false;\n') + output(ind, '}\n') +# end Tree + + +class ExcMultiPattern(MultiPattern): + """Class representing a non-overlapping set of instruction patterns""" + + def output_code(self, i, extracted, outerbits, outermask): + # Defer everything to our decomposed Tree node + self.tree.output_code(i, extracted, outerbits, outermask) + + @staticmethod + def __build_tree(pats, outerbits, outermask): + # Find the intersection of all remaining fixedmask. + innermask = ~outermask & insnmask + for i in pats: + innermask &= i.fixedmask + + if innermask == 0: + # Edge condition: One pattern covers the entire insnmask + if len(pats) == 1: + t = Tree(outermask, innermask) + t.subs.append((0, pats[0])) + return t + + text = 'overlapping patterns:' + for p in pats: + text += '\n' + p.file + ':' + str(p.lineno) + ': ' + str(p) + error_with_file(pats[0].file, pats[0].lineno, text) + + fullmask = outermask | innermask + + # Sort each element of pats into the bin selected by the mask. + bins = {} + for i in pats: + fb = i.fixedbits & innermask + if fb in bins: + bins[fb].append(i) + else: + bins[fb] = [i] + + # We must recurse if any bin has more than one element or if + # the single element in the bin has not been fully matched. + t = Tree(fullmask, innermask) + + for b, l in bins.items(): + s = l[0] + if len(l) > 1 or s.fixedmask & ~fullmask != 0: + s = ExcMultiPattern.__build_tree(l, b | outerbits, fullmask) + t.subs.append((b, s)) + + return t + + def build_tree(self): + super().prop_format() + self.tree = self.__build_tree(self.pats, self.fixedbits, + self.fixedmask) + + @staticmethod + def __prop_format(tree): + """Propagate Format objects into the decode tree""" + + # Depth first search. + for (b, s) in tree.subs: + if isinstance(s, Tree): + ExcMultiPattern.__prop_format(s) + + # If all entries in SUBS have the same format, then + # propagate that into the tree. + f = None + for (b, s) in tree.subs: + if f is None: + f = s.base + if f is None: + return + if f is not s.base: + return + tree.base = f + + def prop_format(self): + super().prop_format() + self.__prop_format(self.tree) + +# end ExcMultiPattern def parse_field(lineno, name, toks): @@ -565,18 +785,19 @@ def infer_format(arg, fieldmask, flds, width): # end infer_format -def parse_generic(lineno, is_format, name, toks): +def parse_generic(lineno, parent_pat, name, toks): """Parse one instruction format from TOKS at LINENO""" global fields global arguments global formats - global patterns global allpatterns global re_ident global insnwidth global insnmask global variablewidth + is_format = parent_pat is None + fixedmask = 0 fixedbits = 0 undefmask = 0 @@ -727,7 +948,7 @@ def parse_generic(lineno, is_format, name, toks): error(lineno, 'field {0} not initialized'.format(f)) pat = Pattern(name, lineno, fmt, fixedbits, fixedmask, undefmask, fieldmask, flds, width) - patterns.append(pat) + parent_pat.pats.append(pat) allpatterns.append(pat) # Validate the masks that we have assembled. @@ -747,62 +968,16 @@ def parse_generic(lineno, is_format, name, toks): .format(allbits ^ insnmask)) # end parse_general -def build_multi_pattern(lineno, pats): - """Validate the Patterns going into a MultiPattern.""" - global patterns - global insnmask - - if len(pats) < 2: - error(lineno, 'less than two patterns within braces') - - fixedmask = insnmask - undefmask = insnmask - - # Collect fixed/undefmask for all of the children. - # Move the defining lineno back to that of the first child. - for p in pats: - fixedmask &= p.fixedmask - undefmask &= p.undefmask - if p.lineno < lineno: - lineno = p.lineno - - width = None - for p in pats: - if width is None: - width = p.width - elif width != p.width: - error(lineno, 'width mismatch in patterns within braces') - - repeat = True - while repeat: - if fixedmask == 0: - error(lineno, 'no overlap in patterns within braces') - fixedbits = None - for p in pats: - thisbits = p.fixedbits & fixedmask - if fixedbits is None: - fixedbits = thisbits - elif fixedbits != thisbits: - fixedmask &= ~(fixedbits ^ thisbits) - break - else: - repeat = False - mp = MultiPattern(lineno, pats, fixedbits, fixedmask, undefmask, width) - patterns.append(mp) -# end build_multi_pattern - -def parse_file(f): +def parse_file(f, parent_pat): """Parse all of the patterns within a file""" - global patterns - # Read all of the lines of the file. Concatenate lines # ending in backslash; discard empty lines and comments. toks = [] lineno = 0 nesting = 0 - saved_pats = [] + nesting_pats = [] for line in f: lineno += 1 @@ -846,17 +1021,23 @@ def parse_file(f): del toks[0] # End nesting? - if name == '}': - if nesting == 0: - error(start_lineno, 'mismatched close brace') + if name == '}' or name == ']': if len(toks) != 0: error(start_lineno, 'extra tokens after close brace') + + # Make sure { } and [ ] nest properly. + if (name == '}') != isinstance(parent_pat, IncMultiPattern): + error(lineno, 'mismatched close brace') + + try: + parent_pat = nesting_pats.pop() + except: + error(lineno, 'extra close brace') + nesting -= 2 if indent != nesting: - error(start_lineno, 'indentation ', indent, ' != ', nesting) - pats = patterns - patterns = saved_pats.pop() - build_multi_pattern(lineno, pats) + error(lineno, 'indentation ', indent, ' != ', nesting) + toks = [] continue @@ -865,11 +1046,18 @@ def parse_file(f): error(start_lineno, 'indentation ', indent, ' != ', nesting) # Start nesting? - if name == '{': + if name == '{' or name == '[': if len(toks) != 0: error(start_lineno, 'extra tokens after open brace') - saved_pats.append(patterns) - patterns = [] + + if name == '{': + nested_pat = IncMultiPattern(start_lineno) + else: + nested_pat = ExcMultiPattern(start_lineno) + parent_pat.pats.append(nested_pat) + nesting_pats.append(parent_pat) + parent_pat = nested_pat + nesting += 2 toks = [] continue @@ -880,113 +1068,14 @@ def parse_file(f): elif name[0] == '&': parse_arguments(start_lineno, name[1:], toks) elif name[0] == '@': - parse_generic(start_lineno, True, name[1:], toks) + parse_generic(start_lineno, None, name[1:], toks) else: - parse_generic(start_lineno, False, name, toks) + parse_generic(start_lineno, parent_pat, name, toks) toks = [] -# end parse_file - - -class Tree: - """Class representing a node in a decode tree""" - - def __init__(self, fm, tm): - self.fixedmask = fm - self.thismask = tm - self.subs = [] - self.base = None - def str1(self, i): - ind = str_indent(i) - r = '{0}{1:08x}'.format(ind, self.fixedmask) - if self.format: - r += ' ' + self.format.name - r += ' [\n' - for (b, s) in self.subs: - r += '{0} {1:08x}:\n'.format(ind, b) - r += s.str1(i + 4) + '\n' - r += ind + ']' - return r - - def __str__(self): - return self.str1(0) - - def output_code(self, i, extracted, outerbits, outermask): - ind = str_indent(i) - - # If we identified all nodes below have the same format, - # extract the fields now. - if not extracted and self.base: - output(ind, self.base.extract_name(), - '(ctx, &u.f_', self.base.base.name, ', insn);\n') - extracted = True - - # Attempt to aid the compiler in producing compact switch statements. - # If the bits in the mask are contiguous, extract them. - sh = is_contiguous(self.thismask) - if sh > 0: - # Propagate SH down into the local functions. - def str_switch(b, sh=sh): - return '(insn >> {0}) & 0x{1:x}'.format(sh, b >> sh) - - def str_case(b, sh=sh): - return '0x{0:x}'.format(b >> sh) - else: - def str_switch(b): - return 'insn & 0x{0:08x}'.format(b) - - def str_case(b): - return '0x{0:08x}'.format(b) - - output(ind, 'switch (', str_switch(self.thismask), ') {\n') - for b, s in sorted(self.subs): - assert (self.thismask & ~s.fixedmask) == 0 - innermask = outermask | self.thismask - innerbits = outerbits | b - output(ind, 'case ', str_case(b), ':\n') - output(ind, ' /* ', - str_match_bits(innerbits, innermask), ' */\n') - s.output_code(i + 4, extracted, innerbits, innermask) - output(ind, ' return false;\n') - output(ind, '}\n') -# end Tree - - -def build_tree(pats, outerbits, outermask): - # Find the intersection of all remaining fixedmask. - innermask = ~outermask & insnmask - for i in pats: - innermask &= i.fixedmask - - if innermask == 0: - text = 'overlapping patterns:' - for p in pats: - text += '\n' + p.file + ':' + str(p.lineno) + ': ' + str(p) - error_with_file(pats[0].file, pats[0].lineno, text) - - fullmask = outermask | innermask - - # Sort each element of pats into the bin selected by the mask. - bins = {} - for i in pats: - fb = i.fixedbits & innermask - if fb in bins: - bins[fb].append(i) - else: - bins[fb] = [i] - - # We must recurse if any bin has more than one element or if - # the single element in the bin has not been fully matched. - t = Tree(fullmask, innermask) - - for b, l in bins.items(): - s = l[0] - if len(l) > 1 or s.fixedmask & ~fullmask != 0: - s = build_tree(l, b | outerbits, fullmask) - t.subs.append((b, s)) - - return t -# end build_tree + if nesting != 0: + error(lineno, 'missing close brace') +# end parse_file class SizeTree: @@ -1130,28 +1219,6 @@ def build_size_tree(pats, width, outerbits, outermask): # end build_size_tree -def prop_format(tree): - """Propagate Format objects into the decode tree""" - - # Depth first search. - for (b, s) in tree.subs: - if isinstance(s, Tree): - prop_format(s) - - # If all entries in SUBS have the same format, then - # propagate that into the tree. - f = None - for (b, s) in tree.subs: - if f is None: - f = s.base - if f is None: - return - if f is not s.base: - return - tree.base = f -# end prop_format - - def prop_size(tree): """Propagate minimum widths up the decode size tree""" @@ -1172,7 +1239,6 @@ def prop_size(tree): def main(): global arguments global formats - global patterns global allpatterns global translate_scope global translate_prefix @@ -1219,19 +1285,30 @@ def main(): if len(args) < 1: error(0, 'missing input file') + + toppat = ExcMultiPattern(0) + for filename in args: input_file = filename f = open(filename, 'r') - parse_file(f) + parse_file(f, toppat) f.close() + # We do not want to compute masks for toppat, because those masks + # are used as a starting point for build_tree. For toppat, we must + # insist that decode begins from naught. + for i in toppat.pats: + i.prop_masks() + + toppat.build_tree() + toppat.prop_format() + if variablewidth: - stree = build_size_tree(patterns, 8, 0, 0) + for i in toppat.pats: + i.prop_width() + stree = build_size_tree(toppat.pats, 8, 0, 0) prop_size(stree) - dtree = build_tree(patterns, 0, 0) - prop_format(dtree) - if output_file: output_fd = open(output_file, 'w') else: @@ -1289,7 +1366,7 @@ def main(): f = arguments[n] output(i4, i4, f.struct_name(), ' f_', f.name, ';\n') output(i4, '} u;\n\n') - dtree.output_code(4, False, 0, 0) + toppat.output_code(4, False, 0, 0) output(i4, 'return false;\n') output('}\n') diff --git a/target/arm/t32.decode b/target/arm/t32.decode index c63082fc9c..c21a988f97 100644 --- a/target/arm/t32.decode +++ b/target/arm/t32.decode @@ -312,13 +312,13 @@ CLZ 1111 1010 1011 ---- 1111 .... 1000 .... @rdm &cps # Miscellaneous control - { + [ CLREX 1111 0011 1011 1111 1000 1111 0010 1111 DSB 1111 0011 1011 1111 1000 1111 0100 ---- DMB 1111 0011 1011 1111 1000 1111 0101 ---- ISB 1111 0011 1011 1111 1000 1111 0110 ---- SB 1111 0011 1011 1111 1000 1111 0111 0000 - } + ] # Note that the v7m insn overlaps both the normal and banked insn. { diff --git a/target/mips/Makefile.objs b/target/mips/Makefile.objs index 91eb691833..b820b3b7bc 100644 --- a/target/mips/Makefile.objs +++ b/target/mips/Makefile.objs @@ -1,6 +1,6 @@ obj-y += translate.o cpu.o gdbstub.o helper.o obj-y += op_helper.o cp0_helper.o fpu_helper.o -obj-y += dsp_helper.o lmi_helper.o msa_helper.o +obj-y += dsp_helper.o lmmi_helper.o msa_helper.o obj-$(CONFIG_SOFTMMU) += mips-semi.o obj-$(CONFIG_SOFTMMU) += machine.o cp0_timer.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 94d01ea798..7cf7f5239f 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -198,8 +198,8 @@ typedef struct mips_def_t mips_def_t; * 3 Config3 WatchLo3 WatchHi * 4 Config4 WatchLo4 WatchHi * 5 Config5 WatchLo5 WatchHi - * 6 WatchLo6 WatchHi - * 7 WatchLo7 WatchHi + * 6 Config6 WatchLo6 WatchHi + * 7 Config7 WatchLo7 WatchHi * * * Register 20 Register 21 Register 22 Register 23 @@ -940,7 +940,35 @@ struct CPUMIPSState { #define CP0C5_UFR 2 #define CP0C5_NFExists 0 int32_t CP0_Config6; + int32_t CP0_Config6_rw_bitmask; +#define CP0C6_BPPASS 31 +#define CP0C6_KPOS 24 +#define CP0C6_KE 23 +#define CP0C6_VTLBONLY 22 +#define CP0C6_LASX 21 +#define CP0C6_SSEN 20 +#define CP0C6_DISDRTIME 19 +#define CP0C6_PIXNUEN 18 +#define CP0C6_SCRAND 17 +#define CP0C6_LLEXCEN 16 +#define CP0C6_DISVC 15 +#define CP0C6_VCLRU 14 +#define CP0C6_DCLRU 13 +#define CP0C6_PIXUEN 12 +#define CP0C6_DISBLKLYEN 11 +#define CP0C6_UMEMUALEN 10 +#define CP0C6_SFBEN 8 +#define CP0C6_FLTINT 7 +#define CP0C6_VLTINT 6 +#define CP0C6_DISBTB 5 +#define CP0C6_STPREFCTL 2 +#define CP0C6_INSTPREF 1 +#define CP0C6_DATAPREF 0 int32_t CP0_Config7; + int64_t CP0_Config7_rw_bitmask; +#define CP0C7_NAPCGEN 2 +#define CP0C7_UNIMUEN 1 +#define CP0C7_VFPUCGEN 0 uint64_t CP0_LLAddr; uint64_t CP0_MAAR[MIPS_MAAR_MAX]; int32_t CP0_MAARI; diff --git a/target/mips/fpu_helper.c b/target/mips/fpu_helper.c index 5287c86c61..7a3a61cab3 100644 --- a/target/mips/fpu_helper.c +++ b/target/mips/fpu_helper.c @@ -189,43 +189,48 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) } } -int ieee_ex_to_mips(int xcpt) +static inline int ieee_to_mips_xcpt(int ieee_xcpt) { - int ret = 0; - if (xcpt) { - if (xcpt & float_flag_invalid) { - ret |= FP_INVALID; - } - if (xcpt & float_flag_overflow) { - ret |= FP_OVERFLOW; - } - if (xcpt & float_flag_underflow) { - ret |= FP_UNDERFLOW; - } - if (xcpt & float_flag_divbyzero) { - ret |= FP_DIV0; - } - if (xcpt & float_flag_inexact) { - ret |= FP_INEXACT; - } + int mips_xcpt = 0; + + if (ieee_xcpt & float_flag_invalid) { + mips_xcpt |= FP_INVALID; + } + if (ieee_xcpt & float_flag_overflow) { + mips_xcpt |= FP_OVERFLOW; + } + if (ieee_xcpt & float_flag_underflow) { + mips_xcpt |= FP_UNDERFLOW; } - return ret; + if (ieee_xcpt & float_flag_divbyzero) { + mips_xcpt |= FP_DIV0; + } + if (ieee_xcpt & float_flag_inexact) { + mips_xcpt |= FP_INEXACT; + } + + return mips_xcpt; } static inline void update_fcr31(CPUMIPSState *env, uintptr_t pc) { - int tmp = ieee_ex_to_mips(get_float_exception_flags( - &env->active_fpu.fp_status)); + int ieee_exception_flags = get_float_exception_flags( + &env->active_fpu.fp_status); + int mips_exception_flags = 0; - SET_FP_CAUSE(env->active_fpu.fcr31, tmp); + if (ieee_exception_flags) { + mips_exception_flags = ieee_to_mips_xcpt(ieee_exception_flags); + } - if (tmp) { + SET_FP_CAUSE(env->active_fpu.fcr31, mips_exception_flags); + + if (mips_exception_flags) { set_float_exception_flags(0, &env->active_fpu.fp_status); - if (GET_FP_ENABLE(env->active_fpu.fcr31) & tmp) { + if (GET_FP_ENABLE(env->active_fpu.fcr31) & mips_exception_flags) { do_raise_exception(env, EXCP_FPE, pc); } else { - UPDATE_FP_FLAGS(env->active_fpu.fcr31, tmp); + UPDATE_FP_FLAGS(env->active_fpu.fcr31, mips_exception_flags); } } } @@ -1059,14 +1064,14 @@ uint32_t helper_float_recip1_s(CPUMIPSState *env, uint32_t fst0) uint64_t helper_float_recip1_ps(CPUMIPSState *env, uint64_t fdt0) { - uint32_t fst2; + uint32_t fstl2; uint32_t fsth2; - fst2 = float32_div(float32_one, fdt0 & 0XFFFFFFFF, - &env->active_fpu.fp_status); + fstl2 = float32_div(float32_one, fdt0 & 0XFFFFFFFF, + &env->active_fpu.fp_status); fsth2 = float32_div(float32_one, fdt0 >> 32, &env->active_fpu.fp_status); update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; + return ((uint64_t)fsth2 << 32) | fstl2; } uint64_t helper_float_rsqrt1_d(CPUMIPSState *env, uint64_t fdt0) @@ -1091,31 +1096,34 @@ uint32_t helper_float_rsqrt1_s(CPUMIPSState *env, uint32_t fst0) uint64_t helper_float_rsqrt1_ps(CPUMIPSState *env, uint64_t fdt0) { - uint32_t fst2; + uint32_t fstl2; uint32_t fsth2; - fst2 = float32_sqrt(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); + fstl2 = float32_sqrt(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); fsth2 = float32_sqrt(fdt0 >> 32, &env->active_fpu.fp_status); - fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); + fstl2 = float32_div(float32_one, fstl2, &env->active_fpu.fp_status); fsth2 = float32_div(float32_one, fsth2, &env->active_fpu.fp_status); update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; + return ((uint64_t)fsth2 << 32) | fstl2; } -#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; \ +uint64_t helper_float_rint_d(CPUMIPSState *env, uint64_t fs) +{ + uint64_t fdret; + + fdret = float64_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 +uint32_t helper_float_rint_s(CPUMIPSState *env, uint32_t fs) +{ + uint32_t fdret; + + fdret = float32_round_to_int(fs, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fdret; +} #define FLOAT_CLASS_SIGNALING_NAN 0x001 #define FLOAT_CLASS_QUIET_NAN 0x002 @@ -1128,91 +1136,220 @@ FLOAT_RINT(rint_d, 64) #define FLOAT_CLASS_POSITIVE_SUBNORMAL 0x100 #define FLOAT_CLASS_POSITIVE_ZERO 0x200 -#define FLOAT_CLASS(name, bits) \ -uint ## bits ## _t float_ ## name(uint ## bits ## _t arg, \ - float_status *status) \ -{ \ - if (float ## bits ## _is_signaling_nan(arg, status)) { \ - return FLOAT_CLASS_SIGNALING_NAN; \ - } else if (float ## bits ## _is_quiet_nan(arg, status)) { \ - 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; \ - } \ - } \ -} \ - \ -uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ - uint ## bits ## _t arg) \ -{ \ - return float_ ## name(arg, &env->active_fpu.fp_status); \ -} - -FLOAT_CLASS(class_s, 32) -FLOAT_CLASS(class_d, 64) -#undef FLOAT_CLASS +uint64_t float_class_d(uint64_t arg, float_status *status) +{ + if (float64_is_signaling_nan(arg, status)) { + return FLOAT_CLASS_SIGNALING_NAN; + } else if (float64_is_quiet_nan(arg, status)) { + return FLOAT_CLASS_QUIET_NAN; + } else if (float64_is_neg(arg)) { + if (float64_is_infinity(arg)) { + return FLOAT_CLASS_NEGATIVE_INFINITY; + } else if (float64_is_zero(arg)) { + return FLOAT_CLASS_NEGATIVE_ZERO; + } else if (float64_is_zero_or_denormal(arg)) { + return FLOAT_CLASS_NEGATIVE_SUBNORMAL; + } else { + return FLOAT_CLASS_NEGATIVE_NORMAL; + } + } else { + if (float64_is_infinity(arg)) { + return FLOAT_CLASS_POSITIVE_INFINITY; + } else if (float64_is_zero(arg)) { + return FLOAT_CLASS_POSITIVE_ZERO; + } else if (float64_is_zero_or_denormal(arg)) { + return FLOAT_CLASS_POSITIVE_SUBNORMAL; + } else { + return FLOAT_CLASS_POSITIVE_NORMAL; + } + } +} + +uint64_t helper_float_class_d(CPUMIPSState *env, uint64_t arg) +{ + return float_class_d(arg, &env->active_fpu.fp_status); +} + +uint32_t float_class_s(uint32_t arg, float_status *status) +{ + if (float32_is_signaling_nan(arg, status)) { + return FLOAT_CLASS_SIGNALING_NAN; + } else if (float32_is_quiet_nan(arg, status)) { + return FLOAT_CLASS_QUIET_NAN; + } else if (float32_is_neg(arg)) { + if (float32_is_infinity(arg)) { + return FLOAT_CLASS_NEGATIVE_INFINITY; + } else if (float32_is_zero(arg)) { + return FLOAT_CLASS_NEGATIVE_ZERO; + } else if (float32_is_zero_or_denormal(arg)) { + return FLOAT_CLASS_NEGATIVE_SUBNORMAL; + } else { + return FLOAT_CLASS_NEGATIVE_NORMAL; + } + } else { + if (float32_is_infinity(arg)) { + return FLOAT_CLASS_POSITIVE_INFINITY; + } else if (float32_is_zero(arg)) { + return FLOAT_CLASS_POSITIVE_ZERO; + } else if (float32_is_zero_or_denormal(arg)) { + return FLOAT_CLASS_POSITIVE_SUBNORMAL; + } else { + return FLOAT_CLASS_POSITIVE_NORMAL; + } + } +} + +uint32_t helper_float_class_s(CPUMIPSState *env, uint32_t arg) +{ + return float_class_s(arg, &env->active_fpu.fp_status); +} /* binary operations */ -#define FLOAT_BINOP(name) \ -uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - uint64_t dt2; \ - \ - dt2 = float64_ ## name(fdt0, fdt1, &env->active_fpu.fp_status);\ - update_fcr31(env, GETPC()); \ - return dt2; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ - uint32_t fst0, uint32_t fst1) \ -{ \ - uint32_t wt2; \ - \ - wt2 = float32_ ## name(fst0, fst1, &env->active_fpu.fp_status);\ - update_fcr31(env, GETPC()); \ - return wt2; \ -} \ - \ -uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ - uint64_t fdt0, \ - uint64_t fdt1) \ -{ \ - uint32_t fst0 = fdt0 & 0XFFFFFFFF; \ - uint32_t fsth0 = fdt0 >> 32; \ - uint32_t fst1 = fdt1 & 0XFFFFFFFF; \ - uint32_t fsth1 = fdt1 >> 32; \ - uint32_t wt2; \ - uint32_t wth2; \ - \ - wt2 = float32_ ## name(fst0, fst1, &env->active_fpu.fp_status); \ - wth2 = float32_ ## name(fsth0, fsth1, &env->active_fpu.fp_status); \ - update_fcr31(env, GETPC()); \ - return ((uint64_t)wth2 << 32) | wt2; \ -} - -FLOAT_BINOP(add) -FLOAT_BINOP(sub) -FLOAT_BINOP(mul) -FLOAT_BINOP(div) -#undef FLOAT_BINOP + +uint64_t helper_float_add_d(CPUMIPSState *env, + uint64_t fdt0, uint64_t fdt1) +{ + uint64_t dt2; + + dt2 = float64_add(fdt0, fdt1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_add_s(CPUMIPSState *env, + uint32_t fst0, uint32_t fst1) +{ + uint32_t wt2; + + wt2 = float32_sub(fst0, fst1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_add_ps(CPUMIPSState *env, + uint64_t fdt0, uint64_t fdt1) +{ + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t wtl2; + uint32_t wth2; + + wtl2 = float32_add(fstl0, fstl1, &env->active_fpu.fp_status); + wth2 = float32_add(fsth0, fsth1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return ((uint64_t)wth2 << 32) | wtl2; +} + +uint64_t helper_float_sub_d(CPUMIPSState *env, + uint64_t fdt0, uint64_t fdt1) +{ + uint64_t dt2; + + dt2 = float64_sub(fdt0, fdt1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_sub_s(CPUMIPSState *env, + uint32_t fst0, uint32_t fst1) +{ + uint32_t wt2; + + wt2 = float32_sub(fst0, fst1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_sub_ps(CPUMIPSState *env, + uint64_t fdt0, uint64_t fdt1) +{ + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t wtl2; + uint32_t wth2; + + wtl2 = float32_sub(fstl0, fstl1, &env->active_fpu.fp_status); + wth2 = float32_sub(fsth0, fsth1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return ((uint64_t)wth2 << 32) | wtl2; +} + +uint64_t helper_float_mul_d(CPUMIPSState *env, + uint64_t fdt0, uint64_t fdt1) +{ + uint64_t dt2; + + dt2 = float64_mul(fdt0, fdt1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_mul_s(CPUMIPSState *env, + uint32_t fst0, uint32_t fst1) +{ + uint32_t wt2; + + wt2 = float32_mul(fst0, fst1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_mul_ps(CPUMIPSState *env, + uint64_t fdt0, uint64_t fdt1) +{ + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t wtl2; + uint32_t wth2; + + wtl2 = float32_mul(fstl0, fstl1, &env->active_fpu.fp_status); + wth2 = float32_mul(fsth0, fsth1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return ((uint64_t)wth2 << 32) | wtl2; +} + +uint64_t helper_float_div_d(CPUMIPSState *env, + uint64_t fdt0, uint64_t fdt1) +{ + uint64_t dt2; + + dt2 = float64_div(fdt0, fdt1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_div_s(CPUMIPSState *env, + uint32_t fst0, uint32_t fst1) +{ + uint32_t wt2; + + wt2 = float32_div(fst0, fst1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_div_ps(CPUMIPSState *env, + uint64_t fdt0, uint64_t fdt1) +{ + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t wtl2; + uint32_t wth2; + + wtl2 = float32_div(fstl0, fstl1, &env->active_fpu.fp_status); + wth2 = float32_div(fsth0, fsth1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return ((uint64_t)wth2 << 32) | wtl2; +} + /* MIPS specific binary operations */ uint64_t helper_float_recip2_d(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) @@ -1235,19 +1372,19 @@ uint32_t helper_float_recip2_s(CPUMIPSState *env, uint32_t fst0, uint32_t fst2) uint64_t helper_float_recip2_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) { - uint32_t fst0 = fdt0 & 0XFFFFFFFF; + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; uint32_t fsth0 = fdt0 >> 32; - uint32_t fst2 = fdt2 & 0XFFFFFFFF; + uint32_t fstl2 = fdt2 & 0XFFFFFFFF; uint32_t fsth2 = fdt2 >> 32; - fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); + fstl2 = float32_mul(fstl0, fstl2, &env->active_fpu.fp_status); fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status); - fst2 = float32_chs(float32_sub(fst2, float32_one, + fstl2 = float32_chs(float32_sub(fstl2, float32_one, &env->active_fpu.fp_status)); fsth2 = float32_chs(float32_sub(fsth2, float32_one, &env->active_fpu.fp_status)); update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; + return ((uint64_t)fsth2 << 32) | fstl2; } uint64_t helper_float_rsqrt2_d(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) @@ -1272,51 +1409,51 @@ uint32_t helper_float_rsqrt2_s(CPUMIPSState *env, uint32_t fst0, uint32_t fst2) uint64_t helper_float_rsqrt2_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) { - uint32_t fst0 = fdt0 & 0XFFFFFFFF; + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; uint32_t fsth0 = fdt0 >> 32; - uint32_t fst2 = fdt2 & 0XFFFFFFFF; + uint32_t fstl2 = fdt2 & 0XFFFFFFFF; uint32_t fsth2 = fdt2 >> 32; - fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); + fstl2 = float32_mul(fstl0, fstl2, &env->active_fpu.fp_status); fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status); - fst2 = float32_sub(fst2, float32_one, &env->active_fpu.fp_status); + fstl2 = float32_sub(fstl2, float32_one, &env->active_fpu.fp_status); fsth2 = float32_sub(fsth2, float32_one, &env->active_fpu.fp_status); - fst2 = float32_chs(float32_div(fst2, FLOAT_TWO32, + fstl2 = float32_chs(float32_div(fstl2, FLOAT_TWO32, &env->active_fpu.fp_status)); fsth2 = float32_chs(float32_div(fsth2, FLOAT_TWO32, &env->active_fpu.fp_status)); update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; + return ((uint64_t)fsth2 << 32) | fstl2; } uint64_t helper_float_addr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1) { - uint32_t fst0 = fdt0 & 0XFFFFFFFF; + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; uint32_t fsth0 = fdt0 >> 32; - uint32_t fst1 = fdt1 & 0XFFFFFFFF; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; uint32_t fsth1 = fdt1 >> 32; - uint32_t fst2; + uint32_t fstl2; uint32_t fsth2; - fst2 = float32_add(fst0, fsth0, &env->active_fpu.fp_status); - fsth2 = float32_add(fst1, fsth1, &env->active_fpu.fp_status); + fstl2 = float32_add(fstl0, fsth0, &env->active_fpu.fp_status); + fsth2 = float32_add(fstl1, fsth1, &env->active_fpu.fp_status); update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; + return ((uint64_t)fsth2 << 32) | fstl2; } uint64_t helper_float_mulr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1) { - uint32_t fst0 = fdt0 & 0XFFFFFFFF; + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; uint32_t fsth0 = fdt0 >> 32; - uint32_t fst1 = fdt1 & 0XFFFFFFFF; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; uint32_t fsth1 = fdt1 >> 32; - uint32_t fst2; + uint32_t fstl2; uint32_t fsth2; - fst2 = float32_mul(fst0, fsth0, &env->active_fpu.fp_status); - fsth2 = float32_mul(fst1, fsth1, &env->active_fpu.fp_status); + fstl2 = float32_mul(fstl0, fsth0, &env->active_fpu.fp_status); + fsth2 = float32_mul(fstl1, fsth1, &env->active_fpu.fp_status); update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; + return ((uint64_t)fsth2 << 32) | fstl2; } #define FLOAT_MINMAX(name, bits, minmaxfunc) \ @@ -1344,60 +1481,171 @@ FLOAT_MINMAX(mina_d, 64, minnummag) #undef FLOAT_MINMAX /* ternary operations */ -#define UNFUSED_FMA(prefix, a, b, c, flags) \ -{ \ - a = prefix##_mul(a, b, &env->active_fpu.fp_status); \ - if ((flags) & float_muladd_negate_c) { \ - a = prefix##_sub(a, c, &env->active_fpu.fp_status); \ - } else { \ - a = prefix##_add(a, c, &env->active_fpu.fp_status); \ - } \ - if ((flags) & float_muladd_negate_result) { \ - a = prefix##_chs(a); \ - } \ -} - -/* FMA based operations */ -#define FLOAT_FMA(name, type) \ -uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ - uint64_t fdt0, uint64_t fdt1, \ - uint64_t fdt2) \ -{ \ - UNFUSED_FMA(float64, fdt0, fdt1, fdt2, type); \ - update_fcr31(env, GETPC()); \ - return fdt0; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ - uint32_t fst0, uint32_t fst1, \ - uint32_t fst2) \ -{ \ - UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ - update_fcr31(env, GETPC()); \ - return fst0; \ -} \ - \ -uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ - uint64_t fdt0, uint64_t fdt1, \ - uint64_t fdt2) \ -{ \ - uint32_t fst0 = fdt0 & 0XFFFFFFFF; \ - uint32_t fsth0 = fdt0 >> 32; \ - uint32_t fst1 = fdt1 & 0XFFFFFFFF; \ - uint32_t fsth1 = fdt1 >> 32; \ - uint32_t fst2 = fdt2 & 0XFFFFFFFF; \ - uint32_t fsth2 = fdt2 >> 32; \ - \ - UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ - UNFUSED_FMA(float32, fsth0, fsth1, fsth2, type); \ - update_fcr31(env, GETPC()); \ - return ((uint64_t)fsth0 << 32) | fst0; \ -} -FLOAT_FMA(madd, 0) -FLOAT_FMA(msub, float_muladd_negate_c) -FLOAT_FMA(nmadd, float_muladd_negate_result) -FLOAT_FMA(nmsub, float_muladd_negate_result | float_muladd_negate_c) -#undef FLOAT_FMA + +uint64_t helper_float_madd_d(CPUMIPSState *env, uint64_t fst0, + uint64_t fst1, uint64_t fst2) +{ + fst0 = float64_mul(fst0, fst1, &env->active_fpu.fp_status); + fst0 = float64_add(fst0, fst2, &env->active_fpu.fp_status); + + update_fcr31(env, GETPC()); + return fst0; +} + +uint32_t helper_float_madd_s(CPUMIPSState *env, uint32_t fst0, + uint32_t fst1, uint32_t fst2) +{ + fst0 = float32_mul(fst0, fst1, &env->active_fpu.fp_status); + fst0 = float32_add(fst0, fst2, &env->active_fpu.fp_status); + + update_fcr31(env, GETPC()); + return fst0; +} + +uint64_t helper_float_madd_ps(CPUMIPSState *env, uint64_t fdt0, + uint64_t fdt1, uint64_t fdt2) +{ + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t fstl2 = fdt2 & 0XFFFFFFFF; + uint32_t fsth2 = fdt2 >> 32; + + fstl0 = float32_mul(fstl0, fstl1, &env->active_fpu.fp_status); + fstl0 = float32_add(fstl0, fstl2, &env->active_fpu.fp_status); + fsth0 = float32_mul(fsth0, fsth1, &env->active_fpu.fp_status); + fsth0 = float32_add(fsth0, fsth2, &env->active_fpu.fp_status); + + update_fcr31(env, GETPC()); + return ((uint64_t)fsth0 << 32) | fstl0; +} + +uint64_t helper_float_msub_d(CPUMIPSState *env, uint64_t fst0, + uint64_t fst1, uint64_t fst2) +{ + fst0 = float64_mul(fst0, fst1, &env->active_fpu.fp_status); + fst0 = float64_sub(fst0, fst2, &env->active_fpu.fp_status); + + update_fcr31(env, GETPC()); + return fst0; +} + +uint32_t helper_float_msub_s(CPUMIPSState *env, uint32_t fst0, + uint32_t fst1, uint32_t fst2) +{ + fst0 = float32_mul(fst0, fst1, &env->active_fpu.fp_status); + fst0 = float32_sub(fst0, fst2, &env->active_fpu.fp_status); + + update_fcr31(env, GETPC()); + return fst0; +} + +uint64_t helper_float_msub_ps(CPUMIPSState *env, uint64_t fdt0, + uint64_t fdt1, uint64_t fdt2) +{ + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t fstl2 = fdt2 & 0XFFFFFFFF; + uint32_t fsth2 = fdt2 >> 32; + + fstl0 = float32_mul(fstl0, fstl1, &env->active_fpu.fp_status); + fstl0 = float32_sub(fstl0, fstl2, &env->active_fpu.fp_status); + fsth0 = float32_mul(fsth0, fsth1, &env->active_fpu.fp_status); + fsth0 = float32_sub(fsth0, fsth2, &env->active_fpu.fp_status); + + update_fcr31(env, GETPC()); + return ((uint64_t)fsth0 << 32) | fstl0; +} + +uint64_t helper_float_nmadd_d(CPUMIPSState *env, uint64_t fst0, + uint64_t fst1, uint64_t fst2) +{ + fst0 = float64_mul(fst0, fst1, &env->active_fpu.fp_status); + fst0 = float64_add(fst0, fst2, &env->active_fpu.fp_status); + fst0 = float64_chs(fst0); + + update_fcr31(env, GETPC()); + return fst0; +} + +uint32_t helper_float_nmadd_s(CPUMIPSState *env, uint32_t fst0, + uint32_t fst1, uint32_t fst2) +{ + fst0 = float32_mul(fst0, fst1, &env->active_fpu.fp_status); + fst0 = float32_add(fst0, fst2, &env->active_fpu.fp_status); + fst0 = float32_chs(fst0); + + update_fcr31(env, GETPC()); + return fst0; +} + +uint64_t helper_float_nmadd_ps(CPUMIPSState *env, uint64_t fdt0, + uint64_t fdt1, uint64_t fdt2) +{ + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t fstl2 = fdt2 & 0XFFFFFFFF; + uint32_t fsth2 = fdt2 >> 32; + + fstl0 = float32_mul(fstl0, fstl1, &env->active_fpu.fp_status); + fstl0 = float32_add(fstl0, fstl2, &env->active_fpu.fp_status); + fstl0 = float32_chs(fstl0); + fsth0 = float32_mul(fsth0, fsth1, &env->active_fpu.fp_status); + fsth0 = float32_add(fsth0, fsth2, &env->active_fpu.fp_status); + fsth0 = float32_chs(fsth0); + + update_fcr31(env, GETPC()); + return ((uint64_t)fsth0 << 32) | fstl0; +} + +uint64_t helper_float_nmsub_d(CPUMIPSState *env, uint64_t fst0, + uint64_t fst1, uint64_t fst2) +{ + fst0 = float64_mul(fst0, fst1, &env->active_fpu.fp_status); + fst0 = float64_sub(fst0, fst2, &env->active_fpu.fp_status); + fst0 = float64_chs(fst0); + + update_fcr31(env, GETPC()); + return fst0; +} + +uint32_t helper_float_nmsub_s(CPUMIPSState *env, uint32_t fst0, + uint32_t fst1, uint32_t fst2) +{ + fst0 = float32_mul(fst0, fst1, &env->active_fpu.fp_status); + fst0 = float32_sub(fst0, fst2, &env->active_fpu.fp_status); + fst0 = float32_chs(fst0); + + update_fcr31(env, GETPC()); + return fst0; +} + +uint64_t helper_float_nmsub_ps(CPUMIPSState *env, uint64_t fdt0, + uint64_t fdt1, uint64_t fdt2) +{ + uint32_t fstl0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fstl1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t fstl2 = fdt2 & 0XFFFFFFFF; + uint32_t fsth2 = fdt2 >> 32; + + fstl0 = float32_mul(fstl0, fstl1, &env->active_fpu.fp_status); + fstl0 = float32_sub(fstl0, fstl2, &env->active_fpu.fp_status); + fstl0 = float32_chs(fstl0); + fsth0 = float32_mul(fsth0, fsth1, &env->active_fpu.fp_status); + fsth0 = float32_sub(fsth0, fsth2, &env->active_fpu.fp_status); + fsth0 = float32_chs(fsth0); + + update_fcr31(env, GETPC()); + return ((uint64_t)fsth0 << 32) | fstl0; +} + #define FLOAT_FMADDSUB(name, bits, muladd_arg) \ uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ diff --git a/target/mips/internal.h b/target/mips/internal.h index 1bf274b3ef..7f159a9230 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -36,7 +36,9 @@ struct mips_def_t { int32_t CP0_Config5; int32_t CP0_Config5_rw_bitmask; int32_t CP0_Config6; + int32_t CP0_Config6_rw_bitmask; int32_t CP0_Config7; + int32_t CP0_Config7_rw_bitmask; target_ulong CP0_LLAddr_rw_bitmask; int CP0_LLAddr_shift; int32_t SYNCI_Step; @@ -224,7 +226,6 @@ uint32_t float_class_s(uint32_t arg, float_status *fst); uint64_t float_class_d(uint64_t arg, float_status *fst); extern unsigned int ieee_rm[]; -int ieee_ex_to_mips(int xcpt); void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask); static inline void restore_rounding_mode(CPUMIPSState *env) diff --git a/target/mips/lmi_helper.c b/target/mips/lmmi_helper.c index 6c645cf679..6c645cf679 100644 --- a/target/mips/lmi_helper.c +++ b/target/mips/lmmi_helper.c diff --git a/target/mips/mips-defs.h b/target/mips/mips-defs.h index a831bb4384..0c129106c8 100644 --- a/target/mips/mips-defs.h +++ b/target/mips/mips-defs.h @@ -15,7 +15,7 @@ * ------------------------------------------------ */ /* - * bits 0-31: MIPS base instruction sets + * bits 0-23: MIPS base instruction sets */ #define ISA_MIPS1 0x0000000000000001ULL #define ISA_MIPS2 0x0000000000000002ULL @@ -34,30 +34,33 @@ #define ISA_MIPS64R6 0x0000000000004000ULL #define ISA_NANOMIPS32 0x0000000000008000ULL /* - * bits 32-47: MIPS ASEs + * bits 24-39: MIPS ASEs */ -#define ASE_MIPS16 0x0000000100000000ULL -#define ASE_MIPS3D 0x0000000200000000ULL -#define ASE_MDMX 0x0000000400000000ULL -#define ASE_DSP 0x0000000800000000ULL -#define ASE_DSP_R2 0x0000001000000000ULL -#define ASE_DSP_R3 0x0000002000000000ULL -#define ASE_MT 0x0000004000000000ULL -#define ASE_SMARTMIPS 0x0000008000000000ULL -#define ASE_MICROMIPS 0x0000010000000000ULL -#define ASE_MSA 0x0000020000000000ULL +#define ASE_MIPS16 0x0000000001000000ULL +#define ASE_MIPS3D 0x0000000002000000ULL +#define ASE_MDMX 0x0000000004000000ULL +#define ASE_DSP 0x0000000008000000ULL +#define ASE_DSP_R2 0x0000000010000000ULL +#define ASE_DSP_R3 0x0000000020000000ULL +#define ASE_MT 0x0000000040000000ULL +#define ASE_SMARTMIPS 0x0000000080000000ULL +#define ASE_MICROMIPS 0x0000000100000000ULL +#define ASE_MSA 0x0000000200000000ULL /* - * bits 48-55: vendor-specific base instruction sets + * bits 40-51: vendor-specific base instruction sets */ -#define INSN_LOONGSON2E 0x0001000000000000ULL -#define INSN_LOONGSON2F 0x0002000000000000ULL -#define INSN_VR54XX 0x0004000000000000ULL -#define INSN_R5900 0x0008000000000000ULL +#define INSN_VR54XX 0x0000010000000000ULL +#define INSN_R5900 0x0000020000000000ULL +#define INSN_LOONGSON2E 0x0000040000000000ULL +#define INSN_LOONGSON2F 0x0000080000000000ULL +#define INSN_LOONGSON3A 0x0000100000000000ULL /* - * bits 56-63: vendor-specific ASEs + * bits 52-63: vendor-specific ASEs */ -#define ASE_MMI 0x0100000000000000ULL -#define ASE_MXU 0x0200000000000000ULL +#define ASE_MMI 0x0010000000000000ULL +#define ASE_MXU 0x0020000000000000ULL +#define ASE_LMMI 0x0040000000000000ULL +#define ASE_LEXT 0x0080000000000000ULL /* MIPS CPU defines. */ #define CPU_MIPS1 (ISA_MIPS1) @@ -94,6 +97,8 @@ /* Wave Computing: "nanoMIPS" */ #define CPU_NANOMIPS32 (CPU_MIPS32R6 | ISA_NANOMIPS32) +#define CPU_LOONGSON3A (CPU_MIPS64R2 | INSN_LOONGSON3A) + /* * Strictly follow the architecture standard: * - Disallow "special" instruction handling for PMON/SPIM. diff --git a/target/mips/msa_helper.c b/target/mips/msa_helper.c index 3c7012c0b8..c3b271934a 100644 --- a/target/mips/msa_helper.c +++ b/target/mips/msa_helper.c @@ -5419,54 +5419,81 @@ static inline void check_msacsr_cause(CPUMIPSState *env, uintptr_t retaddr) #define CLEAR_IS_INEXACT 2 #define RECIPROCAL_INEXACT 4 -static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) + +static inline int ieee_to_mips_xcpt_msa(int ieee_xcpt) { - int ieee_ex; + int mips_xcpt = 0; - int c; + if (ieee_xcpt & float_flag_invalid) { + mips_xcpt |= FP_INVALID; + } + if (ieee_xcpt & float_flag_overflow) { + mips_xcpt |= FP_OVERFLOW; + } + if (ieee_xcpt & float_flag_underflow) { + mips_xcpt |= FP_UNDERFLOW; + } + if (ieee_xcpt & float_flag_divbyzero) { + mips_xcpt |= FP_DIV0; + } + if (ieee_xcpt & float_flag_inexact) { + mips_xcpt |= FP_INEXACT; + } + + return mips_xcpt; +} + +static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) +{ + int ieee_exception_flags; + int mips_exception_flags = 0; int cause; int enable; - ieee_ex = get_float_exception_flags(&env->active_tc.msa_fp_status); + ieee_exception_flags = get_float_exception_flags( + &env->active_tc.msa_fp_status); /* QEMU softfloat does not signal all underflow cases */ if (denormal) { - ieee_ex |= float_flag_underflow; + ieee_exception_flags |= float_flag_underflow; + } + if (ieee_exception_flags) { + mips_exception_flags = ieee_to_mips_xcpt_msa(ieee_exception_flags); } - - c = ieee_ex_to_mips(ieee_ex); enable = GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED; /* Set Inexact (I) when flushing inputs to zero */ - if ((ieee_ex & float_flag_input_denormal) && + if ((ieee_exception_flags & float_flag_input_denormal) && (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { if (action & CLEAR_IS_INEXACT) { - c &= ~FP_INEXACT; + mips_exception_flags &= ~FP_INEXACT; } else { - c |= FP_INEXACT; + mips_exception_flags |= FP_INEXACT; } } /* Set Inexact (I) and Underflow (U) when flushing outputs to zero */ - if ((ieee_ex & float_flag_output_denormal) && + if ((ieee_exception_flags & float_flag_output_denormal) && (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { - c |= FP_INEXACT; + mips_exception_flags |= FP_INEXACT; if (action & CLEAR_FS_UNDERFLOW) { - c &= ~FP_UNDERFLOW; + mips_exception_flags &= ~FP_UNDERFLOW; } else { - c |= FP_UNDERFLOW; + mips_exception_flags |= FP_UNDERFLOW; } } /* Set Inexact (I) when Overflow (O) is not enabled */ - if ((c & FP_OVERFLOW) != 0 && (enable & FP_OVERFLOW) == 0) { - c |= FP_INEXACT; + if ((mips_exception_flags & FP_OVERFLOW) != 0 && + (enable & FP_OVERFLOW) == 0) { + mips_exception_flags |= FP_INEXACT; } /* Clear Exact Underflow when Underflow (U) is not enabled */ - if ((c & FP_UNDERFLOW) != 0 && (enable & FP_UNDERFLOW) == 0 && - (c & FP_INEXACT) == 0) { - c &= ~FP_UNDERFLOW; + if ((mips_exception_flags & FP_UNDERFLOW) != 0 && + (enable & FP_UNDERFLOW) == 0 && + (mips_exception_flags & FP_INEXACT) == 0) { + mips_exception_flags &= ~FP_UNDERFLOW; } /* @@ -5474,11 +5501,11 @@ static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) * divide by zero */ if ((action & RECIPROCAL_INEXACT) && - (c & (FP_INVALID | FP_DIV0)) == 0) { - c = FP_INEXACT; + (mips_exception_flags & (FP_INVALID | FP_DIV0)) == 0) { + mips_exception_flags = FP_INEXACT; } - cause = c & enable; /* all current enabled exceptions */ + cause = mips_exception_flags & enable; /* all current enabled exceptions */ if (cause == 0) { /* @@ -5486,7 +5513,7 @@ static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) * with all current exceptions */ SET_FP_CAUSE(env->active_tc.msacsr, - (GET_FP_CAUSE(env->active_tc.msacsr) | c)); + (GET_FP_CAUSE(env->active_tc.msacsr) | mips_exception_flags)); } else { /* Current exceptions are enabled */ if ((env->active_tc.msacsr & MSACSR_NX_MASK) == 0) { @@ -5495,11 +5522,11 @@ static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) * with all enabled exceptions */ SET_FP_CAUSE(env->active_tc.msacsr, - (GET_FP_CAUSE(env->active_tc.msacsr) | c)); + (GET_FP_CAUSE(env->active_tc.msacsr) | mips_exception_flags)); } } - return c; + return mips_exception_flags; } static inline int get_enabled_exceptions(const CPUMIPSState *env, int c) diff --git a/target/mips/translate.c b/target/mips/translate.c index 25b595a17d..2caf4cba5a 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -31206,7 +31206,9 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Config5 = env->cpu_model->CP0_Config5; env->CP0_Config5_rw_bitmask = env->cpu_model->CP0_Config5_rw_bitmask; env->CP0_Config6 = env->cpu_model->CP0_Config6; + env->CP0_Config6_rw_bitmask = env->cpu_model->CP0_Config6_rw_bitmask; env->CP0_Config7 = env->cpu_model->CP0_Config7; + env->CP0_Config7_rw_bitmask = env->cpu_model->CP0_Config7_rw_bitmask; env->CP0_LLAddr_rw_bitmask = env->cpu_model->CP0_LLAddr_rw_bitmask << env->cpu_model->CP0_LLAddr_shift; env->CP0_LLAddr_shift = env->cpu_model->CP0_LLAddr_shift; diff --git a/target/mips/translate_init.inc.c b/target/mips/translate_init.inc.c index 6d145a905a..637caccd89 100644 --- a/target/mips/translate_init.inc.c +++ b/target/mips/translate_init.inc.c @@ -366,7 +366,7 @@ const mips_def_t mips_defs[] = }, { /* FIXME: - * Config3: CMGCR, PW, VZ, CTXTC, CDMM, TL + * Config3: VZ, CTXTC, CDMM, TL * Config4: MMUExtDef * Config5: MRP * FIR(FCR0): Has2008 @@ -380,10 +380,11 @@ const mips_def_t mips_defs[] = (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | (1 << CP0C1_PC) | (1 << CP0C1_FP), .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) | + .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | + (1 << CP0C3_CMGCR) | (1 << CP0C3_MSAP) | (1 << CP0C3_BP) | (1 << CP0C3_BI) | (1 << CP0C3_SC) | - (1 << CP0C3_ULRI) | (1 << CP0C3_RXI) | (1 << CP0C3_LPA) | - (1 << CP0C3_VInt), + (1 << CP0C3_PW) | (1 << CP0C3_ULRI) | (1 << CP0C3_RXI) | + (1 << CP0C3_LPA) | (1 << CP0C3_VInt), .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (2 << CP0C4_IE) | (0x1c << CP0C4_KScrExist), .CP0_Config4_rw_bitmask = 0, @@ -802,6 +803,92 @@ const mips_def_t mips_defs[] = .mmu_type = MMU_TYPE_R4000, }, { + .name = "Loongson-3A1000", + .CP0_PRid = 0x6305, + /* 64KB I-cache and d-cache. 4 way with 32 bit cache line size. */ + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) | + (3 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | + (3 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | + (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2 | (7 << CP0C2_SS) | (4 << CP0C2_SL) | + (3 << CP0C2_SA), + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_LPA), + .CP0_LLAddr_rw_bitmask = 0, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x74D8FFFF, + .CP0_PageGrain = (1 << CP0PG_ELPA), + .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA), + .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV) | (0x1 << FCR0_F64) | + (0x1 << FCR0_PS) | (0x1 << FCR0_L) | (0x1 << FCR0_W) | + (0x1 << FCR0_D) | (0x1 << FCR0_S), + .CP1_fcr31 = 0, + .CP1_fcr31_rw_bitmask = 0xFF83FFFF, + .SEGBITS = 42, + .PABITS = 48, + .insn_flags = CPU_LOONGSON3A, + .mmu_type = MMU_TYPE_R4000, + }, + { + .name = "Loongson-3A4000", + .CP0_PRid = 0x14C000, + /* 64KB I-cache and d-cache. 4 way with 32 bit cache line size. */ + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) | + (2 << CP0C1_IS) | (5 << CP0C1_IL) | (3 << CP0C1_IA) | + (2 << CP0C1_DS) | (5 << CP0C1_DL) | (3 << CP0C1_DA) | + (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2 | (5 << CP0C2_SS) | (5 << CP0C2_SL) | + (15 << CP0C2_SA), + .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) | + (1 << CP0C3_BP) | (1 << CP0C3_BI) | (1 << CP0C3_ULRI) | + (1 << CP0C3_RXI) | (1 << CP0C3_LPA) | (1 << CP0C3_VInt), + .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (2 << CP0C4_IE) | + (1 << CP0C4_AE) | (0x1c << CP0C4_KScrExist), + .CP0_Config4_rw_bitmask = 0, + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_CRCP) | (1 << CP0C5_NFExists), + .CP0_Config5_rw_bitmask = (1 << CP0C5_K) | (1 << CP0C5_CV) | + (1 << CP0C5_MSAEn) | (1 << CP0C5_UFE) | + (1 << CP0C5_FRE) | (1 << CP0C5_SBRI), + .CP0_Config6 = (1 << CP0C6_VCLRU) | (1 << CP0C6_DCLRU) | + (1 << CP0C6_SFBEN) | (1 << CP0C6_VLTINT) | + (1 << CP0C6_INSTPREF) | (1 << CP0C6_DATAPREF), + .CP0_Config6_rw_bitmask = (1 << CP0C6_BPPASS) | (0x3f << CP0C6_KPOS) | + (1 << CP0C6_KE) | (1 << CP0C6_VTLBONLY) | + (1 << CP0C6_LASX) | (1 << CP0C6_SSEN) | + (1 << CP0C6_DISDRTIME) | (1 << CP0C6_PIXNUEN) | + (1 << CP0C6_SCRAND) | (1 << CP0C6_LLEXCEN) | + (1 << CP0C6_DISVC) | (1 << CP0C6_VCLRU) | + (1 << CP0C6_DCLRU) | (1 << CP0C6_PIXUEN) | + (1 << CP0C6_DISBLKLYEN) | (1 << CP0C6_UMEMUALEN) | + (1 << CP0C6_SFBEN) | (1 << CP0C6_FLTINT) | + (1 << CP0C6_VLTINT) | (1 << CP0C6_DISBTB) | + (3 << CP0C6_STPREFCTL) | (1 << CP0C6_INSTPREF) | + (1 << CP0C6_DATAPREF), + .CP0_Config7 = 0, + .CP0_Config7_rw_bitmask = (1 << CP0C7_NAPCGEN) | (1 << CP0C7_UNIMUEN) | + (1 << CP0C7_VFPUCGEN), + .CP0_LLAddr_rw_bitmask = 1, + .SYNCI_Step = 16, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x7DDBFFFF, + .CP0_PageGrain = (1 << CP0PG_ELPA), + .CP0_PageGrain_rw_bitmask = (1U << CP0PG_RIE) | (1 << CP0PG_XIE) | + (1 << CP0PG_ELPA) | (1 << CP0PG_IEC), + .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV) | (0x1 << FCR0_F64) | + (0x1 << FCR0_PS) | (0x1 << FCR0_L) | (0x1 << FCR0_W) | + (0x1 << FCR0_D) | (0x1 << FCR0_S), + .CP1_fcr31 = 0, + .CP1_fcr31_rw_bitmask = 0xFF83FFFF, + .SEGBITS = 48, + .PABITS = 48, + .insn_flags = CPU_LOONGSON3A, + .mmu_type = MMU_TYPE_R4000, + }, + { /* A generic CPU providing MIPS64 DSP R2 ASE features. FIXME: Eventually this should be replaced by a real CPU model. */ .name = "mips64dspr2", diff --git a/target/sparc/int32_helper.c b/target/sparc/int32_helper.c index 813b47dbb2..9a71e1abd8 100644 --- a/target/sparc/int32_helper.c +++ b/target/sparc/int32_helper.c @@ -23,9 +23,7 @@ #include "exec/log.h" #include "sysemu/runstate.h" -#define DEBUG_PCALL -#ifdef DEBUG_PCALL static const char * const excp_names[0x80] = { [TT_TFAULT] = "Instruction Access Fault", [TT_ILL_INSN] = "Illegal Instruction", @@ -58,7 +56,14 @@ static const char * const excp_names[0x80] = { [TT_DIV_ZERO] = "Division By Zero", [TT_NCP_INSN] = "Coprocessor Disabled", }; -#endif + +static const char *excp_name_str(int32_t exception_index) +{ + if (exception_index < 0 || exception_index >= ARRAY_SIZE(excp_names)) { + return "Unknown"; + } + return excp_names[exception_index]; +} void sparc_cpu_do_interrupt(CPUState *cs) { @@ -71,7 +76,6 @@ void sparc_cpu_do_interrupt(CPUState *cs) cpu_get_psr(env); } -#ifdef DEBUG_PCALL if (qemu_loglevel_mask(CPU_LOG_INT)) { static int count; const char *name; @@ -81,10 +85,7 @@ void sparc_cpu_do_interrupt(CPUState *cs) } else if (intno >= 0x80) { name = "Trap Instruction"; } else { - name = excp_names[intno]; - if (!name) { - name = "Unknown"; - } + name = excp_name_str(intno); } qemu_log("%6d: %s (v=%02x)\n", count, name, intno); @@ -104,15 +105,15 @@ void sparc_cpu_do_interrupt(CPUState *cs) #endif count++; } -#endif #if !defined(CONFIG_USER_ONLY) if (env->psret == 0) { if (cs->exception_index == 0x80 && env->def.features & CPU_FEATURE_TA0_SHUTDOWN) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } else { - cpu_abort(cs, "Trap 0x%02x while interrupts disabled, Error state", - cs->exception_index); + cpu_abort(cs, "Trap 0x%02x (%s) while interrupts disabled, " + "Error state", + cs->exception_index, excp_name_str(cs->exception_index)); } return; } diff --git a/tests/decode/err_pattern_group_nest1.decode b/tests/decode/err_pattern_group_nest1.decode index 92e971c3c5..7d09891a1c 100644 --- a/tests/decode/err_pattern_group_nest1.decode +++ b/tests/decode/err_pattern_group_nest1.decode @@ -3,11 +3,12 @@ %sub1 0:8 %sub2 8:8 -%sub3 16:8 -%sub4 24:8 -# Groups with no overlap are supposed to fail +# Make sure braces are matched { - top 00000000 00000000 00000000 00000000 - sub4 ........ ........ ........ ........ %sub1 %sub2 %sub3 %sub4 + top 00000000 00000000 00000000 00000000 + [ + sub1 00000000 00000000 00000000 ........ %sub1 + sub2 00000000 00000000 ........ ........ %sub1 %sub2 + } } diff --git a/tests/decode/err_pattern_group_nest2.decode b/tests/decode/err_pattern_group_nest2.decode new file mode 100644 index 0000000000..c172239e9b --- /dev/null +++ b/tests/decode/err_pattern_group_nest2.decode @@ -0,0 +1,6 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +# Make sure braces are matched +{ + [ diff --git a/tests/decode/err_pattern_group_nest3.decode b/tests/decode/err_pattern_group_nest3.decode new file mode 100644 index 0000000000..b085d01410 --- /dev/null +++ b/tests/decode/err_pattern_group_nest3.decode @@ -0,0 +1,14 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +%sub1 0:8 +%sub2 8:8 + +# The exclusive group should error for overlap. +{ + top 00000000 00000000 00000000 00000000 + [ + sub1 00000000 00000000 00000000 ........ %sub1 + sub2 00000000 00000000 ........ ........ %sub1 %sub2 + ] +} diff --git a/tests/decode/succ_pattern_group_nest2.decode b/tests/decode/succ_pattern_group_nest2.decode new file mode 100644 index 0000000000..8d5ab4b2d3 --- /dev/null +++ b/tests/decode/succ_pattern_group_nest2.decode @@ -0,0 +1,13 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +%sub1 0:8 +%sub2 8:8 +%sub3 16:8 +%sub4 24:8 + +# Group with complete overlap of the two patterns +{ + top 00000000 00000000 00000000 00000000 + sub4 ........ ........ ........ ........ %sub1 %sub2 %sub3 %sub4 +} diff --git a/tests/decode/succ_pattern_group_nest3.decode b/tests/decode/succ_pattern_group_nest3.decode new file mode 100644 index 0000000000..156249f090 --- /dev/null +++ b/tests/decode/succ_pattern_group_nest3.decode @@ -0,0 +1,11 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +{ + [ + sub1 00000000 a:8 b:8 c:8 + sub2 00000001 a:8 b:8 c:8 + sub3 00000010 a:8 b:8 c:8 + ] + sub4 000000 d:2 a:8 b:8 c:8 +} diff --git a/tests/decode/succ_pattern_group_nest4.decode b/tests/decode/succ_pattern_group_nest4.decode new file mode 100644 index 0000000000..dc54a1d285 --- /dev/null +++ b/tests/decode/succ_pattern_group_nest4.decode @@ -0,0 +1,13 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +# Verify deeper nesting, and a single element in the groups. +{ + [ + { + [ + sub1 00000000 a:8 b:8 c:8 + ] + } + ] +} diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index ed46bd98eb..981b7fcf2a 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -130,7 +130,7 @@ docker-image-debian-sparc64-cross: docker-image-debian10 docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools -docker-image-tricore-cross: docker-image-debian9 +docker-image-debian-tricore-cross: docker-image-debian9 docker-image-debian-arm64-test-cross: docker-image-debian11 # These images may be good enough for building tests but not for test builds diff --git a/tests/docker/dockerfiles/centos7.docker b/tests/docker/dockerfiles/centos7.docker index 9a2a2e515d..e197acdc3c 100644 --- a/tests/docker/dockerfiles/centos7.docker +++ b/tests/docker/dockerfiles/centos7.docker @@ -5,13 +5,11 @@ RUN yum -y update # Please keep this list sorted alphabetically ENV PACKAGES \ - bison \ bzip2 \ bzip2-devel \ ccache \ csnappy-devel \ dbus-daemon \ - flex \ gcc-c++ \ gcc \ gettext \ diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index bfa0d33c9c..9852c5b9ee 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -3,11 +3,9 @@ FROM centos:8.1.1911 RUN dnf -y update ENV PACKAGES \ SDL-devel \ - bison \ bzip2 \ bzip2-devel \ dbus-daemon \ - flex \ gcc \ gcc-c++ \ gettext \ diff --git a/tests/docker/dockerfiles/debian-xtensa-cross.docker b/tests/docker/dockerfiles/debian-xtensa-cross.docker index e6f93f65ee..beb73f46ba 100644 --- a/tests/docker/dockerfiles/debian-xtensa-cross.docker +++ b/tests/docker/dockerfiles/debian-xtensa-cross.docker @@ -11,11 +11,9 @@ RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt-get install -y --no-install-recommends \ - bison \ build-essential \ ca-certificates \ curl \ - flex \ gettext \ git \ python3-minimal diff --git a/tests/docker/dockerfiles/debian10.docker b/tests/docker/dockerfiles/debian10.docker index 0769700a41..bcdff04ddf 100644 --- a/tests/docker/dockerfiles/debian10.docker +++ b/tests/docker/dockerfiles/debian10.docker @@ -18,12 +18,10 @@ RUN apt update && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ bc \ - bison \ build-essential \ ca-certificates \ clang \ dbus \ - flex \ gdb-multiarch \ gettext \ git \ diff --git a/tests/docker/dockerfiles/debian9.docker b/tests/docker/dockerfiles/debian9.docker index 08cc970feb..0f0ebe530a 100644 --- a/tests/docker/dockerfiles/debian9.docker +++ b/tests/docker/dockerfiles/debian9.docker @@ -18,11 +18,9 @@ RUN apt update && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ bc \ - bison \ build-essential \ ca-certificates \ clang \ - flex \ gdb-multiarch \ gettext \ git \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 179575ecaa..92b6e11c8a 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -3,7 +3,6 @@ FROM fedora:30 # Please keep this list sorted alphabetically ENV PACKAGES \ bc \ - bison \ brlapi-devel \ bzip2 \ bzip2-devel \ @@ -13,7 +12,6 @@ ENV PACKAGES \ dbus-daemon \ device-mapper-multipath-devel \ findutils \ - flex \ gcc \ gcc-c++ \ gettext \ diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker index eeb3b22bf2..161806e6b8 100644 --- a/tests/docker/dockerfiles/ubuntu.docker +++ b/tests/docker/dockerfiles/ubuntu.docker @@ -9,8 +9,8 @@ # system won't pick up that it has changed. # -FROM ubuntu:19.04 -ENV PACKAGES flex bison \ +FROM ubuntu:20.04 +ENV PACKAGES \ ccache \ clang \ dbus \ diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker index f66b06f4cf..a10ea2850b 100644 --- a/tests/docker/dockerfiles/ubuntu1804.docker +++ b/tests/docker/dockerfiles/ubuntu1804.docker @@ -1,5 +1,5 @@ FROM ubuntu:18.04 -ENV PACKAGES flex bison \ +ENV PACKAGES \ ccache \ clang \ gcc \ diff --git a/tests/plugin/mem.c b/tests/plugin/mem.c index 878abf09d1..4725bd851d 100644 --- a/tests/plugin/mem.c +++ b/tests/plugin/mem.c @@ -28,7 +28,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) g_string_printf(out, "mem accesses: %" PRIu64 "\n", mem_count); if (do_haddr) { - g_string_append_printf(out, "io accesses: %" PRIu64 "\n", mem_count); + g_string_append_printf(out, "io accesses: %" PRIu64 "\n", io_count); } qemu_plugin_outs(out->str); } diff --git a/tests/qemu-iotests/031.out b/tests/qemu-iotests/031.out index 5a4beda6a2..4b21d6a9ba 100644 --- a/tests/qemu-iotests/031.out +++ b/tests/qemu-iotests/031.out @@ -25,7 +25,7 @@ refcount_order 4 header_length 72 Header extension: -magic 0x12345678 +magic 0x12345678 (<unknown>) length 31 data 'This is a test header extension' @@ -53,7 +53,7 @@ refcount_order 4 header_length 72 Header extension: -magic 0x12345678 +magic 0x12345678 (<unknown>) length 31 data 'This is a test header extension' @@ -81,12 +81,12 @@ refcount_order 4 header_length 72 Header extension: -magic 0xe2792aca +magic 0xe2792aca (Backing format) length 11 data 'host_device' Header extension: -magic 0x12345678 +magic 0x12345678 (<unknown>) length 31 data 'This is a test header extension' @@ -116,12 +116,12 @@ refcount_order 4 header_length 112 Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> Header extension: -magic 0x12345678 +magic 0x12345678 (<unknown>) length 31 data 'This is a test header extension' @@ -149,12 +149,12 @@ refcount_order 4 header_length 112 Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> Header extension: -magic 0x12345678 +magic 0x12345678 (<unknown>) length 31 data 'This is a test header extension' @@ -182,17 +182,17 @@ refcount_order 4 header_length 112 Header extension: -magic 0xe2792aca +magic 0xe2792aca (Backing format) length 11 data 'host_device' Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> Header extension: -magic 0x12345678 +magic 0x12345678 (<unknown>) length 31 data 'This is a test header extension' diff --git a/tests/qemu-iotests/036.out b/tests/qemu-iotests/036.out index e409acf60e..a9bed828e5 100644 --- a/tests/qemu-iotests/036.out +++ b/tests/qemu-iotests/036.out @@ -25,7 +25,7 @@ incompatible_features [] compatible_features [] autoclear_features [63] Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> @@ -37,7 +37,7 @@ incompatible_features [] compatible_features [] autoclear_features [] Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index a51ad1b5ba..2f03cf045c 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -25,7 +25,7 @@ refcount_order 4 header_length 112 Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> @@ -83,7 +83,7 @@ refcount_order 4 header_length 112 Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> @@ -139,7 +139,7 @@ refcount_order 4 header_length 112 Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> @@ -194,7 +194,7 @@ refcount_order 4 header_length 112 Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> @@ -263,7 +263,7 @@ refcount_order 4 header_length 112 Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> @@ -325,7 +325,7 @@ refcount_order 4 header_length 112 Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> @@ -354,7 +354,7 @@ refcount_order 4 header_length 112 Header extension: -magic 0x6803f857 +magic 0x6803f857 (Feature table) length 336 data <binary> diff --git a/tests/qemu-iotests/291 b/tests/qemu-iotests/291 index 3ca83b9cd1..404f8521f7 100755 --- a/tests/qemu-iotests/291 +++ b/tests/qemu-iotests/291 @@ -62,6 +62,8 @@ $QEMU_IO -c 'w 1M 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io $QEMU_IMG bitmap --disable -f $IMGFMT "$TEST_IMG" b1 $QEMU_IMG bitmap --enable -f $IMGFMT "$TEST_IMG" b2 $QEMU_IO -c 'w 2M 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io +echo "Check resulting qcow2 header extensions:" +$PYTHON qcow2.py "$TEST_IMG" dump-header-exts echo echo "=== Bitmap preservation not possible to non-qcow2 ===" @@ -77,7 +79,7 @@ echo # Only bitmaps from the active layer are copied $QEMU_IMG convert --bitmaps -O qcow2 "$TEST_IMG.orig" "$TEST_IMG" -$QEMU_IMG info "$TEST_IMG" | _filter_img_info --format-specific +_img_info --format-specific # But we can also merge in bitmaps from other layers. This test is a bit # contrived to cover more code paths, in reality, you could merge directly # into b0 without going through tmp @@ -87,7 +89,9 @@ $QEMU_IMG bitmap --add --merge b0 -b "$TEST_IMG.base" -F $IMGFMT \ $QEMU_IMG bitmap --merge tmp -f $IMGFMT "$TEST_IMG" b0 $QEMU_IMG bitmap --remove --image-opts \ driver=$IMGFMT,file.driver=file,file.filename="$TEST_IMG" tmp -$QEMU_IMG info "$TEST_IMG" | _filter_img_info --format-specific +_img_info --format-specific +echo "Check resulting qcow2 header extensions:" +$PYTHON qcow2.py "$TEST_IMG" dump-header-exts echo echo "=== Check bitmap contents ===" diff --git a/tests/qemu-iotests/291.out b/tests/qemu-iotests/291.out index 8c62017567..08bfaaaa6b 100644 --- a/tests/qemu-iotests/291.out +++ b/tests/qemu-iotests/291.out @@ -14,6 +14,25 @@ wrote 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 1048576/1048576 bytes at offset 2097152 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Check resulting qcow2 header extensions: +Header extension: +magic 0xe2792aca (Backing format) +length 5 +data 'qcow2' + +Header extension: +magic 0x6803f857 (Feature table) +length 336 +data <binary> + +Header extension: +magic 0x23852875 (Bitmaps) +length 24 +nb_bitmaps 2 +reserved32 0 +bitmap_directory_size 0x40 +bitmap_directory_offset 0x510000 + === Bitmap preservation not possible to non-qcow2 === @@ -24,7 +43,7 @@ qemu-img: Format driver 'raw' does not support bitmaps image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 10 MiB (10485760 bytes) -disk size: 4.39 MiB +cluster_size: 65536 Format specific information: compat: 1.1 compression type: zlib @@ -44,7 +63,7 @@ Format specific information: image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 10 MiB (10485760 bytes) -disk size: 4.48 MiB +cluster_size: 65536 Format specific information: compat: 1.1 compression type: zlib @@ -65,6 +84,20 @@ Format specific information: granularity: 65536 refcount bits: 16 corrupt: false +Check resulting qcow2 header extensions: +Header extension: +magic 0x6803f857 (Feature table) +length 336 +data <binary> + +Header extension: +magic 0x23852875 (Bitmaps) +length 24 +nb_bitmaps 3 +reserved32 0 +bitmap_directory_size 0x60 +bitmap_directory_offset 0x520000 + === Check bitmap contents === diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py index 94a07b2f6f..8c187e9a72 100755 --- a/tests/qemu-iotests/qcow2.py +++ b/tests/qemu-iotests/qcow2.py @@ -1,181 +1,50 @@ #!/usr/bin/env python3 +# +# Manipulations with qcow2 image +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# import sys -import struct -import string - -class QcowHeaderExtension: - - def __init__(self, magic, length, data): - if length % 8 != 0: - padding = 8 - (length % 8) - data += b"\0" * padding - - self.magic = magic - self.length = length - self.data = data - - @classmethod - def create(cls, magic, data): - return QcowHeaderExtension(magic, len(data), data) - -class QcowHeader: - - uint32_t = 'I' - uint64_t = 'Q' - - fields = [ - # Version 2 header fields - [ uint32_t, '%#x', 'magic' ], - [ uint32_t, '%d', 'version' ], - [ uint64_t, '%#x', 'backing_file_offset' ], - [ uint32_t, '%#x', 'backing_file_size' ], - [ uint32_t, '%d', 'cluster_bits' ], - [ uint64_t, '%d', 'size' ], - [ uint32_t, '%d', 'crypt_method' ], - [ uint32_t, '%d', 'l1_size' ], - [ uint64_t, '%#x', 'l1_table_offset' ], - [ uint64_t, '%#x', 'refcount_table_offset' ], - [ uint32_t, '%d', 'refcount_table_clusters' ], - [ uint32_t, '%d', 'nb_snapshots' ], - [ uint64_t, '%#x', 'snapshot_offset' ], - - # Version 3 header fields - [ uint64_t, 'mask', 'incompatible_features' ], - [ uint64_t, 'mask', 'compatible_features' ], - [ uint64_t, 'mask', 'autoclear_features' ], - [ uint32_t, '%d', 'refcount_order' ], - [ uint32_t, '%d', 'header_length' ], - ]; - - fmt = '>' + ''.join(field[0] for field in fields) - - def __init__(self, fd): - - buf_size = struct.calcsize(QcowHeader.fmt) - - fd.seek(0) - buf = fd.read(buf_size) - - header = struct.unpack(QcowHeader.fmt, buf) - self.__dict__ = dict((field[2], header[i]) - for i, field in enumerate(QcowHeader.fields)) - - self.set_defaults() - self.cluster_size = 1 << self.cluster_bits - - fd.seek(self.header_length) - self.load_extensions(fd) - - if self.backing_file_offset: - fd.seek(self.backing_file_offset) - self.backing_file = fd.read(self.backing_file_size) - else: - self.backing_file = None - - def set_defaults(self): - if self.version == 2: - self.incompatible_features = 0 - self.compatible_features = 0 - self.autoclear_features = 0 - self.refcount_order = 4 - self.header_length = 72 - - def load_extensions(self, fd): - self.extensions = [] - - if self.backing_file_offset != 0: - end = min(self.cluster_size, self.backing_file_offset) - else: - end = self.cluster_size - - while fd.tell() < end: - (magic, length) = struct.unpack('>II', fd.read(8)) - if magic == 0: - break - else: - padded = (length + 7) & ~7 - data = fd.read(padded) - self.extensions.append(QcowHeaderExtension(magic, length, data)) - - def update_extensions(self, fd): - - fd.seek(self.header_length) - extensions = self.extensions - extensions.append(QcowHeaderExtension(0, 0, b"")) - for ex in extensions: - buf = struct.pack('>II', ex.magic, ex.length) - fd.write(buf) - fd.write(ex.data) - - if self.backing_file != None: - self.backing_file_offset = fd.tell() - fd.write(self.backing_file) - - if fd.tell() > self.cluster_size: - raise Exception("I think I just broke the image...") - - - def update(self, fd): - header_bytes = self.header_length - - self.update_extensions(fd) - - fd.seek(0) - header = tuple(self.__dict__[f] for t, p, f in QcowHeader.fields) - buf = struct.pack(QcowHeader.fmt, *header) - buf = buf[0:header_bytes-1] - fd.write(buf) - - def dump(self): - for f in QcowHeader.fields: - value = self.__dict__[f[2]] - if f[1] == 'mask': - bits = [] - for bit in range(64): - if value & (1 << bit): - bits.append(bit) - value_str = str(bits) - else: - value_str = f[1] % value - - print("%-25s" % f[2], value_str) - print("") - - def dump_extensions(self): - for ex in self.extensions: - data = ex.data[:ex.length] - if all(c in string.printable.encode('ascii') for c in data): - data = "'%s'" % data.decode('ascii') - else: - data = "<binary>" - - print("Header extension:") - print("%-25s %#x" % ("magic", ex.magic)) - print("%-25s %d" % ("length", ex.length)) - print("%-25s %s" % ("data", data)) - print("") +from qcow2_format import ( + QcowHeader, + QcowHeaderExtension +) def cmd_dump_header(fd): h = QcowHeader(fd) h.dump() + print() h.dump_extensions() + def cmd_dump_header_exts(fd): h = QcowHeader(fd) h.dump_extensions() + def cmd_set_header(fd, name, value): try: value = int(value, 0) - except: + except ValueError: print("'%s' is not a valid number" % value) sys.exit(1) fields = (field[2] for field in QcowHeader.fields) - if not name in fields: + if name not in fields: print("'%s' is not a known header field" % name) sys.exit(1) @@ -183,25 +52,29 @@ def cmd_set_header(fd, name, value): h.__dict__[name] = value h.update(fd) + def cmd_add_header_ext(fd, magic, data): try: magic = int(magic, 0) - except: + except ValueError: print("'%s' is not a valid magic number" % magic) sys.exit(1) h = QcowHeader(fd) - h.extensions.append(QcowHeaderExtension.create(magic, data.encode('ascii'))) + h.extensions.append(QcowHeaderExtension.create(magic, + data.encode('ascii'))) h.update(fd) + def cmd_add_header_ext_stdio(fd, magic): data = sys.stdin.read() cmd_add_header_ext(fd, magic, data) + def cmd_del_header_ext(fd, magic): try: magic = int(magic, 0) - except: + except ValueError: print("'%s' is not a valid magic number" % magic) sys.exit(1) @@ -219,12 +92,13 @@ def cmd_del_header_ext(fd, magic): h.update(fd) + def cmd_set_feature_bit(fd, group, bit): try: bit = int(bit, 0) if bit < 0 or bit >= 64: raise ValueError - except: + except ValueError: print("'%s' is not a valid bit number in range [0, 64)" % bit) sys.exit(1) @@ -236,21 +110,27 @@ def cmd_set_feature_bit(fd, group, bit): elif group == 'autoclear': h.autoclear_features |= 1 << bit else: - print("'%s' is not a valid group, try 'incompatible', 'compatible', or 'autoclear'" % group) + print("'%s' is not a valid group, try " + "'incompatible', 'compatible', or 'autoclear'" % group) sys.exit(1) h.update(fd) + cmds = [ - [ 'dump-header', cmd_dump_header, 0, 'Dump image header and header extensions' ], - [ 'dump-header-exts', cmd_dump_header_exts, 0, 'Dump image header extensions' ], - [ 'set-header', cmd_set_header, 2, 'Set a field in the header'], - [ 'add-header-ext', cmd_add_header_ext, 2, 'Add a header extension' ], - [ 'add-header-ext-stdio', cmd_add_header_ext_stdio, 1, 'Add a header extension, data from stdin' ], - [ 'del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension' ], - [ 'set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'], + ['dump-header', cmd_dump_header, 0, + 'Dump image header and header extensions'], + ['dump-header-exts', cmd_dump_header_exts, 0, + 'Dump image header extensions'], + ['set-header', cmd_set_header, 2, 'Set a field in the header'], + ['add-header-ext', cmd_add_header_ext, 2, 'Add a header extension'], + ['add-header-ext-stdio', cmd_add_header_ext_stdio, 1, + 'Add a header extension, data from stdin'], + ['del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension'], + ['set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'], ] + def main(filename, cmd, args): fd = open(filename, "r+b") try: @@ -267,6 +147,7 @@ def main(filename, cmd, args): finally: fd.close() + def usage(): print("Usage: %s <file> <cmd> [<arg>, ...]" % sys.argv[0]) print("") @@ -274,6 +155,7 @@ def usage(): for name, handler, num_args, desc in cmds: print(" %-20s - %s" % (name, desc)) + if __name__ == '__main__': if len(sys.argv) < 3: usage() diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py new file mode 100644 index 0000000000..0f65fd161d --- /dev/null +++ b/tests/qemu-iotests/qcow2_format.py @@ -0,0 +1,286 @@ +# Library for manipulations with qcow2 image +# +# Copyright (c) 2020 Virtuozzo International GmbH. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import struct +import string + + +class Qcow2Field: + + def __init__(self, value): + self.value = value + + def __str__(self): + return str(self.value) + + +class Flags64(Qcow2Field): + + def __str__(self): + bits = [] + for bit in range(64): + if self.value & (1 << bit): + bits.append(bit) + return str(bits) + + +class Enum(Qcow2Field): + + def __str__(self): + return f'{self.value:#x} ({self.mapping.get(self.value, "<unknown>")})' + + +class Qcow2StructMeta(type): + + # Mapping from c types to python struct format + ctypes = { + 'u8': 'B', + 'u16': 'H', + 'u32': 'I', + 'u64': 'Q' + } + + def __init__(self, name, bases, attrs): + if 'fields' in attrs: + self.fmt = '>' + ''.join(self.ctypes[f[0]] for f in self.fields) + + +class Qcow2Struct(metaclass=Qcow2StructMeta): + + """Qcow2Struct: base class for qcow2 data structures + + Successors should define fields class variable, which is: list of tuples, + each of three elements: + - c-type (one of 'u8', 'u16', 'u32', 'u64') + - format (format_spec to use with .format() when dump or 'mask' to dump + bitmasks) + - field name + """ + + def __init__(self, fd=None, offset=None, data=None): + """ + Two variants: + 1. Specify data. fd and offset must be None. + 2. Specify fd and offset, data must be None. offset may be omitted + in this case, than current position of fd is used. + """ + if data is None: + assert fd is not None + buf_size = struct.calcsize(self.fmt) + if offset is not None: + fd.seek(offset) + data = fd.read(buf_size) + else: + assert fd is None and offset is None + + values = struct.unpack(self.fmt, data) + self.__dict__ = dict((field[2], values[i]) + for i, field in enumerate(self.fields)) + + def dump(self): + for f in self.fields: + value = self.__dict__[f[2]] + if isinstance(f[1], str): + value_str = f[1].format(value) + else: + value_str = str(f[1](value)) + + print('{:<25} {}'.format(f[2], value_str)) + + +class Qcow2BitmapExt(Qcow2Struct): + + fields = ( + ('u32', '{}', 'nb_bitmaps'), + ('u32', '{}', 'reserved32'), + ('u64', '{:#x}', 'bitmap_directory_size'), + ('u64', '{:#x}', 'bitmap_directory_offset') + ) + + +QCOW2_EXT_MAGIC_BITMAPS = 0x23852875 + + +class QcowHeaderExtension(Qcow2Struct): + + class Magic(Enum): + mapping = { + 0xe2792aca: 'Backing format', + 0x6803f857: 'Feature table', + 0x0537be77: 'Crypto header', + QCOW2_EXT_MAGIC_BITMAPS: 'Bitmaps', + 0x44415441: 'Data file' + } + + fields = ( + ('u32', Magic, 'magic'), + ('u32', '{}', 'length') + # length bytes of data follows + # then padding to next multiply of 8 + ) + + def __init__(self, magic=None, length=None, data=None, fd=None): + """ + Support both loading from fd and creation from user data. + For fd-based creation current position in a file will be used to read + the data. + + This should be somehow refactored and functionality should be moved to + superclass (to allow creation of any qcow2 struct), but then, fields + of variable length (data here) should be supported in base class + somehow. Note also, that we probably want to parse different + extensions. Should they be subclasses of this class, or how to do it + better? Should it be something like QAPI union with discriminator field + (magic here). So, it's a TODO. We'll see how to properly refactor this + when we have more qcow2 structures. + """ + if fd is None: + assert all(v is not None for v in (magic, length, data)) + self.magic = magic + self.length = length + if length % 8 != 0: + padding = 8 - (length % 8) + data += b'\0' * padding + self.data = data + else: + assert all(v is None for v in (magic, length, data)) + super().__init__(fd=fd) + padded = (self.length + 7) & ~7 + self.data = fd.read(padded) + assert self.data is not None + + if self.magic == QCOW2_EXT_MAGIC_BITMAPS: + self.obj = Qcow2BitmapExt(data=self.data) + else: + self.obj = None + + def dump(self): + super().dump() + + if self.obj is None: + data = self.data[:self.length] + if all(c in string.printable.encode('ascii') for c in data): + data = f"'{ data.decode('ascii') }'" + else: + data = '<binary>' + print(f'{"data":<25} {data}') + else: + self.obj.dump() + + @classmethod + def create(cls, magic, data): + return QcowHeaderExtension(magic, len(data), data) + + +class QcowHeader(Qcow2Struct): + + fields = ( + # Version 2 header fields + ('u32', '{:#x}', 'magic'), + ('u32', '{}', 'version'), + ('u64', '{:#x}', 'backing_file_offset'), + ('u32', '{:#x}', 'backing_file_size'), + ('u32', '{}', 'cluster_bits'), + ('u64', '{}', 'size'), + ('u32', '{}', 'crypt_method'), + ('u32', '{}', 'l1_size'), + ('u64', '{:#x}', 'l1_table_offset'), + ('u64', '{:#x}', 'refcount_table_offset'), + ('u32', '{}', 'refcount_table_clusters'), + ('u32', '{}', 'nb_snapshots'), + ('u64', '{:#x}', 'snapshot_offset'), + + # Version 3 header fields + ('u64', Flags64, 'incompatible_features'), + ('u64', Flags64, 'compatible_features'), + ('u64', Flags64, 'autoclear_features'), + ('u32', '{}', 'refcount_order'), + ('u32', '{}', 'header_length'), + ) + + def __init__(self, fd): + super().__init__(fd=fd, offset=0) + + self.set_defaults() + self.cluster_size = 1 << self.cluster_bits + + fd.seek(self.header_length) + self.load_extensions(fd) + + if self.backing_file_offset: + fd.seek(self.backing_file_offset) + self.backing_file = fd.read(self.backing_file_size) + else: + self.backing_file = None + + def set_defaults(self): + if self.version == 2: + self.incompatible_features = 0 + self.compatible_features = 0 + self.autoclear_features = 0 + self.refcount_order = 4 + self.header_length = 72 + + def load_extensions(self, fd): + self.extensions = [] + + if self.backing_file_offset != 0: + end = min(self.cluster_size, self.backing_file_offset) + else: + end = self.cluster_size + + while fd.tell() < end: + ext = QcowHeaderExtension(fd=fd) + if ext.magic == 0: + break + else: + self.extensions.append(ext) + + def update_extensions(self, fd): + + fd.seek(self.header_length) + extensions = self.extensions + extensions.append(QcowHeaderExtension(0, 0, b'')) + for ex in extensions: + buf = struct.pack('>II', ex.magic, ex.length) + fd.write(buf) + fd.write(ex.data) + + if self.backing_file is not None: + self.backing_file_offset = fd.tell() + fd.write(self.backing_file) + + if fd.tell() > self.cluster_size: + raise Exception('I think I just broke the image...') + + def update(self, fd): + header_bytes = self.header_length + + self.update_extensions(fd) + + fd.seek(0) + header = tuple(self.__dict__[f] for t, p, f in QcowHeader.fields) + buf = struct.pack(QcowHeader.fmt, *header) + buf = buf[0:header_bytes-1] + fd.write(buf) + + def dump_extensions(self): + for ex in self.extensions: + print('Header extension:') + ex.dump() + print() diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 11c39c601e..3da09a38be 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -68,6 +68,8 @@ run-semiconsole-arm: semiconsole-arm run-plugin-semiconsole-arm-with-%: $(call skip-test, $<, "MANUAL ONLY") +ARM_TESTS += commpage + TESTS += $(ARM_TESTS) # On ARM Linux only supports 4k pages diff --git a/tests/tcg/arm/commpage.c b/tests/tcg/arm/commpage.c new file mode 100644 index 0000000000..c76e70cb8b --- /dev/null +++ b/tests/tcg/arm/commpage.c @@ -0,0 +1,61 @@ +/* + * Verify the COMMPAGE emulation + * + * The ARM commpage is a set of user space helper functions provided + * by the kernel in an effort to ease portability of user space code + * between different CPUs with potentially different capabilities. It + * is a 32 bit invention and similar to the vdso segment in many ways. + * + * The ABI is documented in the Linux kernel: + * Documentation/arm/kernel_userspace_helpers.rst + * + * Copyright (c) 2020 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +#define ARM_COMMPAGE (0xffff0f00u) +#define ARM_KUSER_VERSION (*(int32_t *)(ARM_COMMPAGE + 0xfc)) +typedef void * (get_tls_fn)(void); +#define ARM_KUSER_GET_TLS (*(get_tls_fn *)(ARM_COMMPAGE + 0xe0)) +typedef int (cmpxchg_fn)(int oldval, int newval, volatile int *ptr); +#define ARM_KUSER_CMPXCHG (*(cmpxchg_fn *)(ARM_COMMPAGE + 0xc0)) +typedef void (dmb_fn)(void); +#define ARM_KUSER_DMB (*(dmb_fn *)(ARM_COMMPAGE + 0xa0)) +typedef int (cmpxchg64_fn)(const int64_t *oldval, + const int64_t *newval, + volatile int64_t *ptr); +#define ARM_KUSER_CMPXCHG64 (*(cmpxchg64_fn *)(ARM_COMMPAGE + 0x60)) + +#define fail_unless(x) \ + do { \ + if (!(x)) { \ + fprintf(stderr, "FAILED at %s:%d\n", __FILE__, __LINE__); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + + +int main(int argc, char *argv[argc]) +{ + void *kuser_tls; + int val = 1; + const int64_t oldval = 1, newval = 2; + int64_t val64 = 1; + + fail_unless(ARM_KUSER_VERSION == 0x5); + kuser_tls = ARM_KUSER_GET_TLS(); + printf("TLS = %p\n", kuser_tls); + fail_unless(kuser_tls != 0); + fail_unless(ARM_KUSER_CMPXCHG(1, 2, &val) == 0); + printf("val = %d\n", val); + /* this is a crash test, not checking an actual barrier occurs */ + ARM_KUSER_DMB(); + fail_unless(ARM_KUSER_CMPXCHG64(&oldval, &newval, &val64) == 0); + printf("val64 = %lld\n", val64); + return 0; +} diff --git a/tests/vm/fedora b/tests/vm/fedora index bd9c6cf295..a9195670f4 100755 --- a/tests/vm/fedora +++ b/tests/vm/fedora @@ -32,7 +32,6 @@ class FedoraVM(basevm.BaseVM): pkgs = [ # tools 'git-core', - 'flex', 'bison', 'gcc', 'binutils', 'make', # perl diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 298967fe9c..f87db2b126 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -38,7 +38,6 @@ class FreeBSDVM(basevm.BaseVM): "bash", "gmake", "gsed", - "flex", "bison", # libs: crypto "gnutls", diff --git a/tests/vm/netbsd b/tests/vm/netbsd index b10c9d429d..cdac502dad 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -36,7 +36,6 @@ class NetBSDVM(basevm.BaseVM): "bash", "gmake", "gsed", - "flex", "bison", # libs: crypto "gnutls", diff --git a/tests/vm/openbsd b/tests/vm/openbsd index 0b705f4945..13e7f9a6d5 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -35,7 +35,6 @@ class OpenBSDVM(basevm.BaseVM): "bash", "gmake", "gsed", - "bison", # libs: usb "libusb1", diff --git a/tests/vm/ubuntu.i386 b/tests/vm/ubuntu.i386 index 1570775335..24527cc78c 100755 --- a/tests/vm/ubuntu.i386 +++ b/tests/vm/ubuntu.i386 @@ -52,7 +52,7 @@ class UbuntuX86VM(basevm.BaseVM): self.ssh_root_check("sed -ie s/^#\ deb-src/deb-src/g /etc/apt/sources.list") self.ssh_root_check("apt-get update") self.ssh_root_check("apt-get build-dep -y qemu") - self.ssh_root_check("apt-get install -y libfdt-dev flex bison language-pack-en") + self.ssh_root_check("apt-get install -y libfdt-dev language-pack-en") self.ssh_root("poweroff") self.wait() os.rename(img_tmp, img) |