OSDev.org

The Place to Start for Operating System Developers
It is currently Mon Jul 06, 2020 11:46 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Fri Dec 07, 2018 7:34 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 715
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


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Sat Dec 15, 2018 3:33 am 
Offline
Member
Member

Joined: Sun Oct 21, 2018 1:37 pm
Posts: 38
It looks very interesting, especially because I think I can find some guidance for my project in some points where I a a bit stuck, or not skilled enough to move forward comfortably, and you seem to cover.

Thanks for sharing this !


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Thu Feb 07, 2019 6:24 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 715
Hi All,

I proudly present to you the final release of BOOTBOOT Protocol.

I've finished co-processor (SSE, Neon) initialization and SMP support on all platforms. With those the feature set is now complete, meaning from now on I'll only commit bugfixes (if any) and ports for new platforms. Specification is now frozen, no further modification on the specification will take place.

Multi Processing was tricky to achive without an interface, but finally I have decided to use the simplest method: the same kernel is started on all cores, only with different stacks. BIOS platform uses LAPIC IPI + SIPI (up to 256 cores) and PIT for sleeping. UEFI utilizes the newer PI EFI_MP_SERVICES_PROTOCOL (up to 1024 cores). If there's a need, I was thinking about implementing a fallback to the older FirmwareMPService Protocol (but I couldn't test it, so I skipped). And last but not least, on RPi, well SMP is just working out-of-the-box (with fixed 4 cores) :-)

The bootboot structure had to be changed a bit, few fields were rearranged and bootboot.numcores came in (up to 65535 cores). Otherwise the structure is the same, 64 bytes architecture independent info, 64 bytes platform specific pointers (which already included ACPI and MP pointers), and the rest is the platform independent memory map.

Naturally I have updated the PDF documentation as well, and I've created an OSDev wiki page for BOOTBOOT. Happy OS development and I hope my loader will be useful to you too!

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Thu Apr 09, 2020 5:02 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 715
Hi All,

Lots and lots (and lots) of testing on real hardware. I'd like to say thanks to all the testers! Testing the multicore support under UEFI was extremely helpful.

Some minor modifications had to be made: instead of PIT, the BIOS version now uses the PS/2 port oscillator for delays. Also minor fixes in Multiboot and Linux boot protocol support, they both work perfectly now.

On Raspberry Pi, model 3 was working already, but now model 4 support has been added too. Since the firmware code changed, multicore support had to be changed a bit too, but now SMP works with the latest firmware as well.

Furthermore, I've added sample bootable images and a very simple image creator tool, that can generate hybrid GPT/ISO9660 images with your inird. I've also provided a Makefile with several rules for testing (booting the images from ROM, via BIOS, via GRUB, from UEFI CDROM etc. etc. etc.)

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Mon Jun 15, 2020 8:48 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 715
Hi All,

New features (sort of). Protocol level 2 is now implemented in the reference implementations (UEFI and Raspberry Pi, BIOS remained at level 1). This means you are no longer tied to static link addresses, you are free to move them around (in the higher half -1G to 0 range). The loader will parse your ELF or PE kernel's symbols to find the addresses where to map the data. The affected labels:
  • mmio - virtual address of the MMIO area (on platforms that supports that),
  • fb - virtual address of the linear framebuffer,
  • bootboot - the main information structure,
  • environment - the arguments to your kernel in an UTF-8 string
  • your kernel's segment (using Elf_Phdr->p_vaddr and PE_hdr->code_base).
There can be one loadable segment (concatenated code, data and bss sections) in the kernel. With level 2, the size limit for this is raised to 16M (in comparition level 1 has a limit of 2M for info, environment, code, data, bss and stack). That must be enough for monolithic kernels too. You must be careful where you put these, because these might overlap, and there's absolutely no checks (it is perfectly valid for a kernel to request mapping of bootboot struct and the environment string into the middle of its bss section for example.)

The repo contains a simple, dependency-free image creator tool, mkimg. With that now you can create ESP FAT partition images and hybrid, bootable GPT disk / CDROM / DVD images. This means all disk generation related issues can be solved using this single file utility, no third party tools needed. Just like the loaders, you are free to use this tool with your project.

And last but not least, besides of the C example kernel, now there's a Rust example kernel that you can use as a skeleton for your own kernel.

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Thu Jun 18, 2020 4:06 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 715
Hi All,

I've replaced the quick and dirty mkimg tool with a proper, fully featured mkbootimg. This one is also dependency-free, with precompiled single portable executables available for Windows, MacOSX and Linux.

