From 40e5b53ea0dc67cdff3e6e7d8cac7b258a9d885b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 21 Dec 2021 09:09:48 +0100 Subject: [PATCH 01/20] efi: fix typo in description of struct efi_entry_hdr Add missing colon. Signed-off-by: Heinrich Schuchardt --- include/efi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/efi.h b/include/efi.h index 0ec5913ddd..1432038838 100644 --- a/include/efi.h +++ b/include/efi.h @@ -321,7 +321,7 @@ struct efi_info_hdr { * struct efi_entry_hdr - Header for a table entry * * @type: enum eft_entry_t - * @size size of entry bytes excluding header and padding + * @size: size of entry bytes excluding header and padding * @addr: address of this entry (0 if it follows the header ) * @link: size of entry including header and padding * @spare1: Spare space for expansion From 2707610eb7d3068dbd131401dcde65a57ddc0018 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Fri, 24 Dec 2021 10:08:41 +0200 Subject: [PATCH 02/20] efi_loader: Don't limit the StMM buffer size explicitly Currently we allow and explicitly check a single shared page with StandAloneMM. This is dictated by OP-TEE which runs the application. However there's no way for us dynamically discover the number of pages we are allowed to use. Since writing big EFI signature list variable requires more than a page, OP-TEE has bumped the number of shared pages to four. Let's remove our explicit check and allow the request to reach OP-TEE even if it's bigger than what it supports. There's no need to sanitize the number of pages internally. OP-TEE will fail if we try to write more than it's allowed. The error will just trigger later on, during the StMM access. While at it add an error message to help users figure out what failed. Signed-off-by: Ilias Apalodimas Tested-by: Ying-Chun Liu (PaulLiu) Signed-off-by: Ilias Apalodimas --- include/tee.h | 1 + lib/efi_loader/efi_variable_tee.c | 16 +++++----------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/include/tee.h b/include/tee.h index 50051496ae..13f6096b9a 100644 --- a/include/tee.h +++ b/include/tee.h @@ -58,6 +58,7 @@ #define TEE_SUCCESS 0x00000000 #define TEE_ERROR_STORAGE_NOT_AVAILABLE 0xf0100003 #define TEE_ERROR_GENERIC 0xffff0000 +#define TEE_ERROR_EXCESS_DATA 0xffff0004 #define TEE_ERROR_BAD_PARAMETERS 0xffff0006 #define TEE_ERROR_ITEM_NOT_FOUND 0xffff0008 #define TEE_ERROR_NOT_IMPLEMENTED 0xffff0009 diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index 281f886124..a2c65e3694 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -15,7 +15,6 @@ #include #include -#define OPTEE_PAGE_SIZE BIT(12) extern struct efi_var_file __efi_runtime_data *efi_var_buf; static efi_uintn_t max_buffer_size; /* comm + var + func + data */ static efi_uintn_t max_payload_size; /* func + data */ @@ -114,7 +113,11 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) rc = tee_invoke_func(conn.tee, &arg, 2, param); tee_shm_free(shm); tee_close_session(conn.tee, conn.session); - if (rc || arg.ret != TEE_SUCCESS) + if (rc) + return EFI_DEVICE_ERROR; + if (arg.ret == TEE_ERROR_EXCESS_DATA) + log_err("Variable payload too large\n"); + if (arg.ret != TEE_SUCCESS) return EFI_DEVICE_ERROR; switch (param[1].u.value.a) { @@ -255,15 +258,6 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; - /* - * Although the max payload is configurable on StMM, we only share a - * single page from OP-TEE for the non-secure buffer used to communicate - * with StMM. Since OP-TEE will reject to map anything bigger than that, - * make sure we are in bounds. - */ - if (*size > OPTEE_PAGE_SIZE) - *size = OPTEE_PAGE_SIZE - MM_COMMUNICATE_HEADER_SIZE - - MM_VARIABLE_COMMUNICATE_SIZE; /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix From eaaa5fbbe421c75dbf2319615f95e0484b330b87 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 27 Dec 2021 15:07:36 +0000 Subject: [PATCH 03/20] sunxi: add board documentation Add some long overdue instructions for building and installing U-Boot on Allwinner SoC based boards. This describes the building process, including TF-A and crust, plus installation to SD card, eMMC and SPI flash, both from Linux and U-Boot itself. Also describe FEL booting. Signed-off-by: Andre Przywara Reviewed-by: Heinrich Schuchardt --- doc/board/allwinner/index.rst | 9 + doc/board/allwinner/sunxi.rst | 319 ++++++++++++++++++++++++++++++++++ doc/board/index.rst | 1 + 3 files changed, 329 insertions(+) create mode 100644 doc/board/allwinner/index.rst create mode 100644 doc/board/allwinner/sunxi.rst diff --git a/doc/board/allwinner/index.rst b/doc/board/allwinner/index.rst new file mode 100644 index 0000000000..7352ccd5c0 --- /dev/null +++ b/doc/board/allwinner/index.rst @@ -0,0 +1,9 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Allwinner (sunxi) boards +======================== + +.. toctree:: + :maxdepth: 2 + + sunxi diff --git a/doc/board/allwinner/sunxi.rst b/doc/board/allwinner/sunxi.rst new file mode 100644 index 0000000000..797222d8d3 --- /dev/null +++ b/doc/board/allwinner/sunxi.rst @@ -0,0 +1,319 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (C) 2021 Arm Ltd. + +Allwinner SoC based boards +========================== +For boards using an Allwinner ARM based SoC ("sunxi"), the U-Boot build +system generates a single integrated image file: ``u-boot-sunxi-with-spl.bin.`` +This file can be used on SD cards, eMMC devices, SPI flash and for the +USB-OTG based boot method (FEL). To build this file: + +* For 64-bit SoCs, build Trusted Firmware (TF-A, formerly known as ATF) first, + you will need its ``bl31.bin``. See below for more details. +* Optionally on 64-bit SoCs, build the `crust`_ management processor firmware, + you will need its ``scp.bin``. See below for more details. +* Build U-Boot:: + + $ export BL31=/path/to/bl31.bin # required for 64-bit SoCs + $ export SCP=/path/to/scp.bin # optional for some 64-bit SoCs + $ make _defconfig + $ make +* Transfer to an (micro)SD card (see below for more details):: + + $ sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdX bs=8k seek=1 +* Boot and enjoy! + +.. note:: + The traditional SD card location the Allwinner BootROM loads from is 8KB + (sector 16). This works fine with the old MBR partitioning scheme, which most + SD cards come formatted with. However this is in the middle of a potential + GPT partition table, which will become invalid in this step. Newer SoCs + (starting with the H3 from late 2014) also support booting from 128KB, which + is beyond even a GPT and thus a safer location. + +For more details, and alternative boot locations or installations, see below. + +Building Arm Trusted Firmware (TF-A) +------------------------------------ +Boards using a 64-bit Soc (A64, H5, H6, H616, R329) require the BL31 stage of +the `Arm Trusted Firmware-A`_ firmware. This provides the reference +implementation of secure software for Armv8-A, offering PSCI and SMCCC +services. Allwinner support is fully mainlined. To build bl31.bin:: + + $ git clone https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git + $ cd trusted-firmware-a + $ make CROSS_COMPILE=aarch64-linux-gnu- PLAT=sun50i_a64 DEBUG=1 + $ export BL31=$(pwd)/build/sun50i_a64/debug/bl31.bin + +The target platform (``PLAT=``) for A64 and H5 SoCs is sun50i_a64, for the H6 +sun50i_h6, for the H616 sun50i_h616, and for the R329 sun50i_r329. Use:: + + $ find plat/allwinner -name platform.mk + +to find all supported platforms. TF-A's `docs/plat/allwinner.rst`_ contains +more information and lists some build options. + +Building the Crust management processor firmware +------------------------------------------------ +For some SoCs and boards, the integrated OpenRISC management controller can +be used to provide power management services, foremost suspend to RAM. +There is a community supported Open Source implementation called `crust`_, +which runs on most SoCs featuring a management controller. + +This firmware part is optional, setting the SCP environment variable to +/dev/null avoids the warning message when building without one. + +To build crust's scp.bin, you need an OpenRISC (or1k) cross compiler, then:: + + $ git clone https://github.com/crust-firmware/crust.git + $ cd crust + $ make _defconfig + $ make CROSS_COMPILE=or1k-none-elf- scp + $ export SCP=$(pwd)/build/scp/scp.bin + +Find a list of supported board configurations in the `configs/`_ directory. +The `crust README`_ has more information about the building process, including +information about where to get OpenRISC cross compilers. + +Building the U-Boot image +------------------------- +Find the U-Boot defconfig file for your board first. Those files live in +the ``configs/`` directory; you can grep for the stub name of the devicetree +file, if you know that, or for the SoC name to find the right version:: + + $ git grep -l MACH_SUN8I_H3 configs + $ git grep -l sun50i-h6-orangepi-3 configs + +The `linux-sunxi`_ wiki also lists the name of the defconfig file in the +respective board page. Then use this defconfig file to create the .config +file, and build the image:: + + $ make _defconfig + $ make + +For 64-bit boards, this requires either the BL31 environment variable to be +set (as shown above in the TF-A build example), or it to be supplied on the +build command line:: + + $ make BL31=/src/tf-a.git/build/sun50i_h616/debug/bl31.bin + +The same applies to the (optional) SCP firmware. + +The file containing everything you need is called ``u-boot-sunxi-with-spl.bin``, +you will find it in the root folder of your U-Boot (build) tree. Except for +raw NAND flash devices this very same file can be used for any boot source. +It will contain the SPL image, fitted with the proper signature recognised by +the BROM, and the required checksum. Also it will contain at least U-Boot +proper, either wrapped in the legacy U-Boot image format, or in a FIT image. +The board's devicetree is also included, either appended to the U-Boot proper +image, or contained in the FIT image. If required by the SoC, this FIT file will +also include the other firmware images. + +Installing U-Boot +----------------- + +Installing on a (micro-) SD card +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +All Allwinner SoCs will try to find a boot image at sector 16 (8KB) of +an SD card, connected to the first MMC controller. To transfer the generated +image to an SD card, from any Linux device (including the board itself) with +an (micro-)SD card reader, type:: + + $ sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdX bs=1k seek=8 + +``/dev/sdx`` needs to be replaced with the block device name of the SD card +reader. On some machines this could be ``/dev/mmcblkX``. +Newer SoCs (starting from the H3 from 2014, and including all ARM64 SoCs), +also look at sector 256 (128KB) for the signature (after having checked the +8KB location). Installing the firmware there has the advantage of not +overlapping with a GPT partition table. Simply replace the "``seek=8``" above +with "``seek=128``". + +You can also use an existing (mainline) U-Boot to write to the SD card. Load +the generated U-Boot image somewhere into DRAM (via ``ext4load``, ``fatload``, +or ``tftpboot``), then write to MMC device 0:: + + => fatload mmc 0:1 $kernel_addr_r u-boot-sunxi-with-spl.bin + => mmc dev 0 + => mmc write $kernel_addr_r 0x10 0x7f0 + +To use the alternative boot location on newer SoCs:: + + => mmc write $kernel_addr_r 0x100 0x700 + +Installing on eMMC (on-board flash memory) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Some boards have a soldered eMMC chip, some other boards have an eMMC socket +to receive an optional eMMC module. U-Boot can be installed to those chips, +to boot without an SD card inserted. The Boot-ROM can boot either from the +regular user data partition, or from one of the separate eMMC boot partitions. +U-Boot can be installed either from a running Linux instance on the device, +from a running (mainline) U-Boot, or via an adapter for the (removable) +eMMC module. + +Installing on an eMMC user data partition from Linux +```````````````````````````````````````````````````` +If you have a running Linux instance on the device, and have somehow copied +over the image file to that device, you can write the image directly into the +eMMC device from there. +Find the name of the block device file first, it is one of the +``/dev/mmcblk`` devices. eMMC devices typically also list a +``/dev/mmcblkboot0`` partition (see below), this helps you to tell it apart +from the SD card device. +To install onto the user data partition:: + + $ sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/dev/mmcblkX bs=1k seek=8 + +Similar to SD cards, the BROM in newer SoCs (H3 and above) also checks +sector 256 of an eMMC, so you can use "``seek=128``" as well. Having a GPT +on an eMMC device is much more likely than on an SD card, so you should +probably stick to the alternative location, or use one of the boot partitions. + +Installing on an eMMC boot partition from Linux +``````````````````````````````````````````````` +In the following examples, ``/dev/mmcblkX`` needs to be replaced with the block +device name of the eMMC device. The eMMC device can be recognised by also +listing the boot partitions (``/dev/mmcblkXboot0``) in ``/proc/partitions``. + +To allow booting from one of the eMMC boot partitions, this one needs to be +enabled first. This only needs to be done once, as this setting is +persistent, even though the boot partition can be disabled or changed again +any time later:: + + # apt-get install mmc-utils + # mmc bootbus set single_hs x1 x4 /dev/mmcblkX + # mmc bootpart enable 1 1 /dev/mmcblkX + +The first "1" in the last command points to the boot partition number to be +used, typically devices offer two boot partitions. + +By default Linux disables write access to the boot partitions, to prevent +accidental overwrites. You need to disable the write protection (until the +next reboot), then can write the U-Boot image to the *first* sector of the +selected boot partition:: + + # echo 0 > /sys/block/mmcblkXboot0/force_ro + # dd if=u-boot-sunxi-with-spl.bin of=/dev/mmcblkXboot0 bs=1k + +Installing on an eMMC user data partition from U-Boot +````````````````````````````````````````````````````` +You can also write the generated image file to an SD card, boot the device +from there, and burn the very same image to the eMMC device from U-Boot. +The following commands copy the image from the SD card to the eMMC device:: + + => mmc dev 0 + => mmc read $kernel_addr_r 0x10 0x7f0 + => mmc dev 1 + => mmc write $kernel_addr_r 0x10 0x7f0 + +You can also copy an image from the 8K offset of an SD card to the 128K +offset of the eMMC (or any combination), just change the "``0x10 0x7f0``" above +to "``0x100 0x700``", respectively. Of course the image file can be loaded via +any other loading method, including ``fatload``, ``ext4load``, ``tftpboot``. + +Installing on an eMMC boot partition from U-Boot +```````````````````````````````````````````````` +The selected eMMC boot partition needs to be initially enabled first (same +as in Linux above), you can do this from U-Boot with:: + + => mmc dev 1 + => mmc bootbus 1 1 0 0 + => mmc partconf 1 1 1 1 + +The first "1" in both commands denotes the MMC device number. The second "1" +in the partconf command sets the required ``BOOT_ACK`` option, the last two "1"s +selects the active boot partition and the target for the next data access, +respectively. So for the next "``mmc write``" command to address one of the boot +partitions, the last number must either be "1" or "2", "0" would switch (back) +to the normal user data partition. + +Then load the ``u-boot-sunxi-with-spl.bin`` image file into DRAM, either by +reading directly from an SD card or eMMC user data partition, or from a +file system or TFTP (see above), and transfer it to the boot partition:: + + => tftpboot $kernel_addr_r u-boot-sunxi-with-spl.bin + => mmc write $kernel_addr_r 0 0x7f0 + +After that the device should boot from the selected boot partition, which takes +precedence over booting from the user data partition. + +Installing on SPI flash +^^^^^^^^^^^^^^^^^^^^^^^ +Some devices have a SPI NOR flash chip soldered on the board. If it is +connected to the SPI0 pins on PortC, the BROM can also boot from there. +Typically the SPI flash has the lowest boot priority, so SD card and eMMC +devices will be considered first. + +Installing on SPI flash from Linux +`````````````````````````````````` +If the devicetree enables and describes the SPI flash device, you can access +the SPI flash content from Linux, using the `MTD utils`_:: + + # apt-get install mtd-utils + # mtdinfo + # mtd_debug erase /dev/mtdX 0 0xf0000 + # mtd_debug write /dev/mtdX 0 0xf0000 u-boot-sunxi-with-spl.bin + +``/dev/mtdX`` needs to be replaced with the respective device name, as listed +in the output of ``mtdinfo``. + +Installing on SPI flash from U-Boot +``````````````````````````````````` +If SPI flash driver and command support (``CONFIG_CMD_SF``) is enabled in the +U-Boot configuration, the image file can be installed via U-Boot as well:: + + => tftpboot $kernel_addr_r u-boot-sunxi-with-spl.bin + => sf probe + => sf erase 0 +0xf0000 + => sf write $kernel_addr_r 0 $filesize + +Installing on SPI flash via USB in FEL mode +``````````````````````````````````````````` +If the device is in FEL mode (see below), the SPI flash can also be written to +with the sunxi-fel utility, via an USB(-OTG) cable from any USB host machine:: + + $ sunxi-fel spiflash-write 0 u-boot-sunxi-with-spl.bin + +Booting via the USB(-OTG) FEL mode +---------------------------------- +If none of the boot locations checked by the BROM contains a medium or valid +signature, the BROM will enter the so-called FEL mode, in which it will +listen to commands from a host on the SoC's USB-OTG interface. Those commands +allow to read from and write to arbitrary memory locations, also to start +execution at any address, which allows to bootstrap a board solely via an +USB cable. Some boards feature a "FEL" or "U-Boot" button, which forces +FEL mode despite a valid boot location being present. The same can be achieved +via a `magic binary`_ on an SD card, which allows to enter FEL mode on any +board. + +To use FEL booting, let the board enter FEL mode, via any of the mentioned +methods (no boot media, FEL button, SD card with FEL binary), then connect +a USB cable to the board's USB OTG port. Some boards (Pine64, TV boxes) don't +have a separate OTG port. In this case mostly one of the USB-A ports is +connected to USB0, and can be used via a non-standard USB-A to USB-A cable. + +Typically there is no on-board indication of FEL mode, other than a new USB +device appearing on the connected host computer. The USB vendor/device ID +is 1f3a:efe8. Mostly this will identify as "sunxi SoC OTG connector in +FEL/flashing mode", but older distributions might still report "Onda +(unverified) V972 tablet in flashing mode". + +The `sunxi_fel`_ tool implements the proprietary BROM protocol, and allows to +bootstrap U-Boot by just providing our venerable u-boot-sunxi-with-spl.bin:: + + $ sudo apt-get install sunxi-tools + $ sunxi-fel uboot u-boot-sunxi-with-spl.bin + +Additional binaries like a kernel, an initial ramdisk or a boot script, can +also be uploaded via FEL, check the Wiki's `FEL page`_ for more details. + +.. _`Arm Trusted Firmware-A`: https://www.trustedfirmware.org/projects/tf-a/ +.. _`docs/plat/allwinner.rst`: https://trustedfirmware-a.readthedocs.io/en/latest/plat/allwinner.html +.. _`crust`: https://github.com/crust-firmware/crust +.. _`configs/`: https://github.com/crust-firmware/crust/tree/master/configs +.. _`crust README`: https://github.com/crust-firmware/crust/blob/master/README.md#building-the-firmware +.. _`linux-sunxi`: https://linux-sunxi.org +.. _`MTD utils`: http://www.linux-mtd.infradead.org/ +.. _`magic binary`: https://github.com/linux-sunxi/sunxi-tools/raw/master/bin/fel-sdboot.sunxi +.. _`sunxi_fel`: https://github.com/linux-sunxi/sunxi-tools +.. _`FEL page`: https://linux-sunxi.org/FEL/USBBoot diff --git a/doc/board/index.rst b/doc/board/index.rst index 13f4db848e..0a02fec485 100644 --- a/doc/board/index.rst +++ b/doc/board/index.rst @@ -9,6 +9,7 @@ Board-specific doc actions/index advantech/index AndesTech/index + allwinner/index amlogic/index apple/index atmel/index From 726cd9836db0d698f1f409e9522828e985c912e2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:35 -0700 Subject: [PATCH 04/20] efi: Make unicode printf available to the app This is needed to show unicode strings. Enable this code in the app. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- lib/Kconfig | 2 +- lib/vsprintf.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/Kconfig b/lib/Kconfig index 10ba086b39..1883ac734d 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -52,7 +52,7 @@ config CC_OPTIMIZE_LIBS_FOR_SPEED config CHARSET bool - default y if UT_UNICODE || EFI_LOADER || UFS + default y if UT_UNICODE || EFI_LOADER || UFS || EFI_APP help Enables support for various conversions between different character sets, such as between unicode representations and diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e634bd70b6..de9f236b90 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -276,9 +276,8 @@ static char *string(char *buf, char *end, char *s, int field_width, } /* U-Boot uses UTF-16 strings in the EFI context only. */ -#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD) -static char *string16(char *buf, char *end, u16 *s, int field_width, - int precision, int flags) +static __maybe_unused char *string16(char *buf, char *end, u16 *s, + int field_width, int precision, int flags) { const u16 *str = s ? s : L""; ssize_t i, len = utf16_strnlen(str, precision); @@ -317,7 +316,6 @@ static char *device_path_string(char *buf, char *end, void *dp, int field_width, return buf; } #endif -#endif static char *mac_address_string(char *buf, char *end, u8 *addr, int field_width, int precision, int flags) @@ -616,7 +614,8 @@ repeat: case 's': /* U-Boot uses UTF-16 strings in the EFI context only. */ -#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD) +#if (CONFIG_IS_ENABLED(EFI_LOADER) || CONFIG_IS_ENABLED(EFI_APP)) && \ + !defined(API_BUILD) if (qualifier == 'l') { str = string16(str, end, va_arg(args, u16 *), field_width, precision, flags); From 613cd0c46796cae340382679bc01ef220daf3768 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:36 -0700 Subject: [PATCH 05/20] efi: Locate all block devices in the app When starting the app, locate all block devices and make them available to U-Boot. This allows listing partitions and accessing files in filesystems. EFI also has the concept of 'disks', meaning boot media. For now, this is not obviously useful in U-Boot, but add code to at least locate these. This can be expanded later as needed. We cannot use printf() in the early stub or app since it is not compiled in Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- doc/develop/uefi/u-boot_on_efi.rst | 4 +- include/efi.h | 6 +- include/efi_api.h | 15 +++ lib/efi/efi_app.c | 198 +++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 5 deletions(-) diff --git a/doc/develop/uefi/u-boot_on_efi.rst b/doc/develop/uefi/u-boot_on_efi.rst index 5f2f850f07..8f81b79907 100644 --- a/doc/develop/uefi/u-boot_on_efi.rst +++ b/doc/develop/uefi/u-boot_on_efi.rst @@ -265,9 +265,7 @@ This work could be extended in a number of ways: - Figure out how to solve the interrupt problem -- Add more drivers to the application side (e.g. block devices, USB, - environment access). This would mostly be an academic exercise as a strong - use case is not readily apparent, but it might be fun. +- Add more drivers to the application side (e.g.USB, environment access). - Avoid turning off boot services in the stub. Instead allow U-Boot to make use of boot services in case it wants to. It is unclear what it might want diff --git a/include/efi.h b/include/efi.h index 1432038838..cd0bdcc717 100644 --- a/include/efi.h +++ b/include/efi.h @@ -419,10 +419,12 @@ struct efi_priv { * * @handle: handle of the controller on which this driver is installed * @blkio: block io protocol proxied by this driver + * @device_path: EFI path to the device */ struct efi_media_plat { - efi_handle_t handle; - struct efi_block_io *blkio; + efi_handle_t handle; + struct efi_block_io *blkio; + struct efi_device_path *device_path; }; /* Base address of the EFI image */ diff --git a/include/efi_api.h b/include/efi_api.h index 80109f012b..ec9fa89a93 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -2035,4 +2035,19 @@ struct efi_firmware_management_protocol { const u16 *package_version_name); }; +#define EFI_DISK_IO_PROTOCOL_GUID \ + EFI_GUID(0xce345171, 0xba0b, 0x11d2, 0x8e, 0x4f, \ + 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) + +struct efi_disk { + u64 revision; + efi_status_t (EFIAPI *read_disk)(struct efi_disk *this, u32 media_id, + u64 offset, efi_uintn_t buffer_size, + void *buffer); + + efi_status_t (EFIAPI *write_disk)(struct efi_disk *this, u32 media_id, + u64 offset, efi_uintn_t buffer_size, + void *buffer); +}; + #endif diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c index f61665686c..4a80633edf 100644 --- a/lib/efi/efi_app.c +++ b/lib/efi/efi_app.c @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include DECLARE_GLOBAL_DATA_PTR; @@ -46,6 +49,49 @@ int efi_info_get(enum efi_entry_t type, void **datap, int *sizep) return -ENOSYS; } +/** + * efi_bind_block() - bind a new block device to an EFI device + * + * Binds a new top-level EFI_MEDIA device as well as a child block device so + * that the block device can be accessed in U-Boot. + * + * The device can then be accessed using 'part list efi 0', 'fat ls efi 0:1', + * for example, just like any other interface type. + * + * @handle: handle of the controller on which this driver is installed + * @blkio: block io protocol proxied by this driver + * @device_path: EFI device path structure for this + * @len: Length of @device_path in bytes + * @devp: Returns the bound device + * @return 0 if OK, -ve on error + */ +int efi_bind_block(efi_handle_t handle, struct efi_block_io *blkio, + struct efi_device_path *device_path, int len, + struct udevice **devp) +{ + struct efi_media_plat plat; + struct udevice *dev; + char name[18]; + int ret; + + plat.handle = handle; + plat.blkio = blkio; + plat.device_path = malloc(device_path->length); + if (!plat.device_path) + return log_msg_ret("path", -ENOMEM); + memcpy(plat.device_path, device_path, device_path->length); + ret = device_bind(dm_root(), DM_DRIVER_GET(efi_media), "efi_media", + &plat, ofnode_null(), &dev); + if (ret) + return log_msg_ret("bind", ret); + + snprintf(name, sizeof(name), "efi_media_%x", dev_seq(dev)); + device_set_name(dev, name); + *devp = dev; + + return 0; +} + static efi_status_t setup_memory(struct efi_priv *priv) { struct efi_boot_services *boot = priv->boot; @@ -91,6 +137,14 @@ static efi_status_t setup_memory(struct efi_priv *priv) return 0; } +/** + * free_memory() - Free memory used by the U-Boot app + * + * This frees memory allocated in setup_memory(), in preparation for returning + * to UEFI. It also zeroes the global_data pointer. + * + * @priv: Private EFI data + */ static void free_memory(struct efi_priv *priv) { struct efi_boot_services *boot = priv->boot; @@ -105,6 +159,150 @@ static void free_memory(struct efi_priv *priv) global_data_ptr = NULL; } +/** + * devpath_is_partition() - Figure out if a device path is a partition + * + * Checks if a device path refers to a partition on some media device. This + * works by checking for a valid partition number in a hard-driver media device + * as the final component of the device path. + * + * @path: device path + * Return: true if a partition, false if not + * (e.g. it might be media which contains partitions) + */ +static bool devpath_is_partition(const struct efi_device_path *path) +{ + const struct efi_device_path *p; + bool was_part; + + for (p = path; p->type != DEVICE_PATH_TYPE_END; + p = (void *)p + p->length) { + was_part = false; + if (p->type == DEVICE_PATH_TYPE_MEDIA_DEVICE && + p->sub_type == DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH) { + struct efi_device_path_hard_drive_path *hd = + (void *)path; + + if (hd->partition_number) + was_part = true; + } + } + + return was_part; +} + +/** + * setup_block() - Find all block devices and setup EFI devices for them + * + * Partitions are ignored, since U-Boot has partition handling. Errors with + * particular devices produce a warning but execution continues to try to + * find others. + * + * Return: 0 if found, -ENOSYS if there is no boot-services table, -ENOTSUPP + * if a required protocol is not supported + */ +static int setup_block(void) +{ + efi_guid_t efi_blkio_guid = EFI_BLOCK_IO_PROTOCOL_GUID; + efi_guid_t efi_devpath_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; + efi_guid_t efi_pathutil_guid = EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID; + efi_guid_t efi_pathtext_guid = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; + struct efi_boot_services *boot = efi_get_boot(); + struct efi_device_path_utilities_protocol *util; + struct efi_device_path_to_text_protocol *text; + struct efi_device_path *path; + struct efi_block_io *blkio; + efi_uintn_t num_handles; + efi_handle_t *handle; + int ret, i; + + if (!boot) + return log_msg_ret("sys", -ENOSYS); + + /* Find all devices which support the block I/O protocol */ + ret = boot->locate_handle_buffer(BY_PROTOCOL, &efi_blkio_guid, NULL, + &num_handles, &handle); + if (ret) + return log_msg_ret("loc", -ENOTSUPP); + log_debug("Found %d handles:\n", (int)num_handles); + + /* We need to look up the path size and convert it to text */ + ret = boot->locate_protocol(&efi_pathutil_guid, NULL, (void **)&util); + if (ret) + return log_msg_ret("util", -ENOTSUPP); + ret = boot->locate_protocol(&efi_pathtext_guid, NULL, (void **)&text); + if (ret) + return log_msg_ret("text", -ENOTSUPP); + + for (i = 0; i < num_handles; i++) { + struct udevice *dev; + const u16 *name; + bool is_part; + int len; + + ret = boot->handle_protocol(handle[i], &efi_devpath_guid, + (void **)&path); + if (ret) { + log_warning("- devpath %d failed (ret=%d)\n", i, ret); + continue; + } + + ret = boot->handle_protocol(handle[i], &efi_blkio_guid, + (void **)&blkio); + if (ret) { + log_warning("- blkio %d failed (ret=%d)\n", i, ret); + continue; + } + + name = text->convert_device_path_to_text(path, true, false); + is_part = devpath_is_partition(path); + + if (!is_part) { + len = util->get_device_path_size(path); + ret = efi_bind_block(handle[i], blkio, path, len, &dev); + if (ret) { + log_warning("- blkio bind %d failed (ret=%d)\n", + i, ret); + continue; + } + } else { + dev = NULL; + } + + /* + * Show the device name if we created one. Otherwise indicate + * that it is a partition. + */ + printf("%2d: %-12s %ls\n", i, dev ? dev->name : "", + name); + } + boot->free_pool(handle); + + return 0; +} + +/** + * dm_scan_other() - Scan for UEFI devices that should be available to U-Boot + * + * This sets up block devices within U-Boot for those found in UEFI. With this, + * U-Boot can access those devices + * + * @pre_reloc_only: true to only bind pre-relocation devices (ignored) + * Returns: 0 on success, -ve on error + */ +int dm_scan_other(bool pre_reloc_only) +{ + if (gd->flags & GD_FLG_RELOC) { + int ret; + + ret = setup_block(); + if (ret) + return ret; + } + + return 0; +} + /** * efi_main() - Start an EFI image * From 299606611055075daef60be250eec80cd337a141 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:37 -0700 Subject: [PATCH 06/20] efi: serial: Support arrow keys At present only the backspace key is supported in U-Boot, when running as an EFI app. Add support for arrows, home and end as well, to make the CLI more friendly. Signed-off-by: Simon Glass Acked-by: Heinrich Schuchardt --- drivers/serial/serial_efi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/serial/serial_efi.c b/drivers/serial/serial_efi.c index 33ddbd6080..0067576389 100644 --- a/drivers/serial/serial_efi.c +++ b/drivers/serial/serial_efi.c @@ -24,6 +24,9 @@ struct serial_efi_priv { bool have_key; }; +/* Convert a lower-case character to its ctrl-char equivalent */ +#define CTL_CH(c) ((c) - 'a' + 1) + int serial_efi_setbrg(struct udevice *dev, int baudrate) { return 0; @@ -49,6 +52,7 @@ static int serial_efi_get_key(struct serial_efi_priv *priv) static int serial_efi_getc(struct udevice *dev) { struct serial_efi_priv *priv = dev_get_priv(dev); + char conv_scan[10] = {0, 'p', 'n', 'f', 'b', 'a', 'e', 0, 8}; int ret, ch; ret = serial_efi_get_key(priv); @@ -63,8 +67,11 @@ static int serial_efi_getc(struct udevice *dev) * key scan code of 8. Handle this so that backspace works correctly * in the U-Boot command line. */ - if (!ch && priv->key.scan_code == 8) - ch = 8; + if (!ch && priv->key.scan_code < sizeof(conv_scan)) { + ch = conv_scan[priv->key.scan_code]; + if (ch >= 'a') + ch -= 'a' - 1; + } debug(" [%x %x %x] ", ch, priv->key.unicode_char, priv->key.scan_code); return ch; From 6e7ad4a45f6e2b036fc56942293b8471ece3341d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:38 -0700 Subject: [PATCH 07/20] x86: Allow booting a kernel from the EFI app At present this is disabled, but it should work so long as the kernel does not need EFI services. Enable it and add a note about remaining work. Signed-off-by: Simon Glass --- arch/x86/lib/bootm.c | 11 +++++++---- doc/develop/uefi/u-boot_on_efi.rst | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/x86/lib/bootm.c b/arch/x86/lib/bootm.c index 667e5e689e..57cba5c65d 100644 --- a/arch/x86/lib/bootm.c +++ b/arch/x86/lib/bootm.c @@ -179,10 +179,14 @@ int boot_linux_kernel(ulong setup_base, ulong load_address, bool image_64bit) * U-Boot is setting them up that way for itself in * arch/i386/cpu/cpu.c. * - * Note that we cannot currently boot a kernel while running as - * an EFI application. Please use the payload option for that. + * Note: this is incomplete for EFI kernels! + * + * This can boot a kernel while running as an EFI application, + * but if the kernel requires EFI support then that support needs + * to be enabled first (see EFI_LOADER). Also the EFI information + * must enabled with setup_efi_info(). See setup_zimage() for + * how this is done with the stub. */ -#ifndef CONFIG_EFI_APP __asm__ __volatile__ ( "movl $0, %%ebp\n" "cli\n" @@ -191,7 +195,6 @@ int boot_linux_kernel(ulong setup_base, ulong load_address, bool image_64bit) [boot_params] "S"(setup_base), "b"(0), "D"(0) ); -#endif } /* We can't get to here */ diff --git a/doc/develop/uefi/u-boot_on_efi.rst b/doc/develop/uefi/u-boot_on_efi.rst index 8f81b79907..acad6397e8 100644 --- a/doc/develop/uefi/u-boot_on_efi.rst +++ b/doc/develop/uefi/u-boot_on_efi.rst @@ -269,7 +269,7 @@ This work could be extended in a number of ways: - Avoid turning off boot services in the stub. Instead allow U-Boot to make use of boot services in case it wants to. It is unclear what it might want - though. + though. It is better to use the app. Where is the code? ------------------ From 440c6645aa501e9dfe96fa314c1db78cbb42f453 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:39 -0700 Subject: [PATCH 08/20] x86: Don't process the kernel command line unless enabled If the 'bootm' command is not enabled then this code is not available and this causes a link error. Fix it. Note that for the EFI app, there is no indication of missing code. It just hangs! Signed-off-by: Simon Glass Signed-off-by: Heinrich Schuchardt --- arch/x86/lib/zimage.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c index 7ce02226ef..9cc0449030 100644 --- a/arch/x86/lib/zimage.c +++ b/arch/x86/lib/zimage.c @@ -365,11 +365,14 @@ int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot, strcpy(cmd_line, (char *)cmdline_force); else build_command_line(cmd_line, auto_boot); - ret = bootm_process_cmdline(cmd_line, max_size, BOOTM_CL_ALL); - if (ret) { - printf("Cmdline setup failed (max_size=%x, bootproto=%x, err=%d)\n", - max_size, bootproto, ret); - return ret; + if (IS_ENABLED(CONFIG_CMD_BOOTM)) { + ret = bootm_process_cmdline(cmd_line, max_size, + BOOTM_CL_ALL); + if (ret) { + printf("Cmdline setup failed (max_size=%x, bootproto=%x, err=%d)\n", + max_size, bootproto, ret); + return ret; + } } printf("Kernel command line: \""); puts(cmd_line); From 7f5419a64760e279556c548ec29cf48b8614c2f6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:40 -0700 Subject: [PATCH 09/20] x86: efi: Add room for the binman definition in the dtb At present only 4KB of spare space is left in the DTB when building the EFI app. Increase this to 32KB so there is plenty of space to insert the binman definition. This cannot be expanded later (as with OF_SEPARATE) because the ELF image has already been built. Signed-off-by: Simon Glass Reviwed-by: Heinrich Schuchardt --- arch/x86/dts/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/dts/Makefile b/arch/x86/dts/Makefile index be209aaaf8..5c8c05ec49 100644 --- a/arch/x86/dts/Makefile +++ b/arch/x86/dts/Makefile @@ -24,7 +24,7 @@ dtb-y += bayleybay.dtb \ targets += $(dtb-y) -DTC_FLAGS += -R 4 -p 0x1000 +DTC_FLAGS += -R 4 -p $(if $(CONFIG_EFI_APP),0x8000,0x1000) PHONY += dtbs dtbs: $(addprefix $(obj)/, $(dtb-y)) From bf5236f3ba73231d5065a0500910b1e3216b960f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:41 -0700 Subject: [PATCH 10/20] efi: Drop device_path from struct efi_priv This is not used anywhere drop it. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- include/efi.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/efi.h b/include/efi.h index cd0bdcc717..0cd4b46600 100644 --- a/include/efi.h +++ b/include/efi.h @@ -402,7 +402,6 @@ static inline struct efi_mem_desc *efi_get_next_mem_desc( struct efi_priv { efi_handle_t parent_image; - struct efi_device_path *device_path; struct efi_system_table *sys_table; struct efi_boot_services *boot; struct efi_runtime_services *run; From efd35c7d59814435cadfdd5296337cdeacd66948 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:42 -0700 Subject: [PATCH 11/20] efi: Add comments to struct efi_priv This structure is uncommented. Fix it. Signed-off-by: Simon Glass --- include/efi.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/efi.h b/include/efi.h index 0cd4b46600..ac50a9c899 100644 --- a/include/efi.h +++ b/include/efi.h @@ -400,14 +400,37 @@ static inline struct efi_mem_desc *efi_get_next_mem_desc( return (struct efi_mem_desc *)((ulong)desc + map->desc_size); } +/** + * struct efi_priv - Information about the environment provided by EFI + * + * @parent_image: image passed into the EFI app or stub + * @sys_table: Pointer to system table + * @boot: Pointer to boot-services table + * @run: Pointer to runtime-services table + * + * @use_pool_for_malloc: true if all allocation should go through the EFI 'pool' + * methods allocate_pool() and free_pool(); false to use 'pages' methods + * allocate_pages() and free_pages() + * @ram_base: Base address of RAM (size CONFIG_EFI_RAM_SIZE) + * @image_data_type: Type of the loaded image (e.g. EFI_LOADER_CODE) + * + * @info: Header of the info list, holding info collected by the stub and passed + * to U-Boot + * @info_size: Size of the info list @info in bytes + * @next_hdr: Pointer to where to put the next header when adding to the list + */ struct efi_priv { efi_handle_t parent_image; struct efi_system_table *sys_table; struct efi_boot_services *boot; struct efi_runtime_services *run; + + /* app: */ bool use_pool_for_malloc; unsigned long ram_base; unsigned int image_data_type; + + /* stub: */ struct efi_info_hdr *info; unsigned int info_size; void *next_hdr; From 9079486461c369df3f5a1d8bfb5003f8769554dc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:43 -0700 Subject: [PATCH 12/20] efi: Fix ll_boot_init() operation with the app This should return false when the EFI app is running, since UEFI has done the required low-level init. Fix it. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- include/init.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/init.h b/include/init.h index f2cd46dead..dcd682c1bf 100644 --- a/include/init.h +++ b/include/init.h @@ -14,8 +14,11 @@ #include -/* Avoid using CONFIG_EFI_STUB directly as we may boot from other loaders */ -#ifdef CONFIG_EFI_STUB +/* + * In case of the EFI app the UEFI firmware provides the low-level + * initialisation. + */ +#ifdef CONFIG_EFI #define ll_boot_init() false #else #include From 184be592580178dd149800459c8817199160ca22 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:44 -0700 Subject: [PATCH 13/20] efi: Add a few comments to the stub Comment some functions that need more information. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- lib/efi/efi_stub.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/efi/efi_stub.c b/lib/efi/efi_stub.c index b3393e47fa..31f1e1a72a 100644 --- a/lib/efi/efi_stub.c +++ b/lib/efi/efi_stub.c @@ -225,6 +225,22 @@ static int get_codeseg32(void) return cs32; } +/** + * setup_info_table() - sets up a table containing information from EFI + * + * We must call exit_boot_services() before jumping out of the stub into U-Boot + * proper, so that U-Boot has full control of peripherals, memory, etc. + * + * Once we do this, we cannot call any boot-services functions so we must find + * out everything we need to before doing that. + * + * Set up a struct efi_info_hdr table which can hold various records (e.g. + * struct efi_entry_memmap) with information obtained from EFI. + * + * @priv: Pointer to our private information which contains the list + * @size: Size of the table to allocate + * Return: 0 if OK, non-zero on error + */ static int setup_info_table(struct efi_priv *priv, int size) { struct efi_info_hdr *info; @@ -248,6 +264,19 @@ static int setup_info_table(struct efi_priv *priv, int size) return 0; } +/** + * add_entry_addr() - Add a new entry to the efi_info list + * + * This adds an entry, consisting of a tag and two lots of data. This avoids the + * caller having to coalesce the data first + * + * @priv: Pointer to our private information which contains the list + * @type: Type of the entry to add + * @ptr1: Pointer to first data block to add + * @size1: Size of first data block in bytes (can be 0) + * @ptr2: Pointer to second data block to add + * @size2: Size of second data block in bytes (can be 0) + */ static void add_entry_addr(struct efi_priv *priv, enum efi_entry_t type, void *ptr1, int size1, void *ptr2, int size2) { From 2a1cf03ea4ff9a43fd990dc9ae0110464569c59b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:45 -0700 Subject: [PATCH 14/20] efi: Share struct efi_priv between the app and stub code At present each of these has its own static variable and helper functions. Move them into a shared file. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- include/efi.h | 21 +++++++++++++++++++++ lib/efi/efi.c | 29 +++++++++++++++++++++++++++++ lib/efi/efi_app.c | 21 ++------------------- lib/efi/efi_stub.c | 7 ++++--- 4 files changed, 56 insertions(+), 22 deletions(-) diff --git a/include/efi.h b/include/efi.h index ac50a9c899..8c3f332468 100644 --- a/include/efi.h +++ b/include/efi.h @@ -474,6 +474,27 @@ extern char _binary_u_boot_bin_start[], _binary_u_boot_bin_end[]; EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \ EFI_VARIABLE_APPEND_WRITE) +/** + * efi_get_priv() - Get access to the EFI-private information + * + * This struct it used by both the stub and the app to record things about the + * EFI environment. It is not available in U-Boot proper after the stub has + * jumped there. Use efi_info_get() to obtain info in that case. + * + * Return: pointer to private info + */ +struct efi_priv *efi_get_priv(void); + +/** + * efi_set_priv() - Set up a pointer to the EFI-private information + * + * This is called in the stub and app to record the location of this + * information. + * + * @priv: New location of private data + */ +void efi_set_priv(struct efi_priv *priv); + /** * efi_get_sys_table() - Get access to the main EFI system table * diff --git a/lib/efi/efi.c b/lib/efi/efi.c index 69e52e4574..cd6bf47b18 100644 --- a/lib/efi/efi.c +++ b/lib/efi/efi.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* + * Functions shared by the app and stub + * * Copyright (c) 2015 Google, Inc * * EFI information obtained here: @@ -17,6 +19,33 @@ #include #include +static struct efi_priv *global_priv; + +struct efi_priv *efi_get_priv(void) +{ + return global_priv; +} + +void efi_set_priv(struct efi_priv *priv) +{ + global_priv = priv; +} + +struct efi_system_table *efi_get_sys_table(void) +{ + return global_priv->sys_table; +} + +struct efi_boot_services *efi_get_boot(void) +{ + return global_priv->boot; +} + +unsigned long efi_get_ram_base(void) +{ + return global_priv->ram_base; +} + /* * Global declaration of gd. * diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c index 4a80633edf..214cef9430 100644 --- a/lib/efi/efi_app.c +++ b/lib/efi/efi_app.c @@ -27,23 +27,6 @@ DECLARE_GLOBAL_DATA_PTR; -static struct efi_priv *global_priv; - -struct efi_system_table *efi_get_sys_table(void) -{ - return global_priv->sys_table; -} - -struct efi_boot_services *efi_get_boot(void) -{ - return global_priv->boot; -} - -unsigned long efi_get_ram_base(void) -{ - return global_priv->ram_base; -} - int efi_info_get(enum efi_entry_t type, void **datap, int *sizep) { return -ENOSYS; @@ -319,7 +302,7 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, /* Set up access to EFI data structures */ efi_init(priv, "App", image, sys_table); - global_priv = priv; + efi_set_priv(priv); /* * Set up the EFI debug UART so that printf() works. This is @@ -345,7 +328,7 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, static void efi_exit(void) { - struct efi_priv *priv = global_priv; + struct efi_priv *priv = efi_get_priv(); free_memory(priv); printf("U-Boot EFI exiting\n"); diff --git a/lib/efi/efi_stub.c b/lib/efi/efi_stub.c index 31f1e1a72a..c89ae7c907 100644 --- a/lib/efi/efi_stub.c +++ b/lib/efi/efi_stub.c @@ -31,7 +31,6 @@ #error "This file needs to be ported for use on architectures" #endif -static struct efi_priv *global_priv; static bool use_uart; struct __packed desctab_info { @@ -63,6 +62,8 @@ void _debug_uart_init(void) void putc(const char ch) { + struct efi_priv *priv = efi_get_priv(); + if (ch == '\n') putc('\r'); @@ -73,7 +74,7 @@ void putc(const char ch) ; outb(ch, (ulong)&com_port->thr); } else { - efi_putc(global_priv, ch); + efi_putc(priv, ch); } } @@ -320,7 +321,7 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, puts(" efi_init() failed\n"); return ret; } - global_priv = priv; + efi_set_priv(priv); cs32 = get_codeseg32(); if (cs32 < 0) From bc53a35ac526c575a91c12af21f7106d54f7a496 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:47 -0700 Subject: [PATCH 15/20] efi: Check for failure when initing the app The stub checks for failure with efi_init(). Add this for the app as well. It is unlikely that anything can be done, but we may as well stop. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- lib/efi/efi_app.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c index 214cef9430..32ed5a0d04 100644 --- a/lib/efi/efi_app.c +++ b/lib/efi/efi_app.c @@ -300,8 +300,11 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, efi_status_t ret; /* Set up access to EFI data structures */ - efi_init(priv, "App", image, sys_table); - + ret = efi_init(priv, "App", image, sys_table); + if (ret) { + printf("Failed to set up U-Boot: err=%lx\n", ret); + return ret; + } efi_set_priv(priv); /* From cf376037e232869fd86cf2658bdf297fbe004055 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:48 -0700 Subject: [PATCH 16/20] efi: Mention that efi_info_get() is only used in the stub This provides access to EFI tables after U-Boot has exited boot services. It is not needed in the app since boot services remain alive and we can just call them whenever needed. Add a comment to explain this. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- include/efi.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/efi.h b/include/efi.h index 8c3f332468..877a2e5a8d 100644 --- a/include/efi.h +++ b/include/efi.h @@ -566,6 +566,10 @@ void efi_putc(struct efi_priv *priv, const char ch); /** * efi_info_get() - get an entry from an EFI table * + * This function is called from U-Boot proper to read information set up by the + * EFI stub. It can only be used when running from the EFI stub, not when U-Boot + * is running as an app. + * * @type: Entry type to search for * @datap: Returns pointer to entry data * @sizep: Returns pointer to entry size From 62725e661a17d1c3f3784f115917560c1126aeaa Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:49 -0700 Subject: [PATCH 17/20] efi: Show when allocated pages are used Add a message here so that both paths of memory allocation are reported. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- lib/efi/efi_app.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c index 32ed5a0d04..d60f2f6c28 100644 --- a/lib/efi/efi_app.c +++ b/lib/efi/efi_app.c @@ -106,13 +106,14 @@ static efi_status_t setup_memory(struct efi_priv *priv) ret = boot->allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, priv->image_data_type, pages, &addr); if (ret) { - printf("(using pool %lx) ", ret); + log_info("(using pool %lx) ", ret); priv->ram_base = (ulong)efi_malloc(priv, CONFIG_EFI_RAM_SIZE, &ret); if (!priv->ram_base) return ret; priv->use_pool_for_malloc = true; } else { + log_info("(using allocated RAM address %lx) ", (ulong)addr); priv->ram_base = addr; } gd->ram_size = pages << 12; From 13bfaab3dab42d181327623208c0c299652632eb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:50 -0700 Subject: [PATCH 18/20] efi: Allow easy selection of serial-only operation Add info about how to select vidconsole or serial. Also set up a demo boot command. Signed-off-by: Simon Glass --- include/configs/efi-x86_app.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/configs/efi-x86_app.h b/include/configs/efi-x86_app.h index 6061a6db0a..33afb7ca0f 100644 --- a/include/configs/efi-x86_app.h +++ b/include/configs/efi-x86_app.h @@ -10,8 +10,33 @@ #undef CONFIG_TPM_TIS_BASE_ADDRESS +/* + * Select the output device: Put an 'x' prefix before one of these to disable it + */ + +/* + * Video output - can normally continue after exit_boot_services has been + * called, since output to the display does not require EFI services at that + * point. U-Boot sets up the console memory and does its own drawing. + */ #define CONFIG_STD_DEVICES_SETTINGS "stdin=serial\0" \ "stdout=vidconsole\0" \ "stderr=vidconsole\0" +/* + * Serial output with no console. Run qemu with: + * + * -display none -serial mon:stdio + * + * This will hang or fail to output on the console after exit_boot_services is + * called. + */ +#define xCONFIG_STD_DEVICES_SETTINGS "stdin=serial\0" \ + "stdout=serial\0" \ + "stderr=serial\0" + +#undef CONFIG_BOOTCOMMAND + +#define CONFIG_BOOTCOMMAND "part list efi 0; fatls efi 0:1" + #endif From 40b172314bb68886fce409efbd4db7cbe9d0926a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:53 -0700 Subject: [PATCH 19/20] x86: efi: Show the system-table revision Show the revision of this table as it can be important. Also update the 'efi table' entry to show the actual address of the EFI table rather than our table that points to it. This saves a step and the intermediate table has nothing else in it. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- arch/x86/cpu/efi/payload.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/cpu/efi/payload.c b/arch/x86/cpu/efi/payload.c index 3a9f7d7286..04ce1880b4 100644 --- a/arch/x86/cpu/efi/payload.c +++ b/arch/x86/cpu/efi/payload.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -296,8 +297,14 @@ void setup_efi_info(struct efi_info *efi_info) void efi_show_bdinfo(void) { struct efi_entry_systable *table = NULL; + struct efi_system_table *sys_table; int size, ret; ret = efi_info_get(EFIET_SYS_TABLE, (void **)&table, &size); - bdinfo_print_num_l("efi_table", (ulong)table); + if (!ret) { + bdinfo_print_num_l("efi_table", table->sys_table); + sys_table = (struct efi_system_table *)(uintptr_t) + table->sys_table; + bdinfo_print_num_l(" revision", sys_table->fw_revision); + } } From 86bb48880d75653d692cd02edb81888a2ed2dbb2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 29 Dec 2021 11:57:54 -0700 Subject: [PATCH 20/20] x86: efi: Don't set up global_data again with EFI Since EFI does not relocate and uses the same global_data pointer throughout the board-init process, drop this unnecessary setup, to avoid a hang. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- common/board_r.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/board_r.c b/common/board_r.c index 6d520662db..760c2d05ed 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -841,9 +841,8 @@ void board_init_r(gd_t *new_gd, ulong dest_addr) * TODO(sjg@chromium.org): Consider doing this for all archs, or * dropping the new_gd parameter. */ -#if CONFIG_IS_ENABLED(X86_64) - arch_setup_gd(new_gd); -#endif + if (CONFIG_IS_ENABLED(X86_64) && !IS_ENABLED(CONFIG_EFI_APP)) + arch_setup_gd(new_gd); #ifdef CONFIG_NEEDS_MANUAL_RELOC int i;