Selaa lähdekoodia

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

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

In the UEFI sub-system:

* implement non-blocking file services
* print boot device and file path in helloworld.efi
* improve detection of boot device
* correct argument handling in efivar.py
* implement EFI_DT_FIXUP_PROTOCOL

Bug fixes:

* adjust conitrace command for low baud rates
* check that FIT images are valid FDTs
Tom Rini 3 vuotta sitten
vanhempi
commit
35772ff4f6

+ 88 - 146
cmd/bootefi.c

@@ -29,6 +29,82 @@ DECLARE_GLOBAL_DATA_PTR;
 
 static struct efi_device_path *bootefi_image_path;
 static struct efi_device_path *bootefi_device_path;
+static void *image_addr;
+static size_t image_size;
+
+/**
+ * efi_clear_bootdev() - clear boot device
+ */
+static void efi_clear_bootdev(void)
+{
+	efi_free_pool(bootefi_device_path);
+	efi_free_pool(bootefi_image_path);
+	bootefi_device_path = NULL;
+	bootefi_image_path = NULL;
+	image_addr = NULL;
+	image_size = 0;
+}
+
+/**
+ * efi_set_bootdev() - set boot device
+ *
+ * This function is called when a file is loaded, e.g. via the 'load' command.
+ * We use the path to this file to inform the UEFI binary about the boot device.
+ *
+ * @dev:		device, e.g. "MMC"
+ * @devnr:		number of the device, e.g. "1:2"
+ * @path:		path to file loaded
+ * @buffer:		buffer with file loaded
+ * @buffer_size:	size of file loaded
+ */
+void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
+		     void *buffer, size_t buffer_size)
+{
+	struct efi_device_path *device, *image;
+	efi_status_t ret;
+
+	/* Forget overwritten image */
+	if (buffer + buffer_size >= image_addr &&
+	    image_addr + image_size >= buffer)
+		efi_clear_bootdev();
+
+	/* Remember only PE-COFF and FIT images */
+	if (efi_check_pe(buffer, buffer_size, NULL) != EFI_SUCCESS) {
+#ifdef CONFIG_FIT
+		if (!fit_check_format(buffer))
+			return;
+		/*
+		 * FIT images of type EFI_OS are started via command bootm.
+		 * We should not use their boot device with the bootefi command.
+		 */
+		buffer = 0;
+		buffer_size = 0;
+#else
+		return;
+#endif
+	}
+
+	/* efi_set_bootdev() is typically called repeatedly, recover memory */
+	efi_clear_bootdev();
+
+	image_addr = buffer;
+	image_size = buffer_size;
+
+	ret = efi_dp_from_name(dev, devnr, path, &device, &image);
+	if (ret == EFI_SUCCESS) {
+		bootefi_device_path = device;
+		if (image) {
+			/* FIXME: image should not contain device */
+			struct efi_device_path *image_tmp = image;
+
+			efi_dp_split_file_path(image, &device, &image);
+			efi_free_pool(image_tmp);
+		}
+		bootefi_image_path = image;
+	} else {
+		efi_clear_bootdev();
+	}
+}
 
 /**
  * efi_env_set_load_options() - set load options from environment variable
@@ -135,86 +211,6 @@ done:
 	return ret;
 }
 
-/**
- * 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
- */
-static 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, false);
-	}
-
-	/* 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);
-		}
-	}
-}
-
 /**
  * get_config_table() - get configuration table
  *
@@ -398,43 +394,28 @@ static int do_bootefi_image(const char *image_opt)
 {
 	void *image_buf;
 	unsigned long addr, size;
-	const char *size_str;
 	efi_status_t ret;
 
 #ifdef CONFIG_CMD_BOOTEFI_HELLO
 	if (!strcmp(image_opt, "hello")) {
-		char *saddr;
-
-		saddr = env_get("loadaddr");
+		image_buf = __efi_helloworld_begin;
 		size = __efi_helloworld_end - __efi_helloworld_begin;
-
-		if (saddr)
-			addr = simple_strtoul(saddr, NULL, 16);
-		else
-			addr = CONFIG_SYS_LOAD_ADDR;
-
-		image_buf = map_sysmem(addr, size);
-		memcpy(image_buf, __efi_helloworld_begin, size);
-
-		efi_free_pool(bootefi_device_path);
-		efi_free_pool(bootefi_image_path);
-		bootefi_device_path = NULL;
-		bootefi_image_path = NULL;
+		efi_clear_bootdev();
 	} else
 #endif
 	{
-		size_str = env_get("filesize");
-		if (size_str)
-			size = simple_strtoul(size_str, NULL, 16);
-		else
-			size = 0;
-
-		addr = simple_strtoul(image_opt, NULL, 16);
+		addr = strtoul(image_opt, NULL, 16);
 		/* Check that a numeric value was passed */
-		if (!addr && *image_opt != '0')
+		if (!addr)
 			return CMD_RET_USAGE;
 
