Fork of imbushuo/boot-shim-tegra-3 extended into a complete Linux boot chain for first-generation Microsoft Surface RT (Tegra 3, Aug 2013 firmware), bypassing every layer of Microsoft's UEFI Secure Boot enforcement without modifying the device firmware.
Boots an unmodified Raspberry Pi OS (raspios-bullseye-surface-rt-armhf) ARM Linux kernel on a stock Surface RT 1 via the following chain:
Firmware (signed bootmgfw.efi via /efi/boot/bootarm.efi)
↓
Windows Boot Manager (loads via Microsoft's signed cert)
↓
BCD entry with nointegritychecks=Yes
↓
This boot-shim binary (subsystem=16 BOOT_APPLICATION, ARMNT)
↓
Manual PE loader (bypasses gBS->LoadImage Secure Boot check)
↓
TianoCore EDK II UEFI Shell (Pi OS bootarm.efi)
↓
startup.nsh executes Linux kernel command
↓
gBS->LoadImage / StartImage hooks redirect to our manual loader
↓
gRT->GetVariable hook spoofs SecureBoot=0
↓
ARM Linux kernel boots normally, mounts ext4 rootfs, init=/bin/sh
↓
Root shell prompt on Surface RT display
This repo started as a fork of imbushuo/boot-shim-tegra-3 which was itself a Tegra 3-specific patch of imbushuo/boot-shim. The original boot-shim was a proof-of-concept Windows Boot Manager Application demonstrating context switch from Boot Manager to UEFI on Qualcomm Lumia devices. The Tegra 3 fork added Surface RT screen support but stripped the actual chain-load logic, leaving only a printf demo. This fork rebuilds and extends the chain-load logic specifically for booting Linux.
- Device: Microsoft Surface RT (1st generation, 2012), Tegra 3 (Cortex-A9 ARMv7 quad-core), 2GB RAM
- Firmware:
OemkS EFI Aug 16 2013 14:55:40, 0x00000000— early Windows RT 8 firmware that predates most documented community work - Distro:
2023-05-03-raspios-bullseye-surface-rt-armhf(OpenRT community build) - Boot media: Single USB stick (SanDisk Cruzer 8GB)
| Layer | Block | Bypass |
|---|---|---|
| UEFI Secure Boot policy | Refuses unsigned EFI binaries from firmware boot | Microsoft-signed Windows Boot Manager (already trusted, applied via leaked Golden Keys debug policy) is the firmware-loaded entry point. WBM accepts unsigned children via nointegritychecks=Yes BCD flag. |
| WBM image format filter | Rejects EFI_APPLICATION subsystem (10) binaries | Build as WINDOWS_BOOT_APPLICATION (subsystem 16) with ARMNT machine type. |
Post-UEFI gBS->LoadImage Secure Boot check |
Refuses unsigned binaries (kernel, shell) launched from inside UEFI after WBM exits | Replace gBS->LoadImage and gBS->StartImage function pointers with hooks. Hooks fall back to manual PE loader on Security Violation. |
| Microsoft's internal LoadImage validation | Not exposed via standard EFI_SECURITY_ARCH_PROTOCOL, has its own inline check |
Manual PE loader (parse, allocate, copy sections, apply relocations, install LoadedImage protocol, jump to entry) — never calls firmware's LoadImage. |
| Linux kernel's "Secure Boot = reject dtb=cmdline" policy | Kernel refuses to load DTB from command line when it detects SB enabled | gRT->GetVariable hook returns SecureBoot = 0 for that specific variable. |
| ARM/Thumb mode entry confusion | Default behavior of OR'ing entry address with 1 for Thumb mode | Linux ARM kernel's EFI stub is built as ARM mode (32-bit instructions) despite machine field claiming IMAGE_FILE_MACHINE_THUMB (0x1C2). Trust EntryRVA's bit-0 mode encoding instead of forcing Thumb. |
Surface RT firmware boots /efi/boot/bootarm.efi from the FAT32 USB. This file is the standard Microsoft-signed Windows Boot Manager (~770KB, identifiable by Windows-specific strings like [boot loader], BOOTMGRDEFAULT=, bitlocker). Because it's Microsoft-signed, firmware trusts it unconditionally.
WBM reads /efi/microsoft/boot/bcd and finds our custom Boot Application entry:
identifier {default}
device boot
path \efi\boot\bootshim-test.efi
description Boot Linux
nointegritychecks Yes ← skip signature verify
testsigning Yes
isolatedcontext Yes
WBM launches the boot-shim binary because the flags tell it to skip integrity checks.
Our boot-shim is built with subsystem = IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION (16) and machine type IMAGE_FILE_MACHINE_ARMNT (0x1C4) — the only PE configuration WBM will load via BCD.
BlApplicationEntry runs in WBM context. It calls SwitchToRealModeContext() (the inherited Tegra 3 code from imbushuo's fork) which manipulates ARM CP15 system registers to switch back from Boot Manager context to a normal UEFI environment, then calls efi_main.
Before doing anything else, efi_main saves and replaces:
gBS->LoadImage→HookedLoadImagegBS->StartImage→HookedStartImagegRT->GetVariable→HookedGetVariable
Then recomputes the CRC32 of the BootServices and RuntimeServices tables (firmware checks these).
The boot-shim reads shell.efi (the Pi OS-bundled TianoCore EDK II UEFI shell) from the same USB and runs it through ManualLoad():
- Parse PE/COFF headers
AllocatePages(EfiLoaderCode, ...)forSizeOfImage- Copy each section from the file at
PointerToRawDatatoImageBase + VirtualAddress - Apply base relocations:
IMAGE_REL_BASED_HIGHLOW— 32-bit absolute address fixupIMAGE_REL_BASED_ARM_MOV32T— Thumb-2 MOVW/MOVT instruction pair fixup (encodes 16-bit immediates split acrossi,imm3,imm4,imm8bitfields)
- ARM cache coherency:
- Loop over image,
DCCMVACeach 32-byte cache line (Cortex-A9 line size) to clean D-cache to memory - DSB
ICIALLU(invalidate entire I-cache) +BPIALL(invalidate branch predictor)- DSB + ISB
- Loop over image,
- Allocate
EFI_LOADED_IMAGEstruct, setDeviceHandle,ImageBase,ImageSize InstallProtocolInterfaceforEFI_LOADED_IMAGE_PROTOCOL_GUIDon new handle- Track
(handle, entry_address)in internal table for later StartImage lookup - Jump to entry —
EntryRVA's bit 0 already encodes ARM/Thumb mode, do not force Thumb bit
The shell launches and reads startup.nsh from FS0.
startup.nsh contains:
fs0:
zImage dtb=tegra30-microsoft-surface-rt-efi.dtb root=PARTUUID=... rootwait rw console=tty0 earlycon=efi efi=debug loglevel=8 initcall_debug cpuidle.off=1 init=/bin/sh panic=30
reset -s
Shell calls gBS->LoadImage(zImage). Our hook:
- Tries original LoadImage — returns
EFI_SECURITY_VIOLATION - Falls back: reads zImage via SimpleFileSystem, calls our
ManualLoad - Returns the new handle to shell
Shell sets LoadedImage->LoadOptions = kernel_cmdline then calls gBS->StartImage. Our hook:
- Sees handle is in our tracked table
- Calls the kernel entry directly with
(handle, gST)
Kernel's ARM EFI stub:
- Sets up its own environment
- Calls our hooked
gRT->GetVariable("SecureBoot")→ we return 0 - Sees Secure Boot is "disabled" → accepts the
dtb=cmdline arg - Reads the DTB file from FAT32 via SimpleFileSystem
- Allocates real kernel memory, decompresses, sets up DTB
- Calls
ExitBootServices— UEFI is gone - Jumps to decompressed kernel
Kernel boots normally, brings up 4 cores (penguin logos appear), initializes USB stack, mounts ext4 rootfs from sda2, execs /bin/sh as init. Root shell prompt.
/efi/boot/bootarm.efi ← Microsoft Windows Boot Manager (copied from jailbreak USB)
/efi/boot/bootshim-test.efi ← Our compiled boot-shim
/efi/boot/shell.efi ← TianoCore EDK II UEFI Shell (from Pi OS image)
/efi/microsoft/boot/bcd ← BCD store (modified from jailbreak USB's)
/efi/microsoft/boot/fonts/* ← Required by WBM
/startup.nsh ← Kernel boot command
/zImage ← Linux kernel
/tegra30-microsoft-surface-rt-efi.dtb ← Device tree
... (Pi OS Pi-specific files, unused on Surface RT but harmless)
- Working firmware (boots into recovery / jailbreak USB)
- Golden Keys debug policy applied to NVRAM (via Tegra Jailbreak USB)
- Boot USB written with bullseye Pi OS image
- Jailbreak USB's WBM + BCD + boot-shim copied onto the boot USB (see bootloader-setup.md)
- Visual Studio 2022 Community
- Individual component: MSVC v143 - VS 2022 C++ ARM build tools (Latest)
- Windows SDK 10.0.22621.0 (newer SDKs dropped ARM32 support)
- Git for Windows
git clone --recursive https://github.com/Evan-Haug/surface-rt-linux-bootshim
cd surface-rt-linux-bootshim
"C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" "Boot Shim.sln" /p:Configuration=Debug /p:Platform=ARM /p:WindowsTargetPlatformVersion=10.0.22621.0 /p:PlatformToolset=v143
Output: build\BootShim\ARM\Debug\BootShim.efi (~46KB)
Copy to USB as \efi\boot\bootshim-test.efi, ensure BCD entry points at this path.
- No serial console output. Kernel boots but if anything goes wrong post-
ExitBootServices, you see a frozen screen. Surface RT has internal UART test pads — tapping them with a USB-UART dongle would unlock real kernel debugging. - Touchscreen not yet configured. Linux detects the I2C touchscreen but X11 input mapping needs
xinput_calibratorwork. Plug in a USB keyboard for now. - WiFi/BT may need firmware blobs the bullseye Pi OS image already includes.
- No SMP boot verification beyond "4 penguins shown." Real perf testing not done.
- Battery / charging path bypassed — this particular Surface RT has hardware charging issues; the work is reproducible on a Surface RT in better hardware shape with the original Microsoft charger.
src/EFIApp.c— main boot-shim source with manual PE loader, hooks, and bypass logicsrc/Context.c— Tegra 3 context switch (inherited from upstream)src/BlApplicationEntry.c— BootApp entry, callsSwitchToRealModeContextthenefi_maincontrib/msvc/uefi-simple.vcxproj— VS2022/SDK22621 build config (patched from upstream)JOURNEY.md— chronological story of how this was built in one day
GPL-2.0 (inherited from gnu-efi submodule). See COPYING.md.
- imbushuo — original boot-shim and Tegra 3 fork; the screen-output trick is theirs
- OpenRT community — Pi OS for Surface RT, Tegra Jailbreak USB, documentation
- leaked Microsoft Golden Keys debug policy — without which none of this would work
- The eight-hour debugging session that culminated in 4 Tux penguins on a 13-year-old locked tablet