Merge tag 'efi-2021-04-rc1-2' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi

Pull request for UEFI sub-system for efi-2021-04-rc1-2

* Provide a test tool for initial RAM disk provided via load file2 protocol.
* Make more items configurable to reduce code size:
  * Boot manager
  * EFI_DT_FIXUP_PROTOCOL
  * EFI_DEVICE_PATH_UTILITIES_PROTOCOL
* Bug fixes
  * avoid EFI runtime symbols in global symbol table
diff --git a/cmd/bootefi.c b/cmd/bootefi.c
index fe70eec..c8eb5c3 100644
--- a/cmd/bootefi.c
+++ b/cmd/bootefi.c
@@ -631,10 +631,12 @@
 	else if (ret != EFI_SUCCESS)
 		return CMD_RET_FAILURE;
 
-	if (!strcmp(argv[1], "bootmgr"))
-		return do_efibootmgr();
+	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
+		if (!strcmp(argv[1], "bootmgr"))
+			return do_efibootmgr();
+	}
 #ifdef CONFIG_CMD_BOOTEFI_SELFTEST
-	else if (!strcmp(argv[1], "selftest"))
+	if (!strcmp(argv[1], "selftest"))
 		return do_efi_selftest();
 #endif
 
@@ -657,11 +659,14 @@
 	"    Use environment variable efi_selftest to select a single test.\n"
 	"    Use 'setenv efi_selftest list' to enumerate all tests.\n"
 #endif
+#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR
 	"bootefi bootmgr [fdt address]\n"
 	"  - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
 	"\n"
 	"    If specified, the device tree located at <fdt address> gets\n"
