xref: /aosp_15_r20/external/coreboot/tests/lib/memrange-test.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <tests/test.h>
4 
5 #include <device/device.h>
6 #include <commonlib/helpers.h>
7 #include <memrange.h>
8 
9 #define MEMRANGE_ALIGN (POWER_OF_2(12))
10 
11 enum mem_types {
12 	/* Avoid using 0 to verify that UUT really sets this memory,
13 	   but keep value small, as this will be an index in the table */
14 	CACHEABLE_TAG = 0x10,
15 	RESERVED_TAG,
16 	READONLY_TAG,
17 	INSERTED_TAG,
18 	HOLE_TAG,
19 	UNASSIGNED_TAG,
20 	END_OF_RESOURCES
21 };
22 
23 /* Indices of entries matters, since it must reflect mem_types enum */
24 struct resource res_mock_1[] = {
25 	[UNASSIGNED_TAG] = {.base = 0x0,
26 			    .size = 0x8000,
27 			    .next = &res_mock_1[CACHEABLE_TAG],
28 			    .flags = IORESOURCE_MEM | IORESOURCE_PREFETCH},
29 	[CACHEABLE_TAG] = {.base = 0xE000,
30 			   .size = 0xF2000,
31 			   .next = &res_mock_1[RESERVED_TAG],
32 			   .flags = IORESOURCE_CACHEABLE | IORESOURCE_MEM |
33 				   IORESOURCE_ASSIGNED },
34 	[RESERVED_TAG] = {.base = 4ULL * GiB,
35 			  .size = 4ULL * KiB,
36 			  .next = &res_mock_1[READONLY_TAG],
37 			  .flags = IORESOURCE_RESERVE | IORESOURCE_MEM |
38 				  IORESOURCE_ASSIGNED },
39 	[READONLY_TAG] = {.base = 0xFF0000,
40 			  .size = 0x10000,
41 			  .next = NULL,
42 			  .flags = IORESOURCE_READONLY | IORESOURCE_MEM |
43 				  IORESOURCE_ASSIGNED }
44 };
45 
46 /* Boundary 1 byte below 4GiB and 1 byte above 4GiB. */
47 struct resource res_mock_2[] = {
48 	[CACHEABLE_TAG] = {.base = 0x1000000,
49 			   .size = 4ULL * GiB - 0x1000001ULL,
50 			   .next = &res_mock_2[RESERVED_TAG],
51 			   .flags = IORESOURCE_CACHEABLE | IORESOURCE_MEM |
52 				   IORESOURCE_ASSIGNED },
53 	[RESERVED_TAG] = {.base = 4ULL * GiB + 1ULL,
54 			  .size = 4ULL * GiB,
55 			  .next = &res_mock_2[READONLY_TAG],
56 			  .flags = IORESOURCE_RESERVE | IORESOURCE_MEM |
57 				  IORESOURCE_ASSIGNED },
58 	[READONLY_TAG] = {.base = 0,
59 			  .size = 0x10000,
60 			  .next = NULL,
61 			  .flags = IORESOURCE_READONLY | IORESOURCE_MEM |
62 				  IORESOURCE_ASSIGNED }
63 };
64 
65 /* Boundary crossing 4GiB. */
66 struct resource res_mock_3[] = {
67 	[CACHEABLE_TAG] = {.base = 0xD000,
68 			   .size = 0xF3000,
69 			   .next = &res_mock_3[RESERVED_TAG],
70 			   .flags = IORESOURCE_CACHEABLE | IORESOURCE_MEM |
71 				   IORESOURCE_ASSIGNED },
72 	[RESERVED_TAG] = {.base = 1ULL * GiB,
73 			  .size = 4ULL * GiB,
74 			  .next = &res_mock_3[READONLY_TAG],
75 			  .flags = IORESOURCE_RESERVE | IORESOURCE_MEM |
76 				  IORESOURCE_ASSIGNED },
77 	[READONLY_TAG] = {.base = 0xFF0000,
78 			  .size = 0x10000,
79 			  .next = NULL,
80 			  .flags = IORESOURCE_READONLY | IORESOURCE_MEM |
81 				  IORESOURCE_ASSIGNED}
82 };
83 
84 
85 struct device mock_device = {.enabled = 1};
86 
87 /* Fake memory devices handle */
88 struct device *all_devices = &mock_device;
89 
setup_test_1(void ** state)90 int setup_test_1(void **state)
91 {
92 	*state = res_mock_1;
93 	mock_device.resource_list = &res_mock_1[UNASSIGNED_TAG];
94 
95 	return 0;
96 }
97 
setup_test_2(void ** state)98 int setup_test_2(void **state)
99 {
100 	*state = res_mock_2;
101 	mock_device.resource_list = &res_mock_2[CACHEABLE_TAG];
102 
103 	return 0;
104 }
105 
setup_test_3(void ** state)106 int setup_test_3(void **state)
107 {
108 	*state = res_mock_3;
109 	mock_device.resource_list = &res_mock_3[CACHEABLE_TAG];
110 
111 	return 0;
112 }
113 
get_aligned_base(struct resource * res,struct range_entry * entry)114 resource_t get_aligned_base(struct resource *res, struct range_entry *entry)
115 {
116 	return ALIGN_DOWN(res[range_entry_tag(entry)].base, MEMRANGE_ALIGN);
117 }
118 
get_aligned_end(struct resource * res,struct range_entry * entry)119 resource_t get_aligned_end(struct resource *res, struct range_entry *entry)
120 {
121 	resource_t end = res[range_entry_tag(entry)].base + res[range_entry_tag(entry)].size
122 			 + (res[range_entry_tag(entry)].base - range_entry_base(entry));
123 	return ALIGN_UP(end, MEMRANGE_ALIGN);
124 }
125 
126 /*
127  * This test verifies memranges_init(), memranges_add_resources() and memranges_teardown()
128  * functions. It covers basic functionality of memrange library - implementation of creating
129  * memrange structure from resources available on the platform and method for free'ing
130  * allocated memory.
131  *
132  * Example memory ranges (res_mock1) for test_memrange_basic.
133  * Ranges marked with asterisks (***) are not added to the test_memrange.
134  *
135  *     +-------UNASSIGNED_TAG--------+ <-0x0
136  *     |                             |
137  *     +-----------------------------+ <-0x8000
138  *
139  *
140  *
141  *     +--------CACHEABLE_TAG--------+ <-0xE000
142  *     |                             |
143  *     |                             |
144  *     |                             |
145  *     +-----------------------------+ <-0x100000
146  *
147  *
148  *
149  *     +-----***READONLY_TAG***------+ <-0xFF0000
150  *     |                             |
151  *     |                             |
152  *     |                             |
153  *     +-----------------------------+ <-0x1000000
154  *
155  *
156  *     +--------RESERVED_TAG---------+ <-0x100000000
157  *     |                             |
158  *     +-----------------------------+ <-0x100001000
159  */
test_memrange_basic(void ** state)160 static void test_memrange_basic(void **state)
161 {
162 	int counter = 0;
163 	const unsigned long cacheable = IORESOURCE_CACHEABLE;
164 	const unsigned long reserved = IORESOURCE_RESERVE;
165 	const unsigned long prefetchable = IORESOURCE_PREFETCH;
166 	struct range_entry *ptr;
167 	struct memranges test_memrange;
168 	struct resource *res_mock = *state;
169 	resource_t prev_base = 0;
170 
171 	memranges_init_empty(&test_memrange, NULL, 0);
172 	memranges_add_resources(&test_memrange, prefetchable, prefetchable, UNASSIGNED_TAG);
173 	memranges_add_resources(&test_memrange, cacheable, cacheable, CACHEABLE_TAG);
174 	memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG);
175 
176 	/* There should be two entries, since cacheable and reserved regions are not neighbors.
177 	   Besides these two, a region with an unassigned tag is defined, to emulate an unmapped
178 	   PCI BAR resource. This resource is not mapped into host physical address and hence
179 	   should not be picked up by memranges_add_resources().*/
180 
181 	memranges_each_entry(ptr, &test_memrange)
182 	{
183 		assert_in_range(range_entry_tag(ptr), CACHEABLE_TAG, RESERVED_TAG);
184 		assert_int_equal(range_entry_base(ptr), get_aligned_base(res_mock, ptr));
185 
186 		assert_int_equal(range_entry_end(ptr), get_aligned_end(res_mock, ptr));
187 
188 		/* Ranges have to be returned in increasing order */
189 		assert_true(prev_base <= range_entry_base(ptr));
190 
191 		prev_base = range_entry_base(ptr);
192 		counter++;
193 	};
194 	assert_int_equal(counter, 2);
195 	counter = 0;
196 
197 	/* Remove initial memrange */
198 	memranges_teardown(&test_memrange);
199 	memranges_each_entry(ptr, &test_memrange) counter++;
200 	assert_int_equal(counter, 0);
201 }
202 
203 /*
204  * This test verifies memranges_clone(), memranges_insert() and memranges_update_tag()
205  * functions. All operations are performed on cloned memrange. One of the most important thing
206  * to check, is that memrange_insert() should remove all ranges which are covered by the newly
207  * inserted one.
208  *
209  * Example memory ranges (res_mock1) for test_memrange_clone_insert.
210  * Ranges marked with asterisks (***) are not added to the clone_memrange.
211  * Ranges marked with (^) have tag value changed during test.
212  *
213  *                +--------CACHEABLE_TAG--------+ <-0xE000
214  *         +------|----INSERTED_TAG----------+  | <-0xF000
215  *         |      |  (^READONLY_TAG^)        |  |
216  *         |      |                          |  |
217  *         |      +-----------------------------+ <-0x100000
218  *         +---------------------------------+    <-0x101000
219  *
220  *
221  *                +-----***READONLY_TAG***------+ <-0xFF0000
222  *                |                             |
223  *                |                             |
224  *                |                             |
225  *                +-----------------------------+ <-0x1000000
226  *
227  *
228  *         +------+---------RESERVED_TAG-----+--+ <-0x100000000
229  *         |      |                          |  |
230  *         |      +-----------------------------+ <-0x100001000
231  *         +-----------INSERTED_TAG----------+    <-0x100002000
232  */
test_memrange_clone_insert(void ** state)233 static void test_memrange_clone_insert(void **state)
234 {
235 	int counter = 0;
236 	const unsigned long cacheable = IORESOURCE_CACHEABLE;
237 	const unsigned long reserved = IORESOURCE_RESERVE;
238 	struct range_entry *ptr;
239 	struct memranges test_memrange, clone_memrange;
240 	struct resource *res_mock = *state;
241 	const resource_t new_range_begin_offset = 1ULL << 12;
242 
243 	memranges_init(&test_memrange, cacheable, cacheable, CACHEABLE_TAG);
244 	memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG);
245 
246 	memranges_clone(&clone_memrange, &test_memrange);
247 	memranges_teardown(&test_memrange);
248 
249 	/* Verify that new one is really a clone */
250 	memranges_each_entry(ptr, &clone_memrange)
251 	{
252 		assert_in_range(range_entry_tag(ptr), CACHEABLE_TAG, END_OF_RESOURCES - 1);
253 		assert_int_equal(range_entry_base(ptr), get_aligned_base(res_mock, ptr));
254 
255 		assert_int_equal(range_entry_end(ptr), get_aligned_end(res_mock, ptr));
256 
257 		counter++;
258 	};
259 	assert_int_equal(counter, 2);
260 	counter = 0;
261 
262 	/* Insert new range, which will overlap with first region. */
263 	memranges_insert(&clone_memrange, res_mock[CACHEABLE_TAG].base + new_range_begin_offset,
264 			 res_mock[CACHEABLE_TAG].size, INSERTED_TAG);
265 
266 	/* Three ranges should be there - CACHEABLE(shrunk), INSERTED and RESERVED */
267 	memranges_each_entry(ptr, &clone_memrange)
268 	{
269 		resource_t expected_end;
270 
271 		if (range_entry_tag(ptr) == CACHEABLE_TAG) {
272 			assert_int_equal(range_entry_base(ptr), res_mock[CACHEABLE_TAG].base);
273 
274 			expected_end = res_mock[CACHEABLE_TAG].base + new_range_begin_offset;
275 			assert_int_equal(range_entry_end(ptr), expected_end);
276 		}
277 		if (range_entry_tag(ptr) == INSERTED_TAG) {
278 			assert_int_equal(range_entry_base(ptr),
279 					 res_mock[CACHEABLE_TAG].base + new_range_begin_offset);
280 
281 			expected_end = res_mock[CACHEABLE_TAG].base + new_range_begin_offset
282 				       + res_mock[CACHEABLE_TAG].size;
283 			assert_int_equal(range_entry_end(ptr),
284 					 ALIGN_UP(expected_end, MEMRANGE_ALIGN));
285 		}
286 		counter++;
287 	}
288 	assert_int_equal(counter, 3);
289 	counter = 0;
290 
291 	/* Insert new region, which will shadow readonly range.
292 	 * Additionally verify API for updating tags */
293 	memranges_update_tag(&clone_memrange, INSERTED_TAG, READONLY_TAG);
294 
295 	memranges_each_entry(ptr, &clone_memrange)
296 	{
297 		resource_t expected_end;
298 
299 		assert_int_not_equal(range_entry_tag(ptr), INSERTED_TAG);
300 		if (range_entry_tag(ptr) == READONLY_TAG) {
301 			assert_int_equal(range_entry_base(ptr),
302 					 res_mock[CACHEABLE_TAG].base + new_range_begin_offset);
303 
304 			expected_end = res_mock[CACHEABLE_TAG].base + new_range_begin_offset
305 				       + res_mock[CACHEABLE_TAG].size;
306 			assert_int_equal(range_entry_end(ptr),
307 					 ALIGN_UP(expected_end, MEMRANGE_ALIGN));
308 		}
309 	};
310 
311 	/* Check if alignment (4KiB) is properly applied, that is begin - DOWN and end - UP */
312 	memranges_insert(&clone_memrange, res_mock[RESERVED_TAG].base + 0xAD,
313 			 res_mock[RESERVED_TAG].size, INSERTED_TAG);
314 
315 	memranges_each_entry(ptr, &clone_memrange)
316 	{
317 		resource_t expected_end;
318 
319 		assert_int_not_equal(range_entry_tag(ptr), RESERVED_TAG);
320 		if (range_entry_tag(ptr) == INSERTED_TAG) {
321 			assert_int_equal(
322 				range_entry_base(ptr),
323 				ALIGN_DOWN(res_mock[RESERVED_TAG].base, MEMRANGE_ALIGN));
324 
325 			expected_end = ALIGN_DOWN(res_mock[RESERVED_TAG].base, MEMRANGE_ALIGN)
326 				       + new_range_begin_offset + res_mock[RESERVED_TAG].size;
327 			expected_end = ALIGN_UP(expected_end, MEMRANGE_ALIGN);
328 
329 			assert_int_equal(range_entry_end(ptr), expected_end);
330 		}
331 		counter++;
332 	}
333 	assert_int_equal(counter, 3);
334 
335 	/* Free clone */
336 	memranges_teardown(&clone_memrange);
337 }
338 
339 /*
340  * This test verifies memranges_fill_holes_up_to() and memranges_create_hole(). Idea of the test
341  * is to fill all holes, so that we end up with contiguous address space fully covered by
342  * entries. Then, holes are created on the border of two different regions
343  *
344  * Example memory ranges (res_mock1) for test_memrange_holes.
345  * Space marked with (/) is not covered by any region at the end of the test.
346  *
347  *     +--------CACHEABLE_TAG--------+ <-0xE000
348  *     |                             |
349  *     |                             |
350  *   //|/////////////////////////////| <-0xFF000
351  *   //+-----------HOLE_TAG----------+ <-0x100000
352  *   //|/////////////////////////////| <-0x101000
353  *     |                             |
354  *     |                             |
355  *     |                             |
356  *     |                             |
357  *     +--------RESERVED_TAG---------+ <-0x100000000
358  *     |                             |
359  *     +-----------------------------+ <-0x100001000
360  */
test_memrange_holes(void ** state)361 static void test_memrange_holes(void **state)
362 {
363 	int counter = 0;
364 	const unsigned long cacheable = IORESOURCE_CACHEABLE;
365 	const unsigned long reserved = IORESOURCE_RESERVE;
366 	struct range_entry *ptr;
367 	struct range_entry *hole_ptr = NULL;
368 	struct memranges test_memrange;
369 	struct resource *res_mock = *state;
370 	int holes_found = 0;
371 	resource_t last_range_end = 0;
372 	const resource_t holes_fill_end = res_mock[RESERVED_TAG].base;
373 
374 	memranges_init(&test_memrange, cacheable, cacheable, CACHEABLE_TAG);
375 	memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG);
376 
377 	/* Count holes in ranges */
378 	memranges_each_entry(ptr, &test_memrange)
379 	{
380 		if (!last_range_end) {
381 			last_range_end = range_entry_end(ptr);
382 			continue;
383 		}
384 
385 
386 		if (range_entry_base(ptr) != last_range_end) {
387 			holes_found++;
388 			last_range_end = range_entry_end(ptr);
389 		}
390 
391 		if (range_entry_base(ptr) >= holes_fill_end)
392 			break;
393 	}
394 
395 	/* Create range entries which covers continuous memory range
396 	   (but with different tags) */
397 	memranges_fill_holes_up_to(&test_memrange, holes_fill_end, HOLE_TAG);
398 
399 	memranges_each_entry(ptr, &test_memrange)
400 	{
401 		if (range_entry_tag(ptr) == HOLE_TAG) {
402 			assert_int_equal(range_entry_base(ptr),
403 					 ALIGN_UP(res_mock[CACHEABLE_TAG].base
404 							  + res_mock[CACHEABLE_TAG].size,
405 						  MEMRANGE_ALIGN));
406 			assert_int_equal(range_entry_end(ptr), holes_fill_end);
407 			/* Store pointer to HOLE_TAG region for future use */
408 			hole_ptr = ptr;
409 		}
410 		counter++;
411 	}
412 	assert_int_equal(counter, 2 + holes_found);
413 
414 	/* If test data does not have any holes in it then terminate this test */
415 	if (holes_found == 0)
416 		return;
417 
418 	assert_non_null(hole_ptr);
419 	counter = 0;
420 
421 	/* Create hole crossing the border of two range entries */
422 	const resource_t new_cacheable_end = ALIGN_DOWN(
423 		res_mock[CACHEABLE_TAG].base + res_mock[CACHEABLE_TAG].size - 4 * KiB,
424 		MEMRANGE_ALIGN);
425 	const resource_t new_hole_begin =
426 		ALIGN_UP(range_entry_base(hole_ptr) + 4 * KiB, MEMRANGE_ALIGN);
427 	const resource_t ranges_diff = new_hole_begin - new_cacheable_end;
428 
429 	memranges_create_hole(&test_memrange, new_cacheable_end, ranges_diff);
430 
431 	memranges_each_entry(ptr, &test_memrange)
432 	{
433 		switch (range_entry_tag(ptr)) {
434 		case CACHEABLE_TAG:
435 			assert_int_equal(range_entry_base(ptr), res_mock[CACHEABLE_TAG].base);
436 			assert_int_equal(range_entry_end(ptr), new_cacheable_end);
437 			break;
438 		case RESERVED_TAG:
439 			assert_int_equal(range_entry_base(ptr), res_mock[RESERVED_TAG].base);
440 			assert_int_equal(range_entry_end(ptr),
441 					 res_mock[RESERVED_TAG].base
442 						 + res_mock[RESERVED_TAG].size);
443 			break;
444 		case HOLE_TAG:
445 			assert_int_equal(range_entry_base(ptr), new_hole_begin);
446 			assert_int_equal(range_entry_end(ptr), res_mock[RESERVED_TAG].base);
447 			break;
448 		default:
449 			break;
450 		}
451 		counter++;
452 	}
453 	assert_int_equal(counter, 3);
454 
455 	memranges_teardown(&test_memrange);
456 }
457 
458 /*
459  * This test verifies memranges_steal() function. Simple check is done by attempt
460  * to steal some memory from the top of region with CACHEABLE_TAG and some from
461  * the bottom of region with READONLY_TAG.
462  *
463  * Example memory ranges (res_mock1) for test_memrange_steal.
464  * Space marked with (/) is stolen during the test.
465  *
466  *     +--------CACHEABLE_TAG--------+ <-0xE000
467  *     |                             |
468  *     |                             |
469  *     |/////////////////////////////|             <-stolen_base
470  *     +-----------------------------+ <-0x100000  <-stolen_base + 0x4000
471  *
472  *
473  *
474  *     +--------READONLY_TAG---------+ <-0xFF0000  <-stolen_base
475  *     |/////////////////////////////|             <-stolen_base + 0x4000
476  *     |                             |
477  *     |                             |
478  *     +-----------------------------+ <-0x1000000
479  *
480  *
481  *     +--------RESERVED_TAG---------+ <-0x100000000
482  *     |                             |
483  *     +-----------------------------+ <-0x100001000
484  */
test_memrange_steal(void ** state)485 static void test_memrange_steal(void **state)
486 {
487 	bool status = false;
488 	resource_t stolen;
489 	const unsigned long cacheable = IORESOURCE_CACHEABLE;
490 	const unsigned long reserved = IORESOURCE_RESERVE;
491 	const unsigned long readonly = IORESOURCE_READONLY;
492 	const resource_t stolen_range_size = 0x4000;
493 	struct memranges test_memrange;
494 	struct resource *res_mock = *state;
495 	struct range_entry *ptr;
496 	size_t count = 0;
497 
498 	memranges_init(&test_memrange, cacheable, cacheable, CACHEABLE_TAG);
499 	memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG);
500 	memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG);
501 
502 	status = memranges_steal(&test_memrange,
503 				 res_mock[RESERVED_TAG].base + res_mock[RESERVED_TAG].size,
504 				 stolen_range_size, 12, CACHEABLE_TAG, &stolen, true);
505 	assert_true(status);
506 	assert_in_range(stolen, res_mock[CACHEABLE_TAG].base,
507 			res_mock[CACHEABLE_TAG].base + res_mock[CACHEABLE_TAG].size);
508 	status = memranges_steal(&test_memrange,
509 				 res_mock[RESERVED_TAG].base + res_mock[RESERVED_TAG].size,
510 				 stolen_range_size, 12, READONLY_TAG, &stolen, false);
511 	assert_true(status);
512 	assert_in_range(stolen, res_mock[READONLY_TAG].base,
513 			res_mock[READONLY_TAG].base + res_mock[READONLY_TAG].size);
514 
515 	memranges_each_entry(ptr, &test_memrange)
516 	{
517 		if (range_entry_tag(ptr) == CACHEABLE_TAG) {
518 			assert_int_equal(range_entry_end(ptr),
519 					 ALIGN_DOWN(ALIGN_UP(res_mock[CACHEABLE_TAG].base
520 								+ res_mock[CACHEABLE_TAG].size,
521 							     MEMRANGE_ALIGN)
522 							- stolen_range_size,
523 						    MEMRANGE_ALIGN));
524 		}
525 		if (range_entry_tag(ptr) == READONLY_TAG) {
526 			assert_int_equal(range_entry_base(ptr),
527 					 ALIGN_DOWN(res_mock[READONLY_TAG].base, MEMRANGE_ALIGN)
528 						 + stolen_range_size);
529 		}
530 		count++;
531 	}
532 	assert_int_equal(count, 3);
533 	count = 0;
534 
535 	/* Check if inserting ranges in previously stolen areas will merge them. */
536 	memranges_insert(&test_memrange,
537 			 res_mock[CACHEABLE_TAG].base + res_mock[CACHEABLE_TAG].size
538 				- stolen_range_size - 0x12,
539 			 stolen_range_size, CACHEABLE_TAG);
540 	memranges_insert(&test_memrange, res_mock[READONLY_TAG].base + 0xCC, stolen_range_size,
541 			 READONLY_TAG);
542 	memranges_each_entry(ptr, &test_memrange)
543 	{
544 		const unsigned long tag = range_entry_tag(ptr);
545 		assert_true(tag == CACHEABLE_TAG || tag == READONLY_TAG || tag == RESERVED_TAG);
546 		assert_int_equal(
547 			range_entry_base(ptr),
548 			ALIGN_DOWN(res_mock[tag].base, MEMRANGE_ALIGN));
549 		assert_int_equal(
550 			range_entry_end(ptr),
551 			ALIGN_UP(res_mock[tag].base + res_mock[tag].size, MEMRANGE_ALIGN));
552 		count++;
553 	}
554 	assert_int_equal(count, 3);
555 	count = 0;
556 
557 	memranges_teardown(&test_memrange);
558 }
559 
560 /* Utility function checking number of entries and alignment of their base and end pointers */
check_range_entries_count_and_alignment(struct memranges * ranges,size_t ranges_count,resource_t alignment)561 static void check_range_entries_count_and_alignment(struct memranges *ranges,
562 						    size_t ranges_count, resource_t alignment)
563 {
564 	size_t count = 0;
565 	struct range_entry *ptr;
566 
567 	memranges_each_entry(ptr, ranges)
568 	{
569 		assert_true(IS_ALIGNED(range_entry_base(ptr), alignment));
570 		assert_true(IS_ALIGNED(range_entry_end(ptr), alignment));
571 
572 		count++;
573 	}
574 	assert_int_equal(ranges_count, count);
575 }
576 
577 /* This test verifies memranges_init*() and memranges_teardown() functions.
578    Added ranges are checked correct count and alignment. */
test_memrange_init_and_teardown(void ** state)579 static void test_memrange_init_and_teardown(void **state)
580 {
581 	const unsigned long cacheable = IORESOURCE_CACHEABLE;
582 	const unsigned long reserved = IORESOURCE_RESERVE;
583 	const unsigned long readonly = IORESOURCE_READONLY;
584 	struct memranges test_memrange;
585 	struct range_entry range_entries[4] = {0};
586 
587 	/* Test memranges_init() correctness */
588 	memranges_init(&test_memrange, cacheable, cacheable, CACHEABLE_TAG);
589 	memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG);
590 	memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG);
591 
592 	/* Expect all entries to be aligned to 4KiB (2^12) */
593 	check_range_entries_count_and_alignment(&test_memrange, 3, MEMRANGE_ALIGN);
594 
595 	/* Expect ranges list to be empty after teardown */
596 	memranges_teardown(&test_memrange);
597 	assert_true(memranges_is_empty(&test_memrange));
598 
599 
600 	/* Test memranges_init_with_alignment() correctness with alignment of 1KiB (2^10) */
601 	memranges_init_with_alignment(&test_memrange, cacheable, cacheable, CACHEABLE_TAG, 10);
602 	memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG);
603 	memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG);
604 
605 	check_range_entries_count_and_alignment(&test_memrange, 3, POWER_OF_2(10));
606 
607 	memranges_teardown(&test_memrange);
608 	assert_true(memranges_is_empty(&test_memrange));
609 
610 
611 	/* Test memranges_init_empty() correctness */
612 	memranges_init_empty(&test_memrange, &range_entries[0], ARRAY_SIZE(range_entries));
613 	assert_true(memranges_is_empty(&test_memrange));
614 
615 	memranges_add_resources(&test_memrange, cacheable, cacheable, CACHEABLE_TAG);
616 	memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG);
617 	memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG);
618 
619 	check_range_entries_count_and_alignment(&test_memrange, 3, MEMRANGE_ALIGN);
620 
621 	memranges_teardown(&test_memrange);
622 	assert_true(memranges_is_empty(&test_memrange));
623 
624 
625 	/* Test memranges_init_with_alignment() correctness with alignment of 8KiB (2^13) */
626 	memranges_init_empty_with_alignment(&test_memrange, &range_entries[0],
627 					    ARRAY_SIZE(range_entries), 13);
628 	assert_true(memranges_is_empty(&test_memrange));
629 
630 	memranges_add_resources(&test_memrange, cacheable, cacheable, CACHEABLE_TAG);
631 	memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG);
632 	memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG);
633 
634 	check_range_entries_count_and_alignment(&test_memrange, 3, POWER_OF_2(13));
635 
636 	memranges_teardown(&test_memrange);
637 	assert_true(memranges_is_empty(&test_memrange));
638 }
639 
640 /* Filter function accepting ranges having memory resource flag */
memrange_filter_mem_only(struct device * dev,struct resource * res)641 static int memrange_filter_mem_only(struct device *dev, struct resource *res)
642 {
643 	/* Accept only memory resources */
644 	return res->flags & IORESOURCE_MEM;
645 }
646 
647 /* Filter function rejecting ranges having memory resource flag */
memrange_filter_non_mem(struct device * dev,struct resource * res)648 static int memrange_filter_non_mem(struct device *dev, struct resource *res)
649 {
650 	/* Accept only memory resources */
651 	return !(res->flags & IORESOURCE_MEM);
652 }
653 
654 /* This test verifies memranges_add_resources_filter() function by providing filter functions
655    which accept or reject ranges. */
test_memrange_add_resources_filter(void ** state)656 static void test_memrange_add_resources_filter(void **state)
657 {
658 	const unsigned long cacheable = IORESOURCE_CACHEABLE;
659 	const unsigned long reserved = IORESOURCE_RESERVE;
660 	struct memranges test_memrange;
661 	struct range_entry *ptr;
662 	size_t count = 0;
663 	size_t accepted_tags[] = {CACHEABLE_TAG, RESERVED_TAG};
664 
665 	/* Check if filter accepts range correctly */
666 	memranges_init(&test_memrange, reserved, reserved, RESERVED_TAG);
667 	memranges_add_resources_filter(&test_memrange, cacheable, cacheable, CACHEABLE_TAG,
668 				       memrange_filter_mem_only);
669 
670 	/* Check if filter accepted desired range. */
671 	memranges_each_entry(ptr, &test_memrange)
672 	{
673 		assert_in_set(range_entry_tag(ptr), accepted_tags, ARRAY_SIZE(accepted_tags));
674 		assert_true(IS_ALIGNED(range_entry_base(ptr), MEMRANGE_ALIGN));
675 		assert_true(IS_ALIGNED(range_entry_end(ptr), MEMRANGE_ALIGN));
676 		count++;
677 	}
678 	assert_int_equal(2, count);
679 	count = 0;
680 	memranges_teardown(&test_memrange);
681 
682 	/* Check if filter rejects range correctly */
683 	memranges_init(&test_memrange, reserved, reserved, RESERVED_TAG);
684 	memranges_add_resources_filter(&test_memrange, cacheable, cacheable, CACHEABLE_TAG,
685 				       memrange_filter_non_mem);
686 
687 	check_range_entries_count_and_alignment(&test_memrange, 1, MEMRANGE_ALIGN);
688 
689 	memranges_teardown(&test_memrange);
690 }
691 
main(void)692 int main(void)
693 {
694 	const struct CMUnitTest tests[] = {
695 		cmocka_unit_test(test_memrange_basic),
696 		cmocka_unit_test(test_memrange_clone_insert),
697 		cmocka_unit_test(test_memrange_holes),
698 		cmocka_unit_test(test_memrange_steal),
699 		cmocka_unit_test(test_memrange_init_and_teardown),
700 		cmocka_unit_test(test_memrange_add_resources_filter),
701 	};
702 
703 	return cmocka_run_group_tests_name(__TEST_NAME__ "(Boundary on 4GiB)", tests,
704 					   setup_test_1, NULL)
705 	       + cmocka_run_group_tests_name(__TEST_NAME__ "(Boundaries 1 byte from 4GiB)",
706 					     tests, setup_test_2, NULL)
707 	       + cmocka_run_group_tests_name(__TEST_NAME__ "(Range over 4GiB boundary)", tests,
708 					     setup_test_3, NULL);
709 }
710