GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
config EFI_LOADER
|
||||
bool "Support running UEFI applications"
|
||||
depends on OF_LIBFDT && ( \
|
||||
ARM && (SYS_CPU = arm1136 || \
|
||||
SYS_CPU = arm1176 || \
|
||||
SYS_CPU = armv7 || \
|
||||
SYS_CPU = armv8) || \
|
||||
X86 || RISCV || SANDBOX)
|
||||
# We need EFI_STUB_64BIT to be set on x86_64 with EFI_STUB
|
||||
depends on !EFI_STUB || !X86_64 || EFI_STUB_64BIT
|
||||
# We need EFI_STUB_32BIT to be set on x86_32 with EFI_STUB
|
||||
depends on !EFI_STUB || !X86 || X86_64 || EFI_STUB_32BIT
|
||||
default y if !ARM || SYS_CPU = armv7 || SYS_CPU = armv8
|
||||
select LIB_UUID
|
||||
select HAVE_BLOCK_DEVICE
|
||||
select REGEX
|
||||
imply CFB_CONSOLE_ANSI
|
||||
help
|
||||
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"
|
||||
default y
|
||||
help
|
||||
Select this option to enable correct handling of the capitalization of
|
||||
Unicode codepoints in the range 0x0000-0xffff. If this option is not
|
||||
set, only the the correct handling of the letters of the codepage
|
||||
used by the FAT file system is ensured.
|
||||
|
||||
config EFI_UNICODE_COLLATION_PROTOCOL
|
||||
bool "Deprecated version of the Unicode collation protocol"
|
||||
default n
|
||||
help
|
||||
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 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_PLATFORM_LANG_CODES
|
||||
string "Language codes supported by firmware"
|
||||
default "en-US"
|
||||
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.
|
||||
|
||||
config EFI_HAVE_RUNTIME_RESET
|
||||
# bool "Reset runtime service is available"
|
||||
bool
|
||||
default y
|
||||
depends on ARCH_BCM283X || FSL_LAYERSCAPE || PSCI_RESET || SYSRESET_X86
|
||||
|
||||
config EFI_GRUB_ARM32_WORKAROUND
|
||||
bool "Workaround for GRUB on 32bit ARM"
|
||||
default y
|
||||
depends on ARM && !ARM64
|
||||
help
|
||||
GRUB prior to version 2.04 requires U-Boot to disable caches. This
|
||||
workaround currently is also needed on systems with caches that
|
||||
cannot be managed via CP15.
|
||||
endif
|
||||
@@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2016 Alexander Graf
|
||||
#
|
||||
|
||||
# This file only gets included with CONFIG_EFI_LOADER set, so all
|
||||
# object inclusion implicitly depends on it
|
||||
|
||||
asflags-y += -DHOST_ARCH="$(HOST_ARCH)"
|
||||
ccflags-y += -DHOST_ARCH="$(HOST_ARCH)"
|
||||
|
||||
CFLAGS_efi_boottime.o += \
|
||||
-DFW_VERSION="0x$(VERSION)" \
|
||||
-DFW_PATCHLEVEL="0x$(PATCHLEVEL)"
|
||||
CFLAGS_helloworld.o := $(CFLAGS_EFI) -Os -ffreestanding
|
||||
CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI)
|
||||
|
||||
ifneq ($(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),)
|
||||
always += helloworld.efi
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
|
||||
obj-y += efi_bootmgr.o
|
||||
obj-y += efi_boottime.o
|
||||
obj-y += efi_console.o
|
||||
obj-y += efi_device_path.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-$(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-$(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
|
||||
obj-$(CONFIG_DM_VIDEO) += efi_gop.o
|
||||
obj-$(CONFIG_PARTITIONS) += efi_disk.o
|
||||
obj-$(CONFIG_NET) += efi_net.o
|
||||
obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
|
||||
obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI application ACPI tables support
|
||||
*
|
||||
* Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <asm/acpi_table.h>
|
||||
|
||||
static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID;
|
||||
|
||||
/*
|
||||
* Install the ACPI table as a configuration table.
|
||||
*
|
||||
* @return status code
|
||||
*/
|
||||
efi_status_t efi_acpi_register(void)
|
||||
{
|
||||
/* Map within the low 32 bits, to allow for 32bit ACPI tables */
|
||||
u64 acpi = U32_MAX;
|
||||
efi_status_t ret;
|
||||
|
||||
/* Reserve 64kiB page for ACPI */
|
||||
ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
|
||||
EFI_RUNTIME_SERVICES_DATA, 16, &acpi);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Generate ACPI tables - we know that efi_allocate_pages() returns
|
||||
* a 4k-aligned address, so it is safe to assume that
|
||||
* write_acpi_tables() will write the table at that address.
|
||||
*/
|
||||
assert(!(acpi & 0xf));
|
||||
write_acpi_tables(acpi);
|
||||
|
||||
/* And expose them to our EFI payload */
|
||||
return efi_install_configuration_table(&acpi_guid,
|
||||
(void *)(uintptr_t)acpi);
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI boot manager
|
||||
*
|
||||
* Copyright (c) 2017 Rob Clark
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <charset.h>
|
||||
#include <malloc.h>
|
||||
#include <efi_loader.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
static const struct efi_boot_services *bs;
|
||||
static const struct efi_runtime_services *rs;
|
||||
|
||||
/*
|
||||
* bootmgr implements the logic of trying to find a payload to boot
|
||||
* based on the BootOrder + BootXXXX variables, and then loading it.
|
||||
*
|
||||
* TODO detecting a special key held (f9?) and displaying a boot menu
|
||||
* like you would get on a PC would be clever.
|
||||
*
|
||||
* TODO if we had a way to write and persist variables after the OS
|
||||
* has started, we'd also want to check OsIndications to see if we
|
||||
* should do normal or recovery boot.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* efi_deserialize_load_option() - parse serialized data
|
||||
*
|
||||
* Parse serialized data describing a load option and transform it to the
|
||||
* efi_load_option structure.
|
||||
*
|
||||
* @lo: pointer to target
|
||||
* @data: serialized data
|
||||
*/
|
||||
void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data)
|
||||
{
|
||||
lo->attributes = get_unaligned_le32(data);
|
||||
data += sizeof(u32);
|
||||
|
||||
lo->file_path_length = get_unaligned_le16(data);
|
||||
data += sizeof(u16);
|
||||
|
||||
/* FIXME */
|
||||
lo->label = (u16 *)data;
|
||||
data += (u16_strlen(lo->label) + 1) * sizeof(u16);
|
||||
|
||||
/* FIXME */
|
||||
lo->file_path = (struct efi_device_path *)data;
|
||||
data += lo->file_path_length;
|
||||
|
||||
lo->optional_data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_serialize_load_option() - serialize load option
|
||||
*
|
||||
* Serialize efi_load_option structure into byte stream for BootXXXX.
|
||||
*
|
||||
* @data: buffer for serialized data
|
||||
* @lo: load option
|
||||
* Return: size of allocated buffer
|
||||
*/
|
||||
unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
|
||||
{
|
||||
unsigned long label_len;
|
||||
unsigned long size;
|
||||
u8 *p;
|
||||
|
||||
label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
|
||||
|
||||
/* total size */
|
||||
size = sizeof(lo->attributes);
|
||||
size += sizeof(lo->file_path_length);
|
||||
size += label_len;
|
||||
size += lo->file_path_length;
|
||||
if (lo->optional_data)
|
||||
size += (utf8_utf16_strlen((const char *)lo->optional_data)
|
||||
+ 1) * sizeof(u16);
|
||||
p = malloc(size);
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
/* copy data */
|
||||
*data = p;
|
||||
memcpy(p, &lo->attributes, sizeof(lo->attributes));
|
||||
p += sizeof(lo->attributes);
|
||||
|
||||
memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
|
||||
p += sizeof(lo->file_path_length);
|
||||
|
||||
memcpy(p, lo->label, label_len);
|
||||
p += label_len;
|
||||
|
||||
memcpy(p, lo->file_path, lo->file_path_length);
|
||||
p += lo->file_path_length;
|
||||
|
||||
if (lo->optional_data) {
|
||||
utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
|
||||
p += sizeof(u16); /* size of trailing \0 */
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_var() - get UEFI variable
|
||||
*
|
||||
* It is the caller's duty to free the returned buffer.
|
||||
*
|
||||
* @name: name of variable
|
||||
* @vendor: vendor GUID of variable
|
||||
* @size: size of allocated buffer
|
||||
* Return: buffer with variable data or NULL
|
||||
*/
|
||||
static void *get_var(u16 *name, const efi_guid_t *vendor,
|
||||
efi_uintn_t *size)
|
||||
{
|
||||
efi_guid_t *v = (efi_guid_t *)vendor;
|
||||
efi_status_t ret;
|
||||
void *buf = NULL;
|
||||
|
||||
*size = 0;
|
||||
EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
|
||||
if (ret == EFI_BUFFER_TOO_SMALL) {
|
||||
buf = malloc(*size);
|
||||
EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
|
||||
}
|
||||
|
||||
if (ret != EFI_SUCCESS) {
|
||||
free(buf);
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* try_load_entry() - try to load image for boot option
|
||||
*
|
||||
* Attempt to load load-option number 'n', returning device_path and file_path
|
||||
* if successful. This checks that the EFI_LOAD_OPTION is active (enabled)
|
||||
* and that the specified file to boot exists.
|
||||
*
|
||||
* @n: number of the boot option, e.g. 0x0a13 for Boot0A13
|
||||
* @handle: on return handle for the newly installed image
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t try_load_entry(u16 n, efi_handle_t *handle)
|
||||
{
|
||||
struct efi_load_option lo;
|
||||
u16 varname[] = L"Boot0000";
|
||||
u16 hexmap[] = L"0123456789ABCDEF";
|
||||
void *load_option;
|
||||
efi_uintn_t size;
|
||||
efi_status_t ret;
|
||||
|
||||
varname[4] = hexmap[(n & 0xf000) >> 12];
|
||||
varname[5] = hexmap[(n & 0x0f00) >> 8];
|
||||
varname[6] = hexmap[(n & 0x00f0) >> 4];
|
||||
varname[7] = hexmap[(n & 0x000f) >> 0];
|
||||
|
||||
load_option = get_var(varname, &efi_global_variable_guid, &size);
|
||||
if (!load_option)
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
efi_deserialize_load_option(&lo, load_option);
|
||||
|
||||
if (lo.attributes & LOAD_OPTION_ACTIVE) {
|
||||
u32 attributes;
|
||||
|
||||
debug("%s: trying to load \"%ls\" from %pD\n",
|
||||
__func__, lo.label, lo.file_path);
|
||||
|
||||
ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
|
||||
NULL, 0, handle));
|
||||
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;
|
||||
size = sizeof(n);
|
||||
ret = EFI_CALL(efi_set_variable(
|
||||
L"BootCurrent",
|
||||
(efi_guid_t *)&efi_global_variable_guid,
|
||||
attributes, size, &n));
|
||||
if (ret != EFI_SUCCESS) {
|
||||
if (EFI_CALL(efi_unload_image(*handle))
|
||||
!= EFI_SUCCESS)
|
||||
printf("Unloading image failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
printf("Booting: %ls\n", lo.label);
|
||||
} else {
|
||||
ret = EFI_LOAD_ERROR;
|
||||
}
|
||||
|
||||
error:
|
||||
free(load_option);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_bootmgr_load() - try to load from BootNext or BootOrder
|
||||
*
|
||||
* Attempt to load from BootNext or in the order specified by BootOrder
|
||||
* EFI variable, the available load-options, finding and returning
|
||||
* the first one that can be loaded successfully.
|
||||
*
|
||||
* @handle: on return handle for the newly installed image
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_bootmgr_load(efi_handle_t *handle)
|
||||
{
|
||||
u16 bootnext, *bootorder;
|
||||
efi_uintn_t size;
|
||||
int i, num;
|
||||
efi_status_t ret;
|
||||
|
||||
bs = systab.boottime;
|
||||
rs = systab.runtime;
|
||||
|
||||
/* BootNext */
|
||||
bootnext = 0;
|
||||
size = sizeof(bootnext);
|
||||
ret = EFI_CALL(efi_get_variable(L"BootNext",
|
||||
(efi_guid_t *)&efi_global_variable_guid,
|
||||
NULL, &size, &bootnext));
|
||||
if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
|
||||
/* BootNext does exist here */
|
||||
if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16))
|
||||
printf("BootNext must be 16-bit integer\n");
|
||||
|
||||
/* delete BootNext */
|
||||
ret = EFI_CALL(efi_set_variable(
|
||||
L"BootNext",
|
||||
(efi_guid_t *)&efi_global_variable_guid,
|
||||
EFI_VARIABLE_NON_VOLATILE, 0,
|
||||
&bootnext));
|
||||
|
||||
/* load BootNext */
|
||||
if (ret == EFI_SUCCESS) {
|
||||
if (size == sizeof(u16)) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/* BootOrder */
|
||||
bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
|
||||
if (!bootorder) {
|
||||
printf("BootOrder not defined\n");
|
||||
ret = EFI_NOT_FOUND;
|
||||
goto error;
|
||||
}
|
||||
|
||||
num = size / sizeof(uint16_t);
|
||||
for (i = 0; i < num; i++) {
|
||||
debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
|
||||
ret = try_load_entry(bootorder[i], handle);
|
||||
if (ret == EFI_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
free(bootorder);
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,369 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI device path interface
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
|
||||
#define MAC_OUTPUT_LEN 22
|
||||
#define UNKNOWN_OUTPUT_LEN 23
|
||||
|
||||
#define MAX_NODE_LEN 512
|
||||
#define MAX_PATH_LEN 1024
|
||||
|
||||
const efi_guid_t efi_guid_device_path_to_text_protocol =
|
||||
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
|
||||
|
||||
/**
|
||||
* efi_str_to_u16() - convert ASCII string to UTF-16
|
||||
*
|
||||
* A u16 buffer is allocated from pool. The ASCII string is copied to the u16
|
||||
* buffer.
|
||||
*
|
||||
* @str: ASCII string
|
||||
* Return: UTF-16 string. NULL if out of memory.
|
||||
*/
|
||||
static u16 *efi_str_to_u16(char *str)
|
||||
{
|
||||
efi_uintn_t len;
|
||||
u16 *out, *dst;
|
||||
efi_status_t ret;
|
||||
|
||||
len = sizeof(u16) * (utf8_utf16_strlen(str) + 1);
|
||||
ret = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, len, (void **)&out);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return NULL;
|
||||
dst = out;
|
||||
utf8_utf16_strcpy(&dst, str);
|
||||
return out;
|
||||
}
|
||||
|
||||
static char *dp_unknown(char *s, struct efi_device_path *dp)
|
||||
{
|
||||
s += sprintf(s, "UNKNOWN(%04x,%04x)", dp->type, dp->sub_type);
|
||||
return s;
|
||||
}
|
||||
|
||||
static char *dp_hardware(char *s, struct efi_device_path *dp)
|
||||
{
|
||||
switch (dp->sub_type) {
|
||||
case DEVICE_PATH_SUB_TYPE_MEMORY: {
|
||||
struct efi_device_path_memory *mdp =
|
||||
(struct efi_device_path_memory *)dp;
|
||||
s += sprintf(s, "MemoryMapped(0x%x,0x%llx,0x%llx)",
|
||||
mdp->memory_type,
|
||||
mdp->start_address,
|
||||
mdp->end_address);
|
||||
break;
|
||||
}
|
||||
case DEVICE_PATH_SUB_TYPE_VENDOR: {
|
||||
int i, n;
|
||||
struct efi_device_path_vendor *vdp =
|
||||
(struct efi_device_path_vendor *)dp;
|
||||
|
||||
s += sprintf(s, "VenHw(%pUl", &vdp->guid);
|
||||
n = (int)vdp->dp.length - sizeof(struct efi_device_path_vendor);
|
||||
if (n > 0) {
|
||||
s += sprintf(s, ",");
|
||||
for (i = 0; i < n; ++i)
|
||||
s += sprintf(s, "%02x", vdp->vendor_data[i]);
|
||||
}
|
||||
s += sprintf(s, ")");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
s = dp_unknown(s, dp);
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static char *dp_acpi(char *s, struct efi_device_path *dp)
|
||||
{
|
||||
switch (dp->sub_type) {
|
||||
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,%d)", EISA_PNP_NUM(adp->hid),
|
||||
adp->uid);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
s = dp_unknown(s, dp);
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static char *dp_msging(char *s, struct efi_device_path *dp)
|
||||
{
|
||||
switch (dp->sub_type) {
|
||||
case DEVICE_PATH_SUB_TYPE_MSG_ATAPI: {
|
||||
struct efi_device_path_atapi *ide =
|
||||
(struct efi_device_path_atapi *)dp;
|
||||
s += sprintf(s, "Ata(%d,%d,%d)", ide->primary_secondary,
|
||||
ide->slave_master, ide->logical_unit_number);
|
||||
break;
|
||||
}
|
||||
case DEVICE_PATH_SUB_TYPE_MSG_SCSI: {
|
||||
struct efi_device_path_scsi *ide =
|
||||
(struct efi_device_path_scsi *)dp;
|
||||
s += sprintf(s, "Scsi(%u,%u)", ide->target_id,
|
||||
ide->logical_unit_number);
|
||||
break;
|
||||
}
|
||||
case DEVICE_PATH_SUB_TYPE_MSG_USB: {
|
||||
struct efi_device_path_usb *udp =
|
||||
(struct efi_device_path_usb *)dp;
|
||||
s += sprintf(s, "USB(0x%x,0x%x)", udp->parent_port_number,
|
||||
udp->usb_interface);
|
||||
break;
|
||||
}
|
||||
case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: {
|
||||
int i, n = sizeof(struct efi_mac_addr);
|
||||
struct efi_device_path_mac_addr *mdp =
|
||||
(struct efi_device_path_mac_addr *)dp;
|
||||
|
||||
if (mdp->if_type <= 1)
|
||||
n = 6;
|
||||
s += sprintf(s, "MAC(");
|
||||
for (i = 0; i < n; ++i)
|
||||
s += sprintf(s, "%02x", mdp->mac.addr[i]);
|
||||
s += sprintf(s, ",%u)", mdp->if_type);
|
||||
|
||||
break;
|
||||
}
|
||||
case DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS: {
|
||||
struct efi_device_path_usb_class *ucdp =
|
||||
(struct efi_device_path_usb_class *)dp;
|
||||
|
||||
s += sprintf(s, "UsbClass(0x%x,0x%x,0x%x,0x%x,0x%x)",
|
||||
ucdp->vendor_id, ucdp->product_id,
|
||||
ucdp->device_class, ucdp->device_subclass,
|
||||
ucdp->device_protocol);
|
||||
|
||||
break;
|
||||
}
|
||||
case DEVICE_PATH_SUB_TYPE_MSG_NVME: {
|
||||
struct efi_device_path_nvme *ndp =
|
||||
(struct efi_device_path_nvme *)dp;
|
||||
u32 ns_id;
|
||||
int i;
|
||||
|
||||
memcpy(&ns_id, &ndp->ns_id, sizeof(ns_id));
|
||||
s += sprintf(s, "NVMe(0x%x,", ns_id);
|
||||
for (i = 0; i < sizeof(ndp->eui64); ++i)
|
||||
s += sprintf(s, "%s%02x", i ? "-" : "",
|
||||
ndp->eui64[i]);
|
||||
s += sprintf(s, ")");
|
||||
|
||||
break;
|
||||
}
|
||||
case DEVICE_PATH_SUB_TYPE_MSG_SD:
|
||||
case DEVICE_PATH_SUB_TYPE_MSG_MMC: {
|
||||
const char *typename =
|
||||
(dp->sub_type == DEVICE_PATH_SUB_TYPE_MSG_SD) ?
|
||||
"SD" : "eMMC";
|
||||
struct efi_device_path_sd_mmc_path *sddp =
|
||||
(struct efi_device_path_sd_mmc_path *)dp;
|
||||
s += sprintf(s, "%s(%u)", typename, sddp->slot_number);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
s = dp_unknown(s, dp);
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a media device path node to text.
|
||||
*
|
||||
* @s output buffer
|
||||
* @dp device path node
|
||||
* @return next unused buffer address
|
||||
*/
|
||||
static char *dp_media(char *s, struct efi_device_path *dp)
|
||||
{
|
||||
switch (dp->sub_type) {
|
||||
case DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH: {
|
||||
struct efi_device_path_hard_drive_path *hddp =
|
||||
(struct efi_device_path_hard_drive_path *)dp;
|
||||
void *sig = hddp->partition_signature;
|
||||
u64 start;
|
||||
u64 end;
|
||||
|
||||
/* Copy from packed structure to aligned memory */
|
||||
memcpy(&start, &hddp->partition_start, sizeof(start));
|
||||
memcpy(&end, &hddp->partition_end, sizeof(end));
|
||||
|
||||
switch (hddp->signature_type) {
|
||||
case SIG_TYPE_MBR: {
|
||||
u32 signature;
|
||||
|
||||
memcpy(&signature, sig, sizeof(signature));
|
||||
s += sprintf(
|
||||
s, "HD(%d,MBR,0x%08x,0x%llx,0x%llx)",
|
||||
hddp->partition_number, signature, start, end);
|
||||
break;
|
||||
}
|
||||
case SIG_TYPE_GUID:
|
||||
s += sprintf(
|
||||
s, "HD(%d,GPT,%pUl,0x%llx,0x%llx)",
|
||||
hddp->partition_number, sig, start, end);
|
||||
break;
|
||||
default:
|
||||
s += sprintf(
|
||||
s, "HD(%d,0x%02x,0,0x%llx,0x%llx)",
|
||||
hddp->partition_number, hddp->partmap_type,
|
||||
start, end);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DEVICE_PATH_SUB_TYPE_CDROM_PATH: {
|
||||
struct efi_device_path_cdrom_path *cddp =
|
||||
(struct efi_device_path_cdrom_path *)dp;
|
||||
s += sprintf(s, "CDROM(%u,0x%llx,0x%llx)", cddp->boot_entry,
|
||||
cddp->partition_start, cddp->partition_size);
|
||||
break;
|
||||
}
|
||||
case DEVICE_PATH_SUB_TYPE_FILE_PATH: {
|
||||
struct efi_device_path_file_path *fp =
|
||||
(struct efi_device_path_file_path *)dp;
|
||||
int slen = (dp->length - sizeof(*dp)) / 2;
|
||||
if (slen > MAX_NODE_LEN - 2)
|
||||
slen = MAX_NODE_LEN - 2;
|
||||
s += sprintf(s, "%-.*ls", slen, fp->str);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
s = dp_unknown(s, dp);
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a single node to a char string.
|
||||
*
|
||||
* @buffer output buffer
|
||||
* @dp device path or node
|
||||
* @return end of string
|
||||
*/
|
||||
static char *efi_convert_single_device_node_to_text(
|
||||
char *buffer,
|
||||
struct efi_device_path *dp)
|
||||
{
|
||||
char *str = buffer;
|
||||
|
||||
switch (dp->type) {
|
||||
case DEVICE_PATH_TYPE_HARDWARE_DEVICE:
|
||||
str = dp_hardware(str, dp);
|
||||
break;
|
||||
case DEVICE_PATH_TYPE_ACPI_DEVICE:
|
||||
str = dp_acpi(str, dp);
|
||||
break;
|
||||
case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
|
||||
str = dp_msging(str, dp);
|
||||
break;
|
||||
case DEVICE_PATH_TYPE_MEDIA_DEVICE:
|
||||
str = dp_media(str, dp);
|
||||
break;
|
||||
case DEVICE_PATH_TYPE_END:
|
||||
break;
|
||||
default:
|
||||
str = dp_unknown(str, dp);
|
||||
}
|
||||
|
||||
*str = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function implements the ConvertDeviceNodeToText service of the
|
||||
* EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* device_node device node to be converted
|
||||
* display_only true if the shorter text representation shall be used
|
||||
* allow_shortcuts true if shortcut forms may be used
|
||||
* @return text representation of the device path
|
||||
* NULL if out of memory of device_path is NULL
|
||||
*/
|
||||
static uint16_t EFIAPI *efi_convert_device_node_to_text(
|
||||
struct efi_device_path *device_node,
|
||||
bool display_only,
|
||||
bool allow_shortcuts)
|
||||
{
|
||||
char str[MAX_NODE_LEN];
|
||||
uint16_t *text = NULL;
|
||||
|
||||
EFI_ENTRY("%p, %d, %d", device_node, display_only, allow_shortcuts);
|
||||
|
||||
if (!device_node)
|
||||
goto out;
|
||||
efi_convert_single_device_node_to_text(str, device_node);
|
||||
|
||||
text = efi_str_to_u16(str);
|
||||
|
||||
out:
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
return text;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function implements the ConvertDevicePathToText service of the
|
||||
* EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* device_path device path to be converted
|
||||
* display_only true if the shorter text representation shall be used
|
||||
* allow_shortcuts true if shortcut forms may be used
|
||||
* @return text representation of the device path
|
||||
* NULL if out of memory of device_path is NULL
|
||||
*/
|
||||
static uint16_t EFIAPI *efi_convert_device_path_to_text(
|
||||
struct efi_device_path *device_path,
|
||||
bool display_only,
|
||||
bool allow_shortcuts)
|
||||
{
|
||||
uint16_t *text = NULL;
|
||||
char buffer[MAX_PATH_LEN];
|
||||
char *str = buffer;
|
||||
|
||||
EFI_ENTRY("%p, %d, %d", device_path, display_only, allow_shortcuts);
|
||||
|
||||
if (!device_path)
|
||||
goto out;
|
||||
while (device_path &&
|
||||
str + MAX_NODE_LEN < buffer + MAX_PATH_LEN) {
|
||||
*str++ = '/';
|
||||
str = efi_convert_single_device_node_to_text(str, device_path);
|
||||
device_path = efi_dp_next(device_path);
|
||||
}
|
||||
|
||||
text = efi_str_to_u16(buffer);
|
||||
|
||||
out:
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
return text;
|
||||
}
|
||||
|
||||
/* helper for debug prints.. efi_free_pool() the result. */
|
||||
uint16_t *efi_dp_str(struct efi_device_path *dp)
|
||||
{
|
||||
return EFI_CALL(efi_convert_device_path_to_text(dp, true, true));
|
||||
}
|
||||
|
||||
const struct efi_device_path_to_text_protocol efi_device_path_to_text = {
|
||||
.convert_device_node_to_text = efi_convert_device_node_to_text,
|
||||
.convert_device_path_to_text = efi_convert_device_path_to_text,
|
||||
};
|
||||
@@ -0,0 +1,199 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI device path interface
|
||||
*
|
||||
* Copyright (c) 2017 Leif Lindholm
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
|
||||
const efi_guid_t efi_guid_device_path_utilities_protocol =
|
||||
EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
|
||||
|
||||
/*
|
||||
* Get size of a device path.
|
||||
*
|
||||
* This function implements the GetDevicePathSize service of the device path
|
||||
* utilities protocol. The device path length includes the end of path tag
|
||||
* which may be an instance end.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* @device_path device path
|
||||
* @return size in bytes
|
||||
*/
|
||||
static efi_uintn_t EFIAPI get_device_path_size(
|
||||
const struct efi_device_path *device_path)
|
||||
{
|
||||
efi_uintn_t sz = 0;
|
||||
|
||||
EFI_ENTRY("%pD", device_path);
|
||||
/* size includes the END node: */
|
||||
if (device_path)
|
||||
sz = efi_dp_size(device_path) + sizeof(struct efi_device_path);
|
||||
return EFI_EXIT(sz);
|
||||
}
|
||||
|
||||
/*
|
||||
* Duplicate a device path.
|
||||
*
|
||||
* This function implements the DuplicateDevicePath service of the device path
|
||||
* utilities protocol.
|
||||
*
|
||||
* The UEFI spec does not indicate what happens to the end tag. We follow the
|
||||
* EDK2 logic: In case the device path ends with an end of instance tag, the
|
||||
* copy will also end with an end of instance tag.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* @device_path device path
|
||||
* @return copy of the device path
|
||||
*/
|
||||
static struct efi_device_path * EFIAPI duplicate_device_path(
|
||||
const struct efi_device_path *device_path)
|
||||
{
|
||||
EFI_ENTRY("%pD", device_path);
|
||||
return EFI_EXIT(efi_dp_dup(device_path));
|
||||
}
|
||||
|
||||
/*
|
||||
* Append device path.
|
||||
*
|
||||
* This function implements the AppendDevicePath service of the device path
|
||||
* utilities protocol.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* @src1 1st device path
|
||||
* @src2 2nd device path
|
||||
* @return concatenated device path
|
||||
*/
|
||||
static struct efi_device_path * EFIAPI append_device_path(
|
||||
const struct efi_device_path *src1,
|
||||
const struct efi_device_path *src2)
|
||||
{
|
||||
EFI_ENTRY("%pD, %pD", src1, src2);
|
||||
return EFI_EXIT(efi_dp_append(src1, src2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Append device path node.
|
||||
*
|
||||
* This function implements the AppendDeviceNode service of the device path
|
||||
* utilities protocol.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* @device_path device path
|
||||
* @device_node device node
|
||||
* @return concatenated device path
|
||||
*/
|
||||
static struct efi_device_path * EFIAPI append_device_node(
|
||||
const struct efi_device_path *device_path,
|
||||
const struct efi_device_path *device_node)
|
||||
{
|
||||
EFI_ENTRY("%pD, %p", device_path, device_node);
|
||||
return EFI_EXIT(efi_dp_append_node(device_path, device_node));
|
||||
}
|
||||
|
||||
/*
|
||||
* Append device path instance.
|
||||
*
|
||||
* This function implements the AppendDevicePathInstance service of the device
|
||||
* path utilities protocol.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* @device_path 1st device path
|
||||
* @device_path_instance 2nd device path
|
||||
* @return concatenated device path
|
||||
*/
|
||||
static struct efi_device_path * EFIAPI append_device_path_instance(
|
||||
const struct efi_device_path *device_path,
|
||||
const struct efi_device_path *device_path_instance)
|
||||
{
|
||||
EFI_ENTRY("%pD, %pD", device_path, device_path_instance);
|
||||
return EFI_EXIT(efi_dp_append_instance(device_path,
|
||||
device_path_instance));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get next device path instance.
|
||||
*
|
||||
* This function implements the GetNextDevicePathInstance service of the device
|
||||
* path utilities protocol.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* @device_path_instance next device path instance
|
||||
* @device_path_instance_size size of the device path instance
|
||||
* @return concatenated device path
|
||||
*/
|
||||
static struct efi_device_path * EFIAPI get_next_device_path_instance(
|
||||
struct efi_device_path **device_path_instance,
|
||||
efi_uintn_t *device_path_instance_size)
|
||||
{
|
||||
EFI_ENTRY("%pD, %p", device_path_instance, device_path_instance_size);
|
||||
return EFI_EXIT(efi_dp_get_next_instance(device_path_instance,
|
||||
device_path_instance_size));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a device path contains more than one instance.
|
||||
*
|
||||
* This function implements the AppendDeviceNode service of the device path
|
||||
* utilities protocol.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* @device_path device path
|
||||
* @device_node device node
|
||||
* @return concatenated device path
|
||||
*/
|
||||
static bool EFIAPI is_device_path_multi_instance(
|
||||
const struct efi_device_path *device_path)
|
||||
{
|
||||
EFI_ENTRY("%pD", device_path);
|
||||
return EFI_EXIT(efi_dp_is_multi_instance(device_path));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create device node.
|
||||
*
|
||||
* This function implements the CreateDeviceNode service of the device path
|
||||
* utilities protocol.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* @node_type node type
|
||||
* @node_sub_type node sub type
|
||||
* @node_length node length
|
||||
* @return device path node
|
||||
*/
|
||||
static struct efi_device_path * EFIAPI create_device_node(
|
||||
uint8_t node_type, uint8_t node_sub_type, uint16_t node_length)
|
||||
{
|
||||
EFI_ENTRY("%u, %u, %u", node_type, node_sub_type, node_length);
|
||||
return EFI_EXIT(efi_dp_create_device_node(node_type, node_sub_type,
|
||||
node_length));
|
||||
}
|
||||
|
||||
const struct efi_device_path_utilities_protocol efi_device_path_utilities = {
|
||||
.get_device_path_size = get_device_path_size,
|
||||
.duplicate_device_path = duplicate_device_path,
|
||||
.append_device_path = append_device_path,
|
||||
.append_device_node = append_device_node,
|
||||
.append_device_path_instance = append_device_path_instance,
|
||||
.get_next_device_path_instance = get_next_device_path_instance,
|
||||
.is_device_path_multi_instance = is_device_path_multi_instance,
|
||||
.create_device_node = create_device_node,
|
||||
};
|
||||
@@ -0,0 +1,516 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI application disk support
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <blk.h>
|
||||
#include <dm.h>
|
||||
#include <efi_loader.h>
|
||||
#include <fs.h>
|
||||
#include <part.h>
|
||||
#include <malloc.h>
|
||||
|
||||
const efi_guid_t efi_block_io_guid = EFI_BLOCK_IO_PROTOCOL_GUID;
|
||||
|
||||
/**
|
||||
* struct efi_disk_obj - EFI disk object
|
||||
*
|
||||
* @header: EFI object header
|
||||
* @ops: EFI disk I/O protocol interface
|
||||
* @ifname: interface name for block device
|
||||
* @dev_index: device index of block device
|
||||
* @media: block I/O media information
|
||||
* @dp: device path to the block device
|
||||
* @part: partition
|
||||
* @volume: simple file system protocol of the partition
|
||||
* @offset: offset into disk for simple partition
|
||||
* @desc: internal block device descriptor
|
||||
*/
|
||||
struct efi_disk_obj {
|
||||
struct efi_object header;
|
||||
struct efi_block_io ops;
|
||||
const char *ifname;
|
||||
int dev_index;
|
||||
struct efi_block_io_media media;
|
||||
struct efi_device_path *dp;
|
||||
unsigned int part;
|
||||
struct efi_simple_file_system_protocol *volume;
|
||||
lbaint_t offset;
|
||||
struct blk_desc *desc;
|
||||
};
|
||||
|
||||
/**
|
||||
* efi_disk_reset() - reset block device
|
||||
*
|
||||
* This function implements the Reset service of the EFI_BLOCK_IO_PROTOCOL.
|
||||
*
|
||||
* As U-Boot's block devices do not have a reset function simply return
|
||||
* EFI_SUCCESS.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @this: pointer to the BLOCK_IO_PROTOCOL
|
||||
* @extended_verification: extended verification
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this,
|
||||
char extended_verification)
|
||||
{
|
||||
EFI_ENTRY("%p, %x", this, extended_verification);
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
enum efi_disk_direction {
|
||||
EFI_DISK_READ,
|
||||
EFI_DISK_WRITE,
|
||||
};
|
||||
|
||||
static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
|
||||
u32 media_id, u64 lba, unsigned long buffer_size,
|
||||
void *buffer, enum efi_disk_direction direction)
|
||||
{
|
||||
struct efi_disk_obj *diskobj;
|
||||
struct blk_desc *desc;
|
||||
int blksz;
|
||||
int blocks;
|
||||
unsigned long n;
|
||||
|
||||
diskobj = container_of(this, struct efi_disk_obj, ops);
|
||||
desc = (struct blk_desc *) diskobj->desc;
|
||||
blksz = desc->blksz;
|
||||
blocks = buffer_size / blksz;
|
||||
lba += diskobj->offset;
|
||||
|
||||
EFI_PRINT("blocks=%x lba=%llx blksz=%x dir=%d\n",
|
||||
blocks, lba, blksz, direction);
|
||||
|
||||
/* We only support full block access */
|
||||
if (buffer_size & (blksz - 1))
|
||||
return EFI_BAD_BUFFER_SIZE;
|
||||
|
||||
if (direction == EFI_DISK_READ)
|
||||
n = blk_dread(desc, lba, blocks, buffer);
|
||||
else
|
||||
n = blk_dwrite(desc, lba, blocks, buffer);
|
||||
|
||||
/* We don't do interrupts, so check for timers cooperatively */
|
||||
efi_timer_check();
|
||||
|
||||
EFI_PRINT("n=%lx blocks=%x\n", n, blocks);
|
||||
|
||||
if (n != blocks)
|
||||
return EFI_DEVICE_ERROR;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this,
|
||||
u32 media_id, u64 lba, efi_uintn_t buffer_size,
|
||||
void *buffer)
|
||||
{
|
||||
void *real_buffer = buffer;
|
||||
efi_status_t r;
|
||||
|
||||
if (!this)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
/* TODO: check for media changes */
|
||||
if (media_id != this->media->media_id)
|
||||
return EFI_MEDIA_CHANGED;
|
||||
if (!this->media->media_present)
|
||||
return EFI_NO_MEDIA;
|
||||
/* media->io_align is a power of 2 */
|
||||
if ((uintptr_t)buffer & (this->media->io_align - 1))
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (lba * this->media->block_size + buffer_size >
|
||||
this->media->last_block * this->media->block_size)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
|
||||
if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
|
||||
r = efi_disk_read_blocks(this, media_id, lba,
|
||||
EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
|
||||
if (r != EFI_SUCCESS)
|
||||
return r;
|
||||
return efi_disk_read_blocks(this, media_id, lba +
|
||||
EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
|
||||
buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
|
||||
buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
real_buffer = efi_bounce_buffer;
|
||||
#endif
|
||||
|
||||
EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
|
||||
buffer_size, buffer);
|
||||
|
||||
r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
|
||||
EFI_DISK_READ);
|
||||
|
||||
/* Copy from bounce buffer to real buffer if necessary */
|
||||
if ((r == EFI_SUCCESS) && (real_buffer != buffer))
|
||||
memcpy(buffer, real_buffer, buffer_size);
|
||||
|
||||
return EFI_EXIT(r);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this,
|
||||
u32 media_id, u64 lba, efi_uintn_t buffer_size,
|
||||
void *buffer)
|
||||
{
|
||||
void *real_buffer = buffer;
|
||||
efi_status_t r;
|
||||
|
||||
if (!this)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (this->media->read_only)
|
||||
return EFI_WRITE_PROTECTED;
|
||||
/* TODO: check for media changes */
|
||||
if (media_id != this->media->media_id)
|
||||
return EFI_MEDIA_CHANGED;
|
||||
if (!this->media->media_present)
|
||||
return EFI_NO_MEDIA;
|
||||
/* media->io_align is a power of 2 */
|
||||
if ((uintptr_t)buffer & (this->media->io_align - 1))
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (lba * this->media->block_size + buffer_size >
|
||||
this->media->last_block * this->media->block_size)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
|
||||
if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
|
||||
r = efi_disk_write_blocks(this, media_id, lba,
|
||||
EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
|
||||
if (r != EFI_SUCCESS)
|
||||
return r;
|
||||
return efi_disk_write_blocks(this, media_id, lba +
|
||||
EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
|
||||
buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
|
||||
buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
real_buffer = efi_bounce_buffer;
|
||||
#endif
|
||||
|
||||
EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
|
||||
buffer_size, buffer);
|
||||
|
||||
/* Populate bounce buffer if necessary */
|
||||
if (real_buffer != buffer)
|
||||
memcpy(real_buffer, buffer, buffer_size);
|
||||
|
||||
r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
|
||||
EFI_DISK_WRITE);
|
||||
|
||||
return EFI_EXIT(r);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this)
|
||||
{
|
||||
/* We always write synchronously */
|
||||
EFI_ENTRY("%p", this);
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static const struct efi_block_io block_io_disk_template = {
|
||||
.reset = &efi_disk_reset,
|
||||
.read_blocks = &efi_disk_read_blocks,
|
||||
.write_blocks = &efi_disk_write_blocks,
|
||||
.flush_blocks = &efi_disk_flush_blocks,
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the simple file system protocol for a file device path.
|
||||
*
|
||||
* The full path provided is split into device part and into a file
|
||||
* part. The device part is used to find the handle on which the
|
||||
* simple file system protocol is installed.
|
||||
*
|
||||
* @full_path device path including device and file
|
||||
* @return simple file system protocol
|
||||
*/
|
||||
struct efi_simple_file_system_protocol *
|
||||
efi_fs_from_path(struct efi_device_path *full_path)
|
||||
{
|
||||
struct efi_object *efiobj;
|
||||
struct efi_handler *handler;
|
||||
struct efi_device_path *device_path;
|
||||
struct efi_device_path *file_path;
|
||||
efi_status_t ret;
|
||||
|
||||
/* Split the path into a device part and a file part */
|
||||
ret = efi_dp_split_file_path(full_path, &device_path, &file_path);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return NULL;
|
||||
efi_free_pool(file_path);
|
||||
|
||||
/* Get the EFI object for the partition */
|
||||
efiobj = efi_dp_find_obj(device_path, NULL);
|
||||
efi_free_pool(device_path);
|
||||
if (!efiobj)
|
||||
return NULL;
|
||||
|
||||
/* Find the simple file system protocol */
|
||||
ret = efi_search_protocol(efiobj, &efi_simple_file_system_protocol_guid,
|
||||
&handler);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return NULL;
|
||||
|
||||
/* Return the simple file system protocol for the partition */
|
||||
return handler->protocol_interface;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_fs_exists() - check if a partition bears a file system
|
||||
*
|
||||
* @desc: block device descriptor
|
||||
* @part: partition number
|
||||
* Return: 1 if a file system exists on the partition
|
||||
* 0 otherwise
|
||||
*/
|
||||
static int efi_fs_exists(struct blk_desc *desc, int part)
|
||||
{
|
||||
if (fs_set_blk_dev_with_part(desc, part))
|
||||
return 0;
|
||||
|
||||
if (fs_get_type() == FS_TYPE_ANY)
|
||||
return 0;
|
||||
|
||||
fs_close();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a handle for a partition or disk
|
||||
*
|
||||
* @parent parent handle
|
||||
* @dp_parent parent device path
|
||||
* @if_typename interface name for block device
|
||||
* @desc internal block device
|
||||
* @dev_index device index for block device
|
||||
* @offset offset into disk for simple partitions
|
||||
* @return disk object
|
||||
*/
|
||||
static efi_status_t efi_disk_add_dev(
|
||||
efi_handle_t parent,
|
||||
struct efi_device_path *dp_parent,
|
||||
const char *if_typename,
|
||||
struct blk_desc *desc,
|
||||
int dev_index,
|
||||
lbaint_t offset,
|
||||
unsigned int part,
|
||||
struct efi_disk_obj **disk)
|
||||
{
|
||||
struct efi_disk_obj *diskobj;
|
||||
efi_status_t ret;
|
||||
|
||||
/* Don't add empty devices */
|
||||
if (!desc->lba)
|
||||
return EFI_NOT_READY;
|
||||
|
||||
diskobj = calloc(1, sizeof(*diskobj));
|
||||
if (!diskobj)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
/* Hook up to the device list */
|
||||
efi_add_handle(&diskobj->header);
|
||||
|
||||
/* Fill in object data */
|
||||
if (part) {
|
||||
struct efi_device_path *node = efi_dp_part_node(desc, part);
|
||||
|
||||
diskobj->dp = efi_dp_append_node(dp_parent, node);
|
||||
efi_free_pool(node);
|
||||
} else {
|
||||
diskobj->dp = efi_dp_from_part(desc, part);
|
||||
}
|
||||
diskobj->part = part;
|
||||
ret = efi_add_protocol(&diskobj->header, &efi_block_io_guid,
|
||||
&diskobj->ops);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
ret = efi_add_protocol(&diskobj->header, &efi_guid_device_path,
|
||||
diskobj->dp);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
/* partitions or whole disk without partitions */
|
||||
if ((part || desc->part_type == PART_TYPE_UNKNOWN) &&
|
||||
efi_fs_exists(desc, part)) {
|
||||
diskobj->volume = efi_simple_file_system(desc, part,
|
||||
diskobj->dp);
|
||||
ret = efi_add_protocol(&diskobj->header,
|
||||
&efi_simple_file_system_protocol_guid,
|
||||
diskobj->volume);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
}
|
||||
diskobj->ops = block_io_disk_template;
|
||||
diskobj->ifname = if_typename;
|
||||
diskobj->dev_index = dev_index;
|
||||
diskobj->offset = offset;
|
||||
diskobj->desc = desc;
|
||||
|
||||
/* Fill in EFI IO Media info (for read/write callbacks) */
|
||||
diskobj->media.removable_media = desc->removable;
|
||||
diskobj->media.media_present = 1;
|
||||
/*
|
||||
* MediaID is just an arbitrary counter.
|
||||
* We have to change it if the medium is removed or changed.
|
||||
*/
|
||||
diskobj->media.media_id = 1;
|
||||
diskobj->media.block_size = desc->blksz;
|
||||
diskobj->media.io_align = desc->blksz;
|
||||
diskobj->media.last_block = desc->lba - offset;
|
||||
if (part != 0)
|
||||
diskobj->media.logical_partition = 1;
|
||||
diskobj->ops.media = &diskobj->media;
|
||||
if (disk)
|
||||
*disk = diskobj;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create handles and protocols for the partitions of a block device
|
||||
*
|
||||
* @parent handle of the parent disk
|
||||
* @blk_desc block device
|
||||
* @if_typename interface type
|
||||
* @diskid device number
|
||||
* @pdevname device name
|
||||
* @return number of partitions created
|
||||
*/
|
||||
int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
|
||||
const char *if_typename, int diskid,
|
||||
const char *pdevname)
|
||||
{
|
||||
int disks = 0;
|
||||
char devname[32] = { 0 }; /* dp->str is u16[32] long */
|
||||
disk_partition_t info;
|
||||
int part;
|
||||
struct efi_device_path *dp = NULL;
|
||||
efi_status_t ret;
|
||||
struct efi_handler *handler;
|
||||
|
||||
/* Get the device path of the parent */
|
||||
ret = efi_search_protocol(parent, &efi_guid_device_path, &handler);
|
||||
if (ret == EFI_SUCCESS)
|
||||
dp = handler->protocol_interface;
|
||||
|
||||
/* Add devices for each partition */
|
||||
for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
|
||||
if (part_get_info(desc, part, &info))
|
||||
continue;
|
||||
snprintf(devname, sizeof(devname), "%s:%d", pdevname,
|
||||
part);
|
||||
ret = efi_disk_add_dev(parent, dp, if_typename, desc, diskid,
|
||||
info.start, part, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
printf("Adding partition %s failed\n", pdevname);
|
||||
continue;
|
||||
}
|
||||
disks++;
|
||||
}
|
||||
|
||||
return disks;
|
||||
}
|
||||
|
||||
/*
|
||||
* U-Boot doesn't have a list of all online disk devices. So when running our
|
||||
* EFI payload, we scan through all of the potentially available ones and
|
||||
* store them in our object pool.
|
||||
*
|
||||
* TODO(sjg@chromium.org): Actually with CONFIG_BLK, U-Boot does have this.
|
||||
* Consider converting the code to look up devices as needed. The EFI device
|
||||
* could be a child of the UCLASS_BLK block device, perhaps.
|
||||
*
|
||||
* This gets called from do_bootefi_exec().
|
||||
*/
|
||||
efi_status_t efi_disk_register(void)
|
||||
{
|
||||
struct efi_disk_obj *disk;
|
||||
int disks = 0;
|
||||
efi_status_t ret;
|
||||
#ifdef CONFIG_BLK
|
||||
struct udevice *dev;
|
||||
|
||||
for (uclass_first_device_check(UCLASS_BLK, &dev); dev;
|
||||
uclass_next_device_check(&dev)) {
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
const char *if_typename = blk_get_if_type_name(desc->if_type);
|
||||
|
||||
/* Add block device for the full device */
|
||||
printf("Scanning disk %s...\n", dev->name);
|
||||
ret = efi_disk_add_dev(NULL, NULL, if_typename,
|
||||
desc, desc->devnum, 0, 0, &disk);
|
||||
if (ret == EFI_NOT_READY) {
|
||||
printf("Disk %s not ready\n", dev->name);
|
||||
continue;
|
||||
}
|
||||
if (ret) {
|
||||
printf("ERROR: failure to add disk device %s, r = %lu\n",
|
||||
dev->name, ret & ~EFI_ERROR_MASK);
|
||||
return ret;
|
||||
}
|
||||
disks++;
|
||||
|
||||
/* Partitions show up as block devices in EFI */
|
||||
disks += efi_disk_create_partitions(
|
||||
&disk->header, desc, if_typename,
|
||||
desc->devnum, dev->name);
|
||||
}
|
||||
#else
|
||||
int i, if_type;
|
||||
|
||||
/* Search for all available disk devices */
|
||||
for (if_type = 0; if_type < IF_TYPE_COUNT; if_type++) {
|
||||
const struct blk_driver *cur_drvr;
|
||||
const char *if_typename;
|
||||
|
||||
cur_drvr = blk_driver_lookup_type(if_type);
|
||||
if (!cur_drvr)
|
||||
continue;
|
||||
|
||||
if_typename = cur_drvr->if_typename;
|
||||
printf("Scanning disks on %s...\n", if_typename);
|
||||
for (i = 0; i < 4; i++) {
|
||||
struct blk_desc *desc;
|
||||
char devname[32] = { 0 }; /* dp->str is u16[32] long */
|
||||
|
||||
desc = blk_get_devnum_by_type(if_type, i);
|
||||
if (!desc)
|
||||
continue;
|
||||
if (desc->type == DEV_TYPE_UNKNOWN)
|
||||
continue;
|
||||
|
||||
snprintf(devname, sizeof(devname), "%s%d",
|
||||
if_typename, i);
|
||||
|
||||
/* Add block device for the full device */
|
||||
ret = efi_disk_add_dev(NULL, NULL, if_typename, desc,
|
||||
i, 0, 0, &disk);
|
||||
if (ret == EFI_NOT_READY) {
|
||||
printf("Disk %s not ready\n", devname);
|
||||
continue;
|
||||
}
|
||||
if (ret) {
|
||||
printf("ERROR: failure to add disk device %s, r = %lu\n",
|
||||
devname, ret & ~EFI_ERROR_MASK);
|
||||
return ret;
|
||||
}
|
||||
disks++;
|
||||
|
||||
/* Partitions show up as block devices in EFI */
|
||||
disks += efi_disk_create_partitions
|
||||
(&disk->header, desc,
|
||||
if_typename, i, devname);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
printf("Found %d disks\n", disks);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,883 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI_FILE_PROTOCOL
|
||||
*
|
||||
* Copyright (c) 2017 Rob Clark
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <charset.h>
|
||||
#include <efi_loader.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
#include <fs.h>
|
||||
|
||||
/* GUID for file system information */
|
||||
const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
|
||||
|
||||
/* GUID to obtain the volume label */
|
||||
const efi_guid_t efi_system_volume_label_id = EFI_FILE_SYSTEM_VOLUME_LABEL_ID;
|
||||
|
||||
struct file_system {
|
||||
struct efi_simple_file_system_protocol base;
|
||||
struct efi_device_path *dp;
|
||||
struct blk_desc *desc;
|
||||
int part;
|
||||
};
|
||||
#define to_fs(x) container_of(x, struct file_system, base)
|
||||
|
||||
struct file_handle {
|
||||
struct efi_file_handle base;
|
||||
struct file_system *fs;
|
||||
loff_t offset; /* current file position/cursor */
|
||||
int isdir;
|
||||
u64 open_mode;
|
||||
|
||||
/* for reading a directory: */
|
||||
struct fs_dir_stream *dirs;
|
||||
struct fs_dirent *dent;
|
||||
|
||||
char path[0];
|
||||
};
|
||||
#define to_fh(x) container_of(x, struct file_handle, base)
|
||||
|
||||
static const struct efi_file_handle efi_file_handle_protocol;
|
||||
|
||||
static char *basename(struct file_handle *fh)
|
||||
{
|
||||
char *s = strrchr(fh->path, '/');
|
||||
if (s)
|
||||
return s + 1;
|
||||
return fh->path;
|
||||
}
|
||||
|
||||
static int set_blk_dev(struct file_handle *fh)
|
||||
{
|
||||
return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
|
||||
}
|
||||
|
||||
/**
|
||||
* is_dir() - check if file handle points to directory
|
||||
*
|
||||
* We assume that set_blk_dev(fh) has been called already.
|
||||
*
|
||||
* @fh: file handle
|
||||
* Return: true if file handle points to a directory
|
||||
*/
|
||||
static int is_dir(struct file_handle *fh)
|
||||
{
|
||||
struct fs_dir_stream *dirs;
|
||||
|
||||
dirs = fs_opendir(fh->path);
|
||||
if (!dirs)
|
||||
return 0;
|
||||
|
||||
fs_closedir(dirs);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalize a path which may include either back or fwd slashes,
|
||||
* double slashes, . or .. entries in the path, etc.
|
||||
*/
|
||||
static int sanitize_path(char *path)
|
||||
{
|
||||
char *p;
|
||||
|
||||
/* backslash to slash: */
|
||||
p = path;
|
||||
while ((p = strchr(p, '\\')))
|
||||
*p++ = '/';
|
||||
|
||||
/* handle double-slashes: */
|
||||
p = path;
|
||||
while ((p = strstr(p, "//"))) {
|
||||
char *src = p + 1;
|
||||
memmove(p, src, strlen(src) + 1);
|
||||
}
|
||||
|
||||
/* handle extra /.'s */
|
||||
p = path;
|
||||
while ((p = strstr(p, "/."))) {
|
||||
/*
|
||||
* You'd be tempted to do this *after* handling ".."s
|
||||
* below to avoid having to check if "/." is start of
|
||||
* a "/..", but that won't have the correct results..
|
||||
* for example, "/foo/./../bar" would get resolved to
|
||||
* "/foo/bar" if you did these two passes in the other
|
||||
* order
|
||||
*/
|
||||
if (p[2] == '.') {
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
char *src = p + 2;
|
||||
memmove(p, src, strlen(src) + 1);
|
||||
}
|
||||
|
||||
/* handle extra /..'s: */
|
||||
p = path;
|
||||
while ((p = strstr(p, "/.."))) {
|
||||
char *src = p + 3;
|
||||
|
||||
p--;
|
||||
|
||||
/* find beginning of previous path entry: */
|
||||
while (true) {
|
||||
if (p < path)
|
||||
return -1;
|
||||
if (*p == '/')
|
||||
break;
|
||||
p--;
|
||||
}
|
||||
|
||||
memmove(p, src, strlen(src) + 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_create_file() - create file or directory
|
||||
*
|
||||
* @fh: file handle
|
||||
* @attributes: attributes for newly created file
|
||||
* Returns: 0 for success
|
||||
*/
|
||||
static int efi_create_file(struct file_handle *fh, u64 attributes)
|
||||
{
|
||||
loff_t actwrite;
|
||||
void *buffer = &actwrite;
|
||||
|
||||
if (attributes & EFI_FILE_DIRECTORY)
|
||||
return fs_mkdir(fh->path);
|
||||
else
|
||||
return fs_write(fh->path, map_to_sysmem(buffer), 0, 0,
|
||||
&actwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* file_open() - open a file handle
|
||||
*
|
||||
* @fs: file system
|
||||
* @parent: directory relative to which the file is to be opened
|
||||
* @file_name: path of the file to be opened. '\', '.', or '..' may
|
||||
* be used as modifiers. A leading backslash indicates an
|
||||
* absolute path.
|
||||
* @open_mode: bit mask indicating the access mode (read, write,
|
||||
* create)
|
||||
* @attributes: attributes for newly created file
|
||||
* Returns: handle to the opened file or NULL
|
||||
*/
|
||||
static struct efi_file_handle *file_open(struct file_system *fs,
|
||||
struct file_handle *parent, u16 *file_name, u64 open_mode,
|
||||
u64 attributes)
|
||||
{
|
||||
struct file_handle *fh;
|
||||
char f0[MAX_UTF8_PER_UTF16] = {0};
|
||||
int plen = 0;
|
||||
int flen = 0;
|
||||
|
||||
if (file_name) {
|
||||
utf16_to_utf8((u8 *)f0, file_name, 1);
|
||||
flen = u16_strlen(file_name);
|
||||
}
|
||||
|
||||
/* we could have a parent, but also an absolute path: */
|
||||
if (f0[0] == '\\') {
|
||||
plen = 0;
|
||||
} else if (parent) {
|
||||
plen = strlen(parent->path) + 1;
|
||||
}
|
||||
|
||||
/* +2 is for null and '/' */
|
||||
fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
|
||||
|
||||
fh->open_mode = open_mode;
|
||||
fh->base = efi_file_handle_protocol;
|
||||
fh->fs = fs;
|
||||
|
||||
if (parent) {
|
||||
char *p = fh->path;
|
||||
int exists;
|
||||
|
||||
if (plen > 0) {
|
||||
strcpy(p, parent->path);
|
||||
p += plen - 1;
|
||||
*p++ = '/';
|
||||
}
|
||||
|
||||
utf16_to_utf8((u8 *)p, file_name, flen);
|
||||
|
||||
if (sanitize_path(fh->path))
|
||||
goto error;
|
||||
|
||||
/* check if file exists: */
|
||||
if (set_blk_dev(fh))
|
||||
goto error;
|
||||
|
||||
exists = fs_exists(fh->path);
|
||||
/* fs_exists() calls fs_close(), so open file system again */
|
||||
if (set_blk_dev(fh))
|
||||
goto error;
|
||||
|
||||
if (!exists) {
|
||||
if (!(open_mode & EFI_FILE_MODE_CREATE) ||
|
||||
efi_create_file(fh, attributes))
|
||||
goto error;
|
||||
if (set_blk_dev(fh))
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* figure out if file is a directory: */
|
||||
fh->isdir = is_dir(fh);
|
||||
} else {
|
||||
fh->isdir = 1;
|
||||
strcpy(fh->path, "");
|
||||
}
|
||||
|
||||
return &fh->base;
|
||||
|
||||
error:
|
||||
free(fh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
|
||||
struct efi_file_handle **new_handle,
|
||||
u16 *file_name, u64 open_mode, u64 attributes)
|
||||
{
|
||||
struct file_handle *fh = to_fh(file);
|
||||
efi_status_t ret;
|
||||
|
||||
EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle,
|
||||
file_name, open_mode, attributes);
|
||||
|
||||
/* Check parameters */
|
||||
if (!file || !new_handle || !file_name) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
if (open_mode != EFI_FILE_MODE_READ &&
|
||||
open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
|
||||
open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
|
||||
EFI_FILE_MODE_CREATE)) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* The UEFI spec requires that attributes are only set in create mode.
|
||||
* The SCT does not care about this and sets EFI_FILE_DIRECTORY in
|
||||
* read mode. EDK2 does not check that attributes are zero if not in
|
||||
* create mode.
|
||||
*
|
||||
* So here we only check attributes in create mode and do not check
|
||||
* that they are zero otherwise.
|
||||
*/
|
||||
if ((open_mode & EFI_FILE_MODE_CREATE) &&
|
||||
(attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Open file */
|
||||
*new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
|
||||
if (*new_handle) {
|
||||
EFI_PRINT("file handle %p\n", *new_handle);
|
||||
ret = EFI_SUCCESS;
|
||||
} else {
|
||||
ret = EFI_NOT_FOUND;
|
||||
}
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
static efi_status_t file_close(struct file_handle *fh)
|
||||
{
|
||||
fs_closedir(fh->dirs);
|
||||
free(fh);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
|
||||
{
|
||||
struct file_handle *fh = to_fh(file);
|
||||
EFI_ENTRY("%p", file);
|
||||
return EFI_EXIT(file_close(fh));
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
|
||||
{
|
||||
struct file_handle *fh = to_fh(file);
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p", file);
|
||||
|
||||
if (set_blk_dev(fh) || fs_unlink(fh->path))
|
||||
ret = EFI_WARN_DELETE_FAILURE;
|
||||
|
||||
file_close(fh);
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_get_file_size() - determine the size of a file
|
||||
*
|
||||
* @fh: file handle
|
||||
* @file_size: pointer to receive file size
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_get_file_size(struct file_handle *fh,
|
||||
loff_t *file_size)
|
||||
{
|
||||
if (set_blk_dev(fh))
|
||||
return EFI_DEVICE_ERROR;
|
||||
|
||||
if (fs_size(fh->path, file_size))
|
||||
return EFI_DEVICE_ERROR;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
|
||||
void *buffer)
|
||||
{
|
||||
loff_t actread;
|
||||
efi_status_t ret;
|
||||
loff_t file_size;
|
||||
|
||||
ret = efi_get_file_size(fh, &file_size);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
if (file_size < fh->offset) {
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (set_blk_dev(fh))
|
||||
return EFI_DEVICE_ERROR;
|
||||
if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
|
||||
*buffer_size, &actread))
|
||||
return EFI_DEVICE_ERROR;
|
||||
|
||||
*buffer_size = actread;
|
||||
fh->offset += actread;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
|
||||
void *buffer)
|
||||
{
|
||||
struct efi_file_info *info = buffer;
|
||||
struct fs_dirent *dent;
|
||||
u64 required_size;
|
||||
u16 *dst;
|
||||
|
||||
if (set_blk_dev(fh))
|
||||
return EFI_DEVICE_ERROR;
|
||||
|
||||
if (!fh->dirs) {
|
||||
assert(fh->offset == 0);
|
||||
fh->dirs = fs_opendir(fh->path);
|
||||
if (!fh->dirs)
|
||||
return EFI_DEVICE_ERROR;
|
||||
fh->dent = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* So this is a bit awkward. Since fs layer is stateful and we
|
||||
* can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
|
||||
* we might have to return without consuming the dent.. so we
|
||||
* have to stash it for next call.
|
||||
*/
|
||||
if (fh->dent) {
|
||||
dent = fh->dent;
|
||||
} else {
|
||||
dent = fs_readdir(fh->dirs);
|
||||
}
|
||||
|
||||
if (!dent) {
|
||||
/* no more files in directory */
|
||||
*buffer_size = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/* check buffer size: */
|
||||
required_size = sizeof(*info) +
|
||||
2 * (utf8_utf16_strlen(dent->name) + 1);
|
||||
if (*buffer_size < required_size) {
|
||||
*buffer_size = required_size;
|
||||
fh->dent = dent;
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
fh->dent = NULL;
|
||||
|
||||
*buffer_size = required_size;
|
||||
memset(info, 0, required_size);
|
||||
|
||||
info->size = required_size;
|
||||
info->file_size = dent->size;
|
||||
info->physical_size = dent->size;
|
||||
|
||||
if (dent->type == FS_DT_DIR)
|
||||
info->attribute |= EFI_FILE_DIRECTORY;
|
||||
|
||||
dst = info->file_name;
|
||||
utf8_utf16_strcpy(&dst, dent->name);
|
||||
|
||||
fh->offset++;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
|
||||
efi_uintn_t *buffer_size, void *buffer)
|
||||
{
|
||||
struct file_handle *fh = to_fh(file);
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
u64 bs;
|
||||
|
||||
EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
|
||||
|
||||
if (!buffer_size || !buffer) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto error;
|
||||
}
|
||||
|
||||
bs = *buffer_size;
|
||||
if (fh->isdir)
|
||||
ret = dir_read(fh, &bs, buffer);
|
||||
else
|
||||
ret = file_read(fh, &bs, buffer);
|
||||
if (bs <= SIZE_MAX)
|
||||
*buffer_size = bs;
|
||||
else
|
||||
*buffer_size = SIZE_MAX;
|
||||
|
||||
error:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_file_write() - write to file
|
||||
*
|
||||
* This function implements the Write() service of the EFI_FILE_PROTOCOL.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @file: file handle
|
||||
* @buffer_size: number of bytes to write
|
||||
* @buffer: buffer with the bytes to write
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
|
||||
efi_uintn_t *buffer_size,
|
||||
void *buffer)
|
||||
{
|
||||
struct file_handle *fh = to_fh(file);
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
loff_t actwrite;
|
||||
|
||||
EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
|
||||
|
||||
if (!file || !buffer_size || !buffer) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
if (fh->isdir) {
|
||||
ret = EFI_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) {
|
||||
ret = EFI_ACCESS_DENIED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!*buffer_size)
|
||||
goto out;
|
||||
|
||||
if (set_blk_dev(fh)) {
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
}
|
||||
if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
|
||||
&actwrite)) {
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
}
|
||||
*buffer_size = actwrite;
|
||||
fh->offset += actwrite;
|
||||
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_file_getpos() - get current position in file
|
||||
*
|
||||
* This function implements the GetPosition service of the EFI file protocol.
|
||||
* See the UEFI spec for details.
|
||||
*
|
||||
* @file: file handle
|
||||
* @pos: pointer to file position
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
|
||||
u64 *pos)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
struct file_handle *fh = to_fh(file);
|
||||
|
||||
EFI_ENTRY("%p, %p", file, pos);
|
||||
|
||||
if (fh->isdir) {
|
||||
ret = EFI_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*pos = fh->offset;
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_file_setpos() - set current position in file
|
||||
*
|
||||
* This function implements the SetPosition service of the EFI file protocol.
|
||||
* See the UEFI spec for details.
|
||||
*
|
||||
* @file: file handle
|
||||
* @pos: new file position
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
|
||||
u64 pos)
|
||||
{
|
||||
struct file_handle *fh = to_fh(file);
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p, %llu", file, pos);
|
||||
|
||||
if (fh->isdir) {
|
||||
if (pos != 0) {
|
||||
ret = EFI_UNSUPPORTED;
|
||||
goto error;
|
||||
}
|
||||
fs_closedir(fh->dirs);
|
||||
fh->dirs = NULL;
|
||||
}
|
||||
|
||||
if (pos == ~0ULL) {
|
||||
loff_t file_size;
|
||||
|
||||
ret = efi_get_file_size(fh, &file_size);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto error;
|
||||
pos = file_size;
|
||||
}
|
||||
|
||||
fh->offset = pos;
|
||||
|
||||
error:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
|
||||
const efi_guid_t *info_type,
|
||||
efi_uintn_t *buffer_size,
|
||||
void *buffer)
|
||||
{
|
||||
struct file_handle *fh = to_fh(file);
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
u16 *dst;
|
||||
|
||||
EFI_ENTRY("%p, %pUl, %p, %p", file, info_type, buffer_size, buffer);
|
||||
|
||||
if (!file || !info_type || !buffer_size ||
|
||||
(*buffer_size && !buffer)) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!guidcmp(info_type, &efi_file_info_guid)) {
|
||||
struct efi_file_info *info = buffer;
|
||||
char *filename = basename(fh);
|
||||
unsigned int required_size;
|
||||
loff_t file_size;
|
||||
|
||||
/* check buffer size: */
|
||||
required_size = sizeof(*info) +
|
||||
2 * (utf8_utf16_strlen(filename) + 1);
|
||||
if (*buffer_size < required_size) {
|
||||
*buffer_size = required_size;
|
||||
ret = EFI_BUFFER_TOO_SMALL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = efi_get_file_size(fh, &file_size);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto error;
|
||||
|
||||
memset(info, 0, required_size);
|
||||
|
||||
info->size = required_size;
|
||||
info->file_size = file_size;
|
||||
info->physical_size = file_size;
|
||||
|
||||
if (fh->isdir)
|
||||
info->attribute |= EFI_FILE_DIRECTORY;
|
||||
|
||||
dst = info->file_name;
|
||||
utf8_utf16_strcpy(&dst, filename);
|
||||
} else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
|
||||
struct efi_file_system_info *info = buffer;
|
||||
disk_partition_t part;
|
||||
efi_uintn_t required_size;
|
||||
int r;
|
||||
|
||||
if (fh->fs->part >= 1)
|
||||
r = part_get_info(fh->fs->desc, fh->fs->part, &part);
|
||||
else
|
||||
r = part_get_info_whole_disk(fh->fs->desc, &part);
|
||||
if (r < 0) {
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto error;
|
||||
}
|
||||
required_size = sizeof(*info) + 2;
|
||||
if (*buffer_size < required_size) {
|
||||
*buffer_size = required_size;
|
||||
ret = EFI_BUFFER_TOO_SMALL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
memset(info, 0, required_size);
|
||||
|
||||
info->size = required_size;
|
||||
info->read_only = true;
|
||||
info->volume_size = part.size * part.blksz;
|
||||
info->free_space = 0;
|
||||
info->block_size = part.blksz;
|
||||
/*
|
||||
* TODO: The volume label is not available in U-Boot.
|
||||
*/
|
||||
info->volume_label[0] = 0;
|
||||
} else if (!guidcmp(info_type, &efi_system_volume_label_id)) {
|
||||
if (*buffer_size < 2) {
|
||||
*buffer_size = 2;
|
||||
ret = EFI_BUFFER_TOO_SMALL;
|
||||
goto error;
|
||||
}
|
||||
*(u16 *)buffer = 0;
|
||||
} else {
|
||||
ret = EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
error:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
|
||||
const efi_guid_t *info_type,
|
||||
efi_uintn_t buffer_size,
|
||||
void *buffer)
|
||||
{
|
||||
struct file_handle *fh = to_fh(file);
|
||||
efi_status_t ret = EFI_UNSUPPORTED;
|
||||
|
||||
EFI_ENTRY("%p, %pUl, %zu, %p", file, info_type, buffer_size, buffer);
|
||||
|
||||
if (!guidcmp(info_type, &efi_file_info_guid)) {
|
||||
struct efi_file_info *info = (struct efi_file_info *)buffer;
|
||||
char *filename = basename(fh);
|
||||
char *new_file_name, *pos;
|
||||
loff_t file_size;
|
||||
|
||||
/* The buffer will always contain a file name. */
|
||||
if (buffer_size < sizeof(struct efi_file_info) + 2 ||
|
||||
buffer_size < info->size) {
|
||||
ret = EFI_BAD_BUFFER_SIZE;
|
||||
goto out;
|
||||
}
|
||||
/* We cannot change the directory attribute */
|
||||
if (!fh->isdir != !(info->attribute & EFI_FILE_DIRECTORY)) {
|
||||
ret = EFI_ACCESS_DENIED;
|
||||
goto out;
|
||||
}
|
||||
/* Check for renaming */
|
||||
new_file_name = malloc(utf16_utf8_strlen(info->file_name));
|
||||
if (!new_file_name) {
|
||||
ret = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
pos = new_file_name;
|
||||
utf16_utf8_strcpy(&pos, info->file_name);
|
||||
if (strcmp(new_file_name, filename)) {
|
||||
/* TODO: we do not support renaming */
|
||||
EFI_PRINT("Renaming not supported\n");
|
||||
free(new_file_name);
|
||||
ret = EFI_ACCESS_DENIED;
|
||||
goto out;
|
||||
}
|
||||
free(new_file_name);
|
||||
/* Check for truncation */
|
||||
ret = efi_get_file_size(fh, &file_size);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
if (file_size != info->file_size) {
|
||||
/* TODO: we do not support truncation */
|
||||
EFI_PRINT("Truncation not supported\n");
|
||||
ret = EFI_ACCESS_DENIED;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* We do not care for the other attributes
|
||||
* TODO: Support read only
|
||||
*/
|
||||
ret = EFI_SUCCESS;
|
||||
} else {
|
||||
/* TODO: We do not support changing the volume label */
|
||||
ret = EFI_UNSUPPORTED;
|
||||
}
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
|
||||
{
|
||||
EFI_ENTRY("%p", file);
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *file,
|
||||
struct efi_file_handle **new_handle,
|
||||
u16 *file_name, u64 open_mode, u64 attributes,
|
||||
struct efi_file_io_token *token)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *file,
|
||||
struct efi_file_io_token *token)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *file,
|
||||
struct efi_file_io_token *token)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *file,
|
||||
struct efi_file_io_token *token)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static const struct efi_file_handle efi_file_handle_protocol = {
|
||||
.rev = EFI_FILE_PROTOCOL_REVISION2,
|
||||
.open = efi_file_open,
|
||||
.close = efi_file_close,
|
||||
.delete = efi_file_delete,
|
||||
.read = efi_file_read,
|
||||
.write = efi_file_write,
|
||||
.getpos = efi_file_getpos,
|
||||
.setpos = efi_file_setpos,
|
||||
.getinfo = efi_file_getinfo,
|
||||
.setinfo = efi_file_setinfo,
|
||||
.flush = efi_file_flush,
|
||||
.open_ex = efi_file_open_ex,
|
||||
.read_ex = efi_file_read_ex,
|
||||
.write_ex = efi_file_write_ex,
|
||||
.flush_ex = efi_file_flush_ex,
|
||||
};
|
||||
|
||||
/**
|
||||
* efi_file_from_path() - open file via device path
|
||||
*
|
||||
* @fp: device path
|
||||
* @return: EFI_FILE_PROTOCOL for the file or NULL
|
||||
*/
|
||||
struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
|
||||
{
|
||||
struct efi_simple_file_system_protocol *v;
|
||||
struct efi_file_handle *f;
|
||||
efi_status_t ret;
|
||||
|
||||
v = efi_fs_from_path(fp);
|
||||
if (!v)
|
||||
return NULL;
|
||||
|
||||
EFI_CALL(ret = v->open_volume(v, &f));
|
||||
if (ret != EFI_SUCCESS)
|
||||
return NULL;
|
||||
|
||||
/* Skip over device-path nodes before the file path. */
|
||||
while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
|
||||
fp = efi_dp_next(fp);
|
||||
|
||||
/*
|
||||
* Step through the nodes of the directory path until the actual file
|
||||
* node is reached which is the final node in the device path.
|
||||
*/
|
||||
while (fp) {
|
||||
struct efi_device_path_file_path *fdp =
|
||||
container_of(fp, struct efi_device_path_file_path, dp);
|
||||
struct efi_file_handle *f2;
|
||||
u16 *filename;
|
||||
|
||||
if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
|
||||
printf("bad file path!\n");
|
||||
f->close(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
filename = u16_strdup(fdp->str);
|
||||
if (!filename)
|
||||
return NULL;
|
||||
EFI_CALL(ret = f->open(f, &f2, filename,
|
||||
EFI_FILE_MODE_READ, 0));
|
||||
free(filename);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return NULL;
|
||||
|
||||
fp = efi_dp_next(fp);
|
||||
|
||||
EFI_CALL(f->close(f));
|
||||
f = f2;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI
|
||||
efi_open_volume(struct efi_simple_file_system_protocol *this,
|
||||
struct efi_file_handle **root)
|
||||
{
|
||||
struct file_system *fs = to_fs(this);
|
||||
|
||||
EFI_ENTRY("%p, %p", this, root);
|
||||
|
||||
*root = file_open(fs, NULL, NULL, 0, 0);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
struct efi_simple_file_system_protocol *
|
||||
efi_simple_file_system(struct blk_desc *desc, int part,
|
||||
struct efi_device_path *dp)
|
||||
{
|
||||
struct file_system *fs;
|
||||
|
||||
fs = calloc(1, sizeof(*fs));
|
||||
fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
|
||||
fs->base.open_volume = efi_open_volume;
|
||||
fs->desc = desc;
|
||||
fs->part = part;
|
||||
fs->dp = dp;
|
||||
|
||||
return &fs->base;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Library for freestanding binary
|
||||
*
|
||||
* Copyright 2019, Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* GCC requires that freestanding programs provide memcpy(), memmove(),
|
||||
* memset(), and memcmp().
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
/**
|
||||
* memcmp() - compare memory areas
|
||||
*
|
||||
* @s1: pointer to first area
|
||||
* @s2: pointer to second area
|
||||
* @n: number of bytes to compare
|
||||
* Return: 0 if both memory areas are the same, otherwise the sign of the
|
||||
* result value is the same as the sign of the difference between
|
||||
* the first differing pair of bytes taken as u8.
|
||||
*/
|
||||
int memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
const u8 *pos1 = s1;
|
||||
const u8 *pos2 = s2;
|
||||
|
||||
for (; n; --n) {
|
||||
if (*pos1 != *pos2)
|
||||
return *pos1 - *pos2;
|
||||
++pos1;
|
||||
++pos2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* memcpy() - copy memory area
|
||||
*
|
||||
* @dest: destination buffer
|
||||
* @src: source buffer
|
||||
* @n: number of bytes to copy
|
||||
* Return: pointer to destination buffer
|
||||
*/
|
||||
void *memmove(void *dest, const void *src, size_t n)
|
||||
{
|
||||
u8 *d = dest;
|
||||
const u8 *s = src;
|
||||
|
||||
if (d >= s) {
|
||||
for (; n; --n)
|
||||
*d++ = *s++;
|
||||
} else {
|
||||
d += n;
|
||||
s += n;
|
||||
for (; n; --n)
|
||||
*--d = *--s;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* memcpy() - copy memory area
|
||||
*
|
||||
* @dest: destination buffer
|
||||
* @src: source buffer
|
||||
* @n: number of bytes to copy
|
||||
* Return: pointer to destination buffer
|
||||
*/
|
||||
void *memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
return memmove(dest, src, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* memset() - fill memory with a constant byte
|
||||
*
|
||||
* @s: destination buffer
|
||||
* @c: byte value
|
||||
* @n: number of bytes to set
|
||||
* Return: pointer to destination buffer
|
||||
*/
|
||||
void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
u8 *d = s;
|
||||
|
||||
for (; n; --n)
|
||||
*d++ = c;
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI application disk support
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <efi_loader.h>
|
||||
#include <lcd.h>
|
||||
#include <malloc.h>
|
||||
#include <video.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static const efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
||||
|
||||
/**
|
||||
* struct efi_gop_obj - graphical output protocol object
|
||||
*
|
||||
* @header: EFI object header
|
||||
* @ops: graphical output protocol interface
|
||||
* @info: graphical output mode information
|
||||
* @mode: graphical output mode
|
||||
* @bpix: bits per pixel
|
||||
* @fb: frame buffer
|
||||
*/
|
||||
struct efi_gop_obj {
|
||||
struct efi_object header;
|
||||
struct efi_gop ops;
|
||||
struct efi_gop_mode_info info;
|
||||
struct efi_gop_mode mode;
|
||||
/* Fields we only have access to during init */
|
||||
u32 bpix;
|
||||
void *fb;
|
||||
};
|
||||
|
||||
static efi_status_t EFIAPI gop_query_mode(struct efi_gop *this, u32 mode_number,
|
||||
efi_uintn_t *size_of_info,
|
||||
struct efi_gop_mode_info **info)
|
||||
{
|
||||
struct efi_gop_obj *gopobj;
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p, %x, %p, %p", this, mode_number, size_of_info, info);
|
||||
|
||||
if (!this || !size_of_info || !info || mode_number) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
gopobj = container_of(this, struct efi_gop_obj, ops);
|
||||
ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, sizeof(gopobj->info),
|
||||
(void **)info);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
*size_of_info = sizeof(gopobj->info);
|
||||
memcpy(*info, &gopobj->info, sizeof(gopobj->info));
|
||||
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid)
|
||||
{
|
||||
struct efi_gop_pixel blt = {
|
||||
.reserved = 0,
|
||||
};
|
||||
|
||||
blt.blue = (vid & 0x1f) << 3;
|
||||
vid >>= 5;
|
||||
blt.green = (vid & 0x3f) << 2;
|
||||
vid >>= 6;
|
||||
blt.red = (vid & 0x1f) << 3;
|
||||
return blt;
|
||||
}
|
||||
|
||||
static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt)
|
||||
{
|
||||
return (u16)(blt->red >> 3) << 11 |
|
||||
(u16)(blt->green >> 2) << 5 |
|
||||
(u16)(blt->blue >> 3);
|
||||
}
|
||||
|
||||
static __always_inline efi_status_t gop_blt_int(struct efi_gop *this,
|
||||
struct efi_gop_pixel *bufferp,
|
||||
u32 operation, efi_uintn_t sx,
|
||||
efi_uintn_t sy, efi_uintn_t dx,
|
||||
efi_uintn_t dy,
|
||||
efi_uintn_t width,
|
||||
efi_uintn_t height,
|
||||
efi_uintn_t delta,
|
||||
efi_uintn_t vid_bpp)
|
||||
{
|
||||
struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
|
||||
efi_uintn_t i, j, linelen, slineoff = 0, dlineoff, swidth, dwidth;
|
||||
u32 *fb32 = gopobj->fb;
|
||||
u16 *fb16 = gopobj->fb;
|
||||
struct efi_gop_pixel *buffer = __builtin_assume_aligned(bufferp, 4);
|
||||
|
||||
if (delta) {
|
||||
/* Check for 4 byte alignment */
|
||||
if (delta & 3)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
linelen = delta >> 2;
|
||||
} else {
|
||||
linelen = width;
|
||||
}
|
||||
|
||||
/* Check source rectangle */
|
||||
switch (operation) {
|
||||
case EFI_BLT_VIDEO_FILL:
|
||||
break;
|
||||
case EFI_BLT_BUFFER_TO_VIDEO:
|
||||
if (sx + width > linelen)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
break;
|
||||
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
|
||||
case EFI_BLT_VIDEO_TO_VIDEO:
|
||||
if (sx + width > gopobj->info.width ||
|
||||
sy + height > gopobj->info.height)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
break;
|
||||
default:
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Check destination rectangle */
|
||||
switch (operation) {
|
||||
case EFI_BLT_VIDEO_FILL:
|
||||
case EFI_BLT_BUFFER_TO_VIDEO:
|
||||
case EFI_BLT_VIDEO_TO_VIDEO:
|
||||
if (dx + width > gopobj->info.width ||
|
||||
dy + height > gopobj->info.height)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
break;
|
||||
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
|
||||
if (dx + width > linelen)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate line width */
|
||||
switch (operation) {
|
||||
case EFI_BLT_BUFFER_TO_VIDEO:
|
||||
swidth = linelen;
|
||||
break;
|
||||
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
|
||||
case EFI_BLT_VIDEO_TO_VIDEO:
|
||||
swidth = gopobj->info.width;
|
||||
if (!vid_bpp)
|
||||
return EFI_UNSUPPORTED;
|
||||
break;
|
||||
case EFI_BLT_VIDEO_FILL:
|
||||
swidth = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (operation) {
|
||||
case EFI_BLT_BUFFER_TO_VIDEO:
|
||||
case EFI_BLT_VIDEO_FILL:
|
||||
case EFI_BLT_VIDEO_TO_VIDEO:
|
||||
dwidth = gopobj->info.width;
|
||||
if (!vid_bpp)
|
||||
return EFI_UNSUPPORTED;
|
||||
break;
|
||||
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
|
||||
dwidth = linelen;
|
||||
break;
|
||||
}
|
||||
|
||||
slineoff = swidth * sy;
|
||||
dlineoff = dwidth * dy;
|
||||
for (i = 0; i < height; i++) {
|
||||
for (j = 0; j < width; j++) {
|
||||
struct efi_gop_pixel pix;
|
||||
|
||||
/* Read source pixel */
|
||||
switch (operation) {
|
||||
case EFI_BLT_VIDEO_FILL:
|
||||
pix = *buffer;
|
||||
break;
|
||||
case EFI_BLT_BUFFER_TO_VIDEO:
|
||||
pix = buffer[slineoff + j + sx];
|
||||
break;
|
||||
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
|
||||
case EFI_BLT_VIDEO_TO_VIDEO:
|
||||
if (vid_bpp == 32)
|
||||
pix = *(struct efi_gop_pixel *)&fb32[
|
||||
slineoff + j + sx];
|
||||
else
|
||||
pix = efi_vid16_to_blt_col(fb16[
|
||||
slineoff + j + sx]);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write destination pixel */
|
||||
switch (operation) {
|
||||
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
|
||||
buffer[dlineoff + j + dx] = pix;
|
||||
break;
|
||||
case EFI_BLT_BUFFER_TO_VIDEO:
|
||||
case EFI_BLT_VIDEO_FILL:
|
||||
case EFI_BLT_VIDEO_TO_VIDEO:
|
||||
if (vid_bpp == 32)
|
||||
fb32[dlineoff + j + dx] = *(u32 *)&pix;
|
||||
else
|
||||
fb16[dlineoff + j + dx] =
|
||||
efi_blt_col_to_vid16(&pix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
slineoff += swidth;
|
||||
dlineoff += dwidth;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_uintn_t gop_get_bpp(struct efi_gop *this)
|
||||
{
|
||||
struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
|
||||
efi_uintn_t vid_bpp = 0;
|
||||
|
||||
switch (gopobj->bpix) {
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
case VIDEO_BPP32:
|
||||
#else
|
||||
case LCD_COLOR32:
|
||||
#endif
|
||||
vid_bpp = 32;
|
||||
break;
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
case VIDEO_BPP16:
|
||||
#else
|
||||
case LCD_COLOR16:
|
||||
#endif
|
||||
vid_bpp = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
return vid_bpp;
|
||||
}
|
||||
|
||||
/*
|
||||
* GCC can't optimize our BLT function well, but we need to make sure that
|
||||
* our 2-dimensional loop gets executed very quickly, otherwise the system
|
||||
* will feel slow.
|
||||
*
|
||||
* By manually putting all obvious branch targets into functions which call
|
||||
* our generic BLT function with constants, the compiler can successfully
|
||||
* optimize for speed.
|
||||
*/
|
||||
static efi_status_t gop_blt_video_fill(struct efi_gop *this,
|
||||
struct efi_gop_pixel *buffer,
|
||||
u32 foo, efi_uintn_t sx,
|
||||
efi_uintn_t sy, efi_uintn_t dx,
|
||||
efi_uintn_t dy, efi_uintn_t width,
|
||||
efi_uintn_t height, efi_uintn_t delta,
|
||||
efi_uintn_t vid_bpp)
|
||||
{
|
||||
return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx,
|
||||
dy, width, height, delta, vid_bpp);
|
||||
}
|
||||
|
||||
static efi_status_t gop_blt_buf_to_vid16(struct efi_gop *this,
|
||||
struct efi_gop_pixel *buffer,
|
||||
u32 foo, efi_uintn_t sx,
|
||||
efi_uintn_t sy, efi_uintn_t dx,
|
||||
efi_uintn_t dy, efi_uintn_t width,
|
||||
efi_uintn_t height, efi_uintn_t delta)
|
||||
{
|
||||
return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
|
||||
dy, width, height, delta, 16);
|
||||
}
|
||||
|
||||
static efi_status_t gop_blt_buf_to_vid32(struct efi_gop *this,
|
||||
struct efi_gop_pixel *buffer,
|
||||
u32 foo, efi_uintn_t sx,
|
||||
efi_uintn_t sy, efi_uintn_t dx,
|
||||
efi_uintn_t dy, efi_uintn_t width,
|
||||
efi_uintn_t height, efi_uintn_t delta)
|
||||
{
|
||||
return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
|
||||
dy, width, height, delta, 32);
|
||||
}
|
||||
|
||||
static efi_status_t gop_blt_vid_to_vid(struct efi_gop *this,
|
||||
struct efi_gop_pixel *buffer,
|
||||
u32 foo, efi_uintn_t sx,
|
||||
efi_uintn_t sy, efi_uintn_t dx,
|
||||
efi_uintn_t dy, efi_uintn_t width,
|
||||
efi_uintn_t height, efi_uintn_t delta,
|
||||
efi_uintn_t vid_bpp)
|
||||
{
|
||||
return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx,
|
||||
dy, width, height, delta, vid_bpp);
|
||||
}
|
||||
|
||||
static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this,
|
||||
struct efi_gop_pixel *buffer,
|
||||
u32 foo, efi_uintn_t sx,
|
||||
efi_uintn_t sy, efi_uintn_t dx,
|
||||
efi_uintn_t dy, efi_uintn_t width,
|
||||
efi_uintn_t height, efi_uintn_t delta,
|
||||
efi_uintn_t vid_bpp)
|
||||
{
|
||||
return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy,
|
||||
dx, dy, width, height, delta, vid_bpp);
|
||||
}
|
||||
|
||||
/**
|
||||
* gop_set_mode() - set graphical output mode
|
||||
*
|
||||
* This function implements the SetMode() service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @this: the graphical output protocol
|
||||
* @mode_number: the mode to be set
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
|
||||
{
|
||||
struct efi_gop_obj *gopobj;
|
||||
struct efi_gop_pixel buffer = {0, 0, 0, 0};
|
||||
efi_uintn_t vid_bpp;
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p, %x", this, mode_number);
|
||||
|
||||
if (!this) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
if (mode_number) {
|
||||
ret = EFI_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
gopobj = container_of(this, struct efi_gop_obj, ops);
|
||||
vid_bpp = gop_get_bpp(this);
|
||||
ret = gop_blt_video_fill(this, &buffer, EFI_BLT_VIDEO_FILL, 0, 0, 0, 0,
|
||||
gopobj->info.width, gopobj->info.height, 0,
|
||||
vid_bpp);
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy rectangle.
|
||||
*
|
||||
* This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @this: EFI_GRAPHICS_OUTPUT_PROTOCOL
|
||||
* @buffer: pixel buffer
|
||||
* @sx: source x-coordinate
|
||||
* @sy: source y-coordinate
|
||||
* @dx: destination x-coordinate
|
||||
* @dy: destination y-coordinate
|
||||
* @width: width of rectangle
|
||||
* @height: height of rectangle
|
||||
* @delta: length in bytes of a line in the pixel buffer (optional)
|
||||
* @return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer,
|
||||
u32 operation, efi_uintn_t sx,
|
||||
efi_uintn_t sy, efi_uintn_t dx,
|
||||
efi_uintn_t dy, efi_uintn_t width,
|
||||
efi_uintn_t height, efi_uintn_t delta)
|
||||
{
|
||||
efi_status_t ret = EFI_INVALID_PARAMETER;
|
||||
efi_uintn_t vid_bpp;
|
||||
|
||||
EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
|
||||
buffer, operation, sx, sy, dx, dy, width, height, delta);
|
||||
|
||||
vid_bpp = gop_get_bpp(this);
|
||||
|
||||
/* Allow for compiler optimization */
|
||||
switch (operation) {
|
||||
case EFI_BLT_VIDEO_FILL:
|
||||
ret = gop_blt_video_fill(this, buffer, operation, sx, sy, dx,
|
||||
dy, width, height, delta, vid_bpp);
|
||||
break;
|
||||
case EFI_BLT_BUFFER_TO_VIDEO:
|
||||
/* This needs to be super-fast, so duplicate for 16/32bpp */
|
||||
if (vid_bpp == 32)
|
||||
ret = gop_blt_buf_to_vid32(this, buffer, operation, sx,
|
||||
sy, dx, dy, width, height,
|
||||
delta);
|
||||
else
|
||||
ret = gop_blt_buf_to_vid16(this, buffer, operation, sx,
|
||||
sy, dx, dy, width, height,
|
||||
delta);
|
||||
break;
|
||||
case EFI_BLT_VIDEO_TO_VIDEO:
|
||||
ret = gop_blt_vid_to_vid(this, buffer, operation, sx, sy, dx,
|
||||
dy, width, height, delta, vid_bpp);
|
||||
break;
|
||||
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
|
||||
ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx,
|
||||
dy, width, height, delta, vid_bpp);
|
||||
break;
|
||||
default:
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (ret != EFI_SUCCESS)
|
||||
return EFI_EXIT(ret);
|
||||
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
video_sync_all();
|
||||
#else
|
||||
lcd_sync();
|
||||
#endif
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Install graphical output protocol.
|
||||
*
|
||||
* If no supported video device exists this is not considered as an
|
||||
* error.
|
||||
*/
|
||||
efi_status_t efi_gop_register(void)
|
||||
{
|
||||
struct efi_gop_obj *gopobj;
|
||||
u32 bpix, col, row;
|
||||
u64 fb_base, fb_size;
|
||||
void *fb;
|
||||
efi_status_t ret;
|
||||
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
struct udevice *vdev;
|
||||
struct video_priv *priv;
|
||||
|
||||
/* We only support a single video output device for now */
|
||||
if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) {
|
||||
debug("WARNING: No video device\n");
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
priv = dev_get_uclass_priv(vdev);
|
||||
bpix = priv->bpix;
|
||||
col = video_get_xsize(vdev);
|
||||
row = video_get_ysize(vdev);
|
||||
fb_base = (uintptr_t)priv->fb;
|
||||
fb_size = priv->fb_size;
|
||||
fb = priv->fb;
|
||||
#else
|
||||
int line_len;
|
||||
|
||||
bpix = panel_info.vl_bpix;
|
||||
col = panel_info.vl_col;
|
||||
row = panel_info.vl_row;
|
||||
fb_base = gd->fb_base;
|
||||
fb_size = lcd_get_size(&line_len);
|
||||
fb = (void*)gd->fb_base;
|
||||
#endif
|
||||
|
||||
switch (bpix) {
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
case VIDEO_BPP16:
|
||||
case VIDEO_BPP32:
|
||||
#else
|
||||
case LCD_COLOR32:
|
||||
case LCD_COLOR16:
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
/* So far, we only work in 16 or 32 bit mode */
|
||||
debug("WARNING: Unsupported video mode\n");
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
gopobj = calloc(1, sizeof(*gopobj));
|
||||
if (!gopobj) {
|
||||
printf("ERROR: Out of memory\n");
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
/* Hook up to the device list */
|
||||
efi_add_handle(&gopobj->header);
|
||||
|
||||
/* Fill in object data */
|
||||
ret = efi_add_protocol(&gopobj->header, &efi_gop_guid,
|
||||
&gopobj->ops);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
printf("ERROR: Failure adding GOP protocol\n");
|
||||
return ret;
|
||||
}
|
||||
gopobj->ops.query_mode = gop_query_mode;
|
||||
gopobj->ops.set_mode = gop_set_mode;
|
||||
gopobj->ops.blt = gop_blt;
|
||||
gopobj->ops.mode = &gopobj->mode;
|
||||
|
||||
gopobj->mode.max_mode = 1;
|
||||
gopobj->mode.info = &gopobj->info;
|
||||
gopobj->mode.info_size = sizeof(gopobj->info);
|
||||
|
||||
gopobj->mode.fb_base = fb_base;
|
||||
gopobj->mode.fb_size = fb_size;
|
||||
|
||||
gopobj->info.version = 0;
|
||||
gopobj->info.width = col;
|
||||
gopobj->info.height = row;
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
if (bpix == VIDEO_BPP32)
|
||||
#else
|
||||
if (bpix == LCD_COLOR32)
|
||||
#endif
|
||||
{
|
||||
gopobj->info.pixel_format = EFI_GOT_BGRA8;
|
||||
} else {
|
||||
gopobj->info.pixel_format = EFI_GOT_BITMASK;
|
||||
gopobj->info.pixel_bitmask[0] = 0xf800; /* red */
|
||||
gopobj->info.pixel_bitmask[1] = 0x07e0; /* green */
|
||||
gopobj->info.pixel_bitmask[2] = 0x001f; /* blue */
|
||||
}
|
||||
gopobj->info.pixels_per_scanline = col;
|
||||
gopobj->bpix = bpix;
|
||||
gopobj->fb = fb;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,146 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI Human Interface Infrastructure ... Configuration
|
||||
*
|
||||
* Copyright (c) 2017 Leif Lindholm
|
||||
* Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
|
||||
const efi_guid_t efi_guid_hii_config_routing_protocol
|
||||
= EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID;
|
||||
const efi_guid_t efi_guid_hii_config_access_protocol
|
||||
= EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID;
|
||||
|
||||
/*
|
||||
* EFI_HII_CONFIG_ROUTING_PROTOCOL
|
||||
*/
|
||||
|
||||
static efi_status_t EFIAPI
|
||||
extract_config(const struct efi_hii_config_routing_protocol *this,
|
||||
const efi_string_t request,
|
||||
efi_string_t *progress,
|
||||
efi_string_t *results)
|
||||
{
|
||||
EFI_ENTRY("%p, \"%ls\", %p, %p", this, request, progress, results);
|
||||
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI
|
||||
export_config(const struct efi_hii_config_routing_protocol *this,
|
||||
efi_string_t *results)
|
||||
{
|
||||
EFI_ENTRY("%p, %p", this, results);
|
||||
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI
|
||||
route_config(const struct efi_hii_config_routing_protocol *this,
|
||||
const efi_string_t configuration,
|
||||
efi_string_t *progress)
|
||||
{
|
||||
EFI_ENTRY("%p, \"%ls\", %p", this, configuration, progress);
|
||||
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI
|
||||
block_to_config(const struct efi_hii_config_routing_protocol *this,
|
||||
const efi_string_t config_request,
|
||||
const u8 *block,
|
||||
const efi_uintn_t block_size,
|
||||
efi_string_t *config,
|
||||
efi_string_t *progress)
|
||||
{
|
||||
EFI_ENTRY("%p, \"%ls\", %p, %zu, %p, %p", this, config_request,
|
||||
block, block_size, config, progress);
|
||||
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI
|
||||
config_to_block(const struct efi_hii_config_routing_protocol *this,
|
||||
const efi_string_t config_resp,
|
||||
const u8 *block,
|
||||
const efi_uintn_t *block_size,
|
||||
efi_string_t *progress)
|
||||
{
|
||||
EFI_ENTRY("%p, \"%ls\", %p, %p, %p", this, config_resp,
|
||||
block, block_size, progress);
|
||||
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI
|
||||
get_alt_config(const struct efi_hii_config_routing_protocol *this,
|
||||
const efi_string_t config_resp,
|
||||
const efi_guid_t *guid,
|
||||
const efi_string_t name,
|
||||
const struct efi_device_path *device_path,
|
||||
const efi_string_t alt_cfg_id,
|
||||
efi_string_t *alt_cfg_resp)
|
||||
{
|
||||
EFI_ENTRY("%p, \"%ls\", %pUl, \"%ls\", %p, \"%ls\", %p",
|
||||
this, config_resp, guid, name, device_path,
|
||||
alt_cfg_id, alt_cfg_resp);
|
||||
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
/*
|
||||
* EFI_HII_ACCESS_PROTOCOL
|
||||
*/
|
||||
|
||||
efi_status_t EFIAPI
|
||||
extract_config_access(const struct efi_hii_config_access_protocol *this,
|
||||
const efi_string_t request,
|
||||
efi_string_t *progress,
|
||||
efi_string_t *results)
|
||||
{
|
||||
EFI_ENTRY("%p, \"%ls\", %p, %p", this, request, progress, results);
|
||||
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
};
|
||||
|
||||
efi_status_t EFIAPI
|
||||
route_config_access(const struct efi_hii_config_access_protocol *this,
|
||||
const efi_string_t configuration,
|
||||
efi_string_t *progress)
|
||||
{
|
||||
EFI_ENTRY("%p, \"%ls\", %p", this, configuration, progress);
|
||||
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
};
|
||||
|
||||
efi_status_t EFIAPI
|
||||
form_callback(const struct efi_hii_config_access_protocol *this,
|
||||
efi_browser_action_t action,
|
||||
efi_question_id_t question_id,
|
||||
u8 type,
|
||||
union efi_ifr_type_value *value,
|
||||
efi_browser_action_request_t *action_request)
|
||||
{
|
||||
EFI_ENTRY("%p, 0x%zx, 0x%x, 0x%x, %p, %p", this, action,
|
||||
question_id, type, value, action_request);
|
||||
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
};
|
||||
|
||||
const struct efi_hii_config_routing_protocol efi_hii_config_routing = {
|
||||
.extract_config = extract_config,
|
||||
.export_config = export_config,
|
||||
.route_config = route_config,
|
||||
.block_to_config = block_to_config,
|
||||
.config_to_block = config_to_block,
|
||||
.get_alt_config = get_alt_config
|
||||
};
|
||||
|
||||
const struct efi_hii_config_access_protocol efi_hii_config_access = {
|
||||
.extract_config_access = extract_config_access,
|
||||
.route_config_access = route_config_access,
|
||||
.form_callback = form_callback
|
||||
};
|
||||
@@ -0,0 +1,344 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI image loader
|
||||
*
|
||||
* based partly on wine code
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <efi_loader.h>
|
||||
#include <pe.h>
|
||||
|
||||
const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
|
||||
const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
|
||||
const efi_guid_t efi_guid_loaded_image = EFI_LOADED_IMAGE_PROTOCOL_GUID;
|
||||
const efi_guid_t efi_guid_loaded_image_device_path =
|
||||
EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
|
||||
const efi_guid_t efi_simple_file_system_protocol_guid =
|
||||
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
|
||||
const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
|
||||
|
||||
static int machines[] = {
|
||||
#if defined(__aarch64__)
|
||||
IMAGE_FILE_MACHINE_ARM64,
|
||||
#elif defined(__arm__)
|
||||
IMAGE_FILE_MACHINE_ARM,
|
||||
IMAGE_FILE_MACHINE_THUMB,
|
||||
IMAGE_FILE_MACHINE_ARMNT,
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__)
|
||||
IMAGE_FILE_MACHINE_AMD64,
|
||||
#elif defined(__i386__)
|
||||
IMAGE_FILE_MACHINE_I386,
|
||||
#endif
|
||||
|
||||
#if defined(__riscv) && (__riscv_xlen == 32)
|
||||
IMAGE_FILE_MACHINE_RISCV32,
|
||||
#endif
|
||||
|
||||
#if defined(__riscv) && (__riscv_xlen == 64)
|
||||
IMAGE_FILE_MACHINE_RISCV64,
|
||||
#endif
|
||||
0 };
|
||||
|
||||
/**
|
||||
* efi_print_image_info() - print information about a loaded image
|
||||
*
|
||||
* If the program counter is located within the image the offset to the base
|
||||
* address is shown.
|
||||
*
|
||||
* @obj: EFI object
|
||||
* @image: loaded image
|
||||
* @pc: program counter (use NULL to suppress offset output)
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj,
|
||||
struct efi_loaded_image *image,
|
||||
void *pc)
|
||||
{
|
||||
printf("UEFI image");
|
||||
printf(" [0x%p:0x%p]",
|
||||
image->image_base, image->image_base + image->image_size - 1);
|
||||
if (pc && pc >= image->image_base &&
|
||||
pc < image->image_base + image->image_size)
|
||||
printf(" pc=0x%zx", pc - image->image_base);
|
||||
if (image->file_path)
|
||||
printf(" '%pD'", image->file_path);
|
||||
printf("\n");
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_print_image_infos() - print information about all loaded images
|
||||
*
|
||||
* @pc: program counter (use NULL to suppress offset output)
|
||||
*/
|
||||
void efi_print_image_infos(void *pc)
|
||||
{
|
||||
struct efi_object *efiobj;
|
||||
struct efi_handler *handler;
|
||||
|
||||
list_for_each_entry(efiobj, &efi_obj_list, link) {
|
||||
list_for_each_entry(handler, &efiobj->protocols, link) {
|
||||
if (!guidcmp(handler->guid, &efi_guid_loaded_image)) {
|
||||
efi_print_image_info(
|
||||
(struct efi_loaded_image_obj *)efiobj,
|
||||
handler->protocol_interface, pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_loader_relocate() - relocate UEFI binary
|
||||
*
|
||||
* @rel: pointer to the relocation table
|
||||
* @rel_size: size of the relocation table in bytes
|
||||
* @efi_reloc: actual load address of the image
|
||||
* @pref_address: preferred load address of the image
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
|
||||
unsigned long rel_size, void *efi_reloc,
|
||||
unsigned long pref_address)
|
||||
{
|
||||
unsigned long delta = (unsigned long)efi_reloc - pref_address;
|
||||
const IMAGE_BASE_RELOCATION *end;
|
||||
int i;
|
||||
|
||||
if (delta == 0)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
|
||||
while (rel < end && rel->SizeOfBlock) {
|
||||
const uint16_t *relocs = (const uint16_t *)(rel + 1);
|
||||
i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
|
||||
while (i--) {
|
||||
uint32_t offset = (uint32_t)(*relocs & 0xfff) +
|
||||
rel->VirtualAddress;
|
||||
int type = *relocs >> EFI_PAGE_SHIFT;
|
||||
uint64_t *x64 = efi_reloc + offset;
|
||||
uint32_t *x32 = efi_reloc + offset;
|
||||
uint16_t *x16 = efi_reloc + offset;
|
||||
|
||||
switch (type) {
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
break;
|
||||
case IMAGE_REL_BASED_HIGH:
|
||||
*x16 += ((uint32_t)delta) >> 16;
|
||||
break;
|
||||
case IMAGE_REL_BASED_LOW:
|
||||
*x16 += (uint16_t)delta;
|
||||
break;
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
*x32 += (uint32_t)delta;
|
||||
break;
|
||||
case IMAGE_REL_BASED_DIR64:
|
||||
*x64 += (uint64_t)delta;
|
||||
break;
|
||||
#ifdef __riscv
|
||||
case IMAGE_REL_BASED_RISCV_HI20:
|
||||
*x32 = ((*x32 & 0xfffff000) + (uint32_t)delta) |
|
||||
(*x32 & 0x00000fff);
|
||||
break;
|
||||
case IMAGE_REL_BASED_RISCV_LOW12I:
|
||||
case IMAGE_REL_BASED_RISCV_LOW12S:
|
||||
/* We know that we're 4k aligned */
|
||||
if (delta & 0xfff) {
|
||||
printf("Unsupported reloc offset\n");
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
printf("Unknown Relocation off %x type %x\n",
|
||||
offset, type);
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
relocs++;
|
||||
}
|
||||
rel = (const IMAGE_BASE_RELOCATION *)relocs;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
void __weak invalidate_icache_all(void)
|
||||
{
|
||||
/* If the system doesn't support icache_all flush, cross our fingers */
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_set_code_and_data_type() - determine the memory types to be used for code
|
||||
* and data.
|
||||
*
|
||||
* @loaded_image_info: image descriptor
|
||||
* @image_type: field Subsystem of the optional header for
|
||||
* Windows specific field
|
||||
*/
|
||||
static void efi_set_code_and_data_type(
|
||||
struct efi_loaded_image *loaded_image_info,
|
||||
uint16_t image_type)
|
||||
{
|
||||
switch (image_type) {
|
||||
case IMAGE_SUBSYSTEM_EFI_APPLICATION:
|
||||
loaded_image_info->image_code_type = EFI_LOADER_CODE;
|
||||
loaded_image_info->image_data_type = EFI_LOADER_DATA;
|
||||
break;
|
||||
case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
|
||||
loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE;
|
||||
loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA;
|
||||
break;
|
||||
case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
|
||||
case IMAGE_SUBSYSTEM_EFI_ROM:
|
||||
loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE;
|
||||
loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA;
|
||||
break;
|
||||
default:
|
||||
printf("%s: invalid image type: %u\n", __func__, image_type);
|
||||
/* Let's assume it is an application */
|
||||
loaded_image_info->image_code_type = EFI_LOADER_CODE;
|
||||
loaded_image_info->image_data_type = EFI_LOADER_DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_load_pe() - relocate EFI binary
|
||||
*
|
||||
* This function loads all sections from a PE binary into a newly reserved
|
||||
* piece of memory. On success the entry point is returned as handle->entry.
|
||||
*
|
||||
* @handle: loaded image handle
|
||||
* @efi: pointer to the EFI binary
|
||||
* @loaded_image_info: loaded image protocol
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
|
||||
struct efi_loaded_image *loaded_image_info)
|
||||
{
|
||||
IMAGE_NT_HEADERS32 *nt;
|
||||
IMAGE_DOS_HEADER *dos;
|
||||
IMAGE_SECTION_HEADER *sections;
|
||||
int num_sections;
|
||||
void *efi_reloc;
|
||||
int i;
|
||||
const IMAGE_BASE_RELOCATION *rel;
|
||||
unsigned long rel_size;
|
||||
int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
|
||||
uint64_t image_base;
|
||||
unsigned long virt_size = 0;
|
||||
int supported = 0;
|
||||
|
||||
dos = efi;
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||
printf("%s: Invalid DOS Signature\n", __func__);
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
|
||||
nt = (void *) ((char *)efi + dos->e_lfanew);
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE) {
|
||||
printf("%s: Invalid NT Signature\n", __func__);
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; machines[i]; i++)
|
||||
if (machines[i] == nt->FileHeader.Machine) {
|
||||
supported = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!supported) {
|
||||
printf("%s: Machine type 0x%04x is not supported\n",
|
||||
__func__, nt->FileHeader.Machine);
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
|
||||
/* Calculate upper virtual address boundary */
|
||||
num_sections = nt->FileHeader.NumberOfSections;
|
||||
sections = (void *)&nt->OptionalHeader +
|
||||
nt->FileHeader.SizeOfOptionalHeader;
|
||||
|
||||
for (i = num_sections - 1; i >= 0; i--) {
|
||||
IMAGE_SECTION_HEADER *sec = §ions[i];
|
||||
virt_size = max_t(unsigned long, virt_size,
|
||||
sec->VirtualAddress + sec->Misc.VirtualSize);
|
||||
}
|
||||
|
||||
/* Read 32/64bit specific header bits */
|
||||
if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
||||
IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
|
||||
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) {
|
||||
printf("%s: Could not allocate %lu bytes\n",
|
||||
__func__, virt_size);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
handle->entry = efi_reloc + opt->AddressOfEntryPoint;
|
||||
rel_size = opt->DataDirectory[rel_idx].Size;
|
||||
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
|
||||
virt_size = ALIGN(virt_size, opt->SectionAlignment);
|
||||
} else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
||||
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) {
|
||||
printf("%s: Could not allocate %lu bytes\n",
|
||||
__func__, virt_size);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
handle->entry = efi_reloc + opt->AddressOfEntryPoint;
|
||||
rel_size = opt->DataDirectory[rel_idx].Size;
|
||||
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
|
||||
virt_size = ALIGN(virt_size, opt->SectionAlignment);
|
||||
} else {
|
||||
printf("%s: Invalid optional header magic %x\n", __func__,
|
||||
nt->OptionalHeader.Magic);
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
|
||||
/* Copy PE headers */
|
||||
memcpy(efi_reloc, efi, sizeof(*dos) + sizeof(*nt)
|
||||
+ nt->FileHeader.SizeOfOptionalHeader
|
||||
+ num_sections * sizeof(IMAGE_SECTION_HEADER));
|
||||
|
||||
/* Load sections into RAM */
|
||||
for (i = num_sections - 1; i >= 0; i--) {
|
||||
IMAGE_SECTION_HEADER *sec = §ions[i];
|
||||
memset(efi_reloc + sec->VirtualAddress, 0,
|
||||
sec->Misc.VirtualSize);
|
||||
memcpy(efi_reloc + sec->VirtualAddress,
|
||||
efi + sec->PointerToRawData,
|
||||
sec->SizeOfRawData);
|
||||
}
|
||||
|
||||
/* Run through relocations */
|
||||
if (efi_loader_relocate(rel, rel_size, efi_reloc,
|
||||
(unsigned long)image_base) != EFI_SUCCESS) {
|
||||
efi_free_pages((uintptr_t) efi_reloc,
|
||||
(virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT);
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
|
||||
/* Flush cache */
|
||||
flush_cache((ulong)efi_reloc,
|
||||
ALIGN(virt_size, EFI_CACHELINE_SIZE));
|
||||
invalidate_icache_all();
|
||||
|
||||
/* Populate the loaded image interface bits */
|
||||
loaded_image_info->image_base = efi_reloc;
|
||||
loaded_image_info->image_size = virt_size;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,788 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI application memory management
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <init.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
#include <watchdog.h>
|
||||
#include <linux/list_sort.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* Magic number identifying memory allocated from pool */
|
||||
#define EFI_ALLOC_POOL_MAGIC 0x1fe67ddf6491caa2
|
||||
|
||||
efi_uintn_t efi_memory_map_key;
|
||||
|
||||
struct efi_mem_list {
|
||||
struct list_head link;
|
||||
struct efi_mem_desc desc;
|
||||
};
|
||||
|
||||
#define EFI_CARVE_NO_OVERLAP -1
|
||||
#define EFI_CARVE_LOOP_AGAIN -2
|
||||
#define EFI_CARVE_OVERLAPS_NONRAM -3
|
||||
|
||||
/* This list contains all memory map items */
|
||||
LIST_HEAD(efi_mem);
|
||||
|
||||
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
|
||||
void *efi_bounce_buffer;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct efi_pool_allocation - memory block allocated from pool
|
||||
*
|
||||
* @num_pages: number of pages allocated
|
||||
* @checksum: checksum
|
||||
* @data: allocated pool memory
|
||||
*
|
||||
* U-Boot services each UEFI AllocatePool() request as a separate
|
||||
* (multiple) page allocation. We have to track the number of pages
|
||||
* to be able to free the correct amount later.
|
||||
*
|
||||
* The checksum calculated in function checksum() is used in FreePool() to avoid
|
||||
* freeing memory not allocated by AllocatePool() and duplicate freeing.
|
||||
*
|
||||
* EFI requires 8 byte alignment for pool allocations, so we can
|
||||
* prepend each allocation with these header fields.
|
||||
*/
|
||||
struct efi_pool_allocation {
|
||||
u64 num_pages;
|
||||
u64 checksum;
|
||||
char data[] __aligned(ARCH_DMA_MINALIGN);
|
||||
};
|
||||
|
||||
/**
|
||||
* checksum() - calculate checksum for memory allocated from pool
|
||||
*
|
||||
* @alloc: allocation header
|
||||
* Return: checksum, always non-zero
|
||||
*/
|
||||
static u64 checksum(struct efi_pool_allocation *alloc)
|
||||
{
|
||||
u64 addr = (uintptr_t)alloc;
|
||||
u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
|
||||
EFI_ALLOC_POOL_MAGIC;
|
||||
if (!ret)
|
||||
++ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sorts the memory list from highest address to lowest address
|
||||
*
|
||||
* When allocating memory we should always start from the highest
|
||||
* address chunk, so sort the memory list such that the first list
|
||||
* iterator gets the highest address and goes lower from there.
|
||||
*/
|
||||
static int efi_mem_cmp(void *priv, struct list_head *a, struct list_head *b)
|
||||
{
|
||||
struct efi_mem_list *mema = list_entry(a, struct efi_mem_list, link);
|
||||
struct efi_mem_list *memb = list_entry(b, struct efi_mem_list, link);
|
||||
|
||||
if (mema->desc.physical_start == memb->desc.physical_start)
|
||||
return 0;
|
||||
else if (mema->desc.physical_start < memb->desc.physical_start)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint64_t desc_get_end(struct efi_mem_desc *desc)
|
||||
{
|
||||
return desc->physical_start + (desc->num_pages << EFI_PAGE_SHIFT);
|
||||
}
|
||||
|
||||
static void efi_mem_sort(void)
|
||||
{
|
||||
struct list_head *lhandle;
|
||||
struct efi_mem_list *prevmem = NULL;
|
||||
bool merge_again = true;
|
||||
|
||||
list_sort(NULL, &efi_mem, efi_mem_cmp);
|
||||
|
||||
/* Now merge entries that can be merged */
|
||||
while (merge_again) {
|
||||
merge_again = false;
|
||||
list_for_each(lhandle, &efi_mem) {
|
||||
struct efi_mem_list *lmem;
|
||||
struct efi_mem_desc *prev = &prevmem->desc;
|
||||
struct efi_mem_desc *cur;
|
||||
uint64_t pages;
|
||||
|
||||
lmem = list_entry(lhandle, struct efi_mem_list, link);
|
||||
if (!prevmem) {
|
||||
prevmem = lmem;
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = &lmem->desc;
|
||||
|
||||
if ((desc_get_end(cur) == prev->physical_start) &&
|
||||
(prev->type == cur->type) &&
|
||||
(prev->attribute == cur->attribute)) {
|
||||
/* There is an existing map before, reuse it */
|
||||
pages = cur->num_pages;
|
||||
prev->num_pages += pages;
|
||||
prev->physical_start -= pages << EFI_PAGE_SHIFT;
|
||||
prev->virtual_start -= pages << EFI_PAGE_SHIFT;
|
||||
list_del(&lmem->link);
|
||||
free(lmem);
|
||||
|
||||
merge_again = true;
|
||||
break;
|
||||
}
|
||||
|
||||
prevmem = lmem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** efi_mem_carve_out - unmap memory region
|
||||
*
|
||||
* @map: memory map
|
||||
* @carve_desc: memory region to unmap
|
||||
* @overlap_only_ram: the carved out region may only overlap RAM
|
||||
* Return Value: the number of overlapping pages which have been
|
||||
* removed from the map,
|
||||
* EFI_CARVE_NO_OVERLAP, if the regions don't overlap,
|
||||
* EFI_CARVE_OVERLAPS_NONRAM, if the carve and map overlap,
|
||||
* and the map contains anything but free ram
|
||||
* (only when overlap_only_ram is true),
|
||||
* EFI_CARVE_LOOP_AGAIN, if the mapping list should be
|
||||
* traversed again, as it has been altered.
|
||||
*
|
||||
* Unmaps all memory occupied by the carve_desc region from the list entry
|
||||
* pointed to by map.
|
||||
*
|
||||
* In case of EFI_CARVE_OVERLAPS_NONRAM it is the callers responsibility
|
||||
* to re-add the already carved out pages to the mapping.
|
||||
*/
|
||||
static s64 efi_mem_carve_out(struct efi_mem_list *map,
|
||||
struct efi_mem_desc *carve_desc,
|
||||
bool overlap_only_ram)
|
||||
{
|
||||
struct efi_mem_list *newmap;
|
||||
struct efi_mem_desc *map_desc = &map->desc;
|
||||
uint64_t map_start = map_desc->physical_start;
|
||||
uint64_t map_end = map_start + (map_desc->num_pages << EFI_PAGE_SHIFT);
|
||||
uint64_t carve_start = carve_desc->physical_start;
|
||||
uint64_t carve_end = carve_start +
|
||||
(carve_desc->num_pages << EFI_PAGE_SHIFT);
|
||||
|
||||
/* check whether we're overlapping */
|
||||
if ((carve_end <= map_start) || (carve_start >= map_end))
|
||||
return EFI_CARVE_NO_OVERLAP;
|
||||
|
||||
/* We're overlapping with non-RAM, warn the caller if desired */
|
||||
if (overlap_only_ram && (map_desc->type != EFI_CONVENTIONAL_MEMORY))
|
||||
return EFI_CARVE_OVERLAPS_NONRAM;
|
||||
|
||||
/* Sanitize carve_start and carve_end to lie within our bounds */
|
||||
carve_start = max(carve_start, map_start);
|
||||
carve_end = min(carve_end, map_end);
|
||||
|
||||
/* Carving at the beginning of our map? Just move it! */
|
||||
if (carve_start == map_start) {
|
||||
if (map_end == carve_end) {
|
||||
/* Full overlap, just remove map */
|
||||
list_del(&map->link);
|
||||
free(map);
|
||||
} else {
|
||||
map->desc.physical_start = carve_end;
|
||||
map->desc.virtual_start = carve_end;
|
||||
map->desc.num_pages = (map_end - carve_end)
|
||||
>> EFI_PAGE_SHIFT;
|
||||
}
|
||||
|
||||
return (carve_end - carve_start) >> EFI_PAGE_SHIFT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overlapping maps, just split the list map at carve_start,
|
||||
* it will get moved or removed in the next iteration.
|
||||
*
|
||||
* [ map_desc |__carve_start__| newmap ]
|
||||
*/
|
||||
|
||||
/* Create a new map from [ carve_start ... map_end ] */
|
||||
newmap = calloc(1, sizeof(*newmap));
|
||||
newmap->desc = map->desc;
|
||||
newmap->desc.physical_start = carve_start;
|
||||
newmap->desc.virtual_start = carve_start;
|
||||
newmap->desc.num_pages = (map_end - carve_start) >> EFI_PAGE_SHIFT;
|
||||
/* Insert before current entry (descending address order) */
|
||||
list_add_tail(&newmap->link, &map->link);
|
||||
|
||||
/* Shrink the map to [ map_start ... carve_start ] */
|
||||
map_desc->num_pages = (carve_start - map_start) >> EFI_PAGE_SHIFT;
|
||||
|
||||
return EFI_CARVE_LOOP_AGAIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_add_memory_map() - add memory area to the memory map
|
||||
*
|
||||
* @start: start address, must be a multiple of EFI_PAGE_SIZE
|
||||
* @pages: number of pages to add
|
||||
* @memory_type: type of memory added
|
||||
* @overlap_only_ram: the memory area must overlap existing
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
|
||||
bool overlap_only_ram)
|
||||
{
|
||||
struct list_head *lhandle;
|
||||
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");
|
||||
|
||||
if (memory_type >= EFI_MAX_MEMORY_TYPE)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (!pages)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
++efi_memory_map_key;
|
||||
newlist = calloc(1, sizeof(*newlist));
|
||||
newlist->desc.type = memory_type;
|
||||
newlist->desc.physical_start = start;
|
||||
newlist->desc.virtual_start = start;
|
||||
newlist->desc.num_pages = pages;
|
||||
|
||||
switch (memory_type) {
|
||||
case EFI_RUNTIME_SERVICES_CODE:
|
||||
case EFI_RUNTIME_SERVICES_DATA:
|
||||
newlist->desc.attribute = EFI_MEMORY_WB | EFI_MEMORY_RUNTIME;
|
||||
break;
|
||||
case EFI_MMAP_IO:
|
||||
newlist->desc.attribute = EFI_MEMORY_RUNTIME;
|
||||
break;
|
||||
default:
|
||||
newlist->desc.attribute = EFI_MEMORY_WB;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add our new map */
|
||||
do {
|
||||
carve_again = false;
|
||||
list_for_each(lhandle, &efi_mem) {
|
||||
struct efi_mem_list *lmem;
|
||||
s64 r;
|
||||
|
||||
lmem = list_entry(lhandle, struct efi_mem_list, link);
|
||||
r = efi_mem_carve_out(lmem, &newlist->desc,
|
||||
overlap_only_ram);
|
||||
switch (r) {
|
||||
case EFI_CARVE_OVERLAPS_NONRAM:
|
||||
/*
|
||||
* The user requested to only have RAM overlaps,
|
||||
* but we hit a non-RAM region. Error out.
|
||||
*/
|
||||
return EFI_NO_MAPPING;
|
||||
case EFI_CARVE_NO_OVERLAP:
|
||||
/* Just ignore this list entry */
|
||||
break;
|
||||
case EFI_CARVE_LOOP_AGAIN:
|
||||
/*
|
||||
* We split an entry, but need to loop through
|
||||
* the list again to actually carve it.
|
||||
*/
|
||||
carve_again = true;
|
||||
break;
|
||||
default:
|
||||
/* We carved a number of pages */
|
||||
carved_pages += r;
|
||||
carve_again = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (carve_again) {
|
||||
/* The list changed, we need to start over */
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (carve_again);
|
||||
|
||||
if (overlap_only_ram && (carved_pages != pages)) {
|
||||
/*
|
||||
* The payload wanted to have RAM overlaps, but we overlapped
|
||||
* with an unallocated region. Error out.
|
||||
*/
|
||||
return EFI_NO_MAPPING;
|
||||
}
|
||||
|
||||
/* Add our new map */
|
||||
list_add_tail(&newlist->link, &efi_mem);
|
||||
|
||||
/* 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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_check_allocated() - validate address to be freed
|
||||
*
|
||||
* Check that the address is within allocated memory:
|
||||
*
|
||||
* * 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;
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* Prealign input max address, so we simplify our matching
|
||||
* logic below and can just reuse it as return pointer.
|
||||
*/
|
||||
max_addr &= ~EFI_PAGE_MASK;
|
||||
|
||||
list_for_each(lhandle, &efi_mem) {
|
||||
struct efi_mem_list *lmem = list_entry(lhandle,
|
||||
struct efi_mem_list, link);
|
||||
struct efi_mem_desc *desc = &lmem->desc;
|
||||
uint64_t desc_len = desc->num_pages << EFI_PAGE_SHIFT;
|
||||
uint64_t desc_end = desc->physical_start + desc_len;
|
||||
uint64_t curmax = min(max_addr, desc_end);
|
||||
uint64_t ret = curmax - len;
|
||||
|
||||
/* We only take memory from free RAM */
|
||||
if (desc->type != EFI_CONVENTIONAL_MEMORY)
|
||||
continue;
|
||||
|
||||
/* Out of bounds for max_addr */
|
||||
if ((ret + len) > max_addr)
|
||||
continue;
|
||||
|
||||
/* Out of bounds for upper map limit */
|
||||
if ((ret + len) > desc_end)
|
||||
continue;
|
||||
|
||||
/* Out of bounds for lower map limit */
|
||||
if (ret < desc->physical_start)
|
||||
continue;
|
||||
|
||||
/* Return the highest address in this map within bounds */
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate memory pages.
|
||||
*
|
||||
* @type type of allocation to be performed
|
||||
* @memory_type usage type of the allocated memory
|
||||
* @pages number of pages to be allocated
|
||||
* @memory allocated memory
|
||||
* @return status code
|
||||
*/
|
||||
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 ret;
|
||||
uint64_t addr;
|
||||
|
||||
/* Check import parameters */
|
||||
if (memory_type >= EFI_PERSISTENT_MEMORY_TYPE &&
|
||||
memory_type <= 0x6FFFFFFF)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (!memory)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
switch (type) {
|
||||
case EFI_ALLOCATE_ANY_PAGES:
|
||||
/* Any page */
|
||||
addr = efi_find_free_memory(len, -1ULL);
|
||||
if (!addr)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
break;
|
||||
case EFI_ALLOCATE_MAX_ADDRESS:
|
||||
/* Max address */
|
||||
addr = efi_find_free_memory(len, *memory);
|
||||
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 */
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Reserve that map in our memory maps */
|
||||
if (efi_add_memory_map(addr, pages, memory_type, true) != EFI_SUCCESS)
|
||||
/* Map would overlap, bail out */
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
*memory = addr;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
void *efi_alloc(uint64_t len, int memory_type)
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
uint64_t pages = efi_size_in_pages(len);
|
||||
efi_status_t r;
|
||||
|
||||
r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, memory_type, pages,
|
||||
&ret);
|
||||
if (r == EFI_SUCCESS)
|
||||
return (void*)(uintptr_t)ret;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_free_pages() - free memory pages
|
||||
*
|
||||
* @memory: start of the memory area to be freed
|
||||
* @pages: number of pages to be freed
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
ret = efi_check_allocated(memory, true);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
|
||||
/* Sanity check */
|
||||
if (!memory || (memory & EFI_PAGE_MASK) || !pages) {
|
||||
printf("%s: illegal free 0x%llx, 0x%zx\n", __func__,
|
||||
memory, pages);
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ret = efi_add_memory_map(memory, pages, EFI_CONVENTIONAL_MEMORY, false);
|
||||
/* Merging of adjacent free regions is missing */
|
||||
|
||||
if (ret != EFI_SUCCESS)
|
||||
return EFI_NOT_FOUND;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_allocate_pool - allocate memory from pool
|
||||
*
|
||||
* @pool_type: type of the pool from which memory is to be allocated
|
||||
* @size: number of bytes to be allocated
|
||||
* @buffer: allocated memory
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer)
|
||||
{
|
||||
efi_status_t r;
|
||||
u64 addr;
|
||||
struct efi_pool_allocation *alloc;
|
||||
u64 num_pages = efi_size_in_pages(size +
|
||||
sizeof(struct efi_pool_allocation));
|
||||
|
||||
if (!buffer)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (size == 0) {
|
||||
*buffer = NULL;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
|
||||
&addr);
|
||||
if (r == EFI_SUCCESS) {
|
||||
alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
|
||||
alloc->num_pages = num_pages;
|
||||
alloc->checksum = checksum(alloc);
|
||||
*buffer = alloc->data;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_free_pool() - free memory from pool
|
||||
*
|
||||
* @buffer: start of memory to be freed
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_free_pool(void *buffer)
|
||||
{
|
||||
efi_status_t ret;
|
||||
struct efi_pool_allocation *alloc;
|
||||
|
||||
if (!buffer)
|
||||
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);
|
||||
|
||||
/* Check that this memory was allocated by efi_allocate_pool() */
|
||||
if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
|
||||
alloc->checksum != checksum(alloc)) {
|
||||
printf("%s: illegal free 0x%p\n", __func__, buffer);
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
/* Avoid double free */
|
||||
alloc->checksum = 0;
|
||||
|
||||
ret = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get map describing memory usage.
|
||||
*
|
||||
* @memory_map_size on entry the size, in bytes, of the memory map buffer,
|
||||
* on exit the size of the copied memory map
|
||||
* @memory_map buffer to which the memory map is written
|
||||
* @map_key key for the memory map
|
||||
* @descriptor_size size of an individual memory descriptor
|
||||
* @descriptor_version version number of the memory descriptor structure
|
||||
* @return status code
|
||||
*/
|
||||
efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
|
||||
struct efi_mem_desc *memory_map,
|
||||
efi_uintn_t *map_key,
|
||||
efi_uintn_t *descriptor_size,
|
||||
uint32_t *descriptor_version)
|
||||
{
|
||||
efi_uintn_t map_size = 0;
|
||||
int map_entries = 0;
|
||||
struct list_head *lhandle;
|
||||
efi_uintn_t provided_map_size;
|
||||
|
||||
if (!memory_map_size)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
provided_map_size = *memory_map_size;
|
||||
|
||||
list_for_each(lhandle, &efi_mem)
|
||||
map_entries++;
|
||||
|
||||
map_size = map_entries * sizeof(struct efi_mem_desc);
|
||||
|
||||
*memory_map_size = map_size;
|
||||
|
||||
if (provided_map_size < map_size)
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
|
||||
if (!memory_map)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (descriptor_size)
|
||||
*descriptor_size = sizeof(struct efi_mem_desc);
|
||||
|
||||
if (descriptor_version)
|
||||
*descriptor_version = EFI_MEMORY_DESCRIPTOR_VERSION;
|
||||
|
||||
/* Copy list into array */
|
||||
/* Return the list in ascending order */
|
||||
memory_map = &memory_map[map_entries - 1];
|
||||
list_for_each(lhandle, &efi_mem) {
|
||||
struct efi_mem_list *lmem;
|
||||
|
||||
lmem = list_entry(lhandle, struct efi_mem_list, link);
|
||||
*memory_map = lmem->desc;
|
||||
memory_map--;
|
||||
}
|
||||
|
||||
if (map_key)
|
||||
*map_key = efi_memory_map_key;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_add_conventional_memory_map() - add a RAM memory area to the map
|
||||
*
|
||||
* @ram_start: start address of a RAM memory area
|
||||
* @ram_end: end address of a RAM memory area
|
||||
* @ram_top: max address to be used as conventional memory
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_add_conventional_memory_map(u64 ram_start, u64 ram_end,
|
||||
u64 ram_top)
|
||||
{
|
||||
u64 pages;
|
||||
|
||||
/* Remove partial pages */
|
||||
ram_end &= ~EFI_PAGE_MASK;
|
||||
ram_start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
|
||||
|
||||
if (ram_end <= ram_start) {
|
||||
/* Invalid mapping */
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT;
|
||||
|
||||
efi_add_memory_map(ram_start, pages,
|
||||
EFI_CONVENTIONAL_MEMORY, false);
|
||||
|
||||
/*
|
||||
* Boards may indicate to the U-Boot memory core that they
|
||||
* can not support memory above ram_top. Let's honor this
|
||||
* in the efi_loader subsystem too by declaring any memory
|
||||
* above ram_top as "already occupied by firmware".
|
||||
*/
|
||||
if (ram_top < ram_start) {
|
||||
/* ram_top is before this region, reserve all */
|
||||
efi_add_memory_map(ram_start, pages,
|
||||
EFI_BOOT_SERVICES_DATA, true);
|
||||
} else if ((ram_top >= ram_start) && (ram_top < ram_end)) {
|
||||
/* ram_top is inside this region, reserve parts */
|
||||
pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT;
|
||||
|
||||
efi_add_memory_map(ram_top, pages,
|
||||
EFI_BOOT_SERVICES_DATA, true);
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
__weak void efi_add_known_memory(void)
|
||||
{
|
||||
u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* ram_top is just outside mapped memory. So use an offset of one for
|
||||
* mapping the sandbox address.
|
||||
*/
|
||||
ram_top = (uintptr_t)map_sysmem(ram_top - 1, 0) + 1;
|
||||
|
||||
/* Fix for 32bit targets with ram_top at 4G */
|
||||
if (!ram_top)
|
||||
ram_top = 0x100000000ULL;
|
||||
|
||||
/* Add RAM */
|
||||
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
|
||||
u64 ram_end, ram_start;
|
||||
|
||||
ram_start = (uintptr_t)map_sysmem(gd->bd->bi_dram[i].start, 0);
|
||||
ram_end = ram_start + gd->bd->bi_dram[i].size;
|
||||
|
||||
efi_add_conventional_memory_map(ram_start, ram_end, ram_top);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add memory regions for U-Boot's memory and for the runtime services code */
|
||||
static void add_u_boot_and_runtime(void)
|
||||
{
|
||||
unsigned long runtime_start, runtime_end, runtime_pages;
|
||||
unsigned long runtime_mask = EFI_PAGE_MASK;
|
||||
unsigned long uboot_start, uboot_pages;
|
||||
unsigned long uboot_stack_size = 16 * 1024 * 1024;
|
||||
|
||||
/* Add U-Boot */
|
||||
uboot_start = ((uintptr_t)map_sysmem(gd->start_addr_sp, 0) -
|
||||
uboot_stack_size) & ~EFI_PAGE_MASK;
|
||||
uboot_pages = ((uintptr_t)map_sysmem(gd->ram_top - 1, 0) -
|
||||
uboot_start + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
|
||||
efi_add_memory_map(uboot_start, uboot_pages, EFI_LOADER_DATA, false);
|
||||
|
||||
#if defined(__aarch64__)
|
||||
/*
|
||||
* Runtime Services must be 64KiB aligned according to the
|
||||
* "AArch64 Platforms" section in the UEFI spec (2.7+).
|
||||
*/
|
||||
|
||||
runtime_mask = SZ_64K - 1;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Add Runtime Services. We mark surrounding boottime code as runtime as
|
||||
* well to fulfill the runtime alignment constraints but avoid padding.
|
||||
*/
|
||||
runtime_start = (ulong)&__efi_runtime_start & ~runtime_mask;
|
||||
runtime_end = (ulong)&__efi_runtime_stop;
|
||||
runtime_end = (runtime_end + runtime_mask) & ~runtime_mask;
|
||||
runtime_pages = (runtime_end - runtime_start) >> EFI_PAGE_SHIFT;
|
||||
efi_add_memory_map(runtime_start, runtime_pages,
|
||||
EFI_RUNTIME_SERVICES_CODE, false);
|
||||
}
|
||||
|
||||
int efi_memory_init(void)
|
||||
{
|
||||
efi_add_known_memory();
|
||||
|
||||
add_u_boot_and_runtime();
|
||||
|
||||
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
|
||||
/* Request a 32bit 64MB bounce buffer region */
|
||||
uint64_t efi_bounce_buffer_addr = 0xffffffff;
|
||||
|
||||
if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA,
|
||||
(64 * 1024 * 1024) >> EFI_PAGE_SHIFT,
|
||||
&efi_bounce_buffer_addr) != EFI_SUCCESS)
|
||||
return -1;
|
||||
|
||||
efi_bounce_buffer = (void*)(uintptr_t)efi_bounce_buffer_addr;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,946 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Simple network protocol
|
||||
* PXE base code protocol
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*
|
||||
* The simple network protocol has the following statuses and services
|
||||
* to move between them:
|
||||
*
|
||||
* Start(): EfiSimpleNetworkStopped -> EfiSimpleNetworkStarted
|
||||
* Initialize(): EfiSimpleNetworkStarted -> EfiSimpleNetworkInitialized
|
||||
* Shutdown(): EfiSimpleNetworkInitialized -> EfiSimpleNetworkStarted
|
||||
* Stop(): EfiSimpleNetworkStarted -> EfiSimpleNetworkStopped
|
||||
* Reset(): EfiSimpleNetworkInitialized -> EfiSimpleNetworkInitialized
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <malloc.h>
|
||||
|
||||
static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
|
||||
static const efi_guid_t efi_pxe_base_code_protocol_guid =
|
||||
EFI_PXE_BASE_CODE_PROTOCOL_GUID;
|
||||
static struct efi_pxe_packet *dhcp_ack;
|
||||
static bool new_rx_packet;
|
||||
static void *new_tx_packet;
|
||||
static void *transmit_buffer;
|
||||
|
||||
/*
|
||||
* The notification function of this event is called in every timer cycle
|
||||
* to check if a new network packet has been received.
|
||||
*/
|
||||
static struct efi_event *network_timer_event;
|
||||
/*
|
||||
* This event is signaled when a packet has been received.
|
||||
*/
|
||||
static struct efi_event *wait_for_packet;
|
||||
|
||||
/**
|
||||
* struct efi_net_obj - EFI object representing a network interface
|
||||
*
|
||||
* @header: EFI object header
|
||||
* @net: simple network protocol interface
|
||||
* @net_mode: status of the network interface
|
||||
* @pxe: PXE base code protocol interface
|
||||
* @pxe_mode: status of the PXE base code protocol
|
||||
*/
|
||||
struct efi_net_obj {
|
||||
struct efi_object header;
|
||||
struct efi_simple_network net;
|
||||
struct efi_simple_network_mode net_mode;
|
||||
struct efi_pxe_base_code_protocol pxe;
|
||||
struct efi_pxe_mode pxe_mode;
|
||||
};
|
||||
|
||||
/*
|
||||
* efi_net_start() - start the network interface
|
||||
*
|
||||
* This function implements the Start service of the
|
||||
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
|
||||
* (UEFI) specification for details.
|
||||
*
|
||||
* @this: pointer to the protocol instance
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p", this);
|
||||
|
||||
/* Check parameters */
|
||||
if (!this) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (this->mode->state != EFI_NETWORK_STOPPED) {
|
||||
ret = EFI_ALREADY_STARTED;
|
||||
} else {
|
||||
this->int_status = 0;
|
||||
wait_for_packet->is_signaled = false;
|
||||
this->mode->state = EFI_NETWORK_STARTED;
|
||||
}
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* efi_net_stop() - stop the network interface
|
||||
*
|
||||
* This function implements the Stop service of the
|
||||
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
|
||||
* (UEFI) specification for details.
|
||||
*
|
||||
* @this: pointer to the protocol instance
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p", this);
|
||||
|
||||
/* Check parameters */
|
||||
if (!this) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (this->mode->state == EFI_NETWORK_STOPPED) {
|
||||
ret = EFI_NOT_STARTED;
|
||||
} else {
|
||||
/* Disable hardware and put it into the reset state */
|
||||
eth_halt();
|
||||
this->mode->state = EFI_NETWORK_STOPPED;
|
||||
}
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* efi_net_initialize() - initialize the network interface
|
||||
*
|
||||
* This function implements the Initialize service of the
|
||||
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
|
||||
* (UEFI) specification for details.
|
||||
*
|
||||
* @this: pointer to the protocol instance
|
||||
* @extra_rx: extra receive buffer to be allocated
|
||||
* @extra_tx: extra transmit buffer to be allocated
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
|
||||
ulong extra_rx, ulong extra_tx)
|
||||
{
|
||||
int ret;
|
||||
efi_status_t r = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
|
||||
|
||||
/* Check parameters */
|
||||
if (!this) {
|
||||
r = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (this->mode->state) {
|
||||
case EFI_NETWORK_INITIALIZED:
|
||||
case EFI_NETWORK_STARTED:
|
||||
break;
|
||||
default:
|
||||
r = EFI_NOT_STARTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Setup packet buffers */
|
||||
net_init();
|
||||
/* Disable hardware and put it into the reset state */
|
||||
eth_halt();
|
||||
/* Set current device according to environment variables */
|
||||
eth_set_current();
|
||||
/* Get hardware ready for send and receive operations */
|
||||
ret = eth_init();
|
||||
if (ret < 0) {
|
||||
eth_halt();
|
||||
this->mode->state = EFI_NETWORK_STOPPED;
|
||||
r = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
} else {
|
||||
this->int_status = 0;
|
||||
wait_for_packet->is_signaled = false;
|
||||
this->mode->state = EFI_NETWORK_INITIALIZED;
|
||||
}
|
||||
out:
|
||||
return EFI_EXIT(r);
|
||||
}
|
||||
|
||||
/*
|
||||
* efi_net_reset() - reinitialize the network interface
|
||||
*
|
||||
* This function implements the Reset service of the
|
||||
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
|
||||
* (UEFI) specification for details.
|
||||
*
|
||||
* @this: pointer to the protocol instance
|
||||
* @extended_verification: execute exhaustive verification
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
|
||||
int extended_verification)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
EFI_ENTRY("%p, %x", this, extended_verification);
|
||||
|
||||
/* Check parameters */
|
||||
if (!this) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (this->mode->state) {
|
||||
case EFI_NETWORK_INITIALIZED:
|
||||
break;
|
||||
case EFI_NETWORK_STOPPED:
|
||||
ret = EFI_NOT_STARTED;
|
||||
goto out;
|
||||
default:
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
this->mode->state = EFI_NETWORK_STARTED;
|
||||
ret = EFI_CALL(efi_net_initialize(this, 0, 0));
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* efi_net_shutdown() - shut down the network interface
|
||||
*
|
||||
* This function implements the Shutdown service of the
|
||||
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
|
||||
* (UEFI) specification for details.
|
||||
*
|
||||
* @this: pointer to the protocol instance
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p", this);
|
||||
|
||||
/* Check parameters */
|
||||
if (!this) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (this->mode->state) {
|
||||
case EFI_NETWORK_INITIALIZED:
|
||||
break;
|
||||
case EFI_NETWORK_STOPPED:
|
||||
ret = EFI_NOT_STARTED;
|
||||
goto out;
|
||||
default:
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
eth_halt();
|
||||
this->int_status = 0;
|
||||
wait_for_packet->is_signaled = false;
|
||||
this->mode->state = EFI_NETWORK_STARTED;
|
||||
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* efi_net_receive_filters() - mange multicast receive filters
|
||||
*
|
||||
* This function implements the ReceiveFilters service of the
|
||||
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
|
||||
* (UEFI) specification for details.
|
||||
*
|
||||
* @this: pointer to the protocol instance
|
||||
* @enable: bit mask of receive filters to enable
|
||||
* @disable: bit mask of receive filters to disable
|
||||
* @reset_mcast_filter: true resets contents of the filters
|
||||
* @mcast_filter_count: number of hardware MAC addresses in the new filters list
|
||||
* @mcast_filter: list of new filters
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_receive_filters
|
||||
(struct efi_simple_network *this, u32 enable, u32 disable,
|
||||
int reset_mcast_filter, ulong mcast_filter_count,
|
||||
struct efi_mac_address *mcast_filter)
|
||||
{
|
||||
EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable,
|
||||
reset_mcast_filter, mcast_filter_count, mcast_filter);
|
||||
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/*
|
||||
* efi_net_station_address() - set the hardware MAC address
|
||||
*
|
||||
* This function implements the StationAddress service of the
|
||||
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
|
||||
* (UEFI) specification for details.
|
||||
*
|
||||
* @this: pointer to the protocol instance
|
||||
* @reset: if true reset the address to default
|
||||
* @new_mac: new MAC address
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_station_address
|
||||
(struct efi_simple_network *this, int reset,
|
||||
struct efi_mac_address *new_mac)
|
||||
{
|
||||
EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
|
||||
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/*
|
||||
* efi_net_statistics() - reset or collect statistics of the network interface
|
||||
*
|
||||
* This function implements the Statistics service of the
|
||||
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
|
||||
* (UEFI) specification for details.
|
||||
*
|
||||
* @this: pointer to the protocol instance
|
||||
* @reset: if true, the statistics are reset
|
||||
* @stat_size: size of the statistics table
|
||||
* @stat_table: table to receive the statistics
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
|
||||
int reset, ulong *stat_size,
|
||||
void *stat_table)
|
||||
{
|
||||
EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table);
|
||||
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/*
|
||||
* efi_net_mcastiptomac() - translate multicast IP address to MAC address
|
||||
*
|
||||
* This function implements the MCastIPtoMAC service of the
|
||||
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
|
||||
* (UEFI) specification for details.
|
||||
*
|
||||
* @this: pointer to the protocol instance
|
||||
* @ipv6: true if the IP address is an IPv6 address
|
||||
* @ip: IP address
|
||||
* @mac: MAC address
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
|
||||
int ipv6,
|
||||
struct efi_ip_address *ip,
|
||||
struct efi_mac_address *mac)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac);
|
||||
|
||||
if (!this || !ip || !mac) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ipv6) {
|
||||
ret = EFI_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Multi-cast addresses are in the range 224.0.0.0 - 239.255.255.255 */
|
||||
if ((ip->ip_addr[0] & 0xf0) != 0xe0) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
};
|
||||
|
||||
switch (this->mode->state) {
|
||||
case EFI_NETWORK_INITIALIZED:
|
||||
case EFI_NETWORK_STARTED:
|
||||
break;
|
||||
default:
|
||||
ret = EFI_NOT_STARTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(mac, 0, sizeof(struct efi_mac_address));
|
||||
|
||||
/*
|
||||
* Copy lower 23 bits of IPv4 multi-cast address
|
||||
* RFC 1112, RFC 7042 2.1.1.
|
||||
*/
|
||||
mac->mac_addr[0] = 0x01;
|
||||
mac->mac_addr[1] = 0x00;
|
||||
mac->mac_addr[2] = 0x5E;
|
||||
mac->mac_addr[3] = ip->ip_addr[1] & 0x7F;
|
||||
mac->mac_addr[4] = ip->ip_addr[2];
|
||||
mac->mac_addr[5] = ip->ip_addr[3];
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_net_nvdata() - read or write NVRAM
|
||||
*
|
||||
* This function implements the GetStatus service of the Simple Network
|
||||
* Protocol. See the UEFI spec for details.
|
||||
*
|
||||
* @this: the instance of the Simple Network Protocol
|
||||
* @read_write: true for read, false for write
|
||||
* @offset: offset in NVRAM
|
||||
* @buffer_size: size of buffer
|
||||
* @buffer: buffer
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this,
|
||||
int read_write, ulong offset,
|
||||
ulong buffer_size, char *buffer)
|
||||
{
|
||||
EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size,
|
||||
buffer);
|
||||
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_net_get_status() - get interrupt status
|
||||
*
|
||||
* This function implements the GetStatus service of the Simple Network
|
||||
* Protocol. See the UEFI spec for details.
|
||||
*
|
||||
* @this: the instance of the Simple Network Protocol
|
||||
* @int_status: interface status
|
||||
* @txbuf: transmission buffer
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
|
||||
u32 *int_status, void **txbuf)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
|
||||
|
||||
efi_timer_check();
|
||||
|
||||
/* Check parameters */
|
||||
if (!this) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (this->mode->state) {
|
||||
case EFI_NETWORK_STOPPED:
|
||||
ret = EFI_NOT_STARTED;
|
||||
goto out;
|
||||
case EFI_NETWORK_STARTED:
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (int_status) {
|
||||
*int_status = this->int_status;
|
||||
this->int_status = 0;
|
||||
}
|
||||
if (txbuf)
|
||||
*txbuf = new_tx_packet;
|
||||
|
||||
new_tx_packet = NULL;
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_net_transmit() - transmit a packet
|
||||
*
|
||||
* This function implements the Transmit service of the Simple Network Protocol.
|
||||
* See the UEFI spec for details.
|
||||
*
|
||||
* @this: the instance of the Simple Network Protocol
|
||||
* @header_size: size of the media header
|
||||
* @buffer_size: size of the buffer to receive the packet
|
||||
* @buffer: buffer to receive the packet
|
||||
* @src_addr: source hardware MAC address
|
||||
* @dest_addr: destination hardware MAC address
|
||||
* @protocol: type of header to build
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_transmit
|
||||
(struct efi_simple_network *this, size_t header_size,
|
||||
size_t buffer_size, void *buffer,
|
||||
struct efi_mac_address *src_addr,
|
||||
struct efi_mac_address *dest_addr, u16 *protocol)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
|
||||
(unsigned long)header_size, (unsigned long)buffer_size,
|
||||
buffer, src_addr, dest_addr, protocol);
|
||||
|
||||
efi_timer_check();
|
||||
|
||||
/* Check parameters */
|
||||
if (!this || !buffer) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We do not support jumbo packets */
|
||||
if (buffer_size > PKTSIZE_ALIGN) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* At least the IP header has to fit into the buffer */
|
||||
if (buffer_size < this->mode->media_header_size) {
|
||||
ret = EFI_BUFFER_TOO_SMALL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Support VLANs. Use net_set_ether() for copying the header. Use a
|
||||
* U_BOOT_ENV_CALLBACK to update the media header size.
|
||||
*/
|
||||
if (header_size) {
|
||||
struct ethernet_hdr *header = buffer;
|
||||
|
||||
if (!dest_addr || !protocol ||
|
||||
header_size != this->mode->media_header_size) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
if (!src_addr)
|
||||
src_addr = &this->mode->current_address;
|
||||
|
||||
memcpy(header->et_dest, dest_addr, ARP_HLEN);
|
||||
memcpy(header->et_src, src_addr, ARP_HLEN);
|
||||
header->et_protlen = htons(*protocol);
|
||||
}
|
||||
|
||||
switch (this->mode->state) {
|
||||
case EFI_NETWORK_STOPPED:
|
||||
ret = EFI_NOT_STARTED;
|
||||
goto out;
|
||||
case EFI_NETWORK_STARTED:
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Ethernet packets always fit, just bounce */
|
||||
memcpy(transmit_buffer, buffer, buffer_size);
|
||||
net_send_packet(transmit_buffer, buffer_size);
|
||||
|
||||
new_tx_packet = buffer;
|
||||
this->int_status |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_net_receive() - receive a packet from a network interface
|
||||
*
|
||||
* This function implements the Receive service of the Simple Network Protocol.
|
||||
* See the UEFI spec for details.
|
||||
*
|
||||
* @this: the instance of the Simple Network Protocol
|
||||
* @header_size: size of the media header
|
||||
* @buffer_size: size of the buffer to receive the packet
|
||||
* @buffer: buffer to receive the packet
|
||||
* @src_addr: source MAC address
|
||||
* @dest_addr: destination MAC address
|
||||
* @protocol: protocol
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_net_receive
|
||||
(struct efi_simple_network *this, size_t *header_size,
|
||||
size_t *buffer_size, void *buffer,
|
||||
struct efi_mac_address *src_addr,
|
||||
struct efi_mac_address *dest_addr, u16 *protocol)
|
||||
{
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
struct ethernet_hdr *eth_hdr;
|
||||
size_t hdr_size = sizeof(struct ethernet_hdr);
|
||||
u16 protlen;
|
||||
|
||||
EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
|
||||
buffer_size, buffer, src_addr, dest_addr, protocol);
|
||||
|
||||
/* Execute events */
|
||||
efi_timer_check();
|
||||
|
||||
/* Check parameters */
|
||||
if (!this || !buffer || !buffer_size) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (this->mode->state) {
|
||||
case EFI_NETWORK_STOPPED:
|
||||
ret = EFI_NOT_STARTED;
|
||||
goto out;
|
||||
case EFI_NETWORK_STARTED:
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!new_rx_packet) {
|
||||
ret = EFI_NOT_READY;
|
||||
goto out;
|
||||
}
|
||||
/* Fill export parameters */
|
||||
eth_hdr = (struct ethernet_hdr *)net_rx_packet;
|
||||
protlen = ntohs(eth_hdr->et_protlen);
|
||||
if (protlen == 0x8100) {
|
||||
hdr_size += 4;
|
||||
protlen = ntohs(*(u16 *)&net_rx_packet[hdr_size - 2]);
|
||||
}
|
||||
if (header_size)
|
||||
*header_size = hdr_size;
|
||||
if (dest_addr)
|
||||
memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN);
|
||||
if (src_addr)
|
||||
memcpy(src_addr, eth_hdr->et_src, ARP_HLEN);
|
||||
if (protocol)
|
||||
*protocol = protlen;
|
||||
if (*buffer_size < net_rx_packet_len) {
|
||||
/* Packet doesn't fit, try again with bigger buffer */
|
||||
*buffer_size = net_rx_packet_len;
|
||||
ret = EFI_BUFFER_TOO_SMALL;
|
||||
goto out;
|
||||
}
|
||||
/* Copy packet */
|
||||
memcpy(buffer, net_rx_packet, net_rx_packet_len);
|
||||
*buffer_size = net_rx_packet_len;
|
||||
new_rx_packet = 0;
|
||||
this->int_status &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_net_set_dhcp_ack() - take note of a selected DHCP IP address
|
||||
*
|
||||
* This function is called by dhcp_handler().
|
||||
*
|
||||
* @pkt: packet received by dhcp_handler()
|
||||
* @len: length of the packet received
|
||||
*/
|
||||
void efi_net_set_dhcp_ack(void *pkt, int len)
|
||||
{
|
||||
int maxsize = sizeof(*dhcp_ack);
|
||||
|
||||
if (!dhcp_ack)
|
||||
dhcp_ack = malloc(maxsize);
|
||||
|
||||
memcpy(dhcp_ack, pkt, min(len, maxsize));
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_net_push() - callback for received network packet
|
||||
*
|
||||
* This function is called when a network packet is received by eth_rx().
|
||||
*
|
||||
* @pkt: network packet
|
||||
* @len: length
|
||||
*/
|
||||
static void efi_net_push(void *pkt, int len)
|
||||
{
|
||||
new_rx_packet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_network_timer_notify() - check if a new network packet has been received
|
||||
*
|
||||
* This notification function is called in every timer cycle.
|
||||
*
|
||||
* @event: the event for which this notification function is registered
|
||||
* @context: event context - not used in this function
|
||||
*/
|
||||
static void EFIAPI efi_network_timer_notify(struct efi_event *event,
|
||||
void *context)
|
||||
{
|
||||
struct efi_simple_network *this = (struct efi_simple_network *)context;
|
||||
|
||||
EFI_ENTRY("%p, %p", event, context);
|
||||
|
||||
/*
|
||||
* Some network drivers do not support calling eth_rx() before
|
||||
* initialization.
|
||||
*/
|
||||
if (!this || this->mode->state != EFI_NETWORK_INITIALIZED)
|
||||
goto out;
|
||||
|
||||
if (!new_rx_packet) {
|
||||
push_packet = efi_net_push;
|
||||
eth_rx();
|
||||
push_packet = NULL;
|
||||
if (new_rx_packet) {
|
||||
/* Check that we at least received an Ethernet header */
|
||||
if (net_rx_packet_len >=
|
||||
sizeof(struct ethernet_hdr)) {
|
||||
this->int_status |=
|
||||
EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
|
||||
wait_for_packet->is_signaled = true;
|
||||
} else {
|
||||
new_rx_packet = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_start(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
u8 use_ipv6)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_stop(
|
||||
struct efi_pxe_base_code_protocol *this)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_dhcp(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
u8 sort_offers)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_discover(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
u16 type, u16 *layer, u8 bis,
|
||||
struct efi_pxe_base_code_discover_info *info)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_mtftp(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
u32 operation, void *buffer_ptr,
|
||||
u8 overwrite, efi_uintn_t *buffer_size,
|
||||
struct efi_ip_address server_ip, char *filename,
|
||||
struct efi_pxe_base_code_mtftp_info *info,
|
||||
u8 dont_use_buffer)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_udp_write(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
u16 op_flags, struct efi_ip_address *dest_ip,
|
||||
u16 *dest_port,
|
||||
struct efi_ip_address *gateway_ip,
|
||||
struct efi_ip_address *src_ip, u16 *src_port,
|
||||
efi_uintn_t *header_size, void *header_ptr,
|
||||
efi_uintn_t *buffer_size, void *buffer_ptr)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_udp_read(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
u16 op_flags, struct efi_ip_address *dest_ip,
|
||||
u16 *dest_port, struct efi_ip_address *src_ip,
|
||||
u16 *src_port, efi_uintn_t *header_size,
|
||||
void *header_ptr, efi_uintn_t *buffer_size,
|
||||
void *buffer_ptr)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_set_ip_filter(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
struct efi_pxe_base_code_filter *new_filter)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_arp(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
struct efi_ip_address *ip_addr,
|
||||
struct efi_mac_address *mac_addr)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_set_parameters(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
u8 *new_auto_arp, u8 *new_send_guid,
|
||||
u8 *new_ttl, u8 *new_tos,
|
||||
u8 *new_make_callback)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_set_station_ip(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
struct efi_ip_address *new_station_ip,
|
||||
struct efi_ip_address *new_subnet_mask)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_pxe_base_code_set_packets(
|
||||
struct efi_pxe_base_code_protocol *this,
|
||||
u8 *new_dhcp_discover_valid,
|
||||
u8 *new_dhcp_ack_received,
|
||||
u8 *new_proxy_offer_received,
|
||||
u8 *new_pxe_discover_valid,
|
||||
u8 *new_pxe_reply_received,
|
||||
u8 *new_pxe_bis_reply_received,
|
||||
EFI_PXE_BASE_CODE_PACKET *new_dchp_discover,
|
||||
EFI_PXE_BASE_CODE_PACKET *new_dhcp_acc,
|
||||
EFI_PXE_BASE_CODE_PACKET *new_proxy_offer,
|
||||
EFI_PXE_BASE_CODE_PACKET *new_pxe_discover,
|
||||
EFI_PXE_BASE_CODE_PACKET *new_pxe_reply,
|
||||
EFI_PXE_BASE_CODE_PACKET *new_pxe_bis_reply)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_net_register() - register the simple network protocol
|
||||
*
|
||||
* This gets called from do_bootefi_exec().
|
||||
*/
|
||||
efi_status_t efi_net_register(void)
|
||||
{
|
||||
struct efi_net_obj *netobj = NULL;
|
||||
efi_status_t r;
|
||||
|
||||
if (!eth_get_dev()) {
|
||||
/* No network device active, don't expose any */
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/* We only expose the "active" network device, so one is enough */
|
||||
netobj = calloc(1, sizeof(*netobj));
|
||||
if (!netobj)
|
||||
goto out_of_resources;
|
||||
|
||||
/* Allocate an aligned transmit buffer */
|
||||
transmit_buffer = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
|
||||
if (!transmit_buffer)
|
||||
goto out_of_resources;
|
||||
transmit_buffer = (void *)ALIGN((uintptr_t)transmit_buffer, PKTALIGN);
|
||||
|
||||
/* Hook net up to the device list */
|
||||
efi_add_handle(&netobj->header);
|
||||
|
||||
/* Fill in object data */
|
||||
r = efi_add_protocol(&netobj->header, &efi_net_guid,
|
||||
&netobj->net);
|
||||
if (r != EFI_SUCCESS)
|
||||
goto failure_to_add_protocol;
|
||||
r = efi_add_protocol(&netobj->header, &efi_guid_device_path,
|
||||
efi_dp_from_eth());
|
||||
if (r != EFI_SUCCESS)
|
||||
goto failure_to_add_protocol;
|
||||
r = efi_add_protocol(&netobj->header, &efi_pxe_base_code_protocol_guid,
|
||||
&netobj->pxe);
|
||||
if (r != EFI_SUCCESS)
|
||||
goto failure_to_add_protocol;
|
||||
netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
|
||||
netobj->net.start = efi_net_start;
|
||||
netobj->net.stop = efi_net_stop;
|
||||
netobj->net.initialize = efi_net_initialize;
|
||||
netobj->net.reset = efi_net_reset;
|
||||
netobj->net.shutdown = efi_net_shutdown;
|
||||
netobj->net.receive_filters = efi_net_receive_filters;
|
||||
netobj->net.station_address = efi_net_station_address;
|
||||
netobj->net.statistics = efi_net_statistics;
|
||||
netobj->net.mcastiptomac = efi_net_mcastiptomac;
|
||||
netobj->net.nvdata = efi_net_nvdata;
|
||||
netobj->net.get_status = efi_net_get_status;
|
||||
netobj->net.transmit = efi_net_transmit;
|
||||
netobj->net.receive = efi_net_receive;
|
||||
netobj->net.mode = &netobj->net_mode;
|
||||
netobj->net_mode.state = EFI_NETWORK_STOPPED;
|
||||
memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
|
||||
netobj->net_mode.hwaddr_size = ARP_HLEN;
|
||||
netobj->net_mode.media_header_size = ETHER_HDR_SIZE;
|
||||
netobj->net_mode.max_packet_size = PKTSIZE;
|
||||
netobj->net_mode.if_type = ARP_ETHER;
|
||||
|
||||
netobj->pxe.revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION;
|
||||
netobj->pxe.start = efi_pxe_base_code_start;
|
||||
netobj->pxe.stop = efi_pxe_base_code_stop;
|
||||
netobj->pxe.dhcp = efi_pxe_base_code_dhcp;
|
||||
netobj->pxe.discover = efi_pxe_base_code_discover;
|
||||
netobj->pxe.mtftp = efi_pxe_base_code_mtftp;
|
||||
netobj->pxe.udp_write = efi_pxe_base_code_udp_write;
|
||||
netobj->pxe.udp_read = efi_pxe_base_code_udp_read;
|
||||
netobj->pxe.set_ip_filter = efi_pxe_base_code_set_ip_filter;
|
||||
netobj->pxe.arp = efi_pxe_base_code_arp;
|
||||
netobj->pxe.set_parameters = efi_pxe_base_code_set_parameters;
|
||||
netobj->pxe.set_station_ip = efi_pxe_base_code_set_station_ip;
|
||||
netobj->pxe.set_packets = efi_pxe_base_code_set_packets;
|
||||
netobj->pxe.mode = &netobj->pxe_mode;
|
||||
if (dhcp_ack)
|
||||
netobj->pxe_mode.dhcp_ack = *dhcp_ack;
|
||||
|
||||
/*
|
||||
* Create WaitForPacket event.
|
||||
*/
|
||||
r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
|
||||
efi_network_timer_notify, NULL, NULL,
|
||||
&wait_for_packet);
|
||||
if (r != EFI_SUCCESS) {
|
||||
printf("ERROR: Failed to register network event\n");
|
||||
return r;
|
||||
}
|
||||
netobj->net.wait_for_packet = wait_for_packet;
|
||||
/*
|
||||
* Create a timer event.
|
||||
*
|
||||
* The notification function is used to check if a new network packet
|
||||
* has been received.
|
||||
*
|
||||
* iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL.
|
||||
*/
|
||||
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
|
||||
efi_network_timer_notify, &netobj->net, NULL,
|
||||
&network_timer_event);
|
||||
if (r != EFI_SUCCESS) {
|
||||
printf("ERROR: Failed to register network event\n");
|
||||
return r;
|
||||
}
|
||||
/* Network is time critical, create event in every timer cycle */
|
||||
r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0);
|
||||
if (r != EFI_SUCCESS) {
|
||||
printf("ERROR: Failed to set network timer\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
failure_to_add_protocol:
|
||||
printf("ERROR: Failure to add protocol\n");
|
||||
return r;
|
||||
out_of_resources:
|
||||
free(netobj);
|
||||
/* free(transmit_buffer) not needed yet */
|
||||
printf("ERROR: Out of memory\n");
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Root node for system services
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <efi_loader.h>
|
||||
|
||||
const efi_guid_t efi_u_boot_guid = U_BOOT_GUID;
|
||||
|
||||
efi_handle_t efi_root = NULL;
|
||||
|
||||
struct efi_root_dp {
|
||||
struct efi_device_path_vendor vendor;
|
||||
struct efi_device_path end;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* efi_root_node_register() - create root node
|
||||
*
|
||||
* Create the root node on which we install all protocols that are
|
||||
* not related to a loaded image or a driver.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_root_node_register(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
struct efi_root_dp *dp;
|
||||
|
||||
/* Create device path protocol */
|
||||
dp = calloc(1, sizeof(*dp));
|
||||
if (!dp)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
/* Fill vendor node */
|
||||
dp->vendor.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
|
||||
dp->vendor.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
|
||||
dp->vendor.dp.length = sizeof(struct efi_device_path_vendor);
|
||||
dp->vendor.guid = efi_u_boot_guid;
|
||||
|
||||
/* Fill end node */
|
||||
dp->end.type = DEVICE_PATH_TYPE_END;
|
||||
dp->end.sub_type = DEVICE_PATH_SUB_TYPE_END;
|
||||
dp->end.length = sizeof(struct efi_device_path);
|
||||
|
||||
/* Create root node and install protocols */
|
||||
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,
|
||||
#endif
|
||||
NULL));
|
||||
efi_root->type = EFI_OBJECT_TYPE_U_BOOT_FIRMWARE;
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,883 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI application runtime services
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <cpu_func.h>
|
||||
#include <dm.h>
|
||||
#include <elf.h>
|
||||
#include <efi_loader.h>
|
||||
#include <rtc.h>
|
||||
#include <u-boot/crc.h>
|
||||
|
||||
/* For manual relocation support */
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct efi_runtime_mmio_list {
|
||||
struct list_head link;
|
||||
void **ptr;
|
||||
u64 paddr;
|
||||
u64 len;
|
||||
};
|
||||
|
||||
/* This list contains all runtime available mmio regions */
|
||||
LIST_HEAD(efi_runtime_mmio);
|
||||
|
||||
static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
|
||||
|
||||
/*
|
||||
* TODO(sjg@chromium.org): These defines and structures should come from the ELF
|
||||
* header for each architecture (or a generic header) rather than being repeated
|
||||
* here.
|
||||
*/
|
||||
#if defined(__aarch64__)
|
||||
#define R_RELATIVE R_AARCH64_RELATIVE
|
||||
#define R_MASK 0xffffffffULL
|
||||
#define IS_RELA 1
|
||||
#elif defined(__arm__)
|
||||
#define R_RELATIVE R_ARM_RELATIVE
|
||||
#define R_MASK 0xffULL
|
||||
#elif defined(__i386__)
|
||||
#define R_RELATIVE R_386_RELATIVE
|
||||
#define R_MASK 0xffULL
|
||||
#elif defined(__x86_64__)
|
||||
#define R_RELATIVE R_X86_64_RELATIVE
|
||||
#define R_MASK 0xffffffffULL
|
||||
#define IS_RELA 1
|
||||
#elif defined(__riscv)
|
||||
#define R_RELATIVE R_RISCV_RELATIVE
|
||||
#define R_MASK 0xffULL
|
||||
#define IS_RELA 1
|
||||
|
||||
struct dyn_sym {
|
||||
ulong foo1;
|
||||
ulong addr;
|
||||
u32 foo2;
|
||||
u32 foo3;
|
||||
};
|
||||
#if (__riscv_xlen == 32)
|
||||
#define R_ABSOLUTE R_RISCV_32
|
||||
#define SYM_INDEX 8
|
||||
#elif (__riscv_xlen == 64)
|
||||
#define R_ABSOLUTE R_RISCV_64
|
||||
#define SYM_INDEX 32
|
||||
#else
|
||||
#error unknown riscv target
|
||||
#endif
|
||||
#else
|
||||
#error Need to add relocation awareness
|
||||
#endif
|
||||
|
||||
struct elf_rel {
|
||||
ulong *offset;
|
||||
ulong info;
|
||||
};
|
||||
|
||||
struct elf_rela {
|
||||
ulong *offset;
|
||||
ulong info;
|
||||
long addend;
|
||||
};
|
||||
|
||||
static __efi_runtime_data struct efi_mem_desc *efi_virtmap;
|
||||
static __efi_runtime_data efi_uintn_t efi_descriptor_count;
|
||||
static __efi_runtime_data efi_uintn_t efi_descriptor_size;
|
||||
|
||||
/*
|
||||
* EFI runtime code lives in two stages. In the first stage, U-Boot and an EFI
|
||||
* payload are running concurrently at the same time. In this mode, we can
|
||||
* handle a good number of runtime callbacks
|
||||
*/
|
||||
|
||||
efi_status_t efi_init_runtime_supported(void)
|
||||
{
|
||||
u16 efi_runtime_services_supported =
|
||||
EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP |
|
||||
EFI_RT_SUPPORTED_CONVERT_POINTER;
|
||||
|
||||
/*
|
||||
* This value must be synced with efi_runtime_detach_list
|
||||
* as well as efi_runtime_services.
|
||||
*/
|
||||
#ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
|
||||
efi_runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
|
||||
#endif
|
||||
|
||||
return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
|
||||
&efi_global_variable_guid,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS,
|
||||
sizeof(efi_runtime_services_supported),
|
||||
&efi_runtime_services_supported));
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_update_table_header_crc32() - Update crc32 in table header
|
||||
*
|
||||
* @table: EFI table
|
||||
*/
|
||||
void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table)
|
||||
{
|
||||
table->crc32 = 0;
|
||||
table->crc32 = crc32(0, (const unsigned char *)table,
|
||||
table->headersize);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_reset_system_boottime() - reset system at boot time
|
||||
*
|
||||
* This function implements the ResetSystem() runtime service before
|
||||
* SetVirtualAddressMap() is called.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @reset_type: type of reset to perform
|
||||
* @reset_status: status code for the reset
|
||||
* @data_size: size of reset_data
|
||||
* @reset_data: information about the reset
|
||||
*/
|
||||
static void EFIAPI efi_reset_system_boottime(
|
||||
enum efi_reset_type reset_type,
|
||||
efi_status_t reset_status,
|
||||
unsigned long data_size, void *reset_data)
|
||||
{
|
||||
struct efi_event *evt;
|
||||
|
||||
EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
|
||||
reset_data);
|
||||
|
||||
/* Notify reset */
|
||||
list_for_each_entry(evt, &efi_events, link) {
|
||||
if (evt->group &&
|
||||
!guidcmp(evt->group,
|
||||
&efi_guid_event_group_reset_system)) {
|
||||
efi_signal_event(evt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (reset_type) {
|
||||
case EFI_RESET_COLD:
|
||||
case EFI_RESET_WARM:
|
||||
case EFI_RESET_PLATFORM_SPECIFIC:
|
||||
do_reset(NULL, 0, 0, NULL);
|
||||
break;
|
||||
case EFI_RESET_SHUTDOWN:
|
||||
#ifdef CONFIG_CMD_POWEROFF
|
||||
do_poweroff(NULL, 0, 0, NULL);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
while (1) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_get_time_boottime() - get current time at boot time
|
||||
*
|
||||
* This function implements the GetTime runtime service before
|
||||
* SetVirtualAddressMap() is called.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||
* for details.
|
||||
*
|
||||
* @time: pointer to structure to receive current time
|
||||
* @capabilities: pointer to structure to receive RTC properties
|
||||
* Returns: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_get_time_boottime(
|
||||
struct efi_time *time,
|
||||
struct efi_time_cap *capabilities)
|
||||
{
|
||||
#ifdef CONFIG_EFI_GET_TIME
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
struct rtc_time tm;
|
||||
struct udevice *dev;
|
||||
|
||||
EFI_ENTRY("%p %p", time, capabilities);
|
||||
|
||||
if (!time) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
memset(time, 0, sizeof(*time));
|
||||
time->year = tm.tm_year;
|
||||
time->month = tm.tm_mon;
|
||||
time->day = tm.tm_mday;
|
||||
time->hour = tm.tm_hour;
|
||||
time->minute = tm.tm_min;
|
||||
time->second = tm.tm_sec;
|
||||
if (tm.tm_isdst)
|
||||
time->daylight =
|
||||
EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT;
|
||||
time->timezone = EFI_UNSPECIFIED_TIMEZONE;
|
||||
|
||||
if (capabilities) {
|
||||
/* Set reasonable dummy values */
|
||||
capabilities->resolution = 1; /* 1 Hz */
|
||||
capabilities->accuracy = 100000000; /* 100 ppm */
|
||||
capabilities->sets_to_zero = false;
|
||||
}
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
#else
|
||||
EFI_ENTRY("%p %p", time, capabilities);
|
||||
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
|
||||
*
|
||||
* This function implements the ResetSystem() runtime service after
|
||||
* SetVirtualAddressMap() is called. It only executes an endless loop.
|
||||
* Boards may override the helpers below to implement reset functionality.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @reset_type: type of reset to perform
|
||||
* @reset_status: status code for the reset
|
||||
* @data_size: size of reset_data
|
||||
* @reset_data: information about the reset
|
||||
*/
|
||||
void __weak __efi_runtime EFIAPI efi_reset_system(
|
||||
enum efi_reset_type reset_type,
|
||||
efi_status_t reset_status,
|
||||
unsigned long data_size, void *reset_data)
|
||||
{
|
||||
/* Nothing we can do */
|
||||
while (1) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_reset_system_init() - initialize the reset driver
|
||||
*
|
||||
* Boards may override this function to initialize the reset driver.
|
||||
*/
|
||||
efi_status_t __weak efi_reset_system_init(void)
|
||||
{
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_get_time() - get current time
|
||||
*
|
||||
* This function implements the GetTime 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 receive current time
|
||||
* @capabilities: pointer to structure to receive RTC properties
|
||||
* Returns: status code
|
||||
*/
|
||||
efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
|
||||
struct efi_time *time,
|
||||
struct efi_time_cap *capabilities)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_is_runtime_service_pointer() - check if pointer points to runtime table
|
||||
*
|
||||
* @p: pointer to check
|
||||
* Return: true if the pointer points to a service function pointer in the
|
||||
* runtime table
|
||||
*/
|
||||
static bool efi_is_runtime_service_pointer(void *p)
|
||||
{
|
||||
return (p >= (void *)&efi_runtime_services.get_time &&
|
||||
p <= (void *)&efi_runtime_services.query_variable_info) ||
|
||||
p == (void *)&efi_events.prev ||
|
||||
p == (void *)&efi_events.next;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_runtime_detach() - detach unimplemented runtime functions
|
||||
*/
|
||||
void efi_runtime_detach(void)
|
||||
{
|
||||
efi_runtime_services.reset_system = efi_reset_system;
|
||||
efi_runtime_services.get_time = efi_get_time;
|
||||
efi_runtime_services.set_time = efi_set_time;
|
||||
|
||||
/* Update CRC32 */
|
||||
efi_update_table_header_crc32(&efi_runtime_services.hdr);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_set_virtual_address_map_runtime() - change from physical to virtual
|
||||
* mapping
|
||||
*
|
||||
* This function implements the SetVirtualAddressMap() runtime service after
|
||||
* it is first called.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @memory_map_size: size of the virtual map
|
||||
* @descriptor_size: size of an entry in the map
|
||||
* @descriptor_version: version of the map entries
|
||||
* @virtmap: virtual address mapping information
|
||||
* Return: status code EFI_UNSUPPORTED
|
||||
*/
|
||||
static __efi_runtime efi_status_t EFIAPI efi_set_virtual_address_map_runtime(
|
||||
efi_uintn_t memory_map_size,
|
||||
efi_uintn_t descriptor_size,
|
||||
uint32_t descriptor_version,
|
||||
struct efi_mem_desc *virtmap)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_convert_pointer_runtime() - convert from physical to virtual pointer
|
||||
*
|
||||
* This function implements the ConvertPointer() runtime service after
|
||||
* the first call to SetVirtualAddressMap().
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @debug_disposition: indicates if pointer may be converted to NULL
|
||||
* @address: pointer to be converted
|
||||
* Return: status code EFI_UNSUPPORTED
|
||||
*/
|
||||
static __efi_runtime efi_status_t EFIAPI efi_convert_pointer_runtime(
|
||||
efi_uintn_t debug_disposition, void **address)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_convert_pointer_runtime() - convert from physical to virtual pointer
|
||||
*
|
||||
* This function implements the ConvertPointer() runtime service until
|
||||
* the first call to SetVirtualAddressMap().
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @debug_disposition: indicates if pointer may be converted to NULL
|
||||
* @address: pointer to be converted
|
||||
* Return: status code EFI_UNSUPPORTED
|
||||
*/
|
||||
static __efi_runtime efi_status_t EFIAPI efi_convert_pointer(
|
||||
efi_uintn_t debug_disposition, void **address)
|
||||
{
|
||||
efi_physical_addr_t addr = (uintptr_t)*address;
|
||||
efi_uintn_t i;
|
||||
efi_status_t ret = EFI_NOT_FOUND;
|
||||
|
||||
EFI_ENTRY("%zu %p", debug_disposition, address);
|
||||
|
||||
if (!efi_virtmap) {
|
||||
ret = EFI_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!address) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < efi_descriptor_count; i++) {
|
||||
struct efi_mem_desc *map = (void *)efi_virtmap +
|
||||
(efi_descriptor_size * i);
|
||||
|
||||
if (addr >= map->physical_start &&
|
||||
(addr < map->physical_start
|
||||
+ (map->num_pages << EFI_PAGE_SHIFT))) {
|
||||
*address = (void *)(uintptr_t)
|
||||
(addr + map->virtual_start -
|
||||
map->physical_start);
|
||||
|
||||
ret = EFI_SUCCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
static __efi_runtime void efi_relocate_runtime_table(ulong offset)
|
||||
{
|
||||
ulong patchoff;
|
||||
void **pos;
|
||||
|
||||
/* Relocate the runtime services pointers */
|
||||
patchoff = offset - gd->relocaddr;
|
||||
for (pos = (void **)&efi_runtime_services.get_time;
|
||||
pos <= (void **)&efi_runtime_services.query_variable_info; ++pos) {
|
||||
if (*pos)
|
||||
*pos += patchoff;
|
||||
}
|
||||
|
||||
/*
|
||||
* The entry for SetVirtualAddress() must point to a physical address.
|
||||
* After the first execution the service must return EFI_UNSUPPORTED.
|
||||
*/
|
||||
efi_runtime_services.set_virtual_address_map =
|
||||
&efi_set_virtual_address_map_runtime;
|
||||
|
||||
/*
|
||||
* The entry for ConvertPointer() must point to a physical address.
|
||||
* The service is not usable after SetVirtualAddress().
|
||||
*/
|
||||
efi_runtime_services.convert_pointer = &efi_convert_pointer_runtime;
|
||||
|
||||
/*
|
||||
* TODO: Update UEFI variable RuntimeServicesSupported removing flags
|
||||
* EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP and
|
||||
* EFI_RT_SUPPORTED_CONVERT_POINTER as required by the UEFI spec 2.8.
|
||||
*/
|
||||
|
||||
/* Update CRC32 */
|
||||
efi_update_table_header_crc32(&efi_runtime_services.hdr);
|
||||
}
|
||||
|
||||
/* Relocate EFI runtime to uboot_reloc_base = offset */
|
||||
void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
|
||||
{
|
||||
#ifdef IS_RELA
|
||||
struct elf_rela *rel = (void*)&__efi_runtime_rel_start;
|
||||
#else
|
||||
struct elf_rel *rel = (void*)&__efi_runtime_rel_start;
|
||||
static ulong lastoff = CONFIG_SYS_TEXT_BASE;
|
||||
#endif
|
||||
|
||||
debug("%s: Relocating to offset=%lx\n", __func__, offset);
|
||||
for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) {
|
||||
ulong base = CONFIG_SYS_TEXT_BASE;
|
||||
ulong *p;
|
||||
ulong newaddr;
|
||||
|
||||
p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
|
||||
|
||||
/*
|
||||
* The runtime services table is updated in
|
||||
* efi_relocate_runtime_table()
|
||||
*/
|
||||
if (map && efi_is_runtime_service_pointer(p))
|
||||
continue;
|
||||
|
||||
debug("%s: rel->info=%#lx *p=%#lx rel->offset=%p\n", __func__,
|
||||
rel->info, *p, rel->offset);
|
||||
|
||||
switch (rel->info & R_MASK) {
|
||||
case R_RELATIVE:
|
||||
#ifdef IS_RELA
|
||||
newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE;
|
||||
#else
|
||||
newaddr = *p - lastoff + offset;
|
||||
#endif
|
||||
break;
|
||||
#ifdef R_ABSOLUTE
|
||||
case R_ABSOLUTE: {
|
||||
ulong symidx = rel->info >> SYM_INDEX;
|
||||
extern struct dyn_sym __dyn_sym_start[];
|
||||
newaddr = __dyn_sym_start[symidx].addr + offset;
|
||||
#ifdef IS_RELA
|
||||
newaddr -= CONFIG_SYS_TEXT_BASE;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
printf("%s: Unknown relocation type %llx\n",
|
||||
__func__, rel->info & R_MASK);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if the relocation is inside bounds */
|
||||
if (map && ((newaddr < map->virtual_start) ||
|
||||
newaddr > (map->virtual_start +
|
||||
(map->num_pages << EFI_PAGE_SHIFT)))) {
|
||||
printf("%s: Relocation at %p is out of range (%lx)\n",
|
||||
__func__, p, newaddr);
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("%s: Setting %p to %lx\n", __func__, p, newaddr);
|
||||
*p = newaddr;
|
||||
flush_dcache_range((ulong)p & ~(EFI_CACHELINE_SIZE - 1),
|
||||
ALIGN((ulong)&p[1], EFI_CACHELINE_SIZE));
|
||||
}
|
||||
|
||||
#ifndef IS_RELA
|
||||
lastoff = offset;
|
||||
#endif
|
||||
|
||||
invalidate_icache_all();
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_set_virtual_address_map() - change from physical to virtual mapping
|
||||
*
|
||||
* This function implements the SetVirtualAddressMap() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @memory_map_size: size of the virtual map
|
||||
* @descriptor_size: size of an entry in the map
|
||||
* @descriptor_version: version of the map entries
|
||||
* @virtmap: virtual address mapping information
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_set_virtual_address_map(
|
||||
efi_uintn_t memory_map_size,
|
||||
efi_uintn_t descriptor_size,
|
||||
uint32_t descriptor_version,
|
||||
struct efi_mem_desc *virtmap)
|
||||
{
|
||||
efi_uintn_t n = memory_map_size / descriptor_size;
|
||||
efi_uintn_t i;
|
||||
efi_status_t ret = EFI_INVALID_PARAMETER;
|
||||
int rt_code_sections = 0;
|
||||
struct efi_event *event;
|
||||
|
||||
EFI_ENTRY("%zx %zx %x %p", memory_map_size, descriptor_size,
|
||||
descriptor_version, virtmap);
|
||||
|
||||
if (descriptor_version != EFI_MEMORY_DESCRIPTOR_VERSION ||
|
||||
descriptor_size < sizeof(struct efi_mem_desc))
|
||||
goto out;
|
||||
|
||||
efi_virtmap = virtmap;
|
||||
efi_descriptor_size = descriptor_size;
|
||||
efi_descriptor_count = n;
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Further down we are cheating. While really we should implement
|
||||
* SetVirtualAddressMap() events and ConvertPointer() to allow
|
||||
* dynamically loaded drivers to expose runtime services, we don't
|
||||
* today.
|
||||
*
|
||||
* So let's ensure we see exactly one single runtime section, as
|
||||
* that is the built-in one. If we see more (or less), someone must
|
||||
* have tried adding or removing to that which we don't support yet.
|
||||
* In that case, let's better fail rather than expose broken runtime
|
||||
* services.
|
||||
*/
|
||||
for (i = 0; i < n; i++) {
|
||||
struct efi_mem_desc *map = (void*)virtmap +
|
||||
(descriptor_size * i);
|
||||
|
||||
if (map->type == EFI_RUNTIME_SERVICES_CODE)
|
||||
rt_code_sections++;
|
||||
}
|
||||
|
||||
if (rt_code_sections != 1) {
|
||||
/*
|
||||
* We expose exactly one single runtime code section, so
|
||||
* something is definitely going wrong.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Notify EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
|
||||
list_for_each_entry(event, &efi_events, link) {
|
||||
if (event->notify_function)
|
||||
EFI_CALL_VOID(event->notify_function(
|
||||
event, event->notify_context));
|
||||
}
|
||||
|
||||
/* Rebind mmio pointers */
|
||||
for (i = 0; i < n; i++) {
|
||||
struct efi_mem_desc *map = (void*)virtmap +
|
||||
(descriptor_size * i);
|
||||
struct list_head *lhandle;
|
||||
efi_physical_addr_t map_start = map->physical_start;
|
||||
efi_physical_addr_t map_len = map->num_pages << EFI_PAGE_SHIFT;
|
||||
efi_physical_addr_t map_end = map_start + map_len;
|
||||
u64 off = map->virtual_start - map_start;
|
||||
|
||||
/* Adjust all mmio pointers in this region */
|
||||
list_for_each(lhandle, &efi_runtime_mmio) {
|
||||
struct efi_runtime_mmio_list *lmmio;
|
||||
|
||||
lmmio = list_entry(lhandle,
|
||||
struct efi_runtime_mmio_list,
|
||||
link);
|
||||
if ((map_start <= lmmio->paddr) &&
|
||||
(map_end >= lmmio->paddr)) {
|
||||
uintptr_t new_addr = lmmio->paddr + off;
|
||||
*lmmio->ptr = (void *)new_addr;
|
||||
}
|
||||
}
|
||||
if ((map_start <= (uintptr_t)systab.tables) &&
|
||||
(map_end >= (uintptr_t)systab.tables)) {
|
||||
char *ptr = (char *)systab.tables;
|
||||
|
||||
ptr += off;
|
||||
systab.tables = (struct efi_configuration_table *)ptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Relocate the runtime. See TODO above */
|
||||
for (i = 0; i < n; i++) {
|
||||
struct efi_mem_desc *map;
|
||||
|
||||
map = (void*)virtmap + (descriptor_size * i);
|
||||
if (map->type == EFI_RUNTIME_SERVICES_CODE) {
|
||||
ulong new_offset = map->virtual_start -
|
||||
map->physical_start + gd->relocaddr;
|
||||
|
||||
efi_relocate_runtime_table(new_offset);
|
||||
efi_runtime_relocate(new_offset, map);
|
||||
ret = EFI_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_add_runtime_mmio() - add memory-mapped IO region
|
||||
*
|
||||
* This function adds a memory-mapped IO region to the memory map to make it
|
||||
* available at runtime.
|
||||
*
|
||||
* @mmio_ptr: pointer to a pointer to the start of the memory-mapped
|
||||
* IO region
|
||||
* @len: size of the memory-mapped IO region
|
||||
* Returns: status code
|
||||
*/
|
||||
efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
|
||||
{
|
||||
struct efi_runtime_mmio_list *newmmio;
|
||||
u64 pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
|
||||
uint64_t addr = *(uintptr_t *)mmio_ptr;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = efi_add_memory_map(addr, pages, EFI_MMAP_IO, false);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
newmmio = calloc(1, sizeof(*newmmio));
|
||||
if (!newmmio)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
newmmio->ptr = mmio_ptr;
|
||||
newmmio->paddr = *(uintptr_t *)mmio_ptr;
|
||||
newmmio->len = len;
|
||||
list_add_tail(&newmmio->link, &efi_runtime_mmio);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* In the second stage, U-Boot has disappeared. To isolate our runtime code
|
||||
* that at this point still exists from the rest, we put it into a special
|
||||
* section.
|
||||
*
|
||||
* !!WARNING!!
|
||||
*
|
||||
* This means that we can not rely on any code outside of this file in any
|
||||
* function or variable below this line.
|
||||
*
|
||||
* Please keep everything fully self-contained and annotated with
|
||||
* __efi_runtime and __efi_runtime_data markers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Relocate the EFI runtime stub to a different place. We need to call this
|
||||
* the first time we expose the runtime interface to a user and on set virtual
|
||||
* address map calls.
|
||||
*/
|
||||
|
||||
/**
|
||||
* efi_unimplemented() - replacement function, returns EFI_UNSUPPORTED
|
||||
*
|
||||
* This function is used after SetVirtualAddressMap() is called as replacement
|
||||
* for services that are not available anymore due to constraints of the U-Boot
|
||||
* implementation.
|
||||
*
|
||||
* Return: EFI_UNSUPPORTED
|
||||
*/
|
||||
static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_update_capsule() - process information from operating system
|
||||
*
|
||||
* This function implements the UpdateCapsule() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @capsule_header_array: pointer to array of virtual pointers
|
||||
* @capsule_count: number of pointers in capsule_header_array
|
||||
* @scatter_gather_list: pointer to arry of physical pointers
|
||||
* Returns: status code
|
||||
*/
|
||||
efi_status_t __efi_runtime EFIAPI efi_update_capsule(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 scatter_gather_list)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_query_capsule_caps() - check if capsule is supported
|
||||
*
|
||||
* This function implements the QueryCapsuleCapabilities() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @capsule_header_array: pointer to array of virtual pointers
|
||||
* @capsule_count: number of pointers in capsule_header_array
|
||||
* @maximum_capsule_size: maximum capsule size
|
||||
* @reset_type: type of reset needed for capsule update
|
||||
* Returns: status code
|
||||
*/
|
||||
efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
|
||||
struct efi_capsule_header **capsule_header_array,
|
||||
efi_uintn_t capsule_count,
|
||||
u64 *maximum_capsule_size,
|
||||
u32 *reset_type)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
|
||||
.hdr = {
|
||||
.signature = EFI_RUNTIME_SERVICES_SIGNATURE,
|
||||
.revision = EFI_SPECIFICATION_VERSION,
|
||||
.headersize = sizeof(struct efi_runtime_services),
|
||||
},
|
||||
.get_time = &efi_get_time_boottime,
|
||||
.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,
|
||||
.convert_pointer = efi_convert_pointer,
|
||||
.get_variable = efi_get_variable,
|
||||
.get_next_variable_name = efi_get_next_variable_name,
|
||||
.set_variable = efi_set_variable,
|
||||
.get_next_high_mono_count = (void *)&efi_unimplemented,
|
||||
.reset_system = &efi_reset_system_boottime,
|
||||
.update_capsule = efi_update_capsule,
|
||||
.query_capsule_caps = efi_query_capsule_caps,
|
||||
.query_variable_info = efi_query_variable_info,
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI setup code
|
||||
*
|
||||
* Copyright (c) 2016-2018 Alexander Graf et al.
|
||||
*/
|
||||
|
||||
#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
|
||||
*
|
||||
* Set the PlatformLangCodes and PlatformLang variables.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_init_platform_lang(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_uintn_t data_size = 0;
|
||||
char *lang = CONFIG_EFI_PLATFORM_LANG_CODES;
|
||||
char *pos;
|
||||
|
||||
/*
|
||||
* Variable PlatformLangCodes defines the language codes that the
|
||||
* machine can support.
|
||||
*/
|
||||
ret = EFI_CALL(efi_set_variable(L"PlatformLangCodes",
|
||||
&efi_global_variable_guid,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS,
|
||||
sizeof(CONFIG_EFI_PLATFORM_LANG_CODES),
|
||||
CONFIG_EFI_PLATFORM_LANG_CODES));
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Variable PlatformLang defines the language that the machine has been
|
||||
* configured for.
|
||||
*/
|
||||
ret = EFI_CALL(efi_get_variable(L"PlatformLang",
|
||||
&efi_global_variable_guid,
|
||||
NULL, &data_size, &pos));
|
||||
if (ret == EFI_BUFFER_TOO_SMALL) {
|
||||
/* The variable is already set. Do not change it. */
|
||||
ret = EFI_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The list of supported languages is semicolon separated. Use the first
|
||||
* language to initialize PlatformLang.
|
||||
*/
|
||||
pos = strchr(lang, ';');
|
||||
if (pos)
|
||||
*pos = 0;
|
||||
|
||||
ret = EFI_CALL(efi_set_variable(L"PlatformLang",
|
||||
&efi_global_variable_guid,
|
||||
EFI_VARIABLE_NON_VOLATILE |
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS,
|
||||
1 + strlen(lang), lang));
|
||||
out:
|
||||
if (ret != EFI_SUCCESS)
|
||||
printf("EFI: cannot initialize platform language settings\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_init_obj_list() - Initialize and populate EFI object list
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
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();
|
||||
|
||||
/* Initialize variable services */
|
||||
ret = efi_init_variables();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Indicate supported runtime services */
|
||||
ret = efi_init_runtime_supported();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Initialize system table */
|
||||
ret = efi_initialize_system_table();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Initialize root node */
|
||||
ret = efi_root_node_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Initialize EFI driver uclass */
|
||||
ret = efi_driver_init();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
ret = efi_console_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
#ifdef CONFIG_PARTITIONS
|
||||
ret = efi_disk_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
#endif
|
||||
#if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO)
|
||||
ret = efi_gop_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
#endif
|
||||
#ifdef CONFIG_NET
|
||||
ret = efi_net_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_ACPI_TABLE
|
||||
ret = efi_acpi_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
|
||||
ret = efi_smbios_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
#endif
|
||||
ret = efi_watchdog_register();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Initialize EFI runtime services */
|
||||
ret = efi_reset_system_init();
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
efi_obj_list_initialized = ret;
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI application tables support
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <mapmem.h>
|
||||
#include <smbios.h>
|
||||
|
||||
static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
|
||||
|
||||
/*
|
||||
* Install the SMBIOS table as a configuration table.
|
||||
*
|
||||
* @return status code
|
||||
*/
|
||||
efi_status_t efi_smbios_register(void)
|
||||
{
|
||||
/* Map within the low 32 bits, to allow for 32bit SMBIOS tables */
|
||||
u64 dmi_addr = U32_MAX;
|
||||
efi_status_t ret;
|
||||
void *dmi;
|
||||
|
||||
/* Reserve 4kiB page for SMBIOS */
|
||||
ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
|
||||
EFI_RUNTIME_SERVICES_DATA, 1, &dmi_addr);
|
||||
|
||||
if (ret != EFI_SUCCESS) {
|
||||
/* Could not find space in lowmem, use highmem instead */
|
||||
ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
|
||||
EFI_RUNTIME_SERVICES_DATA, 1,
|
||||
&dmi_addr);
|
||||
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate SMBIOS tables - we know that efi_allocate_pages() returns
|
||||
* a 4k-aligned address, so it is safe to assume that
|
||||
* write_smbios_table() will write the table at that address.
|
||||
*
|
||||
* Note that on sandbox, efi_allocate_pages() unfortunately returns a
|
||||
* pointer even though it uses a uint64_t type. Convert it.
|
||||
*/
|
||||
assert(!(dmi_addr & 0xf));
|
||||
dmi = (void *)(uintptr_t)dmi_addr;
|
||||
write_smbios_table(map_to_sysmem(dmi));
|
||||
|
||||
/* And expose them to our EFI payload */
|
||||
return efi_install_configuration_table(&smbios_guid, dmi);
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI Unicode collation protocol
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <charset.h>
|
||||
#include <cp1250.h>
|
||||
#include <cp437.h>
|
||||
#include <efi_loader.h>
|
||||
|
||||
/* Characters that may not be used in FAT 8.3 file names */
|
||||
static const char illegal[] = "+,<=>:;\"/\\|?*[]\x7f";
|
||||
|
||||
/*
|
||||
* EDK2 assumes codepage 1250 when creating FAT 8.3 file names.
|
||||
* Linux defaults to codepage 437 for FAT 8.3 file names.
|
||||
*/
|
||||
#if CONFIG_FAT_DEFAULT_CODEPAGE == 1250
|
||||
/* Unicode code points for code page 1250 characters 0x80 - 0xff */
|
||||
static const u16 codepage[] = CP1250;
|
||||
#else
|
||||
/* Unicode code points for code page 437 characters 0x80 - 0xff */
|
||||
static const u16 codepage[] = CP437;
|
||||
#endif
|
||||
|
||||
/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL2 */
|
||||
const efi_guid_t efi_guid_unicode_collation_protocol2 =
|
||||
EFI_UNICODE_COLLATION_PROTOCOL2_GUID;
|
||||
|
||||
/**
|
||||
* efi_stri_coll() - compare utf-16 strings case-insenitively
|
||||
*
|
||||
* @this: unicode collation protocol instance
|
||||
* @s1: first string
|
||||
* @s2: second string
|
||||
*
|
||||
* This function implements the StriColl() service of the
|
||||
* EFI_UNICODE_COLLATION_PROTOCOL.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* Return: 0: s1 == s2, > 0: s1 > s2, < 0: s1 < s2
|
||||
*/
|
||||
static efi_intn_t EFIAPI efi_stri_coll(
|
||||
struct efi_unicode_collation_protocol *this, u16 *s1, u16 *s2)
|
||||
{
|
||||
s32 c1, c2;
|
||||
efi_intn_t ret = 0;
|
||||
|
||||
EFI_ENTRY("%p, %ls, %ls", this, s1, s2);
|
||||
for (; *s1 | *s2; ++s1, ++s2) {
|
||||
c1 = utf_to_upper(*s1);
|
||||
c2 = utf_to_upper(*s2);
|
||||
if (c1 < c2) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
} else if (c1 > c2) {
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* next_lower() - get next codepoint converted to lower case
|
||||
*
|
||||
* @string: pointer to u16 string, on return advanced by one codepoint
|
||||
* Return: first codepoint of string converted to lower case
|
||||
*/
|
||||
static s32 next_lower(const u16 **string)
|
||||
{
|
||||
return utf_to_lower(utf16_get(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* metai_match() - compare utf-16 string with a pattern string case-insenitively
|
||||
*
|
||||
* @string: string to compare
|
||||
* @pattern: pattern string
|
||||
*
|
||||
* The pattern string may use these:
|
||||
* - * matches >= 0 characters
|
||||
* - ? matches 1 character
|
||||
* - [<char1><char2>...<charN>] match any character in the set
|
||||
* - [<char1>-<char2>] matches any character in the range
|
||||
*
|
||||
* This function is called my efi_metai_match().
|
||||
*
|
||||
* For '*' pattern searches this function calls itself recursively.
|
||||
* Performance-wise this is suboptimal, especially for multiple '*' wildcards.
|
||||
* But it results in simple code.
|
||||
*
|
||||
* Return: true if the string is matched.
|
||||
*/
|
||||
static bool metai_match(const u16 *string, const u16 *pattern)
|
||||
{
|
||||
s32 first, s, p;
|
||||
|
||||
for (; *string && *pattern;) {
|
||||
const u16 *string_old = string;
|
||||
|
||||
s = next_lower(&string);
|
||||
p = next_lower(&pattern);
|
||||
|
||||
switch (p) {
|
||||
case '*':
|
||||
/* Match 0 or more characters */
|
||||
for (;; s = next_lower(&string)) {
|
||||
if (metai_match(string_old, pattern))
|
||||
return true;
|
||||
if (!s)
|
||||
return false;
|
||||
string_old = string;
|
||||
}
|
||||
case '?':
|
||||
/* Match any one character */
|
||||
break;
|
||||
case '[':
|
||||
/* Match any character in the set */
|
||||
p = next_lower(&pattern);
|
||||
first = p;
|
||||
if (first == ']')
|
||||
/* Empty set */
|
||||
return false;
|
||||
p = next_lower(&pattern);
|
||||
if (p == '-') {
|
||||
/* Range */
|
||||
p = next_lower(&pattern);
|
||||
if (s < first || s > p)
|
||||
return false;
|
||||
p = next_lower(&pattern);
|
||||
if (p != ']')
|
||||
return false;
|
||||
} else {
|
||||
/* Set */
|
||||
bool hit = false;
|
||||
|
||||
if (s == first)
|
||||
hit = true;
|
||||
for (; p && p != ']';
|
||||
p = next_lower(&pattern)) {
|
||||
if (p == s)
|
||||
hit = true;
|
||||
}
|
||||
if (!hit || p != ']')
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Match one character */
|
||||
if (p != s)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!*pattern && !*string)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_metai_match() - compare utf-16 string with a pattern string
|
||||
* case-insenitively
|
||||
*
|
||||
* @this: unicode collation protocol instance
|
||||
* @s: string to compare
|
||||
* @p: pattern string
|
||||
*
|
||||
* The pattern string may use these:
|
||||
* - * matches >= 0 characters
|
||||
* - ? matches 1 character
|
||||
* - [<char1><char2>...<charN>] match any character in the set
|
||||
* - [<char1>-<char2>] matches any character in the range
|
||||
*
|
||||
* This function implements the MetaMatch() service of the
|
||||
* EFI_UNICODE_COLLATION_PROTOCOL.
|
||||
*
|
||||
* Return: true if the string is matched.
|
||||
*/
|
||||
static bool EFIAPI efi_metai_match(struct efi_unicode_collation_protocol *this,
|
||||
const u16 *string, const u16 *pattern)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
EFI_ENTRY("%p, %ls, %ls", this, string, pattern);
|
||||
ret = metai_match(string, pattern);
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_str_lwr() - convert to lower case
|
||||
*
|
||||
* @this: unicode collation protocol instance
|
||||
* @string: string to convert
|
||||
* @p: pattern string
|
||||
*
|
||||
* The conversion is done in place. As long as upper and lower letters use the
|
||||
* same number of words this does not pose a problem.
|
||||
*
|
||||
* This function implements the StrLwr() service of the
|
||||
* EFI_UNICODE_COLLATION_PROTOCOL.
|
||||
*/
|
||||
static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this,
|
||||
u16 *string)
|
||||
{
|
||||
EFI_ENTRY("%p, %ls", this, string);
|
||||
for (; *string; ++string)
|
||||
*string = utf_to_lower(*string);
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_str_upr() - convert to upper case
|
||||
*
|
||||
* @this: unicode collation protocol instance
|
||||
* @string: string to convert
|
||||
* @p: pattern string
|
||||
*
|
||||
* The conversion is done in place. As long as upper and lower letters use the
|
||||
* same number of words this does not pose a problem.
|
||||
*
|
||||
* This function implements the StrUpr() service of the
|
||||
* EFI_UNICODE_COLLATION_PROTOCOL.
|
||||
*/
|
||||
static void EFIAPI efi_str_upr(struct efi_unicode_collation_protocol *this,
|
||||
u16 *string)
|
||||
{
|
||||
EFI_ENTRY("%p, %ls", this, string);
|
||||
for (; *string; ++string)
|
||||
*string = utf_to_upper(*string);
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_fat_to_str() - convert an 8.3 file name from an OEM codepage to Unicode
|
||||
*
|
||||
* @this: unicode collation protocol instance
|
||||
* @fat_size: size of the string to convert
|
||||
* @fat: string to convert
|
||||
* @string: converted string
|
||||
*
|
||||
* This function implements the FatToStr() service of the
|
||||
* EFI_UNICODE_COLLATION_PROTOCOL.
|
||||
*/
|
||||
static void EFIAPI efi_fat_to_str(struct efi_unicode_collation_protocol *this,
|
||||
efi_uintn_t fat_size, char *fat, u16 *string)
|
||||
{
|
||||
efi_uintn_t i;
|
||||
u16 c;
|
||||
|
||||
EFI_ENTRY("%p, %zu, %s, %p", this, fat_size, fat, string);
|
||||
for (i = 0; i < fat_size; ++i) {
|
||||
c = (unsigned char)fat[i];
|
||||
if (c > 0x80)
|
||||
c = codepage[i - 0x80];
|
||||
string[i] = c;
|
||||
if (!c)
|
||||
break;
|
||||
}
|
||||
string[i] = 0;
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_fat_to_str() - convert a utf-16 string to legal characters for a FAT
|
||||
* file name in an OEM code page
|
||||
*
|
||||
* @this: unicode collation protocol instance
|
||||
* @string: Unicode string to convert
|
||||
* @fat_size: size of the target buffer
|
||||
* @fat: converted string
|
||||
*
|
||||
* This function implements the StrToFat() service of the
|
||||
* EFI_UNICODE_COLLATION_PROTOCOL.
|
||||
*
|
||||
* Return: true if an illegal character was substituted by '_'.
|
||||
*/
|
||||
static bool EFIAPI efi_str_to_fat(struct efi_unicode_collation_protocol *this,
|
||||
const u16 *string, efi_uintn_t fat_size,
|
||||
char *fat)
|
||||
{
|
||||
efi_uintn_t i;
|
||||
s32 c;
|
||||
bool ret = false;
|
||||
|
||||
EFI_ENTRY("%p, %ls, %zu, %p", this, string, fat_size, fat);
|
||||
for (i = 0; i < fat_size;) {
|
||||
c = utf16_get(&string);
|
||||
switch (c) {
|
||||
/* Ignore period and space */
|
||||
case '.':
|
||||
case ' ':
|
||||
continue;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
c = utf_to_upper(c);
|
||||
if (c >= 0x80) {
|
||||
int j;
|
||||
|
||||
/* Look for codepage translation */
|
||||
for (j = 0; j < 0x80; ++j) {
|
||||
if (c == codepage[j]) {
|
||||
c = j + 0x80;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j >= 0x80) {
|
||||
c = '_';
|
||||
ret = true;
|
||||
}
|
||||
} else if (c && (c < 0x20 || strchr(illegal, c))) {
|
||||
c = '_';
|
||||
ret = true;
|
||||
}
|
||||
|
||||
fat[i] = c;
|
||||
if (!c)
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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,
|
||||
.str_upr = efi_str_upr,
|
||||
.fat_to_str = efi_fat_to_str,
|
||||
.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
|
||||
@@ -0,0 +1,643 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI utils
|
||||
*
|
||||
* Copyright (c) 2017 Rob Clark
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <env_internal.h>
|
||||
#include <hexdump.h>
|
||||
#include <malloc.h>
|
||||
#include <search.h>
|
||||
#include <u-boot/crc.h>
|
||||
|
||||
#define READ_ONLY BIT(31)
|
||||
|
||||
/*
|
||||
* Mapping between EFI variables and u-boot variables:
|
||||
*
|
||||
* efi_$guid_$varname = {attributes}(type)value
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported=
|
||||
* "{ro,boot,run}(blob)0000000000000000"
|
||||
* efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder=
|
||||
* "(blob)00010000"
|
||||
*
|
||||
* The attributes are a comma separated list of these possible
|
||||
* attributes:
|
||||
*
|
||||
* + ro - read-only
|
||||
* + boot - boot-services access
|
||||
* + run - runtime access
|
||||
*
|
||||
* NOTE: with current implementation, no variables are available after
|
||||
* ExitBootServices, and all are persisted (if possible).
|
||||
*
|
||||
* If not specified, the attributes default to "{boot}".
|
||||
*
|
||||
* The required type is one of:
|
||||
*
|
||||
* + utf8 - raw utf8 string
|
||||
* + blob - arbitrary length hex string
|
||||
*
|
||||
* Maybe a utf16 type would be useful to for a string value to be auto
|
||||
* converted to utf16?
|
||||
*/
|
||||
|
||||
#define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_"))
|
||||
|
||||
/**
|
||||
* efi_to_native() - convert the UEFI variable name and vendor GUID to U-Boot
|
||||
* variable name
|
||||
*
|
||||
* The U-Boot variable name is a concatenation of prefix 'efi', the hexstring
|
||||
* encoded vendor GUID, and the UTF-8 encoded UEFI variable name separated by
|
||||
* underscores, e.g. 'efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder'.
|
||||
*
|
||||
* @native: pointer to pointer to U-Boot variable name
|
||||
* @variable_name: UEFI variable name
|
||||
* @vendor: vendor GUID
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t efi_to_native(char **native, const u16 *variable_name,
|
||||
const efi_guid_t *vendor)
|
||||
{
|
||||
size_t len;
|
||||
char *pos;
|
||||
|
||||
len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1;
|
||||
*native = malloc(len);
|
||||
if (!*native)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
pos = *native;
|
||||
pos += sprintf(pos, "efi_%pUl_", vendor);
|
||||
utf16_utf8_strcpy(&pos, variable_name);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* prefix() - skip over prefix
|
||||
*
|
||||
* Skip over a prefix string.
|
||||
*
|
||||
* @str: string with prefix
|
||||
* @prefix: prefix string
|
||||
* Return: string without prefix, or NULL if prefix not found
|
||||
*/
|
||||
static const char *prefix(const char *str, const char *prefix)
|
||||
{
|
||||
size_t n = strlen(prefix);
|
||||
if (!strncmp(prefix, str, n))
|
||||
return str + n;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse_attr() - decode attributes part of variable value
|
||||
*
|
||||
* Convert the string encoded attributes of a UEFI variable to a bit mask.
|
||||
* TODO: Several attributes are not supported.
|
||||
*
|
||||
* @str: value of U-Boot variable
|
||||
* @attrp: pointer to UEFI attributes
|
||||
* Return: pointer to remainder of U-Boot variable value
|
||||
*/
|
||||
static const char *parse_attr(const char *str, u32 *attrp)
|
||||
{
|
||||
u32 attr = 0;
|
||||
char sep = '{';
|
||||
|
||||
if (*str != '{') {
|
||||
*attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
|
||||
return str;
|
||||
}
|
||||
|
||||
while (*str == sep) {
|
||||
const char *s;
|
||||
|
||||
str++;
|
||||
|
||||
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"))) {
|
||||
attr |= EFI_VARIABLE_RUNTIME_ACCESS;
|
||||
} else {
|
||||
printf("invalid attribute: %s\n", str);
|
||||
break;
|
||||
}
|
||||
|
||||
str = s;
|
||||
sep = ',';
|
||||
}
|
||||
|
||||
str++;
|
||||
|
||||
*attrp = attr;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_get_variable() - retrieve value of a UEFI variable
|
||||
*
|
||||
* This function implements the GetVariable runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @variable_name: name of the variable
|
||||
* @vendor: vendor GUID
|
||||
* @attributes: attributes of the variable
|
||||
* @data_size: size of the buffer to which the variable value is copied
|
||||
* @data: buffer to which the variable value is copied
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
|
||||
const efi_guid_t *vendor, u32 *attributes,
|
||||
efi_uintn_t *data_size, void *data)
|
||||
{
|
||||
char *native_name;
|
||||
efi_status_t ret;
|
||||
unsigned long in_size;
|
||||
const char *val, *s;
|
||||
u32 attr;
|
||||
|
||||
EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
|
||||
data_size, data);
|
||||
|
||||
if (!variable_name || !vendor || !data_size)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
ret = efi_to_native(&native_name, variable_name, vendor);
|
||||
if (ret)
|
||||
return EFI_EXIT(ret);
|
||||
|
||||
EFI_PRINT("get '%s'\n", native_name);
|
||||
|
||||
val = env_get(native_name);
|
||||
free(native_name);
|
||||
if (!val)
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
|
||||
val = parse_attr(val, &attr);
|
||||
|
||||
in_size = *data_size;
|
||||
|
||||
if ((s = prefix(val, "(blob)"))) {
|
||||
size_t len = strlen(s);
|
||||
|
||||
/* number of hexadecimal digits must be even */
|
||||
if (len & 1)
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
|
||||
/* two characters per byte: */
|
||||
len /= 2;
|
||||
*data_size = len;
|
||||
|
||||
if (in_size < len) {
|
||||
ret = EFI_BUFFER_TOO_SMALL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!data)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
if (hex2bin(data, s, len))
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
|
||||
EFI_PRINT("got value: \"%s\"\n", s);
|
||||
} else if ((s = prefix(val, "(utf8)"))) {
|
||||
unsigned len = strlen(s) + 1;
|
||||
|
||||
*data_size = len;
|
||||
|
||||
if (in_size < len) {
|
||||
ret = EFI_BUFFER_TOO_SMALL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!data)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
memcpy(data, s, len);
|
||||
((char *)data)[len] = '\0';
|
||||
|
||||
EFI_PRINT("got value: \"%s\"\n", (char *)data);
|
||||
} else {
|
||||
EFI_PRINT("invalid value: '%s'\n", val);
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
}
|
||||
|
||||
out:
|
||||
if (attributes)
|
||||
*attributes = attr & EFI_VARIABLE_MASK;
|
||||
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
static char *efi_variables_list;
|
||||
static char *efi_cur_variable;
|
||||
|
||||
/**
|
||||
* parse_uboot_variable() - parse a u-boot variable and get uefi-related
|
||||
* information
|
||||
* @variable: whole data of u-boot variable (ie. name=value)
|
||||
* @variable_name_size: size of variable_name buffer in byte
|
||||
* @variable_name: name of uefi variable in u16, null-terminated
|
||||
* @vendor: vendor's guid
|
||||
* @attributes: attributes
|
||||
*
|
||||
* A uefi variable is encoded into a u-boot variable as described above.
|
||||
* This function parses such a u-boot variable and retrieve uefi-related
|
||||
* information into respective parameters. In return, variable_name_size
|
||||
* is the size of variable name including NULL.
|
||||
*
|
||||
* Return: EFI_SUCCESS if parsing is OK, EFI_NOT_FOUND when
|
||||
* the entire variable list has been returned,
|
||||
* otherwise non-zero status code
|
||||
*/
|
||||
static efi_status_t parse_uboot_variable(char *variable,
|
||||
efi_uintn_t *variable_name_size,
|
||||
u16 *variable_name,
|
||||
const efi_guid_t *vendor,
|
||||
u32 *attributes)
|
||||
{
|
||||
char *guid, *name, *end, c;
|
||||
unsigned long name_len;
|
||||
u16 *p;
|
||||
|
||||
guid = strchr(variable, '_');
|
||||
if (!guid)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
guid++;
|
||||
name = strchr(guid, '_');
|
||||
if (!name)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
name++;
|
||||
end = strchr(name, '=');
|
||||
if (!end)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
name_len = end - name;
|
||||
if (*variable_name_size < (name_len + 1)) {
|
||||
*variable_name_size = name_len + 1;
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
end++; /* point to value */
|
||||
|
||||
/* variable name */
|
||||
p = variable_name;
|
||||
utf8_utf16_strncpy(&p, name, name_len);
|
||||
variable_name[name_len] = 0;
|
||||
*variable_name_size = name_len + 1;
|
||||
|
||||
/* guid */
|
||||
c = *(name - 1);
|
||||
*(name - 1) = '\0'; /* guid need be null-terminated here */
|
||||
uuid_str_to_bin(guid, (unsigned char *)vendor, UUID_STR_FORMAT_GUID);
|
||||
*(name - 1) = c;
|
||||
|
||||
/* attributes */
|
||||
parse_attr(end, attributes);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_get_next_variable_name() - enumerate the current variable names
|
||||
*
|
||||
* @variable_name_size: size of variable_name buffer in byte
|
||||
* @variable_name: name of uefi variable's name in u16
|
||||
* @vendor: vendor's guid
|
||||
*
|
||||
* This function implements the GetNextVariableName service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
|
||||
u16 *variable_name,
|
||||
const efi_guid_t *vendor)
|
||||
{
|
||||
char *native_name, *variable;
|
||||
ssize_t name_len, list_len;
|
||||
char regex[256];
|
||||
char * const regexlist[] = {regex};
|
||||
u32 attributes;
|
||||
int i;
|
||||
efi_status_t ret;
|
||||
|
||||
EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
|
||||
|
||||
if (!variable_name_size || !variable_name || !vendor)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
if (variable_name[0]) {
|
||||
/* check null-terminated string */
|
||||
for (i = 0; i < *variable_name_size; i++)
|
||||
if (!variable_name[i])
|
||||
break;
|
||||
if (i >= *variable_name_size)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
/* search for the last-returned variable */
|
||||
ret = efi_to_native(&native_name, variable_name, vendor);
|
||||
if (ret)
|
||||
return EFI_EXIT(ret);
|
||||
|
||||
name_len = strlen(native_name);
|
||||
for (variable = efi_variables_list; variable && *variable;) {
|
||||
if (!strncmp(variable, native_name, name_len) &&
|
||||
variable[name_len] == '=')
|
||||
break;
|
||||
|
||||
variable = strchr(variable, '\n');
|
||||
if (variable)
|
||||
variable++;
|
||||
}
|
||||
|
||||
free(native_name);
|
||||
if (!(variable && *variable))
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
/* next variable */
|
||||
variable = strchr(variable, '\n');
|
||||
if (variable)
|
||||
variable++;
|
||||
if (!(variable && *variable))
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
} else {
|
||||
/*
|
||||
*new search: free a list used in the previous search
|
||||
*/
|
||||
free(efi_variables_list);
|
||||
efi_variables_list = NULL;
|
||||
efi_cur_variable = NULL;
|
||||
|
||||
snprintf(regex, 256, "efi_.*-.*-.*-.*-.*_.*");
|
||||
list_len = hexport_r(&env_htab, '\n',
|
||||
H_MATCH_REGEX | H_MATCH_KEY,
|
||||
&efi_variables_list, 0, 1, regexlist);
|
||||
/* 1 indicates that no match was found */
|
||||
if (list_len <= 1)
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
|
||||
variable = efi_variables_list;
|
||||
}
|
||||
|
||||
ret = parse_uboot_variable(variable, variable_name_size, variable_name,
|
||||
vendor, &attributes);
|
||||
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_set_variable() - set value of a UEFI variable
|
||||
*
|
||||
* This function implements the SetVariable runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @variable_name: name of the variable
|
||||
* @vendor: vendor GUID
|
||||
* @attributes: attributes of the variable
|
||||
* @data_size: size of the buffer with the variable value
|
||||
* @data: buffer with the variable value
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
|
||||
const efi_guid_t *vendor, u32 attributes,
|
||||
efi_uintn_t data_size, const void *data)
|
||||
{
|
||||
char *native_name = NULL, *val = NULL, *s;
|
||||
const char *old_val;
|
||||
size_t old_size;
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
u32 attr;
|
||||
|
||||
EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
|
||||
data_size, data);
|
||||
|
||||
if (!variable_name || !*variable_name || !vendor ||
|
||||
((attributes & EFI_VARIABLE_RUNTIME_ACCESS) &&
|
||||
!(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = efi_to_native(&native_name, variable_name, vendor);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
old_val = env_get(native_name);
|
||||
if (old_val) {
|
||||
old_val = parse_attr(old_val, &attr);
|
||||
|
||||
/* check read-only first */
|
||||
if (attr & READ_ONLY) {
|
||||
ret = EFI_WRITE_PROTECTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((data_size == 0 &&
|
||||
!(attributes & EFI_VARIABLE_APPEND_WRITE)) ||
|
||||
!attributes) {
|
||||
/* delete the variable: */
|
||||
env_set(native_name, NULL);
|
||||
ret = EFI_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* attributes won't be changed */
|
||||
if (attr != (attributes & ~EFI_VARIABLE_APPEND_WRITE)) {
|
||||
ret = EFI_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (attributes & EFI_VARIABLE_APPEND_WRITE) {
|
||||
if (!prefix(old_val, "(blob)")) {
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
goto out;
|
||||
}
|
||||
old_size = strlen(old_val);
|
||||
} else {
|
||||
old_size = 0;
|
||||
}
|
||||
} else {
|
||||
if (data_size == 0 || !attributes ||
|
||||
(attributes & EFI_VARIABLE_APPEND_WRITE)) {
|
||||
/*
|
||||
* Trying to delete or to update a non-existent
|
||||
* variable.
|
||||
*/
|
||||
ret = EFI_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
old_size = 0;
|
||||
}
|
||||
|
||||
val = malloc(old_size + 2 * data_size
|
||||
+ strlen("{ro,run,boot,nv}(blob)") + 1);
|
||||
if (!val) {
|
||||
ret = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
s = val;
|
||||
|
||||
/* store attributes */
|
||||
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_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");
|
||||
|
||||
attributes &= ~attr;
|
||||
if (attributes)
|
||||
s += sprintf(s, ",");
|
||||
}
|
||||
s += sprintf(s, "}");
|
||||
|
||||
if (old_size)
|
||||
/* APPEND_WRITE */
|
||||
s += sprintf(s, old_val);
|
||||
else
|
||||
s += sprintf(s, "(blob)");
|
||||
|
||||
/* store payload: */
|
||||
s = bin2hex(s, data, data_size);
|
||||
*s = '\0';
|
||||
|
||||
EFI_PRINT("setting: %s=%s\n", native_name, val);
|
||||
|
||||
if (env_set(native_name, val))
|
||||
ret = EFI_DEVICE_ERROR;
|
||||
|
||||
out:
|
||||
free(native_name);
|
||||
free(val);
|
||||
|
||||
return EFI_EXIT(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_query_variable_info() - get information about EFI variables
|
||||
*
|
||||
* This function implements the QueryVariableInfo() runtime service.
|
||||
*
|
||||
* See the Unified Extensible Firmware Interface (UEFI) specification for
|
||||
* details.
|
||||
*
|
||||
* @attributes: bitmask to select variables to be
|
||||
* queried
|
||||
* @maximum_variable_storage_size: maximum size of storage area for the
|
||||
* selected variable types
|
||||
* @remaining_variable_storage_size: remaining size of storage are for the
|
||||
* selected variable types
|
||||
* @maximum_variable_size: maximum size of a variable of the
|
||||
* selected type
|
||||
* Returns: status code
|
||||
*/
|
||||
efi_status_t __efi_runtime EFIAPI efi_query_variable_info(
|
||||
u32 attributes,
|
||||
u64 *maximum_variable_storage_size,
|
||||
u64 *remaining_variable_storage_size,
|
||||
u64 *maximum_variable_size)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_get_variable_runtime() - runtime implementation of GetVariable()
|
||||
*
|
||||
* @variable_name: name of the variable
|
||||
* @vendor: vendor GUID
|
||||
* @attributes: attributes of the variable
|
||||
* @data_size: size of the buffer to which the variable value is copied
|
||||
* @data: buffer to which the variable value is copied
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t __efi_runtime EFIAPI
|
||||
efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *vendor,
|
||||
u32 *attributes, efi_uintn_t *data_size, void *data)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_get_next_variable_name_runtime() - runtime implementation of
|
||||
* GetNextVariable()
|
||||
*
|
||||
* @variable_name_size: size of variable_name buffer in byte
|
||||
* @variable_name: name of uefi variable's name in u16
|
||||
* @vendor: vendor's guid
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t __efi_runtime EFIAPI
|
||||
efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
|
||||
u16 *variable_name, const efi_guid_t *vendor)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_set_variable_runtime() - runtime implementation of SetVariable()
|
||||
*
|
||||
* @variable_name: name of the variable
|
||||
* @vendor: vendor GUID
|
||||
* @attributes: attributes of the variable
|
||||
* @data_size: size of the buffer with the variable value
|
||||
* @data: buffer with the variable value
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t __efi_runtime EFIAPI
|
||||
efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor,
|
||||
u32 attributes, efi_uintn_t data_size,
|
||||
const void *data)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_variables_boot_exit_notify() - notify ExitBootServices() is called
|
||||
*/
|
||||
void efi_variables_boot_exit_notify(void)
|
||||
{
|
||||
efi_runtime_services.get_variable = efi_get_variable_runtime;
|
||||
efi_runtime_services.get_next_variable_name =
|
||||
efi_get_next_variable_name_runtime;
|
||||
efi_runtime_services.set_variable = efi_set_variable_runtime;
|
||||
efi_update_table_header_crc32(&efi_runtime_services.hdr);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_init_variables() - initialize variable services
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_init_variables(void)
|
||||
{
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI watchdog
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
|
||||
/* Conversion factor from seconds to multiples of 100ns */
|
||||
#define EFI_SECONDS_TO_100NS 10000000ULL
|
||||
|
||||
static struct efi_event *watchdog_timer_event;
|
||||
|
||||
/*
|
||||
* Reset the system when the watchdog event is notified.
|
||||
*
|
||||
* @event: the watchdog event
|
||||
* @context: not used
|
||||
*/
|
||||
static void EFIAPI efi_watchdog_timer_notify(struct efi_event *event,
|
||||
void *context)
|
||||
{
|
||||
EFI_ENTRY("%p, %p", event, context);
|
||||
|
||||
printf("\nEFI: Watchdog timeout\n");
|
||||
EFI_CALL_VOID(efi_runtime_services.reset_system(EFI_RESET_COLD,
|
||||
EFI_SUCCESS, 0, NULL));
|
||||
|
||||
EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the watchdog timer.
|
||||
*
|
||||
* This function is used by the SetWatchdogTimer service.
|
||||
*
|
||||
* @timeout: seconds before reset by watchdog
|
||||
* @return: status code
|
||||
*/
|
||||
efi_status_t efi_set_watchdog(unsigned long timeout)
|
||||
{
|
||||
efi_status_t r;
|
||||
|
||||
if (timeout)
|
||||
/* Reset watchdog */
|
||||
r = efi_set_timer(watchdog_timer_event, EFI_TIMER_RELATIVE,
|
||||
EFI_SECONDS_TO_100NS * timeout);
|
||||
else
|
||||
/* Deactivate watchdog */
|
||||
r = efi_set_timer(watchdog_timer_event, EFI_TIMER_STOP, 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the EFI watchdog.
|
||||
*
|
||||
* This function is called by efi_init_obj_list()
|
||||
*/
|
||||
efi_status_t efi_watchdog_register(void)
|
||||
{
|
||||
efi_status_t r;
|
||||
|
||||
/*
|
||||
* Create a timer event.
|
||||
*/
|
||||
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
|
||||
efi_watchdog_timer_notify, NULL, NULL,
|
||||
&watchdog_timer_event);
|
||||
if (r != EFI_SUCCESS) {
|
||||
printf("ERROR: Failed to register watchdog event\n");
|
||||
return r;
|
||||
}
|
||||
/*
|
||||
* The UEFI standard requires that the watchdog timer is set to five
|
||||
* minutes when invoking an EFI boot option.
|
||||
*
|
||||
* Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A
|
||||
* 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer
|
||||
*/
|
||||
r = efi_set_watchdog(300);
|
||||
if (r != EFI_SUCCESS) {
|
||||
printf("ERROR: Failed to set watchdog timer\n");
|
||||
return r;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI hello world
|
||||
*
|
||||
* Copyright (c) 2016 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* This program demonstrates calling a boottime service.
|
||||
* It writes a greeting and the load options to the console.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_api.h>
|
||||
|
||||
static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
|
||||
static const efi_guid_t fdt_guid = EFI_FDT_GUID;
|
||||
static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID;
|
||||
static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
|
||||
|
||||
/**
|
||||
* efi_main() - entry point of the EFI application.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI efi_main(efi_handle_t handle,
|
||||
struct efi_system_table *systable)
|
||||
{
|
||||
struct efi_simple_text_output_protocol *con_out = systable->con_out;
|
||||
struct efi_boot_services *boottime = systable->boottime;
|
||||
struct efi_loaded_image *loaded_image;
|
||||
efi_status_t ret;
|
||||
efi_uintn_t i;
|
||||
u16 rev[] = L"0.0.0";
|
||||
|
||||
/* UEFI requires CR LF */
|
||||
con_out->output_string(con_out, L"Hello, world!\r\n");
|
||||
|
||||
/* Print the revision number */
|
||||
rev[0] = (systable->hdr.revision >> 16) + '0';
|
||||
rev[4] = systable->hdr.revision & 0xffff;
|
||||
for (; rev[4] >= 10;) {
|
||||
rev[4] -= 10;
|
||||
++rev[2];
|
||||
}
|
||||
/* Third digit is only to be shown if non-zero */
|
||||
if (rev[4])
|
||||
rev[4] += '0';
|
||||
else
|
||||
rev[3] = 0;
|
||||
|
||||
con_out->output_string(con_out, L"Running on UEFI ");
|
||||
con_out->output_string(con_out, rev);
|
||||
con_out->output_string(con_out, L"\r\n");
|
||||
|
||||
/* Get the loaded image protocol */
|
||||
ret = boottime->handle_protocol(handle, &loaded_image_guid,
|
||||
(void **)&loaded_image);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
con_out->output_string
|
||||
(con_out, L"Cannot open loaded image protocol\r\n");
|
||||
goto out;
|
||||
}
|
||||
/* Find configuration tables */
|
||||
for (i = 0; i < systable->nr_tables; ++i) {
|
||||
if (!memcmp(&systable->tables[i].guid, &fdt_guid,
|
||||
sizeof(efi_guid_t)))
|
||||
con_out->output_string
|
||||
(con_out, L"Have device tree\r\n");
|
||||
if (!memcmp(&systable->tables[i].guid, &acpi_guid,
|
||||
sizeof(efi_guid_t)))
|
||||
con_out->output_string
|
||||
(con_out, L"Have ACPI 2.0 table\r\n");
|
||||
if (!memcmp(&systable->tables[i].guid, &smbios_guid,
|
||||
sizeof(efi_guid_t)))
|
||||
con_out->output_string
|
||||
(con_out, L"Have SMBIOS table\r\n");
|
||||
}
|
||||
/* Output the load options */
|
||||
con_out->output_string(con_out, L"Load options: ");
|
||||
if (loaded_image->load_options_size && loaded_image->load_options)
|
||||
con_out->output_string(con_out,
|
||||
(u16 *)loaded_image->load_options);
|
||||
else
|
||||
con_out->output_string(con_out, L"<none>");
|
||||
con_out->output_string(con_out, L"\r\n");
|
||||
|
||||
out:
|
||||
boottime->exit(handle, ret, 0, NULL);
|
||||
|
||||
/* We should never arrive here */
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user