-		image_buf = map_sysmem(addr, size);
+		image_buf = map_sysmem(addr, 0);
+
+		if (image_buf != image_addr) {
+			log_err("No UEFI binary known at %s\n", image_opt);
+			return CMD_RET_FAILURE;
+		}
+		size = image_size;
 	}
 	ret = efi_run_image(image_buf, size);
 
@@ -567,11 +548,8 @@ static efi_status_t bootefi_test_prepare
 	if (ret == EFI_SUCCESS)
 		return ret;
 
-	efi_free_pool(bootefi_image_path);
-	bootefi_image_path = NULL;
 failure:
-	efi_free_pool(bootefi_device_path);
-	bootefi_device_path = NULL;
+	efi_clear_bootdev();
 	return ret;
 }
 
@@ -691,39 +669,3 @@ U_BOOT_CMD(
 	"Boots an EFI payload from memory",
 	bootefi_help_text
 );
-
-/**
- * efi_set_bootdev() - set boot device
- *
- * This function is called when a file is loaded, e.g. via the 'load' command.
- * We use the path to this file to inform the UEFI binary about the boot device.
- *
- * @dev:	device, e.g. "MMC"
- * @devnr:	number of the device, e.g. "1:2"
- * @path:	path to file loaded
- */
-void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
-{
-	struct efi_device_path *device, *image;
-	efi_status_t ret;
-
-	/* efi_set_bootdev is typically called repeatedly, recover memory */
-	efi_free_pool(bootefi_device_path);
-	efi_free_pool(bootefi_image_path);
-
-	ret = efi_dp_from_name(dev, devnr, path, &device, &image);
-	if (ret == EFI_SUCCESS) {
-		bootefi_device_path = device;
-		if (image) {
-			/* FIXME: image should not contain device */
-			struct efi_device_path *image_tmp = image;
-
-			efi_dp_split_file_path(image, &device, &image);
-			efi_free_pool(image_tmp);
-		}
-		bootefi_image_path = image;
-	} else {
-		bootefi_device_path = NULL;
-		bootefi_image_path = NULL;
-	}
-}

+ 2 - 2
cmd/conitrace.c

