« Hello, world! » en UEFI

Source: https://wiki.osdev.org/UEFI_ISO_Bare_Bones

Récupération du source pour EFI

curl -O http://ftp.debian.org/debian/pool/main/g/gnu-efi/gnu-efi_3.0v.orig.tar.xz
tar jxvf gnu-efi_3.0v.orig.tar.xz

Edition manuelle de lib/data.c, suppression des références à LibStubStriCmp, LibStubMetaiMatch, LibStubStrLwrUpr:

EFI_UNICODE_COLLATION_INTERFACE   LibStubUnicodeInterface = {
    NULL, //LibStubStriCmp,
    NULL, //LibStubMetaiMatch, 
    NULL, //LibStubStrLwrUpr,
    NULL, //LibStubStrLwrUpr,

Construction du bootloader

Fichier assembleur source:

#include <efi.h>
#include <efilib.h>
 
EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_STATUS Status;
    EFI_INPUT_KEY Key;
 
    /* Store the system table for future use in other functions */
    ST = SystemTable;
 
    /* Say hi */
    Status = ST->ConOut->OutputString(ST->ConOut, L"Hello World\r\n"); // EFI Applications use Unicode and CRLF, a la Windows
    if (EFI_ERROR(Status))
        return Status;
 
    /* Now wait for a keystroke before continuing, otherwise your
       message will flash off the screen before you see it.
 
       First, we need to empty the console input buffer to flush
       out any keystrokes entered before this point */
    Status = ST->ConIn->Reset(ST->ConIn, FALSE);
    if (EFI_ERROR(Status))
        return Status;
 
    /* Now wait until a key becomes available.  This is a simple
       polling implementation.  You could try and use the WaitForKey
       event instead if you like */
    while ((Status = ST->ConIn->ReadKeyStroke(ST->ConIn, &Key)) == EFI_NOT_READY) ;
 
    return Status;
}

Fichier Makefile:

name=uefihello
.PHONY: uefi-shell hello
all: uefi-shell hello

hello: BOOTX64.EFI OVMF.fd
BOOTX64.EFI: hello.c build_hello.sh
	./build_hello.sh

OVMF.fd: /usr/share/ovmf/OVMF.fd 
	cp $^ $@ 

clean:
	rm -f OVMF.fd BOOTX64.EFI *.o hdimage.bin fat.img
	(cd mkgpt; make clean)

run-shell: uefi-shell
	qemu-system-x86_64 -boot menu=on -bios OVMF.fd -net none

run-hello: hello
	qemu-system-x86_64 -boot menu=on -bios OVMF.fd -net none -hda hdimage.bin

Fichier Makefile (version Docker):

name=uefihello
.PHONY: uefi-shell hello container
all: uefi-shell hello

hello: BOOTX64.EFI OVMF.fd
BOOTX64.EFI: hello.c build_hello.sh container
	docker run -v `pwd`:/mnt $(name) /mnt/build_hello.sh /mnt

uefi-shell: OVMF.fd
OVMF.fd: container
	docker run -v `pwd`:/mnt $(name) cp /usr/share/ovmf/OVMF.fd /mnt

container: container.build
container.build: Dockerfile
	docker build -t $(name) .
	touch container.build

clean:
	rm -f OVMF.fd BOOTX64.EFI *.o hdimage.bin fat.img container.build

run-shell: uefi-shell
	qemu-system-x86_64 -boot menu=on -bios OVMF.fd -net none

run-hello: hello
	qemu-system-x86_64 -boot menu=on -bios OVMF.fd -net none -hda hdimage.bin

Dockerfile:

FROM debian
LABEL author="gas@mines.paris"
RUN apt-get update 
RUN apt-get install -y ovmf binutils-mingw-w64 gcc-mingw-w64-x86-64 xorriso mtools
RUN apt-get install -y git automake build-essential
RUN git clone https://github.com/jncronin/mkgpt.git
WORKDIR mkgpt
RUN automake --add-missing; exit 0
RUN autoreconf
RUN ./configure
RUN make
RUN pwd
CMD ["ls", "-l"]

Ecriture sur USB

Écriture sur une clef USB du bootloader sous Linux:

lsblk
sudo dd if=hellokernel.bin of=/dev/sdb