Skip to content

Commit

Permalink
Support xml hash check (#421)
Browse files Browse the repository at this point in the history
## Description

Currently, there is no version check btwn loaded xml and bios. 
To prevent user loading the wrong xml for BIOS running on SUT.
This PR makes the below changes.
1. Calc all xml node hash during bios build and put the
hash/guid/var_name in generated header.
2. BIOS can uses this guid and hash saves to a varialbe call 
3. When configeditor loads an xml file, it calc the hash of loaded xml
file and tries to find the xml hash from bios variable and then compare
the hash.

4. if the hash in bios and loaded xml is different, it will show a
warning message to user

![image](https://github.com/user-attachments/assets/f1ba6de7-aa3d-46c9-a008-7db4722ba096)



For details on how to complete these options and their meaning refer to
[CONTRIBUTING.md](https://github.com/microsoft/mu/blob/HEAD/CONTRIBUTING.md).

- [ ] Impacts functionality?
- [ ] Impacts security?
- [x] Breaking change?
- [x] Includes tests?
- [ ] Includes documentation?

## How This Was Tested

Tested on BIOS with variable contains hash and make sure the warning msg
will pop upt when the hash is different

## Integration Instructions

To have warning msg for hash mismatch, platform BIOS include
SetupDataPkg/SetupDataDxe/SchemaXmlHashDriver/SchemaXmlHash.inf so that
it will set variable with xml hash

---------

Co-authored-by: Kane Chen <[email protected]>
  • Loading branch information
kanechen66 and Kane Chen authored Nov 26, 2024
1 parent d1e7861 commit 8b91b57
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 3 deletions.
1 change: 1 addition & 0 deletions SetupDataPkg/Include/Library/PlatformConfigDataLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <Uefi.h>
#include <ConfigStdStructDefs.h>

extern CHAR8 *gSchemaXmlHash;
extern KNOB_DATA gKnobData[];
extern UINTN gNumKnobs;
extern PROFILE gProfileData[];
Expand Down
21 changes: 21 additions & 0 deletions SetupDataPkg/Tools/BoardMiscInfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,27 @@ def get_mfci_policy():
return result


def get_schema_xml_hash_from_bios():
"""
Function to get SCHEMA_XML_HASH variable from BIOS
Returns the SCHEMA_XML_HASH in hexadecimal format or 'Unknown' if an error occurs.
"""
SCHEMA_XML_HASH_VAR_NAME = "SCHEMA_XML_HASH"
SCHEMA_XML_HASH_GUID = "1321e012-14c5-42db-8ca9-e7971d881518"

UefiVar = UefiVariable()
(errorcode, data) = UefiVar.GetUefiVar(SCHEMA_XML_HASH_VAR_NAME, SCHEMA_XML_HASH_GUID)

result = None
if errorcode == 0:
# Remove null byte if exist
if data[-1] == 0:
data = data[:-1]
result = data.decode("utf-8")

return result


def locate_smbios_data():
# Define constants
FIRMWARE_TABLE_ID = 0x52534D42 # 'RSMB' ascii signature for smbios table
Expand Down
31 changes: 31 additions & 0 deletions SetupDataPkg/Tools/CommonUtility.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# Import Modules
#
import string
import hashlib
import xml.etree.ElementTree as ET


def print_bytes(data, indent=0, offset=0, show_ascii=False):
Expand Down Expand Up @@ -97,3 +99,32 @@ def array_str_to_value(val_str):
each = each.strip()
value = (value << 8) | int(each, 0)
return value


def get_xml_full_hash(xml_path):
# Parse the XML file
tree = ET.parse(xml_path)
root = tree.getroot()

# List to store all tags, attributes, and values
hash_content = []

def traverse(element):
# Add tag name
hash_content.append(element.tag)

# Add attribute names and values
for key, value in sorted(element.attrib.items()):
hash_content.append(f"{key}={value}")

# Recursively process each child element
for child in element:
traverse(child)

# Start traversing the root node
traverse(root)

# Combine all items in hash_content list into a single string
combined_string = ''.join(hash_content)

return hashlib.md5(combined_string.encode('utf-8')).hexdigest()
26 changes: 23 additions & 3 deletions SetupDataPkg/Tools/ConfigEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
bytes_to_bracket_str,
value_to_bytes,
array_str_to_value,
get_xml_full_hash
)


Expand Down Expand Up @@ -393,6 +394,8 @@ def __init__(self, master=None):
# self.page_cfg_map[page_id] = cfg_data_idx
self.page_cfg_map = {}

self.bios_schema_xml_hash = None

# Check if current directory contains a file with a .yaml extension
# if not default self.last_dir to a Platform directory where it is
# easier to locate *BoardPkg\CfgData\*Def.yaml files
Expand Down Expand Up @@ -559,6 +562,8 @@ def __init__(self, master=None):
Manufacturing_enabled = (char_ext2_data & (0x1 << 6)) >> 6
print(f"Manufacturing : {Manufacturing_enabled:02X}")

self.bios_schema_xml_hash = BoardMiscInfo.get_schema_xml_hash_from_bios()

# get mfci policy
mfci_policy_result = BoardMiscInfo.get_mfci_policy()
self.canvas = tkinter.Canvas(master, width=240, height=50, bg=master['bg'], highlightthickness=0)
Expand Down Expand Up @@ -968,6 +973,15 @@ def load_cfg_file(self, path, file_id, clear_config):

self.config_xml_path = path
self.output_current_status(f"{path} file is loaded")
# load xml file and get the hash value of all xml nodes
config_xml_hash = get_xml_full_hash(self.config_xml_path)

# Compare the xml hash and the hash claimed in FW.
if self.bios_schema_xml_hash is not None and config_xml_hash != self.bios_schema_xml_hash:
self.output_current_status("WARNING: Config xml file hash mismatches with system FW", color="red")
self.output_current_status(f"FW ConfigXml Hash = {self.bios_schema_xml_hash}", color="red")
self.output_current_status(f"{self.config_xml_path} Hash = {config_xml_hash}", color="red")

return 0

def load_from_ml_and_clear(self):
Expand Down Expand Up @@ -1399,9 +1413,15 @@ def update_config_data_on_page(self):
self.right_grid, self.update_config_data_from_widget
)

def output_current_status(self, output_log):
self.status.insert(tkinter.END, output_log + "\n")
self.status.see(tkinter.END)
def output_current_status(self, output_log, color="black"):
unique_tag = f"color_{color}"
# Configure the color tag only if it hasn't been set before
if unique_tag not in self.status.tag_names():
self.status.tag_config(unique_tag, foreground=color)

# Insert the log with the unique color tag
self.status.insert(tkinter.END, output_log + "\n", (unique_tag,))
self.status.see(tkinter.END) # Auto-scroll to the bottom


if __name__ == "__main__":
Expand Down
2 changes: 2 additions & 0 deletions SetupDataPkg/Tools/KnobService.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import argparse
import re
import VariableList
from CommonUtility import get_xml_full_hash


# Converts a type name from standard C to UEFI
Expand Down Expand Up @@ -632,6 +633,7 @@ def generate_cached_implementation(schema, header_path, efi_type=False, no_chang
out.write("// Generated Header" + get_line_ending(efi_type))
out.write("// Script: {}".format(sys.argv[0]) + get_line_ending(efi_type))
out.write("// Schema: {}".format(schema.path) + get_line_ending(efi_type))
out.write('CHAR8 *gSchemaXmlHash = "' + get_xml_full_hash(schema.path) + '";' + get_line_ending(efi_type))
out.write("" + get_line_ending(efi_type))

out.write("typedef struct {" + get_line_ending(efi_type))
Expand Down

0 comments on commit 8b91b57

Please sign in to comment.