FF een EFI

Dit weekend hield ik mij bezig met het maken van een EFI applicatie. (U)EFI ((Unified) Extensible Firmware Interface) is een soort lite besturingssysteem, de opvolger van BIOS (Basic Input Output System). Het is net als BIOS een softwareomgeving die een paar simpele functies en interfaces biedt om bijvoorbeeld tekst op het scherm te tonen en toetsenbordinput te ontvangen en verwerken. Als je een moderne computer hebt is het vrijwel gegeven dat je elke dag een EFI programma runt als je hem opstart: de bootloader. De bootloader is een programma die het voorbereidende werk doet van het echte besturingssysteem (Linux, BSD, MacOS, Windows) door bijvoorbeeld de geheugenmappings van de CPU in te stellen.

Nou, het leek mij wel interessant om eens een "Hello world" te maken. Dat ging redelijk vanzelfsprekend, zolang je de tutorial van de OS dev wiki volgt. Althans, het begon goed.

Het eerste wat je moet doen is een emulator installeren, ik ging voor QEMU. Nu kun je natuurlijk je computer in het echt gewoon steeds opnieuw opstarten als je je EFI applicatie wil testen, maar dat is een erg tergzaam proces, waar ik geen zin in had. Je moet dan wel zorgen dat je een nep-schijf maakt die als FAT32 is geformateerd, want dat is waar EFI mee werkt, en dat je iets als OVMF (ik kan dat het beste beschrijven als een EFI runtime) inlaadt.

Vervolgens kun je C code compileren als EFI applicatie, maar ook dat is niet helemaal rechtdoorzee. Op Linux hebben programma's een ELF structuur maar op Windows en EFI is het PE+. Dat kun je een beetje zien als twee verschillende prefabgebouwsystemen, die niet compatibel zijn. Nou is het dus een onmogelijk gedoe om op Linux een PE+ executable te compileren met GCC (de C compiler die ik gebruik) dus haalde ik GNU-EFI erbij, die gebruikt zwarte magie om een ELF executable toch compatibel te laten zijn met een PE+ interface.

Nou, het resultaat mag er zijn!

Hmm, nee, dat werkt toch niet helemaal. Bleek dat ik mijn linker niet helemaal goed had ingesteld, en wat betreft die arme UEFI omgeving had mijn programma net zo goed bij wijze van spreken Chinees kunnen spreken.

Dat lijkt er meer op! En hier is de code die hierachter zit. UEFI geeft je een systemtable, dat is een soort catalogus waar allemaal functies en interfaces in staan, en GNU-EFI geeft wrapper functies om beknopt die functies en interfaces te gebruiken, zoals Print().

#include <efi.h>
#include <efilib.h>
 
EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable){
	InitializeLib(ImageHandle, SystemTable);
	Print(L"Hello, world!\n");
	return EFI_SUCCESS;
}

Oh ja, UEFI gebruikt UTF-16 (kots kots kots kots kots kots!!!). Dat wil zeggen dat tekens worden gecodeerd in blokken van 16 bits, maar dan nog kan 1 teken uit meerdere "blokken" (zogeheten codepoints) bestaan (bijvoorbeeld een familie-emoji, wat tot grappige taferelen kan leiden). Dus eigenlijk verspil je altijd wel 8 bits per teken als je het Latijnse alfabet en grofweg alleen leestekens op de toetsenbord gebruikt. En je hebt alsnog te maken met multi-byte encoding dingen, dus je kan niet doen text[23] en dan verwachten dat je het 22e (0-based indexing) teken hebt, nee, misschien zit je dan wel in de tweede helft van een emoji (zie vorige link). Alle nadelen van UTF-8 met alle nadelen van UTF-32 dus en geen van de voordelen. Zucht Microsoft!

Goed, dan nu het klassieke "press a key to continue" implementeren. Dit kreeg ik niet aan de praat, hoe ik het ook probeerde. Ik ben zelfs van compiler gewisseld (Clang) die wèl in één keer PE+ EFI kan targetten. Maar het mocht niet baten, wat ik ook probeerde, mijn programma liep vast en deed helemaal niks. En toen was mijn interesse ook wel weg, want ik heb tentamens deze week en dit heeft helemaal niks met de stof daarvan te maken.

De volgende keer ga ik het opnieuw proberen met EDK2, een development toolkit die er speciaal voor bedoeld is om UEFI applicaties te maken. De reden dat ik het niet direct daarmee deed is simpel: het is nogal een opgave om alles voor EDK2 op te zetten vergeleken met een simpel GCC commando, en waarom moeilijk doen als het ook makkelijk kan? Nouja, in dit geval kon het dus niet makkelijk.

2023-06-19 in blog #programmeren #C #UEFI