@@ -30,8 +30,8 @@ static int do_conitrace(struct cmd_tbl *cmdtp, int flag, int argc,
 		printf("%02x ", c);
 		first = false;
 
-		/* 1 ms delay - serves to detect separate keystrokes */
-		udelay(1000);
+		/* 10 ms delay - serves to detect separate keystrokes */
+		udelay(10000);
 		if (!tstc()) {
 			printf("\n");
 			first = true;

+ 5 - 0
cmd/efidebug.c

@@ -8,6 +8,7 @@
 #include <charset.h>
 #include <common.h>
 #include <command.h>
+#include <efi_dt_fixup.h>
 #include <efi_loader.h>
 #include <efi_rng.h>
 #include <exports.h>
@@ -495,6 +496,10 @@ static const struct {
 		"PXE Base Code",
 		EFI_PXE_BASE_CODE_PROTOCOL_GUID,
 	},
+	{
+		"Device-Tree Fixup",
+		EFI_DT_FIXUP_PROTOCOL_GUID,
+	},
 	/* Configuration table GUIDs */
 	{
 		"ACPI table",

+ 6 - 0
common/image-fit.c

@@ -1553,6 +1553,12 @@ int fit_image_check_comp(const void *fit, int noffset, uint8_t comp)
  */
 int fit_check_format(const void *fit)
 {
+	/* A FIT image must be a valid FDT */
+	if (fdt_check_header(fit)) {
+		debug("Wrong FIT format: not a flattened device tree\n");
+		return 0;
+	}
+
 	/* mandatory / node 'description' property */
 	if (fdt_getprop(fit, 0, FIT_DESC_PROP, NULL) == NULL) {
 		debug("Wrong FIT format: no description\n");

+ 4 - 7
doc/uefi/uefi.rst

@@ -59,13 +59,10 @@ Below you find the output of an example session starting GRUB::
     120832 bytes read in 7 ms (16.5 MiB/s)
     => bootefi ${kernel_addr_r} ${fdt_addr_r}
 
-The bootefi command uses the device, the file name, and the file size
-(environment variable 'filesize') of the most recently loaded file when setting
-up the binary for execution. So the UEFI binary should be loaded last.
-
-The environment variable 'bootargs' is passed as load options in the UEFI system
-table. The Linux kernel EFI stub uses the load options as command line
-arguments.
+When booting from a memory location it is unknown from which file it was loaded.
+Therefore the bootefi command uses the device path of the block device partition
+or the network adapter and the file name of the most recently loaded PE-COFF
+file when setting up the loaded image protocol.
 
 Launching a UEFI binary from a FIT image
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 2 - 1
fs/fs.c

@@ -752,7 +752,8 @@ int do_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
 
 	if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
 		efi_set_bootdev(argv[1], (argc > 2) ? argv[2] : "",
-				(argc > 4) ? argv[4] : "");
+				(argc > 4) ? argv[4] : "", map_sysmem(addr, 0),
+				len_read);
 
 	printf("%llu bytes read in %lu ms", len_read, time);
 	if (time > 0) {

+ 5 - 0
include/efi.h

@@ -20,6 +20,11 @@
 #include <linux/string.h>
 #include <linux/types.h>
 
+/* Type INTN in UEFI specification */
+#define efi_intn_t ssize_t
+/* Type UINTN in UEFI specification*/
+#define efi_uintn_t size_t
+
 /*
  * EFI on x86_64 uses the Microsoft ABI which is not the default for GCC.
  *

+ 16 - 18
include/efi_api.h

@@ -34,8 +34,6 @@ enum efi_timer_delay {
 	EFI_TIMER_RELATIVE = 2
 };
 
-#define efi_intn_t ssize_t
-#define efi_uintn_t size_t
 typedef void *efi_hii_handle_t;
 typedef u16 *efi_string_t;
 typedef u16 efi_string_id_t;
@@ -703,10 +701,10 @@ struct efi_simple_text_output_protocol {
 			char extended_verification);
 	efi_status_t (EFIAPI *output_string)(
 			struct efi_simple_text_output_protocol *this,
-			const efi_string_t str);
+			const u16 *str);
 	efi_status_t (EFIAPI *test_string)(
 			struct efi_simple_text_output_protocol *this,
-			const efi_string_t str);
+			const u16 *str);
 	efi_status_t(EFIAPI *query_mode)(
 			struct efi_simple_text_output_protocol *this,
 			unsigned long mode_number, unsigned long *columns,
@@ -1589,35 +1587,35 @@ struct efi_file_io_token {
 
 struct efi_file_handle {
 	u64 rev;
-	efi_status_t (EFIAPI *open)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *open)(struct efi_file_handle *this,
 			struct efi_file_handle **new_handle,
 			u16 *file_name, u64 open_mode, u64 attributes);
-	efi_status_t (EFIAPI *close)(struct efi_file_handle *file);
-	efi_status_t (EFIAPI *delete)(struct efi_file_handle *file);
-	efi_status_t (EFIAPI *read)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *close)(struct efi_file_handle *this);
+	efi_status_t (EFIAPI *delete)(struct efi_file_handle *this);
+	efi_status_t (EFIAPI *read)(struct efi_file_handle *this,
 			efi_uintn_t *buffer_size, void *buffer);
-	efi_status_t (EFIAPI *write)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *write)(struct efi_file_handle *this,
 			efi_uintn_t *buffer_size, void *buffer);
-	efi_status_t (EFIAPI *getpos)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *getpos)(struct efi_file_handle *this,
 				      u64 *pos);
-	efi_status_t (EFIAPI *setpos)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *setpos)(struct efi_file_handle *this,
 				      u64 pos);
-	efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *this,
 			const efi_guid_t *info_type, efi_uintn_t *buffer_size,
 			void *buffer);
-	efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *this,
 			const efi_guid_t *info_type, efi_uintn_t buffer_size,
 			void *buffer);
-	efi_status_t (EFIAPI *flush)(struct efi_file_handle *file);
-	efi_status_t (EFIAPI *open_ex)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *flush)(struct efi_file_handle *this);
+	efi_status_t (EFIAPI *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 (EFIAPI *read_ex)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *read_ex)(struct efi_file_handle *this,
 			struct efi_file_io_token *token);
-	efi_status_t (EFIAPI *write_ex)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *write_ex)(struct efi_file_handle *this,
 			struct efi_file_io_token *token);
-	efi_status_t (EFIAPI *flush_ex)(struct efi_file_handle *file,
+	efi_status_t (EFIAPI *flush_ex)(struct efi_file_handle *this,
 			struct efi_file_io_token *token);
 };
 

+ 39 - 0
include/efi_dt_fixup.h

@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * EFI_DT_FIXUP_PROTOCOL
+ *
+ * Copyright (c) 2020 Heinrich Schuchardt
+ */
+
+#include <efi_api.h>
+
+#define EFI_DT_FIXUP_PROTOCOL_GUID \
+	EFI_GUID(0xe617d64c, 0xfe08, 0x46da, 0xf4, 0xdc, \
+		 0xbb, 0xd5, 0x87, 0x0c, 0x73, 0x00)
+
+#define EFI_DT_FIXUP_PROTOCOL_REVISION 0x00010000
+
+/* Add nodes and update properties */
+#define EFI_DT_APPLY_FIXUPS    0x00000001
+/*
+ * Reserve memory according to the /reserved-memory node
+ * and the memory reservation block
+ */
+#define EFI_DT_RESERVE_MEMORY  0x00000002
+/* Install the device-tree as configuration table */
+#define EFI_DT_INSTALL_TABLE   0x00000004
+
+#define EFI_DT_ALL (EFI_DT_APPLY_FIXUPS | \
+		    EFI_DT_RESERVE_MEMORY | \
+		    EFI_DT_INSTALL_TABLE)
+
+struct efi_dt_fixup_protocol {
+	u64 revision;
+	efi_status_t (EFIAPI *fixup) (struct efi_dt_fixup_protocol *this,
+				      void *dtb,
+				      efi_uintn_t *buffer_size,
+				      u32 flags);
+};
+
+extern struct efi_dt_fixup_protocol efi_dt_fixup_prot;
+extern const efi_guid_t efi_guid_dt_fixup_protocol;

+ 8 - 2
include/efi_loader.h

@@ -411,6 +411,8 @@ void efi_runtime_detach(void);
 /* efi_convert_pointer() - convert pointer to virtual address */
 efi_status_t EFIAPI efi_convert_pointer(efi_uintn_t debug_disposition,
 					void **address);
+/* Carve out DT reserved memory ranges */
+void efi_carve_out_dt_rsv(void *fdt);
 /* Called by bootefi to make console interface available */
 efi_status_t efi_console_register(void);
 /* Called by bootefi to make all disk storage accessible as EFI objects */
@@ -460,6 +462,8 @@ efi_status_t efi_set_watchdog(unsigned long timeout);
 
 /* Called from places to check whether a timer expired */
 void efi_timer_check(void);
+/* Check if a buffer contains a PE-COFF image */
+efi_status_t efi_check_pe(void *buffer, size_t size, void **nt_header);
 /* PE loader implementation */
 efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
 			 void *efi, size_t efi_size,
@@ -472,7 +476,8 @@ void efi_restore_gd(void);
 /* Call this to relocate the runtime section to an address space */
 void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map);
 /* Call this to set the current device name */
-void efi_set_bootdev(const char *dev, const char *devnr, const char *path);
+void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
+		     void *buffer, size_t buffer_size);
 /* Add a new object to the object list. */
 void efi_add_handle(efi_handle_t obj);
 /* Create handle */
@@ -871,7 +876,8 @@ static inline efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
 /* No loader configured, stub out EFI_ENTRY */
 static inline void efi_restore_gd(void) { }
 static inline void efi_set_bootdev(const char *dev, const char *devnr,
-				   const char *path) { }
+				   const char *path, void *buffer,
+				   size_t buffer_size) { }
 static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
 static inline void efi_print_image_infos(void *pc) { }
 static inline efi_status_t efi_launch_capsules(void)

+ 3 - 0
lib/efi_loader/Makefile

@@ -29,6 +29,9 @@ 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
+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

+ 3 - 3
lib/efi_loader/efi_console.c

@@ -141,12 +141,12 @@ static int term_read_reply(int *n, int num, char end_char)
  */
 static efi_status_t EFIAPI efi_cout_output_string(
 			struct efi_simple_text_output_protocol *this,
-			const efi_string_t string)
+			const u16 *string)
 {
 	struct simple_text_output_mode *con = &efi_con_mode;
 	struct cout_mode *mode = &efi_cout_modes[con->mode];
 	char *buf, *pos;
-	u16 *p;
+	const u16 *p;
 	efi_status_t ret = EFI_SUCCESS;
 
 	EFI_ENTRY("%p, %p", this, string);
@@ -230,7 +230,7 @@ out:
  */
 static efi_status_t EFIAPI efi_cout_test_string(
 			struct efi_simple_text_output_protocol *this,
-			const efi_string_t string)
+			const u16 *string)
 {
 	EFI_ENTRY("%p, %p", this, string);
 	return EFI_EXIT(EFI_SUCCESS);

+ 160 - 0
lib/efi_loader/efi_dt_fixup.c

@@ -0,0 +1,160 @@
+// 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 <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;
+
+/**
+ * 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, false);
+	}
+
+	/* 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);
+		}
+	}
+}
+
+static efi_status_t 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;
+	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) {
+		required_size = fdt_off_dt_strings(dtb) +
+				fdt_size_dt_strings(dtb) +
+				0x3000;
+	} else {
+		required_size = fdt_totalsize(dtb);
+	}
+	if (required_size > *buffer_size) {
+		*buffer_size = required_size;
+		ret = EFI_BUFFER_TOO_SMALL;
+		goto out;
+	}
+	fdt_set_totalsize(dtb, *buffer_size);
+
+	if (flags & EFI_DT_APPLY_FIXUPS) {
+		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 (EFI_DT_INSTALL_TABLE) {
+		ret = efi_install_configuration_table(&efi_guid_fdt, dtb);
+		if (ret != EFI_SUCCESS) {
+			log_err("ERROR: failed to install device tree\n");
+			goto out;
+		}
+	}
+
+	ret = EFI_SUCCESS;
+out:
+	return EFI_EXIT(ret);
+}

+ 266 - 51
lib/efi_loader/efi_file.c

@@ -246,18 +246,16 @@ error:
 	return NULL;
 }
 
-static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
-		struct efi_file_handle **new_handle,
-		u16 *file_name, u64 open_mode, u64 attributes)
+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(file);
+	struct file_handle *fh = to_fh(this);
 	efi_status_t ret;
 
-	EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle,
-		  file_name, open_mode, attributes);
-
 	/* Check parameters */
-	if (!file || !new_handle || !file_name) {
+	if (!this || !new_handle || !file_name) {
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
@@ -291,6 +289,75 @@ static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
 	} 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);
 }
