avionic design with actual uboot and tooling
submodule of avionic design uboot bootloader and with included tools to get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
18
u-boot/lib/efi_loader/Kconfig
Normal file
18
u-boot/lib/efi_loader/Kconfig
Normal file
@@ -0,0 +1,18 @@
|
||||
config EFI_LOADER
|
||||
bool "Support running EFI Applications in U-Boot"
|
||||
depends on (ARM64 || ARM) && OF_LIBFDT
|
||||
default y
|
||||
help
|
||||
Select this option if you want to run EFI applications (like grub2)
|
||||
on top of U-Boot. If this option is enabled, U-Boot will expose EFI
|
||||
interfaces to a loaded EFI application, enabling it to reuse U-Boot's
|
||||
device drivers.
|
||||
|
||||
config EFI_LOADER_BOUNCE_BUFFER
|
||||
bool "EFI Applications use bounce buffers for DMA operations"
|
||||
depends on EFI_LOADER && 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.
|
||||
14
u-boot/lib/efi_loader/Makefile
Normal file
14
u-boot/lib/efi_loader/Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
#
|
||||
# (C) Copyright 2016 Alexander Graf
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
# This file only gets included with CONFIG_EFI_LOADER set, so all
|
||||
# object inclusion implicitly depends on it
|
||||
|
||||
obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
|
||||
obj-y += efi_memory.o
|
||||
obj-$(CONFIG_LCD) += efi_gop.o
|
||||
obj-$(CONFIG_PARTITIONS) += efi_disk.o
|
||||
obj-$(CONFIG_NET) += efi_net.o
|
||||
795
u-boot/lib/efi_loader/efi_boottime.c
Normal file
795
u-boot/lib/efi_loader/efi_boottime.c
Normal file
@@ -0,0 +1,795 @@
|
||||
/*
|
||||
* EFI application boot time services
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/global_data.h>
|
||||
#include <libfdt_env.h>
|
||||
#include <u-boot/crc.h>
|
||||
#include <bootm.h>
|
||||
#include <inttypes.h>
|
||||
#include <watchdog.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* This list contains all the EFI objects our payload has access to */
|
||||
LIST_HEAD(efi_obj_list);
|
||||
|
||||
/*
|
||||
* If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
|
||||
* we need to do trickery with caches. Since we don't want to break the EFI
|
||||
* aware boot path, only apply hacks when loading exiting directly (breaking
|
||||
* direct Linux EFI booting along the way - oh well).
|
||||
*/
|
||||
static bool efi_is_direct_boot = true;
|
||||
|
||||
/*
|
||||
* EFI can pass arbitrary additional "tables" containing vendor specific
|
||||
* information to the payload. One such table is the FDT table which contains
|
||||
* a pointer to a flattened device tree blob.
|
||||
*
|
||||
* In most cases we want to pass an FDT to the payload, so reserve one slot of
|
||||
* config table space for it. The pointer gets populated by do_bootefi_exec().
|
||||
*/
|
||||
static struct efi_configuration_table EFI_RUNTIME_DATA efi_conf_table[1];
|
||||
|
||||
/*
|
||||
* The "gd" pointer lives in a register on ARM and AArch64 that we declare
|
||||
* fixed when compiling U-Boot. However, the payload does not know about that
|
||||
* restriction so we need to manually swap its and our view of that register on
|
||||
* EFI callback entry/exit.
|
||||
*/
|
||||
static volatile void *efi_gd, *app_gd;
|
||||
|
||||
/* Called from do_bootefi_exec() */
|
||||
void efi_save_gd(void)
|
||||
{
|
||||
efi_gd = gd;
|
||||
}
|
||||
|
||||
/* Called on every callback entry */
|
||||
void efi_restore_gd(void)
|
||||
{
|
||||
/* Only restore if we're already in EFI context */
|
||||
if (!efi_gd)
|
||||
return;
|
||||
|
||||
if (gd != efi_gd)
|
||||
app_gd = gd;
|
||||
gd = efi_gd;
|
||||
}
|
||||
|
||||
/* Called on every callback exit */
|
||||
efi_status_t efi_exit_func(efi_status_t ret)
|
||||
{
|
||||
gd = app_gd;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static efi_status_t efi_unsupported(const char *funcname)
|
||||
{
|
||||
debug("EFI: App called into unimplemented function %s\n", funcname);
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
static int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
|
||||
{
|
||||
return memcmp(g1, g2, sizeof(efi_guid_t));
|
||||
}
|
||||
|
||||
static unsigned long EFIAPI efi_raise_tpl(unsigned long new_tpl)
|
||||
{
|
||||
EFI_ENTRY("0x%lx", new_tpl);
|
||||
return EFI_EXIT(0);
|
||||
}
|
||||
|
||||
static void EFIAPI efi_restore_tpl(unsigned long old_tpl)
|
||||
{
|
||||
EFI_ENTRY("0x%lx", old_tpl);
|
||||
EFI_EXIT(efi_unsupported(__func__));
|
||||
}
|
||||
|
||||
efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
|
||||
unsigned long pages,
|
||||
uint64_t *memory)
|
||||
{
|
||||
efi_status_t r;
|
||||
|
||||
EFI_ENTRY("%d, %d, 0x%lx, %p", type, memory_type, pages, memory);
|
||||
r = efi_allocate_pages(type, memory_type, pages, memory);
|
||||
return EFI_EXIT(r);
|
||||
}
|
||||
|
||||
efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory, unsigned long pages)
|
||||
{
|
||||
efi_status_t r;
|
||||
|
||||
EFI_ENTRY("%"PRIx64", 0x%lx", memory, pages);
|
||||
r = efi_free_pages(memory, pages);
|
||||
return EFI_EXIT(r);
|
||||
}
|
||||
|
||||
efi_status_t EFIAPI efi_get_memory_map_ext(unsigned long *memory_map_size,
|
||||
struct efi_mem_desc *memory_map,
|
||||
unsigned long *map_key,
|
||||
unsigned long *descriptor_size,
|
||||
uint32_t *descriptor_version)
|
||||
{
|
||||
efi_status_t r;
|
||||
|
||||
EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map,
|
||||
map_key, descriptor_size, descriptor_version);
|
||||
r = efi_get_memory_map(memory_map_size, memory_map, map_key,
|
||||
descriptor_size, descriptor_version);
|
||||
return EFI_EXIT(r);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_allocate_pool(int pool_type, unsigned long size,
|
||||
void **buffer)
|
||||
{
|
||||
efi_status_t r;
|
||||
|
||||
EFI_ENTRY("%d, %ld, %p", pool_type, size, buffer);
|
||||
r = efi_allocate_pages(0, pool_type, (size + 0xfff) >> 12, (void*)buffer);
|
||||
return EFI_EXIT(r);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_free_pool(void *buffer)
|
||||
{
|
||||
efi_status_t r;
|
||||
|
||||
EFI_ENTRY("%p", buffer);
|
||||
r = efi_free_pages((ulong)buffer, 0);
|
||||
return EFI_EXIT(r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Our event capabilities are very limited. Only support a single
|
||||
* event to exist, so we don't need to maintain lists.
|
||||
*/
|
||||
static struct {
|
||||
enum efi_event_type type;
|
||||
u32 trigger_type;
|
||||
u32 trigger_time;
|
||||
u64 trigger_next;
|
||||
unsigned long notify_tpl;
|
||||
void (*notify_function) (void *event, void *context);
|
||||
void *notify_context;
|
||||
} efi_event = {
|
||||
/* Disable timers on bootup */
|
||||
.trigger_next = -1ULL,
|
||||
};
|
||||
|
||||
static efi_status_t EFIAPI efi_create_event(
|
||||
enum efi_event_type type, ulong notify_tpl,
|
||||
void (*notify_function) (void *event, void *context),
|
||||
void *notify_context, void **event)
|
||||
{
|
||||
EFI_ENTRY("%d, 0x%lx, %p, %p", type, notify_tpl, notify_function,
|
||||
notify_context);
|
||||
if (efi_event.notify_function) {
|
||||
/* We only support one event at a time */
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
efi_event.type = type;
|
||||
efi_event.notify_tpl = notify_tpl;
|
||||
efi_event.notify_function = notify_function;
|
||||
efi_event.notify_context = notify_context;
|
||||
*event = &efi_event;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Our timers have to work without interrupts, so we check whenever keyboard
|
||||
* input or disk accesses happen if enough time elapsed for it to fire.
|
||||
*/
|
||||
void efi_timer_check(void)
|
||||
{
|
||||
u64 now = timer_get_us();
|
||||
|
||||
if (now >= efi_event.trigger_next) {
|
||||
/* Triggering! */
|
||||
if (efi_event.trigger_type == EFI_TIMER_PERIODIC)
|
||||
efi_event.trigger_next += efi_event.trigger_time / 10;
|
||||
efi_event.notify_function(&efi_event, efi_event.notify_context);
|
||||
}
|
||||
|
||||
WATCHDOG_RESET();
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_set_timer(void *event, int type,
|
||||
uint64_t trigger_time)
|
||||
{
|
||||
/* We don't have 64bit division available everywhere, so limit timer
|
||||
* distances to 32bit bits. */
|
||||
u32 trigger32 = trigger_time;
|
||||
|
||||
EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time);
|
||||
|
||||
if (trigger32 < trigger_time) {
|
||||
printf("WARNING: Truncating timer from %"PRIx64" to %x\n",
|
||||
trigger_time, trigger32);
|
||||
}
|
||||
|
||||
if (event != &efi_event) {
|
||||
/* We only support one event at a time */
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case EFI_TIMER_STOP:
|
||||
efi_event.trigger_next = -1ULL;
|
||||
break;
|
||||
case EFI_TIMER_PERIODIC:
|
||||
case EFI_TIMER_RELATIVE:
|
||||
efi_event.trigger_next = timer_get_us() + (trigger32 / 10);
|
||||
break;
|
||||
default:
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
}
|
||||
efi_event.trigger_type = type;
|
||||
efi_event.trigger_time = trigger_time;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events,
|
||||
void *event, unsigned long *index)
|
||||
{
|
||||
u64 now;
|
||||
|
||||
EFI_ENTRY("%ld, %p, %p", num_events, event, index);
|
||||
|
||||
now = timer_get_us();
|
||||
while (now < efi_event.trigger_next) { }
|
||||
efi_timer_check();
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_signal_event(void *event)
|
||||
{
|
||||
EFI_ENTRY("%p", event);
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_close_event(void *event)
|
||||
{
|
||||
EFI_ENTRY("%p", event);
|
||||
efi_event.trigger_next = -1ULL;
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_check_event(void *event)
|
||||
{
|
||||
EFI_ENTRY("%p", event);
|
||||
return EFI_EXIT(EFI_NOT_READY);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_install_protocol_interface(void **handle,
|
||||
efi_guid_t *protocol, int protocol_interface_type,
|
||||
void *protocol_interface)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %d, %p", handle, protocol, protocol_interface_type,
|
||||
protocol_interface);
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle,
|
||||
efi_guid_t *protocol, void *old_interface,
|
||||
void *new_interface)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p, %p", handle, protocol, old_interface,
|
||||
new_interface);
|
||||
return EFI_EXIT(EFI_ACCESS_DENIED);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_uninstall_protocol_interface(void *handle,
|
||||
efi_guid_t *protocol, void *protocol_interface)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface);
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol,
|
||||
void *event,
|
||||
void **registration)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p", protocol, event, registration);
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
static int efi_search(enum efi_locate_search_type search_type,
|
||||
efi_guid_t *protocol, void *search_key,
|
||||
struct efi_object *efiobj)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (search_type) {
|
||||
case all_handles:
|
||||
return 0;
|
||||
case by_register_notify:
|
||||
return -1;
|
||||
case by_protocol:
|
||||
for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
|
||||
const efi_guid_t *guid = efiobj->protocols[i].guid;
|
||||
if (guid && !guidcmp(guid, protocol))
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_locate_handle(
|
||||
enum efi_locate_search_type search_type,
|
||||
efi_guid_t *protocol, void *search_key,
|
||||
unsigned long *buffer_size, efi_handle_t *buffer)
|
||||
{
|
||||
struct list_head *lhandle;
|
||||
unsigned long size = 0;
|
||||
|
||||
EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
|
||||
buffer_size, buffer);
|
||||
|
||||
/* Count how much space we need */
|
||||
list_for_each(lhandle, &efi_obj_list) {
|
||||
struct efi_object *efiobj;
|
||||
efiobj = list_entry(lhandle, struct efi_object, link);
|
||||
if (!efi_search(search_type, protocol, search_key, efiobj)) {
|
||||
size += sizeof(void*);
|
||||
}
|
||||
}
|
||||
|
||||
if (*buffer_size < size) {
|
||||
*buffer_size = size;
|
||||
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
/* Then fill the array */
|
||||
list_for_each(lhandle, &efi_obj_list) {
|
||||
struct efi_object *efiobj;
|
||||
efiobj = list_entry(lhandle, struct efi_object, link);
|
||||
if (!efi_search(search_type, protocol, search_key, efiobj)) {
|
||||
*(buffer++) = efiobj->handle;
|
||||
}
|
||||
}
|
||||
|
||||
*buffer_size = size;
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol,
|
||||
struct efi_device_path **device_path,
|
||||
efi_handle_t *device)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p", protocol, device_path, device);
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_install_configuration_table(efi_guid_t *guid,
|
||||
void *table)
|
||||
{
|
||||
int i;
|
||||
|
||||
EFI_ENTRY("%p, %p", guid, table);
|
||||
|
||||
/* Check for guid override */
|
||||
for (i = 0; i < systab.nr_tables; i++) {
|
||||
if (!guidcmp(guid, &efi_conf_table[i].guid)) {
|
||||
efi_conf_table[i].table = table;
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/* No override, check for overflow */
|
||||
if (i >= ARRAY_SIZE(efi_conf_table))
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
|
||||
/* Add a new entry */
|
||||
memcpy(&efi_conf_table[i].guid, guid, sizeof(*guid));
|
||||
efi_conf_table[i].table = table;
|
||||
systab.nr_tables = i;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_load_image(bool boot_policy,
|
||||
efi_handle_t parent_image,
|
||||
struct efi_device_path *file_path,
|
||||
void *source_buffer,
|
||||
unsigned long source_size,
|
||||
efi_handle_t *image_handle)
|
||||
{
|
||||
static struct efi_object loaded_image_info_obj = {
|
||||
.protocols = {
|
||||
{
|
||||
.guid = &efi_guid_loaded_image,
|
||||
.open = &efi_return_handle,
|
||||
},
|
||||
},
|
||||
};
|
||||
struct efi_loaded_image *info;
|
||||
struct efi_object *obj;
|
||||
|
||||
EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image,
|
||||
file_path, source_buffer, source_size, image_handle);
|
||||
info = malloc(sizeof(*info));
|
||||
obj = malloc(sizeof(loaded_image_info_obj));
|
||||
memset(info, 0, sizeof(*info));
|
||||
memcpy(obj, &loaded_image_info_obj, sizeof(loaded_image_info_obj));
|
||||
obj->handle = info;
|
||||
info->file_path = file_path;
|
||||
info->reserved = efi_load_pe(source_buffer, info);
|
||||
if (!info->reserved) {
|
||||
free(info);
|
||||
free(obj);
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
*image_handle = info;
|
||||
list_add_tail(&obj->link, &efi_obj_list);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
|
||||
unsigned long *exit_data_size,
|
||||
s16 **exit_data)
|
||||
{
|
||||
ulong (*entry)(void *image_handle, struct efi_system_table *st);
|
||||
struct efi_loaded_image *info = image_handle;
|
||||
|
||||
EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
|
||||
entry = info->reserved;
|
||||
|
||||
efi_is_direct_boot = false;
|
||||
|
||||
/* call the image! */
|
||||
if (setjmp(&info->exit_jmp)) {
|
||||
/* We returned from the child image */
|
||||
return EFI_EXIT(info->exit_status);
|
||||
}
|
||||
|
||||
entry(image_handle, &systab);
|
||||
|
||||
/* Should usually never get here */
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
|
||||
efi_status_t exit_status, unsigned long exit_data_size,
|
||||
int16_t *exit_data)
|
||||
{
|
||||
struct efi_loaded_image *loaded_image_info = (void*)image_handle;
|
||||
|
||||
EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
|
||||
exit_data_size, exit_data);
|
||||
|
||||
loaded_image_info->exit_status = exit_status;
|
||||
longjmp(&loaded_image_info->exit_jmp);
|
||||
|
||||
panic("EFI application exited");
|
||||
}
|
||||
|
||||
static struct efi_object *efi_search_obj(void *handle)
|
||||
{
|
||||
struct list_head *lhandle;
|
||||
|
||||
list_for_each(lhandle, &efi_obj_list) {
|
||||
struct efi_object *efiobj;
|
||||
efiobj = list_entry(lhandle, struct efi_object, link);
|
||||
if (efiobj->handle == handle)
|
||||
return efiobj;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_unload_image(void *image_handle)
|
||||
{
|
||||
struct efi_object *efiobj;
|
||||
|
||||
EFI_ENTRY("%p", image_handle);
|
||||
efiobj = efi_search_obj(image_handle);
|
||||
if (efiobj)
|
||||
list_del(&efiobj->link);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static void efi_exit_caches(void)
|
||||
{
|
||||
#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
|
||||
/*
|
||||
* Grub on 32bit ARM needs to have caches disabled before jumping into
|
||||
* a zImage, but does not know of all cache layers. Give it a hand.
|
||||
*/
|
||||
if (efi_is_direct_boot)
|
||||
cleanup_before_linux();
|
||||
#endif
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
|
||||
unsigned long map_key)
|
||||
{
|
||||
EFI_ENTRY("%p, %ld", image_handle, map_key);
|
||||
|
||||
/* Fix up caches for EFI payloads if necessary */
|
||||
efi_exit_caches();
|
||||
|
||||
/* This stops all lingering devices */
|
||||
bootm_disable_interrupts();
|
||||
|
||||
/* Give the payload some time to boot */
|
||||
WATCHDOG_RESET();
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
|
||||
{
|
||||
static uint64_t mono = 0;
|
||||
EFI_ENTRY("%p", count);
|
||||
*count = mono++;
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_stall(unsigned long microseconds)
|
||||
{
|
||||
EFI_ENTRY("%ld", microseconds);
|
||||
udelay(microseconds);
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
|
||||
uint64_t watchdog_code,
|
||||
unsigned long data_size,
|
||||
uint16_t *watchdog_data)
|
||||
{
|
||||
EFI_ENTRY("%ld, 0x%"PRIx64", %ld, %p", timeout, watchdog_code,
|
||||
data_size, watchdog_data);
|
||||
return EFI_EXIT(efi_unsupported(__func__));
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_connect_controller(
|
||||
efi_handle_t controller_handle,
|
||||
efi_handle_t *driver_image_handle,
|
||||
struct efi_device_path *remain_device_path,
|
||||
bool recursive)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle,
|
||||
remain_device_path, recursive);
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle,
|
||||
void *driver_image_handle,
|
||||
void *child_handle)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
|
||||
child_handle);
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_close_protocol(void *handle,
|
||||
efi_guid_t *protocol,
|
||||
void *agent_handle,
|
||||
void *controller_handle)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p, %p", handle, protocol, agent_handle,
|
||||
controller_handle);
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle,
|
||||
efi_guid_t *protocol,
|
||||
struct efi_open_protocol_info_entry **entry_buffer,
|
||||
unsigned long *entry_count)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p, %p", handle, protocol, entry_buffer,
|
||||
entry_count);
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_protocols_per_handle(void *handle,
|
||||
efi_guid_t ***protocol_buffer,
|
||||
unsigned long *protocol_buffer_count)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p", handle, protocol_buffer,
|
||||
protocol_buffer_count);
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_locate_handle_buffer(
|
||||
enum efi_locate_search_type search_type,
|
||||
efi_guid_t *protocol, void *search_key,
|
||||
unsigned long *no_handles, efi_handle_t **buffer)
|
||||
{
|
||||
EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
|
||||
no_handles, buffer);
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
static struct efi_class_map efi_class_maps[] = {
|
||||
{
|
||||
.guid = &efi_guid_console_control,
|
||||
.interface = &efi_console_control
|
||||
},
|
||||
};
|
||||
|
||||
static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol,
|
||||
void *registration,
|
||||
void **protocol_interface)
|
||||
{
|
||||
int i;
|
||||
|
||||
EFI_ENTRY("%p, %p, %p", protocol, registration, protocol_interface);
|
||||
for (i = 0; i < ARRAY_SIZE(efi_class_maps); i++) {
|
||||
struct efi_class_map *curmap = &efi_class_maps[i];
|
||||
if (!guidcmp(protocol, curmap->guid)) {
|
||||
*protocol_interface = (void*)curmap->interface;
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_EXIT(EFI_NOT_FOUND);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces(
|
||||
void **handle, ...)
|
||||
{
|
||||
EFI_ENTRY("%p", handle);
|
||||
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces(
|
||||
void *handle, ...)
|
||||
{
|
||||
EFI_ENTRY("%p", handle);
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_calculate_crc32(void *data,
|
||||
unsigned long data_size,
|
||||
uint32_t *crc32_p)
|
||||
{
|
||||
EFI_ENTRY("%p, %ld", data, data_size);
|
||||
*crc32_p = crc32(0, data, data_size);
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static void EFIAPI efi_copy_mem(void *destination, void *source,
|
||||
unsigned long length)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %ld", destination, source, length);
|
||||
memcpy(destination, source, length);
|
||||
}
|
||||
|
||||
static void EFIAPI efi_set_mem(void *buffer, unsigned long size, uint8_t value)
|
||||
{
|
||||
EFI_ENTRY("%p, %ld, 0x%x", buffer, size, value);
|
||||
memset(buffer, value, size);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_open_protocol(
|
||||
void *handle, efi_guid_t *protocol,
|
||||
void **protocol_interface, void *agent_handle,
|
||||
void *controller_handle, uint32_t attributes)
|
||||
{
|
||||
struct list_head *lhandle;
|
||||
int i;
|
||||
efi_status_t r = EFI_UNSUPPORTED;
|
||||
|
||||
EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol,
|
||||
protocol_interface, agent_handle, controller_handle,
|
||||
attributes);
|
||||
list_for_each(lhandle, &efi_obj_list) {
|
||||
struct efi_object *efiobj;
|
||||
efiobj = list_entry(lhandle, struct efi_object, link);
|
||||
|
||||
if (efiobj->handle != handle)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
|
||||
struct efi_handler *handler = &efiobj->protocols[i];
|
||||
const efi_guid_t *hprotocol = handler->guid;
|
||||
if (!hprotocol)
|
||||
break;
|
||||
if (!guidcmp(hprotocol, protocol)) {
|
||||
r = handler->open(handle, protocol,
|
||||
protocol_interface, agent_handle,
|
||||
controller_handle, attributes);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return EFI_EXIT(r);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_handle_protocol(void *handle,
|
||||
efi_guid_t *protocol,
|
||||
void **protocol_interface)
|
||||
{
|
||||
return efi_open_protocol(handle, protocol, protocol_interface,
|
||||
NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static const struct efi_boot_services efi_boot_services = {
|
||||
.hdr = {
|
||||
.headersize = sizeof(struct efi_table_hdr),
|
||||
},
|
||||
.raise_tpl = efi_raise_tpl,
|
||||
.restore_tpl = efi_restore_tpl,
|
||||
.allocate_pages = efi_allocate_pages_ext,
|
||||
.free_pages = efi_free_pages_ext,
|
||||
.get_memory_map = efi_get_memory_map_ext,
|
||||
.allocate_pool = efi_allocate_pool,
|
||||
.free_pool = efi_free_pool,
|
||||
.create_event = efi_create_event,
|
||||
.set_timer = efi_set_timer,
|
||||
.wait_for_event = efi_wait_for_event,
|
||||
.signal_event = efi_signal_event,
|
||||
.close_event = efi_close_event,
|
||||
.check_event = efi_check_event,
|
||||
.install_protocol_interface = efi_install_protocol_interface,
|
||||
.reinstall_protocol_interface = efi_reinstall_protocol_interface,
|
||||
.uninstall_protocol_interface = efi_uninstall_protocol_interface,
|
||||
.handle_protocol = efi_handle_protocol,
|
||||
.reserved = NULL,
|
||||
.register_protocol_notify = efi_register_protocol_notify,
|
||||
.locate_handle = efi_locate_handle,
|
||||
.locate_device_path = efi_locate_device_path,
|
||||
.install_configuration_table = efi_install_configuration_table,
|
||||
.load_image = efi_load_image,
|
||||
.start_image = efi_start_image,
|
||||
.exit = efi_exit,
|
||||
.unload_image = efi_unload_image,
|
||||
.exit_boot_services = efi_exit_boot_services,
|
||||
.get_next_monotonic_count = efi_get_next_monotonic_count,
|
||||
.stall = efi_stall,
|
||||
.set_watchdog_timer = efi_set_watchdog_timer,
|
||||
.connect_controller = efi_connect_controller,
|
||||
.disconnect_controller = efi_disconnect_controller,
|
||||
.open_protocol = efi_open_protocol,
|
||||
.close_protocol = efi_close_protocol,
|
||||
.open_protocol_information = efi_open_protocol_information,
|
||||
.protocols_per_handle = efi_protocols_per_handle,
|
||||
.locate_handle_buffer = efi_locate_handle_buffer,
|
||||
.locate_protocol = efi_locate_protocol,
|
||||
.install_multiple_protocol_interfaces = efi_install_multiple_protocol_interfaces,
|
||||
.uninstall_multiple_protocol_interfaces = efi_uninstall_multiple_protocol_interfaces,
|
||||
.calculate_crc32 = efi_calculate_crc32,
|
||||
.copy_mem = efi_copy_mem,
|
||||
.set_mem = efi_set_mem,
|
||||
};
|
||||
|
||||
|
||||
static uint16_t EFI_RUNTIME_DATA firmware_vendor[] =
|
||||
{ 'D','a','s',' ','U','-','b','o','o','t',0 };
|
||||
|
||||
struct efi_system_table EFI_RUNTIME_DATA systab = {
|
||||
.hdr = {
|
||||
.signature = EFI_SYSTEM_TABLE_SIGNATURE,
|
||||
.revision = 0x20005, /* 2.5 */
|
||||
.headersize = sizeof(struct efi_table_hdr),
|
||||
},
|
||||
.fw_vendor = (long)firmware_vendor,
|
||||
.con_in = (void*)&efi_con_in,
|
||||
.con_out = (void*)&efi_con_out,
|
||||
.std_err = (void*)&efi_con_out,
|
||||
.runtime = (void*)&efi_runtime_services,
|
||||
.boottime = (void*)&efi_boot_services,
|
||||
.nr_tables = 0,
|
||||
.tables = (void*)efi_conf_table,
|
||||
};
|
||||
360
u-boot/lib/efi_loader/efi_console.c
Normal file
360
u-boot/lib/efi_loader/efi_console.c
Normal file
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* EFI application console interface
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
|
||||
/* If we can't determine the console size, default to 80x24 */
|
||||
static int console_columns = 80;
|
||||
static int console_rows = 24;
|
||||
static bool console_size_queried;
|
||||
|
||||
const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID;
|
||||
|
||||
#define cESC '\x1b'
|
||||
#define ESC "\x1b"
|
||||
|
||||
static efi_status_t EFIAPI efi_cin_get_mode(
|
||||
struct efi_console_control_protocol *this,
|
||||
int *mode, char *uga_exists, char *std_in_locked)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked);
|
||||
|
||||
if (mode)
|
||||
*mode = EFI_CONSOLE_MODE_TEXT;
|
||||
if (uga_exists)
|
||||
*uga_exists = 0;
|
||||
if (std_in_locked)
|
||||
*std_in_locked = 0;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cin_set_mode(
|
||||
struct efi_console_control_protocol *this, int mode)
|
||||
{
|
||||
EFI_ENTRY("%p, %d", this, mode);
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cin_lock_std_in(
|
||||
struct efi_console_control_protocol *this,
|
||||
uint16_t *password)
|
||||
{
|
||||
EFI_ENTRY("%p, %p", this, password);
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
const struct efi_console_control_protocol efi_console_control = {
|
||||
.get_mode = efi_cin_get_mode,
|
||||
.set_mode = efi_cin_set_mode,
|
||||
.lock_std_in = efi_cin_lock_std_in,
|
||||
};
|
||||
|
||||
static struct simple_text_output_mode efi_con_mode = {
|
||||
.max_mode = 0,
|
||||
.mode = 0,
|
||||
.attribute = 0,
|
||||
.cursor_column = 0,
|
||||
.cursor_row = 0,
|
||||
.cursor_visible = 1,
|
||||
};
|
||||
|
||||
static int term_read_reply(int *n, int maxnum, char end_char)
|
||||
{
|
||||
char c;
|
||||
int i = 0;
|
||||
|
||||
c = getc();
|
||||
if (c != cESC)
|
||||
return -1;
|
||||
c = getc();
|
||||
if (c != '[')
|
||||
return -1;
|
||||
|
||||
n[0] = 0;
|
||||
while (1) {
|
||||
c = getc();
|
||||
if (c == ';') {
|
||||
i++;
|
||||
if (i >= maxnum)
|
||||
return -1;
|
||||
n[i] = 0;
|
||||
continue;
|
||||
} else if (c == end_char) {
|
||||
break;
|
||||
} else if (c > '9' || c < '0') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read one more decimal position */
|
||||
n[i] *= 10;
|
||||
n[i] += c - '0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cout_reset(
|
||||
struct efi_simple_text_output_protocol *this,
|
||||
char extended_verification)
|
||||
{
|
||||
EFI_ENTRY("%p, %d", this, extended_verification);
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
static void print_unicode_in_utf8(u16 c)
|
||||
{
|
||||
char utf8[4] = { 0 };
|
||||
char *b = utf8;
|
||||
|
||||
if (c < 0x80) {
|
||||
*(b++) = c;
|
||||
} else if (c < 0x800) {
|
||||
*(b++) = 192 + c / 64;
|
||||
*(b++) = 128 + c % 64;
|
||||
} else {
|
||||
*(b++) = 224 + c / 4096;
|
||||
*(b++) = 128 + c / 64 % 64;
|
||||
*(b++) = 128 + c % 64;
|
||||
}
|
||||
|
||||
puts(utf8);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cout_output_string(
|
||||
struct efi_simple_text_output_protocol *this,
|
||||
const unsigned short *string)
|
||||
{
|
||||
u16 ch;
|
||||
|
||||
EFI_ENTRY("%p, %p", this, string);
|
||||
for (;(ch = *string); string++) {
|
||||
print_unicode_in_utf8(ch);
|
||||
efi_con_mode.cursor_column++;
|
||||
if (ch == '\n') {
|
||||
efi_con_mode.cursor_column = 1;
|
||||
efi_con_mode.cursor_row++;
|
||||
} else if (efi_con_mode.cursor_column > console_columns) {
|
||||
efi_con_mode.cursor_column = 1;
|
||||
efi_con_mode.cursor_row++;
|
||||
}
|
||||
if (efi_con_mode.cursor_row > console_rows) {
|
||||
efi_con_mode.cursor_row = console_rows;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cout_test_string(
|
||||
struct efi_simple_text_output_protocol *this,
|
||||
const unsigned short *string)
|
||||
{
|
||||
EFI_ENTRY("%p, %p", this, string);
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cout_query_mode(
|
||||
struct efi_simple_text_output_protocol *this,
|
||||
unsigned long mode_number, unsigned long *columns,
|
||||
unsigned long *rows)
|
||||
{
|
||||
EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
|
||||
|
||||
if (!console_size_queried) {
|
||||
/* Ask the terminal about its size */
|
||||
int n[3];
|
||||
u64 timeout;
|
||||
|
||||
console_size_queried = true;
|
||||
|
||||
/* Empty input buffer */
|
||||
while (tstc())
|
||||
getc();
|
||||
|
||||
printf(ESC"[18t");
|
||||
|
||||
/* Check if we have a terminal that understands */
|
||||
timeout = timer_get_us() + 1000000;
|
||||
while (!tstc())
|
||||
if (timer_get_us() > timeout)
|
||||
goto out;
|
||||
|
||||
/* Read {depth,rows,cols} */
|
||||
if (term_read_reply(n, 3, 't')) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
console_columns = n[2];
|
||||
console_rows = n[1];
|
||||
}
|
||||
|
||||
out:
|
||||
if (columns)
|
||||
*columns = console_columns;
|
||||
if (rows)
|
||||
*rows = console_rows;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cout_set_mode(
|
||||
struct efi_simple_text_output_protocol *this,
|
||||
unsigned long mode_number)
|
||||
{
|
||||
EFI_ENTRY("%p, %ld", this, mode_number);
|
||||
|
||||
/* We only support text output for now */
|
||||
if (mode_number == EFI_CONSOLE_MODE_TEXT)
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cout_set_attribute(
|
||||
struct efi_simple_text_output_protocol *this,
|
||||
unsigned long attribute)
|
||||
{
|
||||
EFI_ENTRY("%p, %lx", this, attribute);
|
||||
|
||||
/* Just ignore attributes (colors) for now */
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cout_clear_screen(
|
||||
struct efi_simple_text_output_protocol *this)
|
||||
{
|
||||
EFI_ENTRY("%p", this);
|
||||
|
||||
printf(ESC"[2J");
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cout_set_cursor_position(
|
||||
struct efi_simple_text_output_protocol *this,
|
||||
unsigned long column, unsigned long row)
|
||||
{
|
||||
EFI_ENTRY("%p, %ld, %ld", this, column, row);
|
||||
|
||||
printf(ESC"[%d;%df", (int)row, (int)column);
|
||||
efi_con_mode.cursor_column = column;
|
||||
efi_con_mode.cursor_row = row;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cout_enable_cursor(
|
||||
struct efi_simple_text_output_protocol *this,
|
||||
bool enable)
|
||||
{
|
||||
EFI_ENTRY("%p, %d", this, enable);
|
||||
|
||||
printf(ESC"[?25%c", enable ? 'h' : 'l');
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
const struct efi_simple_text_output_protocol efi_con_out = {
|
||||
.reset = efi_cout_reset,
|
||||
.output_string = efi_cout_output_string,
|
||||
.test_string = efi_cout_test_string,
|
||||
.query_mode = efi_cout_query_mode,
|
||||
.set_mode = efi_cout_set_mode,
|
||||
.set_attribute = efi_cout_set_attribute,
|
||||
.clear_screen = efi_cout_clear_screen,
|
||||
.set_cursor_position = efi_cout_set_cursor_position,
|
||||
.enable_cursor = efi_cout_enable_cursor,
|
||||
.mode = (void*)&efi_con_mode,
|
||||
};
|
||||
|
||||
static efi_status_t EFIAPI efi_cin_reset(
|
||||
struct efi_simple_input_interface *this,
|
||||
bool extended_verification)
|
||||
{
|
||||
EFI_ENTRY("%p, %d", this, extended_verification);
|
||||
return EFI_EXIT(EFI_UNSUPPORTED);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_cin_read_key_stroke(
|
||||
struct efi_simple_input_interface *this,
|
||||
struct efi_input_key *key)
|
||||
{
|
||||
struct efi_input_key pressed_key = {
|
||||
.scan_code = 0,
|
||||
.unicode_char = 0,
|
||||
};
|
||||
char ch;
|
||||
|
||||
EFI_ENTRY("%p, %p", this, key);
|
||||
|
||||
/* We don't do interrupts, so check for timers cooperatively */
|
||||
efi_timer_check();
|
||||
|
||||
if (!tstc()) {
|
||||
/* No key pressed */
|
||||
return EFI_EXIT(EFI_NOT_READY);
|
||||
}
|
||||
|
||||
ch = getc();
|
||||
if (ch == cESC) {
|
||||
/* Escape Sequence */
|
||||
ch = getc();
|
||||
switch (ch) {
|
||||
case cESC: /* ESC */
|
||||
pressed_key.scan_code = 23;
|
||||
break;
|
||||
case 'O': /* F1 - F4 */
|
||||
pressed_key.scan_code = getc() - 'P' + 11;
|
||||
break;
|
||||
case 'a'...'z':
|
||||
ch = ch - 'a';
|
||||
break;
|
||||
case '[':
|
||||
ch = getc();
|
||||
switch (ch) {
|
||||
case 'A'...'D': /* up, down right, left */
|
||||
pressed_key.scan_code = ch - 'A' + 1;
|
||||
break;
|
||||
case 'F': /* End */
|
||||
pressed_key.scan_code = 6;
|
||||
break;
|
||||
case 'H': /* Home */
|
||||
pressed_key.scan_code = 5;
|
||||
break;
|
||||
case '1': /* F5 - F8 */
|
||||
pressed_key.scan_code = getc() - '0' + 11;
|
||||
getc();
|
||||
break;
|
||||
case '2': /* F9 - F12 */
|
||||
pressed_key.scan_code = getc() - '0' + 19;
|
||||
getc();
|
||||
break;
|
||||
case '3': /* DEL */
|
||||
pressed_key.scan_code = 8;
|
||||
getc();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (ch == 0x7f) {
|
||||
/* Backspace */
|
||||
ch = 0x08;
|
||||
}
|
||||
pressed_key.unicode_char = ch;
|
||||
*key = pressed_key;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
const struct efi_simple_input_interface efi_con_in = {
|
||||
.reset = efi_cin_reset,
|
||||
.read_key_stroke = efi_cin_read_key_stroke,
|
||||
.wait_for_key = NULL,
|
||||
};
|
||||
341
u-boot/lib/efi_loader/efi_disk.c
Normal file
341
u-boot/lib/efi_loader/efi_disk.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* EFI application disk support
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <blk.h>
|
||||
#include <dm.h>
|
||||
#include <efi_loader.h>
|
||||
#include <inttypes.h>
|
||||
#include <part.h>
|
||||
#include <malloc.h>
|
||||
|
||||
static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID;
|
||||
|
||||
struct efi_disk_obj {
|
||||
/* Generic EFI object parent class data */
|
||||
struct efi_object parent;
|
||||
/* EFI Interface callback struct for block I/O */
|
||||
struct efi_block_io ops;
|
||||
/* U-Boot ifname for block device */
|
||||
const char *ifname;
|
||||
/* U-Boot dev_index for block device */
|
||||
int dev_index;
|
||||
/* EFI Interface Media descriptor struct, referenced by ops */
|
||||
struct efi_block_io_media media;
|
||||
/* EFI device path to this block device */
|
||||
struct efi_device_path_file_path *dp;
|
||||
/* Offset into disk for simple partitions */
|
||||
lbaint_t offset;
|
||||
};
|
||||
|
||||
static efi_status_t efi_disk_open_block(void *handle, efi_guid_t *protocol,
|
||||
void **protocol_interface, void *agent_handle,
|
||||
void *controller_handle, uint32_t attributes)
|
||||
{
|
||||
struct efi_disk_obj *diskobj = handle;
|
||||
|
||||
*protocol_interface = &diskobj->ops;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t efi_disk_open_dp(void *handle, efi_guid_t *protocol,
|
||||
void **protocol_interface, void *agent_handle,
|
||||
void *controller_handle, uint32_t attributes)
|
||||
{
|
||||
struct efi_disk_obj *diskobj = handle;
|
||||
|
||||
*protocol_interface = diskobj->dp;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
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_DEVICE_ERROR);
|
||||
}
|
||||
|
||||
enum efi_disk_direction {
|
||||
EFI_DISK_READ,
|
||||
EFI_DISK_WRITE,
|
||||
};
|
||||
|
||||
static efi_status_t EFIAPI 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);
|
||||
if (!(desc = blk_get_dev(diskobj->ifname, diskobj->dev_index)))
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
blksz = desc->blksz;
|
||||
blocks = buffer_size / blksz;
|
||||
lba += diskobj->offset;
|
||||
|
||||
debug("EFI: %s:%d blocks=%x lba=%"PRIx64" blksz=%x dir=%d\n", __func__,
|
||||
__LINE__, blocks, lba, blksz, direction);
|
||||
|
||||
/* We only support full block access */
|
||||
if (buffer_size & (blksz - 1))
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
|
||||
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();
|
||||
|
||||
debug("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks);
|
||||
|
||||
if (n != blocks)
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t efi_disk_read_blocks(struct efi_block_io *this,
|
||||
u32 media_id, u64 lba, unsigned long buffer_size,
|
||||
void *buffer)
|
||||
{
|
||||
void *real_buffer = buffer;
|
||||
efi_status_t r;
|
||||
|
||||
#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, %"PRIx64", %lx, %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 efi_disk_write_blocks(struct efi_block_io *this,
|
||||
u32 media_id, u64 lba, unsigned long buffer_size,
|
||||
void *buffer)
|
||||
{
|
||||
void *real_buffer = buffer;
|
||||
efi_status_t r;
|
||||
|
||||
#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, %"PRIx64", %lx, %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,
|
||||
};
|
||||
|
||||
static void efi_disk_add_dev(const char *name,
|
||||
const char *if_typename,
|
||||
const struct blk_desc *desc,
|
||||
int dev_index,
|
||||
lbaint_t offset)
|
||||
{
|
||||
struct efi_disk_obj *diskobj;
|
||||
struct efi_device_path_file_path *dp;
|
||||
int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2);
|
||||
|
||||
diskobj = calloc(1, objlen);
|
||||
|
||||
/* Fill in object data */
|
||||
diskobj->parent.protocols[0].guid = &efi_block_io_guid;
|
||||
diskobj->parent.protocols[0].open = efi_disk_open_block;
|
||||
diskobj->parent.protocols[1].guid = &efi_guid_device_path;
|
||||
diskobj->parent.protocols[1].open = efi_disk_open_dp;
|
||||
diskobj->parent.handle = diskobj;
|
||||
diskobj->ops = block_io_disk_template;
|
||||
diskobj->ifname = if_typename;
|
||||
diskobj->dev_index = dev_index;
|
||||
diskobj->offset = offset;
|
||||
|
||||
/* Fill in EFI IO Media info (for read/write callbacks) */
|
||||
diskobj->media.removable_media = desc->removable;
|
||||
diskobj->media.media_present = 1;
|
||||
diskobj->media.block_size = desc->blksz;
|
||||
diskobj->media.io_align = desc->blksz;
|
||||
diskobj->media.last_block = desc->lba;
|
||||
diskobj->ops.media = &diskobj->media;
|
||||
|
||||
/* Fill in device path */
|
||||
dp = (void*)&diskobj[1];
|
||||
diskobj->dp = dp;
|
||||
dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
|
||||
dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
|
||||
dp[0].dp.length = sizeof(*dp);
|
||||
ascii2unicode(dp[0].str, name);
|
||||
|
||||
dp[1].dp.type = DEVICE_PATH_TYPE_END;
|
||||
dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END;
|
||||
dp[1].dp.length = sizeof(*dp);
|
||||
|
||||
/* Hook up to the device list */
|
||||
list_add_tail(&diskobj->parent.link, &efi_obj_list);
|
||||
}
|
||||
|
||||
static int efi_disk_create_eltorito(struct blk_desc *desc,
|
||||
const char *if_typename,
|
||||
int diskid)
|
||||
{
|
||||
int disks = 0;
|
||||
#ifdef CONFIG_ISO_PARTITION
|
||||
char devname[32] = { 0 }; /* dp->str is u16[32] long */
|
||||
disk_partition_t info;
|
||||
int part = 1;
|
||||
|
||||
if (desc->part_type != PART_TYPE_ISO)
|
||||
return 0;
|
||||
|
||||
while (!part_get_info(desc, part, &info)) {
|
||||
snprintf(devname, sizeof(devname), "%s%d:%d", if_typename,
|
||||
diskid, part);
|
||||
efi_disk_add_dev(devname, if_typename, desc, diskid,
|
||||
info.start);
|
||||
part++;
|
||||
disks++;
|
||||
}
|
||||
#endif
|
||||
|
||||
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().
|
||||
*/
|
||||
int efi_disk_register(void)
|
||||
{
|
||||
int disks = 0;
|
||||
#ifdef CONFIG_BLK
|
||||
struct udevice *dev;
|
||||
|
||||
for (uclass_first_device(UCLASS_BLK, &dev);
|
||||
dev;
|
||||
uclass_next_device(&dev)) {
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
const char *if_typename = dev->driver->name;
|
||||
|
||||
printf("Scanning disk %s...\n", dev->name);
|
||||
efi_disk_add_dev(dev->name, if_typename, desc, desc->devnum, 0);
|
||||
disks++;
|
||||
|
||||
/*
|
||||
* El Torito images show up as block devices in an EFI world,
|
||||
* so let's create them here
|
||||
*/
|
||||
disks += efi_disk_create_eltorito(desc, if_typename,
|
||||
desc->devnum);
|
||||
}
|
||||
#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);
|
||||
efi_disk_add_dev(devname, if_typename, desc, i, 0);
|
||||
disks++;
|
||||
|
||||
/*
|
||||
* El Torito images show up as block devices
|
||||
* in an EFI world, so let's create them here
|
||||
*/
|
||||
disks += efi_disk_create_eltorito(desc, if_typename, i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
printf("Found %d disks\n", disks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
192
u-boot/lib/efi_loader/efi_gop.c
Normal file
192
u-boot/lib/efi_loader/efi_gop.c
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* EFI application disk support
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <efi_loader.h>
|
||||
#include <inttypes.h>
|
||||
#include <lcd.h>
|
||||
#include <malloc.h>
|
||||
#include <video.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static const efi_guid_t efi_gop_guid = EFI_GOP_GUID;
|
||||
|
||||
struct efi_gop_obj {
|
||||
/* Generic EFI object parent class data */
|
||||
struct efi_object parent;
|
||||
/* EFI Interface callback struct for gop */
|
||||
struct efi_gop ops;
|
||||
/* The only mode we support */
|
||||
struct efi_gop_mode_info info;
|
||||
struct efi_gop_mode mode;
|
||||
/* Fields we only have acces to during init */
|
||||
u32 bpix;
|
||||
};
|
||||
|
||||
static efi_status_t EFIAPI gop_query_mode(struct efi_gop *this, u32 mode_number,
|
||||
unsigned long *size_of_info,
|
||||
struct efi_gop_mode_info **info)
|
||||
{
|
||||
struct efi_gop_obj *gopobj;
|
||||
|
||||
EFI_ENTRY("%p, %x, %p, %p", this, mode_number, size_of_info, info);
|
||||
|
||||
gopobj = container_of(this, struct efi_gop_obj, ops);
|
||||
*size_of_info = sizeof(gopobj->info);
|
||||
*info = &gopobj->info;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
|
||||
{
|
||||
EFI_ENTRY("%p, %x", this, mode_number);
|
||||
|
||||
if (mode_number != 0)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
|
||||
unsigned long operation, unsigned long sx,
|
||||
unsigned long sy, unsigned long dx,
|
||||
unsigned long dy, unsigned long width,
|
||||
unsigned long height, unsigned long delta)
|
||||
{
|
||||
struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
|
||||
int i, j, line_len16, line_len32;
|
||||
void *fb;
|
||||
|
||||
EFI_ENTRY("%p, %p, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx", this,
|
||||
buffer, operation, sx, sy, dx, dy, width, height, delta);
|
||||
|
||||
if (operation != EFI_BLT_BUFFER_TO_VIDEO)
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
|
||||
fb = (void*)gd->fb_base;
|
||||
line_len16 = gopobj->info.width * sizeof(u16);
|
||||
line_len32 = gopobj->info.width * sizeof(u32);
|
||||
|
||||
/* Copy the contents line by line */
|
||||
|
||||
switch (gopobj->bpix) {
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
case VIDEO_BPP32:
|
||||
#else
|
||||
case LCD_COLOR32:
|
||||
#endif
|
||||
for (i = 0; i < height; i++) {
|
||||
u32 *dest = fb + ((i + dy) * line_len32) +
|
||||
(dx * sizeof(u32));
|
||||
u32 *src = buffer + ((i + sy) * line_len32) +
|
||||
(sx * sizeof(u32));
|
||||
|
||||
/* Same color format, just memcpy */
|
||||
memcpy(dest, src, width * sizeof(u32));
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
case VIDEO_BPP16:
|
||||
#else
|
||||
case LCD_COLOR16:
|
||||
#endif
|
||||
for (i = 0; i < height; i++) {
|
||||
u16 *dest = fb + ((i + dy) * line_len16) +
|
||||
(dx * sizeof(u16));
|
||||
u32 *src = buffer + ((i + sy) * line_len32) +
|
||||
(sx * sizeof(u32));
|
||||
|
||||
/* Convert from rgb888 to rgb565 */
|
||||
for (j = 0; j < width; j++) {
|
||||
u32 rgb888 = src[j];
|
||||
dest[j] = ((((rgb888 >> (16 + 3)) & 0x1f) << 11) |
|
||||
(((rgb888 >> (8 + 2)) & 0x3f) << 5) |
|
||||
(((rgb888 >> (0 + 3)) & 0x1f) << 0));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
video_sync_all();
|
||||
#else
|
||||
lcd_sync();
|
||||
#endif
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
/* This gets called from do_bootefi_exec(). */
|
||||
int efi_gop_register(void)
|
||||
{
|
||||
struct efi_gop_obj *gopobj;
|
||||
u32 bpix, col, row;
|
||||
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
struct udevice *vdev;
|
||||
|
||||
/* We only support a single video output device for now */
|
||||
if (uclass_first_device(UCLASS_VIDEO, &vdev))
|
||||
return -1;
|
||||
|
||||
struct video_priv *priv = dev_get_uclass_priv(vdev);
|
||||
bpix = priv->bpix;
|
||||
col = video_get_xsize(vdev);
|
||||
row = video_get_ysize(vdev);
|
||||
#else
|
||||
|
||||
bpix = panel_info.vl_bpix;
|
||||
col = panel_info.vl_col;
|
||||
row = panel_info.vl_row;
|
||||
#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 */
|
||||
return -1;
|
||||
}
|
||||
|
||||
gopobj = calloc(1, sizeof(*gopobj));
|
||||
|
||||
/* Fill in object data */
|
||||
gopobj->parent.protocols[0].guid = &efi_gop_guid;
|
||||
gopobj->parent.protocols[0].open = efi_return_handle;
|
||||
gopobj->parent.handle = &gopobj->ops;
|
||||
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->info.version = 0;
|
||||
gopobj->info.width = col;
|
||||
gopobj->info.height = row;
|
||||
gopobj->info.pixel_format = EFI_GOT_RGBA8;
|
||||
gopobj->info.pixels_per_scanline = col;
|
||||
|
||||
gopobj->bpix = bpix;
|
||||
|
||||
/* Hook up to the device list */
|
||||
list_add_tail(&gopobj->parent.link, &efi_obj_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
185
u-boot/lib/efi_loader/efi_image_loader.c
Normal file
185
u-boot/lib/efi_loader/efi_image_loader.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* EFI image loader
|
||||
*
|
||||
* based partly on wine code
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <pe.h>
|
||||
#include <asm/global_data.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID;
|
||||
const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
|
||||
|
||||
efi_status_t EFIAPI efi_return_handle(void *handle, efi_guid_t *protocol,
|
||||
void **protocol_interface, void *agent_handle,
|
||||
void *controller_handle, uint32_t attributes)
|
||||
{
|
||||
*protocol_interface = handle;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static void efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
|
||||
unsigned long rel_size, void *efi_reloc)
|
||||
{
|
||||
const IMAGE_BASE_RELOCATION *end;
|
||||
int i;
|
||||
|
||||
end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
|
||||
while (rel < end - 1 && rel->SizeOfBlock) {
|
||||
const uint16_t *relocs = (const uint16_t *)(rel + 1);
|
||||
i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
|
||||
while (i--) {
|
||||
uint16_t offset = (*relocs & 0xfff) +
|
||||
rel->VirtualAddress;
|
||||
int type = *relocs >> EFI_PAGE_SHIFT;
|
||||
unsigned long delta = (unsigned long)efi_reloc;
|
||||
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;
|
||||
default:
|
||||
printf("Unknown Relocation off %x type %x\n",
|
||||
offset, type);
|
||||
}
|
||||
relocs++;
|
||||
}
|
||||
rel = (const IMAGE_BASE_RELOCATION *)relocs;
|
||||
}
|
||||
}
|
||||
|
||||
void __weak invalidate_icache_all(void)
|
||||
{
|
||||
/* If the system doesn't support icache_all flush, cross our fingers */
|
||||
}
|
||||
|
||||
/*
|
||||
* This function loads all sections from a PE binary into a newly reserved
|
||||
* piece of memory. On successful load it then returns the entry point for
|
||||
* the binary. Otherwise NULL.
|
||||
*/
|
||||
void *efi_load_pe(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;
|
||||
void *entry;
|
||||
uint64_t image_size;
|
||||
unsigned long virt_size = 0;
|
||||
bool can_run_nt64 = true;
|
||||
bool can_run_nt32 = true;
|
||||
|
||||
#if defined(CONFIG_ARM64)
|
||||
can_run_nt32 = false;
|
||||
#elif defined(CONFIG_ARM)
|
||||
can_run_nt64 = false;
|
||||
#endif
|
||||
|
||||
dos = efi;
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||
printf("%s: Invalid DOS Signature\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nt = (void *) ((char *)efi + dos->e_lfanew);
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE) {
|
||||
printf("%s: Invalid NT Signature\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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 (can_run_nt64 &&
|
||||
(nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
|
||||
IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
|
||||
IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
|
||||
image_size = opt->SizeOfImage;
|
||||
efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA);
|
||||
if (!efi_reloc) {
|
||||
printf("%s: Could not allocate %ld bytes\n",
|
||||
__func__, virt_size);
|
||||
return NULL;
|
||||
}
|
||||
entry = efi_reloc + opt->AddressOfEntryPoint;
|
||||
rel_size = opt->DataDirectory[rel_idx].Size;
|
||||
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
|
||||
} else if (can_run_nt32 &&
|
||||
(nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
|
||||
IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
|
||||
image_size = opt->SizeOfImage;
|
||||
efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA);
|
||||
if (!efi_reloc) {
|
||||
printf("%s: Could not allocate %ld bytes\n",
|
||||
__func__, virt_size);
|
||||
return NULL;
|
||||
}
|
||||
entry = efi_reloc + opt->AddressOfEntryPoint;
|
||||
rel_size = opt->DataDirectory[rel_idx].Size;
|
||||
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
|
||||
} else {
|
||||
printf("%s: Invalid optional header magic %x\n", __func__,
|
||||
nt->OptionalHeader.Magic);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
efi_loader_relocate(rel, rel_size, efi_reloc);
|
||||
|
||||
/* Flush cache */
|
||||
flush_cache((ulong)efi_reloc, virt_size);
|
||||
invalidate_icache_all();
|
||||
|
||||
/* Populate the loaded image interface bits */
|
||||
loaded_image_info->image_base = efi;
|
||||
loaded_image_info->image_size = image_size;
|
||||
|
||||
return entry;
|
||||
}
|
||||
402
u-boot/lib/efi_loader/efi_memory.c
Normal file
402
u-boot/lib/efi_loader/efi_memory.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* EFI application memory management
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/global_data.h>
|
||||
#include <libfdt_env.h>
|
||||
#include <linux/list_sort.h>
|
||||
#include <inttypes.h>
|
||||
#include <watchdog.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
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
|
||||
|
||||
/*
|
||||
* 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 void efi_mem_sort(void)
|
||||
{
|
||||
list_sort(NULL, &efi_mem, efi_mem_cmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmaps all memory occupied by the carve_desc region from the
|
||||
* list entry pointed to by map.
|
||||
*
|
||||
* Returns 1 if carving was performed or 0 if the regions don't overlap.
|
||||
* Returns -1 if it would affect non-RAM regions but overlap_only_ram is set.
|
||||
* Carving is only guaranteed to complete when all regions return 0.
|
||||
*/
|
||||
static int 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);
|
||||
}
|
||||
|
||||
map_desc->physical_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.num_pages = (map_end - carve_start) >> EFI_PAGE_SHIFT;
|
||||
list_add_tail(&newmap->link, &efi_mem);
|
||||
|
||||
/* Shrink the map to [ map_start ... carve_start ] */
|
||||
map_desc->num_pages = (carve_start - map_start) >> EFI_PAGE_SHIFT;
|
||||
|
||||
return EFI_CARVE_LOOP_AGAIN;
|
||||
}
|
||||
|
||||
uint64_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;
|
||||
|
||||
if (!pages)
|
||||
return start;
|
||||
|
||||
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 = (1 << EFI_MEMORY_WB_SHIFT) |
|
||||
(1ULL << EFI_MEMORY_RUNTIME_SHIFT);
|
||||
break;
|
||||
case EFI_MMAP_IO:
|
||||
newlist->desc.attribute = 1ULL << EFI_MEMORY_RUNTIME_SHIFT;
|
||||
break;
|
||||
default:
|
||||
newlist->desc.attribute = 1 << EFI_MEMORY_WB_SHIFT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add our new map */
|
||||
do {
|
||||
carve_again = false;
|
||||
list_for_each(lhandle, &efi_mem) {
|
||||
struct efi_mem_list *lmem;
|
||||
int 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 0;
|
||||
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 0;
|
||||
}
|
||||
|
||||
/* Add our new map */
|
||||
list_add_tail(&newlist->link, &efi_mem);
|
||||
|
||||
/* And make sure memory is listed in descending order */
|
||||
efi_mem_sort();
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr)
|
||||
{
|
||||
struct list_head *lhandle;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
efi_status_t efi_allocate_pages(int type, int memory_type,
|
||||
unsigned long pages, uint64_t *memory)
|
||||
{
|
||||
u64 len = pages << EFI_PAGE_SHIFT;
|
||||
efi_status_t r = EFI_SUCCESS;
|
||||
uint64_t addr;
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
/* Any page */
|
||||
addr = efi_find_free_memory(len, gd->start_addr_sp);
|
||||
if (!addr) {
|
||||
r = EFI_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* Max address */
|
||||
addr = efi_find_free_memory(len, *memory);
|
||||
if (!addr) {
|
||||
r = EFI_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* Exact address, reserve it. The addr is already in *memory. */
|
||||
addr = *memory;
|
||||
break;
|
||||
default:
|
||||
/* UEFI doesn't specify other allocation types */
|
||||
r = EFI_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
if (r == EFI_SUCCESS) {
|
||||
uint64_t ret;
|
||||
|
||||
/* Reserve that map in our memory maps */
|
||||
ret = efi_add_memory_map(addr, pages, memory_type, true);
|
||||
if (ret == addr) {
|
||||
*memory = addr;
|
||||
} else {
|
||||
/* Map would overlap, bail out */
|
||||
r = EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void *efi_alloc(uint64_t len, int memory_type)
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
uint64_t pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
|
||||
efi_status_t r;
|
||||
|
||||
r = efi_allocate_pages(0, memory_type, pages, &ret);
|
||||
if (r == EFI_SUCCESS)
|
||||
return (void*)(uintptr_t)ret;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
efi_status_t efi_free_pages(uint64_t memory, unsigned long pages)
|
||||
{
|
||||
/* We don't free, let's cross our fingers we have plenty RAM */
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
efi_status_t efi_get_memory_map(unsigned long *memory_map_size,
|
||||
struct efi_mem_desc *memory_map,
|
||||
unsigned long *map_key,
|
||||
unsigned long *descriptor_size,
|
||||
uint32_t *descriptor_version)
|
||||
{
|
||||
ulong map_size = 0;
|
||||
int map_entries = 0;
|
||||
struct list_head *lhandle;
|
||||
|
||||
list_for_each(lhandle, &efi_mem)
|
||||
map_entries++;
|
||||
|
||||
map_size = map_entries * sizeof(struct efi_mem_desc);
|
||||
|
||||
*memory_map_size = map_size;
|
||||
|
||||
if (descriptor_size)
|
||||
*descriptor_size = sizeof(struct efi_mem_desc);
|
||||
|
||||
if (*memory_map_size < map_size)
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
|
||||
/* Copy list into array */
|
||||
if (memory_map) {
|
||||
/* 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--;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
int efi_memory_init(void)
|
||||
{
|
||||
unsigned long runtime_start, runtime_end, runtime_pages;
|
||||
unsigned long uboot_start, uboot_pages;
|
||||
unsigned long uboot_stack_size = 16 * 1024 * 1024;
|
||||
int i;
|
||||
|
||||
/* Add RAM */
|
||||
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
|
||||
u64 ram_start = gd->bd->bi_dram[i].start;
|
||||
u64 ram_size = gd->bd->bi_dram[i].size;
|
||||
u64 start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
|
||||
u64 pages = (ram_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
|
||||
|
||||
efi_add_memory_map(start, pages, EFI_CONVENTIONAL_MEMORY,
|
||||
false);
|
||||
}
|
||||
|
||||
/* Add U-Boot */
|
||||
uboot_start = (gd->start_addr_sp - uboot_stack_size) & ~EFI_PAGE_MASK;
|
||||
uboot_pages = (gd->ram_top - uboot_start) >> EFI_PAGE_SHIFT;
|
||||
efi_add_memory_map(uboot_start, uboot_pages, EFI_LOADER_DATA, false);
|
||||
|
||||
/* Add Runtime Services */
|
||||
runtime_start = (ulong)&__efi_runtime_start & ~EFI_PAGE_MASK;
|
||||
runtime_end = (ulong)&__efi_runtime_stop;
|
||||
runtime_end = (runtime_end + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
|
||||
runtime_pages = (runtime_end - runtime_start) >> EFI_PAGE_SHIFT;
|
||||
efi_add_memory_map(runtime_start, runtime_pages,
|
||||
EFI_RUNTIME_SERVICES_CODE, false);
|
||||
|
||||
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
|
||||
/* Request a 32bit 64MB bounce buffer region */
|
||||
uint64_t efi_bounce_buffer_addr = 0xffffffff;
|
||||
|
||||
if (efi_allocate_pages(1, 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;
|
||||
}
|
||||
291
u-boot/lib/efi_loader/efi_net.c
Normal file
291
u-boot/lib/efi_loader/efi_net.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* EFI application network access support
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <inttypes.h>
|
||||
#include <lcd.h>
|
||||
#include <malloc.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID;
|
||||
static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID;
|
||||
static struct efi_pxe_packet *dhcp_ack;
|
||||
static bool new_rx_packet;
|
||||
static void *new_tx_packet;
|
||||
|
||||
struct efi_net_obj {
|
||||
/* Generic EFI object parent class data */
|
||||
struct efi_object parent;
|
||||
/* EFI Interface callback struct for network */
|
||||
struct efi_simple_network net;
|
||||
struct efi_simple_network_mode net_mode;
|
||||
/* Device path to the network adapter */
|
||||
struct efi_device_path_file_path dp[2];
|
||||
/* PXE struct to transmit dhcp data */
|
||||
struct efi_pxe pxe;
|
||||
struct efi_pxe_mode pxe_mode;
|
||||
};
|
||||
|
||||
static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this)
|
||||
{
|
||||
EFI_ENTRY("%p", this);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
|
||||
{
|
||||
EFI_ENTRY("%p", this);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
|
||||
ulong extra_rx, ulong extra_tx)
|
||||
{
|
||||
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
|
||||
|
||||
eth_init();
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
|
||||
int extended_verification)
|
||||
{
|
||||
EFI_ENTRY("%p, %x", this, extended_verification);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this)
|
||||
{
|
||||
EFI_ENTRY("%p", this);
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* XXX Do we care? */
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
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_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
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_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
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_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac);
|
||||
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
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_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
|
||||
u32 *int_status, void **txbuf)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
|
||||
|
||||
/* We send packets synchronously, so nothing is outstanding */
|
||||
if (int_status)
|
||||
*int_status = 0;
|
||||
if (txbuf)
|
||||
*txbuf = new_tx_packet;
|
||||
|
||||
new_tx_packet = NULL;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this,
|
||||
ulong header_size, ulong buffer_size, void *buffer,
|
||||
struct efi_mac_address *src_addr,
|
||||
struct efi_mac_address *dest_addr, u16 *protocol)
|
||||
{
|
||||
EFI_ENTRY("%p, %lx, %lx, %p, %p, %p, %p", this, header_size,
|
||||
buffer_size, buffer, src_addr, dest_addr, protocol);
|
||||
|
||||
if (header_size) {
|
||||
/* We would need to create the header if header_size != 0 */
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
net_send_packet(buffer, buffer_size);
|
||||
new_tx_packet = buffer;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static void efi_net_push(void *pkt, int len)
|
||||
{
|
||||
new_rx_packet = true;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this,
|
||||
ulong *header_size, ulong *buffer_size, void *buffer,
|
||||
struct efi_mac_address *src_addr,
|
||||
struct efi_mac_address *dest_addr, u16 *protocol)
|
||||
{
|
||||
EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
|
||||
buffer_size, buffer, src_addr, dest_addr, protocol);
|
||||
|
||||
push_packet = efi_net_push;
|
||||
eth_rx();
|
||||
push_packet = NULL;
|
||||
|
||||
if (!new_rx_packet)
|
||||
return EFI_EXIT(EFI_NOT_READY);
|
||||
|
||||
if (*buffer_size < net_rx_packet_len) {
|
||||
/* Packet doesn't fit, try again with bigger buf */
|
||||
*buffer_size = net_rx_packet_len;
|
||||
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
memcpy(buffer, net_rx_packet, net_rx_packet_len);
|
||||
*buffer_size = net_rx_packet_len;
|
||||
new_rx_packet = false;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t efi_net_open_dp(void *handle, efi_guid_t *protocol,
|
||||
void **protocol_interface, void *agent_handle,
|
||||
void *controller_handle, uint32_t attributes)
|
||||
{
|
||||
struct efi_simple_network *net = handle;
|
||||
struct efi_net_obj *netobj = container_of(net, struct efi_net_obj, net);
|
||||
|
||||
*protocol_interface = netobj->dp;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t efi_net_open_pxe(void *handle, efi_guid_t *protocol,
|
||||
void **protocol_interface, void *agent_handle,
|
||||
void *controller_handle, uint32_t attributes)
|
||||
{
|
||||
struct efi_simple_network *net = handle;
|
||||
struct efi_net_obj *netobj = container_of(net, struct efi_net_obj, net);
|
||||
|
||||
*protocol_interface = &netobj->pxe;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/* This gets called from do_bootefi_exec(). */
|
||||
int efi_net_register(void **handle)
|
||||
{
|
||||
struct efi_net_obj *netobj;
|
||||
struct efi_device_path_file_path dp_net = {
|
||||
.dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
|
||||
.dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
|
||||
.dp.length = sizeof(dp_net),
|
||||
.str = { 'N', 'e', 't' },
|
||||
};
|
||||
struct efi_device_path_file_path dp_end = {
|
||||
.dp.type = DEVICE_PATH_TYPE_END,
|
||||
.dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
|
||||
.dp.length = sizeof(dp_end),
|
||||
};
|
||||
|
||||
if (!eth_get_dev()) {
|
||||
/* No eth device active, don't expose any */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We only expose the "active" eth device, so one is enough */
|
||||
netobj = calloc(1, sizeof(*netobj));
|
||||
|
||||
/* Fill in object data */
|
||||
netobj->parent.protocols[0].guid = &efi_net_guid;
|
||||
netobj->parent.protocols[0].open = efi_return_handle;
|
||||
netobj->parent.protocols[1].guid = &efi_guid_device_path;
|
||||
netobj->parent.protocols[1].open = efi_net_open_dp;
|
||||
netobj->parent.protocols[2].guid = &efi_pxe_guid;
|
||||
netobj->parent.protocols[2].open = efi_net_open_pxe;
|
||||
netobj->parent.handle = &netobj->net;
|
||||
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_STARTED;
|
||||
netobj->dp[0] = dp_net;
|
||||
netobj->dp[1] = dp_end;
|
||||
memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
|
||||
netobj->net_mode.max_packet_size = PKTSIZE;
|
||||
|
||||
netobj->pxe.mode = &netobj->pxe_mode;
|
||||
if (dhcp_ack)
|
||||
netobj->pxe_mode.dhcp_ack = *dhcp_ack;
|
||||
|
||||
/* Hook net up to the device list */
|
||||
list_add_tail(&netobj->parent.link, &efi_obj_list);
|
||||
|
||||
if (handle)
|
||||
*handle = &netobj->net;
|
||||
|
||||
return 0;
|
||||
}
|
||||
306
u-boot/lib/efi_loader/efi_runtime.c
Normal file
306
u-boot/lib/efi_loader/efi_runtime.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* EFI application runtime services
|
||||
*
|
||||
* Copyright (c) 2016 Alexander Graf
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <dm.h>
|
||||
#include <efi_loader.h>
|
||||
#include <rtc.h>
|
||||
#include <asm/global_data.h>
|
||||
|
||||
/* For manual relocation support */
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void);
|
||||
static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void);
|
||||
static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void);
|
||||
|
||||
#ifdef CONFIG_SYS_CACHELINE_SIZE
|
||||
#define EFI_CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
|
||||
#else
|
||||
/* Just use the greatest cache flush alignment requirement I'm aware of */
|
||||
#define EFI_CACHELINE_SIZE 128
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARM64)
|
||||
#define R_RELATIVE 1027
|
||||
#define R_MASK 0xffffffffULL
|
||||
#define IS_RELA 1
|
||||
#elif defined(CONFIG_ARM)
|
||||
#define R_RELATIVE 23
|
||||
#define R_MASK 0xffULL
|
||||
#else
|
||||
#error Need to add relocation awareness
|
||||
#endif
|
||||
|
||||
struct elf_rel {
|
||||
ulong *offset;
|
||||
ulong info;
|
||||
};
|
||||
|
||||
struct elf_rela {
|
||||
ulong *offset;
|
||||
ulong info;
|
||||
long addend;
|
||||
};
|
||||
|
||||
/*
|
||||
* EFI Runtime code lives in 2 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
|
||||
*/
|
||||
|
||||
static void EFIAPI efi_reset_system(enum efi_reset_type reset_type,
|
||||
efi_status_t reset_status,
|
||||
unsigned long data_size, void *reset_data)
|
||||
{
|
||||
EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
|
||||
reset_data);
|
||||
|
||||
switch (reset_type) {
|
||||
case EFI_RESET_COLD:
|
||||
case EFI_RESET_WARM:
|
||||
do_reset(NULL, 0, 0, NULL);
|
||||
break;
|
||||
case EFI_RESET_SHUTDOWN:
|
||||
/* We don't have anything to map this to */
|
||||
break;
|
||||
}
|
||||
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_get_time(struct efi_time *time,
|
||||
struct efi_time_cap *capabilities)
|
||||
{
|
||||
#if defined(CONFIG_CMD_DATE) && defined(CONFIG_DM_RTC)
|
||||
struct rtc_time tm;
|
||||
int r;
|
||||
struct udevice *dev;
|
||||
|
||||
EFI_ENTRY("%p %p", time, capabilities);
|
||||
|
||||
r = uclass_get_device(UCLASS_RTC, 0, &dev);
|
||||
if (r)
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
|
||||
r = dm_rtc_get(dev, &tm);
|
||||
if (r)
|
||||
return EFI_EXIT(EFI_DEVICE_ERROR);
|
||||
|
||||
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->daylight = tm.tm_isdst;
|
||||
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
#else
|
||||
return EFI_DEVICE_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct efi_runtime_detach_list_struct {
|
||||
void *ptr;
|
||||
void *patchto;
|
||||
};
|
||||
|
||||
static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
|
||||
{
|
||||
/* do_reset is gone */
|
||||
.ptr = &efi_runtime_services.reset_system,
|
||||
.patchto = NULL,
|
||||
}, {
|
||||
/* invalidate_*cache_all are gone */
|
||||
.ptr = &efi_runtime_services.set_virtual_address_map,
|
||||
.patchto = &efi_invalid_parameter,
|
||||
}, {
|
||||
/* RTC accessors are gone */
|
||||
.ptr = &efi_runtime_services.get_time,
|
||||
.patchto = &efi_device_error,
|
||||
}, {
|
||||
/* Clean up system table */
|
||||
.ptr = &systab.con_in,
|
||||
.patchto = NULL,
|
||||
}, {
|
||||
/* Clean up system table */
|
||||
.ptr = &systab.con_out,
|
||||
.patchto = NULL,
|
||||
}, {
|
||||
/* Clean up system table */
|
||||
.ptr = &systab.std_err,
|
||||
.patchto = NULL,
|
||||
}, {
|
||||
/* Clean up system table */
|
||||
.ptr = &systab.boottime,
|
||||
.patchto = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
static bool efi_runtime_tobedetached(void *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++)
|
||||
if (efi_runtime_detach_list[i].ptr == p)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void efi_runtime_detach(ulong offset)
|
||||
{
|
||||
int i;
|
||||
ulong patchoff = offset - (ulong)gd->relocaddr;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) {
|
||||
ulong patchto = (ulong)efi_runtime_detach_list[i].patchto;
|
||||
ulong *p = efi_runtime_detach_list[i].ptr;
|
||||
ulong newaddr = patchto ? (patchto + patchoff) : 0;
|
||||
|
||||
debug("%s: Setting %p to %lx\n", __func__, p, newaddr);
|
||||
*p = newaddr;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if ((rel->info & R_MASK) != R_RELATIVE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef IS_RELA
|
||||
newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE;
|
||||
#else
|
||||
newaddr = *p - lastoff + offset;
|
||||
#endif
|
||||
|
||||
/* Check if the relocation is inside bounds */
|
||||
if (map && ((newaddr < map->virtual_start) ||
|
||||
newaddr > (map->virtual_start + (map->num_pages << 12)))) {
|
||||
if (!efi_runtime_tobedetached(p))
|
||||
printf("U-Boot EFI: Relocation at %p is out of "
|
||||
"range (%lx)\n", 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();
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI efi_set_virtual_address_map(
|
||||
unsigned long memory_map_size,
|
||||
unsigned long descriptor_size,
|
||||
uint32_t descriptor_version,
|
||||
struct efi_mem_desc *virtmap)
|
||||
{
|
||||
ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL;
|
||||
int n = memory_map_size / descriptor_size;
|
||||
int i;
|
||||
|
||||
EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
|
||||
descriptor_version, virtmap);
|
||||
|
||||
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 - (runtime_start - gd->relocaddr);
|
||||
|
||||
efi_runtime_relocate(new_offset, map);
|
||||
/* Once we're virtual, we can no longer handle
|
||||
complex callbacks */
|
||||
efi_runtime_detach(new_offset);
|
||||
return EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_EXIT(EFI_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_TEXT 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.
|
||||
*/
|
||||
|
||||
static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void)
|
||||
{
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void)
|
||||
{
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = {
|
||||
.hdr = {
|
||||
.signature = EFI_RUNTIME_SERVICES_SIGNATURE,
|
||||
.revision = EFI_RUNTIME_SERVICES_REVISION,
|
||||
.headersize = sizeof(struct efi_table_hdr),
|
||||
},
|
||||
.get_time = &efi_get_time,
|
||||
.set_time = (void *)&efi_device_error,
|
||||
.get_wakeup_time = (void *)&efi_unimplemented,
|
||||
.set_wakeup_time = (void *)&efi_unimplemented,
|
||||
.set_virtual_address_map = &efi_set_virtual_address_map,
|
||||
.convert_pointer = (void *)&efi_invalid_parameter,
|
||||
.get_variable = (void *)&efi_device_error,
|
||||
.get_next_variable = (void *)&efi_device_error,
|
||||
.set_variable = (void *)&efi_device_error,
|
||||
.get_next_high_mono_count = (void *)&efi_device_error,
|
||||
.reset_system = &efi_reset_system,
|
||||
};
|
||||
Reference in New Issue
Block a user