efi_loader: define internal implementations of install/uninstallmultiple

A following patch is cleaning up the core EFI code trying to remove
sequences of efi_create_handle, efi_add_protocol.

Although this works fine there's a problem with the latter since it is
usually combined with efi_delete_handle() which blindly removes all
protocols on a handle and deletes the handle.  We should try to adhere to
the EFI spec which only deletes a handle if the last instance of a protocol
has been removed.  Another problem is that efi_delete_handle() never checks
for opened protocols,  but the EFI spec defines that the caller is
responsible for ensuring that there are no references to a protocol
interface that is going to be removed.

So let's fix this by replacing all callsites of
efi_create_handle(), efi_add_protocol() , efi_delete_handle() with
Install/UninstallMultipleProtocol.

In order to do that redefine functions that can be used by the U-Boot
proper internally and add '_ext' variants that will be used from the
EFI API

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
This commit is contained in:
Ilias Apalodimas 2022-10-06 16:08:46 +03:00 committed by Heinrich Schuchardt
parent 1af680d5bc
commit 05c4c9e21a
8 changed files with 248 additions and 136 deletions

View file

@ -37,12 +37,14 @@
#define EFIAPI __attribute__((ms_abi)) #define EFIAPI __attribute__((ms_abi))
#define efi_va_list __builtin_ms_va_list #define efi_va_list __builtin_ms_va_list
#define efi_va_start __builtin_ms_va_start #define efi_va_start __builtin_ms_va_start
#define efi_va_copy __builtin_ms_va_copy
#define efi_va_arg __builtin_va_arg #define efi_va_arg __builtin_va_arg
#define efi_va_end __builtin_ms_va_end #define efi_va_end __builtin_ms_va_end
#else #else
#define EFIAPI asmlinkage #define EFIAPI asmlinkage
#define efi_va_list va_list #define efi_va_list va_list
#define efi_va_start va_start #define efi_va_start va_start
#define efi_va_copy va_copy
#define efi_va_arg va_arg #define efi_va_arg va_arg
#define efi_va_end va_end #define efi_va_end va_end
#endif /* __x86_64__ */ #endif /* __x86_64__ */

View file

