[PATCH] efi_loader: delete handle from events when a protocol is uninstalled

Heinrich Schuchardt xypron.glpk at gmx.de
Thu Aug 24 14:10:46 CEST 2023


On 24.08.23 13:55, Ilias Apalodimas wrote:
> When a notification event is registered for a protocol the handle of the
> protocol is added in our event notification list.  When all the protocols
> of the handle are uninstalled we delete the handle but we do not remove
> it from the event notification list.
>
> Clean up the protocol removal functions and add a wrapper which
> - Removes the to-be deleted handle from any lists it participates
> - Remove the handle if no more protocols are present
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
> ---
>   lib/efi_loader/efi_boottime.c | 80 ++++++++++++++++++++++++-----------
>   1 file changed, 56 insertions(+), 24 deletions(-)
>
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 0e89c8505b11..96950790ba2a 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -61,7 +61,7 @@ static volatile gd_t *efi_gd, *app_gd;
>
>   static efi_status_t efi_uninstall_protocol
>   			(efi_handle_t handle, const efi_guid_t *protocol,
> -			 void *protocol_interface);
> +			 void *protocol_interface, bool preserve);
>
>   /* 1 if inside U-Boot code, 0 if inside EFI payload code */
>   static int entry_count = 1;
> @@ -207,6 +207,36 @@ static bool efi_event_is_queued(struct efi_event *event)
>   	return !!event->queue_link.next;
>   }
>
> +/**
> + * efi_purge_handle() - Clean the deleted handle from the various lists
> + * @handle: handle to remove
> + *
> + * Return: status code
> + */
> +static efi_status_t efi_purge_handle(efi_handle_t handle)
> +{
> +	struct efi_register_notify_event *item, *next;
> +
> +	if (!list_empty(&handle->protocols))
> +		return EFI_ACCESS_DENIED;
> +	/* The handle is about to be freed. Remove it from events */
> +	list_for_each_entry_safe(item, next, &efi_register_notify_events, link) {

You are not deleting from efi_register_notify_events. Why do you use the
safe version of the iterator?

> +		struct efi_protocol_notification *hitem, *hnext;
> +
> +		list_for_each_entry_safe(hitem, hnext, &item->handles, link) {
> +			if (handle == hitem->handle) {
> +				list_del(&hitem->link);
> +				free(hitem);
> +			}
> +		}
> +	}
> +	/* The last protocol has been removed, delete the handle. */
> +	list_del(&handle->link);
> +	free(handle);
> +
> +	return EFI_SUCCESS;
> +}
> +
>   /**
>    * efi_process_event_queue() - process event queue
>    */
> @@ -615,7 +645,7 @@ static efi_status_t efi_remove_all_protocols(const efi_handle_t handle)
>   		efi_status_t ret;
>
>   		ret = efi_uninstall_protocol(handle, &protocol->guid,
> -					     protocol->protocol_interface);
> +					     protocol->protocol_interface, true);
>   		if (ret != EFI_SUCCESS)
>   			return ret;
>   	}
> @@ -639,10 +669,7 @@ efi_status_t efi_delete_handle(efi_handle_t handle)
>   		return ret;
>   	}
>
> -	list_del(&handle->link);
> -	free(handle);
> -
> -	return ret;
> +	return efi_purge_handle(handle);
>   }
>
>   /**
> @@ -1356,6 +1383,8 @@ reconnect:
>    * @handle:             handle from which the protocol shall be removed
>    * @protocol:           GUID of the protocol to be removed
>    * @protocol_interface: interface to be removed
> + * @preserve:		preserve or delete the handle and remove it from any
> + *			list it participates if no protocols remain
>    *
>    * This function DOES NOT delete a handle without installed protocol.
>    *
> @@ -1363,7 +1392,7 @@ reconnect:
>    */
>   static efi_status_t efi_uninstall_protocol
>   			(efi_handle_t handle, const efi_guid_t *protocol,
> -			 void *protocol_interface)
> +			 void *protocol_interface, bool preserve)
>   {
>   	struct efi_handler *handler;
>   	struct efi_open_protocol_info_item *item;
> @@ -1397,6 +1426,14 @@ static efi_status_t efi_uninstall_protocol
>   		goto out;
>   	}
>   	r = efi_remove_protocol(handle, protocol, protocol_interface);
> +	if (r != EFI_SUCCESS)
> +		return r;
> +	/*
> +	 * We don't care about the return value here since the
> +	 * handle might have more protocols installed
> +	 */
> +	if (!preserve)
> +		efi_purge_handle(handle);
>   out:
>   	return r;
>   }
> @@ -1422,15 +1459,10 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface
>
>   	EFI_ENTRY("%p, %pUs, %p", handle, protocol, protocol_interface);
>
> -	ret = efi_uninstall_protocol(handle, protocol, protocol_interface);
> +	ret = efi_uninstall_protocol(handle, protocol, protocol_interface, false);
>   	if (ret != EFI_SUCCESS)
>   		goto out;
>
> -	/* If the last protocol has been removed, delete the handle. */
> -	if (list_empty(&handle->protocols)) {
> -		list_del(&handle->link);
> -		free(handle);
> -	}
>   out:
>   	return EFI_EXIT(ret);
>   }
> @@ -2785,7 +2817,7 @@ static efi_status_t EFIAPI
>   efi_uninstall_multiple_protocol_interfaces_int(efi_handle_t handle,
>   					       efi_va_list argptr)
>   {
> -	const efi_guid_t *protocol;
> +	const efi_guid_t *protocol, *next_protocol;
>   	void *protocol_interface;
>   	efi_status_t ret = EFI_SUCCESS;
>   	size_t i = 0;
> @@ -2795,25 +2827,25 @@ efi_uninstall_multiple_protocol_interfaces_int(efi_handle_t handle,
>   		return EFI_INVALID_PARAMETER;
>
>   	efi_va_copy(argptr_copy, argptr);
> +	protocol = efi_va_arg(argptr, efi_guid_t*);
>   	for (;;) {
> -		protocol = efi_va_arg(argptr, efi_guid_t*);
> +		bool preserve = true;
> +
>   		if (!protocol)
>   			break;
>   		protocol_interface = efi_va_arg(argptr, void*);
> +		next_protocol = efi_va_arg(argptr, efi_guid_t*);
> +		if (!next_protocol)
> +			preserve = false;

Is your idea that if 5 protocols are to be removed but only the first 3
are installed your want to reinstall the first 3?

Can we have some code comment describing what the function should do? In
5 years nobody will remember why the code was implemented this way.

Best regards

Heinrich


>   		ret = efi_uninstall_protocol(handle, protocol,
> -					     protocol_interface);
> +					     protocol_interface, preserve);
>   		if (ret != EFI_SUCCESS)
>   			break;
>   		i++;
> +		protocol = next_protocol;
>   	}
> -	if (ret == EFI_SUCCESS) {
> -		/* If the last protocol has been removed, delete the handle. */
> -		if (list_empty(&handle->protocols)) {
> -			list_del(&handle->link);
> -			free(handle);
> -		}
> +	if (ret == EFI_SUCCESS)
>   		goto out;
> -	}
>
>   	/* If an error occurred undo all changes. */
>   	for (; i; --i) {
> @@ -3712,7 +3744,7 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface(
>   		  new_interface);
>
>   	/* Uninstall protocol but do not delete handle */
> -	ret = efi_uninstall_protocol(handle, protocol, old_interface);
> +	ret = efi_uninstall_protocol(handle, protocol, old_interface, true);
>   	if (ret != EFI_SUCCESS)
>   		goto out;
>



More information about the U-Boot mailing list