UEFI port of stage0
Go to file
Andrius Štikonas 8ee15ee90e Fix a typo. 2023-01-19 22:55:53 +00:00
Development Build final versions of hex2 and M1. 2022-11-26 18:26:19 +00:00
LICENSES Add high level prototype for hex0. 2022-07-06 17:53:13 +01:00
M2-Mesoplanet@4bf3e2eded Update M2-Mesoplanet. 2023-01-10 23:36:58 +00:00
M2-Planet@f02aaaf67b Update M2-Planet to fix macros with brackets. 2023-01-09 01:48:15 +00:00
M2libc@1139b2bbf5 Update M2-Planet to fix macros with brackets. 2023-01-09 01:48:15 +00:00
amd64 Minor update to comments. 2023-01-15 00:03:34 +00:00
bootstrap-seeds@491b485769 Update bootstrap-seeds submodule. 2022-10-11 21:21:10 +01:00
mescc-tools@3f94182467 Update M2-Planet to fix macros with brackets. 2023-01-09 01:48:15 +00:00
mescc-tools-extra@5a334713fa Update mescc-tools-extra. 2023-01-01 12:50:40 +00:00
.gitignore Add Makefile for creating images and launching qemu. 2022-08-07 11:38:45 +01:00
.gitmodules Add mescc-tools-extra. 2022-12-31 17:20:13 +00:00
Makefile Add Generate-amd64-answers make target to calculate checksums. 2022-12-31 18:18:54 +00:00
README.md Fix a typo. 2023-01-19 22:55:53 +00:00
amd64.answers Update M2-Mesoplanet. 2023-01-10 23:36:58 +00:00
kaem.amd64 Add support for spawning processes in kaem. 2022-12-08 21:22:07 +00:00



This is a port of stage0-posix (https://github.com/oriansj/stage0-posix) to UEFI. Its purpose is to start with a tiny binary seed that can be manually inspected and use it to build C toolchain and some extra tools.


make build/disk.img will create bootable images with stage0-uefi. You can use dd to write them on bootable media.

make qemu will create disk image and launch it in QEMU. To test this in QEMU, you need to provide some QEMU compatible UEFI implementation. In particular, you can use Tianocore's OVMF. make qemu is looking for it in a few paths where distros tend to install it. If not you can specify it via make qemu OVMF_IMG=/path/to/OVMF_CODE.fd

Minimal images

It is possible to create images without kaem-optional-seed.efi that contain only hex0-seed.efi. Those are a bit less automated but if your UEFI implementation lets you pass command line arguments, you can recreate kaem using just hex0.

  • make MINIMAL=1 qemu.
  • Open UEFI settings (often F2 but it varies from system to system).
  • Go to Add Boot option and pick appropriate boot volume that contains hex0-seed.efi.
  • Pick \bootstrap-seeds\UEFI\amd64\hex0-seed.efi.
  • Description can be anything, say kaem-seed.
  • Optional Data has to be hex0 amd64\kaem-minimal.hex0 EFI\BOOT\BOOTX64.efi.
  • Save boot entry and boot it. This will build kaem-minimal and exit.
  • Now boot normally from bootable media.


kaem-optional (Optional "shell")

kaem-optional is a trivial shell that can read list of commands together with their command line arguments from a file and executes them. It also supports line comments but has no other features.


hex0 is fairly trivial to implement and for each pair of hexadecimals characters it outputs a byte. We have also added two types of line comments (# and ;) to create a well commented lines like

    # :loop_options [_start + 0x6F]
    4839D3          ; cmp_rbx,rdx                 # Check if we are done
    74 14           ; je !loop_options_done       # We are done
    4883EB 02       ; sub_rbx, !2                 # --options

In the first steps we use initial hex0 binary seed to rebuild kaem-optional and hex0 from their source.

hex0 code is somewhat tedious to read and write as it is basically a well documented machine code. We have to manually calculate all jumps in the code.


This is the last program that has to be written in hex0 language. hex1 is a simple extension of hex0 and adds a single character labels and allows calculating 32-bit offsets from current position in the code to the label. hex1 code might look like

:a #:loop_options
    4839D3          ; cmp_rbx,rdx                 # Check if we are done
    0F84 %b         ; je %loop_options_done       # We are done
    4883EB 02       ; sub_rbx, !2                 # --options


hex2 is our final hex language that adds support for labels of arbitrary length. It also allows accessing them via 8, 16, 32-bit relative addresses (!, @, %) and via 16-bit or 32-bit ($, &) absolute addresses though only the former addressing mode is used in stage0-uefi.

    4839D3                ; cmp_rbx,rdx                        # Check if we are done
    74 !loop_options_done ; je8 !loop_options_done             # We are done
    4883EB 02             ; sub_rbx, !2                        # --options


catm allows concatenating files via catm.efi output_file input1 input2 ... inputN. This allows us to split shared code into separate files. We will first use it to append PE header to .hex2 files. Before this step PE header had to be included in the source file itself.


The M0 assembly language is the simplest assembly language you can create that enables the creation of real world programs with practical application. It includes only a single keyword: DEFINE and leverages the language properties of hex2 along with extending the behavior to populate immediate values of various sizes and formats.

Thus M0 code looks like

DEFINE cmp_rbx,rdx 4839D3
DEFINE je 0F84
DEFINE sub_rbx, 4881EB

    cmp_rbx,rdx                         # Check if we are done
    je %loop_options_done               # We are done
    sub_rbx, %2                         # --options


The cc_amd64 implements a subset of the C language designed in M0 assembly. It is somewhat limited subset of C but complete enough to make it easy to write a real C compiler written in the C subset that cc_amd64 supports.

At this stage we start using M2libc (https://github.com/oriansj/M2libc/) as our C library. In fact, M2libc ships two versions of C library. At this stage we use a single-file (bootstrap.c) C library that contains just enough to build M2-Planet.


This is the only C program that we build with cc_amd64. M2-Planet (https://github.com/oriansj/M2-Planet) supports a larger subset of C than cc_amd64 and we are somewhat closer to C89 (it does not implement all C89 features but on the other hand it does have some C99 features). M2-Planet also includes a very basic preprocessor, so we can use stuff like #define, #ifdef.

M2-Planet supports generating code for various architectures including x86, amd64, armv7, aarch64, riscv32 and riscv64.

M2-Planet is also capable of using full M2libc C library that has more features and optimizations compared to bootstrap version of M2libc.

M2libc hides all UEFI specific bits inside it, so that applications written for POSIX (such as M2-Planet) can run without any source modifications.

C versions of linker and assembler

We then build C version of hex2 (also called hex2) and C version of M0 called M1. These are more capable than their platform specific hex counterparts and are fully cross-platform. Thus we can now have the whole toolchain written in C.


We now build kaem which is a more capable version of kaem-optional and adds support for variables, environmental variables, conditionals, aliases. It also has various built-ins such as cd and echo.

M2-Planet (built against full M2libc)

We can now rebuild M2-Planet so that it itself can benefit from full M2libc.


M2-Mesoplanet is a preprocessor that is more capable than M2-Planet and supports #include statements. It can also launch compiler, assembler and linker, so we don't need to invoke them manually.


blood-elf is a tool that can generate a bit of debug info for POSIX binaries. It is not immediately useful for UEFI but it might be useful if one wants to build debuggable POSIX binaries on UEFI.


Some extra tools such as sha256sum, untar, ungz, unbz2, mkdir, rm and a few others.