Merge tag 'efi-next' of https://source.denx.de/u-boot/custodians/u-boot-efi into...
authorTom Rini <trini@konsulko.com>
Fri, 26 Mar 2021 02:45:43 +0000 (22:45 -0400)
committerTom Rini <trini@konsulko.com>
Fri, 26 Mar 2021 02:45:43 +0000 (22:45 -0400)
Pull request for efi-next

New:
provide EFI ESRT table
initrd via Load_File2_Protocol uses boot options
create an S-CRTM event for measured boot

Bug fixes:
avoid double free of SPI device in dfu_free_entity()
avoid memory leak in TCG2 protocol

36 files changed:
cmd/bootefi.c
cmd/efidebug.c
cmd/load.c
doc/board/emulation/qemu_capsule_update.rst
doc/uefi/uefi.rst
drivers/dfu/dfu_sf.c
include/efi_api.h
include/efi_loader.h
include/efi_selftest.h
lib/efi_loader/Kconfig
lib/efi_loader/Makefile
lib/efi_loader/efi_bootmgr.c
lib/efi_loader/efi_boottime.c
lib/efi_loader/efi_capsule.c
lib/efi_loader/efi_console.c
lib/efi_loader/efi_device_path.c
lib/efi_loader/efi_device_path_to_text.c
lib/efi_loader/efi_dt_fixup.c
lib/efi_loader/efi_esrt.c [new file with mode: 0644]
lib/efi_loader/efi_file.c
lib/efi_loader/efi_firmware.c
lib/efi_loader/efi_helper.c [new file with mode: 0644]
lib/efi_loader/efi_load_initrd.c
lib/efi_loader/efi_setup.c
lib/efi_loader/efi_tcg2.c
lib/efi_loader/efi_var_common.c
lib/efi_selftest/Makefile
lib/efi_selftest/efi_selftest.c
lib/efi_selftest/efi_selftest_esrt.c [new file with mode: 0644]
lib/efi_selftest/efi_selftest_load_initrd.c [deleted file]
lib/efi_selftest/efi_selftest_set_virtual_address_map.c
lib/efi_selftest/initrddump.c
test/py/tests/test_efi_capsule/test_capsule_firmware.py
test/py/tests/test_efi_secboot/test_signed.py
test/py/tests/test_efi_secboot/test_signed_intca.py
test/py/tests/test_efi_secboot/test_unsigned.py

index 271b385..cba81ff 100644 (file)
@@ -358,6 +358,9 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options)
 
        free(load_options);
 
+       if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD))
+               efi_initrd_deregister();
+
        return ret;
 }
 
index e4030f5..6e36575 100644 (file)
@@ -9,6 +9,7 @@
 #include <common.h>
 #include <command.h>
 #include <efi_dt_fixup.h>
+#include <efi_load_initrd.h>
 #include <efi_loader.h>
 #include <efi_rng.h>
 #include <exports.h>
@@ -19,6 +20,7 @@
 #include <part.h>
 #include <search.h>
 #include <linux/ctype.h>
+#include <linux/err.h>
 
 #define BS systab.boottime
 #define RT systab.runtime
@@ -72,7 +74,7 @@ static int do_efi_capsule_update(struct cmd_tbl *cmdtp, int flag,
                       capsule->capsule_image_size);
        }
 
-       ret = EFI_CALL(RT->update_capsule(&capsule, 1, (u64)NULL));
+       ret = EFI_CALL(RT->update_capsule(&capsule, 1, 0));
        if (ret) {
                printf("Cannot handle a capsule at %p", capsule);
                return CMD_RET_FAILURE;
@@ -129,6 +131,82 @@ static int do_efi_capsule_show(struct cmd_tbl *cmdtp, int flag,
        return CMD_RET_SUCCESS;
 }
 
+#ifdef CONFIG_EFI_ESRT
+
+#define EFI_ESRT_FW_TYPE_NUM 4
+char *efi_fw_type_str[EFI_ESRT_FW_TYPE_NUM] = {"unknown", "system FW", "device FW",
+        "UEFI driver"};
+
+#define EFI_ESRT_UPDATE_STATUS_NUM 9
+char *efi_update_status_str[EFI_ESRT_UPDATE_STATUS_NUM] = {"success", "unsuccessful",
+       "insufficient resources", "incorrect version", "invalid format",
+       "auth error", "power event (AC)", "power event (batt)",
+       "unsatisfied dependencies"};
+
+#define EFI_FW_TYPE_STR_GET(idx) (\
+EFI_ESRT_FW_TYPE_NUM > (idx) ? efi_fw_type_str[(idx)] : "error"\
+)
+
+#define EFI_FW_STATUS_STR_GET(idx) (\
+EFI_ESRT_UPDATE_STATUS_NUM  > (idx) ? efi_update_status_str[(idx)] : "error"\
+)
+
+/**
+ * do_efi_capsule_esrt() - manage UEFI capsules
+ *
+ * @cmdtp:     Command table
+ * @flag:      Command flag
+ * @argc:      Number of arguments
+ * @argv:      Argument array
+ * Return:     CMD_RET_SUCCESS on success,
+ *             CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "capsule esrt" sub-command.
+ * The prints the current ESRT table.
+ *
+ *     efidebug capsule esrt
+ */
+static int do_efi_capsule_esrt(struct cmd_tbl *cmdtp, int flag,
+                              int argc, char * const argv[])
+{
+       struct efi_system_resource_table *esrt = NULL;
+
+       if (argc != 1)
+               return CMD_RET_USAGE;
+
+       for (int idx = 0; idx < systab.nr_tables; idx++)
+               if (!guidcmp(&efi_esrt_guid, &systab.tables[idx].guid))
+                       esrt = (struct efi_system_resource_table *)systab.tables[idx].table;
+
+       if (!esrt) {
+               log_info("ESRT: table not present\n");
+               return CMD_RET_SUCCESS;
+       }
+
+       printf("========================================\n");
+       printf("ESRT: fw_resource_count=%d\n", esrt->fw_resource_count);
+       printf("ESRT: fw_resource_count_max=%d\n", esrt->fw_resource_count_max);
+       printf("ESRT: fw_resource_version=%lld\n", esrt->fw_resource_version);
+
+       for (int idx = 0; idx < esrt->fw_resource_count; idx++) {
+               printf("[entry %d]==============================\n", idx);
+               printf("ESRT: fw_class=%pUL\n", &esrt->entries[idx].fw_class);
+               printf("ESRT: fw_type=%s\n", EFI_FW_TYPE_STR_GET(esrt->entries[idx].fw_type));
+               printf("ESRT: fw_version=%d\n", esrt->entries[idx].fw_version);
+               printf("ESRT: lowest_supported_fw_version=%d\n",
+                      esrt->entries[idx].lowest_supported_fw_version);
+               printf("ESRT: capsule_flags=%d\n",
+                      esrt->entries[idx].capsule_flags);
+               printf("ESRT: last_attempt_version=%d\n",
+                      esrt->entries[idx].last_attempt_version);
+               printf("ESRT: last_attempt_status=%s\n",
+                      EFI_FW_STATUS_STR_GET(esrt->entries[idx].last_attempt_status));
+       }
+       printf("========================================\n");
+
+       return CMD_RET_SUCCESS;
+}
+#endif /*  CONFIG_EFI_ESRT */
 /**
  * do_efi_capsule_res() - show a capsule update result
  *
@@ -221,6 +299,10 @@ static struct cmd_tbl cmd_efidebug_capsule_sub[] = {
                         "", ""),
        U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show,
                         "", ""),
+#ifdef CONFIG_EFI_ESRT
+       U_BOOT_CMD_MKENT(esrt, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_esrt,
+                        "", ""),
+#endif
        U_BOOT_CMD_MKENT(disk-update, 0, 0, do_efi_capsule_on_disk_update,
                         "", ""),
        U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res,
@@ -517,6 +599,10 @@ static const struct {
                EFI_ACPI_TABLE_GUID,
        },
        {
+               "EFI System Resource Table",
+               EFI_SYSTEM_RESOURCE_TABLE_GUID,
+       },
+       {
                "device tree",
                EFI_FDT_GUID,
        },
@@ -799,6 +885,54 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
 }
 
 /**
+ * create_initrd_dp() - Create a special device for our Boot### option
+ *
+ * @dev:       Device
+ * @part:      Disk partition
+ * @file:      Filename
+ * Return:     Pointer to the device path or ERR_PTR
+ *
+ */
+static
+struct efi_device_path *create_initrd_dp(const char *dev, const char *part,
+                                        const char *file)
+
+{
+       struct efi_device_path *tmp_dp = NULL, *tmp_fp = NULL;
+       struct efi_device_path *initrd_dp = NULL;
+       efi_status_t ret;
+       const struct efi_initrd_dp id_dp = {
+               .vendor = {
+                       {
+                       DEVICE_PATH_TYPE_MEDIA_DEVICE,
+                       DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
+                       sizeof(id_dp.vendor),
+                       },
+                       EFI_INITRD_MEDIA_GUID,
+               },
+               .end = {
+                       DEVICE_PATH_TYPE_END,
+                       DEVICE_PATH_SUB_TYPE_END,
+                       sizeof(id_dp.end),
+               }
+       };
+
+       ret = efi_dp_from_name(dev, part, file, &tmp_dp, &tmp_fp);
+       if (ret != EFI_SUCCESS) {
+               printf("Cannot create device path for \"%s %s\"\n", part, file);
+               goto out;
+       }
+
+       initrd_dp = efi_dp_append((const struct efi_device_path *)&id_dp,
+                                 tmp_fp);
+
+out:
+       efi_free_pool(tmp_dp);
+       efi_free_pool(tmp_fp);
+       return initrd_dp;
+}
+
+/**
  * do_efi_boot_add() - set UEFI load option
  *
  * @cmdtp:     Command table
@@ -810,7 +944,9 @@ static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag,
  *
  * Implement efidebug "boot add" sub-command. Create or change UEFI load option.
  *
- *     efidebug boot add <id> <label> <interface> <devnum>[:<part>] <file> <options>
+ * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file>
+ *                   -i <file> <interface2> <devnum2>[:<part>] <initrd>
+ *                   -s '<options>'
  */
 static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
                           int argc, char *const argv[])
@@ -823,55 +959,105 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
        size_t label_len, label_len16;
        u16 *label;
        struct efi_device_path *device_path = NULL, *file_path = NULL;
+       struct efi_device_path *final_fp = NULL;
+       struct efi_device_path *initrd_dp = NULL;
        struct efi_load_option lo;
        void *data = NULL;
        efi_uintn_t size;
+       efi_uintn_t fp_size = 0;
        efi_status_t ret;
        int r = CMD_RET_SUCCESS;
 
-       if (argc < 6 || argc > 7)
-               return CMD_RET_USAGE;
-
-       id = (int)simple_strtoul(argv[1], &endp, 16);
-       if (*endp != '\0' || id > 0xffff)
-               return CMD_RET_USAGE;
-
-       sprintf(var_name, "Boot%04X", id);
-       p = var_name16;
-       utf8_utf16_strncpy(&p, var_name, 9);
-
        guid = efi_global_variable_guid;
 
        /* attributes */
        lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */
+       lo.optional_data = NULL;
+       lo.label = NULL;
 
