1 // © 2022 Qualcomm Innovation Center, Inc. All rights reserved.
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4
5 #if defined(UNIT_TESTS)
6
7 #include <assert.h>
8 #include <hyptypes.h>
9
10 #include <addrspace.h>
11 #include <compiler.h>
12 #include <cpulocal.h>
13 #include <log.h>
14 #include <memdb.h>
15 #include <memextent.h>
16 #include <object.h>
17 #include <panic.h>
18 #include <partition.h>
19 #include <partition_alloc.h>
20 #include <partition_init.h>
21 #include <pgtable.h>
22 #include <preempt.h>
23 #include <trace.h>
24
25 #include <asm/event.h>
26
27 #include "event_handlers.h"
28
29 #define PHYS_MAX (1UL << GPT_PHYS_BITS)
30
31 static addrspace_t *as1;
32 static addrspace_t *as2;
33
34 static _Atomic bool tests_complete;
35
36 static addrspace_t *
create_addrspace(vmid_t vmid)37 create_addrspace(vmid_t vmid)
38 {
39 partition_t *partition = partition_get_root();
40 assert(partition != NULL);
41
42 addrspace_ptr_result_t ret;
43 addrspace_create_t params = { NULL };
44
45 ret = partition_allocate_addrspace(partition, params);
46 if (ret.e != OK) {
47 panic("Failed to create addrspace");
48 }
49
50 if (addrspace_configure(ret.r, vmid) != OK) {
51 panic("Failed addrspace configuration");
52 }
53
54 if (object_activate_addrspace(ret.r) != OK) {
55 panic("Failed addrspace activation");
56 }
57
58 return ret.r;
59 }
60
61 static memextent_t *
create_memextent(memextent_t * parent,size_t offset,size_t size,bool sparse)62 create_memextent(memextent_t *parent, size_t offset, size_t size, bool sparse)
63 {
64 partition_t *partition = partition_get_root();
65 assert(partition != NULL);
66
67 memextent_ptr_result_t ret;
68 memextent_create_t params = { .memextent_device_mem = false };
69
70 ret = partition_allocate_memextent(partition, params);
71 if (ret.e != OK) {
72 panic("Failed to create addrspace");
73 }
74
75 memextent_attrs_t attrs = memextent_attrs_default();
76 memextent_attrs_set_memtype(&attrs, MEMEXTENT_MEMTYPE_ANY);
77 memextent_attrs_set_access(&attrs, PGTABLE_ACCESS_RWX);
78 if (sparse) {
79 memextent_attrs_set_type(&attrs, MEMEXTENT_TYPE_SPARSE);
80 }
81
82 if (parent != NULL) {
83 if (memextent_configure_derive(ret.r, parent, offset, size,
84 attrs)) {
85 panic("Failed to configure derived memextent");
86 }
87 } else {
88 if (memextent_configure(ret.r, offset, size, attrs) != OK) {
89 panic("Failed to configure memextent");
90 }
91 }
92
93 if (object_activate_memextent(ret.r) != OK) {
94 panic("Failed to activate memextent");
95 }
96
97 return ret.r;
98 }
99
100 static error_t
phys_range_walk(paddr_t phys,size_t size,void * arg)101 phys_range_walk(paddr_t phys, size_t size, void *arg)
102 {
103 phys_range_result_t *ret = (phys_range_result_t *)arg;
104 assert(ret != NULL);
105
106 if ((ret->e != OK) && (size >= ret->r.size)) {
107 ret->r.base = phys;
108 ret->e = OK;
109 }
110
111 return OK;
112 }
113
114 static paddr_t
get_free_phys_range(size_t min_size)115 get_free_phys_range(size_t min_size)
116 {
117 partition_t *partition = partition_get_root();
118 assert(partition != NULL);
119
120 phys_range_t range = { .size = min_size };
121 phys_range_result_t ret = { .r = range, .e = ERROR_NOMEM };
122
123 error_t err = memdb_walk((uintptr_t)partition, MEMDB_TYPE_PARTITION,
124 phys_range_walk, &ret);
125 if ((err != OK) || (ret.e != OK)) {
126 panic("Failed to find free phys range");
127 }
128
129 return ret.r.base;
130 }
131
132 static error_t
map_memextent(memextent_t * me,addrspace_t * as,vmaddr_t vbase,size_t offset,size_t size,pgtable_vm_memtype_t memtype,pgtable_access_t access)133 map_memextent(memextent_t *me, addrspace_t *as, vmaddr_t vbase, size_t offset,
134 size_t size, pgtable_vm_memtype_t memtype,
135 pgtable_access_t access)
136 {
137 memextent_mapping_attrs_t map_attrs = memextent_mapping_attrs_default();
138 memextent_mapping_attrs_set_memtype(&map_attrs, memtype);
139 memextent_mapping_attrs_set_user_access(&map_attrs, access);
140 memextent_mapping_attrs_set_kernel_access(&map_attrs, access);
141
142 return memextent_map_partial(me, as, vbase, offset, size, map_attrs);
143 }
144
145 static bool
lookup_addrspace(addrspace_t * as,vmaddr_t vbase,paddr_t expected_phys,pgtable_vm_memtype_t expected_memtype,pgtable_access_t expected_access)146 lookup_addrspace(addrspace_t *as, vmaddr_t vbase, paddr_t expected_phys,
147 pgtable_vm_memtype_t expected_memtype,
148 pgtable_access_t expected_access)
149 {
150 bool ret = false;
151
152 paddr_t mapped_base;
153 size_t mapped_size;
154 pgtable_vm_memtype_t mapped_memtype;
155 pgtable_access_t mapped_vm_kernel_access;
156 pgtable_access_t mapped_vm_user_access;
157
158 bool mapped = pgtable_vm_lookup(&as->vm_pgtable, vbase, &mapped_base,
159 &mapped_size, &mapped_memtype,
160 &mapped_vm_kernel_access,
161 &mapped_vm_user_access);
162
163 if (mapped) {
164 mapped_base += vbase & (mapped_size - 1U);
165 ret = (expected_phys == mapped_base) &&
166 (expected_memtype == mapped_memtype) &&
167 (expected_access == mapped_vm_kernel_access) &&
168 (expected_access == mapped_vm_user_access);
169 }
170
171 return ret;
172 }
173
174 static bool
is_owner(memextent_t * me,paddr_t phys,size_t size)175 is_owner(memextent_t *me, paddr_t phys, size_t size)
176 {
177 return memdb_is_ownership_contiguous(phys, phys + size - 1U,
178 (uintptr_t)me, MEMDB_TYPE_EXTENT);
179 }
180
181 void
tests_memextent_sparse_init(void)182 tests_memextent_sparse_init(void)
183 {
184 as1 = create_addrspace(33U);
185 as2 = create_addrspace(44U);
186 }
187
188 bool
tests_memextent_sparse_start(void)189 tests_memextent_sparse_start(void)
190 {
191 error_t err;
192
193 cpulocal_begin();
194 cpu_index_t cpu = cpulocal_get_index();
195 cpulocal_end();
196
197 if (cpu != 0U) {
198 goto wait;
199 }
200
201 LOG(DEBUG, INFO, "Starting sparse memextent tests");
202
203 memextent_t *me_0_0 = create_memextent(NULL, 0U, PHYS_MAX, true);
204 assert(me_0_0 != NULL);
205
206 // Test 1: Apply mapping after donate from partition.
207 vmaddr_t vbase = 0x80000000U;
208 size_t size = PGTABLE_VM_PAGE_SIZE;
209 paddr_t phys = get_free_phys_range(size);
210 pgtable_vm_memtype_t memtype = PGTABLE_VM_MEMTYPE_NORMAL_WB;
211 pgtable_access_t access = PGTABLE_ACCESS_RW;
212
213 err = map_memextent(me_0_0, as1, vbase, phys, size, memtype, access);
214 assert(err == OK);
215
216 bool mapped = lookup_addrspace(as1, vbase, phys, memtype, access);
217 assert(!mapped);
218
219 err = memextent_donate_child(me_0_0, phys, size, false);
220 assert(err == OK);
221
222 mapped = lookup_addrspace(as1, vbase, phys, memtype, access);
223 assert(mapped);
224
225 err = memextent_donate_child(me_0_0, phys, size, true);
226 assert(err == OK);
227
228 mapped = lookup_addrspace(as1, vbase, phys, memtype, access);
229 assert(!mapped);
230
231 // Test 2: Donate between siblings.
232 memextent_t *me_1_0 = create_memextent(me_0_0, 0U, PHYS_MAX, true);
233 assert(me_1_0 != NULL);
234
235 memextent_t *me_1_1 = create_memextent(me_0_0, 0U, PHYS_MAX, true);
236 assert(me_1_1 != NULL);
237
238 size = 0x10000U;
239 phys = get_free_phys_range(size);
240
241 err = memextent_donate_child(me_0_0, phys, size, false);
242 assert(err == OK);
243
244 bool owner = is_owner(me_0_0, phys, size);
245 assert(owner);
246
247 vmaddr_t vbase_1 = 0x60000000U;
248 vmaddr_t vbase_2a = 0x340404000U;
249 vmaddr_t vbase_2b = 0x288840000U;
250
251 err = map_memextent(me_1_0, as1, vbase_1, phys, 0x6000U, memtype,
252 access);
253 assert(err == OK);
254
255 err = map_memextent(me_1_0, as2, vbase_2a, phys, size, memtype, access);
256 assert(err == OK);
257
258 err = map_memextent(me_1_1, as1, vbase_1 + 0x4000U, phys + 0x4000U,
259 0x6000U, memtype, access);
260 assert(err == OK);
261
262 err = map_memextent(me_1_1, as2, vbase_2b, phys, size, memtype, access);
263 assert(err == OK);
264
265 mapped = lookup_addrspace(as1, vbase_1, phys, memtype, access);
266 assert(!mapped);
267 mapped = lookup_addrspace(as1, vbase_1 + 0x6000U, phys + 0x6000U,
268 memtype, access);
269 assert(!mapped);
270 mapped = lookup_addrspace(as2, vbase_2a, phys, memtype, access);
271 assert(!mapped);
272 mapped = lookup_addrspace(as2, vbase_2b, phys, memtype, access);
273 assert(!mapped);
274
275 err = memextent_donate_child(me_1_0, phys, size, false);
276 assert(err == OK);
277
278 owner = is_owner(me_1_0, phys, size);
279 assert(owner);
280
281 mapped = lookup_addrspace(as1, vbase_1, phys, memtype, access);
282 assert(mapped);
283 mapped = lookup_addrspace(as1, vbase_1 + 0x6000U, phys + 0x6000U,
284 memtype, access);
285 assert(!mapped);
286 mapped = lookup_addrspace(as2, vbase_2a, phys, memtype, access);
287 assert(mapped);
288 mapped = lookup_addrspace(as2, vbase_2b, phys, memtype, access);
289 assert(!mapped);
290
291 err = memextent_donate_sibling(me_1_0, me_1_1, phys, size);
292 assert(err == OK);
293
294 owner = is_owner(me_1_1, phys, size);
295 assert(owner);
296
297 mapped = lookup_addrspace(as1, vbase_1, phys, memtype, access);
298 assert(!mapped);
299 mapped = lookup_addrspace(as1, vbase_1 + 0x6000U, phys + 0x6000U,
300 memtype, access);
301 assert(mapped);
302 mapped = lookup_addrspace(as2, vbase_2a, phys, memtype, access);
303 assert(!mapped);
304 mapped = lookup_addrspace(as2, vbase_2b, phys, memtype, access);
305 assert(mapped);
306
307 err = memextent_unmap_partial(me_1_0, as1, vbase_1, phys, 0x6000U);
308 assert(err == OK);
309
310 err = memextent_unmap_partial(me_1_0, as2, vbase_2a, phys, size);
311 assert(err == OK);
312
313 memextent_unmap_all(me_1_1);
314
315 mapped = lookup_addrspace(as1, vbase_1, phys, memtype, access);
316 assert(!mapped);
317 mapped = lookup_addrspace(as1, vbase_1 + 0x6000U, phys + 0x6000U,
318 memtype, access);
319 assert(!mapped);
320 mapped = lookup_addrspace(as2, vbase_2a, phys, memtype, access);
321 assert(!mapped);
322 mapped = lookup_addrspace(as2, vbase_2b, phys, memtype, access);
323 assert(!mapped);
324
325 object_put_addrspace(as1);
326 object_put_addrspace(as2);
327
328 object_put_memextent(me_0_0);
329 object_put_memextent(me_1_0);
330 object_put_memextent(me_1_1);
331
332 LOG(DEBUG, INFO, "Finished sparse memextent tests");
333
334 asm_event_store_and_wake(&tests_complete, true);
335
336 wait:
337 while (!asm_event_load_before_wait(&tests_complete)) {
338 asm_event_wait(&tests_complete);
339 }
340
341 return false;
342 }
343
344 #else
345
346 extern char unused;
347
348 #endif
349