Skip to content

Commit c7881cf

Browse files
makubackiapop5
authored andcommitted
BaseTools/UefiVarPatcher: Add plugin (#1326)
## Description Adds a build plugin that runs in post-build to modify the UEFI variable store in the ROM image based on variable data in an XML if the plugin is enabled. Details are in UefiVarPatcher/ReadMe.md. - [ ] Impacts functionality? - [ ] Impacts security? - [ ] Breaking change? - [ ] Includes tests? - [x] Includes documentation? - [x] Backport to release branch? ## How This Was Tested - edk2-pytool-extensions unit tests - Run plugin locally in mu_tiano_platforms - Tested on a physical platform build ## Integration Instructions - See `BaseTools/Plugin/UefiVarPatcher/ReadMe.md` for details Signed-off-by: Michael Kubacki <[email protected]>
1 parent c298bdf commit c7881cf

File tree

3 files changed

+191
-0
lines changed

3 files changed

+191
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# ROM UEFI Variable Patcher Build Plugin
2+
3+
## Copyright
4+
5+
Copyright (c) Microsoft Corporation.
6+
SPDX-License-Identifier: BSD-2-Clause-Patent
7+
8+
## About
9+
10+
This plugin can be used to patch UEFI variables in a ROM image in post-build from varriable information provided in
11+
an XML file.
12+
13+
### UEFI Build Plugin
14+
15+
You must add the `uefivarpatcher` scope to you build configuration to enable this plugin.
16+
17+
The plugin operates in the `do_post_build function`. This plugin uses the following variables from the build
18+
environment:
19+
20+
1. `VAR_PATCH_CONFIG_DIR` - [**REQUIRED**] - Package that contains the config file (`BuiltInVars.xml`)
21+
2. `FLASH_SIZE` - [**REQUIRED**] - Total flash image size.
22+
3. `FLASH_REGION_NVSTORAGE_OFFSET` - [**REQUIRED**] - Offset in bytes to the NV storage region.
23+
4. `FLASH_REGION_NVSTORAGE_SIZE` - [**REQUIRED**] - Size in bytes of the NV storage region.
24+
5. `OUTPUT_ROM` - [**REQUIRED**] - Output ROM file path. Multiple ROM files can be semicolon separated.
25+
26+
The variable configuration data must be provided in an XML file named `BuiltInVars.xml`. If a variable in the XML file
27+
does not exist in the ROM image, the script will attempt to create it.
28+
29+
### XML File Example
30+
31+
```xml
32+
<?xml version="1.0"?>
33+
<Variables>
34+
<Variable>
35+
<Name>Variable1</Name>
36+
<GUID>12345678-1234-5678-1234-567812345678</GUID>
37+
<Attributes>0x0000000000000001</Attributes>
38+
<Data type="hex">12345678</Data>
39+
</Variable>
40+
<Variable>
41+
<Name>Variable2</Name>
42+
<GUID>87654321-4321-6789-4321-678943216789</GUID>
43+
<Attributes>0x0000000000000002</Attributes>
44+
<Data type="hex">87654321</Data>
45+
</Variable>
46+
</Variables>
47+
```
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# @file UefiVarPatcher.py
2+
# Plugin used to updated UEFI variables in a ROM image
3+
#
4+
# Copyright (c) Microsoft Corporation
5+
# SPDX-License-Identifier: BSD-2-Clause-Patent
6+
#
7+
import logging
8+
import os
9+
10+
import edk2toollib.uefi.edk2.variablestore_manulipulations as VarStore
11+
import edk2toollib.uefi.edk2.variable_format as VF
12+
13+
from edk2toolext.environment.plugintypes.uefi_build_plugin import IUefiBuildPlugin
14+
from edk2toolext.environment.uefi_build import UefiBuilder
15+
from edk2toolext.uefi.patch_var_store import (
16+
load_variable_xml,
17+
patch_variables,
18+
create_variables,
19+
)
20+
21+
PLUGIN_NAME = "UefiVarPatcher"
22+
CONFIG_FILE_NAME = "BuiltInVars.xml"
23+
24+
25+
class UefiVarPatcher(IUefiBuildPlugin):
26+
def do_post_build(self, builder: UefiBuilder) -> int:
27+
"""UEFI variable patching post-build functionality.
28+
29+
Args:
30+
builder (UefiBuilder): A UEFI builder object for this build.
31+
32+
Returns:
33+
int: Returrn code. 0 for success, -1 for failure.
34+
"""
35+
logging.info(f"PLUGIN {PLUGIN_NAME}: Patching initial NvStore contents")
36+
37+
# Not an error; no VAR_PATCH_CONFIG_DIR defined, so don't try to apply built-in vars.
38+
if not builder.env.GetValue("VAR_PATCH_CONFIG_DIR"):
39+
logging.debug(
40+
f"{PLUGIN_NAME} Post Build is not active due to VAR_PATCH_CONFIG_DIR not defined."
41+
)
42+
return 0
43+
44+
built_in_vars_xml_path = (
45+
builder.edk2path.GetAbsolutePathOnThisSystemFromEdk2RelativePath(
46+
builder.env.GetValue("VAR_PATCH_CONFIG_DIR"),
47+
CONFIG_FILE_NAME,
48+
log_errors=False,
49+
)
50+
)
51+
52+
# Not an error; if `CONFIG_FILE_NAME` does not exist, platform has no variables defined.
53+
if not built_in_vars_xml_path:
54+
logging.debug(
55+
f"{PLUGIN_NAME} Post Build is not active - {CONFIG_FILE_NAME} found."
56+
)
57+
return 0
58+
59+
logging.info(
60+
f"{PLUGIN_NAME} File found in platform directory. Updating ROM image var store."
61+
)
62+
63+
# If FLASH_SIZE not defined, error.
64+
if not builder.env.GetValue("FLASH_SIZE"):
65+
logging.error(
66+
f"{PLUGIN_NAME} Post Build failed due to FLASH_SIZE not being specified."
67+
)
68+
return -1
69+
70+
# If FLASH_NVSTORAGE_OFFSET not defined, error.
71+
if not builder.env.GetValue("FLASH_REGION_NVSTORAGE_OFFSET"):
72+
logging.error(
73+
f"{PLUGIN_NAME}: Post Build failed due to FLASH_REGION_NVSTORAGE_OFFSET not being specified."
74+
)
75+
return -1
76+
77+
# If FLASH_NVSTORAGE_SIZE not defined, error.
78+
if not builder.env.GetValue("FLASH_REGION_NVSTORAGE_SIZE"):
79+
logging.error(
80+
f"{PLUGIN_NAME}: Post Build failed due to FLASH_REGION_NVSTORAGE_SIZE not being specified."
81+
)
82+
return -1
83+
84+
# If FLASH_NVSTORAGE_SIZE not defined, error.
85+
if not builder.env.GetValue("OUTPUT_ROM"):
86+
logging.error(
87+
f"{PLUGIN_NAME}: Post Build failed due to OUTPUT_ROM not being specified."
88+
)
89+
return -1
90+
91+
# OUTPUT_ROM can be a semicolon(;)-delimited list of output ROMs that must be patched.
92+
out_roms = builder.env.GetValue("OUTPUT_ROM").split(";")
93+
for out_rom in out_roms:
94+
# If OUTPUT_ROM is not defined, but XML exists, implies attempt to patch a non-existent ROM. Error.
95+
if not os.path.isfile(out_rom):
96+
logging.error(
97+
f"{PLUGIN_NAME} Post Build failed due to ROM file '{out_rom}' not found!"
98+
)
99+
return -1
100+
101+
# Calculate the offset of the varstore within the file itself.
102+
flash_size = int(builder.env.GetValue("FLASH_SIZE"), 16)
103+
rom_size = os.path.getsize(out_rom)
104+
var_store_rom_offset = (
105+
rom_size
106+
- flash_size
107+
+ int(builder.env.GetValue("FLASH_REGION_NVSTORAGE_OFFSET"), 16)
108+
)
109+
110+
# Load the variable store from the file.
111+
var_store = VarStore.VariableStore(
112+
out_rom,
113+
store_base=var_store_rom_offset,
114+
store_size=int(builder.env.GetValue("FLASH_REGION_NVSTORAGE_SIZE"), 16),
115+
)
116+
117+
# Print information about the current variables.
118+
for var in var_store.variables:
119+
if var.State == VF.VAR_ADDED:
120+
print(f"Var Found: '{var.VendorGuid}:{var.Name}'")
121+
122+
# Attempt to load the set script file.
123+
set_vars = load_variable_xml(built_in_vars_xml_path)
124+
125+
# Attempt to patch existing variables in the var store.
126+
create_vars = patch_variables(set_vars, var_store)
127+
128+
# If we had variables we were unable to update, let's create them now.
129+
create_variables(create_vars, var_store)
130+
131+
var_store.flush_to_file()
132+
133+
return 0
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## @file
2+
# Build Plugin used to updated UEFI variables in a ROM image
3+
#
4+
# Copyright (c) Microsoft Corporation.
5+
# SPDX-License-Identifier: BSD-2-Clause-Patent
6+
##
7+
{
8+
"scope": "uefivarpatcher",
9+
"name": "UEFI Variable ROM Patcher",
10+
"module": "UefiVarPatcher"
11+
}

0 commit comments

Comments
 (0)