BOOTBOOT Multi-platform Micro-kernel Loader
Posted: Fri Dec 07, 2018 7:34 pm
BOOTBOOT Protocol
Unlike any existing boot loaders, this loader is not one big bloated system. Quite the contrary, it's a set of several independent, very thin implementations, all providing the same C/C++ compatible, higher-half 64 bit environment on their corresponding platforms. By very thin I mean really lightweight, no more than a few kilobytes each:
boot.bin (512 bytes), bootboot.bin (11k), bootboot.img (30k), bootboot.efi (93k)
It is ideal for hobby OS developers, as it's easy to use and it's very flexible. It's much easier to dealt with than GRUB, and also unlike with Multiboot you won't need any long mode Assembly trampoline code with dirty GDT and paging tricks in your kernel.
BOOTBOOT can load your higher half, 64 bit-only C/C++ kernel just as-is without any hacks. The repository also contains full documentation in MD and PDF formats, as well as small ANSI C mkboot utilities to help you with the installation. It's a Free and Open Source Sotfware, licensed under the terms of MIT license. If you want to use it as a reference for your own boot loader, the PDF documentation has a detailed description on which firmware interfaces have been used, and also the sources are well commented.
The BOOTBOOT Protocol is very easy to integrate, as it has a totally architecture agnostic interface. You just specify some object addresses in your linker script, and you're done. The information structure is defined in a C/C++ header file and can be used on all platforms. It contains information such as boot date and time, timezone, frame buffer dimension, platform independent memory map, initrd size, pointers to detected system tables (efi, acpi, mp, smbios etc.). Unlike other protocols, BOOTBOOT was written with forward-compatibility in mind. That is, current implementations support static addresses for those variables (Protocol Level 1), but it states that future versions (Level 2) should read the symbol table of the kernel to find those addresses. Kernels written for the Level 1 loaders will be able to boot with the upcoming Level 2 loaders just out-of-the-box.
The loader is capable of loading monolithic kernels, but mainly focuses on micro-kernels with an initrd. It can load the OS from several different sources: from ROM, over serial line, from a GPT partition, or from a file on a FAT16/32 GPT partition (usually ESP. To avoid licensing issues with M$, it's limited to upper case 8+3 filenames). The protocol also allows booting over network with TFTP, although the reference implementations do not use that (not yet ).
Current implementations support kernels in ELF64 and PE32+ formats for the AArch64 and x86_64 architectures. As for the initrd, they support
- statically linked executables (for monolithic kernels and statically linked micro-kernels like Minix)
- cpio (all variants: old hp, newc, and crc too. The latter is used by the Linux kernel btw.)
- tar (POSIX ustar, a very beginner friendly format)
- SFS (both Brendan's and BenLunt's versions)
- James Molloy's initrd (I assume you're already familiar with his tutorial)
- FS/Z (my operating system's native file system format)
- any archive or file system format, provided your kernel file is contiguous and is the first executable in the initrd.
The initrd can be gzip compressed, or optionally encrypted (FS/Z only feature).
The repository contains an example hello world kernel that demonstrates how to write strings and boxes on the frame buffer in an architecture independent way using PSF2 fonts. You are free to use that example kernel as a skeleton for starting your own kernel. Kernels written in C++ are also supported, but you have to provide a small code block at _start that calls your constructors (easiest way is to use a linker script to create .text.init or .text.ctors sections or .init_array table).
How to install
Please note BOOTBOOT is very flexible. What I describe here is just one way, which I believe to be the most common use case for hobby OS developers.
1. create a disk image with GPT partitioning table ("dd if=/dev/zero of=myimage.dd bs=1M count=256" and "fdisk myimage.dd")
2. set the first partition's type to ESP, and format it with FAT16 or FAT32 (fdisk's type 1, and use "mkfs.vfat -F x")
3. mount that partition with a loop device ("sudo mount -o loop,user myimage.dd somedir" or use "losetup"+"mount /dev/loop0")
4. create a BOOTBOOT directory there ("mkdir somedir/BOOTBOOT")
5. create an INITRD with your kernel in it (for example use "find . | cpio -H hpodc -o | gzip >somedir/BOOTBOOT/INITRD", for monolithic kernels, simply copy your kernel executable as INITRD).
6. optionally create a text file named BOOTBOOT/CONFIG (will be parsed by your kernel)
7. copy bootboot.bin into that directory as BOOTBOOT/LOADER (I strongly suggest to set the SYSTEM attribute for this file)
8. unmount the disk image
9. use the x86_64-bios/mkboot.c utility to install a (P)MBR sector on your disk image ("./mkboot myimage.dd")
If you want more control, do the steps 1-6, and choose a loader implementation for your platform and desired configuration (you can also use multiple loaders in the same image to create multiplatform bootable images). The documentation has detailed description of all of these scenarios:
- Raspberry Pi 3 (AArch64)
- UEFI (x86_64)
- UEFI for embedded systems (x86_64, in ROM)
- GRUB Multiboot (x86_64)
- ISOLINUX / LILO / BOOTLIN / etc. (x86_64)
- Legacy BIOS boot with any arbitrary boot manager (x86_64, chainloaded from VBR)
- Legacy BIOS boot with a single OS on a forward-compatible GPT disk (x86_64, booted from MBR)
- Legacy BIOS for embedded systems (x86_64, in ROM)
- CDROM, in El Torito "no emulation" mode (x86_64, hybrid GPT/ISO9660 image)
For more information, read the documentation. Before you comment any critism about this loader, please read the documentation. It's very likely it can do what you think it can't do, as most features and options are not mentioned in this brief introductory post.
Cheers,
bzt
Unlike any existing boot loaders, this loader is not one big bloated system. Quite the contrary, it's a set of several independent, very thin implementations, all providing the same C/C++ compatible, higher-half 64 bit environment on their corresponding platforms. By very thin I mean really lightweight, no more than a few kilobytes each:
boot.bin (512 bytes), bootboot.bin (11k), bootboot.img (30k), bootboot.efi (93k)
It is ideal for hobby OS developers, as it's easy to use and it's very flexible. It's much easier to dealt with than GRUB, and also unlike with Multiboot you won't need any long mode Assembly trampoline code with dirty GDT and paging tricks in your kernel.
BOOTBOOT can load your higher half, 64 bit-only C/C++ kernel just as-is without any hacks. The repository also contains full documentation in MD and PDF formats, as well as small ANSI C mkboot utilities to help you with the installation. It's a Free and Open Source Sotfware, licensed under the terms of MIT license. If you want to use it as a reference for your own boot loader, the PDF documentation has a detailed description on which firmware interfaces have been used, and also the sources are well commented.
The BOOTBOOT Protocol is very easy to integrate, as it has a totally architecture agnostic interface. You just specify some object addresses in your linker script, and you're done. The information structure is defined in a C/C++ header file and can be used on all platforms. It contains information such as boot date and time, timezone, frame buffer dimension, platform independent memory map, initrd size, pointers to detected system tables (efi, acpi, mp, smbios etc.). Unlike other protocols, BOOTBOOT was written with forward-compatibility in mind. That is, current implementations support static addresses for those variables (Protocol Level 1), but it states that future versions (Level 2) should read the symbol table of the kernel to find those addresses. Kernels written for the Level 1 loaders will be able to boot with the upcoming Level 2 loaders just out-of-the-box.
The loader is capable of loading monolithic kernels, but mainly focuses on micro-kernels with an initrd. It can load the OS from several different sources: from ROM, over serial line, from a GPT partition, or from a file on a FAT16/32 GPT partition (usually ESP. To avoid licensing issues with M$, it's limited to upper case 8+3 filenames). The protocol also allows booting over network with TFTP, although the reference implementations do not use that (not yet ).
Current implementations support kernels in ELF64 and PE32+ formats for the AArch64 and x86_64 architectures. As for the initrd, they support
- statically linked executables (for monolithic kernels and statically linked micro-kernels like Minix)
- cpio (all variants: old hp, newc, and crc too. The latter is used by the Linux kernel btw.)
- tar (POSIX ustar, a very beginner friendly format)
- SFS (both Brendan's and BenLunt's versions)
- James Molloy's initrd (I assume you're already familiar with his tutorial)
- FS/Z (my operating system's native file system format)
- any archive or file system format, provided your kernel file is contiguous and is the first executable in the initrd.
The initrd can be gzip compressed, or optionally encrypted (FS/Z only feature).
The repository contains an example hello world kernel that demonstrates how to write strings and boxes on the frame buffer in an architecture independent way using PSF2 fonts. You are free to use that example kernel as a skeleton for starting your own kernel. Kernels written in C++ are also supported, but you have to provide a small code block at _start that calls your constructors (easiest way is to use a linker script to create .text.init or .text.ctors sections or .init_array table).
How to install
Please note BOOTBOOT is very flexible. What I describe here is just one way, which I believe to be the most common use case for hobby OS developers.
1. create a disk image with GPT partitioning table ("dd if=/dev/zero of=myimage.dd bs=1M count=256" and "fdisk myimage.dd")
2. set the first partition's type to ESP, and format it with FAT16 or FAT32 (fdisk's type 1, and use "mkfs.vfat -F x")
3. mount that partition with a loop device ("sudo mount -o loop,user myimage.dd somedir" or use "losetup"+"mount /dev/loop0")
4. create a BOOTBOOT directory there ("mkdir somedir/BOOTBOOT")
5. create an INITRD with your kernel in it (for example use "find . | cpio -H hpodc -o | gzip >somedir/BOOTBOOT/INITRD", for monolithic kernels, simply copy your kernel executable as INITRD).
6. optionally create a text file named BOOTBOOT/CONFIG (will be parsed by your kernel)
7. copy bootboot.bin into that directory as BOOTBOOT/LOADER (I strongly suggest to set the SYSTEM attribute for this file)
8. unmount the disk image
9. use the x86_64-bios/mkboot.c utility to install a (P)MBR sector on your disk image ("./mkboot myimage.dd")
If you want more control, do the steps 1-6, and choose a loader implementation for your platform and desired configuration (you can also use multiple loaders in the same image to create multiplatform bootable images). The documentation has detailed description of all of these scenarios:
- Raspberry Pi 3 (AArch64)
- UEFI (x86_64)
- UEFI for embedded systems (x86_64, in ROM)
- GRUB Multiboot (x86_64)
- ISOLINUX / LILO / BOOTLIN / etc. (x86_64)
- Legacy BIOS boot with any arbitrary boot manager (x86_64, chainloaded from VBR)
- Legacy BIOS boot with a single OS on a forward-compatible GPT disk (x86_64, booted from MBR)
- Legacy BIOS for embedded systems (x86_64, in ROM)
- CDROM, in El Torito "no emulation" mode (x86_64, hybrid GPT/ISO9660 image)
For more information, read the documentation. Before you comment any critism about this loader, please read the documentation. It's very likely it can do what you think it can't do, as most features and options are not mentioned in this brief introductory post.
Cheers,
bzt