UEFI Timer Events for Asynchronous Driver Tasks

The Unified Extensible Firmware Interface (UEFI) environment is inherently single-threaded, but drivers frequently require background polling, timeouts, or periodic state checks without stalling the boot process. UEFI addresses this by providing an event-driven cooperative multitasking model managed by Boot Services. By leveraging timer events and Task Priority Levels (TPL), UEFI drivers can register non-blocking callback functions that the firmware invokes at regular intervals. This article explains how UEFI timer events are created, scheduled, and dispatched to execute asynchronous periodic tasks efficiently.

The UEFI Execution Model and Events

Unlike traditional operating systems, UEFI does not feature a preemptive thread scheduler. Instead, it relies on an event architecture governed by interrupts and Task Priority Levels (TPL_APPLICATION, TPL_CALLBACK, TPL_NOTIFY, and TPL_HIGH_LEVEL).

Asynchronous tasks are represented as EFI_EVENT objects. Drivers register a notification function (a callback) and a context pointer with the event. When the event is signaled, the UEFI core schedules the notification function to run at a specified TPL. Execution is cooperative: a callback will only interrupt code running at a lower TPL, and no two tasks at the same TPL can execute concurrently on a single core.

Creating a Timer Event

To set up an asynchronous task, a driver first initializes an event using the CreateEvent or CreateEventEx boot service.

When calling CreateEvent, the driver sets the Type parameter to include both EVT_TIMER and EVT_NOTIFY_SIGNAL. The driver also specifies:

  • NotifyTpl: Typically TPL_CALLBACK for standard driver tasks, ensuring it runs above general application code but below critical hardware notifications.
  • NotifyFunction: The pointer to the driver routine that handles the periodic work.
  • NotifyContext: A pointer to driver-defined private data (such as a device context structure).
EFI_STATUS Status;
EFI_EVENT  PeriodicEvent;

Status = gBS->CreateEvent (
                EVT_TIMER | EVT_NOTIFY_SIGNAL,
                TPL_CALLBACK,
                DriverPeriodicCallback,
                DeviceContext,
                &PeriodicEvent
                );

Scheduling Periodic Execution with SetTimer

Once the event is created, it remains dormant until armed using the SetTimer boot service. To run tasks repeatedly, the driver configures the timer with the TimerPeriodic type.

Status = gBS->SetTimer (
                PeriodicEvent,
                TimerPeriodic,
                10000000 // 1 second in 100-nanosecond units
                );

The trigger time is specified in 100-nanosecond units. Setting the trigger time to 10,000,000 causes the event to fire once every second.

How UEFI Dispatches Periodic Tasks

  1. Hardware Interrupts: The underlying platform firmware sets up a hardware timer interrupt (such as an APIC timer, HPET, or platform-specific counter) operating at TPL_HIGH_LEVEL.
  2. Tick Tracking: Every time the hardware timer interrupt fires, the firmware updates its internal tick counter and evaluates all active timer events created with EVT_TIMER.
  3. Signaling: When an event's trigger threshold is reached, the core marks the event as signaled. For TimerPeriodic events, the firmware automatically adds the interval to the event's next trigger target.
  4. Queueing and Dispatch: The notification function is placed into the execution queue for its designated TPL. As soon as the processor returns to or drops below that TPL, the firmware invokes DriverPeriodicCallback(Event, Context).

Best Practices and Constraints

Because UEFI multitasking is cooperative, periodic callbacks must be lightweight. A callback that takes too long to execute or performs synchronous blocking I/O will starve lower-priority tasks and degrade system responsiveness.

Additionally, drivers must clean up timer resources before exiting or unloading:

  • Call SetTimer with TimerCancel to disarm the timer.
  • Call CloseEvent to free the event structure and remove pending notifications from the dispatch queue.