Patch queue for efi - 2018-04-09

Highlights this time around:
 
   - Lots of minor spec compliance fixes
   - Support full range of GOP BLT commands
   - More fine grained error checking
   - Network fixes (init, DP)
   - Lots of other bug fixes...
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABAgAGBQJay2U6AAoJECszeR4D/txgE8UP/A5xHq707fv4CcQez/cZj/KE
 Twv4HoSY28by71VWS+SjXXTKkx+XGM/2tLzl6dQCB9V1aFUwtB12zu4mXpplfT8R
 K+dJU9PgkPKJzWRvn08JgeuXs1RQnfSgXz5J2pvSnJ4mySMlZKuVRGwHxw7DLFPf
 EZOqkd2+NI90fogGFoZ2l/aAzbDssBI45jDmH0KcPO49/+7XRVYJqpg0s7TQYmhY
 BeoJ0mAp12Ybpb4E7XDIGKKgCTjOGnftoRmzZyPZPfHPrmVWo2srmtycDbz0f/N0
 tEiXIOAo72DzmhL6aPPNJtt1mjXTGU2iavkV74JVqXODCd3bRJxz7TF6R30Nmvfz
 WMaKM+69Ahior9YJnGv2GUe8ebThxivWUUCB6HmfRh07sGCes6eV3HfklY5Fnglu
 Gw5pRl9+YBOywuUuyMg/yBPpPyA7b1HaTjwhsYWAB+q7htB6JkPhQruMxdiMaQnC
 gvOEoguW1XZo4MSudfC/SUOdayGp+KsQiEivi8gUh11uBPA2nj1Bh028Pj7tTET3
 J4C527uF/ivKwhx2hUHq2YkkgxHWz0Z98LFG3Xkj3q3fZFRwSEzugik1TswT8z3l
 qPxWgwX7Q9dxNYLW4ogvnlXU/09APRg9fVeaXxOfqJTHCCn9PHehbKKpkJ9g3cOt
 lPr1Q/4C5daK8dnxDzYo
 =fzbe
 -----END PGP SIGNATURE-----

Merge tag 'signed-efi-next' of git://github.com/agraf/u-boot

Patch queue for efi - 2018-04-09

Highlights this time around:

  - Lots of minor spec compliance fixes
  - Support full range of GOP BLT commands
  - More fine grained error checking
  - Network fixes (init, DP)
  - Lots of other bug fixes...
This commit is contained in:
Tom Rini 2018-04-09 11:05:44 -04:00
commit 844fb498cc
35 changed files with 2582 additions and 580 deletions

View File

@ -330,7 +330,7 @@ EFI PAYLOAD
M: Alexander Graf <agraf@suse.de>
S: Maintained
T: git git://github.com/agraf/u-boot.git
F: doc/README.efi
F: doc/README.uefi
F: doc/README.iscsi
F: include/efi*
F: include/pe.h

View File

@ -644,6 +644,7 @@ void __efi_runtime EFIAPI efi_reset_system(
switch (reset_type) {
case EFI_RESET_COLD:
case EFI_RESET_WARM:
case EFI_RESET_PLATFORM_SPECIFIC:
reset_cpu(0);
break;
case EFI_RESET_SHUTDOWN:
@ -654,9 +655,9 @@ void __efi_runtime EFIAPI efi_reset_system(
while (1) { }
}
void efi_reset_system_init(void)
efi_status_t efi_reset_system_init(void)
{
efi_add_runtime_mmio(&rstcr, sizeof(*rstcr));
return efi_add_runtime_mmio(&rstcr, sizeof(*rstcr));
}
#endif

View File

@ -146,6 +146,7 @@ void __efi_runtime EFIAPI efi_reset_system(
switch (reset_type) {
case EFI_RESET_COLD:
case EFI_RESET_WARM:
case EFI_RESET_PLATFORM_SPECIFIC:
psci_system_reset();
break;
case EFI_RESET_SHUTDOWN:

View File

@ -20,6 +20,7 @@
*/
#include <common.h>
#include <efi_loader.h>
#include <asm/proc-armv/ptrace.h>
#include <asm/u-boot-arm.h>
#include <efi_loader.h>
@ -51,6 +52,11 @@ void bad_mode (void)
reset_cpu (0);
}
static void show_efi_loaded_images(struct pt_regs *regs)
{
efi_print_image_infos((void *)instruction_pointer(regs));
}
void show_regs (struct pt_regs *regs)
{
unsigned long __maybe_unused flags;
@ -106,6 +112,7 @@ void do_undefined_instruction (struct pt_regs *pt_regs)
printf ("undefined instruction\n");
fixup_pc(pt_regs, -4);
show_regs (pt_regs);
show_efi_loaded_images(pt_regs);
bad_mode ();
}
@ -115,6 +122,7 @@ void do_software_interrupt (struct pt_regs *pt_regs)
printf ("software interrupt\n");
fixup_pc(pt_regs, -4);
show_regs (pt_regs);
show_efi_loaded_images(pt_regs);
bad_mode ();
}
@ -124,6 +132,7 @@ void do_prefetch_abort (struct pt_regs *pt_regs)
printf ("prefetch abort\n");
fixup_pc(pt_regs, -8);
show_regs (pt_regs);
show_efi_loaded_images(pt_regs);
bad_mode ();
}
@ -133,6 +142,7 @@ void do_data_abort (struct pt_regs *pt_regs)
printf ("data abort\n");
fixup_pc(pt_regs, -8);
show_regs (pt_regs);
show_efi_loaded_images(pt_regs);
bad_mode ();
}
@ -142,6 +152,7 @@ void do_not_used (struct pt_regs *pt_regs)
printf ("not used\n");
fixup_pc(pt_regs, -8);
show_regs (pt_regs);
show_efi_loaded_images(pt_regs);
bad_mode ();
}
@ -151,6 +162,7 @@ void do_fiq (struct pt_regs *pt_regs)
printf ("fast interrupt request\n");
fixup_pc(pt_regs, -8);
show_regs (pt_regs);
show_efi_loaded_images(pt_regs);
bad_mode ();
}
@ -160,5 +172,6 @@ void do_irq (struct pt_regs *pt_regs)
printf ("interrupt request\n");
fixup_pc(pt_regs, -8);
show_regs (pt_regs);
show_efi_loaded_images(pt_regs);
bad_mode ();
}

View File

