blob: 85a01364e5a2d456d906028b2d81b90995c883a9 [file] [log] [blame]
Jan Dabros93576932020-07-16 12:05:47 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <bootmem.h>
4#include <commonlib/coreboot_tables.h>
5#include <device/device.h>
6#include <device/resource.h>
7#include <memrange.h>
8#include <stdlib.h>
9#include <string.h>
10#include <symbols.h>
11#include <tests/test.h>
12
13/* Stubs defined to satisfy linker dependencies */
14void cbmem_add_bootmem(void)
15{
16}
17
18void bootmem_arch_add_ranges(void)
19{
20}
21
22struct bootmem_ranges_t {
23 uint64_t start;
24 uint64_t size;
25 uint32_t type;
26};
27
28/* Define symbols for regions required by bootmem.
29 Define constants for regions that do not need to be defined in the executable.
30 There is no need for region memory, just start, end and size symbols are required.
31 Only used values are defined. */
32#define ZERO_REGION_START ((uintptr_t)0x0)
33#define ZERO_REGION_SIZE ((uintptr_t)0x10000)
34
35TEST_REGION_UNALLOCATED(program, 0x10000000, 0x40000);
36#define PROGRAM_START ((uintptr_t)_program)
37#define PROGRAM_SIZE REGION_SIZE(program)
38
39#define CACHEABLE_START ((uintptr_t)0x10000000ULL)
40#define CACHEABLE_SIZE ((uintptr_t)0x100000000ULL)
41#define CACHEABLE_END ((uintptr_t)(CACHEABLE_START + CACHEABLE_SIZE))
42
43/* Stack region end address is hardcoded because `<const> - <symbol>` does not work in GCC */
44TEST_REGION_UNALLOCATED(stack, 0x10040000, 0x1000);
45#define STACK_START ((uintptr_t)_stack)
46#define STACK_SIZE REGION_SIZE(stack)
47#define STACK_END ((uintptr_t)(0x10040000 + 0x1000))
48
49#define RESERVED_START ((uintptr_t)0x100000000ULL)
50#define RESERVED_SIZE ((uintptr_t)0x100000)
51#define RESERVED_END ((uintptr_t)(RESERVED_START + RESERVED_SIZE))
52
53TEST_REGION_UNALLOCATED(ramstage, 0x10000000, 0x41000);
54#define RAMSTAGE_START ((uintptr_t)_ramstage)
55#define RAMSTAGE_SIZE REGION_SIZE(ramstage)
56
57#define CACHEABLE_START_TO_RESERVED_START_SIZE (RESERVED_START - CACHEABLE_START)
58#define RESERVED_END_TO_CACHEABLE_END_SIZE (CACHEABLE_END - RESERVED_END)
59#define STACK_END_TO_RESERVED_START_SIZE (RESERVED_START - STACK_END)
60
61
62/* Bootmem layout for tests
63 *
64 * Regions marked with asterisks (***) are not visible for OS
65 *
66 * +------------------ZERO-----------------+ <-0x0
67 * | |
68 * +---------------------------------------+ <-0x10000
69 *
70 * +-------+----CACHEABLE_MEMORY---------+-+ <-0x10000000
71 * | | ***PROGRAM*** | |
72 * | +-----------------------------+ | <-0x10040000
73 * | | ***STACK*** | |
74 * | +-----------------------------+ | <-0x10041000
75 * | |
76 * | |
77 * | |
78 * | +-------RESERVED_MEMORY-------+ | <-0x100000000
79 * | | | |
80 * | | | |
81 * | | | |
82 * | +-----------------------------+ | <-0x100100000
83 * | |
84 * | |
85 * +---------------------------------------+ <-0x110000000
86 *
87 * Ramstage covers PROGRAM and STACK regions.
88 */
89struct bootmem_ranges_t os_ranges_mock[] = {
90 [0] = { .start = ZERO_REGION_START, .size = ZERO_REGION_SIZE,
91 .type = BM_MEM_RAM},
92 [1] = { .start = CACHEABLE_START, .size = CACHEABLE_START_TO_RESERVED_START_SIZE,
93 .type = BM_MEM_RAM },
94 [2] = { .start = RESERVED_START, .size = RESERVED_SIZE,
95 .type = BM_MEM_RESERVED },
96 [3] = { .start = RESERVED_END, .size = RESERVED_END_TO_CACHEABLE_END_SIZE,
97 .type = BM_MEM_RAM },
98};
99
100struct bootmem_ranges_t ranges_mock[] = {
101 [0] = { .start = ZERO_REGION_START, .size = ZERO_REGION_SIZE,
102 .type = BM_MEM_RAM },
103 [1] = { .start = RAMSTAGE_START, .size = RAMSTAGE_SIZE,
104 .type = BM_MEM_RAMSTAGE },
105 [2] = { .start = STACK_END, .size = STACK_END_TO_RESERVED_START_SIZE,
106 .type = BM_MEM_RAM },
107 [3] = { .start = RESERVED_START, .size = RESERVED_SIZE,
108 .type = BM_MEM_RESERVED },
109 [4] = { .start = RESERVED_END, .size = RESERVED_END_TO_CACHEABLE_END_SIZE,
110 .type = BM_MEM_RAM },
111};
112
113struct bootmem_ranges_t *os_ranges = os_ranges_mock;
114struct bootmem_ranges_t *ranges = ranges_mock;
115
116/* Note that second region overlaps first */
117struct resource res_mock[] = {
118 { .base = ZERO_REGION_START, .size = ZERO_REGION_SIZE, .next = &res_mock[1],
119 .flags = IORESOURCE_CACHEABLE | IORESOURCE_MEM },
120 { .base = CACHEABLE_START, .size = CACHEABLE_SIZE, .next = &res_mock[2],
121 .flags = IORESOURCE_CACHEABLE | IORESOURCE_MEM },
122 { .base = RESERVED_START, .size = RESERVED_SIZE, .next = NULL,
123 .flags = IORESOURCE_RESERVE | IORESOURCE_MEM }
124};
125
126/* Device simulating RAM */
127struct device mem_device_mock = {
128 .enabled = 1,
129 .resource_list = res_mock,
130 .next = NULL
131};
132
133struct device *all_devices = &mem_device_mock;
134
135/* Simplified version for the purpose of tests */
136static uint32_t bootmem_to_lb_tag(const enum bootmem_type tag)
137{
138 switch (tag) {
139 case BM_MEM_RAM:
140 return LB_MEM_RAM;
141 case BM_MEM_RESERVED:
142 return LB_MEM_RESERVED;
143 default:
144 return LB_MEM_RESERVED;
145 }
146}
147
148static void test_bootmem_write_mem_table(void **state)
149{
150 /* Space for 10 lb_mem entries to be safe */
151 const size_t lb_mem_max_size = sizeof(struct lb_memory)
152 + 10 * sizeof(struct lb_memory_range);
153 const size_t expected_allocation_size =
154 (sizeof(struct lb_memory)
155 + ARRAY_SIZE(os_ranges_mock) * sizeof(struct lb_memory_range));
156 const size_t required_unused_space_size = lb_mem_max_size - expected_allocation_size;
157 int i;
158 struct lb_memory *lb_mem;
159 /* Allocate buffer and fill it. Use it to ensure correct size of space used
160 by bootmem_write_memory_table() */
161 u8 sentinel_value_buffer[required_unused_space_size];
162 memset(sentinel_value_buffer, 0x77, required_unused_space_size);
163
164 lb_mem = malloc(lb_mem_max_size);
165 /* Fill rest of buffer with sentinel value */
166 memset(((u8 *)lb_mem) + expected_allocation_size, 0x77, required_unused_space_size);
167
168 bootmem_write_memory_table(lb_mem);
169
170 /* There should be only `os_ranges_mock` entries visible in coreboot table */
171 assert_int_equal(lb_mem->size,
172 ARRAY_SIZE(os_ranges_mock) * sizeof(struct lb_memory_range));
173 assert_memory_equal(sentinel_value_buffer,
174 ((u8 *)lb_mem) + expected_allocation_size,
175 required_unused_space_size);
176
177 for (i = 0; i < lb_mem->size / sizeof(struct lb_memory_range); i++) {
178 assert_int_equal(unpack_lb64(lb_mem->map[i].start), os_ranges[i].start);
179 assert_int_equal(unpack_lb64(lb_mem->map[i].size), os_ranges[i].size);
180 assert_int_equal(lb_mem->map[i].type, bootmem_to_lb_tag(os_ranges[i].type));
181 }
182
183 free(lb_mem);
184}
185
186int os_bootmem_walk_cnt;
187int bootmem_walk_cnt;
188
189static bool verify_os_bootmem_walk(const struct range_entry *r, void *arg)
190{
191 assert_int_equal(range_entry_base(r), os_ranges[os_bootmem_walk_cnt].start);
192 assert_int_equal(range_entry_size(r), os_ranges[os_bootmem_walk_cnt].size);
193 assert_int_equal(range_entry_tag(r), os_ranges[os_bootmem_walk_cnt].type);
194
195 os_bootmem_walk_cnt++;
196
197 return true;
198}
199
200static bool verify_bootmem_walk(const struct range_entry *r, void *arg)
201{
202 assert_int_equal(range_entry_base(r), ranges[bootmem_walk_cnt].start);
203 assert_int_equal(range_entry_size(r), ranges[bootmem_walk_cnt].size);
204 assert_int_equal(range_entry_tag(r), ranges[bootmem_walk_cnt].type);
205
206 bootmem_walk_cnt++;
207
208 return true;
209}
210
211static bool count_entries_os_bootmem_walk(const struct range_entry *r, void *arg)
212{
213 os_bootmem_walk_cnt++;
214
215 return true;
216}
217
218static bool count_entries_bootmem_walk(const struct range_entry *r, void *arg)
219{
220 bootmem_walk_cnt++;
221
222 return true;
223}
224
225/* This function initializes bootmem using bootmem_write_memory_table().
226 bootmem_init() is not accessible directly because it is static. */
227static void init_memory_table_library(void)
228{
229 struct lb_memory *lb_mem;
230
231 /* Allocate space for 10 lb_mem entries to be safe */
232 lb_mem = malloc(sizeof(*lb_mem) + 10 * sizeof(struct lb_memory_range));
233
234 /* We need to call this only to initialize library */
235 bootmem_write_memory_table(lb_mem);
236 free(lb_mem);
237}
238
239static void test_bootmem_add_range(void **state)
240{
241 init_memory_table_library();
242
243 os_bootmem_walk_cnt = 0;
244 bootmem_walk_os_mem(count_entries_os_bootmem_walk, NULL);
245 assert_int_equal(os_bootmem_walk_cnt, 4);
246
247 bootmem_walk_cnt = 0;
248 bootmem_walk(count_entries_bootmem_walk, NULL);
249 assert_int_equal(bootmem_walk_cnt, 5);
250
251 expect_assert_failure(
252 bootmem_add_range(ALIGN_UP(PROGRAM_START, 4096),
253 ALIGN_DOWN(PROGRAM_SIZE / 2, 4096),
254 BM_MEM_ACPI)
255 );
256
257 os_bootmem_walk_cnt = 0;
258 bootmem_walk_os_mem(count_entries_os_bootmem_walk, NULL);
259 assert_int_equal(os_bootmem_walk_cnt, 4);
260
261 bootmem_walk_cnt = 0;
262 bootmem_walk(count_entries_bootmem_walk, NULL);
263 assert_int_equal(bootmem_walk_cnt, 6);
264
265 /* Do not expect assert failure as BM_MEM_RAMSTAGE should not be added to os_bootmem */
266 bootmem_add_range(ALIGN_UP(STACK_END + 4096, 4096),
267 ALIGN_DOWN(STACK_END_TO_RESERVED_START_SIZE / 2, 4096),
268 BM_MEM_RAMSTAGE);
269
270 os_bootmem_walk_cnt = 0;
271 bootmem_walk_os_mem(count_entries_os_bootmem_walk, NULL);
272 assert_int_equal(os_bootmem_walk_cnt, 4);
273
274 /* Two entries are added because added range is in middle of another */
275 bootmem_walk_cnt = 0;
276 bootmem_walk(count_entries_bootmem_walk, NULL);
277 assert_int_equal(bootmem_walk_cnt, 8);
278}
279
280static void test_bootmem_walk(void **state)
281{
282 init_memory_table_library();
283
284 os_bootmem_walk_cnt = 0;
285 bootmem_walk_os_mem(verify_os_bootmem_walk, NULL);
286 assert_int_equal(os_bootmem_walk_cnt, 4);
287
288 bootmem_walk_cnt = 0;
289 bootmem_walk(verify_bootmem_walk, NULL);
290 assert_int_equal(bootmem_walk_cnt, 5);
291}
292
293static void test_bootmem_region_targets_type(void **state)
294{
295 int ret;
296 u64 subregion_start;
297 u64 subregion_size;
298
299 init_memory_table_library();
300
301 /* Single whole region */
302 ret = bootmem_region_targets_type(RAMSTAGE_START, RAMSTAGE_SIZE, BM_MEM_RAMSTAGE);
303 assert_int_equal(ret, 1);
304
305 /* Expect fail because of incorrect bootmem_type */
306 ret = bootmem_region_targets_type(RAMSTAGE_START, RAMSTAGE_SIZE, BM_MEM_RESERVED);
307 assert_int_equal(ret, 0);
308
309 /* Range covering one more byte than one region*/
310 ret = bootmem_region_targets_type(RAMSTAGE_START, RAMSTAGE_SIZE + 1, BM_MEM_RAMSTAGE);
311 assert_int_equal(ret, 0);
312
313 /* Expect success for subregion of ramstage stretching from point in program range
314 to point in stack range. */
315 subregion_start = PROGRAM_START + PROGRAM_SIZE / 4;
316 subregion_size = STACK_END - STACK_SIZE / 4 - subregion_start;
317 ret = bootmem_region_targets_type(subregion_start, subregion_size, BM_MEM_RAMSTAGE);
318 assert_int_equal(ret, 1);
319
320 /* Expect fail for range covering more than one tag as there is no BM_MEM_CACHEABLE */
321 subregion_start = STACK_START + STACK_SIZE / 2;
322 subregion_size = RESERVED_START + RESERVED_SIZE / 4 * 3 - subregion_start;
323 ret = bootmem_region_targets_type(subregion_start, subregion_size, BM_MEM_RAM);
324 assert_int_equal(ret, 0);
325
326 /* Middle of range should not fail */
327 ret = bootmem_region_targets_type(RESERVED_START + RESERVED_SIZE / 4,
328 RESERVED_SIZE / 2, BM_MEM_RESERVED);
329 assert_int_equal(ret, 1);
330
331 /* Subsection of range bordering end edge */
332 ret = bootmem_region_targets_type(RESERVED_END + RESERVED_END_TO_CACHEABLE_END_SIZE / 2,
333 RESERVED_END_TO_CACHEABLE_END_SIZE / 2, BM_MEM_RAM);
334 assert_int_equal(ret, 1);
335
336 /* Region touching zero */
337 ret = bootmem_region_targets_type(ZERO_REGION_START, ZERO_REGION_SIZE, BM_MEM_RAM);
338 assert_int_equal(ret, 1);
339
340 /* Expect failure when passing zero as size. */
341 ret = bootmem_region_targets_type(ZERO_REGION_START, 0, BM_MEM_RAM);
342 assert_int_equal(ret, 0);
343 ret = bootmem_region_targets_type(RESERVED_START, 0, BM_MEM_RESERVED);
344 assert_int_equal(ret, 0);
345}
346
347/* Action function used to check alignment of size and base of allocated ranges */
348static bool verify_bootmem_allocate_buffer(const struct range_entry *r, void *arg)
349{
350 if (range_entry_tag(r) == BM_MEM_PAYLOAD) {
351 assert_true(IS_ALIGNED(range_entry_base(r), 4096));
352 assert_true(IS_ALIGNED(range_entry_size(r), 4096));
353 }
354
355 return true;
356}
357
358
359static void test_bootmem_allocate_buffer(void **state)
360{
361 void *buf;
362 void *prev;
363
364 init_memory_table_library();
365
366 /* All allocated buffers should be below 32bit boundary */
367 buf = bootmem_allocate_buffer(1ULL << 32);
368 assert_null(buf);
369
370 /* Try too big size for our BM_MEM_RAM range below 32bit boundary */
371 buf = bootmem_allocate_buffer(RESERVED_START - PROGRAM_START);
372 assert_null(buf);
373
374 /* Two working cases */
375 buf = bootmem_allocate_buffer(0xE0000000);
376 assert_non_null(buf);
377 assert_int_equal(1, bootmem_region_targets_type((uintptr_t)buf,
378 0xE0000000, BM_MEM_PAYLOAD));
379 assert_in_range((uintptr_t)buf, CACHEABLE_START + RAMSTAGE_SIZE, RESERVED_START);
380 /* Check if allocated (payload) ranges have their base and size aligned */
381 bootmem_walk(verify_bootmem_allocate_buffer, NULL);
382
383 prev = buf;
384 buf = bootmem_allocate_buffer(0xF000000);
385 assert_non_null(buf);
386 assert_int_equal(1, bootmem_region_targets_type((uintptr_t)buf,
387 0xF000000, BM_MEM_PAYLOAD));
388 assert_in_range((uintptr_t)buf, CACHEABLE_START + RAMSTAGE_SIZE, RESERVED_START);
389 /* Check if newly allocated buffer does not overlap with previously allocated range */
390 assert_not_in_range((uintptr_t)buf, (uintptr_t)prev, (uintptr_t)prev + 0xE0000000);
391 /* Check if allocated (payload) ranges have their base and size aligned */
392 bootmem_walk(verify_bootmem_allocate_buffer, NULL);
393
394 /* Run out of memory for new allocations */
395 buf = bootmem_allocate_buffer(0x1000000);
396 assert_null(buf);
397}
398
399int main(void)
400{
401 const struct CMUnitTest tests[] = {
402 cmocka_unit_test(test_bootmem_write_mem_table),
403 cmocka_unit_test(test_bootmem_add_range),
404 cmocka_unit_test(test_bootmem_walk),
405 cmocka_unit_test(test_bootmem_allocate_buffer),
406 cmocka_unit_test(test_bootmem_region_targets_type)
407 };
408
409 return cmocka_run_group_tests(tests, NULL, NULL);
410}