Skip to main content
Aeolos uses the Stivale2 boot protocol to boot the kernel. This modern boot protocol provides a clean interface between the bootloader and kernel.

Stivale2 Protocol

Stivale2 is a tag-based boot protocol where the kernel and bootloader communicate through linked lists of tagged structures.

Kernel Header

The kernel provides a header structure to the bootloader, located in kernel/boot/stivale2.c:18:
kernel/boot/stivale2.c
static uint8_t kernel_stack[16384] = { 0 };

// framebuffer request tag for bootloader
static stv2_hdr_tag_fb header_fb_tag = {
    .tag = { .identifier = STV2_HDR_TAG_FB_ID, .next = 0 },
    .fb_width = 800,
    .fb_height = 600,
    .fb_bpp = 32
};

// stivale2 header
[[gnu::section(".stivale2hdr"), gnu::used]] static stv2_hdr header = {
    .entry_point = 0,
    .stack = (uintptr_t)kernel_stack + sizeof(kernel_stack),
    .flags = 0,
    .tags = (uint64_t)&header_fb_tag
};
The header is placed in the .stivale2hdr section using GCC attributes. The bootloader searches for this section to find the kernel’s requirements.

Header Components

static stv2_hdr_tag_fb header_fb_tag = {
    .tag = { .identifier = STV2_HDR_TAG_FB_ID, .next = 0 },
    .fb_width = 800,
    .fb_height = 600,
    .fb_bpp = 32
};

Bootloader Information

The bootloader passes information to the kernel through a stivale2_struct and associated tags:
kernel/boot/stivale2.h
struct stivale2_struct {
#define STIVALE2_BOOTLOADER_BRAND_SIZE 64
    char bootloader_brand[STIVALE2_BOOTLOADER_BRAND_SIZE];

#define STIVALE2_BOOTLOADER_VERSION_SIZE 64
    char bootloader_version[STIVALE2_BOOTLOADER_VERSION_SIZE];

    stv2_tag* tags;
} __attribute__((packed));

Finding Tags

The kernel walks the tag linked list to find required information:
kernel/boot/stivale2.c
void* stv2_find_struct_tag(stivale2_struct* s, uint64_t id)
{
    for (void* t = s->tags; t;) {
        stv2_tag* tag = (stv2_tag*)PHYS_TO_VIRT(t);
        if (tag->identifier == id)
            return tag;
        t = tag->next;
    }

    klog_warn("stv2_find_struct_tag(): tag %x not found\n", id);
    return NULL;
}
Tag pointers are physical addresses from the bootloader. They must be converted to virtual addresses using PHYS_TO_VIRT() before dereferencing.

Important Tags

The kernel uses several Stivale2 tags during initialization:

Memory Map Tag

kernel/boot/stivale2.h
#define STV2_STRUCT_TAG_MMAP_ID 0x2187f79e8612de07

enum {
    STIVALE2_MMAP_USABLE = 1,
    STIVALE2_MMAP_RESERVED = 2,
    STIVALE2_MMAP_ACPI_RECLAIMABLE = 3,
    STIVALE2_MMAP_ACPI_NVS = 4,
    STIVALE2_MMAP_BAD_MEMORY = 5,
    STIVALE2_MMAP_BOOTLOADER_RECLAIMABLE = 0x1000,
    STIVALE2_MMAP_KERNEL_AND_MODULES = 0x1001
};

struct stivale2_mmap_entry {
    uint64_t base;
    uint64_t length;
    uint32_t type;
    uint32_t unused;
} __attribute__((packed));

typedef struct stivale2_struct_tag_memmap {
    struct stivale2_tag tag;
    uint64_t entries;
    struct stivale2_mmap_entry memmap[];
} stv2_struct_tag_mmap;
This tag provides the physical memory map, which is crucial for PMM initialization.

Framebuffer Tag

kernel/boot/stivale2.h
#define STV2_STRUCT_TAG_FB_ID 0x506461d2950408fa

struct stivale2_struct_tag_framebuffer {
    struct stivale2_tag tag;
    uint8_t* fb_addr;
    uint16_t fb_width;
    uint16_t fb_height;
    uint16_t fb_pitch;
    uint16_t fb_bpp;
    uint8_t memory_model;
    uint8_t red_mask_size;
    uint8_t red_mask_shift;
    uint8_t green_mask_size;
    uint8_t green_mask_shift;
    uint8_t blue_mask_size;
    uint8_t blue_mask_shift;
} __attribute__((packed));
Provides framebuffer information for graphics output.

RSDP Tag

kernel/boot/stivale2.h
#define STV2_STRUCT_TAG_RSDP_ID 0x9e1786930a375e78

struct stivale2_struct_tag_rsdp {
    struct stivale2_tag tag;
    uint64_t rsdp;
} __attribute__((packed));
Points to the ACPI Root System Description Pointer.

