summaryrefslogtreecommitdiff
path: root/cactus/cactus_tests_memory_attributes.c
diff options
context:
space:
mode:
Diffstat (limited to 'cactus/cactus_tests_memory_attributes.c')
-rw-r--r--cactus/cactus_tests_memory_attributes.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/cactus/cactus_tests_memory_attributes.c b/cactus/cactus_tests_memory_attributes.c
new file mode 100644
index 0000000..ac15f7e
--- /dev/null
+++ b/cactus/cactus_tests_memory_attributes.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <secure_partition.h>
+#include <spm_svc.h>
+#include <stdio.h>
+#include <types.h>
+#include <xlat_tables.h>
+
+#include "cactus.h"
+#include "cactus_helpers.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_MEM_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_MEM_ATTR_ACCESS_MASK)
+ << SP_MEM_ATTR_ACCESS_SHIFT);
+}
+
+/*
+ * Send an SP_MEM_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);
+
+ uint64_t ret = cactus_svc(SP_MEM_ATTRIBUTES_SET_AARCH64,
+ base_address,
+ pages_count,
+ memory_access_controls,
+ 0, 0, 0, 0);
+
+ return (int32_t) ret;
+}
+
+/*
+ * Send an SP_MEM_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);
+
+ uint64_t ret = cactus_svc(SP_MEM_ATTRIBUTES_GET_AARCH64,
+ base_address,
+ 0, 0, 0, 0, 0, 0);
+
+ return (int32_t) ret;
+}
+
+/*
+ * 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_MEM_ATTR_NON_EXEC, SP_MEM_ATTR_ACCESS_RO);
+ /* Memory was read-only, let's try changing that to RW */
+ new_attr = mem_access_perm(SP_MEM_ATTR_NON_EXEC, SP_MEM_ATTR_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_MEM_ATTR_EXEC, SP_MEM_ATTR_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_MEM_ATTR_NON_EXEC, SP_MEM_ATTR_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_MEM_ATTR_NON_EXEC, SP_MEM_ATTR_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_MEM_ATTR_NON_EXEC, SP_MEM_ATTR_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_MEM_ATTR_NON_EXEC, SP_MEM_ATTR_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);
+ /*
+ * The Secure Partition is placed in DRAM, so there is a lot of
+ * remaining space that doesn't have any restrictions when it is mapped.
+ * It has most likely been mapped with a granularity of 2MB.
+ */
+ addr = boot_info->sp_mem_limit - 2 * PAGE_SIZE;
+ attributes = mem_access_perm(SP_MEM_ATTR_NON_EXEC, SP_MEM_ATTR_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);
+}