@@ -441,19 +508,15 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
 	return EFI_SUCCESS;
 }
 
-static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
-					 efi_uintn_t *buffer_size, void *buffer)
+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(file);
+	struct file_handle *fh = to_fh(this);
 	efi_status_t ret = EFI_SUCCESS;
 	u64 bs;
 
-	EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
-
-	if (!buffer_size) {
-		ret = EFI_INVALID_PARAMETER;
-		goto error;
-	}
+	if (!this || !buffer_size || !buffer)
+		return EFI_INVALID_PARAMETER;
 
 	bs = *buffer_size;
 	if (fh->isdir)
@@ -465,34 +528,77 @@ static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
 	else
 		*buffer_size = SIZE_MAX;
 
-error:
+	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_write() - write to file
+ * efi_file_read_ex() - read file asynchonously
  *
- * This function implements the Write() service of the EFI_FILE_PROTOCOL.
+ * This function implements the ReadEx() service of the EFI_FILE_PROTOCOL.
  *
  * See the Unified Extensible Firmware Interface (UEFI) specification for
  * details.
  *
- * @file:		file handle
- * @buffer_size:	number of bytes to write
- * @buffer:		buffer with the bytes to write
+ * @this:		file protocol instance
+ * @token:		transaction token
  * Return:		status code
  */
