#include <linux/libfdt_env.h>
#include <u-boot/crc.h>
#include <bootm.h>
+#include <pe.h>
#include <watchdog.h>
DECLARE_GLOBAL_DATA_PTR;
/* 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;
/* 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;
}
/**
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);
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;
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;
}
/**
- * 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)
{
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;
}
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));
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);
}
/**
*/
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);
{
struct efi_object *efiobj;
+ if (!handle)
+ return NULL;
+
list_for_each_entry(efiobj, &efi_obj_list, link) {
if (efiobj == handle)
return efiobj;
}
-
return NULL;
}
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)
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(¬if->link, &event->handles);
+ event->event->is_signaled = false;
+ 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;
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);
++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;
&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(
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 ==
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);
}
/**
* 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);
}
/**
+ * 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.
{
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) {
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;
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;
efi_status_t ret;
struct efi_loaded_image *info = NULL;
struct efi_loaded_image_obj *obj = NULL;
+ struct efi_device_path *dp;
/* In case of EFI_OUT_OF_RESOURCES avoid illegal free by caller. */
*handle_ptr = NULL;
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);
if (device_path) {
info->device_handle = efi_dp_find_obj(device_path, NULL);
- /*
- * When asking for the device path interface, return
- * bootefi_device_path
- */
- ret = efi_add_protocol(&obj->header,
- &efi_guid_device_path, device_path);
- if (ret != EFI_SUCCESS)
+
+ dp = efi_dp_append(device_path, file_path);
+ if (!dp) {
+ ret = EFI_OUT_OF_RESOURCES;
goto failure;
+ }
+ } else {
+ dp = NULL;
}
+ ret = efi_add_protocol(&obj->header,
+ &efi_guid_loaded_image_device_path, dp);
+ if (ret != EFI_SUCCESS)
+ goto failure;
/*
* When asking for the loaded_image interface, just
if (ret != EFI_SUCCESS)
goto failure;
-#if CONFIG_IS_ENABLED(EFI_LOADER_HII)
- ret = efi_add_protocol(&obj->header,
- &efi_guid_hii_string_protocol,
- (void *)&efi_hii_string);
- if (ret != EFI_SUCCESS)
- goto failure;
-
- ret = efi_add_protocol(&obj->header,
- &efi_guid_hii_database_protocol,
- (void *)&efi_hii_database);
- if (ret != EFI_SUCCESS)
- goto failure;
-
- ret = efi_add_protocol(&obj->header,
- &efi_guid_hii_config_routing_protocol,
- (void *)&efi_hii_config_routing);
- if (ret != EFI_SUCCESS)
- goto failure;
-#endif
-
*info_ptr = info;
*handle_ptr = obj;
* @size: size of the loaded image
* Return: status code
*/
+static
efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
void **buffer, efi_uintn_t *size)
{
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_load_image(bool boot_policy,
- efi_handle_t parent_image,
- struct efi_device_path *file_path,
- void *source_buffer,
- efi_uintn_t source_size,
- efi_handle_t *image_handle)
+efi_status_t EFIAPI efi_load_image(bool boot_policy,
+ efi_handle_t parent_image,
+ struct efi_device_path *file_path,
+ void *source_buffer,
+ efi_uintn_t source_size,
+ efi_handle_t *image_handle)
{
struct efi_device_path *dp, *fp;
struct efi_loaded_image *info = NULL;
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;
}
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,
&source_size);
if (ret != EFI_SUCCESS)
goto error;
- /*
- * split file_path which contains both the device and
- * file parts:
- */
- efi_dp_split_file_path(file_path, &dp, &fp);
} else {
- /* In this case, file_path is the "device" path, i.e.
- * something like a HARDWARE_DEVICE:MEMORY_MAPPED
- */
+ if (!source_size) {
+ ret = EFI_LOAD_ERROR;
+ goto error;
+ }
dest_buffer = source_buffer;
- dp = file_path;
- fp = NULL;
}
+ /* split file_path which contains both the device and file parts */
+ efi_dp_split_file_path(file_path, &dp, &fp);
ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
if (ret == EFI_SUCCESS)
ret = efi_load_pe(*image_obj, dest_buffer, info);
}
/**
- * 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
- */
-static 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)
* 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)
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);
}
/**
*/
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);
}
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;
}
item->info.controller_handle == controller_handle) {
efi_delete_open_info(item);
r = EFI_SUCCESS;
- break;
}
}
out:
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);
}
/**
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;
}
/* 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;
}
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
+efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
(efi_handle_t *handle, ...)
{
EFI_ENTRY("%p", handle);
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;
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,
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);
}
/**
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;
}
/* 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 */
}
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);
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)) {
/*
* 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));
/*
}
/**
+ * 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
* 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;
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);
panic("EFI application exited");
out:
- return EFI_EXIT(EFI_INVALID_PARAMETER);
+ return EFI_EXIT(ret);
}
/**
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);
}