5.1.3 Handle Database and Protocol Services

There are several UEFI Boot Services used to add, retrieve, and remove contents from the Handle Database. Concepts of the Handle Database and Protocols are introduced in Section 3.4. This section provides code examples for the UEFI Boot Services commonly used by UEFI Drivers to manage the Handle Database and include the following:

  • InstallMultipleProtocolInterfaces()

  • UninstallMultipleProtocolInterfaces()

  • LocateHandleBuffer()

  • LocateProtocol()

  • OpenProtocol()

  • OpenProtocolInformation()

  • CloseProtocol()

5.1.3.1 InstallMultipleProtocolInterfaces() and UninstallMultipleProtocolInterfaces()

These services are used to do the following:

  • Create new handles in the Handle Database.

  • Remove a handle from the Handle Database.

  • Add protocols to an existing handle in the Handle Database.

  • Remove protocols from an existing handle in the Handle Database.

Extra services to create a new handle in the Handle Database and remove an existing handle from the Handle Database are not required. Instead, the first protocol installed onto a handle automatically creates a new handle and adds that handle to the Handle Database. The last protocol removed from an existing handle automatically removes the handle from the Handle Database and destroys the handle. This means it is not possible for a handle to be present in the Handle Database with zero protocols installed.

Another important concept is that a single handle in the Handle Database is not allowed to have more than one instance of the same Protocol installed onto that handle. If a UEFI Driver is required to produce more than one instance of the same protocol, then the Protocol instances must be installed on different handles in the Handle Database.

UEFI Drivers tend to manage more than one protocol at a time. Because of this, it is recommended that InstallMultipleProtocolInterfaces() and UninstallMultipleProtocolInterfaces() be used instead of the InstallProtocolInterface() and UninstallProtocolInterface(). This results in source code that is easier to maintain and also tends to produce smaller executables. In addition, InstallMultipleProtocolInterfaces() provides more extensive error checking than InstallProtocolInterface(), which allows developers to catch coding errors sooner, and results in higher quality UEFI Driver implementations. The main difference is that InstallMultipleProtocolInterfaces() guarantees that a duplicate Device Path

Protocol is never be added to the Handle Database. Section 3.9 introduces the concept of Device Paths and the requirement for them to be unique.

The InstallMultipleProtocolInterfaces() and UninstallMultipleProtocolInterfaces() services support adding and removing more than one protocol at a time through the use of a variable argument list. Protocols are represented by a pair of pointers to a protocol GUID and a protocol interface. These services parse pairs of arguments until a NULL pointer for the protocol GUID parameter is encountered.

Note: If any errors are generated when the protocols are being added to a handle, any protocols added before the error is returned, are automatically removed by InstallMultipleProtocolInterfaces(). This means the state of the handle in the handle database is identical to the state prior to the call.

Note: If any errors are generated when the protocols are being removed from a handle, any protocols removed before the error is returned, are automatically added back by UninstallMultipleProtocolInterfaces(). This means the state of the handle in the handle database is identical to the state prior to the call.

TIP: If unexpected errors are returned by these services, try converting a single call for multiple protocols to a series of calls that process one protocol at a time. This allows the specific protocol causing the error condition to be identified. It should be rare for these services to return an error condition. If an error condition is present it is likely due to a duplicate GUID, a duplicate device path, or an invalid handle.

Note: When an attempt is made to remove a protocol interface from a handle in the handle database, the UEFI core firmware checks to see if any other UEFI drivers are currently using the services of the protocol to be removed. If UEFI drivers are using that protocol interface, the UEFI core firmware attempts to stop those UEFI drivers with a call to DisconnectController(). This is a quick, legal, and safe way to shut down any protocols associated with this driver's stack.

Caution: A serious issue can occur when a user removes and then reattaches a device on a bus that supports hot-plugging. Driver writers must consider this when writing drivers for hot-plug devices.

The issue occurs when other controllers are also using one, or more, of a driver's protocols. In these cases, the UninstallMultipleProtocolInterfaces service fails.

If the call to DisconnectController() fails, the UEFI core firmware then calls ConnectController() to put the handle database back into the same state that it was in prior to the original call to UninstallMultipleProtocolInterfaces(). This call to ConnectController() has the potential to cause issues upon re-entry in UEFI drivers that must be handled in the UEFI driver. These issues could include lost or missed connected pointer linkages resulting in lost data, confused operation, crashes and other errors. See _Chapter 31 in this guide for recommendations on how to test UEFI drivers._

5.1.3.1.1 Protocols that may be added at the driver entry point