@ -654,8 +654,10 @@ efi_status_t efi_remove_protocol(const efi_handle_t handle,
/* Delete all protocols from a handle */ /* Delete all protocols from a handle */
efi_status_t efi_remove_all_protocols(const efi_handle_t handle); efi_status_t efi_remove_all_protocols(const efi_handle_t handle);
/* Install multiple protocol interfaces */ /* Install multiple protocol interfaces */
efi_status_t EFIAPI efi_install_multiple_protocol_interfaces efi_status_t EFIAPI
(efi_handle_t *handle, ...); efi_install_multiple_protocol_interfaces(efi_handle_t *handle, ...);
efi_status_t EFIAPI
efi_uninstall_multiple_protocol_interfaces(efi_handle_t handle, ...);
/* Get handles that support a given protocol */ /* Get handles that support a given protocol */
efi_status_t EFIAPI efi_locate_handle_buffer( efi_status_t EFIAPI efi_locate_handle_buffer(
enum efi_locate_search_type search_type, enum efi_locate_search_type search_type,

View file

@ -2589,6 +2589,75 @@ found:
return EFI_EXIT(EFI_SUCCESS); return EFI_EXIT(EFI_SUCCESS);
} }
/**
* efi_install_multiple_protocol_interfaces_int() - Install multiple protocol
* interfaces
* @handle: handle on which the protocol interfaces shall be installed
* @argptr: va_list of args
*
* Core functionality of efi_install_multiple_protocol_interfaces
* Must not be called directly
*
* Return: status code
*/
static efi_status_t EFIAPI
efi_install_multiple_protocol_interfaces_int(efi_handle_t *handle,
efi_va_list argptr)
{
const efi_guid_t *protocol;
void *protocol_interface;
efi_handle_t old_handle;
efi_status_t ret = EFI_SUCCESS;
int i = 0;
efi_va_list argptr_copy;
if (!handle)
return EFI_INVALID_PARAMETER;
efi_va_copy(argptr_copy, argptr);
for (;;) {
protocol = efi_va_arg(argptr, efi_guid_t*);
if (!protocol)
break;
protocol_interface = efi_va_arg(argptr, void*);
/* Check that a device path has not been installed before */
if (!guidcmp(protocol, &efi_guid_device_path)) {
struct efi_device_path *dp = protocol_interface;
ret = EFI_CALL(efi_locate_device_path(protocol, &dp,
&old_handle));
if (ret == EFI_SUCCESS &&
dp->type == DEVICE_PATH_TYPE_END) {
EFI_PRINT("Path %pD already installed\n",
protocol_interface);
ret = EFI_ALREADY_STARTED;
break;
}
}
ret = EFI_CALL(efi_install_protocol_interface(handle, protocol,
EFI_NATIVE_INTERFACE,
protocol_interface));
if (ret != EFI_SUCCESS)
break;
i++;
}
if (ret == EFI_SUCCESS)
goto out;
/* If an error occurred undo all changes. */
for (; i; --i) {
protocol = efi_va_arg(argptr_copy, efi_guid_t*);
protocol_interface = efi_va_arg(argptr_copy, void*);
EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol,
protocol_interface));
}
out:
efi_va_end(argptr_copy);
return ret;
}
/** /**
* efi_install_multiple_protocol_interfaces() - Install multiple protocol * efi_install_multiple_protocol_interfaces() - Install multiple protocol
* interfaces * interfaces
@ -2596,6 +2665,32 @@ found:
* @...: NULL terminated argument list with pairs of protocol GUIDS and * @...: NULL terminated argument list with pairs of protocol GUIDS and
* interfaces * interfaces
* *
*
* This is the function for internal usage in U-Boot. For the API function
* implementing the InstallMultipleProtocol service see
* efi_install_multiple_protocol_interfaces_ext()
*
* Return: status code
*/
efi_status_t EFIAPI
efi_install_multiple_protocol_interfaces(efi_handle_t *handle, ...)
{
efi_status_t ret;
efi_va_list argptr;
efi_va_start(argptr, handle);
ret = efi_install_multiple_protocol_interfaces_int(handle, argptr);
efi_va_end(argptr);
return ret;
}
/**
* efi_install_multiple_protocol_interfaces_ext() - Install multiple protocol
* interfaces
* @handle: handle on which the protocol interfaces shall be installed
* @...: NULL terminated argument list with pairs of protocol GUIDS and
* interfaces
*
* This function implements the MultipleProtocolInterfaces service. * This function implements the MultipleProtocolInterfaces service.
* *
* See the Unified Extensible Firmware Interface (UEFI) specification for * See the Unified Extensible Firmware Interface (UEFI) specification for
@ -2603,64 +2698,84 @@ found:
* *
* Return: status code * Return: status code
*/ */
efi_status_t EFIAPI efi_install_multiple_protocol_interfaces static efi_status_t EFIAPI
(efi_handle_t *handle, ...) efi_install_multiple_protocol_interfaces_ext(efi_handle_t *handle, ...)
{ {
EFI_ENTRY("%p", handle); EFI_ENTRY("%p", handle);
efi_status_t ret;
efi_va_list argptr; efi_va_list argptr;
const efi_guid_t *protocol;
void *protocol_interface;
efi_handle_t old_handle;
efi_status_t r = EFI_SUCCESS;
int i = 0;
if (!handle)
return EFI_EXIT(EFI_INVALID_PARAMETER);
efi_va_start(argptr, handle); efi_va_start(argptr, handle);
ret = efi_install_multiple_protocol_interfaces_int(handle, argptr);
efi_va_end(argptr);
return EFI_EXIT(ret);
}
/**
* efi_uninstall_multiple_protocol_interfaces_int() - wrapper for uninstall
* multiple protocol
* interfaces
* @handle: handle from which the protocol interfaces shall be removed
* @argptr: va_list of args
*
* Core functionality of efi_uninstall_multiple_protocol_interfaces
* Must not be called directly
*
* Return: status code
*/
static efi_status_t EFIAPI
efi_uninstall_multiple_protocol_interfaces_int(efi_handle_t handle,
efi_va_list argptr)
{
const efi_guid_t *protocol;
void *protocol_interface;
efi_status_t ret;
size_t i = 0;
efi_va_list argptr_copy;
if (!handle)
return EFI_INVALID_PARAMETER;
efi_va_copy(argptr_copy, argptr);
for (;;) { for (;;) {
protocol = efi_va_arg(argptr, efi_guid_t*); protocol = efi_va_arg(argptr, efi_guid_t*);
if (!protocol) if (!protocol)
break; break;
protocol_interface = efi_va_arg(argptr, void*); protocol_interface = efi_va_arg(argptr, void*);
/* Check that a device path has not been installed before */ ret = efi_uninstall_protocol(handle, protocol,
if (!guidcmp(protocol, &efi_guid_device_path)) {
struct efi_device_path *dp = protocol_interface;
r = EFI_CALL(efi_locate_device_path(protocol, &dp,
&old_handle));
if (r == EFI_SUCCESS &&
dp->type == DEVICE_PATH_TYPE_END) {
EFI_PRINT("Path %pD already installed\n",
protocol_interface); protocol_interface);
r = EFI_ALREADY_STARTED; if (ret != EFI_SUCCESS)
break;
}
}
r = EFI_CALL(efi_install_protocol_interface(
handle, protocol,
EFI_NATIVE_INTERFACE,
protocol_interface));
if (r != EFI_SUCCESS)
break; break;
i++; i++;
} }
efi_va_end(argptr); if (ret == EFI_SUCCESS) {
if (r == EFI_SUCCESS) /* If the last protocol has been removed, delete the handle. */
return EFI_EXIT(r); if (list_empty(&handle->protocols)) {
list_del(&handle->link);
free(handle);
}
goto out;
}
/* If an error occurred undo all changes. */ /* If an error occurred undo all changes. */
efi_va_start(argptr, handle);
for (; i; --i) { for (; i; --i) {
protocol = efi_va_arg(argptr, efi_guid_t*); protocol = efi_va_arg(argptr_copy, efi_guid_t*);
protocol_interface = efi_va_arg(argptr, void*); protocol_interface = efi_va_arg(argptr_copy, void*);
EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol, EFI_CALL(efi_install_protocol_interface(&handle, protocol,
EFI_NATIVE_INTERFACE,
protocol_interface)); protocol_interface));
} }
efi_va_end(argptr); /*
* If any errors are generated while the protocol interfaces are being
* uninstalled, then the protocols uninstalled prior to the error will
* be reinstalled using InstallProtocolInterface() and the status code
* EFI_INVALID_PARAMETER is returned.
*/
ret = EFI_INVALID_PARAMETER;
return EFI_EXIT(r); out:
efi_va_end(argptr_copy);
return ret;
} }
/** /**
@ -2672,60 +2787,49 @@ efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
* *
* This function implements the UninstallMultipleProtocolInterfaces service. * This function implements the UninstallMultipleProtocolInterfaces service.
* *
* This is the function for internal usage in U-Boot. For the API function
* implementing the UninstallMultipleProtocolInterfaces service see
* efi_uninstall_multiple_protocol_interfaces_ext()
*
* Return: status code
*/
efi_status_t EFIAPI
efi_uninstall_multiple_protocol_interfaces(efi_handle_t handle, ...)
{
efi_status_t ret;
efi_va_list argptr;
efi_va_start(argptr, handle);
ret = efi_uninstall_multiple_protocol_interfaces_int(handle, argptr);
efi_va_end(argptr);
return ret;
}
/**
* efi_uninstall_multiple_protocol_interfaces_ext() - uninstall multiple protocol
* interfaces
* @handle: handle from which the protocol interfaces shall be removed
* @...: NULL terminated argument list with pairs of protocol GUIDS and
* interfaces
*
* This function implements the UninstallMultipleProtocolInterfaces service.
*
* See the Unified Extensible Firmware Interface (UEFI) specification for * See the Unified Extensible Firmware Interface (UEFI) specification for
* details. * details.
* *
* Return: status code * Return: status code
*/ */
static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( static efi_status_t EFIAPI
efi_handle_t handle, ...) efi_uninstall_multiple_protocol_interfaces_ext(efi_handle_t handle, ...)
{ {
EFI_ENTRY("%p", handle); EFI_ENTRY("%p", handle);
efi_status_t ret;
efi_va_list argptr; efi_va_list argptr;
const efi_guid_t *protocol;
void *protocol_interface;
efi_status_t r = EFI_SUCCESS;
size_t i = 0;
if (!handle)
return EFI_EXIT(EFI_INVALID_PARAMETER);
efi_va_start(argptr, handle); efi_va_start(argptr, handle);
for (;;) { ret = efi_uninstall_multiple_protocol_interfaces_int(handle, argptr);
protocol = efi_va_arg(argptr, efi_guid_t*);
if (!protocol)
break;
protocol_interface = efi_va_arg(argptr, void*);
r = efi_uninstall_protocol(handle, protocol,
protocol_interface);
if (r != EFI_SUCCESS)
break;
i++;
}
efi_va_end(argptr); efi_va_end(argptr);
if (r == EFI_SUCCESS) { return EFI_EXIT(ret);
/* If the last protocol has been removed, delete the handle. */
if (list_empty(&handle->protocols)) {
list_del(&handle->link);
free(handle);
}
return EFI_EXIT(r);
}
/* If an error occurred undo all changes. */
efi_va_start(argptr, handle);
for (; i; --i) {
protocol = efi_va_arg(argptr, efi_guid_t*);
protocol_interface = efi_va_arg(argptr, void*);
EFI_CALL(efi_install_protocol_interface(&handle, protocol,
EFI_NATIVE_INTERFACE,
protocol_interface));
}
efi_va_end(argptr);
/* In case of an error always return EFI_INVALID_PARAMETER */
return EFI_EXIT(EFI_INVALID_PARAMETER);
} }
/** /**
@ -3785,9 +3889,9 @@ static struct efi_boot_services efi_boot_services = {
.locate_handle_buffer = efi_locate_handle_buffer, .locate_handle_buffer = efi_locate_handle_buffer,
.locate_protocol = efi_locate_protocol, .locate_protocol = efi_locate_protocol,
.install_multiple_protocol_interfaces = .install_multiple_protocol_interfaces =
efi_install_multiple_protocol_interfaces, efi_install_multiple_protocol_interfaces_ext,
.uninstall_multiple_protocol_interfaces = .uninstall_multiple_protocol_interfaces =
efi_uninstall_multiple_protocol_interfaces, efi_uninstall_multiple_protocol_interfaces_ext,
.calculate_crc32 = efi_calculate_crc32, .calculate_crc32 = efi_calculate_crc32,
.copy_mem = efi_copy_mem, .copy_mem = efi_copy_mem,
.set_mem = efi_set_mem, .set_mem = efi_set_mem,

View file

@ -636,17 +636,18 @@ efi_status_t __weak efi_load_capsule_drivers(void)
if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_FIT)) { if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_FIT)) {
handle = NULL; handle = NULL;
ret = EFI_CALL(efi_install_multiple_protocol_interfaces( ret = efi_install_multiple_protocol_interfaces(&handle,
&handle, &efi_guid_firmware_management_protocol, &efi_guid_firmware_management_protocol,
&efi_fmp_fit, NULL)); &efi_fmp_fit,
NULL);
} }
if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_RAW)) { if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_RAW)) {
handle = NULL; handle = NULL;
ret = EFI_CALL(efi_install_multiple_protocol_interfaces( ret = efi_install_multiple_protocol_interfaces(&handle,
&handle,
&efi_guid_firmware_management_protocol, &efi_guid_firmware_management_protocol,
&efi_fmp_raw, NULL)); &efi_fmp_raw,
NULL);
} }
return ret; return ret;

View file

@ -1278,12 +1278,14 @@ efi_status_t efi_console_register(void)
struct efi_device_path *dp; struct efi_device_path *dp;
/* Install protocols on root node */ /* Install protocols on root node */
r = EFI_CALL(efi_install_multiple_protocol_interfaces r = efi_install_multiple_protocol_interfaces(&efi_root,
(&efi_root, &efi_guid_text_output_protocol,
&efi_guid_text_output_protocol, &efi_con_out, &efi_con_out,
&efi_guid_text_input_protocol, &efi_con_in, &efi_guid_text_input_protocol,
&efi_guid_text_input_ex_protocol, &efi_con_in_ex, &efi_con_in,
NULL)); &efi_guid_text_input_ex_protocol,
&efi_con_in_ex,
NULL);
/* Create console node and install device path protocols */ /* Create console node and install device path protocols */
if (CONFIG_IS_ENABLED(DM_SERIAL)) { if (CONFIG_IS_ENABLED(DM_SERIAL)) {

View file

@ -454,10 +454,12 @@ static efi_status_t efi_disk_add_dev(
* in this case. * in this case.
*/ */
handle = &diskobj->header; handle = &diskobj->header;
ret = EFI_CALL(efi_install_multiple_protocol_interfaces( ret = efi_install_multiple_protocol_interfaces(&handle,
&handle, &efi_guid_device_path, diskobj->dp, &efi_guid_device_path,
&efi_block_io_guid, &diskobj->ops, diskobj->dp,
guid, NULL, NULL)); &efi_block_io_guid,
&diskobj->ops, guid,
NULL, NULL);
if (ret != EFI_SUCCESS) if (ret != EFI_SUCCESS)
goto error; goto error;

View file

@ -208,14 +208,13 @@ efi_status_t efi_initrd_register(void)
if (ret != EFI_SUCCESS) if (ret != EFI_SUCCESS)
return ret; return ret;
ret = EFI_CALL(efi_install_multiple_protocol_interfaces ret = efi_install_multiple_protocol_interfaces(&efi_initrd_handle,
(&efi_initrd_handle,
/* initramfs */ /* initramfs */
&efi_guid_device_path, &dp_lf2_handle, &efi_guid_device_path, &dp_lf2_handle,
/* LOAD_FILE2 */ /* LOAD_FILE2 */
&efi_guid_load_file2_protocol, &efi_guid_load_file2_protocol,
(void *)&efi_lf2_protocol, (void *)&efi_lf2_protocol,
NULL)); NULL);
return ret; return ret;
} }

View file

@ -49,38 +49,38 @@ efi_status_t efi_root_node_register(void)
dp->end.length = sizeof(struct efi_device_path); dp->end.length = sizeof(struct efi_device_path);
/* Create root node and install protocols */ /* Create root node and install protocols */
ret = EFI_CALL(efi_install_multiple_protocol_interfaces ret = efi_install_multiple_protocol_interfaces
(&efi_root, (&efi_root,
/* Device path protocol */ /* Device path protocol */
&efi_guid_device_path, dp, &efi_guid_device_path, dp,
#if CONFIG_IS_ENABLED(EFI_DEVICE_PATH_TO_TEXT) #if CONFIG_IS_ENABLED(EFI_DEVICE_PATH_TO_TEXT)
/* Device path to text protocol */ /* Device path to text protocol */
&efi_guid_device_path_to_text_protocol, &efi_guid_device_path_to_text_protocol,
(void *)&efi_device_path_to_text, &efi_device_path_to_text,
#endif #endif
#ifdef CONFIG_EFI_DEVICE_PATH_UTIL #if CONFIG_IS_ENABLED(EFI_DEVICE_PATH_UTIL)
/* Device path utilities protocol */ /* Device path utilities protocol */
&efi_guid_device_path_utilities_protocol, &efi_guid_device_path_utilities_protocol,
(void *)&efi_device_path_utilities, &efi_device_path_utilities,
#endif #endif
#ifdef CONFIG_EFI_DT_FIXUP #if CONFIG_IS_ENABLED(EFI_DT_FIXUP)
/* Device-tree fix-up protocol */ /* Device-tree fix-up protocol */
&efi_guid_dt_fixup_protocol, &efi_guid_dt_fixup_protocol,
(void *)&efi_dt_fixup_prot, &efi_dt_fixup_prot,
#endif #endif
#if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL2) #if CONFIG_IS_ENABLED(EFI_UNICODE_COLLATION_PROTOCOL2)
&efi_guid_unicode_collation_protocol2, &efi_guid_unicode_collation_protocol2,
(void *)&efi_unicode_collation_protocol2, &efi_unicode_collation_protocol2,
#endif #endif
#if CONFIG_IS_ENABLED(EFI_LOADER_HII) #if CONFIG_IS_ENABLED(EFI_LOADER_HII)
/* HII string protocol */ /* HII string protocol */
&efi_guid_hii_string_protocol, &efi_guid_hii_string_protocol,
(void *)&efi_hii_string, &efi_hii_string,
/* HII database protocol */ /* HII database protocol */
&efi_guid_hii_database_protocol, &efi_guid_hii_database_protocol,
(void *)&efi_hii_database, &efi_hii_database,
#endif #endif
NULL)); NULL);
efi_root->type = EFI_OBJECT_TYPE_U_BOOT_FIRMWARE; efi_root->type = EFI_OBJECT_TYPE_U_BOOT_FIRMWARE;
return ret; return ret;
} }