]> git.sur5r.net Git - u-boot/commitdiff
Merge tag 'signed-efi-next' of git://github.com/agraf/u-boot
authorTom Rini <trini@konsulko.com>
Mon, 9 Apr 2018 15:05:44 +0000 (11:05 -0400)
committerTom Rini <trini@konsulko.com>
Mon, 9 Apr 2018 15:05:44 +0000 (11:05 -0400)
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...

35 files changed:
MAINTAINERS
arch/arm/cpu/armv8/fsl-layerscape/cpu.c
arch/arm/cpu/armv8/fwcall.c
arch/arm/lib/interrupts.c
arch/arm/mach-bcm283x/reset.c
arch/x86/config.mk
cmd/bootefi.c
doc/README.efi [deleted file]
doc/README.uefi [new file with mode: 0644]
doc/git-mailrc
include/efi_api.h
include/efi_loader.h
include/pe.h
lib/efi_loader/Makefile
lib/efi_loader/efi_boottime.c
lib/efi_loader/efi_console.c
lib/efi_loader/efi_device_path.c
lib/efi_loader/efi_device_path_utilities.c [new file with mode: 0644]
lib/efi_loader/efi_file.c
lib/efi_loader/efi_gop.c
lib/efi_loader/efi_image_loader.c
lib/efi_loader/efi_memory.c
lib/efi_loader/efi_net.c
lib/efi_loader/efi_runtime.c
lib/efi_loader/efi_smbios.c
lib/efi_loader/efi_watchdog.c
lib/efi_loader/helloworld.c
lib/efi_selftest/Makefile
lib/efi_selftest/efi_selftest_bitblt.c [new file with mode: 0644]
lib/efi_selftest/efi_selftest_block_device.c
lib/efi_selftest/efi_selftest_disk_image.h
lib/efi_selftest/efi_selftest_event_groups.c [new file with mode: 0644]
lib/efi_selftest/efi_selftest_fdt.c [new file with mode: 0644]
lib/efi_selftest/efi_selftest_textinput.c [new file with mode: 0644]
test/py/tests/test_efi_selftest.py

index fde77b2b61161e40cb1006c27dedbab81a39f8fe..bf2a84c7e1ba48e712658d06235f73847624e1c9 100644 (file)
@@ -330,7 +330,7 @@ EFI PAYLOAD
 M:     Alexander Graf <agraf@suse.de>
 S:     Maintained
 T:     git git://github.com/agraf/u-boot.git
-F:     doc/README.efi
+F:     doc/README.uefi
 F:     doc/README.iscsi
 F:     include/efi*
 F:     include/pe.h
index 70a60709357304b38aa05f4826d97600ba9c80c5..45cbd91d97cc9cad7aef7f6ebb02d83986b457ee 100644 (file)
@@ -644,6 +644,7 @@ void __efi_runtime EFIAPI efi_reset_system(
        switch (reset_type) {
        case EFI_RESET_COLD:
        case EFI_RESET_WARM:
+       case EFI_RESET_PLATFORM_SPECIFIC:
                reset_cpu(0);
                break;
        case EFI_RESET_SHUTDOWN:
@@ -654,9 +655,9 @@ void __efi_runtime EFIAPI efi_reset_system(
        while (1) { }
 }
 
-void efi_reset_system_init(void)
+efi_status_t efi_reset_system_init(void)
 {
-       efi_add_runtime_mmio(&rstcr, sizeof(*rstcr));
+       return efi_add_runtime_mmio(&rstcr, sizeof(*rstcr));
 }
 
 #endif
index c2202675366f37d3853f1ef3e5d4d11d2b4a3e08..ff0712bf6505fe913e888e0021600c03291ea1a0 100644 (file)
@@ -146,6 +146,7 @@ void __efi_runtime EFIAPI efi_reset_system(
        switch (reset_type) {
        case EFI_RESET_COLD:
        case EFI_RESET_WARM:
+       case EFI_RESET_PLATFORM_SPECIFIC:
                psci_system_reset();
                break;
        case EFI_RESET_SHUTDOWN:
index 80869adb6107a71dc7a7ba1a74471be1444cd456..cda4d484605444f6e3019ce42b6fee1541ef4b38 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 #include <common.h>
+#include <efi_loader.h>
 #include <asm/proc-armv/ptrace.h>
 #include <asm/u-boot-arm.h>
 #include <efi_loader.h>
@@ -51,6 +52,11 @@ void bad_mode (void)
        reset_cpu (0);
 }
 
+static void show_efi_loaded_images(struct pt_regs *regs)
+{
+       efi_print_image_infos((void *)instruction_pointer(regs));
+}
+
 void show_regs (struct pt_regs *regs)
 {
        unsigned long __maybe_unused flags;
@@ -106,6 +112,7 @@ void do_undefined_instruction (struct pt_regs *pt_regs)
        printf ("undefined instruction\n");
        fixup_pc(pt_regs, -4);
        show_regs (pt_regs);
+       show_efi_loaded_images(pt_regs);
        bad_mode ();
 }
 
@@ -115,6 +122,7 @@ void do_software_interrupt (struct pt_regs *pt_regs)
        printf ("software interrupt\n");
        fixup_pc(pt_regs, -4);
        show_regs (pt_regs);
+       show_efi_loaded_images(pt_regs);
        bad_mode ();
 }
 
@@ -124,6 +132,7 @@ void do_prefetch_abort (struct pt_regs *pt_regs)
        printf ("prefetch abort\n");
        fixup_pc(pt_regs, -8);
        show_regs (pt_regs);
+       show_efi_loaded_images(pt_regs);
        bad_mode ();
 }
 
@@ -133,6 +142,7 @@ void do_data_abort (struct pt_regs *pt_regs)
        printf ("data abort\n");
        fixup_pc(pt_regs, -8);
        show_regs (pt_regs);
+       show_efi_loaded_images(pt_regs);
        bad_mode ();
 }
 
@@ -142,6 +152,7 @@ void do_not_used (struct pt_regs *pt_regs)
        printf ("not used\n");
        fixup_pc(pt_regs, -8);
        show_regs (pt_regs);
+       show_efi_loaded_images(pt_regs);
        bad_mode ();
 }
 
@@ -151,6 +162,7 @@ void do_fiq (struct pt_regs *pt_regs)
        printf ("fast interrupt request\n");
        fixup_pc(pt_regs, -8);
        show_regs (pt_regs);
+       show_efi_loaded_images(pt_regs);
        bad_mode ();
 }
 
@@ -160,5 +172,6 @@ void do_irq (struct pt_regs *pt_regs)
        printf ("interrupt request\n");
        fixup_pc(pt_regs, -8);
        show_regs (pt_regs);
+       show_efi_loaded_images(pt_regs);
        bad_mode ();
 }
