Context
This post is about some of the ways in which an operating system / kernel can be made to boot as a UEFI application image (through UEFI firmware).
To be clear, let’s make sure we have our definitions straight:
- UEFI firmware: The vendor firmware itself, which may or may not support legacy BIOS options. On the OSDev side, we won’t really have control over this. For emulation, I’ll be using TianoCore’s OVMF (OvmfPkgX64) specifically. More setup instructions here.
- UEFI application image: A relocatable
PE/COFF
executable file which the UEFI firmware loads through a FAT partition, usually the file/EFI/BOOT/BOOTX64.EFI
on the filesystem.
With RTEMS, we use a cross-compiler toolchain (for eg. x86_64-rtems5-gcc
,
instead of the host computer’s gcc
). The toolchain is built automatically by
the RTEMS Source Builder (RSB),
which does cool things like pull in a release version of the various tools,
backports patches that the RTEMS community may need (that are not in a release
version yet), and compiles the entire toolchain. These tools (gcc
, binutils
,
etc.) are then used to compile all of the RTEMS kernel, link the kernel to user
applications, the testsuite, etc.
Options
Simplest: Use gnu-efi
, then use objcopy
to convert ELF to PEI
Likely the simplest option, and the one used by FreeBSD as well, and documented on the OSDev Wiki, is to:
- Build an ELF64 shared library including an
elf_main
function - Link it with
gnu-efi
’s libraries andcrt0
runtime - Use
objcopy
with the--target=efi-app-x86_64
/--target=pei-x86-64
x86_64-rtems5-objcopy
doesn’t support thepei-x86-64
target at the moment, but this is easy to add with this patch to bfd
- Put the PE image file on a FAT partition. Here’s a quick script I used.
- Boot the PE image with UEFI firmware now
- ALL GLORY TO THE HYPNOTOAD!
I’ve tested this process with a “hello world” UEFI image using the
x86_64-rtems5-*
toolchain and confirmed it works in QEMU.
(Reproduction steps:
- Use the RSB with my bfd patch to add the
pei-x86-64
target tox86_64-rtems5-objcopy
. (I’ll try to have this upstreamed based on discussions on the RTEMS developers mailing list.) - Download gnu-efi
- Run
make CC=x86_64-rtems5-gcc OBJCOPY=x86_64-rtems5-objcopy ARCH=x86_64
within the project - Run the
make-img.sh
script with the the path to thet.efi
test file (withingnu-efi-3.0.8/x86_64/apps/
at the moment) - Run
qemu-system-x86_64 --bios /path/to/OVMF.fd -net none -cpu qemu64 -drive file=/path/to/uefi.img,if=ide
)
Note: This will involve making gnu-efi
a part of the RTEMS build
process. I’m not sure where this will fit in yet and if there might be license
incompatibility issues here.
Use a tool such as iPXE
’s elf2efi
utility
The iPXE bootloader includes a utility that can convert an existing ELF to an
EFI bootable image. I haven’t explored this option much since the one using
objcopy
seemed more standardized and one that would need less maintenance
overall to me. There would also be licensing considerations, and we would have
to confirm that the tool runs on all hosts that RTEMS intends to support for
development.
https://github.com/joyent/ipxe/blob/master/src/util/elf2efi.c
Use an assembly-generated PE header for a “chimera” ELF?
The wimboot
project associated with iPXE
includes a prefix.S
file which
seems to generate the PE header
table automagically
in assembly, allowing us to possibly create a chimeric file which can act like a
PE - I’m not sure about the differences between ELF and PE and how compatible
they are, so I can’t speak to how viable this method is, but it seems like
something that has worked for some in the past; namely the Linux kernel uses a
technique called the EFI boot stub (or EFISTUB for short), using a header.S
file to masquerade itself as a PE/COFF
image. This
is documented here and
on the Arch Wiki here as well.
Given the possible license incompatibilities with the RTEMS project and the need to shave a PE file-format yak, I’m leaning away from this method.
P.S. - I’d like to say thanks to Chris Johns for helping me discover that the last 2 options were even a possibility. Thank you! :D
Conclusion: I’m leaning towards the first method, but I’d love to hear what you think about the options I’ve laid out, and for any others I may have missed. Hit me up! The RTEMS mailing list discussion thread for this blog post is here.
Important resources:
- https://wiki.osdev.org/UEFI
- https://wiki.osdev.org/UEFI_Bare_Bones
- http://sourceforge.net/projects/gnu-efi/
- https://github.com/joyent/ipxe/blob/master/src/util/elf2efi.c
- https://github.com/ipxe/wimboot/blob/master/src/prefix.S
- https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S
- https://www.kernel.org/doc/Documentation/efi-stub.txt