aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonio Nino Diaz <antonio.ninodiaz@arm.com>2019-03-19 10:59:11 +0000
committerAntonio Nino Diaz <antonio.ninodiaz@arm.com>2019-03-19 11:22:43 +0000
commitf2218e7b6a4f8c2c3d6db00703caad95f5e5b157 (patch)
tree83f72bbabcabae7e25e82c3192d27718dea21f67
parent960906b2d8fc2596f257b55606c69505d2bf1130 (diff)
downloadtf-a-tests-2.1-rc0.tar.gz
Reintroduce Cactus-MM and SPM-MM testsv2.1-rc0
The code has been taken from commit 99f4fd283b6f ("cactus: Use UART2 instead of UART0") and modified slightly to be integrated in the current master. There are three tests that are failing in the CI. They have been disabled for the time being: - mem_attr_changes_tests() in cactus_main() in the file spm/cactus_mm/cactus_mm_main.c. - Two tests in the file tftf/tests/tests-spm-mm.xml. Change-Id: I6332cbff1cefeb82b9447fae1b613879e65186a1 Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
-rw-r--r--Makefile16
-rw-r--r--include/common/debug.h5
-rw-r--r--include/runtime_services/secure_el0_payloads/mm_svc.h31
-rw-r--r--include/runtime_services/secure_el0_payloads/secure_partition.h66
-rw-r--r--include/runtime_services/secure_el0_payloads/spm_svc.h1
-rw-r--r--plat/arm/fvp/include/platform_def.h10
-rw-r--r--spm/cactus_mm/aarch64/cactus_mm_entrypoint.S111
-rw-r--r--spm/cactus_mm/cactus_mm.h36
-rw-r--r--spm/cactus_mm/cactus_mm.ld.S52
-rw-r--r--spm/cactus_mm/cactus_mm.mk60
-rw-r--r--spm/cactus_mm/cactus_mm_main.c126
-rw-r--r--spm/cactus_mm/cactus_mm_service_loop.c141
-rw-r--r--spm/cactus_mm/cactus_mm_tests.h46
-rw-r--r--spm/cactus_mm/cactus_mm_tests_memory_attributes.c236
-rw-r--r--spm/cactus_mm/cactus_mm_tests_misc.c39
-rw-r--r--spm/cactus_mm/cactus_mm_tests_system_setup.c71
-rw-r--r--tftf/tests/runtime_services/mm_service/secure_service_helpers.c23
-rw-r--r--tftf/tests/runtime_services/mm_service/test_secure_service_handle.c254
-rw-r--r--tftf/tests/runtime_services/mm_service/test_secure_service_interrupts.c137
-rw-r--r--tftf/tests/tests-spm-mm.mk12
-rw-r--r--tftf/tests/tests-spm-mm.xml25
21 files changed, 1496 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 0179bbfc..a491bc40 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2018, Arm Limited. All rights reserved.
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -114,6 +114,7 @@ include tftf/framework/framework.mk
include tftf/tests/tests.mk
include fwu/ns_bl1u/ns_bl1u.mk
include fwu/ns_bl2u/ns_bl2u.mk
+include spm/cactus_mm/cactus_mm.mk
include spm/cactus/cactus.mk
include spm/ivy/ivy.mk
@@ -222,6 +223,12 @@ NS_BL2U_CFLAGS += ${COMMON_CFLAGS}
NS_BL2U_ASFLAGS += ${COMMON_ASFLAGS}
NS_BL2U_LDFLAGS += ${COMMON_LDFLAGS}
+CACTUS_MM_SOURCES += ${LIBC_SRCS}
+CACTUS_MM_INCLUDES += ${PLAT_INCLUDES}
+CACTUS_MM_CFLAGS += ${COMMON_CFLAGS}
+CACTUS_MM_ASFLAGS += ${COMMON_ASFLAGS}
+CACTUS_MM_LDFLAGS += ${COMMON_LDFLAGS}
+
CACTUS_SOURCES += ${LIBC_SRCS}
CACTUS_INCLUDES += ${PLAT_INCLUDES}
CACTUS_CFLAGS += ${COMMON_CFLAGS}
@@ -296,6 +303,11 @@ ns_bl1u ns_bl2u:
endif
ifneq (${ARCH}-${PLAT},aarch64-fvp)
+.PHONY: cactus_mm
+cactus_mm:
+ @echo "ERROR: $@ is supported only on AArch64 FVP."
+ @exit 1
+
.PHONY: cactus
cactus:
@echo "ERROR: $@ is supported only on AArch64 FVP."
@@ -430,6 +442,7 @@ ifeq ($(FIRMWARE_UPDATE), 1)
endif
ifeq (${ARCH}-${PLAT},aarch64-fvp)
+ $(eval $(call MAKE_IMG,cactus_mm))
$(eval $(call MAKE_IMG,cactus))
$(eval $(call MAKE_IMG,ivy))
endif
@@ -465,6 +478,7 @@ help:
@echo " ns_bl1u Build the NS_BL1U image"
@echo " ns_bl2u Build the NS_BL2U image"
@echo " cactus Build the Cactus image (Test S-EL0 payload) and resource description."
+ @echo " cactus_mm Build the Cactus-MM image (Test S-EL0 payload)."
@echo " ivy Build the Ivy image (Test S-EL0 payload) and resource description."
@echo " el3_payload Build the EL3 test payload"
@echo " checkcodebase Check the coding style of the entire source tree"
diff --git a/include/common/debug.h b/include/common/debug.h
index 9b2d8421..4b30175d 100644
--- a/include/common/debug.h
+++ b/include/common/debug.h
@@ -9,6 +9,10 @@
#include <stdio.h>
+#ifdef IMAGE_CACTUS_MM
+/* Remove dependency on spinlocks for Cactus-MM */
+#define mp_printf printf
+#else
/*
* Print a formatted string on the UART.
*
@@ -18,6 +22,7 @@
*/
__attribute__((format(printf, 1, 2)))
void mp_printf(const char *fmt, ...);
+#endif /* IMAGE_CACTUS_MM */
/*
* The log output macros print output to the console. These macros produce
diff --git a/include/runtime_services/secure_el0_payloads/mm_svc.h b/include/runtime_services/secure_el0_payloads/mm_svc.h
new file mode 100644
index 00000000..290fd7d1
--- /dev/null
+++ b/include/runtime_services/secure_el0_payloads/mm_svc.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __MM_SVC_H__
+#define __MM_SVC_H__
+
+#include <utils_def.h>
+
+#define MM_VERSION_MAJOR U(1)
+#define MM_VERSION_MAJOR_SHIFT 16
+#define MM_VERSION_MAJOR_MASK U(0x7FFF)
+#define MM_VERSION_MINOR U(0)
+#define MM_VERSION_MINOR_SHIFT 0
+#define MM_VERSION_MINOR_MASK U(0xFFFF)
+#define MM_VERSION_FORM(major, minor) ((major << MM_VERSION_MAJOR_SHIFT) | (minor))
+#define MM_VERSION_COMPILED MM_VERSION_FORM(MM_VERSION_MAJOR, MM_VERSION_MINOR)
+
+/*
+ * SMC IDs defined in [1] for accessing MM services from the Non-secure world.
+ * These FIDs occupy the range 0x40 - 0x5f.
+ * [1] DEN0060A_ARM_MM_Interface_Specification.pdf
+ */
+#define MM_VERSION_AARCH32 U(0x84000040)
+
+#define MM_COMMUNICATE_AARCH64 U(0xC4000041)
+#define MM_COMMUNICATE_AARCH32 U(0x84000041)
+
+#endif /* __MM_SVC_H__ */
diff --git a/include/runtime_services/secure_el0_payloads/secure_partition.h b/include/runtime_services/secure_el0_payloads/secure_partition.h
index 0a41ddef..14d20900 100644
--- a/include/runtime_services/secure_el0_payloads/secure_partition.h
+++ b/include/runtime_services/secure_el0_payloads/secure_partition.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -7,9 +7,22 @@
#ifndef SECURE_PARTITION_H
#define SECURE_PARTITION_H
+#ifndef __ASSEMBLY__
+#include <cassert.h>
#include <param_header.h>
#include <stdint.h>
#include <utils_def.h>
+#endif
+
+/*
+ * Definitions used to access the members of secure_partition_boot_info from
+ * assembly code.
+ */
+#define SP_BOOT_INFO_STACK_BASE_OFFSET U(32)
+#define SP_BOOT_INFO_IMAGE_SIZE_OFFSET U(64)
+#define SP_BOOT_INFO_PCPU_STACK_SIZE_OFFSET U(72)
+
+#ifndef __ASSEMBLY__
/*
* Flags used by the secure_partition_mp_info structure to describe the
@@ -30,9 +43,60 @@ typedef struct secure_partition_mp_info {
typedef struct secure_partition_boot_info {
param_header_t h;
+ uint64_t sp_mem_base;
+ uint64_t sp_mem_limit;
+ uint64_t sp_image_base;
+ uint64_t sp_stack_base;
+ uint64_t sp_heap_base;
+ uint64_t sp_ns_comm_buf_base;
+ uint64_t sp_shared_buf_base;
+ uint64_t sp_image_size;
+ uint64_t sp_pcpu_stack_size;
+ uint64_t sp_heap_size;
+ uint64_t sp_ns_comm_buf_size;
+ uint64_t sp_shared_buf_size;
+ uint32_t num_sp_mem_regions;
uint32_t num_cpus;
secure_partition_mp_info_t *mp_info;
} secure_partition_boot_info_t;
+/*
+ * This structure is used to pass data associated to secure service requests.
+ */
+#define SPS_MAX_PAYLOAD_SIZE 32
+typedef struct secure_partition_request_info {
+ uint32_t id;
+ uint64_t data_size;
+ uint8_t data[SPS_MAX_PAYLOAD_SIZE];
+} secure_partition_request_info_t;
+
+/* Define some fast secure partition requests (SPS) IDs. */
+#define SPS_TIMER_SLEEP 1
+#define SPS_CHECK_ALIVE 2
+
+#define CACTUS_FAST_REQUEST_SUCCESS 0xCACF900D
+
+secure_partition_request_info_t *create_sps_request(uint32_t id,
+ const void *data,
+ uint64_t data_size);
+
+/*
+ * Compile time assertions related to the 'secure_partition_boot_info' structure
+ * to ensure that the assembler and the compiler view of the offsets of the
+ * structure members is the same.
+ */
+CASSERT(SP_BOOT_INFO_STACK_BASE_OFFSET ==
+ __builtin_offsetof(secure_partition_boot_info_t, sp_stack_base), \
+ assert_secure_partition_boot_info_sp_stack_base_offset_mismatch);
+
+CASSERT(SP_BOOT_INFO_IMAGE_SIZE_OFFSET ==
+ __builtin_offsetof(secure_partition_boot_info_t, sp_image_size), \
+ assert_secure_partition_boot_info_sp_image_size_offset_mismatch);
+
+CASSERT(SP_BOOT_INFO_PCPU_STACK_SIZE_OFFSET ==
+ __builtin_offsetof(secure_partition_boot_info_t, sp_pcpu_stack_size), \
+ assert_secure_partition_boot_info_sp_pcpu_stack_size_offset_mismatch);
+
+#endif /* __ASSEMBLY__ */
#endif /* SECURE_PARTITION_H */
diff --git a/include/runtime_services/secure_el0_payloads/spm_svc.h b/include/runtime_services/secure_el0_payloads/spm_svc.h
index e1f9df30..e3b6cc51 100644
--- a/include/runtime_services/secure_el0_payloads/spm_svc.h
+++ b/include/runtime_services/secure_el0_payloads/spm_svc.h
@@ -33,6 +33,7 @@
* They occupy the range 0x60-0x7f.
*/
#define SPM_VERSION_AARCH32 U(0x84000060)
+#define SP_EVENT_COMPLETE_AARCH64 U(0xC4000061)
#define SP_MEMORY_ATTRIBUTES_GET_AARCH64 U(0xC4000064)
#define SP_MEMORY_ATTRIBUTES_SET_AARCH64 U(0xC4000065)
diff --git a/plat/arm/fvp/include/platform_def.h b/plat/arm/fvp/include/platform_def.h
index 55477b44..19be6cb0 100644
--- a/plat/arm/fvp/include/platform_def.h
+++ b/plat/arm/fvp/include/platform_def.h
@@ -222,4 +222,14 @@
#define PLAT_SUSPEND_ENTRY_TIME 15
#define PLAT_SUSPEND_ENTRY_EXIT_TIME 30
+/*******************************************************************************
+ * Location of the memory buffer shared between Normal World (i.e. TFTF) and the
+ * Secure Partition (e.g. Cactus-MM) to pass data associated to secure service
+ * requests. This is only needed for SPM based on MM.
+ * Note: This address has to match the one used in TF (see ARM_SP_IMAGE_NS_BUF_*
+ * macros).
+ ******************************************************************************/
+#define ARM_SECURE_SERVICE_BUFFER_BASE 0xff600000ull
+#define ARM_SECURE_SERVICE_BUFFER_SIZE 0x10000ull
+
#endif /* __PLATFORM_DEF_H__ */
diff --git a/spm/cactus_mm/aarch64/cactus_mm_entrypoint.S b/spm/cactus_mm/aarch64/cactus_mm_entrypoint.S
new file mode 100644
index 00000000..a657aabc
--- /dev/null
+++ b/spm/cactus_mm/aarch64/cactus_mm_entrypoint.S
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <asm_macros.S>
+#include <secure_partition.h>
+#include <spm_svc.h>
+#include <xlat_tables_defs.h>
+
+ .globl cactus_mm_entrypoint
+
+func cactus_mm_entrypoint
+
+ /*
+ * All the information needed to remap the memory of the Secure
+ * Partition is in the buffer whose pointer is passed on X0 and size on
+ * X1. If the size is 0, return with an error.
+ */
+ cmp x1, #0
+ beq .return_error
+
+ /* Save the base address and size of the buffer. */
+ mov x20, x0
+ mov x21, x1
+ /* Size of the Secure Partition image. */
+ ldr x22, [x20, SP_BOOT_INFO_IMAGE_SIZE_OFFSET]
+
+ /*
+ * Remap all sections of the image before doing anything else.
+ *
+ * Not even the console can be initialized before because it needs to
+ * initialize variables (that can only be modified after remapping that
+ * region as RW).
+ *
+ * If any of the calls fails, loop, as there is no console to print an
+ * error message to.
+ */
+ .macro set_sp_mem_attributes
+ cmp x2, #0 /* If size is 0, skip the call. */
+ beq 1f
+ mov_imm x0, SP_MEMORY_ATTRIBUTES_SET_AARCH64
+ svc #0
+ cmp x0, #0
+ bne .return_error
+1:
+ .endm
+
+ adr x1, __TEXT_START__
+ adr x2, __TEXT_END__
+ sub x2, x2, x1 /* __TEXT_SIZE__ */
+ lsr x2, x2, PAGE_SIZE_SHIFT /* __TEXT_SIZE__ in pages */
+ mov x3, SP_MEMORY_ATTRIBUTES_ACCESS_RO | SP_MEMORY_ATTRIBUTES_EXEC
+ set_sp_mem_attributes
+
+ adr x1, __RODATA_START__
+ adr x2, __RODATA_END__
+ sub x2, x2, x1 /* __RODATA_SIZE__ */
+ lsr x2, x2, PAGE_SIZE_SHIFT /* __RODATA_SIZE__ in pages */
+ mov x3, SP_MEMORY_ATTRIBUTES_ACCESS_RO | SP_MEMORY_ATTRIBUTES_NON_EXEC
+ set_sp_mem_attributes
+
+ adr x1, __RWDATA_START__
+ adr x2, __RWDATA_END__
+ sub x2, x2, x1 /* __RWDATA_SIZE__ */
+ lsr x2, x2, PAGE_SIZE_SHIFT /* __RWDATA_SIZE__ in pages */
+ mov x3, SP_MEMORY_ATTRIBUTES_ACCESS_RW | SP_MEMORY_ATTRIBUTES_NON_EXEC
+ set_sp_mem_attributes
+
+ /*
+ * To avoid accessing it by mistake, prevent EL0 from accessing the rest
+ * of the memory reserved for the Secure Partition.
+ *
+ * Unused size = Total size - Used size
+ * = Total size - (__RWDATA_END__ - __TEXT_START__)
+ */
+ adr x1, __RWDATA_END__
+ adr x2, __TEXT_START__
+ sub x2, x1, x2 /* x2 = Used size, x22 = Total size */
+ sub x2, x22, x2 /* x2 = Unused size */
+ lsr x2, x2, PAGE_SIZE_SHIFT /* Unused size in pages */
+ mov x3, SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS | SP_MEMORY_ATTRIBUTES_NON_EXEC
+ set_sp_mem_attributes
+
+ adr x0, __BSS_START__
+ adr x1, __BSS_END__
+ sub x1, x1, x0
+ bl zeromem16
+
+ /* Setup the stack pointer. */
+ ldr x0, [x20, SP_BOOT_INFO_STACK_BASE_OFFSET]
+ ldr x1, [x20, SP_BOOT_INFO_PCPU_STACK_SIZE_OFFSET]
+ add x0, x0, x1
+ mov sp, x0
+
+ /* And do the rest in C code */
+ mov x0, x20
+ mov x1, x21
+ b cactus_main
+
+.return_error:
+ /* Tell SPM that the initialization failed. */
+ mov_imm x0, SP_EVENT_COMPLETE_AARCH64
+ mov x1, #-1
+ svc #0
+
+ /* Loop forever */
+ b .
+
+endfunc cactus_mm_entrypoint
diff --git a/spm/cactus_mm/cactus_mm.h b/spm/cactus_mm/cactus_mm.h
new file mode 100644
index 00000000..1e0f24d6
--- /dev/null
+++ b/spm/cactus_mm/cactus_mm.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __CACTUS_H__
+#define __CACTUS_H__
+
+#include <secure_partition.h>
+#include <stdint.h>
+
+/* Linker symbols used to figure out the memory layout of Cactus. */
+extern uintptr_t __TEXT_START__, __TEXT_END__;
+#define CACTUS_TEXT_START ((uintptr_t)&__TEXT_START__)
+#define CACTUS_TEXT_END ((uintptr_t)&__TEXT_END__)
+
+extern uintptr_t __RODATA_START__, __RODATA_END__;
+#define CACTUS_RODATA_START ((uintptr_t)&__RODATA_START__)
+#define CACTUS_RODATA_END ((uintptr_t)&__RODATA_END__)
+
+extern uintptr_t __RWDATA_START__, __RWDATA_END__;
+#define CACTUS_RWDATA_START ((uintptr_t)&__RWDATA_START__)
+#define CACTUS_RWDATA_END ((uintptr_t)&__RWDATA_END__)
+
+extern uintptr_t __BSS_START__, __BSS_END__;
+#define CACTUS_BSS_START ((uintptr_t)&__BSS_START__)
+#define CACTUS_BSS_END ((uintptr_t)&__BSS_END__)
+
+/*
+ * Once Cactus has finished its initialisation, this is the function it will
+ * jump to to handle runtime services for the rest of its lifetime.
+ */
+__dead2 void secure_services_loop(void);
+
+#endif /* __CACTUS_H__ */
diff --git a/spm/cactus_mm/cactus_mm.ld.S b/spm/cactus_mm/cactus_mm.ld.S
new file mode 100644
index 00000000..4ab82219
--- /dev/null
+++ b/spm/cactus_mm/cactus_mm.ld.S
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017-2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <platform_def.h>
+#include <xlat_tables_defs.h>
+
+OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
+OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
+ENTRY(cactus_mm_entrypoint)
+
+SECTIONS
+{
+ ASSERT(. == ALIGN(PAGE_SIZE),
+ "TEXT_START address is not aligned to PAGE_SIZE.")
+
+ .text : {
+ __TEXT_START__ = .;
+ *cactus_mm_entrypoint.o(.text*)
+ *(.text*)
+ *(.vectors)
+ . = NEXT(PAGE_SIZE);
+ __TEXT_END__ = .;
+ }
+
+ .rodata : {
+ . = ALIGN(PAGE_SIZE);
+ __RODATA_START__ = .;
+ *(.rodata*)
+ . = NEXT(PAGE_SIZE);
+ __RODATA_END__ = .;
+ }
+
+
+ .data : {
+ . = ALIGN(PAGE_SIZE);
+ __RWDATA_START__ = .;
+ *(.data*)
+ }
+
+ .bss : {
+ . = ALIGN(16);
+ __BSS_START__ = .;
+ *(SORT_BY_ALIGNMENT(.bss*))
+ *(COMMON)
+ . = NEXT(PAGE_SIZE);
+ __BSS_END__ = .;
+ __RWDATA_END__ = .;
+ }
+}
diff --git a/spm/cactus_mm/cactus_mm.mk b/spm/cactus_mm/cactus_mm.mk
new file mode 100644
index 00000000..f00d0399
--- /dev/null
+++ b/spm/cactus_mm/cactus_mm.mk
@@ -0,0 +1,60 @@
+#
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+CACTUS_MM_INCLUDES := \
+ -Iinclude \
+ -Iinclude/common \
+ -Iinclude/common/${ARCH} \
+ -Iinclude/drivers \
+ -Iinclude/drivers/arm \
+ -Iinclude/lib \
+ -Iinclude/lib/${ARCH} \
+ -Iinclude/lib/utils \
+ -Iinclude/lib/xlat_tables \
+ -Iinclude/runtime_services \
+ -Iinclude/runtime_services/secure_el0_payloads \
+ -Ispm/cactus_mm \
+ -Ispm/common \
+
+CACTUS_MM_SOURCES := \
+ $(addprefix spm/cactus_mm/, \
+ aarch64/cactus_mm_entrypoint.S \
+ cactus_mm_main.c \
+ cactus_mm_service_loop.c \
+ cactus_mm_tests_memory_attributes.c \
+ cactus_mm_tests_misc.c \
+ cactus_mm_tests_system_setup.c \
+ ) \
+ $(addprefix spm/common/, \
+ aarch64/sp_arch_helpers.S \
+ sp_helpers.c \
+ ) \
+
+# TODO: Remove dependency on TFTF files.
+CACTUS_MM_SOURCES += \
+ tftf/framework/debug.c \
+
+CACTUS_MM_SOURCES += \
+ drivers/arm/pl011/${ARCH}/pl011_console.S \
+ lib/${ARCH}/cache_helpers.S \
+ lib/${ARCH}/misc_helpers.S \
+ ${STDLIB_SOURCES}
+
+CACTUS_MM_LINKERFILE := spm/cactus_mm/cactus_mm.ld.S
+
+CACTUS_MM_DEFINES :=
+
+# TODO: Assertions are disabled because they add several TFTF files as
+# dependencies. It is needed to break the dependencies and remove this line when
+# that is done.
+CACTUS_MM_DEFINES += -DENABLE_ASSERTIONS=0
+
+$(eval $(call add_define,CACTUS_MM_DEFINES,DEBUG))
+$(eval $(call add_define,CACTUS_MM_DEFINES,LOG_LEVEL))
+$(eval $(call add_define,CACTUS_MM_DEFINES,PLAT_${PLAT}))
+$(eval $(call add_define,CACTUS_MM_DEFINES,AARCH64))
+
+cactus_mm: ${AUTOGEN_DIR}/tests_list.h
diff --git a/spm/cactus_mm/cactus_mm_main.c b/spm/cactus_mm/cactus_mm_main.c
new file mode 100644
index 00000000..8452e656
--- /dev/null
+++ b/spm/cactus_mm/cactus_mm_main.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <console.h>
+#include <debug.h>
+#include <pl011.h>
+#include <platform_def.h>
+#include <secure_partition.h>
+#include <sp_helpers.h>
+#include <spm_svc.h>
+#include <std_svc.h>
+
+#include "cactus_mm.h"
+#include "cactus_mm_tests.h"
+
+
+/* Host machine information injected by the build system in the ELF file. */
+extern const char build_message[];
+extern const char version_string[];
+
+/*
+ * The ARM Trusted Firmware passes a description of the memory resources
+ * allocated to the secure partition through the x0 register. This maps to
+ * a secure_partition_boot_info_t structure type.
+ *
+ * This functions prints the information stored in this structure.
+ */
+static void cactus_print_memory_layout(const secure_partition_boot_info_t *boot_info)
+{
+ NOTICE("Secure Partition memory layout:\n");
+
+ NOTICE(" Secure Partition image : %p - %p\n",
+ (void *) boot_info->sp_image_base,
+ (void *)(boot_info->sp_image_base + boot_info->sp_image_size));
+ NOTICE(" Text region : %p - %p\n",
+ (void *) CACTUS_TEXT_START, (void *) CACTUS_TEXT_END);
+ NOTICE(" Read-only data region : %p - %p\n",
+ (void *) CACTUS_RODATA_START, (void *) CACTUS_RODATA_END);
+ NOTICE(" Read-write data region : %p - %p\n",
+ (void *) CACTUS_RWDATA_START, (void *) CACTUS_RWDATA_END);
+ NOTICE(" BSS region : %p - %p\n",
+ (void *) CACTUS_BSS_START, (void *) CACTUS_BSS_END);
+ NOTICE(" Unused SP image space : %p - %p\n",
+ (void *) CACTUS_BSS_END,
+ (void *)(boot_info->sp_image_base + boot_info->sp_image_size));
+
+ NOTICE(" EL3-EL0 shared buffer : %p - %p\n",
+ (void *) boot_info->sp_shared_buf_base,
+ (void *)(boot_info->sp_shared_buf_base + boot_info->sp_shared_buf_size));
+
+ NOTICE(" S-NS shared buffer : %p - %p\n",
+ (void *) boot_info->sp_ns_comm_buf_base,
+ (void *)(boot_info->sp_ns_comm_buf_base + boot_info->sp_ns_comm_buf_size));
+
+ assert(boot_info->sp_ns_comm_buf_base == ARM_SECURE_SERVICE_BUFFER_BASE);
+ assert(boot_info->sp_ns_comm_buf_size == ARM_SECURE_SERVICE_BUFFER_SIZE);
+
+ NOTICE(" Stacks region (%u CPUS) : %p - %p\n",
+ boot_info->num_cpus,
+ (void *) boot_info->sp_stack_base,
+ (void *)(boot_info->sp_stack_base +
+ (boot_info->sp_pcpu_stack_size * boot_info->num_cpus)));
+
+ NOTICE(" Heap region : %p - %p\n",
+ (void *) boot_info->sp_heap_base,
+ (void *)(boot_info->sp_heap_base + boot_info->sp_heap_size));
+
+ NOTICE("Total memory : %p - %p\n",
+ (void *) boot_info->sp_mem_base, (void *) boot_info->sp_mem_limit);
+}
+
+
+void __dead2 cactus_main(void *el3_el0_buffer, size_t el3_el0_buffer_size)
+{
+ console_init(PL011_UART2_BASE,
+ PL011_UART2_CLK_IN_HZ,
+ PL011_BAUDRATE);
+
+ NOTICE("Booting test Secure Partition Cactus\n");
+ NOTICE("%s\n", build_message);
+ NOTICE("%s\n", version_string);
+ NOTICE("Running at S-EL0\n");
+
+ const secure_partition_boot_info_t *boot_info =
+ (const secure_partition_boot_info_t *) el3_el0_buffer;
+
+ if (el3_el0_buffer_size < sizeof(secure_partition_boot_info_t)) {
+ ERROR("The memory buffer shared between EL3/S-EL0 is too small\n");
+ ERROR("It is %lu bytes, it should be at least %lu bytes\n",
+ el3_el0_buffer_size,
+ sizeof(secure_partition_boot_info_t));
+ panic();
+ }
+
+ if ((CACTUS_TEXT_START != boot_info->sp_image_base) ||
+ (CACTUS_RWDATA_END > boot_info->sp_image_base
+ + boot_info->sp_image_size)) {
+ ERROR("Cactus does not fit in the buffer allocated for the secure partition\n");
+ panic();
+ }
+
+ cactus_print_memory_layout(boot_info);
+
+
+ /*
+ * Run some initial tests.
+ *
+ * These are executed when the system is still booting, just after SPM
+ * has handed over to Cactus.
+ */
+ misc_tests();
+ system_setup_tests();
+ /*
+ * TODO: The following tests fail in the CI:
+ * mem_attr_changes_tests(boot_info);
+ */
+
+ /*
+ * Handle secure service requests.
+ */
+ secure_services_loop();
+}
diff --git a/spm/cactus_mm/cactus_mm_service_loop.c b/spm/cactus_mm/cactus_mm_service_loop.c
new file mode 100644
index 00000000..0ad60b62
--- /dev/null
+++ b/spm/cactus_mm/cactus_mm_service_loop.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <mm_svc.h>
+#include <secure_partition.h>
+#include <sp_helpers.h>
+#include <spm_svc.h>
+#include <string.h>
+
+
+/*
+ * Handle a fast secure service request, i.e. one made through an MM_COMMUNICATE
+ * call.
+ *
+ * cc
+ * Calling convention. If MM_COMMUNICATE has been invoked using the SMC32
+ * calling convention, this argument must be 32, else 64.
+ *
+ * sps
+ * Communication buffer attached to the secure partition service request.
+ */
+static int32_t cactus_handle_fast_request(int cc,
+ secure_partition_request_info_t *sps)
+{
+ assert(cc == 32 || cc == 64);
+
+ /* No SMC32 is supported at the moment. Just ignore them. */
+ if (cc == 32) {
+ INFO("Ignoring MM_COMMUNICATE_AARCH32 call\n");
+ return SPM_SUCCESS;
+ }
+
+ /* See secure_partition.h for possible ID values. */
+ switch (sps->id) {
+ case SPS_TIMER_SLEEP: {
+ if (sps->data_size != 1) {
+ ERROR("Invalid payload size for SPM_SPS_TIMER_SLEEP request (%llu)\n",
+ sps->data_size);
+ return SPM_INVALID_PARAMETER;
+ }
+ int duration_sec = sps->data[0];
+ sp_sleep(duration_sec);
+
+ /*
+ * Write back to the communication buffer to acknowledge the
+ * request has been successfully handled.
+ */
+ uint32_t response = CACTUS_FAST_REQUEST_SUCCESS;
+ memcpy(sps->data, &response, sizeof(response));
+ return SPM_SUCCESS;
+ }
+
+ case SPS_CHECK_ALIVE:
+ return SPM_SUCCESS;
+
+ default:
+ INFO("Unsupported MM_COMMUNICATE_AARCH64 call with service ID 0x%x, ignoring it\n",
+ sps->id);
+ return SPM_INVALID_PARAMETER;
+ }
+}
+
+__dead2 void secure_services_loop(void)
+{
+ int32_t event_status_code;
+ svc_args svc_values = { 0 };
+
+ /*
+ * The first time this loop is executed corresponds to when Cactus has
+ * finished initialising its run time environment and is ready to handle
+ * secure service requests.
+ */
+ NOTICE("Cactus: Signal end of init to SPM\n");
+ event_status_code = SPM_SUCCESS;
+
+ while (1) {
+ svc_values.fid = SP_EVENT_COMPLETE_AARCH64;
+ svc_values.arg1 = event_status_code;
+ int32_t event_id = sp_svc(&svc_values);
+
+ switch (event_id) {
+ case MM_COMMUNICATE_AARCH64:
+ {
+ uint64_t ctx_addr = svc_values.arg1;
+ uint32_t ctx_size = svc_values.arg2;
+ uint64_t cookie = svc_values.arg3;
+
+ NOTICE("Cactus: Received MM_COMMUNICATE_AARCH64 call\n");
+ NOTICE("Cactus: Context address: 0x%llx\n", ctx_addr);
+ NOTICE("Cactus: Context size : %u\n", ctx_size);
+ NOTICE("Cactus: Cookie : 0x%llx\n", cookie);
+
+ if (ctx_addr == 0) {
+ ERROR("Context address is invalid\n");
+ event_status_code = SPM_INVALID_PARAMETER;
+ continue;
+ }
+
+ secure_partition_request_info_t *sps = (void *)(uintptr_t) ctx_addr;
+ NOTICE("Received fast secure service request with ID #%u\n",
+ sps->id);
+ event_status_code = cactus_handle_fast_request(64, sps);
+ break;
+ }
+
+ case MM_COMMUNICATE_AARCH32:
+ {
+ uint32_t ctx_addr = svc_values.arg1;
+ uint32_t ctx_size = svc_values.arg2;
+ uint32_t cookie = svc_values.arg3;
+
+ NOTICE("Cactus: Received MM_COMMUNICATE_AARCH32 call\n");
+ NOTICE("Cactus: Context address: 0x%x\n", ctx_addr);
+ NOTICE("Cactus: Context size : %u\n", ctx_size);
+ NOTICE("Cactus: Cookie : 0x%x\n", cookie);
+
+ if (ctx_addr == 0) {
+ ERROR("Context address is invalid\n");
+ event_status_code = SPM_INVALID_PARAMETER;
+ continue;
+ }
+
+ secure_partition_request_info_t *sps = (void *)(uintptr_t) ctx_addr;
+ NOTICE("Received fast secure service request with ID #%u\n",
+ sps->id);
+ event_status_code = cactus_handle_fast_request(32, sps);
+ break;
+ }
+
+ default:
+ NOTICE("Unhandled Service ID 0x%x\n", event_id);
+ event_status_code = SPM_NOT_SUPPORTED;
+ break;
+ }
+ }
+}
diff --git a/spm/cactus_mm/cactus_mm_tests.h b/spm/cactus_mm/cactus_mm_tests.h
new file mode 100644
index 00000000..d0e11dcb
--- /dev/null
+++ b/spm/cactus_mm/cactus_mm_tests.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __CACTUS_TESTS_H__
+#define __CACTUS_TESTS_H__
+
+#include <secure_partition.h>
+
+/*
+ * Test functions
+ */
+
+/*
+ * Test other things like the version number returned by SPM.
+ */
+void misc_tests(void);
+
+/*
+ * The Arm TF is responsible for setting up system registers on behalf of the
+ * Secure Partition. For example, TF is supposed to allow Secure Partitions to
+ * perform cache maintenance operations (by setting the SCTLR_EL1.UCI bit).
+ *
+ * This function attempts to verify that we indeed have access to these system
+ * features from S-EL0. These tests report their results on the UART. They do
+ * not recover from a failure : when an error is encountered they will most
+ * likely trigger an exception into S-EL1.
+ */
+void system_setup_tests(void);
+
+/*
+ * Exercise the SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC interface. A variety of
+ * valid and invalid requests to change memory attributes are tested.
+ *
+ * These tests report their results on the UART. They do not recover from a
+ * failure : when an error is encountered they endlessly loop.
+ *
+ * The argument is a pointer to a secure_partition_boot_info_t struct that has
+ * been filled by EL3 with the information about the memory map of this Secure
+ * Partition.
+ */
+void mem_attr_changes_tests(const secure_partition_boot_info_t *boot_info);
+
+#endif /* __CACTUS_TESTS_H__ */
diff --git a/spm/cactus_mm/cactus_mm_tests_memory_attributes.c b/spm/cactus_mm/cactus_mm_tests_memory_attributes.c
new file mode 100644
index 00000000..1a87f6cf
--- /dev/null
+++ b/spm/cactus_mm/cactus_mm_tests_memory_attributes.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <platform_def.h>
+#include <secure_partition.h>
+#include <sp_helpers.h>
+#include <spm_svc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <xlat_tables_defs.h>
+
+#include "cactus_mm.h"
+#include "cactus_mm_tests.h"
+
+/* This is filled at runtime. */
+static uintptr_t cactus_tests_start;
+static uintptr_t cactus_tests_end;
+static uintptr_t cactus_tests_size;
+
+/*
+ * Given the required instruction and data access permissions,
+ * create a memory access controls value that is formatted as expected
+ * by the SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC.
+ */
+static inline uint32_t mem_access_perm(int instr_access_perm,
+ int data_access_perm)
+{
+ return instr_access_perm |
+ ((data_access_perm & SP_MEMORY_ATTRIBUTES_ACCESS_MASK)
+ << SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT);
+}
+
+/*
+ * Send an SP_MEMORY_ATTRIBUTES_SET_AARCH64 SVC with the given arguments.
+ * Return the return value of the SVC.
+ */
+static int32_t request_mem_attr_changes(uintptr_t base_address,
+ int pages_count,
+ uint32_t memory_access_controls)
+{
+ INFO("Requesting memory attributes change\n");
+ INFO(" Start address : %p\n", (void *) base_address);
+ INFO(" Number of pages: %i\n", pages_count);
+ INFO(" Attributes : 0x%x\n", memory_access_controls);
+
+ svc_args svc_values = { SP_MEMORY_ATTRIBUTES_SET_AARCH64,
+ base_address,
+ pages_count,
+ memory_access_controls };
+ return sp_svc(&svc_values);
+}
+
+/*
+ * Send an SP_MEMORY_ATTRIBUTES_GET_AARCH64 SVC with the given arguments.
+ * Return the return value of the SVC.
+ */
+static int32_t request_get_mem_attr(uintptr_t base_address)
+{
+ INFO("Requesting memory attributes\n");
+ INFO(" Base address : %p\n", (void *) base_address);
+
+ svc_args svc_values = { SP_MEMORY_ATTRIBUTES_GET_AARCH64,
+ base_address };
+ return sp_svc(&svc_values);
+}
+
+/*
+ * This function expects a base address and number of pages identifying the
+ * extents of some memory region mapped as non-executable, read-only.
+ *
+ * 1) It changes its data access permissions to read-write.
+ * 2) It checks this memory can now be written to.
+ * 3) It restores the original data access permissions.
+ *
+ * If any check fails, it loops forever. It could also trigger a permission
+ * fault while trying to write to the memory.
+ */
+static void mem_attr_changes_unittest(uintptr_t addr, int pages_count)
+{
+ int32_t ret;
+ uintptr_t end_addr = addr + pages_count * PAGE_SIZE;
+ uint32_t old_attr, new_attr;
+
+ char test_desc[50];
+
+ snprintf(test_desc, sizeof(test_desc),
+ "RO -> RW (%i page(s) from address 0x%lx)", pages_count, addr);
+ announce_test_start(test_desc);
+
+ /*
+ * Ensure we don't change the attributes of some random memory
+ * location
+ */
+ assert(addr >= cactus_tests_start);
+ assert(end_addr < (cactus_tests_start + cactus_tests_size));
+
+ old_attr = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RO);
+ /* Memory was read-only, let's try changing that to RW */
+ new_attr = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
+
+ ret = request_mem_attr_changes(addr, pages_count, new_attr);
+ expect(ret, SPM_SUCCESS);
+ printf("Successfully changed memory attributes\n");
+
+ /* The attributes should be the ones we have just written. */
+ ret = request_get_mem_attr(addr);
+ expect(ret, new_attr);
+
+ /* If it worked, we should be able to write to this memory now! */
+ for (unsigned char *data = (unsigned char *) addr;
+ (uintptr_t) data != end_addr;
+ ++data) {
+ *data = 42;
+ }
+ printf("Successfully wrote to the memory\n");
+
+ /* Let's revert back to the original attributes for the next test */
+ ret = request_mem_attr_changes(addr, pages_count, old_attr);
+ expect(ret, SPM_SUCCESS);
+ printf("Successfully restored the old attributes\n");
+
+ /* The attributes should be the original ones again. */
+ ret = request_get_mem_attr(addr);
+ expect(ret, old_attr);
+
+ announce_test_end(test_desc);
+}
+
+/*
+ * Exercise the ability of the Trusted Firmware to change the data access
+ * permissions and instruction execution permissions of some memory region.
+ */
+void mem_attr_changes_tests(const secure_partition_boot_info_t *boot_info)
+{
+ uint32_t attributes;
+ int32_t ret;
+ uintptr_t addr;
+
+ cactus_tests_start = CACTUS_BSS_END;
+ cactus_tests_end = boot_info->sp_image_base + boot_info->sp_image_size;
+ cactus_tests_size = cactus_tests_end - cactus_tests_start;
+
+ const char *test_sect_desc = "memory attributes changes";
+
+ announce_test_section_start(test_sect_desc);
+ /*
+ * Start with error cases, i.e. requests that are expected to be denied
+ */
+ const char *test_desc1 = "Read-write, executable";
+
+ announce_test_start(test_desc1);
+ attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
+ ret = request_mem_attr_changes(CACTUS_RWDATA_START, 1, attributes);
+ expect(ret, SPM_INVALID_PARAMETER);
+ announce_test_end(test_desc1);
+
+ const char *test_desc2 = "Size == 0";
+
+ announce_test_start(test_desc2);
+ attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
+ ret = request_mem_attr_changes(CACTUS_RWDATA_START, 0, attributes);
+ expect(ret, SPM_INVALID_PARAMETER);
+ announce_test_end(test_desc2);
+
+ const char *test_desc3 = "Unaligned address";
+
+ announce_test_start(test_desc3);
+ attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
+ /* Choose an address not aligned to a page boundary. */
+ addr = cactus_tests_start + 5;
+ ret = request_mem_attr_changes(addr, 1, attributes);
+ expect(ret, SPM_INVALID_PARAMETER);
+ announce_test_end(test_desc3);
+
+ const char *test_desc4 = "Unmapped memory region";
+
+ announce_test_start(test_desc4);
+ addr = boot_info->sp_mem_limit + 2 * PAGE_SIZE;
+ attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
+ ret = request_mem_attr_changes(addr, 3, attributes);
+ expect(ret, SPM_INVALID_PARAMETER);
+ announce_test_end(test_desc4);
+
+ const char *test_desc5 = "Partially unmapped memory region";
+
+ announce_test_start(test_desc5);
+ addr = boot_info->sp_mem_base - 2 * PAGE_SIZE;
+ attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
+ ret = request_mem_attr_changes(addr, 6, attributes);
+ expect(ret, SPM_INVALID_PARAMETER);
+ announce_test_end(test_desc5);
+
+ const char *test_desc6 = "Memory region mapped with the wrong granularity";
+
+ announce_test_start(test_desc6);
+ /*
+ * This address is usually mapped at a 2 MiB granularity. By using as
+ * test address the block after the console we make sure that in case
+ * the attributes of the block actually changed, the console would work
+ * and we would get the error message.
+ */
+ addr = ((uintptr_t)PLAT_ARM_UART_BASE + 0x200000ULL) & ~(0x200000ULL - 1ULL);
+ attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
+ ret = request_mem_attr_changes(addr, 1, attributes);
+ expect(ret, SPM_INVALID_PARAMETER);
+ announce_test_end(test_desc6);
+
+ const char *test_desc7 = "Try some valid memory change requests";
+
+ announce_test_start(test_desc7);
+ for (unsigned int i = 0; i < 20; ++i) {
+ /*
+ * Choose some random address in the pool of memory reserved
+ * for these tests.
+ */
+ const int pages_max = cactus_tests_size / PAGE_SIZE;
+ int pages_count = bound_rand(1, pages_max);
+
+ addr = bound_rand(
+ cactus_tests_start,
+ cactus_tests_end - (pages_count * PAGE_SIZE));
+ /* Align to PAGE_SIZE. */
+ addr &= ~(PAGE_SIZE - 1);
+
+ mem_attr_changes_unittest(addr, pages_count);
+ }
+ announce_test_end(test_desc7);
+
+ announce_test_section_end(test_sect_desc);
+}
diff --git a/spm/cactus_mm/cactus_mm_tests_misc.c b/spm/cactus_mm/cactus_mm_tests_misc.c
new file mode 100644
index 00000000..1d4d18a5
--- /dev/null
+++ b/spm/cactus_mm/cactus_mm_tests_misc.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <sp_helpers.h>
+#include <spm_svc.h>
+#include <stdint.h>
+
+#include "cactus_mm.h"
+#include "cactus_mm_tests.h"
+
+/*
+ * Miscellaneous SPM tests.
+ */
+void misc_tests(void)
+{
+ int32_t ret;
+
+ const char *test_sect_desc = "miscellaneous";
+
+ announce_test_section_start(test_sect_desc);
+
+ const char *test_version = "SPM version check";
+
+ announce_test_start(test_version);
+ svc_args svc_values = { SPM_VERSION_AARCH32 };
+ ret = sp_svc(&svc_values);
+ INFO("Version = 0x%x (%u.%u)\n", ret,
+ (ret >> 16) & 0x7FFF, ret & 0xFFFF);
+ expect(ret, SPM_VERSION_COMPILED);
+ announce_test_end(test_version);
+
+ announce_test_section_end(test_sect_desc);
+}
diff --git a/spm/cactus_mm/cactus_mm_tests_system_setup.c b/spm/cactus_mm/cactus_mm_tests_system_setup.c
new file mode 100644
index 00000000..300d5f67
--- /dev/null
+++ b/spm/cactus_mm/cactus_mm_tests_system_setup.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <sp_helpers.h>
+#include <stdint.h>
+
+#include "cactus_mm.h"
+
+extern uintptr_t __TEXT_START__;
+
+void system_setup_tests(void)
+{
+ const char *test_sect_desc = "system setup";
+
+ announce_test_section_start(test_sect_desc);
+
+ /*
+ * Try accessing CTR_EL0 register. This should work if SCTLR_EL1.UCT bit
+ * has been correctly setup by TF.
+ */
+ const char *test_desc1 = "Read CTR_EL0 register";
+
+ announce_test_start(test_desc1);
+
+ uint32_t ctr __unused = read_ctr_el0();
+
+ INFO("CTR_EL0 = 0x%x\n", ctr);
+ announce_test_end(test_desc1);
+
+ /*
+ * Try to execute a cache maintenance instruction. This should work if
+ * SCTLR_EL1.UCI bit has been correctly setup by TF.
+ */
+ const char *test_desc2 = "Access to cache maintenance operations";
+
+ announce_test_start(test_desc2);
+ flush_dcache_range((uintptr_t)&__TEXT_START__, 1);
+ announce_test_end(test_desc2);
+
+ /*
+ * Try accessing a floating point register. This should not trap to
+ * S-EL1.
+ */
+ const char *test_desc3 = "Access to FP regs";
+
+ announce_test_start(test_desc3);
+ /*
+ * Can't use the 'double' type here because Cactus (like the rest of
+ * the TF code) is compiled with GCC's -mgeneral-regs-only compiler flag
+ * that disables floating point support in GCC.
+ */
+ uint64_t fp_reg;
+
+ __asm__ volatile("fmov %0, d0" : "=r" (fp_reg) :: "d0");
+ INFO("D0 = 0x%llx\n", fp_reg);
+ __asm__ volatile(
+ "fmov d0, #1.0 \n\t"
+ "fmov %0, d0 \n\t"
+ : "=r" (fp_reg)
+ :
+ : "d0");
+ INFO("D0 = 0x%llx\n", fp_reg);
+ announce_test_end(test_desc3);
+
+ announce_test_section_end(test_sect_desc);
+}
diff --git a/tftf/tests/runtime_services/mm_service/secure_service_helpers.c b/tftf/tests/runtime_services/mm_service/secure_service_helpers.c
new file mode 100644
index 00000000..8675daee
--- /dev/null
+++ b/tftf/tests/runtime_services/mm_service/secure_service_helpers.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <platform.h>
+#include <secure_partition.h>
+#include <string.h>
+
+
+secure_partition_request_info_t *create_sps_request(uint32_t id,
+ const void *data,
+ uint64_t data_size)
+{
+ secure_partition_request_info_t *sps_request
+ = (void *) ARM_SECURE_SERVICE_BUFFER_BASE;
+ sps_request->id = id;
+ sps_request->data_size = data_size;
+ if (data_size != 0)
+ memcpy(sps_request->data, data, data_size);
+ return sps_request;
+}
diff --git a/tftf/tests/runtime_services/mm_service/test_secure_service_handle.c b/tftf/tests/runtime_services/mm_service/test_secure_service_handle.c
new file mode 100644
index 00000000..ce4dd5c2
--- /dev/null
+++ b/tftf/tests/runtime_services/mm_service/test_secure_service_handle.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <events.h>
+#include <mm_svc.h>
+#include <plat_topology.h>
+#include <platform.h>
+#include <power_management.h>
+#include <secure_partition.h>
+#include <smccc.h>
+#include <spm_svc.h>
+#include <test_helpers.h>
+#include <tftf_lib.h>
+#include <xlat_tables_v2.h>
+
+static event_t cpu_has_finished_test[PLATFORM_CORE_COUNT];
+
+/* Test routine for test_secure_partition_secondary_cores_seq() */
+static test_result_t test_secure_partition_secondary_cores_seq_fn(void)
+{
+ test_result_t result = TEST_RESULT_SUCCESS;
+ u_register_t cpu_mpid = read_mpidr_el1() & MPID_MASK;
+ unsigned int core_pos = platform_get_core_pos(cpu_mpid);
+
+ secure_partition_request_info_t *sps_request =
+ create_sps_request(SPS_CHECK_ALIVE, NULL, 0);
+
+ INFO("Sending MM_COMMUNICATE_AARCH64 from CPU %u\n",
+ platform_get_core_pos(read_mpidr_el1() & MPID_MASK));
+
+ smc_args mm_communicate_smc = {
+ MM_COMMUNICATE_AARCH64,
+ 0,
+ (u_register_t) sps_request,
+ 0
+ };
+
+ smc_ret_values smc_ret = tftf_smc(&mm_communicate_smc);
+
+ if ((uint32_t)smc_ret.ret0 != 0) {
+ tftf_testcase_printf("Cactus returned: 0x%x\n",
+ (uint32_t)smc_ret.ret0);
+
+ result = TEST_RESULT_FAIL;
+ }
+
+ tftf_send_event(&cpu_has_finished_test[core_pos]);
+
+ return result;
+}
+
+/*
+ * @Test_Aim@ This tests that secondary CPUs can access SPM services
+ * sequentially.
+ */
+test_result_t test_secure_partition_secondary_cores_seq(void)
+{
+ int psci_ret;
+ u_register_t lead_mpid, cpu_mpid;
+ unsigned int cpu_node, core_pos;
+ test_result_t result = TEST_RESULT_SUCCESS;
+
+ SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
+
+ SKIP_TEST_IF_MM_VERSION_LESS_THAN(1, 0);
+
+ VERBOSE("Mapping NS<->SP shared buffer\n");
+
+ int rc = mmap_add_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE,
+ ARM_SECURE_SERVICE_BUFFER_BASE,
+ ARM_SECURE_SERVICE_BUFFER_SIZE,
+ MT_MEMORY | MT_RW | MT_NS);
+ if (rc != 0) {
+ tftf_testcase_printf("%d: mmap_add_dynamic_region() = %d\n",
+ __LINE__, rc);
+ result = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ lead_mpid = read_mpidr_el1() & MPID_MASK;
+
+ INFO("Lead CPU is CPU %u\n", platform_get_core_pos(lead_mpid));
+
+ if (test_secure_partition_secondary_cores_seq_fn() != TEST_RESULT_SUCCESS) {
+ result = TEST_RESULT_FAIL;
+ goto exit_unmap;
+ }
+
+ for_each_cpu(cpu_node) {
+ cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
+ /* Skip lead CPU, we have already tested it */
+ if (cpu_mpid == lead_mpid) {
+ continue;
+ }
+
+ core_pos = platform_get_core_pos(cpu_mpid);
+
+ tftf_init_event(&cpu_has_finished_test[core_pos]);
+
+ VERBOSE("Powering on CPU %u\n", core_pos);
+
+ psci_ret = tftf_cpu_on(cpu_mpid,
+ (uintptr_t)test_secure_partition_secondary_cores_seq_fn, 0);
+ if (psci_ret != PSCI_E_SUCCESS) {
+ tftf_testcase_printf(
+ "Failed to power on CPU %d (rc = %d)\n",
+ core_pos, psci_ret);
+ result = TEST_RESULT_FAIL;
+ goto exit_unmap;
+ }
+
+ tftf_wait_for_event(&cpu_has_finished_test[core_pos]);
+ }
+
+exit_unmap:
+ VERBOSE("Unmapping NS<->SP shared buffer\n");
+
+ mmap_remove_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE,
+ ARM_SECURE_SERVICE_BUFFER_SIZE);
+
+exit:
+ return result;
+}
+
+/******************************************************************************/
+
+static event_t cpu_can_start_test[PLATFORM_CORE_COUNT];
+
+/* Test routine for test_secure_partition_secondary_core() */
+static test_result_t test_secure_partition_secondary_cores_sim_fn(void)
+{
+ test_result_t result = TEST_RESULT_SUCCESS;
+ u_register_t cpu_mpid = read_mpidr_el1() & MPID_MASK;
+ unsigned int core_pos = platform_get_core_pos(cpu_mpid);
+
+ secure_partition_request_info_t *sps_request =
+ create_sps_request(SPS_CHECK_ALIVE, NULL, 0);
+
+ smc_args mm_communicate_smc = {
+ MM_COMMUNICATE_AARCH64,
+ 0,
+ (u_register_t) sps_request,
+ 0
+ };
+
+ tftf_wait_for_event(&cpu_can_start_test[core_pos]);
+
+ /*
+ * Invoke SMCs for some time to make sure that all CPUs are doing it at
+ * the same time during the test.
+ */
+ for (int i = 0; i < 100; i++) {
+ smc_ret_values smc_ret = tftf_smc(&mm_communicate_smc);
+
+ if ((uint32_t)smc_ret.ret0 != 0) {
+ tftf_testcase_printf("Cactus returned 0x%x at CPU %d\n",
+ (uint32_t)smc_ret.ret0, core_pos);
+ result = TEST_RESULT_FAIL;
+ break;
+ }
+ }
+
+ tftf_send_event(&cpu_has_finished_test[core_pos]);
+
+ return result;
+}
+
+/*
+ * @Test_Aim@ This tests that secondary CPUs can access SPM services
+ * simultaneously.
+ */
+test_result_t test_secure_partition_secondary_cores_sim(void)
+{
+ int psci_ret;
+ u_register_t lead_mpid, cpu_mpid;
+ unsigned int cpu_node, core_pos;
+ test_result_t result = TEST_RESULT_SUCCESS;
+
+ SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
+
+ SKIP_TEST_IF_MM_VERSION_LESS_THAN(1, 0);
+
+ VERBOSE("Mapping NS<->SP shared buffer\n");
+
+ int rc = mmap_add_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE,
+ ARM_SECURE_SERVICE_BUFFER_BASE,
+ ARM_SECURE_SERVICE_BUFFER_SIZE,
+ MT_MEMORY | MT_RW | MT_NS);
+ if (rc != 0) {
+ tftf_testcase_printf("%d: mmap_add_dynamic_region() = %d\n",
+ __LINE__, rc);
+ result = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ lead_mpid = read_mpidr_el1() & MPID_MASK;
+
+ INFO("Lead CPU is CPU %u\n", platform_get_core_pos(lead_mpid));
+
+ for_each_cpu(cpu_node) {
+ cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
+ core_pos = platform_get_core_pos(cpu_mpid);
+ tftf_init_event(&cpu_can_start_test[core_pos]);
+ }
+
+ for_each_cpu(cpu_node) {
+ cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
+ /* Skip lead CPU as it is already powered on */
+ if (cpu_mpid == lead_mpid) {
+ continue;
+ }
+
+ core_pos = platform_get_core_pos(cpu_mpid);
+
+ VERBOSE("Powering on CPU %u\n", core_pos);
+
+ psci_ret = tftf_cpu_on(cpu_mpid,
+ (uintptr_t)test_secure_partition_secondary_cores_sim_fn, 0);
+ if (psci_ret != PSCI_E_SUCCESS) {
+ tftf_testcase_printf(
+ "Failed to power on CPU %d (rc = %d)\n",
+ core_pos, psci_ret);
+ result = TEST_RESULT_FAIL;
+ goto exit_unmap;
+ }
+ }
+
+ for_each_cpu(cpu_node) {
+ cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
+ core_pos = platform_get_core_pos(cpu_mpid);
+ tftf_send_event(&cpu_can_start_test[core_pos]);
+ }
+
+ result = test_secure_partition_secondary_cores_sim_fn();
+
+ /* Wait until all CPUs have finished to unmap the NS<->SP buffer */
+ for_each_cpu(cpu_node) {
+ cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
+ core_pos = platform_get_core_pos(cpu_mpid);
+ tftf_wait_for_event(&cpu_has_finished_test[core_pos]);
+ }
+exit_unmap:
+ VERBOSE("Unmapping NS<->SP shared buffer\n");
+
+ mmap_remove_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE,
+ ARM_SECURE_SERVICE_BUFFER_SIZE);
+exit:
+ return result;
+}
diff --git a/tftf/tests/runtime_services/mm_service/test_secure_service_interrupts.c b/tftf/tests/runtime_services/mm_service/test_secure_service_interrupts.c
new file mode 100644
index 00000000..50c3df67
--- /dev/null
+++ b/tftf/tests/runtime_services/mm_service/test_secure_service_interrupts.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <mm_svc.h>
+#include <secure_partition.h>
+#include <smccc.h>
+#include <spm_svc.h>
+#include <string.h>
+#include <test_helpers.h>
+#include <tftf_lib.h>
+#include <timer.h>
+#include <xlat_tables_v2.h>
+
+static volatile int timer_irq_received;
+
+/*
+ * ISR for the timer interrupt.
+ * Just update a global variable to prove it has been called.
+ */
+static int timer_handler(void *data)
+{
+ assert(timer_irq_received == 0);
+ timer_irq_received = 1;
+ return 0;
+}
+
+
+/*
+ * @Test_Aim@ Test that non-secure interrupts do not interrupt secure service
+ * requests.
+ *
+ * 1. Register a handler for the non-secure timer interrupt.
+ *
+ * 2. Program the non-secure timer to fire in 500 ms.
+ *
+ * 3. Make a long-running (> 500 ms) fast secure service request.
+ * This is achieved by requesting the timer sleep service in Cactus
+ * with a 1 second sleep delay.
+ *
+ * 4. While servicing the timer sleep request, the non-secure timer should
+ * fire but not interrupt Cactus.
+ *
+ * 5. Once back in TFTF, check the response from Cactus, which shows whether the
+ * secure service indeed ran to completion.
+ *
+ * 6. Also check whether the pending non-secure timer interrupt successfully got
+ * handled in TFTF.
+ */
+test_result_t test_secure_partition_interrupt_by_ns(void)
+{
+ secure_partition_request_info_t *sps_request;
+ test_result_t result = TEST_RESULT_FAIL;
+
+ SKIP_TEST_IF_MM_VERSION_LESS_THAN(1, 0);
+
+ VERBOSE("Mapping NS<->SP shared buffer\n");
+
+ int rc = mmap_add_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE,
+ ARM_SECURE_SERVICE_BUFFER_BASE,
+ ARM_SECURE_SERVICE_BUFFER_SIZE,
+ MT_MEMORY | MT_RW | MT_NS);
+ if (rc != 0) {
+ tftf_testcase_printf("%d: mmap_add_dynamic_region() = %d\n",
+ __LINE__, rc);
+ return TEST_RESULT_FAIL;
+ }
+
+ timer_irq_received = 0;
+ tftf_timer_register_handler(timer_handler);
+
+ NOTICE("Programming the timer...\n");
+ rc = tftf_program_timer(500);
+ if (rc < 0) {
+ tftf_testcase_printf("Failed to program timer (%d)\n", rc);
+ goto exit_test;
+ }
+
+ INFO("Sending MM_COMMUNICATE_AARCH64 to Cactus\n");
+
+ uint8_t timer_delay = 1;
+ sps_request = create_sps_request(SPS_TIMER_SLEEP,
+ &timer_delay, sizeof(timer_delay));
+ smc_args mm_communicate_smc = {
+ MM_COMMUNICATE_AARCH64,
+ 0, /* cookie, MBZ */
+ (uintptr_t) sps_request,
+ 0
+ };
+
+ smc_ret_values smc_ret = tftf_smc(&mm_communicate_smc);
+
+ INFO("Returned from Cactus, MM_COMMUNICATE_AARCH64 handling complete\n");
+
+ /*
+ * If MM_COMMUNICATE gets interrupted, SPM will return SPM_QUEUED, which
+ * is normally not a valid return value for MM_COMMUNICATE.
+ */
+ if ((uint32_t) smc_ret.ret0 != SPM_SUCCESS) {
+ tftf_testcase_printf("Cactus returned: 0x%x\n",
+ (uint32_t) smc_ret.ret0);
+ goto exit_test;
+ }
+
+ uint32_t cactus_response;
+ memcpy(&cactus_response, sps_request->data, sizeof(cactus_response));
+ if (cactus_response != CACTUS_FAST_REQUEST_SUCCESS) {
+ tftf_testcase_printf("Error code from the timer secure service: 0x%x\n",
+ cactus_response);
+ goto exit_test;
+ }
+
+ /*
+ * If the timer interrupt is still pending, make sure it is taken right
+ * now.
+ */
+ isb();
+
+ if (timer_irq_received == 1)
+ result = TEST_RESULT_SUCCESS;
+
+exit_test:
+ tftf_cancel_timer();
+ tftf_timer_unregister_handler();
+
+ VERBOSE("Unmapping NS<->SP shared buffer\n");
+
+ mmap_remove_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE,
+ ARM_SECURE_SERVICE_BUFFER_SIZE);
+
+ return result;
+}
diff --git a/tftf/tests/tests-spm-mm.mk b/tftf/tests/tests-spm-mm.mk
new file mode 100644
index 00000000..1a6cb3cd
--- /dev/null
+++ b/tftf/tests/tests-spm-mm.mk
@@ -0,0 +1,12 @@
+#
+# Copyright (c) 2018, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+TESTS_SOURCES += \
+ $(addprefix tftf/tests/runtime_services/mm_service/, \
+ secure_service_helpers.c \
+ test_secure_service_handle.c \
+ test_secure_service_interrupts.c \
+ )
diff --git a/tftf/tests/tests-spm-mm.xml b/tftf/tests/tests-spm-mm.xml
new file mode 100644
index 00000000..fc7d98e1
--- /dev/null
+++ b/tftf/tests/tests-spm-mm.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+-->
+
+<testsuites>
+
+ <testsuite name="SPM tests"
+ description="Test SPM APIs">
+<!-- TODO: This test fails in the CI.
+ <testcase name="SPM NS interrupts test"
+ function="test_secure_partition_interrupt_by_ns" />
+-->
+ <testcase name="SPM secondary CPUs sequential test"
+ function="test_secure_partition_secondary_cores_seq" />
+<!-- TODO: This test fails in the CI.
+ <testcase name="SPM secondary CPUs simultaneous test"
+ function="test_secure_partition_secondary_cores_sim" />
+-->
+ </testsuite>
+
+</testsuites>