@ -63,6 +63,7 @@ void __efi_runtime EFIAPI efi_reset_system(
switch (reset_type) {
case EFI_RESET_COLD:
case EFI_RESET_WARM:
case EFI_RESET_PLATFORM_SPECIFIC:
reset_cpu(0);
break;
case EFI_RESET_SHUTDOWN:
@ -82,9 +83,9 @@ void __efi_runtime EFIAPI efi_reset_system(
while (1) { }
}
void efi_reset_system_init(void)
efi_status_t efi_reset_system_init(void)
{
efi_add_runtime_mmio(&wdog_regs, sizeof(*wdog_regs));
return efi_add_runtime_mmio(&wdog_regs, sizeof(*wdog_regs));
}
#endif

View File

@ -94,12 +94,16 @@ ifneq ($(CONFIG_EFI_STUB_64BIT),)
EFI_LDS := elf_x86_64_efi.lds
EFI_CRT0 := crt0_x86_64_efi.o
EFI_RELOC := reloc_x86_64_efi.o
EFI_TARGET := --target=efi-app-ia32
else
EFI_LDS := elf_ia32_efi.lds
EFI_CRT0 := crt0_ia32_efi.o
EFI_RELOC := reloc_ia32_efi.o
endif
ifdef CONFIG_X86_64
EFI_TARGET := --target=efi-app-x86_64
else
EFI_TARGET := --target=efi-app-ia32
endif
endif

View File

@ -22,37 +22,65 @@
DECLARE_GLOBAL_DATA_PTR;
static uint8_t efi_obj_list_initalized;
#define OBJ_LIST_NOT_INITIALIZED 1
static efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED;
static struct efi_device_path *bootefi_image_path;
static struct efi_device_path *bootefi_device_path;
/* Initialize and populate EFI object list */
static void efi_init_obj_list(void)
efi_status_t efi_init_obj_list(void)
{
efi_obj_list_initalized = 1;
efi_status_t ret = EFI_SUCCESS;
/* Initialize once only */
if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED)
return efi_obj_list_initialized;
/* Initialize EFI driver uclass */
efi_driver_init();
ret = efi_driver_init();
if (ret != EFI_SUCCESS)
goto out;
efi_console_register();
ret = efi_console_register();
if (ret != EFI_SUCCESS)
goto out;
#ifdef CONFIG_PARTITIONS
efi_disk_register();
ret = efi_disk_register();
if (ret != EFI_SUCCESS)
goto out;
#endif
#if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO)
efi_gop_register();
ret = efi_gop_register();
if (ret != EFI_SUCCESS)
goto out;
#endif
#ifdef CONFIG_CMD_NET
efi_net_register();
ret = efi_net_register();
if (ret != EFI_SUCCESS)
goto out;
#endif
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
efi_smbios_register();
ret = efi_smbios_register();
if (ret != EFI_SUCCESS)
goto out;
#endif
efi_watchdog_register();
ret = efi_watchdog_register();
if (ret != EFI_SUCCESS)
goto out;
/* Initialize EFI runtime services */
efi_reset_system_init();
efi_get_time_init();
ret = efi_reset_system_init();
if (ret != EFI_SUCCESS)
goto out;
ret = efi_get_time_init();
if (ret != EFI_SUCCESS)
goto out;
out:
efi_obj_list_initialized = ret;
return ret;
}
/*
@ -150,24 +178,85 @@ static efi_status_t efi_run_in_el2(EFIAPI efi_status_t (*entry)(
}
#endif
/* Carve out DT reserved memory ranges */
static efi_status_t efi_carve_out_dt_rsv(void *fdt)
{
int nr_rsv, i;
uint64_t addr, size, pages;
nr_rsv = fdt_num_mem_rsv(fdt);
/* Look for an existing entry and add it to the efi mem map. */
for (i = 0; i < nr_rsv; i++) {
if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0)
continue;
pages = ALIGN(size, EFI_PAGE_SIZE) >> EFI_PAGE_SHIFT;
efi_add_memory_map(addr, pages, EFI_RESERVED_MEMORY_TYPE,
false);
}
return EFI_SUCCESS;
}
static efi_status_t efi_install_fdt(void *fdt)
{
bootm_headers_t img = { 0 };
ulong fdt_pages, fdt_size, fdt_start, fdt_end;
efi_status_t ret;
if (fdt_check_header(fdt)) {
printf("ERROR: invalid device tree\n");
return EFI_INVALID_PARAMETER;
}
/* Prepare fdt for payload */
fdt = copy_fdt(fdt);
if (!fdt)
return EFI_OUT_OF_RESOURCES;
if (image_setup_libfdt(&img, fdt, 0, NULL)) {
printf("ERROR: failed to process device tree\n");
return EFI_LOAD_ERROR;
}
if (efi_carve_out_dt_rsv(fdt) != EFI_SUCCESS) {
printf("ERROR: failed to carve out memory\n");
return EFI_LOAD_ERROR;
}
/* Link to it in the efi tables */
ret = efi_install_configuration_table(&efi_guid_fdt, fdt);
if (ret != EFI_SUCCESS)
return EFI_OUT_OF_RESOURCES;
/* And reserve the space in the memory map */
fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK;
fdt_end = ((ulong)fdt) + fdt_totalsize(fdt);
fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK;
fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
/* Give a bootloader the chance to modify the device tree */
fdt_pages += 2;
ret = efi_add_memory_map(fdt_start, fdt_pages,
EFI_BOOT_SERVICES_DATA, true);
return ret;
}
/*
* Load an EFI payload into a newly allocated piece of memory, register all
* EFI objects it would want to access and jump to it.
*/
static efi_status_t do_bootefi_exec(void *efi, void *fdt,
static efi_status_t do_bootefi_exec(void *efi,
struct efi_device_path *device_path,
struct efi_device_path *image_path)
{
struct efi_loaded_image loaded_image_info = {};
struct efi_object loaded_image_info_obj = {};
struct efi_device_path *memdp = NULL;
ulong ret;
efi_status_t ret;
EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
struct efi_system_table *st);
ulong fdt_pages, fdt_size, fdt_start, fdt_end;
const efi_guid_t fdt_guid = EFI_FDT_GUID;
bootm_headers_t img = { 0 };
/*
* Special case for efi payload not loaded from disk, such as
@ -183,10 +272,6 @@ static efi_status_t do_bootefi_exec(void *efi, void *fdt,
assert(device_path && image_path);
}
/* Initialize and populate EFI object list */
if (!efi_obj_list_initalized)
efi_init_obj_list();
efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj,
device_path, image_path);
@ -196,38 +281,12 @@ static efi_status_t do_bootefi_exec(void *efi, void *fdt,
*/
efi_save_gd();
if (fdt && !fdt_check_header(fdt)) {
/* Prepare fdt for payload */
fdt = copy_fdt(fdt);
if (image_setup_libfdt(&img, fdt, 0, NULL)) {
printf("ERROR: Failed to process device tree\n");
return -EINVAL;
}
/* Link to it in the efi tables */
efi_install_configuration_table(&fdt_guid, fdt);
/* And reserve the space in the memory map */
fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK;
fdt_end = ((ulong)fdt) + fdt_totalsize(fdt);
fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK;
fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
/* Give a bootloader the chance to modify the device tree */
fdt_pages += 2;
efi_add_memory_map(fdt_start, fdt_pages,
EFI_BOOT_SERVICES_DATA, true);
} else {
printf("WARNING: Invalid device tree, expect boot to fail\n");
efi_install_configuration_table(&fdt_guid, NULL);
}
/* Transfer environment variable bootargs as load options */
set_load_options(&loaded_image_info, "bootargs");
/* Load the EFI payload */
entry = efi_load_pe(efi, &loaded_image_info);
if (!entry) {
ret = -ENOENT;
ret = EFI_LOAD_ERROR;
goto exit;
}
@ -277,16 +336,12 @@ exit:
return ret;
}
static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
static int do_bootefi_bootmgr_exec(void)
{
struct efi_device_path *device_path, *file_path;
void *addr;
efi_status_t r;
/* Initialize and populate EFI object list */
if (!efi_obj_list_initalized)
efi_init_obj_list();
/*
* gd lives in a fixed register which may get clobbered while we execute
* the payload. So save it here and restore it on every callback entry
@ -298,7 +353,7 @@ static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
return 1;
printf("## Starting EFI application at %p ...\n", addr);
r = do_bootefi_exec(addr, (void *)fdt_addr, device_path, file_path);
r = do_bootefi_exec(addr, device_path, file_path);
printf("## Application terminated, r = %lu\n",
r & ~EFI_ERROR_MASK);
@ -311,12 +366,37 @@ static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
/* Interpreter command to boot an arbitrary EFI image from memory */
static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
char *saddr, *sfdt;
unsigned long addr, fdt_addr = 0;
unsigned long addr;
char *saddr;
efi_status_t r;
void *fdt_addr;
/* Initialize EFI drivers */
r = efi_init_obj_list();
if (r != EFI_SUCCESS) {
printf("Error: Cannot set up EFI drivers, r = %lu\n",
r & ~EFI_ERROR_MASK);
return CMD_RET_FAILURE;
}
if (argc < 2)
return CMD_RET_USAGE;
if (argc > 2) {
fdt_addr = (void *)simple_strtoul(argv[2], NULL, 16);
if (!fdt_addr && *argv[2] != '0')
return CMD_RET_USAGE;
/* Install device tree */
r = efi_install_fdt(fdt_addr);
if (r != EFI_SUCCESS) {
printf("ERROR: failed to install device tree\n");
return CMD_RET_FAILURE;
}
} else {
/* Remove device tree. EFI_NOT_FOUND can be ignored here */
efi_install_configuration_table(&efi_guid_fdt, NULL);
printf("WARNING: booting without device tree\n");
}
#ifdef CONFIG_CMD_BOOTEFI_HELLO
if (!strcmp(argv[1], "hello")) {
ulong size = __efi_helloworld_end - __efi_helloworld_begin;
@ -350,8 +430,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
*/
efi_save_gd();
/* Initialize and populate EFI object list */
if (!efi_obj_list_initalized)
efi_init_obj_list();
efi_init_obj_list();
/* Transfer environment variable efi_selftest as load options */
set_load_options(&loaded_image_info, "efi_selftest");
/* Execute the test */
@ -363,12 +442,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
} else
#endif
if (!strcmp(argv[1], "bootmgr")) {
unsigned long fdt_addr = 0;
if (argc > 2)
fdt_addr = simple_strtoul(argv[2], NULL, 16);
return do_bootefi_bootmgr_exec(fdt_addr);
return do_bootefi_bootmgr_exec();
} else {
saddr = argv[1];
@ -377,15 +451,11 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
if (!addr && *saddr != '0')
return CMD_RET_USAGE;
if (argc > 2) {
sfdt = argv[2];
fdt_addr = simple_strtoul(sfdt, NULL, 16);
}
}
printf("## Starting EFI application at %08lx ...\n", addr);
r = do_bootefi_exec((void *)addr, (void *)fdt_addr,
bootefi_device_path, bootefi_image_path);
r = do_bootefi_exec((void *)addr, bootefi_device_path,
bootefi_image_path);
printf("## Application terminated, r = %lu\n",
r & ~EFI_ERROR_MASK);
@ -406,7 +476,7 @@ static char bootefi_help_text[] =
" - boot a sample Hello World application stored within U-Boot\n"
#endif
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
"bootefi selftest\n"
"bootefi selftest [fdt address]\n"
" - boot an EFI selftest application stored within U-Boot\n"
" Use environment variable efi_selftest to select a single test.\n"
" Use 'setenv efi_selftest list' to enumerate all tests.\n"
@ -424,16 +494,6 @@ U_BOOT_CMD(
bootefi_help_text
);
static int parse_partnum(const char *devnr)
{
const char *str = strchr(devnr, ':');
if (str) {
str++;
return simple_strtoul(str, NULL, 16);
}
return 0;
}
void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
{
char filename[32] = { 0 }; /* dp->str is u16[32] long */
@ -441,12 +501,13 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
if (strcmp(dev, "Net")) {
struct blk_desc *desc;
disk_partition_t fs_partition;
int part;
desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
if (!desc)
part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
1);
if (part < 0)
return;
part = parse_partnum(devnr);
bootefi_device_path = efi_dp_from_part(desc, part);
} else {

View File

@ -1,86 +0,0 @@
#
# Copyright (C) 2015 Google, Inc
#
# SPDX-License-Identifier: GPL-2.0+
#
EFI on U-Boot
=============
This document provides information about the implementation of the UEFI API [1]
in U-Boot.
=========== Table of Contents ===========
Motivation
How do I get it?
Status
Future work
Motivation
----------
With this API support in place, you can run any UEFI payload (such as the Linux
kernel, grub2 or gummiboot) on U-Boot. This dramatically simplifies boot loader
configuration, as U-Boot based systems now look and feel (almost) the same way
as TianoCore based systems.
How do I get it?
----------------
EFI support for 32bit ARM and AArch64 is already included in U-Boot. All you
need to do is enable
CONFIG_CMD_BOOTEFI=y
CONFIG_EFI_LOADER=y
in your .config file and you will automatically get a bootefi command to run
an efi application as well as snippet in the default distro boot script that
scans for removable media efi binaries as fallback.
Status
------
I am successfully able to run grub2 and Linux EFI binaries with this code on
ARMv7 as well as AArch64 systems.
When enabled, the resulting U-Boot binary only grows by ~10KB, so it's very
light weight.
All storage devices are directly accessible from the uEFI payload
Removable media booting (search for /efi/boot/boota{a64,arm}.efi) is supported.
Simple use cases like "Plug this SD card into my ARM device and it just
boots into grub which boots into Linux", work very well.
Running HelloWord.efi
---------------------
You can run a simple 'hello world' EFI program in U-Boot.
Enable the option CONFIG_CMD_BOOTEFI_HELLO.
Then you can boot into U-Boot and type:
> bootefi hello
The 'hello world EFI' program will then run, print a message and exit.
Future work
-----------
Of course, there are still a few things one could do on top:
- Improve disk media detection (don't scan, use what information we
have)
- Add EFI variable support using NVRAM
- Add GFX support
- Make EFI Shell work
- Network device support
- Support for payload exit
- Payload Watchdog support
[1] http://uefi.org/

332
doc/README.uefi Normal file
View File

@ -0,0 +1,332 @@
<!--
Copyright (c) 2018 Heinrich Schuchardt
SPDX-License-Identifier: GPL-2.0+
-->
# UEFI on U-Boot
The Unified Extensible Firmware Interface Specification (UEFI) [1] has become
the default for booting on AArch64 and x86 systems. It provides a stable API for
the interaction of drivers and applications with the firmware. The API comprises
access to block storage, network, and console to name a few. The Linux kernel
and boot loaders like GRUB or the FreeBSD loader can be executed.
## Building for UEFI
The UEFI standard supports only little endian systems. The UEFI support can be
activated for ARM and x86 by specifying
CONFIG_CMD_BOOTEFI=y
CONFIG_EFI_LOADER=y
in the .config file.
Support for attaching virtual block devices, e.g. iSCSI drives connected by the
loaded UEFI application [3], requires
CONFIG_BLK=y
CONFIG_PARTITIONS=y
### Executing a UEFI binary
The bootefi command is used to start UEFI applications or to install UEFI
drivers. It takes two parameters
bootefi <image address> [fdt address]
* image address - the memory address of the UEFI binary
* fdt address - the memory address of the flattened device tree
Below you find the output of an example session starting GRUB.
=> load mmc 0:2 ${fdt_addr_r} boot/dtb
29830 bytes read in 14 ms (2 MiB/s)
=> load mmc 0:1 ${kernel_addr_r} efi/debian/grubaa64.efi
reading efi/debian/grubaa64.efi
120832 bytes read in 7 ms (16.5 MiB/s)
=> bootefi ${kernel_addr_r} ${fdt_addr_r}
The environment variable 'bootargs' is passed as load options in the UEFI system
table. The Linux kernel EFI stub uses the load options as command line
arguments.
### Executing the boot manager
The UEFI specfication foresees to define boot entries and boot sequence via UEFI
variables. Booting according to these variables is possible via
bootefi bootmgr [fdt address]
As of U-Boot v2018.03 UEFI variables are not persisted and cannot be set at
runtime.
### Executing the built in hello world application
A hello world UEFI application can be built with
CONFIG_CMD_BOOTEFI_HELLO_COMPILE=y
It can be embedded into the U-Boot binary with
CONFIG_CMD_BOOTEFI_HELLO=y
The bootefi command is used to start the embedded hello world application.
bootefi hello [fdt address]
Below you find the output of an example session.
=> bootefi hello ${fdtcontroladdr}
## Starting EFI application at 01000000 ...
WARNING: using memory device/image path, this may confuse some payloads!
Hello, world!
Running on UEFI 2.7
Have SMBIOS table
Have device tree
Load options: root=/dev/sdb3 init=/sbin/init rootwait ro
## Application terminated, r = 0
The environment variable fdtcontroladdr points to U-Boot's internal device tree
(if available).
### Executing the built-in selftest
An UEFI selftest suite can be embedded in U-Boot by building with
CONFIG_CMD_BOOTEFI_SELFTEST=y
For testing the UEFI implementation the bootefi command can be used to start the
selftest.
bootefi selftest [fdt address]
The environment variable 'efi_selftest' can be used to select a single test. If
it is not provided all tests are executed except those marked as 'on request'.
If the environment variable is set to 'list' a list of all tests is shown.
Below you can find the output of an example session.
=> setenv efi_selftest simple network protocol
=> bootefi selftest
Testing EFI API implementation
Selected test: 'simple network protocol'
Setting up 'simple network protocol'
Setting up 'simple network protocol' succeeded
Executing 'simple network protocol'
DHCP Discover
DHCP reply received from 192.168.76.2 (52:55:c0:a8:4c:02)
as broadcast message.
Executing 'simple network protocol' succeeded
Tearing down 'simple network protocol'
Tearing down 'simple network protocol' succeeded
Boot services terminated
Summary: 0 failures
Preparing for reset. Press any key.
## The UEFI life cycle
After the U-Boot platform has been initialized the UEFI API provides two kinds
of services
* boot services and
* runtime services.
The API can be extended by loading UEFI drivers which come in two variants
* boot drivers and
* runtime drivers.
UEFI drivers are installed with U-Boot's bootefi command. With the same command
UEFI applications can be executed.
Loaded images of UEFI drivers stay in memory after returning to U-Boot while
loaded images of applications are removed from memory.
An UEFI application (e.g. an operating system) that wants to take full control
of the system calls ExitBootServices. After a UEFI application calls
ExitBootServices
* boot services are not available anymore
* timer events are stopped
* the memory used by U-Boot except for runtime services is released
* the memory used by boot time drivers is released
So this is a point of no return. Afterwards the UEFI application can only return
to U-Boot by rebooting.
## The UEFI object model
UEFI offers a flexible and expandable object model. The objects in the UEFI API
are devices, drivers, and loaded images. These objects are referenced by
handles.
The interfaces implemented by the objects are referred to as protocols. These
are identified by GUIDs. They can be installed and uninstalled by calling the
appropriate boot services.
Handles are created by the InstallProtocolInterface or the
InstallMultipleProtocolinterfaces service if NULL is passed as handle.
Handles are deleted when the last protocol has been removed with the
UninstallProtocolInterface or the UninstallMultipleProtocolInterfaces service.
Devices offer the EFI_DEVICE_PATH_PROTOCOL. A device path is the concatenation
of device nodes. By their device paths all devices of a system are arranged in a
tree.
Drivers offer the EFI_DRIVER_BINDING_PROTOCOL. This protocol is used to connect
a driver to devices (which are referenced as controllers in this context).
Loaded images offer the EFI_LOADED_IMAGE_PROTOCOL. This protocol provides meta
information about the image and a pointer to the unload callback function.
## The UEFI events
In the UEFI terminology an event is a data object referencing a notification
function which is queued for calling when the event is signaled. The following
types of events exist:
* periodic and single shot timer events
* exit boot services events, triggered by calling the ExitBootServices() service
* virtual address change events
* memory map change events
* read to boot events
* reset system events
* system table events
* events that are only triggered programmatically
Events can be created with the CreateEvent service and deleted with CloseEvent
service.
Events can be assigned to an event group. If any of the events in a group is
signaled, all other events in the group are also set to the signaled state.
## The UEFI driver model
A driver is specific for a single protocol installed on a device. To install a
driver on a device the ConnectController service is called. In this context
controller refers to the device for which the driver is installed.
The relevant drivers are identified using the EFI_DRIVER_BINDING_PROTOCOL. This
protocol has has three functions:
* supported - determines if the driver is compatible with the device
* start - installs the driver by opening the relevant protocol with
attribute EFI_OPEN_PROTOCOL_BY_DRIVER
* stop - uninstalls the driver
The driver may create child controllers (child devices). E.g. a driver for block
IO devices will create the device handles for the partitions. The child
controllers will open the supported protocol with the attribute
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
A driver can be detached from a device using the DisconnectController service.
## U-Boot devices mapped as UEFI devices
Some of the U-Boot devices are mapped as UEFI devices
* block IO devices
* console
* graphical output
* network adapter
As of U-Boot 2018.03 the logic for doing this is hard coded.
The development target is to integrate the setup of these UEFI devices with the
U-Boot driver model. So when a U-Boot device is discovered a handle should be
created and the device path protocol and the relevant IO protocol should be
installed. The UEFI driver then would be attached by calling ConnectController.
When a U-Boot device is removed DisconnectController should be called.
## UEFI devices mapped as U-Boot devices
UEFI drivers binaries and applications may create new (virtual) devices, install
a protocol and call the ConnectController service. Now the matching UEFI driver
is determined by iterating over the implementations of the
EFI_DRIVER_BINDING_PROTOCOL.
It is the task of the UEFI driver to create a corresponding U-Boot device and to
proxy calls for this U-Boot device to the controller.
In U-Boot 2018.03 this has only been implemented for block IO devices.
### UEFI uclass
An UEFI uclass driver (lib/efi_driver/efi_uclass.c) has been created that
takes care of initializing the UEFI drivers and providing the
EFI_DRIVER_BINDING_PROTOCOL implementation for the UEFI drivers.
A linker created list is used to keep track of the UEFI drivers. To create an
entry in the list the UEFI driver uses the U_BOOT_DRIVER macro specifying
UCLASS_EFI as the ID of its uclass, e.g.
/* Identify as UEFI driver */
U_BOOT_DRIVER(efi_block) = {
.name = "EFI block driver",
.id = UCLASS_EFI,
.ops = &driver_ops,
};
The available operations are defined via the structure struct efi_driver_ops.
struct efi_driver_ops {
const efi_guid_t *protocol;
const efi_guid_t *child_protocol;
int (*bind)(efi_handle_t handle, void *interface);
};
When the supported() function of the EFI_DRIVER_BINDING_PROTOCOL is called the
uclass checks if the protocol GUID matches the protocol GUID of the UEFI driver.
In the start() function the bind() function of the UEFI driver is called after
checking the GUID.
The stop() function of the EFI_DRIVER_BINDING_PROTOCOL disconnects the child
controllers created by the UEFI driver and the UEFI driver. (In U-Boot v2013.03
this is not yet completely implemented.)
### UEFI block IO driver
The UEFI block IO driver supports devices exposing the EFI_BLOCK_IO_PROTOCOL.
When connected it creates a new U-Boot block IO device with interface type
IF_TYPE_EFI, adds child controllers mapping the partitions, and installs the
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on these. This can be used together with the
software iPXE to boot from iSCSI network drives [3].
This driver is only available if U-Boot is configured with
CONFIG_BLK=y
CONFIG_PARTITIONS=y
## TODOs as of U-Boot 2018.03
* unimplemented or incompletely implemented boot services
* Exit - call unload function, unload applications only
* ReinstallProtocolInterface
* UnloadImage
* unimplemented events
* EVT_RUNTIME
* EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
* event groups
* data model
* manage events in a linked list
* manage configuration tables in a linked list
* UEFI drivers
* support DisconnectController for UEFI block devices.
* support for CONFIG_EFI_LOADER in the sandbox (CONFIG_SANDBOX=y)
* UEFI variables
* persistence
* runtime support
## Links
* [1](http://uefi.org/specifications)
http://uefi.org/specifications - UEFI specifications
* [2](./driver-model/README.txt) doc/driver-model/README.txt - Driver model
* [3](./README.iscsi) doc/README.iscsi - iSCSI booting with U-Boot and iPXE

View File

@ -15,6 +15,7 @@ alias abiessmann Andreas Bießmann <andreas@biessmann.org>
alias abrodkin Alexey Brodkin <alexey.brodkin@synopsys.com>
alias afleming Andy Fleming <afleming@gmail.com>
alias ag Anatolij Gustschin <agust@denx.de>
alias agraf Alexander Graf <agraf@suse.de>
alias alisonwang Alison Wang <alison.wang@freescale.com>
alias angelo_ts Angelo Dureghello <angelo@sysam.it>
alias bmeng Bin Meng <bmeng.cn@gmail.com>
@ -120,6 +121,7 @@ alias x86 uboot, sjg, bmeng
alias dm uboot, sjg
alias cfi uboot, stroese
alias dfu uboot, lukma
alias efi uboot, agraf
alias eth uboot, jhersh
alias kerneldoc uboot, marex
alias fdt uboot, sjg

View File

@ -107,7 +107,7 @@ struct efi_boot_services {
efi_status_t (EFIAPI *load_image)(bool boot_policiy,
efi_handle_t parent_image,
struct efi_device_path *file_path, void *source_buffer,
unsigned long source_size, efi_handle_t *image);
efi_uintn_t source_size, efi_handle_t *image);
efi_status_t (EFIAPI *start_image)(efi_handle_t handle,
unsigned long *exitdata_size,
s16 **exitdata);
@ -180,7 +180,8 @@ struct efi_boot_services {
enum efi_reset_type {
EFI_RESET_COLD = 0,
EFI_RESET_WARM = 1,
EFI_RESET_SHUTDOWN = 2
EFI_RESET_SHUTDOWN = 2,
EFI_RESET_PLATFORM_SPECIFIC = 3,
};
/* EFI Runtime Services table */
@ -243,6 +244,27 @@ struct efi_runtime_services {
u64 maximum_variable_size);
};
/* EFI event group GUID definitions */
#define EFI_EVENT_GROUP_EXIT_BOOT_SERVICES \
EFI_GUID(0x27abf055, 0xb1b8, 0x4c26, 0x80, 0x48, \
0x74, 0x8f, 0x37, 0xba, 0xa2, 0xdf)
#define EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE \
EFI_GUID(0x13fa7698, 0xc831, 0x49c7, 0x87, 0xea, \
0x8f, 0x43, 0xfc, 0xc2, 0x51, 0x96)
#define EFI_EVENT_GROUP_MEMORY_MAP_CHANGE \
EFI_GUID(0x78bee926, 0x692f, 0x48fd, 0x9e, 0xdb, \
0x01, 0x42, 0x2e, 0xf0, 0xd7, 0xab)
#define EFI_EVENT_GROUP_READY_TO_BOOT \
EFI_GUID(0x7ce88fb3, 0x4bd7, 0x4679, 0x87, 0xa8, \
0xa8, 0xd8, 0xde, 0xe5, 0x0d, 0x2b)
#define EFI_EVENT_GROUP_RESET_SYSTEM \
EFI_GUID(0x62da6a56, 0x13fb, 0x485a, 0xa8, 0xda, \
0xa3, 0xdd, 0x79, 0x12, 0xcb, 0x6b)
/* EFI Configuration Table and GUID definitions */
#define NULL_GUID \
EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
@ -296,8 +318,8 @@ struct efi_loaded_image {
u32 revision;
void *parent_handle;
struct efi_system_table *system_table;
void *device_handle;
void *file_path;
efi_handle_t device_handle;
struct efi_device_path *file_path;
void *reserved;
u32 load_options_size;
void *load_options;
@ -309,6 +331,8 @@ struct efi_loaded_image {
/* Below are efi loader private fields */
#ifdef CONFIG_EFI_LOADER
void *reloc_base;
aligned_u64 reloc_size;
efi_status_t exit_status;
struct jmp_buf_data exit_jmp;
#endif
@ -571,24 +595,6 @@ struct efi_simple_input_interface {
struct efi_event *wait_for_key;
};
#define CONSOLE_CONTROL_GUID \
EFI_GUID(0xf42f7782, 0x12e, 0x4c12, \
0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21)
#define EFI_CONSOLE_MODE_TEXT 0
#define EFI_CONSOLE_MODE_GFX 1
struct efi_console_control_protocol
{
efi_status_t (EFIAPI *get_mode)(
struct efi_console_control_protocol *this, int *mode,
char *uga_exists, char *std_in_locked);
efi_status_t (EFIAPI *set_mode)(
struct efi_console_control_protocol *this, int mode);
efi_status_t (EFIAPI *lock_std_in)(
struct efi_console_control_protocol *this,
uint16_t *password);
};
#define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID \
EFI_GUID(0x8b843e20, 0x8132, 0x4852, \
0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c)
@ -605,6 +611,35 @@ struct efi_device_path_to_text_protocol
bool allow_shortcuts);
};
#define EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID \
EFI_GUID(0x0379be4e, 0xd706, 0x437d, \
0xb0, 0x37, 0xed, 0xb8, 0x2f, 0xb7, 0x72, 0xa4)
struct efi_device_path_utilities_protocol {
efi_uintn_t (EFIAPI *get_device_path_size)(
const struct efi_device_path *device_path);
struct efi_device_path *(EFIAPI *duplicate_device_path)(
const struct efi_device_path *device_path);
struct efi_device_path *(EFIAPI *append_device_path)(
const struct efi_device_path *src1,
const struct efi_device_path *src2);
struct efi_device_path *(EFIAPI *append_device_node)(
const struct efi_device_path *device_path,
const struct efi_device_path *device_node);
struct efi_device_path *(EFIAPI *append_device_path_instance)(
const struct efi_device_path *device_path,
const struct efi_device_path *device_path_instance);
struct efi_device_path *(EFIAPI *get_next_device_path_instance)(
struct efi_device_path **device_path_instance,
efi_uintn_t *device_path_instance_size);
bool (EFIAPI *is_device_path_multi_instance)(
const struct efi_device_path *device_path);
struct efi_device_path *(EFIAPI *create_device_node)(
uint8_t node_type,
uint8_t node_sub_type,
uint16_t node_length);
};
#define EFI_GOP_GUID \
EFI_GUID(0x9042a9de, 0x23dc, 0x4a38, \
0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a)
@ -633,6 +668,13 @@ struct efi_gop_mode
unsigned long fb_size;
};
struct efi_gop_pixel {
u8 blue;
u8 green;
u8 red;
u8 reserved;
};
#define EFI_BLT_VIDEO_FILL 0
#define EFI_BLT_VIDEO_TO_BLT_BUFFER 1
#define EFI_BLT_BUFFER_TO_VIDEO 2
@ -644,7 +686,8 @@ struct efi_gop
efi_uintn_t *size_of_info,
struct efi_gop_mode_info **info);
efi_status_t (EFIAPI *set_mode)(struct efi_gop *this, u32 mode_number);
efi_status_t (EFIAPI *blt)(struct efi_gop *this, void *buffer,
efi_status_t (EFIAPI *blt)(struct efi_gop *this,
struct efi_gop_pixel *buffer,
u32 operation, efi_uintn_t sx,
efi_uintn_t sy, efi_uintn_t dx,
efi_uintn_t dy, efi_uintn_t width,
@ -662,7 +705,7 @@ struct efi_mac_address {
struct efi_ip_address {
u8 ip_addr[16];
};
} __attribute__((aligned(4)));
enum efi_simple_network_state {
EFI_NETWORK_STOPPED,
@ -756,7 +799,28 @@ struct efi_pxe_packet {
struct efi_pxe_mode
{
u8 unused[52];
u8 started;
u8 ipv6_available;
u8 ipv6_supported;
u8 using_ipv6;
u8 bis_supported;
u8 bis_detected;
u8 auto_arp;
u8 send_guid;
u8 dhcp_discover_valid;
u8 dhcp_ack_received;
u8 proxy_offer_received;
u8 pxe_discover_valid;
u8 pxe_reply_received;
u8 pxe_bis_reply_received;
u8 icmp_error_received;
u8 tftp_error_received;
u8 make_callbacks;
u8 ttl;
u8 tos;
u8 pad;
struct efi_ip_address station_ip;
struct efi_ip_address subnet_mask;
struct efi_pxe_packet dhcp_discover;
struct efi_pxe_packet dhcp_ack;
struct efi_pxe_packet proxy_offer;
@ -794,17 +858,19 @@ struct efi_file_handle {
efi_status_t (EFIAPI *close)(struct efi_file_handle *file);
efi_status_t (EFIAPI *delete)(struct efi_file_handle *file);
efi_status_t (EFIAPI *read)(struct efi_file_handle *file,
u64 *buffer_size, void *buffer);
efi_uintn_t *buffer_size, void *buffer);
efi_status_t (EFIAPI *write)(struct efi_file_handle *file,
u64 *buffer_size, void *buffer);
efi_uintn_t *buffer_size, void *buffer);
efi_status_t (EFIAPI *getpos)(struct efi_file_handle *file,
u64 *pos);
efi_uintn_t *pos);
efi_status_t (EFIAPI *setpos)(struct efi_file_handle *file,
u64 pos);
efi_uintn_t pos);
efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *file,
efi_guid_t *info_type, u64 *buffer_size, void *buffer);
const efi_guid_t *info_type, efi_uintn_t *buffer_size,
void *buffer);
efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *file,
efi_guid_t *info_type, u64 buffer_size, void *buffer);
const efi_guid_t *info_type, efi_uintn_t buffer_size,
void *buffer);
efi_status_t (EFIAPI *flush)(struct efi_file_handle *file);
};
@ -823,6 +889,10 @@ struct efi_simple_file_system_protocol {
EFI_GUID(0x9576e92, 0x6d3f, 0x11d2, \
0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_FILE_SYSTEM_INFO_GUID \
EFI_GUID(0x09576e93, 0x6d3f, 0x11d2, \
0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_FILE_MODE_READ 0x0000000000000001
#define EFI_FILE_MODE_WRITE 0x0000000000000002
#define EFI_FILE_MODE_CREATE 0x8000000000000000
@ -846,6 +916,15 @@ struct efi_file_info {
s16 file_name[0];
};
struct efi_file_system_info {
u64 size;
u8 read_only;
u64 volume_size;
u64 free_space;
u32 block_size;
u16 volume_label[0];
};
#define EFI_DRIVER_BINDING_PROTOCOL_GUID \
EFI_GUID(0x18a031ab, 0xb443, 0x4d1a,\
0xa5, 0xc0, 0x0c, 0x09, 0x26, 0x1e, 0x9f, 0x71)

View File

@ -83,6 +83,9 @@ extern struct efi_simple_text_output_protocol efi_con_out;
extern struct efi_simple_input_interface efi_con_in;
extern struct efi_console_control_protocol efi_console_control;
extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
/* implementation of the EFI_DEVICE_PATH_UTILITIES_PROTOCOL */
extern const struct efi_device_path_utilities_protocol
efi_device_path_utilities;
uint16_t *efi_dp_str(struct efi_device_path *dp);
@ -93,10 +96,25 @@ extern const efi_guid_t efi_guid_console_control;
extern const efi_guid_t efi_guid_device_path;
/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
extern const efi_guid_t efi_guid_driver_binding_protocol;
/* event group ExitBootServices() invoked */
extern const efi_guid_t efi_guid_event_group_exit_boot_services;
/* event group SetVirtualAddressMap() invoked */
extern const efi_guid_t efi_guid_event_group_virtual_address_change;
/* event group memory map changed */
extern const efi_guid_t efi_guid_event_group_memory_map_change;
/* event group boot manager about to boot */
extern const efi_guid_t efi_guid_event_group_ready_to_boot;
/* event group ResetSystem() invoked (before ExitBootServices) */
extern const efi_guid_t efi_guid_event_group_reset_system;
/* GUID of the device tree table */
extern const efi_guid_t efi_guid_fdt;
extern const efi_guid_t efi_guid_loaded_image;
extern const efi_guid_t efi_guid_device_path_to_text_protocol;
extern const efi_guid_t efi_simple_file_system_protocol_guid;
extern const efi_guid_t efi_file_info_guid;
/* GUID for file system information */
extern const efi_guid_t efi_file_system_info_guid;
extern const efi_guid_t efi_guid_device_path_utilities_protocol;
extern unsigned int __efi_runtime_start, __efi_runtime_stop;
extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@ -144,21 +162,25 @@ struct efi_object {
/**
* struct efi_event
*
* @link: Link to list of all events
* @type: Type of event, see efi_create_event
* @notify_tpl: Task priority level of notifications
* @trigger_time: Period of the timer
* @trigger_next: Next time to trigger the timer
* @nofify_function: Function to call when the event is triggered
* @notify_context: Data to be passed to the notify function
* @group: Event group
* @trigger_time: Period of the timer
* @trigger_next: Next time to trigger the timer
* @trigger_type: Type of timer, see efi_set_timer
* @queued: The notification function is queued
* @signaled: The event occurred. The event is in the signaled state.
* @is_queued: The notification function is queued
* @is_signaled: The event occurred. The event is in the signaled state.
*/
struct efi_event {
struct list_head link;
uint32_t type;
efi_uintn_t notify_tpl;
void (EFIAPI *notify_function)(struct efi_event *event, void *context);
void *notify_context;
const efi_guid_t *group;
u64 trigger_next;
u64 trigger_time;
enum efi_timer_delay trigger_type;
@ -166,9 +188,10 @@ struct efi_event {
bool is_signaled;
};
/* This list contains all UEFI objects we know of */
extern struct list_head efi_obj_list;
/* List of all events */
extern struct list_head efi_events;
/* Called by bootefi to make console interface available */
int efi_console_register(void);
@ -179,13 +202,13 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
const char *if_typename, int diskid,
const char *pdevname);
/* Called by bootefi to make GOP (graphical) interface available */
int efi_gop_register(void);
efi_status_t efi_gop_register(void);
/* Called by bootefi to make the network interface available */
int efi_net_register(void);
efi_status_t efi_net_register(void);
/* Called by bootefi to make the watchdog available */
int efi_watchdog_register(void);
efi_status_t efi_watchdog_register(void);
/* Called by bootefi to make SMBIOS tables available */
void efi_smbios_register(void);
efi_status_t efi_smbios_register(void);
struct efi_simple_file_system_protocol *
efi_fs_from_path(struct efi_device_path *fp);
@ -235,7 +258,8 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
void (EFIAPI *notify_function) (
struct efi_event *event,
void *context),
void *notify_context, struct efi_event **event);
void *notify_context, efi_guid_t *group,
struct efi_event **event);
/* Call this to set a timer */
efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
uint64_t trigger_time);
@ -284,6 +308,10 @@ efi_status_t efi_setup_loaded_image(
struct efi_device_path *file_path);
efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
void **buffer);
/* Print information about a loaded image */
efi_status_t efi_print_image_info(struct efi_loaded_image *image, void *pc);
/* Print information about all loaded images */
void efi_print_image_infos(void *pc);
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
extern void *efi_bounce_buffer;
@ -330,6 +358,7 @@ static inline void ascii2unicode(u16 *unicode, const char *ascii)
{
while (*ascii)
*(unicode++) = *(ascii++);
*unicode = 0;
}
static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
@ -346,7 +375,7 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
/* Call this with mmio_ptr as the _pointer_ to a pointer to an MMIO region
* to make it available at runtime */
void efi_add_runtime_mmio(void *mmio_ptr, u64 len);
efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len);
/* Boards may provide the functions below to implement RTS functionality */
@ -354,12 +383,14 @@ void __efi_runtime EFIAPI efi_reset_system(
enum efi_reset_type reset_type,
efi_status_t reset_status,
unsigned long data_size, void *reset_data);
void efi_reset_system_init(void);
/* Architecture specific initialization of the EFI subsystem */
efi_status_t efi_reset_system_init(void);
efi_status_t __efi_runtime EFIAPI efi_get_time(
struct efi_time *time,
struct efi_time_cap *capabilities);
void efi_get_time_init(void);
efi_status_t efi_get_time_init(void);
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
/*
@ -388,13 +419,17 @@ void *efi_bootmgr_load(struct efi_device_path **device_path,
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
#define __efi_runtime_data
#define __efi_runtime
static inline void efi_add_runtime_mmio(void *mmio_ptr, u64 len) { }
static inline efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
{
return EFI_SUCCESS;
}
/* No loader configured, stub out EFI_ENTRY */
static inline void efi_restore_gd(void) { }
static inline void efi_set_bootdev(const char *dev, const char *devnr,
const char *path) { }
static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
static inline void efi_print_image_infos(void *pc) { }
#endif /* CONFIG_EFI_LOADER && !CONFIG_SPL_BUILD */