index b62cb8a51ee0f27e8f9f44ec5a249354eaa6be2f..aa02d3f9f6a6ac78947ce9ddecdfd1254dd360fd 100644 (file)
@@ -63,6 +63,7 @@ void __efi_runtime EFIAPI efi_reset_system(
        switch (reset_type) {
        case EFI_RESET_COLD:
        case EFI_RESET_WARM:
+       case EFI_RESET_PLATFORM_SPECIFIC:
                reset_cpu(0);
                break;
        case EFI_RESET_SHUTDOWN:
@@ -82,9 +83,9 @@ void __efi_runtime EFIAPI efi_reset_system(
        while (1) { }
 }
 
-void efi_reset_system_init(void)
+efi_status_t efi_reset_system_init(void)
 {
-       efi_add_runtime_mmio(&wdog_regs, sizeof(*wdog_regs));
+       return efi_add_runtime_mmio(&wdog_regs, sizeof(*wdog_regs));
 }
 
 #endif
index 472ada549073dadf4bce66dec40fe1cc9ab6fd18..69074f4711cb99b61f9fbca8db3adccd7f758be0 100644 (file)
@@ -94,12 +94,16 @@ ifneq ($(CONFIG_EFI_STUB_64BIT),)
 EFI_LDS := elf_x86_64_efi.lds
 EFI_CRT0 := crt0_x86_64_efi.o
 EFI_RELOC := reloc_x86_64_efi.o
-EFI_TARGET := --target=efi-app-ia32
 else
 EFI_LDS := elf_ia32_efi.lds
 EFI_CRT0 := crt0_ia32_efi.o
 EFI_RELOC := reloc_ia32_efi.o
+endif
+
+ifdef CONFIG_X86_64
 EFI_TARGET := --target=efi-app-x86_64
+else
+EFI_TARGET := --target=efi-app-ia32
 endif
 
 endif
index 65462723485a4d864d3dd5e11d0de9b3bf6156e8..5a2a81005fcf734fbb873537f7b9f216f7d75e38 100644 (file)
 
 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 {
diff --git a/doc/README.efi b/doc/README.efi
deleted file mode 100644 (file)
index 956f5bf..0000000
+++ /dev/null
@@ -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/
diff --git a/doc/README.uefi b/doc/README.uefi
new file mode 100644 (file)
index 0000000..7403be3
--- /dev/null
@@ -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
index 5a365cddd92887d1c1a00db915cc18f49104c979..d5e309708c3d09c9028e2612c777a991364859b1 100644 (file)
@@ -15,6 +15,7 @@ alias abiessmann     Andreas Bießmann <andreas@biessmann.org>
 alias abrodkin       Alexey Brodkin <alexey.brodkin@synopsys.com>
 alias afleming       Andy Fleming <afleming@gmail.com>
 alias ag             Anatolij Gustschin <agust@denx.de>
+alias agraf          Alexander Graf <agraf@suse.de>
 alias alisonwang     Alison Wang <alison.wang@freescale.com>
 alias angelo_ts      Angelo Dureghello <angelo@sysam.it>
 alias bmeng          Bin Meng <bmeng.cn@gmail.com>
@@ -120,6 +121,7 @@ alias x86            uboot, sjg, bmeng
 alias dm             uboot, sjg
 alias cfi            uboot, stroese
 alias dfu            uboot, lukma
+alias efi            uboot, agraf
 alias eth            uboot, jhersh
 alias kerneldoc      uboot, marex
 alias fdt            uboot, sjg
index 3ba650e57e6dae5b6cb5e533a9ba9caab8166db1..ae9306116065f691f4ea8c50c002169157bfb885 100644 (file)
@@ -107,7 +107,7 @@ struct efi_boot_services {
        efi_status_t (EFIAPI *load_image)(bool boot_policiy,
                        efi_handle_t parent_image,
                        struct efi_device_path *file_path, void *source_buffer,
-                       unsigned long source_size, efi_handle_t *image);
+                       efi_uintn_t source_size, efi_handle_t *image);
        efi_status_t (EFIAPI *start_image)(efi_handle_t handle,
                                           unsigned long *exitdata_size,
                                           s16 **exitdata);
@@ -180,7 +180,8 @@ struct efi_boot_services {
 enum efi_reset_type {
        EFI_RESET_COLD = 0,
        EFI_RESET_WARM = 1,
-       EFI_RESET_SHUTDOWN = 2
+       EFI_RESET_SHUTDOWN = 2,
+       EFI_RESET_PLATFORM_SPECIFIC = 3,
 };
 
 /* EFI Runtime Services table */
@@ -243,6 +244,27 @@ struct efi_runtime_services {
                        u64 maximum_variable_size);
 };
 
+/* EFI event group GUID definitions */
+#define EFI_EVENT_GROUP_EXIT_BOOT_SERVICES \
+       EFI_GUID(0x27abf055, 0xb1b8, 0x4c26, 0x80, 0x48, \
+                0x74, 0x8f, 0x37, 0xba, 0xa2, 0xdf)
+
+#define EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE \
+       EFI_GUID(0x13fa7698, 0xc831, 0x49c7, 0x87, 0xea, \
+                0x8f, 0x43, 0xfc, 0xc2, 0x51, 0x96)
+
+#define EFI_EVENT_GROUP_MEMORY_MAP_CHANGE \
+       EFI_GUID(0x78bee926, 0x692f, 0x48fd, 0x9e, 0xdb, \
+                0x01, 0x42, 0x2e, 0xf0, 0xd7, 0xab)
+
+#define EFI_EVENT_GROUP_READY_TO_BOOT \
+       EFI_GUID(0x7ce88fb3, 0x4bd7, 0x4679, 0x87, 0xa8, \
+                0xa8, 0xd8, 0xde, 0xe5, 0x0d, 0x2b)
+
+#define EFI_EVENT_GROUP_RESET_SYSTEM \
+       EFI_GUID(0x62da6a56, 0x13fb, 0x485a, 0xa8, 0xda, \
+                0xa3, 0xdd, 0x79, 0x12, 0xcb, 0x6b)
+
 /* EFI Configuration Table and GUID definitions */
 #define NULL_GUID \
        EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
@@ -296,8 +318,8 @@ struct efi_loaded_image {
        u32 revision;
        void *parent_handle;
        struct efi_system_table *system_table;
-       void *device_handle;
-       void *file_path;
+       efi_handle_t device_handle;
+       struct efi_device_path *file_path;
        void *reserved;
        u32 load_options_size;
        void *load_options;
@@ -309,6 +331,8 @@ struct efi_loaded_image {
 
        /* Below are efi loader private fields */
 #ifdef CONFIG_EFI_LOADER
+       void *reloc_base;
+       aligned_u64 reloc_size;
        efi_status_t exit_status;
        struct jmp_buf_data exit_jmp;
 #endif
@@ -571,24 +595,6 @@ struct efi_simple_input_interface {
        struct efi_event *wait_for_key;
 };
 
-#define CONSOLE_CONTROL_GUID \
-       EFI_GUID(0xf42f7782, 0x12e, 0x4c12, \
-                0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21)
-#define EFI_CONSOLE_MODE_TEXT  0
-#define EFI_CONSOLE_MODE_GFX   1
-
-struct efi_console_control_protocol
-{
-       efi_status_t (EFIAPI *get_mode)(
-                       struct efi_console_control_protocol *this, int *mode,
-                       char *uga_exists, char *std_in_locked);
-       efi_status_t (EFIAPI *set_mode)(
-                       struct efi_console_control_protocol *this, int mode);
-       efi_status_t (EFIAPI *lock_std_in)(
-                       struct efi_console_control_protocol *this,
-                       uint16_t *password);
-};
-
 #define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID \
        EFI_GUID(0x8b843e20, 0x8132, 0x4852, \
                 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c)
@@ -605,6 +611,35 @@ struct efi_device_path_to_text_protocol
                        bool allow_shortcuts);
 };
 
+#define EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID \
+       EFI_GUID(0x0379be4e, 0xd706, 0x437d, \
+                0xb0, 0x37, 0xed, 0xb8, 0x2f, 0xb7, 0x72, 0xa4)
+
+struct efi_device_path_utilities_protocol {
+       efi_uintn_t (EFIAPI *get_device_path_size)(
+               const struct efi_device_path *device_path);
+       struct efi_device_path *(EFIAPI *duplicate_device_path)(
+               const struct efi_device_path *device_path);
+       struct efi_device_path *(EFIAPI *append_device_path)(
+               const struct efi_device_path *src1,
+               const struct efi_device_path *src2);
+       struct efi_device_path *(EFIAPI *append_device_node)(
+               const struct efi_device_path *device_path,
+               const struct efi_device_path *device_node);
+       struct efi_device_path *(EFIAPI *append_device_path_instance)(
+               const struct efi_device_path *device_path,
+               const struct efi_device_path *device_path_instance);
+       struct efi_device_path *(EFIAPI *get_next_device_path_instance)(
+               struct efi_device_path **device_path_instance,
+               efi_uintn_t *device_path_instance_size);
+       bool (EFIAPI *is_device_path_multi_instance)(
+               const struct efi_device_path *device_path);
+       struct efi_device_path *(EFIAPI *create_device_node)(
+               uint8_t node_type,
+               uint8_t node_sub_type,
+               uint16_t node_length);
+};
+
 #define EFI_GOP_GUID \
        EFI_GUID(0x9042a9de, 0x23dc, 0x4a38, \
                 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a)
@@ -633,6 +668,13 @@ struct efi_gop_mode
        unsigned long fb_size;
 };
 
+struct efi_gop_pixel {
+       u8 blue;
+       u8 green;
+       u8 red;
+       u8 reserved;
+};
+
 #define EFI_BLT_VIDEO_FILL             0
 #define EFI_BLT_VIDEO_TO_BLT_BUFFER    1
 #define EFI_BLT_BUFFER_TO_VIDEO                2
@@ -644,7 +686,8 @@ struct efi_gop
                                          efi_uintn_t *size_of_info,
                                          struct efi_gop_mode_info **info);
        efi_status_t (EFIAPI *set_mode)(struct efi_gop *this, u32 mode_number);
-       efi_status_t (EFIAPI *blt)(struct efi_gop *this, void *buffer,
+       efi_status_t (EFIAPI *blt)(struct efi_gop *this,
+                                  struct efi_gop_pixel *buffer,
                                   u32 operation, efi_uintn_t sx,
                                   efi_uintn_t sy, efi_uintn_t dx,
                                   efi_uintn_t dy, efi_uintn_t width,
@@ -662,7 +705,7 @@ struct efi_mac_address {
 
 struct efi_ip_address {
        u8 ip_addr[16];
-};
+} __attribute__((aligned(4)));
 
 enum efi_simple_network_state {
        EFI_NETWORK_STOPPED,
@@ -756,7 +799,28 @@ struct efi_pxe_packet {
 
 struct efi_pxe_mode
 {
-       u8 unused[52];
+       u8 started;
+       u8 ipv6_available;
+       u8 ipv6_supported;
+       u8 using_ipv6;
+       u8 bis_supported;
+       u8 bis_detected;
+       u8 auto_arp;
+       u8 send_guid;
+       u8 dhcp_discover_valid;
+       u8 dhcp_ack_received;
+       u8 proxy_offer_received;
+       u8 pxe_discover_valid;
+       u8 pxe_reply_received;
+       u8 pxe_bis_reply_received;
+       u8 icmp_error_received;
+       u8 tftp_error_received;
+       u8 make_callbacks;
+       u8 ttl;
+       u8 tos;
+       u8 pad;
+       struct efi_ip_address station_ip;
+       struct efi_ip_address subnet_mask;
        struct efi_pxe_packet dhcp_discover;
        struct efi_pxe_packet dhcp_ack;
        struct efi_pxe_packet proxy_offer;
@@ -794,17 +858,19 @@ struct efi_file_handle {
        efi_status_t (EFIAPI *close)(struct efi_file_handle *file);
        efi_status_t (EFIAPI *delete)(struct efi_file_handle *file);
        efi_status_t (EFIAPI *read)(struct efi_file_handle *file,
-                       u64 *buffer_size, void *buffer);
+                       efi_uintn_t *buffer_size, void *buffer);
        efi_status_t (EFIAPI *write)(struct efi_file_handle *file,
-                       u64 *buffer_size, void *buffer);
+                       efi_uintn_t *buffer_size, void *buffer);
        efi_status_t (EFIAPI *getpos)(struct efi_file_handle *file,
-                       u64 *pos);
+                       efi_uintn_t *pos);
        efi_status_t (EFIAPI *setpos)(struct efi_file_handle *file,
-                       u64 pos);
+                       efi_uintn_t pos);
        efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *file,
-                       efi_guid_t *info_type, u64 *buffer_size, void *buffer);
+                       const efi_guid_t *info_type, efi_uintn_t *buffer_size,
+                       void *buffer);
        efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *file,
-                       efi_guid_t *info_type, u64 buffer_size, void *buffer);
+                       const efi_guid_t *info_type, efi_uintn_t buffer_size,
+                       void *buffer);
        efi_status_t (EFIAPI *flush)(struct efi_file_handle *file);
 };
 
