target-arm queue:
* New machine mps2-an521 -- this is a model of the AN521 FPGA image for the MPS2 devboard * Fix various places where we failed to UNDEF invalid A64 instructions * Don't UNDEF a valid FCMLA on 32-bit inputs * Fix some bugs in the newly-added PAuth implementation * microbit: Implement NVMC non-volatile memory controller -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAlxUbmsZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3ubXD/9PJxn0RnOkxpEmnba1QYbj mq1c9d9t2Ewd5wVLPWUZSlVjhLENUtPd6F5+CXGNwo+GpJgIG04tg4EhSXAAJtlF msdvtVJLVhp2O/uoaLx4HilyzJjwMk3RExE4nE9czZ0B3bbfRCpeNV7wwlU8Q+ty fbeK0zsjLmqYJ1J5nXhVbBxUsfpqFeOy0pH3S+9CWg9zxvbORmERFVyp31SgIB5r mctU6IUH9wekNGQfGMKFazLjKLOnCYc39O3e1DuLGaK9ouA1alsfzl+GRiveYp1A royrZjLBO995L5mVt8KcSYvpUaw8AzKZgchJ6KmM68pMGCsabmNHDbwtUtquLjir 7Ik64RovlRYDQeF2ee+giJF+sVH4fKFa7onFIl955Rv4SyEoyvJxEvd8gby3gwAa +JiWY4IDZCpnUvbWP7gdnKMTH5T50srE5PiYCbQ+raBGRGgH3s2Ogt1NVBOKVFv5 YycWbt8Px+c+kSz3vT3ohQshoVUtTzFAWPR0s7+m83oxTqOBZ/wf1VipmgU05NWl j0aJZhIS/5aOmKf+8+iLmfOYQ/8yq2guUnLDUWtN2F0f+7KysZhnauF7iTp/hIGQ rDd74ktQoPFvr6Q2komFisQ5vgmLKaMM6mQUPpObhIwTw7Oz3anwvQDNgMCRB1JU 5PKH8pS1XHVhX4iM3FHv9g== =ak3S -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190201' into staging target-arm queue: * New machine mps2-an521 -- this is a model of the AN521 FPGA image for the MPS2 devboard * Fix various places where we failed to UNDEF invalid A64 instructions * Don't UNDEF a valid FCMLA on 32-bit inputs * Fix some bugs in the newly-added PAuth implementation * microbit: Implement NVMC non-volatile memory controller # gpg: Signature made Fri 01 Feb 2019 16:06:03 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20190201: (47 commits) tests/microbit-test: Add tests for nRF51 NVMC arm: Instantiate NRF51 special NVM's and NVMC hw/nvram/nrf51_nvm: Add nRF51 non-volatile memories target/arm: fix decoding of B{,L}RA{A,B} target/arm: fix AArch64 virtual address space size linux-user: Initialize aarch64 pac keys aarch64-linux-user: Enable HWCAP bits for PAuth aarch64-linux-user: Update HWCAP bits from linux 5.0-rc1 target/arm: Always enable pac keys for user-only arm: Clarify the logic of set_pc() target/arm: Enable API, APK bits in SCR, HCR target/arm: Add a timer to predict PMU counter overflow target/arm: Send interrupts on PMU counter overflow target/arm/translate-a64: Fix mishandling of size in FCMLA decode target/arm/translate-a64: Fix FCMLA decoding error exec.c: Don't reallocate IOMMUNotifiers that are in use target/arm/translate-a64: Don't underdecode SDOT and UDOT target/arm/translate-a64: Don't underdecode FP insns target/arm/translate-a64: Don't underdecode add/sub extended register target/arm/translate-a64: Don't underdecode SIMD ld/st single ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
e83d74286c
@ -624,14 +624,16 @@ F: hw/arm/mps2.c
|
||||
F: hw/arm/mps2-tz.c
|
||||
F: hw/misc/mps2-*.c
|
||||
F: include/hw/misc/mps2-*.h
|
||||
F: hw/arm/iotkit.c
|
||||
F: include/hw/arm/iotkit.h
|
||||
F: hw/arm/armsse.c
|
||||
F: include/hw/arm/armsse.h
|
||||
F: hw/misc/iotkit-secctl.c
|
||||
F: include/hw/misc/iotkit-secctl.h
|
||||
F: hw/misc/iotkit-sysctl.c
|
||||
F: include/hw/misc/iotkit-sysctl.h
|
||||
F: hw/misc/iotkit-sysinfo.c
|
||||
F: include/hw/misc/iotkit-sysinfo.h
|
||||
F: hw/misc/armsse-cpuid.c
|
||||
F: include/hw/misc/armsse-cpuid.h
|
||||
|
||||
Musicpal
|
||||
M: Jan Kiszka <jan.kiszka@web.de>
|
||||
|
@ -114,10 +114,11 @@ CONFIG_MPS2_SCC=y
|
||||
CONFIG_TZ_MPC=y
|
||||
CONFIG_TZ_MSC=y
|
||||
CONFIG_TZ_PPC=y
|
||||
CONFIG_IOTKIT=y
|
||||
CONFIG_ARMSSE=y
|
||||
CONFIG_IOTKIT_SECCTL=y
|
||||
CONFIG_IOTKIT_SYSCTL=y
|
||||
CONFIG_IOTKIT_SYSINFO=y
|
||||
CONFIG_ARMSSE_CPUID=y
|
||||
|
||||
CONFIG_VERSATILE=y
|
||||
CONFIG_VERSATILE_PCI=y
|
||||
|
10
exec.c
10
exec.c
@ -665,7 +665,7 @@ static void tcg_register_iommu_notifier(CPUState *cpu,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cpu->iommu_notifiers->len; i++) {
|
||||
notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i);
|
||||
notifier = g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i);
|
||||
if (notifier->mr == mr && notifier->iommu_idx == iommu_idx) {
|
||||
break;
|
||||
}
|
||||
@ -673,7 +673,8 @@ static void tcg_register_iommu_notifier(CPUState *cpu,
|
||||
if (i == cpu->iommu_notifiers->len) {
|
||||
/* Not found, add a new entry at the end of the array */
|
||||
cpu->iommu_notifiers = g_array_set_size(cpu->iommu_notifiers, i + 1);
|
||||
notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i);
|
||||
notifier = g_new0(TCGIOMMUNotifier, 1);
|
||||
g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i) = notifier;
|
||||
|
||||
notifier->mr = mr;
|
||||
notifier->iommu_idx = iommu_idx;
|
||||
@ -705,8 +706,9 @@ static void tcg_iommu_free_notifier_list(CPUState *cpu)
|
||||
TCGIOMMUNotifier *notifier;
|
||||
|
||||
for (i = 0; i < cpu->iommu_notifiers->len; i++) {
|
||||
notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i);
|
||||
notifier = g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i);
|
||||
memory_region_unregister_iommu_notifier(notifier->mr, ¬ifier->n);
|
||||
g_free(notifier);
|
||||
}
|
||||
g_array_free(cpu->iommu_notifiers, true);
|
||||
}
|
||||
@ -976,7 +978,7 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
|
||||
vmstate_register(NULL, cpu->cpu_index, cc->vmsd, cpu);
|
||||
}
|
||||
|
||||
cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier));
|
||||
cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier *));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ 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
|
||||
obj-$(CONFIG_ARMSSE) += armsse.o
|
||||
obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
|
||||
obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o
|
||||
obj-$(CONFIG_FSL_IMX6UL) += fsl-imx6ul.o mcimx6ul-evk.o
|
||||
|
1241
hw/arm/armsse.c
Normal file
1241
hw/arm/armsse.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -158,7 +158,12 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1);
|
||||
|
||||
s->cpu = ARM_CPU(object_new(s->cpu_type));
|
||||
s->cpu = ARM_CPU(object_new_with_props(s->cpu_type, OBJECT(s), "cpu",
|
||||
&err, NULL));
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory",
|
||||
&error_abort);
|
||||
@ -177,11 +182,21 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (object_property_find(OBJECT(s->cpu), "start-powered-off", NULL)) {
|
||||
object_property_set_bool(OBJECT(s->cpu), s->start_powered_off,
|
||||
"start-powered-off", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the CPU where the NVIC is; it will fail realize if it doesn't
|
||||
* have one.
|
||||
/*
|
||||
* Tell the CPU where the NVIC is; it will fail realize if it doesn't
|
||||
* have one. Similarly, tell the NVIC where its CPU is.
|
||||
*/
|
||||
s->cpu->env.nvic = &s->nvic;
|
||||
s->nvic.cpu = s->cpu;
|
||||
|
||||
object_property_set_bool(OBJECT(s->cpu), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
@ -243,6 +258,8 @@ static Property armv7m_properties[] = {
|
||||
DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *),
|
||||
DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0),
|
||||
DEFINE_PROP_BOOL("enable-bitband", ARMv7MState, enable_bitband, false),
|
||||
DEFINE_PROP_BOOL("start-powered-off", ARMv7MState, start_powered_off,
|
||||
false),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
@ -697,10 +697,6 @@ static void do_cpu_reset(void *opaque)
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
if (!env->aarch64) {
|
||||
env->thumb = info->entry & 1;
|
||||
entry &= 0xfffffffe;
|
||||
}
|
||||
cpu_set_pc(cs, entry);
|
||||
} else {
|
||||
/* If we are booting Linux then we need to check whether we are
|
||||
|
759
hw/arm/iotkit.c
759
hw/arm/iotkit.c
@ -1,759 +0,0 @@
|
||||
/*
|
||||
* 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/arm/arm.h"
|
||||
|
||||
/* Clock frequency in HZ of the 32KHz "slow clock" */
|
||||
#define S32KCLK (32 * 1000)
|
||||
|
||||
/* 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 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);
|
||||
|
||||
sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m),
|
||||
TYPE_ARMV7M);
|
||||
qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type",
|
||||
ARM_CPU_TYPE_NAME("cortex-m33"));
|
||||
|
||||
sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl),
|
||||
TYPE_IOTKIT_SECCTL);
|
||||
sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0),
|
||||
TYPE_TZ_PPC);
|
||||
sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1),
|
||||
TYPE_TZ_PPC);
|
||||
sysbus_init_child_obj(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC);
|
||||
object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate,
|
||||
sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ,
|
||||
&error_abort, NULL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) {
|
||||
char *name = g_strdup_printf("mpc-irq-splitter-%d", i);
|
||||
SplitIRQ *splitter = &s->mpc_irq_splitter[i];
|
||||
|
||||
object_initialize_child(obj, name, splitter, sizeof(*splitter),
|
||||
TYPE_SPLIT_IRQ, &error_abort, NULL);
|
||||
g_free(name);
|
||||
}
|
||||
sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0),
|
||||
TYPE_CMSDK_APB_TIMER);
|
||||
sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1),
|
||||
TYPE_CMSDK_APB_TIMER);
|
||||
sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer),
|
||||
TYPE_CMSDK_APB_TIMER);
|
||||
sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer),
|
||||
TYPE_CMSDK_APB_DUALTIMER);
|
||||
sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog,
|
||||
sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG);
|
||||
sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog,
|
||||
sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG);
|
||||
sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog,
|
||||
sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG);
|
||||
sysbus_init_child_obj(obj, "iotkit-sysctl", &s->sysctl,
|
||||
sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL);
|
||||
sysbus_init_child_obj(obj, "iotkit-sysinfo", &s->sysinfo,
|
||||
sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO);
|
||||
object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate,
|
||||
sizeof(s->nmi_orgate), TYPE_OR_IRQ,
|
||||
&error_abort, NULL);
|
||||
object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate,
|
||||
sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ,
|
||||
&error_abort, NULL);
|
||||
object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter,
|
||||
sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ,
|
||||
&error_abort, NULL);
|
||||
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_child(obj, name, splitter, sizeof(*splitter),
|
||||
TYPE_SPLIT_IRQ, &error_abort, NULL);
|
||||
g_free(name);
|
||||
}
|
||||
}
|
||||
|
||||
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_mpcexp_status(void *opaque, int n, int level)
|
||||
{
|
||||
IoTKit *s = IOTKIT(opaque);
|
||||
qemu_set_irq(s->mpcexp_status_in[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);
|
||||
|
||||
|
||||
/* 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));
|
||||
|
||||
/* This RAM lives behind the Memory Protection Controller */
|
||||
memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
object_property_set_link(OBJECT(&s->mpc), OBJECT(&s->sram0),
|
||||
"downstream", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
object_property_set_bool(OBJECT(&s->mpc), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
/* Map the upstream end of the MPC into the right place... */
|
||||
memory_region_add_subregion(&s->container, 0x20000000,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc),
|
||||
1));
|
||||
/* ...and its register interface */
|
||||
memory_region_add_subregion(&s->container, 0x50083000,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc),
|
||||
0));
|
||||
|
||||
/* We must OR together lines from the MPC splitters to go to the NVIC */
|
||||
object_property_set_int(OBJECT(&s->mpc_irq_orgate),
|
||||
IOTS_NUM_EXP_MPC + IOTS_NUM_MPC, "num-lines", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true,
|
||||
"realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->armv7m), 9));
|
||||
|
||||
/* 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), 4));
|
||||
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_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq);
|
||||
object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->armv7m), 5));
|
||||
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_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK);
|
||||
object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->armv7m), 2));
|
||||
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));
|
||||
|
||||
object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
/* System information registers */
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000);
|
||||
/* System control registers */
|
||||
object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000);
|
||||
|
||||
/* This OR gate wires together outputs from the secure watchdogs to NMI */
|
||||
object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0));
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK);
|
||||
object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0));
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000);
|
||||
|
||||
/* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq);
|
||||
object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->armv7m), 1));
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000);
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq);
|
||||
object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1));
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000);
|
||||
|
||||
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));
|
||||
g_free(gpioname);
|
||||
}
|
||||
|
||||
/* Wire up the splitters for the MPC IRQs */
|
||||
for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) {
|
||||
SplitIRQ *splitter = &s->mpc_irq_splitter[i];
|
||||
DeviceState *dev_splitter = DEVICE(splitter);
|
||||
|
||||
object_property_set_int(OBJECT(splitter), 2, "num-lines", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
object_property_set_bool(OBJECT(splitter), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (i < IOTS_NUM_EXP_MPC) {
|
||||
/* Splitter input is from GPIO input line */
|
||||
s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0);
|
||||
qdev_connect_gpio_out(dev_splitter, 0,
|
||||
qdev_get_gpio_in_named(dev_secctl,
|
||||
"mpcexp_status", i));
|
||||
} else {
|
||||
/* Splitter input is from our own MPC */
|
||||
qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0,
|
||||
qdev_get_gpio_in(dev_splitter, 0));
|
||||
qdev_connect_gpio_out(dev_splitter, 0,
|
||||
qdev_get_gpio_in_named(dev_secctl,
|
||||
"mpc_status", 0));
|
||||
}
|
||||
|
||||
qdev_connect_gpio_out(dev_splitter, 1,
|
||||
qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i));
|
||||
}
|
||||
/* Create GPIO inputs which will pass the line state for our
|
||||
* mpcexp_irq inputs to the correct splitter devices.
|
||||
*/
|
||||
qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status",
|
||||
IOTS_NUM_EXP_MPC);
|
||||
|
||||
iotkit_forward_sec_resp_cfg(s);
|
||||
|
||||
/* Forward the MSC related signals */
|
||||
qdev_pass_gpios(dev_secctl, dev, "mscexp_status");
|
||||
qdev_pass_gpios(dev_secctl, dev, "mscexp_clear");
|
||||
qdev_pass_gpios(dev_secctl, dev, "mscexp_ns");
|
||||
qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->armv7m), 11));
|
||||
|
||||
/*
|
||||
* Expose our container region to the board model; this corresponds
|
||||
* to the AHB Slave Expansion ports which allow bus master devices
|
||||
* (eg DMA controllers) in the board model to make transactions into
|
||||
* devices in the IoTKit.
|
||||
*/
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
|
||||
|
||||
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);
|
121
hw/arm/mps2-tz.c
121
hw/arm/mps2-tz.c
@ -15,6 +15,7 @@
|
||||
* 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
|
||||
* "mps2-an521" -- Dual Cortex-M33 as documented in Application Note AN521
|
||||
*
|
||||
* Links to the TRM for the board itself and to the various Application
|
||||
* Notes which document the FPGA images can be found here:
|
||||
@ -24,10 +25,16 @@
|
||||
* 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
|
||||
* Application Note AN521:
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.dai0521c/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
|
||||
* Similarly, the AN521 uses the SSE-200, and the SSE-200 TRM defines
|
||||
* most of the device layout:
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
@ -46,27 +53,31 @@
|
||||
#include "hw/misc/mps2-fpgaio.h"
|
||||
#include "hw/misc/tz-mpc.h"
|
||||
#include "hw/misc/tz-msc.h"
|
||||
#include "hw/arm/iotkit.h"
|
||||
#include "hw/arm/armsse.h"
|
||||
#include "hw/dma/pl080.h"
|
||||
#include "hw/ssi/pl022.h"
|
||||
#include "hw/devices.h"
|
||||
#include "net/net.h"
|
||||
#include "hw/core/split-irq.h"
|
||||
|
||||
#define MPS2TZ_NUMIRQ 92
|
||||
|
||||
typedef enum MPS2TZFPGAType {
|
||||
FPGA_AN505,
|
||||
FPGA_AN521,
|
||||
} MPS2TZFPGAType;
|
||||
|
||||
typedef struct {
|
||||
MachineClass parent;
|
||||
MPS2TZFPGAType fpga_type;
|
||||
uint32_t scc_id;
|
||||
const char *armsse_type;
|
||||
} MPS2TZMachineClass;
|
||||
|
||||
typedef struct {
|
||||
MachineState parent;
|
||||
|
||||
IoTKit iotkit;
|
||||
ARMSSE iotkit;
|
||||
MemoryRegion psram;
|
||||
MemoryRegion ssram[3];
|
||||
MemoryRegion ssram1_m;
|
||||
@ -85,10 +96,12 @@ typedef struct {
|
||||
SplitIRQ sec_resp_splitter;
|
||||
qemu_or_irq uart_irq_orgate;
|
||||
DeviceState *lan9118;
|
||||
SplitIRQ cpu_irq_splitter[MPS2TZ_NUMIRQ];
|
||||
} MPS2TZMachineState;
|
||||
|
||||
#define TYPE_MPS2TZ_MACHINE "mps2tz"
|
||||
#define TYPE_MPS2TZ_AN505_MACHINE MACHINE_TYPE_NAME("mps2-an505")
|
||||
#define TYPE_MPS2TZ_AN521_MACHINE MACHINE_TYPE_NAME("mps2-an521")
|
||||
|
||||
#define MPS2TZ_MACHINE(obj) \
|
||||
OBJECT_CHECK(MPS2TZMachineState, obj, TYPE_MPS2TZ_MACHINE)
|
||||
@ -111,6 +124,23 @@ static void make_ram_alias(MemoryRegion *mr, const char *name,
|
||||
memory_region_add_subregion(get_system_memory(), base, mr);
|
||||
}
|
||||
|
||||
static qemu_irq get_sse_irq_in(MPS2TZMachineState *mms, int irqno)
|
||||
{
|
||||
/* Return a qemu_irq which will signal IRQ n to all CPUs in the SSE. */
|
||||
MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms);
|
||||
|
||||
assert(irqno < MPS2TZ_NUMIRQ);
|
||||
|
||||
switch (mmc->fpga_type) {
|
||||
case FPGA_AN505:
|
||||
return qdev_get_gpio_in_named(DEVICE(&mms->iotkit), "EXP_IRQ", irqno);
|
||||
case FPGA_AN521:
|
||||
return qdev_get_gpio_in(DEVICE(&mms->cpu_irq_splitter[irqno]), 0);
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
/* 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.
|
||||
@ -161,7 +191,6 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque,
|
||||
int txirqno = i * 2 + 1;
|
||||
int combirqno = i + 10;
|
||||
SysBusDevice *s;
|
||||
DeviceState *iotkitdev = DEVICE(&mms->iotkit);
|
||||
DeviceState *orgate_dev = DEVICE(&mms->uart_irq_orgate);
|
||||
|
||||
sysbus_init_child_obj(OBJECT(mms), name, uart, sizeof(mms->uart[0]),
|
||||
@ -170,14 +199,11 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque,
|
||||
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, 0, get_sse_irq_in(mms, txirqno));
|
||||
sysbus_connect_irq(s, 1, get_sse_irq_in(mms, 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));
|
||||
sysbus_connect_irq(s, 4, get_sse_irq_in(mms, combirqno));
|
||||
return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0);
|
||||
}
|
||||
|
||||
@ -213,7 +239,6 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque,
|
||||
const char *name, hwaddr size)
|
||||
{
|
||||
SysBusDevice *s;
|
||||
DeviceState *iotkitdev = DEVICE(&mms->iotkit);
|
||||
NICInfo *nd = &nd_table[0];
|
||||
|
||||
/* In hardware this is a LAN9220; the LAN9118 is software compatible
|
||||
@ -225,7 +250,7 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque,
|
||||
qdev_init_nofail(mms->lan9118);
|
||||
|
||||
s = SYS_BUS_DEVICE(mms->lan9118);
|
||||
sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16));
|
||||
sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 16));
|
||||
return sysbus_mmio_get_region(s, 0);
|
||||
}
|
||||
|
||||
@ -315,12 +340,9 @@ static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque,
|
||||
|
||||
s = SYS_BUS_DEVICE(dma);
|
||||
/* Wire up DMACINTR, DMACINTERR, DMACINTTC */
|
||||
sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev,
|
||||
"EXP_IRQ", 58 + i * 3));
|
||||
sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev,
|
||||
"EXP_IRQ", 56 + i * 3));
|
||||
sysbus_connect_irq(s, 2, qdev_get_gpio_in_named(iotkitdev,
|
||||
"EXP_IRQ", 57 + i * 3));
|
||||
sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 58 + i * 3));
|
||||
sysbus_connect_irq(s, 1, get_sse_irq_in(mms, 56 + i * 3));
|
||||
sysbus_connect_irq(s, 2, get_sse_irq_in(mms, 57 + i * 3));
|
||||
|
||||
g_free(mscname);
|
||||
return sysbus_mmio_get_region(s, 0);
|
||||
@ -339,21 +361,20 @@ static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque,
|
||||
*/
|
||||
PL022State *spi = opaque;
|
||||
int i = spi - &mms->spi[0];
|
||||
DeviceState *iotkitdev = DEVICE(&mms->iotkit);
|
||||
SysBusDevice *s;
|
||||
|
||||
sysbus_init_child_obj(OBJECT(mms), name, spi, sizeof(mms->spi[0]),
|
||||
TYPE_PL022);
|
||||
object_property_set_bool(OBJECT(spi), true, "realized", &error_fatal);
|
||||
s = SYS_BUS_DEVICE(spi);
|
||||
sysbus_connect_irq(s, 0,
|
||||
qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 51 + i));
|
||||
sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 51 + i));
|
||||
return sysbus_mmio_get_region(s, 0);
|
||||
}
|
||||
|
||||
static void mps2tz_common_init(MachineState *machine)
|
||||
{
|
||||
MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine);
|
||||
MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
DeviceState *iotkitdev;
|
||||
@ -367,15 +388,42 @@ static void mps2tz_common_init(MachineState *machine)
|
||||
}
|
||||
|
||||
sysbus_init_child_obj(OBJECT(machine), "iotkit", &mms->iotkit,
|
||||
sizeof(mms->iotkit), TYPE_IOTKIT);
|
||||
sizeof(mms->iotkit), mmc->armsse_type);
|
||||
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, "EXP_NUMIRQ", MPS2TZ_NUMIRQ);
|
||||
qdev_prop_set_uint32(iotkitdev, "MAINCLK", SYSCLK_FRQ);
|
||||
object_property_set_bool(OBJECT(&mms->iotkit), true, "realized",
|
||||
&error_fatal);
|
||||
|
||||
/*
|
||||
* The AN521 needs us to create splitters to feed the IRQ inputs
|
||||
* for each CPU in the SSE-200 from each device in the board.
|
||||
*/
|
||||
if (mmc->fpga_type == FPGA_AN521) {
|
||||
for (i = 0; i < MPS2TZ_NUMIRQ; i++) {
|
||||
char *name = g_strdup_printf("mps2-irq-splitter%d", i);
|
||||
SplitIRQ *splitter = &mms->cpu_irq_splitter[i];
|
||||
|
||||
object_initialize_child(OBJECT(machine), name,
|
||||
splitter, sizeof(*splitter),
|
||||
TYPE_SPLIT_IRQ, &error_fatal, NULL);
|
||||
g_free(name);
|
||||
|
||||
object_property_set_int(OBJECT(splitter), 2, "num-lines",
|
||||
&error_fatal);
|
||||
object_property_set_bool(OBJECT(splitter), true, "realized",
|
||||
&error_fatal);
|
||||
qdev_connect_gpio_out(DEVICE(splitter), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&mms->iotkit),
|
||||
"EXP_IRQ", i));
|
||||
qdev_connect_gpio_out(DEVICE(splitter), 1,
|
||||
qdev_get_gpio_in_named(DEVICE(&mms->iotkit),
|
||||
"EXP_CPU1_IRQ", i));
|
||||
}
|
||||
}
|
||||
|
||||
/* The sec_resp_cfg output from the IoTKit must be split into multiple
|
||||
* lines, one for each of the PPCs we create here, plus one per MSC.
|
||||
*/
|
||||
@ -426,7 +474,7 @@ static void mps2tz_common_init(MachineState *machine)
|
||||
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));
|
||||
get_sse_irq_in(mms, 15));
|
||||
|
||||
/* Most of the devices in the FPGA are behind Peripheral Protection
|
||||
* Controllers. The required order for initializing things is:
|
||||
@ -593,7 +641,6 @@ static void mps2tz_class_init(ObjectClass *oc, void *data)
|
||||
IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc);
|
||||
|
||||
mc->init = mps2tz_common_init;
|
||||
mc->max_cpus = 1;
|
||||
iic->check = mps2_tz_idau_check;
|
||||
}
|
||||
|
||||
@ -603,9 +650,28 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data)
|
||||
MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc);
|
||||
|
||||
mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33";
|
||||
mc->default_cpus = 1;
|
||||
mc->min_cpus = mc->default_cpus;
|
||||
mc->max_cpus = mc->default_cpus;
|
||||
mmc->fpga_type = FPGA_AN505;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
|
||||
mmc->scc_id = 0x41045050;
|
||||
mmc->armsse_type = TYPE_IOTKIT;
|
||||
}
|
||||
|
||||
static void mps2tz_an521_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc);
|
||||
|
||||
mc->desc = "ARM MPS2 with AN521 FPGA image for dual Cortex-M33";
|
||||
mc->default_cpus = 2;
|
||||
mc->min_cpus = mc->default_cpus;
|
||||
mc->max_cpus = mc->default_cpus;
|
||||
mmc->fpga_type = FPGA_AN521;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
|
||||
mmc->scc_id = 0x41045210;
|
||||
mmc->armsse_type = TYPE_SSE200;
|
||||
}
|
||||
|
||||
static const TypeInfo mps2tz_info = {
|
||||
@ -627,10 +693,17 @@ static const TypeInfo mps2tz_an505_info = {
|
||||
.class_init = mps2tz_an505_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo mps2tz_an521_info = {
|
||||
.name = TYPE_MPS2TZ_AN521_MACHINE,
|
||||
.parent = TYPE_MPS2TZ_MACHINE,
|
||||
.class_init = mps2tz_an521_class_init,
|
||||
};
|
||||
|
||||
static void mps2tz_machine_init(void)
|
||||
{
|
||||
type_register_static(&mps2tz_info);
|
||||
type_register_static(&mps2tz_an505_info);
|
||||
type_register_static(&mps2tz_an521_info);
|
||||
}
|
||||
|
||||
type_init(mps2tz_machine_init);
|
||||
|
@ -29,8 +29,10 @@
|
||||
* are supported in the future, add a sub-class of NRF51SoC for
|
||||
* the specific variants
|
||||
*/
|
||||
#define NRF51822_FLASH_SIZE (256 * NRF51_PAGE_SIZE)
|
||||
#define NRF51822_SRAM_SIZE (16 * NRF51_PAGE_SIZE)
|
||||
#define NRF51822_FLASH_PAGES 256
|
||||
#define NRF51822_SRAM_PAGES 16
|
||||
#define NRF51822_FLASH_SIZE (NRF51822_FLASH_PAGES * NRF51_PAGE_SIZE)
|
||||
#define NRF51822_SRAM_SIZE (NRF51822_SRAM_PAGES * NRF51_PAGE_SIZE)
|
||||
|
||||
#define BASE_TO_IRQ(base) ((base >> 12) & 0x1F)
|
||||
|
||||
@ -81,15 +83,8 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
|
||||
memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1);
|
||||
|
||||
memory_region_init_rom(&s->flash, OBJECT(s), "nrf51.flash", s->flash_size,
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
memory_region_add_subregion(&s->container, NRF51_FLASH_BASE, &s->flash);
|
||||
|
||||
memory_region_init_ram(&s->sram, NULL, "nrf51.sram", s->sram_size, &err);
|
||||
memory_region_init_ram(&s->sram, OBJECT(s), "nrf51.sram", s->sram_size,
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
@ -121,6 +116,29 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
qdev_get_gpio_in(DEVICE(&s->cpu),
|
||||
BASE_TO_IRQ(NRF51_RNG_BASE)));
|
||||
|
||||
/* UICR, FICR, NVMC, FLASH */
|
||||
object_property_set_uint(OBJECT(&s->nvm), s->flash_size, "flash-size",
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->nvm), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 0);
|
||||
memory_region_add_subregion_overlap(&s->container, NRF51_NVMC_BASE, mr, 0);
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 1);
|
||||
memory_region_add_subregion_overlap(&s->container, NRF51_FICR_BASE, mr, 0);
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 2);
|
||||
memory_region_add_subregion_overlap(&s->container, NRF51_UICR_BASE, mr, 0);
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 3);
|
||||
memory_region_add_subregion_overlap(&s->container, NRF51_FLASH_BASE, mr, 0);
|
||||
|
||||
/* GPIO */
|
||||
object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err);
|
||||
if (err) {
|
||||
@ -158,8 +176,6 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
|
||||
create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE,
|
||||
NRF51_IOMEM_SIZE);
|
||||
create_unimplemented_device("nrf51_soc.ficr", NRF51_FICR_BASE,
|
||||
NRF51_FICR_SIZE);
|
||||
create_unimplemented_device("nrf51_soc.private",
|
||||
NRF51_PRIVATE_BASE, NRF51_PRIVATE_SIZE);
|
||||
}
|
||||
@ -186,6 +202,8 @@ static void nrf51_soc_init(Object *obj)
|
||||
sysbus_init_child_obj(obj, "rng", &s->rng, sizeof(s->rng),
|
||||
TYPE_NRF51_RNG);
|
||||
|
||||
sysbus_init_child_obj(obj, "nvm", &s->nvm, sizeof(s->nvm), TYPE_NRF51_NVM);
|
||||
|
||||
sysbus_init_child_obj(obj, "gpio", &s->gpio, sizeof(s->gpio),
|
||||
TYPE_NRF51_GPIO);
|
||||
|
||||
|
@ -2274,8 +2274,7 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
|
||||
Error *err = NULL;
|
||||
int regionlen;
|
||||
|
||||
s->cpu = ARM_CPU(qemu_get_cpu(0));
|
||||
|
||||
/* The armv7m container object will have set our CPU pointer */
|
||||
if (!s->cpu || !arm_feature(&s->cpu->env, ARM_FEATURE_M)) {
|
||||
error_setg(errp, "The NVIC can only be used with a Cortex-M CPU");
|
||||
return;
|
||||
|
@ -69,6 +69,7 @@ obj-$(CONFIG_TZ_PPC) += tz-ppc.o
|
||||
obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o
|
||||
obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o
|
||||
obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o
|
||||
obj-$(CONFIG_ARMSSE_CPUID) += armsse-cpuid.o
|
||||
|
||||
obj-$(CONFIG_PVPANIC) += pvpanic.o
|
||||
obj-$(CONFIG_AUX) += auxbus.o
|
||||
|
134
hw/misc/armsse-cpuid.c
Normal file
134
hw/misc/armsse-cpuid.c
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* ARM SSE-200 CPU_IDENTITY register block
|
||||
*
|
||||
* Copyright (c) 2019 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 "CPU_IDENTITY" register block which is part of the
|
||||
* Arm SSE-200 and documented in
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
|
||||
*
|
||||
* It consists of one read-only CPUID register (set by QOM property), plus the
|
||||
* usual ID registers.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "trace.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/misc/armsse-cpuid.h"
|
||||
|
||||
REG32(CPUID, 0x0)
|
||||
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)
|
||||
|
||||
/* PID/CID values */
|
||||
static const int sysinfo_id[] = {
|
||||
0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
|
||||
0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */
|
||||
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
|
||||
};
|
||||
|
||||
static uint64_t armsse_cpuid_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
ARMSSECPUID *s = ARMSSE_CPUID(opaque);
|
||||
uint64_t r;
|
||||
|
||||
switch (offset) {
|
||||
case A_CPUID:
|
||||
r = s->cpuid;
|
||||
break;
|
||||
case A_PID4 ... A_CID3:
|
||||
r = sysinfo_id[(offset - A_PID4) / 4];
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"SSE CPU_IDENTITY read: bad offset 0x%x\n", (int)offset);
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
trace_armsse_cpuid_read(offset, r, size);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void armsse_cpuid_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
trace_armsse_cpuid_write(offset, value, size);
|
||||
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"SSE CPU_IDENTITY: write to RO offset 0x%x\n", (int)offset);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps armsse_cpuid_ops = {
|
||||
.read = armsse_cpuid_read,
|
||||
.write = armsse_cpuid_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
/* byte/halfword accesses are just zero-padded on reads and writes */
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static Property armsse_cpuid_props[] = {
|
||||
DEFINE_PROP_UINT32("CPUID", ARMSSECPUID, cpuid, 0),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static void armsse_cpuid_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
ARMSSECPUID *s = ARMSSE_CPUID(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &armsse_cpuid_ops,
|
||||
s, "armsse-cpuid", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void armsse_cpuid_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
/*
|
||||
* This device has no guest-modifiable state and so it
|
||||
* does not need a reset function or VMState.
|
||||
*/
|
||||
|
||||
dc->props = armsse_cpuid_props;
|
||||
}
|
||||
|
||||
static const TypeInfo armsse_cpuid_info = {
|
||||
.name = TYPE_ARMSSE_CPUID,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ARMSSECPUID),
|
||||
.instance_init = armsse_cpuid_init,
|
||||
.class_init = armsse_cpuid_class_init,
|
||||
};
|
||||
|
||||
static void armsse_cpuid_register_types(void)
|
||||
{
|
||||
type_register_static(&armsse_cpuid_info);
|
||||
}
|
||||
|
||||
type_init(armsse_cpuid_register_types);
|
@ -600,7 +600,7 @@ static void iotkit_secctl_mpc_status(void *opaque, int n, int level)
|
||||
{
|
||||
IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
|
||||
|
||||
s->mpcintstatus = deposit32(s->mpcintstatus, 0, 1, !!level);
|
||||
s->mpcintstatus = deposit32(s->mpcintstatus, n, 1, !!level);
|
||||
}
|
||||
|
||||
static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level)
|
||||
@ -686,7 +686,8 @@ static void iotkit_secctl_init(Object *obj)
|
||||
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);
|
||||
|
||||
qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", 1);
|
||||
qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status",
|
||||
IOTS_NUM_MPC);
|
||||
qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status,
|
||||
"mpcexp_status", IOTS_NUM_EXP_MPC);
|
||||
|
||||
|
@ -51,15 +51,16 @@ static const int sysinfo_id[] = {
|
||||
static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
IoTKitSysInfo *s = IOTKIT_SYSINFO(opaque);
|
||||
uint64_t r;
|
||||
|
||||
switch (offset) {
|
||||
case A_SYS_VERSION:
|
||||
r = 0x41743;
|
||||
r = s->sys_version;
|
||||
break;
|
||||
|
||||
case A_SYS_CONFIG:
|
||||
r = 0x31;
|
||||
r = s->sys_config;
|
||||
break;
|
||||
case A_PID4 ... A_CID3:
|
||||
r = sysinfo_id[(offset - A_PID4) / 4];
|
||||
@ -94,6 +95,12 @@ static const MemoryRegionOps iotkit_sysinfo_ops = {
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static Property iotkit_sysinfo_props[] = {
|
||||
DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysInfo, sys_version, 0),
|
||||
DEFINE_PROP_UINT32("SYS_CONFIG", IoTKitSysInfo, sys_config, 0),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static void iotkit_sysinfo_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
@ -106,10 +113,14 @@ static void iotkit_sysinfo_init(Object *obj)
|
||||
|
||||
static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
/*
|
||||
* This device has no guest-modifiable state and so it
|
||||
* does not need a reset function or VMState.
|
||||
*/
|
||||
|
||||
dc->props = iotkit_sysinfo_props;
|
||||
}
|
||||
|
||||
static const TypeInfo iotkit_sysinfo_info = {
|
||||
|
@ -132,3 +132,7 @@ iotkit_sysinfo_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysI
|
||||
iotkit_sysctl_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
iotkit_sysctl_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
iotkit_sysctl_reset(void) "IoTKit SysCtl: reset"
|
||||
|
||||
# hw/misc/armsse-cpuid.c
|
||||
armsse_cpuid_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
|
@ -5,3 +5,4 @@ common-obj-y += fw_cfg.o
|
||||
common-obj-y += chrp_nvram.o
|
||||
common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_nvram.o
|
||||
obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o
|
||||
|
388
hw/nvram/nrf51_nvm.c
Normal file
388
hw/nvram/nrf51_nvm.c
Normal file
@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Nordic Semiconductor nRF51 non-volatile memory
|
||||
*
|
||||
* It provides an interface to erase regions in flash memory.
|
||||
* Furthermore it provides the user and factory information registers.
|
||||
*
|
||||
* Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf
|
||||
*
|
||||
* See nRF51 reference manual and product sheet sections:
|
||||
* + Non-Volatile Memory Controller (NVMC)
|
||||
* + Factory Information Configuration Registers (FICR)
|
||||
* + User Information Configuration Registers (UICR)
|
||||
*
|
||||
* Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/log.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/arm/nrf51.h"
|
||||
#include "hw/nvram/nrf51_nvm.h"
|
||||
|
||||
/*
|
||||
* FICR Registers Assignments
|
||||
* CODEPAGESIZE 0x010
|
||||
* CODESIZE 0x014
|
||||
* CLENR0 0x028
|
||||
* PPFC 0x02C
|
||||
* NUMRAMBLOCK 0x034
|
||||
* SIZERAMBLOCKS 0x038
|
||||
* SIZERAMBLOCK[0] 0x038
|
||||
* SIZERAMBLOCK[1] 0x03C
|
||||
* SIZERAMBLOCK[2] 0x040
|
||||
* SIZERAMBLOCK[3] 0x044
|
||||
* CONFIGID 0x05C
|
||||
* DEVICEID[0] 0x060
|
||||
* DEVICEID[1] 0x064
|
||||
* ER[0] 0x080
|
||||
* ER[1] 0x084
|
||||
* ER[2] 0x088
|
||||
* ER[3] 0x08C
|
||||
* IR[0] 0x090
|
||||
* IR[1] 0x094
|
||||
* IR[2] 0x098
|
||||
* IR[3] 0x09C
|
||||
* DEVICEADDRTYPE 0x0A0
|
||||
* DEVICEADDR[0] 0x0A4
|
||||
* DEVICEADDR[1] 0x0A8
|
||||
* OVERRIDEEN 0x0AC
|
||||
* NRF_1MBIT[0] 0x0B0
|
||||
* NRF_1MBIT[1] 0x0B4
|
||||
* NRF_1MBIT[2] 0x0B8
|
||||
* NRF_1MBIT[3] 0x0BC
|
||||
* NRF_1MBIT[4] 0x0C0
|
||||
* BLE_1MBIT[0] 0x0EC
|
||||
* BLE_1MBIT[1] 0x0F0
|
||||
* BLE_1MBIT[2] 0x0F4
|
||||
* BLE_1MBIT[3] 0x0F8
|
||||
* BLE_1MBIT[4] 0x0FC
|
||||
*/
|
||||
static const uint32_t ficr_content[64] = {
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000400,
|
||||
0x00000100, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000002, 0x00002000,
|
||||
0x00002000, 0x00002000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000003,
|
||||
0x12345678, 0x9ABCDEF1, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
static uint64_t ficr_read(void *opaque, hwaddr offset, unsigned int size)
|
||||
{
|
||||
assert(offset < sizeof(ficr_content));
|
||||
return ficr_content[offset / 4];
|
||||
}
|
||||
|
||||
static void ficr_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned int size)
|
||||
{
|
||||
/* Intentionally do nothing */
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ficr_ops = {
|
||||
.read = ficr_read,
|
||||
.write = ficr_write,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN
|
||||
};
|
||||
|
||||
/*
|
||||
* UICR Registers Assignments
|
||||
* CLENR0 0x000
|
||||
* RBPCONF 0x004
|
||||
* XTALFREQ 0x008
|
||||
* FWID 0x010
|
||||
* BOOTLOADERADDR 0x014
|
||||
* NRFFW[0] 0x014
|
||||
* NRFFW[1] 0x018
|
||||
* NRFFW[2] 0x01C
|
||||
* NRFFW[3] 0x020
|
||||
* NRFFW[4] 0x024
|
||||
* NRFFW[5] 0x028
|
||||
* NRFFW[6] 0x02C
|
||||
* NRFFW[7] 0x030
|
||||
* NRFFW[8] 0x034
|
||||
* NRFFW[9] 0x038
|
||||
* NRFFW[10] 0x03C
|
||||
* NRFFW[11] 0x040
|
||||
* NRFFW[12] 0x044
|
||||
* NRFFW[13] 0x048
|
||||
* NRFFW[14] 0x04C
|
||||
* NRFHW[0] 0x050
|
||||
* NRFHW[1] 0x054
|
||||
* NRFHW[2] 0x058
|
||||
* NRFHW[3] 0x05C
|
||||
* NRFHW[4] 0x060
|
||||
* NRFHW[5] 0x064
|
||||
* NRFHW[6] 0x068
|
||||
* NRFHW[7] 0x06C
|
||||
* NRFHW[8] 0x070
|
||||
* NRFHW[9] 0x074
|
||||
* NRFHW[10] 0x078
|
||||
* NRFHW[11] 0x07C
|
||||
* CUSTOMER[0] 0x080
|
||||
* CUSTOMER[1] 0x084
|
||||
* CUSTOMER[2] 0x088
|
||||
* CUSTOMER[3] 0x08C
|
||||
* CUSTOMER[4] 0x090
|
||||
* CUSTOMER[5] 0x094
|
||||
* CUSTOMER[6] 0x098
|
||||
* CUSTOMER[7] 0x09C
|
||||
* CUSTOMER[8] 0x0A0
|
||||
* CUSTOMER[9] 0x0A4
|
||||
* CUSTOMER[10] 0x0A8
|
||||
* CUSTOMER[11] 0x0AC
|
||||
* CUSTOMER[12] 0x0B0
|
||||
* CUSTOMER[13] 0x0B4
|
||||
* CUSTOMER[14] 0x0B8
|
||||
* CUSTOMER[15] 0x0BC
|
||||
* CUSTOMER[16] 0x0C0
|
||||
* CUSTOMER[17] 0x0C4
|
||||
* CUSTOMER[18] 0x0C8
|
||||
* CUSTOMER[19] 0x0CC
|
||||
* CUSTOMER[20] 0x0D0
|
||||
* CUSTOMER[21] 0x0D4
|
||||
* CUSTOMER[22] 0x0D8
|
||||
* CUSTOMER[23] 0x0DC
|
||||
* CUSTOMER[24] 0x0E0
|
||||
* CUSTOMER[25] 0x0E4
|
||||
* CUSTOMER[26] 0x0E8
|
||||
* CUSTOMER[27] 0x0EC
|
||||
* CUSTOMER[28] 0x0F0
|
||||
* CUSTOMER[29] 0x0F4
|
||||
* CUSTOMER[30] 0x0F8
|
||||
* CUSTOMER[31] 0x0FC
|
||||
*/
|
||||
|
||||
static uint64_t uicr_read(void *opaque, hwaddr offset, unsigned int size)
|
||||
{
|
||||
NRF51NVMState *s = NRF51_NVM(opaque);
|
||||
|
||||
assert(offset < sizeof(s->uicr_content));
|
||||
return s->uicr_content[offset / 4];
|
||||
}
|
||||
|
||||
static void uicr_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned int size)
|
||||
{
|
||||
NRF51NVMState *s = NRF51_NVM(opaque);
|
||||
|
||||
assert(offset < sizeof(s->uicr_content));
|
||||
s->uicr_content[offset / 4] = value;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps uicr_ops = {
|
||||
.read = uicr_read,
|
||||
.write = uicr_write,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN
|
||||
};
|
||||
|
||||
|
||||
static uint64_t io_read(void *opaque, hwaddr offset, unsigned int size)
|
||||
{
|
||||
NRF51NVMState *s = NRF51_NVM(opaque);
|
||||
uint64_t r = 0;
|
||||
|
||||
switch (offset) {
|
||||
case NRF51_NVMC_READY:
|
||||
r = NRF51_NVMC_READY_READY;
|
||||
break;
|
||||
case NRF51_NVMC_CONFIG:
|
||||
r = s->config;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: bad read offset 0x%" HWADDR_PRIx "\n", __func__, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void io_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned int size)
|
||||
{
|
||||
NRF51NVMState *s = NRF51_NVM(opaque);
|
||||
|
||||
switch (offset) {
|
||||
case NRF51_NVMC_CONFIG:
|
||||
s->config = value & NRF51_NVMC_CONFIG_MASK;
|
||||
break;
|
||||
case NRF51_NVMC_ERASEPCR0:
|
||||
case NRF51_NVMC_ERASEPCR1:
|
||||
if (s->config & NRF51_NVMC_CONFIG_EEN) {
|
||||
/* Mask in-page sub address */
|
||||
value &= ~(NRF51_PAGE_SIZE - 1);
|
||||
if (value <= (s->flash_size - NRF51_PAGE_SIZE)) {
|
||||
memset(s->storage + value, 0xFF, NRF51_PAGE_SIZE);
|
||||
memory_region_flush_rom_device(&s->flash, value,
|
||||
NRF51_PAGE_SIZE);
|
||||
}
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Flash erase at 0x%" HWADDR_PRIx" while flash not erasable.\n",
|
||||
__func__, offset);
|
||||
}
|
||||
break;
|
||||
case NRF51_NVMC_ERASEALL:
|
||||
if (value == NRF51_NVMC_ERASE) {
|
||||
if (s->config & NRF51_NVMC_CONFIG_EEN) {
|
||||
memset(s->storage, 0xFF, s->flash_size);
|
||||
memory_region_flush_rom_device(&s->flash, 0, s->flash_size);
|
||||
memset(s->uicr_content, 0xFF, sizeof(s->uicr_content));
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash not erasable.\n",
|
||||
__func__);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NRF51_NVMC_ERASEUICR:
|
||||
if (value == NRF51_NVMC_ERASE) {
|
||||
memset(s->uicr_content, 0xFF, sizeof(s->uicr_content));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: bad write offset 0x%" HWADDR_PRIx "\n", __func__, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps io_ops = {
|
||||
.read = io_read,
|
||||
.write = io_write,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
|
||||
static void flash_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned int size)
|
||||
{
|
||||
NRF51NVMState *s = NRF51_NVM(opaque);
|
||||
|
||||
if (s->config & NRF51_NVMC_CONFIG_WEN) {
|
||||
uint32_t oldval;
|
||||
|
||||
assert(offset + size <= s->flash_size);
|
||||
|
||||
/* NOR Flash only allows bits to be flipped from 1's to 0's on write */
|
||||
oldval = ldl_le_p(s->storage + offset);
|
||||
oldval &= value;
|
||||
stl_le_p(s->storage + offset, oldval);
|
||||
|
||||
memory_region_flush_rom_device(&s->flash, offset, size);
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Flash write 0x%" HWADDR_PRIx" while flash not writable.\n",
|
||||
__func__, offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const MemoryRegionOps flash_ops = {
|
||||
.write = flash_write,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void nrf51_nvm_init(Object *obj)
|
||||
{
|
||||
NRF51NVMState *s = NRF51_NVM(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &io_ops, s, "nrf51_soc.nvmc",
|
||||
NRF51_NVMC_SIZE);
|
||||
sysbus_init_mmio(sbd, &s->mmio);
|
||||
|
||||
memory_region_init_io(&s->ficr, obj, &ficr_ops, s, "nrf51_soc.ficr",
|
||||
sizeof(ficr_content));
|
||||
sysbus_init_mmio(sbd, &s->ficr);
|
||||
|
||||
memory_region_init_io(&s->uicr, obj, &uicr_ops, s, "nrf51_soc.uicr",
|
||||
sizeof(s->uicr_content));
|
||||
sysbus_init_mmio(sbd, &s->uicr);
|
||||
}
|
||||
|
||||
static void nrf51_nvm_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
NRF51NVMState *s = NRF51_NVM(dev);
|
||||
Error *err = NULL;
|
||||
|
||||
memory_region_init_rom_device(&s->flash, OBJECT(dev), &flash_ops, s,
|
||||
"nrf51_soc.flash", s->flash_size, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
s->storage = memory_region_get_ram_ptr(&s->flash);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->flash);
|
||||
}
|
||||
|
||||
static void nrf51_nvm_reset(DeviceState *dev)
|
||||
{
|
||||
NRF51NVMState *s = NRF51_NVM(dev);
|
||||
|
||||
s->config = 0x00;
|
||||
memset(s->uicr_content, 0xFF, sizeof(s->uicr_content));
|
||||
}
|
||||
|
||||
static Property nrf51_nvm_properties[] = {
|
||||
DEFINE_PROP_UINT32("flash-size", NRF51NVMState, flash_size, 0x40000),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_nvm = {
|
||||
.name = "nrf51_soc.nvm",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(uicr_content, NRF51NVMState,
|
||||
NRF51_UICR_FIXTURE_SIZE),
|
||||
VMSTATE_UINT32(config, NRF51NVMState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void nrf51_nvm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->props = nrf51_nvm_properties;
|
||||
dc->vmsd = &vmstate_nvm;
|
||||
dc->realize = nrf51_nvm_realize;
|
||||
dc->reset = nrf51_nvm_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo nrf51_nvm_info = {
|
||||
.name = TYPE_NRF51_NVM,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(NRF51NVMState),
|
||||
.instance_init = nrf51_nvm_init,
|
||||
.class_init = nrf51_nvm_class_init
|
||||
};
|
||||
|
||||
static void nrf51_nvm_register_types(void)
|
||||
{
|
||||
type_register_static(&nrf51_nvm_info);
|
||||
}
|
||||
|
||||
type_init(nrf51_nvm_register_types)
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* ARM IoT Kit
|
||||
* ARM SSE (Subsystems for Embedded): IoTKit, SSE-200
|
||||
*
|
||||
* Copyright (c) 2018 Linaro Limited
|
||||
* Written by Peter Maydell
|
||||
@ -9,9 +9,16 @@
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
/* This is a model of the Arm IoT Kit which is documented in
|
||||
/*
|
||||
* This is a model of the Arm "Subsystems for Embedded" family of
|
||||
* hardware, which include the IoT Kit and the SSE-050, SSE-100 and
|
||||
* SSE-200. Currently we model:
|
||||
* - the Arm IoT Kit which is documented in
|
||||
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
|
||||
* It contains:
|
||||
* - the SSE-200 which is documented in
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
|
||||
*
|
||||
* The IoTKit contains:
|
||||
* a Cortex-M33
|
||||
* the IDAU
|
||||
* some timers and watchdogs
|
||||
@ -20,14 +27,29 @@
|
||||
* a security controller
|
||||
* a bus fabric which arranges that some parts of the address
|
||||
* space are secure and non-secure aliases of each other
|
||||
* The SSE-200 additionally contains:
|
||||
* a second Cortex-M33
|
||||
* two Message Handling Units (MHUs)
|
||||
* an optional CryptoCell (which we do not model)
|
||||
* more SRAM banks with associated MPCs
|
||||
* multiple Power Policy Units (PPUs)
|
||||
* a control interface for an icache for each CPU
|
||||
* per-CPU identity and control register blocks
|
||||
*
|
||||
* QEMU interface:
|
||||
* + QOM property "memory" is a MemoryRegion containing the devices provided
|
||||
* by the board model.
|
||||
* + QOM property "MAINCLK" is the frequency of the main system clock
|
||||
* + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts
|
||||
* + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which
|
||||
* are wired to the NVIC lines 32 .. n+32
|
||||
* + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts.
|
||||
* (In hardware, the SSE-200 permits the number of expansion interrupts
|
||||
* for the two CPUs to be configured separately, but we restrict it to
|
||||
* being the same for both, to avoid having to have separate Property
|
||||
* lists for different variants. This restriction can be relaxed later
|
||||
* if necessary.)
|
||||
* + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts for CPU 0,
|
||||
* which are wired to its NVIC lines 32 .. n+32
|
||||
* + Named GPIO inputs "EXP_CPU1_IRQ" 0..n are the expansion interrupts for
|
||||
* CPU 1, which are wired to its NVIC lines 32 .. n+32
|
||||
* + sysbus MMIO region 0 is the "AHB Slave Expansion" which allows
|
||||
* bus master devices in the board model to make transactions into
|
||||
* all the devices and memory areas in the IoTKit
|
||||
@ -55,8 +77,8 @@
|
||||
* + named GPIO outputs mscexp_ns[0..15]
|
||||
*/
|
||||
|
||||
#ifndef IOTKIT_H
|
||||
#define IOTKIT_H
|
||||
#ifndef ARMSSE_H
|
||||
#define ARMSSE_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/arm/armv7m.h"
|
||||
@ -68,11 +90,22 @@
|
||||
#include "hw/watchdog/cmsdk-apb-watchdog.h"
|
||||
#include "hw/misc/iotkit-sysctl.h"
|
||||
#include "hw/misc/iotkit-sysinfo.h"
|
||||
#include "hw/misc/armsse-cpuid.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/or-irq.h"
|
||||
#include "hw/core/split-irq.h"
|
||||
#include "hw/cpu/cluster.h"
|
||||
|
||||
#define TYPE_ARMSSE "arm-sse"
|
||||
#define ARMSSE(obj) OBJECT_CHECK(ARMSSE, (obj), TYPE_ARMSSE)
|
||||
|
||||
/*
|
||||
* These type names are for specific IoTKit subsystems; other than
|
||||
* instantiating them, code using these devices should always handle
|
||||
* them via the ARMSSE base class, so they have no IOTKIT() etc macros.
|
||||
*/
|
||||
#define TYPE_IOTKIT "iotkit"
|
||||
#define IOTKIT(obj) OBJECT_CHECK(IoTKit, (obj), TYPE_IOTKIT)
|
||||
#define TYPE_SSE200 "sse-200"
|
||||
|
||||
/* We have an IRQ splitter and an OR gate input for each external PPC
|
||||
* and the 2 internal PPCs
|
||||
@ -80,16 +113,34 @@
|
||||
#define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC)
|
||||
#define NUM_PPCS (NUM_EXTERNAL_PPCS + 2)
|
||||
|
||||
typedef struct IoTKit {
|
||||
#define MAX_SRAM_BANKS 4
|
||||
#if MAX_SRAM_BANKS > IOTS_NUM_MPC
|
||||
#error Too many SRAM banks
|
||||
#endif
|
||||
|
||||
#define SSE_MAX_CPUS 2
|
||||
|
||||
/* These define what each PPU in the ppu[] index is for */
|
||||
#define CPU0CORE_PPU 0
|
||||
#define CPU1CORE_PPU 1
|
||||
#define DBG_PPU 2
|
||||
#define RAM0_PPU 3
|
||||
#define RAM1_PPU 4
|
||||
#define RAM2_PPU 5
|
||||
#define RAM3_PPU 6
|
||||
#define NUM_PPUS 7
|
||||
|
||||
typedef struct ARMSSE {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
ARMv7MState armv7m;
|
||||
ARMv7MState armv7m[SSE_MAX_CPUS];
|
||||
CPUClusterState cluster[SSE_MAX_CPUS];
|
||||
IoTKitSecCtl secctl;
|
||||
TZPPC apb_ppc0;
|
||||
TZPPC apb_ppc1;
|
||||
TZMPC mpc;
|
||||
TZMPC mpc[IOTS_NUM_MPC];
|
||||
CMSDKAPBTIMER timer0;
|
||||
CMSDKAPBTIMER timer1;
|
||||
CMSDKAPBTIMER s32ktimer;
|
||||
@ -100,6 +151,8 @@ typedef struct IoTKit {
|
||||
qemu_or_irq mpc_irq_orgate;
|
||||
qemu_or_irq nmi_orgate;
|
||||
|
||||
SplitIRQ cpu_irq_splitter[32];
|
||||
|
||||
CMSDKAPBDualTimer dualtimer;
|
||||
|
||||
CMSDKAPBWatchdog s32kwatchdog;
|
||||
@ -109,13 +162,30 @@ typedef struct IoTKit {
|
||||
IoTKitSysCtl sysctl;
|
||||
IoTKitSysCtl sysinfo;
|
||||
|
||||
UnimplementedDeviceState mhu[2];
|
||||
UnimplementedDeviceState ppu[NUM_PPUS];
|
||||
UnimplementedDeviceState cachectrl[SSE_MAX_CPUS];
|
||||
UnimplementedDeviceState cpusecctrl[SSE_MAX_CPUS];
|
||||
|
||||
ARMSSECPUID cpuid[SSE_MAX_CPUS];
|
||||
|
||||
/*
|
||||
* 'container' holds all devices seen by all CPUs.
|
||||
* 'cpu_container[i]' is the view that CPU i has: this has the
|
||||
* per-CPU devices of that CPU, plus as the background 'container'
|
||||
* (or an alias of it, since we can only use it directly once).
|
||||
* container_alias[i] is the alias of 'container' used by CPU i+1;
|
||||
* CPU 0 can use 'container' directly.
|
||||
*/
|
||||
MemoryRegion container;
|
||||
MemoryRegion container_alias[SSE_MAX_CPUS - 1];
|
||||
MemoryRegion cpu_container[SSE_MAX_CPUS];
|
||||
MemoryRegion alias1;
|
||||
MemoryRegion alias2;
|
||||
MemoryRegion alias3;
|
||||
MemoryRegion sram0;
|
||||
MemoryRegion sram[MAX_SRAM_BANKS];
|
||||
|
||||
qemu_irq *exp_irqs;
|
||||
qemu_irq *exp_irqs[SSE_MAX_CPUS];
|
||||
qemu_irq ppc0_irq;
|
||||
qemu_irq ppc1_irq;
|
||||
qemu_irq sec_resp_cfg;
|
||||
@ -131,6 +201,19 @@ typedef struct IoTKit {
|
||||
MemoryRegion *board_memory;
|
||||
uint32_t exp_numirq;
|
||||
uint32_t mainclk_frq;
|
||||
} IoTKit;
|
||||
uint32_t sram_addr_width;
|
||||
} ARMSSE;
|
||||
|
||||
typedef struct ARMSSEInfo ARMSSEInfo;
|
||||
|
||||
typedef struct ARMSSEClass {
|
||||
DeviceClass parent_class;
|
||||
const ARMSSEInfo *info;
|
||||
} ARMSSEClass;
|
||||
|
||||
#define ARMSSE_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(ARMSSEClass, (klass), TYPE_ARMSSE)
|
||||
#define ARMSSE_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(ARMSSEClass, (obj), TYPE_ARMSSE)
|
||||
|
||||
#endif
|
@ -65,6 +65,7 @@ typedef struct ARMv7MState {
|
||||
Object *idau;
|
||||
uint32_t init_svtor;
|
||||
bool enable_bitband;
|
||||
bool start_powered_off;
|
||||
} ARMv7MState;
|
||||
|
||||
#endif
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "hw/char/nrf51_uart.h"
|
||||
#include "hw/misc/nrf51_rng.h"
|
||||
#include "hw/gpio/nrf51_gpio.h"
|
||||
#include "hw/nvram/nrf51_nvm.h"
|
||||
#include "hw/timer/nrf51_timer.h"
|
||||
|
||||
#define TYPE_NRF51_SOC "nrf51-soc"
|
||||
@ -32,6 +33,7 @@ typedef struct NRF51State {
|
||||
|
||||
NRF51UARTState uart;
|
||||
NRF51RNGState rng;
|
||||
NRF51NVMState nvm;
|
||||
NRF51GPIOState gpio;
|
||||
NRF51TimerState timer[NRF51_NUM_TIMERS];
|
||||
|
||||
|
41
include/hw/misc/armsse-cpuid.h
Normal file
41
include/hw/misc/armsse-cpuid.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* ARM SSE-200 CPU_IDENTITY register block
|
||||
*
|
||||
* Copyright (c) 2019 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 "CPU_IDENTITY" register block which is part of the
|
||||
* Arm SSE-200 and documented in
|
||||
* http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
|
||||
*
|
||||
* QEMU interface:
|
||||
* + QOM property "CPUID": the value to use for the CPUID register
|
||||
* + sysbus MMIO region 0: the system information register bank
|
||||
*/
|
||||
|
||||
#ifndef HW_MISC_ARMSSE_CPUID_H
|
||||
#define HW_MISC_ARMSSE_CPUID_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_ARMSSE_CPUID "armsse-cpuid"
|
||||
#define ARMSSE_CPUID(obj) OBJECT_CHECK(ARMSSECPUID, (obj), TYPE_ARMSSE_CPUID)
|
||||
|
||||
typedef struct ARMSSECPUID {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion iomem;
|
||||
|
||||
/* Properties */
|
||||
uint32_t cpuid;
|
||||
} ARMSSECPUID;
|
||||
|
||||
#endif
|
@ -40,8 +40,8 @@
|
||||
* + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable
|
||||
* + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear
|
||||
* + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status
|
||||
* Controlling the MPC in the IoTKit:
|
||||
* + named GPIO input mpc_status
|
||||
* Controlling the (up to) 4 MPCs in the IoTKit/SSE:
|
||||
* + named GPIO inputs mpc_status[0..3]
|
||||
* Controlling each of the 16 expansion MPCs which a system using the IoTKit
|
||||
* might provide:
|
||||
* + named GPIO inputs mpcexp_status[0..15]
|
||||
@ -67,7 +67,7 @@
|
||||
#define IOTS_NUM_APB_EXP_PPC 4
|
||||
#define IOTS_NUM_AHB_EXP_PPC 4
|
||||
#define IOTS_NUM_EXP_MPC 16
|
||||
#define IOTS_NUM_MPC 1
|
||||
#define IOTS_NUM_MPC 4
|
||||
#define IOTS_NUM_EXP_MSC 16
|
||||
|
||||
typedef struct IoTKitSecCtl IoTKitSecCtl;
|
||||
|
@ -14,6 +14,8 @@
|
||||
* Arm IoTKit and documented in
|
||||
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
|
||||
* QEMU interface:
|
||||
* + QOM property "SYS_VERSION": value to use for SYS_VERSION register
|
||||
* + QOM property "SYS_CONFIG": value to use for SYS_CONFIG register
|
||||
* + sysbus MMIO region 0: the system information register bank
|
||||
*/
|
||||
|
||||
@ -32,6 +34,10 @@ typedef struct IoTKitSysInfo {
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion iomem;
|
||||
|
||||
/* Properties */
|
||||
uint32_t sys_version;
|
||||
uint32_t sys_config;
|
||||
} IoTKitSysInfo;
|
||||
|
||||
#endif
|
||||
|
64
include/hw/nvram/nrf51_nvm.h
Normal file
64
include/hw/nvram/nrf51_nvm.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Nordic Semiconductor nRF51 non-volatile memory
|
||||
*
|
||||
* It provides an interface to erase regions in flash memory.
|
||||
* Furthermore it provides the user and factory information registers.
|
||||
*
|
||||
* QEMU interface:
|
||||
* + sysbus MMIO regions 0: NVMC peripheral registers
|
||||
* + sysbus MMIO regions 1: FICR peripheral registers
|
||||
* + sysbus MMIO regions 2: UICR peripheral registers
|
||||
* + flash-size property: flash size in bytes.
|
||||
*
|
||||
* Accuracy of the peripheral model:
|
||||
* + Code regions (MPU configuration) are disregarded.
|
||||
*
|
||||
* Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#ifndef NRF51_NVM_H
|
||||
#define NRF51_NVM_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#define TYPE_NRF51_NVM "nrf51_soc.nvm"
|
||||
#define NRF51_NVM(obj) OBJECT_CHECK(NRF51NVMState, (obj), TYPE_NRF51_NVM)
|
||||
|
||||
#define NRF51_UICR_FIXTURE_SIZE 64
|
||||
|
||||
#define NRF51_NVMC_SIZE 0x1000
|
||||
|
||||
#define NRF51_NVMC_READY 0x400
|
||||
#define NRF51_NVMC_READY_READY 0x01
|
||||
#define NRF51_NVMC_CONFIG 0x504
|
||||
#define NRF51_NVMC_CONFIG_MASK 0x03
|
||||
#define NRF51_NVMC_CONFIG_WEN 0x01
|
||||
#define NRF51_NVMC_CONFIG_EEN 0x02
|
||||
#define NRF51_NVMC_ERASEPCR1 0x508
|
||||
#define NRF51_NVMC_ERASEPCR0 0x510
|
||||
#define NRF51_NVMC_ERASEALL 0x50C
|
||||
#define NRF51_NVMC_ERASEUICR 0x514
|
||||
#define NRF51_NVMC_ERASE 0x01
|
||||
|
||||
#define NRF51_UICR_SIZE 0x100
|
||||
|
||||
typedef struct NRF51NVMState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion mmio;
|
||||
MemoryRegion ficr;
|
||||
MemoryRegion uicr;
|
||||
MemoryRegion flash;
|
||||
|
||||
uint32_t uicr_content[NRF51_UICR_FIXTURE_SIZE];
|
||||
uint32_t flash_size;
|
||||
uint8_t *storage;
|
||||
|
||||
uint32_t config;
|
||||
|
||||
} NRF51NVMState;
|
||||
|
||||
|
||||
#endif
|
@ -103,9 +103,21 @@ struct TranslationBlock;
|
||||
* @get_arch_id: Callback for getting architecture-dependent CPU ID.
|
||||
* @get_paging_enabled: Callback for inquiring whether paging is enabled.
|
||||
* @get_memory_mapping: Callback for obtaining the memory mappings.
|
||||
* @set_pc: Callback for setting the Program Counter register.
|
||||
* @set_pc: Callback for setting the Program Counter register. This
|
||||
* should have the semantics used by the target architecture when
|
||||
* setting the PC from a source such as an ELF file entry point;
|
||||
* for example on Arm it will also set the Thumb mode bit based
|
||||
* on the least significant bit of the new PC value.
|
||||
* If the target behaviour here is anything other than "set
|
||||
* the PC register to the value passed in" then the target must
|
||||
* also implement the synchronize_from_tb hook.
|
||||
* @synchronize_from_tb: Callback for synchronizing state from a TCG
|
||||
* #TranslationBlock.
|
||||
* #TranslationBlock. This is called when we abandon execution
|
||||
* of a TB before starting it, and must set all parts of the CPU
|
||||
* state which the previous TB in the chain may not have updated.
|
||||
* This always includes at least the program counter; some targets
|
||||
* will need to do more. If this hook is not implemented then the
|
||||
* default is to call @set_pc(tb->pc).
|
||||
* @handle_mmu_fault: Callback for handling an MMU fault.
|
||||
* @get_phys_page_debug: Callback for obtaining a physical address.
|
||||
* @get_phys_page_attrs_debug: Callback for obtaining a physical address and the
|
||||
|
@ -147,10 +147,29 @@ void cpu_loop(CPUARMState *env)
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t arm_rand64(void)
|
||||
{
|
||||
int shift = 64 - clz64(RAND_MAX);
|
||||
int i, n = 64 / shift + (64 % shift != 0);
|
||||
uint64_t ret = 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
ret = (ret << shift) | rand();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void arm_init_pauth_key(ARMPACKey *key)
|
||||
{
|
||||
key->lo = arm_rand64();
|
||||
key->hi = arm_rand64();
|
||||
}
|
||||
|
||||
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
TaskState *ts = cpu->opaque;
|
||||
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||
CPUState *cs = CPU(cpu);
|
||||
TaskState *ts = cs->opaque;
|
||||
struct image_info *info = ts->info;
|
||||
int i;
|
||||
|
||||
@ -172,6 +191,14 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cpu_isar_feature(aa64_pauth, cpu)) {
|
||||
arm_init_pauth_key(&env->apia_key);
|
||||
arm_init_pauth_key(&env->apib_key);
|
||||
arm_init_pauth_key(&env->apda_key);
|
||||
arm_init_pauth_key(&env->apdb_key);
|
||||
arm_init_pauth_key(&env->apga_key);
|
||||
}
|
||||
|
||||
ts->stack_base = info->start_stack;
|
||||
ts->heap_base = info->brk;
|
||||
/* This will be filled in on the first SYS_HEAPINFO call. */
|
||||
|
@ -22,4 +22,6 @@ struct target_pt_regs {
|
||||
#define TARGET_PR_SVE_SET_VL 50
|
||||
#define TARGET_PR_SVE_GET_VL 51
|
||||
|
||||
void arm_init_pauth_key(ARMPACKey *key);
|
||||
|
||||
#endif /* AARCH64_TARGET_SYSCALL_H */
|
||||
|
@ -560,6 +560,15 @@ enum {
|
||||
ARM_HWCAP_A64_ASIMDDP = 1 << 20,
|
||||
ARM_HWCAP_A64_SHA512 = 1 << 21,
|
||||
ARM_HWCAP_A64_SVE = 1 << 22,
|
||||
ARM_HWCAP_A64_ASIMDFHM = 1 << 23,
|
||||
ARM_HWCAP_A64_DIT = 1 << 24,
|
||||
ARM_HWCAP_A64_USCAT = 1 << 25,
|
||||
ARM_HWCAP_A64_ILRCPC = 1 << 26,
|
||||
ARM_HWCAP_A64_FLAGM = 1 << 27,
|
||||
ARM_HWCAP_A64_SSBS = 1 << 28,
|
||||
ARM_HWCAP_A64_SB = 1 << 29,
|
||||
ARM_HWCAP_A64_PACA = 1 << 30,
|
||||
ARM_HWCAP_A64_PACG = 1UL << 31,
|
||||
};
|
||||
|
||||
#define ELF_HWCAP get_elf_hwcap()
|
||||
@ -591,6 +600,7 @@ static uint32_t get_elf_hwcap(void)
|
||||
GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP);
|
||||
GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA);
|
||||
GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE);
|
||||
GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG);
|
||||
|
||||
#undef GET_FEATURE_ID
|
||||
|
||||
|
@ -120,11 +120,8 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
|
||||
|
||||
if (info->target_aa64) {
|
||||
target_cpu->env.xregs[0] = info->context_id;
|
||||
target_cpu->env.thumb = false;
|
||||
} else {
|
||||
target_cpu->env.regs[0] = info->context_id;
|
||||
target_cpu->env.thumb = info->entry & 1;
|
||||
info->entry &= 0xfffffffe;
|
||||
}
|
||||
|
||||
/* Start the new CPU at the requested address */
|
||||
|
@ -40,8 +40,31 @@
|
||||
static void arm_cpu_set_pc(CPUState *cs, vaddr value)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
cpu->env.regs[15] = value;
|
||||
if (is_a64(env)) {
|
||||
env->pc = value;
|
||||
env->thumb = 0;
|
||||
} else {
|
||||
env->regs[15] = value & ~1;
|
||||
env->thumb = value & 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void arm_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
/*
|
||||
* It's OK to look at env for the current mode here, because it's
|
||||
* never possible for an AArch64 TB to chain to an AArch32 TB.
|
||||
*/
|
||||
if (is_a64(env)) {
|
||||
env->pc = tb->pc;
|
||||
} else {
|
||||
env->regs[15] = tb->pc;
|
||||
}
|
||||
}
|
||||
|
||||
static bool arm_cpu_has_work(CPUState *cs)
|
||||
@ -162,6 +185,9 @@ static void arm_cpu_reset(CPUState *s)
|
||||
env->pstate = PSTATE_MODE_EL0t;
|
||||
/* Userspace expects access to DC ZVA, CTL_EL0 and the cache ops */
|
||||
env->cp15.sctlr_el[1] |= SCTLR_UCT | SCTLR_UCI | SCTLR_DZE;
|
||||
/* Enable all PAC keys. */
|
||||
env->cp15.sctlr_el[1] |= (SCTLR_EnIA | SCTLR_EnIB |
|
||||
SCTLR_EnDA | SCTLR_EnDB);
|
||||
/* Enable all PAC instructions */
|
||||
env->cp15.hcr_el2 |= HCR_API;
|
||||
env->cp15.scr_el3 |= SCR_API;
|
||||
@ -836,6 +862,13 @@ static void arm_cpu_finalizefn(Object *obj)
|
||||
QLIST_REMOVE(hook, node);
|
||||
g_free(hook);
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (cpu->pmu_timer) {
|
||||
timer_del(cpu->pmu_timer);
|
||||
timer_deinit(cpu->pmu_timer);
|
||||
timer_free(cpu->pmu_timer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
@ -1045,6 +1078,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
arm_register_pre_el_change_hook(cpu, &pmu_pre_el_change, 0);
|
||||
arm_register_el_change_hook(cpu, &pmu_post_el_change, 0);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, arm_pmu_timer_cb,
|
||||
cpu);
|
||||
#endif
|
||||
} else {
|
||||
cpu->id_aa64dfr0 &= ~0xf00;
|
||||
cpu->pmceid0 = 0;
|
||||
@ -2087,6 +2125,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
|
||||
cc->cpu_exec_interrupt = arm_cpu_exec_interrupt;
|
||||
cc->dump_state = arm_cpu_dump_state;
|
||||
cc->set_pc = arm_cpu_set_pc;
|
||||
cc->synchronize_from_tb = arm_cpu_synchronize_from_tb;
|
||||
cc->gdb_read_register = arm_cpu_gdb_read_register;
|
||||
cc->gdb_write_register = arm_cpu_gdb_write_register;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
|
@ -746,6 +746,11 @@ struct ARMCPU {
|
||||
|
||||
/* Timers used by the generic (architected) timer */
|
||||
QEMUTimer *gt_timer[NUM_GTIMERS];
|
||||
/*
|
||||
* Timer used by the PMU. Its state is restored after migration by
|
||||
* pmu_op_finish() - it does not need other handling during migration
|
||||
*/
|
||||
QEMUTimer *pmu_timer;
|
||||
/* GPIO outputs for generic timer */
|
||||
qemu_irq gt_timer_outputs[NUM_GTIMERS];
|
||||
/* GPIO output for GICv3 maintenance interrupt signal */
|
||||
@ -1005,6 +1010,11 @@ void pmccntr_op_finish(CPUARMState *env);
|
||||
void pmu_op_start(CPUARMState *env);
|
||||
void pmu_op_finish(CPUARMState *env);
|
||||
|
||||
/*
|
||||
* Called when a PMU counter is due to overflow
|
||||
*/
|
||||
void arm_pmu_timer_cb(void *opaque);
|
||||
|
||||
/**
|
||||
* Functions to register as EL change hooks for PMU mode filtering
|
||||
*/
|
||||
@ -2502,7 +2512,7 @@ bool write_cpustate_to_list(ARMCPU *cpu);
|
||||
|
||||
#if defined(TARGET_AARCH64)
|
||||
# define TARGET_PHYS_ADDR_SPACE_BITS 48
|
||||
# define TARGET_VIRT_ADDR_SPACE_BITS 64
|
||||
# define TARGET_VIRT_ADDR_SPACE_BITS 48
|
||||
#else
|
||||
# define TARGET_PHYS_ADDR_SPACE_BITS 40
|
||||
# define TARGET_VIRT_ADDR_SPACE_BITS 32
|
||||
|
@ -281,38 +281,6 @@ static void cpu_max_set_sve_vq(Object *obj, Visitor *v, const char *name,
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static void cpu_max_get_packey(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
const uint64_t *bit = opaque;
|
||||
bool enabled = (cpu->env.cp15.sctlr_el[1] & *bit) != 0;
|
||||
|
||||
visit_type_bool(v, name, &enabled, errp);
|
||||
}
|
||||
|
||||
static void cpu_max_set_packey(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
Error *err = NULL;
|
||||
const uint64_t *bit = opaque;
|
||||
bool enabled;
|
||||
|
||||
visit_type_bool(v, name, &enabled, errp);
|
||||
|
||||
if (!err) {
|
||||
if (enabled) {
|
||||
cpu->env.cp15.sctlr_el[1] |= *bit;
|
||||
} else {
|
||||
cpu->env.cp15.sctlr_el[1] &= ~*bit;
|
||||
}
|
||||
}
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
|
||||
* otherwise, a CPU with as many features enabled as our emulation supports.
|
||||
* The version of '-cpu max' for qemu-system-arm is defined in cpu.c;
|
||||
@ -388,34 +356,6 @@ static void aarch64_max_initfn(Object *obj)
|
||||
*/
|
||||
cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
|
||||
cpu->dcz_blocksize = 7; /* 512 bytes */
|
||||
|
||||
/*
|
||||
* Note that Linux will enable enable all of the keys at once.
|
||||
* But doing it this way will allow experimentation beyond that.
|
||||
*/
|
||||
{
|
||||
static const uint64_t apia_bit = SCTLR_EnIA;
|
||||
static const uint64_t apib_bit = SCTLR_EnIB;
|
||||
static const uint64_t apda_bit = SCTLR_EnDA;
|
||||
static const uint64_t apdb_bit = SCTLR_EnDB;
|
||||
|
||||
object_property_add(obj, "apia", "bool", cpu_max_get_packey,
|
||||
cpu_max_set_packey, NULL,
|
||||
(void *)&apia_bit, &error_fatal);
|
||||
object_property_add(obj, "apib", "bool", cpu_max_get_packey,
|
||||
cpu_max_set_packey, NULL,
|
||||
(void *)&apib_bit, &error_fatal);
|
||||
object_property_add(obj, "apda", "bool", cpu_max_get_packey,
|
||||
cpu_max_set_packey, NULL,
|
||||
(void *)&apda_bit, &error_fatal);
|
||||
object_property_add(obj, "apdb", "bool", cpu_max_get_packey,
|
||||
cpu_max_set_packey, NULL,
|
||||
(void *)&apdb_bit, &error_fatal);
|
||||
|
||||
/* Enable all PAC keys by default. */
|
||||
cpu->env.cp15.sctlr_el[1] |= SCTLR_EnIA | SCTLR_EnIB;
|
||||
cpu->env.cp15.sctlr_el[1] |= SCTLR_EnDA | SCTLR_EnDB;
|
||||
}
|
||||
#endif
|
||||
|
||||
cpu->sve_max_vq = ARM_MAX_VQ;
|
||||
@ -480,20 +420,6 @@ static void aarch64_cpu_finalizefn(Object *obj)
|
||||
{
|
||||
}
|
||||
|
||||
static void aarch64_cpu_set_pc(CPUState *cs, vaddr value)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
/* It's OK to look at env for the current mode here, because it's
|
||||
* never possible for an AArch64 TB to chain to an AArch32 TB.
|
||||
* (Otherwise we would need to use synchronize_from_tb instead.)
|
||||
*/
|
||||
if (is_a64(&cpu->env)) {
|
||||
cpu->env.pc = value;
|
||||
} else {
|
||||
cpu->env.regs[15] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static gchar *aarch64_gdb_arch_name(CPUState *cs)
|
||||
{
|
||||
return g_strdup("aarch64");
|
||||
@ -504,7 +430,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
|
||||
cc->cpu_exec_interrupt = arm_cpu_exec_interrupt;
|
||||
cc->set_pc = aarch64_cpu_set_pc;
|
||||
cc->gdb_read_register = aarch64_cpu_gdb_read_register;
|
||||
cc->gdb_write_register = aarch64_cpu_gdb_write_register;
|
||||
cc->gdb_num_core_regs = 34;
|
||||
|
@ -977,6 +977,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
|
||||
/* Definitions for the PMU registers */
|
||||
#define PMCRN_MASK 0xf800
|
||||
#define PMCRN_SHIFT 11
|
||||
#define PMCRLC 0x40
|
||||
#define PMCRDP 0x10
|
||||
#define PMCRD 0x8
|
||||
#define PMCRC 0x4
|
||||
@ -1020,6 +1021,13 @@ typedef struct pm_event {
|
||||
* counters hold a difference from the return value from this function
|
||||
*/
|
||||
uint64_t (*get_count)(CPUARMState *);
|
||||
/*
|
||||
* Return how many nanoseconds it will take (at a minimum) for count events
|
||||
* to occur. A negative value indicates the counter will never overflow, or
|
||||
* that the counter has otherwise arranged for the overflow bit to be set
|
||||
* and the PMU interrupt to be raised on overflow.
|
||||
*/
|
||||
int64_t (*ns_per_count)(uint64_t);
|
||||
} pm_event;
|
||||
|
||||
static bool event_always_supported(CPUARMState *env)
|
||||
@ -1036,6 +1044,11 @@ static uint64_t swinc_get_count(CPUARMState *env)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int64_t swinc_ns_per(uint64_t ignored)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the underlying cycle count for the PMU cycle counters. If we're in
|
||||
* usermode, simply return 0.
|
||||
@ -1051,6 +1064,11 @@ static uint64_t cycles_get_count(CPUARMState *env)
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static int64_t cycles_ns_per(uint64_t cycles)
|
||||
{
|
||||
return (ARM_CPU_FREQ / NANOSECONDS_PER_SECOND) * cycles;
|
||||
}
|
||||
|
||||
static bool instructions_supported(CPUARMState *env)
|
||||
{
|
||||
return use_icount == 1 /* Precise instruction counting */;
|
||||
@ -1060,21 +1078,29 @@ static uint64_t instructions_get_count(CPUARMState *env)
|
||||
{
|
||||
return (uint64_t)cpu_get_icount_raw();
|
||||
}
|
||||
|
||||
static int64_t instructions_ns_per(uint64_t icount)
|
||||
{
|
||||
return cpu_icount_to_ns((int64_t)icount);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const pm_event pm_events[] = {
|
||||
{ .number = 0x000, /* SW_INCR */
|
||||
.supported = event_always_supported,
|
||||
.get_count = swinc_get_count,
|
||||
.ns_per_count = swinc_ns_per,
|
||||
},
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
{ .number = 0x008, /* INST_RETIRED, Instruction architecturally executed */
|
||||
.supported = instructions_supported,
|
||||
.get_count = instructions_get_count,
|
||||
.ns_per_count = instructions_ns_per,
|
||||
},
|
||||
{ .number = 0x011, /* CPU_CYCLES, Cycle */
|
||||
.supported = event_always_supported,
|
||||
.get_count = cycles_get_count,
|
||||
.ns_per_count = cycles_ns_per,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
@ -1293,6 +1319,13 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter)
|
||||
return enabled && !prohibited && !filtered;
|
||||
}
|
||||
|
||||
static void pmu_update_irq(CPUARMState *env)
|
||||
{
|
||||
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||
qemu_set_irq(cpu->pmu_interrupt, (env->cp15.c9_pmcr & PMCRE) &&
|
||||
(env->cp15.c9_pminten & env->cp15.c9_pmovsr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure c15_ccnt is the guest-visible count so that operations such as
|
||||
* enabling/disabling the counter or filtering, modifying the count itself,
|
||||
@ -1310,7 +1343,16 @@ void pmccntr_op_start(CPUARMState *env)
|
||||
eff_cycles /= 64;
|
||||
}
|
||||
|
||||
env->cp15.c15_ccnt = eff_cycles - env->cp15.c15_ccnt_delta;
|
||||
uint64_t new_pmccntr = eff_cycles - env->cp15.c15_ccnt_delta;
|
||||
|
||||
uint64_t overflow_mask = env->cp15.c9_pmcr & PMCRLC ? \
|
||||
1ull << 63 : 1ull << 31;
|
||||
if (env->cp15.c15_ccnt & ~new_pmccntr & overflow_mask) {
|
||||
env->cp15.c9_pmovsr |= (1 << 31);
|
||||
pmu_update_irq(env);
|
||||
}
|
||||
|
||||
env->cp15.c15_ccnt = new_pmccntr;
|
||||
}
|
||||
env->cp15.c15_ccnt_delta = cycles;
|
||||
}
|
||||
@ -1323,13 +1365,27 @@ void pmccntr_op_start(CPUARMState *env)
|
||||
void pmccntr_op_finish(CPUARMState *env)
|
||||
{
|
||||
if (pmu_counter_enabled(env, 31)) {
|
||||
uint64_t prev_cycles = env->cp15.c15_ccnt_delta;
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* Calculate when the counter will next overflow */
|
||||
uint64_t remaining_cycles = -env->cp15.c15_ccnt;
|
||||
if (!(env->cp15.c9_pmcr & PMCRLC)) {
|
||||
remaining_cycles = (uint32_t)remaining_cycles;
|
||||
}
|
||||
int64_t overflow_in = cycles_ns_per(remaining_cycles);
|
||||
|
||||
if (overflow_in > 0) {
|
||||
int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
overflow_in;
|
||||
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||
timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t prev_cycles = env->cp15.c15_ccnt_delta;
|
||||
if (env->cp15.c9_pmcr & PMCRD) {
|
||||
/* Increment once every 64 processor clock cycles */
|
||||
prev_cycles /= 64;
|
||||
}
|
||||
|
||||
env->cp15.c15_ccnt_delta = prev_cycles - env->cp15.c15_ccnt;
|
||||
}
|
||||
}
|
||||
@ -1345,8 +1401,13 @@ static void pmevcntr_op_start(CPUARMState *env, uint8_t counter)
|
||||
}
|
||||
|
||||
if (pmu_counter_enabled(env, counter)) {
|
||||
env->cp15.c14_pmevcntr[counter] =
|
||||
count - env->cp15.c14_pmevcntr_delta[counter];
|
||||
uint32_t new_pmevcntr = count - env->cp15.c14_pmevcntr_delta[counter];
|
||||
|
||||
if (env->cp15.c14_pmevcntr[counter] & ~new_pmevcntr & INT32_MIN) {
|
||||
env->cp15.c9_pmovsr |= (1 << counter);
|
||||
pmu_update_irq(env);
|
||||
}
|
||||
env->cp15.c14_pmevcntr[counter] = new_pmevcntr;
|
||||
}
|
||||
env->cp15.c14_pmevcntr_delta[counter] = count;
|
||||
}
|
||||
@ -1354,6 +1415,21 @@ static void pmevcntr_op_start(CPUARMState *env, uint8_t counter)
|
||||
static void pmevcntr_op_finish(CPUARMState *env, uint8_t counter)
|
||||
{
|
||||
if (pmu_counter_enabled(env, counter)) {
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT;
|
||||
uint16_t event_idx = supported_event_map[event];
|
||||
uint64_t delta = UINT32_MAX -
|
||||
(uint32_t)env->cp15.c14_pmevcntr[counter] + 1;
|
||||
int64_t overflow_in = pm_events[event_idx].ns_per_count(delta);
|
||||
|
||||
if (overflow_in > 0) {
|
||||
int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
overflow_in;
|
||||
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||
timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at);
|
||||
}
|
||||
#endif
|
||||
|
||||
env->cp15.c14_pmevcntr_delta[counter] -=
|
||||
env->cp15.c14_pmevcntr[counter];
|
||||
}
|
||||
@ -1387,6 +1463,20 @@ void pmu_post_el_change(ARMCPU *cpu, void *ignored)
|
||||
pmu_op_finish(&cpu->env);
|
||||
}
|
||||
|
||||
void arm_pmu_timer_cb(void *opaque)
|
||||
{
|
||||
ARMCPU *cpu = opaque;
|
||||
|
||||
/*
|
||||
* Update all the counter values based on the current underlying counts,
|
||||
* triggering interrupts to be raised, if necessary. pmu_op_finish() also
|
||||
* has the effect of setting the cpu->pmu_timer to the next earliest time a
|
||||
* counter may expire.
|
||||
*/
|
||||
pmu_op_start(&cpu->env);
|
||||
pmu_op_finish(&cpu->env);
|
||||
}
|
||||
|
||||
static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
@ -1423,7 +1513,20 @@ static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
/* counter is SW_INCR */
|
||||
(env->cp15.c14_pmevtyper[i] & PMXEVTYPER_EVTCOUNT) == 0x0) {
|
||||
pmevcntr_op_start(env, i);
|
||||
env->cp15.c14_pmevcntr[i]++;
|
||||
|
||||
/*
|
||||
* Detect if this write causes an overflow since we can't predict
|
||||
* PMSWINC overflows like we can for other events
|
||||
*/
|
||||
uint32_t new_pmswinc = env->cp15.c14_pmevcntr[i] + 1;
|
||||
|
||||
if (env->cp15.c14_pmevcntr[i] & ~new_pmswinc & INT32_MIN) {
|
||||
env->cp15.c9_pmovsr |= (1 << i);
|
||||
pmu_update_irq(env);
|
||||
}
|
||||
|
||||
env->cp15.c14_pmevcntr[i] = new_pmswinc;
|
||||
|
||||
pmevcntr_op_finish(env, i);
|
||||
}
|
||||
}
|
||||
@ -1508,6 +1611,7 @@ static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
{
|
||||
value &= pmu_counter_mask(env);
|
||||
env->cp15.c9_pmovsr &= ~value;
|
||||
pmu_update_irq(env);
|
||||
}
|
||||
|
||||
static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
@ -1515,6 +1619,7 @@ static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
{
|
||||
value &= pmu_counter_mask(env);
|
||||
env->cp15.c9_pmovsr |= value;
|
||||
pmu_update_irq(env);
|
||||
}
|
||||
|
||||
static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
@ -1701,6 +1806,7 @@ static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
/* We have no event counters so only the C bit can be changed */
|
||||
value &= pmu_counter_mask(env);
|
||||
env->cp15.c9_pminten |= value;
|
||||
pmu_update_irq(env);
|
||||
}
|
||||
|
||||
static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
@ -1708,6 +1814,7 @@ static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
{
|
||||
value &= pmu_counter_mask(env);
|
||||
env->cp15.c9_pminten &= ~value;
|
||||
pmu_update_irq(env);
|
||||
}
|
||||
|
||||
static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
@ -1752,6 +1859,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||
if (cpu_isar_feature(aa64_lor, cpu)) {
|
||||
valid_mask |= SCR_TLOR;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_pauth, cpu)) {
|
||||
valid_mask |= SCR_API | SCR_APK;
|
||||
}
|
||||
|
||||
/* Clear all-context RES0 bits. */
|
||||
value &= valid_mask;
|
||||
@ -1846,7 +1956,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
|
||||
.writefn = pmcntenclr_write },
|
||||
{ .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
|
||||
.access = PL0_RW,
|
||||
.access = PL0_RW, .type = ARM_CP_IO,
|
||||
.fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr),
|
||||
.accessfn = pmreg_access,
|
||||
.writefn = pmovsr_write,
|
||||
@ -1854,16 +1964,18 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
|
||||
{ .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3,
|
||||
.access = PL0_RW, .accessfn = pmreg_access,
|
||||
.type = ARM_CP_ALIAS,
|
||||
.type = ARM_CP_ALIAS | ARM_CP_IO,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
|
||||
.writefn = pmovsr_write,
|
||||
.raw_writefn = raw_write },
|
||||
{ .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
|
||||
.access = PL0_W, .accessfn = pmreg_access_swinc, .type = ARM_CP_NO_RAW,
|
||||
.access = PL0_W, .accessfn = pmreg_access_swinc,
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
.writefn = pmswinc_write },
|
||||
{ .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4,
|
||||
.access = PL0_W, .accessfn = pmreg_access_swinc, .type = ARM_CP_NO_RAW,
|
||||
.access = PL0_W, .accessfn = pmreg_access_swinc,
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_IO,
|
||||
.writefn = pmswinc_write },
|
||||
{ .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
|
||||
.access = PL0_RW, .type = ARM_CP_ALIAS,
|
||||
@ -2050,14 +2162,14 @@ static const ARMCPRegInfo pmovsset_cp_reginfo[] = {
|
||||
/* PMOVSSET is not implemented in v7 before v7ve */
|
||||
{ .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3,
|
||||
.access = PL0_RW, .accessfn = pmreg_access,
|
||||
.type = ARM_CP_ALIAS,
|
||||
.type = ARM_CP_ALIAS | ARM_CP_IO,
|
||||
.fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr),
|
||||
.writefn = pmovsset_write,
|
||||
.raw_writefn = raw_write },
|
||||
{ .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3,
|
||||
.access = PL0_RW, .accessfn = pmreg_access,
|
||||
.type = ARM_CP_ALIAS,
|
||||
.type = ARM_CP_ALIAS | ARM_CP_IO,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
|
||||
.writefn = pmovsset_write,
|
||||
.raw_writefn = raw_write },
|
||||
@ -4449,6 +4561,9 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||
if (cpu_isar_feature(aa64_lor, cpu)) {
|
||||
valid_mask |= HCR_TLOR;
|
||||
}
|
||||
if (cpu_isar_feature(aa64_pauth, cpu)) {
|
||||
valid_mask |= HCR_API | HCR_APK;
|
||||
}
|
||||
|
||||
/* Clear RES0 bits. */
|
||||
value &= valid_mask;
|
||||
|
@ -2036,7 +2036,7 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
|
||||
if (!dc_isar_feature(aa64_pauth, s)) {
|
||||
goto do_unallocated;
|
||||
}
|
||||
if (op3 != 2 || op3 != 3) {
|
||||
if ((op3 & ~1) != 2) {
|
||||
goto do_unallocated;
|
||||
}
|
||||
if (s->pauth_active) {
|
||||
@ -2144,7 +2144,11 @@ static void disas_b_exc_sys(DisasContext *s, uint32_t insn)
|
||||
break;
|
||||
case 0x6a: /* Exception generation / System */
|
||||
if (insn & (1 << 24)) {
|
||||
disas_system(s, insn);
|
||||
if (extract32(insn, 22, 2) == 0) {
|
||||
disas_system(s, insn);
|
||||
} else {
|
||||
unallocated_encoding(s);
|
||||
}
|
||||
} else {
|
||||
disas_exc(s, insn);
|
||||
}
|
||||
@ -2799,7 +2803,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn,
|
||||
} else {
|
||||
if (size == 3 && opc == 2) {
|
||||
/* PRFM - prefetch */
|
||||
if (is_unpriv) {
|
||||
if (idx != 0) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
@ -3245,6 +3249,7 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
|
||||
{
|
||||
int rt = extract32(insn, 0, 5);
|
||||
int rn = extract32(insn, 5, 5);
|
||||
int rm = extract32(insn, 16, 5);
|
||||
int size = extract32(insn, 10, 2);
|
||||
int opcode = extract32(insn, 12, 4);
|
||||
bool is_store = !extract32(insn, 22, 1);
|
||||
@ -3264,6 +3269,11 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_postidx && rm != 0) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
/* From the shared decode logic */
|
||||
switch (opcode) {
|
||||
case 0x0:
|
||||
@ -3363,7 +3373,6 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
|
||||
}
|
||||
|
||||
if (is_postidx) {
|
||||
int rm = extract32(insn, 16, 5);
|
||||
if (rm == 31) {
|
||||
tcg_gen_mov_i64(tcg_rn, tcg_addr);
|
||||
} else {
|
||||
@ -3400,6 +3409,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
|
||||
{
|
||||
int rt = extract32(insn, 0, 5);
|
||||
int rn = extract32(insn, 5, 5);
|
||||
int rm = extract32(insn, 16, 5);
|
||||
int size = extract32(insn, 10, 2);
|
||||
int S = extract32(insn, 12, 1);
|
||||
int opc = extract32(insn, 13, 3);
|
||||
@ -3415,6 +3425,15 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
|
||||
int ebytes, xs;
|
||||
TCGv_i64 tcg_addr, tcg_rn, tcg_ebytes;
|
||||
|
||||
if (extract32(insn, 31, 1)) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
if (!is_postidx && rm != 0) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (scale) {
|
||||
case 3:
|
||||
if (!is_load || S) {
|
||||
@ -3492,7 +3511,6 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
|
||||
}
|
||||
|
||||
if (is_postidx) {
|
||||
int rm = extract32(insn, 16, 5);
|
||||
if (rm == 31) {
|
||||
tcg_gen_mov_i64(tcg_rn, tcg_addr);
|
||||
} else {
|
||||
@ -4183,6 +4201,7 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn)
|
||||
int imm3 = extract32(insn, 10, 3);
|
||||
int option = extract32(insn, 13, 3);
|
||||
int rm = extract32(insn, 16, 5);
|
||||
int opt = extract32(insn, 22, 2);
|
||||
bool setflags = extract32(insn, 29, 1);
|
||||
bool sub_op = extract32(insn, 30, 1);
|
||||
bool sf = extract32(insn, 31, 1);
|
||||
@ -4191,7 +4210,7 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn)
|
||||
TCGv_i64 tcg_rd;
|
||||
TCGv_i64 tcg_result;
|
||||
|
||||
if (imm3 > 4) {
|
||||
if (imm3 > 4 || opt != 0) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
@ -5617,11 +5636,17 @@ static void handle_fp_fcvt(DisasContext *s, int opcode,
|
||||
*/
|
||||
static void disas_fp_1src(DisasContext *s, uint32_t insn)
|
||||
{
|
||||
int mos = extract32(insn, 29, 3);
|
||||
int type = extract32(insn, 22, 2);
|
||||
int opcode = extract32(insn, 15, 6);
|
||||
int rn = extract32(insn, 5, 5);
|
||||
int rd = extract32(insn, 0, 5);
|
||||
|
||||
if (mos) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case 0x4: case 0x5: case 0x7:
|
||||
{
|
||||
@ -5848,13 +5873,14 @@ static void handle_fp_2src_half(DisasContext *s, int opcode,
|
||||
*/
|
||||
static void disas_fp_2src(DisasContext *s, uint32_t insn)
|
||||
{
|
||||
int mos = extract32(insn, 29, 3);
|
||||
int type = extract32(insn, 22, 2);
|
||||
int rd = extract32(insn, 0, 5);
|
||||
int rn = extract32(insn, 5, 5);
|
||||
int rm = extract32(insn, 16, 5);
|
||||
int opcode = extract32(insn, 12, 4);
|
||||
|
||||
if (opcode > 8) {
|
||||
if (opcode > 8 || mos) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
@ -6009,6 +6035,7 @@ static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1,
|
||||
*/
|
||||
static void disas_fp_3src(DisasContext *s, uint32_t insn)
|
||||
{
|
||||
int mos = extract32(insn, 29, 3);
|
||||
int type = extract32(insn, 22, 2);
|
||||
int rd = extract32(insn, 0, 5);
|
||||
int rn = extract32(insn, 5, 5);
|
||||
@ -6017,6 +6044,11 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn)
|
||||
bool o0 = extract32(insn, 15, 1);
|
||||
bool o1 = extract32(insn, 21, 1);
|
||||
|
||||
if (mos) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
if (!fp_access_check(s)) {
|
||||
@ -6086,12 +6118,19 @@ uint64_t vfp_expand_imm(int size, uint8_t imm8)
|
||||
static void disas_fp_imm(DisasContext *s, uint32_t insn)
|
||||
{
|
||||
int rd = extract32(insn, 0, 5);
|
||||
int imm5 = extract32(insn, 5, 5);
|
||||
int imm8 = extract32(insn, 13, 8);
|
||||
int type = extract32(insn, 22, 2);
|
||||
int mos = extract32(insn, 29, 3);
|
||||
uint64_t imm;
|
||||
TCGv_i64 tcg_res;
|
||||
TCGMemOp sz;
|
||||
|
||||
if (mos || imm5) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
sz = MO_32;
|
||||
@ -12602,7 +12641,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
|
||||
break;
|
||||
case 0x0e: /* SDOT */
|
||||
case 0x1e: /* UDOT */
|
||||
if (size != MO_32 || !dc_isar_feature(aa64_dp, s)) {
|
||||
if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_dp, s)) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
@ -12611,7 +12650,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
|
||||
case 0x13: /* FCMLA #90 */
|
||||
case 0x15: /* FCMLA #180 */
|
||||
case 0x17: /* FCMLA #270 */
|
||||
if (!dc_isar_feature(aa64_fcma, s)) {
|
||||
if (is_scalar || !dc_isar_feature(aa64_fcma, s)) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
@ -12641,7 +12680,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
|
||||
|
||||
case 2: /* complex fp */
|
||||
/* Each indexable element is a complex pair. */
|
||||
size <<= 1;
|
||||
size += 1;
|
||||
switch (size) {
|
||||
case MO_32:
|
||||
if (h && !is_q) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "hw/arm/nrf51.h"
|
||||
#include "hw/char/nrf51_uart.h"
|
||||
#include "hw/gpio/nrf51_gpio.h"
|
||||
#include "hw/nvram/nrf51_nvm.h"
|
||||
#include "hw/timer/nrf51_timer.h"
|
||||
#include "hw/i2c/microbit_i2c.h"
|
||||
|
||||
@ -156,6 +157,112 @@ static void test_microbit_i2c(void)
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
#define FLASH_SIZE (256 * NRF51_PAGE_SIZE)
|
||||
|
||||
static void fill_and_erase(QTestState *qts, hwaddr base, hwaddr size,
|
||||
uint32_t address_reg)
|
||||
{
|
||||
hwaddr i;
|
||||
|
||||
/* Erase Page */
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + address_reg, base);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
|
||||
|
||||
/* Check memory */
|
||||
for (i = 0; i < size / 4; i++) {
|
||||
g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
/* Fill memory */
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01);
|
||||
for (i = 0; i < size / 4; i++) {
|
||||
qtest_writel(qts, base + i * 4, i);
|
||||
g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, i);
|
||||
}
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
|
||||
}
|
||||
|
||||
static void test_nrf51_nvmc(void)
|
||||
{
|
||||
uint32_t value;
|
||||
hwaddr i;
|
||||
QTestState *qts = qtest_init("-M microbit");
|
||||
|
||||
/* Test always ready */
|
||||
value = qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_READY);
|
||||
g_assert_cmpuint(value & 0x01, ==, 0x01);
|
||||
|
||||
/* Test write-read config register */
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x03);
|
||||
g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG),
|
||||
==, 0x03);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
|
||||
g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG),
|
||||
==, 0x00);
|
||||
|
||||
/* Test PCR0 */
|
||||
fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE,
|
||||
NRF51_NVMC_ERASEPCR0);
|
||||
fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE,
|
||||
NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR0);
|
||||
|
||||
/* Test PCR1 */
|
||||
fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE,
|
||||
NRF51_NVMC_ERASEPCR1);
|
||||
fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE,
|
||||
NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR1);
|
||||
|
||||
/* Erase all */
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
|
||||
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01);
|
||||
for (i = 0; i < FLASH_SIZE / 4; i++) {
|
||||
qtest_writel(qts, NRF51_FLASH_BASE + i * 4, i);
|
||||
g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), ==, i);
|
||||
}
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
|
||||
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
|
||||
|
||||
for (i = 0; i < FLASH_SIZE / 4; i++) {
|
||||
g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4),
|
||||
==, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
/* Erase UICR */
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
|
||||
|
||||
for (i = 0; i < NRF51_UICR_SIZE / 4; i++) {
|
||||
g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4),
|
||||
==, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01);
|
||||
for (i = 0; i < NRF51_UICR_SIZE / 4; i++) {
|
||||
qtest_writel(qts, NRF51_UICR_BASE + i * 4, i);
|
||||
g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), ==, i);
|
||||
}
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
|
||||
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01);
|
||||
qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00);
|
||||
|
||||
for (i = 0; i < NRF51_UICR_SIZE / 4; i++) {
|
||||
g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4),
|
||||
==, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_nrf51_gpio(void)
|
||||
{
|
||||
size_t i;
|
||||
@ -392,6 +499,7 @@ int main(int argc, char **argv)
|
||||
|
||||
qtest_add_func("/microbit/nrf51/uart", test_nrf51_uart);
|
||||
qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio);
|
||||
qtest_add_func("/microbit/nrf51/nvmc", test_nrf51_nvmc);
|
||||
qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer);
|
||||
qtest_add_func("/microbit/microbit/i2c", test_microbit_i2c);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user