POSIX-UEFI

From OSDev.wiki
Jump to navigation Jump to search

POSIX-UEFI is a very lightweight developing environment to create UEFI applications, similar to GNU-EFI. The EDK2 is a large, complex environment with its own build system. POSIX-UEFI on the other hand is just a single, statically linked library (about 32k) and headers for compiling UEFI applications under Linux (and other POSIX compatible systems).

This tutorial will guide you through to create a simple Hello World UEFI application using this library.

Basic Concept

The concept is, EDK2's API (which gnu-efi tries to comply with) is horrible and ugly as hell. Why can't we just wrap all that up in a nice and friendly POSIX environment? Sure, we can! And that's exactly what POSIX-UEFI is for.

No cross-compiler needed like in the UEFI App Bare Bones tutorial (but if you have it, POSIX-UEFI will use it), and you can compile with both GNU gcc + ld and LLVM CLang + lld.

Requirements

Download POSIX-UEFI and copy the uefi directory into your source tree (about a dozen files, no more than 96K).

$ git clone https://gitlab.com/bztsrc/posix-uefi.git
$ cd (your project)
$ ln -s ../posix-uefi/uefi

Libraries

When you first time run make, it will create the following files in the "uefi/" directory:

  • crt0_x86_64.o: A CRT0 (C runtime initialization code) that will call your "main" function (note: no efi_main!!!).
  • libuefi.a: A small static library which provides the POSIX compatibility layer.

Headers

POSIX-UEFI provides everything in a single header file. It has defines and typedefs for almost everything you'll ever need (Simple File System Protocol, Console In/Out Protocols, Graphics Output Protocol, Serio IO Protocol, Block IO Protocol etc. etc. etc.), but it not complete, and never intended to be. The goal is to have all the defines needed for it's libc. Since it provides a POSIX layer, all the usual UEFI defines are renamed according to ANSI C standards, like EFI_MEMORY_DESCRIPTOR -> efi_memory_descriptor_t, UINTN -> uintn_t etc.

If you need some more header files, you can use the EDK2 or gnu-efi ones under /usr/include/efi. POSIX-UEFI will add those include paths as well, and its header file was carefully written so that it can work with and without those headers, no naming conflicts.

Linker Script

Don't care. POSIX-UEFI has your back covered and chooses the correct script for you.

Creating an EFI executable

The traditional "Hello, world" UEFI program is shown below, which we will compile using POSIX-UEFI in this tutorial.

main.c:

#include <uefi.h>

int main (int argc, wchar_t **argv)
{
  printf(L"Hello, world!\n");
  return 0;
}

As you can see, almost exactly the same as the traditional POSIX version. There's one notable difference, the UEFI environment uses wide characters, so you get the command line wchar_t, and all the string literals must be prefixed like this: L"".

Compiling and Linking

Create a minimalistic Makefile for your project:

Makefile:

TARGET = helloworld.efi

include uefi/Makefile

That's all. Now you can compile your project for UEFI by running make. To compile using LLVM toolchain, add "USE_LLVM=1" before the include. For a full list of available Makefile variables, see the documentation.

The point being, just don't care about the details. POSIX-UEFI takes care of everything, from figuring out whether you can use the host compiler or you need a cross one, or which flags has to be used for gcc and for CLang, etc. That's the point of POSIX-UEFI, to make your life easier.

As a result, you'll get "helloworld.efi" in your project's directory.

Calling Any Arbitrary UEFI Function

Unlike GNU-EFI, which has many many library functions to cover up the EFI ugliness, POSIX-UEFI is really lightweight and only provides two additional functions, no more. It provides the same globals like GNU-EFI (BS, RT, ST). Furthermore it gives you 'exit_bs' to leave the UEFI realm, and 'getchar_ifany' for non-blocking key press reads. Everything else can be accessed just like in EDK2, no need for "uefi_call_wrapper":

ST->ConOut->OutputString(ST->ConOut, buffer);

On the other hand, POSIX-UEFI provides standard libc API (hence its name). For example, opening a file goes just like under Linux, you can use fopen. Printing with printf, fprintf; using streams like stdin, stdout, stderr; allocating using malloc / free works the same way as expected on a POSIX compliant system.

All string formatting is done on wchar_t, so there's "%C" to print UTF-8 character, "%S" to print UTF-8 strings. There's an extra "%D" format to dump physical memory in hex.

See also

Articles

External Links