How UEFI Drivers Implement the UNDI Specification

This article explains how network interface card (NIC) drivers implement the UEFI Universal Network Driver Interface (UNDI) specification. It covers the architectural relationship between UNDI and the UEFI network stack, the implementation of core protocol interfaces like the Network Interface Identifier Protocol, hardware abstraction techniques, command processing workflows, and buffer management required for pre-boot network communication.

Architectural Role of UNDI in UEFI

In the UEFI networking architecture, UNDI acts as the hardware-dependent device driver layer, abstracting physical NIC hardware away from generic upper-layer networking protocols. It sits below the Simple Network Protocol (SNP) and the Managed Network Protocol (MNP).

Rather than requiring the UEFI firmware to contain dedicated drivers for every network controller, the NIC vendor provides an Option ROM or integrated firmware image implementing UNDI. A generic UEFI SNP driver then consumes this UNDI interface, exposing a standardized EFI_SIMPLE_NETWORK_PROTOCOL to upper layers such as IPv4/IPv6, PXE, and HTTP boot services.

The Network Interface Identifier (NII) Protocol

The bridge between UEFI driver management and the UNDI runtime is the EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL (commonly referred to as NII, supported in 31-bit and 64-bit variants like EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_31).

When a NIC driver initializes, it:

  1. Implements standard EFI_DRIVER_BINDING_PROTOCOL functions (Supported(), Start(), and Stop()).
  2. Probes the PCI bus using EFI_PCI_IO_PROTOCOL to match supported vendor and device IDs.
  3. Allocates and initializes the !PXE interface parameter block.
  4. Installs the EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL on the controller handle, publishing pointers to the entry point and state data.

The UNDI Entry Point and Command Execution Model

Unlike modern operating system drivers that rely on asynchronous hardware interrupts, UEFI operates in a synchronous, polling-based single-threaded execution environment. UNDI implements this model through a single entry point called the API Entry Point.

When upper layers (such as the generic SNP driver) need the NIC to perform an action, they execute the UNDI entry point with a pointer to a Command Descriptor Block (CDB). The driver processes requests via a standardized dispatch table matching predefined opcodes:

  • PXE_OPCODE_START / STOP: Initializes or de-initializes the driver's software state and basic resource tracking.
  • PXE_OPCODE_OPEN / CLOSE: Powers up the physical transceiver (PHY), initializes link negotiation, sets the MAC address, and programs receive filters.
  • PXE_OPCODE_INITIALIZE / SHUTDOWN: Configures memory buffers, ring descriptors, and DMA mappings for hardware access.
  • PXE_OPCODE_TRANSMIT: Accepts a packet buffer address, populates the hardware transmit (TX) descriptor ring, and rings the hardware doorbell/tail register.
  • PXE_OPCODE_RECEIVE: Queries the hardware receive (RX) ring for newly arrived frame descriptors, copies or maps the payload into the caller-provided buffer, and advances the ring index.
  • PXE_OPCODE_GET_STATUS: Clears interrupt status registers and reports link state (up/down, speed, duplex) and TX/RX completion queues.

Direct Memory Access (DMA) and Buffer Management

Because UEFI runs in flat physical memory mode with identity mapping (or specific IOMMU configurations depending on platform security), UNDI drivers must handle DMA transactions using the EFI_PCI_IO_PROTOCOL.

  1. Mapping: Buffers passed to UNDI from external callers are mapped using EFI_PCI_IO_PROTOCOL.Map() with appropriate operation types (EfiPciIoOperationBusMasterRead, EfiPciIoOperationBusMasterWrite, or EfiPciIoOperationBusMasterCommonBuffer).
  2. Ring Allocation: Descriptor rings and hardware-shared structures are allocated via EFI_PCI_IO_PROTOCOL.AllocateBuffer() to guarantee proper hardware-accessible memory alignment and cache attributes.
  3. Unmapping: Once transmission is acknowledged or a receive buffer is handled, drivers unmap the bus addresses using EFI_PCI_IO_PROTOCOL.Unmap() to enforce memory coherence across platforms.

Polling and Event Handling

Because interrupts are generally disabled during UEFI runtime boot phases, the driver does not register an interrupt service routine (ISR). Instead, upper-layer protocols periodically poll UNDI via PXE_OPCODE_GET_STATUS or through a periodic timer event created via CreateEvent(). The driver updates descriptor pointers, checks transmit completion rings, acknowledges status registers, and services the receive engine entirely on demand.

OS Handoff and Cleanup

When an operating system kernel assumes control of the system, UEFI calls ExitBootServices(). The UNDI driver registers an exit-event handler that shuts down the NIC's DMA engines, disables hardware interrupt generation, and resets PHY/MAC controllers to a pristine state so the operating system's native driver can initialize the device without encountering resource conflicts.