Bläddra i källkod

Merge tag 'efi-2019-07-rc4' of git://git.denx.de/u-boot-efi

Pull request for UEFI sub-system for v2019.07-rc4

Corrections for boottime services for protocols and for the SetTime()
service are provided.

Error messages for the 'setenv -e' and 'bootefi bootmgr' commands are
added.
Tom Rini 5 år sedan
förälder
incheckning
38c2a8a001

+ 9 - 1
cmd/nvedit_efi.c

@@ -373,6 +373,8 @@ int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 
 		for ( ; argc > 0; argc--, argv++)
 			if (append_value(&value, &size, argv[0]) < 0) {
+				printf("## Failed to process an argument, %s\n",
+				       argv[0]);
 				ret = CMD_RET_FAILURE;
 				goto out;
 			}
@@ -381,6 +383,7 @@ int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	len = utf8_utf16_strnlen(var_name, strlen(var_name));
 	var_name16 = malloc((len + 1) * 2);
 	if (!var_name16) {
+		printf("## Out of memory\n");
 		ret = CMD_RET_FAILURE;
 		goto out;
 	}
@@ -392,7 +395,12 @@ int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 					EFI_VARIABLE_BOOTSERVICE_ACCESS |
 					EFI_VARIABLE_RUNTIME_ACCESS,
 					size, value));
-	ret = (ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE);
+	if (ret == EFI_SUCCESS) {
+		ret = CMD_RET_SUCCESS;
+	} else {
+		printf("## Failed to set EFI variable\n");
+		ret = CMD_RET_FAILURE;
+	}
 out:
 	free(value);
 	free(var_name16);

+ 1 - 1
drivers/rtc/rtc-lib.c

@@ -23,7 +23,7 @@ static const unsigned char rtc_days_in_month[] = {
 /*
  * The number of days in the month.
  */
-static int rtc_month_days(unsigned int month, unsigned int year)
+int rtc_month_days(unsigned int month, unsigned int year)
 {
 	return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
 }

+ 24 - 4
include/efi_loader.h

@@ -286,20 +286,38 @@ extern struct list_head efi_obj_list;
 /* List of all events */
 extern struct list_head efi_events;
 
+/**
+ * struct efi_protocol_notification - handle for notified protocol
+ *
+ * When a protocol interface is installed for which an event was registered with
+ * the RegisterProtocolNotify() service this structure is used to hold the
+ * handle on which the protocol interface was installed.
+ *
+ * @link:	link to list of all handles notified for this event
+ * @handle:	handle on which the notified protocol interface was installed
+ */
+struct efi_protocol_notification {
+	struct list_head link;
+	efi_handle_t handle;
+};
+
 /**
  * efi_register_notify_event - event registered by RegisterProtocolNotify()
  *
  * The address of this structure serves as registration value.
  *
- * @link:		link to list of all registered events
- * @event:		registered event. The same event may registered for
- *			multiple GUIDs.
- * @protocol:		protocol for which the event is registered
+ * @link:	link to list of all registered events
+ * @event:	registered event. The same event may registered for multiple
+ *		GUIDs.
+ * @protocol:	protocol for which the event is registered
+ * @handles:	linked list of all handles on which the notified protocol was
+ *		installed
  */
 struct efi_register_notify_event {
 	struct list_head link;
 	struct efi_event *event;
 	efi_guid_t protocol;
+	struct list_head handles;
 };
 
 /* List of all events registered by RegisterProtocolNotify() */
@@ -576,6 +594,8 @@ efi_status_t __efi_runtime EFIAPI efi_get_time(
 			struct efi_time *time,
 			struct efi_time_cap *capabilities);
 
+efi_status_t __efi_runtime EFIAPI efi_set_time(struct efi_time *time);
+
 #ifdef CONFIG_CMD_BOOTEFI_SELFTEST
 /*
  * Entry point for the tests of the EFI API.

+ 8 - 0
include/rtc.h

@@ -258,4 +258,12 @@ void rtc_to_tm(u64 time_t, struct rtc_time *time);
  */
 unsigned long rtc_mktime(const struct rtc_time *time);
 
+/**
+ * rtc_month_days() - The number of days in the month
+ *
+ * @month:	month (January = 0)
+ * @year:	year (4 digits)
+ */
+int rtc_month_days(unsigned int month, unsigned int year);
+
 #endif	/* _RTC_H_ */

+ 16 - 0
lib/efi_loader/Kconfig

@@ -18,6 +18,22 @@ config EFI_LOADER
 
 if EFI_LOADER
 
+config EFI_GET_TIME
+	bool "GetTime() runtime service"
+	depends on DM_RTC
+	default y
+	help
+	  Provide the GetTime() runtime service at boottime. This service
+	  can be used by an EFI application to read the real time clock.
+
+config EFI_SET_TIME
+	bool "SetTime() runtime service"
+	depends on EFI_GET_TIME
+	default n
+	help
+	  Provide the SetTime() runtime service at boottime. This service
+	  can be used by an EFI application to adjust the real time clock.
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y

+ 5 - 1
lib/efi_loader/efi_bootmgr.c

@@ -149,8 +149,11 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle)
 
 		ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
 					      NULL, 0, handle));
