1Operation 2========= 3 4Run the "zefi.py" script, passing a path to a built zephyr.elf file 5(NOT a "zephyr.strip" intended for grub/multiboot loading -- we need 6the symbol table) for the target as its sole argument. It will emit a 7"zephyr.efi" file in the current directory. Copy this to your target 8device's EFI boot partition via whatever means you like and run it 9from the EFI shell. 10 11Theory 12====== 13 14This works by creating a "zephyr.efi" EFI binary containing a zephyr 15image extracted from a built zephyr.elf file. EFI applications are 16relocatable, and cannot be placed at specific locations in memory. 17Instead, the stub code will copy the embedded zephyr sections to the 18appropriate locations at startup, clear any zero-filled (BSS, etc...) 19areas, then jump into the 64 bit entry point. 20 21Arguably this is needlessly inefficient, having to load the whole 22binary into memory and copy it vs. a bootloader like grub that will 23load it from the EFI boot filesystem into its correct location with no 24copies. But then, the biggest Zephyr application binaries we have on 25any platform top out at 200kb or so, and grub is at minimum about 5x 26that size, and potentially MUCH more if you start enabling the default 27modules. 28 29Source Code & Linkage 30===================== 31 32The code and link environment here is non-obvious. The simple rules 33are: 34 351. All code must live in a single C file 362. All symbols must be declared static 37 38And if you forget this and use a global by mistake, the build will 39work fine and then fail inexplicably at runtime with a garbage 40address. The sole exception is the "efi_entry()" function, whose 41address is not generated at runtime by the C code here (it's address 42goes into the PE file header instead). 43 44The reason is that we're building an EFI Application Binary with a 45Linux toolchain. EFI binaries are relocatable PE-COFF files -- 46basically Windows DLLs. But our compiler only generates code for ELF 47targets. 48 49These environments differ in the way they implement position 50independent code. Non-static global variables and function addresses 51in ELF get found via GOT and PLT tables that are populated at load 52time by a system binary (ld-linux.so). But there is no ld-linux.so in 53EFI firmware, and the EFI loader only knows how to fill in the PE 54relocation fields, which are a different data structure. 55 56So we can only rely on the C file's internal linkage, which is done 57via the x86_64 RIP-relative addressing mode and doesn't rely on any 58support from the environment. But that only works for symbols that 59the compiler (not the linker) knows a-priori will never be in 60externally linked shared libraries. Which is to say: only static 61symbols work. 62 63You might ask: why build a position-independent executable in the 64first place? Why not just link to a specific address? The PE-COFF 65format certainly allows that, and the UEFI specification doesn't 66clearly rule it out. But my experience with real EFI loaders is that 67they ignore the preferred load address and will put the image at 68arbitrary locations in memory. I couldn't make it work, basically. 69 70Bugs 71==== 72 73No support for 32 bit EFI environments. This would be a very tall 74order given the linker constraints described above (i386 doesn't have 75a IP-relative addressing mode, so we'd have to do some kind of 76relocation of our own). Will probably never happen unless we move to 77a proper PE-COFF toolchain. 78 79There is no protection against colliding mappings. We just assume 80that the EFI environment will load our image at an address that 81doesn't overlap with the target Zephyr memory. In practice on the 82systems I've tried, EFI uses memory near the top of 32-bit-accessible 83physical memory, while Zephyr prefers to load into the bottom of RAM 84at 0x10000. Even given collisions, this is probably tolerable, as we 85could control the Zephyr mapping addresses per-platform if needed to 86sneak around EFI areas. But the Zephyr loader should at least detect 87the overlap and warn about it. 88 89It runs completely standalone, writing output to the current 90directory, and uses the (x86_64 linux) host toolchain right now, when 91it should be integrated with the Zephyr toolchain and build system. 92