]> git.ipfire.org Git - people/ms/u-boot.git/commitdiff
Merge git://git.denx.de/u-boot-rockchip
authorTom Rini <trini@konsulko.com>
Sun, 1 Oct 2017 17:05:53 +0000 (13:05 -0400)
committerTom Rini <trini@konsulko.com>
Sun, 1 Oct 2017 17:05:53 +0000 (13:05 -0400)
38 files changed:
MAINTAINERS
cmd/Kconfig
cmd/bootefi.c
disk/part_dos.c
disk/part_efi.c
include/asm-generic/sections.h
include/blk.h
include/config_distro_bootcmd.h
include/efi.h
include/efi_api.h
include/efi_loader.h
include/efi_selftest.h [new file with mode: 0644]
include/part.h
include/part_efi.h
include/pe.h
lib/Makefile
lib/efi_loader/Makefile
lib/efi_loader/efi_bootmgr.c [new file with mode: 0644]
lib/efi_loader/efi_boottime.c
lib/efi_loader/efi_console.c
lib/efi_loader/efi_device_path.c [new file with mode: 0644]
lib/efi_loader/efi_device_path_to_text.c
lib/efi_loader/efi_disk.c
lib/efi_loader/efi_file.c [new file with mode: 0644]
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_variable.c [new file with mode: 0644]
lib/efi_selftest/Kconfig [new file with mode: 0644]
lib/efi_selftest/Makefile [new file with mode: 0644]
lib/efi_selftest/efi_selftest.c [new file with mode: 0644]
lib/efi_selftest/efi_selftest_console.c [new file with mode: 0644]
lib/efi_selftest/efi_selftest_events.c [new file with mode: 0644]
lib/efi_selftest/efi_selftest_exitbootservices.c [new file with mode: 0644]
lib/efi_selftest/efi_selftest_tpl.c [new file with mode: 0644]
scripts/Makefile.lib
test/py/tests/test_efi_selftest.py [new file with mode: 0644]

index a24e453617855287dbac1f37f31eb2357f607317..b167b028ecf1c6cc0c01d719f83ca1d3299fbcce 100644 (file)
@@ -259,8 +259,9 @@ EFI PAYLOAD
 M:     Alexander Graf <agraf@suse.de>
 S:     Maintained
 T:     git git://github.com/agraf/u-boot.git
-F:     include/efi_loader.h
-F:     lib/efi_loader/
+F:     include/efi*
+F:     lib/efi*
+F:     test/py/tests/test_efi*
 F:     cmd/bootefi.c
 
 FLATTENED DEVICE TREE
index d6d130edfa917c7f6af938be710e0f4e581d8276..3ef9b16b082169189facfadd3ffbeba27d4b21ae 100644 (file)
@@ -222,6 +222,8 @@ config CMD_BOOTEFI_HELLO
          for testing that EFI is working at a basic level, and for bringing
          up EFI support on a new architecture.
 
+source lib/efi_selftest/Kconfig
+
 config CMD_BOOTMENU
        bool "bootmenu"
        select MENU
index 3196d86040729487186c241e9b7c36d597bbb6f2..b7087e3da874e7f14745dc63dfbf62458bf80795 100644 (file)
@@ -22,97 +22,14 @@ DECLARE_GLOBAL_DATA_PTR;
 
 static uint8_t efi_obj_list_initalized;
 
-/*
- * When booting using the "bootefi" command, we don't know which
- * physical device the file came from. So we create a pseudo-device
- * called "bootefi" with the device path /bootefi.
- *
- * In addition to the originating device we also declare the file path
- * of "bootefi" based loads to be /bootefi.
- */
-static struct efi_device_path_file_path bootefi_image_path[] = {
-       {
-               .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
-               .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
-               .dp.length = sizeof(bootefi_image_path[0]),
-               .str = { 'b','o','o','t','e','f','i' },
-       }, {
-               .dp.type = DEVICE_PATH_TYPE_END,
-               .dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
-               .dp.length = sizeof(bootefi_image_path[0]),
-       }
-};
-
-static struct efi_device_path_file_path bootefi_device_path[] = {
-       {
-               .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
-               .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
-               .dp.length = sizeof(bootefi_image_path[0]),
-               .str = { 'b','o','o','t','e','f','i' },
-       }, {
-               .dp.type = DEVICE_PATH_TYPE_END,
-               .dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
-               .dp.length = sizeof(bootefi_image_path[0]),
-       }
-};
-
-/* The EFI loaded_image interface for the image executed via "bootefi" */
-static struct efi_loaded_image loaded_image_info = {
-       .device_handle = bootefi_device_path,
-       .file_path = bootefi_image_path,
-};
-
-/* The EFI object struct for the image executed via "bootefi" */
-static struct efi_object loaded_image_info_obj = {
-       .handle = &loaded_image_info,
-       .protocols = {
-               {
-                       /*
-                        * When asking for the loaded_image interface, just
-                        * return handle which points to loaded_image_info
-                        */
-                       .guid = &efi_guid_loaded_image,
-                       .protocol_interface = &loaded_image_info,
-               },
-               {
-                       /*
-                        * When asking for the device path interface, return
-                        * bootefi_device_path
-                        */
-                       .guid = &efi_guid_device_path,
-                       .protocol_interface = bootefi_device_path,
-               },
-               {
-                       .guid = &efi_guid_console_control,
-                       .protocol_interface = (void *) &efi_console_control
-               },
-               {
-                       .guid = &efi_guid_device_path_to_text_protocol,
-                       .protocol_interface = (void *) &efi_device_path_to_text
-               },
-       },
-};
-
-/* The EFI object struct for the device the "bootefi" image was loaded from */
-static struct efi_object bootefi_device_obj = {
-       .handle = bootefi_device_path,
-       .protocols = {
-               {
-                       /* When asking for the device path interface, return
-                        * bootefi_device_path */
-                       .guid = &efi_guid_device_path,
-                       .protocol_interface = bootefi_device_path
-               }
-       },
-};
+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_obj_list_initalized = 1;
 
-       list_add_tail(&loaded_image_info_obj.link, &efi_obj_list);
-       list_add_tail(&bootefi_device_obj.link, &efi_obj_list);
        efi_console_register();
 #ifdef CONFIG_PARTITIONS
        efi_disk_register();
@@ -121,13 +38,7 @@ static void efi_init_obj_list(void)
        efi_gop_register();
 #endif
 #ifdef CONFIG_NET
-       void *nethandle = loaded_image_info.device_handle;
-       efi_net_register(&nethandle);
-
-       if (!memcmp(bootefi_device_path[0].str, "N\0e\0t", 6))
-               loaded_image_info.device_handle = nethandle;
-       else
-               loaded_image_info.device_handle = bootefi_device_path;
+       efi_net_register();
 #endif
 #ifdef CONFIG_GENERATE_SMBIOS_TABLE
        efi_smbios_register();
@@ -210,14 +121,27 @@ static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)(
  * 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 unsigned long do_bootefi_exec(void *efi, void *fdt)
+static unsigned long do_bootefi_exec(void *efi, void *fdt,
+                                    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 = {};
+       ulong ret;
+
        ulong (*entry)(void *image_handle, struct efi_system_table *st)
                asmlinkage;
        ulong fdt_pages, fdt_size, fdt_start, fdt_end;
        const efi_guid_t fdt_guid = EFI_FDT_GUID;
        bootm_headers_t img = { 0 };
 
+       /* 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);
+
        /*
         * 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
@@ -252,18 +176,21 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt)
 
        /* Load the EFI payload */
        entry = efi_load_pe(efi, &loaded_image_info);
-       if (!entry)
-               return -ENOENT;
+       if (!entry) {
+               ret = -ENOENT;
+               goto exit;
+       }
 
-       /* Initialize and populate EFI object list */
-       if (!efi_obj_list_initalized)
-               efi_init_obj_list();
+       /* we don't support much: */
+       env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported",
+               "{ro,boot}(blob)0000000000000000");
 
        /* Call our payload! */
        debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
 
        if (setjmp(&loaded_image_info.exit_jmp)) {
-               return loaded_image_info.exit_status;
+               ret = loaded_image_info.exit_status;
+               goto exit;
        }
 
 #ifdef CONFIG_ARM64
@@ -282,9 +209,45 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt)
        }
 #endif
 
-       return efi_do_enter(&loaded_image_info, &systab, entry);
+       ret = efi_do_enter(&loaded_image_info, &systab, entry);
+
+exit:
+       /* image has returned, loaded-image obj goes *poof*: */
+       list_del(&loaded_image_info_obj.link);
+
+       return ret;
 }
 
+static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
+{
+       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
+        */
+       efi_save_gd();
+
+       addr = efi_bootmgr_load(&device_path, &file_path);
+       if (!addr)
+               return 1;
+
+       printf("## Starting EFI application at %p ...\n", addr);
+       r = do_bootefi_exec(addr, (void *)fdt_addr, device_path, file_path);
+       printf("## Application terminated, r = %lu\n",
+              r & ~EFI_ERROR_MASK);
+
+       if (r != EFI_SUCCESS)
+               return 1;
+
+       return 0;
+}
 
 /* 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[])
@@ -297,13 +260,44 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                return CMD_RET_USAGE;
 #ifdef CONFIG_CMD_BOOTEFI_HELLO
        if (!strcmp(argv[1], "hello")) {
-               ulong size = __efi_hello_world_end - __efi_hello_world_begin;
-
-               addr = CONFIG_SYS_LOAD_ADDR;
-               memcpy((char *)addr, __efi_hello_world_begin, size);
+               ulong size = __efi_helloworld_end - __efi_helloworld_begin;
+
+               saddr = env_get("loadaddr");
+               if (saddr)
+                       addr = simple_strtoul(saddr, NULL, 16);
+               else
+                       addr = CONFIG_SYS_LOAD_ADDR;
+               memcpy((char *)addr, __efi_helloworld_begin, size);
        } else
 #endif
-       {
+#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
+       if (!strcmp(argv[1], "selftest")) {
+               struct efi_loaded_image loaded_image_info = {};
+               struct efi_object loaded_image_info_obj = {};
+
+               efi_setup_loaded_image(&loaded_image_info,
+                                      &loaded_image_info_obj,
+                                      bootefi_device_path, bootefi_image_path);
+               /*
+                * 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
+                */
+               efi_save_gd();
+               /* Initialize and populate EFI object list */
+               if (!efi_obj_list_initalized)
+                       efi_init_obj_list();
+               return efi_selftest(&loaded_image_info, &systab);
+       } 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);
+       } else {
                saddr = argv[1];
 
                addr = simple_strtoul(saddr, NULL, 16);
@@ -315,7 +309,8 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
        }
 
        printf("## Starting EFI application at %08lx ...\n", addr);
-       r = do_bootefi_exec((void *)addr, (void*)fdt_addr);
+       r = do_bootefi_exec((void *)addr, (void *)fdt_addr,
+                           bootefi_device_path, bootefi_image_path);
        printf("## Application terminated, r = %lu\n",
               r & ~EFI_ERROR_MASK);
 
@@ -332,10 +327,18 @@ static char bootefi_help_text[] =
        "    If specified, the device tree located at <fdt address> gets\n"
        "    exposed as EFI configuration table.\n"
 #ifdef CONFIG_CMD_BOOTEFI_HELLO
-       "hello\n"
-       "  - boot a sample Hello World application stored within U-Boot"
+       "bootefi hello\n"
+       "  - boot a sample Hello World application stored within U-Boot\n"
+#endif
+#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
+       "bootefi selftest\n"
+       "  - boot an EFI selftest application stored within U-Boot\n"
 #endif
-       ;
+       "bootmgr [fdt addr]\n"
+       "  - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
+       "\n"
+       "    If specified, the device tree located at <fdt address> gets\n"
+       "    exposed as EFI configuration table.\n";
 #endif
 
 U_BOOT_CMD(
@@ -344,58 +347,47 @@ U_BOOT_CMD(
        bootefi_help_text
 );
 
-void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
+static int parse_partnum(const char *devnr)
 {
-       __maybe_unused struct blk_desc *desc;
-       char devname[32] = { 0 }; /* dp->str is u16[32] long */
-       char *colon, *s;
-
-#if defined(CONFIG_BLK) || CONFIG_IS_ENABLED(ISO_PARTITION)
-       desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
-#endif
-
-#ifdef CONFIG_BLK
-       if (desc) {
-               snprintf(devname, sizeof(devname), "%s", desc->bdev->name);
-       } else
-#endif
-
-       {
-               /* Assemble the condensed device name we use in efi_disk.c */
-               snprintf(devname, sizeof(devname), "%s%s", dev, 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 */
+       char *s;
 
-       colon = strchr(devname, ':');
+       if (strcmp(dev, "Net")) {
+               struct blk_desc *desc;
+               int part;
 
-#if CONFIG_IS_ENABLED(ISO_PARTITION)
-       /* For ISOs we create partition block devices */
-       if (desc && (desc->type != DEV_TYPE_UNKNOWN) &&
-           (desc->part_type == PART_TYPE_ISO)) {
-               if (!colon)
-                       snprintf(devname, sizeof(devname), "%s:1", devname);
+               desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
+               part = parse_partnum(devnr);
 
-               colon = NULL;
-       }
+               bootefi_device_path = efi_dp_from_part(desc, part);
+       } else {
+#ifdef CONFIG_NET
+               bootefi_device_path = efi_dp_from_eth();
 #endif
+       }
 
-       if (colon)
-               *colon = '\0';
-
-       /* Patch bootefi_device_path to the target device */
-       memset(bootefi_device_path[0].str, 0, sizeof(bootefi_device_path[0].str));
-       ascii2unicode(bootefi_device_path[0].str, devname);
+       if (!path)
+               return;
 
-       /* Patch bootefi_image_path to the target file path */
-       memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str));
        if (strcmp(dev, "Net")) {
                /* Add leading / to fs paths, because they're absolute */
-               snprintf(devname, sizeof(devname), "/%s", path);
+               snprintf(filename, sizeof(filename), "/%s", path);
        } else {
-               snprintf(devname, sizeof(devname), "%s", path);
+               snprintf(filename, sizeof(filename), "%s", path);
        }
        /* DOS style file path: */
-       s = devname;
+       s = filename;
        while ((s = strchr(s, '/')))
                *s++ = '\\';
-       ascii2unicode(bootefi_image_path[0].str, devname);
+       bootefi_image_path = efi_dp_from_file(NULL, 0, filename);
 }
index 7ede15ec261315e6922fdfd6298bf0f17f5220a4..1a36be0446ac06d30ce23aa4dfedfd3fa16a6827 100644 (file)
@@ -44,7 +44,7 @@ static inline int is_extended(int part_type)
 
 static inline int is_bootable(dos_partition_t *p)
 {
-       return p->boot_ind == 0x80;
+       return (p->sys_ind == 0xef) || (p->boot_ind == 0x80);
 }
 
 static void print_one_part(dos_partition_t *p, lbaint_t ext_part_sector,
@@ -89,14 +89,20 @@ static int test_block_type(unsigned char *buffer)
 
 static int part_test_dos(struct blk_desc *dev_desc)
 {
-       ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
+       ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, dev_desc->blksz);
 
-       if (blk_dread(dev_desc, 0, 1, (ulong *)buffer) != 1)
+       if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1)
                return -1;
 
-       if (test_block_type(buffer) != DOS_MBR)
+       if (test_block_type((unsigned char *)mbr) != DOS_MBR)
                return -1;
 
+       if (dev_desc->sig_type == SIG_TYPE_NONE &&
+           mbr->unique_mbr_signature != 0) {
+               dev_desc->sig_type = SIG_TYPE_MBR;
+               dev_desc->mbr_sig = mbr->unique_mbr_signature;
+       }
+
        return 0;
 }
 
index 2973d52f6abb278756a814057e3b77d7c08945ca..208bb14ee88e2e5115134070b41feac72dce1b02 100644 (file)
@@ -923,11 +923,19 @@ static int is_pmbr_valid(legacy_mbr * mbr)
 static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba,
                        gpt_header *pgpt_head, gpt_entry **pgpt_pte)
 {
+       ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, dev_desc->blksz);
+
        if (!dev_desc || !pgpt_head) {
                printf("%s: Invalid Argument(s)\n", __func__);
                return 0;
        }
 
