efi_loader: implement ConnectController
Implement the ConnectController boot service. A unit test is supplied in a subsequent patch. Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
committed by
Alexander Graf
parent
d550414434
commit
f0959dbee2
@@ -805,4 +805,26 @@ struct efi_file_info {
|
|||||||
s16 file_name[0];
|
s16 file_name[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define EFI_DRIVER_BINDING_PROTOCOL_GUID \
|
||||||
|
EFI_GUID(0x18a031ab, 0xb443, 0x4d1a,\
|
||||||
|
0xa5, 0xc0, 0x0c, 0x09, 0x26, 0x1e, 0x9f, 0x71)
|
||||||
|
struct efi_driver_binding_protocol {
|
||||||
|
efi_status_t (EFIAPI * supported)(
|
||||||
|
struct efi_driver_binding_protocol *this,
|
||||||
|
efi_handle_t controller_handle,
|
||||||
|
struct efi_device_path *remaining_device_path);
|
||||||
|
efi_status_t (EFIAPI * start)(
|
||||||
|
struct efi_driver_binding_protocol *this,
|
||||||
|
efi_handle_t controller_handle,
|
||||||
|
struct efi_device_path *remaining_device_path);
|
||||||
|
efi_status_t (EFIAPI * stop)(
|
||||||
|
struct efi_driver_binding_protocol *this,
|
||||||
|
efi_handle_t controller_handle,
|
||||||
|
efi_uintn_t number_of_children,
|
||||||
|
efi_handle_t *child_handle_buffer);
|
||||||
|
u32 version;
|
||||||
|
efi_handle_t image_handle;
|
||||||
|
efi_handle_t driver_binding_handle;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ uint16_t *efi_dp_str(struct efi_device_path *dp);
|
|||||||
extern const efi_guid_t efi_global_variable_guid;
|
extern const efi_guid_t efi_global_variable_guid;
|
||||||
extern const efi_guid_t efi_guid_console_control;
|
extern const efi_guid_t efi_guid_console_control;
|
||||||
extern const efi_guid_t efi_guid_device_path;
|
extern const efi_guid_t efi_guid_device_path;
|
||||||
|
/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
|
||||||
|
extern const efi_guid_t efi_guid_driver_binding_protocol;
|
||||||
extern const efi_guid_t efi_guid_loaded_image;
|
extern const efi_guid_t efi_guid_loaded_image;
|
||||||
extern const efi_guid_t efi_guid_device_path_to_text_protocol;
|
extern const efi_guid_t efi_guid_device_path_to_text_protocol;
|
||||||
extern const efi_guid_t efi_simple_file_system_protocol_guid;
|
extern const efi_guid_t efi_simple_file_system_protocol_guid;
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ static volatile void *efi_gd, *app_gd;
|
|||||||
|
|
||||||
static int entry_count;
|
static int entry_count;
|
||||||
static int nesting_level;
|
static int nesting_level;
|
||||||
|
/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
|
||||||
|
const efi_guid_t efi_guid_driver_binding_protocol =
|
||||||
|
EFI_DRIVER_BINDING_PROTOCOL_GUID;
|
||||||
|
|
||||||
/* Called on every callback entry */
|
/* Called on every callback entry */
|
||||||
int __efi_entry_check(void)
|
int __efi_entry_check(void)
|
||||||
@@ -1634,30 +1637,6 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
|
|||||||
return EFI_EXIT(efi_set_watchdog(timeout));
|
return EFI_EXIT(efi_set_watchdog(timeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Connect a controller to a driver.
|
|
||||||
*
|
|
||||||
* This function implements the ConnectController service.
|
|
||||||
* See the Unified Extensible Firmware Interface (UEFI) specification
|
|
||||||
* for details.
|
|
||||||
*
|
|
||||||
* @controller_handle handle of the controller
|
|
||||||
* @driver_image_handle handle of the driver
|
|
||||||
* @remain_device_path device path of a child controller
|
|
||||||
* @recursive true to connect all child controllers
|
|
||||||
* @return status code
|
|
||||||
*/
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle,
|
|
||||||
remain_device_path, recursive);
|
|
||||||
return EFI_EXIT(EFI_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disconnect a controller from a driver.
|
* Disconnect a controller from a driver.
|
||||||
*
|
*
|
||||||
@@ -2368,6 +2347,166 @@ static efi_status_t EFIAPI efi_handle_protocol(void *handle,
|
|||||||
NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
|
NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static efi_status_t efi_bind_controller(
|
||||||
|
efi_handle_t controller_handle,
|
||||||
|
efi_handle_t driver_image_handle,
|
||||||
|
struct efi_device_path *remain_device_path)
|
||||||
|
{
|
||||||
|
struct efi_driver_binding_protocol *binding_protocol;
|
||||||
|
efi_status_t r;
|
||||||
|
|
||||||
|
r = EFI_CALL(efi_open_protocol(driver_image_handle,
|
||||||
|
&efi_guid_driver_binding_protocol,
|
||||||
|
(void **)&binding_protocol,
|
||||||
|
driver_image_handle, NULL,
|
||||||
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL));
|
||||||
|
if (r != EFI_SUCCESS)
|
||||||
|
return r;
|
||||||
|
r = EFI_CALL(binding_protocol->supported(binding_protocol,
|
||||||
|
controller_handle,
|
||||||
|
remain_device_path));
|
||||||
|
if (r == EFI_SUCCESS)
|
||||||
|
r = EFI_CALL(binding_protocol->start(binding_protocol,
|
||||||
|
controller_handle,
|
||||||
|
remain_device_path));
|
||||||
|
EFI_CALL(efi_close_protocol(driver_image_handle,
|
||||||
|
&efi_guid_driver_binding_protocol,
|
||||||
|
driver_image_handle, NULL));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static efi_status_t efi_connect_single_controller(
|
||||||
|
efi_handle_t controller_handle,
|
||||||
|
efi_handle_t *driver_image_handle,
|
||||||
|
struct efi_device_path *remain_device_path)
|
||||||
|
{
|
||||||
|
efi_handle_t *buffer;
|
||||||
|
size_t count;
|
||||||
|
size_t i;
|
||||||
|
efi_status_t r;
|
||||||
|
size_t connected = 0;
|
||||||
|
|
||||||
|
/* Get buffer with all handles with driver binding protocol */
|
||||||
|
r = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
|
||||||
|
&efi_guid_driver_binding_protocol,
|
||||||
|
NULL, &count, &buffer));
|
||||||
|
if (r != EFI_SUCCESS)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Context Override */
|
||||||
|
if (driver_image_handle) {
|
||||||
|
for (; *driver_image_handle; ++driver_image_handle) {
|
||||||
|
for (i = 0; i < count; ++i) {
|
||||||
|
if (buffer[i] == *driver_image_handle) {
|
||||||
|
buffer[i] = NULL;
|
||||||
|
r = efi_bind_controller(
|
||||||
|
controller_handle,
|
||||||
|
*driver_image_handle,
|
||||||
|
remain_device_path);
|
||||||
|
/*
|
||||||
|
* For drivers that do not support the
|
||||||
|
* controller or are already connected
|
||||||
|
* we receive an error code here.
|
||||||
|
*/
|
||||||
|
if (r == EFI_SUCCESS)
|
||||||
|
++connected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Some overrides are not yet implemented:
|
||||||
|
* - Platform Driver Override
|
||||||
|
* - Driver Family Override Search
|
||||||
|
* - Bus Specific Driver Override
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Driver Binding Search */
|
||||||
|
for (i = 0; i < count; ++i) {
|
||||||
|
if (buffer[i]) {
|
||||||
|
r = efi_bind_controller(controller_handle,
|
||||||
|
buffer[i],
|
||||||
|
remain_device_path);
|
||||||
|
if (r == EFI_SUCCESS)
|
||||||
|
++connected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
efi_free_pool(buffer);
|
||||||
|
if (!connected)
|
||||||
|
return EFI_NOT_FOUND;
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connect a controller to a driver.
|
||||||
|
*
|
||||||
|
* This function implements the ConnectController service.
|
||||||
|
* See the Unified Extensible Firmware Interface (UEFI) specification
|
||||||
|
* for details.
|
||||||
|
*
|
||||||
|
* First all driver binding protocol handles are tried for binding drivers.
|
||||||
|
* Afterwards all handles that have openened a protocol of the controller
|
||||||
|
* with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers.
|
||||||
|
*
|
||||||
|
* @controller_handle handle of the controller
|
||||||
|
* @driver_image_handle handle of the driver
|
||||||
|
* @remain_device_path device path of a child controller
|
||||||
|
* @recursive true to connect all child controllers
|
||||||
|
* @return status code
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
efi_status_t r;
|
||||||
|
efi_status_t ret = EFI_NOT_FOUND;
|
||||||
|
struct efi_object *efiobj;
|
||||||
|
|
||||||
|
EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle,
|
||||||
|
remain_device_path, recursive);
|
||||||
|
|
||||||
|
efiobj = efi_search_obj(controller_handle);
|
||||||
|
if (!efiobj) {
|
||||||
|
ret = EFI_INVALID_PARAMETER;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = efi_connect_single_controller(controller_handle,
|
||||||
|
driver_image_handle,
|
||||||
|
remain_device_path);
|
||||||
|
if (r == EFI_SUCCESS)
|
||||||
|
ret = EFI_SUCCESS;
|
||||||
|
if (recursive) {
|
||||||
|
struct efi_handler *handler;
|
||||||
|
struct efi_open_protocol_info_item *item;
|
||||||
|
|
||||||
|
list_for_each_entry(handler, &efiobj->protocols, link) {
|
||||||
|
list_for_each_entry(item, &handler->open_infos, link) {
|
||||||
|
if (item->info.attributes &
|
||||||
|
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
|
||||||
|
r = EFI_CALL(efi_connect_controller(
|
||||||
|
item->info.controller_handle,
|
||||||
|
driver_image_handle,
|
||||||
|
remain_device_path,
|
||||||
|
recursive));
|
||||||
|
if (r == EFI_SUCCESS)
|
||||||
|
ret = EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Check for child controller specified by end node */
|
||||||
|
if (ret != EFI_SUCCESS && remain_device_path &&
|
||||||
|
remain_device_path->type == DEVICE_PATH_TYPE_END)
|
||||||
|
ret = EFI_SUCCESS;
|
||||||
|
out:
|
||||||
|
return EFI_EXIT(ret);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct efi_boot_services efi_boot_services = {
|
static const struct efi_boot_services efi_boot_services = {
|
||||||
.hdr = {
|
.hdr = {
|
||||||
.headersize = sizeof(struct efi_table_hdr),
|
.headersize = sizeof(struct efi_table_hdr),
|
||||||
|
|||||||
Reference in New Issue
Block a user