EFI Shell mit QEMU
« | 05 Dec 2021 | »Wenn man EFI
Apps entwickelt, möchte man sie natürlich auch testen.
Natürlich kann man jedes neue .efi
Binary auf einen USB-Stick kopieren und
auf einem realen PC über die EFI-Shell ausführen, aber “effizient” ist das nicht.
Der Emulator QEMU bietet mit dem TianoCore OVMF (Open Virtual Machine Firmware) Paket die Möglichkeit, eine EFI Shell Sitzung oder eine EFI-Boot auf dem lokalen System schnell nachzustellen.
Um eine fertige .efi
x64-App-Datei schnell testen zu können, braucht man
folgende Zutaten:
- Eine QEMU Installation mit dem
qemu-system-x86_64
Emulator - Eine Firmware-Datei mit EFI-Shell Support wie z.B. in
OVMF.fd
- Die Linux
mtools
um per Script ein virtuelles USB-Stick-Image zu basteln - Und ein Script, welches alle Aufrufe zusammenfasst.
QEMU
Den System Emulator QEMU findet man auf qemu.org zum Download, aber als OpenSource-Projekt kann man ihn auch direkt aus den Quellcodes bauen lassen.
Am einfachsten läuft die Installation jedoch über das Paketmanagement des Betriebssystems:
TianoCore Open Virtual Machine Firmware (OVMF)
Eigentlich brauchen wir nur eine Datei namens OVMF.fd
, die aus dem TianoCore
Projekt heraus gebildet wurde, damit wir eine virtuelle Maschine starten können,
die von einem virtuellen USB Stick aus unsere App als BOOTX64.EFI
startet.
Entweder sucht man sich dafür in den Weiten des Internet eine OVMF.fd
, oder
man klont sich das TianoCore GIT Projekt
und startet dessen Build-Prozess.
Eine hervorragende Anleitung dazu, die auch bei mir unter Windows in
WSL (Ubuntu 18.04)
funktioniert hat, findet man unter
fabianlee.org/2018/09/12/kvm-building-the-latest-ovmf-firmware-for-virtual-machines/
Hier die Kommandos, die ich erfolgreich ausführen konnte:
1## script from fabianlee.org: 2sudo apt-get install build-essential git uuid-dev iasl nasm python -y 3sudo apt-get install iasl -y 4mkdir -p uuefi; cd uuefi 5git clone git://github.com/tianocore/edk2.git 6cd edk2 7git submodule update --init 8source ./edksetup.sh 9make -C BaseTools/ 10build -a X64 -t GCC5 -b RELEASE -p OvmfPkg/OvmfPkgX64.dsc
Nach dem Durchlaufen der Build-Prozesse findet man unter
Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd
die Firmware-Datei, mit der man
QEMU
zur Ausführung von EFI-Apps bringen kann.
USB-Stack FAT Image mit mtools
EFI braucht am Ende einen Datenträger, von dem gebootet werden kann. Natürlich könnte man jetzt einen richtigen USB-Stick formatieren und die Dateien einspielen und dann ein binäres Image davon ziehen, aber das wäre extrem mühsam.
Linux liefert aber mit den mtools
einige sehr nützlich Programme mit,
die Dateisysteme “offline” bearbeiten können, und wie üblich ist es dabei
egal, ob es um reale Datenträger, oder nur Image-Dateien geht.
EFI erwartet eine FAT
Partition, in der im Unterverzeichnis /EFI/BOOT
die Datei BOOTX64.EFI
liegt.
Ein schönes Beispiel findet man auf
wiki.osdev.org/UEFI_App_Bare_Bones
Nachdem ich mit Windows und WSL arbeite, müssen die mtools
mit
sudo apt install mtools
zuvor installiert sein.
Danach kann man folgendes BATCH
Script nutzen:
In efi-boot.img
liegt dann ein Disketten-FAT-Image (hier mit 2.88 MB), in
welches die EFI-App als BOOTX64.EFI
integriert ist.
EFI-Apps sind ja normalerweise nicht mehrere Megabytes groß, womit das kleine
Laufwerk reicht. Wer mehr will, kann natürlich auch ganze virtuelle
Festplatten mit GUID-Partitionstabelle
und einer eigenen EFI-Partition anlegen.
Da das wsl
Kommando die Linux-Prozesse so startet, dass das
Arbeitsverzeichnis in WSL auf das Windows-Dateisystem zeigt, schreiben die
mtools
die Dateien in das Verzeichnis, in dem das BATCH Script ausgeführt
wurde.
QEMU mit EFI-Firmware und Image starten
Das finale magische Kommando lautet:
qemu-system-x86_64 -L OVMF_dir/ -pflash OVMF.fd -drive if=none,id=stick,format=raw,file=efi-boot.img -device nec-usb-xhci,id=xhci -device usb-storage,bus=xhci.0,drive=stick
Wichtig ist, dass die QEMU
Programme im PATH
liegen und dass sowohl OVMF.fd
und efi-boot.img
(oder wie auch immer man die Dateien nennen möchte) über
die qemu-system-x86_64
Parameter erreichbar sind.
Dann öffnet sich nämlich das Emulator-Fenster und wenn die eigene EFI-App richtig erstellt wurde, kann man ihren Output am Bildschirm sehen und daran Freude haben.
Ich habe mir daher meine gebaute OVMF.fd
fix zum Projekt hinzugefügt und
lasse eine von MSVC fertig gebaute EFI APP mit folgendem Script bei mir
ausführen:
1@ECHO OFF 2REM Starting in "\scripts" 3cd ..\bin 4REM Cleanup output directory 5rmdir /s /q efi_x64_env 6mkdir efi_x64_env 7REM Create EFI boot image 8copy ..\build\msbuild_efi64\deploy\bin\Release\gatecli.efi ^ 9 .\efi_x64_env\BOOTX64.EFI 10wsl dd if=/dev/zero of=./efi_x64_env/efi-boot.img bs=1k count=2880 11wsl mformat -i ./efi_x64_env/efi-boot.img -f 2880 :: 12wsl mmd -i ./efi_x64_env/efi-boot.img ::/EFI 13wsl mmd -i ./efi_x64_env/efi-boot.img ::/EFI/BOOT 14wsl mcopy -i ./efi_x64_env/efi-boot.img ^ 15 ./efi_x64_env/BOOTX64.EFI ::/EFI/BOOT 16REM Start QEMU session 17SET PATH=C:\Program Files\qemu;%PATH% 18qemu-system-x86_64 ^ 19 -L OVMF_dir/ -pflash OVMF.fd -drive ^ 20 if=none,id=stick,format=raw,file=.\efi_x64_env\efi-boot.img ^ 21 -device nec-usb-xhci,id=xhci ^ 22 -device usb-storage,bus=xhci.0,drive=stick
Fazit
Meine ersten Experimente waren echt mühsam. Jedes Kompilat wurde manuell auf einen USB-Stick kopiert und dann wurde ein Test-Mainboard damit gestartet.
Mit QEMU
und mtools
lässt sich eine EFI-App binnen Sekunden in einer
virtuellen Umgebung testen und das ist einfach nur geil!
Mit diesem Werkzeug werde ich also noch einige Zeit verbringen.