1 | # **Platform Runtime Mechanism**
|
---|
2 |
|
---|
3 | Platform Runtime Mechanism (PRM) introduces the capability of moving platform-specific code out of SMM and into a
|
---|
4 | code module that executes within the OS context. Moving this firmware to the OS context provides better transparency
|
---|
5 | and mitigates the negative system impact currently accompanied with SMM solutions. Futhermore, the PRM code is
|
---|
6 | packaged into modules with well-defined entry points, each representing a specific PRM functionality.
|
---|
7 |
|
---|
8 | For more details on PRM, refer to the [Platform Runtime Mechanism Specification on uefi.org](https://uefi.org/sites/default/files/resources/Platform%20Runtime%20Mechanism%20-%20with%20legal%20notice.pdf).
|
---|
9 |
|
---|
10 | The `PrmPkg` maintained in this branch provides a single cohesive set of generic PRM functionality that is intended
|
---|
11 | to be leveraged by platform firmware with minimal overhead to integrate PRM functionality in the firmware.
|
---|
12 |
|
---|
13 | > By default, the build makes use of a new ACPI OperationRegion type specifically introduced for PRM called
|
---|
14 | `PlatformRtMechanism`. Support for this OperationRegion is planned for the next release of the ACPI specification.
|
---|
15 | However, support for `PlatformRtMechanism` is already included in the iASL Compiler/Disassembler for early prototyping
|
---|
16 | (i.e. this package). If you would like the default build to work and/or to use PRM handlers that are invoked
|
---|
17 | through ACPI, iASL compiler [20200528](https://www.intel.com/content/www/us/en/download/774849/774861/acpi-component-architecture-downloads-previous-releases-2020.html)
|
---|
18 | or greater must be used. If you are only interested in compiling the code and/or using direct call style PRM
|
---|
19 | handlers, you can simply remove `PrmSsdtInstallDxe` from `PrmPkg.dsc`.
|
---|
20 |
|
---|
21 | The changes in the ACPI Specification include two elements:
|
---|
22 |
|
---|
23 | 1. `BIT20` in Platform-Wide _OSC Capabilities DWORD2 will be used by an OS to indicate support for PRM
|
---|
24 | 2. A new Operation Region Address Space Identifier Value is defined as `0xB` for `PlatformRtMechanism`
|
---|
25 |
|
---|
26 | ## How to Build PrmPkg
|
---|
27 |
|
---|
28 | As noted earlier, resources in `PrmPkg` are intended to be referenced by a platform firmware so it can adopt support
|
---|
29 | for PRM. In that case, the platform firmware should add the `PrmConfigDxe` and `PrmLoaderDxe` drivers to its DSC and
|
---|
30 | FDF files so they are built in the platform firmware build and dispatched during its runtime. All that is left is to
|
---|
31 | add individual PRM modules to the DSC and FDF. These can be built from source or included as binaries into the platform
|
---|
32 | firmware flash map.
|
---|
33 |
|
---|
34 | ### PrmPkg Standalone Build
|
---|
35 |
|
---|
36 | To build `PrmPkg` as a standalone package:
|
---|
37 |
|
---|
38 | 1. If new to EDK II, follow the directions in [Getting Started with EDK II](https://github.com/tianocore/tianocore.github.io/wiki/Getting-Started-with-EDK-II)
|
---|
39 |
|
---|
40 | 2. Clone the *master* branch on the edk2 repository locally \
|
---|
41 | ``git clone https://github.com/tianocore/edk2.git``
|
---|
42 |
|
---|
43 | 3. Change to the edk2 workspace directory \
|
---|
44 | ``cd edk2``
|
---|
45 |
|
---|
46 | 4. Run *edksetup* to set local environment variables needed for build
|
---|
47 | * Windows:
|
---|
48 | * ``edksetup.bat``
|
---|
49 | * Linux:
|
---|
50 | * If you have not already built BaseTools:
|
---|
51 | * ``make -C BaseTools``
|
---|
52 | * ``. edksetup.sh``
|
---|
53 |
|
---|
54 | 5. Build PrmPkg \
|
---|
55 |
|
---|
56 | The PrmPkg can be built targeting the X64 and AArch64 architectures. PRM is not supported on IA32 and ARM primarily
|
---|
57 | because the OS support for PRM is only in 64 bit OSes. In addition, the MSVC toolchain does not support export tables
|
---|
58 | on IA32 with the unique UEFI configuration required.
|
---|
59 |
|
---|
60 | * X64
|
---|
61 |
|
---|
62 | ``build -p PrmPkg/PrmPkg.dsc -a X64``
|
---|
63 | > ***Note***: Due to the way PRM modules are compiled with exports, **only building on Visual Studio compiler tool
|
---|
64 | chains has been tested**.
|
---|
65 |
|
---|
66 | > ***Note***: \
|
---|
67 | > This package has been used without modification in several environments including client, server,
|
---|
68 | > and virtual systems.
|
---|
69 | >
|
---|
70 | > You can add your own PRM modules into the build and check them with the `PrmInfo` UEFI application described
|
---|
71 | > later in this document and dump the PRMT table in the OS to check if your PRM module is represented as expected.
|
---|
72 |
|
---|
73 | * AArch64
|
---|
74 | ``build -p PrmPkg/PrmPkg.dsc -a AARCH64 -t GCC5``
|
---|
75 |
|
---|
76 | > ***Note***: Only builds with the GCC5 toolchain have been tested.
|
---|
77 | > ***Note***: For builds with the GCC5 toolchain, the PrmModuleExportDescriptor and any other handler entry points
|
---|
78 | symbols, to be listed in the PRMT, must be explicitly preserved by enumerating these in the AARCH64 linker flags.
|
---|
79 | The --require-defined linker flag must be used for each symbol to be preserved.
|
---|
80 |
|
---|
81 | ### PRM Platform GUID
|
---|
82 |
|
---|
83 | **IMPORTANT** PRM has a concept of a "Platform GUID" which associates a specific platform with a set of PRM modules
|
---|
84 | built for that platform. This GUID is used to ensure system compatibility for a given collection of PRM modules.
|
---|
85 |
|
---|
86 | Therefore, each PRM module must only target a single platform and each platform must have a unique GUID. Even if a
|
---|
87 | PRM module is unchanged between two different platforms now, there is no guarantee that will remain the case so always
|
---|
88 | assign a unique Platform GUID for each platform.
|
---|
89 |
|
---|
90 | The PRM Platform GUID is primarily used during PRM module runtime updates in the OS to ensure that the Platform GUID
|
---|
91 | in the system's ACPI table (PRMT) matches the Platform GUID of the module requested for update. Even if runtime
|
---|
92 | updates are not a planned feature for a given platform, still assign a unique Platform GUID for binary module
|
---|
93 | identification (the Platform GUID is in the module's export descriptor) and to ensure such updates can be seamlessly
|
---|
94 | supported in the future if needed.
|
---|
95 |
|
---|
96 | In the `PrmPkg` implementation, the Platform GUID is automatically derived from the PLATFORM_GUID in the DSC file of
|
---|
97 | the package being built.
|
---|
98 |
|
---|
99 | ### Build Output
|
---|
100 |
|
---|
101 | Like a typical EDK II package, the PrmPkg binary build output can be found in the Build directory in the edk2
|
---|
102 | workspace. The organization in that directory follows the same layout as other EDK II packages.
|
---|
103 |
|
---|
104 | For example, that path to PRM module sample binaries for a DEBUG VS2022 X64 build is: \
|
---|
105 | ``edk2/Build/Prm/DEBUG_VS2022/X64/PrmPkg/Samples``
|
---|
106 |
|
---|
107 | ## Overview
|
---|
108 |
|
---|
109 | At a high-level, PRM can be viewed from three levels of granularity:
|
---|
110 |
|
---|
111 | 1. `PRM interface` - Encompassing the entirety of firmware functionalities and data provided to OS runtime. Most
|
---|
112 | information is provided through ACPI tables to be agnostic to a UEFI implementation.
|
---|
113 | 2. `PRM module` - An independently updatable package of PRM handlers. The PRM interface will be composed of multiple
|
---|
114 | PRM modules. This requirement allows for the separation of OEM and IHV PRM code, each of which can be serviced
|
---|
115 | independently.
|
---|
116 | 3. `PRM handler` - The implementation/callback of a single PRM functionality as identified by a GUID.
|
---|
117 |
|
---|
118 | ## Firmware Design
|
---|
119 |
|
---|
120 | The firmware has three key generic drivers to support PRM:
|
---|
121 |
|
---|
122 | 1. A `PRM Loader driver` - Functionality is split across three phases:
|
---|
123 | 1. Discover - Find all PRM modules in the firmware image made available by the platform firmware author.
|
---|
124 | * This phase includes verifying authenticity/integrity of the image, the image executable type, the export
|
---|
125 | table is present and the PRM Export Module Descriptor is present and valid.
|
---|
126 | 2. Process - Convert PRM handler GUID to name mappings in the PRM Module Export Descriptor to PRM handler Name
|
---|
127 | to physical address mappings required to construct the PRM ACPI table.
|
---|
128 | 3. Publish - Publish the PRM ACPI table using the information from the Process phase.
|
---|
129 |
|
---|
130 | 2. A `PRM Configuration driver` - A generic driver responsible for processing PRM module configuration information
|
---|
131 | consumed through a `PRM_CONFIG_PROTOCOL` per PRM module instance. Therefore, the `PRM_CONFIG_PROTOCOL` serves
|
---|
132 | as the dynamic interface for this driver to process PRM module resources and prepare the module's data to be
|
---|
133 | configured properly for OS runtime.
|
---|
134 |
|
---|
135 | 3. A `PRM Module` - Not a single driver but a user written PE/COFF image that follows the PRM module authoring process.
|
---|
136 | A PRM module groups together cohesive sets of PRM functionality into functions referred to as "PRM handlers".
|
---|
137 |
|
---|
138 | ## PrmPkg Code Organization
|
---|
139 |
|
---|
140 | The package follows a standard EDK II style package format. The list below contains some notable areas to
|
---|
141 | explore in the package:
|
---|
142 |
|
---|
143 | * [ACPI Table Definitions](PrmPkg/PrmLoaderDxe/PrmAcpiTable.h)
|
---|
144 | * [Common Interface Definitions](PrmPkg/Include)
|
---|
145 | * [PRM Config Driver](PrmPkg/PrmConfigDxe)
|
---|
146 | * [PRM Loader Driver](PrmPkg/PrmLoaderDxe)
|
---|
147 | * [Sample PRM Modules](PrmPkg/Samples)
|
---|
148 |
|
---|
149 | While the package does provide sample PRM modules to be used as a reference, actual PRM modules should not be
|
---|
150 | maintained in PrmPkg. It is intended to only contain PRM infrastructure code and a few samples of how to use
|
---|
151 | that infrastructure. The PrmPkg is meant to be used as-is by firmware that supports PRM. Any shortcomings that
|
---|
152 | prevent the package from being used as-is should be addressed directly in PrmPkg.
|
---|
153 |
|
---|
154 | ## PRM Information UEFI Application
|
---|
155 |
|
---|
156 | A UEFI application is provided in this package called `PrmInfo` that allows a user to display and test PRM
|
---|
157 | modules on their system.
|
---|
158 |
|
---|
159 | [Link to application source code](PrmPkg/Application/PrmInfo).
|
---|
160 |
|
---|
161 | This application is intended to be helpful during PRM enabling by allowing the user to:
|
---|
162 |
|
---|
163 | 1. Confirm that their firmware port of the PRM infrastructure implemented in this package is functioning correctly.
|
---|
164 | 2. Quickly get information about what PRM modules and handlers that are present on a given system.
|
---|
165 | 3. Quickly test PRM handlers without booting into a full operating system.
|
---|
166 | 4. Develop and exercise PRM handlers prior to the availability of an operating system that is PRM aware.
|
---|
167 |
|
---|
168 | Execute the application help command for detailed usage instructions and examples of how to use the application: \
|
---|
169 | ``PrmInfo -?``
|
---|
170 |
|
---|
171 | *Example Usage:*
|
---|
172 |
|
---|
173 | 
|
---|
174 |
|
---|
175 | ## PRM Module
|
---|
176 |
|
---|
177 | > ***Note***: You can find simple examples of PRM modules in the Samples directory of this package.
|
---|
178 | > [Samples/Readme.md](PrmPkg/Samples/Readme.md) has more information.
|
---|
179 |
|
---|
180 | By default, the EDK II implementation of UEFI does not allow images with the subsystem type
|
---|
181 | `IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER` to be built with exports.
|
---|
182 |
|
---|
183 | ```txt
|
---|
184 | ERROR - Linker #1294 from LINK : fatal exports and import libraries are not supported with /SUBSYSTEM:EFI_RUNTIME_DRIVER
|
---|
185 | ```
|
---|
186 |
|
---|
187 | This can adjusted in the MSVC linker options.
|
---|
188 |
|
---|
189 | The subsystem type is changed in the firmware build to allow the export table to be added but the subsystem type in the
|
---|
190 | final image is still `0xC` (`EFI Runtime Driver`). This is important to allow the DXE dispatcher to use its standard
|
---|
191 | image verification and loading algorithms to load the image into permanent memory during the DXE execution phase.
|
---|
192 |
|
---|
193 | All firmware-loaded PRM modules are loaded into a memory buffer of type `EfiRuntimeServicesCode`. This means the
|
---|
194 | operating system must preserve all PRM handler code and the buffer will be reflected in the UEFI memory map. The
|
---|
195 | execution for invoking PRM handlers is the same as that required for UEFI Runtime Services, notably 4KiB or more of
|
---|
196 | available stack space must be provided and the stack must be 16-byte aligned.
|
---|
197 |
|
---|
198 | ***Note:*** Long term it is possible to similarly load the modules into a `EfiRuntimeServicesCode` buffer and perform
|
---|
199 | relocation fixups with a new EFI module type for PRM if desired. It was simply not done since it is not essential
|
---|
200 | for this POC.
|
---|
201 |
|
---|
202 | Where possible, PRM module information is stored and generated using industry compiler tool chains. This is a key
|
---|
203 | motivation behind using PE/COFF export tables to expose PRM module information and using a single PRM module binary
|
---|
204 | definition consistent between firmware and OS load.
|
---|
205 |
|
---|
206 | ### PRM Module Exports
|
---|
207 |
|
---|
208 | A PRM module must contain at least two exports: A PRM Module Export Descriptor and at least one PRM handler. Here's
|
---|
209 | an example of an export table from a PRM module that has a single PRM handler:
|
---|
210 |
|
---|
211 | ```txt
|
---|
212 | 0000000000005000: 00 00 00 00 FF FF FF FF 00 00 00 00 3C 50 00 00 ............<P..
|
---|
213 | 0000000000005010: 01 00 00 00 02 00 00 00 02 00 00 00 28 50 00 00 ............(P..
|
---|
214 | 0000000000005020: 30 50 00 00 38 50 00 00 78 13 00 00 20 40 00 00 0P..8P..x... @..
|
---|
215 | 0000000000005030: 5D 50 00 00 7C 50 00 00 00 00 01 00 50 72 6D 53 ]P..|P......PrmS
|
---|
216 | 0000000000005040: 61 6D 70 6C 65 43 6F 6E 74 65 78 74 42 75 66 66 ampleContextBuff
|
---|
217 | 0000000000005050: 65 72 4D 6F 64 75 6C 65 2E 64 6C 6C 00 44 75 6D erModule.dll.Dum
|
---|
218 | 0000000000005060: 70 53 74 61 74 69 63 44 61 74 61 42 75 66 66 65 pStaticDataBuffe
|
---|
219 | 0000000000005070: 72 50 72 6D 48 61 6E 64 6C 65 72 00 50 72 6D 4D rPrmHandler.PrmM
|
---|
220 | 0000000000005080: 6F 64 75 6C 65 45 78 70 6F 72 74 44 65 73 63 72 oduleExportDescr
|
---|
221 | 0000000000005090: 69 70 74 6F 72 00 iptor.
|
---|
222 |
|
---|
223 | 00000000 characteristics
|
---|
224 | FFFFFFFF time date stamp
|
---|
225 | 0.00 version
|
---|
226 | 1 ordinal base
|
---|
227 | 2 number of functions
|
---|
228 | 2 number of names
|
---|
229 |
|
---|
230 | ordinal hint RVA name
|
---|
231 |
|
---|
232 | 1 0 00001378 DumpStaticDataBufferPrmHandler
|
---|
233 | 2 1 00004020 PrmModuleExportDescriptor
|
---|
234 |
|
---|
235 | ```
|
---|
236 |
|
---|
237 | ### PRM Image Format
|
---|
238 |
|
---|
239 | PRM modules are ultimately PE/COFF images. However, when packaged in firmware the PE/COFF image is placed into a
|
---|
240 | Firmware File System (FFS) file. This is transparent to the operating system but done to better align with the typical
|
---|
241 | packaging of PE32(+) images managed in the firmware binary image. In the dump of the PRM FV binary image shown earlier,
|
---|
242 | the FFS sections placed by EDK II build tools ("DXE dependency", "User interface", "Version") that reside alongside the
|
---|
243 | PE/COFF binary are shown. A PRM module can be placed into a firmware image as a pre-built PE/COFF binary or built
|
---|
244 | during the firmware build process. In either case, the PE/COFF section is contained in a FFS file as shown in that
|
---|
245 | image.
|
---|
246 |
|
---|
247 | ### PRM Module Implementation
|
---|
248 |
|
---|
249 | To simplify building the PRM Module Export Descriptor, a PRM module implementation can use the following macros to mark
|
---|
250 | functions as PRM handlers. In this example, a PRM module registers three functions by name as PRM handlers with the
|
---|
251 | associated GUIDs.
|
---|
252 |
|
---|
253 | ```c
|
---|
254 | //
|
---|
255 | // Register the PRM export information for this PRM Module
|
---|
256 | //
|
---|
257 | PRM_MODULE_EXPORT (
|
---|
258 | PRM_HANDLER_EXPORT_ENTRY (PRM_HANDLER_1_GUID, PrmHandler1),
|
---|
259 | PRM_HANDLER_EXPORT_ENTRY (PRM_HANDLER_2_GUID, PrmHandler2),
|
---|
260 | PRM_HANDLER_EXPORT_ENTRY (PRM_HANDLER_N_GUID, PrmHandlerN)
|
---|
261 | );
|
---|
262 | ```
|
---|
263 |
|
---|
264 | `PRM_MODULE_EXPORT` take a variable-length argument list of `PRM_HANDLER_EXPORT_ENTRY` entries that each describe an
|
---|
265 | individual PRM handler being exported for the module. Ultimately, this information is used to define the structure
|
---|
266 | necessary to statically allocate the PRM Module Export Descriptor Structure (and its PRM Handler Export Descriptor
|
---|
267 | substructures) in the image.
|
---|
268 |
|
---|
269 | Another required export for PRM modules is automatically provided in `PrmModule.h`, a header file that pulls together
|
---|
270 | all the includes needed to author a PRM module. This export is `PRM_MODULE_UPDATE_LOCK_EXPORT`. By including,
|
---|
271 | `PrmModule.h`, a PRM module has the `PRM_MODULE_UPDATE_LOCK_DESCRIPTOR` automatically exported.
|
---|
272 |
|
---|
273 | ## PRM Handler Constraints
|
---|
274 |
|
---|
275 | At this time, PRM handlers are restricted to a maximum identifier length of 128 characters. This is checked when using
|
---|
276 | the `PRM_HANDLER_EXPORT` macro by using a static assert that reports a violation at build-time.
|
---|
277 |
|
---|
278 | PRM handlers are **not** allowed to use UEFI Runtime Services and should not rely upon any UEFI constructs. For the
|
---|
279 | purposes of this POC, this is currently not explicitly enforced but should be in the final changes.
|
---|