How to Load and Unload UEFI Drivers in UEFI Shell

The Unified Extensible Firmware Interface (UEFI) Shell provides an interactive pre-boot environment that allows firmware and hardware engineers to execute commands, manage storage, and test binaries. This article explains how developers dynamically load and unload custom UEFI test drivers using native UEFI Shell commands, verify driver binding, and manage handle databases without rebuilding or reflashing the system firmware.

Preparing the Environment

Before managing test drivers dynamically, you must access the UEFI Shell and prepare the compiled driver file (.efi):

  1. Build the Driver: Compile your driver using an environment such as the EDK II (EFI Development Kit II) targeting your platform's architecture (typically X64 or AARCH64).

  2. Transfer to Media: Copy the resulting .efi binary onto a FAT32-formatted USB drive or a dedicated EFI System Partition (ESP).

  3. Boot into the Shell: Boot your target system or virtual machine (e.g., QEMU with OVMF) into the UEFI Shell.

  4. Navigate to the File System: Identify the mapped drive corresponding to your storage device by running map -r. Change to that drive and verify the file exists:

    fs0:
    ls

Dynamically Loading a Driver

To load an executable UEFI driver into system memory, use the load command.

load MyTestDriver.efi

When this command runs, the UEFI Boot Service LoadImage() loads the binary into memory, and StartImage() executes its entry point. If the driver is an EFI Driver Model-compliant driver, the entry point installs the EFI_DRIVER_BINDING_PROTOCOL onto the driver's image handle.

If the operation is successful, the shell outputs a confirmation showing the assigned handle:

load: Image 'fs0:\MyTestDriver.efi' loaded at [Handle] - Success

To automatically connect the newly loaded driver to compatible controllers, run:

connect -r

Verifying and Inspecting the Driver

Once loaded, you must verify that the driver initialized correctly and bound to the intended hardware handles.

  • List Installed Drivers: Use the drivers command to see all active drivers and find your driver's Handle ID:

    drivers
  • Inspect Handles and Protocols: Use the dh (Dump Handle) command with your driver's handle identifier (e.g., B4) to inspect the protocols it produces:

    dh -v B4
  • Verify Device Binding: Check which devices the driver is managing using the devices command:

    devices

Dynamically Unloading a Driver

Unloading a driver allows developers to test deinitialization routines, ensure memory cleanup, and test code revisions without rebooting.

To unload a driver, pass its handle identifier to the unload command:

unload B4

When unload executes:

  1. The shell calls the driver's Stop() function on all managed controller handles.
  2. The driver uninstalls any installed protocols and frees allocated pools and pages.
  3. The shell calls UnloadImage() to purge the binary from memory.

To force an unload if a device handle remains bound, you can first manually disconnect the driver:

disconnect [DeviceHandle] [DriverHandle]
unload [DriverHandle]

Common Error Codes

  • EFI_NOT_FOUND / EFI_UNSUPPORTED: Often occurs when required protocol dependencies are missing from the system.
  • EFI_ALREADY_STARTED: Occurs when attempting to bind a driver to a controller handle that is already being managed by an active driver.
  • EFI_ACCESS_DENIED: Typically returned during an unload operation if the driver does not implement an Unload pointer in its EFI_LOADED_IMAGE_PROTOCOL, preventing safe removal.