Repository: tailix/libkernaux (language: python)

Issue:
Add Multiboot 2 header structs and funcs



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 6c1cab76..941b7af6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,11 +42,16 @@
 /examples/printf_va
 /examples/units_human
 
+/tests/multiboot2_header_print1
+/tests/multiboot2_header_print2
 /tests/multiboot2_info_print1
 /tests/multiboot2_info_print2
 /tests/test_cmdline
 /tests/test_elf
 /tests/test_itoa
+/tests/test_multiboot2_header_helpers
+/tests/test_multiboot2_header_print
+/tests/test_multiboot2_header_validation
 /tests/test_multiboot2_info_helpers
 /tests/test_multiboot2_info_print
 /tests/test_multiboot2_info_validation
diff --git a/Makefile.am b/Makefile.am
index f3ca4ed4..36648a5a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -56,14 +56,23 @@ endif
 
 if WITH_MULTIBOOT2
 libkernaux_a_SOURCES += \
+	src/multiboot2/enums_to_str.c \
+	src/multiboot2/header_helpers.c \
+	src/multiboot2/header_is_valid.c \
+	src/multiboot2/header_print.c \
 	src/multiboot2/info_helpers.c \
 	src/multiboot2/info_is_valid.c \
 	src/multiboot2/info_print.c
 TESTS += \
+	tests/test_multiboot2_header_helpers \
+	tests/test_multiboot2_header_print \
+	tests/test_multiboot2_header_validation \
 	tests/test_multiboot2_info_helpers \
 	tests/test_multiboot2_info_print \
 	tests/test_multiboot2_info_validation
 noinst_PROGRAMS += \
+	tests/multiboot2_header_print1 \
+	tests/multiboot2_header_print2 \
 	tests/multiboot2_info_print1 \
 	tests/multiboot2_info_print2
 endif
@@ -111,6 +120,16 @@ examples_units_human_SOURCES = \
 	$(libkernaux_a_SOURCES) \
 	examples/units_human.c
 
+tests_multiboot2_header_print1_SOURCES = \
+	$(libkernaux_a_SOURCES) \
+	tests/multiboot2_header_print1.c \
+	tests/multiboot2_header_example1.h
+
+tests_multiboot2_header_print2_SOURCES = \
+	$(libkernaux_a_SOURCES) \
+	tests/multiboot2_header_print2.c \
+	tests/multiboot2_header_example2.h
+
 tests_multiboot2_info_print1_SOURCES = \
 	$(libkernaux_a_SOURCES) \
 	tests/multiboot2_info_print1.c \
@@ -133,6 +152,22 @@ tests_test_itoa_SOURCES = \
 	$(libkernaux_a_SOURCES) \
 	tests/test_itoa.c
 
+tests_test_multiboot2_header_helpers_SOURCES = \
+	$(libkernaux_a_SOURCES) \
+	tests/test_multiboot2_header_helpers.c \
+	tests/multiboot2_header_example1.h \
+	tests/multiboot2_header_example2.h
+
+tests_test_multiboot2_header_print_SOURCES = \
+	$(libkernaux_a_SOURCES) \
+	tests/test_multiboot2_header_print.c
+
+tests_test_multiboot2_header_validation_SOURCES = \
+	$(libkernaux_a_SOURCES) \
+	tests/test_multiboot2_header_validation.c \
+	tests/multiboot2_header_example1.h \
+	tests/multiboot2_header_example2.h
+
 tests_test_multiboot2_info_helpers_SOURCES = \
 	$(libkernaux_a_SOURCES) \
 	tests/test_multiboot2_info_helpers.c \
diff --git a/README.md b/README.md
index 83a26b8f..9ddf3f72 100644
--- a/README.md
+++ b/README.md
@@ -37,16 +37,16 @@ API
     * [Guards](/examples/assert_guards.c)
   * Stack trace *(planned)*
 * Device drivers (for debugging only)
-  * [Serial console](/include/kernaux/console.h)
+  * [Serial console](/include/kernaux/console.h) *(work in progress)*
   * [Framebuffer](/include/kernaux/framebuffer.h) *(planned)*
   * USB *(planned)*
 * Algorithms
   * [Simple command line parser](/include/kernaux/cmdline.h)
     * [Example](/examples/cmdline.c)
-  * [Page Frame Allocator](/include/kernaux/pfa.h)
+  * [Page Frame Allocator](/include/kernaux/pfa.h) *(work in progress)*
     * [Example](/examples/pfa.c)
 * Data formats
-  * [Multiboot 2 (GRUB 2) information parser](/include/kernaux/multiboot2.h)
+  * [Multiboot 2 (GRUB 2) information parser](/include/kernaux/multiboot2.h) *(work in progress)*
   * Stivale 2 (Limine) information parser *(planned)*
   * [ELF utils](/include/kernaux/elf.h) *(work in progress)*
 * Utilities
diff --git a/examples/kernel-multiboot2-grub/Makefile b/examples/kernel-multiboot2-grub/Makefile
index 73bd6a80..3a642ee3 100644
--- a/examples/kernel-multiboot2-grub/Makefile
+++ b/examples/kernel-multiboot2-grub/Makefile
@@ -30,7 +30,7 @@ CFLAGS = \
 	-fno-stack-protector \
 	-I../../include
 
-OBJS = main.c.o start.S.o
+OBJS = main.c.o multiboot2.c.o start.S.o
 
 test: run
 	$(DIFF) -a -Z $(EXPECTED) $(OUTPUT)
diff --git a/examples/kernel-multiboot2-grub/expected.txt b/examples/kernel-multiboot2-grub/expected.txt
index e382f3ab..5c1ba799 100644
Binary files a/examples/kernel-multiboot2-grub/expected.txt and b/examples/kernel-multiboot2-grub/expected.txt differ
diff --git a/examples/kernel-multiboot2-grub/linker.ld b/examples/kernel-multiboot2-grub/linker.ld
index 32213944..9de8dc69 100644
--- a/examples/kernel-multiboot2-grub/linker.ld
+++ b/examples/kernel-multiboot2-grub/linker.ld
@@ -14,7 +14,7 @@ SECTIONS
 
     .text BLOCK(4K) : ALIGN(4K)
     {
-        *(.multiboot)
+        *(.multiboot2)
         *(.text)
     }
 
diff --git a/examples/kernel-multiboot2-grub/main.c b/examples/kernel-multiboot2-grub/main.c
index e658d1f5..2d8d9030 100644
--- a/examples/kernel-multiboot2-grub/main.c
+++ b/examples/kernel-multiboot2-grub/main.c
@@ -7,13 +7,23 @@
 
 void poweroff();
 
