blob: 1a3072beaa066ca57721f70f6698cd459992639d [file] [log] [blame]
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02001/*
2 * Copyright (c) 2018, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <debug.h>
9#include <errno.h>
10#include <platform_def.h>
11#include <secure_partition.h>
12#include <sp_helpers.h>
13#include <spm_svc.h>
14#include <stdio.h>
15#include <types.h>
16#include <xlat_tables_defs.h>
17
18#include "cactus.h"
19#include "cactus_tests.h"
20
21/* This is filled at runtime. */
22static uintptr_t cactus_tests_start;
23static uintptr_t cactus_tests_end;
24static uintptr_t cactus_tests_size;
25
26/*
27 * Given the required instruction and data access permissions,
28 * create a memory access controls value that is formatted as expected
29 * by the SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC.
30 */
31static inline uint32_t mem_access_perm(int instr_access_perm,
32 int data_access_perm)
33{
34 return instr_access_perm |
35 ((data_access_perm & SP_MEMORY_ATTRIBUTES_ACCESS_MASK)
36 << SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT);
37}
38
39/*
40 * Send an SP_MEMORY_ATTRIBUTES_SET_AARCH64 SVC with the given arguments.
41 * Return the return value of the SVC.
42 */
43static int32_t request_mem_attr_changes(uintptr_t base_address,
44 int pages_count,
45 uint32_t memory_access_controls)
46{
47 INFO("Requesting memory attributes change\n");
48 INFO(" Start address : %p\n", (void *) base_address);
49 INFO(" Number of pages: %i\n", pages_count);
50 INFO(" Attributes : 0x%x\n", memory_access_controls);
51
52 svc_args svc_values = { SP_MEMORY_ATTRIBUTES_SET_AARCH64,
53 base_address,
54 pages_count,
55 memory_access_controls };
56 return sp_svc(&svc_values);
57}
58
59/*
60 * Send an SP_MEMORY_ATTRIBUTES_GET_AARCH64 SVC with the given arguments.
61 * Return the return value of the SVC.
62 */
63static int32_t request_get_mem_attr(uintptr_t base_address)
64{
65 INFO("Requesting memory attributes\n");
66 INFO(" Base address : %p\n", (void *) base_address);
67
68 svc_args svc_values = { SP_MEMORY_ATTRIBUTES_GET_AARCH64,
69 base_address };
70 return sp_svc(&svc_values);
71}
72
73/*
74 * This function expects a base address and number of pages identifying the
75 * extents of some memory region mapped as non-executable, read-only.
76 *
77 * 1) It changes its data access permissions to read-write.
78 * 2) It checks this memory can now be written to.
79 * 3) It restores the original data access permissions.
80 *
81 * If any check fails, it loops forever. It could also trigger a permission
82 * fault while trying to write to the memory.
83 */
84static void mem_attr_changes_unittest(uintptr_t addr, int pages_count)
85{
86 int32_t ret;
87 uintptr_t end_addr = addr + pages_count * PAGE_SIZE;
88 uint32_t old_attr, new_attr;
89
90 char test_desc[50];
91
92 snprintf(test_desc, sizeof(test_desc),
93 "RO -> RW (%i page(s) from address 0x%lx)", pages_count, addr);
94 announce_test_start(test_desc);
95
96 /*
97 * Ensure we don't change the attributes of some random memory
98 * location
99 */
100 assert(addr >= cactus_tests_start);
101 assert(end_addr < (cactus_tests_start + cactus_tests_size));
102
103 old_attr = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RO);
104 /* Memory was read-only, let's try changing that to RW */
105 new_attr = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
106
107 ret = request_mem_attr_changes(addr, pages_count, new_attr);
108 expect(ret, SPM_SUCCESS);
109 printf("Successfully changed memory attributes\n");
110
111 /* The attributes should be the ones we have just written. */
112 ret = request_get_mem_attr(addr);
113 expect(ret, new_attr);
114
115 /* If it worked, we should be able to write to this memory now! */
116 for (unsigned char *data = (unsigned char *) addr;
117 (uintptr_t) data != end_addr;
118 ++data) {
119 *data = 42;
120 }
121 printf("Successfully wrote to the memory\n");
122
123 /* Let's revert back to the original attributes for the next test */
124 ret = request_mem_attr_changes(addr, pages_count, old_attr);
125 expect(ret, SPM_SUCCESS);
126 printf("Successfully restored the old attributes\n");
127
128 /* The attributes should be the original ones again. */
129 ret = request_get_mem_attr(addr);
130 expect(ret, old_attr);
131
132 announce_test_end(test_desc);
133}
134
135/*
136 * Exercise the ability of the Trusted Firmware to change the data access
137 * permissions and instruction execution permissions of some memory region.
138 */
139void mem_attr_changes_tests(const secure_partition_boot_info_t *boot_info)
140{
141 uint32_t attributes;
142 int32_t ret;
143 uintptr_t addr;
144
145 cactus_tests_start = CACTUS_BSS_END;
146 cactus_tests_end = boot_info->sp_image_base + boot_info->sp_image_size;
147 cactus_tests_size = cactus_tests_end - cactus_tests_start;
148
149 const char *test_sect_desc = "memory attributes changes";
150
151 announce_test_section_start(test_sect_desc);
152 /*
153 * Start with error cases, i.e. requests that are expected to be denied
154 */
155 const char *test_desc1 = "Read-write, executable";
156
157 announce_test_start(test_desc1);
158 attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
159 ret = request_mem_attr_changes(CACTUS_RWDATA_START, 1, attributes);
160 expect(ret, SPM_INVALID_PARAMETER);
161 announce_test_end(test_desc1);
162
163 const char *test_desc2 = "Size == 0";
164
165 announce_test_start(test_desc2);
166 attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
167 ret = request_mem_attr_changes(CACTUS_RWDATA_START, 0, attributes);
168 expect(ret, SPM_INVALID_PARAMETER);
169 announce_test_end(test_desc2);
170
171 const char *test_desc3 = "Unaligned address";
172
173 announce_test_start(test_desc3);
174 attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
175 /* Choose an address not aligned to a page boundary. */
176 addr = cactus_tests_start + 5;
177 ret = request_mem_attr_changes(addr, 1, attributes);
178 expect(ret, SPM_INVALID_PARAMETER);
179 announce_test_end(test_desc3);
180
181 const char *test_desc4 = "Unmapped memory region";
182
183 announce_test_start(test_desc4);
184 addr = boot_info->sp_mem_limit + 2 * PAGE_SIZE;
185 attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
186 ret = request_mem_attr_changes(addr, 3, attributes);
187 expect(ret, SPM_INVALID_PARAMETER);
188 announce_test_end(test_desc4);
189
190 const char *test_desc5 = "Partially unmapped memory region";
191
192 announce_test_start(test_desc5);
193 addr = boot_info->sp_mem_base - 2 * PAGE_SIZE;
194 attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
195 ret = request_mem_attr_changes(addr, 6, attributes);
196 expect(ret, SPM_INVALID_PARAMETER);
197 announce_test_end(test_desc5);
198
199 const char *test_desc6 = "Memory region mapped with the wrong granularity";
200
201 announce_test_start(test_desc6);
202 /*
203 * This address is usually mapped at a 2 MiB granularity. By using as
204 * test address the block after the console we make sure that in case
205 * the attributes of the block actually changed, the console would work
206 * and we would get the error message.
207 */
208 addr = ((uintptr_t)PLAT_ARM_UART_BASE + 0x200000ULL) & ~(0x200000ULL - 1ULL);
209 attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW);
210 ret = request_mem_attr_changes(addr, 1, attributes);
211 expect(ret, SPM_INVALID_PARAMETER);
212 announce_test_end(test_desc6);
213
214 const char *test_desc7 = "Try some valid memory change requests";
215
216 announce_test_start(test_desc7);
217 for (unsigned int i = 0; i < 20; ++i) {
218 /*
219 * Choose some random address in the pool of memory reserved
220 * for these tests.
221 */
222 const int pages_max = cactus_tests_size / PAGE_SIZE;
223 int pages_count = bound_rand(1, pages_max);
224
225 addr = bound_rand(
226 cactus_tests_start,
227 cactus_tests_end - (pages_count * PAGE_SIZE));
228 /* Align to PAGE_SIZE. */
229 addr &= ~(PAGE_SIZE - 1);
230
231 mem_attr_changes_unittest(addr, pages_count);
232 }
233 announce_test_end(test_desc7);
234
235 announce_test_section_end(test_sect_desc);
236}