View File

@ -38,11 +38,15 @@ typedef struct _IMAGE_DOS_HEADER {
#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */
#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */
#define IMAGE_FILE_MACHINE_I386 0x014c
#define IMAGE_FILE_MACHINE_ARM 0x01c0
#define IMAGE_FILE_MACHINE_THUMB 0x01c2
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4
#define IMAGE_FILE_MACHINE_AMD64 0x8664
#define IMAGE_FILE_MACHINE_ARM64 0xaa64
#define IMAGE_FILE_MACHINE_RISCV32 0x5032
#define IMAGE_FILE_MACHINE_RISCV64 0x5064
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10

View File

@ -17,7 +17,8 @@ endif
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o
obj-y += efi_file.o efi_variable.o efi_bootmgr.o efi_watchdog.o
obj-y += efi_device_path_utilities.o efi_file.o efi_variable.o efi_bootmgr.o
obj-y += efi_watchdog.o
obj-$(CONFIG_LCD) += efi_gop.o
obj-$(CONFIG_DM_VIDEO) += efi_gop.o
obj-$(CONFIG_PARTITIONS) += efi_disk.o

View File

@ -26,6 +26,9 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION;
/* This list contains all the EFI objects our payload has access to */
LIST_HEAD(efi_obj_list);
/* List of all events */
LIST_HEAD(efi_events);
/*
* If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
* we need to do trickery with caches. Since we don't want to break the EFI
@ -56,10 +59,28 @@ static volatile void *efi_gd, *app_gd;
static int entry_count;
static int nesting_level;
/* GUID of the device tree table */
const efi_guid_t efi_guid_fdt = EFI_FDT_GUID;
/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
const efi_guid_t efi_guid_driver_binding_protocol =
EFI_DRIVER_BINDING_PROTOCOL_GUID;
/* event group ExitBootServices() invoked */
const efi_guid_t efi_guid_event_group_exit_boot_services =
EFI_EVENT_GROUP_EXIT_BOOT_SERVICES;
/* event group SetVirtualAddressMap() invoked */
const efi_guid_t efi_guid_event_group_virtual_address_change =
EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE;
/* event group memory map changed */
const efi_guid_t efi_guid_event_group_memory_map_change =
EFI_EVENT_GROUP_MEMORY_MAP_CHANGE;
/* event group boot manager about to boot */
const efi_guid_t efi_guid_event_group_ready_to_boot =
EFI_EVENT_GROUP_READY_TO_BOOT;
/* event group ResetSystem() invoked (before ExitBootServices) */
const efi_guid_t efi_guid_event_group_reset_system =
EFI_EVENT_GROUP_RESET_SYSTEM;
static efi_status_t EFIAPI efi_disconnect_controller(
efi_handle_t controller_handle,
efi_handle_t driver_image_handle,
@ -121,6 +142,7 @@ static const char *indent_string(int level)
{
const char *indent = " ";
const int max = strlen(indent);
level = min(max, level * 2);
return &indent[max - level];
}
@ -154,7 +176,7 @@ const char *__efi_nesting_dec(void)
* @event event to signal
* @check_tpl check the TPL level
*/
void efi_signal_event(struct efi_event *event, bool check_tpl)
static void efi_queue_event(struct efi_event *event, bool check_tpl)
{
if (event->notify_function) {
event->is_queued = true;
@ -167,6 +189,50 @@ void efi_signal_event(struct efi_event *event, bool check_tpl)
event->is_queued = false;
}
/*
* Signal an EFI event.
*
* This function signals an event. If the event belongs to an event group
* all events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL
* their notification function is queued.
*
* For the SignalEvent service see efi_signal_event_ext.
*
* @event event to signal
* @check_tpl check the TPL level
*/
void efi_signal_event(struct efi_event *event, bool check_tpl)
{
if (event->group) {
struct efi_event *evt;
/*
* The signaled state has to set before executing any
* notification function
*/
list_for_each_entry(evt, &efi_events, link) {
if (!evt->group || guidcmp(evt->group, event->group))
continue;
if (evt->is_signaled)
continue;
evt->is_signaled = true;
if (evt->type & EVT_NOTIFY_SIGNAL &&
evt->notify_function)
evt->is_queued = true;
}
list_for_each_entry(evt, &efi_events, link) {
if (!evt->group || guidcmp(evt->group, event->group))
continue;
if (evt->is_queued)
efi_queue_event(evt, check_tpl);
}
} else if (!event->is_signaled) {
event->is_signaled = true;
if (event->type & EVT_NOTIFY_SIGNAL)
efi_queue_event(event, check_tpl);
}
}
/*
* Raise the task priority level.
*
@ -212,6 +278,11 @@ static void EFIAPI efi_restore_tpl(efi_uintn_t old_tpl)
if (efi_tpl > TPL_HIGH_LEVEL)
efi_tpl = TPL_HIGH_LEVEL;
/*
* Lowering the TPL may have made queued events eligible for execution.
*/
efi_timer_check();
EFI_EXIT(EFI_SUCCESS);
}
@ -255,7 +326,7 @@ static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory,
{
efi_status_t r;
EFI_ENTRY("%"PRIx64", 0x%zx", memory, pages);
EFI_ENTRY("%" PRIx64 ", 0x%zx", memory, pages);
r = efi_free_pages(memory, pages);
return EFI_EXIT(r);
}
@ -470,10 +541,23 @@ void efi_delete_handle(struct efi_object *obj)
}
/*
* Our event capabilities are very limited. Only a small limited
* number of events is allowed to coexist.
* Check if a pointer is a valid event.
*
* @event pointer to check
* @return status code
*/
static struct efi_event efi_events[16];
static efi_status_t efi_is_event(const struct efi_event *event)
{
const struct efi_event *evt;
if (!event)
return EFI_INVALID_PARAMETER;
list_for_each_entry(evt, &efi_events, link) {
if (evt == event)
return EFI_SUCCESS;
}
return EFI_INVALID_PARAMETER;
}
/*
* Create an event.
@ -494,9 +578,10 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
void (EFIAPI *notify_function) (
struct efi_event *event,
void *context),
void *notify_context, struct efi_event **event)
void *notify_context, efi_guid_t *group,
struct efi_event **event)
{
int i;
struct efi_event *evt;
if (event == NULL)
return EFI_INVALID_PARAMETER;
@ -504,25 +589,25 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
if ((type & EVT_NOTIFY_SIGNAL) && (type & EVT_NOTIFY_WAIT))
return EFI_INVALID_PARAMETER;
if ((type & (EVT_NOTIFY_SIGNAL|EVT_NOTIFY_WAIT)) &&
if ((type & (EVT_NOTIFY_SIGNAL | EVT_NOTIFY_WAIT)) &&
notify_function == NULL)
return EFI_INVALID_PARAMETER;
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (efi_events[i].type)
continue;
efi_events[i].type = type;
efi_events[i].notify_tpl = notify_tpl;
efi_events[i].notify_function = notify_function;
efi_events[i].notify_context = notify_context;
/* Disable timers on bootup */
efi_events[i].trigger_next = -1ULL;
efi_events[i].is_queued = false;
efi_events[i].is_signaled = false;
*event = &efi_events[i];
return EFI_SUCCESS;
}
return EFI_OUT_OF_RESOURCES;
evt = calloc(1, sizeof(struct efi_event));
if (!evt)
return EFI_OUT_OF_RESOURCES;
evt->type = type;
evt->notify_tpl = notify_tpl;
evt->notify_function = notify_function;
evt->notify_context = notify_context;
evt->group = group;
/* Disable timers on bootup */
evt->trigger_next = -1ULL;
evt->is_queued = false;
evt->is_signaled = false;
list_add_tail(&evt->link, &efi_events);
*event = evt;
return EFI_SUCCESS;
}
/*
@ -551,10 +636,8 @@ efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl,
{
EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function,
notify_context, event_group);
if (event_group)
return EFI_EXIT(EFI_UNSUPPORTED);
return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
notify_context, event));
notify_context, event_group, event));
}
/*
@ -581,10 +664,9 @@ static efi_status_t EFIAPI efi_create_event_ext(
EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function,
notify_context);
return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
notify_context, event));
notify_context, NULL, event));
}
/*
* Check if a timer event has occurred or a queued notification function should
* be called.
@ -594,30 +676,26 @@ static efi_status_t EFIAPI efi_create_event_ext(
*/
void efi_timer_check(void)
{
int i;
struct efi_event *evt;
u64 now = timer_get_us();
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (!efi_events[i].type)
list_for_each_entry(evt, &efi_events, link) {
if (evt->is_queued)
efi_queue_event(evt, true);
if (!(evt->type & EVT_TIMER) || now < evt->trigger_next)
continue;
if (efi_events[i].is_queued)
efi_signal_event(&efi_events[i], true);
if (!(efi_events[i].type & EVT_TIMER) ||
now < efi_events[i].trigger_next)
continue;
switch (efi_events[i].trigger_type) {
switch (evt->trigger_type) {
case EFI_TIMER_RELATIVE:
efi_events[i].trigger_type = EFI_TIMER_STOP;
evt->trigger_type = EFI_TIMER_STOP;
break;
case EFI_TIMER_PERIODIC:
efi_events[i].trigger_next +=
efi_events[i].trigger_time;
evt->trigger_next += evt->trigger_time;
break;
default:
continue;
}
efi_events[i].is_signaled = true;
efi_signal_event(&efi_events[i], true);
evt->is_signaled = false;
efi_signal_event(evt, true);
}
WATCHDOG_RESET();
}
@ -636,7 +714,9 @@ void efi_timer_check(void)
efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
uint64_t trigger_time)
{
int i;
/* Check that the event is valid */
if (efi_is_event(event) != EFI_SUCCESS || !(event->type & EVT_TIMER))
return EFI_INVALID_PARAMETER;
/*
* The parameter defines a multiple of 100ns.
@ -644,30 +724,21 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
*/
do_div(trigger_time, 10);
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (event != &efi_events[i])
continue;
if (!(event->type & EVT_TIMER))
break;
switch (type) {
case EFI_TIMER_STOP:
event->trigger_next = -1ULL;
break;
case EFI_TIMER_PERIODIC:
case EFI_TIMER_RELATIVE:
event->trigger_next =
timer_get_us() + trigger_time;
break;
default:
return EFI_INVALID_PARAMETER;
}
event->trigger_type = type;
event->trigger_time = trigger_time;
event->is_signaled = false;
return EFI_SUCCESS;
switch (type) {
case EFI_TIMER_STOP:
event->trigger_next = -1ULL;
break;
case EFI_TIMER_PERIODIC:
case EFI_TIMER_RELATIVE:
event->trigger_next = timer_get_us() + trigger_time;
break;
default:
return EFI_INVALID_PARAMETER;
}
return EFI_INVALID_PARAMETER;
event->trigger_type = type;
event->trigger_time = trigger_time;
event->is_signaled = false;
return EFI_SUCCESS;
}
/*
@ -686,7 +757,7 @@ static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event,
enum efi_timer_delay type,
uint64_t trigger_time)
{
EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time);
EFI_ENTRY("%p, %d, %" PRIx64, event, type, trigger_time);
return EFI_EXIT(efi_set_timer(event, type, trigger_time));
}
@ -706,7 +777,7 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events,
struct efi_event **event,
efi_uintn_t *index)
{
int i, j;
int i;
EFI_ENTRY("%zd, %p, %p", num_events, event, index);
@ -717,16 +788,12 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events,
if (efi_tpl != TPL_APPLICATION)
return EFI_EXIT(EFI_UNSUPPORTED);
for (i = 0; i < num_events; ++i) {
for (j = 0; j < ARRAY_SIZE(efi_events); ++j) {
if (event[i] == &efi_events[j])
goto known_event;
}
return EFI_EXIT(EFI_INVALID_PARAMETER);
known_event:
if (efi_is_event(event[i]) != EFI_SUCCESS)
return EFI_EXIT(EFI_INVALID_PARAMETER);
if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
return EFI_EXIT(EFI_INVALID_PARAMETER);
if (!event[i]->is_signaled)
efi_signal_event(event[i], true);
efi_queue_event(event[i], true);
}
/* Wait for signal */
@ -766,19 +833,10 @@ out:
*/
static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
{
int i;
EFI_ENTRY("%p", event);
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (event != &efi_events[i])
continue;
if (event->is_signaled)
break;
event->is_signaled = true;
if (event->type & EVT_NOTIFY_SIGNAL)
efi_signal_event(event, true);
break;
}
if (efi_is_event(event) != EFI_SUCCESS)
return EFI_EXIT(EFI_INVALID_PARAMETER);
efi_signal_event(event, true);
return EFI_EXIT(EFI_SUCCESS);
}
@ -794,19 +852,12 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
*/
static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
{
int i;
EFI_ENTRY("%p", event);
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (event == &efi_events[i]) {
event->type = 0;
event->trigger_next = -1ULL;
event->is_queued = false;
event->is_signaled = false;
return EFI_EXIT(EFI_SUCCESS);
}
}
return EFI_EXIT(EFI_INVALID_PARAMETER);
if (efi_is_event(event) != EFI_SUCCESS)
return EFI_EXIT(EFI_INVALID_PARAMETER);
list_del(&event->link);
free(event);
return EFI_EXIT(EFI_SUCCESS);
}
/*
@ -816,29 +867,26 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
* See the Unified Extensible Firmware Interface (UEFI) specification
* for details.
*
* If an event is not signaled yet the notification function is queued.
* If an event is not signaled yet, the notification function is queued.
* The signaled state is cleared.
*
* @event event to check
* @return status code
*/
static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
{
int i;
EFI_ENTRY("%p", event);
efi_timer_check();
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (event != &efi_events[i])
continue;
if (!event->type || event->type & EVT_NOTIFY_SIGNAL)
break;
if (!event->is_signaled)
efi_signal_event(event, true);
if (event->is_signaled)
return EFI_EXIT(EFI_SUCCESS);
return EFI_EXIT(EFI_NOT_READY);
if (efi_is_event(event) != EFI_SUCCESS ||
event->type & EVT_NOTIFY_SIGNAL)
return EFI_EXIT(EFI_INVALID_PARAMETER);
if (!event->is_signaled)
efi_queue_event(event, true);
if (event->is_signaled) {
event->is_signaled = false;
return EFI_EXIT(EFI_SUCCESS);
}
return EFI_EXIT(EFI_INVALID_PARAMETER);
return EFI_EXIT(EFI_NOT_READY);
}
/*
@ -1259,7 +1307,7 @@ static efi_status_t efi_locate_handle(
/* Count how much space we need */
list_for_each_entry(efiobj, &efi_obj_list, link) {
if (!efi_search(search_type, protocol, search_key, efiobj))
size += sizeof(void*);
size += sizeof(void *);
}
if (*buffer_size < size) {
@ -1310,7 +1358,7 @@ static efi_status_t EFIAPI efi_locate_handle_ext(
static void efi_remove_configuration_table(int i)
{
struct efi_configuration_table *this = &efi_conf_table[i];
struct efi_configuration_table *next = &efi_conf_table[i+1];
struct efi_configuration_table *next = &efi_conf_table[i + 1];
struct efi_configuration_table *end = &efi_conf_table[systab.nr_tables];
memmove(this, next, (ulong)end - (ulong)next);
@ -1327,10 +1375,15 @@ static void efi_remove_configuration_table(int i)
* @table table to be installed
* @return status code
*/
efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table)
efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
void *table)
{
struct efi_event *evt;
int i;
if (!guid)
return EFI_INVALID_PARAMETER;
/* Check for guid override */
for (i = 0; i < systab.nr_tables; i++) {
if (!guidcmp(guid, &efi_conf_table[i].guid)) {
@ -1338,7 +1391,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table
efi_conf_table[i].table = table;
else
efi_remove_configuration_table(i);
return EFI_SUCCESS;
goto out;
}
}
@ -1354,6 +1407,15 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table
efi_conf_table[i].table = table;
systab.nr_tables = i + 1;
out:
/* Notify that the configuration table was changed */
list_for_each_entry(evt, &efi_events, link) {
if (evt->group && !guidcmp(evt->group, guid)) {
efi_signal_event(evt, false);
break;
}
}
return EFI_SUCCESS;
}
@ -1420,14 +1482,15 @@ efi_status_t efi_setup_loaded_image(
if (ret != EFI_SUCCESS)
goto failure;
ret = efi_add_protocol(obj->handle, &efi_guid_console_control,
(void *)&efi_console_control);
ret = efi_add_protocol(obj->handle,
&efi_guid_device_path_to_text_protocol,
(void *)&efi_device_path_to_text);
if (ret != EFI_SUCCESS)
goto failure;
ret = efi_add_protocol(obj->handle,
&efi_guid_device_path_to_text_protocol,
(void *)&efi_device_path_to_text);
&efi_guid_device_path_utilities_protocol,
(void *)&efi_device_path_utilities);
if (ret != EFI_SUCCESS)
goto failure;
@ -1450,7 +1513,7 @@ efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
struct efi_file_info *info = NULL;
struct efi_file_handle *f;
static efi_status_t ret;
uint64_t bs;
efi_uintn_t bs;
f = efi_file_from_path(file_path);
if (!f)
@ -1471,7 +1534,8 @@ efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
if (ret)
goto error;
EFI_CALL(ret = f->read(f, &info->file_size, *buffer));
bs = info->file_size;
EFI_CALL(ret = f->read(f, &bs, *buffer));
error:
free(info);
@ -1505,18 +1569,37 @@ 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_uintn_t source_size,
efi_handle_t *image_handle)
{
struct efi_loaded_image *info;
struct efi_object *obj;
efi_status_t ret;
EFI_ENTRY("%d, %p, %pD, %p, %ld, %p", boot_policy, parent_image,
EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
file_path, source_buffer, source_size, image_handle);
if (!image_handle || !parent_image) {
ret = EFI_INVALID_PARAMETER;
goto error;
}
if (!source_buffer && !file_path) {
ret = EFI_NOT_FOUND;
goto error;
}
info = calloc(1, sizeof(*info));
if (!info) {
ret = EFI_OUT_OF_RESOURCES;
goto error;
}
obj = calloc(1, sizeof(*obj));
if (!obj) {
free(info);
ret = EFI_OUT_OF_RESOURCES;
goto error;
}
if (!source_buffer) {
struct efi_device_path *dp, *fp;
@ -1552,6 +1635,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
failure:
free(info);
efi_delete_handle(obj);
error:
return EFI_EXIT(ret);
}
@ -1635,8 +1719,9 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
* @return status code
*/
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)
efi_status_t exit_status,
unsigned long exit_data_size,
int16_t *exit_data)
{
/*
* We require that the handle points to the original loaded
@ -1649,7 +1734,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
* TODO: We should call the unload procedure of the loaded
* image protocol.
*/
struct efi_loaded_image *loaded_image_info = (void*)image_handle;
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);
@ -1724,7 +1809,7 @@ static void efi_exit_caches(void)
static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
unsigned long map_key)
{
int i;
struct efi_event *evt;
EFI_ENTRY("%p, %ld", image_handle, map_key);
@ -1735,12 +1820,19 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
if (!systab.boottime)
return EFI_EXIT(EFI_SUCCESS);
/* Add related events to the event group */
list_for_each_entry(evt, &efi_events, link) {
if (evt->type == EVT_SIGNAL_EXIT_BOOT_SERVICES)
evt->group = &efi_guid_event_group_exit_boot_services;
}
/* Notify that ExitBootServices is invoked. */
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
if (efi_events[i].type != EVT_SIGNAL_EXIT_BOOT_SERVICES)
continue;
efi_events[i].is_signaled = true;
efi_signal_event(&efi_events[i], false);
list_for_each_entry(evt, &efi_events, link) {
if (evt->group &&
!guidcmp(evt->group,
&efi_guid_event_group_exit_boot_services)) {
efi_signal_event(evt, false);
break;
}
}
/* TODO Should persist EFI variables here */
@ -1786,7 +1878,8 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
*/
static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
{
static uint64_t mono = 0;
static uint64_t mono;
EFI_ENTRY("%p", count);
*count = mono++;
return EFI_EXIT(EFI_SUCCESS);
@ -1827,7 +1920,7 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
unsigned long data_size,
uint16_t *watchdog_data)
{
EFI_ENTRY("%ld, 0x%"PRIx64", %ld, %p", timeout, watchdog_code,
EFI_ENTRY("%ld, 0x%" PRIx64 ", %ld, %p", timeout, watchdog_code,
data_size, watchdog_data);
return EFI_EXIT(efi_set_watchdog(timeout));
}
@ -1892,8 +1985,8 @@ out:
* @entry_count number of entries available in the buffer
* @return status code
*/
static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle,
const efi_guid_t *protocol,
static efi_status_t EFIAPI efi_open_protocol_information(
efi_handle_t handle, const efi_guid_t *protocol,
struct efi_open_protocol_info_entry **entry_buffer,
efi_uintn_t *entry_count)
{
@ -2878,15 +2971,16 @@ static const struct efi_boot_services efi_boot_services = {
.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,
.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,
.create_event_ex = efi_create_event_ex,
};
static uint16_t __efi_runtime_data firmware_vendor[] = L"Das U-Boot";
struct efi_system_table __efi_runtime_data systab = {
@ -2896,11 +2990,11 @@ struct efi_system_table __efi_runtime_data systab = {
.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,
.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,
.tables = (void *)efi_conf_table,
};

View File

@ -45,7 +45,6 @@ static struct cout_mode efi_cout_modes[] = {
},
};
const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID;
const efi_guid_t efi_guid_text_output_protocol =
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
const efi_guid_t efi_guid_text_input_protocol =
@ -54,43 +53,6 @@ const efi_guid_t efi_guid_text_input_protocol =
#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);
}
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,
};
/* Default to mode 0 */
static struct simple_text_output_mode efi_con_mode = {
.max_mode = 1,
@ -399,6 +361,48 @@ static efi_status_t EFIAPI efi_cin_reset(
return EFI_EXIT(EFI_UNSUPPORTED);
}
/*
* Analyze modifiers (shift, alt, ctrl) for function keys.
* This gets called when we have already parsed CSI.
*
* @modifiers: bitmask (shift, alt, ctrl)
* @return: the unmodified code
*/
static char skip_modifiers(int *modifiers)
{
char c, mod = 0, ret = 0;
c = getc();
if (c != ';') {
ret = c;
if (c == '~')
goto out;
c = getc();
}
for (;;) {
switch (c) {
case '0'...'9':
mod *= 10;
mod += c - '0';
/* fall through */
case ';':
c = getc();
break;
default:
goto out;
}
}
out:
if (mod)
--mod;
if (modifiers)
*modifiers = mod;
if (!ret)
ret = c;
return ret;
}
static efi_status_t EFIAPI efi_cin_read_key_stroke(
struct efi_simple_input_interface *this,
struct efi_input_key *key)
@ -421,14 +425,21 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
ch = getc();
if (ch == cESC) {
/* Escape Sequence */
/*
* Xterm Control Sequences
* https://www.xfree86.org/4.8.0/ctlseqs.html
*/
ch = getc();
switch (ch) {
case cESC: /* ESC */
pressed_key.scan_code = 23;
break;
case 'O': /* F1 - F4 */
pressed_key.scan_code = getc() - 'P' + 11;
ch = getc();
/* skip modifiers */
if (ch <= '9')
ch = getc();
pressed_key.scan_code = ch - 'P' + 11;
break;
case 'a'...'z':
ch = ch - 'a';
@ -445,17 +456,51 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
case 'H': /* Home */
pressed_key.scan_code = 5;
break;
case '1': /* F5 - F8 */
pressed_key.scan_code = getc() - '0' + 11;
getc();
case '1':
ch = skip_modifiers(NULL);
switch (ch) {
case '1'...'5': /* F1 - F5 */
pressed_key.scan_code = ch - '1' + 11;
break;
case '7'...'9': /* F6 - F8 */
pressed_key.scan_code = ch - '7' + 16;
break;
case 'A'...'D': /* up, down right, left */
pressed_key.scan_code = ch - 'A' + 1;
break;
case 'F':
pressed_key.scan_code = 6; /* End */
break;
case 'H':
pressed_key.scan_code = 5; /* Home */
break;
}
break;
case '2': /* F9 - F12 */
pressed_key.scan_code = getc() - '0' + 19;
getc();
case '2':
ch = skip_modifiers(NULL);
switch (ch) {
case '0'...'1': /* F9 - F10 */
pressed_key.scan_code = ch - '0' + 19;
break;
case '3'...'4': /* F11 - F12 */
pressed_key.scan_code = ch - '3' + 21;
break;
case '~': /* INS */
pressed_key.scan_code = 7;
break;
}
break;
case '3': /* DEL */
pressed_key.scan_code = 8;
getc();
skip_modifiers(NULL);
break;
case '5': /* PG UP */
pressed_key.scan_code = 9;
skip_modifiers(NULL);
break;
case '6': /* PG DOWN */
pressed_key.scan_code = 10;
skip_modifiers(NULL);
break;
}
break;
@ -464,7 +509,8 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
/* Backspace */
ch = 0x08;
}
pressed_key.unicode_char = ch;
if (!pressed_key.scan_code)
pressed_key.unicode_char = ch;
*key = pressed_key;
return EFI_EXIT(EFI_SUCCESS);
@ -506,18 +552,10 @@ static void EFIAPI efi_console_timer_notify(struct efi_event *event,
int efi_console_register(void)
{
efi_status_t r;
struct efi_object *efi_console_control_obj;
struct efi_object *efi_console_output_obj;
struct efi_object *efi_console_input_obj;
/* Create handles */
r = efi_create_handle((efi_handle_t *)&efi_console_control_obj);
if (r != EFI_SUCCESS)
goto out_of_memory;
r = efi_add_protocol(efi_console_control_obj->handle,
&efi_guid_console_control, &efi_console_control);
if (r != EFI_SUCCESS)
goto out_of_memory;
r = efi_create_handle((efi_handle_t *)&efi_console_output_obj);
if (r != EFI_SUCCESS)
goto out_of_memory;
@ -534,14 +572,14 @@ int efi_console_register(void)
goto out_of_memory;
/* Create console events */
r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
efi_key_notify, NULL, &efi_con_in.wait_for_key);
r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
NULL, NULL, &efi_con_in.wait_for_key);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register WaitForKey event\n");
return r;
}
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
efi_console_timer_notify, NULL,
efi_console_timer_notify, NULL, NULL,
&console_timer_event);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register console event\n");