It receives a JSON configuration file as input, describing the disk you wish to create, and then it saves the resulting disk image. For example:
Code:
{
    "diskguid": "00000000-0000-0000-0000-000000000000",
    "disksize": 128,
    "align": 1024,
    "config": "initrd.dir/sys/config",
    "initrd": { "type": "tar", "gzip": true, "directory": "initrd.dir" },
    "iso9660": 1,
    "partitions": [
        { "type": "boot", "size": 16 },
        { "type": "ntfs", "size": 16, "name": "Win Exchange" },
        { "type": "ext4", "size": 16, "name": "Linux Exchange" },
        { "type": "00000000-0000-0000-0000-000000000000", "size": 32, "name": "MyOS usr", "file": "usrpart.bin" },
        { "type": "00000000-0000-0000-0000-000000000000", "size": 32, "name": "MyOS var", "file": "varpart.bin" }
    ]
}
Numbers are in Megabytes, except "align", which is in Kilobytes (align 0 means sector alignment for partitions). This tool can create:
  • A compressed initrd image from a directory (currently supports cpio and tar, but easily expandable)
  • An ESP FAT boot partition with all the necessary files (including the newly created initrd) and boot code in VBR
  • An MBR / GPT / ISO9660 hybrid partitioning table with the boot partition on it
  • When creating a hybrid image, it also ensures that the root directory, the fat table, and the clusters on the boot partition are all 2048 bytes aligned
  • Optionally it can add any number of partitions (well, up to 248), and it can fill them up using partition images
As a bonus, it also checks your kernel for BOOTBOOT compatibility, gives detailed error messages, and determines the architecture from your executable. It has all the binaries included, so no additional files needed (this includes the Raspberry Pi firmware files too, along with the Broadcom licence).

Creating multiarch images also possible simply by using an array:
Code:
    "initrd": { "type": "tar", "gzip": true, "directory": [ "initrd.x86", "initrd.arm" ] },
