|
| 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 |
0 commit comments