+       /* Read MBR Header from device */
+       if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1) {
+               printf("*** ERROR: Can't read MBR header ***\n");
+               return 0;
+       }
+
        /* Read GPT Header from device */
        if (blk_dread(dev_desc, (lbaint_t)lba, 1, pgpt_head) != 1) {
                printf("*** ERROR: Can't read GPT header ***\n");
@@ -937,6 +945,18 @@ static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba,
        if (validate_gpt_header(pgpt_head, (lbaint_t)lba, dev_desc->lba))
                return 0;
 
+       if (dev_desc->sig_type == SIG_TYPE_NONE) {
+               efi_guid_t empty = {};
+               if (memcmp(&pgpt_head->disk_guid, &empty, sizeof(empty))) {
+                       dev_desc->sig_type = SIG_TYPE_GUID;
+                       memcpy(&dev_desc->guid_sig, &pgpt_head->disk_guid,
+                             sizeof(empty));
+               } else if (mbr->unique_mbr_signature != 0) {
+                       dev_desc->sig_type = SIG_TYPE_MBR;
+                       dev_desc->mbr_sig = mbr->unique_mbr_signature;
+               }
+       }
+
        /* Read and allocate Partition Table Entries */
        *pgpt_pte = alloc_read_gpt_entries(dev_desc, pgpt_head);
        if (*pgpt_pte == NULL) {
index daf021b647e6fdfa6d6baf091eaad707f58b55bb..b6535705a52003a5f047ec2d0aceb3af62abcc47 100644 (file)
@@ -22,8 +22,8 @@ extern char __kprobes_text_start[], __kprobes_text_end[];
 extern char __entry_text_start[], __entry_text_end[];
 extern char __initdata_begin[], __initdata_end[];
 extern char __start_rodata[], __end_rodata[];
-extern char __efi_hello_world_begin[];
-extern char __efi_hello_world_end[];
+extern char __efi_helloworld_begin[];
+extern char __efi_helloworld_end[];
 
 /* Start and end of .ctors section - used for constructor calls. */
 extern char __ctors_start[], __ctors_end[];
index 1965812a9d5a39f0148ce133a895a69b4058d07b..41b4d7efa82b3f88994b95a61071310ae51462fe 100644 (file)
@@ -8,6 +8,8 @@
 #ifndef BLK_H
 #define BLK_H
 
+#include <efi.h>
+
 #ifdef CONFIG_SYS_64BIT_LBA
 typedef uint64_t lbaint_t;
 #define LBAFlength "ll"
@@ -40,6 +42,17 @@ enum if_type {
 #define BLK_PRD_SIZE           20
 #define BLK_REV_SIZE           8
 
+/*
+ * Identifies the partition table type (ie. MBR vs GPT GUID) signature
+ */
+enum sig_type {
+       SIG_TYPE_NONE,
+       SIG_TYPE_MBR,
+       SIG_TYPE_GUID,
+
+       SIG_TYPE_COUNT                  /* Number of signature types */
+};
+
 /*
  * With driver model (CONFIG_BLK) this is uclass platform data, accessible
  * with dev_get_uclass_platdata(dev)
@@ -67,6 +80,11 @@ struct blk_desc {
        char            vendor[BLK_VEN_SIZE + 1]; /* device vendor string */
        char            product[BLK_PRD_SIZE + 1]; /* device product number */
        char            revision[BLK_REV_SIZE + 1]; /* firmware revision */
+       enum sig_type   sig_type;       /* Partition table signature type */
+       union {
+               uint32_t mbr_sig;       /* MBR integer signature */
+               efi_guid_t guid_sig;    /* GPT GUID Signature */
+       };
 #if CONFIG_IS_ENABLED(BLK)
        /*
         * For now we have a few functions which take struct blk_desc as a
index 9ed6b9892cd4a1aca02119319c6eb080ec241208..e0d0034ed344df59391f26cd58f79454b28afa1a 100644 (file)
 
 #define BOOTENV_SHARED_EFI                                                \
        "boot_efi_binary="                                                \
+               "if fdt addr ${fdt_addr_r}; then "                        \
+                       "bootefi bootmgr ${fdt_addr_r};"                  \
+               "else "                                                   \
+                       "bootefi bootmgr ${fdtcontroladdr};"              \
+               "fi;"                                                     \
                "load ${devtype} ${devnum}:${distro_bootpart} "           \
                        "${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; "      \
                "if fdt addr ${fdt_addr_r}; then "                        \
index 02b78b31b129928446ff1f71297b5d2b8e7e2eb3..dc8edc8743aca2f1b30f967c268c63ee616dbaa2 100644 (file)
 
 struct efi_device_path;
 
+typedef struct {
+       u8 b[16];
+} efi_guid_t;
+
 #define EFI_BITS_PER_LONG      BITS_PER_LONG
 
 /*
@@ -77,6 +81,8 @@ struct efi_device_path;
 #define EFI_IP_ADDRESS_CONFLICT                (EFI_ERROR_MASK | 34)
 #define EFI_HTTP_ERROR                 (EFI_ERROR_MASK | 35)
 
+#define EFI_WARN_DELETE_FAILURE        2
+
 typedef unsigned long efi_status_t;
 typedef u64 efi_physical_addr_t;
 typedef u64 efi_virtual_addr_t;
@@ -116,7 +122,7 @@ enum efi_mem_type {
        /* The code portions of a loaded Boot Services Driver */
        EFI_BOOT_SERVICES_CODE,
        /*
-        * The data portions of a loaded Boot Serves Driver and
+        * The data portions of a loaded Boot Services Driver and
         * the default data allocation type used by a Boot Services
         * Driver to allocate pool memory.
         */
@@ -318,6 +324,25 @@ extern char image_base[];
 /* Start and end of U-Boot image (for payload) */
 extern char _binary_u_boot_bin_start[], _binary_u_boot_bin_end[];
 
+/*
+ * Variable Attributes
+ */
+#define EFI_VARIABLE_NON_VOLATILE       0x0000000000000001
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
+#define EFI_VARIABLE_RUNTIME_ACCESS     0x0000000000000004
+#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x0000000000000008
+#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x0000000000000010
+#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x0000000000000020
+#define EFI_VARIABLE_APPEND_WRITE      0x0000000000000040
+
+#define EFI_VARIABLE_MASK      (EFI_VARIABLE_NON_VOLATILE | \
+                               EFI_VARIABLE_BOOTSERVICE_ACCESS | \
+                               EFI_VARIABLE_RUNTIME_ACCESS | \
+                               EFI_VARIABLE_HARDWARE_ERROR_RECORD | \
+                               EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
+                               EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \
+                               EFI_VARIABLE_APPEND_WRITE)
+
 /**
  * efi_get_sys_table() - Get access to the main EFI system table
  *
index ec1b321e8e70bc1158fe6503bfaf9f0afc94bcb0..c3b9032a48d2919d6c0cf5a673bc9319f6a5ce1f 100644 (file)
@@ -29,6 +29,8 @@ enum efi_timer_delay {
 };
 
 #define UINTN size_t
+typedef long INTN;
+typedef uint16_t *efi_string_t;
 
 #define EVT_TIMER                              0x80000000
 #define EVT_RUNTIME                            0x40000000
@@ -211,6 +213,10 @@ struct efi_runtime_services {
        EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
 
+#define EFI_GLOBAL_VARIABLE_GUID \
+       EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
+                0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
+
 #define LOADED_IMAGE_PROTOCOL_GUID \
        EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, \
                 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
@@ -284,28 +290,93 @@ struct efi_device_path {
        u8 type;
        u8 sub_type;
        u16 length;
-};
+} __packed;
 
 struct efi_mac_addr {
        u8 addr[32];
-};
+} __packed;
+
+#define DEVICE_PATH_TYPE_HARDWARE_DEVICE       0x01
+#  define DEVICE_PATH_SUB_TYPE_VENDOR          0x04
+
+struct efi_device_path_vendor {
+       struct efi_device_path dp;
+       efi_guid_t guid;
+       u8 vendor_data[];
+} __packed;
+
+#define DEVICE_PATH_TYPE_ACPI_DEVICE           0x02
+#  define DEVICE_PATH_SUB_TYPE_ACPI_DEVICE     0x01
+
+#define EFI_PNP_ID(ID)                         (u32)(((ID) << 16) | 0x41D0)
+#define EISA_PNP_ID(ID)                                EFI_PNP_ID(ID)
+#define EISA_PNP_NUM(ID)                       ((ID) >> 16)
+
+struct efi_device_path_acpi_path {
+       struct efi_device_path dp;
+       u32 hid;
+       u32 uid;
+} __packed;
 
 #define DEVICE_PATH_TYPE_MESSAGING_DEVICE      0x03
+#  define DEVICE_PATH_SUB_TYPE_MSG_USB         0x05
 #  define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR    0x0b
+#  define DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS   0x0f
+#  define DEVICE_PATH_SUB_TYPE_MSG_SD          0x1a
+#  define DEVICE_PATH_SUB_TYPE_MSG_MMC         0x1d
+
+struct efi_device_path_usb {
+       struct efi_device_path dp;
+       u8 parent_port_number;
+       u8 usb_interface;
+} __packed;
 
 struct efi_device_path_mac_addr {
        struct efi_device_path dp;
        struct efi_mac_addr mac;
        u8 if_type;
-};
+} __packed;
+
+struct efi_device_path_usb_class {
+       struct efi_device_path dp;
+       u16 vendor_id;
+       u16 product_id;
+       u8 device_class;
+       u8 device_subclass;
+       u8 device_protocol;
+} __packed;
+
+struct efi_device_path_sd_mmc_path {
+       struct efi_device_path dp;
+       u8 slot_number;
+} __packed;
 
 #define DEVICE_PATH_TYPE_MEDIA_DEVICE          0x04
+#  define DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH 0x01
+#  define DEVICE_PATH_SUB_TYPE_CDROM_PATH      0x02
 #  define DEVICE_PATH_SUB_TYPE_FILE_PATH       0x04
 
+struct efi_device_path_hard_drive_path {
+       struct efi_device_path dp;
+       u32 partition_number;
+       u64 partition_start;
+       u64 partition_end;
+       u8 partition_signature[16];
+       u8 partmap_type;
+       u8 signature_type;
+} __packed;
+
+struct efi_device_path_cdrom_path {
+       struct efi_device_path dp;
+       u32 boot_entry;
+       u64 partition_start;
+       u64 partition_end;
+} __packed;
+
 struct efi_device_path_file_path {
        struct efi_device_path dp;
-       u16 str[32];
-};
+       u16 str[];
+} __packed;
 
 #define BLOCK_IO_GUID \
        EFI_GUID(0x964e5b21, 0x6459, 0x11d2, \
@@ -358,10 +429,10 @@ struct efi_simple_text_output_protocol {
        void *reset;
        efi_status_t (EFIAPI *output_string)(
                        struct efi_simple_text_output_protocol *this,
-                       const unsigned short *str);
+                       const efi_string_t str);
        efi_status_t (EFIAPI *test_string)(
                        struct efi_simple_text_output_protocol *this,
-                       const unsigned short *str);
+                       const efi_string_t str);
        efi_status_t(EFIAPI *query_mode)(
                        struct efi_simple_text_output_protocol *this,
                        unsigned long mode_number, unsigned long *columns,
@@ -423,22 +494,14 @@ struct efi_console_control_protocol
        EFI_GUID(0x8b843e20, 0x8132, 0x4852, \
                 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c)
 
-struct efi_device_path_protocol
-{
-       uint8_t type;
-       uint8_t sub_type;
-       uint16_t length;
-       uint8_t data[];
-};
-
 struct efi_device_path_to_text_protocol
 {
        uint16_t *(EFIAPI *convert_device_node_to_text)(
-                       struct efi_device_path_protocol *device_node,
+                       struct efi_device_path *device_node,
                        bool display_only,
                        bool allow_shortcuts);
        uint16_t *(EFIAPI *convert_device_path_to_text)(
-                       struct efi_device_path_protocol *device_path,
+                       struct efi_device_path *device_path,
                        bool display_only,
                        bool allow_shortcuts);
 };
@@ -609,4 +672,69 @@ struct efi_pxe {
        struct efi_pxe_mode *mode;
 };
 
+#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \
+       EFI_GUID(0x964e5b22, 0x6459, 0x11d2, \
+                0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+#define EFI_FILE_PROTOCOL_REVISION 0x00010000
+
+struct efi_file_handle {
+       u64 rev;
+       efi_status_t (EFIAPI *open)(struct efi_file_handle *file,
+                       struct efi_file_handle **new_handle,
+                       s16 *file_name, u64 open_mode, u64 attributes);
+       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_status_t (EFIAPI *write)(struct efi_file_handle *file,
+                       u64 *buffer_size, void *buffer);
+       efi_status_t (EFIAPI *getpos)(struct efi_file_handle *file,
+                       u64 *pos);
+       efi_status_t (EFIAPI *setpos)(struct efi_file_handle *file,
+                       u64 pos);
+       efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *file,
+                       efi_guid_t *info_type, u64 *buffer_size, void *buffer);
+       efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *file,
+                       efi_guid_t *info_type, u64 buffer_size, void *buffer);
+       efi_status_t (EFIAPI *flush)(struct efi_file_handle *file);
+};
+
+#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \
+       EFI_GUID(0x964e5b22, 0x6459, 0x11d2, \
+                0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION 0x00010000
+
+struct efi_simple_file_system_protocol {
+       u64 rev;
+       efi_status_t (EFIAPI *open_volume)(struct efi_simple_file_system_protocol *this,
+                       struct efi_file_handle **root);
+};
+
+#define EFI_FILE_INFO_GUID \
+       EFI_GUID(0x9576e92, 0x6d3f, 0x11d2, \
+                0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+
+#define EFI_FILE_MODE_READ     0x0000000000000001
+#define EFI_FILE_MODE_WRITE    0x0000000000000002
+#define EFI_FILE_MODE_CREATE   0x8000000000000000
+
+#define EFI_FILE_READ_ONLY     0x0000000000000001
+#define EFI_FILE_HIDDEN                0x0000000000000002
+#define EFI_FILE_SYSTEM                0x0000000000000004
+#define EFI_FILE_RESERVED      0x0000000000000008
+#define EFI_FILE_DIRECTORY     0x0000000000000010
+#define EFI_FILE_ARCHIVE       0x0000000000000020
+#define EFI_FILE_VALID_ATTR    0x0000000000000037
+
+struct efi_file_info {
+       u64 size;
+       u64 file_size;
+       u64 physical_size;
+       struct efi_time create_time;
+       struct efi_time last_access_time;
+       struct efi_time modification_time;
+       u64 attribute;
+       s16 file_name[0];
+};
+
 #endif
index 1179234f6836b6e7aaa942cbed3e1a701aee2737..2f081f899658ceca83fa8f0abbf96c5c87f43814 100644 (file)
@@ -17,6 +17,7 @@
 
 int __efi_entry_check(void);
 int __efi_exit_check(void);
+const char *__efi_nesting(void);
 const char *__efi_nesting_inc(void);
 const char *__efi_nesting_dec(void);
 
@@ -41,9 +42,22 @@ const char *__efi_nesting_dec(void);
        })
 
 /*
- * Callback into UEFI world from u-boot:
+ * Call non-void UEFI function from u-boot and retrieve return value:
  */
-#define EFI_CALL(exp) do { \
+#define EFI_CALL(exp) ({ \
+       debug("%sEFI: Call: %s\n", __efi_nesting_inc(), #exp); \
+       assert(__efi_exit_check()); \
+       typeof(exp) _r = exp; \
+       assert(__efi_entry_check()); \
+       debug("%sEFI: %lu returned by %s\n", __efi_nesting_dec(), \
+             (unsigned long)((uintptr_t)_r & ~EFI_ERROR_MASK), #exp); \
+       _r; \
+})
+
+/*
+ * Call void UEFI function from u-boot:
+ */
+#define EFI_CALL_VOID(exp) do { \
        debug("%sEFI: Call: %s\n", __efi_nesting_inc(), #exp); \
        assert(__efi_exit_check()); \
        exp; \
@@ -51,6 +65,13 @@ const char *__efi_nesting_dec(void);
        debug("%sEFI: Return From: %s\n", __efi_nesting_dec(), #exp); \
        } while(0)
 
+/*
+ * Write GUID
+ */
+#define EFI_PRINT_GUID(txt, guid) ({ \
+       debug("%sEFI: %s %pUl\n", __efi_nesting(), txt, guid); \
+       })
+
 extern struct efi_runtime_services efi_runtime_services;
 extern struct efi_system_table systab;
 
@@ -59,10 +80,15 @@ extern struct efi_simple_input_interface efi_con_in;
 extern const struct efi_console_control_protocol efi_console_control;
 extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
 
+uint16_t *efi_dp_str(struct efi_device_path *dp);
+
+extern const efi_guid_t efi_global_variable_guid;
 extern const efi_guid_t efi_guid_console_control;
 extern const efi_guid_t efi_guid_device_path;
 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;
 
 extern unsigned int __efi_runtime_start, __efi_runtime_stop;
 extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@@ -110,7 +136,8 @@ struct efi_object {
  * @nofify_function:   Function to call when the event is triggered
  * @notify_context:    Data to be passed to the notify function
  * @trigger_type:      Type of timer, see efi_set_timer
- * @signaled:          The notify function was already called
+ * @queued:            The notification functionis queued
+ * @signaled:          The event occured
  */
 struct efi_event {
        uint32_t type;
@@ -120,6 +147,7 @@ struct efi_event {
        u64 trigger_next;
        u64 trigger_time;
        enum efi_timer_delay trigger_type;
+       int queued;
        int signaled;
 };
 
@@ -134,10 +162,13 @@ int efi_disk_register(void);
 /* Called by bootefi to make GOP (graphical) interface available */
 int efi_gop_register(void);
 /* Called by bootefi to make the network interface available */
-int efi_net_register(void **handle);
+int efi_net_register(void);
 /* Called by bootefi to make SMBIOS tables available */
 void efi_smbios_register(void);
 
+struct efi_simple_file_system_protocol *
+efi_fs_from_path(struct efi_device_path *fp);
+
 /* Called by networking code to memorize the dhcp ack package */
 void efi_net_set_dhcp_ack(void *pkt, int len);
 
@@ -166,6 +197,14 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
 /* Call this to signal an event */
 void efi_signal_event(struct efi_event *event);
 
+/* open file system: */
+struct efi_simple_file_system_protocol *efi_simple_file_system(
+               struct blk_desc *desc, int part, struct efi_device_path *dp);
+
+/* open file from device-path: */
+struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp);
+
+
 /* Generic EFI memory allocator, call this to get memory */
 void *efi_alloc(uint64_t len, int memory_type);
 /* More specific EFI memory allocator, called by EFI payloads */
@@ -191,12 +230,43 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
 int efi_memory_init(void);
 /* Adds new or overrides configuration table entry to the system table */
 efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table);
+void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj,
+                           struct efi_device_path *device_path,
+                           struct efi_device_path *file_path);
+efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
+                                     void **buffer);
 
 #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
 extern void *efi_bounce_buffer;
 #define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024)
 #endif
 
