瀏覽代碼

Merge tag 'signed-efi-next' of git://github.com/agraf/u-boot

Patch queue for efi - 2017-12-05

Highlights for this release:

  - Dynamic EFI object creation (lists instead of static arrays)
  - EFI selftest improvements
  - Minor fixes
Tom Rini 7 年之前
父節點
當前提交
9188c4315c

+ 61 - 12
cmd/bootefi.c

@@ -6,10 +6,12 @@
  *  SPDX-License-Identifier:     GPL-2.0+
  */
 
+#include <charset.h>
 #include <common.h>
 #include <command.h>
 #include <dm.h>
 #include <efi_loader.h>
+#include <efi_selftest.h>
 #include <errno.h>
 #include <libfdt.h>
 #include <libfdt_env.h>
@@ -43,12 +45,39 @@ static void efi_init_obj_list(void)
 #ifdef CONFIG_GENERATE_SMBIOS_TABLE
 	efi_smbios_register();
 #endif
+	efi_watchdog_register();
 
 	/* Initialize EFI runtime services */
 	efi_reset_system_init();
 	efi_get_time_init();
 }
 
+/*
+ * Set the load options of an image from an environment variable.
+ *
+ * @loaded_image_info:	the image
+ * @env_var:		name of the environment variable
+ */
+static void set_load_options(struct efi_loaded_image *loaded_image_info,
+			     const char *env_var)
+{
+	size_t size;
+	const char *env = env_get(env_var);
+
+	loaded_image_info->load_options = NULL;
+	loaded_image_info->load_options_size = 0;
+	if (!env)
+		return;
+	size = strlen(env) + 1;
+	loaded_image_info->load_options = calloc(size, sizeof(u16));
+	if (!loaded_image_info->load_options) {
+		printf("ERROR: Out of memory\n");
+		return;
+	}
+	utf8_to_utf16(loaded_image_info->load_options, (u8 *)env, size);
+	loaded_image_info->load_options_size = size * 2;
+}
+
 static void *copy_fdt(void *fdt)
 {
 	u64 fdt_size = fdt_totalsize(fdt);
@@ -92,10 +121,10 @@ static void *copy_fdt(void *fdt)
 	return new_fdt;
 }
 
-static ulong efi_do_enter(void *image_handle,
-			  struct efi_system_table *st,
-			  asmlinkage ulong (*entry)(void *image_handle,
-				struct efi_system_table *st))
+static efi_status_t efi_do_enter(
+			void *image_handle, struct efi_system_table *st,
+			asmlinkage ulong (*entry)(void *image_handle,
+						  struct efi_system_table *st))
 {
 	efi_status_t ret = EFI_LOAD_ERROR;
 
@@ -106,7 +135,7 @@ static ulong efi_do_enter(void *image_handle,
 }
 
 #ifdef CONFIG_ARM64
-static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)(
+static efi_status_t efi_run_in_el2(asmlinkage ulong (*entry)(
 			void *image_handle, struct efi_system_table *st),
 			void *image_handle, struct efi_system_table *st)
 {
@@ -121,9 +150,9 @@ static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)(
  * Load an EFI payload into a newly allocated piece of memory, register all
  * EFI objects it would want to access and jump to it.
  */
-static unsigned long do_bootefi_exec(void *efi, void *fdt,
-				     struct efi_device_path *device_path,
-				     struct efi_device_path *image_path)
+static efi_status_t do_bootefi_exec(void *efi, void *fdt,
+				    struct efi_device_path *device_path,
+				    struct efi_device_path *image_path)
 {
 	struct efi_loaded_image loaded_image_info = {};
 	struct efi_object loaded_image_info_obj = {};
@@ -189,6 +218,8 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt,
 		efi_install_configuration_table(&fdt_guid, NULL);
 	}
 
+	/* Transfer environment variable bootargs as load options */
+	set_load_options(&loaded_image_info, "bootargs");
 	/* Load the EFI payload */
 	entry = efi_load_pe(efi, &loaded_image_info);
 	if (!entry) {
@@ -223,7 +254,8 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt,
 		dcache_disable();	/* flush cache before switch to EL2 */
 
 		/* Move into EL2 and keep running there */
-		armv8_switch_to_el2((ulong)entry, (ulong)&loaded_image_info,
+		armv8_switch_to_el2((ulong)entry,
+				    (ulong)&loaded_image_info_obj.handle,
 				    (ulong)&systab, 0, (ulong)efi_run_in_el2,
 				    ES_TO_AARCH64);
 
@@ -232,7 +264,7 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt,
 	}
 #endif
 
-	ret = efi_do_enter(&loaded_image_info, &systab, entry);
+	ret = efi_do_enter(loaded_image_info_obj.handle, &systab, entry);
 
 exit:
 	/* image has returned, loaded-image obj goes *poof*: */
@@ -277,7 +309,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
 	char *saddr, *sfdt;
 	unsigned long addr, fdt_addr = 0;
-	unsigned long r;
+	efi_status_t r;
 
 	if (argc < 2)
 		return CMD_RET_USAGE;
@@ -298,6 +330,12 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 		struct efi_loaded_image loaded_image_info = {};
 		struct efi_object loaded_image_info_obj = {};
 
+		/* Construct a dummy device path. */
+		bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
+						      (uintptr_t)&efi_selftest,
+						      (uintptr_t)&efi_selftest);
+		bootefi_image_path = efi_dp_from_file(NULL, 0, "\\selftest");
+
 		efi_setup_loaded_image(&loaded_image_info,
 				       &loaded_image_info_obj,
 				       bootefi_device_path, bootefi_image_path);
@@ -310,7 +348,14 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 		/* Initialize and populate EFI object list */
 		if (!efi_obj_list_initalized)
 			efi_init_obj_list();
-		return efi_selftest(&loaded_image_info, &systab);
+		/* Transfer environment variable efi_selftest as load options */
+		set_load_options(&loaded_image_info, "efi_selftest");
+		/* Execute the test */
+		r = efi_selftest(loaded_image_info_obj.handle, &systab);
+		efi_restore_gd();
+		free(loaded_image_info.load_options);
+		list_del(&loaded_image_info_obj.link);
+		return r != EFI_SUCCESS;
 	} else
 #endif
 	if (!strcmp(argv[1], "bootmgr")) {
@@ -356,6 +401,8 @@ static char bootefi_help_text[] =
 #ifdef CONFIG_CMD_BOOTEFI_SELFTEST
 	"bootefi selftest\n"
 	"  - boot an EFI selftest application stored within U-Boot\n"
+	"    Use environment variable efi_selftest to select a single test.\n"
+	"    Use 'setenv efi_selftest list' to enumerate all tests.\n"
 #endif
 	"bootmgr [fdt addr]\n"
 	"  - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
@@ -390,6 +437,8 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
 		int part;
 
 		desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
+		if (!desc)
+			return;
 		part = parse_partnum(devnr);
 
 		bootefi_device_path = efi_dp_from_part(desc, part);

+ 15 - 0
include/charset.h

@@ -9,6 +9,8 @@
 #ifndef __CHARSET_H_
 #define __CHARSET_H_
 
+#include <linux/types.h>
+
 #define MAX_UTF8_PER_UTF16 3
 
 /**
@@ -62,4 +64,17 @@ uint16_t *utf16_strdup(const uint16_t *s);
  */
 uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size);
 
+/**
+ * utf8_to_utf16() - Convert an utf8 string to utf16
+ *
+ * Converts up to 'size' characters of the utf16 string 'src' to utf8
+ * written to the 'dest' buffer. Stops at 0x00.
+ *
+ * @dest   the destination buffer to write the utf8 characters
+ * @src    the source utf16 string
+ * @size   maximum number of utf16 characters to convert
+ * @return the pointer to the first unwritten byte in 'dest'
+ */
+uint16_t *utf8_to_utf16(uint16_t *dest, const uint8_t *src, size_t size);
+
 #endif /* __CHARSET_H_ */

+ 3 - 3
include/efi.h

@@ -227,9 +227,9 @@ struct efi_time_cap {
 };
 
 enum efi_locate_search_type {
-	all_handles,
-	by_register_notify,
-	by_protocol
+	ALL_HANDLES,
+	BY_REGISTER_NOTIFY,
+	BY_PROTOCOL
 };
 
 struct efi_open_protocol_info_entry {

+ 25 - 23
include/efi_api.h

@@ -28,8 +28,7 @@ enum efi_timer_delay {
 	EFI_TIMER_RELATIVE = 2
 };
 
-#define UINTN size_t
-typedef long INTN;
+#define efi_uintn_t size_t
 typedef uint16_t *efi_string_t;
 
 #define EVT_TIMER				0x80000000
@@ -49,20 +48,22 @@ struct efi_event;
 /* EFI Boot Services table */
 struct efi_boot_services {
 	struct efi_table_hdr hdr;
-	efi_status_t (EFIAPI *raise_tpl)(UINTN new_tpl);
-	void (EFIAPI *restore_tpl)(UINTN old_tpl);
+	efi_status_t (EFIAPI *raise_tpl)(efi_uintn_t new_tpl);
+	void (EFIAPI *restore_tpl)(efi_uintn_t old_tpl);
 
-	efi_status_t (EFIAPI *allocate_pages)(int, int, unsigned long,
+	efi_status_t (EFIAPI *allocate_pages)(int, int, efi_uintn_t,
 					      efi_physical_addr_t *);
-	efi_status_t (EFIAPI *free_pages)(efi_physical_addr_t, unsigned long);
-	efi_status_t (EFIAPI *get_memory_map)(unsigned long *memory_map_size,
-			struct efi_mem_desc *desc, unsigned long *key,
-			unsigned long *desc_size, u32 *desc_version);
-	efi_status_t (EFIAPI *allocate_pool)(int, unsigned long, void **);
+	efi_status_t (EFIAPI *free_pages)(efi_physical_addr_t, efi_uintn_t);
+	efi_status_t (EFIAPI *get_memory_map)(efi_uintn_t *memory_map_size,
+					      struct efi_mem_desc *desc,
+					      efi_uintn_t *key,
+					      efi_uintn_t *desc_size,
+					      u32 *desc_version);
+	efi_status_t (EFIAPI *allocate_pool)(int, efi_uintn_t, void **);
 	efi_status_t (EFIAPI *free_pool)(void *);
 
 	efi_status_t (EFIAPI *create_event)(uint32_t type,
-			UINTN notify_tpl,
+			efi_uintn_t notify_tpl,
 			void (EFIAPI *notify_function) (
 					struct efi_event *event,
 					void *context),
@@ -70,8 +71,9 @@ struct efi_boot_services {
 	efi_status_t (EFIAPI *set_timer)(struct efi_event *event,
 					 enum efi_timer_delay type,
 					 uint64_t trigger_time);
-	efi_status_t (EFIAPI *wait_for_event)(unsigned long number_of_events,
-			struct efi_event **event, size_t *index);
+	efi_status_t (EFIAPI *wait_for_event)(efi_uintn_t number_of_events,
+					      struct efi_event **event,
+					      efi_uintn_t *index);
 	efi_status_t (EFIAPI *signal_event)(struct efi_event *event);
 	efi_status_t (EFIAPI *close_event)(struct efi_event *event);
 	efi_status_t (EFIAPI *check_event)(struct efi_event *event);
@@ -94,7 +96,7 @@ struct efi_boot_services {
 	efi_status_t (EFIAPI *locate_handle)(
 			enum efi_locate_search_type search_type,
 			const efi_guid_t *protocol, void *search_key,
-			unsigned long *buffer_size, efi_handle_t *buffer);
+			efi_uintn_t *buffer_size, efi_handle_t *buffer);
 	efi_status_t (EFIAPI *locate_device_path)(const efi_guid_t *protocol,
 			struct efi_device_path **device_path,
 			efi_handle_t *device);
@@ -141,14 +143,14 @@ struct efi_boot_services {
 	efi_status_t(EFIAPI *open_protocol_information)(efi_handle_t handle,
 			const efi_guid_t *protocol,
 			struct efi_open_protocol_info_entry **entry_buffer,
-			unsigned long *entry_count);
+			efi_uintn_t *entry_count);
 	efi_status_t (EFIAPI *protocols_per_handle)(efi_handle_t handle,
 			efi_guid_t ***protocol_buffer,
-			unsigned long *protocols_buffer_count);
+			efi_uintn_t *protocols_buffer_count);
 	efi_status_t (EFIAPI *locate_handle_buffer) (
 			enum efi_locate_search_type search_type,
 			const efi_guid_t *protocol, void *search_key,
-			unsigned long *no_handles, efi_handle_t **buffer);
+			efi_uintn_t *no_handles, efi_handle_t **buffer);
 	efi_status_t (EFIAPI *locate_protocol)(const efi_guid_t *protocol,
 			void *registration, void **protocol_interface);
 	efi_status_t (EFIAPI *install_multiple_protocol_interfaces)(
@@ -249,7 +251,7 @@ struct efi_system_table {
 	struct efi_simple_text_output_protocol *std_err;
 	struct efi_runtime_services *runtime;
 	struct efi_boot_services *boottime;
-	unsigned long nr_tables;
+	efi_uintn_t nr_tables;
 	struct efi_configuration_table *tables;
 };
 
@@ -583,14 +585,14 @@ struct efi_gop_mode
 struct efi_gop
 {
 	efi_status_t (EFIAPI *query_mode)(struct efi_gop *this, u32 mode_number,
-					  unsigned long *size_of_info,
+					  efi_uintn_t *size_of_info,
 					  struct efi_gop_mode_info **info);
 	efi_status_t (EFIAPI *set_mode)(struct efi_gop *this, u32 mode_number);
 	efi_status_t (EFIAPI *blt)(struct efi_gop *this, void *buffer,
-				   unsigned long operation, unsigned long sx,
-				   unsigned long sy, unsigned long dx,
-				   unsigned long dy, unsigned long width,
-				   unsigned long height, unsigned long delta);
+				   u32 operation, efi_uintn_t sx,
+				   efi_uintn_t sy, efi_uintn_t dx,
+				   efi_uintn_t dy, efi_uintn_t width,
+				   efi_uintn_t height, efi_uintn_t delta);
 	struct efi_gop_mode *mode;
 };
 

+ 44 - 22
include/efi_loader.h

@@ -6,6 +6,9 @@
  *  SPDX-License-Identifier:     GPL-2.0+
  */
 
+#ifndef _EFI_LOADER_H
+#define _EFI_LOADER_H 1
+
 #include <common.h>
 #include <part_efi.h>
 #include <efi_api.h>
@@ -75,9 +78,9 @@ const char *__efi_nesting_dec(void);
 extern struct efi_runtime_services efi_runtime_services;
 extern struct efi_system_table systab;
 
-extern const struct efi_simple_text_output_protocol efi_con_out;
+extern struct efi_simple_text_output_protocol efi_con_out;
 extern struct efi_simple_input_interface efi_con_in;
-extern const struct efi_console_control_protocol efi_console_control;
+extern struct efi_console_control_protocol efi_console_control;
 extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
 
 uint16_t *efi_dp_str(struct efi_device_path *dp);
@@ -98,6 +101,8 @@ extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
  * interface (usually a struct with callback functions), this struct maps the
  * protocol GUID to the respective protocol interface */
 struct efi_handler {
+	/* Link to the list of protocols of a handle */
+	struct list_head link;
 	const efi_guid_t *guid;
 	void *protocol_interface;
 };
@@ -112,20 +117,12 @@ struct efi_handler {
 struct efi_object {
 	/* Every UEFI object is part of a global object list */
 	struct list_head link;
-	/* We support up to 16 "protocols" an object can be accessed through */
-	struct efi_handler protocols[16];
+	/* The list of protocols */
+	struct list_head protocols;
 	/* The object spawner can either use this for data or as identifier */
 	void *handle;
 };
 
-#define EFI_PROTOCOL_OBJECT(_guid, _protocol) (struct efi_object){	\
-	.protocols = {{							\
-		.guid = &(_guid),	 				\
-		.protocol_interface = (void *)(_protocol), 		\
-	}},								\
-	.handle = (void *)(_protocol),					\
-}
-
 /**
  * struct efi_event
  *
@@ -141,7 +138,7 @@ struct efi_object {
  */
 struct efi_event {
 	uint32_t type;
-	UINTN notify_tpl;
+	efi_uintn_t notify_tpl;
 	void (EFIAPI *notify_function)(struct efi_event *event, void *context);
 	void *notify_context;
 	u64 trigger_next;
@@ -163,6 +160,8 @@ int efi_disk_register(void);
 int efi_gop_register(void);
 /* Called by bootefi to make the network interface available */
 int efi_net_register(void);
+/* Called by bootefi to make the watchdog available */
+int efi_watchdog_register(void);
 /* Called by bootefi to make SMBIOS tables available */
 void efi_smbios_register(void);
 
@@ -171,6 +170,8 @@ efi_fs_from_path(struct efi_device_path *fp);
 
 /* Called by networking code to memorize the dhcp ack package */
 void efi_net_set_dhcp_ack(void *pkt, int len);
+/* Called by efi_set_watchdog_timer to reset the timer */
+efi_status_t efi_set_watchdog(unsigned long timeout);
 
 /* Called from places to check whether a timer expired */
 void efi_timer_check(void);
@@ -185,8 +186,26 @@ void efi_restore_gd(void);
 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);
+/* Add a new object to the object list. */
+void efi_add_handle(struct efi_object *obj);
+/* Create handle */
+efi_status_t efi_create_handle(void **handle);
+/* Call this to validate a handle and find the EFI object for it */
+struct efi_object *efi_search_obj(const void *handle);
+/* Find a protocol on a handle */
+efi_status_t efi_search_protocol(const void *handle,
+				 const efi_guid_t *protocol_guid,
+				 struct efi_handler **handler);
+/* Install new protocol on a handle */
+efi_status_t efi_add_protocol(const void *handle, const efi_guid_t *protocol,
+			      void *protocol_interface);
+/* Delete protocol from a handle */
+efi_status_t efi_remove_protocol(const void *handle, const efi_guid_t *protocol,
+				 void *protocol_interface);
+/* Delete all protocols from a handle */
+efi_status_t efi_remove_all_protocols(const void *handle);
 /* Call this to create an event */
-efi_status_t efi_create_event(uint32_t type, UINTN notify_tpl,
+efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
 			      void (EFIAPI *notify_function) (
 					struct efi_event *event,
 					void *context),
@@ -208,20 +227,20 @@ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp);
 /* 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 */
-efi_status_t efi_allocate_pages(int type, int memory_type, unsigned long pages,
+efi_status_t efi_allocate_pages(int type, int memory_type, efi_uintn_t pages,
 				uint64_t *memory);
 /* EFI memory free function. */
-efi_status_t efi_free_pages(uint64_t memory, unsigned long pages);
+efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages);
 /* EFI memory allocator for small allocations */
-efi_status_t efi_allocate_pool(int pool_type, unsigned long size,
+efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size,
 			       void **buffer);
 /* EFI pool memory free function. */
 efi_status_t efi_free_pool(void *buffer);
 /* Returns the EFI memory map */
-efi_status_t efi_get_memory_map(unsigned long *memory_map_size,
+efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
 				struct efi_mem_desc *memory_map,
-				unsigned long *map_key,
-				unsigned long *descriptor_size,
+				efi_uintn_t *map_key,
+				efi_uintn_t *descriptor_size,
 				uint32_t *descriptor_version);
 /* Adds a range into the EFI memory map */
 uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
@@ -243,7 +262,8 @@ extern void *efi_bounce_buffer;
 
 
 struct efi_device_path *efi_dp_next(const struct efi_device_path *dp);
-int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b);
+int efi_dp_match(const struct efi_device_path *a,
+		 const struct efi_device_path *b);
 struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
 				   struct efi_device_path **rem);
 unsigned efi_dp_size(const struct efi_device_path *dp);
@@ -341,4 +361,6 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr,
 				   const char *path) { }
 static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
 
-#endif
+#endif /* CONFIG_EFI_LOADER && !CONFIG_SPL_BUILD */
+
+#endif /* _EFI_LOADER_H */

+ 21 - 0
include/efi_selftest.h

@@ -12,6 +12,7 @@
 #include <common.h>
 #include <efi.h>
 #include <efi_api.h>
+#include <efi_loader.h>
 #include <linker_lists.h>
 
 #define EFI_ST_SUCCESS 0
@@ -26,6 +27,15 @@
 	(efi_st_printf("%s(%u):\nERROR: ", __FILE__, __LINE__), \
 	efi_st_printf(__VA_ARGS__)) \
 
+/*
+ * Prints a TODO message.
+ *
+ * @...	format string followed by fields to print
+ */
+#define efi_st_todo(...) \
+	(efi_st_printf("%s(%u):\nTODO: ", __FILE__, __LINE__), \
+	efi_st_printf(__VA_ARGS__)) \
+
 /*
  * A test may be setup and executed at boottime,
  * it may be setup at boottime and executed at runtime,
@@ -71,6 +81,15 @@ void efi_st_printf(const char *fmt, ...)
  */
 int efi_st_memcmp(const void *buf1, const void *buf2, size_t length);
 
+/*
+ * Compare an u16 string to a char string.
+ *
+ * @buf1:	u16 string
+ * @buf2:	char string
+ * @return:	0 if both buffers contain the same bytes
+ */
+int efi_st_strcmp_16_8(const u16 *buf1, const char *buf2);
+
 /*
  * Reads an Unicode character from the input device.
  *
@@ -88,6 +107,7 @@ u16 efi_st_get_key(void);
  * @setup:	set up the unit test
  * @teardown:	tear down the unit test
  * @execute:	execute the unit test
+ * @on_request:	test is only executed on request
  */
 struct efi_unit_test {
 	const char *name;
@@ -96,6 +116,7 @@ struct efi_unit_test {
 		     const struct efi_system_table *systable);
 	int (*execute)(void);
 	int (*teardown)(void);
+	bool on_request;
 };
 
 /* Declare a new EFI unit test */

+ 56 - 1
lib/charset.c

@@ -6,7 +6,6 @@
  *  SPDX-License-Identifier:     GPL-2.0+
  */
 
-#include <common.h>
 #include <charset.h>
 #include <malloc.h>
 
@@ -99,3 +98,59 @@ uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size)
 
 	return dest;
 }
