From af165909dbddb37ff672ab87783ac97d806c5996 Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Fri, 6 Jan 2023 15:56:18 +0400 Subject: [PATCH] Revert "Remove memory map" This reverts commit 1e749c5c893f92ffa58b073720c43c8ae1e8abe0. --- .github/workflows/main.yml | 1 + ChangeLog | 10 +- Makefile.am | 4 + README.md | 3 + configure.ac | 6 + examples/.gitignore | 1 + examples/Makefile.am | 10 + examples/memmap.c | 59 ++++ include/Makefile.am | 1 + include/kernaux.h | 1 + include/kernaux/memmap.h | 73 +++++ include/kernaux/multiboot2.h | 10 + include/kernaux/version.h.in | 1 + src/memmap.c | 303 ++++++++++++++++++++ src/multiboot2/info_convert.c | 67 +++++ tests/.gitignore | 2 + tests/Makefile.am | 28 ++ tests/test_memmap.c | 145 ++++++++++ tests/test_multiboot2_info_convert_memmap.c | 154 ++++++++++ 19 files changed, 876 insertions(+), 3 deletions(-) create mode 100644 examples/memmap.c create mode 100644 include/kernaux/memmap.h create mode 100644 src/memmap.c create mode 100644 src/multiboot2/info_convert.c create 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 ec375c86..239aaa31 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/ChangeLog b/ChangeLog index 56d4b097..8306d832 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,11 @@ -2023-01-06 Alex Kotov +2023-01-04 Alex Kotov - * configure.ac: Package "--with[out]-memmap" has been removed - * include/kernaux/memmap.h: The header has been removed + * include/kernaux/memmap.h: The function "KernAux_Memmap_Builder_add" has been + changed. + * include/kernaux/multiboot2.h: The function + "KernAux_Multiboot2_Info_to_memmap_builder" has been renamed to + "KernAux_Multiboot2_Info_build_memmap_from_memmap", it's signature has been + changed. 2022-12-27 Alex Kotov diff --git a/Makefile.am b/Makefile.am index d78000a1..d85971ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -77,12 +77,16 @@ endif if WITH_MBR libkernaux_la_SOURCES += src/mbr.c endif +if WITH_MEMMAP +libkernaux_la_SOURCES += src/memmap.c +endif if WITH_MULTIBOOT2 libkernaux_la_SOURCES += \ src/multiboot2/header_enums.c \ 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 982b140d..2e33ba67 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ 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* **?.?.?**) + * [Example](/examples/memmap.c) * [printf format parser](/include/kernaux/printf_fmt.h) (*non-breaking since* **0.6.0**) * [Example](/examples/printf_fmt.c) * Usual functions @@ -153,6 +155,7 @@ explicitly included, use `--without-all`. * `--with[out]-asm` - kernel assembler helpers * `--with[out]-cmdline` - command line parser * `--with[out]-free-list` - free list memory allocator +* `--with[out]-memmap` - memory map * `--with[out]-multiboot2` - Multiboot 2 utils * `--with[out]-ntoa` - itoa/ftoa * `--with[out]-printf` - printf diff --git a/configure.ac b/configure.ac index 4a67bbba..8afa5415 100644 --- a/configure.ac +++ b/configure.ac @@ -75,6 +75,7 @@ AC_ARG_WITH( [cmdline], AS_HELP_STRING([--without-cmdline], [wit AC_ARG_WITH( [elf], AS_HELP_STRING([--without-elf], [without ELF utils])) AC_ARG_WITH( [free-list], AS_HELP_STRING([--without-free-list], [without free list memory allocator])) AC_ARG_WITH( [mbr], AS_HELP_STRING([--without-mbr], [without MBR utils])) +AC_ARG_WITH( [memmap], AS_HELP_STRING([--without-memmap], [without memory map])) AC_ARG_WITH( [multiboot2], AS_HELP_STRING([--without-multiboot2], [without Multiboot 2 utils])) AC_ARG_WITH( [ntoa], AS_HELP_STRING([--without-ntoa], [without itoa/ftoa])) AC_ARG_WITH( [pfa], AS_HELP_STRING([--without-pfa], [without Page Frame Allocator])) @@ -115,6 +116,7 @@ if test -z "$with_cmdline"; then with_cmdline=no; fi if test -z "$with_elf"; then with_elf=no; fi if test -z "$with_free_list"; then with_free_list=no; fi if test -z "$with_mbr"; then with_mbr=no; fi +if test -z "$with_memmap"; then with_memmap=no; fi if test -z "$with_multiboot2"; then with_multiboot2=no; fi if test -z "$with_ntoa"; then with_ntoa=no; fi if test -z "$with_pfa"; then with_pfa=no; fi @@ -159,6 +161,7 @@ AS_IF([test "$with_cmdline" = no ], [with_cmdline=no], [wit AS_IF([test "$with_elf" = no ], [with_elf=no], [with_elf=yes]) AS_IF([test "$with_free_list" = no ], [with_free_list=no], [with_free_list=yes]) AS_IF([test "$with_mbr" = no ], [with_mbr=no], [with_mbr=yes]) +AS_IF([test "$with_memmap" = no ], [with_memmap=no], [with_memmap=yes]) AS_IF([test "$with_multiboot2" = no ], [with_multiboot2=no], [with_multiboot2=yes]) AS_IF([test "$with_ntoa" = no ], [with_ntoa=no], [with_ntoa=yes]) AS_IF([test "$with_pfa" = no ], [with_pfa=no], [with_pfa=yes]) @@ -218,6 +221,7 @@ AM_CONDITIONAL([WITH_CMDLINE], [test "$with_cmdline" = yes]) AM_CONDITIONAL([WITH_ELF], [test "$with_elf" = yes]) AM_CONDITIONAL([WITH_FREE_LIST], [test "$with_free_list" = yes]) AM_CONDITIONAL([WITH_MBR], [test "$with_mbr" = yes]) +AM_CONDITIONAL([WITH_MEMMAP], [test "$with_memmap" = yes]) AM_CONDITIONAL([WITH_MULTIBOOT2], [test "$with_multiboot2" = yes]) AM_CONDITIONAL([WITH_NTOA], [test "$with_ntoa" = yes]) AM_CONDITIONAL([WITH_PFA], [test "$with_pfa" = yes]) @@ -265,6 +269,7 @@ AS_IF([test "$with_cmdline" = yes], [AC_DEFINE([WITH_CMDLINE], AS_IF([test "$with_elf" = yes], [AC_DEFINE([WITH_ELF], [1], [with ELF utils])]) AS_IF([test "$with_free_list" = yes], [AC_DEFINE([WITH_FREE_LIST], [1], [with free list memory allocator])]) AS_IF([test "$with_mbr" = yes], [AC_DEFINE([WITH_MBR], [1], [with MBR utils])]) +AS_IF([test "$with_memmap" = yes], [AC_DEFINE([WITH_MEMMAP], [1], [with memory map])]) AS_IF([test "$with_multiboot2" = yes], [AC_DEFINE([WITH_MULTIBOOT2], [1], [with Multiboot 2 utils])]) AS_IF([test "$with_ntoa" = yes], [AC_DEFINE([WITH_NTOA], [1], [with ntoa])]) AS_IF([test "$with_pfa" = yes], [AC_DEFINE([WITH_PFA], [1], [with Page Frame Allocator])]) @@ -293,6 +298,7 @@ AS_IF([test "$with_cmdline" = no], [AC_SUBST([comment_line_cmdline], [ AS_IF([test "$with_elf" = no], [AC_SUBST([comment_line_elf], [//])]) AS_IF([test "$with_free_list" = no], [AC_SUBST([comment_line_free_list], [//])]) AS_IF([test "$with_mbr" = no], [AC_SUBST([comment_line_mbr], [//])]) +AS_IF([test "$with_memmap" = no], [AC_SUBST([comment_line_memmap], [//])]) AS_IF([test "$with_multiboot2" = no], [AC_SUBST([comment_line_multiboot2], [//])]) AS_IF([test "$with_ntoa" = no], [AC_SUBST([comment_line_ntoa], [//])]) AS_IF([test "$with_pfa" = no], [AC_SUBST([comment_line_pfa], [//])]) diff --git a/examples/.gitignore b/examples/.gitignore index cc9133f5..fea0c55c 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -7,6 +7,7 @@ /macro_container_of /macro_packing /macro_static_test +/memmap /multiboot2_header_macro /ntoa /pfa diff --git a/examples/Makefile.am b/examples/Makefile.am index 0590a325..76411d5c 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -79,6 +79,16 @@ TESTS += macro_static_test macro_static_test_LDADD = $(top_builddir)/libkernaux.la macro_static_test_SOURCES = main.c macro_static_test.c +########## +# memmap # +########## + +if WITH_MEMMAP +TESTS += memmap +memmap_LDADD = $(top_builddir)/libkernaux.la +memmap_SOURCES = main.c memmap.c +endif + ########################### # multiboot2_header_macro # ########################### diff --git a/examples/memmap.c b/examples/memmap.c new file mode 100644 index 00000000..7c2b72e3 --- /dev/null +++ b/examples/memmap.c @@ -0,0 +1,59 @@ +#define KERNAUX_ACCESS_PROTECTED + +#include +#include +#include +#include + +#include +#include +#include +#include + +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() +{ + struct KernAux_FreeList malloc = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&malloc, malloc_memory, sizeof(malloc_memory)); + + KernAux_Memmap_Builder memmap_builder = + KernAux_Memmap_Builder_new(&malloc.malloc); + assert(memmap_builder); + + assert(KernAux_Memmap_Builder_add(memmap_builder, 0x0, 654336, true, "available")); + assert(KernAux_Memmap_Builder_add(memmap_builder, 0x9fc00, 1024, false, "reserved")); + assert(KernAux_Memmap_Builder_add(memmap_builder, 0xf0000, 65536, false, "reserved")); + assert(KernAux_Memmap_Builder_add(memmap_builder, 0x100000, 133038080, true, "available")); + assert(KernAux_Memmap_Builder_add(memmap_builder, 0x7fe0000, 131072, false, "reserved")); + assert(KernAux_Memmap_Builder_add(memmap_builder, 0xfffc0000, 262144, false, "reserved")); + + assert(KernAux_Memmap_Builder_add(memmap_builder, 0x400000, 8192, false, "kernel code")); + assert(KernAux_Memmap_Builder_add(memmap_builder, 0x402000, 4096, false, "kernel data")); + + KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(memmap_builder); + assert(memmap); + + KernAux_Memmap_print(memmap, &display); + + KERNAUX_MEMMAP_FREE(memmap); +} diff --git a/include/Makefile.am b/include/Makefile.am index 7c2df040..9a4f2dae 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -19,6 +19,7 @@ nobase_include_HEADERS = \ kernaux/macro/packing_end.run \ kernaux/macro/packing_start.run \ kernaux/mbr.h \ + kernaux/memmap.h \ kernaux/multiboot2.h \ kernaux/multiboot2/header_enums.h \ kernaux/multiboot2/header_helpers.h \ diff --git a/include/kernaux.h b/include/kernaux.h index 4936eb13..7d3888d5 100644 --- a/include/kernaux.h +++ b/include/kernaux.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/include/kernaux/memmap.h b/include/kernaux/memmap.h new file mode 100644 index 00000000..f31596b7 --- /dev/null +++ b/include/kernaux/memmap.h @@ -0,0 +1,73 @@ +#ifndef KERNAUX_INCLUDED_MEMMAP +#define KERNAUX_INCLUDED_MEMMAP + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include +#include + +#define KERNAUX_MEMMAP_FREE(memmap) do { \ + KernAux_Memmap_free(memmap); \ + memmap = NULL; \ +} while (0) + +/********* + * Types * + *********/ + +typedef const struct KernAux_Memmap_Node { + const struct KernAux_Memmap_Node *parent_node; + unsigned char level; + uint64_t mem_start, mem_end, mem_size; + bool is_available; + const char *tag; + const struct KernAux_Memmap_Node *next, *children; +} *KernAux_Memmap_Node; + +typedef const struct KernAux_Memmap { + KernAux_Memmap_Node root_node; + + KernAux_Malloc KERNAUX_PRIVATE_FIELD(malloc); +} *KernAux_Memmap; + +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, + uint64_t mem_start, + uint64_t mem_size, + bool is_available, + const char *tag +); + +KernAux_Memmap +KernAux_Memmap_Builder_finish_and_free(KernAux_Memmap_Builder builder); + +void KernAux_Memmap_free(KernAux_Memmap memmap); +void KernAux_Memmap_print(KernAux_Memmap memmap, KernAux_Display display); + +KernAux_Memmap_Node +KernAux_Memmap_node_by_addr(KernAux_Memmap memmap, uint64_t addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2.h b/include/kernaux/multiboot2.h index 739b5f5a..89c74365 100644 --- a/include/kernaux/multiboot2.h +++ b/include/kernaux/multiboot2.h @@ -8,6 +8,7 @@ extern "C" { #include #include #include +#include #include #include @@ -560,6 +561,15 @@ KERNAUX_STATIC_TEST_STRUCT_SIZE( #include #include +/******************* + * Other functions * + *******************/ + +bool KernAux_Multiboot2_Info_build_memmap_from_memmap( + const struct KernAux_Multiboot2_Info *multiboot2_info, + KernAux_Memmap_Builder builder +); + #ifdef __cplusplus } #endif diff --git a/include/kernaux/version.h.in b/include/kernaux/version.h.in index 600e7995..24db4093 100644 --- a/include/kernaux/version.h.in +++ b/include/kernaux/version.h.in @@ -5,6 +5,7 @@ @comment_line_elf@#define KERNAUX_VERSION_WITH_ELF @comment_line_free_list@#define KERNAUX_VERSION_WITH_FREE_LIST @comment_line_mbr@#define KERNAUX_VERSION_WITH_MBR +@comment_line_memmap@#define KERNAUX_VERSION_WITH_MEMMAP @comment_line_multiboot2@#define KERNAUX_VERSION_WITH_MULTIBOOT2 @comment_line_ntoa@#define KERNAUX_VERSION_WITH_NTOA @comment_line_pfa@#define KERNAUX_VERSION_WITH_PFA diff --git a/src/memmap.c b/src/memmap.c new file mode 100644 index 00000000..13819f66 --- /dev/null +++ b/src/memmap.c @@ -0,0 +1,303 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +static void free_node(KernAux_Malloc malloc, struct KernAux_Memmap_Node *node); +static KernAux_Memmap_Node node_by_addr( + KernAux_Memmap_Node parent_node, + uint64_t addr +); +static void print_nodes( + KernAux_Memmap_Node node, + KernAux_Display display, + unsigned indentation +); + +KernAux_Memmap_Builder +KernAux_Memmap_Builder_new(const KernAux_Malloc malloc) +{ + 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){ + .parent_node = NULL, + .level = 0, + .mem_start = 0x0, + .mem_end = 0xffffffffffffffff, // 2**64 - 1 + .mem_size = 0xffffffffffffffff, // 2**64 - 1 + .is_available = false, + .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, + const uint64_t mem_start, + const uint64_t mem_size, + const bool is_available, + 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->is_available = is_available; + new_node->tag = tag_copy; + + new_node->parent_node = + KernAux_Memmap_node_by_addr(builder->memmap, new_node->mem_start); + KERNAUX_ASSERT(new_node->parent_node); + + if (new_node->mem_start < new_node->parent_node->mem_start || + new_node->mem_end > new_node->parent_node->mem_end) + { + goto fail_after_new_node; + } + + new_node->level = new_node->parent_node->level + 1; + KERNAUX_ASSERT(new_node->level > 0); + + if (new_node->parent_node->children) { + for ( + struct KernAux_Memmap_Node *curr_node = + (struct KernAux_Memmap_Node*)new_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*)new_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_free(const KernAux_Memmap memmap) +{ + 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); +} + +void KernAux_Memmap_print( + const KernAux_Memmap memmap, + const KernAux_Display display +) { + KERNAUX_NOTNULL(memmap); + KERNAUX_ASSERT(memmap->root_node); + KERNAUX_ASSERT(memmap->malloc); + KERNAUX_ASSERT(memmap->root_node->next == NULL); + + print_nodes(memmap->root_node, display, 0); +} + +KernAux_Memmap_Node +KernAux_Memmap_node_by_addr(const KernAux_Memmap memmap, const uint64_t addr) +{ + return node_by_addr(memmap->root_node, addr); +} + +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 (node->tag) KernAux_Malloc_free(malloc, (void*)node->tag); + KernAux_Malloc_free(malloc, node); +} + +KernAux_Memmap_Node node_by_addr( + const KernAux_Memmap_Node parent_node, + const uint64_t addr +) { + KERNAUX_NOTNULL(parent_node); + + if (parent_node->mem_start > addr || parent_node->mem_end < addr) { + return NULL; + } + + for ( + KernAux_Memmap_Node child_node = parent_node->children; + child_node; + child_node = child_node->next + ) { + const KernAux_Memmap_Node node = node_by_addr(child_node, addr); + if (node) return node; + } + + return parent_node; +} + +#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); + const bool is_available = node->is_available; + + INDENT; + PRINTLNF(" unsigned char level: %hhu", node->level); + 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; + PRINTLNF(" bool is_available: %s", is_available ? "TRUE" : "FALSE"); + INDENT; + if (node->tag) { + PRINTLNF(" char* tag: \"%s\"", node->tag); + } else { + PRINTLN(" char* tag: NULL"); + } + + if (node->children) { + INDENT; + PRINTLN(" struct* children: ["); + print_nodes(node->children, display, indentation + 2); + INDENT; + PRINTLN(" ]"); + } else { + INDENT; + PRINTLN(" struct* children: []"); + } + + INDENT; + PRINTLN("}"); + } +} diff --git a/src/multiboot2/info_convert.c b/src/multiboot2/info_convert.c new file mode 100644 index 00000000..10e5cf1f --- /dev/null +++ b/src/multiboot2/info_convert.c @@ -0,0 +1,67 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include +#include +#include +#include + +#include + +#ifdef WITH_MEMMAP +bool KernAux_Multiboot2_Info_build_memmap_from_memmap( + const struct KernAux_Multiboot2_Info *const multiboot2_info, + const KernAux_Memmap_Builder builder +) { + KERNAUX_NOTNULL(multiboot2_info); + KERNAUX_NOTNULL(builder); + + if (!KernAux_Multiboot2_Info_is_valid(multiboot2_info)) return false; + + 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 false; + + 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 false; + } + + 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, + entry->base_addr, + entry->length, + entry->type == KERNAUX_MULTIBOOT2_MEMMAP_AVAILABLE, + KernAux_Multiboot2_ITag_MemoryMap_EntryBase_Type_to_str( + entry->type + ) + ); + + if (!node) { + KernAux_Memmap_Builder_finish_and_free(builder); + return false; + } + } + + return true; +} +#endif diff --git a/tests/.gitignore b/tests/.gitignore index 6780c747..7ea1d768 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -11,11 +11,13 @@ /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 aea3b8fa..fa1ebda5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -168,6 +168,18 @@ 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 # ################################## @@ -229,6 +241,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 new file mode 100644 index 00000000..5cc41e3b --- /dev/null +++ b/tests/test_memmap.c @@ -0,0 +1,145 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include + +static char buffer[4096]; +static struct KernAux_FreeList malloc; + +void test_main() +{ + malloc = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&malloc, buffer, sizeof(buffer)); + + KernAux_Memmap_Builder memmap_builder = + KernAux_Memmap_Builder_new(&malloc.malloc); + assert(memmap_builder); + + // Level 1 + KernAux_Memmap_Node foo_node = + KernAux_Memmap_Builder_add(memmap_builder, 0x0ULL, 0x8000000000000000ULL, true, "foo"); + KernAux_Memmap_Node bar_node = + KernAux_Memmap_Builder_add(memmap_builder, 0x8000000000000000ULL, 0x8000000000000000ULL, false, "bar"); + + // Level 2 + KernAux_Memmap_Node foo1_node = + KernAux_Memmap_Builder_add(memmap_builder, 0x0ULL, 0x4000000000000000ULL, true, "foo1"); + KernAux_Memmap_Node foo2_node = + KernAux_Memmap_Builder_add(memmap_builder, 0x4000000000000000ULL, 0x4000000000000000ULL, false, "foo2"); + KernAux_Memmap_Node bar1_node = + KernAux_Memmap_Builder_add(memmap_builder, 0x8000000000000000ULL, 0x4000000000000000ULL, true, "bar1"); + KernAux_Memmap_Node bar2_node = + KernAux_Memmap_Builder_add(memmap_builder, 0xc000000000000000ULL, 0x4000000000000000ULL, false, "bar2"); + + // Level 1 + assert(foo_node); + assert(bar_node); + + // Level 2 + assert(foo1_node); + assert(foo2_node); + assert(bar1_node); + assert(bar2_node); + + const KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(memmap_builder); + assert(memmap); + + assert(memmap->root_node->next == NULL); + assert(memmap->root_node->children == foo_node); + + assert(foo_node->next == bar_node); + assert(bar_node->next == NULL); + + assert(foo_node->children == foo1_node); + assert(foo1_node->next == foo2_node); + assert(foo1_node->children == NULL); + assert(foo2_node->next == NULL); + assert(foo2_node->children == NULL); + + assert(bar_node->children == bar1_node); + assert(bar1_node->next == bar2_node); + assert(bar1_node->children == NULL); + assert(bar2_node->next == NULL); + assert(bar2_node->children == NULL); + + assert(foo1_node == KernAux_Memmap_node_by_addr(memmap, 0x0ULL)); + assert(foo1_node == KernAux_Memmap_node_by_addr(memmap, 0x0ULL + 0x4000000000000000ULL / 2)); + assert(foo1_node == KernAux_Memmap_node_by_addr(memmap, 0x0ULL + 0x4000000000000000ULL - 1)); + + assert(foo2_node == KernAux_Memmap_node_by_addr(memmap, 0x4000000000000000ULL)); + assert(foo2_node == KernAux_Memmap_node_by_addr(memmap, 0x4000000000000000ULL + 0x4000000000000000ULL / 2)); + assert(foo2_node == KernAux_Memmap_node_by_addr(memmap, 0x4000000000000000ULL + 0x4000000000000000ULL - 1)); + + assert(bar1_node == KernAux_Memmap_node_by_addr(memmap, 0x8000000000000000ULL)); + assert(bar1_node == KernAux_Memmap_node_by_addr(memmap, 0x8000000000000000ULL + 0x4000000000000000ULL / 2)); + assert(bar1_node == KernAux_Memmap_node_by_addr(memmap, 0x8000000000000000ULL + 0x4000000000000000ULL - 1)); + + assert(bar2_node == KernAux_Memmap_node_by_addr(memmap, 0xc000000000000000ULL)); + assert(bar2_node == KernAux_Memmap_node_by_addr(memmap, 0xc000000000000000ULL + 0x4000000000000000ULL / 2)); + assert(bar2_node == KernAux_Memmap_node_by_addr(memmap, 0xc000000000000000ULL + 0x4000000000000000ULL - 1)); + + // Level 0 + + assert(memmap->root_node->parent_node == NULL); + assert(memmap->root_node->level == 0); + + // Level 1 + + assert(foo_node->parent_node == memmap->root_node); + assert(foo_node->level == 1); + assert(foo_node->mem_start == 0x0); + assert(foo_node->mem_size == 0x8000000000000000ULL); + assert(foo_node->mem_end == 0x7fffffffffffffffULL); + assert(foo_node->is_available == true); + assert(strcmp(foo_node->tag, "foo") == 0); + + assert(bar_node->parent_node == memmap->root_node); + assert(bar_node->level == 1); + assert(bar_node->mem_start == 0x8000000000000000ULL); + assert(bar_node->mem_size == 0x8000000000000000ULL); + assert(bar_node->mem_end == 0xffffffffffffffffULL); + assert(bar_node->is_available == false); + assert(strcmp(bar_node->tag, "bar") == 0); + + // Level 2 + + assert(foo1_node->parent_node == foo_node); + assert(foo1_node->level == 2); + assert(foo1_node->mem_start == 0x0); + assert(foo1_node->mem_size == 0x4000000000000000ULL); + assert(foo1_node->mem_end == 0x3fffffffffffffffULL); + assert(foo1_node->is_available == true); + assert(strcmp(foo1_node->tag, "foo1") == 0); + + assert(foo2_node->parent_node == foo_node); + assert(foo2_node->level == 2); + assert(foo2_node->mem_start == 0x4000000000000000ULL); + assert(foo2_node->mem_size == 0x4000000000000000ULL); + assert(foo2_node->mem_end == 0x7fffffffffffffffULL); + assert(foo2_node->is_available == false); + assert(strcmp(foo2_node->tag, "foo2") == 0); + + assert(bar1_node->parent_node == bar_node); + assert(bar1_node->level == 2); + assert(bar1_node->mem_start == 0x8000000000000000ULL); + assert(bar1_node->mem_size == 0x4000000000000000ULL); + assert(bar1_node->mem_end == 0xbfffffffffffffffULL); + assert(bar1_node->is_available == true); + assert(strcmp(bar1_node->tag, "bar1") == 0); + + assert(bar2_node->parent_node == bar_node); + assert(bar2_node->level == 2); + assert(bar2_node->mem_start == 0xc000000000000000ULL); + assert(bar2_node->mem_size == 0x4000000000000000ULL); + assert(bar2_node->mem_end == 0xffffffffffffffffULL); + assert(bar2_node->is_available == false); + assert(strcmp(bar2_node->tag, "bar2") == 0); +} diff --git a/tests/test_multiboot2_info_convert_memmap.c b/tests/test_multiboot2_info_convert_memmap.c new file mode 100644 index 00000000..194a8562 --- /dev/null +++ b/tests/test_multiboot2_info_convert_memmap.c @@ -0,0 +1,154 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#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 memmap); + +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_Memmap_Builder_new(&malloc.malloc); + assert(builder != NULL); + + const bool result = KernAux_Multiboot2_Info_build_memmap_from_memmap( + &multiboot2_info_example0.multiboot2_info, + builder + ); + assert(!result); + } + + { + const KernAux_Memmap_Builder builder = + KernAux_Memmap_Builder_new(&malloc.malloc); + assert(builder != NULL); + + const bool result = KernAux_Multiboot2_Info_build_memmap_from_memmap( + (const struct KernAux_Multiboot2_Info*) + &multiboot2_info_example1, + builder + ); + assert(result); + + const KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(builder); + assert(memmap); + + test_examples_1_and_2(memmap); + + KernAux_Memmap_free(memmap); + } + + { + const KernAux_Memmap_Builder builder = + KernAux_Memmap_Builder_new(&malloc.malloc); + assert(builder != NULL); + + const bool result = KernAux_Multiboot2_Info_build_memmap_from_memmap( + &multiboot2_info_example2.multiboot2_info, + builder + ); + assert(result); + + const KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(builder); + assert(memmap); + + test_examples_1_and_2(memmap); + + KernAux_Memmap_free(memmap); + } +} + +void test_examples_1_and_2(const KernAux_Memmap memmap) +{ + KernAux_Memmap_Node node = memmap->root_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); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0x0)); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0x9fbff)); + + 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); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0x9fc00)); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0x9ffff)); + + 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); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0xf0000)); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0xfffff)); + + 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); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0x100000)); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0x7fdffff)); + + 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); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0x7fe0000)); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0x7ffffff)); + + 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); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0xfffc0000)); + assert(node == KernAux_Memmap_node_by_addr(memmap, 0xffffffff)); + + node = node->next; + assert(node == NULL); +}