+
+struct efi_device_path *efi_dp_next(const struct efi_device_path *dp);
+int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b);
+struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
+                                  struct efi_device_path **rem);
+unsigned efi_dp_size(const struct efi_device_path *dp);
+struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp);
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+                                     const struct efi_device_path *dp2);
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+                                          const struct efi_device_path *node);
+
+
+struct efi_device_path *efi_dp_from_dev(struct udevice *dev);
+struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part);
+struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
+                                        const char *path);
+struct efi_device_path *efi_dp_from_eth(void);
+void efi_dp_split_file_path(struct efi_device_path *full_path,
+                           struct efi_device_path **device_path,
+                           struct efi_device_path **file_path);
+
+#define EFI_DP_TYPE(_dp, _type, _subtype) \
+       (((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
+        ((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))
+
 /* Convert strings from normal C strings to uEFI strings */
 static inline void ascii2unicode(u16 *unicode, const char *ascii)
 {
@@ -233,6 +303,28 @@ efi_status_t __efi_runtime EFIAPI efi_get_time(
                        struct efi_time_cap *capabilities);
 void efi_get_time_init(void);
 
+#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
+/*
+ * Entry point for the tests of the EFI API.
+ * It is called by 'bootefi selftest'
+ */
+efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
+                                struct efi_system_table *systab);
+#endif
+
+efi_status_t EFIAPI efi_get_variable(s16 *variable_name,
+               efi_guid_t *vendor, u32 *attributes,
+               unsigned long *data_size, void *data);
+efi_status_t EFIAPI efi_get_next_variable(
+               unsigned long *variable_name_size,
+               s16 *variable_name, efi_guid_t *vendor);
+efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
+               efi_guid_t *vendor, u32 attributes,
+               unsigned long data_size, void *data);
+
+void *efi_bootmgr_load(struct efi_device_path **device_path,
+                      struct efi_device_path **file_path);
+
 #else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
diff --git a/include/efi_selftest.h b/include/efi_selftest.h
new file mode 100644 (file)
index 0000000..76304a2
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *  EFI application loader
+ *
+ *  Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef _EFI_SELFTEST_H
+#define _EFI_SELFTEST_H
+
+#include <common.h>
+#include <efi.h>
+#include <efi_api.h>
+#include <linker_lists.h>
+
+/*
+ * Prints an error message.
+ *
+ * @...        format string followed by fields to print
+ */
+#define efi_st_error(...) \
+       efi_st_printf("%s(%u):\nERROR: ", __FILE__, __LINE__); \
+       efi_st_printf(__VA_ARGS__) \
+
+/*
+ * A test may be setup and executed at boottime,
+ * it may be setup at boottime and executed at runtime,
+ * or it may be setup and executed at runtime.
+ */
+enum efi_test_phase {
+       EFI_EXECUTE_BEFORE_BOOTTIME_EXIT = 1,
+       EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+       EFI_SETUP_AFTER_BOOTTIME_EXIT,
+};
+
+extern struct efi_simple_text_output_protocol *con_out;
+extern struct efi_simple_input_interface *con_in;
+
+/*
+ * Exit the boot services.
+ *
+ * The size of the memory map is determined.
+ * Pool memory is allocated to copy the memory map.
+ * The memory amp is copied and the map key is obtained.
+ * The map key is used to exit the boot services.
+ */
+void efi_st_exit_boot_services(void);
+
+/*
+ * Print a pointer to an u16 string
+ *
+ * @pointer: pointer
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+void efi_st_printf(const char *fmt, ...)
+                __attribute__ ((format (__printf__, 1, 2)));
+
+/*
+ * Reads an Unicode character from the input device.
+ *
+ * @return: Unicode character
+ */
+u16 efi_st_get_key(void);
+
+/**
+ * struct efi_unit_test - EFI unit test
+ *
+ * An efi_unit_test provides a interface to an EFI unit test.
+ *
+ * @name:      name of unit test
+ * @phase:     specifies when setup and execute are executed
+ * @setup:     set up the unit test
+ * @teardown:  tear down the unit test
+ * @execute:   execute the unit test
+ */
+struct efi_unit_test {
+       const char *name;
+       const enum efi_test_phase phase;
+       int (*setup)(const efi_handle_t handle,
+                    const struct efi_system_table *systable);
+       int (*execute)(void);
+       int (*teardown)(void);
+};
+
+/* Declare a new EFI unit test */
+#define EFI_UNIT_TEST(__name)                                          \
+       ll_entry_declare(struct efi_unit_test, __name, efi_unit_test)
+
+#endif /* _EFI_SELFTEST_H */
index 86117a7ce5d3c57e80c1ec9dc8293718b5c78c32..b2e820ef8ada0549db9b19014799a5105039fd42 100644 (file)
@@ -280,8 +280,9 @@ struct part_driver {
 #define U_BOOT_PART_TYPE(__name)                                       \
        ll_entry_declare(struct part_driver, __name, part_driver)
 
-#if CONFIG_IS_ENABLED(EFI_PARTITION)
 #include <part_efi.h>
+
+#if CONFIG_IS_ENABLED(EFI_PARTITION)
 /* disk/part_efi.c */
 /**
  * write_gpt_table() - Write the GUID Partition Table to disk
index 317c044795caf68081aee879dc20c40835091256..31e6bc6e140fe2956b15da1012b74be71bf422c0 100644 (file)
 /* linux/include/efi.h */
 typedef u16 efi_char16_t;
 
-typedef struct {
-       u8 b[16];
-} efi_guid_t;
-
 /* based on linux/include/genhd.h */
 struct partition {
        u8 boot_ind;            /* 0x80 - active */
index deb35a0ea452e43ce99c7ba31cae621c5f271a5f..4ef3e92efafcbc29d7c1453c5a41bc7ecd47fe48 100644 (file)
@@ -62,6 +62,12 @@ typedef struct _IMAGE_DATA_DIRECTORY {
 
 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
 
+/* PE32+ Subsystem type for EFI images */
+#define IMAGE_SUBSYSTEM_EFI_APPLICATION         10
+#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
+#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER      12
+#define IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER      13
+
 typedef struct _IMAGE_OPTIONAL_HEADER64 {
        uint16_t Magic; /* 0x20b */
        uint8_t  MajorLinkerVersion;
index faf4538fb5fe200146809a8ae6afd0affe97a397..8e1c9d1bb70a787d70941e02547e66cb77712429 100644 (file)
@@ -9,6 +9,7 @@ ifndef CONFIG_SPL_BUILD
 
 obj-$(CONFIG_EFI) += efi/
 obj-$(CONFIG_EFI_LOADER) += efi_loader/
+obj-$(CONFIG_EFI_LOADER) += efi_selftest/
 obj-$(CONFIG_LZMA) += lzma/
 obj-$(CONFIG_LZO) += lzo/
 obj-$(CONFIG_BZIP2) += bzip2/
index 30bf343a36522d24bbb5e655b6451cae2eeccd70..ddb978f650fffb308084cc56225594a0e3d2d5d3 100644 (file)
 CFLAGS_helloworld.o := $(CFLAGS_EFI)
 CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI)
 
-efiprogs-$(CONFIG_CMD_BOOTEFI_HELLO_COMPILE) += helloworld.efi
-always := $(efiprogs-y)
+ifneq ($(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),)
+always += helloworld.efi
+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
+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
 obj-$(CONFIG_LCD) += efi_gop.o
 obj-$(CONFIG_DM_VIDEO) += efi_gop.o
 obj-$(CONFIG_PARTITIONS) += efi_disk.o
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
new file mode 100644 (file)
index 0000000..857d88a
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ *  EFI utils
+ *
+ *  Copyright (c) 2017 Rob Clark
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <malloc.h>
+#include <efi_loader.h>
+
+static const struct efi_boot_services *bs;
+static const struct efi_runtime_services *rs;
+
+#define LOAD_OPTION_ACTIVE             0x00000001
+#define LOAD_OPTION_FORCE_RECONNECT    0x00000002
+#define LOAD_OPTION_HIDDEN             0x00000008
+
+/*
+ * bootmgr implements the logic of trying to find a payload to boot
+ * based on the BootOrder + BootXXXX variables, and then loading it.
+ *
+ * TODO detecting a special key held (f9?) and displaying a boot menu
+ * like you would get on a PC would be clever.
+ *
+ * TODO if we had a way to write and persist variables after the OS
+ * has started, we'd also want to check OsIndications to see if we
+ * should do normal or recovery boot.
+ */
+
+
+/*
+ * See section 3.1.3 in the v2.7 UEFI spec for more details on
+ * the layout of EFI_LOAD_OPTION.  In short it is:
+ *
+ *    typedef struct _EFI_LOAD_OPTION {
+ *        UINT32 Attributes;
+ *        UINT16 FilePathListLength;
+ *        // CHAR16 Description[];   <-- variable length, NULL terminated
+ *        // EFI_DEVICE_PATH_PROTOCOL FilePathList[];  <-- FilePathListLength bytes
+ *        // UINT8 OptionalData[];
+ *    } EFI_LOAD_OPTION;
+ */
+struct load_option {
+       u32 attributes;
+       u16 file_path_length;
+       u16 *label;
+       struct efi_device_path *file_path;
+       u8 *optional_data;
+};
+
+/* parse an EFI_LOAD_OPTION, as described above */
+static void parse_load_option(struct load_option *lo, void *ptr)
+{
+       lo->attributes = *(u32 *)ptr;
+       ptr += sizeof(u32);
+
+       lo->file_path_length = *(u16 *)ptr;
+       ptr += sizeof(u16);
+
+       lo->label = ptr;
+       ptr += (utf16_strlen(lo->label) + 1) * 2;
+
+       lo->file_path = ptr;
+       ptr += lo->file_path_length;
+
+       lo->optional_data = ptr;
+}
+
+/* free() the result */
+static void *get_var(u16 *name, const efi_guid_t *vendor,
+                    unsigned long *size)
+{
+       efi_guid_t *v = (efi_guid_t *)vendor;
+       efi_status_t ret;
+       void *buf = NULL;
+
+       *size = 0;
+       EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
+       if (ret == EFI_BUFFER_TOO_SMALL) {
+               buf = malloc(*size);
+               EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
+       }
+
+       if (ret != EFI_SUCCESS) {
+               free(buf);
+               *size = 0;
+               return NULL;
+       }
+
+       return buf;
+}
+
+/*
+ * Attempt to load load-option number 'n', returning device_path and file_path
+ * if successful.  This checks that the EFI_LOAD_OPTION is active (enabled)
+ * and that the specified file to boot exists.
+ */
+static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
+                           struct efi_device_path **file_path)
+{
+       struct load_option lo;
+       u16 varname[] = L"Boot0000";
+       u16 hexmap[] = L"0123456789ABCDEF";
+       void *load_option, *image = NULL;
+       unsigned long size;
+
+       varname[4] = hexmap[(n & 0xf000) >> 12];
+       varname[5] = hexmap[(n & 0x0f00) >> 8];
+       varname[6] = hexmap[(n & 0x00f0) >> 4];
+       varname[7] = hexmap[(n & 0x000f) >> 0];
+
+       load_option = get_var(varname, &efi_global_variable_guid, &size);
+       if (!load_option)
+               return NULL;
+
+       parse_load_option(&lo, load_option);
+
+       if (lo.attributes & LOAD_OPTION_ACTIVE) {
+               efi_status_t ret;
+               u16 *str = NULL;
+
+               debug("%s: trying to load \"%ls\" from: %ls\n", __func__,
+                     lo.label, (str = efi_dp_str(lo.file_path)));
+               efi_free_pool(str);
+
+               ret = efi_load_image_from_path(lo.file_path, &image);
+
+               if (ret != EFI_SUCCESS)
+                       goto error;
+
+               printf("Booting: %ls\n", lo.label);
+               efi_dp_split_file_path(lo.file_path, device_path, file_path);
+       }
+
+error:
+       free(load_option);
+
+       return image;
+}
+
+/*
+ * Attempt to load, in the order specified by BootOrder EFI variable, the
+ * available load-options, finding and returning the first one that can
+ * be loaded successfully.
+ */
+void *efi_bootmgr_load(struct efi_device_path **device_path,
+                      struct efi_device_path **file_path)
+{
+       uint16_t *bootorder;
+       unsigned long size;
+       void *image = NULL;
+       int i, num;
+
+       __efi_entry_check();
+
+       bs = systab.boottime;
+       rs = systab.runtime;
+
+       bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
+       if (!bootorder)
+               goto error;
+
+       num = size / sizeof(uint16_t);
+       for (i = 0; i < num; i++) {
+               debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
+               image = try_load_entry(bootorder[i], device_path, file_path);
+               if (image)
+                       break;
+       }
+
+       free(bootorder);
+
+error:
+       __efi_exit_check();
+
+       return image;
+}
index 43f32385fae01fbdbbcfdbae2e9c4d78e7918477..9e741c3cf3c73b008ce9e4186c6bf448bf803f8a 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <environment.h>
 #include <malloc.h>
 #include <asm/global_data.h>
 #include <libfdt_env.h>
@@ -18,6 +19,9 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/* Task priority level */
+static UINTN efi_tpl = TPL_APPLICATION;
+
 /* This list contains all the EFI objects our payload has access to */
 LIST_HEAD(efi_obj_list);
 
@@ -109,6 +113,11 @@ static const char *indent_string(int level)
        return &indent[max - level];
 }
 
+const char *__efi_nesting(void)
+{
+       return indent_string(nesting_level);
+}
+
 const char *__efi_nesting_inc(void)
 {
        return indent_string(nesting_level++);
@@ -154,12 +163,15 @@ static u64 efi_div10(u64 a)
 
 void efi_signal_event(struct efi_event *event)
 {
-       if (event->signaled)
-               return;
-       event->signaled = 1;
-       if (event->type & EVT_NOTIFY_SIGNAL) {
-               EFI_CALL(event->notify_function(event, event->notify_context));
+       if (event->notify_function) {
+               event->queued = 1;
+               /* Check TPL */
+               if (efi_tpl >= event->notify_tpl)
+                       return;
+               EFI_CALL_VOID(event->notify_function(event,
+                                                    event->notify_context));
        }
+       event->queued = 0;
 }
 
 static efi_status_t efi_unsupported(const char *funcname)
@@ -170,14 +182,31 @@ static efi_status_t efi_unsupported(const char *funcname)
 
 static unsigned long EFIAPI efi_raise_tpl(UINTN new_tpl)
 {
+       UINTN old_tpl = efi_tpl;
+
        EFI_ENTRY("0x%zx", new_tpl);
-       return EFI_EXIT(0);
+
+       if (new_tpl < efi_tpl)
+               debug("WARNING: new_tpl < current_tpl in %s\n", __func__);
+       efi_tpl = new_tpl;
+       if (efi_tpl > TPL_HIGH_LEVEL)
+               efi_tpl = TPL_HIGH_LEVEL;
+
+       EFI_EXIT(EFI_SUCCESS);
+       return old_tpl;
 }
 
 static void EFIAPI efi_restore_tpl(UINTN old_tpl)
 {
        EFI_ENTRY("0x%zx", old_tpl);
-       efi_unsupported(__func__);
+
+       if (old_tpl > efi_tpl)
+               debug("WARNING: old_tpl > current_tpl in %s\n", __func__);
+       efi_tpl = old_tpl;
+       if (efi_tpl > TPL_HIGH_LEVEL)
+               efi_tpl = TPL_HIGH_LEVEL;
+
+       EFI_EXIT(EFI_SUCCESS);
 }
 
 static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
@@ -270,6 +299,7 @@ efi_status_t efi_create_event(uint32_t type, UINTN notify_tpl,
                efi_events[i].notify_context = notify_context;
                /* Disable timers on bootup */
                efi_events[i].trigger_next = -1ULL;
+               efi_events[i].queued = 0;
                efi_events[i].signaled = 0;
                *event = &efi_events[i];
                return EFI_SUCCESS;
@@ -301,16 +331,25 @@ void efi_timer_check(void)
        u64 now = timer_get_us();
 
        for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
-               if (!efi_events[i].type ||
-                   !(efi_events[i].type & EVT_TIMER) ||
-                   efi_events[i].trigger_type == EFI_TIMER_STOP ||
+               if (!efi_events[i].type)
+                       continue;
+               if (efi_events[i].queued)
+                       efi_signal_event(&efi_events[i]);
+               if (!(efi_events[i].type & EVT_TIMER) ||
                    now < efi_events[i].trigger_next)
                        continue;
-               if (efi_events[i].trigger_type == EFI_TIMER_PERIODIC) {
+               switch (efi_events[i].trigger_type) {
+               case EFI_TIMER_RELATIVE:
+                       efi_events[i].trigger_type = EFI_TIMER_STOP;
+                       break;
+               case EFI_TIMER_PERIODIC:
                        efi_events[i].trigger_next +=
                                efi_events[i].trigger_time;
-                       efi_events[i].signaled = 0;
+                       break;
+               default:
+                       continue;
                }
+               efi_events[i].signaled = 1;
                efi_signal_event(&efi_events[i]);
        }
        WATCHDOG_RESET();
@@ -347,6 +386,7 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
                }
                event->trigger_type = type;
                event->trigger_time = trigger_time;
+               event->signaled = 0;
                return EFI_SUCCESS;
        }
        return EFI_INVALID_PARAMETER;
@@ -371,6 +411,9 @@ static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events,
        /* Check parameters */
        if (!num_events || !event)
                return EFI_EXIT(EFI_INVALID_PARAMETER);
+       /* Check TPL */
+       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])
@@ -380,6 +423,8 @@ static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events,
 known_event:
                if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
                        return EFI_EXIT(EFI_INVALID_PARAMETER);
+               if (!event[i]->signaled)
+                       efi_signal_event(event[i]);
        }
 
        /* Wait for signal */
@@ -412,7 +457,11 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
        for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
                if (event != &efi_events[i])
                        continue;
-               efi_signal_event(event);
+               if (event->signaled)
+                       break;
+               event->signaled = 1;
+               if (event->type & EVT_NOTIFY_SIGNAL)
+                       efi_signal_event(event);
                break;
        }
        return EFI_EXIT(EFI_SUCCESS);
@@ -427,6 +476,7 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
                if (event == &efi_events[i]) {
                        event->type = 0;
                        event->trigger_next = -1ULL;
+                       event->queued = 0;
                        event->signaled = 0;
                        return EFI_EXIT(EFI_SUCCESS);
                }
@@ -445,6 +495,8 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
                        continue;
                if (!event->type || event->type & EVT_NOTIFY_SIGNAL)
                        break;
+               if (!event->signaled)
+                       efi_signal_event(event);
                if (event->signaled)
                        return EFI_EXIT(EFI_SUCCESS);
                return EFI_EXIT(EFI_NOT_READY);
@@ -513,7 +565,7 @@ static efi_status_t EFIAPI efi_install_protocol_interface_ext(void **handle,
                        efi_guid_t *protocol, int protocol_interface_type,
                        void *protocol_interface)
 {
-       EFI_ENTRY("%p, %p, %d, %p", handle, protocol, protocol_interface_type,
+       EFI_ENTRY("%p, %pUl, %d, %p", handle, protocol, protocol_interface_type,
                  protocol_interface);
 
        return EFI_EXIT(efi_install_protocol_interface(handle, protocol,
@@ -525,7 +577,7 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle,
                        efi_guid_t *protocol, void *old_interface,
                        void *new_interface)
 {
-       EFI_ENTRY("%p, %p, %p, %p", handle, protocol, old_interface,
+       EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface,
                  new_interface);
        return EFI_EXIT(EFI_ACCESS_DENIED);
 }
@@ -574,7 +626,7 @@ out:
 static efi_status_t EFIAPI efi_uninstall_protocol_interface_ext(void *handle,
                        efi_guid_t *protocol, void *protocol_interface)
 {
-       EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface);
+       EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface);
 
        return EFI_EXIT(efi_uninstall_protocol_interface(handle, protocol,
                                                         protocol_interface));
@@ -584,7 +636,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol,
                                                        struct efi_event *event,
                                                        void **registration)
 {
-       EFI_ENTRY("%p, %p, %p", protocol, event, registration);
+       EFI_ENTRY("%pUl, %p, %p", protocol, event, registration);
        return EFI_EXIT(EFI_OUT_OF_RESOURCES);
 }
 
@@ -654,7 +706,7 @@ static efi_status_t EFIAPI efi_locate_handle_ext(
                        efi_guid_t *protocol, void *search_key,
                        unsigned long *buffer_size, efi_handle_t *buffer)
 {
-       EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
+       EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
                  buffer_size, buffer);
 
        return EFI_EXIT(efi_locate_handle(search_type, protocol, search_key,
@@ -665,8 +717,17 @@ static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol,
                        struct efi_device_path **device_path,
                        efi_handle_t *device)
 {
-       EFI_ENTRY("%p, %p, %p", protocol, device_path, device);
-       return EFI_EXIT(EFI_NOT_FOUND);
+       struct efi_object *efiobj;
+
+       EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
+
+       efiobj = efi_dp_find_obj(*device_path, device_path);
+       if (!efiobj)
+               return EFI_EXIT(EFI_NOT_FOUND);
+
+       *device = efiobj->handle;
+
+       return EFI_EXIT(EFI_SUCCESS);
 }
 
 /* Collapses configuration table entries, removing index i */
@@ -713,10 +774,87 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table
 static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid,
                                                               void *table)
 {
-       EFI_ENTRY("%p, %p", guid, table);
+       EFI_ENTRY("%pUl, %p", guid, table);
        return EFI_EXIT(efi_install_configuration_table(guid, table));
 }
 
+/* Initialize a loaded_image_info + loaded_image_info object with correct
+ * protocols, boot-device, etc.
+ */
+void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj,
+                           struct efi_device_path *device_path,
+                           struct efi_device_path *file_path)
+{
+       obj->handle = info;
+
+       /*
+        * When asking for the device path interface, return
+        * bootefi_device_path
+        */
+       obj->protocols[0].guid = &efi_guid_device_path;
+       obj->protocols[0].protocol_interface = device_path;
+
+       /*
+        * When asking for the loaded_image interface, just
+        * return handle which points to loaded_image_info
+        */
+       obj->protocols[1].guid = &efi_guid_loaded_image;
+       obj->protocols[1].protocol_interface = info;
+
+       obj->protocols[2].guid = &efi_guid_console_control;
+       obj->protocols[2].protocol_interface = (void *)&efi_console_control;
+
+       obj->protocols[3].guid = &efi_guid_device_path_to_text_protocol;
+       obj->protocols[3].protocol_interface =
+               (void *)&efi_device_path_to_text;
+
+       info->file_path = file_path;
+       info->device_handle = efi_dp_find_obj(device_path, NULL);
+
+       list_add_tail(&obj->link, &efi_obj_list);
+}
+
+efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
+                                     void **buffer)
+{
+       struct efi_file_info *info = NULL;
+       struct efi_file_handle *f;
+       static efi_status_t ret;
+       uint64_t bs;
+
+       f = efi_file_from_path(file_path);
+       if (!f)
+               return EFI_DEVICE_ERROR;
+
+       bs = 0;
+       EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
+                                 &bs, info));
+       if (ret == EFI_BUFFER_TOO_SMALL) {
+               info = malloc(bs);
+               EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
+                                         &bs, info));
+       }
+       if (ret != EFI_SUCCESS)
+               goto error;
+
+       ret = efi_allocate_pool(EFI_LOADER_DATA, info->file_size, buffer);
+       if (ret)
+               goto error;
+
+       EFI_CALL(ret = f->read(f, &info->file_size, *buffer));
+
+error:
+       free(info);
+       EFI_CALL(f->close(f));
+
+       if (ret != EFI_SUCCESS) {
+               efi_free_pool(*buffer);
+               *buffer = NULL;
+       }
+
+       return ret;
+}
+
 static efi_status_t EFIAPI efi_load_image(bool boot_policy,
                                          efi_handle_t parent_image,
                                          struct efi_device_path *file_path,
@@ -724,25 +862,40 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
                                          unsigned long source_size,
                                          efi_handle_t *image_handle)
 {
-       static struct efi_object loaded_image_info_obj = {
-               .protocols = {
-                       {
-                               .guid = &efi_guid_loaded_image,
-                       },
-               },
-       };
        struct efi_loaded_image *info;
        struct efi_object *obj;
 
        EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image,
                  file_path, source_buffer, source_size, image_handle);