The following protocols may also be added in the driver entry point with the InstallMultipleProtocolInterfaces() service. Please see Chapter 7 for more details on how to install these protocols in a driver entry point along with the recommendations on when each of these protocols should be installed in a driver entry point. Later chapters of this guide cover the implementation of these protocols in more detail.

  • Driver Binding Protocol

  • Component Name Protocol

  • Component Name 2 Protocol

  • Driver Configuration Protocol

  • Driver Configuration 2 Protocol

  • Driver Diagnostics Protocol

  • Driver Diagnostics 2 Protocol

  • HII Config Access Protocol

  • Driver Health Protocol

  • Driver Family Override Protocol

  • Driver Supported EFI Version Protocol

5.1.3.1.2 Removing protocols when a driver is unloaded

If a UEFI driver is unloadable, then the protocols that were added in the driver entry point must be removed in the driver's Unload() function using UninstallMultipleProtocolInterfaces().

TIP: Although the Unload() function is optional, uninstalling the protocols in the Unload() function of a driver is not optional. The install and uninstall sections must mirror each other for the protocols used by the driver.

TIP: The load and unload UEFI Shell commands may be used to test driver load and unload services for handles and protocols.

5.1.3.1.3 Code example

The following code fragment shows how InstallMultipleProtocolInterfaces() can be used from the entry point of a UEFI Driver to install driver related protocols. This example installs the Driver Binding Protocol, required for UEFI Drivers that follow the UEFI Driver Model, along with the Component Name 2 Protocol which is optional for UEFI Drivers that follow the UEFI Driver Model. Both protocols are installed onto the image handle passed into the entry point of the UEFI Driver, and the call to InstallMultipleProtocolInterfaces() uses GUID/Pointer pairs terminated by a NULL GUID value. Additional optional protocols could be added to this one call to InstallMultipleProtocolInterfaces() depending on a specific UEFI Driver requirements and capabilities.

Example 28-Install protocols in UEFI Driver entry point.

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/ComponentName2.h>
EFI_DRIVER_BINDING_PROTOCOL gMyDriverBinding = {
MySupported,
MyStart,
MyStop, 0x10,
NULL,
NULL
};
EFI_COMPONENT_NAME2_PROTOCOL gMyComponentName2 = {
MyGetDriverName,
MyGetControllerName,
"en"
};
EFI_STATUS Status;
EFI_HANDLE ImageHandle;
//
// Install the Driver Binding Protocol and the Component Name 2 Protocol
// onto the image handle that is passed into the driver entry point
//
Status = gBS->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gEfiDriverBindingProtocolGuid,
&gMyDriverBinding,
&gEfiComponentName2ProtocolGuid,
&gMyComponentName2,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}

The code fragment in Example 29 performs the same work as the example above, but uses the EDK II UefiLib to install the UEFI Driver Model related protocols. In this specific case, the Driver Binding Protocol, Component Name Protocol, and Component Name 2 Protocols are all installed using the UefiLib function EfiLibInstallDriverBindingComponentName2(). The Component Name Protocol and Component Name 2 Protocol implementations use the same functions for their protocol implementations, thereby reducing the size overhead for supporting both name protocols.

The EDK II UefiLib provides 4 functions that may be used to initialize a UEFI Driver that follows the UEFI Driver Model. The Component Name Protocols are declared with GLOBAL_REMOVE_IF_UNREFERENCED that guarantees the protocol structures are removed from the final binary UEFI Driver image if the EDK II build is configured to not produce the Component Name Protocols. It does not make sense to use that declaration style for the Driver Binding Protocol since that protocol must always be produced by a UEFI Driver that follows the UEFI Driver Model.

The EDK II library UefiLib uses several Platform Configuration Database (PCD) feature flags to enable and disable the Component Name Protocols at build time. Chapter 30 covers how to build UEFI Drivers in the EDK II and also covers configuration of UEFI Drivers through PCD settings.

Example 29-Install protocols in UEFI Driver entry point using UefiLib.

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/ComponentName2.h>
#include <Protocol/ComponentName.h>
#define MY_VERSION 0x10
EFI_DRIVER_BINDING_PROTOCOL gMyDriverBinding = {
MySupported, MyStart,
MyStop,
MY_VERSION,
NULL,
NULL
};
GLOBAL_REMOVE_IF_UNREFERENCED
EFI_COMPONENT_NAME_PROTOCOL gMyComponentName = {
(EFI_COMPONENT_NAME_GET_DRIVER_NAME) MyGetDriverName,
(EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) MyGetControllerName,
"eng"
};
GLOBAL_REMOVE_IF_UNREFERENCED
EFI_COMPONENT_NAME2_PROTOCOL gMyComponentName2 = {
MyGetDriverName,
MyGetControllerName,
"en"
};
EFI_STATUS Status;
EFI_HANDLE ImageHandle;
//
// Install driver model protocol(s).
//
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gMyDriverBinding,
ImageHandle,
&gMyComponentName
& gMyComponentName2
);
if (EFI_ERROR (Status)) {
return Status;
}