-       /* label */
-       label_len = strlen(argv[2]);
-       label_len16 = utf8_utf16_strnlen(argv[2], label_len);
-       label = malloc((label_len16 + 1) * sizeof(u16));
-       if (!label)
-               return CMD_RET_FAILURE;
-       lo.label = label; /* label will be changed below */
-       utf8_utf16_strncpy(&label, argv[2], label_len);
+       argc--;
+       argv++; /* 'add' */
+       for (; argc > 0; argc--, argv++) {
+               if (!strcmp(argv[0], "-b")) {
+                       if (argc <  5 || lo.label) {
+                               r = CMD_RET_USAGE;
+                               goto out;
+                       }
+                       id = (int)simple_strtoul(argv[1], &endp, 16);
+                       if (*endp != '\0' || id > 0xffff)
+                               return CMD_RET_USAGE;
+
+                       sprintf(var_name, "Boot%04X", id);
+                       p = var_name16;
+                       utf8_utf16_strncpy(&p, var_name, 9);
+
+                       /* label */
+                       label_len = strlen(argv[2]);
+                       label_len16 = utf8_utf16_strnlen(argv[2], label_len);
+                       label = malloc((label_len16 + 1) * sizeof(u16));
+                       if (!label)
+                               return CMD_RET_FAILURE;
+                       lo.label = label; /* label will be changed below */
+                       utf8_utf16_strncpy(&label, argv[2], label_len);
+
+                       /* file path */
+                       ret = efi_dp_from_name(argv[3], argv[4], argv[5],
+                                              &device_path, &file_path);
+                       if (ret != EFI_SUCCESS) {
+                               printf("Cannot create device path for \"%s %s\"\n",
+                                      argv[3], argv[4]);
+                               r = CMD_RET_FAILURE;
+                               goto out;
+                       }
+                       fp_size += efi_dp_size(file_path) +
+                               sizeof(struct efi_device_path);
+                       argc -= 5;
+                       argv += 5;
+               } else if (!strcmp(argv[0], "-i")) {
+                       if (argc < 3 || initrd_dp) {
+                               r = CMD_RET_USAGE;
+                               goto out;
+                       }
 
-       /* file path */
-       ret = efi_dp_from_name(argv[3], argv[4], argv[5], &device_path,
-                              &file_path);
-       if (ret != EFI_SUCCESS) {
-               printf("Cannot create device path for \"%s %s\"\n",
-                      argv[3], argv[4]);
+                       initrd_dp = create_initrd_dp(argv[1], argv[2], argv[3]);
+                       if (!initrd_dp) {
+                               printf("Cannot add an initrd\n");
+                               r = CMD_RET_FAILURE;
+                               goto out;
+                       }
+                       argc -= 3;
+                       argv += 3;
+                       fp_size += efi_dp_size(initrd_dp) +
+                               sizeof(struct efi_device_path);
+               } else if (!strcmp(argv[0], "-s")) {
+                       if (argc < 1 || lo.optional_data) {
+                               r = CMD_RET_USAGE;
+                               goto out;
+                       }
+                       lo.optional_data = (const u8 *)argv[1];
+                       argc -= 1;
+                       argv += 1;
+               } else {
+                       r = CMD_RET_USAGE;
+                       goto out;
+               }
+       }
+
+       if (!file_path) {
+               printf("Missing binary\n");
+               r = CMD_RET_USAGE;
+               goto out;
+       }
+
+       final_fp = efi_dp_concat(file_path, initrd_dp);
+       if (!final_fp) {
+               printf("Cannot create final device path\n");
                r = CMD_RET_FAILURE;
                goto out;
        }
-       lo.file_path = file_path;
-       lo.file_path_length = efi_dp_size(file_path)
-                               + sizeof(struct efi_device_path); /* for END */
 
-       /* optional data */
-       if (argc == 6)
-               lo.optional_data = NULL;
-       else
-               lo.optional_data = (const u8 *)argv[6];
+       lo.file_path = final_fp;
+       lo.file_path_length = fp_size;
 
        size = efi_serialize_load_option(&lo, (u8 **)&data);
        if (!size) {
@@ -888,8 +1074,11 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag,
                printf("Cannot set %ls\n", var_name16);
                r = CMD_RET_FAILURE;
        }
+
 out:
        free(data);
+       efi_free_pool(final_fp);
+       efi_free_pool(initrd_dp);
        efi_free_pool(device_path);
        efi_free_pool(file_path);
        free(lo.label);
@@ -955,11 +1144,14 @@ static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag,
  */
 static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
 {
+       struct efi_device_path *initrd_path = NULL;
        struct efi_load_option lo;
        char *label, *p;
        size_t label_len16, label_len;
        u16 *dp_str;
        efi_status_t ret;
+       efi_uintn_t initrd_dp_size;
+       const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
 
        ret = efi_deserialize_load_option(&lo, data, size);
        if (ret != EFI_SUCCESS) {
@@ -990,6 +1182,14 @@ static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size)
        printf("  file_path: %ls\n", dp_str);
        efi_free_pool(dp_str);
 
+       initrd_path = efi_dp_from_lo(&lo, &initrd_dp_size, lf2_initrd_guid);
+       if (initrd_path) {
+               dp_str = efi_dp_str(initrd_path);
+               printf("  initrd_path: %ls\n", dp_str);
+               efi_free_pool(dp_str);
+               efi_free_pool(initrd_path);
+       }
+
        printf("  data:\n");
        print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
                       lo.optional_data, *size, true);
@@ -1559,7 +1759,10 @@ static int do_efidebug(struct cmd_tbl *cmdtp, int flag,
 static char efidebug_help_text[] =
        "  - UEFI Shell-like interface to configure UEFI environment\n"
        "\n"
-       "efidebug boot add <bootid> <label> <interface> <devnum>[:<part>] <file path> [<load options>]\n"
+       "efidebug boot add "
+       "-b <bootid> <label> <interface> <devnum>[:<part>] <file path> "
+       "-i <interface> <devnum>[:<part>] <initrd file path> "
+       "-s '<optional data>'\n"
        "  - set UEFI BootXXXX variable\n"
        "    <load options> will be passed to UEFI application\n"
        "efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n"
@@ -1580,6 +1783,10 @@ static char efidebug_help_text[] =
        "  - show capsule information\n"
        "efidebug capsule result [<capsule result var>]\n"
        "  - show a capsule update result\n"
+#ifdef CONFIG_EFI_ESRT
+       "efidebug capsule esrt\n"
+       "  - print the ESRT\n"
+#endif
        "\n"
 #endif
        "efidebug devices\n"
@@ -1603,7 +1810,7 @@ static char efidebug_help_text[] =
 #endif
 
 U_BOOT_CMD(
-       efidebug, 10, 0, do_efidebug,
+       efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug,
        "Configure UEFI environment",
        efidebug_help_text
 );
index 5bbc39b..b7894d7 100644 (file)
 #include <command.h>
 #include <console.h>
 #include <cpu_func.h>
+#include <efi_loader.h>
 #include <env.h>
+#include <exports.h>
 #include <flash.h>
 #include <image.h>
-#include <s_record.h>
+#include <mapmem.h>
 #include <net.h>
-#include <exports.h>
+#include <s_record.h>
 #include <serial.h>
 #include <xyzModem.h>
 #include <asm/cache.h>
@@ -996,6 +998,10 @@ static ulong load_serial_ymodem(ulong offset, int mode)
                        }
 
                }
+               if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
+                       efi_set_bootdev("Uart", "", "",
+                                       map_sysmem(offset, 0), size);
+
        } else {
                printf("%s\n", xyzModem_error(err));
        }
index 9fec75f..33ce4bc 100644 (file)
@@ -60,7 +60,7 @@ to be pointing to the EFI System Partition which contains the capsule
 file. The BootNext, BootXXXX and OsIndications variables can be set
 using the following commands::
 
-    => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
+    => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
     => efidebug boot next 0
     => setenv -e -nv -bs -rt -v OsIndications =0x04
     => saveenv
@@ -198,7 +198,7 @@ command line::
     3. Set the following environment and UEFI boot variables
 
         => setenv -e -nv -bs -rt -v OsIndications =0x04
-        => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name>
+        => efidebug boot add -b 0 Boot0000 virtio 0:1 <capsule_file_name>
         => efidebug boot next 0
         => saveenv
 
index 5a67737..b3494c2 100644 (file)
@@ -178,7 +178,7 @@ Now in U-Boot install the keys on your board::
 
 Set up boot parameters on your board::
 
-    efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed ""
+    efidebug boot add -b 1 HELLO mmc 0:1 /helloworld.efi.signed ""
 
 Now your board can run the signed image via the boot manager (see below).
 You can also try this sequence by running Pytest, test_efi_secboot,
index 76b629a..8f8c425 100644 (file)
@@ -87,7 +87,23 @@ static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu)
 
 static void dfu_free_entity_sf(struct dfu_entity *dfu)
 {
-       spi_flash_free(dfu->data.sf.dev);
+       /*
+        * In the DM case it is not necessary to free the SPI device.
+        * For the non-DM case we must ensure that the the SPI device is only
+        * freed once.
+        */
+       if (!CONFIG_IS_ENABLED(DM_SPI_FLASH)) {
+               struct spi_flash *dev = dfu->data.sf.dev;
+
+               if (!dev)
+                       return;
+               dfu->data.sf.dev = NULL;
+               list_for_each_entry(dfu, &dfu_list, list) {
+                       if (dfu->data.sf.dev == dev)
+                               dfu->data.sf.dev = NULL;
+               }
+               spi_flash_free(dev);
+       }
 }
 
 static struct spi_flash *parse_dev(char *devstr)
index 48e48a6..18a1adf 100644 (file)
@@ -523,6 +523,7 @@ struct efi_device_path_acpi_path {
 #  define DEVICE_PATH_SUB_TYPE_MSG_SCSI                0x02
 #  define DEVICE_PATH_SUB_TYPE_MSG_USB         0x05
 #  define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR    0x0b
+#  define DEVICE_PATH_SUB_TYPE_MSG_UART                0x0e
 #  define DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS   0x0f
 #  define DEVICE_PATH_SUB_TYPE_MSG_SATA                0x12
 #  define DEVICE_PATH_SUB_TYPE_MSG_NVME                0x17
@@ -542,6 +543,15 @@ struct efi_device_path_scsi {
        u16 logical_unit_number;
 } __packed;
 
+struct efi_device_path_uart {
+       struct efi_device_path dp;
+       u32 reserved;
+       u64 baud_rate;
+       u8 data_bits;
+       u8 parity;
+       u8 stop_bits;
+} __packed;
+
 struct efi_device_path_usb {
        struct efi_device_path dp;
        u8 parent_port_number;
@@ -1722,6 +1732,23 @@ struct efi_load_file_protocol {
                                         void *buffer);
 };
 
+struct efi_system_resource_entry {
+       efi_guid_t fw_class;
+       u32 fw_type;
+       u32 fw_version;
+       u32 lowest_supported_fw_version;
+       u32 capsule_flags;
+       u32 last_attempt_version;
+       u32 last_attempt_status;
+} __packed;
+
+struct efi_system_resource_table {
+       u32 fw_resource_count;
+       u32 fw_resource_count_max;
+       u64 fw_resource_version;
+       struct efi_system_resource_entry entries[];
+} __packed;
+
 /* Boot manager load options */
 #define LOAD_OPTION_ACTIVE             0x00000001
 #define LOAD_OPTION_FORCE_RECONNECT    0x00000002
@@ -1740,6 +1767,10 @@ struct efi_load_file_protocol {
 #define ESRT_FW_TYPE_DEVICEFIRMWARE    0x00000002
 #define ESRT_FW_TYPE_UEFIDRIVER                0x00000003
 
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID\
+       EFI_GUID(0xb122a263, 0x3661, 0x4f68,\
+               0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80)
+
 /* Last Attempt Status Values */
 #define LAST_ATTEMPT_STATUS_SUCCESS                    0x00000000
 #define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL         0x00000001
index 68daa1a..de1a496 100644 (file)
@@ -214,6 +214,8 @@ extern const efi_guid_t efi_guid_rng_protocol;
 extern const efi_guid_t efi_guid_capsule_report;
 /* GUID of firmware management protocol */
 extern const efi_guid_t efi_guid_firmware_management_protocol;
+/* GUID for the ESRT */
+extern const efi_guid_t efi_esrt_guid;
 
 extern unsigned int __efi_runtime_start, __efi_runtime_stop;
 extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@@ -437,6 +439,7 @@ efi_status_t efi_net_register(void);
 /* Called by bootefi to make the watchdog available */
 efi_status_t efi_watchdog_register(void);
 efi_status_t efi_initrd_register(void);
+void efi_initrd_deregister(void);
 /* Called by bootefi to make SMBIOS tables available */
 /**
  * efi_acpi_register() - write out ACPI tables
@@ -558,6 +561,15 @@ struct efi_simple_file_system_protocol *efi_simple_file_system(
 /* open file from device-path: */
 struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp);
 
+/* Registers a callback function for a notification event. */
+efi_status_t EFIAPI efi_register_protocol_notify(const efi_guid_t *protocol,
+                                                struct efi_event *event,
+                                                void **registration);
+efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size);
+
+/* get a device path from a Boot#### option */
+struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid);
+
 /**
  * efi_size_in_pages() - convert size in bytes to size in pages
  *
@@ -723,6 +735,8 @@ efi_status_t EFIAPI efi_query_variable_info(
                        u64 *remaining_variable_storage_size,
                        u64 *maximum_variable_size);
 
+void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size);
+
 /*
  * 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:
@@ -744,6 +758,10 @@ struct efi_load_option {
        const u8 *optional_data;
 };
 
+struct efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+                                      efi_uintn_t *size, efi_guid_t guid);
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+                                     const struct efi_device_path *dp2);
 efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
                                         efi_uintn_t *size);
 unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
@@ -890,4 +908,22 @@ static inline efi_status_t efi_launch_capsules(void)
 
 #endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
 
+/**
+ * Install the ESRT system table.
+ *
+ * @return     status code
+ */
+efi_status_t efi_esrt_register(void);
+
+/**
+ * efi_esrt_populate() - Populates the ESRT entries from the FMP instances
+ * present in the system.
+ * If an ESRT already exists, the old ESRT is replaced in the system table.
+ * The memory of the old ESRT is deallocated.
+ *
+ * Return:
+ * - EFI_SUCCESS if the ESRT is correctly created
+ * - error code otherwise.
+ */
+efi_status_t efi_esrt_populate(void);
 #endif /* _EFI_LOADER_H */
