diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-31 02:55:10 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-31 02:55:10 +0200 |
| commit | 1963a0a6d7398a426d0701f90a5bc796e32899ae (patch) | |
| tree | 8294a30acd2c8bb0d0ff98ebaee14f9667cbcc6d | |
| download | rsboot-1963a0a6d7398a426d0701f90a5bc796e32899ae.tar.gz rsboot-1963a0a6d7398a426d0701f90a5bc796e32899ae.zip | |
feat(rsboot): initial commit — UEFI bootloader extracted from RNG
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | AGENTS.md | 82 | ||||
| -rw-r--r-- | Makefile | 48 | ||||
| -rw-r--r-- | fix_pe_simple.py | 64 | ||||
| -rw-r--r-- | main.c | 114 | ||||
| -rw-r--r-- | pad_for_optiplex.py | 69 | ||||
| -rw-r--r-- | uefi.h | 230 |
7 files changed, 612 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e828353 --- /dev/null +++ b/.gitignore | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | *.o | ||
| 2 | *.elf | ||
| 3 | /build/ | ||
| 4 | build/ | ||
| 5 | .pi/ | ||
diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a39c192 --- /dev/null +++ b/AGENTS.md | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | # AGENTS.md — rsboot (RaiserSoft Bootloader) | ||
| 2 | |||
| 3 | ## Identity | ||
| 4 | |||
| 5 | rsboot is a custom **UEFI** bootloader (PE32+, x64) that loads a Linux kernel + | ||
| 6 | initramfs from an ext2 partition using UEFI file system protocols, then | ||
| 7 | launches the kernel via `LoadImage`/`StartImage`. Single-stage design — | ||
| 8 | no MBR, no real mode, no stage1/stage2 split. | ||
| 9 | |||
| 10 | This is a standalone submodule of the [RNG](https://git.2137697.xyz/RaiserSoft/rng) project. | ||
| 11 | |||
| 12 | ## Code | ||
| 13 | |||
| 14 | - **Language:** C (freestanding, `-m64`, `-ffreestanding`, `-fPIE`, `-mabi=ms`). | ||
| 15 | - **Assembler:** none currently (no `.asm` files needed). | ||
| 16 | - **Linker:** `ld` with `-Ttext=0x1000 -e efi_main`, producing an ELF64 | ||
| 17 | intermediate (`main.elf`). | ||
| 18 | - **PE conversion:** `objcopy -O pei-x86-64 --subsystem=10` converts the ELF | ||
| 19 | to a PE32+ image, then Python scripts fix up headers and pad for | ||
| 20 | firmware compatibility. | ||
| 21 | - **Dependencies:** none. No libc, no CRT — just raw UEFI protocol calls. | ||
| 22 | - **Debug:** UEFI console output (`con_out->OutputString()` via serial + | ||
| 23 | framebuffer as configured by the firmware). | ||
| 24 | - **UEFI types:** `uefi.h` provides minimal stand-alone type definitions, | ||
| 25 | GUIDs, and protocol structs (system table, boot services, file system | ||
| 26 | protocol, loaded image protocol, etc.). | ||
| 27 | |||
| 28 | ## Source Layout | ||
| 29 | |||
| 30 | ``` | ||
| 31 | main.c — EFI entry point, loads kernel + initrd, starts image | ||
| 32 | uefi.h — UEFI types, GUIDs, protocol structs | ||
| 33 | Makefile — builds BOOTX64.EFI | ||
| 34 | fix_pe_simple.py — PE header fixup (ImageBase, DllChar, strip relocs) | ||
| 35 | pad_for_optiplex.py — PE padding for Optiplex 7010 firmware | ||
| 36 | ``` | ||
| 37 | |||
| 38 | ## Build | ||
| 39 | |||
| 40 | ```sh | ||
| 41 | make # produces ../build/bootloader/BOOTX64.EFI | ||
| 42 | ``` | ||
| 43 | |||
| 44 | The build pipeline: | ||
| 45 | |||
| 46 | 1. **main.o** — compiled with `-fPIE -fshort-wchar -mno-red-zone -mabi=ms` | ||
| 47 | 2. **main.elf** — linked as position-independent ELF64 starting at text 0x1000 | ||
| 48 | 3. **BOOTX64.EFI** — objcopy to `pei-x86-64`, subsystem=10 (EFI application) | ||
| 49 | 4. **fix_pe_simple.py** — sets ImageBase, marks RELOCS_STRIPPED (PIE code | ||
| 50 | uses only PC-relative addressing, so no fixups needed), sets NX | | ||
| 51 | DYNAMIC_BASE | HIGH_ENTROPY_VA, clears .reloc data directory and | ||
| 52 | checksum | ||
| 53 | 5. **pad_for_optiplex.py** — if the PE is smaller than ~160KB, pad to match | ||
| 54 | the size systemd-boot uses (some AMI Aptio firmware versions on | ||
| 55 | Optiplex 7010 reject smaller PE files) | ||
| 56 | |||
| 57 | ## Kernel Loading Flow | ||
| 58 | |||
| 59 | 1. Get our own `LoadedImage` protocol via `handle_protocol` | ||
| 60 | 2. Get `SimpleFileSystem` from the device handle | ||
| 61 | 3. `OpenVolume` → root directory | ||
| 62 | 4. Open `\bzImage`, read into allocated pages | ||
| 63 | 5. Open `\initramfs.cpio.gz`, read into allocated pages | ||
| 64 | 6. `LoadImage` (from memory, not device path) → kernel handle | ||
| 65 | 7. Set kernel's `load_options` (command line) on its `LoadedImage` protocol | ||
| 66 | 8. `StartImage` → kernel takes over | ||
| 67 | |||
| 68 | ## Key Conventions | ||
| 69 | |||
| 70 | - **GUIDs:** EFI_LOADED_IMAGE_GUID, EFI_SIMPLE_FILE_SYSTEM_GUID (defined | ||
| 71 | in `uefi.h` matching the UEFI 2.10 spec). | ||
| 72 | - **Boot device:** The device we were loaded from (our `LoadedImage.device_handle`). | ||
| 73 | - **File paths:** `\bzImage`, `\initramfs.cpio.gz` (backslash prefix for | ||
| 74 | UEFI device paths). | ||
| 75 | - **Cmdline:** `console=ttyS0,115200 console=tty0 earlyprintk=serial,ttyS0,115200 loglevel=3 rng_tty=ttyS0 initrd=\initramfs.cpio.gz` | ||
| 76 | - **Memory allocation:** UEFI boot services `AllocatePages` (type 6 = | ||
| 77 | EfiLoaderData) for kernel/initrd buffers; `AllocatePool` for cmdline copy. | ||
| 78 | - **Compilation quirks:** `-mabi=ms` is required so UEFI calling convention | ||
| 79 | (Microsoft x64 ABI) is used; `-fshort-wchar` makes `wchar_t` 16-bit to | ||
| 80 | match UEFI's CHAR16; `-mno-red-zone` because the red-zone can be clobbered | ||
| 81 | by UEFI firmware handlers. | ||
| 82 | - **Commits:** `feat(rsboot):` prefix. | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bd21c45 --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | # ============================================================================= | ||
| 2 | # Makefile — rsbl (UEFI bootloader for RNG) | ||
| 3 | # | ||
| 4 | # Produces build/bootloader/BOOTX64.EFI (PE32+ for UEFI) | ||
| 5 | # | ||
| 6 | # Uses -fPIE for position-independent code so the image works at any | ||
| 7 | # load address. The Optiplex 7010 firmware ignores ImageBase + relocs, | ||
| 8 | # so RELOCS_STRIPPED + padding to ~160KB is required. | ||
| 9 | # ============================================================================= | ||
| 10 | |||
| 11 | BUILD_DIR = ../build/bootloader | ||
| 12 | CC = gcc | ||
| 13 | LD = ld | ||
| 14 | OBJCOPY = objcopy | ||
| 15 | |||
| 16 | CFLAGS = -m64 -ffreestanding -nostdinc -nostartfiles -nodefaultlibs \ | ||
| 17 | -fno-builtin -fno-stack-protector -fPIE -fPIC \ | ||
| 18 | -fshort-wchar -mno-red-zone -mabi=ms -Qn \ | ||
| 19 | -fno-asynchronous-unwind-tables -fno-unwind-tables \ | ||
| 20 | -fno-exceptions \ | ||
| 21 | -I. -Wall -Wextra -Werror | ||
| 22 | |||
| 23 | LDFLAGS = -m elf_x86_64 -nostdlib -z norelro --no-dynamic-linker \ | ||
| 24 | -Ttext=0x1000 -e efi_main | ||
| 25 | |||
| 26 | .PHONY: all clean | ||
| 27 | |||
| 28 | all: $(BUILD_DIR)/BOOTX64.EFI | ||
| 29 | |||
| 30 | $(BUILD_DIR)/BOOTX64.EFI: main.elf | ||
| 31 | @mkdir -p $(BUILD_DIR) | ||
| 32 | $(OBJCOPY) -O pei-x86-64 --subsystem=10:1.1 \ | ||
| 33 | --heap 0x100000,0x1000 \ | ||
| 34 | --stack 0x100000,0x1000 \ | ||
| 35 | $< $@ | ||
| 36 | python3 fix_pe_simple.py $@ | ||
| 37 | python3 pad_for_optiplex.py $@ | ||
| 38 | @echo " -> $@" | ||
| 39 | @ls -lh $@ | ||
| 40 | |||
| 41 | main.elf: main.o | ||
| 42 | $(LD) $(LDFLAGS) -o "$@" "$^" | ||
| 43 | |||
| 44 | main.o: main.c uefi.h | ||
| 45 | $(CC) $(CFLAGS) -c -o "$@" "$<" | ||
| 46 | |||
| 47 | clean: | ||
| 48 | rm -f *.o *.elf $(BUILD_DIR)/BOOTX64.EFI | ||
diff --git a/fix_pe_simple.py b/fix_pe_simple.py new file mode 100644 index 0000000..77b1366 --- /dev/null +++ b/fix_pe_simple.py | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | """fix_pe_simple.py — PE header fixup for PIE-built UEFI bootloader | ||
| 3 | |||
| 4 | The -fPIE-built bootloader uses only PC-relative addressing, so it needs | ||
| 5 | zero absolute fixups. This script just sets proper PE flags: | ||
| 6 | |||
| 7 | - ImageBase (env IMAGE_BASE, default 0x10000000) | ||
| 8 | - Characteristics = 0x022e (RELOCS_STRIPPED) | ||
| 9 | - DllChar = 0x0160 (NX | DYNAMIC_BASE | HIGH_ENTROPY_VA) | ||
| 10 | - Clears .reloc DD (no relocations needed) | ||
| 11 | - Clears checksum (not signed) | ||
| 12 | """ | ||
| 13 | |||
| 14 | import struct, os, sys | ||
| 15 | |||
| 16 | def fix_pe(path): | ||
| 17 | try: | ||
| 18 | with open(path, 'rb') as f: | ||
| 19 | d = bytearray(f.read()) | ||
| 20 | except IOError as e: | ||
| 21 | print(f" ERROR: cannot read {path}: {e}") | ||
| 22 | return False | ||
| 23 | |||
| 24 | po = struct.unpack('<I', d[0x3C:0x40])[0] | ||
| 25 | if d[po:po+4] != b'PE\x00\x00': | ||
| 26 | print(f" Not a PE file") | ||
| 27 | return False | ||
| 28 | |||
| 29 | coff = po + 4 | ||
| 30 | opt = po + 24 | ||
| 31 | |||
| 32 | magic = struct.unpack('<H', d[opt:opt+2])[0] | ||
| 33 | if magic != 0x20b: | ||
| 34 | print(f" Not PE32+") | ||
| 35 | return False | ||
| 36 | |||
| 37 | ib_env = os.environ.get('IMAGE_BASE', '0x10000000') | ||
| 38 | img_base = int(ib_env, 16) if ib_env.startswith('0x') else 0x10000000 | ||
| 39 | struct.pack_into('<Q', d, opt+24, img_base) | ||
| 40 | struct.pack_into('<H', d, coff+18, 0x022e) | ||
| 41 | struct.pack_into('<H', d, opt+70, 0x0160) | ||
| 42 | struct.pack_into('<I', d, coff+8, 0) | ||
| 43 | struct.pack_into('<I', d, coff+12, 0) | ||
| 44 | struct.pack_into('<I', d, opt+64, 0) | ||
| 45 | |||
| 46 | dd = opt + 112 | ||
| 47 | struct.pack_into('<II', d, dd + 5*8, 0, 0) | ||
| 48 | |||
| 49 | try: | ||
| 50 | with open(path, 'wb') as f: | ||
| 51 | f.write(d) | ||
| 52 | except IOError as e: | ||
| 53 | print(f" ERROR: cannot write {path}: {e}") | ||
| 54 | return False | ||
| 55 | |||
| 56 | print(f" Fixed: ImageBase={img_base:#010x} char=0x022e dll=0x0160 reloc=cleared") | ||
| 57 | return True | ||
| 58 | |||
| 59 | if __name__ == '__main__': | ||
| 60 | if len(sys.argv) < 2: | ||
| 61 | print(f"Usage: IMAGE_BASE=0x... {sys.argv[0]} <pe-path>") | ||
| 62 | sys.exit(1) | ||
| 63 | if not fix_pe(sys.argv[1]): | ||
| 64 | sys.exit(1) | ||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* ============================================================================= | ||
| 2 | * main.c — RNG UEFI bootloader (rsbl) — minimal smoke-test version | ||
| 3 | * | ||
| 4 | * Just prints to screen then loads kernel + initrd via LoadImage/StartImage. | ||
| 5 | * ============================================================================= */ | ||
| 6 | |||
| 7 | #include "uefi.h" | ||
| 8 | |||
| 9 | static const char16 kpath[] = u"\\bzImage"; | ||
| 10 | static const char16 ipath[] = u"\\initramfs.cpio.gz"; | ||
| 11 | /* same cmdline systemd-boot used */ | ||
| 12 | static const char16 base_opt[] = u"console=ttyS0,115200 console=tty0 earlyprintk=serial,ttyS0,115200 loglevel=3 rng_tty=ttyS0 initrd=\\initramfs.cpio.gz"; | ||
| 13 | |||
| 14 | static efi_guid lg = EFI_LOADED_IMAGE_GUID; | ||
| 15 | static efi_guid sg = EFI_SIMPLE_FILE_SYSTEM_GUID; | ||
| 16 | |||
| 17 | static void pr(efi_simple_text_output *c, const char16 *s) { c->output_string(c, s); } | ||
| 18 | static void ph(efi_simple_text_output *c, u64 v) { | ||
| 19 | char16 b[19] = u"0x0000000000000000"; | ||
| 20 | for (int i = 17; i >= 2; i--) { | ||
| 21 | u8 n = (u8)(v & 0xF); | ||
| 22 | b[i] = n < 10 ? u'0' + n : u'a' + n - 10; | ||
| 23 | v >>= 4; | ||
| 24 | } | ||
| 25 | c->output_string(c, b); | ||
| 26 | } | ||
| 27 | |||
| 28 | static u64 allocate_pages(efi_boot_services *b, u64 mt, u64 np, u64 *a) { | ||
| 29 | typedef u64 (*F)(u64, u64, u64, u64 *); | ||
| 30 | return ((F)b->allocate_pages)(0, mt, np, a); | ||
| 31 | } | ||
| 32 | static u8 *rf(efi_boot_services *b, efi_file_protocol *f, u64 *sz, u64 mx) { | ||
| 33 | u64 np = (mx + 4095) / 4096, a; | ||
| 34 | if (allocate_pages(b, 6, np, &a)) return 0; | ||
| 35 | u8 *d = (u8 *)a; | ||
| 36 | u64 r = mx; | ||
| 37 | if (f->read(f, &r, d) && r != mx) return 0; | ||
| 38 | *sz = r; return d; | ||
| 39 | } | ||
| 40 | |||
| 41 | u64 __attribute__((aligned(16))) efi_main(handle ih, efi_system_table *st) { | ||
| 42 | efi_simple_text_output *c = st->con_out; | ||
| 43 | efi_boot_services *b = st->boot_services; | ||
| 44 | u64 s; | ||
| 45 | |||
| 46 | pr(c, u"\r\n"); | ||
| 47 | pr(c, u"rsbl v0.1 -- RNG UEFI Bootloader\r\n"); | ||
| 48 | pr(c, u"\r\n"); | ||
| 49 | |||
| 50 | efi_loaded_image *our_li; | ||
| 51 | s = b->handle_protocol(ih, &lg, (void **)&our_li); | ||
| 52 | if (s) { pr(c, u" handle_protocol LI failed\r\n"); goto h; } | ||
| 53 | |||
| 54 | efi_simple_file_system *fs; | ||
| 55 | s = b->handle_protocol(our_li->device_handle, &sg, (void **)&fs); | ||
| 56 | if (s) { pr(c, u" SFSP not on device handle\r\n"); goto h; } | ||
| 57 | |||
| 58 | efi_file_protocol *rt; | ||
| 59 | s = fs->open_volume(fs, (void **)&rt); | ||
| 60 | if (s) { pr(c, u" open_volume failed\r\n"); goto h; } | ||
| 61 | |||
| 62 | pr(c, u" [ loading kernel\r"); | ||
| 63 | efi_file_protocol *f; | ||
| 64 | s = rt->open(rt, &f, (char16 *)kpath, 1, 0); | ||
| 65 | if (s) { pr(c, u" [FAIL] loading kernel\r\n"); goto h; } | ||
| 66 | u64 ks; u8 *kd = rf(b, f, &ks, 64*1024*1024); | ||
| 67 | f->close(f); | ||
| 68 | if (!kd) { pr(c, u" [FAIL] loading kernel\r\n"); goto h; } | ||
| 69 | pr(c, u" [ ok] loading kernel "); ph(c, ks); pr(c, u" bytes\r\n"); | ||
| 70 | |||
| 71 | pr(c, u" [ loading initrd\r"); | ||
| 72 | s = rt->open(rt, &f, (char16 *)ipath, 1, 0); | ||
| 73 | if (s) { pr(c, u" [FAIL] loading initrd\r\n"); goto h; } | ||
| 74 | u64 is; u8 *id = rf(b, f, &is, 32*1024*1024); | ||
| 75 | f->close(f); | ||
| 76 | if (!id) { pr(c, u" [FAIL] loading initrd\r\n"); goto h; } | ||
| 77 | pr(c, u" [ ok] loading initrd "); ph(c, is); pr(c, u" bytes\r\n"); | ||
| 78 | |||
| 79 | pr(c, u" [ starting image\r"); | ||
| 80 | handle kh = 0; | ||
| 81 | s = b->load_image(0, ih, 0, kd, ks, &kh); | ||
| 82 | if (s) { pr(c, u" [FAIL] LoadImage\r\n"); goto h; } | ||
| 83 | |||
| 84 | efi_loaded_image *kli; | ||
| 85 | s = b->handle_protocol(kh, &lg, (void **)&kli); | ||
| 86 | if (s) { pr(c, u" [FAIL] kernel image info\r\n"); goto h; } | ||
| 87 | kli->device_handle = our_li->device_handle; | ||
| 88 | |||
| 89 | u16 osz = 0; | ||
| 90 | while (base_opt[osz]) osz++; | ||
| 91 | osz = (osz + 1) * 2; | ||
| 92 | u16 *opts; | ||
| 93 | { | ||
| 94 | typedef u64 (*ap_t)(u8, u64, void **); | ||
| 95 | s = ((ap_t)b->allocate_pool)(0, osz, (void **)&opts); | ||
| 96 | } | ||
| 97 | if (s) { pr(c, u" [FAIL] cmdline alloc\r\n"); goto h; } | ||
| 98 | for (u16 i = 0; i < osz / 2; i++) opts[i] = base_opt[i]; | ||
| 99 | kli->load_options = opts; | ||
| 100 | kli->load_options_size = osz; | ||
| 101 | |||
| 102 | pr(c, u" [ ok] starting image\r\n"); | ||
| 103 | pr(c, u"\r\n"); | ||
| 104 | pr(c, u" Launching RNG/Linux ...\r\n"); | ||
| 105 | pr(c, u"\r\n"); | ||
| 106 | |||
| 107 | s = b->start_image(kh, 0, 0); | ||
| 108 | pr(c, u" kernel returned "); ph(c, s); pr(c, u"\r\n"); | ||
| 109 | |||
| 110 | h: | ||
| 111 | pr(c, u" Halt\r\n"); | ||
| 112 | while (1) asm volatile("hlt"); | ||
| 113 | return s; | ||
| 114 | } | ||
diff --git a/pad_for_optiplex.py b/pad_for_optiplex.py new file mode 100644 index 0000000..2d48552 --- /dev/null +++ b/pad_for_optiplex.py | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | """pad_for_optiplex.py — pad a UEFI bootloader PE for Optiplex 7010 | ||
| 3 | |||
| 4 | The Optiplex 7010 AMI Aptio firmware rejects PEs smaller than ~160KB | ||
| 5 | ("no bootable devices"). This pads the PE to match systemd-boot's size | ||
| 6 | and sets SizeOfImage accordingly. | ||
| 7 | """ | ||
| 8 | |||
| 9 | import struct, sys, os | ||
| 10 | |||
| 11 | def pad(path): | ||
| 12 | try: | ||
| 13 | with open(path, 'rb') as f: | ||
| 14 | d = bytearray(f.read()) | ||
| 15 | except IOError as e: | ||
| 16 | print(f" ERROR: cannot read {path}: {e}") | ||
| 17 | return False | ||
| 18 | |||
| 19 | po = struct.unpack('<I', d[0x3C:0x40])[0] | ||
| 20 | if d[po:po+4] != b'PE\x00\x00': | ||
| 21 | print(f" Not a PE file") | ||
| 22 | return False | ||
| 23 | |||
| 24 | opt = po + 24 | ||
| 25 | |||
| 26 | # Target size: 160256 bytes (matching systemd-boot) | ||
| 27 | target = 160256 | ||
| 28 | if len(d) >= target: | ||
| 29 | print(f" Already >= {target} bytes ({len(d)}), nothing to do") | ||
| 30 | return True | ||
| 31 | |||
| 32 | # Compute SizeOfImage: round current SizeOfImage to match file growth | ||
| 33 | cur_si = struct.unpack('<I', d[opt+56:opt+60])[0] | ||
| 34 | cur_file_size = len(d) | ||
| 35 | |||
| 36 | # The file is padded after all sections. SizeOfImage is the highest | ||
| 37 | # RVA + virtual size, rounded up to SectionAlignment. After padding | ||
| 38 | # the file, we need to increase SizeOfImage so the firmware sees a | ||
| 39 | # consistent image. | ||
| 40 | # | ||
| 41 | # Simple approach: set SizeOfImage = target - file_delta + old_si | ||
| 42 | # Actually: SizeOfImage already covers our sections at up to ~0x4000. | ||
| 43 | # We just need to make it large enough for the padded region. | ||
| 44 | # Use systemd-boot's SizeOfImage = 0x2d000 as reference. | ||
| 45 | file_growth = target - len(d) | ||
| 46 | new_si = ((cur_si + file_growth + 0xfff) // 0x1000) * 0x1000 | ||
| 47 | |||
| 48 | # Pad the file | ||
| 49 | d += b'\x00' * file_growth | ||
| 50 | |||
| 51 | # Update SizeOfImage in optional header | ||
| 52 | struct.pack_into('<I', d, opt+56, new_si) | ||
| 53 | |||
| 54 | try: | ||
| 55 | with open(path, 'wb') as f: | ||
| 56 | f.write(d) | ||
| 57 | except IOError as e: | ||
| 58 | print(f" ERROR: cannot write {path}: {e}") | ||
| 59 | return False | ||
| 60 | |||
| 61 | print(f" Padded: {len(d)} bytes (was {cur_file_size}), SizeOfImage = {new_si:#x}") | ||
| 62 | return True | ||
| 63 | |||
| 64 | if __name__ == '__main__': | ||
| 65 | if len(sys.argv) < 2: | ||
| 66 | print(f"Usage: {sys.argv[0]} <pe-path>") | ||
| 67 | sys.exit(1) | ||
| 68 | if not pad(sys.argv[1]): | ||
| 69 | sys.exit(1) | ||
| @@ -0,0 +1,230 @@ | |||
| 1 | /* ============================================================================= | ||
| 2 | * uefi.h — minimal UEFI types and protocols for the RNG bootloader | ||
| 3 | * | ||
| 4 | * Standalone, freestanding, no external headers. | ||
| 5 | * ============================================================================= */ | ||
| 6 | |||
| 7 | #ifndef UEFI_H | ||
| 8 | #define UEFI_H | ||
| 9 | |||
| 10 | /* ---- basic types ---- */ | ||
| 11 | typedef unsigned char u8; | ||
| 12 | typedef unsigned short u16; | ||
| 13 | typedef unsigned int u32; | ||
| 14 | typedef unsigned long long u64; | ||
| 15 | /* UEFI CHAR16 — wchar_t must be 16-bit (use -fshort-wchar). | ||
| 16 | * GCC by default makes wchar_t = int (32-bit). -fshort-wchar makes | ||
| 17 | * it unsigned short (16-bit) to match UEFI's CHAR16. | ||
| 18 | * We don't include <stddef.h> (freestanding), so define our own. */ | ||
| 19 | typedef __CHAR16_TYPE__ char16; | ||
| 20 | typedef signed short int16; | ||
| 21 | typedef long long int64; | ||
| 22 | typedef void *handle; | ||
| 23 | |||
| 24 | /* ---- EFI status codes ---- */ | ||
| 25 | #define EFI_SUCCESS 0 | ||
| 26 | #define EFI_LOAD_ERROR ((u64)1) | ||
| 27 | #define EFI_INVALID_PARAMETER ((u64)2) | ||
| 28 | #define EFI_UNSUPPORTED ((u64)3) | ||
| 29 | #define EFI_NOT_FOUND ((u64)14) | ||
| 30 | #define EFI_ALREADY_STARTED ((u64)25) | ||
| 31 | #define EFI_OUT_OF_RESOURCES ((u64)9) | ||
| 32 | |||
| 33 | /* ---- GUIDs (128-bit) ---- */ | ||
| 34 | typedef struct { u32 a; u16 b; u16 c; u8 d[8]; } efi_guid; | ||
| 35 | |||
| 36 | /* ---- EFI time (unused, part of file info struct) ---- */ | ||
| 37 | typedef struct { | ||
| 38 | u16 year; u8 month; u8 day; | ||
| 39 | u8 hour; u8 minute; u8 second; u8 pad1; | ||
| 40 | u32 ns; int16 tz; u8 daylight; | ||
| 41 | } efi_time; | ||
| 42 | |||
| 43 | /* ---- table headers ---- */ | ||
| 44 | typedef struct { | ||
| 45 | u64 signature; | ||
| 46 | u32 revision; | ||
| 47 | u32 header_size; | ||
| 48 | u32 crc32; | ||
| 49 | u32 reserved; | ||
| 50 | } efi_table_header; | ||
| 51 | |||
| 52 | /* ---- system table ---- */ | ||
| 53 | typedef struct efi_config_table efi_config_table; | ||
| 54 | typedef struct efi_simple_text_output efi_simple_text_output; | ||
| 55 | typedef struct efi_simple_text_input efi_simple_text_input; | ||
| 56 | typedef struct efi_boot_services efi_boot_services; | ||
| 57 | typedef struct efi_runtime_services efi_runtime_services; | ||
| 58 | |||
| 59 | typedef struct { | ||
| 60 | efi_table_header hdr; | ||
| 61 | char16 *firmware_vendor; | ||
| 62 | u32 firmware_revision; | ||
| 63 | handle console_in_handle; | ||
| 64 | efi_simple_text_input *con_in; | ||
| 65 | handle console_out_handle; | ||
| 66 | efi_simple_text_output *con_out; | ||
| 67 | handle stderr_handle; | ||
| 68 | efi_simple_text_output *std_err; | ||
| 69 | efi_runtime_services *runtime_services; | ||
| 70 | efi_boot_services *boot_services; | ||
| 71 | u64 number_of_table_entries; | ||
| 72 | efi_config_table *configuration_table; | ||
| 73 | } efi_system_table; | ||
| 74 | |||
| 75 | /* ---- EFI input key ---- */ | ||
| 76 | typedef struct { | ||
| 77 | u16 scan_code; | ||
| 78 | char16 unicode_char; | ||
| 79 | } efi_input_key; | ||
| 80 | |||
| 81 | /* ---- simple text output (console) ---- */ | ||
| 82 | typedef u64 (*efi_text_reset)(efi_simple_text_output *, u8); | ||
| 83 | typedef u64 (*efi_text_output)(efi_simple_text_output *, const char16 *); | ||
| 84 | |||
| 85 | struct efi_simple_text_output { | ||
| 86 | void *unused; // Index 0: Reset | ||
| 87 | efi_text_output output_string; // Index 1: OutputString | ||
| 88 | void *unused2[8]; // Index 2-9: TestString..Mode | ||
| 89 | }; | ||
| 90 | |||
| 91 | /* ---- simple text input (keyboard) ---- */ | ||
| 92 | typedef u64 (*efi_input_reset)(efi_simple_text_input *, u8); | ||
| 93 | typedef u64 (*efi_input_read_key)(efi_simple_text_input *, efi_input_key *); | ||
| 94 | |||
| 95 | struct efi_simple_text_input { | ||
| 96 | efi_input_reset reset; /* Index 0: Reset */ | ||
| 97 | efi_input_read_key read_key_stroke; /* Index 1: ReadKeyStroke */ | ||
| 98 | handle wait_for_key; /* Index 2: WaitForKey event */ | ||
| 99 | }; | ||
| 100 | |||
| 101 | /* ---- boot services ---- */ | ||
| 102 | typedef u64 (*efi_exit_boot_services)(handle, u64); | ||
| 103 | typedef u64 (*efi_get_memory_map)(void*, void*, void*, void*, void*); | ||
| 104 | typedef u64 (*efi_locate_protocol)(const efi_guid *, void *, void **); | ||
| 105 | typedef u64 (*efi_load_image)(u8, handle, void *, void *, u64, handle *); | ||
| 106 | typedef u64 (*efi_start_image)(handle, void *, u64 *); | ||
| 107 | typedef u64 (*efi_handle_protocol)(handle, const efi_guid *, void **); | ||
| 108 | typedef u64 (*efi_open_volume)(void *, void **); | ||
| 109 | typedef u64 (*efi_open_file)(void *, void **, char16 *, u64, u64); | ||
| 110 | typedef u64 (*efi_read_file)(void *, u64 *, void *); | ||
| 111 | typedef u64 (*efi_allocate_pool)(u8, u64, void **); | ||
| 112 | |||
| 113 | /* Boot Services table matching UEFI spec v2.10 function order. | ||
| 114 | Each entry is one function pointer (8 bytes on x64). */ | ||
| 115 | struct efi_boot_services { | ||
| 116 | efi_table_header hdr; /* 0 */ | ||
| 117 | void *raise_tpl; /* 24 index 0 */ | ||
| 118 | void *restore_tpl; /* 32 index 1 */ | ||
| 119 | void *allocate_pages; /* 40 index 2 */ | ||
| 120 | void *free_pages; /* 48 index 3 */ | ||
| 121 | efi_get_memory_map get_memory_map; /* 56 index 4 */ | ||
| 122 | efi_allocate_pool allocate_pool; /* 64 index 5 */ | ||
| 123 | void *free_pool; /* 72 index 6 */ | ||
| 124 | void *create_event; /* 80 index 7 */ | ||
| 125 | void *set_timer; /* 88 index 8 */ | ||
| 126 | void *wait_for_event; /* 96 index 9 */ | ||
| 127 | void *signal_event; /*104 index 10 */ | ||
| 128 | void *close_event; /*112 index 11 */ | ||
| 129 | void *check_event; /*120 index 12 */ | ||
| 130 | void *install_protocol_interface; /*128 index 13 */ | ||
| 131 | void *reinstall_protocol_interface;/*136 index 14 */ | ||
| 132 | void *uninstall_protocol_interface;/*144 index 15 */ | ||
| 133 | efi_handle_protocol handle_protocol; /*152 index 16 */ | ||
| 134 | void *reserved; /*160 index 17 */ | ||
| 135 | void *register_protocol_notify; /*168 index 18 */ | ||
| 136 | void *locate_handle; /*176 index 19 */ | ||
| 137 | void *locate_device_path; /*184 index 20 */ | ||
| 138 | void *install_config_table; /*192 index 21 */ | ||
| 139 | efi_load_image load_image; /*200 index 22 */ | ||
| 140 | efi_start_image start_image; /*208 index 23 */ | ||
| 141 | void *exit; /*216 index 24 */ | ||
| 142 | void *unload_image; /*224 index 25 */ | ||
| 143 | efi_exit_boot_services exit_boot_services; /*232 index 26 */ | ||
| 144 | void *get_next_monotonic_count; /*240 index 27 */ | ||
| 145 | void *stall; /*248 index 28 */ | ||
| 146 | void *set_watchdog_timer; /*256 index 29 */ | ||
| 147 | void *connect_controller; /*264 index 30 */ | ||
| 148 | void *disconnect_controller; /*272 index 31 */ | ||
| 149 | void *open_protocol; /*280 index 32 */ | ||
| 150 | void *close_protocol; /*288 index 33 */ | ||
| 151 | void *open_protocol_information; /*296 index 34 */ | ||
| 152 | void *protocols_per_handle; /*304 index 35 */ | ||
| 153 | void *locate_handle_buffer; /*312 index 36 */ | ||
| 154 | efi_locate_protocol locate_protocol; /*320 index 37 */ | ||
| 155 | void *install_multiple_protocols; /*328 index 38 */ | ||
| 156 | void *uninstall_multiple_protocols;/*336 index 39 */ | ||
| 157 | void *calculate_crc32; /*344 index 40 */ | ||
| 158 | void *copy_mem; /*352 index 41 */ | ||
| 159 | void *set_mem; /*360 index 42 */ | ||
| 160 | void *create_event_ex; /*368 index 43 */ | ||
| 161 | }; | ||
| 162 | |||
| 163 | /* ---- GUID for Simple File System Protocol ---- */ | ||
| 164 | #define EFI_SIMPLE_FILE_SYSTEM_GUID \ | ||
| 165 | {0x0964e5b22, 0x6459, 0x11d2, {0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b}} | ||
| 166 | |||
| 167 | /* ---- file protocol ---- */ | ||
| 168 | typedef struct { | ||
| 169 | u64 revision; | ||
| 170 | efi_open_volume open_volume; | ||
| 171 | } efi_simple_file_system; | ||
| 172 | |||
| 173 | typedef struct efi_file_protocol efi_file_protocol; | ||
| 174 | |||
| 175 | typedef u64 (*efi_file_open)(efi_file_protocol *, efi_file_protocol **, char16 *, u64, u64); | ||
| 176 | typedef u64 (*efi_file_read)(efi_file_protocol *, u64 *, void *); | ||
| 177 | typedef u64 (*efi_file_close)(efi_file_protocol *); | ||
| 178 | |||
| 179 | #define EFI_FILE_MODE_READ 1 | ||
| 180 | |||
| 181 | struct efi_file_protocol { | ||
| 182 | u64 revision; | ||
| 183 | efi_file_open open; | ||
| 184 | efi_file_close close; | ||
| 185 | void *unused; | ||
| 186 | efi_file_read read; | ||
| 187 | void *unused2[5]; | ||
| 188 | }; | ||
| 189 | |||
| 190 | /* ---- file info (for getting file size) ---- */ | ||
| 191 | typedef struct { | ||
| 192 | u64 size; | ||
| 193 | u64 file_size; | ||
| 194 | u64 physical_size; | ||
| 195 | efi_time create_time; | ||
| 196 | efi_time last_access; | ||
| 197 | efi_time modification_time; | ||
| 198 | u64 attribute; | ||
| 199 | char16 file_name[1]; | ||
| 200 | } efi_file_info; | ||
| 201 | |||
| 202 | /* ---- loaded image protocol ---- */ | ||
| 203 | typedef struct { | ||
| 204 | u32 revision; | ||
| 205 | handle parent_handle; | ||
| 206 | handle system_table; | ||
| 207 | handle device_handle; | ||
| 208 | void *file_path; | ||
| 209 | void *reserved; | ||
| 210 | u32 load_options_size; | ||
| 211 | void *load_options; | ||
| 212 | void *image_base; | ||
| 213 | u64 image_size; | ||
| 214 | u64 image_code_type; | ||
| 215 | u64 image_data_type; | ||
| 216 | void *unused; | ||
| 217 | } efi_loaded_image; | ||
| 218 | |||
| 219 | /* ---- loaded image protocol GUID ---- */ | ||
| 220 | #define EFI_LOADED_IMAGE_GUID \ | ||
| 221 | {0x5b1b31a1, 0x9562, 0x11d2, {0x8e,0x3f,0x00,0xa0,0xc9,0x69,0x72,0x3b}} | ||
| 222 | |||
| 223 | /* ---- device path protocol ---- */ | ||
| 224 | typedef struct { | ||
| 225 | u8 type; | ||
| 226 | u8 sub_type; | ||
| 227 | u16 length; | ||
| 228 | } efi_device_path; | ||
| 229 | |||
| 230 | #endif /* UEFI_H */ | ||
