Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 1 | # |
| 2 | # Copyright (C) 2015 Google, Inc |
| 3 | # |
| 4 | # SPDX-License-Identifier: GPL-2.0+ |
| 5 | # |
| 6 | |
Alexander Graf | 3b931b0 | 2016-03-04 01:10:09 +0100 | [diff] [blame] | 7 | =========== Table of Contents =========== |
| 8 | |
| 9 | 1 U-Boot on EFI |
| 10 | 1.1 In God's Name, Why? |
| 11 | 1.2 Status |
| 12 | 1.3 Build Instructions |
| 13 | 1.4 Trying it out |
| 14 | 1.5 Inner workings |
| 15 | 1.6 EFI Application |
| 16 | 1.7 EFI Payload |
| 17 | 1.8 Tables |
| 18 | 1.9 Interrupts |
| 19 | 1.10 32/64-bit |
| 20 | 1.11 Future work |
| 21 | 1.12 Where is the code? |
| 22 | |
| 23 | 2 EFI on U-Boot |
| 24 | 2.1 In God's Name, Why? |
| 25 | 2.2 How do I get it? |
| 26 | 2.3 Status |
| 27 | 2.4 Future work |
| 28 | |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 29 | U-Boot on EFI |
| 30 | ============= |
| 31 | This document provides information about U-Boot running on top of EFI, either |
| 32 | as an application or just as a means of getting U-Boot onto a new platform. |
| 33 | |
| 34 | |
| 35 | In God's Name, Why? |
| 36 | ------------------- |
| 37 | This is useful in several situations: |
| 38 | |
| 39 | - You have EFI running on a board but U-Boot does not natively support it |
| 40 | fully yet. You can boot into U-Boot from EFI and use that until U-Boot is |
| 41 | fully ported |
| 42 | |
| 43 | - You need to use an EFI implementation (e.g. UEFI) because your vendor |
| 44 | requires it in order to provide support |
| 45 | |
| 46 | - You plan to use coreboot to boot into U-Boot but coreboot support does |
| 47 | not currently exist for your platform. In the meantime you can use U-Boot |
| 48 | on EFI and then move to U-Boot on coreboot when ready |
| 49 | |
| 50 | - You use EFI but want to experiment with a simpler alternative like U-Boot |
| 51 | |
| 52 | |
| 53 | Status |
| 54 | ------ |
| 55 | Only x86 is supported at present. If you are using EFI on another architecture |
| 56 | you may want to reconsider. However, much of the code is generic so could be |
| 57 | ported. |
| 58 | |
| 59 | U-Boot supports running as an EFI application for 32-bit EFI only. This is |
| 60 | not very useful since only a serial port is provided. You can look around at |
| 61 | memory and type 'help' but that is about it. |
| 62 | |
| 63 | More usefully, U-Boot supports building itself as a payload for either 32-bit |
| 64 | or 64-bit EFI. U-Boot is packaged up and loaded in its entirety by EFI. Once |
| 65 | started, U-Boot changes to 32-bit mode (currently) and takes over the |
| 66 | machine. You can use devices, boot a kernel, etc. |
| 67 | |
| 68 | |
| 69 | Build Instructions |
| 70 | ------------------ |
| 71 | First choose a board that has EFI support and obtain an EFI implementation |
Bin Meng | 094f233 | 2015-08-17 20:34:47 -0700 | [diff] [blame] | 72 | for that board. It will be either 32-bit or 64-bit. Alternatively, you can |
| 73 | opt for using QEMU [1] and the OVMF [2], as detailed below. |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 74 | |
Bin Meng | 094f233 | 2015-08-17 20:34:47 -0700 | [diff] [blame] | 75 | To build U-Boot as an EFI application (32-bit EFI required), enable CONFIG_EFI |
| 76 | and CONFIG_EFI_APP. The efi-x86 config (efi-x86_defconfig) is set up for this. |
| 77 | Just build U-Boot as normal, e.g. |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 78 | |
Bin Meng | 094f233 | 2015-08-17 20:34:47 -0700 | [diff] [blame] | 79 | make efi-x86_defconfig |
| 80 | make |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 81 | |
Bin Meng | 094f233 | 2015-08-17 20:34:47 -0700 | [diff] [blame] | 82 | To build U-Boot as an EFI payload (32-bit or 64-bit EFI can be used), adjust an |
| 83 | existing config (like qemu-x86_defconfig) to enable CONFIG_EFI, CONFIG_EFI_STUB |
| 84 | and either CONFIG_EFI_STUB_32BIT or CONFIG_EFI_STUB_64BIT. All of these are |
| 85 | boolean Kconfig options. Then build U-Boot as normal, e.g. |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 86 | |
| 87 | make qemu-x86_defconfig |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 88 | make |
| 89 | |
Bin Meng | 094f233 | 2015-08-17 20:34:47 -0700 | [diff] [blame] | 90 | You will end up with one of these files depending on what you build for: |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 91 | |
| 92 | u-boot-app.efi - U-Boot EFI application |
| 93 | u-boot-payload.efi - U-Boot EFI payload application |
| 94 | |
| 95 | |
| 96 | Trying it out |
| 97 | ------------- |
Bin Meng | 094f233 | 2015-08-17 20:34:47 -0700 | [diff] [blame] | 98 | QEMU is an emulator and it can emulate an x86 machine. Please make sure your |
| 99 | QEMU version is 2.3.0 or above to test this. You can run the payload with |
| 100 | something like this: |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 101 | |
| 102 | mkdir /tmp/efi |
| 103 | cp /path/to/u-boot*.efi /tmp/efi |
| 104 | qemu-system-x86_64 -bios bios.bin -hda fat:/tmp/efi/ |
| 105 | |
| 106 | Add -nographic if you want to use the terminal for output. Once it starts |
| 107 | type 'fs0:u-boot-payload.efi' to run the payload or 'fs0:u-boot-app.efi' to |
Bin Meng | 094f233 | 2015-08-17 20:34:47 -0700 | [diff] [blame] | 108 | run the application. 'bios.bin' is the EFI 'BIOS'. Check [2] to obtain a |
| 109 | prebuilt EFI BIOS for QEMU or you can build one from source as well. |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 110 | |
| 111 | To try it on real hardware, put u-boot-app.efi on a suitable boot medium, |
| 112 | such as a USB stick. Then you can type something like this to start it: |
| 113 | |
| 114 | fs0:u-boot-payload.efi |
| 115 | |
| 116 | (or fs0:u-boot-app.efi for the application) |
| 117 | |
| 118 | This will start the payload, copy U-Boot into RAM and start U-Boot. Note |
| 119 | that EFI does not support booting a 64-bit application from a 32-bit |
| 120 | EFI (or vice versa). Also it will often fail to print an error message if |
| 121 | you get this wrong. |
| 122 | |
| 123 | |
| 124 | Inner workings |
| 125 | ============== |
| 126 | Here follow a few implementation notes for those who want to fiddle with |
| 127 | this and perhaps contribute patches. |
| 128 | |
| 129 | The application and payload approaches sound similar but are in fact |
| 130 | implemented completely differently. |
| 131 | |
| 132 | EFI Application |
| 133 | --------------- |
| 134 | For the application the whole of U-Boot is built as a shared library. The |
| 135 | efi_main() function is in lib/efi/efi_app.c. It sets up some basic EFI |
| 136 | functions with efi_init(), sets up U-Boot global_data, allocates memory for |
| 137 | U-Boot's malloc(), etc. and enters the normal init sequence (board_init_f() |
| 138 | and board_init_r()). |
| 139 | |
| 140 | Since U-Boot limits its memory access to the allocated regions very little |
| 141 | special code is needed. The CONFIG_EFI_APP option controls a few things |
| 142 | that need to change so 'git grep CONFIG_EFI_APP' may be instructive. |
| 143 | The CONFIG_EFI option controls more general EFI adjustments. |
| 144 | |
| 145 | The only available driver is the serial driver. This calls back into EFI |
| 146 | 'boot services' to send and receive characters. Although it is implemented |
| 147 | as a serial driver the console device is not necessarilly serial. If you |
| 148 | boot EFI with video output then the 'serial' device will operate on your |
| 149 | target devices's display instead and the device's USB keyboard will also |
| 150 | work if connected. If you have both serial and video output, then both |
| 151 | consoles will be active. Even though U-Boot does the same thing normally, |
| 152 | These are features of EFI, not U-Boot. |
| 153 | |
| 154 | Very little code is involved in implementing the EFI application feature. |
| 155 | U-Boot is highly portable. Most of the difficulty is in modifying the |
| 156 | Makefile settings to pass the right build flags. In particular there is very |
| 157 | little x86-specific code involved - you can find most of it in |
| 158 | arch/x86/cpu. Porting to ARM (which can also use EFI if you are brave |
| 159 | enough) should be straightforward. |
| 160 | |
| 161 | Use the 'reset' command to get back to EFI. |
| 162 | |
| 163 | EFI Payload |
| 164 | ----------- |
| 165 | The payload approach is a different kettle of fish. It works by building |
| 166 | U-Boot exactly as normal for your target board, then adding the entire |
| 167 | image (including device tree) into a small EFI stub application responsible |
| 168 | for booting it. The stub application is built as a normal EFI application |
| 169 | except that it has a lot of data attached to it. |
| 170 | |
| 171 | The stub application is implemented in lib/efi/efi_stub.c. The efi_main() |
| 172 | function is called by EFI. It is responsible for copying U-Boot from its |
| 173 | original location into memory, disabling EFI boot services and starting |
| 174 | U-Boot. U-Boot then starts as normal, relocates, starts all drivers, etc. |
| 175 | |
| 176 | The stub application is architecture-dependent. At present it has some |
| 177 | x86-specific code and a comment at the top of efi_stub.c describes this. |
| 178 | |
| 179 | While the stub application does allocate some memory from EFI this is not |
| 180 | used by U-Boot (the payload). In fact when U-Boot starts it has all of the |
| 181 | memory available to it and can operate as it pleases (but see the next |
| 182 | section). |
| 183 | |
| 184 | Tables |
| 185 | ------ |
| 186 | The payload can pass information to U-Boot in the form of EFI tables. At |
| 187 | present this feature is used to pass the EFI memory map, an inordinately |
| 188 | large list of memory regions. You can use the 'efi mem all' command to |
| 189 | display this list. U-Boot uses the list to work out where to relocate |
| 190 | itself. |
| 191 | |
| 192 | Although U-Boot can use any memory it likes, EFI marks some memory as used |
| 193 | by 'run-time services', code that hangs around while U-Boot is running and |
| 194 | is even present when Linux is running. This is common on x86 and provides |
| 195 | a way for Linux to call back into the firmware to control things like CPU |
| 196 | fan speed. U-Boot uses only 'conventional' memory, in EFI terminology. It |
| 197 | will relocate itself to the top of the largest block of memory it can find |
| 198 | below 4GB. |
| 199 | |
| 200 | Interrupts |
| 201 | ---------- |
| 202 | U-Boot drivers typically don't use interrupts. Since EFI enables interrupts |
| 203 | it is possible that an interrupt will fire that U-Boot cannot handle. This |
| 204 | seems to cause problems. For this reason the U-Boot payload runs with |
| 205 | interrupts disabled at present. |
| 206 | |
| 207 | 32/64-bit |
| 208 | --------- |
| 209 | While the EFI application can in principle be built as either 32- or 64-bit, |
| 210 | only 32-bit is currently supported. This means that the application can only |
| 211 | be used with 32-bit EFI. |
| 212 | |
| 213 | The payload stub can be build as either 32- or 64-bits. Only a small amount |
| 214 | of code is built this way (see the extra- line in lib/efi/Makefile). |
| 215 | Everything else is built as a normal U-Boot, so is always 32-bit on x86 at |
| 216 | present. |
| 217 | |
| 218 | Future work |
| 219 | ----------- |
| 220 | This work could be extended in a number of ways: |
| 221 | |
| 222 | - Add a generic x86 EFI payload configuration. At present you need to modify |
| 223 | an existing one, but mostly the low-level x86 code is disabled when booting |
| 224 | on EFI anyway, so a generic 'EFI' board could be created with a suitable set |
| 225 | of drivers enabled. |
| 226 | |
| 227 | - Add ARM support |
| 228 | |
| 229 | - Add 64-bit application support |
| 230 | |
| 231 | - Figure out how to solve the interrupt problem |
| 232 | |
| 233 | - Add more drivers to the application side (e.g. video, block devices, USB, |
| 234 | environment access). This would mostly be an academic exercise as a strong |
| 235 | use case is not readily apparent, but it might be fun. |
| 236 | |
| 237 | - Avoid turning off boot services in the stub. Instead allow U-Boot to make |
| 238 | use of boot services in case it wants to. It is unclear what it might want |
| 239 | though. |
| 240 | |
| 241 | Where is the code? |
| 242 | ------------------ |
| 243 | lib/efi |
| 244 | payload stub, application, support code. Mostly arch-neutral |
| 245 | |
| 246 | arch/x86/lib/efi |
| 247 | helper functions for the fake DRAM init, etc. These can be used by |
| 248 | any board that runs as a payload. |
| 249 | |
| 250 | arch/x86/cpu/efi |
| 251 | x86 support code for running as an EFI application |
| 252 | |
| 253 | board/efi/efi-x86/efi.c |
| 254 | x86 board code for running as an EFI application |
| 255 | |
| 256 | common/cmd_efi.c |
| 257 | the 'efi' command |
| 258 | |
Simon Glass | 9191329 | 2015-08-04 12:34:05 -0600 | [diff] [blame] | 259 | -- |
| 260 | Ben Stoltz, Simon Glass |
| 261 | Google, Inc |
| 262 | July 2015 |
Bin Meng | 094f233 | 2015-08-17 20:34:47 -0700 | [diff] [blame] | 263 | |
| 264 | [1] http://www.qemu.org |
| 265 | [2] http://www.tianocore.org/ovmf/ |
Alexander Graf | 3b931b0 | 2016-03-04 01:10:09 +0100 | [diff] [blame] | 266 | |
| 267 | ------------------------------------------------------------------------------- |
| 268 | |
| 269 | EFI on U-Boot |
| 270 | ============= |
| 271 | |
| 272 | In addition to support for running U-Boot as a UEFI application, U-Boot itself |
| 273 | can also expose the UEFI interfaces and thus allow UEFI payloads to run under |
| 274 | it. |
| 275 | |
| 276 | In God's Name, Why? |
| 277 | ------------------- |
| 278 | |
| 279 | With this support in place, you can run any UEFI payload (such as the Linux |
| 280 | kernel, grub2 or gummiboot) on U-Boot. This dramatically simplifies boot loader |
| 281 | configuration, as U-Boot based systems now look and feel (almost) the same way |
| 282 | as TianoCore based systems. |
| 283 | |
| 284 | How do I get it? |
| 285 | ---------------- |
| 286 | |
| 287 | EFI support for 32bit ARM and AArch64 is already included in U-Boot. All you |
| 288 | need to do is enable |
| 289 | |
| 290 | CONFIG_CMD_BOOTEFI=y |
| 291 | CONFIG_EFI_LOADER=y |
| 292 | |
| 293 | in your .config file and you will automatically get a bootefi command to run |
| 294 | an efi application as well as snippet in the default distro boot script that |
| 295 | scans for removable media efi binaries as fallback. |
| 296 | |
| 297 | Status |
| 298 | ------ |
| 299 | |
| 300 | I am successfully able to run grub2 and Linux EFI binaries with this code on |
| 301 | ARMv7 as well as AArch64 systems. |
| 302 | |
| 303 | When enabled, the resulting U-Boot binary only grows by ~10KB, so it's very |
| 304 | light weight. |
| 305 | |
| 306 | All storage devices are directly accessible from the uEFI payload |
| 307 | |
| 308 | Removable media booting (search for /efi/boot/boota{a64,arm}.efi) is supported. |
| 309 | |
| 310 | Simple use cases like "Plug this SD card into my ARM device and it just |
| 311 | boots into grub which boots into Linux", work very well. |
| 312 | |
| 313 | Future work |
| 314 | ----------- |
| 315 | |
| 316 | Of course, there are still a few things one could do on top: |
| 317 | |
| 318 | - Improve disk media detection (don't scan, use what information we |
| 319 | have) |
| 320 | - Add EFI variable support using NVRAM |
| 321 | - Add GFX support |
| 322 | - Make EFI Shell work |
| 323 | - Network device support |
| 324 | - Support for payload exit |
| 325 | - Payload Watchdog support |