index 1515fda..07b6199 100644 (file)
@@ -66,11 +66,10 @@ enum efi_test_phase {
         */
        EFI_SETUP_BEFORE_BOOTTIME_EXIT,
        /**
-        * @EFI_SETUP_AFTER_BOOTTIME_EXIT: - setup after ExitBootServices
-        *
-        * Setup, execute, and teardown are executed after ExitBootServices().
+        * @EFI_SETTING_VIRTUAL_ADDRESS_MAP - calls SetVirtualAddressMap()
+        * Execute calls SetVirtualAddressMap().
         */
-       EFI_SETUP_AFTER_BOOTTIME_EXIT,
+       EFI_SETTING_VIRTUAL_ADDRESS_MAP,
 };
 
 extern struct efi_simple_text_output_protocol *con_out;
index e729f72..e44f004 100644 (file)
@@ -281,7 +281,7 @@ config EFI_HAVE_RUNTIME_RESET
 
 config EFI_GRUB_ARM32_WORKAROUND
        bool "Workaround for GRUB on 32bit ARM"
-       default n if ARCH_QEMU
+       default n if ARCH_BCM283X || ARCH_SUNXI || ARCH_QEMU
        default y
        depends on ARM && !ARM64
        help
@@ -315,18 +315,13 @@ config EFI_TCG2_PROTOCOL_EVENTLOG_SIZE
 
 config EFI_LOAD_FILE2_INITRD
        bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
-       default n
-       help
-         Expose a EFI_FILE_LOAD2_PROTOCOL that the Linux UEFI stub can
-         use to load the initial ramdisk. Once this is enabled using
-         initrd=<ramdisk> will stop working.
-
-config EFI_INITRD_FILESPEC
-       string "initramfs path"
-       default "host 0:1 initrd"
-       depends on EFI_LOAD_FILE2_INITRD
+       default y
        help
-         Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
+         Linux v5.7 and later can make use of this option. If the boot option
+         selected by the UEFI boot manager specifies an existing file to be used
+         as initial RAM disk, a Linux specific Load File2 protocol will be
+         installed and Linux 5.7+ will ignore any initrd=<ramdisk> command line
+         argument.
 
 config EFI_SECURE_BOOT
        bool "Enable EFI secure boot support"
@@ -347,4 +342,11 @@ config EFI_SECURE_BOOT
          it is signed with a trusted key. To do that, you need to install,
          at least, PK, KEK and db.
 
+config EFI_ESRT
+       bool "Enable the UEFI ESRT generation"
+       depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
+       default y
+       help
+         Enabling this option creates the ESRT UEFI system table.
+
 endif
index 10b42e8..8bd343e 100644 (file)
@@ -23,6 +23,7 @@ endif
 obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
 obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += efi_bootmgr.o
 obj-y += efi_boottime.o
+obj-y += efi_helper.o
 obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
 obj-$(CONFIG_EFI_CAPSULE_FIRMWARE) += efi_firmware.o
 obj-y += efi_console.o
@@ -52,6 +53,7 @@ obj-y += efi_variable.o
 obj-$(CONFIG_EFI_VARIABLES_PRESEED) += efi_var_seed.o
 endif
 obj-y += efi_watchdog.o
+obj-$(CONFIG_EFI_ESRT) += efi_esrt.o
 obj-$(CONFIG_LCD) += efi_gop.o
 obj-$(CONFIG_DM_VIDEO) += efi_gop.o
 obj-$(CONFIG_PARTITIONS) += efi_disk.o
index 25f5ceb..46c8011 100644 (file)
@@ -118,11 +118,13 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
                ret = efi_set_variable_int(L"BootCurrent",
                                           &efi_global_variable_guid,
                                           attributes, sizeof(n), &n, false);
-               if (ret != EFI_SUCCESS) {
-                       if (EFI_CALL(efi_unload_image(*handle))
-                           != EFI_SUCCESS)
-                               log_err("Unloading image failed\n");
-                       goto error;
+               if (ret != EFI_SUCCESS)
+                       goto unload;
+               /* try to register load file2 for initrd's */
+               if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
+                       ret = efi_initrd_register();
+                       if (ret != EFI_SUCCESS)
+                               goto unload;
                }
 
                log_info("Booting: %ls\n", lo.label);
@@ -147,6 +149,13 @@ error:
        free(load_option);
 
        return ret;
+
+unload:
+       if (EFI_CALL(efi_unload_image(*handle)) != EFI_SUCCESS)
+               log_err("Unloading image failed\n");
+       free(load_option);
+
+       return ret;
 }
 
 /**
index 41b8949..4777b35 100644 (file)
@@ -1406,10 +1406,9 @@ out:
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_register_protocol_notify(
-                                               const efi_guid_t *protocol,
-                                               struct efi_event *event,
-                                               void **registration)
+efi_status_t EFIAPI efi_register_protocol_notify(const efi_guid_t *protocol,
+                                                struct efi_event *event,
+                                                void **registration)
 {
        struct efi_register_notify_event *item;
        efi_status_t ret = EFI_SUCCESS;
@@ -1877,7 +1876,6 @@ static
 efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
                                      void **buffer, efi_uintn_t *size)
 {
-       struct efi_file_info *info = NULL;
        struct efi_file_handle *f;
        efi_status_t ret;
        u64 addr;
@@ -1888,18 +1886,7 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
        if (!f)
                return EFI_NOT_FOUND;
 
-       /* Get file size */
-       bs = 0;
-       EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
-                                 &bs, info));
-       if (ret != EFI_BUFFER_TOO_SMALL) {
-               ret =  EFI_DEVICE_ERROR;
-               goto error;
-       }
-
-       info = malloc(bs);
-       EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs,
-                                 info));
+       ret = efi_file_size(f, &bs);
        if (ret != EFI_SUCCESS)
                goto error;
 
@@ -1909,7 +1896,6 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
         * allocate a buffer as EFI_BOOT_SERVICES_DATA. The caller has to
         * update the reservation according to the image type.
         */
-       bs = info->file_size;
        ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
                                 EFI_BOOT_SERVICES_DATA,
                                 efi_size_in_pages(bs), &addr);
@@ -1926,7 +1912,6 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
        *size = bs;
 error:
        EFI_CALL(f->close(f));
-       free(info);
        return ret;
 }
 
index b57f030..9df9c35 100644 (file)
@@ -449,7 +449,7 @@ efi_status_t EFIAPI efi_update_capsule(
        unsigned int i;
        efi_status_t ret;
 
-       EFI_ENTRY("%p, %lu, %llu\n", capsule_header_array, capsule_count,
+       EFI_ENTRY("%p, %zu, %llu\n", capsule_header_array, capsule_count,
                  scatter_gather_list);
 
        if (!capsule_count) {
@@ -482,6 +482,14 @@ efi_status_t EFIAPI efi_update_capsule(
                        goto out;
        }
 out:
+
+       if (IS_ENABLED(CONFIG_EFI_ESRT)) {
+               /* Rebuild the ESRT to reflect any updated FW images. */
+               ret = efi_esrt_populate();
+               if (ret != EFI_SUCCESS)
+                       log_warning("EFI Capsule: failed to update ESRT\n");
+       }
+
        return EFI_EXIT(ret);
 }
 
@@ -509,7 +517,7 @@ efi_status_t EFIAPI efi_query_capsule_caps(
        unsigned int i;
        efi_status_t ret;
 
-       EFI_ENTRY("%p, %lu, %p, %p\n", capsule_header_array, capsule_count,
+       EFI_ENTRY("%p, %zu, %p, %p\n", capsule_header_array, capsule_count,
                  maximum_capsule_size, reset_type);
 
        if (!maximum_capsule_size) {
index c400355..6040f3a 100644 (file)
@@ -254,7 +254,7 @@ static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
 }
 
 /**
- * query_console_serial() - query console size
+ * query_console_serial() - query serial console size
  *
  * When using a serial console or the net console we can only devise the
  * terminal size by querying the terminal using ECMA-48 control sequences.
@@ -300,6 +300,37 @@ out:
 }
 
 /**
+ * query_vidconsole() - query video console size
+ *
+ *
+ * @rows:      pointer to return number of rows
+ * @cols:      pointer to return number of columns
+ * Returns:    0 on success
+ */
+static int __maybe_unused query_vidconsole(int *rows, int *cols)
+{
+       const char *stdout_name = env_get("stdout");
+       struct stdio_dev *stdout_dev;
+       struct udevice *dev;
+       struct vidconsole_priv *priv;
+
+       if (!stdout_name || strncmp(stdout_name, "vidconsole", 10))
+               return -ENODEV;
+       stdout_dev = stdio_get_by_name("vidconsole");
+       if (!stdout_dev)
+               return -ENODEV;
+       dev = stdout_dev->priv;
+       if (!dev)
+               return -ENODEV;
+       priv = dev_get_uclass_priv(dev);
+       if (!priv)
+               return -ENODEV;
+       *rows = priv->rows;
+       *cols = priv->cols;
+       return 0;
+}
+
+/**
  * query_console_size() - update the mode table.
  *
  * By default the only mode available is 80x25. If the console has at least 50
@@ -308,21 +339,15 @@ out:
  */
 static void query_console_size(void)
 {
-       const char *stdout_name = env_get("stdout");
        int rows = 25, cols = 80;
+       int ret = -ENODEV;
 
-       if (stdout_name && !strncmp(stdout_name, "vidconsole", 10) &&
-           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)) {
+       if IS_ENABLED(CONFIG_DM_VIDEO)
+               ret = query_vidconsole(&rows, &cols);
+       if (ret)
+               ret = query_console_serial(&rows, &cols);
+       if (ret)
                return;
-       }
 
        /* Test if we can have Mode 1 */
        if (cols >= 80 && rows >= 50) {
index c9315dd..4b20859 100644 (file)
@@ -282,11 +282,31 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
        return ndp;
 }
 
-struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
-                                     const struct efi_device_path *dp2)
+/**
+ * efi_dp_append_or_concatenate() - Append or concatenate two device paths.
+ *                                 Concatenated device path will be separated
+ *                                 by a sub-type 0xff end node
+ *
+ * @dp1:       First device path
+ * @dp2:       Second device path
+ * @concat:    If true the two device paths will be concatenated and separated
+ *             by an end of entrire device path sub-type 0xff end node.
+ *             If true the second device path will be appended to the first and
+ *             terminated by an end node
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+static struct
+efi_device_path *efi_dp_append_or_concatenate(const struct efi_device_path *dp1,
+                                             const struct efi_device_path *dp2,
+                                             bool concat)
 {
        struct efi_device_path *ret;
+       size_t end_size = sizeof(END);
 
+       if (concat)
+               end_size = 2 * sizeof(END);
        if (!dp1 && !dp2) {
                /* return an end node */
                ret = efi_dp_dup(&END);
@@ -298,18 +318,58 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
                /* 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));
+               void *p = dp_alloc(sz1 + sz2 + end_size);
                if (!p)
                        return NULL;
+               ret = p;
                memcpy(p, dp1, sz1);
+               p += sz1;
+
+               if (concat) {
+                       memcpy(p, &END, sizeof(END));
+                       p += sizeof(END);
+               }
+
                /* the end node of the second device path has to be retained */
-               memcpy(p + sz1, dp2, sz2 + sizeof(END));
-               ret = p;
+               memcpy(p, dp2, sz2);
+               p += sz2;
+               memcpy(p, &END, sizeof(END));
        }
 
        return ret;
 }
 
+/**
+ * efi_dp_append() - Append a device to an existing device path.
+ *
+ * @dp1:       First device path
+ * @dp2:       Second device path
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+                                     const struct efi_device_path *dp2)
+{
+       return efi_dp_append_or_concatenate(dp1, dp2, false);
+}
+
+/**
+ * efi_dp_concat() - Concatenate 2 device paths. The final device path will
+ *                   contain two device paths separated by and end node (0xff).
+ *
+ * @dp1:       First device path
+ * @dp2:       Second device path
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+                                     const struct efi_device_path *dp2)
+{
+       return efi_dp_append_or_concatenate(dp1, dp2, true);
+}
+
 struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
                                           const struct efi_device_path *node)
 {
@@ -960,6 +1020,28 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
        return start;
 }
 
+struct efi_device_path *efi_dp_from_uart(void)
+{
+       void *buf, *pos;
+       struct efi_device_path_uart *uart;
+       size_t dpsize = sizeof(ROOT) + sizeof(*uart) + sizeof(END);
+
+       buf = dp_alloc(dpsize);
+       if (!buf)
+               return NULL;
+       pos = buf;
+       memcpy(pos, &ROOT, sizeof(ROOT));
+       pos += sizeof(ROOT);
+       uart = pos;
+       uart->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+       uart->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_UART;
+       uart->dp.length = sizeof(*uart);
+       pos += sizeof(*uart);
+       memcpy(pos, &END, sizeof(END));
+
+       return buf;
+}
+
 #ifdef CONFIG_NET
 struct efi_device_path *efi_dp_from_eth(void)
 {
@@ -1086,7 +1168,6 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
                              struct efi_device_path **device,
                              struct efi_device_path **file)
 {
-       int is_net;
        struct blk_desc *desc = NULL;
        struct disk_partition fs_partition;
        int part = 0;
@@ -1096,8 +1177,15 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
        if (path && !file)
                return EFI_INVALID_PARAMETER;
 
-       is_net = !strcmp(dev, "Net");
-       if (!is_net) {
+       if (!strcmp(dev, "Net")) {
+#ifdef CONFIG_NET
+               if (device)
+                       *device = efi_dp_from_eth();
+#endif
+       } else if (!strcmp(dev, "Uart")) {
+               if (device)
+                       *device = efi_dp_from_uart();
+       } else {
                part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
                                               1);
                if (part < 0 || !desc)
@@ -1105,11 +1193,6 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
 
                if (device)
                        *device = efi_dp_from_part(desc, part);
-       } else {
-#ifdef CONFIG_NET
-               if (device)
-                       *device = efi_dp_from_eth();
-#endif
        }
 
        if (!path)
@@ -1120,7 +1203,7 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
        s = filename;
        while ((s = strchr(s, '/')))
                *s++ = '\\';
-       *file = efi_dp_from_file(is_net ? NULL : desc, part, filename);
+       *file = efi_dp_from_file(desc, part, filename);
 
        if (!*file)
                return EFI_INVALID_PARAMETER;
@@ -1160,3 +1243,43 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp,
                dp = (const struct efi_device_path *)((const u8 *)dp + len);
        }
 }
+
+/**
+ * efi_dp_from_lo() - Get the instance of a VenMedia node in a
+ *                    multi-instance device path that matches
+ *                    a specific GUID. This kind of device paths
+ *                    is found in Boot#### options describing an
+ *                    initrd location
+ *
+ * @lo:                EFI_LOAD_OPTION containing a valid device path
+ * @size:      size of the discovered device path
+ * @guid:      guid to search for
+ *
+ * Return:
+ * device path including the VenMedia node or NULL.
+ * Caller must free the returned value.
+ */
+struct
+efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+                               efi_uintn_t *size, efi_guid_t guid)
+{
+       struct efi_device_path *fp = lo->file_path;
+       struct efi_device_path_vendor *vendor;
+       int lo_len = lo->file_path_length;
+
+       for (; lo_len >=  sizeof(struct efi_device_path);
+            lo_len -= fp->length, fp = (void *)fp + fp->length) {
+               if (lo_len < 0 || efi_dp_check_length(fp, lo_len) < 0)
+                       break;
+               if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
+                   fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH)
+                       continue;
+
+               vendor = (struct efi_device_path_vendor *)fp;
+               if (!guidcmp(&vendor->guid, &guid))
+                       return efi_dp_dup(fp);
+       }
+       log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label);
+
+       return NULL;
+}
index edc9fdc..43554cd 100644 (file)
@@ -118,6 +118,19 @@ static char *dp_msging(char *s, struct efi_device_path *dp)
                             ide->logical_unit_number);
                break;
        }