@@ -823,6 +889,10 @@ struct efi_simple_file_system_protocol {
        EFI_GUID(0x9576e92, 0x6d3f, 0x11d2, \
                 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
 
+#define EFI_FILE_SYSTEM_INFO_GUID \
+       EFI_GUID(0x09576e93, 0x6d3f, 0x11d2, \
+                0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+
 #define EFI_FILE_MODE_READ     0x0000000000000001
 #define EFI_FILE_MODE_WRITE    0x0000000000000002
 #define EFI_FILE_MODE_CREATE   0x8000000000000000
@@ -846,6 +916,15 @@ struct efi_file_info {
        s16 file_name[0];
 };
 
+struct efi_file_system_info {
+       u64 size;
+       u8 read_only;
+       u64 volume_size;
+       u64 free_space;
+       u32 block_size;
+       u16 volume_label[0];
+};
+
 #define EFI_DRIVER_BINDING_PROTOCOL_GUID \
        EFI_GUID(0x18a031ab, 0xb443, 0x4d1a,\
                 0xa5, 0xc0, 0x0c, 0x09, 0x26, 0x1e, 0x9f, 0x71)
index 07730c3f3941de2287cd697122c93905a3b10f13..17f9d3d1ef2c58ec3471291d9d39a8959d44e9d4 100644 (file)
@@ -83,6 +83,9 @@ extern struct efi_simple_text_output_protocol efi_con_out;
 extern struct efi_simple_input_interface efi_con_in;
 extern struct efi_console_control_protocol efi_console_control;
 extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
+/* implementation of the EFI_DEVICE_PATH_UTILITIES_PROTOCOL */
+extern const struct efi_device_path_utilities_protocol
+                                       efi_device_path_utilities;
 
 uint16_t *efi_dp_str(struct efi_device_path *dp);
 
@@ -93,10 +96,25 @@ extern const efi_guid_t efi_guid_console_control;
 extern const efi_guid_t efi_guid_device_path;
 /* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
 extern const efi_guid_t efi_guid_driver_binding_protocol;
+/* event group ExitBootServices() invoked */
+extern const efi_guid_t efi_guid_event_group_exit_boot_services;
+/* event group SetVirtualAddressMap() invoked */
+extern const efi_guid_t efi_guid_event_group_virtual_address_change;
+/* event group memory map changed */
+extern const efi_guid_t efi_guid_event_group_memory_map_change;
+/* event group boot manager about to boot */
+extern const efi_guid_t efi_guid_event_group_ready_to_boot;
+/* event group ResetSystem() invoked (before ExitBootServices) */
+extern const efi_guid_t efi_guid_event_group_reset_system;
+/* GUID of the device tree table */
+extern const efi_guid_t efi_guid_fdt;
 extern const efi_guid_t efi_guid_loaded_image;
 extern const efi_guid_t efi_guid_device_path_to_text_protocol;
 extern const efi_guid_t efi_simple_file_system_protocol_guid;
 extern const efi_guid_t efi_file_info_guid;
+/* GUID for file system information */
+extern const efi_guid_t efi_file_system_info_guid;
+extern const efi_guid_t efi_guid_device_path_utilities_protocol;
 
 extern unsigned int __efi_runtime_start, __efi_runtime_stop;
 extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@@ -144,21 +162,25 @@ struct efi_object {
 /**
  * struct efi_event
  *
+ * @link:              Link to list of all events
  * @type:              Type of event, see efi_create_event
  * @notify_tpl:                Task priority level of notifications
- * @trigger_time:      Period of the timer
- * @trigger_next:      Next time to trigger the timer
  * @nofify_function:   Function to call when the event is triggered
  * @notify_context:    Data to be passed to the notify function
+ * @group:             Event group
+ * @trigger_time:      Period of the timer
+ * @trigger_next:      Next time to trigger the timer
  * @trigger_type:      Type of timer, see efi_set_timer
- * @queued:            The notification function is queued
- * @signaled:          The event occurred. The event is in the signaled state.
+ * @is_queued:         The notification function is queued
+ * @is_signaled:       The event occurred. The event is in the signaled state.
  */
 struct efi_event {
+       struct list_head link;
        uint32_t type;
        efi_uintn_t notify_tpl;
        void (EFIAPI *notify_function)(struct efi_event *event, void *context);
        void *notify_context;
+       const efi_guid_t *group;
        u64 trigger_next;
        u64 trigger_time;
        enum efi_timer_delay trigger_type;
@@ -166,9 +188,10 @@ struct efi_event {
        bool is_signaled;
 };
 
-
 /* This list contains all UEFI objects we know of */
 extern struct list_head efi_obj_list;
+/* List of all events */
+extern struct list_head efi_events;
 
 /* Called by bootefi to make console interface available */
 int efi_console_register(void);
@@ -179,13 +202,13 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
                               const char *if_typename, int diskid,
                               const char *pdevname);
 /* Called by bootefi to make GOP (graphical) interface available */
-int efi_gop_register(void);
+efi_status_t efi_gop_register(void);
 /* Called by bootefi to make the network interface available */
-int efi_net_register(void);
+efi_status_t efi_net_register(void);
 /* Called by bootefi to make the watchdog available */
-int efi_watchdog_register(void);
+efi_status_t efi_watchdog_register(void);
 /* Called by bootefi to make SMBIOS tables available */
-void efi_smbios_register(void);
+efi_status_t efi_smbios_register(void);
 
 struct efi_simple_file_system_protocol *
 efi_fs_from_path(struct efi_device_path *fp);
@@ -235,7 +258,8 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
                              void (EFIAPI *notify_function) (
                                        struct efi_event *event,
                                        void *context),
-                             void *notify_context, struct efi_event **event);
+                             void *notify_context, efi_guid_t *group,
+                             struct efi_event **event);
 /* Call this to set a timer */
 efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
                           uint64_t trigger_time);
@@ -284,6 +308,10 @@ efi_status_t efi_setup_loaded_image(
                        struct efi_device_path *file_path);
 efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
                                      void **buffer);
+/* Print information about a loaded image */
+efi_status_t efi_print_image_info(struct efi_loaded_image *image, void *pc);
+/* Print information about all loaded images */
+void efi_print_image_infos(void *pc);
 
 #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
 extern void *efi_bounce_buffer;
@@ -330,6 +358,7 @@ static inline void ascii2unicode(u16 *unicode, const char *ascii)
 {
        while (*ascii)
                *(unicode++) = *(ascii++);
+       *unicode = 0;
 }
 
 static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
@@ -346,7 +375,7 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
 
 /* Call this with mmio_ptr as the _pointer_ to a pointer to an MMIO region
  * to make it available at runtime */
-void efi_add_runtime_mmio(void *mmio_ptr, u64 len);
+efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len);
 
 /* Boards may provide the functions below to implement RTS functionality */
 
@@ -354,12 +383,14 @@ void __efi_runtime EFIAPI efi_reset_system(
                        enum efi_reset_type reset_type,
                        efi_status_t reset_status,
                        unsigned long data_size, void *reset_data);
-void efi_reset_system_init(void);
+
+/* Architecture specific initialization of the EFI subsystem */
+efi_status_t efi_reset_system_init(void);
 
 efi_status_t __efi_runtime EFIAPI efi_get_time(
                        struct efi_time *time,
                        struct efi_time_cap *capabilities);
-void efi_get_time_init(void);
+efi_status_t efi_get_time_init(void);
 
 #ifdef CONFIG_CMD_BOOTEFI_SELFTEST
 /*
@@ -388,13 +419,17 @@ void *efi_bootmgr_load(struct efi_device_path **device_path,
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
 #define __efi_runtime_data
 #define __efi_runtime
-static inline void efi_add_runtime_mmio(void *mmio_ptr, u64 len) { }
+static inline efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
+{
+       return EFI_SUCCESS;
+}
 
 /* No loader configured, stub out EFI_ENTRY */
 static inline void efi_restore_gd(void) { }
 static inline void efi_set_bootdev(const char *dev, const char *devnr,
                                   const char *path) { }
 static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
+static inline void efi_print_image_infos(void *pc) { }
 
 #endif /* CONFIG_EFI_LOADER && !CONFIG_SPL_BUILD */
 