The code fragment below shows how the protocols installed in the previous example would be uninstalled in a UEFI Driver's Unload() function. A UEFI Driver is not required to implement the Unload() capability, but if the Unload() capability is implemented, it must undo the work performed in the entry point of the UEFI Driver just like InstallMultipleProtocolInterfaces(). UninstallMultipleProtocolInterfaces() allows multiple protocols to be specified in a single call using a set of GUID/Pointer arguments terminated by a NULL GUID value.

Example 30-Uninstall protocols in UEFI Driver Unload() function.

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/ComponentName2.h>
#include <Protocol/ComponentName.h>
EFI_STATUS Status;
EFI_HANDLE ImageHandle;
//
// Uninstall the Driver Binding Protocol and the Component Name Protocol
// from the handle that is passed into the Unload() function.
//
Status = gBS->UninstallMultipleProtocolInterfaces (
ImageHandle,
&gEfiDriverBindingProtocolGuid,
&gMyDriverBinding,
&gEfiComponentName2ProtocolGuid,
&gMyComponentName2,
&gEfiComponentNameProtocolGuid,
&gMyComponentName,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}

UEFI device drivers add protocols for I/O services to existing handles in the handle database in their Start() function and remove those same protocols from those same handles in their Stop() function.

UEFI bus drivers may add protocols to existing handles, but they are also responsible for creating handles for the child device on that bus. This responsibility means that the UEFI bus driver typically adds the EFI_DEVICE_PATH_PROTOCOL and an I/O abstraction for the bus type managed by that bus driver. For example, the PCI bus driver creates child handles with both the EFI_DEVICE_PATH_PROTOCOL and the EFI_PCI_IO_PROTOCOL. The bus driver may also optionally add the EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL to the child handles if the bus type supports a standard container for storing UEFI Drivers.

The following code fragment shows an example of a how a child handle can be added to the handle database with a Device Path Protocol and then add a Block I/O Protocol to that same child handle. These two operations could also be combined into a single call to InstallMultipleProtocolInterfaces().

Example 31-Add child handle to handle database

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DevicePath.h>
#include <Protocol/BlockIo.h>
EFI_STATUS Status;
EFI_HANDLE ChildHandle;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
//
// Add Device Path Protocol to a new handle
//
ChildHandle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&ChildHandle,
&gEfiDevicePathProtocolGuid,
DevicePath,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Add the Block I/O Protocol to the handle created in the previous call
//
Status = gBS->InstallMultipleProtocolInterfaces (
&ChildHandle,
&gEfiBlockIoProtocolGuid,
BlockIo,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}

The following code fragment below shows an example of a how the child handle created in the previous example can be destroyed by uninstalling all the installed protocols in a single call to UninstallMultipleProtocolInterfaces().

Example 32-Remove child handle from handle database.

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DevicePath.h>
#include <Protocol/BlockIo.h>
EFI_STATUS Status;
EFI_HANDLE ChildHandle;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
//
// Remove Device Path Protocol and Block I/O Protocol from the child
// handle created above. Because this call removes all the
// protocols from the handle, the handle is removed from the
// handle database.
//
Status = gBS->UninstallMultipleProtocolInterfaces (
ChildHandle,
&gEfiDevicePathProtocolGuid,
DevicePath,
&gEfiBlockIoProtocolGuid,
BlockIo,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}

A more rare use case of InstallMultipleProtocolInterfaces() is installing a protocol with a NULL protocol interface pointer. The GUID value in this case is called a tag GUID because there are no data fields or services associated with the GUID.

The code fragment below shows an example of adding a tag GUID to the handle of a controller that a UEFI Driver is managing. In this example, the tag GUID used is the GUID name of the UEFI Driver itself called gEfiCallerIdGuid.

Example 33-Add tag GUID to a controller handle.

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
EFI_STATUS Status;
EFI_HANDLE ControllerHandle;
//
// Add tag GUID called gEfiCallerIdGuid to ControllerHandle
//
Status = gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gEfiCallerIdGuid,
NULL,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}

The following code fragment shows how the tag GUID added in the previous example can be removed.

Example 34-Remove tag GUID from a controller handle.

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
EFI_STATUS Status;
EFI_HANDLE ControllerHandle;
//
// Remove tag GUID called gEfiCallerIdGuid from ControllerHandle
//
Status = gBS->UninstallMultipleProtocolInterfaces (
ControllerHandle,
&gEfiCallerIdGuid,
NULL,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}

5.1.3.2 LocateHandleBuffer()