+       case DEVICE_PATH_SUB_TYPE_MSG_UART: {
+               struct efi_device_path_uart *uart =
+                       (struct efi_device_path_uart *)dp;
+               s += sprintf(s, "Uart(%lld,%d,%d,", uart->baud_rate,
+                            uart->data_bits, uart->parity);
+               switch (uart->stop_bits) {
+               case 2:
+                       s += sprintf(s, "1.5)");
+               default:
+                       s += sprintf(s, "%d)", uart->stop_bits);
+               }
+               break;
+       }
        case DEVICE_PATH_SUB_TYPE_MSG_USB: {
                struct efi_device_path_usb *udp =
                        (struct efi_device_path_usb *)dp;
index a4529ee..b6fe5d2 100644 (file)
@@ -61,7 +61,7 @@ void efi_carve_out_dt_rsv(void *fdt)
        for (i = 0; i < nr_rsv; i++) {
                if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0)
                        continue;
-               efi_reserve_memory(addr, size, false);
+               efi_reserve_memory(addr, size, true);
        }
 
        /* process reserved-memory */
diff --git a/lib/efi_loader/efi_esrt.c b/lib/efi_loader/efi_esrt.c
new file mode 100644 (file)
index 0000000..947bdb5
--- /dev/null
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  EFI application ESRT tables support
+ *
+ *  Copyright (C) 2021 Arm Ltd.
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <efi_api.h>
+#include <malloc.h>
+
+const efi_guid_t efi_esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
+
+static struct efi_system_resource_table *esrt;
+
+#define EFI_ESRT_VERSION 1
+
+/**
+ * efi_esrt_image_info_to_entry() - copy the information present in a fw image
+ * descriptor to a ESRT entry.
+ * The function ensures the ESRT entry matches the image_type_id in @img_info.
+ * In case of a mismatch we leave the entry unchanged.
+ *
+ * @img_info:     the source image info descriptor
+ * @entry:        pointer to the ESRT entry to be filled
+ * @desc_version: the version of the elements in img_info
+ * @image_type:   the image type value to be set in the ESRT entry
+ * @flags:        the capsule flags value to be set in the ESRT entry
+ *
+ * Return:
+ * - EFI_SUCCESS if the entry is correctly updated
+ * - EFI_INVALID_PARAMETER if entry does not match image_type_id in @img_info.
+ */
+static efi_status_t
+efi_esrt_image_info_to_entry(struct efi_firmware_image_descriptor *img_info,
+                            struct efi_system_resource_entry *entry,
+                            u32 desc_version, u32 image_type, u32 flags)
+{
+       if (guidcmp(&entry->fw_class, &img_info->image_type_id)) {
+               EFI_PRINT("ESRT entry %pUL mismatches img_type_id %pUL\n",
+                         &entry->fw_class, &img_info->image_type_id);
+               return EFI_INVALID_PARAMETER;
+       }
+
+       entry->fw_version = img_info->version;
+
+       entry->fw_type = image_type;
+       entry->capsule_flags = flags;
+
+       /*
+        * The field lowest_supported_image_version is only present
+        * on image info structure of version 2 or greater.
+        * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
+        */
+       if (desc_version >= 2)
+               entry->lowest_supported_fw_version =
+                       img_info->lowest_supported_image_version;
+       else
+               entry->lowest_supported_fw_version = 0;
+
+       /*
+        * The fields last_attempt_version and last_attempt_status
+        * are only present on image info structure of version 3 or
+        * greater.
+        * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
+        */
+       if (desc_version >= 3) {
+               entry->last_attempt_version =
+                       img_info->last_attempt_version;
+
+               entry->last_attempt_status =
+                       img_info->last_attempt_status;
+       } else {
+               entry->last_attempt_version = 0;
+               entry->last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
+       }
+
+       return EFI_SUCCESS;
+}
+
+/**
+ * efi_esrt_entries_to_size() - Obtain the bytes used by an ESRT
+ * datastructure with @num_entries.
+ *
+ * @num_entries: the number of entries in the ESRT.
+ *
+ * Return: the number of bytes an ESRT with @num_entries occupies in memory.
+ */
+static
+inline u32 efi_esrt_entries_to_size(u32 num_entries)
+{
+       u32 esrt_size = sizeof(struct efi_system_resource_table) +
+               num_entries * sizeof(struct efi_system_resource_entry);
+
+       return esrt_size;
+}
+
+/**
+ * efi_esrt_allocate_install() - Allocates @num_entries for the ESRT and
+ * performs basic ESRT initialization.
+ *
+ * @num_entries: the number of entries that the ESRT will hold.
+ *
+ * Return:
+ * - pointer to the ESRT if successful.
+ * - NULL otherwise.
+ */
+static
+efi_status_t efi_esrt_allocate_install(u32 num_entries)
+{
+       efi_status_t ret;
+       struct efi_system_resource_table *new_esrt;
+       u32 size = efi_esrt_entries_to_size(num_entries);
+       efi_guid_t esrt_guid = efi_esrt_guid;
+
+       /* Reserve num_pages for ESRT */
+       ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, size,
+                               (void **)&new_esrt);
+
+       if (ret != EFI_SUCCESS) {
+               EFI_PRINT("ESRT cannot allocate memory for %d entries (%d bytes)\n",
+                         num_entries, efi_esrt_entries_to_size(num_entries));
+
+               return ret;
+       }
+
+       new_esrt->fw_resource_count_max = num_entries;
+       new_esrt->fw_resource_count = 0;
+       new_esrt->fw_resource_version = EFI_ESRT_VERSION;
+
+       /* Install the ESRT in the system configuration table. */
+       ret = efi_install_configuration_table(&esrt_guid, (void *)new_esrt);
+       if (ret != EFI_SUCCESS) {
+               EFI_PRINT("ESRT failed to install the ESRT in the system table\n");
+               return ret;
+       }
+
+       /* If there was a previous ESRT, deallocate its memory now. */
+       if (esrt)
+               ret = EFI_CALL(efi_free_pool(esrt));
+
+       esrt = new_esrt;
+
+       return EFI_SUCCESS;
+}
+
+/**
+ * esrt_find_entry() - Obtain the ESRT entry for the image with GUID
+ * @img_fw_class.
+ *
+ * If the img_fw_class is not yet present in the ESRT, this function
+ * reserves the tail element of the current ESRT as the entry for that fw_class.
+ * The number of elements in the ESRT is updated in that case.
+ *
+ * @img_fw_class: the GUID of the FW image which ESRT entry we want to obtain.
+ *
+ * Return:
+ *  - A pointer to the ESRT entry for the image with GUID img_fw_class,
+ *  - NULL if:
+ *   - there is no more space in the ESRT,
+ *   - ESRT is not initialized,
+ */
+static
+struct efi_system_resource_entry *esrt_find_entry(efi_guid_t *img_fw_class)
+{
+       u32 filled_entries;
+       u32 max_entries;
+       struct efi_system_resource_entry *entry;
+
+       if (!esrt) {
+               EFI_PRINT("ESRT access before initialized\n");
+               return NULL;
+       }
+
+       filled_entries = esrt->fw_resource_count;
+       entry = esrt->entries;
+
+       /* Check if the image with img_fw_class is already in the ESRT. */
+       for (u32 idx = 0; idx < filled_entries; idx++) {
+               if (!guidcmp(&entry[idx].fw_class, img_fw_class)) {
+                       EFI_PRINT("ESRT found entry for image %pUl at index %d\n",
+                                 img_fw_class, idx);
+                       return &entry[idx];
+               }
+       }
+
+       max_entries = esrt->fw_resource_count_max;
+       /*
+        * Since the image with img_fw_class is not present in the ESRT, check
+        * if ESRT is full before appending the new entry to it.
+        */
+       if (filled_entries == max_entries) {
+               EFI_PRINT("ESRT full, this should not happen\n");
+               return NULL;
+       }
+
+       /*
+        * This is a new entry for a fw image, increment the element
+        * number in the table and set the fw_class field.
+        */
+       esrt->fw_resource_count++;
+       entry[filled_entries].fw_class = *img_fw_class;
+       EFI_PRINT("ESRT allocated new entry for image %pUl at index %d\n",
+                 img_fw_class, filled_entries);
+
+       return &entry[filled_entries];
+}
+
+/**
+ * efi_esrt_add_from_fmp() - Populates a sequence of ESRT entries from the FW
+ * images in the FMP.
+ *
+ * @fmp: the FMP instance from which FW images are added to the ESRT
+ *
+ * Return:
+ * - EFI_SUCCESS if all the FW images in the FMP are added to the ESRT
+ * - Error status otherwise
+ */
+static
+efi_status_t efi_esrt_add_from_fmp(struct efi_firmware_management_protocol *fmp)
+{
+       struct efi_system_resource_entry *entry = NULL;
+       size_t info_size = 0;
+       struct efi_firmware_image_descriptor *img_info = NULL;
+       u32 desc_version;
+       u8 desc_count;
+       size_t desc_size;
+       u32 package_version;
+       u16 *package_version_name;
+       efi_status_t ret = EFI_SUCCESS;
+
+       /*
+        * TODO: set the field image_type depending on the FW image type
+        * defined in a platform basis.
+        */
+       u32 image_type = ESRT_FW_TYPE_UNKNOWN;
+
+       /* TODO: set the capsule flags as a function of the FW image type. */
+       u32 flags = 0;
+
+       ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+                                          &desc_version, &desc_count,
+                                          &desc_size, NULL, NULL));
+
+       if (ret != EFI_BUFFER_TOO_SMALL) {
+               /*
+                * An input of info_size=0 should always lead
+                * fmp->get_image_info to return BUFFER_TO_SMALL.
+                */
+               EFI_PRINT("Erroneous FMP implementation\n");
+               return EFI_INVALID_PARAMETER;
+       }
+
+       ret = EFI_CALL(efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
+                                        (void **)&img_info));
+       if (ret != EFI_SUCCESS) {
+               EFI_PRINT("ESRT failed to allocate memory for image info.\n");
+               return ret;
+       }
+
+       ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+                                          &desc_version, &desc_count,
+                                          &desc_size, &package_version,
+                                          &package_version_name));
+       if (ret != EFI_SUCCESS) {
+               EFI_PRINT("ESRT failed to obtain the FMP image info\n");
+               goto out;
+       }
+
+       /*
+        * Iterate over all the FW images in the FMP.
+        */
+       for (u32 desc_idx = 0; desc_idx < desc_count; desc_idx++) {
+               struct efi_firmware_image_descriptor *cur_img_info =
+                       (struct efi_firmware_image_descriptor *)
+                       ((uintptr_t)img_info + desc_idx * desc_size);
+
+               /*
+                * Obtain the ESRT entry for the FW image with fw_class
+                * equal to cur_img_info->image_type_id.
+                */
+               entry = esrt_find_entry(&cur_img_info->image_type_id);
+
+               if (entry) {
+                       ret = efi_esrt_image_info_to_entry(cur_img_info, entry,
+                                                          desc_version,
+                                                          image_type, flags);
+                       if (ret != EFI_SUCCESS)
+                               EFI_PRINT("ESRT entry mismatches image_type\n");
+
+               } else {
+                       EFI_PRINT("ESRT failed to add entry for %pUl\n",
+                                 &cur_img_info->image_type_id);
+                       continue;
+               }
+       }
+
+out:
+       EFI_CALL(efi_free_pool(img_info));
+       return EFI_SUCCESS;
+}
+
+/**
+ * efi_esrt_populate() - Populates the ESRT entries from the FMP instances
+ * present in the system.
+ * If an ESRT already exists, the old ESRT is replaced in the system table.
+ * The memory of the old ESRT is deallocated.
+ *
+ * Return:
+ * - EFI_SUCCESS if the ESRT is correctly created
+ * - error code otherwise.
+ */
+efi_status_t efi_esrt_populate(void)
+{
+       efi_handle_t *base_handle = NULL;
+       efi_handle_t *it_handle;
+       size_t no_handles = 0;
+       struct efi_firmware_management_protocol *fmp;
+       efi_status_t ret;
+       u32 num_entries = 0;
+       struct efi_handler *handler;
+
+       /*
+        * Obtain the number of registered FMP handles.
+        */
+       ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
+                                               &efi_guid_firmware_management_protocol,
+                                               NULL, &no_handles,
+                                               (efi_handle_t **)&base_handle));
+
+       if (ret != EFI_SUCCESS) {
+               EFI_PRINT("ESRT There are no FMP instances\n");
+
+               ret = efi_esrt_allocate_install(0);
+               if (ret != EFI_SUCCESS) {
+                       EFI_PRINT("ESRT failed to create table with 0 entries\n");
+                       return ret;
+               }
+               return EFI_SUCCESS;
+       }
+
+       EFI_PRINT("ESRT populate esrt from (%ld) available FMP handles\n",
+                 no_handles);
+
+       /*
+        * Iterate over all FMPs to determine an upper bound on the number of
+        * ESRT entries.
+        */
+       it_handle = base_handle;
+       for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
+               struct efi_firmware_image_descriptor *img_info = NULL;
+               size_t info_size = 0;
+               u32 desc_version = 0;
+               u8 desc_count = 0;
+               size_t desc_size = 0;
+               u32 package_version;
+               u16 *package_version_name;
+
+               ret = efi_search_protocol(*it_handle,
+                                         &efi_guid_firmware_management_protocol,
+                                         &handler);
+
+               if (ret != EFI_SUCCESS) {
+                       EFI_PRINT("ESRT Unable to find FMP handle (%d)\n",
+                                 idx);
+                       goto out;
+               }
+               fmp = handler->protocol_interface;
+
+               ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, NULL,
+                                                  &desc_version, &desc_count,
+                                                  &desc_size, &package_version,
+                                                  &package_version_name));
+
+               if (ret != EFI_BUFFER_TOO_SMALL) {
+                       /*
+                        * An input of info_size=0 should always lead
+                        * fmp->get_image_info to return BUFFER_TO_SMALL.
+                        */
+                       EFI_PRINT("ESRT erroneous FMP implementation\n");
+                       ret = EFI_INVALID_PARAMETER;
+                       goto out;
+               }
+
+               ret = EFI_CALL(efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
+                                                (void **)&img_info));
+               if (ret != EFI_SUCCESS) {
+                       EFI_PRINT("ESRT failed to allocate memory for image info\n");
+                       goto out;
+               }
+
+               /*
+                * Calls to a FMP get_image_info method do not return the
+                * desc_count value if the return status differs from EFI_SUCCESS.
+                * We need to repeat the call to get_image_info with a properly
+                * sized buffer in order to obtain the real number of images
+                * handled by the FMP.
+                */
+               ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+                                                  &desc_version, &desc_count,
+                                                  &desc_size, &package_version,
+                                                  &package_version_name));
+
+               if (ret != EFI_SUCCESS) {
+                       EFI_PRINT("ESRT failed to obtain image info from FMP\n");
+                       EFI_CALL(efi_free_pool(img_info));
+                       goto out;
+               }
+
+               num_entries += desc_count;
+
+               EFI_CALL(efi_free_pool(img_info));
+       }
+
+       EFI_PRINT("ESRT create table with %d entries\n", num_entries);
+       /*
+        * Allocate an ESRT with the sufficient number of entries to accommodate
+        * all the FMPs in the system.
+        */
+       ret = efi_esrt_allocate_install(num_entries);
+       if (ret != EFI_SUCCESS) {
+               EFI_PRINT("ESRT failed to initialize table\n");
+               goto out;
+       }
+
+       /*
+        * Populate the ESRT entries with all existing FMP.
+        */
+       it_handle = base_handle;
+       for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
+               ret = EFI_CALL(efi_search_protocol(*it_handle,
+                                                  &efi_guid_firmware_management_protocol,
+                                                  &handler));
+
+               if (ret != EFI_SUCCESS) {
+                       EFI_PRINT("ESRT unable to find FMP handle (%d)\n",
+                                 idx);
+                       break;
+               }
+               fmp = handler->protocol_interface;
+
+               ret = efi_esrt_add_from_fmp(fmp);
+               if (ret != EFI_SUCCESS)
+                       EFI_PRINT("ESRT failed to add FMP to the table\n");
+       }
+
+out:
+
+       EFI_CALL(efi_free_pool(base_handle));
+
+       return ret;
+}
+
+/**
+ * efi_esrt_new_fmp_notify() - Callback for the EVT_NOTIFY_SIGNAL event raised
+ * when a new FMP protocol instance is registered in the system.
+ */
+static void EFIAPI efi_esrt_new_fmp_notify(struct efi_event *event,
+                                          void *context)
+{
+       efi_status_t ret;
+
+       EFI_ENTRY();
+
+       ret = efi_esrt_populate();
+       if (ret != EFI_SUCCESS)
+               EFI_PRINT("ESRT failed to populate ESRT entry\n");
+
+       EFI_EXIT(ret);
+}
+
+/**
+ * efi_esrt_register() - Install the ESRT system table.
+ *
+ * Return: status code
+ */
+efi_status_t efi_esrt_register(void)
+{
+       struct efi_event *ev = NULL;
+       void *registration;
+       efi_status_t ret;
+
+       EFI_PRINT("ESRT creation start\n");
+
+       ret = efi_esrt_populate();
+       if (ret != EFI_SUCCESS) {
+               EFI_PRINT("ESRT failed to initiate the table\n");
+               return ret;
+       }
+
+       ret = EFI_CALL(efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+                                       efi_esrt_new_fmp_notify, NULL, NULL, &ev));
+       if (ret != EFI_SUCCESS) {
+               EFI_PRINT("ESRT failed to create event\n");
+               return ret;
+       }
+
+       ret = EFI_CALL(efi_register_protocol_notify(&efi_guid_firmware_management_protocol,
+                                                   ev, &registration));
+       if (ret != EFI_SUCCESS) {
+               EFI_PRINT("ESRT failed to register FMP callback\n");
+               return ret;
+       }
+
+       EFI_PRINT("ESRT table created\n");
+
+       return ret;
+}
index 8ece8e7..204105e 100644 (file)
@@ -409,6 +409,45 @@ static efi_status_t efi_get_file_size(struct file_handle *fh,
        return EFI_SUCCESS;
 }
 