index c3a19cef765ed17ee0764b5f1edbd1664b6c489f..e7845bb7d2cfa5ff00144b768d12492e7af849e3 100644 (file)
@@ -38,11 +38,15 @@ typedef struct _IMAGE_DOS_HEADER {
 #define IMAGE_DOS_SIGNATURE            0x5A4D     /* MZ   */
 #define IMAGE_NT_SIGNATURE             0x00004550 /* PE00 */
 
+#define IMAGE_FILE_MACHINE_I386                0x014c
 #define IMAGE_FILE_MACHINE_ARM         0x01c0
 #define IMAGE_FILE_MACHINE_THUMB       0x01c2
 #define IMAGE_FILE_MACHINE_ARMNT       0x01c4
 #define IMAGE_FILE_MACHINE_AMD64       0x8664
 #define IMAGE_FILE_MACHINE_ARM64       0xaa64
+#define IMAGE_FILE_MACHINE_RISCV32     0x5032
+#define IMAGE_FILE_MACHINE_RISCV64     0x5064
+
 #define IMAGE_NT_OPTIONAL_HDR32_MAGIC  0x10b
 #define IMAGE_NT_OPTIONAL_HDR64_MAGIC  0x20b
 #define IMAGE_SUBSYSTEM_EFI_APPLICATION        10
index 2a87d9ed7760ed896c0a9010246e87ada524d91b..d2ce89713ec737036fddcfc6fbab98e19467b6b1 100644 (file)
@@ -17,7 +17,8 @@ endif
 obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
 obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
 obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o
-obj-y += efi_file.o efi_variable.o efi_bootmgr.o efi_watchdog.o
+obj-y += efi_device_path_utilities.o efi_file.o efi_variable.o efi_bootmgr.o
+obj-y += efi_watchdog.o
 obj-$(CONFIG_LCD) += efi_gop.o
 obj-$(CONFIG_DM_VIDEO) += efi_gop.o
 obj-$(CONFIG_PARTITIONS) += efi_disk.o
index 66e26fd63a128229a8d62595dd8709d09b801fab..7a9449f59c38368ad3d8bc5e944bb3dd98d54dab 100644 (file)
@@ -26,6 +26,9 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION;
 /* This list contains all the EFI objects our payload has access to */
 LIST_HEAD(efi_obj_list);
 
+/* List of all events */
+LIST_HEAD(efi_events);
+
 /*
  * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
  * we need to do trickery with caches. Since we don't want to break the EFI
@@ -56,10 +59,28 @@ static volatile void *efi_gd, *app_gd;
 
 static int entry_count;
 static int nesting_level;
+/* GUID of the device tree table */
+const efi_guid_t efi_guid_fdt = EFI_FDT_GUID;
 /* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
 const efi_guid_t efi_guid_driver_binding_protocol =
                        EFI_DRIVER_BINDING_PROTOCOL_GUID;
 
+/* event group ExitBootServices() invoked */
+const efi_guid_t efi_guid_event_group_exit_boot_services =
+                       EFI_EVENT_GROUP_EXIT_BOOT_SERVICES;
+/* event group SetVirtualAddressMap() invoked */
+const efi_guid_t efi_guid_event_group_virtual_address_change =
+                       EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE;
+/* event group memory map changed */
+const efi_guid_t efi_guid_event_group_memory_map_change =
+                       EFI_EVENT_GROUP_MEMORY_MAP_CHANGE;
+/* event group boot manager about to boot */
+const efi_guid_t efi_guid_event_group_ready_to_boot =
+                       EFI_EVENT_GROUP_READY_TO_BOOT;
+/* event group ResetSystem() invoked (before ExitBootServices) */
+const efi_guid_t efi_guid_event_group_reset_system =
+                       EFI_EVENT_GROUP_RESET_SYSTEM;
+
 static efi_status_t EFIAPI efi_disconnect_controller(
                                        efi_handle_t controller_handle,
                                        efi_handle_t driver_image_handle,
@@ -121,6 +142,7 @@ static const char *indent_string(int level)
 {
        const char *indent = "                    ";
        const int max = strlen(indent);
+
        level = min(max, level * 2);
        return &indent[max - level];
 }
@@ -154,7 +176,7 @@ const char *__efi_nesting_dec(void)
  * @event      event to signal
  * @check_tpl  check the TPL level
  */
-void efi_signal_event(struct efi_event *event, bool check_tpl)
+static void efi_queue_event(struct efi_event *event, bool check_tpl)
 {
        if (event->notify_function) {
                event->is_queued = true;
@@ -167,6 +189,50 @@ void efi_signal_event(struct efi_event *event, bool check_tpl)
        event->is_queued = false;
 }
 
+/*
+ * Signal an EFI event.
+ *
+ * This function signals an event. If the event belongs to an event group
+ * all events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL
+ * their notification function is queued.
+ *
+ * For the SignalEvent service see efi_signal_event_ext.
+ *
+ * @event      event to signal
+ * @check_tpl  check the TPL level
+ */
+void efi_signal_event(struct efi_event *event, bool check_tpl)
+{
+       if (event->group) {
+               struct efi_event *evt;
+
+               /*
+                * The signaled state has to set before executing any
+                * notification function
+                */
+               list_for_each_entry(evt, &efi_events, link) {
+                       if (!evt->group || guidcmp(evt->group, event->group))
+                               continue;
+                       if (evt->is_signaled)
+                               continue;
+                       evt->is_signaled = true;
+                       if (evt->type & EVT_NOTIFY_SIGNAL &&
+                           evt->notify_function)
+                               evt->is_queued = true;
+               }
+               list_for_each_entry(evt, &efi_events, link) {
+                       if (!evt->group || guidcmp(evt->group, event->group))
+                               continue;
+                       if (evt->is_queued)
+                               efi_queue_event(evt, check_tpl);
+               }
+       } else if (!event->is_signaled) {
+               event->is_signaled = true;
+               if (event->type & EVT_NOTIFY_SIGNAL)
+                       efi_queue_event(event, check_tpl);
+       }
+}
+
 /*
  * Raise the task priority level.
  *
@@ -212,6 +278,11 @@ static void EFIAPI efi_restore_tpl(efi_uintn_t old_tpl)
        if (efi_tpl > TPL_HIGH_LEVEL)
                efi_tpl = TPL_HIGH_LEVEL;
 
+       /*
+        * Lowering the TPL may have made queued events eligible for execution.
+        */
+       efi_timer_check();
+
        EFI_EXIT(EFI_SUCCESS);
 }
 
@@ -255,7 +326,7 @@ static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory,
 {
        efi_status_t r;
 
-       EFI_ENTRY("%"PRIx64", 0x%zx", memory, pages);
+       EFI_ENTRY("%" PRIx64 ", 0x%zx", memory, pages);
        r = efi_free_pages(memory, pages);
        return EFI_EXIT(r);
 }
@@ -470,10 +541,23 @@ void efi_delete_handle(struct efi_object *obj)
 }
 
 /*
- * Our event capabilities are very limited. Only a small limited
- * number of events is allowed to coexist.
+ * Check if a pointer is a valid event.
+ *
+ * @event              pointer to check
+ * @return             status code
  */
-static struct efi_event efi_events[16];
+static efi_status_t efi_is_event(const struct efi_event *event)
+{
+       const struct efi_event *evt;
+
+       if (!event)
+               return EFI_INVALID_PARAMETER;
+       list_for_each_entry(evt, &efi_events, link) {
+               if (evt == event)
+                       return EFI_SUCCESS;
+       }
+       return EFI_INVALID_PARAMETER;
+}
 
 /*
  * Create an event.
@@ -494,9 +578,10 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
                              void (EFIAPI *notify_function) (
                                        struct efi_event *event,
                                        void *context),
-                             void *notify_context, struct efi_event **event)
+                             void *notify_context, efi_guid_t *group,
+                             struct efi_event **event)
 {
-       int i;
+       struct efi_event *evt;
 
        if (event == NULL)
                return EFI_INVALID_PARAMETER;
@@ -504,25 +589,25 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
        if ((type & EVT_NOTIFY_SIGNAL) && (type & EVT_NOTIFY_WAIT))
                return EFI_INVALID_PARAMETER;
 
-       if ((type & (EVT_NOTIFY_SIGNAL|EVT_NOTIFY_WAIT)) &&
+       if ((type & (EVT_NOTIFY_SIGNAL | EVT_NOTIFY_WAIT)) &&
            notify_function == NULL)
                return EFI_INVALID_PARAMETER;
 
-       for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
-               if (efi_events[i].type)
-                       continue;
-               efi_events[i].type = type;
-               efi_events[i].notify_tpl = notify_tpl;
-               efi_events[i].notify_function = notify_function;
-               efi_events[i].notify_context = notify_context;
-               /* Disable timers on bootup */
-               efi_events[i].trigger_next = -1ULL;
-               efi_events[i].is_queued = false;
-               efi_events[i].is_signaled = false;
-               *event = &efi_events[i];
-               return EFI_SUCCESS;
-       }
-       return EFI_OUT_OF_RESOURCES;
+       evt = calloc(1, sizeof(struct efi_event));
+       if (!evt)
+               return EFI_OUT_OF_RESOURCES;
+       evt->type = type;
+       evt->notify_tpl = notify_tpl;
+       evt->notify_function = notify_function;
+       evt->notify_context = notify_context;
+       evt->group = group;
+       /* Disable timers on bootup */
+       evt->trigger_next = -1ULL;
+       evt->is_queued = false;
+       evt->is_signaled = false;
+       list_add_tail(&evt->link, &efi_events);
+       *event = evt;
+       return EFI_SUCCESS;
 }
 
 /*
@@ -551,10 +636,8 @@ efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl,
 {
        EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function,
                  notify_context, event_group);
-       if (event_group)
-               return EFI_EXIT(EFI_UNSUPPORTED);
        return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
-                                        notify_context, event));
+                                        notify_context, event_group, event));
 }
 
 /*
@@ -581,10 +664,9 @@ static efi_status_t EFIAPI efi_create_event_ext(
        EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function,
                  notify_context);
        return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
-                                        notify_context, event));
+                                        notify_context, NULL, event));
 }
 
-
 /*
  * Check if a timer event has occurred or a queued notification function should
  * be called.
@@ -594,30 +676,26 @@ static efi_status_t EFIAPI efi_create_event_ext(
  */
 void efi_timer_check(void)
 {
-       int i;
+       struct efi_event *evt;
        u64 now = timer_get_us();
 
-       for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
-               if (!efi_events[i].type)
-                       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)
+       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;
-               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,
 };
index 28d63635ec7d27a7500757a6ef3285155fb23969..5d1a9a8081eeab0681caf546977c15e76e80e3a8 100644 (file)
@@ -45,7 +45,6 @@ static struct cout_mode efi_cout_modes[] = {
        },
 };
 
-const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID;
 const efi_guid_t efi_guid_text_output_protocol =
                        EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
 const efi_guid_t efi_guid_text_input_protocol =
@@ -54,43 +53,6 @@ const efi_guid_t efi_guid_text_input_protocol =
 #define cESC '\x1b'
 #define ESC "\x1b"
 
