GNU GRUB (GRand Unified Bootloader) jest prawdopodobnie najpopularniejszym obecnie bootloaderem. Jest używany zarówno z systemami uniksopodobnymi (FreeBSD, Linux), jak również do uruchamiania hobbystycznych systemów operacyjnych. Wymaga on jednak wprowadzenia pewnych modyfikacji do jądra systemu, co postaram się przedstawić w tym artykule.
Modyfikacje te wprowadzają zgodność jądra systemowego ze standardem Multiboot, opracowanym przez zespół zajmujący się tworzeniem GRUB'a. Określa on format obrazu jądra systemowego, umożliwiający załadowanie go przez bootloader. Multiboot dostarcza także cennych informacji na temat stanu komputera, w jakim znajduje się on po wczytaniu kernela do pamięci. Więcej informacji na ten temat pod adresem [1].
Aby zapewnić zgodność systemu ze standardem Multiboot, kernel powinien zawierać specjalny obszar zwany Multiboot Header (nagłówek Multiboot). W przypadku jądra zapisanego w formacie ELF (format ten polecam, innymi nie będę się zajmował z powodu utrudnień z nimi związanych) przedstawia się on następująco:
Offset | Typ | Nazwa |
0 | u32 | magic |
4 | u32 | flags |
8 | u32 | checksum |
Można to zapisać w składni assemblera NASM w postaci:
align 4
multiboot_header:
;magic
dd 0x1BADB002
;flags
dd 3
;checksum
dd 0x0E4524FFB
Wartość pola magic jest ściśle ustalona i wynosi 0x1BADB002. Na jej podstawie bootloader wykrywa, że jest to system zgodny z Multiboot. W polu flags ustalone są parametry ładowania jądra systemu. Znaczenie bitów pola flags:
bit 0 ustawiony:
- ładuj system i wszystkie jego moduły na granicy stron, przydaje się, kiedy system używa stronnicowania
bit 1 ustawiony:
- dostarcz informacje na temat dostępnej pamięci operacyjnej przez Multiboot Information Structure
bit 2 ustawiony:
- dostarcz informacje na temat dostępnych trybów wideo (tekstowych i graficznych)
bit 16 ustawiony:
- używany z formatami innymi niż ELF, oznacza użycie rozszerzonego Multiboot Header zawierającego dodatkowe informacje na temat systemu
Na początek polecam wartość 3 dla pola flags. Pole checksum musi zawierać taką wartość, aby po dodaniu do niej wartości pola flags oraz pola magic wynikiem było zero.
Nagłówek Multiboot musi znajdować się w pierwszych 8 kilobajtach jądra systemu i znajdować się pod offsetem podzielnym przez 4, co zapewnione jest użyciem słowa kluczowego align 4.
Taki kod źródłowy (zawierający tylko dane) należy uzupełnić o kod wykonywalny. Trzeba dodać funkcję od której wykonanie ma się zacząć oraz kod wywołujący właściwy kod jądra systemu. Nie można także zapomnieć o wyeksportowaniu danej funkcji:
global _kpreboot ;Wyeksportowanie funkcji _kpreboot
extern kmain ;Funkcja kmain nie znajduje się w tym pliku
_kpreboot:
jmp exec_kmain
[...kod definiujacy naglowek Multiboot]
exec_kmain:
call kmain
Gotowy program należy zassemblować i zlinkować z kernelem, pamiętając o zmianie procedury początkowej systemu(entry point) na _kpreboot. Otrzymany obraz jądra systemu można ładować za pomocą bootloadera GRUB, ze stacji dyskietek lub dysku twardego.
Kompletny kod źródłowy:
multiboot.asm - Do zlinkowania z kernelem
GRUB przed załadowaniem i wykonaniem jądra systemu przygotowuje w miarę pewne środowisko. Należy do niego:
1. Odblokowanie linii adresowej A20 (zdjęcie ograniczenia adresowania do 20 bitów)
2. Przejście do trybu chronionego procesora
3. Ustawienie odpowiednich początkowych deskryptorów pamięci
4. Zablokowanie przerwań
5. Dostarczenie systemowi specjalnej struktury Multiboot Information Structure
[1] Dokumentacja standardu Multiboot