mirror of
https://github.com/ipxe/ipxe
synced 2025-12-25 00:17:57 +03:00
When using a service binding protocol, CreateChild() will create a new protocol instance (and optionally a new handle). The caller will then typically open this new protocol instance with BY_DRIVER attributes, since the service binding mechanism has no equivalent of the driver binding protocol's Stop() method, and there is therefore no other way for the caller to be informed if the protocol instance is about to become invalid (e.g. because the service driver wants to remove the child). The caller cannot ask CreateChild() to install the new protocol instance on the original handle (i.e. the service binding handle), since the whole point of the service binding protocol is to allow for the existence of multiple children, and UEFI does not permit multiple instances of the same protocol to be installed on a handle. Our current drivers all open the original handle (as passed to our driver binding's Start() method) with BY_DRIVER attributes, and so the same handle will be passed to our Stop() method. This changes when our driver must use a separate handle, as described above. Add an optional "child handle" field to struct efi_device (on the assumption that we will not have any drivers that need to create multiple children), and generalise efidev_find() to match on either the original handle or the child handle. Signed-off-by: Michael Brown <mcb30@ipxe.org>
606 lines
16 KiB
C
606 lines
16 KiB
C
/*
|
|
* Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER );
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ipxe/version.h>
|
|
#include <ipxe/efi/efi.h>
|
|
#include <ipxe/efi/Protocol/DriverBinding.h>
|
|
#include <ipxe/efi/Protocol/ComponentName2.h>
|
|
#include <ipxe/efi/Protocol/DevicePath.h>
|
|
#include <ipxe/efi/efi_strings.h>
|
|
#include <ipxe/efi/efi_path.h>
|
|
#include <ipxe/efi/efi_driver.h>
|
|
|
|
/** @file
|
|
*
|
|
* EFI driver interface
|
|
*
|
|
*/
|
|
|
|
/* Disambiguate the various error causes */
|
|
#define EINFO_EEFI_CONNECT \
|
|
__einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
|
|
"Could not connect controllers" )
|
|
#define EINFO_EEFI_CONNECT_PROHIBITED \
|
|
__einfo_platformify ( EINFO_EEFI_CONNECT, \
|
|
EFI_SECURITY_VIOLATION, \
|
|
"Connecting controllers prohibited by " \
|
|
"security policy" )
|
|
#define EEFI_CONNECT_PROHIBITED \
|
|
__einfo_error ( EINFO_EEFI_CONNECT_PROHIBITED )
|
|
#define EEFI_CONNECT( efirc ) EPLATFORM ( EINFO_EEFI_CONNECT, efirc, \
|
|
EEFI_CONNECT_PROHIBITED )
|
|
|
|
static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding;
|
|
|
|
/** List of controlled EFI devices */
|
|
static LIST_HEAD ( efi_devices );
|
|
|
|
/** We are currently disconnecting drivers */
|
|
static int efi_driver_disconnecting;
|
|
|
|
/**
|
|
* Find EFI device
|
|
*
|
|
* @v device EFI device handle (or child handle)
|
|
* @ret efidev EFI device, or NULL if not found
|
|
*/
|
|
static struct efi_device * efidev_find ( EFI_HANDLE device ) {
|
|
struct efi_device *efidev;
|
|
|
|
/* Avoid false positive matches against NULL children */
|
|
if ( ! device )
|
|
return NULL;
|
|
|
|
/* Look for an existing EFI device */
|
|
list_for_each_entry ( efidev, &efi_devices, dev.siblings ) {
|
|
if ( ( device == efidev->device ) ||
|
|
( device == efidev->child ) ) {
|
|
return efidev;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Get parent EFI device
|
|
*
|
|
* @v dev Generic device
|
|
* @ret efidev Parent EFI device, or NULL
|
|
*/
|
|
struct efi_device * efidev_parent ( struct device *dev ) {
|
|
struct device *parent;
|
|
struct efi_device *efidev;
|
|
|
|
/* Walk upwards until we find a registered EFI device */
|
|
while ( ( parent = dev->parent ) ) {
|
|
list_for_each_entry ( efidev, &efi_devices, dev.siblings ) {
|
|
if ( parent == &efidev->dev )
|
|
return efidev;
|
|
}
|
|
dev = parent;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Check to see if driver supports a device
|
|
*
|
|
* @v driver EFI driver
|
|
* @v device EFI device
|
|
* @v child Path to child device, if any
|
|
* @ret efirc EFI status code
|
|
*/
|
|
static EFI_STATUS EFIAPI
|
|
efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
|
|
EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) {
|
|
struct efi_driver *efidrv;
|
|
int rc;
|
|
|
|
DBGCP ( device, "EFIDRV %s DRIVER_SUPPORTED",
|
|
efi_handle_name ( device ) );
|
|
if ( child )
|
|
DBGCP ( device, " (child %s)", efi_devpath_text ( child ) );
|
|
DBGCP ( device, "\n" );
|
|
|
|
/* Do nothing if we are already driving this device */
|
|
if ( efidev_find ( device ) != NULL ) {
|
|
DBGCP ( device, "EFIDRV %s is already started\n",
|
|
efi_handle_name ( device ) );
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
|
|
/* Look for a driver claiming to support this device */
|
|
for_each_table_entry ( efidrv, EFI_DRIVERS ) {
|
|
if ( ( rc = efidrv->supported ( device ) ) == 0 ) {
|
|
DBGC ( device, "EFIDRV %s has driver \"%s\"\n",
|
|
efi_handle_name ( device ), efidrv->name );
|
|
return 0;
|
|
}
|
|
}
|
|
DBGCP ( device, "EFIDRV %s has no driver\n",
|
|
efi_handle_name ( device ) );
|
|
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/**
|
|
* Attach driver to device
|
|
*
|
|
* @v driver EFI driver
|
|
* @v device EFI device
|
|
* @v child Path to child device, if any
|
|
* @ret efirc EFI status code
|
|
*/
|
|
static EFI_STATUS EFIAPI
|
|
efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
|
|
EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
struct efi_driver *efidrv;
|
|
struct efi_device *efidev;
|
|
struct efi_saved_tpl tpl;
|
|
union {
|
|
EFI_DEVICE_PATH_PROTOCOL *path;
|
|
void *interface;
|
|
} path;
|
|
EFI_DEVICE_PATH_PROTOCOL *path_end;
|
|
size_t path_len;
|
|
EFI_STATUS efirc;
|
|
int rc;
|
|
|
|
DBGC ( device, "EFIDRV %s DRIVER_START", efi_handle_name ( device ) );
|
|
if ( child )
|
|
DBGC ( device, " (child %s)", efi_devpath_text ( child ) );
|
|
DBGC ( device, "\n" );
|
|
|
|
/* Do nothing if we are already driving this device */
|
|
efidev = efidev_find ( device );
|
|
if ( efidev ) {
|
|
DBGCP ( device, "EFIDRV %s is already started\n",
|
|
efi_handle_name ( device ) );
|
|
efirc = EFI_ALREADY_STARTED;
|
|
goto err_already_started;
|
|
}
|
|
|
|
/* Raise TPL */
|
|
efi_raise_tpl ( &tpl );
|
|
|
|
/* Do nothing if we are currently disconnecting drivers */
|
|
if ( efi_driver_disconnecting ) {
|
|
DBGC ( device, "EFIDRV %s refusing to start during "
|
|
"disconnection\n", efi_handle_name ( device ) );
|
|
efirc = EFI_NOT_READY;
|
|
goto err_disconnecting;
|
|
}
|
|
|
|
/* Open device path */
|
|
if ( ( efirc = bs->OpenProtocol ( device,
|
|
&efi_device_path_protocol_guid,
|
|
&path.interface, efi_image_handle,
|
|
device,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
|
|
rc = -EEFI ( efirc );
|
|
DBGC ( device, "EFIDRV %s could not open device path: %s\n",
|
|
efi_handle_name ( device ), strerror ( rc ) );
|
|
goto err_open_path;
|
|
}
|
|
path_len = ( efi_path_len ( path.path ) + sizeof ( *path_end ) );
|
|
|
|
/* Allocate and initialise structure */
|
|
efidev = zalloc ( sizeof ( *efidev ) + path_len );
|
|
if ( ! efidev ) {
|
|
efirc = EFI_OUT_OF_RESOURCES;
|
|
goto err_alloc;
|
|
}
|
|
efidev->device = device;
|
|
efidev->dev.desc.bus_type = BUS_TYPE_EFI;
|
|
efidev->path = ( ( ( void * ) efidev ) + sizeof ( *efidev ) );
|
|
memcpy ( efidev->path, path.path, path_len );
|
|
INIT_LIST_HEAD ( &efidev->dev.children );
|
|
list_add ( &efidev->dev.siblings, &efi_devices );
|
|
|
|
/* Close device path */
|
|
bs->CloseProtocol ( device, &efi_device_path_protocol_guid,
|
|
efi_image_handle, device );
|
|
path.path = NULL;
|
|
|
|
/* Try to start this device */
|
|
for_each_table_entry ( efidrv, EFI_DRIVERS ) {
|
|
if ( ( rc = efidrv->supported ( device ) ) != 0 ) {
|
|
DBGC ( device, "EFIDRV %s is not supported by driver "
|
|
"\"%s\": %s\n", efi_handle_name ( device ),
|
|
efidrv->name,
|
|
strerror ( rc ) );
|
|
continue;
|
|
}
|
|
if ( ( rc = efidrv->start ( efidev ) ) == 0 ) {
|
|
efidev->driver = efidrv;
|
|
DBGC ( device, "EFIDRV %s using driver \"%s\"\n",
|
|
efi_handle_name ( device ),
|
|
efidev->driver->name );
|
|
efi_restore_tpl ( &tpl );
|
|
return 0;
|
|
}
|
|
DBGC ( device, "EFIDRV %s could not start driver \"%s\": %s\n",
|
|
efi_handle_name ( device ), efidrv->name,
|
|
strerror ( rc ) );
|
|
}
|
|
efirc = EFI_UNSUPPORTED;
|
|
|
|
list_del ( &efidev->dev.siblings );
|
|
free ( efidev );
|
|
err_alloc:
|
|
if ( path.path ) {
|
|
bs->CloseProtocol ( device, &efi_device_path_protocol_guid,
|
|
efi_image_handle, device );
|
|
}
|
|
err_open_path:
|
|
err_disconnecting:
|
|
efi_restore_tpl ( &tpl );
|
|
err_already_started:
|
|
return efirc;
|
|
}
|
|
|
|
/**
|
|
* Detach driver from device
|
|
*
|
|
* @v driver EFI driver
|
|
* @v device EFI device
|
|
* @v pci PCI device
|
|
* @v num_children Number of child devices
|
|
* @v children List of child devices
|
|
* @ret efirc EFI status code
|
|
*/
|
|
static EFI_STATUS EFIAPI
|
|
efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
|
|
EFI_HANDLE device, UINTN num_children,
|
|
EFI_HANDLE *children ) {
|
|
struct efi_driver *efidrv;
|
|
struct efi_device *efidev;
|
|
struct efi_saved_tpl tpl;
|
|
UINTN i;
|
|
|
|
DBGC ( device, "EFIDRV %s DRIVER_STOP", efi_handle_name ( device ) );
|
|
for ( i = 0 ; i < num_children ; i++ ) {
|
|
DBGC ( device, "%s%s", ( i ? ", " : " child " ),
|
|
efi_handle_name ( children[i] ) );
|
|
}
|
|
DBGC ( device, "\n" );
|
|
|
|
/* Do nothing unless we are driving this device */
|
|
efidev = efidev_find ( device );
|
|
if ( ! efidev ) {
|
|
DBGCP ( device, "EFIDRV %s is not started\n",
|
|
efi_handle_name ( device ) );
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* Raise TPL */
|
|
efi_raise_tpl ( &tpl );
|
|
|
|
/* Stop this device */
|
|
efidrv = efidev->driver;
|
|
assert ( efidrv != NULL );
|
|
efidrv->stop ( efidev );
|
|
list_del ( &efidev->dev.siblings );
|
|
free ( efidev );
|
|
|
|
efi_restore_tpl ( &tpl );
|
|
return 0;
|
|
}
|
|
|
|
/** EFI driver binding protocol */
|
|
static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding = {
|
|
.Supported = efi_driver_supported,
|
|
.Start = efi_driver_start,
|
|
.Stop = efi_driver_stop,
|
|
};
|
|
|
|
/**
|
|
* Look up driver name
|
|
*
|
|
* @v wtf Component name protocol
|
|
* @v language Language to use
|
|
* @v driver_name Driver name to fill in
|
|
* @ret efirc EFI status code
|
|
*/
|
|
static EFI_STATUS EFIAPI
|
|
efi_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused,
|
|
CHAR8 *language __unused, CHAR16 **driver_name ) {
|
|
const wchar_t *name;
|
|
|
|
name = ( product_wname[0] ? product_wname : build_wname );
|
|
*driver_name = ( ( wchar_t * ) name );
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Look up controller name
|
|
*
|
|
* @v wtf Component name protocol
|
|
* @v device Device
|
|
* @v child Child device, or NULL
|
|
* @v language Language to use
|
|
* @v driver_name Device name to fill in
|
|
* @ret efirc EFI status code
|
|
*/
|
|
static EFI_STATUS EFIAPI
|
|
efi_driver_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused,
|
|
EFI_HANDLE device, EFI_HANDLE child,
|
|
CHAR8 *language, CHAR16 **controller_name ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
union {
|
|
EFI_COMPONENT_NAME2_PROTOCOL *name2;
|
|
void *interface;
|
|
} name2;
|
|
EFI_STATUS efirc;
|
|
|
|
/* Delegate to the EFI_COMPONENT_NAME2_PROTOCOL instance
|
|
* installed on child handle, if present.
|
|
*/
|
|
if ( ( child != NULL ) &&
|
|
( ( efirc = bs->OpenProtocol (
|
|
child, &efi_component_name2_protocol_guid,
|
|
&name2.interface, NULL, NULL,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) == 0 ) ) {
|
|
return name2.name2->GetControllerName ( name2.name2, device,
|
|
child, language,
|
|
controller_name );
|
|
}
|
|
|
|
/* Otherwise, let EFI use the default Device Path Name */
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
/** EFI component name protocol */
|
|
static EFI_COMPONENT_NAME2_PROTOCOL efi_wtf = {
|
|
.GetDriverName = efi_driver_name,
|
|
.GetControllerName = efi_driver_controller_name,
|
|
.SupportedLanguages = "en",
|
|
};
|
|
|
|
/**
|
|
* Install EFI driver
|
|
*
|
|
* @ret rc Return status code
|
|
*/
|
|
int efi_driver_install ( void ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
EFI_STATUS efirc;
|
|
int rc;
|
|
|
|
/* Calculate driver version number. We use the build
|
|
* timestamp (in seconds since the Epoch) shifted right by six
|
|
* bits: this gives us an approximately one-minute resolution
|
|
* and a scheme which will last until the year 10680.
|
|
*/
|
|
efi_driver_binding.Version = ( build_timestamp >> 6 );
|
|
|
|
/* Install protocols on image handle */
|
|
efi_driver_binding.ImageHandle = efi_image_handle;
|
|
efi_driver_binding.DriverBindingHandle = efi_image_handle;
|
|
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
|
|
&efi_image_handle,
|
|
&efi_driver_binding_protocol_guid, &efi_driver_binding,
|
|
&efi_component_name2_protocol_guid, &efi_wtf,
|
|
NULL ) ) != 0 ) {
|
|
rc = -EEFI ( efirc );
|
|
DBGC ( &efi_driver_binding, "EFIDRV could not install "
|
|
"protocols: %s\n", strerror ( rc ) );
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Uninstall EFI driver
|
|
*
|
|
*/
|
|
void efi_driver_uninstall ( void ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
|
|
/* Uninstall protocols */
|
|
bs->UninstallMultipleProtocolInterfaces (
|
|
efi_image_handle,
|
|
&efi_driver_binding_protocol_guid, &efi_driver_binding,
|
|
&efi_component_name2_protocol_guid, &efi_wtf, NULL );
|
|
}
|
|
|
|
/**
|
|
* Try to connect EFI driver
|
|
*
|
|
* @v device EFI device
|
|
* @ret rc Return status code
|
|
*/
|
|
static int efi_driver_connect ( EFI_HANDLE device ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
EFI_HANDLE drivers[2] =
|
|
{ efi_driver_binding.DriverBindingHandle, NULL };
|
|
EFI_STATUS efirc;
|
|
int rc;
|
|
|
|
/* Check if we want to drive this device */
|
|
if ( ( efirc = efi_driver_supported ( &efi_driver_binding, device,
|
|
NULL ) ) != 0 ) {
|
|
/* Not supported; not an error */
|
|
return 0;
|
|
}
|
|
|
|
/* Disconnect any existing drivers */
|
|
DBGC2 ( device, "EFIDRV %s before disconnecting:\n",
|
|
efi_handle_name ( device ) );
|
|
DBGC2_EFI_PROTOCOLS ( device, device );
|
|
DBGC ( device, "EFIDRV %s disconnecting existing drivers\n",
|
|
efi_handle_name ( device ) );
|
|
efi_driver_disconnecting = 1;
|
|
if ( ( efirc = bs->DisconnectController ( device, NULL,
|
|
NULL ) ) != 0 ) {
|
|
rc = -EEFI ( efirc );
|
|
DBGC ( device, "EFIDRV %s could not disconnect existing "
|
|
"drivers: %s\n", efi_handle_name ( device ),
|
|
strerror ( rc ) );
|
|
/* Ignore the error and attempt to connect our drivers */
|
|
}
|
|
efi_driver_disconnecting = 0;
|
|
DBGC2 ( device, "EFIDRV %s after disconnecting:\n",
|
|
efi_handle_name ( device ) );
|
|
DBGC2_EFI_PROTOCOLS ( device, device );
|
|
|
|
/* Connect our driver */
|
|
DBGC ( device, "EFIDRV %s connecting new drivers\n",
|
|
efi_handle_name ( device ) );
|
|
if ( ( efirc = bs->ConnectController ( device, drivers, NULL,
|
|
TRUE ) ) != 0 ) {
|
|
rc = -EEFI_CONNECT ( efirc );
|
|
DBGC ( device, "EFIDRV %s could not connect new drivers: "
|
|
"%s\n", efi_handle_name ( device ), strerror ( rc ) );
|
|
DBGC ( device, "EFIDRV %s connecting driver directly\n",
|
|
efi_handle_name ( device ) );
|
|
if ( ( efirc = efi_driver_start ( &efi_driver_binding, device,
|
|
NULL ) ) != 0 ) {
|
|
rc = -EEFI_CONNECT ( efirc );
|
|
DBGC ( device, "EFIDRV %s could not connect driver "
|
|
"directly: %s\n", efi_handle_name ( device ),
|
|
strerror ( rc ) );
|
|
return rc;
|
|
}
|
|
}
|
|
DBGC2 ( device, "EFIDRV %s after connecting:\n",
|
|
efi_handle_name ( device ) );
|
|
DBGC2_EFI_PROTOCOLS ( device, device );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Try to disconnect EFI driver
|
|
*
|
|
* @v device EFI device
|
|
* @ret rc Return status code
|
|
*/
|
|
static int efi_driver_disconnect ( EFI_HANDLE device ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
|
|
/* Disconnect our driver */
|
|
efi_driver_disconnecting = 1;
|
|
bs->DisconnectController ( device,
|
|
efi_driver_binding.DriverBindingHandle,
|
|
NULL );
|
|
efi_driver_disconnecting = 0;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Reconnect original EFI driver
|
|
*
|
|
* @v device EFI device
|
|
* @ret rc Return status code
|
|
*/
|
|
static int efi_driver_reconnect ( EFI_HANDLE device ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
|
|
/* Reconnect any available driver */
|
|
bs->ConnectController ( device, NULL, NULL, TRUE );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Connect/disconnect EFI driver from all handles
|
|
*
|
|
* @v method Connect/disconnect method
|
|
* @ret rc Return status code
|
|
*/
|
|
static int efi_driver_handles ( int ( * method ) ( EFI_HANDLE handle ) ) {
|
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
|
EFI_HANDLE *handles;
|
|
UINTN num_handles;
|
|
EFI_STATUS efirc;
|
|
UINTN i;
|
|
int rc;
|
|
|
|
/* Enumerate all handles */
|
|
if ( ( efirc = bs->LocateHandleBuffer ( AllHandles, NULL, NULL,
|
|
&num_handles,
|
|
&handles ) ) != 0 ) {
|
|
rc = -EEFI ( efirc );
|
|
DBGC ( &efi_driver_binding, "EFIDRV could not list handles: "
|
|
"%s\n", strerror ( rc ) );
|
|
goto err_locate;
|
|
}
|
|
|
|
/* Connect/disconnect driver from all handles */
|
|
for ( i = 0 ; i < num_handles ; i++ ) {
|
|
if ( ( rc = method ( handles[i] ) ) != 0 ) {
|
|
/* Ignore errors and continue to process
|
|
* remaining handles.
|
|
*/
|
|
}
|
|
}
|
|
|
|
/* Success */
|
|
rc = 0;
|
|
|
|
bs->FreePool ( handles );
|
|
err_locate:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Connect EFI driver to all possible devices
|
|
*
|
|
* @ret rc Return status code
|
|
*/
|
|
int efi_driver_connect_all ( void ) {
|
|
|
|
DBGC ( &efi_driver_binding, "EFIDRV connecting our drivers\n" );
|
|
return efi_driver_handles ( efi_driver_connect );
|
|
}
|
|
|
|
/**
|
|
* Disconnect EFI driver from all possible devices
|
|
*
|
|
* @ret rc Return status code
|
|
*/
|
|
void efi_driver_disconnect_all ( void ) {
|
|
|
|
DBGC ( &efi_driver_binding, "EFIDRV disconnecting our drivers\n" );
|
|
efi_driver_handles ( efi_driver_disconnect );
|
|
}
|
|
|
|
/**
|
|
* Reconnect original EFI drivers to all possible devices
|
|
*
|
|
* @ret rc Return status code
|
|
*/
|
|
void efi_driver_reconnect_all ( void ) {
|
|
|
|
DBGC ( &efi_driver_binding, "EFIDRV reconnecting old drivers\n" );
|
|
efi_driver_handles ( efi_driver_reconnect );
|
|
}
|