summaryrefslogtreecommitdiff
path: root/lib/efi_loader
diff options
context:
space:
mode:
Diffstat (limited to 'lib/efi_loader')
-rw-r--r--lib/efi_loader/Kconfig94
-rw-r--r--lib/efi_loader/Makefile6
-rw-r--r--lib/efi_loader/efi_bootmgr.c24
-rw-r--r--lib/efi_loader/efi_boottime.c615
-rw-r--r--lib/efi_loader/efi_console.c12
-rw-r--r--lib/efi_loader/efi_device_path_to_text.c7
-rw-r--r--lib/efi_loader/efi_image_loader.c2
-rw-r--r--lib/efi_loader/efi_memory.c105
-rw-r--r--lib/efi_loader/efi_net.c6
-rw-r--r--lib/efi_loader/efi_root_node.c57
-rw-r--r--lib/efi_loader/efi_runtime.c120
-rw-r--r--lib/efi_loader/efi_setup.c27
-rw-r--r--lib/efi_loader/efi_unicode_collation.c35
-rw-r--r--lib/efi_loader/efi_variable.c44
14 files changed, 910 insertions, 244 deletions
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 50b050159c3..cd5436c576b 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -1,5 +1,5 @@
config EFI_LOADER
- bool "Support running EFI Applications in U-Boot"
+ bool "Support running UEFI applications"
depends on (ARM || X86 || RISCV || SANDBOX) && OF_LIBFDT
# We need EFI_STUB_64BIT to be set on x86_64 with EFI_STUB
depends on !EFI_STUB || !X86_64 || EFI_STUB_64BIT
@@ -11,14 +11,58 @@ config EFI_LOADER
select REGEX
imply CFB_CONSOLE_ANSI
help
- Select this option if you want to run EFI applications (like grub2)
- on top of U-Boot. If this option is enabled, U-Boot will expose EFI
- interfaces to a loaded EFI application, enabling it to reuse U-Boot's
- device drivers.
+ Select this option if you want to run UEFI applications (like GNU
+ GRUB or iPXE) on top of U-Boot. If this option is enabled, U-Boot
+ will expose the UEFI API to a loaded application, enabling it to
+ reuse U-Boot's device drivers.
+
+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
+ help
+ The device path to text protocol converts device nodes and paths to
+ human readable strings.
+
+config EFI_LOADER_HII
+ bool "HII protocols"
+ default y
+ help
+ The Human Interface Infrastructure is a complicated framework that
+ allows UEFI applications to draw fancy menus and hook strings using
+ a translation framework.
+
+ U-Boot implements enough of its features to be able to run the UEFI
+ Shell, but not more than that.
+
+config EFI_UNICODE_COLLATION_PROTOCOL2
+ bool "Unicode collation protocol"
+ default y
+ help
+ The Unicode collation protocol is used for lexical comparisons. It is
+ required to run the UEFI shell.
+
+if EFI_UNICODE_COLLATION_PROTOCOL2
config EFI_UNICODE_CAPITALIZATION
bool "Support Unicode capitalization"
- depends on EFI_LOADER
default y
help
Select this option to enable correct handling of the capitalization of
@@ -26,33 +70,35 @@ config EFI_UNICODE_CAPITALIZATION
set, only the the correct handling of the letters of the codepage
used by the FAT file system is ensured.
-config EFI_PLATFORM_LANG_CODES
- string "Language codes supported by firmware"
- depends on EFI_LOADER
- default "en-US"
+config EFI_UNICODE_COLLATION_PROTOCOL
+ bool "Deprecated version of the Unicode collation protocol"
+ default n
help
- This value is used to initialize the PlatformLangCodes variable. Its
- value is a semicolon (;) separated list of language codes in native
- RFC 4646 format, e.g. "en-US;de-DE". The first language code is used
- to initialize the PlatformLang variable.
+ In EFI 1.10 a version of the Unicode collation protocol using ISO
+ 639-2 language codes existed. This protocol is not part of the UEFI
+ specification any longer. Unfortunately it is required to run the
+ UEFI Self Certification Test (SCT) II, version 2.6, 2017.
+
+ Choose this option for testing only. It is bound to be removed.
+
+endif
config EFI_LOADER_BOUNCE_BUFFER
bool "EFI Applications use bounce buffers for DMA operations"
- depends on EFI_LOADER && ARM64
+ depends on ARM64
default n
help
Some hardware does not support DMA to full 64bit addresses. For this
hardware we can create a bounce buffer so that payloads don't have to
worry about platform details.
-config EFI_LOADER_HII
- bool "Expose HII protocols to EFI applications"
- depends on EFI_LOADER
- default y
+config EFI_PLATFORM_LANG_CODES
+ string "Language codes supported by firmware"
+ default "en-US"
help
- The Human Interface Infrastructure is a complicated framework that
- allows UEFI applications to draw fancy menus and hook strings using
- a translation framework.
+ This value is used to initialize the PlatformLangCodes variable. Its
+ value is a semicolon (;) separated list of language codes in native
+ RFC 4646 format, e.g. "en-US;de-DE". The first language code is used
+ to initialize the PlatformLang variable.
- U-Boot implements enough of its features to be able to run the UEFI
- Shell, but not more than that.
+endif
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 4e90a35896c..01769ea58ba 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -21,16 +21,16 @@ obj-y += efi_bootmgr.o
obj-y += efi_boottime.o
obj-y += efi_console.o
obj-y += efi_device_path.o
-obj-y += efi_device_path_to_text.o
+obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
obj-y += efi_device_path_utilities.o
obj-y += efi_file.o
-obj-y += efi_hii.o efi_hii_config.o
+obj-$(CONFIG_EFI_LOADER_HII) += efi_hii.o efi_hii_config.o
obj-y += efi_image_loader.o
obj-y += efi_memory.o
obj-y += efi_root_node.o
obj-y += efi_runtime.o
obj-y += efi_setup.o
-obj-y += efi_unicode_collation.o
+obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += efi_unicode_collation.o
obj-y += efi_variable.o
obj-y += efi_watchdog.o
obj-$(CONFIG_LCD) += efi_gop.o
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 4ccba228757..b2102c5b5af 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -53,19 +53,20 @@ void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data)
*/
unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
{
- unsigned long label_len, option_len;
+ unsigned long label_len;
unsigned long size;
u8 *p;
label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
- option_len = strlen((char *)lo->optional_data);
/* total size */
size = sizeof(lo->attributes);
size += sizeof(lo->file_path_length);
size += label_len;
size += lo->file_path_length;
- size += option_len + 1;
+ if (lo->optional_data)
+ size += (utf8_utf16_strlen((const char *)lo->optional_data)
+ + 1) * sizeof(u16);
p = malloc(size);
if (!p)
return 0;
@@ -84,10 +85,10 @@ unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
memcpy(p, lo->file_path, lo->file_path_length);
p += lo->file_path_length;
- memcpy(p, lo->optional_data, option_len);
- p += option_len;
- *(char *)p = '\0';
-
+ if (lo->optional_data) {
+ utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
+ p += sizeof(u16); /* size of trailing \0 */
+ }
return size;
}
@@ -148,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;
@@ -206,7 +210,8 @@ efi_status_t efi_bootmgr_load(efi_handle_t *handle)
ret = EFI_CALL(efi_set_variable(
L"BootNext",
(efi_guid_t *)&efi_global_variable_guid,
- 0, 0, &bootnext));
+ EFI_VARIABLE_NON_VOLATILE, 0,
+ &bootnext));
/* load BootNext */
if (ret == EFI_SUCCESS) {
@@ -214,6 +219,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");
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 601b0a2cb88..7d1d6e92138 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -13,6 +13,7 @@
#include <linux/libfdt_env.h>
#include <u-boot/crc.h>
#include <bootm.h>
+#include <pe.h>
#include <watchdog.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -26,6 +27,9 @@ LIST_HEAD(efi_obj_list);
/* List of all events */
LIST_HEAD(efi_events);
+/* List of all events registered by RegisterProtocolNotify() */
+LIST_HEAD(efi_register_notify_events);
+
/* Handle of the currently executing image */
static efi_handle_t current_image;
@@ -177,10 +181,12 @@ static void efi_queue_event(struct efi_event *event, bool check_tpl)
/* Check TPL */
if (check_tpl && efi_tpl >= event->notify_tpl)
return;
+ event->is_queued = false;
EFI_CALL_VOID(event->notify_function(event,
event->notify_context));
+ } else {
+ event->is_queued = false;
}
- event->is_queued = false;
}
/**
@@ -238,7 +244,7 @@ void efi_signal_event(struct efi_event *event, bool check_tpl)
if (evt->is_queued)
efi_queue_event(evt, check_tpl);
}
- } else if (!event->is_signaled) {
+ } else {
event->is_signaled = true;
if (event->type & EVT_NOTIFY_SIGNAL)
efi_queue_event(event, check_tpl);
@@ -263,7 +269,7 @@ static unsigned long EFIAPI efi_raise_tpl(efi_uintn_t new_tpl)
EFI_ENTRY("0x%zx", new_tpl);
if (new_tpl < efi_tpl)
- debug("WARNING: new_tpl < current_tpl in %s\n", __func__);
+ EFI_PRINT("WARNING: new_tpl < current_tpl in %s\n", __func__);
efi_tpl = new_tpl;
if (efi_tpl > TPL_HIGH_LEVEL)
efi_tpl = TPL_HIGH_LEVEL;
@@ -286,7 +292,7 @@ static void EFIAPI efi_restore_tpl(efi_uintn_t old_tpl)
EFI_ENTRY("0x%zx", old_tpl);
if (old_tpl > efi_tpl)
- debug("WARNING: old_tpl > current_tpl in %s\n", __func__);
+ EFI_PRINT("WARNING: old_tpl > current_tpl in %s\n", __func__);
efi_tpl = old_tpl;
if (efi_tpl > TPL_HIGH_LEVEL)
efi_tpl = TPL_HIGH_LEVEL;
@@ -423,10 +429,12 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer)
}
/**
- * efi_add_handle() - add a new object to the object list
- * @obj: object to be added
+ * efi_add_handle() - add a new handle to the object list
*
- * The protocols list is initialized. The object handle is set.
+ * @handle: handle to be added
+ *
+ * The protocols list is initialized. The handle is added to the list of known
+ * UEFI objects.
*/
void efi_add_handle(efi_handle_t handle)
{
@@ -507,10 +515,8 @@ efi_status_t efi_remove_protocol(const efi_handle_t handle,
ret = efi_search_protocol(handle, protocol, &handler);
if (ret != EFI_SUCCESS)
return ret;
- if (guidcmp(handler->guid, protocol))
- return EFI_INVALID_PARAMETER;
if (handler->protocol_interface != protocol_interface)
- return EFI_INVALID_PARAMETER;
+ return EFI_NOT_FOUND;
list_del(&handler->link);
free(handler);
return EFI_SUCCESS;
@@ -618,7 +624,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
}
if ((type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) &&
- (is_valid_tpl(notify_tpl) != EFI_SUCCESS))
+ (!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS))
return EFI_INVALID_PARAMETER;
evt = calloc(1, sizeof(struct efi_event));
@@ -662,10 +668,26 @@ efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl,
efi_guid_t *event_group,
struct efi_event **event)
{
+ efi_status_t ret;
+
EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function,
notify_context, event_group);
- return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
- notify_context, event_group, event));
+
+ /*
+ * The allowable input parameters are the same as in CreateEvent()
+ * except for the following two disallowed event types.
+ */
+ switch (type) {
+ case EVT_SIGNAL_EXIT_BOOT_SERVICES:
+ case EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_create_event(type, notify_tpl, notify_function,
+ notify_context, event_group, event);
+out:
+ return EFI_EXIT(ret);
}
/**
@@ -889,9 +911,29 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
*/
static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
{
+ struct efi_register_notify_event *item, *next;
+
EFI_ENTRY("%p", event);
if (efi_is_event(event) != EFI_SUCCESS)
return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* Remove protocol notify registrations for the 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);
+ }
+ }
+
list_del(&event->link);
free(event);
return EFI_EXIT(EFI_SUCCESS);
@@ -937,11 +979,13 @@ struct efi_object *efi_search_obj(const efi_handle_t handle)
{
struct efi_object *efiobj;
+ if (!handle)
+ return NULL;
+
list_for_each_entry(efiobj, &efi_obj_list, link) {
if (efiobj == handle)
return efiobj;
}
-
return NULL;
}
@@ -995,6 +1039,7 @@ efi_status_t efi_add_protocol(const efi_handle_t handle,
struct efi_object *efiobj;
struct efi_handler *handler;
efi_status_t ret;
+ struct efi_register_notify_event *event;
efiobj = efi_search_obj(handle);
if (!efiobj)
@@ -1009,6 +1054,24 @@ efi_status_t efi_add_protocol(const efi_handle_t handle,
handler->protocol_interface = protocol_interface;
INIT_LIST_HEAD(&handler->open_infos);
list_add_tail(&handler->link, &efiobj->protocols);
+
+ /* Notify registered events */
+ list_for_each_entry(event, &efi_register_notify_events, link) {
+ 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))
EFI_PRINT("installed device path '%pD'\n", protocol_interface);
return EFI_SUCCESS;
@@ -1049,11 +1112,9 @@ static efi_status_t EFIAPI efi_install_protocol_interface(
r = efi_create_handle(handle);
if (r != EFI_SUCCESS)
goto out;
- debug("%sEFI: new handle %p\n", indent_string(nesting_level),
- *handle);
+ EFI_PRINT("new handle %p\n", *handle);
} else {
- debug("%sEFI: handle %p\n", indent_string(nesting_level),
- *handle);
+ EFI_PRINT("handle %p\n", *handle);
}
/* Add new protocol */
r = efi_add_protocol(*handle, protocol, protocol_interface);
@@ -1092,11 +1153,15 @@ static efi_status_t efi_get_drivers(efi_handle_t handle,
++count;
}
}
+ *number_of_drivers = 0;
+ if (!count) {
+ *driver_handle_buffer = NULL;
+ return EFI_SUCCESS;
+ }
/*
* Create buffer. In case of duplicate driver assignments the buffer
* will be too large. But that does not harm.
*/
- *number_of_drivers = 0;
*driver_handle_buffer = calloc(count, sizeof(efi_handle_t));
if (!*driver_handle_buffer)
return EFI_OUT_OF_RESOURCES;
@@ -1152,7 +1217,8 @@ static efi_status_t efi_disconnect_all_drivers
&driver_handle_buffer);
if (ret != EFI_SUCCESS)
return ret;
-
+ if (!number_of_drivers)
+ return EFI_SUCCESS;
ret = EFI_NOT_FOUND;
while (number_of_drivers) {
r = EFI_CALL(efi_disconnect_controller(
@@ -1199,10 +1265,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 ==
@@ -1272,8 +1334,31 @@ static efi_status_t EFIAPI efi_register_protocol_notify(
struct efi_event *event,
void **registration)
{
+ struct efi_register_notify_event *item;
+ efi_status_t ret = EFI_SUCCESS;
+
EFI_ENTRY("%pUl, %p, %p", protocol, event, registration);
- return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ if (!protocol || !event || !registration) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ item = calloc(1, sizeof(struct efi_register_notify_event));
+ if (!item) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ 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);
+
+ *registration = item;
+out:
+ return EFI_EXIT(ret);
}
/**
@@ -1288,17 +1373,13 @@ static efi_status_t EFIAPI efi_register_protocol_notify(
* Return: 0 if the handle implements the protocol
*/
static int efi_search(enum efi_locate_search_type search_type,
- const efi_guid_t *protocol, void *search_key,
- efi_handle_t handle)
+ const efi_guid_t *protocol, efi_handle_t handle)
{
efi_status_t ret;
switch (search_type) {
case ALL_HANDLES:
return 0;
- case BY_REGISTER_NOTIFY:
- /* TODO: RegisterProtocolNotify is not implemented yet */
- return -1;
case BY_PROTOCOL:
ret = efi_search_protocol(handle, protocol, NULL);
return (ret != EFI_SUCCESS);
@@ -1309,12 +1390,34 @@ 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
- * @search_type: selection criterion
- * @protocol: GUID of the protocol
- * @search_key: registration key
- * @buffer_size: size of the buffer to receive the handles in bytes
- * @buffer: buffer to receive the relevant handles
+ *
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @buffer_size: size of the buffer to receive the handles in bytes
+ * @buffer: buffer to receive the relevant handles
*
* This function is meant for U-Boot internal calls. For the API implementation
* of the LocateHandle service see efi_locate_handle_ext.
@@ -1328,6 +1431,8 @@ static efi_status_t efi_locate_handle(
{
struct efi_object *efiobj;
efi_uintn_t size = 0;
+ struct efi_register_notify_event *event;
+ struct efi_protocol_notification *handle = NULL;
/* Check parameters */
switch (search_type) {
@@ -1336,8 +1441,11 @@ static efi_status_t efi_locate_handle(
case BY_REGISTER_NOTIFY:
if (!search_key)
return EFI_INVALID_PARAMETER;
- /* RegisterProtocolNotify is not implemented yet */
- return EFI_UNSUPPORTED;
+ /* Check that the registration key is valid */
+ event = efi_check_register_notify_event(search_key);
+ if (!event)
+ return EFI_INVALID_PARAMETER;
+ break;
case BY_PROTOCOL:
if (!protocol)
return EFI_INVALID_PARAMETER;
@@ -1346,33 +1454,47 @@ static efi_status_t efi_locate_handle(
return EFI_INVALID_PARAMETER;
}
- /*
- * efi_locate_handle_buffer uses this function for
- * the calculation of the necessary buffer size.
- * So do not require a buffer for buffersize == 0.
- */
- if (!buffer_size || (*buffer_size && !buffer))
- return EFI_INVALID_PARAMETER;
-
/* Count how much space we need */
- list_for_each_entry(efiobj, &efi_obj_list, link) {
- if (!efi_search(search_type, protocol, search_key, 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 (!buffer_size)
+ return EFI_INVALID_PARAMETER;
+
if (*buffer_size < size) {
*buffer_size = size;
return EFI_BUFFER_TOO_SMALL;
}
*buffer_size = size;
- if (size == 0)
- return EFI_NOT_FOUND;
+
+ /* The buffer size is sufficient but there is no buffer */
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
/* Then fill the array */
- list_for_each_entry(efiobj, &efi_obj_list, link) {
- if (!efi_search(search_type, protocol, search_key, 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;
@@ -1536,6 +1658,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
free(info);
return EFI_OUT_OF_RESOURCES;
}
+ obj->header.type = EFI_OBJECT_TYPE_LOADED_IMAGE;
/* Add internal object to object list */
efi_add_handle(&obj->header);
@@ -1685,7 +1808,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
file_path, source_buffer, source_size, image_handle);
- if (!image_handle || !parent_image) {
+ if (!image_handle || !efi_search_obj(parent_image)) {
ret = EFI_INVALID_PARAMETER;
goto error;
}
@@ -1694,6 +1817,11 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
ret = EFI_NOT_FOUND;
goto error;
}
+ /* The parent image handle must refer to a loaded image */
+ if (!parent_image->type) {
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
if (!source_buffer) {
ret = efi_load_image_from_path(file_path, &dest_buffer,
@@ -1701,6 +1829,10 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
if (ret != EFI_SUCCESS)
goto error;
} else {
+ if (!source_size) {
+ ret = EFI_LOAD_ERROR;
+ goto error;
+ }
dest_buffer = source_buffer;
}
/* split file_path which contains both the device and file parts */
@@ -1726,29 +1858,6 @@ error:
}
/**
- * efi_unload_image() - unload an EFI image
- * @image_handle: handle of the image to be unloaded
- *
- * This function implements the UnloadImage service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * Return: status code
- */
-efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
-{
- struct efi_object *efiobj;
-
- EFI_ENTRY("%p", image_handle);
- efiobj = efi_search_obj(image_handle);
- if (efiobj)
- list_del(&efiobj->link);
-
- return EFI_EXIT(EFI_SUCCESS);
-}
-
-/**
* efi_exit_caches() - fix up caches for EFI payloads if necessary
*/
static void efi_exit_caches(void)
@@ -1780,11 +1889,11 @@ static void efi_exit_caches(void)
* Return: status code
*/
static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
- unsigned long map_key)
+ efi_uintn_t map_key)
{
struct efi_event *evt;
- EFI_ENTRY("%p, %ld", image_handle, map_key);
+ EFI_ENTRY("%p, %zx", image_handle, map_key);
/* Check that the caller has read the current memory map */
if (map_key != efi_memory_map_key)
@@ -1855,10 +1964,17 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
{
static uint64_t mono;
+ efi_status_t ret;
EFI_ENTRY("%p", count);
+ if (!count) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
*count = mono++;
- return EFI_EXIT(EFI_SUCCESS);
+ ret = EFI_SUCCESS;
+out:
+ return EFI_EXIT(ret);
}
/**
@@ -1874,8 +1990,14 @@ static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
*/
static efi_status_t EFIAPI efi_stall(unsigned long microseconds)
{
+ u64 end_tick;
+
EFI_ENTRY("%ld", microseconds);
- udelay(microseconds);
+
+ end_tick = get_ticks() + usec_to_tick(microseconds);
+ while (get_ticks() < end_tick)
+ efi_timer_check();
+
return EFI_EXIT(EFI_SUCCESS);
}
@@ -1930,7 +2052,8 @@ static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle,
controller_handle);
- if (!agent_handle) {
+ if (!efi_search_obj(agent_handle) ||
+ (controller_handle && !efi_search_obj(controller_handle))) {
r = EFI_INVALID_PARAMETER;
goto out;
}
@@ -1944,7 +2067,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:
@@ -2143,29 +2265,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);
}
/**
@@ -2199,7 +2350,7 @@ static efi_status_t EFIAPI efi_locate_device_path(
EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
- if (!protocol || !device_path || !*device_path || !device) {
+ if (!protocol || !device_path || !*device_path) {
ret = EFI_INVALID_PARAMETER;
goto out;
}
@@ -2232,6 +2383,10 @@ static efi_status_t EFIAPI efi_locate_device_path(
/* Check if dp is a subpath of device_path */
if (memcmp(*device_path, dp, len_dp))
continue;
+ if (!device) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
*device = handles[i];
len_best = len_dp;
}
@@ -2268,6 +2423,7 @@ efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
efi_va_list argptr;
const efi_guid_t *protocol;
void *protocol_interface;
+ efi_handle_t old_handle;
efi_status_t r = EFI_SUCCESS;
int i = 0;
@@ -2280,6 +2436,20 @@ efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
if (!protocol)
break;
protocol_interface = efi_va_arg(argptr, void*);
+ /* Check that a device path has not been installed before */
+ if (!guidcmp(protocol, &efi_guid_device_path)) {
+ struct efi_device_path *dp = protocol_interface;
+
+ r = EFI_CALL(efi_locate_device_path(protocol, &dp,
+ &old_handle));
+ if (r == EFI_SUCCESS &&
+ dp->type == DEVICE_PATH_TYPE_END) {
+ EFI_PRINT("Path %pD already installed\n",
+ protocol_interface);
+ r = EFI_ALREADY_STARTED;
+ break;
+ }
+ }
r = EFI_CALL(efi_install_protocol_interface(
handle, protocol,
EFI_NATIVE_INTERFACE,
@@ -2387,9 +2557,16 @@ static efi_status_t EFIAPI efi_calculate_crc32(const void *data,
efi_uintn_t data_size,
u32 *crc32_p)
{
+ efi_status_t ret = EFI_SUCCESS;
+
EFI_ENTRY("%p, %zu", data, data_size);
+ if (!data || !data_size || !crc32_p) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
*crc32_p = crc32(0, data, data_size);
- return EFI_EXIT(EFI_SUCCESS);
+out:
+ return EFI_EXIT(ret);
}
/**
@@ -2466,34 +2643,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;
}
@@ -2501,7 +2694,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 */
@@ -2583,8 +2777,15 @@ static efi_status_t EFIAPI efi_open_protocol
}
r = efi_search_protocol(handle, protocol, &handler);
- if (r != EFI_SUCCESS)
+ switch (r) {
+ case EFI_SUCCESS:
+ break;
+ case EFI_NOT_FOUND:
+ r = EFI_UNSUPPORTED;
+ goto out;
+ default:
goto out;
+ }
r = efi_protocol_open(handler, protocol_interface, agent_handle,
controller_handle, attributes);
@@ -2626,6 +2827,9 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
efi_is_direct_boot = false;
+ image_obj->exit_data_size = exit_data_size;
+ image_obj->exit_data = exit_data;
+
/* call the image! */
if (setjmp(&image_obj->exit_jmp)) {
/*
@@ -2648,15 +2852,15 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
* missed out steps of EFI_CALL.
*/
assert(__efi_entry_check());
- debug("%sEFI: %lu returned by started image\n",
- __efi_nesting_dec(),
- (unsigned long)((uintptr_t)image_obj->exit_status &
- ~EFI_ERROR_MASK));
+ EFI_PRINT("%lu returned by started image\n",
+ (unsigned long)((uintptr_t)image_obj->exit_status &
+ ~EFI_ERROR_MASK));
current_image = parent_image;
return EFI_EXIT(image_obj->exit_status);
}
current_image = image_handle;
+ image_obj->header.type = EFI_OBJECT_TYPE_STARTED_IMAGE;
EFI_PRINT("Jumping into 0x%p\n", image_obj->entry);
ret = EFI_CALL(image_obj->entry(image_handle, &systab));
@@ -2670,6 +2874,145 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
}
/**
+ * efi_delete_image() - delete loaded image from memory)
+ *
+ * @image_obj: handle of the loaded image
+ * @loaded_image_protocol: loaded image protocol
+ */
+static efi_status_t efi_delete_image
+ (struct efi_loaded_image_obj *image_obj,
+ struct efi_loaded_image *loaded_image_protocol)
+{
+ struct efi_object *efiobj;
+ efi_status_t r, ret = EFI_SUCCESS;
+
+close_next:
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ struct efi_handler *protocol;
+
+ list_for_each_entry(protocol, &efiobj->protocols, link) {
+ struct efi_open_protocol_info_item *info;
+
+ list_for_each_entry(info, &protocol->open_infos, link) {
+ if (info->info.agent_handle !=
+ (efi_handle_t)image_obj)
+ continue;
+ r = EFI_CALL(efi_close_protocol
+ (efiobj, protocol->guid,
+ info->info.agent_handle,
+ info->info.controller_handle
+ ));
+ if (r != EFI_SUCCESS)
+ ret = r;
+ /*
+ * Closing protocols may results in further
+ * items being deleted. To play it safe loop
+ * over all elements again.
+ */
+ goto close_next;
+ }
+ }
+ }
+
+ efi_free_pages((uintptr_t)loaded_image_protocol->image_base,
+ efi_size_in_pages(loaded_image_protocol->image_size));
+ efi_delete_handle(&image_obj->header);
+
+ return ret;
+}
+
+/**
+ * efi_unload_image() - unload an EFI image
+ * @image_handle: handle of the image to be unloaded
+ *
+ * This function implements the UnloadImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct efi_object *efiobj;
+ struct efi_loaded_image *loaded_image_protocol;
+
+ EFI_ENTRY("%p", image_handle);
+
+ efiobj = efi_search_obj(image_handle);
+ if (!efiobj) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* Find the loaded image protocol */
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
+ (void **)&loaded_image_protocol,
+ NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ switch (efiobj->type) {
+ case EFI_OBJECT_TYPE_STARTED_IMAGE:
+ /* Call the unload function */
+ if (!loaded_image_protocol->unload) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ ret = EFI_CALL(loaded_image_protocol->unload(image_handle));
+ if (ret != EFI_SUCCESS)
+ goto out;
+ break;
+ case EFI_OBJECT_TYPE_LOADED_IMAGE:
+ break;
+ default:
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ efi_delete_image((struct efi_loaded_image_obj *)efiobj,
+ loaded_image_protocol);
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_update_exit_data() - fill exit data parameters of StartImage()
+ *
+ * @image_obj image handle
+ * @exit_data_size size of the exit data buffer
+ * @exit_data buffer with data returned by UEFI payload
+ * Return: status code
+ */
+static efi_status_t efi_update_exit_data(struct efi_loaded_image_obj *image_obj,
+ efi_uintn_t exit_data_size,
+ u16 *exit_data)
+{
+ efi_status_t ret;
+
+ /*
+ * If exit_data is not provided to StartImage(), exit_data_size must be
+ * ignored.
+ */
+ if (!image_obj->exit_data)
+ return EFI_SUCCESS;
+ if (image_obj->exit_data_size)
+ *image_obj->exit_data_size = exit_data_size;
+ if (exit_data_size && exit_data) {
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA,
+ exit_data_size,
+ (void **)image_obj->exit_data);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ memcpy(*image_obj->exit_data, exit_data, exit_data_size);
+ } else {
+ image_obj->exit_data = NULL;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
* efi_exit() - leave an EFI application or driver
* @image_handle: handle of the application or driver that is exiting
* @exit_status: status code
@@ -2693,7 +3036,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
* image protocol.
*/
efi_status_t ret;
- void *info;
+ struct efi_loaded_image *loaded_image_protocol;
struct efi_loaded_image_obj *image_obj =
(struct efi_loaded_image_obj *)image_handle;
@@ -2701,13 +3044,45 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
exit_data_size, exit_data);
/* Check parameters */
- if (image_handle != current_image)
- goto out;
ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
- &info, NULL, NULL,
+ (void **)&loaded_image_protocol,
+ NULL, NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL));
- if (ret != EFI_SUCCESS)
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_INVALID_PARAMETER;
goto out;
+ }
+
+ /* Unloading of unstarted images */
+ switch (image_obj->header.type) {
+ case EFI_OBJECT_TYPE_STARTED_IMAGE:
+ break;
+ case EFI_OBJECT_TYPE_LOADED_IMAGE:
+ efi_delete_image(image_obj, loaded_image_protocol);
+ ret = EFI_SUCCESS;
+ goto out;
+ default:
+ /* Handle does not refer to loaded image */
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* A started image can only be unloaded it is the last one started. */
+ if (image_handle != current_image) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Exit data is only foreseen in case of failure. */
+ if (exit_status != EFI_SUCCESS) {
+ ret = efi_update_exit_data(image_obj, exit_data_size,
+ exit_data);
+ /* Exiting has priority. Don't return error to caller. */
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("%s: out of memory\n", __func__);
+ }
+ if (image_obj->image_type == IMAGE_SUBSYSTEM_EFI_APPLICATION ||
+ exit_status != EFI_SUCCESS)
+ efi_delete_image(image_obj, loaded_image_protocol);
/* Make sure entry/exit counts for EFI world cross-overs match */
EFI_EXIT(exit_status);
@@ -2723,7 +3098,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
panic("EFI application exited");
out:
- return EFI_EXIT(EFI_INVALID_PARAMETER);
+ return EFI_EXIT(ret);
}
/**
@@ -2743,7 +3118,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);
}
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index 051fc1d339f..3b7578f3aa4 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -136,6 +136,11 @@ static efi_status_t EFIAPI efi_cout_output_string(
EFI_ENTRY("%p, %p", this, string);
+ if (!this || !string) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
buf = malloc(utf16_utf8_strlen(string) + 1);
if (!buf) {
ret = EFI_OUT_OF_RESOURCES;
@@ -425,6 +430,7 @@ static efi_status_t EFIAPI efi_cout_enable_cursor(
EFI_ENTRY("%p, %d", this, enable);
printf(ESC"[?25%c", enable ? 'h' : 'l');
+ efi_con_mode.cursor_visible = !!enable;
return EFI_EXIT(EFI_SUCCESS);
}
@@ -825,7 +831,7 @@ out:
* efi_cin_set_state() - set toggle key state
*
* @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
- * @key_toggle_state: key toggle state
+ * @key_toggle_state: pointer to key toggle state
* Return: status code
*
* This function implements the SetState service of the
@@ -836,9 +842,9 @@ out:
*/
static efi_status_t EFIAPI efi_cin_set_state(
struct efi_simple_text_input_ex_protocol *this,
- u8 key_toggle_state)
+ u8 *key_toggle_state)
{
- EFI_ENTRY("%p, %u", this, key_toggle_state);
+ EFI_ENTRY("%p, %p", this, key_toggle_state);
/*
* U-Boot supports multiple console input sources like serial and
* net console for which a key toggle state cannot be set at all.
diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c
index e219f84b28d..96fd08971b7 100644
--- a/lib/efi_loader/efi_device_path_to_text.c
+++ b/lib/efi_loader/efi_device_path_to_text.c
@@ -78,10 +78,9 @@ 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));
- if (adp->uid)
- s += sprintf(s, ",%d", adp->uid);
- s += sprintf(s, ")");
+
+ s += sprintf(s, "Acpi(PNP%04X,%d)", EISA_PNP_NUM(adp->hid),
+ adp->uid);
break;
}
default:
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index f8092b62026..13541cfa7a2 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -273,6 +273,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
image_base = opt->ImageBase;
efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
+ handle->image_type = opt->Subsystem;
efi_reloc = efi_alloc(virt_size,
loaded_image_info->image_code_type);
if (!efi_reloc) {
@@ -288,6 +289,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
image_base = opt->ImageBase;
efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
+ handle->image_type = opt->Subsystem;
efi_reloc = efi_alloc(virt_size,
loaded_image_info->image_code_type);
if (!efi_reloc) {
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index 987cc6dc5f6..386cf924fe2 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -230,6 +230,7 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
struct efi_mem_list *newlist;
bool carve_again;
uint64_t carved_pages = 0;
+ struct efi_event *evt;
EFI_PRINT("%s: 0x%llx 0x%llx %d %s\n", __func__,
start, pages, memory_type, overlap_only_ram ? "yes" : "no");
@@ -315,9 +316,57 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
/* And make sure memory is listed in descending order */
efi_mem_sort();
+ /* Notify that the memory map was changed */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !guidcmp(evt->group,
+ &efi_guid_event_group_memory_map_change)) {
+ efi_signal_event(evt, false);
+ break;
+ }
+ }
+
return start;
}
+/**
+ * efi_check_allocated() - validate address to be freed
+ *
+ * Check that the address is within allocated memory:
+ *
+ * * The address cannot be NULL.
+ * * The address must be in a range of the memory map.
+ * * The address may not point to EFI_CONVENTIONAL_MEMORY.
+ *
+ * Page alignment is not checked as this is not a requirement of
+ * efi_free_pool().
+ *
+ * @addr: address of page to be freed
+ * @must_be_allocated: return success if the page is allocated
+ * Return: status code
+ */
+static efi_status_t efi_check_allocated(u64 addr, bool must_be_allocated)
+{
+ struct efi_mem_list *item;
+
+ if (!addr)
+ return EFI_INVALID_PARAMETER;
+ list_for_each_entry(item, &efi_mem, link) {
+ u64 start = item->desc.physical_start;
+ u64 end = start + (item->desc.num_pages << EFI_PAGE_SHIFT);
+
+ if (addr >= start && addr < end) {
+ if (must_be_allocated ^
+ (item->desc.type == EFI_CONVENTIONAL_MEMORY))
+ return EFI_SUCCESS;
+ else
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr)
{
struct list_head *lhandle;
@@ -373,7 +422,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
efi_uintn_t pages, uint64_t *memory)
{
u64 len = pages << EFI_PAGE_SHIFT;
- efi_status_t r = EFI_SUCCESS;
+ efi_status_t ret;
uint64_t addr;
/* Check import parameters */
@@ -387,43 +436,35 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
case EFI_ALLOCATE_ANY_PAGES:
/* Any page */
addr = efi_find_free_memory(len, -1ULL);
- if (!addr) {
- r = EFI_NOT_FOUND;
- break;
- }
+ if (!addr)
+ return EFI_OUT_OF_RESOURCES;
break;
case EFI_ALLOCATE_MAX_ADDRESS:
/* Max address */
addr = efi_find_free_memory(len, *memory);
- if (!addr) {
- r = EFI_NOT_FOUND;
- break;
- }
+ if (!addr)
+ return EFI_OUT_OF_RESOURCES;
break;
case EFI_ALLOCATE_ADDRESS:
/* Exact address, reserve it. The addr is already in *memory. */
+ ret = efi_check_allocated(*memory, false);
+ if (ret != EFI_SUCCESS)
+ return EFI_NOT_FOUND;
addr = *memory;
break;
default:
/* UEFI doesn't specify other allocation types */
- r = EFI_INVALID_PARAMETER;
- break;
+ return EFI_INVALID_PARAMETER;
}
- if (r == EFI_SUCCESS) {
- uint64_t ret;
+ /* Reserve that map in our memory maps */
+ if (efi_add_memory_map(addr, pages, memory_type, true) != addr)
+ /* Map would overlap, bail out */
+ return EFI_OUT_OF_RESOURCES;
- /* Reserve that map in our memory maps */
- ret = efi_add_memory_map(addr, pages, memory_type, true);
- if (ret == addr) {
- *memory = addr;
- } else {
- /* Map would overlap, bail out */
- r = EFI_OUT_OF_RESOURCES;
- }
- }
+ *memory = addr;
- return r;
+ return EFI_SUCCESS;
}
void *efi_alloc(uint64_t len, int memory_type)
@@ -450,9 +491,14 @@ void *efi_alloc(uint64_t len, int memory_type)
efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
{
uint64_t r = 0;
+ efi_status_t ret;
+
+ ret = efi_check_allocated(memory, true);
+ if (ret != EFI_SUCCESS)
+ return ret;
/* Sanity check */
- if (!memory || (memory & EFI_PAGE_MASK)) {
+ if (!memory || (memory & EFI_PAGE_MASK) || !pages) {
printf("%s: illegal free 0x%llx, 0x%zx\n", __func__,
memory, pages);
return EFI_INVALID_PARAMETER;
@@ -511,11 +557,12 @@ efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer)
*/
efi_status_t efi_free_pool(void *buffer)
{
- efi_status_t r;
+ efi_status_t ret;
struct efi_pool_allocation *alloc;
- if (buffer == NULL)
- return EFI_INVALID_PARAMETER;
+ ret = efi_check_allocated((uintptr_t)buffer, true);
+ if (ret != EFI_SUCCESS)
+ return ret;
alloc = container_of(buffer, struct efi_pool_allocation, data);
@@ -528,9 +575,9 @@ efi_status_t efi_free_pool(void *buffer)
/* Avoid double free */
alloc->checksum = 0;
- r = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
+ ret = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
- return r;
+ return ret;
}
/*
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c
index e0e222a70bf..d71c6630682 100644
--- a/lib/efi_loader/efi_net.c
+++ b/lib/efi_loader/efi_net.c
@@ -392,7 +392,7 @@ static efi_status_t EFIAPI efi_net_transmit
efi_timer_check();
/* Check parameters */
- if (!this) {
+ if (!this || !buffer) {
ret = EFI_INVALID_PARAMETER;
goto out;
}
@@ -408,7 +408,7 @@ static efi_status_t EFIAPI efi_net_transmit
* TODO: We would need to create the header
* if header_size != 0
*/
- ret = EFI_INVALID_PARAMETER;
+ ret = EFI_UNSUPPORTED;
goto out;
}
@@ -466,7 +466,7 @@ static efi_status_t EFIAPI efi_net_receive
efi_timer_check();
/* Check parameters */
- if (!this) {
+ if (!this || !buffer || !buffer_size) {
ret = EFI_INVALID_PARAMETER;
goto out;
}
diff --git a/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c
index e0fcbb85a4d..f68b0fdc610 100644
--- a/lib/efi_loader/efi_root_node.c
+++ b/lib/efi_loader/efi_root_node.c
@@ -28,6 +28,7 @@ struct efi_root_dp {
*/
efi_status_t efi_root_node_register(void)
{
+ efi_status_t ret;
struct efi_root_dp *dp;
/* Create device path protocol */
@@ -47,28 +48,40 @@ efi_status_t efi_root_node_register(void)
dp->end.length = sizeof(struct efi_device_path);
/* Create root node and install protocols */
- return EFI_CALL(efi_install_multiple_protocol_interfaces(&efi_root,
- /* Device path protocol */
- &efi_guid_device_path, dp,
- /* Device path to text protocol */
- &efi_guid_device_path_to_text_protocol,
- (void *)&efi_device_path_to_text,
- /* Device path utilities protocol */
- &efi_guid_device_path_utilities_protocol,
- (void *)&efi_device_path_utilities,
- /* Unicode collation protocol */
- &efi_guid_unicode_collation_protocol,
- (void *)&efi_unicode_collation_protocol,
+ ret = EFI_CALL(efi_install_multiple_protocol_interfaces
+ (&efi_root,
+ /* Device path protocol */
+ &efi_guid_device_path, dp,
+#if CONFIG_IS_ENABLED(EFI_DEVICE_PATH_TO_TEXT)
+ /* Device path to text protocol */
+ &efi_guid_device_path_to_text_protocol,
+ (void *)&efi_device_path_to_text,
+#endif
+ /* Device path utilities protocol */
+ &efi_guid_device_path_utilities_protocol,
+ (void *)&efi_device_path_utilities,
+#if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL2)
+#if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL)
+ /* Deprecated Unicode collation protocol */
+ &efi_guid_unicode_collation_protocol,
+ (void *)&efi_unicode_collation_protocol,
+#endif
+ /* Current Unicode collation protocol */
+ &efi_guid_unicode_collation_protocol2,
+ (void *)&efi_unicode_collation_protocol2,
+#endif
#if CONFIG_IS_ENABLED(EFI_LOADER_HII)
- /* HII string protocol */
- &efi_guid_hii_string_protocol,
- (void *)&efi_hii_string,
- /* HII database protocol */
- &efi_guid_hii_database_protocol,
- (void *)&efi_hii_database,
- /* HII configuration routing protocol */
- &efi_guid_hii_config_routing_protocol,
- (void *)&efi_hii_config_routing,
+ /* HII string protocol */
+ &efi_guid_hii_string_protocol,
+ (void *)&efi_hii_string,
+ /* HII database protocol */
+ &efi_guid_hii_database_protocol,
+ (void *)&efi_hii_database,
+ /* HII configuration routing protocol */
+ &efi_guid_hii_config_routing_protocol,
+ (void *)&efi_hii_config_routing,
#endif
- NULL));
+ NULL));
+ efi_root->type = EFI_OBJECT_TYPE_U_BOOT_FIRMWARE;
+ return ret;
}
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index 636dfdab39d..9c50955c9bd 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -167,9 +167,8 @@ 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;
- int r;
struct rtc_time tm;
struct udevice *dev;
@@ -179,11 +178,12 @@ static efi_status_t EFIAPI efi_get_time_boottime(
ret = EFI_INVALID_PARAMETER;
goto out;
}
-
- r = uclass_get_device(UCLASS_RTC, 0, &dev);
- if (!r)
- r = dm_rtc_get(dev, &tm);
- if (r) {
+ if (uclass_get_device(UCLASS_RTC, 0, &dev) ||
+ dm_rtc_get(dev, &tm)) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ if (dm_rtc_get(dev, &tm)) {
ret = EFI_DEVICE_ERROR;
goto out;
}
@@ -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) {
@@ -210,11 +210,86 @@ out:
return EFI_EXIT(ret);
#else
EFI_ENTRY("%p %p", time, capabilities);
- return EFI_EXIT(EFI_DEVICE_ERROR);
+ return EFI_EXIT(EFI_UNSUPPORTED);
#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
+ *
+ * This function implements the SetTime() runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to with current time
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efi_set_time_boottime(struct efi_time *time)
+{
+#ifdef CONFIG_EFI_SET_TIME
+ efi_status_t ret = EFI_SUCCESS;
+ struct rtc_time tm;
+ struct udevice *dev;
+
+ EFI_ENTRY("%p", time);
+
+ if (efi_validate_time(time)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (uclass_get_device(UCLASS_RTC, 0, &dev)) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = time->year;
+ tm.tm_mon = time->month;
+ tm.tm_mday = time->day;
+ tm.tm_hour = time->hour;
+ tm.tm_min = time->minute;
+ tm.tm_sec = time->second;
+ tm.tm_isdst = time->daylight ==
+ (EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT);
+ /* Calculate day of week */
+ rtc_calc_weekday(&tm);
+
+ if (dm_rtc_set(dev, &tm))
+ ret = EFI_DEVICE_ERROR;
+out:
+ return EFI_EXIT(ret);
+#else
+ EFI_ENTRY("%p", time);
+ return EFI_EXIT(EFI_UNSUPPORTED);
+#endif
+}
/**
* efi_reset_system() - reset system
*
@@ -271,6 +346,24 @@ efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
return EFI_DEVICE_ERROR;
}
+/**
+ * efi_set_time() - set current time
+ *
+ * This function implements the SetTime runtime service after
+ * SetVirtualAddressMap() is called. As the U-Boot driver are not available
+ * anymore only an error code is returned.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to with current time
+ * Returns: status code
+ */
+efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
+{
+ return EFI_UNSUPPORTED;
+}
+
struct efi_runtime_detach_list_struct {
void *ptr;
void *patchto;
@@ -290,6 +383,9 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
.ptr = &efi_runtime_services.get_time,
.patchto = &efi_get_time,
}, {
+ .ptr = &efi_runtime_services.set_time,
+ .patchto = &efi_set_time,
+ }, {
/* Clean up system table */
.ptr = &systab.con_in,
.patchto = NULL,
@@ -697,7 +793,7 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
.headersize = sizeof(struct efi_runtime_services),
},
.get_time = &efi_get_time_boottime,
- .set_time = (void *)&efi_device_error,
+ .set_time = &efi_set_time_boottime,
.get_wakeup_time = (void *)&efi_unimplemented,
.set_wakeup_time = (void *)&efi_unimplemented,
.set_virtual_address_map = &efi_set_virtual_address_map,
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index b32a7b3f934..8691d686d29 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -6,12 +6,22 @@
*/
#include <common.h>
+#include <bootm.h>
#include <efi_loader.h>
#define OBJ_LIST_NOT_INITIALIZED 1
static efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED;
+/*
+ * Allow unaligned memory access.
+ *
+ * This routine is overridden by architectures providing this feature.
+ */
+void __weak allow_unaligned(void)
+{
+}
+
/**
* efi_init_platform_lang() - define supported languages
*
@@ -79,17 +89,34 @@ out:
*/
efi_status_t efi_init_obj_list(void)
{
+ u64 os_indications_supported = 0; /* None */
efi_status_t ret = EFI_SUCCESS;
/* Initialize once only */
if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED)
return efi_obj_list_initialized;
+ /* Allow unaligned memory access */
+ allow_unaligned();
+
+ /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */
+ switch_to_non_secure_mode();
+
/* Define supported languages */
ret = efi_init_platform_lang();
if (ret != EFI_SUCCESS)
goto out;
+ /* Indicate supported features */
+ ret = EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(os_indications_supported),
+ &os_indications_supported));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
/* Initialize system table */
ret = efi_initialize_system_table();
if (ret != EFI_SUCCESS)
diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c
index 7f3ea3c77e4..f293b423975 100644
--- a/lib/efi_loader/efi_unicode_collation.c
+++ b/lib/efi_loader/efi_unicode_collation.c
@@ -12,7 +12,7 @@
#include <efi_loader.h>
/* Characters that may not be used in file names */
-static const char illegal[] = "<>:\"/\\|?*";
+static const char illegal[] = "<>:\"/\\|?*\x7f";
/*
* EDK2 assumes codepage 1250 when creating FAT 8.3 file names.
@@ -26,8 +26,8 @@ static const u16 codepage[] = CP1250;
static const u16 codepage[] = CP437;
#endif
-/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL */
-const efi_guid_t efi_guid_unicode_collation_protocol =
+/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL2 */
+const efi_guid_t efi_guid_unicode_collation_protocol2 =
EFI_UNICODE_COLLATION_PROTOCOL2_GUID;
/**
@@ -318,7 +318,7 @@ static bool EFIAPI efi_str_to_fat(struct efi_unicode_collation_protocol *this,
return ret;
}
-const struct efi_unicode_collation_protocol efi_unicode_collation_protocol = {
+const struct efi_unicode_collation_protocol efi_unicode_collation_protocol2 = {
.stri_coll = efi_stri_coll,
.metai_match = efi_metai_match,
.str_lwr = efi_str_lwr,
@@ -327,3 +327,30 @@ const struct efi_unicode_collation_protocol efi_unicode_collation_protocol = {
.str_to_fat = efi_str_to_fat,
.supported_languages = "en",
};
+
+/*
+ * In EFI 1.10 a version of the Unicode collation protocol using ISO 639-2
+ * language codes existed. This protocol is not part of the UEFI specification
+ * any longer. Unfortunately it is required to run the UEFI Self Certification
+ * Test (SCT) II, version 2.6, 2017. So we implement it here for the sole
+ * purpose of running the SCT. It can be removed when a compliant SCT is
+ * available.
+ */
+#if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL)
+
+/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL */
+const efi_guid_t efi_guid_unicode_collation_protocol =
+ EFI_UNICODE_COLLATION_PROTOCOL_GUID;
+
+const struct efi_unicode_collation_protocol efi_unicode_collation_protocol = {
+ .stri_coll = efi_stri_coll,
+ .metai_match = efi_metai_match,
+ .str_lwr = efi_str_lwr,
+ .str_upr = efi_str_upr,
+ .fat_to_str = efi_fat_to_str,
+ .str_to_fat = efi_str_to_fat,
+ /* ISO 639-2 language code */
+ .supported_languages = "eng",
+};
+
+#endif
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index 37728c3c165..e56053194da 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -125,6 +125,8 @@ static const char *parse_attr(const char *str, u32 *attrp)
if ((s = prefix(str, "ro"))) {
attr |= READ_ONLY;
+ } else if ((s = prefix(str, "nv"))) {
+ attr |= EFI_VARIABLE_NON_VOLATILE;
} else if ((s = prefix(str, "boot"))) {
attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
} else if ((s = prefix(str, "run"))) {
@@ -202,8 +204,10 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
len /= 2;
*data_size = len;
- if (in_size < len)
- return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ if (in_size < len) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
if (!data)
return EFI_EXIT(EFI_INVALID_PARAMETER);
@@ -217,8 +221,10 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
*data_size = len;
- if (in_size < len)
- return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ if (in_size < len) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
if (!data)
return EFI_EXIT(EFI_INVALID_PARAMETER);
@@ -232,10 +238,11 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
return EFI_EXIT(EFI_DEVICE_ERROR);
}
+out:
if (attributes)
*attributes = attr & EFI_VARIABLE_MASK;
- return EFI_EXIT(EFI_SUCCESS);
+ return EFI_EXIT(ret);
}
static char *efi_variables_list;
@@ -422,7 +429,9 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
data_size, data);
- if (!variable_name || !vendor) {
+ /* TODO: implement APPEND_WRITE */
+ if (!variable_name || !vendor ||
+ (attributes & EFI_VARIABLE_APPEND_WRITE)) {
ret = EFI_INVALID_PARAMETER;
goto out;
}
@@ -444,15 +453,24 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
if (val) {
parse_attr(val, &attr);
+ /* We should not free val */
+ val = NULL;
if (attr & READ_ONLY) {
- /* We should not free val */
- val = NULL;
ret = EFI_WRITE_PROTECTED;
goto out;
}
+
+ /*
+ * attributes won't be changed
+ * TODO: take care of APPEND_WRITE once supported
+ */
+ if (attr != attributes) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
}
- val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
+ val = malloc(2 * data_size + strlen("{ro,run,boot,nv}(blob)") + 1);
if (!val) {
ret = EFI_OUT_OF_RESOURCES;
goto out;
@@ -464,12 +482,16 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
* store attributes
* TODO: several attributes are not supported
*/
- attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
+ attributes &= (EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS);
s += sprintf(s, "{");
while (attributes) {
u32 attr = 1 << (ffs(attributes) - 1);
- if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
+ if (attr == EFI_VARIABLE_NON_VOLATILE)
+ s += sprintf(s, "nv");
+ else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
s += sprintf(s, "boot");
else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
s += sprintf(s, "run");