-static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
-					  efi_uintn_t *buffer_size,
-					  void *buffer)
+static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *this,
+					    struct efi_file_io_token *token)
 {
-	struct file_handle *fh = to_fh(file);
+	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;
 
-	EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
-
-	if (!file || !buffer_size || !buffer) {
+	if (!this || !buffer_size || !buffer) {
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
@@ -520,6 +626,67 @@ static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
 	*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);
 }
@@ -761,36 +928,84 @@ out:
 	return EFI_EXIT(ret);
 }
 
-static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
+/**
+ * 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)
 {
-	EFI_ENTRY("%p", file);
-	return EFI_EXIT(EFI_SUCCESS);
-}
+	struct file_handle *fh = to_fh(this);
 
-static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *file,
-			struct efi_file_handle **new_handle,
-			u16 *file_name, u64 open_mode, u64 attributes,
-			struct efi_file_io_token *token)
-{
-	return EFI_UNSUPPORTED;
-}
+	if (!this)
+		return EFI_INVALID_PARAMETER;
 
-static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *file,
-			struct efi_file_io_token *token)
-{
-	return EFI_UNSUPPORTED;
+	if (!(fh->open_mode & EFI_FILE_MODE_WRITE))
+		return EFI_ACCESS_DENIED;
+
+	/* TODO: flush for file position after end of file */
+	return EFI_SUCCESS;
 }
 
-static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *file,
-			struct efi_file_io_token *token)
+/**
+ * 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)
 {
-	return EFI_UNSUPPORTED;
+	efi_status_t ret;
+
+	EFI_ENTRY("%p", this);
+
+	ret = efi_file_flush_int(this);
+
+	return EFI_EXIT(ret);
 }
 
-static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *file,
-			struct efi_file_io_token *token)
+/**
+ * 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)
 {
-	return EFI_UNSUPPORTED;
+	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 = {

+ 46 - 34
lib/efi_loader/efi_image_loader.c

@@ -675,6 +675,46 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
 }
 #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
  *
@@ -705,36 +745,10 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
 	int supported = 0;
 	efi_status_t ret;
 
-	/* Sanity check for a file header */
-	if (efi_size < sizeof(*dos)) {
-		log_err("Truncated DOS Header\n");
-		ret = EFI_LOAD_ERROR;
-		goto err;
-	}
-
-	dos = efi;
-	if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
-		log_err("Invalid DOS Signature\n");
-		ret = EFI_LOAD_ERROR;
-		goto err;
-	}
-
-	/*
-	 * 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 (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS64)) {
-		log_err("Invalid offset for Extended Header\n");
-		ret = EFI_LOAD_ERROR;
-		goto err;
-	}
-
-	nt = (void *) ((char *)efi + dos->e_lfanew);
-	if (nt->Signature != IMAGE_NT_SIGNATURE) {
-		log_err("Invalid NT Signature\n");
-		ret = EFI_LOAD_ERROR;
-		goto err;
+	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++)
@@ -746,8 +760,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
 	if (!supported) {
 		log_err("Machine type 0x%04x is not supported\n",
 			nt->FileHeader.Machine);
-		ret = EFI_LOAD_ERROR;
-		goto err;
+		return EFI_LOAD_ERROR;
 	}
 
 	num_sections = nt->FileHeader.NumberOfSections;
@@ -757,8 +770,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
 	if (efi_size < ((void *)sections + sizeof(sections[0]) * num_sections
 			- efi)) {
 		log_err("Invalid number of sections: %d\n", num_sections);
-		ret = EFI_LOAD_ERROR;
-		goto err;
+		return EFI_LOAD_ERROR;
 	}
 
 	/* Authenticate an image */

+ 0 - 2
lib/efi_loader/efi_memory.c

@@ -541,8 +541,6 @@ efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
 
 	ret = efi_add_memory_map_pg(memory, pages, EFI_CONVENTIONAL_MEMORY,
 				    false);