View File

@ -66,6 +66,7 @@ static void *dp_alloc(size_t sz)
return NULL;
}
memset(buf, 0, sz);
return buf;
}
@ -749,7 +750,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
#ifdef CONFIG_CMD_NET
struct efi_device_path *efi_dp_from_eth(void)
{
#ifndef CONFIG_DM_ETH
struct efi_device_path_mac_addr *ndp;
#endif
void *buf, *start;
unsigned dpsize = 0;
@ -759,8 +762,8 @@ struct efi_device_path *efi_dp_from_eth(void)
dpsize += dp_size(eth_get_dev());
#else
dpsize += sizeof(ROOT);
#endif
dpsize += sizeof(*ndp);
#endif
start = buf = dp_alloc(dpsize + sizeof(END));
if (!buf)
@ -771,14 +774,15 @@ struct efi_device_path *efi_dp_from_eth(void)
#else
memcpy(buf, &ROOT, sizeof(ROOT));
buf += sizeof(ROOT);
#endif
ndp = buf;
ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
ndp->dp.length = sizeof(*ndp);
ndp->if_type = 1; /* Ethernet */
memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
buf = &ndp[1];
#endif
*((struct efi_device_path *)buf) = END;

View File

@ -0,0 +1,89 @@
/*
* EFI device path interface
*
* Copyright (c) 2017 Leif Lindholm
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <efi_loader.h>
const efi_guid_t efi_guid_device_path_utilities_protocol =
EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
static efi_uintn_t EFIAPI get_device_path_size(
const struct efi_device_path *device_path)
{
efi_uintn_t sz = 0;
EFI_ENTRY("%p", device_path);
/* size includes the END node: */
if (device_path)
sz = efi_dp_size(device_path) + sizeof(struct efi_device_path);
return EFI_EXIT(sz);
}
static struct efi_device_path * EFIAPI duplicate_device_path(
const struct efi_device_path *device_path)
{
EFI_ENTRY("%p", device_path);
return EFI_EXIT(efi_dp_dup(device_path));
}
static struct efi_device_path * EFIAPI append_device_path(
const struct efi_device_path *src1,
const struct efi_device_path *src2)
{
EFI_ENTRY("%p, %p", src1, src2);
return EFI_EXIT(efi_dp_append(src1, src2));
}
static struct efi_device_path * EFIAPI append_device_node(
const struct efi_device_path *device_path,
const struct efi_device_path *device_node)
{
EFI_ENTRY("%p, %p", device_path, device_node);
return EFI_EXIT(efi_dp_append_node(device_path, device_node));
}
static struct efi_device_path * EFIAPI append_device_path_instance(
const struct efi_device_path *device_path,
const struct efi_device_path *device_path_instance)
{
EFI_ENTRY("%p, %p", device_path, device_path_instance);
return EFI_EXIT(NULL);
}
static struct efi_device_path * EFIAPI get_next_device_path_instance(
struct efi_device_path **device_path_instance,
efi_uintn_t *device_path_instance_size)
{
EFI_ENTRY("%p, %p", device_path_instance, device_path_instance_size);
return EFI_EXIT(NULL);
}
static bool EFIAPI is_device_path_multi_instance(
const struct efi_device_path *device_path)
{
EFI_ENTRY("%p", device_path);
return EFI_EXIT(false);
}
static struct efi_device_path * EFIAPI create_device_node(
uint8_t node_type, uint8_t node_sub_type, uint16_t node_length)
{
EFI_ENTRY("%u, %u, %u", node_type, node_sub_type, node_length);
return EFI_EXIT(NULL);
}
const struct efi_device_path_utilities_protocol efi_device_path_utilities = {
.get_device_path_size = get_device_path_size,
.duplicate_device_path = duplicate_device_path,
.append_device_path = append_device_path,
.append_device_node = append_device_node,
.append_device_path_instance = append_device_path_instance,
.get_next_device_path_instance = get_next_device_path_instance,
.is_device_path_multi_instance = is_device_path_multi_instance,
.create_device_node = create_device_node,
};