+
+uint16_t *utf8_to_utf16(uint16_t *dest, const uint8_t *src, size_t size)
+{
+	while (size--) {
+		int extension_bytes;
+		uint32_t code;
+
+		extension_bytes = 0;
+		if (*src <= 0x7f) {
+			code = *src++;
+			/* Exit on zero byte */
+			if (!code)
+				size = 0;
+		} else if (*src <= 0xbf) {
+			/* Illegal code */
+			code = '?';
+		} else if (*src <= 0xdf) {
+			code = *src++ & 0x1f;
+			extension_bytes = 1;
+		} else if (*src <= 0xef) {
+			code = *src++ & 0x0f;
+			extension_bytes = 2;
+		} else if (*src <= 0xf7) {
+			code = *src++ & 0x07;
+			extension_bytes = 3;
+		} else {
+			/* Illegal code */
+			code = '?';
+		}
+
+		for (; extension_bytes && size; --size, --extension_bytes) {
+			if ((*src & 0xc0) == 0x80) {
+				code <<= 6;
+				code |= *src++ & 0x3f;
+			} else {
+				/* Illegal code */
+				code = '?';
+				++src;
+				--size;
+				break;
+			}
+		}
+
+		if (code < 0x10000) {
+			*dest++ = code;
+		} else {
+			/*
+			 * Simplified expression for
+			 * (((code - 0x10000) >> 10) & 0x3ff) | 0xd800
+			 */
+			*dest++ = (code >> 10) + 0xd7c0;
+			*dest++ = (code & 0x3ff) | 0xdc00;
+		}
+	}
+	return dest;
+}

+ 1 - 1
lib/efi/efi_stub.c

@@ -277,7 +277,7 @@ efi_status_t efi_main(efi_handle_t image, struct efi_system_table *sys_table)
 	struct efi_boot_services *boot = sys_table->boottime;
 	struct efi_mem_desc *desc;
 	struct efi_entry_memmap map;
-	ulong key, desc_size, size;
+	efi_uintn_t key, desc_size, size;
 	efi_status_t ret;
 	u32 version;
 	int cs32;

+ 3 - 3
lib/efi_loader/Makefile

@@ -7,8 +7,8 @@
 # This file only gets included with CONFIG_EFI_LOADER set, so all
 # object inclusion implicitly depends on it
 
-CFLAGS_helloworld.o := $(CFLAGS_EFI)
-CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI)
+CFLAGS_helloworld.o := $(CFLAGS_EFI) -Os -ffreestanding
+CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI) -Os
 
 ifneq ($(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),)
 always += helloworld.efi
@@ -17,7 +17,7 @@ endif
 obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
 obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
 obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o
-obj-y += efi_file.o efi_variable.o efi_bootmgr.o
+obj-y += efi_file.o efi_variable.o efi_bootmgr.o efi_watchdog.o
 obj-$(CONFIG_LCD) += efi_gop.o
 obj-$(CONFIG_DM_VIDEO) += efi_gop.o
 obj-$(CONFIG_PARTITIONS) += efi_disk.o

文件差異過大導致無法顯示
+ 406 - 289
lib/efi_loader/efi_boottime.c


+ 35 - 13
lib/efi_loader/efi_console.c

@@ -46,6 +46,10 @@ static struct cout_mode efi_cout_modes[] = {
 };
 
 const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID;
+const efi_guid_t efi_guid_text_output_protocol =
+			EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
+const efi_guid_t efi_guid_text_input_protocol =
+			EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
 
 #define cESC '\x1b'
 #define ESC "\x1b"
@@ -81,7 +85,7 @@ static efi_status_t EFIAPI efi_cin_lock_std_in(
 	return EFI_EXIT(EFI_UNSUPPORTED);
 }
 