-	/* Merging of adjacent free regions is missing */
-
 	if (ret != EFI_SUCCESS)
 		return EFI_NOT_FOUND;
 

+ 6 - 0
lib/efi_loader/efi_root_node.c

@@ -7,6 +7,7 @@
 
 #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;
@@ -60,6 +61,11 @@ efi_status_t efi_root_node_register(void)
 			 /* Device path utilities protocol */
 			 &efi_guid_device_path_utilities_protocol,
 			 (void *)&efi_device_path_utilities,
+#if !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE)
+			 /* 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 */

+ 137 - 30
lib/efi_loader/helloworld.c

@@ -1,43 +1,41 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * EFI hello world
+ * Hello world EFI application
  *
- * Copyright (c) 2016 Google, Inc
- * Written by Simon Glass <sjg@chromium.org>
+ * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
  *
- * This program demonstrates calling a boottime service.
- * It writes a greeting and the load options to the console.
+ * 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 <common.h>
 #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;
+
 /**
- * efi_main() - entry point of the EFI application.
- *
- * @handle:	handle of the loaded image
- * @systable:	system table
- * @return:	status code
+ * print_uefi_revision() - print UEFI revision number
  */
-efi_status_t EFIAPI efi_main(efi_handle_t handle,
-			     struct efi_system_table *systable)
+static void print_uefi_revision(void)
 {
-	struct efi_simple_text_output_protocol *con_out = systable->con_out;
-	struct efi_boot_services *boottime = systable->boottime;
-	struct efi_loaded_image *loaded_image;
-	efi_status_t ret;
-	efi_uintn_t i;
 	u16 rev[] = L"0.0.0";
 
-	/* UEFI requires CR LF */
-	con_out->output_string(con_out, L"Hello, world!\r\n");
-
-	/* Print the revision number */
 	rev[0] = (systable->hdr.revision >> 16) + '0';
 	rev[4] = systable->hdr.revision & 0xffff;
 	for (; rev[4] >= 10;) {
@@ -53,15 +51,15 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,
 	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;
 
-	/* 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;
-	}
 	/* Find configuration tables */
 	for (i = 0; i < systable->nr_tables; ++i) {
 		if (!memcmp(&systable->tables[i].guid, &fdt_guid,
@@ -77,6 +75,16 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,
 			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)
@@ -85,6 +93,105 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,
 	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 devide 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);

+ 273 - 37
lib/efi_selftest/dtbdump.c

@@ -8,10 +8,12 @@
 
 #include <common.h>
 #include <efi_api.h>
+#include <efi_dt_fixup.h>
 
 #define BUFFER_SIZE 64
 #define ESC 0x17
-#define DEFAULT_FILENAME L"dtb.dtb"
+
+#define efi_size_in_pages(size) ((size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
 
 static struct efi_simple_text_output_protocol *cerr;
 static struct efi_simple_text_output_protocol *cout;
@@ -21,6 +23,22 @@ static const efi_guid_t fdt_guid = EFI_FDT_GUID;
 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 efi_handle_t handle;
+static struct efi_system_table *systable;
+static const efi_guid_t efi_dt_fixup_protocol_guid = EFI_DT_FIXUP_PROTOCOL_GUID;
+static const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
+
+/**
+ * error() - print error string
+ *
+ * @string:	error text
+ */
+static void error(u16 *string)
+{
+	cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
+	cout->output_string(cout, string);
+	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
+}
 
 /**
  * input() - read string from console
@@ -39,6 +57,7 @@ static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
 
 	/* 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)
@@ -62,6 +81,7 @@ static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
 			break;
 		case 0x0a: /* Linefeed */
 		case 0x0d: /* Carriage return */
+			cout->output_string(cout, L"\n");
 			return EFI_SUCCESS;
 		default:
 			break;
@@ -73,6 +93,7 @@ static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
 		    pos < buffer_size - 1) {
 			*outbuf = key.unicode_char;
 			buffer[pos++] = key.unicode_char;
+			buffer[pos] = 0;
 			cout->output_string(cout, outbuf);
 		}
 	}
@@ -117,60 +138,228 @@ void *get_dtb(struct efi_system_table *systable)
 }
 
 /**
- * efi_main() - entry point of the EFI application.
+ * skip_whitespace() - skip over leading whitespace
  *
- * @handle:	handle of the loaded image
- * @systable:	system table
- * @return:	status code
+ * @pos:	UTF-16 string
+ * Return:	pointer to first non-whitespace
  */
