From f09cea36ca086c7b5df372bdf38168c2ce5609ca Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 21 May 2019 18:19:01 +0200 Subject: [PATCH 01/17] efi_loader: correct notification of protocol installation When a protocol is installed the handle should be queued for the registration key of each registered event. LocateHandle() should return the first handle from the queue for the registration key and delete it from the queue. Implement the queueing. Correct the selftest. With the patch the UEFI SCT tests for LocateHandle() are passed without failure. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 26 +++++++-- lib/efi_loader/efi_boottime.c | 58 ++++++++++++++----- .../efi_selftest_register_notify.c | 21 ++++--- 3 files changed, 80 insertions(+), 25 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 43d3a08428..77b2f60bdc 100644 --- a/include/efi_loader.h +++ b/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() */ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 54fff85e64..1ccf54c386 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/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(¬if->link, &event->handles); efi_signal_event(event->event, true); + } } if (!guidcmp(&efi_guid_device_path, protocol)) @@ -1332,6 +1351,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 +1379,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); @@ -1391,6 +1410,7 @@ 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_protocol_notification *handle = NULL; /* Check parameters */ switch (search_type) { @@ -1409,8 +1429,6 @@ static efi_status_t efi_locate_handle( } if (!event) return EFI_INVALID_PARAMETER; - - protocol = &event->protocol; break; case BY_PROTOCOL: if (!protocol) @@ -1421,14 +1439,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 +1471,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; diff --git a/lib/efi_selftest/efi_selftest_register_notify.c b/lib/efi_selftest/efi_selftest_register_notify.c index ee0ef395de..ad763dd6cb 100644 --- a/lib/efi_selftest/efi_selftest_register_notify.c +++ b/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; } From 5b35093834a3019961f3f533a8ee63147a2cb9ed Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 28 May 2019 09:00:35 +0900 Subject: [PATCH 02/17] cmd: env: print a message when setting UEFI variable failed Error message will alert a user that setting/deleting a variable failed. Signed-off-by: AKASHI Takahiro Reviewed-by: Heinrich Schuchardt --- cmd/nvedit_efi.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c index 2805e8182b..ff8eaa1aad 100644 --- a/cmd/nvedit_efi.c +++ b/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); From 8eee1d3ec6776d84e8b45d346e73734456518017 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Fri, 24 May 2019 15:59:31 +0900 Subject: [PATCH 03/17] efi_loader: bootmgr: print a message when loading from BootNext failed If a user defines BootNext but not BootOrder and loading from BootNext fails, you will see only a message like this: BootOrder not defined This may confuse a user. Adding an error message will be helpful. An example output looks like this: => efidebug boot add 0001 label1 scsi 0:1 "\path1\file1.efi" "--option foo" => efidebug boot add 0002 label2 scsi 0:1 "\path2\file2.efi" "--option bar" => efidebug boot add 0003 label3 scsi 0:1 "\path3\file3.efi" "--option no" => efidebug boot order 0001 0002 => efidebug boot next 0003 => bootefi bootmgr Loading from Boot0003 'label3' failed Loading from BootNext failed, falling back to BootOrder Loading from Boot0001 'label1' failed Loading from Boot0002 'label2' failed EFI boot manager: Cannot load any image Signed-off-by: AKASHI Takahiro Adjust messages. Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_bootmgr.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 7bf51874c1..43791422c8 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/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"); From b8abd743ff1669e07e4e022e5793720d875d8e13 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 29 May 2019 07:46:33 +0200 Subject: [PATCH 04/17] efi_loader: factor out efi_check_register_notify_event() The code to check if a registration key is a valid key returned by RegisterProtocolNotify() can be reused. So let us factor it out into a new function efi_check_register_notify_event(). Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1ccf54c386..dd5f706af7 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1388,6 +1388,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 * @@ -1409,7 +1430,7 @@ 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 */ @@ -1420,13 +1441,7 @@ 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; break; From e31b3b16226c6f6847238e1f7006724abf1220df Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 29 May 2019 18:06:46 +0200 Subject: [PATCH 05/17] efi_loader: registration key in LocateProtocol() In LocateProtocol() implement searching by the registration key returned by RegisterNotifyProtocol(). Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 49 ++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index dd5f706af7..df57b3a984 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2259,29 +2259,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); } /** From 399a39e34af00e7342406405dc0300da0557277b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 30 May 2019 14:16:31 +0200 Subject: [PATCH 06/17] efi_loader: correct OpenProtocol() If a protocol is opened BY_DRIVER it cannot be opened by another agent BY_DRIVER. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index df57b3a984..610eea463e 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2637,15 +2637,23 @@ 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) { From dae7ce451c5eaaae98014a4062950cd646f78c2d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 30 May 2019 14:16:31 +0200 Subject: [PATCH 07/17] efi_loader: avoid crash in OpenProtocol() When trying to open a protocol exclusively attached drivers have to be removed. This removes entries in the open protocol information linked list over which we are looping. As additionally child controllers may have been removed the only safe thing to do is to restart the loop over the linked list when a driver is removed. By observing the return code of DisconnectController() we can eliminate a loop. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 610eea463e..f71268440b 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2658,21 +2658,29 @@ static efi_status_t efi_protocol_open( /* 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; } From a248bc805536337317a5cb9777f420c38724dab4 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 30 May 2019 20:12:19 +0200 Subject: [PATCH 08/17] efi_loader: correct UninstallProtocolInterface() When uninstalling a protocol the following steps are needed: * request all drivers to disconnect * close protocol for all non-drivers * check if any open instance of the protocol exists on the handle and return EFI_ACCESS_DENIED in this case * remove the protocol interface By tort we tested for remaining open protocol instances already after requesting drivers to disconnect. With this correction the UEFI SCT II tests for UninstallProtocolInterface() and ReinstallProtocolInterface are passed. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index f71268440b..f5b5828f93 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1260,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 == From 3c1889e6399a0455bb5056f614be046a0b9cf053 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 31 May 2019 07:21:03 +0200 Subject: [PATCH 09/17] rtc: export rtc_month_days() Export function rtc_month_days() for reuse in the UEFI subsystem. Signed-off-by: Heinrich Schuchardt --- drivers/rtc/rtc-lib.c | 2 +- include/rtc.h | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index 6528ddfebb..1f7bdade29 100644 --- a/drivers/rtc/rtc-lib.c +++ b/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); } diff --git a/include/rtc.h b/include/rtc.h index 2c3a5743e3..b255bdc7a3 100644 --- a/include/rtc.h +++ b/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_ */ From e6bcc354523665eace01104d1751c0a9a3039830 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 31 May 2019 07:35:19 +0200 Subject: [PATCH 10/17] efi_loader: check time in SetTime() The UEFI spec prescribes that we check that the timestamp passed to SetTime() is checked for validity. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_runtime.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 058b40a887..2082d1304f 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -214,6 +214,30 @@ out: #endif } +#ifdef CONFIG_DM_RTC + +/** + * 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 * @@ -235,7 +259,7 @@ static efi_status_t EFIAPI efi_set_time_boottime(struct efi_time *time) EFI_ENTRY("%p", time); - if (!time) { + if (efi_validate_time(time)) { ret = EFI_INVALID_PARAMETER; goto out; } From 656f17106b870ed42ff3e00fc6c5e41e779dd934 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 31 May 2019 07:38:29 +0200 Subject: [PATCH 11/17] efi_loader: export efi_set_time() To let a board implement the runtime version of SetTime() we have to provide the definition of the weak function in an include. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/efi_loader.h b/include/efi_loader.h index 77b2f60bdc..23ce732267 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -594,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. From 38b9a79c6366a0e4c98b0a6f33f268fc0d3ba4ce Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 31 May 2019 22:08:45 +0200 Subject: [PATCH 12/17] efi_loader: handling of daylight saving time If SetTime() is meant to set daylight saving time it will be called with Time.Daylight == EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT. Return 0 from GetTime() if time is not in daylight because we cannot determine if we are in a time zone with daylight saving time. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_runtime.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 2082d1304f..98ab4de03d 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -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) { @@ -276,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); From 5ec48e38eefa5fbc2cab0c34b2b48df67c0fa23e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 31 May 2019 22:56:02 +0200 Subject: [PATCH 13/17] efi_loader: Kconfig entries for GetTime(), SetTime() The GetTime() and the SetTime() runtime services are not obligatory. So let's make them customizable. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/Kconfig | 16 ++++++++++++++++ lib/efi_loader/efi_runtime.c | 6 +++--- lib/efi_selftest/Makefile | 2 +- lib/efi_selftest/efi_selftest_rtc.c | 17 ++++++----------- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index fc04ea39d0..cd5436c576 100644 --- a/lib/efi_loader/Kconfig +++ b/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 diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 98ab4de03d..9c50955c9b 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/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; @@ -214,7 +214,7 @@ out: #endif } -#ifdef CONFIG_DM_RTC +#ifdef CONFIG_EFI_SET_TIME /** * efi_validate_time() - checks if timestamp is valid @@ -252,7 +252,7 @@ static int efi_validate_time(struct efi_time *time) */ 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; diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index cfbb40c891..b032154147 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -27,7 +27,6 @@ efi_selftest_loaded_image.o \ efi_selftest_manageprotocols.o \ efi_selftest_memory.o \ efi_selftest_register_notify.o \ -efi_selftest_rtc.o \ efi_selftest_snp.o \ efi_selftest_textinput.o \ efi_selftest_textinputex.o \ @@ -43,6 +42,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 diff --git a/lib/efi_selftest/efi_selftest_rtc.c b/lib/efi_selftest/efi_selftest_rtc.c index 9eb29add3b..6f7035dee6 100644 --- a/lib/efi_selftest/efi_selftest_rtc.c +++ b/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; } From 755d42d4209eda07836b256730e8686c37b18939 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 1 Jun 2019 19:29:39 +0200 Subject: [PATCH 14/17] efi_loader: correct HandleProtocol() The UEFI specification requires that when a protocol is opened via HandleProtocol() the agent handle is the image handle of the EFI firmware (see chapter on EFI_BOOT_SERVICES.OpenProtocol()). Let efi_handle_protocol() pass efi_root as agent handle to efi_open_protocol(). Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index f5b5828f93..481f9b9d3e 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3073,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); } From b4863baa68dd589092a9823f8c0bc513c24dc3a3 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 1 Jun 2019 20:15:10 +0200 Subject: [PATCH 15/17] efi_loader: open protocol information When a protocol is opened the open protocol information must be updated. The key fields of the open protocol information records are ImageHandle, ControllerHandle, and Attributes. Consider the Attributes field when determining if an open protocol information record has to be updated or a new one has to be created. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 481f9b9d3e..1a0e09807a 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2684,7 +2684,8 @@ disconnect_next: /* 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 */ From 7e572cf69d3e1756ee79ca29cee5bbb317f35f6b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 1 Jun 2019 20:54:32 +0200 Subject: [PATCH 16/17] efi_loader: CloseProtocol() fix open protocol information CloseProtocol() must delete all open protocol information records relating to import parameters not only one. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1a0e09807a..5c6bc691a5 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2056,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: From 7950e8e2ebcd0f733ae2b00dbefefe1b742514bc Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 1 Jun 2019 21:00:50 +0200 Subject: [PATCH 17/17] efi_selftest: unit test for OpenProtocolInformation() Provide a unit test that checks that the open protocol information is correctly updated when opening and closing protocols. Signed-off-by: Heinrich Schuchardt --- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_open_protocol.c | 205 ++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_open_protocol.c diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index b032154147..3bebd0f573 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -26,6 +26,7 @@ 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_snp.o \ efi_selftest_textinput.o \ diff --git a/lib/efi_selftest/efi_selftest_open_protocol.c b/lib/efi_selftest/efi_selftest_open_protocol.c new file mode 100644 index 0000000000..e3f351df72 --- /dev/null +++ b/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 + * + * This unit test checks that open protocol information is correctly updated + * when calling: + * HandleProtocol, OpenProtocol, OpenProtocolInformation, CloseProtocol. + */ + +#include + +/* + * 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, +};