Rolling Your Own Bootloader: Difference between revisions

m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(30 intermediate revisions by 17 users not shown)
Line 1:
{{rating|2}}
Some people prefer to use their own software for everything, or wish to try their hand at coding a bootloader. This page attempts to describe what steps to take when you write your own bootloader. Before you start writing one, it is best that you know the background [[Bootloader|theory]].
 
== What and Why ==
=== Disclaimer ===
 
Okay. You are here because you don't want to use a mainstream boot loader. You may also want to code your own bootloader as a learning experience to better understand how they function. We also have pages on many [[GRUBBootloaders]], wedeveloped haveby [[Barethis bones]]community, andthere's the [[C++ Bare Bonesbones]] but still people complain we don't have a page explaining how the bootloader can be coded.
 
I won't try to give you full code that works because if that was what you were looking for, you'd be using one of the [[:Category:Bootloaders|premade bootloaders]] instead. This page plans to tell you what is needed and what could be wished in a bootloader, and optionally points at parts of the FAQ that can help you achieving the goals.
 
Whether or not you'll use your own bootloader or reuse an existing tool is completely up to you. If you get the feeling you don't understand a thing, make sure you read our page about the [[Boot sequenceSequence]] first.<br ;)/>
 
A good reason to have a custom bootloader would be a custom filesystem, though you could add support for your filesystem to [[GRUB]] or perhaps some other bootloader. (GRUB is designed to accommodate such additions, other bootloaders may not be.)
 
=== What you need to do ===
Line 21 ⟶ 23:
Since the bootloader runs in [[Real Mode]], it has easier access to BIOS resources and functions. Therefore it's a good place to perform [[How Do I Determine The Amount Of RAM|memory map detection]], [[Getting VBE Mode Info|detecting available video modes]], loading additional files etc. The bootloader will collect this information and present it in a way the kernel will be able to understand
 
== Loading ... Please wait ... ==
 
=== Where will you load your kernel ? ===
 
You will have to decide where in memory you are going to load your kernel. Your kernel generally depends on it.
Line 33 ⟶ 35:
Note that BIOS will still be unable to write to memory above 1MB, so you need to read stuff in a buffer below 1MB and then perform a rep movsd to place the data where they ultimately should go.
 
=== How will you find your kernel ? ===
 
