From b812b52a9cbc4730c0719be288f0826583f69461 Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Thu, 22 Dec 2022 20:01:54 +0400 Subject: [PATCH] Rewrite memory map (#156) --- .github/workflows/main.yml | 1 + CONTRIBUTING.md | 5 + ChangeLog | 4 + Makefile.am | 1 + README.md | 2 +- examples/memmap.c | 77 +++-- fixtures/multiboot2_info_example1.txt | 12 +- fixtures/multiboot2_info_example2.txt | 12 +- include/kernaux/memmap.h | 77 +++-- include/kernaux/multiboot2.h | 13 +- include/kernaux/multiboot2/info_enums.h | 8 + src/memmap.c | 346 +++++++++++++------- src/multiboot2/info_convert.c | 71 ++++ src/multiboot2/info_enums.c | 19 ++ src/multiboot2/info_print.c | 7 +- tests/.gitignore | 2 +- tests/Makefile.am | 28 +- tests/test_memmap.c | 242 -------------- tests/test_multiboot2_info_convert_memmap.c | 128 ++++++++ 19 files changed, 597 insertions(+), 458 deletions(-) create mode 100644 src/multiboot2/info_convert.c delete mode 100644 tests/test_memmap.c create mode 100644 tests/test_multiboot2_info_convert_memmap.c diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7c301771..7a21c54e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -94,6 +94,7 @@ jobs: - without: 'ntoa' dependencies: '--without-printf --without-units' - without: 'printf' + - without: 'memmap' steps: - uses: actions/checkout@v2 - name: autogen diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9a26086e..c75e7d2a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,6 +101,11 @@ Use **cppcheck**. * Name public (*defined in the headers*) methods with the prefix `KernAux_` and with the prefix of the type name (example: `KernAux_FooBar_car_cdr`). +* Use postfix `size` for a byte size. +* Use postfix `slen` for C string length without terminating `\0` character + (size - 1). +* Use postfix `count` for a number of elements in an array. + * Create `typedef`s with the names of related `struct`s. Use this name with a prefix `struct` to declare the data itself, withoth the prefix to declare a pointer or an array: diff --git a/ChangeLog b/ChangeLog index debee2ba..c8d4aa51 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2022-12-22 Alex Kotov + + * include/kernaux/memmap.h: Complete rewrite + 2022-12-21 Alex Kotov * configure.ac: Rename feature "--(enable|disable)-debug" to diff --git a/Makefile.am b/Makefile.am index 6cea67f1..e84b612f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -97,6 +97,7 @@ libkernaux_la_SOURCES += \ src/multiboot2/header_helpers.c \ src/multiboot2/header_is_valid.c \ src/multiboot2/header_print.c \ + src/multiboot2/info_convert.c \ src/multiboot2/info_enums.c \ src/multiboot2/info_helpers.c \ src/multiboot2/info_is_valid.c \ diff --git a/README.md b/README.md index 2eceab87..7bdc4b3b 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ zero). Work-in-progress APIs can change at any time. * Utilities * [Measurement units utils](/include/kernaux/units.h) (*work in progress*) * [Example: To human](/examples/units_human.c) - * [Memory map](/include/kernaux/memmap.h) (*non-breaking since* **0.4.0**) + * [Memory map](/include/kernaux/memmap.h) (*non-breaking since* **?.?.?**) * [Example](/examples/memmap.c) * [printf format parser](/include/kernaux/printf_fmt.h) (*non-breaking since* **0.6.0**) * [Example](/examples/printf_fmt.c) diff --git a/examples/memmap.c b/examples/memmap.c index 307fb487..66b7c75c 100644 --- a/examples/memmap.c +++ b/examples/memmap.c @@ -1,45 +1,60 @@ +#define KERNAUX_ACCESS_PROTECTED + +#include +#include +#include #include #include -#include #include -#include +#include -#define SIZE_256MiB ( 256 * 1024 * 1024) -#define SIZE_512MiB ( 512 * 1024 * 1024) -#define SIZE_1GiB (1024 * 1024 * 1024) +static char malloc_memory[8192]; + +static void my_putc(void *const display KERNAUX_UNUSED, const char c) +{ + putchar(c); +} + +static void my_vprintf( + void *const display KERNAUX_UNUSED, + const char *const format, + va_list va +) { + vprintf(format, va); +} + +static const struct KernAux_Display display = { + .putc = my_putc, + .vprintf = my_vprintf, +}; void example_main() { - KernAux_MemMap memmap = { KernAux_MemMap_create(SIZE_1GiB) }; + struct KernAux_FreeList malloc = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&malloc, malloc_memory, sizeof(malloc_memory)); - assert(KernAux_MemMap_add_entry(memmap, true, NULL, 0, SIZE_256MiB)); - assert(KernAux_MemMap_add_entry(memmap, false, "foo", SIZE_256MiB, SIZE_256MiB)); - assert(KernAux_MemMap_add_entry(memmap, true, "bar", SIZE_512MiB, SIZE_512MiB)); + KernAux_Memmap_Builder memmap_builder = + KernAux_Memmap_Builder_new(&malloc.malloc); + assert(memmap_builder); - assert(KernAux_MemMap_finish(memmap)); + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0x0, 654336, "available")); + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0x9fc00, 1024, "reserved")); + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0xf0000, 65536, "reserved")); + KernAux_Memmap_Node kernel_node = + KernAux_Memmap_Builder_add (memmap_builder, NULL, 0x100000, 133038080, "available"); + assert(kernel_node); + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0x7fe0000, 131072, "reserved")); + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0xfffc0000, 262144, "reserved")); - // You can get the entry by it's index: - assert( KernAux_MemMap_entry_by_index(memmap, 0)->is_available == true); - assert(strcmp(KernAux_MemMap_entry_by_index(memmap, 0)->tag, "") == 0); - assert( KernAux_MemMap_entry_by_index(memmap, 0)->start == 0); - assert( KernAux_MemMap_entry_by_index(memmap, 0)->size == SIZE_256MiB); - assert( KernAux_MemMap_entry_by_index(memmap, 0)->end == SIZE_256MiB - 1); - assert( KernAux_MemMap_entry_by_index(memmap, 0)->limit == SIZE_256MiB); + assert(KernAux_Memmap_Builder_add(memmap_builder, kernel_node, 0x400000, 8192, "kernel code")); + assert(KernAux_Memmap_Builder_add(memmap_builder, kernel_node, 0x402000, 4096, "kernel data")); - // You can get the entry by it's start address: - assert( KernAux_MemMap_entry_by_start(memmap, SIZE_256MiB)->is_available == false); - assert(strcmp(KernAux_MemMap_entry_by_start(memmap, SIZE_256MiB)->tag, "foo") == 0); - assert( KernAux_MemMap_entry_by_start(memmap, SIZE_256MiB)->start == SIZE_256MiB); - assert( KernAux_MemMap_entry_by_start(memmap, SIZE_256MiB)->size == SIZE_256MiB); - assert( KernAux_MemMap_entry_by_start(memmap, SIZE_256MiB)->end == SIZE_512MiB - 1); - assert( KernAux_MemMap_entry_by_start(memmap, SIZE_256MiB)->limit == SIZE_512MiB); + KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(memmap_builder); + assert(memmap); - // You can get the entry by any address inside it: - assert( KernAux_MemMap_entry_by_addr(memmap, SIZE_512MiB )->is_available == true); - assert(strcmp(KernAux_MemMap_entry_by_addr(memmap, SIZE_512MiB + 1 )->tag, "bar") == 0); - assert( KernAux_MemMap_entry_by_addr(memmap, SIZE_512MiB + SIZE_256MiB)->start == SIZE_512MiB); - assert( KernAux_MemMap_entry_by_addr(memmap, SIZE_1GiB - 3 )->size == SIZE_512MiB); - assert( KernAux_MemMap_entry_by_addr(memmap, SIZE_1GiB - 2 )->end == SIZE_1GiB - 1); - assert( KernAux_MemMap_entry_by_addr(memmap, SIZE_1GiB - 1 )->limit == SIZE_1GiB); + KernAux_Memmap_print(memmap, &display); + + KERNAUX_MEMMAP_FREE(memmap); } diff --git a/fixtures/multiboot2_info_example1.txt b/fixtures/multiboot2_info_example1.txt index 3d73f613..fcf7b20b 100644 --- a/fixtures/multiboot2_info_example1.txt +++ b/fixtures/multiboot2_info_example1.txt @@ -53,37 +53,37 @@ Multiboot 2 info tag { [0]: { u64 base_addr: 0x0 u64 length: 654336 - u32 type: 1 + u32 type: 1 (available) u32 reserved: 0x0 } [1]: { u64 base_addr: 0x9fc00 u64 length: 1024 - u32 type: 2 + u32 type: 2 (reserved) u32 reserved: 0x0 } [2]: { u64 base_addr: 0xf0000 u64 length: 65536 - u32 type: 2 + u32 type: 2 (reserved) u32 reserved: 0x0 } [3]: { u64 base_addr: 0x100000 u64 length: 133038080 - u32 type: 1 + u32 type: 1 (available) u32 reserved: 0x0 } [4]: { u64 base_addr: 0x7fe0000 u64 length: 131072 - u32 type: 2 + u32 type: 2 (reserved) u32 reserved: 0x0 } [5]: { u64 base_addr: 0xfffc0000 u64 length: 262144 - u32 type: 2 + u32 type: 2 (reserved) u32 reserved: 0x0 } ] diff --git a/fixtures/multiboot2_info_example2.txt b/fixtures/multiboot2_info_example2.txt index d32769c5..c4eca9e5 100644 --- a/fixtures/multiboot2_info_example2.txt +++ b/fixtures/multiboot2_info_example2.txt @@ -48,37 +48,37 @@ Multiboot 2 info tag { [0]: { u64 base_addr: 0x0 u64 length: 654336 - u32 type: 1 + u32 type: 1 (available) u32 reserved: 0x0 } [1]: { u64 base_addr: 0x9fc00 u64 length: 1024 - u32 type: 2 + u32 type: 2 (reserved) u32 reserved: 0x0 } [2]: { u64 base_addr: 0xf0000 u64 length: 65536 - u32 type: 2 + u32 type: 2 (reserved) u32 reserved: 0x0 } [3]: { u64 base_addr: 0x100000 u64 length: 133038080 - u32 type: 1 + u32 type: 1 (available) u32 reserved: 0x0 } [4]: { u64 base_addr: 0x7fe0000 u64 length: 131072 - u32 type: 2 + u32 type: 2 (reserved) u32 reserved: 0x0 } [5]: { u64 base_addr: 0xfffc0000 u64 length: 262144 - u32 type: 2 + u32 type: 2 (reserved) u32 reserved: 0x0 } ] diff --git a/include/kernaux/memmap.h b/include/kernaux/memmap.h index 27ca5bf0..91a3c26f 100644 --- a/include/kernaux/memmap.h +++ b/include/kernaux/memmap.h @@ -5,55 +5,60 @@ extern "C" { #endif +#include +#include #include #include #include +#include -#define KERNAUX_MEMMAP_ENTRIES_MAX 100 +#define KERNAUX_MEMMAP_FREE(memmap) do { \ + KernAux_Memmap_free(memmap); \ + memmap = NULL; \ +} while (0) -#define KERNAUX_MEMMAP_ENTRY_TAG_SLEN_MAX 24 -#define KERNAUX_MEMMAP_ENTRY_TAG_SIZE_MAX (KERNAUX_MEMMAP_ENTRY_TAG_SLEN_MAX + 1) +/********* + * Types * + *********/ -typedef const struct KernAux_MemMap_Entry { - bool is_available; - char tag[KERNAUX_MEMMAP_ENTRY_TAG_SIZE_MAX]; - size_t start, size, end, limit; -} *KernAux_MemMap_Entry; +typedef const struct KernAux_Memmap_Node { + uint64_t mem_start, mem_end, mem_size; + const char *tag; + const struct KernAux_Memmap_Node *next, *children; +} *KernAux_Memmap_Node; -typedef struct KernAux_MemMap { - bool KERNAUX_PRIVATE_FIELD(is_finished); - size_t KERNAUX_PRIVATE_FIELD(memory_size); - size_t KERNAUX_PRIVATE_FIELD(entries_count); - struct KernAux_MemMap_Entry KERNAUX_PRIVATE_FIELD(entries)[KERNAUX_MEMMAP_ENTRIES_MAX]; -} KernAux_MemMap[1]; +typedef const struct KernAux_Memmap { + KernAux_Memmap_Node root_node; -struct KernAux_MemMap KernAux_MemMap_create(size_t memory_size); -void KernAux_MemMap_init(KernAux_MemMap memmap, size_t memory_size); + KernAux_Malloc KERNAUX_PRIVATE_FIELD(malloc); +} *KernAux_Memmap; -/// @warning Must only be called with NOT finished memmap, otherwise panics. -bool KernAux_MemMap_add_entry( - KernAux_MemMap memmap, - bool is_available, - const char *tag, - size_t start, - size_t size +typedef struct KernAux_Memmap_Builder { + KernAux_Memmap KERNAUX_PRIVATE_FIELD(memmap); +} *KernAux_Memmap_Builder; + +/************* + * Functions * + *************/ + +KernAux_Memmap_Builder +KernAux_Memmap_Builder_new(KernAux_Malloc malloc); + +KernAux_Memmap_Node +KernAux_Memmap_Builder_add( + KernAux_Memmap_Builder builder, + KernAux_Memmap_Node parent_node, + uint64_t mem_start, + uint64_t mem_size, + const char *tag ); -/// @warning Must only be called with NOT finished memmap, otherwise panics. -bool KernAux_MemMap_finish(KernAux_MemMap memmap); +KernAux_Memmap +KernAux_Memmap_Builder_finish_and_free(KernAux_Memmap_Builder builder); -/// @warning Must only be called with finished memmap, otherwise panics. -KernAux_MemMap_Entry -KernAux_MemMap_entry_by_index(KernAux_MemMap memmap, size_t index); - -/// @warning Must only be called with finished memmap, otherwise panics. -KernAux_MemMap_Entry -KernAux_MemMap_entry_by_start(KernAux_MemMap memmap, size_t start); - -/// @warning Must only be called with finished memmap, otherwise panics. -KernAux_MemMap_Entry -KernAux_MemMap_entry_by_addr(KernAux_MemMap memmap, size_t addr); +void KernAux_Memmap_free(KernAux_Memmap memmap); +void KernAux_Memmap_print(KernAux_Memmap memmap, KernAux_Display display); #ifdef __cplusplus } diff --git a/include/kernaux/multiboot2.h b/include/kernaux/multiboot2.h index fbd67a58..87999307 100644 --- a/include/kernaux/multiboot2.h +++ b/include/kernaux/multiboot2.h @@ -5,8 +5,10 @@ extern "C" { #endif -#include #include +#include +#include +#include #include #include @@ -559,6 +561,15 @@ KERNAUX_STATIC_TEST_STRUCT_SIZE( #include #include +/******************* + * Other functions * + *******************/ + +KernAux_Memmap_Builder KernAux_Multiboot2_Info_to_memmap_builder( + const struct KernAux_Multiboot2_Info *multiboot2_info, + KernAux_Malloc malloc +); + #ifdef __cplusplus } #endif diff --git a/include/kernaux/multiboot2/info_enums.h b/include/kernaux/multiboot2/info_enums.h index b2f93de2..e4ac5b36 100644 --- a/include/kernaux/multiboot2/info_enums.h +++ b/include/kernaux/multiboot2/info_enums.h @@ -32,7 +32,15 @@ extern "C" { #define KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR 20 #define KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR 21 +#define KERNAUX_MULTIBOOT2_MEMMAP_AVAILABLE 1 +#define KERNAUX_MULTIBOOT2_MEMMAP_RESERVED 2 +#define KERNAUX_MULTIBOOT2_MEMMAP_ACPI_RECLAIMABLE 3 +#define KERNAUX_MULTIBOOT2_MEMMAP_NVS 4 +#define KERNAUX_MULTIBOOT2_MEMMAP_BADRAM 5 + const char *KernAux_Multiboot2_ITag_to_str(uint32_t tag_type); +const char* +KernAux_Multiboot2_ITag_MemoryMap_EntryBase_Type_to_str(uint32_t type); #ifdef __cplusplus } diff --git a/src/memmap.c b/src/memmap.c index cb76d25c..46f9fba1 100644 --- a/src/memmap.c +++ b/src/memmap.c @@ -4,6 +4,9 @@ #include "assert.h" +#include +#include +#include #include #include @@ -11,144 +14,245 @@ #include #include -#define MEMMAP (*memmap) +static void free_node(KernAux_Malloc malloc, struct KernAux_Memmap_Node *node); +static void print_nodes( + KernAux_Memmap_Node node, + KernAux_Display display, + unsigned indentation +); -struct KernAux_MemMap KernAux_MemMap_create(const size_t memory_size) +KernAux_Memmap_Builder +KernAux_Memmap_Builder_new(const KernAux_Malloc malloc) { - struct KernAux_MemMap memmap; - KernAux_MemMap_init(&memmap, memory_size); + KERNAUX_NOTNULL(malloc); + + struct KernAux_Memmap_Builder *const builder = + KernAux_Malloc_malloc(malloc, sizeof(*builder)); + if (!builder) { + return NULL; + } + + struct KernAux_Memmap *const memmap = + KernAux_Malloc_malloc(malloc, sizeof(*memmap)); + if (!memmap) { + KernAux_Malloc_free(malloc, builder); + return NULL; + } + + struct KernAux_Memmap_Node *const root_node = + KernAux_Malloc_malloc(malloc, sizeof(*root_node)); + if (!root_node) { + KernAux_Malloc_free(malloc, memmap); + KernAux_Malloc_free(malloc, builder); + return NULL; + } + + *root_node = (struct KernAux_Memmap_Node){ + .mem_start = 0x0, + .mem_end = 0xffffffffffffffff, // 2**64 - 1 + .mem_size = 0xffffffffffffffff, // 2**64 - 1 + .tag = NULL, + .next = NULL, + .children = NULL, + }; + *memmap = (struct KernAux_Memmap){ + .malloc = malloc, + .root_node = root_node, + }; + *builder = (struct KernAux_Memmap_Builder){ + .memmap = memmap, + }; + + return builder; +} + +KernAux_Memmap_Node KernAux_Memmap_Builder_add( + const KernAux_Memmap_Builder builder, + KernAux_Memmap_Node parent_node, + const uint64_t mem_start, + const uint64_t mem_size, + const char *tag +) { + KERNAUX_NOTNULL(builder); + KERNAUX_ASSERT(builder->memmap); + KERNAUX_ASSERT(builder->memmap->root_node); + KERNAUX_ASSERT(builder->memmap->malloc); + + if (mem_size == 0) goto fail; + + char *tag_copy = NULL; + if (tag) { + tag_copy = + KernAux_Malloc_malloc(builder->memmap->malloc, strlen(tag) + 1); + if (!tag_copy) goto fail; + strcpy(tag_copy, tag); + } + + struct KernAux_Memmap_Node *const new_node = + KernAux_Malloc_malloc(builder->memmap->malloc, sizeof(*new_node)); + if (!new_node) goto fail_after_tag; + + new_node->mem_start = mem_start; + new_node->mem_size = mem_size; + new_node->mem_end = mem_start + mem_size - 1; + new_node->tag = tag_copy; + + if (!parent_node) { + parent_node = (struct KernAux_Memmap_Node*)builder->memmap->root_node; + } + + if (new_node->mem_start < parent_node->mem_start || + new_node->mem_end > parent_node->mem_end) + { + goto fail_after_new_node; + } + + if (parent_node->children) { + for ( + struct KernAux_Memmap_Node *curr_node = + (struct KernAux_Memmap_Node*)parent_node->children; + curr_node; + curr_node = (struct KernAux_Memmap_Node*)curr_node->next + ) { + if (!curr_node->next || + curr_node->next->mem_start > new_node->mem_start) + { + if (new_node->next && + new_node->mem_end >= new_node->next->mem_start) + { + goto fail_after_new_node; + } + new_node->next = curr_node->next; + curr_node->next = new_node; + break; + } + } + } else { + new_node->next = NULL; + ((struct KernAux_Memmap_Node*)parent_node)->children = new_node; + } + + return new_node; + +fail_after_new_node: + KernAux_Malloc_free(builder->memmap->malloc, new_node); +fail_after_tag: + if (tag_copy) KernAux_Malloc_free(builder->memmap->malloc, tag_copy); +fail: + return NULL; +} + +KernAux_Memmap +KernAux_Memmap_Builder_finish_and_free(const KernAux_Memmap_Builder builder) +{ + KERNAUX_NOTNULL(builder); + KERNAUX_ASSERT(builder->memmap); + KERNAUX_ASSERT(builder->memmap->root_node); + KERNAUX_ASSERT(builder->memmap->malloc); + + KernAux_Memmap memmap = builder->memmap; + builder->memmap = NULL; + KernAux_Malloc_free(memmap->malloc, builder); return memmap; } -void KernAux_MemMap_init(KernAux_MemMap memmap, const size_t memory_size) +void KernAux_Memmap_free(const KernAux_Memmap memmap) { - MEMMAP.is_finished = false; - MEMMAP.memory_size = memory_size; - MEMMAP.entries_count = 0; - memset(MEMMAP.entries, 0, sizeof(MEMMAP.entries)); + KERNAUX_NOTNULL(memmap); + KERNAUX_ASSERT(memmap->root_node); + KERNAUX_ASSERT(memmap->malloc); + KERNAUX_ASSERT(memmap->root_node->next == NULL); + + free_node(memmap->malloc, (struct KernAux_Memmap_Node*)memmap->root_node); + + KernAux_Malloc malloc = memmap->malloc; + + ((struct KernAux_Memmap*)memmap)->root_node = NULL; + ((struct KernAux_Memmap*)memmap)->malloc = NULL; + + KernAux_Malloc_free(malloc, (void*)memmap); } -bool KernAux_MemMap_add_entry( - KernAux_MemMap memmap, - const bool is_available, - const char *const tag, - const size_t start, - const size_t size +void KernAux_Memmap_print( + const KernAux_Memmap memmap, + const KernAux_Display display ) { - if (MEMMAP.is_finished) { - KERNAUX_PANIC("memmap is finished"); - return false; - } + KERNAUX_NOTNULL(memmap); + KERNAUX_ASSERT(memmap->root_node); + KERNAUX_ASSERT(memmap->malloc); + KERNAUX_ASSERT(memmap->root_node->next == NULL); - if (MEMMAP.entries_count >= KERNAUX_MEMMAP_ENTRIES_MAX) return false; - if (SIZE_MAX - start < size) return false; - if (size == 0) return false; - - const size_t index = MEMMAP.entries_count++; - - memset(&MEMMAP.entries[index], 0, sizeof(MEMMAP.entries[index])); - - MEMMAP.entries[index].is_available = is_available; - MEMMAP.entries[index].start = start; - MEMMAP.entries[index].size = size; - MEMMAP.entries[index].end = start + size - 1; - MEMMAP.entries[index].limit = start + size; - - if (tag) { - strncpy( - MEMMAP.entries[index].tag, - tag, - KERNAUX_MEMMAP_ENTRY_TAG_SLEN_MAX - ); - } - - return true; + print_nodes(memmap->root_node, display, 0); } -bool KernAux_MemMap_finish(KernAux_MemMap memmap) -{ - if (MEMMAP.is_finished) { - KERNAUX_PANIC("memmap is finished"); - return false; +void free_node( + const KernAux_Malloc malloc, + struct KernAux_Memmap_Node *const node +) { + KERNAUX_NOTNULL(malloc); + KERNAUX_NOTNULL(node); + + for ( + struct KernAux_Memmap_Node *child_node = + (struct KernAux_Memmap_Node*)node->children; + child_node; + child_node = (struct KernAux_Memmap_Node*)child_node->next + ) { + free_node(malloc, child_node); } - if ((MEMMAP.entries_count == 0 && MEMMAP.memory_size != 0) || - MEMMAP.entries_count > KERNAUX_MEMMAP_ENTRIES_MAX || - MEMMAP.entries[0].start != 0 || - MEMMAP.entries[MEMMAP.entries_count - 1].limit != MEMMAP.memory_size) - { - return false; - } + if (node->tag) KernAux_Malloc_free(malloc, (void*)node->tag); + KernAux_Malloc_free(malloc, node); +} - // At first, let's validate the individual entries. - for (size_t index = 0; index < MEMMAP.entries_count; ++index) { - if (SIZE_MAX - MEMMAP.entries[index].start < - MEMMAP.entries[index].size - || - MEMMAP.entries[index].end != - MEMMAP.entries[index].start + MEMMAP.entries[index].size - 1 - || - MEMMAP.entries[index].limit != - MEMMAP.entries[index].start + MEMMAP.entries[index].size) - { - return false; +#define PRINT(s) do { KernAux_Display_print (display, s); } while (0) +#define PRINTLN(s) do { KernAux_Display_println(display, s); } while (0) + +#define PRINTLNF(format, ...) \ + do { KernAux_Display_printlnf(display, format, __VA_ARGS__); } while (0) + +#define INDENT do { \ + for (unsigned index = 0; index < indentation; ++index) PRINT(" "); \ +} while (0) + +void print_nodes( + KernAux_Memmap_Node node, + const KernAux_Display display, + const unsigned indentation +) { + for (; node; node = node->next) { + INDENT; + PRINTLN("{"); + + KERNAUX_CAST_CONST(unsigned long long, mem_start, node->mem_start); + KERNAUX_CAST_CONST(unsigned long long, mem_size, node->mem_size); + KERNAUX_CAST_CONST(unsigned long long, mem_end, node->mem_end); + + INDENT; + PRINTLNF(" u64 mem_start: 0x%llx", mem_start); + INDENT; + PRINTLNF(" u64 mem_size: %llu", mem_size); + INDENT; + PRINTLNF(" u64 mem_end: 0x%llx", mem_end); + INDENT; + if (node->tag) { + PRINTLNF(" char* tag: \"%s\"", node->tag); + } else { + PRINTLN(" char* tag: NULL"); } - } - // TODO: Next, let's sort the entries. - - // Finally, let's validate that the entries fit each other properly. - for (size_t index = 1; index < MEMMAP.entries_count; ++index) { - if (MEMMAP.entries[index - 1].limit != MEMMAP.entries[index].start) { - return false; + if (node->children) { + INDENT; + PRINTLN(" struct* children: ["); + print_nodes(node->children, display, indentation + 2); + INDENT; + PRINTLN(" ]"); + } else { + INDENT; + PRINTLN(" struct* children: []"); } - } - return MEMMAP.is_finished = true; -} - -KernAux_MemMap_Entry -KernAux_MemMap_entry_by_index(KernAux_MemMap memmap, const size_t index) -{ - if (!MEMMAP.is_finished) { - KERNAUX_PANIC("memmap is not finished"); - return NULL; - } - - if (index >= MEMMAP.entries_count) return NULL; - return &MEMMAP.entries[index]; -} - -KernAux_MemMap_Entry -KernAux_MemMap_entry_by_start(KernAux_MemMap memmap, const size_t start) -{ - if (!MEMMAP.is_finished) { - KERNAUX_PANIC("memmap is not finished"); - return NULL; - } - - for (size_t index = 0; index < MEMMAP.entries_count; ++index) { - if (MEMMAP.entries[index].start == start) return &MEMMAP.entries[index]; - } - - return NULL; -} - -KernAux_MemMap_Entry -KernAux_MemMap_entry_by_addr(KernAux_MemMap memmap, const size_t addr) -{ - if (!MEMMAP.is_finished) { - KERNAUX_PANIC("memmap is not finished"); - return NULL; - } - - for (size_t index = 0; index < MEMMAP.entries_count; ++index) { - if (addr >= MEMMAP.entries[index].start && - addr <= MEMMAP.entries[index].end) - { - return &MEMMAP.entries[index]; - } - } - - return NULL; + INDENT; + PRINTLN("}"); + } } diff --git a/src/multiboot2/info_convert.c b/src/multiboot2/info_convert.c new file mode 100644 index 00000000..3e0b7076 --- /dev/null +++ b/src/multiboot2/info_convert.c @@ -0,0 +1,71 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include +#include +#include +#include + +#include + +#ifdef WITH_MEMMAP +KernAux_Memmap_Builder KernAux_Multiboot2_Info_to_memmap_builder( + const struct KernAux_Multiboot2_Info *const multiboot2_info, + const KernAux_Malloc malloc +) { + KERNAUX_NOTNULL(multiboot2_info); + KERNAUX_NOTNULL(malloc); + + if (!KernAux_Multiboot2_Info_is_valid(multiboot2_info)) return NULL; + + const struct KernAux_Multiboot2_ITag_MemoryMap *const memory_map_tag = + (const struct KernAux_Multiboot2_ITag_MemoryMap*) + KernAux_Multiboot2_Info_first_tag_with_type( + multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP + ); + if (!memory_map_tag) return NULL; + + const void *const data = KERNAUX_MULTIBOOT2_DATA(memory_map_tag); + size_t data_size = memory_map_tag->base.size - sizeof(*memory_map_tag); + const void *const data_end = ((const char*)data) + data_size; + + if (memory_map_tag->entry_size < + sizeof(struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase)) + { + return NULL; + } + + KernAux_Memmap_Builder builder = + KernAux_Memmap_Builder_new(malloc); + if (!builder) return NULL; + + for ( + const struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase *entry = data; + (const char*)entry < (const char*)data_end; + entry = + (const struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase*) + (((const char*)entry) + memory_map_tag->entry_size) + ) { + const void *const node = KernAux_Memmap_Builder_add( + builder, + NULL, + entry->base_addr, + entry->length, + KernAux_Multiboot2_ITag_MemoryMap_EntryBase_Type_to_str( + entry->type + ) + ); + + if (!node) { + KernAux_Memmap_Builder_finish_and_free(builder); + return NULL; + } + } + + return builder; +} +#endif diff --git a/src/multiboot2/info_enums.c b/src/multiboot2/info_enums.c index 4abbcf14..06c5058d 100644 --- a/src/multiboot2/info_enums.c +++ b/src/multiboot2/info_enums.c @@ -58,3 +58,22 @@ const char *KernAux_Multiboot2_ITag_to_str(const uint32_t tag_type) return NULL; } } + +const char* +KernAux_Multiboot2_ITag_MemoryMap_EntryBase_Type_to_str(const uint32_t type) +{ + switch (type) { + case KERNAUX_MULTIBOOT2_MEMMAP_AVAILABLE: + return "available"; + case KERNAUX_MULTIBOOT2_MEMMAP_RESERVED: + return "reserved"; + case KERNAUX_MULTIBOOT2_MEMMAP_ACPI_RECLAIMABLE: + return "ACPI reclaimable"; + case KERNAUX_MULTIBOOT2_MEMMAP_NVS: + return "ACPI Non-Volatile Storage"; + case KERNAUX_MULTIBOOT2_MEMMAP_BADRAM: + return "bad RAM"; + default: + return NULL; + } +} diff --git a/src/multiboot2/info_print.c b/src/multiboot2/info_print.c index ecdb10cd..d02898be 100644 --- a/src/multiboot2/info_print.c +++ b/src/multiboot2/info_print.c @@ -378,7 +378,12 @@ void KernAux_Multiboot2_ITag_MemoryMap_print( PRINTLNF(" [%zu]: {", index); PRINTLNF(" u64 base_addr: 0x%llx", base_addr); PRINTLNF(" u64 length: %llu", length); - PRINTLNF(" u32 type: %lu", type); + PRINTLNF(" u32 type: %lu (%s)", + type, + KernAux_Multiboot2_ITag_MemoryMap_EntryBase_Type_to_str( + entries[index].type + ) + ); PRINTLNF(" u32 reserved: 0x%lx", reserved); PRINTLN (" }"); } diff --git a/tests/.gitignore b/tests/.gitignore index e98bf740..957f241c 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -11,12 +11,12 @@ /test_elf /test_free_list /test_mbr -/test_memmap /test_multiboot2_common_packing /test_multiboot2_header_helpers /test_multiboot2_header_print /test_multiboot2_header_print.c /test_multiboot2_header_validation +/test_multiboot2_info_convert_memmap /test_multiboot2_info_helpers /test_multiboot2_info_print /test_multiboot2_info_print.c diff --git a/tests/Makefile.am b/tests/Makefile.am index b3a3ab20..3fc5cbc6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -168,18 +168,6 @@ test_mbr_SOURCES = \ test_mbr.c endif -############### -# test_memmap # -############### - -if WITH_MEMMAP -TESTS += test_memmap -test_memmap_LDADD = $(top_builddir)/libkernaux.la -test_memmap_SOURCES = \ - main.c \ - test_memmap.c -endif - ################################## # test_multiboot2_common_packing # ################################## @@ -241,6 +229,22 @@ test_multiboot2_header_validation_SOURCES = \ ../fixtures/multiboot2_header_example2.h endif +####################################### +# test_multiboot2_info_convert_memmap # +####################################### + +if WITH_MULTIBOOT2 +if WITH_MEMMAP +TESTS += test_multiboot2_info_convert_memmap +test_multiboot2_info_convert_memmap_LDADD = $(top_builddir)/libkernaux.la +test_multiboot2_info_convert_memmap_SOURCES = \ + main.c \ + test_multiboot2_info_convert_memmap.c \ + ../fixtures/multiboot2_header_example2.h \ + ../fixtures/multiboot2_info_example2.h +endif +endif + ################################ # test_multiboot2_info_helpers # ################################ diff --git a/tests/test_memmap.c b/tests/test_memmap.c deleted file mode 100644 index ee33769e..00000000 --- a/tests/test_memmap.c +++ /dev/null @@ -1,242 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define KERNAUX_ACCESS_PRIVATE - -#include -#include -#include - -#include -#include -#include -#include -#include - -static KernAux_MemMap memmap; - -static jmp_buf jmpbuf; -static unsigned int assert_count_exp = 0; -static unsigned int assert_count_ctr = 0; -static const char *assert_last_file = NULL; - -static void assert_cb( - const char *const file, - const int line KERNAUX_UNUSED, - const char *const msg KERNAUX_UNUSED -) { - ++assert_count_ctr; - assert_last_file = file; - - longjmp(jmpbuf, 1); -} - -static void before_assert() -{ - assert(assert_count_ctr == assert_count_exp); -} - -static void expect_assert() -{ -#ifdef ENABLE_ASSERT - // cppcheck-suppress assignmentInAssert - assert(assert_count_ctr == ++assert_count_exp); - assert(strstr(assert_last_file, "src/memmap.c") != NULL); - assert_last_file = NULL; -#else - assert(assert_count_ctr == 0); - assert(assert_last_file == NULL); -#endif -} - -#define MEMSET memset(memmap, 0xff, sizeof(memmap)) -#define MEMMAP (*memmap) - -void test_main() -{ - assert(setjmp(jmpbuf) == 0); - - kernaux_assert_cb = assert_cb; - - { - MEMSET; - KernAux_MemMap_init(memmap, 0); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 0); - assert(MEMMAP.entries_count == 0); - - assert(KernAux_MemMap_finish(memmap)); - - assert(MEMMAP.is_finished); - assert(MEMMAP.memory_size == 0); - assert(MEMMAP.entries_count == 0); - - assert(KernAux_MemMap_entry_by_index(memmap, 0) == NULL); - - before_assert(); - if (setjmp(jmpbuf) == 0) { - assert(!KernAux_MemMap_finish(memmap)); - } - expect_assert(); - } - - { - MEMSET; - KernAux_MemMap_init(memmap, 0); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 0); - assert(MEMMAP.entries_count == 0); - - assert(!KernAux_MemMap_add_entry(memmap, false, NULL, 0, 0)); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 0); - assert(MEMMAP.entries_count == 0); - - assert(KernAux_MemMap_finish(memmap)); - - assert(MEMMAP.is_finished); - assert(MEMMAP.memory_size == 0); - assert(MEMMAP.entries_count == 0); - - assert(KernAux_MemMap_entry_by_index(memmap, 0) == NULL); - } - - { - MEMSET; - KernAux_MemMap_init(memmap, 1); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 1); - assert(MEMMAP.entries_count == 0); - - assert(KernAux_MemMap_add_entry(memmap, false, NULL, 0, 1)); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 1); - assert(MEMMAP.entries_count == 1); - - assert(KernAux_MemMap_finish(memmap)); - - assert(MEMMAP.is_finished); - assert(MEMMAP.memory_size == 1); - assert(MEMMAP.entries_count == 1); - assert(MEMMAP.entries[0].is_available == false); - assert(MEMMAP.entries[0].tag[0] == '\0'); - assert(MEMMAP.entries[0].start == 0); - assert(MEMMAP.entries[0].size == 1); - assert(MEMMAP.entries[0].end == 0); - assert(MEMMAP.entries[0].limit == 1); - - assert(KernAux_MemMap_entry_by_index(memmap, 0) == &MEMMAP.entries[0]); - } - - { - MEMSET; - KernAux_MemMap_init(memmap, 2); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 2); - assert(MEMMAP.entries_count == 0); - - assert(KernAux_MemMap_add_entry(memmap, false, NULL, 0, 2)); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 2); - assert(MEMMAP.entries_count == 1); - - assert(KernAux_MemMap_finish(memmap)); - - assert(MEMMAP.is_finished); - assert(MEMMAP.memory_size == 2); - assert(MEMMAP.entries_count == 1); - assert(MEMMAP.entries[0].is_available == false); - assert(MEMMAP.entries[0].tag[0] == '\0'); - assert(MEMMAP.entries[0].start == 0); - assert(MEMMAP.entries[0].size == 2); - assert(MEMMAP.entries[0].end == 1); - assert(MEMMAP.entries[0].limit == 2); - - assert(KernAux_MemMap_entry_by_index(memmap, 0) == &MEMMAP.entries[0]); - } - - { - MEMSET; - KernAux_MemMap_init(memmap, 1); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 1); - assert(MEMMAP.entries_count == 0); - - assert(KernAux_MemMap_add_entry(memmap, false, NULL, 0, 2)); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 1); - assert(MEMMAP.entries_count == 1); - - assert(!KernAux_MemMap_finish(memmap)); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 1); - assert(MEMMAP.entries_count == 1); - assert(MEMMAP.entries[0].is_available == false); - assert(MEMMAP.entries[0].tag[0] == '\0'); - assert(MEMMAP.entries[0].start == 0); - assert(MEMMAP.entries[0].size == 2); - assert(MEMMAP.entries[0].end == 1); - assert(MEMMAP.entries[0].limit == 2); - - before_assert(); - if (setjmp(jmpbuf) == 0) { - assert(KernAux_MemMap_entry_by_index(memmap, 0) == NULL); - } - expect_assert(); - } - - { - MEMSET; - KernAux_MemMap_init(memmap, 2); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 2); - assert(MEMMAP.entries_count == 0); - - assert(KernAux_MemMap_add_entry(memmap, false, NULL, 0, 1)); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 2); - assert(MEMMAP.entries_count == 1); - - assert(KernAux_MemMap_add_entry(memmap, false, NULL, 1, 1)); - - assert(MEMMAP.is_finished == false); - assert(MEMMAP.memory_size == 2); - assert(MEMMAP.entries_count == 2); - - assert(KernAux_MemMap_finish(memmap)); - - assert(MEMMAP.is_finished); - assert(MEMMAP.memory_size == 2); - assert(MEMMAP.entries_count == 2); - assert(MEMMAP.entries[0].is_available == false); - assert(MEMMAP.entries[0].tag[0] == '\0'); - assert(MEMMAP.entries[0].start == 0); - assert(MEMMAP.entries[0].size == 1); - assert(MEMMAP.entries[0].end == 0); - assert(MEMMAP.entries[0].limit == 1); - assert(MEMMAP.entries[1].is_available == false); - assert(MEMMAP.entries[1].tag[0] == '\0'); - assert(MEMMAP.entries[1].start == 1); - assert(MEMMAP.entries[1].size == 1); - assert(MEMMAP.entries[1].end == 1); - assert(MEMMAP.entries[1].limit == 2); - - assert(KernAux_MemMap_entry_by_index(memmap, 0) == &MEMMAP.entries[0]); - assert(KernAux_MemMap_entry_by_index(memmap, 1) == &MEMMAP.entries[1]); - } - - assert(assert_count_ctr == assert_count_exp); -} diff --git a/tests/test_multiboot2_info_convert_memmap.c b/tests/test_multiboot2_info_convert_memmap.c new file mode 100644 index 00000000..083f828a --- /dev/null +++ b/tests/test_multiboot2_info_convert_memmap.c @@ -0,0 +1,128 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include "../fixtures/multiboot2_info_example0.h" +#include "../fixtures/multiboot2_info_example1.h" +#include "../fixtures/multiboot2_info_example2.h" + +static char buffer[4096]; + +static void test_examples_1_and_2(KernAux_Memmap_Node node); + +void test_main() +{ + struct KernAux_FreeList malloc = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&malloc, buffer, sizeof(buffer)); + + { + const KernAux_Memmap_Builder builder = + KernAux_Multiboot2_Info_to_memmap_builder( + &multiboot2_info_example0.multiboot2_info, + &malloc.malloc + ); + assert(builder == NULL); + } + + { + const KernAux_Memmap_Builder builder = + KernAux_Multiboot2_Info_to_memmap_builder( + (const struct KernAux_Multiboot2_Info*) + &multiboot2_info_example1, + &malloc.malloc + ); + assert(builder); + const KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(builder); + assert(memmap); + + test_examples_1_and_2(memmap->root_node); + + KernAux_Memmap_free(memmap); + } + + { + const KernAux_Memmap_Builder builder = + KernAux_Multiboot2_Info_to_memmap_builder( + &multiboot2_info_example2.multiboot2_info, + &malloc.malloc + ); + assert(builder); + const KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(builder); + assert(memmap); + + test_examples_1_and_2(memmap->root_node); + + KernAux_Memmap_free(memmap); + } +} + +void test_examples_1_and_2(KernAux_Memmap_Node node) +{ + assert(node); + assert(node->mem_start == 0x0); + assert(node->mem_size == 0xffffffffffffffff); + assert(node->mem_end == 0xffffffffffffffff); + assert(node->tag == NULL); + assert(node->next == NULL); + + node = node->children; + assert(node); + assert(node->mem_start == 0x0); + assert(node->mem_size == 654336); + assert(node->mem_end == 0x9fbff); + assert(strcmp(node->tag, "available") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0x9fc00); + assert(node->mem_size == 1024); + assert(node->mem_end == 0x9ffff); + assert(strcmp(node->tag, "reserved") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0xf0000); + assert(node->mem_size == 65536); + assert(node->mem_end == 0xfffff); + assert(strcmp(node->tag, "reserved") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0x100000); + assert(node->mem_size == 133038080); + assert(node->mem_end == 0x7fdffff); + assert(strcmp(node->tag, "available") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0x7fe0000); + assert(node->mem_size == 131072); + assert(node->mem_end == 0x7ffffff); + assert(strcmp(node->tag, "reserved") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0xfffc0000); + assert(node->mem_size == 262144); + assert(node->mem_end == 0xffffffff); + assert(strcmp(node->tag, "reserved") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node == NULL); +}