+/**
+ * efi_file_size() - Get the size of a file using an EFI file handle
+ *
+ * @fh:                EFI file handle
+ * @size:      buffer to fill in the discovered size
+ *
+ * Return:     size of the file
+ */
+efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size)
+{
+       struct efi_file_info *info = NULL;
+       efi_uintn_t bs = 0;
+       efi_status_t ret;
+
+       *size = 0;
+       ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
+                                  info));
+       if (ret != EFI_BUFFER_TOO_SMALL) {
+               ret = EFI_DEVICE_ERROR;
+               goto out;
+       }
+
+       info = malloc(bs);
+       if (!info) {
+               ret = EFI_OUT_OF_RESOURCES;
+               goto out;
+       }
+       ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
+                                  info));
+       if (ret != EFI_SUCCESS)
+               goto out;
+
+       *size = info->file_size;
+
+out:
+       free(info);
+       return ret;
+}
+
 static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
                void *buffer)
 {
index 5e401bb..7a3cca2 100644 (file)
@@ -299,7 +299,7 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
        efi_status_t (*progress)(efi_uintn_t completion),
        u16 **abort_reason)
 {
-       EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image,
+       EFI_ENTRY("%p %d %p %zd %p %p %p\n", this, image_index, image,
                  image_size, vendor_code, progress, abort_reason);
 
        if (!image || image_index != 1)
@@ -414,7 +414,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
        efi_status_t status;
        efi_uintn_t capsule_payload_size;
 
-       EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image,
+       EFI_ENTRY("%p %d %p %zd %p %p %p\n", this, image_index, image,
                  image_size, vendor_code, progress, abort_reason);
 
        if (!image)
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
new file mode 100644 (file)
index 0000000..d03a736
--- /dev/null
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+#include <common.h>
+#include <env.h>
+#include <malloc.h>
+#include <dm.h>
+#include <fs.h>
+#include <efi_load_initrd.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+
+/**
+ * efi_create_current_boot_var() - Return Boot#### name were #### is replaced by
+ *                                the value of BootCurrent
+ *
+ * @var_name:          variable name
+ * @var_name_size:     size of var_name
+ *
+ * Return:     Status code
+ */
+static efi_status_t efi_create_current_boot_var(u16 var_name[],
+                                               size_t var_name_size)
+{
+       efi_uintn_t boot_current_size;
+       efi_status_t ret;
+       u16 boot_current;
+       u16 *pos;
+
+       boot_current_size = sizeof(boot_current);
+       ret = efi_get_variable_int(L"BootCurrent",
+                                  &efi_global_variable_guid, NULL,
+                                  &boot_current_size, &boot_current, NULL);
+       if (ret != EFI_SUCCESS)
+               goto out;
+
+       pos = efi_create_indexed_name(var_name, var_name_size, "Boot",
+                                     boot_current);
+       if (!pos) {
+               ret = EFI_OUT_OF_RESOURCES;
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+/**
+ * efi_get_dp_from_boot() - Retrieve and return a device path from an EFI
+ *                         Boot### variable.
+ *                         A boot option may contain an array of device paths.
+ *                         We use a VenMedia() with a specific GUID to identify
+ *                         the usage of the array members. This function is
+ *                         used to extract a specific device path
+ *
+ * @guid:      vendor GUID of the VenMedia() device path node identifying the
+ *             device path
+ *
+ * Return:     device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid)
+{
+       struct efi_device_path *file_path = NULL;
+       struct efi_device_path *tmp = NULL;
+       struct efi_load_option lo;
+       void *var_value = NULL;
+       efi_uintn_t size;
+       efi_status_t ret;
+       u16 var_name[16];
+
+       ret = efi_create_current_boot_var(var_name, sizeof(var_name));
+       if (ret != EFI_SUCCESS)
+               return NULL;
+
+       var_value = efi_get_var(var_name, &efi_global_variable_guid, &size);
+       if (!var_value)
+               return NULL;
+
+       ret = efi_deserialize_load_option(&lo, var_value, &size);
+       if (ret != EFI_SUCCESS)
+               goto out;
+
+       tmp = efi_dp_from_lo(&lo, &size, guid);
+       if (!tmp)
+               goto out;
+
+       /* efi_dp_dup will just return NULL if efi_dp_next is NULL */
+       file_path = efi_dp_dup(efi_dp_next(tmp));
+
+out:
+       efi_free_pool(tmp);
+       free(var_value);
+
+       return file_path;
+}
index b9ee883..e2a8063 100644 (file)
@@ -3,9 +3,11 @@
  * Copyright (c) 2020, Linaro Limited
  */
 
+#define LOG_CATEGORY LOGC_EFI
 #include <common.h>
 #include <efi_loader.h>
 #include <efi_load_initrd.h>
+#include <efi_variable.h>
 #include <fs.h>
 #include <malloc.h>
 #include <mapmem.h>
@@ -23,57 +25,56 @@ static const struct efi_load_file_protocol efi_lf2_protocol = {
  * Device path defined by Linux to identify the handle providing the
  * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
  */
-static const struct efi_initrd_dp dp = {
+static const struct efi_initrd_dp dp_lf2_handle = {
        .vendor = {
                {
                   DEVICE_PATH_TYPE_MEDIA_DEVICE,
                   DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
-                  sizeof(dp.vendor),
+                  sizeof(dp_lf2_handle.vendor),
                },
                EFI_INITRD_MEDIA_GUID,
        },
        .end = {
                DEVICE_PATH_TYPE_END,
                DEVICE_PATH_SUB_TYPE_END,
-               sizeof(dp.end),
+               sizeof(dp_lf2_handle.end),
        }
 };
 
+static efi_handle_t efi_initrd_handle;
+
 /**
- * get_file_size() - retrieve the size of initramfs, set efi status on error
+ * get_initrd_fp() - Get initrd device path from a FilePathList device path
  *
- * @dev:                       device to read from, e.g. "mmc"
- * @part:                      device partition, e.g. "0:1"
- * @file:                      name of file
- * @status:                    EFI exit code in case of failure
+ * @initrd_fp: the final initrd filepath
  *
- * Return:                     size of file
+ * Return:     status code. Caller must free initrd_fp
  */
-static loff_t get_file_size(const char *dev, const char *part, const char *file,
-                           efi_status_t *status)
+static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
 {
-       loff_t sz = 0;
-       int ret;
-
-       ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
-       if (ret) {
-               *status = EFI_NO_MEDIA;
-               goto out;
-       }
+       const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
+       struct efi_device_path *dp = NULL;
 
-       ret = fs_size(file, &sz);
-       if (ret) {
-               sz = 0;
-               *status = EFI_NOT_FOUND;
-               goto out;
-       }
+       /*
+        * if bootmgr is setup with and initrd, the device path will be
+        * in the FilePathList[] of our load options in Boot####.
+        * The first device path of the multi instance device path will
+        * start with a VenMedia and the initrds will follow.
+        *
+        * If the device path is not found return EFI_INVALID_PARAMETER.
+        * We can then use this specific return value and not install the
+        * protocol, while allowing the boot to continue
+        */
+       dp = efi_get_dp_from_boot(lf2_initrd_guid);
+       if (!dp)
+               return EFI_INVALID_PARAMETER;
 
-out:
-       return sz;
+       *initrd_fp = dp;
+       return EFI_SUCCESS;
 }
 
 /**
- * efi_load_file2initrd() - load initial RAM disk
+ * efi_load_file2_initrd() - load initial RAM disk
  *
  * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
  * in order to load an initial RAM disk requested by the Linux kernel stub.
@@ -93,102 +94,125 @@ efi_load_file2_initrd(struct efi_load_file_protocol *this,
                      struct efi_device_path *file_path, bool boot_policy,
                      efi_uintn_t *buffer_size, void *buffer)
 {
-       char *filespec;
-       efi_status_t status = EFI_NOT_FOUND;
-       loff_t file_sz = 0, read_sz = 0;
-       char *dev, *part, *file;
-       char *pos;
-       int ret;
+       struct efi_device_path *initrd_fp = NULL;
+       efi_status_t ret = EFI_NOT_FOUND;
+       struct efi_file_handle *f = NULL;
+       efi_uintn_t bs;
 
        EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
                  buffer_size, buffer);
 
-       filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
-       if (!filespec)
-               goto out;
-       pos = filespec;
-
        if (!this || this != &efi_lf2_protocol ||
            !buffer_size) {
-               status = EFI_INVALID_PARAMETER;
+               ret = EFI_INVALID_PARAMETER;
                goto out;
        }
 
-       if (file_path->type != dp.end.type ||
-           file_path->sub_type != dp.end.sub_type) {
-               status = EFI_INVALID_PARAMETER;
+       if (file_path->type != dp_lf2_handle.end.type ||
+           file_path->sub_type != dp_lf2_handle.end.sub_type) {
+               ret = EFI_INVALID_PARAMETER;
                goto out;
        }
 
        if (boot_policy) {
-               status = EFI_UNSUPPORTED;
+               ret = EFI_UNSUPPORTED;
                goto out;
        }
 
-       /*
-        * expect a string with three space separated parts:
-        *
-        * * a block device type, e.g. "mmc"
-        * * a device and partition identifier, e.g. "0:1"
-        * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
-        */
-       dev = strsep(&pos, " ");
-       if (!dev)
+       ret = get_initrd_fp(&initrd_fp);
+       if (ret != EFI_SUCCESS)
                goto out;
-       part = strsep(&pos, " ");
-       if (!part)
-               goto out;
-       file = strsep(&pos, " ");
-       if (!file)
+
+       /* Open file */
+       f = efi_file_from_path(initrd_fp);
+       if (!f) {
+               log_err("Can't find initrd specified in Boot####\n");
+               ret = EFI_NOT_FOUND;
                goto out;
+       }
 
-       file_sz = get_file_size(dev, part, file, &status);
-       if (!file_sz)
+       /* Get file size */
+       ret = efi_file_size(f, &bs);
+       if (ret != EFI_SUCCESS)
                goto out;
 
-       if (!buffer || *buffer_size < file_sz) {
-               status = EFI_BUFFER_TOO_SMALL;
-               *buffer_size = file_sz;
+       if (!buffer || *buffer_size < bs) {
+               ret = EFI_BUFFER_TOO_SMALL;
+               *buffer_size = bs;
        } else {
-               ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
-               if (ret) {
-                       status = EFI_NO_MEDIA;
-                       goto out;
-               }
-
-               ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
-                             &read_sz);
-               if (ret || read_sz != file_sz)
-                       goto out;
-               *buffer_size = read_sz;
-
-               status = EFI_SUCCESS;
+               ret = EFI_CALL(f->read(f, &bs, (void *)(uintptr_t)buffer));
+               *buffer_size = bs;
+       }
+
+out:
+       efi_free_pool(initrd_fp);
+       if (f)
+               EFI_CALL(f->close(f));
+       return EFI_EXIT(ret);
+}
+
+/**
+ * check_initrd() - Determine if the file defined as an initrd in Boot####
+ *                 load_options device path is present
+ *
+ * Return:     status code
+ */
+static efi_status_t check_initrd(void)
+{
+       struct efi_device_path *initrd_fp = NULL;
+       struct efi_file_handle *f;
+       efi_status_t ret;
+
+       ret = get_initrd_fp(&initrd_fp);
+       if (ret != EFI_SUCCESS)
+               goto out;
+
+       /*
+        * If the file is not found, but the file path is set, return an error
+        * and trigger the bootmgr fallback
+        */
+       f = efi_file_from_path(initrd_fp);
+       if (!f) {
+               log_err("Can't find initrd specified in Boot####\n");
+               ret = EFI_NOT_FOUND;
+               goto out;
        }
 
+       EFI_CALL(f->close(f));
+
 out:
-       free(filespec);
-       return EFI_EXIT(status);
+       efi_free_pool(initrd_fp);
+       return ret;
 }
 
 /**
  * efi_initrd_register() - create handle for loading initial RAM disk
  *
  * This function creates a new handle and installs a Linux specific vendor
- * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
+ * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
  * to identify the handle and then calls the LoadFile service of the
- * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
+ * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
  *
  * Return:     status code
  */
 efi_status_t efi_initrd_register(void)
 {
-       efi_handle_t efi_initrd_handle = NULL;
        efi_status_t ret;
 
+       /*
+        * Allow the user to continue if Boot#### file path is not set for
+        * an initrd
+        */
+       ret = check_initrd();
+       if (ret == EFI_INVALID_PARAMETER)
+               return EFI_SUCCESS;
+       if (ret != EFI_SUCCESS)
+               return ret;
+
        ret = EFI_CALL(efi_install_multiple_protocol_interfaces
                       (&efi_initrd_handle,
                        /* initramfs */
-                       &efi_guid_device_path, &dp,
+                       &efi_guid_device_path, &dp_lf2_handle,
                        /* LOAD_FILE2 */
                        &efi_guid_load_file2_protocol,
                        (void *)&efi_lf2_protocol,
@@ -196,3 +220,17 @@ efi_status_t efi_initrd_register(void)
 
        return ret;
 }
+
+/**
+ * efi_initrd_deregister() - delete the handle for loading initial RAM disk
+ *
+ * This will delete the handle containing the Linux specific vendor device
+ * path and EFI_LOAD_FILE2_PROTOCOL for loading an initrd
+ *
+ * Return:     status code
+ */
+void efi_initrd_deregister(void)
+{
+       efi_delete_handle(efi_initrd_handle);
+       efi_initrd_handle = NULL;
+}
index b1c5125..3c5cf9a 100644 (file)
@@ -227,6 +227,12 @@ efi_status_t efi_init_obj_list(void)
        if (ret != EFI_SUCCESS)
                goto out;
 
+       if (IS_ENABLED(CONFIG_EFI_ESRT)) {
+               ret = efi_esrt_register();
+               if (ret != EFI_SUCCESS)
+                       goto out;
+       }
+
        if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
                ret = efi_tcg2_register();
                if (ret != EFI_SUCCESS)
index 797d6eb..0904684 100644 (file)
@@ -13,6 +13,7 @@
 #include <efi_loader.h>
 #include <efi_tcg2.h>
 #include <log.h>
+#include <version.h>
 #include <tpm-v2.h>
 #include <u-boot/sha1.h>
 #include <u-boot/sha256.h>
@@ -958,6 +959,23 @@ out:
 }
 
 /**
+ * tcg2_uninit - remove the final event table and free efi memory on failures
+ */
+void tcg2_uninit(void)
+{
+       efi_status_t ret;
+
+       ret = efi_install_configuration_table(&efi_guid_final_events, NULL);
+       if (ret != EFI_SUCCESS)
+               log_err("Failed to delete final events config table\n");
+
+       efi_free_pool(event_log.buffer);
+       event_log.buffer = NULL;
+       efi_free_pool(event_log.final_buffer);
+       event_log.final_buffer = NULL;
+}
+
+/**
  * create_final_event() - Create the final event and install the config
  *                     defined by the TCG EFI spec
  */
@@ -983,10 +1001,6 @@ static efi_status_t create_final_event(void)
        event_log.final_pos = sizeof(*final_event);
        ret = efi_install_configuration_table(&efi_guid_final_events,
                                              final_event);
-       if (ret != EFI_SUCCESS)
-               goto out;
-
-       return EFI_SUCCESS;
 out:
        return ret;
 }
@@ -1041,6 +1055,40 @@ static efi_status_t efi_init_event_log(void)
        event_log.last_event_size = event_log.pos;
 
        ret = create_final_event();
+       if (ret != EFI_SUCCESS)
+               goto out;
+
+       return EFI_SUCCESS;
+out:
+       tcg2_uninit();
+       return ret;
+}
+
+/**
+ * efi_append_scrtm_version - Append an S-CRTM EV_S_CRTM_VERSION event on the
+ *                           eventlog and extend the PCRs
+ *
+ * @dev:       TPM device
+ *
+ * @Return:    status code
+ */
+static efi_status_t efi_append_scrtm_version(struct udevice *dev)
+{
+       struct tpml_digest_values digest_list;
+       u8 ver[] = U_BOOT_VERSION_STRING;
+       const int pcr_index = 0;
+       efi_status_t ret;
+
+       ret = tcg2_create_digest(ver, sizeof(ver), &digest_list);
+       if (ret != EFI_SUCCESS)
+               goto out;
+
+       ret = tcg2_pcr_extend(dev, pcr_index, &digest_list);
+       if (ret != EFI_SUCCESS)
+               goto out;
+
+       ret = tcg2_agile_log_append(pcr_index, EV_S_CRTM_VERSION, &digest_list,
+                                   sizeof(ver), ver);
 
 out:
        return ret;
@@ -1055,23 +1103,34 @@ out:
  */
 efi_status_t efi_tcg2_register(void)
 {
-       efi_status_t ret;
+       efi_status_t ret = EFI_SUCCESS;
        struct udevice *dev;
 
        ret = platform_get_tpm2_device(&dev);
        if (ret != EFI_SUCCESS) {
                log_warning("Unable to find TPMv2 device\n");
-               return EFI_SUCCESS;
+               ret = EFI_SUCCESS;
+               goto out;
        }
 
        ret = efi_init_event_log();
        if (ret != EFI_SUCCESS)
-               return ret;
+               goto fail;
+
+       ret = efi_append_scrtm_version(dev);
+       if (ret != EFI_SUCCESS)
+               goto out;
 
        ret = efi_add_protocol(efi_root, &efi_guid_tcg2_protocol,
                               (void *)&efi_tcg2_protocol);
-       if (ret != EFI_SUCCESS)
+       if (ret != EFI_SUCCESS) {
                log_err("Cannot install EFI_TCG2_PROTOCOL\n");
+               goto fail;
+       }
 
+out:
+       return ret;
+fail:
+       tcg2_uninit();
        return ret;
 }
index 1c74592..b11ed91 100644 (file)
@@ -9,6 +9,7 @@
 #include <common.h>
 #include <efi_loader.h>
 #include <efi_variable.h>
+#include <stdlib.h>
 
 enum efi_secure_mode {
        EFI_MODE_SETUP,
@@ -343,3 +344,35 @@ enum efi_auth_var_type efi_auth_var_get_type(u16 *name, const efi_guid_t *guid)
        }
        return EFI_AUTH_VAR_NONE;
 }
+
+/**
+ * efi_get_var() - read value of an EFI variable
+ *
+ * @name:      variable name
+ * @start:     vendor GUID
+ * @size:      size of allocated buffer
+ *
+ * Return:     buffer with variable data or NULL
+ */
+void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size)
+{
+       efi_status_t ret;
+       void *buf = NULL;
+
+       *size = 0;
+       ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+       if (ret == EFI_BUFFER_TOO_SMALL) {
+               buf = malloc(*size);
+               if (!buf)
+                       return NULL;
+               ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+       }
+
+       if (ret != EFI_SUCCESS) {
+               free(buf);
+               *size = 0;
+               return NULL;
+       }
+
+       return buf;
+}
index b02fd56..aa71d09 100644 (file)
@@ -61,7 +61,6 @@ obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o
 obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o
 obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_selftest_rng.o
 obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o
-obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_selftest_load_initrd.o
 obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_selftest_tcg2.o
 
 ifeq ($(CONFIG_GENERATE_ACPI_TABLE),)
@@ -72,6 +71,8 @@ ifeq ($(CONFIG_BLK)$(CONFIG_DOS_PARTITION),yy)
 obj-y += efi_selftest_block_device.o
 endif
 
+obj-$(CONFIG_EFI_ESRT) += efi_selftest_esrt.o
+
 targets += \
 efi_miniapp_file_image_exception.h \
 efi_miniapp_file_image_exit.h \
index b8eed04..39ee2ed 100644 (file)
@@ -160,7 +160,7 @@ static bool need_reset(const u16 *testname)
                if (testname && efi_st_strcmp_16_8(testname, test->name))
                        continue;
                if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT ||
-                   test->phase == EFI_SETUP_AFTER_BOOTTIME_EXIT)
+                   test->phase == EFI_SETTING_VIRTUAL_ADDRESS_MAP)
                        return true;
        }
        return false;