-static efi_status_t EFIAPI efi_cin_get_mode(
-                       struct efi_console_control_protocol *this,
-                       int *mode, char *uga_exists, char *std_in_locked)
-{
-       EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked);
-
-       if (mode)
-               *mode = EFI_CONSOLE_MODE_TEXT;
-       if (uga_exists)
-               *uga_exists = 0;
-       if (std_in_locked)
-               *std_in_locked = 0;
-
-       return EFI_EXIT(EFI_SUCCESS);
-}
-
-static efi_status_t EFIAPI efi_cin_set_mode(
-                       struct efi_console_control_protocol *this, int mode)
-{
-       EFI_ENTRY("%p, %d", this, mode);
-       return EFI_EXIT(EFI_UNSUPPORTED);
-}
-
-static efi_status_t EFIAPI efi_cin_lock_std_in(
-                       struct efi_console_control_protocol *this,
-                       uint16_t *password)
-{
-       EFI_ENTRY("%p, %p", this, password);
-       return EFI_EXIT(EFI_UNSUPPORTED);
-}
-
-struct efi_console_control_protocol efi_console_control = {
-       .get_mode = efi_cin_get_mode,
-       .set_mode = efi_cin_set_mode,
-       .lock_std_in = efi_cin_lock_std_in,
-};
-
 /* Default to mode 0 */
 static struct simple_text_output_mode efi_con_mode = {
        .max_mode = 1,
@@ -399,6 +361,48 @@ static efi_status_t EFIAPI efi_cin_reset(
        return EFI_EXIT(EFI_UNSUPPORTED);
 }
 
+/*
+ * Analyze modifiers (shift, alt, ctrl) for function keys.
+ * This gets called when we have already parsed CSI.
+ *
+ * @modifiers:  bitmask (shift, alt, ctrl)
+ * @return:    the unmodified code
+ */
+static char skip_modifiers(int *modifiers)
+{
+       char c, mod = 0, ret = 0;
+
+       c = getc();
+
+       if (c != ';') {
+               ret = c;
+               if (c == '~')
+                       goto out;
+               c = getc();
+       }
+       for (;;) {
+               switch (c) {
+               case '0'...'9':
+                       mod *= 10;
+                       mod += c - '0';
+               /* fall through */
+               case ';':
+                       c = getc();
+                       break;
+               default:
+                       goto out;
+               }
+       }
+out:
+       if (mod)
+               --mod;
+       if (modifiers)
+               *modifiers = mod;
+       if (!ret)
+               ret = c;
+       return ret;
+}
+
 static efi_status_t EFIAPI efi_cin_read_key_stroke(
                        struct efi_simple_input_interface *this,
                        struct efi_input_key *key)
@@ -421,14 +425,21 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
 
        ch = getc();
        if (ch == cESC) {
-               /* Escape Sequence */
+               /*
+                * Xterm Control Sequences
+                * https://www.xfree86.org/4.8.0/ctlseqs.html
+                */
                ch = getc();
                switch (ch) {
                case cESC: /* ESC */
                        pressed_key.scan_code = 23;
                        break;
                case 'O': /* F1 - F4 */
-                       pressed_key.scan_code = getc() - 'P' + 11;
+                       ch = getc();
+                       /* skip modifiers */
+                       if (ch <= '9')
+                               ch = getc();
+                       pressed_key.scan_code = ch - 'P' + 11;
                        break;
                case 'a'...'z':
                        ch = ch - 'a';
@@ -445,17 +456,51 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
                        case 'H': /* Home */
                                pressed_key.scan_code = 5;
                                break;
-                       case '1': /* F5 - F8 */
-                               pressed_key.scan_code = getc() - '0' + 11;
-                               getc();
+                       case '1':
+                               ch = skip_modifiers(NULL);
+                               switch (ch) {
+                               case '1'...'5': /* F1 - F5 */
+                                       pressed_key.scan_code = ch - '1' + 11;
+                                       break;
+                               case '7'...'9': /* F6 - F8 */
+                                       pressed_key.scan_code = ch - '7' + 16;
+                                       break;
+                               case 'A'...'D': /* up, down right, left */
+                                       pressed_key.scan_code = ch - 'A' + 1;
+                                       break;
+                               case 'F':
+                                       pressed_key.scan_code = 6; /* End */
+                                       break;
+                               case 'H':
+                                       pressed_key.scan_code = 5; /* Home */
+                                       break;
+                               }
                                break;
-                       case '2': /* F9 - F12 */
-                               pressed_key.scan_code = getc() - '0' + 19;
-                               getc();
+                       case '2':
+                               ch = skip_modifiers(NULL);
+                               switch (ch) {
+                               case '0'...'1': /* F9 - F10 */
+                                       pressed_key.scan_code = ch - '0' + 19;
+                                       break;
+                               case '3'...'4': /* F11 - F12 */
+                                       pressed_key.scan_code = ch - '3' + 21;
+                                       break;
+                               case '~': /* INS */
+                                       pressed_key.scan_code = 7;
+                                       break;
+                               }
                                break;
                        case '3': /* DEL */
                                pressed_key.scan_code = 8;
-                               getc();
+                               skip_modifiers(NULL);
+                               break;
+                       case '5': /* PG UP */
+                               pressed_key.scan_code = 9;
+                               skip_modifiers(NULL);
+                               break;
+                       case '6': /* PG DOWN */
+                               pressed_key.scan_code = 10;
+                               skip_modifiers(NULL);
                                break;
                        }
                        break;
@@ -464,7 +509,8 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
                /* Backspace */
                ch = 0x08;
        }
-       pressed_key.unicode_char = ch;
+       if (!pressed_key.scan_code)
+               pressed_key.unicode_char = ch;
        *key = pressed_key;
 
        return EFI_EXIT(EFI_SUCCESS);
@@ -506,18 +552,10 @@ static void EFIAPI efi_console_timer_notify(struct efi_event *event,
 int efi_console_register(void)
 {
        efi_status_t r;
-       struct efi_object *efi_console_control_obj;
        struct efi_object *efi_console_output_obj;
        struct efi_object *efi_console_input_obj;
 
        /* Create handles */
-       r = efi_create_handle((efi_handle_t *)&efi_console_control_obj);
-       if (r != EFI_SUCCESS)
-               goto out_of_memory;
-       r = efi_add_protocol(efi_console_control_obj->handle,
-                            &efi_guid_console_control, &efi_console_control);
-       if (r != EFI_SUCCESS)
-               goto out_of_memory;
        r = efi_create_handle((efi_handle_t *)&efi_console_output_obj);
        if (r != EFI_SUCCESS)
                goto out_of_memory;
@@ -534,14 +572,14 @@ int efi_console_register(void)
                goto out_of_memory;
 
        /* Create console events */
-       r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
-                            efi_key_notify, NULL, &efi_con_in.wait_for_key);
+       r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
+                            NULL, NULL, &efi_con_in.wait_for_key);
        if (r != EFI_SUCCESS) {
                printf("ERROR: Failed to register WaitForKey event\n");
                return r;
        }
        r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
-                            efi_console_timer_notify, NULL,
+                            efi_console_timer_notify, NULL, NULL,
                             &console_timer_event);
        if (r != EFI_SUCCESS) {
                printf("ERROR: Failed to register console event\n");
index 3c735e60d3bc540875221bf8e64f2e3fcea2cf04..ab28b2fd257cdfaefe3201b55160f7d9c9135f46 100644 (file)
@@ -66,6 +66,7 @@ static void *dp_alloc(size_t sz)
                return NULL;
        }
 
+       memset(buf, 0, sz);
        return buf;
 }
 
@@ -749,7 +750,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
 #ifdef CONFIG_CMD_NET
 struct efi_device_path *efi_dp_from_eth(void)
 {
+#ifndef CONFIG_DM_ETH
        struct efi_device_path_mac_addr *ndp;
+#endif
        void *buf, *start;
        unsigned dpsize = 0;
 
@@ -759,8 +762,8 @@ struct efi_device_path *efi_dp_from_eth(void)
        dpsize += dp_size(eth_get_dev());
 #else
        dpsize += sizeof(ROOT);
-#endif
        dpsize += sizeof(*ndp);
+#endif
 
        start = buf = dp_alloc(dpsize + sizeof(END));
        if (!buf)
@@ -771,14 +774,15 @@ struct efi_device_path *efi_dp_from_eth(void)
 #else
        memcpy(buf, &ROOT, sizeof(ROOT));
        buf += sizeof(ROOT);
-#endif
 
        ndp = buf;
        ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
        ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
        ndp->dp.length = sizeof(*ndp);
+       ndp->if_type = 1; /* Ethernet */
        memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
        buf = &ndp[1];
+#endif
 
        *((struct efi_device_path *)buf) = END;
 
diff --git a/lib/efi_loader/efi_device_path_utilities.c b/lib/efi_loader/efi_device_path_utilities.c
new file mode 100644 (file)
index 0000000..bc97eee
--- /dev/null
@@ -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,
+};
index 52a4e7438e33c2d6dfddda22debdde81f1a82a34..cec8347f558722c32ed45ea584f45d3a3497668b 100644 (file)
@@ -12,6 +12,9 @@
 #include <malloc.h>
 #include <fs.h>
 
+/* GUID for file system information */
+const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
+
 struct file_system {
        struct efi_simple_file_system_protocol base;
        struct efi_device_path *dp;
@@ -314,29 +317,41 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
 }
 
 static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
-               u64 *buffer_size, void *buffer)
+                                        efi_uintn_t *buffer_size, void *buffer)
 {
        struct file_handle *fh = to_fh(file);
        efi_status_t ret = EFI_SUCCESS;
+       u64 bs;
 
        EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
 
+       if (!buffer_size || !buffer) {
+               ret = EFI_INVALID_PARAMETER;
+               goto error;
+       }
+
        if (set_blk_dev(fh)) {
                ret = EFI_DEVICE_ERROR;
                goto error;
        }
 
+       bs = *buffer_size;
        if (fh->isdir)
-               ret = dir_read(fh, buffer_size, buffer);
+               ret = dir_read(fh, &bs, buffer);
        else
-               ret = file_read(fh, buffer_size, buffer);
+               ret = file_read(fh, &bs, buffer);
+       if (bs <= SIZE_MAX)
+               *buffer_size = bs;
+       else
+               *buffer_size = SIZE_MAX;
 
 error:
        return EFI_EXIT(ret);
 }
 
 static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
