From ca483a196c091c16ea0a426ce5f915b184a34412 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 20 Mar 2024 12:47:25 +0000 Subject: [efi] Add helper functions for service binding protocols The EFI service binding abstraction is used to add and remove child handles for multiple different protocols. Provide a common interface for doing so. Signed-off-by: Michael Brown --- src/include/ipxe/efi/Protocol/ServiceBinding.h | 90 ++++++++++++++++ src/include/ipxe/efi/efi_service.h | 19 ++++ src/include/ipxe/errfile.h | 1 + src/interface/efi/efi_service.c | 138 +++++++++++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 src/include/ipxe/efi/Protocol/ServiceBinding.h create mode 100644 src/include/ipxe/efi/efi_service.h create mode 100644 src/interface/efi/efi_service.c diff --git a/src/include/ipxe/efi/Protocol/ServiceBinding.h b/src/include/ipxe/efi/Protocol/ServiceBinding.h new file mode 100644 index 00000000..6baf73aa --- /dev/null +++ b/src/include/ipxe/efi/Protocol/ServiceBinding.h @@ -0,0 +1,90 @@ +/** @file + UEFI Service Binding Protocol is defined in UEFI specification. + + The file defines the generic Service Binding Protocol functions. + It provides services that are required to create and destroy child + handles that support a given set of protocols. + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_SERVICE_BINDING_H__ +#define __EFI_SERVICE_BINDING_H__ + +FILE_LICENCE ( BSD2_PATENT ); + +/// +/// Forward reference for pure ANSI compatability +/// +typedef struct _EFI_SERVICE_BINDING_PROTOCOL EFI_SERVICE_BINDING_PROTOCOL; + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create + the child + @retval other The child handle was not created + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SERVICE_BINDING_CREATE_CHILD)( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SERVICE_BINDING_DESTROY_CHILD)( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +/// +/// The EFI_SERVICE_BINDING_PROTOCOL provides member functions to create and destroy +/// child handles. A driver is responsible for adding protocols to the child handle +/// in CreateChild() and removing protocols in DestroyChild(). It is also required +/// that the CreateChild() function opens the parent protocol BY_CHILD_CONTROLLER +/// to establish the parent-child relationship, and closes the protocol in DestroyChild(). +/// The pseudo code for CreateChild() and DestroyChild() is provided to specify the +/// required behavior, not to specify the required implementation. Each consumer of +/// a software protocol is responsible for calling CreateChild() when it requires the +/// protocol and calling DestroyChild() when it is finished with that protocol. +/// +struct _EFI_SERVICE_BINDING_PROTOCOL { + EFI_SERVICE_BINDING_CREATE_CHILD CreateChild; + EFI_SERVICE_BINDING_DESTROY_CHILD DestroyChild; +}; + +#endif diff --git a/src/include/ipxe/efi/efi_service.h b/src/include/ipxe/efi/efi_service.h new file mode 100644 index 00000000..ca4c7b2a --- /dev/null +++ b/src/include/ipxe/efi/efi_service.h @@ -0,0 +1,19 @@ +#ifndef _IPXE_EFI_SERVICE_H +#define _IPXE_EFI_SERVICE_H + +/** @file + * + * EFI service binding + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +extern int efi_service_add ( EFI_HANDLE service, EFI_GUID *binding, + EFI_HANDLE *handle ); +extern int efi_service_del ( EFI_HANDLE service, EFI_GUID *binding, + EFI_HANDLE handle ); + +#endif /* _IPXE_EFI_SERVICE_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index c673b9a4..083c77e1 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -82,6 +82,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_uuid ( ERRFILE_CORE | 0x002a0000 ) #define ERRFILE_efi_path ( ERRFILE_CORE | 0x002b0000 ) #define ERRFILE_efi_mp ( ERRFILE_CORE | 0x002c0000 ) +#define ERRFILE_efi_service ( ERRFILE_CORE | 0x002d0000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) diff --git a/src/interface/efi/efi_service.c b/src/interface/efi/efi_service.c new file mode 100644 index 00000000..d4129c0d --- /dev/null +++ b/src/interface/efi/efi_service.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2024 Michael Brown . + * + * 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * EFI service binding + * + */ + +#include +#include +#include +#include +#include + +/** + * Add service to child handle + * + * @v service Service binding handle + * @v binding Service binding protocol GUID + * @v handle Handle on which to install child + * @ret rc Return status code + */ +int efi_service_add ( EFI_HANDLE service, EFI_GUID *binding, + EFI_HANDLE *handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_SERVICE_BINDING_PROTOCOL *sb; + void *interface; + } u; + EFI_STATUS efirc; + int rc; + + /* Open service binding protocol */ + if ( ( efirc = bs->OpenProtocol ( service, binding, &u.interface, + efi_image_handle, service, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( service, "EFISVC %s cannot open %s binding: %s\n", + efi_handle_name ( service ), efi_guid_ntoa ( binding ), + strerror ( rc ) ); + goto err_open; + } + + /* Create child handle */ + if ( ( efirc = u.sb->CreateChild ( u.sb, handle ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( service, "EFISVC %s could not create %s child: %s\n", + efi_handle_name ( service ), efi_guid_ntoa ( binding ), + strerror ( rc ) ); + goto err_create; + } + + /* Success */ + rc = 0; + DBGC ( service, "EFISVC %s created %s child ", + efi_handle_name ( service ), efi_guid_ntoa ( binding ) ); + DBGC ( service, "%s\n", efi_handle_name ( *handle ) ); + + err_create: + bs->CloseProtocol ( service, binding, efi_image_handle, service ); + err_open: + return rc; +} + +/** + * Remove service from child handle + * + * @v service Service binding handle + * @v binding Service binding protocol GUID + * @v handle Child handle + * @ret rc Return status code + */ +int efi_service_del ( EFI_HANDLE service, EFI_GUID *binding, + EFI_HANDLE handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_SERVICE_BINDING_PROTOCOL *sb; + void *interface; + } u; + EFI_STATUS efirc; + int rc; + + DBGC ( service, "EFISVC %s removing %s child ", + efi_handle_name ( service ), efi_guid_ntoa ( binding ) ); + DBGC ( service, "%s\n", efi_handle_name ( handle ) ); + + /* Open service binding protocol */ + if ( ( efirc = bs->OpenProtocol ( service, binding, &u.interface, + efi_image_handle, service, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( service, "EFISVC %s cannot open %s binding: %s\n", + efi_handle_name ( service ), efi_guid_ntoa ( binding ), + strerror ( rc ) ); + goto err_open; + } + + /* Destroy child handle */ + if ( ( efirc = u.sb->DestroyChild ( u.sb, handle ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( service, "EFISVC %s could not destroy %s child ", + efi_handle_name ( service ), efi_guid_ntoa ( binding ) ); + DBGC ( service, "%s: %s\n", + efi_handle_name ( handle ), strerror ( rc ) ); + goto err_destroy; + } + + /* Success */ + rc = 0; + + err_destroy: + bs->CloseProtocol ( service, binding, efi_image_handle, service ); + err_open: + return rc; +} -- cgit v1.2.3-55-g7522