diff options
author | Tom Rini <trini@konsulko.com> | 2021-03-25 22:45:43 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2021-03-25 22:45:43 -0400 |
commit | 4be994b0e1f006c271e0791a60b6363137542090 (patch) | |
tree | 4a880a9274c31963b1a330520c5dd99916164db7 | |
parent | 874dcedee92a828b03c8d3146dccfaa8b709618b (diff) | |
parent | ac30aad21076e9eeb460af2865b863f3ae3d2eaa (diff) | |
download | u-boot-WIP/25Mar2021-next.zip u-boot-WIP/25Mar2021-next.tar.gz u-boot-WIP/25Mar2021-next.tar.bz2 |
Merge tag 'efi-next' of https://source.denx.de/u-boot/custodians/u-boot-efi into nextWIP/25Mar2021-next
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, 1781 insertions, 453 deletions
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 271b385..cba81ff 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -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; } diff --git a/cmd/efidebug.c b/cmd/efidebug.c index e4030f5..6e36575 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -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 ); @@ -11,12 +11,14 @@ #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)); } diff --git a/doc/board/emulation/qemu_capsule_update.rst b/doc/board/emulation/qemu_capsule_update.rst index 9fec75f..33ce4bc 100644 --- a/doc/board/emulation/qemu_capsule_update.rst +++ b/doc/board/emulation/qemu_capsule_update.rst @@ -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 diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst index 5a67737..b3494c2 100644 --- a/doc/uefi/uefi.rst +++ b/doc/uefi/uefi.rst @@ -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, diff --git a/drivers/dfu/dfu_sf.c b/drivers/dfu/dfu_sf.c index 76b629a..8f8c425 100644 --- a/drivers/dfu/dfu_sf.c +++ b/drivers/dfu/dfu_sf.c @@ -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) diff --git a/include/efi_api.h b/include/efi_api.h index 48e48a6..18a1adf 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -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 diff --git a/include/efi_loader.h b/include/efi_loader.h index 68daa1a..de1a496 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -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 */ diff --git a/include/efi_selftest.h b/include/efi_selftest.h index 1515fda..07b6199 100644 --- a/include/efi_selftest.h +++ b/include/efi_selftest.h @@ -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; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index e729f72..e44f004 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -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 diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 10b42e8..8bd343e 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -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 diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 25f5ceb..46c8011 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -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; } /** diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 41b8949..4777b35 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -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; } diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index b57f030..9df9c35 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -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) { diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index c400355..6040f3a 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -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) { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index c9315dd..4b20859 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -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; +} diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index edc9fdc..43554cd 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -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; diff --git a/lib/efi_loader/efi_dt_fixup.c b/lib/efi_loader/efi_dt_fixup.c index a4529ee..b6fe5d2 100644 --- a/lib/efi_loader/efi_dt_fixup.c +++ b/lib/efi_loader/efi_dt_fixup.c @@ -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 index 0000000..947bdb5 --- /dev/null +++ b/lib/efi_loader/efi_esrt.c @@ -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, ®istration)); + if (ret != EFI_SUCCESS) { + EFI_PRINT("ESRT failed to register FMP callback\n"); + return ret; + } + + EFI_PRINT("ESRT table created\n"); + + return ret; +} diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 8ece8e7..204105e 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -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) { diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c index 5e401bb..7a3cca2 100644 --- a/lib/efi_loader/efi_firmware.c +++ b/lib/efi_loader/efi_firmware.c @@ -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 index 0000000..d03a736 --- /dev/null +++ b/lib/efi_loader/efi_helper.c @@ -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; +} diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c index b9ee883..e2a8063 100644 --- a/lib/efi_loader/efi_load_initrd.c +++ b/lib/efi_loader/efi_load_initrd.c @@ -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; +} diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index b1c5125..3c5cf9a 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -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) diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c index 797d6eb..0904684 100644 --- a/lib/efi_loader/efi_tcg2.c +++ b/lib/efi_loader/efi_tcg2.c @@ -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; } diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c index 1c74592..b11ed91 100644 --- a/lib/efi_loader/efi_var_common.c +++ b/lib/efi_loader/efi_var_common.c @@ -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; +} diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index b02fd56..aa71d09 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -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 \ diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c index b8eed04..39ee2ed 100644 --- a/lib/efi_selftest/efi_selftest.c +++ b/lib/efi_selftest/efi_selftest.c @@ -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 index 0000000..99251f2 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_esrt.c @@ -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 index f591dcd..0000000 --- a/lib/efi_selftest/efi_selftest_load_initrd.c +++ /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, -}; diff --git a/lib/efi_selftest/efi_selftest_set_virtual_address_map.c b/lib/efi_selftest/efi_selftest_set_virtual_address_map.c index b097a81..8e2e8ba 100644 --- a/lib/efi_selftest/efi_selftest_set_virtual_address_map.c +++ b/lib/efi_selftest/efi_selftest_set_virtual_address_map.c @@ -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, }; diff --git a/lib/efi_selftest/initrddump.c b/lib/efi_selftest/initrddump.c index c23a05c..325951b 100644 --- a/lib/efi_selftest/initrddump.c +++ b/lib/efi_selftest/initrddump.c @@ -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) { diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py index f006fa9..160a64a 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py @@ -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]) diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py index 863685e..75f5ea7 100644 --- a/test/py/tests/test_efi_secboot/test_signed.py +++ b/test/py/tests/test_efi_secboot/test_signed.py @@ -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) diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py index 70d6be0..0849572 100644 --- a/test/py/tests/test_efi_secboot/test_signed_intca.py +++ b/test/py/tests/test_efi_secboot/test_signed_intca.py @@ -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) diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py index 56f56e1..8e026f7 100644 --- a/test/py/tests/test_efi_secboot/test_unsigned.py +++ b/test/py/tests/test_efi_secboot/test_unsigned.py @@ -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) |