-efi_status_t EFIAPI efi_main(efi_handle_t handle,
-			     struct efi_system_table *systable)
+u16 *skip_whitespace(u16 *pos)
 {
-	efi_uintn_t ret;
-	u16 filename[BUFFER_SIZE] = {0};
-	efi_uintn_t dtb_size;
+	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
+ */
+bool starts_with(u16 *string, u16 *keyword)
+{
+	for (; *keyword; ++string, ++keyword) {
+		if (*string != *keyword)
+			return false;
+	}
+	return true;
+}
+
+/**
+ * do_help() - print help
+ */
+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");
+}
+
+/**
+ * do_load() - load and install device-tree
+ *
+ * @filename:	file name
+ * Return:	status code
+ */
+efi_status_t do_load(u16 *filename)
+{
+	struct efi_dt_fixup_protocol *dt_fixup_prot;
 	struct efi_loaded_image *loaded_image;
 	struct efi_simple_file_system_protocol *file_system;
-	struct efi_file_handle *root, *file;
+	struct efi_file_handle *root = NULL, *file = NULL;
+	u64 addr = 0;
+	struct efi_file_info *info;
 	struct fdt_header *dtb;
+	efi_uintn_t buffer_size;
+	efi_uintn_t pages;
+	efi_status_t ret, ret2;
 
-	cerr = systable->std_err;
-	cout = systable->con_out;
-	cin = systable->con_in;
-	bs = systable->boottime;
+	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");
+		return ret;
+	}
 
-	cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
-	cout->clear_screen(cout);
-	cout->set_attribute(cout, EFI_YELLOW | EFI_BACKGROUND_BLACK);
-	cout->output_string(cout, L"DTB Dump\n\n");
-	cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
+	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\n");
+		return ret;
+	}
+	/* 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\n");
+		goto out;
+	}
+
+	/* Open volume */
+	ret = file_system->open_volume(file_system, &root);
+	if (ret != EFI_SUCCESS) {
+		error(L"Failed to open volume\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");
+		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");
+		goto out;
+	}
+	ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info);
+	if (ret != EFI_SUCCESS) {
+		error(L"Out of memory\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");
+		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");
+	/* 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");
+		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");
+		goto out;
+	}
+	/* Fixup file, expecting EFI_BUFFER_TOO_SMALL */
+	ret = dt_fixup_prot->fixup(dt_fixup_prot, dtb, &buffer_size,
+				   EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
+				   EFI_DT_INSTALL_TABLE);
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		/* Read file into larger buffer */
+		ret = bs->free_pages(addr, pages);
+		if (ret != EFI_SUCCESS)
+			error(L"Can't free memory pages\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");
+			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");
+			goto out;
+		}
+		ret = file->read(file, &buffer_size, dtb);
+		if (ret != EFI_SUCCESS) {
+			error(L"Can't read file\n");
+			goto out;
+		}
+		buffer_size = pages << EFI_PAGE_SHIFT;
+		ret = dt_fixup_prot->fixup(
+				dt_fixup_prot, dtb, &buffer_size,
+				EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
+				EFI_DT_INSTALL_TABLE);
+	}
+	if (ret == EFI_SUCCESS)
+		cout->output_string(cout, L"device-tree installed\n");
+	else
+		error(L"Device-tree fix-up failed\n");
+out:
+	if (addr) {
+		ret2 = bs->free_pages(addr, pages);
+		if (ret2 != EFI_SUCCESS)
+			error(L"Can't free memory pages\n");
+	}
+	if (file) {
+		ret2 = file->close(file);
+		if (ret2 != EFI_SUCCESS)
+			error(L"Can't close file\n");
+	}
+	if (root) {
+		ret2 = root->close(root);
+		if (ret2 != EFI_SUCCESS)
+			error(L"Can't close volume\n");
+	}
+	return ret;
+}
+
+/**
+ * do_save() - save current device-tree
+ *
+ * @filename:	file name
+ * Return:	status code
+ */
+efi_status_t do_save(u16 *filename)
+{
+	struct efi_loaded_image *loaded_image;
+	struct efi_simple_file_system_protocol *file_system;
+	efi_uintn_t dtb_size;
+	struct efi_file_handle *root, *file;
+	struct fdt_header *dtb;
+	efi_uintn_t ret;
 
 	dtb = get_dtb(systable);
 	if (!dtb) {
-		cerr->output_string(cout, L"DTB not found\n");
+		error(L"DTB not found\n");
 		return EFI_NOT_FOUND;
 	}
 	if (f2h(dtb->magic) != FDT_MAGIC) {
-		cerr->output_string(cout, L"Wrong device tree magic\n");
+		error(L"Wrong device tree magic\n");
 		return EFI_NOT_FOUND;
 	}
 	dtb_size = f2h(dtb->totalsize);
 
-	cout->output_string(cout, L"Filename (" DEFAULT_FILENAME ")?\n");
-	ret = efi_input(filename, sizeof(filename));
-	if (ret != EFI_SUCCESS)
-		return ret;
-	if (!*filename)
-		memcpy(filename, DEFAULT_FILENAME, sizeof(DEFAULT_FILENAME));
-
-	cout->output_string(cout, L"\n");
+	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) {
-		cerr->output_string(cout,
-				    L"Loaded image protocol not found\n");
+		error(L"Loaded image protocol not found\n");
 		return ret;
 	}
 
@@ -180,15 +369,14 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,
 				(void **)&file_system, NULL, NULL,
 				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 	if (ret != EFI_SUCCESS) {
-		cerr->output_string(
-			cout, L"Failed to open simple file system protocol\n");
+		error(L"Failed to open simple file system protocol\n");
 		return ret;
 	}
 
 	/* Open volume */
 	ret = file_system->open_volume(file_system, &root);
 	if (ret != EFI_SUCCESS) {
-		cerr->output_string(cerr, L"Failed to open volume\n");
+		error(L"Failed to open volume\n");
 		return ret;
 	}
 	/* Create file */