-const struct efi_console_control_protocol efi_console_control = {
+struct efi_console_control_protocol efi_console_control = {
 	.get_mode = efi_cin_get_mode,
 	.set_mode = efi_cin_set_mode,
 	.lock_std_in = efi_cin_lock_std_in,
@@ -374,7 +378,7 @@ static efi_status_t EFIAPI efi_cout_enable_cursor(
 	return EFI_EXIT(EFI_SUCCESS);
 }
 
-const struct efi_simple_text_output_protocol efi_con_out = {
+struct efi_simple_text_output_protocol efi_con_out = {
 	.reset = efi_cout_reset,
 	.output_string = efi_cout_output_string,
 	.test_string = efi_cout_test_string,
@@ -490,23 +494,38 @@ static void EFIAPI efi_console_timer_notify(struct efi_event *event,
 }
 
 
-static struct efi_object efi_console_control_obj =
-	EFI_PROTOCOL_OBJECT(efi_guid_console_control, &efi_console_control);
-static struct efi_object efi_console_output_obj =
-	EFI_PROTOCOL_OBJECT(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID, &efi_con_out);
-static struct efi_object efi_console_input_obj =
-	EFI_PROTOCOL_OBJECT(EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID, &efi_con_in);
-
 /* This gets called from do_bootefi_exec(). */
 int efi_console_register(void)
 {
 	efi_status_t r;
+	struct efi_object *efi_console_control_obj;
+	struct efi_object *efi_console_output_obj;
+	struct efi_object *efi_console_input_obj;
 
-	/* Hook up to the device list */
-	list_add_tail(&efi_console_control_obj.link, &efi_obj_list);
-	list_add_tail(&efi_console_output_obj.link, &efi_obj_list);
-	list_add_tail(&efi_console_input_obj.link, &efi_obj_list);
+	/* Create handles */
+	r = efi_create_handle((void **)&efi_console_control_obj);
+	if (r != EFI_SUCCESS)
+		goto out_of_memory;
+	r = efi_add_protocol(efi_console_control_obj->handle,
+			     &efi_guid_console_control, &efi_console_control);
+	if (r != EFI_SUCCESS)
+		goto out_of_memory;
+	r = efi_create_handle((void **)&efi_console_output_obj);
+	if (r != EFI_SUCCESS)
+		goto out_of_memory;
+	r = efi_add_protocol(efi_console_output_obj->handle,
+			     &efi_guid_text_output_protocol, &efi_con_out);
+	if (r != EFI_SUCCESS)
+		goto out_of_memory;
+	r = efi_create_handle((void **)&efi_console_input_obj);
+	if (r != EFI_SUCCESS)
+		goto out_of_memory;
+	r = efi_add_protocol(efi_console_input_obj->handle,
+			     &efi_guid_text_input_protocol, &efi_con_in);
+	if (r != EFI_SUCCESS)
+		goto out_of_memory;
 
+	/* Create console events */
 	r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
 			     efi_key_notify, NULL, &efi_con_in.wait_for_key);
 	if (r != EFI_SUCCESS) {
@@ -525,4 +544,7 @@ int efi_console_register(void)
 	if (r != EFI_SUCCESS)
 		printf("ERROR: Failed to set console timer\n");
 	return r;
+out_of_memory:
+	printf("ERROR: Out of meemory\n");
+	return r;
 }

+ 40 - 27
lib/efi_loader/efi_device_path.c

@@ -68,7 +68,8 @@ struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
  * representing a device with one representing a file on the device, or
  * a device with a parent device.
  */
-int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b)
+int efi_dp_match(const struct efi_device_path *a,
+		 const struct efi_device_path *b)
 {
 	while (1) {
 		int ret;
@@ -127,32 +128,27 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
 	struct efi_object *efiobj;
 
 	list_for_each_entry(efiobj, &efi_obj_list, link) {
-		int i;
-
-		for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
-			struct efi_handler *handler = &efiobj->protocols[i];
-			struct efi_device_path *obj_dp;
-
-			if (!handler->guid)
-				break;
-
-			if (guidcmp(handler->guid, &efi_guid_device_path))
-				continue;
-
-			obj_dp = handler->protocol_interface;
-
-			do {
-				if (efi_dp_match(dp, obj_dp) == 0) {
-					if (rem) {
-						*rem = ((void *)dp) +
-							efi_dp_size(obj_dp);
-					}
-					return efiobj;
+		struct efi_handler *handler;
+		struct efi_device_path *obj_dp;
+		efi_status_t ret;
+
+		ret = efi_search_protocol(efiobj->handle,
+					  &efi_guid_device_path, &handler);
+		if (ret != EFI_SUCCESS)
+			continue;
+		obj_dp = handler->protocol_interface;
+
+		do {
+			if (efi_dp_match(dp, obj_dp) == 0) {
+				if (rem) {
+					*rem = ((void *)dp) +
+						efi_dp_size(obj_dp);
 				}
+				return efiobj;
+			}
 
-				obj_dp = shorten_path(efi_dp_next(obj_dp));
-			} while (short_path && obj_dp);
-		}
+			obj_dp = shorten_path(efi_dp_next(obj_dp));
+		} while (short_path && obj_dp);
 	}
 
 	return NULL;
@@ -427,10 +423,27 @@ static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
 			hddp->partmap_type = 2;
 		else
 			hddp->partmap_type = 1;
-		hddp->signature_type = desc->sig_type;
-		if (hddp->signature_type != 0)
+
+		switch (desc->sig_type) {
+		case SIG_TYPE_NONE:
+		default:
+			hddp->signature_type = 0;
+			memset(hddp->partition_signature, 0,
+			       sizeof(hddp->partition_signature));
+			break;
+		case SIG_TYPE_MBR:
+			hddp->signature_type = 1;
+			memset(hddp->partition_signature, 0,
+			       sizeof(hddp->partition_signature));
+			memcpy(hddp->partition_signature, &desc->mbr_sig,
+			       sizeof(desc->mbr_sig));
+			break;
+		case SIG_TYPE_GUID:
+			hddp->signature_type = 2;
 			memcpy(hddp->partition_signature, &desc->guid_sig,
 			       sizeof(hddp->partition_signature));
+			break;
+		}
 
 		buf = &hddp[1];
 	}

+ 120 - 73
lib/efi_loader/efi_device_path_to_text.c

@@ -12,12 +12,31 @@
 #define MAC_OUTPUT_LEN 22
 #define UNKNOWN_OUTPUT_LEN 23
 
+#define MAX_NODE_LEN 512
+#define MAX_PATH_LEN 1024
+
 const efi_guid_t efi_guid_device_path_to_text_protocol =
 		EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
 
+static u16 *efi_str_to_u16(char *str)
+{
+	efi_uintn_t len;
+	u16 *out;
+	efi_status_t ret;
+
+	len = strlen(str) + 1;
+	ret = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, len * sizeof(u16),
+				(void **)&out);
+	if (ret != EFI_SUCCESS)
+		return NULL;
+	ascii2unicode(out, str);
+	out[len - 1] = 0;
+	return out;
+}
+
 static char *dp_unknown(char *s, struct efi_device_path *dp)
 {
-	s += sprintf(s, "/UNKNOWN(%04x,%04x)", dp->type, dp->sub_type);
+	s += sprintf(s, "UNKNOWN(%04x,%04x)", dp->type, dp->sub_type);
 	return s;
 }
 
@@ -27,7 +46,7 @@ static char *dp_hardware(char *s, struct efi_device_path *dp)
 	case DEVICE_PATH_SUB_TYPE_MEMORY: {
 		struct efi_device_path_memory *mdp =
 			(struct efi_device_path_memory *)dp;
-		s += sprintf(s, "/MemoryMapped(0x%x,0x%llx,0x%llx)",
+		s += sprintf(s, "MemoryMapped(0x%x,0x%llx,0x%llx)",
 			     mdp->memory_type,
 			     mdp->start_address,
 			     mdp->end_address);
@@ -36,7 +55,7 @@ static char *dp_hardware(char *s, struct efi_device_path *dp)
 	case DEVICE_PATH_SUB_TYPE_VENDOR: {
 		struct efi_device_path_vendor *vdp =
 			(struct efi_device_path_vendor *)dp;
-		s += sprintf(s, "/VenHw(%pUl)", &vdp->guid);
+		s += sprintf(s, "VenHw(%pUl)", &vdp->guid);
 		break;
 	}
 	default:
@@ -52,7 +71,7 @@ static char *dp_acpi(char *s, struct efi_device_path *dp)
 	case DEVICE_PATH_SUB_TYPE_ACPI_DEVICE: {
 		struct efi_device_path_acpi_path *adp =
 			(struct efi_device_path_acpi_path *)dp;
-		s += sprintf(s, "/Acpi(PNP%04x", EISA_PNP_NUM(adp->hid));
+		s += sprintf(s, "Acpi(PNP%04x", EISA_PNP_NUM(adp->hid));
 		if (adp->uid)
 			s += sprintf(s, ",%d", adp->uid);
 		s += sprintf(s, ")");
@@ -71,7 +90,7 @@ static char *dp_msging(char *s, struct efi_device_path *dp)
 	case DEVICE_PATH_SUB_TYPE_MSG_USB: {
 		struct efi_device_path_usb *udp =
 			(struct efi_device_path_usb *)dp;
-		s += sprintf(s, "/Usb(0x%x,0x%x)", udp->parent_port_number,
+		s += sprintf(s, "Usb(0x%x,0x%x)", udp->parent_port_number,
 			     udp->usb_interface);
 		break;
 	}
@@ -82,7 +101,7 @@ static char *dp_msging(char *s, struct efi_device_path *dp)
 		if (mdp->if_type != 0 && mdp->if_type != 1)
 			break;
 
-		s += sprintf(s, "/MAC(%02x%02x%02x%02x%02x%02x,0x%1x)",
+		s += sprintf(s, "MAC(%02x%02x%02x%02x%02x%02x,0x%1x)",
 			mdp->mac.addr[0], mdp->mac.addr[1],
 			mdp->mac.addr[2], mdp->mac.addr[3],
 			mdp->mac.addr[4], mdp->mac.addr[5],
@@ -94,7 +113,7 @@ static char *dp_msging(char *s, struct efi_device_path *dp)
 		struct efi_device_path_usb_class *ucdp =
 			(struct efi_device_path_usb_class *)dp;
 
-		s += sprintf(s, "/USBClass(%x,%x,%x,%x,%x)",
+		s += sprintf(s, "USBClass(%x,%x,%x,%x,%x)",
 			ucdp->vendor_id, ucdp->product_id,
 			ucdp->device_class, ucdp->device_subclass,
 			ucdp->device_protocol);
@@ -108,7 +127,7 @@ static char *dp_msging(char *s, struct efi_device_path *dp)
 					"SDCard" : "MMC";
 		struct efi_device_path_sd_mmc_path *sddp =
 			(struct efi_device_path_sd_mmc_path *)dp;
-		s += sprintf(s, "/%s(Slot%u)", typename, sddp->slot_number);
+		s += sprintf(s, "%s(Slot%u)", typename, sddp->slot_number);
 		break;
 	}
 	default:
@@ -128,17 +147,19 @@ static char *dp_media(char *s, struct efi_device_path *dp)
 
 		switch (hddp->signature_type) {
 		case SIG_TYPE_MBR:
-			s += sprintf(s, "/HD(Part%d,Sig%08x)",
+			s += sprintf(s, "HD(Part%d,Sig%08x)",
 				     hddp->partition_number,
 				     *(uint32_t *)sig);
 			break;
 		case SIG_TYPE_GUID:
-			s += sprintf(s, "/HD(Part%d,Sig%pUl)",
+			s += sprintf(s, "HD(Part%d,Sig%pUl)",
 				     hddp->partition_number, sig);
+			break;
 		default:
-			s += sprintf(s, "/HD(Part%d,MBRType=%02x,SigType=%02x)",
+			s += sprintf(s, "HD(Part%d,MBRType=%02x,SigType=%02x)",
 				     hddp->partition_number, hddp->partmap_type,
 				     hddp->signature_type);
+			break;
 		}
 
 		break;
@@ -146,14 +167,16 @@ static char *dp_media(char *s, struct efi_device_path *dp)
 	case DEVICE_PATH_SUB_TYPE_CDROM_PATH: {
 		struct efi_device_path_cdrom_path *cddp =
 			(struct efi_device_path_cdrom_path *)dp;
-		s += sprintf(s, "/CDROM(0x%x)", cddp->boot_entry);
+		s += sprintf(s, "CDROM(0x%x)", cddp->boot_entry);
 		break;
 	}
 	case DEVICE_PATH_SUB_TYPE_FILE_PATH: {
 		struct efi_device_path_file_path *fp =
 			(struct efi_device_path_file_path *)dp;
 		int slen = (dp->length - sizeof(*dp)) / 2;
-		s += sprintf(s, "/%-*ls", slen, fp->str);
+		if (slen > MAX_NODE_LEN - 2)
+			slen = MAX_NODE_LEN - 2;
+		s += sprintf(s, "%-.*ls", slen, fp->str);
 		break;
 	}
 	default:
@@ -163,95 +186,119 @@ static char *dp_media(char *s, struct efi_device_path *dp)
 	return s;
 }
 
-static uint16_t *efi_convert_device_node_to_text(
-		struct efi_device_path *dp,
-		bool display_only,
-		bool allow_shortcuts)
+/*
+ * Converts a single node to a char string.
+ *
+ * @buffer		output buffer
+ * @dp			device path or node
+ * @return		end of string
+ */
+static char *efi_convert_single_device_node_to_text(
+		char *buffer,
+		struct efi_device_path *dp)
 {
-	unsigned long len;
-	efi_status_t r;
-	char buf[512];  /* this ought be be big enough for worst case */
-	char *str = buf;
-	uint16_t *out;
-
-	while (dp) {
-		switch (dp->type) {
-		case DEVICE_PATH_TYPE_HARDWARE_DEVICE:
-			str = dp_hardware(str, dp);
-			break;
-		case DEVICE_PATH_TYPE_ACPI_DEVICE:
-			str = dp_acpi(str, dp);
-			break;
-		case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
-			str = dp_msging(str, dp);
-			break;
-		case DEVICE_PATH_TYPE_MEDIA_DEVICE:
-			str = dp_media(str, dp);
-			break;
-		default:
-			str = dp_unknown(str, dp);
-		}
+	char *str = buffer;
 
-		dp = efi_dp_next(dp);
+	switch (dp->type) {
+	case DEVICE_PATH_TYPE_HARDWARE_DEVICE:
+		str = dp_hardware(str, dp);
+		break;
+	case DEVICE_PATH_TYPE_ACPI_DEVICE:
+		str = dp_acpi(str, dp);
+		break;
+	case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
+		str = dp_msging(str, dp);
+		break;
+	case DEVICE_PATH_TYPE_MEDIA_DEVICE:
+		str = dp_media(str, dp);
+		break;
+	default:
+		str = dp_unknown(str, dp);
 	}
 
-	*str++ = '\0';
-
-	len = str - buf;
-	r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, 2 * len, (void **)&out);
-	if (r != EFI_SUCCESS)
-		return NULL;
-
-	ascii2unicode(out, buf);
-	out[len - 1] = 0;
-
-	return out;
+	*str = '\0';
+	return str;
 }
 
-/* helper for debug prints.. efi_free_pool() the result. */
-uint16_t *efi_dp_str(struct efi_device_path *dp)
-{
-	return efi_convert_device_node_to_text(dp, true, true);
-}
-
-
-static uint16_t EFIAPI *efi_convert_device_node_to_text_ext(
+/*
+ * This function implements the ConvertDeviceNodeToText service of the
+ * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * device_node		device node to be converted
+ * display_only		true if the shorter text represenation shall be used
+ * allow_shortcuts	true if shortcut forms may be used
+ * @return		text represenation of the device path
+ *			NULL if out of memory of device_path is NULL
+ */
+static uint16_t EFIAPI *efi_convert_device_node_to_text(
 		struct efi_device_path *device_node,
 		bool display_only,
 		bool allow_shortcuts)
 {
-	uint16_t *buffer;
+	char str[MAX_NODE_LEN];
+	uint16_t *text = NULL;
 
 	EFI_ENTRY("%p, %d, %d", device_node, display_only, allow_shortcuts);
 
-	buffer = efi_convert_device_node_to_text(device_node, display_only,
-						 allow_shortcuts);
+	if (!device_node)
+		goto out;
+	efi_convert_single_device_node_to_text(str, device_node);
+
+	text = efi_str_to_u16(str);
 
+out:
 	EFI_EXIT(EFI_SUCCESS);
-	return buffer;
+	return text;
 }
 
+/*
+ * This function implements the ConvertDevicePathToText service of the
+ * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * device_path		device path to be converted
+ * display_only		true if the shorter text represenation shall be used
+ * allow_shortcuts	true if shortcut forms may be used
+ * @return		text represenation of the device path
+ *			NULL if out of memory of device_path is NULL
+ */
 static uint16_t EFIAPI *efi_convert_device_path_to_text(
 		struct efi_device_path *device_path,
 		bool display_only,
 		bool allow_shortcuts)
 {
-	uint16_t *buffer;
+	uint16_t *text = NULL;
+	char buffer[MAX_PATH_LEN];
+	char *str = buffer;
 
 	EFI_ENTRY("%p, %d, %d", device_path, display_only, allow_shortcuts);
 
-	/*
-	 * Our device paths are all of depth one. So its is sufficient to
-	 * to convert the first node.
-	 */
-	buffer = efi_convert_device_node_to_text(device_path, display_only,
-						 allow_shortcuts);
+	if (!device_path)
+		goto out;
+	while (device_path &&
+	       str + MAX_NODE_LEN < buffer + MAX_PATH_LEN) {
+		*str++ = '/';
+		str = efi_convert_single_device_node_to_text(str, device_path);
+		device_path = efi_dp_next(device_path);
+	}
+
+	text = efi_str_to_u16(buffer);
 
+out:
 	EFI_EXIT(EFI_SUCCESS);
-	return buffer;
+	return text;
+}
+
+/* helper for debug prints.. efi_free_pool() the result. */
+uint16_t *efi_dp_str(struct efi_device_path *dp)
+{
+	return EFI_CALL(efi_convert_device_path_to_text(dp, true, true));
 }
 
 const struct efi_device_path_to_text_protocol efi_device_path_to_text = {
-	.convert_device_node_to_text = efi_convert_device_node_to_text_ext,
+	.convert_device_node_to_text = efi_convert_device_node_to_text,
 	.convert_device_path_to_text = efi_convert_device_path_to_text,
 };

+ 44 - 59
lib/efi_loader/efi_disk.c

@@ -196,6 +196,15 @@ efi_fs_from_path(struct efi_device_path *fp)
 	return diskobj->volume;
 }
 
+/*
+ * Create a device for a disk
+ *
+ * @name	not used
+ * @if_typename interface name for block device
+ * @desc	internal block device
+ * @dev_index   device index for block device
+ * @offset	offset into disk for simple partitions
+ */
 static void efi_disk_add_dev(const char *name,
 			     const char *if_typename,
 			     struct blk_desc *desc,
@@ -204,29 +213,39 @@ static void efi_disk_add_dev(const char *name,
 			     unsigned int part)
 {
 	struct efi_disk_obj *diskobj;
+	efi_status_t ret;
 
 	/* Don't add empty devices */
 	if (!desc->lba)
 		return;
 
 	diskobj = calloc(1, sizeof(*diskobj));
+	if (!diskobj)
+		goto out_of_memory;
+
+	/* Hook up to the device list */
+	efi_add_handle(&diskobj->parent);
 
 	/* Fill in object data */
 	diskobj->dp = efi_dp_from_part(desc, part);
 	diskobj->part = part;
-	diskobj->parent.protocols[0].guid = &efi_block_io_guid;
-	diskobj->parent.protocols[0].protocol_interface = &diskobj->ops;
-	diskobj->parent.protocols[1].guid = &efi_guid_device_path;
-	diskobj->parent.protocols[1].protocol_interface = diskobj->dp;
+	ret = efi_add_protocol(diskobj->parent.handle, &efi_block_io_guid,
+			       &diskobj->ops);
+	if (ret != EFI_SUCCESS)
+		goto out_of_memory;
+	ret = efi_add_protocol(diskobj->parent.handle, &efi_guid_device_path,
+			       diskobj->dp);
+	if (ret != EFI_SUCCESS)
+		goto out_of_memory;
 	if (part >= 1) {
 		diskobj->volume = efi_simple_file_system(desc, part,
 							 diskobj->dp);
-		diskobj->parent.protocols[2].guid =
-			&efi_simple_file_system_protocol_guid;
-		diskobj->parent.protocols[2].protocol_interface =
-			diskobj->volume;
+		ret = efi_add_protocol(diskobj->parent.handle,
+				       &efi_simple_file_system_protocol_guid,
+				       &diskobj->volume);
+		if (ret != EFI_SUCCESS)
+			goto out_of_memory;
 	}
-	diskobj->parent.handle = diskobj;
 	diskobj->ops = block_io_disk_template;
 	diskobj->ifname = if_typename;
 	diskobj->dev_index = dev_index;
@@ -240,26 +259,22 @@ static void efi_disk_add_dev(const char *name,
 	diskobj->media.io_align = desc->blksz;
 	diskobj->media.last_block = desc->lba - offset;
 	diskobj->ops.media = &diskobj->media;
-
-	/* Hook up to the device list */
-	list_add_tail(&diskobj->parent.link, &efi_obj_list);
+	return;
+out_of_memory:
+	printf("ERROR: Out of memory\n");
 }
 
-static int efi_disk_create_eltorito(struct blk_desc *desc,
-				    const char *if_typename,
-				    int diskid,
-				    const char *pdevname)
+static int efi_disk_create_partitions(struct blk_desc *desc,
+				      const char *if_typename,
+				      int diskid,
+				      const char *pdevname)
 {
 	int disks = 0;
-#if CONFIG_IS_ENABLED(ISO_PARTITION)
 	char devname[32] = { 0 }; /* dp->str is u16[32] long */
 	disk_partition_t info;
 	int part;
 
-	if (desc->part_type != PART_TYPE_ISO)
-		return 0;
-
-	/* and devices for each partition: */
+	/* Add devices for each partition */
 	for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
 		if (part_get_info(desc, part, &info))
 			continue;
@@ -270,10 +285,6 @@ static int efi_disk_create_eltorito(struct blk_desc *desc,
 		disks++;
 	}
 
-	/* ... and add block device: */
-	efi_disk_add_dev(devname, if_typename, desc, diskid, 0, 0);
-#endif
-
 	return disks;
 }
 
@@ -299,31 +310,18 @@ int efi_disk_register(void)
 	     uclass_next_device_check(&dev)) {
 		struct blk_desc *desc = dev_get_uclass_platdata(dev);
 		const char *if_typename = dev->driver->name;
-		disk_partition_t info;
-		int part;
 
 		printf("Scanning disk %s...\n", dev->name);
 
-		/* add devices for each partition: */
-		for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
-			if (part_get_info(desc, part, &info))
-				continue;
-			efi_disk_add_dev(dev->name, if_typename, desc,
-					 desc->devnum, 0, part);
-		}
-
-		/* ... and add block device: */
+		/* Add block device for the full device */
 		efi_disk_add_dev(dev->name, if_typename, desc,
 				 desc->devnum, 0, 0);
 
 		disks++;
 
-		/*
-		* El Torito images show up as block devices in an EFI world,
-		* so let's create them here
-		*/
-		disks += efi_disk_create_eltorito(desc, if_typename,
-						  desc->devnum, dev->name);
+		/* Partitions show up as block devices in EFI */
+		disks += efi_disk_create_partitions(desc, if_typename,
+						    desc->devnum, dev->name);
 	}
 #else
 	int i, if_type;
@@ -342,8 +340,6 @@ int efi_disk_register(void)
 		for (i = 0; i < 4; i++) {
 			struct blk_desc *desc;
 			char devname[32] = { 0 }; /* dp->str is u16[32] long */
-			disk_partition_t info;
-			int part;
 
 			desc = blk_get_devnum_by_type(if_type, i);
 			if (!desc)
@@ -354,24 +350,13 @@ int efi_disk_register(void)
 			snprintf(devname, sizeof(devname), "%s%d",
 				 if_typename, i);
 
-			/* add devices for each partition: */
-			for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
-				if (part_get_info(desc, part, &info))
-					continue;
-				efi_disk_add_dev(devname, if_typename, desc,
-						 i, 0, part);
-			}
-
-			/* ... and add block device: */
+			/* Add block device for the full device */
 			efi_disk_add_dev(devname, if_typename, desc, i, 0, 0);
 			disks++;
 
-			/*
-			 * El Torito images show up as block devices
-			 * in an EFI world, so let's create them here
-			 */
-			disks += efi_disk_create_eltorito(desc, if_typename,
-							  i, devname);
+			/* Partitions show up as block devices in EFI */
+			disks += efi_disk_create_partitions(desc, if_typename,
+							    i, devname);
 		}
 	}
 #endif

+ 21 - 13
lib/efi_loader/efi_gop.c

@@ -32,7 +32,7 @@ struct efi_gop_obj {
 };
 
 static efi_status_t EFIAPI gop_query_mode(struct efi_gop *this, u32 mode_number,
-					  unsigned long *size_of_info,
+					  efi_uintn_t *size_of_info,
 					  struct efi_gop_mode_info **info)
 {
 	struct efi_gop_obj *gopobj;
@@ -56,17 +56,17 @@ static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
 	return EFI_EXIT(EFI_SUCCESS);
 }
 
-static efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
-				   unsigned long operation, unsigned long sx,
-				   unsigned long sy, unsigned long dx,
-				   unsigned long dy, unsigned long width,
-				   unsigned long height, unsigned long delta)
+efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
+			    u32 operation, efi_uintn_t sx,
+			    efi_uintn_t sy, efi_uintn_t dx,
+			    efi_uintn_t dy, efi_uintn_t width,
+			    efi_uintn_t height, efi_uintn_t delta)
 {
 	struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
 	int i, j, line_len16, line_len32;
 	void *fb;
 
-	EFI_ENTRY("%p, %p, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx", this,
+	EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
 		  buffer, operation, sx, sy, dx, dy, width, height, delta);
 
 	if (operation != EFI_BLT_BUFFER_TO_VIDEO)
@@ -132,6 +132,7 @@ int efi_gop_register(void)
 	u32 bpix, col, row;
 	u64 fb_base, fb_size;
 	void *fb;
+	efi_status_t ret;
 
 #ifdef CONFIG_DM_VIDEO
 	struct udevice *vdev;
@@ -173,11 +174,21 @@ int efi_gop_register(void)
 	}
 
 	gopobj = calloc(1, sizeof(*gopobj));
+	if (!gopobj) {
+		printf("ERROR: Out of memory\n");
+		return 1;
+	}
+
+	/* Hook up to the device list */
+	efi_add_handle(&gopobj->parent);
 
 	/* Fill in object data */
-	gopobj->parent.protocols[0].guid = &efi_gop_guid;
-	gopobj->parent.protocols[0].protocol_interface = &gopobj->ops;
-	gopobj->parent.handle = &gopobj->ops;
+	ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid,
+			       &gopobj->ops);
+	if (ret != EFI_SUCCESS) {
+		printf("ERROR: Out of memory\n");
+		return 1;
+	}
 	gopobj->ops.query_mode = gop_query_mode;
 	gopobj->ops.set_mode = gop_set_mode;
 	gopobj->ops.blt = gop_blt;
@@ -206,8 +217,5 @@ int efi_gop_register(void)
 	gopobj->bpix = bpix;
 	gopobj->fb = fb;
 
-	/* Hook up to the device list */
-	list_add_tail(&gopobj->parent.link, &efi_obj_list);
-
 	return 0;
 }

+ 10 - 10
lib/efi_loader/efi_memory.c

@@ -276,7 +276,7 @@ static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr)
 }
 
 efi_status_t efi_allocate_pages(int type, int memory_type,
-				unsigned long pages, uint64_t *memory)
+				efi_uintn_t pages, uint64_t *memory)
 {
 	u64 len = pages << EFI_PAGE_SHIFT;
 	efi_status_t r = EFI_SUCCESS;
@@ -338,7 +338,7 @@ void *efi_alloc(uint64_t len, int memory_type)
 	return NULL;
 }
 
-efi_status_t efi_free_pages(uint64_t memory, unsigned long pages)
+efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
 {
 	uint64_t r = 0;
 
@@ -351,7 +351,7 @@ efi_status_t efi_free_pages(uint64_t memory, unsigned long pages)
 	return EFI_NOT_FOUND;
 }
 
-efi_status_t efi_allocate_pool(int pool_type, unsigned long size,
+efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size,
 			       void **buffer)
 {
 	efi_status_t r;
@@ -392,16 +392,16 @@ efi_status_t efi_free_pool(void *buffer)
 	return r;
 }
 
-efi_status_t efi_get_memory_map(unsigned long *memory_map_size,
-			       struct efi_mem_desc *memory_map,
-			       unsigned long *map_key,
-			       unsigned long *descriptor_size,
-			       uint32_t *descriptor_version)
+efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
+				struct efi_mem_desc *memory_map,
+				efi_uintn_t *map_key,
+				efi_uintn_t *descriptor_size,
+				uint32_t *descriptor_version)
 {
-	ulong map_size = 0;
+	efi_uintn_t map_size = 0;
 	int map_entries = 0;
 	struct list_head *lhandle;
-	unsigned long provided_map_size = *memory_map_size;
+	efi_uintn_t provided_map_size = *memory_map_size;
 
 	list_for_each(lhandle, &efi_mem)
 		map_entries++;

+ 20 - 11
lib/efi_loader/efi_net.c

@@ -292,16 +292,25 @@ int efi_net_register(void)
 
 	/* We only expose the "active" eth device, so one is enough */
 	netobj = calloc(1, sizeof(*netobj));
+	if (!netobj)
+		goto out_of_memory;
+
+	/* Hook net up to the device list */
+	efi_add_handle(&netobj->parent);
 
 	/* Fill in object data */
-	netobj->parent.protocols[0].guid = &efi_net_guid;
-	netobj->parent.protocols[0].protocol_interface = &netobj->net;
-	netobj->parent.protocols[1].guid = &efi_guid_device_path;
-	netobj->parent.protocols[1].protocol_interface =
-		efi_dp_from_eth();
-	netobj->parent.protocols[2].guid = &efi_pxe_guid;
-	netobj->parent.protocols[2].protocol_interface = &netobj->pxe;
-	netobj->parent.handle = &netobj->net;
+	r = efi_add_protocol(netobj->parent.handle, &efi_net_guid,
+			     &netobj->net);
+	if (r != EFI_SUCCESS)
+		goto out_of_memory;
+	r = efi_add_protocol(netobj->parent.handle, &efi_guid_device_path,
+			     efi_dp_from_eth());
+	if (r != EFI_SUCCESS)
+		goto out_of_memory;
+	r = efi_add_protocol(netobj->parent.handle, &efi_pxe_guid,
+			     &netobj->pxe);
+	if (r != EFI_SUCCESS)
+		goto out_of_memory;
 	netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
 	netobj->net.start = efi_net_start;
 	netobj->net.stop = efi_net_stop;
@@ -326,9 +335,6 @@ int efi_net_register(void)
 	if (dhcp_ack)
 		netobj->pxe_mode.dhcp_ack = *dhcp_ack;
 
-	/* Hook net up to the device list */
-	list_add_tail(&netobj->parent.link, &efi_obj_list);
-
 	/*
 	 * Create WaitForPacket event.
 	 */
@@ -361,4 +367,7 @@ int efi_net_register(void)
 	}
 
 	return 0;
+out_of_memory:
+	printf("ERROR: Out of memory\n");
+	return 1;
 }

