mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-25 13:14:19 +08:00
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:
commit
844fb498cc
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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 ();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
229
cmd/bootefi.c
229
cmd/bootefi.c
@ -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 {
|
||||
|
@ -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
332
doc/README.uefi
Normal 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
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
|
89
lib/efi_loader/efi_device_path_utilities.c
Normal file
89
lib/efi_loader/efi_device_path_utilities.c
Normal 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,
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
311
lib/efi_selftest/efi_selftest_bitblt.c
Normal file
311
lib/efi_selftest/efi_selftest_bitblt.c
Normal 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,
|
||||
};
|
@ -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) {
|
||||
|
@ -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} } }
|
||||
|
140
lib/efi_selftest/efi_selftest_event_groups.c
Normal file
140
lib/efi_selftest/efi_selftest_event_groups.c
Normal 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,
|
||||
};
|
188
lib/efi_selftest/efi_selftest_fdt.c
Normal file
188
lib/efi_selftest/efi_selftest_fdt.c
Normal 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,
|
||||
};
|
182
lib/efi_selftest/efi_selftest_textinput.c
Normal file
182
lib/efi_selftest/efi_selftest_textinput.c
Normal 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,
|
||||
};
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user