@@ -199,10 +387,10 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,
 		/* Write file */
 		ret = file->write(file, &dtb_size, dtb);
 		if (ret != EFI_SUCCESS)
-			cerr->output_string(cerr, L"Failed to write file\n");
+			error(L"Failed to write file\n");
 		file->close(file);
 	} else {
-		cerr->output_string(cerr, L"Failed to open file\n");
+		error(L"Failed to open file\n");
 	}
 	root->close(root);
 
@@ -213,3 +401,51 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,
 
 	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);
+	cout->output_string(cout, L"DTB Dump\n========\n\n");
+	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
+
+	for (;;) {
+		u16 command[BUFFER_SIZE];
+		u16 *pos;
+		efi_uintn_t ret;
+
+		cout->output_string(cout, 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(pos + 5);
+		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;
+}

+ 6 - 3
net/tftp.c

@@ -329,6 +329,12 @@ static void tftp_complete(void)
 			time_start * 1000, "/s");
 	}
 	puts("\ndone\n");
+	if (IS_ENABLED(CONFIG_CMD_BOOTEFI)) {
+		if (!tftp_put_active)
+			efi_set_bootdev("Net", "", tftp_filename,
+					map_sysmem(tftp_load_addr, 0),
+					net_boot_file_size);
+	}
 	net_set_state(NETLOOP_SUCCESS);
 }
 
@@ -841,9 +847,6 @@ void tftp_start(enum proto_t protocol)
 		printf("Load address: 0x%lx\n", tftp_load_addr);
 		puts("Loading: *\b");
 		tftp_state = STATE_SEND_RRQ;
-#ifdef CONFIG_CMD_BOOTEFI
-		efi_set_bootdev("Net", "", tftp_filename);
-#endif
 	}
 
 	time_start = get_timer(0);

+ 21 - 18
tools/efivar.py

@@ -51,21 +51,21 @@ var_guids = {
 }
 
 class EfiStruct:
-        # struct efi_var_file
-        var_file_fmt = '<QQLL'
-        var_file_size = struct.calcsize(var_file_fmt)
-        # struct efi_var_entry
-        var_entry_fmt = '<LLQ16s'
-        var_entry_size = struct.calcsize(var_entry_fmt)
-        # struct efi_time
-        var_time_fmt = '<H6BLh2B'
-        var_time_size = struct.calcsize(var_time_fmt)
-        # WIN_CERTIFICATE
-        var_win_cert_fmt = '<L2H'
-        var_win_cert_size = struct.calcsize(var_win_cert_fmt)
-        # WIN_CERTIFICATE_UEFI_GUID
-        var_win_cert_uefi_guid_fmt = var_win_cert_fmt+'16s'
-        var_win_cert_uefi_guid_size = struct.calcsize(var_win_cert_uefi_guid_fmt)
+    # struct efi_var_file
+    var_file_fmt = '<QQLL'
+    var_file_size = struct.calcsize(var_file_fmt)
+    # struct efi_var_entry
+    var_entry_fmt = '<LLQ16s'
+    var_entry_size = struct.calcsize(var_entry_fmt)
+    # struct efi_time
+    var_time_fmt = '<H6BLh2B'
+    var_time_size = struct.calcsize(var_time_fmt)
+    # WIN_CERTIFICATE
+    var_win_cert_fmt = '<L2H'
+    var_win_cert_size = struct.calcsize(var_win_cert_fmt)
+    # WIN_CERTIFICATE_UEFI_GUID
+    var_win_cert_uefi_guid_fmt = var_win_cert_fmt+'16s'
+    var_win_cert_uefi_guid_size = struct.calcsize(var_win_cert_uefi_guid_fmt)
 
 class EfiVariable:
     def __init__(self, size, attrs, time, guid, name, data):
@@ -149,7 +149,7 @@ class EfiVariableStore:
         offs = 0
         while offs < len(self.ents):
             var, loffs = self._next_var(offs)
-            if var.name == name and str(var.guid):
+            if var.name == name and str(var.guid) == guid:
                 if var.attrs != attrs:
                     print("err: attributes don't match")
                     exit(1)
@@ -292,7 +292,7 @@ def pkcs7_sign(cert, key, buf):
 
 # UEFI 2.8 Errata B "8.2.2 Using the EFI_VARIABLE_AUTHENTICATION_2 descriptor"
 def cmd_sign(args):
-    guid, name, attrs, data, size = parse_args(args)
+    guid, name, attrs, data, _ = parse_args(args)
     attrs |= EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
     efi = EfiStruct()
 
@@ -357,7 +357,10 @@ def main():
     signp.set_defaults(func=cmd_sign)
 
     args = ap.parse_args()
-    args.func(args)
+    if hasattr(args, "func"):
+        args.func(args)
+    else:
+        ap.print_help()
 
 def group(a, *ns):
     for n in ns: