diff mbox series

[U-Boot,09/16] efi_loader: implement ConnectController

Message ID 20171217154342.15469-10-xypron.glpk@gmx.de
State Superseded, archived
Delegated to: Alexander Graf
Headers show
Series efi_loader: implement driver management | expand

Commit Message

Heinrich Schuchardt Dec. 17, 2017, 3:43 p.m. UTC
Implement the ConnectController boot service.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
---
 include/efi_api.h             |  22 ++++++
 include/efi_loader.h          |   2 +
 lib/efi_loader/efi_boottime.c | 178 ++++++++++++++++++++++++++++++++++++------
 3 files changed, 178 insertions(+), 24 deletions(-)

Comments

Simon Glass Jan. 8, 2018, 3:35 a.m. UTC | #1
Hi Heinrick,

On 17 December 2017 at 08:43, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> Implement the ConnectController boot service.
>
> Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
> ---
>  include/efi_api.h             |  22 ++++++
>  include/efi_loader.h          |   2 +
>  lib/efi_loader/efi_boottime.c | 178 ++++++++++++++++++++++++++++++++++++------
>  3 files changed, 178 insertions(+), 24 deletions(-)

Reviewed-by: Simon Glass <sjg@chromium.org>

Please see below. Also please mention when tests come for these.

>
> diff --git a/include/efi_api.h b/include/efi_api.h
> index 46963f2891..81e580dbbc 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -805,4 +805,26 @@ struct efi_file_info {
>         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
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 637e6e166d..9e1ae8866b 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -88,6 +88,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_guid_console_control;
>  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_device_path_to_text_protocol;
>  extern const efi_guid_t efi_simple_file_system_protocol_guid;
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index b5d6808bf7..6cc0659eb9 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -56,6 +56,9 @@ static volatile void *efi_gd, *app_gd;
>
>  static int entry_count;
>  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 */
>  int __efi_entry_check(void)
> @@ -1619,30 +1622,6 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long 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.
>   *
> @@ -2352,6 +2331,157 @@ static efi_status_t EFIAPI efi_handle_protocol(void *handle,
>                                  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);
> +                                       if (r == EFI_SUCCESS)
> +                                               ++connected;

What happens to any error here?

> +                               }
> +                       }
> +               }
> +       }
> +
> +       /*
> +        * TODO: Some overrides are not yet implemented:
> +        * - Platform Driver Override
> +        * - Driver Family Override Search
> +        * - Bus Specific Driver Override

Bus-specific ?

> +        */
> +
> +       /* 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.

Well I think it would be good to explain briefly what it does.

> + *
> + * @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 = {
>         .hdr = {
>                 .headersize = sizeof(struct efi_table_hdr),
> --
> 2.14.2
>

Regards,
Simon
diff mbox series

Patch

diff --git a/include/efi_api.h b/include/efi_api.h
index 46963f2891..81e580dbbc 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -805,4 +805,26 @@  struct efi_file_info {
 	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
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 637e6e166d..9e1ae8866b 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -88,6 +88,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_guid_console_control;
 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_device_path_to_text_protocol;
 extern const efi_guid_t efi_simple_file_system_protocol_guid;
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index b5d6808bf7..6cc0659eb9 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -56,6 +56,9 @@  static volatile void *efi_gd, *app_gd;
 
 static int entry_count;
 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 */
 int __efi_entry_check(void)
@@ -1619,30 +1622,6 @@  static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long 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.
  *
@@ -2352,6 +2331,157 @@  static efi_status_t EFIAPI efi_handle_protocol(void *handle,
 				 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);
+					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.
+ *
+ * @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 = {
 	.hdr = {
 		.headersize = sizeof(struct efi_table_hdr),