View File

@ -12,6 +12,9 @@
#include <malloc.h>
#include <fs.h>
/* GUID for file system information */
const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
struct file_system {
struct efi_simple_file_system_protocol base;
struct efi_device_path *dp;
@ -314,29 +317,41 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
}
static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
u64 *buffer_size, void *buffer)
efi_uintn_t *buffer_size, void *buffer)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
u64 bs;
EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
if (!buffer_size || !buffer) {
ret = EFI_INVALID_PARAMETER;
goto error;
}
if (set_blk_dev(fh)) {
ret = EFI_DEVICE_ERROR;
goto error;
}
bs = *buffer_size;
if (fh->isdir)
ret = dir_read(fh, buffer_size, buffer);
ret = dir_read(fh, &bs, buffer);
else
ret = file_read(fh, buffer_size, buffer);
ret = file_read(fh, &bs, buffer);
if (bs <= SIZE_MAX)
*buffer_size = bs;
else
*buffer_size = SIZE_MAX;
error:
return EFI_EXIT(ret);
}
static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
u64 *buffer_size, void *buffer)
efi_uintn_t *buffer_size,
void *buffer)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
@ -363,21 +378,27 @@ error:
}
static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
u64 *pos)
efi_uintn_t *pos)
{
struct file_handle *fh = to_fh(file);
EFI_ENTRY("%p, %p", file, pos);
*pos = fh->offset;
return EFI_EXIT(EFI_SUCCESS);
if (fh->offset <= SIZE_MAX) {
*pos = fh->offset;
return EFI_EXIT(EFI_SUCCESS);
} else {
return EFI_EXIT(EFI_DEVICE_ERROR);
}
}
static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
u64 pos)
efi_uintn_t pos)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
EFI_ENTRY("%p, %llu", file, pos);
EFI_ENTRY("%p, %zu", file, pos);
if (fh->isdir) {
if (pos != 0) {
@ -411,7 +432,9 @@ error:
}
static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
efi_guid_t *info_type, u64 *buffer_size, void *buffer)
const efi_guid_t *info_type,
efi_uintn_t *buffer_size,
void *buffer)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
@ -452,6 +475,41 @@ static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
info->attribute |= EFI_FILE_DIRECTORY;
ascii2unicode((u16 *)info->file_name, filename);
} else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
struct efi_file_system_info *info = buffer;
disk_partition_t part;
efi_uintn_t required_size;
int r;
if (fh->fs->part >= 1)
r = part_get_info(fh->fs->desc, fh->fs->part, &part);
else
r = part_get_info_whole_disk(fh->fs->desc, &part);
if (r < 0) {
ret = EFI_DEVICE_ERROR;
goto error;
}
required_size = sizeof(info) + 2 *
(strlen((const char *)part.name) + 1);
if (*buffer_size < required_size) {
*buffer_size = required_size;
ret = EFI_BUFFER_TOO_SMALL;
goto error;
}
memset(info, 0, required_size);
info->size = required_size;
info->read_only = true;
info->volume_size = part.size * part.blksz;
info->free_space = 0;
info->block_size = part.blksz;
/*
* TODO: The volume label is not available in U-Boot.
* Use the partition name as substitute.
*/
ascii2unicode((u16 *)info->volume_label,
(const char *)part.name);
} else {
ret = EFI_UNSUPPORTED;
}
@ -461,9 +519,12 @@ error:
}
static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
efi_guid_t *info_type, u64 buffer_size, void *buffer)
const efi_guid_t *info_type,
efi_uintn_t buffer_size,
void *buffer)
{
EFI_ENTRY("%p, %p, %llu, %p", file, info_type, buffer_size, buffer);
EFI_ENTRY("%p, %p, %zu, %p", file, info_type, buffer_size, buffer);
return EFI_EXIT(EFI_UNSUPPORTED);
}

View File

