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

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

DECLARE_GLOBAL_DATA_PTR;

/* This list contains all the EFI objects our payload has access to */
LIST_HEAD(efi_obj_list);

/*
 * 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 entry_count;

/* 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;
/* Low 32 bit */
#define EFI_LOW32(a) (a & 0xFFFFFFFFULL)
/* High 32 bit */
#define EFI_HIGH32(a) (a >> 32)

/*
 * 64bit division by 10 implemented as multiplication by 1 / 10
 *
 * Decimals of one tenth: 0x1 / 0xA = 0x0.19999...
 */
#define EFI_TENTH 0x199999999999999A
static u64 efi_div10(u64 a)
{
	u64 prod;
	u64 rem;
	u64 ret;

	ret  = EFI_HIGH32(a) * EFI_HIGH32(EFI_TENTH);
	prod = EFI_HIGH32(a) * EFI_LOW32(EFI_TENTH);
	rem  = EFI_LOW32(prod);
	ret += EFI_HIGH32(prod);
	prod = EFI_LOW32(a) * EFI_HIGH32(EFI_TENTH);
	rem += EFI_LOW32(prod);
	ret += EFI_HIGH32(prod);
	prod = EFI_LOW32(a) * EFI_LOW32(EFI_TENTH);
	rem += EFI_HIGH32(prod);
	ret += EFI_HIGH32(rem);
	/* Round to nearest integer */
	if (rem >= (1 << 31))
		++ret;
	return ret;
}

void efi_signal_event(struct efi_event *event)
{
	if (event->signaled)
		return;
	event->signaled = 1;
	if (event->type & EVT_NOTIFY_SIGNAL) {
		EFI_CALL(event->notify_function(event, event->notify_context));
static efi_status_t efi_unsupported(const char *funcname)
{
	debug("EFI: App called into unimplemented function %s\n", funcname);
	return EFI_EXIT(EFI_UNSUPPORTED);
}

static unsigned long EFIAPI efi_raise_tpl(UINTN new_tpl)
	EFI_ENTRY("0x%zx", new_tpl);
	return EFI_EXIT(0);
}

static void EFIAPI efi_restore_tpl(UINTN old_tpl)
	EFI_ENTRY("0x%zx", old_tpl);
	efi_unsupported(__func__);
static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
						  unsigned long pages,
						  uint64_t *memory)
{
	efi_status_t r;

	EFI_ENTRY("%d, %d, 0x%lx, %p", type, memory_type, pages, memory);
	r = efi_allocate_pages(type, memory_type, pages, memory);
	return EFI_EXIT(r);
}

static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory,
					      unsigned long pages)
{
	efi_status_t r;

	EFI_ENTRY("%"PRIx64", 0x%lx", memory, pages);
	r = efi_free_pages(memory, pages);
	return EFI_EXIT(r);
}

static efi_status_t EFIAPI efi_get_memory_map_ext(
					unsigned long *memory_map_size,
					struct efi_mem_desc *memory_map,
					unsigned long *map_key,
					unsigned long *descriptor_size,
					uint32_t *descriptor_version)
{
	efi_status_t r;

	EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map,
		  map_key, descriptor_size, descriptor_version);
	r = efi_get_memory_map(memory_map_size, memory_map, map_key,
			       descriptor_size, descriptor_version);
	return EFI_EXIT(r);
}

static efi_status_t EFIAPI efi_allocate_pool_ext(int pool_type,
						 unsigned long size,
						 void **buffer)
Loading
Loading full blame...