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.
The kernel provides a header structure to the bootloader, located in kernel/boot/stivale2.c:18:
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.
Framebuffer Request
Header Structure
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
};
The bootloader passes information to the kernel through a stivale2_struct and associated tags:
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));
The kernel walks the tag linked list to find required information:
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.
The kernel uses several Stivale2 tags during initialization:
Memory Map Tag
#define STV2_STRUCT_TAG_MMAP_ID 0x 2187f79e8612de07
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 = 0x 1000 ,
STIVALE2_MMAP_KERNEL_AND_MODULES = 0x 1001
};
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
#define STV2_STRUCT_TAG_FB_ID 0x 506461d2950408fa
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
#define STV2_STRUCT_TAG_RSDP_ID 0x 9e1786930a375e78
struct stivale2_struct_tag_rsdp {
struct stivale2_tag tag;
uint64_t rsdp;
} __attribute__ ((packed));
Points to the ACPI Root System Description Pointer.
Modules Tag
#define STV2_STRUCT_TAG_MODULES_ID 0x 4b6fe466aade04ce
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:
Load Kernel
The bootloader loads the kernel ELF binary into memory.
Setup Paging
Identity maps the first 4GB and sets up higher-half mapping. The kernel is mapped at 0xFFFFFFFF80000000.
Prepare Tags
Constructs tag structures with system information (memory map, framebuffer, ACPI tables, etc.).
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:
_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
Convert Bootinfo to Virtual Address
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.
Initialize PMM Before VMM
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 ();
Reclaim Bootloader Memory
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:
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 <= 0x 100000 )
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
Tag Identifier Purpose Memory Map 0x2187f79e8612de07Physical memory layout Framebuffer 0x506461d2950408faGraphics framebuffer info RSDP 0x9e1786930a375e78ACPI root pointer Modules 0x4b6fe466aade04ceLoaded modules (initrd) SMP 0x34d1d96339647025CPU/core information
Next Steps
Memory Management Learn how PMM and VMM manage physical and virtual memory
Process Management Explore task creation and scheduling