diff options
Diffstat (limited to 'lib')
33 files changed, 1648 insertions, 489 deletions
diff --git a/lib/Kconfig b/lib/Kconfig index c8b3ec1ec9..07e61de5b6 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -508,6 +508,7 @@ config SHA256 config SHA512 bool "Enable SHA512 support" + default y if TI_SECURE_DEVICE && FIT_SIGNATURE help This option enables support of hashing using SHA512 algorithm. The hash is calculated in software. @@ -533,6 +534,17 @@ config SHA_HW_ACCEL if SPL +config SPL_CRC32 + bool "Enable CRC32 support in SPL" + default y if SPL_LEGACY_IMAGE_SUPPORT || SPL_EFI_PARTITION + default y if SPL_ENV_SUPPORT || TPL_BLOBLIST + help + This option enables support of hashing using CRC32 algorithm. + The CRC32 algorithm produces 32-bit checksum value. For FIT + images, this is the least secure type of checksum, suitable for + detected accidental image corruption. For secure applications you + should consider SHA256 or SHA384. + config SPL_SHA1 bool "Enable SHA1 support in SPL" default y if SHA1 diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 7c4189e243..a8d4b47000 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -11,8 +11,7 @@ #include <log.h> #include <mapmem.h> #include <tables_csum.h> -#include <timestamp.h> -#include <version.h> +#include <version_string.h> #include <acpi/acpi_table.h> #include <asm/global_data.h> #include <dm/acpi.h> @@ -25,12 +24,12 @@ * to have valid date. So for U-Boot version 2021.04 OEM_REVISION is set to * value 0x20210401. */ -#define OEM_REVISION ((((U_BOOT_VERSION_NUM / 1000) % 10) << 28) | \ - (((U_BOOT_VERSION_NUM / 100) % 10) << 24) | \ - (((U_BOOT_VERSION_NUM / 10) % 10) << 20) | \ - ((U_BOOT_VERSION_NUM % 10) << 16) | \ - (((U_BOOT_VERSION_NUM_PATCH / 10) % 10) << 12) | \ - ((U_BOOT_VERSION_NUM_PATCH % 10) << 8) | \ +#define OEM_REVISION ((((version_num / 1000) % 10) << 28) | \ + (((version_num / 100) % 10) << 24) | \ + (((version_num / 10) % 10) << 20) | \ + ((version_num % 10) << 16) | \ + (((version_num_patch / 10) % 10) << 12) | \ + ((version_num_patch % 10) << 8) | \ 0x01) int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags) diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c index d5939af2c5..5fa9be10b4 100644 --- a/lib/ecdsa/ecdsa-libcrypto.c +++ b/lib/ecdsa/ecdsa-libcrypto.c @@ -111,16 +111,30 @@ static size_t ecdsa_key_size_bytes(const EC_KEY *key) return EC_GROUP_order_bits(group) / 8; } +static int default_password(char *buf, int size, int rwflag, void *u) +{ + strncpy(buf, (char *)u, size); + buf[size - 1] = '\0'; + return strlen(buf); +} + static int read_key(struct signer *ctx, const char *key_name) { FILE *f = fopen(key_name, "r"); + const char *key_pass; if (!f) { fprintf(stderr, "Can not get key file '%s'\n", key_name); return -ENOENT; } - ctx->evp_key = PEM_read_PrivateKey(f, NULL, NULL, NULL); + key_pass = getenv("MKIMAGE_SIGN_PASSWORD"); + if (key_pass) { + ctx->evp_key = PEM_read_PrivateKey(f, NULL, default_password, (void *)key_pass); + + } else { + ctx->evp_key = PEM_read_PrivateKey(f, NULL, NULL, NULL); + } fclose(f); if (!ctx->evp_key) { fprintf(stderr, "Can not read key from '%s'\n", key_name); diff --git a/lib/efi_driver/efi_block_device.c b/lib/efi_driver/efi_block_device.c index add00eeebb..e3abd90275 100644 --- a/lib/efi_driver/efi_block_device.c +++ b/lib/efi_driver/efi_block_device.c @@ -124,10 +124,8 @@ efi_bl_create_block_device(efi_handle_t handle, void *interface) struct efi_block_io *io = interface; struct efi_blk_plat *plat; - devnum = blk_find_max_devnum(UCLASS_EFI_LOADER); - if (devnum == -ENODEV) - devnum = 0; - else if (devnum < 0) + devnum = blk_next_free_devnum(UCLASS_EFI_LOADER); + if (devnum < 0) return EFI_OUT_OF_RESOURCES; name = calloc(1, 18); /* strlen("efiblk#2147483648") + 1 */ diff --git a/lib/efi_driver/efi_uclass.c b/lib/efi_driver/efi_uclass.c index 45f9351988..66a45e156d 100644 --- a/lib/efi_driver/efi_uclass.c +++ b/lib/efi_driver/efi_uclass.c @@ -285,10 +285,8 @@ static efi_status_t efi_add_driver(struct driver *drv) bp->ops = ops; ret = efi_create_handle(&bp->bp.driver_binding_handle); - if (ret != EFI_SUCCESS) { - free(bp); - goto out; - } + if (ret != EFI_SUCCESS) + goto err; bp->bp.image_handle = bp->bp.driver_binding_handle; ret = efi_add_protocol(bp->bp.driver_binding_handle, &efi_guid_driver_binding_protocol, bp); @@ -299,11 +297,11 @@ static efi_status_t efi_add_driver(struct driver *drv) if (ret != EFI_SUCCESS) goto err; } -out: - return ret; + return ret; err: - efi_delete_handle(bp->bp.driver_binding_handle); + if (bp->bp.driver_binding_handle) + efi_delete_handle(bp->bp.driver_binding_handle); free(bp); return ret; } diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..9989e3f384 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,50 @@ config EFI_VARIABLE_FILE_STORE stored as file /ubootefi.var on the EFI system partition. config EFI_MM_COMM_TEE - bool "UEFI variables storage service via OP-TEE" + bool "UEFI variables storage service via the trusted world" depends on OPTEE help + Allowing access to the MM SP services (SPs such as StandAlonneMM, smm-gateway). + When using the u-boot OP-TEE driver, StandAlonneMM is supported. + When using the u-boot FF-A driver any MM SP is supported. + If OP-TEE is present and running StandAloneMM, dispatch all UEFI variable related operations to that. The application will verify, authenticate and store the variables on an RPMB. + When ARM_FFA_TRANSPORT is used, dispatch all UEFI variable related + operations to the MM SP running in the secure world. + A door bell mechanism is used to notify the SP when there is data in the shared + MM buffer. The data is copied by u-boot to the shared buffer before issuing + the door bell event. + +config FFA_SHARED_MM_BUF_SIZE + int "Memory size of the shared MM communication buffer" + depends on EFI_MM_COMM_TEE && ARM_FFA_TRANSPORT + help + This defines the size in bytes of the memory area reserved for the shared + buffer used for communication between the MM feature in U-Boot and + the MM SP in secure world. + The size of the memory region must be a multiple of the size of the maximum + translation granule size that is specified in the ID_AA64MMFR0_EL1 System register. + It is assumed that the MM SP knows the size of the shared MM communication buffer. + +config FFA_SHARED_MM_BUF_OFFSET + int "Data offset in the shared MM communication buffer" + depends on EFI_MM_COMM_TEE && ARM_FFA_TRANSPORT + help + This defines the offset in bytes of the data read or written to in the shared + buffer by the MM SP. + +config FFA_SHARED_MM_BUF_ADDR + hex "Define the address of the shared MM communication buffer" + depends on EFI_MM_COMM_TEE && ARM_FFA_TRANSPORT + help + This defines the address of the shared MM communication buffer + used for communication between the MM feature in U-Boot and + the MM SP in secure world. + It is assumed that the MM SP knows the address of the shared MM communication buffer. + config EFI_VARIABLE_NO_STORE bool "Don't persist non-volatile UEFI variables" help @@ -96,7 +133,8 @@ endif config EFI_VAR_BUF_SIZE int "Memory size of the UEFI variable store" - default 16384 + default 16384 if EFI_MM_COMM_TEE + default 65536 range 4096 2147483647 help This defines the size in bytes of the memory area reserved for keeping @@ -106,7 +144,7 @@ config EFI_VAR_BUF_SIZE match the value of PcdFlashNvStorageVariableSize used to compile the StandAloneMM module. - Minimum 4096, default 16384. + Minimum 4096, default 65536, or 16384 when using StandAloneMM. config EFI_GET_TIME bool "GetTime() runtime service" diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 13a35eae6c..1a8c8d7cab 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -23,6 +23,7 @@ CFLAGS_REMOVE_initrddump.o := $(CFLAGS_NON_EFI) ifdef CONFIG_RISCV always += boothart.efi +targets += boothart.o endif ifneq ($(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),) @@ -32,10 +33,12 @@ endif ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) always += dtbdump.efi +targets += dtbdump.o endif ifdef CONFIG_EFI_LOAD_FILE2_INITRD always += initrddump.efi +targets += initrddump.o endif obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o diff --git a/lib/efi_loader/efi_acpi.c b/lib/efi_loader/efi_acpi.c index 2ddc3502b5..f755af76f8 100644 --- a/lib/efi_loader/efi_acpi.c +++ b/lib/efi_loader/efi_acpi.c @@ -10,6 +10,9 @@ #include <log.h> #include <mapmem.h> #include <acpi/acpi_table.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID; @@ -20,26 +23,28 @@ static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID; */ efi_status_t efi_acpi_register(void) { - /* Map within the low 32 bits, to allow for 32bit ACPI tables */ - u64 acpi = U32_MAX; + ulong addr, start, end; efi_status_t ret; - ulong addr; - /* Reserve 64kiB page for ACPI */ - ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, - EFI_ACPI_RECLAIM_MEMORY, 16, &acpi); + /* Mark space used for tables */ + start = ALIGN_DOWN(gd->arch.table_start, EFI_PAGE_MASK); + end = ALIGN(gd->arch.table_end, EFI_PAGE_MASK); + ret = efi_add_memory_map(start, end - start, EFI_ACPI_RECLAIM_MEMORY); if (ret != EFI_SUCCESS) return ret; + if (gd->arch.table_start_high) { + start = ALIGN_DOWN(gd->arch.table_start_high, EFI_PAGE_MASK); + end = ALIGN(gd->arch.table_end_high, EFI_PAGE_MASK); + ret = efi_add_memory_map(start, end - start, + EFI_ACPI_RECLAIM_MEMORY); + if (ret != EFI_SUCCESS) + return ret; + } - /* - * Generate ACPI tables - we know that efi_allocate_pages() returns - * a 4k-aligned address, so it is safe to assume that - * write_acpi_tables() will write the table at that address. - */ - addr = map_to_sysmem((void *)(ulong)acpi); - write_acpi_tables(addr); + addr = gd_acpi_start(); + printf("EFI using ACPI tables at %lx\n", addr); /* And expose them to our EFI payload */ return efi_install_configuration_table(&acpi_guid, - (void *)(uintptr_t)acpi); + (void *)(ulong)addr); } diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 7ac5f89f76..a40762c74c 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -344,3 +344,388 @@ efi_status_t efi_bootmgr_load(efi_handle_t *handle, void **load_options) error: return ret; } + +/** + * efi_bootmgr_enumerate_boot_option() - enumerate the possible bootable media + * + * @opt: pointer to the media boot option structure + * @volume_handles: pointer to the efi handles + * @count: number of efi handle + * Return: status code + */ +static efi_status_t efi_bootmgr_enumerate_boot_option(struct eficonfig_media_boot_option *opt, + efi_handle_t *volume_handles, + efi_status_t count) +{ + u32 i; + struct efi_handler *handler; + efi_status_t ret = EFI_SUCCESS; + + for (i = 0; i < count; i++) { + u16 *p; + u16 dev_name[BOOTMENU_DEVICE_NAME_MAX]; + char *optional_data; + struct efi_load_option lo; + char buf[BOOTMENU_DEVICE_NAME_MAX]; + struct efi_device_path *device_path; + struct efi_device_path *short_dp; + + ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler); + if (ret != EFI_SUCCESS) + continue; + ret = efi_protocol_open(handler, (void **)&device_path, + efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) + continue; + + ret = efi_disk_get_device_name(volume_handles[i], buf, BOOTMENU_DEVICE_NAME_MAX); + if (ret != EFI_SUCCESS) + continue; + + p = dev_name; + utf8_utf16_strncpy(&p, buf, strlen(buf)); + + /* prefer to short form device path */ + short_dp = efi_dp_shorten(device_path); + if (short_dp) + device_path = short_dp; + + lo.label = dev_name; + lo.attributes = LOAD_OPTION_ACTIVE; + lo.file_path = device_path; + lo.file_path_length = efi_dp_size(device_path) + sizeof(END); + /* + * Set the dedicated guid to optional_data, it is used to identify + * the boot option that automatically generated by the bootmenu. + * efi_serialize_load_option() expects optional_data is null-terminated + * utf8 string, so set the "1234567" string to allocate enough space + * to store guid, instead of realloc the load_option. + */ + lo.optional_data = "1234567"; + opt[i].size = efi_serialize_load_option(&lo, (u8 **)&opt[i].lo); + if (!opt[i].size) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + /* set the guid */ + optional_data = (char *)opt[i].lo + (opt[i].size - u16_strsize(u"1234567")); + memcpy(optional_data, &efi_guid_bootmenu_auto_generated, sizeof(efi_guid_t)); + } + +out: + return ret; +} + +/** + * efi_bootmgr_delete_invalid_boot_option() - delete non-existing boot option + * + * @opt: pointer to the media boot option structure + * @count: number of media boot option structure + * Return: status code + */ +static efi_status_t efi_bootmgr_delete_invalid_boot_option(struct eficonfig_media_boot_option *opt, + efi_status_t count) +{ + efi_uintn_t size; + void *load_option; + u32 i, list_size = 0; + struct efi_load_option lo; + u16 *var_name16 = NULL; + u16 varname[] = u"Boot####"; + efi_status_t ret = EFI_SUCCESS; + u16 *delete_index_list = NULL, *p; + efi_uintn_t buf_size; + + buf_size = 128; + var_name16 = malloc(buf_size); + if (!var_name16) + return EFI_OUT_OF_RESOURCES; + + var_name16[0] = 0; + for (;;) { + int index; + efi_guid_t guid; + efi_uintn_t tmp; + + ret = efi_next_variable_name(&buf_size, &var_name16, &guid); + if (ret == EFI_NOT_FOUND) { + /* + * EFI_NOT_FOUND indicates we retrieved all EFI variables. + * This should be treated as success. + */ + ret = EFI_SUCCESS; + break; + } + + if (ret != EFI_SUCCESS) + goto out; + + if (!efi_varname_is_load_option(var_name16, &index)) + continue; + + efi_create_indexed_name(varname, sizeof(varname), "Boot", index); + load_option = efi_get_var(varname, &efi_global_variable_guid, &size); + if (!load_option) + continue; + + tmp = size; + ret = efi_deserialize_load_option(&lo, load_option, &size); + if (ret != EFI_SUCCESS) + goto next; + + if (size >= sizeof(efi_guid_bootmenu_auto_generated) && + !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) { + for (i = 0; i < count; i++) { + if (opt[i].size == tmp && + memcmp(opt[i].lo, load_option, tmp) == 0) { + opt[i].exist = true; + break; + } + } + + /* + * The entire list of variables must be retrieved by + * efi_get_next_variable_name_int() before deleting the invalid + * boot option, just save the index here. + */ + if (i == count) { + p = realloc(delete_index_list, sizeof(u32) * + (list_size + 1)); + if (!p) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + delete_index_list = p; + delete_index_list[list_size++] = index; + } + } +next: + free(load_option); + } + + /* delete all invalid boot options */ + for (i = 0; i < list_size; i++) { + ret = efi_bootmgr_delete_boot_option(delete_index_list[i]); + if (ret != EFI_SUCCESS) + goto out; + } + +out: + free(var_name16); + free(delete_index_list); + + return ret; +} + +/** + * efi_bootmgr_get_unused_bootoption() - get unused "Boot####" index + * + * @buf: pointer to the buffer to store boot option variable name + * @buf_size: buffer size + * @index: pointer to store the index in the BootOrder variable + * Return: status code + */ +efi_status_t efi_bootmgr_get_unused_bootoption(u16 *buf, efi_uintn_t buf_size, + unsigned int *index) +{ + u32 i; + efi_status_t ret; + efi_uintn_t size; + + if (buf_size < u16_strsize(u"Boot####")) + return EFI_BUFFER_TOO_SMALL; + + for (i = 0; i <= 0xFFFF; i++) { + size = 0; + efi_create_indexed_name(buf, buf_size, "Boot", i); + ret = efi_get_variable_int(buf, &efi_global_variable_guid, + NULL, &size, NULL, NULL); + if (ret == EFI_BUFFER_TOO_SMALL) + continue; + else + break; + } + + if (i > 0xFFFF) + return EFI_OUT_OF_RESOURCES; + + *index = i; + + return EFI_SUCCESS; +} + +/** + * efi_bootmgr_append_bootorder() - append new boot option in BootOrder variable + * + * @index: "Boot####" index to append to BootOrder variable + * Return: status code + */ +efi_status_t efi_bootmgr_append_bootorder(u16 index) +{ + u16 *bootorder; + efi_status_t ret; + u16 *new_bootorder = NULL; + efi_uintn_t last, size, new_size; + + /* append new boot option */ + bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size); + last = size / sizeof(u16); + new_size = size + sizeof(u16); + new_bootorder = calloc(1, new_size); + if (!new_bootorder) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + memcpy(new_bootorder, bootorder, size); + new_bootorder[last] = index; + + ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + new_size, new_bootorder, false); + if (ret != EFI_SUCCESS) + goto out; + +out: + free(bootorder); + free(new_bootorder); + + return ret; +} + +/** + * efi_bootmgr_delete_boot_option() - delete selected boot option + * + * @boot_index: boot option index to delete + * Return: status code + */ +efi_status_t efi_bootmgr_delete_boot_option(u16 boot_index) +{ + u16 *bootorder; + u16 varname[9]; + efi_status_t ret; + unsigned int index; + efi_uintn_t num, size; + + efi_create_indexed_name(varname, sizeof(varname), + "Boot", boot_index); + ret = efi_set_variable_int(varname, &efi_global_variable_guid, + 0, 0, NULL, false); + if (ret != EFI_SUCCESS) { + log_err("delete boot option(%ls) failed\n", varname); + return ret; + } + + /* update BootOrder if necessary */ + bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size); + if (!bootorder) + return EFI_SUCCESS; + + num = size / sizeof(u16); + if (!efi_search_bootorder(bootorder, num, boot_index, &index)) + return EFI_SUCCESS; + + memmove(&bootorder[index], &bootorder[index + 1], + (num - index - 1) * sizeof(u16)); + size -= sizeof(u16); + ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + size, bootorder, false); + + return ret; +} + +/** + * efi_bootmgr_update_media_device_boot_option() - generate the media device boot option + * + * This function enumerates all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL + * and generate the bootmenu entries. + * This function also provide the BOOT#### variable maintenance for + * the media device entries. + * - Automatically create the BOOT#### variable for the newly detected device, + * this BOOT#### variable is distinguished by the special GUID + * stored in the EFI_LOAD_OPTION.optional_data + * - If the device is not attached to the system, the associated BOOT#### variable + * is automatically deleted. + * + * Return: status code + */ +efi_status_t efi_bootmgr_update_media_device_boot_option(void) +{ + u32 i; + efi_status_t ret; + efi_uintn_t count; + efi_handle_t *volume_handles = NULL; + struct eficonfig_media_boot_option *opt = NULL; + + ret = efi_locate_handle_buffer_int(BY_PROTOCOL, + &efi_simple_file_system_protocol_guid, + NULL, &count, + (efi_handle_t **)&volume_handles); + if (ret != EFI_SUCCESS) + goto out; + + opt = calloc(count, sizeof(struct eficonfig_media_boot_option)); + if (!opt) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + /* enumerate all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL */ + ret = efi_bootmgr_enumerate_boot_option(opt, volume_handles, count); + if (ret != EFI_SUCCESS) + goto out; + + /* + * System hardware configuration may vary depending on the user setup. + * The boot option is automatically added by the bootmenu. + * If the device is not attached to the system, the boot option needs + * to be deleted. + */ + ret = efi_bootmgr_delete_invalid_boot_option(opt, count); + if (ret != EFI_SUCCESS) + goto out; + + /* add non-existent boot option */ + for (i = 0; i < count; i++) { + u32 boot_index; + u16 var_name[9]; + + if (!opt[i].exist) { + ret = efi_bootmgr_get_unused_bootoption(var_name, sizeof(var_name), + &boot_index); + if (ret != EFI_SUCCESS) + goto out; + + ret = efi_set_variable_int(var_name, &efi_global_variable_guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + opt[i].size, opt[i].lo, false); + if (ret != EFI_SUCCESS) + goto out; + + ret = efi_bootmgr_append_bootorder(boot_index); + if (ret != EFI_SUCCESS) { + efi_set_variable_int(var_name, &efi_global_variable_guid, + 0, 0, NULL, false); + goto out; + } + } + } + +out: + if (opt) { + for (i = 0; i < count; i++) + free(opt[i].lo); + } + free(opt); + efi_free_pool(volume_handles); + + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + return ret; +} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d5065f296a..0e89c8505b 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -59,6 +59,10 @@ static efi_handle_t current_image; static volatile gd_t *efi_gd, *app_gd; #endif +static efi_status_t efi_uninstall_protocol + (efi_handle_t handle, const efi_guid_t *protocol, + void *protocol_interface); + /* 1 if inside U-Boot code, 0 if inside EFI payload code */ static int entry_count = 1; static int nesting_level; @@ -97,6 +101,12 @@ static efi_status_t EFIAPI efi_disconnect_controller( efi_handle_t driver_image_handle, efi_handle_t child_handle); +static +efi_status_t EFIAPI efi_connect_controller(efi_handle_t controller_handle, + efi_handle_t *driver_image_handle, + struct efi_device_path *remain_device_path, + bool recursive); + /* Called on every callback entry */ int __efi_entry_check(void) { @@ -569,9 +579,9 @@ efi_status_t efi_search_protocol(const efi_handle_t handle, * * Return: status code */ -efi_status_t efi_remove_protocol(const efi_handle_t handle, - const efi_guid_t *protocol, - void *protocol_interface) +static efi_status_t efi_remove_protocol(const efi_handle_t handle, + const efi_guid_t *protocol, + void *protocol_interface) { struct efi_handler *handler; efi_status_t ret; @@ -604,8 +614,8 @@ static efi_status_t efi_remove_all_protocols(const efi_handle_t handle) list_for_each_entry_safe(protocol, pos, &efiobj->protocols, link) { efi_status_t ret; - ret = efi_remove_protocol(handle, &protocol->guid, - protocol->protocol_interface); + ret = efi_uninstall_protocol(handle, &protocol->guid, + protocol->protocol_interface); if (ret != EFI_SUCCESS) return ret; } @@ -616,19 +626,23 @@ static efi_status_t efi_remove_all_protocols(const efi_handle_t handle) * efi_delete_handle() - delete handle * * @handle: handle to delete + * + * Return: status code */ -void efi_delete_handle(efi_handle_t handle) +efi_status_t efi_delete_handle(efi_handle_t handle) { efi_status_t ret; ret = efi_remove_all_protocols(handle); - if (ret == EFI_INVALID_PARAMETER) { - log_err("Can't remove invalid handle %p\n", handle); - return; + if (ret != EFI_SUCCESS) { + log_err("Handle %p has protocols installed. Unable to delete\n", handle); + return ret; } list_del(&handle->link); free(handle); + + return ret; } /** @@ -1298,7 +1312,7 @@ static efi_status_t efi_disconnect_all_drivers const efi_guid_t *protocol, efi_handle_t child_handle) { - efi_uintn_t number_of_drivers; + efi_uintn_t number_of_drivers, tmp; efi_handle_t *driver_handle_buffer; efi_status_t r, ret; @@ -1308,15 +1322,30 @@ static efi_status_t efi_disconnect_all_drivers return ret; if (!number_of_drivers) return EFI_SUCCESS; - ret = EFI_NOT_FOUND; + + tmp = number_of_drivers; while (number_of_drivers) { - r = EFI_CALL(efi_disconnect_controller( + ret = EFI_CALL(efi_disconnect_controller( handle, driver_handle_buffer[--number_of_drivers], child_handle)); - if (r == EFI_SUCCESS) - ret = r; + if (ret != EFI_SUCCESS) + goto reconnect; } + + free(driver_handle_buffer); + return ret; + +reconnect: + /* Reconnect all disconnected drivers */ + for (; number_of_drivers < tmp; number_of_drivers++) { + r = EFI_CALL(efi_connect_controller(handle, + &driver_handle_buffer[number_of_drivers], + NULL, true)); + if (r != EFI_SUCCESS) + EFI_PRINT("Failed to reconnect controller\n"); + } + free(driver_handle_buffer); return ret; } @@ -1336,34 +1365,35 @@ static efi_status_t efi_uninstall_protocol (efi_handle_t handle, const efi_guid_t *protocol, void *protocol_interface) { - struct efi_object *efiobj; struct efi_handler *handler; struct efi_open_protocol_info_item *item; struct efi_open_protocol_info_item *pos; efi_status_t r; - /* Check handle */ - efiobj = efi_search_obj(handle); - if (!efiobj) { - r = EFI_INVALID_PARAMETER; - goto out; - } /* Find the protocol on the handle */ r = efi_search_protocol(handle, protocol, &handler); if (r != EFI_SUCCESS) goto out; + if (handler->protocol_interface != protocol_interface) + return EFI_NOT_FOUND; /* Disconnect controllers */ - efi_disconnect_all_drivers(efiobj, protocol, NULL); + r = efi_disconnect_all_drivers(handle, protocol, NULL); + if (r != EFI_SUCCESS) { + r = EFI_ACCESS_DENIED; + goto out; + } /* Close protocol */ list_for_each_entry_safe(item, pos, &handler->open_infos, link) { if (item->info.attributes == EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL || item->info.attributes == EFI_OPEN_PROTOCOL_GET_PROTOCOL || item->info.attributes == EFI_OPEN_PROTOCOL_TEST_PROTOCOL) - list_del(&item->link); + efi_delete_open_info(item); } + /* if agents didn't close the protocols properly */ if (!list_empty(&handler->open_infos)) { r = EFI_ACCESS_DENIED; + EFI_CALL(efi_connect_controller(handle, NULL, NULL, true)); goto out; } r = efi_remove_protocol(handle, protocol, protocol_interface); diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 7a6f195cbc..af8a2ee940 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -368,9 +368,8 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s auth_hdr->auth_info.hdr.dwLength - sizeof(auth_hdr->auth_info), &buf); - if (IS_ERR(capsule_sig)) { + if (!capsule_sig) { debug("Parsing variable's pkcs7 header failed\n"); - capsule_sig = NULL; goto out; } @@ -581,6 +580,13 @@ static efi_status_t efi_capsule_update_firmware( fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0; } + if (guidcmp(&capsule_data->capsule_guid, + &efi_guid_firmware_management_capsule_id)) { + log_err("Unsupported capsule type: %pUs\n", + &capsule_data->capsule_guid); + return EFI_UNSUPPORTED; + } + /* sanity check */ if (capsule_data->header_size < sizeof(*capsule) || capsule_data->header_size >= capsule_data->capsule_image_size) @@ -751,15 +757,7 @@ efi_status_t EFIAPI efi_update_capsule( log_debug("Capsule[%d] (guid:%pUs)\n", i, &capsule->capsule_guid); - if (!guidcmp(&capsule->capsule_guid, - &efi_guid_firmware_management_capsule_id)) { - ret = efi_capsule_update_firmware(capsule); - } else { - log_err("Unsupported capsule type: %pUs\n", - &capsule->capsule_guid); - ret = EFI_UNSUPPORTED; - } - + ret = efi_capsule_update_firmware(capsule); if (ret != EFI_SUCCESS) goto out; } diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 04ebb449ca..ed7214f3a3 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -10,6 +10,7 @@ #include <common.h> #include <blk.h> #include <dm.h> +#include <dm/root.h> #include <log.h> #include <net.h> #include <usb.h> @@ -21,16 +22,6 @@ #include <asm-generic/unaligned.h> #include <linux/compat.h> /* U16_MAX */ -#ifdef CONFIG_BLKMAP -const efi_guid_t efi_guid_blkmap_dev = U_BOOT_BLKMAP_DEV_GUID; -#endif -#ifdef CONFIG_SANDBOX -const efi_guid_t efi_guid_host_dev = U_BOOT_HOST_DEV_GUID; -#endif -#ifdef CONFIG_VIRTIO_BLK -const efi_guid_t efi_guid_virtio_dev = U_BOOT_VIRTIO_DEV_GUID; -#endif - /* template END node: */ const struct efi_device_path END = { .type = DEVICE_PATH_TYPE_END, @@ -38,16 +29,6 @@ const struct efi_device_path END = { .length = sizeof(END), }; -/* template ROOT node: */ -static const struct efi_device_path_vendor ROOT = { - .dp = { - .type = DEVICE_PATH_TYPE_HARDWARE_DEVICE, - .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR, - .length = sizeof(ROOT), - }, - .guid = U_BOOT_GUID, -}; - #if defined(CONFIG_MMC) /* * Determine if an MMC device is an SD card. @@ -497,13 +478,12 @@ bool efi_dp_is_multi_instance(const struct efi_device_path *dp) __maybe_unused static unsigned int dp_size(struct udevice *dev) { if (!dev || !dev->driver) - return sizeof(ROOT); + return sizeof(struct efi_device_path_udevice); switch (device_get_uclass_id(dev)) { case UCLASS_ROOT: - case UCLASS_SIMPLE_BUS: /* stop traversing parents at this point: */ - return sizeof(ROOT); + return sizeof(struct efi_device_path_udevice); case UCLASS_ETH: return dp_size(dev->parent) + sizeof(struct efi_device_path_mac_addr); @@ -534,43 +514,15 @@ __maybe_unused static unsigned int dp_size(struct udevice *dev) return dp_size(dev->parent) + sizeof(struct efi_device_path_nvme); #endif -#ifdef CONFIG_SANDBOX - case UCLASS_HOST: - /* - * Sandbox's host device will be represented - * as vendor device with extra one byte for - * device number - */ - return dp_size(dev->parent) - + sizeof(struct efi_device_path_vendor) + 1; -#endif #ifdef CONFIG_USB case UCLASS_MASS_STORAGE: return dp_size(dev->parent) + sizeof(struct efi_device_path_controller); #endif -#ifdef CONFIG_VIRTIO_BLK - case UCLASS_VIRTIO: - /* - * Virtio devices will be represented as a vendor - * device node with an extra byte for the device - * number. - */ - return dp_size(dev->parent) - + sizeof(struct efi_device_path_vendor) + 1; -#endif -#ifdef CONFIG_BLKMAP - case UCLASS_BLKMAP: - /* - * blkmap devices will be represented as a vendor - * device node with an extra byte for the device - * number. - */ - return dp_size(dev->parent) - + sizeof(struct efi_device_path_vendor) + 1; -#endif default: - return dp_size(dev->parent); + /* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */ + return dp_size(dev->parent) + + sizeof(struct efi_device_path_udevice); } #if defined(CONFIG_MMC) case UCLASS_MMC: @@ -582,8 +534,8 @@ __maybe_unused static unsigned int dp_size(struct udevice *dev) return dp_size(dev->parent) + sizeof(struct efi_device_path_usb); default: - /* just skip over unknown classes: */ - return dp_size(dev->parent); + return dp_size(dev->parent) + + sizeof(struct efi_device_path_udevice); } } @@ -596,21 +548,19 @@ __maybe_unused static unsigned int dp_size(struct udevice *dev) */ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) { + enum uclass_id uclass_id; + if (!dev || !dev->driver) return buf; - switch (device_get_uclass_id(dev)) { - case UCLASS_ROOT: - case UCLASS_SIMPLE_BUS: { - /* stop traversing parents at this point: */ - struct efi_device_path_vendor *vdp = buf; - *vdp = ROOT; - return &vdp[1]; - } + uclass_id = device_get_uclass_id(dev); + if (uclass_id != UCLASS_ROOT) + buf = dp_fill(buf, dev->parent); + + switch (uclass_id) { #ifdef CONFIG_NETDEVICES case UCLASS_ETH: { - struct efi_device_path_mac_addr *dp = - dp_fill(buf, dev->parent); + struct efi_device_path_mac_addr *dp = buf; struct eth_pdata *pdata = dev_get_plat(dev); dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; @@ -625,63 +575,10 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) } #endif case UCLASS_BLK: - switch (dev->parent->uclass->uc_drv->id) { -#ifdef CONFIG_BLKMAP - case UCLASS_BLKMAP: { - struct efi_device_path_vendor *dp; - struct blk_desc *desc = dev_get_uclass_plat(dev); - - dp_fill(buf, dev->parent); - dp = buf; - ++dp; - dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; - dp->dp.length = sizeof(*dp) + 1; - memcpy(&dp->guid, &efi_guid_blkmap_dev, - sizeof(efi_guid_t)); - dp->vendor_data[0] = desc->devnum; - return &dp->vendor_data[1]; - } -#endif -#ifdef CONFIG_SANDBOX - case UCLASS_HOST: { - /* stop traversing parents at this point: */ - struct efi_device_path_vendor *dp; - struct blk_desc *desc = dev_get_uclass_plat(dev); - - dp_fill(buf, dev->parent); - dp = buf; - ++dp; - dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; - dp->dp.length = sizeof(*dp) + 1; - memcpy(&dp->guid, &efi_guid_host_dev, - sizeof(efi_guid_t)); - dp->vendor_data[0] = desc->devnum; - return &dp->vendor_data[1]; - } -#endif -#ifdef CONFIG_VIRTIO_BLK - case UCLASS_VIRTIO: { - struct efi_device_path_vendor *dp; - struct blk_desc *desc = dev_get_uclass_plat(dev); - - dp_fill(buf, dev->parent); - dp = buf; - ++dp; - dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; - dp->dp.length = sizeof(*dp) + 1; - memcpy(&dp->guid, &efi_guid_virtio_dev, - sizeof(efi_guid_t)); - dp->vendor_data[0] = desc->devnum; - return &dp->vendor_data[1]; - } -#endif + switch (device_get_uclass_id(dev->parent)) { #ifdef CONFIG_IDE case UCLASS_IDE: { - struct efi_device_path_atapi *dp = - dp_fill(buf, dev->parent); + struct efi_device_path_atapi *dp = buf; struct blk_desc *desc = dev_get_uclass_plat(dev); dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; @@ -697,8 +594,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) #endif #if defined(CONFIG_SCSI) case UCLASS_SCSI: { - struct efi_device_path_scsi *dp = - dp_fill(buf, dev->parent); + struct efi_device_path_scsi *dp = buf; struct blk_desc *desc = dev_get_uclass_plat(dev); dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; @@ -711,8 +607,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) #endif #if defined(CONFIG_MMC) case UCLASS_MMC: { - struct efi_device_path_sd_mmc_path *sddp = - dp_fill(buf, dev->parent); + struct efi_device_path_sd_mmc_path *sddp = buf; struct blk_desc *desc = dev_get_uclass_plat(dev); sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; @@ -726,8 +621,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) #endif #if defined(CONFIG_AHCI) || defined(CONFIG_SATA) case UCLASS_AHCI: { - struct efi_device_path_sata *dp = - dp_fill(buf, dev->parent); + struct efi_device_path_sata *dp = buf; struct blk_desc *desc = dev_get_uclass_plat(dev); dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; @@ -742,8 +636,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) #endif #if defined(CONFIG_NVME) case UCLASS_NVME: { - struct efi_device_path_nvme *dp = - dp_fill(buf, dev->parent); + struct efi_device_path_nvme *dp = buf; u32 ns_id; dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; @@ -757,8 +650,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) #if defined(CONFIG_USB) case UCLASS_MASS_STORAGE: { struct blk_desc *desc = dev_get_uclass_plat(dev); - struct efi_device_path_controller *dp = - dp_fill(buf, dev->parent); + struct efi_device_path_controller *dp = buf; dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CONTROLLER; @@ -767,16 +659,26 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) return &dp[1]; } #endif - default: - debug("%s(%u) %s: unhandled parent class: %s (%u)\n", - __FILE__, __LINE__, __func__, - dev->name, dev->parent->uclass->uc_drv->id); - return dp_fill(buf, dev->parent); + default: { + /* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */ + struct efi_device_path_udevice *dp = buf; + struct blk_desc *desc = dev_get_uclass_plat(dev); + + dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; + dp->dp.length = sizeof(*dp); + memcpy(&dp->guid, &efi_u_boot_guid, + sizeof(efi_guid_t)); + dp->uclass_id = (UCLASS_BLK & 0xffff) | + (desc->uclass_id << 16); + dp->dev_number = desc->devnum; + + return &dp[1]; } + } #if defined(CONFIG_MMC) case UCLASS_MMC: { - struct efi_device_path_sd_mmc_path *sddp = - dp_fill(buf, dev->parent); + struct efi_device_path_sd_mmc_path *sddp = buf; struct mmc *mmc = mmc_get_mmc_dev(dev); struct blk_desc *desc = mmc_get_blk_desc(mmc); @@ -792,7 +694,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) #endif case UCLASS_MASS_STORAGE: case UCLASS_USB_HUB: { - struct efi_device_path_usb *udp = dp_fill(buf, dev->parent); + struct efi_device_path_usb *udp = buf; switch (device_get_uclass_id(dev->parent)) { case UCLASS_USB_HUB: { @@ -811,11 +713,18 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) return &udp[1]; } - default: - /* If the uclass driver is missing, this will show NULL */ - log_debug("unhandled device class: %s (%s)\n", dev->name, - dev_get_uclass_name(dev)); - return dp_fill(buf, dev->parent); + default: { + struct efi_device_path_udevice *vdp = buf; + + vdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; + vdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; + vdp->dp.length = sizeof(*vdp); + memcpy(&vdp->guid, &efi_u_boot_guid, sizeof(efi_guid_t)); + vdp->uclass_id = uclass_id; + vdp->dev_number = dev->seq_; + + return &vdp[1]; + } } } @@ -1052,14 +961,12 @@ 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); + size_t dpsize = dp_size(dm_root()) + sizeof(*uart) + sizeof(END); buf = efi_alloc(dpsize); if (!buf) return NULL; - pos = buf; - memcpy(pos, &ROOT, sizeof(ROOT)); - pos += sizeof(ROOT); + pos = dp_fill(buf, dm_root()); uart = pos; uart->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; uart->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_UART; diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 28c8cdf710..f0d76113b0 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -487,15 +487,16 @@ static efi_status_t efi_disk_add_dev( */ if ((part || desc->part_type == PART_TYPE_UNKNOWN) && efi_fs_exists(desc, part)) { - diskobj->volume = efi_simple_file_system(desc, part, - diskobj->dp); + ret = efi_create_simple_file_system(desc, part, diskobj->dp, + &diskobj->volume); + if (ret != EFI_SUCCESS) + goto error; + ret = efi_add_protocol(&diskobj->header, &efi_simple_file_system_protocol_guid, diskobj->volume); - if (ret != EFI_SUCCESS) { - log_debug("simple FS failed\n"); - return ret; - } + if (ret != EFI_SUCCESS) + goto error; } diskobj->ops = block_io_disk_template; diskobj->dev_index = dev_index; @@ -538,6 +539,8 @@ static efi_status_t efi_disk_add_dev( return EFI_SUCCESS; error: efi_delete_handle(&diskobj->header); + free(diskobj->volume); + free(diskobj); return ret; } @@ -708,6 +711,7 @@ int efi_disk_remove(void *ctx, struct event *event) efi_handle_t handle; struct blk_desc *desc; struct efi_disk_obj *diskobj = NULL; + efi_status_t ret; if (dev_tag_get_ptr(dev, DM_TAG_EFI, (void **)&handle)) return 0; @@ -727,10 +731,14 @@ int efi_disk_remove(void *ctx, struct event *event) return 0; } + ret = efi_delete_handle(handle); + /* Do not delete DM device if there are still EFI drivers attached. */ + if (ret != EFI_SUCCESS) + return -1; + if (diskobj) efi_free_pool(diskobj->dp); - efi_delete_handle(handle); dev_tag_del(dev, DM_TAG_EFI); return 0; diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 520c730220..3c56cebf96 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -195,6 +195,8 @@ static struct efi_file_handle *file_open(struct file_system *fs, /* +2 is for null and '/' */ fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2); + if (!fh) + return NULL; fh->open_mode = open_mode; fh->base = efi_file_handle_protocol; @@ -1192,18 +1194,22 @@ efi_open_volume(struct efi_simple_file_system_protocol *this, return EFI_EXIT(efi_open_volume_int(this, root)); } -struct efi_simple_file_system_protocol * -efi_simple_file_system(struct blk_desc *desc, int part, - struct efi_device_path *dp) +efi_status_t +efi_create_simple_file_system(struct blk_desc *desc, int part, + struct efi_device_path *dp, + struct efi_simple_file_system_protocol **fsp) { struct file_system *fs; fs = calloc(1, sizeof(*fs)); + if (!fs) + return EFI_OUT_OF_RESOURCES; fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION; fs->base.open_volume = efi_open_volume; fs->desc = desc; fs->part = part; fs->dp = dp; + *fsp = &fs->base; - return &fs->base; + return EFI_SUCCESS; } diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c index 93e2b01c07..9abb29f1df 100644 --- a/lib/efi_loader/efi_firmware.c +++ b/lib/efi_loader/efi_firmware.c @@ -10,6 +10,7 @@ #include <charset.h> #include <dfu.h> #include <efi_loader.h> +#include <efi_variable.h> #include <fwu.h> #include <image.h> #include <signatures.h> @@ -36,11 +37,52 @@ struct fmp_payload_header { u32 lowest_supported_version; }; +/** + * struct fmp_state - fmp firmware update state + * + * This structure describes the state of the firmware update + * through FMP protocol. + * + * @fw_version: Firmware versions used + * @lowest_supported_version: Lowest supported version + * @last_attempt_version: Last attempt version + * @last_attempt_status: Last attempt status + */ +struct fmp_state { + u32 fw_version; + u32 lowest_supported_version; /* not used */ + u32 last_attempt_version; /* not used */ + u32 last_attempt_status; /* not used */ +}; + __weak void set_dfu_alt_info(char *interface, char *devstr) { env_set("dfu_alt_info", update_info.dfu_string); } +/** + * efi_firmware_get_image_type_id - get image_type_id + * @image_index: image index + * + * Return the image_type_id identified by the image index. + * + * Return: pointer to the image_type_id, NULL if image_index is invalid + */ +static +efi_guid_t *efi_firmware_get_image_type_id(u8 image_index) +{ + int i; + struct efi_fw_image *fw_array; + + fw_array = update_info.images; + for (i = 0; i < update_info.num_images; i++) { + if (fw_array[i].image_index == image_index) + return &fw_array[i].image_type_id; + } + + return NULL; +} + /* Place holder; not supported */ static efi_status_t EFIAPI efi_firmware_get_image_unsupported( @@ -103,6 +145,91 @@ efi_status_t EFIAPI efi_firmware_set_package_info_unsupported( } /** + * efi_firmware_get_lsv_from_dtb - get lowest supported version from dtb + * @image_index: Image index + * @image_type_id: Image type id + * @lsv: Pointer to store the lowest supported version + * + * Read the firmware version information from dtb. + */ +static void efi_firmware_get_lsv_from_dtb(u8 image_index, + efi_guid_t *image_type_id, u32 *lsv) +{ + const void *fdt = gd->fdt_blob; + const fdt32_t *val; + const char *guid_str; + int len, offset, index; + int parent, ret; + + *lsv = 0; + + parent = fdt_subnode_offset(fdt, 0, "firmware-version"); + if (parent < 0) + return; + + fdt_for_each_subnode(offset, fdt, parent) { + efi_guid_t guid; + + guid_str = fdt_getprop(fdt, offset, "image-type-id", &len); + if (!guid_str) + continue; + ret = uuid_str_to_bin(guid_str, guid.b, UUID_STR_FORMAT_GUID); + if (ret < 0) { + log_warning("Wrong image-type-id format.\n"); + continue; + } + + val = fdt_getprop(fdt, offset, "image-index", &len); + if (!val) + continue; + index = fdt32_to_cpu(*val); + + if (!guidcmp(&guid, image_type_id) && index == image_index) { + val = fdt_getprop(fdt, offset, + "lowest-supported-version", &len); + if (val) + *lsv = fdt32_to_cpu(*val); + } + } +} + +/** + * efi_firmware_fill_version_info - fill the version information + * @image_info: Image information + * @fw_array: Pointer to size of new image + * + * Fill the version information into image_info strucrure. + * + */ +static +void efi_firmware_fill_version_info(struct efi_firmware_image_descriptor *image_info, + struct efi_fw_image *fw_array) +{ + u16 varname[13]; /* u"FmpStateXXXX" */ + efi_status_t ret; + efi_uintn_t size; + struct fmp_state var_state = { 0 }; + + efi_create_indexed_name(varname, sizeof(varname), "FmpState", + fw_array->image_index); + size = sizeof(var_state); + ret = efi_get_variable_int(varname, &fw_array->image_type_id, + NULL, &size, &var_state, NULL); + if (ret == EFI_SUCCESS) + image_info->version = var_state.fw_version; + else + image_info->version = 0; + + efi_firmware_get_lsv_from_dtb(fw_array->image_index, + &fw_array->image_type_id, + &image_info->lowest_supported_image_version); + + image_info->version_name = NULL; /* not supported */ + image_info->last_attempt_version = 0; + image_info->last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS; +} + +/** * efi_fill_image_desc_array - populate image descriptor array * @image_info_size: Size of @image_info * @image_info: Image information @@ -131,7 +258,7 @@ static efi_status_t efi_fill_image_desc_array( struct efi_fw_image *fw_array; int i; - total_size = sizeof(*image_info) * num_image_type_guids; + total_size = sizeof(*image_info) * update_info.num_images; if (*image_info_size < total_size) { *image_info_size = total_size; @@ -141,21 +268,20 @@ static efi_status_t efi_fill_image_desc_array( *image_info_size = total_size; fw_array = update_info.images; - *descriptor_count = num_image_type_guids; + *descriptor_count = update_info.num_images; *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; *descriptor_size = sizeof(*image_info); *package_version = 0xffffffff; /* not supported */ *package_version_name = NULL; /* not supported */ - for (i = 0; i < num_image_type_guids; i++) { + for (i = 0; i < update_info.num_images; i++) { image_info[i].image_index = fw_array[i].image_index; image_info[i].image_type_id = fw_array[i].image_type_id; image_info[i].image_id = fw_array[i].image_index; - image_info[i].image_id_name = fw_array[i].fw_name; - image_info[i].version = 0; /* not supported */ - image_info[i].version_name = NULL; /* not supported */ + efi_firmware_fill_version_info(&image_info[i], &fw_array[i]); + image_info[i].size = 0; image_info[i].attributes_supported = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | @@ -168,9 +294,6 @@ static efi_status_t efi_fill_image_desc_array( image_info[0].attributes_setting |= IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED; - image_info[i].lowest_supported_image_version = 0; - image_info[i].last_attempt_version = 0; - image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS; image_info[i].hardware_instance = 1; image_info[i].dependencies = NULL; } @@ -194,8 +317,6 @@ efi_status_t efi_firmware_capsule_authenticate(const void **p_image, { const void *image = *p_image; efi_uintn_t image_size = *p_image_size; - u32 fmp_hdr_signature; - struct fmp_payload_header *header; void *capsule_payload; efi_status_t status; efi_uintn_t capsule_payload_size; @@ -222,27 +343,122 @@ efi_status_t efi_firmware_capsule_authenticate(const void **p_image, debug("Updating capsule without authenticating.\n"); } - fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE; - header = (void *)image; - - if (!memcmp(&header->signature, &fmp_hdr_signature, - sizeof(fmp_hdr_signature))) { - /* - * When building the capsule with the scripts in - * edk2, a FMP header is inserted above the capsule - * payload. Compensate for this header to get the - * actual payload that is to be updated. - */ - image += header->header_size; - image_size -= header->header_size; - } - *p_image = image; *p_image_size = image_size; return EFI_SUCCESS; } /** + * efi_firmware_set_fmp_state_var - set FmpStateXXXX variable + * @state: Pointer to fmp state + * @image_index: image index + * + * Update the FmpStateXXXX variable with the firmware update state. + * + * Return: status code + */ +static +efi_status_t efi_firmware_set_fmp_state_var(struct fmp_state *state, u8 image_index) +{ + u16 varname[13]; /* u"FmpStateXXXX" */ + efi_status_t ret; + efi_guid_t *image_type_id; + struct fmp_state var_state = { 0 }; + + image_type_id = efi_firmware_get_image_type_id(image_index); + if (!image_type_id) + return EFI_INVALID_PARAMETER; + + efi_create_indexed_name(varname, sizeof(varname), "FmpState", + image_index); + + /* + * Only the fw_version is set here. + * lowest_supported_version in FmpState variable is ignored since + * it can be tampered if the file based EFI variable storage is used. + */ + var_state.fw_version = state->fw_version; + + ret = efi_set_variable_int(varname, image_type_id, + EFI_VARIABLE_READ_ONLY | + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(var_state), &var_state, false); + + return ret; +} + +/** + * efi_firmware_get_fw_version - get fw_version from FMP payload header + * @p_image: Pointer to new image + * @p_image_size: Pointer to size of new image + * @state: Pointer to fmp state + * + * Parse the FMP payload header and fill the fmp_state structure. + * If no FMP payload header is found, fmp_state structure is not updated. + * + */ +static void efi_firmware_get_fw_version(const void **p_image, + efi_uintn_t *p_image_size, + struct fmp_state *state) +{ + const struct fmp_payload_header *header; + u32 fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE; + + header = *p_image; + if (header->signature == fmp_hdr_signature) { + /* FMP header is inserted above the capsule payload */ + state->fw_version = header->fw_version; + + *p_image += header->header_size; + *p_image_size -= header->header_size; + } +} + +/** + * efi_firmware_verify_image - verify image + * @p_image: Pointer to new image + * @p_image_size: Pointer to size of new image + * @image_index: Image index + * @state: Pointer to fmp state + * + * Verify the capsule authentication and check if the fw_version + * is equal or greater than the lowest supported version. + * + * Return: status code + */ +static +efi_status_t efi_firmware_verify_image(const void **p_image, + efi_uintn_t *p_image_size, + u8 image_index, + struct fmp_state *state) +{ + u32 lsv; + efi_status_t ret; + efi_guid_t *image_type_id; + + ret = efi_firmware_capsule_authenticate(p_image, p_image_size); + if (ret != EFI_SUCCESS) + return ret; + + efi_firmware_get_fw_version(p_image, p_image_size, state); + + image_type_id = efi_firmware_get_image_type_id(image_index); + if (!image_type_id) + return EFI_INVALID_PARAMETER; + + efi_firmware_get_lsv_from_dtb(image_index, image_type_id, &lsv); + if (state->fw_version < lsv) { + log_err("Firmware version %u too low. Expecting >= %u. Aborting update\n", + state->fw_version, lsv); + return EFI_INVALID_PARAMETER; + } + + return ret; +} + +/** * efi_firmware_get_image_info - return information about the current * firmware image * @this: Protocol instance @@ -331,6 +547,7 @@ efi_status_t EFIAPI efi_firmware_fit_set_image( u16 **abort_reason) { efi_status_t status; + struct fmp_state state = { 0 }; EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image, image_size, vendor_code, progress, abort_reason); @@ -338,13 +555,16 @@ efi_status_t EFIAPI efi_firmware_fit_set_image( if (!image || image_index != 1) return EFI_EXIT(EFI_INVALID_PARAMETER); - status = efi_firmware_capsule_authenticate(&image, &image_size); + status = efi_firmware_verify_image(&image, &image_size, image_index, + &state); if (status != EFI_SUCCESS) return EFI_EXIT(status); if (fit_update(image)) return EFI_EXIT(EFI_DEVICE_ERROR); + efi_firmware_set_fmp_state_var(&state, image_index); + return EFI_EXIT(EFI_SUCCESS); } @@ -392,6 +612,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image( { int ret; efi_status_t status; + struct fmp_state state = { 0 }; EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image, image_size, vendor_code, progress, abort_reason); @@ -399,7 +620,8 @@ efi_status_t EFIAPI efi_firmware_raw_set_image( if (!image) return EFI_EXIT(EFI_INVALID_PARAMETER); - status = efi_firmware_capsule_authenticate(&image, &image_size); + status = efi_firmware_verify_image(&image, &image_size, image_index, + &state); if (status != EFI_SUCCESS) return EFI_EXIT(status); @@ -419,6 +641,8 @@ efi_status_t EFIAPI efi_firmware_raw_set_image( NULL, NULL)) return EFI_EXIT(EFI_DEVICE_ERROR); + efi_firmware_set_fmp_state_var(&state, image_index); + return EFI_EXIT(EFI_SUCCESS); } diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c index 1f4ab2b419..cdfd16ea77 100644 --- a/lib/efi_loader/efi_helper.c +++ b/lib/efi_loader/efi_helper.c @@ -257,3 +257,28 @@ efi_status_t efi_next_variable_name(efi_uintn_t *size, u16 **buf, efi_guid_t *gu return ret; } + +/** + * efi_search_bootorder() - search the boot option index in BootOrder + * + * @bootorder: pointer to the BootOrder variable + * @num: number of BootOrder entry + * @target: target boot option index to search + * @index: pointer to store the index of BootOrder variable + * Return: true if exists, false otherwise + */ +bool efi_search_bootorder(u16 *bootorder, efi_uintn_t num, u32 target, u32 *index) +{ + u32 i; + + for (i = 0; i < num; i++) { + if (target == bootorder[i]) { + if (index) + *index = i; + + return true; + } + } + + return false; +} diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 26df0da16c..97547571ce 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -592,6 +592,7 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) struct efi_signature_store *db = NULL, *dbx = NULL; void *new_efi = NULL; u8 *auth, *wincerts_end; + u64 new_efi_size = efi_size; size_t auth_size; bool ret = false; @@ -600,11 +601,11 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) if (!efi_secure_boot_enabled()) return true; - new_efi = efi_prepare_aligned_image(efi, (u64 *)&efi_size); + new_efi = efi_prepare_aligned_image(efi, &new_efi_size); if (!new_efi) return false; - if (!efi_image_parse(new_efi, efi_size, ®s, &wincerts, + if (!efi_image_parse(new_efi, new_efi_size, ®s, &wincerts, &wincerts_len)) { log_err("Parsing PE executable image failed\n"); goto out; diff --git a/lib/efi_loader/efi_load_options.c b/lib/efi_loader/efi_load_options.c index 3cfddee014..5f62184da1 100644 --- a/lib/efi_loader/efi_load_options.c +++ b/lib/efi_loader/efi_load_options.c @@ -31,10 +31,10 @@ efi_status_t efi_set_load_options(efi_handle_t handle, efi_status_t ret; ret = efi_search_protocol(handle, &efi_guid_loaded_image, &handler); - loaded_image_info = handler->protocol_interface; if (ret != EFI_SUCCESS) return EFI_INVALID_PARAMETER; + loaded_image_info = handler->protocol_interface; loaded_image_info->load_options = load_options; loaded_image_info->load_options_size = load_options_size; diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index e2ca78d935..f752703b43 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -34,6 +34,7 @@ struct efi_mem_list { #define EFI_CARVE_NO_OVERLAP -1 #define EFI_CARVE_LOOP_AGAIN -2 #define EFI_CARVE_OVERLAPS_NONRAM -3 +#define EFI_CARVE_OUT_OF_RESOURCES -4 /* This list contains all memory map items */ static LIST_HEAD(efi_mem); @@ -239,6 +240,8 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map, /* Create a new map from [ carve_start ... map_end ] */ newmap = calloc(1, sizeof(*newmap)); + if (!newmap) + return EFI_CARVE_OUT_OF_RESOURCES; newmap->desc = map->desc; newmap->desc.physical_start = carve_start; newmap->desc.virtual_start = carve_start; @@ -282,6 +285,8 @@ static efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, ++efi_memory_map_key; newlist = calloc(1, sizeof(*newlist)); + if (!newlist) + return EFI_OUT_OF_RESOURCES; newlist->desc.type = memory_type; newlist->desc.physical_start = start; newlist->desc.virtual_start = start; @@ -311,11 +316,15 @@ static efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, r = efi_mem_carve_out(lmem, &newlist->desc, overlap_only_ram); switch (r) { + case EFI_CARVE_OUT_OF_RESOURCES: + free(newlist); + return EFI_OUT_OF_RESOURCES; case EFI_CARVE_OVERLAPS_NONRAM: /* * The user requested to only have RAM overlaps, * but we hit a non-RAM region. Error out. */ + free(newlist); return EFI_NO_MAPPING; case EFI_CARVE_NO_OVERLAP: /* Just ignore this list entry */ @@ -346,6 +355,7 @@ static efi_status_t efi_add_memory_map_pg(u64 start, u64 pages, * The payload wanted to have RAM overlaps, but we overlapped * with an unallocated region. Error out. */ + free(newlist); return EFI_NO_MAPPING; } @@ -487,7 +497,7 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type, enum efi_memory_type memory_type, efi_uintn_t pages, uint64_t *memory) { - u64 len = pages << EFI_PAGE_SHIFT; + u64 len; efi_status_t ret; uint64_t addr; @@ -497,6 +507,11 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type, return EFI_INVALID_PARAMETER; if (!memory) return EFI_INVALID_PARAMETER; + len = (u64)pages << EFI_PAGE_SHIFT; + /* Catch possible overflow on 64bit systems */ + if (sizeof(efi_uintn_t) == sizeof(u64) && + (len >> EFI_PAGE_SHIFT) != (u64)pages) + return EFI_OUT_OF_RESOURCES; switch (type) { case EFI_ALLOCATE_ANY_PAGES: @@ -862,7 +877,7 @@ efi_status_t efi_add_conventional_memory_map(u64 ram_start, u64 ram_end, */ __weak void efi_add_known_memory(void) { - u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK; + u64 ram_top = gd->ram_top & ~EFI_PAGE_MASK; int i; /* diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c index a83ae7a46c..7b7926a0d4 100644 --- a/lib/efi_loader/efi_tcg2.c +++ b/lib/efi_loader/efi_tcg2.c @@ -706,8 +706,7 @@ static efi_status_t tcg2_create_digest(const u8 *input, u32 length, sha512_finish(&ctx_512, final); break; default: - EFI_PRINT("Unsupported algorithm %x\n", hash_alg); - return EFI_INVALID_PARAMETER; + continue; } digest_list->digests[digest_list->count].hash_alg = hash_alg; memcpy(&digest_list->digests[digest_list->count].digest, final, @@ -930,8 +929,7 @@ static efi_status_t tcg2_hash_pe_image(void *efi, u64 efi_size, hash_calculate("sha512", regs->reg, regs->num, hash); break; default: - EFI_PRINT("Unsupported algorithm %x\n", hash_alg); - return EFI_INVALID_PARAMETER; + continue; } digest_list->digests[digest_list->count].hash_alg = hash_alg; memcpy(&digest_list->digests[digest_list->count].digest, hash, @@ -1680,8 +1678,8 @@ void tcg2_uninit(void) if (!is_tcg2_protocol_installed()) return; - ret = efi_remove_protocol(efi_root, &efi_guid_tcg2_protocol, - (void *)&efi_tcg2_protocol); + ret = efi_uninstall_multiple_protocol_interfaces(efi_root, &efi_guid_tcg2_protocol, + &efi_tcg2_protocol, NULL); if (ret != EFI_SUCCESS) log_err("Failed to remove EFI TCG2 protocol\n"); } @@ -2507,8 +2505,8 @@ efi_status_t efi_tcg2_register(void) goto fail; } - ret = efi_add_protocol(efi_root, &efi_guid_tcg2_protocol, - (void *)&efi_tcg2_protocol); + ret = efi_install_multiple_protocol_interfaces(&efi_root, &efi_guid_tcg2_protocol, + &efi_tcg2_protocol, NULL); if (ret != EFI_SUCCESS) { tcg2_uninit(); goto fail; diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c index d6b65aed12..5fa7dcb8d3 100644 --- a/lib/efi_loader/efi_var_mem.c +++ b/lib/efi_loader/efi_var_mem.c @@ -177,6 +177,10 @@ efi_status_t __efi_runtime efi_var_mem_ins( u64 __efi_runtime efi_var_mem_free(void) { + if (efi_var_buf->length + sizeof(struct efi_var_entry) >= + EFI_VAR_BUF_SIZE) + return 0; + return EFI_VAR_BUF_SIZE - efi_var_buf->length - sizeof(struct efi_var_entry); } diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435d..09d03c0eee 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,16 +4,38 @@ * * Copyright (C) 2019 Linaro Ltd. <sughosh.ganu@linaro.org> * Copyright (C) 2019 Linaro Ltd. <ilias.apalodimas@linaro.org> + * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> */ #include <common.h> +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h> + +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +/* MM return codes */ +#define MM_SUCCESS (0) +#define MM_NOT_SUPPORTED (-1) +#define MM_INVALID_PARAMETER (-2) +#define MM_DENIED (-3) +#define MM_NO_MEMORY (-5) + +static const char *mm_sp_svc_uuid = MM_SP_UUID; +static u16 mm_sp_id; +#endif extern struct efi_var_file __efi_runtime_data *efi_var_buf; static efi_uintn_t max_buffer_size; /* comm + var + func + data */ @@ -144,12 +166,238 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) return ret; } +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +/** + * ffa_notify_mm_sp() - Announce there is data in the shared buffer + * + * Notify the MM partition in the trusted world that + * data is available in the shared buffer. + * This is a blocking call during which trusted world has exclusive access + * to the MM shared buffer. + * + * Return: + * + * 0 on success + */ +static int ffa_notify_mm_sp(void) +{ + struct ffa_send_direct_data msg = {0}; + int ret; + int sp_event_ret; + struct udevice *dev; + + ret = uclass_first_device_err(UCLASS_FFA, &dev); + if (ret) { + log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n"); + return ret; + } + + msg.data0 = CONFIG_FFA_SHARED_MM_BUF_OFFSET; /* x3 */ + + ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1); + if (ret) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + switch (sp_event_ret) { + case MM_SUCCESS: + ret = 0; + break; + case MM_NOT_SUPPORTED: + ret = -EINVAL; + break; + case MM_INVALID_PARAMETER: + ret = -EPERM; + break; + case MM_DENIED: + ret = -EACCES; + break; + case MM_NO_MEMORY: + ret = -EBUSY; + break; + default: + ret = -EACCES; + } + + return ret; +} + +/** + * ffa_discover_mm_sp_id() - Query the MM partition ID + * + * Use the FF-A driver to get the MM partition ID. + * If multiple partitions are found, use the first one. + * This is a boot time function. + * + * Return: + * + * 0 on success + */ +static int ffa_discover_mm_sp_id(void) +{ + u32 count = 0; + int ret; + struct ffa_partition_desc *descs; + struct udevice *dev; + + ret = uclass_first_device_err(UCLASS_FFA, &dev); + if (ret) { + log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n"); + return ret; + } + + /* Ask the driver to fill the buffer with the SPs info */ + ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, &descs); + if (ret) { + log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret); + return ret; + } + + /* MM SPs found , use the first one */ + + mm_sp_id = descs[0].info.id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + return 0; +} + /** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * ffa_mm_communicate() - Exchange EFI services data with the MM partition using FF-A + * @comm_buf: locally allocated communication buffer used for rx/tx + * @dsize: communication buffer size + * + * Issue a door bell event to notify the MM partition (SP) running in OP-TEE + * that there is data to read from the shared buffer. + * Communication with the MM SP is performed using FF-A transport. + * On the event, MM SP can read the data from the buffer and + * update the MM shared buffer with response data. + * The response data is copied back to the communication buffer. + * + * Return: + * + * EFI status code + */ +static efi_status_t ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) +{ + ulong tx_data_size; + int ffa_ret; + efi_status_t efi_ret; + struct efi_mm_communicate_header *mm_hdr; + void *virt_shared_buf; + + if (!comm_buf) + return EFI_INVALID_PARAMETER; + + /* Discover MM partition ID at boot time */ + if (!mm_sp_id && ffa_discover_mm_sp_id()) { + log_err("EFI: Failure to discover MM SP ID at boot time, FF-A MM comms failure\n"); + return EFI_UNSUPPORTED; + } + + mm_hdr = (struct efi_mm_communicate_header *)comm_buf; + tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t); + + if (comm_buf_size != tx_data_size || tx_data_size > CONFIG_FFA_SHARED_MM_BUF_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = map_sysmem((phys_addr_t)CONFIG_FFA_SHARED_MM_BUF_ADDR, 0); + memcpy(virt_shared_buf, comm_buf, tx_data_size); + + /* + * The secure world might have cache disabled for + * the device region used for shared buffer (which is the case for Optee). + * In this case, the secure world reads the data from DRAM. + * Let's flush the cache so the DRAM is updated with the latest data. + */ +#ifdef CONFIG_ARM64 + invalidate_dcache_all(); +#endif + + /* Announce there is data in the shared buffer */ + + ffa_ret = ffa_notify_mm_sp(); + + switch (ffa_ret) { + case 0: { + ulong rx_data_size; + /* Copy the MM SP response from the shared buffer to the communication buffer */ + rx_data_size = ((struct efi_mm_communicate_header *)virt_shared_buf)->message_len + + sizeof(efi_guid_t) + + sizeof(size_t); + + if (rx_data_size > comm_buf_size) { + efi_ret = EFI_OUT_OF_RESOURCES; + break; + } + + memcpy(comm_buf, virt_shared_buf, rx_data_size); + efi_ret = EFI_SUCCESS; + break; + } + case -EINVAL: + efi_ret = EFI_DEVICE_ERROR; + break; + case -EPERM: + efi_ret = EFI_INVALID_PARAMETER; + break; + case -EACCES: + efi_ret = EFI_ACCESS_DENIED; + break; + case -EBUSY: + efi_ret = EFI_OUT_OF_RESOURCES; + break; + default: + efi_ret = EFI_ACCESS_DENIED; + } + + unmap_sysmem(virt_shared_buf); + return efi_ret; +} + +/** + * get_mm_comms() - detect the available MM transport + * + * Make sure the FF-A bus is probed successfully + * which means FF-A communication with secure world works and ready + * for use. + * + * If FF-A bus is not ready, use OPTEE comms. + * + * Return: + * + * MM_COMMS_FFA or MM_COMMS_OPTEE + */ +static enum mm_comms_select get_mm_comms(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_FFA, &dev); + if (ret) { + log_debug("EFI: Cannot find FF-A bus device, trying Optee comms\n"); + return MM_COMMS_OPTEE; + } + + return MM_COMMS_FFA; +} +#endif + +/** + * mm_communicate() - Adjust the communication buffer to the MM SP and send * it to OP-TEE * - * @comm_buf: locally allocted communcation buffer + * @comm_buf: locally allocated communication buffer * @dsize: buffer size + * + * The SP (also called partition) can be any MM SP such as StandAlonneMM or smm-gateway. + * The comm_buf format is the same for both partitions. + * When using the u-boot OP-TEE driver, StandAlonneMM is supported. + * When using the u-boot FF-A driver, any MM SP is supported. + * * Return: status code */ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) @@ -157,12 +405,24 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr; +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + enum mm_comms_select mm_comms; +#endif dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE; mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; - ret = optee_mm_communicate(comm_buf, dsize); +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + mm_comms = get_mm_comms(); + if (mm_comms == MM_COMMS_FFA) + ret = ffa_mm_communicate(comm_buf, dsize); + else + ret = optee_mm_communicate(comm_buf, dsize); +#else + ret = optee_mm_communicate(comm_buf, dsize); +#endif + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -697,7 +957,7 @@ void efi_variables_boot_exit_notify(void) ret = EFI_NOT_FOUND; if (ret != EFI_SUCCESS) - log_err("Unable to notify StMM for ExitBootServices\n"); + log_err("Unable to notify the MM partition for ExitBootServices\n"); free(comm_buf); /* diff --git a/lib/efi_selftest/efi_selftest_controllers.c b/lib/efi_selftest/efi_selftest_controllers.c index 63e674bedc..02f19574f8 100644 --- a/lib/efi_selftest/efi_selftest_controllers.c +++ b/lib/efi_selftest/efi_selftest_controllers.c @@ -28,6 +28,7 @@ static efi_guid_t guid_child_controller = static efi_handle_t handle_controller; static efi_handle_t handle_child_controller[NUMBER_OF_CHILD_CONTROLLERS]; static efi_handle_t handle_driver; +static bool allow_removal; /* * Count child controllers @@ -85,8 +86,8 @@ static efi_status_t EFIAPI supported( controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER); switch (ret) { case EFI_ACCESS_DENIED: - case EFI_ALREADY_STARTED: return ret; + case EFI_ALREADY_STARTED: case EFI_SUCCESS: break; default: @@ -124,8 +125,8 @@ static efi_status_t EFIAPI start( controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER); switch (ret) { case EFI_ACCESS_DENIED: - case EFI_ALREADY_STARTED: return ret; + case EFI_ALREADY_STARTED: case EFI_SUCCESS: break; default: @@ -238,6 +239,9 @@ static efi_status_t EFIAPI stop( if (ret != EFI_SUCCESS) efi_st_error("Cannot free buffer\n"); + if (!allow_removal) + return EFI_DEVICE_ERROR; + /* Detach driver from controller */ ret = boottime->close_protocol( controller_handle, &guid_controller, @@ -342,6 +346,7 @@ static int execute(void) return EFI_ST_FAILURE; } /* Destroy remaining child controllers and disconnect controller */ + allow_removal = true; ret = boottime->disconnect_controller(handle_controller, NULL, NULL); if (ret != EFI_SUCCESS) { efi_st_error("Failed to disconnect controller\n"); @@ -393,7 +398,40 @@ static int execute(void) efi_st_error("Number of children %u != %u\n", (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS); } - /* Uninstall controller protocol */ + + allow_removal = false; + /* Try to uninstall controller protocol using the wrong interface */ + ret = boottime->uninstall_protocol_interface(handle_controller, + &guid_controller, + &interface1); + if (ret != EFI_NOT_FOUND) { + efi_st_error("Interface not checked when uninstalling protocol\n"); + return EFI_ST_FAILURE; + } + + /* + * Uninstall a protocol while Disconnect controller won't + * allow it. + */ + ret = boottime->uninstall_protocol_interface(handle_controller, + &guid_controller, + &interface2); + if (ret != EFI_ACCESS_DENIED) { + efi_st_error("Uninstall protocol interface failed\n"); + return EFI_ST_FAILURE; + } + /* + * Check number of child controllers and make sure children have + * been reconnected + */ + ret = count_child_controllers(handle_controller, &guid_controller, + &count); + if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) { + efi_st_error("Number of children %u != %u\n", + (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS); + } + + allow_removal = true; ret = boottime->uninstall_protocol_interface(handle_controller, &guid_controller, &interface2); diff --git a/lib/efi_selftest/efi_selftest_hii.c b/lib/efi_selftest/efi_selftest_hii.c index f4b55889e2..f219c0120a 100644 --- a/lib/efi_selftest/efi_selftest_hii.c +++ b/lib/efi_selftest/efi_selftest_hii.c @@ -220,14 +220,12 @@ static int test_hii_database_list_package_lists(void) if (ret != EFI_BUFFER_TOO_SMALL) { efi_st_error("list_package_lists returned %u\n", (unsigned int)ret); - ret = EFI_ST_FAILURE; goto out; } ret = boottime->allocate_pool(EFI_LOADER_DATA, handles_size, (void **)&handles); if (ret != EFI_SUCCESS) { efi_st_error("AllocatePool failed\n"); - ret = EFI_ST_FAILURE; goto out; } ret = hii_database_protocol->list_package_lists(hii_database_protocol, @@ -236,7 +234,6 @@ static int test_hii_database_list_package_lists(void) if (ret != EFI_SUCCESS) { efi_st_error("list_package_lists returned %u\n", (unsigned int)ret); - ret = EFI_ST_FAILURE; goto out; } ret = boottime->free_pool(handles); @@ -254,14 +251,12 @@ static int test_hii_database_list_package_lists(void) if (ret != EFI_BUFFER_TOO_SMALL) { efi_st_error("list_package_lists returned %u\n", (unsigned int)ret); - ret = EFI_ST_FAILURE; goto out; } ret = boottime->allocate_pool(EFI_LOADER_DATA, handles_size, (void **)&handles); if (ret != EFI_SUCCESS) { efi_st_error("AllocatePool failed\n"); - ret = EFI_ST_FAILURE; goto out; } ret = hii_database_protocol->list_package_lists(hii_database_protocol, @@ -270,13 +265,11 @@ static int test_hii_database_list_package_lists(void) if (ret != EFI_SUCCESS) { efi_st_error("list_package_lists returned %u\n", (unsigned int)ret); - ret = EFI_ST_FAILURE; goto out; } ret = boottime->free_pool(handles); if (ret != EFI_SUCCESS) { efi_st_error("FreePool failed\n"); - ret = EFI_ST_FAILURE; goto out; } @@ -289,14 +282,12 @@ static int test_hii_database_list_package_lists(void) if (ret != EFI_BUFFER_TOO_SMALL) { efi_st_error("list_package_lists returned %u\n", (unsigned int)ret); - ret = EFI_ST_FAILURE; goto out; } ret = boottime->allocate_pool(EFI_LOADER_DATA, handles_size, (void **)&handles); if (ret != EFI_SUCCESS) { efi_st_error("AllocatePool failed\n"); - ret = EFI_ST_FAILURE; goto out; } ret = hii_database_protocol->list_package_lists(hii_database_protocol, @@ -305,13 +296,11 @@ static int test_hii_database_list_package_lists(void) if (ret != EFI_SUCCESS) { efi_st_error("list_package_lists returned %u\n", (unsigned int)ret); - ret = EFI_ST_FAILURE; goto out; } ret = boottime->free_pool(handles); if (ret != EFI_SUCCESS) { efi_st_error("FreePool failed\n"); - ret = EFI_ST_FAILURE; goto out; } diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile index 1993088e5b..c9e3c06b48 100644 --- a/lib/fwu_updates/Makefile +++ b/lib/fwu_updates/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o +obj-$(CONFIG_FWU_MDATA_MTD) += fwu_mtd.o diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 5313d07302..4d0c8b84b9 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -15,13 +15,13 @@ #include <linux/errno.h> #include <linux/types.h> +#include <u-boot/crc.h> + +static struct fwu_mdata g_mdata; /* = {0} makes uninit crc32 always invalid */ +static struct udevice *g_dev; static u8 in_trial; static u8 boottime_check; -#include <linux/errno.h> -#include <linux/types.h> -#include <u-boot/crc.h> - enum { IMAGE_ACCEPT_SET = 1, IMAGE_ACCEPT_CLEAR, @@ -33,26 +33,6 @@ enum { BOTH_PARTS, }; -static int fwu_get_dev_mdata(struct udevice **dev, struct fwu_mdata *mdata) -{ - int ret; - - ret = uclass_first_device_err(UCLASS_FWU_MDATA, dev); - if (ret) { - log_debug("Cannot find fwu device\n"); - return ret; - } - - if (!mdata) - return 0; - - ret = fwu_get_mdata(*dev, mdata); - if (ret < 0) - log_debug("Unable to get valid FWU metadata\n"); - - return ret; -} - static int trial_counter_update(u16 *trial_state_ctr) { bool delete; @@ -115,6 +95,8 @@ static int fwu_trial_count_update(void) log_err("Unable to revert active_index\n"); ret = 1; } else { + log_info("Trial State count: attempt %d out of %d\n", + trial_state_ctr, CONFIG_FWU_TRIAL_STATE_CNT); ret = trial_counter_update(&trial_state_ctr); if (ret) log_err("Unable to increment TrialStateCtr variable\n"); @@ -151,7 +133,7 @@ static int fwu_get_image_type_id(u8 *image_index, efi_guid_t *image_type_id) index = *image_index; image = update_info.images; - for (i = 0; i < num_image_type_guids; i++) { + for (i = 0; i < update_info.num_images; i++) { if (index == image[i].image_index) { guidcpy(image_type_id, &image[i].image_type_id); return 0; @@ -162,133 +144,124 @@ static int fwu_get_image_type_id(u8 *image_index, efi_guid_t *image_type_id) } /** - * fwu_verify_mdata() - Verify the FWU metadata + * fwu_sync_mdata() - Update given meta-data partition(s) with the copy provided * @mdata: FWU metadata structure - * @pri_part: FWU metadata partition is primary or secondary - * - * Verify the FWU metadata by computing the CRC32 for the metadata - * structure and comparing it against the CRC32 value stored as part - * of the structure. + * @part: Bitmask of FWU metadata partitions to be written to * * Return: 0 if OK, -ve on error - * */ -int fwu_verify_mdata(struct fwu_mdata *mdata, bool pri_part) +static int fwu_sync_mdata(struct fwu_mdata *mdata, int part) { - u32 calc_crc32; - void *buf; + void *buf = &mdata->version; + int err; + + if (part == BOTH_PARTS) { + err = fwu_sync_mdata(mdata, SECONDARY_PART); + if (err) + return err; + part = PRIMARY_PART; + } - buf = &mdata->version; - calc_crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32)); + /* + * Calculate the crc32 for the updated FWU metadata + * and put the updated value in the FWU metadata crc32 + * field + */ + mdata->crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32)); - if (calc_crc32 != mdata->crc32) { - log_debug("crc32 check failed for %s FWU metadata partition\n", - pri_part ? "primary" : "secondary"); - return -EINVAL; + err = fwu_write_mdata(g_dev, mdata, part == PRIMARY_PART); + if (err) { + log_err("Unable to write %s mdata\n", + part == PRIMARY_PART ? "primary" : "secondary"); + return err; } + /* update the cached copy of meta-data */ + memcpy(&g_mdata, mdata, sizeof(struct fwu_mdata)); + return 0; } +static inline int mdata_crc_check(struct fwu_mdata *mdata) +{ + void *buf = &mdata->version; + u32 calc_crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32)); + + return calc_crc32 == mdata->crc32 ? 0 : -EINVAL; +} + /** - * fwu_check_mdata_validity() - Check for validity of the FWU metadata copies + * fwu_get_mdata() - Read, verify and return the FWU metadata + * @mdata: Output FWU metadata read or NULL * * Read both the metadata copies from the storage media, verify their checksum, * and ascertain that both copies match. If one of the copies has gone bad, * restore it from the good copy. * * Return: 0 if OK, -ve on error - * */ -int fwu_check_mdata_validity(void) +int fwu_get_mdata(struct fwu_mdata *mdata) { - int ret; - struct udevice *dev; - struct fwu_mdata pri_mdata; - struct fwu_mdata secondary_mdata; - uint mdata_parts[2]; - uint valid_partitions, invalid_partitions; - - ret = fwu_get_dev_mdata(&dev, NULL); - if (ret) - return ret; - - /* - * Check if the platform has defined its own - * function to check the metadata partitions' - * validity. If so, that takes precedence. - */ - ret = fwu_mdata_check(dev); - if (!ret || ret != -ENOSYS) - return ret; - - /* - * Two FWU metadata partitions are expected. - * If we don't have two, user needs to create - * them first - */ - valid_partitions = 0; - ret = fwu_get_mdata_part_num(dev, mdata_parts); - if (ret < 0) { - log_debug("Error getting the FWU metadata partitions\n"); - return -ENOENT; - } - - ret = fwu_read_mdata_partition(dev, &pri_mdata, mdata_parts[0]); - if (!ret) { - ret = fwu_verify_mdata(&pri_mdata, 1); - if (!ret) - valid_partitions |= PRIMARY_PART; - } - - ret = fwu_read_mdata_partition(dev, &secondary_mdata, mdata_parts[1]); - if (!ret) { - ret = fwu_verify_mdata(&secondary_mdata, 0); - if (!ret) - valid_partitions |= SECONDARY_PART; + int err; + bool parts_ok[2] = { false }; + struct fwu_mdata s, *parts_mdata[2]; + + parts_mdata[0] = &g_mdata; + parts_mdata[1] = &s; + + /* if mdata already read and ready */ + err = mdata_crc_check(parts_mdata[0]); + if (!err) + goto ret_mdata; + /* else read, verify and, if needed, fix mdata */ + + for (int i = 0; i < 2; i++) { + parts_ok[i] = false; + err = fwu_read_mdata(g_dev, parts_mdata[i], !i); + if (!err) { + err = mdata_crc_check(parts_mdata[i]); + if (!err) + parts_ok[i] = true; + else + log_debug("mdata : %s crc32 failed\n", i ? "secondary" : "primary"); + } } - if (valid_partitions == (PRIMARY_PART | SECONDARY_PART)) { + if (parts_ok[0] && parts_ok[1]) { /* * Before returning, check that both the - * FWU metadata copies are the same. If not, - * populate the secondary partition from the + * FWU metadata copies are the same. + */ + err = memcmp(parts_mdata[0], parts_mdata[1], sizeof(struct fwu_mdata)); + if (!err) + goto ret_mdata; + + /* + * If not, populate the secondary partition from the * primary partition copy. */ - if (!memcmp(&pri_mdata, &secondary_mdata, - sizeof(struct fwu_mdata))) { - ret = 0; - } else { - log_info("Both FWU metadata copies are valid but do not match."); - log_info(" Restoring the secondary partition from the primary\n"); - ret = fwu_write_mdata_partition(dev, &pri_mdata, - mdata_parts[1]); - if (ret) - log_debug("Restoring secondary FWU metadata partition failed\n"); - } - goto out; + log_info("Both FWU metadata copies are valid but do not match."); + log_info(" Restoring the secondary partition from the primary\n"); + parts_ok[1] = false; } - if (!(valid_partitions & BOTH_PARTS)) { - log_info("Both FWU metadata partitions invalid\n"); - ret = -EBADMSG; - goto out; - } + for (int i = 0; i < 2; i++) { + if (parts_ok[i]) + continue; - invalid_partitions = valid_partitions ^ BOTH_PARTS; - ret = fwu_write_mdata_partition(dev, - (invalid_partitions == PRIMARY_PART) ? - &secondary_mdata : &pri_mdata, - (invalid_partitions == PRIMARY_PART) ? - mdata_parts[0] : mdata_parts[1]); + memcpy(parts_mdata[i], parts_mdata[1 - i], sizeof(struct fwu_mdata)); + err = fwu_sync_mdata(parts_mdata[i], i ? SECONDARY_PART : PRIMARY_PART); + if (err) { + log_debug("mdata : %s write failed\n", i ? "secondary" : "primary"); + return err; + } + } - if (ret) - log_debug("Restoring %s FWU metadata partition failed\n", - (invalid_partitions == PRIMARY_PART) ? - "primary" : "secondary"); +ret_mdata: + if (!err && mdata) + memcpy(mdata, parts_mdata[0], sizeof(struct fwu_mdata)); -out: - return ret; + return err; } /** @@ -303,19 +276,14 @@ out: */ int fwu_get_active_index(uint *active_idx) { - int ret; - struct udevice *dev; - struct fwu_mdata mdata = { 0 }; - - ret = fwu_get_dev_mdata(&dev, &mdata); - if (ret) - return ret; + int ret = 0; + struct fwu_mdata *mdata = &g_mdata; /* * Found the FWU metadata partition, now read the active_index * value */ - *active_idx = mdata.active_index; + *active_idx = mdata->active_index; if (*active_idx >= CONFIG_FWU_NUM_BANKS) { log_debug("Active index value read is incorrect\n"); ret = -EINVAL; @@ -336,30 +304,25 @@ int fwu_get_active_index(uint *active_idx) int fwu_set_active_index(uint active_idx) { int ret; - struct udevice *dev; - struct fwu_mdata mdata = { 0 }; + struct fwu_mdata *mdata = &g_mdata; if (active_idx >= CONFIG_FWU_NUM_BANKS) { log_debug("Invalid active index value\n"); return -EINVAL; } - ret = fwu_get_dev_mdata(&dev, &mdata); - if (ret) - return ret; - /* * Update the active index and previous_active_index fields * in the FWU metadata */ - mdata.previous_active_index = mdata.active_index; - mdata.active_index = active_idx; + mdata->previous_active_index = mdata->active_index; + mdata->active_index = active_idx; /* * Now write this updated FWU metadata to both the * FWU metadata partitions */ - ret = fwu_update_mdata(dev, &mdata); + ret = fwu_sync_mdata(mdata, BOTH_PARTS); if (ret) { log_debug("Failed to update FWU metadata partitions\n"); ret = -EIO; @@ -389,15 +352,10 @@ int fwu_get_image_index(u8 *image_index) u8 alt_num; uint update_bank; efi_guid_t *image_guid, image_type_id; - struct udevice *dev; - struct fwu_mdata mdata = { 0 }; + struct fwu_mdata *mdata = &g_mdata; struct fwu_image_entry *img_entry; struct fwu_image_bank_info *img_bank_info; - ret = fwu_get_dev_mdata(&dev, &mdata); - if (ret) - return ret; - ret = fwu_plat_get_update_index(&update_bank); if (ret) { log_debug("Failed to get the FWU update bank\n"); @@ -418,11 +376,11 @@ int fwu_get_image_index(u8 *image_index) */ for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { if (!guidcmp(&image_type_id, - &mdata.img_entry[i].image_type_uuid)) { - img_entry = &mdata.img_entry[i]; + &mdata->img_entry[i].image_type_uuid)) { + img_entry = &mdata->img_entry[i]; img_bank_info = &img_entry->img_bank_info[update_bank]; image_guid = &img_bank_info->image_uuid; - ret = fwu_plat_get_alt_num(dev, image_guid, &alt_num); + ret = fwu_plat_get_alt_num(g_dev, image_guid, &alt_num); if (ret) { log_debug("alt_num not found for partition with GUID %pUs\n", image_guid); @@ -436,8 +394,8 @@ int fwu_get_image_index(u8 *image_index) } } - log_debug("Partition with the image type %pUs not found\n", - &image_type_id); + log_err("Partition with the image type %pUs not found\n", + &image_type_id); out: return ret; @@ -457,26 +415,21 @@ int fwu_revert_boot_index(void) { int ret; u32 cur_active_index; - struct udevice *dev; - struct fwu_mdata mdata = { 0 }; - - ret = fwu_get_dev_mdata(&dev, &mdata); - if (ret) - return ret; + struct fwu_mdata *mdata = &g_mdata; /* * Swap the active index and previous_active_index fields * in the FWU metadata */ - cur_active_index = mdata.active_index; - mdata.active_index = mdata.previous_active_index; - mdata.previous_active_index = cur_active_index; + cur_active_index = mdata->active_index; + mdata->active_index = mdata->previous_active_index; + mdata->previous_active_index = cur_active_index; /* * Now write this updated FWU metadata to both the * FWU metadata partitions */ - ret = fwu_update_mdata(dev, &mdata); + ret = fwu_sync_mdata(mdata, BOTH_PARTS); if (ret) { log_debug("Failed to update FWU metadata partitions\n"); ret = -EIO; @@ -503,16 +456,11 @@ int fwu_revert_boot_index(void) static int fwu_clrset_image_accept(efi_guid_t *img_type_id, u32 bank, u8 action) { int ret, i; - struct udevice *dev; - struct fwu_mdata mdata = { 0 }; + struct fwu_mdata *mdata = &g_mdata; struct fwu_image_entry *img_entry; struct fwu_image_bank_info *img_bank_info; - ret = fwu_get_dev_mdata(&dev, &mdata); - if (ret) - return ret; - - img_entry = &mdata.img_entry[0]; + img_entry = &mdata->img_entry[0]; for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) { img_bank_info = &img_entry[i].img_bank_info[bank]; @@ -521,7 +469,7 @@ static int fwu_clrset_image_accept(efi_guid_t *img_type_id, u32 bank, u8 action) else img_bank_info->accepted = 0; - ret = fwu_update_mdata(dev, &mdata); + ret = fwu_sync_mdata(mdata, BOTH_PARTS); goto out; } } @@ -600,6 +548,24 @@ __weak int fwu_plat_get_update_index(uint *update_idx) } /** + * fwu_plat_get_bootidx() - Get the value of the boot index + * @boot_idx: Boot index value + * + * Get the value of the bank(partition) from which the platform + * has booted. This value is passed to U-Boot from the earlier + * stage bootloader which loads and boots all the relevant + * firmware images + */ +__weak void fwu_plat_get_bootidx(uint *boot_idx) +{ + int ret; + + ret = fwu_get_active_index(boot_idx); + if (ret < 0) + *boot_idx = 0; /* Dummy value */ +} + +/** * fwu_update_checks_pass() - Check if FWU update can be done * * Check if the FWU update can be executed. The updates are @@ -656,8 +622,6 @@ static int fwu_boottime_checks(void *ctx, struct event *event) { int ret; u32 boot_idx, active_idx; - struct udevice *dev; - struct fwu_mdata mdata = { 0 }; /* Don't have boot time checks on sandbox */ if (IS_ENABLED(CONFIG_SANDBOX)) { @@ -665,9 +629,17 @@ static int fwu_boottime_checks(void *ctx, struct event *event) return 0; } - ret = fwu_check_mdata_validity(); - if (ret) - return 0; + ret = uclass_first_device_err(UCLASS_FWU_MDATA, &g_dev); + if (ret) { + log_debug("Cannot find fwu device\n"); + return ret; + } + + ret = fwu_get_mdata(NULL); + if (ret) { + log_debug("Unable to read meta-data\n"); + return ret; + } /* * Get the Boot Index, i.e. the bank from @@ -696,18 +668,12 @@ static int fwu_boottime_checks(void *ctx, struct event *event) ret = fwu_set_active_index(boot_idx); if (!ret) boottime_check = 1; - - return 0; } if (efi_init_obj_list() != EFI_SUCCESS) return 0; - ret = fwu_get_dev_mdata(&dev, &mdata); - if (ret) - return ret; - - in_trial = in_trial_state(&mdata); + in_trial = in_trial_state(&g_mdata); if (!in_trial || (ret = fwu_trial_count_update()) > 0) ret = trial_counter_update(NULL); diff --git a/lib/fwu_updates/fwu_mtd.c b/lib/fwu_updates/fwu_mtd.c new file mode 100644 index 0000000000..69cd3d7001 --- /dev/null +++ b/lib/fwu_updates/fwu_mtd.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include <dm.h> +#include <dfu.h> +#include <fwu.h> +#include <fwu_mdata.h> +#include <log.h> +#include <malloc.h> +#include <mtd.h> +#include <uuid.h> +#include <vsprintf.h> + +#include <dm/ofnode.h> + +struct fwu_mtd_image_info +fwu_mtd_images[CONFIG_FWU_NUM_BANKS * CONFIG_FWU_NUM_IMAGES_PER_BANK]; + +static struct fwu_mtd_image_info *mtd_img_by_uuid(const char *uuidbuf) +{ + int num_images = ARRAY_SIZE(fwu_mtd_images); + + for (int i = 0; i < num_images; i++) + if (!strcmp(uuidbuf, fwu_mtd_images[i].uuidbuf)) + return &fwu_mtd_images[i]; + + return NULL; +} + +int fwu_mtd_get_alt_num(efi_guid_t *image_id, u8 *alt_num, + const char *mtd_dev) +{ + struct fwu_mtd_image_info *mtd_img_info; + char uuidbuf[UUID_STR_LEN + 1]; + fdt_addr_t offset, size = 0; + struct dfu_entity *dfu; + int i, nalt, ret; + + mtd_probe_devices(); + + uuid_bin_to_str(image_id->b, uuidbuf, UUID_STR_FORMAT_STD); + + mtd_img_info = mtd_img_by_uuid(uuidbuf); + if (!mtd_img_info) { + log_err("%s: Not found partition for image %s\n", __func__, uuidbuf); + return -ENOENT; + } + + offset = mtd_img_info->start; + size = mtd_img_info->size; + + ret = dfu_init_env_entities(NULL, NULL); + if (ret) + return -ENOENT; + + nalt = 0; + list_for_each_entry(dfu, &dfu_list, list) + nalt++; + + if (!nalt) { + log_warning("No entities in dfu_alt_info\n"); + dfu_free_entities(); + return -ENOENT; + } + + ret = -ENOENT; + for (i = 0; i < nalt; i++) { + dfu = dfu_get_entity(i); + + /* Only MTD RAW access */ + if (!dfu || dfu->dev_type != DFU_DEV_MTD || + dfu->layout != DFU_RAW_ADDR || + dfu->data.mtd.start != offset || + dfu->data.mtd.size != size) + continue; + + *alt_num = dfu->alt; + ret = 0; + break; + } + + dfu_free_entities(); + + log_debug("%s: %s -> %d\n", __func__, uuidbuf, *alt_num); + return ret; +} + +/** + * fwu_plat_get_alt_num() - Get the DFU Alt Num for the image from the platform + * @dev: FWU device + * @image_id: Image GUID for which DFU alt number needs to be retrieved + * @alt_num: Pointer to the alt_num + * + * Get the DFU alt number from the platform for the image specified by the + * image GUID. + * + * Note: This is a weak function and platforms can override this with + * their own implementation for obtaining the alt number value. + * + * Return: 0 if OK, -ve on error + */ +__weak int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_id, + u8 *alt_num) +{ + return fwu_mtd_get_alt_num(image_id, alt_num, "nor1"); +} + +static int gen_image_alt_info(char *buf, size_t len, int sidx, + struct fwu_image_entry *img, struct mtd_info *mtd) +{ + char *p = buf, *end = buf + len; + int i; + + p += snprintf(p, end - p, "mtd %s", mtd->name); + if (end < p) { + log_err("%s:%d Run out of buffer\n", __func__, __LINE__); + return -E2BIG; + } + + /* + * List the image banks in the FWU mdata and search the corresponding + * partition based on partition's uuid. + */ + for (i = 0; i < CONFIG_FWU_NUM_BANKS; i++) { + struct fwu_mtd_image_info *mtd_img_info; + struct fwu_image_bank_info *bank; + char uuidbuf[UUID_STR_LEN + 1]; + u32 offset, size; + + /* Query a partition by image UUID */ + bank = &img->img_bank_info[i]; + uuid_bin_to_str(bank->image_uuid.b, uuidbuf, UUID_STR_FORMAT_STD); + + mtd_img_info = mtd_img_by_uuid(uuidbuf); + if (!mtd_img_info) { + log_err("%s: Not found partition for image %s\n", __func__, uuidbuf); + break; + } + + offset = mtd_img_info->start; + size = mtd_img_info->size; + + p += snprintf(p, end - p, "%sbank%d raw %x %x", + i == 0 ? "=" : ";", i, offset, size); + if (end < p) { + log_err("%s:%d Run out of buffer\n", __func__, __LINE__); + return -E2BIG; + } + } + + if (i == CONFIG_FWU_NUM_BANKS) + return 0; + + return -ENOENT; +} + +int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd) +{ + struct fwu_mdata mdata; + int i, l, ret; + + ret = fwu_get_mdata(&mdata); + if (ret < 0) { + log_err("Failed to get the FWU mdata.\n"); + return ret; + } + + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { + ret = gen_image_alt_info(buf, len, i * CONFIG_FWU_NUM_BANKS, + &mdata.img_entry[i], mtd); + if (ret) + break; + + l = strlen(buf); + /* Replace the last ';' with '&' if there is another image. */ + if (i != CONFIG_FWU_NUM_IMAGES_PER_BANK - 1 && l) { + buf[l] = '&'; + buf++; + } + len -= l; + buf += l; + } + + return ret; +} diff --git a/lib/image-sparse.c b/lib/image-sparse.c index 5ec0f94ab3..8f8a67e158 100644 --- a/lib/image-sparse.c +++ b/lib/image-sparse.c @@ -55,7 +55,8 @@ static lbaint_t write_sparse_chunk_raw(struct sparse_storage *info, void *data, char *response) { - lbaint_t n = blkcnt, write_blks, blks = 0, aligned_buf_blks = 100; + lbaint_t n = blkcnt, write_blks, blks = 0; + lbaint_t aligned_buf_blks = FASTBOOT_MAX_BLK_WRITE; uint32_t *aligned_buf = NULL; if (CONFIG_IS_ENABLED(SYS_DCACHE_OFF)) { diff --git a/lib/lzma/LzmaDec.c b/lib/lzma/LzmaDec.c index 341149f766..a90b35c6a9 100644 --- a/lib/lzma/LzmaDec.c +++ b/lib/lzma/LzmaDec.c @@ -152,8 +152,7 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte const Byte *buf = p->buf; UInt32 range = p->range; UInt32 code = p->code; - - schedule(); + unsigned int loop = 0; do { @@ -162,6 +161,9 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte unsigned ttt; unsigned posState = processedPos & pbMask; + if (!(loop++ & 1023)) + schedule(); + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; IF_BIT_0(prob) { @@ -177,8 +179,6 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte state -= (state < 4) ? state : 3; symbol = 1; - schedule(); - do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); } else @@ -188,8 +188,6 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte state -= (state < 10) ? 3 : 6; symbol = 1; - schedule(); - do { unsigned bit; @@ -321,8 +319,6 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte UInt32 mask = 1; unsigned i = 1; - schedule(); - do { GET_BIT2(prob + i, i, ; , distance |= mask); @@ -335,8 +331,6 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte { numDirectBits -= kNumAlignBits; - schedule(); - do { NORMALIZE @@ -409,8 +403,6 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte const Byte *lim = dest + curLen; dicPos += curLen; - schedule(); - do *(dest) = (Byte)*(dest + src); while (++dest != lim); @@ -418,8 +410,6 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte else { - schedule(); - do { dic[dicPos++] = dic[pos]; diff --git a/lib/of_live.c b/lib/of_live.c index 1b5964d09a..25f7af6106 100644 --- a/lib/of_live.c +++ b/lib/of_live.c @@ -287,9 +287,12 @@ int unflatten_device_tree(const void *blob, struct device_node **mynodes) debug(" size is %lx, allocating...\n", size); /* Allocate memory for the expanded device tree */ - mem = malloc(size + 4); + mem = memalign(__alignof__(struct device_node), size + 4); memset(mem, '\0', size); + /* Set up value for dm_test_livetree_align() */ + *(u32 *)mem = BAD_OF_ROOT; + *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef); debug(" unflattening %p...\n", mem); @@ -327,3 +330,9 @@ int of_live_build(const void *fdt_blob, struct device_node **rootp) return ret; } + +void of_live_free(struct device_node *root) +{ + /* the tree is stored as a contiguous block of memory */ + free(root); +} diff --git a/lib/uuid.c b/lib/uuid.c index 96e1af3c8b..d0187007d0 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2011 Calxeda, Inc. + * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> */ #include <common.h> @@ -255,7 +259,7 @@ static const struct { EFI_CERT_TYPE_PKCS7_GUID, }, #endif -#ifdef CONFIG_EFI +#if defined(CONFIG_CMD_EFIDEBUG) || defined(CONFIG_EFI) { "EFI_LZMA_COMPRESSED", EFI_LZMA_COMPRESSED }, { "EFI_DXE_SERVICES", EFI_DXE_SERVICES }, { "EFI_HOB_LIST", EFI_HOB_LIST }, @@ -354,6 +358,50 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; } +/** + * uuid_str_to_le_bin() - Convert string UUID to little endian binary data. + * @uuid_str: pointer to UUID string + * @uuid_bin: pointer to allocated array for little endian output [16B] + * + * UUID string is 36 characters (36 bytes): + * + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * + * where x is a hexadecimal character. Fields are separated by '-'s. + * When converting to a little endian binary UUID, the string fields are reversed. + * + * Return: + * + * uuid_bin filled with little endian UUID data + * On success 0 is returned. Otherwise, failure code. + */ +int uuid_str_to_le_bin(const char *uuid_str, unsigned char *uuid_bin) +{ + u16 tmp16; + u32 tmp32; + u64 tmp64; + + if (!uuid_str_valid(uuid_str) || !uuid_bin) + return -EINVAL; + + tmp32 = cpu_to_le32(hextoul(uuid_str, NULL)); + memcpy(uuid_bin, &tmp32, 4); + + tmp16 = cpu_to_le16(hextoul(uuid_str + 9, NULL)); + memcpy(uuid_bin + 4, &tmp16, 2); + + tmp16 = cpu_to_le16(hextoul(uuid_str + 14, NULL)); + memcpy(uuid_bin + 6, &tmp16, 2); + + tmp16 = cpu_to_le16(hextoul(uuid_str + 19, NULL)); + memcpy(uuid_bin + 8, &tmp16, 2); + + tmp64 = cpu_to_le64(simple_strtoull(uuid_str + 24, NULL, 16)); + memcpy(uuid_bin + 10, &tmp64, 6); + + return 0; +} + /* * uuid_bin_to_str() - convert big endian binary data to string UUID or GUID. * diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e87503e41a..e14c6ca9f9 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -680,8 +680,10 @@ repeat: break; case 'd': - if (fmt[1] == 'E') + if (fmt[1] == 'E') { flags |= ERRSTR; + fmt++; + } /* fallthrough */ case 'i': flags |= SIGN; @@ -725,7 +727,6 @@ repeat: ADDCH(str, ' '); for (p = errno_str(num); *p; p++) ADDCH(str, *p); - fmt++; } } diff --git a/lib/zlib/inflate.c b/lib/zlib/inflate.c index 30dfe15599..8f767b7b9d 100644 --- a/lib/zlib/inflate.c +++ b/lib/zlib/inflate.c @@ -455,8 +455,9 @@ int ZEXPORT inflate(z_streamp strm, int flush) if (copy > have) copy = have; if (copy) { if (state->head != Z_NULL && - state->head->extra != Z_NULL) { - len = state->head->extra_len - state->length; + state->head->extra != Z_NULL && + (len = state->head->extra_len - state->length) < + state->head->extra_max) { zmemcpy(state->head->extra + len, next, len + copy > state->head->extra_max ? state->head->extra_max - len : copy); |