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