This service retrieves a list of handles that meet a search criterion from the handle database. The following are the search options:

  • Retrieve AllHandles: Retrieve all handles in the handle database.

  • Retrieve ByProtocol: Retrieve all handles in the handle database that support a specified protocol.

  • Retrieve ByRegisterNotify: Retrieve the handle for which a specific protocol was just installed and configured for register notification using RegisterProtocolNotify(). This search option is strongly discouraged for UEFI Drivers. It was used with previous releases of the EFI Specification before the introduction of the UEFI Driver Model.

The buffer returned by LocateHandleBuffer() is allocated by the service AllocatePool(). A UEFI driver using this service is responsible for freeing the returned buffer when the UEFI driver no longer requires its contents use the service FreePool(). The following code fragment shows how all the handles in the handle database can be retrieved.

Example 35-Retrieve all handles in handle database

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
//
// Retrieve the list of all the handles in the handle database. The
// number of handles in the handle database is returned in HandleCount,
// and the array of handle values is returned in HandleBuffer which
// is allocated using AllocatePool().
//
Status = gBS->LocateHandleBuffer (
AllHandles,
NULL,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Free the array of handles allocated by gBS >LocateHandleBuffer()
//
FreePool (HandleBuffer);

The code fragment below shows how all the handles that support the Block I/O Protocol can be retrieved and how the individual Block I/O Protocol instances can be retrieved using OpenProtocol().

Example 36-Retrieve all Block I/O Protocols in handle database

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/BlockIo.h>
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
//
// Retrieve the list of handles that support the Block I/O
// Protocol from the handle database. The number of handles
// that support the Block I/O Protocol is returned in HandleCount,
// and the array of handle values is returned in HandleBuffer
// which is allocated using AlocatePool()
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiBlockIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Loop through all the handles that support the Block I/O
// Protocol, and retrieve the Block I/O Protocol instance
// from each handle.
//
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gEfiBlockIoProtocolGuid,
(VOID **)&BlockIo,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// BlockIo can be used here to make Block I/O Protocol
// service requests.
//
}
//
// Free the array of handles allocated by gBS->LocateHandleBuffer()
//
FreePool (HandleBuffer);

5.1.3.3 LocateProtocol()

This service finds the first instance of a protocol interface in the handle database. This service is typically used by UEFI drivers to retrieve service protocols on service handles that are guaranteed to have, at most, one instance of the protocol in the handle database. The UEFI Specification defines the following service protocols:

  • EFI_PLATFORM_DRIVER_OVERRIDE_PROTOCOL

  • EFI_PLATFORM_TO_DRIVER_CONFIGURATION_PROTOCOL

  • EFI_UNICODE_COLLATION_PROTOCOL

  • EFI_DEBUG_SUPPORT_PROTOCOL

  • EFI_DECOMPRESS_PROTOCOL

  • EFI_ACPI_TABLE_PROTOCOL

  • EFI_EBC_PROTOCOL

  • EFI_BIS_PROTOCOL

  • EFI_KEY_MANAGEMENT_SERVICE_PROTOCOL

  • EFI_HII_FONT_PROTOCOL

  • EFI_HII_STRING_PROTOCOL

  • EFI_HII_IMAGE_PROTOCOL

  • EFI_HII_DATABASE_PROTOCOL

  • EFI_HII_CONFIG_ROUTING_PROTOCOL

  • EFI_FORM_BROWSER2_PROTOCOL

  • EFI_USER_MANAGER_PROTOCOL

  • EFI_DEFERRED_IMAGE_LOAD_PROTOCOL - EFI_FIRMWARE_MANAGEMENT_PROTOCOL

This service also supports retrieving protocols that have been notified with RegisterProtocolNotify(), but use of RegisterProtocolNotify() is discouraged in UEFI Drivers, so this use case of LocateProtocol() is not covered. See Section 5.3.6 for more details on RegisterProtocolNotify().

The following code fragment shows how the LocateProtocol() service can be used to retrieve the first instance of a service protocol in the handle database. In this example the EFI_DECOMPRESS_PROTOCOL is used.

Example 37-Locate first Decompress Protocol in handle database

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/Decompress.h>
EFI_STATUS Status;
EFI_DECOMPRESS_PROTOCOL *Decompress;
Status = gBS->LocateProtocol (
&gEfiDecompressProtocolGuid,
NULL,
(VOID **)&Decompress
);
if (EFI_ERROR (Status)) {
return Status;
}

5.1.3.4 OpenProtocol() and CloseProtocol()

The OpenProtocol() and CloseProtocol() services are used by UEFI drivers to acquire and release the protocol interfaces from the handle database that the UEFI drivers require to produce their services. The OpenProtocol() service is one of the most complex UEFI Boot Services because it is required to support all of the various UEFI Driver types. UEFI applications and UEFI OS loaders may also use these services to lookup and use protocol interfaces in the handle database.

