aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/lib/efi_loader
diff options
context:
space:
mode:
Diffstat (limited to 'roms/u-boot/lib/efi_loader')
-rw-r--r--roms/u-boot/lib/efi_loader/.gitignore3
-rw-r--r--roms/u-boot/lib/efi_loader/Kconfig376
-rw-r--r--roms/u-boot/lib/efi_loader/Makefile69
-rw-r--r--roms/u-boot/lib/efi_loader/efi_acpi.c42
-rw-r--r--roms/u-boot/lib/efi_loader/efi_bootmgr.c201
-rw-r--r--roms/u-boot/lib/efi_loader/efi_boottime.c3783
-rw-r--r--roms/u-boot/lib/efi_loader/efi_capsule.c1055
-rw-r--r--roms/u-boot/lib/efi_loader/efi_console.c1316
-rw-r--r--roms/u-boot/lib/efi_loader/efi_device_path.c1290
-rw-r--r--roms/u-boot/lib/efi_loader/efi_device_path_to_text.c419
-rw-r--r--roms/u-boot/lib/efi_loader/efi_device_path_utilities.c199
-rw-r--r--roms/u-boot/lib/efi_loader/efi_disk.c666
-rw-r--r--roms/u-boot/lib/efi_loader/efi_dt_fixup.c168
-rw-r--r--roms/u-boot/lib/efi_loader/efi_esrt.c510
-rw-r--r--roms/u-boot/lib/efi_loader/efi_file.c1153
-rw-r--r--roms/u-boot/lib/efi_loader/efi_firmware.c477
-rw-r--r--roms/u-boot/lib/efi_loader/efi_freestanding.c122
-rw-r--r--roms/u-boot/lib/efi_loader/efi_gop.c532
-rw-r--r--roms/u-boot/lib/efi_loader/efi_helper.c98
-rw-r--r--roms/u-boot/lib/efi_loader/efi_hii.c1073
-rw-r--r--roms/u-boot/lib/efi_loader/efi_hii_config.c150
-rw-r--r--roms/u-boot/lib/efi_loader/efi_image_loader.c966
-rw-r--r--roms/u-boot/lib/efi_loader/efi_load_initrd.c236
-rw-r--r--roms/u-boot/lib/efi_loader/efi_load_options.c149
-rw-r--r--roms/u-boot/lib/efi_loader/efi_memory.c814
-rw-r--r--roms/u-boot/lib/efi_loader/efi_net.c993
-rw-r--r--roms/u-boot/lib/efi_loader/efi_rng.c179
-rw-r--r--roms/u-boot/lib/efi_loader/efi_root_node.c86
-rw-r--r--roms/u-boot/lib/efi_loader/efi_runtime.c959
-rw-r--r--roms/u-boot/lib/efi_loader/efi_setup.c341
-rw-r--r--roms/u-boot/lib/efi_loader/efi_signature.c784
-rw-r--r--roms/u-boot/lib/efi_loader/efi_smbios.c56
-rw-r--r--roms/u-boot/lib/efi_loader/efi_string.c43
-rw-r--r--roms/u-boot/lib/efi_loader/efi_tcg2.c1345
-rw-r--r--roms/u-boot/lib/efi_loader/efi_unicode_collation.c326
-rw-r--r--roms/u-boot/lib/efi_loader/efi_var_common.c394
-rw-r--r--roms/u-boot/lib/efi_loader/efi_var_file.c222
-rw-r--r--roms/u-boot/lib/efi_loader/efi_var_mem.c352
-rw-r--r--roms/u-boot/lib/efi_loader/efi_variable.c439
-rw-r--r--roms/u-boot/lib/efi_loader/efi_variable_tee.c745
-rw-r--r--roms/u-boot/lib/efi_loader/efi_watchdog.c92
-rw-r--r--roms/u-boot/lib/efi_loader/helloworld.c201
42 files changed, 23424 insertions, 0 deletions
diff --git a/roms/u-boot/lib/efi_loader/.gitignore b/roms/u-boot/lib/efi_loader/.gitignore
new file mode 100644
index 000000000..f2d7c1444
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/.gitignore
@@ -0,0 +1,3 @@
+*.efi
+*.so
+*.S
diff --git a/roms/u-boot/lib/efi_loader/Kconfig b/roms/u-boot/lib/efi_loader/Kconfig
new file mode 100644
index 000000000..156b39152
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/Kconfig
@@ -0,0 +1,376 @@
+config EFI_LOADER
+ bool "Support running UEFI applications"
+ depends on OF_LIBFDT && ( \
+ ARM && (SYS_CPU = arm1136 || \
+ SYS_CPU = arm1176 || \
+ SYS_CPU = armv7 || \
+ SYS_CPU = armv8) || \
+ X86 || RISCV || SANDBOX)
+ # We need EFI_STUB_64BIT to be set on x86_64 with EFI_STUB
+ depends on !EFI_STUB || !X86_64 || EFI_STUB_64BIT
+ # We need EFI_STUB_32BIT to be set on x86_32 with EFI_STUB
+ depends on !EFI_STUB || !X86 || X86_64 || EFI_STUB_32BIT
+ default y if !ARM || SYS_CPU = armv7 || SYS_CPU = armv8
+ select LIB_UUID
+ select HAVE_BLOCK_DEVICE
+ select REGEX
+ imply CFB_CONSOLE_ANSI
+ imply FAT
+ imply FAT_WRITE
+ imply USB_KEYBOARD_FN_KEYS
+ imply VIDEO_ANSI
+ help
+ Select this option if you want to run UEFI applications (like GNU
+ GRUB or iPXE) on top of U-Boot. If this option is enabled, U-Boot
+ will expose the UEFI API to a loaded application, enabling it to
+ reuse U-Boot's device drivers.
+
+if EFI_LOADER
+
+config CMD_BOOTEFI_BOOTMGR
+ bool "UEFI Boot Manager"
+ default y
+ help
+ Select this option if you want to select the UEFI binary to be booted
+ via UEFI variables Boot####, BootOrder, and BootNext. This enables the
+ 'bootefi bootmgr' command.
+
+config EFI_SETUP_EARLY
+ bool
+ default n
+
+choice
+ prompt "Store for non-volatile UEFI variables"
+ default EFI_VARIABLE_FILE_STORE
+ help
+ Select where non-volatile UEFI variables shall be stored.
+
+config EFI_VARIABLE_FILE_STORE
+ bool "Store non-volatile UEFI variables as file"
+ depends on FAT_WRITE
+ help
+ Select this option if you want non-volatile UEFI variables to be
+ stored as file /ubootefi.var on the EFI system partition.
+
+config EFI_MM_COMM_TEE
+ bool "UEFI variables storage service via OP-TEE"
+ depends on OPTEE
+ help
+ If OP-TEE is present and running StandAloneMM, dispatch all UEFI
+ variable related operations to that. The application will verify,
+ authenticate and store the variables on an RPMB.
+
+endchoice
+
+config EFI_VARIABLES_PRESEED
+ bool "Initial values for UEFI variables"
+ depends on EFI_VARIABLE_FILE_STORE
+ help
+ Include a file with the initial values for non-volatile UEFI variables
+ into the U-Boot binary. If this configuration option is set, changes
+ to authentication related variables (PK, KEK, db, dbx) are not
+ allowed.
+
+if EFI_VARIABLES_PRESEED
+
+config EFI_VAR_SEED_FILE
+ string "File with initial values of non-volatile UEFI variables"
+ default ubootefi.var
+ help
+ File with initial values of non-volatile UEFI variables. The file must
+ be in the same format as the storage in the EFI system partition. The
+ easiest way to create it is by setting the non-volatile variables in
+ U-Boot. If a relative file path is used, it is relative to the source
+ directory.
+
+endif
+
+config EFI_VAR_BUF_SIZE
+ int "Memory size of the UEFI variable store"
+ default 16384
+ range 4096 2147483647
+ help
+ This defines the size in bytes of the memory area reserved for keeping
+ UEFI variables.
+
+ When using StandAloneMM (CONFIG_EFI_MM_COMM_TEE=y) this value should
+ match the value of PcdFlashNvStorageVariableSize used to compile the
+ StandAloneMM module.
+
+ Minimum 4096, default 16384.
+
+config EFI_GET_TIME
+ bool "GetTime() runtime service"
+ depends on DM_RTC
+ default y
+ help
+ Provide the GetTime() runtime service at boottime. This service
+ can be used by an EFI application to read the real time clock.
+
+config EFI_SET_TIME
+ bool "SetTime() runtime service"
+ depends on EFI_GET_TIME
+ default y if ARCH_QEMU || SANDBOX
+ default n
+ help
+ Provide the SetTime() runtime service at boottime. This service
+ can be used by an EFI application to adjust the real time clock.
+
+config EFI_HAVE_CAPSULE_SUPPORT
+ bool
+
+config EFI_RUNTIME_UPDATE_CAPSULE
+ bool "UpdateCapsule() runtime service"
+ default n
+ select EFI_HAVE_CAPSULE_SUPPORT
+ help
+ Select this option if you want to use UpdateCapsule and
+ QueryCapsuleCapabilities API's.
+
+config EFI_CAPSULE_ON_DISK
+ bool "Enable capsule-on-disk support"
+ select EFI_HAVE_CAPSULE_SUPPORT
+ default n
+ help
+ Select this option if you want to use capsule-on-disk feature,
+ that is, capsules can be fetched and executed from files
+ under a specific directory on UEFI system partition instead of
+ via UpdateCapsule API.
+
+config EFI_IGNORE_OSINDICATIONS
+ bool "Ignore OsIndications for CapsuleUpdate on-disk"
+ depends on EFI_CAPSULE_ON_DISK
+ default n
+ help
+ There are boards where U-Boot does not support SetVariable at runtime.
+ Select this option if you want to use the capsule-on-disk feature
+ without setting the EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
+ flag in variable OsIndications.
+
+config EFI_CAPSULE_ON_DISK_EARLY
+ bool "Initiate capsule-on-disk at U-Boot boottime"
+ depends on EFI_CAPSULE_ON_DISK
+ default n
+ select EFI_SETUP_EARLY
+ help
+ Normally, without this option enabled, capsules will be
+ executed only at the first time of invoking one of efi command.
+ If this option is enabled, capsules will be enforced to be
+ executed as part of U-Boot initialisation so that they will
+ surely take place whatever is set to distro_bootcmd.
+
+config EFI_CAPSULE_FIRMWARE
+ bool
+ default n
+
+config EFI_CAPSULE_FIRMWARE_MANAGEMENT
+ bool "Capsule: Firmware Management Protocol"
+ depends on EFI_HAVE_CAPSULE_SUPPORT
+ default y
+ help
+ Select this option if you want to enable capsule-based
+ firmware update using Firmware Management Protocol.
+
+config EFI_CAPSULE_FIRMWARE_FIT
+ bool "FMP driver for FIT images"
+ depends on FIT
+ depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
+ select UPDATE_FIT
+ select DFU
+ select EFI_CAPSULE_FIRMWARE
+ help
+ Select this option if you want to enable firmware management protocol
+ driver for FIT image
+
+config EFI_CAPSULE_FIRMWARE_RAW
+ bool "FMP driver for raw images"
+ depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
+ depends on SANDBOX || (!SANDBOX && !EFI_CAPSULE_FIRMWARE_FIT)
+ select DFU_WRITE_ALT
+ select DFU
+ select EFI_CAPSULE_FIRMWARE
+ help
+ Select this option if you want to enable firmware management protocol
+ driver for raw image
+
+config EFI_CAPSULE_AUTHENTICATE
+ bool "Update Capsule authentication"
+ depends on EFI_CAPSULE_FIRMWARE
+ depends on EFI_CAPSULE_ON_DISK
+ depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
+ select HASH
+ select SHA256
+ select RSA
+ select RSA_VERIFY
+ select RSA_VERIFY_WITH_PKEY
+ select X509_CERTIFICATE_PARSER
+ select PKCS7_MESSAGE_PARSER
+ select PKCS7_VERIFY
+ select IMAGE_SIGN_INFO
+ select EFI_SIGNATURE_SUPPORT
+ default n
+ help
+ Select this option if you want to enable capsule
+ authentication
+
+config EFI_DEVICE_PATH_TO_TEXT
+ bool "Device path to text protocol"
+ default y
+ help
+ The device path to text protocol converts device nodes and paths to
+ human readable strings.
+
+config EFI_DEVICE_PATH_UTIL
+ bool "Device path utilities protocol"
+ default y
+ help
+ The device path utilities protocol creates and manipulates device
+ paths and device nodes. It is required to run the EFI Shell.
+
+config EFI_DT_FIXUP
+ bool "Device tree fixup protocol"
+ depends on !GENERATE_ACPI_TABLE
+ default y
+ help
+ The EFI device-tree fix-up protocol provides a function to let the
+ firmware apply fix-ups. This may be used by boot loaders.
+
+config EFI_LOADER_HII
+ bool "HII protocols"
+ default y
+ help
+ The Human Interface Infrastructure is a complicated framework that
+ allows UEFI applications to draw fancy menus and hook strings using
+ a translation framework.
+
+ U-Boot implements enough of its features to be able to run the UEFI
+ Shell, but not more than that.
+
+config EFI_UNICODE_COLLATION_PROTOCOL2
+ bool "Unicode collation protocol"
+ default y
+ help
+ The Unicode collation protocol is used for lexical comparisons. It is
+ required to run the UEFI shell.
+
+if EFI_UNICODE_COLLATION_PROTOCOL2
+
+config EFI_UNICODE_CAPITALIZATION
+ bool "Support Unicode capitalization"
+ default y
+ help
+ Select this option to enable correct handling of the capitalization of
+ Unicode codepoints in the range 0x0000-0xffff. If this option is not
+ set, only the the correct handling of the letters of the codepage
+ used by the FAT file system is ensured.
+
+endif
+
+config EFI_LOADER_BOUNCE_BUFFER
+ bool "EFI Applications use bounce buffers for DMA operations"
+ depends on ARM64
+ default n
+ help
+ Some hardware does not support DMA to full 64bit addresses. For this
+ hardware we can create a bounce buffer so that payloads don't have to
+ worry about platform details.
+
+config EFI_PLATFORM_LANG_CODES
+ string "Language codes supported by firmware"
+ default "en-US"
+ help
+ This value is used to initialize the PlatformLangCodes variable. Its
+ value is a semicolon (;) separated list of language codes in native
+ RFC 4646 format, e.g. "en-US;de-DE". The first language code is used
+ to initialize the PlatformLang variable.
+
+config EFI_HAVE_RUNTIME_RESET
+ # bool "Reset runtime service is available"
+ bool
+ default y
+ depends on ARCH_BCM283X || FSL_LAYERSCAPE || PSCI_RESET || \
+ SANDBOX || SYSRESET_X86
+
+config EFI_GRUB_ARM32_WORKAROUND
+ bool "Workaround for GRUB on 32bit ARM"
+ default n if ARCH_BCM283X || ARCH_SUNXI || ARCH_QEMU
+ default y
+ depends on ARM && !ARM64
+ help
+ GRUB prior to version 2.04 requires U-Boot to disable caches. This
+ workaround currently is also needed on systems with caches that
+ cannot be managed via CP15.
+
+config EFI_RNG_PROTOCOL
+ bool "EFI_RNG_PROTOCOL support"
+ depends on DM_RNG
+ default y
+ help
+ Provide a EFI_RNG_PROTOCOL implementation using the hardware random
+ number generator of the platform.
+
+config EFI_TCG2_PROTOCOL
+ bool "EFI_TCG2_PROTOCOL support"
+ default y
+ depends on TPM_V2
+ select SHA1
+ select SHA256
+ select SHA512_ALGO
+ select SHA384
+ select SHA512
+ select HASH
+ help
+ Provide a EFI_TCG2_PROTOCOL implementation using the TPM hardware
+ of the platform.
+
+config EFI_TCG2_PROTOCOL_EVENTLOG_SIZE
+ int "EFI_TCG2_PROTOCOL EventLog size"
+ depends on EFI_TCG2_PROTOCOL
+ default 4096
+ help
+ Define the size of the EventLog for EFI_TCG2_PROTOCOL. Note that
+ this is going to be allocated twice. One for the eventlog it self
+ and one for the configuration table that is required from the spec
+
+config EFI_LOAD_FILE2_INITRD
+ bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
+ default y
+ help
+ Linux v5.7 and later can make use of this option. If the boot option
+ selected by the UEFI boot manager specifies an existing file to be used
+ as initial RAM disk, a Linux specific Load File2 protocol will be
+ installed and Linux 5.7+ will ignore any initrd=<ramdisk> command line
+ argument.
+
+config EFI_SECURE_BOOT
+ bool "Enable EFI secure boot support"
+ depends on EFI_LOADER
+ select HASH
+ select SHA256
+ select RSA
+ select RSA_VERIFY_WITH_PKEY
+ select IMAGE_SIGN_INFO
+ select ASYMMETRIC_KEY_TYPE
+ select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select X509_CERTIFICATE_PARSER
+ select PKCS7_MESSAGE_PARSER
+ select PKCS7_VERIFY
+ select EFI_SIGNATURE_SUPPORT
+ default n
+ help
+ Select this option to enable EFI secure boot support.
+ Once SecureBoot mode is enforced, any EFI binary can run only if
+ it is signed with a trusted key. To do that, you need to install,
+ at least, PK, KEK and db.
+
+config EFI_SIGNATURE_SUPPORT
+ bool
+
+config EFI_ESRT
+ bool "Enable the UEFI ESRT generation"
+ depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
+ default y
+ help
+ Enabling this option creates the ESRT UEFI system table.
+
+endif
diff --git a/roms/u-boot/lib/efi_loader/Makefile b/roms/u-boot/lib/efi_loader/Makefile
new file mode 100644
index 000000000..fd344cea2
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/Makefile
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2016 Alexander Graf
+#
+
+# This file only gets included with CONFIG_EFI_LOADER set, so all
+# object inclusion implicitly depends on it
+
+asflags-y += -DHOST_ARCH="$(HOST_ARCH)" -I.
+ccflags-y += -DHOST_ARCH="$(HOST_ARCH)"
+
+CFLAGS_efi_boottime.o += \
+ -DFW_VERSION="0x$(VERSION)" \
+ -DFW_PATCHLEVEL="0x$(PATCHLEVEL)"
+CFLAGS_helloworld.o := $(CFLAGS_EFI) -Os -ffreestanding
+CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI)
+
+ifneq ($(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),)
+always += helloworld.efi
+targets += helloworld.o
+endif
+
+obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
+obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += efi_bootmgr.o
+obj-y += efi_boottime.o
+obj-y += efi_helper.o
+obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
+obj-$(CONFIG_EFI_CAPSULE_FIRMWARE) += efi_firmware.o
+obj-y += efi_console.o
+obj-y += efi_device_path.o
+obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
+obj-$(CONFIG_EFI_DEVICE_PATH_UTIL) += efi_device_path_utilities.o
+ifeq ($(CONFIG_GENERATE_ACPI_TABLE),)
+obj-y += efi_dt_fixup.o
+endif
+obj-y += efi_file.o
+obj-$(CONFIG_EFI_LOADER_HII) += efi_hii.o
+obj-y += efi_image_loader.o
+obj-y += efi_load_options.o
+obj-y += efi_memory.o
+obj-y += efi_root_node.o
+obj-y += efi_runtime.o
+obj-y += efi_setup.o
+obj-y += efi_string.o
+obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += efi_unicode_collation.o
+obj-y += efi_var_common.o
+obj-y += efi_var_mem.o
+obj-y += efi_var_file.o
+ifeq ($(CONFIG_EFI_MM_COMM_TEE),y)
+obj-y += efi_variable_tee.o
+else
+obj-y += efi_variable.o
+obj-$(CONFIG_EFI_VARIABLES_PRESEED) += efi_var_seed.o
+endif
+obj-y += efi_watchdog.o
+obj-$(CONFIG_EFI_ESRT) += efi_esrt.o
+obj-$(CONFIG_LCD) += efi_gop.o
+obj-$(CONFIG_DM_VIDEO) += efi_gop.o
+obj-$(CONFIG_PARTITIONS) += efi_disk.o
+obj-$(CONFIG_NET) += efi_net.o
+obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
+obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
+obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o
+obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_tcg2.o
+obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o
+obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o
+
+EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
+$(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
diff --git a/roms/u-boot/lib/efi_loader/efi_acpi.c b/roms/u-boot/lib/efi_loader/efi_acpi.c
new file mode 100644
index 000000000..a62c34009
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_acpi.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application ACPI tables support
+ *
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <acpi/acpi_table.h>
+
+static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID;
+
+/*
+ * Install the ACPI table as a configuration table.
+ *
+ * @return status code
+ */
+efi_status_t efi_acpi_register(void)
+{
+ /* Map within the low 32 bits, to allow for 32bit ACPI tables */
+ u64 acpi = U32_MAX;
+ efi_status_t ret;
+
+ /* Reserve 64kiB page for ACPI */
+ ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
+ EFI_ACPI_RECLAIM_MEMORY, 16, &acpi);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /*
+ * Generate ACPI tables - we know that efi_allocate_pages() returns
+ * a 4k-aligned address, so it is safe to assume that
+ * write_acpi_tables() will write the table at that address.
+ */
+ write_acpi_tables(acpi);
+
+ /* And expose them to our EFI payload */
+ return efi_install_configuration_table(&acpi_guid,
+ (void *)(uintptr_t)acpi);
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_bootmgr.c b/roms/u-boot/lib/efi_loader/efi_bootmgr.c
new file mode 100644
index 000000000..1fe19237f
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_bootmgr.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI boot manager
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <charset.h>
+#include <log.h>
+#include <malloc.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <asm/unaligned.h>
+
+static const struct efi_boot_services *bs;
+static const struct efi_runtime_services *rs;
+
+/*
+ * bootmgr implements the logic of trying to find a payload to boot
+ * based on the BootOrder + BootXXXX variables, and then loading it.
+ *
+ * TODO detecting a special key held (f9?) and displaying a boot menu
+ * like you would get on a PC would be clever.
+ *
+ * TODO if we had a way to write and persist variables after the OS
+ * has started, we'd also want to check OsIndications to see if we
+ * should do normal or recovery boot.
+ */
+
+/**
+ * try_load_entry() - try to load image for boot option
+ *
+ * Attempt to load load-option number 'n', returning device_path and file_path
+ * if successful. This checks that the EFI_LOAD_OPTION is active (enabled)
+ * and that the specified file to boot exists.
+ *
+ * @n: number of the boot option, e.g. 0x0a13 for Boot0A13
+ * @handle: on return handle for the newly installed image
+ * @load_options: load options set on the loaded image protocol
+ * Return: status code
+ */
+static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
+ void **load_options)
+{
+ struct efi_load_option lo;
+ u16 varname[] = L"Boot0000";
+ u16 hexmap[] = L"0123456789ABCDEF";
+ void *load_option;
+ efi_uintn_t size;
+ efi_status_t ret;
+
+ varname[4] = hexmap[(n & 0xf000) >> 12];
+ varname[5] = hexmap[(n & 0x0f00) >> 8];
+ varname[6] = hexmap[(n & 0x00f0) >> 4];
+ varname[7] = hexmap[(n & 0x000f) >> 0];
+
+ load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
+ if (!load_option)
+ return EFI_LOAD_ERROR;
+
+ ret = efi_deserialize_load_option(&lo, load_option, &size);
+ if (ret != EFI_SUCCESS) {
+ log_warning("Invalid load option for %ls\n", varname);
+ goto error;
+ }
+
+ if (lo.attributes & LOAD_OPTION_ACTIVE) {
+ u32 attributes;
+
+ log_debug("%s: trying to load \"%ls\" from %pD\n",
+ __func__, lo.label, lo.file_path);
+
+ ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
+ NULL, 0, handle));
+ if (ret != EFI_SUCCESS) {
+ log_warning("Loading %ls '%ls' failed\n",
+ varname, lo.label);
+ goto error;
+ }
+
+ attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+ ret = efi_set_variable_int(L"BootCurrent",
+ &efi_global_variable_guid,
+ attributes, sizeof(n), &n, false);
+ if (ret != EFI_SUCCESS)
+ goto unload;
+ /* try to register load file2 for initrd's */
+ if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
+ ret = efi_initrd_register();
+ if (ret != EFI_SUCCESS)
+ goto unload;
+ }
+
+ log_info("Booting: %ls\n", lo.label);
+ } else {
+ ret = EFI_LOAD_ERROR;
+ }
+
+ /* Set load options */
+ if (size) {
+ *load_options = malloc(size);
+ if (!*load_options) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+ memcpy(*load_options, lo.optional_data, size);
+ ret = efi_set_load_options(*handle, size, *load_options);
+ } else {
+ *load_options = NULL;
+ }
+
+error:
+ free(load_option);
+
+ return ret;
+
+unload:
+ if (EFI_CALL(efi_unload_image(*handle)) != EFI_SUCCESS)
+ log_err("Unloading image failed\n");
+ free(load_option);
+
+ return ret;
+}
+
+/**
+ * efi_bootmgr_load() - try to load from BootNext or BootOrder
+ *
+ * Attempt to load from BootNext or in the order specified by BootOrder
+ * EFI variable, the available load-options, finding and returning
+ * the first one that can be loaded successfully.
+ *
+ * @handle: on return handle for the newly installed image
+ * @load_options: load options set on the loaded image protocol
+ * Return: status code
+ */
+efi_status_t efi_bootmgr_load(efi_handle_t *handle, void **load_options)
+{
+ u16 bootnext, *bootorder;
+ efi_uintn_t size;
+ int i, num;
+ efi_status_t ret;
+
+ bs = systab.boottime;
+ rs = systab.runtime;
+
+ /* BootNext */
+ size = sizeof(bootnext);
+ ret = efi_get_variable_int(L"BootNext",
+ &efi_global_variable_guid,
+ NULL, &size, &bootnext, NULL);
+ if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+ /* BootNext does exist here */
+ if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16))
+ log_err("BootNext must be 16-bit integer\n");
+
+ /* delete BootNext */
+ ret = efi_set_variable_int(L"BootNext",
+ &efi_global_variable_guid,
+ 0, 0, NULL, false);
+
+ /* load BootNext */
+ if (ret == EFI_SUCCESS) {
+ if (size == sizeof(u16)) {
+ ret = try_load_entry(bootnext, handle,
+ load_options);
+ if (ret == EFI_SUCCESS)
+ return ret;
+ log_warning(
+ "Loading from BootNext failed, falling back to BootOrder\n");
+ }
+ } else {
+ log_err("Deleting BootNext failed\n");
+ }
+ }
+
+ /* BootOrder */
+ bootorder = efi_get_var(L"BootOrder", &efi_global_variable_guid, &size);
+ if (!bootorder) {
+ log_info("BootOrder not defined\n");
+ ret = EFI_NOT_FOUND;
+ goto error;
+ }
+
+ num = size / sizeof(uint16_t);
+ for (i = 0; i < num; i++) {
+ log_debug("%s trying to load Boot%04X\n", __func__,
+ bootorder[i]);
+ ret = try_load_entry(bootorder[i], handle, load_options);
+ if (ret == EFI_SUCCESS)
+ break;
+ }
+
+ free(bootorder);
+
+error:
+ return ret;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_boottime.c b/roms/u-boot/lib/efi_loader/efi_boottime.c
new file mode 100644
index 000000000..f6d5ba05e
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_boottime.c
@@ -0,0 +1,3783 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application boot time services
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <bootm.h>
+#include <div64.h>
+#include <dm/device.h>
+#include <dm/root.h>
+#include <efi_loader.h>
+#include <irq_func.h>
+#include <log.h>
+#include <malloc.h>
+#include <pe.h>
+#include <time.h>
+#include <u-boot/crc.h>
+#include <usb.h>
+#include <watchdog.h>
+#include <asm/global_data.h>
+#include <linux/libfdt_env.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Task priority level */
+static efi_uintn_t efi_tpl = TPL_APPLICATION;
+
+/* This list contains all the EFI objects our payload has access to */
+LIST_HEAD(efi_obj_list);
+
+/* List of all events */
+__efi_runtime_data LIST_HEAD(efi_events);
+
+/* List of queued events */
+LIST_HEAD(efi_event_queue);
+
+/* Flag to disable timer activity in ExitBootServices() */
+static bool timers_enabled = true;
+
+/* Flag used by the selftest to avoid detaching devices in ExitBootServices() */
+bool efi_st_keep_devices;
+
+/* List of all events registered by RegisterProtocolNotify() */
+LIST_HEAD(efi_register_notify_events);
+
+/* Handle of the currently executing image */
+static efi_handle_t current_image;
+
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
+/*
+ * The "gd" pointer lives in a register on ARM and RISC-V that we declare
+ * fixed when compiling U-Boot. However, the payload does not know about that
+ * restriction so we need to manually swap its and our view of that register on
+ * EFI callback entry/exit.
+ */
+static volatile gd_t *efi_gd, *app_gd;
+#endif
+
+/* 1 if inside U-Boot code, 0 if inside EFI payload code */
+static int entry_count = 1;
+static int nesting_level;
+/* GUID of the device tree table */
+const efi_guid_t efi_guid_fdt = EFI_FDT_GUID;
+/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
+const efi_guid_t efi_guid_driver_binding_protocol =
+ EFI_DRIVER_BINDING_PROTOCOL_GUID;
+
+/* event group ExitBootServices() invoked */
+const efi_guid_t efi_guid_event_group_exit_boot_services =
+ EFI_EVENT_GROUP_EXIT_BOOT_SERVICES;
+/* event group SetVirtualAddressMap() invoked */
+const efi_guid_t efi_guid_event_group_virtual_address_change =
+ EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE;
+/* event group memory map changed */
+const efi_guid_t efi_guid_event_group_memory_map_change =
+ EFI_EVENT_GROUP_MEMORY_MAP_CHANGE;
+/* event group boot manager about to boot */
+const efi_guid_t efi_guid_event_group_ready_to_boot =
+ EFI_EVENT_GROUP_READY_TO_BOOT;
+/* event group ResetSystem() invoked (before ExitBootServices) */
+const efi_guid_t efi_guid_event_group_reset_system =
+ EFI_EVENT_GROUP_RESET_SYSTEM;
+/* GUIDs of the Load File and Load File2 protocols */
+const efi_guid_t efi_guid_load_file_protocol = EFI_LOAD_FILE_PROTOCOL_GUID;
+const efi_guid_t efi_guid_load_file2_protocol = EFI_LOAD_FILE2_PROTOCOL_GUID;
+
+static efi_status_t EFIAPI efi_disconnect_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t driver_image_handle,
+ efi_handle_t child_handle);
+
+/* Called on every callback entry */
+int __efi_entry_check(void)
+{
+ int ret = entry_count++ == 0;
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
+ assert(efi_gd);
+ app_gd = gd;
+ set_gd(efi_gd);
+#endif
+ return ret;
+}
+
+/* Called on every callback exit */
+int __efi_exit_check(void)
+{
+ int ret = --entry_count == 0;
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
+ set_gd(app_gd);
+#endif
+ return ret;
+}
+
+/**
+ * efi_save_gd() - save global data register
+ *
+ * On the ARM and RISC-V architectures gd is mapped to a fixed register.
+ * As this register may be overwritten by an EFI payload we save it here
+ * and restore it on every callback entered.
+ *
+ * This function is called after relocation from initr_reloc_global_data().
+ */
+void efi_save_gd(void)
+{
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
+ efi_gd = gd;
+#endif
+}
+
+/**
+ * efi_restore_gd() - restore global data register
+ *
+ * On the ARM and RISC-V architectures gd is mapped to a fixed register.
+ * Restore it after returning from the UEFI world to the value saved via
+ * efi_save_gd().
+ */
+void efi_restore_gd(void)
+{
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
+ /* Only restore if we're already in EFI context */
+ if (!efi_gd)
+ return;
+ set_gd(efi_gd);
+#endif
+}
+
+/**
+ * indent_string() - returns a string for indenting with two spaces per level
+ * @level: indent level
+ *
+ * A maximum of ten indent levels is supported. Higher indent levels will be
+ * truncated.
+ *
+ * Return: A string for indenting with two spaces per level is
+ * returned.
+ */
+static const char *indent_string(int level)
+{
+ const char *indent = " ";
+ const int max = strlen(indent);
+
+ level = min(max, level * 2);
+ return &indent[max - level];
+}
+
+const char *__efi_nesting(void)
+{
+ return indent_string(nesting_level);
+}
+
+const char *__efi_nesting_inc(void)
+{
+ return indent_string(nesting_level++);
+}
+
+const char *__efi_nesting_dec(void)
+{
+ return indent_string(--nesting_level);
+}
+
+/**
+ * efi_event_is_queued() - check if an event is queued
+ *
+ * @event: event
+ * Return: true if event is queued
+ */
+static bool efi_event_is_queued(struct efi_event *event)
+{
+ return !!event->queue_link.next;
+}
+
+/**
+ * efi_process_event_queue() - process event queue
+ */
+static void efi_process_event_queue(void)
+{
+ while (!list_empty(&efi_event_queue)) {
+ struct efi_event *event;
+ efi_uintn_t old_tpl;
+
+ event = list_first_entry(&efi_event_queue, struct efi_event,
+ queue_link);
+ if (efi_tpl >= event->notify_tpl)
+ return;
+ list_del(&event->queue_link);
+ event->queue_link.next = NULL;
+ event->queue_link.prev = NULL;
+ /* Events must be executed at the event's TPL */
+ old_tpl = efi_tpl;
+ efi_tpl = event->notify_tpl;
+ EFI_CALL_VOID(event->notify_function(event,
+ event->notify_context));
+ efi_tpl = old_tpl;
+ if (event->type == EVT_NOTIFY_SIGNAL)
+ event->is_signaled = 0;
+ }
+}
+
+/**
+ * efi_queue_event() - queue an EFI event
+ * @event: event to signal
+ *
+ * This function queues the notification function of the event for future
+ * execution.
+ *
+ */
+static void efi_queue_event(struct efi_event *event)
+{
+ struct efi_event *item;
+
+ if (!event->notify_function)
+ return;
+
+ if (!efi_event_is_queued(event)) {
+ /*
+ * Events must be notified in order of decreasing task priority
+ * level. Insert the new event accordingly.
+ */
+ list_for_each_entry(item, &efi_event_queue, queue_link) {
+ if (item->notify_tpl < event->notify_tpl) {
+ list_add_tail(&event->queue_link,
+ &item->queue_link);
+ event = NULL;
+ break;
+ }
+ }
+ if (event)
+ list_add_tail(&event->queue_link, &efi_event_queue);
+ efi_process_event_queue();
+ }
+}
+
+/**
+ * is_valid_tpl() - check if the task priority level is valid
+ *
+ * @tpl: TPL level to check
+ * Return: status code
+ */
+efi_status_t is_valid_tpl(efi_uintn_t tpl)
+{
+ switch (tpl) {
+ case TPL_APPLICATION:
+ case TPL_CALLBACK:
+ case TPL_NOTIFY:
+ return EFI_SUCCESS;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+}
+
+/**
+ * efi_signal_event() - signal an EFI event
+ * @event: event to signal
+ *
+ * This function signals an event. If the event belongs to an event group, all
+ * events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL,
+ * their notification function is queued.
+ *
+ * For the SignalEvent service see efi_signal_event_ext.
+ */
+void efi_signal_event(struct efi_event *event)
+{
+ if (event->is_signaled)
+ return;
+ if (event->group) {
+ struct efi_event *evt;
+
+ /*
+ * The signaled state has to set before executing any
+ * notification function
+ */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!evt->group || guidcmp(evt->group, event->group))
+ continue;
+ if (evt->is_signaled)
+ continue;
+ evt->is_signaled = true;
+ }
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!evt->group || guidcmp(evt->group, event->group))
+ continue;
+ efi_queue_event(evt);
+ }
+ } else {
+ event->is_signaled = true;
+ efi_queue_event(event);
+ }
+}
+
+/**
+ * efi_raise_tpl() - raise the task priority level
+ * @new_tpl: new value of the task priority level
+ *
+ * This function implements the RaiseTpl service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: old value of the task priority level
+ */
+static unsigned long EFIAPI efi_raise_tpl(efi_uintn_t new_tpl)
+{
+ efi_uintn_t old_tpl = efi_tpl;
+
+ EFI_ENTRY("0x%zx", new_tpl);
+
+ if (new_tpl < efi_tpl)
+ EFI_PRINT("WARNING: new_tpl < current_tpl in %s\n", __func__);
+ efi_tpl = new_tpl;
+ if (efi_tpl > TPL_HIGH_LEVEL)
+ efi_tpl = TPL_HIGH_LEVEL;
+
+ EFI_EXIT(EFI_SUCCESS);
+ return old_tpl;
+}
+
+/**
+ * efi_restore_tpl() - lower the task priority level
+ * @old_tpl: value of the task priority level to be restored
+ *
+ * This function implements the RestoreTpl service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static void EFIAPI efi_restore_tpl(efi_uintn_t old_tpl)
+{
+ EFI_ENTRY("0x%zx", old_tpl);
+
+ if (old_tpl > efi_tpl)
+ EFI_PRINT("WARNING: old_tpl > current_tpl in %s\n", __func__);
+ efi_tpl = old_tpl;
+ if (efi_tpl > TPL_HIGH_LEVEL)
+ efi_tpl = TPL_HIGH_LEVEL;
+
+ /*
+ * Lowering the TPL may have made queued events eligible for execution.
+ */
+ efi_timer_check();
+
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_allocate_pages_ext() - allocate memory pages
+ * @type: type of allocation to be performed
+ * @memory_type: usage type of the allocated memory
+ * @pages: number of pages to be allocated
+ * @memory: allocated memory
+ *
+ * This function implements the AllocatePages service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
+ efi_uintn_t pages,
+ uint64_t *memory)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%d, %d, 0x%zx, %p", type, memory_type, pages, memory);
+ r = efi_allocate_pages(type, memory_type, pages, memory);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_free_pages_ext() - Free memory pages.
+ * @memory: start of the memory area to be freed
+ * @pages: number of pages to be freed
+ *
+ * This function implements the FreePages service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory,
+ efi_uintn_t pages)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%llx, 0x%zx", memory, pages);
+ r = efi_free_pages(memory, pages);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_get_memory_map_ext() - get map describing memory usage
+ * @memory_map_size: on entry the size, in bytes, of the memory map buffer,
+ * on exit the size of the copied memory map
+ * @memory_map: buffer to which the memory map is written
+ * @map_key: key for the memory map
+ * @descriptor_size: size of an individual memory descriptor
+ * @descriptor_version: version number of the memory descriptor structure
+ *
+ * This function implements the GetMemoryMap service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_get_memory_map_ext(
+ efi_uintn_t *memory_map_size,
+ struct efi_mem_desc *memory_map,
+ efi_uintn_t *map_key,
+ efi_uintn_t *descriptor_size,
+ uint32_t *descriptor_version)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map,
+ map_key, descriptor_size, descriptor_version);
+ r = efi_get_memory_map(memory_map_size, memory_map, map_key,
+ descriptor_size, descriptor_version);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_allocate_pool_ext() - allocate memory from pool
+ * @pool_type: type of the pool from which memory is to be allocated
+ * @size: number of bytes to be allocated
+ * @buffer: allocated memory
+ *
+ * This function implements the AllocatePool service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_allocate_pool_ext(int pool_type,
+ efi_uintn_t size,
+ void **buffer)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%d, %zd, %p", pool_type, size, buffer);
+ r = efi_allocate_pool(pool_type, size, buffer);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_free_pool_ext() - free memory from pool
+ * @buffer: start of memory to be freed
+ *
+ * This function implements the FreePool service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_free_pool_ext(void *buffer)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%p", buffer);
+ r = efi_free_pool(buffer);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_add_handle() - add a new handle to the object list
+ *
+ * @handle: handle to be added
+ *
+ * The protocols list is initialized. The handle is added to the list of known
+ * UEFI objects.
+ */
+void efi_add_handle(efi_handle_t handle)
+{
+ if (!handle)
+ return;
+ INIT_LIST_HEAD(&handle->protocols);
+ list_add_tail(&handle->link, &efi_obj_list);
+}
+
+/**
+ * efi_create_handle() - create handle
+ * @handle: new handle
+ *
+ * Return: status code
+ */
+efi_status_t efi_create_handle(efi_handle_t *handle)
+{
+ struct efi_object *obj;
+
+ obj = calloc(1, sizeof(struct efi_object));
+ if (!obj)
+ return EFI_OUT_OF_RESOURCES;
+
+ efi_add_handle(obj);
+ *handle = obj;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_search_protocol() - find a protocol on a handle.
+ * @handle: handle
+ * @protocol_guid: GUID of the protocol
+ * @handler: reference to the protocol
+ *
+ * Return: status code
+ */
+efi_status_t efi_search_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol_guid,
+ struct efi_handler **handler)
+{
+ struct efi_object *efiobj;
+ struct list_head *lhandle;
+
+ if (!handle || !protocol_guid)
+ return EFI_INVALID_PARAMETER;
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_INVALID_PARAMETER;
+ list_for_each(lhandle, &efiobj->protocols) {
+ struct efi_handler *protocol;
+
+ protocol = list_entry(lhandle, struct efi_handler, link);
+ if (!guidcmp(protocol->guid, protocol_guid)) {
+ if (handler)
+ *handler = protocol;
+ return EFI_SUCCESS;
+ }
+ }
+ return EFI_NOT_FOUND;
+}
+
+/**
+ * efi_remove_protocol() - delete protocol from a handle
+ * @handle: handle from which the protocol shall be deleted
+ * @protocol: GUID of the protocol to be deleted
+ * @protocol_interface: interface of the protocol implementation
+ *
+ * Return: status code
+ */
+efi_status_t efi_remove_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ struct efi_handler *handler;
+ efi_status_t ret;
+
+ ret = efi_search_protocol(handle, protocol, &handler);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (handler->protocol_interface != protocol_interface)
+ return EFI_NOT_FOUND;
+ list_del(&handler->link);
+ free(handler);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_remove_all_protocols() - delete all protocols from a handle
+ * @handle: handle from which the protocols shall be deleted
+ *
+ * Return: status code
+ */
+efi_status_t efi_remove_all_protocols(const efi_handle_t handle)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *protocol;
+ struct efi_handler *pos;
+
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_INVALID_PARAMETER;
+ list_for_each_entry_safe(protocol, pos, &efiobj->protocols, link) {
+ efi_status_t ret;
+
+ ret = efi_remove_protocol(handle, protocol->guid,
+ protocol->protocol_interface);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_delete_handle() - delete handle
+ *
+ * @handle: handle to delete
+ */
+void efi_delete_handle(efi_handle_t handle)
+{
+ if (!handle)
+ return;
+ efi_remove_all_protocols(handle);
+ list_del(&handle->link);
+ free(handle);
+}
+
+/**
+ * efi_is_event() - check if a pointer is a valid event
+ * @event: pointer to check
+ *
+ * Return: status code
+ */
+static efi_status_t efi_is_event(const struct efi_event *event)
+{
+ const struct efi_event *evt;
+
+ if (!event)
+ return EFI_INVALID_PARAMETER;
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt == event)
+ return EFI_SUCCESS;
+ }
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ * efi_create_event() - create an event
+ *
+ * @type: type of the event to create
+ * @notify_tpl: task priority level of the event
+ * @notify_function: notification function of the event
+ * @notify_context: pointer passed to the notification function
+ * @group: event group
+ * @event: created event
+ *
+ * This function is used inside U-Boot code to create an event.
+ *
+ * For the API function implementing the CreateEvent service see
+ * efi_create_event_ext.
+ *
+ * Return: status code
+ */
+efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context, efi_guid_t *group,
+ struct efi_event **event)
+{
+ struct efi_event *evt;
+ efi_status_t ret;
+ int pool_type;
+
+ if (event == NULL)
+ return EFI_INVALID_PARAMETER;
+
+ switch (type) {
+ case 0:
+ case EVT_TIMER:
+ case EVT_NOTIFY_SIGNAL:
+ case EVT_TIMER | EVT_NOTIFY_SIGNAL:
+ case EVT_NOTIFY_WAIT:
+ case EVT_TIMER | EVT_NOTIFY_WAIT:
+ case EVT_SIGNAL_EXIT_BOOT_SERVICES:
+ pool_type = EFI_BOOT_SERVICES_DATA;
+ break;
+ case EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:
+ pool_type = EFI_RUNTIME_SERVICES_DATA;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /*
+ * The UEFI specification requires event notification levels to be
+ * > TPL_APPLICATION and <= TPL_HIGH_LEVEL.
+ *
+ * Parameter NotifyTpl should not be checked if it is not used.
+ */
+ if ((type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) &&
+ (!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS ||
+ notify_tpl == TPL_APPLICATION))
+ return EFI_INVALID_PARAMETER;
+
+ ret = efi_allocate_pool(pool_type, sizeof(struct efi_event),
+ (void **)&evt);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ memset(evt, 0, sizeof(struct efi_event));
+ evt->type = type;
+ evt->notify_tpl = notify_tpl;
+ evt->notify_function = notify_function;
+ evt->notify_context = notify_context;
+ evt->group = group;
+ /* Disable timers on boot up */
+ evt->trigger_next = -1ULL;
+ list_add_tail(&evt->link, &efi_events);
+ *event = evt;
+ return EFI_SUCCESS;
+}
+
+/*
+ * efi_create_event_ex() - create an event in a group
+ * @type: type of the event to create
+ * @notify_tpl: task priority level of the event
+ * @notify_function: notification function of the event
+ * @notify_context: pointer passed to the notification function
+ * @event: created event
+ * @event_group: event group
+ *
+ * This function implements the CreateEventEx service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context,
+ efi_guid_t *event_group,
+ struct efi_event **event)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function,
+ notify_context, event_group);
+
+ /*
+ * The allowable input parameters are the same as in CreateEvent()
+ * except for the following two disallowed event types.
+ */
+ switch (type) {
+ case EVT_SIGNAL_EXIT_BOOT_SERVICES:
+ case EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_create_event(type, notify_tpl, notify_function,
+ notify_context, event_group, event);
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_create_event_ext() - create an event
+ * @type: type of the event to create
+ * @notify_tpl: task priority level of the event
+ * @notify_function: notification function of the event
+ * @notify_context: pointer passed to the notification function
+ * @event: created event
+ *
+ * This function implements the CreateEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_create_event_ext(
+ uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context, struct efi_event **event)
+{
+ EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function,
+ notify_context);
+ return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
+ notify_context, NULL, event));
+}
+
+/**
+ * efi_timer_check() - check if a timer event has occurred
+ *
+ * Check if a timer event has occurred or a queued notification function should
+ * be called.
+ *
+ * Our timers have to work without interrupts, so we check whenever keyboard
+ * input or disk accesses happen if enough time elapsed for them to fire.
+ */
+void efi_timer_check(void)
+{
+ struct efi_event *evt;
+ u64 now = timer_get_us();
+
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!timers_enabled)
+ continue;
+ if (!(evt->type & EVT_TIMER) || now < evt->trigger_next)
+ continue;
+ switch (evt->trigger_type) {
+ case EFI_TIMER_RELATIVE:
+ evt->trigger_type = EFI_TIMER_STOP;
+ break;
+ case EFI_TIMER_PERIODIC:
+ evt->trigger_next += evt->trigger_time;
+ break;
+ default:
+ continue;
+ }
+ evt->is_signaled = false;
+ efi_signal_event(evt);
+ }
+ efi_process_event_queue();
+ WATCHDOG_RESET();
+}
+
+/**
+ * efi_set_timer() - set the trigger time for a timer event or stop the event
+ * @event: event for which the timer is set
+ * @type: type of the timer
+ * @trigger_time: trigger period in multiples of 100 ns
+ *
+ * This is the function for internal usage in U-Boot. For the API function
+ * implementing the SetTimer service see efi_set_timer_ext.
+ *
+ * Return: status code
+ */
+efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
+ uint64_t trigger_time)
+{
+ /* Check that the event is valid */
+ if (efi_is_event(event) != EFI_SUCCESS || !(event->type & EVT_TIMER))
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * The parameter defines a multiple of 100 ns.
+ * We use multiples of 1000 ns. So divide by 10.
+ */
+ do_div(trigger_time, 10);
+
+ switch (type) {
+ case EFI_TIMER_STOP:
+ event->trigger_next = -1ULL;
+ break;
+ case EFI_TIMER_PERIODIC:
+ case EFI_TIMER_RELATIVE:
+ event->trigger_next = timer_get_us() + trigger_time;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+ event->trigger_type = type;
+ event->trigger_time = trigger_time;
+ event->is_signaled = false;
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_set_timer_ext() - Set the trigger time for a timer event or stop the
+ * event
+ * @event: event for which the timer is set
+ * @type: type of the timer
+ * @trigger_time: trigger period in multiples of 100 ns
+ *
+ * This function implements the SetTimer service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event,
+ enum efi_timer_delay type,
+ uint64_t trigger_time)
+{
+ EFI_ENTRY("%p, %d, %llx", event, type, trigger_time);
+ return EFI_EXIT(efi_set_timer(event, type, trigger_time));
+}
+
+/**
+ * efi_wait_for_event() - wait for events to be signaled
+ * @num_events: number of events to be waited for
+ * @event: events to be waited for
+ * @index: index of the event that was signaled
+ *
+ * This function implements the WaitForEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events,
+ struct efi_event **event,
+ efi_uintn_t *index)
+{
+ int i;
+
+ EFI_ENTRY("%zd, %p, %p", num_events, event, index);
+
+ /* Check parameters */
+ if (!num_events || !event)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ /* Check TPL */
+ if (efi_tpl != TPL_APPLICATION)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+ for (i = 0; i < num_events; ++i) {
+ if (efi_is_event(event[i]) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event[i]->is_signaled)
+ efi_queue_event(event[i]);
+ }
+
+ /* Wait for signal */
+ for (;;) {
+ for (i = 0; i < num_events; ++i) {
+ if (event[i]->is_signaled)
+ goto out;
+ }
+ /* Allow events to occur. */
+ efi_timer_check();
+ }
+
+out:
+ /*
+ * Reset the signal which is passed to the caller to allow periodic
+ * events to occur.
+ */
+ event[i]->is_signaled = false;
+ if (index)
+ *index = i;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_signal_event_ext() - signal an EFI event
+ * @event: event to signal
+ *
+ * This function implements the SignalEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * This functions sets the signaled state of the event and queues the
+ * notification function for execution.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
+{
+ EFI_ENTRY("%p", event);
+ if (efi_is_event(event) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ efi_signal_event(event);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_close_event() - close an EFI event
+ * @event: event to close
+ *
+ * This function implements the CloseEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
+{
+ struct efi_register_notify_event *item, *next;
+
+ EFI_ENTRY("%p", event);
+ if (efi_is_event(event) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* Remove protocol notify registrations for the event */
+ list_for_each_entry_safe(item, next, &efi_register_notify_events,
+ link) {
+ if (event == item->event) {
+ struct efi_protocol_notification *hitem, *hnext;
+
+ /* Remove signaled handles */
+ list_for_each_entry_safe(hitem, hnext, &item->handles,
+ link) {
+ list_del(&hitem->link);
+ free(hitem);
+ }
+ list_del(&item->link);
+ free(item);
+ }
+ }
+ /* Remove event from queue */
+ if (efi_event_is_queued(event))
+ list_del(&event->queue_link);
+
+ list_del(&event->link);
+ efi_free_pool(event);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_check_event() - check if an event is signaled
+ * @event: event to check
+ *
+ * This function implements the CheckEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * If an event is not signaled yet, the notification function is queued. The
+ * signaled state is cleared.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
+{
+ EFI_ENTRY("%p", event);
+ efi_timer_check();
+ if (efi_is_event(event) != EFI_SUCCESS ||
+ event->type & EVT_NOTIFY_SIGNAL)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event->is_signaled)
+ efi_queue_event(event);
+ if (event->is_signaled) {
+ event->is_signaled = false;
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ return EFI_EXIT(EFI_NOT_READY);
+}
+
+/**
+ * efi_search_obj() - find the internal EFI object for a handle
+ * @handle: handle to find
+ *
+ * Return: EFI object
+ */
+struct efi_object *efi_search_obj(const efi_handle_t handle)
+{
+ struct efi_object *efiobj;
+
+ if (!handle)
+ return NULL;
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ if (efiobj == handle)
+ return efiobj;
+ }
+ return NULL;
+}
+
+/**
+ * efi_open_protocol_info_entry() - create open protocol info entry and add it
+ * to a protocol
+ * @handler: handler of a protocol
+ *
+ * Return: open protocol info entry
+ */
+static struct efi_open_protocol_info_entry *efi_create_open_info(
+ struct efi_handler *handler)
+{
+ struct efi_open_protocol_info_item *item;
+
+ item = calloc(1, sizeof(struct efi_open_protocol_info_item));
+ if (!item)
+ return NULL;
+ /* Append the item to the open protocol info list. */
+ list_add_tail(&item->link, &handler->open_infos);
+
+ return &item->info;
+}
+
+/**
+ * efi_delete_open_info() - remove an open protocol info entry from a protocol
+ * @item: open protocol info entry to delete
+ *
+ * Return: status code
+ */
+static efi_status_t efi_delete_open_info(
+ struct efi_open_protocol_info_item *item)
+{
+ list_del(&item->link);
+ free(item);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_add_protocol() - install new protocol on a handle
+ * @handle: handle on which the protocol shall be installed
+ * @protocol: GUID of the protocol to be installed
+ * @protocol_interface: interface of the protocol implementation
+ *
+ * Return: status code
+ */
+efi_status_t efi_add_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+ efi_status_t ret;
+ struct efi_register_notify_event *event;
+
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_INVALID_PARAMETER;
+ ret = efi_search_protocol(handle, protocol, NULL);
+ if (ret != EFI_NOT_FOUND)
+ return EFI_INVALID_PARAMETER;
+ handler = calloc(1, sizeof(struct efi_handler));
+ if (!handler)
+ return EFI_OUT_OF_RESOURCES;
+ handler->guid = protocol;
+ handler->protocol_interface = protocol_interface;
+ INIT_LIST_HEAD(&handler->open_infos);
+ list_add_tail(&handler->link, &efiobj->protocols);
+
+ /* Notify registered events */
+ list_for_each_entry(event, &efi_register_notify_events, link) {
+ if (!guidcmp(protocol, &event->protocol)) {
+ struct efi_protocol_notification *notif;
+
+ notif = calloc(1, sizeof(*notif));
+ if (!notif) {
+ list_del(&handler->link);
+ free(handler);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ notif->handle = handle;
+ list_add_tail(&notif->link, &event->handles);
+ event->event->is_signaled = false;
+ efi_signal_event(event->event);
+ }
+ }
+
+ if (!guidcmp(&efi_guid_device_path, protocol))
+ EFI_PRINT("installed device path '%pD'\n", protocol_interface);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_install_protocol_interface() - install protocol interface
+ * @handle: handle on which the protocol shall be installed
+ * @protocol: GUID of the protocol to be installed
+ * @protocol_interface_type: type of the interface to be installed,
+ * always EFI_NATIVE_INTERFACE
+ * @protocol_interface: interface of the protocol implementation
+ *
+ * This function implements the InstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_install_protocol_interface(
+ efi_handle_t *handle, const efi_guid_t *protocol,
+ int protocol_interface_type, void *protocol_interface)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %pUl, %d, %p", handle, protocol, protocol_interface_type,
+ protocol_interface);
+
+ if (!handle || !protocol ||
+ protocol_interface_type != EFI_NATIVE_INTERFACE) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Create new handle if requested. */
+ if (!*handle) {
+ r = efi_create_handle(handle);
+ if (r != EFI_SUCCESS)
+ goto out;
+ EFI_PRINT("new handle %p\n", *handle);
+ } else {
+ EFI_PRINT("handle %p\n", *handle);
+ }
+ /* Add new protocol */
+ r = efi_add_protocol(*handle, protocol, protocol_interface);
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_get_drivers() - get all drivers associated to a controller
+ * @handle: handle of the controller
+ * @protocol: protocol GUID (optional)
+ * @number_of_drivers: number of child controllers
+ * @driver_handle_buffer: handles of the the drivers
+ *
+ * The allocated buffer has to be freed with free().
+ *
+ * Return: status code
+ */
+static efi_status_t efi_get_drivers(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_uintn_t *number_of_drivers,
+ efi_handle_t **driver_handle_buffer)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ efi_uintn_t count = 0, i;
+ bool duplicate;
+
+ /* Count all driver associations */
+ list_for_each_entry(handler, &handle->protocols, link) {
+ if (protocol && guidcmp(handler->guid, protocol))
+ continue;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_DRIVER)
+ ++count;
+ }
+ }
+ *number_of_drivers = 0;
+ if (!count) {
+ *driver_handle_buffer = NULL;
+ return EFI_SUCCESS;
+ }
+ /*
+ * Create buffer. In case of duplicate driver assignments the buffer
+ * will be too large. But that does not harm.
+ */
+ *driver_handle_buffer = calloc(count, sizeof(efi_handle_t));
+ if (!*driver_handle_buffer)
+ return EFI_OUT_OF_RESOURCES;
+ /* Collect unique driver handles */
+ list_for_each_entry(handler, &handle->protocols, link) {
+ if (protocol && guidcmp(handler->guid, protocol))
+ continue;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_DRIVER) {
+ /* Check this is a new driver */
+ duplicate = false;
+ for (i = 0; i < *number_of_drivers; ++i) {
+ if ((*driver_handle_buffer)[i] ==
+ item->info.agent_handle)
+ duplicate = true;
+ }
+ /* Copy handle to buffer */
+ if (!duplicate) {
+ i = (*number_of_drivers)++;
+ (*driver_handle_buffer)[i] =
+ item->info.agent_handle;
+ }
+ }
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_disconnect_all_drivers() - disconnect all drivers from a controller
+ * @handle: handle of the controller
+ * @protocol: protocol GUID (optional)
+ * @child_handle: handle of the child to destroy
+ *
+ * This function implements the DisconnectController service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_disconnect_all_drivers
+ (efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_handle_t child_handle)
+{
+ efi_uintn_t number_of_drivers;
+ efi_handle_t *driver_handle_buffer;
+ efi_status_t r, ret;
+
+ ret = efi_get_drivers(handle, protocol, &number_of_drivers,
+ &driver_handle_buffer);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (!number_of_drivers)
+ return EFI_SUCCESS;
+ ret = EFI_NOT_FOUND;
+ while (number_of_drivers) {
+ r = EFI_CALL(efi_disconnect_controller(
+ handle,
+ driver_handle_buffer[--number_of_drivers],
+ child_handle));
+ if (r == EFI_SUCCESS)
+ ret = r;
+ }
+ free(driver_handle_buffer);
+ return ret;
+}
+
+/**
+ * efi_uninstall_protocol() - uninstall protocol interface
+ *
+ * @handle: handle from which the protocol shall be removed
+ * @protocol: GUID of the protocol to be removed
+ * @protocol_interface: interface to be removed
+ *
+ * This function DOES NOT delete a handle without installed protocol.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_uninstall_protocol
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ struct efi_open_protocol_info_item *pos;
+ efi_status_t r;
+
+ /* Check handle */
+ efiobj = efi_search_obj(handle);
+ if (!efiobj) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* Find the protocol on the handle */
+ r = efi_search_protocol(handle, protocol, &handler);
+ if (r != EFI_SUCCESS)
+ goto out;
+ /* Disconnect controllers */
+ efi_disconnect_all_drivers(efiobj, protocol, NULL);
+ /* Close protocol */
+ list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
+ if (item->info.attributes ==
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL ||
+ item->info.attributes == EFI_OPEN_PROTOCOL_GET_PROTOCOL ||
+ item->info.attributes == EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
+ list_del(&item->link);
+ }
+ if (!list_empty(&handler->open_infos)) {
+ r = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ r = efi_remove_protocol(handle, protocol, protocol_interface);
+out:
+ return r;
+}
+
+/**
+ * efi_uninstall_protocol_interface() - uninstall protocol interface
+ * @handle: handle from which the protocol shall be removed
+ * @protocol: GUID of the protocol to be removed
+ * @protocol_interface: interface to be removed
+ *
+ * This function implements the UninstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_uninstall_protocol_interface
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface);
+
+ ret = efi_uninstall_protocol(handle, protocol, protocol_interface);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* If the last protocol has been removed, delete the handle. */
+ if (list_empty(&handle->protocols)) {
+ list_del(&handle->link);
+ free(handle);
+ }
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_register_protocol_notify() - register an event for notification when a
+ * protocol is installed.
+ * @protocol: GUID of the protocol whose installation shall be notified
+ * @event: event to be signaled upon installation of the protocol
+ * @registration: key for retrieving the registration information
+ *
+ * This function implements the RegisterProtocolNotify service.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_register_protocol_notify(const efi_guid_t *protocol,
+ struct efi_event *event,
+ void **registration)
+{
+ struct efi_register_notify_event *item;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%pUl, %p, %p", protocol, event, registration);
+
+ if (!protocol || !event || !registration) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ item = calloc(1, sizeof(struct efi_register_notify_event));
+ if (!item) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ item->event = event;
+ guidcpy(&item->protocol, protocol);
+ INIT_LIST_HEAD(&item->handles);
+
+ list_add_tail(&item->link, &efi_register_notify_events);
+
+ *registration = item;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_search() - determine if an EFI handle implements a protocol
+ *
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @handle: handle
+ *
+ * See the documentation of the LocateHandle service in the UEFI specification.
+ *
+ * Return: 0 if the handle implements the protocol
+ */
+static int efi_search(enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, efi_handle_t handle)
+{
+ efi_status_t ret;
+
+ switch (search_type) {
+ case ALL_HANDLES:
+ return 0;
+ case BY_PROTOCOL:
+ ret = efi_search_protocol(handle, protocol, NULL);
+ return (ret != EFI_SUCCESS);
+ default:
+ /* Invalid search type */
+ return -1;
+ }
+}
+
+/**
+ * efi_check_register_notify_event() - check if registration key is valid
+ *
+ * Check that a pointer is a valid registration key as returned by
+ * RegisterProtocolNotify().
+ *
+ * @key: registration key
+ * Return: valid registration key or NULL
+ */
+static struct efi_register_notify_event *efi_check_register_notify_event
+ (void *key)
+{
+ struct efi_register_notify_event *event;
+
+ list_for_each_entry(event, &efi_register_notify_events, link) {
+ if (event == (struct efi_register_notify_event *)key)
+ return event;
+ }
+ return NULL;
+}
+
+/**
+ * efi_locate_handle() - locate handles implementing a protocol
+ *
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @buffer_size: size of the buffer to receive the handles in bytes
+ * @buffer: buffer to receive the relevant handles
+ *
+ * This function is meant for U-Boot internal calls. For the API implementation
+ * of the LocateHandle service see efi_locate_handle_ext.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_locate_handle(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *buffer_size, efi_handle_t *buffer)
+{
+ struct efi_object *efiobj;
+ efi_uintn_t size = 0;
+ struct efi_register_notify_event *event;
+ struct efi_protocol_notification *handle = NULL;
+
+ /* Check parameters */
+ switch (search_type) {
+ case ALL_HANDLES:
+ break;
+ case BY_REGISTER_NOTIFY:
+ if (!search_key)
+ return EFI_INVALID_PARAMETER;
+ /* Check that the registration key is valid */
+ event = efi_check_register_notify_event(search_key);
+ if (!event)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case BY_PROTOCOL:
+ if (!protocol)
+ return EFI_INVALID_PARAMETER;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /* Count how much space we need */
+ if (search_type == BY_REGISTER_NOTIFY) {
+ if (list_empty(&event->handles))
+ return EFI_NOT_FOUND;
+ handle = list_first_entry(&event->handles,
+ struct efi_protocol_notification,
+ link);
+ efiobj = handle->handle;
+ size += sizeof(void *);
+ } else {
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ if (!efi_search(search_type, protocol, efiobj))
+ size += sizeof(void *);
+ }
+ if (size == 0)
+ return EFI_NOT_FOUND;
+ }
+
+ if (!buffer_size)
+ return EFI_INVALID_PARAMETER;
+
+ if (*buffer_size < size) {
+ *buffer_size = size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *buffer_size = size;
+
+ /* The buffer size is sufficient but there is no buffer */
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+
+ /* Then fill the array */
+ if (search_type == BY_REGISTER_NOTIFY) {
+ *buffer = efiobj;
+ list_del(&handle->link);
+ } else {
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ if (!efi_search(search_type, protocol, efiobj))
+ *buffer++ = efiobj;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_locate_handle_ext() - locate handles implementing a protocol.
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @buffer_size: size of the buffer to receive the handles in bytes
+ * @buffer: buffer to receive the relevant handles
+ *
+ * This function implements the LocateHandle service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: 0 if the handle implements the protocol
+ */
+static efi_status_t EFIAPI efi_locate_handle_ext(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *buffer_size, efi_handle_t *buffer)
+{
+ EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
+ buffer_size, buffer);
+
+ return EFI_EXIT(efi_locate_handle(search_type, protocol, search_key,
+ buffer_size, buffer));
+}
+
+/**
+ * efi_remove_configuration_table() - collapses configuration table entries,
+ * removing index i
+ *
+ * @i: index of the table entry to be removed
+ */
+static void efi_remove_configuration_table(int i)
+{
+ struct efi_configuration_table *this = &systab.tables[i];
+ struct efi_configuration_table *next = &systab.tables[i + 1];
+ struct efi_configuration_table *end = &systab.tables[systab.nr_tables];
+
+ memmove(this, next, (ulong)end - (ulong)next);
+ systab.nr_tables--;
+}
+
+/**
+ * efi_install_configuration_table() - adds, updates, or removes a
+ * configuration table
+ * @guid: GUID of the installed table
+ * @table: table to be installed
+ *
+ * This function is used for internal calls. For the API implementation of the
+ * InstallConfigurationTable service see efi_install_configuration_table_ext.
+ *
+ * Return: status code
+ */
+efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
+ void *table)
+{
+ struct efi_event *evt;
+ int i;
+
+ if (!guid)
+ return EFI_INVALID_PARAMETER;
+
+ /* Check for GUID override */
+ for (i = 0; i < systab.nr_tables; i++) {
+ if (!guidcmp(guid, &systab.tables[i].guid)) {
+ if (table)
+ systab.tables[i].table = table;
+ else
+ efi_remove_configuration_table(i);
+ goto out;
+ }
+ }
+
+ if (!table)
+ return EFI_NOT_FOUND;
+
+ /* No override, check for overflow */
+ if (i >= EFI_MAX_CONFIGURATION_TABLES)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Add a new entry */
+ guidcpy(&systab.tables[i].guid, guid);
+ systab.tables[i].table = table;
+ systab.nr_tables = i + 1;
+
+out:
+ /* systab.nr_tables may have changed. So we need to update the CRC32 */
+ efi_update_table_header_crc32(&systab.hdr);
+
+ /* Notify that the configuration table was changed */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group && !guidcmp(evt->group, guid)) {
+ efi_signal_event(evt);
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_install_configuration_table_ex() - Adds, updates, or removes a
+ * configuration table.
+ * @guid: GUID of the installed table
+ * @table: table to be installed
+ *
+ * This function implements the InstallConfigurationTable service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid,
+ void *table)
+{
+ EFI_ENTRY("%pUl, %p", guid, table);
+ return EFI_EXIT(efi_install_configuration_table(guid, table));
+}
+
+/**
+ * efi_setup_loaded_image() - initialize a loaded image
+ *
+ * Initialize a loaded_image_info and loaded_image_info object with correct
+ * protocols, boot-device, etc.
+ *
+ * In case of an error \*handle_ptr and \*info_ptr are set to NULL and an error
+ * code is returned.
+ *
+ * @device_path: device path of the loaded image
+ * @file_path: file path of the loaded image
+ * @handle_ptr: handle of the loaded image
+ * @info_ptr: loaded image protocol
+ * Return: status code
+ */
+efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
+ struct efi_device_path *file_path,
+ struct efi_loaded_image_obj **handle_ptr,
+ struct efi_loaded_image **info_ptr)
+{
+ efi_status_t ret;
+ struct efi_loaded_image *info = NULL;
+ struct efi_loaded_image_obj *obj = NULL;
+ struct efi_device_path *dp;
+
+ /* In case of EFI_OUT_OF_RESOURCES avoid illegal free by caller. */
+ *handle_ptr = NULL;
+ *info_ptr = NULL;
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return EFI_OUT_OF_RESOURCES;
+ obj = calloc(1, sizeof(*obj));
+ if (!obj) {
+ free(info);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ obj->header.type = EFI_OBJECT_TYPE_LOADED_IMAGE;
+
+ /* Add internal object to object list */
+ efi_add_handle(&obj->header);
+
+ info->revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
+ info->file_path = file_path;
+ info->system_table = &systab;
+
+ if (device_path) {
+ info->device_handle = efi_dp_find_obj(device_path, NULL);
+
+ dp = efi_dp_append(device_path, file_path);
+ if (!dp) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto failure;
+ }
+ } else {
+ dp = NULL;
+ }
+ ret = efi_add_protocol(&obj->header,
+ &efi_guid_loaded_image_device_path, dp);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ /*
+ * When asking for the loaded_image interface, just
+ * return handle which points to loaded_image_info
+ */
+ ret = efi_add_protocol(&obj->header,
+ &efi_guid_loaded_image, info);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ *info_ptr = info;
+ *handle_ptr = obj;
+
+ return ret;
+failure:
+ printf("ERROR: Failure to install protocols for loaded image\n");
+ efi_delete_handle(&obj->header);
+ free(info);
+ return ret;
+}
+
+/**
+ * efi_locate_device_path() - Get the device path and handle of an device
+ * implementing a protocol
+ * @protocol: GUID of the protocol
+ * @device_path: device path
+ * @device: handle of the device
+ *
+ * This function implements the LocateDevicePath service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_locate_device_path(
+ const efi_guid_t *protocol,
+ struct efi_device_path **device_path,
+ efi_handle_t *device)
+{
+ struct efi_device_path *dp;
+ size_t i;
+ struct efi_handler *handler;
+ efi_handle_t *handles;
+ size_t len, len_dp;
+ size_t len_best = 0;
+ efi_uintn_t no_handles;
+ u8 *remainder;
+ efi_status_t ret;
+
+ EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
+
+ if (!protocol || !device_path || !*device_path) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Find end of device path */
+ len = efi_dp_instance_size(*device_path);
+
+ /* Get all handles implementing the protocol */
+ ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL,
+ &no_handles, &handles));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ for (i = 0; i < no_handles; ++i) {
+ /* Find the device path protocol */
+ ret = efi_search_protocol(handles[i], &efi_guid_device_path,
+ &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ dp = (struct efi_device_path *)handler->protocol_interface;
+ len_dp = efi_dp_instance_size(dp);
+ /*
+ * This handle can only be a better fit
+ * if its device path length is longer than the best fit and
+ * if its device path length is shorter of equal the searched
+ * device path.
+ */
+ if (len_dp <= len_best || len_dp > len)
+ continue;
+ /* Check if dp is a subpath of device_path */
+ if (memcmp(*device_path, dp, len_dp))
+ continue;
+ if (!device) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ *device = handles[i];
+ len_best = len_dp;
+ }
+ if (len_best) {
+ remainder = (u8 *)*device_path + len_best;
+ *device_path = (struct efi_device_path *)remainder;
+ ret = EFI_SUCCESS;
+ } else {
+ ret = EFI_NOT_FOUND;
+ }
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_load_image_from_file() - load an image from file system
+ *
+ * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the
+ * callers obligation to update the memory type as needed.
+ *
+ * @file_path: the path of the image to load
+ * @buffer: buffer containing the loaded image
+ * @size: size of the loaded image
+ * Return: status code
+ */
+static
+efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
+ void **buffer, efi_uintn_t *size)
+{
+ struct efi_file_handle *f;
+ efi_status_t ret;
+ u64 addr;
+ efi_uintn_t bs;
+
+ /* Open file */
+ f = efi_file_from_path(file_path);
+ if (!f)
+ return EFI_NOT_FOUND;
+
+ ret = efi_file_size(f, &bs);
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ /*
+ * When reading the file we do not yet know if it contains an
+ * application, a boottime driver, or a runtime driver. So here we
+ * allocate a buffer as EFI_BOOT_SERVICES_DATA. The caller has to
+ * update the reservation according to the image type.
+ */
+ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_BOOT_SERVICES_DATA,
+ efi_size_in_pages(bs), &addr);
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+
+ /* Read file */
+ EFI_CALL(ret = f->read(f, &bs, (void *)(uintptr_t)addr));
+ if (ret != EFI_SUCCESS)
+ efi_free_pages(addr, efi_size_in_pages(bs));
+ *buffer = (void *)(uintptr_t)addr;
+ *size = bs;
+error:
+ EFI_CALL(f->close(f));
+ return ret;
+}
+
+/**
+ * efi_load_image_from_path() - load an image using a file path
+ *
+ * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the
+ * callers obligation to update the memory type as needed.
+ *
+ * @boot_policy: true for request originating from the boot manager
+ * @file_path: the path of the image to load
+ * @buffer: buffer containing the loaded image
+ * @size: size of the loaded image
+ * Return: status code
+ */
+static
+efi_status_t efi_load_image_from_path(bool boot_policy,
+ struct efi_device_path *file_path,
+ void **buffer, efi_uintn_t *size)
+{
+ efi_handle_t device;
+ efi_status_t ret;
+ struct efi_device_path *dp;
+ struct efi_load_file_protocol *load_file_protocol = NULL;
+ efi_uintn_t buffer_size;
+ uint64_t addr, pages;
+ const efi_guid_t *guid;
+
+ /* In case of failure nothing is returned */
+ *buffer = NULL;
+ *size = 0;
+
+ dp = file_path;
+ ret = EFI_CALL(efi_locate_device_path(
+ &efi_simple_file_system_protocol_guid, &dp, &device));
+ if (ret == EFI_SUCCESS)
+ return efi_load_image_from_file(file_path, buffer, size);
+
+ ret = EFI_CALL(efi_locate_device_path(
+ &efi_guid_load_file_protocol, &dp, &device));
+ if (ret == EFI_SUCCESS) {
+ guid = &efi_guid_load_file_protocol;
+ } else if (!boot_policy) {
+ guid = &efi_guid_load_file2_protocol;
+ ret = EFI_CALL(efi_locate_device_path(guid, &dp, &device));
+ }
+ if (ret != EFI_SUCCESS)
+ return EFI_NOT_FOUND;
+ ret = EFI_CALL(efi_handle_protocol(device, guid,
+ (void **)&load_file_protocol));
+ if (ret != EFI_SUCCESS)
+ return EFI_NOT_FOUND;
+ buffer_size = 0;
+ ret = load_file_protocol->load_file(load_file_protocol, dp,
+ boot_policy, &buffer_size,
+ NULL);
+ if (ret != EFI_BUFFER_TOO_SMALL)
+ goto out;
+ pages = efi_size_in_pages(buffer_size);
+ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_BOOT_SERVICES_DATA,
+ pages, &addr);
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ ret = EFI_CALL(load_file_protocol->load_file(
+ load_file_protocol, dp, boot_policy,
+ &buffer_size, (void *)(uintptr_t)addr));
+ if (ret != EFI_SUCCESS)
+ efi_free_pages(addr, pages);
+out:
+ EFI_CALL(efi_close_protocol(device, guid, efi_root, NULL));
+ if (ret == EFI_SUCCESS) {
+ *buffer = (void *)(uintptr_t)addr;
+ *size = buffer_size;
+ }
+
+ return ret;
+}
+
+/**
+ * efi_load_image() - load an EFI image into memory
+ * @boot_policy: true for request originating from the boot manager
+ * @parent_image: the caller's image handle
+ * @file_path: the path of the image to load
+ * @source_buffer: memory location from which the image is installed
+ * @source_size: size of the memory area from which the image is installed
+ * @image_handle: handle for the newly installed image
+ *
+ * This function implements the LoadImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_load_image(bool boot_policy,
+ efi_handle_t parent_image,
+ struct efi_device_path *file_path,
+ void *source_buffer,
+ efi_uintn_t source_size,
+ efi_handle_t *image_handle)
+{
+ struct efi_device_path *dp, *fp;
+ struct efi_loaded_image *info = NULL;
+ struct efi_loaded_image_obj **image_obj =
+ (struct efi_loaded_image_obj **)image_handle;
+ efi_status_t ret;
+ void *dest_buffer;
+
+ EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
+ file_path, source_buffer, source_size, image_handle);
+
+ if (!image_handle || (!source_buffer && !file_path) ||
+ !efi_search_obj(parent_image) ||
+ /* The parent image handle must refer to a loaded image */
+ !parent_image->type) {
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+
+ if (!source_buffer) {
+ ret = efi_load_image_from_path(boot_policy, file_path,
+ &dest_buffer, &source_size);
+ if (ret != EFI_SUCCESS)
+ goto error;
+ } else {
+ dest_buffer = source_buffer;
+ }
+ /* split file_path which contains both the device and file parts */
+ efi_dp_split_file_path(file_path, &dp, &fp);
+ ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
+ if (ret == EFI_SUCCESS)
+ ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
+ if (!source_buffer)
+ /* Release buffer to which file was loaded */
+ efi_free_pages((uintptr_t)dest_buffer,
+ efi_size_in_pages(source_size));
+ if (ret == EFI_SUCCESS || ret == EFI_SECURITY_VIOLATION) {
+ info->system_table = &systab;
+ info->parent_handle = parent_image;
+ } else {
+ /* The image is invalid. Release all associated resources. */
+ efi_delete_handle(*image_handle);
+ *image_handle = NULL;
+ free(info);
+ }
+error:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_exit_caches() - fix up caches for EFI payloads if necessary
+ */
+static void efi_exit_caches(void)
+{
+#if defined(CONFIG_EFI_GRUB_ARM32_WORKAROUND)
+ /*
+ * Boooting Linux via GRUB prior to version 2.04 fails on 32bit ARM if
+ * caches are enabled.
+ *
+ * TODO:
+ * According to the UEFI spec caches that can be managed via CP15
+ * operations should be enabled. Caches requiring platform information
+ * to manage should be disabled. This should not happen in
+ * ExitBootServices() but before invoking any UEFI binary is invoked.
+ *
+ * We want to keep the current workaround while GRUB prior to version
+ * 2.04 is still in use.
+ */
+ cleanup_before_linux();
+#endif
+}
+
+/**
+ * efi_exit_boot_services() - stop all boot services
+ * @image_handle: handle of the loaded image
+ * @map_key: key of the memory map
+ *
+ * This function implements the ExitBootServices service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * All timer events are disabled. For exit boot services events the
+ * notification function is called. The boot services are disabled in the
+ * system table.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
+ efi_uintn_t map_key)
+{
+ struct efi_event *evt, *next_event;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %zx", image_handle, map_key);
+
+ /* Check that the caller has read the current memory map */
+ if (map_key != efi_memory_map_key) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Check if ExitBootServices has already been called */
+ if (!systab.boottime)
+ goto out;
+
+ /* Stop all timer related activities */
+ timers_enabled = false;
+
+ /* Add related events to the event group */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->type == EVT_SIGNAL_EXIT_BOOT_SERVICES)
+ evt->group = &efi_guid_event_group_exit_boot_services;
+ }
+ /* Notify that ExitBootServices is invoked. */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !guidcmp(evt->group,
+ &efi_guid_event_group_exit_boot_services)) {
+ efi_signal_event(evt);
+ break;
+ }
+ }
+
+ /* Make sure that notification functions are not called anymore */
+ efi_tpl = TPL_HIGH_LEVEL;
+
+ /* Notify variable services */
+ efi_variables_boot_exit_notify();
+
+ /* Remove all events except EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
+ list_for_each_entry_safe(evt, next_event, &efi_events, link) {
+ if (evt->type != EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)
+ list_del(&evt->link);
+ }
+
+ if (!efi_st_keep_devices) {
+ if (IS_ENABLED(CONFIG_USB_DEVICE))
+ udc_disconnect();
+ board_quiesce_devices();
+ dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+ }
+
+ /* Patch out unsupported runtime function */
+ efi_runtime_detach();
+
+ /* Fix up caches for EFI payloads if necessary */
+ efi_exit_caches();
+
+ /* This stops all lingering devices */
+ bootm_disable_interrupts();
+
+ /* Disable boot time services */
+ systab.con_in_handle = NULL;
+ systab.con_in = NULL;
+ systab.con_out_handle = NULL;
+ systab.con_out = NULL;
+ systab.stderr_handle = NULL;
+ systab.std_err = NULL;
+ systab.boottime = NULL;
+
+ /* Recalculate CRC32 */
+ efi_update_table_header_crc32(&systab.hdr);
+
+ /* Give the payload some time to boot */
+ efi_set_watchdog(0);
+ WATCHDOG_RESET();
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_get_next_monotonic_count() - get next value of the counter
+ * @count: returned value of the counter
+ *
+ * This function implements the NextMonotonicCount service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
+{
+ static uint64_t mono;
+ efi_status_t ret;
+
+ EFI_ENTRY("%p", count);
+ if (!count) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ *count = mono++;
+ ret = EFI_SUCCESS;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_stall() - sleep
+ * @microseconds: period to sleep in microseconds
+ *
+ * This function implements the Stall service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_stall(unsigned long microseconds)
+{
+ u64 end_tick;
+
+ EFI_ENTRY("%ld", microseconds);
+
+ end_tick = get_ticks() + usec_to_tick(microseconds);
+ while (get_ticks() < end_tick)
+ efi_timer_check();
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_set_watchdog_timer() - reset the watchdog timer
+ * @timeout: seconds before reset by watchdog
+ * @watchdog_code: code to be logged when resetting
+ * @data_size: size of buffer in bytes
+ * @watchdog_data: buffer with data describing the reset reason
+ *
+ * This function implements the SetWatchdogTimer service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
+ uint64_t watchdog_code,
+ unsigned long data_size,
+ uint16_t *watchdog_data)
+{
+ EFI_ENTRY("%ld, 0x%llx, %ld, %p", timeout, watchdog_code,
+ data_size, watchdog_data);
+ return EFI_EXIT(efi_set_watchdog(timeout));
+}
+
+/**
+ * efi_close_protocol() - close a protocol
+ * @handle: handle on which the protocol shall be closed
+ * @protocol: GUID of the protocol to close
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ *
+ * This function implements the CloseProtocol service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_handle_t agent_handle,
+ efi_handle_t controller_handle)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ struct efi_open_protocol_info_item *pos;
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle,
+ controller_handle);
+
+ if (!efi_search_obj(agent_handle) ||
+ (controller_handle && !efi_search_obj(controller_handle))) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ r = efi_search_protocol(handle, protocol, &handler);
+ if (r != EFI_SUCCESS)
+ goto out;
+
+ r = EFI_NOT_FOUND;
+ list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
+ if (item->info.agent_handle == agent_handle &&
+ item->info.controller_handle == controller_handle) {
+ efi_delete_open_info(item);
+ r = EFI_SUCCESS;
+ }
+ }
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_open_protocol_information() - provide information about then open status
+ * of a protocol on a handle
+ * @handle: handle for which the information shall be retrieved
+ * @protocol: GUID of the protocol
+ * @entry_buffer: buffer to receive the open protocol information
+ * @entry_count: number of entries available in the buffer
+ *
+ * This function implements the OpenProtocolInformation service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_open_protocol_information(
+ efi_handle_t handle, const efi_guid_t *protocol,
+ struct efi_open_protocol_info_entry **entry_buffer,
+ efi_uintn_t *entry_count)
+{
+ unsigned long buffer_size;
+ unsigned long count;
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, entry_buffer,
+ entry_count);
+
+ /* Check parameters */
+ if (!entry_buffer) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ r = efi_search_protocol(handle, protocol, &handler);
+ if (r != EFI_SUCCESS)
+ goto out;
+
+ /* Count entries */
+ count = 0;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.open_count)
+ ++count;
+ }
+ *entry_count = count;
+ *entry_buffer = NULL;
+ if (!count) {
+ r = EFI_SUCCESS;
+ goto out;
+ }
+
+ /* Copy entries */
+ buffer_size = count * sizeof(struct efi_open_protocol_info_entry);
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+ (void **)entry_buffer);
+ if (r != EFI_SUCCESS)
+ goto out;
+ list_for_each_entry_reverse(item, &handler->open_infos, link) {
+ if (item->info.open_count)
+ (*entry_buffer)[--count] = item->info;
+ }
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_protocols_per_handle() - get protocols installed on a handle
+ * @handle: handle for which the information is retrieved
+ * @protocol_buffer: buffer with protocol GUIDs
+ * @protocol_buffer_count: number of entries in the buffer
+ *
+ * This function implements the ProtocolsPerHandleService.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_protocols_per_handle(
+ efi_handle_t handle, efi_guid_t ***protocol_buffer,
+ efi_uintn_t *protocol_buffer_count)
+{
+ unsigned long buffer_size;
+ struct efi_object *efiobj;
+ struct list_head *protocol_handle;
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %p, %p", handle, protocol_buffer,
+ protocol_buffer_count);
+
+ if (!handle || !protocol_buffer || !protocol_buffer_count)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ *protocol_buffer = NULL;
+ *protocol_buffer_count = 0;
+
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* Count protocols */
+ list_for_each(protocol_handle, &efiobj->protocols) {
+ ++*protocol_buffer_count;
+ }
+
+ /* Copy GUIDs */
+ if (*protocol_buffer_count) {
+ size_t j = 0;
+
+ buffer_size = sizeof(efi_guid_t *) * *protocol_buffer_count;
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+ (void **)protocol_buffer);
+ if (r != EFI_SUCCESS)
+ return EFI_EXIT(r);
+ list_for_each(protocol_handle, &efiobj->protocols) {
+ struct efi_handler *protocol;
+
+ protocol = list_entry(protocol_handle,
+ struct efi_handler, link);
+ (*protocol_buffer)[j] = (void *)protocol->guid;
+ ++j;
+ }
+ }
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_locate_handle_buffer() - locate handles implementing a protocol
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @no_handles: number of returned handles
+ * @buffer: buffer with the returned handles
+ *
+ * This function implements the LocateHandleBuffer service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_locate_handle_buffer(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *no_handles, efi_handle_t **buffer)
+{
+ efi_status_t r;
+ efi_uintn_t buffer_size = 0;
+
+ EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
+ no_handles, buffer);
+
+ if (!no_handles || !buffer) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ *no_handles = 0;
+ *buffer = NULL;
+ r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
+ *buffer);
+ if (r != EFI_BUFFER_TOO_SMALL)
+ goto out;
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+ (void **)buffer);
+ if (r != EFI_SUCCESS)
+ goto out;
+ r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
+ *buffer);
+ if (r == EFI_SUCCESS)
+ *no_handles = buffer_size / sizeof(efi_handle_t);
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_locate_protocol() - find an interface implementing a protocol
+ * @protocol: GUID of the protocol
+ * @registration: registration key passed to the notification function
+ * @protocol_interface: interface implementing the protocol
+ *
+ * This function implements the LocateProtocol service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol,
+ void *registration,
+ void **protocol_interface)
+{
+ struct efi_handler *handler;
+ efi_status_t ret;
+ struct efi_object *efiobj;
+
+ EFI_ENTRY("%pUl, %p, %p", protocol, registration, protocol_interface);
+
+ /*
+ * The UEFI spec explicitly requires a protocol even if a registration
+ * key is provided. This differs from the logic in LocateHandle().
+ */
+ if (!protocol || !protocol_interface)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if (registration) {
+ struct efi_register_notify_event *event;
+ struct efi_protocol_notification *handle;
+
+ event = efi_check_register_notify_event(registration);
+ if (!event)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ /*
+ * The UEFI spec requires to return EFI_NOT_FOUND if no
+ * protocol instance matches protocol and registration.
+ * So let's do the same for a mismatch between protocol and
+ * registration.
+ */
+ if (guidcmp(&event->protocol, protocol))
+ goto not_found;
+ if (list_empty(&event->handles))
+ goto not_found;
+ handle = list_first_entry(&event->handles,
+ struct efi_protocol_notification,
+ link);
+ efiobj = handle->handle;
+ list_del(&handle->link);
+ free(handle);
+ ret = efi_search_protocol(efiobj, protocol, &handler);
+ if (ret == EFI_SUCCESS)
+ goto found;
+ } else {
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ ret = efi_search_protocol(efiobj, protocol, &handler);
+ if (ret == EFI_SUCCESS)
+ goto found;
+ }
+ }
+not_found:
+ *protocol_interface = NULL;
+ return EFI_EXIT(EFI_NOT_FOUND);
+found:
+ *protocol_interface = handler->protocol_interface;
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_install_multiple_protocol_interfaces() - Install multiple protocol
+ * interfaces
+ * @handle: handle on which the protocol interfaces shall be installed
+ * @...: NULL terminated argument list with pairs of protocol GUIDS and
+ * interfaces
+ *
+ * This function implements the MultipleProtocolInterfaces service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
+ (efi_handle_t *handle, ...)
+{
+ EFI_ENTRY("%p", handle);
+
+ efi_va_list argptr;
+ const efi_guid_t *protocol;
+ void *protocol_interface;
+ efi_handle_t old_handle;
+ efi_status_t r = EFI_SUCCESS;
+ int i = 0;
+
+ if (!handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ efi_va_start(argptr, handle);
+ for (;;) {
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ if (!protocol)
+ break;
+ protocol_interface = efi_va_arg(argptr, void*);
+ /* Check that a device path has not been installed before */
+ if (!guidcmp(protocol, &efi_guid_device_path)) {
+ struct efi_device_path *dp = protocol_interface;
+
+ r = EFI_CALL(efi_locate_device_path(protocol, &dp,
+ &old_handle));
+ if (r == EFI_SUCCESS &&
+ dp->type == DEVICE_PATH_TYPE_END) {
+ EFI_PRINT("Path %pD already installed\n",
+ protocol_interface);
+ r = EFI_ALREADY_STARTED;
+ break;
+ }
+ }
+ r = EFI_CALL(efi_install_protocol_interface(
+ handle, protocol,
+ EFI_NATIVE_INTERFACE,
+ protocol_interface));
+ if (r != EFI_SUCCESS)
+ break;
+ i++;
+ }
+ efi_va_end(argptr);
+ if (r == EFI_SUCCESS)
+ return EFI_EXIT(r);
+
+ /* If an error occurred undo all changes. */
+ efi_va_start(argptr, handle);
+ for (; i; --i) {
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ protocol_interface = efi_va_arg(argptr, void*);
+ EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol,
+ protocol_interface));
+ }
+ efi_va_end(argptr);
+
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_uninstall_multiple_protocol_interfaces() - uninstall multiple protocol
+ * interfaces
+ * @handle: handle from which the protocol interfaces shall be removed
+ * @...: NULL terminated argument list with pairs of protocol GUIDS and
+ * interfaces
+ *
+ * This function implements the UninstallMultipleProtocolInterfaces service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces(
+ efi_handle_t handle, ...)
+{
+ EFI_ENTRY("%p", handle);
+
+ efi_va_list argptr;
+ const efi_guid_t *protocol;
+ void *protocol_interface;
+ efi_status_t r = EFI_SUCCESS;
+ size_t i = 0;
+
+ if (!handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ efi_va_start(argptr, handle);
+ for (;;) {
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ if (!protocol)
+ break;
+ protocol_interface = efi_va_arg(argptr, void*);
+ r = efi_uninstall_protocol(handle, protocol,
+ protocol_interface);
+ if (r != EFI_SUCCESS)
+ break;
+ i++;
+ }
+ efi_va_end(argptr);
+ if (r == EFI_SUCCESS) {
+ /* If the last protocol has been removed, delete the handle. */
+ if (list_empty(&handle->protocols)) {
+ list_del(&handle->link);
+ free(handle);
+ }
+ return EFI_EXIT(r);
+ }
+
+ /* If an error occurred undo all changes. */
+ efi_va_start(argptr, handle);
+ for (; i; --i) {
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ protocol_interface = efi_va_arg(argptr, void*);
+ EFI_CALL(efi_install_protocol_interface(&handle, protocol,
+ EFI_NATIVE_INTERFACE,
+ protocol_interface));
+ }
+ efi_va_end(argptr);
+
+ /* In case of an error always return EFI_INVALID_PARAMETER */
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
+/**
+ * efi_calculate_crc32() - calculate cyclic redundancy code
+ * @data: buffer with data
+ * @data_size: size of buffer in bytes
+ * @crc32_p: cyclic redundancy code
+ *
+ * This function implements the CalculateCrc32 service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_calculate_crc32(const void *data,
+ efi_uintn_t data_size,
+ u32 *crc32_p)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %zu", data, data_size);
+ if (!data || !data_size || !crc32_p) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ *crc32_p = crc32(0, data, data_size);
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_copy_mem() - copy memory
+ * @destination: destination of the copy operation
+ * @source: source of the copy operation
+ * @length: number of bytes to copy
+ *
+ * This function implements the CopyMem service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static void EFIAPI efi_copy_mem(void *destination, const void *source,
+ size_t length)
+{
+ EFI_ENTRY("%p, %p, %ld", destination, source, (unsigned long)length);
+ memmove(destination, source, length);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_set_mem() - Fill memory with a byte value.
+ * @buffer: buffer to fill
+ * @size: size of buffer in bytes
+ * @value: byte to copy to the buffer
+ *
+ * This function implements the SetMem service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value)
+{
+ EFI_ENTRY("%p, %ld, 0x%x", buffer, (unsigned long)size, value);
+ memset(buffer, value, size);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_protocol_open() - open protocol interface on a handle
+ * @handler: handler of a protocol
+ * @protocol_interface: interface implementing the protocol
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ * @attributes: attributes indicating how to open the protocol
+ *
+ * Return: status code
+ */
+efi_status_t efi_protocol_open(
+ struct efi_handler *handler,
+ void **protocol_interface, void *agent_handle,
+ void *controller_handle, uint32_t attributes)
+{
+ struct efi_open_protocol_info_item *item;
+ struct efi_open_protocol_info_entry *match = NULL;
+ bool opened_by_driver = false;
+ bool opened_exclusive = false;
+
+ /* If there is no agent, only return the interface */
+ if (!agent_handle)
+ goto out;
+
+ /* For TEST_PROTOCOL ignore interface attribute */
+ if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
+ *protocol_interface = NULL;
+
+ /*
+ * Check if the protocol is already opened by a driver with the same
+ * attributes or opened exclusively
+ */
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == agent_handle) {
+ if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) &&
+ (item->info.attributes == attributes))
+ return EFI_ALREADY_STARTED;
+ } else {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_DRIVER)
+ opened_by_driver = true;
+ }
+ if (item->info.attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE)
+ opened_exclusive = true;
+ }
+
+ /* Only one controller can open the protocol exclusively */
+ if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
+ if (opened_exclusive)
+ return EFI_ACCESS_DENIED;
+ } else if (attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) {
+ if (opened_exclusive || opened_by_driver)
+ return EFI_ACCESS_DENIED;
+ }
+
+ /* Prepare exclusive opening */
+ if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
+ /* Try to disconnect controllers */
+disconnect_next:
+ opened_by_driver = false;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ efi_status_t ret;
+
+ if (item->info.attributes ==
+ EFI_OPEN_PROTOCOL_BY_DRIVER) {
+ ret = EFI_CALL(efi_disconnect_controller(
+ item->info.controller_handle,
+ item->info.agent_handle,
+ NULL));
+ if (ret == EFI_SUCCESS)
+ /*
+ * Child controllers may have been
+ * removed from the open_infos list. So
+ * let's restart the loop.
+ */
+ goto disconnect_next;
+ else
+ opened_by_driver = true;
+ }
+ }
+ /* Only one driver can be connected */
+ if (opened_by_driver)
+ return EFI_ACCESS_DENIED;
+ }
+
+ /* Find existing entry */
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == agent_handle &&
+ item->info.controller_handle == controller_handle &&
+ item->info.attributes == attributes)
+ match = &item->info;
+ }
+ /* None found, create one */
+ if (!match) {
+ match = efi_create_open_info(handler);
+ if (!match)
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ match->agent_handle = agent_handle;
+ match->controller_handle = controller_handle;
+ match->attributes = attributes;
+ match->open_count++;
+
+out:
+ /* For TEST_PROTOCOL ignore interface attribute. */
+ if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
+ *protocol_interface = handler->protocol_interface;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_open_protocol() - open protocol interface on a handle
+ * @handle: handle on which the protocol shall be opened
+ * @protocol: GUID of the protocol
+ * @protocol_interface: interface implementing the protocol
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ * @attributes: attributes indicating how to open the protocol
+ *
+ * This function implements the OpenProtocol interface.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_open_protocol
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void **protocol_interface, efi_handle_t agent_handle,
+ efi_handle_t controller_handle, uint32_t attributes)
+{
+ struct efi_handler *handler;
+ efi_status_t r = EFI_INVALID_PARAMETER;
+
+ EFI_ENTRY("%p, %pUl, %p, %p, %p, 0x%x", handle, protocol,
+ protocol_interface, agent_handle, controller_handle,
+ attributes);
+
+ if (!handle || !protocol ||
+ (!protocol_interface && attributes !=
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL)) {
+ goto out;
+ }
+
+ switch (attributes) {
+ case EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL:
+ case EFI_OPEN_PROTOCOL_GET_PROTOCOL:
+ case EFI_OPEN_PROTOCOL_TEST_PROTOCOL:
+ break;
+ case EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER:
+ if (controller_handle == handle)
+ goto out;
+ /* fall-through */
+ case EFI_OPEN_PROTOCOL_BY_DRIVER:
+ case EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE:
+ /* Check that the controller handle is valid */
+ if (!efi_search_obj(controller_handle))
+ goto out;
+ /* fall-through */
+ case EFI_OPEN_PROTOCOL_EXCLUSIVE:
+ /* Check that the agent handle is valid */
+ if (!efi_search_obj(agent_handle))
+ goto out;
+ break;
+ default:
+ goto out;
+ }
+
+ r = efi_search_protocol(handle, protocol, &handler);
+ switch (r) {
+ case EFI_SUCCESS:
+ break;
+ case EFI_NOT_FOUND:
+ r = EFI_UNSUPPORTED;
+ goto out;
+ default:
+ goto out;
+ }
+
+ r = efi_protocol_open(handler, protocol_interface, agent_handle,
+ controller_handle, attributes);
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_start_image() - call the entry point of an image
+ * @image_handle: handle of the image
+ * @exit_data_size: size of the buffer
+ * @exit_data: buffer to receive the exit data of the called image
+ *
+ * This function implements the StartImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
+ efi_uintn_t *exit_data_size,
+ u16 **exit_data)
+{
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+ efi_status_t ret;
+ void *info;
+ efi_handle_t parent_image = current_image;
+ efi_status_t exit_status;
+ struct jmp_buf_data exit_jmp;
+
+ EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+
+ if (!efi_search_obj(image_handle))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* Check parameters */
+ if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if (image_obj->auth_status != EFI_IMAGE_AUTH_PASSED)
+ return EFI_EXIT(EFI_SECURITY_VIOLATION);
+
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
+ &info, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ image_obj->exit_data_size = exit_data_size;
+ image_obj->exit_data = exit_data;
+ image_obj->exit_status = &exit_status;
+ image_obj->exit_jmp = &exit_jmp;
+
+ /* call the image! */
+ if (setjmp(&exit_jmp)) {
+ /*
+ * We called the entry point of the child image with EFI_CALL
+ * in the lines below. The child image called the Exit() boot
+ * service efi_exit() which executed the long jump that brought
+ * us to the current line. This implies that the second half
+ * of the EFI_CALL macro has not been executed.
+ */
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
+ /*
+ * efi_exit() called efi_restore_gd(). We have to undo this
+ * otherwise __efi_entry_check() will put the wrong value into
+ * app_gd.
+ */
+ set_gd(app_gd);
+#endif
+ /*
+ * To get ready to call EFI_EXIT below we have to execute the
+ * missed out steps of EFI_CALL.
+ */
+ assert(__efi_entry_check());
+ EFI_PRINT("%lu returned by started image\n",
+ (unsigned long)((uintptr_t)exit_status &
+ ~EFI_ERROR_MASK));
+ current_image = parent_image;
+ return EFI_EXIT(exit_status);
+ }
+
+ current_image = image_handle;
+ image_obj->header.type = EFI_OBJECT_TYPE_STARTED_IMAGE;
+ EFI_PRINT("Jumping into 0x%p\n", image_obj->entry);
+ ret = EFI_CALL(image_obj->entry(image_handle, &systab));
+
+ /*
+ * Control is returned from a started UEFI image either by calling
+ * Exit() (where exit data can be provided) or by simply returning from
+ * the entry point. In the latter case call Exit() on behalf of the
+ * image.
+ */
+ return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
+}
+
+/**
+ * efi_delete_image() - delete loaded image from memory)
+ *
+ * @image_obj: handle of the loaded image
+ * @loaded_image_protocol: loaded image protocol
+ */
+static efi_status_t efi_delete_image
+ (struct efi_loaded_image_obj *image_obj,
+ struct efi_loaded_image *loaded_image_protocol)
+{
+ struct efi_object *efiobj;
+ efi_status_t r, ret = EFI_SUCCESS;
+
+close_next:
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ struct efi_handler *protocol;
+
+ list_for_each_entry(protocol, &efiobj->protocols, link) {
+ struct efi_open_protocol_info_item *info;
+
+ list_for_each_entry(info, &protocol->open_infos, link) {
+ if (info->info.agent_handle !=
+ (efi_handle_t)image_obj)
+ continue;
+ r = EFI_CALL(efi_close_protocol
+ (efiobj, protocol->guid,
+ info->info.agent_handle,
+ info->info.controller_handle
+ ));
+ if (r != EFI_SUCCESS)
+ ret = r;
+ /*
+ * Closing protocols may results in further
+ * items being deleted. To play it safe loop
+ * over all elements again.
+ */
+ goto close_next;
+ }
+ }
+ }
+
+ efi_free_pages((uintptr_t)loaded_image_protocol->image_base,
+ efi_size_in_pages(loaded_image_protocol->image_size));
+ efi_delete_handle(&image_obj->header);
+
+ return ret;
+}
+
+/**
+ * efi_unload_image() - unload an EFI image
+ * @image_handle: handle of the image to be unloaded
+ *
+ * This function implements the UnloadImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct efi_object *efiobj;
+ struct efi_loaded_image *loaded_image_protocol;
+
+ EFI_ENTRY("%p", image_handle);
+
+ efiobj = efi_search_obj(image_handle);
+ if (!efiobj) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* Find the loaded image protocol */
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
+ (void **)&loaded_image_protocol,
+ NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ switch (efiobj->type) {
+ case EFI_OBJECT_TYPE_STARTED_IMAGE:
+ /* Call the unload function */
+ if (!loaded_image_protocol->unload) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ ret = EFI_CALL(loaded_image_protocol->unload(image_handle));
+ if (ret != EFI_SUCCESS)
+ goto out;
+ break;
+ case EFI_OBJECT_TYPE_LOADED_IMAGE:
+ break;
+ default:
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ efi_delete_image((struct efi_loaded_image_obj *)efiobj,
+ loaded_image_protocol);
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_update_exit_data() - fill exit data parameters of StartImage()
+ *
+ * @image_obj: image handle
+ * @exit_data_size: size of the exit data buffer
+ * @exit_data: buffer with data returned by UEFI payload
+ * Return: status code
+ */
+static efi_status_t efi_update_exit_data(struct efi_loaded_image_obj *image_obj,
+ efi_uintn_t exit_data_size,
+ u16 *exit_data)
+{
+ efi_status_t ret;
+
+ /*
+ * If exit_data is not provided to StartImage(), exit_data_size must be
+ * ignored.
+ */
+ if (!image_obj->exit_data)
+ return EFI_SUCCESS;
+ if (image_obj->exit_data_size)
+ *image_obj->exit_data_size = exit_data_size;
+ if (exit_data_size && exit_data) {
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA,
+ exit_data_size,
+ (void **)image_obj->exit_data);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ memcpy(*image_obj->exit_data, exit_data, exit_data_size);
+ } else {
+ image_obj->exit_data = NULL;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_exit() - leave an EFI application or driver
+ * @image_handle: handle of the application or driver that is exiting
+ * @exit_status: status code
+ * @exit_data_size: size of the buffer in bytes
+ * @exit_data: buffer with data describing an error
+ *
+ * This function implements the Exit service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
+ efi_status_t exit_status,
+ efi_uintn_t exit_data_size,
+ u16 *exit_data)
+{
+ /*
+ * TODO: We should call the unload procedure of the loaded
+ * image protocol.
+ */
+ efi_status_t ret;
+ struct efi_loaded_image *loaded_image_protocol;
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+ struct jmp_buf_data *exit_jmp;
+
+ EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
+ exit_data_size, exit_data);
+
+ /* Check parameters */
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
+ (void **)&loaded_image_protocol,
+ NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Unloading of unstarted images */
+ switch (image_obj->header.type) {
+ case EFI_OBJECT_TYPE_STARTED_IMAGE:
+ break;
+ case EFI_OBJECT_TYPE_LOADED_IMAGE:
+ efi_delete_image(image_obj, loaded_image_protocol);
+ ret = EFI_SUCCESS;
+ goto out;
+ default:
+ /* Handle does not refer to loaded image */
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* A started image can only be unloaded it is the last one started. */
+ if (image_handle != current_image) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Exit data is only foreseen in case of failure. */
+ if (exit_status != EFI_SUCCESS) {
+ ret = efi_update_exit_data(image_obj, exit_data_size,
+ exit_data);
+ /* Exiting has priority. Don't return error to caller. */
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("%s: out of memory\n", __func__);
+ }
+ /* efi_delete_image() frees image_obj. Copy before the call. */
+ exit_jmp = image_obj->exit_jmp;
+ *image_obj->exit_status = exit_status;
+ if (image_obj->image_type == IMAGE_SUBSYSTEM_EFI_APPLICATION ||
+ exit_status != EFI_SUCCESS)
+ efi_delete_image(image_obj, loaded_image_protocol);
+
+ /* Make sure entry/exit counts for EFI world cross-overs match */
+ EFI_EXIT(exit_status);
+
+ /*
+ * But longjmp out with the U-Boot gd, not the application's, as
+ * the other end is a setjmp call inside EFI context.
+ */
+ efi_restore_gd();
+
+ longjmp(exit_jmp, 1);
+
+ panic("EFI application exited");
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_handle_protocol() - get interface of a protocol on a handle
+ * @handle: handle on which the protocol shall be opened
+ * @protocol: GUID of the protocol
+ * @protocol_interface: interface implementing the protocol
+ *
+ * This function implements the HandleProtocol service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ void **protocol_interface)
+{
+ return efi_open_protocol(handle, protocol, protocol_interface, efi_root,
+ NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
+}
+
+/**
+ * efi_bind_controller() - bind a single driver to a controller
+ * @controller_handle: controller handle
+ * @driver_image_handle: driver handle
+ * @remain_device_path: remaining path
+ *
+ * Return: status code
+ */
+static efi_status_t efi_bind_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t driver_image_handle,
+ struct efi_device_path *remain_device_path)
+{
+ struct efi_driver_binding_protocol *binding_protocol;
+ efi_status_t r;
+
+ r = EFI_CALL(efi_open_protocol(driver_image_handle,
+ &efi_guid_driver_binding_protocol,
+ (void **)&binding_protocol,
+ driver_image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (r != EFI_SUCCESS)
+ return r;
+ r = EFI_CALL(binding_protocol->supported(binding_protocol,
+ controller_handle,
+ remain_device_path));
+ if (r == EFI_SUCCESS)
+ r = EFI_CALL(binding_protocol->start(binding_protocol,
+ controller_handle,
+ remain_device_path));
+ EFI_CALL(efi_close_protocol(driver_image_handle,
+ &efi_guid_driver_binding_protocol,
+ driver_image_handle, NULL));
+ return r;
+}
+
+/**
+ * efi_connect_single_controller() - connect a single driver to a controller
+ * @controller_handle: controller
+ * @driver_image_handle: driver
+ * @remain_device_path: remaining path
+ *
+ * Return: status code
+ */
+static efi_status_t efi_connect_single_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t *driver_image_handle,
+ struct efi_device_path *remain_device_path)
+{
+ efi_handle_t *buffer;
+ size_t count;
+ size_t i;
+ efi_status_t r;
+ size_t connected = 0;
+
+ /* Get buffer with all handles with driver binding protocol */
+ r = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
+ &efi_guid_driver_binding_protocol,
+ NULL, &count, &buffer));
+ if (r != EFI_SUCCESS)
+ return r;
+
+ /* Context Override */
+ if (driver_image_handle) {
+ for (; *driver_image_handle; ++driver_image_handle) {
+ for (i = 0; i < count; ++i) {
+ if (buffer[i] == *driver_image_handle) {
+ buffer[i] = NULL;
+ r = efi_bind_controller(
+ controller_handle,
+ *driver_image_handle,
+ remain_device_path);
+ /*
+ * For drivers that do not support the
+ * controller or are already connected
+ * we receive an error code here.
+ */
+ if (r == EFI_SUCCESS)
+ ++connected;
+ }
+ }
+ }
+ }
+
+ /*
+ * TODO: Some overrides are not yet implemented:
+ * - Platform Driver Override
+ * - Driver Family Override Search
+ * - Bus Specific Driver Override
+ */
+
+ /* Driver Binding Search */
+ for (i = 0; i < count; ++i) {
+ if (buffer[i]) {
+ r = efi_bind_controller(controller_handle,
+ buffer[i],
+ remain_device_path);
+ if (r == EFI_SUCCESS)
+ ++connected;
+ }
+ }
+
+ efi_free_pool(buffer);
+ if (!connected)
+ return EFI_NOT_FOUND;
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_connect_controller() - connect a controller to a driver
+ * @controller_handle: handle of the controller
+ * @driver_image_handle: handle of the driver
+ * @remain_device_path: device path of a child controller
+ * @recursive: true to connect all child controllers
+ *
+ * This function implements the ConnectController service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * First all driver binding protocol handles are tried for binding drivers.
+ * Afterwards all handles that have opened a protocol of the controller
+ * with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_connect_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t *driver_image_handle,
+ struct efi_device_path *remain_device_path,
+ bool recursive)
+{
+ efi_status_t r;
+ efi_status_t ret = EFI_NOT_FOUND;
+ struct efi_object *efiobj;
+
+ EFI_ENTRY("%p, %p, %pD, %d", controller_handle, driver_image_handle,
+ remain_device_path, recursive);
+
+ efiobj = efi_search_obj(controller_handle);
+ if (!efiobj) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ r = efi_connect_single_controller(controller_handle,
+ driver_image_handle,
+ remain_device_path);
+ if (r == EFI_SUCCESS)
+ ret = EFI_SUCCESS;
+ if (recursive) {
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
+ r = EFI_CALL(efi_connect_controller(
+ item->info.controller_handle,
+ driver_image_handle,
+ remain_device_path,
+ recursive));
+ if (r == EFI_SUCCESS)
+ ret = EFI_SUCCESS;
+ }
+ }
+ }
+ }
+ /* Check for child controller specified by end node */
+ if (ret != EFI_SUCCESS && remain_device_path &&
+ remain_device_path->type == DEVICE_PATH_TYPE_END)
+ ret = EFI_SUCCESS;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_reinstall_protocol_interface() - reinstall protocol interface
+ * @handle: handle on which the protocol shall be reinstalled
+ * @protocol: GUID of the protocol to be installed
+ * @old_interface: interface to be removed
+ * @new_interface: interface to be installed
+ *
+ * This function implements the ReinstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * The old interface is uninstalled. The new interface is installed.
+ * Drivers are connected.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_reinstall_protocol_interface(
+ efi_handle_t handle, const efi_guid_t *protocol,
+ void *old_interface, void *new_interface)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface,
+ new_interface);
+
+ /* Uninstall protocol but do not delete handle */
+ ret = efi_uninstall_protocol(handle, protocol, old_interface);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Install the new protocol */
+ ret = efi_add_protocol(handle, protocol, new_interface);
+ /*
+ * The UEFI spec does not specify what should happen to the handle
+ * if in case of an error no protocol interface remains on the handle.
+ * So let's do nothing here.
+ */
+ if (ret != EFI_SUCCESS)
+ goto out;
+ /*
+ * The returned status code has to be ignored.
+ * Do not create an error if no suitable driver for the handle exists.
+ */
+ EFI_CALL(efi_connect_controller(handle, NULL, NULL, true));
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_get_child_controllers() - get all child controllers associated to a driver
+ * @efiobj: handle of the controller
+ * @driver_handle: handle of the driver
+ * @number_of_children: number of child controllers
+ * @child_handle_buffer: handles of the the child controllers
+ *
+ * The allocated buffer has to be freed with free().
+ *
+ * Return: status code
+ */
+static efi_status_t efi_get_child_controllers(
+ struct efi_object *efiobj,
+ efi_handle_t driver_handle,
+ efi_uintn_t *number_of_children,
+ efi_handle_t **child_handle_buffer)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ efi_uintn_t count = 0, i;
+ bool duplicate;
+
+ /* Count all child controller associations */
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == driver_handle &&
+ item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER)
+ ++count;
+ }
+ }
+ /*
+ * Create buffer. In case of duplicate child controller assignments
+ * the buffer will be too large. But that does not harm.
+ */
+ *number_of_children = 0;
+ if (!count)
+ return EFI_SUCCESS;
+ *child_handle_buffer = calloc(count, sizeof(efi_handle_t));
+ if (!*child_handle_buffer)
+ return EFI_OUT_OF_RESOURCES;
+ /* Copy unique child handles */
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == driver_handle &&
+ item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
+ /* Check this is a new child controller */
+ duplicate = false;
+ for (i = 0; i < *number_of_children; ++i) {
+ if ((*child_handle_buffer)[i] ==
+ item->info.controller_handle)
+ duplicate = true;
+ }
+ /* Copy handle to buffer */
+ if (!duplicate) {
+ i = (*number_of_children)++;
+ (*child_handle_buffer)[i] =
+ item->info.controller_handle;
+ }
+ }
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_disconnect_controller() - disconnect a controller from a driver
+ * @controller_handle: handle of the controller
+ * @driver_image_handle: handle of the driver
+ * @child_handle: handle of the child to destroy
+ *
+ * This function implements the DisconnectController service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_disconnect_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t driver_image_handle,
+ efi_handle_t child_handle)
+{
+ struct efi_driver_binding_protocol *binding_protocol;
+ efi_handle_t *child_handle_buffer = NULL;
+ size_t number_of_children = 0;
+ efi_status_t r;
+ struct efi_object *efiobj;
+ bool sole_child;
+
+ EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
+ child_handle);
+
+ efiobj = efi_search_obj(controller_handle);
+ if (!efiobj) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (child_handle && !efi_search_obj(child_handle)) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* If no driver handle is supplied, disconnect all drivers */
+ if (!driver_image_handle) {
+ r = efi_disconnect_all_drivers(efiobj, NULL, child_handle);
+ goto out;
+ }
+
+ /* Create list of child handles */
+ r = efi_get_child_controllers(efiobj,
+ driver_image_handle,
+ &number_of_children,
+ &child_handle_buffer);
+ if (r != EFI_SUCCESS)
+ return r;
+ sole_child = (number_of_children == 1);
+
+ if (child_handle) {
+ number_of_children = 1;
+ free(child_handle_buffer);
+ child_handle_buffer = &child_handle;
+ }
+
+ /* Get the driver binding protocol */
+ r = EFI_CALL(efi_open_protocol(driver_image_handle,
+ &efi_guid_driver_binding_protocol,
+ (void **)&binding_protocol,
+ driver_image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (r != EFI_SUCCESS) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* Remove the children */
+ if (number_of_children) {
+ r = EFI_CALL(binding_protocol->stop(binding_protocol,
+ controller_handle,
+ number_of_children,
+ child_handle_buffer));
+ if (r != EFI_SUCCESS) {
+ r = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ }
+ /* Remove the driver */
+ if (!child_handle || sole_child) {
+ r = EFI_CALL(binding_protocol->stop(binding_protocol,
+ controller_handle,
+ 0, NULL));
+ if (r != EFI_SUCCESS) {
+ r = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ }
+ EFI_CALL(efi_close_protocol(driver_image_handle,
+ &efi_guid_driver_binding_protocol,
+ driver_image_handle, NULL));
+ r = EFI_SUCCESS;
+out:
+ if (!child_handle)
+ free(child_handle_buffer);
+ return EFI_EXIT(r);
+}
+
+static struct efi_boot_services efi_boot_services = {
+ .hdr = {
+ .signature = EFI_BOOT_SERVICES_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_boot_services),
+ },
+ .raise_tpl = efi_raise_tpl,
+ .restore_tpl = efi_restore_tpl,
+ .allocate_pages = efi_allocate_pages_ext,
+ .free_pages = efi_free_pages_ext,
+ .get_memory_map = efi_get_memory_map_ext,
+ .allocate_pool = efi_allocate_pool_ext,
+ .free_pool = efi_free_pool_ext,
+ .create_event = efi_create_event_ext,
+ .set_timer = efi_set_timer_ext,
+ .wait_for_event = efi_wait_for_event,
+ .signal_event = efi_signal_event_ext,
+ .close_event = efi_close_event,
+ .check_event = efi_check_event,
+ .install_protocol_interface = efi_install_protocol_interface,
+ .reinstall_protocol_interface = efi_reinstall_protocol_interface,
+ .uninstall_protocol_interface = efi_uninstall_protocol_interface,
+ .handle_protocol = efi_handle_protocol,
+ .reserved = NULL,
+ .register_protocol_notify = efi_register_protocol_notify,
+ .locate_handle = efi_locate_handle_ext,
+ .locate_device_path = efi_locate_device_path,
+ .install_configuration_table = efi_install_configuration_table_ext,
+ .load_image = efi_load_image,
+ .start_image = efi_start_image,
+ .exit = efi_exit,
+ .unload_image = efi_unload_image,
+ .exit_boot_services = efi_exit_boot_services,
+ .get_next_monotonic_count = efi_get_next_monotonic_count,
+ .stall = efi_stall,
+ .set_watchdog_timer = efi_set_watchdog_timer,
+ .connect_controller = efi_connect_controller,
+ .disconnect_controller = efi_disconnect_controller,
+ .open_protocol = efi_open_protocol,
+ .close_protocol = efi_close_protocol,
+ .open_protocol_information = efi_open_protocol_information,
+ .protocols_per_handle = efi_protocols_per_handle,
+ .locate_handle_buffer = efi_locate_handle_buffer,
+ .locate_protocol = efi_locate_protocol,
+ .install_multiple_protocol_interfaces =
+ efi_install_multiple_protocol_interfaces,
+ .uninstall_multiple_protocol_interfaces =
+ efi_uninstall_multiple_protocol_interfaces,
+ .calculate_crc32 = efi_calculate_crc32,
+ .copy_mem = efi_copy_mem,
+ .set_mem = efi_set_mem,
+ .create_event_ex = efi_create_event_ex,
+};
+
+static u16 __efi_runtime_data firmware_vendor[] = L"Das U-Boot";
+
+struct efi_system_table __efi_runtime_data systab = {
+ .hdr = {
+ .signature = EFI_SYSTEM_TABLE_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_system_table),
+ },
+ .fw_vendor = firmware_vendor,
+ .fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8,
+ .runtime = &efi_runtime_services,
+ .nr_tables = 0,
+ .tables = NULL,
+};
+
+/**
+ * efi_initialize_system_table() - Initialize system table
+ *
+ * Return: status code
+ */
+efi_status_t efi_initialize_system_table(void)
+{
+ efi_status_t ret;
+
+ /* Allocate configuration table array */
+ ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
+ EFI_MAX_CONFIGURATION_TABLES *
+ sizeof(struct efi_configuration_table),
+ (void **)&systab.tables);
+
+ /*
+ * These entries will be set to NULL in ExitBootServices(). To avoid
+ * relocation in SetVirtualAddressMap(), set them dynamically.
+ */
+ systab.con_in = &efi_con_in;
+ systab.con_out = &efi_con_out;
+ systab.std_err = &efi_con_out;
+ systab.boottime = &efi_boot_services;
+
+ /* Set CRC32 field in table headers */
+ efi_update_table_header_crc32(&systab.hdr);
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
+ efi_update_table_header_crc32(&efi_boot_services.hdr);
+
+ return ret;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_capsule.c b/roms/u-boot/lib/efi_loader/efi_capsule.c
new file mode 100644
index 000000000..50bed32bf
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_capsule.c
@@ -0,0 +1,1055 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Capsule
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Author: AKASHI Takahiro
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <sort.h>
+
+#include <crypto/pkcs7.h>
+#include <crypto/pkcs7_parser.h>
+#include <linux/err.h>
+
+const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
+static const efi_guid_t efi_guid_firmware_management_capsule_id =
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
+const efi_guid_t efi_guid_firmware_management_protocol =
+ EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
+
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+/* for file system access */
+static struct efi_file_handle *bootdev_root;
+#endif
+
+/**
+ * get_last_capsule - get the last capsule index
+ *
+ * Retrieve the index of the capsule invoked last time from "CapsuleLast"
+ * variable.
+ *
+ * Return:
+ * * > 0 - the last capsule index invoked
+ * * 0xffff - on error, or no capsule invoked yet
+ */
+static __maybe_unused unsigned int get_last_capsule(void)
+{
+ u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
+ char value[5];
+ efi_uintn_t size;
+ unsigned long index = 0xffff;
+ efi_status_t ret;
+ int i;
+
+ size = sizeof(value16);
+ ret = efi_get_variable_int(L"CapsuleLast", &efi_guid_capsule_report,
+ NULL, &size, value16, NULL);
+ if (ret != EFI_SUCCESS || size != 22 ||
+ u16_strncmp(value16, L"Capsule", 7))
+ goto err;
+ for (i = 0; i < 4; ++i) {
+ u16 c = value16[i + 7];
+
+ if (!c || c > 0x7f)
+ goto err;
+ value[i] = c;
+ }
+ value[4] = 0;
+ if (strict_strtoul(value, 16, &index))
+ index = 0xffff;
+err:
+ return index;
+}
+
+/**
+ * set_capsule_result - set a result variable
+ * @capsule: Capsule
+ * @return_status: Return status
+ *
+ * Create and set a result variable, "CapsuleXXXX", for the capsule,
+ * @capsule.
+ */
+static __maybe_unused
+void set_capsule_result(int index, struct efi_capsule_header *capsule,
+ efi_status_t return_status)
+{
+ u16 variable_name16[12];
+ struct efi_capsule_result_variable_header result;
+ struct efi_time time;
+ efi_status_t ret;
+
+ efi_create_indexed_name(variable_name16, sizeof(variable_name16),
+ "Capsule", index);
+ result.variable_total_size = sizeof(result);
+ result.capsule_guid = capsule->capsule_guid;
+ ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL));
+ if (ret == EFI_SUCCESS)
+ memcpy(&result.capsule_processed, &time, sizeof(time));
+ else
+ memset(&result.capsule_processed, 0, sizeof(time));
+ result.capsule_status = return_status;
+ ret = efi_set_variable(variable_name16, &efi_guid_capsule_report,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(result), &result);
+ if (ret)
+ log_err("EFI: creating %ls failed\n", variable_name16);
+}
+
+#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT
+/**
+ * efi_fmp_find - search for Firmware Management Protocol drivers
+ * @image_type: Image type guid
+ * @instance: Instance number
+ * @handles: Handles of FMP drivers
+ * @no_handles: Number of handles
+ *
+ * Search for Firmware Management Protocol drivers, matching the image
+ * type, @image_type and the machine instance, @instance, from the list,
+ * @handles.
+ *
+ * Return:
+ * * Protocol instance - on success
+ * * NULL - on failure
+ */
+static struct efi_firmware_management_protocol *
+efi_fmp_find(efi_guid_t *image_type, u64 instance, efi_handle_t *handles,
+ efi_uintn_t no_handles)
+{
+ efi_handle_t *handle;
+ struct efi_firmware_management_protocol *fmp;
+ struct efi_firmware_image_descriptor *image_info, *desc;
+ efi_uintn_t info_size, descriptor_size;
+ u32 descriptor_version;
+ u8 descriptor_count;
+ u32 package_version;
+ u16 *package_version_name;
+ bool found = false;
+ int i, j;
+ efi_status_t ret;
+
+ for (i = 0, handle = handles; i < no_handles; i++, handle++) {
+ ret = EFI_CALL(efi_handle_protocol(
+ *handle,
+ &efi_guid_firmware_management_protocol,
+ (void **)&fmp));
+ if (ret != EFI_SUCCESS)
+ continue;
+
+ /* get device's image info */
+ info_size = 0;
+ image_info = NULL;
+ descriptor_version = 0;
+ descriptor_count = 0;
+ descriptor_size = 0;
+ package_version = 0;
+ package_version_name = NULL;
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
+ image_info,
+ &descriptor_version,
+ &descriptor_count,
+ &descriptor_size,
+ &package_version,
+ &package_version_name));
+ if (ret != EFI_BUFFER_TOO_SMALL)
+ goto skip;
+
+ image_info = malloc(info_size);
+ if (!image_info)
+ goto skip;
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size,
+ image_info,
+ &descriptor_version,
+ &descriptor_count,
+ &descriptor_size,
+ &package_version,
+ &package_version_name));
+ if (ret != EFI_SUCCESS ||
+ descriptor_version != EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION)
+ goto skip;
+
+ /* matching */
+ for (j = 0, desc = image_info; j < descriptor_count;
+ j++, desc = (void *)desc + descriptor_size) {
+ log_debug("+++ desc[%d] index: %d, name: %ls\n",
+ j, desc->image_index, desc->image_id_name);
+ if (!guidcmp(&desc->image_type_id, image_type) &&
+ (!instance ||
+ !desc->hardware_instance ||
+ desc->hardware_instance == instance))
+ found = true;
+ }
+
+skip:
+ efi_free_pool(package_version_name);
+ free(image_info);
+ EFI_CALL(efi_close_protocol(
+ (efi_handle_t)fmp,
+ &efi_guid_firmware_management_protocol,
+ NULL, NULL));
+ if (found)
+ return fmp;
+ }
+
+ return NULL;
+}
+
+#if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+
+const efi_guid_t efi_guid_capsule_root_cert_guid =
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
+
+efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size,
+ void **image, efi_uintn_t *image_size)
+{
+ u8 *buf;
+ int ret;
+ void *fdt_pkey, *pkey;
+ efi_uintn_t pkey_len;
+ uint64_t monotonic_count;
+ struct efi_signature_store *truststore;
+ struct pkcs7_message *capsule_sig;
+ struct efi_image_regions *regs;
+ struct efi_firmware_image_authentication *auth_hdr;
+ efi_status_t status;
+
+ status = EFI_SECURITY_VIOLATION;
+ capsule_sig = NULL;
+ truststore = NULL;
+ regs = NULL;
+
+ /* Sanity checks */
+ if (capsule == NULL || capsule_size == 0)
+ goto out;
+
+ auth_hdr = (struct efi_firmware_image_authentication *)capsule;
+ if (capsule_size < sizeof(*auth_hdr))
+ goto out;
+
+ if (auth_hdr->auth_info.hdr.dwLength <=
+ offsetof(struct win_certificate_uefi_guid, cert_data))
+ goto out;
+
+ if (guidcmp(&auth_hdr->auth_info.cert_type, &efi_guid_cert_type_pkcs7))
+ goto out;
+
+ *image = (uint8_t *)capsule + sizeof(auth_hdr->monotonic_count) +
+ auth_hdr->auth_info.hdr.dwLength;
+ *image_size = capsule_size - auth_hdr->auth_info.hdr.dwLength -
+ sizeof(auth_hdr->monotonic_count);
+ memcpy(&monotonic_count, &auth_hdr->monotonic_count,
+ sizeof(monotonic_count));
+
+ /* data to be digested */
+ regs = calloc(sizeof(*regs) + sizeof(struct image_region) * 2, 1);
+ if (!regs)
+ goto out;
+
+ regs->max = 2;
+ efi_image_region_add(regs, (uint8_t *)*image,
+ (uint8_t *)*image + *image_size, 1);
+
+ efi_image_region_add(regs, (uint8_t *)&monotonic_count,
+ (uint8_t *)&monotonic_count + sizeof(monotonic_count),
+ 1);
+
+ capsule_sig = efi_parse_pkcs7_header(auth_hdr->auth_info.cert_data,
+ auth_hdr->auth_info.hdr.dwLength
+ - sizeof(auth_hdr->auth_info),
+ &buf);
+ if (IS_ERR(capsule_sig)) {
+ debug("Parsing variable's pkcs7 header failed\n");
+ capsule_sig = NULL;
+ goto out;
+ }
+
+ ret = efi_get_public_key_data(&fdt_pkey, &pkey_len);
+ if (ret < 0)
+ goto out;
+
+ pkey = malloc(pkey_len);
+ if (!pkey)
+ goto out;
+
+ memcpy(pkey, fdt_pkey, pkey_len);
+ truststore = efi_build_signature_store(pkey, pkey_len);
+ if (!truststore)
+ goto out;
+
+ /* verify signature */
+ if (efi_signature_verify(regs, capsule_sig, truststore, NULL)) {
+ debug("Verified\n");
+ } else {
+ debug("Verifying variable's signature failed\n");
+ goto out;
+ }
+
+ status = EFI_SUCCESS;
+
+out:
+ efi_sigstore_free(truststore);
+ pkcs7_free_message(capsule_sig);
+ free(regs);
+
+ return status;
+}
+#else
+efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size,
+ void **image, efi_uintn_t *image_size)
+{
+ return EFI_UNSUPPORTED;
+}
+#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */
+
+
+/**
+ * efi_capsule_update_firmware - update firmware from capsule
+ * @capsule_data: Capsule
+ *
+ * Update firmware, using a capsule, @capsule_data. Loading any FMP
+ * drivers embedded in a capsule is not supported.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_capsule_update_firmware(
+ struct efi_capsule_header *capsule_data)
+{
+ struct efi_firmware_management_capsule_header *capsule;
+ struct efi_firmware_management_capsule_image_header *image;
+ size_t capsule_size;
+ void *image_binary, *vendor_code;
+ efi_handle_t *handles;
+ efi_uintn_t no_handles;
+ int item;
+ struct efi_firmware_management_protocol *fmp;
+ u16 *abort_reason;
+ efi_status_t ret = EFI_SUCCESS;
+
+ /* sanity check */
+ if (capsule_data->header_size < sizeof(*capsule) ||
+ capsule_data->header_size >= capsule_data->capsule_image_size)
+ return EFI_INVALID_PARAMETER;
+
+ capsule = (void *)capsule_data + capsule_data->header_size;
+ capsule_size = capsule_data->capsule_image_size
+ - capsule_data->header_size;
+
+ if (capsule->version != 0x00000001)
+ return EFI_UNSUPPORTED;
+
+ handles = NULL;
+ ret = EFI_CALL(efi_locate_handle_buffer(
+ BY_PROTOCOL,
+ &efi_guid_firmware_management_protocol,
+ NULL, &no_handles, (efi_handle_t **)&handles));
+ if (ret != EFI_SUCCESS)
+ return EFI_UNSUPPORTED;
+
+ /* Payload */
+ for (item = capsule->embedded_driver_count;
+ item < capsule->embedded_driver_count
+ + capsule->payload_item_count; item++) {
+ /* sanity check */
+ if ((capsule->item_offset_list[item] + sizeof(*image)
+ >= capsule_size)) {
+ log_err("EFI: A capsule has not enough data\n");
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ image = (void *)capsule + capsule->item_offset_list[item];
+
+ if (image->version != 0x00000003) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ /* find a device for update firmware */
+ /* TODO: should we pass index as well, or nothing but type? */
+ fmp = efi_fmp_find(&image->update_image_type_id,
+ image->update_hardware_instance,
+ handles, no_handles);
+ if (!fmp) {
+ log_err("EFI Capsule: driver not found for firmware type: %pUl, hardware instance: %lld\n",
+ &image->update_image_type_id,
+ image->update_hardware_instance);
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ /* do update */
+ image_binary = (void *)image + sizeof(*image);
+ vendor_code = image_binary + image->update_image_size;
+
+ abort_reason = NULL;
+ ret = EFI_CALL(fmp->set_image(fmp, image->update_image_index,
+ image_binary,
+ image->update_image_size,
+ vendor_code, NULL,
+ &abort_reason));
+ if (ret != EFI_SUCCESS) {
+ log_err("EFI Capsule: firmware update failed: %ls\n",
+ abort_reason);
+ efi_free_pool(abort_reason);
+ goto out;
+ }
+ }
+
+out:
+ efi_free_pool(handles);
+
+ return ret;
+}
+#else
+static efi_status_t efi_capsule_update_firmware(
+ struct efi_capsule_header *capsule_data)
+{
+ return EFI_UNSUPPORTED;
+}
+#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT */
+
+/**
+ * efi_update_capsule() - process information from operating system
+ * @capsule_header_array: Array of virtual address pointers
+ * @capsule_count: Number of pointers in capsule_header_array
+ * @scatter_gather_list: Array of physical address pointers
+ *
+ * This function implements the UpdateCapsule() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_update_capsule(
+ struct efi_capsule_header **capsule_header_array,
+ efi_uintn_t capsule_count,
+ u64 scatter_gather_list)
+{
+ struct efi_capsule_header *capsule;
+ unsigned int i;
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %zu, %llu\n", capsule_header_array, capsule_count,
+ scatter_gather_list);
+
+ if (!capsule_count) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = EFI_SUCCESS;
+ for (i = 0, capsule = *capsule_header_array; i < capsule_count;
+ i++, capsule = *(++capsule_header_array)) {
+ /* sanity check */
+ if (capsule->header_size < sizeof(*capsule) ||
+ capsule->capsule_image_size < sizeof(*capsule)) {
+ log_err("EFI: A capsule has not enough data\n");
+ continue;
+ }
+
+ log_debug("Capsule[%d] (guid:%pUl)\n",
+ i, &capsule->capsule_guid);
+ if (!guidcmp(&capsule->capsule_guid,
+ &efi_guid_firmware_management_capsule_id)) {
+ ret = efi_capsule_update_firmware(capsule);
+ } else {
+ log_err("EFI: not support capsule type: %pUl\n",
+ &capsule->capsule_guid);
+ ret = EFI_UNSUPPORTED;
+ }
+
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+ if (IS_ENABLED(CONFIG_EFI_ESRT)) {
+ /* Rebuild the ESRT to reflect any updated FW images. */
+ ret = efi_esrt_populate();
+ if (ret != EFI_SUCCESS)
+ log_warning("EFI Capsule: failed to update ESRT\n");
+ }
+out:
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_query_capsule_caps() - check if capsule is supported
+ * @capsule_header_array: Array of virtual pointers
+ * @capsule_count: Number of pointers in capsule_header_array
+ * @maximum_capsule_size: Maximum capsule size
+ * @reset_type: Type of reset needed for capsule update
+ *
+ * This function implements the QueryCapsuleCapabilities() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_query_capsule_caps(
+ struct efi_capsule_header **capsule_header_array,
+ efi_uintn_t capsule_count,
+ u64 *maximum_capsule_size,
+ u32 *reset_type)
+{
+ struct efi_capsule_header *capsule __attribute__((unused));
+ unsigned int i;
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %zu, %p, %p\n", capsule_header_array, capsule_count,
+ maximum_capsule_size, reset_type);
+
+ if (!maximum_capsule_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ *maximum_capsule_size = U64_MAX;
+ *reset_type = EFI_RESET_COLD;
+
+ ret = EFI_SUCCESS;
+ for (i = 0, capsule = *capsule_header_array; i < capsule_count;
+ i++, capsule = *(++capsule_header_array)) {
+ /* TODO */
+ }
+out:
+ return EFI_EXIT(ret);
+}
+
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK
+/**
+ * get_dp_device - retrieve a device path from boot variable
+ * @boot_var: Boot variable name
+ * @device_dp Device path
+ *
+ * Retrieve a device patch from boot variable, @boot_var.
+ *
+ * Return: status code
+ */
+static efi_status_t get_dp_device(u16 *boot_var,
+ struct efi_device_path **device_dp)
+{
+ void *buf = NULL;
+ efi_uintn_t size;
+ struct efi_load_option lo;
+ struct efi_device_path *file_dp;
+ efi_status_t ret;
+
+ size = 0;
+ ret = efi_get_variable_int(boot_var, &efi_global_variable_guid,
+ NULL, &size, NULL, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ buf = malloc(size);
+ if (!buf)
+ return EFI_OUT_OF_RESOURCES;
+ ret = efi_get_variable_int(boot_var, &efi_global_variable_guid,
+ NULL, &size, buf, NULL);
+ }
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ efi_deserialize_load_option(&lo, buf, &size);
+
+ if (lo.attributes & LOAD_OPTION_ACTIVE) {
+ efi_dp_split_file_path(lo.file_path, device_dp, &file_dp);
+ efi_free_pool(file_dp);
+
+ ret = EFI_SUCCESS;
+ } else {
+ ret = EFI_NOT_FOUND;
+ }
+
+ free(buf);
+
+ return ret;
+}
+
+/**
+ * device_is_present_and_system_part - check if a device exists
+ * @dp Device path
+ *
+ * Check if a device pointed to by the device path, @dp, exists and is
+ * located in UEFI system partition.
+ *
+ * Return: true - yes, false - no
+ */
+static bool device_is_present_and_system_part(struct efi_device_path *dp)
+{
+ efi_handle_t handle;
+
+ handle = efi_dp_find_obj(dp, NULL);
+ if (!handle)
+ return false;
+
+ return efi_disk_is_system_part(handle);
+}
+
+/**
+ * find_boot_device - identify the boot device
+ *
+ * Identify the boot device from boot-related variables as UEFI
+ * specification describes and put its handle into bootdev_root.
+ *
+ * Return: status code
+ */
+static efi_status_t find_boot_device(void)
+{
+ char boot_var[9];
+ u16 boot_var16[9], *p, bootnext, *boot_order = NULL;
+ efi_uintn_t size;
+ int i, num;
+ struct efi_simple_file_system_protocol *volume;
+ struct efi_device_path *boot_dev = NULL;
+ efi_status_t ret;
+
+ /* find active boot device in BootNext */
+ bootnext = 0;
+ size = sizeof(bootnext);
+ ret = efi_get_variable_int(L"BootNext",
+ (efi_guid_t *)&efi_global_variable_guid,
+ NULL, &size, &bootnext, NULL);
+ if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+ /* BootNext does exist here */
+ if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) {
+ log_err("BootNext must be 16-bit integer\n");
+ goto skip;
+ }
+ sprintf((char *)boot_var, "Boot%04X", bootnext);
+ p = boot_var16;
+ utf8_utf16_strcpy(&p, boot_var);
+
+ ret = get_dp_device(boot_var16, &boot_dev);
+ if (ret == EFI_SUCCESS) {
+ if (device_is_present_and_system_part(boot_dev)) {
+ goto out;
+ } else {
+ efi_free_pool(boot_dev);
+ boot_dev = NULL;
+ }
+ }
+ }
+
+skip:
+ /* find active boot device in BootOrder */
+ size = 0;
+ ret = efi_get_variable_int(L"BootOrder", &efi_global_variable_guid,
+ NULL, &size, NULL, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ boot_order = malloc(size);
+ if (!boot_order) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ ret = efi_get_variable_int(L"BootOrder",
+ &efi_global_variable_guid,
+ NULL, &size, boot_order, NULL);
+ }
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* check in higher order */
+ num = size / sizeof(u16);
+ for (i = 0; i < num; i++) {
+ sprintf((char *)boot_var, "Boot%04X", boot_order[i]);
+ p = boot_var16;
+ utf8_utf16_strcpy(&p, boot_var);
+ ret = get_dp_device(boot_var16, &boot_dev);
+ if (ret != EFI_SUCCESS)
+ continue;
+
+ if (device_is_present_and_system_part(boot_dev))
+ break;
+
+ efi_free_pool(boot_dev);
+ boot_dev = NULL;
+ }
+ if (boot_dev) {
+ u16 *path_str;
+
+ path_str = efi_dp_str(boot_dev);
+ log_debug("EFI Capsule: bootdev is %ls\n", path_str);
+ efi_free_pool(path_str);
+
+ volume = efi_fs_from_path(boot_dev);
+ if (!volume)
+ ret = EFI_DEVICE_ERROR;
+ else
+ ret = EFI_CALL(volume->open_volume(volume,
+ &bootdev_root));
+ efi_free_pool(boot_dev);
+ } else {
+ ret = EFI_NOT_FOUND;
+ }
+out:
+ free(boot_order);
+
+ return ret;
+}
+
+/**
+ * efi_capsule_scan_dir - traverse a capsule directory in boot device
+ * @files: Array of file names
+ * @num: Number of elements in @files
+ *
+ * Traverse a capsule directory in boot device.
+ * Called by initialization code, and returns an array of capsule file
+ * names in @files.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_capsule_scan_dir(u16 ***files, unsigned int *num)
+{
+ struct efi_file_handle *dirh;
+ struct efi_file_info *dirent;
+ efi_uintn_t dirent_size, tmp_size;
+ unsigned int count;
+ u16 **tmp_files;
+ efi_status_t ret;
+
+ ret = find_boot_device();
+ if (ret == EFI_NOT_FOUND) {
+ log_debug("EFI Capsule: bootdev is not set\n");
+ *num = 0;
+ return EFI_SUCCESS;
+ } else if (ret != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ /* count capsule files */
+ ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
+ EFI_CAPSULE_DIR,
+ EFI_FILE_MODE_READ, 0));
+ if (ret != EFI_SUCCESS) {
+ *num = 0;
+ return EFI_SUCCESS;
+ }
+
+ dirent_size = 256;
+ dirent = malloc(dirent_size);
+ if (!dirent)
+ return EFI_OUT_OF_RESOURCES;
+
+ count = 0;
+ while (1) {
+ tmp_size = dirent_size;
+ ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ struct efi_file_info *old_dirent = dirent;
+
+ dirent = realloc(dirent, tmp_size);
+ if (!dirent) {
+ dirent = old_dirent;
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+ dirent_size = tmp_size;
+ ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
+ }
+ if (ret != EFI_SUCCESS)
+ goto err;
+ if (!tmp_size)
+ break;
+
+ if (!(dirent->attribute & EFI_FILE_DIRECTORY))
+ count++;
+ }
+
+ ret = EFI_CALL((*dirh->setpos)(dirh, 0));
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ /* make a list */
+ tmp_files = malloc(count * sizeof(*tmp_files));
+ if (!tmp_files) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+
+ count = 0;
+ while (1) {
+ tmp_size = dirent_size;
+ ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent));
+ if (ret != EFI_SUCCESS)
+ goto err;
+ if (!tmp_size)
+ break;
+
+ if (!(dirent->attribute & EFI_FILE_DIRECTORY) &&
+ u16_strcmp(dirent->file_name, L".") &&
+ u16_strcmp(dirent->file_name, L".."))
+ tmp_files[count++] = u16_strdup(dirent->file_name);
+ }
+ /* ignore an error */
+ EFI_CALL((*dirh->close)(dirh));
+
+ /* in ascii order */
+ /* FIXME: u16 version of strcasecmp */
+ qsort(tmp_files, count, sizeof(*tmp_files),
+ (int (*)(const void *, const void *))strcasecmp);
+ *files = tmp_files;
+ *num = count;
+ ret = EFI_SUCCESS;
+err:
+ free(dirent);
+
+ return ret;
+}
+
+/**
+ * efi_capsule_read_file - read in a capsule file
+ * @filename: File name
+ * @capsule: Pointer to buffer for capsule
+ *
+ * Read a capsule file and put its content in @capsule.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_capsule_read_file(const u16 *filename,
+ struct efi_capsule_header **capsule)
+{
+ struct efi_file_handle *dirh, *fh;
+ struct efi_file_info *file_info = NULL;
+ struct efi_capsule_header *buf = NULL;
+ efi_uintn_t size;
+ efi_status_t ret;
+
+ ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
+ EFI_CAPSULE_DIR,
+ EFI_FILE_MODE_READ, 0));
+ if (ret != EFI_SUCCESS)
+ return ret;
+ ret = EFI_CALL((*dirh->open)(dirh, &fh, (u16 *)filename,
+ EFI_FILE_MODE_READ, 0));
+ /* ignore an error */
+ EFI_CALL((*dirh->close)(dirh));
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* file size */
+ size = 0;
+ ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
+ &size, file_info));
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ file_info = malloc(size);
+ if (!file_info) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+ ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid,
+ &size, file_info));
+ }
+ if (ret != EFI_SUCCESS)
+ goto err;
+ size = file_info->file_size;
+ free(file_info);
+ buf = malloc(size);
+ if (!buf) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+
+ /* fetch data */
+ ret = EFI_CALL((*fh->read)(fh, &size, buf));
+ if (ret == EFI_SUCCESS) {
+ if (size >= buf->capsule_image_size) {
+ *capsule = buf;
+ } else {
+ free(buf);
+ ret = EFI_INVALID_PARAMETER;
+ }
+ } else {
+ free(buf);
+ }
+err:
+ EFI_CALL((*fh->close)(fh));
+
+ return ret;
+}
+
+/**
+ * efi_capsule_delete_file - delete a capsule file
+ * @filename: File name
+ *
+ * Delete a capsule file from capsule directory.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_capsule_delete_file(const u16 *filename)
+{
+ struct efi_file_handle *dirh, *fh;
+ efi_status_t ret;
+
+ ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh,
+ EFI_CAPSULE_DIR,
+ EFI_FILE_MODE_READ, 0));
+ if (ret != EFI_SUCCESS)
+ return ret;
+ ret = EFI_CALL((*dirh->open)(dirh, &fh, (u16 *)filename,
+ EFI_FILE_MODE_READ, 0));
+ /* ignore an error */
+ EFI_CALL((*dirh->close)(dirh));
+
+ if (ret == EFI_SUCCESS)
+ ret = EFI_CALL((*fh->delete)(fh));
+
+ return ret;
+}
+
+/**
+ * efi_capsule_scan_done - reset a scan help function
+ *
+ * Reset a scan help function
+ */
+static void efi_capsule_scan_done(void)
+{
+ EFI_CALL((*bootdev_root->close)(bootdev_root));
+ bootdev_root = NULL;
+}
+
+/**
+ * efi_load_capsule_drivers - initialize capsule drivers
+ *
+ * Generic FMP drivers backed by DFU
+ *
+ * Return: status code
+ */
+efi_status_t __weak efi_load_capsule_drivers(void)
+{
+ __maybe_unused efi_handle_t handle;
+ efi_status_t ret = EFI_SUCCESS;
+
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_FIT)) {
+ handle = NULL;
+ ret = EFI_CALL(efi_install_multiple_protocol_interfaces(
+ &handle, &efi_guid_firmware_management_protocol,
+ &efi_fmp_fit, NULL));
+ }
+
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_RAW)) {
+ handle = NULL;
+ ret = EFI_CALL(efi_install_multiple_protocol_interfaces(
+ &handle,
+ &efi_guid_firmware_management_protocol,
+ &efi_fmp_raw, NULL));
+ }
+
+ return ret;
+}
+
+/**
+ * check_run_capsules - Check whether capsule update should run
+ *
+ * The spec says OsIndications must be set in order to run the capsule update
+ * on-disk. Since U-Boot doesn't support runtime SetVariable, allow capsules to
+ * run explicitly if CONFIG_EFI_IGNORE_OSINDICATIONS is selected
+ */
+static bool check_run_capsules(void)
+{
+ u64 os_indications;
+ efi_uintn_t size;
+ efi_status_t ret;
+
+ if (IS_ENABLED(CONFIG_EFI_IGNORE_OSINDICATIONS))
+ return true;
+
+ size = sizeof(os_indications);
+ ret = efi_get_variable_int(L"OsIndications", &efi_global_variable_guid,
+ NULL, &size, &os_indications, NULL);
+ if (ret == EFI_SUCCESS &&
+ (os_indications
+ & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED))
+ return true;
+
+ return false;
+}
+
+/**
+ * efi_launch_capsule - launch capsules
+ *
+ * Launch all the capsules in system at boot time.
+ * Called by efi init code
+ *
+ * Return: status codde
+ */
+efi_status_t efi_launch_capsules(void)
+{
+ struct efi_capsule_header *capsule = NULL;
+ u16 **files;
+ unsigned int nfiles, index, i;
+ u16 variable_name16[12];
+ efi_status_t ret;
+
+ if (!check_run_capsules())
+ return EFI_SUCCESS;
+
+ index = get_last_capsule();
+
+ /*
+ * Find capsules on disk.
+ * All the capsules are collected at the beginning because
+ * capsule files will be removed instantly.
+ */
+ nfiles = 0;
+ files = NULL;
+ ret = efi_capsule_scan_dir(&files, &nfiles);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (!nfiles)
+ return EFI_SUCCESS;
+
+ /* Launch capsules */
+ for (i = 0, ++index; i < nfiles; i++, index++) {
+ log_debug("capsule from %ls ...\n", files[i]);
+ if (index > 0xffff)
+ index = 0;
+ ret = efi_capsule_read_file(files[i], &capsule);
+ if (ret == EFI_SUCCESS) {
+ ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0));
+ if (ret != EFI_SUCCESS)
+ log_err("EFI Capsule update failed at %ls\n",
+ files[i]);
+
+ free(capsule);
+ } else {
+ log_err("EFI: reading capsule failed: %ls\n", files[i]);
+ }
+ /* create CapsuleXXXX */
+ set_capsule_result(index, capsule, ret);
+
+ /* delete a capsule either in case of success or failure */
+ ret = efi_capsule_delete_file(files[i]);
+ if (ret != EFI_SUCCESS)
+ log_err("EFI: deleting a capsule file failed: %ls\n",
+ files[i]);
+ }
+ efi_capsule_scan_done();
+
+ for (i = 0; i < nfiles; i++)
+ free(files[i]);
+ free(files);
+
+ /* CapsuleLast */
+ efi_create_indexed_name(variable_name16, sizeof(variable_name16),
+ "Capsule", index - 1);
+ efi_set_variable_int(L"CapsuleLast", &efi_guid_capsule_report,
+ EFI_VARIABLE_READ_ONLY |
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ 22, variable_name16, false);
+
+ return ret;
+}
+#endif /* CONFIG_EFI_CAPSULE_ON_DISK */
diff --git a/roms/u-boot/lib/efi_loader/efi_console.c b/roms/u-boot/lib/efi_loader/efi_console.c
new file mode 100644
index 000000000..3b012e1a6
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_console.c
@@ -0,0 +1,1316 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application console interface
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <malloc.h>
+#include <time.h>
+#include <dm/device.h>
+#include <efi_loader.h>
+#include <env.h>
+#include <stdio_dev.h>
+#include <video_console.h>
+#include <linux/delay.h>
+
+#define EFI_COUT_MODE_2 2
+#define EFI_MAX_COUT_MODE 3
+
+struct cout_mode {
+ unsigned long columns;
+ unsigned long rows;
+ int present;
+};
+
+static struct cout_mode efi_cout_modes[] = {
+ /* EFI Mode 0 is 80x25 and always present */
+ {
+ .columns = 80,
+ .rows = 25,
+ .present = 1,
+ },
+ /* EFI Mode 1 is always 80x50 */
+ {
+ .columns = 80,
+ .rows = 50,
+ .present = 0,
+ },
+ /* Value are unknown until we query the console */
+ {
+ .columns = 0,
+ .rows = 0,
+ .present = 0,
+ },
+};
+
+const efi_guid_t efi_guid_text_input_ex_protocol =
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
+const efi_guid_t efi_guid_text_input_protocol =
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
+const efi_guid_t efi_guid_text_output_protocol =
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
+
+#define cESC '\x1b'
+#define ESC "\x1b"
+
+/* Default to mode 0 */
+static struct simple_text_output_mode efi_con_mode = {
+ .max_mode = 1,
+ .mode = 0,
+ .attribute = 0,
+ .cursor_column = 0,
+ .cursor_row = 0,
+ .cursor_visible = 1,
+};
+
+static int term_get_char(s32 *c)
+{
+ u64 timeout;
+
+ /* Wait up to 100 ms for a character */
+ timeout = timer_get_us() + 100000;
+
+ while (!tstc())
+ if (timer_get_us() > timeout)
+ return 1;
+
+ *c = getchar();
+ return 0;
+}
+
+/**
+ * Receive and parse a reply from the terminal.
+ *
+ * @n: array of return values
+ * @num: number of return values expected
+ * @end_char: character indicating end of terminal message
+ * Return: non-zero indicates error
+ */
+static int term_read_reply(int *n, int num, char end_char)
+{
+ s32 c;
+ int i = 0;
+
+ if (term_get_char(&c) || c != cESC)
+ return -1;
+
+ if (term_get_char(&c) || c != '[')
+ return -1;
+
+ n[0] = 0;
+ while (1) {
+ if (!term_get_char(&c)) {
+ if (c == ';') {
+ i++;
+ if (i >= num)
+ return -1;
+ n[i] = 0;
+ continue;
+ } else if (c == end_char) {
+ break;
+ } else if (c > '9' || c < '0') {
+ return -1;
+ }
+
+ /* Read one more decimal position */
+ n[i] *= 10;
+ n[i] += c - '0';
+ } else {
+ return -1;
+ }
+ }
+ if (i != num - 1)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * efi_cout_output_string() - write Unicode string to console
+ *
+ * This function implements the OutputString service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: simple text output protocol
+ * @string: u16 string
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_output_string(
+ struct efi_simple_text_output_protocol *this,
+ const u16 *string)
+{
+ struct simple_text_output_mode *con = &efi_con_mode;
+ struct cout_mode *mode = &efi_cout_modes[con->mode];
+ char *buf, *pos;
+ const u16 *p;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, string);
+
+ if (!this || !string) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ buf = malloc(utf16_utf8_strlen(string) + 1);
+ if (!buf) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ pos = buf;
+ utf16_utf8_strcpy(&pos, string);
+ fputs(stdout, buf);
+ free(buf);
+
+ /*
+ * Update the cursor position.
+ *
+ * The UEFI spec provides advance rules for U+0000, U+0008, U+000A,
+ * and U000D. All other control characters are ignored. Any non-control
+ * character increase the column by one.
+ */
+ for (p = string; *p; ++p) {
+ switch (*p) {
+ case '\b': /* U+0008, backspace */
+ if (con->cursor_column)
+ con->cursor_column--;
+ break;
+ case '\n': /* U+000A, newline */
+ con->cursor_column = 0;
+ con->cursor_row++;
+ break;
+ case '\r': /* U+000D, carriage-return */
+ con->cursor_column = 0;
+ break;
+ case 0xd800 ... 0xdbff:
+ /*
+ * Ignore high surrogates, we do not want to count a
+ * Unicode character twice.
+ */
+ break;
+ default:
+ /* Exclude control codes */
+ if (*p > 0x1f)
+ con->cursor_column++;
+ break;
+ }
+ if (con->cursor_column >= mode->columns) {
+ con->cursor_column = 0;
+ con->cursor_row++;
+ }
+ /*
+ * When we exceed the row count the terminal will scroll up one
+ * line. We have to adjust the cursor position.
+ */
+ if (con->cursor_row >= mode->rows && con->cursor_row)
+ con->cursor_row--;
+ }
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cout_test_string() - test writing Unicode string to console
+ *
+ * This function implements the TestString service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * As in OutputString we simply convert UTF-16 to UTF-8 there are no unsupported
+ * code points and we can always return EFI_SUCCESS.
+ *
+ * @this: simple text output protocol
+ * @string: u16 string
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_test_string(
+ struct efi_simple_text_output_protocol *this,
+ const u16 *string)
+{
+ EFI_ENTRY("%p, %p", this, string);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * cout_mode_matches() - check if mode has given terminal size
+ *
+ * @mode: text mode
+ * @rows: number of rows
+ * @cols: number of columns
+ * Return: true if number of rows and columns matches the mode and
+ * the mode is present
+ */
+static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
+{
+ if (!mode->present)
+ return false;
+
+ return (mode->rows == rows) && (mode->columns == cols);
+}
+
+/**
+ * query_console_serial() - query serial console size
+ *
+ * When using a serial console or the net console we can only devise the
+ * terminal size by querying the terminal using ECMA-48 control sequences.
+ *
+ * @rows: pointer to return number of rows
+ * @cols: pointer to return number of columns
+ * Returns: 0 on success
+ */
+static int query_console_serial(int *rows, int *cols)
+{
+ int ret = 0;
+ int n[2];
+
+ /* Empty input buffer */
+ while (tstc())
+ getchar();
+
+ /*
+ * Not all terminals understand CSI [18t for querying the console size.
+ * We should adhere to escape sequences documented in the console_codes
+ * man page and the ECMA-48 standard.
+ *
+ * So here we follow a different approach. We position the cursor to the
+ * bottom right and query its position. Before leaving the function we
+ * restore the original cursor position.
+ */
+ printf(ESC "7" /* Save cursor position */
+ ESC "[r" /* Set scrolling region to full window */
+ ESC "[999;999H" /* Move to bottom right corner */
+ ESC "[6n"); /* Query cursor position */
+
+ /* Read {rows,cols} */
+ if (term_read_reply(n, 2, 'R')) {
+ ret = 1;
+ goto out;
+ }
+
+ *cols = n[1];
+ *rows = n[0];
+out:
+ printf(ESC "8"); /* Restore cursor position */
+ return ret;
+}
+
+/**
+ * query_vidconsole() - query video console size
+ *
+ *
+ * @rows: pointer to return number of rows
+ * @cols: pointer to return number of columns
+ * Returns: 0 on success
+ */
+static int __maybe_unused query_vidconsole(int *rows, int *cols)
+{
+ const char *stdout_name = env_get("stdout");
+ struct stdio_dev *stdout_dev;
+ struct udevice *dev;
+ struct vidconsole_priv *priv;
+
+ if (!stdout_name || strncmp(stdout_name, "vidconsole", 10))
+ return -ENODEV;
+ stdout_dev = stdio_get_by_name("vidconsole");
+ if (!stdout_dev)
+ return -ENODEV;
+ dev = stdout_dev->priv;
+ if (!dev)
+ return -ENODEV;
+ priv = dev_get_uclass_priv(dev);
+ if (!priv)
+ return -ENODEV;
+ *rows = priv->rows;
+ *cols = priv->cols;
+ return 0;
+}
+
+/**
+ * query_console_size() - update the mode table.
+ *
+ * By default the only mode available is 80x25. If the console has at least 50
+ * lines, enable mode 80x50. If we can query the console size and it is neither
+ * 80x25 nor 80x50, set it as an additional mode.
+ */
+static void query_console_size(void)
+{
+ int rows = 25, cols = 80;
+ int ret = -ENODEV;
+
+ if (IS_ENABLED(CONFIG_DM_VIDEO))
+ ret = query_vidconsole(&rows, &cols);
+ if (ret)
+ ret = query_console_serial(&rows, &cols);
+ if (ret)
+ return;
+
+ /* Test if we can have Mode 1 */
+ if (cols >= 80 && rows >= 50) {
+ efi_cout_modes[1].present = 1;
+ efi_con_mode.max_mode = 2;
+ }
+
+ /*
+ * Install our mode as mode 2 if it is different
+ * than mode 0 or 1 and set it as the currently selected mode
+ */
+ if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) &&
+ !cout_mode_matches(&efi_cout_modes[1], rows, cols)) {
+ efi_cout_modes[EFI_COUT_MODE_2].columns = cols;
+ efi_cout_modes[EFI_COUT_MODE_2].rows = rows;
+ efi_cout_modes[EFI_COUT_MODE_2].present = 1;
+ efi_con_mode.max_mode = EFI_MAX_COUT_MODE;
+ efi_con_mode.mode = EFI_COUT_MODE_2;
+ }
+}
+
+
+/**
+ * efi_cout_query_mode() - get terminal size for a text mode
+ *
+ * This function implements the QueryMode service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: simple text output protocol
+ * @mode_number: mode number to retrieve information on
+ * @columns: number of columns
+ * @rows: number of rows
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_query_mode(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long mode_number, unsigned long *columns,
+ unsigned long *rows)
+{
+ EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
+
+ if (mode_number >= efi_con_mode.max_mode)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ if (efi_cout_modes[mode_number].present != 1)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ if (columns)
+ *columns = efi_cout_modes[mode_number].columns;
+ if (rows)
+ *rows = efi_cout_modes[mode_number].rows;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static const struct {
+ unsigned int fg;
+ unsigned int bg;
+} color[] = {
+ { 30, 40 }, /* 0: black */
+ { 34, 44 }, /* 1: blue */
+ { 32, 42 }, /* 2: green */
+ { 36, 46 }, /* 3: cyan */
+ { 31, 41 }, /* 4: red */
+ { 35, 45 }, /* 5: magenta */
+ { 33, 43 }, /* 6: brown, map to yellow as EDK2 does*/
+ { 37, 47 }, /* 7: light gray, map to white */
+};
+
+/**
+ * efi_cout_set_attribute() - set fore- and background color
+ *
+ * This function implements the SetAttribute service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: simple text output protocol
+ * @attribute: foreground color - bits 0-3, background color - bits 4-6
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_set_attribute(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long attribute)
+{
+ unsigned int bold = EFI_ATTR_BOLD(attribute);
+ unsigned int fg = EFI_ATTR_FG(attribute);
+ unsigned int bg = EFI_ATTR_BG(attribute);
+
+ EFI_ENTRY("%p, %lx", this, attribute);
+
+ efi_con_mode.attribute = attribute;
+ if (attribute)
+ printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg);
+ else
+ printf(ESC"[0;37;40m");
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_cout_clear_screen() - clear screen
+ *
+ * This function implements the ClearScreen service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: pointer to the protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_clear_screen(
+ struct efi_simple_text_output_protocol *this)
+{
+ EFI_ENTRY("%p", this);
+
+ /*
+ * The Linux console wants both a clear and a home command. The video
+ * uclass does not support <ESC>[H without coordinates, yet.
+ */
+ printf(ESC "[2J" ESC "[1;1H");
+ efi_con_mode.cursor_column = 0;
+ efi_con_mode.cursor_row = 0;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_cout_clear_set_mode() - set text model
+ *
+ * This function implements the SetMode service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: pointer to the protocol instance
+ * @mode_number: number of the text mode to set
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_set_mode(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long mode_number)
+{
+ EFI_ENTRY("%p, %ld", this, mode_number);
+
+ if (mode_number >= efi_con_mode.max_mode)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ if (!efi_cout_modes[mode_number].present)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ efi_con_mode.mode = mode_number;
+ EFI_CALL(efi_cout_clear_screen(this));
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_cout_reset() - reset the terminal
+ *
+ * This function implements the Reset service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: pointer to the protocol instance
+ * @extended_verification: if set an extended verification may be executed
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_reset(
+ struct efi_simple_text_output_protocol *this,
+ char extended_verification)
+{
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Clear screen */
+ EFI_CALL(efi_cout_clear_screen(this));
+ /* Set default colors */
+ efi_con_mode.attribute = 0x07;
+ printf(ESC "[0;37;40m");
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_cout_set_cursor_position() - reset the terminal
+ *
+ * This function implements the SetCursorPosition service of the simple text
+ * output protocol. See the Unified Extensible Firmware Interface (UEFI)
+ * specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @column: column to move to
+ * @row: row to move to
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_set_cursor_position(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long column, unsigned long row)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct simple_text_output_mode *con = &efi_con_mode;
+ struct cout_mode *mode = &efi_cout_modes[con->mode];
+
+ EFI_ENTRY("%p, %ld, %ld", this, column, row);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (row >= mode->rows || column >= mode->columns) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ /*
+ * Set cursor position by sending CSI H.
+ * EFI origin is [0, 0], terminal origin is [1, 1].
+ */
+ printf(ESC "[%d;%dH", (int)row + 1, (int)column + 1);
+ efi_con_mode.cursor_column = column;
+ efi_con_mode.cursor_row = row;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cout_enable_cursor() - enable the cursor
+ *
+ * This function implements the EnableCursor service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: pointer to the protocol instance
+ * @enable: if true enable, if false disable the cursor
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_enable_cursor(
+ struct efi_simple_text_output_protocol *this,
+ bool enable)
+{
+ EFI_ENTRY("%p, %d", this, enable);
+
+ printf(ESC"[?25%c", enable ? 'h' : 'l');
+ efi_con_mode.cursor_visible = !!enable;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+struct efi_simple_text_output_protocol efi_con_out = {
+ .reset = efi_cout_reset,
+ .output_string = efi_cout_output_string,
+ .test_string = efi_cout_test_string,
+ .query_mode = efi_cout_query_mode,
+ .set_mode = efi_cout_set_mode,
+ .set_attribute = efi_cout_set_attribute,
+ .clear_screen = efi_cout_clear_screen,
+ .set_cursor_position = efi_cout_set_cursor_position,
+ .enable_cursor = efi_cout_enable_cursor,
+ .mode = (void*)&efi_con_mode,
+};
+
+/**
+ * struct efi_cin_notify_function - registered console input notify function
+ *
+ * @link: link to list
+ * @key: key to notify
+ * @function: function to call
+ */
+struct efi_cin_notify_function {
+ struct list_head link;
+ struct efi_key_data key;
+ efi_status_t (EFIAPI *function)
+ (struct efi_key_data *key_data);
+};
+
+static bool key_available;
+static struct efi_key_data next_key;
+static LIST_HEAD(cin_notify_functions);
+
+/**
+ * set_shift_mask() - set shift mask
+ *
+ * @mod: Xterm shift mask
+ * @key_state: receives the state of the shift, alt, control, and logo keys
+ */
+void set_shift_mask(int mod, struct efi_key_state *key_state)
+{
+ key_state->key_shift_state = EFI_SHIFT_STATE_VALID;
+ if (mod) {
+ --mod;
+ if (mod & 1)
+ key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED;
+ if (mod & 2)
+ key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED;
+ if (mod & 4)
+ key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED;
+ if (!mod || (mod & 8))
+ key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED;
+ }
+}
+
+/**
+ * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys
+ *
+ * This gets called when we have already parsed CSI.
+ *
+ * @key_state: receives the state of the shift, alt, control, and logo keys
+ * Return: the unmodified code
+ */
+static int analyze_modifiers(struct efi_key_state *key_state)
+{
+ int c, mod = 0, ret = 0;
+
+ c = getchar();
+
+ if (c != ';') {
+ ret = c;
+ if (c == '~')
+ goto out;
+ c = getchar();
+ }
+ for (;;) {
+ switch (c) {
+ case '0'...'9':
+ mod *= 10;
+ mod += c - '0';
+ /* fall through */
+ case ';':
+ c = getchar();
+ break;
+ default:
+ goto out;
+ }
+ }
+out:
+ set_shift_mask(mod, key_state);
+ if (!ret)
+ ret = c;
+ return ret;
+}
+
+/**
+ * efi_cin_read_key() - read a key from the console input
+ *
+ * @key: - key received
+ * Return: - status code
+ */
+static efi_status_t efi_cin_read_key(struct efi_key_data *key)
+{
+ struct efi_input_key pressed_key = {
+ .scan_code = 0,
+ .unicode_char = 0,
+ };
+ s32 ch;
+
+ if (console_read_unicode(&ch))
+ return EFI_NOT_READY;
+
+ key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID;
+ key->key_state.key_toggle_state = EFI_TOGGLE_STATE_INVALID;
+
+ /* We do not support multi-word codes */
+ if (ch >= 0x10000)
+ ch = '?';
+
+ switch (ch) {
+ case 0x1b:
+ /*
+ * If a second key is received within 10 ms, assume that we are
+ * dealing with an escape sequence. Otherwise consider this the
+ * escape key being hit. 10 ms is long enough to work fine at
+ * 1200 baud and above.
+ */
+ udelay(10000);
+ if (!tstc()) {
+ pressed_key.scan_code = 23;
+ break;
+ }
+ /*
+ * Xterm Control Sequences
+ * https://www.xfree86.org/4.8.0/ctlseqs.html
+ */
+ ch = getchar();
+ switch (ch) {
+ case cESC: /* ESC */
+ pressed_key.scan_code = 23;
+ break;
+ case 'O': /* F1 - F4, End */
+ ch = getchar();
+ /* consider modifiers */
+ if (ch == 'F') { /* End */
+ pressed_key.scan_code = 6;
+ break;
+ } else if (ch < 'P') {
+ set_shift_mask(ch - '0', &key->key_state);
+ ch = getchar();
+ }
+ pressed_key.scan_code = ch - 'P' + 11;
+ break;
+ case '[':
+ ch = getchar();
+ switch (ch) {
+ case 'A'...'D': /* up, down right, left */
+ pressed_key.scan_code = ch - 'A' + 1;
+ break;
+ case 'F': /* End */
+ pressed_key.scan_code = 6;
+ break;
+ case 'H': /* Home */
+ pressed_key.scan_code = 5;
+ break;
+ case '1':
+ ch = analyze_modifiers(&key->key_state);
+ switch (ch) {
+ case '1'...'5': /* F1 - F5 */
+ pressed_key.scan_code = ch - '1' + 11;
+ break;
+ case '6'...'9': /* F5 - F8 */
+ pressed_key.scan_code = ch - '6' + 15;
+ break;
+ case 'A'...'D': /* up, down right, left */
+ pressed_key.scan_code = ch - 'A' + 1;
+ break;
+ case 'F': /* End */
+ pressed_key.scan_code = 6;
+ break;
+ case 'H': /* Home */
+ pressed_key.scan_code = 5;
+ break;
+ case '~': /* Home */
+ pressed_key.scan_code = 5;
+ break;
+ }
+ break;
+ case '2':
+ ch = analyze_modifiers(&key->key_state);
+ switch (ch) {
+ case '0'...'1': /* F9 - F10 */
+ pressed_key.scan_code = ch - '0' + 19;
+ break;
+ case '3'...'4': /* F11 - F12 */
+ pressed_key.scan_code = ch - '3' + 21;
+ break;
+ case '~': /* INS */
+ pressed_key.scan_code = 7;
+ break;
+ }
+ break;
+ case '3': /* DEL */
+ pressed_key.scan_code = 8;
+ analyze_modifiers(&key->key_state);
+ break;
+ case '5': /* PG UP */
+ pressed_key.scan_code = 9;
+ analyze_modifiers(&key->key_state);
+ break;
+ case '6': /* PG DOWN */
+ pressed_key.scan_code = 10;
+ analyze_modifiers(&key->key_state);
+ break;
+ } /* [ */
+ break;
+ default:
+ /* ALT key */
+ set_shift_mask(3, &key->key_state);
+ }
+ break;
+ case 0x7f:
+ /* Backspace */
+ ch = 0x08;
+ }
+ if (pressed_key.scan_code) {
+ key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID;
+ } else {
+ pressed_key.unicode_char = ch;
+
+ /*
+ * Assume left control key for control characters typically
+ * entered using the control key.
+ */
+ if (ch >= 0x01 && ch <= 0x1f) {
+ key->key_state.key_shift_state |=
+ EFI_SHIFT_STATE_VALID;
+ switch (ch) {
+ case 0x01 ... 0x07:
+ case 0x0b ... 0x0c:
+ case 0x0e ... 0x1f:
+ key->key_state.key_shift_state |=
+ EFI_LEFT_CONTROL_PRESSED;
+ }
+ }
+ }
+ key->key = pressed_key;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_cin_notify() - notify registered functions
+ */
+static void efi_cin_notify(void)
+{
+ struct efi_cin_notify_function *item;
+
+ list_for_each_entry(item, &cin_notify_functions, link) {
+ bool match = true;
+
+ /* We do not support toggle states */
+ if (item->key.key.unicode_char || item->key.key.scan_code) {
+ if (item->key.key.unicode_char !=
+ next_key.key.unicode_char ||
+ item->key.key.scan_code != next_key.key.scan_code)
+ match = false;
+ }
+ if (item->key.key_state.key_shift_state &&
+ item->key.key_state.key_shift_state !=
+ next_key.key_state.key_shift_state)
+ match = false;
+
+ if (match)
+ /* We don't bother about the return code */
+ EFI_CALL(item->function(&next_key));
+ }
+}
+
+/**
+ * efi_cin_check() - check if keyboard input is available
+ */
+static void efi_cin_check(void)
+{
+ efi_status_t ret;
+
+ if (key_available) {
+ efi_signal_event(efi_con_in.wait_for_key);
+ return;
+ }
+
+ if (tstc()) {
+ ret = efi_cin_read_key(&next_key);
+ if (ret == EFI_SUCCESS) {
+ key_available = true;
+
+ /* Notify registered functions */
+ efi_cin_notify();
+
+ /* Queue the wait for key event */
+ if (key_available)
+ efi_signal_event(efi_con_in.wait_for_key);
+ }
+ }
+}
+
+/**
+ * efi_cin_empty_buffer() - empty input buffer
+ */
+static void efi_cin_empty_buffer(void)
+{
+ while (tstc())
+ getchar();
+ key_available = false;
+}
+
+/**
+ * efi_cin_reset_ex() - reset console input
+ *
+ * @this: - the extended simple text input protocol
+ * @extended_verification: - extended verification
+ *
+ * This function implements the reset service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: old value of the task priority level
+ */
+static efi_status_t EFIAPI efi_cin_reset_ex(
+ struct efi_simple_text_input_ex_protocol *this,
+ bool extended_verification)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efi_cin_empty_buffer();
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_read_key_stroke_ex() - read key stroke
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data: key read from console
+ * Return: status code
+ *
+ * This function implements the ReadKeyStrokeEx service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_read_key_stroke_ex(
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, key_data);
+
+ /* Check parameters */
+ if (!this || !key_data) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ /* Enable console input after ExitBootServices */
+ efi_cin_check();
+
+ if (!key_available) {
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ /*
+ * CTRL+A - CTRL+Z have to be signaled as a - z.
+ * SHIFT+CTRL+A - SHIFT+CTRL+Z have to be signaled as A - Z.
+ */
+ switch (next_key.key.unicode_char) {
+ case 0x01 ... 0x07:
+ case 0x0b ... 0x0c:
+ case 0x0e ... 0x1a:
+ if (!(next_key.key_state.key_toggle_state &
+ EFI_CAPS_LOCK_ACTIVE) ^
+ !(next_key.key_state.key_shift_state &
+ (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)))
+ next_key.key.unicode_char += 0x40;
+ else
+ next_key.key.unicode_char += 0x60;
+ }
+ *key_data = next_key;
+ key_available = false;
+ efi_con_in.wait_for_key->is_signaled = false;
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_set_state() - set toggle key state
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_toggle_state: pointer to key toggle state
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_set_state(
+ struct efi_simple_text_input_ex_protocol *this,
+ u8 *key_toggle_state)
+{
+ EFI_ENTRY("%p, %p", this, key_toggle_state);
+ /*
+ * U-Boot supports multiple console input sources like serial and
+ * net console for which a key toggle state cannot be set at all.
+ *
+ * According to the UEFI specification it is allowable to not implement
+ * this service.
+ */
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_cin_register_key_notify() - register key notification function
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data: key to be notified
+ * @key_notify_function: function to be called if the key is pressed
+ * @notify_handle: handle for unregistering the notification
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_register_key_notify(
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data,
+ efi_status_t (EFIAPI *key_notify_function)(
+ struct efi_key_data *key_data),
+ void **notify_handle)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct efi_cin_notify_function *notify_function;
+
+ EFI_ENTRY("%p, %p, %p, %p",
+ this, key_data, key_notify_function, notify_handle);
+
+ /* Check parameters */
+ if (!this || !key_data || !key_notify_function || !notify_handle) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ EFI_PRINT("u+%04x, sc %04x, sh %08x, tg %02x\n",
+ key_data->key.unicode_char,
+ key_data->key.scan_code,
+ key_data->key_state.key_shift_state,
+ key_data->key_state.key_toggle_state);
+
+ notify_function = calloc(1, sizeof(struct efi_cin_notify_function));
+ if (!notify_function) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ notify_function->key = *key_data;
+ notify_function->function = key_notify_function;
+ list_add_tail(&notify_function->link, &cin_notify_functions);
+ *notify_handle = notify_function;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_unregister_key_notify() - unregister key notification function
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @notification_handle: handle received when registering
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_unregister_key_notify(
+ struct efi_simple_text_input_ex_protocol *this,
+ void *notification_handle)
+{
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+ struct efi_cin_notify_function *item, *notify_function =
+ notification_handle;
+
+ EFI_ENTRY("%p, %p", this, notification_handle);
+
+ /* Check parameters */
+ if (!this || !notification_handle)
+ goto out;
+
+ list_for_each_entry(item, &cin_notify_functions, link) {
+ if (item == notify_function) {
+ ret = EFI_SUCCESS;
+ break;
+ }
+ }
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Remove the notify function */
+ list_del(&notify_function->link);
+ free(notify_function);
+out:
+ return EFI_EXIT(ret);
+}
+
+
+/**
+ * efi_cin_reset() - drain the input buffer
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @extended_verification: allow for exhaustive verification
+ * Return: status code
+ *
+ * This function implements the Reset service of the
+ * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_reset
+ (struct efi_simple_text_input_protocol *this,
+ bool extended_verification)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efi_cin_empty_buffer();
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_read_key_stroke() - read key stroke
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key: key read from console
+ * Return: status code
+ *
+ * This function implements the ReadKeyStroke service of the
+ * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_read_key_stroke
+ (struct efi_simple_text_input_protocol *this,
+ struct efi_input_key *key)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, key);
+
+ /* Check parameters */
+ if (!this || !key) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ /* Enable console input after ExitBootServices */
+ efi_cin_check();
+
+ if (!key_available) {
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ *key = next_key.key;
+ key_available = false;
+ efi_con_in.wait_for_key->is_signaled = false;
+out:
+ return EFI_EXIT(ret);
+}
+
+static struct efi_simple_text_input_ex_protocol efi_con_in_ex = {
+ .reset = efi_cin_reset_ex,
+ .read_key_stroke_ex = efi_cin_read_key_stroke_ex,
+ .wait_for_key_ex = NULL,
+ .set_state = efi_cin_set_state,
+ .register_key_notify = efi_cin_register_key_notify,
+ .unregister_key_notify = efi_cin_unregister_key_notify,
+};
+
+struct efi_simple_text_input_protocol efi_con_in = {
+ .reset = efi_cin_reset,
+ .read_key_stroke = efi_cin_read_key_stroke,
+ .wait_for_key = NULL,
+};
+
+static struct efi_event *console_timer_event;
+
+/*
+ * efi_console_timer_notify() - notify the console timer event
+ *
+ * @event: console timer event
+ * @context: not used
+ */
+static void EFIAPI efi_console_timer_notify(struct efi_event *event,
+ void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+ efi_cin_check();
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_key_notify() - notify the wait for key event
+ *
+ * @event: wait for key event
+ * @context: not used
+ */
+static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+ efi_cin_check();
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_console_register() - install the console protocols
+ *
+ * This function is called from do_bootefi_exec().
+ *
+ * Return: status code
+ */
+efi_status_t efi_console_register(void)
+{
+ efi_status_t r;
+ efi_handle_t console_output_handle;
+ efi_handle_t console_input_handle;
+
+ /* Set up mode information */
+ query_console_size();
+
+ /* Create handles */
+ r = efi_create_handle(&console_output_handle);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+
+ r = efi_add_protocol(console_output_handle,
+ &efi_guid_text_output_protocol, &efi_con_out);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+ systab.con_out_handle = console_output_handle;
+ systab.stderr_handle = console_output_handle;
+
+ r = efi_create_handle(&console_input_handle);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+
+ r = efi_add_protocol(console_input_handle,
+ &efi_guid_text_input_protocol, &efi_con_in);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+ systab.con_in_handle = console_input_handle;
+ r = efi_add_protocol(console_input_handle,
+ &efi_guid_text_input_ex_protocol, &efi_con_in_ex);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+
+ /* Create console events */
+ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
+ NULL, NULL, &efi_con_in.wait_for_key);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register WaitForKey event\n");
+ return r;
+ }
+ efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key;
+ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ efi_console_timer_notify, NULL, NULL,
+ &console_timer_event);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register console event\n");
+ return r;
+ }
+ /* 5000 ns cycle is sufficient for 2 MBaud */
+ r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50);
+ if (r != EFI_SUCCESS)
+ printf("ERROR: Failed to set console timer\n");
+ return r;
+out_of_memory:
+ printf("ERROR: Out of memory\n");
+ return r;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_device_path.c b/roms/u-boot/lib/efi_loader/efi_device_path.c
new file mode 100644
index 000000000..76c2f82fe
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_device_path.c
@@ -0,0 +1,1290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI device path from u-boot device-model mapping
+ *
+ * (C) Copyright 2017 Rob Clark
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <log.h>
+#include <net.h>
+#include <usb.h>
+#include <mmc.h>
+#include <nvme.h>
+#include <efi_loader.h>
+#include <part.h>
+#include <sandboxblockdev.h>
+#include <asm-generic/unaligned.h>
+#include <linux/compat.h> /* U16_MAX */
+
+#ifdef CONFIG_SANDBOX
+const efi_guid_t efi_guid_host_dev = U_BOOT_HOST_DEV_GUID;
+#endif
+#ifdef CONFIG_VIRTIO_BLK
+const efi_guid_t efi_guid_virtio_dev = U_BOOT_VIRTIO_DEV_GUID;
+#endif
+
+/* template END node: */
+static const struct efi_device_path END = {
+ .type = DEVICE_PATH_TYPE_END,
+ .sub_type = DEVICE_PATH_SUB_TYPE_END,
+ .length = sizeof(END),
+};
+
+/* template ROOT node: */
+static const struct efi_device_path_vendor ROOT = {
+ .dp = {
+ .type = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
+ .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
+ .length = sizeof(ROOT),
+ },
+ .guid = U_BOOT_GUID,
+};
+
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+/*
+ * Determine if an MMC device is an SD card.
+ *
+ * @desc block device descriptor
+ * @return true if the device is an SD card
+ */
+static bool is_sd(struct blk_desc *desc)
+{
+ struct mmc *mmc = find_mmc_device(desc->devnum);
+
+ if (!mmc)
+ return false;
+
+ return IS_SD(mmc) != 0U;
+}
+#endif
+
+static void *dp_alloc(size_t sz)
+{
+ void *buf;
+
+ if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) !=
+ EFI_SUCCESS) {
+ debug("EFI: ERROR: out of memory in %s\n", __func__);
+ return NULL;
+ }
+
+ memset(buf, 0, sz);
+ return buf;
+}
+
+/*
+ * Iterate to next block in device-path, terminating (returning NULL)
+ * at /End* node.
+ */
+struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
+{
+ if (dp == NULL)
+ return NULL;
+ if (dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ dp = ((void *)dp) + dp->length;
+ if (dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ return (struct efi_device_path *)dp;
+}
+
+/*
+ * Compare two device-paths, stopping when the shorter of the two hits
+ * an End* node. This is useful to, for example, compare a device-path
+ * representing a device with one representing a file on the device, or
+ * a device with a parent device.
+ */
+int efi_dp_match(const struct efi_device_path *a,
+ const struct efi_device_path *b)
+{
+ while (1) {
+ int ret;
+
+ ret = memcmp(&a->length, &b->length, sizeof(a->length));
+ if (ret)
+ return ret;
+
+ ret = memcmp(a, b, a->length);
+ if (ret)
+ return ret;
+
+ a = efi_dp_next(a);
+ b = efi_dp_next(b);
+
+ if (!a || !b)
+ return 0;
+ }
+}
+
+/*
+ * We can have device paths that start with a USB WWID or a USB Class node,
+ * and a few other cases which don't encode the full device path with bus
+ * hierarchy:
+ *
+ * - MESSAGING:USB_WWID
+ * - MESSAGING:USB_CLASS
+ * - MEDIA:FILE_PATH
+ * - MEDIA:HARD_DRIVE
+ * - MESSAGING:URI
+ *
+ * See UEFI spec (section 3.1.2, about short-form device-paths)
+ */
+static struct efi_device_path *shorten_path(struct efi_device_path *dp)
+{
+ while (dp) {
+ /*
+ * TODO: Add MESSAGING:USB_WWID and MESSAGING:URI..
+ * in practice fallback.efi just uses MEDIA:HARD_DRIVE
+ * so not sure when we would see these other cases.
+ */
+ if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_CLASS) ||
+ EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) ||
+ EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH))
+ return dp;
+
+ dp = efi_dp_next(dp);
+ }
+
+ return dp;
+}
+
+static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
+ struct efi_device_path **rem)
+{
+ struct efi_object *efiobj;
+ efi_uintn_t dp_size = efi_dp_instance_size(dp);
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ struct efi_handler *handler;
+ struct efi_device_path *obj_dp;
+ efi_status_t ret;
+
+ ret = efi_search_protocol(efiobj,
+ &efi_guid_device_path, &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ obj_dp = handler->protocol_interface;
+
+ do {
+ if (efi_dp_match(dp, obj_dp) == 0) {
+ if (rem) {
+ /*
+ * Allow partial matches, but inform
+ * the caller.
+ */
+ *rem = ((void *)dp) +
+ efi_dp_instance_size(obj_dp);
+ return efiobj;
+ } else {
+ /* Only return on exact matches */
+ if (efi_dp_instance_size(obj_dp) ==
+ dp_size)
+ return efiobj;
+ }
+ }
+
+ obj_dp = shorten_path(efi_dp_next(obj_dp));
+ } while (short_path && obj_dp);
+ }
+
+ return NULL;
+}
+
+/*
+ * Find an efiobj from device-path, if 'rem' is not NULL, returns the
+ * remaining part of the device path after the matched object.
+ */
+struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
+ struct efi_device_path **rem)
+{
+ struct efi_object *efiobj;
+
+ /* Search for an exact match first */
+ efiobj = find_obj(dp, false, NULL);
+
+ /* Then for a fuzzy match */
+ if (!efiobj)
+ efiobj = find_obj(dp, false, rem);
+
+ /* And now for a fuzzy short match */
+ if (!efiobj)
+ efiobj = find_obj(dp, true, rem);
+
+ return efiobj;
+}
+
+/*
+ * Determine the last device path node that is not the end node.
+ *
+ * @dp device path
+ * @return last node before the end node if it exists
+ * otherwise NULL
+ */
+const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp)
+{
+ struct efi_device_path *ret;
+
+ if (!dp || dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ while (dp) {
+ ret = (struct efi_device_path *)dp;
+ dp = efi_dp_next(dp);
+ }
+ return ret;
+}
+
+/* get size of the first device path instance excluding end node */
+efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp)
+{
+ efi_uintn_t sz = 0;
+
+ if (!dp || dp->type == DEVICE_PATH_TYPE_END)
+ return 0;
+ while (dp) {
+ sz += dp->length;
+ dp = efi_dp_next(dp);
+ }
+
+ return sz;
+}
+
+/* get size of multi-instance device path excluding end node */
+efi_uintn_t efi_dp_size(const struct efi_device_path *dp)
+{
+ const struct efi_device_path *p = dp;
+
+ if (!p)
+ return 0;
+ while (p->type != DEVICE_PATH_TYPE_END ||
+ p->sub_type != DEVICE_PATH_SUB_TYPE_END)
+ p = (void *)p + p->length;
+
+ return (void *)p - (void *)dp;
+}
+
+/* copy multi-instance device path */
+struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
+{
+ struct efi_device_path *ndp;
+ size_t sz = efi_dp_size(dp) + sizeof(END);
+
+ if (!dp)
+ return NULL;
+
+ ndp = dp_alloc(sz);
+ if (!ndp)
+ return NULL;
+ memcpy(ndp, dp, sz);
+
+ return ndp;
+}
+
+/**
+ * efi_dp_append_or_concatenate() - Append or concatenate two device paths.
+ * Concatenated device path will be separated
+ * by a sub-type 0xff end node
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ * @concat: If true the two device paths will be concatenated and separated
+ * by an end of entrire device path sub-type 0xff end node.
+ * If true the second device path will be appended to the first and
+ * terminated by an end node
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+static struct
+efi_device_path *efi_dp_append_or_concatenate(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2,
+ bool concat)
+{
+ struct efi_device_path *ret;
+ size_t end_size = sizeof(END);
+
+ if (concat)
+ end_size = 2 * sizeof(END);
+ if (!dp1 && !dp2) {
+ /* return an end node */
+ ret = efi_dp_dup(&END);
+ } else if (!dp1) {
+ ret = efi_dp_dup(dp2);
+ } else if (!dp2) {
+ ret = efi_dp_dup(dp1);
+ } else {
+ /* both dp1 and dp2 are non-null */
+ unsigned sz1 = efi_dp_size(dp1);
+ unsigned sz2 = efi_dp_size(dp2);
+ void *p = dp_alloc(sz1 + sz2 + end_size);
+ if (!p)
+ return NULL;
+ ret = p;
+ memcpy(p, dp1, sz1);
+ p += sz1;
+
+ if (concat) {
+ memcpy(p, &END, sizeof(END));
+ p += sizeof(END);
+ }
+
+ /* the end node of the second device path has to be retained */
+ memcpy(p, dp2, sz2);
+ p += sz2;
+ memcpy(p, &END, sizeof(END));
+ }
+
+ return ret;
+}
+
+/**
+ * efi_dp_append() - Append a device to an existing device path.
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2)
+{
+ return efi_dp_append_or_concatenate(dp1, dp2, false);
+}
+
+/**
+ * efi_dp_concat() - Concatenate 2 device paths. The final device path will
+ * contain two device paths separated by and end node (0xff).
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2)
+{
+ return efi_dp_append_or_concatenate(dp1, dp2, true);
+}
+
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+ const struct efi_device_path *node)
+{
+ struct efi_device_path *ret;
+
+ if (!node && !dp) {
+ ret = efi_dp_dup(&END);
+ } else if (!node) {
+ ret = efi_dp_dup(dp);
+ } else if (!dp) {
+ size_t sz = node->length;
+ void *p = dp_alloc(sz + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, node, sz);
+ memcpy(p + sz, &END, sizeof(END));
+ ret = p;
+ } else {
+ /* both dp and node are non-null */
+ size_t sz = efi_dp_size(dp);
+ void *p = dp_alloc(sz + node->length + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, dp, sz);
+ memcpy(p + sz, node, node->length);
+ memcpy(p + sz + node->length, &END, sizeof(END));
+ ret = p;
+ }
+
+ return ret;
+}
+
+struct efi_device_path *efi_dp_create_device_node(const u8 type,
+ const u8 sub_type,
+ const u16 length)
+{
+ struct efi_device_path *ret;
+
+ if (length < sizeof(struct efi_device_path))
+ return NULL;
+
+ ret = dp_alloc(length);
+ if (!ret)
+ return ret;
+ ret->type = type;
+ ret->sub_type = sub_type;
+ ret->length = length;
+ return ret;
+}
+
+struct efi_device_path *efi_dp_append_instance(
+ const struct efi_device_path *dp,
+ const struct efi_device_path *dpi)
+{
+ size_t sz, szi;
+ struct efi_device_path *p, *ret;
+
+ if (!dpi)
+ return NULL;
+ if (!dp)
+ return efi_dp_dup(dpi);
+ sz = efi_dp_size(dp);
+ szi = efi_dp_instance_size(dpi);
+ p = dp_alloc(sz + szi + 2 * sizeof(END));
+ if (!p)
+ return NULL;
+ ret = p;
+ memcpy(p, dp, sz + sizeof(END));
+ p = (void *)p + sz;
+ p->sub_type = DEVICE_PATH_SUB_TYPE_INSTANCE_END;
+ p = (void *)p + sizeof(END);
+ memcpy(p, dpi, szi);
+ p = (void *)p + szi;
+ memcpy(p, &END, sizeof(END));
+ return ret;
+}
+
+struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
+ efi_uintn_t *size)
+{
+ size_t sz;
+ struct efi_device_path *p;
+
+ if (size)
+ *size = 0;
+ if (!dp || !*dp)
+ return NULL;
+ sz = efi_dp_instance_size(*dp);
+ p = dp_alloc(sz + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, *dp, sz + sizeof(END));
+ *dp = (void *)*dp + sz;
+ if ((*dp)->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END)
+ *dp = (void *)*dp + sizeof(END);
+ else
+ *dp = NULL;
+ if (size)
+ *size = sz + sizeof(END);
+ return p;
+}
+
+bool efi_dp_is_multi_instance(const struct efi_device_path *dp)
+{
+ const struct efi_device_path *p = dp;
+
+ if (!p)
+ return false;
+ while (p->type != DEVICE_PATH_TYPE_END)
+ p = (void *)p + p->length;
+ return p->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END;
+}
+
+#ifdef CONFIG_DM
+/* size of device-path not including END node for device and all parents
+ * up to the root device.
+ */
+__maybe_unused static unsigned int dp_size(struct udevice *dev)
+{
+ if (!dev || !dev->driver)
+ return sizeof(ROOT);
+
+ switch (dev->driver->id) {
+ case UCLASS_ROOT:
+ case UCLASS_SIMPLE_BUS:
+ /* stop traversing parents at this point: */
+ return sizeof(ROOT);
+ case UCLASS_ETH:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_mac_addr);
+#ifdef CONFIG_BLK
+ case UCLASS_BLK:
+ switch (dev->parent->uclass->uc_drv->id) {
+#ifdef CONFIG_IDE
+ case UCLASS_IDE:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_atapi);
+#endif
+#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI)
+ case UCLASS_SCSI:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_scsi);
+#endif
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+ case UCLASS_MMC:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_sd_mmc_path);
+#endif
+#if defined(CONFIG_AHCI) || defined(CONFIG_SATA)
+ case UCLASS_AHCI:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_sata);
+#endif
+#if defined(CONFIG_NVME)
+ case UCLASS_NVME:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_nvme);
+#endif
+#ifdef CONFIG_SANDBOX
+ case UCLASS_ROOT:
+ /*
+ * Sandbox's host device will be represented
+ * as vendor device with extra one byte for
+ * device number
+ */
+ return dp_size(dev->parent)
+ + sizeof(struct efi_device_path_vendor) + 1;
+#endif
+#ifdef CONFIG_VIRTIO_BLK
+ case UCLASS_VIRTIO:
+ /*
+ * Virtio devices will be represented as a vendor
+ * device node with an extra byte for the device
+ * number.
+ */
+ return dp_size(dev->parent)
+ + sizeof(struct efi_device_path_vendor) + 1;
+#endif
+ default:
+ return dp_size(dev->parent);
+ }
+#endif
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+ case UCLASS_MMC:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_sd_mmc_path);
+#endif
+ case UCLASS_MASS_STORAGE:
+ case UCLASS_USB_HUB:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_usb_class);
+ default:
+ /* just skip over unknown classes: */
+ return dp_size(dev->parent);
+ }
+}
+
+/*
+ * Recursively build a device path.
+ *
+ * @buf pointer to the end of the device path
+ * @dev device
+ * @return pointer to the end of the device path
+ */
+__maybe_unused static void *dp_fill(void *buf, struct udevice *dev)
+{
+ if (!dev || !dev->driver)
+ return buf;
+
+ switch (dev->driver->id) {
+ case UCLASS_ROOT:
+ case UCLASS_SIMPLE_BUS: {
+ /* stop traversing parents at this point: */
+ struct efi_device_path_vendor *vdp = buf;
+ *vdp = ROOT;
+ return &vdp[1];
+ }
+#ifdef CONFIG_DM_ETH
+ case UCLASS_ETH: {
+ struct efi_device_path_mac_addr *dp =
+ dp_fill(buf, dev->parent);
+ struct eth_pdata *pdata = dev_get_plat(dev);
+
+ dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
+ dp->dp.length = sizeof(*dp);
+ memset(&dp->mac, 0, sizeof(dp->mac));
+ /* We only support IPv4 */
+ memcpy(&dp->mac, &pdata->enetaddr, ARP_HLEN);
+ /* Ethernet */
+ dp->if_type = 1;
+ return &dp[1];
+ }
+#endif
+#ifdef CONFIG_BLK
+ case UCLASS_BLK:
+ switch (dev->parent->uclass->uc_drv->id) {
+#ifdef CONFIG_SANDBOX
+ case UCLASS_ROOT: {
+ /* stop traversing parents at this point: */
+ struct efi_device_path_vendor *dp;
+ struct blk_desc *desc = dev_get_uclass_plat(dev);
+
+ dp_fill(buf, dev->parent);
+ dp = buf;
+ ++dp;
+ dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+ dp->dp.length = sizeof(*dp) + 1;
+ memcpy(&dp->guid, &efi_guid_host_dev,
+ sizeof(efi_guid_t));
+ dp->vendor_data[0] = desc->devnum;
+ return &dp->vendor_data[1];
+ }
+#endif
+#ifdef CONFIG_VIRTIO_BLK
+ case UCLASS_VIRTIO: {
+ struct efi_device_path_vendor *dp;
+ struct blk_desc *desc = dev_get_uclass_plat(dev);
+
+ dp_fill(buf, dev->parent);
+ dp = buf;
+ ++dp;
+ dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+ dp->dp.length = sizeof(*dp) + 1;
+ memcpy(&dp->guid, &efi_guid_virtio_dev,
+ sizeof(efi_guid_t));
+ dp->vendor_data[0] = desc->devnum;
+ return &dp->vendor_data[1];
+ }
+#endif
+#ifdef CONFIG_IDE
+ case UCLASS_IDE: {
+ struct efi_device_path_atapi *dp =
+ dp_fill(buf, dev->parent);
+ struct blk_desc *desc = dev_get_uclass_plat(dev);
+
+ dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI;
+ dp->dp.length = sizeof(*dp);
+ dp->logical_unit_number = desc->devnum;
+ dp->primary_secondary = IDE_BUS(desc->devnum);
+ dp->slave_master = desc->devnum %
+ (CONFIG_SYS_IDE_MAXDEVICE /
+ CONFIG_SYS_IDE_MAXBUS);
+ return &dp[1];
+ }
+#endif
+#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI)
+ case UCLASS_SCSI: {
+ struct efi_device_path_scsi *dp =
+ dp_fill(buf, dev->parent);
+ struct blk_desc *desc = dev_get_uclass_plat(dev);
+
+ dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI;
+ dp->dp.length = sizeof(*dp);
+ dp->logical_unit_number = desc->lun;
+ dp->target_id = desc->target;
+ return &dp[1];
+ }
+#endif
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+ case UCLASS_MMC: {
+ struct efi_device_path_sd_mmc_path *sddp =
+ dp_fill(buf, dev->parent);
+ struct blk_desc *desc = dev_get_uclass_plat(dev);
+
+ sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ sddp->dp.sub_type = is_sd(desc) ?
+ DEVICE_PATH_SUB_TYPE_MSG_SD :
+ DEVICE_PATH_SUB_TYPE_MSG_MMC;
+ sddp->dp.length = sizeof(*sddp);
+ sddp->slot_number = dev_seq(dev);
+ return &sddp[1];
+ }
+#endif
+#if defined(CONFIG_AHCI) || defined(CONFIG_SATA)
+ case UCLASS_AHCI: {
+ struct efi_device_path_sata *dp =
+ dp_fill(buf, dev->parent);
+ struct blk_desc *desc = dev_get_uclass_plat(dev);
+
+ dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SATA;
+ dp->dp.length = sizeof(*dp);
+ dp->hba_port = desc->devnum;
+ /* default 0xffff implies no port multiplier */
+ dp->port_multiplier_port = 0xffff;
+ dp->logical_unit_number = desc->lun;
+ return &dp[1];
+ }
+#endif
+#if defined(CONFIG_NVME)
+ case UCLASS_NVME: {
+ struct efi_device_path_nvme *dp =
+ dp_fill(buf, dev->parent);
+ u32 ns_id;
+
+ dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_NVME;
+ dp->dp.length = sizeof(*dp);
+ nvme_get_namespace_id(dev, &ns_id, dp->eui64);
+ memcpy(&dp->ns_id, &ns_id, sizeof(ns_id));
+ return &dp[1];
+ }
+#endif
+ default:
+ debug("%s(%u) %s: unhandled parent class: %s (%u)\n",
+ __FILE__, __LINE__, __func__,
+ dev->name, dev->parent->uclass->uc_drv->id);
+ return dp_fill(buf, dev->parent);
+ }
+#endif
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+ case UCLASS_MMC: {
+ struct efi_device_path_sd_mmc_path *sddp =
+ dp_fill(buf, dev->parent);
+ struct mmc *mmc = mmc_get_mmc_dev(dev);
+ struct blk_desc *desc = mmc_get_blk_desc(mmc);
+
+ sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ sddp->dp.sub_type = is_sd(desc) ?
+ DEVICE_PATH_SUB_TYPE_MSG_SD :
+ DEVICE_PATH_SUB_TYPE_MSG_MMC;
+ sddp->dp.length = sizeof(*sddp);
+ sddp->slot_number = dev_seq(dev);
+
+ return &sddp[1];
+ }
+#endif
+ case UCLASS_MASS_STORAGE:
+ case UCLASS_USB_HUB: {
+ struct efi_device_path_usb_class *udp =
+ dp_fill(buf, dev->parent);
+ struct usb_device *udev = dev_get_parent_priv(dev);
+ struct usb_device_descriptor *desc = &udev->descriptor;
+
+ udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS;
+ udp->dp.length = sizeof(*udp);
+ udp->vendor_id = desc->idVendor;
+ udp->product_id = desc->idProduct;
+ udp->device_class = desc->bDeviceClass;
+ udp->device_subclass = desc->bDeviceSubClass;
+ udp->device_protocol = desc->bDeviceProtocol;
+
+ return &udp[1];
+ }
+ default:
+ debug("%s(%u) %s: unhandled device class: %s (%u)\n",
+ __FILE__, __LINE__, __func__,
+ dev->name, dev->driver->id);
+ return dp_fill(buf, dev->parent);
+ }
+}
+#endif
+
+static unsigned dp_part_size(struct blk_desc *desc, int part)
+{
+ unsigned dpsize;
+
+#ifdef CONFIG_BLK
+ {
+ struct udevice *dev;
+ int ret = blk_find_device(desc->if_type, desc->devnum, &dev);
+
+ if (ret)
+ dev = desc->bdev->parent;
+ dpsize = dp_size(dev);
+ }
+#else
+ dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb);
+#endif
+
+ if (part == 0) /* the actual disk, not a partition */
+ return dpsize;
+
+ if (desc->part_type == PART_TYPE_ISO)
+ dpsize += sizeof(struct efi_device_path_cdrom_path);
+ else
+ dpsize += sizeof(struct efi_device_path_hard_drive_path);
+
+ return dpsize;
+}
+
+/*
+ * Create a device node for a block device partition.
+ *
+ * @buf buffer to which the device path is written
+ * @desc block device descriptor
+ * @part partition number, 0 identifies a block device
+ */
+static void *dp_part_node(void *buf, struct blk_desc *desc, int part)
+{
+ struct disk_partition info;
+
+ part_get_info(desc, part, &info);
+
+ if (desc->part_type == PART_TYPE_ISO) {
+ struct efi_device_path_cdrom_path *cddp = buf;
+
+ cddp->boot_entry = part;
+ cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH;
+ cddp->dp.length = sizeof(*cddp);
+ cddp->partition_start = info.start;
+ cddp->partition_size = info.size;
+
+ buf = &cddp[1];
+ } else {
+ struct efi_device_path_hard_drive_path *hddp = buf;
+
+ hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH;
+ hddp->dp.length = sizeof(*hddp);
+ hddp->partition_number = part;
+ hddp->partition_start = info.start;
+ hddp->partition_end = info.size;
+ if (desc->part_type == PART_TYPE_EFI)
+ hddp->partmap_type = 2;
+ else
+ hddp->partmap_type = 1;
+
+ switch (desc->sig_type) {
+ case SIG_TYPE_NONE:
+ default:
+ hddp->signature_type = 0;
+ memset(hddp->partition_signature, 0,
+ sizeof(hddp->partition_signature));
+ break;
+ case SIG_TYPE_MBR:
+ hddp->signature_type = 1;
+ memset(hddp->partition_signature, 0,
+ sizeof(hddp->partition_signature));
+ memcpy(hddp->partition_signature, &desc->mbr_sig,
+ sizeof(desc->mbr_sig));
+ break;
+ case SIG_TYPE_GUID:
+ hddp->signature_type = 2;
+ memcpy(hddp->partition_signature, &desc->guid_sig,
+ sizeof(hddp->partition_signature));
+ break;
+ }
+
+ buf = &hddp[1];
+ }
+
+ return buf;
+}
+
+/*
+ * Create a device path for a block device or one of its partitions.
+ *
+ * @buf buffer to which the device path is written
+ * @desc block device descriptor
+ * @part partition number, 0 identifies a block device
+ */
+static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
+{
+#ifdef CONFIG_BLK
+ {
+ struct udevice *dev;
+ int ret = blk_find_device(desc->if_type, desc->devnum, &dev);
+
+ if (ret)
+ dev = desc->bdev->parent;
+ buf = dp_fill(buf, dev);
+ }
+#else
+ /*
+ * We *could* make a more accurate path, by looking at if_type
+ * and handling all the different cases like we do for non-
+ * legacy (i.e. CONFIG_BLK=y) case. But most important thing
+ * is just to have a unique device-path for if_type+devnum.
+ * So map things to a fictitious USB device.
+ */
+ struct efi_device_path_usb *udp;
+
+ memcpy(buf, &ROOT, sizeof(ROOT));
+ buf += sizeof(ROOT);
+
+ udp = buf;
+ udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB;
+ udp->dp.length = sizeof(*udp);
+ udp->parent_port_number = desc->if_type;
+ udp->usb_interface = desc->devnum;
+ buf = &udp[1];
+#endif
+
+ if (part == 0) /* the actual disk, not a partition */
+ return buf;
+
+ return dp_part_node(buf, desc, part);
+}
+
+/* Construct a device-path from a partition on a block device: */
+struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
+{
+ void *buf, *start;
+
+ start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END));
+ if (!buf)
+ return NULL;
+
+ buf = dp_part_fill(buf, desc, part);
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+
+/*
+ * Create a device node for a block device partition.
+ *
+ * @buf buffer to which the device path is written
+ * @desc block device descriptor
+ * @part partition number, 0 identifies a block device
+ */
+struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part)
+{
+ efi_uintn_t dpsize;
+ void *buf;
+
+ if (desc->part_type == PART_TYPE_ISO)
+ dpsize = sizeof(struct efi_device_path_cdrom_path);
+ else
+ dpsize = sizeof(struct efi_device_path_hard_drive_path);
+ buf = dp_alloc(dpsize);
+
+ dp_part_node(buf, desc, part);
+
+ return buf;
+}
+
+/**
+ * path_to_uefi() - convert UTF-8 path to an UEFI style path
+ *
+ * Convert UTF-8 path to a UEFI style path (i.e. with backslashes as path
+ * separators and UTF-16).
+ *
+ * @src: source buffer
+ * @uefi: target buffer, possibly unaligned
+ */
+static void path_to_uefi(void *uefi, const char *src)
+{
+ u16 *pos = uefi;
+
+ /*
+ * efi_set_bootdev() calls this routine indirectly before the UEFI
+ * subsystem is initialized. So we cannot assume unaligned access to be
+ * enabled.
+ */
+ allow_unaligned();
+
+ while (*src) {
+ s32 code = utf8_get(&src);
+
+ if (code < 0)
+ code = '?';
+ else if (code == '/')
+ code = '\\';
+ utf16_put(code, &pos);
+ }
+ *pos = 0;
+}
+
+/*
+ * If desc is NULL, this creates a path with only the file component,
+ * otherwise it creates a full path with both device and file components
+ */
+struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
+ const char *path)
+{
+ struct efi_device_path_file_path *fp;
+ void *buf, *start;
+ size_t dpsize = 0, fpsize;
+
+ if (desc)
+ dpsize = dp_part_size(desc, part);
+
+ fpsize = sizeof(struct efi_device_path) +
+ 2 * (utf8_utf16_strlen(path) + 1);
+ if (fpsize > U16_MAX)
+ return NULL;
+
+ dpsize += fpsize;
+
+ start = buf = dp_alloc(dpsize + sizeof(END));
+ if (!buf)
+ return NULL;
+
+ if (desc)
+ buf = dp_part_fill(buf, desc, part);
+
+ /* add file-path: */
+ fp = buf;
+ fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
+ fp->dp.length = (u16)fpsize;
+ path_to_uefi(fp->str, path);
+ buf += fpsize;
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+
+struct efi_device_path *efi_dp_from_uart(void)
+{
+ void *buf, *pos;
+ struct efi_device_path_uart *uart;
+ size_t dpsize = sizeof(ROOT) + sizeof(*uart) + sizeof(END);
+
+ buf = dp_alloc(dpsize);
+ if (!buf)
+ return NULL;
+ pos = buf;
+ memcpy(pos, &ROOT, sizeof(ROOT));
+ pos += sizeof(ROOT);
+ uart = pos;
+ uart->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ uart->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_UART;
+ uart->dp.length = sizeof(*uart);
+ pos += sizeof(*uart);
+ memcpy(pos, &END, sizeof(END));
+
+ return buf;
+}
+
+#ifdef CONFIG_NET
+struct efi_device_path *efi_dp_from_eth(void)
+{
+#ifndef CONFIG_DM_ETH
+ struct efi_device_path_mac_addr *ndp;
+#endif
+ void *buf, *start;
+ unsigned dpsize = 0;
+
+ assert(eth_get_dev());
+
+#ifdef CONFIG_DM_ETH
+ dpsize += dp_size(eth_get_dev());
+#else
+ dpsize += sizeof(ROOT);
+ dpsize += sizeof(*ndp);
+#endif
+
+ start = buf = dp_alloc(dpsize + sizeof(END));
+ if (!buf)
+ return NULL;
+
+#ifdef CONFIG_DM_ETH
+ buf = dp_fill(buf, eth_get_dev());
+#else
+ memcpy(buf, &ROOT, sizeof(ROOT));
+ buf += sizeof(ROOT);
+
+ ndp = buf;
+ ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
+ ndp->dp.length = sizeof(*ndp);
+ ndp->if_type = 1; /* Ethernet */
+ memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
+ buf = &ndp[1];
+#endif
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+#endif
+
+/* Construct a device-path for memory-mapped image */
+struct efi_device_path *efi_dp_from_mem(uint32_t memory_type,
+ uint64_t start_address,
+ uint64_t end_address)
+{
+ struct efi_device_path_memory *mdp;
+ void *buf, *start;
+
+ start = buf = dp_alloc(sizeof(*mdp) + sizeof(END));
+ if (!buf)
+ return NULL;
+
+ mdp = buf;
+ mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY;
+ mdp->dp.length = sizeof(*mdp);
+ mdp->memory_type = memory_type;
+ mdp->start_address = start_address;
+ mdp->end_address = end_address;
+ buf = &mdp[1];
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+
+/**
+ * efi_dp_split_file_path() - split of relative file path from device path
+ *
+ * Given a device path indicating a file on a device, separate the device
+ * path in two: the device path of the actual device and the file path
+ * relative to this device.
+ *
+ * @full_path: device path including device and file path
+ * @device_path: path of the device
+ * @file_path: relative path of the file or NULL if there is none
+ * Return: status code
+ */
+efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
+ struct efi_device_path **device_path,
+ struct efi_device_path **file_path)
+{
+ struct efi_device_path *p, *dp, *fp = NULL;
+
+ *device_path = NULL;
+ *file_path = NULL;
+ dp = efi_dp_dup(full_path);
+ if (!dp)
+ return EFI_OUT_OF_RESOURCES;
+ p = dp;
+ while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) {
+ p = efi_dp_next(p);
+ if (!p)
+ goto out;
+ }
+ fp = efi_dp_dup(p);
+ if (!fp)
+ return EFI_OUT_OF_RESOURCES;
+ p->type = DEVICE_PATH_TYPE_END;
+ p->sub_type = DEVICE_PATH_SUB_TYPE_END;
+ p->length = sizeof(*p);
+
+out:
+ *device_path = dp;
+ *file_path = fp;
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_dp_from_name() - convert U-Boot device and file path to device path
+ *
+ * @dev: U-Boot device, e.g. 'mmc'
+ * @devnr: U-Boot device number, e.g. 1 for 'mmc:1'
+ * @path: file path relative to U-Boot device, may be NULL
+ * @device: pointer to receive device path of the device
+ * @file: pointer to receive device path for the file
+ * Return: status code
+ */
+efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
+ const char *path,
+ struct efi_device_path **device,
+ struct efi_device_path **file)
+{
+ struct blk_desc *desc = NULL;
+ struct disk_partition fs_partition;
+ int part = 0;
+ char *filename;
+ char *s;
+
+ if (path && !file)
+ return EFI_INVALID_PARAMETER;
+
+ if (!strcmp(dev, "Net")) {
+#ifdef CONFIG_NET
+ if (device)
+ *device = efi_dp_from_eth();
+#endif
+ } else if (!strcmp(dev, "Uart")) {
+ if (device)
+ *device = efi_dp_from_uart();
+ } else {
+ part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
+ 1);
+ if (part < 0 || !desc)
+ return EFI_INVALID_PARAMETER;
+
+ if (device)
+ *device = efi_dp_from_part(desc, part);
+ }
+
+ if (!path)
+ return EFI_SUCCESS;
+
+ filename = calloc(1, strlen(path) + 1);
+ if (!filename)
+ return EFI_OUT_OF_RESOURCES;
+
+ sprintf(filename, "%s", path);
+ /* DOS style file path: */
+ s = filename;
+ while ((s = strchr(s, '/')))
+ *s++ = '\\';
+ *file = efi_dp_from_file(desc, part, filename);
+ free(filename);
+
+ if (!*file)
+ return EFI_INVALID_PARAMETER;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_dp_check_length() - check length of a device path
+ *
+ * @dp: pointer to device path
+ * @maxlen: maximum length of the device path
+ * Return:
+ * * length of the device path if it is less or equal @maxlen
+ * * -1 if the device path is longer then @maxlen
+ * * -1 if a device path node has a length of less than 4
+ * * -EINVAL if maxlen exceeds SSIZE_MAX
+ */
+ssize_t efi_dp_check_length(const struct efi_device_path *dp,
+ const size_t maxlen)
+{
+ ssize_t ret = 0;
+ u16 len;
+
+ if (maxlen > SSIZE_MAX)
+ return -EINVAL;
+ for (;;) {
+ len = dp->length;
+ if (len < 4)
+ return -1;
+ ret += len;
+ if (ret > maxlen)
+ return -1;
+ if (dp->type == DEVICE_PATH_TYPE_END &&
+ dp->sub_type == DEVICE_PATH_SUB_TYPE_END)
+ return ret;
+ dp = (const struct efi_device_path *)((const u8 *)dp + len);
+ }
+}
+
+/**
+ * efi_dp_from_lo() - Get the instance of a VenMedia node in a
+ * multi-instance device path that matches
+ * a specific GUID. This kind of device paths
+ * is found in Boot#### options describing an
+ * initrd location
+ *
+ * @lo: EFI_LOAD_OPTION containing a valid device path
+ * @size: size of the discovered device path
+ * @guid: guid to search for
+ *
+ * Return:
+ * device path including the VenMedia node or NULL.
+ * Caller must free the returned value.
+ */
+struct
+efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+ efi_uintn_t *size, efi_guid_t guid)
+{
+ struct efi_device_path *fp = lo->file_path;
+ struct efi_device_path_vendor *vendor;
+ int lo_len = lo->file_path_length;
+
+ for (; lo_len >= sizeof(struct efi_device_path);
+ lo_len -= fp->length, fp = (void *)fp + fp->length) {
+ if (lo_len < 0 || efi_dp_check_length(fp, lo_len) < 0)
+ break;
+ if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
+ fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH)
+ continue;
+
+ vendor = (struct efi_device_path_vendor *)fp;
+ if (!guidcmp(&vendor->guid, &guid))
+ return efi_dp_dup(fp);
+ }
+ log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label);
+
+ return NULL;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_device_path_to_text.c b/roms/u-boot/lib/efi_loader/efi_device_path_to_text.c
new file mode 100644
index 000000000..675e80bcb
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_device_path_to_text.c
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI device path interface
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <efi_loader.h>
+
+#define MAC_OUTPUT_LEN 22
+#define UNKNOWN_OUTPUT_LEN 23
+
+#define MAX_NODE_LEN 512
+#define MAX_PATH_LEN 1024
+
+const efi_guid_t efi_guid_device_path_to_text_protocol =
+ EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+
+/**
+ * efi_str_to_u16() - convert ASCII string to UTF-16
+ *
+ * A u16 buffer is allocated from pool. The ASCII string is copied to the u16
+ * buffer.
+ *
+ * @str: ASCII string
+ * Return: UTF-16 string. NULL if out of memory.
+ */
+static u16 *efi_str_to_u16(char *str)
+{
+ efi_uintn_t len;
+ u16 *out, *dst;
+ efi_status_t ret;
+
+ len = sizeof(u16) * (utf8_utf16_strlen(str) + 1);
+ ret = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, len, (void **)&out);
+ if (ret != EFI_SUCCESS)
+ return NULL;
+ dst = out;
+ utf8_utf16_strcpy(&dst, str);
+ return out;
+}
+
+static char *dp_unknown(char *s, struct efi_device_path *dp)
+{
+ s += sprintf(s, "UNKNOWN(%04x,%04x)", dp->type, dp->sub_type);
+ return s;
+}
+
+static char *dp_hardware(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_MEMORY: {
+ struct efi_device_path_memory *mdp =
+ (struct efi_device_path_memory *)dp;
+ s += sprintf(s, "MemoryMapped(0x%x,0x%llx,0x%llx)",
+ mdp->memory_type,
+ mdp->start_address,
+ mdp->end_address);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_VENDOR: {
+ int i, n;
+ struct efi_device_path_vendor *vdp =
+ (struct efi_device_path_vendor *)dp;
+
+ s += sprintf(s, "VenHw(%pUl", &vdp->guid);
+ n = (int)vdp->dp.length - sizeof(struct efi_device_path_vendor);
+ /* Node must fit into MAX_NODE_LEN) */
+ if (n > 0 && n < MAX_NODE_LEN / 2 - 22) {
+ s += sprintf(s, ",");
+ for (i = 0; i < n; ++i)
+ s += sprintf(s, "%02x", vdp->vendor_data[i]);
+ }
+ s += sprintf(s, ")");
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
+ break;
+ }
+ return s;
+}
+
+static char *dp_acpi(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_ACPI_DEVICE: {
+ struct efi_device_path_acpi_path *adp =
+ (struct efi_device_path_acpi_path *)dp;
+
+ s += sprintf(s, "Acpi(PNP%04X,%d)", EISA_PNP_NUM(adp->hid),
+ adp->uid);
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
+ break;
+ }
+ return s;
+}
+
+static char *dp_msging(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_MSG_ATAPI: {
+ struct efi_device_path_atapi *ide =
+ (struct efi_device_path_atapi *)dp;
+ s += sprintf(s, "Ata(%d,%d,%d)", ide->primary_secondary,
+ ide->slave_master, ide->logical_unit_number);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_SCSI: {
+ struct efi_device_path_scsi *ide =
+ (struct efi_device_path_scsi *)dp;
+ s += sprintf(s, "Scsi(%u,%u)", ide->target_id,
+ ide->logical_unit_number);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_UART: {
+ struct efi_device_path_uart *uart =
+ (struct efi_device_path_uart *)dp;
+ s += sprintf(s, "Uart(%lld,%d,%d,", uart->baud_rate,
+ uart->data_bits, uart->parity);
+ switch (uart->stop_bits) {
+ case 2:
+ s += sprintf(s, "1.5)");
+ break;
+ default:
+ s += sprintf(s, "%d)", uart->stop_bits);
+ break;
+ }
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_USB: {
+ struct efi_device_path_usb *udp =
+ (struct efi_device_path_usb *)dp;
+ s += sprintf(s, "USB(0x%x,0x%x)", udp->parent_port_number,
+ udp->usb_interface);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: {
+ int i, n = sizeof(struct efi_mac_addr);
+ struct efi_device_path_mac_addr *mdp =
+ (struct efi_device_path_mac_addr *)dp;
+
+ if (mdp->if_type <= 1)
+ n = 6;
+ s += sprintf(s, "MAC(");
+ for (i = 0; i < n; ++i)
+ s += sprintf(s, "%02x", mdp->mac.addr[i]);
+ s += sprintf(s, ",%u)", mdp->if_type);
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS: {
+ struct efi_device_path_usb_class *ucdp =
+ (struct efi_device_path_usb_class *)dp;
+
+ s += sprintf(s, "UsbClass(0x%x,0x%x,0x%x,0x%x,0x%x)",
+ ucdp->vendor_id, ucdp->product_id,
+ ucdp->device_class, ucdp->device_subclass,
+ ucdp->device_protocol);
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_SATA: {
+ struct efi_device_path_sata *sdp =
+ (struct efi_device_path_sata *) dp;
+
+ s += sprintf(s, "Sata(0x%x,0x%x,0x%x)",
+ sdp->hba_port,
+ sdp->port_multiplier_port,
+ sdp->logical_unit_number);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_NVME: {
+ struct efi_device_path_nvme *ndp =
+ (struct efi_device_path_nvme *)dp;
+ u32 ns_id;
+ int i;
+
+ memcpy(&ns_id, &ndp->ns_id, sizeof(ns_id));
+ s += sprintf(s, "NVMe(0x%x,", ns_id);
+ for (i = 0; i < sizeof(ndp->eui64); ++i)
+ s += sprintf(s, "%s%02x", i ? "-" : "",
+ ndp->eui64[i]);
+ s += sprintf(s, ")");
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_SD:
+ case DEVICE_PATH_SUB_TYPE_MSG_MMC: {
+ const char *typename =
+ (dp->sub_type == DEVICE_PATH_SUB_TYPE_MSG_SD) ?
+ "SD" : "eMMC";
+ struct efi_device_path_sd_mmc_path *sddp =
+ (struct efi_device_path_sd_mmc_path *)dp;
+ s += sprintf(s, "%s(%u)", typename, sddp->slot_number);
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
+ break;
+ }
+ return s;
+}
+
+/*
+ * Convert a media device path node to text.
+ *
+ * @s output buffer
+ * @dp device path node
+ * @return next unused buffer address
+ */
+static char *dp_media(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH: {
+ struct efi_device_path_hard_drive_path *hddp =
+ (struct efi_device_path_hard_drive_path *)dp;
+ void *sig = hddp->partition_signature;
+ u64 start;
+ u64 end;
+
+ /* Copy from packed structure to aligned memory */
+ memcpy(&start, &hddp->partition_start, sizeof(start));
+ memcpy(&end, &hddp->partition_end, sizeof(end));
+
+ switch (hddp->signature_type) {
+ case SIG_TYPE_MBR: {
+ u32 signature;
+
+ memcpy(&signature, sig, sizeof(signature));
+ s += sprintf(
+ s, "HD(%d,MBR,0x%08x,0x%llx,0x%llx)",
+ hddp->partition_number, signature, start, end);
+ break;
+ }
+ case SIG_TYPE_GUID:
+ s += sprintf(
+ s, "HD(%d,GPT,%pUl,0x%llx,0x%llx)",
+ hddp->partition_number, sig, start, end);
+ break;
+ default:
+ s += sprintf(
+ s, "HD(%d,0x%02x,0,0x%llx,0x%llx)",
+ hddp->partition_number, hddp->partmap_type,
+ start, end);
+ break;
+ }
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_CDROM_PATH: {
+ struct efi_device_path_cdrom_path *cddp =
+ (struct efi_device_path_cdrom_path *)dp;
+ s += sprintf(s, "CDROM(%u,0x%llx,0x%llx)", cddp->boot_entry,
+ cddp->partition_start, cddp->partition_size);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_VENDOR_PATH: {
+ int i, n;
+ struct efi_device_path_vendor *vdp =
+ (struct efi_device_path_vendor *)dp;
+
+ s += sprintf(s, "VenMedia(%pUl", &vdp->guid);
+ n = (int)vdp->dp.length - sizeof(struct efi_device_path_vendor);
+ /* Node must fit into MAX_NODE_LEN) */
+ if (n > 0 && n < MAX_NODE_LEN / 2 - 24) {
+ s += sprintf(s, ",");
+ for (i = 0; i < n; ++i)
+ s += sprintf(s, "%02x", vdp->vendor_data[i]);
+ }
+ s += sprintf(s, ")");
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_FILE_PATH: {
+ struct efi_device_path_file_path *fp =
+ (struct efi_device_path_file_path *)dp;
+ int slen = (dp->length - sizeof(*dp)) / 2;
+ if (slen > MAX_NODE_LEN - 2)
+ slen = MAX_NODE_LEN - 2;
+ s += sprintf(s, "%-.*ls", slen, fp->str);
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
+ break;
+ }
+ return s;
+}
+
+/*
+ * Converts a single node to a char string.
+ *
+ * @buffer output buffer
+ * @dp device path or node
+ * @return end of string
+ */
+static char *efi_convert_single_device_node_to_text(
+ char *buffer,
+ struct efi_device_path *dp)
+{
+ char *str = buffer;
+
+ switch (dp->type) {
+ case DEVICE_PATH_TYPE_HARDWARE_DEVICE:
+ str = dp_hardware(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_ACPI_DEVICE:
+ str = dp_acpi(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
+ str = dp_msging(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_MEDIA_DEVICE:
+ str = dp_media(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_END:
+ break;
+ default:
+ str = dp_unknown(str, dp);
+ }
+
+ *str = '\0';
+ return str;
+}
+
+/*
+ * This function implements the ConvertDeviceNodeToText service of the
+ * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * device_node device node to be converted
+ * display_only true if the shorter text representation shall be used
+ * allow_shortcuts true if shortcut forms may be used
+ * @return text representation of the device path
+ * NULL if out of memory of device_path is NULL
+ */
+static uint16_t EFIAPI *efi_convert_device_node_to_text(
+ struct efi_device_path *device_node,
+ bool display_only,
+ bool allow_shortcuts)
+{
+ char str[MAX_NODE_LEN];
+ uint16_t *text = NULL;
+
+ EFI_ENTRY("%p, %d, %d", device_node, display_only, allow_shortcuts);
+
+ if (!device_node)
+ goto out;
+ efi_convert_single_device_node_to_text(str, device_node);
+
+ text = efi_str_to_u16(str);
+
+out:
+ EFI_EXIT(EFI_SUCCESS);
+ return text;
+}
+
+/*
+ * This function implements the ConvertDevicePathToText service of the
+ * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * device_path device path to be converted
+ * display_only true if the shorter text representation shall be used
+ * allow_shortcuts true if shortcut forms may be used
+ * @return text representation of the device path
+ * NULL if out of memory of device_path is NULL
+ */
+static uint16_t EFIAPI *efi_convert_device_path_to_text(
+ struct efi_device_path *device_path,
+ bool display_only,
+ bool allow_shortcuts)
+{
+ uint16_t *text = NULL;
+ char buffer[MAX_PATH_LEN];
+ char *str = buffer;
+
+ EFI_ENTRY("%p, %d, %d", device_path, display_only, allow_shortcuts);
+
+ if (!device_path)
+ goto out;
+ while (device_path && str + MAX_NODE_LEN < buffer + MAX_PATH_LEN) {
+ if (device_path->type == DEVICE_PATH_TYPE_END) {
+ if (device_path->sub_type !=
+ DEVICE_PATH_SUB_TYPE_INSTANCE_END)
+ break;
+ *str++ = ',';
+ } else {
+ *str++ = '/';
+ str = efi_convert_single_device_node_to_text(
+ str, device_path);
+ }
+ *(u8 **)&device_path += device_path->length;
+ }
+
+ text = efi_str_to_u16(buffer);
+
+out:
+ EFI_EXIT(EFI_SUCCESS);
+ return text;
+}
+
+/* helper for debug prints.. efi_free_pool() the result. */
+uint16_t *efi_dp_str(struct efi_device_path *dp)
+{
+ return EFI_CALL(efi_convert_device_path_to_text(dp, true, true));
+}
+
+const struct efi_device_path_to_text_protocol efi_device_path_to_text = {
+ .convert_device_node_to_text = efi_convert_device_node_to_text,
+ .convert_device_path_to_text = efi_convert_device_path_to_text,
+};
diff --git a/roms/u-boot/lib/efi_loader/efi_device_path_utilities.c b/roms/u-boot/lib/efi_loader/efi_device_path_utilities.c
new file mode 100644
index 000000000..94015329c
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_device_path_utilities.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI device path interface
+ *
+ * Copyright (c) 2017 Leif Lindholm
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+const efi_guid_t efi_guid_device_path_utilities_protocol =
+ EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
+
+/*
+ * Get size of a device path.
+ *
+ * This function implements the GetDevicePathSize service of the device path
+ * utilities protocol. The device path length includes the end of path tag
+ * which may be an instance end.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @return size in bytes
+ */
+static efi_uintn_t EFIAPI get_device_path_size(
+ const struct efi_device_path *device_path)
+{
+ efi_uintn_t sz = 0;
+
+ EFI_ENTRY("%pD", device_path);
+ /* size includes the END node: */
+ if (device_path)
+ sz = efi_dp_size(device_path) + sizeof(struct efi_device_path);
+ return EFI_EXIT(sz);
+}
+
+/*
+ * Duplicate a device path.
+ *
+ * This function implements the DuplicateDevicePath service of the device path
+ * utilities protocol.
+ *
+ * The UEFI spec does not indicate what happens to the end tag. We follow the
+ * EDK2 logic: In case the device path ends with an end of instance tag, the
+ * copy will also end with an end of instance tag.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @return copy of the device path
+ */
+static struct efi_device_path * EFIAPI duplicate_device_path(
+ const struct efi_device_path *device_path)
+{
+ EFI_ENTRY("%pD", device_path);
+ return EFI_EXIT(efi_dp_dup(device_path));
+}
+
+/*
+ * Append device path.
+ *
+ * This function implements the AppendDevicePath service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @src1 1st device path
+ * @src2 2nd device path
+ * @return concatenated device path
+ */
+static struct efi_device_path * EFIAPI append_device_path(
+ const struct efi_device_path *src1,
+ const struct efi_device_path *src2)
+{
+ EFI_ENTRY("%pD, %pD", src1, src2);
+ return EFI_EXIT(efi_dp_append(src1, src2));
+}
+
+/*
+ * Append device path node.
+ *
+ * This function implements the AppendDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @device_node device node
+ * @return concatenated device path
+ */
+static struct efi_device_path * EFIAPI append_device_node(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_node)
+{
+ EFI_ENTRY("%pD, %p", device_path, device_node);
+ return EFI_EXIT(efi_dp_append_node(device_path, device_node));
+}
+
+/*
+ * Append device path instance.
+ *
+ * This function implements the AppendDevicePathInstance service of the device
+ * path utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path 1st device path
+ * @device_path_instance 2nd device path
+ * @return concatenated device path
+ */
+static struct efi_device_path * EFIAPI append_device_path_instance(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_path_instance)
+{
+ EFI_ENTRY("%pD, %pD", device_path, device_path_instance);
+ return EFI_EXIT(efi_dp_append_instance(device_path,
+ device_path_instance));
+}
+
+/*
+ * Get next device path instance.
+ *
+ * This function implements the GetNextDevicePathInstance service of the device
+ * path utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path_instance next device path instance
+ * @device_path_instance_size size of the device path instance
+ * @return concatenated device path
+ */
+static struct efi_device_path * EFIAPI get_next_device_path_instance(
+ struct efi_device_path **device_path_instance,
+ efi_uintn_t *device_path_instance_size)
+{
+ EFI_ENTRY("%pD, %p", device_path_instance, device_path_instance_size);
+ return EFI_EXIT(efi_dp_get_next_instance(device_path_instance,
+ device_path_instance_size));
+}
+
+/*
+ * Check if a device path contains more than one instance.
+ *
+ * This function implements the AppendDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @device_node device node
+ * @return concatenated device path
+ */
+static bool EFIAPI is_device_path_multi_instance(
+ const struct efi_device_path *device_path)
+{
+ EFI_ENTRY("%pD", device_path);
+ return EFI_EXIT(efi_dp_is_multi_instance(device_path));
+}
+
+/*
+ * Create device node.
+ *
+ * This function implements the CreateDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @node_type node type
+ * @node_sub_type node sub type
+ * @node_length node length
+ * @return device path node
+ */
+static struct efi_device_path * EFIAPI create_device_node(
+ uint8_t node_type, uint8_t node_sub_type, uint16_t node_length)
+{
+ EFI_ENTRY("%u, %u, %u", node_type, node_sub_type, node_length);
+ return EFI_EXIT(efi_dp_create_device_node(node_type, node_sub_type,
+ node_length));
+}
+
+const struct efi_device_path_utilities_protocol efi_device_path_utilities = {
+ .get_device_path_size = get_device_path_size,
+ .duplicate_device_path = duplicate_device_path,
+ .append_device_path = append_device_path,
+ .append_device_node = append_device_node,
+ .append_device_path_instance = append_device_path_instance,
+ .get_next_device_path_instance = get_next_device_path_instance,
+ .is_device_path_multi_instance = is_device_path_multi_instance,
+ .create_device_node = create_device_node,
+};
diff --git a/roms/u-boot/lib/efi_loader/efi_disk.c b/roms/u-boot/lib/efi_loader/efi_disk.c
new file mode 100644
index 000000000..988907ecb
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_disk.c
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application disk support
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <efi_loader.h>
+#include <fs.h>
+#include <log.h>
+#include <part.h>
+#include <malloc.h>
+
+struct efi_system_partition efi_system_partition;
+
+const efi_guid_t efi_block_io_guid = EFI_BLOCK_IO_PROTOCOL_GUID;
+const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID;
+
+/**
+ * struct efi_disk_obj - EFI disk object
+ *
+ * @header: EFI object header
+ * @ops: EFI disk I/O protocol interface
+ * @ifname: interface name for block device
+ * @dev_index: device index of block device
+ * @media: block I/O media information
+ * @dp: device path to the block device
+ * @part: partition
+ * @volume: simple file system protocol of the partition
+ * @offset: offset into disk for simple partition
+ * @desc: internal block device descriptor
+ */
+struct efi_disk_obj {
+ struct efi_object header;
+ struct efi_block_io ops;
+ const char *ifname;
+ int dev_index;
+ struct efi_block_io_media media;
+ struct efi_device_path *dp;
+ unsigned int part;
+ struct efi_simple_file_system_protocol *volume;
+ lbaint_t offset;
+ struct blk_desc *desc;
+};
+
+/**
+ * efi_disk_reset() - reset block device
+ *
+ * This function implements the Reset service of the EFI_BLOCK_IO_PROTOCOL.
+ *
+ * As U-Boot's block devices do not have a reset function simply return
+ * EFI_SUCCESS.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @extended_verification: extended verification
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this,
+ char extended_verification)
+{
+ EFI_ENTRY("%p, %x", this, extended_verification);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+enum efi_disk_direction {
+ EFI_DISK_READ,
+ EFI_DISK_WRITE,
+};
+
+static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
+ u32 media_id, u64 lba, unsigned long buffer_size,
+ void *buffer, enum efi_disk_direction direction)
+{
+ struct efi_disk_obj *diskobj;
+ struct blk_desc *desc;
+ int blksz;
+ int blocks;
+ unsigned long n;
+
+ diskobj = container_of(this, struct efi_disk_obj, ops);
+ desc = (struct blk_desc *) diskobj->desc;
+ blksz = desc->blksz;
+ blocks = buffer_size / blksz;
+ lba += diskobj->offset;
+
+ EFI_PRINT("blocks=%x lba=%llx blksz=%x dir=%d\n",
+ blocks, lba, blksz, direction);
+
+ /* We only support full block access */
+ if (buffer_size & (blksz - 1))
+ return EFI_BAD_BUFFER_SIZE;
+
+ if (direction == EFI_DISK_READ)
+ n = blk_dread(desc, lba, blocks, buffer);
+ else
+ n = blk_dwrite(desc, lba, blocks, buffer);
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ EFI_PRINT("n=%lx blocks=%x\n", n, blocks);
+
+ if (n != blocks)
+ return EFI_DEVICE_ERROR;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_disk_read_blocks() - reads blocks from device
+ *
+ * This function implements the ReadBlocks service of the EFI_BLOCK_IO_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @media_id: id of the medium to be read from
+ * @lba: starting logical block for reading
+ * @buffer_size: size of the read buffer
+ * @buffer: pointer to the destination buffer
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this,
+ u32 media_id, u64 lba, efi_uintn_t buffer_size,
+ void *buffer)
+{
+ void *real_buffer = buffer;
+ efi_status_t r;
+
+ if (!this)
+ return EFI_INVALID_PARAMETER;
+ /* TODO: check for media changes */
+ if (media_id != this->media->media_id)
+ return EFI_MEDIA_CHANGED;
+ if (!this->media->media_present)
+ return EFI_NO_MEDIA;
+ /* media->io_align is a power of 2 or 0 */
+ if (this->media->io_align &&
+ (uintptr_t)buffer & (this->media->io_align - 1))
+ return EFI_INVALID_PARAMETER;
+ if (lba * this->media->block_size + buffer_size >
+ (this->media->last_block + 1) * this->media->block_size)
+ return EFI_INVALID_PARAMETER;
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+ if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
+ r = efi_disk_read_blocks(this, media_id, lba,
+ EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
+ if (r != EFI_SUCCESS)
+ return r;
+ return efi_disk_read_blocks(this, media_id, lba +
+ EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
+ buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
+ buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
+ }
+
+ real_buffer = efi_bounce_buffer;
+#endif
+
+ EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
+ buffer_size, buffer);
+
+ r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
+ EFI_DISK_READ);
+
+ /* Copy from bounce buffer to real buffer if necessary */
+ if ((r == EFI_SUCCESS) && (real_buffer != buffer))
+ memcpy(buffer, real_buffer, buffer_size);
+
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_disk_write_blocks() - writes blocks to device
+ *
+ * This function implements the WriteBlocks service of the
+ * EFI_BLOCK_IO_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @media_id: id of the medium to be written to
+ * @lba: starting logical block for writing
+ * @buffer_size: size of the write buffer
+ * @buffer: pointer to the source buffer
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this,
+ u32 media_id, u64 lba, efi_uintn_t buffer_size,
+ void *buffer)
+{
+ void *real_buffer = buffer;
+ efi_status_t r;
+
+ if (!this)
+ return EFI_INVALID_PARAMETER;
+ if (this->media->read_only)
+ return EFI_WRITE_PROTECTED;
+ /* TODO: check for media changes */
+ if (media_id != this->media->media_id)
+ return EFI_MEDIA_CHANGED;
+ if (!this->media->media_present)
+ return EFI_NO_MEDIA;
+ /* media->io_align is a power of 2 or 0 */
+ if (this->media->io_align &&
+ (uintptr_t)buffer & (this->media->io_align - 1))
+ return EFI_INVALID_PARAMETER;
+ if (lba * this->media->block_size + buffer_size >
+ (this->media->last_block + 1) * this->media->block_size)
+ return EFI_INVALID_PARAMETER;
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+ if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
+ r = efi_disk_write_blocks(this, media_id, lba,
+ EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
+ if (r != EFI_SUCCESS)
+ return r;
+ return efi_disk_write_blocks(this, media_id, lba +
+ EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
+ buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
+ buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
+ }
+
+ real_buffer = efi_bounce_buffer;
+#endif
+
+ EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
+ buffer_size, buffer);
+
+ /* Populate bounce buffer if necessary */
+ if (real_buffer != buffer)
+ memcpy(real_buffer, buffer, buffer_size);
+
+ r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
+ EFI_DISK_WRITE);
+
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_disk_flush_blocks() - flushes modified data to the device
+ *
+ * This function implements the FlushBlocks service of the
+ * EFI_BLOCK_IO_PROTOCOL.
+ *
+ * As we always write synchronously nothing is done here.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this)
+{
+ EFI_ENTRY("%p", this);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static const struct efi_block_io block_io_disk_template = {
+ .reset = &efi_disk_reset,
+ .read_blocks = &efi_disk_read_blocks,
+ .write_blocks = &efi_disk_write_blocks,
+ .flush_blocks = &efi_disk_flush_blocks,
+};
+
+/**
+ * efi_fs_from_path() - retrieve simple file system protocol
+ *
+ * Gets the simple file system protocol for a file device path.
+ *
+ * The full path provided is split into device part and into a file
+ * part. The device part is used to find the handle on which the
+ * simple file system protocol is installed.
+ *
+ * @full_path: device path including device and file
+ * Return: simple file system protocol
+ */
+struct efi_simple_file_system_protocol *
+efi_fs_from_path(struct efi_device_path *full_path)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+ struct efi_device_path *device_path;
+ struct efi_device_path *file_path;
+ efi_status_t ret;
+
+ /* Split the path into a device part and a file part */
+ ret = efi_dp_split_file_path(full_path, &device_path, &file_path);
+ if (ret != EFI_SUCCESS)
+ return NULL;
+ efi_free_pool(file_path);
+
+ /* Get the EFI object for the partition */
+ efiobj = efi_dp_find_obj(device_path, NULL);
+ efi_free_pool(device_path);
+ if (!efiobj)
+ return NULL;
+
+ /* Find the simple file system protocol */
+ ret = efi_search_protocol(efiobj, &efi_simple_file_system_protocol_guid,
+ &handler);
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ /* Return the simple file system protocol for the partition */
+ return handler->protocol_interface;
+}
+
+/**
+ * efi_fs_exists() - check if a partition bears a file system
+ *
+ * @desc: block device descriptor
+ * @part: partition number
+ * Return: 1 if a file system exists on the partition
+ * 0 otherwise
+ */
+static int efi_fs_exists(struct blk_desc *desc, int part)
+{
+ if (fs_set_blk_dev_with_part(desc, part))
+ return 0;
+
+ if (fs_get_type() == FS_TYPE_ANY)
+ return 0;
+
+ fs_close();
+
+ return 1;
+}
+
+/**
+ * efi_disk_add_dev() - create a handle for a partition or disk
+ *
+ * @parent: parent handle
+ * @dp_parent: parent device path
+ * @if_typename: interface name for block device
+ * @desc: internal block device
+ * @dev_index: device index for block device
+ * @part_info: partition info
+ * @part: partition
+ * @disk: pointer to receive the created handle
+ * Return: disk object
+ */
+static efi_status_t efi_disk_add_dev(
+ efi_handle_t parent,
+ struct efi_device_path *dp_parent,
+ const char *if_typename,
+ struct blk_desc *desc,
+ int dev_index,
+ struct disk_partition *part_info,
+ unsigned int part,
+ struct efi_disk_obj **disk)
+{
+ struct efi_disk_obj *diskobj;
+ struct efi_object *handle;
+ const efi_guid_t *guid = NULL;
+ efi_status_t ret;
+
+ /* Don't add empty devices */
+ if (!desc->lba)
+ return EFI_NOT_READY;
+
+ diskobj = calloc(1, sizeof(*diskobj));
+ if (!diskobj)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Hook up to the device list */
+ efi_add_handle(&diskobj->header);
+
+ /* Fill in object data */
+ if (part_info) {
+ struct efi_device_path *node = efi_dp_part_node(desc, part);
+ struct efi_handler *handler;
+ void *protocol_interface;
+
+ /* Parent must expose EFI_BLOCK_IO_PROTOCOL */
+ ret = efi_search_protocol(parent, &efi_block_io_guid, &handler);
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ /*
+ * Link the partition (child controller) to the block device
+ * (controller).
+ */
+ ret = efi_protocol_open(handler, &protocol_interface, NULL,
+ &diskobj->header,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ diskobj->dp = efi_dp_append_node(dp_parent, node);
+ efi_free_pool(node);
+ diskobj->offset = part_info->start;
+ diskobj->media.last_block = part_info->size - 1;
+ if (part_info->bootable & PART_EFI_SYSTEM_PARTITION)
+ guid = &efi_system_partition_guid;
+ } else {
+ diskobj->dp = efi_dp_from_part(desc, part);
+ diskobj->offset = 0;
+ diskobj->media.last_block = desc->lba - 1;
+ }
+ diskobj->part = part;
+
+ /*
+ * Install the device path and the block IO protocol.
+ *
+ * InstallMultipleProtocolInterfaces() checks if the device path is
+ * already installed on an other handle and returns EFI_ALREADY_STARTED
+ * in this case.
+ */
+ handle = &diskobj->header;
+ ret = EFI_CALL(efi_install_multiple_protocol_interfaces(
+ &handle, &efi_guid_device_path, diskobj->dp,
+ &efi_block_io_guid, &diskobj->ops,
+ guid, NULL, NULL));
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /*
+ * On partitions or whole disks without partitions install the
+ * simple file system protocol if a file system is available.
+ */
+ if ((part || desc->part_type == PART_TYPE_UNKNOWN) &&
+ efi_fs_exists(desc, part)) {
+ diskobj->volume = efi_simple_file_system(desc, part,
+ diskobj->dp);
+ ret = efi_add_protocol(&diskobj->header,
+ &efi_simple_file_system_protocol_guid,
+ diskobj->volume);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+ diskobj->ops = block_io_disk_template;
+ diskobj->ifname = if_typename;
+ diskobj->dev_index = dev_index;
+ diskobj->desc = desc;
+
+ /* Fill in EFI IO Media info (for read/write callbacks) */
+ diskobj->media.removable_media = desc->removable;
+ diskobj->media.media_present = 1;
+ /*
+ * MediaID is just an arbitrary counter.
+ * We have to change it if the medium is removed or changed.
+ */
+ diskobj->media.media_id = 1;
+ diskobj->media.block_size = desc->blksz;
+ diskobj->media.io_align = desc->blksz;
+ if (part)
+ diskobj->media.logical_partition = 1;
+ diskobj->ops.media = &diskobj->media;
+ if (disk)
+ *disk = diskobj;
+
+ EFI_PRINT("BlockIO: part %u, present %d, logical %d, removable %d"
+ ", offset " LBAF ", last_block %llu\n",
+ diskobj->part,
+ diskobj->media.media_present,
+ diskobj->media.logical_partition,
+ diskobj->media.removable_media,
+ diskobj->offset,
+ diskobj->media.last_block);
+
+ /* Store first EFI system partition */
+ if (part && !efi_system_partition.if_type) {
+ if (part_info->bootable & PART_EFI_SYSTEM_PARTITION) {
+ efi_system_partition.if_type = desc->if_type;
+ efi_system_partition.devnum = desc->devnum;
+ efi_system_partition.part = part;
+ EFI_PRINT("EFI system partition: %s %x:%x\n",
+ blk_get_if_type_name(desc->if_type),
+ desc->devnum, part);
+ }
+ }
+ return EFI_SUCCESS;
+error:
+ efi_delete_handle(&diskobj->header);
+ return ret;
+}
+
+/**
+ * efi_disk_create_partitions() - create handles and protocols for partitions
+ *
+ * Create handles and protocols for the partitions of a block device.
+ *
+ * @parent: handle of the parent disk
+ * @desc: block device
+ * @if_typename: interface type
+ * @diskid: device number
+ * @pdevname: device name
+ * Return: number of partitions created
+ */
+int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
+ const char *if_typename, int diskid,
+ const char *pdevname)
+{
+ int disks = 0;
+ char devname[32] = { 0 }; /* dp->str is u16[32] long */
+ int part;
+ struct efi_device_path *dp = NULL;
+ efi_status_t ret;
+ struct efi_handler *handler;
+
+ /* Get the device path of the parent */
+ ret = efi_search_protocol(parent, &efi_guid_device_path, &handler);
+ if (ret == EFI_SUCCESS)
+ dp = handler->protocol_interface;
+
+ /* Add devices for each partition */
+ for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
+ struct disk_partition info;
+
+ if (part_get_info(desc, part, &info))
+ continue;
+ snprintf(devname, sizeof(devname), "%s:%x", pdevname,
+ part);
+ ret = efi_disk_add_dev(parent, dp, if_typename, desc, diskid,
+ &info, part, NULL);
+ if (ret != EFI_SUCCESS) {
+ log_err("Adding partition %s failed\n", pdevname);
+ continue;
+ }
+ disks++;
+ }
+
+ return disks;
+}
+
+/**
+ * efi_disk_register() - register block devices
+ *
+ * U-Boot doesn't have a list of all online disk devices. So when running our
+ * EFI payload, we scan through all of the potentially available ones and
+ * store them in our object pool.
+ *
+ * This function is called in efi_init_obj_list().
+ *
+ * TODO(sjg@chromium.org): Actually with CONFIG_BLK, U-Boot does have this.
+ * Consider converting the code to look up devices as needed. The EFI device
+ * could be a child of the UCLASS_BLK block device, perhaps.
+ *
+ * Return: status code
+ */
+efi_status_t efi_disk_register(void)
+{
+ struct efi_disk_obj *disk;
+ int disks = 0;
+ efi_status_t ret;
+#ifdef CONFIG_BLK
+ struct udevice *dev;
+
+ for (uclass_first_device_check(UCLASS_BLK, &dev); dev;
+ uclass_next_device_check(&dev)) {
+ struct blk_desc *desc = dev_get_uclass_plat(dev);
+ const char *if_typename = blk_get_if_type_name(desc->if_type);
+
+ /* Add block device for the full device */
+ log_info("Scanning disk %s...\n", dev->name);
+ ret = efi_disk_add_dev(NULL, NULL, if_typename,
+ desc, desc->devnum, NULL, 0, &disk);
+ if (ret == EFI_NOT_READY) {
+ log_notice("Disk %s not ready\n", dev->name);
+ continue;
+ }
+ if (ret) {
+ log_err("ERROR: failure to add disk device %s, r = %lu\n",
+ dev->name, ret & ~EFI_ERROR_MASK);
+ return ret;
+ }
+ disks++;
+
+ /* Partitions show up as block devices in EFI */
+ disks += efi_disk_create_partitions(
+ &disk->header, desc, if_typename,
+ desc->devnum, dev->name);
+ }
+#else
+ int i, if_type;
+
+ /* Search for all available disk devices */
+ for (if_type = 0; if_type < IF_TYPE_COUNT; if_type++) {
+ const struct blk_driver *cur_drvr;
+ const char *if_typename;
+
+ cur_drvr = blk_driver_lookup_type(if_type);
+ if (!cur_drvr)
+ continue;
+
+ if_typename = cur_drvr->if_typename;
+ log_info("Scanning disks on %s...\n", if_typename);
+ for (i = 0; i < 4; i++) {
+ struct blk_desc *desc;
+ char devname[32] = { 0 }; /* dp->str is u16[32] long */
+
+ desc = blk_get_devnum_by_type(if_type, i);
+ if (!desc)
+ continue;
+ if (desc->type == DEV_TYPE_UNKNOWN)
+ continue;
+
+ snprintf(devname, sizeof(devname), "%s%d",
+ if_typename, i);
+
+ /* Add block device for the full device */
+ ret = efi_disk_add_dev(NULL, NULL, if_typename, desc,
+ i, NULL, 0, &disk);
+ if (ret == EFI_NOT_READY) {
+ log_notice("Disk %s not ready\n", devname);
+ continue;
+ }
+ if (ret) {
+ log_err("ERROR: failure to add disk device %s, r = %lu\n",
+ devname, ret & ~EFI_ERROR_MASK);
+ return ret;
+ }
+ disks++;
+
+ /* Partitions show up as block devices in EFI */
+ disks += efi_disk_create_partitions
+ (&disk->header, desc,
+ if_typename, i, devname);
+ }
+ }
+#endif
+ log_info("Found %d disks\n", disks);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_disk_is_system_part() - check if handle refers to an EFI system partition
+ *
+ * @handle: handle of partition
+ *
+ * Return: true if handle refers to an EFI system partition
+ */
+bool efi_disk_is_system_part(efi_handle_t handle)
+{
+ struct efi_handler *handler;
+ struct efi_disk_obj *diskobj;
+ struct disk_partition info;
+ efi_status_t ret;
+ int r;
+
+ /* check if this is a block device */
+ ret = efi_search_protocol(handle, &efi_block_io_guid, &handler);
+ if (ret != EFI_SUCCESS)
+ return false;
+
+ diskobj = container_of(handle, struct efi_disk_obj, header);
+
+ r = part_get_info(diskobj->desc, diskobj->part, &info);
+ if (r)
+ return false;
+
+ return !!(info.bootable & PART_EFI_SYSTEM_PARTITION);
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_dt_fixup.c b/roms/u-boot/lib/efi_loader/efi_dt_fixup.c
new file mode 100644
index 000000000..b6fe5d2e5
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_dt_fixup.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI_DT_FIXUP_PROTOCOL
+ *
+ * Copyright (c) 2020 Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <efi_dt_fixup.h>
+#include <efi_loader.h>
+#include <fdtdec.h>
+#include <mapmem.h>
+
+const efi_guid_t efi_guid_dt_fixup_protocol = EFI_DT_FIXUP_PROTOCOL_GUID;
+
+/**
+ * efi_reserve_memory() - add reserved memory to memory map
+ *
+ * @addr: start address of the reserved memory range
+ * @size: size of the reserved memory range
+ * @nomap: indicates that the memory range shall not be accessed by the
+ * UEFI payload
+ */
+static void efi_reserve_memory(u64 addr, u64 size, bool nomap)
+{
+ int type;
+ efi_uintn_t ret;
+
+ /* Convert from sandbox address space. */
+ addr = (uintptr_t)map_sysmem(addr, 0);
+
+ if (nomap)
+ type = EFI_RESERVED_MEMORY_TYPE;
+ else
+ type = EFI_BOOT_SERVICES_DATA;
+
+ ret = efi_add_memory_map(addr, size, type);
+ if (ret != EFI_SUCCESS)
+ log_err("Reserved memory mapping failed addr %llx size %llx\n",
+ addr, size);
+}
+
+/**
+ * efi_carve_out_dt_rsv() - Carve out DT reserved memory ranges
+ *
+ * The mem_rsv entries of the FDT are added to the memory map. Any failures are
+ * ignored because this is not critical and we would rather continue to try to
+ * boot.
+ *
+ * @fdt: Pointer to device tree
+ */
+void efi_carve_out_dt_rsv(void *fdt)
+{
+ int nr_rsv, i;
+ u64 addr, size;
+ int nodeoffset, subnode;
+
+ nr_rsv = fdt_num_mem_rsv(fdt);
+
+ /* Look for an existing entry and add it to the efi mem map. */
+ for (i = 0; i < nr_rsv; i++) {
+ if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0)
+ continue;
+ efi_reserve_memory(addr, size, true);
+ }
+
+ /* process reserved-memory */
+ nodeoffset = fdt_subnode_offset(fdt, 0, "reserved-memory");
+ if (nodeoffset >= 0) {
+ subnode = fdt_first_subnode(fdt, nodeoffset);
+ while (subnode >= 0) {
+ fdt_addr_t fdt_addr;
+ fdt_size_t fdt_size;
+
+ /* check if this subnode has a reg property */
+ fdt_addr = fdtdec_get_addr_size_auto_parent(
+ fdt, nodeoffset, subnode,
+ "reg", 0, &fdt_size, false);
+ /*
+ * The /reserved-memory node may have children with
+ * a size instead of a reg property.
+ */
+ if (fdt_addr != FDT_ADDR_T_NONE &&
+ fdtdec_get_is_enabled(fdt, subnode)) {
+ bool nomap;
+
+ nomap = !!fdt_getprop(fdt, subnode, "no-map",
+ NULL);
+ efi_reserve_memory(fdt_addr, fdt_size, nomap);
+ }
+ subnode = fdt_next_subnode(fdt, subnode);
+ }
+ }
+}
+
+/**
+ * efi_dt_fixup() - fix up device tree
+ *
+ * This function implements the Fixup() service of the
+ * EFI Device Tree Fixup Protocol.
+ *
+ * @this: instance of the protocol
+ * @dtb: device tree provided by caller
+ * @buffer_size: size of buffer for the device tree including free space
+ * @flags: bit field designating action to be performed
+ * Return: status code
+ */
+static efi_status_t __maybe_unused EFIAPI
+efi_dt_fixup(struct efi_dt_fixup_protocol *this, void *dtb,
+ efi_uintn_t *buffer_size, u32 flags)
+{
+ efi_status_t ret;
+ size_t required_size;
+ size_t total_size;
+ bootm_headers_t img = { 0 };
+
+ EFI_ENTRY("%p, %p, %p, %d", this, dtb, buffer_size, flags);
+
+ if (this != &efi_dt_fixup_prot || !dtb || !buffer_size ||
+ !flags || (flags & ~EFI_DT_ALL)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (fdt_check_header(dtb)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (flags & EFI_DT_APPLY_FIXUPS) {
+ /* Check size */
+ required_size = fdt_off_dt_strings(dtb) +
+ fdt_size_dt_strings(dtb) +
+ 0x3000;
+ total_size = fdt_totalsize(dtb);
+ if (required_size < total_size)
+ required_size = total_size;
+ if (required_size > *buffer_size) {
+ *buffer_size = required_size;
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
+
+ fdt_set_totalsize(dtb, *buffer_size);
+ if (image_setup_libfdt(&img, dtb, 0, NULL)) {
+ log_err("failed to process device tree\n");
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ if (flags & EFI_DT_RESERVE_MEMORY)
+ efi_carve_out_dt_rsv(dtb);
+
+ if (flags & EFI_DT_INSTALL_TABLE) {
+ ret = efi_install_configuration_table(&efi_guid_fdt, dtb);
+ if (ret != EFI_SUCCESS) {
+ log_err("failed to install device tree\n");
+ goto out;
+ }
+ }
+
+ ret = EFI_SUCCESS;
+out:
+ return EFI_EXIT(ret);
+}
+
+struct efi_dt_fixup_protocol efi_dt_fixup_prot = {
+ .revision = EFI_DT_FIXUP_PROTOCOL_REVISION,
+ .fixup = efi_dt_fixup
+};
diff --git a/roms/u-boot/lib/efi_loader/efi_esrt.c b/roms/u-boot/lib/efi_loader/efi_esrt.c
new file mode 100644
index 000000000..3ca55ce23
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_esrt.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * EFI application ESRT tables support
+ *
+ * Copyright (C) 2021 Arm Ltd.
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <efi_api.h>
+#include <malloc.h>
+
+const efi_guid_t efi_esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
+
+static struct efi_system_resource_table *esrt;
+
+#define EFI_ESRT_VERSION 1
+
+/**
+ * efi_esrt_image_info_to_entry() - copy the information present in a fw image
+ * descriptor to a ESRT entry.
+ * The function ensures the ESRT entry matches the image_type_id in @img_info.
+ * In case of a mismatch we leave the entry unchanged.
+ *
+ * @img_info: the source image info descriptor
+ * @entry: pointer to the ESRT entry to be filled
+ * @desc_version: the version of the elements in img_info
+ * @image_type: the image type value to be set in the ESRT entry
+ * @flags: the capsule flags value to be set in the ESRT entry
+ *
+ * Return:
+ * - EFI_SUCCESS if the entry is correctly updated
+ * - EFI_INVALID_PARAMETER if entry does not match image_type_id in @img_info.
+ */
+static efi_status_t
+efi_esrt_image_info_to_entry(struct efi_firmware_image_descriptor *img_info,
+ struct efi_system_resource_entry *entry,
+ u32 desc_version, u32 image_type, u32 flags)
+{
+ if (guidcmp(&entry->fw_class, &img_info->image_type_id)) {
+ EFI_PRINT("ESRT entry %pUL mismatches img_type_id %pUL\n",
+ &entry->fw_class, &img_info->image_type_id);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ entry->fw_version = img_info->version;
+
+ entry->fw_type = image_type;
+ entry->capsule_flags = flags;
+
+ /*
+ * The field lowest_supported_image_version is only present
+ * on image info structure of version 2 or greater.
+ * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
+ */
+ if (desc_version >= 2)
+ entry->lowest_supported_fw_version =
+ img_info->lowest_supported_image_version;
+ else
+ entry->lowest_supported_fw_version = 0;
+
+ /*
+ * The fields last_attempt_version and last_attempt_status
+ * are only present on image info structure of version 3 or
+ * greater.
+ * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
+ */
+ if (desc_version >= 3) {
+ entry->last_attempt_version =
+ img_info->last_attempt_version;
+
+ entry->last_attempt_status =
+ img_info->last_attempt_status;
+ } else {
+ entry->last_attempt_version = 0;
+ entry->last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_esrt_entries_to_size() - Obtain the bytes used by an ESRT
+ * datastructure with @num_entries.
+ *
+ * @num_entries: the number of entries in the ESRT.
+ *
+ * Return: the number of bytes an ESRT with @num_entries occupies in memory.
+ */
+static
+inline u32 efi_esrt_entries_to_size(u32 num_entries)
+{
+ u32 esrt_size = sizeof(struct efi_system_resource_table) +
+ num_entries * sizeof(struct efi_system_resource_entry);
+
+ return esrt_size;
+}
+
+/**
+ * efi_esrt_allocate_install() - Allocates @num_entries for the ESRT and
+ * performs basic ESRT initialization.
+ *
+ * @num_entries: the number of entries that the ESRT will hold.
+ *
+ * Return:
+ * - pointer to the ESRT if successful.
+ * - NULL otherwise.
+ */
+static
+efi_status_t efi_esrt_allocate_install(u32 num_entries)
+{
+ efi_status_t ret;
+ struct efi_system_resource_table *new_esrt;
+ u32 size = efi_esrt_entries_to_size(num_entries);
+ efi_guid_t esrt_guid = efi_esrt_guid;
+
+ /* Reserve num_pages for ESRT */
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, size,
+ (void **)&new_esrt);
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT cannot allocate memory for %u entries (%u bytes)\n",
+ num_entries, size);
+
+ return ret;
+ }
+
+ new_esrt->fw_resource_count_max = num_entries;
+ new_esrt->fw_resource_count = 0;
+ new_esrt->fw_resource_version = EFI_ESRT_VERSION;
+
+ /* Install the ESRT in the system configuration table. */
+ ret = efi_install_configuration_table(&esrt_guid, (void *)new_esrt);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to install the ESRT in the system table\n");
+ return ret;
+ }
+
+ /* If there was a previous ESRT, deallocate its memory now. */
+ if (esrt)
+ ret = efi_free_pool(esrt);
+
+ esrt = new_esrt;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * esrt_find_entry() - Obtain the ESRT entry for the image with GUID
+ * @img_fw_class.
+ *
+ * If the img_fw_class is not yet present in the ESRT, this function
+ * reserves the tail element of the current ESRT as the entry for that fw_class.
+ * The number of elements in the ESRT is updated in that case.
+ *
+ * @img_fw_class: the GUID of the FW image which ESRT entry we want to obtain.
+ *
+ * Return:
+ * - A pointer to the ESRT entry for the image with GUID img_fw_class,
+ * - NULL if:
+ * - there is no more space in the ESRT,
+ * - ESRT is not initialized,
+ */
+static
+struct efi_system_resource_entry *esrt_find_entry(efi_guid_t *img_fw_class)
+{
+ u32 filled_entries;
+ u32 max_entries;
+ struct efi_system_resource_entry *entry;
+
+ if (!esrt) {
+ EFI_PRINT("ESRT access before initialized\n");
+ return NULL;
+ }
+
+ filled_entries = esrt->fw_resource_count;
+ entry = esrt->entries;
+
+ /* Check if the image with img_fw_class is already in the ESRT. */
+ for (u32 idx = 0; idx < filled_entries; idx++) {
+ if (!guidcmp(&entry[idx].fw_class, img_fw_class)) {
+ EFI_PRINT("ESRT found entry for image %pUl at index %u\n",
+ img_fw_class, idx);
+ return &entry[idx];
+ }
+ }
+
+ max_entries = esrt->fw_resource_count_max;
+ /*
+ * Since the image with img_fw_class is not present in the ESRT, check
+ * if ESRT is full before appending the new entry to it.
+ */
+ if (filled_entries == max_entries) {
+ EFI_PRINT("ESRT full, this should not happen\n");
+ return NULL;
+ }
+
+ /*
+ * This is a new entry for a fw image, increment the element
+ * number in the table and set the fw_class field.
+ */
+ esrt->fw_resource_count++;
+ entry[filled_entries].fw_class = *img_fw_class;
+ EFI_PRINT("ESRT allocated new entry for image %pUl at index %u\n",
+ img_fw_class, filled_entries);
+
+ return &entry[filled_entries];
+}
+
+/**
+ * efi_esrt_add_from_fmp() - Populates a sequence of ESRT entries from the FW
+ * images in the FMP.
+ *
+ * @fmp: the FMP instance from which FW images are added to the ESRT
+ *
+ * Return:
+ * - EFI_SUCCESS if all the FW images in the FMP are added to the ESRT
+ * - Error status otherwise
+ */
+static
+efi_status_t efi_esrt_add_from_fmp(struct efi_firmware_management_protocol *fmp)
+{
+ struct efi_system_resource_entry *entry = NULL;
+ size_t info_size = 0;
+ struct efi_firmware_image_descriptor *img_info = NULL;
+ u32 desc_version;
+ u8 desc_count;
+ size_t desc_size;
+ u32 package_version;
+ u16 *package_version_name;
+ efi_status_t ret = EFI_SUCCESS;
+
+ /*
+ * TODO: set the field image_type depending on the FW image type
+ * defined in a platform basis.
+ */
+ u32 image_type = ESRT_FW_TYPE_UNKNOWN;
+
+ /* TODO: set the capsule flags as a function of the FW image type. */
+ u32 flags = 0;
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+ &desc_version, &desc_count,
+ &desc_size, NULL, NULL));
+
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ /*
+ * An input of info_size=0 should always lead
+ * fmp->get_image_info to return BUFFER_TO_SMALL.
+ */
+ EFI_PRINT("Erroneous FMP implementation\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
+ (void **)&img_info);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to allocate memory for image info.\n");
+ return ret;
+ }
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+ &desc_version, &desc_count,
+ &desc_size, &package_version,
+ &package_version_name));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to obtain the FMP image info\n");
+ goto out;
+ }
+
+ /*
+ * Iterate over all the FW images in the FMP.
+ */
+ for (u32 desc_idx = 0; desc_idx < desc_count; desc_idx++) {
+ struct efi_firmware_image_descriptor *cur_img_info =
+ (struct efi_firmware_image_descriptor *)
+ ((uintptr_t)img_info + desc_idx * desc_size);
+
+ /*
+ * Obtain the ESRT entry for the FW image with fw_class
+ * equal to cur_img_info->image_type_id.
+ */
+ entry = esrt_find_entry(&cur_img_info->image_type_id);
+
+ if (entry) {
+ ret = efi_esrt_image_info_to_entry(cur_img_info, entry,
+ desc_version,
+ image_type, flags);
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("ESRT entry mismatches image_type\n");
+
+ } else {
+ EFI_PRINT("ESRT failed to add entry for %pUl\n",
+ &cur_img_info->image_type_id);
+ continue;
+ }
+ }
+
+out:
+ efi_free_pool(img_info);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_esrt_populate() - Populates the ESRT entries from the FMP instances
+ * present in the system.
+ * If an ESRT already exists, the old ESRT is replaced in the system table.
+ * The memory of the old ESRT is deallocated.
+ *
+ * Return:
+ * - EFI_SUCCESS if the ESRT is correctly created
+ * - error code otherwise.
+ */
+efi_status_t efi_esrt_populate(void)
+{
+ efi_handle_t *base_handle = NULL;
+ efi_handle_t *it_handle;
+ efi_uintn_t no_handles = 0;
+ struct efi_firmware_management_protocol *fmp;
+ efi_status_t ret;
+ u32 num_entries = 0;
+ struct efi_handler *handler;
+
+ /*
+ * Obtain the number of registered FMP handles.
+ */
+ ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
+ &efi_guid_firmware_management_protocol,
+ NULL, &no_handles,
+ (efi_handle_t **)&base_handle));
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT There are no FMP instances\n");
+
+ ret = efi_esrt_allocate_install(0);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to create table with 0 entries\n");
+ return ret;
+ }
+ return EFI_SUCCESS;
+ }
+
+ EFI_PRINT("ESRT populate esrt from (%zd) available FMP handles\n",
+ no_handles);
+
+ /*
+ * Iterate over all FMPs to determine an upper bound on the number of
+ * ESRT entries.
+ */
+ it_handle = base_handle;
+ for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
+ struct efi_firmware_image_descriptor *img_info = NULL;
+ size_t info_size = 0;
+ u32 desc_version = 0;
+ u8 desc_count = 0;
+ size_t desc_size = 0;
+ u32 package_version;
+ u16 *package_version_name;
+
+ ret = efi_search_protocol(*it_handle,
+ &efi_guid_firmware_management_protocol,
+ &handler);
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT Unable to find FMP handle (%u)\n",
+ idx);
+ goto out;
+ }
+ fmp = handler->protocol_interface;
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, NULL,
+ &desc_version, &desc_count,
+ &desc_size, &package_version,
+ &package_version_name));
+
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ /*
+ * An input of info_size=0 should always lead
+ * fmp->get_image_info to return BUFFER_TO_SMALL.
+ */
+ EFI_PRINT("ESRT erroneous FMP implementation\n");
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
+ (void **)&img_info);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to allocate memory for image info\n");
+ goto out;
+ }
+
+ /*
+ * Calls to a FMP get_image_info method do not return the
+ * desc_count value if the return status differs from EFI_SUCCESS.
+ * We need to repeat the call to get_image_info with a properly
+ * sized buffer in order to obtain the real number of images
+ * handled by the FMP.
+ */
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+ &desc_version, &desc_count,
+ &desc_size, &package_version,
+ &package_version_name));
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to obtain image info from FMP\n");
+ efi_free_pool(img_info);
+ goto out;
+ }
+
+ num_entries += desc_count;
+
+ efi_free_pool(img_info);
+ }
+
+ EFI_PRINT("ESRT create table with %u entries\n", num_entries);
+ /*
+ * Allocate an ESRT with the sufficient number of entries to accommodate
+ * all the FMPs in the system.
+ */
+ ret = efi_esrt_allocate_install(num_entries);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to initialize table\n");
+ goto out;
+ }
+
+ /*
+ * Populate the ESRT entries with all existing FMP.
+ */
+ it_handle = base_handle;
+ for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
+ ret = efi_search_protocol(*it_handle,
+ &efi_guid_firmware_management_protocol,
+ &handler);
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT unable to find FMP handle (%u)\n",
+ idx);
+ break;
+ }
+ fmp = handler->protocol_interface;
+
+ ret = efi_esrt_add_from_fmp(fmp);
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("ESRT failed to add FMP to the table\n");
+ }
+
+out:
+
+ efi_free_pool(base_handle);
+
+ return ret;
+}
+
+/**
+ * efi_esrt_new_fmp_notify() - Callback for the EVT_NOTIFY_SIGNAL event raised
+ * when a new FMP protocol instance is registered in the system.
+ */
+static void EFIAPI efi_esrt_new_fmp_notify(struct efi_event *event,
+ void *context)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY();
+
+ ret = efi_esrt_populate();
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("ESRT failed to populate ESRT entry\n");
+
+ EFI_EXIT(ret);
+}
+
+/**
+ * efi_esrt_register() - Install the ESRT system table.
+ *
+ * Return: status code
+ */
+efi_status_t efi_esrt_register(void)
+{
+ struct efi_event *ev = NULL;
+ void *registration;
+ efi_status_t ret;
+
+ EFI_PRINT("ESRT creation start\n");
+
+ ret = efi_esrt_populate();
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to initiate the table\n");
+ return ret;
+ }
+
+ ret = efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ efi_esrt_new_fmp_notify, NULL, NULL, &ev);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to create event\n");
+ return ret;
+ }
+
+ ret = EFI_CALL(efi_register_protocol_notify(&efi_guid_firmware_management_protocol,
+ ev, &registration));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to register FMP callback\n");
+ return ret;
+ }
+
+ EFI_PRINT("ESRT table created\n");
+
+ return ret;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_file.c b/roms/u-boot/lib/efi_loader/efi_file.c
new file mode 100644
index 000000000..6b3f5962b
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_file.c
@@ -0,0 +1,1153 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI_FILE_PROTOCOL
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <fs.h>
+#include <part.h>
+
+/* GUID for file system information */
+const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
+
+/* GUID to obtain the volume label */
+const efi_guid_t efi_system_volume_label_id = EFI_FILE_SYSTEM_VOLUME_LABEL_ID;
+
+struct file_system {
+ struct efi_simple_file_system_protocol base;
+ struct efi_device_path *dp;
+ struct blk_desc *desc;
+ int part;
+};
+#define to_fs(x) container_of(x, struct file_system, base)
+
+struct file_handle {
+ struct efi_file_handle base;
+ struct file_system *fs;
+ loff_t offset; /* current file position/cursor */
+ int isdir;
+ u64 open_mode;
+
+ /* for reading a directory: */
+ struct fs_dir_stream *dirs;
+ struct fs_dirent *dent;
+
+ char path[0];
+};
+#define to_fh(x) container_of(x, struct file_handle, base)
+
+static const struct efi_file_handle efi_file_handle_protocol;
+
+static char *basename(struct file_handle *fh)
+{
+ char *s = strrchr(fh->path, '/');
+ if (s)
+ return s + 1;
+ return fh->path;
+}
+
+static int set_blk_dev(struct file_handle *fh)
+{
+ return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
+}
+
+/**
+ * is_dir() - check if file handle points to directory
+ *
+ * We assume that set_blk_dev(fh) has been called already.
+ *
+ * @fh: file handle
+ * Return: true if file handle points to a directory
+ */
+static int is_dir(struct file_handle *fh)
+{
+ struct fs_dir_stream *dirs;
+
+ dirs = fs_opendir(fh->path);
+ if (!dirs)
+ return 0;
+
+ fs_closedir(dirs);
+
+ return 1;
+}
+
+/*
+ * Normalize a path which may include either back or fwd slashes,
+ * double slashes, . or .. entries in the path, etc.
+ */
+static int sanitize_path(char *path)
+{
+ char *p;
+
+ /* backslash to slash: */
+ p = path;
+ while ((p = strchr(p, '\\')))
+ *p++ = '/';
+
+ /* handle double-slashes: */
+ p = path;
+ while ((p = strstr(p, "//"))) {
+ char *src = p + 1;
+ memmove(p, src, strlen(src) + 1);
+ }
+
+ /* handle extra /.'s */
+ p = path;
+ while ((p = strstr(p, "/."))) {
+ /*
+ * You'd be tempted to do this *after* handling ".."s
+ * below to avoid having to check if "/." is start of
+ * a "/..", but that won't have the correct results..
+ * for example, "/foo/./../bar" would get resolved to
+ * "/foo/bar" if you did these two passes in the other
+ * order
+ */
+ if (p[2] == '.') {
+ p += 2;
+ continue;
+ }
+ char *src = p + 2;
+ memmove(p, src, strlen(src) + 1);
+ }
+
+ /* handle extra /..'s: */
+ p = path;
+ while ((p = strstr(p, "/.."))) {
+ char *src = p + 3;
+
+ p--;
+
+ /* find beginning of previous path entry: */
+ while (true) {
+ if (p < path)
+ return -1;
+ if (*p == '/')
+ break;
+ p--;
+ }
+
+ memmove(p, src, strlen(src) + 1);
+ }
+
+ return 0;
+}
+
+/**
+ * efi_create_file() - create file or directory
+ *
+ * @fh: file handle
+ * @attributes: attributes for newly created file
+ * Returns: 0 for success
+ */
+static int efi_create_file(struct file_handle *fh, u64 attributes)
+{
+ loff_t actwrite;
+ void *buffer = &actwrite;
+
+ if (attributes & EFI_FILE_DIRECTORY)
+ return fs_mkdir(fh->path);
+ else
+ return fs_write(fh->path, map_to_sysmem(buffer), 0, 0,
+ &actwrite);
+}
+
+/**
+ * file_open() - open a file handle
+ *
+ * @fs: file system
+ * @parent: directory relative to which the file is to be opened
+ * @file_name: path of the file to be opened. '\', '.', or '..' may
+ * be used as modifiers. A leading backslash indicates an
+ * absolute path.
+ * @open_mode: bit mask indicating the access mode (read, write,
+ * create)
+ * @attributes: attributes for newly created file
+ * Returns: handle to the opened file or NULL
+ */
+static struct efi_file_handle *file_open(struct file_system *fs,
+ struct file_handle *parent, u16 *file_name, u64 open_mode,
+ u64 attributes)
+{
+ struct file_handle *fh;
+ char f0[MAX_UTF8_PER_UTF16] = {0};
+ int plen = 0;
+ int flen = 0;
+
+ if (file_name) {
+ utf16_to_utf8((u8 *)f0, file_name, 1);
+ flen = u16_strlen(file_name);
+ }
+
+ /* we could have a parent, but also an absolute path: */
+ if (f0[0] == '\\') {
+ plen = 0;
+ } else if (parent) {
+ plen = strlen(parent->path) + 1;
+ }
+
+ /* +2 is for null and '/' */
+ fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
+
+ fh->open_mode = open_mode;
+ fh->base = efi_file_handle_protocol;
+ fh->fs = fs;
+
+ if (parent) {
+ char *p = fh->path;
+ int exists;
+
+ if (plen > 0) {
+ strcpy(p, parent->path);
+ p += plen - 1;
+ *p++ = '/';
+ }
+
+ utf16_to_utf8((u8 *)p, file_name, flen);
+
+ if (sanitize_path(fh->path))
+ goto error;
+
+ /* check if file exists: */
+ if (set_blk_dev(fh))
+ goto error;
+
+ exists = fs_exists(fh->path);
+ /* fs_exists() calls fs_close(), so open file system again */
+ if (set_blk_dev(fh))
+ goto error;
+
+ if (!exists) {
+ if (!(open_mode & EFI_FILE_MODE_CREATE) ||
+ efi_create_file(fh, attributes))
+ goto error;
+ if (set_blk_dev(fh))
+ goto error;
+ }
+
+ /* figure out if file is a directory: */
+ fh->isdir = is_dir(fh);
+ } else {
+ fh->isdir = 1;
+ strcpy(fh->path, "");
+ }
+
+ return &fh->base;
+
+error:
+ free(fh);
+ return NULL;
+}
+
+static efi_status_t efi_file_open_int(struct efi_file_handle *this,
+ struct efi_file_handle **new_handle,
+ u16 *file_name, u64 open_mode,
+ u64 attributes)
+{
+ struct file_handle *fh = to_fh(this);
+ efi_status_t ret;
+
+ /* Check parameters */
+ if (!this || !new_handle || !file_name) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (open_mode != EFI_FILE_MODE_READ &&
+ open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
+ open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
+ EFI_FILE_MODE_CREATE)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /*
+ * The UEFI spec requires that attributes are only set in create mode.
+ * The SCT does not care about this and sets EFI_FILE_DIRECTORY in
+ * read mode. EDK2 does not check that attributes are zero if not in
+ * create mode.
+ *
+ * So here we only check attributes in create mode and do not check
+ * that they are zero otherwise.
+ */
+ if ((open_mode & EFI_FILE_MODE_CREATE) &&
+ (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Open file */
+ *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
+ if (*new_handle) {
+ EFI_PRINT("file handle %p\n", *new_handle);
+ ret = EFI_SUCCESS;
+ } else {
+ ret = EFI_NOT_FOUND;
+ }
+out:
+ return ret;
+}
+
+/**
+ * efi_file_open_()
+ *
+ * This function implements the Open service of the File Protocol.
+ * See the UEFI spec for details.
+ *
+ * @this: EFI_FILE_PROTOCOL instance
+ * @new_handle: on return pointer to file handle
+ * @file_name: file name
+ * @open_mode: mode to open the file (read, read/write, create/read/write)
+ * @attributes: attributes for newly created file
+ */
+static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *this,
+ struct efi_file_handle **new_handle,
+ u16 *file_name, u64 open_mode,
+ u64 attributes)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", this, new_handle,
+ file_name, open_mode, attributes);
+
+ ret = efi_file_open_int(this, new_handle, file_name, open_mode,
+ attributes);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_file_open_ex() - open file asynchronously
+ *
+ * This function implements the OpenEx service of the File Protocol.
+ * See the UEFI spec for details.
+ *
+ * @this: EFI_FILE_PROTOCOL instance
+ * @new_handle: on return pointer to file handle
+ * @file_name: file name
+ * @open_mode: mode to open the file (read, read/write, create/read/write)
+ * @attributes: attributes for newly created file
+ * @token: transaction token
+ */
+static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *this,
+ struct efi_file_handle **new_handle,
+ u16 *file_name, u64 open_mode,
+ u64 attributes,
+ struct efi_file_io_token *token)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu, %p", this, new_handle,
+ file_name, open_mode, attributes, token);
+
+ if (!token) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_file_open_int(this, new_handle, file_name, open_mode,
+ attributes);
+
+ if (ret == EFI_SUCCESS && token->event) {
+ token->status = EFI_SUCCESS;
+ efi_signal_event(token->event);
+ }
+
+out:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t file_close(struct file_handle *fh)
+{
+ fs_closedir(fh->dirs);
+ free(fh);
+ return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
+{
+ struct file_handle *fh = to_fh(file);
+ EFI_ENTRY("%p", file);
+ return EFI_EXIT(file_close(fh));
+}
+
+static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p", file);
+
+ if (set_blk_dev(fh) || fs_unlink(fh->path))
+ ret = EFI_WARN_DELETE_FAILURE;
+
+ file_close(fh);
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_get_file_size() - determine the size of a file
+ *
+ * @fh: file handle
+ * @file_size: pointer to receive file size
+ * Return: status code
+ */
+static efi_status_t efi_get_file_size(struct file_handle *fh,
+ loff_t *file_size)
+{
+ if (set_blk_dev(fh))
+ return EFI_DEVICE_ERROR;
+
+ if (fs_size(fh->path, file_size))
+ return EFI_DEVICE_ERROR;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_file_size() - Get the size of a file using an EFI file handle
+ *
+ * @fh: EFI file handle
+ * @size: buffer to fill in the discovered size
+ *
+ * Return: size of the file
+ */
+efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size)
+{
+ struct efi_file_info *info = NULL;
+ efi_uintn_t bs = 0;
+ efi_status_t ret;
+
+ *size = 0;
+ ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
+ info));
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ info = malloc(bs);
+ if (!info) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
+ info));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ *size = info->file_size;
+
+out:
+ free(info);
+ return ret;
+}
+
+static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
+ void *buffer)
+{
+ loff_t actread;
+ efi_status_t ret;
+ loff_t file_size;
+
+ if (!buffer) {
+ ret = EFI_INVALID_PARAMETER;
+ return ret;
+ }
+
+ ret = efi_get_file_size(fh, &file_size);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (file_size < fh->offset) {
+ ret = EFI_DEVICE_ERROR;
+ return ret;
+ }
+
+ if (set_blk_dev(fh))
+ return EFI_DEVICE_ERROR;
+ if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
+ *buffer_size, &actread))
+ return EFI_DEVICE_ERROR;
+
+ *buffer_size = actread;
+ fh->offset += actread;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
+ void *buffer)
+{
+ struct efi_file_info *info = buffer;
+ struct fs_dirent *dent;
+ u64 required_size;
+ u16 *dst;
+
+ if (set_blk_dev(fh))
+ return EFI_DEVICE_ERROR;
+
+ if (!fh->dirs) {
+ assert(fh->offset == 0);
+ fh->dirs = fs_opendir(fh->path);
+ if (!fh->dirs)
+ return EFI_DEVICE_ERROR;
+ fh->dent = NULL;
+ }
+
+ /*
+ * So this is a bit awkward. Since fs layer is stateful and we
+ * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
+ * we might have to return without consuming the dent.. so we
+ * have to stash it for next call.
+ */
+ if (fh->dent) {
+ dent = fh->dent;
+ } else {
+ dent = fs_readdir(fh->dirs);
+ }
+
+ if (!dent) {
+ /* no more files in directory */
+ *buffer_size = 0;
+ return EFI_SUCCESS;
+ }
+
+ /* check buffer size: */
+ required_size = sizeof(*info) +
+ 2 * (utf8_utf16_strlen(dent->name) + 1);
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ fh->dent = dent;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+ fh->dent = NULL;
+
+ *buffer_size = required_size;
+ memset(info, 0, required_size);
+
+ info->size = required_size;
+ info->file_size = dent->size;
+ info->physical_size = dent->size;
+
+ if (dent->type == FS_DT_DIR)
+ info->attribute |= EFI_FILE_DIRECTORY;
+
+ dst = info->file_name;
+ utf8_utf16_strcpy(&dst, dent->name);
+
+ fh->offset++;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t efi_file_read_int(struct efi_file_handle *this,
+ efi_uintn_t *buffer_size, void *buffer)
+{
+ struct file_handle *fh = to_fh(this);
+ efi_status_t ret = EFI_SUCCESS;
+ u64 bs;
+
+ if (!this || !buffer_size)
+ return EFI_INVALID_PARAMETER;
+
+ bs = *buffer_size;
+ if (fh->isdir)
+ ret = dir_read(fh, &bs, buffer);
+ else
+ ret = file_read(fh, &bs, buffer);
+ if (bs <= SIZE_MAX)
+ *buffer_size = bs;
+ else
+ *buffer_size = SIZE_MAX;
+
+ return ret;
+}
+
+/**
+ * efi_file_read() - read file
+ *
+ * This function implements the Read() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @buffer_size: number of bytes to read
+ * @buffer: read buffer
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *this,
+ efi_uintn_t *buffer_size, void *buffer)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer);
+
+ ret = efi_file_read_int(this, buffer_size, buffer);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_file_read_ex() - read file asynchonously
+ *
+ * This function implements the ReadEx() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @token: transaction token
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *this,
+ struct efi_file_io_token *token)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p", this, token);
+
+ if (!token) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_file_read_int(this, &token->buffer_size, token->buffer);
+
+ if (ret == EFI_SUCCESS && token->event) {
+ token->status = EFI_SUCCESS;
+ efi_signal_event(token->event);
+ }
+
+out:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t efi_file_write_int(struct efi_file_handle *this,
+ efi_uintn_t *buffer_size, void *buffer)
+{
+ struct file_handle *fh = to_fh(this);
+ efi_status_t ret = EFI_SUCCESS;
+ loff_t actwrite;
+
+ if (!this || !buffer_size || !buffer) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (fh->isdir) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) {
+ ret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+
+ if (!*buffer_size)
+ goto out;
+
+ if (set_blk_dev(fh)) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
+ &actwrite)) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ *buffer_size = actwrite;
+ fh->offset += actwrite;
+
+out:
+ return ret;
+}
+
+/**
+ * efi_file_write() - write to file
+ *
+ * This function implements the Write() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @buffer_size: number of bytes to write
+ * @buffer: buffer with the bytes to write
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *this,
+ efi_uintn_t *buffer_size,
+ void *buffer)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer);
+
+ ret = efi_file_write_int(this, buffer_size, buffer);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_file_write_ex() - write to file
+ *
+ * This function implements the WriteEx() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @token: transaction token
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *this,
+ struct efi_file_io_token *token)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p", this, token);
+
+ if (!token) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_file_write_int(this, &token->buffer_size, token->buffer);
+
+ if (ret == EFI_SUCCESS && token->event) {
+ token->status = EFI_SUCCESS;
+ efi_signal_event(token->event);
+ }
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_file_getpos() - get current position in file
+ *
+ * This function implements the GetPosition service of the EFI file protocol.
+ * See the UEFI spec for details.
+ *
+ * @file: file handle
+ * @pos: pointer to file position
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
+ u64 *pos)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct file_handle *fh = to_fh(file);
+
+ EFI_ENTRY("%p, %p", file, pos);
+
+ if (fh->isdir) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ *pos = fh->offset;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_file_setpos() - set current position in file
+ *
+ * This function implements the SetPosition service of the EFI file protocol.
+ * See the UEFI spec for details.
+ *
+ * @file: file handle
+ * @pos: new file position
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
+ u64 pos)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %llu", file, pos);
+
+ if (fh->isdir) {
+ if (pos != 0) {
+ ret = EFI_UNSUPPORTED;
+ goto error;
+ }
+ fs_closedir(fh->dirs);
+ fh->dirs = NULL;
+ }
+
+ if (pos == ~0ULL) {
+ loff_t file_size;
+
+ ret = efi_get_file_size(fh, &file_size);
+ if (ret != EFI_SUCCESS)
+ goto error;
+ pos = file_size;
+ }
+
+ fh->offset = pos;
+
+error:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
+ const efi_guid_t *info_type,
+ efi_uintn_t *buffer_size,
+ void *buffer)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_SUCCESS;
+ u16 *dst;
+
+ EFI_ENTRY("%p, %pUl, %p, %p", file, info_type, buffer_size, buffer);
+
+ if (!file || !info_type || !buffer_size ||
+ (*buffer_size && !buffer)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+
+ if (!guidcmp(info_type, &efi_file_info_guid)) {
+ struct efi_file_info *info = buffer;
+ char *filename = basename(fh);
+ unsigned int required_size;
+ loff_t file_size;
+
+ /* check buffer size: */
+ required_size = sizeof(*info) +
+ 2 * (utf8_utf16_strlen(filename) + 1);
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto error;
+ }
+
+ ret = efi_get_file_size(fh, &file_size);
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ memset(info, 0, required_size);
+
+ info->size = required_size;
+ info->file_size = file_size;
+ info->physical_size = file_size;
+
+ if (fh->isdir)
+ info->attribute |= EFI_FILE_DIRECTORY;
+
+ dst = info->file_name;
+ utf8_utf16_strcpy(&dst, filename);
+ } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
+ struct efi_file_system_info *info = buffer;
+ struct disk_partition part;
+ efi_uintn_t required_size;
+ int r;
+
+ if (fh->fs->part >= 1)
+ r = part_get_info(fh->fs->desc, fh->fs->part, &part);
+ else
+ r = part_get_info_whole_disk(fh->fs->desc, &part);
+ if (r < 0) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+ required_size = sizeof(*info) + 2;
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto error;
+ }
+
+ memset(info, 0, required_size);
+
+ info->size = required_size;
+ /*
+ * TODO: We cannot determine if the volume can be written to.
+ */
+ info->read_only = false;
+ info->volume_size = part.size * part.blksz;
+ /*
+ * TODO: We currently have no function to determine the free
+ * space. The volume size is the best upper bound we have.
+ */
+ info->free_space = info->volume_size;
+ info->block_size = part.blksz;
+ /*
+ * TODO: The volume label is not available in U-Boot.
+ */
+ info->volume_label[0] = 0;
+ } else if (!guidcmp(info_type, &efi_system_volume_label_id)) {
+ if (*buffer_size < 2) {
+ *buffer_size = 2;
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto error;
+ }
+ *(u16 *)buffer = 0;
+ } else {
+ ret = EFI_UNSUPPORTED;
+ }
+
+error:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
+ const efi_guid_t *info_type,
+ efi_uintn_t buffer_size,
+ void *buffer)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_UNSUPPORTED;
+
+ EFI_ENTRY("%p, %pUl, %zu, %p", file, info_type, buffer_size, buffer);
+
+ if (!guidcmp(info_type, &efi_file_info_guid)) {
+ struct efi_file_info *info = (struct efi_file_info *)buffer;
+ char *filename = basename(fh);
+ char *new_file_name, *pos;
+ loff_t file_size;
+
+ /* The buffer will always contain a file name. */
+ if (buffer_size < sizeof(struct efi_file_info) + 2 ||
+ buffer_size < info->size) {
+ ret = EFI_BAD_BUFFER_SIZE;
+ goto out;
+ }
+ /* We cannot change the directory attribute */
+ if (!fh->isdir != !(info->attribute & EFI_FILE_DIRECTORY)) {
+ ret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ /* Check for renaming */
+ new_file_name = malloc(utf16_utf8_strlen(info->file_name) + 1);
+ if (!new_file_name) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ pos = new_file_name;
+ utf16_utf8_strcpy(&pos, info->file_name);
+ if (strcmp(new_file_name, filename)) {
+ /* TODO: we do not support renaming */
+ EFI_PRINT("Renaming not supported\n");
+ free(new_file_name);
+ ret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ free(new_file_name);
+ /* Check for truncation */
+ ret = efi_get_file_size(fh, &file_size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ if (file_size != info->file_size) {
+ /* TODO: we do not support truncation */
+ EFI_PRINT("Truncation not supported\n");
+ ret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ /*
+ * We do not care for the other attributes
+ * TODO: Support read only
+ */
+ ret = EFI_SUCCESS;
+ } else {
+ /* TODO: We do not support changing the volume label */
+ ret = EFI_UNSUPPORTED;
+ }
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_file_flush_int() - flush file
+ *
+ * This is the internal implementation of the Flush() and FlushEx() services of
+ * the EFI_FILE_PROTOCOL.
+ *
+ * @this: file protocol instance
+ * Return: status code
+ */
+static efi_status_t efi_file_flush_int(struct efi_file_handle *this)
+{
+ struct file_handle *fh = to_fh(this);
+
+ if (!this)
+ return EFI_INVALID_PARAMETER;
+
+ if (!(fh->open_mode & EFI_FILE_MODE_WRITE))
+ return EFI_ACCESS_DENIED;
+
+ /* TODO: flush for file position after end of file */
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_file_flush() - flush file
+ *
+ * This function implements the Flush() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *this)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p", this);
+
+ ret = efi_file_flush_int(this);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_file_flush_ex() - flush file
+ *
+ * This function implements the FlushEx() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @token: transaction token
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *this,
+ struct efi_file_io_token *token)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p", this, token);
+
+ if (!token) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_file_flush_int(this);
+
+ if (ret == EFI_SUCCESS && token->event) {
+ token->status = EFI_SUCCESS;
+ efi_signal_event(token->event);
+ }
+
+out:
+ return EFI_EXIT(ret);
+}
+
+static const struct efi_file_handle efi_file_handle_protocol = {
+ .rev = EFI_FILE_PROTOCOL_REVISION2,
+ .open = efi_file_open,
+ .close = efi_file_close,
+ .delete = efi_file_delete,
+ .read = efi_file_read,
+ .write = efi_file_write,
+ .getpos = efi_file_getpos,
+ .setpos = efi_file_setpos,
+ .getinfo = efi_file_getinfo,
+ .setinfo = efi_file_setinfo,
+ .flush = efi_file_flush,
+ .open_ex = efi_file_open_ex,
+ .read_ex = efi_file_read_ex,
+ .write_ex = efi_file_write_ex,
+ .flush_ex = efi_file_flush_ex,
+};
+
+/**
+ * efi_file_from_path() - open file via device path
+ *
+ * @fp: device path
+ * @return: EFI_FILE_PROTOCOL for the file or NULL
+ */
+struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
+{
+ struct efi_simple_file_system_protocol *v;
+ struct efi_file_handle *f;
+ efi_status_t ret;
+
+ v = efi_fs_from_path(fp);
+ if (!v)
+ return NULL;
+
+ EFI_CALL(ret = v->open_volume(v, &f));
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ /* Skip over device-path nodes before the file path. */
+ while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
+ fp = efi_dp_next(fp);
+
+ /*
+ * Step through the nodes of the directory path until the actual file
+ * node is reached which is the final node in the device path.
+ */
+ while (fp) {
+ struct efi_device_path_file_path *fdp =
+ container_of(fp, struct efi_device_path_file_path, dp);
+ struct efi_file_handle *f2;
+ u16 *filename;
+
+ if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
+ printf("bad file path!\n");
+ f->close(f);
+ return NULL;
+ }
+
+ filename = u16_strdup(fdp->str);
+ if (!filename)
+ return NULL;
+ EFI_CALL(ret = f->open(f, &f2, filename,
+ EFI_FILE_MODE_READ, 0));
+ free(filename);
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ fp = efi_dp_next(fp);
+
+ EFI_CALL(f->close(f));
+ f = f2;
+ }
+
+ return f;
+}
+
+static efi_status_t EFIAPI
+efi_open_volume(struct efi_simple_file_system_protocol *this,
+ struct efi_file_handle **root)
+{
+ struct file_system *fs = to_fs(this);
+
+ EFI_ENTRY("%p, %p", this, root);
+
+ *root = file_open(fs, NULL, NULL, 0, 0);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+struct efi_simple_file_system_protocol *
+efi_simple_file_system(struct blk_desc *desc, int part,
+ struct efi_device_path *dp)
+{
+ struct file_system *fs;
+
+ fs = calloc(1, sizeof(*fs));
+ fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
+ fs->base.open_volume = efi_open_volume;
+ fs->desc = desc;
+ fs->part = part;
+ fs->dp = dp;
+
+ return &fs->base;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_firmware.c b/roms/u-boot/lib/efi_loader/efi_firmware.c
new file mode 100644
index 000000000..a1b88dbfc
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_firmware.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Firmware management protocol
+ *
+ * Copyright (c) 2020 Linaro Limited
+ * Author: AKASHI Takahiro
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <dfu.h>
+#include <efi_loader.h>
+#include <image.h>
+#include <signatures.h>
+
+#include <linux/list.h>
+
+#define FMP_PAYLOAD_HDR_SIGNATURE SIGNATURE_32('M', 'S', 'S', '1')
+
+/**
+ * struct fmp_payload_header - EDK2 header for the FMP payload
+ *
+ * This structure describes the header which is preprended to the
+ * FMP payload by the edk2 capsule generation scripts.
+ *
+ * @signature: Header signature used to identify the header
+ * @header_size: Size of the structure
+ * @fw_version: Firmware versions used
+ * @lowest_supported_version: Lowest supported version
+ */
+struct fmp_payload_header {
+ u32 signature;
+ u32 header_size;
+ u32 fw_version;
+ u32 lowest_supported_version;
+};
+
+/* Place holder; not supported */
+static
+efi_status_t EFIAPI efi_firmware_get_image_unsupported(
+ struct efi_firmware_management_protocol *this,
+ u8 image_index,
+ void *image,
+ efi_uintn_t *image_size)
+{
+ EFI_ENTRY("%p %d %p %p\n", this, image_index, image, image_size);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/* Place holder; not supported */
+static
+efi_status_t EFIAPI efi_firmware_check_image_unsupported(
+ struct efi_firmware_management_protocol *this,
+ u8 image_index,
+ const void *image,
+ efi_uintn_t *image_size,
+ u32 *image_updatable)
+{
+ EFI_ENTRY("%p %d %p %p %p\n", this, image_index, image, image_size,
+ image_updatable);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/* Place holder; not supported */
+static
+efi_status_t EFIAPI efi_firmware_get_package_info_unsupported(
+ struct efi_firmware_management_protocol *this,
+ u32 *package_version,
+ u16 **package_version_name,
+ u32 *package_version_name_maxlen,
+ u64 *attributes_supported,
+ u64 *attributes_setting)
+{
+ EFI_ENTRY("%p %p %p %p %p %p\n", this, package_version,
+ package_version_name, package_version_name_maxlen,
+ attributes_supported, attributes_setting);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/* Place holder; not supported */
+static
+efi_status_t EFIAPI efi_firmware_set_package_info_unsupported(
+ struct efi_firmware_management_protocol *this,
+ const void *image,
+ efi_uintn_t *image_size,
+ const void *vendor_code,
+ u32 package_version,
+ const u16 *package_version_name)
+{
+ EFI_ENTRY("%p %p %p %p %x %p\n", this, image, image_size, vendor_code,
+ package_version, package_version_name);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_get_dfu_info - return information about the current firmware image
+ * @this: Protocol instance
+ * @image_info_size: Size of @image_info
+ * @image_info: Image information
+ * @descriptor_version: Pointer to version number
+ * @descriptor_count: Pointer to number of descriptors
+ * @descriptor_size: Pointer to descriptor size
+ * package_version: Package version
+ * package_version_name: Package version's name
+ * image_type: Image type GUID
+ *
+ * Return information bout the current firmware image in @image_info.
+ * @image_info will consist of a number of descriptors.
+ * Each descriptor will be created based on "dfu_alt_info" variable.
+ *
+ * Return status code
+ */
+static efi_status_t efi_get_dfu_info(
+ efi_uintn_t *image_info_size,
+ struct efi_firmware_image_descriptor *image_info,
+ u32 *descriptor_version,
+ u8 *descriptor_count,
+ efi_uintn_t *descriptor_size,
+ u32 *package_version,
+ u16 **package_version_name,
+ const efi_guid_t *image_type)
+{
+ struct dfu_entity *dfu;
+ size_t names_len, total_size;
+ int dfu_num, i;
+ u16 *name, *next;
+
+ dfu_init_env_entities(NULL, NULL);
+
+ names_len = 0;
+ dfu_num = 0;
+ list_for_each_entry(dfu, &dfu_list, list) {
+ names_len += (utf8_utf16_strlen(dfu->name) + 1) * 2;
+ dfu_num++;
+ }
+ if (!dfu_num) {
+ log_warning("Probably dfu_alt_info not defined\n");
+ *image_info_size = 0;
+ dfu_free_entities();
+
+ return EFI_SUCCESS;
+ }
+
+ total_size = sizeof(*image_info) * dfu_num + names_len;
+ /*
+ * we will assume that sizeof(*image_info) * dfu_name
+ * is, at least, a multiple of 2. So the start address for
+ * image_id_name would be aligned with 2 bytes.
+ */
+ if (*image_info_size < total_size) {
+ *image_info_size = total_size;
+ dfu_free_entities();
+
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ *image_info_size = total_size;
+
+ *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
+ *descriptor_count = dfu_num;
+ *descriptor_size = sizeof(*image_info);
+ *package_version = 0xffffffff; /* not supported */
+ *package_version_name = NULL; /* not supported */
+
+ /* DFU alt number should correspond to image_index */
+ i = 0;
+ /* Name area starts just after descriptors */
+ name = (u16 *)((u8 *)image_info + sizeof(*image_info) * dfu_num);
+ next = name;
+ list_for_each_entry(dfu, &dfu_list, list) {
+ image_info[i].image_index = dfu->alt + 1;
+ image_info[i].image_type_id = *image_type;
+ image_info[i].image_id = dfu->alt;
+
+ /* copy the DFU entity name */
+ utf8_utf16_strcpy(&next, dfu->name);
+ image_info[i].image_id_name = name;
+ name = ++next;
+
+ image_info[i].version = 0; /* not supported */
+ image_info[i].version_name = NULL; /* not supported */
+ image_info[i].size = 0;
+ image_info[i].attributes_supported =
+ IMAGE_ATTRIBUTE_IMAGE_UPDATABLE |
+ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
+ image_info[i].attributes_setting =
+ IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
+
+ /* Check if the capsule authentication is enabled */
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE))
+ image_info[0].attributes_setting |=
+ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
+
+ image_info[i].lowest_supported_image_version = 0;
+ image_info[i].last_attempt_version = 0;
+ image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
+ image_info[i].hardware_instance = 1;
+ image_info[i].dependencies = NULL;
+
+ i++;
+ }
+
+ dfu_free_entities();
+
+ return EFI_SUCCESS;
+}
+
+#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT
+/*
+ * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update
+ * method with existing FIT image format, and handles
+ * - multiple regions of firmware via DFU
+ * but doesn't support
+ * - versioning of firmware image
+ * - package information
+ */
+const efi_guid_t efi_firmware_image_type_uboot_fit =
+ EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
+
+/**
+ * efi_firmware_fit_get_image_info - return information about the current
+ * firmware image
+ * @this: Protocol instance
+ * @image_info_size: Size of @image_info
+ * @image_info: Image information
+ * @descriptor_version: Pointer to version number
+ * @descriptor_count: Pointer to number of descriptors
+ * @descriptor_size: Pointer to descriptor size
+ * package_version: Package version
+ * package_version_name: Package version's name
+ *
+ * Return information bout the current firmware image in @image_info.
+ * @image_info will consist of a number of descriptors.
+ * Each descriptor will be created based on "dfu_alt_info" variable.
+ *
+ * Return status code
+ */
+static
+efi_status_t EFIAPI efi_firmware_fit_get_image_info(
+ struct efi_firmware_management_protocol *this,
+ efi_uintn_t *image_info_size,
+ struct efi_firmware_image_descriptor *image_info,
+ u32 *descriptor_version,
+ u8 *descriptor_count,
+ efi_uintn_t *descriptor_size,
+ u32 *package_version,
+ u16 **package_version_name)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
+ image_info_size, image_info,
+ descriptor_version, descriptor_count, descriptor_size,
+ package_version, package_version_name);
+
+ if (!image_info_size)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if (*image_info_size &&
+ (!image_info || !descriptor_version || !descriptor_count ||
+ !descriptor_size || !package_version || !package_version_name))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ ret = efi_get_dfu_info(image_info_size, image_info,
+ descriptor_version, descriptor_count,
+ descriptor_size,
+ package_version, package_version_name,
+ &efi_firmware_image_type_uboot_fit);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_firmware_fit_set_image - update the firmware image
+ * @this: Protocol instance
+ * @image_index: Image index number
+ * @image: New image
+ * @image_size: Size of new image
+ * @vendor_code: Vendor-specific update policy
+ * @progress: Function to report the progress of update
+ * @abort_reason: Pointer to string of abort reason
+ *
+ * Update the firmware to new image, using dfu. The new image should
+ * have FIT image format commonly used in U-Boot.
+ * @vendor_code, @progress and @abort_reason are not supported.
+ *
+ * Return: status code
+ */
+static
+efi_status_t EFIAPI efi_firmware_fit_set_image(
+ struct efi_firmware_management_protocol *this,
+ u8 image_index,
+ const void *image,
+ efi_uintn_t image_size,
+ const void *vendor_code,
+ efi_status_t (*progress)(efi_uintn_t completion),
+ u16 **abort_reason)
+{
+ EFI_ENTRY("%p %d %p %zd %p %p %p\n", this, image_index, image,
+ image_size, vendor_code, progress, abort_reason);
+
+ if (!image || image_index != 1)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if (fit_update(image))
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+const struct efi_firmware_management_protocol efi_fmp_fit = {
+ .get_image_info = efi_firmware_fit_get_image_info,
+ .get_image = efi_firmware_get_image_unsupported,
+ .set_image = efi_firmware_fit_set_image,
+ .check_image = efi_firmware_check_image_unsupported,
+ .get_package_info = efi_firmware_get_package_info_unsupported,
+ .set_package_info = efi_firmware_set_package_info_unsupported,
+};
+#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_FIT */
+
+#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_RAW
+/*
+ * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update
+ * method with raw data.
+ */
+const efi_guid_t efi_firmware_image_type_uboot_raw =
+ EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
+
+/**
+ * efi_firmware_raw_get_image_info - return information about the current
+ firmware image
+ * @this: Protocol instance
+ * @image_info_size: Size of @image_info
+ * @image_info: Image information
+ * @descriptor_version: Pointer to version number
+ * @descriptor_count: Pointer to number of descriptors
+ * @descriptor_size: Pointer to descriptor size
+ * package_version: Package version
+ * package_version_name: Package version's name
+ *
+ * Return information bout the current firmware image in @image_info.
+ * @image_info will consist of a number of descriptors.
+ * Each descriptor will be created based on "dfu_alt_info" variable.
+ *
+ * Return status code
+ */
+static
+efi_status_t EFIAPI efi_firmware_raw_get_image_info(
+ struct efi_firmware_management_protocol *this,
+ efi_uintn_t *image_info_size,
+ struct efi_firmware_image_descriptor *image_info,
+ u32 *descriptor_version,
+ u8 *descriptor_count,
+ efi_uintn_t *descriptor_size,
+ u32 *package_version,
+ u16 **package_version_name)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this,
+ image_info_size, image_info,
+ descriptor_version, descriptor_count, descriptor_size,
+ package_version, package_version_name);
+
+ if (!image_info_size)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if (*image_info_size &&
+ (!image_info || !descriptor_version || !descriptor_count ||
+ !descriptor_size || !package_version || !package_version_name))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ ret = efi_get_dfu_info(image_info_size, image_info,
+ descriptor_version, descriptor_count,
+ descriptor_size,
+ package_version, package_version_name,
+ &efi_firmware_image_type_uboot_raw);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_firmware_raw_set_image - update the firmware image
+ * @this: Protocol instance
+ * @image_index: Image index number
+ * @image: New image
+ * @image_size: Size of new image
+ * @vendor_code: Vendor-specific update policy
+ * @progress: Function to report the progress of update
+ * @abort_reason: Pointer to string of abort reason
+ *
+ * Update the firmware to new image, using dfu. The new image should
+ * be a single raw image.
+ * @vendor_code, @progress and @abort_reason are not supported.
+ *
+ * Return: status code
+ */
+static
+efi_status_t EFIAPI efi_firmware_raw_set_image(
+ struct efi_firmware_management_protocol *this,
+ u8 image_index,
+ const void *image,
+ efi_uintn_t image_size,
+ const void *vendor_code,
+ efi_status_t (*progress)(efi_uintn_t completion),
+ u16 **abort_reason)
+{
+ u32 fmp_hdr_signature;
+ struct fmp_payload_header *header;
+ void *capsule_payload;
+ efi_status_t status;
+ efi_uintn_t capsule_payload_size;
+
+ EFI_ENTRY("%p %d %p %zd %p %p %p\n", this, image_index, image,
+ image_size, vendor_code, progress, abort_reason);
+
+ if (!image)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* Authenticate the capsule if authentication enabled */
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) {
+ capsule_payload = NULL;
+ capsule_payload_size = 0;
+ status = efi_capsule_authenticate(image, image_size,
+ &capsule_payload,
+ &capsule_payload_size);
+
+ if (status == EFI_SECURITY_VIOLATION) {
+ printf("Capsule authentication check failed. Aborting update\n");
+ return EFI_EXIT(status);
+ } else if (status != EFI_SUCCESS) {
+ return EFI_EXIT(status);
+ }
+
+ debug("Capsule authentication successfull\n");
+ image = capsule_payload;
+ image_size = capsule_payload_size;
+ } else {
+ debug("Capsule authentication disabled. ");
+ debug("Updating capsule without authenticating.\n");
+ }
+
+ fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
+ header = (void *)image;
+
+ if (!memcmp(&header->signature, &fmp_hdr_signature,
+ sizeof(fmp_hdr_signature))) {
+ /*
+ * When building the capsule with the scripts in
+ * edk2, a FMP header is inserted above the capsule
+ * payload. Compensate for this header to get the
+ * actual payload that is to be updated.
+ */
+ image += header->header_size;
+ image_size -= header->header_size;
+
+ }
+
+ if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
+ NULL, NULL))
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+const struct efi_firmware_management_protocol efi_fmp_raw = {
+ .get_image_info = efi_firmware_raw_get_image_info,
+ .get_image = efi_firmware_get_image_unsupported,
+ .set_image = efi_firmware_raw_set_image,
+ .check_image = efi_firmware_check_image_unsupported,
+ .get_package_info = efi_firmware_get_package_info_unsupported,
+ .set_package_info = efi_firmware_set_package_info_unsupported,
+};
+#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_RAW */
diff --git a/roms/u-boot/lib/efi_loader/efi_freestanding.c b/roms/u-boot/lib/efi_loader/efi_freestanding.c
new file mode 100644
index 000000000..bd0dff162
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_freestanding.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Library for freestanding binary
+ *
+ * Copyright 2019, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * GCC requires that freestanding programs provide memcpy(), memmove(),
+ * memset(), and memcmp().
+ */
+
+#include <common.h>
+
+/**
+ * memcmp() - compare memory areas
+ *
+ * @s1: pointer to first area
+ * @s2: pointer to second area
+ * @n: number of bytes to compare
+ * Return: 0 if both memory areas are the same, otherwise the sign of the
+ * result value is the same as the sign of the difference between
+ * the first differing pair of bytes taken as u8.
+ */
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+ const u8 *pos1 = s1;
+ const u8 *pos2 = s2;
+
+ for (; n; --n) {
+ if (*pos1 != *pos2)
+ return *pos1 - *pos2;
+ ++pos1;
+ ++pos2;
+ }
+ return 0;
+}
+
+/**
+ * memcpy() - copy memory area
+ *
+ * @dest: destination buffer
+ * @src: source buffer
+ * @n: number of bytes to copy
+ * Return: pointer to destination buffer
+ */
+void *memmove(void *dest, const void *src, size_t n)
+{
+ u8 *d = dest;
+ const u8 *s = src;
+
+ if (d <= s) {
+ for (; n; --n)
+ *d++ = *s++;
+ } else {
+ d += n;
+ s += n;
+ for (; n; --n)
+ *--d = *--s;
+ }
+ return dest;
+}
+
+/**
+ * memcpy() - copy memory area
+ *
+ * @dest: destination buffer
+ * @src: source buffer
+ * @n: number of bytes to copy
+ * Return: pointer to destination buffer
+ */
+void *memcpy(void *dest, const void *src, size_t n)
+{
+ return memmove(dest, src, n);
+}
+
+/**
+ * memset() - fill memory with a constant byte
+ *
+ * @s: destination buffer
+ * @c: byte value
+ * @n: number of bytes to set
+ * Return: pointer to destination buffer
+ */
+void *memset(void *s, int c, size_t n)
+{
+ u8 *d = s;
+
+ for (; n; --n)
+ *d++ = c;
+ return s;
+}
+
+/**
+ * __cyg_profile_func_enter() - record function entry
+ *
+ * This is called on every function entry when compiling with
+ * -finstrument-functions.
+ *
+ * We do nothing here.
+ *
+ * @param func_ptr Pointer to function being entered
+ * @param caller Pointer to function which called this function
+ */
+void __attribute__((no_instrument_function))
+__cyg_profile_func_enter(void *func_ptr, void *caller)
+{
+}
+
+/**
+ * __cyg_profile_func_exit() - record function exit
+ *
+ * This is called on every function exit when compiling with
+ * -finstrument-functions.
+ *
+ * We do nothing here.
+ *
+ * @param func_ptr Pointer to function being entered
+ * @param caller Pointer to function which called this function
+ */
+void __attribute__((no_instrument_function))
+__cyg_profile_func_exit(void *func_ptr, void *caller)
+{
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_gop.c b/roms/u-boot/lib/efi_loader/efi_gop.c
new file mode 100644
index 000000000..1206b2d7a
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_gop.c
@@ -0,0 +1,532 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application disk support
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <efi_loader.h>
+#include <lcd.h>
+#include <log.h>
+#include <malloc.h>
+#include <video.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+
+/**
+ * struct efi_gop_obj - graphical output protocol object
+ *
+ * @header: EFI object header
+ * @ops: graphical output protocol interface
+ * @info: graphical output mode information
+ * @mode: graphical output mode
+ * @bpix: bits per pixel
+ * @fb: frame buffer
+ */
+struct efi_gop_obj {
+ struct efi_object header;
+ struct efi_gop ops;
+ struct efi_gop_mode_info info;
+ struct efi_gop_mode mode;
+ /* Fields we only have access to during init */
+ u32 bpix;
+ void *fb;
+};
+
+static efi_status_t EFIAPI gop_query_mode(struct efi_gop *this, u32 mode_number,
+ efi_uintn_t *size_of_info,
+ struct efi_gop_mode_info **info)
+{
+ struct efi_gop_obj *gopobj;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %x, %p, %p", this, mode_number, size_of_info, info);
+
+ if (!this || !size_of_info || !info || mode_number) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ gopobj = container_of(this, struct efi_gop_obj, ops);
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, sizeof(gopobj->info),
+ (void **)info);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ *size_of_info = sizeof(gopobj->info);
+ memcpy(*info, &gopobj->info, sizeof(gopobj->info));
+
+out:
+ return EFI_EXIT(ret);
+}
+
+static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid)
+{
+ struct efi_gop_pixel blt = {
+ .reserved = 0,
+ };
+
+ blt.blue = (vid & 0x1f) << 3;
+ vid >>= 5;
+ blt.green = (vid & 0x3f) << 2;
+ vid >>= 6;
+ blt.red = (vid & 0x1f) << 3;
+ return blt;
+}
+
+static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt)
+{
+ return (u16)(blt->red >> 3) << 11 |
+ (u16)(blt->green >> 2) << 5 |
+ (u16)(blt->blue >> 3);
+}
+
+static __always_inline efi_status_t gop_blt_int(struct efi_gop *this,
+ struct efi_gop_pixel *bufferp,
+ u32 operation, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy,
+ efi_uintn_t width,
+ efi_uintn_t height,
+ efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
+ efi_uintn_t i, j, linelen, slineoff = 0, dlineoff, swidth, dwidth;
+ u32 *fb32 = gopobj->fb;
+ u16 *fb16 = gopobj->fb;
+ struct efi_gop_pixel *buffer = __builtin_assume_aligned(bufferp, 4);
+
+ if (delta) {
+ /* Check for 4 byte alignment */
+ if (delta & 3)
+ return EFI_INVALID_PARAMETER;
+ linelen = delta >> 2;
+ } else {
+ linelen = width;
+ }
+
+ /* Check source rectangle */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ if (sx + width > linelen)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (sx + width > gopobj->info.width ||
+ sy + height > gopobj->info.height)
+ return EFI_INVALID_PARAMETER;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /* Check destination rectangle */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (dx + width > gopobj->info.width ||
+ dy + height > gopobj->info.height)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ if (dx + width > linelen)
+ return EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Calculate line width */
+ switch (operation) {
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ swidth = linelen;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ swidth = gopobj->info.width;
+ if (!vid_bpp)
+ return EFI_UNSUPPORTED;
+ break;
+ case EFI_BLT_VIDEO_FILL:
+ swidth = 0;
+ break;
+ }
+
+ switch (operation) {
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ dwidth = gopobj->info.width;
+ if (!vid_bpp)
+ return EFI_UNSUPPORTED;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ dwidth = linelen;
+ break;
+ }
+
+ slineoff = swidth * sy;
+ dlineoff = dwidth * dy;
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width; j++) {
+ struct efi_gop_pixel pix;
+
+ /* Read source pixel */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ pix = *buffer;
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ pix = buffer[slineoff + j + sx];
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (vid_bpp == 32)
+ pix = *(struct efi_gop_pixel *)&fb32[
+ slineoff + j + sx];
+ else
+ pix = efi_vid16_to_blt_col(fb16[
+ slineoff + j + sx]);
+ break;
+ }
+
+ /* Write destination pixel */
+ switch (operation) {
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ buffer[dlineoff + j + dx] = pix;
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (vid_bpp == 32)
+ fb32[dlineoff + j + dx] = *(u32 *)&pix;
+ else
+ fb16[dlineoff + j + dx] =
+ efi_blt_col_to_vid16(&pix);
+ break;
+ }
+ }
+ slineoff += swidth;
+ dlineoff += dwidth;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static efi_uintn_t gop_get_bpp(struct efi_gop *this)
+{
+ struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
+ efi_uintn_t vid_bpp = 0;
+
+ switch (gopobj->bpix) {
+#ifdef CONFIG_DM_VIDEO
+ case VIDEO_BPP32:
+#else
+ case LCD_COLOR32:
+#endif
+ vid_bpp = 32;
+ break;
+#ifdef CONFIG_DM_VIDEO
+ case VIDEO_BPP16:
+#else
+ case LCD_COLOR16:
+#endif
+ vid_bpp = 16;
+ break;
+ }
+
+ return vid_bpp;
+}
+
+/*
+ * GCC can't optimize our BLT function well, but we need to make sure that
+ * our 2-dimensional loop gets executed very quickly, otherwise the system
+ * will feel slow.
+ *
+ * By manually putting all obvious branch targets into functions which call
+ * our generic BLT function with constants, the compiler can successfully
+ * optimize for speed.
+ */
+static efi_status_t gop_blt_video_fill(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+}
+
+static efi_status_t gop_blt_buf_to_vid16(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, 16);
+}
+
+static efi_status_t gop_blt_buf_to_vid32(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, 32);
+}
+
+static efi_status_t gop_blt_vid_to_vid(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+}
+
+static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy,
+ dx, dy, width, height, delta, vid_bpp);
+}
+
+/**
+ * gop_set_mode() - set graphical output mode
+ *
+ * This function implements the SetMode() service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: the graphical output protocol
+ * @mode_number: the mode to be set
+ * Return: status code
+ */
+static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
+{
+ struct efi_gop_obj *gopobj;
+ struct efi_gop_pixel buffer = {0, 0, 0, 0};
+ efi_uintn_t vid_bpp;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %x", this, mode_number);
+
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (mode_number) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ gopobj = container_of(this, struct efi_gop_obj, ops);
+ vid_bpp = gop_get_bpp(this);
+ ret = gop_blt_video_fill(this, &buffer, EFI_BLT_VIDEO_FILL, 0, 0, 0, 0,
+ gopobj->info.width, gopobj->info.height, 0,
+ vid_bpp);
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * Copy rectangle.
+ *
+ * This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: EFI_GRAPHICS_OUTPUT_PROTOCOL
+ * @buffer: pixel buffer
+ * @sx: source x-coordinate
+ * @sy: source y-coordinate
+ * @dx: destination x-coordinate
+ * @dy: destination y-coordinate
+ * @width: width of rectangle
+ * @height: height of rectangle
+ * @delta: length in bytes of a line in the pixel buffer (optional)
+ * @return: status code
+ */
+efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer,
+ u32 operation, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+ efi_uintn_t vid_bpp;
+
+ EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
+ buffer, operation, sx, sy, dx, dy, width, height, delta);
+
+ vid_bpp = gop_get_bpp(this);
+
+ /* Allow for compiler optimization */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ ret = gop_blt_video_fill(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ /* This needs to be super-fast, so duplicate for 16/32bpp */
+ if (vid_bpp == 32)
+ ret = gop_blt_buf_to_vid32(this, buffer, operation, sx,
+ sy, dx, dy, width, height,
+ delta);
+ else
+ ret = gop_blt_buf_to_vid16(this, buffer, operation, sx,
+ sy, dx, dy, width, height,
+ delta);
+ break;
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ ret = gop_blt_vid_to_vid(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ default:
+ ret = EFI_INVALID_PARAMETER;
+ }
+
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(ret);
+
+#ifdef CONFIG_DM_VIDEO
+ video_sync_all();
+#else
+ lcd_sync();
+#endif
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/*
+ * Install graphical output protocol.
+ *
+ * If no supported video device exists this is not considered as an
+ * error.
+ */
+efi_status_t efi_gop_register(void)
+{
+ struct efi_gop_obj *gopobj;
+ u32 bpix, col, row;
+ u64 fb_base, fb_size;
+ void *fb;
+ efi_status_t ret;
+
+#ifdef CONFIG_DM_VIDEO
+ struct udevice *vdev;
+ struct video_priv *priv;
+
+ /* We only support a single video output device for now */
+ if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) {
+ debug("WARNING: No video device\n");
+ return EFI_SUCCESS;
+ }
+
+ priv = dev_get_uclass_priv(vdev);
+ bpix = priv->bpix;
+ col = video_get_xsize(vdev);
+ row = video_get_ysize(vdev);
+ fb_base = (uintptr_t)priv->fb;
+ fb_size = priv->fb_size;
+ fb = priv->fb;
+#else
+ int line_len;
+
+ bpix = panel_info.vl_bpix;
+ col = panel_info.vl_col;
+ row = panel_info.vl_row;
+ fb_base = gd->fb_base;
+ fb_size = lcd_get_size(&line_len);
+ fb = (void*)gd->fb_base;
+#endif
+
+ switch (bpix) {
+#ifdef CONFIG_DM_VIDEO
+ case VIDEO_BPP16:
+ case VIDEO_BPP32:
+#else
+ case LCD_COLOR32:
+ case LCD_COLOR16:
+#endif
+ break;
+ default:
+ /* So far, we only work in 16 or 32 bit mode */
+ debug("WARNING: Unsupported video mode\n");
+ return EFI_SUCCESS;
+ }
+
+ gopobj = calloc(1, sizeof(*gopobj));
+ if (!gopobj) {
+ printf("ERROR: Out of memory\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ /* Hook up to the device list */
+ efi_add_handle(&gopobj->header);
+
+ /* Fill in object data */
+ ret = efi_add_protocol(&gopobj->header, &efi_gop_guid,
+ &gopobj->ops);
+ if (ret != EFI_SUCCESS) {
+ printf("ERROR: Failure adding GOP protocol\n");
+ return ret;
+ }
+ gopobj->ops.query_mode = gop_query_mode;
+ gopobj->ops.set_mode = gop_set_mode;
+ gopobj->ops.blt = gop_blt;
+ gopobj->ops.mode = &gopobj->mode;
+
+ gopobj->mode.max_mode = 1;
+ gopobj->mode.info = &gopobj->info;
+ gopobj->mode.info_size = sizeof(gopobj->info);
+
+ gopobj->mode.fb_base = fb_base;
+ gopobj->mode.fb_size = fb_size;
+
+ gopobj->info.version = 0;
+ gopobj->info.width = col;
+ gopobj->info.height = row;
+#ifdef CONFIG_DM_VIDEO
+ if (bpix == VIDEO_BPP32)
+#else
+ if (bpix == LCD_COLOR32)
+#endif
+ {
+ gopobj->info.pixel_format = EFI_GOT_BGRA8;
+ } else {
+ gopobj->info.pixel_format = EFI_GOT_BITMASK;
+ gopobj->info.pixel_bitmask[0] = 0xf800; /* red */
+ gopobj->info.pixel_bitmask[1] = 0x07e0; /* green */
+ gopobj->info.pixel_bitmask[2] = 0x001f; /* blue */
+ }
+ gopobj->info.pixels_per_scanline = col;
+ gopobj->bpix = bpix;
+ gopobj->fb = fb;
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_helper.c b/roms/u-boot/lib/efi_loader/efi_helper.c
new file mode 100644
index 000000000..d03a73646
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_helper.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+#include <common.h>
+#include <env.h>
+#include <malloc.h>
+#include <dm.h>
+#include <fs.h>
+#include <efi_load_initrd.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+
+/**
+ * efi_create_current_boot_var() - Return Boot#### name were #### is replaced by
+ * the value of BootCurrent
+ *
+ * @var_name: variable name
+ * @var_name_size: size of var_name
+ *
+ * Return: Status code
+ */
+static efi_status_t efi_create_current_boot_var(u16 var_name[],
+ size_t var_name_size)
+{
+ efi_uintn_t boot_current_size;
+ efi_status_t ret;
+ u16 boot_current;
+ u16 *pos;
+
+ boot_current_size = sizeof(boot_current);
+ ret = efi_get_variable_int(L"BootCurrent",
+ &efi_global_variable_guid, NULL,
+ &boot_current_size, &boot_current, NULL);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ pos = efi_create_indexed_name(var_name, var_name_size, "Boot",
+ boot_current);
+ if (!pos) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * efi_get_dp_from_boot() - Retrieve and return a device path from an EFI
+ * Boot### variable.
+ * A boot option may contain an array of device paths.
+ * We use a VenMedia() with a specific GUID to identify
+ * the usage of the array members. This function is
+ * used to extract a specific device path
+ *
+ * @guid: vendor GUID of the VenMedia() device path node identifying the
+ * device path
+ *
+ * Return: device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid)
+{
+ struct efi_device_path *file_path = NULL;
+ struct efi_device_path *tmp = NULL;
+ struct efi_load_option lo;
+ void *var_value = NULL;
+ efi_uintn_t size;
+ efi_status_t ret;
+ u16 var_name[16];
+
+ ret = efi_create_current_boot_var(var_name, sizeof(var_name));
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ var_value = efi_get_var(var_name, &efi_global_variable_guid, &size);
+ if (!var_value)
+ return NULL;
+
+ ret = efi_deserialize_load_option(&lo, var_value, &size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ tmp = efi_dp_from_lo(&lo, &size, guid);
+ if (!tmp)
+ goto out;
+
+ /* efi_dp_dup will just return NULL if efi_dp_next is NULL */
+ file_path = efi_dp_dup(efi_dp_next(tmp));
+
+out:
+ efi_free_pool(tmp);
+ free(var_value);
+
+ return file_path;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_hii.c b/roms/u-boot/lib/efi_loader/efi_hii.c
new file mode 100644
index 000000000..77e330285
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_hii.c
@@ -0,0 +1,1073 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Human Interface Infrastructure ... database and packages
+ *
+ * Copyright (c) 2017 Leif Lindholm
+ * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <malloc.h>
+#include <asm/unaligned.h>
+
+const efi_guid_t efi_guid_hii_database_protocol
+ = EFI_HII_DATABASE_PROTOCOL_GUID;
+const efi_guid_t efi_guid_hii_string_protocol = EFI_HII_STRING_PROTOCOL_GUID;
+
+static LIST_HEAD(efi_package_lists);
+static LIST_HEAD(efi_keyboard_layout_list);
+
+struct efi_hii_packagelist {
+ struct list_head link;
+ // TODO should there be an associated efi_object?
+ efi_handle_t driver_handle;
+ u32 max_string_id;
+ struct list_head string_tables; /* list of efi_string_table */
+ struct list_head guid_list;
+ struct list_head keyboard_packages;
+
+ /* we could also track fonts, images, etc */
+};
+
+static int efi_hii_packagelist_exists(efi_hii_handle_t package_list)
+{
+ struct efi_hii_packagelist *hii;
+ int found = 0;
+
+ list_for_each_entry(hii, &efi_package_lists, link) {
+ if (hii == package_list) {
+ found = 1;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static u32 efi_hii_package_type(struct efi_hii_package_header *header)
+{
+ u32 fields;
+
+ fields = get_unaligned_le32(&header->fields);
+
+ return (fields >> __EFI_HII_PACKAGE_TYPE_SHIFT)
+ & __EFI_HII_PACKAGE_TYPE_MASK;
+}
+
+static u32 efi_hii_package_len(struct efi_hii_package_header *header)
+{
+ u32 fields;
+
+ fields = get_unaligned_le32(&header->fields);
+
+ return (fields >> __EFI_HII_PACKAGE_LEN_SHIFT)
+ & __EFI_HII_PACKAGE_LEN_MASK;
+}
+
+struct efi_string_info {
+ efi_string_t string;
+ /* we could also track font info, etc */
+};
+
+struct efi_string_table {
+ struct list_head link;
+ efi_string_id_t language_name;
+ char *language;
+ u32 nstrings;
+ /*
+ * NOTE:
+ * string id starts at 1 so value is stbl->strings[id-1],
+ * and strings[] is a array of stbl->nstrings elements
+ */
+ struct efi_string_info *strings;
+};
+
+struct efi_guid_data {
+ struct list_head link;
+ struct efi_hii_guid_package package;
+};
+
+struct efi_keyboard_layout_data {
+ struct list_head link; /* in package */
+ struct list_head link_sys; /* in global list */
+ struct efi_hii_keyboard_layout keyboard_layout;
+};
+
+struct efi_keyboard_package_data {
+ struct list_head link; /* in package_list */
+ struct list_head keyboard_layout_list;
+};
+
+static void free_strings_table(struct efi_string_table *stbl)
+{
+ int i;
+
+ for (i = 0; i < stbl->nstrings; i++)
+ free(stbl->strings[i].string);
+ free(stbl->strings);
+ free(stbl->language);
+ free(stbl);
+}
+
+static void remove_strings_package(struct efi_hii_packagelist *hii)
+{
+ while (!list_empty(&hii->string_tables)) {
+ struct efi_string_table *stbl;
+
+ stbl = list_first_entry(&hii->string_tables,
+ struct efi_string_table, link);
+ list_del(&stbl->link);
+ free_strings_table(stbl);
+ }
+}
+
+static efi_status_t
+add_strings_package(struct efi_hii_packagelist *hii,
+ struct efi_hii_strings_package *strings_package)
+{
+ struct efi_hii_string_block *block;
+ void *end;
+ u32 nstrings = 0, idx = 0;
+ struct efi_string_table *stbl = NULL;
+ efi_status_t ret;
+
+ EFI_PRINT("header_size: %08x\n",
+ get_unaligned_le32(&strings_package->header_size));
+ EFI_PRINT("string_info_offset: %08x\n",
+ get_unaligned_le32(&strings_package->string_info_offset));
+ EFI_PRINT("language_name: %u\n",
+ get_unaligned_le16(&strings_package->language_name));
+ EFI_PRINT("language: %s\n", strings_package->language);
+
+ /* count # of string entries: */
+ end = ((void *)strings_package)
+ + efi_hii_package_len(&strings_package->header);
+ block = ((void *)strings_package)
+ + get_unaligned_le32(&strings_package->string_info_offset);
+
+ while ((void *)block < end) {
+ switch (block->block_type) {
+ case EFI_HII_SIBT_STRING_UCS2: {
+ struct efi_hii_sibt_string_ucs2_block *ucs2;
+
+ ucs2 = (void *)block;
+ nstrings++;
+ block = efi_hii_sibt_string_ucs2_block_next(ucs2);
+ break;
+ }
+ case EFI_HII_SIBT_END:
+ block = end;
+ break;
+ default:
+ EFI_PRINT("unknown HII string block type: %02x\n",
+ block->block_type);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ stbl = calloc(sizeof(*stbl), 1);
+ if (!stbl) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+ stbl->strings = calloc(sizeof(stbl->strings[0]), nstrings);
+ if (!stbl->strings) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+ stbl->language_name =
+ get_unaligned_le16(&strings_package->language_name);
+ stbl->language = strdup((char *)strings_package->language);
+ if (!stbl->language) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+ stbl->nstrings = nstrings;
+
+ /* and now parse string entries and populate efi_string_table */
+ block = ((void *)strings_package)
+ + get_unaligned_le32(&strings_package->string_info_offset);
+
+ while ((void *)block < end) {
+ switch (block->block_type) {
+ case EFI_HII_SIBT_STRING_UCS2: {
+ struct efi_hii_sibt_string_ucs2_block *ucs2;
+
+ ucs2 = (void *)block;
+ EFI_PRINT("%4u: \"%ls\"\n", idx + 1, ucs2->string_text);
+ stbl->strings[idx].string =
+ u16_strdup(ucs2->string_text);
+ if (!stbl->strings[idx].string) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+ idx++;
+ /* FIXME: accessing u16 * here */
+ block = efi_hii_sibt_string_ucs2_block_next(ucs2);
+ break;
+ }
+ case EFI_HII_SIBT_END:
+ goto out;
+ default:
+ EFI_PRINT("unknown HII string block type: %02x\n",
+ block->block_type);
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+ }
+
+out:
+ list_add(&stbl->link, &hii->string_tables);
+ if (hii->max_string_id < nstrings)
+ hii->max_string_id = nstrings;
+
+ return EFI_SUCCESS;
+
+error:
+ if (stbl) {
+ free(stbl->language);
+ while (idx > 0)
+ free(stbl->strings[--idx].string);
+ free(stbl->strings);
+ }
+ free(stbl);
+
+ return ret;
+}
+
+static void remove_guid_package(struct efi_hii_packagelist *hii)
+{
+ struct efi_guid_data *data;
+
+ while (!list_empty(&hii->guid_list)) {
+ data = list_first_entry(&hii->guid_list,
+ struct efi_guid_data, link);
+ list_del(&data->link);
+ free(data);
+ }
+}
+
+static efi_status_t
+add_guid_package(struct efi_hii_packagelist *hii,
+ struct efi_hii_guid_package *package)
+{
+ struct efi_guid_data *data;
+
+ data = calloc(sizeof(*data), 1);
+ if (!data)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* TODO: we don't know any about data field */
+ memcpy(&data->package, package, sizeof(*package));
+ list_add_tail(&data->link, &hii->guid_list);
+
+ return EFI_SUCCESS;
+}
+
+static void free_keyboard_layouts(struct efi_keyboard_package_data *package)
+{
+ struct efi_keyboard_layout_data *layout_data;
+
+ while (!list_empty(&package->keyboard_layout_list)) {
+ layout_data = list_first_entry(&package->keyboard_layout_list,
+ struct efi_keyboard_layout_data,
+ link);
+ list_del(&layout_data->link);
+ list_del(&layout_data->link_sys);
+ free(layout_data);
+ }
+}
+
+static void remove_keyboard_package(struct efi_hii_packagelist *hii)
+{
+ struct efi_keyboard_package_data *package;
+
+ while (!list_empty(&hii->keyboard_packages)) {
+ package = list_first_entry(&hii->keyboard_packages,
+ struct efi_keyboard_package_data,
+ link);
+ free_keyboard_layouts(package);
+ list_del(&package->link);
+ free(package);
+ }
+}
+
+static efi_status_t
+add_keyboard_package(struct efi_hii_packagelist *hii,
+ struct efi_hii_keyboard_package *keyboard_package)
+{
+ struct efi_keyboard_package_data *package_data;
+ struct efi_hii_keyboard_layout *layout;
+ struct efi_keyboard_layout_data *layout_data;
+ u16 layout_count, layout_length;
+ int i;
+
+ package_data = malloc(sizeof(*package_data));
+ if (!package_data)
+ return EFI_OUT_OF_RESOURCES;
+ INIT_LIST_HEAD(&package_data->link);
+ INIT_LIST_HEAD(&package_data->keyboard_layout_list);
+
+ layout = &keyboard_package->layout[0];
+ layout_count = get_unaligned_le16(&keyboard_package->layout_count);
+ for (i = 0; i < layout_count; i++) {
+ layout_length = get_unaligned_le16(&layout->layout_length);
+ layout_data = malloc(sizeof(*layout_data) + layout_length);
+ if (!layout_data)
+ goto out;
+
+ memcpy(&layout_data->keyboard_layout, layout, layout_length);
+ list_add_tail(&layout_data->link,
+ &package_data->keyboard_layout_list);
+ list_add_tail(&layout_data->link_sys,
+ &efi_keyboard_layout_list);
+
+ layout += layout_length;
+ }
+
+ list_add_tail(&package_data->link, &hii->keyboard_packages);
+
+ return EFI_SUCCESS;
+
+out:
+ free_keyboard_layouts(package_data);
+ free(package_data);
+
+ return EFI_OUT_OF_RESOURCES;
+}
+
+static struct efi_hii_packagelist *new_packagelist(void)
+{
+ struct efi_hii_packagelist *hii;
+
+ hii = malloc(sizeof(*hii));
+ list_add_tail(&hii->link, &efi_package_lists);
+ hii->max_string_id = 0;
+ INIT_LIST_HEAD(&hii->string_tables);
+ INIT_LIST_HEAD(&hii->guid_list);
+ INIT_LIST_HEAD(&hii->keyboard_packages);
+
+ return hii;
+}
+
+static void free_packagelist(struct efi_hii_packagelist *hii)
+{
+ remove_strings_package(hii);
+ remove_guid_package(hii);
+ remove_keyboard_package(hii);
+
+ list_del(&hii->link);
+ free(hii);
+}
+
+static efi_status_t
+add_packages(struct efi_hii_packagelist *hii,
+ const struct efi_hii_package_list_header *package_list)
+{
+ struct efi_hii_package_header *package;
+ void *end;
+ efi_status_t ret = EFI_SUCCESS;
+
+ end = ((void *)package_list)
+ + get_unaligned_le32(&package_list->package_length);
+
+ EFI_PRINT("package_list: %pUl (%u)\n", &package_list->package_list_guid,
+ get_unaligned_le32(&package_list->package_length));
+
+ package = ((void *)package_list) + sizeof(*package_list);
+ while ((void *)package < end) {
+ EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
+ efi_hii_package_type(package),
+ efi_hii_package_len(package));
+
+ switch (efi_hii_package_type(package)) {
+ case EFI_HII_PACKAGE_TYPE_GUID:
+ ret = add_guid_package(hii,
+ (struct efi_hii_guid_package *)package);
+ break;
+ case EFI_HII_PACKAGE_FORMS:
+ EFI_PRINT("Form package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_STRINGS:
+ ret = add_strings_package(hii,
+ (struct efi_hii_strings_package *)package);
+ break;
+ case EFI_HII_PACKAGE_FONTS:
+ EFI_PRINT("Font package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_IMAGES:
+ EFI_PRINT("Image package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_SIMPLE_FONTS:
+ EFI_PRINT("Simple font package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_DEVICE_PATH:
+ EFI_PRINT("Device path package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
+ ret = add_keyboard_package(hii,
+ (struct efi_hii_keyboard_package *)package);
+ break;
+ case EFI_HII_PACKAGE_ANIMATIONS:
+ EFI_PRINT("Animation package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_END:
+ goto out;
+ case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN:
+ case EFI_HII_PACKAGE_TYPE_SYSTEM_END:
+ default:
+ break;
+ }
+
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ package = (void *)package + efi_hii_package_len(package);
+ }
+out:
+ // TODO in theory there is some notifications that should be sent..
+ return EFI_SUCCESS;
+}
+
+/*
+ * EFI_HII_DATABASE_PROTOCOL
+ */
+
+static efi_status_t EFIAPI
+new_package_list(const struct efi_hii_database_protocol *this,
+ const struct efi_hii_package_list_header *package_list,
+ const efi_handle_t driver_handle,
+ efi_hii_handle_t *handle)
+{
+ struct efi_hii_packagelist *hii;
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p, %p, %p", this, package_list, driver_handle, handle);
+
+ if (!package_list || !handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ hii = new_packagelist();
+ if (!hii)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ ret = add_packages(hii, package_list);
+ if (ret != EFI_SUCCESS) {
+ free_packagelist(hii);
+ return EFI_EXIT(ret);
+ }
+
+ hii->driver_handle = driver_handle;
+ *handle = hii;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+remove_package_list(const struct efi_hii_database_protocol *this,
+ efi_hii_handle_t handle)
+{
+ struct efi_hii_packagelist *hii = handle;
+
+ EFI_ENTRY("%p, %p", this, handle);
+
+ if (!handle || !efi_hii_packagelist_exists(handle))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ free_packagelist(hii);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+update_package_list(const struct efi_hii_database_protocol *this,
+ efi_hii_handle_t handle,
+ const struct efi_hii_package_list_header *package_list)
+{
+ struct efi_hii_packagelist *hii = handle;
+ struct efi_hii_package_header *package;
+ void *end;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p, %p", this, handle, package_list);
+
+ if (!handle || !efi_hii_packagelist_exists(handle))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!package_list)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ EFI_PRINT("package_list: %pUl (%u)\n", &package_list->package_list_guid,
+ get_unaligned_le32(&package_list->package_length));
+
+ package = ((void *)package_list) + sizeof(*package_list);
+ end = ((void *)package_list)
+ + get_unaligned_le32(&package_list->package_length);
+
+ while ((void *)package < end) {
+ EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
+ efi_hii_package_type(package),
+ efi_hii_package_len(package));
+
+ switch (efi_hii_package_type(package)) {
+ case EFI_HII_PACKAGE_TYPE_GUID:
+ remove_guid_package(hii);
+ break;
+ case EFI_HII_PACKAGE_FORMS:
+ EFI_PRINT("Form package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_STRINGS:
+ remove_strings_package(hii);
+ break;
+ case EFI_HII_PACKAGE_FONTS:
+ EFI_PRINT("Font package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_IMAGES:
+ EFI_PRINT("Image package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_SIMPLE_FONTS:
+ EFI_PRINT("Simple font package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_DEVICE_PATH:
+ EFI_PRINT("Device path package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
+ remove_keyboard_package(hii);
+ break;
+ case EFI_HII_PACKAGE_ANIMATIONS:
+ EFI_PRINT("Animation package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_END:
+ goto out;
+ case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN:
+ case EFI_HII_PACKAGE_TYPE_SYSTEM_END:
+ default:
+ break;
+ }
+
+ /* TODO: already removed some packages */
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(ret);
+
+ package = ((void *)package)
+ + efi_hii_package_len(package);
+ }
+out:
+ ret = add_packages(hii, package_list);
+
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI
+list_package_lists(const struct efi_hii_database_protocol *this,
+ u8 package_type,
+ const efi_guid_t *package_guid,
+ efi_uintn_t *handle_buffer_length,
+ efi_hii_handle_t *handle)
+{
+ struct efi_hii_packagelist *hii =
+ (struct efi_hii_packagelist *)handle;
+ int package_cnt, package_max;
+ efi_status_t ret = EFI_NOT_FOUND;
+
+ EFI_ENTRY("%p, %u, %pUl, %p, %p", this, package_type, package_guid,
+ handle_buffer_length, handle);
+
+ if (!handle_buffer_length ||
+ (*handle_buffer_length && !handle)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) ||
+ (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ EFI_PRINT("package type=%x, guid=%pUl, length=%zu\n", (int)package_type,
+ package_guid, *handle_buffer_length);
+
+ package_cnt = 0;
+ package_max = *handle_buffer_length / sizeof(*handle);
+ list_for_each_entry(hii, &efi_package_lists, link) {
+ switch (package_type) {
+ case EFI_HII_PACKAGE_TYPE_ALL:
+ break;
+ case EFI_HII_PACKAGE_TYPE_GUID:
+ if (!list_empty(&hii->guid_list))
+ break;
+ continue;
+ case EFI_HII_PACKAGE_STRINGS:
+ if (!list_empty(&hii->string_tables))
+ break;
+ continue;
+ case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
+ if (!list_empty(&hii->keyboard_packages))
+ break;
+ continue;
+ default:
+ continue;
+ }
+
+ package_cnt++;
+ if (package_cnt <= package_max) {
+ *handle++ = hii;
+ ret = EFI_SUCCESS;
+ } else {
+ ret = EFI_BUFFER_TOO_SMALL;
+ }
+ }
+ *handle_buffer_length = package_cnt * sizeof(*handle);
+out:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI
+export_package_lists(const struct efi_hii_database_protocol *this,
+ efi_hii_handle_t handle,
+ efi_uintn_t *buffer_size,
+ struct efi_hii_package_list_header *buffer)
+{
+ EFI_ENTRY("%p, %p, %p, %p", this, handle, buffer_size, buffer);
+
+ if (!buffer_size || !buffer)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+register_package_notify(const struct efi_hii_database_protocol *this,
+ u8 package_type,
+ const efi_guid_t *package_guid,
+ const void *package_notify_fn,
+ efi_uintn_t notify_type,
+ efi_handle_t *notify_handle)
+{
+ EFI_ENTRY("%p, %u, %pUl, %p, %zu, %p", this, package_type,
+ package_guid, package_notify_fn, notify_type,
+ notify_handle);
+
+ if (!notify_handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) ||
+ (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+unregister_package_notify(const struct efi_hii_database_protocol *this,
+ efi_handle_t notification_handle)
+{
+ EFI_ENTRY("%p, %p", this, notification_handle);
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+find_keyboard_layouts(const struct efi_hii_database_protocol *this,
+ u16 *key_guid_buffer_length,
+ efi_guid_t *key_guid_buffer)
+{
+ struct efi_keyboard_layout_data *layout_data;
+ int package_cnt, package_max;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p, %p", this, key_guid_buffer_length, key_guid_buffer);
+
+ if (!key_guid_buffer_length ||
+ (*key_guid_buffer_length && !key_guid_buffer))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ package_cnt = 0;
+ package_max = *key_guid_buffer_length / sizeof(*key_guid_buffer);
+ list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) {
+ package_cnt++;
+ if (package_cnt <= package_max)
+ memcpy(key_guid_buffer++,
+ &layout_data->keyboard_layout.guid,
+ sizeof(*key_guid_buffer));
+ else
+ ret = EFI_BUFFER_TOO_SMALL;
+ }
+ *key_guid_buffer_length = package_cnt * sizeof(*key_guid_buffer);
+
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI
+get_keyboard_layout(const struct efi_hii_database_protocol *this,
+ efi_guid_t *key_guid,
+ u16 *keyboard_layout_length,
+ struct efi_hii_keyboard_layout *keyboard_layout)
+{
+ struct efi_keyboard_layout_data *layout_data;
+ u16 layout_length;
+
+ EFI_ENTRY("%p, %pUl, %p, %p", this, key_guid, keyboard_layout_length,
+ keyboard_layout);
+
+ if (!keyboard_layout_length ||
+ (*keyboard_layout_length && !keyboard_layout))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* TODO: no notion of current keyboard layout */
+ if (!key_guid)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) {
+ if (!guidcmp(&layout_data->keyboard_layout.guid, key_guid))
+ goto found;
+ }
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+found:
+ layout_length =
+ get_unaligned_le16(&layout_data->keyboard_layout.layout_length);
+ if (*keyboard_layout_length < layout_length) {
+ *keyboard_layout_length = layout_length;
+ return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ }
+
+ memcpy(keyboard_layout, &layout_data->keyboard_layout, layout_length);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+set_keyboard_layout(const struct efi_hii_database_protocol *this,
+ efi_guid_t *key_guid)
+{
+ EFI_ENTRY("%p, %pUl", this, key_guid);
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+get_package_list_handle(const struct efi_hii_database_protocol *this,
+ efi_hii_handle_t package_list_handle,
+ efi_handle_t *driver_handle)
+{
+ struct efi_hii_packagelist *hii;
+
+ EFI_ENTRY("%p, %p, %p", this, package_list_handle, driver_handle);
+
+ if (!driver_handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(hii, &efi_package_lists, link) {
+ if (hii == package_list_handle) {
+ *driver_handle = hii->driver_handle;
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+const struct efi_hii_database_protocol efi_hii_database = {
+ .new_package_list = new_package_list,
+ .remove_package_list = remove_package_list,
+ .update_package_list = update_package_list,
+ .list_package_lists = list_package_lists,
+ .export_package_lists = export_package_lists,
+ .register_package_notify = register_package_notify,
+ .unregister_package_notify = unregister_package_notify,
+ .find_keyboard_layouts = find_keyboard_layouts,
+ .get_keyboard_layout = get_keyboard_layout,
+ .set_keyboard_layout = set_keyboard_layout,
+ .get_package_list_handle = get_package_list_handle
+};
+
+/*
+ * EFI_HII_STRING_PROTOCOL
+ */
+
+static bool language_match(char *language, char *languages)
+{
+ size_t n;
+
+ n = strlen(language);
+ /* match primary language? */
+ if (!strncasecmp(language, languages, n) &&
+ (languages[n] == ';' || languages[n] == '\0'))
+ return true;
+
+ return false;
+}
+
+static efi_status_t EFIAPI
+new_string(const struct efi_hii_string_protocol *this,
+ efi_hii_handle_t package_list,
+ efi_string_id_t *string_id,
+ const u8 *language,
+ const u16 *language_name,
+ const efi_string_t string,
+ const struct efi_font_info *string_font_info)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+
+ EFI_ENTRY("%p, %p, %p, \"%s\", %p, \"%ls\", %p", this, package_list,
+ string_id, language, language_name, string,
+ string_font_info);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!string_id || !language || !string)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (language_match((char *)language, stbl->language)) {
+ efi_string_id_t new_id;
+ void *buf;
+ efi_string_t str;
+
+ new_id = ++hii->max_string_id;
+ if (stbl->nstrings < new_id) {
+ buf = realloc(stbl->strings,
+ sizeof(stbl->strings[0])
+ * new_id);
+ if (!buf)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ memset(&stbl->strings[stbl->nstrings], 0,
+ (new_id - stbl->nstrings)
+ * sizeof(stbl->strings[0]));
+ stbl->strings = buf;
+ stbl->nstrings = new_id;
+ }
+
+ str = u16_strdup(string);
+ if (!str)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ stbl->strings[new_id - 1].string = str;
+ *string_id = new_id;
+
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+get_string(const struct efi_hii_string_protocol *this,
+ const u8 *language,
+ efi_hii_handle_t package_list,
+ efi_string_id_t string_id,
+ efi_string_t string,
+ efi_uintn_t *string_size,
+ struct efi_font_info **string_font_info)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+
+ EFI_ENTRY("%p, \"%s\", %p, %u, %p, %p, %p", this, language,
+ package_list, string_id, string, string_size,
+ string_font_info);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (language_match((char *)language, stbl->language)) {
+ efi_string_t str;
+ size_t len;
+
+ if (stbl->nstrings < string_id)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ str = stbl->strings[string_id - 1].string;
+ if (str) {
+ len = (u16_strlen(str) + 1) * sizeof(u16);
+ if (*string_size < len) {
+ *string_size = len;
+
+ return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ }
+ memcpy(string, str, len);
+ *string_size = len;
+ } else {
+ return EFI_EXIT(EFI_NOT_FOUND);
+ }
+
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+set_string(const struct efi_hii_string_protocol *this,
+ efi_hii_handle_t package_list,
+ efi_string_id_t string_id,
+ const u8 *language,
+ const efi_string_t string,
+ const struct efi_font_info *string_font_info)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+
+ EFI_ENTRY("%p, %p, %u, \"%s\", \"%ls\", %p", this, package_list,
+ string_id, language, string, string_font_info);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (string_id > hii->max_string_id)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!string || !language)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (language_match((char *)language, stbl->language)) {
+ efi_string_t str;
+
+ if (hii->max_string_id < string_id)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (stbl->nstrings < string_id) {
+ void *buf;
+
+ buf = realloc(stbl->strings,
+ string_id
+ * sizeof(stbl->strings[0]));
+ if (!buf)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ memset(&stbl->strings[string_id - 1], 0,
+ (string_id - stbl->nstrings)
+ * sizeof(stbl->strings[0]));
+ stbl->strings = buf;
+ }
+
+ str = u16_strdup(string);
+ if (!str)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ free(stbl->strings[string_id - 1].string);
+ stbl->strings[string_id - 1].string = str;
+
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+get_languages(const struct efi_hii_string_protocol *this,
+ efi_hii_handle_t package_list,
+ u8 *languages,
+ efi_uintn_t *languages_size)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+ size_t len = 0;
+ char *p;
+
+ EFI_ENTRY("%p, %p, %p, %p", this, package_list, languages,
+ languages_size);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!languages_size ||
+ (*languages_size && !languages))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* figure out required size: */
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ len += strlen((char *)stbl->language) + 1;
+ }
+
+ if (*languages_size < len) {
+ *languages_size = len;
+
+ return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ }
+
+ p = (char *)languages;
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (p != (char *)languages)
+ *p++ = ';';
+ strcpy(p, stbl->language);
+ p += strlen((char *)stbl->language);
+ }
+ *p = '\0';
+
+ EFI_PRINT("languages: %s\n", languages);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+get_secondary_languages(const struct efi_hii_string_protocol *this,
+ efi_hii_handle_t package_list,
+ const u8 *primary_language,
+ u8 *secondary_languages,
+ efi_uintn_t *secondary_languages_size)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+ bool found = false;
+
+ EFI_ENTRY("%p, %p, \"%s\", %p, %p", this, package_list,
+ primary_language, secondary_languages,
+ secondary_languages_size);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!secondary_languages_size ||
+ (*secondary_languages_size && !secondary_languages))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (language_match((char *)primary_language, stbl->language)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return EFI_EXIT(EFI_INVALID_LANGUAGE);
+
+ /*
+ * TODO: What is secondary language?
+ * *secondary_languages = '\0';
+ * *secondary_languages_size = 0;
+ */
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+const struct efi_hii_string_protocol efi_hii_string = {
+ .new_string = new_string,
+ .get_string = get_string,
+ .set_string = set_string,
+ .get_languages = get_languages,
+ .get_secondary_languages = get_secondary_languages
+};
diff --git a/roms/u-boot/lib/efi_loader/efi_hii_config.c b/roms/u-boot/lib/efi_loader/efi_hii_config.c
new file mode 100644
index 000000000..237e8acf8
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_hii_config.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Human Interface Infrastructure ... Configuration
+ *
+ * Copyright (c) 2017 Leif Lindholm
+ * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
+ *
+ * As this is still a non-working stub and the protocol is neither required
+ * by the EFI shell nor by the UEFI SCT this module has been removed from
+ * the Makefile.
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+const efi_guid_t efi_guid_hii_config_routing_protocol
+ = EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID;
+const efi_guid_t efi_guid_hii_config_access_protocol
+ = EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID;
+
+/*
+ * EFI_HII_CONFIG_ROUTING_PROTOCOL
+ */
+
+static efi_status_t EFIAPI
+extract_config(const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t request,
+ efi_string_t *progress,
+ efi_string_t *results)
+{
+ EFI_ENTRY("%p, \"%ls\", %p, %p", this, request, progress, results);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+export_config(const struct efi_hii_config_routing_protocol *this,
+ efi_string_t *results)
+{
+ EFI_ENTRY("%p, %p", this, results);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+route_config(const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t configuration,
+ efi_string_t *progress)
+{
+ EFI_ENTRY("%p, \"%ls\", %p", this, configuration, progress);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+block_to_config(const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t config_request,
+ const u8 *block,
+ const efi_uintn_t block_size,
+ efi_string_t *config,
+ efi_string_t *progress)
+{
+ EFI_ENTRY("%p, \"%ls\", %p, %zu, %p, %p", this, config_request,
+ block, block_size, config, progress);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+config_to_block(const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t config_resp,
+ const u8 *block,
+ const efi_uintn_t *block_size,
+ efi_string_t *progress)
+{
+ EFI_ENTRY("%p, \"%ls\", %p, %p, %p", this, config_resp,
+ block, block_size, progress);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+get_alt_config(const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t config_resp,
+ const efi_guid_t *guid,
+ const efi_string_t name,
+ const struct efi_device_path *device_path,
+ const efi_string_t alt_cfg_id,
+ efi_string_t *alt_cfg_resp)
+{
+ EFI_ENTRY("%p, \"%ls\", %pUl, \"%ls\", %p, \"%ls\", %p",
+ this, config_resp, guid, name, device_path,
+ alt_cfg_id, alt_cfg_resp);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+/*
+ * EFI_HII_ACCESS_PROTOCOL
+ */
+
+efi_status_t EFIAPI
+extract_config_access(const struct efi_hii_config_access_protocol *this,
+ const efi_string_t request,
+ efi_string_t *progress,
+ efi_string_t *results)
+{
+ EFI_ENTRY("%p, \"%ls\", %p, %p", this, request, progress, results);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+};
+
+efi_status_t EFIAPI
+route_config_access(const struct efi_hii_config_access_protocol *this,
+ const efi_string_t configuration,
+ efi_string_t *progress)
+{
+ EFI_ENTRY("%p, \"%ls\", %p", this, configuration, progress);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+};
+
+efi_status_t EFIAPI
+form_callback(const struct efi_hii_config_access_protocol *this,
+ efi_browser_action_t action,
+ efi_question_id_t question_id,
+ u8 type,
+ union efi_ifr_type_value *value,
+ efi_browser_action_request_t *action_request)
+{
+ EFI_ENTRY("%p, 0x%zx, 0x%x, 0x%x, %p, %p", this, action,
+ question_id, type, value, action_request);
+
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+};
+
+const struct efi_hii_config_routing_protocol efi_hii_config_routing = {
+ .extract_config = extract_config,
+ .export_config = export_config,
+ .route_config = route_config,
+ .block_to_config = block_to_config,
+ .config_to_block = config_to_block,
+ .get_alt_config = get_alt_config
+};
+
+const struct efi_hii_config_access_protocol efi_hii_config_access = {
+ .extract_config_access = extract_config_access,
+ .route_config_access = route_config_access,
+ .form_callback = form_callback
+};
diff --git a/roms/u-boot/lib/efi_loader/efi_image_loader.c b/roms/u-boot/lib/efi_loader/efi_image_loader.c
new file mode 100644
index 000000000..a0eb63fce
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_image_loader.c
@@ -0,0 +1,966 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI image loader
+ *
+ * based partly on wine code
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <cpu_func.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <malloc.h>
+#include <pe.h>
+#include <sort.h>
+#include <crypto/pkcs7_parser.h>
+#include <linux/err.h>
+
+const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
+const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
+const efi_guid_t efi_guid_loaded_image = EFI_LOADED_IMAGE_PROTOCOL_GUID;
+const efi_guid_t efi_guid_loaded_image_device_path =
+ EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
+const efi_guid_t efi_simple_file_system_protocol_guid =
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
+
+static int machines[] = {
+#if defined(__aarch64__)
+ IMAGE_FILE_MACHINE_ARM64,
+#elif defined(__arm__)
+ IMAGE_FILE_MACHINE_ARM,
+ IMAGE_FILE_MACHINE_THUMB,
+ IMAGE_FILE_MACHINE_ARMNT,
+#endif
+
+#if defined(__x86_64__)
+ IMAGE_FILE_MACHINE_AMD64,
+#elif defined(__i386__)
+ IMAGE_FILE_MACHINE_I386,
+#endif
+
+#if defined(__riscv) && (__riscv_xlen == 32)
+ IMAGE_FILE_MACHINE_RISCV32,
+#endif
+
+#if defined(__riscv) && (__riscv_xlen == 64)
+ IMAGE_FILE_MACHINE_RISCV64,
+#endif
+ 0 };
+
+/**
+ * efi_print_image_info() - print information about a loaded image
+ *
+ * If the program counter is located within the image the offset to the base
+ * address is shown.
+ *
+ * @obj: EFI object
+ * @image: loaded image
+ * @pc: program counter (use NULL to suppress offset output)
+ * Return: status code
+ */
+static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj,
+ struct efi_loaded_image *image,
+ void *pc)
+{
+ printf("UEFI image");
+ printf(" [0x%p:0x%p]",
+ image->image_base, image->image_base + image->image_size - 1);
+ if (pc && pc >= image->image_base &&
+ pc < image->image_base + image->image_size)
+ printf(" pc=0x%zx", pc - image->image_base);
+ if (image->file_path)
+ printf(" '%pD'", image->file_path);
+ printf("\n");
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_print_image_infos() - print information about all loaded images
+ *
+ * @pc: program counter (use NULL to suppress offset output)
+ */
+void efi_print_image_infos(void *pc)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ if (!guidcmp(handler->guid, &efi_guid_loaded_image)) {
+ efi_print_image_info(
+ (struct efi_loaded_image_obj *)efiobj,
+ handler->protocol_interface, pc);
+ }
+ }
+ }
+}
+
+/**
+ * efi_loader_relocate() - relocate UEFI binary
+ *
+ * @rel: pointer to the relocation table
+ * @rel_size: size of the relocation table in bytes
+ * @efi_reloc: actual load address of the image
+ * @pref_address: preferred load address of the image
+ * Return: status code
+ */
+static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
+ unsigned long rel_size, void *efi_reloc,
+ unsigned long pref_address)
+{
+ unsigned long delta = (unsigned long)efi_reloc - pref_address;
+ const IMAGE_BASE_RELOCATION *end;
+ int i;
+
+ if (delta == 0)
+ return EFI_SUCCESS;
+
+ end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
+ while (rel < end && rel->SizeOfBlock) {
+ const uint16_t *relocs = (const uint16_t *)(rel + 1);
+ i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
+ while (i--) {
+ uint32_t offset = (uint32_t)(*relocs & 0xfff) +
+ rel->VirtualAddress;
+ int type = *relocs >> EFI_PAGE_SHIFT;
+ uint64_t *x64 = efi_reloc + offset;
+ uint32_t *x32 = efi_reloc + offset;
+ uint16_t *x16 = efi_reloc + offset;
+
+ switch (type) {
+ case IMAGE_REL_BASED_ABSOLUTE:
+ break;
+ case IMAGE_REL_BASED_HIGH:
+ *x16 += ((uint32_t)delta) >> 16;
+ break;
+ case IMAGE_REL_BASED_LOW:
+ *x16 += (uint16_t)delta;
+ break;
+ case IMAGE_REL_BASED_HIGHLOW:
+ *x32 += (uint32_t)delta;
+ break;
+ case IMAGE_REL_BASED_DIR64:
+ *x64 += (uint64_t)delta;
+ break;
+#ifdef __riscv
+ case IMAGE_REL_BASED_RISCV_HI20:
+ *x32 = ((*x32 & 0xfffff000) + (uint32_t)delta) |
+ (*x32 & 0x00000fff);
+ break;
+ case IMAGE_REL_BASED_RISCV_LOW12I:
+ case IMAGE_REL_BASED_RISCV_LOW12S:
+ /* We know that we're 4k aligned */
+ if (delta & 0xfff) {
+ log_err("Unsupported reloc offset\n");
+ return EFI_LOAD_ERROR;
+ }
+ break;
+#endif
+ default:
+ log_err("Unknown Relocation off %x type %x\n",
+ offset, type);
+ return EFI_LOAD_ERROR;
+ }
+ relocs++;
+ }
+ rel = (const IMAGE_BASE_RELOCATION *)relocs;
+ }
+ return EFI_SUCCESS;
+}
+
+void __weak invalidate_icache_all(void)
+{
+ /* If the system doesn't support icache_all flush, cross our fingers */
+}
+
+/**
+ * efi_set_code_and_data_type() - determine the memory types to be used for code
+ * and data.
+ *
+ * @loaded_image_info: image descriptor
+ * @image_type: field Subsystem of the optional header for
+ * Windows specific field
+ */
+static void efi_set_code_and_data_type(
+ struct efi_loaded_image *loaded_image_info,
+ uint16_t image_type)
+{
+ switch (image_type) {
+ case IMAGE_SUBSYSTEM_EFI_APPLICATION:
+ loaded_image_info->image_code_type = EFI_LOADER_CODE;
+ loaded_image_info->image_data_type = EFI_LOADER_DATA;
+ break;
+ case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
+ loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE;
+ loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA;
+ break;
+ case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
+ case IMAGE_SUBSYSTEM_EFI_ROM:
+ loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE;
+ loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA;
+ break;
+ default:
+ log_err("invalid image type: %u\n", image_type);
+ /* Let's assume it is an application */
+ loaded_image_info->image_code_type = EFI_LOADER_CODE;
+ loaded_image_info->image_data_type = EFI_LOADER_DATA;
+ break;
+ }
+}
+
+/**
+ * efi_image_region_add() - add an entry of region
+ * @regs: Pointer to array of regions
+ * @start: Start address of region (included)
+ * @end: End address of region (excluded)
+ * @nocheck: flag against overlapped regions
+ *
+ * Take one entry of region \[@start, @end\[ and insert it into the list.
+ *
+ * * If @nocheck is false, the list will be sorted ascending by address.
+ * Overlapping entries will not be allowed.
+ *
+ * * If @nocheck is true, the list will be sorted ascending by sequence
+ * of adding the entries. Overlapping is allowed.
+ *
+ * Return: status code
+ */
+efi_status_t efi_image_region_add(struct efi_image_regions *regs,
+ const void *start, const void *end,
+ int nocheck)
+{
+ struct image_region *reg;
+ int i, j;
+
+ if (regs->num >= regs->max) {
+ EFI_PRINT("%s: no more room for regions\n", __func__);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (end < start)
+ return EFI_INVALID_PARAMETER;
+
+ for (i = 0; i < regs->num; i++) {
+ reg = &regs->reg[i];
+ if (nocheck)
+ continue;
+
+ /* new data after registered region */
+ if (start >= reg->data + reg->size)
+ continue;
+
+ /* new data preceding registered region */
+ if (end <= reg->data) {
+ for (j = regs->num - 1; j >= i; j--)
+ memcpy(&regs->reg[j + 1], &regs->reg[j],
+ sizeof(*reg));
+ break;
+ }
+
+ /* new data overlapping registered region */
+ EFI_PRINT("%s: new region already part of another\n", __func__);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ reg = &regs->reg[i];
+ reg->data = start;
+ reg->size = end - start;
+ regs->num++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * cmp_pe_section() - compare virtual addresses of two PE image sections
+ * @arg1: pointer to pointer to first section header
+ * @arg2: pointer to pointer to second section header
+ *
+ * Compare the virtual addresses of two sections of an portable executable.
+ * The arguments are defined as const void * to allow usage with qsort().
+ *
+ * Return: -1 if the virtual address of arg1 is less than that of arg2,
+ * 0 if the virtual addresses are equal, 1 if the virtual address
+ * of arg1 is greater than that of arg2.
+ */
+static int cmp_pe_section(const void *arg1, const void *arg2)
+{
+ const IMAGE_SECTION_HEADER *section1, *section2;
+
+ section1 = *((const IMAGE_SECTION_HEADER **)arg1);
+ section2 = *((const IMAGE_SECTION_HEADER **)arg2);
+
+ if (section1->VirtualAddress < section2->VirtualAddress)
+ return -1;
+ else if (section1->VirtualAddress == section2->VirtualAddress)
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * efi_prepare_aligned_image() - prepare 8-byte aligned image
+ * @efi: pointer to the EFI binary
+ * @efi_size: size of @efi binary
+ *
+ * If @efi is not 8-byte aligned, this function newly allocates
+ * the image buffer.
+ *
+ * Return: valid pointer to a image, return NULL if allocation fails.
+ */
+void *efi_prepare_aligned_image(void *efi, u64 *efi_size)
+{
+ size_t new_efi_size;
+ void *new_efi;
+
+ /*
+ * Size must be 8-byte aligned and the trailing bytes must be
+ * zero'ed. Otherwise hash value may be incorrect.
+ */
+ if (!IS_ALIGNED(*efi_size, 8)) {
+ new_efi_size = ALIGN(*efi_size, 8);
+ new_efi = calloc(new_efi_size, 1);
+ if (!new_efi)
+ return NULL;
+ memcpy(new_efi, efi, *efi_size);
+ *efi_size = new_efi_size;
+ return new_efi;
+ } else {
+ return efi;
+ }
+}
+
+/**
+ * efi_image_parse() - parse a PE image
+ * @efi: Pointer to image
+ * @len: Size of @efi
+ * @regp: Pointer to a list of regions
+ * @auth: Pointer to a pointer to authentication data in PE
+ * @auth_len: Size of @auth
+ *
+ * Parse image binary in PE32(+) format, assuming that sanity of PE image
+ * has been checked by a caller.
+ * On success, an address of authentication data in @efi and its size will
+ * be returned in @auth and @auth_len, respectively.
+ *
+ * Return: true on success, false on error
+ */
+bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
+ WIN_CERTIFICATE **auth, size_t *auth_len)
+{
+ struct efi_image_regions *regs;
+ IMAGE_DOS_HEADER *dos;
+ IMAGE_NT_HEADERS32 *nt;
+ IMAGE_SECTION_HEADER *sections, **sorted;
+ int num_regions, num_sections, i;
+ int ctidx = IMAGE_DIRECTORY_ENTRY_SECURITY;
+ u32 align, size, authsz, authoff;
+ size_t bytes_hashed;
+
+ dos = (void *)efi;
+ nt = (void *)(efi + dos->e_lfanew);
+ authoff = 0;
+ authsz = 0;
+
+ /*
+ * Count maximum number of regions to be digested.
+ * We don't have to have an exact number here.
+ * See efi_image_region_add()'s in parsing below.
+ */
+ num_regions = 3; /* for header */
+ num_regions += nt->FileHeader.NumberOfSections;
+ num_regions++; /* for extra */
+
+ regs = calloc(sizeof(*regs) + sizeof(struct image_region) * num_regions,
+ 1);
+ if (!regs)
+ goto err;
+ regs->max = num_regions;
+
+ /*
+ * Collect data regions for hash calculation
+ * 1. File headers
+ */
+ if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
+ IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
+
+ /* Skip CheckSum */
+ efi_image_region_add(regs, efi, &opt->CheckSum, 0);
+ if (nt64->OptionalHeader.NumberOfRvaAndSizes <= ctidx) {
+ efi_image_region_add(regs,
+ &opt->Subsystem,
+ efi + opt->SizeOfHeaders, 0);
+ } else {
+ /* Skip Certificates Table */
+ efi_image_region_add(regs,
+ &opt->Subsystem,
+ &opt->DataDirectory[ctidx], 0);
+ efi_image_region_add(regs,
+ &opt->DataDirectory[ctidx] + 1,
+ efi + opt->SizeOfHeaders, 0);
+
+ authoff = opt->DataDirectory[ctidx].VirtualAddress;
+ authsz = opt->DataDirectory[ctidx].Size;
+ }
+
+ bytes_hashed = opt->SizeOfHeaders;
+ align = opt->FileAlignment;
+ } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
+
+ /* Skip CheckSum */
+ efi_image_region_add(regs, efi, &opt->CheckSum, 0);
+ if (nt->OptionalHeader.NumberOfRvaAndSizes <= ctidx) {
+ efi_image_region_add(regs,
+ &opt->Subsystem,
+ efi + opt->SizeOfHeaders, 0);
+ } else {
+ /* Skip Certificates Table */
+ efi_image_region_add(regs, &opt->Subsystem,
+ &opt->DataDirectory[ctidx], 0);
+ efi_image_region_add(regs,
+ &opt->DataDirectory[ctidx] + 1,
+ efi + opt->SizeOfHeaders, 0);
+
+ authoff = opt->DataDirectory[ctidx].VirtualAddress;
+ authsz = opt->DataDirectory[ctidx].Size;
+ }
+
+ bytes_hashed = opt->SizeOfHeaders;
+ align = opt->FileAlignment;
+ } else {
+ EFI_PRINT("%s: Invalid optional header magic %x\n", __func__,
+ nt->OptionalHeader.Magic);
+ goto err;
+ }
+
+ /* 2. Sections */
+ num_sections = nt->FileHeader.NumberOfSections;
+ sections = (void *)((uint8_t *)&nt->OptionalHeader +
+ nt->FileHeader.SizeOfOptionalHeader);
+ sorted = calloc(sizeof(IMAGE_SECTION_HEADER *), num_sections);
+ if (!sorted) {
+ EFI_PRINT("%s: Out of memory\n", __func__);
+ goto err;
+ }
+
+ /*
+ * Make sure the section list is in ascending order.
+ */
+ for (i = 0; i < num_sections; i++)
+ sorted[i] = &sections[i];
+ qsort(sorted, num_sections, sizeof(sorted[0]), cmp_pe_section);
+
+ for (i = 0; i < num_sections; i++) {
+ if (!sorted[i]->SizeOfRawData)
+ continue;
+
+ size = (sorted[i]->SizeOfRawData + align - 1) & ~(align - 1);
+ efi_image_region_add(regs, efi + sorted[i]->PointerToRawData,
+ efi + sorted[i]->PointerToRawData + size,
+ 0);
+ EFI_PRINT("section[%d](%s): raw: 0x%x-0x%x, virt: %x-%x\n",
+ i, sorted[i]->Name,
+ sorted[i]->PointerToRawData,
+ sorted[i]->PointerToRawData + size,
+ sorted[i]->VirtualAddress,
+ sorted[i]->VirtualAddress
+ + sorted[i]->Misc.VirtualSize);
+
+ bytes_hashed += size;
+ }
+ free(sorted);
+
+ /* 3. Extra data excluding Certificates Table */
+ if (bytes_hashed + authsz < len) {
+ EFI_PRINT("extra data for hash: %zu\n",
+ len - (bytes_hashed + authsz));
+ efi_image_region_add(regs, efi + bytes_hashed,
+ efi + len - authsz, 0);
+ }
+
+ /* Return Certificates Table */
+ if (authsz) {
+ if (len < authoff + authsz) {
+ EFI_PRINT("%s: Size for auth too large: %u >= %zu\n",
+ __func__, authsz, len - authoff);
+ goto err;
+ }
+ if (authsz < sizeof(*auth)) {
+ EFI_PRINT("%s: Size for auth too small: %u < %zu\n",
+ __func__, authsz, sizeof(*auth));
+ goto err;
+ }
+ *auth = efi + authoff;
+ *auth_len = authsz;
+ EFI_PRINT("WIN_CERTIFICATE: 0x%x, size: 0x%x\n", authoff,
+ authsz);
+ } else {
+ *auth = NULL;
+ *auth_len = 0;
+ }
+
+ *regp = regs;
+
+ return true;
+
+err:
+ free(regs);
+
+ return false;
+}
+
+#ifdef CONFIG_EFI_SECURE_BOOT
+/**
+ * efi_image_unsigned_authenticate() - authenticate unsigned image with
+ * SHA256 hash
+ * @regs: List of regions to be verified
+ *
+ * If an image is not signed, it doesn't have a signature. In this case,
+ * its message digest is calculated and it will be compared with one of
+ * hash values stored in signature databases.
+ *
+ * Return: true if authenticated, false if not
+ */
+static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs)
+{
+ struct efi_signature_store *db = NULL, *dbx = NULL;
+ bool ret = false;
+
+ dbx = efi_sigstore_parse_sigdb(L"dbx");
+ if (!dbx) {
+ EFI_PRINT("Getting signature database(dbx) failed\n");
+ goto out;
+ }
+
+ db = efi_sigstore_parse_sigdb(L"db");
+ if (!db) {
+ EFI_PRINT("Getting signature database(db) failed\n");
+ goto out;
+ }
+
+ /* try black-list first */
+ if (efi_signature_lookup_digest(regs, dbx)) {
+ EFI_PRINT("Image is not signed and its digest found in \"dbx\"\n");
+ goto out;
+ }
+
+ /* try white-list */
+ if (efi_signature_lookup_digest(regs, db))
+ ret = true;
+ else
+ EFI_PRINT("Image is not signed and its digest not found in \"db\" or \"dbx\"\n");
+
+out:
+ efi_sigstore_free(db);
+ efi_sigstore_free(dbx);
+
+ return ret;
+}
+
+/**
+ * efi_image_authenticate() - verify a signature of signed image
+ * @efi: Pointer to image
+ * @efi_size: Size of @efi
+ *
+ * A signed image should have its signature stored in a table of its PE header.
+ * So if an image is signed and only if if its signature is verified using
+ * signature databases, an image is authenticated.
+ * If an image is not signed, its validity is checked by using
+ * efi_image_unsigned_authenticated().
+ * TODO:
+ * When AuditMode==0, if the image's signature is not found in
+ * the authorized database, or is found in the forbidden database,
+ * the image will not be started and instead, information about it
+ * will be placed in this table.
+ * When AuditMode==1, an EFI_IMAGE_EXECUTION_INFO element is created
+ * in the EFI_IMAGE_EXECUTION_INFO_TABLE for every certificate found
+ * in the certificate table of every image that is validated.
+ *
+ * Return: true if authenticated, false if not
+ */
+static bool efi_image_authenticate(void *efi, size_t efi_size)
+{
+ struct efi_image_regions *regs = NULL;
+ WIN_CERTIFICATE *wincerts = NULL, *wincert;
+ size_t wincerts_len;
+ struct pkcs7_message *msg = NULL;
+ struct efi_signature_store *db = NULL, *dbx = NULL;
+ void *new_efi = NULL;
+ u8 *auth, *wincerts_end;
+ size_t auth_size;
+ bool ret = false;
+
+ EFI_PRINT("%s: Enter, %d\n", __func__, ret);
+
+ if (!efi_secure_boot_enabled())
+ return true;
+
+ new_efi = efi_prepare_aligned_image(efi, (u64 *)&efi_size);
+ if (!new_efi)
+ return false;
+
+ if (!efi_image_parse(new_efi, efi_size, &regs, &wincerts,
+ &wincerts_len)) {
+ EFI_PRINT("Parsing PE executable image failed\n");
+ goto err;
+ }
+
+ if (!wincerts) {
+ /* The image is not signed */
+ ret = efi_image_unsigned_authenticate(regs);
+
+ goto err;
+ }
+
+ /*
+ * verify signature using db and dbx
+ */
+ db = efi_sigstore_parse_sigdb(L"db");
+ if (!db) {
+ EFI_PRINT("Getting signature database(db) failed\n");
+ goto err;
+ }
+
+ dbx = efi_sigstore_parse_sigdb(L"dbx");
+ if (!dbx) {
+ EFI_PRINT("Getting signature database(dbx) failed\n");
+ goto err;
+ }
+
+ if (efi_signature_lookup_digest(regs, dbx)) {
+ EFI_PRINT("Image's digest was found in \"dbx\"\n");
+ goto err;
+ }
+
+ /*
+ * go through WIN_CERTIFICATE list
+ * NOTE:
+ * We may have multiple signatures either as WIN_CERTIFICATE's
+ * in PE header, or as pkcs7 SignerInfo's in SignedData.
+ * So the verification policy here is:
+ * - Success if, at least, one of signatures is verified
+ * - unless signature is rejected explicitly with its digest.
+ */
+
+ for (wincert = wincerts, wincerts_end = (u8 *)wincerts + wincerts_len;
+ (u8 *)wincert < wincerts_end;
+ wincert = (WIN_CERTIFICATE *)
+ ((u8 *)wincert + ALIGN(wincert->dwLength, 8))) {
+ if ((u8 *)wincert + sizeof(*wincert) >= wincerts_end)
+ break;
+
+ if (wincert->dwLength <= sizeof(*wincert)) {
+ EFI_PRINT("dwLength too small: %u < %zu\n",
+ wincert->dwLength, sizeof(*wincert));
+ continue;
+ }
+
+ EFI_PRINT("WIN_CERTIFICATE_TYPE: 0x%x\n",
+ wincert->wCertificateType);
+
+ auth = (u8 *)wincert + sizeof(*wincert);
+ auth_size = wincert->dwLength - sizeof(*wincert);
+ if (wincert->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
+ if (auth + sizeof(efi_guid_t) >= wincerts_end)
+ break;
+
+ if (auth_size <= sizeof(efi_guid_t)) {
+ EFI_PRINT("dwLength too small: %u < %zu\n",
+ wincert->dwLength, sizeof(*wincert));
+ continue;
+ }
+ if (guidcmp(auth, &efi_guid_cert_type_pkcs7)) {
+ EFI_PRINT("Certificate type not supported: %pUl\n",
+ auth);
+ continue;
+ }
+
+ auth += sizeof(efi_guid_t);
+ auth_size -= sizeof(efi_guid_t);
+ } else if (wincert->wCertificateType
+ != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+ EFI_PRINT("Certificate type not supported\n");
+ continue;
+ }
+
+ msg = pkcs7_parse_message(auth, auth_size);
+ if (IS_ERR(msg)) {
+ EFI_PRINT("Parsing image's signature failed\n");
+ msg = NULL;
+ continue;
+ }
+
+ /*
+ * NOTE:
+ * UEFI specification defines two signature types possible
+ * in signature database:
+ * a. x509 certificate, where a signature in image is
+ * a message digest encrypted by RSA public key
+ * (EFI_CERT_X509_GUID)
+ * b. bare hash value of message digest
+ * (EFI_CERT_SHAxxx_GUID)
+ *
+ * efi_signature_verify() handles case (a), while
+ * efi_signature_lookup_digest() handles case (b).
+ *
+ * There is a third type:
+ * c. message digest of a certificate
+ * (EFI_CERT_X509_SHAAxxx_GUID)
+ * This type of signature is used only in revocation list
+ * (dbx) and handled as part of efi_signatgure_verify().
+ */
+ /* try black-list first */
+ if (efi_signature_verify_one(regs, msg, dbx)) {
+ EFI_PRINT("Signature was rejected by \"dbx\"\n");
+ continue;
+ }
+
+ if (!efi_signature_check_signers(msg, dbx)) {
+ EFI_PRINT("Signer(s) in \"dbx\"\n");
+ continue;
+ }
+
+ /* try white-list */
+ if (efi_signature_verify(regs, msg, db, dbx)) {
+ ret = true;
+ break;
+ }
+
+ EFI_PRINT("Signature was not verified by \"db\"\n");
+
+ if (efi_signature_lookup_digest(regs, db)) {
+ ret = true;
+ break;
+ }
+
+ EFI_PRINT("Image's digest was not found in \"db\" or \"dbx\"\n");
+ }
+
+err:
+ efi_sigstore_free(db);
+ efi_sigstore_free(dbx);
+ pkcs7_free_message(msg);
+ free(regs);
+ if (new_efi != efi)
+ free(new_efi);
+
+ EFI_PRINT("%s: Exit, %d\n", __func__, ret);
+ return ret;
+}
+#else
+static bool efi_image_authenticate(void *efi, size_t efi_size)
+{
+ return true;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
+
+/**
+ * efi_check_pe() - check if a memory buffer contains a PE-COFF image
+ *
+ * @buffer: buffer to check
+ * @size: size of buffer
+ * @nt_header: on return pointer to NT header of PE-COFF image
+ * Return: EFI_SUCCESS if the buffer contains a PE-COFF image
+ */
+efi_status_t efi_check_pe(void *buffer, size_t size, void **nt_header)
+{
+ IMAGE_DOS_HEADER *dos = buffer;
+ IMAGE_NT_HEADERS32 *nt;
+
+ if (size < sizeof(*dos))
+ return EFI_INVALID_PARAMETER;
+
+ /* Check for DOS magix */
+ if (dos->e_magic != IMAGE_DOS_SIGNATURE)
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * Check if the image section header fits into the file. Knowing that at
+ * least one section header follows we only need to check for the length
+ * of the 64bit header which is longer than the 32bit header.
+ */
+ if (size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS32))
+ return EFI_INVALID_PARAMETER;
+ nt = (IMAGE_NT_HEADERS32 *)((u8 *)buffer + dos->e_lfanew);
+
+ /* Check for PE-COFF magic */
+ if (nt->Signature != IMAGE_NT_SIGNATURE)
+ return EFI_INVALID_PARAMETER;
+
+ if (nt_header)
+ *nt_header = nt;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_load_pe() - relocate EFI binary
+ *
+ * This function loads all sections from a PE binary into a newly reserved
+ * piece of memory. On success the entry point is returned as handle->entry.
+ *
+ * @handle: loaded image handle
+ * @efi: pointer to the EFI binary
+ * @efi_size: size of @efi binary
+ * @loaded_image_info: loaded image protocol
+ * Return: status code
+ */
+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
+ void *efi, size_t efi_size,
+ struct efi_loaded_image *loaded_image_info)
+{
+ IMAGE_NT_HEADERS32 *nt;
+ IMAGE_DOS_HEADER *dos;
+ IMAGE_SECTION_HEADER *sections;
+ int num_sections;
+ void *efi_reloc;
+ int i;
+ const IMAGE_BASE_RELOCATION *rel;
+ unsigned long rel_size;
+ int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
+ uint64_t image_base;
+ unsigned long virt_size = 0;
+ int supported = 0;
+ efi_status_t ret;
+
+ ret = efi_check_pe(efi, efi_size, (void **)&nt);
+ if (ret != EFI_SUCCESS) {
+ log_err("Not a PE-COFF file\n");
+ return EFI_LOAD_ERROR;
+ }
+
+ for (i = 0; machines[i]; i++)
+ if (machines[i] == nt->FileHeader.Machine) {
+ supported = 1;
+ break;
+ }
+
+ if (!supported) {
+ log_err("Machine type 0x%04x is not supported\n",
+ nt->FileHeader.Machine);
+ return EFI_LOAD_ERROR;
+ }
+
+ num_sections = nt->FileHeader.NumberOfSections;
+ sections = (void *)&nt->OptionalHeader +
+ nt->FileHeader.SizeOfOptionalHeader;
+
+ if (efi_size < ((void *)sections + sizeof(sections[0]) * num_sections
+ - efi)) {
+ log_err("Invalid number of sections: %d\n", num_sections);
+ return EFI_LOAD_ERROR;
+ }
+
+ /* Authenticate an image */
+ if (efi_image_authenticate(efi, efi_size)) {
+ handle->auth_status = EFI_IMAGE_AUTH_PASSED;
+ } else {
+ handle->auth_status = EFI_IMAGE_AUTH_FAILED;
+ log_err("Image not authenticated\n");
+ }
+
+ /* Calculate upper virtual address boundary */
+ for (i = num_sections - 1; i >= 0; i--) {
+ IMAGE_SECTION_HEADER *sec = &sections[i];
+ virt_size = max_t(unsigned long, virt_size,
+ sec->VirtualAddress + sec->Misc.VirtualSize);
+ }
+
+ /* Read 32/64bit specific header bits */
+ if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
+ IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
+ image_base = opt->ImageBase;
+ efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
+ handle->image_type = opt->Subsystem;
+ efi_reloc = efi_alloc(virt_size,
+ loaded_image_info->image_code_type);
+ if (!efi_reloc) {
+ log_err("Out of memory\n");
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+ handle->entry = efi_reloc + opt->AddressOfEntryPoint;
+ rel_size = opt->DataDirectory[rel_idx].Size;
+ rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+ virt_size = ALIGN(virt_size, opt->SectionAlignment);
+ } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
+ image_base = opt->ImageBase;
+ efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
+ handle->image_type = opt->Subsystem;
+ efi_reloc = efi_alloc(virt_size,
+ loaded_image_info->image_code_type);
+ if (!efi_reloc) {
+ log_err("Out of memory\n");
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+ handle->entry = efi_reloc + opt->AddressOfEntryPoint;
+ rel_size = opt->DataDirectory[rel_idx].Size;
+ rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+ virt_size = ALIGN(virt_size, opt->SectionAlignment);
+ } else {
+ log_err("Invalid optional header magic %x\n",
+ nt->OptionalHeader.Magic);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
+#if CONFIG_IS_ENABLED(EFI_TCG2_PROTOCOL)
+ /* Measure an PE/COFF image */
+ if (tcg2_measure_pe_image(efi, efi_size, handle,
+ loaded_image_info))
+ log_err("PE image measurement failed\n");
+#endif
+
+ /* Copy PE headers */
+ memcpy(efi_reloc, efi,
+ sizeof(*dos)
+ + sizeof(*nt)
+ + nt->FileHeader.SizeOfOptionalHeader
+ + num_sections * sizeof(IMAGE_SECTION_HEADER));
+
+ /* Load sections into RAM */
+ for (i = num_sections - 1; i >= 0; i--) {
+ IMAGE_SECTION_HEADER *sec = &sections[i];
+ memset(efi_reloc + sec->VirtualAddress, 0,
+ sec->Misc.VirtualSize);
+ memcpy(efi_reloc + sec->VirtualAddress,
+ efi + sec->PointerToRawData,
+ min(sec->Misc.VirtualSize, sec->SizeOfRawData));
+ }
+
+ /* Run through relocations */
+ if (efi_loader_relocate(rel, rel_size, efi_reloc,
+ (unsigned long)image_base) != EFI_SUCCESS) {
+ efi_free_pages((uintptr_t) efi_reloc,
+ (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
+ /* Flush cache */
+ flush_cache((ulong)efi_reloc,
+ ALIGN(virt_size, EFI_CACHELINE_SIZE));
+ invalidate_icache_all();
+
+ /* Populate the loaded image interface bits */
+ loaded_image_info->image_base = efi_reloc;
+ loaded_image_info->image_size = virt_size;
+
+ if (handle->auth_status == EFI_IMAGE_AUTH_PASSED)
+ return EFI_SUCCESS;
+ else
+ return EFI_SECURITY_VIOLATION;
+
+err:
+ return ret;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_load_initrd.c b/roms/u-boot/lib/efi_loader/efi_load_initrd.c
new file mode 100644
index 000000000..e2a806302
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_load_initrd.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_load_initrd.h>
+#include <efi_variable.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+
+static efi_status_t EFIAPI
+efi_load_file2_initrd(struct efi_load_file_protocol *this,
+ struct efi_device_path *file_path, bool boot_policy,
+ efi_uintn_t *buffer_size, void *buffer);
+
+static const struct efi_load_file_protocol efi_lf2_protocol = {
+ .load_file = efi_load_file2_initrd,
+};
+
+/*
+ * Device path defined by Linux to identify the handle providing the
+ * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
+ */
+static const struct efi_initrd_dp dp_lf2_handle = {
+ .vendor = {
+ {
+ DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+ sizeof(dp_lf2_handle.vendor),
+ },
+ EFI_INITRD_MEDIA_GUID,
+ },
+ .end = {
+ DEVICE_PATH_TYPE_END,
+ DEVICE_PATH_SUB_TYPE_END,
+ sizeof(dp_lf2_handle.end),
+ }
+};
+
+static efi_handle_t efi_initrd_handle;
+
+/**
+ * get_initrd_fp() - Get initrd device path from a FilePathList device path
+ *
+ * @initrd_fp: the final initrd filepath
+ *
+ * Return: status code. Caller must free initrd_fp
+ */
+static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
+{
+ const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
+ struct efi_device_path *dp = NULL;
+
+ /*
+ * if bootmgr is setup with and initrd, the device path will be
+ * in the FilePathList[] of our load options in Boot####.
+ * The first device path of the multi instance device path will
+ * start with a VenMedia and the initrds will follow.
+ *
+ * If the device path is not found return EFI_INVALID_PARAMETER.
+ * We can then use this specific return value and not install the
+ * protocol, while allowing the boot to continue
+ */
+ dp = efi_get_dp_from_boot(lf2_initrd_guid);
+ if (!dp)
+ return EFI_INVALID_PARAMETER;
+
+ *initrd_fp = dp;
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_load_file2_initrd() - load initial RAM disk
+ *
+ * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
+ * in order to load an initial RAM disk requested by the Linux kernel stub.
+ *
+ * See the UEFI spec for details.
+ *
+ * @this: EFI_LOAD_FILE2_PROTOCOL instance
+ * @file_path: media device path of the file, "" in this case
+ * @boot_policy: must be false
+ * @buffer_size: size of allocated buffer
+ * @buffer: buffer to load the file
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_load_file2_initrd(struct efi_load_file_protocol *this,
+ struct efi_device_path *file_path, bool boot_policy,
+ efi_uintn_t *buffer_size, void *buffer)
+{
+ struct efi_device_path *initrd_fp = NULL;
+ efi_status_t ret = EFI_NOT_FOUND;
+ struct efi_file_handle *f = NULL;
+ efi_uintn_t bs;
+
+ EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
+ buffer_size, buffer);
+
+ if (!this || this != &efi_lf2_protocol ||
+ !buffer_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (file_path->type != dp_lf2_handle.end.type ||
+ file_path->sub_type != dp_lf2_handle.end.sub_type) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (boot_policy) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ ret = get_initrd_fp(&initrd_fp);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Open file */
+ f = efi_file_from_path(initrd_fp);
+ if (!f) {
+ log_err("Can't find initrd specified in Boot####\n");
+ ret = EFI_NOT_FOUND;
+ goto out;
+ }
+
+ /* Get file size */
+ ret = efi_file_size(f, &bs);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (!buffer || *buffer_size < bs) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ *buffer_size = bs;
+ } else {
+ ret = EFI_CALL(f->read(f, &bs, (void *)(uintptr_t)buffer));
+ *buffer_size = bs;
+ }
+
+out:
+ efi_free_pool(initrd_fp);
+ if (f)
+ EFI_CALL(f->close(f));
+ return EFI_EXIT(ret);
+}
+
+/**
+ * check_initrd() - Determine if the file defined as an initrd in Boot####
+ * load_options device path is present
+ *
+ * Return: status code
+ */
+static efi_status_t check_initrd(void)
+{
+ struct efi_device_path *initrd_fp = NULL;
+ struct efi_file_handle *f;
+ efi_status_t ret;
+
+ ret = get_initrd_fp(&initrd_fp);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /*
+ * If the file is not found, but the file path is set, return an error
+ * and trigger the bootmgr fallback
+ */
+ f = efi_file_from_path(initrd_fp);
+ if (!f) {
+ log_err("Can't find initrd specified in Boot####\n");
+ ret = EFI_NOT_FOUND;
+ goto out;
+ }
+
+ EFI_CALL(f->close(f));
+
+out:
+ efi_free_pool(initrd_fp);
+ return ret;
+}
+
+/**
+ * efi_initrd_register() - create handle for loading initial RAM disk
+ *
+ * This function creates a new handle and installs a Linux specific vendor
+ * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
+ * to identify the handle and then calls the LoadFile service of the
+ * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
+ *
+ * Return: status code
+ */
+efi_status_t efi_initrd_register(void)
+{
+ efi_status_t ret;
+
+ /*
+ * Allow the user to continue if Boot#### file path is not set for
+ * an initrd
+ */
+ ret = check_initrd();
+ if (ret == EFI_INVALID_PARAMETER)
+ return EFI_SUCCESS;
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = EFI_CALL(efi_install_multiple_protocol_interfaces
+ (&efi_initrd_handle,
+ /* initramfs */
+ &efi_guid_device_path, &dp_lf2_handle,
+ /* LOAD_FILE2 */
+ &efi_guid_load_file2_protocol,
+ (void *)&efi_lf2_protocol,
+ NULL));
+
+ return ret;
+}
+
+/**
+ * efi_initrd_deregister() - delete the handle for loading initial RAM disk
+ *
+ * This will delete the handle containing the Linux specific vendor device
+ * path and EFI_LOAD_FILE2_PROTOCOL for loading an initrd
+ *
+ * Return: status code
+ */
+void efi_initrd_deregister(void)
+{
+ efi_delete_handle(efi_initrd_handle);
+ efi_initrd_handle = NULL;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_load_options.c b/roms/u-boot/lib/efi_loader/efi_load_options.c
new file mode 100644
index 000000000..68cd85ba2
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_load_options.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI boot manager
+ *
+ * Copyright (c) 2018 AKASHI Takahiro, et.al.
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <charset.h>
+#include <log.h>
+#include <malloc.h>
+#include <efi_loader.h>
+#include <asm/unaligned.h>
+
+/**
+ * efi_set_load_options() - set the load options of a loaded image
+ *
+ * @handle: the image handle
+ * @load_options_size: size of load options
+ * @load_options: pointer to load options
+ * Return: status code
+ */
+efi_status_t efi_set_load_options(efi_handle_t handle,
+ efi_uintn_t load_options_size,
+ void *load_options)
+{
+ struct efi_loaded_image *loaded_image_info;
+ efi_status_t ret;
+
+ ret = EFI_CALL(systab.boottime->open_protocol(
+ handle,
+ &efi_guid_loaded_image,
+ (void **)&loaded_image_info,
+ efi_root, NULL,
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL));
+ if (ret != EFI_SUCCESS)
+ return EFI_INVALID_PARAMETER;
+
+ loaded_image_info->load_options = load_options;
+ loaded_image_info->load_options_size = load_options_size;
+
+ return EFI_CALL(systab.boottime->close_protocol(handle,
+ &efi_guid_loaded_image,
+ efi_root, NULL));
+}
+
+/**
+ * efi_deserialize_load_option() - parse serialized data
+ *
+ * Parse serialized data describing a load option and transform it to the
+ * efi_load_option structure.
+ *
+ * @lo: pointer to target
+ * @data: serialized data
+ * @size: size of the load option, on return size of the optional data
+ * Return: status code
+ */
+efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
+ efi_uintn_t *size)
+{
+ efi_uintn_t len;
+
+ len = sizeof(u32);
+ if (*size < len + 2 * sizeof(u16))
+ return EFI_INVALID_PARAMETER;
+ lo->attributes = get_unaligned_le32(data);
+ data += len;
+ *size -= len;
+
+ len = sizeof(u16);
+ lo->file_path_length = get_unaligned_le16(data);
+ data += len;
+ *size -= len;
+
+ lo->label = (u16 *)data;
+ len = u16_strnlen(lo->label, *size / sizeof(u16) - 1);
+ if (lo->label[len])
+ return EFI_INVALID_PARAMETER;
+ len = (len + 1) * sizeof(u16);
+ if (*size < len)
+ return EFI_INVALID_PARAMETER;
+ data += len;
+ *size -= len;
+
+ len = lo->file_path_length;
+ if (*size < len)
+ return EFI_INVALID_PARAMETER;
+ lo->file_path = (struct efi_device_path *)data;
+ if (efi_dp_check_length(lo->file_path, len) < 0)
+ return EFI_INVALID_PARAMETER;
+ data += len;
+ *size -= len;
+
+ lo->optional_data = data;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_serialize_load_option() - serialize load option
+ *
+ * Serialize efi_load_option structure into byte stream for BootXXXX.
+ *
+ * @data: buffer for serialized data
+ * @lo: load option
+ * Return: size of allocated buffer
+ */
+unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
+{
+ unsigned long label_len;
+ unsigned long size;
+ u8 *p;
+
+ label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
+
+ /* total size */
+ size = sizeof(lo->attributes);
+ size += sizeof(lo->file_path_length);
+ size += label_len;
+ size += lo->file_path_length;
+ if (lo->optional_data)
+ size += (utf8_utf16_strlen((const char *)lo->optional_data)
+ + 1) * sizeof(u16);
+ p = malloc(size);
+ if (!p)
+ return 0;
+
+ /* copy data */
+ *data = p;
+ memcpy(p, &lo->attributes, sizeof(lo->attributes));
+ p += sizeof(lo->attributes);
+
+ memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
+ p += sizeof(lo->file_path_length);
+
+ memcpy(p, lo->label, label_len);
+ p += label_len;
+
+ memcpy(p, lo->file_path, lo->file_path_length);
+ p += lo->file_path_length;
+
+ if (lo->optional_data) {
+ utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
+ p += sizeof(u16); /* size of trailing \0 */
+ }
+ return size;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_memory.c b/roms/u-boot/lib/efi_loader/efi_memory.c
new file mode 100644
index 000000000..be2f655df
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_memory.c
@@ -0,0 +1,814 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application memory management
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <init.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <watchdog.h>
+#include <asm/cache.h>
+#include <asm/global_data.h>
+#include <linux/list_sort.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Magic number identifying memory allocated from pool */
+#define EFI_ALLOC_POOL_MAGIC 0x1fe67ddf6491caa2
+
+efi_uintn_t efi_memory_map_key;
+
+struct efi_mem_list {
+ struct list_head link;
+ struct efi_mem_desc desc;
+};
+
+#define EFI_CARVE_NO_OVERLAP -1
+#define EFI_CARVE_LOOP_AGAIN -2
+#define EFI_CARVE_OVERLAPS_NONRAM -3
+
+/* This list contains all memory map items */
+LIST_HEAD(efi_mem);
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+void *efi_bounce_buffer;
+#endif
+
+/**
+ * struct efi_pool_allocation - memory block allocated from pool
+ *
+ * @num_pages: number of pages allocated
+ * @checksum: checksum
+ * @data: allocated pool memory
+ *
+ * U-Boot services each UEFI AllocatePool() request as a separate
+ * (multiple) page allocation. We have to track the number of pages
+ * to be able to free the correct amount later.
+ *
+ * The checksum calculated in function checksum() is used in FreePool() to avoid
+ * freeing memory not allocated by AllocatePool() and duplicate freeing.
+ *
+ * EFI requires 8 byte alignment for pool allocations, so we can
+ * prepend each allocation with these header fields.
+ */
+struct efi_pool_allocation {
+ u64 num_pages;
+ u64 checksum;
+ char data[] __aligned(ARCH_DMA_MINALIGN);
+};
+
+/**
+ * checksum() - calculate checksum for memory allocated from pool
+ *
+ * @alloc: allocation header
+ * Return: checksum, always non-zero
+ */
+static u64 checksum(struct efi_pool_allocation *alloc)
+{
+ u64 addr = (uintptr_t)alloc;
+ u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
+ EFI_ALLOC_POOL_MAGIC;
+ if (!ret)
+ ++ret;
+ return ret;
+}
+
+/*
+ * Sorts the memory list from highest address to lowest address
+ *
+ * When allocating memory we should always start from the highest
+ * address chunk, so sort the memory list such that the first list
+ * iterator gets the highest address and goes lower from there.
+ */
+static int efi_mem_cmp(void *priv, struct list_head *a, struct list_head *b)
+{
+ struct efi_mem_list *mema = list_entry(a, struct efi_mem_list, link);
+ struct efi_mem_list *memb = list_entry(b, struct efi_mem_list, link);
+
+ if (mema->desc.physical_start == memb->desc.physical_start)
+ return 0;
+ else if (mema->desc.physical_start < memb->desc.physical_start)
+ return 1;
+ else
+ return -1;
+}
+
+static uint64_t desc_get_end(struct efi_mem_desc *desc)
+{
+ return desc->physical_start + (desc->num_pages << EFI_PAGE_SHIFT);
+}
+
+static void efi_mem_sort(void)
+{
+ struct list_head *lhandle;
+ struct efi_mem_list *prevmem = NULL;
+ bool merge_again = true;
+
+ list_sort(NULL, &efi_mem, efi_mem_cmp);
+
+ /* Now merge entries that can be merged */
+ while (merge_again) {
+ merge_again = false;
+ list_for_each(lhandle, &efi_mem) {
+ struct efi_mem_list *lmem;
+ struct efi_mem_desc *prev = &prevmem->desc;
+ struct efi_mem_desc *cur;
+ uint64_t pages;
+
+ lmem = list_entry(lhandle, struct efi_mem_list, link);
+ if (!prevmem) {
+ prevmem = lmem;
+ continue;
+ }
+
+ cur = &lmem->desc;
+
+ if ((desc_get_end(cur) == prev->physical_start) &&
+ (prev->type == cur->type) &&
+ (prev->attribute == cur->attribute)) {
+ /* There is an existing map before, reuse it */
+ pages = cur->num_pages;
+ prev->num_pages += pages;
+ prev->physical_start -= pages << EFI_PAGE_SHIFT;
+ prev->virtual_start -= pages << EFI_PAGE_SHIFT;
+ list_del(&lmem->link);
+ free(lmem);
+
+ merge_again = true;
+ break;
+ }
+
+ prevmem = lmem;
+ }
+ }
+}
+
+/** efi_mem_carve_out - unmap memory region
+ *
+ * @map: memory map
+ * @carve_desc: memory region to unmap
+ * @overlap_only_ram: the carved out region may only overlap RAM
+ * Return Value: the number of overlapping pages which have been
+ * removed from the map,
+ * EFI_CARVE_NO_OVERLAP, if the regions don't overlap,
+ * EFI_CARVE_OVERLAPS_NONRAM, if the carve and map overlap,
+ * and the map contains anything but free ram
+ * (only when overlap_only_ram is true),
+ * EFI_CARVE_LOOP_AGAIN, if the mapping list should be
+ * traversed again, as it has been altered.
+ *
+ * Unmaps all memory occupied by the carve_desc region from the list entry
+ * pointed to by map.
+ *
+ * In case of EFI_CARVE_OVERLAPS_NONRAM it is the callers responsibility
+ * to re-add the already carved out pages to the mapping.
+ */
+static s64 efi_mem_carve_out(struct efi_mem_list *map,
+ struct efi_mem_desc *carve_desc,
+ bool overlap_only_ram)
+{
+ struct efi_mem_list *newmap;
+ struct efi_mem_desc *map_desc = &map->desc;
+ uint64_t map_start = map_desc->physical_start;
+ uint64_t map_end = map_start + (map_desc->num_pages << EFI_PAGE_SHIFT);
+ uint64_t carve_start = carve_desc->physical_start;
+ uint64_t carve_end = carve_start +
+ (carve_desc->num_pages << EFI_PAGE_SHIFT);
+
+ /* check whether we're overlapping */
+ if ((carve_end <= map_start) || (carve_start >= map_end))
+ return EFI_CARVE_NO_OVERLAP;
+
+ /* We're overlapping with non-RAM, warn the caller if desired */
+ if (overlap_only_ram && (map_desc->type != EFI_CONVENTIONAL_MEMORY))
+ return EFI_CARVE_OVERLAPS_NONRAM;
+
+ /* Sanitize carve_start and carve_end to lie within our bounds */
+ carve_start = max(carve_start, map_start);
+ carve_end = min(carve_end, map_end);
+
+ /* Carving at the beginning of our map? Just move it! */
+ if (carve_start == map_start) {
+ if (map_end == carve_end) {
+ /* Full overlap, just remove map */
+ list_del(&map->link);
+ free(map);
+ } else {
+ map->desc.physical_start = carve_end;
+ map->desc.virtual_start = carve_end;
+ map->desc.num_pages = (map_end - carve_end)
+ >> EFI_PAGE_SHIFT;
+ }
+
+ return (carve_end - carve_start) >> EFI_PAGE_SHIFT;
+ }
+
+ /*
+ * Overlapping maps, just split the list map at carve_start,
+ * it will get moved or removed in the next iteration.
+ *
+ * [ map_desc |__carve_start__| newmap ]
+ */
+
+ /* Create a new map from [ carve_start ... map_end ] */
+ newmap = calloc(1, sizeof(*newmap));
+ newmap->desc = map->desc;
+ newmap->desc.physical_start = carve_start;
+ newmap->desc.virtual_start = carve_start;
+ newmap->desc.num_pages = (map_end - carve_start) >> EFI_PAGE_SHIFT;
+ /* Insert before current entry (descending address order) */
+ list_add_tail(&newmap->link, &map->link);
+
+ /* Shrink the map to [ map_start ... carve_start ] */
+ map_desc->num_pages = (carve_start - map_start) >> EFI_PAGE_SHIFT;
+
+ return EFI_CARVE_LOOP_AGAIN;
+}
+
+/**
+ * efi_add_memory_map_pg() - add pages to the memory map
+ *
+ * @start: start address, must be a multiple of EFI_PAGE_SIZE
+ * @pages: number of pages to add
+ * @memory_type: type of memory added
+ * @overlap_only_ram: region may only overlap RAM
+ * Return: status code
+ */
+static efi_status_t efi_add_memory_map_pg(u64 start, u64 pages,
+ int memory_type,
+ bool overlap_only_ram)
+{
+ struct list_head *lhandle;
+ struct efi_mem_list *newlist;
+ bool carve_again;
+ uint64_t carved_pages = 0;
+ struct efi_event *evt;
+
+ EFI_PRINT("%s: 0x%llx 0x%llx %d %s\n", __func__,
+ start, pages, memory_type, overlap_only_ram ? "yes" : "no");
+
+ if (memory_type >= EFI_MAX_MEMORY_TYPE)
+ return EFI_INVALID_PARAMETER;
+
+ if (!pages)
+ return EFI_SUCCESS;
+
+ ++efi_memory_map_key;
+ newlist = calloc(1, sizeof(*newlist));
+ newlist->desc.type = memory_type;
+ newlist->desc.physical_start = start;
+ newlist->desc.virtual_start = start;
+ newlist->desc.num_pages = pages;
+
+ switch (memory_type) {
+ case EFI_RUNTIME_SERVICES_CODE:
+ case EFI_RUNTIME_SERVICES_DATA:
+ newlist->desc.attribute = EFI_MEMORY_WB | EFI_MEMORY_RUNTIME;
+ break;
+ case EFI_MMAP_IO:
+ newlist->desc.attribute = EFI_MEMORY_RUNTIME;
+ break;
+ default:
+ newlist->desc.attribute = EFI_MEMORY_WB;
+ break;
+ }
+
+ /* Add our new map */
+ do {
+ carve_again = false;
+ list_for_each(lhandle, &efi_mem) {
+ struct efi_mem_list *lmem;
+ s64 r;
+
+ lmem = list_entry(lhandle, struct efi_mem_list, link);
+ r = efi_mem_carve_out(lmem, &newlist->desc,
+ overlap_only_ram);
+ switch (r) {
+ case EFI_CARVE_OVERLAPS_NONRAM:
+ /*
+ * The user requested to only have RAM overlaps,
+ * but we hit a non-RAM region. Error out.
+ */
+ return EFI_NO_MAPPING;
+ case EFI_CARVE_NO_OVERLAP:
+ /* Just ignore this list entry */
+ break;
+ case EFI_CARVE_LOOP_AGAIN:
+ /*
+ * We split an entry, but need to loop through
+ * the list again to actually carve it.
+ */
+ carve_again = true;
+ break;
+ default:
+ /* We carved a number of pages */
+ carved_pages += r;
+ carve_again = true;
+ break;
+ }
+
+ if (carve_again) {
+ /* The list changed, we need to start over */
+ break;
+ }
+ }
+ } while (carve_again);
+
+ if (overlap_only_ram && (carved_pages != pages)) {
+ /*
+ * The payload wanted to have RAM overlaps, but we overlapped
+ * with an unallocated region. Error out.
+ */
+ return EFI_NO_MAPPING;
+ }
+
+ /* Add our new map */
+ list_add_tail(&newlist->link, &efi_mem);
+
+ /* And make sure memory is listed in descending order */
+ efi_mem_sort();
+
+ /* Notify that the memory map was changed */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !guidcmp(evt->group,
+ &efi_guid_event_group_memory_map_change)) {
+ efi_signal_event(evt);
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_add_memory_map() - add memory area to the memory map
+ *
+ * @start: start address of the memory area
+ * @size: length in bytes of the memory area
+ * @memory_type: type of memory added
+ *
+ * Return: status code
+ *
+ * This function automatically aligns the start and size of the memory area
+ * to EFI_PAGE_SIZE.
+ */
+efi_status_t efi_add_memory_map(u64 start, u64 size, int memory_type)
+{
+ u64 pages;
+
+ pages = efi_size_in_pages(size + (start & EFI_PAGE_MASK));
+ start &= ~EFI_PAGE_MASK;
+
+ return efi_add_memory_map_pg(start, pages, memory_type, false);
+}
+
+/**
+ * efi_check_allocated() - validate address to be freed
+ *
+ * Check that the address is within allocated memory:
+ *
+ * * The address must be in a range of the memory map.
+ * * The address may not point to EFI_CONVENTIONAL_MEMORY.
+ *
+ * Page alignment is not checked as this is not a requirement of
+ * efi_free_pool().
+ *
+ * @addr: address of page to be freed
+ * @must_be_allocated: return success if the page is allocated
+ * Return: status code
+ */
+static efi_status_t efi_check_allocated(u64 addr, bool must_be_allocated)
+{
+ struct efi_mem_list *item;
+
+ list_for_each_entry(item, &efi_mem, link) {
+ u64 start = item->desc.physical_start;
+ u64 end = start + (item->desc.num_pages << EFI_PAGE_SHIFT);
+
+ if (addr >= start && addr < end) {
+ if (must_be_allocated ^
+ (item->desc.type == EFI_CONVENTIONAL_MEMORY))
+ return EFI_SUCCESS;
+ else
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr)
+{
+ struct list_head *lhandle;
+
+ /*
+ * Prealign input max address, so we simplify our matching
+ * logic below and can just reuse it as return pointer.
+ */
+ max_addr &= ~EFI_PAGE_MASK;
+
+ list_for_each(lhandle, &efi_mem) {
+ struct efi_mem_list *lmem = list_entry(lhandle,
+ struct efi_mem_list, link);
+ struct efi_mem_desc *desc = &lmem->desc;
+ uint64_t desc_len = desc->num_pages << EFI_PAGE_SHIFT;
+ uint64_t desc_end = desc->physical_start + desc_len;
+ uint64_t curmax = min(max_addr, desc_end);
+ uint64_t ret = curmax - len;
+
+ /* We only take memory from free RAM */
+ if (desc->type != EFI_CONVENTIONAL_MEMORY)
+ continue;
+
+ /* Out of bounds for max_addr */
+ if ((ret + len) > max_addr)
+ continue;
+
+ /* Out of bounds for upper map limit */
+ if ((ret + len) > desc_end)
+ continue;
+
+ /* Out of bounds for lower map limit */
+ if (ret < desc->physical_start)
+ continue;
+
+ /* Return the highest address in this map within bounds */
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Allocate memory pages.
+ *
+ * @type type of allocation to be performed
+ * @memory_type usage type of the allocated memory
+ * @pages number of pages to be allocated
+ * @memory allocated memory
+ * @return status code
+ */
+efi_status_t efi_allocate_pages(int type, int memory_type,
+ efi_uintn_t pages, uint64_t *memory)
+{
+ u64 len = pages << EFI_PAGE_SHIFT;
+ efi_status_t ret;
+ uint64_t addr;
+
+ /* Check import parameters */
+ if (memory_type >= EFI_PERSISTENT_MEMORY_TYPE &&
+ memory_type <= 0x6FFFFFFF)
+ return EFI_INVALID_PARAMETER;
+ if (!memory)
+ return EFI_INVALID_PARAMETER;
+
+ switch (type) {
+ case EFI_ALLOCATE_ANY_PAGES:
+ /* Any page */
+ addr = efi_find_free_memory(len, -1ULL);
+ if (!addr)
+ return EFI_OUT_OF_RESOURCES;
+ break;
+ case EFI_ALLOCATE_MAX_ADDRESS:
+ /* Max address */
+ addr = efi_find_free_memory(len, *memory);
+ if (!addr)
+ return EFI_OUT_OF_RESOURCES;
+ break;
+ case EFI_ALLOCATE_ADDRESS:
+ /* Exact address, reserve it. The addr is already in *memory. */
+ ret = efi_check_allocated(*memory, false);
+ if (ret != EFI_SUCCESS)
+ return EFI_NOT_FOUND;
+ addr = *memory;
+ break;
+ default:
+ /* UEFI doesn't specify other allocation types */
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /* Reserve that map in our memory maps */
+ ret = efi_add_memory_map_pg(addr, pages, memory_type, true);
+ if (ret != EFI_SUCCESS)
+ /* Map would overlap, bail out */
+ return EFI_OUT_OF_RESOURCES;
+
+ *memory = addr;
+
+ return EFI_SUCCESS;
+}
+
+void *efi_alloc(uint64_t len, int memory_type)
+{
+ uint64_t ret = 0;
+ uint64_t pages = efi_size_in_pages(len);
+ efi_status_t r;
+
+ r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, memory_type, pages,
+ &ret);
+ if (r == EFI_SUCCESS)
+ return (void*)(uintptr_t)ret;
+
+ return NULL;
+}
+
+/**
+ * efi_free_pages() - free memory pages
+ *
+ * @memory: start of the memory area to be freed
+ * @pages: number of pages to be freed
+ * Return: status code
+ */
+efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
+{
+ efi_status_t ret;
+
+ ret = efi_check_allocated(memory, true);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* Sanity check */
+ if (!memory || (memory & EFI_PAGE_MASK) || !pages) {
+ printf("%s: illegal free 0x%llx, 0x%zx\n", __func__,
+ memory, pages);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ret = efi_add_memory_map_pg(memory, pages, EFI_CONVENTIONAL_MEMORY,
+ false);
+ if (ret != EFI_SUCCESS)
+ return EFI_NOT_FOUND;
+
+ return ret;
+}
+
+/**
+ * efi_allocate_pool - allocate memory from pool
+ *
+ * @pool_type: type of the pool from which memory is to be allocated
+ * @size: number of bytes to be allocated
+ * @buffer: allocated memory
+ * Return: status code
+ */
+efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer)
+{
+ efi_status_t r;
+ u64 addr;
+ struct efi_pool_allocation *alloc;
+ u64 num_pages = efi_size_in_pages(size +
+ sizeof(struct efi_pool_allocation));
+
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+
+ if (size == 0) {
+ *buffer = NULL;
+ return EFI_SUCCESS;
+ }
+
+ r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
+ &addr);
+ if (r == EFI_SUCCESS) {
+ alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
+ alloc->num_pages = num_pages;
+ alloc->checksum = checksum(alloc);
+ *buffer = alloc->data;
+ }
+
+ return r;
+}
+
+/**
+ * efi_free_pool() - free memory from pool
+ *
+ * @buffer: start of memory to be freed
+ * Return: status code
+ */
+efi_status_t efi_free_pool(void *buffer)
+{
+ efi_status_t ret;
+ struct efi_pool_allocation *alloc;
+
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+
+ ret = efi_check_allocated((uintptr_t)buffer, true);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ alloc = container_of(buffer, struct efi_pool_allocation, data);
+
+ /* Check that this memory was allocated by efi_allocate_pool() */
+ if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
+ alloc->checksum != checksum(alloc)) {
+ printf("%s: illegal free 0x%p\n", __func__, buffer);
+ return EFI_INVALID_PARAMETER;
+ }
+ /* Avoid double free */
+ alloc->checksum = 0;
+
+ ret = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
+
+ return ret;
+}
+
+/*
+ * Get map describing memory usage.
+ *
+ * @memory_map_size on entry the size, in bytes, of the memory map buffer,
+ * on exit the size of the copied memory map
+ * @memory_map buffer to which the memory map is written
+ * @map_key key for the memory map
+ * @descriptor_size size of an individual memory descriptor
+ * @descriptor_version version number of the memory descriptor structure
+ * @return status code
+ */
+efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
+ struct efi_mem_desc *memory_map,
+ efi_uintn_t *map_key,
+ efi_uintn_t *descriptor_size,
+ uint32_t *descriptor_version)
+{
+ efi_uintn_t map_size = 0;
+ int map_entries = 0;
+ struct list_head *lhandle;
+ efi_uintn_t provided_map_size;
+
+ if (!memory_map_size)
+ return EFI_INVALID_PARAMETER;
+
+ provided_map_size = *memory_map_size;
+
+ list_for_each(lhandle, &efi_mem)
+ map_entries++;
+
+ map_size = map_entries * sizeof(struct efi_mem_desc);
+
+ *memory_map_size = map_size;
+
+ if (descriptor_size)
+ *descriptor_size = sizeof(struct efi_mem_desc);
+
+ if (descriptor_version)
+ *descriptor_version = EFI_MEMORY_DESCRIPTOR_VERSION;
+
+ if (provided_map_size < map_size)
+ return EFI_BUFFER_TOO_SMALL;
+
+ if (!memory_map)
+ return EFI_INVALID_PARAMETER;
+
+ /* Copy list into array */
+ /* Return the list in ascending order */
+ memory_map = &memory_map[map_entries - 1];
+ list_for_each(lhandle, &efi_mem) {
+ struct efi_mem_list *lmem;
+
+ lmem = list_entry(lhandle, struct efi_mem_list, link);
+ *memory_map = lmem->desc;
+ memory_map--;
+ }
+
+ if (map_key)
+ *map_key = efi_memory_map_key;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_add_conventional_memory_map() - add a RAM memory area to the map
+ *
+ * @ram_start: start address of a RAM memory area
+ * @ram_end: end address of a RAM memory area
+ * @ram_top: max address to be used as conventional memory
+ * Return: status code
+ */
+efi_status_t efi_add_conventional_memory_map(u64 ram_start, u64 ram_end,
+ u64 ram_top)
+{
+ u64 pages;
+
+ /* Remove partial pages */
+ ram_end &= ~EFI_PAGE_MASK;
+ ram_start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
+
+ if (ram_end <= ram_start) {
+ /* Invalid mapping */
+ return EFI_INVALID_PARAMETER;
+ }
+
+ pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT;
+
+ efi_add_memory_map_pg(ram_start, pages,
+ EFI_CONVENTIONAL_MEMORY, false);
+
+ /*
+ * Boards may indicate to the U-Boot memory core that they
+ * can not support memory above ram_top. Let's honor this
+ * in the efi_loader subsystem too by declaring any memory
+ * above ram_top as "already occupied by firmware".
+ */
+ if (ram_top < ram_start) {
+ /* ram_top is before this region, reserve all */
+ efi_add_memory_map_pg(ram_start, pages,
+ EFI_BOOT_SERVICES_DATA, true);
+ } else if ((ram_top >= ram_start) && (ram_top < ram_end)) {
+ /* ram_top is inside this region, reserve parts */
+ pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT;
+
+ efi_add_memory_map_pg(ram_top, pages,
+ EFI_BOOT_SERVICES_DATA, true);
+ }
+
+ return EFI_SUCCESS;
+}
+
+__weak void efi_add_known_memory(void)
+{
+ u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK;
+ int i;
+
+ /*
+ * ram_top is just outside mapped memory. So use an offset of one for
+ * mapping the sandbox address.
+ */
+ ram_top = (uintptr_t)map_sysmem(ram_top - 1, 0) + 1;
+
+ /* Fix for 32bit targets with ram_top at 4G */
+ if (!ram_top)
+ ram_top = 0x100000000ULL;
+
+ /* Add RAM */
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+ u64 ram_end, ram_start;
+
+ ram_start = (uintptr_t)map_sysmem(gd->bd->bi_dram[i].start, 0);
+ ram_end = ram_start + gd->bd->bi_dram[i].size;
+
+ efi_add_conventional_memory_map(ram_start, ram_end, ram_top);
+ }
+}
+
+/* Add memory regions for U-Boot's memory and for the runtime services code */
+static void add_u_boot_and_runtime(void)
+{
+ unsigned long runtime_start, runtime_end, runtime_pages;
+ unsigned long runtime_mask = EFI_PAGE_MASK;
+ unsigned long uboot_start, uboot_pages;
+ unsigned long uboot_stack_size = CONFIG_STACK_SIZE;
+
+ /* Add U-Boot */
+ uboot_start = ((uintptr_t)map_sysmem(gd->start_addr_sp, 0) -
+ uboot_stack_size) & ~EFI_PAGE_MASK;
+ uboot_pages = ((uintptr_t)map_sysmem(gd->ram_top - 1, 0) -
+ uboot_start + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
+ efi_add_memory_map_pg(uboot_start, uboot_pages, EFI_LOADER_DATA,
+ false);
+
+#if defined(__aarch64__)
+ /*
+ * Runtime Services must be 64KiB aligned according to the
+ * "AArch64 Platforms" section in the UEFI spec (2.7+).
+ */
+
+ runtime_mask = SZ_64K - 1;
+#endif
+
+ /*
+ * Add Runtime Services. We mark surrounding boottime code as runtime as
+ * well to fulfill the runtime alignment constraints but avoid padding.
+ */
+ runtime_start = (ulong)&__efi_runtime_start & ~runtime_mask;
+ runtime_end = (ulong)&__efi_runtime_stop;
+ runtime_end = (runtime_end + runtime_mask) & ~runtime_mask;
+ runtime_pages = (runtime_end - runtime_start) >> EFI_PAGE_SHIFT;
+ efi_add_memory_map_pg(runtime_start, runtime_pages,
+ EFI_RUNTIME_SERVICES_CODE, false);
+}
+
+int efi_memory_init(void)
+{
+ efi_add_known_memory();
+
+ add_u_boot_and_runtime();
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+ /* Request a 32bit 64MB bounce buffer region */
+ uint64_t efi_bounce_buffer_addr = 0xffffffff;
+
+ if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA,
+ (64 * 1024 * 1024) >> EFI_PAGE_SHIFT,
+ &efi_bounce_buffer_addr) != EFI_SUCCESS)
+ return -1;
+
+ efi_bounce_buffer = (void*)(uintptr_t)efi_bounce_buffer_addr;
+#endif
+
+ return 0;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_net.c b/roms/u-boot/lib/efi_loader/efi_net.c
new file mode 100644
index 000000000..69276b275
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_net.c
@@ -0,0 +1,993 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Simple network protocol
+ * PXE base code protocol
+ *
+ * Copyright (c) 2016 Alexander Graf
+ *
+ * The simple network protocol has the following statuses and services
+ * to move between them:
+ *
+ * Start(): EfiSimpleNetworkStopped -> EfiSimpleNetworkStarted
+ * Initialize(): EfiSimpleNetworkStarted -> EfiSimpleNetworkInitialized
+ * Shutdown(): EfiSimpleNetworkInitialized -> EfiSimpleNetworkStarted
+ * Stop(): EfiSimpleNetworkStarted -> EfiSimpleNetworkStopped
+ * Reset(): EfiSimpleNetworkInitialized -> EfiSimpleNetworkInitialized
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <malloc.h>
+#include <net.h>
+
+static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
+static const efi_guid_t efi_pxe_base_code_protocol_guid =
+ EFI_PXE_BASE_CODE_PROTOCOL_GUID;
+static struct efi_pxe_packet *dhcp_ack;
+static void *new_tx_packet;
+static void *transmit_buffer;
+static uchar **receive_buffer;
+static size_t *receive_lengths;
+static int rx_packet_idx;
+static int rx_packet_num;
+
+/*
+ * The notification function of this event is called in every timer cycle
+ * to check if a new network packet has been received.
+ */
+static struct efi_event *network_timer_event;
+/*
+ * This event is signaled when a packet has been received.
+ */
+static struct efi_event *wait_for_packet;
+
+/**
+ * struct efi_net_obj - EFI object representing a network interface
+ *
+ * @header: EFI object header
+ * @net: simple network protocol interface
+ * @net_mode: status of the network interface
+ * @pxe: PXE base code protocol interface
+ * @pxe_mode: status of the PXE base code protocol
+ */
+struct efi_net_obj {
+ struct efi_object header;
+ struct efi_simple_network net;
+ struct efi_simple_network_mode net_mode;
+ struct efi_pxe_base_code_protocol pxe;
+ struct efi_pxe_mode pxe_mode;
+};
+
+/*
+ * efi_net_start() - start the network interface
+ *
+ * This function implements the Start service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p", this);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (this->mode->state != EFI_NETWORK_STOPPED) {
+ ret = EFI_ALREADY_STARTED;
+ } else {
+ this->int_status = 0;
+ wait_for_packet->is_signaled = false;
+ this->mode->state = EFI_NETWORK_STARTED;
+ }
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * efi_net_stop() - stop the network interface
+ *
+ * This function implements the Stop service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p", this);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (this->mode->state == EFI_NETWORK_STOPPED) {
+ ret = EFI_NOT_STARTED;
+ } else {
+ /* Disable hardware and put it into the reset state */
+ eth_halt();
+ /* Clear cache of packets */
+ rx_packet_num = 0;
+ this->mode->state = EFI_NETWORK_STOPPED;
+ }
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * efi_net_initialize() - initialize the network interface
+ *
+ * This function implements the Initialize service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @extra_rx: extra receive buffer to be allocated
+ * @extra_tx: extra transmit buffer to be allocated
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
+ ulong extra_rx, ulong extra_tx)
+{
+ int ret;
+ efi_status_t r = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
+
+ /* Check parameters */
+ if (!this) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_INITIALIZED:
+ case EFI_NETWORK_STARTED:
+ break;
+ default:
+ r = EFI_NOT_STARTED;
+ goto out;
+ }
+
+ /* Setup packet buffers */
+ net_init();
+ /* Disable hardware and put it into the reset state */
+ eth_halt();
+ /* Clear cache of packets */
+ rx_packet_num = 0;
+ /* Set current device according to environment variables */
+ eth_set_current();
+ /* Get hardware ready for send and receive operations */
+ ret = eth_init();
+ if (ret < 0) {
+ eth_halt();
+ this->mode->state = EFI_NETWORK_STOPPED;
+ r = EFI_DEVICE_ERROR;
+ goto out;
+ } else {
+ this->int_status = 0;
+ wait_for_packet->is_signaled = false;
+ this->mode->state = EFI_NETWORK_INITIALIZED;
+ }
+out:
+ return EFI_EXIT(r);
+}
+
+/*
+ * efi_net_reset() - reinitialize the network interface
+ *
+ * This function implements the Reset service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @extended_verification: execute exhaustive verification
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
+ int extended_verification)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %x", this, extended_verification);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_INITIALIZED:
+ break;
+ case EFI_NETWORK_STOPPED:
+ ret = EFI_NOT_STARTED;
+ goto out;
+ default:
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ this->mode->state = EFI_NETWORK_STARTED;
+ ret = EFI_CALL(efi_net_initialize(this, 0, 0));
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * efi_net_shutdown() - shut down the network interface
+ *
+ * This function implements the Shutdown service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p", this);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_INITIALIZED:
+ break;
+ case EFI_NETWORK_STOPPED:
+ ret = EFI_NOT_STARTED;
+ goto out;
+ default:
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ eth_halt();
+ this->int_status = 0;
+ wait_for_packet->is_signaled = false;
+ this->mode->state = EFI_NETWORK_STARTED;
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * efi_net_receive_filters() - mange multicast receive filters
+ *
+ * This function implements the ReceiveFilters service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @enable: bit mask of receive filters to enable
+ * @disable: bit mask of receive filters to disable
+ * @reset_mcast_filter: true resets contents of the filters
+ * @mcast_filter_count: number of hardware MAC addresses in the new filters list
+ * @mcast_filter: list of new filters
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_receive_filters
+ (struct efi_simple_network *this, u32 enable, u32 disable,
+ int reset_mcast_filter, ulong mcast_filter_count,
+ struct efi_mac_address *mcast_filter)
+{
+ EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable,
+ reset_mcast_filter, mcast_filter_count, mcast_filter);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/*
+ * efi_net_station_address() - set the hardware MAC address
+ *
+ * This function implements the StationAddress service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @reset: if true reset the address to default
+ * @new_mac: new MAC address
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_station_address
+ (struct efi_simple_network *this, int reset,
+ struct efi_mac_address *new_mac)
+{
+ EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/*
+ * efi_net_statistics() - reset or collect statistics of the network interface
+ *
+ * This function implements the Statistics service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @reset: if true, the statistics are reset
+ * @stat_size: size of the statistics table
+ * @stat_table: table to receive the statistics
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
+ int reset, ulong *stat_size,
+ void *stat_table)
+{
+ EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/*
+ * efi_net_mcastiptomac() - translate multicast IP address to MAC address
+ *
+ * This function implements the MCastIPtoMAC service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @ipv6: true if the IP address is an IPv6 address
+ * @ip: IP address
+ * @mac: MAC address
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
+ int ipv6,
+ struct efi_ip_address *ip,
+ struct efi_mac_address *mac)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac);
+
+ if (!this || !ip || !mac) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (ipv6) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ /* Multi-cast addresses are in the range 224.0.0.0 - 239.255.255.255 */
+ if ((ip->ip_addr[0] & 0xf0) != 0xe0) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ };
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_INITIALIZED:
+ case EFI_NETWORK_STARTED:
+ break;
+ default:
+ ret = EFI_NOT_STARTED;
+ goto out;
+ }
+
+ memset(mac, 0, sizeof(struct efi_mac_address));
+
+ /*
+ * Copy lower 23 bits of IPv4 multi-cast address
+ * RFC 1112, RFC 7042 2.1.1.
+ */
+ mac->mac_addr[0] = 0x01;
+ mac->mac_addr[1] = 0x00;
+ mac->mac_addr[2] = 0x5E;
+ mac->mac_addr[3] = ip->ip_addr[1] & 0x7F;
+ mac->mac_addr[4] = ip->ip_addr[2];
+ mac->mac_addr[5] = ip->ip_addr[3];
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_net_nvdata() - read or write NVRAM
+ *
+ * This function implements the GetStatus service of the Simple Network
+ * Protocol. See the UEFI spec for details.
+ *
+ * @this: the instance of the Simple Network Protocol
+ * @read_write: true for read, false for write
+ * @offset: offset in NVRAM
+ * @buffer_size: size of buffer
+ * @buffer: buffer
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this,
+ int read_write, ulong offset,
+ ulong buffer_size, char *buffer)
+{
+ EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size,
+ buffer);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_net_get_status() - get interrupt status
+ *
+ * This function implements the GetStatus service of the Simple Network
+ * Protocol. See the UEFI spec for details.
+ *
+ * @this: the instance of the Simple Network Protocol
+ * @int_status: interface status
+ * @txbuf: transmission buffer
+ */
+static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
+ u32 *int_status, void **txbuf)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
+
+ efi_timer_check();
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_STOPPED:
+ ret = EFI_NOT_STARTED;
+ goto out;
+ case EFI_NETWORK_STARTED:
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ default:
+ break;
+ }
+
+ if (int_status) {
+ *int_status = this->int_status;
+ this->int_status = 0;
+ }
+ if (txbuf)
+ *txbuf = new_tx_packet;
+
+ new_tx_packet = NULL;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_net_transmit() - transmit a packet
+ *
+ * This function implements the Transmit service of the Simple Network Protocol.
+ * See the UEFI spec for details.
+ *
+ * @this: the instance of the Simple Network Protocol
+ * @header_size: size of the media header
+ * @buffer_size: size of the buffer to receive the packet
+ * @buffer: buffer to receive the packet
+ * @src_addr: source hardware MAC address
+ * @dest_addr: destination hardware MAC address
+ * @protocol: type of header to build
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_transmit
+ (struct efi_simple_network *this, size_t header_size,
+ size_t buffer_size, void *buffer,
+ struct efi_mac_address *src_addr,
+ struct efi_mac_address *dest_addr, u16 *protocol)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
+ (unsigned long)header_size, (unsigned long)buffer_size,
+ buffer, src_addr, dest_addr, protocol);
+
+ efi_timer_check();
+
+ /* Check parameters */
+ if (!this || !buffer) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We do not support jumbo packets */
+ if (buffer_size > PKTSIZE_ALIGN) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* At least the IP header has to fit into the buffer */
+ if (buffer_size < this->mode->media_header_size) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
+
+ /*
+ * TODO:
+ * Support VLANs. Use net_set_ether() for copying the header. Use a
+ * U_BOOT_ENV_CALLBACK to update the media header size.
+ */
+ if (header_size) {
+ struct ethernet_hdr *header = buffer;
+
+ if (!dest_addr || !protocol ||
+ header_size != this->mode->media_header_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (!src_addr)
+ src_addr = &this->mode->current_address;
+
+ memcpy(header->et_dest, dest_addr, ARP_HLEN);
+ memcpy(header->et_src, src_addr, ARP_HLEN);
+ header->et_protlen = htons(*protocol);
+ }
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_STOPPED:
+ ret = EFI_NOT_STARTED;
+ goto out;
+ case EFI_NETWORK_STARTED:
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ default:
+ break;
+ }
+
+ /* Ethernet packets always fit, just bounce */
+ memcpy(transmit_buffer, buffer, buffer_size);
+ net_send_packet(transmit_buffer, buffer_size);
+
+ new_tx_packet = buffer;
+ this->int_status |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_net_receive() - receive a packet from a network interface
+ *
+ * This function implements the Receive service of the Simple Network Protocol.
+ * See the UEFI spec for details.
+ *
+ * @this: the instance of the Simple Network Protocol
+ * @header_size: size of the media header
+ * @buffer_size: size of the buffer to receive the packet
+ * @buffer: buffer to receive the packet
+ * @src_addr: source MAC address
+ * @dest_addr: destination MAC address
+ * @protocol: protocol
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_receive
+ (struct efi_simple_network *this, size_t *header_size,
+ size_t *buffer_size, void *buffer,
+ struct efi_mac_address *src_addr,
+ struct efi_mac_address *dest_addr, u16 *protocol)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct ethernet_hdr *eth_hdr;
+ size_t hdr_size = sizeof(struct ethernet_hdr);
+ u16 protlen;
+
+ EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
+ buffer_size, buffer, src_addr, dest_addr, protocol);
+
+ /* Execute events */
+ efi_timer_check();
+
+ /* Check parameters */
+ if (!this || !buffer || !buffer_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_STOPPED:
+ ret = EFI_NOT_STARTED;
+ goto out;
+ case EFI_NETWORK_STARTED:
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ default:
+ break;
+ }
+
+ if (!rx_packet_num) {
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ /* Fill export parameters */
+ eth_hdr = (struct ethernet_hdr *)receive_buffer[rx_packet_idx];
+ protlen = ntohs(eth_hdr->et_protlen);
+ if (protlen == 0x8100) {
+ hdr_size += 4;
+ protlen = ntohs(*(u16 *)&receive_buffer[rx_packet_idx][hdr_size - 2]);
+ }
+ if (header_size)
+ *header_size = hdr_size;
+ if (dest_addr)
+ memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN);
+ if (src_addr)
+ memcpy(src_addr, eth_hdr->et_src, ARP_HLEN);
+ if (protocol)
+ *protocol = protlen;
+ if (*buffer_size < receive_lengths[rx_packet_idx]) {
+ /* Packet doesn't fit, try again with bigger buffer */
+ *buffer_size = receive_lengths[rx_packet_idx];
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
+ /* Copy packet */
+ memcpy(buffer, receive_buffer[rx_packet_idx],
+ receive_lengths[rx_packet_idx]);
+ *buffer_size = receive_lengths[rx_packet_idx];
+ rx_packet_idx = (rx_packet_idx + 1) % ETH_PACKETS_BATCH_RECV;
+ rx_packet_num--;
+ if (rx_packet_num)
+ wait_for_packet->is_signaled = true;
+ else
+ this->int_status &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_net_set_dhcp_ack() - take note of a selected DHCP IP address
+ *
+ * This function is called by dhcp_handler().
+ *
+ * @pkt: packet received by dhcp_handler()
+ * @len: length of the packet received
+ */
+void efi_net_set_dhcp_ack(void *pkt, int len)
+{
+ int maxsize = sizeof(*dhcp_ack);
+
+ if (!dhcp_ack)
+ dhcp_ack = malloc(maxsize);
+
+ memcpy(dhcp_ack, pkt, min(len, maxsize));
+}
+
+/**
+ * efi_net_push() - callback for received network packet
+ *
+ * This function is called when a network packet is received by eth_rx().
+ *
+ * @pkt: network packet
+ * @len: length
+ */
+static void efi_net_push(void *pkt, int len)
+{
+ int rx_packet_next;
+
+ /* Check that we at least received an Ethernet header */
+ if (len < sizeof(struct ethernet_hdr))
+ return;
+
+ /* Check that the buffer won't overflow */
+ if (len > PKTSIZE_ALIGN)
+ return;
+
+ /* Can't store more than pre-alloced buffer */
+ if (rx_packet_num >= ETH_PACKETS_BATCH_RECV)
+ return;
+
+ rx_packet_next = (rx_packet_idx + rx_packet_num) %
+ ETH_PACKETS_BATCH_RECV;
+ memcpy(receive_buffer[rx_packet_next], pkt, len);
+ receive_lengths[rx_packet_next] = len;
+
+ rx_packet_num++;
+}
+
+/**
+ * efi_network_timer_notify() - check if a new network packet has been received
+ *
+ * This notification function is called in every timer cycle.
+ *
+ * @event: the event for which this notification function is registered
+ * @context: event context - not used in this function
+ */
+static void EFIAPI efi_network_timer_notify(struct efi_event *event,
+ void *context)
+{
+ struct efi_simple_network *this = (struct efi_simple_network *)context;
+
+ EFI_ENTRY("%p, %p", event, context);
+
+ /*
+ * Some network drivers do not support calling eth_rx() before
+ * initialization.
+ */
+ if (!this || this->mode->state != EFI_NETWORK_INITIALIZED)
+ goto out;
+
+ if (!rx_packet_num) {
+ push_packet = efi_net_push;
+ eth_rx();
+ push_packet = NULL;
+ if (rx_packet_num) {
+ this->int_status |=
+ EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+ wait_for_packet->is_signaled = true;
+ }
+ }
+out:
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_start(
+ struct efi_pxe_base_code_protocol *this,
+ u8 use_ipv6)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_stop(
+ struct efi_pxe_base_code_protocol *this)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_dhcp(
+ struct efi_pxe_base_code_protocol *this,
+ u8 sort_offers)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_discover(
+ struct efi_pxe_base_code_protocol *this,
+ u16 type, u16 *layer, u8 bis,
+ struct efi_pxe_base_code_discover_info *info)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_mtftp(
+ struct efi_pxe_base_code_protocol *this,
+ u32 operation, void *buffer_ptr,
+ u8 overwrite, efi_uintn_t *buffer_size,
+ struct efi_ip_address server_ip, char *filename,
+ struct efi_pxe_base_code_mtftp_info *info,
+ u8 dont_use_buffer)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_udp_write(
+ struct efi_pxe_base_code_protocol *this,
+ u16 op_flags, struct efi_ip_address *dest_ip,
+ u16 *dest_port,
+ struct efi_ip_address *gateway_ip,
+ struct efi_ip_address *src_ip, u16 *src_port,
+ efi_uintn_t *header_size, void *header_ptr,
+ efi_uintn_t *buffer_size, void *buffer_ptr)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_udp_read(
+ struct efi_pxe_base_code_protocol *this,
+ u16 op_flags, struct efi_ip_address *dest_ip,
+ u16 *dest_port, struct efi_ip_address *src_ip,
+ u16 *src_port, efi_uintn_t *header_size,
+ void *header_ptr, efi_uintn_t *buffer_size,
+ void *buffer_ptr)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_set_ip_filter(
+ struct efi_pxe_base_code_protocol *this,
+ struct efi_pxe_base_code_filter *new_filter)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_arp(
+ struct efi_pxe_base_code_protocol *this,
+ struct efi_ip_address *ip_addr,
+ struct efi_mac_address *mac_addr)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_set_parameters(
+ struct efi_pxe_base_code_protocol *this,
+ u8 *new_auto_arp, u8 *new_send_guid,
+ u8 *new_ttl, u8 *new_tos,
+ u8 *new_make_callback)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_set_station_ip(
+ struct efi_pxe_base_code_protocol *this,
+ struct efi_ip_address *new_station_ip,
+ struct efi_ip_address *new_subnet_mask)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_set_packets(
+ struct efi_pxe_base_code_protocol *this,
+ u8 *new_dhcp_discover_valid,
+ u8 *new_dhcp_ack_received,
+ u8 *new_proxy_offer_received,
+ u8 *new_pxe_discover_valid,
+ u8 *new_pxe_reply_received,
+ u8 *new_pxe_bis_reply_received,
+ EFI_PXE_BASE_CODE_PACKET *new_dchp_discover,
+ EFI_PXE_BASE_CODE_PACKET *new_dhcp_acc,
+ EFI_PXE_BASE_CODE_PACKET *new_proxy_offer,
+ EFI_PXE_BASE_CODE_PACKET *new_pxe_discover,
+ EFI_PXE_BASE_CODE_PACKET *new_pxe_reply,
+ EFI_PXE_BASE_CODE_PACKET *new_pxe_bis_reply)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_net_register() - register the simple network protocol
+ *
+ * This gets called from do_bootefi_exec().
+ */
+efi_status_t efi_net_register(void)
+{
+ struct efi_net_obj *netobj = NULL;
+ efi_status_t r;
+ int i;
+
+ if (!eth_get_dev()) {
+ /* No network device active, don't expose any */
+ return EFI_SUCCESS;
+ }
+
+ /* We only expose the "active" network device, so one is enough */
+ netobj = calloc(1, sizeof(*netobj));
+ if (!netobj)
+ goto out_of_resources;
+
+ /* Allocate an aligned transmit buffer */
+ transmit_buffer = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
+ if (!transmit_buffer)
+ goto out_of_resources;
+ transmit_buffer = (void *)ALIGN((uintptr_t)transmit_buffer, PKTALIGN);
+
+ /* Allocate a number of receive buffers */
+ receive_buffer = calloc(ETH_PACKETS_BATCH_RECV,
+ sizeof(*receive_buffer));
+ if (!receive_buffer)
+ goto out_of_resources;
+ for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) {
+ receive_buffer[i] = malloc(PKTSIZE_ALIGN);
+ if (!receive_buffer[i])
+ goto out_of_resources;
+ }
+ receive_lengths = calloc(ETH_PACKETS_BATCH_RECV,
+ sizeof(*receive_lengths));
+ if (!receive_lengths)
+ goto out_of_resources;
+
+ /* Hook net up to the device list */
+ efi_add_handle(&netobj->header);
+
+ /* Fill in object data */
+ r = efi_add_protocol(&netobj->header, &efi_net_guid,
+ &netobj->net);
+ if (r != EFI_SUCCESS)
+ goto failure_to_add_protocol;
+ r = efi_add_protocol(&netobj->header, &efi_guid_device_path,
+ efi_dp_from_eth());
+ if (r != EFI_SUCCESS)
+ goto failure_to_add_protocol;
+ r = efi_add_protocol(&netobj->header, &efi_pxe_base_code_protocol_guid,
+ &netobj->pxe);
+ if (r != EFI_SUCCESS)
+ goto failure_to_add_protocol;
+ netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+ netobj->net.start = efi_net_start;
+ netobj->net.stop = efi_net_stop;
+ netobj->net.initialize = efi_net_initialize;
+ netobj->net.reset = efi_net_reset;
+ netobj->net.shutdown = efi_net_shutdown;
+ netobj->net.receive_filters = efi_net_receive_filters;
+ netobj->net.station_address = efi_net_station_address;
+ netobj->net.statistics = efi_net_statistics;
+ netobj->net.mcastiptomac = efi_net_mcastiptomac;
+ netobj->net.nvdata = efi_net_nvdata;
+ netobj->net.get_status = efi_net_get_status;
+ netobj->net.transmit = efi_net_transmit;
+ netobj->net.receive = efi_net_receive;
+ netobj->net.mode = &netobj->net_mode;
+ netobj->net_mode.state = EFI_NETWORK_STOPPED;
+ memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
+ netobj->net_mode.hwaddr_size = ARP_HLEN;
+ netobj->net_mode.media_header_size = ETHER_HDR_SIZE;
+ netobj->net_mode.max_packet_size = PKTSIZE;
+ netobj->net_mode.if_type = ARP_ETHER;
+
+ netobj->pxe.revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION;
+ netobj->pxe.start = efi_pxe_base_code_start;
+ netobj->pxe.stop = efi_pxe_base_code_stop;
+ netobj->pxe.dhcp = efi_pxe_base_code_dhcp;
+ netobj->pxe.discover = efi_pxe_base_code_discover;
+ netobj->pxe.mtftp = efi_pxe_base_code_mtftp;
+ netobj->pxe.udp_write = efi_pxe_base_code_udp_write;
+ netobj->pxe.udp_read = efi_pxe_base_code_udp_read;
+ netobj->pxe.set_ip_filter = efi_pxe_base_code_set_ip_filter;
+ netobj->pxe.arp = efi_pxe_base_code_arp;
+ netobj->pxe.set_parameters = efi_pxe_base_code_set_parameters;
+ netobj->pxe.set_station_ip = efi_pxe_base_code_set_station_ip;
+ netobj->pxe.set_packets = efi_pxe_base_code_set_packets;
+ netobj->pxe.mode = &netobj->pxe_mode;
+ if (dhcp_ack)
+ netobj->pxe_mode.dhcp_ack = *dhcp_ack;
+
+ /*
+ * Create WaitForPacket event.
+ */
+ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
+ efi_network_timer_notify, NULL, NULL,
+ &wait_for_packet);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register network event\n");
+ return r;
+ }
+ netobj->net.wait_for_packet = wait_for_packet;
+ /*
+ * Create a timer event.
+ *
+ * The notification function is used to check if a new network packet
+ * has been received.
+ *
+ * iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL.
+ */
+ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
+ efi_network_timer_notify, &netobj->net, NULL,
+ &network_timer_event);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register network event\n");
+ return r;
+ }
+ /* Network is time critical, create event in every timer cycle */
+ r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to set network timer\n");
+ return r;
+ }
+
+ return EFI_SUCCESS;
+failure_to_add_protocol:
+ printf("ERROR: Failure to add protocol\n");
+ return r;
+out_of_resources:
+ free(netobj);
+ free(transmit_buffer);
+ if (receive_buffer)
+ for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++)
+ free(receive_buffer[i]);
+ free(receive_buffer);
+ free(receive_lengths);
+ printf("ERROR: Out of memory\n");
+ return EFI_OUT_OF_RESOURCES;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_rng.c b/roms/u-boot/lib/efi_loader/efi_rng.c
new file mode 100644
index 000000000..0e0654685
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_rng.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <dm.h>
+#include <efi_loader.h>
+#include <efi_rng.h>
+#include <log.h>
+#include <rng.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+const efi_guid_t efi_guid_rng_protocol = EFI_RNG_PROTOCOL_GUID;
+
+/**
+ * platform_get_rng_device() - retrieve random number generator
+ *
+ * This function retrieves the udevice implementing a hardware random
+ * number generator.
+ *
+ * This function may be overridden if special initialization is needed.
+ *
+ * @dev: udevice
+ * Return: status code
+ */
+__weak efi_status_t platform_get_rng_device(struct udevice **dev)
+{
+ int ret;
+ struct udevice *devp;
+
+ ret = uclass_get_device(UCLASS_RNG, 0, &devp);
+ if (ret) {
+ debug("Unable to get rng device\n");
+ return EFI_DEVICE_ERROR;
+ }
+
+ *dev = devp;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * rng_getinfo() - get information about random number generation
+ *
+ * This function implement the GetInfo() service of the EFI random number
+ * generator protocol. See the UEFI spec for details.
+ *
+ * @this: random number generator protocol instance
+ * @rng_algorithm_list_size: number of random number generation algorithms
+ * @rng_algorithm_list: descriptions of random number generation
+ * algorithms
+ * Return: status code
+ */
+static efi_status_t EFIAPI rng_getinfo(struct efi_rng_protocol *this,
+ efi_uintn_t *rng_algorithm_list_size,
+ efi_guid_t *rng_algorithm_list)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ efi_guid_t rng_algo_guid = EFI_RNG_ALGORITHM_RAW;
+
+ EFI_ENTRY("%p, %p, %p", this, rng_algorithm_list_size,
+ rng_algorithm_list);
+
+ if (!this || !rng_algorithm_list_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto back;
+ }
+
+ if (!rng_algorithm_list ||
+ *rng_algorithm_list_size < sizeof(*rng_algorithm_list)) {
+ *rng_algorithm_list_size = sizeof(*rng_algorithm_list);
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto back;
+ }
+
+ /*
+ * For now, use EFI_RNG_ALGORITHM_RAW as the default
+ * algorithm. If a new algorithm gets added in the
+ * future through a Kconfig, rng_algo_guid will be set
+ * based on that Kconfig option
+ */
+ *rng_algorithm_list_size = sizeof(*rng_algorithm_list);
+ guidcpy(rng_algorithm_list, &rng_algo_guid);
+
+back:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * rng_getrng() - get random value
+ *
+ * This function implement the GetRng() service of the EFI random number
+ * generator protocol. See the UEFI spec for details.
+ *
+ * @this: random number generator protocol instance
+ * @rng_algorithm: random number generation algorithm
+ * @rng_value_length: number of random bytes to generate, buffer length
+ * @rng_value: buffer to receive random bytes
+ * Return: status code
+ */
+static efi_status_t EFIAPI getrng(struct efi_rng_protocol *this,
+ efi_guid_t *rng_algorithm,
+ efi_uintn_t rng_value_length,
+ uint8_t *rng_value)
+{
+ int ret;
+ efi_status_t status = EFI_SUCCESS;
+ struct udevice *dev;
+ const efi_guid_t rng_raw_guid = EFI_RNG_ALGORITHM_RAW;
+
+ EFI_ENTRY("%p, %p, %zu, %p", this, rng_algorithm, rng_value_length,
+ rng_value);
+
+ if (!this || !rng_value || !rng_value_length) {
+ status = EFI_INVALID_PARAMETER;
+ goto back;
+ }
+
+ if (rng_algorithm) {
+ EFI_PRINT("RNG algorithm %pUl\n", rng_algorithm);
+ if (guidcmp(rng_algorithm, &rng_raw_guid)) {
+ status = EFI_UNSUPPORTED;
+ goto back;
+ }
+ }
+
+ ret = platform_get_rng_device(&dev);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("Rng device not found\n");
+ status = EFI_UNSUPPORTED;
+ goto back;
+ }
+
+ ret = dm_rng_read(dev, rng_value, rng_value_length);
+ if (ret < 0) {
+ EFI_PRINT("Rng device read failed\n");
+ status = EFI_DEVICE_ERROR;
+ goto back;
+ }
+
+back:
+ return EFI_EXIT(status);
+}
+
+static const struct efi_rng_protocol efi_rng_protocol = {
+ .get_info = rng_getinfo,
+ .get_rng = getrng,
+};
+
+/**
+ * efi_rng_register() - register EFI_RNG_PROTOCOL
+ *
+ * If a RNG device is available, the Random Number Generator Protocol is
+ * registered.
+ *
+ * Return: An error status is only returned if adding the protocol fails.
+ */
+efi_status_t efi_rng_register(void)
+{
+ efi_status_t ret;
+ struct udevice *dev;
+
+ ret = platform_get_rng_device(&dev);
+ if (ret != EFI_SUCCESS) {
+ log_warning("Missing RNG device for EFI_RNG_PROTOCOL\n");
+ return EFI_SUCCESS;
+ }
+ ret = efi_add_protocol(efi_root, &efi_guid_rng_protocol,
+ (void *)&efi_rng_protocol);
+ if (ret != EFI_SUCCESS)
+ log_err("Cannot install EFI_RNG_PROTOCOL\n");
+
+ return ret;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_root_node.c b/roms/u-boot/lib/efi_loader/efi_root_node.c
new file mode 100644
index 000000000..739c6867f
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_root_node.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Root node for system services
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <efi_dt_fixup.h>
+#include <efi_loader.h>
+
+const efi_guid_t efi_u_boot_guid = U_BOOT_GUID;
+
+efi_handle_t efi_root = NULL;
+
+struct efi_root_dp {
+ struct efi_device_path_vendor vendor;
+ struct efi_device_path end;
+} __packed;
+
+/**
+ * efi_root_node_register() - create root node
+ *
+ * Create the root node on which we install all protocols that are
+ * not related to a loaded image or a driver.
+ *
+ * Return: status code
+ */
+efi_status_t efi_root_node_register(void)
+{
+ efi_status_t ret;
+ struct efi_root_dp *dp;
+
+ /* Create device path protocol */
+ dp = calloc(1, sizeof(*dp));
+ if (!dp)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Fill vendor node */
+ dp->vendor.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ dp->vendor.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+ dp->vendor.dp.length = sizeof(struct efi_device_path_vendor);
+ dp->vendor.guid = efi_u_boot_guid;
+
+ /* Fill end node */
+ dp->end.type = DEVICE_PATH_TYPE_END;
+ dp->end.sub_type = DEVICE_PATH_SUB_TYPE_END;
+ dp->end.length = sizeof(struct efi_device_path);
+
+ /* Create root node and install protocols */
+ ret = EFI_CALL(efi_install_multiple_protocol_interfaces
+ (&efi_root,
+ /* Device path protocol */
+ &efi_guid_device_path, dp,
+#if CONFIG_IS_ENABLED(EFI_DEVICE_PATH_TO_TEXT)
+ /* Device path to text protocol */
+ &efi_guid_device_path_to_text_protocol,
+ (void *)&efi_device_path_to_text,
+#endif
+#ifdef CONFIG_EFI_DEVICE_PATH_UTIL
+ /* Device path utilities protocol */
+ &efi_guid_device_path_utilities_protocol,
+ (void *)&efi_device_path_utilities,
+#endif
+#ifdef CONFIG_EFI_DT_FIXUP
+ /* Device-tree fix-up protocol */
+ &efi_guid_dt_fixup_protocol,
+ (void *)&efi_dt_fixup_prot,
+#endif
+#if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL2)
+ &efi_guid_unicode_collation_protocol2,
+ (void *)&efi_unicode_collation_protocol2,
+#endif
+#if CONFIG_IS_ENABLED(EFI_LOADER_HII)
+ /* HII string protocol */
+ &efi_guid_hii_string_protocol,
+ (void *)&efi_hii_string,
+ /* HII database protocol */
+ &efi_guid_hii_database_protocol,
+ (void *)&efi_hii_database,
+#endif
+ NULL));
+ efi_root->type = EFI_OBJECT_TYPE_U_BOOT_FIRMWARE;
+ return ret;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_runtime.c b/roms/u-boot/lib/efi_loader/efi_runtime.c
new file mode 100644
index 000000000..93a695fc2
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_runtime.c
@@ -0,0 +1,959 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application runtime services
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <command.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <elf.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <malloc.h>
+#include <rtc.h>
+#include <asm/global_data.h>
+#include <u-boot/crc.h>
+
+/* For manual relocation support */
+DECLARE_GLOBAL_DATA_PTR;
+
+/* GUID of the runtime properties table */
+static const efi_guid_t efi_rt_properties_table_guid =
+ EFI_RT_PROPERTIES_TABLE_GUID;
+
+struct efi_runtime_mmio_list {
+ struct list_head link;
+ void **ptr;
+ u64 paddr;
+ u64 len;
+};
+
+/* This list contains all runtime available mmio regions */
+LIST_HEAD(efi_runtime_mmio);
+
+static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
+
+/*
+ * TODO(sjg@chromium.org): These defines and structures should come from the ELF
+ * header for each architecture (or a generic header) rather than being repeated
+ * here.
+ */
+#if defined(__aarch64__)
+#define R_RELATIVE R_AARCH64_RELATIVE
+#define R_MASK 0xffffffffULL
+#define IS_RELA 1
+#elif defined(__arm__)
+#define R_RELATIVE R_ARM_RELATIVE
+#define R_MASK 0xffULL
+#elif defined(__i386__)
+#define R_RELATIVE R_386_RELATIVE
+#define R_MASK 0xffULL
+#elif defined(__x86_64__)
+#define R_RELATIVE R_X86_64_RELATIVE
+#define R_MASK 0xffffffffULL
+#define IS_RELA 1
+#elif defined(__riscv)
+#define R_RELATIVE R_RISCV_RELATIVE
+#define R_MASK 0xffULL
+#define IS_RELA 1
+
+struct dyn_sym {
+ ulong foo1;
+ ulong addr;
+ u32 foo2;
+ u32 foo3;
+};
+#if (__riscv_xlen == 32)
+#define R_ABSOLUTE R_RISCV_32
+#define SYM_INDEX 8
+#elif (__riscv_xlen == 64)
+#define R_ABSOLUTE R_RISCV_64
+#define SYM_INDEX 32
+#else
+#error unknown riscv target
+#endif
+#else
+#error Need to add relocation awareness
+#endif
+
+struct elf_rel {
+ ulong *offset;
+ ulong info;
+};
+
+struct elf_rela {
+ ulong *offset;
+ ulong info;
+ long addend;
+};
+
+static __efi_runtime_data struct efi_mem_desc *efi_virtmap;
+static __efi_runtime_data efi_uintn_t efi_descriptor_count;
+static __efi_runtime_data efi_uintn_t efi_descriptor_size;
+
+/*
+ * EFI runtime code lives in two stages. In the first stage, U-Boot and an EFI
+ * payload are running concurrently at the same time. In this mode, we can
+ * handle a good number of runtime callbacks
+ */
+
+/**
+ * efi_init_runtime_supported() - create runtime properties table
+ *
+ * Create a configuration table specifying which services are available at
+ * runtime.
+ *
+ * Return: status code
+ */
+efi_status_t efi_init_runtime_supported(void)
+{
+ efi_status_t ret;
+ struct efi_rt_properties_table *rt_table;
+
+ ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
+ sizeof(struct efi_rt_properties_table),
+ (void **)&rt_table);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ rt_table->version = EFI_RT_PROPERTIES_TABLE_VERSION;
+ rt_table->length = sizeof(struct efi_rt_properties_table);
+ rt_table->runtime_services_supported =
+ EFI_RT_SUPPORTED_GET_VARIABLE |
+ EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME |
+ EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP |
+ EFI_RT_SUPPORTED_CONVERT_POINTER;
+
+ /*
+ * This value must be synced with efi_runtime_detach_list
+ * as well as efi_runtime_services.
+ */
+#ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
+ rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
+#endif
+
+ ret = efi_install_configuration_table(&efi_rt_properties_table_guid,
+ rt_table);
+ return ret;
+}
+
+/**
+ * efi_memcpy_runtime() - copy memory area
+ *
+ * At runtime memcpy() is not available.
+ *
+ * Overlapping memory areas can be copied safely if src >= dest.
+ *
+ * @dest: destination buffer
+ * @src: source buffer
+ * @n: number of bytes to copy
+ * Return: pointer to destination buffer
+ */
+void __efi_runtime efi_memcpy_runtime(void *dest, const void *src, size_t n)
+{
+ u8 *d = dest;
+ const u8 *s = src;
+
+ for (; n; --n)
+ *d++ = *s++;
+}
+
+/**
+ * efi_update_table_header_crc32() - Update crc32 in table header
+ *
+ * @table: EFI table
+ */
+void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table)
+{
+ table->crc32 = 0;
+ table->crc32 = crc32(0, (const unsigned char *)table,
+ table->headersize);
+}
+
+/**
+ * efi_reset_system_boottime() - reset system at boot time
+ *
+ * This function implements the ResetSystem() runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @reset_type: type of reset to perform
+ * @reset_status: status code for the reset
+ * @data_size: size of reset_data
+ * @reset_data: information about the reset
+ */
+static void EFIAPI efi_reset_system_boottime(
+ enum efi_reset_type reset_type,
+ efi_status_t reset_status,
+ unsigned long data_size, void *reset_data)
+{
+ struct efi_event *evt;
+
+ EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
+ reset_data);
+
+ /* Notify reset */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !guidcmp(evt->group,
+ &efi_guid_event_group_reset_system)) {
+ efi_signal_event(evt);
+ break;
+ }
+ }
+ switch (reset_type) {
+ case EFI_RESET_COLD:
+ case EFI_RESET_WARM:
+ case EFI_RESET_PLATFORM_SPECIFIC:
+ do_reset(NULL, 0, 0, NULL);
+ break;
+ case EFI_RESET_SHUTDOWN:
+#ifdef CONFIG_CMD_POWEROFF
+ do_poweroff(NULL, 0, 0, NULL);
+#endif
+ break;
+ }
+
+ while (1) { }
+}
+
+/**
+ * efi_get_time_boottime() - get current time at boot time
+ *
+ * This function implements the GetTime runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to receive current time
+ * @capabilities: pointer to structure to receive RTC properties
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efi_get_time_boottime(
+ struct efi_time *time,
+ struct efi_time_cap *capabilities)
+{
+#ifdef CONFIG_EFI_GET_TIME
+ efi_status_t ret = EFI_SUCCESS;
+ struct rtc_time tm;
+ struct udevice *dev;
+
+ EFI_ENTRY("%p %p", time, capabilities);
+
+ if (!time) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (uclass_get_device(UCLASS_RTC, 0, &dev) ||
+ dm_rtc_get(dev, &tm)) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ if (dm_rtc_get(dev, &tm)) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ memset(time, 0, sizeof(*time));
+ time->year = tm.tm_year;
+ time->month = tm.tm_mon;
+ time->day = tm.tm_mday;
+ time->hour = tm.tm_hour;
+ time->minute = tm.tm_min;
+ time->second = tm.tm_sec;
+ if (tm.tm_isdst > 0)
+ time->daylight =
+ EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT;
+ else if (!tm.tm_isdst)
+ time->daylight = EFI_TIME_ADJUST_DAYLIGHT;
+ else
+ time->daylight = 0;
+ time->timezone = EFI_UNSPECIFIED_TIMEZONE;
+
+ if (capabilities) {
+ /* Set reasonable dummy values */
+ capabilities->resolution = 1; /* 1 Hz */
+ capabilities->accuracy = 100000000; /* 100 ppm */
+ capabilities->sets_to_zero = false;
+ }
+out:
+ return EFI_EXIT(ret);
+#else
+ EFI_ENTRY("%p %p", time, capabilities);
+ return EFI_EXIT(EFI_UNSUPPORTED);
+#endif
+}
+
+#ifdef CONFIG_EFI_SET_TIME
+
+/**
+ * efi_validate_time() - checks if timestamp is valid
+ *
+ * @time: timestamp to validate
+ * Returns: 0 if timestamp is valid, 1 otherwise
+ */
+static int efi_validate_time(struct efi_time *time)
+{
+ return (!time ||
+ time->year < 1900 || time->year > 9999 ||
+ !time->month || time->month > 12 || !time->day ||
+ time->day > rtc_month_days(time->month - 1, time->year) ||
+ time->hour > 23 || time->minute > 59 || time->second > 59 ||
+ time->nanosecond > 999999999 ||
+ time->daylight &
+ ~(EFI_TIME_IN_DAYLIGHT | EFI_TIME_ADJUST_DAYLIGHT) ||
+ ((time->timezone < -1440 || time->timezone > 1440) &&
+ time->timezone != EFI_UNSPECIFIED_TIMEZONE));
+}
+
+#endif
+
+/**
+ * efi_set_time_boottime() - set current time
+ *
+ * This function implements the SetTime() runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to with current time
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efi_set_time_boottime(struct efi_time *time)
+{
+#ifdef CONFIG_EFI_SET_TIME
+ efi_status_t ret = EFI_SUCCESS;
+ struct rtc_time tm;
+ struct udevice *dev;
+
+ EFI_ENTRY("%p", time);
+
+ if (efi_validate_time(time)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (uclass_get_device(UCLASS_RTC, 0, &dev)) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = time->year;
+ tm.tm_mon = time->month;
+ tm.tm_mday = time->day;
+ tm.tm_hour = time->hour;
+ tm.tm_min = time->minute;
+ tm.tm_sec = time->second;
+ switch (time->daylight) {
+ case EFI_TIME_ADJUST_DAYLIGHT:
+ tm.tm_isdst = 0;
+ break;
+ case EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT:
+ tm.tm_isdst = 1;
+ break;
+ default:
+ tm.tm_isdst = -1;
+ break;
+ }
+ /* Calculate day of week */
+ rtc_calc_weekday(&tm);
+
+ if (dm_rtc_set(dev, &tm))
+ ret = EFI_DEVICE_ERROR;
+out:
+ return EFI_EXIT(ret);
+#else
+ EFI_ENTRY("%p", time);
+ return EFI_EXIT(EFI_UNSUPPORTED);
+#endif
+}
+/**
+ * efi_reset_system() - reset system
+ *
+ * This function implements the ResetSystem() runtime service after
+ * SetVirtualAddressMap() is called. As this placeholder cannot reset the
+ * system it simply return to the caller.
+ *
+ * Boards may override the helpers below to implement reset functionality.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @reset_type: type of reset to perform
+ * @reset_status: status code for the reset
+ * @data_size: size of reset_data
+ * @reset_data: information about the reset
+ */
+void __weak __efi_runtime EFIAPI efi_reset_system(
+ enum efi_reset_type reset_type,
+ efi_status_t reset_status,
+ unsigned long data_size, void *reset_data)
+{
+ return;
+}
+
+/**
+ * efi_reset_system_init() - initialize the reset driver
+ *
+ * Boards may override this function to initialize the reset driver.
+ */
+efi_status_t __weak efi_reset_system_init(void)
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_get_time() - get current time
+ *
+ * This function implements the GetTime runtime service after
+ * SetVirtualAddressMap() is called. As the U-Boot driver are not available
+ * anymore only an error code is returned.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to receive current time
+ * @capabilities: pointer to structure to receive RTC properties
+ * Returns: status code
+ */
+efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
+ struct efi_time *time,
+ struct efi_time_cap *capabilities)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_set_time() - set current time
+ *
+ * This function implements the SetTime runtime service after
+ * SetVirtualAddressMap() is called. As the U-Boot driver are not available
+ * anymore only an error code is returned.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to with current time
+ * Returns: status code
+ */
+efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_update_capsule_unsupported() - process information from operating system
+ *
+ * This function implements the UpdateCapsule() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array: pointer to array of virtual pointers
+ * @capsule_count: number of pointers in capsule_header_array
+ * @scatter_gather_list: pointer to array of physical pointers
+ * Returns: status code
+ */
+efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
+ struct efi_capsule_header **capsule_header_array,
+ efi_uintn_t capsule_count,
+ u64 scatter_gather_list)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_query_capsule_caps_unsupported() - check if capsule is supported
+ *
+ * This function implements the QueryCapsuleCapabilities() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array: pointer to array of virtual pointers
+ * @capsule_count: number of pointers in capsule_header_array
+ * @maximum_capsule_size: maximum capsule size
+ * @reset_type: type of reset needed for capsule update
+ * Returns: status code
+ */
+efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps_unsupported(
+ struct efi_capsule_header **capsule_header_array,
+ efi_uintn_t capsule_count,
+ u64 *maximum_capsule_size,
+ u32 *reset_type)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_is_runtime_service_pointer() - check if pointer points to runtime table
+ *
+ * @p: pointer to check
+ * Return: true if the pointer points to a service function pointer in the
+ * runtime table
+ */
+static bool efi_is_runtime_service_pointer(void *p)
+{
+ return (p >= (void *)&efi_runtime_services.get_time &&
+ p <= (void *)&efi_runtime_services.query_variable_info) ||
+ p == (void *)&efi_events.prev ||
+ p == (void *)&efi_events.next;
+}
+
+/**
+ * efi_runtime_detach() - detach unimplemented runtime functions
+ */
+void efi_runtime_detach(void)
+{
+ efi_runtime_services.reset_system = efi_reset_system;
+ efi_runtime_services.get_time = efi_get_time;
+ efi_runtime_services.set_time = efi_set_time;
+ if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE)) {
+ /* won't support at runtime */
+ efi_runtime_services.update_capsule =
+ efi_update_capsule_unsupported;
+ efi_runtime_services.query_capsule_caps =
+ efi_query_capsule_caps_unsupported;
+ }
+
+ /* Update CRC32 */
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
+}
+
+/**
+ * efi_set_virtual_address_map_runtime() - change from physical to virtual
+ * mapping
+ *
+ * This function implements the SetVirtualAddressMap() runtime service after
+ * it is first called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @memory_map_size: size of the virtual map
+ * @descriptor_size: size of an entry in the map
+ * @descriptor_version: version of the map entries
+ * @virtmap: virtual address mapping information
+ * Return: status code EFI_UNSUPPORTED
+ */
+static __efi_runtime efi_status_t EFIAPI efi_set_virtual_address_map_runtime(
+ efi_uintn_t memory_map_size,
+ efi_uintn_t descriptor_size,
+ uint32_t descriptor_version,
+ struct efi_mem_desc *virtmap)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_convert_pointer_runtime() - convert from physical to virtual pointer
+ *
+ * This function implements the ConvertPointer() runtime service after
+ * the first call to SetVirtualAddressMap().
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @debug_disposition: indicates if pointer may be converted to NULL
+ * @address: pointer to be converted
+ * Return: status code EFI_UNSUPPORTED
+ */
+static __efi_runtime efi_status_t EFIAPI efi_convert_pointer_runtime(
+ efi_uintn_t debug_disposition, void **address)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_convert_pointer() - convert from physical to virtual pointer
+ *
+ * This function implements the ConvertPointer() runtime service until
+ * the first call to SetVirtualAddressMap().
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @debug_disposition: indicates if pointer may be converted to NULL
+ * @address: pointer to be converted
+ * Return: status code
+ */
+__efi_runtime efi_status_t EFIAPI
+efi_convert_pointer(efi_uintn_t debug_disposition, void **address)
+{
+ efi_physical_addr_t addr;
+ efi_uintn_t i;
+ efi_status_t ret = EFI_NOT_FOUND;
+
+ if (!efi_virtmap) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ if (!address) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (!*address) {
+ if (debug_disposition & EFI_OPTIONAL_PTR)
+ return EFI_SUCCESS;
+ else
+ return EFI_INVALID_PARAMETER;
+ }
+
+ addr = (uintptr_t)*address;
+ for (i = 0; i < efi_descriptor_count; i++) {
+ struct efi_mem_desc *map = (void *)efi_virtmap +
+ (efi_descriptor_size * i);
+
+ if (addr >= map->physical_start &&
+ (addr < map->physical_start
+ + (map->num_pages << EFI_PAGE_SHIFT))) {
+ *address = (void *)(uintptr_t)
+ (addr + map->virtual_start -
+ map->physical_start);
+
+ ret = EFI_SUCCESS;
+ break;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static __efi_runtime void efi_relocate_runtime_table(ulong offset)
+{
+ ulong patchoff;
+ void **pos;
+
+ /* Relocate the runtime services pointers */
+ patchoff = offset - gd->relocaddr;
+ for (pos = (void **)&efi_runtime_services.get_time;
+ pos <= (void **)&efi_runtime_services.query_variable_info; ++pos) {
+ if (*pos)
+ *pos += patchoff;
+ }
+
+ /*
+ * The entry for SetVirtualAddress() must point to a physical address.
+ * After the first execution the service must return EFI_UNSUPPORTED.
+ */
+ efi_runtime_services.set_virtual_address_map =
+ &efi_set_virtual_address_map_runtime;
+
+ /*
+ * The entry for ConvertPointer() must point to a physical address.
+ * The service is not usable after SetVirtualAddress().
+ */
+ efi_runtime_services.convert_pointer = &efi_convert_pointer_runtime;
+
+ /*
+ * TODO: Update UEFI variable RuntimeServicesSupported removing flags
+ * EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP and
+ * EFI_RT_SUPPORTED_CONVERT_POINTER as required by the UEFI spec 2.8.
+ */
+
+ /* Update CRC32 */
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
+}
+
+/* Relocate EFI runtime to uboot_reloc_base = offset */
+void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
+{
+#ifdef IS_RELA
+ struct elf_rela *rel = (void*)&__efi_runtime_rel_start;
+#else
+ struct elf_rel *rel = (void*)&__efi_runtime_rel_start;
+ static ulong lastoff = CONFIG_SYS_TEXT_BASE;
+#endif
+
+ debug("%s: Relocating to offset=%lx\n", __func__, offset);
+ for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) {
+ ulong base = CONFIG_SYS_TEXT_BASE;
+ ulong *p;
+ ulong newaddr;
+
+ p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
+
+ /*
+ * The runtime services table is updated in
+ * efi_relocate_runtime_table()
+ */
+ if (map && efi_is_runtime_service_pointer(p))
+ continue;
+
+ debug("%s: rel->info=%#lx *p=%#lx rel->offset=%p\n", __func__,
+ rel->info, *p, rel->offset);
+
+ switch (rel->info & R_MASK) {
+ case R_RELATIVE:
+#ifdef IS_RELA
+ newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE;
+#else
+ newaddr = *p - lastoff + offset;
+#endif
+ break;
+#ifdef R_ABSOLUTE
+ case R_ABSOLUTE: {
+ ulong symidx = rel->info >> SYM_INDEX;
+ extern struct dyn_sym __dyn_sym_start[];
+ newaddr = __dyn_sym_start[symidx].addr + offset;
+#ifdef IS_RELA
+ newaddr -= CONFIG_SYS_TEXT_BASE;
+#endif
+ break;
+ }
+#endif
+ default:
+ printf("%s: Unknown relocation type %llx\n",
+ __func__, rel->info & R_MASK);
+ continue;
+ }
+
+ /* Check if the relocation is inside bounds */
+ if (map && ((newaddr < map->virtual_start) ||
+ newaddr > (map->virtual_start +
+ (map->num_pages << EFI_PAGE_SHIFT)))) {
+ printf("%s: Relocation at %p is out of range (%lx)\n",
+ __func__, p, newaddr);
+ continue;
+ }
+
+ debug("%s: Setting %p to %lx\n", __func__, p, newaddr);
+ *p = newaddr;
+ flush_dcache_range((ulong)p & ~(EFI_CACHELINE_SIZE - 1),
+ ALIGN((ulong)&p[1], EFI_CACHELINE_SIZE));
+ }
+
+#ifndef IS_RELA
+ lastoff = offset;
+#endif
+
+ invalidate_icache_all();
+}
+
+/**
+ * efi_set_virtual_address_map() - change from physical to virtual mapping
+ *
+ * This function implements the SetVirtualAddressMap() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @memory_map_size: size of the virtual map
+ * @descriptor_size: size of an entry in the map
+ * @descriptor_version: version of the map entries
+ * @virtmap: virtual address mapping information
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_set_virtual_address_map(
+ efi_uintn_t memory_map_size,
+ efi_uintn_t descriptor_size,
+ uint32_t descriptor_version,
+ struct efi_mem_desc *virtmap)
+{
+ efi_uintn_t n = memory_map_size / descriptor_size;
+ efi_uintn_t i;
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+ int rt_code_sections = 0;
+ struct efi_event *event;
+
+ EFI_ENTRY("%zx %zx %x %p", memory_map_size, descriptor_size,
+ descriptor_version, virtmap);
+
+ if (descriptor_version != EFI_MEMORY_DESCRIPTOR_VERSION ||
+ descriptor_size < sizeof(struct efi_mem_desc))
+ goto out;
+
+ efi_virtmap = virtmap;
+ efi_descriptor_size = descriptor_size;
+ efi_descriptor_count = n;
+
+ /*
+ * TODO:
+ * Further down we are cheating. While really we should implement
+ * SetVirtualAddressMap() events and ConvertPointer() to allow
+ * dynamically loaded drivers to expose runtime services, we don't
+ * today.
+ *
+ * So let's ensure we see exactly one single runtime section, as
+ * that is the built-in one. If we see more (or less), someone must
+ * have tried adding or removing to that which we don't support yet.
+ * In that case, let's better fail rather than expose broken runtime
+ * services.
+ */
+ for (i = 0; i < n; i++) {
+ struct efi_mem_desc *map = (void*)virtmap +
+ (descriptor_size * i);
+
+ if (map->type == EFI_RUNTIME_SERVICES_CODE)
+ rt_code_sections++;
+ }
+
+ if (rt_code_sections != 1) {
+ /*
+ * We expose exactly one single runtime code section, so
+ * something is definitely going wrong.
+ */
+ goto out;
+ }
+
+ /* Notify EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
+ list_for_each_entry(event, &efi_events, link) {
+ if (event->notify_function)
+ EFI_CALL_VOID(event->notify_function(
+ event, event->notify_context));
+ }
+
+ /* Rebind mmio pointers */
+ for (i = 0; i < n; i++) {
+ struct efi_mem_desc *map = (void*)virtmap +
+ (descriptor_size * i);
+ struct list_head *lhandle;
+ efi_physical_addr_t map_start = map->physical_start;
+ efi_physical_addr_t map_len = map->num_pages << EFI_PAGE_SHIFT;
+ efi_physical_addr_t map_end = map_start + map_len;
+ u64 off = map->virtual_start - map_start;
+
+ /* Adjust all mmio pointers in this region */
+ list_for_each(lhandle, &efi_runtime_mmio) {
+ struct efi_runtime_mmio_list *lmmio;
+
+ lmmio = list_entry(lhandle,
+ struct efi_runtime_mmio_list,
+ link);
+ if ((map_start <= lmmio->paddr) &&
+ (map_end >= lmmio->paddr)) {
+ uintptr_t new_addr = lmmio->paddr + off;
+ *lmmio->ptr = (void *)new_addr;
+ }
+ }
+ if ((map_start <= (uintptr_t)systab.tables) &&
+ (map_end >= (uintptr_t)systab.tables)) {
+ char *ptr = (char *)systab.tables;
+
+ ptr += off;
+ systab.tables = (struct efi_configuration_table *)ptr;
+ }
+ }
+
+ /* Relocate the runtime. See TODO above */
+ for (i = 0; i < n; i++) {
+ struct efi_mem_desc *map;
+
+ map = (void*)virtmap + (descriptor_size * i);
+ if (map->type == EFI_RUNTIME_SERVICES_CODE) {
+ ulong new_offset = map->virtual_start -
+ map->physical_start + gd->relocaddr;
+
+ efi_relocate_runtime_table(new_offset);
+ efi_runtime_relocate(new_offset, map);
+ ret = EFI_SUCCESS;
+ goto out;
+ }
+ }
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_add_runtime_mmio() - add memory-mapped IO region
+ *
+ * This function adds a memory-mapped IO region to the memory map to make it
+ * available at runtime.
+ *
+ * @mmio_ptr: pointer to a pointer to the start of the memory-mapped
+ * IO region
+ * @len: size of the memory-mapped IO region
+ * Returns: status code
+ */
+efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
+{
+ struct efi_runtime_mmio_list *newmmio;
+ uint64_t addr = *(uintptr_t *)mmio_ptr;
+ efi_status_t ret;
+
+ ret = efi_add_memory_map(addr, len, EFI_MMAP_IO);
+ if (ret != EFI_SUCCESS)
+ return EFI_OUT_OF_RESOURCES;
+
+ newmmio = calloc(1, sizeof(*newmmio));
+ if (!newmmio)
+ return EFI_OUT_OF_RESOURCES;
+ newmmio->ptr = mmio_ptr;
+ newmmio->paddr = *(uintptr_t *)mmio_ptr;
+ newmmio->len = len;
+ list_add_tail(&newmmio->link, &efi_runtime_mmio);
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * In the second stage, U-Boot has disappeared. To isolate our runtime code
+ * that at this point still exists from the rest, we put it into a special
+ * section.
+ *
+ * !!WARNING!!
+ *
+ * This means that we can not rely on any code outside of this file in any
+ * function or variable below this line.
+ *
+ * Please keep everything fully self-contained and annotated with
+ * __efi_runtime and __efi_runtime_data markers.
+ */
+
+/*
+ * Relocate the EFI runtime stub to a different place. We need to call this
+ * the first time we expose the runtime interface to a user and on set virtual
+ * address map calls.
+ */
+
+/**
+ * efi_unimplemented() - replacement function, returns EFI_UNSUPPORTED
+ *
+ * This function is used after SetVirtualAddressMap() is called as replacement
+ * for services that are not available anymore due to constraints of the U-Boot
+ * implementation.
+ *
+ * Return: EFI_UNSUPPORTED
+ */
+static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
+{
+ return EFI_UNSUPPORTED;
+}
+
+struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
+ .hdr = {
+ .signature = EFI_RUNTIME_SERVICES_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_runtime_services),
+ },
+ .get_time = &efi_get_time_boottime,
+ .set_time = &efi_set_time_boottime,
+ .get_wakeup_time = (void *)&efi_unimplemented,
+ .set_wakeup_time = (void *)&efi_unimplemented,
+ .set_virtual_address_map = &efi_set_virtual_address_map,
+ .convert_pointer = efi_convert_pointer,
+ .get_variable = efi_get_variable,
+ .get_next_variable_name = efi_get_next_variable_name,
+ .set_variable = efi_set_variable,
+ .get_next_high_mono_count = (void *)&efi_unimplemented,
+ .reset_system = &efi_reset_system_boottime,
+#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
+ .update_capsule = efi_update_capsule,
+ .query_capsule_caps = efi_query_capsule_caps,
+#else
+ .update_capsule = efi_update_capsule_unsupported,
+ .query_capsule_caps = efi_query_capsule_caps_unsupported,
+#endif
+ .query_variable_info = efi_query_variable_info,
+};
diff --git a/roms/u-boot/lib/efi_loader/efi_setup.c b/roms/u-boot/lib/efi_loader/efi_setup.c
new file mode 100644
index 000000000..a2338d74a
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_setup.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI setup code
+ *
+ * Copyright (c) 2016-2018 Alexander Graf et al.
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <log.h>
+
+#define OBJ_LIST_NOT_INITIALIZED 1
+
+efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED;
+
+/*
+ * Allow unaligned memory access.
+ *
+ * This routine is overridden by architectures providing this feature.
+ */
+void __weak allow_unaligned(void)
+{
+}
+
+/**
+ * efi_init_platform_lang() - define supported languages
+ *
+ * Set the PlatformLangCodes and PlatformLang variables.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_init_platform_lang(void)
+{
+ efi_status_t ret;
+ efi_uintn_t data_size = 0;
+ char *lang = CONFIG_EFI_PLATFORM_LANG_CODES;
+ char *pos;
+
+ /*
+ * Variable PlatformLangCodes defines the language codes that the
+ * machine can support.
+ */
+ ret = efi_set_variable_int(L"PlatformLangCodes",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ sizeof(CONFIG_EFI_PLATFORM_LANG_CODES),
+ CONFIG_EFI_PLATFORM_LANG_CODES, false);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /*
+ * Variable PlatformLang defines the language that the machine has been
+ * configured for.
+ */
+ ret = efi_get_variable_int(L"PlatformLang",
+ &efi_global_variable_guid,
+ NULL, &data_size, &pos, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ /* The variable is already set. Do not change it. */
+ ret = EFI_SUCCESS;
+ goto out;
+ }
+
+ /*
+ * The list of supported languages is semicolon separated. Use the first
+ * language to initialize PlatformLang.
+ */
+ pos = strchr(lang, ';');
+ if (pos)
+ *pos = 0;
+
+ ret = efi_set_variable_int(L"PlatformLang",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ 1 + strlen(lang), lang, false);
+out:
+ if (ret != EFI_SUCCESS)
+ printf("EFI: cannot initialize platform language settings\n");
+ return ret;
+}
+
+#ifdef CONFIG_EFI_SECURE_BOOT
+/**
+ * efi_init_secure_boot - initialize secure boot state
+ *
+ * Return: status code
+ */
+static efi_status_t efi_init_secure_boot(void)
+{
+ efi_guid_t signature_types[] = {
+ EFI_CERT_SHA256_GUID,
+ EFI_CERT_X509_GUID,
+ };
+ efi_status_t ret;
+
+ ret = efi_set_variable_int(L"SignatureSupport",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_READ_ONLY |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(signature_types),
+ &signature_types, false);
+ if (ret != EFI_SUCCESS)
+ printf("EFI: cannot initialize SignatureSupport variable\n");
+
+ return ret;
+}
+#else
+static efi_status_t efi_init_secure_boot(void)
+{
+ return EFI_SUCCESS;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
+/**
+ * efi_init_capsule - initialize capsule update state
+ *
+ * Return: status code
+ */
+static efi_status_t efi_init_capsule(void)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_UPDATE)) {
+ ret = efi_set_variable_int(L"CapsuleMax",
+ &efi_guid_capsule_report,
+ EFI_VARIABLE_READ_ONLY |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ 22, L"CapsuleFFFF", false);
+ if (ret != EFI_SUCCESS)
+ printf("EFI: cannot initialize CapsuleMax variable\n");
+ }
+
+ return ret;
+}
+
+/**
+ * efi_init_os_indications() - indicate supported features for OS requests
+ *
+ * Set the OsIndicationsSupported variable.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_init_os_indications(void)
+{
+ u64 os_indications_supported = 0;
+
+ if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT))
+ os_indications_supported |=
+ EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
+
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK))
+ os_indications_supported |=
+ EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;
+
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT))
+ os_indications_supported |=
+ EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED;
+
+ return efi_set_variable_int(L"OsIndicationsSupported",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ sizeof(os_indications_supported),
+ &os_indications_supported, false);
+}
+
+
+/**
+ * efi_clear_os_indications() - clear OsIndications
+ *
+ * Clear EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
+ */
+static efi_status_t efi_clear_os_indications(void)
+{
+ efi_uintn_t size;
+ u64 os_indications;
+ efi_status_t ret;
+
+ size = sizeof(os_indications);
+ ret = efi_get_variable_int(L"OsIndications", &efi_global_variable_guid,
+ NULL, &size, &os_indications, NULL);
+ if (ret != EFI_SUCCESS)
+ os_indications = 0;
+ else
+ os_indications &=
+ ~EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;
+ ret = efi_set_variable_int(L"OsIndications", &efi_global_variable_guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(os_indications), &os_indications,
+ false);
+ if (ret != EFI_SUCCESS)
+ log_err("Setting %ls failed\n", L"OsIndications");
+ return ret;
+}
+
+/**
+ * efi_init_obj_list() - Initialize and populate EFI object list
+ *
+ * Return: status code
+ */
+efi_status_t efi_init_obj_list(void)
+{
+ efi_status_t r, ret = EFI_SUCCESS;
+
+ /* Initialize once only */
+ if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED)
+ return efi_obj_list_initialized;
+
+ /* Allow unaligned memory access */
+ allow_unaligned();
+
+ /* Initialize root node */
+ ret = efi_root_node_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = efi_console_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+#ifdef CONFIG_PARTITIONS
+ ret = efi_disk_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+#endif
+ if (IS_ENABLED(CONFIG_EFI_RNG_PROTOCOL)) {
+ ret = efi_rng_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+ /* Initialize variable services */
+ ret = efi_init_variables();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Define supported languages */
+ ret = efi_init_platform_lang();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Indicate supported features */
+ ret = efi_init_os_indications();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Initialize system table */
+ ret = efi_initialize_system_table();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (IS_ENABLED(CONFIG_EFI_ESRT)) {
+ ret = efi_esrt_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+ if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
+ ret = efi_tcg2_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+ /* Secure boot */
+ ret = efi_init_secure_boot();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Indicate supported runtime services */
+ ret = efi_init_runtime_supported();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Initialize EFI driver uclass */
+ ret = efi_driver_init();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)) {
+ ret = efi_load_capsule_drivers();
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
+#if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO)
+ ret = efi_gop_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+#endif
+#ifdef CONFIG_NET
+ ret = efi_net_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+#endif
+#ifdef CONFIG_GENERATE_ACPI_TABLE
+ ret = efi_acpi_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+#endif
+#ifdef CONFIG_GENERATE_SMBIOS_TABLE
+ ret = efi_smbios_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+#endif
+ ret = efi_watchdog_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = efi_init_capsule();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Initialize EFI runtime services */
+ ret = efi_reset_system_init();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Execute capsules after reboot */
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK) &&
+ !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
+ ret = efi_launch_capsules();
+
+out:
+ r = efi_clear_os_indications();
+ if (ret == EFI_SUCCESS)
+ ret = r;
+ efi_obj_list_initialized = ret;
+ return ret;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_signature.c b/roms/u-boot/lib/efi_loader/efi_signature.c
new file mode 100644
index 000000000..bdd09881f
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_signature.c
@@ -0,0 +1,784 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
+ * Copyright (c) 2019 Linaro Limited, Author: AKASHI Takahiro
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <efi_loader.h>
+#include <image.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <crypto/pkcs7.h>
+#include <crypto/pkcs7_parser.h>
+#include <crypto/public_key.h>
+#include <linux/compat.h>
+#include <linux/oid_registry.h>
+#include <u-boot/hash-checksum.h>
+#include <u-boot/rsa.h>
+#include <u-boot/sha256.h>
+
+const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
+const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
+const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
+const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
+const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+
+static u8 pkcs7_hdr[] = {
+ /* SEQUENCE */
+ 0x30, 0x82, 0x05, 0xc7,
+ /* OID: pkcs7-signedData */
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02,
+ /* Context Structured? */
+ 0xa0, 0x82, 0x05, 0xb8,
+};
+
+/**
+ * efi_parse_pkcs7_header - parse a signature in payload
+ * @buf: Pointer to payload's value
+ * @buflen: Length of @buf
+ * @tmpbuf: Pointer to temporary buffer
+ *
+ * Parse a signature embedded in payload's value and instantiate
+ * a pkcs7_message structure. Since pkcs7_parse_message() accepts only
+ * pkcs7's signedData, some header needed be prepended for correctly
+ * parsing authentication data
+ * A temporary buffer will be allocated if needed, and it should be
+ * kept valid during the authentication because some data in the buffer
+ * will be referenced by efi_signature_verify().
+ *
+ * Return: Pointer to pkcs7_message structure on success, NULL on error
+ */
+struct pkcs7_message *efi_parse_pkcs7_header(const void *buf,
+ size_t buflen,
+ u8 **tmpbuf)
+{
+ u8 *ebuf;
+ size_t ebuflen, len;
+ struct pkcs7_message *msg;
+
+ /*
+ * This is the best assumption to check if the binary is
+ * already in a form of pkcs7's signedData.
+ */
+ if (buflen > sizeof(pkcs7_hdr) &&
+ !memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) {
+ msg = pkcs7_parse_message(buf, buflen);
+ if (IS_ERR(msg))
+ return NULL;
+ return msg;
+ }
+
+ /*
+ * Otherwise, we should add a dummy prefix sequence for pkcs7
+ * message parser to be able to process.
+ * NOTE: EDK2 also uses similar hack in WrapPkcs7Data()
+ * in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c
+ * TODO:
+ * The header should be composed in a more refined manner.
+ */
+ EFI_PRINT("Makeshift prefix added to authentication data\n");
+ ebuflen = sizeof(pkcs7_hdr) + buflen;
+ if (ebuflen <= 0x7f) {
+ EFI_PRINT("Data is too short\n");
+ return NULL;
+ }
+
+ ebuf = malloc(ebuflen);
+ if (!ebuf) {
+ EFI_PRINT("Out of memory\n");
+ return NULL;
+ }
+
+ memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr));
+ memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen);
+ len = ebuflen - 4;
+ ebuf[2] = (len >> 8) & 0xff;
+ ebuf[3] = len & 0xff;
+ len = ebuflen - 0x13;
+ ebuf[0x11] = (len >> 8) & 0xff;
+ ebuf[0x12] = len & 0xff;
+
+ msg = pkcs7_parse_message(ebuf, ebuflen);
+
+ if (IS_ERR(msg)) {
+ free(ebuf);
+ return NULL;
+ }
+
+ *tmpbuf = ebuf;
+ return msg;
+}
+
+/**
+ * efi_hash_regions - calculate a hash value
+ * @regs: Array of regions
+ * @count: Number of regions
+ * @hash: Pointer to a pointer to buffer holding a hash value
+ * @size: Size of buffer to be returned
+ *
+ * Calculate a sha256 value of @regs and return a value in @hash.
+ *
+ * Return: true on success, false on error
+ */
+static bool efi_hash_regions(struct image_region *regs, int count,
+ void **hash, size_t *size)
+{
+ if (!*hash) {
+ *hash = calloc(1, SHA256_SUM_LEN);
+ if (!*hash) {
+ EFI_PRINT("Out of memory\n");
+ return false;
+ }
+ }
+ if (size)
+ *size = SHA256_SUM_LEN;
+
+ hash_calculate("sha256", regs, count, *hash);
+#ifdef DEBUG
+ EFI_PRINT("hash calculated:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ *hash, SHA256_SUM_LEN, false);
+#endif
+
+ return true;
+}
+
+/**
+ * efi_signature_lookup_digest - search for an image's digest in sigdb
+ * @regs: List of regions to be authenticated
+ * @db: Signature database for trusted certificates
+ *
+ * A message digest of image pointed to by @regs is calculated and
+ * its hash value is compared to entries in signature database pointed
+ * to by @db.
+ *
+ * Return: true if found, false if not
+ */
+bool efi_signature_lookup_digest(struct efi_image_regions *regs,
+ struct efi_signature_store *db)
+{
+ struct efi_signature_store *siglist;
+ struct efi_sig_data *sig_data;
+ void *hash = NULL;
+ size_t size = 0;
+ bool found = false;
+
+ EFI_PRINT("%s: Enter, %p, %p\n", __func__, regs, db);
+
+ if (!regs || !db || !db->sig_data_list)
+ goto out;
+
+ for (siglist = db; siglist; siglist = siglist->next) {
+ /* TODO: support other hash algorithms */
+ if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) {
+ EFI_PRINT("Digest algorithm is not supported: %pUl\n",
+ &siglist->sig_type);
+ break;
+ }
+
+ if (!efi_hash_regions(regs->reg, regs->num, &hash, &size)) {
+ EFI_PRINT("Digesting an image failed\n");
+ break;
+ }
+
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+#ifdef DEBUG
+ EFI_PRINT("Msg digest in database:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ sig_data->data, sig_data->size, false);
+#endif
+ if (sig_data->size == size &&
+ !memcmp(sig_data->data, hash, size)) {
+ found = true;
+ free(hash);
+ goto out;
+ }
+ }
+
+ free(hash);
+ hash = NULL;
+ }
+
+out:
+ EFI_PRINT("%s: Exit, found: %d\n", __func__, found);
+ return found;
+}
+
+/**
+ * efi_lookup_certificate - find a certificate within db
+ * @msg: Signature
+ * @db: Signature database
+ *
+ * Search signature database pointed to by @db and find a certificate
+ * pointed to by @cert.
+ *
+ * Return: true if found, false otherwise.
+ */
+static bool efi_lookup_certificate(struct x509_certificate *cert,
+ struct efi_signature_store *db)
+{
+ struct efi_signature_store *siglist;
+ struct efi_sig_data *sig_data;
+ struct image_region reg[1];
+ void *hash = NULL, *hash_tmp = NULL;
+ size_t size = 0;
+ bool found = false;
+
+ EFI_PRINT("%s: Enter, %p, %p\n", __func__, cert, db);
+
+ if (!cert || !db || !db->sig_data_list)
+ goto out;
+
+ /*
+ * TODO: identify a certificate using sha256 digest
+ * Is there any better way?
+ */
+ /* calculate hash of TBSCertificate */
+ reg[0].data = cert->tbs;
+ reg[0].size = cert->tbs_size;
+ if (!efi_hash_regions(reg, 1, &hash, &size))
+ goto out;
+
+ EFI_PRINT("%s: searching for %s\n", __func__, cert->subject);
+ for (siglist = db; siglist; siglist = siglist->next) {
+ /* only with x509 certificate */
+ if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509))
+ continue;
+
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+ struct x509_certificate *cert_tmp;
+
+ cert_tmp = x509_cert_parse(sig_data->data,
+ sig_data->size);
+ if (IS_ERR_OR_NULL(cert_tmp))
+ continue;
+
+ EFI_PRINT("%s: against %s\n", __func__,
+ cert_tmp->subject);
+ reg[0].data = cert_tmp->tbs;
+ reg[0].size = cert_tmp->tbs_size;
+ if (!efi_hash_regions(reg, 1, &hash_tmp, NULL))
+ goto out;
+
+ x509_free_certificate(cert_tmp);
+
+ if (!memcmp(hash, hash_tmp, size)) {
+ found = true;
+ goto out;
+ }
+ }
+ }
+out:
+ free(hash);
+ free(hash_tmp);
+
+ EFI_PRINT("%s: Exit, found: %d\n", __func__, found);
+ return found;
+}
+
+/**
+ * efi_verify_certificate - verify certificate's signature with database
+ * @signer: Certificate
+ * @db: Signature database
+ * @root: Certificate to verify @signer
+ *
+ * Determine if certificate pointed to by @signer may be verified
+ * by one of certificates in signature database pointed to by @db.
+ *
+ * Return: true if certificate is verified, false otherwise.
+ */
+static bool efi_verify_certificate(struct x509_certificate *signer,
+ struct efi_signature_store *db,
+ struct x509_certificate **root)
+{
+ struct efi_signature_store *siglist;
+ struct efi_sig_data *sig_data;
+ struct x509_certificate *cert;
+ bool verified = false;
+ int ret;
+
+ EFI_PRINT("%s: Enter, %p, %p\n", __func__, signer, db);
+
+ if (!signer || !db || !db->sig_data_list)
+ goto out;
+
+ for (siglist = db; siglist; siglist = siglist->next) {
+ /* only with x509 certificate */
+ if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509))
+ continue;
+
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+ cert = x509_cert_parse(sig_data->data, sig_data->size);
+ if (IS_ERR_OR_NULL(cert)) {
+ EFI_PRINT("Cannot parse x509 certificate\n");
+ continue;
+ }
+
+ ret = public_key_verify_signature(cert->pub,
+ signer->sig);
+ if (!ret) {
+ verified = true;
+ if (root)
+ *root = cert;
+ else
+ x509_free_certificate(cert);
+ goto out;
+ }
+ x509_free_certificate(cert);
+ }
+ }
+
+out:
+ EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified);
+ return verified;
+}
+
+/**
+ * efi_signature_check_revocation - check revocation with dbx
+ * @sinfo: Signer's info
+ * @cert: x509 certificate
+ * @dbx: Revocation signature database
+ *
+ * Search revocation signature database pointed to by @dbx and find
+ * an entry matching to certificate pointed to by @cert.
+ *
+ * While this entry contains revocation time, we don't support timestamp
+ * protocol at this time and any image will be unconditionally revoked
+ * when this match occurs.
+ *
+ * Return: true if check passed (not found), false otherwise.
+ */
+static bool efi_signature_check_revocation(struct pkcs7_signed_info *sinfo,
+ struct x509_certificate *cert,
+ struct efi_signature_store *dbx)
+{
+ struct efi_signature_store *siglist;
+ struct efi_sig_data *sig_data;
+ struct image_region reg[1];
+ void *hash = NULL;
+ size_t size = 0;
+ time64_t revoc_time;
+ bool revoked = false;
+
+ EFI_PRINT("%s: Enter, %p, %p, %p\n", __func__, sinfo, cert, dbx);
+
+ if (!sinfo || !cert || !dbx || !dbx->sig_data_list)
+ goto out;
+
+ EFI_PRINT("Checking revocation against %s\n", cert->subject);
+ for (siglist = dbx; siglist; siglist = siglist->next) {
+ if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256))
+ continue;
+
+ /* calculate hash of TBSCertificate */
+ reg[0].data = cert->tbs;
+ reg[0].size = cert->tbs_size;
+ if (!efi_hash_regions(reg, 1, &hash, &size))
+ goto out;
+
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+ /*
+ * struct efi_cert_x509_sha256 {
+ * u8 tbs_hash[256/8];
+ * time64_t revocation_time;
+ * };
+ */
+#ifdef DEBUG
+ if (sig_data->size >= size) {
+ EFI_PRINT("hash in db:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET,
+ 16, 1,
+ sig_data->data, size, false);
+ }
+#endif
+ if ((sig_data->size < size + sizeof(time64_t)) ||
+ memcmp(sig_data->data, hash, size))
+ continue;
+
+ memcpy(&revoc_time, sig_data->data + size,
+ sizeof(revoc_time));
+ EFI_PRINT("revocation time: 0x%llx\n", revoc_time);
+ /*
+ * TODO: compare signing timestamp in sinfo
+ * with revocation time
+ */
+
+ revoked = true;
+ free(hash);
+ goto out;
+ }
+ free(hash);
+ hash = NULL;
+ }
+out:
+ EFI_PRINT("%s: Exit, revoked: %d\n", __func__, revoked);
+ return !revoked;
+}
+
+/*
+ * efi_signature_verify - verify signatures with db and dbx
+ * @regs: List of regions to be authenticated
+ * @msg: Signature
+ * @db: Signature database for trusted certificates
+ * @dbx: Revocation signature database
+ *
+ * All the signature pointed to by @msg against image pointed to by @regs
+ * will be verified by signature database pointed to by @db and @dbx.
+ *
+ * Return: true if verification for all signatures passed, false otherwise
+ */
+bool efi_signature_verify(struct efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ struct efi_signature_store *db,
+ struct efi_signature_store *dbx)
+{
+ struct pkcs7_signed_info *sinfo;
+ struct x509_certificate *signer, *root;
+ bool verified = false;
+ int ret;
+
+ EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, dbx);
+
+ if (!regs || !msg || !db || !db->sig_data_list)
+ goto out;
+
+ for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) {
+ EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n",
+ sinfo->sig->hash_algo, sinfo->sig->pkey_algo);
+
+ /*
+ * only for authenticated variable.
+ *
+ * If this function is called for image,
+ * hash calculation will be done in
+ * pkcs7_verify_one().
+ */
+ if (!msg->data &&
+ !efi_hash_regions(regs->reg, regs->num,
+ (void **)&sinfo->sig->digest, NULL)) {
+ EFI_PRINT("Digesting an image failed\n");
+ goto out;
+ }
+
+ EFI_PRINT("Verifying certificate chain\n");
+ signer = NULL;
+ ret = pkcs7_verify_one(msg, sinfo, &signer);
+ if (ret == -ENOPKG)
+ continue;
+
+ if (ret < 0 || !signer)
+ goto out;
+
+ if (sinfo->blacklisted)
+ goto out;
+
+ EFI_PRINT("Verifying last certificate in chain\n");
+ if (signer->self_signed) {
+ if (efi_lookup_certificate(signer, db))
+ if (efi_signature_check_revocation(sinfo,
+ signer, dbx))
+ break;
+ } else if (efi_verify_certificate(signer, db, &root)) {
+ bool check;
+
+ check = efi_signature_check_revocation(sinfo, root,
+ dbx);
+ x509_free_certificate(root);
+ if (check)
+ break;
+ }
+
+ EFI_PRINT("Certificate chain didn't reach trusted CA\n");
+ }
+ if (sinfo)
+ verified = true;
+out:
+ EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified);
+ return verified;
+}
+
+/**
+ * efi_signature_check_signers - check revocation against all signers with dbx
+ * @msg: Signature
+ * @dbx: Revocation signature database
+ *
+ * Determine if none of signers' certificates in @msg are revoked
+ * by signature database pointed to by @dbx.
+ *
+ * Return: true if all signers passed, false otherwise.
+ */
+bool efi_signature_check_signers(struct pkcs7_message *msg,
+ struct efi_signature_store *dbx)
+{
+ struct pkcs7_signed_info *sinfo;
+ bool revoked = false;
+
+ EFI_PRINT("%s: Enter, %p, %p\n", __func__, msg, dbx);
+
+ if (!msg || !dbx)
+ goto out;
+
+ for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) {
+ if (sinfo->signer &&
+ !efi_signature_check_revocation(sinfo, sinfo->signer,
+ dbx)) {
+ revoked = true;
+ break;
+ }
+ }
+out:
+ EFI_PRINT("%s: Exit, revoked: %d\n", __func__, revoked);
+ return !revoked;
+}
+
+/**
+ * efi_sigstore_free - free signature store
+ * @sigstore: Pointer to signature store structure
+ *
+ * Feee all the memories held in signature store and itself,
+ * which were allocated by efi_sigstore_parse_sigdb().
+ */
+void efi_sigstore_free(struct efi_signature_store *sigstore)
+{
+ struct efi_signature_store *sigstore_next;
+ struct efi_sig_data *sig_data, *sig_data_next;
+
+ while (sigstore) {
+ sigstore_next = sigstore->next;
+
+ sig_data = sigstore->sig_data_list;
+ while (sig_data) {
+ sig_data_next = sig_data->next;
+ free(sig_data->data);
+ free(sig_data);
+ sig_data = sig_data_next;
+ }
+
+ free(sigstore);
+ sigstore = sigstore_next;
+ }
+}
+
+/**
+ * efi_sigstore_parse_siglist - parse a signature list
+ * @name: Pointer to signature list
+ *
+ * Parse signature list and instantiate a signature store structure.
+ * Signature database is a simple concatenation of one or more
+ * signature list(s).
+ *
+ * Return: Pointer to signature store on success, NULL on error
+ */
+static struct efi_signature_store *
+efi_sigstore_parse_siglist(struct efi_signature_list *esl)
+{
+ struct efi_signature_store *siglist = NULL;
+ struct efi_sig_data *sig_data, *sig_data_next;
+ struct efi_signature_data *esd;
+ size_t left;
+
+ /*
+ * UEFI specification defines certificate types:
+ * for non-signed images,
+ * EFI_CERT_SHA256_GUID
+ * EFI_CERT_RSA2048_GUID
+ * EFI_CERT_RSA2048_SHA256_GUID
+ * EFI_CERT_SHA1_GUID
+ * EFI_CERT_RSA2048_SHA_GUID
+ * EFI_CERT_SHA224_GUID
+ * EFI_CERT_SHA384_GUID
+ * EFI_CERT_SHA512_GUID
+ *
+ * for signed images,
+ * EFI_CERT_X509_GUID
+ * NOTE: Each certificate will normally be in a separate
+ * EFI_SIGNATURE_LIST as the size may vary depending on
+ * its algo's.
+ *
+ * for timestamp revocation of certificate,
+ * EFI_CERT_X509_SHA512_GUID
+ * EFI_CERT_X509_SHA256_GUID
+ * EFI_CERT_X509_SHA384_GUID
+ */
+
+ if (esl->signature_list_size
+ <= (sizeof(*esl) + esl->signature_header_size)) {
+ EFI_PRINT("Siglist in wrong format\n");
+ return NULL;
+ }
+
+ /* Create a head */
+ siglist = calloc(sizeof(*siglist), 1);
+ if (!siglist) {
+ EFI_PRINT("Out of memory\n");
+ goto err;
+ }
+ memcpy(&siglist->sig_type, &esl->signature_type, sizeof(efi_guid_t));
+
+ /* Go through the list */
+ sig_data_next = NULL;
+ left = esl->signature_list_size
+ - (sizeof(*esl) + esl->signature_header_size);
+ esd = (struct efi_signature_data *)
+ ((u8 *)esl + sizeof(*esl) + esl->signature_header_size);
+
+ while (left > 0) {
+ /* Signature must exist if there is remaining data. */
+ if (left < esl->signature_size) {
+ EFI_PRINT("Certificate is too small\n");
+ goto err;
+ }
+
+ sig_data = calloc(esl->signature_size
+ - sizeof(esd->signature_owner), 1);
+ if (!sig_data) {
+ EFI_PRINT("Out of memory\n");
+ goto err;
+ }
+
+ /* Append signature data */
+ memcpy(&sig_data->owner, &esd->signature_owner,
+ sizeof(efi_guid_t));
+ sig_data->size = esl->signature_size
+ - sizeof(esd->signature_owner);
+ sig_data->data = malloc(sig_data->size);
+ if (!sig_data->data) {
+ EFI_PRINT("Out of memory\n");
+ goto err;
+ }
+ memcpy(sig_data->data, esd->signature_data, sig_data->size);
+
+ sig_data->next = sig_data_next;
+ sig_data_next = sig_data;
+
+ /* Next */
+ esd = (struct efi_signature_data *)
+ ((u8 *)esd + esl->signature_size);
+ left -= esl->signature_size;
+ }
+ siglist->sig_data_list = sig_data_next;
+
+ return siglist;
+
+err:
+ efi_sigstore_free(siglist);
+
+ return NULL;
+}
+
+/**
+ * efi_sigstore_parse_sigdb - parse the signature list and populate
+ * the signature store
+ *
+ * @sig_list: Pointer to the signature list
+ * @size: Size of the signature list
+ *
+ * Parse the efi signature list and instantiate a signature store
+ * structure.
+ *
+ * Return: Pointer to signature store on success, NULL on error
+ */
+struct efi_signature_store *efi_build_signature_store(void *sig_list,
+ efi_uintn_t size)
+{
+ struct efi_signature_list *esl;
+ struct efi_signature_store *sigstore = NULL, *siglist;
+
+ esl = sig_list;
+ while (size > 0) {
+ /* List must exist if there is remaining data. */
+ if (size < sizeof(*esl)) {
+ EFI_PRINT("Signature list in wrong format\n");
+ goto err;
+ }
+
+ if (size < esl->signature_list_size) {
+ EFI_PRINT("Signature list in wrong format\n");
+ goto err;
+ }
+
+ /* Parse a single siglist. */
+ siglist = efi_sigstore_parse_siglist(esl);
+ if (!siglist) {
+ EFI_PRINT("Parsing of signature list of failed\n");
+ goto err;
+ }
+
+ /* Append siglist */
+ siglist->next = sigstore;
+ sigstore = siglist;
+
+ /* Next */
+ size -= esl->signature_list_size;
+ esl = (void *)esl + esl->signature_list_size;
+ }
+ free(sig_list);
+
+ return sigstore;
+
+err:
+ efi_sigstore_free(sigstore);
+ free(sig_list);
+
+ return NULL;
+}
+
+/**
+ * efi_sigstore_parse_sigdb - parse a signature database variable
+ * @name: Variable's name
+ *
+ * Read in a value of signature database variable pointed to by
+ * @name, parse it and instantiate a signature store structure.
+ *
+ * Return: Pointer to signature store on success, NULL on error
+ */
+struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)
+{
+ struct efi_signature_store *sigstore = NULL;
+ const efi_guid_t *vendor;
+ void *db;
+ efi_uintn_t db_size;
+ efi_status_t ret;
+
+ if (!u16_strcmp(name, L"PK") || !u16_strcmp(name, L"KEK")) {
+ vendor = &efi_global_variable_guid;
+ } else if (!u16_strcmp(name, L"db") || !u16_strcmp(name, L"dbx")) {
+ vendor = &efi_guid_image_security_database;
+ } else {
+ EFI_PRINT("unknown signature database, %ls\n", name);
+ return NULL;
+ }
+
+ /* retrieve variable data */
+ db_size = 0;
+ ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, NULL));
+ if (ret == EFI_NOT_FOUND) {
+ EFI_PRINT("variable, %ls, not found\n", name);
+ sigstore = calloc(sizeof(*sigstore), 1);
+ return sigstore;
+ } else if (ret != EFI_BUFFER_TOO_SMALL) {
+ EFI_PRINT("Getting variable, %ls, failed\n", name);
+ return NULL;
+ }
+
+ db = malloc(db_size);
+ if (!db) {
+ EFI_PRINT("Out of memory\n");
+ return NULL;
+ }
+
+ ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, db));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("Getting variable, %ls, failed\n", name);
+ free(db);
+ return NULL;
+ }
+
+ return efi_build_signature_store(db, db_size);
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_smbios.c b/roms/u-boot/lib/efi_loader/efi_smbios.c
new file mode 100644
index 000000000..719d3e888
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_smbios.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application tables support
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <mapmem.h>
+#include <smbios.h>
+
+static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
+
+/*
+ * Install the SMBIOS table as a configuration table.
+ *
+ * @return status code
+ */
+efi_status_t efi_smbios_register(void)
+{
+ /* Map within the low 32 bits, to allow for 32bit SMBIOS tables */
+ u64 dmi_addr = U32_MAX;
+ efi_status_t ret;
+ void *dmi;
+
+ /* Reserve 4kiB page for SMBIOS */
+ ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
+ EFI_RUNTIME_SERVICES_DATA, 1, &dmi_addr);
+
+ if (ret != EFI_SUCCESS) {
+ /* Could not find space in lowmem, use highmem instead */
+ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_RUNTIME_SERVICES_DATA, 1,
+ &dmi_addr);
+
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+
+ /*
+ * Generate SMBIOS tables - we know that efi_allocate_pages() returns
+ * a 4k-aligned address, so it is safe to assume that
+ * write_smbios_table() will write the table at that address.
+ *
+ * Note that on sandbox, efi_allocate_pages() unfortunately returns a
+ * pointer even though it uses a uint64_t type. Convert it.
+ */
+ assert(!(dmi_addr & 0xf));
+ dmi = (void *)(uintptr_t)dmi_addr;
+ write_smbios_table(map_to_sysmem(dmi));
+
+ /* And expose them to our EFI payload */
+ return efi_install_configuration_table(&smbios_guid, dmi);
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_string.c b/roms/u-boot/lib/efi_loader/efi_string.c
new file mode 100644
index 000000000..a3b8edf5a
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_string.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * String functions
+ *
+ * Copyright (c) 2020 AKASHI Takahiro, Linaro Limited
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <efi_loader.h>
+
+/**
+ * efi_create_indexed_name - create a string name with an index
+ * @buffer: Buffer
+ * @name: Name string
+ * @index: Index
+ *
+ * Create a utf-16 string with @name, appending @index.
+ * For example, L"Capsule0001"
+ *
+ * The caller must ensure that the buffer has enough space for the resulting
+ * string including the trailing L'\0'.
+ *
+ * Return: A pointer to the next position after the created string
+ * in @buffer, or NULL otherwise
+ */
+u16 *efi_create_indexed_name(u16 *buffer, size_t buffer_size, const char *name,
+ unsigned int index)
+{
+ u16 *p = buffer;
+ char index_buf[5];
+ size_t size;
+
+ size = (utf8_utf16_strlen(name) * sizeof(u16) +
+ sizeof(index_buf) * sizeof(u16));
+ if (buffer_size < size)
+ return NULL;
+ utf8_utf16_strcpy(&p, name);
+ snprintf(index_buf, sizeof(index_buf), "%04X", index);
+ utf8_utf16_strcpy(&p, index_buf);
+
+ return p;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_tcg2.c b/roms/u-boot/lib/efi_loader/efi_tcg2.c
new file mode 100644
index 000000000..1319a8b37
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_tcg2.c
@@ -0,0 +1,1345 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Defines APIs that allow an OS to interact with UEFI firmware to query
+ * information about the device.
+ * https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/
+ *
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+#include <common.h>
+#include <dm.h>
+#include <efi_loader.h>
+#include <efi_tcg2.h>
+#include <log.h>
+#include <malloc.h>
+#include <version.h>
+#include <tpm-v2.h>
+#include <u-boot/hash-checksum.h>
+#include <u-boot/sha1.h>
+#include <u-boot/sha256.h>
+#include <u-boot/sha512.h>
+#include <linux/unaligned/access_ok.h>
+#include <linux/unaligned/generic.h>
+#include <hexdump.h>
+
+struct event_log_buffer {
+ void *buffer;
+ void *final_buffer;
+ size_t pos; /* eventlog position */
+ size_t final_pos; /* final events config table position */
+ size_t last_event_size;
+ bool get_event_called;
+ bool truncated;
+};
+
+static struct event_log_buffer event_log;
+/*
+ * When requesting TPM2_CAP_TPM_PROPERTIES the value is on a standard offset.
+ * Since the current tpm2_get_capability() response buffers starts at
+ * 'union tpmu_capabilities data' of 'struct tpms_capability_data', calculate
+ * the response size and offset once for all consumers
+ */
+#define TPM2_RESPONSE_BUFFER_SIZE (sizeof(struct tpms_capability_data) - \
+ offsetof(struct tpms_capability_data, data))
+#define properties_offset (offsetof(struct tpml_tagged_tpm_property, tpm_property) + \
+ offsetof(struct tpms_tagged_property, value))
+
+static const efi_guid_t efi_guid_tcg2_protocol = EFI_TCG2_PROTOCOL_GUID;
+static const efi_guid_t efi_guid_final_events = EFI_TCG2_FINAL_EVENTS_TABLE_GUID;
+
+struct digest_info {
+ u16 hash_alg;
+ u32 hash_mask;
+ u16 hash_len;
+};
+
+static const struct digest_info hash_algo_list[] = {
+ {
+ TPM2_ALG_SHA1,
+ EFI_TCG2_BOOT_HASH_ALG_SHA1,
+ TPM2_SHA1_DIGEST_SIZE,
+ },
+ {
+ TPM2_ALG_SHA256,
+ EFI_TCG2_BOOT_HASH_ALG_SHA256,
+ TPM2_SHA256_DIGEST_SIZE,
+ },
+ {
+ TPM2_ALG_SHA384,
+ EFI_TCG2_BOOT_HASH_ALG_SHA384,
+ TPM2_SHA384_DIGEST_SIZE,
+ },
+ {
+ TPM2_ALG_SHA512,
+ EFI_TCG2_BOOT_HASH_ALG_SHA512,
+ TPM2_SHA512_DIGEST_SIZE,
+ },
+};
+
+#define MAX_HASH_COUNT ARRAY_SIZE(hash_algo_list)
+
+/**
+ * alg_to_mask - Get a TCG hash mask for algorithms
+ *
+ * @hash_alg: TCG defined algorithm
+ *
+ * @Return: TCG hashing algorithm bitmaps, 0 if the algorithm is not supported
+ */
+static u32 alg_to_mask(u16 hash_alg)
+{
+ size_t i;
+
+ for (i = 0; i < MAX_HASH_COUNT; i++) {
+ if (hash_algo_list[i].hash_alg == hash_alg)
+ return hash_algo_list[i].hash_mask;
+ }
+
+ return 0;
+}
+
+/**
+ * alg_to_len - Get a TCG hash len for algorithms
+ *
+ * @hash_alg: TCG defined algorithm
+ *
+ * @Return: len of chosen algorithm, 0 if the algorithm is not supported
+ */
+static u16 alg_to_len(u16 hash_alg)
+{
+ size_t i;
+
+ for (i = 0; i < MAX_HASH_COUNT; i++) {
+ if (hash_algo_list[i].hash_alg == hash_alg)
+ return hash_algo_list[i].hash_len;
+ }
+
+ return 0;
+}
+
+static u32 tcg_event_final_size(struct tpml_digest_values *digest_list)
+{
+ u32 len;
+ size_t i;
+
+ len = offsetof(struct tcg_pcr_event2, digests);
+ len += offsetof(struct tpml_digest_values, digests);
+ for (i = 0; i < digest_list->count; i++) {
+ u16 hash_alg = digest_list->digests[i].hash_alg;
+
+ len += offsetof(struct tpmt_ha, digest);
+ len += alg_to_len(hash_alg);
+ }
+ len += sizeof(u32); /* tcg_pcr_event2 event_size*/
+
+ return len;
+}
+
+/* tcg2_pcr_extend - Extend PCRs for a TPM2 device for a given tpml_digest_values
+ *
+ * @dev: device
+ * @digest_list: list of digest algorithms to extend
+ *
+ * @Return: status code
+ */
+static efi_status_t tcg2_pcr_extend(struct udevice *dev, u32 pcr_index,
+ struct tpml_digest_values *digest_list)
+{
+ u32 rc;
+ size_t i;
+
+ for (i = 0; i < digest_list->count; i++) {
+ u32 alg = digest_list->digests[i].hash_alg;
+
+ rc = tpm2_pcr_extend(dev, pcr_index, alg,
+ (u8 *)&digest_list->digests[i].digest,
+ alg_to_len(alg));
+ if (rc) {
+ EFI_PRINT("Failed to extend PCR\n");
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/* tcg2_agile_log_append - Append an agile event to out eventlog
+ *
+ * @pcr_index: PCR index
+ * @event_type: type of event added
+ * @digest_list: list of digest algorithms to add
+ * @size: size of event
+ * @event: event to add
+ *
+ * @Return: status code
+ */
+static efi_status_t tcg2_agile_log_append(u32 pcr_index, u32 event_type,
+ struct tpml_digest_values *digest_list,
+ u32 size, u8 event[])
+{
+ void *log = (void *)((uintptr_t)event_log.buffer + event_log.pos);
+ size_t pos;
+ size_t i;
+ u32 event_size;
+
+ if (event_log.get_event_called)
+ log = (void *)((uintptr_t)event_log.final_buffer +
+ event_log.final_pos);
+
+ /*
+ * size refers to the length of event[] only, we need to check against
+ * the final tcg_pcr_event2 size
+ */
+ event_size = size + tcg_event_final_size(digest_list);
+ if (event_log.pos + event_size > TPM2_EVENT_LOG_SIZE ||
+ event_log.final_pos + event_size > TPM2_EVENT_LOG_SIZE) {
+ event_log.truncated = true;
+ return EFI_VOLUME_FULL;
+ }
+
+ put_unaligned_le32(pcr_index, log);
+ pos = offsetof(struct tcg_pcr_event2, event_type);
+ put_unaligned_le32(event_type, (void *)((uintptr_t)log + pos));
+ pos = offsetof(struct tcg_pcr_event2, digests); /* count */
+ put_unaligned_le32(digest_list->count, (void *)((uintptr_t)log + pos));
+
+ pos += offsetof(struct tpml_digest_values, digests);
+ for (i = 0; i < digest_list->count; i++) {
+ u16 hash_alg = digest_list->digests[i].hash_alg;
+ u8 *digest = (u8 *)&digest_list->digests[i].digest;
+
+ put_unaligned_le16(hash_alg, (void *)((uintptr_t)log + pos));
+ pos += offsetof(struct tpmt_ha, digest);
+ memcpy((void *)((uintptr_t)log + pos), digest, alg_to_len(hash_alg));
+ pos += alg_to_len(hash_alg);
+ }
+
+ put_unaligned_le32(size, (void *)((uintptr_t)log + pos));
+ pos += sizeof(u32); /* tcg_pcr_event2 event_size*/
+ memcpy((void *)((uintptr_t)log + pos), event, size);
+ pos += size;
+
+ /* make sure the calculated buffer is what we checked against */
+ if (pos != event_size)
+ return EFI_INVALID_PARAMETER;
+
+ /* if GetEventLog hasn't been called update the normal log */
+ if (!event_log.get_event_called) {
+ event_log.pos += pos;
+ event_log.last_event_size = pos;
+ } else {
+ /* if GetEventLog has been called update config table log */
+ struct efi_tcg2_final_events_table *final_event;
+
+ final_event =
+ (struct efi_tcg2_final_events_table *)(event_log.final_buffer);
+ final_event->number_of_events++;
+ event_log.final_pos += pos;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * platform_get_tpm_device() - retrieve TPM device
+ *
+ * This function retrieves the udevice implementing a TPM
+ *
+ * This function may be overridden if special initialization is needed.
+ *
+ * @dev: udevice
+ * Return: status code
+ */
+__weak efi_status_t platform_get_tpm2_device(struct udevice **dev)
+{
+ for_each_tpm_device(*dev) {
+ /* Only support TPMv2 devices */
+ if (tpm_get_version(*dev) == TPM_V2)
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ * tpm2_get_max_command_size() - get the supported max command size
+ *
+ * @dev: TPM device
+ * @max_command_size: output buffer for the size
+ *
+ * Return: 0 on success, -1 on error
+ */
+static int tpm2_get_max_command_size(struct udevice *dev, u16 *max_command_size)
+{
+ u8 response[TPM2_RESPONSE_BUFFER_SIZE];
+ u32 ret;
+
+ memset(response, 0, sizeof(response));
+ ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+ TPM2_PT_MAX_COMMAND_SIZE, response, 1);
+ if (ret)
+ return -1;
+
+ *max_command_size = (uint16_t)get_unaligned_be32(response +
+ properties_offset);
+
+ return 0;
+}
+
+/**
+ * tpm2_get_max_response_size() - get the supported max response size
+ *
+ * @dev: TPM device
+ * @max_response_size: output buffer for the size
+ *
+ * Return: 0 on success, -1 on error
+ */
+static int tpm2_get_max_response_size(struct udevice *dev,
+ u16 *max_response_size)
+{
+ u8 response[TPM2_RESPONSE_BUFFER_SIZE];
+ u32 ret;
+
+ memset(response, 0, sizeof(response));
+ ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+ TPM2_PT_MAX_RESPONSE_SIZE, response, 1);
+ if (ret)
+ return -1;
+
+ *max_response_size = (uint16_t)get_unaligned_be32(response +
+ properties_offset);
+
+ return 0;
+}
+
+/**
+ * tpm2_get_manufacturer_id() - get the manufacturer ID
+ *
+ * @dev: TPM device
+ * @manufacturer_id: output buffer for the id
+ *
+ * Return: 0 on success, -1 on error
+ */
+static int tpm2_get_manufacturer_id(struct udevice *dev, u32 *manufacturer_id)
+{
+ u8 response[TPM2_RESPONSE_BUFFER_SIZE];
+ u32 ret;
+
+ memset(response, 0, sizeof(response));
+ ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+ TPM2_PT_MANUFACTURER, response, 1);
+ if (ret)
+ return -1;
+
+ *manufacturer_id = get_unaligned_be32(response + properties_offset);
+
+ return 0;
+}
+
+/**
+ * tpm2_get_num_pcr() - get the number of PCRs
+ *
+ * @dev: TPM device
+ * @manufacturer_id: output buffer for the number
+ *
+ * Return: 0 on success, -1 on error
+ */
+static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
+{
+ u8 response[TPM2_RESPONSE_BUFFER_SIZE];
+ u32 ret;
+
+ memset(response, 0, sizeof(response));
+ ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+ TPM2_PT_PCR_COUNT, response, 1);
+ if (ret)
+ return -1;
+
+ *num_pcr = get_unaligned_be32(response + properties_offset);
+ if (*num_pcr > TPM2_MAX_PCRS)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * is_active_pcr() - Check if a supported algorithm is active
+ *
+ * @dev: TPM device
+ * @selection: struct of PCR information
+ *
+ * Return: true if PCR is active
+ */
+static bool is_active_pcr(struct tpms_pcr_selection *selection)
+{
+ int i;
+ /*
+ * check the pcr_select. If at least one of the PCRs supports the
+ * algorithm add it on the active ones
+ */
+ for (i = 0; i < selection->size_of_select; i++) {
+ if (selection->pcr_select[i])
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * tpm2_get_pcr_info() - get the supported, active PCRs and number of banks
+ *
+ * @dev: TPM device
+ * @supported_pcr: bitmask with the algorithms supported
+ * @active_pcr: bitmask with the active algorithms
+ * @pcr_banks: number of PCR banks
+ *
+ * Return: 0 on success, -1 on error
+ */
+static int tpm2_get_pcr_info(struct udevice *dev, u32 *supported_pcr,
+ u32 *active_pcr, u32 *pcr_banks)
+{
+ u8 response[TPM2_RESPONSE_BUFFER_SIZE];
+ struct tpml_pcr_selection pcrs;
+ u32 ret, num_pcr;
+ size_t i;
+ int tpm_ret;
+
+ *supported_pcr = 0;
+ *active_pcr = 0;
+ *pcr_banks = 0;
+ memset(response, 0, sizeof(response));
+ ret = tpm2_get_capability(dev, TPM2_CAP_PCRS, 0, response, 1);
+ if (ret)
+ goto out;
+
+ pcrs.count = get_unaligned_be32(response);
+ /*
+ * We only support 5 algorithms for now so check against that
+ * instead of TPM2_NUM_PCR_BANKS
+ */
+ if (pcrs.count > MAX_HASH_COUNT || pcrs.count < 1)
+ goto out;
+
+ tpm_ret = tpm2_get_num_pcr(dev, &num_pcr);
+ if (tpm_ret)
+ goto out;
+
+ for (i = 0; i < pcrs.count; i++) {
+ /*
+ * Definition of TPMS_PCR_SELECTION Structure
+ * hash: u16
+ * size_of_select: u8
+ * pcr_select: u8 array
+ *
+ * The offsets depend on the number of the device PCRs
+ * so we have to calculate them based on that
+ */
+ u32 hash_offset = offsetof(struct tpml_pcr_selection, selection) +
+ i * offsetof(struct tpms_pcr_selection, pcr_select) +
+ i * ((num_pcr + 7) / 8);
+ u32 size_select_offset =
+ hash_offset + offsetof(struct tpms_pcr_selection,
+ size_of_select);
+ u32 pcr_select_offset =
+ hash_offset + offsetof(struct tpms_pcr_selection,
+ pcr_select);
+
+ pcrs.selection[i].hash =
+ get_unaligned_be16(response + hash_offset);
+ pcrs.selection[i].size_of_select =
+ __get_unaligned_be(response + size_select_offset);
+ if (pcrs.selection[i].size_of_select > TPM2_PCR_SELECT_MAX)
+ goto out;
+ /* copy the array of pcr_select */
+ memcpy(pcrs.selection[i].pcr_select, response + pcr_select_offset,
+ pcrs.selection[i].size_of_select);
+ }
+
+ for (i = 0; i < pcrs.count; i++) {
+ u32 hash_mask = alg_to_mask(pcrs.selection[i].hash);
+
+ if (hash_mask) {
+ *supported_pcr |= hash_mask;
+ if (is_active_pcr(&pcrs.selection[i]))
+ *active_pcr |= hash_mask;
+ } else {
+ EFI_PRINT("Unknown algorithm %x\n", pcrs.selection[i].hash);
+ }
+ }
+
+ *pcr_banks = pcrs.count;
+
+ return 0;
+out:
+ return -1;
+}
+
+/**
+ * __get_active_pcr_banks() - returns the currently active PCR banks
+ *
+ * @active_pcr_banks: pointer for receiving the bitmap of currently
+ * active PCR banks
+ *
+ * Return: status code
+ */
+static efi_status_t __get_active_pcr_banks(u32 *active_pcr_banks)
+{
+ struct udevice *dev;
+ u32 active = 0, supported = 0, pcr_banks = 0;
+ efi_status_t ret;
+ int err;
+
+ ret = platform_get_tpm2_device(&dev);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ err = tpm2_get_pcr_info(dev, &supported, &active, &pcr_banks);
+ if (err) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ *active_pcr_banks = active;
+
+out:
+ return ret;
+}
+
+/* tcg2_create_digest - create a list of digests of the supported PCR banks
+ * for a given memory range
+ *
+ * @input: input memory
+ * @length: length of buffer to calculate the digest
+ * @digest_list: list of digests to fill in
+ *
+ * Return: status code
+ */
+static efi_status_t tcg2_create_digest(const u8 *input, u32 length,
+ struct tpml_digest_values *digest_list)
+{
+ sha1_context ctx;
+ sha256_context ctx_256;
+ sha512_context ctx_512;
+ u8 final[TPM2_SHA512_DIGEST_SIZE];
+ efi_status_t ret;
+ u32 active;
+ size_t i;
+
+ ret = __get_active_pcr_banks(&active);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ digest_list->count = 0;
+ for (i = 0; i < MAX_HASH_COUNT; i++) {
+ u16 hash_alg = hash_algo_list[i].hash_alg;
+
+ if (!(active & alg_to_mask(hash_alg)))
+ continue;
+ switch (hash_alg) {
+ case TPM2_ALG_SHA1:
+ sha1_starts(&ctx);
+ sha1_update(&ctx, input, length);
+ sha1_finish(&ctx, final);
+ break;
+ case TPM2_ALG_SHA256:
+ sha256_starts(&ctx_256);
+ sha256_update(&ctx_256, input, length);
+ sha256_finish(&ctx_256, final);
+ break;
+ case TPM2_ALG_SHA384:
+ sha384_starts(&ctx_512);
+ sha384_update(&ctx_512, input, length);
+ sha384_finish(&ctx_512, final);
+ break;
+ case TPM2_ALG_SHA512:
+ sha512_starts(&ctx_512);
+ sha512_update(&ctx_512, input, length);
+ sha512_finish(&ctx_512, final);
+ break;
+ default:
+ EFI_PRINT("Unsupported algorithm %x\n", hash_alg);
+ return EFI_INVALID_PARAMETER;
+ }
+ digest_list->count++;
+ digest_list->digests[i].hash_alg = hash_alg;
+ memcpy(&digest_list->digests[i].digest, final, (u32)alg_to_len(hash_alg));
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_tcg2_get_capability() - protocol capability information and state information
+ *
+ * @this: TCG2 protocol instance
+ * @capability: caller allocated memory with size field to the size of
+ * the structure allocated
+
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_tcg2_get_capability(struct efi_tcg2_protocol *this,
+ struct efi_tcg2_boot_service_capability *capability)
+{
+ struct udevice *dev;
+ efi_status_t efi_ret;
+ int ret;
+
+ EFI_ENTRY("%p, %p", this, capability);
+
+ if (!this || !capability) {
+ efi_ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (capability->size < boot_service_capability_min) {
+ capability->size = boot_service_capability_min;
+ efi_ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
+
+ if (capability->size < sizeof(*capability)) {
+ capability->size = sizeof(*capability);
+ efi_ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
+
+ capability->structure_version.major = 1;
+ capability->structure_version.minor = 1;
+ capability->protocol_version.major = 1;
+ capability->protocol_version.minor = 1;
+
+ efi_ret = platform_get_tpm2_device(&dev);
+ if (efi_ret != EFI_SUCCESS) {
+ capability->supported_event_logs = 0;
+ capability->hash_algorithm_bitmap = 0;
+ capability->tpm_present_flag = false;
+ capability->max_command_size = 0;
+ capability->max_response_size = 0;
+ capability->manufacturer_id = 0;
+ capability->number_of_pcr_banks = 0;
+ capability->active_pcr_banks = 0;
+
+ efi_ret = EFI_SUCCESS;
+ goto out;
+ }
+
+ /* We only allow a TPMv2 device to register the EFI protocol */
+ capability->supported_event_logs = TCG2_EVENT_LOG_FORMAT_TCG_2;
+
+ capability->tpm_present_flag = true;
+
+ /* Supported and active PCRs */
+ capability->hash_algorithm_bitmap = 0;
+ capability->active_pcr_banks = 0;
+ ret = tpm2_get_pcr_info(dev, &capability->hash_algorithm_bitmap,
+ &capability->active_pcr_banks,
+ &capability->number_of_pcr_banks);
+ if (ret) {
+ efi_ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ /* Max command size */
+ ret = tpm2_get_max_command_size(dev, &capability->max_command_size);
+ if (ret) {
+ efi_ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ /* Max response size */
+ ret = tpm2_get_max_response_size(dev, &capability->max_response_size);
+ if (ret) {
+ efi_ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ /* Manufacturer ID */
+ ret = tpm2_get_manufacturer_id(dev, &capability->manufacturer_id);
+ if (ret) {
+ efi_ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ return EFI_EXIT(EFI_SUCCESS);
+out:
+ return EFI_EXIT(efi_ret);
+}
+
+/**
+ * efi_tcg2_get_eventlog() - retrieve the the address of an event log and its
+ * last entry
+ *
+ * @this: TCG2 protocol instance
+ * @log_format: type of event log format
+ * @event_log_location: pointer to the memory address of the event log
+ * @event_log_last_entry: pointer to the address of the start of the last
+ * entry in the event log in memory, if log contains
+ * more than 1 entry
+ * @event_log_truncated: set to true, if the Event Log is missing at i
+ * least one entry
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_tcg2_get_eventlog(struct efi_tcg2_protocol *this,
+ efi_tcg_event_log_format log_format,
+ u64 *event_log_location, u64 *event_log_last_entry,
+ bool *event_log_truncated)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct udevice *dev;
+
+ EFI_ENTRY("%p, %u, %p, %p, %p", this, log_format, event_log_location,
+ event_log_last_entry, event_log_truncated);
+
+ ret = platform_get_tpm2_device(&dev);
+ if (ret != EFI_SUCCESS) {
+ event_log_location = NULL;
+ event_log_last_entry = NULL;
+ *event_log_truncated = false;
+ ret = EFI_SUCCESS;
+ goto out;
+ }
+ *event_log_location = (uintptr_t)event_log.buffer;
+ *event_log_last_entry = (uintptr_t)(event_log.buffer + event_log.pos -
+ event_log.last_event_size);
+ *event_log_truncated = event_log.truncated;
+ event_log.get_event_called = true;
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * tcg2_hash_pe_image() - calculate PE/COFF image hash
+ *
+ * @efi: pointer to the EFI binary
+ * @efi_size: size of @efi binary
+ * @digest_list: list of digest algorithms to extend
+ *
+ * Return: status code
+ */
+static efi_status_t tcg2_hash_pe_image(void *efi, u64 efi_size,
+ struct tpml_digest_values *digest_list)
+{
+ WIN_CERTIFICATE *wincerts = NULL;
+ size_t wincerts_len;
+ struct efi_image_regions *regs = NULL;
+ void *new_efi = NULL;
+ u8 hash[TPM2_SHA512_DIGEST_SIZE];
+ efi_status_t ret;
+ u32 active;
+ int i;
+
+ new_efi = efi_prepare_aligned_image(efi, &efi_size);
+ if (!new_efi)
+ return EFI_OUT_OF_RESOURCES;
+
+ if (!efi_image_parse(new_efi, efi_size, &regs, &wincerts,
+ &wincerts_len)) {
+ log_err("Parsing PE executable image failed\n");
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ ret = __get_active_pcr_banks(&active);
+ if (ret != EFI_SUCCESS) {
+ goto out;
+ }
+
+ digest_list->count = 0;
+ for (i = 0; i < MAX_HASH_COUNT; i++) {
+ u16 hash_alg = hash_algo_list[i].hash_alg;
+
+ if (!(active & alg_to_mask(hash_alg)))
+ continue;
+ switch (hash_alg) {
+ case TPM2_ALG_SHA1:
+ hash_calculate("sha1", regs->reg, regs->num, hash);
+ break;
+ case TPM2_ALG_SHA256:
+ hash_calculate("sha256", regs->reg, regs->num, hash);
+ break;
+ case TPM2_ALG_SHA384:
+ hash_calculate("sha384", regs->reg, regs->num, hash);
+ break;
+ case TPM2_ALG_SHA512:
+ hash_calculate("sha512", regs->reg, regs->num, hash);
+ break;
+ default:
+ EFI_PRINT("Unsupported algorithm %x\n", hash_alg);
+ return EFI_INVALID_PARAMETER;
+ }
+ digest_list->digests[i].hash_alg = hash_alg;
+ memcpy(&digest_list->digests[i].digest, hash, (u32)alg_to_len(hash_alg));
+ digest_list->count++;
+ }
+
+out:
+ if (new_efi != efi)
+ free(new_efi);
+ free(regs);
+
+ return ret;
+}
+
+/**
+ * tcg2_measure_pe_image() - measure PE/COFF image
+ *
+ * @efi: pointer to the EFI binary
+ * @efi_size: size of @efi binary
+ * @handle: loaded image handle
+ * @loaded_image: loaded image protocol
+ *
+ * Return: status code
+ */
+efi_status_t tcg2_measure_pe_image(void *efi, u64 efi_size,
+ struct efi_loaded_image_obj *handle,
+ struct efi_loaded_image *loaded_image)
+{
+ struct tpml_digest_values digest_list;
+ efi_status_t ret;
+ struct udevice *dev;
+ u32 pcr_index, event_type, event_size;
+ struct uefi_image_load_event *image_load_event;
+ struct efi_device_path *device_path;
+ u32 device_path_length;
+ IMAGE_DOS_HEADER *dos;
+ IMAGE_NT_HEADERS32 *nt;
+ struct efi_handler *handler;
+
+ ret = platform_get_tpm2_device(&dev);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ switch (handle->image_type) {
+ case IMAGE_SUBSYSTEM_EFI_APPLICATION:
+ pcr_index = 4;
+ event_type = EV_EFI_BOOT_SERVICES_APPLICATION;
+ break;
+ case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
+ pcr_index = 2;
+ event_type = EV_EFI_BOOT_SERVICES_DRIVER;
+ break;
+ case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
+ pcr_index = 2;
+ event_type = EV_EFI_RUNTIME_SERVICES_DRIVER;
+ break;
+ default:
+ return EFI_UNSUPPORTED;
+ }
+
+ ret = tcg2_hash_pe_image(efi, efi_size, &digest_list);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = tcg2_pcr_extend(dev, pcr_index, &digest_list);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = EFI_CALL(efi_search_protocol(&handle->header,
+ &efi_guid_loaded_image_device_path,
+ &handler));
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ device_path = EFI_CALL(handler->protocol_interface);
+ device_path_length = efi_dp_size(device_path);
+ if (device_path_length > 0) {
+ /* add end node size */
+ device_path_length += sizeof(struct efi_device_path);
+ }
+ event_size = sizeof(struct uefi_image_load_event) + device_path_length;
+ image_load_event = (struct uefi_image_load_event *)malloc(event_size);
+ if (!image_load_event)
+ return EFI_OUT_OF_RESOURCES;
+
+ image_load_event->image_location_in_memory = (uintptr_t)efi;
+ image_load_event->image_length_in_memory = efi_size;
+ image_load_event->length_of_device_path = device_path_length;
+
+ dos = (IMAGE_DOS_HEADER *)efi;
+ nt = (IMAGE_NT_HEADERS32 *)(efi + dos->e_lfanew);
+ if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ IMAGE_NT_HEADERS64 *nt64 = (IMAGE_NT_HEADERS64 *)nt;
+
+ image_load_event->image_link_time_address =
+ nt64->OptionalHeader.ImageBase;
+ } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ image_load_event->image_link_time_address =
+ nt->OptionalHeader.ImageBase;
+ } else {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (device_path_length > 0) {
+ memcpy(image_load_event->device_path, device_path,
+ device_path_length);
+ }
+
+ ret = tcg2_agile_log_append(pcr_index, event_type, &digest_list,
+ event_size, (u8 *)image_load_event);
+
+out:
+ free(image_load_event);
+
+ return ret;
+}
+
+/**
+ * efi_tcg2_hash_log_extend_event() - extend and optionally log events
+ *
+ * @this: TCG2 protocol instance
+ * @flags: bitmap providing additional information on the
+ * operation
+ * @data_to_hash: physical address of the start of the data buffer
+ * to be hashed
+ * @data_to_hash_len: the length in bytes of the buffer referenced by
+ * data_to_hash
+ * @efi_tcg_event: pointer to data buffer containing information
+ * about the event
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_tcg2_hash_log_extend_event(struct efi_tcg2_protocol *this, u64 flags,
+ u64 data_to_hash, u64 data_to_hash_len,
+ struct efi_tcg2_event *efi_tcg_event)
+{
+ struct udevice *dev;
+ efi_status_t ret;
+ u32 event_type, pcr_index, event_size;
+ struct tpml_digest_values digest_list;
+
+ EFI_ENTRY("%p, %llu, %llu, %llu, %p", this, flags, data_to_hash,
+ data_to_hash_len, efi_tcg_event);
+
+ if (!this || !data_to_hash || !efi_tcg_event) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = platform_get_tpm2_device(&dev);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (efi_tcg_event->size < efi_tcg_event->header.header_size +
+ sizeof(u32)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (efi_tcg_event->header.pcr_index > TPM2_MAX_PCRS) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /*
+ * if PE_COFF_IMAGE is set we need to make sure the image is not
+ * corrupted, verify it and hash the PE/COFF image in accordance with
+ * the procedure specified in "Calculating the PE Image Hash"
+ * section of the "Windows Authenticode Portable Executable Signature
+ * Format"
+ */
+ if (flags & PE_COFF_IMAGE) {
+ IMAGE_NT_HEADERS32 *nt;
+
+ ret = efi_check_pe((void *)(uintptr_t)data_to_hash,
+ data_to_hash_len, (void **)&nt);
+ if (ret != EFI_SUCCESS) {
+ log_err("Not a valid PE-COFF file\n");
+ goto out;
+ }
+ ret = tcg2_hash_pe_image((void *)(uintptr_t)data_to_hash,
+ data_to_hash_len, &digest_list);
+ } else {
+ ret = tcg2_create_digest((u8 *)(uintptr_t)data_to_hash,
+ data_to_hash_len, &digest_list);
+ }
+
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ pcr_index = efi_tcg_event->header.pcr_index;
+ event_type = efi_tcg_event->header.event_type;
+
+ ret = tcg2_pcr_extend(dev, pcr_index, &digest_list);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (flags & EFI_TCG2_EXTEND_ONLY) {
+ if (event_log.truncated)
+ ret = EFI_VOLUME_FULL;
+ goto out;
+ }
+
+ /*
+ * The efi_tcg_event size includes the size component and the
+ * headersize
+ */
+ event_size = efi_tcg_event->size - sizeof(efi_tcg_event->size) -
+ efi_tcg_event->header.header_size;
+ ret = tcg2_agile_log_append(pcr_index, event_type, &digest_list,
+ event_size, efi_tcg_event->event);
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_tcg2_submit_command() - Send command to the TPM
+ *
+ * @this: TCG2 protocol instance
+ * @input_param_block_size: size of the TPM input parameter block
+ * @input_param_block: pointer to the TPM input parameter block
+ * @output_param_block_size: size of the TPM output parameter block
+ * @output_param_block: pointer to the TPM output parameter block
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_tcg2_submit_command(__maybe_unused struct efi_tcg2_protocol *this,
+ u32 __maybe_unused input_param_block_size,
+ u8 __maybe_unused *input_param_block,
+ u32 __maybe_unused output_param_block_size,
+ u8 __maybe_unused *output_param_block)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_tcg2_get_active_pcr_banks() - returns the currently active PCR banks
+ *
+ * @this: TCG2 protocol instance
+ * @active_pcr_banks: pointer for receiving the bitmap of currently
+ * active PCR banks
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_tcg2_get_active_pcr_banks(struct efi_tcg2_protocol *this,
+ u32 *active_pcr_banks)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p", this, active_pcr_banks);
+ ret = __get_active_pcr_banks(active_pcr_banks);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_tcg2_set_active_pcr_banks() - sets the currently active PCR banks
+ *
+ * @this: TCG2 protocol instance
+ * @active_pcr_banks: bitmap of the requested active PCR banks
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_tcg2_set_active_pcr_banks(__maybe_unused struct efi_tcg2_protocol *this,
+ u32 __maybe_unused active_pcr_banks)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_tcg2_get_result_of_set_active_pcr_banks() - retrieve result for previous
+ * set_active_pcr_banks()
+ *
+ * @this: TCG2 protocol instance
+ * @operation_present: non-zero value to indicate a
+ * set_active_pcr_banks operation was
+ * invoked during last boot
+ * @response: result value could be returned
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_tcg2_get_result_of_set_active_pcr_banks(__maybe_unused struct efi_tcg2_protocol *this,
+ u32 __maybe_unused *operation_present,
+ u32 __maybe_unused *response)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static const struct efi_tcg2_protocol efi_tcg2_protocol = {
+ .get_capability = efi_tcg2_get_capability,
+ .get_eventlog = efi_tcg2_get_eventlog,
+ .hash_log_extend_event = efi_tcg2_hash_log_extend_event,
+ .submit_command = efi_tcg2_submit_command,
+ .get_active_pcr_banks = efi_tcg2_get_active_pcr_banks,
+ .set_active_pcr_banks = efi_tcg2_set_active_pcr_banks,
+ .get_result_of_set_active_pcr_banks = efi_tcg2_get_result_of_set_active_pcr_banks,
+};
+
+/**
+ * create_specid_event() - Create the first event in the eventlog
+ *
+ * @dev: tpm device
+ * @event_header: Pointer to the final event header
+ * @event_size: final spec event size
+ *
+ * Return: status code
+ */
+static efi_status_t create_specid_event(struct udevice *dev, void *buffer,
+ size_t *event_size)
+{
+ struct tcg_efi_spec_id_event *spec_event;
+ size_t spec_event_size;
+ efi_status_t ret = EFI_DEVICE_ERROR;
+ u32 active = 0, supported = 0;
+ int err;
+ size_t i;
+
+ /*
+ * Create Spec event. This needs to be the first event in the log
+ * according to the TCG EFI protocol spec
+ */
+
+ /* Setup specID event data */
+ spec_event = (struct tcg_efi_spec_id_event *)buffer;
+ memcpy(spec_event->signature, TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03,
+ sizeof(spec_event->signature));
+ put_unaligned_le32(0, &spec_event->platform_class); /* type client */
+ spec_event->spec_version_minor =
+ TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2;
+ spec_event->spec_version_major =
+ TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2;
+ spec_event->spec_errata =
+ TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_ERRATA_TPM2;
+ spec_event->uintn_size = sizeof(efi_uintn_t) / sizeof(u32);
+
+ err = tpm2_get_pcr_info(dev, &supported, &active,
+ &spec_event->number_of_algorithms);
+ if (err)
+ goto out;
+ if (spec_event->number_of_algorithms > MAX_HASH_COUNT ||
+ spec_event->number_of_algorithms < 1)
+ goto out;
+
+ for (i = 0; i < spec_event->number_of_algorithms; i++) {
+ u16 hash_alg = hash_algo_list[i].hash_alg;
+ u16 hash_len = hash_algo_list[i].hash_len;
+
+ if (active && alg_to_mask(hash_alg)) {
+ put_unaligned_le16(hash_alg,
+ &spec_event->digest_sizes[i].algorithm_id);
+ put_unaligned_le16(hash_len,
+ &spec_event->digest_sizes[i].digest_size);
+ }
+ }
+ /*
+ * the size of the spec event and placement of vendor_info_size
+ * depends on supported algoriths
+ */
+ spec_event_size =
+ offsetof(struct tcg_efi_spec_id_event, digest_sizes) +
+ spec_event->number_of_algorithms * sizeof(spec_event->digest_sizes[0]);
+ /* no vendor info for us */
+ memset(buffer + spec_event_size, 0,
+ sizeof(spec_event->vendor_info_size));
+ spec_event_size += sizeof(spec_event->vendor_info_size);
+ *event_size = spec_event_size;
+
+ return EFI_SUCCESS;
+
+out:
+ return ret;
+}
+
+/**
+ * tcg2_uninit - remove the final event table and free efi memory on failures
+ */
+void tcg2_uninit(void)
+{
+ efi_status_t ret;
+
+ ret = efi_install_configuration_table(&efi_guid_final_events, NULL);
+ if (ret != EFI_SUCCESS)
+ log_err("Failed to delete final events config table\n");
+
+ efi_free_pool(event_log.buffer);
+ event_log.buffer = NULL;
+ efi_free_pool(event_log.final_buffer);
+ event_log.final_buffer = NULL;
+}
+
+/**
+ * create_final_event() - Create the final event and install the config
+ * defined by the TCG EFI spec
+ */
+static efi_status_t create_final_event(void)
+{
+ struct efi_tcg2_final_events_table *final_event;
+ efi_status_t ret;
+
+ /*
+ * All events generated after the invocation of
+ * EFI_TCG2_GET_EVENT_LOGS need to be stored in an instance of an
+ * EFI_CONFIGURATION_TABLE
+ */
+ ret = efi_allocate_pool(EFI_ACPI_MEMORY_NVS, TPM2_EVENT_LOG_SIZE,
+ &event_log.final_buffer);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ memset(event_log.final_buffer, 0xff, TPM2_EVENT_LOG_SIZE);
+ final_event = event_log.final_buffer;
+ final_event->number_of_events = 0;
+ final_event->version = EFI_TCG2_FINAL_EVENTS_TABLE_VERSION;
+ event_log.final_pos = sizeof(*final_event);
+ ret = efi_install_configuration_table(&efi_guid_final_events,
+ final_event);
+ if (ret != EFI_SUCCESS) {
+ efi_free_pool(event_log.final_buffer);
+ event_log.final_buffer = NULL;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * efi_init_event_log() - initialize an eventlog
+ */
+static efi_status_t efi_init_event_log(void)
+{
+ /*
+ * vendor_info_size is currently set to 0, we need to change the length
+ * and allocate the flexible array member if this changes
+ */
+ struct tcg_pcr_event *event_header = NULL;
+ struct udevice *dev;
+ size_t spec_event_size;
+ efi_status_t ret;
+
+ ret = platform_get_tpm2_device(&dev);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, TPM2_EVENT_LOG_SIZE,
+ (void **)&event_log.buffer);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /*
+ * initialize log area as 0xff so the OS can easily figure out the
+ * last log entry
+ */
+ memset(event_log.buffer, 0xff, TPM2_EVENT_LOG_SIZE);
+ event_log.pos = 0;
+ event_log.last_event_size = 0;
+ event_log.get_event_called = false;
+ event_log.truncated = false;
+
+ /*
+ * The log header is defined to be in SHA1 event log entry format.
+ * Setup event header
+ */
+ event_header = (struct tcg_pcr_event *)event_log.buffer;
+ put_unaligned_le32(0, &event_header->pcr_index);
+ put_unaligned_le32(EV_NO_ACTION, &event_header->event_type);
+ memset(&event_header->digest, 0, sizeof(event_header->digest));
+ ret = create_specid_event(dev, (void *)((uintptr_t)event_log.buffer + sizeof(*event_header)),
+ &spec_event_size);
+ if (ret != EFI_SUCCESS)
+ goto free_pool;
+ put_unaligned_le32(spec_event_size, &event_header->event_size);
+ event_log.pos = spec_event_size + sizeof(*event_header);
+ event_log.last_event_size = event_log.pos;
+
+ ret = create_final_event();
+ if (ret != EFI_SUCCESS)
+ goto free_pool;
+
+out:
+ return ret;
+
+free_pool:
+ efi_free_pool(event_log.buffer);
+ event_log.buffer = NULL;
+ return ret;
+}
+
+/**
+ * efi_append_scrtm_version - Append an S-CRTM EV_S_CRTM_VERSION event on the
+ * eventlog and extend the PCRs
+ *
+ * @dev: TPM device
+ *
+ * @Return: status code
+ */
+static efi_status_t efi_append_scrtm_version(struct udevice *dev)
+{
+ struct tpml_digest_values digest_list;
+ u8 ver[] = U_BOOT_VERSION_STRING;
+ const int pcr_index = 0;
+ efi_status_t ret;
+
+ ret = tcg2_create_digest(ver, sizeof(ver), &digest_list);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = tcg2_pcr_extend(dev, pcr_index, &digest_list);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = tcg2_agile_log_append(pcr_index, EV_S_CRTM_VERSION, &digest_list,
+ sizeof(ver), ver);
+
+out:
+ return ret;
+}
+
+/**
+ * efi_tcg2_register() - register EFI_TCG2_PROTOCOL
+ *
+ * If a TPM2 device is available, the TPM TCG2 Protocol is registered
+ *
+ * Return: An error status is only returned if adding the protocol fails.
+ */
+efi_status_t efi_tcg2_register(void)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct udevice *dev;
+
+ ret = platform_get_tpm2_device(&dev);
+ if (ret != EFI_SUCCESS) {
+ log_warning("Unable to find TPMv2 device\n");
+ return EFI_SUCCESS;
+ }
+
+ ret = efi_init_event_log();
+ if (ret != EFI_SUCCESS)
+ goto fail;
+
+ ret = efi_append_scrtm_version(dev);
+ if (ret != EFI_SUCCESS) {
+ tcg2_uninit();
+ goto fail;
+ }
+
+ ret = efi_add_protocol(efi_root, &efi_guid_tcg2_protocol,
+ (void *)&efi_tcg2_protocol);
+ if (ret != EFI_SUCCESS) {
+ tcg2_uninit();
+ goto fail;
+ }
+ return ret;
+
+fail:
+ log_err("Cannot install EFI_TCG2_PROTOCOL\n");
+ /*
+ * Return EFI_SUCCESS and don't stop the EFI subsystem.
+ * That's done for 2 reasons
+ * - If the protocol is not installed the PCRs won't be extended. So
+ * someone later in the boot flow will notice that and take the
+ * necessary actions.
+ * - The TPM sandbox is limited and we won't be able to run any efi
+ * related tests with TCG2 enabled
+ */
+ return EFI_SUCCESS;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_unicode_collation.c b/roms/u-boot/lib/efi_loader/efi_unicode_collation.c
new file mode 100644
index 000000000..36be798f6
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_unicode_collation.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Unicode collation protocol
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <cp1250.h>
+#include <cp437.h>
+#include <efi_loader.h>
+
+/* Characters that may not be used in FAT 8.3 file names */
+static const char illegal[] = "+,<=>:;\"/\\|?*[]\x7f";
+
+/*
+ * EDK2 assumes codepage 1250 when creating FAT 8.3 file names.
+ * Linux defaults to codepage 437 for FAT 8.3 file names.
+ */
+#if CONFIG_FAT_DEFAULT_CODEPAGE == 1250
+/* Unicode code points for code page 1250 characters 0x80 - 0xff */
+static const u16 codepage[] = CP1250;
+#else
+/* Unicode code points for code page 437 characters 0x80 - 0xff */
+static const u16 *codepage = codepage_437;
+#endif
+
+/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL2 */
+const efi_guid_t efi_guid_unicode_collation_protocol2 =
+ EFI_UNICODE_COLLATION_PROTOCOL2_GUID;
+
+/**
+ * efi_stri_coll() - compare utf-16 strings case-insenitively
+ *
+ * @this: unicode collation protocol instance
+ * @s1: first string
+ * @s2: second string
+ *
+ * This function implements the StriColl() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: 0: s1 == s2, > 0: s1 > s2, < 0: s1 < s2
+ */
+static efi_intn_t EFIAPI efi_stri_coll(
+ struct efi_unicode_collation_protocol *this, u16 *s1, u16 *s2)
+{
+ s32 c1, c2;
+ efi_intn_t ret = 0;
+
+ EFI_ENTRY("%p, %ls, %ls", this, s1, s2);
+ for (; *s1 | *s2; ++s1, ++s2) {
+ c1 = utf_to_upper(*s1);
+ c2 = utf_to_upper(*s2);
+ if (c1 < c2) {
+ ret = -1;
+ goto out;
+ } else if (c1 > c2) {
+ ret = 1;
+ goto out;
+ }
+ }
+out:
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+/**
+ * next_lower() - get next codepoint converted to lower case
+ *
+ * @string: pointer to u16 string, on return advanced by one codepoint
+ * Return: first codepoint of string converted to lower case
+ */
+static s32 next_lower(const u16 **string)
+{
+ return utf_to_lower(utf16_get(string));
+}
+
+/**
+ * metai_match() - compare utf-16 string with a pattern string case-insenitively
+ *
+ * @string: string to compare
+ * @pattern: pattern string
+ *
+ * The pattern string may use these:
+ * - * matches >= 0 characters
+ * - ? matches 1 character
+ * - [<char1><char2>...<charN>] match any character in the set
+ * - [<char1>-<char2>] matches any character in the range
+ *
+ * This function is called my efi_metai_match().
+ *
+ * For '*' pattern searches this function calls itself recursively.
+ * Performance-wise this is suboptimal, especially for multiple '*' wildcards.
+ * But it results in simple code.
+ *
+ * Return: true if the string is matched.
+ */
+static bool metai_match(const u16 *string, const u16 *pattern)
+{
+ s32 first, s, p;
+
+ for (; *string && *pattern;) {
+ const u16 *string_old = string;
+
+ s = next_lower(&string);
+ p = next_lower(&pattern);
+
+ switch (p) {
+ case '*':
+ /* Match 0 or more characters */
+ for (;; s = next_lower(&string)) {
+ if (metai_match(string_old, pattern))
+ return true;
+ if (!s)
+ return false;
+ string_old = string;
+ }
+ case '?':
+ /* Match any one character */
+ break;
+ case '[':
+ /* Match any character in the set */
+ p = next_lower(&pattern);
+ first = p;
+ if (first == ']')
+ /* Empty set */
+ return false;
+ p = next_lower(&pattern);
+ if (p == '-') {
+ /* Range */
+ p = next_lower(&pattern);
+ if (s < first || s > p)
+ return false;
+ p = next_lower(&pattern);
+ if (p != ']')
+ return false;
+ } else {
+ /* Set */
+ bool hit = false;
+
+ if (s == first)
+ hit = true;
+ for (; p && p != ']';
+ p = next_lower(&pattern)) {
+ if (p == s)
+ hit = true;
+ }
+ if (!hit || p != ']')
+ return false;
+ }
+ break;
+ default:
+ /* Match one character */
+ if (p != s)
+ return false;
+ }
+ }
+ if (!*pattern && !*string)
+ return true;
+ return false;
+}
+
+/**
+ * efi_metai_match() - compare utf-16 string with a pattern string
+ * case-insenitively
+ *
+ * @this: unicode collation protocol instance
+ * @string: string to compare
+ * @pattern: pattern string
+ *
+ * The pattern string may use these:
+ * - * matches >= 0 characters
+ * - ? matches 1 character
+ * - [<char1><char2>...<charN>] match any character in the set
+ * - [<char1>-<char2>] matches any character in the range
+ *
+ * This function implements the MetaMatch() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ *
+ * Return: true if the string is matched.
+ */
+static bool EFIAPI efi_metai_match(struct efi_unicode_collation_protocol *this,
+ const u16 *string, const u16 *pattern)
+{
+ bool ret;
+
+ EFI_ENTRY("%p, %ls, %ls", this, string, pattern);
+ ret = metai_match(string, pattern);
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+/**
+ * efi_str_lwr() - convert to lower case
+ *
+ * @this: unicode collation protocol instance
+ * @string: string to convert
+ *
+ * The conversion is done in place. As long as upper and lower letters use the
+ * same number of words this does not pose a problem.
+ *
+ * This function implements the StrLwr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ */
+static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this,
+ u16 *string)
+{
+ EFI_ENTRY("%p, %ls", this, string);
+ for (; *string; ++string)
+ *string = utf_to_lower(*string);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_str_upr() - convert to upper case
+ *
+ * @this: unicode collation protocol instance
+ * @string: string to convert
+ *
+ * The conversion is done in place. As long as upper and lower letters use the
+ * same number of words this does not pose a problem.
+ *
+ * This function implements the StrUpr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ */
+static void EFIAPI efi_str_upr(struct efi_unicode_collation_protocol *this,
+ u16 *string)
+{
+ EFI_ENTRY("%p, %ls", this, string);
+ for (; *string; ++string)
+ *string = utf_to_upper(*string);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_fat_to_str() - convert an 8.3 file name from an OEM codepage to Unicode
+ *
+ * @this: unicode collation protocol instance
+ * @fat_size: size of the string to convert
+ * @fat: string to convert
+ * @string: converted string
+ *
+ * This function implements the FatToStr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ */
+static void EFIAPI efi_fat_to_str(struct efi_unicode_collation_protocol *this,
+ efi_uintn_t fat_size, char *fat, u16 *string)
+{
+ efi_uintn_t i;
+ u16 c;
+
+ EFI_ENTRY("%p, %zu, %s, %p", this, fat_size, fat, string);
+ for (i = 0; i < fat_size; ++i) {
+ c = (unsigned char)fat[i];
+ if (c > 0x80)
+ c = codepage[i - 0x80];
+ string[i] = c;
+ if (!c)
+ break;
+ }
+ string[i] = 0;
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_fat_to_str() - convert a utf-16 string to legal characters for a FAT
+ * file name in an OEM code page
+ *
+ * @this: unicode collation protocol instance
+ * @string: Unicode string to convert
+ * @fat_size: size of the target buffer
+ * @fat: converted string
+ *
+ * This function implements the StrToFat() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ *
+ * Return: true if an illegal character was substituted by '_'.
+ */
+static bool EFIAPI efi_str_to_fat(struct efi_unicode_collation_protocol *this,
+ const u16 *string, efi_uintn_t fat_size,
+ char *fat)
+{
+ efi_uintn_t i;
+ s32 c;
+ bool ret = false;
+
+ EFI_ENTRY("%p, %ls, %zu, %p", this, string, fat_size, fat);
+ for (i = 0; i < fat_size;) {
+ c = utf16_get(&string);
+ switch (c) {
+ /* Ignore period and space */
+ case '.':
+ case ' ':
+ continue;
+ case 0:
+ break;
+ }
+ c = utf_to_upper(c);
+ if (utf_to_cp(&c, codepage) ||
+ (c && (c < 0x20 || strchr(illegal, c)))) {
+ ret = true;
+ c = '_';
+ }
+
+ fat[i] = c;
+ if (!c)
+ break;
+ ++i;
+ }
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+const struct efi_unicode_collation_protocol efi_unicode_collation_protocol2 = {
+ .stri_coll = efi_stri_coll,
+ .metai_match = efi_metai_match,
+ .str_lwr = efi_str_lwr,
+ .str_upr = efi_str_upr,
+ .fat_to_str = efi_fat_to_str,
+ .str_to_fat = efi_str_to_fat,
+ .supported_languages = "en",
+};
diff --git a/roms/u-boot/lib/efi_loader/efi_var_common.c b/roms/u-boot/lib/efi_loader/efi_var_common.c
new file mode 100644
index 000000000..3d92afe2e
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_var_common.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * UEFI runtime variable services
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ * Copyright (c) 2020 Linaro Limited, Author: AKASHI Takahiro
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <stdlib.h>
+
+enum efi_secure_mode {
+ EFI_MODE_SETUP,
+ EFI_MODE_USER,
+ EFI_MODE_AUDIT,
+ EFI_MODE_DEPLOYED,
+};
+
+struct efi_auth_var_name_type {
+ const u16 *name;
+ const efi_guid_t *guid;
+ const enum efi_auth_var_type type;
+};
+
+const efi_guid_t efi_guid_image_security_database =
+ EFI_IMAGE_SECURITY_DATABASE_GUID;
+
+static const struct efi_auth_var_name_type name_type[] = {
+ {u"PK", &efi_global_variable_guid, EFI_AUTH_VAR_PK},
+ {u"KEK", &efi_global_variable_guid, EFI_AUTH_VAR_KEK},
+ {u"db", &efi_guid_image_security_database, EFI_AUTH_VAR_DB},
+ {u"dbx", &efi_guid_image_security_database, EFI_AUTH_VAR_DBX},
+ /* not used yet
+ {u"dbt", &efi_guid_image_security_database, EFI_AUTH_VAR_DBT},
+ {u"dbr", &efi_guid_image_security_database, EFI_AUTH_VAR_DBR},
+ */
+};
+
+static bool efi_secure_boot;
+static enum efi_secure_mode efi_secure_mode;
+
+/**
+ * efi_efi_get_variable() - retrieve value of a UEFI variable
+ *
+ * This function implements the GetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer to which the variable value is copied
+ * @data: buffer to which the variable value is copied
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
+ const efi_guid_t *vendor, u32 *attributes,
+ efi_uintn_t *data_size, void *data)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ ret = efi_get_variable_int(variable_name, vendor, attributes,
+ data_size, data, NULL);
+
+ /* Remove EFI_VARIABLE_READ_ONLY flag */
+ if (attributes)
+ *attributes &= EFI_VARIABLE_MASK;
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_set_variable() - set value of a UEFI variable
+ *
+ * This function implements the SetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
+ const efi_guid_t *vendor, u32 attributes,
+ efi_uintn_t data_size, const void *data)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ /* Make sure that the EFI_VARIABLE_READ_ONLY flag is not set */
+ if (attributes & ~(u32)EFI_VARIABLE_MASK)
+ ret = EFI_INVALID_PARAMETER;
+ else
+ ret = efi_set_variable_int(variable_name, vendor, attributes,
+ data_size, data, true);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_get_next_variable_name() - enumerate the current variable names
+ *
+ * @variable_name_size: size of variable_name buffer in byte
+ * @variable_name: name of uefi variable's name in u16
+ * @vendor: vendor's guid
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
+ u16 *variable_name,
+ efi_guid_t *vendor)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
+
+ ret = efi_get_next_variable_name_int(variable_name_size, variable_name,
+ vendor);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_query_variable_info() - get information about EFI variables
+ *
+ * This function implements the QueryVariableInfo() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @attributes: bitmask to select variables to be
+ * queried
+ * @maximum_variable_storage_size: maximum size of storage area for the
+ * selected variable types
+ * @remaining_variable_storage_size: remaining size of storage are for the
+ * selected variable types
+ * @maximum_variable_size: maximum size of a variable of the
+ * selected type
+ * Returns: status code
+ */
+efi_status_t EFIAPI efi_query_variable_info(
+ u32 attributes, u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%x %p %p %p", attributes, maximum_variable_storage_size,
+ remaining_variable_storage_size, maximum_variable_size);
+
+ if (!maximum_variable_storage_size ||
+ !remaining_variable_storage_size ||
+ !maximum_variable_size ||
+ !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if ((attributes & ~(u32)EFI_VARIABLE_MASK) ||
+ (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) ||
+ (attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) ||
+ (!IS_ENABLED(CONFIG_EFI_SECURE_BOOT) &&
+ (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)))
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ ret = efi_query_variable_info_int(attributes,
+ maximum_variable_storage_size,
+ remaining_variable_storage_size,
+ maximum_variable_size);
+
+ return EFI_EXIT(ret);
+}
+
+efi_status_t __efi_runtime EFIAPI
+efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
+ u32 *attributes, efi_uintn_t *data_size, void *data)
+{
+ efi_status_t ret;
+
+ ret = efi_get_variable_mem(variable_name, guid, attributes, data_size, data, NULL);
+
+ /* Remove EFI_VARIABLE_READ_ONLY flag */
+ if (attributes)
+ *attributes &= EFI_VARIABLE_MASK;
+
+ return ret;
+}
+
+efi_status_t __efi_runtime EFIAPI
+efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
+ u16 *variable_name, efi_guid_t *guid)
+{
+ return efi_get_next_variable_name_mem(variable_name_size, variable_name, guid);
+}
+
+/**
+ * efi_set_secure_state - modify secure boot state variables
+ * @secure_boot: value of SecureBoot
+ * @setup_mode: value of SetupMode
+ * @audit_mode: value of AuditMode
+ * @deployed_mode: value of DeployedMode
+ *
+ * Modify secure boot status related variables as indicated.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_set_secure_state(u8 secure_boot, u8 setup_mode,
+ u8 audit_mode, u8 deployed_mode)
+{
+ efi_status_t ret;
+ const u32 attributes_ro = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY;
+ const u32 attributes_rw = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+
+ efi_secure_boot = secure_boot;
+
+ ret = efi_set_variable_int(L"SecureBoot", &efi_global_variable_guid,
+ attributes_ro, sizeof(secure_boot),
+ &secure_boot, false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = efi_set_variable_int(L"SetupMode", &efi_global_variable_guid,
+ attributes_ro, sizeof(setup_mode),
+ &setup_mode, false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = efi_set_variable_int(L"AuditMode", &efi_global_variable_guid,
+ audit_mode || setup_mode ?
+ attributes_ro : attributes_rw,
+ sizeof(audit_mode), &audit_mode, false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = efi_set_variable_int(L"DeployedMode",
+ &efi_global_variable_guid,
+ audit_mode || deployed_mode || setup_mode ?
+ attributes_ro : attributes_rw,
+ sizeof(deployed_mode), &deployed_mode,
+ false);
+err:
+ return ret;
+}
+
+/**
+ * efi_transfer_secure_state - handle a secure boot state transition
+ * @mode: new state
+ *
+ * Depending on @mode, secure boot related variables are updated.
+ * Those variables are *read-only* for users, efi_set_variable_int()
+ * is called here.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_transfer_secure_state(enum efi_secure_mode mode)
+{
+ efi_status_t ret;
+
+ EFI_PRINT("Switching secure state from %d to %d\n", efi_secure_mode,
+ mode);
+
+ if (mode == EFI_MODE_DEPLOYED) {
+ ret = efi_set_secure_state(1, 0, 0, 1);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else if (mode == EFI_MODE_AUDIT) {
+ ret = efi_set_variable_int(L"PK", &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ 0, NULL, false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = efi_set_secure_state(0, 1, 1, 0);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else if (mode == EFI_MODE_USER) {
+ ret = efi_set_secure_state(1, 0, 0, 0);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else if (mode == EFI_MODE_SETUP) {
+ ret = efi_set_secure_state(0, 1, 0, 0);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ efi_secure_mode = mode;
+
+ return EFI_SUCCESS;
+
+err:
+ /* TODO: What action should be taken here? */
+ printf("ERROR: Secure state transition failed\n");
+ return ret;
+}
+
+efi_status_t efi_init_secure_state(void)
+{
+ enum efi_secure_mode mode = EFI_MODE_SETUP;
+ u8 efi_vendor_keys = 0;
+ efi_uintn_t size = 0;
+ efi_status_t ret;
+
+ ret = efi_get_variable_int(L"PK", &efi_global_variable_guid,
+ NULL, &size, NULL, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT))
+ mode = EFI_MODE_USER;
+ }
+
+ ret = efi_transfer_secure_state(mode);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* As we do not provide vendor keys this variable is always 0. */
+ ret = efi_set_variable_int(L"VendorKeys",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ sizeof(efi_vendor_keys),
+ &efi_vendor_keys, false);
+ return ret;
+}
+
+/**
+ * efi_secure_boot_enabled - return if secure boot is enabled or not
+ *
+ * Return: true if enabled, false if disabled
+ */
+bool efi_secure_boot_enabled(void)
+{
+ return efi_secure_boot;
+}
+
+enum efi_auth_var_type efi_auth_var_get_type(u16 *name, const efi_guid_t *guid)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(name_type); ++i) {
+ if (!u16_strcmp(name, name_type[i].name) &&
+ !guidcmp(guid, name_type[i].guid))
+ return name_type[i].type;
+ }
+ return EFI_AUTH_VAR_NONE;
+}
+
+/**
+ * efi_get_var() - read value of an EFI variable
+ *
+ * @name: variable name
+ * @start: vendor GUID
+ * @size: size of allocated buffer
+ *
+ * Return: buffer with variable data or NULL
+ */
+void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size)
+{
+ efi_status_t ret;
+ void *buf = NULL;
+
+ *size = 0;
+ ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ buf = malloc(*size);
+ if (!buf)
+ return NULL;
+ ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+ }
+
+ if (ret != EFI_SUCCESS) {
+ free(buf);
+ *size = 0;
+ return NULL;
+ }
+
+ return buf;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_var_file.c b/roms/u-boot/lib/efi_loader/efi_var_file.c
new file mode 100644
index 000000000..de076b8cb
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_var_file.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * File interface for UEFI variables
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <charset.h>
+#include <fs.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <u-boot/crc.h>
+
+#define PART_STR_LEN 10
+
+/**
+ * efi_set_blk_dev_to_system_partition() - select EFI system partition
+ *
+ * Set the EFI system partition as current block device.
+ *
+ * Return: status code
+ */
+static efi_status_t __maybe_unused efi_set_blk_dev_to_system_partition(void)
+{
+ char part_str[PART_STR_LEN];
+ int r;
+
+ if (!efi_system_partition.if_type) {
+ log_err("No EFI system partition\n");
+ return EFI_DEVICE_ERROR;
+ }
+ snprintf(part_str, PART_STR_LEN, "%x:%x",
+ efi_system_partition.devnum, efi_system_partition.part);
+ r = fs_set_blk_dev(blk_get_if_type_name(efi_system_partition.if_type),
+ part_str, FS_TYPE_ANY);
+ if (r) {
+ log_err("Cannot read EFI system partition\n");
+ return EFI_DEVICE_ERROR;
+ }
+ return EFI_SUCCESS;
+}
+
+efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp, loff_t *lenp,
+ u32 check_attr_mask)
+{
+ size_t len = EFI_VAR_BUF_SIZE;
+ struct efi_var_file *buf;
+ struct efi_var_entry *var, *old_var;
+ size_t old_var_name_length = 2;
+
+ *bufp = NULL; /* Avoid double free() */
+ buf = calloc(1, len);
+ if (!buf)
+ return EFI_OUT_OF_RESOURCES;
+ var = buf->var;
+ old_var = var;
+ for (;;) {
+ efi_uintn_t data_length, var_name_length;
+ u8 *data;
+ efi_status_t ret;
+
+ if ((uintptr_t)buf + len <=
+ (uintptr_t)var->name + old_var_name_length)
+ return EFI_BUFFER_TOO_SMALL;
+
+ var_name_length = (uintptr_t)buf + len - (uintptr_t)var->name;
+ memcpy(var->name, old_var->name, old_var_name_length);
+ guidcpy(&var->guid, &old_var->guid);
+ ret = efi_get_next_variable_name_int(
+ &var_name_length, var->name, &var->guid);
+ if (ret == EFI_NOT_FOUND)
+ break;
+ if (ret != EFI_SUCCESS) {
+ free(buf);
+ return ret;
+ }
+ old_var_name_length = var_name_length;
+ old_var = var;
+
+ data = (u8 *)var->name + old_var_name_length;
+ data_length = (uintptr_t)buf + len - (uintptr_t)data;
+ ret = efi_get_variable_int(var->name, &var->guid,
+ &var->attr, &data_length, data,
+ &var->time);
+ if (ret != EFI_SUCCESS) {
+ free(buf);
+ return ret;
+ }
+ if ((var->attr & check_attr_mask) == check_attr_mask) {
+ var->length = data_length;
+ var = (struct efi_var_entry *)ALIGN((uintptr_t)data + data_length, 8);
+ }
+ }
+
+ buf->reserved = 0;
+ buf->magic = EFI_VAR_FILE_MAGIC;
+ len = (uintptr_t)var - (uintptr_t)buf;
+ buf->crc32 = crc32(0, (u8 *)buf->var,
+ len - sizeof(struct efi_var_file));
+ buf->length = len;
+ *bufp = buf;
+ *lenp = len;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_var_to_file() - save non-volatile variables as file
+ *
+ * File ubootefi.var is created on the EFI system partion.
+ *
+ * Return: status code
+ */
+efi_status_t efi_var_to_file(void)
+{
+#ifdef CONFIG_EFI_VARIABLE_FILE_STORE
+ efi_status_t ret;
+ struct efi_var_file *buf;
+ loff_t len;
+ loff_t actlen;
+ int r;
+
+ ret = efi_var_collect(&buf, &len, EFI_VARIABLE_NON_VOLATILE);
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ ret = efi_set_blk_dev_to_system_partition();
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ r = fs_write(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, len, &actlen);
+ if (r || len != actlen)
+ ret = EFI_DEVICE_ERROR;
+
+error:
+ if (ret != EFI_SUCCESS)
+ log_err("Failed to persist EFI variables\n");
+ free(buf);
+ return ret;
+#else
+ return EFI_SUCCESS;
+#endif
+}
+
+efi_status_t efi_var_restore(struct efi_var_file *buf)
+{
+ struct efi_var_entry *var, *last_var;
+ efi_status_t ret;
+
+ if (buf->reserved || buf->magic != EFI_VAR_FILE_MAGIC ||
+ buf->crc32 != crc32(0, (u8 *)buf->var,
+ buf->length - sizeof(struct efi_var_file))) {
+ log_err("Invalid EFI variables file\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ var = buf->var;
+ last_var = (struct efi_var_entry *)((u8 *)buf + buf->length);
+ while (var < last_var) {
+ u16 *data = var->name + u16_strlen(var->name) + 1;
+
+ if (var->attr & EFI_VARIABLE_NON_VOLATILE && var->length) {
+ ret = efi_var_mem_ins(var->name, &var->guid, var->attr,
+ var->length, data, 0, NULL,
+ var->time);
+ if (ret != EFI_SUCCESS)
+ log_err("Failed to set EFI variable %ls\n",
+ var->name);
+ }
+ var = (struct efi_var_entry *)
+ ALIGN((uintptr_t)data + var->length, 8);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_var_from_file() - read variables from file
+ *
+ * File ubootefi.var is read from the EFI system partitions and the variables
+ * stored in the file are created.
+ *
+ * In case the file does not exist yet or a variable cannot be set EFI_SUCCESS
+ * is returned.
+ *
+ * Return: status code
+ */
+efi_status_t efi_var_from_file(void)
+{
+#ifdef CONFIG_EFI_VARIABLE_FILE_STORE
+ struct efi_var_file *buf;
+ loff_t len;
+ efi_status_t ret;
+ int r;
+
+ buf = calloc(1, EFI_VAR_BUF_SIZE);
+ if (!buf) {
+ log_err("Out of memory\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ret = efi_set_blk_dev_to_system_partition();
+ if (ret != EFI_SUCCESS)
+ goto error;
+ r = fs_read(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, EFI_VAR_BUF_SIZE,
+ &len);
+ if (r || len < sizeof(struct efi_var_file)) {
+ log_err("Failed to load EFI variables\n");
+ goto error;
+ }
+ if (buf->length != len || efi_var_restore(buf) != EFI_SUCCESS)
+ log_err("Invalid EFI variables file\n");
+error:
+ free(buf);
+#endif
+ return EFI_SUCCESS;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_var_mem.c b/roms/u-boot/lib/efi_loader/efi_var_mem.c
new file mode 100644
index 000000000..3d335a827
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_var_mem.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * File interface for UEFI variables
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <u-boot/crc.h>
+
+/*
+ * The variables efi_var_file and efi_var_entry must be static to avoid
+ * referencing them via the global offset table (section .got). The GOT
+ * is neither mapped as EfiRuntimeServicesData nor do we support its
+ * relocation during SetVirtualAddressMap().
+ */
+static struct efi_var_file __efi_runtime_data *efi_var_buf;
+static struct efi_var_entry __efi_runtime_data *efi_current_var;
+
+/**
+ * efi_var_mem_compare() - compare GUID and name with a variable
+ *
+ * @var: variable to compare
+ * @guid: GUID to compare
+ * @name: variable name to compare
+ * @next: pointer to next variable
+ * Return: true if match
+ */
+static bool __efi_runtime
+efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
+ const u16 *name, struct efi_var_entry **next)
+{
+ int i;
+ u8 *guid1, *guid2;
+ const u16 *data, *var_name;
+ bool match = true;
+
+ for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
+ i < sizeof(efi_guid_t) && match; ++i)
+ match = (guid1[i] == guid2[i]);
+
+ for (data = var->name, var_name = name;; ++data, ++var_name) {
+ if (match)
+ match = (*data == *var_name);
+ if (!*data)
+ break;
+ }
+
+ ++data;
+
+ if (next)
+ *next = (struct efi_var_entry *)
+ ALIGN((uintptr_t)data + var->length, 8);
+
+ if (match)
+ efi_current_var = var;
+
+ return match;
+}
+
+struct efi_var_entry __efi_runtime
+*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
+ struct efi_var_entry **next)
+{
+ struct efi_var_entry *var, *last;
+
+ last = (struct efi_var_entry *)
+ ((uintptr_t)efi_var_buf + efi_var_buf->length);
+
+ if (!*name) {
+ if (next) {
+ *next = efi_var_buf->var;
+ if (*next >= last)
+ *next = NULL;
+ }
+ return NULL;
+ }
+ if (efi_current_var &&
+ efi_var_mem_compare(efi_current_var, guid, name, next)) {
+ if (next && *next >= last)
+ *next = NULL;
+ return efi_current_var;
+ }
+
+ var = efi_var_buf->var;
+ if (var < last) {
+ for (; var;) {
+ struct efi_var_entry *pos;
+ bool match;
+
+ match = efi_var_mem_compare(var, guid, name, &pos);
+ if (pos >= last)
+ pos = NULL;
+ if (match) {
+ if (next)
+ *next = pos;
+ return var;
+ }
+ var = pos;
+ }
+ }
+ if (next)
+ *next = NULL;
+ return NULL;
+}
+
+void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
+{
+ u16 *data;
+ struct efi_var_entry *next, *last;
+
+ if (!var)
+ return;
+
+ last = (struct efi_var_entry *)
+ ((uintptr_t)efi_var_buf + efi_var_buf->length);
+ if (var <= efi_current_var)
+ efi_current_var = NULL;
+
+ for (data = var->name; *data; ++data)
+ ;
+ ++data;
+ next = (struct efi_var_entry *)
+ ALIGN((uintptr_t)data + var->length, 8);
+ efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
+
+ /* efi_memcpy_runtime() can be used because next >= var. */
+ efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
+ efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
+ efi_var_buf->length -
+ sizeof(struct efi_var_file));
+}
+
+efi_status_t __efi_runtime efi_var_mem_ins(
+ u16 *variable_name,
+ const efi_guid_t *vendor, u32 attributes,
+ const efi_uintn_t size1, const void *data1,
+ const efi_uintn_t size2, const void *data2,
+ const u64 time)
+{
+ u16 *data;
+ struct efi_var_entry *var;
+ u32 var_name_len;
+
+ var = (struct efi_var_entry *)
+ ((uintptr_t)efi_var_buf + efi_var_buf->length);
+ for (var_name_len = 0; variable_name[var_name_len]; ++var_name_len)
+ ;
+ ++var_name_len;
+ data = var->name + var_name_len;
+
+ if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
+ EFI_VAR_BUF_SIZE)
+ return EFI_OUT_OF_RESOURCES;
+
+ var->attr = attributes;
+ var->length = size1 + size2;
+ var->time = time;
+
+ efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
+ efi_memcpy_runtime(var->name, variable_name,
+ sizeof(u16) * var_name_len);
+ efi_memcpy_runtime(data, data1, size1);
+ efi_memcpy_runtime((u8 *)data + size1, data2, size2);
+
+ var = (struct efi_var_entry *)
+ ALIGN((uintptr_t)data + var->length, 8);
+ efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
+ efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
+ efi_var_buf->length -
+ sizeof(struct efi_var_file));
+
+ return EFI_SUCCESS;
+}
+
+u64 __efi_runtime efi_var_mem_free(void)
+{
+ return EFI_VAR_BUF_SIZE - efi_var_buf->length -
+ sizeof(struct efi_var_entry);
+}
+
+/**
+ * efi_var_mem_bs_del() - delete boot service only variables
+ */
+static void efi_var_mem_bs_del(void)
+{
+ struct efi_var_entry *var = efi_var_buf->var;
+
+ for (;;) {
+ struct efi_var_entry *last;
+
+ last = (struct efi_var_entry *)
+ ((uintptr_t)efi_var_buf + efi_var_buf->length);
+ if (var >= last)
+ break;
+ if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
+ u16 *data;
+
+ /* skip variable */
+ for (data = var->name; *data; ++data)
+ ;
+ ++data;
+ var = (struct efi_var_entry *)
+ ALIGN((uintptr_t)data + var->length, 8);
+ } else {
+ /* delete variable */
+ efi_var_mem_del(var);
+ }
+ }
+}
+
+/**
+ * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
+ *
+ * @event: callback event
+ * @context: callback context
+ */
+static void EFIAPI
+efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+
+ /* Delete boot service only variables */
+ efi_var_mem_bs_del();
+
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
+ *
+ * @event: callback event
+ * @context: callback context
+ */
+static void EFIAPI __efi_runtime
+efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
+{
+ efi_convert_pointer(0, (void **)&efi_var_buf);
+ efi_current_var = NULL;
+}
+
+efi_status_t efi_var_mem_init(void)
+{
+ u64 memory;
+ efi_status_t ret;
+ struct efi_event *event;
+
+ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_RUNTIME_SERVICES_DATA,
+ efi_size_in_pages(EFI_VAR_BUF_SIZE),
+ &memory);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
+ memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
+ efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
+ efi_var_buf->length = (uintptr_t)efi_var_buf->var -
+ (uintptr_t)efi_var_buf;
+ /* crc32 for 0 bytes = 0 */
+
+ ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
+ efi_var_mem_notify_exit_boot_services, NULL,
+ NULL, &event);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
+ efi_var_mem_notify_virtual_address_map, NULL,
+ NULL, &event);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ return ret;
+}
+
+efi_status_t __efi_runtime
+efi_get_variable_mem(u16 *variable_name, const efi_guid_t *vendor, u32 *attributes,
+ efi_uintn_t *data_size, void *data, u64 *timep)
+{
+ efi_uintn_t old_size;
+ struct efi_var_entry *var;
+ u16 *pdata;
+
+ if (!variable_name || !vendor || !data_size)
+ return EFI_INVALID_PARAMETER;
+ var = efi_var_mem_find(vendor, variable_name, NULL);
+ if (!var)
+ return EFI_NOT_FOUND;
+
+ if (attributes)
+ *attributes = var->attr;
+ if (timep)
+ *timep = var->time;
+
+ old_size = *data_size;
+ *data_size = var->length;
+ if (old_size < var->length)
+ return EFI_BUFFER_TOO_SMALL;
+
+ if (!data)
+ return EFI_INVALID_PARAMETER;
+
+ for (pdata = var->name; *pdata; ++pdata)
+ ;
+ ++pdata;
+
+ efi_memcpy_runtime(data, pdata, var->length);
+
+ return EFI_SUCCESS;
+}
+
+efi_status_t __efi_runtime
+efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
+ u16 *variable_name, efi_guid_t *vendor)
+{
+ struct efi_var_entry *var;
+ efi_uintn_t old_size;
+ u16 *pdata;
+
+ if (!variable_name_size || !variable_name || !vendor)
+ return EFI_INVALID_PARAMETER;
+
+ if (u16_strnlen(variable_name, *variable_name_size) ==
+ *variable_name_size)
+ return EFI_INVALID_PARAMETER;
+
+ if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
+ return EFI_INVALID_PARAMETER;
+
+ if (!var)
+ return EFI_NOT_FOUND;
+
+ for (pdata = var->name; *pdata; ++pdata)
+ ;
+ ++pdata;
+
+ old_size = *variable_name_size;
+ *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
+
+ if (old_size < *variable_name_size)
+ return EFI_BUFFER_TOO_SMALL;
+
+ efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
+ efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
+
+ return EFI_SUCCESS;
+}
+
+void efi_var_buf_update(struct efi_var_file *var_buf)
+{
+ memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_variable.c b/roms/u-boot/lib/efi_loader/efi_variable.c
new file mode 100644
index 000000000..ba0874e9e
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_variable.c
@@ -0,0 +1,439 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * UEFI runtime variable services
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <env.h>
+#include <env_internal.h>
+#include <hexdump.h>
+#include <log.h>
+#include <malloc.h>
+#include <rtc.h>
+#include <search.h>
+#include <uuid.h>
+#include <crypto/pkcs7_parser.h>
+#include <linux/compat.h>
+#include <u-boot/crc.h>
+#include <asm/sections.h>
+
+#ifdef CONFIG_EFI_SECURE_BOOT
+
+/**
+ * efi_variable_authenticate - authenticate a variable
+ * @variable: Variable name in u16
+ * @vendor: Guid of variable
+ * @data_size: Size of @data
+ * @data: Pointer to variable's value
+ * @given_attr: Attributes to be given at SetVariable()
+ * @env_attr: Attributes that an existing variable holds
+ * @time: signed time that an existing variable holds
+ *
+ * Called by efi_set_variable() to verify that the input is correct.
+ * Will replace the given data pointer with another that points to
+ * the actual data to store in the internal memory.
+ * On success, @data and @data_size will be replaced with variable's
+ * actual data, excluding authentication data, and its size, and variable's
+ * attributes and signed time will also be returned in @env_attr and @time,
+ * respectively.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_variable_authenticate(u16 *variable,
+ const efi_guid_t *vendor,
+ efi_uintn_t *data_size,
+ const void **data, u32 given_attr,
+ u32 *env_attr, u64 *time)
+{
+ const struct efi_variable_authentication_2 *auth;
+ struct efi_signature_store *truststore, *truststore2;
+ struct pkcs7_message *var_sig;
+ struct efi_image_regions *regs;
+ struct efi_time timestamp;
+ struct rtc_time tm;
+ u64 new_time;
+ u8 *ebuf;
+ enum efi_auth_var_type var_type;
+ efi_status_t ret;
+
+ var_sig = NULL;
+ truststore = NULL;
+ truststore2 = NULL;
+ regs = NULL;
+ ebuf = NULL;
+ ret = EFI_SECURITY_VIOLATION;
+
+ if (*data_size < sizeof(struct efi_variable_authentication_2))
+ goto err;
+
+ /* authentication data */
+ auth = *data;
+ if (*data_size < (sizeof(auth->time_stamp)
+ + auth->auth_info.hdr.dwLength))
+ goto err;
+
+ if (guidcmp(&auth->auth_info.cert_type, &efi_guid_cert_type_pkcs7))
+ goto err;
+
+ memcpy(&timestamp, &auth->time_stamp, sizeof(timestamp));
+ if (timestamp.pad1 || timestamp.nanosecond || timestamp.timezone ||
+ timestamp.daylight || timestamp.pad2)
+ goto err;
+
+ *data += sizeof(auth->time_stamp) + auth->auth_info.hdr.dwLength;
+ *data_size -= (sizeof(auth->time_stamp)
+ + auth->auth_info.hdr.dwLength);
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = timestamp.year;
+ tm.tm_mon = timestamp.month;
+ tm.tm_mday = timestamp.day;
+ tm.tm_hour = timestamp.hour;
+ tm.tm_min = timestamp.minute;
+ tm.tm_sec = timestamp.second;
+ new_time = rtc_mktime(&tm);
+
+ if (!efi_secure_boot_enabled()) {
+ /* finished checking */
+ *time = new_time;
+ return EFI_SUCCESS;
+ }
+
+ if (new_time <= *time)
+ goto err;
+
+ /* data to be digested */
+ regs = calloc(sizeof(*regs) + sizeof(struct image_region) * 5, 1);
+ if (!regs)
+ goto err;
+ regs->max = 5;
+ efi_image_region_add(regs, (uint8_t *)variable,
+ (uint8_t *)variable
+ + u16_strlen(variable) * sizeof(u16), 1);
+ efi_image_region_add(regs, (uint8_t *)vendor,
+ (uint8_t *)vendor + sizeof(*vendor), 1);
+ efi_image_region_add(regs, (uint8_t *)&given_attr,
+ (uint8_t *)&given_attr + sizeof(given_attr), 1);
+ efi_image_region_add(regs, (uint8_t *)&timestamp,
+ (uint8_t *)&timestamp + sizeof(timestamp), 1);
+ efi_image_region_add(regs, (uint8_t *)*data,
+ (uint8_t *)*data + *data_size, 1);
+
+ /* variable's signature list */
+ if (auth->auth_info.hdr.dwLength < sizeof(auth->auth_info))
+ goto err;
+
+ /* ebuf should be kept valid during the authentication */
+ var_sig = efi_parse_pkcs7_header(auth->auth_info.cert_data,
+ auth->auth_info.hdr.dwLength
+ - sizeof(auth->auth_info),
+ &ebuf);
+ if (!var_sig) {
+ EFI_PRINT("Parsing variable's signature failed\n");
+ goto err;
+ }
+
+ /* signature database used for authentication */
+ var_type = efi_auth_var_get_type(variable, vendor);
+ switch (var_type) {
+ case EFI_AUTH_VAR_PK:
+ case EFI_AUTH_VAR_KEK:
+ /* with PK */
+ truststore = efi_sigstore_parse_sigdb(L"PK");
+ if (!truststore)
+ goto err;
+ break;
+ case EFI_AUTH_VAR_DB:
+ case EFI_AUTH_VAR_DBX:
+ /* with PK and KEK */
+ truststore = efi_sigstore_parse_sigdb(L"KEK");
+ truststore2 = efi_sigstore_parse_sigdb(L"PK");
+ if (!truststore) {
+ if (!truststore2)
+ goto err;
+
+ truststore = truststore2;
+ truststore2 = NULL;
+ }
+ break;
+ default:
+ /* TODO: support private authenticated variables */
+ goto err;
+ }
+
+ /* verify signature */
+ if (efi_signature_verify(regs, var_sig, truststore, NULL)) {
+ EFI_PRINT("Verified\n");
+ } else {
+ if (truststore2 &&
+ efi_signature_verify(regs, var_sig, truststore2, NULL)) {
+ EFI_PRINT("Verified\n");
+ } else {
+ EFI_PRINT("Verifying variable's signature failed\n");
+ goto err;
+ }
+ }
+
+ /* finished checking */
+ *time = new_time;
+ ret = EFI_SUCCESS;
+
+err:
+ efi_sigstore_free(truststore);
+ efi_sigstore_free(truststore2);
+ pkcs7_free_message(var_sig);
+ free(ebuf);
+ free(regs);
+
+ return ret;
+}
+#else
+static efi_status_t efi_variable_authenticate(u16 *variable,
+ const efi_guid_t *vendor,
+ efi_uintn_t *data_size,
+ const void **data, u32 given_attr,
+ u32 *env_attr, u64 *time)
+{
+ return EFI_SUCCESS;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
+efi_status_t __efi_runtime
+efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor,
+ u32 *attributes, efi_uintn_t *data_size, void *data,
+ u64 *timep)
+{
+ return efi_get_variable_mem(variable_name, vendor, attributes, data_size, data, timep);
+}
+
+efi_status_t __efi_runtime
+efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
+ u16 *variable_name, efi_guid_t *vendor)
+{
+ return efi_get_next_variable_name_mem(variable_name_size, variable_name, vendor);
+}
+
+efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data, bool ro_check)
+{
+ struct efi_var_entry *var;
+ efi_uintn_t ret;
+ bool append, delete;
+ u64 time = 0;
+ enum efi_auth_var_type var_type;
+
+ if (!variable_name || !*variable_name || !vendor ||
+ ((attributes & EFI_VARIABLE_RUNTIME_ACCESS) &&
+ !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)))
+ return EFI_INVALID_PARAMETER;
+
+ /* check if a variable exists */
+ var = efi_var_mem_find(vendor, variable_name, NULL);
+ append = !!(attributes & EFI_VARIABLE_APPEND_WRITE);
+ attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE;
+ delete = !append && (!data_size || !attributes);
+
+ /* check attributes */
+ var_type = efi_auth_var_get_type(variable_name, vendor);
+ if (var) {
+ if (ro_check && (var->attr & EFI_VARIABLE_READ_ONLY))
+ return EFI_WRITE_PROTECTED;
+
+ if (IS_ENABLED(CONFIG_EFI_VARIABLES_PRESEED)) {
+ if (var_type != EFI_AUTH_VAR_NONE)
+ return EFI_WRITE_PROTECTED;
+ }
+
+ /* attributes won't be changed */
+ if (!delete &&
+ ((ro_check && var->attr != attributes) ||
+ (!ro_check && ((var->attr & ~(u32)EFI_VARIABLE_READ_ONLY)
+ != (attributes & ~(u32)EFI_VARIABLE_READ_ONLY))))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ time = var->time;
+ } else {
+ if (delete || append)
+ /*
+ * Trying to delete or to update a non-existent
+ * variable.
+ */
+ return EFI_NOT_FOUND;
+ }
+
+ if (var_type != EFI_AUTH_VAR_NONE) {
+ /* authentication is mandatory */
+ if (!(attributes &
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {
+ EFI_PRINT("%ls: TIME_BASED_AUTHENTICATED_WRITE_ACCESS required\n",
+ variable_name);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ /* authenticate a variable */
+ if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) {
+ if (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
+ return EFI_INVALID_PARAMETER;
+ if (attributes &
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
+ u32 env_attr;
+
+ ret = efi_variable_authenticate(variable_name, vendor,
+ &data_size, &data,
+ attributes, &env_attr,
+ &time);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* last chance to check for delete */
+ if (!data_size)
+ delete = true;
+ }
+ } else {
+ if (attributes &
+ (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS |
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {
+ EFI_PRINT("Secure boot is not configured\n");
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (delete) {
+ /* EFI_NOT_FOUND has been handled before */
+ attributes = var->attr;
+ ret = EFI_SUCCESS;
+ } else if (append) {
+ u16 *old_data = var->name;
+
+ for (; *old_data; ++old_data)
+ ;
+ ++old_data;
+ ret = efi_var_mem_ins(variable_name, vendor, attributes,
+ var->length, old_data, data_size, data,
+ time);
+ } else {
+ ret = efi_var_mem_ins(variable_name, vendor, attributes,
+ data_size, data, 0, NULL, time);
+ }
+ efi_var_mem_del(var);
+
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ if (var_type == EFI_AUTH_VAR_PK)
+ ret = efi_init_secure_state();
+ else
+ ret = EFI_SUCCESS;
+
+ /* Write non-volatile EFI variables to file */
+ if (attributes & EFI_VARIABLE_NON_VOLATILE &&
+ ret == EFI_SUCCESS && efi_obj_list_initialized == EFI_SUCCESS)
+ efi_var_to_file();
+
+ return EFI_SUCCESS;
+}
+
+efi_status_t efi_query_variable_info_int(u32 attributes,
+ u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size)
+{
+ *maximum_variable_storage_size = EFI_VAR_BUF_SIZE -
+ sizeof(struct efi_var_file);
+ *remaining_variable_storage_size = efi_var_mem_free();
+ *maximum_variable_size = EFI_VAR_BUF_SIZE -
+ sizeof(struct efi_var_file) -
+ sizeof(struct efi_var_entry);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_query_variable_info_runtime() - runtime implementation of
+ * QueryVariableInfo()
+ *
+ * @attributes: bitmask to select variables to be
+ * queried
+ * @maximum_variable_storage_size: maximum size of storage area for the
+ * selected variable types
+ * @remaining_variable_storage_size: remaining size of storage are for the
+ * selected variable types
+ * @maximum_variable_size: maximum size of a variable of the
+ * selected type
+ * Returns: status code
+ */
+efi_status_t __efi_runtime EFIAPI efi_query_variable_info_runtime(
+ u32 attributes,
+ u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_set_variable_runtime() - runtime implementation of SetVariable()
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+static efi_status_t __efi_runtime EFIAPI
+efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_variables_boot_exit_notify() - notify ExitBootServices() is called
+ */
+void efi_variables_boot_exit_notify(void)
+{
+ /* Switch variable services functions to runtime version */
+ efi_runtime_services.get_variable = efi_get_variable_runtime;
+ efi_runtime_services.get_next_variable_name =
+ efi_get_next_variable_name_runtime;
+ efi_runtime_services.set_variable = efi_set_variable_runtime;
+ efi_runtime_services.query_variable_info =
+ efi_query_variable_info_runtime;
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
+}
+
+/**
+ * efi_init_variables() - initialize variable services
+ *
+ * Return: status code
+ */
+efi_status_t efi_init_variables(void)
+{
+ efi_status_t ret;
+
+ ret = efi_var_mem_init();
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ if (IS_ENABLED(CONFIG_EFI_VARIABLES_PRESEED)) {
+ ret = efi_var_restore((struct efi_var_file *)
+ __efi_var_file_begin);
+ if (ret != EFI_SUCCESS)
+ log_err("Invalid EFI variable seed\n");
+ }
+
+ ret = efi_var_from_file();
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ return efi_init_secure_state();
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_variable_tee.c b/roms/u-boot/lib/efi_loader/efi_variable_tee.c
new file mode 100644
index 000000000..51920bcb5
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_variable_tee.c
@@ -0,0 +1,745 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI variable service via OP-TEE
+ *
+ * Copyright (C) 2019 Linaro Ltd. <sughosh.ganu@linaro.org>
+ * Copyright (C) 2019 Linaro Ltd. <ilias.apalodimas@linaro.org>
+ */
+
+#include <common.h>
+#include <efi.h>
+#include <efi_api.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <tee.h>
+#include <malloc.h>
+#include <mm_communication.h>
+
+#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 */
+
+struct mm_connection {
+ struct udevice *tee;
+ u32 session;
+};
+
+/**
+ * get_connection() - Retrieve OP-TEE session for a specific UUID.
+ *
+ * @conn: session buffer to fill
+ * Return: status code
+ */
+static int get_connection(struct mm_connection *conn)
+{
+ static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID;
+ struct udevice *tee = NULL;
+ struct tee_open_session_arg arg;
+ int rc = -ENODEV;
+
+ tee = tee_find_device(tee, NULL, NULL, NULL);
+ if (!tee)
+ goto out;
+
+ memset(&arg, 0, sizeof(arg));
+ tee_optee_ta_uuid_to_octets(arg.uuid, &uuid);
+ rc = tee_open_session(tee, &arg, 0, NULL);
+ if (rc)
+ goto out;
+
+ /* Check the internal OP-TEE result */
+ if (arg.ret != TEE_SUCCESS) {
+ rc = -EIO;
+ goto out;
+ }
+
+ conn->tee = tee;
+ conn->session = arg.session;
+
+ return 0;
+out:
+ return rc;
+}
+
+/**
+ * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
+ *
+ * @comm_buf: locally allocted communcation buffer
+ * @dsize: buffer size
+ * Return: status code
+ */
+static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
+{
+ ulong buf_size;
+ efi_status_t ret;
+ struct efi_mm_communicate_header *mm_hdr;
+ struct mm_connection conn = { NULL, 0 };
+ struct tee_invoke_arg arg;
+ struct tee_param param[2];
+ struct tee_shm *shm = NULL;
+ int rc;
+
+ if (!comm_buf)
+ return EFI_INVALID_PARAMETER;
+
+ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+ buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
+
+ if (dsize != buf_size)
+ return EFI_INVALID_PARAMETER;
+
+ rc = get_connection(&conn);
+ if (rc) {
+ log_err("Unable to open OP-TEE session (err=%d)\n", rc);
+ return EFI_UNSUPPORTED;
+ }
+
+ if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) {
+ log_err("Unable to register shared memory\n");
+ tee_close_session(conn.tee, conn.session);
+ return EFI_UNSUPPORTED;
+ }
+
+ memset(&arg, 0, sizeof(arg));
+ arg.func = PTA_STMM_CMDID_COMMUNICATE;
+ arg.session = conn.session;
+
+ memset(param, 0, sizeof(param));
+ param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT;
+ param[0].u.memref.size = buf_size;
+ param[0].u.memref.shm = shm;
+ param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+
+ 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)
+ return EFI_DEVICE_ERROR;
+
+ switch (param[1].u.value.a) {
+ case ARM_SVC_SPM_RET_SUCCESS:
+ ret = EFI_SUCCESS;
+ break;
+
+ case ARM_SVC_SPM_RET_INVALID_PARAMS:
+ ret = EFI_INVALID_PARAMETER;
+ break;
+
+ case ARM_SVC_SPM_RET_DENIED:
+ ret = EFI_ACCESS_DENIED;
+ break;
+
+ case ARM_SVC_SPM_RET_NO_MEMORY:
+ ret = EFI_OUT_OF_RESOURCES;
+ break;
+
+ default:
+ ret = EFI_ACCESS_DENIED;
+ }
+
+ return ret;
+}
+
+/**
+ * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
+ * it to OP-TEE
+ *
+ * @comm_buf: locally allocted communcation buffer
+ * @dsize: buffer size
+ * Return: status code
+ */
+static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
+{
+ efi_status_t ret;
+ struct efi_mm_communicate_header *mm_hdr;
+ struct smm_variable_communicate_header *var_hdr;
+
+ dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE;
+ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+ var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
+
+ ret = optee_mm_communicate(comm_buf, dsize);
+ if (ret != EFI_SUCCESS) {
+ log_err("%s failed!\n", __func__);
+ return ret;
+ }
+
+ return var_hdr->ret_status;
+}
+
+/**
+ * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the
+ * header data.
+ *
+ * @dptr: pointer address of the corresponding StandAloneMM
+ * function
+ * @payload_size: buffer size
+ * @func: standAloneMM function number
+ * @ret: EFI return code
+ * Return: buffer or NULL
+ */
+static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
+ efi_uintn_t func, efi_status_t *ret)
+{
+ const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID;
+ struct efi_mm_communicate_header *mm_hdr;
+ struct smm_variable_communicate_header *var_hdr;
+ u8 *comm_buf;
+
+ /* In the init function we initialize max_buffer_size with
+ * get_max_payload(). So skip the test if max_buffer_size is initialized
+ * StandAloneMM will perform similar checks and drop the buffer if it's
+ * too long
+ */
+ if (max_buffer_size && max_buffer_size <
+ (MM_COMMUNICATE_HEADER_SIZE +
+ MM_VARIABLE_COMMUNICATE_SIZE +
+ payload_size)) {
+ *ret = EFI_INVALID_PARAMETER;
+ return NULL;
+ }
+
+ comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE +
+ MM_VARIABLE_COMMUNICATE_SIZE +
+ payload_size);
+ if (!comm_buf) {
+ *ret = EFI_OUT_OF_RESOURCES;
+ return NULL;
+ }
+
+ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+ guidcpy(&mm_hdr->header_guid, &mm_var_guid);
+ mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size;
+
+ var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
+ var_hdr->function = func;
+ if (dptr)
+ *dptr = var_hdr->data;
+ *ret = EFI_SUCCESS;
+
+ return comm_buf;
+}
+
+/**
+ * get_max_payload() - Get variable payload size from StandAloneMM.
+ *
+ * @size: size of the variable in storage
+ * Return: status code
+ */
+efi_status_t EFIAPI get_max_payload(efi_uintn_t *size)
+{
+ struct smm_variable_payload_size *var_payload = NULL;
+ efi_uintn_t payload_size;
+ u8 *comm_buf = NULL;
+ efi_status_t ret;
+
+ if (!size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ payload_size = sizeof(*var_payload);
+ comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
+ SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
+ if (!comm_buf)
+ goto out;
+
+ ret = mm_communicate(comm_buf, payload_size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Make sure the buffer is big enough for storing variables */
+ if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
+ ret = EFI_DEVICE_ERROR;
+ 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
+ * it up here to ensure backwards compatibility with older versions
+ * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
+ * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
+ * flexible array member).
+ *
+ * size is guaranteed to be > 2 due to checks on the beginning.
+ */
+ *size -= 2;
+out:
+ free(comm_buf);
+ return ret;
+}
+
+/*
+ * StMM can store internal attributes and properties for variables, i.e enabling
+ * R/O variables
+ */
+static efi_status_t set_property_int(u16 *variable_name, efi_uintn_t name_size,
+ const efi_guid_t *vendor,
+ struct var_check_property *var_property)
+{
+ struct smm_variable_var_check_property *smm_property;
+ efi_uintn_t payload_size;
+ u8 *comm_buf = NULL;
+ efi_status_t ret;
+
+ payload_size = sizeof(*smm_property) + name_size;
+ if (payload_size > max_payload_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
+ SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET,
+ &ret);
+ if (!comm_buf)
+ goto out;
+
+ guidcpy(&smm_property->guid, vendor);
+ smm_property->name_size = name_size;
+ memcpy(&smm_property->property, var_property,
+ sizeof(smm_property->property));
+ memcpy(smm_property->name, variable_name, name_size);
+
+ ret = mm_communicate(comm_buf, payload_size);
+
+out:
+ free(comm_buf);
+ return ret;
+}
+
+static efi_status_t get_property_int(u16 *variable_name, efi_uintn_t name_size,
+ const efi_guid_t *vendor,
+ struct var_check_property *var_property)
+{
+ struct smm_variable_var_check_property *smm_property;
+ efi_uintn_t payload_size;
+ u8 *comm_buf = NULL;
+ efi_status_t ret;
+
+ memset(var_property, 0, sizeof(*var_property));
+ payload_size = sizeof(*smm_property) + name_size;
+ if (payload_size > max_payload_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
+ SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET,
+ &ret);
+ if (!comm_buf)
+ goto out;
+
+ guidcpy(&smm_property->guid, vendor);
+ smm_property->name_size = name_size;
+ memcpy(smm_property->name, variable_name, name_size);
+
+ ret = mm_communicate(comm_buf, payload_size);
+ /*
+ * Currently only R/O property is supported in StMM.
+ * Variables that are not set to R/O will not set the property in StMM
+ * and the call will return EFI_NOT_FOUND. We are setting the
+ * properties to 0x0 so checking against that is enough for the
+ * EFI_NOT_FOUND case.
+ */
+ if (ret == EFI_NOT_FOUND)
+ ret = EFI_SUCCESS;
+ if (ret != EFI_SUCCESS)
+ goto out;
+ memcpy(var_property, &smm_property->property, sizeof(*var_property));
+
+out:
+ free(comm_buf);
+ return ret;
+}
+
+efi_status_t efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor,
+ u32 *attributes, efi_uintn_t *data_size,
+ void *data, u64 *timep)
+{
+ struct var_check_property var_property;
+ struct smm_variable_access *var_acc;
+ efi_uintn_t payload_size;
+ efi_uintn_t name_size;
+ efi_uintn_t tmp_dsize;
+ u8 *comm_buf = NULL;
+ efi_status_t ret;
+
+ if (!variable_name || !vendor || !data_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Check payload size */
+ name_size = u16_strsize(variable_name);
+ if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Trim output buffer size */
+ tmp_dsize = *data_size;
+ if (name_size + tmp_dsize >
+ max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
+ tmp_dsize = max_payload_size -
+ MM_VARIABLE_ACCESS_HEADER_SIZE -
+ name_size;
+ }
+
+ /* Get communication buffer and initialize header */
+ payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize;
+ comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
+ SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret);
+ if (!comm_buf)
+ goto out;
+
+ /* Fill in contents */
+ guidcpy(&var_acc->guid, vendor);
+ var_acc->data_size = tmp_dsize;
+ var_acc->name_size = name_size;
+ var_acc->attr = attributes ? *attributes : 0;
+ memcpy(var_acc->name, variable_name, name_size);
+
+ /* Communicate */
+ ret = mm_communicate(comm_buf, payload_size);
+ if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+ /* Update with reported data size for trimmed case */
+ *data_size = var_acc->data_size;
+ }
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = get_property_int(variable_name, name_size, vendor, &var_property);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (attributes) {
+ *attributes = var_acc->attr;
+ if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)
+ *attributes |= EFI_VARIABLE_READ_ONLY;
+ }
+
+ if (data)
+ memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
+ var_acc->data_size);
+ else
+ ret = EFI_INVALID_PARAMETER;
+
+out:
+ free(comm_buf);
+ return ret;
+}
+
+efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
+ u16 *variable_name,
+ efi_guid_t *guid)
+{
+ struct smm_variable_getnext *var_getnext;
+ efi_uintn_t payload_size;
+ efi_uintn_t out_name_size;
+ efi_uintn_t in_name_size;
+ u8 *comm_buf = NULL;
+ efi_status_t ret;
+
+ if (!variable_name_size || !variable_name || !guid) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ out_name_size = *variable_name_size;
+ in_name_size = u16_strsize(variable_name);
+
+ if (out_name_size < in_name_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Trim output buffer size */
+ if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE)
+ out_name_size = max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE;
+
+ payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size;
+ comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size,
+ SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME,
+ &ret);
+ if (!comm_buf)
+ goto out;
+
+ /* Fill in contents */
+ guidcpy(&var_getnext->guid, guid);
+ var_getnext->name_size = out_name_size;
+ memcpy(var_getnext->name, variable_name, in_name_size);
+ memset((u8 *)var_getnext->name + in_name_size, 0x0,
+ out_name_size - in_name_size);
+
+ /* Communicate */
+ ret = mm_communicate(comm_buf, payload_size);
+ if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+ /* Update with reported data size for trimmed case */
+ *variable_name_size = var_getnext->name_size;
+ }
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ guidcpy(guid, &var_getnext->guid);
+ memcpy(variable_name, var_getnext->name, var_getnext->name_size);
+
+out:
+ free(comm_buf);
+ return ret;
+}
+
+efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data, bool ro_check)
+{
+ efi_status_t ret, alt_ret = EFI_SUCCESS;
+ struct var_check_property var_property;
+ struct smm_variable_access *var_acc;
+ efi_uintn_t payload_size;
+ efi_uintn_t name_size;
+ u8 *comm_buf = NULL;
+ bool ro;
+
+ if (!variable_name || variable_name[0] == 0 || !vendor) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (data_size > 0 && !data) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* Check payload size */
+ name_size = u16_strsize(variable_name);
+ payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
+ if (payload_size > max_payload_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /*
+ * Allocate the buffer early, before switching to RW (if needed)
+ * so we won't need to account for any failures in reading/setting
+ * the properties, if the allocation fails
+ */
+ comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
+ SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret);
+ if (!comm_buf)
+ goto out;
+
+ ro = !!(attributes & EFI_VARIABLE_READ_ONLY);
+ attributes &= EFI_VARIABLE_MASK;
+
+ /*
+ * The API has the ability to override RO flags. If no RO check was
+ * requested switch the variable to RW for the duration of this call
+ */
+ ret = get_property_int(variable_name, name_size, vendor,
+ &var_property);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
+ /* Bypass r/o check */
+ if (!ro_check) {
+ var_property.property &= ~VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
+ ret = set_property_int(variable_name, name_size, vendor, &var_property);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ } else {
+ ret = EFI_WRITE_PROTECTED;
+ goto out;
+ }
+ }
+
+ /* Fill in contents */
+ guidcpy(&var_acc->guid, vendor);
+ var_acc->data_size = data_size;
+ var_acc->name_size = name_size;
+ var_acc->attr = attributes;
+ memcpy(var_acc->name, variable_name, name_size);
+ memcpy((u8 *)var_acc->name + name_size, data, data_size);
+
+ /* Communicate */
+ ret = mm_communicate(comm_buf, payload_size);
+ if (ret != EFI_SUCCESS)
+ alt_ret = ret;
+
+ if (ro && !(var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)) {
+ var_property.revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION;
+ var_property.property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
+ var_property.attributes = attributes;
+ var_property.minsize = 1;
+ var_property.maxsize = var_acc->data_size;
+ ret = set_property_int(variable_name, name_size, vendor, &var_property);
+ }
+
+ if (alt_ret != EFI_SUCCESS)
+ goto out;
+
+ if (!u16_strcmp(variable_name, L"PK"))
+ alt_ret = efi_init_secure_state();
+out:
+ free(comm_buf);
+ return alt_ret == EFI_SUCCESS ? ret : alt_ret;
+}
+
+efi_status_t efi_query_variable_info_int(u32 attributes,
+ u64 *max_variable_storage_size,
+ u64 *remain_variable_storage_size,
+ u64 *max_variable_size)
+{
+ struct smm_variable_query_info *mm_query_info;
+ efi_uintn_t payload_size;
+ efi_status_t ret;
+ u8 *comm_buf;
+
+ payload_size = sizeof(*mm_query_info);
+ comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size,
+ SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO,
+ &ret);
+ if (!comm_buf)
+ goto out;
+
+ mm_query_info->attr = attributes;
+ ret = mm_communicate(comm_buf, payload_size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ *max_variable_storage_size = mm_query_info->max_variable_storage;
+ *remain_variable_storage_size =
+ mm_query_info->remaining_variable_storage;
+ *max_variable_size = mm_query_info->max_variable_size;
+
+out:
+ free(comm_buf);
+ return ret;
+}
+
+/**
+ * efi_query_variable_info() - get information about EFI variables
+ *
+ * This function implements the QueryVariableInfo() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @attributes: bitmask to select variables to be
+ * queried
+ * @maximum_variable_storage_size: maximum size of storage area for the
+ * selected variable types
+ * @remaining_variable_storage_size: remaining size of storage are for the
+ * selected variable types
+ * @maximum_variable_size: maximum size of a variable of the
+ * selected type
+ * Return: status code
+ */
+efi_status_t EFIAPI __efi_runtime
+efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size,
+ u64 *remain_variable_storage_size,
+ u64 *max_variable_size)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_set_variable_runtime() - runtime implementation of SetVariable()
+ *
+ * @variable_name: name of the variable
+ * @guid: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+static efi_status_t __efi_runtime EFIAPI
+efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_variables_boot_exit_notify() - notify ExitBootServices() is called
+ */
+void efi_variables_boot_exit_notify(void)
+{
+ efi_status_t ret;
+ u8 *comm_buf;
+ loff_t len;
+ struct efi_var_file *var_buf;
+
+ comm_buf = setup_mm_hdr(NULL, 0,
+ SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE, &ret);
+ if (comm_buf)
+ ret = mm_communicate(comm_buf, 0);
+ else
+ ret = EFI_NOT_FOUND;
+
+ if (ret != EFI_SUCCESS)
+ log_err("Unable to notify StMM for ExitBootServices\n");
+ free(comm_buf);
+
+ /*
+ * Populate the list for runtime variables.
+ * asking EFI_VARIABLE_RUNTIME_ACCESS is redundant, since
+ * efi_var_mem_notify_exit_boot_services will clean those, but that's fine
+ */
+ ret = efi_var_collect(&var_buf, &len, EFI_VARIABLE_RUNTIME_ACCESS);
+ if (ret != EFI_SUCCESS)
+ log_err("Can't populate EFI variables. No runtime variables will be available\n");
+ else
+ efi_var_buf_update(var_buf);
+ free(var_buf);
+
+ /* Update runtime service table */
+ efi_runtime_services.query_variable_info =
+ efi_query_variable_info_runtime;
+ efi_runtime_services.get_variable = efi_get_variable_runtime;
+ efi_runtime_services.get_next_variable_name =
+ efi_get_next_variable_name_runtime;
+ efi_runtime_services.set_variable = efi_set_variable_runtime;
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
+}
+
+/**
+ * efi_init_variables() - initialize variable services
+ *
+ * Return: status code
+ */
+efi_status_t efi_init_variables(void)
+{
+ efi_status_t ret;
+
+ /* Create a cached copy of the variables that will be enabled on ExitBootServices() */
+ ret = efi_var_mem_init();
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = get_max_payload(&max_payload_size);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
+ MM_VARIABLE_COMMUNICATE_SIZE +
+ max_payload_size;
+
+ ret = efi_init_secure_state();
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/u-boot/lib/efi_loader/efi_watchdog.c b/roms/u-boot/lib/efi_loader/efi_watchdog.c
new file mode 100644
index 000000000..61ea0f792
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/efi_watchdog.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI watchdog
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+/* Conversion factor from seconds to multiples of 100ns */
+#define EFI_SECONDS_TO_100NS 10000000ULL
+
+static struct efi_event *watchdog_timer_event;
+
+/**
+ * efi_watchdog_timer_notify() - resets system upon watchdog event
+ *
+ * Reset the system when the watchdog event is notified.
+ *
+ * @event: the watchdog event
+ * @context: not used
+ */
+static void EFIAPI efi_watchdog_timer_notify(struct efi_event *event,
+ void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+
+ printf("\nEFI: Watchdog timeout\n");
+ EFI_CALL_VOID(efi_runtime_services.reset_system(EFI_RESET_COLD,
+ EFI_SUCCESS, 0, NULL));
+
+ EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_set_watchdog() - resets the watchdog timer
+ *
+ * This function is used by the SetWatchdogTimer service.
+ *
+ * @timeout: seconds before reset by watchdog
+ * Return: status code
+ */
+efi_status_t efi_set_watchdog(unsigned long timeout)
+{
+ efi_status_t r;
+
+ if (timeout)
+ /* Reset watchdog */
+ r = efi_set_timer(watchdog_timer_event, EFI_TIMER_RELATIVE,
+ EFI_SECONDS_TO_100NS * timeout);
+ else
+ /* Deactivate watchdog */
+ r = efi_set_timer(watchdog_timer_event, EFI_TIMER_STOP, 0);
+ return r;
+}
+
+/**
+ * efi_watchdog_register() - initializes the EFI watchdog
+ *
+ * This function is called by efi_init_obj_list().
+ *
+ * Return: status code
+ */
+efi_status_t efi_watchdog_register(void)
+{
+ efi_status_t r;
+
+ /*
+ * Create a timer event.
+ */
+ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ efi_watchdog_timer_notify, NULL, NULL,
+ &watchdog_timer_event);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register watchdog event\n");
+ return r;
+ }
+ /*
+ * The UEFI standard requires that the watchdog timer is set to five
+ * minutes when invoking an EFI boot option.
+ *
+ * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A
+ * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer
+ */
+ r = efi_set_watchdog(300);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to set watchdog timer\n");
+ return r;
+ }
+ return EFI_SUCCESS;
+}
diff --git a/roms/u-boot/lib/efi_loader/helloworld.c b/roms/u-boot/lib/efi_loader/helloworld.c
new file mode 100644
index 000000000..3f215e2a4
--- /dev/null
+++ b/roms/u-boot/lib/efi_loader/helloworld.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hello world EFI application
+ *
+ * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This test program is used to test the invocation of an EFI application.
+ * It writes
+ *
+ * * a greeting
+ * * the firmware's UEFI version
+ * * the installed configuration tables
+ * * the boot device's device path and the file path
+ *
+ * to the console.
+ */
+
+#include <efi_api.h>
+
+static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
+static const efi_guid_t device_path_to_text_protocol_guid =
+ EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+static const efi_guid_t device_path_guid = EFI_DEVICE_PATH_PROTOCOL_GUID;
+static const efi_guid_t fdt_guid = EFI_FDT_GUID;
+static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID;
+static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
+
+static struct efi_system_table *systable;
+static struct efi_boot_services *boottime;
+static struct efi_simple_text_output_protocol *con_out;
+
+/**
+ * print_uefi_revision() - print UEFI revision number
+ */
+static void print_uefi_revision(void)
+{
+ u16 rev[] = L"0.0.0";
+
+ rev[0] = (systable->hdr.revision >> 16) + '0';
+ rev[4] = systable->hdr.revision & 0xffff;
+ for (; rev[4] >= 10;) {
+ rev[4] -= 10;
+ ++rev[2];
+ }
+ /* Third digit is only to be shown if non-zero */
+ if (rev[4])
+ rev[4] += '0';
+ else
+ rev[3] = 0;
+
+ con_out->output_string(con_out, L"Running on UEFI ");
+ con_out->output_string(con_out, rev);
+ con_out->output_string(con_out, L"\r\n");
+}
+
+/**
+ * print_config_tables() - print configuration tables
+ */
+static void print_config_tables(void)
+{
+ efi_uintn_t i;
+
+ /* Find configuration tables */
+ for (i = 0; i < systable->nr_tables; ++i) {
+ if (!memcmp(&systable->tables[i].guid, &fdt_guid,
+ sizeof(efi_guid_t)))
+ con_out->output_string
+ (con_out, L"Have device tree\r\n");
+ if (!memcmp(&systable->tables[i].guid, &acpi_guid,
+ sizeof(efi_guid_t)))
+ con_out->output_string
+ (con_out, L"Have ACPI 2.0 table\r\n");
+ if (!memcmp(&systable->tables[i].guid, &smbios_guid,
+ sizeof(efi_guid_t)))
+ con_out->output_string
+ (con_out, L"Have SMBIOS table\r\n");
+ }
+}
+
+/**
+ * print_load_options() - print load options
+ *
+ * @systable: system table
+ * @con_out: simple text output protocol
+ */
+void print_load_options(struct efi_loaded_image *loaded_image)
+{
+ /* Output the load options */
+ con_out->output_string(con_out, L"Load options: ");
+ if (loaded_image->load_options_size && loaded_image->load_options)
+ con_out->output_string(con_out,
+ (u16 *)loaded_image->load_options);
+ else
+ con_out->output_string(con_out, L"<none>");
+ con_out->output_string(con_out, L"\r\n");
+}
+
+/**
+ * print_device_path() - print device path
+ *
+ * @device_path: device path to print
+ * @dp2txt: device path to text protocol
+ */
+efi_status_t print_device_path(struct efi_device_path *device_path,
+ struct efi_device_path_to_text_protocol *dp2txt)
+{
+ u16 *string;
+ efi_status_t ret;
+
+ if (!device_path) {
+ con_out->output_string(con_out, L"<none>\r\n");
+ return EFI_SUCCESS;
+ }
+
+ string = dp2txt->convert_device_path_to_text(device_path, true, false);
+ if (!string) {
+ con_out->output_string
+ (con_out, L"Cannot convert device path to text\r\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+ con_out->output_string(con_out, string);
+ con_out->output_string(con_out, L"\r\n");
+ ret = boottime->free_pool(string);
+ if (ret != EFI_SUCCESS) {
+ con_out->output_string(con_out, L"Cannot free pool memory\r\n");
+ return ret;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_main() - entry point of the EFI application.
+ *
+ * @handle: handle of the loaded image
+ * @systab: system table
+ * @return: status code
+ */
+efi_status_t EFIAPI efi_main(efi_handle_t handle,
+ struct efi_system_table *systab)
+{
+ struct efi_loaded_image *loaded_image;
+ struct efi_device_path_to_text_protocol *device_path_to_text;
+ struct efi_device_path *device_path;
+ efi_status_t ret;
+
+ systable = systab;
+ boottime = systable->boottime;
+ con_out = systable->con_out;
+
+ /* UEFI requires CR LF */
+ con_out->output_string(con_out, L"Hello, world!\r\n");
+
+ print_uefi_revision();
+ print_config_tables();
+
+ /* Get the loaded image protocol */
+ ret = boottime->handle_protocol(handle, &loaded_image_guid,
+ (void **)&loaded_image);
+ if (ret != EFI_SUCCESS) {
+ con_out->output_string
+ (con_out, L"Cannot open loaded image protocol\r\n");
+ goto out;
+ }
+ print_load_options(loaded_image);
+
+ /* Get the device path to text protocol */
+ ret = boottime->locate_protocol(&device_path_to_text_protocol_guid,
+ NULL, (void **)&device_path_to_text);
+ if (ret != EFI_SUCCESS) {
+ con_out->output_string
+ (con_out, L"Cannot open device path to text protocol\r\n");
+ goto out;
+ }
+ if (!loaded_image->device_handle) {
+ con_out->output_string
+ (con_out, L"Missing device handle\r\n");
+ goto out;
+ }
+ ret = boottime->handle_protocol(loaded_image->device_handle,
+ &device_path_guid,
+ (void **)&device_path);
+ if (ret != EFI_SUCCESS) {
+ con_out->output_string
+ (con_out, L"Missing device path for device handle\r\n");
+ goto out;
+ }
+ con_out->output_string(con_out, L"Boot device: ");
+ ret = print_device_path(device_path, device_path_to_text);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ con_out->output_string(con_out, L"File path: ");
+ ret = print_device_path(loaded_image->file_path, device_path_to_text);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+out:
+ boottime->exit(handle, ret, 0, NULL);
+
+ /* We should never arrive here */
+ return ret;
+}