-               u64 *buffer_size, void *buffer)
+                                         efi_uintn_t *buffer_size,
+                                         void *buffer)
 {
        struct file_handle *fh = to_fh(file);
        efi_status_t ret = EFI_SUCCESS;
@@ -363,21 +378,27 @@ error:
 }
 
 static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
-               u64 *pos)
+                                          efi_uintn_t *pos)
 {
        struct file_handle *fh = to_fh(file);
+
        EFI_ENTRY("%p, %p", file, pos);
-       *pos = fh->offset;
-       return EFI_EXIT(EFI_SUCCESS);
+
+       if (fh->offset <= SIZE_MAX) {
+               *pos = fh->offset;
+               return EFI_EXIT(EFI_SUCCESS);
+       } else {
+               return EFI_EXIT(EFI_DEVICE_ERROR);
+       }
 }
 
 static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
-               u64 pos)
+               efi_uintn_t pos)
 {
        struct file_handle *fh = to_fh(file);
        efi_status_t ret = EFI_SUCCESS;
 
-       EFI_ENTRY("%p, %llu", file, pos);
+       EFI_ENTRY("%p, %zu", file, pos);
 
        if (fh->isdir) {
                if (pos != 0) {
@@ -411,7 +432,9 @@ error:
 }
 
 static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
-               efi_guid_t *info_type, u64 *buffer_size, void *buffer)
+                                           const efi_guid_t *info_type,
+                                           efi_uintn_t *buffer_size,
+                                           void *buffer)
 {
        struct file_handle *fh = to_fh(file);
        efi_status_t ret = EFI_SUCCESS;
@@ -452,6 +475,41 @@ static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
                        info->attribute |= EFI_FILE_DIRECTORY;
 
                ascii2unicode((u16 *)info->file_name, filename);
+       } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
+               struct efi_file_system_info *info = buffer;
+               disk_partition_t part;
+               efi_uintn_t required_size;
+               int r;
+
+               if (fh->fs->part >= 1)
+                       r = part_get_info(fh->fs->desc, fh->fs->part, &part);
+               else
+                       r = part_get_info_whole_disk(fh->fs->desc, &part);
+               if (r < 0) {
+                       ret = EFI_DEVICE_ERROR;
+                       goto error;
+               }
+               required_size = sizeof(info) + 2 *
+                               (strlen((const char *)part.name) + 1);
+               if (*buffer_size < required_size) {
+                       *buffer_size = required_size;
+                       ret = EFI_BUFFER_TOO_SMALL;
+                       goto error;
+               }
+
+               memset(info, 0, required_size);
+
+               info->size = required_size;
+               info->read_only = true;
+               info->volume_size = part.size * part.blksz;
+               info->free_space = 0;
+               info->block_size = part.blksz;
+               /*
+                * TODO: The volume label is not available in U-Boot.
+                * Use the partition name as substitute.
+                */
+               ascii2unicode((u16 *)info->volume_label,
+                             (const char *)part.name);
        } else {
                ret = EFI_UNSUPPORTED;
        }
@@ -461,9 +519,12 @@ error:
 }
 
 static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
-               efi_guid_t *info_type, u64 buffer_size, void *buffer)
+                                           const efi_guid_t *info_type,
+                                           efi_uintn_t buffer_size,
+                                           void *buffer)
 {
-       EFI_ENTRY("%p, %p, %llu, %p", file, info_type, buffer_size, buffer);
+       EFI_ENTRY("%p, %p, %zu, %p", file, info_type, buffer_size, buffer);
+
        return EFI_EXIT(EFI_UNSUPPORTED);
 }
 
index 3caddd5f8442cf97a96a7301e647a2e6581a8017..363ccbb78904a2c32bde7148dff5a3cfdc5632fc 100644 (file)
@@ -56,27 +56,166 @@ static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
        return EFI_EXIT(EFI_SUCCESS);
 }
 
-efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
-                           u32 operation, efi_uintn_t sx,
-                           efi_uintn_t sy, efi_uintn_t dx,
-                           efi_uintn_t dy, efi_uintn_t width,
-                           efi_uintn_t height, efi_uintn_t delta)
+static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid)
+{
+       struct efi_gop_pixel blt = {
+               .reserved = 0,
+       };
+
+       blt.blue  = (vid & 0x1f) << 3;
+       vid >>= 5;
+       blt.green = (vid & 0x3f) << 2;
+       vid >>= 6;
+       blt.red   = (vid & 0x1f) << 3;
+       return blt;
+}
+
+static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt)
+{
+       return (u16)(blt->red   >> 3) << 11 |
+              (u16)(blt->green >> 2) <<  5 |
+              (u16)(blt->blue  >> 3);
+}
+
+static __always_inline efi_status_t gop_blt_int(struct efi_gop *this,
+                                               struct efi_gop_pixel *bufferp,
+                                               u32 operation, efi_uintn_t sx,
+                                               efi_uintn_t sy, efi_uintn_t dx,
+                                               efi_uintn_t dy,
+                                               efi_uintn_t width,
+                                               efi_uintn_t height,
+                                               efi_uintn_t delta,
+                                               efi_uintn_t vid_bpp)
 {
        struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
-       int i, j, line_len16, line_len32;
-       void *fb;
+       efi_uintn_t i, j, linelen, slineoff = 0, dlineoff, swidth, dwidth;
+       u32 *fb32 = gopobj->fb;
+       u16 *fb16 = gopobj->fb;
+       struct efi_gop_pixel *buffer = __builtin_assume_aligned(bufferp, 4);
+
+       if (delta) {
+               /* Check for 4 byte alignment */
+               if (delta & 3)
+                       return EFI_INVALID_PARAMETER;
+               linelen = delta >> 2;
+       } else {
+               linelen = width;
+       }
 
-       EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
-                 buffer, operation, sx, sy, dx, dy, width, height, delta);
+       /* 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;
+       }
 
-       if (operation != EFI_BLT_BUFFER_TO_VIDEO)
-               return EFI_EXIT(EFI_INVALID_PARAMETER);
+       /* Check destination rectangle */
+       switch (operation) {
+       case EFI_BLT_VIDEO_FILL:
+       case EFI_BLT_BUFFER_TO_VIDEO:
+       case EFI_BLT_VIDEO_TO_VIDEO:
+               if (dx + width > gopobj->info.width ||
+                   dy + height > gopobj->info.height)
+                       return EFI_INVALID_PARAMETER;
+               break;
+       case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+               if (dx + width > linelen)
+                       return EFI_INVALID_PARAMETER;
+               break;
+       }
 
-       fb = gopobj->fb;
-       line_len16 = gopobj->info.width * sizeof(u16);
-       line_len32 = gopobj->info.width * sizeof(u32);
+       /* 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;
+       }
 
-       /* Copy the contents line by line */
+       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;
 }
index cac64ba9fec229f9f1f2fd9b9efb8e31ce7fe5db..d5fbba3138358e9827c5a3a58bcf650e268c21c5 100644 (file)
@@ -22,6 +22,76 @@ const efi_guid_t efi_simple_file_system_protocol_guid =
                EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
 const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
 
+static int machines[] = {
+#if defined(CONFIG_ARM64)
+       IMAGE_FILE_MACHINE_ARM64,
+#elif defined(CONFIG_ARM)
+       IMAGE_FILE_MACHINE_ARM,
+       IMAGE_FILE_MACHINE_THUMB,
+       IMAGE_FILE_MACHINE_ARMNT,
+#endif
+
+#if defined(CONFIG_X86_64)
+       IMAGE_FILE_MACHINE_AMD64,
+#elif defined(CONFIG_X86)
+       IMAGE_FILE_MACHINE_I386,
+#endif
+
+#if defined(CONFIG_CPU_RISCV_32)
+       IMAGE_FILE_MACHINE_RISCV32,
+#endif
+
+#if defined(CONFIG_CPU_RISCV_64)
+       IMAGE_FILE_MACHINE_RISCV64,
+#endif
+       0 };
+
+/*
+ * Print information about a loaded image.
+ *
+ * If the program counter is located within the image the offset to the base
+ * address is shown.
+ *
+ * @image:     loaded image
+ * @pc:                program counter (use NULL to suppress offset output)
+ * @return:    status code
+ */
+efi_status_t efi_print_image_info(struct efi_loaded_image *image, void *pc)
+{
+       if (!image)
+               return EFI_INVALID_PARAMETER;
+       printf("UEFI image");
+       printf(" [0x%p:0x%p]",
+              image->reloc_base, image->reloc_base + image->reloc_size - 1);
+       if (pc && pc >= image->reloc_base &&
+           pc < image->reloc_base + image->reloc_size)
+               printf(" pc=0x%zx", pc - image->reloc_base);
+       if (image->file_path)
+               printf(" '%pD'", image->file_path);
+       printf("\n");
+       return EFI_SUCCESS;
+}
+
+/*
+ * Print information about all loaded images.
+ *
+ * @pc:                program counter (use NULL to suppress offset output)
+ */
+void efi_print_image_infos(void *pc)
+{
+       struct efi_object *efiobj;
+       struct efi_handler *handler;
+
+       list_for_each_entry(efiobj, &efi_obj_list, link) {
+               list_for_each_entry(handler, &efiobj->protocols, link) {
+                       if (!guidcmp(handler->guid, &efi_guid_loaded_image)) {
+                               efi_print_image_info(
+                                       handler->protocol_interface, pc);
+                       }
+               }
+       }
+}
+
 static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
                        unsigned long rel_size, void *efi_reloc)
 {
@@ -126,14 +196,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
        void *entry;
        uint64_t image_size;
        unsigned long virt_size = 0;
-       bool can_run_nt64 = true;
-       bool can_run_nt32 = true;
-
-#if defined(CONFIG_ARM64)
-       can_run_nt32 = false;
-#elif defined(CONFIG_ARM)
-       can_run_nt64 = false;
-#endif
+       int supported = 0;
 
        dos = efi;
        if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
@@ -147,6 +210,18 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
                return NULL;
        }
 
