Bootloader

From OSDev.wiki
Revision as of 06:13, 10 September 2008 by Viral (talk | contribs) (Useful link section added)
Jump to navigation Jump to search

A bootloader is a program written to load a more complex kernel. Implementation details are gathered in Rolling Your Own Bootloader

What does a bootloader do

The bootloader ultimately has to:

  • Bring the kernel (and all the kernel needs to bootstrap) in memory
  • Provide the kernel with the information it needs to work correctly
  • Switch to an environment that the kernel will like
  • Transfer control to the kernel

On the x86, the bootloader runs in Real Mode. Consequently it has easy access to BIOS resources and functions. Therefore it's a good place to perform memory map detection, detecting available video modes, loading additionnal files etc. The bootloader will collect this information and present it in a way the kernel will be able to understand

Loading your kernel

The bits of your kernel are somewhere on some disk (presumably the booting disk, but this is not mandatory). Question is: where on the disk ? Is it a regular file on a FAT-formatted floppy ? is it a collection of consecutive sectors in the "reserved area" of the FAT12 floppy (in which case you may need a dedicated tool to format the disk and install the kernel on it) ? Or is the floppy simply left unformatted and kernel pasted directly with a disk image tool

All the above options are possible. Maybe the one i'd choose myself would be to reserve enough space on a FAT12 floppy to store the list of sectors used by the kernel file. The "advantage" of being fully-FAT12 is that you don't need to re-write the bootsector everytime you rewrite the kernel.

What needs to be loaded mainly depends on what's in your kernel. Linux, for instance, requires an additionnal 'initrd' file that will contain the 'initialization process' (as user level). If your kernel is modular and if Filesystems are understood by some modules, you need to load the modules along with the kernel. Same goes for 'microkernel services' like disk/files/memory services, etc.

Giving the kernel its information

Some kernels require some extra information to run. For example, You'll need to tell Linux the root partition to start from. Pretty useful information to have is a map of the address space - effectively a map of where physical memory is and where not. Other popular queries regard video modes.

In general, anything that involves a BIOS call are easier to do in Real Mode, so better do them while in real mode than trying to come back to real mode for a trip later.

Establishing an environment

Most kernels require protected mode. For these kernels you'll have to

before giving control to the kernel.

It's common for the loader to keep interrupts disabled (the kernel will enable them later when an IDT is properly set up).

Note: give yourself the time about thinking whether you'll enable paging now or not. Keep in mind that debugging paging initialization code without the help of exception handlers may quickly become a nightmare !

Bootloader design

Virtually any bootloader follows a common design.

Single Stage Bootloader

A single stage bootloader consists of a single file that is loaded entirely by the BIOS. This image then performs the steps described above to start the kernel. However, on the x86 you are usually limited to 512 bytes for a first stage (An exception is no-emulation El Torito), which is not much. Also, a lot of this size may be dedicated to bios structures and FAT headers, which leaves even less space to work with

Two-Stage Bootloader

A two-stage bootloader actually consists of two bootloaders after each other. The first being small with the sole purpose of loading the second one. The second one can then contain all the code needed for loading the kernel. GRUB uses two (or arguably, three) stages.

Mixed Bootloader

Another way to avoid the 512-byte barrier is to split the bootloader in two parts, where the first half (512 bytes) can load the rest. This can be achieved by inserting a '512-bytes' break in the ASM code, making sure the rest of the loader is put after the bootsector.

Booting multiple OSes

The easiest way to boot another OS is a mechanism called chainloading. Windows stores something akin to a second-stage bootloader in the boot sector of the partition it was installed in. When installing Linux, writing e.g. LILO or GRUB to the partition boot sector instead of the MBR is also an option. Now, the thing your MBR bootsector can do is to relocate itself (copying from 0x07c0:0x0000 to, traditionally, 0x0060:0x0000), parse the partition table, display some kind of menu and let the user chose which partition to boot from. Then, your (relocated) MBR bootsector would load that partition boot sector to 0x07c0:0x0000, and jump there. The partition boot sector would be none the wiser that there already was a bootsector loaded before, and could actually load yet another bootsector - which is why it's called chainloading.

You see that with diplaying a menu in some intelligible way and accepting keystrokes, such a multi-option bootloader can get quite complex rather quickly. We didn't even touch the subject of booting from extended partitions, which would require sequentially reading and parsing multiple extended partition tables before printing the menu.

Taken to the extreme, bootmanagers like that can become as complex as a simple OS, GRUB being a good example: It offers reading from various filesystems, booting Multiboot kernels, chainloading, loading initrd ramdisks etc. etc.

Useful links

Writing Hello World Bootloader