diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-03-02 14:37:10 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-03-02 14:37:10 +0000 |
commit | 86f4c7e05b1c44dbe1b329a51f311f10aef6ff34 (patch) | |
tree | 6073147f05719812e5ecb14ffd6994a66fed9a7f | |
parent | 2e7b766594e17f786a6b2e5be690bc5b43ce6036 (diff) | |
parent | e66a67bf28e1b4fce2e3d72a2610dbd48d9d3078 (diff) |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180302' into staging
target-arm queue:
* implement FCMA and RDM v8.1 and v8.3 instructions
* enable Cortex-M33 v8M core, and provide new mps2-an505 board model
that uses it
* decodetree: Propagate return value from translate subroutines
* xlnx-zynqmp: Implement the RTC device
# gpg: Signature made Fri 02 Mar 2018 11:05:40 GMT
# gpg: using RSA key 3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg: aka "Peter Maydell <pmaydell@gmail.com>"
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE
* remotes/pmaydell/tags/pull-target-arm-20180302: (39 commits)
target/arm: Enable ARM_FEATURE_V8_FCMA
target/arm: Decode t32 simd 3reg and 2reg_scalar extension
target/arm: Decode aa32 armv8.3 2-reg-index
target/arm: Decode aa32 armv8.3 3-same
target/arm: Decode aa64 armv8.3 fcmla
target/arm: Decode aa64 armv8.3 fcadd
target/arm: Add ARM_FEATURE_V8_FCMA
target/arm: Enable ARM_FEATURE_V8_RDM
target/arm: Decode aa32 armv8.1 two reg and a scalar
target/arm: Decode aa32 armv8.1 three same
target/arm: Decode aa64 armv8.1 scalar/vector x indexed element
target/arm: Decode aa64 armv8.1 three same extra
target/arm: Decode aa64 armv8.1 scalar three same extra
target/arm: Refactor disas_simd_indexed size checks
target/arm: Refactor disas_simd_indexed decode
target/arm: Add ARM_FEATURE_V8_RDM
mps2-an505: New board model: MPS2 with AN505 Cortex-M33 FPGA image
hw/arm/iotkit: Model Arm IOT Kit
hw/misc/iotkit-secctl: Add remaining simple registers
hw/misc/iotkit-secctl: Add handling for PPCs
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
45 files changed, 4659 insertions, 191 deletions
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 54f855d072..d724106bd3 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -102,8 +102,13 @@ CONFIG_STM32F205_SOC=y CONFIG_CMSDK_APB_TIMER=y CONFIG_CMSDK_APB_UART=y +CONFIG_MPS2_FPGAIO=y CONFIG_MPS2_SCC=y +CONFIG_TZ_PPC=y +CONFIG_IOTKIT=y +CONFIG_IOTKIT_SECCTL=y + CONFIG_VERSATILE_PCI=y CONFIG_VERSATILE_I2C=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 1c896bafb4..232258160a 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -19,4 +19,6 @@ obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o obj-$(CONFIG_MPS2) += mps2.o +obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o +obj-$(CONFIG_IOTKIT) += iotkit.o diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 56770a7048..f123cc7d3d 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -19,6 +19,7 @@ #include "sysemu/qtest.h" #include "qemu/error-report.h" #include "exec/address-spaces.h" +#include "target/arm/idau.h" /* Bitbanded IO. Each word corresponds to a single bit. */ @@ -162,6 +163,21 @@ static void armv7m_realize(DeviceState *dev, Error **errp) object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory", &error_abort); + if (object_property_find(OBJECT(s->cpu), "idau", NULL)) { + object_property_set_link(OBJECT(s->cpu), s->idau, "idau", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + } + if (object_property_find(OBJECT(s->cpu), "init-svtor", NULL)) { + object_property_set_uint(OBJECT(s->cpu), s->init_svtor, + "init-svtor", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + } object_property_set_bool(OBJECT(s->cpu), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); @@ -217,6 +233,8 @@ static Property armv7m_properties[] = { DEFINE_PROP_STRING("cpu-type", ARMv7MState, cpu_type), DEFINE_PROP_LINK("memory", ARMv7MState, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *), + DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -270,6 +288,9 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) uint64_t entry; uint64_t lowaddr; int big_endian; + AddressSpace *as; + int asidx; + CPUState *cs = CPU(cpu); #ifdef TARGET_WORDS_BIGENDIAN big_endian = 1; @@ -282,11 +303,19 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) exit(1); } + if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { + asidx = ARMASIdx_S; + } else { + asidx = ARMASIdx_NS; + } + as = cpu_get_address_space(cs, asidx); + if (kernel_filename) { - image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, - NULL, big_endian, EM_ARM, 1, 0); + image_size = load_elf_as(kernel_filename, NULL, NULL, &entry, &lowaddr, + NULL, big_endian, EM_ARM, 1, 0, as); if (image_size < 0) { - image_size = load_image_targphys(kernel_filename, 0, mem_size); + image_size = load_image_targphys_as(kernel_filename, 0, + mem_size, as); lowaddr = 0; } if (image_size < 0) { diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 05108bc42f..6d0c92ab88 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -36,6 +36,25 @@ #define ARM64_TEXT_OFFSET_OFFSET 8 #define ARM64_MAGIC_OFFSET 56 +static AddressSpace *arm_boot_address_space(ARMCPU *cpu, + const struct arm_boot_info *info) +{ + /* Return the address space to use for bootloader reads and writes. + * We prefer the secure address space if the CPU has it and we're + * going to boot the guest into it. + */ + int asidx; + CPUState *cs = CPU(cpu); + + if (arm_feature(&cpu->env, ARM_FEATURE_EL3) && info->secure_boot) { + asidx = ARMASIdx_S; + } else { + asidx = ARMASIdx_NS; + } + + return cpu_get_address_space(cs, asidx); +} + typedef enum { FIXUP_NONE = 0, /* do nothing */ FIXUP_TERMINATOR, /* end of insns */ @@ -125,7 +144,8 @@ static const ARMInsnFixup smpboot[] = { }; static void write_bootloader(const char *name, hwaddr addr, - const ARMInsnFixup *insns, uint32_t *fixupcontext) + const ARMInsnFixup *insns, uint32_t *fixupcontext, + AddressSpace *as) { /* Fix up the specified bootloader fragment and write it into * guest memory using rom_add_blob_fixed(). fixupcontext is @@ -164,7 +184,7 @@ static void write_bootloader(const char *name, hwaddr addr, code[i] = tswap32(insn); } - rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr); + rom_add_blob_fixed_as(name, code, len * sizeof(uint32_t), addr, as); g_free(code); } @@ -173,6 +193,7 @@ static void default_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) { uint32_t fixupcontext[FIXUP_MAX]; + AddressSpace *as = arm_boot_address_space(cpu, info); fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr; fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr; @@ -183,13 +204,14 @@ static void default_write_secondary(ARMCPU *cpu, } write_bootloader("smpboot", info->smp_loader_start, - smpboot, fixupcontext); + smpboot, fixupcontext, as); } void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, const struct arm_boot_info *info, hwaddr mvbar_addr) { + AddressSpace *as = arm_boot_address_space(cpu, info); int n; uint32_t mvbar_blob[] = { /* mvbar_addr: secure monitor vectors @@ -227,22 +249,23 @@ void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, for (n = 0; n < ARRAY_SIZE(mvbar_blob); n++) { mvbar_blob[n] = tswap32(mvbar_blob[n]); } - rom_add_blob_fixed("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob), - mvbar_addr); + rom_add_blob_fixed_as("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob), + mvbar_addr, as); for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) { board_setup_blob[n] = tswap32(board_setup_blob[n]); } - rom_add_blob_fixed("board-setup", board_setup_blob, - sizeof(board_setup_blob), info->board_setup_addr); + rom_add_blob_fixed_as("board-setup", board_setup_blob, + sizeof(board_setup_blob), info->board_setup_addr, as); } static void default_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) { + AddressSpace *as = arm_boot_address_space(cpu, info); CPUState *cs = CPU(cpu); - address_space_stl_notdirty(&address_space_memory, info->smp_bootreg_addr, + address_space_stl_notdirty(as, info->smp_bootreg_addr, 0, MEMTXATTRS_UNSPECIFIED, NULL); cpu_set_pc(cs, info->smp_loader_start); } @@ -253,12 +276,12 @@ static inline bool have_dtb(const struct arm_boot_info *info) } #define WRITE_WORD(p, value) do { \ - address_space_stl_notdirty(&address_space_memory, p, value, \ + address_space_stl_notdirty(as, p, value, \ MEMTXATTRS_UNSPECIFIED, NULL); \ p += 4; \ } while (0) -static void set_kernel_args(const struct arm_boot_info *info) +static void set_kernel_args(const struct arm_boot_info *info, AddressSpace *as) { int initrd_size = info->initrd_size; hwaddr base = info->loader_start; @@ -289,8 +312,9 @@ static void set_kernel_args(const struct arm_boot_info *info) int cmdline_size; cmdline_size = strlen(info->kernel_cmdline); - cpu_physical_memory_write(p + 8, info->kernel_cmdline, - cmdline_size + 1); + address_space_write(as, p + 8, MEMTXATTRS_UNSPECIFIED, + (const uint8_t *)info->kernel_cmdline, + cmdline_size + 1); cmdline_size = (cmdline_size >> 2) + 1; WRITE_WORD(p, cmdline_size + 2); WRITE_WORD(p, 0x54410009); @@ -304,7 +328,8 @@ static void set_kernel_args(const struct arm_boot_info *info) atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3; WRITE_WORD(p, (atag_board_len + 8) >> 2); WRITE_WORD(p, 0x414f4d50); - cpu_physical_memory_write(p, atag_board_buf, atag_board_len); + address_space_write(as, p, MEMTXATTRS_UNSPECIFIED, + atag_board_buf, atag_board_len); p += atag_board_len; } /* ATAG_END */ @@ -312,7 +337,8 @@ static void set_kernel_args(const struct arm_boot_info *info) WRITE_WORD(p, 0); } -static void set_kernel_args_old(const struct arm_boot_info *info) +static void set_kernel_args_old(const struct arm_boot_info *info, + AddressSpace *as) { hwaddr p; const char *s; @@ -380,7 +406,8 @@ static void set_kernel_args_old(const struct arm_boot_info *info) } s = info->kernel_cmdline; if (s) { - cpu_physical_memory_write(p, s, strlen(s) + 1); + address_space_write(as, p, MEMTXATTRS_UNSPECIFIED, + (const uint8_t *)s, strlen(s) + 1); } else { WRITE_WORD(p, 0); } @@ -454,6 +481,7 @@ static void fdt_add_psci_node(void *fdt) * @addr: the address to load the image at * @binfo: struct describing the boot environment * @addr_limit: upper limit of the available memory area at @addr + * @as: address space to load image to * * Load a device tree supplied by the machine or by the user with the * '-dtb' command line option, and put it at offset @addr in target @@ -470,7 +498,7 @@ static void fdt_add_psci_node(void *fdt) * Note: Must not be called unless have_dtb(binfo) is true. */ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, - hwaddr addr_limit) + hwaddr addr_limit, AddressSpace *as) { void *fdt = NULL; int size, rc; @@ -616,7 +644,7 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, /* Put the DTB into the memory map as a ROM image: this will ensure * the DTB is copied again upon reset, even if addr points into RAM. */ - rom_add_blob_fixed("dtb", fdt, size, addr); + rom_add_blob_fixed_as("dtb", fdt, size, addr, as); g_free(fdt); @@ -703,13 +731,15 @@ static void do_cpu_reset(void *opaque) } if (cs == first_cpu) { + AddressSpace *as = arm_boot_address_space(cpu, info); + cpu_set_pc(cs, info->loader_start); if (!have_dtb(info)) { if (old_param) { - set_kernel_args_old(info); + set_kernel_args_old(info, as); } else { - set_kernel_args(info); + set_kernel_args(info, as); } } } else { @@ -784,7 +814,7 @@ static int do_arm_linux_init(Object *obj, void *opaque) static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - int elf_machine) + int elf_machine, AddressSpace *as) { bool elf_is64; union { @@ -827,9 +857,9 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } } - ret = load_elf(info->kernel_filename, NULL, NULL, - pentry, lowaddr, highaddr, big_endian, elf_machine, - 1, data_swab); + ret = load_elf_as(info->kernel_filename, NULL, NULL, + pentry, lowaddr, highaddr, big_endian, elf_machine, + 1, data_swab, as); if (ret <= 0) { /* The header loaded but the image didn't */ exit(1); @@ -839,7 +869,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, - hwaddr *entry) + hwaddr *entry, AddressSpace *as) { hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR; uint8_t *buffer; @@ -874,7 +904,7 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, } *entry = mem_base + kernel_load_offset; - rom_add_blob_fixed(filename, buffer, size, *entry); + rom_add_blob_fixed_as(filename, buffer, size, *entry, as); g_free(buffer); @@ -896,6 +926,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) ARMCPU *cpu = n->cpu; struct arm_boot_info *info = container_of(n, struct arm_boot_info, load_kernel_notifier); + AddressSpace *as = arm_boot_address_space(cpu, info); /* The board code is not supposed to set secure_board_setup unless * running its code in secure mode is actually possible, and KVM @@ -913,7 +944,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) * the kernel is supposed to be loaded by the bootloader), copy the * DTB to the base of RAM for the bootloader to pick up. */ - if (load_dtb(info->loader_start, info, 0) < 0) { + if (load_dtb(info->loader_start, info, 0, as) < 0) { exit(1); } } @@ -988,7 +1019,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) /* Assume that raw images are linux kernels, and ELF images are not. */ kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr, - &elf_high_addr, elf_machine); + &elf_high_addr, elf_machine, as); if (kernel_size > 0 && have_dtb(info)) { /* If there is still some room left at the base of RAM, try and put * the DTB there like we do for images loaded with -bios or -pflash. @@ -1001,25 +1032,26 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) if (elf_low_addr < info->loader_start) { elf_low_addr = 0; } - if (load_dtb(info->loader_start, info, elf_low_addr) < 0) { + if (load_dtb(info->loader_start, info, elf_low_addr, as) < 0) { exit(1); } } } entry = elf_entry; if (kernel_size < 0) { - kernel_size = load_uimage(info->kernel_filename, &entry, NULL, - &is_linux, NULL, NULL); + kernel_size = load_uimage_as(info->kernel_filename, &entry, NULL, + &is_linux, NULL, NULL, as); } if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) { kernel_size = load_aarch64_image(info->kernel_filename, - info->loader_start, &entry); + info->loader_start, &entry, as); is_linux = 1; } else if (kernel_size < 0) { /* 32-bit ARM */ entry = info->loader_start + KERNEL_LOAD_ADDR; - kernel_size = load_image_targphys(info->kernel_filename, entry, - info->ram_size - KERNEL_LOAD_ADDR); + kernel_size = load_image_targphys_as(info->kernel_filename, entry, + info->ram_size - KERNEL_LOAD_ADDR, + as); is_linux = 1; } if (kernel_size < 0) { @@ -1031,15 +1063,16 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) uint32_t fixupcontext[FIXUP_MAX]; if (info->initrd_filename) { - initrd_size = load_ramdisk(info->initrd_filename, - info->initrd_start, - info->ram_size - - info->initrd_start); + initrd_size = load_ramdisk_as(info->initrd_filename, + info->initrd_start, + info->ram_size - info->initrd_start, + as); if (initrd_size < 0) { - initrd_size = load_image_targphys(info->initrd_filename, - info->initrd_start, - info->ram_size - - info->initrd_start); + initrd_size = load_image_targphys_as(info->initrd_filename, + info->initrd_start, + info->ram_size - + info->initrd_start, + as); } if (initrd_size < 0) { error_report("could not load initrd '%s'", @@ -1080,7 +1113,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) /* Place the DTB after the initrd in memory with alignment. */ dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, align); - if (load_dtb(dtb_start, info, 0) < 0) { + if (load_dtb(dtb_start, info, 0, as) < 0) { exit(1); } fixupcontext[FIXUP_ARGPTR] = dtb_start; @@ -1096,7 +1129,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) fixupcontext[FIXUP_ENTRYPOINT] = entry; write_bootloader("bootloader", info->loader_start, - primary_loader, fixupcontext); + primary_loader, fixupcontext, as); if (info->nb_cpus > 1) { info->write_secondary_boot(cpu, info); diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c new file mode 100644 index 0000000000..c5f0a5b98a --- /dev/null +++ b/hw/arm/iotkit.c @@ -0,0 +1,598 @@ +/* + * Arm IoT Kit + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/arm/iotkit.h" +#include "hw/misc/unimp.h" +#include "hw/arm/arm.h" + +/* Create an alias region of @size bytes starting at @base + * which mirrors the memory starting at @orig. + */ +static void make_alias(IoTKit *s, MemoryRegion *mr, const char *name, + hwaddr base, hwaddr size, hwaddr orig) +{ + memory_region_init_alias(mr, NULL, name, &s->container, orig, size); + /* The alias is even lower priority than unimplemented_device regions */ + memory_region_add_subregion_overlap(&s->container, base, mr, -1500); +} + +static void init_sysbus_child(Object *parent, const char *childname, + void *child, size_t childsize, + const char *childtype) +{ + object_initialize(child, childsize, childtype); + object_property_add_child(parent, childname, OBJECT(child), &error_abort); + qdev_set_parent_bus(DEVICE(child), sysbus_get_default()); +} + +static void irq_status_forwarder(void *opaque, int n, int level) +{ + qemu_irq destirq = opaque; + + qemu_set_irq(destirq, level); +} + +static void nsccfg_handler(void *opaque, int n, int level) +{ + IoTKit *s = IOTKIT(opaque); + + s->nsccfg = level; +} + +static void iotkit_forward_ppc(IoTKit *s, const char *ppcname, int ppcnum) +{ + /* Each of the 4 AHB and 4 APB PPCs that might be present in a + * system using the IoTKit has a collection of control lines which + * are provided by the security controller and which we want to + * expose as control lines on the IoTKit device itself, so the + * code using the IoTKit can wire them up to the PPCs. + */ + SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum]; + DeviceState *iotkitdev = DEVICE(s); + DeviceState *dev_secctl = DEVICE(&s->secctl); + DeviceState *dev_splitter = DEVICE(splitter); + char *name; + + name = g_strdup_printf("%s_nonsec", ppcname); + qdev_pass_gpios(dev_secctl, iotkitdev, name); + g_free(name); + name = g_strdup_printf("%s_ap", ppcname); + qdev_pass_gpios(dev_secctl, iotkitdev, name); + g_free(name); + name = g_strdup_printf("%s_irq_enable", ppcname); + qdev_pass_gpios(dev_secctl, iotkitdev, name); + g_free(name); + name = g_strdup_printf("%s_irq_clear", ppcname); + qdev_pass_gpios(dev_secctl, iotkitdev, name); + g_free(name); + + /* irq_status is a little more tricky, because we need to + * split it so we can send it both to the security controller + * and to our OR gate for the NVIC interrupt line. + * Connect up the splitter's outputs, and create a GPIO input + * which will pass the line state to the input splitter. + */ + name = g_strdup_printf("%s_irq_status", ppcname); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + name, 0)); + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum)); + s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0); + qdev_init_gpio_in_named_with_opaque(iotkitdev, irq_status_forwarder, + s->irq_status_in[ppcnum], name, 1); + g_free(name); +} + +static void iotkit_forward_sec_resp_cfg(IoTKit *s) +{ + /* Forward the 3rd output from the splitter device as a + * named GPIO output of the iotkit object. + */ + DeviceState *dev = DEVICE(s); + DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter); + + qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); + s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder, + s->sec_resp_cfg, 1); + qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); +} + +static void iotkit_init(Object *obj) +{ + IoTKit *s = IOTKIT(obj); + int i; + + memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX); + + init_sysbus_child(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), + TYPE_ARMV7M); + qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", + ARM_CPU_TYPE_NAME("cortex-m33")); + + init_sysbus_child(obj, "secctl", &s->secctl, sizeof(s->secctl), + TYPE_IOTKIT_SECCTL); + init_sysbus_child(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), + TYPE_TZ_PPC); + init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), + TYPE_TZ_PPC); + init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0), + TYPE_CMSDK_APB_TIMER); + init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1), + TYPE_CMSDK_APB_TIMER); + init_sysbus_child(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize(&s->ppc_irq_orgate, sizeof(s->ppc_irq_orgate), + TYPE_OR_IRQ); + object_property_add_child(obj, "ppc-irq-orgate", + OBJECT(&s->ppc_irq_orgate), &error_abort); + object_initialize(&s->sec_resp_splitter, sizeof(s->sec_resp_splitter), + TYPE_SPLIT_IRQ); + object_property_add_child(obj, "sec-resp-splitter", + OBJECT(&s->sec_resp_splitter), &error_abort); + for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { + char *name = g_strdup_printf("ppc-irq-splitter-%d", i); + SplitIRQ *splitter = &s->ppc_irq_splitter[i]; + + object_initialize(splitter, sizeof(*splitter), TYPE_SPLIT_IRQ); + object_property_add_child(obj, name, OBJECT(splitter), &error_abort); + } + init_sysbus_child(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), + TYPE_UNIMPLEMENTED_DEVICE); +} + +static void iotkit_exp_irq(void *opaque, int n, int level) +{ + IoTKit *s = IOTKIT(opaque); + + qemu_set_irq(s->exp_irqs[n], level); +} + +static void iotkit_realize(DeviceState *dev, Error **errp) +{ + IoTKit *s = IOTKIT(dev); + int i; + MemoryRegion *mr; + Error *err = NULL; + SysBusDevice *sbd_apb_ppc0; + SysBusDevice *sbd_secctl; + DeviceState *dev_apb_ppc0; + DeviceState *dev_apb_ppc1; + DeviceState *dev_secctl; + DeviceState *dev_splitter; + + if (!s->board_memory) { + error_setg(errp, "memory property was not set"); + return; + } + + if (!s->mainclk_frq) { + error_setg(errp, "MAINCLK property was not set"); + return; + } + + /* Handling of which devices should be available only to secure + * code is usually done differently for M profile than for A profile. + * Instead of putting some devices only into the secure address space, + * devices exist in both address spaces but with hard-wired security + * permissions that will cause the CPU to fault for non-secure accesses. + * + * The IoTKit has an IDAU (Implementation Defined Access Unit), + * which specifies hard-wired security permissions for different + * areas of the physical address space. For the IoTKit IDAU, the + * top 4 bits of the physical address are the IDAU region ID, and + * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS + * region, otherwise it is an S region. + * + * The various devices and RAMs are generally all mapped twice, + * once into a region that the IDAU defines as secure and once + * into a non-secure region. They sit behind either a Memory + * Protection Controller (for RAM) or a Peripheral Protection + * Controller (for devices), which allow a more fine grained + * configuration of whether non-secure accesses are permitted. + * + * (The other place that guest software can configure security + * permissions is in the architected SAU (Security Attribution + * Unit), which is entirely inside the CPU. The IDAU can upgrade + * the security attributes for a region to more restrictive than + * the SAU specifies, but cannot downgrade them.) + * + * 0x10000000..0x1fffffff alias of 0x00000000..0x0fffffff + * 0x20000000..0x2007ffff 32KB FPGA block RAM + * 0x30000000..0x3fffffff alias of 0x20000000..0x2fffffff + * 0x40000000..0x4000ffff base peripheral region 1 + * 0x40010000..0x4001ffff CPU peripherals (none for IoTKit) + * 0x40020000..0x4002ffff system control element peripherals + * 0x40080000..0x400fffff base peripheral region 2 + * 0x50000000..0x5fffffff alias of 0x40000000..0x4fffffff + */ + + memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); + + qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", s->exp_numirq + 32); + /* In real hardware the initial Secure VTOR is set from the INITSVTOR0 + * register in the IoT Kit System Control Register block, and the + * initial value of that is in turn specifiable by the FPGA that + * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, + * and simply set the CPU's init-svtor to the IoT Kit default value. + */ + qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor", 0x10000000); + object_property_set_link(OBJECT(&s->armv7m), OBJECT(&s->container), + "memory", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_link(OBJECT(&s->armv7m), OBJECT(s), "idau", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* Connect our EXP_IRQ GPIOs to the NVIC's lines 32 and up. */ + s->exp_irqs = g_new(qemu_irq, s->exp_numirq); + for (i = 0; i < s->exp_numirq; i++) { + s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32); + } + qdev_init_gpio_in_named(dev, iotkit_exp_irq, "EXP_IRQ", s->exp_numirq); + + /* Set up the big aliases first */ + make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); + make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000); + /* The 0x50000000..0x5fffffff region is not a pure alias: it has + * a few extra devices that only appear there (generally the + * control interfaces for the protection controllers). + * We implement this by mapping those devices over the top of this + * alias MR at a higher priority. + */ + make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); + + /* This RAM should be behind a Memory Protection Controller, but we + * don't implement that yet. + */ + memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(&s->container, 0x20000000, &s->sram0); + + /* Security controller */ + object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sbd_secctl = SYS_BUS_DEVICE(&s->secctl); + dev_secctl = DEVICE(&s->secctl); + sysbus_mmio_map(sbd_secctl, 0, 0x50080000); + sysbus_mmio_map(sbd_secctl, 1, 0x40080000); + + s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1); + qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in); + + /* The sec_resp_cfg output from the security controller must be split into + * multiple lines, one for each of the PPCs within the IoTKit and one + * that will be an output from the IoTKit to the system. + */ + object_property_set_int(OBJECT(&s->sec_resp_splitter), 3, + "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->sec_resp_splitter), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + dev_splitter = DEVICE(&s->sec_resp_splitter); + qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, + qdev_get_gpio_in(dev_splitter, 0)); + + /* Devices behind APB PPC0: + * 0x40000000: timer0 + * 0x40001000: timer1 + * 0x40002000: dual timer + * We must configure and realize each downstream device and connect + * it to the appropriate PPC port; then we can realize the PPC and + * map its upstream ends to the right place in the container. + */ + qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + qdev_prop_set_string(DEVICE(&s->dualtimer), "name", "Dual timer"); + qdev_prop_set_uint64(DEVICE(&s->dualtimer), "size", 0x1000); + object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0); + dev_apb_ppc0 = DEVICE(&s->apb_ppc0); + + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0); + memory_region_add_subregion(&s->container, 0x40000000, mr); + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1); + memory_region_add_subregion(&s->container, 0x40001000, mr); + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2); + memory_region_add_subregion(&s->container, 0x40002000, mr); + for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) { + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i, + qdev_get_gpio_in_named(dev_apb_ppc0, + "cfg_nonsec", i)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i, + qdev_get_gpio_in_named(dev_apb_ppc0, + "cfg_ap", i)); + } + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0, + qdev_get_gpio_in_named(dev_apb_ppc0, + "irq_enable", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0, + qdev_get_gpio_in_named(dev_apb_ppc0, + "irq_clear", 0)); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_apb_ppc0, + "cfg_sec_resp", 0)); + + /* All the PPC irq lines (from the 2 internal PPCs and the 8 external + * ones) are sent individually to the security controller, and also + * ORed together to give a single combined PPC interrupt to the NVIC. + */ + object_property_set_int(OBJECT(&s->ppc_irq_orgate), + NUM_PPCS, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 10)); + + /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ + + /* 0x40020000 .. 0x4002ffff : IoTKit system control peripheral region */ + /* Devices behind APB PPC1: + * 0x4002f000: S32K timer + */ + qdev_prop_set_string(DEVICE(&s->s32ktimer), "name", "S32KTIMER"); + qdev_prop_set_uint64(DEVICE(&s->s32ktimer), "size", 0x1000); + object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); + object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0); + memory_region_add_subregion(&s->container, 0x4002f000, mr); + + dev_apb_ppc1 = DEVICE(&s->apb_ppc1); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "cfg_nonsec", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "cfg_ap", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "irq_enable", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "irq_clear", 0)); + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in_named(dev_apb_ppc1, + "cfg_sec_resp", 0)); + + /* Using create_unimplemented_device() maps the stub into the + * system address space rather than into our container, but the + * overall effect to the guest is the same. + */ + create_unimplemented_device("SYSINFO", 0x40020000, 0x1000); + + create_unimplemented_device("SYSCONTROL", 0x50021000, 0x1000); + create_unimplemented_device("S32KWATCHDOG", 0x5002e000, 0x1000); + + /* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */ + + create_unimplemented_device("NS watchdog", 0x40081000, 0x1000); + create_unimplemented_device("S watchdog", 0x50081000, 0x1000); + + create_unimplemented_device("SRAM0 MPC", 0x50083000, 0x1000); + + for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { + Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); + + object_property_set_int(splitter, 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(splitter, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { + char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); + + iotkit_forward_ppc(s, ppcname, i); + g_free(ppcname); + } + + for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { + char *ppcname = g_strdup_printf("apb_ppcexp%d", i); + + iotkit_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); + g_free(ppcname); + } + + for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) { + /* Wire up IRQ splitter for internal PPCs */ + DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]); + char *gpioname = g_strdup_printf("apb_ppc%d_irq_status", + i - NUM_EXTERNAL_PPCS); + TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1; + + qdev_connect_gpio_out(devs, 0, + qdev_get_gpio_in_named(dev_secctl, gpioname, 0)); + qdev_connect_gpio_out(devs, 1, + qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); + qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, + qdev_get_gpio_in(devs, 0)); + } + + iotkit_forward_sec_resp_cfg(s); + + system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; +} + +static void iotkit_idau_check(IDAUInterface *ii, uint32_t address, + int *iregion, bool *exempt, bool *ns, bool *nsc) +{ + /* For IoTKit systems the IDAU responses are simple logical functions + * of the address bits. The NSC attribute is guest-adjustable via the + * NSCCFG register in the security controller. + */ + IoTKit *s = IOTKIT(ii); + int region = extract32(address, 28, 4); + + *ns = !(region & 1); + *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2)); + /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ + *exempt = (address & 0xeff00000) == 0xe0000000; + *iregion = region; +} + +static const VMStateDescription iotkit_vmstate = { + .name = "iotkit", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(nsccfg, IoTKit), + VMSTATE_END_OF_LIST() + } +}; + +static Property iotkit_properties[] = { + DEFINE_PROP_LINK("memory", IoTKit, board_memory, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_UINT32("EXP_NUMIRQ", IoTKit, exp_numirq, 64), + DEFINE_PROP_UINT32("MAINCLK", IoTKit, mainclk_frq, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void iotkit_reset(DeviceState *dev) +{ + IoTKit *s = IOTKIT(dev); + + s->nsccfg = 0; +} + +static void iotkit_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); + + dc->realize = iotkit_realize; + dc->vmsd = &iotkit_vmstate; + dc->props = iotkit_properties; + dc->reset = iotkit_reset; + iic->check = iotkit_idau_check; +} + +static const TypeInfo iotkit_info = { + .name = TYPE_IOTKIT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IoTKit), + .instance_init = iotkit_init, + .class_init = iotkit_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_IDAU_INTERFACE }, + { } + } +}; + +static void iotkit_register_types(void) +{ + type_register_static(&iotkit_info); +} + +type_init(iotkit_register_types); diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c new file mode 100644 index 0000000000..8c86cffa9e --- /dev/null +++ b/hw/arm/mps2-tz.c @@ -0,0 +1,503 @@ +/* + * ARM V2M MPS2 board emulation, trustzone aware FPGA images + * + * Copyright (c) 2017 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* The MPS2 and MPS2+ dev boards are FPGA based (the 2+ has a bigger + * FPGA but is otherwise the same as the 2). Since the CPU itself + * and most of the devices are in the FPGA, the details of the board + * as seen by the guest depend significantly on the FPGA image. + * This source file covers the following FPGA images, for TrustZone cores: + * "mps2-an505" -- Cortex-M33 as documented in ARM Application Note AN505 + * + * Links to the TRM for the board itself and to the various Application + * Notes which document the FPGA images can be found here: + * https://developer.arm.com/products/system-design/development-boards/fpga-prototyping-boards/mps2 + * + * Board TRM: + * http://infocenter.arm.com/help/topic/com.arm.doc.100112_0200_06_en/versatile_express_cortex_m_prototyping_systems_v2m_mps2_and_v2m_mps2plus_technical_reference_100112_0200_06_en.pdf + * Application Note AN505: + * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html + * + * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide + * (ARM ECM0601256) for the details of some of the device layout: + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/arm/arm.h" +#include "hw/arm/armv7m.h" +#include "hw/or-irq.h" +#include "hw/boards.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "hw/misc/unimp.h" +#include "hw/char/cmsdk-apb-uart.h" +#include "hw/timer/cmsdk-apb-timer.h" +#include "hw/misc/mps2-scc.h" +#include "hw/misc/mps2-fpgaio.h" +#include "hw/arm/iotkit.h" +#include "hw/devices.h" +#include "net/net.h" +#include "hw/core/split-irq.h" + +typedef enum MPS2TZFPGAType { + FPGA_AN505, +} MPS2TZFPGAType; + +typedef struct { + MachineClass parent; + MPS2TZFPGAType fpga_type; + uint32_t scc_id; +} MPS2TZMachineClass; + +typedef struct { + MachineState parent; + + IoTKit iotkit; + MemoryRegion psram; + MemoryRegion ssram1; + MemoryRegion ssram1_m; + MemoryRegion ssram23; + MPS2SCC scc; + MPS2FPGAIO fpgaio; + TZPPC ppc[5]; + UnimplementedDeviceState ssram_mpc[3]; + UnimplementedDeviceState spi[5]; + UnimplementedDeviceState i2c[4]; + UnimplementedDeviceState i2s_audio; + UnimplementedDeviceState gpio[5]; + UnimplementedDeviceState dma[4]; + UnimplementedDeviceState gfx; + CMSDKAPBUART uart[5]; + SplitIRQ sec_resp_splitter; + qemu_or_irq uart_irq_orgate; +} MPS2TZMachineState; + +#define TYPE_MPS2TZ_MACHINE "mps2tz" +#define TYPE_MPS2TZ_AN505_MACHINE MACHINE_TYPE_NAME("mps2-an505") + +#define MPS2TZ_MACHINE(obj) \ + OBJECT_CHECK(MPS2TZMachineState, obj, TYPE_MPS2TZ_MACHINE) +#define MPS2TZ_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MPS2TZMachineClass, obj, TYPE_MPS2TZ_MACHINE) +#define MPS2TZ_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(MPS2TZMachineClass, klass, TYPE_MPS2TZ_MACHINE) + +/* Main SYSCLK frequency in Hz */ +#define SYSCLK_FRQ 20000000 + +/* Initialize the auxiliary RAM region @mr and map it into + * the memory map at @base. + */ +static void make_ram(MemoryRegion *mr, const char *name, + hwaddr base, hwaddr size) +{ + memory_region_init_ram(mr, NULL, name, size, &error_fatal); + memory_region_add_subregion(get_system_memory(), base, mr); +} + +/* Create an alias of an entire original MemoryRegion @orig + * located at @base in the memory map. + */ +static void make_ram_alias(MemoryRegion *mr, const char *name, + MemoryRegion *orig, hwaddr base) +{ + memory_region_init_alias(mr, NULL, name, orig, 0, + memory_region_size(orig)); + memory_region_add_subregion(get_system_memory(), base, mr); +} + +static void init_sysbus_child(Object *parent, const char *childname, + void *child, size_t childsize, + const char *childtype) +{ + object_initialize(child, childsize, childtype); + object_property_add_child(parent, childname, OBJECT(child), &error_abort); + qdev_set_parent_bus(DEVICE(child), sysbus_get_default()); + +} + +/* Most of the devices in the AN505 FPGA image sit behind + * Peripheral Protection Controllers. These data structures + * define the layout of which devices sit behind which PPCs. + * The devfn for each port is a function which creates, configures + * and initializes the device, returning the MemoryRegion which + * needs to be plugged into the downstream end of the PPC port. + */ +typedef MemoryRegion *MakeDevFn(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size); + +typedef struct PPCPortInfo { + const char *name; + MakeDevFn *devfn; + void *opaque; + hwaddr addr; + hwaddr size; +} PPCPortInfo; + +typedef struct PPCInfo { + const char *name; + PPCPortInfo ports[TZ_NUM_PORTS]; +} PPCInfo; + +static MemoryRegion *make_unimp_dev(MPS2TZMachineState *mms, + void *opaque, + const char *name, hwaddr size) +{ + /* Initialize, configure and realize a TYPE_UNIMPLEMENTED_DEVICE, + * and return a pointer to its MemoryRegion. + */ + UnimplementedDeviceState *uds = opaque; + + init_sysbus_child(OBJECT(mms), name, uds, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(uds), "name", name); + qdev_prop_set_uint64(DEVICE(uds), "size", size); + object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal); + return sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0); +} + +static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + CMSDKAPBUART *uart = opaque; + int i = uart - &mms->uart[0]; + Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL; + int rxirqno = i * 2; + int txirqno = i * 2 + 1; + int combirqno = i + 10; + SysBusDevice *s; + DeviceState *iotkitdev = DEVICE(&mms->iotkit); + DeviceState *orgate_dev = DEVICE(&mms->uart_irq_orgate); + + init_sysbus_child(OBJECT(mms), name, uart, + sizeof(mms->uart[0]), TYPE_CMSDK_APB_UART); + qdev_prop_set_chr(DEVICE(uart), "chardev", uartchr); + qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", SYSCLK_FRQ); + object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal); + s = SYS_BUS_DEVICE(uart); + sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, + "EXP_IRQ", txirqno)); + sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev, + "EXP_IRQ", rxirqno)); + sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2)); + sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1)); + sysbus_connect_irq(s, 4, qdev_get_gpio_in_named(iotkitdev, + "EXP_IRQ", combirqno)); + return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0); +} + +static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + MPS2SCC *scc = opaque; + DeviceState *sccdev; + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + + object_initialize(scc, sizeof(mms->scc), TYPE_MPS2_SCC); + sccdev = DEVICE(scc); + qdev_set_parent_bus(sccdev, sysbus_get_default()); + qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); + qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008); + qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); + object_property_set_bool(OBJECT(scc), true, "realized", &error_fatal); + return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0); +} + +static MemoryRegion *make_fpgaio(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + MPS2FPGAIO *fpgaio = opaque; + + object_initialize(fpgaio, sizeof(mms->fpgaio), TYPE_MPS2_FPGAIO); + qdev_set_parent_bus(DEVICE(fpgaio), sysbus_get_default()); + object_property_set_bool(OBJECT(fpgaio), true, "realized", &error_fatal); + return sysbus_mmio_get_region(SYS_BUS_DEVICE(fpgaio), 0); +} + +static void mps2tz_common_init(MachineState *machine) +{ + MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); + MachineClass *mc = MACHINE_GET_CLASS(machine); + MemoryRegion *system_memory = get_system_memory(); + DeviceState *iotkitdev; + DeviceState *dev_splitter; + int i; + + if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { + error_report("This board can only be used with CPU %s", + mc->default_cpu_type); + exit(1); + } + + init_sysbus_child(OBJECT(machine), "iotkit", &mms->iotkit, + sizeof(mms->iotkit), TYPE_IOTKIT); + iotkitdev = DEVICE(&mms->iotkit); + object_property_set_link(OBJECT(&mms->iotkit), OBJECT(system_memory), + "memory", &error_abort); + qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", 92); + qdev_prop_set_uint32(iotkitdev, "MAINCLK", SYSCLK_FRQ); + object_property_set_bool(OBJECT(&mms->iotkit), true, "realized", + &error_fatal); + + /* The sec_resp_cfg output from the IoTKit must be split into multiple + * lines, one for each of the PPCs we create here. + */ + object_initialize(&mms->sec_resp_splitter, sizeof(mms->sec_resp_splitter), + TYPE_SPLIT_IRQ); + object_property_add_child(OBJECT(machine), "sec-resp-splitter", + OBJECT(&mms->sec_resp_splitter), &error_abort); + object_property_set_int(OBJECT(&mms->sec_resp_splitter), 5, + "num-lines", &error_fatal); + object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true, + "realized", &error_fatal); + dev_splitter = DEVICE(&mms->sec_resp_splitter); + qdev_connect_gpio_out_named(iotkitdev, "sec_resp_cfg", 0, + qdev_get_gpio_in(dev_splitter, 0)); + + /* The IoTKit sets up much of the memory layout, including + * the aliases between secure and non-secure regions in the + * address space. The FPGA itself contains: + * + * 0x00000000..0x003fffff SSRAM1 + * 0x00400000..0x007fffff alias of SSRAM1 + * 0x28000000..0x283fffff 4MB SSRAM2 + SSRAM3 + * 0x40100000..0x4fffffff AHB Master Expansion 1 interface devices + * 0x80000000..0x80ffffff 16MB PSRAM + */ + + /* The FPGA images have an odd combination of different RAMs, + * because in hardware they are different implementations and + * connected to different buses, giving varying performance/size + * tradeoffs. For QEMU they're all just RAM, though. We arbitrarily + * call the 16MB our "system memory", as it's the largest lump. + */ + memory_region_allocate_system_memory(&mms->psram, + NULL, "mps.ram", 0x01000000); + memory_region_add_subregion(system_memory, 0x80000000, &mms->psram); + + /* The SSRAM memories should all be behind Memory Protection Controllers, + * but we don't implement that yet. + */ + make_ram(&mms->ssram1, "mps.ssram1", 0x00000000, 0x00400000); + make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x00400000); + + make_ram(&mms->ssram23, "mps.ssram23", 0x28000000, 0x00400000); + + /* The overflow IRQs for all UARTs are ORed together. + * Tx, Rx and "combined" IRQs are sent to the NVIC separately. + * Create the OR gate for this. + */ + object_initialize(&mms->uart_irq_orgate, sizeof(mms->uart_irq_orgate), + TYPE_OR_IRQ); + object_property_add_child(OBJECT(mms), "uart-irq-orgate", + OBJECT(&mms->uart_irq_orgate), &error_abort); + object_property_set_int(OBJECT(&mms->uart_irq_orgate), 10, "num-lines", + &error_fatal); + object_property_set_bool(OBJECT(&mms->uart_irq_orgate), true, + "realized", &error_fatal); + qdev_connect_gpio_out(DEVICE(&mms->uart_irq_orgate), 0, + qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 15)); + + /* Most of the devices in the FPGA are behind Peripheral Protection + * Controllers. The required order for initializing things is: + * + initialize the PPC + * + initialize, configure and realize downstream devices + * + connect downstream device MemoryRegions to the PPC + * + realize the PPC + * + map the PPC's MemoryRegions to the places in the address map + * where the downstream devices should appear + * + wire up the PPC's control lines to the IoTKit object + */ + + const PPCInfo ppcs[] = { { + .name = "apb_ppcexp0", + .ports = { + { "ssram-mpc0", make_unimp_dev, &mms->ssram_mpc[0], + 0x58007000, 0x1000 }, + { "ssram-mpc1", make_unimp_dev, &mms->ssram_mpc[1], + 0x58008000, 0x1000 }, + { "ssram-mpc2", make_unimp_dev, &mms->ssram_mpc[2], + 0x58009000, 0x1000 }, + }, + }, { + .name = "apb_ppcexp1", + .ports = { + { "spi0", make_unimp_dev, &mms->spi[0], 0x40205000, 0x1000 }, + { "spi1", make_unimp_dev, &mms->spi[1], 0x40206000, 0x1000 }, + { "spi2", make_unimp_dev, &mms->spi[2], 0x40209000, 0x1000 }, + { "spi3", make_unimp_dev, &mms->spi[3], 0x4020a000, 0x1000 }, + { "spi4", make_unimp_dev, &mms->spi[4], 0x4020b000, 0x1000 }, + { "uart0", make_uart, &mms->uart[0], 0x40200000, 0x1000 }, + { "uart1", make_uart, &mms->uart[1], 0x40201000, 0x1000 }, + { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 }, + { "uart3", make_uart, &mms->uart[3], 0x40203000, 0x1000 }, + { "uart4", make_uart, &mms->uart[4], 0x40204000, 0x1000 }, + { "i2c0", make_unimp_dev, &mms->i2c[0], 0x40207000, 0x1000 }, + { "i2c1", make_unimp_dev, &mms->i2c[1], 0x40208000, 0x1000 }, + { "i2c2", make_unimp_dev, &mms->i2c[2], 0x4020c000, 0x1000 }, + { "i2c3", make_unimp_dev, &mms->i2c[3], 0x4020d000, 0x1000 }, + }, + }, { + .name = "apb_ppcexp2", + .ports = { + { "scc", make_scc, &mms->scc, 0x40300000, 0x1000 }, + { "i2s-audio", make_unimp_dev, &mms->i2s_audio, + 0x40301000, 0x1000 }, + { "fpgaio", make_fpgaio, &mms->fpgaio, 0x40302000, 0x1000 }, + }, + }, { + .name = "ahb_ppcexp0", + .ports = { + { "gfx", make_unimp_dev, &mms->gfx, 0x41000000, 0x140000 }, + { "gpio0", make_unimp_dev, &mms->gpio[0], 0x40100000, 0x1000 }, + { "gpio1", make_unimp_dev, &mms->gpio[1], 0x40101000, 0x1000 }, + { "gpio2", make_unimp_dev, &mms->gpio[2], 0x40102000, 0x1000 }, + { "gpio3", make_unimp_dev, &mms->gpio[3], 0x40103000, 0x1000 }, + { "gpio4", make_unimp_dev, &mms->gpio[4], 0x40104000, 0x1000 }, + }, + }, { + .name = "ahb_ppcexp1", + .ports = { + { "dma0", make_unimp_dev, &mms->dma[0], 0x40110000, 0x1000 }, + { "dma1", make_unimp_dev, &mms->dma[1], 0x40111000, 0x1000 }, + { "dma2", make_unimp_dev, &mms->dma[2], 0x40112000, 0x1000 }, + { "dma3", make_unimp_dev, &mms->dma[3], 0x40113000, 0x1000 }, + }, + }, + }; + + for (i = 0; i < ARRAY_SIZE(ppcs); i++) { + const PPCInfo *ppcinfo = &ppcs[i]; + TZPPC *ppc = &mms->ppc[i]; + DeviceState *ppcdev; + int port; + char *gpioname; + + init_sysbus_child(OBJECT(machine), ppcinfo->name, ppc, + sizeof(TZPPC), TYPE_TZ_PPC); + ppcdev = DEVICE(ppc); + + for (port = 0; port < TZ_NUM_PORTS; port++) { + const PPCPortInfo *pinfo = &ppcinfo->ports[port]; + MemoryRegion *mr; + char *portname; + + if (!pinfo->devfn) { + continue; + } + + mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size); + portname = g_strdup_printf("port[%d]", port); + object_property_set_link(OBJECT(ppc), OBJECT(mr), + portname, &error_fatal); + g_free(portname); + } + + object_property_set_bool(OBJECT(ppc), true, "realized", &error_fatal); + + for (port = 0; port < TZ_NUM_PORTS; port++) { + const PPCPortInfo *pinfo = &ppcinfo->ports[port]; + + if (!pinfo->devfn) { + continue; + } + sysbus_mmio_map(SYS_BUS_DEVICE(ppc), port, pinfo->addr); + + gpioname = g_strdup_printf("%s_nonsec", ppcinfo->name); + qdev_connect_gpio_out_named(iotkitdev, gpioname, port, + qdev_get_gpio_in_named(ppcdev, + "cfg_nonsec", + port)); + g_free(gpioname); + gpioname = g_strdup_printf("%s_ap", ppcinfo->name); + qdev_connect_gpio_out_named(iotkitdev, gpioname, port, + qdev_get_gpio_in_named(ppcdev, + "cfg_ap", port)); + g_free(gpioname); + } + + gpioname = g_strdup_printf("%s_irq_enable", ppcinfo->name); + qdev_connect_gpio_out_named(iotkitdev, gpioname, 0, + qdev_get_gpio_in_named(ppcdev, + "irq_enable", 0)); + g_free(gpioname); + gpioname = g_strdup_printf("%s_irq_clear", ppcinfo->name); + qdev_connect_gpio_out_named(iotkitdev, gpioname, 0, + qdev_get_gpio_in_named(ppcdev, + "irq_clear", 0)); + g_free(gpioname); + gpioname = g_strdup_printf("%s_irq_status", ppcinfo->name); + qdev_connect_gpio_out_named(ppcdev, "irq", 0, + qdev_get_gpio_in_named(iotkitdev, + gpioname, 0)); + g_free(gpioname); + + qdev_connect_gpio_out(dev_splitter, i, + qdev_get_gpio_in_named(ppcdev, + "cfg_sec_resp", 0)); + } + + /* In hardware this is a LAN9220; the LAN9118 is software compatible + * except that it doesn't support the checksum-offload feature. + * The ethernet controller is not behind a PPC. + */ + lan9118_init(&nd_table[0], 0x42000000, + qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16)); + + create_unimplemented_device("FPGA NS PC", 0x48007000, 0x1000); + + armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000); +} + +static void mps2tz_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = mps2tz_common_init; + mc->max_cpus = 1; +} + +static void mps2tz_an505_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); + + mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33"; + mmc->fpga_type = FPGA_AN505; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mmc->scc_id = 0x41040000 | (505 << 4); +} + +static const TypeInfo mps2tz_info = { + .name = TYPE_MPS2TZ_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(MPS2TZMachineState), + .class_size = sizeof(MPS2TZMachineClass), + .class_init = mps2tz_class_init, +}; + +static const TypeInfo mps2tz_an505_info = { + .name = TYPE_MPS2TZ_AN505_MACHINE, + .parent = TYPE_MPS2TZ_MACHINE, + .class_init = mps2tz_an505_class_init, +}; + +static void mps2tz_machine_init(void) +{ + type_register_static(&mps2tz_info); + type_register_static(&mps2tz_an505_info); +} + +type_init(mps2tz_machine_init); diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 4b93a3abd2..69227fd4c9 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -53,6 +53,9 @@ #define IPI_ADDR 0xFF300000 #define IPI_IRQ 64 +#define RTC_ADDR 0xffa60000 +#define RTC_IRQ 26 + #define SDHCI_CAPABILITIES 0x280737ec6481 /* Datasheet: UG1085 (v1.7) */ static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = { @@ -191,6 +194,9 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize(&s->ipi, sizeof(s->ipi), TYPE_XLNX_ZYNQMP_IPI); qdev_set_parent_bus(DEVICE(&s->ipi), sysbus_get_default()); + + object_initialize(&s->rtc, sizeof(s->rtc), TYPE_XLNX_ZYNQMP_RTC); + qdev_set_parent_bus(DEVICE(&s->rtc), sysbus_get_default()); } static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) @@ -476,6 +482,14 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->ipi), 0, IPI_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ipi), 0, gic_spi[IPI_IRQ]); + + object_property_set_bool(OBJECT(&s->rtc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, RTC_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]); } static Property xlnx_zynqmp_props[] = { diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 1240728c87..eb88ca979e 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -18,6 +18,7 @@ common-obj-$(CONFIG_FITLOADER) += loader-fit.o common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o common-obj-$(CONFIG_SOFTMMU) += register.o common-obj-$(CONFIG_SOFTMMU) += or-irq.o +common-obj-$(CONFIG_SOFTMMU) += split-irq.o common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o obj-$(CONFIG_SOFTMMU) += generic-loader.o diff --git a/hw/core/loader.c b/hw/core/loader.c index c08f130461..76b244c508 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -730,8 +730,14 @@ int load_uimage_as(const char *filename, hwaddr *ep, hwaddr *loadaddr, /* Load a ramdisk. */ int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) { + return load_ramdisk_as(filename, addr, max_sz, NULL); +} + +int load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz, + AddressSpace *as) +{ return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK, - NULL, NULL, NULL); + NULL, NULL, as); } /* Load a gzip-compressed kernel to a dynamically allocated buffer. */ diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 7ed1f431f0..f3754ee606 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -385,15 +385,17 @@ static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev, return ngl; } -void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler, - const char *name, int n) +void qdev_init_gpio_in_named_with_opaque(DeviceState *dev, + qemu_irq_handler handler, + void *opaque, + const char *name, int n) { int i; NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); assert(gpio_list->num_out == 0 || !name); gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler, - dev, n); + opaque, n); if (!name) { name = "unnamed-gpio-in"; diff --git a/hw/core/split-irq.c b/hw/core/split-irq.c new file mode 100644 index 0000000000..7e64cd4969 --- /dev/null +++ b/hw/core/split-irq.c @@ -0,0 +1,89 @@ +/* + * IRQ splitter device. + * + * Copyright (c) 2018 Linaro Limited. + * Written by Peter Maydell + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/core/split-irq.h" +#include "qapi/error.h" + +static void split_irq_handler(void *opaque, int n, int level) +{ + SplitIRQ *s = SPLIT_IRQ(opaque); + int i; + + for (i = 0; i < s->num_lines; i++) { + qemu_set_irq(s->out_irq[i], level); + } +} + +static void split_irq_init(Object *obj) +{ + qdev_init_gpio_in(DEVICE(obj), split_irq_handler, 1); +} + +static void split_irq_realize(DeviceState *dev, Error **errp) +{ + SplitIRQ *s = SPLIT_IRQ(dev); + + if (s->num_lines < 1 || s->num_lines >= MAX_SPLIT_LINES) { + error_setg(errp, + "IRQ splitter number of lines %d is not between 1 and %d", + s->num_lines, MAX_SPLIT_LINES); + return; + } + + qdev_init_gpio_out(dev, s->out_irq, s->num_lines); +} + +static Property split_irq_properties[] = { + DEFINE_PROP_UINT16("num-lines", SplitIRQ, num_lines, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void split_irq_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + /* No state to reset or migrate */ + dc->props = split_irq_properties; + dc->realize = split_irq_realize; + + /* Reason: Needs to be wired up to work */ + dc->user_creatable = false; +} + +static const TypeInfo split_irq_type_info = { + .name = TYPE_SPLIT_IRQ, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SplitIRQ), + .instance_init = split_irq_init, + .class_init = split_irq_class_init, +}; + +static void split_irq_register_types(void) +{ + type_register_static(&split_irq_type_info); +} + +type_init(split_irq_register_types) diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index f33b37a8e5..00e834d0f0 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -58,8 +58,12 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o obj-$(CONFIG_MIPS_CPS) += mips_cpc.o obj-$(CONFIG_MIPS_ITU) += mips_itu.o +obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o obj-$(CONFIG_MPS2_SCC) += mps2-scc.o +obj-$(CONFIG_TZ_PPC) += tz-ppc.o +obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o + obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o obj-$(CONFIG_AUX) += auxbus.o diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c new file mode 100644 index 0000000000..ddd1584d34 --- /dev/null +++ b/hw/misc/iotkit-secctl.c @@ -0,0 +1,704 @@ +/* + * Arm IoT Kit security controller + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/iotkit-secctl.h" + +/* Registers in the secure privilege control block */ +REG32(SECRESPCFG, 0x10) +REG32(NSCCFG, 0x14) +REG32(SECMPCINTSTATUS, 0x1c) +REG32(SECPPCINTSTAT, 0x20) +REG32(SECPPCINTCLR, 0x24) +REG32(SECPPCINTEN, 0x28) +REG32(SECMSCINTSTAT, 0x30) +REG32(SECMSCINTCLR, 0x34) +REG32(SECMSCINTEN, 0x38) +REG32(BRGINTSTAT, 0x40) +REG32(BRGINTCLR, 0x44) +REG32(BRGINTEN, 0x48) +REG32(AHBNSPPC0, 0x50) +REG32(AHBNSPPCEXP0, 0x60) +REG32(AHBNSPPCEXP1, 0x64) +REG32(AHBNSPPCEXP2, 0x68) +REG32(AHBNSPPCEXP3, 0x6c) +REG32(APBNSPPC0, 0x70) +REG32(APBNSPPC1, 0x74) +REG32(APBNSPPCEXP0, 0x80) +REG32(APBNSPPCEXP1, 0x84) +REG32(APBNSPPCEXP2, 0x88) +REG32(APBNSPPCEXP3, 0x8c) +REG32(AHBSPPPC0, 0x90) +REG32(AHBSPPPCEXP0, 0xa0) +REG32(AHBSPPPCEXP1, 0xa4) +REG32(AHBSPPPCEXP2, 0xa8) +REG32(AHBSPPPCEXP3, 0xac) +REG32(APBSPPPC0, 0xb0) +REG32(APBSPPPC1, 0xb4) +REG32(APBSPPPCEXP0, 0xc0) +REG32(APBSPPPCEXP1, 0xc4) +REG32(APBSPPPCEXP2, 0xc8) +REG32(APBSPPPCEXP3, 0xcc) +REG32(NSMSCEXP, 0xd0) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* Registers in the non-secure privilege control block */ +REG32(AHBNSPPPC0, 0x90) +REG32(AHBNSPPPCEXP0, 0xa0) +REG32(AHBNSPPPCEXP1, 0xa4) +REG32(AHBNSPPPCEXP2, 0xa8) +REG32(AHBNSPPPCEXP3, 0xac) +REG32(APBNSPPPC0, 0xb0) +REG32(APBNSPPPC1, 0xb4) +REG32(APBNSPPPCEXP0, 0xc0) +REG32(APBNSPPPCEXP1, 0xc4) +REG32(APBNSPPPCEXP2, 0xc8) +REG32(APBNSPPPCEXP3, 0xcc) +/* PID and CID registers are also present in the NS block */ + +static const uint8_t iotkit_secctl_s_idregs[] = { + 0x04, 0x00, 0x00, 0x00, + 0x52, 0xb8, 0x0b, 0x00, + 0x0d, 0xf0, 0x05, 0xb1, +}; + +static const uint8_t iotkit_secctl_ns_idregs[] = { + 0x04, 0x00, 0x00, 0x00, + 0x53, 0xb8, 0x0b, 0x00, + 0x0d, 0xf0, 0x05, 0xb1, +}; + +/* The register sets for the various PPCs (AHB internal, APB internal, + * AHB expansion, APB expansion) are all set up so that they are + * in 16-aligned blocks so offsets 0xN0, 0xN4, 0xN8, 0xNC are PPCs + * 0, 1, 2, 3 of that type, so we can convert a register address offset + * into an an index into a PPC array easily. + */ +static inline int offset_to_ppc_idx(uint32_t offset) +{ + return extract32(offset, 2, 2); +} + +typedef void PerPPCFunction(IoTKitSecCtlPPC *ppc); + +static void foreach_ppc(IoTKitSecCtl *s, PerPPCFunction *fn) +{ + int i; + + for (i = 0; i < IOTS_NUM_APB_PPC; i++) { + fn(&s->apb[i]); + } + for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { + fn(&s->apbexp[i]); + } + for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { + fn(&s->ahbexp[i]); + } +} + +static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, + uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + uint64_t r; + uint32_t offset = addr & ~0x3; + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + switch (offset) { + case A_AHBNSPPC0: + case A_AHBSPPPC0: + r = 0; + break; + case A_SECRESPCFG: + r = s->secrespcfg; + break; + case A_NSCCFG: + r = s->nsccfg; + break; + case A_SECPPCINTSTAT: + r = s->secppcintstat; + break; + case A_SECPPCINTEN: + r = s->secppcinten; + break; + case A_BRGINTSTAT: + /* QEMU's bus fabric can never report errors as it doesn't buffer + * writes, so we never report bridge interrupts. + */ + r = 0; + break; + case A_BRGINTEN: + r = s->brginten; + break; + case A_AHBNSPPCEXP0: + case A_AHBNSPPCEXP1: + case A_AHBNSPPCEXP2: + case A_AHBNSPPCEXP3: + r = s->ahbexp[offset_to_ppc_idx(offset)].ns; + break; + case A_APBNSPPC0: + case A_APBNSPPC1: + r = s->apb[offset_to_ppc_idx(offset)].ns; + break; + case A_APBNSPPCEXP0: + case A_APBNSPPCEXP1: + case A_APBNSPPCEXP2: + case A_APBNSPPCEXP3: + r = s->apbexp[offset_to_ppc_idx(offset)].ns; + break; + case A_AHBSPPPCEXP0: + case A_AHBSPPPCEXP1: + case A_AHBSPPPCEXP2: + case A_AHBSPPPCEXP3: + r = s->apbexp[offset_to_ppc_idx(offset)].sp; + break; + case A_APBSPPPC0: + case A_APBSPPPC1: + r = s->apb[offset_to_ppc_idx(offset)].sp; + break; + case A_APBSPPPCEXP0: + case A_APBSPPPCEXP1: + case A_APBSPPPCEXP2: + case A_APBSPPPCEXP3: + r = s->apbexp[offset_to_ppc_idx(offset)].sp; + break; + case A_SECMPCINTSTATUS: + case A_SECMSCINTSTAT: + case A_SECMSCINTEN: + case A_NSMSCEXP: + qemu_log_mask(LOG_UNIMP, + "IoTKit SecCtl S block read: " + "unimplemented offset 0x%x\n", offset); + r = 0; + break; + case A_PID4: + case A_PID5: + case A_PID6: + case A_PID7: + case A_PID0: + case A_PID1: + case A_PID2: + case A_PID3: + case A_CID0: + case A_CID1: + case A_CID2: + case A_CID3: + r = iotkit_secctl_s_idregs[(offset - A_PID4) / 4]; + break; + case A_SECPPCINTCLR: + case A_SECMSCINTCLR: + case A_BRGINTCLR: + qemu_log_mask(LOG_GUEST_ERROR, + "IotKit SecCtl S block read: write-only offset 0x%x\n", + offset); + r = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IotKit SecCtl S block read: bad offset 0x%x\n", offset); + r = 0; + break; + } + + if (size != 4) { + /* None of our registers are access-sensitive, so just pull the right + * byte out of the word read result. + */ + r = extract32(r, (addr & 3) * 8, size * 8); + } + + trace_iotkit_secctl_s_read(offset, r, size); + *pdata = r; + return MEMTX_OK; +} + +static void iotkit_secctl_update_ppc_ap(IoTKitSecCtlPPC *ppc) +{ + int i; + + for (i = 0; i < ppc->numports; i++) { + bool v; + + if (extract32(ppc->ns, i, 1)) { + v = extract32(ppc->nsp, i, 1); + } else { + v = extract32(ppc->sp, i, 1); + } + qemu_set_irq(ppc->ap[i], v); + } +} + +static void iotkit_secctl_ppc_ns_write(IoTKitSecCtlPPC *ppc, uint32_t value) +{ + int i; + + ppc->ns = value & MAKE_64BIT_MASK(0, ppc->numports); + for (i = 0; i < ppc->numports; i++) { + qemu_set_irq(ppc->nonsec[i], extract32(ppc->ns, i, 1)); + } + iotkit_secctl_update_ppc_ap(ppc); +} + +static void iotkit_secctl_ppc_sp_write(IoTKitSecCtlPPC *ppc, uint32_t value) +{ + ppc->sp = value & MAKE_64BIT_MASK(0, ppc->numports); + iotkit_secctl_update_ppc_ap(ppc); +} + +static void iotkit_secctl_ppc_nsp_write(IoTKitSecCtlPPC *ppc, uint32_t value) +{ + ppc->nsp = value & MAKE_64BIT_MASK(0, ppc->numports); + iotkit_secctl_update_ppc_ap(ppc); +} + +static void iotkit_secctl_ppc_update_irq_clear(IoTKitSecCtlPPC *ppc) +{ + uint32_t value = ppc->parent->secppcintstat; + + qemu_set_irq(ppc->irq_clear, extract32(value, ppc->irq_bit_offset, 1)); +} + +static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc) +{ + uint32_t value = ppc->parent->secppcinten; + + qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1)); +} + +static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, + uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + uint32_t offset = addr; + IoTKitSecCtlPPC *ppc; + + trace_iotkit_secctl_s_write(offset, value, size); + + if (size != 4) { + /* Byte and halfword writes are ignored */ + qemu_log_mask(LOG_GUEST_ERROR, + "IotKit SecCtl S block write: bad size, ignored\n"); + return MEMTX_OK; + } + + switch (offset) { + case A_NSCCFG: + s->nsccfg = value & 3; + qemu_set_irq(s->nsc_cfg_irq, s->nsccfg); + break; + case A_SECRESPCFG: + value &= 1; + s->secrespcfg = value; + qemu_set_irq(s->sec_resp_cfg, s->secrespcfg); + break; + case A_SECPPCINTCLR: + value &= 0x00f000f3; + foreach_ppc(s, iotkit_secctl_ppc_update_irq_clear); + break; + case A_SECPPCINTEN: + s->secppcinten = value & 0x00f000f3; + foreach_ppc(s, iotkit_secctl_ppc_update_irq_enable); + break; + case A_BRGINTCLR: + break; + case A_BRGINTEN: + s->brginten = value & 0xffff0000; + break; + case A_AHBNSPPCEXP0: + case A_AHBNSPPCEXP1: + case A_AHBNSPPCEXP2: + case A_AHBNSPPCEXP3: + ppc = &s->ahbexp[offset_to_ppc_idx(offset)]; + iotkit_secctl_ppc_ns_write(ppc, value); + break; + case A_APBNSPPC0: + case A_APBNSPPC1: + ppc = &s->apb[offset_to_ppc_idx(offset)]; + iotkit_secctl_ppc_ns_write(ppc, value); + break; + case A_APBNSPPCEXP0: + case A_APBNSPPCEXP1: + case A_APBNSPPCEXP2: + case A_APBNSPPCEXP3: + ppc = &s->apbexp[offset_to_ppc_idx(offset)]; + iotkit_secctl_ppc_ns_write(ppc, value); + break; + case A_AHBSPPPCEXP0: + case A_AHBSPPPCEXP1: + case A_AHBSPPPCEXP2: + case A_AHBSPPPCEXP3: + ppc = &s->ahbexp[offset_to_ppc_idx(offset)]; + iotkit_secctl_ppc_sp_write(ppc, value); + break; + case A_APBSPPPC0: + case A_APBSPPPC1: + ppc = &s->apb[offset_to_ppc_idx(offset)]; + iotkit_secctl_ppc_sp_write(ppc, value); + break; + case A_APBSPPPCEXP0: + case A_APBSPPPCEXP1: + case A_APBSPPPCEXP2: + case A_APBSPPPCEXP3: + ppc = &s->apbexp[offset_to_ppc_idx(offset)]; + iotkit_secctl_ppc_sp_write(ppc, value); + break; + case A_SECMSCINTCLR: + case A_SECMSCINTEN: + qemu_log_mask(LOG_UNIMP, + "IoTKit SecCtl S block write: " + "unimplemented offset 0x%x\n", offset); + break; + case A_SECMPCINTSTATUS: + case A_SECPPCINTSTAT: + case A_SECMSCINTSTAT: + case A_BRGINTSTAT: + case A_AHBNSPPC0: + case A_AHBSPPPC0: + case A_NSMSCEXP: + case A_PID4: + case A_PID5: + case A_PID6: + case A_PID7: + case A_PID0: + case A_PID1: + case A_PID2: + case A_PID3: + case A_CID0: + case A_CID1: + case A_CID2: + case A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SecCtl S block write: " + "read-only offset 0x%x\n", offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IotKit SecCtl S block write: bad offset 0x%x\n", + offset); + break; + } + + return MEMTX_OK; +} + +static MemTxResult iotkit_secctl_ns_read(void *opaque, hwaddr addr, + uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + uint64_t r; + uint32_t offset = addr & ~0x3; + + switch (offset) { + case A_AHBNSPPPC0: + r = 0; + break; + case A_AHBNSPPPCEXP0: + case A_AHBNSPPPCEXP1: + case A_AHBNSPPPCEXP2: + case A_AHBNSPPPCEXP3: + r = s->ahbexp[offset_to_ppc_idx(offset)].nsp; + break; + case A_APBNSPPPC0: + case A_APBNSPPPC1: + r = s->apb[offset_to_ppc_idx(offset)].nsp; + break; + case A_APBNSPPPCEXP0: + case A_APBNSPPPCEXP1: + case A_APBNSPPPCEXP2: + case A_APBNSPPPCEXP3: + r = s->apbexp[offset_to_ppc_idx(offset)].nsp; + break; + case A_PID4: + case A_PID5: + case A_PID6: + case A_PID7: + case A_PID0: + case A_PID1: + case A_PID2: + case A_PID3: + case A_CID0: + case A_CID1: + case A_CID2: + case A_CID3: + r = iotkit_secctl_ns_idregs[(offset - A_PID4) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IotKit SecCtl NS block write: bad offset 0x%x\n", + offset); + r = 0; + break; + } + + if (size != 4) { + /* None of our registers are access-sensitive, so just pull the right + * byte out of the word read result. + */ + r = extract32(r, (addr & 3) * 8, size * 8); + } + + trace_iotkit_secctl_ns_read(offset, r, size); + *pdata = r; + return MEMTX_OK; +} + +static MemTxResult iotkit_secctl_ns_write(void *opaque, hwaddr addr, + uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + uint32_t offset = addr; + IoTKitSecCtlPPC *ppc; + + trace_iotkit_secctl_ns_write(offset, value, size); + + if (size != 4) { + /* Byte and halfword writes are ignored */ + qemu_log_mask(LOG_GUEST_ERROR, + "IotKit SecCtl NS block write: bad size, ignored\n"); + return MEMTX_OK; + } + + switch (offset) { + case A_AHBNSPPPCEXP0: + case A_AHBNSPPPCEXP1: + case A_AHBNSPPPCEXP2: + case A_AHBNSPPPCEXP3: + ppc = &s->ahbexp[offset_to_ppc_idx(offset)]; + iotkit_secctl_ppc_nsp_write(ppc, value); + break; + case A_APBNSPPPC0: + case A_APBNSPPPC1: + ppc = &s->apb[offset_to_ppc_idx(offset)]; + iotkit_secctl_ppc_nsp_write(ppc, value); + break; + case A_APBNSPPPCEXP0: + case A_APBNSPPPCEXP1: + case A_APBNSPPPCEXP2: + case A_APBNSPPPCEXP3: + ppc = &s->apbexp[offset_to_ppc_idx(offset)]; + iotkit_secctl_ppc_nsp_write(ppc, value); + break; + case A_AHBNSPPPC0: + case A_PID4: + case A_PID5: + case A_PID6: + case A_PID7: + case A_PID0: + case A_PID1: + case A_PID2: + case A_PID3: + case A_CID0: + case A_CID1: + case A_CID2: + case A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SecCtl NS block write: " + "read-only offset 0x%x\n", offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IotKit SecCtl NS block write: bad offset 0x%x\n", + offset); + break; + } + + return MEMTX_OK; +} + +static const MemoryRegionOps iotkit_secctl_s_ops = { + .read_with_attrs = iotkit_secctl_s_read, + .write_with_attrs = iotkit_secctl_s_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, +}; + +static const MemoryRegionOps iotkit_secctl_ns_ops = { + .read_with_attrs = iotkit_secctl_ns_read, + .write_with_attrs = iotkit_secctl_ns_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, +}; + +static void iotkit_secctl_reset_ppc(IoTKitSecCtlPPC *ppc) +{ + ppc->ns = 0; + ppc->sp = 0; + ppc->nsp = 0; +} + +static void iotkit_secctl_reset(DeviceState *dev) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(dev); + + s->secppcintstat = 0; + s->secppcinten = 0; + s->secrespcfg = 0; + s->nsccfg = 0; + s->brginten = 0; + + foreach_ppc(s, iotkit_secctl_reset_ppc); +} + +static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level) +{ + IoTKitSecCtlPPC *ppc = opaque; + IoTKitSecCtl *s = IOTKIT_SECCTL(ppc->parent); + int irqbit = ppc->irq_bit_offset + n; + + s->secppcintstat = deposit32(s->secppcintstat, irqbit, 1, level); +} + +static void iotkit_secctl_init_ppc(IoTKitSecCtl *s, + IoTKitSecCtlPPC *ppc, + const char *name, + int numports, + int irq_bit_offset) +{ + char *gpioname; + DeviceState *dev = DEVICE(s); + + ppc->numports = numports; + ppc->irq_bit_offset = irq_bit_offset; + ppc->parent = s; + + gpioname = g_strdup_printf("%s_nonsec", name); + qdev_init_gpio_out_named(dev, ppc->nonsec, gpioname, numports); + g_free(gpioname); + gpioname = g_strdup_printf("%s_ap", name); + qdev_init_gpio_out_named(dev, ppc->ap, gpioname, numports); + g_free(gpioname); + gpioname = g_strdup_printf("%s_irq_enable", name); + qdev_init_gpio_out_named(dev, &ppc->irq_enable, gpioname, 1); + g_free(gpioname); + gpioname = g_strdup_printf("%s_irq_clear", name); + qdev_init_gpio_out_named(dev, &ppc->irq_clear, gpioname, 1); + g_free(gpioname); + gpioname = g_strdup_printf("%s_irq_status", name); + qdev_init_gpio_in_named_with_opaque(dev, iotkit_secctl_ppc_irqstatus, + ppc, gpioname, 1); + g_free(gpioname); +} + +static void iotkit_secctl_init(Object *obj) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + DeviceState *dev = DEVICE(obj); + int i; + + iotkit_secctl_init_ppc(s, &s->apb[0], "apb_ppc0", + IOTS_APB_PPC0_NUM_PORTS, 0); + iotkit_secctl_init_ppc(s, &s->apb[1], "apb_ppc1", + IOTS_APB_PPC1_NUM_PORTS, 1); + + for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { + IoTKitSecCtlPPC *ppc = &s->apbexp[i]; + char *ppcname = g_strdup_printf("apb_ppcexp%d", i); + iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 4 + i); + g_free(ppcname); + } + for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { + IoTKitSecCtlPPC *ppc = &s->ahbexp[i]; + char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); + iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 20 + i); + g_free(ppcname); + } + + qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); + qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1); + + memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops, + s, "iotkit-secctl-s-regs", 0x1000); + memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops, + s, "iotkit-secctl-ns-regs", 0x1000); + sysbus_init_mmio(sbd, &s->s_regs); + sysbus_init_mmio(sbd, &s->ns_regs); +} + +static const VMStateDescription iotkit_secctl_ppc_vmstate = { + .name = "iotkit-secctl-ppc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ns, IoTKitSecCtlPPC), + VMSTATE_UINT32(sp, IoTKitSecCtlPPC), + VMSTATE_UINT32(nsp, IoTKitSecCtlPPC), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription iotkit_secctl_vmstate = { + .name = "iotkit-secctl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(secppcintstat, IoTKitSecCtl), + VMSTATE_UINT32(secppcinten, IoTKitSecCtl), + VMSTATE_UINT32(secrespcfg, IoTKitSecCtl), + VMSTATE_UINT32(nsccfg, IoTKitSecCtl), + VMSTATE_UINT32(brginten, IoTKitSecCtl), + VMSTATE_STRUCT_ARRAY(apb, IoTKitSecCtl, IOTS_NUM_APB_PPC, 1, + iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), + VMSTATE_STRUCT_ARRAY(apbexp, IoTKitSecCtl, IOTS_NUM_APB_EXP_PPC, 1, + iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), + VMSTATE_STRUCT_ARRAY(ahbexp, IoTKitSecCtl, IOTS_NUM_AHB_EXP_PPC, 1, + iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), + VMSTATE_END_OF_LIST() + } +}; + +static void iotkit_secctl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &iotkit_secctl_vmstate; + dc->reset = iotkit_secctl_reset; +} + +static const TypeInfo iotkit_secctl_info = { + .name = TYPE_IOTKIT_SECCTL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IoTKitSecCtl), + .instance_init = iotkit_secctl_init, + .class_init = iotkit_secctl_class_init, +}; + +static void iotkit_secctl_register_types(void) +{ + type_register_static(&iotkit_secctl_info); +} + +type_init(iotkit_secctl_register_types); diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c new file mode 100644 index 0000000000..7394a057d8 --- /dev/null +++ b/hw/misc/mps2-fpgaio.c @@ -0,0 +1,176 @@ +/* + * ARM MPS2 AN505 FPGAIO emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the "FPGA system control and I/O" block found + * in the AN505 FPGA image for the MPS2 devboard. + * It is documented in AN505: + * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/mps2-fpgaio.h" + +REG32(LED0, 0) +REG32(BUTTON, 8) +REG32(CLK1HZ, 0x10) +REG32(CLK100HZ, 0x14) +REG32(COUNTER, 0x18) +REG32(PRESCALE, 0x1c) +REG32(PSCNTR, 0x20) +REG32(MISC, 0x4c) + +static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size) +{ + MPS2FPGAIO *s = MPS2_FPGAIO(opaque); + uint64_t r; + + switch (offset) { + case A_LED0: + r = s->led0; + break; + case A_BUTTON: + /* User-pressable board buttons. We don't model that, so just return + * zeroes. + */ + r = 0; + break; + case A_PRESCALE: + r = s->prescale; + break; + case A_MISC: + r = s->misc; + break; + case A_CLK1HZ: + case A_CLK100HZ: + case A_COUNTER: + case A_PSCNTR: + /* These are all upcounters of various frequencies. */ + qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n"); + r = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "MPS2 FPGAIO read: bad offset %x\n", (int) offset); + r = 0; + break; + } + + trace_mps2_fpgaio_read(offset, r, size); + return r; +} + +static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + MPS2FPGAIO *s = MPS2_FPGAIO(opaque); + + trace_mps2_fpgaio_write(offset, value, size); + + switch (offset) { + case A_LED0: + /* LED bits [1:0] control board LEDs. We don't currently have + * a mechanism for displaying this graphically, so use a trace event. + */ + trace_mps2_fpgaio_leds(value & 0x02 ? '*' : '.', + value & 0x01 ? '*' : '.'); + s->led0 = value & 0x3; + break; + case A_PRESCALE: + s->prescale = value; + break; + case A_MISC: + /* These are control bits for some of the other devices on the + * board (SPI, CLCD, etc). We don't implement that yet, so just + * make the bits read as written. + */ + qemu_log_mask(LOG_UNIMP, + "MPS2 FPGAIO: MISC control bits unimplemented\n"); + s->misc = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset); + break; + } +} + +static const MemoryRegionOps mps2_fpgaio_ops = { + .read = mps2_fpgaio_read, + .write = mps2_fpgaio_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void mps2_fpgaio_reset(DeviceState *dev) +{ + MPS2FPGAIO *s = MPS2_FPGAIO(dev); + + trace_mps2_fpgaio_reset(); + s->led0 = 0; + s->prescale = 0; + s->misc = 0; +} + +static void mps2_fpgaio_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MPS2FPGAIO *s = MPS2_FPGAIO(obj); + + memory_region_init_io(&s->iomem, obj, &mps2_fpgaio_ops, s, + "mps2-fpgaio", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription mps2_fpgaio_vmstate = { + .name = "mps2-fpgaio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(led0, MPS2FPGAIO), + VMSTATE_UINT32(prescale, MPS2FPGAIO), + VMSTATE_UINT32(misc, MPS2FPGAIO), + VMSTATE_END_OF_LIST() + } +}; + +static Property mps2_fpgaio_properties[] = { + /* Frequency of the prescale counter */ + DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mps2_fpgaio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &mps2_fpgaio_vmstate; + dc->reset = mps2_fpgaio_reset; + dc->props = mps2_fpgaio_properties; +} + +static const TypeInfo mps2_fpgaio_info = { + .name = TYPE_MPS2_FPGAIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MPS2FPGAIO), + .instance_init = mps2_fpgaio_init, + .class_init = mps2_fpgaio_class_init, +}; + +static void mps2_fpgaio_register_types(void) +{ + type_register_static(&mps2_fpgaio_info); +} + +type_init(mps2_fpgaio_register_types); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index b340d4e81c..eb5ffcc0a8 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -62,6 +62,12 @@ mps2_scc_leds(char led7, char led6, char led5, char led4, char led3, char led2, mps2_scc_cfg_write(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config write: function %d device %d data 0x%" PRIx32 mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config read: function %d device %d data 0x%" PRIx32 +# hw/misc/mps2_fpgaio.c +mps2_fpgaio_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +mps2_fpgaio_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +mps2_fpgaio_reset(void) "MPS2 FPGAIO: reset" +mps2_fpgaio_leds(char led1, char led0) "MPS2 FPGAIO LEDs: %c%c" + # hw/misc/msf2-sysreg.c msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" HWADDR_PRIx " data 0x%" PRIx32 " prev 0x%" PRIx32 msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" HWADDR_PRIx " data 0x%08" PRIx32 @@ -77,3 +83,21 @@ mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d co mos6522_set_sr_int(void) "set sr_int" mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x" + +# hw/misc/tz-ppc.c +tz_ppc_reset(void) "TZ PPC: reset" +tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d" +tz_ppc_cfg_ap(int n, int level) "TZ PPC: cfg_ap[%d] = %d" +tz_ppc_cfg_sec_resp(int level) "TZ PPC: cfg_sec_resp = %d" +tz_ppc_irq_enable(int level) "TZ PPC: int_enable = %d" +tz_ppc_irq_clear(int level) "TZ PPC: int_clear = %d" +tz_ppc_update_irq(int level) "TZ PPC: setting irq line to %d" +tz_ppc_read_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " read (secure %d user %d) blocked" +tz_ppc_write_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " write (secure %d user %d) blocked" + +# hw/misc/iotkit-secctl.c +iotkit_secctl_s_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs read: offset 0x%x data 0x%" PRIx64 " size %u" +iotkit_secctl_s_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs write: offset 0x%x data 0x%" PRIx64 " size %u" +iotkit_secctl_ns_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs read: offset 0x%x data 0x%" PRIx64 " size %u" +iotkit_secctl_ns_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs write: offset 0x%x data 0x%" PRIx64 " size %u" +iotkit_secctl_reset(void) "IoTKit SecCtl: reset" diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c new file mode 100644 index 0000000000..3dd045c15f --- /dev/null +++ b/hw/misc/tz-ppc.c @@ -0,0 +1,302 @@ +/* + * ARM TrustZone peripheral protection controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/tz-ppc.h" + +static void tz_ppc_update_irq(TZPPC *s) +{ + bool level = s->irq_status && s->irq_enable; + + trace_tz_ppc_update_irq(level); + qemu_set_irq(s->irq, level); +} + +static void tz_ppc_cfg_nonsec(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + assert(n < TZ_NUM_PORTS); + trace_tz_ppc_cfg_nonsec(n, level); + s->cfg_nonsec[n] = level; +} + +static void tz_ppc_cfg_ap(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + assert(n < TZ_NUM_PORTS); + trace_tz_ppc_cfg_ap(n, level); + s->cfg_ap[n] = level; +} + +static void tz_ppc_cfg_sec_resp(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + trace_tz_ppc_cfg_sec_resp(level); + s->cfg_sec_resp = level; +} + +static void tz_ppc_irq_enable(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + trace_tz_ppc_irq_enable(level); + s->irq_enable = level; + tz_ppc_update_irq(s); +} + +static void tz_ppc_irq_clear(void *opaque, int n, int level) +{ + TZPPC *s = TZ_PPC(opaque); + + trace_tz_ppc_irq_clear(level); + + s->irq_clear = level; + if (level) { + s->irq_status = false; + tz_ppc_update_irq(s); + } +} + +static bool tz_ppc_check(TZPPC *s, int n, MemTxAttrs attrs) +{ + /* Check whether to allow an access to port n; return true if + * the check passes, and false if the transaction must be blocked. + * If the latter, the caller must check cfg_sec_resp to determine + * whether to abort or RAZ/WI the transaction. + * The checks are: + * + nonsec_mask suppresses any check of the secure attribute + * + otherwise, block if cfg_nonsec is 1 and transaction is secure, + * or if cfg_nonsec is 0 and transaction is non-secure + * + block if transaction is usermode and cfg_ap is 0 + */ + if ((attrs.secure == s->cfg_nonsec[n] && !(s->nonsec_mask & (1 << n))) || + (attrs.user && !s->cfg_ap[n])) { + /* Block the transaction. */ + if (!s->irq_clear) { + /* Note that holding irq_clear high suppresses interrupts */ + s->irq_status = true; + tz_ppc_update_irq(s); + } + return false; + } + return true; +} + +static MemTxResult tz_ppc_read(void *opaque, hwaddr addr, uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + TZPPCPort *p = opaque; + TZPPC *s = p->ppc; + int n = p - s->port; + AddressSpace *as = &p->downstream_as; + uint64_t data; + MemTxResult res; + + if (!tz_ppc_check(s, n, attrs)) { + trace_tz_ppc_read_blocked(n, addr, attrs.secure, attrs.user); + if (s->cfg_sec_resp) { + return MEMTX_ERROR; + } else { + *pdata = 0; + return MEMTX_OK; + } + } + + switch (size) { + case 1: + data = address_space_ldub(as, addr, attrs, &res); + break; + case 2: + data = address_space_lduw_le(as, addr, attrs, &res); + break; + case 4: + data = address_space_ldl_le(as, addr, attrs, &res); + break; + case 8: + data = address_space_ldq_le(as, addr, attrs, &res); + break; + default: + g_assert_not_reached(); + } + *pdata = data; + return res; +} + +static MemTxResult tz_ppc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + TZPPCPort *p = opaque; + TZPPC *s = p->ppc; + AddressSpace *as = &p->downstream_as; + int n = p - s->port; + MemTxResult res; + + if (!tz_ppc_check(s, n, attrs)) { + trace_tz_ppc_write_blocked(n, addr, attrs.secure, attrs.user); + if (s->cfg_sec_resp) { + return MEMTX_ERROR; + } else { + return MEMTX_OK; + } + } + + switch (size) { + case 1: + address_space_stb(as, addr, val, attrs, &res); + break; + case 2: + address_space_stw_le(as, addr, val, attrs, &res); + break; + case 4: + address_space_stl_le(as, addr, val, attrs, &res); + break; + case 8: + address_space_stq_le(as, addr, val, attrs, &res); + break; + default: + g_assert_not_reached(); + } + return res; +} + +static const MemoryRegionOps tz_ppc_ops = { + .read_with_attrs = tz_ppc_read, + .write_with_attrs = tz_ppc_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void tz_ppc_reset(DeviceState *dev) +{ + TZPPC *s = TZ_PPC(dev); + + trace_tz_ppc_reset(); + s->cfg_sec_resp = false; + memset(s->cfg_nonsec, 0, sizeof(s->cfg_nonsec)); + memset(s->cfg_ap, 0, sizeof(s->cfg_ap)); +} + +static void tz_ppc_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + TZPPC *s = TZ_PPC(obj); + + qdev_init_gpio_in_named(dev, tz_ppc_cfg_nonsec, "cfg_nonsec", TZ_NUM_PORTS); + qdev_init_gpio_in_named(dev, tz_ppc_cfg_ap, "cfg_ap", TZ_NUM_PORTS); + qdev_init_gpio_in_named(dev, tz_ppc_cfg_sec_resp, "cfg_sec_resp", 1); + qdev_init_gpio_in_named(dev, tz_ppc_irq_enable, "irq_enable", 1); + qdev_init_gpio_in_named(dev, tz_ppc_irq_clear, "irq_clear", 1); + qdev_init_gpio_out_named(dev, &s->irq, "irq", 1); +} + +static void tz_ppc_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + TZPPC *s = TZ_PPC(dev); + int i; + + /* We can't create the upstream end of the port until realize, + * as we don't know the size of the MR used as the downstream until then. + */ + for (i = 0; i < TZ_NUM_PORTS; i++) { + TZPPCPort *port = &s->port[i]; + char *name; + uint64_t size; + + if (!port->downstream) { + continue; + } + + name = g_strdup_printf("tz-ppc-port[%d]", i); + + port->ppc = s; + address_space_init(&port->downstream_as, port->downstream, name); + + size = memory_region_size(port->downstream); + memory_region_init_io(&port->upstream, obj, &tz_ppc_ops, + port, name, size); + sysbus_init_mmio(sbd, &port->upstream); + g_free(name); + } +} + +static const VMStateDescription tz_ppc_vmstate = { + .name = "tz-ppc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL_ARRAY(cfg_nonsec, TZPPC, 16), + VMSTATE_BOOL_ARRAY(cfg_ap, TZPPC, 16), + VMSTATE_BOOL(cfg_sec_resp, TZPPC), + VMSTATE_BOOL(irq_enable, TZPPC), + VMSTATE_BOOL(irq_clear, TZPPC), + VMSTATE_BOOL(irq_status, TZPPC), + VMSTATE_END_OF_LIST() + } +}; + +#define DEFINE_PORT(N) \ + DEFINE_PROP_LINK("port[" #N "]", TZPPC, port[N].downstream, \ + TYPE_MEMORY_REGION, MemoryRegion *) + +static Property tz_ppc_properties[] = { + DEFINE_PROP_UINT32("NONSEC_MASK", TZPPC, nonsec_mask, 0), + DEFINE_PORT(0), + DEFINE_PORT(1), + DEFINE_PORT(2), + DEFINE_PORT(3), + DEFINE_PORT(4), + DEFINE_PORT(5), + DEFINE_PORT(6), + DEFINE_PORT(7), + DEFINE_PORT(8), + DEFINE_PORT(9), + DEFINE_PORT(10), + DEFINE_PORT(11), + DEFINE_PORT(12), + DEFINE_PORT(13), + DEFINE_PORT(14), + DEFINE_PORT(15), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tz_ppc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = tz_ppc_realize; + dc->vmsd = &tz_ppc_vmstate; + dc->reset = tz_ppc_reset; + dc->props = tz_ppc_properties; +} + +static const TypeInfo tz_ppc_info = { + .name = TYPE_TZ_PPC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TZPPC), + .instance_init = tz_ppc_init, + .class_init = tz_ppc_class_init, +}; + +static void tz_ppc_register_types(void) +{ + type_register_static(&tz_ppc_info); +} + +type_init(tz_ppc_register_types); diff --git a/hw/misc/unimp.c b/hw/misc/unimp.c index bcbb585888..1c0ba2f0a7 100644 --- a/hw/misc/unimp.c +++ b/hw/misc/unimp.c @@ -18,16 +18,6 @@ #include "qemu/log.h" #include "qapi/error.h" -#define UNIMPLEMENTED_DEVICE(obj) \ - OBJECT_CHECK(UnimplementedDeviceState, (obj), TYPE_UNIMPLEMENTED_DEVICE) - -typedef struct { - SysBusDevice parent_obj; - MemoryRegion iomem; - char *name; - uint64_t size; -} UnimplementedDeviceState; - static uint64_t unimp_read(void *opaque, hwaddr offset, unsigned size) { UnimplementedDeviceState *s = UNIMPLEMENTED_DEVICE(opaque); diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 8c19eac3b6..8b27a4b7ef 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -21,6 +21,7 @@ common-obj-$(CONFIG_IMX) += imx_epit.o common-obj-$(CONFIG_IMX) += imx_gpt.o common-obj-$(CONFIG_LM32) += lm32_timer.o common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o +common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 640722b5d1..e6e042fddb 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -60,3 +60,6 @@ systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset" + +# hw/timer/xlnx-zynqmp-rtc.c +xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d" diff --git a/hw/timer/xlnx-zynqmp-rtc.c b/hw/timer/xlnx-zynqmp-rtc.c new file mode 100644 index 0000000000..c98dc3d94e --- /dev/null +++ b/hw/timer/xlnx-zynqmp-rtc.c @@ -0,0 +1,272 @@ +/* + * QEMU model of the Xilinx ZynqMP Real Time Clock (RTC). + * + * Copyright (c) 2017 Xilinx Inc. + * + * Written-by: Alistair Francis <alistair.francis@xilinx.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/register.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "hw/ptimer.h" +#include "qemu/cutils.h" +#include "sysemu/sysemu.h" +#include "trace.h" +#include "hw/timer/xlnx-zynqmp-rtc.h" + +#ifndef XLNX_ZYNQMP_RTC_ERR_DEBUG +#define XLNX_ZYNQMP_RTC_ERR_DEBUG 0 +#endif + +static void rtc_int_update_irq(XlnxZynqMPRTC *s) +{ + bool pending = s->regs[R_RTC_INT_STATUS] & ~s->regs[R_RTC_INT_MASK]; + qemu_set_irq(s->irq_rtc_int, pending); +} + +static void addr_error_int_update_irq(XlnxZynqMPRTC *s) +{ + bool pending = s->regs[R_ADDR_ERROR] & ~s->regs[R_ADDR_ERROR_INT_MASK]; + qemu_set_irq(s->irq_addr_error_int, pending); +} + +static uint32_t rtc_get_count(XlnxZynqMPRTC *s) +{ + int64_t now = qemu_clock_get_ns(rtc_clock); + return s->tick_offset + now / NANOSECONDS_PER_SECOND; +} + +static uint64_t current_time_postr(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + return rtc_get_count(s); +} + +static void rtc_int_status_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + rtc_int_update_irq(s); +} + +static uint64_t rtc_int_en_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + s->regs[R_RTC_INT_MASK] &= (uint32_t) ~val64; + rtc_int_update_irq(s); + return 0; +} + +static uint64_t rtc_int_dis_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + s->regs[R_RTC_INT_MASK] |= (uint32_t) val64; + rtc_int_update_irq(s); + return 0; +} + +static void addr_error_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + addr_error_int_update_irq(s); +} + +static uint64_t addr_error_int_en_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + s->regs[R_ADDR_ERROR_INT_MASK] &= (uint32_t) ~val64; + addr_error_int_update_irq(s); + return 0; +} + +static uint64_t addr_error_int_dis_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + s->regs[R_ADDR_ERROR_INT_MASK] |= (uint32_t) val64; + addr_error_int_update_irq(s); + return 0; +} + +static const RegisterAccessInfo rtc_regs_info[] = { + { .name = "SET_TIME_WRITE", .addr = A_SET_TIME_WRITE, + .unimp = MAKE_64BIT_MASK(0, 32), + },{ .name = "SET_TIME_READ", .addr = A_SET_TIME_READ, + .ro = 0xffffffff, + .post_read = current_time_postr, + },{ .name = "CALIB_WRITE", .addr = A_CALIB_WRITE, + .unimp = MAKE_64BIT_MASK(0, 32), + },{ .name = "CALIB_READ", .addr = A_CALIB_READ, + .ro = 0x1fffff, + },{ .name = "CURRENT_TIME", .addr = A_CURRENT_TIME, + .ro = 0xffffffff, + .post_read = current_time_postr, + },{ .name = "CURRENT_TICK", .addr = A_CURRENT_TICK, + .ro = 0xffff, + },{ .name = "ALARM", .addr = A_ALARM, + },{ .name = "RTC_INT_STATUS", .addr = A_RTC_INT_STATUS, + .w1c = 0x3, + .post_write = rtc_int_status_postw, + },{ .name = "RTC_INT_MASK", .addr = A_RTC_INT_MASK, + .reset = 0x3, + .ro = 0x3, + },{ .name = "RTC_INT_EN", .addr = A_RTC_INT_EN, + .pre_write = rtc_int_en_prew, + },{ .name = "RTC_INT_DIS", .addr = A_RTC_INT_DIS, + .pre_write = rtc_int_dis_prew, + },{ .name = "ADDR_ERROR", .addr = A_ADDR_ERROR, + .w1c = 0x1, + .post_write = addr_error_postw, + },{ .name = "ADDR_ERROR_INT_MASK", .addr = A_ADDR_ERROR_INT_MASK, + .reset = 0x1, + .ro = 0x1, + },{ .name = "ADDR_ERROR_INT_EN", .addr = A_ADDR_ERROR_INT_EN, + .pre_write = addr_error_int_en_prew, + },{ .name = "ADDR_ERROR_INT_DIS", .addr = A_ADDR_ERROR_INT_DIS, + .pre_write = addr_error_int_dis_prew, + },{ .name = "CONTROL", .addr = A_CONTROL, + .reset = 0x1000000, + .rsvd = 0x70fffffe, + },{ .name = "SAFETY_CHK", .addr = A_SAFETY_CHK, + } +}; + +static void rtc_reset(DeviceState *dev) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + + rtc_int_update_irq(s); + addr_error_int_update_irq(s); +} + +static const MemoryRegionOps rtc_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void rtc_init(Object *obj) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + struct tm current_tm; + + memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_RTC, + XLNX_ZYNQMP_RTC_R_MAX * 4); + reg_array = + register_init_block32(DEVICE(obj), rtc_regs_info, + ARRAY_SIZE(rtc_regs_info), + s->regs_info, s->regs, + &rtc_ops, + XLNX_ZYNQMP_RTC_ERR_DEBUG, + XLNX_ZYNQMP_RTC_R_MAX * 4); + memory_region_add_subregion(&s->iomem, + 0x0, + ®_array->mem); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq_rtc_int); + sysbus_init_irq(sbd, &s->irq_addr_error_int); + + qemu_get_timedate(¤t_tm, 0); + s->tick_offset = mktimegm(¤t_tm) - + qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; + + trace_xlnx_zynqmp_rtc_gettime(current_tm.tm_year, current_tm.tm_mon, + current_tm.tm_mday, current_tm.tm_hour, + current_tm.tm_min, current_tm.tm_sec); +} + +static int rtc_pre_save(void *opaque) +{ + XlnxZynqMPRTC *s = opaque; + int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; + + /* Add the time at migration */ + s->tick_offset = s->tick_offset + now; + + return 0; +} + +static int rtc_post_load(void *opaque, int version_id) +{ + XlnxZynqMPRTC *s = opaque; + int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; + + /* Subtract the time after migration. This combined with the pre_save + * action results in us having subtracted the time that the guest was + * stopped to the offset. + */ + s->tick_offset = s->tick_offset - now; + + return 0; +} + +static const VMStateDescription vmstate_rtc = { + .name = TYPE_XLNX_ZYNQMP_RTC, + .version_id = 1, + .minimum_version_id = 1, + .pre_save = rtc_pre_save, + .post_load = rtc_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPRTC, XLNX_ZYNQMP_RTC_R_MAX), + VMSTATE_UINT32(tick_offset, XlnxZynqMPRTC), + VMSTATE_END_OF_LIST(), + } +}; + +static void rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = rtc_reset; + dc->vmsd = &vmstate_rtc; +} + +static const TypeInfo rtc_info = { + .name = TYPE_XLNX_ZYNQMP_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZynqMPRTC), + .class_init = rtc_class_init, + .instance_init = rtc_init, +}; + +static void rtc_register_types(void) +{ + type_register_static(&rtc_info); +} + +type_init(rtc_register_types) diff --git a/include/hw/arm/armv7m.h b/include/hw/arm/armv7m.h index 35ab757264..78308d1484 100644 --- a/include/hw/arm/armv7m.h +++ b/include/hw/arm/armv7m.h @@ -12,6 +12,7 @@ #include "hw/sysbus.h" #include "hw/intc/armv7m_nvic.h" +#include "target/arm/idau.h" #define TYPE_BITBAND "ARM,bitband-memory" #define BITBAND(obj) OBJECT_CHECK(BitBandState, (obj), TYPE_BITBAND) @@ -40,6 +41,8 @@ typedef struct { * + Property "memory": MemoryRegion defining the physical address space * that CPU accesses see. (The NVIC, bitbanding and other CPU-internal * devices will be automatically layered on top of this view.) + * + Property "idau": IDAU interface (forwarded to CPU object) + * + Property "init-svtor": secure VTOR reset value (forwarded to CPU object) */ typedef struct ARMv7MState { /*< private >*/ @@ -58,6 +61,8 @@ typedef struct ARMv7MState { char *cpu_type; /* MemoryRegion the board provides to us (with its devices, RAM, etc) */ MemoryRegion *board_memory; + Object *idau; + uint32_t init_svtor; } ARMv7MState; #endif diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h new file mode 100644 index 0000000000..c6129d926b --- /dev/null +++ b/include/hw/arm/iotkit.h @@ -0,0 +1,109 @@ +/* + * ARM IoT Kit + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the Arm IoT Kit which is documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * It contains: + * a Cortex-M33 + * the IDAU + * some timers and watchdogs + * two peripheral protection controllers + * a memory protection controller + * a security controller + * a bus fabric which arranges that some parts of the address + * space are secure and non-secure aliases of each other + * + * QEMU interface: + * + QOM property "memory" is a MemoryRegion containing the devices provided + * by the board model. + * + QOM property "MAINCLK" is the frequency of the main system clock + * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts + * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which + * are wired to the NVIC lines 32 .. n+32 + * Controlling up to 4 AHB expansion PPBs which a system using the IoTKit + * might provide: + * + named GPIO outputs apb_ppcexp{0,1,2,3}_nonsec[0..15] + * + named GPIO outputs apb_ppcexp{0,1,2,3}_ap[0..15] + * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_enable + * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_clear + * + named GPIO inputs apb_ppcexp{0,1,2,3}_irq_status + * Controlling each of the 4 expansion AHB PPCs which a system using the IoTKit + * might provide: + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_nonsec[0..15] + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_ap[0..15] + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear + * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + */ + +#ifndef IOTKIT_H +#define IOTKIT_H + +#include "hw/sysbus.h" +#include "hw/arm/armv7m.h" +#include "hw/misc/iotkit-secctl.h" +#include "hw/misc/tz-ppc.h" +#include "hw/timer/cmsdk-apb-timer.h" +#include "hw/misc/unimp.h" +#include "hw/or-irq.h" +#include "hw/core/split-irq.h" + +#define TYPE_IOTKIT "iotkit" +#define IOTKIT(obj) OBJECT_CHECK(IoTKit, (obj), TYPE_IOTKIT) + +/* We have an IRQ splitter and an OR gate input for each external PPC + * and the 2 internal PPCs + */ +#define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC) +#define NUM_PPCS (NUM_EXTERNAL_PPCS + 2) + +typedef struct IoTKit { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + ARMv7MState armv7m; + IoTKitSecCtl secctl; + TZPPC apb_ppc0; + TZPPC apb_ppc1; + CMSDKAPBTIMER timer0; + CMSDKAPBTIMER timer1; + qemu_or_irq ppc_irq_orgate; + SplitIRQ sec_resp_splitter; + SplitIRQ ppc_irq_splitter[NUM_PPCS]; + + UnimplementedDeviceState dualtimer; + UnimplementedDeviceState s32ktimer; + + MemoryRegion container; + MemoryRegion alias1; + MemoryRegion alias2; + MemoryRegion alias3; + MemoryRegion sram0; + + qemu_irq *exp_irqs; + qemu_irq ppc0_irq; + qemu_irq ppc1_irq; + qemu_irq sec_resp_cfg; + qemu_irq sec_resp_cfg_in; + qemu_irq nsc_cfg_in; + + qemu_irq irq_status_in[NUM_EXTERNAL_PPCS]; + + uint32_t nsccfg; + + /* Properties */ + MemoryRegion *board_memory; + uint32_t exp_numirq; + uint32_t mainclk_frq; +} IoTKit; + +#endif diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 0a2b037c6b..3b613e364d 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -29,6 +29,7 @@ #include "hw/dma/xlnx_dpdma.h" #include "hw/display/xlnx_dp.h" #include "hw/intc/xlnx-zynqmp-ipi.h" +#include "hw/timer/xlnx-zynqmp-rtc.h" #define TYPE_XLNX_ZYNQMP "xlnx,zynqmp" #define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \ @@ -92,6 +93,7 @@ typedef struct XlnxZynqMPState { XlnxDPState dp; XlnxDPDMAState dpdma; XlnxZynqMPIPI ipi; + XlnxZynqMPRTC rtc; char *boot_cpu; ARMCPU *boot_cpu_ptr; diff --git a/include/hw/core/split-irq.h b/include/hw/core/split-irq.h new file mode 100644 index 0000000000..bb87157c5a --- /dev/null +++ b/include/hw/core/split-irq.h @@ -0,0 +1,57 @@ +/* + * IRQ splitter device. + * + * Copyright (c) 2018 Linaro Limited. + * Written by Peter Maydell + * + * 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. + */ + +/* This is a simple device which has one GPIO input line and multiple + * GPIO output lines. Any change on the input line is forwarded to all + * of the outputs. + * + * QEMU interface: + * + one unnamed GPIO input: the input line + * + N unnamed GPIO outputs: the output lines + * + QOM property "num-lines": sets the number of output lines + */ +#ifndef HW_SPLIT_IRQ_H +#define HW_SPLIT_IRQ_H + +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_SPLIT_IRQ "split-irq" + +#define MAX_SPLIT_LINES 16 + +typedef struct SplitIRQ SplitIRQ; + +#define SPLIT_IRQ(obj) OBJECT_CHECK(SplitIRQ, (obj), TYPE_SPLIT_IRQ) + +struct SplitIRQ { + DeviceState parent_obj; + + qemu_irq out_irq[MAX_SPLIT_LINES]; + uint16_t num_lines; +}; + +#endif diff --git a/include/hw/irq.h b/include/hw/irq.h index 4c4c2eaf9a..7a40e3ed26 100644 --- a/include/hw/irq.h +++ b/include/hw/irq.h @@ -50,7 +50,9 @@ void qemu_free_irq(qemu_irq irq); /* Returns a new IRQ with opposite polarity. */ qemu_irq qemu_irq_invert(qemu_irq irq); -/* Returns a new IRQ which feeds into both the passed IRQs */ +/* Returns a new IRQ which feeds into both the passed IRQs. + * It's probably better to use the TYPE_SPLIT_IRQ device instead. + */ qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2); /* Returns a new IRQ set which connects 1:1 to another IRQ set, which diff --git a/include/hw/loader.h b/include/hw/loader.h index 5edbe02b1c..2504cc2259 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -162,16 +162,26 @@ int load_uimage(const char *filename, hwaddr *ep, void *translate_opaque); /** - * load_ramdisk: + * load_ramdisk_as: * @filename: Path to the ramdisk image * @addr: Memory address to load the ramdisk to * @max_sz: Maximum allowed ramdisk size (for non-u-boot ramdisks) + * @as: The AddressSpace to load the ELF to. The value of address_space_memory + * is used if nothing is supplied here. * * Load a ramdisk image with U-Boot header to the specified memory * address. * * Returns the size of the loaded image on success, -1 otherwise. */ +int load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz, + AddressSpace *as); + +/** + * load_ramdisk: + * Same as load_ramdisk_as(), but doesn't allow the caller to specify + * an AddressSpace. + */ int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz); ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen); diff --git a/include/hw/misc/iotkit-secctl.h b/include/hw/misc/iotkit-secctl.h new file mode 100644 index 0000000000..faad0c9190 --- /dev/null +++ b/include/hw/misc/iotkit-secctl.h @@ -0,0 +1,103 @@ +/* + * ARM IoT Kit security controller + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the security controller which is part of the + * Arm IoT Kit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * + * QEMU interface: + * + sysbus MMIO region 0 is the "secure privilege control block" registers + * + sysbus MMIO region 1 is the "non-secure privilege control block" registers + * + named GPIO output "sec_resp_cfg" indicating whether blocked accesses + * should RAZ/WI or bus error + * + named GPIO output "nsc_cfg" whose value tracks the NSCCFG register value + * Controlling the 2 APB PPCs in the IoTKit: + * + named GPIO outputs apb_ppc0_nonsec[0..2] and apb_ppc1_nonsec + * + named GPIO outputs apb_ppc0_ap[0..2] and apb_ppc1_ap + * + named GPIO outputs apb_ppc{0,1}_irq_enable + * + named GPIO outputs apb_ppc{0,1}_irq_clear + * + named GPIO inputs apb_ppc{0,1}_irq_status + * Controlling each of the 4 expansion APB PPCs which a system using the IoTKit + * might provide: + * + named GPIO outputs apb_ppcexp{0,1,2,3}_nonsec[0..15] + * + named GPIO outputs apb_ppcexp{0,1,2,3}_ap[0..15] + * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_enable + * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_clear + * + named GPIO inputs apb_ppcexp{0,1,2,3}_irq_status + * Controlling each of the 4 expansion AHB PPCs which a system using the IoTKit + * might provide: + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_nonsec[0..15] + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_ap[0..15] + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear + * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + */ + +#ifndef IOTKIT_SECCTL_H +#define IOTKIT_SECCTL_H + +#include "hw/sysbus.h" + +#define TYPE_IOTKIT_SECCTL "iotkit-secctl" +#define IOTKIT_SECCTL(obj) OBJECT_CHECK(IoTKitSecCtl, (obj), TYPE_IOTKIT_SECCTL) + +#define IOTS_APB_PPC0_NUM_PORTS 3 +#define IOTS_APB_PPC1_NUM_PORTS 1 +#define IOTS_PPC_NUM_PORTS 16 +#define IOTS_NUM_APB_PPC 2 +#define IOTS_NUM_APB_EXP_PPC 4 +#define IOTS_NUM_AHB_EXP_PPC 4 + +typedef struct IoTKitSecCtl IoTKitSecCtl; + +/* State and IRQ lines relating to a PPC. For the + * PPCs in the IoTKit not all the IRQ lines are used. + */ +typedef struct IoTKitSecCtlPPC { + qemu_irq nonsec[IOTS_PPC_NUM_PORTS]; + qemu_irq ap[IOTS_PPC_NUM_PORTS]; + qemu_irq irq_enable; + qemu_irq irq_clear; + + uint32_t ns; + uint32_t sp; + uint32_t nsp; + + /* Number of ports actually present */ + int numports; + /* Offset of this PPC's interrupt bits in SECPPCINTSTAT */ + int irq_bit_offset; + IoTKitSecCtl *parent; +} IoTKitSecCtlPPC; + +struct IoTKitSecCtl { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + qemu_irq sec_resp_cfg; + qemu_irq nsc_cfg_irq; + + MemoryRegion s_regs; + MemoryRegion ns_regs; + + uint32_t secppcintstat; + uint32_t secppcinten; + uint32_t secrespcfg; + uint32_t nsccfg; + uint32_t brginten; + + IoTKitSecCtlPPC apb[IOTS_NUM_APB_PPC]; + IoTKitSecCtlPPC apbexp[IOTS_NUM_APB_EXP_PPC]; + IoTKitSecCtlPPC ahbexp[IOTS_NUM_APB_EXP_PPC]; +}; + +#endif diff --git a/include/hw/misc/mps2-fpgaio.h b/include/hw/misc/mps2-fpgaio.h new file mode 100644 index 0000000000..eedf17ebc6 --- /dev/null +++ b/include/hw/misc/mps2-fpgaio.h @@ -0,0 +1,43 @@ +/* + * ARM MPS2 FPGAIO emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the FPGAIO register block in the AN505 + * FPGA image for the MPS2 dev board; it is documented in the + * application note: + * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html + * + * QEMU interface: + * + sysbus MMIO region 0: the register bank + */ + +#ifndef MPS2_FPGAIO_H +#define MPS2_FPGAIO_H + +#include "hw/sysbus.h" + +#define TYPE_MPS2_FPGAIO "mps2-fpgaio" +#define MPS2_FPGAIO(obj) OBJECT_CHECK(MPS2FPGAIO, (obj), TYPE_MPS2_FPGAIO) + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + uint32_t led0; + uint32_t prescale; + uint32_t misc; + + uint32_t prescale_clk; +} MPS2FPGAIO; + +#endif diff --git a/include/hw/misc/tz-ppc.h b/include/hw/misc/tz-ppc.h new file mode 100644 index 0000000000..fc8b806e4d --- /dev/null +++ b/include/hw/misc/tz-ppc.h @@ -0,0 +1,101 @@ +/* + * ARM TrustZone peripheral protection controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the TrustZone peripheral protection controller (PPC). + * It is documented in the ARM CoreLink SIE-200 System IP for Embedded TRM + * (DDI 0571G): + * https://developer.arm.com/products/architecture/m-profile/docs/ddi0571/g + * + * The PPC sits in front of peripherals and allows secure software to + * configure it to either pass through or reject transactions. + * Rejected transactions may be configured to either be aborted, or to + * behave as RAZ/WI. An interrupt can be signalled for a rejected transaction. + * + * The PPC has no register interface -- it is configured purely by a + * collection of input signals from other hardware in the system. Typically + * they are either hardwired or exposed in an ad-hoc register interface by + * the SoC that uses the PPC. + * + * This QEMU model can be used to model either the AHB5 or APB4 TZ PPC, + * since the only difference between them is that the AHB version has a + * "default" port which has no security checks applied. In QEMU the default + * port can be emulated simply by wiring its downstream devices directly + * into the parent address space, since the PPC does not need to intercept + * transactions there. + * + * In the hardware, selection of which downstream port to use is done by + * the user's decode logic asserting one of the hsel[] signals. In QEMU, + * we provide 16 MMIO regions, one per port, and the user maps these into + * the desired addresses to implement the address decode. + * + * QEMU interface: + * + sysbus MMIO regions 0..15: MemoryRegions defining the upstream end + * of each of the 16 ports of the PPC + * + Property "port[0..15]": MemoryRegion defining the downstream device(s) + * for each of the 16 ports of the PPC + * + Named GPIO inputs "cfg_nonsec[0..15]": set to 1 if the port should be + * accessible to NonSecure transactions + * + Named GPIO inputs "cfg_ap[0..15]": set to 1 if the port should be + * accessible to non-privileged transactions + * + Named GPIO input "cfg_sec_resp": set to 1 if a rejected transaction should + * result in a transaction error, or 0 for the transaction to RAZ/WI + * + Named GPIO input "irq_enable": set to 1 to enable interrupts + * + Named GPIO input "irq_clear": set to 1 to clear a pending interrupt + * + Named GPIO output "irq": set for a transaction-failed interrupt + * + Property "NONSEC_MASK": if a bit is set in this mask then accesses to + * the associated port do not have the TZ security check performed. (This + * corresponds to the hardware allowing this to be set as a Verilog + * parameter.) + */ + +#ifndef TZ_PPC_H +#define TZ_PPC_H + +#include "hw/sysbus.h" + +#define TYPE_TZ_PPC "tz-ppc" +#define TZ_PPC(obj) OBJECT_CHECK(TZPPC, (obj), TYPE_TZ_PPC) + +#define TZ_NUM_PORTS 16 + +typedef struct TZPPC TZPPC; + +typedef struct TZPPCPort { + TZPPC *ppc; + MemoryRegion upstream; + AddressSpace downstream_as; + MemoryRegion *downstream; +} TZPPCPort; + +struct TZPPC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + + /* State: these just track the values of our input signals */ + bool cfg_nonsec[TZ_NUM_PORTS]; + bool cfg_ap[TZ_NUM_PORTS]; + bool cfg_sec_resp; + bool irq_enable; + bool irq_clear; + /* State: are we asserting irq ? */ + bool irq_status; + + qemu_irq irq; + + /* Properties */ + uint32_t nonsec_mask; + + TZPPCPort port[TZ_NUM_PORTS]; +}; + +#endif diff --git a/include/hw/misc/unimp.h b/include/hw/misc/unimp.h index 52e068ec3e..2a291ca42d 100644 --- a/include/hw/misc/unimp.h +++ b/include/hw/misc/unimp.h @@ -12,6 +12,16 @@ #define TYPE_UNIMPLEMENTED_DEVICE "unimplemented-device" +#define UNIMPLEMENTED_DEVICE(obj) \ + OBJECT_CHECK(UnimplementedDeviceState, (obj), TYPE_UNIMPLEMENTED_DEVICE) + +typedef struct { + SysBusDevice parent_obj; + MemoryRegion iomem; + char *name; + uint64_t size; +} UnimplementedDeviceState; + /** * create_unimplemented_device: create and map a dummy device * @name: name of the device for debug logging diff --git a/include/hw/or-irq.h b/include/hw/or-irq.h index fd900fcf19..3f6fc1b58a 100644 --- a/include/hw/or-irq.h +++ b/include/hw/or-irq.h @@ -22,6 +22,9 @@ * THE SOFTWARE. */ +#ifndef HW_OR_IRQ_H +#define HW_OR_IRQ_H + #include "hw/irq.h" #include "hw/sysbus.h" #include "qom/object.h" @@ -41,3 +44,5 @@ struct OrIRQState { bool levels[MAX_OR_LINES]; uint16_t num_lines; }; + +#endif diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index fc9d617a76..9453588160 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -311,10 +311,36 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name); /* GPIO inputs also double as IRQ sinks. */ void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n); void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n); -void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler, - const char *name, int n); void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, const char *name, int n); +/** + * qdev_init_gpio_in_named_with_opaque: create an array of input GPIO lines + * for the specified device + * + * @dev: Device to create input GPIOs for + * @handler: Function to call when GPIO line value is set + * @opaque: Opaque data pointer to pass to @handler + * @name: Name of the GPIO input (must be unique for this device) + * @n: Number of GPIO lines in this input set + */ +void qdev_init_gpio_in_named_with_opaque(DeviceState *dev, + qemu_irq_handler handler, + void *opaque, + const char *name, int n); + +/** + * qdev_init_gpio_in_named: create an array of input GPIO lines + * for the specified device + * + * Like qdev_init_gpio_in_named_with_opaque(), but the opaque pointer + * passed to the handler is @dev (which is the most commonly desired behaviour). + */ +static inline void qdev_init_gpio_in_named(DeviceState *dev, + qemu_irq_handler handler, + const char *name, int n) +{ + qdev_init_gpio_in_named_with_opaque(dev, handler, dev, name, n); +} void qdev_pass_gpios(DeviceState *dev, DeviceState *container, const char *name); diff --git a/include/hw/timer/xlnx-zynqmp-rtc.h b/include/hw/timer/xlnx-zynqmp-rtc.h new file mode 100644 index 0000000000..5ba4d8bc4a --- /dev/null +++ b/include/hw/timer/xlnx-zynqmp-rtc.h @@ -0,0 +1,86 @@ +/* + * QEMU model of the Xilinx ZynqMP Real Time Clock (RTC). + * + * Copyright (c) 2017 Xilinx Inc. + * + * Written-by: Alistair Francis <alistair.francis@xilinx.com> + * + * 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/register.h" + +#define TYPE_XLNX_ZYNQMP_RTC "xlnx-zynmp.rtc" + +#define XLNX_ZYNQMP_RTC(obj) \ + OBJECT_CHECK(XlnxZynqMPRTC, (obj), TYPE_XLNX_ZYNQMP_RTC) + +REG32(SET_TIME_WRITE, 0x0) +REG32(SET_TIME_READ, 0x4) +REG32(CALIB_WRITE, 0x8) + FIELD(CALIB_WRITE, FRACTION_EN, 20, 1) + FIELD(CALIB_WRITE, FRACTION_DATA, 16, 4) + FIELD(CALIB_WRITE, MAX_TICK, 0, 16) +REG32(CALIB_READ, 0xc) + FIELD(CALIB_READ, FRACTION_EN, 20, 1) + FIELD(CALIB_READ, FRACTION_DATA, 16, 4) + FIELD(CALIB_READ, MAX_TICK, 0, 16) +REG32(CURRENT_TIME, 0x10) +REG32(CURRENT_TICK, 0x14) + FIELD(CURRENT_TICK, VALUE, 0, 16) +REG32(ALARM, 0x18) +REG32(RTC_INT_STATUS, 0x20) + FIELD(RTC_INT_STATUS, ALARM, 1, 1) + FIELD(RTC_INT_STATUS, SECONDS, 0, 1) +REG32(RTC_INT_MASK, 0x24) + FIELD(RTC_INT_MASK, ALARM, 1, 1) + FIELD(RTC_INT_MASK, SECONDS, 0, 1) +REG32(RTC_INT_EN, 0x28) + FIELD(RTC_INT_EN, ALARM, 1, 1) + FIELD(RTC_INT_EN, SECONDS, 0, 1) +REG32(RTC_INT_DIS, 0x2c) + FIELD(RTC_INT_DIS, ALARM, 1, 1) + FIELD(RTC_INT_DIS, SECONDS, 0, 1) +REG32(ADDR_ERROR, 0x30) + FIELD(ADDR_ERROR, STATUS, 0, 1) +REG32(ADDR_ERROR_INT_MASK, 0x34) + FIELD(ADDR_ERROR_INT_MASK, MASK, 0, 1) +REG32(ADDR_ERROR_INT_EN, 0x38) + FIELD(ADDR_ERROR_INT_EN, MASK, 0, 1) +REG32(ADDR_ERROR_INT_DIS, 0x3c) + FIELD(ADDR_ERROR_INT_DIS, MASK, 0, 1) +REG32(CONTROL, 0x40) + FIELD(CONTROL, BATTERY_DISABLE, 31, 1) + FIELD(CONTROL, OSC_CNTRL, 24, 4) + FIELD(CONTROL, SLVERR_ENABLE, 0, 1) +REG32(SAFETY_CHK, 0x50) + +#define XLNX_ZYNQMP_RTC_R_MAX (R_SAFETY_CHK + 1) + +typedef struct XlnxZynqMPRTC { + SysBusDevice parent_obj; + MemoryRegion iomem; + qemu_irq irq_rtc_int; + qemu_irq irq_addr_error_int; + + uint32_t tick_offset; + + uint32_t regs[XLNX_ZYNQMP_RTC_R_MAX]; + RegisterInfo regs_info[XLNX_ZYNQMP_RTC_R_MAX]; +} XlnxZynqMPRTC; diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6689089cd2..e3689c658a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -553,6 +553,8 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE(ARM_FEATURE_V8_SHA512, ARM_HWCAP_A64_SHA512); GET_FEATURE(ARM_FEATURE_V8_FP16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); + GET_FEATURE(ARM_FEATURE_V8_RDM, ARM_HWCAP_A64_ASIMDRDM); + GET_FEATURE(ARM_FEATURE_V8_FCMA, ARM_HWCAP_A64_FCMA); #undef GET_FEATURE return hwcaps; diff --git a/scripts/decodetree.py b/scripts/decodetree.py index 6a33f8f8dd..41301c84aa 100755 --- a/scripts/decodetree.py +++ b/scripts/decodetree.py @@ -461,7 +461,7 @@ class Pattern(General): global translate_prefix output('typedef ', self.base.base.struct_name(), ' arg_', self.name, ';\n') - output(translate_scope, 'void ', translate_prefix, '_', self.name, + output(translate_scope, 'bool ', translate_prefix, '_', self.name, '(DisasContext *ctx, arg_', self.name, ' *a, ', insntype, ' insn);\n') @@ -474,9 +474,8 @@ class Pattern(General): output(ind, self.base.extract_name(), '(&u.f_', arg, ', insn);\n') for n, f in self.fields.items(): output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n') - output(ind, translate_prefix, '_', self.name, + output(ind, 'return ', translate_prefix, '_', self.name, '(ctx, &u.f_', arg, ', insn);\n') - output(ind, 'return true;\n') # end Pattern diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index 847fb52ee0..1297bead5f 100644 --- a/target/arm/Makefile.objs +++ b/target/arm/Makefile.objs @@ -5,7 +5,7 @@ obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o -obj-y += neon_helper.o iwmmxt_helper.o +obj-y += neon_helper.o iwmmxt_helper.o vec_helper.o obj-y += gdbstub.o obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o obj-y += crypto_helper.o diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1b3ae62db6..6b77aaa445 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "target/arm/idau.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "cpu.h" @@ -186,6 +187,7 @@ static void arm_cpu_reset(CPUState *s) uint32_t initial_msp; /* Loaded from 0x0 */ uint32_t initial_pc; /* Loaded from 0x4 */ uint8_t *rom; + uint32_t vecbase; if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { env->v7m.secure = true; @@ -213,8 +215,11 @@ static void arm_cpu_reset(CPUState *s) /* Unlike A/R profile, M profile defines the reset LR value */ env->regs[14] = 0xffffffff; - /* Load the initial SP and PC from the vector table at address 0 */ - rom = rom_ptr(0); + env->v7m.vecbase[M_REG_S] = cpu->init_svtor & 0xffffff80; + + /* Load the initial SP and PC from offset 0 and 4 in the vector table */ + vecbase = env->v7m.vecbase[env->v7m.secure]; + rom = rom_ptr(vecbase); if (rom) { /* Address zero is covered by ROM which hasn't yet been * copied into physical memory. @@ -227,8 +232,8 @@ static void arm_cpu_reset(CPUState *s) * it got copied into memory. In the latter case, rom_ptr * will return a NULL pointer and we should use ldl_phys instead. */ - initial_msp = ldl_phys(s->as, 0); - initial_pc = ldl_phys(s->as, 4); + initial_msp = ldl_phys(s->as, vecbase); + initial_pc = ldl_phys(s->as, vecbase + 4); } env->regs[13] = initial_msp & 0xFFFFFFFC; @@ -623,6 +628,10 @@ static Property arm_cpu_pmsav7_dregion_property = pmsav7_dregion, qdev_prop_uint32, uint32_t); +/* M profile: initial value of the Secure VTOR */ +static Property arm_cpu_initsvtor_property = + DEFINE_PROP_UINT32("init-svtor", ARMCPU, init_svtor, 0); + static void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -688,6 +697,15 @@ static void arm_cpu_post_init(Object *obj) } } + if (arm_feature(&cpu->env, ARM_FEATURE_M_SECURITY)) { + object_property_add_link(obj, "idau", TYPE_IDAU_INTERFACE, &cpu->idau, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_initsvtor_property, + &error_abort); + } + qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property, &error_abort); } @@ -1188,6 +1206,35 @@ static void cortex_m4_initfn(Object *obj) cpu->id_isar5 = 0x00000000; } +static void cortex_m33_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); + set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); + cpu->midr = 0x410fd213; /* r0p3 */ + cpu->pmsav7_dregion = 16; + cpu->sau_sregion = 8; + cpu->id_pfr0 = 0x00000030; + cpu->id_pfr1 = 0x00000210; + cpu->id_dfr0 = 0x00200000; + cpu->id_afr0 = 0x00000000; + cpu->id_mmfr0 = 0x00101F40; + cpu->id_mmfr1 = 0x00000000; + cpu->id_mmfr2 = 0x01000000; + cpu->id_mmfr3 = 0x00000000; + cpu->id_isar0 = 0x01101110; + cpu->id_isar1 = 0x02212000; + cpu->id_isar2 = 0x20232232; + cpu->id_isar3 = 0x01111131; + cpu->id_isar4 = 0x01310132; + cpu->id_isar5 = 0x00000000; + cpu->clidr = 0x00000000; + cpu->ctr = 0x8000c000; +} + static void arm_v7m_class_init(ObjectClass *oc, void *data) { CPUClass *cc = CPU_CLASS(oc); @@ -1650,6 +1697,8 @@ static void arm_any_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); + set_feature(&cpu->env, ARM_FEATURE_V8_RDM); + set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); cpu->midr = 0xffffffff; } #endif @@ -1679,6 +1728,8 @@ static const ARMCPUInfo arm_cpus[] = { .class_init = arm_v7m_class_init }, { .name = "cortex-m4", .initfn = cortex_m4_initfn, .class_init = arm_v7m_class_init }, + { .name = "cortex-m33", .initfn = cortex_m33_initfn, + .class_init = arm_v7m_class_init }, { .name = "cortex-r5", .initfn = cortex_r5_initfn }, { .name = "cortex-a7", .initfn = cortex_a7_initfn }, { .name = "cortex-a8", .initfn = cortex_a8_initfn }, @@ -1821,11 +1872,18 @@ static const TypeInfo arm_cpu_type_info = { .class_init = arm_cpu_class_init, }; +static const TypeInfo idau_interface_type_info = { + .name = TYPE_IDAU_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(IDAUInterfaceClass), +}; + static void arm_cpu_register_types(void) { const ARMCPUInfo *info = arm_cpus; type_register_static(&arm_cpu_type_info); + type_register_static(&idau_interface_type_info); while (info->name) { cpu_register(info); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 2b9740878b..8dd6b788df 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -694,6 +694,9 @@ struct ARMCPU { /* MemoryRegion to use for secure physical accesses */ MemoryRegion *secure_memory; + /* For v8M, pointer to the IDAU interface provided by board/SoC */ + Object *idau; + /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; @@ -728,6 +731,9 @@ struct ARMCPU { */ uint32_t psci_conduit; + /* For v8M, initial value of the Secure VTOR */ + uint32_t init_svtor; + /* [QEMU_]KVM_ARM_TARGET_* constant for this CPU, or * QEMU_KVM_ARM_TARGET_NONE if the kernel doesn't support this CPU type. */ @@ -1427,7 +1433,9 @@ enum arm_features { ARM_FEATURE_V8_SHA3, /* implements SHA3 part of v8 Crypto Extensions */ ARM_FEATURE_V8_SM3, /* implements SM3 part of v8 Crypto Extensions */ ARM_FEATURE_V8_SM4, /* implements SM4 part of v8 Crypto Extensions */ + ARM_FEATURE_V8_RDM, /* implements v8.1 simd round multiply */ ARM_FEATURE_V8_FP16, /* implements v8.2 half-precision float */ + ARM_FEATURE_V8_FCMA, /* has complex number part of v8.3 extensions. */ }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 9743bdc8c3..4228713b19 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -230,7 +230,9 @@ static void aarch64_any_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_SM4); set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); + set_feature(&cpu->env, ARM_FEATURE_V8_RDM); set_feature(&cpu->env, ARM_FEATURE_V8_FP16); + set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */ cpu->dcz_blocksize = 7; /* 512 bytes */ } diff --git a/target/arm/helper.c b/target/arm/helper.c index c82f63d440..09893e3f72 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "target/arm/idau.h" #include "trace.h" #include "cpu.h" #include "internals.h" @@ -9741,19 +9742,32 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, */ ARMCPU *cpu = arm_env_get_cpu(env); int r; + bool idau_exempt = false, idau_ns = true, idau_nsc = true; + int idau_region = IREGION_NOTVALID; - /* TODO: implement IDAU */ + if (cpu->idau) { + IDAUInterfaceClass *iic = IDAU_INTERFACE_GET_CLASS(cpu->idau); + IDAUInterface *ii = IDAU_INTERFACE(cpu->idau); + + iic->check(ii, address, &idau_region, &idau_exempt, &idau_ns, + &idau_nsc); + } if (access_type == MMU_INST_FETCH && extract32(address, 28, 4) == 0xf) { /* 0xf0000000..0xffffffff is always S for insn fetches */ return; } - if (v8m_is_sau_exempt(env, address, access_type)) { + if (idau_exempt || v8m_is_sau_exempt(env, address, access_type)) { sattrs->ns = !regime_is_secure(env, mmu_idx); return; } + if (idau_region != IREGION_NOTVALID) { + sattrs->irvalid = true; + sattrs->iregion = idau_region; + } + switch (env->sau.ctrl & 3) { case 0: /* SAU.ENABLE == 0, SAU.ALLNS == 0 */ break; @@ -9790,7 +9804,15 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, } } - /* TODO when we support the IDAU then it may override the result here */ + /* The IDAU will override the SAU lookup results if it specifies + * higher security than the SAU does. + */ + if (!idau_ns) { + if (sattrs->ns || (!idau_nsc && sattrs->nsc)) { + sattrs->ns = false; + sattrs->nsc = idau_nsc; + } + } break; } } diff --git a/target/arm/helper.h b/target/arm/helper.h index 6dd8504ec3..0d2094f2be 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -365,8 +365,12 @@ DEF_HELPER_FLAGS_1(neon_rbit_u8, TCG_CALL_NO_RWG_SE, i32, i32) DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32) DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32) +DEF_HELPER_4(neon_qrdmlah_s16, i32, env, i32, i32, i32) +DEF_HELPER_4(neon_qrdmlsh_s16, i32, env, i32, i32, i32) DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32) DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32) +DEF_HELPER_4(neon_qrdmlah_s32, i32, env, s32, s32, s32) +DEF_HELPER_4(neon_qrdmlsh_s32, i32, env, s32, s32, s32) DEF_HELPER_1(neon_narrow_u8, i32, i64) DEF_HELPER_1(neon_narrow_u16, i32, i64) @@ -565,6 +569,33 @@ DEF_HELPER_2(dc_zva, void, env, i64) DEF_HELPER_FLAGS_2(neon_pmull_64_lo, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(neon_pmull_64_hi, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_5(gvec_qrdmlah_s16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_qrdmlah_s32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcaddd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fcmlah, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcmlah_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcmlas, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcmlas_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcmlad, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "helper-a64.h" #endif diff --git a/target/arm/idau.h b/target/arm/idau.h new file mode 100644 index 0000000000..cac27b95fa --- /dev/null +++ b/target/arm/idau.h @@ -0,0 +1,61 @@ +/* + * QEMU ARM CPU -- interface for the Arm v8M IDAU + * + * Copyright (c) 2018 Linaro Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + * <http://www.gnu.org/licenses/gpl-2.0.html> + * + * In the v8M architecture, the IDAU is a small piece of hardware + * typically implemented in the SoC which provides board or SoC + * specific security attribution information for each address that + * the CPU performs MPU/SAU checks on. For QEMU, we model this with a + * QOM interface which is implemented by the board or SoC object and + * connected to the CPU using a link property. + */ + +#ifndef TARGET_ARM_IDAU_H +#define TARGET_ARM_IDAU_H + +#include "qom/object.h" + +#define TYPE_IDAU_INTERFACE "idau-interface" +#define IDAU_INTERFACE(obj) \ + INTERFACE_CHECK(IDAUInterface, (obj), TYPE_IDAU_INTERFACE) +#define IDAU_INTERFACE_CLASS(class) \ + OBJECT_CLASS_CHECK(IDAUInterfaceClass, (class), TYPE_IDAU_INTERFACE) +#define IDAU_INTERFACE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IDAUInterfaceClass, (obj), TYPE_IDAU_INTERFACE) + +typedef struct IDAUInterface { + Object parent; +} IDAUInterface; + +#define IREGION_NOTVALID -1 + +typedef struct IDAUInterfaceClass { + InterfaceClass parent; + + /* Check the specified address and return the IDAU security information + * for it by filling in iregion, exempt, ns and nsc: + * iregion: IDAU region number, or IREGION_NOTVALID if not valid + * exempt: true if address is exempt from security attribution + * ns: true if the address is NonSecure + * nsc: true if the address is NonSecure-callable + */ + void (*check)(IDAUInterface *ii, uint32_t address, int *iregion, + bool *exempt, bool *ns, bool *nsc); +} IDAUInterfaceClass; + +#endif diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 32811dc8b0..31ff0479e6 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -701,6 +701,33 @@ static void gen_gvec_op3(DisasContext *s, bool is_q, int rd, vec_full_reg_size(s), gvec_op); } +/* Expand a 3-operand + env pointer operation using + * an out-of-line helper. + */ +static void gen_gvec_op3_env(DisasContext *s, bool is_q, int rd, + int rn, int rm, gen_helper_gvec_3_ptr *fn) +{ + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), cpu_env, + is_q ? 16 : 8, vec_full_reg_size(s), 0, fn); +} + +/* Expand a 3-operand + fpstatus pointer + simd data value operation using + * an out-of-line helper. + */ +static void gen_gvec_op3_fpst(DisasContext *s, bool is_q, int rd, int rn, + int rm, bool is_fp16, int data, + gen_helper_gvec_3_ptr *fn) +{ + TCGv_ptr fpst = get_fpstatus_ptr(is_fp16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), fpst, + is_q ? 16 : 8, vec_full_reg_size(s), data, fn); + tcg_temp_free_ptr(fpst); +} + /* Set ZF and NF based on a 64 bit result. This is alas fiddlier * than the 32 bit equivalent. */ @@ -7971,6 +7998,89 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, tcg_temp_free_ptr(fpst); } +/* AdvSIMD scalar three same extra + * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 + * +-----+---+-----------+------+---+------+---+--------+---+----+----+ + * | 0 1 | U | 1 1 1 1 0 | size | 0 | Rm | 1 | opcode | 1 | Rn | Rd | + * +-----+---+-----------+------+---+------+---+--------+---+----+----+ + */ +static void disas_simd_scalar_three_reg_same_extra(DisasContext *s, + uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int opcode = extract32(insn, 11, 4); + int rm = extract32(insn, 16, 5); + int size = extract32(insn, 22, 2); + bool u = extract32(insn, 29, 1); + TCGv_i32 ele1, ele2, ele3; + TCGv_i64 res; + int feature; + + switch (u * 16 + opcode) { + case 0x10: /* SQRDMLAH (vector) */ + case 0x11: /* SQRDMLSH (vector) */ + if (size != 1 && size != 2) { + unallocated_encoding(s); + return; + } + feature = ARM_FEATURE_V8_RDM; + break; + default: + unallocated_encoding(s); + return; + } + if (!arm_dc_feature(s, feature)) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + + /* Do a single operation on the lowest element in the vector. + * We use the standard Neon helpers and rely on 0 OP 0 == 0 + * with no side effects for all these operations. + * OPTME: special-purpose helpers would avoid doing some + * unnecessary work in the helper for the 16 bit cases. + */ + ele1 = tcg_temp_new_i32(); + ele2 = tcg_temp_new_i32(); + ele3 = tcg_temp_new_i32(); + + read_vec_element_i32(s, ele1, rn, 0, size); + read_vec_element_i32(s, ele2, rm, 0, size); + read_vec_element_i32(s, ele3, rd, 0, size); + + switch (opcode) { + case 0x0: /* SQRDMLAH */ + if (size == 1) { + gen_helper_neon_qrdmlah_s16(ele3, cpu_env, ele1, ele2, ele3); + } else { + gen_helper_neon_qrdmlah_s32(ele3, cpu_env, ele1, ele2, ele3); + } + break; + case 0x1: /* SQRDMLSH */ + if (size == 1) { + gen_helper_neon_qrdmlsh_s16(ele3, cpu_env, ele1, ele2, ele3); + } else { + gen_helper_neon_qrdmlsh_s32(ele3, cpu_env, ele1, ele2, ele3); + } + break; + default: + g_assert_not_reached(); + } + tcg_temp_free_i32(ele1); + tcg_temp_free_i32(ele2); + + res = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(res, ele3); + tcg_temp_free_i32(ele3); + + write_fp_dreg(s, rd, res); + tcg_temp_free_i64(res); +} + static void handle_2misc_64(DisasContext *s, int opcode, bool u, TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i32 tcg_rmode, TCGv_ptr tcg_fpstatus) @@ -10706,6 +10816,134 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) clear_vec_high(s, is_q, rd); } +/* AdvSIMD three same extra + * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 + * +---+---+---+-----------+------+---+------+---+--------+---+----+----+ + * | 0 | Q | U | 0 1 1 1 0 | size | 0 | Rm | 1 | opcode | 1 | Rn | Rd | + * +---+---+---+-----------+------+---+------+---+--------+---+----+----+ + */ +static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int opcode = extract32(insn, 11, 4); + int rm = extract32(insn, 16, 5); + int size = extract32(insn, 22, 2); + bool u = extract32(insn, 29, 1); + bool is_q = extract32(insn, 30, 1); + int feature, rot; + + switch (u * 16 + opcode) { + case 0x10: /* SQRDMLAH (vector) */ + case 0x11: /* SQRDMLSH (vector) */ + if (size != 1 && size != 2) { + unallocated_encoding(s); + return; + } + feature = ARM_FEATURE_V8_RDM; + break; + case 0x8: /* FCMLA, #0 */ + case 0x9: /* FCMLA, #90 */ + case 0xa: /* FCMLA, #180 */ + case 0xb: /* FCMLA, #270 */ + case 0xc: /* FCADD, #90 */ + case 0xe: /* FCADD, #270 */ + if (size == 0 + || (size == 1 && !arm_dc_feature(s, ARM_FEATURE_V8_FP16)) + || (size == 3 && !is_q)) { + unallocated_encoding(s); + return; + } + feature = ARM_FEATURE_V8_FCMA; + break; + default: + unallocated_encoding(s); + return; + } + if (!arm_dc_feature(s, feature)) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + + switch (opcode) { + case 0x0: /* SQRDMLAH (vector) */ + switch (size) { + case 1: + gen_gvec_op3_env(s, is_q, rd, rn, rm, gen_helper_gvec_qrdmlah_s16); + break; + case 2: + gen_gvec_op3_env(s, is_q, rd, rn, rm, gen_helper_gvec_qrdmlah_s32); + break; + default: + g_assert_not_reached(); + } + return; + + case 0x1: /* SQRDMLSH (vector) */ + switch (size) { + case 1: + gen_gvec_op3_env(s, is_q, rd, rn, rm, gen_helper_gvec_qrdmlsh_s16); + break; + case 2: + gen_gvec_op3_env(s, is_q, rd, rn, rm, gen_helper_gvec_qrdmlsh_s32); + break; + default: + g_assert_not_reached(); + } + return; + + case 0x8: /* FCMLA, #0 */ + case 0x9: /* FCMLA, #90 */ + case 0xa: /* FCMLA, #180 */ + case 0xb: /* FCMLA, #270 */ + rot = extract32(opcode, 0, 2); + switch (size) { + case 1: + gen_gvec_op3_fpst(s, is_q, rd, rn, rm, true, rot, + gen_helper_gvec_fcmlah); + break; + case 2: + gen_gvec_op3_fpst(s, is_q, rd, rn, rm, false, rot, + gen_helper_gvec_fcmlas); + break; + case 3: + gen_gvec_op3_fpst(s, is_q, rd, rn, rm, false, rot, + gen_helper_gvec_fcmlad); + break; + default: + g_assert_not_reached(); + } + return; + + case 0xc: /* FCADD, #90 */ + case 0xe: /* FCADD, #270 */ + rot = extract32(opcode, 1, 1); + switch (size) { + case 1: + gen_gvec_op3_fpst(s, is_q, rd, rn, rm, size == 1, rot, + gen_helper_gvec_fcaddh); + break; + case 2: + gen_gvec_op3_fpst(s, is_q, rd, rn, rm, size == 1, rot, + gen_helper_gvec_fcadds); + break; + case 3: + gen_gvec_op3_fpst(s, is_q, rd, rn, rm, size == 1, rot, + gen_helper_gvec_fcaddd); + break; + default: + g_assert_not_reached(); + } + return; + + default: + g_assert_not_reached(); + } +} + static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, int size, int rn, int rd) { @@ -11782,107 +12020,137 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); bool is_long = false; - bool is_fp = false; + int is_fp = 0; bool is_fp16 = false; int index; TCGv_ptr fpst; - switch (opcode) { - case 0x0: /* MLA */ - case 0x4: /* MLS */ - if (!u || is_scalar) { + switch (16 * u + opcode) { + case 0x08: /* MUL */ + case 0x10: /* MLA */ + case 0x14: /* MLS */ + if (is_scalar) { unallocated_encoding(s); return; } break; - case 0x2: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 0x6: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 0xa: /* SMULL, SMULL2, UMULL, UMULL2 */ + case 0x02: /* SMLAL, SMLAL2 */ + case 0x12: /* UMLAL, UMLAL2 */ + case 0x06: /* SMLSL, SMLSL2 */ + case 0x16: /* UMLSL, UMLSL2 */ + case 0x0a: /* SMULL, SMULL2 */ + case 0x1a: /* UMULL, UMULL2 */ if (is_scalar) { unallocated_encoding(s); return; } is_long = true; break; - case 0x3: /* SQDMLAL, SQDMLAL2 */ - case 0x7: /* SQDMLSL, SQDMLSL2 */ - case 0xb: /* SQDMULL, SQDMULL2 */ + case 0x03: /* SQDMLAL, SQDMLAL2 */ + case 0x07: /* SQDMLSL, SQDMLSL2 */ + case 0x0b: /* SQDMULL, SQDMULL2 */ is_long = true; - /* fall through */ - case 0xc: /* SQDMULH */ - case 0xd: /* SQRDMULH */ - if (u) { - unallocated_encoding(s); - return; - } break; - case 0x8: /* MUL */ - if (u || is_scalar) { - unallocated_encoding(s); - return; - } + case 0x0c: /* SQDMULH */ + case 0x0d: /* SQRDMULH */ break; - case 0x1: /* FMLA */ - case 0x5: /* FMLS */ - if (u) { + case 0x01: /* FMLA */ + case 0x05: /* FMLS */ + case 0x09: /* FMUL */ + case 0x19: /* FMULX */ + is_fp = 1; + break; + case 0x1d: /* SQRDMLAH */ + case 0x1f: /* SQRDMLSH */ + if (!arm_dc_feature(s, ARM_FEATURE_V8_RDM)) { unallocated_encoding(s); return; } - /* fall through */ - case 0x9: /* FMUL, FMULX */ - if (size == 1) { + break; + case 0x11: /* FCMLA #0 */ + case 0x13: /* FCMLA #90 */ + case 0x15: /* FCMLA #180 */ + case 0x17: /* FCMLA #270 */ + if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA)) { unallocated_encoding(s); return; } - is_fp = true; + is_fp = 2; break; default: unallocated_encoding(s); return; } - if (is_fp) { + switch (is_fp) { + case 1: /* normal fp */ /* convert insn encoded size to TCGMemOp size */ switch (size) { - case 2: /* single precision */ - size = MO_32; - index = h << 1 | l; - rm |= (m << 4); - break; - case 3: /* double precision */ - size = MO_64; - if (l || !is_q) { - unallocated_encoding(s); - return; - } - index = h; - rm |= (m << 4); - break; - case 0: /* half precision */ + case 0: /* half-precision */ size = MO_16; - index = h << 2 | l << 1 | m; is_fp16 = true; - if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { - break; - } - /* fallthru */ - default: /* unallocated */ + break; + case MO_32: /* single precision */ + case MO_64: /* double precision */ + break; + default: unallocated_encoding(s); return; } - } else { + break; + + case 2: /* complex fp */ + /* Each indexable element is a complex pair. */ + size <<= 1; switch (size) { - case 1: - index = h << 2 | l << 1 | m; + case MO_32: + if (h && !is_q) { + unallocated_encoding(s); + return; + } + is_fp16 = true; break; - case 2: - index = h << 1 | l; - rm |= (m << 4); + case MO_64: break; default: unallocated_encoding(s); return; } + break; + + default: /* integer */ + switch (size) { + case MO_8: + case MO_64: + unallocated_encoding(s); + return; + } + break; + } + if (is_fp16 && !arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + + /* Given TCGMemOp size, adjust register and indexing. */ + switch (size) { + case MO_16: + index = h << 2 | l << 1 | m; + break; + case MO_32: + index = h << 1 | l; + rm |= m << 4; + break; + case MO_64: + if (l || !is_q) { + unallocated_encoding(s); + return; + } + index = h; + rm |= m << 4; + break; + default: + g_assert_not_reached(); } if (!fp_access_check(s)) { @@ -11895,6 +12163,23 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) fpst = NULL; } + switch (16 * u + opcode) { + case 0x11: /* FCMLA #0 */ + case 0x13: /* FCMLA #90 */ + case 0x15: /* FCMLA #180 */ + case 0x17: /* FCMLA #270 */ + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_reg_offset(s, rm, index, size), fpst, + is_q ? 16 : 8, vec_full_reg_size(s), + extract32(insn, 13, 2), /* rot */ + size == MO_64 + ? gen_helper_gvec_fcmlas_idx + : gen_helper_gvec_fcmlah_idx); + tcg_temp_free_ptr(fpst); + return; + } + if (size == 3) { TCGv_i64 tcg_idx = tcg_temp_new_i64(); int pass; @@ -11909,21 +12194,20 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) read_vec_element(s, tcg_op, rn, pass, MO_64); - switch (opcode) { - case 0x5: /* FMLS */ + switch (16 * u + opcode) { + case 0x05: /* FMLS */ /* As usual for ARM, separate negation for fused multiply-add */ gen_helper_vfp_negd(tcg_op, tcg_op); /* fall through */ - case 0x1: /* FMLA */ + case 0x01: /* FMLA */ read_vec_element(s, tcg_res, rd, pass, MO_64); gen_helper_vfp_muladdd(tcg_res, tcg_op, tcg_idx, tcg_res, fpst); break; - case 0x9: /* FMUL, FMULX */ - if (u) { - gen_helper_vfp_mulxd(tcg_res, tcg_op, tcg_idx, fpst); - } else { - gen_helper_vfp_muld(tcg_res, tcg_op, tcg_idx, fpst); - } + case 0x09: /* FMUL */ + gen_helper_vfp_muld(tcg_res, tcg_op, tcg_idx, fpst); + break; + case 0x19: /* FMULX */ + gen_helper_vfp_mulxd(tcg_res, tcg_op, tcg_idx, fpst); break; default: g_assert_not_reached(); @@ -11966,10 +12250,10 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_op, rn, pass, is_scalar ? size : MO_32); - switch (opcode) { - case 0x0: /* MLA */ - case 0x4: /* MLS */ - case 0x8: /* MUL */ + switch (16 * u + opcode) { + case 0x08: /* MUL */ + case 0x10: /* MLA */ + case 0x14: /* MLS */ { static NeonGenTwoOpFn * const fns[2][2] = { { gen_helper_neon_add_u16, gen_helper_neon_sub_u16 }, @@ -11991,8 +12275,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) genfn(tcg_res, tcg_op, tcg_res); break; } - case 0x5: /* FMLS */ - case 0x1: /* FMLA */ + case 0x05: /* FMLS */ + case 0x01: /* FMLA */ read_vec_element_i32(s, tcg_res, rd, pass, is_scalar ? size : MO_32); switch (size) { @@ -12023,39 +12307,43 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) g_assert_not_reached(); } break; - case 0x9: /* FMUL, FMULX */ + case 0x09: /* FMUL */ switch (size) { case 1: - if (u) { - if (is_scalar) { - gen_helper_advsimd_mulxh(tcg_res, tcg_op, - tcg_idx, fpst); - } else { - gen_helper_advsimd_mulx2h(tcg_res, tcg_op, - tcg_idx, fpst); - } + if (is_scalar) { + gen_helper_advsimd_mulh(tcg_res, tcg_op, + tcg_idx, fpst); } else { - if (is_scalar) { - gen_helper_advsimd_mulh(tcg_res, tcg_op, - tcg_idx, fpst); - } else { - gen_helper_advsimd_mul2h(tcg_res, tcg_op, - tcg_idx, fpst); - } + gen_helper_advsimd_mul2h(tcg_res, tcg_op, + tcg_idx, fpst); } break; case 2: - if (u) { - gen_helper_vfp_mulxs(tcg_res, tcg_op, tcg_idx, fpst); + gen_helper_vfp_muls(tcg_res, tcg_op, tcg_idx, fpst); + break; + default: + g_assert_not_reached(); + } + break; + case 0x19: /* FMULX */ + switch (size) { + case 1: + if (is_scalar) { + gen_helper_advsimd_mulxh(tcg_res, tcg_op, + tcg_idx, fpst); } else { - gen_helper_vfp_muls(tcg_res, tcg_op, tcg_idx, fpst); + gen_helper_advsimd_mulx2h(tcg_res, tcg_op, + tcg_idx, fpst); } break; + case 2: + gen_helper_vfp_mulxs(tcg_res, tcg_op, tcg_idx, fpst); + break; default: g_assert_not_reached(); } break; - case 0xc: /* SQDMULH */ + case 0x0c: /* SQDMULH */ if (size == 1) { gen_helper_neon_qdmulh_s16(tcg_res, cpu_env, tcg_op, tcg_idx); @@ -12064,7 +12352,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) tcg_op, tcg_idx); } break; - case 0xd: /* SQRDMULH */ + case 0x0d: /* SQRDMULH */ if (size == 1) { gen_helper_neon_qrdmulh_s16(tcg_res, cpu_env, tcg_op, tcg_idx); @@ -12073,6 +12361,28 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) tcg_op, tcg_idx); } break; + case 0x1d: /* SQRDMLAH */ + read_vec_element_i32(s, tcg_res, rd, pass, + is_scalar ? size : MO_32); + if (size == 1) { + gen_helper_neon_qrdmlah_s16(tcg_res, cpu_env, + tcg_op, tcg_idx, tcg_res); + } else { + gen_helper_neon_qrdmlah_s32(tcg_res, cpu_env, + tcg_op, tcg_idx, tcg_res); + } + break; + case 0x1f: /* SQRDMLSH */ + read_vec_element_i32(s, tcg_res, rd, pass, + is_scalar ? size : MO_32); + if (size == 1) { + gen_helper_neon_qrdmlsh_s16(tcg_res, cpu_env, + tcg_op, tcg_idx, tcg_res); + } else { + gen_helper_neon_qrdmlsh_s32(tcg_res, cpu_env, + tcg_op, tcg_idx, tcg_res); + } + break; default: g_assert_not_reached(); } @@ -12794,6 +13104,7 @@ static void disas_crypto_three_reg_imm2(DisasContext *s, uint32_t insn) static const AArch64DecodeTable data_proc_simd[] = { /* pattern , mask , fn */ { 0x0e200400, 0x9f200400, disas_simd_three_reg_same }, + { 0x0e008400, 0x9f208400, disas_simd_three_reg_same_extra }, { 0x0e200000, 0x9f200c00, disas_simd_three_reg_diff }, { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, { 0x0e300800, 0x9f3e0c00, disas_simd_across_lanes }, @@ -12806,6 +13117,7 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0x0e000800, 0xbf208c00, disas_simd_zip_trn }, { 0x2e000000, 0xbf208400, disas_simd_ext }, { 0x5e200400, 0xdf200400, disas_simd_scalar_three_reg_same }, + { 0x5e008400, 0xdf208400, disas_simd_scalar_three_reg_same_extra }, { 0x5e200000, 0xdf200c00, disas_simd_scalar_three_reg_diff }, { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, { 0x5e300800, 0xdf3e0c00, disas_simd_scalar_pairwise }, diff --git a/target/arm/translate.c b/target/arm/translate.c index aa6dcaa577..ba6ab7d287 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -25,6 +25,7 @@ #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg-op.h" +#include "tcg-op-gvec.h" #include "qemu/log.h" #include "qemu/bitops.h" #include "arm_ldst.h" @@ -75,6 +76,10 @@ static const char *regnames[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "pc" }; +/* Function prototypes for gen_ functions calling Neon helpers. */ +typedef void NeonGenThreeOpEnvFn(TCGv_i32, TCGv_env, TCGv_i32, + TCGv_i32, TCGv_i32); + /* initialize TCG globals. */ void arm_translate_init(void) { @@ -5374,9 +5379,9 @@ static void gen_neon_narrow_op(int op, int u, int size, #define NEON_3R_VPMAX 20 #define NEON_3R_VPMIN 21 #define NEON_3R_VQDMULH_VQRDMULH 22 -#define NEON_3R_VPADD 23 +#define NEON_3R_VPADD_VQRDMLAH 23 #define NEON_3R_SHA 24 /* SHA1C,SHA1P,SHA1M,SHA1SU0,SHA256H{2},SHA256SU1 */ -#define NEON_3R_VFM 25 /* VFMA, VFMS : float fused multiply-add */ +#define NEON_3R_VFM_VQRDMLSH 25 /* VFMA, VFMS, VQRDMLSH */ #define NEON_3R_FLOAT_ARITH 26 /* float VADD, VSUB, VPADD, VABD */ #define NEON_3R_FLOAT_MULTIPLY 27 /* float VMLA, VMLS, VMUL */ #define NEON_3R_FLOAT_CMP 28 /* float VCEQ, VCGE, VCGT */ @@ -5408,9 +5413,9 @@ static const uint8_t neon_3r_sizes[] = { [NEON_3R_VPMAX] = 0x7, [NEON_3R_VPMIN] = 0x7, [NEON_3R_VQDMULH_VQRDMULH] = 0x6, - [NEON_3R_VPADD] = 0x7, + [NEON_3R_VPADD_VQRDMLAH] = 0x7, [NEON_3R_SHA] = 0xf, /* size field encodes op type */ - [NEON_3R_VFM] = 0x5, /* size bit 1 encodes op */ + [NEON_3R_VFM_VQRDMLSH] = 0x7, /* For VFM, size bit 1 encodes op */ [NEON_3R_FLOAT_ARITH] = 0x5, /* size bit 1 encodes op */ [NEON_3R_FLOAT_MULTIPLY] = 0x5, /* size bit 1 encodes op */ [NEON_3R_FLOAT_CMP] = 0x5, /* size bit 1 encodes op */ @@ -5589,6 +5594,22 @@ static const uint8_t neon_2rm_sizes[] = { [NEON_2RM_VCVT_UF] = 0x4, }; + +/* Expand v8.1 simd helper. */ +static int do_v81_helper(DisasContext *s, gen_helper_gvec_3_ptr *fn, + int q, int rd, int rn, int rm) +{ + if (arm_dc_feature(s, ARM_FEATURE_V8_RDM)) { + int opr_sz = (1 + q) * 8; + tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), cpu_env, + opr_sz, opr_sz, 0, fn); + return 0; + } + return 1; +} + /* Translate a NEON data processing instruction. Return nonzero if the instruction is invalid. We process data in a mixture of 32-bit and 64-bit chunks. @@ -5641,12 +5662,13 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) if (q && ((rd | rn | rm) & 1)) { return 1; } - /* - * The SHA-1/SHA-256 3-register instructions require special treatment - * here, as their size field is overloaded as an op type selector, and - * they all consume their input in a single pass. - */ - if (op == NEON_3R_SHA) { + switch (op) { + case NEON_3R_SHA: + /* The SHA-1/SHA-256 3-register instructions require special + * treatment here, as their size field is overloaded as an + * op type selector, and they all consume their input in a + * single pass. + */ if (!q) { return 1; } @@ -5683,6 +5705,40 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) tcg_temp_free_ptr(ptr2); tcg_temp_free_ptr(ptr3); return 0; + + case NEON_3R_VPADD_VQRDMLAH: + if (!u) { + break; /* VPADD */ + } + /* VQRDMLAH */ + switch (size) { + case 1: + return do_v81_helper(s, gen_helper_gvec_qrdmlah_s16, + q, rd, rn, rm); + case 2: + return do_v81_helper(s, gen_helper_gvec_qrdmlah_s32, + q, rd, rn, rm); + } + return 1; + + case NEON_3R_VFM_VQRDMLSH: + if (!u) { + /* VFM, VFMS */ + if (size == 1) { + return 1; + } + break; + } + /* VQRDMLSH */ + switch (size) { + case 1: + return do_v81_helper(s, gen_helper_gvec_qrdmlsh_s16, + q, rd, rn, rm); + case 2: + return do_v81_helper(s, gen_helper_gvec_qrdmlsh_s32, + q, rd, rn, rm); + } + return 1; } if (size == 3 && op != NEON_3R_LOGIC) { /* 64-bit element instructions. */ @@ -5768,11 +5824,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) rm = rtmp; } break; - case NEON_3R_VPADD: - if (u) { - return 1; - } - /* Fall through */ + case NEON_3R_VPADD_VQRDMLAH: case NEON_3R_VPMAX: case NEON_3R_VPMIN: pairwise = 1; @@ -5806,8 +5858,8 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) return 1; } break; - case NEON_3R_VFM: - if (!arm_dc_feature(s, ARM_FEATURE_VFP4) || u) { + case NEON_3R_VFM_VQRDMLSH: + if (!arm_dc_feature(s, ARM_FEATURE_VFP4)) { return 1; } break; @@ -6004,7 +6056,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } } break; - case NEON_3R_VPADD: + case NEON_3R_VPADD_VQRDMLAH: switch (size) { case 0: gen_helper_neon_padd_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_padd_u16(tmp, tmp, tmp2); break; @@ -6103,7 +6155,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } } break; - case NEON_3R_VFM: + case NEON_3R_VFM_VQRDMLSH: { /* VFMA, VFMS: fused multiply-add */ TCGv_ptr fpstatus = get_fpstatus_ptr(1); @@ -6937,11 +6989,45 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } neon_store_reg64(cpu_V0, rd + pass); } + break; + case 14: /* VQRDMLAH scalar */ + case 15: /* VQRDMLSH scalar */ + { + NeonGenThreeOpEnvFn *fn; + if (!arm_dc_feature(s, ARM_FEATURE_V8_RDM)) { + return 1; + } + if (u && ((rd | rn) & 1)) { + return 1; + } + if (op == 14) { + if (size == 1) { + fn = gen_helper_neon_qrdmlah_s16; + } else { + fn = gen_helper_neon_qrdmlah_s32; + } + } else { + if (size == 1) { + fn = gen_helper_neon_qrdmlsh_s16; + } else { + fn = gen_helper_neon_qrdmlsh_s32; + } + } + tmp2 = neon_get_scalar(size, rm); + for (pass = 0; pass < (u ? 4 : 2); pass++) { + tmp = neon_load_reg(rn, pass); + tmp3 = neon_load_reg(rd, pass); + fn(tmp, cpu_env, tmp, tmp2, tmp3); + tcg_temp_free_i32(tmp3); + neon_store_reg(rd, pass, tmp); + } + tcg_temp_free_i32(tmp2); + } break; - default: /* 14 and 15 are RESERVED */ - return 1; + default: + g_assert_not_reached(); } } } else { /* size == 3 */ @@ -7594,6 +7680,123 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) return 0; } +/* Advanced SIMD three registers of the same length extension. + * 31 25 23 22 20 16 12 11 10 9 8 3 0 + * +---------------+-----+---+-----+----+----+---+----+---+----+---------+----+ + * | 1 1 1 1 1 1 0 | op1 | D | op2 | Vn | Vd | 1 | o3 | 0 | o4 | N Q M U | Vm | + * +---------------+-----+---+-----+----+----+---+----+---+----+---------+----+ + */ +static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) +{ + gen_helper_gvec_3_ptr *fn_gvec_ptr; + int rd, rn, rm, rot, size, opr_sz; + TCGv_ptr fpst; + bool q; + + q = extract32(insn, 6, 1); + VFP_DREG_D(rd, insn); + VFP_DREG_N(rn, insn); + VFP_DREG_M(rm, insn); + if ((rd | rn | rm) & q) { + return 1; + } + + if ((insn & 0xfe200f10) == 0xfc200800) { + /* VCMLA -- 1111 110R R.1S .... .... 1000 ...0 .... */ + size = extract32(insn, 20, 1); + rot = extract32(insn, 23, 2); + if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) + || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { + return 1; + } + fn_gvec_ptr = size ? gen_helper_gvec_fcmlas : gen_helper_gvec_fcmlah; + } else if ((insn & 0xfea00f10) == 0xfc800800) { + /* VCADD -- 1111 110R 1.0S .... .... 1000 ...0 .... */ + size = extract32(insn, 20, 1); + rot = extract32(insn, 24, 1); + if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) + || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { + return 1; + } + fn_gvec_ptr = size ? gen_helper_gvec_fcadds : gen_helper_gvec_fcaddh; + } else { + return 1; + } + + if (s->fp_excp_el) { + gen_exception_insn(s, 4, EXCP_UDEF, + syn_fp_access_trap(1, 0xe, false), s->fp_excp_el); + return 0; + } + if (!s->vfp_enabled) { + return 1; + } + + opr_sz = (1 + q) * 8; + fpst = get_fpstatus_ptr(1); + tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), fpst, + opr_sz, opr_sz, rot, fn_gvec_ptr); + tcg_temp_free_ptr(fpst); + return 0; +} + +/* Advanced SIMD two registers and a scalar extension. + * 31 24 23 22 20 16 12 11 10 9 8 3 0 + * +-----------------+----+---+----+----+----+---+----+---+----+---------+----+ + * | 1 1 1 1 1 1 1 0 | o1 | D | o2 | Vn | Vd | 1 | o3 | 0 | o4 | N Q M U | Vm | + * +-----------------+----+---+----+----+----+---+----+---+----+---------+----+ + * + */ + +static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) +{ + int rd, rn, rm, rot, size, opr_sz; + TCGv_ptr fpst; + bool q; + + q = extract32(insn, 6, 1); + VFP_DREG_D(rd, insn); + VFP_DREG_N(rn, insn); + VFP_DREG_M(rm, insn); + if ((rd | rn) & q) { + return 1; + } + + if ((insn & 0xff000f10) == 0xfe000800) { + /* VCMLA (indexed) -- 1111 1110 S.RR .... .... 1000 ...0 .... */ + rot = extract32(insn, 20, 2); + size = extract32(insn, 23, 1); + if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) + || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { + return 1; + } + } else { + return 1; + } + + if (s->fp_excp_el) { + gen_exception_insn(s, 4, EXCP_UDEF, + syn_fp_access_trap(1, 0xe, false), s->fp_excp_el); + return 0; + } + if (!s->vfp_enabled) { + return 1; + } + + opr_sz = (1 + q) * 8; + fpst = get_fpstatus_ptr(1); + tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), fpst, + opr_sz, opr_sz, rot, + size ? gen_helper_gvec_fcmlas_idx + : gen_helper_gvec_fcmlah_idx); + tcg_temp_free_ptr(fpst); + return 0; +} + static int disas_coproc_insn(DisasContext *s, uint32_t insn) { int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2; @@ -8338,6 +8541,18 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } } } + } else if ((insn & 0x0e000a00) == 0x0c000800 + && arm_dc_feature(s, ARM_FEATURE_V8)) { + if (disas_neon_insn_3same_ext(s, insn)) { + goto illegal_op; + } + return; + } else if ((insn & 0x0f000a00) == 0x0e000800 + && arm_dc_feature(s, ARM_FEATURE_V8)) { + if (disas_neon_insn_2reg_scalar_ext(s, insn)) { + goto illegal_op; + } + return; } else if ((insn & 0x0fe00000) == 0x0c400000) { /* Coprocessor double register transfer. */ ARCH(5TE); @@ -10559,7 +10774,19 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) default_exception_el(s)); break; } - if (((insn >> 24) & 3) == 3) { + if ((insn & 0xfe000a00) == 0xfc000800 + && arm_dc_feature(s, ARM_FEATURE_V8)) { + /* The Thumb2 and ARM encodings are identical. */ + if (disas_neon_insn_3same_ext(s, insn)) { + goto illegal_op; + } + } else if ((insn & 0xff000a00) == 0xfe000800 + && arm_dc_feature(s, ARM_FEATURE_V8)) { + /* The Thumb2 and ARM encodings are identical. */ + if (disas_neon_insn_2reg_scalar_ext(s, insn)) { + goto illegal_op; + } + } else if (((insn >> 24) & 3) == 3) { /* Translate into the equivalent ARM encoding. */ insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28); if (disas_neon_data_insn(s, insn)) { diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c new file mode 100644 index 0000000000..ec705cfca5 --- /dev/null +++ b/target/arm/vec_helper.c @@ -0,0 +1,429 @@ +/* + * ARM AdvSIMD / SVE Vector Operations + * + * Copyright (c) 2018 Linaro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "tcg/tcg-gvec-desc.h" +#include "fpu/softfloat.h" + + +/* Note that vector data is stored in host-endian 64-bit chunks, + so addressing units smaller than that needs a host-endian fixup. */ +#ifdef HOST_WORDS_BIGENDIAN +#define H1(x) ((x) ^ 7) +#define H2(x) ((x) ^ 3) +#define H4(x) ((x) ^ 1) +#else +#define H1(x) (x) +#define H2(x) (x) +#define H4(x) (x) +#endif + +#define SET_QC() env->vfp.xregs[ARM_VFP_FPSCR] |= CPSR_Q + +static void clear_tail(void *vd, uintptr_t opr_sz, uintptr_t max_sz) +{ + uint64_t *d = vd + opr_sz; + uintptr_t i; + + for (i = opr_sz; i < max_sz; i += 8) { + *d++ = 0; + } +} + +/* Signed saturating rounding doubling multiply-accumulate high half, 16-bit */ +static uint16_t inl_qrdmlah_s16(CPUARMState *env, int16_t src1, + int16_t src2, int16_t src3) +{ + /* Simplify: + * = ((a3 << 16) + ((e1 * e2) << 1) + (1 << 15)) >> 16 + * = ((a3 << 15) + (e1 * e2) + (1 << 14)) >> 15 + */ + int32_t ret = (int32_t)src1 * src2; + ret = ((int32_t)src3 << 15) + ret + (1 << 14); + ret >>= 15; + if (ret != (int16_t)ret) { + SET_QC(); + ret = (ret < 0 ? -0x8000 : 0x7fff); + } + return ret; +} + +uint32_t HELPER(neon_qrdmlah_s16)(CPUARMState *env, uint32_t src1, + uint32_t src2, uint32_t src3) +{ + uint16_t e1 = inl_qrdmlah_s16(env, src1, src2, src3); + uint16_t e2 = inl_qrdmlah_s16(env, src1 >> 16, src2 >> 16, src3 >> 16); + return deposit32(e1, 16, 16, e2); +} + +void HELPER(gvec_qrdmlah_s16)(void *vd, void *vn, void *vm, + void *ve, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + int16_t *d = vd; + int16_t *n = vn; + int16_t *m = vm; + CPUARMState *env = ve; + uintptr_t i; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = inl_qrdmlah_s16(env, n[i], m[i], d[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* Signed saturating rounding doubling multiply-subtract high half, 16-bit */ +static uint16_t inl_qrdmlsh_s16(CPUARMState *env, int16_t src1, + int16_t src2, int16_t src3) +{ + /* Similarly, using subtraction: + * = ((a3 << 16) - ((e1 * e2) << 1) + (1 << 15)) >> 16 + * = ((a3 << 15) - (e1 * e2) + (1 << 14)) >> 15 + */ + int32_t ret = (int32_t)src1 * src2; + ret = ((int32_t)src3 << 15) - ret + (1 << 14); + ret >>= 15; + if (ret != (int16_t)ret) { + SET_QC(); + ret = (ret < 0 ? -0x8000 : 0x7fff); + } + return ret; +} + +uint32_t HELPER(neon_qrdmlsh_s16)(CPUARMState *env, uint32_t src1, + uint32_t src2, uint32_t src3) +{ + uint16_t e1 = inl_qrdmlsh_s16(env, src1, src2, src3); + uint16_t e2 = inl_qrdmlsh_s16(env, src1 >> 16, src2 >> 16, src3 >> 16); + return deposit32(e1, 16, 16, e2); +} + +void HELPER(gvec_qrdmlsh_s16)(void *vd, void *vn, void *vm, + void *ve, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + int16_t *d = vd; + int16_t *n = vn; + int16_t *m = vm; + CPUARMState *env = ve; + uintptr_t i; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = inl_qrdmlsh_s16(env, n[i], m[i], d[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* Signed saturating rounding doubling multiply-accumulate high half, 32-bit */ +uint32_t HELPER(neon_qrdmlah_s32)(CPUARMState *env, int32_t src1, + int32_t src2, int32_t src3) +{ + /* Simplify similarly to int_qrdmlah_s16 above. */ + int64_t ret = (int64_t)src1 * src2; + ret = ((int64_t)src3 << 31) + ret + (1 << 30); + ret >>= 31; + if (ret != (int32_t)ret) { + SET_QC(); + ret = (ret < 0 ? INT32_MIN : INT32_MAX); + } + return ret; +} + +void HELPER(gvec_qrdmlah_s32)(void *vd, void *vn, void *vm, + void *ve, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + int32_t *d = vd; + int32_t *n = vn; + int32_t *m = vm; + CPUARMState *env = ve; + uintptr_t i; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = helper_neon_qrdmlah_s32(env, n[i], m[i], d[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* Signed saturating rounding doubling multiply-subtract high half, 32-bit */ +uint32_t HELPER(neon_qrdmlsh_s32)(CPUARMState *env, int32_t src1, + int32_t src2, int32_t src3) +{ + /* Simplify similarly to int_qrdmlsh_s16 above. */ + int64_t ret = (int64_t)src1 * src2; + ret = ((int64_t)src3 << 31) - ret + (1 << 30); + ret >>= 31; + if (ret != (int32_t)ret) { + SET_QC(); + ret = (ret < 0 ? INT32_MIN : INT32_MAX); + } + return ret; +} + +void HELPER(gvec_qrdmlsh_s32)(void *vd, void *vn, void *vm, + void *ve, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + int32_t *d = vd; + int32_t *n = vn; + int32_t *m = vm; + CPUARMState *env = ve; + uintptr_t i; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = helper_neon_qrdmlsh_s32(env, n[i], m[i], d[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float16 *d = vd; + float16 *n = vn; + float16 *m = vm; + float_status *fpst = vfpst; + uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = neg_real ^ 1; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 15; + neg_imag <<= 15; + + for (i = 0; i < opr_sz / 2; i += 2) { + float16 e0 = n[H2(i)]; + float16 e1 = m[H2(i + 1)] ^ neg_imag; + float16 e2 = n[H2(i + 1)]; + float16 e3 = m[H2(i)] ^ neg_real; + + d[H2(i)] = float16_add(e0, e1, fpst); + d[H2(i + 1)] = float16_add(e2, e3, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcadds)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float32 *d = vd; + float32 *n = vn; + float32 *m = vm; + float_status *fpst = vfpst; + uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = neg_real ^ 1; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 31; + neg_imag <<= 31; + + for (i = 0; i < opr_sz / 4; i += 2) { + float32 e0 = n[H4(i)]; + float32 e1 = m[H4(i + 1)] ^ neg_imag; + float32 e2 = n[H4(i + 1)]; + float32 e3 = m[H4(i)] ^ neg_real; + + d[H4(i)] = float32_add(e0, e1, fpst); + d[H4(i + 1)] = float32_add(e2, e3, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcaddd)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float64 *d = vd; + float64 *n = vn; + float64 *m = vm; + float_status *fpst = vfpst; + uint64_t neg_real = extract64(desc, SIMD_DATA_SHIFT, 1); + uint64_t neg_imag = neg_real ^ 1; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 63; + neg_imag <<= 63; + + for (i = 0; i < opr_sz / 8; i += 2) { + float64 e0 = n[i]; + float64 e1 = m[i + 1] ^ neg_imag; + float64 e2 = n[i + 1]; + float64 e3 = m[i] ^ neg_real; + + d[i] = float64_add(e0, e1, fpst); + d[i + 1] = float64_add(e2, e3, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlah)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float16 *d = vd; + float16 *n = vn; + float16 *m = vm; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t neg_real = flip ^ neg_imag; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 15; + neg_imag <<= 15; + + for (i = 0; i < opr_sz / 2; i += 2) { + float16 e2 = n[H2(i + flip)]; + float16 e1 = m[H2(i + flip)] ^ neg_real; + float16 e4 = e2; + float16 e3 = m[H2(i + 1 - flip)] ^ neg_imag; + + d[H2(i)] = float16_muladd(e2, e1, d[H2(i)], 0, fpst); + d[H2(i + 1)] = float16_muladd(e4, e3, d[H2(i + 1)], 0, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float16 *d = vd; + float16 *n = vn; + float16 *m = vm; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t neg_real = flip ^ neg_imag; + uintptr_t i; + float16 e1 = m[H2(flip)]; + float16 e3 = m[H2(1 - flip)]; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 15; + neg_imag <<= 15; + e1 ^= neg_real; + e3 ^= neg_imag; + + for (i = 0; i < opr_sz / 2; i += 2) { + float16 e2 = n[H2(i + flip)]; + float16 e4 = e2; + + d[H2(i)] = float16_muladd(e2, e1, d[H2(i)], 0, fpst); + d[H2(i + 1)] = float16_muladd(e4, e3, d[H2(i + 1)], 0, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlas)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float32 *d = vd; + float32 *n = vn; + float32 *m = vm; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t neg_real = flip ^ neg_imag; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 31; + neg_imag <<= 31; + + for (i = 0; i < opr_sz / 4; i += 2) { + float32 e2 = n[H4(i + flip)]; + float32 e1 = m[H4(i + flip)] ^ neg_real; + float32 e4 = e2; + float32 e3 = m[H4(i + 1 - flip)] ^ neg_imag; + + d[H4(i)] = float32_muladd(e2, e1, d[H4(i)], 0, fpst); + d[H4(i + 1)] = float32_muladd(e4, e3, d[H4(i + 1)], 0, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float32 *d = vd; + float32 *n = vn; + float32 *m = vm; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t neg_real = flip ^ neg_imag; + uintptr_t i; + float32 e1 = m[H4(flip)]; + float32 e3 = m[H4(1 - flip)]; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 31; + neg_imag <<= 31; + e1 ^= neg_real; + e3 ^= neg_imag; + + for (i = 0; i < opr_sz / 4; i += 2) { + float32 e2 = n[H4(i + flip)]; + float32 e4 = e2; + + d[H4(i)] = float32_muladd(e2, e1, d[H4(i)], 0, fpst); + d[H4(i + 1)] = float32_muladd(e4, e3, d[H4(i + 1)], 0, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlad)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float64 *d = vd; + float64 *n = vn; + float64 *m = vm; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint64_t neg_real = flip ^ neg_imag; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 63; + neg_imag <<= 63; + + for (i = 0; i < opr_sz / 8; i += 2) { + float64 e2 = n[i + flip]; + float64 e1 = m[i + flip] ^ neg_real; + float64 e4 = e2; + float64 e3 = m[i + 1 - flip] ^ neg_imag; + + d[i] = float64_muladd(e2, e1, d[i], 0, fpst); + d[i + 1] = float64_muladd(e4, e3, d[i + 1], 0, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} |