+ 89 - 0
lib/efi_loader/efi_watchdog.c

@@ -0,0 +1,89 @@
+/*
+ *  EFI watchdog
+ *
+ *  Copyright (c) 2017 Heinrich Schuchardt
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+/* Conversion factor from seconds to multiples of 100ns */
+#define EFI_SECONDS_TO_100NS 10000000ULL
+
+static struct efi_event *watchdog_timer_event;
+
+/*
+ * Reset the system when the watchdog event is notified.
+ *
+ * @event:	the watchdog event
+ * @context:	not used
+ */
+static void EFIAPI efi_watchdog_timer_notify(struct efi_event *event,
+					     void *context)
+{
+	EFI_ENTRY("%p, %p", event, context);
+
+	printf("\nEFI: Watchdog timeout\n");
+	EFI_CALL_VOID(efi_runtime_services.reset_system(EFI_RESET_COLD,
+							EFI_SUCCESS, 0, NULL));
+
+	EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/*
+ * Reset the watchdog timer.
+ *
+ * This function is used by the SetWatchdogTimer service.
+ *
+ * @timeout:		seconds before reset by watchdog
+ * @return:		status code
+ */
+efi_status_t efi_set_watchdog(unsigned long timeout)
+{
+	efi_status_t r;
+
+	if (timeout)
+		/* Reset watchdog */
+		r = efi_set_timer(watchdog_timer_event, EFI_TIMER_RELATIVE,
+				  EFI_SECONDS_TO_100NS * timeout);
+	else
+		/* Deactivate watchdog */
+		r = efi_set_timer(watchdog_timer_event, EFI_TIMER_STOP, 0);
+	return r;
+}
+
+/*
+ * Initialize the EFI watchdog.
+ *
+ * This function is called by efi_init_obj_list()
+ */
+int efi_watchdog_register(void)
+{
+	efi_status_t r;
+
+	/*
+	 * Create a timer event.
+	 */
+	r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+			     efi_watchdog_timer_notify, NULL,
+			     &watchdog_timer_event);
+	if (r != EFI_SUCCESS) {
+		printf("ERROR: Failed to register watchdog event\n");
+		return r;
+	}
+	/*
+	 * The UEFI standard requires that the watchdog timer is set to five
+	 * minutes when invoking an EFI boot option.
+	 *
+	 * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A
+	 * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer
+	 */
+	r = efi_set_watchdog(300);
+	if (r != EFI_SUCCESS) {
+		printf("ERROR: Failed to set watchdog timer\n");
+		return r;
+	}
+	return 0;
+}

