Jan Dabros has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/43510 )
Change subject: tests: Add lib/bootmem-test test case ......................................................................
tests: Add lib/bootmem-test test case
Signed-off-by: Jan Dabros jsd@semihalf.com Change-Id: Ic1e539061ee5051d4158712a8a981a475ea7458a --- M tests/lib/Makefile.inc A tests/lib/bootmem-test.c A tests/lib/bootmem-test.ld 3 files changed, 296 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/10/43510/1
diff --git a/tests/lib/Makefile.inc b/tests/lib/Makefile.inc index dae406a..69e3fd4 100644 --- a/tests/lib/Makefile.inc +++ b/tests/lib/Makefile.inc @@ -4,6 +4,7 @@ tests-y += b64_decode-test tests-y += hexstrtobin-test tests-y += memrange-test +tests-y += bootmem-test
string-test-srcs += tests/lib/string-test.c string-test-srcs += src/lib/string.c @@ -19,3 +20,10 @@ memrange-test-srcs += src/lib/memrange.c memrange-test-srcs += tests/stubs/console.c memrange-test-srcs += tests/mocks/device/device_util.c + +bootmem-test-srcs += tests/lib/bootmem-test.c +bootmem-test-srcs += src/lib/bootmem.c +bootmem-test-srcs += src/lib/memrange.c +bootmem-test-srcs += tests/stubs/console.c +bootmem-test-srcs += tests/mocks/device/device_util.c +bootmem-test-cflags += -Wl,--script=tests/lib/bootmem-test.ld diff --git a/tests/lib/bootmem-test.c b/tests/lib/bootmem-test.c new file mode 100644 index 0000000..8082720 --- /dev/null +++ b/tests/lib/bootmem-test.c @@ -0,0 +1,272 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <tests/test.h> +#include <stdlib.h> + +#include <bootmem.h> +#include <commonlib/coreboot_tables.h> +#include <device/device.h> +#include <device/resource.h> +#include <memrange.h> +#include <symbols.h> + +/* Stubs defined to satisfy linker dependencies */ +void cbmem_add_bootmem(void) +{ +} + +void bootmem_arch_add_ranges(void) +{ +} + +struct bootmem_ranges_t { + uint64_t start; + uint64_t size; + uint32_t type; +}; + +struct bootmem_ranges_t *os_ranges; +struct bootmem_ranges_t *ranges; + +/* See tests/lib/bootmem-test.ld for boundaries definitions */ +extern u8 _ramstage_size[]; +#define PROGRAM_START ((uintptr_t)_program) +#define RAMSTAGE_SIZE ((uintptr_t)_ramstage_size) +#define CACHEABLE_START PROGRAM_START +#define CACHEABLE_SIZE (1ULL << 32) +#define CACHEABLE_END (CACHEABLE_START + CACHEABLE_SIZE) +#define RESERVED_START (1ULL << 32) +#define RESERVED_SIZE 0x100000 +#define RESERVED_END (RESERVED_START + RESERVED_SIZE) +#define FIRST (RESERVED_END - CACHEABLE_START) + +/* Note that second region overlaps first */ +struct resource res_mock[] = { + { .base = CACHEABLE_START, .size = CACHEABLE_SIZE, .next = &res_mock[1], + .flags = IORESOURCE_CACHEABLE | IORESOURCE_MEM }, + { .base = RESERVED_START, .size = RESERVED_SIZE, .next = NULL, + .flags = IORESOURCE_RESERVE | IORESOURCE_MEM } +}; + +/* Device simulating RAM */ +struct device mem_device_mock = { + .enabled = 1, + .resource_list = res_mock, + .next = NULL +}; + +/* Simplified version for the purpose of tests */ +static uint32_t bootmem_to_lb_tag(const enum bootmem_type tag) +{ + switch (tag) { + case BM_MEM_RAM: + return LB_MEM_RAM; + case BM_MEM_RESERVED: + return LB_MEM_RESERVED; + default: + return LB_MEM_RESERVED; + } +} + +/* Bootmem layout for bootmem tests + * + * Regions marked with asteriks (***) are not visible for OS + * + * +-------+----CACHEABLE_MEMORY---------+-+ <-0x10000000 + * | | ***PROGRAM*** | | + * | +-----------------------------+ | <-0x10040000 + * | | ***STACK*** | | + * | +-----------------------------+ | <-0x10041000 + * | | + * | | + * | | + * | +-------RESERVED_MEMORY-------+ | <-0x100000000 + * | | | | + * | | | | + * | | | | + * | +-----------------------------+ | <-0x100100000 + * | | + * | | + * +---------------------------------------+ <-0x110000000 + * + */ +static int test_basic_setup(void **state) +{ + os_ranges = (struct bootmem_ranges_t *)malloc(3 * sizeof(*os_ranges)); + + if (!os_ranges) + return -1; + + os_ranges[0].start = CACHEABLE_START; + os_ranges[0].size = RESERVED_START - CACHEABLE_START; + os_ranges[0].type = BM_MEM_RAM; + + os_ranges[1].start = RESERVED_START; + os_ranges[1].size = RESERVED_SIZE; + os_ranges[1].type = BM_MEM_RESERVED; + + os_ranges[2].start = RESERVED_END; + os_ranges[2].size = CACHEABLE_END - RESERVED_END; + os_ranges[2].type = BM_MEM_RAM; + + return 0; +} + +static int test_basic_teardown(void **state) +{ + free(os_ranges); + + return 0; +} + +/* This test need to be run first, in order to use bootmem library API */ +static void test_bootmem_write_mem_table(void **state) +{ + int i; + struct lb_memory *lb_mem; + + will_return_always(search_global_resources, &mem_device_mock); + + /* Allocate space for 5 lb_mem entries to be safe */ + lb_mem = malloc(sizeof(*lb_mem) + 5 * sizeof(struct lb_memory_range)); + + bootmem_write_memory_table(lb_mem); + + /* There should be only three entries visible in coreboot table */ + assert_int_equal(lb_mem->size, 3 * sizeof(struct lb_memory_range)); + + for (i = 0; i < lb_mem->size / sizeof(struct lb_memory_range); i++) { + assert_int_equal(unpack_lb64(lb_mem->map[i].start), os_ranges[i].start); + assert_int_equal(unpack_lb64(lb_mem->map[i].size), os_ranges[i].size); + assert_int_equal(lb_mem->map[i].type, bootmem_to_lb_tag(os_ranges[i].type)); + } + + free(lb_mem); +} + +int os_bootmem_walk_cnt; +int bootmem_walk_cnt; + +static bool verify_os_bootmem_walk(const struct range_entry *r, void *arg) +{ + assert_int_equal(range_entry_base(r), os_ranges[os_bootmem_walk_cnt].start); + assert_int_equal(range_entry_size(r), os_ranges[os_bootmem_walk_cnt].size); + assert_int_equal(range_entry_tag(r), os_ranges[os_bootmem_walk_cnt].type); + + os_bootmem_walk_cnt++; + + return true; +} + +static bool verify_bootmem_walk(const struct range_entry *r, void *arg) +{ + assert_int_equal(range_entry_base(r), ranges[bootmem_walk_cnt].start); + assert_int_equal(range_entry_size(r), ranges[bootmem_walk_cnt].size); + assert_int_equal(range_entry_tag(r), ranges[bootmem_walk_cnt].type); + + bootmem_walk_cnt++; + + return true; +} + +static int test_bootmem_walk_setup(void **state) +{ + if (test_basic_setup(state) < 0) + return -1; + + /* program and stack regions should be merged since they are neighbors */ + ranges = (struct bootmem_ranges_t *)malloc(4 * sizeof(*os_ranges)); + + ranges[0].start = PROGRAM_START; + ranges[0].size = RAMSTAGE_SIZE; + ranges[0].type = BM_MEM_RAMSTAGE; + + ranges[1].start = CACHEABLE_START + RAMSTAGE_SIZE; + ranges[1].size = RESERVED_START - ranges[1].start; + ranges[1].type = BM_MEM_RAM; + + ranges[2].start = RESERVED_START; + ranges[2].size = RESERVED_SIZE; + ranges[2].type = BM_MEM_RESERVED; + + ranges[3].start = RESERVED_END; + ranges[3].size = CACHEABLE_END - RESERVED_END; + ranges[3].type = BM_MEM_RAM; + + os_bootmem_walk_cnt = 0; + bootmem_walk_cnt = 0; + + return 0; +} + +static int test_bootmem_walk_teardown(void **state) +{ + test_basic_teardown(state); + + free(ranges); + + return 0; +} + +static void test_bootmem_walk(void **state) +{ + bootmem_walk_os_mem(verify_os_bootmem_walk, NULL); + bootmem_walk(verify_bootmem_walk, NULL); + + assert_int_equal(os_bootmem_walk_cnt, 3); + assert_int_equal(bootmem_walk_cnt, 4); +} + +static void test_bootmem_region_targets_type(void **state) +{ + int ret; + + ret = bootmem_region_targets_type(PROGRAM_START, RAMSTAGE_SIZE, BM_MEM_RAMSTAGE); + assert_int_equal(ret, 1); + + /* Below range covers two differently tagged regions */ + ret = bootmem_region_targets_type(PROGRAM_START, RAMSTAGE_SIZE, BM_MEM_RAMSTAGE + 1); + assert_int_equal(ret, 0); +} + +static void test_bootmem_allocate_buffer(void **state) +{ + void *buf; + + /* All allocated buffers should be below 32bit boundary */ + buf = bootmem_allocate_buffer((1ULL << 32)); + assert_null(buf); + + /* Try too big size for our BM_MEM_RAM range below 32bit boundary */ + buf = bootmem_allocate_buffer(RESERVED_START - PROGRAM_START); + assert_null(buf); + + /* Two working cases */ + buf = bootmem_allocate_buffer(0xE0000000); + assert_non_null(buf); + assert_in_range((uintptr_t)buf, CACHEABLE_START + RAMSTAGE_SIZE, RESERVED_START); + + buf = bootmem_allocate_buffer(0xF000000); + assert_non_null(buf); + assert_in_range((uintptr_t)buf, CACHEABLE_START + RAMSTAGE_SIZE, RESERVED_START); + + /* Run out of memory for new allocations */ + buf = bootmem_allocate_buffer(0x1000000); + assert_null(buf); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_bootmem_write_mem_table, + test_basic_setup, + test_basic_teardown), + cmocka_unit_test_setup_teardown(test_bootmem_walk, + test_bootmem_walk_setup, + test_bootmem_walk_teardown), + cmocka_unit_test(test_bootmem_allocate_buffer), + cmocka_unit_test(test_bootmem_region_targets_type) + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/lib/bootmem-test.ld b/tests/lib/bootmem-test.ld new file mode 100644 index 0000000..eefef63 --- /dev/null +++ b/tests/lib/bootmem-test.ld @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Below values needs to be in sync with tests/lib/bootmem-test.c code, + * since there are some assumptions about overlapping regions, neighboring + * ones etc. + */ +SECTIONS { + _program = 0x10000000; + _eprogram = _program + 0x40000; + _stack = _eprogram; + _estack = _stack + 0x1000; + _ramstage_size = _estack - _program; +} +/* Below instruction is just for the default script not being overwritten by this helper */ +INSERT AFTER .rodata;