-	"    exposed as EFI configuration table.\n";
+	"    exposed as EFI configuration table.\n"
+#endif
+	;
 #endif
 
 U_BOOT_CMD(
diff --git a/cmd/efidebug.c b/cmd/efidebug.c
index 6de81ca..9a2d4dd 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -1367,8 +1367,8 @@
  *
  *     efidebug test bootmgr
  */
-static int do_efi_test_bootmgr(struct cmd_tbl *cmdtp, int flag,
-			       int argc, char * const argv[])
+static __maybe_unused int do_efi_test_bootmgr(struct cmd_tbl *cmdtp, int flag,
+					      int argc, char * const argv[])
 {
 	efi_handle_t image;
 	efi_uintn_t exit_data_size = 0;
@@ -1392,8 +1392,10 @@
 }
 
 static struct cmd_tbl cmd_efidebug_test_sub[] = {
+#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR
 	U_BOOT_CMD_MKENT(bootmgr, CONFIG_SYS_MAXARGS, 1, do_efi_test_bootmgr,
 			 "", ""),
+#endif
 };
 
 /**
@@ -1581,8 +1583,10 @@
 	"  - show UEFI memory map\n"
 	"efidebug tables\n"
 	"  - show UEFI configuration tables\n"
+#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR
 	"efidebug test bootmgr\n"
 	"  - run simple bootmgr for test\n"
+#endif
 	"efidebug query [-nv][-bs][-rt][-at]\n"
 	"  - show size of UEFI variables store\n";
 #endif
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
index c986226..2627c2a 100644
--- a/include/config_distro_bootcmd.h
+++ b/include/config_distro_bootcmd.h
@@ -123,14 +123,20 @@
 #endif
 #endif
 
-
-#define BOOTENV_SHARED_EFI                                                \
+#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR
+#define BOOTENV_EFI_BOOTMGR                                               \
 	"boot_efi_bootmgr="                                               \
 		"if fdt addr ${fdt_addr_r}; then "                        \
 			"bootefi bootmgr ${fdt_addr_r};"                  \
 		"else "                                                   \
 			"bootefi bootmgr;"                                \
-		"fi\0"                                                    \
+		"fi\0"
+#else
+#define BOOTENV_EFI_BOOTMGR
+#endif
+
+#define BOOTENV_SHARED_EFI                                                \
+	BOOTENV_EFI_BOOTMGR                                               \
 	\
 	"boot_efi_binary="                                                \
 		"load ${devtype} ${devnum}:${distro_bootpart} "           \
diff --git a/include/efi_api.h b/include/efi_api.h
index df9bee2..48e48a6 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -1693,10 +1693,6 @@
 	efi_handle_t driver_binding_handle;
 };
 
-/* Deprecated version of the Unicode collation protocol */
-#define EFI_UNICODE_COLLATION_PROTOCOL_GUID \
-	EFI_GUID(0x1d85cd7f, 0xf43d, 0x11d2, \
-		 0x9a, 0x0c, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
 /* Current version of the Unicode collation protocol */
 #define EFI_UNICODE_COLLATION_PROTOCOL2_GUID \
 	EFI_GUID(0xa4c751fc, 0x23ae, 0x4c3e, \
diff --git a/include/efi_loader.h b/include/efi_loader.h
index e53d286..f470bbd 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -147,9 +147,6 @@
 /* implementation of the EFI_DEVICE_PATH_UTILITIES_PROTOCOL */
 extern const struct efi_device_path_utilities_protocol
 					efi_device_path_utilities;
-/* deprecated version of the EFI_UNICODE_COLLATION_PROTOCOL */
-extern const struct efi_unicode_collation_protocol
-					efi_unicode_collation_protocol;
 /* current version of the EFI_UNICODE_COLLATION_PROTOCOL */
 extern const struct efi_unicode_collation_protocol
 					efi_unicode_collation_protocol2;
@@ -564,7 +561,7 @@
  * @size:	size in bytes
  * Return:	size in pages
  */
-#define efi_size_in_pages(size) ((size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
+#define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
 /* Generic EFI memory allocator, call this to get memory */
 void *efi_alloc(uint64_t len, int memory_type);
 /* More specific EFI memory allocator, called by EFI payloads */
diff --git a/include/efi_variable.h b/include/efi_variable.h
index bf50762..4623a64 100644
--- a/include/efi_variable.h
+++ b/include/efi_variable.h
@@ -306,4 +306,15 @@
 efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
 				   u16 *variable_name, efi_guid_t *guid);
 
+/**
+ * efi_var_buf_update() - udpate memory buffer for variables
+ *
+ * @var_buf:	source buffer
+ *
+ * This function copies to the memory buffer for UEFI variables. Call this
+ * function in ExitBootServices() if memory backed variables are only used
+ * at runtime to fill the buffer.
+ */
+void efi_var_buf_update(struct efi_var_file *var_buf);
+
 #endif
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index fdf245d..e729f72 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -27,6 +27,14 @@
 
 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
@@ -200,6 +208,21 @@
 	  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
@@ -229,17 +252,6 @@
 	  set, only the the correct handling of the letters of the codepage
 	  used by the FAT file system is ensured.
 
-config EFI_UNICODE_COLLATION_PROTOCOL
-	bool "Deprecated version of the Unicode collation protocol"
-	default n
-	help
-	  In EFI 1.10 a version of the Unicode collation protocol using ISO
-	  639-2 language codes existed. This protocol is not part of the UEFI
-	  specification any longer. Unfortunately it is required to run the
-	  UEFI Self Certification Test (SCT) II, version 2.6, 2017.
-
-	  Choose this option for testing only. It is bound to be removed.
-
 endif
 
 config EFI_LOADER_BOUNCE_BUFFER
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 412fa88..10b42e8 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -21,20 +21,21 @@
 endif
 
 obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
-obj-y += efi_bootmgr.o
+obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += efi_bootmgr.o
 obj-y += efi_boottime.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-y += efi_device_path_utilities.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
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index d3be2f9..25f5ceb 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -31,141 +31,6 @@
  */
 
 /**
- * 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;
-}
-
-/**
  * get_var() - get UEFI variable
  *
  * It is the caller's duty to free the returned buffer.
diff --git a/lib/efi_loader/efi_dt_fixup.c b/lib/efi_loader/efi_dt_fixup.c
index 5f0ae5c..3850ab3 100644
--- a/lib/efi_loader/efi_dt_fixup.c
+++ b/lib/efi_loader/efi_dt_fixup.c
@@ -10,16 +10,6 @@
 #include <efi_loader.h>
 #include <mapmem.h>
 
-static efi_status_t EFIAPI efi_dt_fixup(struct efi_dt_fixup_protocol *this,
-					void *dtb,
-					efi_uintn_t *buffer_size,
-					u32 flags);
-
-struct efi_dt_fixup_protocol efi_dt_fixup_prot = {
-	.revision = EFI_DT_FIXUP_PROTOCOL_REVISION,
-	.fixup = efi_dt_fixup
-};
-
 const efi_guid_t efi_guid_dt_fixup_protocol = EFI_DT_FIXUP_PROTOCOL_GUID;
 
 /**
@@ -102,10 +92,21 @@
 	}
 }
 
-static efi_status_t EFIAPI efi_dt_fixup(struct efi_dt_fixup_protocol *this,
-					void *dtb,
-					efi_uintn_t *buffer_size,
-					u32 flags)
+/**
+ * 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;
@@ -158,3 +159,8 @@
 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/lib/efi_loader/efi_load_options.c b/lib/efi_loader/efi_load_options.c
new file mode 100644
index 0000000..68cd85b
--- /dev/null
+++ b/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/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c
index b411a12..739c686 100644
--- a/lib/efi_loader/efi_root_node.c
+++ b/lib/efi_loader/efi_root_node.c
@@ -58,21 +58,17 @@
 			 &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,
-#if !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE)
+#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)
-#if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL)
-			 /* Deprecated Unicode collation protocol */
-			 &efi_guid_unicode_collation_protocol,
-			 (void *)&efi_unicode_collation_protocol,
-#endif
-			 /* Current Unicode collation protocol */
 			 &efi_guid_unicode_collation_protocol2,
 			 (void *)&efi_unicode_collation_protocol2,
 #endif
diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c
index 6655c68..f6c875b 100644
--- a/lib/efi_loader/efi_unicode_collation.c
+++ b/lib/efi_loader/efi_unicode_collation.c
@@ -38,7 +38,7 @@
  * @s2:		second string
  *
  * This function implements the StriColl() service of the
- * EFI_UNICODE_COLLATION_PROTOCOL.
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
  *
  * See the Unified Extensible Firmware Interface (UEFI) specification for
  * details.
@@ -179,7 +179,7 @@
  *	- [<char1>-<char2>] matches any character in the range
  *
  * This function implements the MetaMatch() service of the
- * EFI_UNICODE_COLLATION_PROTOCOL.
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
  *
  * Return:	true if the string is matched.
  */
@@ -204,7 +204,7 @@
  * same number of words this does not pose a problem.
  *
  * This function implements the StrLwr() service of the
- * EFI_UNICODE_COLLATION_PROTOCOL.
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
  */
 static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this,
 			       u16 *string)
@@ -225,7 +225,7 @@
  * same number of words this does not pose a problem.
  *
  * This function implements the StrUpr() service of the
- * EFI_UNICODE_COLLATION_PROTOCOL.
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
  */
 static void EFIAPI efi_str_upr(struct efi_unicode_collation_protocol *this,
 			       u16 *string)
@@ -245,7 +245,7 @@
  * @string:	converted string
  *
  * This function implements the FatToStr() service of the
- * EFI_UNICODE_COLLATION_PROTOCOL.
+ * 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)
@@ -276,7 +276,7 @@
  * @fat:	converted string
  *
  * This function implements the StrToFat() service of the