This will create two initrds, one for each arch and it will put both loaders for them into the boot partition (the code is written in a way that it can support many architectures, however there's only loader for x86_64 and AArch64, so right now it is limited to two).

The generated images were tested with: FAT16/32: fsck.vfat, TianoCore UEFI; GPT: fdisk and gdisk's verify function. All green. :-)

Hope this will be useful to some of you,
bzt


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Thu Jun 18, 2020 8:28 am 
Offline
Member
Member

Joined: Thu May 17, 2007 1:27 pm
Posts: 730
bzt wrote:
There can be one loadable segment (concatenated code, data and bss sections) in the kernel. With level 2, the size limit for this is raised to 16M (in comparition level 1 has a limit of 2M for info, environment, code, data, bss and stack). That must be enough for monolithic kernels too. You must be careful where you put these, because these might overlap, and there's absolutely no checks (it is perfectly valid for a kernel to request mapping of bootboot struct and the environment string into the middle of its bss section for example.)

Out of curiosity, why did you pick this design? Do you map that segment as RWX? That prevents many useful sanity checking ("security") features from working, in particular WP and NX. WP and NX have probably caught more memory bugs in Managarm than any other measures combined. (I mean sure, one can enable it retroactively by some linker script hacks but that seems unnecessarily clunky for something that just amounts to setting two bits in the bootloader).

_________________
managarm: Microkernel-based OS capable of running a Wayland desktop (Discord: https://discord.gg/7WB6Ur3). My OS-dev projects: [mlibc: Portable C library for managarm, qword, Linux, Sigma, ...] [LAI: AML interpreter] [xbstrap: Build system for OS distributions].


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Thu Jun 18, 2020 10:58 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 715
Hi,

Thanks for checking out! The answer is in the documentation, but thanks for asking!

Korona wrote:
Out of curiosity, why did you pick this design? Do you map that segment as RWX?
It uses one boot segment for several reasons:

First, for simplicity. This is also required by many other boot loaders (all that works with raw binaries this is actually a must otherwise you can't convert ELFs to raws).
Second, BOOTBOOT can load PE32+ kernels too, and PE doesn't have the concept of multiple, configurable load segments (there's a segment pointer for code and one for the data in the header, that's it).
Third, there's no point in overcomplicating because your kernel will switch away from identity mapping and will use its paging tables as soon as possible anyway.
Fourth, BOOTBOOT is a multiplatform loader, and it provides the same environment on all platforms. It cannot and should not rely on specific CPU features, it tries to keep it as minimal as possible so that it can support more platforms. (For example AArch64 has WNX bit too, but x86 doesn't. If BOOTBOOT were about to utilize that, then it couldn't load kernels on x86; not with the same environment that is).

The Multiboot specification is way too x86 related, and you simply can't use that on other platforms. As a result, loaders on other platforms create their own, unofficial and incompatible versions of the Multiboot spec (read this for example). I put a lot of effort in the BOOTBOOT Specification so this can never happen to it. As it focuses on the smallest common denominator among platforms, it's environment should be available on all platforms, no need for tweaking the spec.

Korona wrote:
That prevents many useful sanity checking ("security") features from working, in particular WP and NX.
No, not really. My kernel, which is loaded using BOOTBOOT does take advantage of both WP and NX security bits (and on AArch64 WNX too), so it is doable just fine. From the BOOTBOOT User's Manual, in section "Machine State":
Quote:
If a kernel wants to separate it's code on a read-only segment and data on a non-executable segment for security, it
can override the page translation tables as soon as it gains control. BOOTBOOT Protocol does only
handle one loadable segment for simplicity (called boot in the example linker script, see Appendix).
Providing memory security is not a job for a bootloader. Your kernel will drop identity paging and will use it's own tables anyway. It will drop them for sure by the time it creates its first process. So why should the loader complicate things? I like simple. This is similar how GDT works (with Grub too). It is set up because it's needed, but your kernel should set a known GDT according its needs as soon as possible anyway.

In short, the bootloader's job is not to provide full functionality, rather to provide the bare minimum to get the system going. For example, BOOTBOOT allocates only 1k stack for your kernel per core, because it doesn't want to tell you how you should organize your memory. 1k is not enough for your kernel that's for sure, but it is surely enough to set up paging and stacks the way your kernel wants them, and that's about it.

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Thu Jun 18, 2020 12:19 pm 
Offline
Member
Member
User avatar

Joined: Fri Nov 22, 2019 5:46 am
Posts: 212
Great job!

But why do you call it "micro-kernel" loader? It's seems like unnecessary modesty to me. You could load nearly any kernel, don't you?

And what platforms is it available for so far? If I understood it correctly: Legacy-BIOS PCs, UEFI PCs, RaspPi 3 and 4. Correct?

Greetings
Peter

_________________
https://wiki.osdev.org/User:PeterX


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Thu Jun 18, 2020 7:36 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 715
PeterX wrote:
Great job!
Thanks!
PeterX wrote:
But why do you call it "micro-kernel" loader? It's seems like unnecessary modesty to me. You could load nearly any kernel, don't you?
Well, yes, it can load statically linked images, so it can load monolithic kernels too. I call it micro-kernel loader because it has a neat feature that no other boot loader: it loads an initrd into the memory with a bunch of files, then it locates the kernel amongst them (so the kernel is not a separate file as with GRUB, just a regular file inside the initrd). This suits primarily the needs of a micro-kernel, which requires to load several files on boot before it could access the disks.
PeterX wrote:
And what platforms is it available for so far? If I understood it correctly: Legacy-BIOS PCs, UEFI PCs, RaspPi 3 and 4. Correct?
Yes, for disks, USB storages and SD cards. Plus it can also boot from a CDROM via El Torito (both legacy BIOS and UEFI), and from ROM (via BIOS Boot Specification and under UEFI as Option ROM, this is useful for embedded systems); you can load it as a Linux kernel (using the Linux/x86 boot protocol, e.g. qemu -kernel), and last but not least, via GRUB, because it supports Multiboot too. Check out https://gitlab.com/bztsrc/bootboot/tree/master/images, along with the example images there's a Makefile with lots of qemu commands, one for each boot option.

In the future if I could get decent documentation and my hands on a board to test with, then I would like to support more. Beageboard is definitely on the bucket list, for example. I was also thinking about UltraSparc64, but as it's market is shrinking rapidly I don't think it worth the effort any more. Making it to work with Macs also a possibility (so far I had only one roadblock, I don't know how to get the frame buffer address using UGA, but that's the only one thing. Everything else works).

Greetings
Peter[/quote]


Top
 Profile  
 
 Post subject: Re: BOOTBOOT Multi-platform Micro-kernel Loader
PostPosted: Fri Jun 19, 2020 9:41 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 715
Hi All,

Just a little heads-up. I've upgraded the image creator:
  • It's multilingual now and autodetects OS' language
  • If initrd is given as an image file which is compressed, now that's transparently uncompressed to look up the kernel inside
  • New initrd formats has been added, it supports hpodc cpio, POSIX ustar, James Molloy's initrd and FS/Z (in lack of an up-to-date specification and with no available image creator I had to abandon SFS. But if you somehow manage to create an image, you can include it with the "file" directive and you should still be able to boot from it.)
  • Many JSON tags made optional, the tool now calculates as much properties as it can. For example on a partition it's enough to specify "type" and "file".
  • You can also use the "directory" directive for partitions, meaning you can create partition images from directories on-the-fly. Currently supported: tar and FS/Z (easy to add new file system drivers, see documentation). Could have added cpio and jamesm too, but those play not well with 512 byte sectors.
  • I've updated the FS API to inform it if the image is generated for initrd or partition (this is needed for FS/Z, because it supports gaps in files, which must be avoided in initrds)
  • On platforms that supports it (that's basically all save Windows) symlinks are also parsed and generated into initrds and partition images. This depends on S_ISLNK macro and readlink() libc function.
Cheers,
bzt


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group