@@ -694,6 +694,9 @@ struct ARMCPU {
/* MemoryRegion to use for secure physical accesses */
MemoryRegion *secure_memory;
+ /* For v8M, pointer to the IDAU interface provided by board/SoC */
+ Object *idau;
+
/* 'compatible' string for this CPU for Linux device trees */
const char *dtb_compatible;
new file mode 100644
@@ -0,0 +1,61 @@
+/*
+ * QEMU ARM CPU -- interface for the Arm v8M IDAU
+ *
+ * Copyright (c) 2018 Linaro Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see
+ * <http://www.gnu.org/licenses/gpl-2.0.html>
+ *
+ * In the v8M architecture, the IDAU is a small piece of hardware
+ * typically implemented in the SoC which provides board or SoC
+ * specific security attribution information for each address that
+ * the CPU performs MPU/SAU checks on. For QEMU, we model this with a
+ * QOM interface which is implemented by the board or SoC object and
+ * connected to the CPU using a link property.
+ */
+
+#ifndef TARGET_ARM_IDAU_H
+#define TARGET_ARM_IDAU_H
+
+#include "qom/object.h"
+
+#define TYPE_IDAU_INTERFACE "idau-interface"
+#define IDAU_INTERFACE(obj) \
+ INTERFACE_CHECK(IDAUInterface, (obj), TYPE_IDAU_INTERFACE)
+#define IDAU_INTERFACE_CLASS(class) \
+ OBJECT_CLASS_CHECK(IDAUInterfaceClass, (class), TYPE_IDAU_INTERFACE)
+#define IDAU_INTERFACE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(IDAUInterfaceClass, (obj), TYPE_IDAU_INTERFACE)
+
+typedef struct IDAUInterface {
+ Object parent;
+} IDAUInterface;
+
+#define IREGION_NOTVALID -1
+
+typedef struct IDAUInterfaceClass {
+ InterfaceClass parent;
+
+ /* Check the specified address and return the IDAU security information
+ * for it by filling in iregion, exempt, ns and nsc:
+ * iregion: IDAU region number, or IREGION_NOTVALID if not valid
+ * exempt: true if address is exempt from security attribution
+ * ns: true if the address is NonSecure
+ * nsc: true if the address is NonSecure-callable
+ */
+ void (*check)(IDAUInterface *ii, uint32_t address, int *iregion,
+ bool *exempt, bool *ns, bool *nsc);
+} IDAUInterfaceClass;
+
+#endif
@@ -19,6 +19,7 @@
*/
#include "qemu/osdep.h"
+#include "target/arm/idau.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "cpu.h"
@@ -688,6 +689,13 @@ static void arm_cpu_post_init(Object *obj)
}
}
+ if (arm_feature(&cpu->env, ARM_FEATURE_M_SECURITY)) {
+ object_property_add_link(obj, "idau", TYPE_IDAU_INTERFACE, &cpu->idau,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+ }
+
qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property,
&error_abort);
}
@@ -1821,11 +1829,18 @@ static const TypeInfo arm_cpu_type_info = {
.class_init = arm_cpu_class_init,
};
+static const TypeInfo idau_interface_type_info = {
+ .name = TYPE_IDAU_INTERFACE,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(IDAUInterfaceClass),
+};
+
static void arm_cpu_register_types(void)
{
const ARMCPUInfo *info = arm_cpus;
type_register_static(&arm_cpu_type_info);
+ type_register_static(&idau_interface_type_info);
while (info->name) {
cpu_register(info);
@@ -1,4 +1,5 @@
#include "qemu/osdep.h"
+#include "target/arm/idau.h"
#include "trace.h"
#include "cpu.h"
#include "internals.h"
@@ -9741,19 +9742,32 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address,
*/
ARMCPU *cpu = arm_env_get_cpu(env);
int r;
+ bool idau_exempt = false, idau_ns = true, idau_nsc = true;
+ int idau_region = IREGION_NOTVALID;
- /* TODO: implement IDAU */
+ if (cpu->idau) {
+ IDAUInterfaceClass *iic = IDAU_INTERFACE_GET_CLASS(cpu->idau);
+ IDAUInterface *ii = IDAU_INTERFACE(cpu->idau);
+
+ iic->check(ii, address, &idau_region, &idau_exempt, &idau_ns,
+ &idau_nsc);
+ }
if (access_type == MMU_INST_FETCH && extract32(address, 28, 4) == 0xf) {
/* 0xf0000000..0xffffffff is always S for insn fetches */
return;
}
- if (v8m_is_sau_exempt(env, address, access_type)) {
+ if (idau_exempt || v8m_is_sau_exempt(env, address, access_type)) {
sattrs->ns = !regime_is_secure(env, mmu_idx);
return;
}
+ if (idau_region != IREGION_NOTVALID) {
+ sattrs->irvalid = true;
+ sattrs->iregion = idau_region;
+ }
+
switch (env->sau.ctrl & 3) {
case 0: /* SAU.ENABLE == 0, SAU.ALLNS == 0 */
break;
@@ -9790,7 +9804,15 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address,
}
}
- /* TODO when we support the IDAU then it may override the result here */
+ /* The IDAU will override the SAU lookup results if it specifies
+ * higher security than the SAU does.
+ */
+ if (!idau_ns) {
+ if (sattrs->ns || (!idau_nsc && sattrs->nsc)) {
+ sattrs->ns = false;
+ sattrs->nsc = idau_nsc;
+ }
+ }
break;
}
}