- * EFI_UNICODE_COLLATION_PROTOCOL.
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
  *
  * Return:	true if an illegal character was substituted by '_'.
  */
@@ -337,30 +337,3 @@
 	.str_to_fat = efi_str_to_fat,
 	.supported_languages = "en",
 };
-
-/*
- * In EFI 1.10 a version of the Unicode collation protocol using ISO 639-2
- * language codes existed. This protocol is not part of the UEFI specification
- * any longer. Unfortunately it is required to run the UEFI Self Certification
- * Test (SCT) II, version 2.6, 2017. So we implement it here for the sole
- * purpose of running the SCT. It can be removed when a compliant SCT is
- * available.
- */
-#if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL)
-
-/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL */
-const efi_guid_t efi_guid_unicode_collation_protocol =
-	EFI_UNICODE_COLLATION_PROTOCOL_GUID;
-
-const struct efi_unicode_collation_protocol efi_unicode_collation_protocol = {
-	.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,
-	/* ISO 639-2 language code */
-	.supported_languages = "eng",
-};
-
-#endif
diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c
index d155f25..3d335a8 100644
--- a/lib/efi_loader/efi_var_mem.c
+++ b/lib/efi_loader/efi_var_mem.c
@@ -10,7 +10,13 @@
 #include <efi_variable.h>
 #include <u-boot/crc.h>
 
-struct efi_var_file __efi_runtime_data *efi_var_buf;
+/*
+ * 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;
 
 /**
@@ -339,3 +345,8 @@
 
 	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/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c
index b8808fd..51920bc 100644
--- a/lib/efi_loader/efi_variable_tee.c
+++ b/lib/efi_loader/efi_variable_tee.c
@@ -702,7 +702,7 @@
 	if (ret != EFI_SUCCESS)
 		log_err("Can't populate EFI variables. No runtime variables will be available\n");
 	else
-		memcpy(efi_var_buf, var_buf, len);
+		efi_var_buf_update(var_buf);
 	free(var_buf);
 
 	/* Update runtime service table */
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
index 426552b..7d6ea30 100644
--- a/lib/efi_selftest/Makefile
+++ b/lib/efi_selftest/Makefile
@@ -14,6 +14,8 @@
 CFLAGS_REMOVE_efi_selftest_miniapp_exit.o := $(CFLAGS_NON_EFI)
 CFLAGS_efi_selftest_miniapp_return.o := $(CFLAGS_EFI) -Os -ffreestanding
 CFLAGS_REMOVE_efi_selftest_miniapp_return.o := $(CFLAGS_NON_EFI)
+CFLAGS_initrddump_exit.o := $(CFLAGS_EFI) -Os -ffreestanding
+CFLAGS_REMOVE_initrddump.o := $(CFLAGS_NON_EFI)
 
 obj-y += \
 efi_selftest.o \
@@ -78,8 +80,13 @@
 efi_selftest_miniapp_exit.efi \
 efi_selftest_miniapp_return.efi
 
-always += \
-dtbdump.efi
+ifeq ($(CONFIG_GENERATE_ACPI_TABLE),)
+always += dtbdump.efi
+endif
+
+ifdef CONFIG_EFI_LOAD_FILE2_INITRD
+always += initrddump.efi
+endif
 
 $(obj)/efi_miniapp_file_image_exception.h: $(obj)/efi_selftest_miniapp_exception.efi
 	$(obj)/../../tools/file2include $(obj)/efi_selftest_miniapp_exception.efi > \
