Skip to content
Snippets Groups Projects
efi_boottime.c 79.3 KiB
Newer Older
/*
 *  EFI application boot time services
 *
 *  Copyright (c) 2016 Alexander Graf
 *
 *  SPDX-License-Identifier:     GPL-2.0+
 */

#include <common.h>
#include <div64.h>
#include <efi_loader.h>
#include <environment.h>
#include <malloc.h>
#include <asm/global_data.h>
#include <u-boot/crc.h>
#include <bootm.h>
#include <inttypes.h>
#include <watchdog.h>

DECLARE_GLOBAL_DATA_PTR;

/* Task priority level */
static efi_uintn_t efi_tpl = TPL_APPLICATION;
/* This list contains all the EFI objects our payload has access to */
LIST_HEAD(efi_obj_list);

/* List of all events */
LIST_HEAD(efi_events);
/*
 * 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;

/*
 * EFI can pass arbitrary additional "tables" containing vendor specific
 * information to the payload. One such table is the FDT table which contains
 * a pointer to a flattened device tree blob.
 *
 * In most cases we want to pass an FDT to the payload, so reserve one slot of
 * config table space for it. The pointer gets populated by do_bootefi_exec().
 */
static struct efi_configuration_table __efi_runtime_data efi_conf_table[2];
#ifdef CONFIG_ARM
/*
 * The "gd" pointer lives in a register on ARM and AArch64 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 int nesting_level;
/* GUID of the device tree table */
const efi_guid_t efi_guid_fdt = EFI_FDT_GUID;
/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
const efi_guid_t efi_guid_driver_binding_protocol =
			EFI_DRIVER_BINDING_PROTOCOL_GUID;
/* event group ExitBootServices() invoked */
const efi_guid_t efi_guid_event_group_exit_boot_services =
			EFI_EVENT_GROUP_EXIT_BOOT_SERVICES;
/* event group SetVirtualAddressMap() invoked */
const efi_guid_t efi_guid_event_group_virtual_address_change =
			EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE;
/* event group memory map changed */
const efi_guid_t efi_guid_event_group_memory_map_change =
			EFI_EVENT_GROUP_MEMORY_MAP_CHANGE;
/* event group boot manager about to boot */
const efi_guid_t efi_guid_event_group_ready_to_boot =
			EFI_EVENT_GROUP_READY_TO_BOOT;
/* event group ResetSystem() invoked (before ExitBootServices) */
const efi_guid_t efi_guid_event_group_reset_system =
			EFI_EVENT_GROUP_RESET_SYSTEM;

static efi_status_t EFIAPI efi_disconnect_controller(
					efi_handle_t controller_handle,
					efi_handle_t driver_image_handle,
					efi_handle_t child_handle);
/* Called on every callback entry */
int __efi_entry_check(void)
{
	int ret = entry_count++ == 0;
#ifdef CONFIG_ARM
	assert(efi_gd);
	app_gd = gd;
	gd = efi_gd;
#endif
	return ret;
}

/* Called on every callback exit */
int __efi_exit_check(void)
{
	int ret = --entry_count == 0;
#ifdef CONFIG_ARM
	gd = app_gd;
#endif
	return ret;
}

/* Called from do_bootefi_exec() */
void efi_save_gd(void)
{
#ifdef CONFIG_ARM
	efi_gd = gd;
/*
 * Special case handler for error/abort that just forces things back
 * to u-boot world so we can dump out an abort msg, without any care
 * about returning back to UEFI world.
 */
void efi_restore_gd(void)
{
#ifdef CONFIG_ARM
	/* Only restore if we're already in EFI context */
	if (!efi_gd)
		return;
	gd = efi_gd;
 * Return a string for indenting with two spaces per level. A maximum of ten
 * indent levels is supported. Higher indent levels will be truncated.
 *
 * @level	indent level
 * @return	indent string
 */
static const char *indent_string(int level)
{
	const char *indent = "                    ";
	const int max = strlen(indent);
	level = min(max, level * 2);
	return &indent[max - level];
}

const char *__efi_nesting(void)
{
	return indent_string(nesting_level);
}

const char *__efi_nesting_inc(void)
{
	return indent_string(nesting_level++);
}

const char *__efi_nesting_dec(void)
{
	return indent_string(--nesting_level);
}

/*
 * Queue an EFI event.
 *
 * This function queues the notification function of the event for future
 * execution.
 *
 * The notification function is called if the task priority level of the
 * event is higher than the current task priority level.
 *
 * For the SignalEvent service see efi_signal_event_ext.
 *
 * @event	event to signal
 * @check_tpl	check the TPL level
static void efi_queue_event(struct efi_event *event, bool check_tpl)
		event->is_queued = true;
		if (check_tpl && efi_tpl >= event->notify_tpl)
		EFI_CALL_VOID(event->notify_function(event,
						     event->notify_context));
	event->is_queued = false;
/*
 * Signal an EFI event.
 *
 * This function signals an event. If the event belongs to an event group
 * all events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL
 * their notification function is queued.
 *
 * For the SignalEvent service see efi_signal_event_ext.
 *
Loading
Loading full blame...