From c4f2db58b76b6574b8a04f6b85f9cf016a9e9ec0 Mon Sep 17 00:00:00 2001 From: Daniel Kouchekinia Date: Mon, 8 Sep 2025 16:40:34 -0700 Subject: [PATCH 1/9] First draft of RFC --- .../rfc/text/0000-fixed-pcd-config-interop.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 docs/src/rfc/text/0000-fixed-pcd-config-interop.md diff --git a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md new file mode 100644 index 000000000..8e9290f8a --- /dev/null +++ b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md @@ -0,0 +1,162 @@ +# RFC: Fixed PCD and `Config` Interop + +*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).* + +This RFC proposes a translation mechanism to allow fixed PCDs to be converted into component-friendly `Config`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: +- The proposed mechanism will not require custom Cargo build support, and will not require Patina re-compilation when configuration values are changed. +- The proposed mechanism will not require Patina re-compilation when configuration values are changed. +- Patina will be unaware of EDK2 build system and PCD name/namespace specifics +- Platform C-code (apart from what is autogenerated), will be unaware of Patina configuration structures + +At a high level, the basic flow for using PCD-defined values in Patina components will be as follows: + +1. Configuration structs can be marked with `#[derive(ExternallyConfigurable)]`, indicating they can be configured via an external build system. +2. On the EDK2-side, minimal per-Component `.inf` files will define mappings between marked configuration structure fields and fixed PCDs. + + ```yaml + # MyComponent.inf + + [Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MyComponent + MODULE_TYPE = PATINA_COMPONENT + + [ComponentConfigurations] + MyConfigStruct.Field1 = PcdNameSpace.PcdName + ``` + +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. +4. The build system extension will add these to a HOB via an auto-generated PEIM. +5. A Patina `ExternalConfiguration` component will process this HOB and generate `Config`s as appropriate. + +More detailed information on each step can be found in the "Code Design" section of this document. + +## Change Log + +This text can be modified over time. Add a change log entry for every change made to the RFC. + +- 2025-09-08: Initial RFC created. + +## Motivation + +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. + +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. + +## Technology Background + +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/). + +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. + +## Goals + +The goal of this RFC is to create a translation layer that allows fixed PCDs to be used by Patina Components. This layer should: +1. Be automatic and minimize potential for mismatched or incomplete configuration by emitting build-time errors. +2. Be ergonomic and require minimal user interaction. +3. Be EDK2-unaware from Patina's perspective. + - 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 +4. Be efficient and avoid unnecessary duplication of large shared configuration parameters. +5. Provide a mechanism for evaluating whether a given configuration state is valid and should be installed as a `Config`. + +## Requirements + +1. Configuration fields must be effectively read-only. +2. Configuration must not require a modified Rust build system and should be compatible with Cargo registries. +3. Modifying configuration values must not require rebuilding Rust crates. +4. Modifying a fixed PCD's value in EDK2 must be sufficient to change the corresponding value within the Rust configuration struct. + +## Unresolved Questions + +This section will be updated as questions arise and are resolved during the prototyping phase. + +### 1. Will the component model need to be updated to allow per-component overrides? + +PCD overrides are a core functionality heavily worth considering. Currently, component `Config`s are applicable to all components. To allow seamless per-component overrides, the Component model may need to be updated to allow `Config` 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. + +### 2. What limitations will need to be placed on structures marked as `#[derive(ExternallyConfigurable)]`? + +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. + +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. + +### 3. How will the Patina ExternalConfiguration component determine whether a configuration structure is "enabled" and should be installed as a `Config`? + +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` should be installed. + +### 4. Where should PCD mappings for configuration structures shared by multiple crates be located? + +There are a couple of options here: +1. Store metadata in the HOB that would mandate `Config` for `ExternallyConfigurable` structs can only be installed on components that define the config in their `.inf`. +2. Allow non-component-specific PCD-mapping `.inf`s. + +## Prior Art (Existing PI C Implementation) + +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/). + +## Alternatives + +### Alternative 1: Manual Definition in Code Containing Invocation of Patina +- PCD configuration values can be manually copied from the EDK2 build system to Rust code containing the platform's invocation of Patina DXE Core. +- Issues + - Requires maintaining parity between EDK2 and Rust code manually, which is manual and error prone. + +### Alternative 2: Single PEIM Installing a HOB +- 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`. +- Issues + - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures. + - All PCDs would be in the PEIM scope. + - There is no good way to provide per-component PCD overrides. + - Layout errors and struct field mismatches wouldn't be easily caught. + +### Alternative 3: Central + Per-Component (or Per-`Config`) PEIMs +- Instead of using a build system tool, one central PEIM and per-component PEIMs which would install split HOBs. +- Issues + - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures. + - All PCDs would still be in the PEIM scope. + - Layout errors and struct field mismatches wouldn't be easily caught. + - Extremely unergonomic, and hard to keep track of. + +### Alternative 4: Consolidate All Required PCDs into a "PCD Store" HOB +- 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`s +- Fields in configuration structs would be annotated with the PCD namespace and GUID they are associated with: `#[derive(FromPCD(PcdNameSpace, PcdName))]` +- Issues + - Deserialization to Rust-compatible structures (and the errors which may arise) would be runtime, not build-time + - Patina components would need to be aware of the EDK2 build system, and would need to keep track of PCD names and namespaces + +## Code Design + +This RFC will be prototyped in the coming weeks. More detailed information on code design will be provided once a prototype is built. + +This RFC will consist of three parts: a macro crate, an EDK2 build system plugin, and a Patina component. + +### 1. Shared `ExternallyConfigurable` Macro Crate +The macro crate will be responsible for defining the `ExternallyConfigurable` procedural macro. + +This macro will serve two purposes on the structs it's derived upon: +1. Installing a marker trait +2. Ensuring the struct is laid out in such a way that is safe to serialize and portable across Rust compiler versions + +### 2. EDK2 Build System Plugin + +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: +1. Gathering mappings of configuration struct fields to named PCDs +2. Retrieving the value of the named PCDs +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 + - Specifically, it'd be looking for all Crates that take a dependency on the macro crate +4. Building versions of those structs with fields populated by the gathered PCD values +5. (TBD) Run deserialization functions on non-trivial data types to catch errors early +5. Serializing those structs in a Rust-portable way +6. Generating and injecting a PEIM that installs the serialized structs as a HOB + +### 3. Patina `ExternalConfiguration` Component +The Patina component would be responsible for the following aspects of this RFC: +1. Unpacking the configuration structs from the HOB +2. Running a check to see if the configuration struct should be considered "enabled" and installed as a `Config` +3. Installing enabled configuration structs as `Config`s + + + +## Guide-Level Explanation + +This RFC will be prototyped in the coming weeks. A guide-level explanation will be provided once additional details have been finalized. From b1b8b3f5781d8a6fc804c63dab6a172cfcb7fcb3 Mon Sep 17 00:00:00 2001 From: Daniel Kouchekinia Date: Mon, 8 Sep 2025 18:00:25 -0700 Subject: [PATCH 2/9] Spelling fix --- docs/src/rfc/text/0000-fixed-pcd-config-interop.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md index 8e9290f8a..6719a11d7 100644 --- a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md +++ b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md @@ -2,7 +2,7 @@ *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).* -This RFC proposes a translation mechanism to allow fixed PCDs to be converted into component-friendly `Config`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: +This RFC proposes a translation mechanism to allow fixed PCDs to be processed into component-friendly `Config`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: - The proposed mechanism will not require custom Cargo build support, and will not require Patina re-compilation when configuration values are changed. - The proposed mechanism will not require Patina re-compilation when configuration values are changed. - Patina will be unaware of EDK2 build system and PCD name/namespace specifics @@ -39,7 +39,7 @@ This text can be modified over time. Add a change log entry for every change mad ## Motivation -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. +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 transferred, this process is error prone, especially as configuration changes may occur in EDK2 within Git submodules. 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. @@ -115,7 +115,7 @@ Corresponding EDK2 C behavior is outlined in the fixed PCD sections of the [EDK2 - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures. - All PCDs would still be in the PEIM scope. - Layout errors and struct field mismatches wouldn't be easily caught. - - Extremely unergonomic, and hard to keep track of. + - Not ergonomic, and hard to keep track of. ### Alternative 4: Consolidate All Required PCDs into a "PCD Store" HOB - 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`s From 70e80b537839c797947def4f535b34444492b621 Mon Sep 17 00:00:00 2001 From: Daniel Kouchekinia Date: Mon, 8 Sep 2025 19:21:33 -0700 Subject: [PATCH 3/9] Rewording to address a few of Michael's comments --- docs/src/rfc/text/0000-fixed-pcd-config-interop.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md index 6719a11d7..0b37589a7 100644 --- a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md +++ b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md @@ -3,10 +3,10 @@ *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).* This RFC proposes a translation mechanism to allow fixed PCDs to be processed into component-friendly `Config`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: -- The proposed mechanism will not require custom Cargo build support, and will not require Patina re-compilation when configuration values are changed. +- The proposed mechanism will not require custom Cargo build support. - The proposed mechanism will not require Patina re-compilation when configuration values are changed. -- Patina will be unaware of EDK2 build system and PCD name/namespace specifics -- Platform C-code (apart from what is autogenerated), will be unaware of Patina configuration structures +- Patina will be unaware of EDK2 build system and PCD name/namespace specifics. +- Platform C-code (apart from what is autogenerated), will be unaware of Patina configuration structures. At a high level, the basic flow for using PCD-defined values in Patina components will be as follows: @@ -39,7 +39,9 @@ This text can be modified over time. Add a change log entry for every change mad ## Motivation -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 transferred, this process is error prone, especially as configuration changes may occur in EDK2 within Git submodules. +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. + +**Put simply, platform configuration ought to be defined only once, and EDK2 mechanisms are the common denominator.** 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. @@ -70,9 +72,9 @@ The goal of this RFC is to create a translation layer that allows fixed PCDs to This section will be updated as questions arise and are resolved during the prototyping phase. -### 1. Will the component model need to be updated to allow per-component overrides? +### 1. Will the component model need to be updated to allow per-component overrides by adding `Config`-instance component targeting? -PCD overrides are a core functionality heavily worth considering. Currently, component `Config`s are applicable to all components. To allow seamless per-component overrides, the Component model may need to be updated to allow `Config` 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. +PCD overrides are a core functionality heavily worth considering. Currently, component `Config` instances are applicable to all components. To allow seamless per-component overrides, the Component model may need to be updated to allow `Config` instances to be targeted for specific components. Whether this is a step worth taking and the mechanism by which this would be done are both open questions. ### 2. What limitations will need to be placed on structures marked as `#[derive(ExternallyConfigurable)]`? From 6563cbfac7867fa9bb8c8d5122a40e4a07b3f67c Mon Sep 17 00:00:00 2001 From: Daniel Kouchekinia Date: Tue, 9 Sep 2025 17:52:23 -0700 Subject: [PATCH 4/9] Rewrote RFC --- .../rfc/text/0000-fixed-pcd-config-interop.md | 251 ++++++++++-------- 1 file changed, 140 insertions(+), 111 deletions(-) diff --git a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md index 0b37589a7..84ad89caf 100644 --- a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md +++ b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md @@ -1,164 +1,193 @@ -# RFC: Fixed PCD and `Config` Interop +# RFC: EDK II `Config` Interop -*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).* +This RFC proposes a translation mechanism to allow fixed-at-build EDK II build system configuration mechanisms--which will be referred to as "fixed EDK II configurations"--to be mapped to Patina `Config` structure fields through a level of indirection that will prevent the concept of PCDs from being directly introduced into Patina source code. -This RFC proposes a translation mechanism to allow fixed PCDs to be processed into component-friendly `Config`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: -- The proposed mechanism will not require custom Cargo build support. -- The proposed mechanism will not require Patina re-compilation when configuration values are changed. -- Patina will be unaware of EDK2 build system and PCD name/namespace specifics. -- Platform C-code (apart from what is autogenerated), will be unaware of Patina configuration structures. +For the purposes of this RFC, the following are considered fixed-at-build EDK II configurations: fixed PCDs (including FixedAtBuild, PatchableInModule, and FeatureFlags) and EDK II build variables. In the current version of this RFC, dynamic PCDs are considered out-of-scope, but may be introduced in a follow-up-RFC. A table of considered configurations to pass through with this RFC can be found in the "Considered EDK II Configurations" section of this document. -At a high level, the basic flow for using PCD-defined values in Patina components will be as follows: +Note that while fixed PCDs and PCD feature flags in EDK II can be overwritten per-module, per-component PCD overrides are considered out-of-scope in this RFC, and, should this document be approved, will likely be addressed in a follow-up RFC. -1. Configuration structs can be marked with `#[derive(ExternallyConfigurable)]`, indicating they can be configured via an external build system. -2. On the EDK2-side, minimal per-Component `.inf` files will define mappings between marked configuration structure fields and fixed PCDs. +While exact specifics are to-be-determined following a prototyping phase, the basic mechanism for configuration transfer is as follows: - ```yaml - # MyComponent.inf +1. Procedural macros are used to generate representations of configuration structures that are stable across versions of Rust (likely `#[repr(C)]` and `#[align(1)]`) +2. On the EDK II-side, Patina configuration `.inf` files (as denoted with `MODULE TYPE = PATINA_CONFIGURATION`) map fixed EDK II configurations to Patina configuration structure fields +3. Using a top-level `Cargo.lock` file, a Rust EDK II build system plugin locates all valid configuration structures within Patina-related source files and creates portable instances of their stable representations populated with the values of the mapped fixed EDK II configurations +4. The Rust EDK II build system plugin places these representations into either one combined or several split (TBD) HOBs through an auto-generated PEIM +5. A Patina `ExternalConfigurationExtractor` Component parses these HOB(s), converting them into their non-portable struct counterparts, checking whether they should be considered "enabled" and installing them as `Config`s as appropriate. - [Defines] - INF_VERSION = 0x00010005 - BASE_NAME = MyComponent - MODULE_TYPE = PATINA_COMPONENT +Additional details on the above steps can be found in the sections below. - [ComponentConfigurations] - MyConfigStruct.Field1 = PcdNameSpace.PcdName - ``` +## Change Log -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. -4. The build system extension will add these to a HOB via an auto-generated PEIM. -5. A Patina `ExternalConfiguration` component will process this HOB and generate `Config`s as appropriate. +- 2025-09-08: Initial RFC created. -More detailed information on each step can be found in the "Code Design" section of this document. +## Motivation -## Change Log +The ability to configure Components is vital to ensue that they can be reused across projects (or platforms within those projects) which may have different memory layouts, feature requirements, and expected behaviors. While Patina Components can already be configured in Rust, integration with outside build systems minimizes room for mismatches between values configured within and outside of Patina. **Put simply, platform configuration ought to be defined only once, and EDK II mechanisms are the common denominator** -This text can be modified over time. Add a change log entry for every change made to the RFC. +While PCDs as a concept could be added to Rust themselves, this FRC opts for a level of indirection (in the form of a key-value store of configuration fields to fixed EDK II configuration values) to avoid tightly coupling Patina with a non-Cargo build system. While this RFC addresses EDK II specifically with an EDK II build plugin, other build systems could theoretically be supported by the same Patina-side changes with an equivalent plugin. -- 2025-09-08: Initial RFC created. +## Goals -## Motivation +The goal of this RFC is to create a translation layer that allows fixed EDK II configuration values to be applied to Patina configuration structures. This layer should: -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. +1. Be automatic and minimize potential for mismatched or incomplete configurations by emitting build-time errors when possible +2. Be ergonomic and require minimal user interaction from both EDK II and Patina +3. Be EDK II-unaware from patina's perspective +4. Be efficient of memory use +5. Provide a mechanism for evaluating whether a given configuration is to be considered enabled (and subsequently installed as a `Config`) -**Put simply, platform configuration ought to be defined only once, and EDK2 mechanisms are the common denominator.** +## Requirements -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. +1. Configuration must not break compatibility with `cargo` and Cargo registries +2. Modifying configuration values must not require modifying or rebuilding Rust crates. +3. Modifying fixed EDK II configurations within the EDK II build system must be sufficient to change the corresponding value within the installed Rust `Config` -## Technology Background +## Software Components -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/). +### 1. EDK II Build System Plugin -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. +The EDK II build system plugin, which will likely be written in combination of Rust and Python, will be responsible for gathering the mapping of fixed EDK II configuration values to Rust configurations, and arrange for a HOB to be published containing those values in the stable structure format. -## Goals +Whether this plugin would best be unstreamed to Tianocore or modularly included from a Patina EDK II repo as needed depends on the degree of invasiveness into the EDK II build system required to implement it. This will be best determined after an initial prototyping phase. -The goal of this RFC is to create a translation layer that allows fixed PCDs to be used by Patina Components. This layer should: -1. Be automatic and minimize potential for mismatched or incomplete configuration by emitting build-time errors. -2. Be ergonomic and require minimal user interaction. -3. Be EDK2-unaware from Patina's perspective. - - 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 -4. Be efficient and avoid unnecessary duplication of large shared configuration parameters. -5. Provide a mechanism for evaluating whether a given configuration state is valid and should be installed as a `Config`. +#### Specifying Mappings -## Requirements +Mappings between fixed EDK II configurations and Patina configuration structures will be defined in a `[PatinaConfigurations]` section of any `.inf` included in a `.dsc` file of `MODULE_TYPE = PATINA_CONFIGURATION`. Different syntax, will be used to specify different types of configurations. The following is an example of such a `.inf`, but the exact format is still TBD. -1. Configuration fields must be effectively read-only. -2. Configuration must not require a modified Rust build system and should be compatible with Cargo registries. -3. Modifying configuration values must not require rebuilding Rust crates. -4. Modifying a fixed PCD's value in EDK2 must be sufficient to change the corresponding value within the Rust configuration struct. +```yaml +[Defines] + INF_VERSION                    = 0x00010005 + MODULE_TYPE                    = PATINA_COMPONENT -## Unresolved Questions +[PatinaConfigurations] + MyConfigStruct.Field1 = PcdNameSpace.PcdName # PCD (FixedAtBuild, PatchableInModule) + MyConfigStruct.Field2 = PcdNameSpace.PcdFeatureFlagName # PCD (FeatureFlag) + MyConfigStruct.Field3 = $(BUILD_SYSTEM_VAR_NAME) # Build system variable +``` -This section will be updated as questions arise and are resolved during the prototyping phase. +For simplicity, nested configuration structs are out-of-scope for this RFC. -### 1. Will the component model need to be updated to allow per-component overrides by adding `Config`-instance component targeting? +#### Determining Patina Portable Struct Layout -PCD overrides are a core functionality heavily worth considering. Currently, component `Config` instances are applicable to all components. To allow seamless per-component overrides, the Component model may need to be updated to allow `Config` instances to be targeted for specific components. Whether this is a step worth taking and the mechanism by which this would be done are both open questions. +The EDK II build plugin will use a form of cross-crate reflection to inspect the portable layout of referenced structs in the crates in which they are defined. These portable layouts are automatically generated by the procedural macros in the `ExternalConfigurationExtractor` crate, as described in its section below. To ensure the correct version of the crate is used, the top-level `Cargo.lock` file of the Crate containing the invocation of the Patina core will need to be referenced by the plugin. From there, the build plugin will download and inspect the source code as appropriate using either `syn` or the JSON output mode of `cargo rustdoc`. The exact mechanism is TBD pending the prototyping phase of this RFC. -### 2. What limitations will need to be placed on structures marked as `#[derive(ExternallyConfigurable)]`? +#### Verifying Mapping Compatibility -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. +Once the structure of the portable-version of the targeted structs are known, the EDK build plugin will verify three things: -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. +1. All configuration structure and field names exist in valid Rust configuration structures +2. If any field in a configuration structure is mapped, that all fields in that configuration structure is mapped +3. Compatibility between the data types of the fixed EDK II configurations and Patina configuration struct fields -### 3. How will the Patina ExternalConfiguration component determine whether a configuration structure is "enabled" and should be installed as a `Config`? +The strictness of the the third verification (type-checking) is an open question. -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` should be installed. +Fixed-at-build PCDs come in `u8`, `u16`, `u32`, `u64`, `bool`, and byte-array (`VOID*`) variants. PCD feature flags come in type `bool` whereas EDK II build system variables are ASCII strings. -### 4. Where should PCD mappings for configuration structures shared by multiple crates be located? +While is is a reasonable assumption that, when it comes to integer types, that any unsigned integer type can be converted into the same type or a wider type, a few of the questions that remain are: -There are a couple of options here: -1. Store metadata in the HOB that would mandate `Config` for `ExternallyConfigurable` structs can only be installed on components that define the config in their `.inf`. -2. Allow non-component-specific PCD-mapping `.inf`s. +- Should you be able to convert a wider integer into a narrower integer if the value fits in the narrower type? +- Should it be allowed that unsigned integer types be converted into signed types? If so, should it be a build error if that value would then be negative? +- Should byte arrays be allowed to be converted into `zerocopy::FromBytes` slices? Should non-byte-array types be able to? -## Prior Art (Existing PI C Implementation) +#### Constructing Instances of Portable Structs -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/). +After the mappings have been verified to be compatible, instances of the portable structs will be generated and placed into either one or multiple HOBs (TBD). While the exact mechanism for this is TBD, likely candidates are compiling a Rust portion of the EDK II build plugin with `syn`, or generating byte-compatible (but not named) representations of the structs. -## Alternatives +#### Orchestrating the HOB(s) Installations -### Alternative 1: Manual Definition in Code Containing Invocation of Patina -- PCD configuration values can be manually copied from the EDK2 build system to Rust code containing the platform's invocation of Patina DXE Core. -- Issues - - Requires maintaining parity between EDK2 and Rust code manually, which is manual and error prone. +Once HOB(s) have been generated, the EDK II build plugin will ensure the HOBs are installed by auto-generating and including a minimal PEIM which serves that purpose. -### Alternative 2: Single PEIM Installing a HOB -- 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`. -- Issues - - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures. - - All PCDs would be in the PEIM scope. - - There is no good way to provide per-component PCD overrides. - - Layout errors and struct field mismatches wouldn't be easily caught. +### 2. Patina `ExternalConfigurationExtractor` Component -### Alternative 3: Central + Per-Component (or Per-`Config`) PEIMs -- Instead of using a build system tool, one central PEIM and per-component PEIMs which would install split HOBs. -- Issues - - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures. - - All PCDs would still be in the PEIM scope. - - Layout errors and struct field mismatches wouldn't be easily caught. - - Not ergonomic, and hard to keep track of. +The `ExternalConfigurationExtractor` component will have four primary responsibilities: -### Alternative 4: Consolidate All Required PCDs into a "PCD Store" HOB -- 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`s -- Fields in configuration structs would be annotated with the PCD namespace and GUID they are associated with: `#[derive(FromPCD(PcdNameSpace, PcdName))]` -- Issues - - Deserialization to Rust-compatible structures (and the errors which may arise) would be runtime, not build-time - - Patina components would need to be aware of the EDK2 build system, and would need to keep track of PCD names and namespaces +1. Unpacking the portable configuration structs from the HOB(s) +2. Converting those portable configuration structs to their unmodified originals +3. Determining whether that configuration structure is "enabled" and ought to be installed +4. Installing "enabled" configuration structures as `Config`s + +To aid in the above steps, procedural macros defined within the crate will automatically generate the following for each compatible configuration structure: + +- A representation of the structure that is stable across versions of Rust (likely `#[repr(C)]` and `#[align(1)]`) +- A routine to convert the stable structure to the original form, to check whether it is "enabled", and to install it as a `Cargo` from a HOB -## Code Design +Whether or not it is possible to achieve this without a manually specified `#[derive]` on all configuration structs or top-level build support is to-be-determined following the prototyping phase. -This RFC will be prototyped in the coming weeks. More detailed information on code design will be provided once a prototype is built. +If it is not possible for a portable representation of a configuration structure to be automatically generated (such as if the configuration struct contains a `Box`), a manually defined portable representation and conversion routine will need to be provided. The exact mechanism for this is an open question. -This RFC will consist of three parts: a macro crate, an EDK2 build system plugin, and a Patina component. +#### Unpacking The Portable Configuration Structs -### 1. Shared `ExternallyConfigurable` Macro Crate -The macro crate will be responsible for defining the `ExternallyConfigurable` procedural macro. +The implementation of unpacking the portable configuration structs is dependent on whether a single HOB is used, or whether individual configuration structure HOBs are used. -This macro will serve two purposes on the structs it's derived upon: -1. Installing a marker trait -2. Ensuring the struct is laid out in such a way that is safe to serialize and portable across Rust compiler versions +In the single-HOB option, the portable configuration structures are packed together in one contiguous structure, with each struct prepended by a the name of the structure as an ASCII string. -### 2. EDK2 Build System Plugin +In the multiple-HOB option, `HOB`s would be generated procedurally per configuration struct (with their HOB GUIDs generated procedurally as well). In this scenario the portable structs would be converted to `HOB` before further processing. -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: -1. Gathering mappings of configuration struct fields to named PCDs -2. Retrieving the value of the named PCDs -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 - - Specifically, it'd be looking for all Crates that take a dependency on the macro crate -4. Building versions of those structs with fields populated by the gathered PCD values -5. (TBD) Run deserialization functions on non-trivial data types to catch errors early -5. Serializing those structs in a Rust-portable way -6. Generating and injecting a PEIM that installs the serialized structs as a HOB +Which of these methods should be used is an option question. The single-HOB option has the benefit of simplicity and room for future metadata which would facilitate extensions such as Component-specific `Config` instances, whereas the multi-HOB option is more in keeping with how existing HOB-based configurations are converted into `Config`s. -### 3. Patina `ExternalConfiguration` Component -The Patina component would be responsible for the following aspects of this RFC: -1. Unpacking the configuration structs from the HOB -2. Running a check to see if the configuration struct should be considered "enabled" and installed as a `Config` -3. Installing enabled configuration structs as `Config`s +#### Converting Portable Configuration Structs into their Original Counterparts +The routines to convert the portable configuration structs into their original (likely non-Portable) counterparts will be generated via the procedural macro described above. +#### Determining Whether a Configuration is "Enabled" -## Guide-Level Explanation +Before installing the configurations structures as `Config`s, the component will check to determine whether a given configuration is considered "enabled". The exact mechanism for this is TBD, but a candidate would be to have some `ConditionallyEnabled` trait that can be derived or implemented for configuration structures that defines the function `fn is_enabled(&self) -> bool`. -This RFC will be prototyped in the coming weeks. A guide-level explanation will be provided once additional details have been finalized. +#### Installing "Enabled" Configuration Structures + +After verifying a configuration structure is "enabled", the Component will install the structure as a `Config`, making it available for use by other components. + +## Unresolved Questions + +1. How strict should type checking be between fixed EDK II configurations and Patina configuration struct fields? (See: "Verifying Mapping Compatibility") +2. Should the portable configuration structures be stored in a single HOB, or in multiple HOBs (one per configuration structure)? (See: "Unpacking The Portable Configuration Structs") +3. How should custom portable structure definitions and conversion routines be provided for non-`#[repr(C)]`-safe configuration structs? (See: "Patina `ExternalConfigurationExtractor` Component" section) +4. How should the Patina `ExternalConfigurationExtractor` component determine whether a configuration structure is "enabled" and should be installed as a `Config`? (See: "Determining Whether a Configuration is "Enabled"") + +## Alternatives + +### Alternative 1: Manual Definition in Code Containing Invocation of Patina + +- PCD configuration values can be manually copied from the EDK II build system to Rust code containing the platform's invocation of Patina DXE Core +- Issues +    - Requires maintaining parity between EDK II and Rust code manually, which is extremely error prone + +### Alternative 2: Single PEIM Installing a HOB + +- 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` +- Issues +    - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures +    - All PCDs would be in the PEIM scope +    - There is no good way to provide per-component PCD overrides +    - Layout errors and struct field mismatches wouldn't be easily caught + +### Alternative 3: Single + Per-Component (or Per-`Config`) PEIMs + +- Instead of using a build system tool, one central PEIM and per-component PEIMs which would install split HOBs +- Issues +    - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures +    - All PCDs would still be in the PEIM scope +    - Layout errors and struct field mismatches wouldn't be easily caught +    - Unergonomic and hard to keep track of + +### Alternative 4: Consolidate All Required PCDs into a "PCD Store" HOB + +- Rather than trying to generate Rust-compatible structs from the EDK II build system, publish all required PCDs in a consolidated HOB which a Patina-side component would split apart into `Config`s +- Fields could be populated with PCDs gathered through a PCD macro: `PCD!(PcdNamespace, PcdName)` +- Issues +    - Deserialization to Rust-compatible types (and the errors which may arise) would be runtime, not build-time +    - Patina components would need to be aware of the EDK II build system, and would need to keep track of PCD names and namespaces + +## Considered EDK II Configurations + +The following EDK II configurations have been considered for initial inclusion in this RFC. The final list of allowed configurations is open to debate. + +| EDK-II-Related Configuration | Included | Reason | +| ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| FixedAtBuild PCDs | Yes | Fixed at build time | +| FeatureFlag PCDs | Yes | Fixed at built time | +| PatchableInModule PCDs | Yes | Fixed at build time | +| EDK II Build System Variable | Yes | Fixed at build time | +| Dynamic PCDs | No | Currently out of scope as their handling on the EDK II build-extension side would be very different than fixed-at-build values. This may be added to the current document, or added in a follow-up RFC. | +| C Compiler Flags | No | Lack of clear use case when EDK II Build System Variable support exists. | +| UEFI Variables | No | Outside scope and covered by Variable Services. | From 547a7c39b36bfbcfd9a77b4ed2645a92dc8ea904 Mon Sep 17 00:00:00 2001 From: Daniel Kouchekinia Date: Tue, 9 Sep 2025 18:01:03 -0700 Subject: [PATCH 5/9] Reworded RFC title --- ...ig-interop.md => 0000-edk-ii-build-system-config-interop.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/src/rfc/text/{0000-fixed-pcd-config-interop.md => 0000-edk-ii-build-system-config-interop.md} (98%) diff --git a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md b/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md similarity index 98% rename from docs/src/rfc/text/0000-fixed-pcd-config-interop.md rename to docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md index 84ad89caf..d5b420fbd 100644 --- a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md +++ b/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md @@ -1,4 +1,4 @@ -# RFC: EDK II `Config` Interop +# RFC: EDK II Build System `Config` Interop This RFC proposes a translation mechanism to allow fixed-at-build EDK II build system configuration mechanisms--which will be referred to as "fixed EDK II configurations"--to be mapped to Patina `Config` structure fields through a level of indirection that will prevent the concept of PCDs from being directly introduced into Patina source code. From c9cdb5335615e9137c468ea70ba37c12cb6efefd Mon Sep 17 00:00:00 2001 From: Daniel Kouchekinia Date: Tue, 9 Sep 2025 18:07:55 -0700 Subject: [PATCH 6/9] Updated section on potential build plugin inclusion in Tianocore --- docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md b/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md index d5b420fbd..6a23b7ce0 100644 --- a/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md +++ b/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md @@ -48,7 +48,7 @@ The goal of this RFC is to create a translation layer that allows fixed EDK II c The EDK II build system plugin, which will likely be written in combination of Rust and Python, will be responsible for gathering the mapping of fixed EDK II configuration values to Rust configurations, and arrange for a HOB to be published containing those values in the stable structure format. -Whether this plugin would best be unstreamed to Tianocore or modularly included from a Patina EDK II repo as needed depends on the degree of invasiveness into the EDK II build system required to implement it. This will be best determined after an initial prototyping phase. +Whether this plugin would best be unstreamed to Tianocore or modularly included from a Patina EDK II repo as needed depends on the 1) the degree of invasiveness into the EDK II build system required to implement it and 2) whether the build plugin will need to take a dependency on a Patina crate. This will be best determined after an initial prototyping phase. #### Specifying Mappings From f17180eeecbe10cd38441ca695305eeb9b68a10d Mon Sep 17 00:00:00 2001 From: Daniel Kouchekinia Date: Tue, 9 Sep 2025 18:12:54 -0700 Subject: [PATCH 7/9] Update changelog --- .../rfc/text/0000-fixed-pcd-config-interop.md | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 docs/src/rfc/text/0000-fixed-pcd-config-interop.md diff --git a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md new file mode 100644 index 000000000..12ec50da4 --- /dev/null +++ b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md @@ -0,0 +1,194 @@ +# RFC: EDK II Build System `Config` Interop + +This RFC proposes a translation mechanism to allow fixed-at-build EDK II build system configuration mechanisms--which will be referred to as "fixed EDK II configurations"--to be mapped to Patina `Config` structure fields through a level of indirection that will prevent the concept of PCDs from being directly introduced into Patina source code. + +For the purposes of this RFC, the following are considered fixed-at-build EDK II configurations: fixed PCDs (including FixedAtBuild, PatchableInModule, and FeatureFlags) and EDK II build variables. In the current version of this RFC, dynamic PCDs are considered out-of-scope, but may be introduced in a follow-up-RFC. A table of considered configurations to pass through with this RFC can be found in the "Considered EDK II Configurations" section of this document. + +Note that while fixed PCDs and PCD feature flags in EDK II can be overwritten per-module, per-component PCD overrides are considered out-of-scope in this RFC, and, should this document be approved, will likely be addressed in a follow-up RFC. + +While exact specifics are to-be-determined following a prototyping phase, the basic mechanism for configuration transfer is as follows: + +1. Procedural macros are used to generate representations of configuration structures that are stable across versions of Rust (likely `#[repr(C)]` and `#[align(1)]`) +2. On the EDK II-side, Patina configuration `.inf` files (as denoted with `MODULE TYPE = PATINA_CONFIGURATION`) map fixed EDK II configurations to Patina configuration structure fields +3. Using a top-level `Cargo.lock` file, a Rust EDK II build system plugin locates all valid configuration structures within Patina-related source files and creates portable instances of their stable representations populated with the values of the mapped fixed EDK II configurations +4. The Rust EDK II build system plugin places these representations into either one combined or several split (TBD) HOBs through an auto-generated PEIM +5. A Patina `ExternalConfigurationExtractor` Component parses these HOB(s), converting them into their non-portable struct counterparts, checking whether they should be considered "enabled" and installing them as `Config`s as appropriate. + +Additional details on the above steps can be found in the sections below. + +## Change Log + +- 2025-09-08: Initial RFC created. +- 2025-09-09: Updated to second draft of RFC. + +## Motivation + +The ability to configure Components is vital to ensue that they can be reused across projects (or platforms within those projects) which may have different memory layouts, feature requirements, and expected behaviors. While Patina Components can already be configured in Rust, integration with outside build systems minimizes room for mismatches between values configured within and outside of Patina. **Put simply, platform configuration ought to be defined only once, and EDK II mechanisms are the common denominator** + +While PCDs as a concept could be added to Rust themselves, this FRC opts for a level of indirection (in the form of a key-value store of configuration fields to fixed EDK II configuration values) to avoid tightly coupling Patina with a non-Cargo build system. While this RFC addresses EDK II specifically with an EDK II build plugin, other build systems could theoretically be supported by the same Patina-side changes with an equivalent plugin. + +## Goals + +The goal of this RFC is to create a translation layer that allows fixed EDK II configuration values to be applied to Patina configuration structures. This layer should: + +1. Be automatic and minimize potential for mismatched or incomplete configurations by emitting build-time errors when possible +2. Be ergonomic and require minimal user interaction from both EDK II and Patina +3. Be EDK II-unaware from patina's perspective +4. Be efficient of memory use +5. Provide a mechanism for evaluating whether a given configuration is to be considered enabled (and subsequently installed as a `Config`) + +## Requirements + +1. Configuration must not break compatibility with `cargo` and Cargo registries +2. Modifying configuration values must not require modifying or rebuilding Rust crates. +3. Modifying fixed EDK II configurations within the EDK II build system must be sufficient to change the corresponding value within the installed Rust `Config` + +## Software Components + +### 1. EDK II Build System Plugin + +The EDK II build system plugin, which will likely be written in combination of Rust and Python, will be responsible for gathering the mapping of fixed EDK II configuration values to Rust configurations, and arrange for a HOB to be published containing those values in the stable structure format. + +Whether this plugin would best be unstreamed to Tianocore or modularly included from a Patina EDK II repo as needed depends on the degree of invasiveness into the EDK II build system required to implement it. This will be best determined after an initial prototyping phase. + +#### Specifying Mappings + +Mappings between fixed EDK II configurations and Patina configuration structures will be defined in a `[PatinaConfigurations]` section of any `.inf` included in a `.dsc` file of `MODULE_TYPE = PATINA_CONFIGURATION`. Different syntax, will be used to specify different types of configurations. The following is an example of such a `.inf`, but the exact format is still TBD. + +```yaml +[Defines] + INF_VERSION                    = 0x00010005 + MODULE_TYPE                    = PATINA_COMPONENT + +[PatinaConfigurations] + MyConfigStruct.Field1 = PcdNameSpace.PcdName # PCD (FixedAtBuild, PatchableInModule) + MyConfigStruct.Field2 = PcdNameSpace.PcdFeatureFlagName # PCD (FeatureFlag) + MyConfigStruct.Field3 = $(BUILD_SYSTEM_VAR_NAME) # Build system variable +``` + +For simplicity, nested configuration structs are out-of-scope for this RFC. + +#### Determining Patina Portable Struct Layout + +The EDK II build plugin will use a form of cross-crate reflection to inspect the portable layout of referenced structs in the crates in which they are defined. These portable layouts are automatically generated by the procedural macros in the `ExternalConfigurationExtractor` crate, as described in its section below. To ensure the correct version of the crate is used, the top-level `Cargo.lock` file of the Crate containing the invocation of the Patina core will need to be referenced by the plugin. From there, the build plugin will download and inspect the source code as appropriate using either `syn` or the JSON output mode of `cargo rustdoc`. The exact mechanism is TBD pending the prototyping phase of this RFC. + +#### Verifying Mapping Compatibility + +Once the structure of the portable-version of the targeted structs are known, the EDK build plugin will verify three things: + +1. All configuration structure and field names exist in valid Rust configuration structures +2. If any field in a configuration structure is mapped, that all fields in that configuration structure is mapped +3. Compatibility between the data types of the fixed EDK II configurations and Patina configuration struct fields + +The strictness of the the third verification (type-checking) is an open question. + +Fixed-at-build PCDs come in `u8`, `u16`, `u32`, `u64`, `bool`, and byte-array (`VOID*`) variants. PCD feature flags come in type `bool` whereas EDK II build system variables are ASCII strings. + +While is is a reasonable assumption that, when it comes to integer types, that any unsigned integer type can be converted into the same type or a wider type, a few of the questions that remain are: + +- Should you be able to convert a wider integer into a narrower integer if the value fits in the narrower type? +- Should it be allowed that unsigned integer types be converted into signed types? If so, should it be a build error if that value would then be negative? +- Should byte arrays be allowed to be converted into `zerocopy::FromBytes` slices? Should non-byte-array types be able to? + +#### Constructing Instances of Portable Structs + +After the mappings have been verified to be compatible, instances of the portable structs will be generated and placed into either one or multiple HOBs (TBD). While the exact mechanism for this is TBD, likely candidates are compiling a Rust portion of the EDK II build plugin with `syn`, or generating byte-compatible (but not named) representations of the structs. + +#### Orchestrating the HOB(s) Installations + +Once HOB(s) have been generated, the EDK II build plugin will ensure the HOBs are installed by auto-generating and including a minimal PEIM which serves that purpose. + +### 2. Patina `ExternalConfigurationExtractor` Component + +The `ExternalConfigurationExtractor` component will have four primary responsibilities: + +1. Unpacking the portable configuration structs from the HOB(s) +2. Converting those portable configuration structs to their unmodified originals +3. Determining whether that configuration structure is "enabled" and ought to be installed +4. Installing "enabled" configuration structures as `Config`s + +To aid in the above steps, procedural macros defined within the crate will automatically generate the following for each compatible configuration structure: + +- A representation of the structure that is stable across versions of Rust (likely `#[repr(C)]` and `#[align(1)]`) +- A routine to convert the stable structure to the original form, to check whether it is "enabled", and to install it as a `Cargo` from a HOB + +Whether or not it is possible to achieve this without a manually specified `#[derive]` on all configuration structs or top-level build support is to-be-determined following the prototyping phase. + +If it is not possible for a portable representation of a configuration structure to be automatically generated (such as if the configuration struct contains a `Box`), a manually defined portable representation and conversion routine will need to be provided. The exact mechanism for this is an open question. + +#### Unpacking The Portable Configuration Structs + +The implementation of unpacking the portable configuration structs is dependent on whether a single HOB is used, or whether individual configuration structure HOBs are used. + +In the single-HOB option, the portable configuration structures are packed together in one contiguous structure, with each struct prepended by a the name of the structure as an ASCII string. + +In the multiple-HOB option, `HOB`s would be generated procedurally per configuration struct (with their HOB GUIDs generated procedurally as well). In this scenario the portable structs would be converted to `HOB` before further processing. + +Which of these methods should be used is an option question. The single-HOB option has the benefit of simplicity and room for future metadata which would facilitate extensions such as Component-specific `Config` instances, whereas the multi-HOB option is more in keeping with how existing HOB-based configurations are converted into `Config`s. + +#### Converting Portable Configuration Structs into their Original Counterparts + +The routines to convert the portable configuration structs into their original (likely non-Portable) counterparts will be generated via the procedural macro described above. + +#### Determining Whether a Configuration is "Enabled" + +Before installing the configurations structures as `Config`s, the component will check to determine whether a given configuration is considered "enabled". The exact mechanism for this is TBD, but a candidate would be to have some `ConditionallyEnabled` trait that can be derived or implemented for configuration structures that defines the function `fn is_enabled(&self) -> bool`. + +#### Installing "Enabled" Configuration Structures + +After verifying a configuration structure is "enabled", the Component will install the structure as a `Config`, making it available for use by other components. + +## Unresolved Questions + +1. How strict should type checking be between fixed EDK II configurations and Patina configuration struct fields? (See: "Verifying Mapping Compatibility") +2. Should the portable configuration structures be stored in a single HOB, or in multiple HOBs (one per configuration structure)? (See: "Unpacking The Portable Configuration Structs") +3. How should custom portable structure definitions and conversion routines be provided for non-`#[repr(C)]`-safe configuration structs? (See: "Patina `ExternalConfigurationExtractor` Component" section) +4. How should the Patina `ExternalConfigurationExtractor` component determine whether a configuration structure is "enabled" and should be installed as a `Config`? (See: "Determining Whether a Configuration is "Enabled"") + +## Alternatives + +### Alternative 1: Manual Definition in Code Containing Invocation of Patina + +- PCD configuration values can be manually copied from the EDK II build system to Rust code containing the platform's invocation of Patina DXE Core +- Issues +    - Requires maintaining parity between EDK II and Rust code manually, which is extremely error prone + +### Alternative 2: Single PEIM Installing a HOB + +- 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` +- Issues +    - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures +    - All PCDs would be in the PEIM scope +    - There is no good way to provide per-component PCD overrides +    - Layout errors and struct field mismatches wouldn't be easily caught + +### Alternative 3: Single + Per-Component (or Per-`Config`) PEIMs + +- Instead of using a build system tool, one central PEIM and per-component PEIMs which would install split HOBs +- Issues +    - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures +    - All PCDs would still be in the PEIM scope +    - Layout errors and struct field mismatches wouldn't be easily caught +    - Unergonomic and hard to keep track of + +### Alternative 4: Consolidate All Required PCDs into a "PCD Store" HOB + +- Rather than trying to generate Rust-compatible structs from the EDK II build system, publish all required PCDs in a consolidated HOB which a Patina-side component would split apart into `Config`s +- Fields could be populated with PCDs gathered through a PCD macro: `PCD!(PcdNamespace, PcdName)` +- Issues +    - Deserialization to Rust-compatible types (and the errors which may arise) would be runtime, not build-time +    - Patina components would need to be aware of the EDK II build system, and would need to keep track of PCD names and namespaces + +## Considered EDK II Configurations + +The following EDK II configurations have been considered for initial inclusion in this RFC. The final list of allowed configurations is open to debate. + +| EDK-II-Related Configuration | Included | Reason | +| ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| FixedAtBuild PCDs | Yes | Fixed at build time | +| FeatureFlag PCDs | Yes | Fixed at built time | +| PatchableInModule PCDs | Yes | Fixed at build time | +| EDK II Build System Variable | Yes | Fixed at build time | +| Dynamic PCDs | No | Currently out of scope as their handling on the EDK II build-extension side would be very different than fixed-at-build values. This may be added to the current document, or added in a follow-up RFC. | +| C Compiler Flags | No | Lack of clear use case when EDK II Build System Variable support exists. | +| UEFI Variables | No | Outside scope and covered by Variable Services. | From 353526e4d384474bbb294c068081bf11b59fb2b6 Mon Sep 17 00:00:00 2001 From: Daniel Kouchekinia Date: Wed, 10 Sep 2025 09:47:15 -0700 Subject: [PATCH 8/9] Removed extra draft --- ...0000-edk-ii-build-system-config-interop.md | 1 + .../rfc/text/0000-fixed-pcd-config-interop.md | 194 ------------------ 2 files changed, 1 insertion(+), 194 deletions(-) delete mode 100644 docs/src/rfc/text/0000-fixed-pcd-config-interop.md diff --git a/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md b/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md index 6a23b7ce0..f791550f4 100644 --- a/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md +++ b/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md @@ -19,6 +19,7 @@ Additional details on the above steps can be found in the sections below. ## Change Log - 2025-09-08: Initial RFC created. +- 2025-09-09: Updated to second draft of RFC. ## Motivation diff --git a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md b/docs/src/rfc/text/0000-fixed-pcd-config-interop.md deleted file mode 100644 index 12ec50da4..000000000 --- a/docs/src/rfc/text/0000-fixed-pcd-config-interop.md +++ /dev/null @@ -1,194 +0,0 @@ -# RFC: EDK II Build System `Config` Interop - -This RFC proposes a translation mechanism to allow fixed-at-build EDK II build system configuration mechanisms--which will be referred to as "fixed EDK II configurations"--to be mapped to Patina `Config` structure fields through a level of indirection that will prevent the concept of PCDs from being directly introduced into Patina source code. - -For the purposes of this RFC, the following are considered fixed-at-build EDK II configurations: fixed PCDs (including FixedAtBuild, PatchableInModule, and FeatureFlags) and EDK II build variables. In the current version of this RFC, dynamic PCDs are considered out-of-scope, but may be introduced in a follow-up-RFC. A table of considered configurations to pass through with this RFC can be found in the "Considered EDK II Configurations" section of this document. - -Note that while fixed PCDs and PCD feature flags in EDK II can be overwritten per-module, per-component PCD overrides are considered out-of-scope in this RFC, and, should this document be approved, will likely be addressed in a follow-up RFC. - -While exact specifics are to-be-determined following a prototyping phase, the basic mechanism for configuration transfer is as follows: - -1. Procedural macros are used to generate representations of configuration structures that are stable across versions of Rust (likely `#[repr(C)]` and `#[align(1)]`) -2. On the EDK II-side, Patina configuration `.inf` files (as denoted with `MODULE TYPE = PATINA_CONFIGURATION`) map fixed EDK II configurations to Patina configuration structure fields -3. Using a top-level `Cargo.lock` file, a Rust EDK II build system plugin locates all valid configuration structures within Patina-related source files and creates portable instances of their stable representations populated with the values of the mapped fixed EDK II configurations -4. The Rust EDK II build system plugin places these representations into either one combined or several split (TBD) HOBs through an auto-generated PEIM -5. A Patina `ExternalConfigurationExtractor` Component parses these HOB(s), converting them into their non-portable struct counterparts, checking whether they should be considered "enabled" and installing them as `Config`s as appropriate. - -Additional details on the above steps can be found in the sections below. - -## Change Log - -- 2025-09-08: Initial RFC created. -- 2025-09-09: Updated to second draft of RFC. - -## Motivation - -The ability to configure Components is vital to ensue that they can be reused across projects (or platforms within those projects) which may have different memory layouts, feature requirements, and expected behaviors. While Patina Components can already be configured in Rust, integration with outside build systems minimizes room for mismatches between values configured within and outside of Patina. **Put simply, platform configuration ought to be defined only once, and EDK II mechanisms are the common denominator** - -While PCDs as a concept could be added to Rust themselves, this FRC opts for a level of indirection (in the form of a key-value store of configuration fields to fixed EDK II configuration values) to avoid tightly coupling Patina with a non-Cargo build system. While this RFC addresses EDK II specifically with an EDK II build plugin, other build systems could theoretically be supported by the same Patina-side changes with an equivalent plugin. - -## Goals - -The goal of this RFC is to create a translation layer that allows fixed EDK II configuration values to be applied to Patina configuration structures. This layer should: - -1. Be automatic and minimize potential for mismatched or incomplete configurations by emitting build-time errors when possible -2. Be ergonomic and require minimal user interaction from both EDK II and Patina -3. Be EDK II-unaware from patina's perspective -4. Be efficient of memory use -5. Provide a mechanism for evaluating whether a given configuration is to be considered enabled (and subsequently installed as a `Config`) - -## Requirements - -1. Configuration must not break compatibility with `cargo` and Cargo registries -2. Modifying configuration values must not require modifying or rebuilding Rust crates. -3. Modifying fixed EDK II configurations within the EDK II build system must be sufficient to change the corresponding value within the installed Rust `Config` - -## Software Components - -### 1. EDK II Build System Plugin - -The EDK II build system plugin, which will likely be written in combination of Rust and Python, will be responsible for gathering the mapping of fixed EDK II configuration values to Rust configurations, and arrange for a HOB to be published containing those values in the stable structure format. - -Whether this plugin would best be unstreamed to Tianocore or modularly included from a Patina EDK II repo as needed depends on the degree of invasiveness into the EDK II build system required to implement it. This will be best determined after an initial prototyping phase. - -#### Specifying Mappings - -Mappings between fixed EDK II configurations and Patina configuration structures will be defined in a `[PatinaConfigurations]` section of any `.inf` included in a `.dsc` file of `MODULE_TYPE = PATINA_CONFIGURATION`. Different syntax, will be used to specify different types of configurations. The following is an example of such a `.inf`, but the exact format is still TBD. - -```yaml -[Defines] - INF_VERSION                    = 0x00010005 - MODULE_TYPE                    = PATINA_COMPONENT - -[PatinaConfigurations] - MyConfigStruct.Field1 = PcdNameSpace.PcdName # PCD (FixedAtBuild, PatchableInModule) - MyConfigStruct.Field2 = PcdNameSpace.PcdFeatureFlagName # PCD (FeatureFlag) - MyConfigStruct.Field3 = $(BUILD_SYSTEM_VAR_NAME) # Build system variable -``` - -For simplicity, nested configuration structs are out-of-scope for this RFC. - -#### Determining Patina Portable Struct Layout - -The EDK II build plugin will use a form of cross-crate reflection to inspect the portable layout of referenced structs in the crates in which they are defined. These portable layouts are automatically generated by the procedural macros in the `ExternalConfigurationExtractor` crate, as described in its section below. To ensure the correct version of the crate is used, the top-level `Cargo.lock` file of the Crate containing the invocation of the Patina core will need to be referenced by the plugin. From there, the build plugin will download and inspect the source code as appropriate using either `syn` or the JSON output mode of `cargo rustdoc`. The exact mechanism is TBD pending the prototyping phase of this RFC. - -#### Verifying Mapping Compatibility - -Once the structure of the portable-version of the targeted structs are known, the EDK build plugin will verify three things: - -1. All configuration structure and field names exist in valid Rust configuration structures -2. If any field in a configuration structure is mapped, that all fields in that configuration structure is mapped -3. Compatibility between the data types of the fixed EDK II configurations and Patina configuration struct fields - -The strictness of the the third verification (type-checking) is an open question. - -Fixed-at-build PCDs come in `u8`, `u16`, `u32`, `u64`, `bool`, and byte-array (`VOID*`) variants. PCD feature flags come in type `bool` whereas EDK II build system variables are ASCII strings. - -While is is a reasonable assumption that, when it comes to integer types, that any unsigned integer type can be converted into the same type or a wider type, a few of the questions that remain are: - -- Should you be able to convert a wider integer into a narrower integer if the value fits in the narrower type? -- Should it be allowed that unsigned integer types be converted into signed types? If so, should it be a build error if that value would then be negative? -- Should byte arrays be allowed to be converted into `zerocopy::FromBytes` slices? Should non-byte-array types be able to? - -#### Constructing Instances of Portable Structs - -After the mappings have been verified to be compatible, instances of the portable structs will be generated and placed into either one or multiple HOBs (TBD). While the exact mechanism for this is TBD, likely candidates are compiling a Rust portion of the EDK II build plugin with `syn`, or generating byte-compatible (but not named) representations of the structs. - -#### Orchestrating the HOB(s) Installations - -Once HOB(s) have been generated, the EDK II build plugin will ensure the HOBs are installed by auto-generating and including a minimal PEIM which serves that purpose. - -### 2. Patina `ExternalConfigurationExtractor` Component - -The `ExternalConfigurationExtractor` component will have four primary responsibilities: - -1. Unpacking the portable configuration structs from the HOB(s) -2. Converting those portable configuration structs to their unmodified originals -3. Determining whether that configuration structure is "enabled" and ought to be installed -4. Installing "enabled" configuration structures as `Config`s - -To aid in the above steps, procedural macros defined within the crate will automatically generate the following for each compatible configuration structure: - -- A representation of the structure that is stable across versions of Rust (likely `#[repr(C)]` and `#[align(1)]`) -- A routine to convert the stable structure to the original form, to check whether it is "enabled", and to install it as a `Cargo` from a HOB - -Whether or not it is possible to achieve this without a manually specified `#[derive]` on all configuration structs or top-level build support is to-be-determined following the prototyping phase. - -If it is not possible for a portable representation of a configuration structure to be automatically generated (such as if the configuration struct contains a `Box`), a manually defined portable representation and conversion routine will need to be provided. The exact mechanism for this is an open question. - -#### Unpacking The Portable Configuration Structs - -The implementation of unpacking the portable configuration structs is dependent on whether a single HOB is used, or whether individual configuration structure HOBs are used. - -In the single-HOB option, the portable configuration structures are packed together in one contiguous structure, with each struct prepended by a the name of the structure as an ASCII string. - -In the multiple-HOB option, `HOB`s would be generated procedurally per configuration struct (with their HOB GUIDs generated procedurally as well). In this scenario the portable structs would be converted to `HOB` before further processing. - -Which of these methods should be used is an option question. The single-HOB option has the benefit of simplicity and room for future metadata which would facilitate extensions such as Component-specific `Config` instances, whereas the multi-HOB option is more in keeping with how existing HOB-based configurations are converted into `Config`s. - -#### Converting Portable Configuration Structs into their Original Counterparts - -The routines to convert the portable configuration structs into their original (likely non-Portable) counterparts will be generated via the procedural macro described above. - -#### Determining Whether a Configuration is "Enabled" - -Before installing the configurations structures as `Config`s, the component will check to determine whether a given configuration is considered "enabled". The exact mechanism for this is TBD, but a candidate would be to have some `ConditionallyEnabled` trait that can be derived or implemented for configuration structures that defines the function `fn is_enabled(&self) -> bool`. - -#### Installing "Enabled" Configuration Structures - -After verifying a configuration structure is "enabled", the Component will install the structure as a `Config`, making it available for use by other components. - -## Unresolved Questions - -1. How strict should type checking be between fixed EDK II configurations and Patina configuration struct fields? (See: "Verifying Mapping Compatibility") -2. Should the portable configuration structures be stored in a single HOB, or in multiple HOBs (one per configuration structure)? (See: "Unpacking The Portable Configuration Structs") -3. How should custom portable structure definitions and conversion routines be provided for non-`#[repr(C)]`-safe configuration structs? (See: "Patina `ExternalConfigurationExtractor` Component" section) -4. How should the Patina `ExternalConfigurationExtractor` component determine whether a configuration structure is "enabled" and should be installed as a `Config`? (See: "Determining Whether a Configuration is "Enabled"") - -## Alternatives - -### Alternative 1: Manual Definition in Code Containing Invocation of Patina - -- PCD configuration values can be manually copied from the EDK II build system to Rust code containing the platform's invocation of Patina DXE Core -- Issues -    - Requires maintaining parity between EDK II and Rust code manually, which is extremely error prone - -### Alternative 2: Single PEIM Installing a HOB - -- 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` -- Issues -    - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures -    - All PCDs would be in the PEIM scope -    - There is no good way to provide per-component PCD overrides -    - Layout errors and struct field mismatches wouldn't be easily caught - -### Alternative 3: Single + Per-Component (or Per-`Config`) PEIMs - -- Instead of using a build system tool, one central PEIM and per-component PEIMs which would install split HOBs -- Issues -    - The potential for struct-layout mismatches would require sharing a set of header files which cover all Patina configuration structures -    - All PCDs would still be in the PEIM scope -    - Layout errors and struct field mismatches wouldn't be easily caught -    - Unergonomic and hard to keep track of - -### Alternative 4: Consolidate All Required PCDs into a "PCD Store" HOB - -- Rather than trying to generate Rust-compatible structs from the EDK II build system, publish all required PCDs in a consolidated HOB which a Patina-side component would split apart into `Config`s -- Fields could be populated with PCDs gathered through a PCD macro: `PCD!(PcdNamespace, PcdName)` -- Issues -    - Deserialization to Rust-compatible types (and the errors which may arise) would be runtime, not build-time -    - Patina components would need to be aware of the EDK II build system, and would need to keep track of PCD names and namespaces - -## Considered EDK II Configurations - -The following EDK II configurations have been considered for initial inclusion in this RFC. The final list of allowed configurations is open to debate. - -| EDK-II-Related Configuration | Included | Reason | -| ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| FixedAtBuild PCDs | Yes | Fixed at build time | -| FeatureFlag PCDs | Yes | Fixed at built time | -| PatchableInModule PCDs | Yes | Fixed at build time | -| EDK II Build System Variable | Yes | Fixed at build time | -| Dynamic PCDs | No | Currently out of scope as their handling on the EDK II build-extension side would be very different than fixed-at-build values. This may be added to the current document, or added in a follow-up RFC. | -| C Compiler Flags | No | Lack of clear use case when EDK II Build System Variable support exists. | -| UEFI Variables | No | Outside scope and covered by Variable Services. | From c8718fef0536a8c57b5118eaba3687290b29b0a3 Mon Sep 17 00:00:00 2001 From: Daniel Kouchekinia Date: Wed, 10 Sep 2025 10:44:12 -0700 Subject: [PATCH 9/9] Added unresolved question regarding evaluation capabilities in config mappings --- .../0000-edk-ii-build-system-config-interop.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md b/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md index f791550f4..29e81c281 100644 --- a/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md +++ b/docs/src/rfc/text/0000-edk-ii-build-system-config-interop.md @@ -66,7 +66,16 @@ Mappings between fixed EDK II configurations and Patina configuration structures MyConfigStruct.Field3 = $(BUILD_SYSTEM_VAR_NAME) # Build system variable ``` -For simplicity, nested configuration structs are out-of-scope for this RFC. +For simplicity, nested configuration structures are out-of-scope for this RFC. + +*Whether additional evaluation capabilities to these mappings should be added is an open question.* Limiting fields to be set only directly to PCD values is limiting and may incentivize configuration struct authors to tailor their implementations to match PCD layouts (such as having bitfield layouts match their C-counterparts). Additional evalulation could possibly be provided through EDK II side Rust code attached to the `.inf` as source, or through inline evaluation through a scripting language such as Python, which may look something like: + +```yaml +[PatinaConfigurations] + MyConfigStruct.Field1 = PcdNameSpace.PcdName1 & 0x1 + MyConfigStruct.Field2 = PcdNameSpace.PcdName2 + PcdNameSpace.PcdName3 + PcdNameSpace.PcdName4 + MyConfigStruct.Field3 = $(BUILD_SYSTEM_VAR_NAME_1) + "_" + $(BUILD_SYSTEM_VAR_NAME_2) +``` #### Determining Patina Portable Struct Layout @@ -114,7 +123,7 @@ To aid in the above steps, procedural macros defined within the crate will autom Whether or not it is possible to achieve this without a manually specified `#[derive]` on all configuration structs or top-level build support is to-be-determined following the prototyping phase. -If it is not possible for a portable representation of a configuration structure to be automatically generated (such as if the configuration struct contains a `Box`), a manually defined portable representation and conversion routine will need to be provided. The exact mechanism for this is an open question. +If it is not possible for a portable representation of a configuration structure to be automatically generated (such as if the configuration struct contains a `Box`), a manually defined portable representation and conversion routine will need to be provided. The exact mechanism for this is an open question, but one potential solution is to allow a portable data structure to be specified and marked with some `#[derive]` macro that links the portable representation to the main configuration structure and allows mapping to the portable structure's field instead. #### Unpacking The Portable Configuration Structs @@ -144,6 +153,7 @@ After verifying a configuration structure is "enabled", the Component will insta 2. Should the portable configuration structures be stored in a single HOB, or in multiple HOBs (one per configuration structure)? (See: "Unpacking The Portable Configuration Structs") 3. How should custom portable structure definitions and conversion routines be provided for non-`#[repr(C)]`-safe configuration structs? (See: "Patina `ExternalConfigurationExtractor` Component" section) 4. How should the Patina `ExternalConfigurationExtractor` component determine whether a configuration structure is "enabled" and should be installed as a `Config`? (See: "Determining Whether a Configuration is "Enabled"") +5. Should more advanced evaluation capabilities be added to the configuration mapping functionality? If so, what form should that take? (See: "Specifying Mappings") ## Alternatives