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/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);
+}