Caution: Proper use of 'OpenProtocol()' and 'CloseProtocol()' is required for interoperability with other UEFI components. There are UEFI Shell commands that may be used to help verify the proper use of these services including 'dh', 'connect', 'disconnect', 'reconnect', 'drivers', 'devices', 'devtree', and 'openinfo'.

OpenProtocol() is typically used by the Supported() and Start() functions of a UEFI driver to retrieve protocol interface(s) that are installed on handles in the handle database. The code, below, shows the full function prototype for the UEFI Boot Service OpenProtocol()

The CloseProtocol() service removes an element from the list of agents that are consuming a protocol interface. UEFI drivers must close each protocol they open when the UEFI Driver no longer requires the use of that protocol. Closing protocols is typically done in the Stop() function.

Example 38-OpenProtocol() function prototype

#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002
#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004
#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER 0x00000008
#define EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010
#define EFI_OPEN_PROTOCOL_EXCLUSIVE 0x00000020
/**
Queries a handle to determine if it supports a specified protocol.
If the protocol is supported by the handle, it opens the protocol on behalf of the calling agent.
@param Handle The handle for the protocol interface that is being opened.
@param Protocol The published unique identifier of the protocol.
@param Interface Supplies the address where a pointer to the corresponding Protocol Interface
is returned.
@param AgentHandle The handle of the agent that is opening the protocol interface specified by
Protocol and Interface.
@param ControllerHandle If the agent that is opening a protocol is a driver that follows
the UEFI Driver Model, then this parameter is the controller
handle that requires the protocol interface. If the agent does not
follow the UEFI Driver Model, then this parameter is optional and
may be NULL. @param Attributes The open mode of the protocol interface
specified by Handle and Protocol.
@retval EFI_SUCCESS An item was added to the open list for the protocol interface,
and the protocol interface was returned in Interface.
@retval EFI_UNSUPPORTED Handle does not support Protocol.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_ACCESS_DENIED Required attributes can't be supported in current environment.
@retval EFI_ALREADY_STARTED Item on the open list already has required attributes whose
agent handle is the same as AgentHandle.
**/
typedef
EFI_STATUS
(EFIAPI * EFI_OPEN_PROTOCOL)(
IN EFI_HANDLE Handle,
IN EFI_GUID *Protocol,
OUT VOID **Interface, OPTIONAL
IN EFI_HANDLE AgentHandle,
IN EFI_HANDLE ControllerHandle,
IN UINT32 Attributes
);

The Handle and Protocol parameters specify what protocol interface is being opened. The AgentHandle and ControllerHandle specifies who is opening the protocol interface.

The Attributes parameter specifies why a protocol interface is being opened. The Interface parameter is used to return the protocol interface if it is successfully opened, and the EFI_STATUS return code tells if the protocol interface was opened or not and if not, why it could not be opened. The UEFI core records these input parameter values to track how each protocol interface is being used. This tracking information can be retrieved through the OpenProtocolInformation() service. The EFI_STATUS code returned by OpenProtocol() is very important and must be examined by UEFI drivers that use this service. In some cases, error code such as EFI_ALREADY_STARTED may be the expected result and may not be an error at all for that specific UEFI Driver.

Caution: Make sure that all status codes returned by OpenProtocol() are properly evaluated.

AgentHandle and ControllerHandle describe "who" is opening the protocol interface. For UEFI drivers, the AgentHandle parameter is typically the DriverBindingHandle field from the EFI_DRIVER_BINDING_PROTOCOL produced by the UEFI Driver. UEFI Drivers that are device drivers producing additional protocols on the same handle typically use the same value for Handle and ControllerHandle. UEFI Drivers that are bus drivers producing child handles may use OpenProtocol() with Handle set to the handle for the bus controller and ControllerHandle set to the handle of a child controller.

The Attributes parameter is a bitmask that describes "why" the protocol interface is being opened. The #define values used to build an Attributes value are also shown in Example 38 above. They are the #define statements. A summary of the attribute combinations used by UEFI drivers is listed below.

Caution: Make sure UEFI Drivers use the attributes correctly. If the attributes are used incorrectly, a driver may not function properly and may cause problems with other drivers. There are UEFI Shell commands to help verify the proper use of attributes including dh, connect, disconnect, reconnect, drivers, devices, devtree, and openinfo. EFI_OPEN_PROTOCOL_TEST_PROTOCOL

Tests to see if a protocol interface is present on a handle. Typically used in the Supported() service of a UEFI driver if the services of the protocol being tested are not required to complete the support check.

EFI_OPEN_PROTOCOL_GET_PROTOCOL

Retrieves a protocol interface from a handle. Typically used in the Supported() and Start() services of a UEFI driver to make use of the services of a protocol that is allowed to be used by more than one UEFI Driver.

EFI_OPEN_PROTOCOL_BY_DRIVER