diff --git a/lib/efi_selftest/dtbdump.c b/lib/efi_selftest/dtbdump.c
index efef759..953b264 100644
--- a/lib/efi_selftest/dtbdump.c
+++ b/lib/efi_selftest/dtbdump.c
@@ -29,6 +29,16 @@
 static const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
 
 /**
+ * print() - print string
+ *
+ * @string:	text
+ */
+static void print(u16 *string)
+{
+	cout->output_string(cout, string);
+}
+
+/**
  * error() - print error string
  *
  * @string:	error text
@@ -36,12 +46,56 @@
 static void error(u16 *string)
 {
 	cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
-	cout->output_string(cout, string);
+	print(string);
 	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
 }
 
 /**
- * input() - read string from console
+ * efi_input_yn() - get answer to yes/no question
+ *
+ * Return:
+ * y or Y
+ *     EFI_SUCCESS
+ * n or N
+ *     EFI_ACCESS_DENIED
+ * ESC
+ *     EFI_ABORTED
+ */
+static efi_status_t efi_input_yn(void)
+{
+	struct efi_input_key key = {0};
+	efi_uintn_t index;
+	efi_status_t ret;
+
+	/* Drain the console input */
+	ret = cin->reset(cin, true);
+	for (;;) {
+		ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
+		if (ret != EFI_SUCCESS)
+			continue;
+		ret = cin->read_key_stroke(cin, &key);
+		if (ret != EFI_SUCCESS)
+			continue;
+		switch (key.scan_code) {
+		case 0x17: /* Escape */
+			return EFI_ABORTED;
+		default:
+			break;
+		}
+		/* Convert to lower case */
+		switch (key.unicode_char | 0x20) {
+		case 'y':
+			return EFI_SUCCESS;
+		case 'n':
+			return EFI_ACCESS_DENIED;
+		default:
+			break;
+		}
+	}
+}
+
+/**
+ * efi_input() - read string from console
  *
  * @buffer:		input buffer
  * @buffer_size:	buffer size
@@ -67,7 +121,7 @@
 			continue;
 		switch (key.scan_code) {
 		case 0x17: /* Escape */
-			cout->output_string(cout, L"\nAborted\n");
+			print(L"\r\nAborted\r\n");
 			return EFI_ABORTED;
 		default:
 			break;
@@ -76,12 +130,12 @@
 		case 0x08: /* Backspace */
 			if (pos) {
 				buffer[pos--] = 0;
-				cout->output_string(cout, L"\b \b");
+				print(L"\b \b");
 			}
 			break;
 		case 0x0a: /* Linefeed */
 		case 0x0d: /* Carriage return */
-			cout->output_string(cout, L"\n");
+			print(L"\r\n");
 			return EFI_SUCCESS;
 		default:
 			break;
@@ -94,7 +148,7 @@
 			*outbuf = key.unicode_char;
 			buffer[pos++] = key.unicode_char;
 			buffer[pos] = 0;
-			cout->output_string(cout, outbuf);
+			print(outbuf);
 		}
 	}
 }
@@ -171,9 +225,9 @@
  */
 void do_help(void)
 {
-	error(L"load <dtb> - load device-tree from file\n");
-	error(L"save <dtb> - save device-tree to file\n");
-	error(L"exit       - exit the shell\n");
+	error(L"load <dtb> - load device-tree from file\r\n");
+	error(L"save <dtb> - save device-tree to file\r\n");
+	error(L"exit       - exit the shell\r\n");
 }
 
 /**
@@ -198,7 +252,7 @@
 	ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL,
 				  (void **)&dt_fixup_prot);
 	if (ret != EFI_SUCCESS) {
-		error(L"Device-tree fix-up protocol not found\n");
+		error(L"Device-tree fix-up protocol not found\r\n");
 		return ret;
 	}
 
@@ -208,7 +262,7 @@
 				(void **)&loaded_image, NULL, NULL,
 				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 	if (ret != EFI_SUCCESS) {
-		error(L"Loaded image protocol not found\n");
+		error(L"Loaded image protocol not found\r\n");
 		return ret;
 	}
 	/* Open the simple file system protocol */
@@ -217,57 +271,57 @@
 				(void **)&file_system, NULL, NULL,
 				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 	if (ret != EFI_SUCCESS) {
-		error(L"Failed to open simple file system protocol\n");
+		error(L"Failed to open simple file system protocol\r\n");
 		goto out;
 	}
 
 	/* Open volume */
 	ret = file_system->open_volume(file_system, &root);
 	if (ret != EFI_SUCCESS) {
-		error(L"Failed to open volume\n");
+		error(L"Failed to open volume\r\n");
 		goto out;
 	}
 
 	/* Open file */
 	ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
 	if (ret != EFI_SUCCESS) {
-		error(L"File not found\n");
+		error(L"File not found\r\n");
 		goto out;
 	}
 	/* Get file size */
 	buffer_size = 0;
 	ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, NULL);
 	if (ret != EFI_BUFFER_TOO_SMALL) {
-		error(L"Can't get file info size\n");
+		error(L"Can't get file info size\r\n");
 		goto out;
 	}
 	ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info);
 	if (ret != EFI_SUCCESS) {
-		error(L"Out of memory\n");
+		error(L"Out of memory\r\n");
 		goto out;
 	}
 	ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info);
 	if (ret != EFI_SUCCESS) {
-		error(L"Can't get file info\n");
+		error(L"Can't get file info\r\n");
 		goto out;
 	}
 	buffer_size = info->file_size;
 	pages = efi_size_in_pages(buffer_size);
 	ret = bs->free_pool(info);
 	if (ret != EFI_SUCCESS)
-		error(L"Can't free memory pool\n");
+		error(L"Can't free memory pool\r\n");
 	/* Read file */
 	ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
 				 EFI_ACPI_RECLAIM_MEMORY,
 				 pages, &addr);
 	if (ret != EFI_SUCCESS) {
-		error(L"Out of memory\n");
+		error(L"Out of memory\r\n");
 		goto out;
 	}
 	dtb = (struct fdt_header *)(uintptr_t)addr;
 	ret = file->read(file, &buffer_size, dtb);
 	if (ret != EFI_SUCCESS) {
-		error(L"Can't read file\n");
+		error(L"Can't read file\r\n");
 		goto out;
 	}
 	/* Fixup file, expecting EFI_BUFFER_TOO_SMALL */
@@ -278,24 +332,24 @@
 		/* Read file into larger buffer */
 		ret = bs->free_pages(addr, pages);
 		if (ret != EFI_SUCCESS)
-			error(L"Can't free memory pages\n");
+			error(L"Can't free memory pages\r\n");
 		pages = efi_size_in_pages(buffer_size);
 		ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
 					 EFI_ACPI_RECLAIM_MEMORY,
 					 pages, &addr);
 		if (ret != EFI_SUCCESS) {
-			error(L"Out of memory\n");
+			error(L"Out of memory\r\n");
 			goto out;
 		}
 		dtb = (struct fdt_header *)(uintptr_t)addr;
 		ret = file->setpos(file, 0);
 		if (ret != EFI_SUCCESS) {
-			error(L"Can't position file\n");
+			error(L"Can't position file\r\n");
 			goto out;
 		}
 		ret = file->read(file, &buffer_size, dtb);
 		if (ret != EFI_SUCCESS) {
-			error(L"Can't read file\n");
+			error(L"Can't read file\r\n");
 			goto out;
 		}
 		buffer_size = pages << EFI_PAGE_SHIFT;
@@ -305,24 +359,24 @@
 				EFI_DT_INSTALL_TABLE);
 	}
 	if (ret == EFI_SUCCESS)
-		cout->output_string(cout, L"device-tree installed\n");
+		print(L"device-tree installed\r\n");
 	else
-		error(L"Device-tree fix-up failed\n");
+		error(L"Device-tree fix-up failed\r\n");
 out:
 	if (addr) {
 		ret2 = bs->free_pages(addr, pages);
 		if (ret2 != EFI_SUCCESS)
-			error(L"Can't free memory pages\n");
+			error(L"Can't free memory pages\r\n");
 	}
 	if (file) {
 		ret2 = file->close(file);
 		if (ret2 != EFI_SUCCESS)
-			error(L"Can't close file\n");
+			error(L"Can't close file\r\n");
 	}
 	if (root) {
 		ret2 = root->close(root);
 		if (ret2 != EFI_SUCCESS)
-			error(L"Can't close volume\n");
+			error(L"Can't close volume\r\n");
 	}
 	return ret;
 }
@@ -344,11 +398,11 @@
 
 	dtb = get_dtb(systable);
 	if (!dtb) {
-		error(L"DTB not found\n");
+		error(L"DTB not found\r\n");
 		return EFI_NOT_FOUND;
 	}
 	if (f2h(dtb->magic) != FDT_MAGIC) {
-		error(L"Wrong device tree magic\n");
+		error(L"Wrong device tree magic\r\n");
 		return EFI_NOT_FOUND;
 	}
 	dtb_size = f2h(dtb->totalsize);
@@ -359,7 +413,7 @@
 				(void **)&loaded_image, NULL, NULL,
 				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 	if (ret != EFI_SUCCESS) {
-		error(L"Loaded image protocol not found\n");
+		error(L"Loaded image protocol not found\r\n");
 		return ret;
 	}
 
@@ -369,16 +423,30 @@
 				(void **)&file_system, NULL, NULL,
 				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 	if (ret != EFI_SUCCESS) {
-		error(L"Failed to open simple file system protocol\n");
+		error(L"Failed to open simple file system protocol\r\n");
 		return ret;
 	}
 
 	/* Open volume */
 	ret = file_system->open_volume(file_system, &root);
 	if (ret != EFI_SUCCESS) {
-		error(L"Failed to open volume\n");
+		error(L"Failed to open volume\r\n");
 		return ret;
 	}
+	/* Check if file already exists */
+	ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
+	if (ret == EFI_SUCCESS) {
+		file->close(file);
+		print(L"Overwrite existing file (y/n)? ");
+		ret = efi_input_yn();
+		print(L"\r\n");
+		if (ret != EFI_SUCCESS) {
+			root->close(root);
+			error(L"Aborted by user\r\n");
+			return ret;
+		}
+	}
+
 	/* Create file */
 	ret = root->open(root, &file, filename,
 			 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
@@ -387,16 +455,16 @@
 		/* Write file */
 		ret = file->write(file, &dtb_size, dtb);
 		if (ret != EFI_SUCCESS)
-			error(L"Failed to write file\n");
+			error(L"Failed to write file\r\n");
 		file->close(file);
 	} else {
-		error(L"Failed to open file\n");
+		error(L"Failed to open file\r\n");
 	}
 	root->close(root);
 
 	if (ret == EFI_SUCCESS) {
-		cout->output_string(cout, filename);
-		cout->output_string(cout, L" written\n");
+		print(filename);
+		print(L" written\r\n");
 	}
 
 	return ret;
@@ -422,7 +490,7 @@
 	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
 	cout->clear_screen(cout);
 	cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK);
-	cout->output_string(cout, L"DTB Dump\n========\n\n");
+	print(L"DTB Dump\r\n========\r\n\r\n");
 	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
 
 	for (;;) {
@@ -430,7 +498,7 @@
 		u16 *pos;
 		efi_uintn_t ret;
 
-		cout->output_string(cout, L"=> ");
+		print(L"=> ");
 		ret = efi_input(command, sizeof(command));
 		if (ret == EFI_ABORTED)
 			break;
diff --git a/lib/efi_selftest/efi_selftest_console.c b/lib/efi_selftest/efi_selftest_console.c
index 0219bd7..ffd88a1 100644
--- a/lib/efi_selftest/efi_selftest_console.c
+++ b/lib/efi_selftest/efi_selftest_console.c
@@ -46,11 +46,10 @@
 /*
  * printx() - print hexadecimal number to an u16 string
  *
- * @pointer:	pointer
+ * @p:		value to print
  * @prec:	minimum number of digits to print
  * @buf:	pointer to buffer address,
  *		on return position of terminating zero word
- * @size:	size of value to be printed in bytes
  */
 static void printx(u64 p, int prec, u16 **buf)
 {
diff --git a/lib/efi_selftest/initrddump.c b/lib/efi_selftest/initrddump.c
new file mode 100644
index 0000000..c23a05c
--- /dev/null
+++ b/lib/efi_selftest/initrddump.c
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * initrddump.efi saves the initial RAM disk provided via the
+ * EFI_LOAD_FILE2_PROTOCOL.
+ */
+
+#include <common.h>
+#include <efi_api.h>
+#include <efi_load_initrd.h>
+
+#define BUFFER_SIZE 64
+#define ESC 0x17
+
+#define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
+
+static struct efi_system_table *systable;
+static struct efi_boot_services *bs;
+static struct efi_simple_text_output_protocol *cerr;
+static struct efi_simple_text_output_protocol *cout;
+static struct efi_simple_text_input_protocol *cin;
+static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
+static const efi_guid_t guid_simple_file_system_protocol =
+					EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+static const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
+static efi_handle_t handle;
+
+/*
+ * 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 initrd_dp = {
+	.vendor = {
+		{
+		   DEVICE_PATH_TYPE_MEDIA_DEVICE,
+		   DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+		   sizeof(initrd_dp.vendor),
+		},
+		EFI_INITRD_MEDIA_GUID,
+	},
+	.end = {
+		DEVICE_PATH_TYPE_END,
+		DEVICE_PATH_SUB_TYPE_END,
+		sizeof(initrd_dp.end),
+	}
+};
+
+/**
+ * print() - print string
+ *
+ * @string:	text
+ */
+static void print(u16 *string)
+{
+	cout->output_string(cout, string);
+}
+
+/**
+ * error() - print error string
+ *
+ * @string:	error text
+ */
+static void error(u16 *string)
+{
+	cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
+	print(string);
+	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
+}
+
+/*
+ * printx() - print hexadecimal number
+ *
+ * @val:	value to print;
+ * @prec:	minimum number of digits to print
+ */
+static void printx(u64 val, u32 prec)
+{
+	int i;
+	u16 c;
+	u16 buf[16];
+	u16 *pos = buf;
+
+	for (i = 2 * sizeof(val) - 1; i >= 0; --i) {
+		c = (val >> (4 * i)) & 0x0f;
+		if (c || pos != buf || !i || i < prec) {
+			c += '0';
+			if (c > '9')
+				c += 'a' - '9' - 1;
+			*pos++ = c;
+		}
+	}
+	*pos = 0;
+	print(buf);
+}
+
+/**
+ * efi_input_yn() - get answer to yes/no question
+ *
+ * Return:
+ * y or Y
+ *     EFI_SUCCESS
+ * n or N
+ *     EFI_ACCESS_DENIED
+ * ESC
+ *     EFI_ABORTED
+ */
+static efi_status_t efi_input_yn(void)
+{
+	struct efi_input_key key = {0};
+	efi_uintn_t index;
+	efi_status_t ret;
+
+	/* Drain the console input */
+	ret = cin->reset(cin, true);
+	for (;;) {
+		ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
+		if (ret != EFI_SUCCESS)
+			continue;
+		ret = cin->read_key_stroke(cin, &key);
+		if (ret != EFI_SUCCESS)
+			continue;
+		switch (key.scan_code) {
+		case 0x17: /* Escape */
+			return EFI_ABORTED;
+		default:
+			break;
+		}
+		/* Convert to lower case */
+		switch (key.unicode_char | 0x20) {
+		case 'y':
+			return EFI_SUCCESS;
+		case 'n':
+			return EFI_ACCESS_DENIED;
+		default:
+			break;
+		}
+	}
+}
+
+/**
+ * efi_input() - read string from console
+ *
+ * @buffer:		input buffer
+ * @buffer_size:	buffer size
+ * Return:		status code
+ */
+static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
+{
+	struct efi_input_key key = {0};
+	efi_uintn_t index;
+	efi_uintn_t pos = 0;
+	u16 outbuf[2] = L" ";
+	efi_status_t ret;
+
+	/* Drain the console input */
+	ret = cin->reset(cin, true);
+	*buffer = 0;
+	for (;;) {
+		ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
+		if (ret != EFI_SUCCESS)
+			continue;
+		ret = cin->read_key_stroke(cin, &key);
+		if (ret != EFI_SUCCESS)
+			continue;
+		switch (key.scan_code) {
+		case 0x17: /* Escape */
+			print(L"\r\nAborted\r\n");
+			return EFI_ABORTED;
+		default:
+			break;
+		}
+		switch (key.unicode_char) {
+		case 0x08: /* Backspace */
+			if (pos) {
+				buffer[pos--] = 0;
+				print(L"\b \b");
+			}
+			break;
+		case 0x0a: /* Linefeed */
+		case 0x0d: /* Carriage return */
+			print(L"\r\n");
+			return EFI_SUCCESS;
+		default:
+			break;
+		}
+		/* Ignore surrogate codes */
+		if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
+			continue;
+		if (key.unicode_char >= 0x20 &&
+		    pos < buffer_size - 1) {
+			*outbuf = key.unicode_char;
+			buffer[pos++] = key.unicode_char;
+			buffer[pos] = 0;
+			print(outbuf);
+		}
+	}
+}
+
+/**
+ * skip_whitespace() - skip over leading whitespace
+ *
+ * @pos:	UTF-16 string
+ * Return:	pointer to first non-whitespace
+ */
+static u16 *skip_whitespace(u16 *pos)
+{
+	for (; *pos && *pos <= 0x20; ++pos)
+		;
+	return pos;
+}
+
+/**
+ * starts_with() - check if @string starts with @keyword
+ *
+ * @string:	string to search for keyword
+ * @keyword:	keyword to be searched
+ * Return:	true fi @string starts with the keyword
+ */
+static bool starts_with(u16 *string, u16 *keyword)
+{
+	for (; *keyword; ++string, ++keyword) {
+		if (*string != *keyword)
+			return false;
+	}
+	return true;
+}
+
+/**
+ * do_help() - print help
+ */
+static void do_help(void)
+{
+	error(L"load          - show length and CRC32 of initial RAM disk\r\n");
+	error(L"save <initrd> - save initial RAM disk to file\r\n");
+	error(L"exit          - exit the shell\r\n");
+}
+
+/**
+ * get_initrd() - read initial RAM disk via EFI_LOAD_FILE2_PROTOCOL
+ *
+ * @initrd:		on return buffer with initial RAM disk
+ * @initrd_size:	size of initial RAM disk
+ * Return:		status code
+ */
+static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size)
+{
+	struct efi_device_path *dp = (struct efi_device_path *)&initrd_dp;
+	struct efi_load_file_protocol *load_file2_prot;
+	u64 buffer;
+	efi_handle_t handle;
+	efi_status_t ret;
+
+	*initrd = NULL;
+	*initrd_size = 0;
+	ret = bs->locate_device_path(&load_file2_guid, &dp, &handle);
+	if (ret != EFI_SUCCESS) {
+		error(L"Load File2 protocol not found\r\n");
+		return ret;
+	}
+	ret = bs->handle_protocol(handle, &load_file2_guid,
+				 (void **)&load_file2_prot);
+	ret = load_file2_prot->load_file(load_file2_prot, dp, false,
+					 initrd_size, NULL);
+	if (ret != EFI_BUFFER_TOO_SMALL) {
+		error(L"Load File2 protocol does not provide file length\r\n");
+		return EFI_LOAD_ERROR;
+	}
+	ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA,
+				 efi_size_in_pages(*initrd_size), &buffer);
+	if (ret != EFI_SUCCESS) {
+		error(L"Out of memory\r\n");
+		return ret;
+	}
+	*initrd = (void *)buffer;
+	ret = load_file2_prot->load_file(load_file2_prot, dp, false,
+					 initrd_size, *initrd);
+	if (ret != EFI_SUCCESS) {
+		error(L"Load File2 protocol failed to provide file\r\n");
+		bs->free_pages(buffer, efi_size_in_pages(*initrd_size));
+		return EFI_LOAD_ERROR;
+	}
+	return ret;
+}
+
+/**
+ * do_load() - load initial RAM disk and display CRC32 and length
+ *
+ * @filename:	file name
+ * Return:	status code
+ */
+static efi_status_t do_load(void)
+{
+	void *initrd;
+	efi_uintn_t initrd_size;
+	u32 crc32;
+	efi_uintn_t ret;
+
+	ret =  get_initrd(&initrd, &initrd_size);
+	if (ret != EFI_SUCCESS)
+		return ret;
+	print(L"length: 0x");
+	printx(initrd_size, 1);
+	print(L"\r\n");
+
+	ret = bs->calculate_crc32(initrd, initrd_size, &crc32);
+	if (ret != EFI_SUCCESS) {
+		error(L"Calculating CRC32 failed\r\n");
+		return EFI_LOAD_ERROR;
+	}
+	print(L"crc32: 0x");
+	printx(crc32, 8);
+	print(L"\r\n");
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * do_save() - save initial RAM disk
+ *
+ * @filename:	file name
+ * Return:	status code
+ */
+static efi_status_t do_save(u16 *filename)
+{
+	struct efi_loaded_image *loaded_image;
+	struct efi_simple_file_system_protocol *file_system;
+	struct efi_file_handle *root, *file;
+	void *initrd;
+	efi_uintn_t initrd_size;
+	efi_uintn_t ret;
+
+	ret = get_initrd(&initrd, &initrd_size);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	filename = skip_whitespace(filename);
+
+	ret = bs->open_protocol(handle, &loaded_image_guid,
+				(void **)&loaded_image, NULL, NULL,
+				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+	if (ret != EFI_SUCCESS) {
+		error(L"Loaded image protocol not found\r\n");
+		goto out;
+	}
+
+	/* Open the simple file system protocol */
+	ret = bs->open_protocol(loaded_image->device_handle,
+				&guid_simple_file_system_protocol,
+				(void **)&file_system, NULL, NULL,
+				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+	if (ret != EFI_SUCCESS) {
+		error(L"Failed to open simple file system protocol\r\n");
+		goto out;
+	}
+
+	/* Open volume */
+	ret = file_system->open_volume(file_system, &root);
+	if (ret != EFI_SUCCESS) {
+		error(L"Failed to open volume\r\n");
+		goto out;
+	}
+	/* Check if file already exists */
+	ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
+	if (ret == EFI_SUCCESS) {
+		file->close(file);
+		print(L"Overwrite existing file (y/n)? ");
+		ret = efi_input_yn();
+		print(L"\r\n");
+		if (ret != EFI_SUCCESS) {
+			root->close(root);
+			error(L"Aborted by user\r\n");
+			goto out;
+		}
+	}
+
+	/* Create file */
+	ret = root->open(root, &file, filename,
+			 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
+			 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
+	if (ret == EFI_SUCCESS) {
+		/* Write file */
+		ret = file->write(file, &initrd_size, initrd);
+		if (ret != EFI_SUCCESS) {
+			error(L"Failed to write file\r\n");
+		} else {
+			print(filename);
+			print(L" written\r\n");
+		}
+		file->close(file);
+	} else {
+		error(L"Failed to open file\r\n");
+	}
+	root->close(root);
+
+out:
+	if (initrd)
+		bs->free_pages((uintptr_t)initrd,
+			       efi_size_in_pages(initrd_size));
+	return ret;
+}
+
+/**
+ * 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 image_handle,
+			     struct efi_system_table *systab)
+{
+	handle = image_handle;
+	systable = systab;
+	cerr = systable->std_err;
+	cout = systable->con_out;
+	cin = systable->con_in;
+	bs = systable->boottime;
+
+	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
+	cout->clear_screen(cout);
+	cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK);
+	print(L"INITRD Dump\r\n========\r\n\r\n");
+	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
+
+	for (;;) {
+		u16 command[BUFFER_SIZE];
+		u16 *pos;
+		efi_uintn_t ret;
+
+		print(L"=> ");
+		ret = efi_input(command, sizeof(command));
+		if (ret == EFI_ABORTED)
+			break;
+		pos = skip_whitespace(command);
+		if (starts_with(pos, L"exit"))
+			break;
+		else if (starts_with(pos, L"load"))
+			do_load();
+		else if (starts_with(pos, L"save "))
+			do_save(pos + 5);
+		else
+			do_help();
+	}
+
+	cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
+	cout->clear_screen(cout);
+	return EFI_SUCCESS;
+}