Trusted Firmware-A Tests, version 2.0

This is the first public version of the tests for the Trusted
Firmware-A project. Please see the documentation provided in the
source tree for more details.

Change-Id: I6f3452046a1351ac94a71b3525c30a4ca8db7867
Signed-off-by: Sandrine Bailleux <sandrine.bailleux@arm.com>
Co-authored-by: amobal01 <amol.balasokamble@arm.com>
Co-authored-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
Co-authored-by: Asha R <asha.r@arm.com>
Co-authored-by: Chandni Cherukuri <chandni.cherukuri@arm.com>
Co-authored-by: David Cunado <david.cunado@arm.com>
Co-authored-by: Dimitris Papastamos <dimitris.papastamos@arm.com>
Co-authored-by: Douglas Raillard <douglas.raillard@arm.com>
Co-authored-by: dp-arm <dimitris.papastamos@arm.com>
Co-authored-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
Co-authored-by: Jonathan Wright <jonathan.wright@arm.com>
Co-authored-by: Kévin Petit <kevin.petit@arm.com>
Co-authored-by: Roberto Vargas <roberto.vargas@arm.com>
Co-authored-by: Sathees Balya <sathees.balya@arm.com>
Co-authored-by: Shawon Roy <Shawon.Roy@arm.com>
Co-authored-by: Soby Mathew <soby.mathew@arm.com>
Co-authored-by: Thomas Abraham <thomas.abraham@arm.com>
Co-authored-by: Vikram Kanigiri <vikram.kanigiri@arm.com>
Co-authored-by: Yatharth Kochar <yatharth.kochar@arm.com>
diff --git a/spm/cactus/aarch64/cactus_entrypoint.S b/spm/cactus/aarch64/cactus_entrypoint.S
new file mode 100644
index 0000000..704905e
--- /dev/null
+++ b/spm/cactus/aarch64/cactus_entrypoint.S
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2017-2018, 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_entrypoint
+
+func cactus_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_entrypoint
diff --git a/spm/cactus/cactus.h b/spm/cactus/cactus.h
new file mode 100644
index 0000000..966b154
--- /dev/null
+++ b/spm/cactus/cactus.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __CACTUS_H__
+#define __CACTUS_H__
+
+#include <secure_partition.h>
+#include <types.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/cactus.ld.S b/spm/cactus/cactus.ld.S
new file mode 100644
index 0000000..b33f591
--- /dev/null
+++ b/spm/cactus/cactus.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_entrypoint)
+
+SECTIONS
+{
+    ASSERT(. == ALIGN(PAGE_SIZE),
+           "TEXT_START address is not aligned to PAGE_SIZE.")
+
+    .text : {
+        __TEXT_START__ = .;
+        *cactus_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/cactus.mk b/spm/cactus/cactus.mk
new file mode 100644
index 0000000..f7794db
--- /dev/null
+++ b/spm/cactus/cactus.mk
@@ -0,0 +1,73 @@
+#
+# Copyright (c) 2018, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+CACTUS_INCLUDES :=					\
+	-Iinclude					\
+	-Iinclude/common				\
+	-Iinclude/common/${ARCH}			\
+	-Iinclude/drivers				\
+	-Iinclude/drivers/arm				\
+	-Iinclude/lib					\
+	-Iinclude/lib/${ARCH}				\
+	-Iinclude/lib/stdlib				\
+	-Iinclude/lib/stdlib/sys			\
+	-Iinclude/lib/utils				\
+	-Iinclude/lib/xlat_tables			\
+	-Iinclude/runtime_services			\
+	-Iinclude/runtime_services/secure_el0_payloads	\
+	-Ispm/cactus					\
+	-Ispm/common					\
+
+CACTUS_SOURCES	:=					\
+	$(addprefix spm/cactus/,			\
+		aarch64/cactus_entrypoint.S		\
+		cactus_main.c				\
+		cactus_service_loop.c			\
+		cactus_tests_memory_attributes.c	\
+		cactus_tests_misc.c			\
+		cactus_tests_system_setup.c		\
+	)						\
+	$(addprefix spm/common/,			\
+		aarch64/sp_arch_helpers.S		\
+		sp_helpers.c				\
+	)						\
+
+STDLIB_SOURCES	:=	$(addprefix lib/stdlib/,	\
+	assert.c					\
+	mem.c						\
+	putchar.c					\
+	printf.c					\
+	rand.c						\
+	strlen.c					\
+	subr_prf.c					\
+)
+
+# TODO: Remove dependency on TFTF files.
+CACTUS_SOURCES	+=					\
+	tftf/framework/debug.c				\
+	tftf/framework/${ARCH}/asm_debug.S
+
+CACTUS_SOURCES	+= 	drivers/arm/pl011/${ARCH}/pl011_console.S	\
+			lib/${ARCH}/cache_helpers.S			\
+			lib/${ARCH}/misc_helpers.S			\
+			plat/common/${ARCH}/platform_helpers.S		\
+			${STDLIB_SOURCES}
+
+CACTUS_LINKERFILE	:=	spm/cactus/cactus.ld.S
+
+CACTUS_DEFINES	:=
+
+$(eval $(call add_define,CACTUS_DEFINES,DEBUG))
+$(eval $(call add_define,CACTUS_DEFINES,ENABLE_ASSERTIONS))
+$(eval $(call add_define,CACTUS_DEFINES,LOG_LEVEL))
+$(eval $(call add_define,CACTUS_DEFINES,PLAT_${PLAT}))
+ifeq (${ARCH},aarch32)
+        $(eval $(call add_define,CACTUS_DEFINES,AARCH32))
+else
+        $(eval $(call add_define,CACTUS_DEFINES,AARCH64))
+endif
+
+cactus: ${AUTOGEN_DIR}/tests_list.h
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
new file mode 100644
index 0000000..45197d3
--- /dev/null
+++ b/spm/cactus/cactus_main.c
@@ -0,0 +1,124 @@
+/*
+ * 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 <plat_arm.h>
+#include <platform_def.h>
+#include <secure_partition.h>
+#include <sp_helpers.h>
+#include <spm_svc.h>
+#include <std_svc.h>
+
+#include "cactus.h"
+#include "cactus_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(PLAT_ARM_UART_BASE,
+		     PLAT_ARM_UART_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();
+	mem_attr_changes_tests(boot_info);
+
+	/*
+	 * Handle secure service requests.
+	 */
+	secure_services_loop();
+}
diff --git a/spm/cactus/cactus_service_loop.c b/spm/cactus/cactus_service_loop.c
new file mode 100644
index 0000000..9f542b5
--- /dev/null
+++ b/spm/cactus/cactus_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.arg0 = 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/cactus_tests.h b/spm/cactus/cactus_tests.h
new file mode 100644
index 0000000..d0e11dc
--- /dev/null
+++ b/spm/cactus/cactus_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/cactus_tests_memory_attributes.c b/spm/cactus/cactus_tests_memory_attributes.c
new file mode 100644
index 0000000..1a3072b
--- /dev/null
+++ b/spm/cactus/cactus_tests_memory_attributes.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2018, 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 <stdio.h>
+#include <types.h>
+#include <xlat_tables_defs.h>
+
+#include "cactus.h"
+#include "cactus_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/cactus_tests_misc.c b/spm/cactus/cactus_tests_misc.c
new file mode 100644
index 0000000..bb8eaa7
--- /dev/null
+++ b/spm/cactus/cactus_tests_misc.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018, 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 <types.h>
+
+#include "cactus.h"
+#include "cactus_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/cactus_tests_system_setup.c b/spm/cactus/cactus_tests_system_setup.c
new file mode 100644
index 0000000..685d82d
--- /dev/null
+++ b/spm/cactus/cactus_tests_system_setup.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <sp_helpers.h>
+#include <types.h>
+
+#include "cactus.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/spm/common/aarch64/sp_arch_helpers.S b/spm/common/aarch64/sp_arch_helpers.S
new file mode 100644
index 0000000..11f4087
--- /dev/null
+++ b/spm/common/aarch64/sp_arch_helpers.S
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <asm_macros.S>
+
+	.globl	sp_svc
+
+func sp_svc
+	/*
+	 * Save the address of the svc_args structure on the stack.
+	 *
+	 * Although x0 contains an 8-byte value, we are allocating 16 bytes
+	 * on the stack to respect the 16-byte stack-alignment.
+	 */
+	str	x0, [sp, #-16]!
+
+	/* Load the SVC arguments values into the appropriate registers. */
+	ldp	x6, x7, [x0, #48]
+	ldp	x4, x5, [x0, #32]
+	ldp	x2, x3, [x0, #16]
+	ldp	x0, x1, [x0, #0]
+
+	svc	#0
+
+	/*
+	 * Pop the svc_args structure address from the stack into a caller-saved
+	 * register.
+	 */
+	ldr	x9, [sp], #16
+
+	/*
+	 * The return values are stored in x0-x3, put them in the svc_args
+	 * return structure.
+	 */
+	stp	x0, x1, [x9, #0]
+	stp	x2, x3, [x9, #16]
+	ret
+endfunc sp_svc
diff --git a/spm/common/sp_helpers.c b/spm/common/sp_helpers.c
new file mode 100644
index 0000000..cccaf8b
--- /dev/null
+++ b/spm/common/sp_helpers.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+#include <mmio.h>
+#include <platform_def.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+uintptr_t bound_rand(uintptr_t min, uintptr_t max)
+{
+	/*
+	 * This is not ideal as some numbers will never be generated because of
+	 * the integer arithmetic rounding.
+	 */
+	return ((rand() * (UINT64_MAX/RAND_MAX)) % (max - min)) + min;
+}
+
+/*******************************************************************************
+ * Test framework helpers
+ ******************************************************************************/
+
+void expect(int expr, int expected)
+{
+	if (expr != expected) {
+		ERROR("Expected value %i, got %i\n", expected, expr);
+		while (1)
+			continue;
+	}
+}
+
+void announce_test_section_start(const char *test_sect_desc)
+{
+	INFO("========================================\n");
+	INFO("Starting %s tests\n", test_sect_desc);
+	INFO("========================================\n");
+}
+void announce_test_section_end(const char *test_sect_desc)
+{
+	INFO("========================================\n");
+	INFO("End of %s tests\n", test_sect_desc);
+	INFO("========================================\n");
+}
+
+void announce_test_start(const char *test_desc)
+{
+	INFO("[+] %s\n", test_desc);
+}
+
+void announce_test_end(const char *test_desc)
+{
+	INFO("Test \"%s\" passed.\n", test_desc);
+}
+
+void sp_sleep(uint32_t duration_sec)
+{
+	uint32_t timer_freq = mmio_read_32(SYS_CNT_CONTROL_BASE + CNTFID_OFF);
+	VERBOSE("%s: Timer frequency = %u\n", __func__, timer_freq);
+
+	INFO("%s: Sleeping for %u seconds...\n", __func__, duration_sec);
+	uint64_t time1 = mmio_read_64(SYS_CNT_READ_BASE);
+	volatile uint64_t time2 = time1;
+	while ((time2 - time1) < duration_sec * timer_freq) {
+		time2 = mmio_read_64(SYS_CNT_READ_BASE);
+	}
+
+	INFO("%s: Done\n", __func__);
+}
diff --git a/spm/common/sp_helpers.h b/spm/common/sp_helpers.h
new file mode 100644
index 0000000..ab6415d
--- /dev/null
+++ b/spm/common/sp_helpers.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SP_HELPERS_H
+#define SP_HELPERS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+typedef struct {
+	u_register_t arg0;
+	u_register_t arg1;
+	u_register_t arg2;
+	u_register_t arg3;
+	u_register_t arg4;
+	u_register_t arg5;
+	u_register_t arg6;
+	u_register_t arg7;
+} svc_args;
+
+/*
+ * Trigger an SVC call.
+ *
+ * The arguments to pass through the SVC call must be stored in the svc_args
+ * structure. The return values of the SVC call will be stored in the same
+ * structure (overriding the input arguments).
+ *
+ * Return the first return value. It is equivalent to args.arg0 but is also
+ * provided as the return value for convenience.
+ */
+u_register_t sp_svc(svc_args *args);
+
+/*
+ * Choose a pseudo-random number within the [min,max] range (both limits are
+ * inclusive).
+ */
+uintptr_t bound_rand(uintptr_t min, uintptr_t max);
+
+/*
+ * Check that expr == expected.
+ * If not, loop forever.
+ */
+void expect(int expr, int expected);
+
+/*
+ * Test framework functions
+ */
+
+void announce_test_section_start(const char *test_sect_desc);
+void announce_test_section_end(const char *test_sect_desc);
+
+void announce_test_start(const char *test_desc);
+void announce_test_end(const char *test_desc);
+
+/* Sleep for at least 'duration_sec' seconds then return. */
+void sp_sleep(uint32_t duration_sec);
+
+#endif /* SP_HELPERS_H */