aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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>