*/
#include <common.h>
+#include <bootm.h>
#include <div64.h>
+#include <dm/device.h>
+#include <dm/root.h>
#include <efi_loader.h>
-#include <environment.h>
+#include <irq_func.h>
+#include <log.h>
#include <malloc.h>
-#include <linux/libfdt_env.h>
-#include <u-boot/crc.h>
-#include <bootm.h>
#include <pe.h>
+#include <time.h>
+#include <u-boot/crc.h>
+#include <usb.h>
#include <watchdog.h>
+#include <linux/libfdt_env.h>
DECLARE_GLOBAL_DATA_PTR;
LIST_HEAD(efi_obj_list);
/* List of all events */
-LIST_HEAD(efi_events);
+__efi_runtime_data LIST_HEAD(efi_events);
/* List of queued events */
LIST_HEAD(efi_event_queue);
/* Flag to disable timer activity in ExitBootServices() */
static bool timers_enabled = true;
+/* Flag used by the selftest to avoid detaching devices in ExitBootServices() */
+bool efi_st_keep_devices;
+
/* 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;
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
/*
- * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
- * we need to do trickery with caches. Since we don't want to break the EFI
- * aware boot path, only apply hacks when loading exiting directly (breaking
- * direct Linux EFI booting along the way - oh well).
- */
-static bool efi_is_direct_boot = true;
-
-#ifdef CONFIG_ARM
-/*
- * The "gd" pointer lives in a register on ARM and AArch64 that we declare
+ * The "gd" pointer lives in a register on ARM and RISC-V that we declare
* fixed when compiling U-Boot. However, the payload does not know about that
* restriction so we need to manually swap its and our view of that register on
* EFI callback entry/exit.
*/
-static volatile void *efi_gd, *app_gd;
+static volatile gd_t *efi_gd, *app_gd;
#endif
/* 1 if inside U-Boot code, 0 if inside EFI payload code */
int __efi_entry_check(void)
{
int ret = entry_count++ == 0;
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
assert(efi_gd);
app_gd = gd;
- gd = efi_gd;
+ set_gd(efi_gd);
#endif
return ret;
}
int __efi_exit_check(void)
{
int ret = --entry_count == 0;
-#ifdef CONFIG_ARM
- gd = app_gd;
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
+ set_gd(app_gd);
#endif
return ret;
}
-/* Called from do_bootefi_exec() */
+/**
+ * efi_save_gd() - save global data register
+ *
+ * On the ARM and RISC-V architectures gd is mapped to a fixed register.
+ * As this register may be overwritten by an EFI payload we save it here
+ * and restore it on every callback entered.
+ *
+ * This function is called after relocation from initr_reloc_global_data().
+ */
void efi_save_gd(void)
{
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
efi_gd = gd;
#endif
}
-/*
- * Special case handler for error/abort that just forces things back to u-boot
- * world so we can dump out an abort message, without any care about returning
- * back to UEFI world.
+/**
+ * efi_restore_gd() - restore global data register
+ *
+ * On the ARM and RISC-V architectures gd is mapped to a fixed register.
+ * Restore it after returning from the UEFI world to the value saved via
+ * efi_save_gd().
*/
void efi_restore_gd(void)
{
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
/* Only restore if we're already in EFI context */
if (!efi_gd)
return;
- gd = efi_gd;
+ set_gd(efi_gd);
#endif
}
*/
static void efi_queue_event(struct efi_event *event)
{
- struct efi_event *item = NULL;
+ struct efi_event *item;
if (!event->notify_function)
return;
/**
* efi_delete_handle() - delete handle
*
- * @obj: handle to delete
+ * @handle: handle to delete
*/
void efi_delete_handle(efi_handle_t handle)
{
/**
* efi_create_event() - create an event
+ *
* @type: type of the event to create
* @notify_tpl: task priority level of the event
* @notify_function: notification function of the event
struct efi_event **event)
{
struct efi_event *evt;
+ efi_status_t ret;
+ int pool_type;
if (event == NULL)
return EFI_INVALID_PARAMETER;
case EVT_NOTIFY_WAIT:
case EVT_TIMER | EVT_NOTIFY_WAIT:
case EVT_SIGNAL_EXIT_BOOT_SERVICES:
+ pool_type = EFI_BOOT_SERVICES_DATA;
+ break;
case EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:
+ pool_type = EFI_RUNTIME_SERVICES_DATA;
break;
default:
return EFI_INVALID_PARAMETER;
(!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS))
return EFI_INVALID_PARAMETER;
- evt = calloc(1, sizeof(struct efi_event));
- if (!evt)
- return EFI_OUT_OF_RESOURCES;
+ ret = efi_allocate_pool(pool_type, sizeof(struct efi_event),
+ (void **)&evt);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ memset(evt, 0, sizeof(struct efi_event));
evt->type = type;
evt->notify_tpl = notify_tpl;
evt->notify_function = notify_function;
list_del(&event->queue_link);
list_del(&event->link);
- free(event);
+ efi_free_pool(event);
return EFI_EXIT(EFI_SUCCESS);
}
}
item->event = event;
- memcpy(&item->protocol, protocol, sizeof(efi_guid_t));
+ guidcpy(&item->protocol, protocol);
INIT_LIST_HEAD(&item->handles);
list_add_tail(&item->link, &efi_register_notify_events);
/**
* efi_search() - determine if an EFI handle implements a protocol
+ *
* @search_type: selection criterion
* @protocol: GUID of the protocol
- * @search_key: registration key
* @handle: handle
*
* See the documentation of the LocateHandle service in the UEFI specification.
return EFI_OUT_OF_RESOURCES;
/* Add a new entry */
- memcpy(&systab.tables[i].guid, guid, sizeof(*guid));
+ guidcpy(&systab.tables[i].guid, guid);
systab.tables[i].table = table;
systab.nr_tables = i + 1;
* Initialize a loaded_image_info and loaded_image_info object with correct
* protocols, boot-device, etc.
*
- * In case of an error *handle_ptr and *info_ptr are set to NULL and an error
+ * In case of an error \*handle_ptr and \*info_ptr are set to NULL and an error
* code is returned.
*
* @device_path: device path of the loaded image
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 */
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);
+ ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
if (!source_buffer)
/* Release buffer to which file was loaded */
efi_free_pages((uintptr_t)dest_buffer,
efi_size_in_pages(source_size));
- if (ret == EFI_SUCCESS) {
+ if (ret == EFI_SUCCESS || ret == EFI_SECURITY_VIOLATION) {
info->system_table = &systab;
info->parent_handle = parent_image;
} else {
*/
static void efi_exit_caches(void)
{
-#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
+#if defined(CONFIG_EFI_GRUB_ARM32_WORKAROUND)
/*
- * Grub on 32bit ARM needs to have caches disabled before jumping into
- * a zImage, but does not know of all cache layers. Give it a hand.
+ * Boooting Linux via GRUB prior to version 2.04 fails on 32bit ARM if
+ * caches are enabled.
+ *
+ * TODO:
+ * According to the UEFI spec caches that can be managed via CP15
+ * operations should be enabled. Caches requiring platform information
+ * to manage should be disabled. This should not happen in
+ * ExitBootServices() but before invoking any UEFI binary is invoked.
+ *
+ * We want to keep the current workaround while GRUB prior to version
+ * 2.04 is still in use.
*/
- if (efi_is_direct_boot)
- cleanup_before_linux();
+ cleanup_before_linux();
#endif
}
static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
efi_uintn_t map_key)
{
- struct efi_event *evt;
+ struct efi_event *evt, *next_event;
efi_status_t ret = EFI_SUCCESS;
EFI_ENTRY("%p, %zx", image_handle, map_key);
/* Notify variable services */
efi_variables_boot_exit_notify();
- board_quiesce_devices();
+ /* Remove all events except EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
+ list_for_each_entry_safe(evt, next_event, &efi_events, link) {
+ if (evt->type != EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)
+ list_del(&evt->link);
+ }
+
+ if (!efi_st_keep_devices) {
+ if IS_ENABLED(CONFIG_USB_DEVICE)
+ udc_disconnect();
+ board_quiesce_devices();
+ dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+ }
/* Patch out unsupported runtime function */
efi_runtime_detach();
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
- const efi_guid_t *protocol,
- efi_handle_t agent_handle,
- efi_handle_t controller_handle)
+efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_handle_t agent_handle,
+ efi_handle_t controller_handle)
{
struct efi_handler *handler;
struct efi_open_protocol_info_item *item;
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_locate_handle_buffer(
+efi_status_t EFIAPI efi_locate_handle_buffer(
enum efi_locate_search_type search_type,
const efi_guid_t *protocol, void *search_key,
efi_uintn_t *no_handles, efi_handle_t **buffer)
EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+ if (!efi_search_obj(image_handle))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
/* Check parameters */
if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE)
return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (image_obj->auth_status != EFI_IMAGE_AUTH_PASSED)
+ return EFI_EXIT(EFI_SECURITY_VIOLATION);
+
ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
&info, NULL, NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL));
if (ret != EFI_SUCCESS)
return EFI_EXIT(EFI_INVALID_PARAMETER);
- efi_is_direct_boot = false;
-
image_obj->exit_data_size = exit_data_size;
image_obj->exit_data = exit_data;
* us to the current line. This implies that the second half
* of the EFI_CALL macro has not been executed.
*/
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
/*
* efi_exit() called efi_restore_gd(). We have to undo this
* otherwise __efi_entry_check() will put the wrong value into
* app_gd.
*/
- gd = app_gd;
+ set_gd(app_gd);
#endif
/*
* To get ready to call EFI_EXIT below we have to execute the
ret = EFI_CALL(image_obj->entry(image_handle, &systab));
/*
- * Usually UEFI applications call Exit() instead of returning.
- * But because the world doesn't consist of ponies and unicorns,
- * we're happy to emulate that behavior on behalf of a payload
- * that forgot.
+ * Control is returned from a started UEFI image either by calling
+ * Exit() (where exit data can be provided) or by simply returning from
+ * the entry point. In the latter case call Exit() on behalf of the
+ * image.
*/
return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
}
/**
* 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
+ * @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,
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
- const efi_guid_t *protocol,
- void **protocol_interface)
+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, efi_root,
NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
* the buffer will be too large. But that does not harm.
*/
*number_of_children = 0;
+ if (!count)
+ return EFI_SUCCESS;
*child_handle_buffer = calloc(count, sizeof(efi_handle_t));
if (!*child_handle_buffer)
return EFI_OUT_OF_RESOURCES;
efi_handle_t *child_handle_buffer = NULL;
size_t number_of_children = 0;
efi_status_t r;
- size_t stop_count = 0;
struct efi_object *efiobj;
+ bool sole_child;
EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
child_handle);
}
/* Create list of child handles */
+ r = efi_get_child_controllers(efiobj,
+ driver_image_handle,
+ &number_of_children,
+ &child_handle_buffer);
+ if (r != EFI_SUCCESS)
+ return r;
+ sole_child = (number_of_children == 1);
+
if (child_handle) {
number_of_children = 1;
+ free(child_handle_buffer);
child_handle_buffer = &child_handle;
- } else {
- efi_get_child_controllers(efiobj,
- driver_image_handle,
- &number_of_children,
- &child_handle_buffer);
}
/* Get the driver binding protocol */
(void **)&binding_protocol,
driver_image_handle, NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL));
- if (r != EFI_SUCCESS)
+ if (r != EFI_SUCCESS) {
+ r = EFI_INVALID_PARAMETER;
goto out;
+ }
/* Remove the children */
if (number_of_children) {
r = EFI_CALL(binding_protocol->stop(binding_protocol,
controller_handle,
number_of_children,
child_handle_buffer));
- if (r == EFI_SUCCESS)
- ++stop_count;
+ if (r != EFI_SUCCESS) {
+ r = EFI_DEVICE_ERROR;
+ goto out;
+ }
}
/* Remove the driver */
- if (!child_handle)
+ if (!child_handle || sole_child) {
r = EFI_CALL(binding_protocol->stop(binding_protocol,
controller_handle,
0, NULL));
- if (r == EFI_SUCCESS)
- ++stop_count;
+ if (r != EFI_SUCCESS) {
+ r = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ }
EFI_CALL(efi_close_protocol(driver_image_handle,
&efi_guid_driver_binding_protocol,
driver_image_handle, NULL));
-
- if (stop_count)
- r = EFI_SUCCESS;
- else
- r = EFI_NOT_FOUND;
+ r = EFI_SUCCESS;
out:
if (!child_handle)
free(child_handle_buffer);