Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions docs/src/rfc/text/0000-runtime-services-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# RFC: Runtime Services Support

This RFC proposes a roadmap for Runtime Services and runtime module support for the
Patina project. Runtime modules and services must exist in separate memory regions
from the monolithic Patina core and so present problems for using the existing core
and infrastructure to support. This RFC proposes that a new runtime module be spun
up from the existing Rust code and to defer other future support. This RFC leaves open
the possibility that this module be extended in the future.

## Change Log

- 10-14-25: Initial RFC created.

## Motivation

Runtime services present a unique challenge for the monolithic binary model that
Patina uses. Runtime services must exist after boot services have been unloaded,
and so it is not possible for the runtime services to exist as part of the
monolithic binary, at least without very specialized handling. Runtime services
must have a focused roadmap to align with the goals and development models of
Patina to be clear about expected investments.

## Technology Background

Currently the EDKII implementation dispatches runtime modules as part of DXE phase,
most notably RuntimeDxe which provides core services such as `SetVirtualAddressMap`
and other modules to implement more platform specific runtime services. The original
implementation on Patina provided an implementation of `SetVirtualAddressMap` within
the monolithic core, but this results in some problems on Windows boot as we will
leverage boot services memory in `SetVirtualAddressMap` which is a runtime service
and executes after ExitBootServices. Generally, this makes assumptions about OS
behavior which may not be sustainable. These were resolved in the following PRs.

