commit
239d59a65e20 ("efi_loader: reconnect drivers on failure")
tried to fix the UninstallProtocol interface which must reconnect
any controllers it disconnected by calling ConnectController()
in case of failure. However, the reconnect functionality was wired in
efi_disconnect_all_drivers() instead of efi_uninstall_protocol().
As a result some SCT tests started failing.
Specifically, BBTestOpenProtocolInterfaceTest333CheckPoint3() test
- Calls ConnectController for DriverImageHandle1
- Calls DisconnectController for DriverImageHandle1 which will
disconnect everything apart from TestProtocol4. That will remain
open on purpose.
- Calls ConnectController for DriverImageHandle2. TestProtocol4
which was explicitly preserved was installed wth BY_DRIVER attributes.
The new protocol will call DisconnectController since its attributes
are BY_DRIVER|EXCLUSIVE, but TestProtocol4 will not be removed. The
test expects EFI_ACCESS_DENIED which works fine.
The problem is that DisconnectController, will eventually call
EFI_DRIVER_BINDING_PROTOCOL.Stop(). But on the aforementioned test
this will call CloseProtocol -- the binding protocol is defined in
'DBindingDriver3.c' and the .Stop function uses CloseProtocol.
If that close protocol call fails with EFI_NOT_FOUND, the current code
will try to mistakenly reconnect all drivers and the subsequent tests
that rely on the device being disconnected will fail.
Move the reconnection in efi_uninstall_protocol() were it belongs.
Fixes: commit 239d59a65e20 ("efi_loader: reconnect drivers on failure")
Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Reviewed-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
const efi_guid_t *protocol,
efi_handle_t child_handle)
{
- efi_uintn_t number_of_drivers, tmp;
+ efi_uintn_t number_of_drivers;
efi_handle_t *driver_handle_buffer;
efi_status_t r, ret;
if (!number_of_drivers)
return EFI_SUCCESS;
- tmp = number_of_drivers;
while (number_of_drivers) {
- ret = EFI_CALL(efi_disconnect_controller(
+ r = EFI_CALL(efi_disconnect_controller(
handle,
driver_handle_buffer[--number_of_drivers],
child_handle));
- 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");
+ ret = r;
}
free(driver_handle_buffer);
r = efi_disconnect_all_drivers(handle, protocol, NULL);
if (r != EFI_SUCCESS) {
r = EFI_ACCESS_DENIED;
+ /*
+ * This will reconnect all controllers of the handle, even ones
+ * that were not connected before. This can be done better
+ * but we are following the EDKII implementation on this for
+ * now
+ */
+ EFI_CALL(efi_connect_controller(handle, NULL, NULL, true));
goto out;
}
/* Close protocol */