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:
2026-03-03 21:46:32 +02:00
parent fe3ba02c96
commit 68d74d3181
11967 changed files with 2221897 additions and 0 deletions

View 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.

View 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

View 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,
};

View 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,
};

View 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;
}

View 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;
}

View 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 = &sections[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 = &sections[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;
}

View 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;
}

View 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;
}

View 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,
};