-       info = malloc(sizeof(*info));
-       loaded_image_info_obj.protocols[0].protocol_interface = info;
-       obj = malloc(sizeof(loaded_image_info_obj));
-       memset(info, 0, sizeof(*info));
-       memcpy(obj, &loaded_image_info_obj, sizeof(loaded_image_info_obj));
-       obj->handle = info;
-       info->file_path = file_path;
+
+       info = calloc(1, sizeof(*info));
+       obj = calloc(1, sizeof(*obj));
+
+       if (!source_buffer) {
+               struct efi_device_path *dp, *fp;
+               efi_status_t ret;
+
+               ret = efi_load_image_from_path(file_path, &source_buffer);
+               if (ret != EFI_SUCCESS) {
+                       free(info);
+                       free(obj);
+                       return EFI_EXIT(ret);
+               }
+
+               /*
+                * split file_path which contains both the device and
+                * file parts:
+                */
+               efi_dp_split_file_path(file_path, &dp, &fp);
+
+               efi_setup_loaded_image(info, obj, dp, fp);
+       } else {
+               /* In this case, file_path is the "device" path, ie.
+                * something like a HARDWARE_DEVICE:MEMORY_MAPPED
+                */
+               efi_setup_loaded_image(info, obj, file_path, NULL);
+       }
+
        info->reserved = efi_load_pe(source_buffer, info);
        if (!info->reserved) {
                free(info);
@@ -751,7 +904,6 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
        }
 
        *image_handle = info;
-       list_add_tail(&obj->link, &efi_obj_list);
 
        return EFI_EXIT(EFI_SUCCESS);
 }
@@ -793,6 +945,15 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
        EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
                  exit_data_size, exit_data);
 
+       /* Make sure entry/exit counts for EFI world cross-overs match */
+       __efi_exit_check();
+
+       /*
+        * But longjmp out with the U-Boot gd, not the application's, as
+        * the other end is a setjmp call inside EFI context.
+        */
+       efi_restore_gd();
+
        loaded_image_info->exit_status = exit_status;
        longjmp(&loaded_image_info->exit_jmp, 1);
 
@@ -840,8 +1001,24 @@ static void efi_exit_caches(void)
 static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
                                                  unsigned long map_key)
 {
+       int i;
+
        EFI_ENTRY("%p, %ld", image_handle, map_key);
 
+       /* 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_signal_event(&efi_events[i]);
+       }
+       /* Make sure that notification functions are not called anymore */
+       efi_tpl = TPL_HIGH_LEVEL;
+
+#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
+       /* save any EFI variables that have been written: */
+       env_save();
+#endif
+
        board_quiesce_devices();
 
        /* Fix up caches for EFI payloads if necessary */
@@ -906,7 +1083,7 @@ static efi_status_t EFIAPI efi_close_protocol(void *handle,
                                              void *agent_handle,
                                              void *controller_handle)
 {
-       EFI_ENTRY("%p, %p, %p, %p", handle, protocol, agent_handle,
+       EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle,
                  controller_handle);
        return EFI_EXIT(EFI_NOT_FOUND);
 }
@@ -916,7 +1093,7 @@ static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle,
                        struct efi_open_protocol_info_entry **entry_buffer,
                        unsigned long *entry_count)
 {
-       EFI_ENTRY("%p, %p, %p, %p", handle, protocol, entry_buffer,
+       EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, entry_buffer,
                  entry_count);
        return EFI_EXIT(EFI_NOT_FOUND);
 }
@@ -982,7 +1159,7 @@ static efi_status_t EFIAPI efi_locate_handle_buffer(
        efi_status_t r;
        unsigned long buffer_size = 0;
 
-       EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
+       EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
                  no_handles, buffer);
 
        if (!no_handles || !buffer) {
@@ -1014,11 +1191,13 @@ static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol,
        struct list_head *lhandle;
        int i;
 
-       EFI_ENTRY("%p, %p, %p", protocol, registration, protocol_interface);
+       EFI_ENTRY("%pUl, %p, %p", protocol, registration, protocol_interface);
 
        if (!protocol || !protocol_interface)
                return EFI_EXIT(EFI_INVALID_PARAMETER);
 
+       EFI_PRINT_GUID("protocol", protocol);
+
        list_for_each(lhandle, &efi_obj_list) {
                struct efi_object *efiobj;
 
@@ -1122,7 +1301,7 @@ static efi_status_t EFIAPI efi_open_protocol(
        int i;
        efi_status_t r = EFI_INVALID_PARAMETER;
 
-       EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol,
+       EFI_ENTRY("%p, %pUl, %p, %p, %p, 0x%x", handle, protocol,
                  protocol_interface, agent_handle, controller_handle,
                  attributes);
 
@@ -1132,6 +1311,8 @@ static efi_status_t EFIAPI efi_open_protocol(
                goto out;
        }
 
+       EFI_PRINT_GUID("protocol", protocol);
+
        switch (attributes) {
        case EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL:
        case EFI_OPEN_PROTOCOL_GET_PROTOCOL:
index 3fc82b87261cba504da576393fb589d769b6eaa1..fd5398d61d94ff40fa83884de8c3dea26a8944c2 100644 (file)
@@ -8,7 +8,10 @@
 
 #include <common.h>
 #include <charset.h>
+#include <dm/device.h>
 #include <efi_loader.h>
+#include <stdio_dev.h>
+#include <video_console.h>
 
 static bool console_size_queried;
 
@@ -137,34 +140,46 @@ static efi_status_t EFIAPI efi_cout_reset(
        return EFI_EXIT(EFI_UNSUPPORTED);
 }
 
-static void print_unicode_in_utf8(u16 c)
-{
-       char utf8[MAX_UTF8_PER_UTF16] = { 0 };
-       utf16_to_utf8((u8 *)utf8, &c, 1);
-       puts(utf8);
-}
-
 static efi_status_t EFIAPI efi_cout_output_string(
                        struct efi_simple_text_output_protocol *this,
-                       const unsigned short *string)
+                       const efi_string_t string)
 {
-       struct cout_mode *mode;
-       u16 ch;
+       struct simple_text_output_mode *con = &efi_con_mode;
+       struct cout_mode *mode = &efi_cout_modes[con->mode];
 
-       mode = &efi_cout_modes[efi_con_mode.mode];
        EFI_ENTRY("%p, %p", this, string);
-       for (;(ch = *string); string++) {
-               print_unicode_in_utf8(ch);
-               efi_con_mode.cursor_column++;
-               if (ch == '\n') {
-                       efi_con_mode.cursor_column = 1;
-                       efi_con_mode.cursor_row++;
-               } else if (efi_con_mode.cursor_column > mode->columns) {
-                       efi_con_mode.cursor_column = 1;
-                       efi_con_mode.cursor_row++;
+
+       unsigned int n16 = utf16_strlen(string);
+       char buf[MAX_UTF8_PER_UTF16 * n16 + 1];
+       char *p;
+
+       *utf16_to_utf8((u8 *)buf, string, n16) = '\0';
+
+       fputs(stdout, buf);
+
+       for (p = buf; *p; p++) {
+               switch (*p) {
+               case '\r':   /* carriage-return */
+                       con->cursor_column = 0;
+                       break;
+               case '\n':   /* newline */
+                       con->cursor_column = 0;
+                       con->cursor_row++;
+                       break;
+               case '\t':   /* tab, assume 8 char align */
+                       break;
+               case '\b':   /* backspace */
+                       con->cursor_column = max(0, con->cursor_column - 1);
+                       break;
+               default:
+                       con->cursor_column++;
+                       break;
                }
-               if (efi_con_mode.cursor_row > mode->rows)
-                       efi_con_mode.cursor_row = mode->rows;
+               if (con->cursor_column >= mode->columns) {
+                       con->cursor_column = 0;
+                       con->cursor_row++;
+               }
+               con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1);
        }
 
        return EFI_EXIT(EFI_SUCCESS);
@@ -172,7 +187,7 @@ static efi_status_t EFIAPI efi_cout_output_string(
 
 static efi_status_t EFIAPI efi_cout_test_string(
                        struct efi_simple_text_output_protocol *this,
-                       const unsigned short *string)
+                       const efi_string_t string)
 {
        EFI_ENTRY("%p, %p", this, string);
        return EFI_EXIT(EFI_SUCCESS);
@@ -186,6 +201,34 @@ static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
        return (mode->rows == rows) && (mode->columns == cols);
 }
 
+static int query_console_serial(int *rows, int *cols)
+{
+       /* Ask the terminal about its size */
+       int n[3];
+       u64 timeout;
+
+       /* Empty input buffer */
+       while (tstc())
+               getc();
+
+       printf(ESC"[18t");
+
+       /* Check if we have a terminal that understands */
+       timeout = timer_get_us() + 1000000;
+       while (!tstc())
+               if (timer_get_us() > timeout)
+                       return -1;
+
+       /* Read {depth,rows,cols} */
+       if (term_read_reply(n, 3, 't'))
+               return -1;
+
+       *cols = n[2];
+       *rows = n[1];
+
+       return 0;
+}
+
 static efi_status_t EFIAPI efi_cout_query_mode(
                        struct efi_simple_text_output_protocol *this,
                        unsigned long mode_number, unsigned long *columns,
@@ -194,34 +237,24 @@ static efi_status_t EFIAPI efi_cout_query_mode(
        EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
 
        if (!console_size_queried) {
-               /* Ask the terminal about its size */
-               int n[3];
-               int cols;
-               int rows;
-               u64 timeout;
+               const char *stdout_name = env_get("stdout");
+               int rows, cols;
 
                console_size_queried = true;
 
-               /* Empty input buffer */
-               while (tstc())
-                       getc();
-
-               printf(ESC"[18t");
-
-               /* Check if we have a terminal that understands */
-               timeout = timer_get_us() + 1000000;
-               while (!tstc())
-                       if (timer_get_us() > timeout)
-                               goto out;
-
-               /* Read {depth,rows,cols} */
-               if (term_read_reply(n, 3, 't')) {
+               if (stdout_name && !strcmp(stdout_name, "vidconsole") &&
+                   IS_ENABLED(CONFIG_DM_VIDEO)) {
+                       struct stdio_dev *stdout_dev =
+                               stdio_get_by_name("vidconsole");
+                       struct udevice *dev = stdout_dev->priv;
+                       struct vidconsole_priv *priv =
+                               dev_get_uclass_priv(dev);
+                       rows = priv->rows;
+                       cols = priv->cols;
+               } else if (query_console_serial(&rows, &cols)) {
                        goto out;
                }
 
-               cols = n[2];
-               rows = n[1];
-
                /* Test if we can have Mode 1 */
                if (cols >= 80 && rows >= 50) {
                        efi_cout_modes[1].present = 1;
@@ -426,8 +459,10 @@ static void EFIAPI efi_console_timer_notify(struct efi_event *event,
                                            void *context)
 {
        EFI_ENTRY("%p, %p", event, context);
-       if (tstc())
+       if (tstc()) {
+               efi_con_in.wait_for_key->signaled = 1;
                efi_signal_event(efi_con_in.wait_for_key);
+               }
        EFI_EXIT(EFI_SUCCESS);
 }
 
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
new file mode 100644 (file)
index 0000000..5d5c3b3
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ * EFI device path from u-boot device-model mapping
+ *
+ * (C) Copyright 2017 Rob Clark
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <usb.h>
+#include <mmc.h>
+#include <efi_loader.h>
+#include <inttypes.h>
+#include <part.h>
+
+/* template END node: */
+static const struct efi_device_path END = {
+       .type     = DEVICE_PATH_TYPE_END,
+       .sub_type = DEVICE_PATH_SUB_TYPE_END,
+       .length   = sizeof(END),
+};
+
+#define U_BOOT_GUID \
+       EFI_GUID(0xe61d73b9, 0xa384, 0x4acc, \
+                0xae, 0xab, 0x82, 0xe8, 0x28, 0xf3, 0x62, 0x8b)
+
+/* template ROOT node: */
+static const struct efi_device_path_vendor ROOT = {
+       .dp = {
+               .type     = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
+               .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
+               .length   = sizeof(ROOT),
+       },
+       .guid = U_BOOT_GUID,
+};
+
+static void *dp_alloc(size_t sz)
+{
+       void *buf;
+
+       if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) != EFI_SUCCESS)
+               return NULL;
+
+       return buf;
+}
+
+/*
+ * Iterate to next block in device-path, terminating (returning NULL)
+ * at /End* node.
+ */
+struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
+{
+       if (dp == NULL)
+               return NULL;
+       if (dp->type == DEVICE_PATH_TYPE_END)
+               return NULL;
+       dp = ((void *)dp) + dp->length;
+       if (dp->type == DEVICE_PATH_TYPE_END)
+               return NULL;
+       return (struct efi_device_path *)dp;
+}
+
+/*
+ * Compare two device-paths, stopping when the shorter of the two hits
+ * an End* node.  This is useful to, for example, compare a device-path
+ * representing a device with one representing a file on the device, or
+ * a device with a parent device.
+ */
+int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b)
+{
+       while (1) {
+               int ret;
+
+               ret = memcmp(&a->length, &b->length, sizeof(a->length));
+               if (ret)
+                       return ret;
+
+               ret = memcmp(a, b, a->length);
+               if (ret)
+                       return ret;
+
+               a = efi_dp_next(a);
+               b = efi_dp_next(b);
+
+               if (!a || !b)
+                       return 0;
+       }
+}
+
+
+/*
+ * See UEFI spec (section 3.1.2, about short-form device-paths..
+ * tl;dr: we can have a device-path that starts with a USB WWID
+ * or USB Class node, and a few other cases which don't encode
+ * the full device path with bus hierarchy:
+ *
+ *   - MESSAGING:USB_WWID
+ *   - MESSAGING:USB_CLASS
+ *   - MEDIA:FILE_PATH
+ *   - MEDIA:HARD_DRIVE
+ *   - MESSAGING:URI
+ */
+static struct efi_device_path *shorten_path(struct efi_device_path *dp)
+{
+       while (dp) {
+               /*
+                * TODO: Add MESSAGING:USB_WWID and MESSAGING:URI..
+                * in practice fallback.efi just uses MEDIA:HARD_DRIVE
+                * so not sure when we would see these other cases.
+                */
+               if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_CLASS) ||
+                   EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) ||
+                   EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH))
+                       return dp;
+
+               dp = efi_dp_next(dp);
+       }
+
+       return dp;
+}
+
+static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
+                                  struct efi_device_path **rem)
+{
+       struct efi_object *efiobj;
+
+       list_for_each_entry(efiobj, &efi_obj_list, link) {
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
+                       struct efi_handler *handler = &efiobj->protocols[i];
+                       struct efi_device_path *obj_dp;
+
+                       if (!handler->guid)
+                               break;
+
+                       if (guidcmp(handler->guid, &efi_guid_device_path))
+                               continue;
+
+                       obj_dp = handler->protocol_interface;
+
+                       do {
+                               if (efi_dp_match(dp, obj_dp) == 0) {
+                                       if (rem) {
+                                               *rem = ((void *)dp) +
+                                                       efi_dp_size(obj_dp);
+                                       }
+                                       return efiobj;
+                               }
+
+                               obj_dp = shorten_path(efi_dp_next(obj_dp));
+                       } while (short_path && obj_dp);
+               }
+       }
+
+       return NULL;
+}
+
+
+/*
+ * Find an efiobj from device-path, if 'rem' is not NULL, returns the
+ * remaining part of the device path after the matched object.
+ */
+struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
+                                  struct efi_device_path **rem)
+{
+       struct efi_object *efiobj;
+
+       efiobj = find_obj(dp, false, rem);
+
+       if (!efiobj)
+               efiobj = find_obj(dp, true, rem);
+
+       return efiobj;
+}
+
+/* return size not including End node: */
+unsigned efi_dp_size(const struct efi_device_path *dp)
+{
+       unsigned sz = 0;
+
+       while (dp) {
+               sz += dp->length;
+               dp = efi_dp_next(dp);
+       }
+
+       return sz;
+}
+
+struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
+{
+       struct efi_device_path *ndp;
+       unsigned sz = efi_dp_size(dp) + sizeof(END);
+
+       if (!dp)
+               return NULL;
+
+       ndp = dp_alloc(sz);
+       memcpy(ndp, dp, sz);
+
+       return ndp;
+}
+
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+                                     const struct efi_device_path *dp2)
+{
+       struct efi_device_path *ret;
+
+       if (!dp1) {
+               ret = efi_dp_dup(dp2);
+       } else if (!dp2) {
+               ret = efi_dp_dup(dp1);
+       } else {
+               /* both dp1 and dp2 are non-null */
+               unsigned sz1 = efi_dp_size(dp1);
+               unsigned sz2 = efi_dp_size(dp2);
+               void *p = dp_alloc(sz1 + sz2 + sizeof(END));
+               memcpy(p, dp1, sz1);
+               memcpy(p + sz1, dp2, sz2);
+               memcpy(p + sz1 + sz2, &END, sizeof(END));
+               ret = p;
+       }
+
+       return ret;
+}
+
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+                                          const struct efi_device_path *node)
+{
+       struct efi_device_path *ret;
+
+       if (!node && !dp) {
+               ret = efi_dp_dup(&END);
+       } else if (!node) {
+               ret = efi_dp_dup(dp);
+       } else if (!dp) {
+               unsigned sz = node->length;
+               void *p = dp_alloc(sz + sizeof(END));
+               memcpy(p, node, sz);
+               memcpy(p + sz, &END, sizeof(END));
+               ret = p;
+       } else {
+               /* both dp and node are non-null */
+               unsigned sz = efi_dp_size(dp);
+               void *p = dp_alloc(sz + node->length + sizeof(END));
+               memcpy(p, dp, sz);
+               memcpy(p + sz, node, node->length);
+               memcpy(p + sz + node->length, &END, sizeof(END));
+               ret = p;
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_DM
+/* size of device-path not including END node for device and all parents
+ * up to the root device.
+ */
+static unsigned dp_size(struct udevice *dev)
+{
+       if (!dev || !dev->driver)
+               return sizeof(ROOT);
+
+       switch (dev->driver->id) {
+       case UCLASS_ROOT:
+       case UCLASS_SIMPLE_BUS:
+               /* stop traversing parents at this point: */
+               return sizeof(ROOT);
+       case UCLASS_MMC:
+               return dp_size(dev->parent) +
+                       sizeof(struct efi_device_path_sd_mmc_path);
+       case UCLASS_MASS_STORAGE:
+       case UCLASS_USB_HUB:
+               return dp_size(dev->parent) +
+                       sizeof(struct efi_device_path_usb_class);
+       default:
+               /* just skip over unknown classes: */
+               return dp_size(dev->parent);
+       }
+}
+
+static void *dp_fill(void *buf, struct udevice *dev)
+{
+       if (!dev || !dev->driver)
+               return buf;
+
+       switch (dev->driver->id) {
+       case UCLASS_ROOT:
+       case UCLASS_SIMPLE_BUS: {
+               /* stop traversing parents at this point: */
+               struct efi_device_path_vendor *vdp = buf;
+               *vdp = ROOT;
+               return &vdp[1];
+       }
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+       case UCLASS_MMC: {
+               struct efi_device_path_sd_mmc_path *sddp =
+                       dp_fill(buf, dev->parent);
+               struct mmc *mmc = mmc_get_mmc_dev(dev);
+               struct blk_desc *desc = mmc_get_blk_desc(mmc);
+
+               sddp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+               sddp->dp.sub_type = (desc->if_type == IF_TYPE_MMC) ?
+                       DEVICE_PATH_SUB_TYPE_MSG_MMC :
+                       DEVICE_PATH_SUB_TYPE_MSG_SD;
+               sddp->dp.length   = sizeof(*sddp);
+               sddp->slot_number = dev->seq;
+
+               return &sddp[1];
+       }
+#endif
+       case UCLASS_MASS_STORAGE:
+       case UCLASS_USB_HUB: {
+               struct efi_device_path_usb_class *udp =
+                       dp_fill(buf, dev->parent);
+               struct usb_device *udev = dev_get_parent_priv(dev);
+               struct usb_device_descriptor *desc = &udev->descriptor;
+
+               udp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+               udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS;
+               udp->dp.length   = sizeof(*udp);
+               udp->vendor_id   = desc->idVendor;
+               udp->product_id  = desc->idProduct;
+               udp->device_class    = desc->bDeviceClass;
+               udp->device_subclass = desc->bDeviceSubClass;
+               udp->device_protocol = desc->bDeviceProtocol;
+
+               return &udp[1];
+       }
+       default:
+               debug("unhandled device class: %s (%u)\n",
+                     dev->name, dev->driver->id);
+               return dp_fill(buf, dev->parent);
+       }
+}
+
+/* Construct a device-path from a device: */
+struct efi_device_path *efi_dp_from_dev(struct udevice *dev)
+{
+       void *buf, *start;
+
+       start = buf = dp_alloc(dp_size(dev) + sizeof(END));
+       buf = dp_fill(buf, dev);
+       *((struct efi_device_path *)buf) = END;
+
+       return start;
+}
+#endif
+
+static unsigned dp_part_size(struct blk_desc *desc, int part)
+{
+       unsigned dpsize;
+
+#ifdef CONFIG_BLK
+       dpsize = dp_size(desc->bdev->parent);
+#else
+       dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb);
+#endif
+
+       if (part == 0) /* the actual disk, not a partition */
+               return dpsize;
+
+       if (desc->part_type == PART_TYPE_ISO)
+               dpsize += sizeof(struct efi_device_path_cdrom_path);
+       else
+               dpsize += sizeof(struct efi_device_path_hard_drive_path);
+
+       return dpsize;
+}
+
+static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
+{
+       disk_partition_t info;
+
+#ifdef CONFIG_BLK
+       buf = dp_fill(buf, desc->bdev->parent);
+#else
+       /*
+        * We *could* make a more accurate path, by looking at if_type
+        * and handling all the different cases like we do for non-
+        * legacy (ie CONFIG_BLK=y) case.  But most important thing
+        * is just to have a unique device-path for if_type+devnum.
+        * So map things to a fictional USB device:
+        */
+       struct efi_device_path_usb *udp;
+
+       memcpy(buf, &ROOT, sizeof(ROOT));
+       buf += sizeof(ROOT);
+
+       udp = buf;
+       udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+       udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB;
+       udp->dp.length = sizeof(*udp);
+       udp->parent_port_number = desc->if_type;
+       udp->usb_interface = desc->devnum;
+       buf = &udp[1];
+#endif
+
+       if (part == 0) /* the actual disk, not a partition */
+               return buf;
+
+       part_get_info(desc, part, &info);
+
+       if (desc->part_type == PART_TYPE_ISO) {
+               struct efi_device_path_cdrom_path *cddp = buf;
+
+               cddp->boot_entry = part - 1;
+               cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+               cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH;
+               cddp->dp.length = sizeof(*cddp);
+               cddp->partition_start = info.start;
+               cddp->partition_end = info.size;
+
+               buf = &cddp[1];
+       } else {
+               struct efi_device_path_hard_drive_path *hddp = buf;
+
+               hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+               hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH;
+               hddp->dp.length = sizeof(*hddp);
+               hddp->partition_number = part - 1;
+               hddp->partition_start = info.start;
+               hddp->partition_end = info.size;
+               if (desc->part_type == PART_TYPE_EFI)
+                       hddp->partmap_type = 2;
+               else
+                       hddp->partmap_type = 1;
+               hddp->signature_type = desc->sig_type;
+               if (hddp->signature_type != 0)
+                       memcpy(hddp->partition_signature, &desc->guid_sig,
+                              sizeof(hddp->partition_signature));
+
+               buf = &hddp[1];
+       }
+
+       return buf;
+}
+
+
+/* Construct a device-path from a partition on a blk device: */
+struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
+{
+       void *buf, *start;
+
+       start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END));
+
+       buf = dp_part_fill(buf, desc, part);
+
+       *((struct efi_device_path *)buf) = END;
+
+       return start;
+}
+
+/* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */
+static void path_to_uefi(u16 *uefi, const char *path)
+{
+       while (*path) {
+               char c = *(path++);
+               if (c == '/')
+                       c = '\\';
+               *(uefi++) = c;
+       }
+       *uefi = '\0';
+}
+
+/*
+ * If desc is NULL, this creates a path with only the file component,
+ * otherwise it creates a full path with both device and file components
+ */
+struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
+               const char *path)
+{
+       struct efi_device_path_file_path *fp;
+       void *buf, *start;
+       unsigned dpsize = 0, fpsize;
+
+       if (desc)
+               dpsize = dp_part_size(desc, part);
+
+       fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+       dpsize += fpsize;
+
+       start = buf = dp_alloc(dpsize + sizeof(END));
+
+       if (desc)
+               buf = dp_part_fill(buf, desc, part);
+
+       /* add file-path: */
+       fp = buf;
+       fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+       fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
+       fp->dp.length = fpsize;
+       path_to_uefi(fp->str, path);
+       buf += fpsize;
+
+       *((struct efi_device_path *)buf) = END;
+
+       return start;
+}
+
+#ifdef CONFIG_NET
+struct efi_device_path *efi_dp_from_eth(void)
+{
+       struct efi_device_path_mac_addr *ndp;
+       void *buf, *start;
+       unsigned dpsize = 0;
+
+       assert(eth_get_dev());
+
+#ifdef CONFIG_DM_ETH
+       dpsize += dp_size(eth_get_dev());
+#else
+       dpsize += sizeof(ROOT);
+#endif
+       dpsize += sizeof(*ndp);
+
+       start = buf = dp_alloc(dpsize + sizeof(END));
+
+#ifdef CONFIG_DM_ETH
+       buf = dp_fill(buf, eth_get_dev());
+#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);
+       memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
+       buf = &ndp[1];
+
+       *((struct efi_device_path *)buf) = END;
+
+       return start;
+}
+#endif
+
+/*
+ * Helper to split a full device path (containing both device and file
+ * parts) into it's constituent parts.
+ */
+void efi_dp_split_file_path(struct efi_device_path *full_path,
+                           struct efi_device_path **device_path,
+                           struct efi_device_path **file_path)
+{
+       struct efi_device_path *p, *dp, *fp;
+
+       dp = efi_dp_dup(full_path);
+       p = dp;
+       while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH))
+               p = efi_dp_next(p);
+       fp = efi_dp_dup(p);
+
+       p->type = DEVICE_PATH_TYPE_END;
+       p->sub_type = DEVICE_PATH_SUB_TYPE_END;
+       p->length = sizeof(*p);
+
+       *device_path = dp;
+       *file_path = fp;
+}
index 4b2f43f0c8a65e037eed759426ffa8cb13ff66d4..1a5ef3919bac92035c7a1a112b08a4313a8d6c71 100644 (file)
 const efi_guid_t efi_guid_device_path_to_text_protocol =
                EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
 