+extern const struct KernAux_Multiboot2_Header multiboot2_header;
+
 static void panic(const char *str);
 
 void main(
     const uint32_t multiboot2_magic,
     const struct KernAux_Multiboot2_Info *const multiboot2_info
 ) {
-    if (multiboot2_magic != KERNAUX_MULTIBOOT2_MAGIC) {
+    if (!KernAux_Multiboot2_Header_is_valid(&multiboot2_header)) {
+        panic("Multiboot 2 header is invalid");
+    } else {
+        kernaux_console_printf("Multiboot 2 header is valid\n");
+    }
+
+    KernAux_Multiboot2_Header_print(&multiboot2_header, kernaux_console_printf);
+
+    if (multiboot2_magic != KERNAUX_MULTIBOOT2_INFO_MAGIC) {
         panic("Multiboot 2 magic number is invalid");
     } else {
         kernaux_console_printf("Multiboot 2 magic number is valid\n");
diff --git a/examples/kernel-multiboot2-grub/multiboot2.c b/examples/kernel-multiboot2-grub/multiboot2.c
new file mode 100644
index 00000000..acaf61a0
--- /dev/null
+++ b/examples/kernel-multiboot2-grub/multiboot2.c
@@ -0,0 +1,28 @@
+#include <kernaux/multiboot2.h>
+
+// TODO: add more tags
+__attribute__((section(".multiboot2"), used))
+__attribute__((aligned(KERNAUX_MULTIBOOT2_HEADER_ALIGN)))
+const struct {
+    struct KernAux_Multiboot2_Header multiboot2_header;
+    struct KernAux_Multiboot2_HTag_None tag_none;
+}
+__attribute__((packed))
+multiboot2_header = {
+    .multiboot2_header = {
+        .magic = KERNAUX_MULTIBOOT2_HEADER_MAGIC,
+        .arch = KERNAUX_MULTIBOOT2_HEADER_ARCH_I386,
+        .total_size = sizeof(multiboot2_header),
+        .checksum = KERNAUX_MULTIBOOT2_HEADER_CHECKSUM(
+            KERNAUX_MULTIBOOT2_HEADER_ARCH_I386,
+            sizeof(multiboot2_header)
+        ),
+    },
+    .tag_none = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_NONE,
+            .flags = 0,
+            .size = sizeof(multiboot2_header.tag_none),
+        },
+    },
+};
diff --git a/examples/kernel-multiboot2-grub/start.S b/examples/kernel-multiboot2-grub/start.S
index 3c668a31..5c6a4f1b 100644
--- a/examples/kernel-multiboot2-grub/start.S
+++ b/examples/kernel-multiboot2-grub/start.S
@@ -1,21 +1,3 @@
-.set MULTIBOOT_MAGIC, 0xE85250D6
-.set MULTIBOOT_ARCH, 0
-.set MULTIBOOT_LENGTH, 16 + 8
-.set MULTIBOOT_CHECKSUM, -(MULTIBOOT_MAGIC + MULTIBOOT_ARCH + MULTIBOOT_LENGTH)
-
-
-
-.section .multiboot
-.align 4
-.long MULTIBOOT_MAGIC
-.long MULTIBOOT_ARCH
-.long MULTIBOOT_LENGTH
-.long MULTIBOOT_CHECKSUM
-.long 0
-.long 8
-
-
-
 .section .bss
 
 .global _kernel_stack_start
diff --git a/examples/kernel-multiboot2-limine/Makefile b/examples/kernel-multiboot2-limine/Makefile
index 56d1fc8d..4e296b10 100644
--- a/examples/kernel-multiboot2-limine/Makefile
+++ b/examples/kernel-multiboot2-limine/Makefile
@@ -45,7 +45,7 @@ CFLAGS = \
 	-fno-stack-protector \
 	-I../../include
 
-OBJS = main.c.o start.S.o
+OBJS = main.c.o multiboot2.c.o start.S.o
 
 test: run
 	$(DIFF) -a -Z $(EXPECTED) $(OUTPUT)
diff --git a/examples/kernel-multiboot2-limine/expected.txt b/examples/kernel-multiboot2-limine/expected.txt
index 8f235096..312e0261 100644
Binary files a/examples/kernel-multiboot2-limine/expected.txt and b/examples/kernel-multiboot2-limine/expected.txt differ
diff --git a/examples/kernel-multiboot2-limine/linker.ld b/examples/kernel-multiboot2-limine/linker.ld
index 32213944..9de8dc69 100644
--- a/examples/kernel-multiboot2-limine/linker.ld
+++ b/examples/kernel-multiboot2-limine/linker.ld
@@ -14,7 +14,7 @@ SECTIONS
 
     .text BLOCK(4K) : ALIGN(4K)
     {
-        *(.multiboot)
+        *(.multiboot2)
         *(.text)
     }
 
diff --git a/examples/kernel-multiboot2-limine/main.c b/examples/kernel-multiboot2-limine/main.c
index e658d1f5..2d8d9030 100644
--- a/examples/kernel-multiboot2-limine/main.c
+++ b/examples/kernel-multiboot2-limine/main.c
@@ -7,13 +7,23 @@
 
 void poweroff();
 
+extern const struct KernAux_Multiboot2_Header multiboot2_header;
+
 static void panic(const char *str);
 
 void main(
     const uint32_t multiboot2_magic,
     const struct KernAux_Multiboot2_Info *const multiboot2_info
 ) {
-    if (multiboot2_magic != KERNAUX_MULTIBOOT2_MAGIC) {
+    if (!KernAux_Multiboot2_Header_is_valid(&multiboot2_header)) {
+        panic("Multiboot 2 header is invalid");
+    } else {
+        kernaux_console_printf("Multiboot 2 header is valid\n");
+    }
+
+    KernAux_Multiboot2_Header_print(&multiboot2_header, kernaux_console_printf);
+
+    if (multiboot2_magic != KERNAUX_MULTIBOOT2_INFO_MAGIC) {
         panic("Multiboot 2 magic number is invalid");
     } else {
         kernaux_console_printf("Multiboot 2 magic number is valid\n");
diff --git a/examples/kernel-multiboot2-limine/multiboot2.c b/examples/kernel-multiboot2-limine/multiboot2.c
new file mode 100644
index 00000000..acaf61a0
--- /dev/null
+++ b/examples/kernel-multiboot2-limine/multiboot2.c
@@ -0,0 +1,28 @@
+#include <kernaux/multiboot2.h>
+
+// TODO: add more tags
+__attribute__((section(".multiboot2"), used))
+__attribute__((aligned(KERNAUX_MULTIBOOT2_HEADER_ALIGN)))
+const struct {
+    struct KernAux_Multiboot2_Header multiboot2_header;
+    struct KernAux_Multiboot2_HTag_None tag_none;
+}
+__attribute__((packed))
+multiboot2_header = {
+    .multiboot2_header = {
+        .magic = KERNAUX_MULTIBOOT2_HEADER_MAGIC,
+        .arch = KERNAUX_MULTIBOOT2_HEADER_ARCH_I386,
+        .total_size = sizeof(multiboot2_header),
+        .checksum = KERNAUX_MULTIBOOT2_HEADER_CHECKSUM(
+            KERNAUX_MULTIBOOT2_HEADER_ARCH_I386,
+            sizeof(multiboot2_header)
+        ),
+    },
+    .tag_none = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_NONE,
+            .flags = 0,
+            .size = sizeof(multiboot2_header.tag_none),
+        },
+    },
+};
diff --git a/examples/kernel-multiboot2-limine/start.S b/examples/kernel-multiboot2-limine/start.S
index 3c668a31..5c6a4f1b 100644
--- a/examples/kernel-multiboot2-limine/start.S
+++ b/examples/kernel-multiboot2-limine/start.S
@@ -1,21 +1,3 @@
-.set MULTIBOOT_MAGIC, 0xE85250D6
-.set MULTIBOOT_ARCH, 0
-.set MULTIBOOT_LENGTH, 16 + 8
-.set MULTIBOOT_CHECKSUM, -(MULTIBOOT_MAGIC + MULTIBOOT_ARCH + MULTIBOOT_LENGTH)
-
-
-
-.section .multiboot
-.align 4
-.long MULTIBOOT_MAGIC
-.long MULTIBOOT_ARCH
-.long MULTIBOOT_LENGTH
-.long MULTIBOOT_CHECKSUM
-.long 0
-.long 8
-
-
-
 .section .bss
 
 .global _kernel_stack_start
diff --git a/include/kernaux/multiboot2.h b/include/kernaux/multiboot2.h
index e41961a5..8a1f2d13 100644
--- a/include/kernaux/multiboot2.h
+++ b/include/kernaux/multiboot2.h
@@ -8,14 +8,53 @@ extern "C" {
 #include <stdint.h>
 #include <stdbool.h>
 
-#define KERNAUX_MULTIBOOT2_MAGIC 0x36d76289
+#define KERNAUX_MULTIBOOT2_HEADER_MAGIC 0xE85250D6
+#define KERNAUX_MULTIBOOT2_INFO_MAGIC   0x36D76289
+
+#define KERNAUX_MULTIBOOT2_HEADER_ALIGN 4
+
+#define KERNAUX_MULTIBOOT2_HEADER_CHECKSUM(arch, total_size) \
+    ((uint32_t)(-(                                           \
+        ((uint32_t)KERNAUX_MULTIBOOT2_HEADER_MAGIC) +        \
+        ((uint32_t)(arch)) +                                 \
+        ((uint32_t)(total_size))                             \
+    )))
 
 #define KERNAUX_MULTIBOOT2_DATA(ptr) (((uint8_t*)(ptr)) + sizeof(*(ptr)))
 
+#define KERNAUX_MULTIBOOT2_TAG_ALIGN 8
+
+#define KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base) \
+    ((struct KernAux_Multiboot2_HTagBase*)KERNAUX_MULTIBOOT2_TAG_NEXT(tag_base))
+#define KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base) \
+    ((struct KernAux_Multiboot2_ITagBase*)KERNAUX_MULTIBOOT2_TAG_NEXT(tag_base))
+#define KERNAUX_MULTIBOOT2_TAG_NEXT(tag_base) \
+    ((uint8_t*)tag_base + KERNAUX_MULTIBOOT2_TAG_SIZE_ALIGN(tag_base))
+#define KERNAUX_MULTIBOOT2_TAG_SIZE_ALIGN(tag_base) \
+    (((tag_base)->size + 7) & ~7)
+
+#define KERNAUX_MULTIBOOT2_HTAG_BASE_FLAG_OPTIONAL 1
+
+#define KERNAUX_MULTIBOOT2_HTAG_FLAGS_REQUIRE_CONSOLE (1 << 0)
+#define KERNAUX_MULTIBOOT2_HTAG_FLAGS_EGA_SUPPORT     (1 << 1)
+
 /***********************
  * Header common types *
  ***********************/
 
+enum KernAux_Multiboot2_Header_Arch {
+    KERNAUX_MULTIBOOT2_HEADER_ARCH_I386   = 0,
+    KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32 = 4,
+};
+
+struct KernAux_Multiboot2_Header {
+    unsigned magic                           : 32;
+    enum KernAux_Multiboot2_Header_Arch arch : 32;
+    unsigned total_size                      : 32;
+    unsigned checksum                        : 32;
+}
+__attribute__((packed));
+
 enum KernAux_Multiboot2_HTag {
     KERNAUX_MULTIBOOT2_HTAG_NONE = 0,
     KERNAUX_MULTIBOOT2_HTAG_INFO_REQ = 1,
@@ -30,14 +69,6 @@ enum KernAux_Multiboot2_HTag {
     KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER = 10,
 };
 
-struct KernAux_Multiboot2_Header {
-    unsigned magic      : 32;
-    unsigned arch       : 32;
-    unsigned total_size : 32;
-    unsigned checksum   : 32;
-}
-__attribute__((packed));
-
 struct KernAux_Multiboot2_HTagBase {
     enum KernAux_Multiboot2_HTag type : 16;
     unsigned flags                    : 16;
@@ -49,6 +80,12 @@ __attribute__((packed));
  * Information common types *
  ****************************/
 
+struct KernAux_Multiboot2_Info {
+    unsigned total_size : 32;
+    unsigned reserved1  : 32;
+}
+__attribute__((packed));
+
 enum KernAux_Multiboot2_ITag {
     KERNAUX_MULTIBOOT2_ITAG_NONE = 0,
     KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE = 1,
@@ -74,18 +111,137 @@ enum KernAux_Multiboot2_ITag {
     KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR = 21,
 };
 
-struct KernAux_Multiboot2_Info {
-    unsigned total_size : 32;
-    unsigned reserved1  : 32;
-}
-__attribute__((packed));
-
 struct KernAux_Multiboot2_ITagBase {
     enum KernAux_Multiboot2_ITag type : 32;
     unsigned size                     : 32;
 }
 __attribute__((packed));
 
+/***************************
+ * Header additional types *
+ ***************************/
+
+enum KernAux_Multiboot2_HTag_RelocatableHeader_Preference {
+    KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_NONE    = 0,
+    KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_LOWEST  = 1,
+    KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_HIGHEST = 2,
+};
+
+/********************************
+ * Information additional types *
+ ********************************/
+
+struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase {
+    unsigned long long base_addr : 64;
+    unsigned long long length    : 64;
+    unsigned type                : 32;
+    unsigned reserved1           : 32;
+}
+__attribute__((packed));
+
+/*************************
+ * Header tag structures *
+ *************************/
+
+struct KernAux_Multiboot2_HTag_None {
+    // type = 0
+    // size = 8
+    struct KernAux_Multiboot2_HTagBase base;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_InfoReq {
+    // type = 1
+    // size > 8
+    struct KernAux_Multiboot2_HTagBase base;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_Addr {
+    // type = 2
+    // size = 24
+    struct KernAux_Multiboot2_HTagBase base;
+
+    unsigned header_addr   : 32;
+    unsigned load_addr     : 32;
+    unsigned load_end_addr : 32;
+    unsigned bss_end_addr  : 32;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_EntryAddr {
+    // type = 3
+    // size = 12
+    struct KernAux_Multiboot2_HTagBase base;
+
+    unsigned entry_addr : 32;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_Flags {
+    // type = 4
+    // size = 12
+    struct KernAux_Multiboot2_HTagBase base;
+
+    unsigned console_flags : 32;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_Framebuffer {
+    // type = 5
+    // size = 20
+    struct KernAux_Multiboot2_HTagBase base;
+
+    unsigned width  : 32;
+    unsigned height : 32;
+    unsigned depth  : 32;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_ModuleAlign {
+    // type = 6
+    // size = 8
+    struct KernAux_Multiboot2_HTagBase base;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_EFIBootServices {
+    // type = 7
+    // size = 8
+    struct KernAux_Multiboot2_HTagBase base;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_EFII386EntryAddr {
+    // type = 8
+    // size = 12
+    struct KernAux_Multiboot2_HTagBase base;
+
+    unsigned entry_addr : 32;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr {
+    // type = 9
+    // size = 12
+    struct KernAux_Multiboot2_HTagBase base;
+
+    unsigned entry_addr : 32;
+}
+__attribute__((packed));
+
+struct KernAux_Multiboot2_HTag_RelocatableHeader {
+    // type = 10
+    // size = 24
+    struct KernAux_Multiboot2_HTagBase base;
+
+    unsigned min_addr                                                     : 32;
+    unsigned max_addr                                                     : 32;
+    unsigned align                                                        : 32;
+    enum KernAux_Multiboot2_HTag_RelocatableHeader_Preference preferences : 32;
+}
+__attribute__((packed));
+
 /******************************
  * Information tag structures *
  ******************************/
@@ -304,17 +460,42 @@ struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr {
 }
 __attribute__((packed));
 
-/*************************
- * Additional structures *
- *************************/
+/********************
+ * String functions *
+ ********************/
 
-struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase {
-    unsigned long long base_addr : 64;
-    unsigned long long length    : 64;
-    unsigned type                : 32;
-    unsigned reserved1           : 32;
-}
-__attribute__((packed));
+const char *KernAux_Multiboot2_Header_Arch_to_str(
+    enum KernAux_Multiboot2_Header_Arch arch
+);
+
+const char *KernAux_Multiboot2_HTag_to_str(
+    enum KernAux_Multiboot2_HTag tag_type
+);
+
+const char *KernAux_Multiboot2_ITag_to_str(
+    enum KernAux_Multiboot2_ITag tag_type
+);
+
+const char *KernAux_Multiboot2_HTag_RelocatableHeader_Preference_to_str(
+    enum KernAux_Multiboot2_HTag_RelocatableHeader_Preference pref
+);
+
+/***************************
+ * Header helper functions *
+ ***************************/
+
+const struct KernAux_Multiboot2_HTagBase
+*KernAux_Multiboot2_Header_first_tag_with_type(
+    const struct KernAux_Multiboot2_Header *multiboot2_header,
+    enum KernAux_Multiboot2_HTag tag_type
+);
+
+const struct KernAux_Multiboot2_HTagBase
+*KernAux_Multiboot2_Header_tag_with_type_after(
+    const struct KernAux_Multiboot2_Header *multiboot2_header,
+    enum KernAux_Multiboot2_HTag tag_type,
+    const struct KernAux_Multiboot2_HTagBase *after_tag
+);
 
 /********************************
  * Information helper functions *
@@ -337,6 +518,20 @@ const char *KernAux_Multiboot2_Info_boot_cmd_line(
     const struct KernAux_Multiboot2_Info *multiboot2_info
 );
 
+/**************************
+ * Header print functions *
+ **************************/
+
+void KernAux_Multiboot2_Header_print(
+    const struct KernAux_Multiboot2_Header *multiboot2_header,
+    void (*printf)(const char *format, ...)
+);
+
+void KernAux_Multiboot2_HTagBase_print(
+    const struct KernAux_Multiboot2_HTagBase *tag_base,
+    void (*printf)(const char *format, ...)
+);
+
 /*******************************
  * Information print functions *
  *******************************/
@@ -351,6 +546,62 @@ void KernAux_Multiboot2_ITagBase_print(
     void (*printf)(const char *format, ...)
 );
 
+/*******************************
+ * Header validation functions *
+ *******************************/
+
+bool KernAux_Multiboot2_Header_is_valid(
+    const struct KernAux_Multiboot2_Header *multiboot2_header
+);
+
+bool KernAux_Multiboot2_HTagBase_is_valid(
+    const struct KernAux_Multiboot2_HTagBase *tag_base
+);
+
+bool KernAux_Multiboot2_HTag_None_is_valid(
+    const struct KernAux_Multiboot2_HTag_None *tag
+);
+
+bool KernAux_Multiboot2_HTag_InfoReq_is_valid(
+    const struct KernAux_Multiboot2_HTag_InfoReq *tag
+);
+
+bool KernAux_Multiboot2_HTag_Addr_is_valid(
+    const struct KernAux_Multiboot2_HTag_Addr *tag
+);
+
+bool KernAux_Multiboot2_HTag_EntryAddr_is_valid(
+    const struct KernAux_Multiboot2_HTag_EntryAddr *tag
+);
+
+bool KernAux_Multiboot2_HTag_Flags_is_valid(
+    const struct KernAux_Multiboot2_HTag_Flags *tag
+);
+
+bool KernAux_Multiboot2_HTag_Framebuffer_is_valid(
+    const struct KernAux_Multiboot2_HTag_Framebuffer *tag
+);
+
+bool KernAux_Multiboot2_HTag_ModuleAlign_is_valid(
+    const struct KernAux_Multiboot2_HTag_ModuleAlign *tag
+);
+
+bool KernAux_Multiboot2_HTag_EFIBootServices_is_valid(
+    const struct KernAux_Multiboot2_HTag_EFIBootServices *tag
+);
+
+bool KernAux_Multiboot2_HTag_EFII386EntryAddr_is_valid(
+    const struct KernAux_Multiboot2_HTag_EFII386EntryAddr *tag
+);
+
+bool KernAux_Multiboot2_HTag_EFIAmd64EntryAddr_is_valid(
+    const struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr *tag
+);
+
+bool KernAux_Multiboot2_HTag_RelocatableHeader_is_valid(
+    const struct KernAux_Multiboot2_HTag_RelocatableHeader *tag
+);
+
 /************************************
  * Information validation functions *
  ************************************/
diff --git a/src/multiboot2/enums_to_str.c b/src/multiboot2/enums_to_str.c
new file mode 100644
index 00000000..dcb8b14e
--- /dev/null
+++ b/src/multiboot2/enums_to_str.c
@@ -0,0 +1,119 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kernaux/multiboot2.h>
+
+#include <stddef.h>
+
+const char *KernAux_Multiboot2_Header_Arch_to_str(
+    const enum KernAux_Multiboot2_Header_Arch arch
+) {
+    switch (arch) {
+    case KERNAUX_MULTIBOOT2_HEADER_ARCH_I386:
+        return "i386";
+    case KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32:
+        return "MIPS32"; // TODO: specify this architecture in README
+    default:
+        return NULL;
+    }
+}
+
+const char *KernAux_Multiboot2_HTag_to_str(
+    const enum KernAux_Multiboot2_HTag tag_type
+) {
+    switch (tag_type) {
+    case KERNAUX_MULTIBOOT2_HTAG_NONE:
+        return "none";
+    case KERNAUX_MULTIBOOT2_HTAG_INFO_REQ:
+        return "information request";
+    case KERNAUX_MULTIBOOT2_HTAG_ADDR:
+        return "address";
+    case KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR:
+        return "entry address";
+    case KERNAUX_MULTIBOOT2_HTAG_FLAGS:
+        return "flags";
+    case KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER:
+        return "framebuffer";
+    case KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN:
+        return "module alignment";
+    case KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES:
+        return "EFI boot services";
+    case KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR:
+        return "EFI i386 entry address";
+    case KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR:
+        return "EFI amd64 entry address";
+    case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER:
+        return "relocatable header";
+    default:
+        return NULL;
+    }
+}
+
+const char *KernAux_Multiboot2_ITag_to_str(
+    const enum KernAux_Multiboot2_ITag tag_type
+) {
+    switch (tag_type) {
+    case KERNAUX_MULTIBOOT2_ITAG_NONE:
+        return "none";
+    case KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE:
+        return "boot cmd line";
+    case KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME:
+        return "boot loader name";
+    case KERNAUX_MULTIBOOT2_ITAG_MODULE:
+        return "module";
+    case KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO:
+        return "basic memory info";
+    case KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE:
+        return "BIOS boot device";
+    case KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP:
+        return "memory map";
+    case KERNAUX_MULTIBOOT2_ITAG_VBE_INFO:
+        return "VBE info";
+    case KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO:
+        return "framebuffer info";
+    case KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS:
+        return "ELF symbols";
+    case KERNAUX_MULTIBOOT2_ITAG_APM_TABLE:
+        return "APM table";
+    case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR:
+        return "EFI 32bit system table ptr";
+    case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR:
+        return "EFI 64bit system table ptr";
+    case KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES:
+        return "SMBIOS tables";
+    case KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP:
+        return "ACPI old RSDP";
+    case KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP:
+        return "ACPI new RSDP";
+    case KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO:
+        return "networking info";
+    case KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP:
+        return "EFI memory map";
+    case KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED:
+        return "EFI boot services not terminated";
+    case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR:
+        return "EFI 32bit image handle ptr";
+    case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR:
+        return "EFI 64bit image handle ptr";
+    case KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR:
+        return "image load base phys addr";
+    default:
+        return NULL;
+    }
+}
+
+const char *KernAux_Multiboot2_HTag_RelocatableHeader_Preference_to_str(
+    const enum KernAux_Multiboot2_HTag_RelocatableHeader_Preference pref
+) {
+    switch (pref) {
+    case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_NONE:
+        return "none";
+    case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_LOWEST:
+        return "lowest";
+    case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_HIGHEST:
+        return "highest";
+    default:
+        return NULL;
+    }
+}
diff --git a/src/multiboot2/header_helpers.c b/src/multiboot2/header_helpers.c
new file mode 100644
index 00000000..92ae2b51
--- /dev/null
+++ b/src/multiboot2/header_helpers.c
@@ -0,0 +1,53 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kernaux/multiboot2.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+const struct KernAux_Multiboot2_HTagBase
+*KernAux_Multiboot2_Header_first_tag_with_type(
+    const struct KernAux_Multiboot2_Header *const multiboot2_header,
+    const enum KernAux_Multiboot2_HTag tag_type
+) {
+    const struct KernAux_Multiboot2_HTagBase *tag_base =
+        (struct KernAux_Multiboot2_HTagBase*)
+        KERNAUX_MULTIBOOT2_DATA(multiboot2_header);
+
+    while (tag_base <
+           (struct KernAux_Multiboot2_HTagBase*)
+           ((uint8_t*)multiboot2_header + multiboot2_header->total_size))
+    {
+        if (!KernAux_Multiboot2_HTagBase_is_valid(tag_base)) return NULL;
+        if (tag_base->type == tag_type) return tag_base;
+
+        tag_base = KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base);
+    }
+
+    return NULL;
+}
+
+const struct KernAux_Multiboot2_HTagBase
+*KernAux_Multiboot2_Header_tag_with_type_after(
+    const struct KernAux_Multiboot2_Header *const multiboot2_header,
+    const enum KernAux_Multiboot2_HTag tag_type,
+    const struct KernAux_Multiboot2_HTagBase *const after_tag
+) {
+    const struct KernAux_Multiboot2_HTagBase *tag_base =
+        (struct KernAux_Multiboot2_HTagBase*)
+        KERNAUX_MULTIBOOT2_DATA(multiboot2_header);
+
+    while (tag_base <
+           (struct KernAux_Multiboot2_HTagBase*)
+           ((uint8_t*)multiboot2_header + multiboot2_header->total_size))
+    {
+        if (!KernAux_Multiboot2_HTagBase_is_valid(tag_base)) return NULL;
+        if (tag_base->type == tag_type && tag_base > after_tag) return tag_base;
+
+        tag_base = KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base);
+    }
+
+    return NULL;
+}
diff --git a/src/multiboot2/header_is_valid.c b/src/multiboot2/header_is_valid.c
new file mode 100644
index 00000000..6930c95a
--- /dev/null
+++ b/src/multiboot2/header_is_valid.c
@@ -0,0 +1,248 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kernaux/multiboot2.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+bool KernAux_Multiboot2_Header_is_valid(
+    const struct KernAux_Multiboot2_Header *const multiboot2_header
+) {
+    if (multiboot2_header->magic != KERNAUX_MULTIBOOT2_HEADER_MAGIC) {
+        return false;
+    }
+
+    if (multiboot2_header->arch != KERNAUX_MULTIBOOT2_HEADER_ARCH_I386 &&
+        multiboot2_header->arch != KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32)
+    {
+        return false;
+    }
+
+    if (multiboot2_header->total_size <
+        sizeof(struct KernAux_Multiboot2_Header) +
+        sizeof(struct KernAux_Multiboot2_HTag_None))
+    {
+        return false;
+    }
+
+    if (multiboot2_header->total_size % KERNAUX_MULTIBOOT2_TAG_ALIGN != 0) {
+        return false;
+    }
+
+    if (multiboot2_header->checksum !=
+        KERNAUX_MULTIBOOT2_HEADER_CHECKSUM(
+            multiboot2_header->arch,
+            multiboot2_header->total_size
+        )
+    ) {
+        return false;
+    }
+
+    const struct KernAux_Multiboot2_HTagBase *tag_base =
+        (struct KernAux_Multiboot2_HTagBase*)
+        KERNAUX_MULTIBOOT2_DATA(multiboot2_header);
+
+    const struct KernAux_Multiboot2_HTagBase *none_tag_base = NULL;
+
+    while (tag_base <
+           (struct KernAux_Multiboot2_HTagBase*)
+           ((uint8_t*)multiboot2_header + multiboot2_header->total_size))
+    {
+        if (!KernAux_Multiboot2_HTagBase_is_valid(tag_base)) return false;
+
+        if (tag_base->type == KERNAUX_MULTIBOOT2_HTAG_NONE &&
+            none_tag_base == NULL
+        ) {
+            none_tag_base = tag_base;
+        }
+
+        tag_base = KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base);
+    }
+
+    if (tag_base !=
+        (struct KernAux_Multiboot2_HTagBase*)
+        ((uint8_t*)multiboot2_header + multiboot2_header->total_size))
+    {
+        return false;
+    }
+
+    if (none_tag_base !=
+        (struct KernAux_Multiboot2_HTagBase*)
+        ((uint8_t*)tag_base -
+         sizeof(struct KernAux_Multiboot2_HTag_None)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool KernAux_Multiboot2_HTagBase_is_valid(
+    const struct KernAux_Multiboot2_HTagBase *tag_base
+) {
+    switch (tag_base->type) {
+    case KERNAUX_MULTIBOOT2_HTAG_NONE:
+        return KernAux_Multiboot2_HTag_None_is_valid(
+            (struct KernAux_Multiboot2_HTag_None*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_INFO_REQ:
+        return KernAux_Multiboot2_HTag_InfoReq_is_valid(
+            (struct KernAux_Multiboot2_HTag_InfoReq*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_ADDR:
+        return KernAux_Multiboot2_HTag_Addr_is_valid(
+            (struct KernAux_Multiboot2_HTag_Addr*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR:
+        return KernAux_Multiboot2_HTag_EntryAddr_is_valid(
+            (struct KernAux_Multiboot2_HTag_EntryAddr*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_FLAGS:
+        return KernAux_Multiboot2_HTag_Flags_is_valid(
+            (struct KernAux_Multiboot2_HTag_Flags*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER:
+        return KernAux_Multiboot2_HTag_Framebuffer_is_valid(
+            (struct KernAux_Multiboot2_HTag_Framebuffer*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN:
+        return KernAux_Multiboot2_HTag_ModuleAlign_is_valid(
+            (struct KernAux_Multiboot2_HTag_ModuleAlign*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES:
+        return KernAux_Multiboot2_HTag_EFIBootServices_is_valid(
+            (struct KernAux_Multiboot2_HTag_EFIBootServices*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR:
+        return KernAux_Multiboot2_HTag_EFII386EntryAddr_is_valid(
+            (struct KernAux_Multiboot2_HTag_EFII386EntryAddr*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR:
+        return KernAux_Multiboot2_HTag_EFIAmd64EntryAddr_is_valid(
+            (struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr*)tag_base
+        );
+    case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER:
+        return KernAux_Multiboot2_HTag_RelocatableHeader_is_valid(
+            (struct KernAux_Multiboot2_HTag_RelocatableHeader*)tag_base
+        );
+    default:
+        return false;
+    }
+}
+
+bool KernAux_Multiboot2_HTag_None_is_valid(
+    const struct KernAux_Multiboot2_HTag_None *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_NONE &&
+        tag->base.size == 8
+    );
+}
+
+bool KernAux_Multiboot2_HTag_InfoReq_is_valid(
+    const struct KernAux_Multiboot2_HTag_InfoReq *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_INFO_REQ &&
+        tag->base.size > 8 &&
+        tag->base.size % 4 == 0
+    );
+}
+
+bool KernAux_Multiboot2_HTag_Addr_is_valid(
+    const struct KernAux_Multiboot2_HTag_Addr *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_ADDR &&
+        tag->base.size == 24
+    );
+}
+
+bool KernAux_Multiboot2_HTag_EntryAddr_is_valid(
+    const struct KernAux_Multiboot2_HTag_EntryAddr *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR &&
+        tag->base.size == 12
+    );
+}
+
+bool KernAux_Multiboot2_HTag_Flags_is_valid(
+    const struct KernAux_Multiboot2_HTag_Flags *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_FLAGS &&
+        tag->base.size == 12
+    );
+}
+
+bool KernAux_Multiboot2_HTag_Framebuffer_is_valid(
+    const struct KernAux_Multiboot2_HTag_Framebuffer *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER &&
+        tag->base.size == 20
+    );
+}
+
+bool KernAux_Multiboot2_HTag_ModuleAlign_is_valid(
+    const struct KernAux_Multiboot2_HTag_ModuleAlign *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN &&
+        tag->base.size == 8
+    );
+}
+
+bool KernAux_Multiboot2_HTag_EFIBootServices_is_valid(
+    const struct KernAux_Multiboot2_HTag_EFIBootServices *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES &&
+        tag->base.size == 8
+    );
+}
+
+bool KernAux_Multiboot2_HTag_EFII386EntryAddr_is_valid(
+    const struct KernAux_Multiboot2_HTag_EFII386EntryAddr *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR &&
+        tag->base.size == 12
+    );
+}
+
+bool KernAux_Multiboot2_HTag_EFIAmd64EntryAddr_is_valid(
+    const struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr *tag
+) {
+    return (
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR &&
+        tag->base.size == 12
+    );
+}
+
+bool KernAux_Multiboot2_HTag_RelocatableHeader_is_valid(
+    const struct KernAux_Multiboot2_HTag_RelocatableHeader *tag
+) {
+    if (!(
+        tag->base.type == KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER &&
+        tag->base.size == 24 &&
+        tag->min_addr <= tag->max_addr
+        // TODO: additional requirements?
+    )) {
+        return false;
+    }
+
+    switch (tag->preferences) {
+    case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_NONE:    break;
+    case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_LOWEST:  break;
+    case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_HIGHEST: break;
+    default:
+        return false;
+    }
+
+    return true;
+}
diff --git a/src/multiboot2/header_print.c b/src/multiboot2/header_print.c
new file mode 100644
index 00000000..3fd31c0e
--- /dev/null
+++ b/src/multiboot2/header_print.c
@@ -0,0 +1,111 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kernaux/multiboot2.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+void KernAux_Multiboot2_Header_print(
+    const struct KernAux_Multiboot2_Header *const multiboot2_header,
+    void (*const printf)(const char *format, ...) __attribute__((format(printf, 1, 2)))
+) {
+    printf("Multiboot 2 header\n");
+    printf("  magic: %u\n", multiboot2_header->magic);
+    printf(
+        "  arch: %u (%s)\n",
+        multiboot2_header->arch,
+        KernAux_Multiboot2_Header_Arch_to_str(multiboot2_header->arch)
+    );
+    printf("  size: %u\n", multiboot2_header->total_size);
+    printf("  checksum: %u\n", multiboot2_header->checksum);
+
+    const struct KernAux_Multiboot2_HTagBase *tag_base =
+        (struct KernAux_Multiboot2_HTagBase*)
+        KERNAUX_MULTIBOOT2_DATA(multiboot2_header);
+
+    while (tag_base <
+           (struct KernAux_Multiboot2_HTagBase*)
+           ((uint8_t*)multiboot2_header + multiboot2_header->total_size))
+    {
+        if (!KernAux_Multiboot2_HTagBase_is_valid(tag_base)) return;
+
+        KernAux_Multiboot2_HTagBase_print(tag_base, printf);
+
+        tag_base = KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base);
+    }
+}
+
+void KernAux_Multiboot2_HTagBase_print(
+    const struct KernAux_Multiboot2_HTagBase *const tag_base,
+    void (*const printf)(const char *format, ...) __attribute__((format(printf, 1, 2)))
+) {
+    if (!KernAux_Multiboot2_HTagBase_is_valid(tag_base)) return;
+
+    printf("Multiboot 2 header tag\n");
+
+    printf(
+        "  type: %u (%s)\n",
+        tag_base->type,
+        KernAux_Multiboot2_HTag_to_str(tag_base->type)
+    );
+
+    printf("  flags: %u\n", tag_base->flags);
+    printf("  size: %u\n", tag_base->size);
+
+    switch (tag_base->type) {
+    case KERNAUX_MULTIBOOT2_HTAG_NONE:
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_INFO_REQ:
+        {
+            // TODO: print
+        }
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_ADDR:
+        {
+            // TODO: print
+        }
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR:
+        {
+            // TODO: print
+        }
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_FLAGS:
+        {
+            // TODO: print
+        }
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER:
+        {
+            // TODO: print
+        }
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN:
+        {
+            // TODO: print
+        }
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES:
+        {
+            // TODO: print
+        }
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR:
+        {
+            // TODO: print
+        }
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR:
+        {
+            // TODO: print
+        }
+        break;
+    case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER:
+        {
+            // TODO: print
+        }
+        break;
+    }
+}
diff --git a/src/multiboot2/info_helpers.c b/src/multiboot2/info_helpers.c
index e8ad8ccc..2531a5ff 100644
--- a/src/multiboot2/info_helpers.c
+++ b/src/multiboot2/info_helpers.c
@@ -5,6 +5,7 @@
 #include <kernaux/multiboot2.h>
 
 #include <stddef.h>
+#include <stdint.h>
 
 const struct KernAux_Multiboot2_ITagBase
 *KernAux_Multiboot2_Info_first_tag_with_type(
@@ -17,14 +18,12 @@ const struct KernAux_Multiboot2_ITagBase
 
     while (tag_base <
            (struct KernAux_Multiboot2_ITagBase*)
-           ((unsigned char*)multiboot2_info + multiboot2_info->total_size))
+           ((uint8_t*)multiboot2_info + multiboot2_info->total_size))
     {
         if (!KernAux_Multiboot2_ITagBase_is_valid(tag_base)) return NULL;
         if (tag_base->type == tag_type) return tag_base;
 
-        tag_base = (struct KernAux_Multiboot2_ITagBase*)(
-            (unsigned char*)tag_base + ((tag_base->size + 7) & ~7)
-        );
+        tag_base = KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base);
     }
 
     return NULL;
@@ -42,14 +41,12 @@ const struct KernAux_Multiboot2_ITagBase
 
     while (tag_base <
            (struct KernAux_Multiboot2_ITagBase*)
-           (unsigned char*)multiboot2_info + multiboot2_info->total_size)
+           ((uint8_t*)multiboot2_info + multiboot2_info->total_size))
     {
         if (!KernAux_Multiboot2_ITagBase_is_valid(tag_base)) return NULL;
         if (tag_base->type == tag_type && tag_base > after_tag) return tag_base;
 
-        tag_base = (struct KernAux_Multiboot2_ITagBase*)(
-            (unsigned char*)tag_base + ((tag_base->size + 7) & ~7)
-        );
+        tag_base = KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base);
     }
 
     return NULL;
diff --git a/src/multiboot2/info_is_valid.c b/src/multiboot2/info_is_valid.c
index e0c954dd..36ce1c7b 100644
--- a/src/multiboot2/info_is_valid.c
+++ b/src/multiboot2/info_is_valid.c
@@ -4,12 +4,23 @@
 
 #include <kernaux/multiboot2.h>
 
+#include <stdbool.h>
 #include <stddef.h>
+#include <stdint.h>
 
 bool KernAux_Multiboot2_Info_is_valid(
     const struct KernAux_Multiboot2_Info *const multiboot2_info
 ) {
-    if (multiboot2_info->total_size <= 8) return false;
+    if (multiboot2_info->total_size <
+        sizeof(struct KernAux_Multiboot2_Info) +
+        sizeof(struct KernAux_Multiboot2_ITag_None))
+    {
+        return false;
+    }
+
+    if (multiboot2_info->total_size % KERNAUX_MULTIBOOT2_TAG_ALIGN != 0) {
+        return false;
+    }
 
     const struct KernAux_Multiboot2_ITagBase *tag_base =
         (struct KernAux_Multiboot2_ITagBase*)
@@ -19,7 +30,7 @@ bool KernAux_Multiboot2_Info_is_valid(
 
     while (tag_base <
            (struct KernAux_Multiboot2_ITagBase*)
-           ((unsigned char*)multiboot2_info + multiboot2_info->total_size))
+           ((uint8_t*)multiboot2_info + multiboot2_info->total_size))
     {
         if (!KernAux_Multiboot2_ITagBase_is_valid(tag_base)) return false;
 
@@ -29,21 +40,19 @@ bool KernAux_Multiboot2_Info_is_valid(
             none_tag_base = tag_base;
         }
 
-        tag_base = (struct KernAux_Multiboot2_ITagBase*)(
-            (unsigned char*)tag_base + ((tag_base->size + 7) & ~7)
-        );
+        tag_base = KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base);
     }
 
     if (tag_base !=
         (struct KernAux_Multiboot2_ITagBase*)
-        ((unsigned char*)multiboot2_info + multiboot2_info->total_size))
+        ((uint8_t*)multiboot2_info + multiboot2_info->total_size))
     {
         return false;
     }
 
     if (none_tag_base !=
         (struct KernAux_Multiboot2_ITagBase*)
-        ((unsigned char*)tag_base -
+        ((uint8_t*)tag_base -
          sizeof(struct KernAux_Multiboot2_ITag_None)))
     {
         return false;
diff --git a/src/multiboot2/info_print.c b/src/multiboot2/info_print.c
index d978afbd..d39325ae 100644
--- a/src/multiboot2/info_print.c
+++ b/src/multiboot2/info_print.c
@@ -5,10 +5,7 @@
 #include <kernaux/multiboot2.h>
 
 #include <stddef.h>
-
-static const char *KernAux_Multiboot2_ITag_to_str(
-    enum KernAux_Multiboot2_ITag tag_type
-);
+#include <stdint.h>
 
 static void KernAux_Multiboot2_ITag_MemoryMap_print(
     const struct KernAux_Multiboot2_ITag_MemoryMap *tag,
@@ -20,59 +17,6 @@ static void KernAux_Multiboot2_ITag_ELFSymbols_print(
     void (*printf)(const char *format, ...) __attribute__((format(printf, 1, 2)))
 );
 
-const char *KernAux_Multiboot2_ITag_to_str(
-    enum KernAux_Multiboot2_ITag tag_type
-) {
-    switch (tag_type) {
-    case KERNAUX_MULTIBOOT2_ITAG_NONE:
-        return "none";
-    case KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE:
-        return "boot cmd line";
-    case KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME:
-        return "boot loader name";
-    case KERNAUX_MULTIBOOT2_ITAG_MODULE:
-        return "module";
-    case KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO:
-        return "basic memory info";
-    case KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE:
-        return "BIOS boot device";
-    case KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP:
-        return "memory map";
-    case KERNAUX_MULTIBOOT2_ITAG_VBE_INFO:
-        return "VBE info";
-    case KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO:
-        return "framebuffer info";
-    case KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS:
-        return "ELF symbols";
-    case KERNAUX_MULTIBOOT2_ITAG_APM_TABLE:
-        return "APM table";
-    case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR:
-        return "EFI 32bit system table ptr";
-    case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR:
-        return "EFI 64bit system table ptr";
-    case KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES:
-        return "SMBIOS tables";
-    case KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP:
-        return "ACPI old RSDP";
-    case KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP:
-        return "ACPI new RSDP";
-    case KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO:
-        return "networking info";
-    case KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP:
-        return "EFI memory map";
-    case KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED:
-        return "EFI boot services not terminated";
-    case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR:
-        return "EFI 32bit image handle ptr";
-    case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR:
-        return "EFI 64bit image handle ptr";
-    case KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR:
-        return "image load base phys addr";
-    default:
-        return NULL;
-    }
-}
-
 void KernAux_Multiboot2_Info_print(
     const struct KernAux_Multiboot2_Info *const multiboot2_info,
     void (*const printf)(const char *format, ...) __attribute__((format(printf, 1, 2)))
@@ -87,15 +31,13 @@ void KernAux_Multiboot2_Info_print(
 
     while (tag_base <
            (struct KernAux_Multiboot2_ITagBase*)
-           ((unsigned char*)multiboot2_info + multiboot2_info->total_size))
+           ((uint8_t*)multiboot2_info + multiboot2_info->total_size))
     {
         if (!KernAux_Multiboot2_ITagBase_is_valid(tag_base)) return;
 
         KernAux_Multiboot2_ITagBase_print(tag_base, printf);
 
-        tag_base = (struct KernAux_Multiboot2_ITagBase*)(
-            (unsigned char*)tag_base + ((tag_base->size + 7) & ~7)
-        );
+        tag_base = KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base);
     }
 }
 
@@ -105,7 +47,7 @@ void KernAux_Multiboot2_ITagBase_print(
 ) {
     if (!KernAux_Multiboot2_ITagBase_is_valid(tag_base)) return;
 
-    printf("Multiboot 2 tag\n");
+    printf("Multiboot 2 info tag\n");
 
     printf(
         "  type: %u (%s)\n",


Test patch:
diff --git a/tests/multiboot2_header_example1.h b/tests/multiboot2_header_example1.h
new file mode 100644
index 00000000..93cdc036
--- /dev/null
+++ b/tests/multiboot2_header_example1.h
@@ -0,0 +1,21 @@
+static const struct {
+    struct KernAux_Multiboot2_Header multiboot2_header;
+    struct KernAux_Multiboot2_HTag_None tag_none;
+} multiboot2_header_example1 = {
+    .multiboot2_header = {
+        .magic = KERNAUX_MULTIBOOT2_HEADER_MAGIC,
+        .arch = KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32,
+        .total_size = sizeof(multiboot2_header_example1),
+        .checksum = KERNAUX_MULTIBOOT2_HEADER_CHECKSUM(
+            KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32,
+            sizeof(multiboot2_header_example1)
+        ),
+    },
+    .tag_none = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_NONE,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example1.tag_none),
+        },
+    },
+};
diff --git a/tests/multiboot2_header_example2.h b/tests/multiboot2_header_example2.h
new file mode 100644
index 00000000..80cdf01f
--- /dev/null
+++ b/tests/multiboot2_header_example2.h
@@ -0,0 +1,165 @@
+static const struct {
+    struct KernAux_Multiboot2_Header multiboot2_header;
+
+    struct {
+        struct KernAux_Multiboot2_HTag_InfoReq tag;
+        enum KernAux_Multiboot2_ITag mbi_tag_types[22];
+    } tag_info_req;
+
+    struct KernAux_Multiboot2_HTag_Addr tag_addr;
+
+    struct KernAux_Multiboot2_HTag_EntryAddr tag_entry_addr;
+    uint8_t _align1[4];
+
+    struct KernAux_Multiboot2_HTag_Flags tag_flags;
+    uint8_t _align2[4];
+
+    struct KernAux_Multiboot2_HTag_Framebuffer tag_framebuffer;
+    uint8_t _align3[4];
+
+    struct KernAux_Multiboot2_HTag_ModuleAlign tag_module_align;
+
+    struct KernAux_Multiboot2_HTag_EFIBootServices tag_efi_boot_services;
+
+    struct KernAux_Multiboot2_HTag_EFII386EntryAddr tag_efi_i386_entry_addr;
+    uint8_t _align4[4];
+
+    struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr tag_efi_amd64_entry_addr;
+    uint8_t _align5[4];
+
+    struct KernAux_Multiboot2_HTag_RelocatableHeader tag_relocatable_header;
+
+    struct KernAux_Multiboot2_HTag_None tag_none;
+} multiboot2_header_example2 = {
+    .multiboot2_header = {
+        .magic = KERNAUX_MULTIBOOT2_HEADER_MAGIC,
+        .arch = KERNAUX_MULTIBOOT2_HEADER_ARCH_I386,
+        .total_size = sizeof(multiboot2_header_example2),
+        .checksum = KERNAUX_MULTIBOOT2_HEADER_CHECKSUM(
+            KERNAUX_MULTIBOOT2_HEADER_ARCH_I386,
+            sizeof(multiboot2_header_example2)
+        ),
+    },
+    .tag_info_req = {
+        .tag = {
+            .base = {
+                .type = KERNAUX_MULTIBOOT2_HTAG_INFO_REQ,
+                .flags = 0,
+                .size = sizeof(multiboot2_header_example2.tag_info_req),
+            },
+        },
+        .mbi_tag_types = {
+            KERNAUX_MULTIBOOT2_ITAG_NONE,
+            KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE,
+            KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME,
+            KERNAUX_MULTIBOOT2_ITAG_MODULE,
+            KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO,
+            KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE,
+            KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP,
+            KERNAUX_MULTIBOOT2_ITAG_VBE_INFO,
+            KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO,
+            KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS,
+            KERNAUX_MULTIBOOT2_ITAG_APM_TABLE,
+            KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR,
+            KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR,
+            KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES,
+            KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP,
+            KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP,
+            KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO,
+            KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP,
+            KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED,
+            KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR,
+            KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR,
+            KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR,
+        },
+    },
+    .tag_addr = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_ADDR,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_addr),
+        },
+        .header_addr = 0,
+        .load_addr = 0,
+        .load_end_addr = 0,
+        .bss_end_addr = 0,
+    },
+    .tag_entry_addr = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_entry_addr),
+        },
+        .entry_addr = 0,
+    },
+    .tag_flags = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_FLAGS,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_flags),
+        },
+        .console_flags = (
+            KERNAUX_MULTIBOOT2_HTAG_FLAGS_REQUIRE_CONSOLE |
+            KERNAUX_MULTIBOOT2_HTAG_FLAGS_EGA_SUPPORT
+        ),
+    },
+    .tag_framebuffer = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_framebuffer),
+        },
+        .width = 0,
+        .height = 0,
+        .depth = 0,
+    },
+    .tag_module_align = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_module_align),
+        },
+    },
+    .tag_efi_boot_services = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_efi_boot_services),
+        },
+    },
+    .tag_efi_i386_entry_addr = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_efi_i386_entry_addr),
+        },
+        .entry_addr = 0,
+    },
+    .tag_efi_amd64_entry_addr = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_efi_amd64_entry_addr),
+        },
+        .entry_addr = 0,
+    },
+    .tag_relocatable_header = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_relocatable_header),
+        },
+        .min_addr = 0,
+        .max_addr = 0,
+        .align = 0,
+        .preferences =
+            KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_LOWEST,
+    },
+    .tag_none = {
+        .base = {
+            .type = KERNAUX_MULTIBOOT2_HTAG_NONE,
+            .flags = 0,
+            .size = sizeof(multiboot2_header_example2.tag_none),
+        },
+    },
+};
diff --git a/tests/multiboot2_header_print1.c b/tests/multiboot2_header_print1.c
new file mode 100644
index 00000000..b13407cc
--- /dev/null
+++ b/tests/multiboot2_header_print1.c
@@ -0,0 +1,24 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kernaux/multiboot2.h>
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "multiboot2_header_example1.h"
+
+int main()
+{
+    assert(KernAux_Multiboot2_Header_is_valid(
+        (struct KernAux_Multiboot2_Header*)&multiboot2_header_example1
+    ));
+
+    KernAux_Multiboot2_Header_print(
+        (struct KernAux_Multiboot2_Header*)&multiboot2_header_example1,
+        (void (*)(const char *format, ...))printf
+    );
+
+    return 0;
+}
diff --git a/tests/multiboot2_header_print2.c b/tests/multiboot2_header_print2.c
new file mode 100644
index 00000000..4c13b286
--- /dev/null
+++ b/tests/multiboot2_header_print2.c
@@ -0,0 +1,24 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kernaux/multiboot2.h>
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "multiboot2_header_example2.h"
+
+int main()
+{
+    assert(KernAux_Multiboot2_Header_is_valid(
+        &multiboot2_header_example2.multiboot2_header
+    ));
+
+    KernAux_Multiboot2_Header_print(
+        &multiboot2_header_example2.multiboot2_header,
+        (void (*)(const char *format, ...))printf
+    );
+
+    return 0;
+}
diff --git a/tests/multiboot2_info_example1.h b/tests/multiboot2_info_example1.h
index a3078324..62c4b66f 100644
--- a/tests/multiboot2_info_example1.h
+++ b/tests/multiboot2_info_example1.h
@@ -1,4 +1,4 @@
-static const unsigned char multiboot2_info_example1[864] = {
+static const uint8_t multiboot2_info_example1[864] = {
     96, 3, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 12, 0, 0, 0,
     0, 0, 64, 0, 0, 0, 232, 133, 1, 0, 0, 0, 21, 0, 0, 0,
     104, 101, 108, 108, 111, 32, 107, 101, 114, 110, 101, 108, 0, 139, 69, 228,
diff --git a/tests/multiboot2_info_example2.h b/tests/multiboot2_info_example2.h
index bf0b2aa3..dc18006d 100644
--- a/tests/multiboot2_info_example2.h
+++ b/tests/multiboot2_info_example2.h
@@ -5,111 +5,100 @@ static const struct {
         struct KernAux_Multiboot2_ITag_BootCmdLine tag;
         char cmdline[15];
     } tag_boot_cmd_line;
-
-    unsigned char _align1[1];
+    uint8_t _align1[1];
 
     struct {
         struct KernAux_Multiboot2_ITag_BootLoaderName tag;
         char name[22];
     } tag_boot_loader_name;
-
-    unsigned char _align2[2];
+    uint8_t _align2[2];
 
     struct {
         struct KernAux_Multiboot2_ITag_Module tag;
         char cmdline[17];
     } tag_module1;
-
-    unsigned char _align3[7];
+    uint8_t _align3[7];
 
     struct {
         struct KernAux_Multiboot2_ITag_Module tag;
         char cmdline[17];
     } tag_module2;
-
-    unsigned char _align4[7];
+    uint8_t _align4[7];
 
     struct KernAux_Multiboot2_ITag_BasicMemoryInfo tag_basic_memory_info;
 
     struct KernAux_Multiboot2_ITag_BIOSBootDevice tag_bios_boot_device;
-
-    unsigned char _align5[4];
+    uint8_t _align5[4];
 
     struct {
         struct KernAux_Multiboot2_ITag_MemoryMap tag;
 
-        unsigned char
-            data[160 - sizeof(struct KernAux_Multiboot2_ITag_MemoryMap)];
+        uint8_t data[160 - sizeof(struct KernAux_Multiboot2_ITag_MemoryMap)];
     } tag_memory_map;
 
     struct KernAux_Multiboot2_ITag_VBEInfo tag_vbe_info;
 
     struct {
         struct KernAux_Multiboot2_ITag_FramebufferInfo tag;
-        unsigned char data[8];
+        uint8_t data[8];
     } tag_framebuffer_info;
-
-    unsigned char _align6[1];
+    uint8_t _align6[1];
 
     struct {
         struct KernAux_Multiboot2_ITag_ELFSymbols tag;
 
-        unsigned char
-            data[420 - sizeof(struct KernAux_Multiboot2_ITag_ELFSymbols)];
+        uint8_t data[420 - sizeof(struct KernAux_Multiboot2_ITag_ELFSymbols)];
     } tag_elf_symbols;
-
-    unsigned char _align7[4];
+    uint8_t _align7[4];
 
     struct KernAux_Multiboot2_ITag_APMTable tag_apm_table;
+    uint8_t _align8[4];
 
-    unsigned char _align8[4];
-
-    struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr tag_efi_32bit_system_table_ptr;
-
-    unsigned char _align9[4];
+    struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr
+        tag_efi_32bit_system_table_ptr;
+    uint8_t _align9[4];
 
-    struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr tag_efi_64bit_system_table_ptr;
+    struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr
+        tag_efi_64bit_system_table_ptr;
 
     struct {
         struct KernAux_Multiboot2_ITag_SMBIOSTables tag;
-        unsigned char data[8];
+        uint8_t data[8];
     } tag_smbios_tables;
 
     struct {
         struct KernAux_Multiboot2_ITag_ACPIOldRSDP tag;
-        unsigned char data[8];
+        uint8_t data[8];
     } tag_acpi_old_rsdp;
 
     struct {
         struct KernAux_Multiboot2_ITag_ACPINewRSDP tag;
-        unsigned char data[8];
+        uint8_t data[8];
     } tag_acpi_new_rsdp;
 
     struct {
         struct KernAux_Multiboot2_ITag_NetworkingInfo tag;
-        unsigned char data[8];
+        uint8_t data[8];
     } tag_networking_info;
 
     struct {
         struct KernAux_Multiboot2_ITag_EFIMemoryMap tag;
-        unsigned char data[8];
+        uint8_t data[8];
     } tag_efi_memory_map;
 
     struct KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated
-    tag_efi_boot_services_not_terminated;
+        tag_efi_boot_services_not_terminated;
 
     struct KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr
-    tag_efi_32bit_image_handle_ptr;
-
-    unsigned char _align10[4];
+        tag_efi_32bit_image_handle_ptr;
+    uint8_t _align10[4];
 
     struct KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr
-    tag_efi_64bit_image_handle_ptr;
+        tag_efi_64bit_image_handle_ptr;
 
     struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr
-    tag_image_load_base_phys_addr;
-
-    unsigned char _align11[4];
+        tag_image_load_base_phys_addr;
+    uint8_t _align11[4];
 
     struct KernAux_Multiboot2_ITag_None tag_none;
 } multiboot2_info_example2 = {
diff --git a/tests/test_multiboot2_header_helpers.c b/tests/test_multiboot2_header_helpers.c
new file mode 100644
index 00000000..f2320b4e
--- /dev/null
+++ b/tests/test_multiboot2_header_helpers.c
@@ -0,0 +1,14 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kernaux/multiboot2.h>
+
+#include "multiboot2_header_example1.h"
+#include "multiboot2_header_example2.h"
+
+int main()
+{
+    // TODO: write this
+    return 0;
+}
diff --git a/tests/test_multiboot2_header_print.c b/tests/test_multiboot2_header_print.c
new file mode 100644
index 00000000..fc4af8fa
--- /dev/null
+++ b/tests/test_multiboot2_header_print.c
@@ -0,0 +1,99 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+
+#define __USE_POSIX2
+#include <stdio.h>
+
+static const char output1[] =
+    "Multiboot 2 header\n"
+    "  magic: 3897708758\n"
+    "  arch: 4 (MIPS32)\n"
+    "  size: 24\n"
+    "  checksum: 397258510\n"
+    "Multiboot 2 header tag\n"
+    "  type: 0 (none)\n"
+    "  flags: 0\n"
+    "  size: 8\n";
+
+static const char output2[] =
+    "Multiboot 2 header\n"
+    "  magic: 3897708758\n"
+    "  arch: 0 (i386)\n"
+    "  size: 272\n"
+    "  checksum: 397258266\n"
+    "Multiboot 2 header tag\n"
+    "  type: 1 (information request)\n"
+    "  flags: 0\n"
+    "  size: 96\n"
+    "Multiboot 2 header tag\n"
+    "  type: 2 (address)\n"
+    "  flags: 0\n"
+    "  size: 24\n"
+    "Multiboot 2 header tag\n"
+    "  type: 3 (entry address)\n"
+    "  flags: 0\n"
+    "  size: 12\n"
+    "Multiboot 2 header tag\n"
+    "  type: 4 (flags)\n"
+    "  flags: 0\n"
+    "  size: 12\n"
+    "Multiboot 2 header tag\n"
+    "  type: 5 (framebuffer)\n"
+    "  flags: 0\n"
+    "  size: 20\n"
+    "Multiboot 2 header tag\n"
+    "  type: 6 (module alignment)\n"
+    "  flags: 0\n"
+    "  size: 8\n"
+    "Multiboot 2 header tag\n"
+    "  type: 7 (EFI boot services)\n"
+    "  flags: 0\n"
+    "  size: 8\n"
+    "Multiboot 2 header tag\n"
+    "  type: 8 (EFI i386 entry address)\n"
+    "  flags: 0\n"
+    "  size: 12\n"
+    "Multiboot 2 header tag\n"
+    "  type: 9 (EFI amd64 entry address)\n"
+    "  flags: 0\n"
+    "  size: 12\n"
+    "Multiboot 2 header tag\n"
+    "  type: 10 (relocatable header)\n"
+    "  flags: 0\n"
+    "  size: 24\n"
+    "Multiboot 2 header tag\n"
+    "  type: 0 (none)\n"
+    "  flags: 0\n"
+    "  size: 8\n";
+
+int main()
+{
+    {
+        FILE *const fd = popen("tests/multiboot2_header_print1", "r");
+        assert(fd != NULL);
+
+        for (const char *ch = output1; *ch; ++ch) {
+            assert(fgetc(fd) == *ch);
+        }
+
+        const int status = pclose(fd);
+        assert(status == 0);
+    }
+
+    {
+        FILE *const fd = popen("tests/multiboot2_header_print2", "r");
+        assert(fd != NULL);
+
+        for (const char *ch = output2; *ch; ++ch) {
+            assert(fgetc(fd) == *ch);
+        }
+
+        const int status = pclose(fd);
+        assert(status == 0);
+    }
+
+    return 0;
+}
diff --git a/tests/test_multiboot2_header_validation.c b/tests/test_multiboot2_header_validation.c
new file mode 100644
index 00000000..30a2860b
--- /dev/null
+++ b/tests/test_multiboot2_header_validation.c
@@ -0,0 +1,9 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+int main()
+{
+    // TODO: write this
+    return 0;
+}
diff --git a/tests/test_multiboot2_info_print.c b/tests/test_multiboot2_info_print.c
index 25d004fc..181675a9 100644
--- a/tests/test_multiboot2_info_print.c
+++ b/tests/test_multiboot2_info_print.c
@@ -11,19 +11,19 @@ static const char output1[] =
     "Multiboot 2 info\n"
     "  size: 864\n"
     "  reserved1: 0\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 21 (image load base phys addr)\n"
     "  size: 12\n"
     "  load base addr: 4194304\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 1 (boot cmd line)\n"
     "  size: 21\n"
     "  cmdline: hello kernel\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 2 (boot loader name)\n"
     "  size: 30\n"
     "  name: GRUB 2.02-2ubuntu8.20\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 10 (APM table)\n"
     "  size: 28\n"
     "  version: 258\n"
@@ -35,19 +35,19 @@ static const char output1[] =
     "  cseg len: 65520\n"
     "  cseg 16 len: 65520\n"
     "  dseg len: 65520\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 3 (module)\n"
     "  size: 29\n"
     "  start: 1056768\n"
     "  end: 1061532\n"
     "  cmdline: hello module\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 3 (module)\n"
     "  size: 17\n"
     "  start: 1064960\n"
     "  end: 1069652\n"
     "  cmdline: \n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 6 (memory map)\n"
     "  size: 160\n"
     "  entry size: 24\n"
@@ -83,25 +83,25 @@ static const char output1[] =
     "      length: 262144\n"
     "      type: 2\n"
     "      reserved1: 0\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 9 (ELF symbols)\n"
     "  size: 420\n"
     "  num: 10\n"
     "  entsize: 0\n"
     "  shndx: 40\n"
     "  reserved1: 0\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 4 (basic memory info)\n"
     "  size: 16\n"
     "  mem lower: 639\n"
     "  mem upper: 129920\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 5 (BIOS boot device)\n"
     "  size: 20\n"
     "  bios dev: 224\n"
     "  partition: 4294967295\n"
     "  sub_partition: 4294967295\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 8 (framebuffer info)\n"
     "  size: 32\n"
     "  framebuffer addr: 753664\n"
@@ -111,10 +111,10 @@ static const char output1[] =
     "  framebuffer bpp: 16\n"
     "  framebuffer type: 2\n"
     "  reserved1: 0\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 14 (ACPI old RSDP)\n"
     "  size: 28\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 0 (none)\n"
     "  size: 8\n";
 
@@ -122,38 +122,38 @@ static const char output2[] =
     "Multiboot 2 info\n"
     "  size: 1816\n"
     "  reserved1: 0\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 1 (boot cmd line)\n"
     "  size: 23\n"
     "  cmdline: Hello, Kernel!\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 2 (boot loader name)\n"
     "  size: 30\n"
     "  name: GRUB 2.02-2ubuntu8.20\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 3 (module)\n"
     "  size: 33\n"
     "  start: 123\n"
     "  end: 456\n"
     "  cmdline: Hello, Module 1!\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 3 (module)\n"
     "  size: 33\n"
     "  start: 123\n"
     "  end: 456\n"
     "  cmdline: Hello, Module 2!\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 4 (basic memory info)\n"
     "  size: 16\n"
     "  mem lower: 123\n"
     "  mem upper: 456\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 5 (BIOS boot device)\n"
     "  size: 20\n"
     "  bios dev: 0\n"
     "  partition: 1\n"
     "  sub_partition: 2\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 6 (memory map)\n"
     "  size: 160\n"
     "  entry size: 24\n"
@@ -189,14 +189,14 @@ static const char output2[] =
     "      length: 262144\n"
     "      type: 2\n"
     "      reserved1: 0\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 7 (VBE info)\n"
     "  size: 784\n"
     "  VBE mode: 0\n"
     "  VBE interface seg: 123\n"
     "  VBE interface off: 456\n"
     "  VBE interface len: 789\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 8 (framebuffer info)\n"
     "  size: 39\n"
     "  framebuffer addr: 123\n"
@@ -206,14 +206,14 @@ static const char output2[] =
     "  framebuffer bpp: 8\n"
     "  framebuffer type: 1\n"
     "  reserved1: 0\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 9 (ELF symbols)\n"
     "  size: 420\n"
     "  num: 10\n"
     "  entsize: 0\n"
     "  shndx: 40\n"
     "  reserved1: 0\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 10 (APM table)\n"
     "  size: 28\n"
     "  version: 0\n"
@@ -225,44 +225,44 @@ static const char output2[] =
     "  cseg len: 456\n"
     "  cseg 16 len: 789\n"
     "  dseg len: 123\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 11 (EFI 32bit system table ptr)\n"
     "  size: 12\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 12 (EFI 64bit system table ptr)\n"
     "  size: 16\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 13 (SMBIOS tables)\n"
     "  size: 24\n"
     "  major: 1\n"
     "  minor: 2\n"
     "  reserved1: {0, 0, 0, 0, 0, 0}\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 14 (ACPI old RSDP)\n"
     "  size: 16\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 15 (ACPI new RSDP)\n"
     "  size: 16\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 16 (networking info)\n"
     "  size: 16\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 17 (EFI memory map)\n"
     "  size: 24\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 18 (EFI boot services not terminated)\n"
     "  size: 8\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 19 (EFI 32bit image handle ptr)\n"
     "  size: 12\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 20 (EFI 64bit image handle ptr)\n"
     "  size: 16\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 21 (image load base phys addr)\n"
     "  size: 12\n"
     "  load base addr: 123\n"
-    "Multiboot 2 tag\n"
+    "Multiboot 2 info tag\n"
     "  type: 0 (none)\n"
     "  size: 8\n";