-		if (ret != EFI_SUCCESS)
+		if (ret != EFI_SUCCESS) {
+			printf("Loading from Boot%04X '%ls' failed\n", n,
+			       lo.label);
 			goto error;
+		}
 
 		attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
 			     EFI_VARIABLE_RUNTIME_ACCESS;
@@ -215,6 +218,7 @@ efi_status_t efi_bootmgr_load(efi_handle_t *handle)
 				ret = try_load_entry(bootnext, handle);
 				if (ret == EFI_SUCCESS)
 					return ret;
+				printf("Loading from BootNext failed, falling back to BootOrder\n");
 			}
 		} else {
 			printf("Deleting BootNext failed\n");

+ 138 - 50
lib/efi_loader/efi_boottime.c

@@ -921,6 +921,14 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
 	list_for_each_entry_safe(item, next, &efi_register_notify_events,
 				 link) {
 		if (event == item->event) {
+			struct efi_protocol_notification *hitem, *hnext;
+
+			/* Remove signaled handles */
+			list_for_each_entry_safe(hitem, hnext, &item->handles,
+						 link) {
+				list_del(&hitem->link);
+				free(hitem);
+			}
 			list_del(&item->link);
 			free(item);
 		}
@@ -1049,8 +1057,19 @@ efi_status_t efi_add_protocol(const efi_handle_t handle,
 
 	/* Notify registered events */
 	list_for_each_entry(event, &efi_register_notify_events, link) {
-		if (!guidcmp(protocol, &event->protocol))
+		if (!guidcmp(protocol, &event->protocol)) {
+			struct efi_protocol_notification *notif;
+
+			notif = calloc(1, sizeof(*notif));
+			if (!notif) {
+				list_del(&handler->link);
+				free(handler);
+				return EFI_OUT_OF_RESOURCES;
+			}
+			notif->handle = handle;
+			list_add_tail(&notif->link, &event->handles);
 			efi_signal_event(event->event, true);
+		}
 	}
 
 	if (!guidcmp(&efi_guid_device_path, protocol))
@@ -1241,10 +1260,6 @@ static efi_status_t efi_uninstall_protocol
 		goto out;
 	/* Disconnect controllers */
 	efi_disconnect_all_drivers(efiobj, protocol, NULL);
-	if (!list_empty(&handler->open_infos)) {
-		r =  EFI_ACCESS_DENIED;
-		goto out;
-	}
 	/* Close protocol */
 	list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
 		if (item->info.attributes ==
@@ -1332,6 +1347,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify(
 
 	item->event = event;
 	memcpy(&item->protocol, protocol, sizeof(efi_guid_t));
+	INIT_LIST_HEAD(&item->handles);
 
 	list_add_tail(&item->link, &efi_register_notify_events);
 
@@ -1359,7 +1375,6 @@ static int efi_search(enum efi_locate_search_type search_type,
 	switch (search_type) {
 	case ALL_HANDLES:
 		return 0;
-	case BY_REGISTER_NOTIFY:
 	case BY_PROTOCOL:
 		ret = efi_search_protocol(handle, protocol, NULL);
 		return (ret != EFI_SUCCESS);
@@ -1369,6 +1384,27 @@ static int efi_search(enum efi_locate_search_type search_type,
 	}
 }
 
+/**
+ * efi_check_register_notify_event() - check if registration key is valid
+ *
+ * Check that a pointer is a valid registration key as returned by
+ * RegisterProtocolNotify().
+ *
+ * @key:	registration key
+ * Return:	valid registration key or NULL
+ */
+static struct efi_register_notify_event *efi_check_register_notify_event
+								(void *key)
+{
+	struct efi_register_notify_event *event;
+
+	list_for_each_entry(event, &efi_register_notify_events, link) {
+		if (event == (struct efi_register_notify_event *)key)
+			return event;
+	}
+	return NULL;
+}
+
 /**
  * efi_locate_handle() - locate handles implementing a protocol
  *
@@ -1390,7 +1426,8 @@ static efi_status_t efi_locate_handle(
 {
 	struct efi_object *efiobj;
 	efi_uintn_t size = 0;
-	struct efi_register_notify_event *item, *event = NULL;
+	struct efi_register_notify_event *event;
+	struct efi_protocol_notification *handle = NULL;
 
 	/* Check parameters */
 	switch (search_type) {
@@ -1400,17 +1437,9 @@ static efi_status_t efi_locate_handle(
 		if (!search_key)
 			return EFI_INVALID_PARAMETER;
 		/* Check that the registration key is valid */
-		list_for_each_entry(item, &efi_register_notify_events, link) {
-			if (item ==
-			    (struct efi_register_notify_event *)search_key) {
-				event = item;
-				break;
-			}
-		}
+		event = efi_check_register_notify_event(search_key);
 		if (!event)
 			return EFI_INVALID_PARAMETER;
-
-		protocol = &event->protocol;
 		break;
 	case BY_PROTOCOL:
 		if (!protocol)
@@ -1421,14 +1450,23 @@ static efi_status_t efi_locate_handle(
 	}
 
 	/* Count how much space we need */
-	list_for_each_entry(efiobj, &efi_obj_list, link) {
-		if (!efi_search(search_type, protocol, efiobj))
-			size += sizeof(void *);
+	if (search_type == BY_REGISTER_NOTIFY) {
+		if (list_empty(&event->handles))
+			return EFI_NOT_FOUND;
+		handle = list_first_entry(&event->handles,
+					  struct efi_protocol_notification,
+					  link);
+		efiobj = handle->handle;
+		size += sizeof(void *);
+	} else {
+		list_for_each_entry(efiobj, &efi_obj_list, link) {
+			if (!efi_search(search_type, protocol, efiobj))
+				size += sizeof(void *);
+		}
+		if (size == 0)
+			return EFI_NOT_FOUND;
 	}
 
-	if (size == 0)
-		return EFI_NOT_FOUND;
-
 	if (!buffer_size)
 		return EFI_INVALID_PARAMETER;
 
@@ -1444,9 +1482,14 @@ static efi_status_t efi_locate_handle(
 		return EFI_INVALID_PARAMETER;
 
 	/* Then fill the array */
-	list_for_each_entry(efiobj, &efi_obj_list, link) {
-		if (!efi_search(search_type, protocol, efiobj))
-			*buffer++ = efiobj;
+	if (search_type == BY_REGISTER_NOTIFY) {
+		*buffer = efiobj;
+		list_del(&handle->link);
+	} else {
+		list_for_each_entry(efiobj, &efi_obj_list, link) {
+			if (!efi_search(search_type, protocol, efiobj))
+				*buffer++ = efiobj;
+		}
 	}
 
 	return EFI_SUCCESS;
@@ -2013,7 +2056,6 @@ static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
 		    item->info.controller_handle == controller_handle) {
 			efi_delete_open_info(item);
 			r = EFI_SUCCESS;
-			break;
 		}
 	}
 out:
@@ -2212,29 +2254,58 @@ static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol,
 					       void *registration,
 					       void **protocol_interface)
 {
-	struct list_head *lhandle;
+	struct efi_handler *handler;
 	efi_status_t ret;
+	struct efi_object *efiobj;
 
 	EFI_ENTRY("%pUl, %p, %p", protocol, registration, protocol_interface);
 
+	/*
+	 * The UEFI spec explicitly requires a protocol even if a registration
+	 * key is provided. This differs from the logic in LocateHandle().
+	 */
 	if (!protocol || !protocol_interface)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 
-	list_for_each(lhandle, &efi_obj_list) {
-		struct efi_object *efiobj;
-		struct efi_handler *handler;
-
-		efiobj = list_entry(lhandle, struct efi_object, link);
+	if (registration) {
+		struct efi_register_notify_event *event;
+		struct efi_protocol_notification *handle;
 
+		event = efi_check_register_notify_event(registration);
+		if (!event)
+			return EFI_EXIT(EFI_INVALID_PARAMETER);
+		/*
+		 * The UEFI spec requires to return EFI_NOT_FOUND if no
+		 * protocol instance matches protocol and registration.
+		 * So let's do the same for a mismatch between protocol and
+		 * registration.
+		 */
+		if (guidcmp(&event->protocol, protocol))
+			goto not_found;
+		if (list_empty(&event->handles))
+			goto not_found;
+		handle = list_first_entry(&event->handles,
+					  struct efi_protocol_notification,
+					  link);
+		efiobj = handle->handle;
+		list_del(&handle->link);
+		free(handle);
 		ret = efi_search_protocol(efiobj, protocol, &handler);
-		if (ret == EFI_SUCCESS) {
-			*protocol_interface = handler->protocol_interface;
-			return EFI_EXIT(EFI_SUCCESS);
+		if (ret == EFI_SUCCESS)
+			goto found;
+	} else {
+		list_for_each_entry(efiobj, &efi_obj_list, link) {
+			ret = efi_search_protocol(efiobj, protocol, &handler);
+			if (ret == EFI_SUCCESS)
+				goto found;
 		}
 	}
+not_found:
 	*protocol_interface = NULL;
-
 	return EFI_EXIT(EFI_NOT_FOUND);
+found:
+	*protocol_interface = handler->protocol_interface;
+	return EFI_EXIT(EFI_SUCCESS);
 }
 
 /**
@@ -2561,34 +2632,50 @@ static efi_status_t efi_protocol_open(
 			if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) &&
 			    (item->info.attributes == attributes))
 				return EFI_ALREADY_STARTED;
+		} else {
+			if (item->info.attributes &
+			    EFI_OPEN_PROTOCOL_BY_DRIVER)
+				opened_by_driver = true;
 		}
 		if (item->info.attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE)
 			opened_exclusive = true;
 	}
 
 	/* Only one controller can open the protocol exclusively */
-	if (opened_exclusive && attributes &
-	    (EFI_OPEN_PROTOCOL_EXCLUSIVE | EFI_OPEN_PROTOCOL_BY_DRIVER))
-		return EFI_ACCESS_DENIED;
+	if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
+		if (opened_exclusive)
+			return EFI_ACCESS_DENIED;
+	} else if (attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) {
+		if (opened_exclusive || opened_by_driver)
+			return EFI_ACCESS_DENIED;
+	}
 
 	/* Prepare exclusive opening */
 	if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
 		/* Try to disconnect controllers */
+disconnect_next:
+		opened_by_driver = false;
 		list_for_each_entry(item, &handler->open_infos, link) {
+			efi_status_t ret;
+
 			if (item->info.attributes ==
-					EFI_OPEN_PROTOCOL_BY_DRIVER)
-				EFI_CALL(efi_disconnect_controller(
+					EFI_OPEN_PROTOCOL_BY_DRIVER) {
+				ret = EFI_CALL(efi_disconnect_controller(
 						item->info.controller_handle,
 						item->info.agent_handle,
 						NULL));
+				if (ret == EFI_SUCCESS)
+					/*
+					 * Child controllers may have been
+					 * removed from the open_infos list. So
+					 * let's restart the loop.
+					 */
+					goto disconnect_next;
+				else
+					opened_by_driver = true;
+			}
 		}
-		opened_by_driver = false;
-		/* Check if all controllers are disconnected */
-		list_for_each_entry(item, &handler->open_infos, link) {
-			if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER)
-				opened_by_driver = true;
-		}
-		/* Only one controller can be connected */
+		/* Only one driver can be connected */
 		if (opened_by_driver)
 			return EFI_ACCESS_DENIED;
 	}
@@ -2596,7 +2683,8 @@ static efi_status_t efi_protocol_open(
 	/* Find existing entry */
 	list_for_each_entry(item, &handler->open_infos, link) {
 		if (item->info.agent_handle == agent_handle &&
-		    item->info.controller_handle == controller_handle)
+		    item->info.controller_handle == controller_handle &&
+		    item->info.attributes == attributes)
 			match = &item->info;
 	}
 	/* None found, create one */
@@ -2985,7 +3073,7 @@ static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
 					       const efi_guid_t *protocol,
 					       void **protocol_interface)
 {
-	return efi_open_protocol(handle, protocol, protocol_interface, NULL,
+	return efi_open_protocol(handle, protocol, protocol_interface, efi_root,
 				 NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
 }
 

+ 32 - 7
lib/efi_loader/efi_runtime.c

@@ -167,7 +167,7 @@ static efi_status_t EFIAPI efi_get_time_boottime(
 			struct efi_time *time,
 			struct efi_time_cap *capabilities)
 {
-#ifdef CONFIG_DM_RTC
+#ifdef CONFIG_EFI_GET_TIME
 	efi_status_t ret = EFI_SUCCESS;
 	struct rtc_time tm;
 	struct udevice *dev;
@@ -195,9 +195,9 @@ static efi_status_t EFIAPI efi_get_time_boottime(
 	time->hour = tm.tm_hour;
 	time->minute = tm.tm_min;
 	time->second = tm.tm_sec;
-	time->daylight = EFI_TIME_ADJUST_DAYLIGHT;
-	if (tm.tm_isdst > 0)
-		time->daylight |= EFI_TIME_IN_DAYLIGHT;
+	if (tm.tm_isdst)
+		time->daylight =
+			EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT;
 	time->timezone = EFI_UNSPECIFIED_TIMEZONE;
 
 	if (capabilities) {
@@ -214,6 +214,30 @@ out:
 #endif
 }
 
+#ifdef CONFIG_EFI_SET_TIME
+
+/**
+ * efi_validate_time() - checks if timestamp is valid
+ *
+ * @time:	timestamp to validate
+ * Returns:	0 if timestamp is valid, 1 otherwise
+ */
+static int efi_validate_time(struct efi_time *time)
+{
+	return (!time ||
+		time->year < 1900 || time->year > 9999 ||
+		!time->month || time->month > 12 || !time->day ||
+		time->day > rtc_month_days(time->month - 1, time->year) ||
+		time->hour > 23 || time->minute > 59 || time->second > 59 ||
+		time->nanosecond > 999999999 ||
+		time->daylight &
+		~(EFI_TIME_IN_DAYLIGHT | EFI_TIME_ADJUST_DAYLIGHT) ||
+		((time->timezone < -1440 || time->timezone > 1440) &&
+		time->timezone != EFI_UNSPECIFIED_TIMEZONE));
+}
+
+#endif
+
 /**
  * efi_set_time_boottime() - set current time
  *
@@ -228,14 +252,14 @@ out:
  */
 static efi_status_t EFIAPI efi_set_time_boottime(struct efi_time *time)
 {
-#ifdef CONFIG_DM_RTC
+#ifdef CONFIG_EFI_SET_TIME
 	efi_status_t ret = EFI_SUCCESS;
 	struct rtc_time tm;
 	struct udevice *dev;
 
 	EFI_ENTRY("%p", time);
 
-	if (!time) {
+	if (efi_validate_time(time)) {
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
@@ -252,7 +276,8 @@ static efi_status_t EFIAPI efi_set_time_boottime(struct efi_time *time)
 	tm.tm_hour = time->hour;
 	tm.tm_min = time->minute;
 	tm.tm_sec = time->second;
-	tm.tm_isdst = time->daylight == EFI_TIME_IN_DAYLIGHT;
+	tm.tm_isdst = time->daylight ==
+		      (EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT);
 	/* Calculate day of week */
 	rtc_calc_weekday(&tm);
 

+ 2 - 1
lib/efi_selftest/Makefile

@@ -26,8 +26,8 @@ efi_selftest_gop.o \
 efi_selftest_loaded_image.o \
 efi_selftest_manageprotocols.o \
 efi_selftest_memory.o \
+efi_selftest_open_protocol.o \
 efi_selftest_register_notify.o \
-efi_selftest_rtc.o \
 efi_selftest_snp.o \
 efi_selftest_textinput.o \
 efi_selftest_textinputex.o \
@@ -43,6 +43,7 @@ efi_selftest_unicode_collation.o
 
 obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o
 obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o
+obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o
 
 ifeq ($(CONFIG_GENERATE_ACPI_TABLE),)
 obj-y += efi_selftest_fdt.o

+ 205 - 0
lib/efi_selftest/efi_selftest_open_protocol.c

@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_open_protocol
+ *
+ * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks that open protocol information is correctly updated
+ * when calling:
+ * HandleProtocol, OpenProtocol, OpenProtocolInformation, CloseProtocol.
+ */
+
+#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(0x492a0e38, 0x1442, 0xf819,
+		 0x14, 0xaa, 0x4b, 0x8d, 0x09, 0xfe, 0x5a, 0xb9);
+static efi_handle_t handle1;
+static struct interface interface1;
+
+/*
+ * Setup unit test.
+ *
+ * Create a handle and install a protocol interface on it.
+ *
+ * @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;
+
+	boottime = systable->boottime;
+
+	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 (!handle1) {
+		efi_st_error
+			("InstallProtocolInterface failed to create handle\n");
+		return EFI_ST_FAILURE;
+	}
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ */
+static int teardown(void)
+{
+	efi_status_t ret;
+
+	if (handle1) {
+		ret = boottime->uninstall_protocol_interface(handle1, &guid1,
+							     &interface1);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("UninstallProtocolInterface failed\n");
+			return EFI_ST_FAILURE;
+		}
+	}
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Open the installed protocol twice via HandleProtocol() and once via
+ * OpenProtocol(EFI_OPEN_PROTOCOL_GET_PROTOCOL). Read the open protocol
+ * information and check the open counts. Finally close the protocol and
+ * check again.
+ */
+static int execute(void)
+{
+	void *interface;
+	struct efi_open_protocol_info_entry *entry_buffer;
+	efi_uintn_t entry_count;
+	efi_handle_t firmware_handle;
+	efi_status_t ret;
+
+	ret = boottime->handle_protocol(handle1, &guid1, &interface);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("HandleProtocol failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (interface != &interface1) {
+		efi_st_error("HandleProtocol returned wrong interface\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->open_protocol_information(handle1, &guid1,
+						  &entry_buffer, &entry_count);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("OpenProtocolInformation failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (entry_count != 1) {
+		efi_st_error("Incorrect OpenProtocolInformation count\n");
+		efi_st_printf("Expected 1, got %u\n",
+			      (unsigned int)entry_count);
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->free_pool(entry_buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->handle_protocol(handle1, &guid1, &interface);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("HandleProtocol failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->open_protocol_information(handle1, &guid1,
+						  &entry_buffer, &entry_count);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("OpenProtocolInformation failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (entry_count != 1) {
+		efi_st_error("Incorrect OpenProtocolInformation count\n");
+		efi_st_printf("Expected 1, got %u\n",
+			      (unsigned int)entry_count);
+		return EFI_ST_FAILURE;
+	}
+	if (entry_buffer[0].open_count != 2) {
+		efi_st_error("Incorrect open count: expected 2 got %u\n",
+			     entry_buffer[0].open_count);
+		return EFI_ST_FAILURE;
+	}
+	firmware_handle = entry_buffer[0].agent_handle;
+	ret = boottime->free_pool(entry_buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->open_protocol(handle1, &guid1, &interface,
+				      firmware_handle, NULL,
+				      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("OpenProtocol failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->open_protocol_information(handle1, &guid1,
+						  &entry_buffer, &entry_count);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("OpenProtocolInformation failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (entry_count != 2) {
+		efi_st_error("Incorrect OpenProtocolInformation count\n");
+		efi_st_printf("Expected 2, got %u\n",
+			      (unsigned int)entry_count);
+		return EFI_ST_FAILURE;
+	}
+	if (entry_buffer[0].open_count + entry_buffer[1].open_count != 3) {
+		efi_st_error("Incorrect open count: expected 3 got %u\n",
+			     entry_buffer[0].open_count +
+			     entry_buffer[1].open_count);
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->free_pool(entry_buffer);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->close_protocol(handle1, &guid1, firmware_handle, NULL);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("CloseProtocol failed\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->open_protocol_information(handle1, &guid1,
+						  &entry_buffer, &entry_count);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("OpenProtocolInformation failed\n");
+		return EFI_ST_FAILURE;
+	}
+	if (entry_count) {
+		efi_st_error("Incorrect OpenProtocolInformation count\n");
+		efi_st_printf("Expected 0, got %u\n",
+			      (unsigned int)entry_count);
+		return EFI_ST_FAILURE;
+	}
+
+	return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(openprot) = {
+	.name = "open protocol",
+	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+	.setup = setup,
+	.execute = execute,
+	.teardown = teardown,
+};

+ 13 - 8
lib/efi_selftest/efi_selftest_register_notify.c

@@ -47,15 +47,20 @@ static void EFIAPI notify(struct efi_event *event, void *context)
 {
 	struct context *cp = context;
 	efi_status_t ret;
+	efi_uintn_t handle_count;
+	efi_handle_t *handles;
 
 	cp->notify_count++;
 
-	ret = boottime->locate_handle_buffer(BY_REGISTER_NOTIFY, NULL,
-					     cp->registration_key,
-					     &cp->handle_count,
-					     &cp->handles);
-	if (ret != EFI_SUCCESS)
-		cp->handle_count = 0;
+	for (;;) {
+		ret = boottime->locate_handle_buffer(BY_REGISTER_NOTIFY, NULL,
+						     cp->registration_key,
+						     &handle_count, &handles);
+		if (ret != EFI_SUCCESS)
+			break;
+		cp->handle_count += handle_count;
+		cp->handles = handles;
+	}
 }
 
 /*
@@ -170,7 +175,7 @@ static int execute(void)
 		efi_st_error("reinstall was notified too often\n");
 		return EFI_ST_FAILURE;
 	}
-	if (context.handle_count != 1) {
+	if (context.handle_count != 2) {
 		efi_st_error("LocateHandle failed\n");
 		return EFI_ST_FAILURE;
 	}
@@ -195,7 +200,7 @@ static int execute(void)
 		efi_st_error("install was notified too often\n");
 		return EFI_ST_FAILURE;
 	}
-	if (context.handle_count != 2) {
+	if (context.handle_count != 3) {
 		efi_st_error("LocateHandle failed\n");
 		return EFI_ST_FAILURE;
 	}

+ 6 - 11
lib/efi_selftest/efi_selftest_rtc.c

@@ -40,7 +40,9 @@ static int setup(const efi_handle_t handle,
 static int execute(void)
 {
 	efi_status_t ret;
-	struct efi_time tm, tm_old, tm_new = {
+	struct efi_time tm_old;
+#ifdef CONFIG_EFI_SET_TIME
+	struct efi_time tm, tm_new = {
 		.year = 2017,
 		.month = 5,
 		.day = 19,
@@ -48,31 +50,23 @@ static int execute(void)
 		.minute = 47,
 		.second = 53,
 	};
+#endif
 
 	/* Display current time */
 	ret = runtime->get_time(&tm_old, NULL);
 	if (ret != EFI_SUCCESS) {
-#ifdef CONFIG_CMD_DATE
 		efi_st_error(EFI_ST_NO_RTC);
 		return EFI_ST_FAILURE;
-#else
-		efi_st_todo(EFI_ST_NO_RTC);
-		return EFI_ST_SUCCESS;
-#endif
 	}
 	efi_st_printf("Time according to real time clock: "
 		      "%.4u-%.2u-%.2u %.2u:%.2u:%.2u\n",
 		      tm_old.year, tm_old.month, tm_old.day,
 		      tm_old.hour, tm_old.minute, tm_old.second);
+#ifdef CONFIG_EFI_SET_TIME
 	ret = runtime->set_time(&tm_new);
 	if (ret != EFI_SUCCESS) {
-#ifdef CONFIG_CMD_DATE
 		efi_st_error(EFI_ST_NO_RTC_SET);
 		return EFI_ST_FAILURE;
-#else
-		efi_st_todo(EFI_ST_NO_RTC_SET);
-		return EFI_ST_SUCCESS;
-#endif
 	}
 	ret = runtime->get_time(&tm, NULL);
 	if (ret != EFI_SUCCESS) {
@@ -95,6 +89,7 @@ static int execute(void)
 		efi_st_error(EFI_ST_NO_RTC_SET);
 		return EFI_ST_FAILURE;
 	}
+#endif
 
 	return EFI_ST_SUCCESS;
 }