The bits of your kernel are somewhere on some disk (presumably the [[Bootable Disk|booting disk]], but this is not mandatory). Question is: where on the disk ? Is it a regular file on a [[FAT|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 Images|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 every time you rewrite the kernel.
 
=== What else could you need to load ? ===
 
That mainly depends on what's in your kernel. Linux, for instance, requires an additional 'initrd' file that will contain the 'initialization process' (as user level). If your kernel is modular and if [[File Systems]] 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.
 
=== What if iI get beyond the 512 bytes of the boot sector ? ===
 
Use [[GRUB]] ;) -- nah, kidding ... just makeMake sure the first 512 bytes are able to load the rest of your loader and you're safe. Some do this with a separate "second stage" loader, others by really inserting a '512-bytes' break in their ASM code, making sure the rest of the loader is put after the bootsector (that is, starting at 0x7e00 ;).
 
=== What if I wish to offer the user the option to boot several 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 0x0000:0x7c00 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 0x0000:0x7c00, 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''. It doesn't really matter where you decide to relocate the boot sector as long as you don't overwrite the [[IVT]] (if IF in EFLAGS is set), the [[Memory Map (x86)#BIOS Data Area (BDA)|BDA]] or the [[Memory Map (x86)#Extended BIOS Data Area (EBDA)|EBDA]].
Line 53 ⟶ 55:
You see that with displaying 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, boot managers like that can become as complex as a simple OS (much like GRUB is, which offers reading from various filesystems, booting [[Multiboot]] kernels, chainloading, loading initrd ramdisks etc. etc. - such internals will not be addressed here.
 
=== How do I actually load bytes ===
 
[[BIOS]] interrupt 13h. Get info about it at [[Ralf Brown's Interrupt List]], make sure you know (the now outdated) floppies may fail one or two times, that you cannot read more than a track at once, and you're done.have Toto readuse fromCHS theaddressing hard drive,and you're probably want int 13h, ah=0x42, drive number 0x80. Details in the interrupt listdone.
 
To read from the hard drive (which is the preferred way these days, also used by CDROMs and USB-sticks), you probably want int 13h, ah=0x42, drive number 0x80 that uses simple [[LBA]] addressing. Details in the interrupt list.
 
If you need guidance, feel free to check [http://clicker.cvs.sourceforge.net/clicker/c32-lxsdk/kernel/src/sosflppy/lowlevel.asm?view=log lowlevel.asm]
 
 
Note also that most [[File Systems]] involve some conversion between allocation units (blocks/clusters) and physical "Cylinder:Head:Sector" values. Those conversions are simple once you know the ''sectors-per-track'' and ''heads'' counts. Check out [http://www.nondot.org/sabre/os/articles OSRC] for additional info. This is only relevant for outdated floppies; everything else, like hard drives, CDROMs, USB-sticks all use the simple LBA addressing scheme.
 
<pre>
Line 102 ⟶ 106:
To enter protected mode you should first disable interrupts and set global descriptor table. After it set PE bit of CR0:
 
<sourcesyntaxhighlight lang="asm">
mov eax,cr0
or eax,1
mov cr0,eax
</syntaxhighlight>
</source>
 
After it set registers and do a far jump to kernel.
If data selector is 10h, code selector is 8 and kernel offset is 10000h do:
<sourcesyntaxhighlight lang="asm">
mov ax,10h
mov ds,ax
Line 118 ⟶ 122:
mov ss,ax
jmp 8:10000h
</syntaxhighlight>
</source>
 
Notes:
* that in this case, the GDT will be ''temporary''. Indeed, the loader has no idea of what the kernel wants to do with the GDT, so all it can do is providing a minimal and let the kernel reload GDTR with an appropriate GDT later.
* it's common for the loader to keep interrupts disabled (the kernel will enable them later when an IDT is properly set up)
* 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 !
* it is possible to perform more initialization once protected mode is enabled and before kernel is loaded. This will, however, require that you mix 16 bits and 32bits code in a single object file, which can quickly become a nightmare too...
* it is very likely that your kernel does not start with an executable code, rather it has an ELF or PE header at 10000h. You'll have to parse that to get the entry point to jump to.
 
== You have long way to continue... ==
Now, you are very distant from using extern and call function for C Code. You will need to enable [[A20]], make something which reads images (so you can actually boot any .bin or .sys file), and so on.
 
== Help I'm Stuck! ==
The only way to figure out what's wrong with your boot loader is to debug it in a VM. You could probably print out variables, but the limited space makes this uneccesarily hard.
Also reading [[My_Bootloader_Does_Not_Work|common mistakes and gotchas]] may give you ideas about your issue.
 
[[Category:OS Development]]
[[Category:Tutorials]]
[[Category:Bootloaders]]
[[de:Eigener Bootloader]]
 
== A list of things you might want to do ==
 
* Setup 16-bit segment registers and stack
* Print startup message
* Check presence of PCI, CPUID, MSRs
* Enable and confirm enabled A20 line
* Load GDTR
* Inform BIOS of target processor mode
* Get memory map from BIOS
* Locate kernel in filesystem
* Allocate memory to load kernel image
* Load kernel image into buffer
* Enable graphics mode
* Check kernel image ELF headers
* Enable long mode, if 64-bit
* Allocate and map memory for kernel segments
* Setup stack
* Setup COM serial output port
* Setup IDT
* Disable PIC
* Check presence of CPU features (NX, SMEP, x87, PCID, global pages, TCE, WP, MMX, SSE, SYSCALL), and enable them
* Assign a PAT to write combining
* Setup FS/GS base
* Load IDTR
* Enable APIC and setup using information in ACPI tables
* Setup GDT and TSS