Wie wäre es mal mit einem eigenen Bootloader? Mit diesem Artikel geht es los und wir entwickeln unseren ersten eigenen kleinen Bootloader…
Quellcode für den Bootloader Version 1:
#include <efi.h>
#include <efilib.h>
// Das ist der Einstiegspunkt (wie die main-Funktion in normalen C-Programmen)
EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
// Initialisiert die UEFI-Hilfsbibliothek
InitializeLib(ImageHandle, SystemTable);
// 1. Bildschirm leeren
SystemTable->ConOut->ClearScreen(SystemTable->ConOut);
// 2. Text ausgeben (Das L vor dem String steht für UTF-16, was UEFI verlangt)
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hallo! Dein eigener Bootloader laeuft.\r\n");
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Druecke eine beliebige Taste zum Neustarten...\r\n");
// 3. Auf Tastendruck warten
UINTN EventIndex;
SystemTable->BootServices->WaitForEvent(1, &SystemTable->ConIn->WaitForKey, &EventIndex);
// 4. PC neu starten (Reset)
SystemTable->RuntimeServices->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
return EFI_SUCCESS;
}
Terminalbefehle für den Compiler:
- gcc -nostdlib -fno-builtin -fpic -fshort-wchar -mno-red-zone -I/usr/include/efi -I/usr/include/efi/x86_64 -DEFI_FUNCTION_WRAPPER -c bootloader.c -o bootloader.o
- ld -nostdlib -znocombreloc -shared -Bsymbolic -L/usr/lib -T /usr/lib/elf_x86_64_efi.lds /usr/lib/crt0-efi-x86_64.o bootloader.o -o bootloader.so -lefi -lgnuefi
- objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela -j .reloc –target=efi-app-x86_64 bootloader.so bootloader.efi
Befehl 1 erstellt die Datei bootloader.o. Befehl 2 erstellt die so-Datei und Befehl 3 die efi-Datei.
Vorbereitung des USB-Sticks:
Der USB-Stick wird mit Partitionsschema GPT als FAT32 formatiert (etwa per Gparted). Nun braucht es eine ganz bestimmte Ordnerstruktur auf dem Stick. Es braucht einen Hauptordner EFI und darin einen Unterordner BOOT. Der Pfad sieht dann so aus: `X:\EFI\BOOT\` (wobei `X` dein USB-Laufwerk ist).
Für den automatischen Start auf einem Standard-64-Bit-PC muss die efi-Datei, also die Datei die wir mit dem Compiler erstellt haben, zwingend BOOTX64.EFI heißen. Diese Datei wird dann in den Unterordner BOOT kopiert.
Der erste Praxistest für Bootloader 1:
Leider klappte es so noch nicht und der Bildschirm blieb schwarz. Als Testsystem diente hier übrigens ein altes H81T Mainboard von Asus.
Quellcode für den Bootloader Version 2:
#include <stdint.h>
// Wir definieren die UEFI-Typen einfach selbst
typedef unsigned long long UINTN;
typedef long long INTN;
typedef void *EFI_HANDLE;
typedef INTN EFI_STATUS;
#define EFI_SUCCESS 0
// Hier sagen wir dem Compiler, wie die UEFI-Funktionen aufgebaut sind
struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
typedef EFI_STATUS (__attribute__((ms_abi)) *EFI_TEXT_STR)(struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, const uint16_t *String);
typedef EFI_STATUS (__attribute__((ms_abi)) *EFI_TEXT_CLR)(struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This);
typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
void *Reset;
EFI_TEXT_STR OutputString;
void *TestString;
void *QueryMode;
void *SetMode;
void *SetAttribute;
EFI_TEXT_CLR ClearScreen;
} EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
typedef struct {
char Header[24];
void *FirmwareVendor;
uint32_t FirmwareRevision;
EFI_HANDLE ConsoleInHandle;
void *ConIn;
EFI_HANDLE ConsoleOutHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
} EFI_SYSTEM_TABLE;
// Der eigentliche Einstiegspunkt – komplett ohne externe Bibliotheken
__attribute__((ms_abi))
EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
// 1. Bildschirm leeren
SystemTable->ConOut->ClearScreen(SystemTable->ConOut);
// 2. Text ausgeben (uint16_t Cast sorgt für das richtige UTF-16 Format)
SystemTable->ConOut->OutputString(SystemTable->ConOut, (const uint16_t *)L"Hallo! Der unabhängige Bootloader läuft!\r\n");
// 3. Endlosschleife, damit der PC an dieser Stelle stehen bleibt und wir den Text sehen
while(1) {
__asm__("hlt");
}
return EFI_SUCCESS;
}
Installation des Ming-Compilers:
- sudo apt install gcc-mingw-w64-x86-64
Terminalbefehl für den Ming-Compiler:
- x86_64-w64-mingw32-gcc -shared -nostdlib -fno-builtin -fshort-wchar -mno-red-zone -Wl,-entry=efi_main -Wl,–subsystem,10 -o BOOTX64.EFI bootloader.c
Hier erstellen wir also direkt die EFI-Datei und auch mit dem richtigen Namen für den Praxistest.
Der erste Praxistest für Bootloader 2:
Mit Bootloader Version 2 und dem Ming Compiler hat es geklappt. Unser Bootloader zeigt eine kleine Textmeldung.