+       for (i = 0; machines[i]; i++)
+               if (machines[i] == nt->FileHeader.Machine) {
+                       supported = 1;
+                       break;
+               }
+
+       if (!supported) {
+               printf("%s: Machine type 0x%04x is not supported\n",
+                      __func__, nt->FileHeader.Machine);
+               return NULL;
+       }
+
        /* Calculate upper virtual address boundary */
        num_sections = nt->FileHeader.NumberOfSections;
        sections = (void *)&nt->OptionalHeader +
@@ -159,8 +234,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
        }
 
        /* Read 32/64bit specific header bits */
-       if (can_run_nt64 &&
-           (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
+       if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
                IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
                IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
                image_size = opt->SizeOfImage;
@@ -175,8 +249,8 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
                entry = efi_reloc + opt->AddressOfEntryPoint;
                rel_size = opt->DataDirectory[rel_idx].Size;
                rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
-       } else if (can_run_nt32 &&
-                  (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
+               virt_size = ALIGN(virt_size, opt->SectionAlignment);
+       } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
                IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
                image_size = opt->SizeOfImage;
                efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
@@ -190,6 +264,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
                entry = efi_reloc + opt->AddressOfEntryPoint;
                rel_size = opt->DataDirectory[rel_idx].Size;
                rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+               virt_size = ALIGN(virt_size, opt->SectionAlignment);
        } else {
                printf("%s: Invalid optional header magic %x\n", __func__,
                       nt->OptionalHeader.Magic);
@@ -221,6 +296,8 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
        /* Populate the loaded image interface bits */
        loaded_image_info->image_base = efi;
        loaded_image_info->image_size = image_size;
+       loaded_image_info->reloc_base = efi_reloc;
+       loaded_image_info->reloc_size = virt_size;
 
        return entry;
 }
index ff0edf30ffb10dfde064bfc0a8cf3cc058afcb82..95f9ff0a14017ee9fb0f04045061837c6a41d5b8 100644 (file)
@@ -8,12 +8,11 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <inttypes.h>
 #include <malloc.h>
+#include <watchdog.h>
 #include <asm/global_data.h>
-#include <linux/libfdt_env.h>
 #include <linux/list_sort.h>
-#include <inttypes.h>
-#include <watchdog.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;
index 8c5d5b492cab03df847ea6a443c3919cbe592a5b..9afe76cdb31de77783e68338705f97b3f67a6686 100644 (file)
@@ -54,14 +54,46 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
        return EFI_EXIT(EFI_SUCCESS);
 }
 
+/*
+ * Initialize network adapter and allocate transmit and receive buffers.
+ *
+ * This function implements the Initialize service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this:      pointer to the protocol instance
+ * @extra_rx:  extra receive buffer to be allocated
+ * @extra_tx:  extra transmit buffer to be allocated
+ * @return:    status code
+ */
 static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
                                              ulong extra_rx, ulong extra_tx)
 {
+       int ret;
+       efi_status_t r = EFI_SUCCESS;
+
        EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
 
-       eth_init();
+       if (!this) {
+               r = EFI_INVALID_PARAMETER;
+               goto error;
+       }
 
-       return EFI_EXIT(EFI_SUCCESS);
+       /* Setup packet buffers */
+       net_init();
+       /* Disable hardware and put it into the reset state */
+       eth_halt();
+       /* Set current device according to environment variables */
+       eth_set_current();
+       /* Get hardware ready for send and receive operations */
+       ret = eth_init();
+       if (ret < 0) {
+               eth_halt();
+               r = EFI_DEVICE_ERROR;
+       }
+
+error:
+       return EFI_EXIT(r);
 }
 
 static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
@@ -280,20 +312,22 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event,
 }
 
 /* This gets called from do_bootefi_exec(). */
-int efi_net_register(void)
+efi_status_t efi_net_register(void)
 {
        struct efi_net_obj *netobj;
        efi_status_t r;
 
        if (!eth_get_dev()) {
                /* No eth device active, don't expose any */
-               return 0;
+               return EFI_SUCCESS;
        }
 
        /* We only expose the "active" eth device, so one is enough */
        netobj = calloc(1, sizeof(*netobj));
-       if (!netobj)
-               goto out_of_memory;
+       if (!netobj) {
+               printf("ERROR: Out of memory\n");
+               return EFI_OUT_OF_RESOURCES;
+       }
 
        /* Hook net up to the device list */
        efi_add_handle(&netobj->parent);
@@ -302,15 +336,15 @@ int efi_net_register(void)
        r = efi_add_protocol(netobj->parent.handle, &efi_net_guid,
                             &netobj->net);
        if (r != EFI_SUCCESS)
-               goto out_of_memory;
+               goto failure_to_add_protocol;
        r = efi_add_protocol(netobj->parent.handle, &efi_guid_device_path,
                             efi_dp_from_eth());
        if (r != EFI_SUCCESS)
-               goto out_of_memory;
+               goto failure_to_add_protocol;
        r = efi_add_protocol(netobj->parent.handle, &efi_pxe_guid,
                             &netobj->pxe);
        if (r != EFI_SUCCESS)
-               goto out_of_memory;
+               goto failure_to_add_protocol;
        netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
        netobj->net.start = efi_net_start;
        netobj->net.stop = efi_net_stop;
@@ -339,7 +373,7 @@ int efi_net_register(void)
         * Create WaitForPacket event.
         */
        r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
-                            efi_network_timer_notify, NULL,
+                            efi_network_timer_notify, NULL, NULL,
                             &wait_for_packet);
        if (r != EFI_SUCCESS) {
                printf("ERROR: Failed to register network event\n");
@@ -351,9 +385,11 @@ int efi_net_register(void)
         *
         * The notification function is used to check if a new network packet
         * has been received.
+        *
+        * iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL.
         */
-       r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
-                            efi_network_timer_notify, NULL,
+       r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
+                            efi_network_timer_notify, NULL, NULL,
                             &network_timer_event);
        if (r != EFI_SUCCESS) {
                printf("ERROR: Failed to register network event\n");
@@ -366,8 +402,8 @@ int efi_net_register(void)
                return r;
        }
 
-       return 0;
-out_of_memory:
-       printf("ERROR: Out of memory\n");
-       return 1;
+       return EFI_SUCCESS;
+failure_to_add_protocol:
+       printf("ERROR: Failure to add protocol\n");
+       return r;
 }
index ccb4fc6141b8d45a19675a7eb8d78f455de3ddc2..8558124c0a12d8dcb4d80c27d824747ab88b8e7e 100644 (file)
@@ -74,12 +74,24 @@ static void EFIAPI efi_reset_system_boottime(
                        efi_status_t reset_status,
                        unsigned long data_size, void *reset_data)
 {
+       struct efi_event *evt;
+
        EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
                  reset_data);
 
+       /* Notify reset */
+       list_for_each_entry(evt, &efi_events, link) {
+               if (evt->group &&
+                   !guidcmp(evt->group,
+                            &efi_guid_event_group_reset_system)) {
+                       efi_signal_event(evt, false);
+                       break;
+               }
+       }
        switch (reset_type) {
        case EFI_RESET_COLD:
        case EFI_RESET_WARM:
+       case EFI_RESET_PLATFORM_SPECIFIC:
                do_reset(NULL, 0, 0, NULL);
                break;
        case EFI_RESET_SHUTDOWN:
@@ -134,8 +146,9 @@ void __weak __efi_runtime EFIAPI efi_reset_system(
        while (1) { }
 }
 
-void __weak efi_reset_system_init(void)
+efi_status_t __weak efi_reset_system_init(void)
 {
+       return EFI_SUCCESS;
 }
 
 efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
@@ -146,8 +159,9 @@ efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
        return EFI_DEVICE_ERROR;
 }
 
-void __weak efi_get_time_init(void)
+efi_status_t __weak efi_get_time_init(void)
 {
+       return EFI_SUCCESS;
 }
 
 struct efi_runtime_detach_list_struct {
@@ -332,18 +346,26 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
        return EFI_EXIT(EFI_INVALID_PARAMETER);
 }
 
-void efi_add_runtime_mmio(void *mmio_ptr, u64 len)
+efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
 {
        struct efi_runtime_mmio_list *newmmio;
-
        u64 pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
-       efi_add_memory_map(*(uintptr_t *)mmio_ptr, pages, EFI_MMAP_IO, false);
+       uint64_t addr = *(uintptr_t *)mmio_ptr;
+       uint64_t retaddr;
+
+       retaddr = efi_add_memory_map(addr, pages, EFI_MMAP_IO, false);
+       if (retaddr != addr)
+               return EFI_OUT_OF_RESOURCES;
 
        newmmio = calloc(1, sizeof(*newmmio));
+       if (!newmmio)
+               return EFI_OUT_OF_RESOURCES;
        newmmio->ptr = mmio_ptr;
        newmmio->paddr = *(uintptr_t *)mmio_ptr;
        newmmio->len = len;
        list_add_tail(&newmmio->link, &efi_runtime_mmio);
+
+       return EFI_SUCCESS;
 }
 
 /*
index ac412e7362a95e8fb62eb86cf67d53a38df6bc12..62e96979021d51c5b8e9488a107411a40f3ab662 100644 (file)
 
 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);
 }
index 35a45dedf849ce752a0bce6cdb21c98b4d6dc3e1..d12e51da0a21e89e7687119f050867f52975f654 100644 (file)
@@ -59,7 +59,7 @@ efi_status_t efi_set_watchdog(unsigned long timeout)
  *
  * This function is called by efi_init_obj_list()
  */