-static uint16_t *efi_convert_device_node_to_text(
-               struct efi_device_path_protocol *device_node,
-               bool display_only,
-               bool allow_shortcuts)
+static char *dp_unknown(char *s, struct efi_device_path *dp)
 {
-       unsigned long buffer_size;
-       efi_status_t r;
-       uint16_t *buffer = NULL;
-       int i;
+       s += sprintf(s, "/UNKNOWN(%04x,%04x)", dp->type, dp->sub_type);
+       return s;
+}
 
-       switch (device_node->type) {
-       case DEVICE_PATH_TYPE_END:
-               return NULL;
-       case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
-               switch (device_node->sub_type) {
-               case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: {
-                       struct efi_device_path_mac_addr *dp =
-                               (struct efi_device_path_mac_addr *)device_node;
-
-                       if (dp->if_type != 0 && dp->if_type != 1)
-                               break;
-                       r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
-                                             2 * MAC_OUTPUT_LEN,
-                                             (void **)&buffer);
-                       if (r != EFI_SUCCESS)
-                               return NULL;
-                       sprintf((char *)buffer,
-                               "MAC(%02x%02x%02x%02x%02x%02x,0x%1x)",
-                               dp->mac.addr[0], dp->mac.addr[1],
-                               dp->mac.addr[2], dp->mac.addr[3],
-                               dp->mac.addr[4], dp->mac.addr[5],
-                               dp->if_type);
-                       for (i = MAC_OUTPUT_LEN - 1; i >= 0; --i)
-                               buffer[i] = ((uint8_t *)buffer)[i];
+static char *dp_hardware(char *s, struct efi_device_path *dp)
+{
+       switch (dp->sub_type) {
+       case DEVICE_PATH_SUB_TYPE_VENDOR: {
+               struct efi_device_path_vendor *vdp =
+                       (struct efi_device_path_vendor *)dp;
+               s += sprintf(s, "/VenHw(%pUl)", &vdp->guid);
+               break;
+       }
+       default:
+               s = dp_unknown(s, dp);
+               break;
+       }
+       return s;
+}
+
+static char *dp_acpi(char *s, struct efi_device_path *dp)
+{
+       switch (dp->sub_type) {
+       case DEVICE_PATH_SUB_TYPE_ACPI_DEVICE: {
+               struct efi_device_path_acpi_path *adp =
+                       (struct efi_device_path_acpi_path *)dp;
+               s += sprintf(s, "/Acpi(PNP%04x", EISA_PNP_NUM(adp->hid));
+               if (adp->uid)
+                       s += sprintf(s, ",%d", adp->uid);
+               s += sprintf(s, ")");
+               break;
+       }
+       default:
+               s = dp_unknown(s, dp);
+               break;
+       }
+       return s;
+}
+
+static char *dp_msging(char *s, struct efi_device_path *dp)
+{
+       switch (dp->sub_type) {
+       case DEVICE_PATH_SUB_TYPE_MSG_USB: {
+               struct efi_device_path_usb *udp =
+                       (struct efi_device_path_usb *)dp;
+               s += sprintf(s, "/Usb(0x%x,0x%x)", udp->parent_port_number,
+                            udp->usb_interface);
+               break;
+       }
+       case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: {
+               struct efi_device_path_mac_addr *mdp =
+                       (struct efi_device_path_mac_addr *)dp;
+
+               if (mdp->if_type != 0 && mdp->if_type != 1)
                        break;
-                       }
-               }
+
+               s += sprintf(s, "/MAC(%02x%02x%02x%02x%02x%02x,0x%1x)",
+                       mdp->mac.addr[0], mdp->mac.addr[1],
+                       mdp->mac.addr[2], mdp->mac.addr[3],
+                       mdp->mac.addr[4], mdp->mac.addr[5],
+                       mdp->if_type);
+
+               break;
+       }
+       case DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS: {
+               struct efi_device_path_usb_class *ucdp =
+                       (struct efi_device_path_usb_class *)dp;
+
+               s += sprintf(s, "/USBClass(%x,%x,%x,%x,%x)",
+                       ucdp->vendor_id, ucdp->product_id,
+                       ucdp->device_class, ucdp->device_subclass,
+                       ucdp->device_protocol);
+
+               break;
+       }
+       case DEVICE_PATH_SUB_TYPE_MSG_SD:
+       case DEVICE_PATH_SUB_TYPE_MSG_MMC: {
+               const char *typename =
+                       (dp->sub_type == DEVICE_PATH_SUB_TYPE_MSG_SD) ?
+                                       "SDCard" : "MMC";
+               struct efi_device_path_sd_mmc_path *sddp =
+                       (struct efi_device_path_sd_mmc_path *)dp;
+               s += sprintf(s, "/%s(Slot%u)", typename, sddp->slot_number);
+               break;
+       }
+       default:
+               s = dp_unknown(s, dp);
                break;
-       case DEVICE_PATH_TYPE_MEDIA_DEVICE:
-               switch (device_node->sub_type) {
-               case DEVICE_PATH_SUB_TYPE_FILE_PATH:
-                       buffer_size = device_node->length - 4;
-                       r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
-                                             buffer_size, (void **) &buffer);
-                       if (r != EFI_SUCCESS)
-                               return NULL;
-                       memcpy(buffer, device_node->data, buffer_size);
+       }
+       return s;
+}
+
+static char *dp_media(char *s, struct efi_device_path *dp)
+{
+       switch (dp->sub_type) {
+       case DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH: {
+               struct efi_device_path_hard_drive_path *hddp =
+                       (struct efi_device_path_hard_drive_path *)dp;
+               void *sig = hddp->partition_signature;
+
+               switch (hddp->signature_type) {
+               case SIG_TYPE_MBR:
+                       s += sprintf(s, "/HD(Part%d,Sig%08x)",
+                                    hddp->partition_number,
+                                    *(uint32_t *)sig);
                        break;
+               case SIG_TYPE_GUID:
+                       s += sprintf(s, "/HD(Part%d,Sig%pUl)",
+                                    hddp->partition_number, sig);
+               default:
+                       s += sprintf(s, "/HD(Part%d,MBRType=%02x,SigType=%02x)",
+                                    hddp->partition_number, hddp->partmap_type,
+                                    hddp->signature_type);
                }
+
+               break;
+       }
+       case DEVICE_PATH_SUB_TYPE_CDROM_PATH: {
+               struct efi_device_path_cdrom_path *cddp =
+                       (struct efi_device_path_cdrom_path *)dp;
+               s += sprintf(s, "/CDROM(0x%x)", cddp->boot_entry);
+               break;
+       }
+       case DEVICE_PATH_SUB_TYPE_FILE_PATH: {
+               struct efi_device_path_file_path *fp =
+                       (struct efi_device_path_file_path *)dp;
+               int slen = (dp->length - sizeof(*dp)) / 2;
+               s += sprintf(s, "/%-*ls", slen, fp->str);
                break;
        }
+       default:
+               s = dp_unknown(s, dp);
+               break;
+       }
+       return s;
+}
 
-       /*
-        * For all node types that we do not yet support return
-        * 'UNKNOWN(type,subtype)'.
-        */
-       if (!buffer) {
-               r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
-                                     2 * UNKNOWN_OUTPUT_LEN,
-                                     (void **)&buffer);
-               if (r != EFI_SUCCESS)
-                       return NULL;
-               sprintf((char *)buffer,
-                       "UNKNOWN(%04x,%04x)",
-                       device_node->type,
-                       device_node->sub_type);
-               for (i = UNKNOWN_OUTPUT_LEN - 1; i >= 0; --i)
-                       buffer[i] = ((uint8_t *)buffer)[i];
+static uint16_t *efi_convert_device_node_to_text(
+               struct efi_device_path *dp,
+               bool display_only,
+               bool allow_shortcuts)
+{
+       unsigned long len;
+       efi_status_t r;
+       char buf[512];  /* this ought be be big enough for worst case */
+       char *str = buf;
+       uint16_t *out;
+
+       while (dp) {
+               switch (dp->type) {
+               case DEVICE_PATH_TYPE_HARDWARE_DEVICE:
+                       str = dp_hardware(str, dp);
+                       break;
+               case DEVICE_PATH_TYPE_ACPI_DEVICE:
+                       str = dp_acpi(str, dp);
+                       break;
+               case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
+                       str = dp_msging(str, dp);
+                       break;
+               case DEVICE_PATH_TYPE_MEDIA_DEVICE:
+                       str = dp_media(str, dp);
+                       break;
+               default:
+                       str = dp_unknown(str, dp);
+               }
+
+               dp = efi_dp_next(dp);
        }
 
-       return buffer;
+       *str++ = '\0';
+
+       len = str - buf;
+       r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, 2 * len, (void **)&out);
+       if (r != EFI_SUCCESS)
+               return NULL;
+
+       ascii2unicode(out, buf);
+       out[len - 1] = 0;
+
+       return out;
 }
 
