Chapter 4: UEFI Driver Model
Understanding how UEFI drivers manage devices and provide services.
Overview
When to Use the Driver Model
Use the UEFI Driver Model when you need to:
- Manage hardware devices (storage controllers, network cards, graphics adapters)
- Provide services that persist throughout boot (remain loaded after entry point returns)
- Create reusable device abstractions that work across different platforms
- Implement hot-plug support where devices can be connected/disconnected
| Scenario | Use Driver Model? | Reason |
|---|---|---|
| PCI network card driver | Yes | Hardware management, stays resident |
| USB mass storage driver | Yes | Device binding, hot-plug support |
| One-time diagnostic tool | No | Use UEFI_APPLICATION instead |
| Boot menu application | No | Runs once and exits |
| Platform initialization | Maybe | Use DXE_DRIVER (no binding) for services |
| Runtime variable access | Yes | Use DXE_RUNTIME_DRIVER |
Common Driver Development Scenarios:
- OEM/ODM: Writing drivers for custom hardware on your platform
- IHV (Independent Hardware Vendor): Creating drivers for add-in cards (GPUs, NICs, RAID controllers)
- Platform developers: Initializing SoC peripherals during DXE phase
- Firmware engineers: Porting existing drivers to new platforms
What is a UEFI Driver?
A UEFI Driver is code that:
- Manages one or more devices or provides services
- Stays resident in memory after loading
- Installs protocols on handles for other code to use
- Follows the UEFI Driver Model for device binding
flowchart TB
subgraph UEFI Driver Lifecycle
LOAD[Driver Loaded]
ENTRY[Entry Point Called]
BIND[Binding Protocol Installed]
CONN[Start Called - Device Connected]
RUN[Driver Running]
DISC[Stop Called - Device Disconnected]
UNL[Driver Unloaded]
end
LOAD --> ENTRY
ENTRY --> BIND
BIND --> CONN
CONN --> RUN
RUN --> DISC
DISC --> UNL
style ENTRY fill:#e74c3c,color:#fff
style CONN fill:#2ecc71,color:#fff
style RUN fill:#3498db,color:#fff
Driver vs Application
| Aspect | Application | Driver |
|---|---|---|
| Purpose | Perform a task, then exit | Manage devices, stay resident |
| Entry Point | UefiMain |
DriverEntryPoint |
| MODULE_TYPE | UEFI_APPLICATION |
UEFI_DRIVER or DXE_DRIVER |
| Memory | Released when app exits | Stays allocated |
| Protocols | Consumer (uses protocols) | Producer (installs protocols) |
| Lifetime | Runs once | Until Stop() or unload |
Driver Types
| Type | MODULE_TYPE | Phase | Description |
|---|---|---|---|
| UEFI Driver | UEFI_DRIVER |
BDS | Follows UEFI Driver Model (binding) |
| DXE Driver | DXE_DRIVER |
DXE | Platform initialization, no binding |
| DXE Runtime | DXE_RUNTIME_DRIVER |
DXE→RT | Survives ExitBootServices |
| SMM Driver | DXE_SMM_DRIVER |
DXE | Runs in System Management Mode |
| PEIM | PEIM |
PEI | Pre-EFI Initialization Module |
The Driver Binding Protocol
UEFI drivers implement three key functions:
flowchart LR
subgraph Driver Binding Protocol
SUP[Supported]
START[Start]
STOP[Stop]
end
CTRL[Controller Handle]
CTRL --> |"Can you manage this?"| SUP
SUP --> |Yes| START
START --> |"Create child handles"| PROD[Protocols Installed]
PROD --> |"Release device"| STOP
style SUP fill:#f39c12,color:#fff
style START fill:#2ecc71,color:#fff
style STOP fill:#e74c3c,color:#fff
Initialization
Driver Entry Point
The entry point installs the Driver Binding Protocol:
EFI_STATUS
EFIAPI
MyDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Install Driver Binding Protocol
//
Status = EfiLibInstallDriverBindingComponentName2(
ImageHandle,
SystemTable,
&gMyDriverBinding,
ImageHandle,
&gMyComponentName,
&gMyComponentName2
);
return Status;
}
The Driver Binding Structure
EFI_DRIVER_BINDING_PROTOCOL gMyDriverBinding = {
MyDriverSupported, // Supported() function
MyDriverStart, // Start() function
MyDriverStop, // Stop() function
0x10, // Version (higher = preferred)
NULL, // ImageHandle (filled by install)
NULL // DriverBindingHandle (filled by install)
};
Supported() Function
Determines if this driver can manage a controller:
EFI_STATUS
EFIAPI
MyDriverSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
EFI_PCI_IO_PROTOCOL *PciIo;
//
// Check if this controller has a protocol we need
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Check if this is our device (e.g., check Vendor/Device ID)
//
// ... device-specific checks ...
//
// Close protocol - we're just checking, not using yet
//
gBS->CloseProtocol(
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return EFI_SUCCESS; // or EFI_UNSUPPORTED
}
Start() Function
Takes control of the device and installs protocols:
EFI_STATUS
EFIAPI
MyDriverStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
MY_DEVICE_DATA *DeviceData;
//
// Allocate device context
//
DeviceData = AllocateZeroPool(sizeof(MY_DEVICE_DATA));
if (DeviceData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Initialize device
//
DeviceData->Signature = MY_DEVICE_SIGNATURE;
DeviceData->ControllerHandle = ControllerHandle;
//
// Open required protocols
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiPciIoProtocolGuid,
(VOID **)&DeviceData->PciIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
//
// Install our protocol
//
Status = gBS->InstallProtocolInterface(
&ControllerHandle,
&gMyProtocolGuid,
EFI_NATIVE_INTERFACE,
&DeviceData->MyProtocol
);
return Status;
}
Stop() Function
Releases the device and cleans up:
EFI_STATUS
EFIAPI
MyDriverStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
)
{
EFI_STATUS Status;
MY_PROTOCOL *MyProtocol;
MY_DEVICE_DATA *DeviceData;
//
// Get our protocol to find device context
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gMyProtocolGuid,
(VOID **)&MyProtocol,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Get device data from protocol
//
DeviceData = MY_DEVICE_DATA_FROM_PROTOCOL(MyProtocol);
//
// Uninstall our protocol
//
Status = gBS->UninstallProtocolInterface(
ControllerHandle,
&gMyProtocolGuid,
&DeviceData->MyProtocol
);
//
// Close protocols we opened
//
gBS->CloseProtocol(
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
//
// Free device context
//
FreePool(DeviceData);
return EFI_SUCCESS;
}
Configuration
INF File for Drivers
[Defines]
INF_VERSION = 0x00010017
BASE_NAME = MyDriver
FILE_GUID = 12345678-1234-1234-1234-123456789abc
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = MyDriverEntryPoint
[Sources]
MyDriver.c
MyDriver.h
ComponentName.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiDriverEntryPoint
UefiBootServicesTableLib
UefiLib
MemoryAllocationLib
BaseMemoryLib
DebugLib
[Protocols]
gEfiDriverBindingProtocolGuid ## PRODUCES
gEfiComponentNameProtocolGuid ## PRODUCES
gEfiComponentName2ProtocolGuid ## PRODUCES
gEfiPciIoProtocolGuid ## CONSUMES
gMyProtocolGuid ## PRODUCES
[Depex]
TRUE
MODULE_TYPE Options
| MODULE_TYPE | Description | Entry Library |
|---|---|---|
UEFI_DRIVER |
UEFI driver with binding | UefiDriverEntryPoint |
DXE_DRIVER |
DXE phase driver | UefiDriverEntryPoint |
DXE_RUNTIME_DRIVER |
Runtime driver | UefiDriverEntryPoint |
DXE_SMM_DRIVER |
SMM driver | SmmDriverEntryPoint |
Dependency Expressions (DEPEX)
Control driver load order:
[Depex]
gEfiPciRootBridgeIoProtocolGuid AND
gEfiVariableArchProtocolGuid
Common DEPEX patterns:
| Pattern | Meaning |
|---|---|
TRUE |
No dependencies, load anytime |
gProtocolGuid |
Wait for protocol |
A AND B |
Wait for both |
A OR B |
Wait for either |
Porting Guide
Platform Considerations
| Platform | Notes |
|---|---|
| x64 | Standard calling convention (MS ABI) |
| ARM64 | AAPCS64 calling convention |
| RISCV64 | RISC-V calling convention |
Hardware Abstraction
Create a Hardware Abstraction Layer (HAL) for portability:
// Platform-independent interface
typedef struct _MY_HW_INTERFACE {
EFI_STATUS (*ReadRegister)(UINT32 Offset, UINT32 *Value);
EFI_STATUS (*WriteRegister)(UINT32 Offset, UINT32 Value);
} MY_HW_INTERFACE;
// x64 PCI implementation
EFI_STATUS
X64PciReadRegister(
UINT32 Offset,
UINT32 *Value
)
{
return PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, 0, Offset, 1, Value);
}
Runtime Driver Considerations
For DXE_RUNTIME_DRIVER:
// Convert pointers when OS calls SetVirtualAddressMap
VOID
EFIAPI
MyDriverVirtualAddressChange (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EfiConvertPointer(0, (VOID **)&gMyDeviceBase);
EfiConvertPointer(0, (VOID **)&gRT);
}
// Register for virtual address change notification
gBS->CreateEventEx(
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
MyDriverVirtualAddressChange,
NULL,
&gEfiEventVirtualAddressChangeGuid,
&VirtualAddressChangeEvent
);
Example: Minimal Driver
MinimalDriver.c
/** @file
Minimal UEFI Driver Example
Demonstrates the basic structure of a UEFI driver with
Driver Binding Protocol implementation.
Copyright (c) 2024, UEFI Guide Contributors.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Protocol/DriverBinding.h>
//
// Driver Binding Protocol functions
//
EFI_STATUS
EFIAPI
MinimalDriverSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
DEBUG((DEBUG_INFO, "MinimalDriver: Supported() called\n"));
// For demonstration, support no controllers
return EFI_UNSUPPORTED;
}
EFI_STATUS
EFIAPI
MinimalDriverStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
DEBUG((DEBUG_INFO, "MinimalDriver: Start() called\n"));
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
MinimalDriverStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
)
{
DEBUG((DEBUG_INFO, "MinimalDriver: Stop() called\n"));
return EFI_SUCCESS;
}
//
// Driver Binding Protocol instance
//
EFI_DRIVER_BINDING_PROTOCOL gMinimalDriverBinding = {
MinimalDriverSupported,
MinimalDriverStart,
MinimalDriverStop,
0x10, // Version
NULL, // ImageHandle
NULL // DriverBindingHandle
};
/**
Driver entry point.
**/
EFI_STATUS
EFIAPI
MinimalDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
DEBUG((DEBUG_INFO, "MinimalDriver: Entry point\n"));
//
// Install Driver Binding Protocol
//
Status = gBS->InstallMultipleProtocolInterfaces(
&ImageHandle,
&gEfiDriverBindingProtocolGuid,
&gMinimalDriverBinding,
NULL
);
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_ERROR, "MinimalDriver: Failed to install binding: %r\n", Status));
}
return Status;
}
UEFI Specification Reference
- UEFI Spec Section 11: Protocols - UEFI Driver Model
- UEFI Spec Section 11.1: Driver Binding Protocol
- UEFI Spec Section 11.2: Platform to Driver Configuration
- UEFI Spec Section 11.3: Driver Diagnostics
- UEFI Spec Section 11.4: Component Name Protocol
Summary
- Drivers stay resident and manage devices via protocols
- Driver Binding Protocol has three functions: Supported, Start, Stop
- Supported() checks if driver can manage a controller
- Start() takes control and installs protocols
- Stop() releases device and cleans up
- MODULE_TYPE determines driver category and entry point library
Next Steps
- Chapter 5: Protocols and Handles - Deep dive into protocol architecture
- Chapter 6: Memory Services - Memory allocation in UEFI
Source Code: See
examples/UefiGuidePkg/DriverExample/for complete driver examples.