@ -56,27 +56,166 @@ static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
return EFI_EXIT(EFI_SUCCESS);
}
efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
u32 operation, efi_uintn_t sx,
efi_uintn_t sy, efi_uintn_t dx,
efi_uintn_t dy, efi_uintn_t width,
efi_uintn_t height, efi_uintn_t delta)
static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid)
{
struct efi_gop_pixel blt = {
.reserved = 0,
};
blt.blue = (vid & 0x1f) << 3;
vid >>= 5;
blt.green = (vid & 0x3f) << 2;
vid >>= 6;
blt.red = (vid & 0x1f) << 3;
return blt;
}
static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt)
{
return (u16)(blt->red >> 3) << 11 |
(u16)(blt->green >> 2) << 5 |
(u16)(blt->blue >> 3);
}
static __always_inline efi_status_t gop_blt_int(struct efi_gop *this,
struct efi_gop_pixel *bufferp,
u32 operation, efi_uintn_t sx,
efi_uintn_t sy, efi_uintn_t dx,
efi_uintn_t dy,
efi_uintn_t width,
efi_uintn_t height,
efi_uintn_t delta,
efi_uintn_t vid_bpp)
{
struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
int i, j, line_len16, line_len32;
void *fb;
efi_uintn_t i, j, linelen, slineoff = 0, dlineoff, swidth, dwidth;
u32 *fb32 = gopobj->fb;
u16 *fb16 = gopobj->fb;
struct efi_gop_pixel *buffer = __builtin_assume_aligned(bufferp, 4);
EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
buffer, operation, sx, sy, dx, dy, width, height, delta);
if (delta) {
/* Check for 4 byte alignment */
if (delta & 3)
return EFI_INVALID_PARAMETER;
linelen = delta >> 2;
} else {
linelen = width;
}
if (operation != EFI_BLT_BUFFER_TO_VIDEO)
return EFI_EXIT(EFI_INVALID_PARAMETER);
/* Check source rectangle */
switch (operation) {
case EFI_BLT_VIDEO_FILL:
break;
case EFI_BLT_BUFFER_TO_VIDEO:
if (sx + width > linelen)
return EFI_INVALID_PARAMETER;
break;
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
case EFI_BLT_VIDEO_TO_VIDEO:
if (sx + width > gopobj->info.width ||
sy + height > gopobj->info.height)
return EFI_INVALID_PARAMETER;
break;
default:
return EFI_INVALID_PARAMETER;
}
fb = gopobj->fb;
line_len16 = gopobj->info.width * sizeof(u16);
line_len32 = gopobj->info.width * sizeof(u32);
/* Check destination rectangle */
switch (operation) {
case EFI_BLT_VIDEO_FILL:
case EFI_BLT_BUFFER_TO_VIDEO:
case EFI_BLT_VIDEO_TO_VIDEO:
if (dx + width > gopobj->info.width ||
dy + height > gopobj->info.height)
return EFI_INVALID_PARAMETER;
break;
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
if (dx + width > linelen)
return EFI_INVALID_PARAMETER;
break;
}
/* Copy the contents line by line */
/* Calculate line width */
switch (operation) {
case EFI_BLT_BUFFER_TO_VIDEO:
swidth = linelen;
break;
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
case EFI_BLT_VIDEO_TO_VIDEO:
swidth = gopobj->info.width;
if (!vid_bpp)
return EFI_UNSUPPORTED;
break;
case EFI_BLT_VIDEO_FILL:
swidth = 0;
break;
}
switch (operation) {
case EFI_BLT_BUFFER_TO_VIDEO:
case EFI_BLT_VIDEO_FILL:
case EFI_BLT_VIDEO_TO_VIDEO:
dwidth = gopobj->info.width;
if (!vid_bpp)
return EFI_UNSUPPORTED;
break;
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
dwidth = linelen;
break;
}
slineoff = swidth * sy;
dlineoff = dwidth * dy;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
struct efi_gop_pixel pix;
/* Read source pixel */
switch (operation) {
case EFI_BLT_VIDEO_FILL:
pix = *buffer;
break;
case EFI_BLT_BUFFER_TO_VIDEO:
pix = buffer[slineoff + j + sx];
break;
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
case EFI_BLT_VIDEO_TO_VIDEO:
if (vid_bpp == 32)
pix = *(struct efi_gop_pixel *)&fb32[
slineoff + j + sx];
else
pix = efi_vid16_to_blt_col(fb16[
slineoff + j + sx]);
break;
}
/* Write destination pixel */
switch (operation) {
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
buffer[dlineoff + j + dx] = pix;
break;
case EFI_BLT_BUFFER_TO_VIDEO:
case EFI_BLT_VIDEO_FILL:
case EFI_BLT_VIDEO_TO_VIDEO:
if (vid_bpp == 32)
fb32[dlineoff + j + dx] = *(u32 *)&pix;
else
fb16[dlineoff + j + dx] =
efi_blt_col_to_vid16(&pix);
break;
}
}
slineoff += swidth;
dlineoff += dwidth;
}
return EFI_SUCCESS;
}
static efi_uintn_t gop_get_bpp(struct efi_gop *this)
{
struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
efi_uintn_t vid_bpp = 0;
switch (gopobj->bpix) {
#ifdef CONFIG_DM_VIDEO
@ -84,38 +223,151 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
#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));
}
vid_bpp = 32;
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));
}
}
vid_bpp = 16;
break;
}
return vid_bpp;
}
/*
* Gcc can't optimize our BLT function well, but we need to make sure that
* our 2-dimensional loop gets executed very quickly, otherwise the system
* will feel slow.
*
* By manually putting all obvious branch targets into functions which call
* our generic blt function with constants, the compiler can successfully
* optimize for speed.
*/
static efi_status_t gop_blt_video_fill(struct efi_gop *this,
struct efi_gop_pixel *buffer,
u32 foo, efi_uintn_t sx,
efi_uintn_t sy, efi_uintn_t dx,
efi_uintn_t dy, efi_uintn_t width,
efi_uintn_t height, efi_uintn_t delta,
efi_uintn_t vid_bpp)
{
return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx,
dy, width, height, delta, vid_bpp);
}
static efi_status_t gop_blt_buf_to_vid16(struct efi_gop *this,
struct efi_gop_pixel *buffer,
u32 foo, efi_uintn_t sx,
efi_uintn_t sy, efi_uintn_t dx,
efi_uintn_t dy, efi_uintn_t width,
efi_uintn_t height, efi_uintn_t delta)
{
return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
dy, width, height, delta, 16);
}
static efi_status_t gop_blt_buf_to_vid32(struct efi_gop *this,
struct efi_gop_pixel *buffer,
u32 foo, efi_uintn_t sx,
efi_uintn_t sy, efi_uintn_t dx,
efi_uintn_t dy, efi_uintn_t width,
efi_uintn_t height, efi_uintn_t delta)
{
return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
dy, width, height, delta, 32);
}
static efi_status_t gop_blt_vid_to_vid(struct efi_gop *this,
struct efi_gop_pixel *buffer,
u32 foo, efi_uintn_t sx,
efi_uintn_t sy, efi_uintn_t dx,
efi_uintn_t dy, efi_uintn_t width,
efi_uintn_t height, efi_uintn_t delta,
efi_uintn_t vid_bpp)
{
return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx,
dy, width, height, delta, vid_bpp);
}
static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this,
struct efi_gop_pixel *buffer,
u32 foo, efi_uintn_t sx,
efi_uintn_t sy, efi_uintn_t dx,
efi_uintn_t dy, efi_uintn_t width,
efi_uintn_t height, efi_uintn_t delta,
efi_uintn_t vid_bpp)
{
return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy,
dx, dy, width, height, delta, vid_bpp);
}
/*
* Copy rectangle.
*
* This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
* See the Unified Extensible Firmware Interface (UEFI) specification for
* details.
*
* @this: EFI_GRAPHICS_OUTPUT_PROTOCOL
* @buffer: pixel buffer
* @sx: source x-coordinate
* @sy: source y-coordinate
* @dx: destination x-coordinate
* @dy: destination y-coordinate
* @width: width of rectangle
* @height: height of rectangle
* @delta: length in bytes of a line in the pixel buffer (optional)
* @return: status code
*/
efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer,
u32 operation, efi_uintn_t sx,
efi_uintn_t sy, efi_uintn_t dx,
efi_uintn_t dy, efi_uintn_t width,
efi_uintn_t height, efi_uintn_t delta)
{
efi_status_t ret = EFI_INVALID_PARAMETER;
efi_uintn_t vid_bpp;
EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
buffer, operation, sx, sy, dx, dy, width, height, delta);
vid_bpp = gop_get_bpp(this);
/* Allow for compiler optimization */
switch (operation) {
case EFI_BLT_VIDEO_FILL:
ret = gop_blt_video_fill(this, buffer, operation, sx, sy, dx,
dy, width, height, delta, vid_bpp);
break;
case EFI_BLT_BUFFER_TO_VIDEO:
/* This needs to be super-fast, so duplicate for 16/32bpp */
if (vid_bpp == 32)
ret = gop_blt_buf_to_vid32(this, buffer, operation, sx,
sy, dx, dy, width, height,
delta);
else
ret = gop_blt_buf_to_vid16(this, buffer, operation, sx,
sy, dx, dy, width, height,
delta);
break;
case EFI_BLT_VIDEO_TO_VIDEO:
ret = gop_blt_vid_to_vid(this, buffer, operation, sx, sy, dx,
dy, width, height, delta, vid_bpp);
break;
case EFI_BLT_VIDEO_TO_BLT_BUFFER:
ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx,
dy, width, height, delta, vid_bpp);
break;
default:
ret = EFI_UNSUPPORTED;
}
if (ret != EFI_SUCCESS)
return EFI_EXIT(ret);
#ifdef CONFIG_DM_VIDEO
video_sync_all();
#else
@ -125,8 +377,13 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
return EFI_EXIT(EFI_SUCCESS);
}
/* This gets called from do_bootefi_exec(). */
int efi_gop_register(void)
/*
* Install graphical output protocol.
*
* If no supported video device exists this is not considered as an
* error.
*/
efi_status_t efi_gop_register(void)
{
struct efi_gop_obj *gopobj;
u32 bpix, col, row;
@ -136,12 +393,15 @@ int efi_gop_register(void)
#ifdef CONFIG_DM_VIDEO
struct udevice *vdev;
struct video_priv *priv;
/* We only support a single video output device for now */
if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev)
return -1;
if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) {
debug("WARNING: No video device\n");
return EFI_SUCCESS;
}
struct video_priv *priv = dev_get_uclass_priv(vdev);
priv = dev_get_uclass_priv(vdev);
bpix = priv->bpix;
col = video_get_xsize(vdev);
row = video_get_ysize(vdev);
@ -170,13 +430,14 @@ int efi_gop_register(void)
break;
default:
/* So far, we only work in 16 or 32 bit mode */
return -1;
debug("WARNING: Unsupported video mode\n");
return EFI_SUCCESS;
}
gopobj = calloc(1, sizeof(*gopobj));
if (!gopobj) {
printf("ERROR: Out of memory\n");
return 1;
return EFI_OUT_OF_RESOURCES;
}
/* Hook up to the device list */
@ -186,8 +447,8 @@ int efi_gop_register(void)
ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid,
&gopobj->ops);
if (ret != EFI_SUCCESS) {
printf("ERROR: Out of memory\n");
return 1;
printf("ERROR: Failure adding gop protocol\n");
return ret;
}
gopobj->ops.query_mode = gop_query_mode;
gopobj->ops.set_mode = gop_set_mode;
@ -199,10 +460,11 @@ int efi_gop_register(void)
gopobj->mode.info_size = sizeof(gopobj->info);
#ifdef CONFIG_DM_VIDEO
if (bpix == VIDEO_BPP32) {
if (bpix == VIDEO_BPP32)
#else
if (bpix == LCD_COLOR32) {
if (bpix == LCD_COLOR32)
#endif
{
/* With 32bit color space we can directly expose the fb */
gopobj->mode.fb_base = fb_base;
gopobj->mode.fb_size = fb_size;
@ -217,5 +479,5 @@ int efi_gop_register(void)
gopobj->bpix = bpix;
gopobj->fb = fb;
return 0;
return EFI_SUCCESS;
}

View File

@ -22,6 +22,76 @@ const efi_guid_t efi_simple_file_system_protocol_guid =
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
static int machines[] = {
#if defined(CONFIG_ARM64)
IMAGE_FILE_MACHINE_ARM64,
#elif defined(CONFIG_ARM)
IMAGE_FILE_MACHINE_ARM,
IMAGE_FILE_MACHINE_THUMB,
IMAGE_FILE_MACHINE_ARMNT,
#endif
#if defined(CONFIG_X86_64)
IMAGE_FILE_MACHINE_AMD64,
#elif defined(CONFIG_X86)
IMAGE_FILE_MACHINE_I386,
#endif
#if defined(CONFIG_CPU_RISCV_32)
IMAGE_FILE_MACHINE_RISCV32,
#endif
#if defined(CONFIG_CPU_RISCV_64)
IMAGE_FILE_MACHINE_RISCV64,
#endif
0 };
/*
* Print information about a loaded image.
*
* If the program counter is located within the image the offset to the base
* address is shown.
*
* @image: loaded image
* @pc: program counter (use NULL to suppress offset output)
* @return: status code
*/
efi_status_t efi_print_image_info(struct efi_loaded_image *image, void *pc)
{
if (!image)
return EFI_INVALID_PARAMETER;
printf("UEFI image");
printf(" [0x%p:0x%p]",
image->reloc_base, image->reloc_base + image->reloc_size - 1);
if (pc && pc >= image->reloc_base &&
pc < image->reloc_base + image->reloc_size)
printf(" pc=0x%zx", pc - image->reloc_base);
if (image->file_path)
printf(" '%pD'", image->file_path);
printf("\n");
return EFI_SUCCESS;
}
/*
* Print information about all loaded images.
*
* @pc: program counter (use NULL to suppress offset output)
*/
void efi_print_image_infos(void *pc)
{
struct efi_object *efiobj;
struct efi_handler *handler;
list_for_each_entry(efiobj, &efi_obj_list, link) {
list_for_each_entry(handler, &efiobj->protocols, link) {
if (!guidcmp(handler->guid, &efi_guid_loaded_image)) {
efi_print_image_info(
handler->protocol_interface, pc);
}
}
}
}
static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
unsigned long rel_size, void *efi_reloc)
{
@ -126,14 +196,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
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
int supported = 0;
dos = efi;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
@ -147,6 +210,18 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
return NULL;
}
for (i = 0; machines[i]; i++)
if (machines[i] == nt->FileHeader.Machine) {
supported = 1;
break;
}
if (!supported) {
printf("%s: Machine type 0x%04x is not supported\n",
__func__, nt->FileHeader.Machine);
return NULL;
}
/* Calculate upper virtual address boundary */
num_sections = nt->FileHeader.NumberOfSections;
sections = (void *)&nt->OptionalHeader +
@ -159,8 +234,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
}
/* Read 32/64bit specific header bits */
if (can_run_nt64 &&
(nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
image_size = opt->SizeOfImage;
@ -175,8 +249,8 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
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)) {
virt_size = ALIGN(virt_size, opt->SectionAlignment);
} else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
image_size = opt->SizeOfImage;
efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
@ -190,6 +264,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
virt_size = ALIGN(virt_size, opt->SectionAlignment);
} else {
printf("%s: Invalid optional header magic %x\n", __func__,
nt->OptionalHeader.Magic);
@ -221,6 +296,8 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
/* Populate the loaded image interface bits */
loaded_image_info->image_base = efi;
loaded_image_info->image_size = image_size;
loaded_image_info->reloc_base = efi_reloc;
loaded_image_info->reloc_size = virt_size;
return entry;
}

View File

@ -8,12 +8,11 @@
#include <common.h>
#include <efi_loader.h>
#include <malloc.h>
#include <asm/global_data.h>
#include <linux/libfdt_env.h>
#include <linux/list_sort.h>
#include <inttypes.h>
#include <malloc.h>
#include <watchdog.h>
#include <asm/global_data.h>
#include <linux/list_sort.h>
DECLARE_GLOBAL_DATA_PTR;
@ -292,7 +291,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
uint64_t addr;
switch (type) {
case 0:
case EFI_ALLOCATE_ANY_PAGES:
/* Any page */
addr = efi_find_free_memory(len, gd->start_addr_sp);
if (!addr) {
@ -300,7 +299,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
break;
}
break;
case 1:
case EFI_ALLOCATE_MAX_ADDRESS:
/* Max address */
addr = efi_find_free_memory(len, *memory);
if (!addr) {
@ -308,7 +307,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
break;
}
break;
case 2:
case EFI_ALLOCATE_ADDRESS:
/* Exact address, reserve it. The addr is already in *memory. */
addr = *memory;
break;

View File

@ -54,14 +54,46 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
return EFI_EXIT(EFI_SUCCESS);
}
/*
* Initialize network adapter and allocate transmit and receive buffers.
*
* This function implements the Initialize service of the
* EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
* (UEFI) specification for details.
*
* @this: pointer to the protocol instance
* @extra_rx: extra receive buffer to be allocated
* @extra_tx: extra transmit buffer to be allocated
* @return: status code
*/
static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
ulong extra_rx, ulong extra_tx)
{
int ret;
efi_status_t r = EFI_SUCCESS;
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
eth_init();
if (!this) {
r = EFI_INVALID_PARAMETER;
goto error;
}
return EFI_EXIT(EFI_SUCCESS);
/* Setup packet buffers */
net_init();
/* Disable hardware and put it into the reset state */
eth_halt();
/* Set current device according to environment variables */
eth_set_current();
/* Get hardware ready for send and receive operations */
ret = eth_init();
if (ret < 0) {
eth_halt();
r = EFI_DEVICE_ERROR;
}
error:
return EFI_EXIT(r);
}
static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
@ -280,20 +312,22 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event,
}
/* This gets called from do_bootefi_exec(). */
int efi_net_register(void)
efi_status_t efi_net_register(void)
{
struct efi_net_obj *netobj;
efi_status_t r;
if (!eth_get_dev()) {
/* No eth device active, don't expose any */
return 0;
return EFI_SUCCESS;
}
/* We only expose the "active" eth device, so one is enough */
netobj = calloc(1, sizeof(*netobj));
if (!netobj)
goto out_of_memory;
if (!netobj) {
printf("ERROR: Out of memory\n");
return EFI_OUT_OF_RESOURCES;
}
/* Hook net up to the device list */
efi_add_handle(&netobj->parent);
@ -302,15 +336,15 @@ int efi_net_register(void)
r = efi_add_protocol(netobj->parent.handle, &efi_net_guid,
&netobj->net);
if (r != EFI_SUCCESS)
goto out_of_memory;
goto failure_to_add_protocol;
r = efi_add_protocol(netobj->parent.handle, &efi_guid_device_path,
efi_dp_from_eth());
if (r != EFI_SUCCESS)
goto out_of_memory;
goto failure_to_add_protocol;
r = efi_add_protocol(netobj->parent.handle, &efi_pxe_guid,
&netobj->pxe);
if (r != EFI_SUCCESS)
goto out_of_memory;
goto failure_to_add_protocol;
netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
netobj->net.start = efi_net_start;
netobj->net.stop = efi_net_stop;
@ -339,7 +373,7 @@ int efi_net_register(void)
* Create WaitForPacket event.
*/
r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
efi_network_timer_notify, NULL,
efi_network_timer_notify, NULL, NULL,
&wait_for_packet);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register network event\n");
@ -351,9 +385,11 @@ int efi_net_register(void)
*
* The notification function is used to check if a new network packet
* has been received.
*
* iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL.
*/
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
efi_network_timer_notify, NULL,
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
efi_network_timer_notify, NULL, NULL,
&network_timer_event);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register network event\n");
@ -366,8 +402,8 @@ int efi_net_register(void)
return r;
}
return 0;
out_of_memory:
printf("ERROR: Out of memory\n");
return 1;
return EFI_SUCCESS;
failure_to_add_protocol:
printf("ERROR: Failure to add protocol\n");
return r;
}

View File

@ -74,12 +74,24 @@ static void EFIAPI efi_reset_system_boottime(
efi_status_t reset_status,
unsigned long data_size, void *reset_data)
{
struct efi_event *evt;
EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
reset_data);
/* Notify reset */
list_for_each_entry(evt, &efi_events, link) {
if (evt->group &&
!guidcmp(evt->group,
&efi_guid_event_group_reset_system)) {
efi_signal_event(evt, false);
break;
}
}
switch (reset_type) {
case EFI_RESET_COLD:
case EFI_RESET_WARM:
case EFI_RESET_PLATFORM_SPECIFIC:
do_reset(NULL, 0, 0, NULL);
break;
case EFI_RESET_SHUTDOWN:
@ -134,8 +146,9 @@ void __weak __efi_runtime EFIAPI efi_reset_system(
while (1) { }
}
void __weak efi_reset_system_init(void)
efi_status_t __weak efi_reset_system_init(void)
{
return EFI_SUCCESS;
}
efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
@ -146,8 +159,9 @@ efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
return EFI_DEVICE_ERROR;
}
void __weak efi_get_time_init(void)
efi_status_t __weak efi_get_time_init(void)
{
return EFI_SUCCESS;
}
struct efi_runtime_detach_list_struct {
@ -332,18 +346,26 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
return EFI_EXIT(EFI_INVALID_PARAMETER);
}
void efi_add_runtime_mmio(void *mmio_ptr, u64 len)
efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
{
struct efi_runtime_mmio_list *newmmio;
u64 pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
efi_add_memory_map(*(uintptr_t *)mmio_ptr, pages, EFI_MMAP_IO, false);
uint64_t addr = *(uintptr_t *)mmio_ptr;
uint64_t retaddr;
retaddr = efi_add_memory_map(addr, pages, EFI_MMAP_IO, false);
if (retaddr != addr)
return EFI_OUT_OF_RESOURCES;
newmmio = calloc(1, sizeof(*newmmio));
if (!newmmio)
return EFI_OUT_OF_RESOURCES;
newmmio->ptr = mmio_ptr;
newmmio->paddr = *(uintptr_t *)mmio_ptr;
newmmio->len = len;
list_add_tail(&newmmio->link, &efi_runtime_mmio);
return EFI_SUCCESS;
}
/*

View File

@ -13,20 +13,27 @@
static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
void efi_smbios_register(void)
/*
* Install the SMBIOS table as a configuration table.
*
* @return status code
*/
efi_status_t efi_smbios_register(void)
{
/* Map within the low 32 bits, to allow for 32bit SMBIOS tables */
uint64_t dmi = 0xffffffff;
/* Reserve 4kb for SMBIOS */
uint64_t pages = 1;
int memtype = EFI_RUNTIME_SERVICES_DATA;
u64 dmi = U32_MAX;
efi_status_t ret;
if (efi_allocate_pages(1, memtype, pages, &dmi) != EFI_SUCCESS)
return;
/* Reserve 4kiB page for SMBIOS */
ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
EFI_RUNTIME_SERVICES_DATA, 1, &dmi);
if (ret != EFI_SUCCESS)
return ret;
/* Generate SMBIOS tables */
write_smbios_table(dmi);
/* And expose them to our EFI payload */
efi_install_configuration_table(&smbios_guid, (void*)(uintptr_t)dmi);
return efi_install_configuration_table(&smbios_guid,
(void *)(uintptr_t)dmi);
}

