summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Maydell2019-03-05 18:23:25 +0100
committerPeter Maydell2019-03-05 18:23:25 +0100
commitc99b58326d92034a00003c4f2e019f662c3dbee5 (patch)
tree6053513cce22b4be037227e99a16f1487e711307
parentMerge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into st... (diff)
parenthw/arm/stellaris: Implement watchdog timer (diff)
downloadqemu-c99b58326d92034a00003c4f2e019f662c3dbee5.tar.gz
qemu-c99b58326d92034a00003c4f2e019f662c3dbee5.tar.xz
qemu-c99b58326d92034a00003c4f2e019f662c3dbee5.zip
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190305' into staging
target-arm queue: * Fix PC test for LDM (exception return) * Implement ARMv8.0-SB * Implement ARMv8.0-PredInv * Implement ARMv8.4-CondM * Implement ARMv8.5-CondM * Implement ARMv8.5-FRINT * hw/arm/stellaris: Implement watchdog timer * virt: support more than 255GB of RAM # gpg: Signature made Tue 05 Mar 2019 16:49:47 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20190305: (22 commits) hw/arm/stellaris: Implement watchdog timer hw/arm/virt: Bump the 255GB initial RAM limit hw/arm/virt: Check the VCPU PA range in TCG mode hw/arm/virt: Implement kvm_type function for 4.0 machine hw/arm/virt: Dynamic memory map depending on RAM requirements vl: Set machine ram_size, maxram_size and ram_slots earlier kvm: add kvm_arm_get_max_vm_ipa_size hw/boards: Add a MachineState parameter to kvm_type callback hw/arm/virt: Split the memory map description hw/arm/virt: Rename highmem IO regions hw/arm/boot: introduce fdt_add_memory_node helper target/arm: Implement ARMv8.5-FRINT target/arm: Restructure handle_fp_1src_{single, double} target/arm: Implement ARMv8.5-CondM target/arm: Implement ARMv8.4-CondM target/arm: Rearrange disas_data_proc_reg target/arm: Add set/clear_pstate_bits, share gen_ss_advance target/arm: Split helper_msr_i_pstate into 3 target/arm: Implement ARMv8.0-PredInv target/arm: Implement ARMv8.0-SB ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--accel/kvm/kvm-all.c2
-rw-r--r--hw/arm/boot.c54
-rw-r--r--hw/arm/stellaris.c22
-rw-r--r--hw/arm/virt-acpi-build.c10
-rw-r--r--hw/arm/virt.c196
-rw-r--r--hw/ppc/mac_newworld.c3
-rw-r--r--hw/ppc/mac_oldworld.c2
-rw-r--r--hw/ppc/spapr.c2
-rw-r--r--hw/watchdog/cmsdk-apb-watchdog.c74
-rw-r--r--include/hw/arm/virt.h16
-rw-r--r--include/hw/boards.h5
-rw-r--r--include/hw/watchdog/cmsdk-apb-watchdog.h8
-rw-r--r--linux-user/elfload.c2
-rw-r--r--target/arm/cpu.c2
-rw-r--r--target/arm/cpu.h62
-rw-r--r--target/arm/cpu64.c6
-rw-r--r--target/arm/helper-a64.c30
-rw-r--r--target/arm/helper-a64.h3
-rw-r--r--target/arm/helper.c63
-rw-r--r--target/arm/helper.h8
-rw-r--r--target/arm/internals.h15
-rw-r--r--target/arm/kvm.c10
-rw-r--r--target/arm/kvm_arm.h13
-rw-r--r--target/arm/op_helper.c47
-rw-r--r--target/arm/translate-a64.c472
-rw-r--r--target/arm/translate.c35
-rw-r--r--target/arm/translate.h34
-rw-r--r--target/arm/vfp_helper.c96
-rw-r--r--vl.c6
29 files changed, 1028 insertions, 270 deletions
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index fd92b6f375..241db496c3 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -1593,7 +1593,7 @@ static int kvm_init(MachineState *ms)
kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type");
if (mc->kvm_type) {
- type = mc->kvm_type(kvm_type);
+ type = mc->kvm_type(ms, kvm_type);
} else if (kvm_type) {
ret = -EINVAL;
fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index d90af2f17d..a830655e1a 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -423,6 +423,32 @@ static void set_kernel_args_old(const struct arm_boot_info *info,
}
}
+static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base,
+ uint32_t scells, hwaddr mem_len,
+ int numa_node_id)
+{
+ char *nodename;
+ int ret;
+
+ nodename = g_strdup_printf("/memory@%" PRIx64, mem_base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+ ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", acells, mem_base,
+ scells, mem_len);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* only set the NUMA ID if it is specified */
+ if (numa_node_id >= 0) {
+ ret = qemu_fdt_setprop_cell(fdt, nodename,
+ "numa-node-id", numa_node_id);
+ }
+out:
+ g_free(nodename);
+ return ret;
+}
+
static void fdt_add_psci_node(void *fdt)
{
uint32_t cpu_suspend_fn;
@@ -502,7 +528,6 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
void *fdt = NULL;
int size, rc, n = 0;
uint32_t acells, scells;
- char *nodename;
unsigned int i;
hwaddr mem_base, mem_len;
char **node_path;
@@ -576,35 +601,24 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
mem_base = binfo->loader_start;
for (i = 0; i < nb_numa_nodes; i++) {
mem_len = numa_info[i].node_mem;
- nodename = g_strdup_printf("/memory@%" PRIx64, mem_base);
- qemu_fdt_add_subnode(fdt, nodename);
- qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
- rc = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
- acells, mem_base,
- scells, mem_len);
+ rc = fdt_add_memory_node(fdt, acells, mem_base,
+ scells, mem_len, i);
if (rc < 0) {
- fprintf(stderr, "couldn't set %s/reg for node %d\n", nodename,
- i);
+ fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
+ mem_base);
goto fail;
}
- qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", i);
mem_base += mem_len;
- g_free(nodename);
}
} else {
- nodename = g_strdup_printf("/memory@%" PRIx64, binfo->loader_start);
- qemu_fdt_add_subnode(fdt, nodename);
- qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
-
- rc = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
- acells, binfo->loader_start,
- scells, binfo->ram_size);
+ rc = fdt_add_memory_node(fdt, acells, binfo->loader_start,
+ scells, binfo->ram_size, -1);
if (rc < 0) {
- fprintf(stderr, "couldn't set %s reg\n", nodename);
+ fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
+ binfo->loader_start);
goto fail;
}
- g_free(nodename);
}
rc = fdt_path_offset(fdt, "/chosen");
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index 7b45fe3ccf..05f86749f4 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -22,6 +22,7 @@
#include "sysemu/sysemu.h"
#include "hw/arm/armv7m.h"
#include "hw/char/pl011.h"
+#include "hw/watchdog/cmsdk-apb-watchdog.h"
#include "hw/misc/unimp.h"
#include "cpu.h"
@@ -1243,7 +1244,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
* Stellaris LM3S6965 Microcontroller Data Sheet (rev I)
* http://www.ti.com/lit/ds/symlink/lm3s6965.pdf
*
- * 40000000 wdtimer (unimplemented)
+ * 40000000 wdtimer
* 40002000 i2c (unimplemented)
* 40004000 GPIO
* 40005000 GPIO
@@ -1338,6 +1339,24 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28),
board, nd_table[0].macaddr.a);
+
+ if (board->dc1 & (1 << 3)) { /* watchdog present */
+ dev = qdev_create(NULL, TYPE_LUMINARY_WATCHDOG);
+
+ /* system_clock_scale is valid now */
+ uint32_t mainclk = NANOSECONDS_PER_SECOND / system_clock_scale;
+ qdev_prop_set_uint32(dev, "wdogclk-frq", mainclk);
+
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev),
+ 0,
+ 0x40000000u);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev),
+ 0,
+ qdev_get_gpio_in(nvic, 18));
+ }
+
+
for (i = 0; i < 7; i++) {
if (board->dc4 & (1 << i)) {
gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i],
@@ -1431,7 +1450,6 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
/* Add dummy regions for the devices we don't implement yet,
* so guest accesses don't cause unlogged crashes.
*/
- create_unimplemented_device("wdtimer", 0x40000000, 0x1000);
create_unimplemented_device("i2c-0", 0x40002000, 0x1000);
create_unimplemented_device("i2c-2", 0x40021000, 0x1000);
create_unimplemented_device("PWM", 0x40028000, 0x1000);
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 04b62c714d..d7e2e4885b 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -229,8 +229,8 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
size_pio));
if (use_highmem) {
- hwaddr base_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].base;
- hwaddr size_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].size;
+ hwaddr base_mmio_high = memmap[VIRT_HIGH_PCIE_MMIO].base;
+ hwaddr size_mmio_high = memmap[VIRT_HIGH_PCIE_MMIO].size;
aml_append(rbuf,
aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
@@ -663,8 +663,10 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
gicr = acpi_data_push(table_data, sizeof(*gicr));
gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR;
gicr->length = sizeof(*gicr);
- gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST2].base);
- gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST2].size);
+ gicr->base_address =
+ cpu_to_le64(memmap[VIRT_HIGH_GIC_REDIST2].base);
+ gicr->range_length =
+ cpu_to_le32(memmap[VIRT_HIGH_GIC_REDIST2].size);
}
if (its_class_name() && !vmc->no_its) {
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 99c2b6e60d..c7fb5348ae 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -29,6 +29,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/units.h"
#include "qapi/error.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
@@ -58,6 +59,8 @@
#include "qapi/visitor.h"
#include "standard-headers/linux/input.h"
#include "hw/arm/smmuv3.h"
+#include "hw/acpi/acpi.h"
+#include "target/arm/internals.h"
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -92,22 +95,9 @@
#define PLATFORM_BUS_NUM_IRQS 64
-/* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means
- * RAM can go up to the 256GB mark, leaving 256GB of the physical
- * address space unallocated and free for future use between 256G and 512G.
- * If we need to provide more RAM to VMs in the future then we need to:
- * * allocate a second bank of RAM starting at 2TB and working up
- * * fix the DT and ACPI table generation code in QEMU to correctly
- * report two split lumps of RAM to the guest
- * * fix KVM in the host kernel to allow guests with >40 bit address spaces
- * (We don't want to fill all the way up to 512GB with RAM because
- * we might want it for non-RAM purposes later. Conversely it seems
- * reasonable to assume that anybody configuring a VM with a quarter
- * of a terabyte of RAM will be doing it on a host with more than a
- * terabyte of physical address space.)
- */
-#define RAMLIMIT_GB 255
-#define RAMLIMIT_BYTES (RAMLIMIT_GB * 1024ULL * 1024 * 1024)
+/* Legacy RAM limit in GB (< version 4.0) */
+#define LEGACY_RAMLIMIT_GB 255
+#define LEGACY_RAMLIMIT_BYTES (LEGACY_RAMLIMIT_GB * GiB)
/* Addresses and sizes of our components.
* 0..128MB is space for a flash device so we can run bootrom code such as UEFI.
@@ -121,7 +111,7 @@
* Note that devices should generally be placed at multiples of 0x10000,
* to accommodate guests using 64K pages.
*/
-static const MemMapEntry a15memmap[] = {
+static const MemMapEntry base_memmap[] = {
/* Space up to 0x8000000 is reserved for a boot ROM */
[VIRT_FLASH] = { 0, 0x08000000 },
[VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 },
@@ -148,12 +138,26 @@ static const MemMapEntry a15memmap[] = {
[VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
- [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES },
+ /* Actual RAM size depends on initial RAM and device memory settings */
+ [VIRT_MEM] = { GiB, LEGACY_RAMLIMIT_BYTES },
+};
+
+/*
+ * Highmem IO Regions: This memory map is floating, located after the RAM.
+ * Each MemMapEntry base (GPA) will be dynamically computed, depending on the
+ * top of the RAM, so that its base get the same alignment as the size,
+ * ie. a 512GiB entry will be aligned on a 512GiB boundary. If there is
+ * less than 256GiB of RAM, the floating area starts at the 256GiB mark.
+ * Note the extended_memmap is sized so that it eventually also includes the
+ * base_memmap entries (VIRT_HIGH_GIC_REDIST2 index is greater than the last
+ * index of base_memmap).
+ */
+static MemMapEntry extended_memmap[] = {
/* Additional 64 MB redist region (can contain up to 512 redistributors) */
- [VIRT_GIC_REDIST2] = { 0x4000000000ULL, 0x4000000 },
- [VIRT_PCIE_ECAM_HIGH] = { 0x4010000000ULL, 0x10000000 },
- /* Second PCIe window, 512GB wide at the 512GB boundary */
- [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL },
+ [VIRT_HIGH_GIC_REDIST2] = { 0x0, 64 * MiB },
+ [VIRT_HIGH_PCIE_ECAM] = { 0x0, 256 * MiB },
+ /* Second PCIe window */
+ [VIRT_HIGH_PCIE_MMIO] = { 0x0, 512 * GiB },
};
static const int a15irqmap[] = {
@@ -431,12 +435,12 @@ static void fdt_add_gic_node(VirtMachineState *vms)
2, vms->memmap[VIRT_GIC_REDIST].size);
} else {
qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg",
- 2, vms->memmap[VIRT_GIC_DIST].base,
- 2, vms->memmap[VIRT_GIC_DIST].size,
- 2, vms->memmap[VIRT_GIC_REDIST].base,
- 2, vms->memmap[VIRT_GIC_REDIST].size,
- 2, vms->memmap[VIRT_GIC_REDIST2].base,
- 2, vms->memmap[VIRT_GIC_REDIST2].size);
+ 2, vms->memmap[VIRT_GIC_DIST].base,
+ 2, vms->memmap[VIRT_GIC_DIST].size,
+ 2, vms->memmap[VIRT_GIC_REDIST].base,
+ 2, vms->memmap[VIRT_GIC_REDIST].size,
+ 2, vms->memmap[VIRT_HIGH_GIC_REDIST2].base,
+ 2, vms->memmap[VIRT_HIGH_GIC_REDIST2].size);
}
if (vms->virt) {
@@ -584,7 +588,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
if (nb_redist_regions == 2) {
uint32_t redist1_capacity =
- vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE;
+ vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE;
qdev_prop_set_uint32(gicdev, "redist-region-count[1]",
MIN(smp_cpus - redist0_count, redist1_capacity));
@@ -601,7 +605,8 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
if (type == 3) {
sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base);
if (nb_redist_regions == 2) {
- sysbus_mmio_map(gicbusdev, 2, vms->memmap[VIRT_GIC_REDIST2].base);
+ sysbus_mmio_map(gicbusdev, 2,
+ vms->memmap[VIRT_HIGH_GIC_REDIST2].base);
}
} else {
sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_CPU].base);
@@ -1088,8 +1093,8 @@ static void create_pcie(VirtMachineState *vms, qemu_irq *pic)
{
hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base;
hwaddr size_mmio = vms->memmap[VIRT_PCIE_MMIO].size;
- hwaddr base_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].base;
- hwaddr size_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].size;
+ hwaddr base_mmio_high = vms->memmap[VIRT_HIGH_PCIE_MMIO].base;
+ hwaddr size_mmio_high = vms->memmap[VIRT_HIGH_PCIE_MMIO].size;
hwaddr base_pio = vms->memmap[VIRT_PCIE_PIO].base;
hwaddr size_pio = vms->memmap[VIRT_PCIE_PIO].size;
hwaddr base_ecam, size_ecam;
@@ -1353,6 +1358,62 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
return arm_cpu_mp_affinity(idx, clustersz);
}
+static void virt_set_memmap(VirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ hwaddr base, device_memory_base, device_memory_size;
+ int i;
+
+ vms->memmap = extended_memmap;
+
+ for (i = 0; i < ARRAY_SIZE(base_memmap); i++) {
+ vms->memmap[i] = base_memmap[i];
+ }
+
+ if (ms->ram_slots > ACPI_MAX_RAM_SLOTS) {
+ error_report("unsupported number of memory slots: %"PRIu64,
+ ms->ram_slots);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * We compute the base of the high IO region depending on the
+ * amount of initial and device memory. The device memory start/size
+ * is aligned on 1GiB. We never put the high IO region below 256GiB
+ * so that if maxram_size is < 255GiB we keep the legacy memory map.
+ * The device region size assumes 1GiB page max alignment per slot.
+ */
+ device_memory_base =
+ ROUND_UP(vms->memmap[VIRT_MEM].base + ms->ram_size, GiB);
+ device_memory_size = ms->maxram_size - ms->ram_size + ms->ram_slots * GiB;
+
+ /* Base address of the high IO region */
+ base = device_memory_base + ROUND_UP(device_memory_size, GiB);
+ if (base < device_memory_base) {
+ error_report("maxmem/slots too huge");
+ exit(EXIT_FAILURE);
+ }
+ if (base < vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES) {
+ base = vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES;
+ }
+
+ for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) {
+ hwaddr size = extended_memmap[i].size;
+
+ base = ROUND_UP(base, size);
+ vms->memmap[i].base = base;
+ vms->memmap[i].size = size;
+ base += size;
+ }
+ vms->highest_gpa = base - 1;
+ if (device_memory_size > 0) {
+ ms->device_memory = g_malloc0(sizeof(*ms->device_memory));
+ ms->device_memory->base = device_memory_base;
+ memory_region_init(&ms->device_memory->mr, OBJECT(vms),
+ "device-memory", device_memory_size);
+ }
+}
+
static void machvirt_init(MachineState *machine)
{
VirtMachineState *vms = VIRT_MACHINE(machine);
@@ -1367,6 +1428,14 @@ static void machvirt_init(MachineState *machine)
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
bool aarch64 = true;
+ /*
+ * In accelerated mode, the memory map is computed earlier in kvm_type()
+ * to create a VM with the right number of IPA bits.
+ */
+ if (!vms->memmap) {
+ virt_set_memmap(vms);
+ }
+
/* We can probe only here because during property set
* KVM is not available yet
*/
@@ -1417,8 +1486,10 @@ static void machvirt_init(MachineState *machine)
* many redistributors we can fit into the memory map.
*/
if (vms->gic_version == 3) {
- virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
- virt_max_cpus += vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE;
+ virt_max_cpus =
+ vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
+ virt_max_cpus +=
+ vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE;
} else {
virt_max_cpus = GIC_NCPU;
}
@@ -1432,11 +1503,6 @@ static void machvirt_init(MachineState *machine)
vms->smp_cpus = smp_cpus;
- if (machine->ram_size > vms->memmap[VIRT_MEM].size) {
- error_report("mach-virt: cannot model more than %dGB RAM", RAMLIMIT_GB);
- exit(1);
- }
-
if (vms->virt && kvm_enabled()) {
error_report("mach-virt: KVM does not support providing "
"Virtualization extensions to the guest CPU");
@@ -1524,9 +1590,29 @@ static void machvirt_init(MachineState *machine)
fdt_add_timer_nodes(vms);
fdt_add_cpu_nodes(vms);
+ if (!kvm_enabled()) {
+ ARMCPU *cpu = ARM_CPU(first_cpu);
+ bool aarch64 = object_property_get_bool(OBJECT(cpu), "aarch64", NULL);
+
+ if (aarch64 && vms->highmem) {
+ int requested_pa_size, pamax = arm_pamax(cpu);
+
+ requested_pa_size = 64 - clz64(vms->highest_gpa);
+ if (pamax < requested_pa_size) {
+ error_report("VCPU supports less PA bits (%d) than requested "
+ "by the memory map (%d)", pamax, requested_pa_size);
+ exit(1);
+ }
+ }
+ }
+
memory_region_allocate_system_memory(ram, NULL, "mach-virt.ram",
machine->ram_size);
memory_region_add_subregion(sysmem, vms->memmap[VIRT_MEM].base, ram);
+ if (machine->device_memory) {
+ memory_region_add_subregion(sysmem, machine->device_memory->base,
+ &machine->device_memory->mr);
+ }
create_flash(vms, sysmem, secure_sysmem ? secure_sysmem : sysmem);
@@ -1747,6 +1833,36 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
return NULL;
}
+/*
+ * for arm64 kvm_type [7-0] encodes the requested number of bits
+ * in the IPA address space
+ */
+static int virt_kvm_type(MachineState *ms, const char *type_str)
+{
+ VirtMachineState *vms = VIRT_MACHINE(ms);
+ int max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms);
+ int requested_pa_size;
+
+ /* we freeze the memory map to compute the highest gpa */
+ virt_set_memmap(vms);
+
+ requested_pa_size = 64 - clz64(vms->highest_gpa);
+
+ if (requested_pa_size > max_vm_pa_size) {
+ error_report("-m and ,maxmem option values "
+ "require an IPA range (%d bits) larger than "
+ "the one supported by the host (%d bits)",
+ requested_pa_size, max_vm_pa_size);
+ exit(1);
+ }
+ /*
+ * By default we return 0 which corresponds to an implicit legacy
+ * 40b IPA setting. Otherwise we return the actual requested PA
+ * logsize
+ */
+ return requested_pa_size > 40 ? requested_pa_size : 0;
+}
+
static void virt_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
@@ -1771,6 +1887,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
mc->cpu_index_to_instance_props = virt_cpu_index_to_props;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15");
mc->get_default_cpu_node_id = virt_get_default_cpu_node_id;
+ mc->kvm_type = virt_kvm_type;
assert(!mc->get_hotplug_handler);
mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
hc->plug = virt_machine_device_plug_cb;
@@ -1842,7 +1959,6 @@ static void virt_instance_init(Object *obj)
"Valid values are none and smmuv3",
NULL);
- vms->memmap = a15memmap;
vms->irqmap = a15irqmap;
}
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index 98461052ac..97e8817145 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -564,8 +564,7 @@ static char *core99_fw_dev_path(FWPathProvider *p, BusState *bus,
return NULL;
}
-
-static int core99_kvm_type(const char *arg)
+static int core99_kvm_type(MachineState *machine, const char *arg)
{
/* Always force PR KVM */
return 2;
diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c
index 284431ddd6..cc1e463466 100644
--- a/hw/ppc/mac_oldworld.c
+++ b/hw/ppc/mac_oldworld.c
@@ -420,7 +420,7 @@ static char *heathrow_fw_dev_path(FWPathProvider *p, BusState *bus,
return NULL;
}
-static int heathrow_kvm_type(const char *arg)
+static int heathrow_kvm_type(MachineState *machine, const char *arg)
{
/* Always force PR KVM */
return 2;
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index b6a571b6f1..d7850ada7d 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -3023,7 +3023,7 @@ static void spapr_machine_init(MachineState *machine)
}
}
-static int spapr_kvm_type(const char *vm_type)
+static int spapr_kvm_type(MachineState *machine, const char *vm_type)
{
if (!vm_type) {
return 0;
diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c
index eb79a73fa6..9c865bce67 100644
--- a/hw/watchdog/cmsdk-apb-watchdog.c
+++ b/hw/watchdog/cmsdk-apb-watchdog.c
@@ -14,6 +14,10 @@
* System Design Kit (CMSDK) and documented in the Cortex-M System
* Design Kit Technical Reference Manual (ARM DDI0479C):
* https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
+ *
+ * We also support the variant of this device found in the TI
+ * Stellaris/Luminary boards and documented in:
+ * http://www.ti.com/lit/ds/symlink/lm3s6965.pdf
*/
#include "qemu/osdep.h"
@@ -37,6 +41,7 @@ REG32(WDOGINTCLR, 0xc)
REG32(WDOGRIS, 0x10)
FIELD(WDOGRIS, INT, 0, 1)
REG32(WDOGMIS, 0x14)
+REG32(WDOGTEST, 0x418) /* only in Stellaris/Luminary version of the device */
REG32(WDOGLOCK, 0xc00)
#define WDOG_UNLOCK_VALUE 0x1ACCE551
REG32(WDOGITCR, 0xf00)
@@ -61,12 +66,18 @@ REG32(CID2, 0xff8)
REG32(CID3, 0xffc)
/* PID/CID values */
-static const int watchdog_id[] = {
+static const uint32_t cmsdk_apb_watchdog_id[] = {
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
0x24, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
+static const uint32_t luminary_watchdog_id[] = {
+ 0x00, 0x00, 0x00, 0x00, /* PID4..PID7 */
+ 0x05, 0x18, 0x18, 0x01, /* PID0..PID3 */
+ 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
+};
+
static bool cmsdk_apb_watchdog_intstatus(CMSDKAPBWatchdog *s)
{
/* Return masked interrupt status */
@@ -85,6 +96,10 @@ static void cmsdk_apb_watchdog_update(CMSDKAPBWatchdog *s)
bool wdogres;
if (s->itcr) {
+ /*
+ * Not checking that !s->is_luminary since s->itcr can't be written
+ * when s->is_luminary in the first place.
+ */
wdogint = s->itop & R_WDOGITOP_WDOGINT_MASK;
wdogres = s->itop & R_WDOGITOP_WDOGRES_MASK;
} else {
@@ -124,19 +139,34 @@ static uint64_t cmsdk_apb_watchdog_read(void *opaque, hwaddr offset,
r = s->lock;
break;
case A_WDOGITCR:
+ if (s->is_luminary) {
+ goto bad_offset;
+ }
r = s->itcr;
break;
case A_PID4 ... A_CID3:
- r = watchdog_id[(offset - A_PID4) / 4];
+ r = s->id[(offset - A_PID4) / 4];
break;
case A_WDOGINTCLR:
case A_WDOGITOP:
+ if (s->is_luminary) {
+ goto bad_offset;
+ }
qemu_log_mask(LOG_GUEST_ERROR,
"CMSDK APB watchdog read: read of WO offset %x\n",
(int)offset);
r = 0;
break;
+ case A_WDOGTEST:
+ if (!s->is_luminary) {
+ goto bad_offset;
+ }
+ qemu_log_mask(LOG_UNIMP,
+ "Luminary watchdog read: stall not implemented\n");
+ r = 0;
+ break;
default:
+bad_offset:
qemu_log_mask(LOG_GUEST_ERROR,
"CMSDK APB watchdog read: bad offset %x\n", (int)offset);
r = 0;
@@ -170,6 +200,14 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset,
ptimer_run(s->timer, 0);
break;
case A_WDOGCONTROL:
+ if (s->is_luminary && 0 != (R_WDOGCONTROL_INTEN_MASK & s->control)) {
+ /*
+ * The Luminary version of this device ignores writes to
+ * this register after the guest has enabled interrupts
+ * (so they can only be disabled again via reset).
+ */
+ break;
+ }
s->control = value & R_WDOGCONTROL_VALID_MASK;
cmsdk_apb_watchdog_update(s);
break;
@@ -182,10 +220,16 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset,
s->lock = (value != WDOG_UNLOCK_VALUE);
break;
case A_WDOGITCR:
+ if (s->is_luminary) {
+ goto bad_offset;
+ }
s->itcr = value & R_WDOGITCR_VALID_MASK;
cmsdk_apb_watchdog_update(s);
break;
case A_WDOGITOP:
+ if (s->is_luminary) {
+ goto bad_offset;
+ }
s->itop = value & R_WDOGITOP_VALID_MASK;
cmsdk_apb_watchdog_update(s);
break;
@@ -197,7 +241,15 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset,
"CMSDK APB watchdog write: write to RO offset 0x%x\n",
(int)offset);
break;
+ case A_WDOGTEST:
+ if (!s->is_luminary) {
+ goto bad_offset;
+ }
+ qemu_log_mask(LOG_UNIMP,
+ "Luminary watchdog write: stall not implemented\n");
+ break;
default:
+bad_offset:
qemu_log_mask(LOG_GUEST_ERROR,
"CMSDK APB watchdog write: bad offset 0x%x\n",
(int)offset);
@@ -256,6 +308,9 @@ static void cmsdk_apb_watchdog_init(Object *obj)
s, "cmsdk-apb-watchdog", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->wdogint);
+
+ s->is_luminary = false;
+ s->id = cmsdk_apb_watchdog_id;
}
static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp)
@@ -318,9 +373,24 @@ static const TypeInfo cmsdk_apb_watchdog_info = {
.class_init = cmsdk_apb_watchdog_class_init,
};
+static void luminary_watchdog_init(Object *obj)
+{
+ CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(obj);
+
+ s->is_luminary = true;
+ s->id = luminary_watchdog_id;
+}
+
+static const TypeInfo luminary_watchdog_info = {
+ .name = TYPE_LUMINARY_WATCHDOG,
+ .parent = TYPE_CMSDK_APB_WATCHDOG,
+ .instance_init = luminary_watchdog_init
+};
+
static void cmsdk_apb_watchdog_register_types(void)
{
type_register_static(&cmsdk_apb_watchdog_info);
+ type_register_static(&luminary_watchdog_info);
}
type_init(cmsdk_apb_watchdog_register_types);
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 4cc57a7ef6..507517c603 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -64,7 +64,6 @@ enum {
VIRT_GIC_VCPU,
VIRT_GIC_ITS,
VIRT_GIC_REDIST,
- VIRT_GIC_REDIST2,
VIRT_SMMU,
VIRT_UART,
VIRT_MMIO,
@@ -74,12 +73,18 @@ enum {
VIRT_PCIE_MMIO,
VIRT_PCIE_PIO,
VIRT_PCIE_ECAM,
- VIRT_PCIE_ECAM_HIGH,
VIRT_PLATFORM_BUS,
- VIRT_PCIE_MMIO_HIGH,
VIRT_GPIO,
VIRT_SECURE_UART,
VIRT_SECURE_MEM,
+ VIRT_LOWMEMMAP_LAST,
+};
+
+/* indices of IO regions located after the RAM */
+enum {
+ VIRT_HIGH_GIC_REDIST2 = VIRT_LOWMEMMAP_LAST,
+ VIRT_HIGH_PCIE_ECAM,
+ VIRT_HIGH_PCIE_MMIO,
};
typedef enum VirtIOMMUType {
@@ -116,7 +121,7 @@ typedef struct {
int32_t gic_version;
VirtIOMMUType iommu;
struct arm_boot_info bootinfo;
- const MemMapEntry *memmap;
+ MemMapEntry *memmap;
const int *irqmap;
int smp_cpus;
void *fdt;
@@ -126,9 +131,10 @@ typedef struct {
uint32_t msi_phandle;
uint32_t iommu_phandle;
int psci_conduit;
+ hwaddr highest_gpa;
} VirtMachineState;
-#define VIRT_ECAM_ID(high) (high ? VIRT_PCIE_ECAM_HIGH : VIRT_PCIE_ECAM)
+#define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)
#define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
#define VIRT_MACHINE(obj) \
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 21212f0859..9690c71a6d 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -156,6 +156,9 @@ typedef struct {
* should instead use "unimplemented-device" for all memory ranges where
* the guest will attempt to probe for a device that QEMU doesn't
* implement and a stub device is required.
+ * @kvm_type:
+ * Return the type of KVM corresponding to the kvm-type string option or
+ * computed based on other criteria such as the host kernel capabilities.
*/
struct MachineClass {
/*< private >*/
@@ -171,7 +174,7 @@ struct MachineClass {
void (*init)(MachineState *state);
void (*reset)(void);
void (*hot_add_cpu)(const int64_t id, Error **errp);
- int (*kvm_type)(const char *arg);
+ int (*kvm_type)(MachineState *machine, const char *arg);
BlockInterfaceType block_default_type;
int units_per_default_bus;
diff --git a/include/hw/watchdog/cmsdk-apb-watchdog.h b/include/hw/watchdog/cmsdk-apb-watchdog.h
index ab8b5987a1..6ae9531370 100644
--- a/include/hw/watchdog/cmsdk-apb-watchdog.h
+++ b/include/hw/watchdog/cmsdk-apb-watchdog.h
@@ -38,6 +38,12 @@
#define CMSDK_APB_WATCHDOG(obj) OBJECT_CHECK(CMSDKAPBWatchdog, (obj), \
TYPE_CMSDK_APB_WATCHDOG)
+/*
+ * This shares the same struct (and cast macro) as the base
+ * cmsdk-apb-watchdog device.
+ */
+#define TYPE_LUMINARY_WATCHDOG "luminary-watchdog"
+
typedef struct CMSDKAPBWatchdog {
/*< private >*/
SysBusDevice parent_obj;
@@ -46,6 +52,7 @@ typedef struct CMSDKAPBWatchdog {
MemoryRegion iomem;
qemu_irq wdogint;
uint32_t wdogclk_frq;
+ bool is_luminary;
struct ptimer_state *timer;
uint32_t control;
@@ -54,6 +61,7 @@ typedef struct CMSDKAPBWatchdog {
uint32_t itcr;
uint32_t itop;
uint32_t resetstatus;
+ const uint32_t *id;
} CMSDKAPBWatchdog;
#endif
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index b9f7cbbdc1..6e8762b40d 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -604,6 +604,8 @@ static uint32_t get_elf_hwcap(void)
GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG);
GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM);
GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT);
+ GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB);
+ GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM);
#undef GET_FEATURE_ID
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 54b61f917b..96f0ff0ec7 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2021,6 +2021,8 @@ static void arm_max_initfn(Object *obj)
t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1);
t = FIELD_DP32(t, ID_ISAR6, DP, 1);
t = FIELD_DP32(t, ID_ISAR6, FHM, 1);
+ t = FIELD_DP32(t, ID_ISAR6, SB, 1);
+ t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1);
cpu->isar.id_isar6 = t;
t = cpu->id_mmfr4;
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 36cd365efa..5f23c62132 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1060,7 +1060,8 @@ void pmu_init(ARMCPU *cpu);
#define SCTLR_R (1U << 9) /* up to v6; RAZ in v7 */
#define SCTLR_UMA (1U << 9) /* v8 onward, AArch64 only */
#define SCTLR_F (1U << 10) /* up to v6 */
-#define SCTLR_SW (1U << 10) /* v7, RES0 in v8 */
+#define SCTLR_SW (1U << 10) /* v7 */
+#define SCTLR_EnRCTX (1U << 10) /* in v8.0-PredInv */
#define SCTLR_Z (1U << 11) /* in v7, RES1 in v8 */
#define SCTLR_EOS (1U << 11) /* v8.5-ExS */
#define SCTLR_I (1U << 12)
@@ -3042,11 +3043,20 @@ static inline bool arm_sctlr_b(CPUARMState *env)
(env->cp15.sctlr_el[1] & SCTLR_B) != 0;
}
+static inline uint64_t arm_sctlr(CPUARMState *env, int el)
+{
+ if (el == 0) {
+ /* FIXME: ARMv8.1-VHE S2 translation regime. */
+ return env->cp15.sctlr_el[1];
+ } else {
+ return env->cp15.sctlr_el[el];
+ }
+}
+
+
/* Return true if the processor is in big-endian mode. */
static inline bool arm_cpu_data_is_big_endian(CPUARMState *env)
{
- int cur_el;
-
/* In 32bit endianness is determined by looking at CPSR's E bit */
if (!is_a64(env)) {
return
@@ -3065,15 +3075,12 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env)
arm_sctlr_b(env) ||
#endif
((env->uncached_cpsr & CPSR_E) ? 1 : 0);
- }
-
- cur_el = arm_current_el(env);
+ } else {
+ int cur_el = arm_current_el(env);
+ uint64_t sctlr = arm_sctlr(env, cur_el);
- if (cur_el == 0) {
- return (env->cp15.sctlr_el[1] & SCTLR_E0E) != 0;
+ return (sctlr & (cur_el ? SCTLR_EE : SCTLR_E0E)) != 0;
}
-
- return (env->cp15.sctlr_el[cur_el] & SCTLR_EE) != 0;
}
#include "exec/cpu-all.h"
@@ -3301,6 +3308,16 @@ static inline bool isar_feature_aa32_fhm(const ARMISARegisters *id)
return FIELD_EX32(id->id_isar6, ID_ISAR6, FHM) != 0;
}
+static inline bool isar_feature_aa32_sb(const ARMISARegisters *id)
+{
+ return FIELD_EX32(id->id_isar6, ID_ISAR6, SB) != 0;
+}
+
+static inline bool isar_feature_aa32_predinv(const ARMISARegisters *id)
+{
+ return FIELD_EX32(id->id_isar6, ID_ISAR6, SPECRES) != 0;
+}
+
static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id)
{
/*
@@ -3414,6 +3431,16 @@ static inline bool isar_feature_aa64_fhm(const ARMISARegisters *id)
return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, FHM) != 0;
}
+static inline bool isar_feature_aa64_condm_4(const ARMISARegisters *id)
+{
+ return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) != 0;
+}
+
+static inline bool isar_feature_aa64_condm_5(const ARMISARegisters *id)
+{
+ return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) >= 2;
+}
+
static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id)
{
return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0;
@@ -3439,6 +3466,21 @@ static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id)
FIELD_DP64(0, ID_AA64ISAR1, GPI, 0xf))) != 0;
}
+static inline bool isar_feature_aa64_sb(const ARMISARegisters *id)
+{
+ return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SB) != 0;
+}
+
+static inline bool isar_feature_aa64_predinv(const ARMISARegisters *id)
+{
+ return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SPECRES) != 0;
+}
+
+static inline bool isar_feature_aa64_frint(const ARMISARegisters *id)
+{
+ return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FRINTTS) != 0;
+}
+
static inline bool isar_feature_aa64_fp16(const ARMISARegisters *id)
{
/* We always set the AdvSIMD and FP fields identically wrt FP16. */
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 1b0c427277..228906f267 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -309,6 +309,7 @@ static void aarch64_max_initfn(Object *obj)
t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 1);
t = FIELD_DP64(t, ID_AA64ISAR0, DP, 1);
t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 1);
+ t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* v8.5-CondM */
cpu->isar.id_aa64isar0 = t;
t = cpu->isar.id_aa64isar1;
@@ -318,6 +319,9 @@ static void aarch64_max_initfn(Object *obj)
t = FIELD_DP64(t, ID_AA64ISAR1, API, 0);
t = FIELD_DP64(t, ID_AA64ISAR1, GPA, 1);
t = FIELD_DP64(t, ID_AA64ISAR1, GPI, 0);
+ t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1);
+ t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1);
+ t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1);
cpu->isar.id_aa64isar1 = t;
t = cpu->isar.id_aa64pfr0;
@@ -349,6 +353,8 @@ static void aarch64_max_initfn(Object *obj)
u = FIELD_DP32(u, ID_ISAR6, JSCVT, 1);
u = FIELD_DP32(u, ID_ISAR6, DP, 1);
u = FIELD_DP32(u, ID_ISAR6, FHM, 1);
+ u = FIELD_DP32(u, ID_ISAR6, SB, 1);
+ u = FIELD_DP32(u, ID_ISAR6, SPECRES, 1);
cpu->isar.id_isar6 = u;
/*
diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c
index 70850e564d..796ef34b55 100644
--- a/target/arm/helper-a64.c
+++ b/target/arm/helper-a64.c
@@ -61,6 +61,36 @@ uint64_t HELPER(rbit64)(uint64_t x)
return revbit64(x);
}
+void HELPER(msr_i_spsel)(CPUARMState *env, uint32_t imm)
+{
+ update_spsel(env, imm);
+}
+
+static void daif_check(CPUARMState *env, uint32_t op,
+ uint32_t imm, uintptr_t ra)
+{
+ /* DAIF update to PSTATE. This is OK from EL0 only if UMA is set. */
+ if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UMA)) {
+ raise_exception_ra(env, EXCP_UDEF,
+ syn_aa64_sysregtrap(0, extract32(op, 0, 3),
+ extract32(op, 3, 3), 4,
+ imm, 0x1f, 0),
+ exception_target_el(env), ra);
+ }
+}
+
+void HELPER(msr_i_daifset)(CPUARMState *env, uint32_t imm)
+{
+ daif_check(env, 0x1e, imm, GETPC());
+ env->daif |= (imm << 6) & PSTATE_DAIF;
+}
+
+void HELPER(msr_i_daifclear)(CPUARMState *env, uint32_t imm)
+{
+ daif_check(env, 0x1f, imm, GETPC());
+ env->daif &= ~((imm << 6) & PSTATE_DAIF);
+}
+
/* Convert a softfloat float_relation_ (as returned by
* the float*_compare functions) to the correct ARM
* NZCV flag state.
diff --git a/target/arm/helper-a64.h b/target/arm/helper-a64.h
index aff8d6c9f3..a915c1247f 100644
--- a/target/arm/helper-a64.h
+++ b/target/arm/helper-a64.h
@@ -19,6 +19,9 @@
DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64)
DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_2(msr_i_spsel, void, env, i32)
+DEF_HELPER_2(msr_i_daifset, void, env, i32)
+DEF_HELPER_2(msr_i_daifclear, void, env, i32)
DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, ptr)
DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, ptr)
DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 1fa282a7fc..2607d39ad1 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -5719,6 +5719,50 @@ static const ARMCPRegInfo pauth_reginfo[] = {
};
#endif
+static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
+{
+ int el = arm_current_el(env);
+
+ if (el == 0) {
+ uint64_t sctlr = arm_sctlr(env, el);
+ if (!(sctlr & SCTLR_EnRCTX)) {
+ return CP_ACCESS_TRAP;
+ }
+ } else if (el == 1) {
+ uint64_t hcr = arm_hcr_el2_eff(env);
+ if (hcr & HCR_NV) {
+ return CP_ACCESS_TRAP_EL2;
+ }
+ }
+ return CP_ACCESS_OK;
+}
+
+static const ARMCPRegInfo predinv_reginfo[] = {
+ { .name = "CFP_RCTX", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 4,
+ .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv },
+ { .name = "DVP_RCTX", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 5,
+ .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv },
+ { .name = "CPP_RCTX", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 7,
+ .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv },
+ /*
+ * Note the AArch32 opcodes have a different OPC1.
+ */
+ { .name = "CFPRCTX", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 4,
+ .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv },
+ { .name = "DVPRCTX", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 5,
+ .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv },
+ { .name = "CPPRCTX", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 7,
+ .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv },
+ REGINFO_SENTINEL
+};
+
void register_cp_regs_for_features(ARMCPU *cpu)
{
/* Register all the coprocessor registers based on feature bits */
@@ -6618,6 +6662,17 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, pauth_reginfo);
}
#endif
+
+ /*
+ * While all v8.0 cpus support aarch64, QEMU does have configurations
+ * that do not set ID_AA64ISAR1, e.g. user-only qemu-arm -cpu max,
+ * which will set ID_ISAR6.
+ */
+ if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)
+ ? cpu_isar_feature(aa64_predinv, cpu)
+ : cpu_isar_feature(aa32_predinv, cpu)) {
+ define_arm_cp_regs(cpu, predinv_reginfo);
+ }
}
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
@@ -12854,12 +12909,8 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
flags = FIELD_DP32(flags, TBFLAG_A64, ZCR_LEN, zcr_len);
}
- if (current_el == 0) {
- /* FIXME: ARMv8.1-VHE S2 translation regime. */
- sctlr = env->cp15.sctlr_el[1];
- } else {
- sctlr = env->cp15.sctlr_el[current_el];
- }
+ sctlr = arm_sctlr(env, current_el);
+
if (cpu_isar_feature(aa64_pauth, cpu)) {
/*
* In order to save space in flags, we record only whether
diff --git a/target/arm/helper.h b/target/arm/helper.h
index d363904278..a09566f795 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -77,9 +77,6 @@ DEF_HELPER_2(get_cp_reg, i32, env, ptr)
DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64)
DEF_HELPER_2(get_cp_reg64, i64, env, ptr)
-DEF_HELPER_3(msr_i_pstate, void, env, i32, i32)
-DEF_HELPER_1(clear_pstate_ss, void, env)
-
DEF_HELPER_2(get_r13_banked, i32, env, i32)
DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
@@ -686,6 +683,11 @@ DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a32, TCG_CALL_NO_RWG,
DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a64, TCG_CALL_NO_RWG,
void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_2(frint32_s, TCG_CALL_NO_RWG, f32, f32, ptr)
+DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, ptr)
+DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, ptr)
+DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, ptr)
+
#ifdef TARGET_AARCH64
#include "helper-a64.h"
#include "helper-sve.h"
diff --git a/target/arm/internals.h b/target/arm/internals.h
index a4bd1becb7..587a1ddf58 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -968,4 +968,19 @@ ARMVAParameters aa64_va_parameters_both(CPUARMState *env, uint64_t va,
ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
ARMMMUIdx mmu_idx, bool data);
+static inline int exception_target_el(CPUARMState *env)
+{
+ int target_el = MAX(1, arm_current_el(env));
+
+ /*
+ * No such thing as secure EL1 if EL3 is aarch32,
+ * so update the target EL to EL3 in this case.
+ */
+ if (arm_is_secure(env) && !arm_el_is_aa64(env, 3) && target_el == 1) {
+ target_el = 3;
+ }
+
+ return target_el;
+}
+
#endif
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index e00ccf9c98..79a79f0190 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -18,6 +18,7 @@
#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
+#include "sysemu/kvm_int.h"
#include "kvm_arm.h"
#include "cpu.h"
#include "trace.h"
@@ -162,6 +163,15 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
env->features = arm_host_cpu_features.features;
}
+int kvm_arm_get_max_vm_ipa_size(MachineState *ms)
+{
+ KVMState *s = KVM_STATE(ms->accelerator);
+ int ret;
+
+ ret = kvm_check_extension(s, KVM_CAP_ARM_VM_IPA_SIZE);
+ return ret > 0 ? ret : 40;
+}
+
int kvm_arch_init(MachineState *ms, KVMState *s)
{
/* For ARM interrupt delivery is always asynchronous,
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index 6393455b1d..2a07333c61 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -208,6 +208,14 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf);
void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu);
/**
+ * kvm_arm_get_max_vm_ipa_size - Returns the number of bits in the
+ * IPA address space supported by KVM
+ *
+ * @ms: Machine state handle
+ */
+int kvm_arm_get_max_vm_ipa_size(MachineState *ms);
+
+/**
* kvm_arm_sync_mpstate_to_kvm
* @cpu: ARMCPU
*
@@ -239,6 +247,11 @@ static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
cpu->host_cpu_probe_failed = true;
}
+static inline int kvm_arm_get_max_vm_ipa_size(MachineState *ms)
+{
+ return -ENOENT;
+}
+
static inline int kvm_arm_vgic_probe(void)
{
return 0;
diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
index c998eadfaa..8698b4dc83 100644
--- a/target/arm/op_helper.c
+++ b/target/arm/op_helper.c
@@ -68,20 +68,6 @@ void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome,
cpu_loop_exit_restore(cs, ra);
}
-static int exception_target_el(CPUARMState *env)
-{
- int target_el = MAX(1, arm_current_el(env));
-
- /* No such thing as secure EL1 if EL3 is aarch32, so update the target EL
- * to EL3 in this case.
- */
- if (arm_is_secure(env) && !arm_el_is_aa64(env, 3) && target_el == 1) {
- target_el = 3;
- }
-
- return target_el;
-}
-
uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, void *vn,
uint32_t maxindex)
{
@@ -875,39 +861,6 @@ uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
return res;
}
-void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm)
-{
- /* MSR_i to update PSTATE. This is OK from EL0 only if UMA is set.
- * Note that SPSel is never OK from EL0; we rely on handle_msr_i()
- * to catch that case at translate time.
- */
- if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UMA)) {
- uint32_t syndrome = syn_aa64_sysregtrap(0, extract32(op, 0, 3),
- extract32(op, 3, 3), 4,
- imm, 0x1f, 0);
- raise_exception(env, EXCP_UDEF, syndrome, exception_target_el(env));
- }
-
- switch (op) {
- case 0x05: /* SPSel */
- update_spsel(env, imm);
- break;
- case 0x1e: /* DAIFSet */
- env->daif |= (imm << 6) & PSTATE_DAIF;
- break;
- case 0x1f: /* DAIFClear */
- env->daif &= ~((imm << 6) & PSTATE_DAIF);
- break;
- default:
- g_assert_not_reached();
- }
-}
-
-void HELPER(clear_pstate_ss)(CPUARMState *env)
-{
- env->pstate &= ~PSTATE_SS;
-}
-
void HELPER(pre_hvc)(CPUARMState *env)
{
ARMCPU *cpu = arm_env_get_cpu(env);
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index d3c8eaf089..1959046343 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -421,17 +421,6 @@ static void gen_exception_bkpt_insn(DisasContext *s, int offset,
s->base.is_jmp = DISAS_NORETURN;
}
-static void gen_ss_advance(DisasContext *s)
-{
- /* If the singlestep state is Active-not-pending, advance to
- * Active-pending.
- */
- if (s->ss_active) {
- s->pstate_ss = 0;
- gen_helper_clear_pstate_ss(cpu_env);
- }
-}
-
static void gen_step_complete_exception(DisasContext *s)
{
/* We just completed step of an insn. Move from Active-not-pending
@@ -1637,39 +1626,128 @@ static void handle_sync(DisasContext *s, uint32_t insn,
reset_btype(s);
gen_goto_tb(s, 0, s->pc);
return;
+
+ case 7: /* SB */
+ if (crm != 0 || !dc_isar_feature(aa64_sb, s)) {
+ goto do_unallocated;
+ }
+ /*
+ * TODO: There is no speculation barrier opcode for TCG;
+ * MB and end the TB instead.
+ */
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
+ gen_goto_tb(s, 0, s->pc);
+ return;
+
default:
+ do_unallocated:
unallocated_encoding(s);
return;
}
}
+static void gen_xaflag(void)
+{
+ TCGv_i32 z = tcg_temp_new_i32();
+
+ tcg_gen_setcondi_i32(TCG_COND_EQ, z, cpu_ZF, 0);
+
+ /*
+ * (!C & !Z) << 31
+ * (!(C | Z)) << 31
+ * ~((C | Z) << 31)
+ * ~-(C | Z)
+ * (C | Z) - 1
+ */
+ tcg_gen_or_i32(cpu_NF, cpu_CF, z);
+ tcg_gen_subi_i32(cpu_NF, cpu_NF, 1);
+
+ /* !(Z & C) */
+ tcg_gen_and_i32(cpu_ZF, z, cpu_CF);
+ tcg_gen_xori_i32(cpu_ZF, cpu_ZF, 1);
+
+ /* (!C & Z) << 31 -> -(Z & ~C) */
+ tcg_gen_andc_i32(cpu_VF, z, cpu_CF);
+ tcg_gen_neg_i32(cpu_VF, cpu_VF);
+
+ /* C | Z */
+ tcg_gen_or_i32(cpu_CF, cpu_CF, z);
+
+ tcg_temp_free_i32(z);
+}
+
+static void gen_axflag(void)
+{
+ tcg_gen_sari_i32(cpu_VF, cpu_VF, 31); /* V ? -1 : 0 */
+ tcg_gen_andc_i32(cpu_CF, cpu_CF, cpu_VF); /* C & !V */
+
+ /* !(Z | V) -> !(!ZF | V) -> ZF & !V -> ZF & ~VF */
+ tcg_gen_andc_i32(cpu_ZF, cpu_ZF, cpu_VF);
+
+ tcg_gen_movi_i32(cpu_NF, 0);
+ tcg_gen_movi_i32(cpu_VF, 0);
+}
+
/* MSR (immediate) - move immediate to processor state field */
static void handle_msr_i(DisasContext *s, uint32_t insn,
unsigned int op1, unsigned int op2, unsigned int crm)
{
+ TCGv_i32 t1;
int op = op1 << 3 | op2;
+
+ /* End the TB by default, chaining is ok. */
+ s->base.is_jmp = DISAS_TOO_MANY;
+
switch (op) {
+ case 0x00: /* CFINV */
+ if (crm != 0 || !dc_isar_feature(aa64_condm_4, s)) {
+ goto do_unallocated;
+ }
+ tcg_gen_xori_i32(cpu_CF, cpu_CF, 1);
+ s->base.is_jmp = DISAS_NEXT;
+ break;
+
+ case 0x01: /* XAFlag */
+ if (crm != 0 || !dc_isar_feature(aa64_condm_5, s)) {
+ goto do_unallocated;
+ }
+ gen_xaflag();
+ s->base.is_jmp = DISAS_NEXT;
+ break;
+
+ case 0x02: /* AXFlag */
+ if (crm != 0 || !dc_isar_feature(aa64_condm_5, s)) {
+ goto do_unallocated;
+ }
+ gen_axflag();
+ s->base.is_jmp = DISAS_NEXT;
+ break;
+
case 0x05: /* SPSel */
if (s->current_el == 0) {
- unallocated_encoding(s);
- return;
+ goto do_unallocated;
}
- /* fall through */
+ t1 = tcg_const_i32(crm & PSTATE_SP);
+ gen_helper_msr_i_spsel(cpu_env, t1);
+ tcg_temp_free_i32(t1);
+ break;
+
case 0x1e: /* DAIFSet */
+ t1 = tcg_const_i32(crm);
+ gen_helper_msr_i_daifset(cpu_env, t1);
+ tcg_temp_free_i32(t1);
+ break;
+
case 0x1f: /* DAIFClear */
- {
- TCGv_i32 tcg_imm = tcg_const_i32(crm);
- TCGv_i32 tcg_op = tcg_const_i32(op);
- gen_a64_set_pc_im(s->pc - 4);
- gen_helper_msr_i_pstate(cpu_env, tcg_op, tcg_imm);
- tcg_temp_free_i32(tcg_imm);
- tcg_temp_free_i32(tcg_op);
+ t1 = tcg_const_i32(crm);
+ gen_helper_msr_i_daifclear(cpu_env, t1);
+ tcg_temp_free_i32(t1);
/* For DAIFClear, exit the cpu loop to re-evaluate pending IRQs. */
- gen_a64_set_pc_im(s->pc);
- s->base.is_jmp = (op == 0x1f ? DISAS_EXIT : DISAS_JUMP);
+ s->base.is_jmp = DISAS_UPDATE;
break;
- }
+
default:
+ do_unallocated:
unallocated_encoding(s);
return;
}
@@ -1698,7 +1776,6 @@ static void gen_get_nzcv(TCGv_i64 tcg_rt)
}
static void gen_set_nzcv(TCGv_i64 tcg_rt)
-
{
TCGv_i32 nzcv = tcg_temp_new_i32();
@@ -4482,11 +4559,10 @@ static void disas_data_proc_3src(DisasContext *s, uint32_t insn)
}
/* Add/subtract (with carry)
- * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0
- * +--+--+--+------------------------+------+---------+------+-----+
- * |sf|op| S| 1 1 0 1 0 0 0 0 | rm | opcode2 | Rn | Rd |
- * +--+--+--+------------------------+------+---------+------+-----+
- * [000000]
+ * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0
+ * +--+--+--+------------------------+------+-------------+------+-----+
+ * |sf|op| S| 1 1 0 1 0 0 0 0 | rm | 0 0 0 0 0 0 | Rn | Rd |
+ * +--+--+--+------------------------+------+-------------+------+-----+
*/
static void disas_adc_sbc(DisasContext *s, uint32_t insn)
@@ -4494,11 +4570,6 @@ static void disas_adc_sbc(DisasContext *s, uint32_t insn)
unsigned int sf, op, setflags, rm, rn, rd;
TCGv_i64 tcg_y, tcg_rn, tcg_rd;
- if (extract32(insn, 10, 6) != 0) {
- unallocated_encoding(s);
- return;
- }
-
sf = extract32(insn, 31, 1);
op = extract32(insn, 30, 1);
setflags = extract32(insn, 29, 1);
@@ -4523,6 +4594,84 @@ static void disas_adc_sbc(DisasContext *s, uint32_t insn)
}
}
+/*
+ * Rotate right into flags
+ * 31 30 29 21 15 10 5 4 0
+ * +--+--+--+-----------------+--------+-----------+------+--+------+
+ * |sf|op| S| 1 1 0 1 0 0 0 0 | imm6 | 0 0 0 0 1 | Rn |o2| mask |
+ * +--+--+--+-----------------+--------+-----------+------+--+------+
+ */
+static void disas_rotate_right_into_flags(DisasContext *s, uint32_t insn)
+{
+ int mask = extract32(insn, 0, 4);
+ int o2 = extract32(insn, 4, 1);
+ int rn = extract32(insn, 5, 5);
+ int imm6 = extract32(insn, 15, 6);
+ int sf_op_s = extract32(insn, 29, 3);
+ TCGv_i64 tcg_rn;
+ TCGv_i32 nzcv;
+
+ if (sf_op_s != 5 || o2 != 0 || !dc_isar_feature(aa64_condm_4, s)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_rn = read_cpu_reg(s, rn, 1);
+ tcg_gen_rotri_i64(tcg_rn, tcg_rn, imm6);
+
+ nzcv = tcg_temp_new_i32();
+ tcg_gen_extrl_i64_i32(nzcv, tcg_rn);
+
+ if (mask & 8) { /* N */
+ tcg_gen_shli_i32(cpu_NF, nzcv, 31 - 3);
+ }
+ if (mask & 4) { /* Z */
+ tcg_gen_not_i32(cpu_ZF, nzcv);
+ tcg_gen_andi_i32(cpu_ZF, cpu_ZF, 4);
+ }
+ if (mask & 2) { /* C */
+ tcg_gen_extract_i32(cpu_CF, nzcv, 1, 1);
+ }
+ if (mask & 1) { /* V */
+ tcg_gen_shli_i32(cpu_VF, nzcv, 31 - 0);
+ }
+
+ tcg_temp_free_i32(nzcv);
+}
+
+/*
+ * Evaluate into flags
+ * 31 30 29 21 15 14 10 5 4 0
+ * +--+--+--+-----------------+---------+----+---------+------+--+------+
+ * |sf|op| S| 1 1 0 1 0 0 0 0 | opcode2 | sz | 0 0 1 0 | Rn |o3| mask |
+ * +--+--+--+-----------------+---------+----+---------+------+--+------+
+ */
+static void disas_evaluate_into_flags(DisasContext *s, uint32_t insn)
+{
+ int o3_mask = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int o2 = extract32(insn, 15, 6);
+ int sz = extract32(insn, 14, 1);
+ int sf_op_s = extract32(insn, 29, 3);
+ TCGv_i32 tmp;
+ int shift;
+
+ if (sf_op_s != 1 || o2 != 0 || o3_mask != 0xd ||
+ !dc_isar_feature(aa64_condm_4, s)) {
+ unallocated_encoding(s);
+ return;
+ }
+ shift = sz ? 16 : 24; /* SETF16 or SETF8 */
+
+ tmp = tcg_temp_new_i32();
+ tcg_gen_extrl_i64_i32(tmp, cpu_reg(s, rn));
+ tcg_gen_shli_i32(cpu_NF, tmp, shift);
+ tcg_gen_shli_i32(cpu_VF, tmp, shift - 1);
+ tcg_gen_mov_i32(cpu_ZF, cpu_NF);
+ tcg_gen_xor_i32(cpu_VF, cpu_VF, cpu_NF);
+ tcg_temp_free_i32(tmp);
+}
+
/* Conditional compare (immediate / register)
* 31 30 29 28 27 26 25 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0
* +--+--+--+------------------------+--------+------+----+--+------+--+-----+
@@ -5152,47 +5301,81 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn)
}
}
-/* Data processing - register */
+/*
+ * Data processing - register
+ * 31 30 29 28 25 21 20 16 10 0
+ * +--+---+--+---+-------+-----+-------+-------+---------+
+ * | |op0| |op1| 1 0 1 | op2 | | op3 | |
+ * +--+---+--+---+-------+-----+-------+-------+---------+
+ */
static void disas_data_proc_reg(DisasContext *s, uint32_t insn)
{
- switch (extract32(insn, 24, 5)) {
- case 0x0a: /* Logical (shifted register) */
- disas_logic_reg(s, insn);
- break;
- case 0x0b: /* Add/subtract */
- if (insn & (1 << 21)) { /* (extended register) */
- disas_add_sub_ext_reg(s, insn);
+ int op0 = extract32(insn, 30, 1);
+ int op1 = extract32(insn, 28, 1);
+ int op2 = extract32(insn, 21, 4);
+ int op3 = extract32(insn, 10, 6);
+
+ if (!op1) {
+ if (op2 & 8) {
+ if (op2 & 1) {
+ /* Add/sub (extended register) */
+ disas_add_sub_ext_reg(s, insn);
+ } else {
+ /* Add/sub (shifted register) */
+ disas_add_sub_reg(s, insn);
+ }
} else {
- disas_add_sub_reg(s, insn);
+ /* Logical (shifted register) */
+ disas_logic_reg(s, insn);
}
- break;
- case 0x1b: /* Data-processing (3 source) */
- disas_data_proc_3src(s, insn);
- break;
- case 0x1a:
- switch (extract32(insn, 21, 3)) {
- case 0x0: /* Add/subtract (with carry) */
+ return;
+ }
+
+ switch (op2) {
+ case 0x0:
+ switch (op3) {
+ case 0x00: /* Add/subtract (with carry) */
disas_adc_sbc(s, insn);
break;
- case 0x2: /* Conditional compare */
- disas_cc(s, insn); /* both imm and reg forms */
- break;
- case 0x4: /* Conditional select */
- disas_cond_select(s, insn);
+
+ case 0x01: /* Rotate right into flags */
+ case 0x21:
+ disas_rotate_right_into_flags(s, insn);
break;
- case 0x6: /* Data-processing */
- if (insn & (1 << 30)) { /* (1 source) */
- disas_data_proc_1src(s, insn);
- } else { /* (2 source) */
- disas_data_proc_2src(s, insn);
- }
+
+ case 0x02: /* Evaluate into flags */
+ case 0x12:
+ case 0x22:
+ case 0x32:
+ disas_evaluate_into_flags(s, insn);
break;
+
default:
- unallocated_encoding(s);
- break;
+ goto do_unallocated;
}
break;
+
+ case 0x2: /* Conditional compare */
+ disas_cc(s, insn); /* both imm and reg forms */
+ break;
+
+ case 0x4: /* Conditional select */
+ disas_cond_select(s, insn);
+ break;
+
+ case 0x6: /* Data-processing */
+ if (op0) { /* (1 source) */
+ disas_data_proc_1src(s, insn);
+ } else { /* (2 source) */
+ disas_data_proc_2src(s, insn);
+ }
+ break;
+ case 0x8 ... 0xf: /* (3 source) */
+ disas_data_proc_3src(s, insn);
+ break;
+
default:
+ do_unallocated:
unallocated_encoding(s);
break;
}
@@ -5505,55 +5688,73 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn)
/* Floating-point data-processing (1 source) - single precision */
static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn)
{
+ void (*gen_fpst)(TCGv_i32, TCGv_i32, TCGv_ptr);
+ TCGv_i32 tcg_op, tcg_res;
TCGv_ptr fpst;
- TCGv_i32 tcg_op;
- TCGv_i32 tcg_res;
+ int rmode = -1;
- fpst = get_fpstatus_ptr(false);
tcg_op = read_fp_sreg(s, rn);
tcg_res = tcg_temp_new_i32();
switch (opcode) {
case 0x0: /* FMOV */
tcg_gen_mov_i32(tcg_res, tcg_op);
- break;
+ goto done;
case 0x1: /* FABS */
gen_helper_vfp_abss(tcg_res, tcg_op);
- break;
+ goto done;
case 0x2: /* FNEG */
gen_helper_vfp_negs(tcg_res, tcg_op);
- break;
+ goto done;
case 0x3: /* FSQRT */
gen_helper_vfp_sqrts(tcg_res, tcg_op, cpu_env);
- break;
+ goto done;
case 0x8: /* FRINTN */
case 0x9: /* FRINTP */
case 0xa: /* FRINTM */
case 0xb: /* FRINTZ */
case 0xc: /* FRINTA */
- {
- TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7));
-
- gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
- gen_helper_rints(tcg_res, tcg_op, fpst);
-
- gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
- tcg_temp_free_i32(tcg_rmode);
+ rmode = arm_rmode_to_sf(opcode & 7);
+ gen_fpst = gen_helper_rints;
break;
- }
case 0xe: /* FRINTX */
- gen_helper_rints_exact(tcg_res, tcg_op, fpst);
+ gen_fpst = gen_helper_rints_exact;
break;
case 0xf: /* FRINTI */
- gen_helper_rints(tcg_res, tcg_op, fpst);
+ gen_fpst = gen_helper_rints;
+ break;
+ case 0x10: /* FRINT32Z */
+ rmode = float_round_to_zero;
+ gen_fpst = gen_helper_frint32_s;
+ break;
+ case 0x11: /* FRINT32X */
+ gen_fpst = gen_helper_frint32_s;
+ break;
+ case 0x12: /* FRINT64Z */
+ rmode = float_round_to_zero;
+ gen_fpst = gen_helper_frint64_s;
+ break;
+ case 0x13: /* FRINT64X */
+ gen_fpst = gen_helper_frint64_s;
break;
default:
- abort();
+ g_assert_not_reached();
}
- write_fp_sreg(s, rd, tcg_res);
-
+ fpst = get_fpstatus_ptr(false);
+ if (rmode >= 0) {
+ TCGv_i32 tcg_rmode = tcg_const_i32(rmode);
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
+ gen_fpst(tcg_res, tcg_op, fpst);
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
+ tcg_temp_free_i32(tcg_rmode);
+ } else {
+ gen_fpst(tcg_res, tcg_op, fpst);
+ }
tcg_temp_free_ptr(fpst);
+
+ done:
+ write_fp_sreg(s, rd, tcg_res);
tcg_temp_free_i32(tcg_op);
tcg_temp_free_i32(tcg_res);
}
@@ -5561,9 +5762,10 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn)
/* Floating-point data-processing (1 source) - double precision */
static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn)
{
+ void (*gen_fpst)(TCGv_i64, TCGv_i64, TCGv_ptr);
+ TCGv_i64 tcg_op, tcg_res;
TCGv_ptr fpst;
- TCGv_i64 tcg_op;
- TCGv_i64 tcg_res;
+ int rmode = -1;
switch (opcode) {
case 0x0: /* FMOV */
@@ -5571,48 +5773,65 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn)
return;
}
- fpst = get_fpstatus_ptr(false);
tcg_op = read_fp_dreg(s, rn);
tcg_res = tcg_temp_new_i64();
switch (opcode) {
case 0x1: /* FABS */
gen_helper_vfp_absd(tcg_res, tcg_op);
- break;
+ goto done;
case 0x2: /* FNEG */
gen_helper_vfp_negd(tcg_res, tcg_op);
- break;
+ goto done;
case 0x3: /* FSQRT */
gen_helper_vfp_sqrtd(tcg_res, tcg_op, cpu_env);
- break;
+ goto done;
case 0x8: /* FRINTN */
case 0x9: /* FRINTP */
case 0xa: /* FRINTM */
case 0xb: /* FRINTZ */
case 0xc: /* FRINTA */
- {
- TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7));
-
- gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
- gen_helper_rintd(tcg_res, tcg_op, fpst);
-
- gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
- tcg_temp_free_i32(tcg_rmode);
+ rmode = arm_rmode_to_sf(opcode & 7);
+ gen_fpst = gen_helper_rintd;
break;
- }
case 0xe: /* FRINTX */
- gen_helper_rintd_exact(tcg_res, tcg_op, fpst);
+ gen_fpst = gen_helper_rintd_exact;
break;
case 0xf: /* FRINTI */
- gen_helper_rintd(tcg_res, tcg_op, fpst);
+ gen_fpst = gen_helper_rintd;
+ break;
+ case 0x10: /* FRINT32Z */
+ rmode = float_round_to_zero;
+ gen_fpst = gen_helper_frint32_d;
+ break;
+ case 0x11: /* FRINT32X */
+ gen_fpst = gen_helper_frint32_d;
+ break;
+ case 0x12: /* FRINT64Z */
+ rmode = float_round_to_zero;
+ gen_fpst = gen_helper_frint64_d;
+ break;
+ case 0x13: /* FRINT64X */
+ gen_fpst = gen_helper_frint64_d;
break;
default:
- abort();
+ g_assert_not_reached();
}
- write_fp_dreg(s, rd, tcg_res);
-
+ fpst = get_fpstatus_ptr(false);
+ if (rmode >= 0) {
+ TCGv_i32 tcg_rmode = tcg_const_i32(rmode);
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
+ gen_fpst(tcg_res, tcg_op, fpst);
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
+ tcg_temp_free_i32(tcg_rmode);
+ } else {
+ gen_fpst(tcg_res, tcg_op, fpst);
+ }
tcg_temp_free_ptr(fpst);
+
+ done:
+ write_fp_dreg(s, rd, tcg_res);
tcg_temp_free_i64(tcg_op);
tcg_temp_free_i64(tcg_res);
}
@@ -5731,6 +5950,13 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn)
handle_fp_fcvt(s, opcode, rd, rn, dtype, type);
break;
}
+
+ case 0x10 ... 0x13: /* FRINT{32,64}{X,Z} */
+ if (type > 1 || !dc_isar_feature(aa64_frint, s)) {
+ unallocated_encoding(s);
+ return;
+ }
+ /* fall through */
case 0x0 ... 0x3:
case 0x8 ... 0xc:
case 0xe ... 0xf:
@@ -5740,14 +5966,12 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn)
if (!fp_access_check(s)) {
return;
}
-
handle_fp_1src_single(s, opcode, rd, rn);
break;
case 1:
if (!fp_access_check(s)) {
return;
}
-
handle_fp_1src_double(s, opcode, rd, rn);
break;
case 3:
@@ -5759,13 +5983,13 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn)
if (!fp_access_check(s)) {
return;
}
-
handle_fp_1src_half(s, opcode, rd, rn);
break;
default:
unallocated_encoding(s);
}
break;
+
default:
unallocated_encoding(s);
break;
@@ -9293,6 +9517,14 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u,
case 0x59: /* FRINTX */
gen_helper_rintd_exact(tcg_rd, tcg_rn, tcg_fpstatus);
break;
+ case 0x1e: /* FRINT32Z */
+ case 0x5e: /* FRINT32X */
+ gen_helper_frint32_d(tcg_rd, tcg_rn, tcg_fpstatus);
+ break;
+ case 0x1f: /* FRINT64Z */
+ case 0x5f: /* FRINT64X */
+ gen_helper_frint64_d(tcg_rd, tcg_rn, tcg_fpstatus);
+ break;
default:
g_assert_not_reached();
}
@@ -11943,8 +12175,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
}
break;
case 0xc ... 0xf:
- case 0x16 ... 0x1d:
- case 0x1f:
+ case 0x16 ... 0x1f:
{
/* Floating point: U, size[1] and opcode indicate operation;
* size[0] indicates single or double precision.
@@ -12087,6 +12318,19 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
}
need_fpstatus = true;
break;
+ case 0x1e: /* FRINT32Z */
+ case 0x1f: /* FRINT64Z */
+ need_rmode = true;
+ rmode = FPROUNDING_ZERO;
+ /* fall through */
+ case 0x5e: /* FRINT32X */
+ case 0x5f: /* FRINT64X */
+ need_fpstatus = true;
+ if ((size == 3 && !is_q) || !dc_isar_feature(aa64_frint, s)) {
+ unallocated_encoding(s);
+ return;
+ }
+ break;
default:
unallocated_encoding(s);
return;
@@ -12252,6 +12496,14 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
case 0x7c: /* URSQRTE */
gen_helper_rsqrte_u32(tcg_res, tcg_op, tcg_fpstatus);
break;
+ case 0x1e: /* FRINT32Z */
+ case 0x5e: /* FRINT32X */
+ gen_helper_frint32_s(tcg_res, tcg_op, tcg_fpstatus);
+ break;
+ case 0x1f: /* FRINT64Z */
+ case 0x5f: /* FRINT64X */
+ gen_helper_frint64_s(tcg_res, tcg_op, tcg_fpstatus);
+ break;
default:
g_assert_not_reached();
}
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 8f7f5b95aa..d408e4d7ef 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -298,17 +298,6 @@ static void gen_exception(int excp, uint32_t syndrome, uint32_t target_el)
tcg_temp_free_i32(tcg_excp);
}
-static void gen_ss_advance(DisasContext *s)
-{
- /* If the singlestep state is Active-not-pending, advance to
- * Active-pending.
- */
- if (s->ss_active) {
- s->pstate_ss = 0;
- gen_helper_clear_pstate_ss(cpu_env);
- }
-}
-
static void gen_step_complete_exception(DisasContext *s)
{
/* We just completed step of an insn. Move from Active-not-pending
@@ -9282,6 +9271,17 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
*/
gen_goto_tb(s, 0, s->pc & ~1);
return;
+ case 7: /* sb */
+ if ((insn & 0xf) || !dc_isar_feature(aa32_sb, s)) {
+ goto illegal_op;
+ }
+ /*
+ * TODO: There is no speculation barrier opcode
+ * for TCG; MB and end the TB instead.
+ */
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
+ gen_goto_tb(s, 0, s->pc & ~1);
+ return;
default:
goto illegal_op;
}
@@ -10612,7 +10612,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
} else if (i == rn) {
loaded_var = tmp;
loaded_base = 1;
- } else if (rn == 15 && exc_return) {
+ } else if (i == 15 && exc_return) {
store_pc_exc_ret(s, tmp);
} else {
store_reg_from_load(s, i, tmp);
@@ -11900,6 +11900,17 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
*/
gen_goto_tb(s, 0, s->pc & ~1);
break;
+ case 7: /* sb */
+ if ((insn & 0xf) || !dc_isar_feature(aa32_sb, s)) {
+ goto illegal_op;
+ }
+ /*
+ * TODO: There is no speculation barrier opcode
+ * for TCG; MB and end the TB instead.
+ */
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
+ gen_goto_tb(s, 0, s->pc & ~1);
+ break;
default:
goto illegal_op;
}
diff --git a/target/arm/translate.h b/target/arm/translate.h
index f25fe75685..912cc2a4a5 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -202,6 +202,40 @@ static inline TCGv_i32 get_ahp_flag(void)
return ret;
}
+/* Set bits within PSTATE. */
+static inline void set_pstate_bits(uint32_t bits)
+{
+ TCGv_i32 p = tcg_temp_new_i32();
+
+ tcg_debug_assert(!(bits & CACHED_PSTATE_BITS));
+
+ tcg_gen_ld_i32(p, cpu_env, offsetof(CPUARMState, pstate));
+ tcg_gen_ori_i32(p, p, bits);
+ tcg_gen_st_i32(p, cpu_env, offsetof(CPUARMState, pstate));
+ tcg_temp_free_i32(p);
+}
+
+/* Clear bits within PSTATE. */
+static inline void clear_pstate_bits(uint32_t bits)
+{
+ TCGv_i32 p = tcg_temp_new_i32();
+
+ tcg_debug_assert(!(bits & CACHED_PSTATE_BITS));
+
+ tcg_gen_ld_i32(p, cpu_env, offsetof(CPUARMState, pstate));
+ tcg_gen_andi_i32(p, p, ~bits);
+ tcg_gen_st_i32(p, cpu_env, offsetof(CPUARMState, pstate));
+ tcg_temp_free_i32(p);
+}
+
+/* If the singlestep state is Active-not-pending, advance to Active-pending. */
+static inline void gen_ss_advance(DisasContext *s)
+{
+ if (s->ss_active) {
+ s->pstate_ss = 0;
+ clear_pstate_bits(PSTATE_SS);
+ }
+}
/* Vector operations shared between ARM and AArch64. */
extern const GVecGen3 bsl_op;
diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c
index cc7f9f5cb1..2468fc1629 100644
--- a/target/arm/vfp_helper.c
+++ b/target/arm/vfp_helper.c
@@ -1174,3 +1174,99 @@ uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env)
return result;
}
+
+/* Round a float32 to an integer that fits in int32_t or int64_t. */
+static float32 frint_s(float32 f, float_status *fpst, int intsize)
+{
+ int old_flags = get_float_exception_flags(fpst);
+ uint32_t exp = extract32(f, 23, 8);
+
+ if (unlikely(exp == 0xff)) {
+ /* NaN or Inf. */
+ goto overflow;
+ }
+
+ /* Round and re-extract the exponent. */
+ f = float32_round_to_int(f, fpst);
+ exp = extract32(f, 23, 8);
+
+ /* Validate the range of the result. */
+ if (exp < 126 + intsize) {
+ /* abs(F) <= INT{N}_MAX */
+ return f;
+ }
+ if (exp == 126 + intsize) {
+ uint32_t sign = extract32(f, 31, 1);
+ uint32_t frac = extract32(f, 0, 23);
+ if (sign && frac == 0) {
+ /* F == INT{N}_MIN */
+ return f;
+ }
+ }
+
+ overflow:
+ /*
+ * Raise Invalid and return INT{N}_MIN as a float. Revert any
+ * inexact exception float32_round_to_int may have raised.
+ */
+ set_float_exception_flags(old_flags | float_flag_invalid, fpst);
+ return (0x100u + 126u + intsize) << 23;
+}
+
+float32 HELPER(frint32_s)(float32 f, void *fpst)
+{
+ return frint_s(f, fpst, 32);
+}
+
+float32 HELPER(frint64_s)(float32 f, void *fpst)
+{
+ return frint_s(f, fpst, 64);
+}
+
+/* Round a float64 to an integer that fits in int32_t or int64_t. */
+static float64 frint_d(float64 f, float_status *fpst, int intsize)
+{
+ int old_flags = get_float_exception_flags(fpst);
+ uint32_t exp = extract64(f, 52, 11);
+
+ if (unlikely(exp == 0x7ff)) {
+ /* NaN or Inf. */
+ goto overflow;
+ }
+
+ /* Round and re-extract the exponent. */
+ f = float64_round_to_int(f, fpst);
+ exp = extract64(f, 52, 11);
+
+ /* Validate the range of the result. */
+ if (exp < 1022 + intsize) {
+ /* abs(F) <= INT{N}_MAX */
+ return f;
+ }
+ if (exp == 1022 + intsize) {
+ uint64_t sign = extract64(f, 63, 1);
+ uint64_t frac = extract64(f, 0, 52);
+ if (sign && frac == 0) {
+ /* F == INT{N}_MIN */
+ return f;
+ }
+ }
+
+ overflow:
+ /*
+ * Raise Invalid and return INT{N}_MIN as a float. Revert any
+ * inexact exception float64_round_to_int may have raised.
+ */
+ set_float_exception_flags(old_flags | float_flag_invalid, fpst);
+ return (uint64_t)(0x800 + 1022 + intsize) << 52;
+}
+
+float64 HELPER(frint32_d)(float64 f, void *fpst)
+{
+ return frint_d(f, fpst, 32);
+}
+
+float64 HELPER(frint64_d)(float64 f, void *fpst)
+{
+ return frint_d(f, fpst, 64);
+}
diff --git a/vl.c b/vl.c
index 502857a176..fd0d51320d 100644
--- a/vl.c
+++ b/vl.c
@@ -4239,6 +4239,9 @@ int main(int argc, char **argv, char **envp)
machine_opts = qemu_get_machine_opts();
qemu_opt_foreach(machine_opts, machine_set_property, current_machine,
&error_fatal);
+ current_machine->ram_size = ram_size;
+ current_machine->maxram_size = maxram_size;
+ current_machine->ram_slots = ram_slots;
configure_accelerator(current_machine, argv[0]);
@@ -4434,9 +4437,6 @@ int main(int argc, char **argv, char **envp)
replay_checkpoint(CHECKPOINT_INIT);
qdev_machine_init();
- current_machine->ram_size = ram_size;
- current_machine->maxram_size = maxram_size;
- current_machine->ram_slots = ram_slots;
current_machine->boot_order = boot_order;
/* parse features once if machine provides default cpu_type */