summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/rng.c17
-rw-r--r--block/qcow2.c21
-rw-r--r--block/qed.c6
-rw-r--r--block/vhdx-log.c4
-rw-r--r--block/vhdx.c8
-rw-r--r--block/vmdk.c48
-rw-r--r--exec.c6
-rw-r--r--hw/arm/boot.c9
-rw-r--r--hw/arm/xilinx_zynq.c7
-rw-r--r--hw/block/dataplane/virtio-blk.c2
-rw-r--r--hw/cris/Makefile.objs1
-rw-r--r--hw/cris/axis_dev88.c7
-rw-r--r--hw/cris/pic_cpu.c47
-rw-r--r--hw/display/blizzard_template.h40
-rw-r--r--hw/display/pl110_template.h12
-rw-r--r--hw/display/pxa2xx_template.h22
-rw-r--r--hw/display/tc6393xb_template.h14
-rw-r--r--hw/intc/arm_gic.c21
-rw-r--r--hw/misc/vfio.c78
-rw-r--r--hw/s390x/Makefile.objs1
-rw-r--r--hw/s390x/event-facility.c6
-rw-r--r--hw/s390x/sclp.c53
-rw-r--r--hw/s390x/sclpcpu.c112
-rw-r--r--hw/virtio/virtio-rng.c15
-rw-r--r--include/exec/exec-all.h1
-rw-r--r--include/hw/cris/etraxfs.h2
-rw-r--r--include/hw/intc/arm_gic_common.h1
-rw-r--r--include/hw/s390x/event-facility.h5
-rw-r--r--include/hw/s390x/sclp.h75
-rw-r--r--include/qom/object_interfaces.h62
-rw-r--r--include/sysemu/rng.h11
-rw-r--r--kvm-all.c1
-rw-r--r--linux-user/signal.c8
-rw-r--r--monitor.c4
-rw-r--r--pc-bios/QEMU,tcx.binbin1242 -> 1410 bytes
-rw-r--r--pc-bios/README2
-rw-r--r--pc-bios/openbios-ppcbin729880 -> 729912 bytes
-rw-r--r--pc-bios/openbios-sparc32bin381488 -> 381512 bytes
-rw-r--r--pc-bios/openbios-sparc64bin1598328 -> 1598376 bytes
-rw-r--r--qemu-doc.texi23
-rw-r--r--qemu-img.texi23
-rw-r--r--qmp.c22
-rw-r--r--qom/Makefile.objs1
-rw-r--r--qom/object_interfaces.c32
m---------roms/openbios0
-rw-r--r--target-arm/cpu.c1
-rw-r--r--target-arm/cpu.h2
-rw-r--r--target-arm/helper-a64.c31
-rw-r--r--target-arm/helper-a64.h1
-rw-r--r--target-arm/helper.c45
-rw-r--r--target-arm/helper.h1
-rw-r--r--target-arm/translate-a64.c2699
-rw-r--r--target-arm/translate.c251
-rw-r--r--target-cris/cpu.c26
-rw-r--r--target-cris/cpu.h4
-rw-r--r--target-cris/helper.c5
-rw-r--r--target-s390x/cpu.h17
-rw-r--r--target-s390x/kvm.c136
-rwxr-xr-xtests/qemu-iotests/04028
-rwxr-xr-xtests/qemu-iotests/0712
-rw-r--r--translate-all.c14
-rw-r--r--vl.c22
62 files changed, 3783 insertions, 332 deletions
diff --git a/backends/rng.c b/backends/rng.c
index 85cb83f5e1..8b8d5a4973 100644
--- a/backends/rng.c
+++ b/backends/rng.c
@@ -12,6 +12,7 @@
#include "sysemu/rng.h"
#include "qapi/qmp/qerror.h"
+#include "qom/object_interfaces.h"
void rng_backend_request_entropy(RngBackend *s, size_t size,
EntropyReceiveFunc *receive_entropy,
@@ -40,9 +41,9 @@ static bool rng_backend_prop_get_opened(Object *obj, Error **errp)
return s->opened;
}
-void rng_backend_open(RngBackend *s, Error **errp)
+static void rng_backend_complete(UserCreatable *uc, Error **errp)
{
- object_property_set_bool(OBJECT(s), true, "opened", errp);
+ object_property_set_bool(OBJECT(uc), true, "opened", errp);
}
static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp)
@@ -76,13 +77,25 @@ static void rng_backend_init(Object *obj)
NULL);
}
+static void rng_backend_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+ ucc->complete = rng_backend_complete;
+}
+
static const TypeInfo rng_backend_info = {
.name = TYPE_RNG_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(RngBackend),
.instance_init = rng_backend_init,
.class_size = sizeof(RngBackendClass),
+ .class_init = rng_backend_class_init,
.abstract = true,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
};
static void register_types(void)
diff --git a/block/qcow2.c b/block/qcow2.c
index 2da62b8a90..99a1ad13e6 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1689,26 +1689,6 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options,
return ret;
}
-static int qcow2_make_empty(BlockDriverState *bs)
-{
-#if 0
- /* XXX: not correct */
- BDRVQcowState *s = bs->opaque;
- uint32_t l1_length = s->l1_size * sizeof(uint64_t);
- int ret;
-
- memset(s->l1_table, 0, l1_length);
- if (bdrv_pwrite(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0)
- return -1;
- ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length);
- if (ret < 0)
- return ret;
-
- l2_cache_reset(bs);
-#endif
- return 0;
-}
-
static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
{
@@ -2252,7 +2232,6 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_get_block_status = qcow2_co_get_block_status,
.bdrv_set_key = qcow2_set_key,
- .bdrv_make_empty = qcow2_make_empty,
.bdrv_co_readv = qcow2_co_readv,
.bdrv_co_writev = qcow2_co_writev,
diff --git a/block/qed.c b/block/qed.c
index 694e6e2ee0..b9ca7ac0da 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -731,11 +731,6 @@ static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
return cb.status;
}
-static int bdrv_qed_make_empty(BlockDriverState *bs)
-{
- return -ENOTSUP;
-}
-
static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
{
return acb->common.bs->opaque;
@@ -1617,7 +1612,6 @@ static BlockDriver bdrv_qed = {
.bdrv_create = bdrv_qed_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
- .bdrv_make_empty = bdrv_qed_make_empty,
.bdrv_aio_readv = bdrv_qed_aio_readv,
.bdrv_aio_writev = bdrv_qed_aio_writev,
.bdrv_co_write_zeroes = bdrv_qed_co_write_zeroes,
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
index 8c9ae0d8e7..02755b8ded 100644
--- a/block/vhdx-log.c
+++ b/block/vhdx-log.c
@@ -965,8 +965,8 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
cpu_to_le32s((uint32_t *)(buffer + 4));
/* now write to the log */
- vhdx_log_write_sectors(bs, &s->log, &sectors_written, buffer,
- desc_sectors + sectors);
+ ret = vhdx_log_write_sectors(bs, &s->log, &sectors_written, buffer,
+ desc_sectors + sectors);
if (ret < 0) {
goto exit;
}
diff --git a/block/vhdx.c b/block/vhdx.c
index 9ee0a612ff..55689cf641 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -374,7 +374,7 @@ static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s,
inactive_header->log_guid = *log_guid;
}
- vhdx_write_header(bs->file, inactive_header, header_offset, true);
+ ret = vhdx_write_header(bs->file, inactive_header, header_offset, true);
if (ret < 0) {
goto exit;
}
@@ -1810,13 +1810,13 @@ static int vhdx_create(const char *filename, QEMUOptionParameter *options,
creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL,
&creator_items, NULL);
signature = cpu_to_le64(VHDX_FILE_SIGNATURE);
- bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
+ ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
if (ret < 0) {
goto delete_and_exit;
}
if (creator) {
- bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature), creator,
- creator_items * sizeof(gunichar2));
+ ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature),
+ creator, creator_items * sizeof(gunichar2));
if (ret < 0) {
goto delete_and_exit;
}
diff --git a/block/vmdk.c b/block/vmdk.c
index 99ca60fdb9..e809e2ef46 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1942,6 +1942,53 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
return info;
}
+static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
+ BdrvCheckMode fix)
+{
+ BDRVVmdkState *s = bs->opaque;
+ VmdkExtent *extent = NULL;
+ int64_t sector_num = 0;
+ int64_t total_sectors = bdrv_getlength(bs) / BDRV_SECTOR_SIZE;
+ int ret;
+ uint64_t cluster_offset;
+
+ if (fix) {
+ return -ENOTSUP;
+ }
+
+ for (;;) {
+ if (sector_num >= total_sectors) {
+ return 0;
+ }
+ extent = find_extent(s, sector_num, extent);
+ if (!extent) {
+ fprintf(stderr,
+ "ERROR: could not find extent for sector %" PRId64 "\n",
+ sector_num);
+ break;
+ }
+ ret = get_cluster_offset(bs, extent, NULL,
+ sector_num << BDRV_SECTOR_BITS,
+ 0, &cluster_offset);
+ if (ret == VMDK_ERROR) {
+ fprintf(stderr,
+ "ERROR: could not get cluster_offset for sector %"
+ PRId64 "\n", sector_num);
+ break;
+ }
+ if (ret == VMDK_OK && cluster_offset >= bdrv_getlength(extent->file)) {
+ fprintf(stderr,
+ "ERROR: cluster offset for sector %"
+ PRId64 " points after EOF\n", sector_num);
+ break;
+ }
+ sector_num += extent->cluster_sectors;
+ }
+
+ result->corruptions++;
+ return 0;
+}
+
static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
{
int i;
@@ -2015,6 +2062,7 @@ static BlockDriver bdrv_vmdk = {
.instance_size = sizeof(BDRVVmdkState),
.bdrv_probe = vmdk_probe,
.bdrv_open = vmdk_open,
+ .bdrv_check = vmdk_check,
.bdrv_reopen_prepare = vmdk_reopen_prepare,
.bdrv_read = vmdk_co_read,
.bdrv_write = vmdk_co_write,
diff --git a/exec.c b/exec.c
index 2435d9ecd9..9ad0a4b045 100644
--- a/exec.c
+++ b/exec.c
@@ -325,7 +325,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
hwaddr *plen, bool resolve_subpage)
{
MemoryRegionSection *section;
- Int128 diff;
+ Int128 diff, diff_page;
section = address_space_lookup_region(d, addr, resolve_subpage);
/* Compute offset within MemoryRegionSection */
@@ -334,7 +334,9 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
/* Compute offset within MemoryRegion */
*xlat = addr + section->offset_within_region;
+ diff_page = int128_make64(((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr);
diff = int128_sub(section->mr->size, int128_make64(addr));
+ diff = int128_min(diff, diff_page);
*plen = int128_get64(int128_min(diff, int128_make64(*plen)));
return section;
}
@@ -349,7 +351,7 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
hwaddr len = *plen;
for (;;) {
- section = address_space_translate_internal(as->dispatch, addr, &addr, plen, true);
+ section = address_space_translate_internal(as->dispatch, addr, &addr, &len, true);
mr = section->mr;
if (!mr->iommu_ops) {
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 1c1b0e5258..4036262f50 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -173,6 +173,11 @@ static void default_reset_secondary(ARMCPU *cpu,
env->regs[15] = info->smp_loader_start;
}
+static inline bool have_dtb(const struct arm_boot_info *info)
+{
+ return info->dtb_filename || info->get_dtb;
+}
+
#define WRITE_WORD(p, value) do { \
stl_phys_notdirty(p, value); \
p += 4; \
@@ -421,7 +426,7 @@ static void do_cpu_reset(void *opaque)
env->regs[15] = info->loader_start;
}
- if (!info->dtb_filename) {
+ if (!have_dtb(info)) {
if (old_param) {
set_kernel_args_old(info);
} else {
@@ -542,7 +547,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
/* for device tree boot, we pass the DTB directly in r2. Otherwise
* we point to the kernel args.
*/
- if (info->dtb_filename || info->get_dtb) {
+ if (have_dtb(info)) {
/* Place the DTB after the initrd in memory. Note that some
* kernels will trash anything in the 4K page the initrd
* ends in, so make sure the DTB isn't caught up in that.
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 98e0958a77..9ee21e726a 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -37,6 +37,7 @@
#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */
#define MPCORE_PERIPHBASE 0xF8F00000
+#define ZYNQ_BOARD_MIDR 0x413FC090
static const int dma_irqs[8] = {
46, 47, 48, 49, 72, 73, 74, 75
@@ -125,6 +126,12 @@ static void zynq_init(QEMUMachineInitArgs *args)
cpu = ARM_CPU(object_new(object_class_get_name(cpu_oc)));
+ object_property_set_int(OBJECT(cpu), ZYNQ_BOARD_MIDR, "midr", &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
+ exit(1);
+ }
+
object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err);
if (err) {
error_report("%s", error_get_pretty(err));
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 456d437ac3..2237edb4eb 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -145,7 +145,7 @@ static void do_get_id_cmd(VirtIOBlockDataPlane *s,
{
char id[VIRTIO_BLK_ID_BYTES];
- /* Serial number not NUL-terminated when shorter than buffer */
+ /* Serial number not NUL-terminated when longer than buffer */
strncpy(id, s->blk->serial ? s->blk->serial : "", sizeof(id));
iov_from_buf(iov, iov_cnt, 0, id, sizeof(id));
complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_OK);
diff --git a/hw/cris/Makefile.objs b/hw/cris/Makefile.objs
index 776db7c5cd..7624173f77 100644
--- a/hw/cris/Makefile.objs
+++ b/hw/cris/Makefile.objs
@@ -1,3 +1,2 @@
-obj-y += pic_cpu.o
obj-y += boot.o
obj-y += axis_dev88.o
diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c
index 55240886f5..645e45ccdf 100644
--- a/hw/cris/axis_dev88.c
+++ b/hw/cris/axis_dev88.c
@@ -254,7 +254,7 @@ void axisdev88_init(QEMUMachineInitArgs *args)
DeviceState *dev;
SysBusDevice *s;
DriveInfo *nand;
- qemu_irq irq[30], nmi[2], *cpu_irq;
+ qemu_irq irq[30], nmi[2];
void *etraxfs_dmac;
struct etraxfs_dma_client *dma_eth;
int i;
@@ -296,15 +296,14 @@ void axisdev88_init(QEMUMachineInitArgs *args)
&gpio_state.iomem);
- cpu_irq = cris_pic_init_cpu(env);
dev = qdev_create(NULL, "etraxfs,pic");
/* FIXME: Is there a proper way to signal vectors to the CPU core? */
qdev_prop_set_ptr(dev, "interrupt_vector", &env->interrupt_vector);
qdev_init_nofail(dev);
s = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(s, 0, 0x3001c000);
- sysbus_connect_irq(s, 0, cpu_irq[0]);
- sysbus_connect_irq(s, 1, cpu_irq[1]);
+ sysbus_connect_irq(s, 0, qdev_get_gpio_in(DEVICE(cpu), CRIS_CPU_IRQ));
+ sysbus_connect_irq(s, 1, qdev_get_gpio_in(DEVICE(cpu), CRIS_CPU_NMI));
for (i = 0; i < 30; i++) {
irq[i] = qdev_get_gpio_in(dev, i);
}
diff --git a/hw/cris/pic_cpu.c b/hw/cris/pic_cpu.c
deleted file mode 100644
index bd47bf1a5d..0000000000
--- a/hw/cris/pic_cpu.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * QEMU CRIS CPU interrupt wrapper logic.
- *
- * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "hw/hw.h"
-#include "hw/cris/etraxfs.h"
-
-#define D(x)
-
-static void cris_pic_cpu_handler(void *opaque, int irq, int level)
-{
- CRISCPU *cpu = opaque;
- CPUState *cs = CPU(cpu);
- int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
-
- if (level) {
- cpu_interrupt(cs, type);
- } else {
- cpu_reset_interrupt(cs, type);
- }
-}
-
-qemu_irq *cris_pic_init_cpu(CPUCRISState *env)
-{
- return qemu_allocate_irqs(cris_pic_cpu_handler, cris_env_get_cpu(env), 2);
-}
diff --git a/hw/display/blizzard_template.h b/hw/display/blizzard_template.h
index a8a8899478..b7ef27c808 100644
--- a/hw/display/blizzard_template.h
+++ b/hw/display/blizzard_template.h
@@ -18,25 +18,35 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#define SKIP_PIXEL(to) to += deststep
+#define SKIP_PIXEL(to) (to += deststep)
#if DEPTH == 8
-# define PIXEL_TYPE uint8_t
-# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
-# define COPY_PIXEL1(to, from) *to ++ = from
+# define PIXEL_TYPE uint8_t
+# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0)
+# define COPY_PIXEL1(to, from) (*to++ = from)
#elif DEPTH == 15 || DEPTH == 16
-# define PIXEL_TYPE uint16_t
-# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
-# define COPY_PIXEL1(to, from) *to ++ = from
+# define PIXEL_TYPE uint16_t
+# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0)
+# define COPY_PIXEL1(to, from) (*to++ = from)
#elif DEPTH == 24
-# define PIXEL_TYPE uint8_t
-# define COPY_PIXEL(to, from) \
- to[0] = from; to[1] = (from) >> 8; to[2] = (from) >> 16; SKIP_PIXEL(to)
-# define COPY_PIXEL1(to, from) \
- *to ++ = from; *to ++ = (from) >> 8; *to ++ = (from) >> 16
+# define PIXEL_TYPE uint8_t
+# define COPY_PIXEL(to, from) \
+ do { \
+ to[0] = from; \
+ to[1] = (from) >> 8; \
+ to[2] = (from) >> 16; \
+ SKIP_PIXEL(to); \
+ } while (0)
+
+# define COPY_PIXEL1(to, from) \
+ do { \
+ *to++ = from; \
+ *to++ = (from) >> 8; \
+ *to++ = (from) >> 16; \
+ } while (0)
#elif DEPTH == 32
-# define PIXEL_TYPE uint32_t
-# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
-# define COPY_PIXEL1(to, from) *to ++ = from
+# define PIXEL_TYPE uint32_t
+# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0)
+# define COPY_PIXEL1(to, from) (*to++ = from)
#else
# error unknown bit depth
#endif
diff --git a/hw/display/pl110_template.h b/hw/display/pl110_template.h
index e738e4a241..36ba791c6f 100644
--- a/hw/display/pl110_template.h
+++ b/hw/display/pl110_template.h
@@ -14,12 +14,16 @@
#if BITS == 8
#define COPY_PIXEL(to, from) *(to++) = from
#elif BITS == 15 || BITS == 16
-#define COPY_PIXEL(to, from) *(uint16_t *)to = from; to += 2;
+#define COPY_PIXEL(to, from) do { *(uint16_t *)to = from; to += 2; } while (0)
#elif BITS == 24
-#define COPY_PIXEL(to, from) \
- *(to++) = from; *(to++) = (from) >> 8; *(to++) = (from) >> 16
+#define COPY_PIXEL(to, from) \
+ do { \
+ *(to++) = from; \
+ *(to++) = (from) >> 8; \
+ *(to++) = (from) >> 16; \
+ } while (0)
#elif BITS == 32
-#define COPY_PIXEL(to, from) *(uint32_t *)to = from; to += 4;
+#define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0)
#else
#error unknown bit depth
#endif
diff --git a/hw/display/pxa2xx_template.h b/hw/display/pxa2xx_template.h
index 1cbe36cb80..c64eebc4b6 100644
--- a/hw/display/pxa2xx_template.h
+++ b/hw/display/pxa2xx_template.h
@@ -11,14 +11,26 @@
# define SKIP_PIXEL(to) to += deststep
#if BITS == 8
-# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0)
#elif BITS == 15 || BITS == 16
-# define COPY_PIXEL(to, from) *(uint16_t *) to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL(to, from) \
+ do { \
+ *(uint16_t *) to = from; \
+ SKIP_PIXEL(to); \
+ } while (0)
#elif BITS == 24
-# define COPY_PIXEL(to, from) \
- *(uint16_t *) to = from; *(to + 2) = (from) >> 16; SKIP_PIXEL(to)
+# define COPY_PIXEL(to, from) \
+ do { \
+ *(uint16_t *) to = from; \
+ *(to + 2) = (from) >> 16; \
+ SKIP_PIXEL(to); \
+ } while (0)
#elif BITS == 32
-# define COPY_PIXEL(to, from) *(uint32_t *) to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL(to, from) \
+ do { \
+ *(uint32_t *) to = from; \
+ SKIP_PIXEL(to); \
+ } while (0)
#else
# error unknown bit depth
#endif
diff --git a/hw/display/tc6393xb_template.h b/hw/display/tc6393xb_template.h
index 154aafd400..78629c07f9 100644
--- a/hw/display/tc6393xb_template.h
+++ b/hw/display/tc6393xb_template.h
@@ -22,14 +22,18 @@
*/
#if BITS == 8
-# define SET_PIXEL(addr, color) *(uint8_t*)addr = color;
+# define SET_PIXEL(addr, color) (*(uint8_t *)addr = color)
#elif BITS == 15 || BITS == 16
-# define SET_PIXEL(addr, color) *(uint16_t*)addr = color;
+# define SET_PIXEL(addr, color) (*(uint16_t *)addr = color)
#elif BITS == 24
-# define SET_PIXEL(addr, color) \
- addr[0] = color; addr[1] = (color) >> 8; addr[2] = (color) >> 16;
+# define SET_PIXEL(addr, color) \
+ do { \
+ addr[0] = color; \
+ addr[1] = (color) >> 8; \
+ addr[2] = (color) >> 16; \
+ } while (0)
#elif BITS == 32
-# define SET_PIXEL(addr, color) *(uint32_t*)addr = color;
+# define SET_PIXEL(addr, color) (*(uint32_t *)addr = color)
#else
# error unknown bit depth
#endif
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 9409684ce8..1c4a1143af 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -380,8 +380,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- if (irq < 16)
- value = 0xff;
+ if (irq < GIC_NR_SGIS) {
+ value = 0xff;
+ }
+
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
int mask =
@@ -406,8 +408,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- if (irq < 16)
- value = 0;
+ if (irq < GIC_NR_SGIS) {
+ value = 0;
+ }
+
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
@@ -423,8 +427,9 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- if (irq < 16)
- irq = 0;
+ if (irq < GIC_NR_SGIS) {
+ value = 0;
+ }
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
@@ -436,6 +441,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
+ if (irq < GIC_NR_SGIS) {
+ value = 0;
+ }
+
for (i = 0; i < 8; i++) {
/* ??? This currently clears the pending bit for all CPUs, even
for per-CPU interrupts. It's unclear whether this is the
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
index 9aecaa82bc..8db182fa3d 100644
--- a/hw/misc/vfio.c
+++ b/hw/misc/vfio.c
@@ -135,12 +135,18 @@ enum {
struct VFIOGroup;
+typedef struct VFIOType1 {
+ MemoryListener listener;
+ int error;
+ bool initialized;
+} VFIOType1;
+
typedef struct VFIOContainer {
int fd; /* /dev/vfio/vfio, empowered by the attached groups */
struct {
/* enable abstraction to support various iommu backends */
union {
- MemoryListener listener; /* Used by type1 iommu */
+ VFIOType1 type1;
};
void (*release)(struct VFIOContainer *);
} iommu_data;
@@ -191,6 +197,7 @@ typedef struct VFIODevice {
bool has_flr;
bool has_pm_reset;
bool needs_reset;
+ bool rom_read_failed;
} VFIODevice;
typedef struct VFIOGroup {
@@ -592,7 +599,7 @@ static void vfio_msi_interrupt(void *opaque)
return;
}
-#ifdef VFIO_DEBUG
+#ifdef DEBUG_VFIO
MSIMessage msg;
if (vdev->interrupt == VFIO_INT_MSIX) {
@@ -1125,6 +1132,14 @@ static void vfio_pci_load_rom(VFIODevice *vdev)
vdev->rom_offset = reg_info.offset;
if (!vdev->rom_size) {
+ vdev->rom_read_failed = true;
+ error_report("vfio-pci: Cannot read device rom at "
+ "%04x:%02x:%02x.%x\n",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
+ error_printf("Device option ROM contents are probably invalid "
+ "(check dmesg).\nSkip option ROM probe with rombar=0, "
+ "or load from file with romfile=\n");
return;
}
@@ -1156,6 +1171,9 @@ static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size)
/* Load the ROM lazily when the guest tries to read it */
if (unlikely(!vdev->rom)) {
vfio_pci_load_rom(vdev);
+ if (unlikely(!vdev->rom && !vdev->rom_read_failed)) {
+ vfio_pci_load_rom(vdev);
+ }
}
memcpy(&val, vdev->rom + addr,
@@ -1223,6 +1241,7 @@ static void vfio_pci_size_rom(VFIODevice *vdev)
PCI_BASE_ADDRESS_SPACE_MEMORY, &vdev->pdev.rom);
vdev->pdev.has_rom = true;
+ vdev->rom_read_failed = false;
}
static void vfio_vga_write(void *opaque, hwaddr addr,
@@ -1968,6 +1987,7 @@ static void vfio_vga_quirk_teardown(VFIODevice *vdev)
while (!QLIST_EMPTY(&vdev->vga.region[i].quirks)) {
VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga.region[i].quirks);
memory_region_del_subregion(&vdev->vga.region[i].mem, &quirk->mem);
+ memory_region_destroy(&quirk->mem);
QLIST_REMOVE(quirk, next);
g_free(quirk);
}
@@ -1990,6 +2010,7 @@ static void vfio_bar_quirk_teardown(VFIODevice *vdev, int nr)
while (!QLIST_EMPTY(&bar->quirks)) {
VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks);
memory_region_del_subregion(&bar->mem, &quirk->mem);
+ memory_region_destroy(&quirk->mem);
QLIST_REMOVE(quirk, next);
g_free(quirk);
}
@@ -2141,14 +2162,21 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova,
static bool vfio_listener_skipped_section(MemoryRegionSection *section)
{
- return !memory_region_is_ram(section->mr);
+ return !memory_region_is_ram(section->mr) ||
+ /*
+ * Sizing an enabled 64-bit BAR can cause spurious mappings to
+ * addresses in the upper part of the 64-bit address space. These
+ * are never accessed by the CPU and beyond the address width of
+ * some IOMMU hardware. TODO: VFIO should tell us the IOMMU width.
+ */
+ section->offset_within_address_space & (1ULL << 63);
}
static void vfio_listener_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
VFIOContainer *container = container_of(listener, VFIOContainer,
- iommu_data.listener);
+ iommu_data.type1.listener);
hwaddr iova, end;
void *vaddr;
int ret;
@@ -2190,6 +2218,19 @@ static void vfio_listener_region_add(MemoryListener *listener,
error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
"0x%"HWADDR_PRIx", %p) = %d (%m)",
container, iova, end - iova, vaddr, ret);
+
+ /*
+ * On the initfn path, store the first error in the container so we
+ * can gracefully fail. Runtime, there's not much we can do other
+ * than throw a hardware error.
+ */
+ if (!container->iommu_data.type1.initialized) {
+ if (!container->iommu_data.type1.error) {
+ container->iommu_data.type1.error = ret;
+ }
+ } else {
+ hw_error("vfio: DMA mapping failed, unable to continue\n");
+ }
}
}
@@ -2197,7 +2238,7 @@ static void vfio_listener_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
VFIOContainer *container = container_of(listener, VFIOContainer,
- iommu_data.listener);
+ iommu_data.type1.listener);
hwaddr iova, end;
int ret;
@@ -2242,7 +2283,7 @@ static MemoryListener vfio_memory_listener = {
static void vfio_listener_release(VFIOContainer *container)
{
- memory_listener_unregister(&container->iommu_data.listener);
+ memory_listener_unregister(&container->iommu_data.type1.listener);
}
/*
@@ -2412,10 +2453,12 @@ static void vfio_unmap_bar(VFIODevice *vdev, int nr)
memory_region_del_subregion(&bar->mem, &bar->mmap_mem);
munmap(bar->mmap, memory_region_size(&bar->mmap_mem));
+ memory_region_destroy(&bar->mmap_mem);
if (vdev->msix && vdev->msix->table_bar == nr) {
memory_region_del_subregion(&bar->mem, &vdev->msix->mmap_mem);
munmap(vdev->msix->mmap, memory_region_size(&vdev->msix->mmap_mem));
+ memory_region_destroy(&vdev->msix->mmap_mem);
}
memory_region_destroy(&bar->mem);
@@ -2501,7 +2544,7 @@ static void vfio_map_bar(VFIODevice *vdev, int nr)
* potentially insert a direct-mapped subregion before and after it.
*/
if (vdev->msix && vdev->msix->table_bar == nr) {
- size = vdev->msix->table_offset & TARGET_PAGE_MASK;
+ size = vdev->msix->table_offset & qemu_host_page_mask;
}
strncat(name, " mmap", sizeof(name) - strlen(name) - 1);
@@ -2513,8 +2556,8 @@ static void vfio_map_bar(VFIODevice *vdev, int nr)
if (vdev->msix && vdev->msix->table_bar == nr) {
unsigned start;
- start = TARGET_PAGE_ALIGN(vdev->msix->table_offset +
- (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE));
+ start = HOST_PAGE_ALIGN(vdev->msix->table_offset +
+ (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE));
size = start < bar->size ? bar->size - start : 0;
strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1);
@@ -3212,10 +3255,23 @@ static int vfio_connect_container(VFIOGroup *group)
return -errno;
}
- container->iommu_data.listener = vfio_memory_listener;
+ container->iommu_data.type1.listener = vfio_memory_listener;
container->iommu_data.release = vfio_listener_release;
- memory_listener_register(&container->iommu_data.listener, &address_space_memory);
+ memory_listener_register(&container->iommu_data.type1.listener,
+ &address_space_memory);
+
+ if (container->iommu_data.type1.error) {
+ ret = container->iommu_data.type1.error;
+ vfio_listener_release(container);
+ g_free(container);
+ close(fd);
+ error_report("vfio: memory listener initialization failed for container\n");
+ return ret;
+ }
+
+ container->iommu_data.type1.initialized = true;
+
} else {
error_report("vfio: No available IOMMU models");
g_free(container);
diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
index 77e1218447..1ba6c3ab70 100644
--- a/hw/s390x/Makefile.objs
+++ b/hw/s390x/Makefile.objs
@@ -3,6 +3,7 @@ obj-y += s390-virtio-hcall.o
obj-y += sclp.o
obj-y += event-facility.o
obj-y += sclpquiesce.o
+obj-y += sclpcpu.o
obj-y += ipl.o
obj-y += css.o
obj-y += s390-virtio-ccw.o
diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c
index 25951a020a..a73c0b924a 100644
--- a/hw/s390x/event-facility.c
+++ b/hw/s390x/event-facility.c
@@ -32,6 +32,8 @@ struct SCLPEventFacility {
unsigned int receive_mask;
};
+SCLPEvent cpu_hotplug;
+
/* return true if any child has event pending set */
static bool event_pending(SCLPEventFacility *ef)
{
@@ -335,6 +337,10 @@ static int init_event_facility(S390SCLPDevice *sdev)
}
qdev_init_nofail(quiesce);
+ object_initialize(&cpu_hotplug, sizeof(cpu_hotplug), TYPE_SCLP_CPU_HOTPLUG);
+ qdev_set_parent_bus(DEVICE(&cpu_hotplug), BUS(&event_facility->sbus));
+ object_property_set_bool(OBJECT(&cpu_hotplug), true, "realized", NULL);
+
return 0;
}
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index 86d6ae0023..4e0c564c5c 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -15,6 +15,7 @@
#include "cpu.h"
#include "sysemu/kvm.h"
#include "exec/memory.h"
+#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
@@ -31,7 +32,26 @@ static inline S390SCLPDevice *get_event_facility(void)
static void read_SCP_info(SCCB *sccb)
{
ReadInfo *read_info = (ReadInfo *) sccb;
+ CPUState *cpu;
int shift = 0;
+ int cpu_count = 0;
+ int i = 0;
+
+ CPU_FOREACH(cpu) {
+ cpu_count++;
+ }
+
+ /* CPU information */
+ read_info->entries_cpu = cpu_to_be16(cpu_count);
+ read_info->offset_cpu = cpu_to_be16(offsetof(ReadInfo, entries));
+ read_info->highest_cpu = cpu_to_be16(max_cpus);
+
+ for (i = 0; i < cpu_count; i++) {
+ read_info->entries[i].address = i;
+ read_info->entries[i].type = 0;
+ }
+
+ read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO);
while ((ram_size >> (20 + shift)) > 65535) {
shift++;
@@ -41,15 +61,46 @@ static void read_SCP_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
+/* Provide information about the CPU */
+static void sclp_read_cpu_info(SCCB *sccb)
+{
+ ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb;
+ CPUState *cpu;
+ int cpu_count = 0;
+ int i = 0;
+
+ CPU_FOREACH(cpu) {
+ cpu_count++;
+ }
+
+ cpu_info->nr_configured = cpu_to_be16(cpu_count);
+ cpu_info->offset_configured = cpu_to_be16(offsetof(ReadCpuInfo, entries));
+ cpu_info->nr_standby = cpu_to_be16(0);
+
+ /* The standby offset is 16-byte for each CPU */
+ cpu_info->offset_standby = cpu_to_be16(cpu_info->offset_configured
+ + cpu_info->nr_configured*sizeof(CPUEntry));
+
+ for (i = 0; i < cpu_count; i++) {
+ cpu_info->entries[i].address = i;
+ cpu_info->entries[i].type = 0;
+ }
+
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
+}
+
static void sclp_execute(SCCB *sccb, uint64_t code)
{
S390SCLPDevice *sdev = get_event_facility();
- switch (code) {
+ switch (code & SCLP_CMD_CODE_MASK) {
case SCLP_CMDW_READ_SCP_INFO:
case SCLP_CMDW_READ_SCP_INFO_FORCED:
read_SCP_info(sccb);
break;
+ case SCLP_CMDW_READ_CPU_INFO:
+ sclp_read_cpu_info(sccb);
+ break;
default:
sdev->sclp_command_handler(sdev->ef, sccb, code);
break;
diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c
new file mode 100644
index 0000000000..b9c238a0da
--- /dev/null
+++ b/hw/s390x/sclpcpu.c
@@ -0,0 +1,112 @@
+/*
+ * SCLP event type
+ * Signal CPU - Trigger SCLP interrupt for system CPU configure or
+ * de-configure
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ * Thang Pham <thang.pham@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at your
+ * option) any later version. See the COPYING file in the top-level directory.
+ *
+ */
+#include "sysemu/sysemu.h"
+#include "hw/s390x/sclp.h"
+#include "hw/s390x/event-facility.h"
+#include "cpu.h"
+#include "sysemu/cpus.h"
+#include "sysemu/kvm.h"
+
+typedef struct ConfigMgtData {
+ EventBufferHeader ebh;
+ uint8_t reserved;
+ uint8_t event_qualifier;
+} QEMU_PACKED ConfigMgtData;
+
+static qemu_irq irq_cpu_hotplug; /* Only used in this file */
+
+#define EVENT_QUAL_CPU_CHANGE 1
+
+void raise_irq_cpu_hotplug(void)
+{
+ qemu_irq_raise(irq_cpu_hotplug);
+}
+
+static unsigned int send_mask(void)
+{
+ return SCLP_EVENT_MASK_CONFIG_MGT_DATA;
+}
+
+static unsigned int receive_mask(void)
+{
+ return 0;
+}
+
+static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
+ int *slen)
+{
+ ConfigMgtData *cdata = (ConfigMgtData *) evt_buf_hdr;
+ if (*slen < sizeof(ConfigMgtData)) {
+ return 0;
+ }
+
+ /* Event is no longer pending */
+ if (!event->event_pending) {
+ return 0;
+ }
+ event->event_pending = false;
+
+ /* Event header data */
+ cdata->ebh.length = cpu_to_be16(sizeof(ConfigMgtData));
+ cdata->ebh.type = SCLP_EVENT_CONFIG_MGT_DATA;
+ cdata->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED;
+
+ /* Trigger a rescan of CPUs by setting event qualifier */
+ cdata->event_qualifier = EVENT_QUAL_CPU_CHANGE;
+ *slen -= sizeof(ConfigMgtData);
+
+ return 1;
+}
+
+static void trigger_signal(void *opaque, int n, int level)
+{
+ SCLPEvent *event = opaque;
+ event->event_pending = true;
+
+ /* Trigger SCLP read operation */
+ sclp_service_interrupt(0);
+}
+
+static int irq_cpu_hotplug_init(SCLPEvent *event)
+{
+ irq_cpu_hotplug = *qemu_allocate_irqs(trigger_signal, event, 1);
+ return 0;
+}
+
+static void cpu_class_init(ObjectClass *oc, void *data)
+{
+ SCLPEventClass *k = SCLP_EVENT_CLASS(oc);
+
+ k->init = irq_cpu_hotplug_init;
+ k->get_send_mask = send_mask;
+ k->get_receive_mask = receive_mask;
+ k->read_event_data = read_event_data;
+ k->write_event_data = NULL;
+}
+
+static const TypeInfo sclp_cpu_info = {
+ .name = "sclp-cpu-hotplug",
+ .parent = TYPE_SCLP_EVENT,
+ .instance_size = sizeof(SCLPEvent),
+ .class_init = cpu_class_init,
+ .class_size = sizeof(SCLPEventClass),
+};
+
+static void sclp_cpu_register_types(void)
+{
+ type_register_static(&sclp_cpu_info);
+}
+
+type_init(sclp_cpu_register_types)
diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c
index 755fdee628..a16e3bc52e 100644
--- a/hw/virtio/virtio-rng.c
+++ b/hw/virtio/virtio-rng.c
@@ -15,6 +15,7 @@
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-rng.h"
#include "sysemu/rng.h"
+#include "qom/object_interfaces.h"
static bool is_guest_ready(VirtIORNG *vrng)
{
@@ -148,6 +149,14 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp)
if (vrng->conf.rng == NULL) {
vrng->conf.default_backend = RNG_RANDOM(object_new(TYPE_RNG_RANDOM));
+ user_creatable_complete(OBJECT(vrng->conf.default_backend),
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ object_unref(OBJECT(vrng->conf.default_backend));
+ return;
+ }
+
object_property_add_child(OBJECT(dev),
"default-backend",
OBJECT(vrng->conf.default_backend),
@@ -166,12 +175,6 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp)
return;
}
- rng_backend_open(vrng->rng, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
vrng->vq = virtio_add_queue(vdev, 8, handle_input);
assert(vrng->conf.max_bytes <= INT64_MAX);
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index ea90b649d4..3b03cbfcf8 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -81,6 +81,7 @@ void cpu_gen_init(void);
int cpu_gen_code(CPUArchState *env, struct TranslationBlock *tb,
int *gen_code_size_ptr);
bool cpu_restore_state(CPUArchState *env, uintptr_t searched_pc);
+void page_size_init(void);
void QEMU_NORETURN cpu_resume_from_signal(CPUArchState *env1, void *puc);
void QEMU_NORETURN cpu_io_recompile(CPUArchState *env, uintptr_t retaddr);
diff --git a/include/hw/cris/etraxfs.h b/include/hw/cris/etraxfs.h
index ab30559c79..73a6134c1e 100644
--- a/include/hw/cris/etraxfs.h
+++ b/include/hw/cris/etraxfs.h
@@ -28,8 +28,6 @@
#include "net/net.h"
#include "hw/cris/etraxfs_dma.h"
-qemu_irq *cris_pic_init_cpu(CPUCRISState *env);
-
/* Instantiate an ETRAXFS Ethernet MAC. */
static inline DeviceState *
etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr,
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index 0d232dfb67..8a2aa00cee 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -27,6 +27,7 @@
#define GIC_MAXIRQ 1020
/* First 32 are private to each CPU (SGIs and PPIs). */
#define GIC_INTERNAL 32
+#define GIC_NR_SGIS 16
/* Maximum number of possible CPU interfaces, determined by GIC architecture */
#define GIC_NCPU 8
diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h
index 7ce7079f9f..870edd46f6 100644
--- a/include/hw/s390x/event-facility.h
+++ b/include/hw/s390x/event-facility.h
@@ -17,10 +17,12 @@
#include <hw/qdev.h>
#include "qemu/thread.h"
+#include "hw/s390x/sclp.h"
/* SCLP event types */
#define SCLP_EVENT_OPRTNS_COMMAND 0x01
#define SCLP_EVENT_MESSAGE 0x02
+#define SCLP_EVENT_CONFIG_MGT_DATA 0x04
#define SCLP_EVENT_PMSGCMD 0x09
#define SCLP_EVENT_ASCII_CONSOLE_DATA 0x1a
#define SCLP_EVENT_SIGNAL_QUIESCE 0x1d
@@ -28,6 +30,7 @@
/* SCLP event masks */
#define SCLP_EVENT_MASK_SIGNAL_QUIESCE 0x00000008
#define SCLP_EVENT_MASK_MSG_ASCII 0x00000040
+#define SCLP_EVENT_MASK_CONFIG_MGT_DATA 0x10000000
#define SCLP_EVENT_MASK_OP_CMD 0x80000000
#define SCLP_EVENT_MASK_MSG 0x40000000
#define SCLP_EVENT_MASK_PMSGCMD 0x00800000
@@ -43,6 +46,8 @@
#define SCLP_EVENT_GET_CLASS(obj) \
OBJECT_GET_CLASS(SCLPEventClass, (obj), TYPE_SCLP_EVENT)
+#define TYPE_SCLP_CPU_HOTPLUG "sclp-cpu-hotplug"
+
typedef struct WriteEventMask {
SCCBHeader h;
uint16_t _reserved;
diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h
index 231a38aa09..35112d92b1 100644
--- a/include/hw/s390x/sclp.h
+++ b/include/hw/s390x/sclp.h
@@ -17,21 +17,41 @@
#include <hw/sysbus.h>
#include <hw/qdev.h>
+#define SCLP_CMD_CODE_MASK 0xffff00ff
+
/* SCLP command codes */
#define SCLP_CMDW_READ_SCP_INFO 0x00020001
#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
+#define SCLP_READ_STORAGE_ELEMENT_INFO 0x00040001
+#define SCLP_ATTACH_STORAGE_ELEMENT 0x00080001
+#define SCLP_ASSIGN_STORAGE 0x000D0001
+#define SCLP_UNASSIGN_STORAGE 0x000C0001
#define SCLP_CMD_READ_EVENT_DATA 0x00770005
#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005
#define SCLP_CMD_READ_EVENT_DATA 0x00770005
#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005
#define SCLP_CMD_WRITE_EVENT_MASK 0x00780005
+/* SCLP Memory hotplug codes */
+#define SCLP_FC_ASSIGN_ATTACH_READ_STOR 0xE00000000000ULL
+#define SCLP_STARTING_SUBINCREMENT_ID 0x10001
+#define SCLP_INCREMENT_UNIT 0x10000
+#define MAX_AVAIL_SLOTS 32
+
+/* CPU hotplug SCLP codes */
+#define SCLP_HAS_CPU_INFO 0x0C00000000000000ULL
+#define SCLP_CMDW_READ_CPU_INFO 0x00010001
+#define SCLP_CMDW_CONFIGURE_CPU 0x00110001
+#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001
+
/* SCLP response codes */
#define SCLP_RC_NORMAL_READ_COMPLETION 0x0010
#define SCLP_RC_NORMAL_COMPLETION 0x0020
+#define SCLP_RC_SCCB_BOUNDARY_VIOLATION 0x0100
#define SCLP_RC_INVALID_SCLP_COMMAND 0x01f0
#define SCLP_RC_CONTAINED_EQUIPMENT_CHECK 0x0340
#define SCLP_RC_INSUFFICIENT_SCCB_LENGTH 0x0300
+#define SCLP_RC_STANDBY_READ_COMPLETION 0x0410
#define SCLP_RC_INVALID_FUNCTION 0x40f0
#define SCLP_RC_NO_EVENT_BUFFERS_STORED 0x60f0
#define SCLP_RC_INVALID_SELECTION_MASK 0x70f0
@@ -71,12 +91,66 @@ typedef struct SCCBHeader {
#define SCCB_DATA_LEN (SCCB_SIZE - sizeof(SCCBHeader))
+/* CPU information */
+typedef struct CPUEntry {
+ uint8_t address;
+ uint8_t reserved0[13];
+ uint8_t type;
+ uint8_t reserved1;
+} QEMU_PACKED CPUEntry;
+
typedef struct ReadInfo {
SCCBHeader h;
uint16_t rnmax;
uint8_t rnsize;
+ uint8_t _reserved1[16 - 11]; /* 11-15 */
+ uint16_t entries_cpu; /* 16-17 */
+ uint16_t offset_cpu; /* 18-19 */
+ uint8_t _reserved2[24 - 20]; /* 20-23 */
+ uint8_t loadparm[8]; /* 24-31 */
+ uint8_t _reserved3[48 - 32]; /* 32-47 */
+ uint64_t facilities; /* 48-55 */
+ uint8_t _reserved0[100 - 56];
+ uint32_t rnsize2;
+ uint64_t rnmax2;
+ uint8_t _reserved4[120-112]; /* 112-119 */
+ uint16_t highest_cpu;
+ uint8_t _reserved5[128 - 122]; /* 122-127 */
+ struct CPUEntry entries[0];
} QEMU_PACKED ReadInfo;
+typedef struct ReadCpuInfo {
+ SCCBHeader h;
+ uint16_t nr_configured; /* 8-9 */
+ uint16_t offset_configured; /* 10-11 */
+ uint16_t nr_standby; /* 12-13 */
+ uint16_t offset_standby; /* 14-15 */
+ uint8_t reserved0[24-16]; /* 16-23 */
+ struct CPUEntry entries[0];
+} QEMU_PACKED ReadCpuInfo;
+
+typedef struct ReadStorageElementInfo {
+ SCCBHeader h;
+ uint16_t max_id;
+ uint16_t assigned;
+ uint16_t standby;
+ uint8_t _reserved0[16 - 14]; /* 14-15 */
+ uint32_t entries[0];
+} QEMU_PACKED ReadStorageElementInfo;
+
+typedef struct AttachStorageElement {
+ SCCBHeader h;
+ uint8_t _reserved0[10 - 8]; /* 8-9 */
+ uint16_t assigned;
+ uint8_t _reserved1[16 - 12]; /* 12-15 */
+ uint32_t entries[0];
+} QEMU_PACKED AttachStorageElement;
+
+typedef struct AssignStorage {
+ SCCBHeader h;
+ uint16_t rn;
+} QEMU_PACKED AssignStorage;
+
typedef struct SCCB {
SCCBHeader h;
char data[SCCB_DATA_LEN];
@@ -114,5 +188,6 @@ typedef struct S390SCLPDeviceClass {
void s390_sclp_init(void);
void sclp_service_interrupt(uint32_t sccb);
+void raise_irq_cpu_hotplug(void);
#endif
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
new file mode 100644
index 0000000000..b7922833e1
--- /dev/null
+++ b/include/qom/object_interfaces.h
@@ -0,0 +1,62 @@
+#ifndef OBJECT_INTERFACES_H
+#define OBJECT_INTERFACES_H
+
+#include "qom/object.h"
+
+#define TYPE_USER_CREATABLE "user-creatable"
+
+#define USER_CREATABLE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(UserCreatableClass, (klass), \
+ TYPE_USER_CREATABLE)
+#define USER_CREATABLE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(UserCreatableClass, (obj), \
+ TYPE_USER_CREATABLE)
+#define USER_CREATABLE(obj) \
+ INTERFACE_CHECK(UserCreatable, (obj), \
+ TYPE_USER_CREATABLE)
+
+
+typedef struct UserCreatable {
+ /* <private> */
+ Object Parent;
+} UserCreatable;
+
+/**
+ * UserCreatableClass:
+ * @parent_class: the base class
+ * @complete: callback to be called after @obj's properties are set.
+ *
+ * Interface is designed to work with -object/object-add/object_add
+ * commands.
+ * Interface is mandatory for objects that are designed to be user
+ * creatable (i.e. -object/object-add/object_add, will accept only
+ * objects that inherit this interface).
+ *
+ * Interface also provides an optional ability to do the second
+ * stage * initialization of the object after its properties were
+ * set.
+ *
+ * For objects created without using -object/object-add/object_add,
+ * @user_creatable_complete() wrapper should be called manually if
+ * object's type implements USER_CREATABLE interface and needs
+ * complete() callback to be called.
+ */
+typedef struct UserCreatableClass {
+ /* <private> */
+ InterfaceClass parent_class;
+
+ /* <public> */
+ void (*complete)(UserCreatable *uc, Error **errp);
+} UserCreatableClass;
+
+/**
+ * user_creatable_complete:
+ * @obj: the object whose complete() method is called if defined
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Wrapper to call complete() method if one of types it's inherited
+ * from implements USER_CREATABLE interface, otherwise the call does
+ * nothing.
+ */
+void user_creatable_complete(Object *obj, Error **errp);
+#endif
diff --git a/include/sysemu/rng.h b/include/sysemu/rng.h
index 7637fac52d..0a27c9b88c 100644
--- a/include/sysemu/rng.h
+++ b/include/sysemu/rng.h
@@ -79,15 +79,4 @@ void rng_backend_request_entropy(RngBackend *s, size_t size,
* to stop tracking any request.
*/
void rng_backend_cancel_requests(RngBackend *s);
-
-/**
- * rng_backend_open:
- * @s: the backend to open
- * @errp: a pointer to return the #Error object if an error occurs.
- *
- * This function will open the backend if it is not already open. Calling this
- * function on an already opened backend will not result in an error.
- */
-void rng_backend_open(RngBackend *s, Error **errp);
-
#endif
diff --git a/kvm-all.c b/kvm-all.c
index a3fb8de268..9588feab99 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -1360,6 +1360,7 @@ int kvm_init(void)
* page size for the system though.
*/
assert(TARGET_PAGE_SIZE <= getpagesize());
+ page_size_init();
#ifdef KVM_CAP_SET_GUEST_DEBUG
QTAILQ_INIT(&s->kvm_sw_breakpoints);
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 01d7c393df..82e8592546 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -3659,7 +3659,7 @@ struct target_sigcontext {
struct target_signal_frame {
struct target_sigcontext sc;
uint32_t extramask[TARGET_NSIG_WORDS - 1];
- uint8_t retcode[8]; /* Trampoline code. */
+ uint16_t retcode[4]; /* Trampoline code. */
};
struct rt_signal_frame {
@@ -3667,7 +3667,7 @@ struct rt_signal_frame {
void *puc;
siginfo_t info;
struct ucontext uc;
- uint8_t retcode[8]; /* Trampoline code. */
+ uint16_t retcode[4]; /* Trampoline code. */
};
static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env)
@@ -3745,8 +3745,8 @@ static void setup_frame(int sig, struct target_sigaction *ka,
*/
err |= __put_user(0x9c5f, frame->retcode+0);
err |= __put_user(TARGET_NR_sigreturn,
- frame->retcode+2);
- err |= __put_user(0xe93d, frame->retcode+4);
+ frame->retcode + 1);
+ err |= __put_user(0xe93d, frame->retcode + 2);
/* Save the mask. */
err |= __put_user(set->sig[0], &frame->sc.oldmask);
diff --git a/monitor.c b/monitor.c
index 5ba541dc6f..b1ea262e8b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -288,8 +288,8 @@ void monitor_flush(Monitor *mon)
if (len && !mon->mux_out) {
rc = qemu_chr_fe_write(mon->chr, (const uint8_t *) buf, len);
- if (rc == len) {
- /* all flushed */
+ if ((rc < 0 && errno != EAGAIN) || (rc == len)) {
+ /* all flushed or error */
QDECREF(mon->outbuf);
mon->outbuf = qstring_new();
return;
diff --git a/pc-bios/QEMU,tcx.bin b/pc-bios/QEMU,tcx.bin
index a8ddd70ef3..eed108f3f1 100644
--- a/pc-bios/QEMU,tcx.bin
+++ b/pc-bios/QEMU,tcx.bin
Binary files differ
diff --git a/pc-bios/README b/pc-bios/README
index a110125a97..f1900686dc 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -12,7 +12,7 @@
1275-1994 (referred to as Open Firmware) compliant firmware.
The included images for PowerPC (for 32 and 64 bit PPC CPUs),
Sparc32 (including QEMU,tcx.bin) and Sparc64 are built from OpenBIOS SVN
- revision 1229.
+ revision 1246.
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc
index 550273a5ef..f4a3a396c4 100644
--- a/pc-bios/openbios-ppc
+++ b/pc-bios/openbios-ppc
Binary files differ
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
index 01105fc904..bb7cdfb4ec 100644
--- a/pc-bios/openbios-sparc32
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
index 62c9e77983..46b4fddd08 100644
--- a/pc-bios/openbios-sparc64
+++ b/pc-bios/openbios-sparc64
Binary files differ
diff --git a/qemu-doc.texi b/qemu-doc.texi
index ce61f30d6e..ad31f2d2d0 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -547,10 +547,27 @@ File name of a base image (see @option{create} subcommand)
@item backing_fmt
Image format of the base image
@item encryption
-If this option is set to @code{on}, the image is encrypted.
+If this option is set to @code{on}, the image is encrypted with 128-bit AES-CBC.
+
+The use of encryption in qcow and qcow2 images is considered to be flawed by
+modern cryptography standards, suffering from a number of design problems:
+
+@itemize @minus
+@item The AES-CBC cipher is used with predictable initialization vectors based
+on the sector number. This makes it vulnerable to chosen plaintext attacks
+which can reveal the existence of encrypted data.
+@item The user passphrase is directly used as the encryption key. A poorly
+chosen or short passphrase will compromise the security of the encryption.
+@item In the event of the passphrase being compromised there is no way to
+change the passphrase to protect data in any qcow images. The files must
+be cloned, using a different encryption passphrase in the new file. The
+original file must then be securely erased using a program like shred,
+though even this is ineffective with many modern storage technologies.
+@end itemize
-Encryption uses the AES format which is very secure (128 bit keys). Use
-a long password (16 characters) to get maximum protection.
+Use of qcow / qcow2 encryption is thus strongly discouraged. Users are
+recommended to use an alternative encryption technology such as the
+Linux dm-crypt / LUKS system.
@item cluster_size
Changes the qcow2 cluster size (must be between 512 and 2M). Smaller cluster
diff --git a/qemu-img.texi b/qemu-img.texi
index 526d56a458..f84590ebf0 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -409,10 +409,27 @@ File name of a base image (see @option{create} subcommand)
@item backing_fmt
Image format of the base image
@item encryption
-If this option is set to @code{on}, the image is encrypted.
+If this option is set to @code{on}, the image is encrypted with 128-bit AES-CBC.
-Encryption uses the AES format which is very secure (128 bit keys). Use
-a long password (16 characters) to get maximum protection.
+The use of encryption in qcow and qcow2 images is considered to be flawed by
+modern cryptography standards, suffering from a number of design problems:
+
+@itemize @minus
+@item The AES-CBC cipher is used with predictable initialization vectors based
+on the sector number. This makes it vulnerable to chosen plaintext attacks
+which can reveal the existence of encrypted data.
+@item The user passphrase is directly used as the encryption key. A poorly
+chosen or short passphrase will compromise the security of the encryption.
+@item In the event of the passphrase being compromised there is no way to
+change the passphrase to protect data in any qcow images. The files must
+be cloned, using a different encryption passphrase in the new file. The
+original file must then be securely erased using a program like shred,
+though even this is ineffective with many modern storage technologies.
+@end itemize
+
+Use of qcow / qcow2 encryption is thus strongly discouraged. Users are
+recommended to use an alternative encryption technology such as the
+Linux dm-crypt / LUKS system.
@item cluster_size
Changes the qcow2 cluster size (must be between 512 and 2M). Smaller cluster
diff --git a/qmp.c b/qmp.c
index 0f46171aad..d0d98e777b 100644
--- a/qmp.c
+++ b/qmp.c
@@ -27,6 +27,7 @@
#include "qapi/qmp/qobject.h"
#include "qapi/qmp-input-visitor.h"
#include "hw/boards.h"
+#include "qom/object_interfaces.h"
NameInfo *qmp_query_name(Error **errp)
{
@@ -549,15 +550,28 @@ void object_add(const char *type, const char *id, const QDict *qdict,
for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
object_property_set(obj, v, e->key, &local_err);
if (local_err) {
- error_propagate(errp, local_err);
- object_unref(obj);
- return;
+ goto out;
}
}
}
+ if (!object_dynamic_cast(obj, TYPE_USER_CREATABLE)) {
+ error_setg(&local_err, "object '%s' isn't supported by object-add",
+ id);
+ goto out;
+ }
+
+ user_creatable_complete(obj, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
object_property_add_child(container_get(object_get_root(), "/objects"),
- id, obj, errp);
+ id, obj, &local_err);
+out:
+ if (local_err) {
+ error_propagate(errp, local_err);
+ }
object_unref(obj);
}
diff --git a/qom/Makefile.objs b/qom/Makefile.objs
index 6a93ac7398..985003bd03 100644
--- a/qom/Makefile.objs
+++ b/qom/Makefile.objs
@@ -1,2 +1,3 @@
common-obj-y = object.o container.o qom-qobject.o
common-obj-y += cpu.o
+common-obj-y += object_interfaces.o
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
new file mode 100644
index 0000000000..6360818397
--- /dev/null
+++ b/qom/object_interfaces.c
@@ -0,0 +1,32 @@
+#include "qom/object_interfaces.h"
+#include "qemu/module.h"
+
+void user_creatable_complete(Object *obj, Error **errp)
+{
+
+ UserCreatableClass *ucc;
+ UserCreatable *uc =
+ (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE);
+
+ if (!uc) {
+ return;
+ }
+
+ ucc = USER_CREATABLE_GET_CLASS(uc);
+ if (ucc->complete) {
+ ucc->complete(uc, errp);
+ }
+}
+
+static void register_types(void)
+{
+ static const TypeInfo uc_interface_info = {
+ .name = TYPE_USER_CREATABLE,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(UserCreatableClass),
+ };
+
+ type_register_static(&uc_interface_info);
+}
+
+type_init(register_types)
diff --git a/roms/openbios b/roms/openbios
-Subproject d363cf50c50c268da7e6d0bf707adde1893d1ab
+Subproject 888126272f92294b0da45158393f1b862742cf6
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 52efd5d66f..45ad7f0260 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -982,6 +982,7 @@ static const ARMCPUInfo arm_cpus[] = {
static Property arm_cpu_properties[] = {
DEFINE_PROP_BOOL("start-powered-off", ARMCPU, start_powered_off, false),
+ DEFINE_PROP_UINT32("midr", ARMCPU, midr, 0),
DEFINE_PROP_END_OF_LIST()
};
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 198b6b8d4e..383c58221e 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -496,6 +496,8 @@ enum arm_fprounding {
FPROUNDING_ODD
};
+int arm_rmode_to_sf(int rmode);
+
enum arm_cpu_mode {
ARM_CPU_MODE_USR = 0x10,
ARM_CPU_MODE_FIQ = 0x11,
diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c
index 4ce0d01a85..6ca958afb1 100644
--- a/target-arm/helper-a64.c
+++ b/target-arm/helper-a64.c
@@ -122,3 +122,34 @@ uint64_t HELPER(vfp_cmped_a64)(float64 x, float64 y, void *fp_status)
{
return float_rel_to_flags(float64_compare(x, y, fp_status));
}
+
+uint64_t HELPER(simd_tbl)(CPUARMState *env, uint64_t result, uint64_t indices,
+ uint32_t rn, uint32_t numregs)
+{
+ /* Helper function for SIMD TBL and TBX. We have to do the table
+ * lookup part for the 64 bits worth of indices we're passed in.
+ * result is the initial results vector (either zeroes for TBL
+ * or some guest values for TBX), rn the register number where
+ * the table starts, and numregs the number of registers in the table.
+ * We return the results of the lookups.
+ */
+ int shift;
+
+ for (shift = 0; shift < 64; shift += 8) {
+ int index = extract64(indices, shift, 8);
+ if (index < 16 * numregs) {
+ /* Convert index (a byte offset into the virtual table
+ * which is a series of 128-bit vectors concatenated)
+ * into the correct vfp.regs[] element plus a bit offset
+ * into that element, bearing in mind that the table
+ * can wrap around from V31 to V0.
+ */
+ int elt = (rn * 2 + (index >> 3)) % 64;
+ int bitidx = (index & 7) * 8;
+ uint64_t val = extract64(env->vfp.regs[elt], bitidx, 8);
+
+ result = deposit64(result, shift, 8, val);
+ }
+ }
+ return result;
+}
diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h
index bca19f3dea..99832ee55e 100644
--- a/target-arm/helper-a64.h
+++ b/target-arm/helper-a64.h
@@ -26,3 +26,4 @@ DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr)
DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, ptr)
DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, ptr)
DEF_HELPER_3(vfp_cmped_a64, i64, f64, f64, ptr)
+DEF_HELPER_FLAGS_5(simd_tbl, TCG_CALL_NO_RWG_SE, i64, env, i64, i64, i32, i32)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index c708f15e27..ca5b0000ad 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -4048,6 +4048,23 @@ uint32_t HELPER(set_rmode)(uint32_t rmode, CPUARMState *env)
return prev_rmode;
}
+/* Set the current fp rounding mode in the standard fp status and return
+ * the old one. This is for NEON instructions that need to change the
+ * rounding mode but wish to use the standard FPSCR values for everything
+ * else. Always set the rounding mode back to the correct value after
+ * modifying it.
+ * The argument is a softfloat float_round_ value.
+ */
+uint32_t HELPER(set_neon_rmode)(uint32_t rmode, CPUARMState *env)
+{
+ float_status *fp_status = &env->vfp.standard_fp_status;
+
+ uint32_t prev_rmode = get_float_rounding_mode(fp_status);
+ set_float_rounding_mode(rmode, fp_status);
+
+ return prev_rmode;
+}
+
/* Half precision conversions. */
static float32 do_fcvt_f16_to_f32(uint32_t a, CPUARMState *env, float_status *s)
{
@@ -4418,3 +4435,31 @@ float64 HELPER(rintd)(float64 x, void *fp_status)
return ret;
}
+
+/* Convert ARM rounding mode to softfloat */
+int arm_rmode_to_sf(int rmode)
+{
+ switch (rmode) {
+ case FPROUNDING_TIEAWAY:
+ rmode = float_round_ties_away;
+ break;
+ case FPROUNDING_ODD:
+ /* FIXME: add support for TIEAWAY and ODD */
+ qemu_log_mask(LOG_UNIMP, "arm: unimplemented rounding mode: %d\n",
+ rmode);
+ case FPROUNDING_TIEEVEN:
+ default:
+ rmode = float_round_nearest_even;
+ break;
+ case FPROUNDING_POSINF:
+ rmode = float_round_up;
+ break;
+ case FPROUNDING_NEGINF:
+ rmode = float_round_down;
+ break;
+ case FPROUNDING_ZERO:
+ rmode = float_round_to_zero;
+ break;
+ }
+ return rmode;
+}
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 70872dffc6..71b8411120 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -149,6 +149,7 @@ DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr)
DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr)
DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, env)
+DEF_HELPER_FLAGS_2(set_neon_rmode, TCG_CALL_NO_RWG, i32, i32, env)
DEF_HELPER_2(vfp_fcvt_f16_to_f32, f32, i32, env)
DEF_HELPER_2(vfp_fcvt_f32_to_f16, i32, f32, env)
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index cf80c46b90..6c1ec1edc6 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -61,6 +61,20 @@ enum a64_shift_type {
A64_SHIFT_TYPE_ROR = 3
};
+/* Table based decoder typedefs - used when the relevant bits for decode
+ * are too awkwardly scattered across the instruction (eg SIMD).
+ */
+typedef void AArch64DecodeFn(DisasContext *s, uint32_t insn);
+
+typedef struct AArch64DecodeTable {
+ uint32_t pattern;
+ uint32_t mask;
+ AArch64DecodeFn *disas_fn;
+} AArch64DecodeTable;
+
+/* Function prototype for gen_ functions for calling Neon helpers */
+typedef void NeonGenTwoOpFn(TCGv_i32, TCGv_i32, TCGv_i32);
+
/* initialize TCG globals. */
void a64_translate_init(void)
{
@@ -308,6 +322,28 @@ static TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf)
return v;
}
+/* Return the offset into CPUARMState of an element of specified
+ * size, 'element' places in from the least significant end of
+ * the FP/vector register Qn.
+ */
+static inline int vec_reg_offset(int regno, int element, TCGMemOp size)
+{
+ int offs = offsetof(CPUARMState, vfp.regs[regno * 2]);
+#ifdef HOST_WORDS_BIGENDIAN
+ /* This is complicated slightly because vfp.regs[2n] is
+ * still the low half and vfp.regs[2n+1] the high half
+ * of the 128 bit vector, even on big endian systems.
+ * Calculate the offset assuming a fully bigendian 128 bits,
+ * then XOR to account for the order of the two 64 bit halves.
+ */
+ offs += (16 - ((element + 1) * (1 << size)));
+ offs ^= 8;
+#else
+ offs += element * (1 << size);
+#endif
+ return offs;
+}
+
/* Return the offset into CPUARMState of a slice (from
* the least significant end) of FP register Qn (ie
* Dn, Sn, Hn or Bn).
@@ -661,6 +697,156 @@ static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size)
}
/*
+ * Vector load/store helpers.
+ *
+ * The principal difference between this and a FP load is that we don't
+ * zero extend as we are filling a partial chunk of the vector register.
+ * These functions don't support 128 bit loads/stores, which would be
+ * normal load/store operations.
+ *
+ * The _i32 versions are useful when operating on 32 bit quantities
+ * (eg for floating point single or using Neon helper functions).
+ */
+
+/* Get value of an element within a vector register */
+static void read_vec_element(DisasContext *s, TCGv_i64 tcg_dest, int srcidx,
+ int element, TCGMemOp memop)
+{
+ int vect_off = vec_reg_offset(srcidx, element, memop & MO_SIZE);
+ switch (memop) {
+ case MO_8:
+ tcg_gen_ld8u_i64(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_16:
+ tcg_gen_ld16u_i64(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_32:
+ tcg_gen_ld32u_i64(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_8|MO_SIGN:
+ tcg_gen_ld8s_i64(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_16|MO_SIGN:
+ tcg_gen_ld16s_i64(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_32|MO_SIGN:
+ tcg_gen_ld32s_i64(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_64:
+ case MO_64|MO_SIGN:
+ tcg_gen_ld_i64(tcg_dest, cpu_env, vect_off);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void read_vec_element_i32(DisasContext *s, TCGv_i32 tcg_dest, int srcidx,
+ int element, TCGMemOp memop)
+{
+ int vect_off = vec_reg_offset(srcidx, element, memop & MO_SIZE);
+ switch (memop) {
+ case MO_8:
+ tcg_gen_ld8u_i32(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_16:
+ tcg_gen_ld16u_i32(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_8|MO_SIGN:
+ tcg_gen_ld8s_i32(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_16|MO_SIGN:
+ tcg_gen_ld16s_i32(tcg_dest, cpu_env, vect_off);
+ break;
+ case MO_32:
+ case MO_32|MO_SIGN:
+ tcg_gen_ld_i32(tcg_dest, cpu_env, vect_off);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/* Set value of an element within a vector register */
+static void write_vec_element(DisasContext *s, TCGv_i64 tcg_src, int destidx,
+ int element, TCGMemOp memop)
+{
+ int vect_off = vec_reg_offset(destidx, element, memop & MO_SIZE);
+ switch (memop) {
+ case MO_8:
+ tcg_gen_st8_i64(tcg_src, cpu_env, vect_off);
+ break;
+ case MO_16:
+ tcg_gen_st16_i64(tcg_src, cpu_env, vect_off);
+ break;
+ case MO_32:
+ tcg_gen_st32_i64(tcg_src, cpu_env, vect_off);
+ break;
+ case MO_64:
+ tcg_gen_st_i64(tcg_src, cpu_env, vect_off);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void write_vec_element_i32(DisasContext *s, TCGv_i32 tcg_src,
+ int destidx, int element, TCGMemOp memop)
+{
+ int vect_off = vec_reg_offset(destidx, element, memop & MO_SIZE);
+ switch (memop) {
+ case MO_8:
+ tcg_gen_st8_i32(tcg_src, cpu_env, vect_off);
+ break;
+ case MO_16:
+ tcg_gen_st16_i32(tcg_src, cpu_env, vect_off);
+ break;
+ case MO_32:
+ tcg_gen_st_i32(tcg_src, cpu_env, vect_off);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/* Clear the high 64 bits of a 128 bit vector (in general non-quad
+ * vector ops all need to do this).
+ */
+static void clear_vec_high(DisasContext *s, int rd)
+{
+ TCGv_i64 tcg_zero = tcg_const_i64(0);
+
+ write_vec_element(s, tcg_zero, rd, 1, MO_64);
+ tcg_temp_free_i64(tcg_zero);
+}
+
+/* Store from vector register to memory */
+static void do_vec_st(DisasContext *s, int srcidx, int element,
+ TCGv_i64 tcg_addr, int size)
+{
+ TCGMemOp memop = MO_TE + size;
+ TCGv_i64 tcg_tmp = tcg_temp_new_i64();
+
+ read_vec_element(s, tcg_tmp, srcidx, element, size);
+ tcg_gen_qemu_st_i64(tcg_tmp, tcg_addr, get_mem_index(s), memop);
+
+ tcg_temp_free_i64(tcg_tmp);
+}
+
+/* Load from memory to vector register */
+static void do_vec_ld(DisasContext *s, int destidx, int element,
+ TCGv_i64 tcg_addr, int size)
+{
+ TCGMemOp memop = MO_TE + size;
+ TCGv_i64 tcg_tmp = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, get_mem_index(s), memop);
+ write_vec_element(s, tcg_tmp, destidx, element, size);
+
+ tcg_temp_free_i64(tcg_tmp);
+}
+
+/*
* This utility function is for doing register extension with an
* optional shift. You will likely want to pass a temporary for the
* destination register. See DecodeRegExtend() in the ARM ARM.
@@ -722,6 +908,31 @@ static inline void gen_check_sp_alignment(DisasContext *s)
}
/*
+ * This provides a simple table based table lookup decoder. It is
+ * intended to be used when the relevant bits for decode are too
+ * awkwardly placed and switch/if based logic would be confusing and
+ * deeply nested. Since it's a linear search through the table, tables
+ * should be kept small.
+ *
+ * It returns the first handler where insn & mask == pattern, or
+ * NULL if there is no match.
+ * The table is terminated by an empty mask (i.e. 0)
+ */
+static inline AArch64DecodeFn *lookup_disas_fn(const AArch64DecodeTable *table,
+ uint32_t insn)
+{
+ const AArch64DecodeTable *tptr = table;
+
+ while (tptr->mask) {
+ if ((insn & tptr->mask) == tptr->pattern) {
+ return tptr->disas_fn;
+ }
+ tptr++;
+ }
+ return NULL;
+}
+
+/*
* the instruction disassembly implemented here matches
* the instruction encoding classifications in chapter 3 (C3)
* of the ARM Architecture Reference Manual (DDI0487A_a)
@@ -1835,16 +2046,278 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn)
}
}
-/* AdvSIMD load/store multiple structures */
+/* C3.3.1 AdvSIMD load/store multiple structures
+ *
+ * 31 30 29 23 22 21 16 15 12 11 10 9 5 4 0
+ * +---+---+---------------+---+-------------+--------+------+------+------+
+ * | 0 | Q | 0 0 1 1 0 0 0 | L | 0 0 0 0 0 0 | opcode | size | Rn | Rt |
+ * +---+---+---------------+---+-------------+--------+------+------+------+
+ *
+ * C3.3.2 AdvSIMD load/store multiple structures (post-indexed)
+ *
+ * 31 30 29 23 22 21 20 16 15 12 11 10 9 5 4 0
+ * +---+---+---------------+---+---+---------+--------+------+------+------+
+ * | 0 | Q | 0 0 1 1 0 0 1 | L | 0 | Rm | opcode | size | Rn | Rt |
+ * +---+---+---------------+---+---+---------+--------+------+------+------+
+ *
+ * Rt: first (or only) SIMD&FP register to be transferred
+ * Rn: base address or SP
+ * Rm (post-index only): post-index register (when !31) or size dependent #imm
+ */
static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
{
- unsupported_encoding(s, insn);
+ int rt = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int size = extract32(insn, 10, 2);
+ int opcode = extract32(insn, 12, 4);
+ bool is_store = !extract32(insn, 22, 1);
+ bool is_postidx = extract32(insn, 23, 1);
+ bool is_q = extract32(insn, 30, 1);
+ TCGv_i64 tcg_addr, tcg_rn;
+
+ int ebytes = 1 << size;
+ int elements = (is_q ? 128 : 64) / (8 << size);
+ int rpt; /* num iterations */
+ int selem; /* structure elements */
+ int r;
+
+ if (extract32(insn, 31, 1) || extract32(insn, 21, 1)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ /* From the shared decode logic */
+ switch (opcode) {
+ case 0x0:
+ rpt = 1;
+ selem = 4;
+ break;
+ case 0x2:
+ rpt = 4;
+ selem = 1;
+ break;
+ case 0x4:
+ rpt = 1;
+ selem = 3;
+ break;
+ case 0x6:
+ rpt = 3;
+ selem = 1;
+ break;
+ case 0x7:
+ rpt = 1;
+ selem = 1;
+ break;
+ case 0x8:
+ rpt = 1;
+ selem = 2;
+ break;
+ case 0xa:
+ rpt = 2;
+ selem = 1;
+ break;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (size == 3 && !is_q && selem != 1) {
+ /* reserved */
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (rn == 31) {
+ gen_check_sp_alignment(s);
+ }
+
+ tcg_rn = cpu_reg_sp(s, rn);
+ tcg_addr = tcg_temp_new_i64();
+ tcg_gen_mov_i64(tcg_addr, tcg_rn);
+
+ for (r = 0; r < rpt; r++) {
+ int e;
+ for (e = 0; e < elements; e++) {
+ int tt = (rt + r) % 32;
+ int xs;
+ for (xs = 0; xs < selem; xs++) {
+ if (is_store) {
+ do_vec_st(s, tt, e, tcg_addr, size);
+ } else {
+ do_vec_ld(s, tt, e, tcg_addr, size);
+
+ /* For non-quad operations, setting a slice of the low
+ * 64 bits of the register clears the high 64 bits (in
+ * the ARM ARM pseudocode this is implicit in the fact
+ * that 'rval' is a 64 bit wide variable). We optimize
+ * by noticing that we only need to do this the first
+ * time we touch a register.
+ */
+ if (!is_q && e == 0 && (r == 0 || xs == selem - 1)) {
+ clear_vec_high(s, tt);
+ }
+ }
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, ebytes);
+ tt = (tt + 1) % 32;
+ }
+ }
+ }
+
+ if (is_postidx) {
+ int rm = extract32(insn, 16, 5);
+ if (rm == 31) {
+ tcg_gen_mov_i64(tcg_rn, tcg_addr);
+ } else {
+ tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm));
+ }
+ }
+ tcg_temp_free_i64(tcg_addr);
}
-/* AdvSIMD load/store single structure */
+/* C3.3.3 AdvSIMD load/store single structure
+ *
+ * 31 30 29 23 22 21 20 16 15 13 12 11 10 9 5 4 0
+ * +---+---+---------------+-----+-----------+-----+---+------+------+------+
+ * | 0 | Q | 0 0 1 1 0 1 0 | L R | 0 0 0 0 0 | opc | S | size | Rn | Rt |
+ * +---+---+---------------+-----+-----------+-----+---+------+------+------+
+ *
+ * C3.3.4 AdvSIMD load/store single structure (post-indexed)
+ *
+ * 31 30 29 23 22 21 20 16 15 13 12 11 10 9 5 4 0
+ * +---+---+---------------+-----+-----------+-----+---+------+------+------+
+ * | 0 | Q | 0 0 1 1 0 1 1 | L R | Rm | opc | S | size | Rn | Rt |
+ * +---+---+---------------+-----+-----------+-----+---+------+------+------+
+ *
+ * Rt: first (or only) SIMD&FP register to be transferred
+ * Rn: base address or SP
+ * Rm (post-index only): post-index register (when !31) or size dependent #imm
+ * index = encoded in Q:S:size dependent on size
+ *
+ * lane_size = encoded in R, opc
+ * transfer width = encoded in opc, S, size
+ */
static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
{
- unsupported_encoding(s, insn);
+ int rt = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int size = extract32(insn, 10, 2);
+ int S = extract32(insn, 12, 1);
+ int opc = extract32(insn, 13, 3);
+ int R = extract32(insn, 21, 1);
+ int is_load = extract32(insn, 22, 1);
+ int is_postidx = extract32(insn, 23, 1);
+ int is_q = extract32(insn, 30, 1);
+
+ int scale = extract32(opc, 1, 2);
+ int selem = (extract32(opc, 0, 1) << 1 | R) + 1;
+ bool replicate = false;
+ int index = is_q << 3 | S << 2 | size;
+ int ebytes, xs;
+ TCGv_i64 tcg_addr, tcg_rn;
+
+ switch (scale) {
+ case 3:
+ if (!is_load || S) {
+ unallocated_encoding(s);
+ return;
+ }
+ scale = size;
+ replicate = true;
+ break;
+ case 0:
+ break;
+ case 1:
+ if (extract32(size, 0, 1)) {
+ unallocated_encoding(s);
+ return;
+ }
+ index >>= 1;
+ break;
+ case 2:
+ if (extract32(size, 1, 1)) {
+ unallocated_encoding(s);
+ return;
+ }
+ if (!extract32(size, 0, 1)) {
+ index >>= 2;
+ } else {
+ if (S) {
+ unallocated_encoding(s);
+ return;
+ }
+ index >>= 3;
+ scale = 3;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ ebytes = 1 << scale;
+
+ if (rn == 31) {
+ gen_check_sp_alignment(s);
+ }
+
+ tcg_rn = cpu_reg_sp(s, rn);
+ tcg_addr = tcg_temp_new_i64();
+ tcg_gen_mov_i64(tcg_addr, tcg_rn);
+
+ for (xs = 0; xs < selem; xs++) {
+ if (replicate) {
+ /* Load and replicate to all elements */
+ uint64_t mulconst;
+ TCGv_i64 tcg_tmp = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr,
+ get_mem_index(s), MO_TE + scale);
+ switch (scale) {
+ case 0:
+ mulconst = 0x0101010101010101ULL;
+ break;
+ case 1:
+ mulconst = 0x0001000100010001ULL;
+ break;
+ case 2:
+ mulconst = 0x0000000100000001ULL;
+ break;
+ case 3:
+ mulconst = 0;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ if (mulconst) {
+ tcg_gen_muli_i64(tcg_tmp, tcg_tmp, mulconst);
+ }
+ write_vec_element(s, tcg_tmp, rt, 0, MO_64);
+ if (is_q) {
+ write_vec_element(s, tcg_tmp, rt, 1, MO_64);
+ } else {
+ clear_vec_high(s, rt);
+ }
+ tcg_temp_free_i64(tcg_tmp);
+ } else {
+ /* Load/store one element per register */
+ if (is_load) {
+ do_vec_ld(s, rt, index, tcg_addr, MO_TE + scale);
+ } else {
+ do_vec_st(s, rt, index, tcg_addr, MO_TE + scale);
+ }
+ }
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, ebytes);
+ rt = (rt + 1) % 32;
+ }
+
+ if (is_postidx) {
+ int rm = extract32(insn, 16, 5);
+ if (rm == 31) {
+ tcg_gen_mov_i64(tcg_rn, tcg_addr);
+ } else {
+ tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm));
+ }
+ }
+ tcg_temp_free_i64(tcg_addr);
}
/* C3.3 Loads and stores */
@@ -3186,34 +3659,6 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn)
}
}
-/* Convert ARM rounding mode to softfloat */
-static inline int arm_rmode_to_sf(int rmode)
-{
- switch (rmode) {
- case FPROUNDING_TIEAWAY:
- rmode = float_round_ties_away;
- break;
- case FPROUNDING_ODD:
- /* FIXME: add support for TIEAWAY and ODD */
- qemu_log_mask(LOG_UNIMP, "arm: unimplemented rounding mode: %d\n",
- rmode);
- case FPROUNDING_TIEEVEN:
- default:
- rmode = float_round_nearest_even;
- break;
- case FPROUNDING_POSINF:
- rmode = float_round_up;
- break;
- case FPROUNDING_NEGINF:
- rmode = float_round_down;
- break;
- case FPROUNDING_ZERO:
- rmode = float_round_to_zero;
- break;
- }
- return rmode;
-}
-
static void handle_fp_compare(DisasContext *s, bool is_double,
unsigned int rn, unsigned int rm,
bool cmp_with_zero, bool signal_all_nans)
@@ -4224,13 +4669,2201 @@ static void disas_data_proc_fp(DisasContext *s, uint32_t insn)
}
}
+static void do_ext64(DisasContext *s, TCGv_i64 tcg_left, TCGv_i64 tcg_right,
+ int pos)
+{
+ /* Extract 64 bits from the middle of two concatenated 64 bit
+ * vector register slices left:right. The extracted bits start
+ * at 'pos' bits into the right (least significant) side.
+ * We return the result in tcg_right, and guarantee not to
+ * trash tcg_left.
+ */
+ TCGv_i64 tcg_tmp = tcg_temp_new_i64();
+ assert(pos > 0 && pos < 64);
+
+ tcg_gen_shri_i64(tcg_right, tcg_right, pos);
+ tcg_gen_shli_i64(tcg_tmp, tcg_left, 64 - pos);
+ tcg_gen_or_i64(tcg_right, tcg_right, tcg_tmp);
+
+ tcg_temp_free_i64(tcg_tmp);
+}
+
+/* C3.6.1 EXT
+ * 31 30 29 24 23 22 21 20 16 15 14 11 10 9 5 4 0
+ * +---+---+-------------+-----+---+------+---+------+---+------+------+
+ * | 0 | Q | 1 0 1 1 1 0 | op2 | 0 | Rm | 0 | imm4 | 0 | Rn | Rd |
+ * +---+---+-------------+-----+---+------+---+------+---+------+------+
+ */
+static void disas_simd_ext(DisasContext *s, uint32_t insn)
+{
+ int is_q = extract32(insn, 30, 1);
+ int op2 = extract32(insn, 22, 2);
+ int imm4 = extract32(insn, 11, 4);
+ int rm = extract32(insn, 16, 5);
+ int rn = extract32(insn, 5, 5);
+ int rd = extract32(insn, 0, 5);
+ int pos = imm4 << 3;
+ TCGv_i64 tcg_resl, tcg_resh;
+
+ if (op2 != 0 || (!is_q && extract32(imm4, 3, 1))) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_resh = tcg_temp_new_i64();
+ tcg_resl = tcg_temp_new_i64();
+
+ /* Vd gets bits starting at pos bits into Vm:Vn. This is
+ * either extracting 128 bits from a 128:128 concatenation, or
+ * extracting 64 bits from a 64:64 concatenation.
+ */
+ if (!is_q) {
+ read_vec_element(s, tcg_resl, rn, 0, MO_64);
+ if (pos != 0) {
+ read_vec_element(s, tcg_resh, rm, 0, MO_64);
+ do_ext64(s, tcg_resh, tcg_resl, pos);
+ }
+ tcg_gen_movi_i64(tcg_resh, 0);
+ } else {
+ TCGv_i64 tcg_hh;
+ typedef struct {
+ int reg;
+ int elt;
+ } EltPosns;
+ EltPosns eltposns[] = { {rn, 0}, {rn, 1}, {rm, 0}, {rm, 1} };
+ EltPosns *elt = eltposns;
+
+ if (pos >= 64) {
+ elt++;
+ pos -= 64;
+ }
+
+ read_vec_element(s, tcg_resl, elt->reg, elt->elt, MO_64);
+ elt++;
+ read_vec_element(s, tcg_resh, elt->reg, elt->elt, MO_64);
+ elt++;
+ if (pos != 0) {
+ do_ext64(s, tcg_resh, tcg_resl, pos);
+ tcg_hh = tcg_temp_new_i64();
+ read_vec_element(s, tcg_hh, elt->reg, elt->elt, MO_64);
+ do_ext64(s, tcg_hh, tcg_resh, pos);
+ tcg_temp_free_i64(tcg_hh);
+ }
+ }
+
+ write_vec_element(s, tcg_resl, rd, 0, MO_64);
+ tcg_temp_free_i64(tcg_resl);
+ write_vec_element(s, tcg_resh, rd, 1, MO_64);
+ tcg_temp_free_i64(tcg_resh);
+}
+
+/* C3.6.2 TBL/TBX
+ * 31 30 29 24 23 22 21 20 16 15 14 13 12 11 10 9 5 4 0
+ * +---+---+-------------+-----+---+------+---+-----+----+-----+------+------+
+ * | 0 | Q | 0 0 1 1 1 0 | op2 | 0 | Rm | 0 | len | op | 0 0 | Rn | Rd |
+ * +---+---+-------------+-----+---+------+---+-----+----+-----+------+------+
+ */
+static void disas_simd_tb(DisasContext *s, uint32_t insn)
+{
+ int op2 = extract32(insn, 22, 2);
+ int is_q = extract32(insn, 30, 1);
+ int rm = extract32(insn, 16, 5);
+ int rn = extract32(insn, 5, 5);
+ int rd = extract32(insn, 0, 5);
+ int is_tblx = extract32(insn, 12, 1);
+ int len = extract32(insn, 13, 2);
+ TCGv_i64 tcg_resl, tcg_resh, tcg_idx;
+ TCGv_i32 tcg_regno, tcg_numregs;
+
+ if (op2 != 0) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ /* This does a table lookup: for every byte element in the input
+ * we index into a table formed from up to four vector registers,
+ * and then the output is the result of the lookups. Our helper
+ * function does the lookup operation for a single 64 bit part of
+ * the input.
+ */
+ tcg_resl = tcg_temp_new_i64();
+ tcg_resh = tcg_temp_new_i64();
+
+ if (is_tblx) {
+ read_vec_element(s, tcg_resl, rd, 0, MO_64);
+ } else {
+ tcg_gen_movi_i64(tcg_resl, 0);
+ }
+ if (is_tblx && is_q) {
+ read_vec_element(s, tcg_resh, rd, 1, MO_64);
+ } else {
+ tcg_gen_movi_i64(tcg_resh, 0);
+ }
+
+ tcg_idx = tcg_temp_new_i64();
+ tcg_regno = tcg_const_i32(rn);
+ tcg_numregs = tcg_const_i32(len + 1);
+ read_vec_element(s, tcg_idx, rm, 0, MO_64);
+ gen_helper_simd_tbl(tcg_resl, cpu_env, tcg_resl, tcg_idx,
+ tcg_regno, tcg_numregs);
+ if (is_q) {
+ read_vec_element(s, tcg_idx, rm, 1, MO_64);
+ gen_helper_simd_tbl(tcg_resh, cpu_env, tcg_resh, tcg_idx,
+ tcg_regno, tcg_numregs);
+ }
+ tcg_temp_free_i64(tcg_idx);
+ tcg_temp_free_i32(tcg_regno);
+ tcg_temp_free_i32(tcg_numregs);
+
+ write_vec_element(s, tcg_resl, rd, 0, MO_64);
+ tcg_temp_free_i64(tcg_resl);
+ write_vec_element(s, tcg_resh, rd, 1, MO_64);
+ tcg_temp_free_i64(tcg_resh);
+}
+
+/* C3.6.3 ZIP/UZP/TRN
+ * 31 30 29 24 23 22 21 20 16 15 14 12 11 10 9 5 4 0
+ * +---+---+-------------+------+---+------+---+------------------+------+
+ * | 0 | Q | 0 0 1 1 1 0 | size | 0 | Rm | 0 | opc | 1 0 | Rn | Rd |
+ * +---+---+-------------+------+---+------+---+------------------+------+
+ */
+static void disas_simd_zip_trn(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int rm = extract32(insn, 16, 5);
+ int size = extract32(insn, 22, 2);
+ /* opc field bits [1:0] indicate ZIP/UZP/TRN;
+ * bit 2 indicates 1 vs 2 variant of the insn.
+ */
+ int opcode = extract32(insn, 12, 2);
+ bool part = extract32(insn, 14, 1);
+ bool is_q = extract32(insn, 30, 1);
+ int esize = 8 << size;
+ int i, ofs;
+ int datasize = is_q ? 128 : 64;
+ int elements = datasize / esize;
+ TCGv_i64 tcg_res, tcg_resl, tcg_resh;
+
+ if (opcode == 0 || (size == 3 && !is_q)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_resl = tcg_const_i64(0);
+ tcg_resh = tcg_const_i64(0);
+ tcg_res = tcg_temp_new_i64();
+
+ for (i = 0; i < elements; i++) {
+ switch (opcode) {
+ case 1: /* UZP1/2 */
+ {
+ int midpoint = elements / 2;
+ if (i < midpoint) {
+ read_vec_element(s, tcg_res, rn, 2 * i + part, size);
+ } else {
+ read_vec_element(s, tcg_res, rm,
+ 2 * (i - midpoint) + part, size);
+ }
+ break;
+ }
+ case 2: /* TRN1/2 */
+ if (i & 1) {
+ read_vec_element(s, tcg_res, rm, (i & ~1) + part, size);
+ } else {
+ read_vec_element(s, tcg_res, rn, (i & ~1) + part, size);
+ }
+ break;
+ case 3: /* ZIP1/2 */
+ {
+ int base = part * elements / 2;
+ if (i & 1) {
+ read_vec_element(s, tcg_res, rm, base + (i >> 1), size);
+ } else {
+ read_vec_element(s, tcg_res, rn, base + (i >> 1), size);
+ }
+ break;
+ }
+ default:
+ g_assert_not_reached();
+ }
+
+ ofs = i * esize;
+ if (ofs < 64) {
+ tcg_gen_shli_i64(tcg_res, tcg_res, ofs);
+ tcg_gen_or_i64(tcg_resl, tcg_resl, tcg_res);
+ } else {
+ tcg_gen_shli_i64(tcg_res, tcg_res, ofs - 64);
+ tcg_gen_or_i64(tcg_resh, tcg_resh, tcg_res);
+ }
+ }
+
+ tcg_temp_free_i64(tcg_res);
+
+ write_vec_element(s, tcg_resl, rd, 0, MO_64);
+ tcg_temp_free_i64(tcg_resl);
+ write_vec_element(s, tcg_resh, rd, 1, MO_64);
+ tcg_temp_free_i64(tcg_resh);
+}
+
+static void do_minmaxop(DisasContext *s, TCGv_i32 tcg_elt1, TCGv_i32 tcg_elt2,
+ int opc, bool is_min, TCGv_ptr fpst)
+{
+ /* Helper function for disas_simd_across_lanes: do a single precision
+ * min/max operation on the specified two inputs,
+ * and return the result in tcg_elt1.
+ */
+ if (opc == 0xc) {
+ if (is_min) {
+ gen_helper_vfp_minnums(tcg_elt1, tcg_elt1, tcg_elt2, fpst);
+ } else {
+ gen_helper_vfp_maxnums(tcg_elt1, tcg_elt1, tcg_elt2, fpst);
+ }
+ } else {
+ assert(opc == 0xf);
+ if (is_min) {
+ gen_helper_vfp_mins(tcg_elt1, tcg_elt1, tcg_elt2, fpst);
+ } else {
+ gen_helper_vfp_maxs(tcg_elt1, tcg_elt1, tcg_elt2, fpst);
+ }
+ }
+}
+
+/* C3.6.4 AdvSIMD across lanes
+ * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0
+ * +---+---+---+-----------+------+-----------+--------+-----+------+------+
+ * | 0 | Q | U | 0 1 1 1 0 | size | 1 1 0 0 0 | opcode | 1 0 | Rn | Rd |
+ * +---+---+---+-----------+------+-----------+--------+-----+------+------+
+ */
+static void disas_simd_across_lanes(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int size = extract32(insn, 22, 2);
+ int opcode = extract32(insn, 12, 5);
+ bool is_q = extract32(insn, 30, 1);
+ bool is_u = extract32(insn, 29, 1);
+ bool is_fp = false;
+ bool is_min = false;
+ int esize;
+ int elements;
+ int i;
+ TCGv_i64 tcg_res, tcg_elt;
+
+ switch (opcode) {
+ case 0x1b: /* ADDV */
+ if (is_u) {
+ unallocated_encoding(s);
+ return;
+ }
+ /* fall through */
+ case 0x3: /* SADDLV, UADDLV */
+ case 0xa: /* SMAXV, UMAXV */
+ case 0x1a: /* SMINV, UMINV */
+ if (size == 3 || (size == 2 && !is_q)) {
+ unallocated_encoding(s);
+ return;
+ }
+ break;
+ case 0xc: /* FMAXNMV, FMINNMV */
+ case 0xf: /* FMAXV, FMINV */
+ if (!is_u || !is_q || extract32(size, 0, 1)) {
+ unallocated_encoding(s);
+ return;
+ }
+ /* Bit 1 of size field encodes min vs max, and actual size is always
+ * 32 bits: adjust the size variable so following code can rely on it
+ */
+ is_min = extract32(size, 1, 1);
+ is_fp = true;
+ size = 2;
+ break;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+
+ esize = 8 << size;
+ elements = (is_q ? 128 : 64) / esize;
+
+ tcg_res = tcg_temp_new_i64();
+ tcg_elt = tcg_temp_new_i64();
+
+ /* These instructions operate across all lanes of a vector
+ * to produce a single result. We can guarantee that a 64
+ * bit intermediate is sufficient:
+ * + for [US]ADDLV the maximum element size is 32 bits, and
+ * the result type is 64 bits
+ * + for FMAX*V, FMIN*V, ADDV the intermediate type is the
+ * same as the element size, which is 32 bits at most
+ * For the integer operations we can choose to work at 64
+ * or 32 bits and truncate at the end; for simplicity
+ * we use 64 bits always. The floating point
+ * ops do require 32 bit intermediates, though.
+ */
+ if (!is_fp) {
+ read_vec_element(s, tcg_res, rn, 0, size | (is_u ? 0 : MO_SIGN));
+
+ for (i = 1; i < elements; i++) {
+ read_vec_element(s, tcg_elt, rn, i, size | (is_u ? 0 : MO_SIGN));
+
+ switch (opcode) {
+ case 0x03: /* SADDLV / UADDLV */
+ case 0x1b: /* ADDV */
+ tcg_gen_add_i64(tcg_res, tcg_res, tcg_elt);
+ break;
+ case 0x0a: /* SMAXV / UMAXV */
+ tcg_gen_movcond_i64(is_u ? TCG_COND_GEU : TCG_COND_GE,
+ tcg_res,
+ tcg_res, tcg_elt, tcg_res, tcg_elt);
+ break;
+ case 0x1a: /* SMINV / UMINV */
+ tcg_gen_movcond_i64(is_u ? TCG_COND_LEU : TCG_COND_LE,
+ tcg_res,
+ tcg_res, tcg_elt, tcg_res, tcg_elt);
+ break;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ }
+ } else {
+ /* Floating point ops which work on 32 bit (single) intermediates.
+ * Note that correct NaN propagation requires that we do these
+ * operations in exactly the order specified by the pseudocode.
+ */
+ TCGv_i32 tcg_elt1 = tcg_temp_new_i32();
+ TCGv_i32 tcg_elt2 = tcg_temp_new_i32();
+ TCGv_i32 tcg_elt3 = tcg_temp_new_i32();
+ TCGv_ptr fpst = get_fpstatus_ptr();
+
+ assert(esize == 32);
+ assert(elements == 4);
+
+ read_vec_element(s, tcg_elt, rn, 0, MO_32);
+ tcg_gen_trunc_i64_i32(tcg_elt1, tcg_elt);
+ read_vec_element(s, tcg_elt, rn, 1, MO_32);
+ tcg_gen_trunc_i64_i32(tcg_elt2, tcg_elt);
+
+ do_minmaxop(s, tcg_elt1, tcg_elt2, opcode, is_min, fpst);
+
+ read_vec_element(s, tcg_elt, rn, 2, MO_32);
+ tcg_gen_trunc_i64_i32(tcg_elt2, tcg_elt);
+ read_vec_element(s, tcg_elt, rn, 3, MO_32);
+ tcg_gen_trunc_i64_i32(tcg_elt3, tcg_elt);
+
+ do_minmaxop(s, tcg_elt2, tcg_elt3, opcode, is_min, fpst);
+
+ do_minmaxop(s, tcg_elt1, tcg_elt2, opcode, is_min, fpst);
+
+ tcg_gen_extu_i32_i64(tcg_res, tcg_elt1);
+ tcg_temp_free_i32(tcg_elt1);
+ tcg_temp_free_i32(tcg_elt2);
+ tcg_temp_free_i32(tcg_elt3);
+ tcg_temp_free_ptr(fpst);
+ }
+
+ tcg_temp_free_i64(tcg_elt);
+
+ /* Now truncate the result to the width required for the final output */
+ if (opcode == 0x03) {
+ /* SADDLV, UADDLV: result is 2*esize */
+ size++;
+ }
+
+ switch (size) {
+ case 0:
+ tcg_gen_ext8u_i64(tcg_res, tcg_res);
+ break;
+ case 1:
+ tcg_gen_ext16u_i64(tcg_res, tcg_res);
+ break;
+ case 2:
+ tcg_gen_ext32u_i64(tcg_res, tcg_res);
+ break;
+ case 3:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ write_fp_dreg(s, rd, tcg_res);
+ tcg_temp_free_i64(tcg_res);
+}
+
+/* C6.3.31 DUP (Element, Vector)
+ *
+ * 31 30 29 21 20 16 15 10 9 5 4 0
+ * +---+---+-------------------+--------+-------------+------+------+
+ * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 0 1 | Rn | Rd |
+ * +---+---+-------------------+--------+-------------+------+------+
+ *
+ * size: encoded in imm5 (see ARM ARM LowestSetBit())
+ */
+static void handle_simd_dupe(DisasContext *s, int is_q, int rd, int rn,
+ int imm5)
+{
+ int size = ctz32(imm5);
+ int esize = 8 << size;
+ int elements = (is_q ? 128 : 64) / esize;
+ int index, i;
+ TCGv_i64 tmp;
+
+ if (size > 3 || (size == 3 && !is_q)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ index = imm5 >> (size + 1);
+
+ tmp = tcg_temp_new_i64();
+ read_vec_element(s, tmp, rn, index, size);
+
+ for (i = 0; i < elements; i++) {
+ write_vec_element(s, tmp, rd, i, size);
+ }
+
+ if (!is_q) {
+ clear_vec_high(s, rd);
+ }
+
+ tcg_temp_free_i64(tmp);
+}
+
+/* C6.3.31 DUP (element, scalar)
+ * 31 21 20 16 15 10 9 5 4 0
+ * +-----------------------+--------+-------------+------+------+
+ * | 0 1 0 1 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 0 1 | Rn | Rd |
+ * +-----------------------+--------+-------------+------+------+
+ */
+static void handle_simd_dupes(DisasContext *s, int rd, int rn,
+ int imm5)
+{
+ int size = ctz32(imm5);
+ int index;
+ TCGv_i64 tmp;
+
+ if (size > 3) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ index = imm5 >> (size + 1);
+
+ /* This instruction just extracts the specified element and
+ * zero-extends it into the bottom of the destination register.
+ */
+ tmp = tcg_temp_new_i64();
+ read_vec_element(s, tmp, rn, index, size);
+ write_fp_dreg(s, rd, tmp);
+ tcg_temp_free_i64(tmp);
+}
+
+/* C6.3.32 DUP (General)
+ *
+ * 31 30 29 21 20 16 15 10 9 5 4 0
+ * +---+---+-------------------+--------+-------------+------+------+
+ * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 1 1 | Rn | Rd |
+ * +---+---+-------------------+--------+-------------+------+------+
+ *
+ * size: encoded in imm5 (see ARM ARM LowestSetBit())
+ */
+static void handle_simd_dupg(DisasContext *s, int is_q, int rd, int rn,
+ int imm5)
+{
+ int size = ctz32(imm5);
+ int esize = 8 << size;
+ int elements = (is_q ? 128 : 64)/esize;
+ int i = 0;
+
+ if (size > 3 || ((size == 3) && !is_q)) {
+ unallocated_encoding(s);
+ return;
+ }
+ for (i = 0; i < elements; i++) {
+ write_vec_element(s, cpu_reg(s, rn), rd, i, size);
+ }
+ if (!is_q) {
+ clear_vec_high(s, rd);
+ }
+}
+
+/* C6.3.150 INS (Element)
+ *
+ * 31 21 20 16 15 14 11 10 9 5 4 0
+ * +-----------------------+--------+------------+---+------+------+
+ * | 0 1 1 0 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd |
+ * +-----------------------+--------+------------+---+------+------+
+ *
+ * size: encoded in imm5 (see ARM ARM LowestSetBit())
+ * index: encoded in imm5<4:size+1>
+ */
+static void handle_simd_inse(DisasContext *s, int rd, int rn,
+ int imm4, int imm5)
+{
+ int size = ctz32(imm5);
+ int src_index, dst_index;
+ TCGv_i64 tmp;
+
+ if (size > 3) {
+ unallocated_encoding(s);
+ return;
+ }
+ dst_index = extract32(imm5, 1+size, 5);
+ src_index = extract32(imm4, size, 4);
+
+ tmp = tcg_temp_new_i64();
+
+ read_vec_element(s, tmp, rn, src_index, size);
+ write_vec_element(s, tmp, rd, dst_index, size);
+
+ tcg_temp_free_i64(tmp);
+}
+
+
+/* C6.3.151 INS (General)
+ *
+ * 31 21 20 16 15 10 9 5 4 0
+ * +-----------------------+--------+-------------+------+------+
+ * | 0 1 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 1 1 1 | Rn | Rd |
+ * +-----------------------+--------+-------------+------+------+
+ *
+ * size: encoded in imm5 (see ARM ARM LowestSetBit())
+ * index: encoded in imm5<4:size+1>
+ */
+static void handle_simd_insg(DisasContext *s, int rd, int rn, int imm5)
+{
+ int size = ctz32(imm5);
+ int idx;
+
+ if (size > 3) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ idx = extract32(imm5, 1 + size, 4 - size);
+ write_vec_element(s, cpu_reg(s, rn), rd, idx, size);
+}
+
+/*
+ * C6.3.321 UMOV (General)
+ * C6.3.237 SMOV (General)
+ *
+ * 31 30 29 21 20 16 15 12 10 9 5 4 0
+ * +---+---+-------------------+--------+-------------+------+------+
+ * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 1 U 1 1 | Rn | Rd |
+ * +---+---+-------------------+--------+-------------+------+------+
+ *
+ * U: unsigned when set
+ * size: encoded in imm5 (see ARM ARM LowestSetBit())
+ */
+static void handle_simd_umov_smov(DisasContext *s, int is_q, int is_signed,
+ int rn, int rd, int imm5)
+{
+ int size = ctz32(imm5);
+ int element;
+ TCGv_i64 tcg_rd;
+
+ /* Check for UnallocatedEncodings */
+ if (is_signed) {
+ if (size > 2 || (size == 2 && !is_q)) {
+ unallocated_encoding(s);
+ return;
+ }
+ } else {
+ if (size > 3
+ || (size < 3 && is_q)
+ || (size == 3 && !is_q)) {
+ unallocated_encoding(s);
+ return;
+ }
+ }
+ element = extract32(imm5, 1+size, 4);
+
+ tcg_rd = cpu_reg(s, rd);
+ read_vec_element(s, tcg_rd, rn, element, size | (is_signed ? MO_SIGN : 0));
+ if (is_signed && !is_q) {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+}
+
+/* C3.6.5 AdvSIMD copy
+ * 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0
+ * +---+---+----+-----------------+------+---+------+---+------+------+
+ * | 0 | Q | op | 0 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd |
+ * +---+---+----+-----------------+------+---+------+---+------+------+
+ */
+static void disas_simd_copy(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int imm4 = extract32(insn, 11, 4);
+ int op = extract32(insn, 29, 1);
+ int is_q = extract32(insn, 30, 1);
+ int imm5 = extract32(insn, 16, 5);
+
+ if (op) {
+ if (is_q) {
+ /* INS (element) */
+ handle_simd_inse(s, rd, rn, imm4, imm5);
+ } else {
+ unallocated_encoding(s);
+ }
+ } else {
+ switch (imm4) {
+ case 0:
+ /* DUP (element - vector) */
+ handle_simd_dupe(s, is_q, rd, rn, imm5);
+ break;
+ case 1:
+ /* DUP (general) */
+ handle_simd_dupg(s, is_q, rd, rn, imm5);
+ break;
+ case 3:
+ if (is_q) {
+ /* INS (general) */
+ handle_simd_insg(s, rd, rn, imm5);
+ } else {
+ unallocated_encoding(s);
+ }
+ break;
+ case 5:
+ case 7:
+ /* UMOV/SMOV (is_q indicates 32/64; imm4 indicates signedness) */
+ handle_simd_umov_smov(s, is_q, (imm4 == 5), rn, rd, imm5);
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+ }
+}
+
+/* C3.6.6 AdvSIMD modified immediate
+ * 31 30 29 28 19 18 16 15 12 11 10 9 5 4 0
+ * +---+---+----+---------------------+-----+-------+----+---+-------+------+
+ * | 0 | Q | op | 0 1 1 1 1 0 0 0 0 0 | abc | cmode | o2 | 1 | defgh | Rd |
+ * +---+---+----+---------------------+-----+-------+----+---+-------+------+
+ *
+ * There are a number of operations that can be carried out here:
+ * MOVI - move (shifted) imm into register
+ * MVNI - move inverted (shifted) imm into register
+ * ORR - bitwise OR of (shifted) imm with register
+ * BIC - bitwise clear of (shifted) imm with register
+ */
+static void disas_simd_mod_imm(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int cmode = extract32(insn, 12, 4);
+ int cmode_3_1 = extract32(cmode, 1, 3);
+ int cmode_0 = extract32(cmode, 0, 1);
+ int o2 = extract32(insn, 11, 1);
+ uint64_t abcdefgh = extract32(insn, 5, 5) | (extract32(insn, 16, 3) << 5);
+ bool is_neg = extract32(insn, 29, 1);
+ bool is_q = extract32(insn, 30, 1);
+ uint64_t imm = 0;
+ TCGv_i64 tcg_rd, tcg_imm;
+ int i;
+
+ if (o2 != 0 || ((cmode == 0xf) && is_neg && !is_q)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ /* See AdvSIMDExpandImm() in ARM ARM */
+ switch (cmode_3_1) {
+ case 0: /* Replicate(Zeros(24):imm8, 2) */
+ case 1: /* Replicate(Zeros(16):imm8:Zeros(8), 2) */
+ case 2: /* Replicate(Zeros(8):imm8:Zeros(16), 2) */
+ case 3: /* Replicate(imm8:Zeros(24), 2) */
+ {
+ int shift = cmode_3_1 * 8;
+ imm = bitfield_replicate(abcdefgh << shift, 32);
+ break;
+ }
+ case 4: /* Replicate(Zeros(8):imm8, 4) */
+ case 5: /* Replicate(imm8:Zeros(8), 4) */
+ {
+ int shift = (cmode_3_1 & 0x1) * 8;
+ imm = bitfield_replicate(abcdefgh << shift, 16);
+ break;
+ }
+ case 6:
+ if (cmode_0) {
+ /* Replicate(Zeros(8):imm8:Ones(16), 2) */
+ imm = (abcdefgh << 16) | 0xffff;
+ } else {
+ /* Replicate(Zeros(16):imm8:Ones(8), 2) */
+ imm = (abcdefgh << 8) | 0xff;
+ }
+ imm = bitfield_replicate(imm, 32);
+ break;
+ case 7:
+ if (!cmode_0 && !is_neg) {
+ imm = bitfield_replicate(abcdefgh, 8);
+ } else if (!cmode_0 && is_neg) {
+ int i;
+ imm = 0;
+ for (i = 0; i < 8; i++) {
+ if ((abcdefgh) & (1 << i)) {
+ imm |= 0xffULL << (i * 8);
+ }
+ }
+ } else if (cmode_0) {
+ if (is_neg) {
+ imm = (abcdefgh & 0x3f) << 48;
+ if (abcdefgh & 0x80) {
+ imm |= 0x8000000000000000ULL;
+ }
+ if (abcdefgh & 0x40) {
+ imm |= 0x3fc0000000000000ULL;
+ } else {
+ imm |= 0x4000000000000000ULL;
+ }
+ } else {
+ imm = (abcdefgh & 0x3f) << 19;
+ if (abcdefgh & 0x80) {
+ imm |= 0x80000000;
+ }
+ if (abcdefgh & 0x40) {
+ imm |= 0x3e000000;
+ } else {
+ imm |= 0x40000000;
+ }
+ imm |= (imm << 32);
+ }
+ }
+ break;
+ }
+
+ if (cmode_3_1 != 7 && is_neg) {
+ imm = ~imm;
+ }
+
+ tcg_imm = tcg_const_i64(imm);
+ tcg_rd = new_tmp_a64(s);
+
+ for (i = 0; i < 2; i++) {
+ int foffs = i ? fp_reg_hi_offset(rd) : fp_reg_offset(rd, MO_64);
+
+ if (i == 1 && !is_q) {
+ /* non-quad ops clear high half of vector */
+ tcg_gen_movi_i64(tcg_rd, 0);
+ } else if ((cmode & 0x9) == 0x1 || (cmode & 0xd) == 0x9) {
+ tcg_gen_ld_i64(tcg_rd, cpu_env, foffs);
+ if (is_neg) {
+ /* AND (BIC) */
+ tcg_gen_and_i64(tcg_rd, tcg_rd, tcg_imm);
+ } else {
+ /* ORR */
+ tcg_gen_or_i64(tcg_rd, tcg_rd, tcg_imm);
+ }
+ } else {
+ /* MOVI */
+ tcg_gen_mov_i64(tcg_rd, tcg_imm);
+ }
+ tcg_gen_st_i64(tcg_rd, cpu_env, foffs);
+ }
+
+ tcg_temp_free_i64(tcg_imm);
+}
+
+/* C3.6.7 AdvSIMD scalar copy
+ * 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0
+ * +-----+----+-----------------+------+---+------+---+------+------+
+ * | 0 1 | op | 1 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd |
+ * +-----+----+-----------------+------+---+------+---+------+------+
+ */
+static void disas_simd_scalar_copy(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int imm4 = extract32(insn, 11, 4);
+ int imm5 = extract32(insn, 16, 5);
+ int op = extract32(insn, 29, 1);
+
+ if (op != 0 || imm4 != 0) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ /* DUP (element, scalar) */
+ handle_simd_dupes(s, rd, rn, imm5);
+}
+
+/* C3.6.8 AdvSIMD scalar pairwise
+ * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0
+ * +-----+---+-----------+------+-----------+--------+-----+------+------+
+ * | 0 1 | U | 1 1 1 1 0 | size | 1 1 0 0 0 | opcode | 1 0 | Rn | Rd |
+ * +-----+---+-----------+------+-----------+--------+-----+------+------+
+ */
+static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/*
+ * Common SSHR[RA]/USHR[RA] - Shift right (optional rounding/accumulate)
+ *
+ * This code is handles the common shifting code and is used by both
+ * the vector and scalar code.
+ */
+static void handle_shri_with_rndacc(TCGv_i64 tcg_res, TCGv_i64 tcg_src,
+ TCGv_i64 tcg_rnd, bool accumulate,
+ bool is_u, int size, int shift)
+{
+ bool extended_result = false;
+ bool round = !TCGV_IS_UNUSED_I64(tcg_rnd);
+ int ext_lshift = 0;
+ TCGv_i64 tcg_src_hi;
+
+ if (round && size == 3) {
+ extended_result = true;
+ ext_lshift = 64 - shift;
+ tcg_src_hi = tcg_temp_new_i64();
+ } else if (shift == 64) {
+ if (!accumulate && is_u) {
+ /* result is zero */
+ tcg_gen_movi_i64(tcg_res, 0);
+ return;
+ }
+ }
+
+ /* Deal with the rounding step */
+ if (round) {
+ if (extended_result) {
+ TCGv_i64 tcg_zero = tcg_const_i64(0);
+ if (!is_u) {
+ /* take care of sign extending tcg_res */
+ tcg_gen_sari_i64(tcg_src_hi, tcg_src, 63);
+ tcg_gen_add2_i64(tcg_src, tcg_src_hi,
+ tcg_src, tcg_src_hi,
+ tcg_rnd, tcg_zero);
+ } else {
+ tcg_gen_add2_i64(tcg_src, tcg_src_hi,
+ tcg_src, tcg_zero,
+ tcg_rnd, tcg_zero);
+ }
+ tcg_temp_free_i64(tcg_zero);
+ } else {
+ tcg_gen_add_i64(tcg_src, tcg_src, tcg_rnd);
+ }
+ }
+
+ /* Now do the shift right */
+ if (round && extended_result) {
+ /* extended case, >64 bit precision required */
+ if (ext_lshift == 0) {
+ /* special case, only high bits matter */
+ tcg_gen_mov_i64(tcg_src, tcg_src_hi);
+ } else {
+ tcg_gen_shri_i64(tcg_src, tcg_src, shift);
+ tcg_gen_shli_i64(tcg_src_hi, tcg_src_hi, ext_lshift);
+ tcg_gen_or_i64(tcg_src, tcg_src, tcg_src_hi);
+ }
+ } else {
+ if (is_u) {
+ if (shift == 64) {
+ /* essentially shifting in 64 zeros */
+ tcg_gen_movi_i64(tcg_src, 0);
+ } else {
+ tcg_gen_shri_i64(tcg_src, tcg_src, shift);
+ }
+ } else {
+ if (shift == 64) {
+ /* effectively extending the sign-bit */
+ tcg_gen_sari_i64(tcg_src, tcg_src, 63);
+ } else {
+ tcg_gen_sari_i64(tcg_src, tcg_src, shift);
+ }
+ }
+ }
+
+ if (accumulate) {
+ tcg_gen_add_i64(tcg_res, tcg_res, tcg_src);
+ } else {
+ tcg_gen_mov_i64(tcg_res, tcg_src);
+ }
+
+ if (extended_result) {
+ tcg_temp_free_i64(tcg_src_hi);
+ }
+}
+
+/* Common SHL/SLI - Shift left with an optional insert */
+static void handle_shli_with_ins(TCGv_i64 tcg_res, TCGv_i64 tcg_src,
+ bool insert, int shift)
+{
+ if (insert) { /* SLI */
+ tcg_gen_deposit_i64(tcg_res, tcg_res, tcg_src, shift, 64 - shift);
+ } else { /* SHL */
+ tcg_gen_shli_i64(tcg_res, tcg_src, shift);
+ }
+}
+
+/* SSHR[RA]/USHR[RA] - Scalar shift right (optional rounding/accumulate) */
+static void handle_scalar_simd_shri(DisasContext *s,
+ bool is_u, int immh, int immb,
+ int opcode, int rn, int rd)
+{
+ const int size = 3;
+ int immhb = immh << 3 | immb;
+ int shift = 2 * (8 << size) - immhb;
+ bool accumulate = false;
+ bool round = false;
+ TCGv_i64 tcg_rn;
+ TCGv_i64 tcg_rd;
+ TCGv_i64 tcg_round;
+
+ if (!extract32(immh, 3, 1)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (opcode) {
+ case 0x02: /* SSRA / USRA (accumulate) */
+ accumulate = true;
+ break;
+ case 0x04: /* SRSHR / URSHR (rounding) */
+ round = true;
+ break;
+ case 0x06: /* SRSRA / URSRA (accum + rounding) */
+ accumulate = round = true;
+ break;
+ }
+
+ if (round) {
+ uint64_t round_const = 1ULL << (shift - 1);
+ tcg_round = tcg_const_i64(round_const);
+ } else {
+ TCGV_UNUSED_I64(tcg_round);
+ }
+
+ tcg_rn = read_fp_dreg(s, rn);
+ tcg_rd = accumulate ? read_fp_dreg(s, rd) : tcg_temp_new_i64();
+
+ handle_shri_with_rndacc(tcg_rd, tcg_rn, tcg_round,
+ accumulate, is_u, size, shift);
+
+ write_fp_dreg(s, rd, tcg_rd);
+
+ tcg_temp_free_i64(tcg_rn);
+ tcg_temp_free_i64(tcg_rd);
+ if (round) {
+ tcg_temp_free_i64(tcg_round);
+ }
+}
+
+/* SHL/SLI - Scalar shift left */
+static void handle_scalar_simd_shli(DisasContext *s, bool insert,
+ int immh, int immb, int opcode,
+ int rn, int rd)
+{
+ int size = 32 - clz32(immh) - 1;
+ int immhb = immh << 3 | immb;
+ int shift = immhb - (8 << size);
+ TCGv_i64 tcg_rn = new_tmp_a64(s);
+ TCGv_i64 tcg_rd = new_tmp_a64(s);
+
+ if (!extract32(immh, 3, 1)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_rn = read_fp_dreg(s, rn);
+ tcg_rd = insert ? read_fp_dreg(s, rd) : tcg_temp_new_i64();
+
+ handle_shli_with_ins(tcg_rd, tcg_rn, insert, shift);
+
+ write_fp_dreg(s, rd, tcg_rd);
+
+ tcg_temp_free_i64(tcg_rn);
+ tcg_temp_free_i64(tcg_rd);
+}
+
+/* C3.6.9 AdvSIMD scalar shift by immediate
+ * 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0
+ * +-----+---+-------------+------+------+--------+---+------+------+
+ * | 0 1 | U | 1 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd |
+ * +-----+---+-------------+------+------+--------+---+------+------+
+ *
+ * This is the scalar version so it works on a fixed sized registers
+ */
+static void disas_simd_scalar_shift_imm(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int opcode = extract32(insn, 11, 5);
+ int immb = extract32(insn, 16, 3);
+ int immh = extract32(insn, 19, 4);
+ bool is_u = extract32(insn, 29, 1);
+
+ switch (opcode) {
+ case 0x00: /* SSHR / USHR */
+ case 0x02: /* SSRA / USRA */
+ case 0x04: /* SRSHR / URSHR */
+ case 0x06: /* SRSRA / URSRA */
+ handle_scalar_simd_shri(s, is_u, immh, immb, opcode, rn, rd);
+ break;
+ case 0x0a: /* SHL / SLI */
+ handle_scalar_simd_shli(s, is_u, immh, immb, opcode, rn, rd);
+ break;
+ default:
+ unsupported_encoding(s, insn);
+ break;
+ }
+}
+
+/* C3.6.10 AdvSIMD scalar three different
+ * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0
+ * +-----+---+-----------+------+---+------+--------+-----+------+------+
+ * | 0 1 | U | 1 1 1 1 0 | size | 1 | Rm | opcode | 0 0 | Rn | Rd |
+ * +-----+---+-----------+------+---+------+--------+-----+------+------+
+ */
+static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+static void handle_3same_64(DisasContext *s, int opcode, bool u,
+ TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 tcg_rm)
+{
+ /* Handle 64x64->64 opcodes which are shared between the scalar
+ * and vector 3-same groups. We cover every opcode where size == 3
+ * is valid in either the three-reg-same (integer, not pairwise)
+ * or scalar-three-reg-same groups. (Some opcodes are not yet
+ * implemented.)
+ */
+ TCGCond cond;
+
+ switch (opcode) {
+ case 0x6: /* CMGT, CMHI */
+ /* 64 bit integer comparison, result = test ? (2^64 - 1) : 0.
+ * We implement this using setcond (test) and then negating.
+ */
+ cond = u ? TCG_COND_GTU : TCG_COND_GT;
+ do_cmop:
+ tcg_gen_setcond_i64(cond, tcg_rd, tcg_rn, tcg_rm);
+ tcg_gen_neg_i64(tcg_rd, tcg_rd);
+ break;
+ case 0x7: /* CMGE, CMHS */
+ cond = u ? TCG_COND_GEU : TCG_COND_GE;
+ goto do_cmop;
+ case 0x11: /* CMTST, CMEQ */
+ if (u) {
+ cond = TCG_COND_EQ;
+ goto do_cmop;
+ }
+ /* CMTST : test is "if (X & Y != 0)". */
+ tcg_gen_and_i64(tcg_rd, tcg_rn, tcg_rm);
+ tcg_gen_setcondi_i64(TCG_COND_NE, tcg_rd, tcg_rd, 0);
+ tcg_gen_neg_i64(tcg_rd, tcg_rd);
+ break;
+ case 0x10: /* ADD, SUB */
+ if (u) {
+ tcg_gen_sub_i64(tcg_rd, tcg_rn, tcg_rm);
+ } else {
+ tcg_gen_add_i64(tcg_rd, tcg_rn, tcg_rm);
+ }
+ break;
+ case 0x1: /* SQADD */
+ case 0x5: /* SQSUB */
+ case 0x8: /* SSHL, USHL */
+ case 0x9: /* SQSHL, UQSHL */
+ case 0xa: /* SRSHL, URSHL */
+ case 0xb: /* SQRSHL, UQRSHL */
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/* Handle the 3-same-operands float operations; shared by the scalar
+ * and vector encodings. The caller must filter out any encodings
+ * not allocated for the encoding it is dealing with.
+ */
+static void handle_3same_float(DisasContext *s, int size, int elements,
+ int fpopcode, int rd, int rn, int rm)
+{
+ int pass;
+ TCGv_ptr fpst = get_fpstatus_ptr();
+
+ for (pass = 0; pass < elements; pass++) {
+ if (size) {
+ /* Double */
+ TCGv_i64 tcg_op1 = tcg_temp_new_i64();
+ TCGv_i64 tcg_op2 = tcg_temp_new_i64();
+ TCGv_i64 tcg_res = tcg_temp_new_i64();
+
+ read_vec_element(s, tcg_op1, rn, pass, MO_64);
+ read_vec_element(s, tcg_op2, rm, pass, MO_64);
+
+ switch (fpopcode) {
+ case 0x18: /* FMAXNM */
+ gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x1a: /* FADD */
+ gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x1e: /* FMAX */
+ gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x38: /* FMINNM */
+ gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x3a: /* FSUB */
+ gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x3e: /* FMIN */
+ gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x5b: /* FMUL */
+ gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x5f: /* FDIV */
+ gen_helper_vfp_divd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x7a: /* FABD */
+ gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst);
+ gen_helper_vfp_absd(tcg_res, tcg_res);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ write_vec_element(s, tcg_res, rd, pass, MO_64);
+
+ tcg_temp_free_i64(tcg_res);
+ tcg_temp_free_i64(tcg_op1);
+ tcg_temp_free_i64(tcg_op2);
+ } else {
+ /* Single */
+ TCGv_i32 tcg_op1 = tcg_temp_new_i32();
+ TCGv_i32 tcg_op2 = tcg_temp_new_i32();
+ TCGv_i32 tcg_res = tcg_temp_new_i32();
+
+ read_vec_element_i32(s, tcg_op1, rn, pass, MO_32);
+ read_vec_element_i32(s, tcg_op2, rm, pass, MO_32);
+
+ switch (fpopcode) {
+ case 0x1a: /* FADD */
+ gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x1e: /* FMAX */
+ gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x18: /* FMAXNM */
+ gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x38: /* FMINNM */
+ gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x3a: /* FSUB */
+ gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x3e: /* FMIN */
+ gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x5b: /* FMUL */
+ gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x5f: /* FDIV */
+ gen_helper_vfp_divs(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x7a: /* FABD */
+ gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst);
+ gen_helper_vfp_abss(tcg_res, tcg_res);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (elements == 1) {
+ /* scalar single so clear high part */
+ TCGv_i64 tcg_tmp = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(tcg_tmp, tcg_res);
+ write_vec_element(s, tcg_tmp, rd, pass, MO_64);
+ tcg_temp_free_i64(tcg_tmp);
+ } else {
+ write_vec_element_i32(s, tcg_res, rd, pass, MO_32);
+ }
+
+ tcg_temp_free_i32(tcg_res);
+ tcg_temp_free_i32(tcg_op1);
+ tcg_temp_free_i32(tcg_op2);
+ }
+ }
+
+ tcg_temp_free_ptr(fpst);
+
+ if ((elements << size) < 4) {
+ /* scalar, or non-quad vector op */
+ clear_vec_high(s, rd);
+ }
+}
+
+/* C3.6.11 AdvSIMD scalar three same
+ * 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0
+ * +-----+---+-----------+------+---+------+--------+---+------+------+
+ * | 0 1 | U | 1 1 1 1 0 | size | 1 | Rm | opcode | 1 | Rn | Rd |
+ * +-----+---+-----------+------+---+------+--------+---+------+------+
+ */
+static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int opcode = extract32(insn, 11, 5);
+ int rm = extract32(insn, 16, 5);
+ int size = extract32(insn, 22, 2);
+ bool u = extract32(insn, 29, 1);
+ TCGv_i64 tcg_rn;
+ TCGv_i64 tcg_rm;
+ TCGv_i64 tcg_rd;
+
+ if (opcode >= 0x18) {
+ /* Floating point: U, size[1] and opcode indicate operation */
+ int fpopcode = opcode | (extract32(size, 1, 1) << 5) | (u << 6);
+ switch (fpopcode) {
+ case 0x1b: /* FMULX */
+ case 0x1c: /* FCMEQ */
+ case 0x1f: /* FRECPS */
+ case 0x3f: /* FRSQRTS */
+ case 0x5c: /* FCMGE */
+ case 0x5d: /* FACGE */
+ case 0x7c: /* FCMGT */
+ case 0x7d: /* FACGT */
+ unsupported_encoding(s, insn);
+ return;
+ case 0x7a: /* FABD */
+ break;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+
+ handle_3same_float(s, extract32(size, 0, 1), 1, fpopcode, rd, rn, rm);
+ return;
+ }
+
+ switch (opcode) {
+ case 0x1: /* SQADD, UQADD */
+ case 0x5: /* SQSUB, UQSUB */
+ case 0x8: /* SSHL, USHL */
+ case 0xa: /* SRSHL, URSHL */
+ unsupported_encoding(s, insn);
+ return;
+ case 0x6: /* CMGT, CMHI */
+ case 0x7: /* CMGE, CMHS */
+ case 0x11: /* CMTST, CMEQ */
+ case 0x10: /* ADD, SUB (vector) */
+ if (size != 3) {
+ unallocated_encoding(s);
+ return;
+ }
+ break;
+ case 0x9: /* SQSHL, UQSHL */
+ case 0xb: /* SQRSHL, UQRSHL */
+ unsupported_encoding(s, insn);
+ return;
+ case 0x16: /* SQDMULH, SQRDMULH (vector) */
+ if (size != 1 && size != 2) {
+ unallocated_encoding(s);
+ return;
+ }
+ unsupported_encoding(s, insn);
+ return;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_rn = read_fp_dreg(s, rn); /* op1 */
+ tcg_rm = read_fp_dreg(s, rm); /* op2 */
+ tcg_rd = tcg_temp_new_i64();
+
+ /* For the moment we only support the opcodes which are
+ * 64-bit-width only. The size != 3 cases will
+ * be handled later when the relevant ops are implemented.
+ */
+ handle_3same_64(s, opcode, u, tcg_rd, tcg_rn, tcg_rm);
+
+ write_fp_dreg(s, rd, tcg_rd);
+
+ tcg_temp_free_i64(tcg_rn);
+ tcg_temp_free_i64(tcg_rm);
+ tcg_temp_free_i64(tcg_rd);
+}
+
+/* C3.6.12 AdvSIMD scalar two reg misc
+ * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0
+ * +-----+---+-----------+------+-----------+--------+-----+------+------+
+ * | 0 1 | U | 1 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd |
+ * +-----+---+-----------+------+-----------+--------+-----+------+------+
+ */
+static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* C3.6.13 AdvSIMD scalar x indexed element
+ * 31 30 29 28 24 23 22 21 20 19 16 15 12 11 10 9 5 4 0
+ * +-----+---+-----------+------+---+---+------+-----+---+---+------+------+
+ * | 0 1 | U | 1 1 1 1 1 | size | L | M | Rm | opc | H | 0 | Rn | Rd |
+ * +-----+---+-----------+------+---+---+------+-----+---+---+------+------+
+ */
+static void disas_simd_scalar_indexed(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* SSHR[RA]/USHR[RA] - Vector shift right (optional rounding/accumulate) */
+static void handle_vec_simd_shri(DisasContext *s, bool is_q, bool is_u,
+ int immh, int immb, int opcode, int rn, int rd)
+{
+ int size = 32 - clz32(immh) - 1;
+ int immhb = immh << 3 | immb;
+ int shift = 2 * (8 << size) - immhb;
+ bool accumulate = false;
+ bool round = false;
+ int dsize = is_q ? 128 : 64;
+ int esize = 8 << size;
+ int elements = dsize/esize;
+ TCGMemOp memop = size | (is_u ? 0 : MO_SIGN);
+ TCGv_i64 tcg_rn = new_tmp_a64(s);
+ TCGv_i64 tcg_rd = new_tmp_a64(s);
+ TCGv_i64 tcg_round;
+ int i;
+
+ if (extract32(immh, 3, 1) && !is_q) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (size > 3 && !is_q) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (opcode) {
+ case 0x02: /* SSRA / USRA (accumulate) */
+ accumulate = true;
+ break;
+ case 0x04: /* SRSHR / URSHR (rounding) */
+ round = true;
+ break;
+ case 0x06: /* SRSRA / URSRA (accum + rounding) */
+ accumulate = round = true;
+ break;
+ }
+
+ if (round) {
+ uint64_t round_const = 1ULL << (shift - 1);
+ tcg_round = tcg_const_i64(round_const);
+ } else {
+ TCGV_UNUSED_I64(tcg_round);
+ }
+
+ for (i = 0; i < elements; i++) {
+ read_vec_element(s, tcg_rn, rn, i, memop);
+ if (accumulate) {
+ read_vec_element(s, tcg_rd, rd, i, memop);
+ }
+
+ handle_shri_with_rndacc(tcg_rd, tcg_rn, tcg_round,
+ accumulate, is_u, size, shift);
+
+ write_vec_element(s, tcg_rd, rd, i, size);
+ }
+
+ if (!is_q) {
+ clear_vec_high(s, rd);
+ }
+
+ if (round) {
+ tcg_temp_free_i64(tcg_round);
+ }
+}
+
+/* SHL/SLI - Vector shift left */
+static void handle_vec_simd_shli(DisasContext *s, bool is_q, bool insert,
+ int immh, int immb, int opcode, int rn, int rd)
+{
+ int size = 32 - clz32(immh) - 1;
+ int immhb = immh << 3 | immb;
+ int shift = immhb - (8 << size);
+ int dsize = is_q ? 128 : 64;
+ int esize = 8 << size;
+ int elements = dsize/esize;
+ TCGv_i64 tcg_rn = new_tmp_a64(s);
+ TCGv_i64 tcg_rd = new_tmp_a64(s);
+ int i;
+
+ if (extract32(immh, 3, 1) && !is_q) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (size > 3 && !is_q) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ for (i = 0; i < elements; i++) {
+ read_vec_element(s, tcg_rn, rn, i, size);
+ if (insert) {
+ read_vec_element(s, tcg_rd, rd, i, size);
+ }
+
+ handle_shli_with_ins(tcg_rd, tcg_rn, insert, shift);
+
+ write_vec_element(s, tcg_rd, rd, i, size);
+ }
+
+ if (!is_q) {
+ clear_vec_high(s, rd);
+ }
+}
+
+/* USHLL/SHLL - Vector shift left with widening */
+static void handle_vec_simd_wshli(DisasContext *s, bool is_q, bool is_u,
+ int immh, int immb, int opcode, int rn, int rd)
+{
+ int size = 32 - clz32(immh) - 1;
+ int immhb = immh << 3 | immb;
+ int shift = immhb - (8 << size);
+ int dsize = 64;
+ int esize = 8 << size;
+ int elements = dsize/esize;
+ TCGv_i64 tcg_rn = new_tmp_a64(s);
+ TCGv_i64 tcg_rd = new_tmp_a64(s);
+ int i;
+
+ if (size >= 3) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ /* For the LL variants the store is larger than the load,
+ * so if rd == rn we would overwrite parts of our input.
+ * So load everything right now and use shifts in the main loop.
+ */
+ read_vec_element(s, tcg_rn, rn, is_q ? 1 : 0, MO_64);
+
+ for (i = 0; i < elements; i++) {
+ tcg_gen_shri_i64(tcg_rd, tcg_rn, i * esize);
+ ext_and_shift_reg(tcg_rd, tcg_rd, size | (!is_u << 2), 0);
+ tcg_gen_shli_i64(tcg_rd, tcg_rd, shift);
+ write_vec_element(s, tcg_rd, rd, i, size + 1);
+ }
+}
+
+
+/* C3.6.14 AdvSIMD shift by immediate
+ * 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0
+ * +---+---+---+-------------+------+------+--------+---+------+------+
+ * | 0 | Q | U | 0 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd |
+ * +---+---+---+-------------+------+------+--------+---+------+------+
+ */
+static void disas_simd_shift_imm(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int opcode = extract32(insn, 11, 5);
+ int immb = extract32(insn, 16, 3);
+ int immh = extract32(insn, 19, 4);
+ bool is_u = extract32(insn, 29, 1);
+ bool is_q = extract32(insn, 30, 1);
+
+ switch (opcode) {
+ case 0x00: /* SSHR / USHR */
+ case 0x02: /* SSRA / USRA (accumulate) */
+ case 0x04: /* SRSHR / URSHR (rounding) */
+ case 0x06: /* SRSRA / URSRA (accum + rounding) */
+ handle_vec_simd_shri(s, is_q, is_u, immh, immb, opcode, rn, rd);
+ break;
+ case 0x0a: /* SHL / SLI */
+ handle_vec_simd_shli(s, is_q, is_u, immh, immb, opcode, rn, rd);
+ break;
+ case 0x14: /* SSHLL / USHLL */
+ handle_vec_simd_wshli(s, is_q, is_u, immh, immb, opcode, rn, rd);
+ break;
+ default:
+ /* We don't currently implement any of the Narrow or saturating shifts;
+ * nor do we implement the fixed-point conversions in this
+ * encoding group (SCVTF, FCVTZS, UCVTF, FCVTZU).
+ */
+ unsupported_encoding(s, insn);
+ return;
+ }
+}
+
+static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size,
+ int opcode, int rd, int rn, int rm)
+{
+ /* 3-reg-different widening insns: 64 x 64 -> 128 */
+ TCGv_i64 tcg_res[2];
+ int pass, accop;
+
+ tcg_res[0] = tcg_temp_new_i64();
+ tcg_res[1] = tcg_temp_new_i64();
+
+ /* Does this op do an adding accumulate, a subtracting accumulate,
+ * or no accumulate at all?
+ */
+ switch (opcode) {
+ case 5:
+ case 8:
+ case 9:
+ accop = 1;
+ break;
+ case 10:
+ case 11:
+ accop = -1;
+ break;
+ default:
+ accop = 0;
+ break;
+ }
+
+ if (accop != 0) {
+ read_vec_element(s, tcg_res[0], rd, 0, MO_64);
+ read_vec_element(s, tcg_res[1], rd, 1, MO_64);
+ }
+
+ /* size == 2 means two 32x32->64 operations; this is worth special
+ * casing because we can generally handle it inline.
+ */
+ if (size == 2) {
+ for (pass = 0; pass < 2; pass++) {
+ TCGv_i64 tcg_op1 = tcg_temp_new_i64();
+ TCGv_i64 tcg_op2 = tcg_temp_new_i64();
+ TCGv_i64 tcg_passres;
+ TCGMemOp memop = MO_32 | (is_u ? 0 : MO_SIGN);
+
+ int elt = pass + is_q * 2;
+
+ read_vec_element(s, tcg_op1, rn, elt, memop);
+ read_vec_element(s, tcg_op2, rm, elt, memop);
+
+ if (accop == 0) {
+ tcg_passres = tcg_res[pass];
+ } else {
+ tcg_passres = tcg_temp_new_i64();
+ }
+
+ switch (opcode) {
+ case 5: /* SABAL, SABAL2, UABAL, UABAL2 */
+ case 7: /* SABDL, SABDL2, UABDL, UABDL2 */
+ {
+ TCGv_i64 tcg_tmp1 = tcg_temp_new_i64();
+ TCGv_i64 tcg_tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_sub_i64(tcg_tmp1, tcg_op1, tcg_op2);
+ tcg_gen_sub_i64(tcg_tmp2, tcg_op2, tcg_op1);
+ tcg_gen_movcond_i64(is_u ? TCG_COND_GEU : TCG_COND_GE,
+ tcg_passres,
+ tcg_op1, tcg_op2, tcg_tmp1, tcg_tmp2);
+ tcg_temp_free_i64(tcg_tmp1);
+ tcg_temp_free_i64(tcg_tmp2);
+ break;
+ }
+ case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */
+ case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */
+ case 12: /* UMULL, UMULL2, SMULL, SMULL2 */
+ tcg_gen_mul_i64(tcg_passres, tcg_op1, tcg_op2);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (accop > 0) {
+ tcg_gen_add_i64(tcg_res[pass], tcg_res[pass], tcg_passres);
+ tcg_temp_free_i64(tcg_passres);
+ } else if (accop < 0) {
+ tcg_gen_sub_i64(tcg_res[pass], tcg_res[pass], tcg_passres);
+ tcg_temp_free_i64(tcg_passres);
+ }
+
+ tcg_temp_free_i64(tcg_op1);
+ tcg_temp_free_i64(tcg_op2);
+ }
+ } else {
+ /* size 0 or 1, generally helper functions */
+ for (pass = 0; pass < 2; pass++) {
+ TCGv_i32 tcg_op1 = tcg_temp_new_i32();
+ TCGv_i32 tcg_op2 = tcg_temp_new_i32();
+ TCGv_i64 tcg_passres;
+ int elt = pass + is_q * 2;
+
+ read_vec_element_i32(s, tcg_op1, rn, elt, MO_32);
+ read_vec_element_i32(s, tcg_op2, rm, elt, MO_32);
+
+ if (accop == 0) {
+ tcg_passres = tcg_res[pass];
+ } else {
+ tcg_passres = tcg_temp_new_i64();
+ }
+
+ switch (opcode) {
+ case 5: /* SABAL, SABAL2, UABAL, UABAL2 */
+ case 7: /* SABDL, SABDL2, UABDL, UABDL2 */
+ if (size == 0) {
+ if (is_u) {
+ gen_helper_neon_abdl_u16(tcg_passres, tcg_op1, tcg_op2);
+ } else {
+ gen_helper_neon_abdl_s16(tcg_passres, tcg_op1, tcg_op2);
+ }
+ } else {
+ if (is_u) {
+ gen_helper_neon_abdl_u32(tcg_passres, tcg_op1, tcg_op2);
+ } else {
+ gen_helper_neon_abdl_s32(tcg_passres, tcg_op1, tcg_op2);
+ }
+ }
+ break;
+ case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */
+ case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */
+ case 12: /* UMULL, UMULL2, SMULL, SMULL2 */
+ if (size == 0) {
+ if (is_u) {
+ gen_helper_neon_mull_u8(tcg_passres, tcg_op1, tcg_op2);
+ } else {
+ gen_helper_neon_mull_s8(tcg_passres, tcg_op1, tcg_op2);
+ }
+ } else {
+ if (is_u) {
+ gen_helper_neon_mull_u16(tcg_passres, tcg_op1, tcg_op2);
+ } else {
+ gen_helper_neon_mull_s16(tcg_passres, tcg_op1, tcg_op2);
+ }
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ tcg_temp_free_i32(tcg_op1);
+ tcg_temp_free_i32(tcg_op2);
+
+ if (accop > 0) {
+ if (size == 0) {
+ gen_helper_neon_addl_u16(tcg_res[pass], tcg_res[pass],
+ tcg_passres);
+ } else {
+ gen_helper_neon_addl_u32(tcg_res[pass], tcg_res[pass],
+ tcg_passres);
+ }
+ tcg_temp_free_i64(tcg_passres);
+ } else if (accop < 0) {
+ if (size == 0) {
+ gen_helper_neon_subl_u16(tcg_res[pass], tcg_res[pass],
+ tcg_passres);
+ } else {
+ gen_helper_neon_subl_u32(tcg_res[pass], tcg_res[pass],
+ tcg_passres);
+ }
+ tcg_temp_free_i64(tcg_passres);
+ }
+ }
+ }
+
+ write_vec_element(s, tcg_res[0], rd, 0, MO_64);
+ write_vec_element(s, tcg_res[1], rd, 1, MO_64);
+ tcg_temp_free_i64(tcg_res[0]);
+ tcg_temp_free_i64(tcg_res[1]);
+}
+
+/* C3.6.15 AdvSIMD three different
+ * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0
+ * +---+---+---+-----------+------+---+------+--------+-----+------+------+
+ * | 0 | Q | U | 0 1 1 1 0 | size | 1 | Rm | opcode | 0 0 | Rn | Rd |
+ * +---+---+---+-----------+------+---+------+--------+-----+------+------+
+ */
+static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn)
+{
+ /* Instructions in this group fall into three basic classes
+ * (in each case with the operation working on each element in
+ * the input vectors):
+ * (1) widening 64 x 64 -> 128 (with possibly Vd as an extra
+ * 128 bit input)
+ * (2) wide 64 x 128 -> 128
+ * (3) narrowing 128 x 128 -> 64
+ * Here we do initial decode, catch unallocated cases and
+ * dispatch to separate functions for each class.
+ */
+ int is_q = extract32(insn, 30, 1);
+ int is_u = extract32(insn, 29, 1);
+ int size = extract32(insn, 22, 2);
+ int opcode = extract32(insn, 12, 4);
+ int rm = extract32(insn, 16, 5);
+ int rn = extract32(insn, 5, 5);
+ int rd = extract32(insn, 0, 5);
+
+ switch (opcode) {
+ case 1: /* SADDW, SADDW2, UADDW, UADDW2 */
+ case 3: /* SSUBW, SSUBW2, USUBW, USUBW2 */
+ /* 64 x 128 -> 128 */
+ unsupported_encoding(s, insn);
+ break;
+ case 4: /* ADDHN, ADDHN2, RADDHN, RADDHN2 */
+ case 6: /* SUBHN, SUBHN2, RSUBHN, RSUBHN2 */
+ /* 128 x 128 -> 64 */
+ unsupported_encoding(s, insn);
+ break;
+ case 9:
+ case 11:
+ case 13:
+ case 14:
+ if (is_u) {
+ unallocated_encoding(s);
+ return;
+ }
+ /* fall through */
+ case 0:
+ case 2:
+ unsupported_encoding(s, insn);
+ break;
+ case 5:
+ case 7:
+ case 8:
+ case 10:
+ case 12:
+ /* 64 x 64 -> 128 */
+ if (size == 3) {
+ unallocated_encoding(s);
+ return;
+ }
+ handle_3rd_widening(s, is_q, is_u, size, opcode, rd, rn, rm);
+ break;
+ default:
+ /* opcode 15 not allocated */
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* Logic op (opcode == 3) subgroup of C3.6.16. */
+static void disas_simd_3same_logic(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int rm = extract32(insn, 16, 5);
+ int size = extract32(insn, 22, 2);
+ bool is_u = extract32(insn, 29, 1);
+ bool is_q = extract32(insn, 30, 1);
+ TCGv_i64 tcg_op1 = tcg_temp_new_i64();
+ TCGv_i64 tcg_op2 = tcg_temp_new_i64();
+ TCGv_i64 tcg_res[2];
+ int pass;
+
+ tcg_res[0] = tcg_temp_new_i64();
+ tcg_res[1] = tcg_temp_new_i64();
+
+ for (pass = 0; pass < (is_q ? 2 : 1); pass++) {
+ read_vec_element(s, tcg_op1, rn, pass, MO_64);
+ read_vec_element(s, tcg_op2, rm, pass, MO_64);
+
+ if (!is_u) {
+ switch (size) {
+ case 0: /* AND */
+ tcg_gen_and_i64(tcg_res[pass], tcg_op1, tcg_op2);
+ break;
+ case 1: /* BIC */
+ tcg_gen_andc_i64(tcg_res[pass], tcg_op1, tcg_op2);
+ break;
+ case 2: /* ORR */
+ tcg_gen_or_i64(tcg_res[pass], tcg_op1, tcg_op2);
+ break;
+ case 3: /* ORN */
+ tcg_gen_orc_i64(tcg_res[pass], tcg_op1, tcg_op2);
+ break;
+ }
+ } else {
+ if (size != 0) {
+ /* B* ops need res loaded to operate on */
+ read_vec_element(s, tcg_res[pass], rd, pass, MO_64);
+ }
+
+ switch (size) {
+ case 0: /* EOR */
+ tcg_gen_xor_i64(tcg_res[pass], tcg_op1, tcg_op2);
+ break;
+ case 1: /* BSL bitwise select */
+ tcg_gen_xor_i64(tcg_op1, tcg_op1, tcg_op2);
+ tcg_gen_and_i64(tcg_op1, tcg_op1, tcg_res[pass]);
+ tcg_gen_xor_i64(tcg_res[pass], tcg_op2, tcg_op1);
+ break;
+ case 2: /* BIT, bitwise insert if true */
+ tcg_gen_xor_i64(tcg_op1, tcg_op1, tcg_res[pass]);
+ tcg_gen_and_i64(tcg_op1, tcg_op1, tcg_op2);
+ tcg_gen_xor_i64(tcg_res[pass], tcg_res[pass], tcg_op1);
+ break;
+ case 3: /* BIF, bitwise insert if false */
+ tcg_gen_xor_i64(tcg_op1, tcg_op1, tcg_res[pass]);
+ tcg_gen_andc_i64(tcg_op1, tcg_op1, tcg_op2);
+ tcg_gen_xor_i64(tcg_res[pass], tcg_res[pass], tcg_op1);
+ break;
+ }
+ }
+ }
+
+ write_vec_element(s, tcg_res[0], rd, 0, MO_64);
+ if (!is_q) {
+ tcg_gen_movi_i64(tcg_res[1], 0);
+ }
+ write_vec_element(s, tcg_res[1], rd, 1, MO_64);
+
+ tcg_temp_free_i64(tcg_op1);
+ tcg_temp_free_i64(tcg_op2);
+ tcg_temp_free_i64(tcg_res[0]);
+ tcg_temp_free_i64(tcg_res[1]);
+}
+
+/* Pairwise op subgroup of C3.6.16. */
+static void disas_simd_3same_pair(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* Floating point op subgroup of C3.6.16. */
+static void disas_simd_3same_float(DisasContext *s, uint32_t insn)
+{
+ /* For floating point ops, the U, size[1] and opcode bits
+ * together indicate the operation. size[0] indicates single
+ * or double.
+ */
+ int fpopcode = extract32(insn, 11, 5)
+ | (extract32(insn, 23, 1) << 5)
+ | (extract32(insn, 29, 1) << 6);
+ int is_q = extract32(insn, 30, 1);
+ int size = extract32(insn, 22, 1);
+ int rm = extract32(insn, 16, 5);
+ int rn = extract32(insn, 5, 5);
+ int rd = extract32(insn, 0, 5);
+
+ int datasize = is_q ? 128 : 64;
+ int esize = 32 << size;
+ int elements = datasize / esize;
+
+ if (size == 1 && !is_q) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (fpopcode) {
+ case 0x58: /* FMAXNMP */
+ case 0x5a: /* FADDP */
+ case 0x5e: /* FMAXP */
+ case 0x78: /* FMINNMP */
+ case 0x7e: /* FMINP */
+ /* pairwise ops */
+ unsupported_encoding(s, insn);
+ return;
+ case 0x1b: /* FMULX */
+ case 0x1c: /* FCMEQ */
+ case 0x1f: /* FRECPS */
+ case 0x3f: /* FRSQRTS */
+ case 0x5c: /* FCMGE */
+ case 0x5d: /* FACGE */
+ case 0x7c: /* FCMGT */
+ case 0x7d: /* FACGT */
+ case 0x19: /* FMLA */
+ case 0x39: /* FMLS */
+ unsupported_encoding(s, insn);
+ return;
+ case 0x18: /* FMAXNM */
+ case 0x1a: /* FADD */
+ case 0x1e: /* FMAX */
+ case 0x38: /* FMINNM */
+ case 0x3a: /* FSUB */
+ case 0x3e: /* FMIN */
+ case 0x5b: /* FMUL */
+ case 0x5f: /* FDIV */
+ case 0x7a: /* FABD */
+ handle_3same_float(s, size, elements, fpopcode, rd, rn, rm);
+ return;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+}
+
+/* Integer op subgroup of C3.6.16. */
+static void disas_simd_3same_int(DisasContext *s, uint32_t insn)
+{
+ int is_q = extract32(insn, 30, 1);
+ int u = extract32(insn, 29, 1);
+ int size = extract32(insn, 22, 2);
+ int opcode = extract32(insn, 11, 5);
+ int rm = extract32(insn, 16, 5);
+ int rn = extract32(insn, 5, 5);
+ int rd = extract32(insn, 0, 5);
+ int pass;
+
+ switch (opcode) {
+ case 0x13: /* MUL, PMUL */
+ if (u && size != 0) {
+ unallocated_encoding(s);
+ return;
+ }
+ /* fall through */
+ case 0x0: /* SHADD, UHADD */
+ case 0x2: /* SRHADD, URHADD */
+ case 0x4: /* SHSUB, UHSUB */
+ case 0xc: /* SMAX, UMAX */
+ case 0xd: /* SMIN, UMIN */
+ case 0xe: /* SABD, UABD */
+ case 0xf: /* SABA, UABA */
+ case 0x12: /* MLA, MLS */
+ if (size == 3) {
+ unallocated_encoding(s);
+ return;
+ }
+ unsupported_encoding(s, insn);
+ return;
+ case 0x1: /* SQADD */
+ case 0x5: /* SQSUB */
+ case 0x8: /* SSHL, USHL */
+ case 0x9: /* SQSHL, UQSHL */
+ case 0xa: /* SRSHL, URSHL */
+ case 0xb: /* SQRSHL, UQRSHL */
+ if (size == 3 && !is_q) {
+ unallocated_encoding(s);
+ return;
+ }
+ unsupported_encoding(s, insn);
+ return;
+ case 0x16: /* SQDMULH, SQRDMULH */
+ if (size == 0 || size == 3) {
+ unallocated_encoding(s);
+ return;
+ }
+ unsupported_encoding(s, insn);
+ return;
+ default:
+ if (size == 3 && !is_q) {
+ unallocated_encoding(s);
+ return;
+ }
+ break;
+ }
+
+ if (size == 3) {
+ for (pass = 0; pass < (is_q ? 2 : 1); pass++) {
+ TCGv_i64 tcg_op1 = tcg_temp_new_i64();
+ TCGv_i64 tcg_op2 = tcg_temp_new_i64();
+ TCGv_i64 tcg_res = tcg_temp_new_i64();
+
+ read_vec_element(s, tcg_op1, rn, pass, MO_64);
+ read_vec_element(s, tcg_op2, rm, pass, MO_64);
+
+ handle_3same_64(s, opcode, u, tcg_res, tcg_op1, tcg_op2);
+
+ write_vec_element(s, tcg_res, rd, pass, MO_64);
+
+ tcg_temp_free_i64(tcg_res);
+ tcg_temp_free_i64(tcg_op1);
+ tcg_temp_free_i64(tcg_op2);
+ }
+ } else {
+ for (pass = 0; pass < (is_q ? 4 : 2); pass++) {
+ TCGv_i32 tcg_op1 = tcg_temp_new_i32();
+ TCGv_i32 tcg_op2 = tcg_temp_new_i32();
+ TCGv_i32 tcg_res = tcg_temp_new_i32();
+ NeonGenTwoOpFn *genfn;
+
+ read_vec_element_i32(s, tcg_op1, rn, pass, MO_32);
+ read_vec_element_i32(s, tcg_op2, rm, pass, MO_32);
+
+ switch (opcode) {
+ case 0x6: /* CMGT, CMHI */
+ {
+ static NeonGenTwoOpFn * const fns[3][2] = {
+ { gen_helper_neon_cgt_s8, gen_helper_neon_cgt_u8 },
+ { gen_helper_neon_cgt_s16, gen_helper_neon_cgt_u16 },
+ { gen_helper_neon_cgt_s32, gen_helper_neon_cgt_u32 },
+ };
+ genfn = fns[size][u];
+ break;
+ }
+ case 0x7: /* CMGE, CMHS */
+ {
+ static NeonGenTwoOpFn * const fns[3][2] = {
+ { gen_helper_neon_cge_s8, gen_helper_neon_cge_u8 },
+ { gen_helper_neon_cge_s16, gen_helper_neon_cge_u16 },
+ { gen_helper_neon_cge_s32, gen_helper_neon_cge_u32 },
+ };
+ genfn = fns[size][u];
+ break;
+ }
+ case 0x10: /* ADD, SUB */
+ {
+ static NeonGenTwoOpFn * const fns[3][2] = {
+ { gen_helper_neon_add_u8, gen_helper_neon_sub_u8 },
+ { gen_helper_neon_add_u16, gen_helper_neon_sub_u16 },
+ { tcg_gen_add_i32, tcg_gen_sub_i32 },
+ };
+ genfn = fns[size][u];
+ break;
+ }
+ case 0x11: /* CMTST, CMEQ */
+ {
+ static NeonGenTwoOpFn * const fns[3][2] = {
+ { gen_helper_neon_tst_u8, gen_helper_neon_ceq_u8 },
+ { gen_helper_neon_tst_u16, gen_helper_neon_ceq_u16 },
+ { gen_helper_neon_tst_u32, gen_helper_neon_ceq_u32 },
+ };
+ genfn = fns[size][u];
+ break;
+ }
+ default:
+ g_assert_not_reached();
+ }
+
+ genfn(tcg_res, tcg_op1, tcg_op2);
+
+ write_vec_element_i32(s, tcg_res, rd, pass, MO_32);
+
+ tcg_temp_free_i32(tcg_res);
+ tcg_temp_free_i32(tcg_op1);
+ tcg_temp_free_i32(tcg_op2);
+ }
+ }
+
+ if (!is_q) {
+ clear_vec_high(s, rd);
+ }
+}
+
+/* C3.6.16 AdvSIMD three same
+ * 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0
+ * +---+---+---+-----------+------+---+------+--------+---+------+------+
+ * | 0 | Q | U | 0 1 1 1 0 | size | 1 | Rm | opcode | 1 | Rn | Rd |
+ * +---+---+---+-----------+------+---+------+--------+---+------+------+
+ */
+static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn)
+{
+ int opcode = extract32(insn, 11, 5);
+
+ switch (opcode) {
+ case 0x3: /* logic ops */
+ disas_simd_3same_logic(s, insn);
+ break;
+ case 0x17: /* ADDP */
+ case 0x14: /* SMAXP, UMAXP */
+ case 0x15: /* SMINP, UMINP */
+ /* Pairwise operations */
+ disas_simd_3same_pair(s, insn);
+ break;
+ case 0x18 ... 0x31:
+ /* floating point ops, sz[1] and U are part of opcode */
+ disas_simd_3same_float(s, insn);
+ break;
+ default:
+ disas_simd_3same_int(s, insn);
+ break;
+ }
+}
+
+/* C3.6.17 AdvSIMD two reg misc
+ * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0
+ * +---+---+---+-----------+------+-----------+--------+-----+------+------+
+ * | 0 | Q | U | 0 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd |
+ * +---+---+---+-----------+------+-----------+--------+-----+------+------+
+ */
+static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* C3.6.18 AdvSIMD vector x indexed element
+ * 31 30 29 28 24 23 22 21 20 19 16 15 12 11 10 9 5 4 0
+ * +---+---+---+-----------+------+---+---+------+-----+---+---+------+------+
+ * | 0 | Q | U | 0 1 1 1 1 | size | L | M | Rm | opc | H | 0 | Rn | Rd |
+ * +---+---+---+-----------+------+---+---+------+-----+---+---+------+------+
+ */
+static void disas_simd_indexed_vector(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* C3.6.19 Crypto AES
+ * 31 24 23 22 21 17 16 12 11 10 9 5 4 0
+ * +-----------------+------+-----------+--------+-----+------+------+
+ * | 0 1 0 0 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd |
+ * +-----------------+------+-----------+--------+-----+------+------+
+ */
+static void disas_crypto_aes(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* C3.6.20 Crypto three-reg SHA
+ * 31 24 23 22 21 20 16 15 14 12 11 10 9 5 4 0
+ * +-----------------+------+---+------+---+--------+-----+------+------+
+ * | 0 1 0 1 1 1 1 0 | size | 0 | Rm | 0 | opcode | 0 0 | Rn | Rd |
+ * +-----------------+------+---+------+---+--------+-----+------+------+
+ */
+static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* C3.6.21 Crypto two-reg SHA
+ * 31 24 23 22 21 17 16 12 11 10 9 5 4 0
+ * +-----------------+------+-----------+--------+-----+------+------+
+ * | 0 1 0 1 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd |
+ * +-----------------+------+-----------+--------+-----+------+------+
+ */
+static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* C3.6 Data processing - SIMD, inc Crypto
+ *
+ * As the decode gets a little complex we are using a table based
+ * approach for this part of the decode.
+ */
+static const AArch64DecodeTable data_proc_simd[] = {
+ /* pattern , mask , fn */
+ { 0x0e200400, 0x9f200400, disas_simd_three_reg_same },
+ { 0x0e200000, 0x9f200c00, disas_simd_three_reg_diff },
+ { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc },
+ { 0x0e300800, 0x9f3e0c00, disas_simd_across_lanes },
+ { 0x0e000400, 0x9fe08400, disas_simd_copy },
+ { 0x0f000000, 0x9f000400, disas_simd_indexed_vector },
+ /* simd_mod_imm decode is a subset of simd_shift_imm, so must precede it */
+ { 0x0f000400, 0x9ff80400, disas_simd_mod_imm },
+ { 0x0f000400, 0x9f800400, disas_simd_shift_imm },
+ { 0x0e000000, 0xbf208c00, disas_simd_tb },
+ { 0x0e000800, 0xbf208c00, disas_simd_zip_trn },
+ { 0x2e000000, 0xbf208400, disas_simd_ext },
+ { 0x5e200400, 0xdf200400, disas_simd_scalar_three_reg_same },
+ { 0x5e200000, 0xdf200c00, disas_simd_scalar_three_reg_diff },
+ { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc },
+ { 0x5e300800, 0xdf3e0c00, disas_simd_scalar_pairwise },
+ { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy },
+ { 0x5f000000, 0xdf000400, disas_simd_scalar_indexed },
+ { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm },
+ { 0x4e280800, 0xff3e0c00, disas_crypto_aes },
+ { 0x5e000000, 0xff208c00, disas_crypto_three_reg_sha },
+ { 0x5e280800, 0xff3e0c00, disas_crypto_two_reg_sha },
+ { 0x00000000, 0x00000000, NULL }
+};
+
static void disas_data_proc_simd(DisasContext *s, uint32_t insn)
{
/* Note that this is called with all non-FP cases from
* table C3-6 so it must UNDEF for entries not specifically
* allocated to instructions in that table.
*/
- unsupported_encoding(s, insn);
+ AArch64DecodeFn *fn = lookup_disas_fn(&data_proc_simd[0], insn);
+ if (fn) {
+ fn(s, insn);
+ } else {
+ unallocated_encoding(s);
+ }
}
/* C3.6 Data processing - SIMD and floating point */
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 8d240e160d..e701c0f9e1 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -2759,6 +2759,113 @@ static int handle_vminmaxnm(uint32_t insn, uint32_t rd, uint32_t rn,
return 0;
}
+static int handle_vrint(uint32_t insn, uint32_t rd, uint32_t rm, uint32_t dp,
+ int rounding)
+{
+ TCGv_ptr fpst = get_fpstatus_ptr(0);
+ TCGv_i32 tcg_rmode;
+
+ tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rounding));
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+
+ if (dp) {
+ TCGv_i64 tcg_op;
+ TCGv_i64 tcg_res;
+ tcg_op = tcg_temp_new_i64();
+ tcg_res = tcg_temp_new_i64();
+ tcg_gen_ld_f64(tcg_op, cpu_env, vfp_reg_offset(dp, rm));
+ gen_helper_rintd(tcg_res, tcg_op, fpst);
+ tcg_gen_st_f64(tcg_res, cpu_env, vfp_reg_offset(dp, rd));
+ tcg_temp_free_i64(tcg_op);
+ tcg_temp_free_i64(tcg_res);
+ } else {
+ TCGv_i32 tcg_op;
+ TCGv_i32 tcg_res;
+ tcg_op = tcg_temp_new_i32();
+ tcg_res = tcg_temp_new_i32();
+ tcg_gen_ld_f32(tcg_op, cpu_env, vfp_reg_offset(dp, rm));
+ gen_helper_rints(tcg_res, tcg_op, fpst);
+ tcg_gen_st_f32(tcg_res, cpu_env, vfp_reg_offset(dp, rd));
+ tcg_temp_free_i32(tcg_op);
+ tcg_temp_free_i32(tcg_res);
+ }
+
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ tcg_temp_free_i32(tcg_rmode);
+
+ tcg_temp_free_ptr(fpst);
+ return 0;
+}
+
+static int handle_vcvt(uint32_t insn, uint32_t rd, uint32_t rm, uint32_t dp,
+ int rounding)
+{
+ bool is_signed = extract32(insn, 7, 1);
+ TCGv_ptr fpst = get_fpstatus_ptr(0);
+ TCGv_i32 tcg_rmode, tcg_shift;
+
+ tcg_shift = tcg_const_i32(0);
+
+ tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rounding));
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+
+ if (dp) {
+ TCGv_i64 tcg_double, tcg_res;
+ TCGv_i32 tcg_tmp;
+ /* Rd is encoded as a single precision register even when the source
+ * is double precision.
+ */
+ rd = ((rd << 1) & 0x1e) | ((rd >> 4) & 0x1);
+ tcg_double = tcg_temp_new_i64();
+ tcg_res = tcg_temp_new_i64();
+ tcg_tmp = tcg_temp_new_i32();
+ tcg_gen_ld_f64(tcg_double, cpu_env, vfp_reg_offset(1, rm));
+ if (is_signed) {
+ gen_helper_vfp_tosld(tcg_res, tcg_double, tcg_shift, fpst);
+ } else {
+ gen_helper_vfp_tould(tcg_res, tcg_double, tcg_shift, fpst);
+ }
+ tcg_gen_trunc_i64_i32(tcg_tmp, tcg_res);
+ tcg_gen_st_f32(tcg_tmp, cpu_env, vfp_reg_offset(0, rd));
+ tcg_temp_free_i32(tcg_tmp);
+ tcg_temp_free_i64(tcg_res);
+ tcg_temp_free_i64(tcg_double);
+ } else {
+ TCGv_i32 tcg_single, tcg_res;
+ tcg_single = tcg_temp_new_i32();
+ tcg_res = tcg_temp_new_i32();
+ tcg_gen_ld_f32(tcg_single, cpu_env, vfp_reg_offset(0, rm));
+ if (is_signed) {
+ gen_helper_vfp_tosls(tcg_res, tcg_single, tcg_shift, fpst);
+ } else {
+ gen_helper_vfp_touls(tcg_res, tcg_single, tcg_shift, fpst);
+ }
+ tcg_gen_st_f32(tcg_res, cpu_env, vfp_reg_offset(0, rd));
+ tcg_temp_free_i32(tcg_res);
+ tcg_temp_free_i32(tcg_single);
+ }
+
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ tcg_temp_free_i32(tcg_rmode);
+
+ tcg_temp_free_i32(tcg_shift);
+
+ tcg_temp_free_ptr(fpst);
+
+ return 0;
+}
+
+/* Table for converting the most common AArch32 encoding of
+ * rounding mode to arm_fprounding order (which matches the
+ * common AArch64 order); see ARM ARM pseudocode FPDecodeRM().
+ */
+static const uint8_t fp_decode_rm[] = {
+ FPROUNDING_TIEAWAY,
+ FPROUNDING_TIEEVEN,
+ FPROUNDING_POSINF,
+ FPROUNDING_NEGINF,
+};
+
static int disas_vfp_v8_insn(CPUARMState *env, DisasContext *s, uint32_t insn)
{
uint32_t rd, rn, rm, dp = extract32(insn, 8, 1);
@@ -2781,6 +2888,14 @@ static int disas_vfp_v8_insn(CPUARMState *env, DisasContext *s, uint32_t insn)
return handle_vsel(insn, rd, rn, rm, dp);
} else if ((insn & 0x0fb00e10) == 0x0e800a00) {
return handle_vminmaxnm(insn, rd, rn, rm, dp);
+ } else if ((insn & 0x0fbc0ed0) == 0x0eb80a40) {
+ /* VRINTA, VRINTN, VRINTP, VRINTM */
+ int rounding = fp_decode_rm[extract32(insn, 16, 2)];
+ return handle_vrint(insn, rd, rm, dp, rounding);
+ } else if ((insn & 0x0fbc0e50) == 0x0ebc0a40) {
+ /* VCVTA, VCVTN, VCVTP, VCVTM */
+ int rounding = fp_decode_rm[extract32(insn, 16, 2)];
+ return handle_vcvt(insn, rd, rm, dp, rounding);
}
return 1;
}
@@ -3325,6 +3440,44 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
gen_vfp_F1_ld0(dp);
gen_vfp_cmpe(dp);
break;
+ case 12: /* vrintr */
+ {
+ TCGv_ptr fpst = get_fpstatus_ptr(0);
+ if (dp) {
+ gen_helper_rintd(cpu_F0d, cpu_F0d, fpst);
+ } else {
+ gen_helper_rints(cpu_F0s, cpu_F0s, fpst);
+ }
+ tcg_temp_free_ptr(fpst);
+ break;
+ }
+ case 13: /* vrintz */
+ {
+ TCGv_ptr fpst = get_fpstatus_ptr(0);
+ TCGv_i32 tcg_rmode;
+ tcg_rmode = tcg_const_i32(float_round_to_zero);
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ if (dp) {
+ gen_helper_rintd(cpu_F0d, cpu_F0d, fpst);
+ } else {
+ gen_helper_rints(cpu_F0s, cpu_F0s, fpst);
+ }
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ tcg_temp_free_i32(tcg_rmode);
+ tcg_temp_free_ptr(fpst);
+ break;
+ }
+ case 14: /* vrintx */
+ {
+ TCGv_ptr fpst = get_fpstatus_ptr(0);
+ if (dp) {
+ gen_helper_rintd_exact(cpu_F0d, cpu_F0d, fpst);
+ } else {
+ gen_helper_rints_exact(cpu_F0s, cpu_F0s, fpst);
+ }
+ tcg_temp_free_ptr(fpst);
+ break;
+ }
case 15: /* single<->double conversion */
if (dp)
gen_helper_vfp_fcvtsd(cpu_F0s, cpu_F0d, cpu_env);
@@ -4617,8 +4770,22 @@ static const uint8_t neon_3r_sizes[] = {
#define NEON_2RM_VMOVN 36 /* Includes VQMOVN, VQMOVUN */
#define NEON_2RM_VQMOVN 37 /* Includes VQMOVUN */
#define NEON_2RM_VSHLL 38
+#define NEON_2RM_VRINTN 40
+#define NEON_2RM_VRINTX 41
+#define NEON_2RM_VRINTA 42
+#define NEON_2RM_VRINTZ 43
#define NEON_2RM_VCVT_F16_F32 44
+#define NEON_2RM_VRINTM 45
#define NEON_2RM_VCVT_F32_F16 46
+#define NEON_2RM_VRINTP 47
+#define NEON_2RM_VCVTAU 48
+#define NEON_2RM_VCVTAS 49
+#define NEON_2RM_VCVTNU 50
+#define NEON_2RM_VCVTNS 51
+#define NEON_2RM_VCVTPU 52
+#define NEON_2RM_VCVTPS 53
+#define NEON_2RM_VCVTMU 54
+#define NEON_2RM_VCVTMS 55
#define NEON_2RM_VRECPE 56
#define NEON_2RM_VRSQRTE 57
#define NEON_2RM_VRECPE_F 58
@@ -4632,6 +4799,9 @@ static int neon_2rm_is_float_op(int op)
{
/* Return true if this neon 2reg-misc op is float-to-float */
return (op == NEON_2RM_VABS_F || op == NEON_2RM_VNEG_F ||
+ (op >= NEON_2RM_VRINTN && op <= NEON_2RM_VRINTZ) ||
+ op == NEON_2RM_VRINTM ||
+ (op >= NEON_2RM_VRINTP && op <= NEON_2RM_VCVTMS) ||
op >= NEON_2RM_VRECPE_F);
}
@@ -4676,8 +4846,22 @@ static const uint8_t neon_2rm_sizes[] = {
[NEON_2RM_VMOVN] = 0x7,
[NEON_2RM_VQMOVN] = 0x7,
[NEON_2RM_VSHLL] = 0x7,
+ [NEON_2RM_VRINTN] = 0x4,
+ [NEON_2RM_VRINTX] = 0x4,
+ [NEON_2RM_VRINTA] = 0x4,
+ [NEON_2RM_VRINTZ] = 0x4,
[NEON_2RM_VCVT_F16_F32] = 0x2,
+ [NEON_2RM_VRINTM] = 0x4,
[NEON_2RM_VCVT_F32_F16] = 0x2,
+ [NEON_2RM_VRINTP] = 0x4,
+ [NEON_2RM_VCVTAU] = 0x4,
+ [NEON_2RM_VCVTAS] = 0x4,
+ [NEON_2RM_VCVTNU] = 0x4,
+ [NEON_2RM_VCVTNS] = 0x4,
+ [NEON_2RM_VCVTPU] = 0x4,
+ [NEON_2RM_VCVTPS] = 0x4,
+ [NEON_2RM_VCVTMU] = 0x4,
+ [NEON_2RM_VCVTMS] = 0x4,
[NEON_2RM_VRECPE] = 0x4,
[NEON_2RM_VRSQRTE] = 0x4,
[NEON_2RM_VRECPE_F] = 0x4,
@@ -6388,6 +6572,73 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins
}
neon_store_reg(rm, pass, tmp2);
break;
+ case NEON_2RM_VRINTN:
+ case NEON_2RM_VRINTA:
+ case NEON_2RM_VRINTM:
+ case NEON_2RM_VRINTP:
+ case NEON_2RM_VRINTZ:
+ {
+ TCGv_i32 tcg_rmode;
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ int rmode;
+
+ if (op == NEON_2RM_VRINTZ) {
+ rmode = FPROUNDING_ZERO;
+ } else {
+ rmode = fp_decode_rm[((op & 0x6) >> 1) ^ 1];
+ }
+
+ tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode));
+ gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode,
+ cpu_env);
+ gen_helper_rints(cpu_F0s, cpu_F0s, fpstatus);
+ gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode,
+ cpu_env);
+ tcg_temp_free_ptr(fpstatus);
+ tcg_temp_free_i32(tcg_rmode);
+ break;
+ }
+ case NEON_2RM_VRINTX:
+ {
+ TCGv_ptr fpstatus = get_fpstatus_ptr(1);
+ gen_helper_rints_exact(cpu_F0s, cpu_F0s, fpstatus);
+ tcg_temp_free_ptr(fpstatus);
+ break;
+ }
+ case NEON_2RM_VCVTAU:
+ case NEON_2RM_VCVTAS:
+ case NEON_2RM_VCVTNU:
+ case NEON_2RM_VCVTNS:
+ case NEON_2RM_VCVTPU:
+ case NEON_2RM_VCVTPS:
+ case NEON_2RM_VCVTMU:
+ case NEON_2RM_VCVTMS:
+ {
+ bool is_signed = !extract32(insn, 7, 1);
+ TCGv_ptr fpst = get_fpstatus_ptr(1);
+ TCGv_i32 tcg_rmode, tcg_shift;
+ int rmode = fp_decode_rm[extract32(insn, 8, 2)];
+
+ tcg_shift = tcg_const_i32(0);
+ tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode));
+ gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode,
+ cpu_env);
+
+ if (is_signed) {
+ gen_helper_vfp_tosls(cpu_F0s, cpu_F0s,
+ tcg_shift, fpst);
+ } else {
+ gen_helper_vfp_touls(cpu_F0s, cpu_F0s,
+ tcg_shift, fpst);
+ }
+
+ gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode,
+ cpu_env);
+ tcg_temp_free_i32(tcg_rmode);
+ tcg_temp_free_i32(tcg_shift);
+ tcg_temp_free_ptr(fpst);
+ break;
+ }
case NEON_2RM_VRECPE:
gen_helper_recpe_u32(tmp, tmp, cpu_env);
break;
diff --git a/target-cris/cpu.c b/target-cris/cpu.c
index 44301a4b10..1ac8124d8c 100644
--- a/target-cris/cpu.c
+++ b/target-cris/cpu.c
@@ -66,6 +66,12 @@ static ObjectClass *cris_cpu_class_by_name(const char *cpu_model)
return NULL;
}
+#if defined(CONFIG_USER_ONLY)
+ if (strcasecmp(cpu_model, "any") == 0) {
+ return object_class_by_name("crisv32-" TYPE_CRIS_CPU);
+ }
+#endif
+
typename = g_strdup_printf("%s-" TYPE_CRIS_CPU, cpu_model);
oc = object_class_by_name(typename);
g_free(typename);
@@ -146,6 +152,21 @@ static void cris_cpu_realizefn(DeviceState *dev, Error **errp)
ccc->parent_realize(dev, errp);
}
+#ifndef CONFIG_USER_ONLY
+static void cris_cpu_set_irq(void *opaque, int irq, int level)
+{
+ CRISCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+ int type = irq == CRIS_CPU_IRQ ? CPU_INTERRUPT_HARD : CPU_INTERRUPT_NMI;
+
+ if (level) {
+ cpu_interrupt(cs, type);
+ } else {
+ cpu_reset_interrupt(cs, type);
+ }
+}
+#endif
+
static void cris_cpu_initfn(Object *obj)
{
CPUState *cs = CPU(obj);
@@ -159,6 +180,11 @@ static void cris_cpu_initfn(Object *obj)
env->pregs[PR_VR] = ccc->vr;
+#ifndef CONFIG_USER_ONLY
+ /* IRQ and NMI lines. */
+ qdev_init_gpio_in(DEVICE(cpu), cris_cpu_set_irq, 2);
+#endif
+
if (tcg_enabled() && !tcg_initialized) {
tcg_initialized = true;
if (env->pregs[PR_VR] < 32) {
diff --git a/target-cris/cpu.h b/target-cris/cpu.h
index 4b9fc4cb45..1d7d80d3dc 100644
--- a/target-cris/cpu.h
+++ b/target-cris/cpu.h
@@ -42,6 +42,10 @@
/* CRIS-specific interrupt pending bits. */
#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3
+/* CRUS CPU device objects interrupt lines. */
+#define CRIS_CPU_IRQ 0
+#define CRIS_CPU_NMI 1
+
/* Register aliases. R0 - R15 */
#define R_FP 8
#define R_SP 14
diff --git a/target-cris/helper.c b/target-cris/helper.c
index d274b388b8..c940582132 100644
--- a/target-cris/helper.c
+++ b/target-cris/helper.c
@@ -126,6 +126,11 @@ void crisv10_cpu_do_interrupt(CPUState *cs)
env->exception_index,
cs->interrupt_request);
+ if (env->dslot) {
+ /* CRISv10 never takes interrupts while in a delay-slot. */
+ cpu_abort(env, "CRIS: Interrupt on delay-slot\n");
+ }
+
assert(!(env->pregs[PR_CCS] & PFIX_FLAG));
switch (env->exception_index) {
case EXCP_BREAK:
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 68b5ab7056..96c2b4a7e9 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -78,11 +78,6 @@ typedef struct MchkQueue {
uint16_t type;
} MchkQueue;
-/* Defined values for CPUS390XState.runtime_reg_dirty_mask */
-#define KVM_S390_RUNTIME_DIRTY_NONE 0
-#define KVM_S390_RUNTIME_DIRTY_PARTIAL 1
-#define KVM_S390_RUNTIME_DIRTY_FULL 2
-
typedef struct CPUS390XState {
uint64_t regs[16]; /* GP registers */
CPU_DoubleU fregs[16]; /* FP registers */
@@ -126,13 +121,6 @@ typedef struct CPUS390XState {
uint64_t cputm;
uint32_t todpr;
- /* on S390 the runtime register set has two dirty states:
- * a partial dirty state in which only the registers that
- * are needed all the time are fetched. And a fully dirty
- * state in which all runtime registers are fetched.
- */
- uint32_t runtime_reg_dirty_mask;
-
CPU_COMMON
/* reset does memset(0) up to here */
@@ -1076,7 +1064,6 @@ void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id,
uint32_t io_int_word);
void kvm_s390_crw_mchk(S390CPU *cpu);
void kvm_s390_enable_css_support(S390CPU *cpu);
-int kvm_s390_get_registers_partial(CPUState *cpu);
int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
int vq, bool assign);
int kvm_s390_cpu_restart(S390CPU *cpu);
@@ -1094,10 +1081,6 @@ static inline void kvm_s390_crw_mchk(S390CPU *cpu)
static inline void kvm_s390_enable_css_support(S390CPU *cpu)
{
}
-static inline int kvm_s390_get_registers_partial(CPUState *cpu)
-{
- return -ENOSYS;
-}
static inline int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier,
uint32_t sch, int vq,
bool assign)
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index f7b772668c..f60ccdc326 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -152,33 +152,30 @@ int kvm_arch_put_registers(CPUState *cs, int level)
}
}
- if (env->runtime_reg_dirty_mask == KVM_S390_RUNTIME_DIRTY_FULL) {
- reg.id = KVM_REG_S390_CPU_TIMER;
- reg.addr = (__u64)&(env->cputm);
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
- if (ret < 0) {
- return ret;
- }
+ /* Do we need to save more than that? */
+ if (level == KVM_PUT_RUNTIME_STATE) {
+ return 0;
+ }
- reg.id = KVM_REG_S390_CLOCK_COMP;
- reg.addr = (__u64)&(env->ckc);
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
- if (ret < 0) {
- return ret;
- }
+ reg.id = KVM_REG_S390_CPU_TIMER;
+ reg.addr = (__u64)&(env->cputm);
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+ if (ret < 0) {
+ return ret;
+ }
- reg.id = KVM_REG_S390_TODPR;
- reg.addr = (__u64)&(env->todpr);
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
- if (ret < 0) {
- return ret;
- }
+ reg.id = KVM_REG_S390_CLOCK_COMP;
+ reg.addr = (__u64)&(env->ckc);
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+ if (ret < 0) {
+ return ret;
}
- env->runtime_reg_dirty_mask = KVM_S390_RUNTIME_DIRTY_NONE;
- /* Do we need to save more than that? */
- if (level == KVM_PUT_RUNTIME_STATE) {
- return 0;
+ reg.id = KVM_REG_S390_TODPR;
+ reg.addr = (__u64)&(env->todpr);
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+ if (ret < 0) {
+ return ret;
}
if (cap_sync_regs &&
@@ -216,50 +213,9 @@ int kvm_arch_get_registers(CPUState *cs)
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
struct kvm_one_reg reg;
- int r;
-
- r = kvm_s390_get_registers_partial(cs);
- if (r < 0) {
- return r;
- }
-
- reg.id = KVM_REG_S390_CPU_TIMER;
- reg.addr = (__u64)&(env->cputm);
- r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
- if (r < 0) {
- return r;
- }
-
- reg.id = KVM_REG_S390_CLOCK_COMP;
- reg.addr = (__u64)&(env->ckc);
- r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
- if (r < 0) {
- return r;
- }
-
- reg.id = KVM_REG_S390_TODPR;
- reg.addr = (__u64)&(env->todpr);
- r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
- if (r < 0) {
- return r;
- }
-
- env->runtime_reg_dirty_mask = KVM_S390_RUNTIME_DIRTY_FULL;
- return 0;
-}
-
-int kvm_s390_get_registers_partial(CPUState *cs)
-{
- S390CPU *cpu = S390_CPU(cs);
- CPUS390XState *env = &cpu->env;
struct kvm_sregs sregs;
struct kvm_regs regs;
- int ret;
- int i;
-
- if (env->runtime_reg_dirty_mask) {
- return 0;
- }
+ int i, r;
/* get the PSW */
env->psw.addr = cs->kvm_run->psw_addr;
@@ -271,9 +227,9 @@ int kvm_s390_get_registers_partial(CPUState *cs)
env->regs[i] = cs->kvm_run->s.regs.gprs[i];
}
} else {
- ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, &regs);
- if (ret < 0) {
- return ret;
+ r = kvm_vcpu_ioctl(cs, KVM_GET_REGS, &regs);
+ if (r < 0) {
+ return r;
}
for (i = 0; i < 16; i++) {
env->regs[i] = regs.gprs[i];
@@ -289,9 +245,9 @@ int kvm_s390_get_registers_partial(CPUState *cs)
env->cregs[i] = cs->kvm_run->s.regs.crs[i];
}
} else {
- ret = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs);
- if (ret < 0) {
- return ret;
+ r = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs);
+ if (r < 0) {
+ return r;
}
for (i = 0; i < 16; i++) {
env->aregs[i] = sregs.acrs[i];
@@ -299,14 +255,33 @@ int kvm_s390_get_registers_partial(CPUState *cs)
}
}
- /* Finally the prefix */
+ /* The prefix */
if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) {
env->psa = cs->kvm_run->s.regs.prefix;
- } else {
- /* no prefix without sync regs */
}
- env->runtime_reg_dirty_mask = KVM_S390_RUNTIME_DIRTY_PARTIAL;
+ /* One Regs */
+ reg.id = KVM_REG_S390_CPU_TIMER;
+ reg.addr = (__u64)&(env->cputm);
+ r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+ if (r < 0) {
+ return r;
+ }
+
+ reg.id = KVM_REG_S390_CLOCK_COMP;
+ reg.addr = (__u64)&(env->ckc);
+ r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+ if (r < 0) {
+ return r;
+ }
+
+ reg.id = KVM_REG_S390_TODPR;
+ reg.addr = (__u64)&(env->todpr);
+ r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+ if (r < 0) {
+ return r;
+ }
+
return 0;
}
@@ -442,15 +417,13 @@ static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run,
uint8_t ipa0, uint8_t ipa1, uint8_t ipb)
{
CPUS390XState *env = &cpu->env;
- CPUState *cs = CPU(cpu);
if (ipa0 != 0xb2) {
/* Not handled for now. */
return -1;
}
- kvm_s390_get_registers_partial(cs);
- cs->kvm_vcpu_dirty = true;
+ cpu_synchronize_state(CPU(cpu));
switch (ipa1) {
case PRIV_XSCH:
@@ -537,11 +510,9 @@ static int handle_priv(S390CPU *cpu, struct kvm_run *run,
static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
{
- CPUState *cs = CPU(cpu);
CPUS390XState *env = &cpu->env;
- kvm_s390_get_registers_partial(cs);
- cs->kvm_vcpu_dirty = true;
+ cpu_synchronize_state(CPU(cpu));
env->regs[2] = s390_virtio_hypercall(env);
return 0;
@@ -767,8 +738,7 @@ static int handle_tsch(S390CPU *cpu)
struct kvm_run *run = cs->kvm_run;
int ret;
- kvm_s390_get_registers_partial(cs);
- cs->kvm_vcpu_dirty = true;
+ cpu_synchronize_state(cs);
ret = ioinst_handle_tsch(env, env->regs[1], run->s390_tsch.ipb);
if (ret >= 0) {
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index 72eaad5b08..734b6a6bb4 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -35,12 +35,8 @@ test_img = os.path.join(iotests.test_dir, 'test.img')
class ImageCommitTestCase(iotests.QMPTestCase):
'''Abstract base class for image commit test cases'''
- def assert_no_active_commit(self):
- result = self.vm.qmp('query-block-jobs')
- self.assert_qmp(result, 'return', [])
-
def run_commit_test(self, top, base):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
self.assert_qmp(result, 'return', {})
@@ -59,7 +55,7 @@ class ImageCommitTestCase(iotests.QMPTestCase):
self.assert_qmp(event, 'data/len', self.image_len)
self.vm.qmp('block-job-complete', device='drive0')
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
self.vm.shutdown()
class TestSingleDrive(ImageCommitTestCase):
@@ -91,19 +87,19 @@ class TestSingleDrive(ImageCommitTestCase):
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
def test_top_same_base(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % backing_img)
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % backing_img)
def test_top_invalid(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % backing_img)
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Top image file badfile not found')
def test_base_invalid(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img, base='badfile')
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
@@ -114,13 +110,13 @@ class TestSingleDrive(ImageCommitTestCase):
self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
def test_top_and_base_reversed(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img)
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img)
def test_top_omitted(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0')
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', "Parameter 'top' is missing")
@@ -181,19 +177,19 @@ class TestRelativePaths(ImageCommitTestCase):
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
def test_top_same_base(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='%s' % self.mid_img)
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % self.mid_img)
def test_top_invalid(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % self.backing_img)
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Top image file badfile not found')
def test_base_invalid(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='badfile')
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
@@ -204,7 +200,7 @@ class TestRelativePaths(ImageCommitTestCase):
self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
def test_top_and_base_reversed(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.backing_img, base='%s' % self.mid_img)
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % self.mid_img)
@@ -229,7 +225,7 @@ class TestSetSpeed(ImageCommitTestCase):
os.remove(backing_img)
def test_set_speed(self):
- self.assert_no_active_commit()
+ self.assert_no_active_block_jobs()
self.vm.pause_drive('drive0')
result = self.vm.qmp('block-commit', device='drive0', top=mid_img, speed=1024 * 1024)
diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071
index 2a22546e1a..dbc07c6c4f 100755
--- a/tests/qemu-iotests/071
+++ b/tests/qemu-iotests/071
@@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.rc
. ./common.filter
-_supported_fmt generic
+_supported_fmt qcow2
_supported_proto generic
_supported_os Linux
diff --git a/translate-all.c b/translate-all.c
index 105c25aff3..543e1ffe77 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -289,17 +289,15 @@ static inline void map_exec(void *addr, long size)
}
#endif
-static void page_init(void)
+void page_size_init(void)
{
/* NOTE: we can always suppose that qemu_host_page_size >=
TARGET_PAGE_SIZE */
#ifdef _WIN32
- {
- SYSTEM_INFO system_info;
+ SYSTEM_INFO system_info;
- GetSystemInfo(&system_info);
- qemu_real_host_page_size = system_info.dwPageSize;
- }
+ GetSystemInfo(&system_info);
+ qemu_real_host_page_size = system_info.dwPageSize;
#else
qemu_real_host_page_size = getpagesize();
#endif
@@ -310,7 +308,11 @@ static void page_init(void)
qemu_host_page_size = TARGET_PAGE_SIZE;
}
qemu_host_page_mask = ~(qemu_host_page_size - 1);
+}
+static void page_init(void)
+{
+ page_size_init();
#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY)
{
#ifdef HAVE_KINFO_GETVMMAP
diff --git a/vl.c b/vl.c
index c73462e811..383be1b617 100644
--- a/vl.c
+++ b/vl.c
@@ -170,6 +170,7 @@ int main(int argc, char **argv)
#include "ui/qemu-spice.h"
#include "qapi/string-input-visitor.h"
+#include "qom/object_interfaces.h"
//#define DEBUG_NET
//#define DEBUG_SLIRP
@@ -2800,6 +2801,7 @@ static int object_create(QemuOpts *opts, void *opaque)
{
const char *type = qemu_opt_get(opts, "qom-type");
const char *id = qemu_opts_id(opts);
+ Error *local_err = NULL;
Object *obj;
g_assert(type != NULL);
@@ -2815,9 +2817,27 @@ static int object_create(QemuOpts *opts, void *opaque)
return -1;
}
+ if (!object_dynamic_cast(obj, TYPE_USER_CREATABLE)) {
+ error_setg(&local_err, "object '%s' isn't supported by -object",
+ id);
+ goto out;
+ }
+
+ user_creatable_complete(obj, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
object_property_add_child(container_get(object_get_root(), "/objects"),
- id, obj, NULL);
+ id, obj, &local_err);
+
+out:
object_unref(obj);
+ if (local_err) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return -1;
+ }
return 0;
}