View File

@ -59,7 +59,7 @@ efi_status_t efi_set_watchdog(unsigned long timeout)
*
* This function is called by efi_init_obj_list()
*/
int efi_watchdog_register(void)
efi_status_t efi_watchdog_register(void)
{
efi_status_t r;
@ -67,7 +67,7 @@ int efi_watchdog_register(void)
* Create a timer event.
*/
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
efi_watchdog_timer_notify, NULL,
efi_watchdog_timer_notify, NULL, NULL,
&watchdog_timer_event);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register watchdog event\n");
@ -85,5 +85,5 @@ int efi_watchdog_register(void)
printf("ERROR: Failed to set watchdog timer\n");
return r;
}
return 0;
return EFI_SUCCESS;
}

View File

@ -46,9 +46,27 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,
struct efi_loaded_image *loaded_image;
efi_status_t ret;
efi_uintn_t i;
u16 rev[] = L"0.0.0";
con_out->output_string(con_out, L"Hello, world!\n");
/* Print the revision number */
rev[0] = (systable->hdr.revision >> 16) + '0';
rev[4] = systable->hdr.revision & 0xffff;
for (; rev[4] >= 10;) {
rev[4] -= 10;
++rev[2];
}
/* Third digit is only to be shown if non-zero */
if (rev[4])
rev[4] += '0';
else
rev[3] = 0;
con_out->output_string(con_out, L"Running on UEFI ");
con_out->output_string(con_out, rev);
con_out->output_string(con_out, L"\n");
/* Get the loaded image protocol */
ret = boottime->handle_protocol(handle, &loaded_image_guid,
(void **)&loaded_image);

View File

@ -14,14 +14,18 @@ CFLAGS_REMOVE_efi_selftest_miniapp_return.o := $(CFLAGS_NON_EFI) -Os
obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
efi_selftest.o \
efi_selftest_bitblt.o \
efi_selftest_controllers.o \
efi_selftest_console.o \
efi_selftest_devicepath.o \
efi_selftest_events.o \
efi_selftest_event_groups.o \
efi_selftest_exitbootservices.o \
efi_selftest_fdt.o \
efi_selftest_gop.o \
efi_selftest_manageprotocols.o \
efi_selftest_snp.o \
efi_selftest_textinput.o \
efi_selftest_textoutput.o \
efi_selftest_tpl.o \
efi_selftest_util.o \

View File

@ -0,0 +1,311 @@
/*
* efi_selftest_bitblt
*
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* Test the block image transfer in the graphical output protocol.
* An animated submarine is shown.
*/
#include <efi_selftest.h>
#define WIDTH 200
#define HEIGHT 120
#define DEPTH 60
static const struct efi_gop_pixel BLACK = { 0, 0, 0, 0};
static const struct efi_gop_pixel RED = { 0, 0, 255, 0};
static const struct efi_gop_pixel ORANGE = { 0, 128, 255, 0};
static const struct efi_gop_pixel YELLOW = { 0, 255, 255, 0};
static const struct efi_gop_pixel GREEN = { 0, 255, 0, 0};
static const struct efi_gop_pixel DARK_BLUE = {128, 0, 0, 0};
static const struct efi_gop_pixel LIGHT_BLUE = {255, 192, 192, 0};
static struct efi_boot_services *boottime;
static efi_guid_t efi_gop_guid = EFI_GOP_GUID;
static struct efi_gop *gop;
static struct efi_gop_pixel *bitmap;
static struct efi_event *event;
static efi_uintn_t xpos;
static void ellipse(efi_uintn_t x, efi_uintn_t y,
efi_uintn_t x0, efi_uintn_t y0,
efi_uintn_t x1, efi_uintn_t y1,
const struct efi_gop_pixel col, struct efi_gop_pixel *pix)
{
efi_uintn_t xm = x0 + x1;
efi_uintn_t ym = y0 + y1;
efi_uintn_t dx = x1 - x0 + 1;
efi_uintn_t dy = y1 - y0 + 1;
if (dy * dy * (2 * x - xm) * (2 * x - xm) +
dx * dx * (2 * y - ym) * (2 * y - ym) <= dx * dx * dy * dy)
*pix = col;
}
static void rectangle(efi_uintn_t x, efi_uintn_t y,
efi_uintn_t x0, efi_uintn_t y0,
efi_uintn_t x1, efi_uintn_t y1,
const struct efi_gop_pixel col, struct efi_gop_pixel *pix)
{
if (x >= x0 && y >= y0 && x <= x1 && y <= y1)
*pix = col;
}
/*
* Notification function, copies image to video.
* The position is incremented in each call.
*
* @event notified event
* @context pointer to the notification count
*/
static void EFIAPI notify(struct efi_event *event, void *context)
{
efi_uintn_t *pos = context;
efi_uintn_t dx, sx, width;
if (!pos)
return;
/* Increment position */
*pos += 5;
if (*pos >= WIDTH + gop->mode->info->width)
*pos = 0;
width = WIDTH;
dx = *pos - WIDTH;
sx = 0;
if (*pos >= gop->mode->info->width) {
width = WIDTH + gop->mode->info->width - *pos;
} else if (*pos < WIDTH) {
dx = 0;
sx = WIDTH - *pos;
width = *pos;
}
/* Copy image to video */
gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, sx, 0, dx, DEPTH,
width, HEIGHT, WIDTH * sizeof(struct efi_gop_pixel));
}
/*
* Setup unit test.
*
* @handle: handle of the loaded image
* @systable: system table
* @return: EFI_ST_SUCCESS for success
*/
static int setup(const efi_handle_t handle,
const struct efi_system_table *systable)
{
efi_status_t ret;
struct efi_gop_pixel pix;
efi_uintn_t x, y;
boottime = systable->boottime;
/* Create event */
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK, notify, (void *)&xpos,
&event);
if (ret != EFI_SUCCESS) {
efi_st_error("could not create event\n");
return EFI_ST_FAILURE;
}
/* Get graphical output protocol */
ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
if (ret != EFI_SUCCESS) {
gop = NULL;
efi_st_printf("Graphical output protocol is not available.\n");
return EFI_ST_SUCCESS;
}
/* Prepare image of submarine */
ret = boottime->allocate_pool(EFI_LOADER_DATA,
sizeof(struct efi_gop_pixel) *
WIDTH * HEIGHT, (void **)&bitmap);
if (ret != EFI_SUCCESS) {
efi_st_error("Out of memory\n");
return EFI_ST_FAILURE;
}
for (y = 0; y < HEIGHT; ++y) {
for (x = 0; x < WIDTH; ++x) {
pix = DARK_BLUE;
/* Propeller */
ellipse(x, y, 35, 55, 43, 75, BLACK, &pix);
ellipse(x, y, 36, 56, 42, 74, LIGHT_BLUE, &pix);
ellipse(x, y, 35, 75, 43, 95, BLACK, &pix);
ellipse(x, y, 36, 76, 42, 94, LIGHT_BLUE, &pix);
/* Shaft */
rectangle(x, y, 35, 73, 100, 77, BLACK, &pix);
/* Periscope */
ellipse(x, y, 120, 10, 160, 50, BLACK, &pix);
ellipse(x, y, 121, 11, 159, 59, YELLOW, &pix);
ellipse(x, y, 130, 20, 150, 40, BLACK, &pix);
ellipse(x, y, 131, 21, 149, 49, DARK_BLUE, &pix);
rectangle(x, y, 135, 10, 160, 50, DARK_BLUE, &pix);
ellipse(x, y, 132, 10, 138, 20, BLACK, &pix);
ellipse(x, y, 133, 11, 139, 19, RED, &pix);
/* Rudder */
ellipse(x, y, 45, 40, 75, 70, BLACK, &pix);
ellipse(x, y, 46, 41, 74, 69, ORANGE, &pix);
ellipse(x, y, 45, 80, 75, 109, BLACK, &pix);
ellipse(x, y, 46, 81, 74, 108, RED, &pix);
/* Bridge */
ellipse(x, y, 100, 30, 120, 50, BLACK, &pix);
ellipse(x, y, 101, 31, 119, 49, GREEN, &pix);
ellipse(x, y, 140, 30, 160, 50, BLACK, &pix);
ellipse(x, y, 141, 31, 159, 49, GREEN, &pix);
rectangle(x, y, 110, 30, 150, 50, BLACK, &pix);
rectangle(x, y, 110, 31, 150, 50, GREEN, &pix);
/* Hull */
ellipse(x, y, 50, 40, 199, 109, BLACK, &pix);
ellipse(x, y, 51, 41, 198, 108, LIGHT_BLUE, &pix);
/* Port holes */
ellipse(x, y, 79, 57, 109, 82, BLACK, &pix);
ellipse(x, y, 80, 58, 108, 81, LIGHT_BLUE, &pix);
ellipse(x, y, 83, 61, 105, 78, BLACK, &pix);
ellipse(x, y, 84, 62, 104, 77, YELLOW, &pix);
/*
* This port hole is created by copying
* ellipse(x, y, 119, 57, 149, 82, BLACK, &pix);
* ellipse(x, y, 120, 58, 148, 81, LIGHT_BLUE, &pix);
* ellipse(x, y, 123, 61, 145, 78, BLACK, &pix);
* ellipse(x, y, 124, 62, 144, 77, YELLOW, &pix);
*/
ellipse(x, y, 159, 57, 189, 82, BLACK, &pix);
ellipse(x, y, 160, 58, 188, 81, LIGHT_BLUE, &pix);
ellipse(x, y, 163, 61, 185, 78, BLACK, &pix);
ellipse(x, y, 164, 62, 184, 77, YELLOW, &pix);
bitmap[WIDTH * y + x] = pix;
}
}
return EFI_ST_SUCCESS;
}
/*
* Tear down unit test.
*
* @return: EFI_ST_SUCCESS for success
*/
static int teardown(void)
{
efi_status_t ret;
if (bitmap) {
ret = boottime->free_pool(bitmap);
if (ret != EFI_SUCCESS) {
efi_st_error("FreePool failed\n");
return EFI_ST_FAILURE;
}
}
if (event) {
ret = boottime->close_event(event);
event = NULL;
if (ret != EFI_SUCCESS) {
efi_st_error("could not close event\n");
return EFI_ST_FAILURE;
}
}
return EFI_ST_SUCCESS;
}
/*
* Execute unit test.
*
* @return: EFI_ST_SUCCESS for success
*/
static int execute(void)
{
u32 max_mode;
efi_status_t ret;
struct efi_gop_mode_info *info;
if (!gop)
return EFI_ST_SUCCESS;
if (!gop->mode) {
efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n");
return EFI_ST_FAILURE;
}
info = gop->mode->info;
max_mode = gop->mode->max_mode;
if (!max_mode) {
efi_st_error("No graphical mode available\n");
return EFI_ST_FAILURE;
}
/* Fill background */
ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_FILL, 0, 0, 0, 0,
info->width, info->height, 0);
if (ret != EFI_SUCCESS) {
efi_st_error("EFI_BLT_VIDEO_FILL failed\n");
return EFI_ST_FAILURE;
}
/* Copy image to video */
ret = gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, 0, 0, 0, DEPTH,
WIDTH, HEIGHT, 0);
if (ret != EFI_SUCCESS) {
efi_st_error("EFI_BLT_BUFFER_TO_VIDEO failed\n");
return EFI_ST_FAILURE;
}
/* Copy left port hole */
ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_VIDEO,
79, 57 + DEPTH, 119, 57 + DEPTH,
31, 26, 0);
if (ret != EFI_SUCCESS) {
efi_st_error("EFI_BLT_VIDEO_TO_VIDEO failed\n");
return EFI_ST_FAILURE;
}
/* Copy port holes back to buffer */
ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_BLT_BUFFER,
94, 57 + DEPTH, 94, 57,
90, 26, WIDTH * sizeof(struct efi_gop_pixel));
if (ret != EFI_SUCCESS) {
efi_st_error("EFI_BLT_VIDEO_TO_BLT_BUFFER failed\n");
return EFI_ST_FAILURE;
}
/* Set 250ms timer */
xpos = WIDTH;
ret = boottime->set_timer(event, EFI_TIMER_PERIODIC, 250000);
if (ret != EFI_SUCCESS) {
efi_st_error("Could not set timer\n");
return EFI_ST_FAILURE;
}
con_out->set_cursor_position(con_out, 0, 0);
con_out->set_attribute(con_out, EFI_WHITE | EFI_BACKGROUND_BLUE);
efi_st_printf("The submarine should have three yellow port holes.\n");
efi_st_printf("Press any key to continue");
efi_st_get_key();
con_out->set_attribute(con_out, EFI_LIGHTGRAY);
efi_st_printf("\n");
return EFI_ST_SUCCESS;
}
EFI_UNIT_TEST(bitblt) = {
.name = "block image transfer",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.teardown = teardown,
.on_request = true,
};

View File

