Die Fehlersuche überspringen wir elegant und nutzen ab jetzt “gnu-efi“ für unseren Bootloader…
Quellcodes und Troubleshooting:
Die Fehlersuche führte nach dem letzten Quellcode aus dem vorherigen Artikel zu dutzenden weiteren Versuchen mit angepasstem Quellcode. Da alle diese Versuche leider erfolglos geblieben sind, geht es heute mit einer anderen Strategie weiter.
Wir wechseln jetzt auf “gnu-efi“ und damit soll es viel einfacher werden. Außerdem installieren wir mit Qemu einen Emulator für schnellere Tests unseres Bootloaders.
Installation von “gnu-efi“ und Qemu:
- sudo apt install -y gnu-efi qemu-system-x86 qemu-utils ovmf build-essential
Quellcode für den Bootloader Version 3:
#include <efi.h>
#include <efilib.h>
EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
InitializeLib(ImageHandle, SystemTable);
// Begrüßung auf dem Bildschirm
Print(L"Bootloader erfolgreich geladen!\n");
Print(L"Versuche OpenVolume() via gnu-efi Wrapper...\n");
EFI_LOADED_IMAGE *LoadedImage;
EFI_STATUS Status;
// 1. Hole das LoadedImage-Protokoll für unseren eigenen Bootloader
Status = uefi_call_wrapper(SystemTable->BootServices->HandleProtocol, 3,
ImageHandle, &LoadedImageProtocol, (void **)&LoadedImage);
if (EFI_ERROR(Status)) {
Print(L"Fehler beim Laden des LoadedImage-Protokolls.\n");
return Status;
}
// 2. Hole das SimpleFileSystem-Protokoll vom Quell-Laufwerk
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
Status = uefi_call_wrapper(SystemTable->BootServices->HandleProtocol, 3,
LoadedImage->DeviceHandle, &FileSystemProtocol, (void **)&Volume);
if (EFI_ERROR(Status)) {
Print(L"Fehler: Dateisystem konnte nicht geoeffnet werden.\n");
return Status;
}
// 3. Das magische OpenVolume() ohne Register-Mischmasch!
EFI_FILE_HANDLE Root;
Status = uefi_call_wrapper(Volume->OpenVolume, 2, Volume, &Root);
if (EFI_ERROR(Status)) {
Print(L"OpenVolume() fehlgeschlagen!\n");
} else {
Print(L"Erfolg! OpenVolume() hat fehlerfrei funktioniert.\n");
// Hier schließt man das Handle im echten Leben wieder, aber für den Test reicht es
}
// Kurz warten, damit wir die Ausgabe im Emulator auch sehen
uefi_call_wrapper(SystemTable->BootServices->Stall, 1, 5000000); // 5 Sekunden
return EFI_SUCCESS;
}
Quellcode für Makefile:
Wir erstellen jetzt noch eine Datei mit dem Namen “Makefile“ mit folgendem Inhalt:
ARCH = x86_64
TARGET = bootloader.efi
CC = gcc
LD = ld
OBJCOPY = objcopy
# Pfade für gnu-efi (Standard unter Ubuntu/Debian)
EFI_INC = /usr/include/efi
EFI_INCS = -I$(EFI_INC) -I$(EFI_INC)/$(ARCH) -I$(EFI_INC)/protocol
EFI_LIB = /usr/lib
CFLAGS = $(EFI_INCS) -fno-stack-protector -fpic -fshort-wchar \
-mno-red-zone -Wall -DEFI_FUNCTION_WRAPPER
ifeq ($(ARCH),x86_64)
CFLAGS += -m64
endif
LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LIB)/elf_$(ARCH)_efi.lds \
-shared -Bsymbolic $(EFI_LIB)/crt0-efi-$(ARCH).o
LIBS = $(EFI_LIB)/libefi.a $(EFI_LIB)/libgnuefi.a
all: $(TARGET)
main.o: main.c
$(CC) $(CFLAGS) -c $< -o $@
bootloader.so: main.o
$(LD) $(LDFLAGS) $< -o $@ $(LIBS)
$(TARGET): bootloader.so
$(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym \
-j .rel -j .rela -j .reloc --target=efi-app-$(ARCH) $< $@
run: all
# Virtuelles Verzeichnis für QEMU bauen, das den UEFI-Pfad simuliert
mkdir -p iso/EFI/BOOT
cp $(TARGET) iso/EFI/BOOT/BOOTX64.EFI
# QEMU starten mit OVMF-Bios (Pfad ggf. anpassen falls Arch Linux: /usr/share/edk2/x64/OVMF.fd)
qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd -drive file=fat:rw:iso,format=raw -display gtk
clean:
rm -rf *.o *.so *.efi iso
Die Datei für den Bootloader nennen wir also “main.c“ und im Terminal starten wir den Compiler mit dem Befehl “make run“. Am Ende sollten wir unsere Datei “bootloader.efi“ erhalten haben und Qemu sollte sich öffnen und jetzt sollte es auch mit dem Hauptverzeichnis klappen…