+/* helper for debug prints.. efi_free_pool() the result. */
+uint16_t *efi_dp_str(struct efi_device_path *dp)
+{
+       return efi_convert_device_node_to_text(dp, true, true);
+}
+
+
 static uint16_t EFIAPI *efi_convert_device_node_to_text_ext(
-               struct efi_device_path_protocol *device_node,
+               struct efi_device_path *device_node,
                bool display_only,
                bool allow_shortcuts)
 {
@@ -105,7 +223,7 @@ static uint16_t EFIAPI *efi_convert_device_node_to_text_ext(
 }
 
 static uint16_t EFIAPI *efi_convert_device_path_to_text(
-               struct efi_device_path_protocol *device_path,
+               struct efi_device_path *device_path,
                bool display_only,
                bool allow_shortcuts)
 {
index ed06485e334b771b2e2e8872615952b19735c0f2..eb9ce772d1d6fe00830e9bbc2732917e8136b8af 100644 (file)
@@ -28,11 +28,15 @@ struct efi_disk_obj {
        /* EFI Interface Media descriptor struct, referenced by ops */
        struct efi_block_io_media media;
        /* EFI device path to this block device */
-       struct efi_device_path_file_path *dp;
+       struct efi_device_path *dp;
+       /* partition # */
+       unsigned int part;
+       /* handle to filesys proto (for partition objects) */
+       struct efi_simple_file_system_protocol *volume;
        /* Offset into disk for simple partitions */
        lbaint_t offset;
        /* Internal block device */
-       const struct blk_desc *desc;
+       struct blk_desc *desc;
 };
 
 static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this,
@@ -47,7 +51,7 @@ enum efi_disk_direction {
        EFI_DISK_WRITE,
 };
 
-static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this,
+static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
                        u32 media_id, u64 lba, unsigned long buffer_size,
                        void *buffer, enum efi_disk_direction direction)
 {
@@ -170,28 +174,58 @@ static const struct efi_block_io block_io_disk_template = {
        .flush_blocks = &efi_disk_flush_blocks,
 };
 
+/*
+ * Find filesystem from a device-path.  The passed in path 'p' probably
+ * contains one or more /File(name) nodes, so the comparison stops at
+ * the first /File() node, and returns the pointer to that via 'rp'.
+ * This is mostly intended to be a helper to map a device-path to an
+ * efi_file_handle object.
+ */
+struct efi_simple_file_system_protocol *
+efi_fs_from_path(struct efi_device_path *fp)
+{
+       struct efi_object *efiobj;
+       struct efi_disk_obj *diskobj;
+
+       efiobj = efi_dp_find_obj(fp, NULL);
+       if (!efiobj)
+               return NULL;
+
+       diskobj = container_of(efiobj, struct efi_disk_obj, parent);
+
+       return diskobj->volume;
+}
+
 static void efi_disk_add_dev(const char *name,
                             const char *if_typename,
-                            const struct blk_desc *desc,
+                            struct blk_desc *desc,
                             int dev_index,
-                            lbaint_t offset)
+                            lbaint_t offset,
+                            unsigned int part)
 {
        struct efi_disk_obj *diskobj;
-       struct efi_device_path_file_path *dp;
-       int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2);
 
        /* Don't add empty devices */
        if (!desc->lba)
                return;
 
-       diskobj = calloc(1, objlen);
+       diskobj = calloc(1, sizeof(*diskobj));
 
        /* Fill in object data */
-       dp = (void *)&diskobj[1];
+       diskobj->dp = efi_dp_from_part(desc, part);
+       diskobj->part = part;
        diskobj->parent.protocols[0].guid = &efi_block_io_guid;
        diskobj->parent.protocols[0].protocol_interface = &diskobj->ops;
        diskobj->parent.protocols[1].guid = &efi_guid_device_path;
-       diskobj->parent.protocols[1].protocol_interface = dp;
+       diskobj->parent.protocols[1].protocol_interface = diskobj->dp;
+       if (part >= 1) {
+               diskobj->volume = efi_simple_file_system(desc, part,
+                                                        diskobj->dp);
+               diskobj->parent.protocols[2].guid =
+                       &efi_simple_file_system_protocol_guid;
+               diskobj->parent.protocols[2].protocol_interface =
+                       diskobj->volume;
+       }
        diskobj->parent.handle = diskobj;
        diskobj->ops = block_io_disk_template;
        diskobj->ifname = if_typename;
@@ -207,17 +241,6 @@ static void efi_disk_add_dev(const char *name,
        diskobj->media.last_block = desc->lba - offset;
        diskobj->ops.media = &diskobj->media;
 
-       /* Fill in device path */
-       diskobj->dp = dp;
-       dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
-       dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
-       dp[0].dp.length = sizeof(*dp);
-       ascii2unicode(dp[0].str, name);
-
-       dp[1].dp.type = DEVICE_PATH_TYPE_END;
-       dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END;
-       dp[1].dp.length = sizeof(*dp);
-
        /* Hook up to the device list */
        list_add_tail(&diskobj->parent.link, &efi_obj_list);
 }
@@ -236,14 +259,18 @@ static int efi_disk_create_eltorito(struct blk_desc *desc,
        if (desc->part_type != PART_TYPE_ISO)
                return 0;
 
+       /* and devices for each partition: */
        while (!part_get_info(desc, part, &info)) {
                snprintf(devname, sizeof(devname), "%s:%d", pdevname,
                         part);
                efi_disk_add_dev(devname, if_typename, desc, diskid,
-                                info.start);
+                                info.start, part);
                part++;
                disks++;
        }
+
+       /* ... and add block device: */
+       efi_disk_add_dev(devname, if_typename, desc, diskid, 0, 0);
 #endif
 
        return disks;
@@ -271,9 +298,22 @@ int efi_disk_register(void)
             uclass_next_device_check(&dev)) {
                struct blk_desc *desc = dev_get_uclass_platdata(dev);
                const char *if_typename = dev->driver->name;
+               disk_partition_t info;
+               int part = 1;
 
                printf("Scanning disk %s...\n", dev->name);
-               efi_disk_add_dev(dev->name, if_typename, desc, desc->devnum, 0);
+
+               /* add devices for each partition: */
+               while (!part_get_info(desc, part, &info)) {
+                       efi_disk_add_dev(dev->name, if_typename, desc,
+                                        desc->devnum, 0, part);
+                       part++;
+               }
+
+               /* ... and add block device: */
+               efi_disk_add_dev(dev->name, if_typename, desc,
+                                desc->devnum, 0, 0);
+
                disks++;
 
                /*
@@ -309,7 +349,7 @@ int efi_disk_register(void)
 
                        snprintf(devname, sizeof(devname), "%s%d",
                                 if_typename, i);
-                       efi_disk_add_dev(devname, if_typename, desc, i, 0);
+                       efi_disk_add_dev(devname, if_typename, desc, i, 0, 0);
                        disks++;
 
                        /*
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
new file mode 100644 (file)
index 0000000..52a4e74
--- /dev/null
@@ -0,0 +1,560 @@
+/*
+ *  EFI utils
+ *
+ *  Copyright (c) 2017 Rob Clark
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <efi_loader.h>
+#include <malloc.h>
+#include <fs.h>
+
+struct file_system {
+       struct efi_simple_file_system_protocol base;
+       struct efi_device_path *dp;
+       struct blk_desc *desc;
+       int part;
+};
+#define to_fs(x) container_of(x, struct file_system, base)
+
+struct file_handle {
+       struct efi_file_handle base;
+       struct file_system *fs;
+       loff_t offset;       /* current file position/cursor */
+       int isdir;
+
+       /* for reading a directory: */
+       struct fs_dir_stream *dirs;
+       struct fs_dirent *dent;
+
+       char path[0];
+};
+#define to_fh(x) container_of(x, struct file_handle, base)
+
+static const struct efi_file_handle efi_file_handle_protocol;
+
+static char *basename(struct file_handle *fh)
+{
+       char *s = strrchr(fh->path, '/');
+       if (s)
+               return s + 1;
+       return fh->path;
+}
+
+static int set_blk_dev(struct file_handle *fh)
+{
+       return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
+}
+
+static int is_dir(struct file_handle *fh)
+{
+       struct fs_dir_stream *dirs;
+
+       set_blk_dev(fh);
+       dirs = fs_opendir(fh->path);
+       if (!dirs)
+               return 0;
+
+       fs_closedir(dirs);
+
+       return 1;
+}
+
+/*
+ * Normalize a path which may include either back or fwd slashes,
+ * double slashes, . or .. entries in the path, etc.
+ */
+static int sanitize_path(char *path)
+{
+       char *p;
+
+       /* backslash to slash: */
+       p = path;
+       while ((p = strchr(p, '\\')))
+               *p++ = '/';
+
+       /* handle double-slashes: */
+       p = path;
+       while ((p = strstr(p, "//"))) {
+               char *src = p + 1;
+               memmove(p, src, strlen(src) + 1);
+       }
+
+       /* handle extra /.'s */
+       p = path;
+       while ((p = strstr(p, "/."))) {
+               /*
+                * You'd be tempted to do this *after* handling ".."s
+                * below to avoid having to check if "/." is start of
+                * a "/..", but that won't have the correct results..
+                * for example, "/foo/./../bar" would get resolved to
+                * "/foo/bar" if you did these two passes in the other
+                * order
+                */
+               if (p[2] == '.') {
+                       p += 2;
+                       continue;
+               }
+               char *src = p + 2;
+               memmove(p, src, strlen(src) + 1);
+       }
+
+       /* handle extra /..'s: */
+       p = path;
+       while ((p = strstr(p, "/.."))) {
+               char *src = p + 3;
+
+               p--;
+
+               /* find beginning of previous path entry: */
+               while (true) {
+                       if (p < path)
+                               return -1;
+                       if (*p == '/')
+                               break;
+                       p--;
+               }
+
+               memmove(p, src, strlen(src) + 1);
+       }
+
+       return 0;
+}
+
+/* NOTE: despite what you would expect, 'file_name' is actually a path.
+ * With windoze style backlashes, ofc.
+ */
+static struct efi_file_handle *file_open(struct file_system *fs,
+               struct file_handle *parent, s16 *file_name, u64 mode)
+{
+       struct file_handle *fh;
+       char f0[MAX_UTF8_PER_UTF16] = {0};
+       int plen = 0;
+       int flen = 0;
+
+       if (file_name) {
+               utf16_to_utf8((u8 *)f0, (u16 *)file_name, 1);
+               flen = utf16_strlen((u16 *)file_name);
+       }
+
+       /* we could have a parent, but also an absolute path: */
+       if (f0[0] == '\\') {
+               plen = 0;
+       } else if (parent) {
+               plen = strlen(parent->path) + 1;
+       }
+
+       /* +2 is for null and '/' */
+       fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
+
+       fh->base = efi_file_handle_protocol;
+       fh->fs = fs;
+
+       if (parent) {
+               char *p = fh->path;
+
+               if (plen > 0) {
+                       strcpy(p, parent->path);
+                       p += plen - 1;
+                       *p++ = '/';
+               }
+
+               utf16_to_utf8((u8 *)p, (u16 *)file_name, flen);
+
+               if (sanitize_path(fh->path))
+                       goto error;
+
+               /* check if file exists: */
+               if (set_blk_dev(fh))
+                       goto error;
+
+               if (!((mode & EFI_FILE_MODE_CREATE) || fs_exists(fh->path)))
+                       goto error;
+
+               /* figure out if file is a directory: */
+               fh->isdir = is_dir(fh);
+       } else {
+               fh->isdir = 1;
+               strcpy(fh->path, "");
+       }
+
+       return &fh->base;
+
+error:
+       free(fh);
+       return NULL;
+}
+
+static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
+               struct efi_file_handle **new_handle,
+               s16 *file_name, u64 open_mode, u64 attributes)
+{
+       struct file_handle *fh = to_fh(file);
+
+       EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, file_name,
+                 open_mode, attributes);
+
+       *new_handle = file_open(fh->fs, fh, file_name, open_mode);
+       if (!*new_handle)
+               return EFI_EXIT(EFI_NOT_FOUND);
+
+       return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t file_close(struct file_handle *fh)
+{
+       fs_closedir(fh->dirs);
+       free(fh);
+       return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
+{
+       struct file_handle *fh = to_fh(file);
+       EFI_ENTRY("%p", file);
+       return EFI_EXIT(file_close(fh));
+}
+
+static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
+{
+       struct file_handle *fh = to_fh(file);
+       EFI_ENTRY("%p", file);
+       file_close(fh);
+       return EFI_EXIT(EFI_WARN_DELETE_FAILURE);
+}
+
+static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
+               void *buffer)
+{
+       loff_t actread;
+
+       if (fs_read(fh->path, (ulong)buffer, fh->offset,
+                   *buffer_size, &actread))
+               return EFI_DEVICE_ERROR;
+
+       *buffer_size = actread;
+       fh->offset += actread;
+
+       return EFI_SUCCESS;
+}
+
+static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
+               void *buffer)
+{
+       struct efi_file_info *info = buffer;
+       struct fs_dirent *dent;
+       unsigned int required_size;
+
+       if (!fh->dirs) {
+               assert(fh->offset == 0);
+               fh->dirs = fs_opendir(fh->path);
+               if (!fh->dirs)
+                       return EFI_DEVICE_ERROR;
+       }
+
+       /*
+        * So this is a bit awkward.  Since fs layer is stateful and we
+        * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
+        * we might have to return without consuming the dent.. so we
+        * have to stash it for next call.
+        */
+       if (fh->dent) {
+               dent = fh->dent;
+               fh->dent = NULL;
+       } else {
+               dent = fs_readdir(fh->dirs);
+       }
+
+
+       if (!dent) {
+               /* no more files in directory: */
+               /* workaround shim.efi bug/quirk.. as find_boot_csv()
+                * loops through directory contents, it initially calls
+                * read w/ zero length buffer to find out how much mem
+                * to allocate for the EFI_FILE_INFO, then allocates,
+                * and then calls a 2nd time.  If we return size of
+                * zero the first time, it happily passes that to
+                * AllocateZeroPool(), and when that returns NULL it
+                * thinks it is EFI_OUT_OF_RESOURCES.  So on first
+                * call return a non-zero size:
+                */
+               if (*buffer_size == 0)
+                       *buffer_size = sizeof(*info);
+               else
+                       *buffer_size = 0;
+               return EFI_SUCCESS;
+       }
+
+       /* check buffer size: */
+       required_size = sizeof(*info) + 2 * (strlen(dent->name) + 1);
+       if (*buffer_size < required_size) {
+               *buffer_size = required_size;
+               fh->dent = dent;
+               return EFI_BUFFER_TOO_SMALL;
+       }
+
+       *buffer_size = required_size;
+       memset(info, 0, required_size);
+
+       info->size = required_size;
+       info->file_size = dent->size;
+       info->physical_size = dent->size;
+
+       if (dent->type == FS_DT_DIR)
+               info->attribute |= EFI_FILE_DIRECTORY;
+
+       ascii2unicode((u16 *)info->file_name, dent->name);
+
+       fh->offset++;
+
+       return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
+               u64 *buffer_size, void *buffer)
+{
+       struct file_handle *fh = to_fh(file);
+       efi_status_t ret = EFI_SUCCESS;
+
+       EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
+
+       if (set_blk_dev(fh)) {
+               ret = EFI_DEVICE_ERROR;
+               goto error;
+       }
+
+       if (fh->isdir)
+               ret = dir_read(fh, buffer_size, buffer);
+       else
+               ret = file_read(fh, buffer_size, buffer);
+
+error:
+       return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
+               u64 *buffer_size, void *buffer)
+{
+       struct file_handle *fh = to_fh(file);
+       efi_status_t ret = EFI_SUCCESS;
+       loff_t actwrite;
+
+       EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
+
+       if (set_blk_dev(fh)) {
+               ret = EFI_DEVICE_ERROR;
+               goto error;
+       }
+
+       if (fs_write(fh->path, (ulong)buffer, fh->offset, *buffer_size,
+                    &actwrite)) {
+               ret = EFI_DEVICE_ERROR;
+               goto error;
+       }
+
+       *buffer_size = actwrite;
+       fh->offset += actwrite;
+
+error:
+       return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
+               u64 *pos)
+{
+       struct file_handle *fh = to_fh(file);
+       EFI_ENTRY("%p, %p", file, pos);
+       *pos = fh->offset;
+       return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
+               u64 pos)
+{
+       struct file_handle *fh = to_fh(file);
+       efi_status_t ret = EFI_SUCCESS;
+
+       EFI_ENTRY("%p, %llu", file, pos);
+
+       if (fh->isdir) {
+               if (pos != 0) {
+                       ret = EFI_UNSUPPORTED;
+                       goto error;
+               }
+               fs_closedir(fh->dirs);
+               fh->dirs = NULL;
+       }
+
+       if (pos == ~0ULL) {
+               loff_t file_size;
+
+               if (set_blk_dev(fh)) {
+                       ret = EFI_DEVICE_ERROR;
+                       goto error;
+               }
+
+               if (fs_size(fh->path, &file_size)) {
+                       ret = EFI_DEVICE_ERROR;
+                       goto error;
+               }
+
+               pos = file_size;
+       }
+
+       fh->offset = pos;
+
+error:
+       return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
+               efi_guid_t *info_type, u64 *buffer_size, void *buffer)
+{
+       struct file_handle *fh = to_fh(file);
+       efi_status_t ret = EFI_SUCCESS;
+
+       EFI_ENTRY("%p, %p, %p, %p", file, info_type, buffer_size, buffer);
+
+       if (!guidcmp(info_type, &efi_file_info_guid)) {
+               struct efi_file_info *info = buffer;
+               char *filename = basename(fh);
+               unsigned int required_size;
+               loff_t file_size;
+
+               /* check buffer size: */
+               required_size = sizeof(*info) + 2 * (strlen(filename) + 1);
+               if (*buffer_size < required_size) {
+                       *buffer_size = required_size;
+                       ret = EFI_BUFFER_TOO_SMALL;
+                       goto error;
+               }
+
+               if (set_blk_dev(fh)) {
+                       ret = EFI_DEVICE_ERROR;
+                       goto error;
+               }
+
+               if (fs_size(fh->path, &file_size)) {
+                       ret = EFI_DEVICE_ERROR;
+                       goto error;
+               }
+
+               memset(info, 0, required_size);
+
+               info->size = required_size;
+               info->file_size = file_size;
+               info->physical_size = file_size;
+
+               if (fh->isdir)
+                       info->attribute |= EFI_FILE_DIRECTORY;
+
+               ascii2unicode((u16 *)info->file_name, filename);
+       } else {
+               ret = EFI_UNSUPPORTED;
+       }
+
+error:
+       return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
+               efi_guid_t *info_type, u64 buffer_size, void *buffer)
+{
+       EFI_ENTRY("%p, %p, %llu, %p", file, info_type, buffer_size, buffer);
+       return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
+{
+       EFI_ENTRY("%p", file);
+       return EFI_EXIT(EFI_SUCCESS);
+}
+
+static const struct efi_file_handle efi_file_handle_protocol = {
+       .rev = EFI_FILE_PROTOCOL_REVISION,
+       .open = efi_file_open,
+       .close = efi_file_close,
+       .delete = efi_file_delete,
+       .read = efi_file_read,
+       .write = efi_file_write,
+       .getpos = efi_file_getpos,
+       .setpos = efi_file_setpos,
+       .getinfo = efi_file_getinfo,
+       .setinfo = efi_file_setinfo,
+       .flush = efi_file_flush,
+};
+
+struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
+{
+       struct efi_simple_file_system_protocol *v;
+       struct efi_file_handle *f;
+       efi_status_t ret;
+
+       v = efi_fs_from_path(fp);
+       if (!v)
+               return NULL;
+
+       EFI_CALL(ret = v->open_volume(v, &f));
+       if (ret != EFI_SUCCESS)
+               return NULL;
+
+       /* skip over device-path nodes before the file path: */
+       while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
+               fp = efi_dp_next(fp);
+
+       while (fp) {
+               struct efi_device_path_file_path *fdp =
+                       container_of(fp, struct efi_device_path_file_path, dp);
+               struct efi_file_handle *f2;
+
+               if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
+                       printf("bad file path!\n");
+                       f->close(f);
+                       return NULL;
+               }
+
+               EFI_CALL(ret = f->open(f, &f2, (s16 *)fdp->str,
+                                      EFI_FILE_MODE_READ, 0));
+               if (ret != EFI_SUCCESS)
+                       return NULL;
+
+               fp = efi_dp_next(fp);
+
+               EFI_CALL(f->close(f));
+               f = f2;
+       }
+
+       return f;
+}
+
+static efi_status_t EFIAPI
+efi_open_volume(struct efi_simple_file_system_protocol *this,
+               struct efi_file_handle **root)
+{
+       struct file_system *fs = to_fs(this);
+
+       EFI_ENTRY("%p, %p", this, root);
+
+       *root = file_open(fs, NULL, NULL, 0);
+
+       return EFI_EXIT(EFI_SUCCESS);
+}
+
+struct efi_simple_file_system_protocol *
+efi_simple_file_system(struct blk_desc *desc, int part,
+                      struct efi_device_path *dp)
+{
+       struct file_system *fs;
+
+       fs = calloc(1, sizeof(*fs));
+       fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
+       fs->base.open_volume = efi_open_volume;
+       fs->desc = desc;
+       fs->part = part;
+       fs->dp = dp;
+
+       return &fs->base;
+}
index f961407f50ab8e59301aebdf30db57115d170040..af29cc4f04e9849c1ca5a84805a2eab01f28ed02 100644 (file)
 
 DECLARE_GLOBAL_DATA_PTR;
 
+const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
 const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID;
 const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
+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 efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
                        unsigned long rel_size, void *efi_reloc)
@@ -90,6 +94,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
        unsigned long virt_size = 0;
        bool can_run_nt64 = true;
        bool can_run_nt32 = true;
+       uint16_t image_type;
 
 #if defined(CONFIG_ARM64)
        can_run_nt32 = false;
