| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <tests/test.h> |
| |
| #include <device/device.h> |
| #include <commonlib/helpers.h> |
| #include <memrange.h> |
| |
| #define MEMRANGE_ALIGN (POWER_OF_2(12)) |
| |
| enum mem_types { |
| /* Avoid using 0 to verify that UUT really sets this memory, |
| but keep value small, as this will be an index in the table */ |
| CACHEABLE_TAG = 0x10, |
| RESERVED_TAG, |
| READONLY_TAG, |
| INSERTED_TAG, |
| HOLE_TAG, |
| UNASSIGNED_TAG, |
| END_OF_RESOURCES |
| }; |
| |
| /* Indices of entries matters, since it must reflect mem_types enum */ |
| struct resource res_mock_1[] = { |
| [UNASSIGNED_TAG] = {.base = 0x0, |
| .size = 0x8000, |
| .next = &res_mock_1[CACHEABLE_TAG], |
| .flags = IORESOURCE_MEM | IORESOURCE_PREFETCH}, |
| [CACHEABLE_TAG] = {.base = 0xE000, |
| .size = 0xF2000, |
| .next = &res_mock_1[RESERVED_TAG], |
| .flags = IORESOURCE_CACHEABLE | IORESOURCE_MEM | |
| IORESOURCE_ASSIGNED }, |
| [RESERVED_TAG] = {.base = 4ULL * GiB, |
| .size = 4ULL * KiB, |
| .next = &res_mock_1[READONLY_TAG], |
| .flags = IORESOURCE_RESERVE | IORESOURCE_MEM | |
| IORESOURCE_ASSIGNED }, |
| [READONLY_TAG] = {.base = 0xFF0000, |
| .size = 0x10000, |
| .next = NULL, |
| .flags = IORESOURCE_READONLY | IORESOURCE_MEM | |
| IORESOURCE_ASSIGNED } |
| }; |
| |
| /* Boundary 1 byte below 4GiB and 1 byte above 4GiB. */ |
| struct resource res_mock_2[] = { |
| [CACHEABLE_TAG] = {.base = 0x1000000, |
| .size = 4ULL * GiB - 0x1000001ULL, |
| .next = &res_mock_2[RESERVED_TAG], |
| .flags = IORESOURCE_CACHEABLE | IORESOURCE_MEM | |
| IORESOURCE_ASSIGNED }, |
| [RESERVED_TAG] = {.base = 4ULL * GiB + 1ULL, |
| .size = 4ULL * GiB, |
| .next = &res_mock_2[READONLY_TAG], |
| .flags = IORESOURCE_RESERVE | IORESOURCE_MEM | |
| IORESOURCE_ASSIGNED }, |
| [READONLY_TAG] = {.base = 0, |
| .size = 0x10000, |
| .next = NULL, |
| .flags = IORESOURCE_READONLY | IORESOURCE_MEM | |
| IORESOURCE_ASSIGNED } |
| }; |
| |
| /* Boundary crossing 4GiB. */ |
| struct resource res_mock_3[] = { |
| [CACHEABLE_TAG] = {.base = 0xD000, |
| .size = 0xF3000, |
| .next = &res_mock_3[RESERVED_TAG], |
| .flags = IORESOURCE_CACHEABLE | IORESOURCE_MEM | |
| IORESOURCE_ASSIGNED }, |
| [RESERVED_TAG] = {.base = 1ULL * GiB, |
| .size = 4ULL * GiB, |
| .next = &res_mock_3[READONLY_TAG], |
| .flags = IORESOURCE_RESERVE | IORESOURCE_MEM | |
| IORESOURCE_ASSIGNED }, |
| [READONLY_TAG] = {.base = 0xFF0000, |
| .size = 0x10000, |
| .next = NULL, |
| .flags = IORESOURCE_READONLY | IORESOURCE_MEM | |
| IORESOURCE_ASSIGNED} |
| }; |
| |
| |
| struct device mock_device = {.enabled = 1}; |
| |
| /* Fake memory devices handle */ |
| struct device *all_devices = &mock_device; |
| |
| int setup_test_1(void **state) |
| { |
| *state = res_mock_1; |
| mock_device.resource_list = &res_mock_1[UNASSIGNED_TAG]; |
| |
| return 0; |
| } |
| |
| int setup_test_2(void **state) |
| { |
| *state = res_mock_2; |
| mock_device.resource_list = &res_mock_2[CACHEABLE_TAG]; |
| |
| return 0; |
| } |
| |
| int setup_test_3(void **state) |
| { |
| *state = res_mock_3; |
| mock_device.resource_list = &res_mock_3[CACHEABLE_TAG]; |
| |
| return 0; |
| } |
| |
| resource_t get_aligned_base(struct resource *res, struct range_entry *entry) |
| { |
| return ALIGN_DOWN(res[range_entry_tag(entry)].base, MEMRANGE_ALIGN); |
| } |
| |
| resource_t get_aligned_end(struct resource *res, struct range_entry *entry) |
| { |
| resource_t end = res[range_entry_tag(entry)].base + res[range_entry_tag(entry)].size |
| + (res[range_entry_tag(entry)].base - range_entry_base(entry)); |
| return ALIGN_UP(end, MEMRANGE_ALIGN); |
| } |
| |
| /* |
| * This test verifies memranges_init(), memranges_add_resources() and memranges_teardown() |
| * functions. It covers basic functionality of memrange library - implementation of creating |
| * memrange structure from resources available on the platform and method for free'ing |
| * allocated memory. |
| * |
| * Example memory ranges (res_mock1) for test_memrange_basic. |
| * Ranges marked with asterisks (***) are not added to the test_memrange. |
| * |
| * +-------UNASSIGNED_TAG--------+ <-0x0 |
| * | | |
| * +-----------------------------+ <-0x8000 |
| * |
| * |
| * |
| * +--------CACHEABLE_TAG--------+ <-0xE000 |
| * | | |
| * | | |
| * | | |
| * +-----------------------------+ <-0x100000 |
| * |
| * |
| * |
| * +-----***READONLY_TAG***------+ <-0xFF0000 |
| * | | |
| * | | |
| * | | |
| * +-----------------------------+ <-0x1000000 |
| * |
| * |
| * +--------RESERVED_TAG---------+ <-0x100000000 |
| * | | |
| * +-----------------------------+ <-0x100001000 |
| */ |
| static void test_memrange_basic(void **state) |
| { |
| int counter = 0; |
| const unsigned long cacheable = IORESOURCE_CACHEABLE; |
| const unsigned long reserved = IORESOURCE_RESERVE; |
| const unsigned long prefetchable = IORESOURCE_PREFETCH; |
| struct range_entry *ptr; |
| struct memranges test_memrange; |
| struct resource *res_mock = *state; |
| resource_t prev_base = 0; |
| |
| memranges_init_empty(&test_memrange, NULL, 0); |
| memranges_add_resources(&test_memrange, prefetchable, prefetchable, UNASSIGNED_TAG); |
| memranges_add_resources(&test_memrange, cacheable, cacheable, CACHEABLE_TAG); |
| memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG); |
| |
| /* There should be two entries, since cacheable and reserved regions are not neighbors. |
| Besides these two, a region with an unassigned tag is defined, to emulate an unmapped |
| PCI BAR resource. This resource is not mapped into host physical address and hence |
| should not be picked up by memranges_add_resources().*/ |
| |
| memranges_each_entry(ptr, &test_memrange) |
| { |
| assert_in_range(range_entry_tag(ptr), CACHEABLE_TAG, RESERVED_TAG); |
| assert_int_equal(range_entry_base(ptr), get_aligned_base(res_mock, ptr)); |
| |
| assert_int_equal(range_entry_end(ptr), get_aligned_end(res_mock, ptr)); |
| |
| /* Ranges have to be returned in increasing order */ |
| assert_true(prev_base <= range_entry_base(ptr)); |
| |
| prev_base = range_entry_base(ptr); |
| counter++; |
| }; |
| assert_int_equal(counter, 2); |
| counter = 0; |
| |
| /* Remove initial memrange */ |
| memranges_teardown(&test_memrange); |
| memranges_each_entry(ptr, &test_memrange) counter++; |
| assert_int_equal(counter, 0); |
| } |
| |
| /* |
| * This test verifies memranges_clone(), memranges_insert() and memranges_update_tag() |
| * functions. All operations are performed on cloned memrange. One of the most important thing |
| * to check, is that memrange_insert() should remove all ranges which are covered by the newly |
| * inserted one. |
| * |
| * Example memory ranges (res_mock1) for test_memrange_clone_insert. |
| * Ranges marked with asterisks (***) are not added to the clone_memrange. |
| * Ranges marked with (^) have tag value changed during test. |
| * |
| * +--------CACHEABLE_TAG--------+ <-0xE000 |
| * +------|----INSERTED_TAG----------+ | <-0xF000 |
| * | | (^READONLY_TAG^) | | |
| * | | | | |
| * | +-----------------------------+ <-0x100000 |
| * +---------------------------------+ <-0x101000 |
| * |
| * |
| * +-----***READONLY_TAG***------+ <-0xFF0000 |
| * | | |
| * | | |
| * | | |
| * +-----------------------------+ <-0x1000000 |
| * |
| * |
| * +------+---------RESERVED_TAG-----+--+ <-0x100000000 |
| * | | | | |
| * | +-----------------------------+ <-0x100001000 |
| * +-----------INSERTED_TAG----------+ <-0x100002000 |
| */ |
| static void test_memrange_clone_insert(void **state) |
| { |
| int counter = 0; |
| const unsigned long cacheable = IORESOURCE_CACHEABLE; |
| const unsigned long reserved = IORESOURCE_RESERVE; |
| struct range_entry *ptr; |
| struct memranges test_memrange, clone_memrange; |
| struct resource *res_mock = *state; |
| const resource_t new_range_begin_offset = 1ULL << 12; |
| |
| memranges_init(&test_memrange, cacheable, cacheable, CACHEABLE_TAG); |
| memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG); |
| |
| memranges_clone(&clone_memrange, &test_memrange); |
| memranges_teardown(&test_memrange); |
| |
| /* Verify that new one is really a clone */ |
| memranges_each_entry(ptr, &clone_memrange) |
| { |
| assert_in_range(range_entry_tag(ptr), CACHEABLE_TAG, END_OF_RESOURCES - 1); |
| assert_int_equal(range_entry_base(ptr), get_aligned_base(res_mock, ptr)); |
| |
| assert_int_equal(range_entry_end(ptr), get_aligned_end(res_mock, ptr)); |
| |
| counter++; |
| }; |
| assert_int_equal(counter, 2); |
| counter = 0; |
| |
| /* Insert new range, which will overlap with first region. */ |
| memranges_insert(&clone_memrange, res_mock[CACHEABLE_TAG].base + new_range_begin_offset, |
| res_mock[CACHEABLE_TAG].size, INSERTED_TAG); |
| |
| /* Three ranges should be there - CACHEABLE(shrunk), INSERTED and RESERVED */ |
| memranges_each_entry(ptr, &clone_memrange) |
| { |
| resource_t expected_end; |
| |
| if (range_entry_tag(ptr) == CACHEABLE_TAG) { |
| assert_int_equal(range_entry_base(ptr), res_mock[CACHEABLE_TAG].base); |
| |
| expected_end = res_mock[CACHEABLE_TAG].base + new_range_begin_offset; |
| assert_int_equal(range_entry_end(ptr), expected_end); |
| } |
| if (range_entry_tag(ptr) == INSERTED_TAG) { |
| assert_int_equal(range_entry_base(ptr), |
| res_mock[CACHEABLE_TAG].base + new_range_begin_offset); |
| |
| expected_end = res_mock[CACHEABLE_TAG].base + new_range_begin_offset |
| + res_mock[CACHEABLE_TAG].size; |
| assert_int_equal(range_entry_end(ptr), |
| ALIGN_UP(expected_end, MEMRANGE_ALIGN)); |
| } |
| counter++; |
| } |
| assert_int_equal(counter, 3); |
| counter = 0; |
| |
| /* Insert new region, which will shadow readonly range. |
| * Additionally verify API for updating tags */ |
| memranges_update_tag(&clone_memrange, INSERTED_TAG, READONLY_TAG); |
| |
| memranges_each_entry(ptr, &clone_memrange) |
| { |
| resource_t expected_end; |
| |
| assert_int_not_equal(range_entry_tag(ptr), INSERTED_TAG); |
| if (range_entry_tag(ptr) == READONLY_TAG) { |
| assert_int_equal(range_entry_base(ptr), |
| res_mock[CACHEABLE_TAG].base + new_range_begin_offset); |
| |
| expected_end = res_mock[CACHEABLE_TAG].base + new_range_begin_offset |
| + res_mock[CACHEABLE_TAG].size; |
| assert_int_equal(range_entry_end(ptr), |
| ALIGN_UP(expected_end, MEMRANGE_ALIGN)); |
| } |
| }; |
| |
| /* Check if alignment (4KiB) is properly applied, that is begin - DOWN and end - UP */ |
| memranges_insert(&clone_memrange, res_mock[RESERVED_TAG].base + 0xAD, |
| res_mock[RESERVED_TAG].size, INSERTED_TAG); |
| |
| memranges_each_entry(ptr, &clone_memrange) |
| { |
| resource_t expected_end; |
| |
| assert_int_not_equal(range_entry_tag(ptr), RESERVED_TAG); |
| if (range_entry_tag(ptr) == INSERTED_TAG) { |
| assert_int_equal( |
| range_entry_base(ptr), |
| ALIGN_DOWN(res_mock[RESERVED_TAG].base, MEMRANGE_ALIGN)); |
| |
| expected_end = ALIGN_DOWN(res_mock[RESERVED_TAG].base, MEMRANGE_ALIGN) |
| + new_range_begin_offset + res_mock[RESERVED_TAG].size; |
| expected_end = ALIGN_UP(expected_end, MEMRANGE_ALIGN); |
| |
| assert_int_equal(range_entry_end(ptr), expected_end); |
| } |
| counter++; |
| } |
| assert_int_equal(counter, 3); |
| |
| /* Free clone */ |
| memranges_teardown(&clone_memrange); |
| } |
| |
| /* |
| * This test verifies memranges_fill_holes_up_to() and memranges_create_hole(). Idea of the test |
| * is to fill all holes, so that we end up with contiguous address space fully covered by |
| * entries. Then, holes are created on the border of two different regions |
| * |
| * Example memory ranges (res_mock1) for test_memrange_holes. |
| * Space marked with (/) is not covered by any region at the end of the test. |
| * |
| * +--------CACHEABLE_TAG--------+ <-0xE000 |
| * | | |
| * | | |
| * //|/////////////////////////////| <-0xFF000 |
| * //+-----------HOLE_TAG----------+ <-0x100000 |
| * //|/////////////////////////////| <-0x101000 |
| * | | |
| * | | |
| * | | |
| * | | |
| * +--------RESERVED_TAG---------+ <-0x100000000 |
| * | | |
| * +-----------------------------+ <-0x100001000 |
| */ |
| static void test_memrange_holes(void **state) |
| { |
| int counter = 0; |
| const unsigned long cacheable = IORESOURCE_CACHEABLE; |
| const unsigned long reserved = IORESOURCE_RESERVE; |
| struct range_entry *ptr; |
| struct range_entry *hole_ptr = NULL; |
| struct memranges test_memrange; |
| struct resource *res_mock = *state; |
| int holes_found = 0; |
| resource_t last_range_end = 0; |
| const resource_t holes_fill_end = res_mock[RESERVED_TAG].base; |
| |
| memranges_init(&test_memrange, cacheable, cacheable, CACHEABLE_TAG); |
| memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG); |
| |
| /* Count holes in ranges */ |
| memranges_each_entry(ptr, &test_memrange) |
| { |
| if (!last_range_end) { |
| last_range_end = range_entry_end(ptr); |
| continue; |
| } |
| |
| |
| if (range_entry_base(ptr) != last_range_end) { |
| holes_found++; |
| last_range_end = range_entry_end(ptr); |
| } |
| |
| if (range_entry_base(ptr) >= holes_fill_end) |
| break; |
| } |
| |
| /* Create range entries which covers continuous memory range |
| (but with different tags) */ |
| memranges_fill_holes_up_to(&test_memrange, holes_fill_end, HOLE_TAG); |
| |
| memranges_each_entry(ptr, &test_memrange) |
| { |
| if (range_entry_tag(ptr) == HOLE_TAG) { |
| assert_int_equal(range_entry_base(ptr), |
| ALIGN_UP(res_mock[CACHEABLE_TAG].base |
| + res_mock[CACHEABLE_TAG].size, |
| MEMRANGE_ALIGN)); |
| assert_int_equal(range_entry_end(ptr), holes_fill_end); |
| /* Store pointer to HOLE_TAG region for future use */ |
| hole_ptr = ptr; |
| } |
| counter++; |
| } |
| assert_int_equal(counter, 2 + holes_found); |
| |
| /* If test data does not have any holes in it then terminate this test */ |
| if (holes_found == 0) |
| return; |
| |
| assert_non_null(hole_ptr); |
| counter = 0; |
| |
| /* Create hole crossing the border of two range entries */ |
| const resource_t new_cacheable_end = ALIGN_DOWN( |
| res_mock[CACHEABLE_TAG].base + res_mock[CACHEABLE_TAG].size - 4 * KiB, |
| MEMRANGE_ALIGN); |
| const resource_t new_hole_begin = |
| ALIGN_UP(range_entry_base(hole_ptr) + 4 * KiB, MEMRANGE_ALIGN); |
| const resource_t ranges_diff = new_hole_begin - new_cacheable_end; |
| |
| memranges_create_hole(&test_memrange, new_cacheable_end, ranges_diff); |
| |
| memranges_each_entry(ptr, &test_memrange) |
| { |
| switch (range_entry_tag(ptr)) { |
| case CACHEABLE_TAG: |
| assert_int_equal(range_entry_base(ptr), res_mock[CACHEABLE_TAG].base); |
| assert_int_equal(range_entry_end(ptr), new_cacheable_end); |
| break; |
| case RESERVED_TAG: |
| assert_int_equal(range_entry_base(ptr), res_mock[RESERVED_TAG].base); |
| assert_int_equal(range_entry_end(ptr), |
| res_mock[RESERVED_TAG].base |
| + res_mock[RESERVED_TAG].size); |
| break; |
| case HOLE_TAG: |
| assert_int_equal(range_entry_base(ptr), new_hole_begin); |
| assert_int_equal(range_entry_end(ptr), res_mock[RESERVED_TAG].base); |
| break; |
| default: |
| break; |
| } |
| counter++; |
| } |
| assert_int_equal(counter, 3); |
| |
| memranges_teardown(&test_memrange); |
| } |
| |
| /* |
| * This test verifies memranges_steal() function. Simple check is done by attempt |
| * to steal some memory from the top of region with CACHEABLE_TAG and some from |
| * the bottom of region with READONLY_TAG. |
| * |
| * Example memory ranges (res_mock1) for test_memrange_steal. |
| * Space marked with (/) is stolen during the test. |
| * |
| * +--------CACHEABLE_TAG--------+ <-0xE000 |
| * | | |
| * | | |
| * |/////////////////////////////| <-stolen_base |
| * +-----------------------------+ <-0x100000 <-stolen_base + 0x4000 |
| * |
| * |
| * |
| * +--------READONLY_TAG---------+ <-0xFF0000 <-stolen_base |
| * |/////////////////////////////| <-stolen_base + 0x4000 |
| * | | |
| * | | |
| * +-----------------------------+ <-0x1000000 |
| * |
| * |
| * +--------RESERVED_TAG---------+ <-0x100000000 |
| * | | |
| * +-----------------------------+ <-0x100001000 |
| */ |
| static void test_memrange_steal(void **state) |
| { |
| bool status = false; |
| resource_t stolen; |
| const unsigned long cacheable = IORESOURCE_CACHEABLE; |
| const unsigned long reserved = IORESOURCE_RESERVE; |
| const unsigned long readonly = IORESOURCE_READONLY; |
| const resource_t stolen_range_size = 0x4000; |
| struct memranges test_memrange; |
| struct resource *res_mock = *state; |
| struct range_entry *ptr; |
| size_t count = 0; |
| |
| memranges_init(&test_memrange, cacheable, cacheable, CACHEABLE_TAG); |
| memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG); |
| memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG); |
| |
| status = memranges_steal(&test_memrange, |
| res_mock[RESERVED_TAG].base + res_mock[RESERVED_TAG].size, |
| stolen_range_size, 12, CACHEABLE_TAG, &stolen, true); |
| assert_true(status); |
| assert_in_range(stolen, res_mock[CACHEABLE_TAG].base, |
| res_mock[CACHEABLE_TAG].base + res_mock[CACHEABLE_TAG].size); |
| status = memranges_steal(&test_memrange, |
| res_mock[RESERVED_TAG].base + res_mock[RESERVED_TAG].size, |
| stolen_range_size, 12, READONLY_TAG, &stolen, false); |
| assert_true(status); |
| assert_in_range(stolen, res_mock[READONLY_TAG].base, |
| res_mock[READONLY_TAG].base + res_mock[READONLY_TAG].size); |
| |
| memranges_each_entry(ptr, &test_memrange) |
| { |
| if (range_entry_tag(ptr) == CACHEABLE_TAG) { |
| assert_int_equal(range_entry_end(ptr), |
| ALIGN_DOWN(ALIGN_UP(res_mock[CACHEABLE_TAG].base |
| + res_mock[CACHEABLE_TAG].size, |
| MEMRANGE_ALIGN) |
| - stolen_range_size, |
| MEMRANGE_ALIGN)); |
| } |
| if (range_entry_tag(ptr) == READONLY_TAG) { |
| assert_int_equal(range_entry_base(ptr), |
| ALIGN_DOWN(res_mock[READONLY_TAG].base, MEMRANGE_ALIGN) |
| + stolen_range_size); |
| } |
| count++; |
| } |
| assert_int_equal(count, 3); |
| count = 0; |
| |
| /* Check if inserting ranges in previously stolen areas will merge them. */ |
| memranges_insert(&test_memrange, |
| res_mock[CACHEABLE_TAG].base + res_mock[CACHEABLE_TAG].size |
| - stolen_range_size - 0x12, |
| stolen_range_size, CACHEABLE_TAG); |
| memranges_insert(&test_memrange, res_mock[READONLY_TAG].base + 0xCC, stolen_range_size, |
| READONLY_TAG); |
| memranges_each_entry(ptr, &test_memrange) |
| { |
| const unsigned long tag = range_entry_tag(ptr); |
| assert_true(tag == CACHEABLE_TAG || tag == READONLY_TAG || tag == RESERVED_TAG); |
| assert_int_equal( |
| range_entry_base(ptr), |
| ALIGN_DOWN(res_mock[tag].base, MEMRANGE_ALIGN)); |
| assert_int_equal( |
| range_entry_end(ptr), |
| ALIGN_UP(res_mock[tag].base + res_mock[tag].size, MEMRANGE_ALIGN)); |
| count++; |
| } |
| assert_int_equal(count, 3); |
| count = 0; |
| |
| memranges_teardown(&test_memrange); |
| } |
| |
| /* Utility function checking number of entries and alignment of their base and end pointers */ |
| static void check_range_entries_count_and_alignment(struct memranges *ranges, |
| size_t ranges_count, resource_t alignment) |
| { |
| size_t count = 0; |
| struct range_entry *ptr; |
| |
| memranges_each_entry(ptr, ranges) |
| { |
| assert_true(IS_ALIGNED(range_entry_base(ptr), alignment)); |
| assert_true(IS_ALIGNED(range_entry_end(ptr), alignment)); |
| |
| count++; |
| } |
| assert_int_equal(ranges_count, count); |
| } |
| |
| /* This test verifies memranges_init*() and memranges_teardown() functions. |
| Added ranges are checked correct count and alignment. */ |
| static void test_memrange_init_and_teardown(void **state) |
| { |
| const unsigned long cacheable = IORESOURCE_CACHEABLE; |
| const unsigned long reserved = IORESOURCE_RESERVE; |
| const unsigned long readonly = IORESOURCE_READONLY; |
| struct memranges test_memrange; |
| struct range_entry range_entries[4] = {0}; |
| |
| /* Test memranges_init() correctness */ |
| memranges_init(&test_memrange, cacheable, cacheable, CACHEABLE_TAG); |
| memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG); |
| memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG); |
| |
| /* Expect all entries to be aligned to 4KiB (2^12) */ |
| check_range_entries_count_and_alignment(&test_memrange, 3, MEMRANGE_ALIGN); |
| |
| /* Expect ranges list to be empty after teardown */ |
| memranges_teardown(&test_memrange); |
| assert_true(memranges_is_empty(&test_memrange)); |
| |
| |
| /* Test memranges_init_with_alignment() correctness with alignment of 1KiB (2^10) */ |
| memranges_init_with_alignment(&test_memrange, cacheable, cacheable, CACHEABLE_TAG, 10); |
| memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG); |
| memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG); |
| |
| check_range_entries_count_and_alignment(&test_memrange, 3, POWER_OF_2(10)); |
| |
| memranges_teardown(&test_memrange); |
| assert_true(memranges_is_empty(&test_memrange)); |
| |
| |
| /* Test memranges_init_empty() correctness */ |
| memranges_init_empty(&test_memrange, &range_entries[0], ARRAY_SIZE(range_entries)); |
| assert_true(memranges_is_empty(&test_memrange)); |
| |
| memranges_add_resources(&test_memrange, cacheable, cacheable, CACHEABLE_TAG); |
| memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG); |
| memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG); |
| |
| check_range_entries_count_and_alignment(&test_memrange, 3, MEMRANGE_ALIGN); |
| |
| memranges_teardown(&test_memrange); |
| assert_true(memranges_is_empty(&test_memrange)); |
| |
| |
| /* Test memranges_init_with_alignment() correctness with alignment of 8KiB (2^13) */ |
| memranges_init_empty_with_alignment(&test_memrange, &range_entries[0], |
| ARRAY_SIZE(range_entries), 13); |
| assert_true(memranges_is_empty(&test_memrange)); |
| |
| memranges_add_resources(&test_memrange, cacheable, cacheable, CACHEABLE_TAG); |
| memranges_add_resources(&test_memrange, reserved, reserved, RESERVED_TAG); |
| memranges_add_resources(&test_memrange, readonly, readonly, READONLY_TAG); |
| |
| check_range_entries_count_and_alignment(&test_memrange, 3, POWER_OF_2(13)); |
| |
| memranges_teardown(&test_memrange); |
| assert_true(memranges_is_empty(&test_memrange)); |
| } |
| |
| /* Filter function accepting ranges having memory resource flag */ |
| static int memrange_filter_mem_only(struct device *dev, struct resource *res) |
| { |
| /* Accept only memory resources */ |
| return res->flags & IORESOURCE_MEM; |
| } |
| |
| /* Filter function rejecting ranges having memory resource flag */ |
| static int memrange_filter_non_mem(struct device *dev, struct resource *res) |
| { |
| /* Accept only memory resources */ |
| return !(res->flags & IORESOURCE_MEM); |
| } |
| |
| /* This test verifies memranges_add_resources_filter() function by providing filter functions |
| which accept or reject ranges. */ |
| static void test_memrange_add_resources_filter(void **state) |
| { |
| const unsigned long cacheable = IORESOURCE_CACHEABLE; |
| const unsigned long reserved = IORESOURCE_RESERVE; |
| struct memranges test_memrange; |
| struct range_entry *ptr; |
| size_t count = 0; |
| size_t accepted_tags[] = {CACHEABLE_TAG, RESERVED_TAG}; |
| |
| /* Check if filter accepts range correctly */ |
| memranges_init(&test_memrange, reserved, reserved, RESERVED_TAG); |
| memranges_add_resources_filter(&test_memrange, cacheable, cacheable, CACHEABLE_TAG, |
| memrange_filter_mem_only); |
| |
| /* Check if filter accepted desired range. */ |
| memranges_each_entry(ptr, &test_memrange) |
| { |
| assert_in_set(range_entry_tag(ptr), accepted_tags, ARRAY_SIZE(accepted_tags)); |
| assert_true(IS_ALIGNED(range_entry_base(ptr), MEMRANGE_ALIGN)); |
| assert_true(IS_ALIGNED(range_entry_end(ptr), MEMRANGE_ALIGN)); |
| count++; |
| } |
| assert_int_equal(2, count); |
| count = 0; |
| memranges_teardown(&test_memrange); |
| |
| /* Check if filter rejects range correctly */ |
| memranges_init(&test_memrange, reserved, reserved, RESERVED_TAG); |
| memranges_add_resources_filter(&test_memrange, cacheable, cacheable, CACHEABLE_TAG, |
| memrange_filter_non_mem); |
| |
| check_range_entries_count_and_alignment(&test_memrange, 1, MEMRANGE_ALIGN); |
| |
| memranges_teardown(&test_memrange); |
| } |
| |
| int main(void) |
| { |
| const struct CMUnitTest tests[] = { |
| cmocka_unit_test(test_memrange_basic), |
| cmocka_unit_test(test_memrange_clone_insert), |
| cmocka_unit_test(test_memrange_holes), |
| cmocka_unit_test(test_memrange_steal), |
| cmocka_unit_test(test_memrange_init_and_teardown), |
| cmocka_unit_test(test_memrange_add_resources_filter), |
| }; |
| |
| return cmocka_run_group_tests_name(__TEST_NAME__ "(Boundary on 4GiB)", tests, |
| setup_test_1, NULL) |
| + cmocka_run_group_tests_name(__TEST_NAME__ "(Boundaries 1 byte from 4GiB)", |
| tests, setup_test_2, NULL) |
| + cmocka_run_group_tests_name(__TEST_NAME__ "(Range over 4GiB boundary)", tests, |
| setup_test_3, NULL); |
| } |