Skip to content

Commit c4f2db5

Browse files
committed
First draft of RFC
1 parent ad69df0 commit c4f2db5

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# RFC: Fixed PCD and `Config<T>` Interop
2+
3+
*This RFC is in a draft state and will be updated with additional details. Prototyping is set to begin next week (week of 9/15).*
4+
5+
This RFC proposes a translation mechanism to allow fixed PCDs to be converted into component-friendly `Config<T>`s without directly introducing the concept of PCDs into Patina. The conversion mechanism will in the form of an EDK2 build system plugin and a Patina component connected through a crate which defines a shared macro. Of note:
6+
- The proposed mechanism will not require custom Cargo build support, and will not require Patina re-compilation when configuration values are changed.
7+
- The proposed mechanism will not require Patina re-compilation when configuration values are changed.
8+
- Patina will be unaware of EDK2 build system and PCD name/namespace specifics
9+
- Platform C-code (apart from what is autogenerated), will be unaware of Patina configuration structures
10+
11+
At a high level, the basic flow for using PCD-defined values in Patina components will be as follows:
12+
13+
1. Configuration structs can be marked with `#[derive(ExternallyConfigurable)]`, indicating they can be configured via an external build system.
14+
2. On the EDK2-side, minimal per-Component `.inf` files will define mappings between marked configuration structure fields and fixed PCDs.
15+
16+
```yaml
17+
# MyComponent.inf
18+
19+
[Defines]
20+
INF_VERSION = 0x00010005
21+
BASE_NAME = MyComponent
22+
MODULE_TYPE = PATINA_COMPONENT
23+
24+
[ComponentConfigurations]
25+
MyConfigStruct.Field1 = PcdNameSpace.PcdName
26+
```
27+
28+
3. Using a top-level `Cargo.lock`, a Rust EDK2 build system extension will locate all marked structures within Patina-related source files and create instances of these structs populated with the values of the mapped PCDs.
29+
4. The build system extension will add these to a HOB via an auto-generated PEIM.
30+
5. A Patina `ExternalConfiguration` component will process this HOB and generate `Config<T>`s as appropriate.
31+
32+
More detailed information on each step can be found in the "Code Design" section of this document.
33+
34+
## Change Log
35+
36+
This text can be modified over time. Add a change log entry for every change made to the RFC.
37+
38+
- 2025-09-08: Initial RFC created.
39+
40+
## Motivation
41+
42+
The ability to statically configure Components is vital for facilitating Component-sharing between platforms that require divergent behavior. Furthermore, allowing this configuration to be global to not just Patina but to the build system Patina is incorporated within ensures configuration parity between Patina and non-Patina UEFI components. While, as present, these configurations could be manually transfered, this process is error prone, especially as configuration changes may occur in EDK2 within Git submodules.
43+
44+
EDK2 largely achieves static configuration through fixed PCDs that have their values compiled into the C-based modules that request them via declarations in `.inf`, `.dsc`, and `.dec` files. Fixed PCDs meet the challenge of configuring modules for a potentially wide array of platforms well by providing a hierarchical model where PCDs can be scoped and overwritten as needed. As directly adding support for fixed PCDs into the cargo build system would be non-idiomatic for Rust and how Crate repositories generally work (see "Alternatives") section, providing a level of indirection by which the EDK2 build system can publish Rust-compatible configurations based on PCD values provides a middle-ground in which Patina requires no knowledge of the EDK2 build system (outside of the Rust build plugin) or of PCD-specifics.
45+
46+
## Technology Background
47+
48+
Fixed PCDs allow the EDK2 build system to compile constant values into UEFI modules. These values can be of types `u8`, `u16`, `u32`, `u64`, `bool`, or byte-arrays. Information on fixed PCDs and PCDs in general can be found [here](https://github.com/tianocore/tianocore.github.io/wiki/PCD) and in the [EDK2 Build Specification](https://tianocore-docs.github.io/edk2-BuildSpecification/release-1.28/).
49+
50+
In order to read and populate structs defined in crates containing configuration structures, cross-crate reflection will be required. While the exact approach, this can be achieved with a combination of the [`cargo rustdoc --`](https://dirname.github.io/rust-std-doc/cargo/commands/cargo-rustdoc.html) command and by either populating a byte-compatible `[u8; N]` or by compiling the EDK2 build tool at build-time, using [procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html) and the [syn](https://docs.rs/syn/latest/syn/) crate as needed.
51+
52+
## Goals
53+
54+
The goal of this RFC is to create a translation layer that allows fixed PCDs to be used by Patina Components. This layer should:
55+
1. Be automatic and minimize potential for mismatched or incomplete configuration by emitting build-time errors.
56+
2. Be ergonomic and require minimal user interaction.
57+
3. Be EDK2-unaware from Patina's perspective.
58+
- The interaction between the EDK2 build tool and Patina should be standardized and build-system agnostic, potentially allowing seamless integration with other build systems as well
59+
4. Be efficient and avoid unnecessary duplication of large shared configuration parameters.
60+
5. Provide a mechanism for evaluating whether a given configuration state is valid and should be installed as a `Config<T>`.
61+
62+
## Requirements
63+
64+
1. Configuration fields must be effectively read-only.
65+
2. Configuration must not require a modified Rust build system and should be compatible with Cargo registries.
66+
3. Modifying configuration values must not require rebuilding Rust crates.
67+
4. Modifying a fixed PCD's value in EDK2 must be sufficient to change the corresponding value within the Rust configuration struct.
68+
69+
## Unresolved Questions
70+
71+
This section will be updated as questions arise and are resolved during the prototyping phase.
72+
73+
### 1. Will the component model need to be updated to allow per-component overrides?
74+
75+
PCD overrides are a core functionality heavily worth considering. Currently, component `Config<T>`s are applicable to all components. To allow seamless per-component overrides, the Component model may need to be updated to allow `Config<T>` instances to be earmarked for specific components. Whether this is a step worth taking and the mechanism by which this would be done are both open questions.
76+
77+
### 2. What limitations will need to be placed on structures marked as `#[derive(ExternallyConfigurable)]`?
78+
79+
To ensure portability between the Rust version used to compile Patina and the version used to compile the EDK2 build tool, structures passed to Patina through the HOB will need to be `#[repr(C)]` and `#[pack(1)]`. Whether these limitations can be confined to internal, procedurally generated, structures, or must apply to all structures marked with `#[derive(ExternallyConfigurable)]` is to be determined.
80+
81+
Additional limitations will also likely be placed on the marked data structures to ensure PCD types can only be mapped to their defined types (or to structs that implement a deserialization trait such as `zerocopy::FromBytes`) in the case of `VOID*` byte-array fixed PCDs.
82+
83+
### 3. How will the Patina ExternalConfiguration component determine whether a configuration structure is "enabled" and should be installed as a `Config<T>`?
84+
85+
This could be achieved a few ways. The simplest would be to add an "enabled" boolean configuration field to each marked struct which the ExternalConfiguration component would check. Alternatively, marked structures would implement a `ConditionallyEnabled` trait which would define a struct-specific function for determining whether a `Config<T>` should be installed.
86+
87+
### 4. Where should PCD mappings for configuration structures shared by multiple crates be located?
88+
89+
There are a couple of options here:
90+
1. Store metadata in the HOB that would mandate `Config<T>` for `ExternallyConfigurable` structs can only be installed on components that define the config in their `.inf`.
91+
2. Allow non-component-specific PCD-mapping `.inf`s.
92+
93+
## Prior Art (Existing PI C Implementation)
94+
95+
Corresponding EDK2 C behavior is outlined in the fixed PCD sections of the [EDK2 Build Specification](https://tianocore-docs.github.io/edk2-BuildSpecification/release-1.28/).
96+
97+
## Alternatives
98+
99+
### Alternative 1: Manual Definition in Code Containing Invocation of Patina
100+
- PCD configuration values can be manually copied from the EDK2 build system to Rust code containing the platform's invocation of Patina DXE Core.
101+
- Issues
102+
- Requires maintaining parity between EDK2 and Rust code manually, which is manual and error prone.
103+
104+
### Alternative 2: Single PEIM Installing a HOB
105+
- Instead of using a build system tool, a single PEIM can install the HOB which it populates from PCDs it requires through its own `.inf`.
106+
- Issues
107+
- The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures.
108+
- All PCDs would be in the PEIM scope.
109+
- There is no good way to provide per-component PCD overrides.
110+
- Layout errors and struct field mismatches wouldn't be easily caught.
111+
112+
### Alternative 3: Central + Per-Component (or Per-`Config<T>`) PEIMs
113+
- Instead of using a build system tool, one central PEIM and per-component PEIMs which would install split HOBs.
114+
- Issues
115+
- The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures.
116+
- All PCDs would still be in the PEIM scope.
117+
- Layout errors and struct field mismatches wouldn't be easily caught.
118+
- Extremely unergonomic, and hard to keep track of.
119+
120+
### Alternative 4: Consolidate All Required PCDs into a "PCD Store" HOB
121+
- Rather than trying to generate Rust-compatible structs from the EDK2 build system, publish all required PCDs in a consolidated HOB which a Patina-side component would split apart into `Config<T>`s
122+
- Fields in configuration structs would be annotated with the PCD namespace and GUID they are associated with: `#[derive(FromPCD(PcdNameSpace, PcdName))]`
123+
- Issues
124+
- Deserialization to Rust-compatible structures (and the errors which may arise) would be runtime, not build-time
125+
- Patina components would need to be aware of the EDK2 build system, and would need to keep track of PCD names and namespaces
126+
127+
## Code Design
128+
129+
This RFC will be prototyped in the coming weeks. More detailed information on code design will be provided once a prototype is built.
130+
131+
This RFC will consist of three parts: a macro crate, an EDK2 build system plugin, and a Patina component.
132+
133+
### 1. Shared `ExternallyConfigurable` Macro Crate
134+
The macro crate will be responsible for defining the `ExternallyConfigurable` procedural macro.
135+
136+
This macro will serve two purposes on the structs it's derived upon:
137+
1. Installing a marker trait
138+
2. Ensuring the struct is laid out in such a way that is safe to serialize and portable across Rust compiler versions
139+
140+
### 2. EDK2 Build System Plugin
141+
142+
The EDK2 build system plugin, which will be written with both Rust and Python components, will be responsible for the following aspects of this RFC:
143+
1. Gathering mappings of configuration struct fields to named PCDs
144+
2. Retrieving the value of the named PCDs
145+
3. Using some form of reflection to scan all relevant Crates in the EDK2 project's top-level Patina `cargo.lock` for configuration structs marked with `#[derive(ExternallyConfigurable)]`, then gather metadata on those structs
146+
- Specifically, it'd be looking for all Crates that take a dependency on the macro crate
147+
4. Building versions of those structs with fields populated by the gathered PCD values
148+
5. (TBD) Run deserialization functions on non-trivial data types to catch errors early
149+
5. Serializing those structs in a Rust-portable way
150+
6. Generating and injecting a PEIM that installs the serialized structs as a HOB
151+
152+
### 3. Patina `ExternalConfiguration` Component
153+
The Patina component would be responsible for the following aspects of this RFC:
154+
1. Unpacking the configuration structs from the HOB
155+
2. Running a check to see if the configuration struct should be considered "enabled" and installed as a `Config<T>`
156+
3. Installing enabled configuration structs as `Config<T>`s
157+
158+
159+
160+
## Guide-Level Explanation
161+
162+
This RFC will be prototyped in the coming weeks. A guide-level explanation will be provided once additional details have been finalized.

0 commit comments

Comments
 (0)