+ 35 - 3
lib/efi_loader/helloworld.c

@@ -5,20 +5,52 @@
  * Written by Simon Glass <sjg@chromium.org>
  *
  * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * This program demonstrates calling a boottime service.
+ * It writes a greeting and the load options to the console.
  */
 
 #include <common.h>
-#include <part_efi.h>
 #include <efi_api.h>
 
+/*
+ * Entry point of the EFI application.
+ *
+ * @handle	handle of the loaded image
+ * @systable	system table
+ * @return	status code
+ */
 efi_status_t EFIAPI efi_main(efi_handle_t handle,
 			     struct efi_system_table *systable)
 {
 	struct efi_simple_text_output_protocol *con_out = systable->con_out;
 	struct efi_boot_services *boottime = systable->boottime;
+	struct efi_loaded_image *loaded_image;
+	const efi_guid_t loaded_image_guid = LOADED_IMAGE_GUID;
+	efi_status_t ret;
 
 	con_out->output_string(con_out, L"Hello, world!\n");
-	boottime->exit(handle, 0, 0, NULL);
 
-	return EFI_SUCCESS;
+	/* 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\n");
+		goto out;
+	}
+	/* Output the load options */
+	con_out->output_string(con_out, L"Load options: ");
+	if (loaded_image->load_options_size && loaded_image->load_options)
+		con_out->output_string(con_out,
+				       (u16 *)loaded_image->load_options);
+	else
+		con_out->output_string(con_out, L"<none>");
+	con_out->output_string(con_out, L"\n");
+
+out:
+	boottime->exit(handle, ret, 0, NULL);
+
+	/* We should never arrive here */
+	return ret;
 }

+ 6 - 16
lib/efi_selftest/Makefile

@@ -7,26 +7,16 @@
 # This file only gets included with CONFIG_EFI_LOADER set, so all
 # object inclusion implicitly depends on it
 
-CFLAGS_efi_selftest.o := $(CFLAGS_EFI)
-CFLAGS_REMOVE_efi_selftest.o := $(CFLAGS_NON_EFI)
-CFLAGS_efi_selftest_console.o := $(CFLAGS_EFI)
-CFLAGS_REMOVE_efi_selftest_console.o := $(CFLAGS_NON_EFI)
-CFLAGS_efi_selftest_events.o := $(CFLAGS_EFI)
-CFLAGS_REMOVE_efi_selftest_events.o := $(CFLAGS_NON_EFI)
-CFLAGS_efi_selftest_exitbootservices.o := $(CFLAGS_EFI)
-CFLAGS_REMOVE_efi_selftest_exitbootservices.o := $(CFLAGS_NON_EFI)
-CFLAGS_efi_selftest_snp.o := $(CFLAGS_EFI)
-CFLAGS_REMOVE_efi_selftest_snp.o := $(CFLAGS_NON_EFI)
-CFLAGS_efi_selftest_tpl.o := $(CFLAGS_EFI)
-CFLAGS_REMOVE_efi_selftest_tpl.o := $(CFLAGS_NON_EFI)
-CFLAGS_efi_selftest_util.o := $(CFLAGS_EFI)
-CFLAGS_REMOVE_efi_selftest_util.o := $(CFLAGS_NON_EFI)
-
 obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
 efi_selftest.o \
 efi_selftest_console.o \
+efi_selftest_devicepath.o \
 efi_selftest_events.o \
 efi_selftest_exitbootservices.o \
+efi_selftest_gop.o \
+efi_selftest_manageprotocols.o \
 efi_selftest_snp.o \
+efi_selftest_textoutput.o \
 efi_selftest_tpl.o \
-efi_selftest_util.o
+efi_selftest_util.o \
+efi_selftest_watchdog.o

+ 119 - 34
lib/efi_selftest/efi_selftest.c

@@ -9,6 +9,13 @@
 #include <efi_selftest.h>
 #include <vsprintf.h>
 
+/*
+ * Constants for test step bitmap
+ */
+#define EFI_ST_SETUP	1
+#define EFI_ST_EXECUTE	2
+#define EFI_ST_TEARDOWN	4
+
 static const struct efi_system_table *systable;
 static const struct efi_boot_services *boottime;
 static const struct efi_runtime_services *runtime;
@@ -25,9 +32,9 @@ static u16 reset_message[] = L"Selftest completed";
  */
 void efi_st_exit_boot_services(void)
 {
-	unsigned long  map_size = 0;
-	unsigned long  map_key;
-	unsigned long desc_size;
+	efi_uintn_t map_size = 0;
+	efi_uintn_t map_key;
+	efi_uintn_t desc_size;
 	u32 desc_version;
 	efi_status_t ret;
 	struct efi_mem_desc *memory_map;
@@ -133,6 +140,70 @@ static int teardown(struct efi_unit_test *test, unsigned int *failures)
 	return ret;
 }
 
