From da5188f3ea73900f1c6a4e44a8345b48320d396f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 22 Mar 2024 16:50:13 +0000 Subject: [efi] Allow for drivers to be located via child handles 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 --- src/interface/efi/efi_driver.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/interface/efi/efi_driver.c') diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 8e537d53..8f8c9f3d 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -64,16 +64,22 @@ static int efi_driver_disconnecting; /** * Find EFI device * - * @v device EFI device handle + * @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 ( efidev->device == device ) + if ( ( device == efidev->device ) || + ( device == efidev->child ) ) { return efidev; + } } return NULL; -- cgit v1.2.3-55-g7522 From b52b4a46d9ee854130db7a8927f33391fc6ba1fe Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 29 Mar 2024 12:43:24 +0000 Subject: [efi] Allow for allocating EFI devices from arbitrary handles Split out the code that allocates our internal struct efi_device representations, to allow for the creation of temporary MNP devices in order to download the autoexec.ipxe script. Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_driver.h | 2 + src/interface/efi/efi_driver.c | 107 +++++++++++++++++++++++--------------- 2 files changed, 67 insertions(+), 42 deletions(-) (limited to 'src/interface/efi/efi_driver.c') diff --git a/src/include/ipxe/efi/efi_driver.h b/src/include/ipxe/efi/efi_driver.h index 411e9364..7b64e1e0 100644 --- a/src/include/ipxe/efi/efi_driver.h +++ b/src/include/ipxe/efi/efi_driver.h @@ -86,6 +86,8 @@ static inline void * efidev_get_drvdata ( struct efi_device *efidev ) { return efidev->priv; } +extern struct efi_device * efidev_alloc ( EFI_HANDLE device ); +extern void efidev_free ( struct efi_device *efidev ); extern struct efi_device * efidev_parent ( struct device *dev ); extern int efi_driver_install ( void ); extern void efi_driver_uninstall ( void ); diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 8f8c9f3d..fd9be5f5 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -61,6 +61,67 @@ static LIST_HEAD ( efi_devices ); /** We are currently disconnecting drivers */ static int efi_driver_disconnecting; +/** + * Allocate new EFI device + * + * @v device EFI device handle + * @ret efidev EFI device, or NULL on error + */ +struct efi_device * efidev_alloc ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_device *efidev = NULL; + union { + EFI_DEVICE_PATH_PROTOCOL *path; + void *interface; + } path; + EFI_DEVICE_PATH_PROTOCOL *path_end; + size_t path_len; + EFI_STATUS efirc; + int rc; + + /* 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 ) + 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 ); + + err_alloc: + bs->CloseProtocol ( device, &efi_device_path_protocol_guid, + efi_image_handle, device ); + err_open_path: + return efidev; +} + +/** + * Free EFI device + * + * @v efidev EFI device + */ +void efidev_free ( struct efi_device *efidev ) { + + assert ( list_empty ( &efidev->dev.children ) ); + list_del ( &efidev->dev.siblings ); + free ( efidev ); +} + /** * Find EFI device * @@ -159,16 +220,9 @@ efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, 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; @@ -197,36 +251,12 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, 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 ); + /* Add new device */ + efidev = efidev_alloc ( device ); 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 ) { @@ -251,14 +281,8 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, } efirc = EFI_UNSUPPORTED; - list_del ( &efidev->dev.siblings ); - free ( efidev ); + efidev_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: @@ -306,8 +330,7 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, efidrv = efidev->driver; assert ( efidrv != NULL ); efidrv->stop ( efidev ); - list_del ( &efidev->dev.siblings ); - free ( efidev ); + efidev_free ( efidev ); efi_restore_tpl ( &tpl ); return 0; -- cgit v1.2.3-55-g7522