mirror of
https://github.com/qemu/qemu.git
synced 2024-11-28 06:13:46 +08:00
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJamTAEAAoJEDwlJe0UNgzeLRoP/3ODFPW6nHkKbV/IYSX5AmMw gN0sWhd3NrKYJ3kIV22JUJ+Lj8uHspY3XkiL93xQ1EAGCi54TVNwoH3BlHiPdq07 iHGlG8Fm8DHQHQWvxB902V1Z64PXaS+2CJ9tsIRjiWKq32UPXCW9SuvlKRZDfdkJ L4PxO9DJdDRP4z8u7w/dnOBtEmwFMsDQlbB/tXYY7y9RGoGADdOwdYrKyLKaQjAE N4yIHU6wcMxZuCOEJVrxKBNen71YGlqi9nVq4+Quf9n8OC6P8Y6elMknxDUzeiCn poWUjuTdMcFG0LJ6cM74m/8pymgZJRaalW0q1yjV2NYD1FPJ4UjjZiWvvpSlIyrv tLUOLysH+G6Iv3GG9opuz8xqWIfCzVKa0kdq6uDIouEs5c0WlgFBujMqwwut8xWy auRxyVAgN8qFM7KwuuwlwdF6CDwJPbmdswYdlOW+sGpD0LBYnA5uY+x0xzzM8x6E FbBk68AXcmOMKMJRVnS+7SozDuZuWKmmG+JNBq4wRtHIxb07SSzfEO3dtDNb4a1u iQI/hMTyV9gtKxSOnK4FViMky/kf6i9eFFAdvAwPo/oj/PLmjv0bEL7Dnu68TBvk x5WuyPigLpZkiHVCuz7rsQkG1icEUVztkmeGo3acvR8TBOV2YEpkKWgCBgTu+7cM EjCAvuxzALREOop32QTY =uwlT -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
86f4c7e05b
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
119
hw/arm/boot.c
119
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);
|
||||
|
598
hw/arm/iotkit.c
Normal file
598
hw/arm/iotkit.c
Normal file
@ -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);
|
503
hw/arm/mps2-tz.c
Normal file
503
hw/arm/mps2-tz.c
Normal file
@ -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);
|
@ -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[] = {
|
||||
|
@ -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
|
||||
|
@ -729,9 +729,15 @@ 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. */
|
||||
|
@ -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";
|
||||
|
89
hw/core/split-irq.c
Normal file
89
hw/core/split-irq.c
Normal file
@ -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)
|
@ -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
|
||||
|
704
hw/misc/iotkit-secctl.c
Normal file
704
hw/misc/iotkit-secctl.c
Normal file
@ -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);
|
176
hw/misc/mps2-fpgaio.c
Normal file
176
hw/misc/mps2-fpgaio.c
Normal file
@ -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);
|
@ -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"
|
||||
|
302
hw/misc/tz-ppc.c
Normal file
302
hw/misc/tz-ppc.c
Normal file
@ -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);
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
272
hw/timer/xlnx-zynqmp-rtc.c
Normal file
272
hw/timer/xlnx-zynqmp-rtc.c
Normal file
@ -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)
|
@ -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
|
||||
|
109
include/hw/arm/iotkit.h
Normal file
109
include/hw/arm/iotkit.h
Normal file
@ -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
|
@ -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;
|
||||
|
57
include/hw/core/split-irq.h
Normal file
57
include/hw/core/split-irq.h
Normal file
@ -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
|
@ -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
|
||||
|
@ -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);
|
||||
|
103
include/hw/misc/iotkit-secctl.h
Normal file
103
include/hw/misc/iotkit-secctl.h
Normal file
@ -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
|
43
include/hw/misc/mps2-fpgaio.h
Normal file
43
include/hw/misc/mps2-fpgaio.h
Normal file
@ -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
|
101
include/hw/misc/tz-ppc.h
Normal file
101
include/hw/misc/tz-ppc.h
Normal file
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
86
include/hw/timer/xlnx-zynqmp-rtc.h
Normal file
86
include/hw/timer/xlnx-zynqmp-rtc.h
Normal file
@ -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;
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
61
target/arm/idau.h
Normal file
61
target/arm/idau.h
Normal file
@ -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
|
@ -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) {
|
||||
break;
|
||||
case 0x0c: /* SQDMULH */
|
||||
case 0x0d: /* SQRDMULH */
|
||||
break;
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case 0x8: /* MUL */
|
||||
if (u || is_scalar) {
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case 0x1: /* FMLA */
|
||||
case 0x5: /* FMLS */
|
||||
if (u) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
/* fall through */
|
||||
case 0x9: /* FMUL, FMULX */
|
||||
if (size == 1) {
|
||||
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 */
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
switch (size) {
|
||||
case 1:
|
||||
index = h << 2 | l << 1 | m;
|
||||
break;
|
||||
case 2:
|
||||
index = h << 1 | l;
|
||||
rm |= (m << 4);
|
||||
case MO_32: /* single precision */
|
||||
case MO_64: /* double precision */
|
||||
break;
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: /* complex fp */
|
||||
/* Each indexable element is a complex pair. */
|
||||
size <<= 1;
|
||||
switch (size) {
|
||||
case MO_32:
|
||||
if (h && !is_q) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
is_fp16 = true;
|
||||
break;
|
||||
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);
|
||||
} else {
|
||||
gen_helper_vfp_muls(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 0xc: /* SQDMULH */
|
||||
case 0x19: /* FMULX */
|
||||
switch (size) {
|
||||
case 1:
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
gen_helper_vfp_mulxs(tcg_res, tcg_op, tcg_idx, fpst);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
break;
|
||||
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 },
|
||||
|
@ -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;
|
||||
default: /* 14 and 15 are RESERVED */
|
||||
return 1;
|
||||
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:
|
||||
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)) {
|
||||
|
429
target/arm/vec_helper.c
Normal file
429
target/arm/vec_helper.c
Normal file
@ -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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user