+/*
+ * Check that a test exists.
+ *
+ * @testname:	name of the test
+ * @return:	test
+ */
+static struct efi_unit_test *find_test(const u16 *testname)
+{
+	struct efi_unit_test *test;
+
+	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+		if (!efi_st_strcmp_16_8(testname, test->name))
+			return test;
+	}
+	efi_st_printf("\nTest '%ps' not found\n", testname);
+	return NULL;
+}
+
+/*
+ * List all available tests.
+ */
+static void list_all_tests(void)
+{
+	struct efi_unit_test *test;
+
+	/* List all tests */
+	efi_st_printf("\nAvailable tests:\n");
+	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+		efi_st_printf("'%s'%s\n", test->name,
+			      test->on_request ? " - on request" : "");
+	}
+}
+
+/*
+ * Execute test steps of one phase.
+ *
+ * @testname	name of a single selected test or NULL
+ * @phase	test phase
+ * @steps	steps to execute
+ * failures	returns EFI_ST_SUCCESS if all test steps succeeded
+ */
+void efi_st_do_tests(const u16 *testname, unsigned int phase,
+		     unsigned int steps, unsigned int *failures)
+{
+	struct efi_unit_test *test;
+
+	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+		if (testname ?
+		    efi_st_strcmp_16_8(testname, test->name) : test->on_request)
+			continue;
+		if (test->phase != phase)
+			continue;
+		if (steps & EFI_ST_SETUP)
+			setup(test, failures);
+		if (steps & EFI_ST_EXECUTE)
+			execute(test, failures);
+		if (steps & EFI_ST_TEARDOWN)
+			teardown(test, failures);
+	}
+}
+
 /*
  * Execute selftest of the EFI API
  *
@@ -153,8 +224,10 @@ static int teardown(struct efi_unit_test *test, unsigned int *failures)
 efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
 				 struct efi_system_table *systab)
 {
-	struct efi_unit_test *test;
 	unsigned int failures = 0;
+	const u16 *testname = NULL;
+	struct efi_loaded_image *loaded_image;
+	efi_status_t ret;
 
 	systable = systab;
 	boottime = systable->boottime;
@@ -163,47 +236,59 @@ efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
 	con_out = systable->con_out;
 	con_in = systable->con_in;
 
-	efi_st_printf("\nTesting EFI API implementation\n");
+	ret = boottime->handle_protocol(image_handle, &efi_guid_loaded_image,
+					(void **)&loaded_image);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Cannot open loaded image protocol\n");
+		return ret;
+	}
 
-	efi_st_printf("\nNumber of tests to execute: %u\n",
-		      ll_entry_count(struct efi_unit_test, efi_unit_test));
+	if (loaded_image->load_options)
+		testname = (u16 *)loaded_image->load_options;
 
-	/* Execute boottime tests */
-	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
-	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
-		if (test->phase == EFI_EXECUTE_BEFORE_BOOTTIME_EXIT) {
-			setup(test, &failures);
-			execute(test, &failures);
-			teardown(test, &failures);
+	if (testname) {
+		if (!efi_st_strcmp_16_8(testname, "list") ||
+		    !find_test(testname)) {
+			list_all_tests();
+			/*
+			 * TODO:
+			 * Once the Exit boottime service is correctly
+			 * implemented we should call
+			 *   boottime->exit(image_handle, EFI_SUCCESS, 0, NULL);
+			 * here, cf.
+			 * https://lists.denx.de/pipermail/u-boot/2017-October/308720.html
+			 */
+			return EFI_SUCCESS;
 		}
 	}
 
+	efi_st_printf("\nTesting EFI API implementation\n");
+
+	if (testname)
+		efi_st_printf("\nSelected test: '%ps'\n", testname);
+	else
+		efi_st_printf("\nNumber of tests to execute: %u\n",
+			      ll_entry_count(struct efi_unit_test,
+					     efi_unit_test));
+
+	/* Execute boottime tests */
+	efi_st_do_tests(testname, EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+			EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
+			&failures);
+
 	/* Execute mixed tests */
-	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
-	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
-		if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT)
-			setup(test, &failures);
-	}
+	efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+			EFI_ST_SETUP, &failures);
 
 	efi_st_exit_boot_services();
 
-	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
-	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
-		if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT) {
-			execute(test, &failures);
-			teardown(test, &failures);
-		}
-	}
+	efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+			EFI_ST_EXECUTE | EFI_ST_TEARDOWN, &failures);
 
 	/* Execute runtime tests */
-	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
-	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
-		if (test->phase == EFI_SETUP_AFTER_BOOTTIME_EXIT) {
-			setup(test, &failures);
-			execute(test, &failures);
-			teardown(test, &failures);
-		}
-	}
+	efi_st_do_tests(testname, EFI_SETUP_AFTER_BOOTTIME_EXIT,
+			EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
+			&failures);
 
 	/* Give feedback */
 	efi_st_printf("\nSummary: %u failures\n\n", failures);

+ 10 - 0
lib/efi_selftest/efi_selftest_console.c

@@ -142,6 +142,7 @@ void efi_st_printf(const char *fmt, ...)
 	const char *c;
 	u16 *pos = buf;
 	const char *s;
+	const u16 *u;
 
 	va_start(args, fmt);
 
@@ -179,9 +180,18 @@ void efi_st_printf(const char *fmt, ...)
 			case 'p':
 				++c;
 				switch (*c) {
+				/* MAC address */
 				case 'm':
 					mac(va_arg(args, void*), &pos);
 					break;
+
+				/* u16 string */
+				case 's':
+					u = va_arg(args, u16*);
+					/* Ensure string fits into buffer */
+					for (; *u && pos < buf + 120; ++u)
+						*pos++ = *u;
+					break;
 				default:
 					--c;
 					pointer(va_arg(args, void*), &pos);

+ 390 - 0
lib/efi_selftest/efi_selftest_devicepath.c

@@ -0,0 +1,390 @@
+/*
+ * efi_selftest_devicepath
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * This unit test checks the following protocol services:
+ * DevicePathToText
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+
+static efi_handle_t handle1;
+static efi_handle_t handle2;
+static efi_handle_t handle3;
+
+struct interface {
+	void (EFIAPI * inc)(void);
+} interface;
+
+static efi_guid_t guid_device_path = DEVICE_PATH_GUID;
+
+static efi_guid_t guid_device_path_to_text_protocol =
+	EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+
+static efi_guid_t guid_protocol =
+	EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+		 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0x7d);
+
+static efi_guid_t guid_vendor1 =
+	EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+		 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xb1);
+
+static efi_guid_t guid_vendor2 =
+	EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+		 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xa2);
+
+static efi_guid_t guid_vendor3 =
+	EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+		 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xc3);
+
+static u8 *dp1;
+static u8 *dp2;
+static u8 *dp3;
+
+struct efi_device_path_to_text_protocol *device_path_to_text;
+
+/*
+ * Setup unit test.
+ *
+ * Create three handles. Install a new protocol on two of them and
+ * provice device paths.
+ *
+ * handle1
+ *   guid interface
+ * handle2
+ *   guid interface
+ * handle3
+ *
+ * @handle:	handle of the loaded image
+ * @systable:	system table
+ */
+static int setup(const efi_handle_t img_handle,
+		 const struct efi_system_table *systable)
+{
+	struct efi_device_path_vendor vendor_node;
+	struct efi_device_path end_node;
+	efi_status_t ret;
+
+	boottime = systable->boottime;
+
+	ret = boottime->locate_protocol(&guid_device_path_to_text_protocol,
+					NULL, (void **)&device_path_to_text);
+	if (ret != EFI_SUCCESS) {
+		device_path_to_text = NULL;
+		efi_st_error(
+			"Device path to text protocol is not available.\n");
+		return EFI_ST_FAILURE;
+	}
+
+	ret = boottime->allocate_pool(EFI_LOADER_DATA,
+				      sizeof(struct efi_device_path_vendor) +
+				      sizeof(struct efi_device_path),
+				      (void **)&dp1);
+	if (ret != EFI_SUCCESS)
+		goto out_of_memory;
+
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, 2 *
+				      sizeof(struct efi_device_path_vendor) +
+				      sizeof(struct efi_device_path),
+				      (void **)&dp2);
+	if (ret != EFI_SUCCESS)
+		goto out_of_memory;
+
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, 3 *
+				      sizeof(struct efi_device_path_vendor) +
+				      sizeof(struct efi_device_path),
+				      (void **)&dp3);
+	if (ret != EFI_SUCCESS)
+		goto out_of_memory;
+
+	vendor_node.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+	vendor_node.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+	vendor_node.dp.length = sizeof(struct efi_device_path_vendor);
+
+	boottime->copy_mem(&vendor_node.guid, &guid_vendor1,
+			   sizeof(efi_guid_t));
+	boottime->copy_mem(dp1, &vendor_node,
+			   sizeof(struct efi_device_path_vendor));
+	boottime->copy_mem(dp2, &vendor_node,
+			   sizeof(struct efi_device_path_vendor));
+	boottime->copy_mem(dp3, &vendor_node,
+			   sizeof(struct efi_device_path_vendor));
+
+	boottime->copy_mem(&vendor_node.guid, &guid_vendor2,
+			   sizeof(efi_guid_t));
+	boottime->copy_mem(dp2 + sizeof(struct efi_device_path_vendor),
+			   &vendor_node, sizeof(struct efi_device_path_vendor));
+	boottime->copy_mem(dp3 + sizeof(struct efi_device_path_vendor),
+			   &vendor_node, sizeof(struct efi_device_path_vendor));
+
+	boottime->copy_mem(&vendor_node.guid, &guid_vendor3,
+			   sizeof(efi_guid_t));
+	boottime->copy_mem(dp3 + 2 * sizeof(struct efi_device_path_vendor),
+			   &vendor_node, sizeof(struct efi_device_path_vendor));
+
+	end_node.type = DEVICE_PATH_TYPE_END;
+	end_node.sub_type = DEVICE_PATH_SUB_TYPE_END;
+	end_node.length = sizeof(struct efi_device_path);
+	boottime->copy_mem(dp1 + sizeof(struct efi_device_path_vendor),
+			   &end_node, sizeof(struct efi_device_path));
+	boottime->copy_mem(dp2 + 2 * sizeof(struct efi_device_path_vendor),
+			   &end_node, sizeof(struct efi_device_path));
+	boottime->copy_mem(dp3 + 3 * sizeof(struct efi_device_path_vendor),
+			   &end_node, sizeof(struct efi_device_path));
+
+	ret = boottime->install_protocol_interface(&handle1,
+						   &guid_device_path,
+						   EFI_NATIVE_INTERFACE,
+						   dp1);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("InstallProtocolInterface failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->install_protocol_interface(&handle1,
+						   &guid_protocol,
+						   EFI_NATIVE_INTERFACE,
+						   &interface);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("InstallProtocolInterface failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->install_protocol_interface(&handle2,
+						   &guid_device_path,
+						   EFI_NATIVE_INTERFACE,
+						   dp2);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("InstallProtocolInterface failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->install_protocol_interface(&handle2,
+						   &guid_protocol,
+						   EFI_NATIVE_INTERFACE,
+						   &interface);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("InstallProtocolInterface failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->install_protocol_interface(&handle3,
+						   &guid_device_path,
+						   EFI_NATIVE_INTERFACE,
+						   dp3);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("InstallProtocolInterface failed\n");
+		return EFI_ST_FAILURE;
+	}
+	return EFI_ST_SUCCESS;
+
+out_of_memory:
+	efi_st_error("Out of memory\n");
+	return EFI_ST_FAILURE;
+}
+
+/*
+ * Tear down unit test.
+ *
+ */
+static int teardown(void)
+{
+	efi_status_t ret;
+
+	ret = boottime->uninstall_protocol_interface(&handle1,
+						     &guid_device_path,
+						     dp1);
+	if (ret != EFI_SUCCESS)
+		efi_st_todo("UninstallProtocolInterface failed\n");
+	ret = boottime->uninstall_protocol_interface(&handle1,
+						     &guid_protocol,
+						     &interface);
+	if (ret != EFI_SUCCESS)
+		efi_st_todo("UninstallProtocolInterface failed\n");
+	ret = boottime->uninstall_protocol_interface(&handle2,
+						     &guid_device_path,
+						     dp2);
+	if (ret != EFI_SUCCESS)
+		efi_st_todo("UninstallProtocolInterface failed\n");
+	ret = boottime->uninstall_protocol_interface(&handle2,
+						     &guid_protocol,
+						     &interface);
+	if (ret != EFI_SUCCESS)
+		efi_st_todo("UninstallProtocolInterface failed\n");
+	ret = boottime->uninstall_protocol_interface(&handle3,
+						     &guid_device_path,
+						     dp3);
+	if (ret != EFI_SUCCESS)
+		efi_st_todo("UninstallProtocolInterface failed\n");
+	if (dp1) {
+		ret = boottime->free_pool(dp1);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("FreePool failed\n");
+			return EFI_ST_FAILURE;
+		}
+	}
+	if (dp2) {
+		ret = boottime->free_pool(dp2);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("FreePool failed\n");
+			return EFI_ST_FAILURE;
+		}
+	}
+	if (dp3) {
+		ret = boottime->free_pool(dp3);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("FreePool failed\n");
+			return EFI_ST_FAILURE;
+		}
+	}
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ */
+static int execute(void)
+{
+	struct efi_device_path *remaining_dp;
+	void *handle;
+	/*
+	 * This device path node ends with the letter 't' of 'u-boot'.
+	 * The following '.bin' does not belong to the node but is
+	 * helps to test the correct truncation.
+	 */
+	struct {
+		struct efi_device_path dp;
+		u16 text[12];
+	} __packed dp_node = {
+			{ DEVICE_PATH_TYPE_MEDIA_DEVICE,
+			  DEVICE_PATH_SUB_TYPE_FILE_PATH,
+			  sizeof(struct efi_device_path) + 12},
+			L"u-boot.bin",
+		};
+	u16 *string;
+	efi_status_t ret;
+	efi_uintn_t i, no_handles;
+	efi_handle_t *handles;
+	struct efi_device_path *dp;
+
+	/* Display all available device paths */
+	ret = boottime->locate_handle_buffer(BY_PROTOCOL,
+					     &guid_device_path,
+					     NULL, &no_handles, &handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Cannot retrieve device path protocols.\n");
+		return EFI_ST_FAILURE;
+	}
+
+	efi_st_printf("Installed device path protocols:\n");
+	for (i = 0; i < no_handles; ++i) {
+		ret = boottime->open_protocol(handles[i], &guid_device_path,
+					      (void **)&dp, NULL, NULL,
+					      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("Cannot open device path protocol.\n");
+			return EFI_ST_FAILURE;
+		}
+		string = device_path_to_text->convert_device_path_to_text(
+					dp, true, false);
+		if (!string) {
+			efi_st_error("ConvertDevicePathToText failed\n");
+			return EFI_ST_FAILURE;
+		}
+		efi_st_printf("%ps\n", string);
+		ret = boottime->free_pool(string);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("FreePool failed\n");
+			return EFI_ST_FAILURE;
+		}
+		ret = boottime->close_protocol(handles[i], &guid_device_path,
+					       NULL, NULL);
+		if (ret != EFI_SUCCESS)
+			efi_st_todo("Cannot close device path protocol.\n");
+	}
+	ret = boottime->free_pool(handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		return EFI_ST_FAILURE;
+	}
+	efi_st_printf("\n");
+
+	/* Test ConvertDevicePathToText */
+	string = device_path_to_text->convert_device_path_to_text(
+			(struct efi_device_path *)dp2, true, false);
+	if (!string) {
+		efi_st_error("ConvertDevicePathToText failed\n");
+		return EFI_ST_FAILURE;
+	}
+	efi_st_printf("dp2: %ps\n", string);
+	if (efi_st_strcmp_16_8(
+		string,
+		"/VenHw(dbca4c98-6cb0-694d-0872-819c650cbbb1)/VenHw(dbca4c98-6cb0-694d-0872-819c650cbba2)")
+	    ) {
+		efi_st_error("Incorrect text from ConvertDevicePathToText\n");
+		return EFI_ST_FAILURE;
+	}
+
+	ret = boottime->free_pool(string);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		return EFI_ST_FAILURE;
+	}
+
+	/* Test ConvertDeviceNodeToText */
+	string = device_path_to_text->convert_device_node_to_text(
+			(struct efi_device_path *)&dp_node, true, false);
+	if (!string) {
+		efi_st_error("ConvertDeviceNodeToText failed\n");
+		return EFI_ST_FAILURE;
+	}
+	efi_st_printf("dp_node: %ps\n", string);
+	ret = boottime->free_pool(string);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (efi_st_strcmp_16_8(string, "u-boot")) {
+		efi_st_error(
+			"Incorrect conversion by ConvertDeviceNodeToText\n");
+		return EFI_ST_FAILURE;
+	}
+
+	/* Test LocateDevicePath */
+	remaining_dp = (struct efi_device_path *)dp3;
+	ret = boottime->locate_device_path(&guid_protocol, &remaining_dp,
+					   &handle);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateDevicePath failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (handle != handle2) {
+		efi_st_error("LocateDevicePath returned wrong handle\n");
+		return EFI_ST_FAILURE;
+	}
+	string = device_path_to_text->convert_device_path_to_text(remaining_dp,
+								  true, false);
+	if (!string) {
+		efi_st_error("ConvertDevicePathToText failed\n");
+		return EFI_ST_FAILURE;
+	}
+	efi_st_printf("remaining device path: %ps\n", string);
+	if (efi_st_strcmp_16_8(string,
+			       "/VenHw(dbca4c98-6cb0-694d-0872-819c650cbbc3)")
+	    ) {
+		efi_st_error("LocateDevicePath: wrong remaining device path\n");
+		return EFI_ST_FAILURE;
+	}
+
+	return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(devicepath) = {
+	.name = "device path",
+	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+	.setup = setup,
+	.execute = execute,
+	.teardown = teardown,
+};