@@ -135,6 +140,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;
+               image_type = opt->Subsystem;
        } else if (can_run_nt32 &&
                   (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
                IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
@@ -148,12 +154,32 @@ 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;
+               image_type = opt->Subsystem;
        } else {
                printf("%s: Invalid optional header magic %x\n", __func__,
                       nt->OptionalHeader.Magic);
                return NULL;
        }
 
+       switch (image_type) {
+       case IMAGE_SUBSYSTEM_EFI_APPLICATION:
+               loaded_image_info->image_code_type = EFI_LOADER_CODE;
+               loaded_image_info->image_data_type = EFI_LOADER_DATA;
+               break;
+       case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
+               loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE;
+               loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA;
+               break;
+       case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
+       case IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER:
+               loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE;
+               loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA;
+               break;
+       default:
+               printf("%s: invalid image type: %u\n", __func__, image_type);
+               break;
+       }
+
        /* Load sections into RAM */
        for (i = num_sections - 1; i >= 0; i--) {
                IMAGE_SECTION_HEADER *sec = &sections[i];
index 9e079f1fa3a66cc301cd1bf039b7896f97f473f4..d47759e08edc62104db13b0c4cef4ae9234abf82 100644 (file)
@@ -43,7 +43,7 @@ void *efi_bounce_buffer;
  */
 struct efi_pool_allocation {
        u64 num_pages;
-       char data[];
+       char data[] __aligned(ARCH_DMA_MINALIGN);
 };
 
 /*
@@ -356,7 +356,8 @@ efi_status_t efi_allocate_pool(int pool_type, unsigned long size,
 {
        efi_status_t r;
        efi_physical_addr_t t;
-       u64 num_pages = (size + sizeof(u64) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
+       u64 num_pages = (size + sizeof(struct efi_pool_allocation) +
+                        EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
 
        if (size == 0) {
                *buffer = NULL;
index 0b949d86e8d5386dfa14c51dd30806994a40d51b..91f1e4a69e15aad0c79e50ad9edbe6473e8ae7ae 100644 (file)
@@ -26,9 +26,6 @@ struct efi_net_obj {
        /* EFI Interface callback struct for network */
        struct efi_simple_network net;
        struct efi_simple_network_mode net_mode;
-       /* Device path to the network adapter */
-       struct efi_device_path_mac_addr dp_mac;
-       struct efi_device_path_file_path dp_end;
        /* PXE struct to transmit dhcp data */
        struct efi_pxe pxe;
        struct efi_pxe_mode pxe_mode;
@@ -210,19 +207,9 @@ void efi_net_set_dhcp_ack(void *pkt, int len)
 }
 
 /* This gets called from do_bootefi_exec(). */
-int efi_net_register(void **handle)
+int efi_net_register(void)
 {
        struct efi_net_obj *netobj;
-       struct efi_device_path_mac_addr dp_net = {
-               .dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE,
-               .dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR,
-               .dp.length = sizeof(dp_net),
-       };
-       struct efi_device_path_file_path dp_end = {
-               .dp.type = DEVICE_PATH_TYPE_END,
-               .dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
-               .dp.length = sizeof(dp_end),
-       };
 
        if (!eth_get_dev()) {
                /* No eth device active, don't expose any */
@@ -236,7 +223,8 @@ int efi_net_register(void **handle)
        netobj->parent.protocols[0].guid = &efi_net_guid;
        netobj->parent.protocols[0].protocol_interface = &netobj->net;
        netobj->parent.protocols[1].guid = &efi_guid_device_path;
-       netobj->parent.protocols[1].protocol_interface = &netobj->dp_mac;
+       netobj->parent.protocols[1].protocol_interface =
+               efi_dp_from_eth();
        netobj->parent.protocols[2].guid = &efi_pxe_guid;
        netobj->parent.protocols[2].protocol_interface = &netobj->pxe;
        netobj->parent.handle = &netobj->net;
@@ -255,9 +243,6 @@ int efi_net_register(void **handle)
        netobj->net.receive = efi_net_receive;
        netobj->net.mode = &netobj->net_mode;
        netobj->net_mode.state = EFI_NETWORK_STARTED;
-       netobj->dp_mac = dp_net;
-       netobj->dp_end = dp_end;
-       memcpy(netobj->dp_mac.mac.addr, eth_get_ethaddr(), 6);
        memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
        netobj->net_mode.max_packet_size = PKTSIZE;
 
@@ -268,8 +253,5 @@ int efi_net_register(void **handle)
        /* Hook net up to the device list */
        list_add_tail(&netobj->parent.link, &efi_obj_list);
 
-       if (handle)
-               *handle = &netobj->net;
-
        return 0;
 }
index ad7f3754bd0970b5209f756d5f1d7ce4883e4587..8104e08c466ca1853bcc6461cb696c99b15814b7 100644 (file)
@@ -184,7 +184,16 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
                /* Clean up system table */
                .ptr = &systab.boottime,
                .patchto = NULL,
-       },
+       }, {
+               .ptr = &efi_runtime_services.get_variable,
+               .patchto = &efi_device_error,
+       }, {
+               .ptr = &efi_runtime_services.get_next_variable,
+               .patchto = &efi_device_error,
+       }, {
+               .ptr = &efi_runtime_services.set_variable,
+               .patchto = &efi_device_error,
+       }
 };
 
 static bool efi_runtime_tobedetached(void *p)