-int efi_watchdog_register(void)
+efi_status_t efi_watchdog_register(void)
 {
        efi_status_t r;
 
@@ -67,7 +67,7 @@ int efi_watchdog_register(void)
         * Create a timer event.
         */
        r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
-                            efi_watchdog_timer_notify, NULL,
+                            efi_watchdog_timer_notify, NULL, NULL,
                             &watchdog_timer_event);
        if (r != EFI_SUCCESS) {
                printf("ERROR: Failed to register watchdog event\n");
@@ -85,5 +85,5 @@ int efi_watchdog_register(void)
                printf("ERROR: Failed to set watchdog timer\n");
                return r;
        }
-       return 0;
+       return EFI_SUCCESS;
 }
index 1ec0179226328f4f31b6fc44645699095153fab2..6c539ba2049853f786776b42caef0555bd074124 100644 (file)
@@ -46,9 +46,27 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,
        struct efi_loaded_image *loaded_image;
        efi_status_t ret;
        efi_uintn_t i;
+       u16 rev[] = L"0.0.0";
 
        con_out->output_string(con_out, L"Hello, world!\n");
 
+       /* Print the revision number */
+       rev[0] = (systable->hdr.revision >> 16) + '0';
+       rev[4] = systable->hdr.revision & 0xffff;
+       for (; rev[4] >= 10;) {
+               rev[4] -= 10;
+               ++rev[2];
+       }
+       /* Third digit is only to be shown if non-zero */
+       if (rev[4])
+               rev[4] += '0';
+       else
+               rev[3] = 0;
+
+       con_out->output_string(con_out, L"Running on UEFI ");
+       con_out->output_string(con_out, rev);
+       con_out->output_string(con_out, L"\n");
+
        /* Get the loaded image protocol */
        ret = boottime->handle_protocol(handle, &loaded_image_guid,
                                        (void **)&loaded_image);
index c4bdbdf6c05a84b7612a1666c4fb99d362238f43..31b444fc8b18dcad6aca77c2012daf67e49714be 100644 (file)
@@ -14,14 +14,18 @@ CFLAGS_REMOVE_efi_selftest_miniapp_return.o := $(CFLAGS_NON_EFI) -Os
 
 obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
 efi_selftest.o \
+efi_selftest_bitblt.o \
 efi_selftest_controllers.o \
 efi_selftest_console.o \
 efi_selftest_devicepath.o \
 efi_selftest_events.o \
+efi_selftest_event_groups.o \
 efi_selftest_exitbootservices.o \
+efi_selftest_fdt.o \
 efi_selftest_gop.o \
 efi_selftest_manageprotocols.o \
 efi_selftest_snp.o \
+efi_selftest_textinput.o \
 efi_selftest_textoutput.o \
 efi_selftest_tpl.o \
 efi_selftest_util.o \
diff --git a/lib/efi_selftest/efi_selftest_bitblt.c b/lib/efi_selftest/efi_selftest_bitblt.c
new file mode 100644 (file)
index 0000000..0fb76cc
--- /dev/null
@@ -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,
+};
index 9e4b93d9a618e0ddd77cd49c154ced9c09dc3301..a8979ed56bd89eb611835c0fbbc99c5f764770b5 100644 (file)
@@ -29,6 +29,7 @@ static const efi_guid_t block_io_protocol_guid = BLOCK_IO_GUID;
 static const efi_guid_t guid_device_path = DEVICE_PATH_GUID;
 static const efi_guid_t guid_simple_file_system_protocol =
                                        EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
 static efi_guid_t guid_vendor =
        EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
                 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xb7, 0xb8);
@@ -302,7 +303,11 @@ static int execute(void)
        struct efi_device_path *dp_partition;
        struct efi_simple_file_system_protocol *file_system;
        struct efi_file_handle *root, *file;
-       u64 buf_size;
+       struct {
+               struct efi_file_system_info info;
+               u16 label[12];
+       } system_info;
+       efi_uintn_t buf_size;
        char buf[16] __aligned(ARCH_DMA_MINALIGN);
 
        ret = boottime->connect_controller(disk_handle, NULL, NULL, 1);
@@ -356,6 +361,23 @@ static int execute(void)
                efi_st_error("Failed to open volume\n");
                return EFI_ST_FAILURE;
        }
+       buf_size = sizeof(system_info);
+       ret = root->getinfo(root, &guid_file_system_info, &buf_size,
+                           &system_info);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Failed to get file system info\n");
+               return EFI_ST_FAILURE;
+       }
+       if (system_info.info.block_size != 512) {
+               efi_st_error("Wrong block size %u, expected 512\n",
+                            system_info.info.block_size);
+               return EFI_ST_FAILURE;
+       }
+       if (efi_st_strcmp_16_8(system_info.info.volume_label, "U-BOOT TEST")) {
+               efi_st_todo(
+                       "Wrong volume label '%ps', expected 'U-BOOT TEST'\n",
+                       system_info.info.volume_label);
+       }
        ret = root->open(root, &file, (s16 *)L"hello.txt", EFI_FILE_MODE_READ,
                         0);
        if (ret != EFI_SUCCESS) {
index 4775dace706f5e116c8f515951d756bfb51c3b88..9c741ce136c4cc0d47cb77c3f0ffc4bef0349494 100644 (file)
@@ -3,21 +3,21 @@
  *
  *  Generated with tools/file2include
  *
- *  SPDX-License-Identifier:     GPL-2.0+
+ *  SPDX-License-Identifier:   GPL-2.0+
  */
 
 #define EFI_ST_DISK_IMG { 0x00010000, { \
-       {0x000001b8, "\x94\x37\x69\xfc\x00\x00\x00\x00"}, /* .7i..... */ \
-       {0x000001c0, "\x02\x00\x83\x02\x02\x00\x01\x00"}, /* ........ */ \
+       {0x000001b8, "\x21\x5d\x53\xd1\x00\x00\x00\x00"}, /* !]S..... */ \
+       {0x000001c0, "\x02\x00\x01\x02\x02\x00\x01\x00"}, /* ........ */ \
        {0x000001c8, "\x00\x00\x7f\x00\x00\x00\x00\x00"}, /* ........ */ \
        {0x000001f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
        {0x00000200, "\xeb\x3c\x90\x6d\x6b\x66\x73\x2e"}, /* .<.mkfs. */ \
        {0x00000208, "\x66\x61\x74\x00\x02\x04\x01\x00"}, /* fat..... */ \
        {0x00000210, "\x02\x00\x02\x7f\x00\xf8\x01\x00"}, /* ........ */ \
        {0x00000218, "\x20\x00\x40\x00\x00\x00\x00\x00"}, /*  .@..... */ \
-       {0x00000220, "\x00\x00\x00\x00\x80\x00\x29\x86"}, /* ......). */ \
-       {0x00000228, "\xe8\x82\x80\x4e\x4f\x20\x4e\x41"}, /* ...NO NA */ \
-       {0x00000230, "\x4d\x45\x20\x20\x20\x20\x46\x41"}, /* ME    FA */ \
+       {0x00000220, "\x00\x00\x00\x00\x80\x00\x29\xc4"}, /* ......). */ \
+       {0x00000228, "\xc4\x88\x11\x55\x2d\x42\x4f\x4f"}, /* ...U-BOO */ \
+       {0x00000230, "\x54\x20\x54\x45\x53\x54\x46\x41"}, /* T TESTFA */ \
        {0x00000238, "\x54\x31\x32\x20\x20\x20\x0e\x1f"}, /* T12   .. */ \
        {0x00000240, "\xbe\x5b\x7c\xac\x22\xc0\x74\x0b"}, /* .[|.".t. */ \
        {0x00000248, "\x56\xb4\x0e\xbb\x07\x00\xcd\x10"}, /* V....... */ \
        {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} } }
diff --git a/lib/efi_selftest/efi_selftest_event_groups.c b/lib/efi_selftest/efi_selftest_event_groups.c
new file mode 100644 (file)
index 0000000..79e4ea1
--- /dev/null
@@ -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,
+};
diff --git a/lib/efi_selftest/efi_selftest_fdt.c b/lib/efi_selftest/efi_selftest_fdt.c
new file mode 100644 (file)
index 0000000..e5a8d6a
--- /dev/null
@@ -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,
+};
diff --git a/lib/efi_selftest/efi_selftest_textinput.c b/lib/efi_selftest/efi_selftest_textinput.c
new file mode 100644 (file)
index 0000000..c890ff8
--- /dev/null
@@ -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,
+};
index 66b799bed6695a381c60b1c15a10d2f095f5a08b..b1ef6bd5811a07ddf180fa564f0608c96d173105 100644 (file)
@@ -24,6 +24,20 @@ def test_efi_selftest(u_boot_console):
                raise Exception('Reset failed during the EFI selftest')
        u_boot_console.restart_uboot();
 
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+@pytest.mark.buildconfigspec('of_control')
+def test_efi_selftest_device_tree(u_boot_console):
+       u_boot_console.run_command(cmd='setenv efi_selftest list')
+       output = u_boot_console.run_command('bootefi selftest')
+       assert '\'device tree\'' in output
+       u_boot_console.run_command(cmd='setenv efi_selftest device tree')
+       u_boot_console.run_command(cmd='setenv -f serial# Testing DT')
+       u_boot_console.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False)
+       m = u_boot_console.p.expect(['serial-number: Testing DT', 'U-Boot'])
+       if m != 0:
+               raise Exception('Reset failed in \'device tree\' test')
+       u_boot_console.restart_uboot();
+
 @pytest.mark.buildconfigspec('cmd_bootefi_selftest')
 def test_efi_selftest_watchdog_reboot(u_boot_console):
        u_boot_console.run_command(cmd='setenv efi_selftest list')