+ 1 - 1
lib/efi_selftest/efi_selftest_events.c

@@ -108,7 +108,7 @@ static int teardown(void)
  */
 static int execute(void)
 {
-	size_t index;
+	efi_uintn_t index;
 	efi_status_t ret;
 
 	/* Set 10 ms timer */

+ 95 - 0
lib/efi_selftest/efi_selftest_gop.c

@@ -0,0 +1,95 @@
+/*
+ * efi_selftest_gop
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * Test the graphical output protocol.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+static efi_guid_t efi_gop_guid = EFI_GOP_GUID;
+static struct efi_gop *gop;
+
+/*
+ * Setup unit test.
+ *
+ * @handle:	handle of the loaded image
+ * @systable:	system table
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+		 const struct efi_system_table *systable)
+{
+	efi_status_t ret;
+
+	boottime = systable->boottime;
+
+	ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
+	if (ret != EFI_SUCCESS) {
+		gop = NULL;
+		efi_st_printf("Graphical output protocol is not available.\n");
+	}
+
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+	efi_status_t ret;
+	u32 i, max_mode;
+	efi_uintn_t size;
+	struct efi_gop_mode_info *info;
+
+	if (!gop)
+		return EFI_ST_SUCCESS;
+
+	if (!gop->mode) {
+		efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n");
+		return EFI_ST_FAILURE;
+	}
+	max_mode = gop->mode->max_mode;
+	if (!max_mode) {
+		efi_st_error("No graphical mode available\n");
+		return EFI_ST_FAILURE;
+	}
+	efi_st_printf("Number of available modes: %u\n", max_mode);
+
+	for (i = 0; i < max_mode; ++i) {
+		ret = gop->query_mode(gop, i, &size, &info);
+		if (ret != EFI_SUCCESS) {
+			efi_st_printf("Could not query mode %u\n", i);
+			return EFI_ST_FAILURE;
+		}
+		efi_st_printf("Mode %u: %u x %u\n",
+			      i, info->width, info->height);
+	}
+
+	return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(gop) = {
+	.name = "graphical output",
+	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+	.setup = setup,
+	.execute = execute,
+	.teardown = teardown,
+};

+ 354 - 0
lib/efi_selftest/efi_selftest_manageprotocols.c

@@ -0,0 +1,354 @@
+/*
+ * efi_selftest_manageprotocols
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * This unit test checks the following protocol services:
+ * InstallProtocolInterface, UninstallProtocolInterface,
+ * InstallMultipleProtocolsInterfaces, UninstallMultipleProtocolsInterfaces,
+ * HandleProtocol, ProtocolsPerHandle,
+ * LocateHandle, LocateHandleBuffer.
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * The test currently does not actually call the interface function.
+ * So this is just a dummy structure.
+ */
+struct interface {
+	void (EFIAPI * inc)(void);
+};
+
+static struct efi_boot_services *boottime;
+static efi_guid_t guid1 =
+	EFI_GUID(0x2e7ca819, 0x21d3, 0x0a3a,
+		 0xf7, 0x91, 0x82, 0x1f, 0x7a, 0x83, 0x67, 0xaf);
+static efi_guid_t guid2 =
+	EFI_GUID(0xf909f2bb, 0x90a8, 0x0d77,
+		 0x94, 0x0c, 0x3e, 0xa8, 0xea, 0x38, 0xd6, 0x6f);
+static efi_guid_t guid3 =
+	EFI_GUID(0x06d641a3, 0xf4e7, 0xe0c9,
+		 0xe7, 0x8d, 0x41, 0x2d, 0x72, 0xa6, 0xb1, 0x24);
+static efi_handle_t handle1;
+static efi_handle_t handle2;
+static struct interface interface1;
+static struct interface interface2;
+static struct interface interface3;
+static struct interface interface4;
+
+/*
+ * Find a handle in an array.
+ *
+ * @handle:	handle to find
+ * @count:	number of entries in the array
+ * @buffer:	array to search
+ */
+efi_status_t find_in_buffer(efi_handle_t handle, size_t count,
+			    efi_handle_t *buffer)
+{
+	size_t i;
+
+	for (i = 0; i < count; ++i) {
+		if (buffer[i] == handle)
+			return EFI_SUCCESS;
+	}
+	return EFI_NOT_FOUND;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two handles and install two out of three protocol interfaces on each
+ * of them:
+ *
+ * handle1
+ *   guid1 interface1
+ *   guid3 interface3
+ * handle2
+ *   guid1 interface4
+ *   guid2 interface2
+ *
+ * @handle:	handle of the loaded image
+ * @systable:	system table
+ */
+static int setup(const efi_handle_t img_handle,
+		 const struct efi_system_table *systable)
+{
+	efi_status_t ret;
+	efi_handle_t handle;
+
+	boottime = systable->boottime;
+
+	ret = boottime->install_protocol_interface(&handle1, &guid3,
+						   EFI_NATIVE_INTERFACE,
+						   &interface3);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("InstallProtocolInterface failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (!handle1) {
+		efi_st_error("InstallProtocolInterface failed to create handle\n");
+		return EFI_ST_FAILURE;
+	}
+	handle = handle1;
+	ret = boottime->install_protocol_interface(&handle1, &guid1,
+						   EFI_NATIVE_INTERFACE,
+						   &interface1);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("InstallProtocolInterface failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (handle != handle1) {
+		efi_st_error("InstallProtocolInterface failed to use handle\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->install_multiple_protocol_interfaces(&handle2,
+			&guid1, &interface4, &guid2, &interface2, NULL);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("InstallMultipleProtocolInterfaces failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (!handle2 || handle1 == handle2) {
+		efi_st_error("InstallMultipleProtocolInterfaces failed to create handle\n");
+		return EFI_ST_FAILURE;
+	}
+
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ */
+static int teardown(void)
+{
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ */
+static int execute(void)
+{
+	struct interface *interface;
+	efi_status_t ret;
+	efi_handle_t *buffer;
+	size_t buffer_size;
+	efi_uintn_t count = 0;
+	efi_guid_t **prot_buffer;
+	efi_uintn_t prot_count;
+
+	/*
+	 * Test HandleProtocol
+	 */
+	ret = boottime->handle_protocol(handle1, &guid3, (void **)&interface);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("HandleProtocol failed to retrieve interface\n");
+		return EFI_ST_FAILURE;
+	}
+	if (interface != &interface3) {
+		efi_st_error("HandleProtocol returned wrong interface\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->handle_protocol(handle1, &guid2, (void **)&interface);
+	if (ret == EFI_SUCCESS) {
+		efi_st_error("HandleProtocol returned not installed interface\n");
+		return EFI_ST_FAILURE;
+	}
+
+	/*
+	 * Test LocateHandleBuffer with AllHandles
+	 */
+	ret = boottime->locate_handle_buffer(ALL_HANDLES, NULL, NULL,
+					     &count, &buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandleBuffer with AllHandles failed\n");
+		return EFI_ST_FAILURE;
+	}
+	buffer_size = count;
+	ret = find_in_buffer(handle1, count, buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = find_in_buffer(handle2, count, buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+		return EFI_ST_FAILURE;
+	}
+	boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+	/*
+	 * Test error handling in UninstallMultipleProtocols
+	 *
+	 * Try to uninstall more protocols than there are installed.
+	 */
+	ret = boottime->uninstall_multiple_protocol_interfaces(
+						handle2,
+						&guid1, &interface4,
+						&guid2, &interface2,
+						&guid3, &interface3,
+						NULL);
+	if (ret == EFI_SUCCESS) {
+		efi_st_todo("UninstallMultipleProtocolInterfaces did not catch error\n");
+		return EFI_ST_FAILURE;
+	}
+
+	/*
+	 * Test LocateHandleBuffer with ByProtocol
+	 */
+	count = buffer_size;
+	ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL,
+					     &count, &buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandleBuffer failed to locate new handles\n");
+		return EFI_ST_FAILURE;
+	}
+	if (count != 2) {
+		efi_st_error("LocateHandleBuffer failed to locate new handles\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = find_in_buffer(handle1, count, buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = find_in_buffer(handle2, count, buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+		return EFI_ST_FAILURE;
+	}
+	boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+	/*
+	 * Test LocateHandle with ByProtocol
+	 */
+	count = buffer_size * sizeof(efi_handle_t);
+	ret = boottime->locate_handle(BY_PROTOCOL, &guid1, NULL,
+				      &count, buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandle with ByProtocol failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (count / sizeof(efi_handle_t) != 2) {
+		efi_st_error("LocateHandle failed to locate new handles\n");
+		return EFI_ST_FAILURE;
+	}
+	buffer_size = count;
+	ret = find_in_buffer(handle1, count, buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandle failed to locate new handles\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = find_in_buffer(handle2, count, buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandle failed to locate new handles\n");
+		return EFI_ST_FAILURE;
+	}
+	boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+	/*
+	 * Test LocateProtocol
+	 */
+	ret = boottime->locate_protocol(&guid1, NULL, (void **)&interface);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateProtocol failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (interface != &interface1 && interface != &interface4) {
+		efi_st_error("LocateProtocol failed to locate protocol\n");
+		return EFI_ST_FAILURE;
+	}
+
+	/*
+	 * Test UninstallMultipleProtocols
+	 */
+	ret = boottime->uninstall_multiple_protocol_interfaces(
+						handle2,
+						&guid1, &interface4,
+						&guid2, &interface2,
+						NULL);
+	if (ret != EFI_SUCCESS) {
+		efi_st_todo("UninstallMultipleProtocolInterfaces failed\n");
+		/* This test is known to fail due to missing implementation */
+	}
+	/*
+	 * Check that the protocols are really uninstalled.
+	 */
+	count = buffer_size;
+	ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL,
+					     &count, &buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("LocateHandleBuffer failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (count != 1) {
+		efi_st_todo("UninstallMultipleProtocolInterfaces failed to uninstall protocols\n");
+		/* This test is known to fail due to missing implementation */
+	}
+	ret = find_in_buffer(handle1, count, buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Failed to locate new handle\n");
+		return EFI_ST_FAILURE;
+	}
+	boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+	/*
+	 * Test ProtocolsPerHandle
+	 */
+	ret = boottime->protocols_per_handle(handle1,
+					     &prot_buffer, &prot_count);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Failed to get protocols per handle\n");
+		return EFI_ST_FAILURE;
+	}
+	if (prot_count != 2) {
+		efi_st_error("Failed to get protocols per handle\n");
+		return EFI_ST_FAILURE;
+	}
+	if (efi_st_memcmp(prot_buffer[0], &guid1, 16) &&
+	    efi_st_memcmp(prot_buffer[1], &guid1, 16)) {
+		efi_st_error("Failed to get protocols per handle\n");
+		return EFI_ST_FAILURE;
+	}
+	if (efi_st_memcmp(prot_buffer[0], &guid3, 16) &&
+	    efi_st_memcmp(prot_buffer[1], &guid3, 16)) {
+		efi_st_error("Failed to get protocols per handle\n");
+		return EFI_ST_FAILURE;
+	}
+
+	/*
+	 * Uninstall remaining protocols
+	 */
+	ret = boottime->uninstall_protocol_interface(handle1, &guid1,
+						     &interface1);
+	if (ret != EFI_SUCCESS) {
+		efi_st_todo("UninstallProtocolInterface failed\n");
+		/* This test is known to fail due to missing implementation */
+	}
+	ret = boottime->handle_protocol(handle1, &guid1, (void **)&interface);
+	if (ret == EFI_SUCCESS) {
+		efi_st_todo("UninstallProtocolInterface failed\n");
+		/* This test is known to fail due to missing implementation */
+	}
+	ret = boottime->uninstall_protocol_interface(handle1, &guid3,
+						     &interface1);
+	if (ret != EFI_SUCCESS) {
+		efi_st_todo("UninstallProtocolInterface failed\n");
+		/* This test is known to fail due to missing implementation */
+	}
+
+	return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(protserv) = {
+	.name = "manage protocols",
+	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+	.setup = setup,
+	.execute = execute,
+	.teardown = teardown,
+};

+ 1 - 1
lib/efi_selftest/efi_selftest_snp.c

@@ -260,7 +260,7 @@ static int execute(void)
 {
 	efi_status_t ret;
 	struct efi_event *events[2];
-	size_t index;
+	efi_uintn_t index;
 	union {
 		struct dhcp p;
 		u8 b[PKTSIZE];

+ 53 - 0
lib/efi_selftest/efi_selftest_textoutput.c

@@ -0,0 +1,53 @@
+/*
+ * efi_selftest_textoutput
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
+ *
+ * The following services are tested:
+ * OutputString, TestString, SetAttribute.
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * Execute unit test.
+ *
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+	size_t foreground;
+	size_t background;
+	size_t attrib;
+	efi_status_t ret;
+
+	/* SetAttribute */
+	efi_st_printf("\nColor palette\n");
+	for (foreground = 0; foreground < 0x10; ++foreground) {
+		for (background = 0; background < 0x80; background += 0x10) {
+			attrib = foreground | background;
+			con_out->set_attribute(con_out, attrib);
+			efi_st_printf("%p", (void *)attrib);
+		}
+		con_out->set_attribute(con_out, 0);
+		efi_st_printf("\n");
+	}
+	/* TestString */
+	ret = con_out->test_string(con_out,
+			L" !\"#$%&'()*+,-./0-9:;<=>?@A-Z[\\]^_`a-z{|}~\n");
+	if (ret != EFI_ST_SUCCESS) {
+		efi_st_error("TestString failed for ANSI characters\n");
+		return EFI_ST_FAILURE;
+	}
+	return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(textoutput) = {
+	.name = "text output",
+	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+	.execute = execute,
+};

+ 2 - 2
lib/efi_selftest/efi_selftest_tpl.c

@@ -111,9 +111,9 @@ static int teardown(void)
  */
 static int execute(void)
 {
-	size_t index;
+	efi_uintn_t index;
 	efi_status_t ret;
-	UINTN old_tpl;
+	efi_uintn_t old_tpl;
 
 	/* Set 10 ms timer */
 	notification_count = 0;

+ 10 - 1
lib/efi_selftest/efi_selftest_util.c

@@ -21,5 +21,14 @@ int efi_st_memcmp(const void *buf1, const void *buf2, size_t length)
 		++pos1;
 		++pos2;
 	}
-	return EFI_ST_SUCCESS;
+	return 0;
+}
+
+int efi_st_strcmp_16_8(const u16 *buf1, const char *buf2)
+{
+	for (; *buf1 || *buf2; ++buf1, ++buf2) {
+		if (*buf1 != *buf2)
+			return *buf1 - *buf2;
+	}
+	return 0;
 }

+ 231 - 0
lib/efi_selftest/efi_selftest_watchdog.c

@@ -0,0 +1,231 @@
+/*
+ * efi_selftest_watchdog
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * The 'watchdog timer' unit test checks that the watchdog timer
+ * will not cause a system restart during the timeout period after
+ * a timer reset.
+ *
+ * The 'watchdog reboot' unit test checks that the watchdog timer
+ * actually reboots the system after a timeout. The test is only
+ * executed on explicit request. Use the following commands:
+ *
+ *	setenv efi_selftest watchdog reboot
+ *	bootefi selftest
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * This is the communication structure for the notification function.
+ */
+struct notify_context {
+	/* Status code returned when resetting watchdog */
+	efi_status_t status;
+	/* Number of invocations of the notification function */
+	unsigned int timer_ticks;
+};
+
+static struct efi_event *event_notify;
+static struct efi_event *event_wait;
+static struct efi_boot_services *boottime;
+static struct notify_context notification_context;
+static bool watchdog_reset;
+
+/*
+ * Notification function, increments the notfication count if parameter
+ * context is provided.
+ *
+ * @event	notified event
+ * @context	pointer to the timeout
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+	struct notify_context *notify_context = context;
+	efi_status_t ret = EFI_SUCCESS;
+
+	if (!notify_context)
+		return;
+
+	/* Reset watchdog timer to one second */
+	ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
+	if (ret != EFI_SUCCESS)
+		notify_context->status = ret;
+	/* Count number of calls */
+	notify_context->timer_ticks++;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two timer events.
+ * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
+ *
+ * @handle:	handle of the loaded image
+ * @systable:	system table
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+		 const struct efi_system_table *systable)
+{
+	efi_status_t ret;
+
+	boottime = systable->boottime;
+
+	notification_context.status = EFI_SUCCESS;
+	notification_context.timer_ticks = 0;
+	ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
+				     TPL_CALLBACK, notify,
+				     (void *)&notification_context,
+				     &event_notify);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("could not create event\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
+				     TPL_CALLBACK, notify, NULL, &event_wait);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("could not create event\n");
+		return EFI_ST_FAILURE;
+	}
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute the test resetting the watchdog in a timely manner. No reboot occurs.
+ *
+ * @handle:	handle of the loaded image
+ * @systable:	system table
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int setup_timer(const efi_handle_t handle,
+		       const struct efi_system_table *systable)
+{
+	watchdog_reset = true;
+	return setup(handle, systable);
+}
+
+/*
+ * Execute the test without resetting the watchdog. A system reboot occurs.
+ *
+ * @handle:	handle of the loaded image
+ * @systable:	system table
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int setup_reboot(const efi_handle_t handle,
+			const struct efi_system_table *systable)
+{
+	watchdog_reset = false;
+	return setup(handle, systable);
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the events created in setup.
+ *
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+	efi_status_t ret;
+
+	/* Set the watchdog timer to the five minute default value */
+	ret = boottime->set_watchdog_timer(300, 0, 0, NULL);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Setting watchdog timer failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (event_notify) {
+		ret = boottime->close_event(event_notify);
+		event_notify = NULL;
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("Could not close event\n");
+			return EFI_ST_FAILURE;
+		}
+	}
+	if (event_wait) {
+		ret = boottime->close_event(event_wait);
+		event_wait = NULL;
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("Could not close event\n");
+			return EFI_ST_FAILURE;
+		}
+	}
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Run a 600 ms periodic timer that resets the watchdog to one second
+ * on every timer tick.
+ *
+ * Run a 1350 ms single shot timer and check that the 600ms timer has
+ * been called 2 times.
+ *
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+	size_t index;
+	efi_status_t ret;
+
+	/* Set the watchdog timeout to one second */
+	ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Setting watchdog timer failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (watchdog_reset) {
+		/* Set 600 ms timer */
+		ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC,
+					  6000000);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("Could not set timer\n");
+			return EFI_ST_FAILURE;
+		}
+	}
+	/* Set 1350 ms timer */
+	ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 13500000);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Could not set timer\n");
+		return EFI_ST_FAILURE;
+	}
+
+	ret = boottime->wait_for_event(1, &event_wait, &index);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Could not wait for event\n");
+		return EFI_ST_FAILURE;
+	}
+	if (notification_context.status != EFI_SUCCESS) {
+		efi_st_error("Setting watchdog timer failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (notification_context.timer_ticks != 2) {
+		efi_st_error("The timer was called %u times, expected 2.\n",
+			     notification_context.timer_ticks);
+		return EFI_ST_FAILURE;
+	}
+	return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(watchdog1) = {
+	.name = "watchdog timer",
+	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+	.setup = setup_timer,
+	.execute = execute,
+	.teardown = teardown,
+};
+
+EFI_UNIT_TEST(watchdog2) = {
+	.name = "watchdog reboot",
+	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+	.setup = setup_reboot,
+	.execute = execute,
+	.teardown = teardown,
+	.on_request = true,
+};

+ 3 - 1
test/py/tests/test_efi_loader.py

@@ -12,7 +12,7 @@ import u_boot_utils
 
 """
 Note: This test relies on boardenv_* containing configuration values to define
-which the network environment available for testing. Without this, the parts
+which network environment is available for testing. Without this, the parts
 that rely on network will be automatically skipped.
 
 For example:
@@ -154,6 +154,8 @@ def test_efi_helloworld_net(u_boot_console):
     output = u_boot_console.run_command('bootefi %x' % addr)
     expected_text = 'Hello, world'
     assert expected_text in output
+    expected_text = '## Application terminated, r = 0'
+    assert expected_text in output
 
 @pytest.mark.buildconfigspec('cmd_bootefi_hello')
 def test_efi_helloworld_builtin(u_boot_console):

+ 13 - 1
test/py/tests/test_efi_selftest.py

@@ -1,4 +1,3 @@
-# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
 # Copyright (c) 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
 #
 # SPDX-License-Identifier: GPL-2.0
@@ -14,6 +13,7 @@ def test_efi_selftest(u_boot_console):
 	Run bootefi selftest
 	"""
 
+	u_boot_console.run_command(cmd='setenv efi_selftest')
 	u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
 	m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])
 	if m != 0:
@@ -23,3 +23,15 @@ def test_efi_selftest(u_boot_console):
 	if m != 0:
 		raise Exception('Reset failed during the EFI selftest')
 	u_boot_console.restart_uboot();
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_watchdog_reboot(u_boot_console):
+	u_boot_console.run_command(cmd='setenv efi_selftest list')
+	output = u_boot_console.run_command('bootefi selftest')
+	assert '\'watchdog reboot\'' in output
+	u_boot_console.run_command(cmd='setenv efi_selftest watchdog reboot')
+	u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
+	m = u_boot_console.p.expect(['resetting', 'U-Boot'])
+	if m != 0:
+		raise Exception('Reset failed in \'watchdog reboot\' test')
+	u_boot_console.restart_uboot();

部分文件因文件數量過多而無法顯示