Hi,
I've been faced by the chicken&egg problem. If I try to write a specification, I think I need to reply & refer to all the posts. If I try to reply to all the posts, I keep on referring to the non-existent specification. I'll therefore draw up a rudimentary version of the specifications, over here.
bluemoon wrote:Let's say we support two boot mechanism: BIOS and UEFI.
That's the plan.
bluemoon wrote:For BIOS, there are few media types:
- HDD - Standard MBR will be loaded and chain to VBR_BIOS_HDD_{FS}
FDD - Boot sector will be loaded, VBR_BIOS_FDD_{FS}
iso9660 - Boot sector will be loaded, VBR_BIOS_CD_{FS}
PXE - VBR_BIOS_PXE_{FS}
For UEFI, the similar versions apply, but instead of VBR it is the boot application.
So, there will be VBR_{FIRMWARE}_{MEDIA}_{FS}, many versions of VBR, it basically does:
1. provide API to identify booting mechanism, ie. BIOS, UEFI
2. provide API to access media (wrapper to the firmware functions)
3. provide API to access file (specific to the FS)
(I will not go into implementation detail here, but it can be highly modulized)
A specific version of VBR_{FIRMWARE}_{MEDIA}_{FS} will be picked up in the supported matrix, upon "format".
So, the VBRs or UEFI boot application will then load the next stage, by convention let's make it /boot/boot.bin
The boot.bin will see an uniform interface independent of boot mechanism (BIOS or UEFI)
It will make use of the API setup previously, and
1. handle the necessary business logic(memory map, video mode, CPUID, pull in configuration, etc),
2. load additional resources
3. setup consistent enviroment and machine state,
4. then boot the kernel.
I have a similarly based design, and, without any further ado, here it is:
There exists roughly two stages. The general idea is that the first stage (hence called "Stage 1") would be firmware-dependent and boot-device-dependent. The next stage (hence called "Stage 2") would be both firmware-independent and boot-device-independant.
Stage 1 would first start of by doing everything which is firmware-dependent - this includes enabling A20, getting a memory map, getting VBE information, getting EDID information, finding the ACPI & MPS tables, switching to an identifiable text mode, and anything else I haven't thought of. It would then expose an API which would allow Stage 2 to access all file related functions (Open, Read, Close) and other firmware related functions (SwitchVideoMode, anything else I haven't thought of).
Stage 2 would first parse the config file. It would then do everything common - it would clean (is sanitate a word?) the memory map, find the best video mode and switch to it, make some clean & neat table to store all the data. It would then read the kernel & modules, switch on paging (or perhaps long mode), and jump to the kernel.
That is mostly an overview. The actual implementation details would be something like the following:
- Stage 1. The Stage 1 would have the purpose of doing all things firmware-specific and boot-device-specific. There'd be two versions of this:
- BIOS.
- For all things NOT-hardisk, I'll split Stage 1 into two stages (d'oh!) - Stage 1 & Stage 1.5. Stage 1 should do everything boot-device-specific, while Stage 1.5 would do everything firmware-specific. Stage 1 would provide an API (most probably in some register, so that implementation can vary) which would allow the later stages to Open, Read & Close files. This API should be generic. The idea is that different Stage 1s can be used with the same Stage 1.5. The location for these would be as follows:
- Floppy. For floppies, we'd support FAT 12/16/32 and the no-filesystem idea described by Brendan. In case a filesystem is present, FAT supports having some number of reserved sectors. All the stages (1, 1.5 & 2) should be stored in these reserved sectors. Since we can load the rest of the sectors for Stage 1 from these reserved sectors, the size of the Stage 1 isn't limited to 512 bytes.
- PXE.For PXE, the location of the file on disk shouldn't really matter, and it's size isn't limited either.
- CD.For CDs, if I recall correctly, ISO9660 doesn't put the bootsector at LBA 0, but somewhere around. Moreover, while the size of the boot file can be as large as you want, most firmwares support 2KiB best. The rest of the files would be in a BOOT directory.
Stage 1 would be therefore filesystem (err, whatever) aware. It would load Stage 1.5, pass the API and jump to it. Stage 1.5 would do everything firmware specific (set A20 pin, get memory map, etc). These two stages would operate in real mode.
- For all things harddisk, things start getting a bit confused (darn you to hell, filesystem designers). The most generic idea I could gather from all the posts, is still filesystem aware (can't do without it) and limited. The idea is that the MBR & VBR would both be custom. The MBR (512 bytes) would provide all hard disk specific functions (ReadSector, etc) and good enough error detection & reporting.
It'd then pass control to the VBR (512 bytes) which would provide all filesystem related functions (Open, Read, Close), and would be different for each filesystem. These both, together, form the Stage 1. These would load Stage 1.5, which would do everything firmware related. Obviously, this is severely limited. Doing disk-access in 512 bytes, and filesystem-access in 512 bytes - I'm not too sure how plausible this is (especially to do it at a good level, not something for a 512-byte competition or something) Again, everything's in real mode, here.
- UEFI. For UEFI, Stage 1 would exist as a single binary. As far as I've heard, if it's a 32-bit PE, then it'd be loaded in 32-bit protected+paging mode. This is what we'd do, then (for reasons described later). This shouldn't be tough - again, I'm not entirely familiar with UEFI, but I think it provides a FAT partition in every case (even CDs), so it should be generic. If anyone has more knowledge on this, please share.
- Stage 2. Stage 2 would do everything not related with the firmware. It'd be in 32-bit flat mode (no paging), which is why we prefer 32-bit protected+paging in the UEFI loader. Obviously, there'd be change in modes a lot - from 32-bit to real mode, or from no-paging to paging. These switches would be handled by lower modes (Stage 1), and higher modes shouldn't care about them. There'd be a minimalistic configuration file, since most of the information should be specified by some sort of a binary header in the kernel. A file is still needed for things like module list, etc. Please note that the Stage 2 would be same for UEFI & BIOS. It'd load the kernel, parse everything (and stuff), switch modes finally, and jump to the kernel.
I've missed a lot of stuff in there. I haven't covered GPT at all, since I lack intimate knowledge on it. I guess someone could assist. Stuff on UEFI is lacking, and I've assumed a lot for it. Again, this should be covered as we progress.
I'll now reply to the rest of the posts (and hopefully, it should be easier). Let the criticism flow!
Regards,
Shikhin