Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Patina UEFI Services

Patina UEFI Services are defined in patina::component::service::uefi_services.

They give components an idiomatic Rust way to use the services defined in the UEFI Specification, mainly Boot Services and, over time, Runtime Services. The same pattern could be extended to other specification defined interfaces a component might need, such as DXE Services from the Platform Initialization (PI) Specification, but none are provided today.

This page does not document what each service does. See Component Interface for that, or go straight to a service's reference in the Summary below. This page is about when a component should use UEFI Services.

What they are for

If you already know your design mandates cooperation with C drivers or must fulfill certain requirements, such as registering for an event, installing a protocol, or connecting a controller, Patina UEFI Services are the answer for doing the same thing in a Patina component. They give you an obvious, idiomatic interface for a specification defined operation, a Result instead of an EFI_STATUS, a closure instead of a raw notify function pointer, a typed protocol reference instead of a *mut c_void. They intentionally track closely to the C APIs with idiomatic patterns while taking advantage of Rust's type system and safety guarantees where possible.

They do not change what the operation means on the specification side, though. Installing a protocol through ProtocolServices still puts a real protocol into the global UEFI handle database, where any C driver can find and open it. An EventServices callback still runs at the TPL passed when the event is created, with the same semantics as in C. That interoperability is both the point and potential danger of UEFI Services. While Patina improves safety at the component interface level, it does not change the nature of those operations. Because of that, Patina UEFI Services are always ultimately bound to their C-oriented specification, and should be used only when a design requires that touchpoint, not as a general way for two Patina components to talk to each other.

When a design requires them

These are examples of when a design might call for UEFI Services:

  • A C driver signals a notification a component cares about, an event group like End of DXE or Ready To Boot, for example. Registering a callback in the event database, through EventServices, is the only way to find out.
  • A component needs to publish a protocol that C drivers, or other UEFI aware code, expect to find. Installing it through ProtocolServices is not optional once that requirement exists.
  • Locating a controller and connecting a driver to it is part of the UEFI driver model, which lives at the protocol and handle level. That is what DriverServices is for.

In each case, the problem itself requires the service.

When a design chooses them

Other times, nothing forces the decision. A component could keep a piece of functionality entirely inside the Patina component model, behind an ordinary Service<T>, but instead chooses to publish it as a UEFI protocol or event so that C code, or other Rust code that only knows the specification defined interface, can use it too. This is a reasonable choice when broader reuse is valuable. In those cases, it may also be possible to publish a complementary Patina service, so that other Patina components can use the same functionality without going through the UEFI interface. That option will vary in usefulness and safety depending on the design.

Note

Publishing through a UEFI Service means giving something up. Whatever you install or store will reside outside of Rust's ownership model, exposed and shared with C code through global, specification defined structures like the handle database or an event's callback list. The compiler can no longer track who owns it or when it is safe to change.

When you do not need them

If a design is Rust from top to bottom, with nothing that has to be exposed to or consumed from C code and there are no pre-existing specification obligations (such as protocol requirements) to fulfill, Patina UEFI Services may not be needed. Two Patina components can share functionality and control dispatch order using the regular component model instead, a Service<T> for shared functionality, Config<T> or ConfigMut<T> for shared configuration, and Commands to register either one. None of that needs to go through a UEFI protocol, event, or table. It is simpler, it keeps everything inside Rust's ownership model, and dependency injection already handles ordering for you.

---
config:
  layout: elk
  look: classic
  flowchart:
    nodeSpacing: 80
    rankSpacing: 100
  elk:
    direction: DOWN
    spacing: 100
---
flowchart TD
    A[Does the design need to interact with a C driver or an existing UEFI/PI defined interface?] -->|Yes, the spec requires it| B[Use the relevant Patina UEFI Service]
    A -->|No| C{Do you still want to expose this to C code, or other spec aware Rust code, by choice?}
    C -->|Yes| B
    C -->|No, everything stays in Rust| D[Use the regular component model: Service, Config, Commands]

Over time, as more code is implemented in Rust and Patina components, the reliance on compatibility outside the Patina component model may decrease. However, the need for UEFI Services and compatibility with existing C-based access patterns will likely remain important for the foreseeable future.

Summary

Patina UEFI Services are grouped by function, so a component can depend on only the piece it actually uses. Each one is a normal Service<T> parameter, consumed the same way as any other service.

ServiceCoversReference
ConfigurationTableServicesInstalling and looking up UEFI configuration tablesdocs.rs
DriverServicesConnecting and disconnecting drivers and controllersdocs.rs
EventServicesCreating events and registering closures as notificationsdocs.rs
ImageServicesLoading, starting, and unloading UEFI imagesdocs.rs
ProtocolServicesInstalling, locating, and opening protocolsdocs.rs
TimerEventServicesScheduling timer events, once or on a repeating intervaldocs.rs
TimingServicesStalling and the watchdog timerdocs.rs
TplServicesRaising and restoring Task Priority Leveldocs.rs

CRC-32 checksum calculation, matching the UEFI CalculateCrc32 boot service is available as a stateless computation with no dispatch-ordering requirement, so components and the DXE Core call the plain function patina::crc32::calculate_crc32 directly instead.