Retrieves a protocol interface from a handle and marks that interface so it cannot be opened by other UEFI drivers or UEFI applications unless the other UEFI driver agrees to release the protocol interface. Typically used in the Supported() and Start() services of a UEFI driver to use the services of a protocol that is not allowed to be used by more than one UEFI Driver.

EFI_OPEN_PROTOCOL_EXCLUSIVE

Typically used by UEFI Applications to gain exclusive access to a protocol interface.

If any drivers have the same protocol interface opened with an attribute of

EFI_OPEN_PROTOCOL_BY_DRIVER, then an attempt is made to remove them by calling Stop() function in that UEFI Driver. If a UEFI Driver opens a protocol interface with this attribute, no other drivers are allowed to open the same protocol interface with the EFI_OPEN_PROTOCOL_BY_DRIVER attribute. This attribute is used very rarely. TIP: For good coding practices, UEFI Drivers that require the use of the EFI_OPEN_PROTOCOL_EXCLUSIVE attribute should combine it with the EFI_OPEN_PROTOCOL_BY_DRIVER attribute.

EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE

Retrieves a protocol interface from a handle and marks the interface so it cannot be opened by other UEFI drivers or UEFI applications. This protocol is not released until the driver that opened this attribute chooses to close it. This attribute is used very rarely.

EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

Used by bus drivers. A bus driver is required to open the parent I/O abstraction on behalf of each child controller that the bus driver produces. This requirement allows the UEFI core to keep track of the parent/child relationships no matter how complex the bus hierarchies become.

EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL

Do not use from a UEFI Driver. Only provided for backwards compatibility with older versions of the EFI Specification. Use EFI_OPEN_PROTOCOL_GET_PROTOCOL instead.

5.1.3.4.1 Using EFI_OPEN_PROTOCOL_TEST_PROTOCOL

The code fragment below tests for the presence of the PCI I/O Protocol using the EFI_OPEN_PROTOCOL_TEST_PROTOCOL attribute. When this attribute is used, the protocol does not have to be closed because a protocol interface is not returned when this open mode is used.

Example 39-OpenProtocol() TEST_PROTOCOL

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/PciIo.h>
EFI_STATUS Status;
EFI_DRIVER_BINDING_PROTOCOL *This;
EFI_HANDLE ControllerHandle;
//
// Test to see if ControllerHandle supports the PCI I/O Protocol
//
Status = gBS->OpenProtocol (
ControllerHandle, // Handle
&gEfiPciIoProtocolGuid, // Protocol
NULL, // Interface
This->DriverBindingHandle, // AgentHandle
ControllerHandle, // ControllerHandle
EFI_OPEN_PROTOCOL_TEST_PROTOCOL // Attributes
);
if (EFI_ERROR (Status)) {
return Status;
}

5.1.3.4.2 Using EFI_OPEN_PROTOCOL_GET_PROTOCOL

The following code fragment shows the same example as above but retrieves the PCI I/O Protocol using the EFI_OPEN_PROTOCOL_GET_PROTOCOL attribute. With this attribute, the protocol does not have to be closed.

Example 40-OpenProtocol() GET_PROTOCOL

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/PciIo.h>
EFI_STATUS Status;
EFI_DRIVER_BINDING_PROTOCOL *This;
EFI_HANDLE ControllerHandle;
//
// Retrieve PCI I/O Protocol interface on ControllerHandle
//
Status = gBS->OpenProtocol (
ControllerHandle, // Handle
&gEfiPciIoProtocolGuid, // Protocol
NULL, // Interface
This->DriverBindingHandle, // AgentHandle
ControllerHandle, // ControllerHandle
EFI_OPEN_PROTOCOL_GET_PROTOCOL // Attributes
);
if (EFI_ERROR (Status)) {
return Status;
}

Caution: It can be dangerous to use this open mode (in which a protocol does not have to be closed) because a protocol may be removed at any time without notifying the UEFI Driver that used this mode. This means that a driver using EFIOPEN_PROTOCOL_GET_PROTOCOL may attempt to use a stale protocol interface pointer that is no longer valid._

TIP: Use EFI_OPEN_PROTOCOL_BY_DRIVER first, to prevent the protocol from being removed while a driver is using the protocol.

The EFI_OPEN_PROTOCOL_GET_PROTOCOL attribute can then be used to retrieve the needed protocol interface.

A UEFI driver should be designed to use EFI_OPEN_PROTOCOL_BYDRIVER as its first choice. However, there are cases where a different UEFI driver has already opened the protocol that is required by `EFI_OPEN_PROTOCOLBY_DRIVER. In these cases, useEFIOPEN_PROTOCOLGET_PROTOCOL. This scenario may occur when protocols are layered on top of each other so that each layer uses the services of the layer immediately below. Each layer immediately below is opened withEFIOPEN_PROTOCOL`BY_DRIVER.

