aboutsummaryrefslogtreecommitdiff
path: root/lib/efi_loader
diff options
context:
space:
mode:
Diffstat (limited to 'lib/efi_loader')
-rw-r--r--lib/efi_loader/Kconfig44
-rw-r--r--lib/efi_loader/Makefile3
-rw-r--r--lib/efi_loader/efi_acpi.c33
-rw-r--r--lib/efi_loader/efi_bootmgr.c385
-rw-r--r--lib/efi_loader/efi_boottime.c76
-rw-r--r--lib/efi_loader/efi_capsule.c20
-rw-r--r--lib/efi_loader/efi_device_path.c203
-rw-r--r--lib/efi_loader/efi_disk.c22
-rw-r--r--lib/efi_loader/efi_file.c14
-rw-r--r--lib/efi_loader/efi_firmware.c280
-rw-r--r--lib/efi_loader/efi_helper.c25
-rw-r--r--lib/efi_loader/efi_image_loader.c5
-rw-r--r--lib/efi_loader/efi_load_options.c2
-rw-r--r--lib/efi_loader/efi_memory.c19
-rw-r--r--lib/efi_loader/efi_tcg2.c14
-rw-r--r--lib/efi_loader/efi_var_mem.c4
-rw-r--r--lib/efi_loader/efi_variable_tee.c270
17 files changed, 1163 insertions, 256 deletions
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, &regs, &wincerts,
+ if (!efi_image_parse(new_efi, new_efi_size, &regs, &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);
/*