Modules Tag

kernel/boot/stivale2.h
#define STV2_STRUCT_TAG_MODULES_ID 0x4b6fe466aade04ce

struct stivale2_module {
    uint64_t begin;
    uint64_t end;
    char string[STIVALE2_MODULE_STRING_SIZE];
} __attribute__((packed));

struct stivale2_struct_tag_modules {
    struct stivale2_tag tag;
    uint64_t module_count;
    struct stivale2_module modules[];
} __attribute__((packed));
Provides information about loaded modules (such as the initrd).

Initialization Sequence

The bootloader performs these steps before jumping to the kernel:
1

Load Kernel

The bootloader loads the kernel ELF binary into memory.
2

Setup Paging

Identity maps the first 4GB and sets up higher-half mapping. The kernel is mapped at 0xFFFFFFFF80000000.
3

Prepare Tags

Constructs tag structures with system information (memory map, framebuffer, ACPI tables, etc.).
4

Jump to Kernel

Jumps to the kernel entry point with a pointer to stivale2_struct as the first argument.

Kernel Initialization

Once control is transferred to the kernel, kmain() executes:
kernel/kmain.c
_Noreturn void kmain(stivale2_struct* info)
{
    // convert the physical address to a virtual one, since we will be removing identity mapping later
    bootinfo = (stivale2_struct*)PHYS_TO_VIRT(info);

    klog_printf("Aeolos x86_64 (alpha)\n");
    klog_printf("Built on "__DATE__" at "__TIME__".\n\n");

    idt_init();
    cpu_features_init();

    // system initialization
    pmm_init((stv2_struct_tag_mmap*)stv2_find_struct_tag(bootinfo, STV2_STRUCT_TAG_MMAP_ID));
    vmm_init();
    gdt_init();

    // initialize framebuffer and terminal
    fb_init((stv2_struct_tag_fb*)stv2_find_struct_tag(bootinfo, STV2_STRUCT_TAG_FB_ID));
    serial_init();
    term_init();

    // further system initialization
    acpi_init((stv2_struct_tag_rsdp*)stv2_find_struct_tag(bootinfo, STV2_STRUCT_TAG_RSDP_ID));
    hpet_init();
    apic_init();
    vfs_init();
    smp_init();

    // since we do not need the bootloader info anymore
    pmm_reclaim_bootloader_mem();

    // initialize multitasking
    sched_init(kinit);

    while (true)
        ;
}

Critical Early Steps

The first action is converting the bootloader structure pointer from physical to virtual:
bootinfo = (stivale2_struct*)PHYS_TO_VIRT(info);
This is critical because the kernel will remove identity mapping during VMM initialization.
The Physical Memory Manager must be initialized before the Virtual Memory Manager, as VMM depends on PMM to allocate page tables:
pmm_init((stv2_struct_tag_mmap*)stv2_find_struct_tag(bootinfo, STV2_STRUCT_TAG_MMAP_ID));
vmm_init();
After initialization is complete and bootloader information is no longer needed:
pmm_reclaim_bootloader_mem();
This frees memory regions marked as STIVALE2_MMAP_BOOTLOADER_RECLAIMABLE.

Memory Map Handling

During PMM initialization, the kernel processes the memory map:
kernel/mm/pmm.c
klog_info("bootloader provided memory map: \n");
for (size_t i = 0; i < map->entries; i++) {
    struct stivale2_mmap_entry entry = map->memmap[i];

    if (entry.base + entry.length <= 0x100000)
        continue;

    uint64_t newphyslimit = entry.base + entry.length;
    if (newphyslimit > memstats.phys_limit)
        memstats.phys_limit = newphyslimit;

    klog_printf("  base: %x, length: %x (%d KiB), type: %d\n", 
                entry.base, entry.length, entry.length/1024, entry.type);
    if (entry.type == STIVALE2_MMAP_USABLE || 
        entry.type == STIVALE2_MMAP_BOOTLOADER_RECLAIMABLE ||
        entry.type == STIVALE2_MMAP_ACPI_RECLAIMABLE || 
        entry.type == STIVALE2_MMAP_KERNEL_AND_MODULES)
        memstats.total_mem += entry.length;
}
The kernel skips memory below 1MB (0x100000) as this region typically contains legacy hardware and BIOS data.

Tag Identifiers Reference

TagIdentifierPurpose
Memory Map0x2187f79e8612de07Physical memory layout
Framebuffer0x506461d2950408faGraphics framebuffer info
RSDP0x9e1786930a375e78ACPI root pointer
Modules0x4b6fe466aade04ceLoaded modules (initrd)
SMP0x34d1d96339647025CPU/core information

Next Steps

Memory Management

Learn how PMM and VMM manage physical and virtual memory

Process Management

Explore task creation and scheduling

Build docs developers (and LLMs) love