If a layer needs to skip a layer to reach a lower-level service, then it is safe to use EFI_OPEN_PROTOCOL_GET_PROTOCOL because the driver is informed through the layers if the lower-level protocol is removed.

The best example of this case in the EDK II is the FAT driver. The FAT driver uses the services of the Disk I/O Protocol to access the contents of a mass storage device. However, the Disk I/O Protocol does not have a flush service. Only the Block I/O Protocol has a flush service. The Disk I/O driver opens the Block I/O Protocol EFI_OPEN_PROTOCOL_BY_DRIVER, so the FAT driver is also not allowed to open the Block I/O Protocol EFI_OPEN_PROTOCOL_BY_DRIVER. Instead, the FAT driver must use EFI_OPEN_PROTOCOL_GET_PROTOCOL. This method is safe because the FAT driver is indirectly notified if the Block I/O Protocol is removed when the Disk I/O Protocol is removed in response to the Block I/O Protocol being removed.

5.1.3.4.3 Using EFI_OPEN_PROTOCOL_BY_DRIVER

The code fragment in shows the same example as above, but it retrieves the PCI I/O Protocol using the EFI_OPEN_PROTOCOL_BY_DRIVER attribute. When this attribute is used, the protocol must be closed when the UEFI Driver no longer requires the services of the PCI I/O Protocol. This example also shows CloseProtocol() being used to close the protocol, which is commonly found in implementations of Supported() and Stop(). Notice that the parameters passed to CloseProtocol() are identical to the parameters passed to OpenProtocol() with the Interface and Attributes parameters removed.