- [dxe_core: Move runtime event handling into runtime module](https://github.com/OpenDevicePartnership/patina/pull/738)
- [patina_dxe_core: Switch to use external RuntimeDxe implementation](https://github.com/OpenDevicePartnership/patina/pull/769)

The approach that has been taken in the interim is to remove the Patina
implementation for now and to ask platforms to go back to the EDKII RuntimeDxe.

## Goals

1. Set short term support plan for runtime services/modules.
2. Enumerate expansion plans for runtime services in the future.

## Requirements

1. Runtime services must continue to operate as expected for short term.
2. Runtime modules must exist purely in runtime service memory.
3. Long term goals should remove dependence on EDKII runtime code.

## Unresolved Questions

- Is there a suitable type of dynamic linking that may make other options suitable
without excessive one-off infrastructure?

## Prior Art (Existing PI C Implementation)

The current C implementation consists of one primary runtime module, usually
RuntimeDxe and a variable amount of additional runtime modules. These modules are
dispatched by DXE, but will be loaded in runtime memory. The primary runtime module
should produce the runtime architecture protocol, defined in the PI spec section
12.8. This protocol allows for the core to fill in critical information for the
runtime module to implement SetVirtualAddressMap. This includes the image list of
runtime modules, events registered for virtualizing memory, and memory map
information that is not currently used. When `SetVirtualAddressMap` is called, the
runtime DXE module will use the information from the protocol it published and the
core filled to re-relocate the images, virtualizing their execution, and call all
events registered to allow drivers/libraries to virtualize any stored pointers.

## Alternatives
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may have missed it, but what is the current proposed solution versus listing of alternatives?

Copy link
Contributor Author

@cfernald cfernald Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make the plan more clear in the summary


### Create new interface for runtime architectural information

Instead of using runtime architectural protocol for the chosen driver model, a new
protocol or interface could be invented that would allow safer parsing. For example,
for the event and image lists, these could be instead maintained in a vector and
the slices provided through a protocol. This would prevent the need for unsafe logic
in walking a raw pointer based linked list. However, this would be creating a new
set of rust-to-rust interfaces that do not exist within the monolithically compiled
binaries. This both complicates the interoperability story with existing code, and
will only be applicable to this one off scenario for the time being.

### Dynamic Module Linking

An alternative to leveraging the protocol communication would be to allow dynamic
linking and direct invocation of the runtime module. This would prevent the need
for secondary structures and callbacks. However, this raises many questions and
potential toolchain problems such as support between GCC and MSVC tool chains for
dynamic linking. This would also invent additional infrastructure needed only for
runtime services.

### Runtime Service Removal

The long term goal of runtime services, instead of replacing them, should be to
remove them. Most runtime services are superfluous on modern machines. The only
runtime service that is typically invoked at runtime that cannot be replaced with
other existing mechanisms is variable services. Even so, variable services are nearly
always a simple wrapper around calls to the implementation executing in either SMM
or TrustZone. An alternative to re-implementing runtime services for Patina, would
be to simply not support runtime services at runtime, but instead provide a
specification defined interface to access variable services directly through SMM
and TrustZone.

However, this is not feasible in the short term. Any solution here would require
updates both to the UEFI specifications to support them as well as specification
of the new SMM/TrustZone interfaces, not to mention OS support. While removal of
runtime services is a desirable long term goal it does not satisfy the immediate
goals of this RFC.

### Defer Runtime Module Support

Another solution is to defer implementation of any Rust runtime module until DXE
and MM are solved and a more clear interfacing model appears, and continue to use
the EDKII implementation of RuntimeDxe in the interim. The EDKII implementation
would either be replaced by a more robust implementation of a Patina runtime or
removed in the future if runtime services are no longer required in the future.
However, as explained above a more robust solution in this space is filled with
complications and is unlikely to occur in the near future. Additionally, runtime
service removal will be a long process of standardization and OS support. Deferring
until a more complete solution may not be feasible.

## Rust Code Design

Patina should continue to use separate runtime modules which interface using the
UEFI and PI defined interfaces. This includes the system architectural protocol for
resources from the core as well as boot services for allocations and other DXE
phase calls.

However, as a stepping stone to replacement RuntimeDxe should be implementation of a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
However, as a stepping stone to replacement RuntimeDxe should be implementation of a
However, as a stepping stone to replacing RuntimeDxe, Patina should implement a

Rust driver leveraging the original code removed in the above PRs. This driver would
be implemented in the Rust driver infrastructure developed for EDKII and located in
the PatinaPkg. This module would use all existing UEFI/PI interfaces to access boot
services and communicate runtime image and event data. The responsibilities of each
module in this model are detailed below.

### Patina DXE Core

The Patina DXE core will continue to listen for the registration of the runtime
architectural protocol, and register event and image data to the protocol. This
will be updated whenever a new runtime image is loaded or a new `SetVirtualAddressMap`
callback is registered.

### Patina Runtime (new)

A new module compiled as a Rust driver in an EDKII environment, provides a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this has been discussed, but from my point of view, I'm not sure how much value this adds. I know we have the Rust code that was originally part of Patina that could be used, but, as evidenced it already had design issues and would have to be refactored into an EDKII Rust driver, which is a model we have decided has major drawbacks. From my point of view, I would just keep to using EDKII's RuntimeDxe until we have a comprehensive solution in Patina, whether it is the dynamic linking approach or removal approach. But I worry that we promote a model we don't want to promote and that we open ourselves up for bugs in a place where we want to remove support entirely. As we've noted, Rust in a single driver may only add minimal improvement (particularly a heavily protocol reliant driver such as this) and it adds risk by introducing a new implementation. My two cents would be to not incur this risk for RuntimeDxe, it doesn't feel like a valuable investment of resources. Certainly open to other opinions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I've also been having this internal debate. There are a few benefits of using a rust runtime DXE.

  1. Even if using the C-ABI to the core, it provides a foundation to write key services that could be migrated to a more robust solution later.
  2. Further reduces the C code towards a goal of having a pure Patina solution (at some point)

That being said, it is an investment and whether those benefits are worth the cost is debatable.

@makubacki should weigh in on this as well.

Copy link
Collaborator

@makubacki makubacki Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RuntimeDxe is broken out from the C DXE Core (DXE Foundation) for the same reason we're having to break it out here. It is essentially "core" code than can't exist in the DXE Core binary image. If our goal is to write "core" code in a memory safe language, that would extend to services implemented behind EFI_RUNTIME_ARCH_PROTOCOL.

The other issue is torn state with Patina DXE Core. The Patina DXE Core produces Boot Services which includes GetMemoryMap. The EFI_RUNTIME_ARCH_PROTOCOL interface is:

struct _EFI_RUNTIME_ARCH_PROTOCOL {
  EFI_LIST_ENTRY           ImageHead;              ///< A list of type EFI_RUNTIME_IMAGE_ENTRY.
  EFI_LIST_ENTRY           EventHead;              ///< A list of type EFI_RUNTIME_EVENT_ENTRY.
  UINTN                    MemoryDescriptorSize;   ///< Size of a memory descriptor that is returned by GetMemoryMap().
  UINT32                   MemoryDesciptorVersion; ///< Version of a memory descriptor that is returned by GetMemoryMap().
  UINTN                    MemoryMapSize;          ///< Size of the memory map in bytes contained in MemoryMapPhysical and MemoryMapVirtual.
  EFI_MEMORY_DESCRIPTOR    *MemoryMapPhysical;     ///< Pointer to a runtime buffer that contains a copy of
                                                   ///< the memory map returned via GetMemoryMap().
  EFI_MEMORY_DESCRIPTOR    *MemoryMapVirtual;      ///< Pointer to MemoryMapPhysical that is updated to virtual mode after SetVirtualAddressMap().
  BOOLEAN                  VirtualMode;            ///< Boolean that is TRUE if SetVirtualAddressMap() has been called.
  BOOLEAN                  AtRuntime;              ///< Boolean that is TRUE if ExitBootServices () has been called.
};

Now, we have an interface produced in RuntimeDxe describing the properties of an interface implemented in the Patina DXE Core:

  • MemoryDescriptorSize
  • MemoryDesciptorVersion
  • MemoryMapSize

Regardless of RuntimeDxe implementation details in edk2, it seems strange to leave this information open to an unknown EFI_RUNTIME_ARCH_PROTOCOL (of which might be RuntimeDxe).

That said, the runtime services logic is relatively minimal and I'd prefer to be written in a memory safe lanuage with full unit test coverage, the ability for integration level testing, and using the same underlying dependencies used by the Patina DXE Core for consistency.


We don't have a universal rule that separate .efi binaries are bad (at least while co-existing in today's PI platforms). When code can be built as a single binary and we can better leverage static safety mechanisms, we should do so, but I don't see it as extending beyond that. We simply can't do so here, right now. So, I don't see it in opposition to our stance for a single monolithic DXE Core binary, it's just the rationale doesn't apply here. Just like we'll need to build a separate monolithic MM Core binary becuse it doesn't apply there either.


All that said, for practical reasons, most people are going to use the well-known RuntimeDxe module and be fine. But, from a design perspective, I consider this an extension to the "DXE Foundation" and it is within the spirit of our work that the extension also have a Rust solution available.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what it's worth, we aren't actually filling any of the memory map fields today as they are not actually used. The only information we are providing is the image and event lists.

Overall, I agree that switching to a rust implementation of RuntimeDxe seems reasonable, and I think its fine to put as the point of record with this RFC, though I'm not concerned with this being accomplished in any immediate timeframe and the cost/reward seems limited.

compatible implementation as RuntimeDxe to implement core functions, i.e.
`SetVirtualAddressMap` & `ConvertPointer`. This driver will install the runtime
architectural protocol. On `SetVirtualAddressMap` the module will be responsible for
re-relocating images and invoking all appropriate events filled in the
architectural protocol.

### Refactoring Relocation Logic (optional)

The logic necessary to relocate the PE is currently split across multiple modules
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The logic necessary to relocate the PE is currently split across multiple modules
The logic necessary to relocate PE images is currently split across multiple modules

in patina_dxe_core. To reduce code duplication, it may be desirable to refactor the
PE loader logic into a standalone crate, e.g. `patina_loader`. Without this,
relocation and PE parsing logic may need to be duplicated.

## Guide-Level Explanation

Does not change any consumable rust APIs.