:: commit 6c88eab95d79a6112602ea290c53dfe7e0766b53

pitust <piotr@stelmaszek.com> — 2021-11-01 17:13

parents: 935a392a36

bootboot: introduce support for the bootboot protocol

diff --git a/stage23/entry.s3.c b/stage23/entry.s3.c
index 72d5a790..626d6076 100644
--- a/stage23/entry.s3.c
+++ b/stage23/entry.s3.c
@@ -14,6 +14,7 @@
 #include <mm/pmm.h>
 #include <protos/stivale.h>
 #include <protos/stivale2.h>
+#include <protos/bootboot.h>
 #include <protos/linux.h>
 #include <protos/chainload.h>
 #include <protos/multiboot1.h>
@@ -177,6 +178,13 @@ void stage3_common(void) {
         multiboot1_load(config, cmdline);
     } else if (!strcmp(proto, "multiboot2")) {
         multiboot2_load(config, cmdline);
+    } else if (!strcmp(proto, "bootboot")) {
+#if bios == 1
+        void *efi_system_table = NULL;
+#elif uefi == 1
+        void *efi_system_table = gST;
+#endif
+        bootboot_load(config, cmdline, efi_system_table);
     }
 
     panic("Invalid protocol specified");
diff --git a/stage23/protos/bootboot.32.c b/stage23/protos/bootboot.32.c
new file mode 100644
index 00000000..f1a2b2d4
--- /dev/null
+++ b/stage23/protos/bootboot.32.c
@@ -0,0 +1,90 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <mm/vmm.h>
+
+__attribute__((noreturn)) void bootboot_spinup_32(
+                 uint32_t pagemap_top_lv,
+                 uint32_t entry_point_lo, uint32_t entry_point_hi,
+                 uint32_t stack_lo, uint32_t stack_hi) {
+    uint64_t casted_to_64[] = {
+        (uint64_t)entry_point_lo | ((uint64_t)entry_point_hi << 32),
+        (uint64_t)stack_lo | ((uint64_t)stack_hi << 32)
+    };
+
+
+    asm volatile (
+        "cld\n\t"
+        "movl %%eax, %%cr3\n\t"
+        "movl %%cr4, %%eax\n\t"
+        "btsl $5, %%eax\n\t"
+        "movl %%eax, %%cr4\n\t"
+        "movl $0xc0000080, %%ecx\n\t"
+        "rdmsr\n\t"
+        "btsl $8, %%eax\n\t"
+        "wrmsr\n\t"
+        "movl %%cr0, %%eax\n\t"
+        "btsl $31, %%eax\n\t"
+        "movl %%eax, %%cr0\n\t"
+        "call 1f\n\t"
+        "1: popl %%eax\n\t"
+        "addl $8, %%eax\n\t"
+        "pushl $0x28\n\t"
+        "pushl %%eax\n\t"
+        "lret\n\t"
+        ".code64\n\t"
+        "movl $0x30, %%eax\n\t"
+        "movl %%eax, %%ds\n\t"
+        "movl %%eax, %%es\n\t"
+        "movl %%eax, %%fs\n\t"
+        "movl %%eax, %%gs\n\t"
+        "movl %%eax, %%ss\n\t"
+
+        // Since we don't really know what is now present in the upper
+        // 32 bits of the 64 bit registers, clear up the upper bits
+        // of the register that points to the 64-bit casted value array.
+        "movl %%esi, %%esi\n\t"
+
+        // Move in 64-bit values
+        "movq 0x00(%%rsi), %%rbx\n\t"
+        "movq 0x08(%%rsi), %%rsi\n\t"
+
+        // Let's pretend we push a return address
+        "testq %%rsi, %%rsi\n\t"
+        "jz 1f\n\t"
+
+        "subq $8, %%rsi\n\t"
+        "movq $0, (%%rsi)\n\t"
+
+        "1:\n\t"
+        "pushq $0x30\n\t"
+        "pushq %%rsi\n\t"
+        "pushfq\n\t"
+        "pushq $0x28\n\t"
+        "pushq %%rbx\n\t"
+
+        "xorl %%eax, %%eax\n\t"
+        "xorl %%ebx, %%ebx\n\t"
+        "xorl %%ecx, %%ecx\n\t"
+        "xorl %%edx, %%edx\n\t"
+        "xorl %%edi, %%edi\n\t"
+        "xorl %%esi, %%esi\n\t"
+        "xorl %%ebp, %%ebp\n\t"
+        "xorq %%r8,  %%r8\n\t"
+        "xorq %%r9,  %%r9\n\t"
+        "xorq %%r10, %%r10\n\t"
+        "xorq %%r11, %%r11\n\t"
+        "xorq %%r12, %%r12\n\t"
+        "xorq %%r13, %%r13\n\t"
+        "xorq %%r14, %%r14\n\t"
+        "xorq %%r15, %%r15\n\t"
+
+        "iretq\n\t"
+        ".code32\n\t"
+        :
+        : "a" (pagemap_top_lv), "S" (casted_to_64)
+        : "memory"
+    );
+
+    __builtin_unreachable();
+}
diff --git a/stage23/protos/bootboot.c b/stage23/protos/bootboot.c
new file mode 100644
index 00000000..12c30cb7
--- /dev/null
+++ b/stage23/protos/bootboot.c
@@ -0,0 +1,310 @@
+#include "lib/gterm.h"
+#include "sys/smp.h"
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <protos/bootboot.h>
+#include <lib/libc.h>
+#include <lib/elf.h>
+#include <lib/blib.h>
+#include <lib/acpi.h>
+#include <lib/config.h>
+#include <lib/time.h>
+#include <lib/print.h>
+#include <lib/real.h>
+#include <lib/uri.h>
+#include <lib/fb.h>
+#include <lib/term.h>
+#include <sys/pic.h>
+#include <sys/cpu.h>
+#include <sys/gdt.h>
+#include <sys/idt.h>
+#include <sys/lapic.h>
+#include <fs/file.h>
+#include <mm/vmm.h>
+#include <mm/pmm.h>
+#include <drivers/vga_textmode.h>
+
+__attribute__((noreturn)) void bootboot_spinup(
+                 pagemap_t *pagemap,
+                 uint64_t entry_point, uint64_t stack,
+                 size_t numcores, struct smp_information* cores);
+struct elf64_hdr {
+    uint8_t  ident[16];
+    uint16_t type;
+    uint16_t machine;
+    uint32_t version;
+    uint64_t entry;
+    uint64_t phoff;
+    uint64_t shoff;
+    uint32_t flags;
+    uint16_t hdr_size;
+    uint16_t phdr_size;
+    uint16_t ph_num;
+    uint16_t shdr_size;
+    uint16_t sh_num;
+    uint16_t shstrndx;
+};
+struct elf64_shdr {
+    uint32_t sh_name;
+    uint32_t sh_type;
+    uint64_t sh_flags;
+    uint64_t sh_addr;
+    uint64_t sh_offset;
+    uint64_t sh_size;
+    uint32_t sh_link;
+    uint32_t sh_info;
+    uint64_t sh_addralign;
+    uint64_t sh_entsize;
+};
+struct elf64_sym {
+    uint32_t st_name;
+    uint8_t  st_info;
+    uint8_t  st_other;
+    uint16_t st_shndx;
+    uint64_t st_value;
+    uint64_t st_size;
+};
+#define BOOTBOOT_FB     0xfffffffffc000000
+#define BOOTBOOT_INFO   0xffffffffffe00000
+#define BOOTBOOT_ENV    0xffffffffffe01000
+#define BOOTBOOT_CORE   0xffffffffffe02000
+
+void bootboot_load(char *config, char *cmdline, void *efi_system_table) {    
+    uint64_t fb_vaddr = BOOTBOOT_FB;
+    uint64_t struct_vaddr = BOOTBOOT_INFO;
+    uint64_t cmdline_vaddr = BOOTBOOT_ENV;
+    uint64_t init_stack_size = 1024;
+
+    /// Config ///
+    char *kernel_path = config_get_value(config, 0, "KERNEL_PATH");
+    if (kernel_path == NULL)
+        panic("bootboot: KERNEL_PATH not specified");
+    
+    char *ramdisk = config_get_value(config, 0, "RAMDISK");
+    if (ramdisk == NULL) {
+        print("bootboot: no ramdisk!\n");
+    }
+
+
+    /// Kernel loading code ///
+    print("bootboot: Loading kernel `%s`...\n", kernel_path);
+    struct file_handle* kernel_file;
+    if ((kernel_file = uri_open(kernel_path)) == NULL)
+        panic("bootboot: Failed to open kernel with path `%s`. Is the path correct?\n", kernel_path);
+
+    uint8_t* kernel = freadall(kernel_file, MEMMAP_KERNEL_AND_MODULES);
+    
+    /// Funky macros ///
+#define KOFFSET(type, off) (type)&kernel[(off)]
+#define ESECTION(idx) KOFFSET(struct elf64_shdr*, elf_header->shoff + elf_header->shdr_size * (idx))
+
+    /// Bootboot symbols ///
+    struct elf64_hdr* elf_header = (struct elf64_hdr*)kernel;
+    struct elf64_shdr* section_header_strings_section = ESECTION(elf_header->shstrndx);
+    char* section_header_strings = KOFFSET(char*, section_header_strings_section->sh_offset);
+    struct elf64_shdr* symbol_table;
+    struct elf64_shdr* string_table;
+    for(uint32_t i = 0; i < elf_header->sh_num; i++){
+        struct elf64_shdr* section_header = ESECTION(i);
+        char* secname = &section_header_strings[section_header->sh_name];
+        if(!strcmp(secname, ".symtab")) symbol_table = section_header;
+        if(!strcmp(secname, ".strtab")) string_table = section_header;
+    }
+    if (!symbol_table || !string_table) {
+        print("bootboot: warning: no symbol/string tables in the ELF!");
+    } else {
+        struct elf64_sym* symbols = KOFFSET(struct elf64_sym*, symbol_table->sh_offset);
+        char* symbol_strings = KOFFSET(char*, string_table->sh_offset);
+        for (uint32_t i = 0, symcount = symbol_table->sh_size / sizeof(struct elf64_sym);i < symcount;i++) {
+            char* symbol = &symbol_strings[symbols[i].st_name];
+            uint64_t symaddr = symbols[i].st_value;
+
+            print("bootboot: symbol `%s`\n", symbol);
+
+            if(!strcmp(symbol, "bootboot")) struct_vaddr = symaddr;
+            if(!strcmp(symbol, "environment")) cmdline_vaddr = symaddr;
+            if(!strcmp(symbol, "fb")) fb_vaddr = symaddr;
+            if(!strcmp(symbol, "initstack")) init_stack_size = symaddr;
+        }
+    }
+
+    uint64_t entry, top, slide, rangecount, physbase, virtbase = 0;
+    struct elf_range* ranges;
+
+    /// Memory mappings ///
+    pagemap_t pmap = new_pagemap(4);
+
+    /// Load kernel ///
+    {
+        if (elf64_load(
+            kernel, &entry, &top, &slide, MEMMAP_KERNEL_AND_MODULES,
+            false, false, &ranges, &rangecount, true, &physbase, &virtbase)) {
+            panic("bootboot: elf64 load failed");
+        }
+        for (uint64_t mapvirt = virtbase, mapphys = physbase; mapphys < top;mapvirt += 0x1000, mapphys += 0x1000) {
+            map_page(pmap, mapvirt, mapphys, VMM_FLAG_PRESENT | VMM_FLAG_WRITE, false);
+        }
+    }
+    BOOTBOOT* bootboot = (BOOTBOOT*)ext_mem_alloc_type_aligned(4096, MEMMAP_BOOTLOADER_RECLAIMABLE, 4096);
+    map_page(pmap, struct_vaddr, (uint64_t)bootboot, VMM_FLAG_PRESENT | VMM_FLAG_WRITE, false);
+
+    char** env = (char**)ext_mem_alloc_type_aligned(4096, MEMMAP_BOOTLOADER_RECLAIMABLE, 4096);
+    map_page(pmap, cmdline_vaddr, (uint64_t)env, VMM_FLAG_PRESENT | VMM_FLAG_WRITE, false);
+    memcpy(env, cmdline, strlen(cmdline));
+
+    for (uint64_t i = 0; i < 0x400000000; i += 0x200000) {
+        map_page(pmap, i, i, 0x03, true);
+    }
+
+    /// Framebuffer init ///
+    size_t fbwidth = 0, fbheight = 0, fbbpp = 32;
+    struct fb_info fbi;
+    char *resolution = config_get_value(config, 0, "RESOLUTION");
+    if (resolution != NULL)
+        parse_resolution(&fbwidth, &fbheight, &fbbpp, resolution);
+    
+    term_deinit();
+    fb_init(&fbi, fbwidth, fbheight, fbbpp);
+    uint64_t fb_size = fbi.framebuffer_height * fbi.framebuffer_pitch;
+
+    for (uint64_t current = 0;current < fb_size;current += 0x1000) {
+        map_page(pmap, fb_vaddr + current, fbi.framebuffer_addr + current, VMM_FLAG_PRESENT | VMM_FLAG_WRITE, false);
+    }
+
+    /// Ramdisk loading ///
+    uint64_t ramdisk_start = 0, ramdisk_size = 0;
+    if (ramdisk) {
+        struct file_handle* ramdisk_file;
+        if ((ramdisk_file = uri_open(ramdisk)) == NULL)
+            panic("bootboot: Failed to open ramdisk with path `%s`. Is the path correct?\n", ramdisk);
+
+        uint8_t* ramdisk_data = freadall(ramdisk_file, MEMMAP_KERNEL_AND_MODULES);
+        ramdisk_size = ramdisk_file->size;
+        ramdisk_start = (uint64_t)ramdisk_data;
+    }
+
+    /// Header info ///
+    memcpy(bootboot->magic, "BOOT", 4);
+#if bios
+    bootboot->protocol = 2 | (0 << 2);
+#elif uefi
+    bootboot->protocol = 2 | (1 << 2);
+#else
+#error bootboot: unknown target, not uefi or bios, what the fuck?
+#endif
+
+    /// SMP info ///
+    size_t numcores;
+    uint32_t bsplapic;
+    struct smp_information* cores;
+    init_smp(0, (void**)&cores, &numcores, &bsplapic, true, false, pmap, false, false);
+    bootboot->numcores = numcores;
+    bootboot->bspid = bsplapic;
+    for (size_t i = 0;i < numcores;i++) {
+        cores[i].stack_addr = ((uint64_t)ext_mem_alloc(init_stack_size)) + init_stack_size;
+    }
+
+    /// Time stubs ///
+    print("bootboot: todo/help wanted: if you feel like adding support for weird time bullshit, please contribute\n");
+    bootboot->timezone = 0;
+    memset(bootboot->datetime, 0, 8);
+
+    /// Ramdisk ///
+    bootboot->initrd_ptr = ramdisk_start;
+    bootboot->initrd_size = ramdisk_size;
+
+    /// Framebuffer ///
+    bootboot->fb_ptr = fbi.framebuffer_addr;
+    bootboot->fb_size = fb_size;
+    bootboot->fb_width = fbi.framebuffer_width;
+    bootboot->fb_height = fbi.framebuffer_height;
+    bootboot->fb_scanline = fbi.framebuffer_pitch;
+    bootboot->fb_type = 1;
+
+    /// SMBIOS and ACPI ///
+    uint64_t smbios_entry_32 = 0, smbios_entry_64 = 0;
+    acpi_get_smbios((void **)&smbios_entry_32, (void **)&smbios_entry_64);
+
+    bootboot->arch.x86_64.acpi_ptr = (uint64_t)(size_t)acpi_get_rsdp();
+    if (smbios_entry_64) bootboot->arch.x86_64.smbi_ptr = smbios_entry_64;
+    else if (smbios_entry_32) bootboot->arch.x86_64.smbi_ptr = smbios_entry_32;
+    else bootboot->arch.x86_64.smbi_ptr = 0;
+    bootboot->arch.x86_64.efi_ptr = (uint64_t)efi_system_table;
+    bootboot->arch.x86_64.mp_ptr = 0;
+    
+    /// Memory map ///
+    {
+        size_t mmapent;
+        struct e820_entry_t* e820e = get_memmap(&mmapent);
+        if (mmapent > 248) {
+            term_reinit();
+            size_t rows, cols;
+            gterm_init(&rows, &cols, 0, 0);
+            panic("Too much memory entries! our god bzt decided that %d entries is too much, max is 248", mmapent);
+        }
+        for (uint32_t i = 0;i < mmapent;i++) {
+            uint32_t btype = 0;
+// #define MEMMAP_USABLE                 1
+// #define MEMMAP_RESERVED               2
+// #define MEMMAP_ACPI_RECLAIMABLE       3
+// #define MEMMAP_ACPI_NVS               4
+// #define MEMMAP_BAD_MEMORY             5
+// #define MEMMAP_BOOTLOADER_RECLAIMABLE 0x1000
+// #define MEMMAP_KERNEL_AND_MODULES     0x1001
+// #define MEMMAP_FRAMEBUFFER            0x1002
+// #define MEMMAP_EFI_RECLAIMABLE        0x2000
+// #define MEMMAP_EFI_BOOTSERVICES       0x2001
+            if (e820e[i].type == 1) btype = 1;
+            if (e820e[i].type == 3) btype = 2;
+            if (e820e[i].type == 4) btype = 2;
+
+            print("mapping the type %x to bootboot type %x\n", e820e[i].type, btype);
+            bootboot->mmap[i].size = (e820e[i].length & 0xF) | btype;
+            bootboot->mmap[i].ptr = e820e[i].base;
+        }
+        bootboot->size = 128 + mmapent * 16;
+    }
+
+    /// Late framebuffer init ///
+    memset((void*)(size_t)fbi.framebuffer_addr, 0, fb_size);
+
+    /// Spinup ///
+    bootboot_spinup(&pmap, entry, cores[0].stack_addr, numcores, cores);
+
+}
+
+__attribute__((noreturn)) void bootboot_spinup_32(
+                 uint32_t pagemap_top_lv,
+                 uint32_t entry_point_lo, uint32_t entry_point_hi,
+                 uint32_t stack_lo, uint32_t stack_hi);
+
+__attribute__((noreturn)) void bootboot_spinup(
+                 pagemap_t *pagemap,
+                 uint64_t entry_point, uint64_t stack,
+                 size_t numcores, struct smp_information* cores) {
+#if bios == 1
+    // If we're going 64, we might as well call this BIOS interrupt
+    // to tell the BIOS that we are entering Long Mode, since it is in
+    // the specification.
+    struct rm_regs r = {0};
+    r.eax = 0xec00;
+    r.ebx = 0x02;   // Long mode only
+    rm_int(0x15, &r, &r);
+#endif
+
+    pic_mask_all();
+    io_apic_mask_all();
+
+    irq_flush_type = IRQ_PIC_APIC_FLUSH;
+
+    for (size_t i = 0;i < numcores;i++) {
+        cores[i].extra_argument = 0;
+        cores[i].goto_address = entry_point;
+    }
+
+    common_spinup(bootboot_spinup_32, 10,
+        (uint32_t)(uintptr_t)pagemap->top_level,
+        (uint32_t)entry_point, (uint32_t)(entry_point >> 32),
+        (uint32_t)stack, (uint32_t)(stack >> 32));
+}
diff --git a/stage23/protos/bootboot.h b/stage23/protos/bootboot.h
new file mode 100644
index 00000000..1151f771
--- /dev/null
+++ b/stage23/protos/bootboot.h
@@ -0,0 +1,133 @@
+#ifndef __PROTOS__BOOTBOOT_H__
+#define __PROTOS__BOOTBOOT_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+void bootboot_load(char *config, char *cmdline, void *efi_system_table);
+
+/*
+ * bootboot.h
+ * https://gitlab.com/bztsrc/bootboot
+ *
+ * Copyright (C) 2021 pitust (piotr@stelmaszek.com)
+ * Copyright (C) 2017 - 2021 bzt (bztsrc@gitlab)
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of the BOOTBOOT Protocol package.
+ * @brief The BOOTBOOT structure
+ *
+ */
+
+#define BOOTBOOT_MAGIC "BOOT"
+
+/* default virtual addresses for level 0 and 1 static loaders */
+#define BOOTBOOT_MMIO   0xfffffffff8000000  /* memory mapped IO virtual address */
+#define BOOTBOOT_FB     0xfffffffffc000000  /* frame buffer virtual address */
+#define BOOTBOOT_INFO   0xffffffffffe00000  /* bootboot struct virtual address */
+#define BOOTBOOT_ENV    0xffffffffffe01000  /* environment string virtual address */
+#define BOOTBOOT_CORE   0xffffffffffe02000  /* core loadable segment start */
+
+/* minimum protocol level:
+ *  hardcoded kernel name, static kernel memory addresses */
+#define PROTOCOL_MINIMAL 0
+/* static protocol level:
+ *  kernel name parsed from environment, static kernel memory addresses */
+#define PROTOCOL_STATIC  1
+/* dynamic protocol level:
+ *  kernel name parsed, kernel memory addresses from ELF or PE symbols */
+#define PROTOCOL_DYNAMIC 2
+/* big-endian flag */
+#define PROTOCOL_BIGENDIAN 0x80
+
+/* loader types, just informational */
+#define LOADER_BIOS     (0<<2)
+#define LOADER_UEFI     (1<<2)
+#define LOADER_RPI      (2<<2)
+#define LOADER_COREBOOT (3<<2)
+
+/* framebuffer pixel format, only 32 bits supported */
+#define FB_ARGB   0
+#define FB_RGBA   1
+#define FB_ABGR   2
+#define FB_BGRA   3
+
+/* mmap entry, type is stored in least significant tetrad (half byte) of size
+ * this means size described in 16 byte units (not a problem, most modern
+ * firmware report memory in pages, 4096 byte units anyway). */
+typedef struct {
+  uint64_t   ptr;
+  uint64_t   size;
+} MMapEnt;
+#define MMapEnt_Ptr(a)  ((a)->ptr)
+#define MMapEnt_Size(a) ((a)->size & 0xFFFFFFFFFFFFFFF0)
+#define MMapEnt_Type(a) ((a)->size & 0xF)
+#define MMapEnt_IsFree(a) (((a)->size&0xF)==1)
+#define MMapEnt_Combine(addr, type) (((addr) & ~0xF) | ((type)))
+
+#define MMAP_USED     0   /* don't use. Reserved or unknown regions */
+#define MMAP_FREE     1   /* usable memory */
+#define MMAP_ACPI     2   /* acpi memory, volatile and non-volatile as well */
+#define MMAP_MMIO     3   /* memory mapped IO region */
+
+#define INITRD_MAXSIZE 16 /* Mb */
+
+typedef struct {
+  /* first 64 bytes is platform independent */
+  uint8_t    magic[4];    /* 'BOOT' magic */
+  uint32_t   size;        /* length of bootboot structure, minimum 128 */
+  uint8_t    protocol;    /* 1, static addresses, see PROTOCOL_* and LOADER_* above */
+  uint8_t    fb_type;     /* framebuffer type, see FB_* above */
+  uint16_t   numcores;    /* number of processor cores */
+  uint16_t   bspid;       /* Bootsrap processor ID (Local APIC Id on x86_64) */
+  int16_t    timezone;    /* in minutes -1440..1440 */
+  uint8_t    datetime[8]; /* in BCD yyyymmddhhiiss UTC (independent to timezone) */
+  uint64_t   initrd_ptr;  /* ramdisk image position and size */
+  uint64_t   initrd_size;
+  uint64_t   fb_ptr;      /* framebuffer pointer and dimensions */
+  uint32_t   fb_size;
+  uint32_t   fb_width;
+  uint32_t   fb_height;
+  uint32_t   fb_scanline;
+
+  /* the rest (64 bytes) is platform specific */
+  union {
+    struct {
+      uint64_t acpi_ptr;
+      uint64_t smbi_ptr;
+      uint64_t efi_ptr;
+      uint64_t mp_ptr;
+      uint64_t unused0;
+      uint64_t unused1;
+      uint64_t unused2;
+      uint64_t unused3;
+    } x86_64;
+  } arch;
+
+  /* from 128th byte, MMapEnt[], more records may follow */
+  MMapEnt    mmap[];
+  /* use like this:
+   * MMapEnt *mmap_ent = &bootboot.mmap; mmap_ent++;
+   * until you reach bootboot->size, while(mmap_ent < bootboot + bootboot->size) */
+} BOOTBOOT;
+
+#endif
tab: 248 wrap: offon