@@ -327,15 +327,16 @@ efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
        /* Execute mixed tests */
        efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
                        EFI_ST_SETUP, &failures);
+       efi_st_do_tests(testname, EFI_SETTING_VIRTUAL_ADDRESS_MAP,
+                       EFI_ST_SETUP, &failures);
 
        efi_st_exit_boot_services();
 
        efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
                        EFI_ST_EXECUTE | EFI_ST_TEARDOWN, &failures);
-
-       /* Execute runtime tests */
-       efi_st_do_tests(testname, EFI_SETUP_AFTER_BOOTTIME_EXIT,
-                       EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
+       /* Execute test setting the virtual address map */
+       efi_st_do_tests(testname, EFI_SETTING_VIRTUAL_ADDRESS_MAP,
+                       EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
                        &failures);
 
        /* Give feedback */
diff --git a/lib/efi_selftest/efi_selftest_esrt.c b/lib/efi_selftest/efi_selftest_esrt.c
new file mode 100644 (file)
index 0000000..99251f2
--- /dev/null
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  Test ESRT tables support
+ *
+ *  Copyright (C) 2021 Arm Ltd.
+ */
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_selftest.h>
+
+// This value must not exceed 255.
+// An FMP cannot contain more than 255 FW images.
+#define TEST_ESRT_NUM_ENTRIES 255
+
+static
+struct efi_firmware_image_descriptor static_img_info[TEST_ESRT_NUM_ENTRIES];
+
+static const struct efi_system_table *local_systable;
+
+static efi_handle_t fmp_handle;
+
+static const efi_guid_t efi_fmp_guid =
+               EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
+
+static void efi_test_esrt_init_info(void)
+{
+       for (int idx = 0; idx < TEST_ESRT_NUM_ENTRIES; idx++) {
+               static_img_info[idx].image_index = idx;
+
+               // Note: the 16 byte value present in
+               // static_img_info[idx].image_type_id is not strictly a GUID.
+               // The value is used for the sake of code testing.
+               static_img_info[idx].image_type_id.b[0] = idx;
+
+               static_img_info[idx].image_id = 0;
+               static_img_info[idx].image_id_name = NULL;
+               static_img_info[idx].version = 0;
+               static_img_info[idx].version_name = NULL;
+               static_img_info[idx].size = 0;
+               static_img_info[idx].lowest_supported_image_version = 1;
+               static_img_info[idx].last_attempt_version = 2;
+               static_img_info[idx].last_attempt_status = 3;
+               static_img_info[idx].hardware_instance = 1;
+       }
+}
+
+static efi_status_t
+EFIAPI efi_test_fmp_get_image_info(struct efi_firmware_management_protocol *this,
+                                  efi_uintn_t *image_info_size,
+                                  struct efi_firmware_image_descriptor *image_info,
+                                  u32 *descriptor_version,
+                                  u8 *descriptor_count,
+                                  efi_uintn_t *descriptor_size,
+                                  u32 *package_version,
+                                  u16 **package_version_name)
+{
+       efi_status_t ret = EFI_SUCCESS;
+
+       if (!image_info_size)
+               return EFI_INVALID_PARAMETER;
+
+       if (descriptor_version)
+               *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
+       if (descriptor_count)
+               *descriptor_count = TEST_ESRT_NUM_ENTRIES;
+       if (descriptor_size)
+               *descriptor_size = sizeof(*image_info);
+       if (package_version)
+               *package_version = 0xffffffff;
+       if (package_version_name)
+               *package_version_name = NULL;
+
+       if (*image_info_size < sizeof(*image_info)) {
+               *image_info_size = *descriptor_size * *descriptor_count;
+               return EFI_BUFFER_TOO_SMALL;
+       }
+
+       for (int idx = 0; idx < TEST_ESRT_NUM_ENTRIES; idx++)
+               image_info[idx] = static_img_info[idx];
+
+       return ret;
+}
+
+static struct efi_firmware_management_protocol efi_test_fmp = {
+       .get_image_info = efi_test_fmp_get_image_info,
+       .get_image = NULL,
+       .set_image = NULL,
+       .check_image = NULL,
+       .get_package_info = NULL,
+       .set_package_info = NULL,
+};
+
+static void *lib_test_get_esrt(void)
+{
+       for (int idx = 0; idx < local_systable->nr_tables; idx++)
+               if (!guidcmp(&efi_esrt_guid, &local_systable->tables[idx].guid))
+                       return local_systable->tables[idx].table;
+
+       return NULL;
+}
+
+/**
+ * lib_test_check_uuid_entry: Find an ESRT entry for which the fw_calss field matches
+ * the image_type_id in the @img_info.
+ * Ensure that all of the field in the ESRT entry have the same value as the corresponding
+ * fields in the @img_info.
+ *
+ * @esrt: pointer to the ESRT
+ * @img_info: an image_info_descriptor output by the FMP get_image_info
+ *
+ * @return: true if matching ESRT entry is found and if all the ESRT entry fields match the
+ * corresponding @img_info fields.
+ */
+static bool lib_test_check_uuid_entry(struct efi_system_resource_table *esrt,
+                                     struct efi_firmware_image_descriptor
+                                     *img_info)
+{
+       const u32 filled_entries = esrt->fw_resource_count;
+       struct efi_system_resource_entry *entry = esrt->entries;
+
+       for (u32 idx = 0; idx < filled_entries; idx++) {
+               if (!guidcmp(&entry[idx].fw_class, &img_info->image_type_id)) {
+                       if (entry[idx].fw_version != img_info->version) {
+                               efi_st_error("ESRT field mismatch for entry with fw_class=%pUl\n",
+                                            &img_info->image_type_id);
+                               return false;
+                       }
+
+                       if (entry[idx].lowest_supported_fw_version !=
+                               img_info->lowest_supported_image_version) {
+                               efi_st_error("ESRT field mismatch for entry with fw_class=%pUl\n",
+                                            &img_info->image_type_id);
+                               return false;
+                       }
+
+                       if (entry[idx].last_attempt_version !=
+                               img_info->last_attempt_version) {
+                               efi_st_error("ESRT field mismatch for entry with fw_class=%pUl\n",
+                                            &img_info->image_type_id);
+                               return false;
+                       }
+
+                       if (entry[idx].last_attempt_status !=
+                               img_info->last_attempt_status) {
+                               efi_st_error("ESRT field mismatch for entry with fw_class=%pUl\n",
+                                            &img_info->image_type_id);
+                               return false;
+                       }
+
+                       /*
+                        * The entry with fw_class = img_uuid matches with the
+                        * remainder fmp input.
+                        */
+                       return true;
+               }
+       }
+
+       /* There exists no entry with fw_class equal to img_uuid in the ESRT. */
+       efi_st_error("ESRT no entry with fw_class= %pUl\n", &img_info->image_type_id);
+
+       return false;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Initialize the test FMP datastructure.
+ *
+ * @handle:    handle of the loaded image
+ * @systable:  system table
+ * @return:    EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+                const struct efi_system_table *systable)
+{
+       local_systable = systable;
+
+       efi_test_esrt_init_info();
+
+       return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Uninstall the test FMP.
+ *
+ * @return:    EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+       efi_status_t ret = EFI_SUCCESS;
+       struct efi_boot_services *bt;
+
+       bt = local_systable->boottime;
+
+       if (!bt) {
+               efi_st_error("Cannot find boottime services structure\n");
+               return EFI_ST_FAILURE;
+       }
+
+       ret = bt->uninstall_multiple_protocol_interfaces
+               (fmp_handle, &efi_fmp_guid,
+                &efi_test_fmp, NULL);
+
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Failed to uninstall FMP\n");
+               return EFI_ST_FAILURE;
+       }
+
+       return EFI_ST_SUCCESS;
+}
+
+/*
+ * Perform the test
+ *
+ * The test consists of the following steps:
+ *
+ * 1) Obtain the ESRT
+ * 2) Record the number of ESRT entries prior to test start
+ * 3) Install the test FMP
+ * 4) Re-obtain the ESRT (the ESRT pointer may have changed with the FMP install)
+ * 5) verify that the ESRT entries have increased by the number of entries in the
+ *     test FMP.
+ * 6) Traverse all the elements used as the test FMP input and verify that each
+ *     has a corresponding ESRT entry and that the fields are correctly set.
+ *
+ * The failure of any of the above steps results in a test failure.
+ *
+ */
+static int execute(void)
+{
+       struct efi_system_resource_table *esrt;
+       efi_status_t ret = EFI_SUCCESS;
+       u32 base_entry_count;
+       u32 entry_delta;
+       struct efi_boot_services *bt;
+
+       bt = local_systable->boottime;
+
+       if (!bt) {
+               efi_st_error("Cannot find boottime services structure\n");
+               return EFI_ST_FAILURE;
+       }
+
+       esrt = lib_test_get_esrt();
+       if (!esrt) {
+               efi_st_error("ESRT table not present\n");
+               return EFI_ST_FAILURE;
+       }
+       base_entry_count = esrt->fw_resource_count;
+
+       ret = bt->install_multiple_protocol_interfaces(&fmp_handle,
+                                                      &efi_fmp_guid,
+                                                      &efi_test_fmp,
+                                                      NULL);
+
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("Failed to install FMP\n");
+               return EFI_ST_FAILURE;
+       }
+
+       esrt = lib_test_get_esrt();
+       if (!esrt) {
+               efi_st_error("ESRT table not present\n");
+               return EFI_ST_FAILURE;
+       }
+
+       entry_delta = esrt->fw_resource_count - base_entry_count;
+       if (entry_delta != TEST_ESRT_NUM_ENTRIES) {
+               efi_st_error("ESRT mismatch in new entry count (%d), expected (%d).\n",
+                            entry_delta, TEST_ESRT_NUM_ENTRIES);
+               return EFI_ST_FAILURE;
+       }
+
+       for (u32 idx = 0; idx < TEST_ESRT_NUM_ENTRIES; idx++)
+               if (!lib_test_check_uuid_entry(esrt, &static_img_info[idx])) {
+                       efi_st_error("ESRT entry mismatch\n");
+                       return EFI_ST_FAILURE;
+               }
+
+       return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(esrt) = {
+       .name = "esrt",
+       .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+       .setup = setup,
+       .execute = execute,
+       .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_load_initrd.c b/lib/efi_selftest/efi_selftest_load_initrd.c
deleted file mode 100644 (file)
index f591dcd..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * efi_selftest_load_initrd
- *
- * Copyright (c) 2020 Ilias Apalodimas <ilias.apalodimas@linaro.org>
- *
- * This test checks the FileLoad2 protocol.
- * A known file is read from the file system and verified.
- *
- * An example usage - given a file image with a file system in partition 1
- * holding file initrd - is:
- *
- * * Configure the sandbox with
- *
- *   CONFIG_EFI_SELFTEST=y
- *   CONFIG_EFI_LOAD_FILE2_INITRD=y
- *   CONFIG_EFI_INITRD_FILESPEC="host 0:1 initrd"
- *
- * * Run ./u-boot and execute
- *
- *   host bind 0 image
- *   setenv efi_selftest load initrd
- *   bootefi selftest
- *
- * This would provide a test output like:
- *
- *   Testing EFI API implementation
- *
- *   Selected test: 'load initrd'
- *
- *   Setting up 'load initrd'
- *   Setting up 'load initrd' succeeded
- *
- *   Executing 'load initrd'
- *   Loaded 12378613 bytes
- *   CRC32 2997478465
- *
- * Now the size and CRC32 can be compared to the provided file.
- */
-
-#include <efi_selftest.h>
-#include <efi_loader.h>
-#include <efi_load_initrd.h>
-
-static struct efi_boot_services *boottime;
-
-static struct efi_initrd_dp dp = {
-       .vendor = {
-               {
-                  DEVICE_PATH_TYPE_MEDIA_DEVICE,
-                  DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
-                  sizeof(dp.vendor),
-               },
-               EFI_INITRD_MEDIA_GUID,
-       },
-       .end = {
-               DEVICE_PATH_TYPE_END,
-               DEVICE_PATH_SUB_TYPE_END,
-               sizeof(dp.end),
-       }
-};
-
-static struct efi_initrd_dp dp_invalid = {
-       .vendor = {
-               {
-                  DEVICE_PATH_TYPE_MEDIA_DEVICE,
-                  DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
-                  sizeof(dp.vendor),
-               },
-               EFI_INITRD_MEDIA_GUID,
-       },
-       .end = {
-               0x8f, /* invalid */
-               0xfe, /* invalid */
-               sizeof(dp.end),
-       }
-};
-
-static int setup(const efi_handle_t handle,
-                const struct efi_system_table *systable)
-{
-       boottime = systable->boottime;
-
-       return EFI_ST_SUCCESS;
-}
-
-static int execute(void)
-{
-       struct efi_load_file_protocol *lf2;
-       struct efi_device_path *dp2, *dp2_invalid;
-       efi_status_t status;
-       efi_handle_t handle;
-       char buffer[64];
-       efi_uintn_t buffer_size;
-       void *buf;
-       u32 crc32;
-
-       memset(buffer, 0, sizeof(buffer));
-
-       dp2 = (struct efi_device_path *)&dp;
-       status = boottime->locate_device_path(&efi_guid_load_file2_protocol,
-                                             &dp2, &handle);
-       if (status != EFI_SUCCESS) {
-               efi_st_error("Unable to locate device path\n");
-               return EFI_ST_FAILURE;
-       }
-
-       status = boottime->handle_protocol(handle,
-                                          &efi_guid_load_file2_protocol,
-                                          (void **)&lf2);
-       if (status != EFI_SUCCESS) {
-               efi_st_error("Unable to locate protocol\n");
-               return EFI_ST_FAILURE;
-       }
-
-       /* Case 1:
-        * buffer_size can't be NULL
-        * protocol can't be NULL
-        */
-       status = lf2->load_file(lf2, dp2, false, NULL, &buffer);
-       if (status != EFI_INVALID_PARAMETER) {
-               efi_st_error("Buffer size can't be NULL\n");
-               return EFI_ST_FAILURE;
-       }
-       buffer_size = sizeof(buffer);
-       status = lf2->load_file(NULL, dp2, false, &buffer_size, &buffer);
-       if (status != EFI_INVALID_PARAMETER) {
-               efi_st_error("Protocol can't be NULL\n");
-               return EFI_ST_FAILURE;
-       }
-
-       /*
-        * Case 2: Match end node type/sub-type on device path
-        */
-       dp2_invalid = (struct efi_device_path *)&dp_invalid;
-       buffer_size = sizeof(buffer);
-       status = lf2->load_file(lf2, dp2_invalid, false, &buffer_size, &buffer);
-       if (status != EFI_INVALID_PARAMETER) {
-               efi_st_error("Invalid device path type must return EFI_INVALID_PARAMETER\n");
-               return EFI_ST_FAILURE;
-       }
-
-       status = lf2->load_file(lf2, dp2_invalid, false, &buffer_size, &buffer);
-       if (status != EFI_INVALID_PARAMETER) {
-               efi_st_error("Invalid device path sub-type must return EFI_INVALID_PARAMETER\n");
-               return EFI_ST_FAILURE;
-       }
-
-       /*
-        * Case 3:
-        * BootPolicy 'true' must return EFI_UNSUPPORTED
-        */
-       buffer_size = sizeof(buffer);
-       status = lf2->load_file(lf2, dp2, true, &buffer_size, &buffer);
-       if (status != EFI_UNSUPPORTED) {
-               efi_st_error("BootPolicy true must return EFI_UNSUPPORTED\n");
-               return EFI_ST_FAILURE;
-       }
-
-       /*
-        * Case: Pass buffer size as zero, firmware must return
-        * EFI_BUFFER_TOO_SMALL and an appropriate size
-        */
-       buffer_size = 0;
-       status = lf2->load_file(lf2, dp2, false, &buffer_size, NULL);
-       if (status != EFI_BUFFER_TOO_SMALL || !buffer_size) {
-               efi_st_printf("buffer_size: %u\n", (unsigned int)buffer_size);
-               efi_st_printf("status: %x\n", (unsigned int)status);
-               efi_st_error("Buffer size not updated\n");
-               return EFI_ST_FAILURE;
-       }
-
-       /*
-        * Case: Pass buffer size as smaller than the file_size,
-        * firmware must return * EFI_BUFFER_TOO_SMALL and an appropriate size
-        */
-       buffer_size = 1;
-       status = lf2->load_file(lf2, dp2, false, &buffer_size, &buffer);
-       if (status != EFI_BUFFER_TOO_SMALL || buffer_size <= 1) {
-               efi_st_error("Buffer size not updated\n");
-               return EFI_ST_FAILURE;
-       }
-
-       status = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
-                                        &buf);
-       if (status != EFI_SUCCESS) {
-               efi_st_error("Cannot allocate buffer\n");
-               return EFI_ST_FAILURE;
-       }
-
-       /* Case: Pass correct buffer, load the file and verify checksum*/
-       status = lf2->load_file(lf2, dp2, false, &buffer_size, buf);
-       if (status != EFI_SUCCESS) {
-               efi_st_error("Loading initrd failed\n");
-               return EFI_ST_FAILURE;
-       }
-
-       efi_st_printf("Loaded %u bytes\n", (unsigned int)buffer_size);
-       status = boottime->calculate_crc32(buf, buffer_size, &crc32);
-       if (status != EFI_SUCCESS) {
-               efi_st_error("Could not determine CRC32\n");
-               return EFI_ST_FAILURE;
-       }
-       efi_st_printf("CRC32 %.8x\n", (unsigned int)crc32);
-
-       status = boottime->free_pool(buf);
-       if (status != EFI_SUCCESS) {
-               efi_st_error("Cannot free buffer\n");
-               return EFI_ST_FAILURE;
-       }
-
-       return EFI_ST_SUCCESS;
-}
-
-EFI_UNIT_TEST(load_initrd) = {
-       .name = "load initrd",
-       .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
-       .setup = setup,
-       .execute = execute,
-       .on_request = true,
-};
index b097a81..8e2e8ba 100644 (file)
@@ -201,7 +201,7 @@ static int execute(void)
 
 EFI_UNIT_TEST(virtaddrmap) = {
        .name = "virtual address map",
-       .phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+       .phase = EFI_SETTING_VIRTUAL_ADDRESS_MAP,
        .setup = setup,
        .execute = execute,
 };
