aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/Makefile.objs2
-rw-r--r--hw/arm/armv7m.c35
-rw-r--r--hw/arm/boot.c119
-rw-r--r--hw/arm/iotkit.c598
-rw-r--r--hw/arm/mps2-tz.c503
-rw-r--r--hw/arm/xlnx-zynqmp.c14
-rw-r--r--hw/core/Makefile.objs1
-rw-r--r--hw/core/loader.c8
-rw-r--r--hw/core/qdev.c8
-rw-r--r--hw/core/split-irq.c89
-rw-r--r--hw/misc/Makefile.objs4
-rw-r--r--hw/misc/iotkit-secctl.c704
-rw-r--r--hw/misc/mps2-fpgaio.c176
-rw-r--r--hw/misc/trace-events24
-rw-r--r--hw/misc/tz-ppc.c302
-rw-r--r--hw/misc/unimp.c10
-rw-r--r--hw/timer/Makefile.objs1
-rw-r--r--hw/timer/trace-events3
-rw-r--r--hw/timer/xlnx-zynqmp-rtc.c272
19 files changed, 2813 insertions, 60 deletions
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 1c896bafb4..232258160a 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -19,4 +19,6 @@ obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o
obj-$(CONFIG_MPS2) += mps2.o
+obj-$(CONFIG_MPS2) += mps2-tz.o
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
+obj-$(CONFIG_IOTKIT) += iotkit.o
diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c
index 56770a7048..f123cc7d3d 100644
--- a/hw/arm/armv7m.c
+++ b/hw/arm/armv7m.c
@@ -19,6 +19,7 @@
#include "sysemu/qtest.h"
#include "qemu/error-report.h"
#include "exec/address-spaces.h"
+#include "target/arm/idau.h"
/* Bitbanded IO. Each word corresponds to a single bit. */
@@ -162,6 +163,21 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory",
&error_abort);
+ if (object_property_find(OBJECT(s->cpu), "idau", NULL)) {
+ object_property_set_link(OBJECT(s->cpu), s->idau, "idau", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
+ if (object_property_find(OBJECT(s->cpu), "init-svtor", NULL)) {
+ object_property_set_uint(OBJECT(s->cpu), s->init_svtor,
+ "init-svtor", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
object_property_set_bool(OBJECT(s->cpu), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
@@ -217,6 +233,8 @@ static Property armv7m_properties[] = {
DEFINE_PROP_STRING("cpu-type", ARMv7MState, cpu_type),
DEFINE_PROP_LINK("memory", ARMv7MState, board_memory, TYPE_MEMORY_REGION,
MemoryRegion *),
+ DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *),
+ DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0),
DEFINE_PROP_END_OF_LIST(),
};
@@ -270,6 +288,9 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size)
uint64_t entry;
uint64_t lowaddr;
int big_endian;
+ AddressSpace *as;
+ int asidx;
+ CPUState *cs = CPU(cpu);
#ifdef TARGET_WORDS_BIGENDIAN
big_endian = 1;
@@ -282,11 +303,19 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size)
exit(1);
}
+ if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) {
+ asidx = ARMASIdx_S;
+ } else {
+ asidx = ARMASIdx_NS;
+ }
+ as = cpu_get_address_space(cs, asidx);
+
if (kernel_filename) {
- image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
- NULL, big_endian, EM_ARM, 1, 0);
+ image_size = load_elf_as(kernel_filename, NULL, NULL, &entry, &lowaddr,
+ NULL, big_endian, EM_ARM, 1, 0, as);
if (image_size < 0) {
- image_size = load_image_targphys(kernel_filename, 0, mem_size);
+ image_size = load_image_targphys_as(kernel_filename, 0,
+ mem_size, as);
lowaddr = 0;
}
if (image_size < 0) {
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 05108bc42f..6d0c92ab88 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -36,6 +36,25 @@
#define ARM64_TEXT_OFFSET_OFFSET 8
#define ARM64_MAGIC_OFFSET 56
+static AddressSpace *arm_boot_address_space(ARMCPU *cpu,
+ const struct arm_boot_info *info)
+{
+ /* Return the address space to use for bootloader reads and writes.
+ * We prefer the secure address space if the CPU has it and we're
+ * going to boot the guest into it.
+ */
+ int asidx;
+ CPUState *cs = CPU(cpu);
+
+ if (arm_feature(&cpu->env, ARM_FEATURE_EL3) && info->secure_boot) {
+ asidx = ARMASIdx_S;
+ } else {
+ asidx = ARMASIdx_NS;
+ }
+
+ return cpu_get_address_space(cs, asidx);
+}
+
typedef enum {
FIXUP_NONE = 0, /* do nothing */
FIXUP_TERMINATOR, /* end of insns */
@@ -125,7 +144,8 @@ static const ARMInsnFixup smpboot[] = {
};
static void write_bootloader(const char *name, hwaddr addr,
- const ARMInsnFixup *insns, uint32_t *fixupcontext)
+ const ARMInsnFixup *insns, uint32_t *fixupcontext,
+ AddressSpace *as)
{
/* Fix up the specified bootloader fragment and write it into
* guest memory using rom_add_blob_fixed(). fixupcontext is
@@ -164,7 +184,7 @@ static void write_bootloader(const char *name, hwaddr addr,
code[i] = tswap32(insn);
}
- rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr);
+ rom_add_blob_fixed_as(name, code, len * sizeof(uint32_t), addr, as);
g_free(code);
}
@@ -173,6 +193,7 @@ static void default_write_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
uint32_t fixupcontext[FIXUP_MAX];
+ AddressSpace *as = arm_boot_address_space(cpu, info);
fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr;
fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr;
@@ -183,13 +204,14 @@ static void default_write_secondary(ARMCPU *cpu,
}
write_bootloader("smpboot", info->smp_loader_start,
- smpboot, fixupcontext);
+ smpboot, fixupcontext, as);
}
void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
const struct arm_boot_info *info,
hwaddr mvbar_addr)
{
+ AddressSpace *as = arm_boot_address_space(cpu, info);
int n;
uint32_t mvbar_blob[] = {
/* mvbar_addr: secure monitor vectors
@@ -227,22 +249,23 @@ void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
for (n = 0; n < ARRAY_SIZE(mvbar_blob); n++) {
mvbar_blob[n] = tswap32(mvbar_blob[n]);
}
- rom_add_blob_fixed("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob),
- mvbar_addr);
+ rom_add_blob_fixed_as("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob),
+ mvbar_addr, as);
for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
board_setup_blob[n] = tswap32(board_setup_blob[n]);
}
- rom_add_blob_fixed("board-setup", board_setup_blob,
- sizeof(board_setup_blob), info->board_setup_addr);
+ rom_add_blob_fixed_as("board-setup", board_setup_blob,
+ sizeof(board_setup_blob), info->board_setup_addr, as);
}
static void default_reset_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
+ AddressSpace *as = arm_boot_address_space(cpu, info);
CPUState *cs = CPU(cpu);
- address_space_stl_notdirty(&address_space_memory, info->smp_bootreg_addr,
+ address_space_stl_notdirty(as, info->smp_bootreg_addr,
0, MEMTXATTRS_UNSPECIFIED, NULL);
cpu_set_pc(cs, info->smp_loader_start);
}
@@ -253,12 +276,12 @@ static inline bool have_dtb(const struct arm_boot_info *info)
}
#define WRITE_WORD(p, value) do { \
- address_space_stl_notdirty(&address_space_memory, p, value, \
+ address_space_stl_notdirty(as, p, value, \
MEMTXATTRS_UNSPECIFIED, NULL); \
p += 4; \
} while (0)
-static void set_kernel_args(const struct arm_boot_info *info)
+static void set_kernel_args(const struct arm_boot_info *info, AddressSpace *as)
{
int initrd_size = info->initrd_size;
hwaddr base = info->loader_start;
@@ -289,8 +312,9 @@ static void set_kernel_args(const struct arm_boot_info *info)
int cmdline_size;
cmdline_size = strlen(info->kernel_cmdline);
- cpu_physical_memory_write(p + 8, info->kernel_cmdline,
- cmdline_size + 1);
+ address_space_write(as, p + 8, MEMTXATTRS_UNSPECIFIED,
+ (const uint8_t *)info->kernel_cmdline,
+ cmdline_size + 1);
cmdline_size = (cmdline_size >> 2) + 1;
WRITE_WORD(p, cmdline_size + 2);
WRITE_WORD(p, 0x54410009);
@@ -304,7 +328,8 @@ static void set_kernel_args(const struct arm_boot_info *info)
atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3;
WRITE_WORD(p, (atag_board_len + 8) >> 2);
WRITE_WORD(p, 0x414f4d50);
- cpu_physical_memory_write(p, atag_board_buf, atag_board_len);
+ address_space_write(as, p, MEMTXATTRS_UNSPECIFIED,
+ atag_board_buf, atag_board_len);
p += atag_board_len;
}
/* ATAG_END */
@@ -312,7 +337,8 @@ static void set_kernel_args(const struct arm_boot_info *info)
WRITE_WORD(p, 0);
}
-static void set_kernel_args_old(const struct arm_boot_info *info)
+static void set_kernel_args_old(const struct arm_boot_info *info,
+ AddressSpace *as)
{
hwaddr p;
const char *s;
@@ -380,7 +406,8 @@ static void set_kernel_args_old(const struct arm_boot_info *info)
}
s = info->kernel_cmdline;
if (s) {
- cpu_physical_memory_write(p, s, strlen(s) + 1);
+ address_space_write(as, p, MEMTXATTRS_UNSPECIFIED,
+ (const uint8_t *)s, strlen(s) + 1);
} else {
WRITE_WORD(p, 0);
}
@@ -454,6 +481,7 @@ static void fdt_add_psci_node(void *fdt)
* @addr: the address to load the image at
* @binfo: struct describing the boot environment
* @addr_limit: upper limit of the available memory area at @addr
+ * @as: address space to load image to
*
* Load a device tree supplied by the machine or by the user with the
* '-dtb' command line option, and put it at offset @addr in target
@@ -470,7 +498,7 @@ static void fdt_add_psci_node(void *fdt)
* Note: Must not be called unless have_dtb(binfo) is true.
*/
static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
- hwaddr addr_limit)
+ hwaddr addr_limit, AddressSpace *as)
{
void *fdt = NULL;
int size, rc;
@@ -616,7 +644,7 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
/* Put the DTB into the memory map as a ROM image: this will ensure
* the DTB is copied again upon reset, even if addr points into RAM.
*/
- rom_add_blob_fixed("dtb", fdt, size, addr);
+ rom_add_blob_fixed_as("dtb", fdt, size, addr, as);
g_free(fdt);
@@ -703,13 +731,15 @@ static void do_cpu_reset(void *opaque)
}
if (cs == first_cpu) {
+ AddressSpace *as = arm_boot_address_space(cpu, info);
+
cpu_set_pc(cs, info->loader_start);
if (!have_dtb(info)) {
if (old_param) {
- set_kernel_args_old(info);
+ set_kernel_args_old(info, as);
} else {
- set_kernel_args(info);
+ set_kernel_args(info, as);
}
}
} else {
@@ -784,7 +814,7 @@ static int do_arm_linux_init(Object *obj, void *opaque)
static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
uint64_t *lowaddr, uint64_t *highaddr,
- int elf_machine)
+ int elf_machine, AddressSpace *as)
{
bool elf_is64;
union {
@@ -827,9 +857,9 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
}
}
- ret = load_elf(info->kernel_filename, NULL, NULL,
- pentry, lowaddr, highaddr, big_endian, elf_machine,
- 1, data_swab);
+ ret = load_elf_as(info->kernel_filename, NULL, NULL,
+ pentry, lowaddr, highaddr, big_endian, elf_machine,
+ 1, data_swab, as);
if (ret <= 0) {
/* The header loaded but the image didn't */
exit(1);
@@ -839,7 +869,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
}
static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
- hwaddr *entry)
+ hwaddr *entry, AddressSpace *as)
{
hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR;
uint8_t *buffer;
@@ -874,7 +904,7 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
}
*entry = mem_base + kernel_load_offset;
- rom_add_blob_fixed(filename, buffer, size, *entry);
+ rom_add_blob_fixed_as(filename, buffer, size, *entry, as);
g_free(buffer);
@@ -896,6 +926,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
ARMCPU *cpu = n->cpu;
struct arm_boot_info *info =
container_of(n, struct arm_boot_info, load_kernel_notifier);
+ AddressSpace *as = arm_boot_address_space(cpu, info);
/* The board code is not supposed to set secure_board_setup unless
* running its code in secure mode is actually possible, and KVM
@@ -913,7 +944,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
* the kernel is supposed to be loaded by the bootloader), copy the
* DTB to the base of RAM for the bootloader to pick up.
*/
- if (load_dtb(info->loader_start, info, 0) < 0) {
+ if (load_dtb(info->loader_start, info, 0, as) < 0) {
exit(1);
}
}
@@ -988,7 +1019,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
/* Assume that raw images are linux kernels, and ELF images are not. */
kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr,
- &elf_high_addr, elf_machine);
+ &elf_high_addr, elf_machine, as);
if (kernel_size > 0 && have_dtb(info)) {
/* If there is still some room left at the base of RAM, try and put
* the DTB there like we do for images loaded with -bios or -pflash.
@@ -1001,25 +1032,26 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
if (elf_low_addr < info->loader_start) {
elf_low_addr = 0;
}
- if (load_dtb(info->loader_start, info, elf_low_addr) < 0) {
+ if (load_dtb(info->loader_start, info, elf_low_addr, as) < 0) {
exit(1);
}
}
}
entry = elf_entry;
if (kernel_size < 0) {
- kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
- &is_linux, NULL, NULL);
+ kernel_size = load_uimage_as(info->kernel_filename, &entry, NULL,
+ &is_linux, NULL, NULL, as);
}
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
kernel_size = load_aarch64_image(info->kernel_filename,
- info->loader_start, &entry);
+ info->loader_start, &entry, as);
is_linux = 1;
} else if (kernel_size < 0) {
/* 32-bit ARM */
entry = info->loader_start + KERNEL_LOAD_ADDR;
- kernel_size = load_image_targphys(info->kernel_filename, entry,
- info->ram_size - KERNEL_LOAD_ADDR);
+ kernel_size = load_image_targphys_as(info->kernel_filename, entry,
+ info->ram_size - KERNEL_LOAD_ADDR,
+ as);
is_linux = 1;
}
if (kernel_size < 0) {
@@ -1031,15 +1063,16 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
uint32_t fixupcontext[FIXUP_MAX];
if (info->initrd_filename) {
- initrd_size = load_ramdisk(info->initrd_filename,
- info->initrd_start,
- info->ram_size -
- info->initrd_start);
+ initrd_size = load_ramdisk_as(info->initrd_filename,
+ info->initrd_start,
+ info->ram_size - info->initrd_start,
+ as);
if (initrd_size < 0) {
- initrd_size = load_image_targphys(info->initrd_filename,
- info->initrd_start,
- info->ram_size -
- info->initrd_start);
+ initrd_size = load_image_targphys_as(info->initrd_filename,
+ info->initrd_start,
+ info->ram_size -
+ info->initrd_start,
+ as);
}
if (initrd_size < 0) {
error_report("could not load initrd '%s'",
@@ -1080,7 +1113,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
/* Place the DTB after the initrd in memory with alignment. */
dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, align);
- if (load_dtb(dtb_start, info, 0) < 0) {
+ if (load_dtb(dtb_start, info, 0, as) < 0) {
exit(1);
}
fixupcontext[FIXUP_ARGPTR] = dtb_start;
@@ -1096,7 +1129,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
fixupcontext[FIXUP_ENTRYPOINT] = entry;
write_bootloader("bootloader", info->loader_start,
- primary_loader, fixupcontext);
+ primary_loader, fixupcontext, as);
if (info->nb_cpus > 1) {
info->write_secondary_boot(cpu, info);
diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c
new file mode 100644
index 0000000000..c5f0a5b98a
--- /dev/null
+++ b/hw/arm/iotkit.c
@@ -0,0 +1,598 @@
+/*
+ * Arm IoT Kit
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/arm/iotkit.h"
+#include "hw/misc/unimp.h"
+#include "hw/arm/arm.h"
+
+/* Create an alias region of @size bytes starting at @base
+ * which mirrors the memory starting at @orig.
+ */
+static void make_alias(IoTKit *s, MemoryRegion *mr, const char *name,
+ hwaddr base, hwaddr size, hwaddr orig)
+{
+ memory_region_init_alias(mr, NULL, name, &s->container, orig, size);
+ /* The alias is even lower priority than unimplemented_device regions */
+ memory_region_add_subregion_overlap(&s->container, base, mr, -1500);
+}
+
+static void init_sysbus_child(Object *parent, const char *childname,
+ void *child, size_t childsize,
+ const char *childtype)
+{
+ object_initialize(child, childsize, childtype);
+ object_property_add_child(parent, childname, OBJECT(child), &error_abort);
+ qdev_set_parent_bus(DEVICE(child), sysbus_get_default());
+}
+
+static void irq_status_forwarder(void *opaque, int n, int level)
+{
+ qemu_irq destirq = opaque;
+
+ qemu_set_irq(destirq, level);
+}
+
+static void nsccfg_handler(void *opaque, int n, int level)
+{
+ IoTKit *s = IOTKIT(opaque);
+
+ s->nsccfg = level;
+}
+
+static void iotkit_forward_ppc(IoTKit *s, const char *ppcname, int ppcnum)
+{
+ /* Each of the 4 AHB and 4 APB PPCs that might be present in a
+ * system using the IoTKit has a collection of control lines which
+ * are provided by the security controller and which we want to
+ * expose as control lines on the IoTKit device itself, so the
+ * code using the IoTKit can wire them up to the PPCs.
+ */
+ SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum];
+ DeviceState *iotkitdev = DEVICE(s);
+ DeviceState *dev_secctl = DEVICE(&s->secctl);
+ DeviceState *dev_splitter = DEVICE(splitter);
+ char *name;
+
+ name = g_strdup_printf("%s_nonsec", ppcname);
+ qdev_pass_gpios(dev_secctl, iotkitdev, name);
+ g_free(name);
+ name = g_strdup_printf("%s_ap", ppcname);
+ qdev_pass_gpios(dev_secctl, iotkitdev, name);
+ g_free(name);
+ name = g_strdup_printf("%s_irq_enable", ppcname);
+ qdev_pass_gpios(dev_secctl, iotkitdev, name);
+ g_free(name);
+ name = g_strdup_printf("%s_irq_clear", ppcname);
+ qdev_pass_gpios(dev_secctl, iotkitdev, name);
+ g_free(name);
+
+ /* irq_status is a little more tricky, because we need to
+ * split it so we can send it both to the security controller
+ * and to our OR gate for the NVIC interrupt line.
+ * Connect up the splitter's outputs, and create a GPIO input
+ * which will pass the line state to the input splitter.
+ */
+ name = g_strdup_printf("%s_irq_status", ppcname);
+ qdev_connect_gpio_out(dev_splitter, 0,
+ qdev_get_gpio_in_named(dev_secctl,
+ name, 0));
+ qdev_connect_gpio_out(dev_splitter, 1,
+ qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum));
+ s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0);
+ qdev_init_gpio_in_named_with_opaque(iotkitdev, irq_status_forwarder,
+ s->irq_status_in[ppcnum], name, 1);
+ g_free(name);
+}
+
+static void iotkit_forward_sec_resp_cfg(IoTKit *s)
+{
+ /* Forward the 3rd output from the splitter device as a
+ * named GPIO output of the iotkit object.
+ */
+ DeviceState *dev = DEVICE(s);
+ DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter);
+
+ qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1);
+ s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder,
+ s->sec_resp_cfg, 1);
+ qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in);
+}
+
+static void iotkit_init(Object *obj)
+{
+ IoTKit *s = IOTKIT(obj);
+ int i;
+
+ memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX);
+
+ init_sysbus_child(obj, "armv7m", &s->armv7m, sizeof(s->armv7m),
+ TYPE_ARMV7M);
+ qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type",
+ ARM_CPU_TYPE_NAME("cortex-m33"));
+
+ init_sysbus_child(obj, "secctl", &s->secctl, sizeof(s->secctl),
+ TYPE_IOTKIT_SECCTL);
+ init_sysbus_child(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0),
+ TYPE_TZ_PPC);
+ init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1),
+ TYPE_TZ_PPC);
+ init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0),
+ TYPE_CMSDK_APB_TIMER);
+ init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1),
+ TYPE_CMSDK_APB_TIMER);
+ init_sysbus_child(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer),
+ TYPE_UNIMPLEMENTED_DEVICE);
+ object_initialize(&s->ppc_irq_orgate, sizeof(s->ppc_irq_orgate),
+ TYPE_OR_IRQ);
+ object_property_add_child(obj, "ppc-irq-orgate",
+ OBJECT(&s->ppc_irq_orgate), &error_abort);
+ object_initialize(&s->sec_resp_splitter, sizeof(s->sec_resp_splitter),
+ TYPE_SPLIT_IRQ);
+ object_property_add_child(obj, "sec-resp-splitter",
+ OBJECT(&s->sec_resp_splitter), &error_abort);
+ for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) {
+ char *name = g_strdup_printf("ppc-irq-splitter-%d", i);
+ SplitIRQ *splitter = &s->ppc_irq_splitter[i];
+
+ object_initialize(splitter, sizeof(*splitter), TYPE_SPLIT_IRQ);
+ object_property_add_child(obj, name, OBJECT(splitter), &error_abort);
+ }
+ init_sysbus_child(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer),
+ TYPE_UNIMPLEMENTED_DEVICE);
+}
+
+static void iotkit_exp_irq(void *opaque, int n, int level)
+{
+ IoTKit *s = IOTKIT(opaque);
+
+ qemu_set_irq(s->exp_irqs[n], level);
+}
+
+static void iotkit_realize(DeviceState *dev, Error **errp)
+{
+ IoTKit *s = IOTKIT(dev);
+ int i;
+ MemoryRegion *mr;
+ Error *err = NULL;
+ SysBusDevice *sbd_apb_ppc0;
+ SysBusDevice *sbd_secctl;
+ DeviceState *dev_apb_ppc0;
+ DeviceState *dev_apb_ppc1;
+ DeviceState *dev_secctl;
+ DeviceState *dev_splitter;
+
+ if (!s->board_memory) {
+ error_setg(errp, "memory property was not set");
+ return;
+ }
+
+ if (!s->mainclk_frq) {
+ error_setg(errp, "MAINCLK property was not set");
+ return;
+ }
+
+ /* Handling of which devices should be available only to secure
+ * code is usually done differently for M profile than for A profile.
+ * Instead of putting some devices only into the secure address space,
+ * devices exist in both address spaces but with hard-wired security
+ * permissions that will cause the CPU to fault for non-secure accesses.
+ *
+ * The IoTKit has an IDAU (Implementation Defined Access Unit),
+ * which specifies hard-wired security permissions for different
+ * areas of the physical address space. For the IoTKit IDAU, the
+ * top 4 bits of the physical address are the IDAU region ID, and
+ * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS
+ * region, otherwise it is an S region.
+ *
+ * The various devices and RAMs are generally all mapped twice,
+ * once into a region that the IDAU defines as secure and once
+ * into a non-secure region. They sit behind either a Memory
+ * Protection Controller (for RAM) or a Peripheral Protection
+ * Controller (for devices), which allow a more fine grained
+ * configuration of whether non-secure accesses are permitted.
+ *
+ * (The other place that guest software can configure security
+ * permissions is in the architected SAU (Security Attribution
+ * Unit), which is entirely inside the CPU. The IDAU can upgrade
+ * the security attributes for a region to more restrictive than
+ * the SAU specifies, but cannot downgrade them.)
+ *
+ * 0x10000000..0x1fffffff alias of 0x00000000..0x0fffffff
+ * 0x20000000..0x2007ffff 32KB FPGA block RAM
+ * 0x30000000..0x3fffffff alias of 0x20000000..0x2fffffff
+ * 0x40000000..0x4000ffff base peripheral region 1
+ * 0x40010000..0x4001ffff CPU peripherals (none for IoTKit)
+ * 0x40020000..0x4002ffff system control element peripherals
+ * 0x40080000..0x400fffff base peripheral region 2
+ * 0x50000000..0x5fffffff alias of 0x40000000..0x4fffffff
+ */
+
+ memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1);
+
+ qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", s->exp_numirq + 32);
+ /* In real hardware the initial Secure VTOR is set from the INITSVTOR0
+ * register in the IoT Kit System Control Register block, and the
+ * initial value of that is in turn specifiable by the FPGA that
+ * instantiates the IoT Kit. In QEMU we don't implement this wrinkle,
+ * and simply set the CPU's init-svtor to the IoT Kit default value.
+ */
+ qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor", 0x10000000);
+ object_property_set_link(OBJECT(&s->armv7m), OBJECT(&s->container),
+ "memory", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_link(OBJECT(&s->armv7m), OBJECT(s), "idau", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ /* Connect our EXP_IRQ GPIOs to the NVIC's lines 32 and up. */
+ s->exp_irqs = g_new(qemu_irq, s->exp_numirq);
+ for (i = 0; i < s->exp_numirq; i++) {
+ s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32);
+ }
+ qdev_init_gpio_in_named(dev, iotkit_exp_irq, "EXP_IRQ", s->exp_numirq);
+
+ /* Set up the big aliases first */
+ make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000);
+ make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000);
+ /* The 0x50000000..0x5fffffff region is not a pure alias: it has
+ * a few extra devices that only appear there (generally the
+ * control interfaces for the protection controllers).
+ * We implement this by mapping those devices over the top of this
+ * alias MR at a higher priority.
+ */
+ make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000);
+
+ /* This RAM should be behind a Memory Protection Controller, but we
+ * don't implement that yet.
+ */
+ memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(&s->container, 0x20000000, &s->sram0);
+
+ /* Security controller */
+ object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sbd_secctl = SYS_BUS_DEVICE(&s->secctl);
+ dev_secctl = DEVICE(&s->secctl);
+ sysbus_mmio_map(sbd_secctl, 0, 0x50080000);
+ sysbus_mmio_map(sbd_secctl, 1, 0x40080000);
+
+ s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1);
+ qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in);
+
+ /* The sec_resp_cfg output from the security controller must be split into
+ * multiple lines, one for each of the PPCs within the IoTKit and one
+ * that will be an output from the IoTKit to the system.
+ */
+ object_property_set_int(OBJECT(&s->sec_resp_splitter), 3,
+ "num-lines", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_bool(OBJECT(&s->sec_resp_splitter), true,
+ "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ dev_splitter = DEVICE(&s->sec_resp_splitter);
+ qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0,
+ qdev_get_gpio_in(dev_splitter, 0));
+
+ /* Devices behind APB PPC0:
+ * 0x40000000: timer0
+ * 0x40001000: timer1
+ * 0x40002000: dual timer
+ * We must configure and realize each downstream device and connect
+ * it to the appropriate PPC port; then we can realize the PPC and
+ * map its upstream ends to the right place in the container.
+ */
+ qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq);
+ object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0,
+ qdev_get_gpio_in(DEVICE(&s->armv7m), 3));
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0);
+ object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq);
+ object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0,
+ qdev_get_gpio_in(DEVICE(&s->armv7m), 3));
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0);
+ object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ qdev_prop_set_string(DEVICE(&s->dualtimer), "name", "Dual timer");
+ qdev_prop_set_uint64(DEVICE(&s->dualtimer), "size", 0x1000);
+ object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0);
+ object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0);
+ dev_apb_ppc0 = DEVICE(&s->apb_ppc0);
+
+ mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0);
+ memory_region_add_subregion(&s->container, 0x40000000, mr);
+ mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1);
+ memory_region_add_subregion(&s->container, 0x40001000, mr);
+ mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2);
+ memory_region_add_subregion(&s->container, 0x40002000, mr);
+ for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) {
+ qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i,
+ qdev_get_gpio_in_named(dev_apb_ppc0,
+ "cfg_nonsec", i));
+ qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i,
+ qdev_get_gpio_in_named(dev_apb_ppc0,
+ "cfg_ap", i));
+ }
+ qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0,
+ qdev_get_gpio_in_named(dev_apb_ppc0,
+ "irq_enable", 0));
+ qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0,
+ qdev_get_gpio_in_named(dev_apb_ppc0,
+ "irq_clear", 0));
+ qdev_connect_gpio_out(dev_splitter, 0,
+ qdev_get_gpio_in_named(dev_apb_ppc0,
+ "cfg_sec_resp", 0));
+
+ /* All the PPC irq lines (from the 2 internal PPCs and the 8 external
+ * ones) are sent individually to the security controller, and also
+ * ORed together to give a single combined PPC interrupt to the NVIC.
+ */
+ object_property_set_int(OBJECT(&s->ppc_irq_orgate),
+ NUM_PPCS, "num-lines", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true,
+ "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0,
+ qdev_get_gpio_in(DEVICE(&s->armv7m), 10));
+
+ /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */
+
+ /* 0x40020000 .. 0x4002ffff : IoTKit system control peripheral region */
+ /* Devices behind APB PPC1:
+ * 0x4002f000: S32K timer
+ */
+ qdev_prop_set_string(DEVICE(&s->s32ktimer), "name", "S32KTIMER");
+ qdev_prop_set_uint64(DEVICE(&s->s32ktimer), "size", 0x1000);
+ object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0);
+ object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0);
+ memory_region_add_subregion(&s->container, 0x4002f000, mr);
+
+ dev_apb_ppc1 = DEVICE(&s->apb_ppc1);
+ qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0,
+ qdev_get_gpio_in_named(dev_apb_ppc1,
+ "cfg_nonsec", 0));
+ qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0,
+ qdev_get_gpio_in_named(dev_apb_ppc1,
+ "cfg_ap", 0));
+ qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0,
+ qdev_get_gpio_in_named(dev_apb_ppc1,
+ "irq_enable", 0));
+ qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0,
+ qdev_get_gpio_in_named(dev_apb_ppc1,
+ "irq_clear", 0));
+ qdev_connect_gpio_out(dev_splitter, 1,
+ qdev_get_gpio_in_named(dev_apb_ppc1,
+ "cfg_sec_resp", 0));
+
+ /* Using create_unimplemented_device() maps the stub into the
+ * system address space rather than into our container, but the
+ * overall effect to the guest is the same.
+ */
+ create_unimplemented_device("SYSINFO", 0x40020000, 0x1000);
+
+ create_unimplemented_device("SYSCONTROL", 0x50021000, 0x1000);
+ create_unimplemented_device("S32KWATCHDOG", 0x5002e000, 0x1000);
+
+ /* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */
+
+ create_unimplemented_device("NS watchdog", 0x40081000, 0x1000);
+ create_unimplemented_device("S watchdog", 0x50081000, 0x1000);
+
+ create_unimplemented_device("SRAM0 MPC", 0x50083000, 0x1000);
+
+ for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) {
+ Object *splitter = OBJECT(&s->ppc_irq_splitter[i]);
+
+ object_property_set_int(splitter, 2, "num-lines", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_bool(splitter, true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
+
+ for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) {
+ char *ppcname = g_strdup_printf("ahb_ppcexp%d", i);
+
+ iotkit_forward_ppc(s, ppcname, i);
+ g_free(ppcname);
+ }
+
+ for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) {
+ char *ppcname = g_strdup_printf("apb_ppcexp%d", i);
+
+ iotkit_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC);
+ g_free(ppcname);
+ }
+
+ for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) {
+ /* Wire up IRQ splitter for internal PPCs */
+ DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]);
+ char *gpioname = g_strdup_printf("apb_ppc%d_irq_status",
+ i - NUM_EXTERNAL_PPCS);
+ TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1;
+
+ qdev_connect_gpio_out(devs, 0,
+ qdev_get_gpio_in_named(dev_secctl, gpioname, 0));
+ qdev_connect_gpio_out(devs, 1,
+ qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i));
+ qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0,
+ qdev_get_gpio_in(devs, 0));
+ }
+
+ iotkit_forward_sec_resp_cfg(s);
+
+ system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq;
+}
+
+static void iotkit_idau_check(IDAUInterface *ii, uint32_t address,
+ int *iregion, bool *exempt, bool *ns, bool *nsc)
+{
+ /* For IoTKit systems the IDAU responses are simple logical functions
+ * of the address bits. The NSC attribute is guest-adjustable via the
+ * NSCCFG register in the security controller.
+ */
+ IoTKit *s = IOTKIT(ii);
+ int region = extract32(address, 28, 4);
+
+ *ns = !(region & 1);
+ *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2));
+ /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */
+ *exempt = (address & 0xeff00000) == 0xe0000000;
+ *iregion = region;
+}
+
+static const VMStateDescription iotkit_vmstate = {
+ .name = "iotkit",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(nsccfg, IoTKit),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property iotkit_properties[] = {
+ DEFINE_PROP_LINK("memory", IoTKit, board_memory, TYPE_MEMORY_REGION,
+ MemoryRegion *),
+ DEFINE_PROP_UINT32("EXP_NUMIRQ", IoTKit, exp_numirq, 64),
+ DEFINE_PROP_UINT32("MAINCLK", IoTKit, mainclk_frq, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void iotkit_reset(DeviceState *dev)
+{
+ IoTKit *s = IOTKIT(dev);
+
+ s->nsccfg = 0;
+}
+
+static void iotkit_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass);
+
+ dc->realize = iotkit_realize;
+ dc->vmsd = &iotkit_vmstate;
+ dc->props = iotkit_properties;
+ dc->reset = iotkit_reset;
+ iic->check = iotkit_idau_check;
+}
+
+static const TypeInfo iotkit_info = {
+ .name = TYPE_IOTKIT,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IoTKit),
+ .instance_init = iotkit_init,
+ .class_init = iotkit_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_IDAU_INTERFACE },
+ { }
+ }
+};
+
+static void iotkit_register_types(void)
+{
+ type_register_static(&iotkit_info);
+}
+
+type_init(iotkit_register_types);
diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c
new file mode 100644
index 0000000000..8c86cffa9e
--- /dev/null
+++ b/hw/arm/mps2-tz.c
@@ -0,0 +1,503 @@
+/*
+ * ARM V2M MPS2 board emulation, trustzone aware FPGA images
+ *
+ * Copyright (c) 2017 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/* The MPS2 and MPS2+ dev boards are FPGA based (the 2+ has a bigger
+ * FPGA but is otherwise the same as the 2). Since the CPU itself
+ * and most of the devices are in the FPGA, the details of the board
+ * as seen by the guest depend significantly on the FPGA image.
+ * This source file covers the following FPGA images, for TrustZone cores:
+ * "mps2-an505" -- Cortex-M33 as documented in ARM Application Note AN505
+ *
+ * Links to the TRM for the board itself and to the various Application
+ * Notes which document the FPGA images can be found here:
+ * https://developer.arm.com/products/system-design/development-boards/fpga-prototyping-boards/mps2
+ *
+ * Board TRM:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.100112_0200_06_en/versatile_express_cortex_m_prototyping_systems_v2m_mps2_and_v2m_mps2plus_technical_reference_100112_0200_06_en.pdf
+ * Application Note AN505:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html
+ *
+ * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide
+ * (ARM ECM0601256) for the details of some of the device layout:
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "hw/arm/arm.h"
+#include "hw/arm/armv7m.h"
+#include "hw/or-irq.h"
+#include "hw/boards.h"
+#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
+#include "hw/misc/unimp.h"
+#include "hw/char/cmsdk-apb-uart.h"
+#include "hw/timer/cmsdk-apb-timer.h"
+#include "hw/misc/mps2-scc.h"
+#include "hw/misc/mps2-fpgaio.h"
+#include "hw/arm/iotkit.h"
+#include "hw/devices.h"
+#include "net/net.h"
+#include "hw/core/split-irq.h"
+
+typedef enum MPS2TZFPGAType {
+ FPGA_AN505,
+} MPS2TZFPGAType;
+
+typedef struct {
+ MachineClass parent;
+ MPS2TZFPGAType fpga_type;
+ uint32_t scc_id;
+} MPS2TZMachineClass;
+
+typedef struct {
+ MachineState parent;
+
+ IoTKit iotkit;
+ MemoryRegion psram;
+ MemoryRegion ssram1;
+ MemoryRegion ssram1_m;
+ MemoryRegion ssram23;
+ MPS2SCC scc;
+ MPS2FPGAIO fpgaio;
+ TZPPC ppc[5];
+ UnimplementedDeviceState ssram_mpc[3];
+ UnimplementedDeviceState spi[5];
+ UnimplementedDeviceState i2c[4];
+ UnimplementedDeviceState i2s_audio;
+ UnimplementedDeviceState gpio[5];
+ UnimplementedDeviceState dma[4];
+ UnimplementedDeviceState gfx;
+ CMSDKAPBUART uart[5];
+ SplitIRQ sec_resp_splitter;
+ qemu_or_irq uart_irq_orgate;
+} MPS2TZMachineState;
+
+#define TYPE_MPS2TZ_MACHINE "mps2tz"
+#define TYPE_MPS2TZ_AN505_MACHINE MACHINE_TYPE_NAME("mps2-an505")
+
+#define MPS2TZ_MACHINE(obj) \
+ OBJECT_CHECK(MPS2TZMachineState, obj, TYPE_MPS2TZ_MACHINE)
+#define MPS2TZ_MACHINE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(MPS2TZMachineClass, obj, TYPE_MPS2TZ_MACHINE)
+#define MPS2TZ_MACHINE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(MPS2TZMachineClass, klass, TYPE_MPS2TZ_MACHINE)
+
+/* Main SYSCLK frequency in Hz */
+#define SYSCLK_FRQ 20000000
+
+/* Initialize the auxiliary RAM region @mr and map it into
+ * the memory map at @base.
+ */
+static void make_ram(MemoryRegion *mr, const char *name,
+ hwaddr base, hwaddr size)
+{
+ memory_region_init_ram(mr, NULL, name, size, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), base, mr);
+}
+
+/* Create an alias of an entire original MemoryRegion @orig
+ * located at @base in the memory map.
+ */
+static void make_ram_alias(MemoryRegion *mr, const char *name,
+ MemoryRegion *orig, hwaddr base)
+{
+ memory_region_init_alias(mr, NULL, name, orig, 0,
+ memory_region_size(orig));
+ memory_region_add_subregion(get_system_memory(), base, mr);
+}
+
+static void init_sysbus_child(Object *parent, const char *childname,
+ void *child, size_t childsize,
+ const char *childtype)
+{
+ object_initialize(child, childsize, childtype);
+ object_property_add_child(parent, childname, OBJECT(child), &error_abort);
+ qdev_set_parent_bus(DEVICE(child), sysbus_get_default());
+
+}
+
+/* Most of the devices in the AN505 FPGA image sit behind
+ * Peripheral Protection Controllers. These data structures
+ * define the layout of which devices sit behind which PPCs.
+ * The devfn for each port is a function which creates, configures
+ * and initializes the device, returning the MemoryRegion which
+ * needs to be plugged into the downstream end of the PPC port.
+ */
+typedef MemoryRegion *MakeDevFn(MPS2TZMachineState *mms, void *opaque,
+ const char *name, hwaddr size);
+
+typedef struct PPCPortInfo {
+ const char *name;
+ MakeDevFn *devfn;
+ void *opaque;
+ hwaddr addr;
+ hwaddr size;
+} PPCPortInfo;
+
+typedef struct PPCInfo {
+ const char *name;
+ PPCPortInfo ports[TZ_NUM_PORTS];
+} PPCInfo;
+
+static MemoryRegion *make_unimp_dev(MPS2TZMachineState *mms,
+ void *opaque,
+ const char *name, hwaddr size)
+{
+ /* Initialize, configure and realize a TYPE_UNIMPLEMENTED_DEVICE,
+ * and return a pointer to its MemoryRegion.
+ */
+ UnimplementedDeviceState *uds = opaque;
+
+ init_sysbus_child(OBJECT(mms), name, uds,
+ sizeof(UnimplementedDeviceState),
+ TYPE_UNIMPLEMENTED_DEVICE);
+ qdev_prop_set_string(DEVICE(uds), "name", name);
+ qdev_prop_set_uint64(DEVICE(uds), "size", size);
+ object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal);
+ return sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0);
+}
+
+static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque,
+ const char *name, hwaddr size)
+{
+ CMSDKAPBUART *uart = opaque;
+ int i = uart - &mms->uart[0];
+ Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL;
+ int rxirqno = i * 2;
+ int txirqno = i * 2 + 1;
+ int combirqno = i + 10;
+ SysBusDevice *s;
+ DeviceState *iotkitdev = DEVICE(&mms->iotkit);
+ DeviceState *orgate_dev = DEVICE(&mms->uart_irq_orgate);
+
+ init_sysbus_child(OBJECT(mms), name, uart,
+ sizeof(mms->uart[0]), TYPE_CMSDK_APB_UART);
+ qdev_prop_set_chr(DEVICE(uart), "chardev", uartchr);
+ qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", SYSCLK_FRQ);
+ object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal);
+ s = SYS_BUS_DEVICE(uart);
+ sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev,
+ "EXP_IRQ", txirqno));
+ sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev,
+ "EXP_IRQ", rxirqno));
+ sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2));
+ sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1));
+ sysbus_connect_irq(s, 4, qdev_get_gpio_in_named(iotkitdev,
+ "EXP_IRQ", combirqno));
+ return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0);
+}
+
+static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque,
+ const char *name, hwaddr size)
+{
+ MPS2SCC *scc = opaque;
+ DeviceState *sccdev;
+ MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms);
+
+ object_initialize(scc, sizeof(mms->scc), TYPE_MPS2_SCC);
+ sccdev = DEVICE(scc);
+ qdev_set_parent_bus(sccdev, sysbus_get_default());
+ qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2);
+ qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008);
+ qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id);
+ object_property_set_bool(OBJECT(scc), true, "realized", &error_fatal);
+ return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0);
+}
+
+static MemoryRegion *make_fpgaio(MPS2TZMachineState *mms, void *opaque,
+ const char *name, hwaddr size)
+{
+ MPS2FPGAIO *fpgaio = opaque;
+
+ object_initialize(fpgaio, sizeof(mms->fpgaio), TYPE_MPS2_FPGAIO);
+ qdev_set_parent_bus(DEVICE(fpgaio), sysbus_get_default());
+ object_property_set_bool(OBJECT(fpgaio), true, "realized", &error_fatal);
+ return sysbus_mmio_get_region(SYS_BUS_DEVICE(fpgaio), 0);
+}
+
+static void mps2tz_common_init(MachineState *machine)
+{
+ MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine);
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ MemoryRegion *system_memory = get_system_memory();
+ DeviceState *iotkitdev;
+ DeviceState *dev_splitter;
+ int i;
+
+ if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
+ error_report("This board can only be used with CPU %s",
+ mc->default_cpu_type);
+ exit(1);
+ }
+
+ init_sysbus_child(OBJECT(machine), "iotkit", &mms->iotkit,
+ sizeof(mms->iotkit), TYPE_IOTKIT);
+ iotkitdev = DEVICE(&mms->iotkit);
+ object_property_set_link(OBJECT(&mms->iotkit), OBJECT(system_memory),
+ "memory", &error_abort);
+ qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", 92);
+ qdev_prop_set_uint32(iotkitdev, "MAINCLK", SYSCLK_FRQ);
+ object_property_set_bool(OBJECT(&mms->iotkit), true, "realized",
+ &error_fatal);
+
+ /* The sec_resp_cfg output from the IoTKit must be split into multiple
+ * lines, one for each of the PPCs we create here.
+ */
+ object_initialize(&mms->sec_resp_splitter, sizeof(mms->sec_resp_splitter),
+ TYPE_SPLIT_IRQ);
+ object_property_add_child(OBJECT(machine), "sec-resp-splitter",
+ OBJECT(&mms->sec_resp_splitter), &error_abort);
+ object_property_set_int(OBJECT(&mms->sec_resp_splitter), 5,
+ "num-lines", &error_fatal);
+ object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true,
+ "realized", &error_fatal);
+ dev_splitter = DEVICE(&mms->sec_resp_splitter);
+ qdev_connect_gpio_out_named(iotkitdev, "sec_resp_cfg", 0,
+ qdev_get_gpio_in(dev_splitter, 0));
+
+ /* The IoTKit sets up much of the memory layout, including
+ * the aliases between secure and non-secure regions in the
+ * address space. The FPGA itself contains:
+ *
+ * 0x00000000..0x003fffff SSRAM1
+ * 0x00400000..0x007fffff alias of SSRAM1
+ * 0x28000000..0x283fffff 4MB SSRAM2 + SSRAM3
+ * 0x40100000..0x4fffffff AHB Master Expansion 1 interface devices
+ * 0x80000000..0x80ffffff 16MB PSRAM
+ */
+
+ /* The FPGA images have an odd combination of different RAMs,
+ * because in hardware they are different implementations and
+ * connected to different buses, giving varying performance/size
+ * tradeoffs. For QEMU they're all just RAM, though. We arbitrarily
+ * call the 16MB our "system memory", as it's the largest lump.
+ */
+ memory_region_allocate_system_memory(&mms->psram,
+ NULL, "mps.ram", 0x01000000);
+ memory_region_add_subregion(system_memory, 0x80000000, &mms->psram);
+
+ /* The SSRAM memories should all be behind Memory Protection Controllers,
+ * but we don't implement that yet.
+ */
+ make_ram(&mms->ssram1, "mps.ssram1", 0x00000000, 0x00400000);
+ make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x00400000);
+
+ make_ram(&mms->ssram23, "mps.ssram23", 0x28000000, 0x00400000);
+
+ /* The overflow IRQs for all UARTs are ORed together.
+ * Tx, Rx and "combined" IRQs are sent to the NVIC separately.
+ * Create the OR gate for this.
+ */
+ object_initialize(&mms->uart_irq_orgate, sizeof(mms->uart_irq_orgate),
+ TYPE_OR_IRQ);
+ object_property_add_child(OBJECT(mms), "uart-irq-orgate",
+ OBJECT(&mms->uart_irq_orgate), &error_abort);
+ object_property_set_int(OBJECT(&mms->uart_irq_orgate), 10, "num-lines",
+ &error_fatal);
+ object_property_set_bool(OBJECT(&mms->uart_irq_orgate), true,
+ "realized", &error_fatal);
+ qdev_connect_gpio_out(DEVICE(&mms->uart_irq_orgate), 0,
+ qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 15));
+
+ /* Most of the devices in the FPGA are behind Peripheral Protection
+ * Controllers. The required order for initializing things is:
+ * + initialize the PPC
+ * + initialize, configure and realize downstream devices
+ * + connect downstream device MemoryRegions to the PPC
+ * + realize the PPC
+ * + map the PPC's MemoryRegions to the places in the address map
+ * where the downstream devices should appear
+ * + wire up the PPC's control lines to the IoTKit object
+ */
+
+ const PPCInfo ppcs[] = { {
+ .name = "apb_ppcexp0",
+ .ports = {
+ { "ssram-mpc0", make_unimp_dev, &mms->ssram_mpc[0],
+ 0x58007000, 0x1000 },
+ { "ssram-mpc1", make_unimp_dev, &mms->ssram_mpc[1],
+ 0x58008000, 0x1000 },
+ { "ssram-mpc2", make_unimp_dev, &mms->ssram_mpc[2],
+ 0x58009000, 0x1000 },
+ },
+ }, {
+ .name = "apb_ppcexp1",
+ .ports = {
+ { "spi0", make_unimp_dev, &mms->spi[0], 0x40205000, 0x1000 },
+ { "spi1", make_unimp_dev, &mms->spi[1], 0x40206000, 0x1000 },
+ { "spi2", make_unimp_dev, &mms->spi[2], 0x40209000, 0x1000 },
+ { "spi3", make_unimp_dev, &mms->spi[3], 0x4020a000, 0x1000 },
+ { "spi4", make_unimp_dev, &mms->spi[4], 0x4020b000, 0x1000 },
+ { "uart0", make_uart, &mms->uart[0], 0x40200000, 0x1000 },
+ { "uart1", make_uart, &mms->uart[1], 0x40201000, 0x1000 },
+ { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 },
+ { "uart3", make_uart, &mms->uart[3], 0x40203000, 0x1000 },
+ { "uart4", make_uart, &mms->uart[4], 0x40204000, 0x1000 },
+ { "i2c0", make_unimp_dev, &mms->i2c[0], 0x40207000, 0x1000 },
+ { "i2c1", make_unimp_dev, &mms->i2c[1], 0x40208000, 0x1000 },
+ { "i2c2", make_unimp_dev, &mms->i2c[2], 0x4020c000, 0x1000 },
+ { "i2c3", make_unimp_dev, &mms->i2c[3], 0x4020d000, 0x1000 },
+ },
+ }, {
+ .name = "apb_ppcexp2",
+ .ports = {
+ { "scc", make_scc, &mms->scc, 0x40300000, 0x1000 },
+ { "i2s-audio", make_unimp_dev, &mms->i2s_audio,
+ 0x40301000, 0x1000 },
+ { "fpgaio", make_fpgaio, &mms->fpgaio, 0x40302000, 0x1000 },
+ },
+ }, {
+ .name = "ahb_ppcexp0",
+ .ports = {
+ { "gfx", make_unimp_dev, &mms->gfx, 0x41000000, 0x140000 },
+ { "gpio0", make_unimp_dev, &mms->gpio[0], 0x40100000, 0x1000 },
+ { "gpio1", make_unimp_dev, &mms->gpio[1], 0x40101000, 0x1000 },
+ { "gpio2", make_unimp_dev, &mms->gpio[2], 0x40102000, 0x1000 },
+ { "gpio3", make_unimp_dev, &mms->gpio[3], 0x40103000, 0x1000 },
+ { "gpio4", make_unimp_dev, &mms->gpio[4], 0x40104000, 0x1000 },
+ },
+ }, {
+ .name = "ahb_ppcexp1",
+ .ports = {
+ { "dma0", make_unimp_dev, &mms->dma[0], 0x40110000, 0x1000 },
+ { "dma1", make_unimp_dev, &mms->dma[1], 0x40111000, 0x1000 },
+ { "dma2", make_unimp_dev, &mms->dma[2], 0x40112000, 0x1000 },
+ { "dma3", make_unimp_dev, &mms->dma[3], 0x40113000, 0x1000 },
+ },
+ },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(ppcs); i++) {
+ const PPCInfo *ppcinfo = &ppcs[i];
+ TZPPC *ppc = &mms->ppc[i];
+ DeviceState *ppcdev;
+ int port;
+ char *gpioname;
+
+ init_sysbus_child(OBJECT(machine), ppcinfo->name, ppc,
+ sizeof(TZPPC), TYPE_TZ_PPC);
+ ppcdev = DEVICE(ppc);
+
+ for (port = 0; port < TZ_NUM_PORTS; port++) {
+ const PPCPortInfo *pinfo = &ppcinfo->ports[port];
+ MemoryRegion *mr;
+ char *portname;
+
+ if (!pinfo->devfn) {
+ continue;
+ }
+
+ mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size);
+ portname = g_strdup_printf("port[%d]", port);
+ object_property_set_link(OBJECT(ppc), OBJECT(mr),
+ portname, &error_fatal);
+ g_free(portname);
+ }
+
+ object_property_set_bool(OBJECT(ppc), true, "realized", &error_fatal);
+
+ for (port = 0; port < TZ_NUM_PORTS; port++) {
+ const PPCPortInfo *pinfo = &ppcinfo->ports[port];
+
+ if (!pinfo->devfn) {
+ continue;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(ppc), port, pinfo->addr);
+
+ gpioname = g_strdup_printf("%s_nonsec", ppcinfo->name);
+ qdev_connect_gpio_out_named(iotkitdev, gpioname, port,
+ qdev_get_gpio_in_named(ppcdev,
+ "cfg_nonsec",
+ port));
+ g_free(gpioname);
+ gpioname = g_strdup_printf("%s_ap", ppcinfo->name);
+ qdev_connect_gpio_out_named(iotkitdev, gpioname, port,
+ qdev_get_gpio_in_named(ppcdev,
+ "cfg_ap", port));
+ g_free(gpioname);
+ }
+
+ gpioname = g_strdup_printf("%s_irq_enable", ppcinfo->name);
+ qdev_connect_gpio_out_named(iotkitdev, gpioname, 0,
+ qdev_get_gpio_in_named(ppcdev,
+ "irq_enable", 0));
+ g_free(gpioname);
+ gpioname = g_strdup_printf("%s_irq_clear", ppcinfo->name);
+ qdev_connect_gpio_out_named(iotkitdev, gpioname, 0,
+ qdev_get_gpio_in_named(ppcdev,
+ "irq_clear", 0));
+ g_free(gpioname);
+ gpioname = g_strdup_printf("%s_irq_status", ppcinfo->name);
+ qdev_connect_gpio_out_named(ppcdev, "irq", 0,
+ qdev_get_gpio_in_named(iotkitdev,
+ gpioname, 0));
+ g_free(gpioname);
+
+ qdev_connect_gpio_out(dev_splitter, i,
+ qdev_get_gpio_in_named(ppcdev,
+ "cfg_sec_resp", 0));
+ }
+
+ /* In hardware this is a LAN9220; the LAN9118 is software compatible
+ * except that it doesn't support the checksum-offload feature.
+ * The ethernet controller is not behind a PPC.
+ */
+ lan9118_init(&nd_table[0], 0x42000000,
+ qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16));
+
+ create_unimplemented_device("FPGA NS PC", 0x48007000, 0x1000);
+
+ armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000);
+}
+
+static void mps2tz_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = mps2tz_common_init;
+ mc->max_cpus = 1;
+}
+
+static void mps2tz_an505_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc);
+
+ mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33";
+ mmc->fpga_type = FPGA_AN505;
+ mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
+ mmc->scc_id = 0x41040000 | (505 << 4);
+}
+
+static const TypeInfo mps2tz_info = {
+ .name = TYPE_MPS2TZ_MACHINE,
+ .parent = TYPE_MACHINE,
+ .abstract = true,
+ .instance_size = sizeof(MPS2TZMachineState),
+ .class_size = sizeof(MPS2TZMachineClass),
+ .class_init = mps2tz_class_init,
+};
+
+static const TypeInfo mps2tz_an505_info = {
+ .name = TYPE_MPS2TZ_AN505_MACHINE,
+ .parent = TYPE_MPS2TZ_MACHINE,
+ .class_init = mps2tz_an505_class_init,
+};
+
+static void mps2tz_machine_init(void)
+{
+ type_register_static(&mps2tz_info);
+ type_register_static(&mps2tz_an505_info);
+}
+
+type_init(mps2tz_machine_init);
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index 4b93a3abd2..69227fd4c9 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -53,6 +53,9 @@
#define IPI_ADDR 0xFF300000
#define IPI_IRQ 64
+#define RTC_ADDR 0xffa60000
+#define RTC_IRQ 26
+
#define SDHCI_CAPABILITIES 0x280737ec6481 /* Datasheet: UG1085 (v1.7) */
static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
@@ -191,6 +194,9 @@ static void xlnx_zynqmp_init(Object *obj)
object_initialize(&s->ipi, sizeof(s->ipi), TYPE_XLNX_ZYNQMP_IPI);
qdev_set_parent_bus(DEVICE(&s->ipi), sysbus_get_default());
+
+ object_initialize(&s->rtc, sizeof(s->rtc), TYPE_XLNX_ZYNQMP_RTC);
+ qdev_set_parent_bus(DEVICE(&s->rtc), sysbus_get_default());
}
static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
@@ -476,6 +482,14 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ipi), 0, IPI_ADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->ipi), 0, gic_spi[IPI_IRQ]);
+
+ object_property_set_bool(OBJECT(&s->rtc), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, RTC_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]);
}
static Property xlnx_zynqmp_props[] = {
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index 1240728c87..eb88ca979e 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -18,6 +18,7 @@ common-obj-$(CONFIG_FITLOADER) += loader-fit.o
common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
common-obj-$(CONFIG_SOFTMMU) += register.o
common-obj-$(CONFIG_SOFTMMU) += or-irq.o
+common-obj-$(CONFIG_SOFTMMU) += split-irq.o
common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o
obj-$(CONFIG_SOFTMMU) += generic-loader.o
diff --git a/hw/core/loader.c b/hw/core/loader.c
index c08f130461..76b244c508 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -730,8 +730,14 @@ int load_uimage_as(const char *filename, hwaddr *ep, hwaddr *loadaddr,
/* Load a ramdisk. */
int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz)
{
+ return load_ramdisk_as(filename, addr, max_sz, NULL);
+}
+
+int load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz,
+ AddressSpace *as)
+{
return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK,
- NULL, NULL, NULL);
+ NULL, NULL, as);
}
/* Load a gzip-compressed kernel to a dynamically allocated buffer. */
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 7ed1f431f0..f3754ee606 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -385,15 +385,17 @@ static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev,
return ngl;
}
-void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler,
- const char *name, int n)
+void qdev_init_gpio_in_named_with_opaque(DeviceState *dev,
+ qemu_irq_handler handler,
+ void *opaque,
+ const char *name, int n)
{
int i;
NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
assert(gpio_list->num_out == 0 || !name);
gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler,
- dev, n);
+ opaque, n);
if (!name) {
name = "unnamed-gpio-in";
diff --git a/hw/core/split-irq.c b/hw/core/split-irq.c
new file mode 100644
index 0000000000..7e64cd4969
--- /dev/null
+++ b/hw/core/split-irq.c
@@ -0,0 +1,89 @@
+/*
+ * IRQ splitter device.
+ *
+ * Copyright (c) 2018 Linaro Limited.
+ * Written by Peter Maydell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/split-irq.h"
+#include "qapi/error.h"
+
+static void split_irq_handler(void *opaque, int n, int level)
+{
+ SplitIRQ *s = SPLIT_IRQ(opaque);
+ int i;
+
+ for (i = 0; i < s->num_lines; i++) {
+ qemu_set_irq(s->out_irq[i], level);
+ }
+}
+
+static void split_irq_init(Object *obj)
+{
+ qdev_init_gpio_in(DEVICE(obj), split_irq_handler, 1);
+}
+
+static void split_irq_realize(DeviceState *dev, Error **errp)
+{
+ SplitIRQ *s = SPLIT_IRQ(dev);
+
+ if (s->num_lines < 1 || s->num_lines >= MAX_SPLIT_LINES) {
+ error_setg(errp,
+ "IRQ splitter number of lines %d is not between 1 and %d",
+ s->num_lines, MAX_SPLIT_LINES);
+ return;
+ }
+
+ qdev_init_gpio_out(dev, s->out_irq, s->num_lines);
+}
+
+static Property split_irq_properties[] = {
+ DEFINE_PROP_UINT16("num-lines", SplitIRQ, num_lines, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void split_irq_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ /* No state to reset or migrate */
+ dc->props = split_irq_properties;
+ dc->realize = split_irq_realize;
+
+ /* Reason: Needs to be wired up to work */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo split_irq_type_info = {
+ .name = TYPE_SPLIT_IRQ,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(SplitIRQ),
+ .instance_init = split_irq_init,
+ .class_init = split_irq_class_init,
+};
+
+static void split_irq_register_types(void)
+{
+ type_register_static(&split_irq_type_info);
+}
+
+type_init(split_irq_register_types)
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index f33b37a8e5..00e834d0f0 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -58,8 +58,12 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o
obj-$(CONFIG_MIPS_CPS) += mips_cpc.o
obj-$(CONFIG_MIPS_ITU) += mips_itu.o
+obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o
obj-$(CONFIG_MPS2_SCC) += mps2-scc.o
+obj-$(CONFIG_TZ_PPC) += tz-ppc.o
+obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o
+
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
obj-$(CONFIG_AUX) += auxbus.o
diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c
new file mode 100644
index 0000000000..ddd1584d34
--- /dev/null
+++ b/hw/misc/iotkit-secctl.c
@@ -0,0 +1,704 @@
+/*
+ * Arm IoT Kit security controller
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/iotkit-secctl.h"
+
+/* Registers in the secure privilege control block */
+REG32(SECRESPCFG, 0x10)
+REG32(NSCCFG, 0x14)
+REG32(SECMPCINTSTATUS, 0x1c)
+REG32(SECPPCINTSTAT, 0x20)
+REG32(SECPPCINTCLR, 0x24)
+REG32(SECPPCINTEN, 0x28)
+REG32(SECMSCINTSTAT, 0x30)
+REG32(SECMSCINTCLR, 0x34)
+REG32(SECMSCINTEN, 0x38)
+REG32(BRGINTSTAT, 0x40)
+REG32(BRGINTCLR, 0x44)
+REG32(BRGINTEN, 0x48)
+REG32(AHBNSPPC0, 0x50)
+REG32(AHBNSPPCEXP0, 0x60)
+REG32(AHBNSPPCEXP1, 0x64)
+REG32(AHBNSPPCEXP2, 0x68)
+REG32(AHBNSPPCEXP3, 0x6c)
+REG32(APBNSPPC0, 0x70)
+REG32(APBNSPPC1, 0x74)
+REG32(APBNSPPCEXP0, 0x80)
+REG32(APBNSPPCEXP1, 0x84)
+REG32(APBNSPPCEXP2, 0x88)
+REG32(APBNSPPCEXP3, 0x8c)
+REG32(AHBSPPPC0, 0x90)
+REG32(AHBSPPPCEXP0, 0xa0)
+REG32(AHBSPPPCEXP1, 0xa4)
+REG32(AHBSPPPCEXP2, 0xa8)
+REG32(AHBSPPPCEXP3, 0xac)
+REG32(APBSPPPC0, 0xb0)
+REG32(APBSPPPC1, 0xb4)
+REG32(APBSPPPCEXP0, 0xc0)
+REG32(APBSPPPCEXP1, 0xc4)
+REG32(APBSPPPCEXP2, 0xc8)
+REG32(APBSPPPCEXP3, 0xcc)
+REG32(NSMSCEXP, 0xd0)
+REG32(PID4, 0xfd0)
+REG32(PID5, 0xfd4)
+REG32(PID6, 0xfd8)
+REG32(PID7, 0xfdc)
+REG32(PID0, 0xfe0)
+REG32(PID1, 0xfe4)
+REG32(PID2, 0xfe8)
+REG32(PID3, 0xfec)
+REG32(CID0, 0xff0)
+REG32(CID1, 0xff4)
+REG32(CID2, 0xff8)
+REG32(CID3, 0xffc)
+
+/* Registers in the non-secure privilege control block */
+REG32(AHBNSPPPC0, 0x90)
+REG32(AHBNSPPPCEXP0, 0xa0)
+REG32(AHBNSPPPCEXP1, 0xa4)
+REG32(AHBNSPPPCEXP2, 0xa8)
+REG32(AHBNSPPPCEXP3, 0xac)
+REG32(APBNSPPPC0, 0xb0)
+REG32(APBNSPPPC1, 0xb4)
+REG32(APBNSPPPCEXP0, 0xc0)
+REG32(APBNSPPPCEXP1, 0xc4)
+REG32(APBNSPPPCEXP2, 0xc8)
+REG32(APBNSPPPCEXP3, 0xcc)
+/* PID and CID registers are also present in the NS block */
+
+static const uint8_t iotkit_secctl_s_idregs[] = {
+ 0x04, 0x00, 0x00, 0x00,
+ 0x52, 0xb8, 0x0b, 0x00,
+ 0x0d, 0xf0, 0x05, 0xb1,
+};
+
+static const uint8_t iotkit_secctl_ns_idregs[] = {
+ 0x04, 0x00, 0x00, 0x00,
+ 0x53, 0xb8, 0x0b, 0x00,
+ 0x0d, 0xf0, 0x05, 0xb1,
+};
+
+/* The register sets for the various PPCs (AHB internal, APB internal,
+ * AHB expansion, APB expansion) are all set up so that they are
+ * in 16-aligned blocks so offsets 0xN0, 0xN4, 0xN8, 0xNC are PPCs
+ * 0, 1, 2, 3 of that type, so we can convert a register address offset
+ * into an an index into a PPC array easily.
+ */
+static inline int offset_to_ppc_idx(uint32_t offset)
+{
+ return extract32(offset, 2, 2);
+}
+
+typedef void PerPPCFunction(IoTKitSecCtlPPC *ppc);
+
+static void foreach_ppc(IoTKitSecCtl *s, PerPPCFunction *fn)
+{
+ int i;
+
+ for (i = 0; i < IOTS_NUM_APB_PPC; i++) {
+ fn(&s->apb[i]);
+ }
+ for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) {
+ fn(&s->apbexp[i]);
+ }
+ for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) {
+ fn(&s->ahbexp[i]);
+ }
+}
+
+static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr,
+ uint64_t *pdata,
+ unsigned size, MemTxAttrs attrs)
+{
+ uint64_t r;
+ uint32_t offset = addr & ~0x3;
+ IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+
+ switch (offset) {
+ case A_AHBNSPPC0:
+ case A_AHBSPPPC0:
+ r = 0;
+ break;
+ case A_SECRESPCFG:
+ r = s->secrespcfg;
+ break;
+ case A_NSCCFG:
+ r = s->nsccfg;
+ break;
+ case A_SECPPCINTSTAT:
+ r = s->secppcintstat;
+ break;
+ case A_SECPPCINTEN:
+ r = s->secppcinten;
+ break;
+ case A_BRGINTSTAT:
+ /* QEMU's bus fabric can never report errors as it doesn't buffer
+ * writes, so we never report bridge interrupts.
+ */
+ r = 0;
+ break;
+ case A_BRGINTEN:
+ r = s->brginten;
+ break;
+ case A_AHBNSPPCEXP0:
+ case A_AHBNSPPCEXP1:
+ case A_AHBNSPPCEXP2:
+ case A_AHBNSPPCEXP3:
+ r = s->ahbexp[offset_to_ppc_idx(offset)].ns;
+ break;
+ case A_APBNSPPC0:
+ case A_APBNSPPC1:
+ r = s->apb[offset_to_ppc_idx(offset)].ns;
+ break;
+ case A_APBNSPPCEXP0:
+ case A_APBNSPPCEXP1:
+ case A_APBNSPPCEXP2:
+ case A_APBNSPPCEXP3:
+ r = s->apbexp[offset_to_ppc_idx(offset)].ns;
+ break;
+ case A_AHBSPPPCEXP0:
+ case A_AHBSPPPCEXP1:
+ case A_AHBSPPPCEXP2:
+ case A_AHBSPPPCEXP3:
+ r = s->apbexp[offset_to_ppc_idx(offset)].sp;
+ break;
+ case A_APBSPPPC0:
+ case A_APBSPPPC1:
+ r = s->apb[offset_to_ppc_idx(offset)].sp;
+ break;
+ case A_APBSPPPCEXP0:
+ case A_APBSPPPCEXP1:
+ case A_APBSPPPCEXP2:
+ case A_APBSPPPCEXP3:
+ r = s->apbexp[offset_to_ppc_idx(offset)].sp;
+ break;
+ case A_SECMPCINTSTATUS:
+ case A_SECMSCINTSTAT:
+ case A_SECMSCINTEN:
+ case A_NSMSCEXP:
+ qemu_log_mask(LOG_UNIMP,
+ "IoTKit SecCtl S block read: "
+ "unimplemented offset 0x%x\n", offset);
+ r = 0;
+ break;
+ case A_PID4:
+ case A_PID5:
+ case A_PID6:
+ case A_PID7:
+ case A_PID0:
+ case A_PID1:
+ case A_PID2:
+ case A_PID3:
+ case A_CID0:
+ case A_CID1:
+ case A_CID2:
+ case A_CID3:
+ r = iotkit_secctl_s_idregs[(offset - A_PID4) / 4];
+ break;
+ case A_SECPPCINTCLR:
+ case A_SECMSCINTCLR:
+ case A_BRGINTCLR:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IotKit SecCtl S block read: write-only offset 0x%x\n",
+ offset);
+ r = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IotKit SecCtl S block read: bad offset 0x%x\n", offset);
+ r = 0;
+ break;
+ }
+
+ if (size != 4) {
+ /* None of our registers are access-sensitive, so just pull the right
+ * byte out of the word read result.
+ */
+ r = extract32(r, (addr & 3) * 8, size * 8);
+ }
+
+ trace_iotkit_secctl_s_read(offset, r, size);
+ *pdata = r;
+ return MEMTX_OK;
+}
+
+static void iotkit_secctl_update_ppc_ap(IoTKitSecCtlPPC *ppc)
+{
+ int i;
+
+ for (i = 0; i < ppc->numports; i++) {
+ bool v;
+
+ if (extract32(ppc->ns, i, 1)) {
+ v = extract32(ppc->nsp, i, 1);
+ } else {
+ v = extract32(ppc->sp, i, 1);
+ }
+ qemu_set_irq(ppc->ap[i], v);
+ }
+}
+
+static void iotkit_secctl_ppc_ns_write(IoTKitSecCtlPPC *ppc, uint32_t value)
+{
+ int i;
+
+ ppc->ns = value & MAKE_64BIT_MASK(0, ppc->numports);
+ for (i = 0; i < ppc->numports; i++) {
+ qemu_set_irq(ppc->nonsec[i], extract32(ppc->ns, i, 1));
+ }
+ iotkit_secctl_update_ppc_ap(ppc);
+}
+
+static void iotkit_secctl_ppc_sp_write(IoTKitSecCtlPPC *ppc, uint32_t value)
+{
+ ppc->sp = value & MAKE_64BIT_MASK(0, ppc->numports);
+ iotkit_secctl_update_ppc_ap(ppc);
+}
+
+static void iotkit_secctl_ppc_nsp_write(IoTKitSecCtlPPC *ppc, uint32_t value)
+{
+ ppc->nsp = value & MAKE_64BIT_MASK(0, ppc->numports);
+ iotkit_secctl_update_ppc_ap(ppc);
+}
+
+static void iotkit_secctl_ppc_update_irq_clear(IoTKitSecCtlPPC *ppc)
+{
+ uint32_t value = ppc->parent->secppcintstat;
+
+ qemu_set_irq(ppc->irq_clear, extract32(value, ppc->irq_bit_offset, 1));
+}
+
+static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc)
+{
+ uint32_t value = ppc->parent->secppcinten;
+
+ qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1));
+}
+
+static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr,
+ uint64_t value,
+ unsigned size, MemTxAttrs attrs)
+{
+ IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+ uint32_t offset = addr;
+ IoTKitSecCtlPPC *ppc;
+
+ trace_iotkit_secctl_s_write(offset, value, size);
+
+ if (size != 4) {
+ /* Byte and halfword writes are ignored */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IotKit SecCtl S block write: bad size, ignored\n");
+ return MEMTX_OK;
+ }
+
+ switch (offset) {
+ case A_NSCCFG:
+ s->nsccfg = value & 3;
+ qemu_set_irq(s->nsc_cfg_irq, s->nsccfg);
+ break;
+ case A_SECRESPCFG:
+ value &= 1;
+ s->secrespcfg = value;
+ qemu_set_irq(s->sec_resp_cfg, s->secrespcfg);
+ break;
+ case A_SECPPCINTCLR:
+ value &= 0x00f000f3;
+ foreach_ppc(s, iotkit_secctl_ppc_update_irq_clear);
+ break;
+ case A_SECPPCINTEN:
+ s->secppcinten = value & 0x00f000f3;
+ foreach_ppc(s, iotkit_secctl_ppc_update_irq_enable);
+ break;
+ case A_BRGINTCLR:
+ break;
+ case A_BRGINTEN:
+ s->brginten = value & 0xffff0000;
+ break;
+ case A_AHBNSPPCEXP0:
+ case A_AHBNSPPCEXP1:
+ case A_AHBNSPPCEXP2:
+ case A_AHBNSPPCEXP3:
+ ppc = &s->ahbexp[offset_to_ppc_idx(offset)];
+ iotkit_secctl_ppc_ns_write(ppc, value);
+ break;
+ case A_APBNSPPC0:
+ case A_APBNSPPC1:
+ ppc = &s->apb[offset_to_ppc_idx(offset)];
+ iotkit_secctl_ppc_ns_write(ppc, value);
+ break;
+ case A_APBNSPPCEXP0:
+ case A_APBNSPPCEXP1:
+ case A_APBNSPPCEXP2:
+ case A_APBNSPPCEXP3:
+ ppc = &s->apbexp[offset_to_ppc_idx(offset)];
+ iotkit_secctl_ppc_ns_write(ppc, value);
+ break;
+ case A_AHBSPPPCEXP0:
+ case A_AHBSPPPCEXP1:
+ case A_AHBSPPPCEXP2:
+ case A_AHBSPPPCEXP3:
+ ppc = &s->ahbexp[offset_to_ppc_idx(offset)];
+ iotkit_secctl_ppc_sp_write(ppc, value);
+ break;
+ case A_APBSPPPC0:
+ case A_APBSPPPC1:
+ ppc = &s->apb[offset_to_ppc_idx(offset)];
+ iotkit_secctl_ppc_sp_write(ppc, value);
+ break;
+ case A_APBSPPPCEXP0:
+ case A_APBSPPPCEXP1:
+ case A_APBSPPPCEXP2:
+ case A_APBSPPPCEXP3:
+ ppc = &s->apbexp[offset_to_ppc_idx(offset)];
+ iotkit_secctl_ppc_sp_write(ppc, value);
+ break;
+ case A_SECMSCINTCLR:
+ case A_SECMSCINTEN:
+ qemu_log_mask(LOG_UNIMP,
+ "IoTKit SecCtl S block write: "
+ "unimplemented offset 0x%x\n", offset);
+ break;
+ case A_SECMPCINTSTATUS:
+ case A_SECPPCINTSTAT:
+ case A_SECMSCINTSTAT:
+ case A_BRGINTSTAT:
+ case A_AHBNSPPC0:
+ case A_AHBSPPPC0:
+ case A_NSMSCEXP:
+ case A_PID4:
+ case A_PID5:
+ case A_PID6:
+ case A_PID7:
+ case A_PID0:
+ case A_PID1:
+ case A_PID2:
+ case A_PID3:
+ case A_CID0:
+ case A_CID1:
+ case A_CID2:
+ case A_CID3:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IoTKit SecCtl S block write: "
+ "read-only offset 0x%x\n", offset);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IotKit SecCtl S block write: bad offset 0x%x\n",
+ offset);
+ break;
+ }
+
+ return MEMTX_OK;
+}
+
+static MemTxResult iotkit_secctl_ns_read(void *opaque, hwaddr addr,
+ uint64_t *pdata,
+ unsigned size, MemTxAttrs attrs)
+{
+ IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+ uint64_t r;
+ uint32_t offset = addr & ~0x3;
+
+ switch (offset) {
+ case A_AHBNSPPPC0:
+ r = 0;
+ break;
+ case A_AHBNSPPPCEXP0:
+ case A_AHBNSPPPCEXP1:
+ case A_AHBNSPPPCEXP2:
+ case A_AHBNSPPPCEXP3:
+ r = s->ahbexp[offset_to_ppc_idx(offset)].nsp;
+ break;
+ case A_APBNSPPPC0:
+ case A_APBNSPPPC1:
+ r = s->apb[offset_to_ppc_idx(offset)].nsp;
+ break;
+ case A_APBNSPPPCEXP0:
+ case A_APBNSPPPCEXP1:
+ case A_APBNSPPPCEXP2:
+ case A_APBNSPPPCEXP3:
+ r = s->apbexp[offset_to_ppc_idx(offset)].nsp;
+ break;
+ case A_PID4:
+ case A_PID5:
+ case A_PID6:
+ case A_PID7:
+ case A_PID0:
+ case A_PID1:
+ case A_PID2:
+ case A_PID3:
+ case A_CID0:
+ case A_CID1:
+ case A_CID2:
+ case A_CID3:
+ r = iotkit_secctl_ns_idregs[(offset - A_PID4) / 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IotKit SecCtl NS block write: bad offset 0x%x\n",
+ offset);
+ r = 0;
+ break;
+ }
+
+ if (size != 4) {
+ /* None of our registers are access-sensitive, so just pull the right
+ * byte out of the word read result.
+ */
+ r = extract32(r, (addr & 3) * 8, size * 8);
+ }
+
+ trace_iotkit_secctl_ns_read(offset, r, size);
+ *pdata = r;
+ return MEMTX_OK;
+}
+
+static MemTxResult iotkit_secctl_ns_write(void *opaque, hwaddr addr,
+ uint64_t value,
+ unsigned size, MemTxAttrs attrs)
+{
+ IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+ uint32_t offset = addr;
+ IoTKitSecCtlPPC *ppc;
+
+ trace_iotkit_secctl_ns_write(offset, value, size);
+
+ if (size != 4) {
+ /* Byte and halfword writes are ignored */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IotKit SecCtl NS block write: bad size, ignored\n");
+ return MEMTX_OK;
+ }
+
+ switch (offset) {
+ case A_AHBNSPPPCEXP0:
+ case A_AHBNSPPPCEXP1:
+ case A_AHBNSPPPCEXP2:
+ case A_AHBNSPPPCEXP3:
+ ppc = &s->ahbexp[offset_to_ppc_idx(offset)];
+ iotkit_secctl_ppc_nsp_write(ppc, value);
+ break;
+ case A_APBNSPPPC0:
+ case A_APBNSPPPC1:
+ ppc = &s->apb[offset_to_ppc_idx(offset)];
+ iotkit_secctl_ppc_nsp_write(ppc, value);
+ break;
+ case A_APBNSPPPCEXP0:
+ case A_APBNSPPPCEXP1:
+ case A_APBNSPPPCEXP2:
+ case A_APBNSPPPCEXP3:
+ ppc = &s->apbexp[offset_to_ppc_idx(offset)];
+ iotkit_secctl_ppc_nsp_write(ppc, value);
+ break;
+ case A_AHBNSPPPC0:
+ case A_PID4:
+ case A_PID5:
+ case A_PID6:
+ case A_PID7:
+ case A_PID0:
+ case A_PID1:
+ case A_PID2:
+ case A_PID3:
+ case A_CID0:
+ case A_CID1:
+ case A_CID2:
+ case A_CID3:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IoTKit SecCtl NS block write: "
+ "read-only offset 0x%x\n", offset);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IotKit SecCtl NS block write: bad offset 0x%x\n",
+ offset);
+ break;
+ }
+
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps iotkit_secctl_s_ops = {
+ .read_with_attrs = iotkit_secctl_s_read,
+ .write_with_attrs = iotkit_secctl_s_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+};
+
+static const MemoryRegionOps iotkit_secctl_ns_ops = {
+ .read_with_attrs = iotkit_secctl_ns_read,
+ .write_with_attrs = iotkit_secctl_ns_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+};
+
+static void iotkit_secctl_reset_ppc(IoTKitSecCtlPPC *ppc)
+{
+ ppc->ns = 0;
+ ppc->sp = 0;
+ ppc->nsp = 0;
+}
+
+static void iotkit_secctl_reset(DeviceState *dev)
+{
+ IoTKitSecCtl *s = IOTKIT_SECCTL(dev);
+
+ s->secppcintstat = 0;
+ s->secppcinten = 0;
+ s->secrespcfg = 0;
+ s->nsccfg = 0;
+ s->brginten = 0;
+
+ foreach_ppc(s, iotkit_secctl_reset_ppc);
+}
+
+static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level)
+{
+ IoTKitSecCtlPPC *ppc = opaque;
+ IoTKitSecCtl *s = IOTKIT_SECCTL(ppc->parent);
+ int irqbit = ppc->irq_bit_offset + n;
+
+ s->secppcintstat = deposit32(s->secppcintstat, irqbit, 1, level);
+}
+
+static void iotkit_secctl_init_ppc(IoTKitSecCtl *s,
+ IoTKitSecCtlPPC *ppc,
+ const char *name,
+ int numports,
+ int irq_bit_offset)
+{
+ char *gpioname;
+ DeviceState *dev = DEVICE(s);
+
+ ppc->numports = numports;
+ ppc->irq_bit_offset = irq_bit_offset;
+ ppc->parent = s;
+
+ gpioname = g_strdup_printf("%s_nonsec", name);
+ qdev_init_gpio_out_named(dev, ppc->nonsec, gpioname, numports);
+ g_free(gpioname);
+ gpioname = g_strdup_printf("%s_ap", name);
+ qdev_init_gpio_out_named(dev, ppc->ap, gpioname, numports);
+ g_free(gpioname);
+ gpioname = g_strdup_printf("%s_irq_enable", name);
+ qdev_init_gpio_out_named(dev, &ppc->irq_enable, gpioname, 1);
+ g_free(gpioname);
+ gpioname = g_strdup_printf("%s_irq_clear", name);
+ qdev_init_gpio_out_named(dev, &ppc->irq_clear, gpioname, 1);
+ g_free(gpioname);
+ gpioname = g_strdup_printf("%s_irq_status", name);
+ qdev_init_gpio_in_named_with_opaque(dev, iotkit_secctl_ppc_irqstatus,
+ ppc, gpioname, 1);
+ g_free(gpioname);
+}
+
+static void iotkit_secctl_init(Object *obj)
+{
+ IoTKitSecCtl *s = IOTKIT_SECCTL(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ DeviceState *dev = DEVICE(obj);
+ int i;
+
+ iotkit_secctl_init_ppc(s, &s->apb[0], "apb_ppc0",
+ IOTS_APB_PPC0_NUM_PORTS, 0);
+ iotkit_secctl_init_ppc(s, &s->apb[1], "apb_ppc1",
+ IOTS_APB_PPC1_NUM_PORTS, 1);
+
+ for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) {
+ IoTKitSecCtlPPC *ppc = &s->apbexp[i];
+ char *ppcname = g_strdup_printf("apb_ppcexp%d", i);
+ iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 4 + i);
+ g_free(ppcname);
+ }
+ for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) {
+ IoTKitSecCtlPPC *ppc = &s->ahbexp[i];
+ char *ppcname = g_strdup_printf("ahb_ppcexp%d", i);
+ iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 20 + i);
+ g_free(ppcname);
+ }
+
+ qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1);
+ qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1);
+
+ memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops,
+ s, "iotkit-secctl-s-regs", 0x1000);
+ memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops,
+ s, "iotkit-secctl-ns-regs", 0x1000);
+ sysbus_init_mmio(sbd, &s->s_regs);
+ sysbus_init_mmio(sbd, &s->ns_regs);
+}
+
+static const VMStateDescription iotkit_secctl_ppc_vmstate = {
+ .name = "iotkit-secctl-ppc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ns, IoTKitSecCtlPPC),
+ VMSTATE_UINT32(sp, IoTKitSecCtlPPC),
+ VMSTATE_UINT32(nsp, IoTKitSecCtlPPC),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription iotkit_secctl_vmstate = {
+ .name = "iotkit-secctl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(secppcintstat, IoTKitSecCtl),
+ VMSTATE_UINT32(secppcinten, IoTKitSecCtl),
+ VMSTATE_UINT32(secrespcfg, IoTKitSecCtl),
+ VMSTATE_UINT32(nsccfg, IoTKitSecCtl),
+ VMSTATE_UINT32(brginten, IoTKitSecCtl),
+ VMSTATE_STRUCT_ARRAY(apb, IoTKitSecCtl, IOTS_NUM_APB_PPC, 1,
+ iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC),
+ VMSTATE_STRUCT_ARRAY(apbexp, IoTKitSecCtl, IOTS_NUM_APB_EXP_PPC, 1,
+ iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC),
+ VMSTATE_STRUCT_ARRAY(ahbexp, IoTKitSecCtl, IOTS_NUM_AHB_EXP_PPC, 1,
+ iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void iotkit_secctl_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &iotkit_secctl_vmstate;
+ dc->reset = iotkit_secctl_reset;
+}
+
+static const TypeInfo iotkit_secctl_info = {
+ .name = TYPE_IOTKIT_SECCTL,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IoTKitSecCtl),
+ .instance_init = iotkit_secctl_init,
+ .class_init = iotkit_secctl_class_init,
+};
+
+static void iotkit_secctl_register_types(void)
+{
+ type_register_static(&iotkit_secctl_info);
+}
+
+type_init(iotkit_secctl_register_types);
diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c
new file mode 100644
index 0000000000..7394a057d8
--- /dev/null
+++ b/hw/misc/mps2-fpgaio.c
@@ -0,0 +1,176 @@
+/*
+ * ARM MPS2 AN505 FPGAIO emulation
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/* This is a model of the "FPGA system control and I/O" block found
+ * in the AN505 FPGA image for the MPS2 devboard.
+ * It is documented in AN505:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/mps2-fpgaio.h"
+
+REG32(LED0, 0)
+REG32(BUTTON, 8)
+REG32(CLK1HZ, 0x10)
+REG32(CLK100HZ, 0x14)
+REG32(COUNTER, 0x18)
+REG32(PRESCALE, 0x1c)
+REG32(PSCNTR, 0x20)
+REG32(MISC, 0x4c)
+
+static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
+{
+ MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
+ uint64_t r;
+
+ switch (offset) {
+ case A_LED0:
+ r = s->led0;
+ break;
+ case A_BUTTON:
+ /* User-pressable board buttons. We don't model that, so just return
+ * zeroes.
+ */
+ r = 0;
+ break;
+ case A_PRESCALE:
+ r = s->prescale;
+ break;
+ case A_MISC:
+ r = s->misc;
+ break;
+ case A_CLK1HZ:
+ case A_CLK100HZ:
+ case A_COUNTER:
+ case A_PSCNTR:
+ /* These are all upcounters of various frequencies. */
+ qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n");
+ r = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "MPS2 FPGAIO read: bad offset %x\n", (int) offset);
+ r = 0;
+ break;
+ }
+
+ trace_mps2_fpgaio_read(offset, r, size);
+ return r;
+}
+
+static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
+
+ trace_mps2_fpgaio_write(offset, value, size);
+
+ switch (offset) {
+ case A_LED0:
+ /* LED bits [1:0] control board LEDs. We don't currently have
+ * a mechanism for displaying this graphically, so use a trace event.
+ */
+ trace_mps2_fpgaio_leds(value & 0x02 ? '*' : '.',
+ value & 0x01 ? '*' : '.');
+ s->led0 = value & 0x3;
+ break;
+ case A_PRESCALE:
+ s->prescale = value;
+ break;
+ case A_MISC:
+ /* These are control bits for some of the other devices on the
+ * board (SPI, CLCD, etc). We don't implement that yet, so just
+ * make the bits read as written.
+ */
+ qemu_log_mask(LOG_UNIMP,
+ "MPS2 FPGAIO: MISC control bits unimplemented\n");
+ s->misc = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps mps2_fpgaio_ops = {
+ .read = mps2_fpgaio_read,
+ .write = mps2_fpgaio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void mps2_fpgaio_reset(DeviceState *dev)
+{
+ MPS2FPGAIO *s = MPS2_FPGAIO(dev);
+
+ trace_mps2_fpgaio_reset();
+ s->led0 = 0;
+ s->prescale = 0;
+ s->misc = 0;
+}
+
+static void mps2_fpgaio_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MPS2FPGAIO *s = MPS2_FPGAIO(obj);
+
+ memory_region_init_io(&s->iomem, obj, &mps2_fpgaio_ops, s,
+ "mps2-fpgaio", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription mps2_fpgaio_vmstate = {
+ .name = "mps2-fpgaio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(led0, MPS2FPGAIO),
+ VMSTATE_UINT32(prescale, MPS2FPGAIO),
+ VMSTATE_UINT32(misc, MPS2FPGAIO),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property mps2_fpgaio_properties[] = {
+ /* Frequency of the prescale counter */
+ DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mps2_fpgaio_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &mps2_fpgaio_vmstate;
+ dc->reset = mps2_fpgaio_reset;
+ dc->props = mps2_fpgaio_properties;
+}
+
+static const TypeInfo mps2_fpgaio_info = {
+ .name = TYPE_MPS2_FPGAIO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MPS2FPGAIO),
+ .instance_init = mps2_fpgaio_init,
+ .class_init = mps2_fpgaio_class_init,
+};
+
+static void mps2_fpgaio_register_types(void)
+{
+ type_register_static(&mps2_fpgaio_info);
+}
+
+type_init(mps2_fpgaio_register_types);
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index b340d4e81c..eb5ffcc0a8 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -62,6 +62,12 @@ mps2_scc_leds(char led7, char led6, char led5, char led4, char led3, char led2,
mps2_scc_cfg_write(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config write: function %d device %d data 0x%" PRIx32
mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config read: function %d device %d data 0x%" PRIx32
+# hw/misc/mps2_fpgaio.c
+mps2_fpgaio_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+mps2_fpgaio_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+mps2_fpgaio_reset(void) "MPS2 FPGAIO: reset"
+mps2_fpgaio_leds(char led1, char led0) "MPS2 FPGAIO LEDs: %c%c"
+
# hw/misc/msf2-sysreg.c
msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" HWADDR_PRIx " data 0x%" PRIx32 " prev 0x%" PRIx32
msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" HWADDR_PRIx " data 0x%08" PRIx32
@@ -77,3 +83,21 @@ mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d co
mos6522_set_sr_int(void) "set sr_int"
mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
+
+# hw/misc/tz-ppc.c
+tz_ppc_reset(void) "TZ PPC: reset"
+tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d"
+tz_ppc_cfg_ap(int n, int level) "TZ PPC: cfg_ap[%d] = %d"
+tz_ppc_cfg_sec_resp(int level) "TZ PPC: cfg_sec_resp = %d"
+tz_ppc_irq_enable(int level) "TZ PPC: int_enable = %d"
+tz_ppc_irq_clear(int level) "TZ PPC: int_clear = %d"
+tz_ppc_update_irq(int level) "TZ PPC: setting irq line to %d"
+tz_ppc_read_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " read (secure %d user %d) blocked"
+tz_ppc_write_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " write (secure %d user %d) blocked"
+
+# hw/misc/iotkit-secctl.c
+iotkit_secctl_s_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs read: offset 0x%x data 0x%" PRIx64 " size %u"
+iotkit_secctl_s_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs write: offset 0x%x data 0x%" PRIx64 " size %u"
+iotkit_secctl_ns_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs read: offset 0x%x data 0x%" PRIx64 " size %u"
+iotkit_secctl_ns_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs write: offset 0x%x data 0x%" PRIx64 " size %u"
+iotkit_secctl_reset(void) "IoTKit SecCtl: reset"
diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c
new file mode 100644
index 0000000000..3dd045c15f
--- /dev/null
+++ b/hw/misc/tz-ppc.c
@@ -0,0 +1,302 @@
+/*
+ * ARM TrustZone peripheral protection controller emulation
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/tz-ppc.h"
+
+static void tz_ppc_update_irq(TZPPC *s)
+{
+ bool level = s->irq_status && s->irq_enable;
+
+ trace_tz_ppc_update_irq(level);
+ qemu_set_irq(s->irq, level);
+}
+
+static void tz_ppc_cfg_nonsec(void *opaque, int n, int level)
+{
+ TZPPC *s = TZ_PPC(opaque);
+
+ assert(n < TZ_NUM_PORTS);
+ trace_tz_ppc_cfg_nonsec(n, level);
+ s->cfg_nonsec[n] = level;
+}
+
+static void tz_ppc_cfg_ap(void *opaque, int n, int level)
+{
+ TZPPC *s = TZ_PPC(opaque);
+
+ assert(n < TZ_NUM_PORTS);
+ trace_tz_ppc_cfg_ap(n, level);
+ s->cfg_ap[n] = level;
+}
+
+static void tz_ppc_cfg_sec_resp(void *opaque, int n, int level)
+{
+ TZPPC *s = TZ_PPC(opaque);
+
+ trace_tz_ppc_cfg_sec_resp(level);
+ s->cfg_sec_resp = level;
+}
+
+static void tz_ppc_irq_enable(void *opaque, int n, int level)
+{
+ TZPPC *s = TZ_PPC(opaque);
+
+ trace_tz_ppc_irq_enable(level);
+ s->irq_enable = level;
+ tz_ppc_update_irq(s);
+}
+
+static void tz_ppc_irq_clear(void *opaque, int n, int level)
+{
+ TZPPC *s = TZ_PPC(opaque);
+
+ trace_tz_ppc_irq_clear(level);
+
+ s->irq_clear = level;
+ if (level) {
+ s->irq_status = false;
+ tz_ppc_update_irq(s);
+ }
+}
+
+static bool tz_ppc_check(TZPPC *s, int n, MemTxAttrs attrs)
+{
+ /* Check whether to allow an access to port n; return true if
+ * the check passes, and false if the transaction must be blocked.
+ * If the latter, the caller must check cfg_sec_resp to determine
+ * whether to abort or RAZ/WI the transaction.
+ * The checks are:
+ * + nonsec_mask suppresses any check of the secure attribute
+ * + otherwise, block if cfg_nonsec is 1 and transaction is secure,
+ * or if cfg_nonsec is 0 and transaction is non-secure
+ * + block if transaction is usermode and cfg_ap is 0
+ */
+ if ((attrs.secure == s->cfg_nonsec[n] && !(s->nonsec_mask & (1 << n))) ||
+ (attrs.user && !s->cfg_ap[n])) {
+ /* Block the transaction. */
+ if (!s->irq_clear) {
+ /* Note that holding irq_clear high suppresses interrupts */
+ s->irq_status = true;
+ tz_ppc_update_irq(s);
+ }
+ return false;
+ }
+ return true;
+}
+
+static MemTxResult tz_ppc_read(void *opaque, hwaddr addr, uint64_t *pdata,
+ unsigned size, MemTxAttrs attrs)
+{
+ TZPPCPort *p = opaque;
+ TZPPC *s = p->ppc;
+ int n = p - s->port;
+ AddressSpace *as = &p->downstream_as;
+ uint64_t data;
+ MemTxResult res;
+
+ if (!tz_ppc_check(s, n, attrs)) {
+ trace_tz_ppc_read_blocked(n, addr, attrs.secure, attrs.user);
+ if (s->cfg_sec_resp) {
+ return MEMTX_ERROR;
+ } else {
+ *pdata = 0;
+ return MEMTX_OK;
+ }
+ }
+
+ switch (size) {
+ case 1:
+ data = address_space_ldub(as, addr, attrs, &res);
+ break;
+ case 2:
+ data = address_space_lduw_le(as, addr, attrs, &res);
+ break;
+ case 4:
+ data = address_space_ldl_le(as, addr, attrs, &res);
+ break;
+ case 8:
+ data = address_space_ldq_le(as, addr, attrs, &res);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ *pdata = data;
+ return res;
+}
+
+static MemTxResult tz_ppc_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size, MemTxAttrs attrs)
+{
+ TZPPCPort *p = opaque;
+ TZPPC *s = p->ppc;
+ AddressSpace *as = &p->downstream_as;
+ int n = p - s->port;
+ MemTxResult res;
+
+ if (!tz_ppc_check(s, n, attrs)) {
+ trace_tz_ppc_write_blocked(n, addr, attrs.secure, attrs.user);
+ if (s->cfg_sec_resp) {
+ return MEMTX_ERROR;
+ } else {
+ return MEMTX_OK;
+ }
+ }
+
+ switch (size) {
+ case 1:
+ address_space_stb(as, addr, val, attrs, &res);
+ break;
+ case 2:
+ address_space_stw_le(as, addr, val, attrs, &res);
+ break;
+ case 4:
+ address_space_stl_le(as, addr, val, attrs, &res);
+ break;
+ case 8:
+ address_space_stq_le(as, addr, val, attrs, &res);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return res;
+}
+
+static const MemoryRegionOps tz_ppc_ops = {
+ .read_with_attrs = tz_ppc_read,
+ .write_with_attrs = tz_ppc_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void tz_ppc_reset(DeviceState *dev)
+{
+ TZPPC *s = TZ_PPC(dev);
+
+ trace_tz_ppc_reset();
+ s->cfg_sec_resp = false;
+ memset(s->cfg_nonsec, 0, sizeof(s->cfg_nonsec));
+ memset(s->cfg_ap, 0, sizeof(s->cfg_ap));
+}
+
+static void tz_ppc_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ TZPPC *s = TZ_PPC(obj);
+
+ qdev_init_gpio_in_named(dev, tz_ppc_cfg_nonsec, "cfg_nonsec", TZ_NUM_PORTS);
+ qdev_init_gpio_in_named(dev, tz_ppc_cfg_ap, "cfg_ap", TZ_NUM_PORTS);
+ qdev_init_gpio_in_named(dev, tz_ppc_cfg_sec_resp, "cfg_sec_resp", 1);
+ qdev_init_gpio_in_named(dev, tz_ppc_irq_enable, "irq_enable", 1);
+ qdev_init_gpio_in_named(dev, tz_ppc_irq_clear, "irq_clear", 1);
+ qdev_init_gpio_out_named(dev, &s->irq, "irq", 1);
+}
+
+static void tz_ppc_realize(DeviceState *dev, Error **errp)
+{
+ Object *obj = OBJECT(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ TZPPC *s = TZ_PPC(dev);
+ int i;
+
+ /* We can't create the upstream end of the port until realize,
+ * as we don't know the size of the MR used as the downstream until then.
+ */
+ for (i = 0; i < TZ_NUM_PORTS; i++) {
+ TZPPCPort *port = &s->port[i];
+ char *name;
+ uint64_t size;
+
+ if (!port->downstream) {
+ continue;
+ }
+
+ name = g_strdup_printf("tz-ppc-port[%d]", i);
+
+ port->ppc = s;
+ address_space_init(&port->downstream_as, port->downstream, name);
+
+ size = memory_region_size(port->downstream);
+ memory_region_init_io(&port->upstream, obj, &tz_ppc_ops,
+ port, name, size);
+ sysbus_init_mmio(sbd, &port->upstream);
+ g_free(name);
+ }
+}
+
+static const VMStateDescription tz_ppc_vmstate = {
+ .name = "tz-ppc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL_ARRAY(cfg_nonsec, TZPPC, 16),
+ VMSTATE_BOOL_ARRAY(cfg_ap, TZPPC, 16),
+ VMSTATE_BOOL(cfg_sec_resp, TZPPC),
+ VMSTATE_BOOL(irq_enable, TZPPC),
+ VMSTATE_BOOL(irq_clear, TZPPC),
+ VMSTATE_BOOL(irq_status, TZPPC),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define DEFINE_PORT(N) \
+ DEFINE_PROP_LINK("port[" #N "]", TZPPC, port[N].downstream, \
+ TYPE_MEMORY_REGION, MemoryRegion *)
+
+static Property tz_ppc_properties[] = {
+ DEFINE_PROP_UINT32("NONSEC_MASK", TZPPC, nonsec_mask, 0),
+ DEFINE_PORT(0),
+ DEFINE_PORT(1),
+ DEFINE_PORT(2),
+ DEFINE_PORT(3),
+ DEFINE_PORT(4),
+ DEFINE_PORT(5),
+ DEFINE_PORT(6),
+ DEFINE_PORT(7),
+ DEFINE_PORT(8),
+ DEFINE_PORT(9),
+ DEFINE_PORT(10),
+ DEFINE_PORT(11),
+ DEFINE_PORT(12),
+ DEFINE_PORT(13),
+ DEFINE_PORT(14),
+ DEFINE_PORT(15),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tz_ppc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = tz_ppc_realize;
+ dc->vmsd = &tz_ppc_vmstate;
+ dc->reset = tz_ppc_reset;
+ dc->props = tz_ppc_properties;
+}
+
+static const TypeInfo tz_ppc_info = {
+ .name = TYPE_TZ_PPC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(TZPPC),
+ .instance_init = tz_ppc_init,
+ .class_init = tz_ppc_class_init,
+};
+
+static void tz_ppc_register_types(void)
+{
+ type_register_static(&tz_ppc_info);
+}
+
+type_init(tz_ppc_register_types);
diff --git a/hw/misc/unimp.c b/hw/misc/unimp.c
index bcbb585888..1c0ba2f0a7 100644
--- a/hw/misc/unimp.c
+++ b/hw/misc/unimp.c
@@ -18,16 +18,6 @@
#include "qemu/log.h"
#include "qapi/error.h"
-#define UNIMPLEMENTED_DEVICE(obj) \
- OBJECT_CHECK(UnimplementedDeviceState, (obj), TYPE_UNIMPLEMENTED_DEVICE)
-
-typedef struct {
- SysBusDevice parent_obj;
- MemoryRegion iomem;
- char *name;
- uint64_t size;
-} UnimplementedDeviceState;
-
static uint64_t unimp_read(void *opaque, hwaddr offset, unsigned size)
{
UnimplementedDeviceState *s = UNIMPLEMENTED_DEVICE(opaque);
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 8c19eac3b6..8b27a4b7ef 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -21,6 +21,7 @@ common-obj-$(CONFIG_IMX) += imx_epit.o
common-obj-$(CONFIG_IMX) += imx_gpt.o
common-obj-$(CONFIG_LM32) += lm32_timer.o
common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o
+common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o
obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index 640722b5d1..e6e042fddb 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -60,3 +60,6 @@ systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr
cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset"
+
+# hw/timer/xlnx-zynqmp-rtc.c
+xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d"
diff --git a/hw/timer/xlnx-zynqmp-rtc.c b/hw/timer/xlnx-zynqmp-rtc.c
new file mode 100644
index 0000000000..c98dc3d94e
--- /dev/null
+++ b/hw/timer/xlnx-zynqmp-rtc.c
@@ -0,0 +1,272 @@
+/*
+ * QEMU model of the Xilinx ZynqMP Real Time Clock (RTC).
+ *
+ * Copyright (c) 2017 Xilinx Inc.
+ *
+ * Written-by: Alistair Francis <alistair.francis@xilinx.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "hw/register.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "hw/ptimer.h"
+#include "qemu/cutils.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+#include "hw/timer/xlnx-zynqmp-rtc.h"
+
+#ifndef XLNX_ZYNQMP_RTC_ERR_DEBUG
+#define XLNX_ZYNQMP_RTC_ERR_DEBUG 0
+#endif
+
+static void rtc_int_update_irq(XlnxZynqMPRTC *s)
+{
+ bool pending = s->regs[R_RTC_INT_STATUS] & ~s->regs[R_RTC_INT_MASK];
+ qemu_set_irq(s->irq_rtc_int, pending);
+}
+
+static void addr_error_int_update_irq(XlnxZynqMPRTC *s)
+{
+ bool pending = s->regs[R_ADDR_ERROR] & ~s->regs[R_ADDR_ERROR_INT_MASK];
+ qemu_set_irq(s->irq_addr_error_int, pending);
+}
+
+static uint32_t rtc_get_count(XlnxZynqMPRTC *s)
+{
+ int64_t now = qemu_clock_get_ns(rtc_clock);
+ return s->tick_offset + now / NANOSECONDS_PER_SECOND;
+}
+
+static uint64_t current_time_postr(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
+
+ return rtc_get_count(s);
+}
+
+static void rtc_int_status_postw(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
+ rtc_int_update_irq(s);
+}
+
+static uint64_t rtc_int_en_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
+
+ s->regs[R_RTC_INT_MASK] &= (uint32_t) ~val64;
+ rtc_int_update_irq(s);
+ return 0;
+}
+
+static uint64_t rtc_int_dis_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
+
+ s->regs[R_RTC_INT_MASK] |= (uint32_t) val64;
+ rtc_int_update_irq(s);
+ return 0;
+}
+
+static void addr_error_postw(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
+ addr_error_int_update_irq(s);
+}
+
+static uint64_t addr_error_int_en_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
+
+ s->regs[R_ADDR_ERROR_INT_MASK] &= (uint32_t) ~val64;
+ addr_error_int_update_irq(s);
+ return 0;
+}
+
+static uint64_t addr_error_int_dis_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
+
+ s->regs[R_ADDR_ERROR_INT_MASK] |= (uint32_t) val64;
+ addr_error_int_update_irq(s);
+ return 0;
+}
+
+static const RegisterAccessInfo rtc_regs_info[] = {
+ { .name = "SET_TIME_WRITE", .addr = A_SET_TIME_WRITE,
+ .unimp = MAKE_64BIT_MASK(0, 32),
+ },{ .name = "SET_TIME_READ", .addr = A_SET_TIME_READ,
+ .ro = 0xffffffff,
+ .post_read = current_time_postr,
+ },{ .name = "CALIB_WRITE", .addr = A_CALIB_WRITE,
+ .unimp = MAKE_64BIT_MASK(0, 32),
+ },{ .name = "CALIB_READ", .addr = A_CALIB_READ,
+ .ro = 0x1fffff,
+ },{ .name = "CURRENT_TIME", .addr = A_CURRENT_TIME,
+ .ro = 0xffffffff,
+ .post_read = current_time_postr,
+ },{ .name = "CURRENT_TICK", .addr = A_CURRENT_TICK,
+ .ro = 0xffff,
+ },{ .name = "ALARM", .addr = A_ALARM,
+ },{ .name = "RTC_INT_STATUS", .addr = A_RTC_INT_STATUS,
+ .w1c = 0x3,
+ .post_write = rtc_int_status_postw,
+ },{ .name = "RTC_INT_MASK", .addr = A_RTC_INT_MASK,
+ .reset = 0x3,
+ .ro = 0x3,
+ },{ .name = "RTC_INT_EN", .addr = A_RTC_INT_EN,
+ .pre_write = rtc_int_en_prew,
+ },{ .name = "RTC_INT_DIS", .addr = A_RTC_INT_DIS,
+ .pre_write = rtc_int_dis_prew,
+ },{ .name = "ADDR_ERROR", .addr = A_ADDR_ERROR,
+ .w1c = 0x1,
+ .post_write = addr_error_postw,
+ },{ .name = "ADDR_ERROR_INT_MASK", .addr = A_ADDR_ERROR_INT_MASK,
+ .reset = 0x1,
+ .ro = 0x1,
+ },{ .name = "ADDR_ERROR_INT_EN", .addr = A_ADDR_ERROR_INT_EN,
+ .pre_write = addr_error_int_en_prew,
+ },{ .name = "ADDR_ERROR_INT_DIS", .addr = A_ADDR_ERROR_INT_DIS,
+ .pre_write = addr_error_int_dis_prew,
+ },{ .name = "CONTROL", .addr = A_CONTROL,
+ .reset = 0x1000000,
+ .rsvd = 0x70fffffe,
+ },{ .name = "SAFETY_CHK", .addr = A_SAFETY_CHK,
+ }
+};
+
+static void rtc_reset(DeviceState *dev)
+{
+ XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(dev);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
+ register_reset(&s->regs_info[i]);
+ }
+
+ rtc_int_update_irq(s);
+ addr_error_int_update_irq(s);
+}
+
+static const MemoryRegionOps rtc_ops = {
+ .read = register_read_memory,
+ .write = register_write_memory,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void rtc_init(Object *obj)
+{
+ XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ RegisterInfoArray *reg_array;
+ struct tm current_tm;
+
+ memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_RTC,
+ XLNX_ZYNQMP_RTC_R_MAX * 4);
+ reg_array =
+ register_init_block32(DEVICE(obj), rtc_regs_info,
+ ARRAY_SIZE(rtc_regs_info),
+ s->regs_info, s->regs,
+ &rtc_ops,
+ XLNX_ZYNQMP_RTC_ERR_DEBUG,
+ XLNX_ZYNQMP_RTC_R_MAX * 4);
+ memory_region_add_subregion(&s->iomem,
+ 0x0,
+ &reg_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(&current_tm, 0);
+ s->tick_offset = mktimegm(&current_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)