@ -29,6 +29,7 @@ static const efi_guid_t block_io_protocol_guid = BLOCK_IO_GUID;
static const efi_guid_t guid_device_path = DEVICE_PATH_GUID;
static const efi_guid_t guid_simple_file_system_protocol =
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
static efi_guid_t guid_vendor =
EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xb7, 0xb8);
@ -302,7 +303,11 @@ static int execute(void)
struct efi_device_path *dp_partition;
struct efi_simple_file_system_protocol *file_system;
struct efi_file_handle *root, *file;
u64 buf_size;
struct {
struct efi_file_system_info info;
u16 label[12];
} system_info;
efi_uintn_t buf_size;
char buf[16] __aligned(ARCH_DMA_MINALIGN);
ret = boottime->connect_controller(disk_handle, NULL, NULL, 1);
@ -356,6 +361,23 @@ static int execute(void)
efi_st_error("Failed to open volume\n");
return EFI_ST_FAILURE;
}
buf_size = sizeof(system_info);
ret = root->getinfo(root, &guid_file_system_info, &buf_size,
&system_info);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to get file system info\n");
return EFI_ST_FAILURE;
}
if (system_info.info.block_size != 512) {
efi_st_error("Wrong block size %u, expected 512\n",
system_info.info.block_size);
return EFI_ST_FAILURE;
}
if (efi_st_strcmp_16_8(system_info.info.volume_label, "U-BOOT TEST")) {
efi_st_todo(
"Wrong volume label '%ps', expected 'U-BOOT TEST'\n",
system_info.info.volume_label);
}
ret = root->open(root, &file, (s16 *)L"hello.txt", EFI_FILE_MODE_READ,
0);
if (ret != EFI_SUCCESS) {

View File

@ -3,21 +3,21 @@
*
* Generated with tools/file2include
*
* SPDX-License-Identifier: GPL-2.0+
* SPDX-License-Identifier: GPL-2.0+
*/
#define EFI_ST_DISK_IMG { 0x00010000, { \
{0x000001b8, "\x94\x37\x69\xfc\x00\x00\x00\x00"}, /* .7i..... */ \
{0x000001c0, "\x02\x00\x83\x02\x02\x00\x01\x00"}, /* ........ */ \
{0x000001b8, "\x21\x5d\x53\xd1\x00\x00\x00\x00"}, /* !]S..... */ \
{0x000001c0, "\x02\x00\x01\x02\x02\x00\x01\x00"}, /* ........ */ \
{0x000001c8, "\x00\x00\x7f\x00\x00\x00\x00\x00"}, /* ........ */ \
{0x000001f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
{0x00000200, "\xeb\x3c\x90\x6d\x6b\x66\x73\x2e"}, /* .<.mkfs. */ \
{0x00000208, "\x66\x61\x74\x00\x02\x04\x01\x00"}, /* fat..... */ \
{0x00000210, "\x02\x00\x02\x7f\x00\xf8\x01\x00"}, /* ........ */ \
{0x00000218, "\x20\x00\x40\x00\x00\x00\x00\x00"}, /* .@..... */ \
{0x00000220, "\x00\x00\x00\x00\x80\x00\x29\x86"}, /* ......). */ \
{0x00000228, "\xe8\x82\x80\x4e\x4f\x20\x4e\x41"}, /* ...NO NA */ \
{0x00000230, "\x4d\x45\x20\x20\x20\x20\x46\x41"}, /* ME FA */ \
{0x00000220, "\x00\x00\x00\x00\x80\x00\x29\xc4"}, /* ......). */ \
{0x00000228, "\xc4\x88\x11\x55\x2d\x42\x4f\x4f"}, /* ...U-BOO */ \
{0x00000230, "\x54\x20\x54\x45\x53\x54\x46\x41"}, /* T TESTFA */ \
{0x00000238, "\x54\x31\x32\x20\x20\x20\x0e\x1f"}, /* T12 .. */ \
{0x00000240, "\xbe\x5b\x7c\xac\x22\xc0\x74\x0b"}, /* .[|.".t. */ \
{0x00000248, "\x56\xb4\x0e\xbb\x07\x00\xcd\x10"}, /* V....... */ \
@ -36,34 +36,20 @@
{0x000002b0, "\x72\x79\x20\x61\x67\x61\x69\x6e"}, /* ry again */ \
{0x000002b8, "\x20\x2e\x2e\x2e\x20\x0d\x0a\x00"}, /* ... ... */ \
{0x000003f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
{0x00000400, "\xf8\xff\xff\x00\x00\x00\x00\xf0"}, /* ........ */ \
{0x00000408, "\xff\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
{0x00000600, "\xf8\xff\xff\x00\x00\x00\x00\xf0"}, /* ........ */ \
{0x00000608, "\xff\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
{0x00000800, "\xe5\x70\x00\x00\x00\xff\xff\xff"}, /* .p...... */ \
{0x00000808, "\xff\xff\xff\x0f\x00\x0e\xff\xff"}, /* ........ */ \
{0x00000810, "\xff\xff\xff\xff\xff\xff\xff\xff"}, /* ........ */ \
{0x00000818, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \
{0x00000820, "\xe5\x2e\x00\x68\x00\x65\x00\x6c"}, /* ...h.e.l */ \
{0x00000828, "\x00\x6c\x00\x0f\x00\x0e\x6f\x00"}, /* .l....o. */ \
{0x00000830, "\x2e\x00\x74\x00\x78\x00\x74\x00"}, /* ..t.x.t. */ \
{0x00000838, "\x2e\x00\x00\x00\x73\x00\x77\x00"}, /* ....s.w. */ \
{0x00000840, "\xe5\x45\x4c\x4c\x4f\x54\x7e\x31"}, /* .ELLOT~1 */ \
{0x00000848, "\x53\x57\x50\x20\x00\x64\xd0\x8a"}, /* SWP .d.. */ \
{0x00000850, "\x92\x4b\x92\x4b\x00\x00\xd0\x8a"}, /* .K.K.... */ \
{0x00000858, "\x92\x4b\x00\x00\x00\x00\x00\x00"}, /* .K...... */ \
{0x00000860, "\x41\x68\x00\x65\x00\x6c\x00\x6c"}, /* Ah.e.l.l */ \
{0x00000868, "\x00\x6f\x00\x0f\x00\xf1\x2e\x00"}, /* .o...... */ \
{0x00000870, "\x74\x00\x78\x00\x74\x00\x00\x00"}, /* t.x.t... */ \
{0x00000878, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \
{0x00000880, "\x48\x45\x4c\x4c\x4f\x20\x20\x20"}, /* HELLO */ \
{0x00000888, "\x54\x58\x54\x20\x00\x64\xd4\x8a"}, /* TXT .d.. */ \
{0x00000890, "\x92\x4b\x92\x4b\x00\x00\xd4\x8a"}, /* .K.K.... */ \
{0x00000898, "\x92\x4b\x05\x00\x0d\x00\x00\x00"}, /* .K...... */ \
{0x000008a0, "\xe5\x45\x4c\x4c\x4f\x54\x7e\x31"}, /* .ELLOT~1 */ \
{0x000008a8, "\x53\x57\x58\x20\x00\x64\xd0\x8a"}, /* SWX .d.. */ \
{0x000008b0, "\x92\x4b\x92\x4b\x00\x00\xd0\x8a"}, /* .K.K.... */ \
{0x000008b8, "\x92\x4b\x00\x00\x00\x00\x00\x00"}, /* .K...... */ \
{0x00006000, "\x48\x65\x6c\x6c\x6f\x20\x77\x6f"}, /* Hello wo */ \
{0x00006008, "\x72\x6c\x64\x21\x0a\x00\x00\x00"}, /* rld!.... */ \
{0x00000400, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \
{0x00000600, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \
{0x00000800, "\x55\x2d\x42\x4f\x4f\x54\x20\x54"}, /* U-BOOT T */ \
{0x00000808, "\x45\x53\x54\x08\x00\x00\xaa\x56"}, /* EST....V */ \
{0x00000810, "\x84\x4c\x84\x4c\x00\x00\xaa\x56"}, /* .L.L...V */ \
{0x00000818, "\x84\x4c\x00\x00\x00\x00\x00\x00"}, /* .L...... */ \
{0x00000820, "\x41\x68\x00\x65\x00\x6c\x00\x6c"}, /* Ah.e.l.l */ \
{0x00000828, "\x00\x6f\x00\x0f\x00\xf1\x2e\x00"}, /* .o...... */ \
{0x00000830, "\x74\x00\x78\x00\x74\x00\x00\x00"}, /* t.x.t... */ \
{0x00000838, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \
{0x00000840, "\x48\x45\x4c\x4c\x4f\x20\x20\x20"}, /* HELLO */ \
{0x00000848, "\x54\x58\x54\x20\x00\x64\xd7\x46"}, /* TXT .d.F */ \
{0x00000850, "\x84\x4c\x84\x4c\x00\x00\xd7\x46"}, /* .L.L...F */ \
{0x00000858, "\x84\x4c\x03\x00\x0d\x00\x00\x00"}, /* .L...... */ \
{0x00005000, "\x48\x65\x6c\x6c\x6f\x20\x77\x6f"}, /* Hello wo */ \
{0x00005008, "\x72\x6c\x64\x21\x0a\x00\x00\x00"}, /* rld!.... */ \
{0, NULL} } }

View File

@ -0,0 +1,140 @@
/*
* efi_selftest_event_groups
*
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This test checks the notification of group events and the
* following services:
* CreateEventEx, CloseEvent, SignalEvent, CheckEvent.
*/
#include <efi_selftest.h>
#define GROUP_SIZE 16
static struct efi_boot_services *boottime;
static efi_guid_t event_group =
EFI_GUID(0x2335905b, 0xc3b9, 0x4221, 0xa3, 0x71,
0x0e, 0x5b, 0x45, 0xc0, 0x56, 0x91);
/*
* Notification function, increments the notfication count if parameter
* context is provided.
*
* @event notified event
* @context pointer to the notification count
*/
static void EFIAPI notify(struct efi_event *event, void *context)
{
unsigned int *count = context;
if (count)
++*count;
}
/*
* Setup unit test.
*
* @handle: handle of the loaded image
* @systable: system table
* @return: EFI_ST_SUCCESS for success
*/
static int setup(const efi_handle_t handle,
const struct efi_system_table *systable)
{
boottime = systable->boottime;
return EFI_ST_SUCCESS;
}
/*
* Execute unit test.
*
* Create multiple events in an event group. Signal each event once and check
* that all events are notified once in each round.
*
* @return: EFI_ST_SUCCESS for success
*/
static int execute(void)
{
unsigned int counter[GROUP_SIZE] = {0};
struct efi_event *events[GROUP_SIZE];
size_t i, j;
efi_status_t ret;
for (i = 0; i < GROUP_SIZE; ++i) {
ret = boottime->create_event_ex(0, TPL_NOTIFY,
notify, (void *)&counter[i],
&event_group, &events[i]);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to create event\n");
return EFI_ST_FAILURE;
}
}
for (i = 0; i < GROUP_SIZE; ++i) {
ret = boottime->signal_event(events[i]);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to signal event\n");
return EFI_ST_FAILURE;
}
for (j = 0; j < GROUP_SIZE; ++j) {
if (counter[j] != i) {
efi_st_printf("i %u, j %u, count %u\n",
(unsigned int)i, (unsigned int)j,
(unsigned int)counter[j]);
efi_st_error(
"Notification function was called\n");
return EFI_ST_FAILURE;
}
/* Clear signaled state */
ret = boottime->check_event(events[j]);
if (ret != EFI_SUCCESS) {
efi_st_error("Event was not signaled\n");
return EFI_ST_FAILURE;
}
if (counter[j] != i) {
efi_st_printf("i %u, j %u, count %u\n",
(unsigned int)i, (unsigned int)j,
(unsigned int)counter[j]);
efi_st_error(
"Notification function was called\n");
return EFI_ST_FAILURE;
}
/* Call notification function */
ret = boottime->check_event(events[j]);
if (ret != EFI_NOT_READY) {
efi_st_error(
"Signaled state not cleared\n");
return EFI_ST_FAILURE;
}
if (counter[j] != i + 1) {
efi_st_printf("i %u, j %u, count %u\n",
(unsigned int)i, (unsigned int)j,
(unsigned int)counter[j]);
efi_st_error(
"Nofification function not called\n");
return EFI_ST_FAILURE;
}
}
}
for (i = 0; i < GROUP_SIZE; ++i) {
ret = boottime->close_event(events[i]);
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to close event\n");
return EFI_ST_FAILURE;
}
}
return EFI_ST_SUCCESS;
}
EFI_UNIT_TEST(eventgoups) = {
.name = "event groups",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
};

View File

@ -0,0 +1,188 @@
/*
* efi_selftest_pos
*
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
*
* The following services are tested:
* OutputString, TestString, SetAttribute.
*/
#include <efi_selftest.h>
#include <linux/libfdt.h>
static struct efi_boot_services *boottime;
static const char *fdt;
/* This should be sufficent for */
#define BUFFERSIZE 0x100000
static efi_guid_t fdt_guid = EFI_FDT_GUID;
/*
* Convert FDT value to host endianness.
*
* @val FDT value
* @return converted value
*/
static uint32_t f2h(fdt32_t val)
{
char *buf = (char *)&val;
char i;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
/* Swap the bytes */
i = buf[0]; buf[0] = buf[3]; buf[3] = i;
i = buf[1]; buf[1] = buf[2]; buf[2] = i;
#endif
return *(uint32_t *)buf;
}
/*
* Return the value of a property of the FDT root node.
*
* @name name of the property
* @return value of the property
*/
static char *get_property(const u16 *property)
{
struct fdt_header *header = (struct fdt_header *)fdt;
const fdt32_t *pos;
const char *strings;
if (!header)
return NULL;
if (f2h(header->magic) != FDT_MAGIC) {
printf("Wrong magic\n");
return NULL;
}
pos = (fdt32_t *)(fdt + f2h(header->off_dt_struct));
strings = fdt + f2h(header->off_dt_strings);
for (;;) {
switch (f2h(pos[0])) {
case FDT_BEGIN_NODE: {
char *c = (char *)&pos[1];
size_t i;
for (i = 0; c[i]; ++i)
;
pos = &pos[2 + (i >> 2)];
break;
}
case FDT_PROP: {
struct fdt_property *prop = (struct fdt_property *)pos;
const char *label = &strings[f2h(prop->nameoff)];
efi_status_t ret;
/* Check if this is the property to be returned */
if (!efi_st_strcmp_16_8(property, label)) {
char *str;
efi_uintn_t len = f2h(prop->len);
if (!len)
return NULL;
/*
* The string might not be 0 terminated.
* It is safer to make a copy.
*/
ret = boottime->allocate_pool(
EFI_LOADER_DATA, len + 1,
(void **)&str);
if (ret != EFI_SUCCESS) {
efi_st_printf("AllocatePool failed\n");
return NULL;
}
boottime->copy_mem(str, &pos[3], len);
str[len] = 0;
return str;
}
pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)];
break;
}
case FDT_NOP:
pos = &pos[1];
break;
default:
return NULL;
}
}
}
/*
* Setup unit test.
*
* @handle: handle of the loaded image
* @systable: system table
* @return: EFI_ST_SUCCESS for success
*/
static int setup(const efi_handle_t img_handle,
const struct efi_system_table *systable)
{
efi_uintn_t i;
boottime = systable->boottime;
/* Find configuration tables */
for (i = 0; i < systable->nr_tables; ++i) {
if (!efi_st_memcmp(&systable->tables[i].guid, &fdt_guid,
sizeof(efi_guid_t)))
fdt = systable->tables[i].table;
}
if (!fdt) {
efi_st_error("Missing device tree\n");
return EFI_ST_FAILURE;
}
return EFI_ST_SUCCESS;
}
/*
* Execute unit test.
*
* @return: EFI_ST_SUCCESS for success
*/
static int execute(void)
{
char *str;
efi_status_t ret;
str = get_property(L"compatible");
if (str) {
efi_st_printf("compatible: %s\n", str);
ret = boottime->free_pool(str);
if (ret != EFI_SUCCESS) {
efi_st_error("FreePool failed\n");
return EFI_ST_FAILURE;
}
} else {
efi_st_printf("Missing property 'compatible'\n");
return EFI_ST_FAILURE;
}
str = get_property(L"serial-number");
if (str) {
efi_st_printf("serial-number: %s\n", str);
ret = boottime->free_pool(str);
if (ret != EFI_SUCCESS) {
efi_st_error("FreePool failed\n");
return EFI_ST_FAILURE;
}
}
return EFI_ST_SUCCESS;
}
EFI_UNIT_TEST(fdt) = {
.name = "device tree",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.on_request = true,
};

View File

@ -0,0 +1,182 @@
/*
* efi_selftest_textinput
*
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
* The unicode character and the scan code are printed for text
* input. To run the test:
*
* setenv efi_selftest text input
* bootefi selftest
*/
#include <efi_selftest.h>
struct translate {
u16 code;
u16 *text;
};
static struct efi_boot_services *boottime;
static struct translate control_characters[] = {
{0, L"Null"},
{8, L"BS"},
{9, L"TAB"},
{10, L"LF"},
{13, L"CR"},
{0, NULL},
};
static u16 ch[] = L"' '";
static u16 unknown[] = L"unknown";
static struct translate scan_codes[] = {
{0x00, L"Null"},
{0x01, L"Up"},
{0x02, L"Down"},
{0x03, L"Right"},
{0x04, L"Left"},
{0x05, L"Home"},
{0x06, L"End"},
{0x07, L"Insert"},
{0x08, L"Delete"},
{0x09, L"Page Up"},
{0x0a, L"Page Down"},
{0x0b, L"FN 1"},
{0x0c, L"FN 2"},
{0x0d, L"FN 3"},
{0x0e, L"FN 4"},
{0x0f, L"FN 5"},
{0x10, L"FN 6"},
{0x11, L"FN 7"},
{0x12, L"FN 8"},
{0x13, L"FN 9"},
{0x14, L"FN 10"},
{0x15, L"FN 11"},
{0x16, L"FN 12"},
{0x17, L"Escape"},
{0x68, L"FN 13"},
{0x69, L"FN 14"},
{0x6a, L"FN 15"},
{0x6b, L"FN 16"},
{0x6c, L"FN 17"},
{0x6d, L"FN 18"},
{0x6e, L"FN 19"},
{0x6f, L"FN 20"},
{0x70, L"FN 21"},
{0x71, L"FN 22"},
{0x72, L"FN 23"},
{0x73, L"FN 24"},
{0x7f, L"Mute"},
{0x80, L"Volume Up"},
{0x81, L"Volume Down"},
{0x100, L"Brightness Up"},
{0x101, L"Brightness Down"},
{0x102, L"Suspend"},
{0x103, L"Hibernate"},
{0x104, L"Toggle Display"},
{0x105, L"Recovery"},
{0x106, L"Reject"},
{0x0, NULL},
};
/*
* Translate a unicode character to a string.
*
* @code unicode character
* @return string
*/
static u16 *translate_char(u16 code)
{
struct translate *tr;
if (code >= ' ') {
ch[1] = code;
return ch;
}
for (tr = control_characters; tr->text; ++tr) {
if (tr->code == code)
return tr->text;
}
return unknown;
}
/*
* Translate a scan code to a human readable string.
*
* @code unicode character
* @return string
*/
static u16 *translate_code(u16 code)
{
struct translate *tr;
for (tr = scan_codes; tr->text; ++tr) {
if (tr->code == code)
return tr->text;
}
return unknown;
}
/*
* Setup unit test.
*
* @handle: handle of the loaded image
* @systable: system table
* @return: EFI_ST_SUCCESS for success
*/
static int setup(const efi_handle_t handle,
const struct efi_system_table *systable)
{
boottime = systable->boottime;
return EFI_ST_SUCCESS;
}
/*
* Execute unit test.
*
* @return: EFI_ST_SUCCESS for success
*/
static int execute(void)
{
struct efi_input_key input_key = {0};
efi_status_t ret;
efi_st_printf("Waiting for your input\n");
efi_st_printf("To terminate type 'x'\n");
for (;;) {
/* Wait for next key */
do {
ret = con_in->read_key_stroke(con_in, &input_key);
} while (ret == EFI_NOT_READY);
/* Allow 5 minutes until time out */
boottime->set_watchdog_timer(300, 0, 0, NULL);
efi_st_printf("Unicode char %u (%ps), scan code %u (%ps)\n",
(unsigned int)input_key.unicode_char,
translate_char(input_key.unicode_char),
(unsigned int)input_key.scan_code,
translate_code(input_key.scan_code));
switch (input_key.unicode_char) {
case 'x':
case 'X':
return EFI_ST_SUCCESS;
}
}
}
EFI_UNIT_TEST(textinput) = {
.name = "text input",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.on_request = true,
};

View File

@ -24,6 +24,20 @@ def test_efi_selftest(u_boot_console):
raise Exception('Reset failed during the EFI selftest')
u_boot_console.restart_uboot();
@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
@pytest.mark.buildconfigspec('of_control')
def test_efi_selftest_device_tree(u_boot_console):
u_boot_console.run_command(cmd='setenv efi_selftest list')
output = u_boot_console.run_command('bootefi selftest')
assert '\'device tree\'' in output
u_boot_console.run_command(cmd='setenv efi_selftest device tree')
u_boot_console.run_command(cmd='setenv -f serial# Testing DT')
u_boot_console.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False)
m = u_boot_console.p.expect(['serial-number: Testing DT', 'U-Boot'])
if m != 0:
raise Exception('Reset failed in \'device tree\' test')
u_boot_console.restart_uboot();
@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
def test_efi_selftest_watchdog_reboot(u_boot_console):
u_boot_console.run_command(cmd='setenv efi_selftest list')