index c23a05c..325951b 100644 (file)
@@ -272,7 +272,7 @@ static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size)
                error(L"Out of memory\r\n");
                return ret;
        }
-       *initrd = (void *)buffer;
+       *initrd = (void *)(uintptr_t)buffer;
        ret = load_file2_prot->load_file(load_file2_prot, dp, false,
                                         initrd_size, *initrd);
        if (ret != EFI_SUCCESS) {
index f006fa9..160a64a 100644 (file)
@@ -39,7 +39,7 @@ class TestEfiCapsuleFirmwareFit(object):
         with u_boot_console.log.section('Test Case 1-a, before reboot'):
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
                 'efidebug boot order 1',
                 'env set -e OsIndications',
                 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
@@ -114,7 +114,7 @@ class TestEfiCapsuleFirmwareFit(object):
         with u_boot_console.log.section('Test Case 2-a, before reboot'):
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
                 'efidebug boot order 1',
                 'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
                 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
@@ -188,7 +188,7 @@ class TestEfiCapsuleFirmwareFit(object):
         with u_boot_console.log.section('Test Case 3-a, before reboot'):
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-                'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi ""',
                 'efidebug boot order 1',
                 'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
                 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
@@ -229,6 +229,14 @@ class TestEfiCapsuleFirmwareFit(object):
                 output = u_boot_console.run_command(
                     'env print -e -all Capsule0000')
 
+            output = u_boot_console.run_command_list(['efidebug capsule esrt'])
+
+            # ensure that EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID is in the ESRT.
+            assert 'AE13FF2D-9AD4-4E25-9AC8-6D80B3B22147' in ''.join(output)
+
+            # ensure that  EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID is in the ESRT.
+            assert 'E2BB9C06-70E9-4B14-97A3-5A7913176E3F' in ''.join(output)
+
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
                 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
index 863685e..75f5ea7 100644 (file)
@@ -28,7 +28,7 @@ class TestEfiSignedImage(object):
             # Test Case 1a, run signed image if no PK
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -36,7 +36,7 @@ class TestEfiSignedImage(object):
         with u_boot_console.log.section('Test Case 1b'):
             # Test Case 1b, run unsigned image if no PK
             output = u_boot_console.run_command_list([
-                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
                 'efidebug boot next 2',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -58,13 +58,13 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert('\'HELLO1\' failed' in ''.join(output))
             assert('efi_start_image() returned: 26' in ''.join(output))
             output = u_boot_console.run_command_list([
-                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi ""',
                 'efidebug boot next 2',
                 'efidebug test bootmgr'])
             assert '\'HELLO2\' failed' in ''.join(output)
@@ -104,7 +104,7 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)
@@ -142,7 +142,7 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)
@@ -169,7 +169,7 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -227,7 +227,7 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
             assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
index 70d6be0..0849572 100644 (file)
@@ -39,7 +39,7 @@ class TestEfiSignedImageIntca(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
+                'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert '\'HELLO_a\' failed' in ''.join(output)
@@ -48,7 +48,7 @@ class TestEfiSignedImageIntca(object):
         with u_boot_console.log.section('Test Case 1b'):
             # Test Case 1b, signed and authenticated by root CA
             output = u_boot_console.run_command_list([
-                'efidebug boot add 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
+                'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab ""',
                 'efidebug boot next 2',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -70,7 +70,7 @@ class TestEfiSignedImageIntca(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
+                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert '\'HELLO_abc\' failed' in ''.join(output)
@@ -116,7 +116,7 @@ class TestEfiSignedImageIntca(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
+                'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
             assert 'Hello, world!' in ''.join(output)
index 56f56e1..8e026f7 100644 (file)
@@ -35,7 +35,7 @@ class TestEfiUnsignedImage(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)
@@ -64,7 +64,7 @@ class TestEfiUnsignedImage(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert 'Hello, world!' in ''.join(output)
@@ -88,7 +88,7 @@ class TestEfiUnsignedImage(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)
@@ -106,7 +106,7 @@ class TestEfiUnsignedImage(object):
             assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
-                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
             assert '\'HELLO\' failed' in ''.join(output)