@@ -243,7 +252,8 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
 
                /* Check if the relocation is inside bounds */
                if (map && ((newaddr < map->virtual_start) ||
-                   newaddr > (map->virtual_start + (map->num_pages << 12)))) {
+                   newaddr > (map->virtual_start +
+                             (map->num_pages << EFI_PAGE_SHIFT)))) {
                        if (!efi_runtime_tobedetached(p))
                                printf("U-Boot EFI: Relocation at %p is out of "
                                       "range (%lx)\n", p, newaddr);
@@ -269,7 +279,8 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
                        uint32_t descriptor_version,
                        struct efi_mem_desc *virtmap)
 {
-       ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL;
+       ulong runtime_start = (ulong)&__efi_runtime_start &
+                             ~(ulong)EFI_PAGE_MASK;
        int n = memory_map_size / descriptor_size;
        int i;
 
@@ -382,9 +393,9 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
        .set_wakeup_time = (void *)&efi_unimplemented,
        .set_virtual_address_map = &efi_set_virtual_address_map,
        .convert_pointer = (void *)&efi_invalid_parameter,
-       .get_variable = (void *)&efi_device_error,
-       .get_next_variable = (void *)&efi_device_error,
-       .set_variable = (void *)&efi_device_error,
+       .get_variable = efi_get_variable,
+       .get_next_variable = efi_get_next_variable,
+       .set_variable = efi_set_variable,
        .get_next_high_mono_count = (void *)&efi_device_error,
        .reset_system = &efi_reset_system_boottime,
 };
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
new file mode 100644 (file)
index 0000000..6c177da
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ *  EFI utils
+ *
+ *  Copyright (c) 2017 Rob Clark
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <malloc.h>
+#include <charset.h>
+#include <efi_loader.h>
+
+#define READ_ONLY BIT(31)
+
+/*
+ * Mapping between EFI variables and u-boot variables:
+ *
+ *   efi_$guid_$varname = {attributes}(type)value
+ *
+ * For example:
+ *
+ *   efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported=
+ *      "{ro,boot,run}(blob)0000000000000000"
+ *   efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder=
+ *      "(blob)00010000"
+ *
+ * The attributes are a comma separated list of these possible
+ * attributes:
+ *
+ *   + ro   - read-only
+ *   + boot - boot-services access
+ *   + run  - runtime access
+ *
+ * NOTE: with current implementation, no variables are available after
+ * ExitBootServices, and all are persisted (if possible).
+ *
+ * If not specified, the attributes default to "{boot}".
+ *
+ * The required type is one of:
+ *
+ *   + utf8 - raw utf8 string
+ *   + blob - arbitrary length hex string
+ *
+ * Maybe a utf16 type would be useful to for a string value to be auto
+ * converted to utf16?
+ */
+
+#define MAX_VAR_NAME 31
+#define MAX_NATIVE_VAR_NAME \
+       (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx_") + \
+               (MAX_VAR_NAME * MAX_UTF8_PER_UTF16))
+
+static int hex(unsigned char ch)
+{
+       if (ch >= 'a' && ch <= 'f')
+               return ch-'a'+10;
+       if (ch >= '0' && ch <= '9')
+               return ch-'0';
+       if (ch >= 'A' && ch <= 'F')
+               return ch-'A'+10;
+       return -1;
+}
+
+static const char *hex2mem(u8 *mem, const char *hexstr, int count)
+{
+       memset(mem, 0, count/2);
+
+       do {
+               int nibble;
+
+               *mem = 0;
+
+               if (!count || !*hexstr)
+                       break;
+
+               nibble = hex(*hexstr);
+               if (nibble < 0)
+                       break;
+
+               *mem = nibble;
+               count--;
+               hexstr++;
+
+               if (!count || !*hexstr)
+                       break;
+
+               nibble = hex(*hexstr);
+               if (nibble < 0)
+                       break;
+
+               *mem = (*mem << 4) | nibble;
+               count--;
+               hexstr++;
+               mem++;
+
+       } while (1);
+
+       if (*hexstr)
+               return hexstr;
+
+       return NULL;
+}
+
+static char *mem2hex(char *hexstr, const u8 *mem, int count)
+{
+       static const char hexchars[] = "0123456789abcdef";
+
+       while (count-- > 0) {
+               u8 ch = *mem++;
+               *hexstr++ = hexchars[ch >> 4];
+               *hexstr++ = hexchars[ch & 0xf];
+       }
+
+       return hexstr;
+}
+
+static efi_status_t efi_to_native(char *native, s16 *variable_name,
+               efi_guid_t *vendor)
+{
+       size_t len;
+
+       len = utf16_strlen((u16 *)variable_name);
+       if (len >= MAX_VAR_NAME)
+               return EFI_DEVICE_ERROR;
+
+       native += sprintf(native, "efi_%pUl_", vendor);
+       native  = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len);
+       *native = '\0';
+
+       return EFI_SUCCESS;
+}
+
+static const char *prefix(const char *str, const char *prefix)
+{
+       size_t n = strlen(prefix);
+       if (!strncmp(prefix, str, n))
+               return str + n;
+       return NULL;
+}
+
+/* parse attributes part of variable value, if present: */
+static const char *parse_attr(const char *str, u32 *attrp)
+{
+       u32 attr = 0;
+       char sep = '{';
+
+       if (*str != '{') {
+               *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
+               return str;
+       }
+
+       while (*str == sep) {
+               const char *s;
+
+               str++;
+
+               if ((s = prefix(str, "ro"))) {
+                       attr |= READ_ONLY;
+               } else if ((s = prefix(str, "boot"))) {
+                       attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
+               } else if ((s = prefix(str, "run"))) {
+                       attr |= EFI_VARIABLE_RUNTIME_ACCESS;
+               } else {
+                       printf("invalid attribute: %s\n", str);
+                       break;
+               }
+
+               str = s;
+               sep = ',';
+       }
+
+       str++;
+
+       *attrp = attr;
+
+       return str;
+}
+
+/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */
+efi_status_t EFIAPI efi_get_variable(s16 *variable_name,
+               efi_guid_t *vendor, u32 *attributes,
+               unsigned long *data_size, void *data)
+{
+       char native_name[MAX_NATIVE_VAR_NAME + 1];
+       efi_status_t ret;
+       unsigned long in_size;
+       const char *val, *s;
+       u32 attr;
+
+       EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
+                 data_size, data);
+
+       if (!variable_name || !vendor || !data_size)
+               return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+       ret = efi_to_native(native_name, variable_name, vendor);
+       if (ret)
+               return EFI_EXIT(ret);
+
+       debug("%s: get '%s'\n", __func__, native_name);
+
+       val = env_get(native_name);
+       if (!val)
+               return EFI_EXIT(EFI_NOT_FOUND);
+
+       val = parse_attr(val, &attr);
+
+       in_size = *data_size;
+
+       if ((s = prefix(val, "(blob)"))) {
+               unsigned len = strlen(s);
+
+               /* two characters per byte: */
+               len = DIV_ROUND_UP(len, 2);
+               *data_size = len;
+
+               if (in_size < len)
+                       return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+
+               if (!data)
+                       return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+               if (hex2mem(data, s, len * 2))
+                       return EFI_EXIT(EFI_DEVICE_ERROR);
+
+               debug("%s: got value: \"%s\"\n", __func__, s);
+       } else if ((s = prefix(val, "(utf8)"))) {
+               unsigned len = strlen(s) + 1;
+
+               *data_size = len;
+
+               if (in_size < len)
+                       return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+
+               if (!data)
+                       return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+               memcpy(data, s, len);
+               ((char *)data)[len] = '\0';
+
+               debug("%s: got value: \"%s\"\n", __func__, (char *)data);
+       } else {
+               debug("%s: invalid value: '%s'\n", __func__, val);
+               return EFI_EXIT(EFI_DEVICE_ERROR);
+       }
+
+       if (attributes)
+               *attributes = attr & EFI_VARIABLE_MASK;
+
+       return EFI_EXIT(EFI_SUCCESS);
+}
+
+/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */
+efi_status_t EFIAPI efi_get_next_variable(
+               unsigned long *variable_name_size,
+               s16 *variable_name, efi_guid_t *vendor)
+{
+       EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
+
+       return EFI_EXIT(EFI_DEVICE_ERROR);
+}
+
+/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */
+efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
+               efi_guid_t *vendor, u32 attributes,
+               unsigned long data_size, void *data)
+{
+       char native_name[MAX_NATIVE_VAR_NAME + 1];
+       efi_status_t ret = EFI_SUCCESS;
+       char *val, *s;
+       u32 attr;
+
+       EFI_ENTRY("\"%ls\" %pUl %x %lu %p", variable_name, vendor, attributes,
+                 data_size, data);
+
+       if (!variable_name || !vendor)
+               return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+       ret = efi_to_native(native_name, variable_name, vendor);
+       if (ret)
+               return EFI_EXIT(ret);
+
+#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
+
+       if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
+               /* delete the variable: */
+               env_set(native_name, NULL);
+               return EFI_EXIT(EFI_SUCCESS);
+       }
+
+       val = env_get(native_name);
+       if (val) {
+               parse_attr(val, &attr);
+
+               if (attr & READ_ONLY)
+                       return EFI_EXIT(EFI_WRITE_PROTECTED);
+       }
+
+       val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
+       if (!val)
+               return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+       s = val;
+
+       /* store attributes: */
+       attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
+       s += sprintf(s, "{");
+       while (attributes) {
+               u32 attr = 1 << (ffs(attributes) - 1);
+
+               if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
+                       s += sprintf(s, "boot");
+               else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
+                       s += sprintf(s, "run");
+
+               attributes &= ~attr;
+               if (attributes)
+                       s += sprintf(s, ",");
+       }
+       s += sprintf(s, "}");
+
+       /* store payload: */
+       s += sprintf(s, "(blob)");
+       s = mem2hex(s, data, data_size);
+       *s = '\0';
+
+       debug("%s: setting: %s=%s\n", __func__, native_name, val);
+
+       if (env_set(native_name, val))
+               ret = EFI_DEVICE_ERROR;
+
+       free(val);
+
+       return EFI_EXIT(ret);
+}
diff --git a/lib/efi_selftest/Kconfig b/lib/efi_selftest/Kconfig
new file mode 100644 (file)
index 0000000..3b5f3a1
--- /dev/null
@@ -0,0 +1,7 @@
+config CMD_BOOTEFI_SELFTEST
+       bool "Allow booting an EFI efi_selftest"
+       depends on CMD_BOOTEFI
+       help
+         This adds an EFI test application to U-Boot that can be executed
+         with the 'bootefi selftest' command. It provides extended tests of
+         the EFI API implementation.
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
new file mode 100644 (file)
index 0000000..30f1960
--- /dev/null
@@ -0,0 +1,26 @@
+:
+# (C) Copyright 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
+#
+#  SPDX-License-Identifier:     GPL-2.0+
+#
+
+# This file only gets included with CONFIG_EFI_LOADER set, so all
+# object inclusion implicitly depends on it
+
+CFLAGS_efi_selftest.o := $(CFLAGS_EFI)
+CFLAGS_REMOVE_efi_selftest.o := $(CFLAGS_NON_EFI)
+CFLAGS_efi_selftest_console.o := $(CFLAGS_EFI)
+CFLAGS_REMOVE_efi_selftest_console.o := $(CFLAGS_NON_EFI)
+CFLAGS_efi_selftest_events.o := $(CFLAGS_EFI)
+CFLAGS_REMOVE_efi_selftest_events.o := $(CFLAGS_NON_EFI)
+CFLAGS_efi_selftest_exitbootservices.o := $(CFLAGS_EFI)
+CFLAGS_REMOVE_efi_selftest_exitbootservices.o := $(CFLAGS_NON_EFI)
+CFLAGS_efi_selftest_tpl.o := $(CFLAGS_EFI)
+CFLAGS_REMOVE_efi_selftest_tpl.o := $(CFLAGS_NON_EFI)
+
+obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
+efi_selftest.o \
+efi_selftest_console.o \
+efi_selftest_events.o \
+efi_selftest_exitbootservices.o \
+efi_selftest_tpl.o
diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c
new file mode 100644 (file)
index 0000000..efec832
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * EFI efi_selftest
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <efi_selftest.h>
+#include <vsprintf.h>
+
+static const struct efi_system_table *systable;
+static const struct efi_boot_services *boottime;
+static const struct efi_runtime_services *runtime;
+static efi_handle_t handle;
+static u16 reset_message[] = L"Selftest completed";
+
+/*
+ * Exit the boot services.
+ *
+ * The size of the memory map is determined.
+ * Pool memory is allocated to copy the memory map.
+ * The memory amp is copied and the map key is obtained.
+ * The map key is used to exit the boot services.
+ */
+void efi_st_exit_boot_services(void)
+{
+       unsigned long  map_size = 0;
+       unsigned long  map_key;
+       unsigned long desc_size;
+       u32 desc_version;
+       efi_status_t ret;
+       struct efi_mem_desc *memory_map;
+
+       ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
+                                      &desc_version);
+       if (ret != EFI_BUFFER_TOO_SMALL) {
+               efi_st_printf("ERROR: GetMemoryMap did not return "
+                             "EFI_BUFFER_TOO_SMALL\n");
+               return;
+       }
+       /* Allocate extra space for newly allocated memory */
+       map_size += sizeof(struct efi_mem_desc);
+       ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
+                                     (void **)&memory_map);
+       if (ret != EFI_SUCCESS) {
+               efi_st_printf("ERROR: AllocatePool did not return "
+                             "EFI_SUCCESS\n");
+               return;
+       }
+       ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
+                                      &desc_size, &desc_version);
+       if (ret != EFI_SUCCESS) {
+               efi_st_printf("ERROR: GetMemoryMap did not return "
+                             "EFI_SUCCESS\n");
+               return;
+       }
+       ret = boottime->exit_boot_services(handle, map_key);
+       if (ret != EFI_SUCCESS) {
+               efi_st_printf("ERROR: ExitBootServices did not return "
+                             "EFI_SUCCESS\n");
+               return;
+       }
+       efi_st_printf("\nBoot services terminated\n");
+}
+
+/*
+ * Set up a test.
+ *
+ * @test       the test to be executed
+ * @failures   counter that will be incremented if a failure occurs
+ */
+static int setup(struct efi_unit_test *test, unsigned int *failures)
+{
+       int ret;
+
+       if (!test->setup)
+               return 0;
+       efi_st_printf("\nSetting up '%s'\n", test->name);
+       ret = test->setup(handle, systable);
+       if (ret) {
+               efi_st_printf("ERROR: Setting up '%s' failed\n", test->name);
+               ++*failures;
+       } else {
+               efi_st_printf("Setting up '%s' succeeded\n", test->name);
+       }
+       return ret;
+}
+
+/*
+ * Execute a test.
+ *
+ * @test       the test to be executed
+ * @failures   counter that will be incremented if a failure occurs
+ */
+static int execute(struct efi_unit_test *test, unsigned int *failures)
+{
+       int ret;
+
+       if (!test->execute)
+               return 0;
+       efi_st_printf("\nExecuting '%s'\n", test->name);
+       ret = test->execute();
+       if (ret) {
+               efi_st_printf("ERROR: Executing '%s' failed\n", test->name);
+               ++*failures;
+       } else {
+               efi_st_printf("Executing '%s' succeeded\n", test->name);
+       }
+       return ret;
+}
+
+/*
+ * Tear down a test.
+ *
+ * @test       the test to be torn down
+ * @failures   counter that will be incremented if a failure occurs
+ */
+static int teardown(struct efi_unit_test *test, unsigned int *failures)
+{
+       int ret;
+
+       if (!test->teardown)
+               return 0;
+       efi_st_printf("\nTearing down '%s'\n", test->name);
+       ret = test->teardown();
+       if (ret) {
+               efi_st_printf("ERROR: Tearing down '%s' failed\n", test->name);
+               ++*failures;
+       } else {
+               efi_st_printf("Tearing down '%s' succeeded\n", test->name);
+       }
+       return ret;
+}
+
+/*
+ * Execute selftest of the EFI API
+ *
+ * This is the main entry point of the EFI selftest application.
+ *
+ * All tests use a driver model and are run in three phases:
+ * setup, execute, teardown.
+ *
+ * A test may be setup and executed at boottime,
+ * it may be setup at boottime and executed at runtime,
+ * or it may be setup and executed at runtime.
+ *
+ * After executing all tests the system is reset.
+ *
+ * @image_handle:      handle of the loaded EFI image
+ * @systab:            EFI system table
+ */
+efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
+                                struct efi_system_table *systab)
+{
+       struct efi_unit_test *test;
+       unsigned int failures = 0;
+
+       systable = systab;
+       boottime = systable->boottime;
+       runtime = systable->runtime;
+       handle = image_handle;
+       con_out = systable->con_out;
+       con_in = systable->con_in;
+
+       efi_st_printf("\nTesting EFI API implementation\n");
+
+       efi_st_printf("\nNumber of tests to execute: %u\n",
+                     ll_entry_count(struct efi_unit_test, efi_unit_test));
+
+       /* Execute boottime tests */
+       for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+            test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+               if (test->phase == EFI_EXECUTE_BEFORE_BOOTTIME_EXIT) {
+                       setup(test, &failures);
+                       execute(test, &failures);
+                       teardown(test, &failures);
+               }
+       }
+
+       /* Execute mixed tests */
+       for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+            test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+               if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT)
+                       setup(test, &failures);
+       }
+
+       efi_st_exit_boot_services();
+
+       for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+            test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+               if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT) {
+                       execute(test, &failures);
+                       teardown(test, &failures);
+               }
+       }
+
+       /* Execute runtime tests */
+       for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+            test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+               if (test->phase == EFI_SETUP_AFTER_BOOTTIME_EXIT) {
+                       setup(test, &failures);
+                       execute(test, &failures);
+                       teardown(test, &failures);
+               }
+       }
+
+       /* Give feedback */
+       efi_st_printf("\nSummary: %u failures\n\n", failures);
+
+       /* Reset system */
+       efi_st_printf("Preparing for reset. Press any key.\n");
+       efi_st_get_key();
+       runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY,
+                             sizeof(reset_message), reset_message);
+       efi_st_printf("\nERROR: reset failed.\n");
+
+       return EFI_UNSUPPORTED;
+}
diff --git a/lib/efi_selftest/efi_selftest_console.c b/lib/efi_selftest/efi_selftest_console.c
new file mode 100644 (file)
index 0000000..7b5b724
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * EFI efi_selftest
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <efi_selftest.h>
+#include <vsprintf.h>
+
+struct efi_simple_text_output_protocol *con_out;
+struct efi_simple_input_interface *con_in;
+
+/*
+ * Print a pointer to an u16 string
+ *
+ * @pointer: pointer
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void pointer(void *pointer, u16 **buf)
+{
+       int i;
+       u16 c;
+       uintptr_t p = (uintptr_t)pointer;
+       u16 *pos = *buf;
+
+       for (i = 8 * sizeof(p) - 4; i >= 0; i -= 4) {
+               c = (p >> i) & 0x0f;
+               c += '0';
+               if (c > '9')
+                       c += 'a' - '9' - 1;
+               *pos++ = c;
+       }
+       *pos = 0;
+       *buf = pos;
+}
+
+/*
+ * Print an unsigned 32bit value as decimal number to an u16 string
+ *
+ * @value: value to be printed
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void uint2dec(u32 value, u16 **buf)
+{
+       u16 *pos = *buf;
+       int i;
+       u16 c;
+       u64 f;
+
+       /*
+        * Increment by .5 and multiply with
+        * (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
+        * to move the first digit to bit 60-63.
+        */
+       f = 0x225C17D0;
+       f += (0x9B5A52DULL * value) >> 28;
+       f += 0x44B82FA0ULL * value;
+
+       for (i = 0; i < 10; ++i) {
+               /* Write current digit */
+               c = f >> 60;
+               if (c || pos != *buf)
+                       *pos++ = c + '0';
+               /* Eliminate current digit */
+               f &= 0xfffffffffffffff;
+               /* Get next digit */
+               f *= 0xaULL;
+       }
+       if (pos == *buf)
+               *pos++ = '0';
+       *pos = 0;
+       *buf = pos;
+}
+
+/*
+ * Print a signed 32bit value as decimal number to an u16 string
+ *
+ * @value: value to be printed
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void int2dec(s32 value, u16 **buf)
+{
+       u32 u;
+       u16 *pos = *buf;
+
+       if (value < 0) {
+               *pos++ = '-';
+               u = -value;
+       } else {
+               u = value;
+       }
+       uint2dec(u, &pos);
+       *buf = pos;
+}
+
+/*
+ * Print a formatted string to the EFI console
+ *
+ * @fmt: format string
+ * @...: optional arguments
+ */
+void efi_st_printf(const char *fmt, ...)
+{
+       va_list args;
+       u16 buf[160];
+       const char *c;
+       u16 *pos = buf;
+       const char *s;
+
+       va_start(args, fmt);
+
+       c = fmt;
+       for (; *c; ++c) {
+               switch (*c) {
+               case '\\':
+                       ++c;
+                       switch (*c) {
+                       case '\0':
+                               --c;
+                               break;
+                       case 'n':
+                               *pos++ = '\n';
+                               break;
+                       case 'r':
+                               *pos++ = '\r';
+                               break;
+                       case 't':
+                               *pos++ = '\t';
+                               break;
+                       default:
+                               *pos++ = *c;
+                       }
+                       break;
+               case '%':
+                       ++c;
+                       switch (*c) {
+                       case '\0':
+                               --c;
+                               break;
+                       case 'd':
+                               int2dec(va_arg(args, s32), &pos);
+                               break;
+                       case 'p':
+                               pointer(va_arg(args, void*), &pos);
+                               break;
+                       case 's':
+                               s = va_arg(args, const char *);
+                               for (; *s; ++s)
+                                       *pos++ = *s;
+                               break;
+                       case 'u':
+                               uint2dec(va_arg(args, u32), &pos);
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       *pos++ = *c;
+               }
+       }
+       va_end(args);
+       *pos = 0;
+       con_out->output_string(con_out, buf);
+}
+
+/*
+ * Reads an Unicode character from the input device.
+ *
+ * @return: Unicode character
+ */
+u16 efi_st_get_key(void)
+{
+       struct efi_input_key input_key;
+       efi_status_t ret;
+
+       /* Wait for next key */
+       do {
+               ret = con_in->read_key_stroke(con_in, &input_key);
+       } while (ret == EFI_NOT_READY);
+       return input_key.unicode_char;
+}
diff --git a/lib/efi_selftest/efi_selftest_events.c b/lib/efi_selftest/efi_selftest_events.c
new file mode 100644 (file)
index 0000000..c4f6695
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * efi_selftest_events
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * This unit test uses timer events to check the implementation
+ * of the following boottime services:
+ * CreateEvent, CloseEvent, WaitForEvent, CheckEvent, SetTimer.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_event *event_notify;
+static struct efi_event *event_wait;
+static unsigned int counter;
+static struct efi_boot_services *boottime;
+
+/*
+ * Notification function, increments a counter.
+ *
+ * @event      notified event
+ * @context    pointer to the counter
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+       if (!context)
+               return;
+       ++*(unsigned int *)context;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two timer events.
+ * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
+ *
+ * @handle:    handle of the loaded image
+ * @systable:  system table
+ */
+static int setup(const efi_handle_t handle,
+                const struct efi_system_table *systable)
+{
+       efi_status_t ret;
+
+       boottime = systable->boottime;
+
+       ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                                    TPL_CALLBACK, notify, (void *)&counter,
+                                    &event_notify);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("could not create event\n");
+               return 1;
+       }
+       ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
+                                    TPL_CALLBACK, notify, NULL, &event_wait);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("could not create event\n");
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the events created in setup.
+ */
+static int teardown(void)
+{
+       efi_status_t ret;
+
+       if (event_notify) {
+               ret = boottime->close_event(event_notify);
+               event_notify = NULL;
+               if (ret != EFI_SUCCESS) {
+                       efi_st_error("could not close event\n");
+                       return 1;
+               }
+       }
+       if (event_wait) {
+               ret = boottime->close_event(event_wait);
+               event_wait = NULL;
+               if (ret != EFI_SUCCESS) {
+                       efi_st_error("could not close event\n");
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Run a 10 ms periodic timer and check that it is called 10 times
+ * while waiting for 100 ms single shot timer.
+ *
+ * Run a 100 ms single shot timer and check that it is called once
+ * while waiting for 100 ms periodic timer for two periods.
+ */
+static int execute(void)
+{
+       unsigned long index;
+       efi_status_t ret;
+
+       /* Set 10 ms timer */
+       counter = 0;
+       ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not set timer\n");
+               return 1;
+       }
+       /* Set 100 ms timer */
+       ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not set timer\n");
+               return 1;
+       }
+
+       index = 5;
+       ret = boottime->wait_for_event(1, &event_wait, &index);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not wait for event\n");
+               return 1;
+       }
+       ret = boottime->check_event(event_wait);
+       if (ret != EFI_NOT_READY) {
+               efi_st_error("Signaled state was not cleared.\n");
+               efi_st_printf("ret = %u\n", (unsigned int)ret);
+               return 1;
+       }
+       if (index != 0) {
+               efi_st_error("WaitForEvent returned wrong index\n");
+               return 1;
+       }
+       efi_st_printf("Counter periodic: %u\n", counter);
+       if (counter < 8 || counter > 12) {
+               efi_st_error("Incorrect timing of events\n");
+               return 1;
+       }
+       ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
+       if (index != 0) {
+               efi_st_error("Could not cancel timer\n");
+               return 1;
+       }
+       /* Set 10 ms timer */
+       counter = 0;
+       ret = boottime->set_timer(event_notify, EFI_TIMER_RELATIVE, 100000);
+       if (index != 0) {
+               efi_st_error("Could not set timer\n");
+               return 1;
+       }
+       /* Set 100 ms timer */
+       ret = boottime->set_timer(event_wait, EFI_TIMER_PERIODIC, 1000000);
+       if (index != 0) {
+               efi_st_error("Could not set timer\n");
+               return 1;
+       }
+       ret = boottime->wait_for_event(1, &event_wait, &index);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not wait for event\n");
+               return 1;
+       }
+       efi_st_printf("Counter single shot: %u\n", counter);
+       if (counter != 1) {
+               efi_st_error("Single shot timer failed\n");
+               return 1;
+       }
+       ret = boottime->wait_for_event(1, &event_wait, &index);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not wait for event\n");
+               return 1;
+       }
+       efi_st_printf("Stopped counter: %u\n", counter);
+       if (counter != 1) {
+               efi_st_error("Stopped timer fired\n");
+               return 1;
+       }
+       ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
+       if (index != 0) {
+               efi_st_error("Could not cancel timer\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+EFI_UNIT_TEST(events) = {
+       .name = "event services",
+       .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+       .setup = setup,
+       .execute = execute,
+       .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_exitbootservices.c b/lib/efi_selftest/efi_selftest_exitbootservices.c
new file mode 100644 (file)
index 0000000..60271e6
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * efi_selftest_events
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * This unit test checks that the notification function of an
+ * EVT_SIGNAL_EXIT_BOOT_SERVICES event is called exactly once.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+static struct efi_event *event_notify;
+static unsigned int counter;
+
+/*
+ * Notification function, increments a counter.
+ *
+ * @event      notified event
+ * @context    pointer to the counter
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+       if (!context)
+               return;
+       ++*(unsigned int *)context;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create an EVT_SIGNAL_EXIT_BOOT_SERVICES event.
+ *
+ * @handle:    handle of the loaded image
+ * @systable:  system table
+ */
+static int setup(const efi_handle_t handle,
+                const struct efi_system_table *systable)
+{
+       efi_status_t ret;
+
+       boottime = systable->boottime;
+
+       counter = 0;
+       ret = boottime->create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                                    TPL_CALLBACK, notify, (void *)&counter,
+                                    &event_notify);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("could not create event\n");
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the event created in setup.
+ */
+static int teardown(void)
+{
+       efi_status_t ret;
+
+       if (event_notify) {
+               ret = boottime->close_event(event_notify);
+               event_notify = NULL;
+               if (ret != EFI_SUCCESS) {
+                       efi_st_error("could not close event\n");
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Check that the notification function of the EVT_SIGNAL_EXIT_BOOT_SERVICES
+ * event has been called.
+ *
+ * Call ExitBootServices again and check that the notification function is
+ * not called again.
+ */
+static int execute(void)
+{
+       if (counter != 1) {
+               efi_st_error("ExitBootServices was not notified");
+               return 1;
+       }
+       efi_st_exit_boot_services();
+       if (counter != 1) {
+               efi_st_error("ExitBootServices was notified twice");
+               return 1;
+       }
+       return 0;
+}
+
+EFI_UNIT_TEST(exitbootservices) = {
+       .name = "ExitBootServices",
+       .phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+       .setup = setup,
+       .execute = execute,
+       .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_tpl.c b/lib/efi_selftest/efi_selftest_tpl.c
new file mode 100644 (file)
index 0000000..90ace0f
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * efi_selftest_events
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * This unit test uses timer events to check the handling of
+ * task priority levels.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_event *event_notify;
+static struct efi_event *event_wait;
+static unsigned int counter;
+static struct efi_boot_services *boottime;
+
+/*
+ * Notification function, increments a counter.
+ *
+ * @event      notified event
+ * @context    pointer to the counter
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+       if (!context)
+               return;
+       ++*(unsigned int *)context;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two timer events.
+ * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
+ *
+ * @handle:    handle of the loaded image
+ * @systable:  system table
+ */
+static int setup(const efi_handle_t handle,
+                const struct efi_system_table *systable)
+{
+       efi_status_t ret;
+
+       boottime = systable->boottime;
+
+       ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                                    TPL_CALLBACK, notify, (void *)&counter,
+                                    &event_notify);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("could not create event\n");
+               return 1;
+       }
+       ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
+                                    TPL_HIGH_LEVEL, notify, NULL, &event_wait);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("could not create event\n");
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the events created in setup.
+ */
+static int teardown(void)
+{
+       efi_status_t ret;
+
+       if (event_notify) {
+               ret = boottime->close_event(event_notify);
+               event_notify = NULL;
+               if (ret != EFI_SUCCESS) {
+                       efi_st_error("could not close event\n");
+                       return 1;
+               }
+       }
+       if (event_wait) {
+               ret = boottime->close_event(event_wait);
+               event_wait = NULL;
+               if (ret != EFI_SUCCESS) {
+                       efi_st_error("could not close event\n");
+                       return 1;
+               }
+       }
+       boottime->restore_tpl(TPL_APPLICATION);
+       return 0;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Run a 10 ms periodic timer and check that it is called 10 times
+ * while waiting for 100 ms single shot timer.
+ *
+ * Raise the TPL level to the level of the 10 ms timer and observe
+ * that the notification function is not called again.
+ *
+ * Lower the TPL level and check that the queued notification
+ * function is called.
+ */
+static int execute(void)
+{
+       unsigned long index;
+       efi_status_t ret;
+       UINTN old_tpl;
+
+       /* Set 10 ms timer */
+       counter = 0;
+       ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not set timer\n");
+               return 1;
+       }
+       /* Set 100 ms timer */
+       ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not set timer\n");
+               return 1;
+       }
+       index = 5;
+       ret = boottime->wait_for_event(1, &event_wait, &index);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not wait for event\n");
+               return 1;
+       }
+       ret = boottime->check_event(event_wait);
+       if (ret != EFI_NOT_READY) {
+               efi_st_error("Signaled state was not cleared.\n");
+               efi_st_printf("ret = %u\n", (unsigned int)ret);
+               return 1;
+       }
+       if (index != 0) {
+               efi_st_error("WaitForEvent returned wrong index\n");
+               return 1;
+       }
+       efi_st_printf("Counter with TPL level TPL_APPLICATION: %u\n", counter);
+       if (counter < 8 || counter > 12) {
+               efi_st_error("Incorrect timing of events\n");
+               return 1;
+       }
+       ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
+       if (index != 0) {
+               efi_st_error("Could not cancel timer\n");
+               return 1;
+       }
+       /* Raise TPL level */
+       old_tpl = boottime->raise_tpl(TPL_CALLBACK);
+       if (old_tpl != TPL_APPLICATION) {
+               efi_st_error("Initial TPL level was not TPL_APPLICATION");
+               return 1;
+       }
+       /* Set 10 ms timer */
+       counter = 0;
+       ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
+       if (index != 0) {
+               efi_st_error("Could not set timer\n");
+               return 1;
+       }
+       /* Set 100 ms timer */
+       ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not set timer\n");
+               return 1;
+       }
+       do {
+               ret = boottime->check_event(event_wait);
+       } while (ret == EFI_NOT_READY);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not check event\n");
+               return 1;
+       }
+       efi_st_printf("Counter with TPL level TPL_CALLBACK: %u\n", counter);
+       if (counter != 0) {
+               efi_st_error("Suppressed timer fired\n");
+               return 1;
+       }
+       /* Set 1 ms timer */
+       ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not set timer\n");
+               return 1;
+       }
+       /* Restore the old TPL level */
+       boottime->restore_tpl(TPL_APPLICATION);
+       ret = boottime->wait_for_event(1, &event_wait, &index);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Could not wait for event\n");
+               return 1;
+       }
+       efi_st_printf("Counter with TPL level TPL_APPLICATION: %u\n", counter);
+       if (counter < 1) {
+               efi_st_error("Queued timer event did not fire\n");
+               return 1;
+       }
+       ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
+       if (index != 0) {
+               efi_st_error("Could not cancel timer\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+EFI_UNIT_TEST(tpl) = {
+       .name = "task priority levels",
+       .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+       .setup = setup,
+       .execute = execute,
+       .teardown = teardown,
+};
index 2a7ed70cf26b3cd8d72d772d1bb1ae6a6c9f5773..861a3dc3f1d301378c8c807bf576bfcfcc437ab4 100644 (file)
@@ -359,20 +359,22 @@ cmd_S_ttf=                                                \
 $(obj)/%.S: $(src)/%.ttf
        $(call cmd,S_ttf)
 
-# EFI Hello World application
+# EFI applications
+# A Makefile target *.efi is built as EFI application.
+# A Makefile target *_efi.S wraps *.efi as built-in EFI application.
 # ---------------------------------------------------------------------------
 
 # Generate an assembly file to wrap the EFI app
-cmd_S_efi=                                             \
-(                                                      \
-       echo '.section .rodata.efi.init,"a"';           \
-       echo '.balign 16';                              \
-       echo '.global __efi_hello_world_begin';         \
-       echo '__efi_hello_world_begin:';                \
-       echo '.incbin "$<" ';                           \
-       echo '__efi_hello_world_end:';                  \
-       echo '.global __efi_hello_world_end';           \
-       echo '.balign 16';                              \
+cmd_S_efi=                                     \
+(                                              \
+       echo '.section .rodata.$*.init,"a"';    \
+       echo '.balign 16';                      \
+       echo '.global __efi_$*_begin';          \
+       echo '__efi_$*_begin:';                 \
+       echo '.incbin "$<" ';                   \
+       echo '__efi_$*_end:';                   \
+       echo '.global __efi_$*_end';            \
+       echo '.balign 16';                      \
 ) > $@
 
 $(obj)/%_efi.S: $(obj)/%.efi
@@ -383,7 +385,7 @@ cmd_efi_objcopy = $(OBJCOPY) -j .header -j .text -j .sdata -j .data -j \
                .dynamic -j .dynsym  -j .rel* -j .rela* -j .reloc \
                $(if $(EFI_TARGET),$(EFI_TARGET),-O binary) $^ $@
 
-$(obj)/%.efi: $(obj)/%.so
+$(obj)/%.efi: $(obj)/%_efi.so
        $(call cmd,efi_objcopy)
 
 quiet_cmd_efi_ld = LD      $@
@@ -392,9 +394,7 @@ cmd_efi_ld = $(LD) -nostdlib -znocombreloc -T $(EFI_LDS_PATH) -shared \
 
 EFI_LDS_PATH = $(srctree)/arch/$(ARCH)/lib/$(EFI_LDS)
 
-$(obj)/helloworld.so: $(EFI_LDS_PATH)
-
-$(obj)/helloworld.so: $(obj)/helloworld.o arch/$(ARCH)/lib/$(EFI_CRT0) \
+$(obj)/%_efi.so: $(obj)/%.o arch/$(ARCH)/lib/$(EFI_CRT0) \
                arch/$(ARCH)/lib/$(EFI_RELOC)
        $(call cmd,efi_ld)
 
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py
new file mode 100644 (file)
index 0000000..76e282a
--- /dev/null
@@ -0,0 +1,25 @@
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+# Copyright (c) 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Test efi API implementation
+
+import pytest
+import u_boot_utils
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest(u_boot_console):
+       """
+       Run bootefi selftest
+       """
+
+       u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
+       m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])
+       if m != 0:
+               raise Exception('Failures occured during the EFI selftest')
+       u_boot_console.run_command(cmd='', wait_for_echo=False, wait_for_prompt=False);
+       m = u_boot_console.p.expect(['resetting', 'U-Boot'])
+       if m != 0:
+               raise Exception('Reset failed during the EFI selftest')
+       u_boot_console.restart_uboot();