Example 41-OpenProtocol() EFI_OPEN_PROTOCOL_BY_DRIVER

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/PciIo.h>
EFI_STATUS Status;
EFI_DRIVER_BINDING_PROTOCOL *This;
EFI_HANDLE ControllerHandle;
//
// Retrieve PCI I/O Protocol interface on ControllerHandle
//
Status = gBS->OpenProtocol (
ControllerHandle, // Handle
&gEfiPciIoProtocolGuid, // Protocol
NULL, // Interface
This->DriverBindingHandle, // AgentHandle
ControllerHandle, // ControllerHandle
EFI_OPEN_PROTOCOL_BY_DRIVER // Attributes
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Close PCI I/O Protocol on ControllerHandle
//
Status = gBS->CloseProtocol (
ControllerHandle, // Handle
&gEfiPciIoProtocolGuid, // Protocol
This->DriverBindingHandle, // AgentHandle
ControllerHandle // ControllerHandle
);
if (EFI_ERROR (Status)) {
return Status;
}

5.1.3.4.4 Using EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE

The following code fragment in shows the same example as above, but it retrieves the PCI I/O Protocol using both the EFI_OPEN_PROTOCOL_BY_DRIVER attribute and the EFI_OPEN_PROTOCOL_EXCLUSIVE attribute, which requests any other UEFI Driver that are using the PCI I/O Protocol release it.

There are only a very few instances where EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE should be used. These are cases where a UEFI driver actually wants to gain exclusive access to a protocol, even if it requires stopping other UEFI drivers to do so.
This combination of attributes is used rarely. One example in the EDK II is the debug port driver that opens the Serial I/O Protocol with the EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE attribute. This attribute allows a debugger to take control of a serial port even if it is already being used as a console device. If this device is the only console device in the system, then the user loses the only console device when the debug port driver is started.

Caution: This open mode can be dangerous if the system requires the services produced by the UEFI drivers that are stopped.

Example 42-OpenProtocol() EFI_OPEN_PROTOCOL_BY_DRIVER

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/PciIo.h>
EFI_STATUS Status;
EFI_DRIVER_BINDING_PROTOCOL *This;
EFI_HANDLE ControllerHandle;
//
// Retrieve PCI I/O Protocol interface on ControllerHandle
//
Status = gBS->OpenProtocol (
ControllerHandle, // Handle
&gEfiPciIoProtocolGuid, // Protocol
NULL, // Interface
This->DriverBindingHandle, // AgentHandle
ControllerHandle, // ControllerHandle
EFI_OPEN_PROTOCOL_BY_DRIVER | // Attributes
EFI_OPEN_PROTOCOL_EXCLUSIVE
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Close PCI I/O Protocol on ControllerHandle
//
Status = gBS->CloseProtocol (
ControllerHandle, // Handle
&gEfiPciIoProtocolGuid, // Protocol
This->DriverBindingHandle, // AgentHandle
ControllerHandle // ControllerHandle
);
if (EFI_ERROR (Status)) {
return Status;
}

EFI_OPEN_PROTOCOL_EXCLUSIVE

5.1.3.4.5 Using EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

The code fragment below shows an example that may be used by a UEFI Bus Driver that produces child handles. This specific example shows the PCI bus driver creating a child handle, opening the PCI Root Bridge I/O Protocol using the EFI_OPEN_PROTOCOL_BY_CHILD_CONROLLER attribute on behalf of a child PCI controller that the PCI bus driver created, closing the PCI Root Bridge I/O Protocol, and destroying the child handle. These operations are typically spread across the Start() and Stop() functions.

Example 43-OpenProtocol() EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/PciRootBridgeIo.h>
#include <Protocol/DevicePath.h>
#include <Protocol/PciIo.h>
EFI_STATUS Status;
EFI_DRIVER_BINDING_PROTOCOL *This;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_HANDLE ControllerHandle;
EFI_HANDLE ChildHandle;
//
// Create new child handle
//
ChildHandle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&ChildHandle,
&gEfiDevicePathProtocolGuid,
DevicePath,
&gEfiPciIoProtocolGuid,
PciIo,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Open parent PCI Root Bridge I/O Protocol
//
Status = gBS->OpenProtocol (
ControllerHandle, //Handle
&gEfiPciRootBridgeIoProtocolGuid, //Protocol
(VOID **)&PciRootBridgeIo, //Interface
This->DriverBindingHandle, //AgentHandle
ChildHandle, //ControllerHandle
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER //Attributes
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Close parent PCI Root Bridge I/O Protocol
//
Status = gBS->CloseProtocol (
ControllerHandle, // Handle
&gEfiPciRootBridgeIoProtocolGuid, // Protocol
This->DriverBindingHandle, // AgentHandle
ChildHandle // ControllerHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Destroy child handle
//
Status = gBS->UninstallMultipleProtocolInterfaces (
ChildHandle,
&gEfiDevicePathProtocolGuid,
DevicePath,
&gEfiPciIoProtocolGuid,
PciIo,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}

5.1.3.5 OpenProtocolInformation()

This service retrieves the list of agents currently using a specific protocol interface installed on a handle in the handle database. An agent may be a UEFI Driver or a UEFI Application using the services of a protocol interface. The OpenProtocol() service adds agents to the list, and the CloseProtocol() service removes agents from the list. The return buffer from this service is allocated using AllocatePool(). To prevent memory leaks, the caller must free the return buffer with FreePool() when it no longer needs it.

The UEFI Shell command openinfo uses this service to view the results from OpenProtocolInformation() for any protocol installed into the handle database. It is very useful when debugging UEFI Drivers to evaluate the state of protocols the drivers consume and produce in the handle database and to verify that the UEFI Driver is using OpenProtocol() and CloseProtocol() properly.

A UEFI Driver may use this service to find the list of controllers the UEFI Driver is managing or the list of child handles that the UEFI driver has produced in previous calls to the Start(). A UEFI Driver may also choose to keep track of this type of information itself and not use the Protocol Handler Services to retrieve this type of information.

The following code fragment uses LocateHandleBuffer() to retrieve the list of handles that support the PCI Root Bridge I/O Protocol. It then uses OpenProtocolInformation() on the first handle that supports the PCI Root Bridge I/O Protocol to retrieve information on all the agents that are using that specific PCI Root Bridge I/O Protocol instance. This example then loops through all the consumers of that PCI Root Bridge I/O Protocol and counts the number of handles that have opened the PCI Root Bridge I/O Protocol instance with an open mode of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. This open mode indicates that the agent is a child handle. The result is the total number of PCI controllers that are attached to that specific PCI Root Bridge I/O Protocol instance.

Example 44-Count child handles using OpenProtocolInformation()

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/PciRootBridgeIo.h>
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
EFI_HANDLE ControllerHandle;
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfo;
UINTN EntryCount;
UINTN Index;
UINT32 Attributes;
UINTN NumberOfChildren;
//
// Retrieve array of handles that support the USB I/O Protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiPciRootBridgeIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
if (HandleCount == 0) {
return EFI_NOT_FOUND;
}
//
// Assign ControllerHandle to the first handle in the array
//
ControllerHandle = HandleBuffer[0];
//
// Free the array of handles
//
FreePool (HandleBuffer);
//
// Retrieve information about how the PCI Root Bridge I/O Protocol
// instance on ControllerHandle is being used.
//
Status = gBS->OpenProtocolInformation (
ControllerHandle,
&gEfiPciRootBridgeIoProtocolGuid,
&OpenInfo,
&EntryCount
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Count the number child handles that are currently using the PCI Root
// Bridge I/O Protocol on ControllerHandle children
//
for (Index = 0, NumberOfChildren = 0; Index < EntryCount; Index++) {
Attributes = OpenInfo[Index].Attributes;
if ((Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
NumberOfChildren++;
}
}
//
// Free the buffer allocated by OpenProtocolInformation()
//
FreePool (OpenInfo);