tools/manifest_tools/manifest_parser.py (711 lines of code) (raw):

""" Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. """ from __future__ import print_function from __future__ import unicode_literals import re import binascii import traceback import xml.etree.ElementTree as et import manifest_types XML_ATTESTATION_FAIL_RETRY_ATTRIB = "attestation_fail_retry" XML_ATTESTATION_PROTOCOL_ATTRIB = "attestation_protocol" XML_ATTESTATION_SUCCESS_RETRY_ATTRIB = "attestation_success_retry" XML_ATTESTATION_RSP_NOT_READY_MAX_RETRY_ATTRIB = "attestation_rsp_not_ready_max_retry" XML_ATTESTATION_RSP_NOT_READY_MAX_DURATION_ATTRIB = "attestation_rsp_not_ready_max_duration" XML_CONNECTION_ATTRIB = "connection" XML_COUNT_ATTRIB = "count" XML_DISCOVERY_FAIL_RETRY_ATTRIB = "discovery_fail_retry" XML_EMPTY_ATTRIB = "empty" XML_ENTRY_ID_ATTRIB = "entry_id" XML_ID_ATTRIB = "id" XML_INDEX_ATTRIB = "index" XML_MEASUREMENT_ID_ATTRIB = "measurement_id" XML_LEVEL_ATTRIB = "level" XML_MCTP_BRIDGE_ADDITIONAL_TIMEOUT_ATTRIB= "mctp_bridge_additional_timeout" XML_MCTP_BRIDGE_GET_TABLE_WAIT_ATTRIB = "mctp_bridge_get_table_wait" XML_MCTP_CTRL_TIMEOUT_ATTRIB = "mctp_ctrl_timeout" XML_MEASUREMENT_HASH_TYPE_ATTRIB = "measurement_hash_type" XML_PLATFORM_ATTRIB = "platform" XML_PMR_ID_ATTRIB = "pmr_id" XML_PORT_ATTRIB = "port" XML_SKU_ATTRIB = "sku" XML_SLOT_NUM_ATTRIB = "slot_num" XML_TRANSCRIPT_HASH_TYPE_ATTRIB = "transcript_hash_type" XML_TYPE_ATTRIB = "type" XML_VERSION_ATTRIB = "version" XML_ACTIVE_TAG = "Active" XML_ADDRESS_TAG = "Address" XML_ALLOWABLE_CFM_TAG = "AllowableCFM" XML_ALLOWABLE_DATA_TAG = "AllowableData" XML_ALLOWABLE_PCD_TAG = "AllowablePCD" XML_ALLOWABLE_PFM_TAG = "AllowablePFM" XML_BITMASK_TAG = "Bitmask" XML_BRIDGE_ADDRESS_TAG = "BridgeAddress" XML_BRIDGE_EID_TAG = "BridgeEID" XML_BUS_TAG = "Bus" XML_CHANNEL_TAG = "Channel" XML_CHECK_TAG = "Check" XML_COMPONENT_TAG = "Component" XML_CFM_COMPONENT_TAG = "CFMComponent" XML_COMPONENTS_TAG = "Components" XML_DATA_TAG = "Data" XML_DEVICE_ID_TAG = "DeviceID" XML_DEVICETYPE_TAG = "DeviceType" XML_DIGEST_TAG = "Digest" XML_EID_TAG = "EID" XML_END_ADDR_TAG = "EndAddr" XML_ENDIANNESS_TAG = "Endianness" XML_FAILURE_ACTION_TAG = "FailureAction" XML_FW_TAG = "Firmware" XML_FLASHMODE_TAG = "FlashMode" XML_HASH_TAG = "Hash" XML_HASH_TYPE_TAG = "HashType" XML_HOSTRESETACTION_TAG = "HostResetAction" XML_I2CMODE_TAG = "I2CMode" XML_ID_TAG = "ID" XML_INITIAL_VALUE_TAG = "InitialValue" XML_INTERFACE_TAG = "Interface" XML_MASK_TAG = "Mask" XML_MANIFEST_ID_TAG = "ManifestID" XML_MEASUREMENT_TAG = "Measurement" XML_MEASUREMENT_DATA_TAG = "MeasurementData" XML_MUX_TAG = "Mux" XML_MUXES_TAG = "Muxes" XML_OPERATION_ON_FAILURE_TAG = "OperationOnFailure" XML_PMR_TAG = "PMR" XML_PMR_DIGEST_TAG = "PMRDigest" XML_POLICY_TAG = "Policy" XML_PORT_TAG = "Port" XML_PORTS_TAG = "Ports" XML_POWER_CONTROLLER_TAG = "PowerController" XML_PB_KEY_TAG = "PublicKey" XML_PULSEINTERVAL_TAG = "PulseInterval" XML_PWRCTRL_TAG = "PwrCtrl" XML_RW_TAG = "ReadWrite" XML_REGION_TAG = "Region" XML_REGISTER_TAG = "Register" XML_RESETCTRL_TAG = "ResetCtrl" XML_ROOT_CA_DIGEST_TAG = "RootCADigest" XML_ROT_TAG = "RoT" XML_ROT_EID_TAG = "RoTEID" XML_RUNTIME_UPDATE_TAG = "RuntimeUpdate" XML_RUNTIMEVERIFICATION_TAG = "RuntimeVerification" XML_SIG_TAG = "Signature" XML_IMG_TAG = "SignedImage" XML_SPIFREQ_TAG = "SPIFreq" XML_START_ADDR_TAG = "StartAddr" XML_SUB_DEVICE_ID_TAG = "SubsystemDeviceID" XML_SUB_VENDOR_ID_TAG = "SubsystemVendorID" XML_UNUSED_BYTE_TAG = "UnusedByte" XML_WATCHDOGMONITORING_TAG = "WatchdogMonitoring" XML_VALIDATE_TAG = "ValidateOnBoot" XML_VENDOR_ID_TAG = "VendorID" XML_VERSION_TAG = "Version" XML_VERSION_ADDR_TAG = "VersionAddr" PCD_ROT_TYPE_PA_ROT = "PA-RoT" PCD_ROT_TYPE_AC_ROT = "AC-RoT" PCD_FLASH_MODE_SINGLE = "Single" PCD_FLASH_MODE_DUAL = "Dual" PCD_FLASH_MODE_SINGLE_FILTERED_BYPASS = "SingleFilteredBypass" PCD_FLASH_MODE_DUAL_FILTERED_BYPASS = "DualFilteredBypass" PCD_HOST_RESET_ACTION_NONE = "None" PCD_HOST_RESET_ACTION_RESET_FLASH = "ResetFlash" PCD_RESET_CTRL_NOTIFY = "Notify" PCD_RESET_CTRL_RESET = "Reset" PCD_RESET_CTRL_PULSE = "Pulse" PCD_POLICY_ACTIVE = "Active" PCD_POLICY_PASSIVE = "Passive" PCD_INTERFACE_TYPE_I2C = "I2C" PCD_INTERFACE_I2C_MODE_MM = "MultiMaster" PCD_INTERFACE_I2C_MODE_MS = "MasterSlave" PCD_COMPONENT_CONNECTION_DIRECT = "Direct" PCD_COMPONENT_CONNECTION_MCTP_BRIDGE = "MCTPBridge" PCD_ENABLED = "Enabled" PCD_DISABLED = "Disabled" CFM_CHECK_EQUAL = "Equal" CFM_CHECK_NOT_EQUAL = "NotEqual" CFM_CHECK_LESS_THAN = "LessThan" CFM_CHECK_LESS_OR_EQUAL = "LessOrEqual" CFM_CHECK_GREATER_THAN = "GreaterThan" CFM_CHECK_GREATER_OR_EQUAL = "GreaterOrEqual" CFM_ENDIANNESS_BIG_ENDIAN = "BigEndian" CFM_ENDIANNESS_LITTLE_ENDIAN = "LittleEndian" def xml_extract_attrib (root, attrib_name, string, xml_file, required=True): """ Fetch attribute from an XML tag. :param root: XML tag to utilize :param attrib_name: Attribute to fetch :param string: A boolean indicating whether attribute value is expected to be a string :param xml_file: Filename of XML :param key: A boolean indicating whether attribute is required for a valid manifest XML :return Attribute value """ attrib = root.attrib.get (attrib_name) if not attrib: if required: raise KeyError ("Missing {0} attribute in manifest {1}".format (attrib_name, xml_file)) return None if string: attrib.encode ("utf8") return attrib.strip () def xml_find_single_tag (root, tag_name, xml_file, required=True): """ Fetch an XML tag from XML. :param root: XML to utilize :param tag_name: Name of tag to fetch :param key: A boolean indicating whether tag is required for a valid manifest XML :param xml_file: Filename of XML :return Tag if found """ tag = root.findall (tag_name) if not tag: if required: raise KeyError ("Missing {0} tag in manifest {1}".format (tag_name, xml_file)) return None elif len (tag) > 1: raise ValueError ("Too many {0} tags in manifest {1}".format (tag_name, xml_file)) return tag[0] def xml_extract_single_value (root, requests, xml_file): """ Fetch element values from XML tag. :param root: XML tag to utilize :param requests: A list of XML elements to get values of :param xml_file: Filename of XML :return Dictionary of elements and their values """ result = {} for name, tag_name in requests.items (): tag = root.findall (tag_name) if not tag: raise KeyError ("Missing {0} tag in manifest {1}".format (tag_name, xml_file)) elif len (tag) > 1: raise ValueError ("Too many {0} tags in manifest {1}".format (tag_name, xml_file)) result.update ({name:tag[0].text.strip ()}) return result def process_pfm (root, xml_file): """ Process PFM XML and generate list of element and attribute values. :param root: XML to utilize :param xml_file: Name of XML file to process :return List of PFM values, manifest version, a boolean False since PFM XMLs are never empty """ def process_region (root, version_id, xml_file): region = {} addr = xml_find_single_tag (root, XML_START_ADDR_TAG, xml_file) region["start"] = addr.text.strip () addr = xml_find_single_tag (root, XML_END_ADDR_TAG, xml_file) region["end"] = addr.text.strip () return region xml = {} pfm_version = manifest_types.VERSION_1 result = xml_extract_attrib (root, XML_VERSION_ATTRIB, True, xml_file) xml.update ({"version_id":result}) result = xml_extract_attrib (root, XML_PLATFORM_ATTRIB, True, xml_file) xml.update ({"platform_id":result}) firmware_type = xml_extract_attrib (root, XML_TYPE_ATTRIB, True, xml_file, False) if firmware_type is not None: pfm_version = manifest_types.VERSION_2 xml["fw_type"] = firmware_type runtime_update = xml_find_single_tag (root, XML_RUNTIME_UPDATE_TAG, xml_file) xml["runtime_update"] = runtime_update.text.strip ().lower () version_addr = xml_find_single_tag (root, XML_VERSION_ADDR_TAG, xml_file) xml["version_addr"] = version_addr.text.strip () unused_byte = xml_find_single_tag (root, XML_UNUSED_BYTE_TAG, xml_file, False) if unused_byte is not None: xml["unused_byte"] = unused_byte.text.strip () else: xml["unused_byte"] = "0xff" xml["rw_regions"] = [] for rw in root.findall (XML_RW_TAG): for region in rw.findall (XML_REGION_TAG): processed_region = process_region (region, xml["version_id"], xml_file) if processed_region is None: raise ValueError ("Failed to process RW region") else: if pfm_version == manifest_types.VERSION_2: fail_operation = xml_find_single_tag (region, XML_OPERATION_ON_FAILURE_TAG, xml_file, False) if fail_operation is None or fail_operation.text.strip () == "Nothing": processed_region["operation_fail"] = "0x0" elif fail_operation.text.strip () == "Erase": processed_region["operation_fail"] = "0x2" elif fail_operation.text.strip () == "Restore": processed_region["operation_fail"] = "0x1" else: raise ValueError ( "Unknown operation on failure setting '{0}' for RW region (start: 0x{1}, end: 0x{2})".format ( fail_operation.text.strip (), processed_region["start"], processed_region["end"])) xml["rw_regions"].append (processed_region) xml["signed_imgs"] = [] for img in root.findall (XML_IMG_TAG): image = {} image["regions"] = [] if pfm_version == manifest_types.VERSION_2: img_hash = xml_find_single_tag (img, XML_HASH_TAG, xml_file) image["hash"] = binascii.a2b_hex (re.sub ("\s", "", img_hash.text.strip ())) hash_type = xml_find_single_tag (img, XML_HASH_TYPE_TAG, xml_file, False) if hash_type is None or hash_type.text.strip () == "SHA256": image["hash_type"] = "0x0" elif hash_type.text.strip () == "SHA512": image["hash_type"] = "0x2" elif hash_type.text.strip () == "SHA384": image["hash_type"] = "0x1" else: raise ValueError ("Unknown hash type '{0}' in signed image".format ( hash_type.text.strip ())) else: pbkey = xml_find_single_tag (img, XML_PB_KEY_TAG, xml_file) image["pbkey"] = pbkey.text.strip () sig = xml_find_single_tag (img, XML_SIG_TAG, xml_file) image["signature"] = binascii.a2b_hex (re.sub ("\s", "", sig.text.strip ())) for region in img.findall (XML_REGION_TAG): processed_region = process_region (region, xml["version_id"], xml_file) if processed_region is None: raise ValueError ("Failed to process signed image") image["regions"].append (processed_region) if not image["regions"]: raise ValueError ("No regions found for SignedImage, firmware: {0}".format ( xml["version_id"])) prop = xml_find_single_tag (img, XML_VALIDATE_TAG, xml_file) image["validate"] = prop.text.strip () xml["signed_imgs"].append (image) if not xml["signed_imgs"]: raise ValueError ("No signed images found for firmware: {0}".format (xml["version_id"])) return xml, pfm_version, False def process_cfm_check (root, manifest_name, component_type, xml_file): result = xml_extract_single_value (root, {"check": XML_CHECK_TAG}, xml_file) if result["check"] is None or result["check"] == CFM_CHECK_EQUAL: return 0 elif result["check"] == CFM_CHECK_NOT_EQUAL: return 1 elif result["check"] == CFM_CHECK_LESS_THAN: return 2 elif result["check"] == CFM_CHECK_LESS_OR_EQUAL: return 3 elif result["check"] == CFM_CHECK_GREATER_THAN: return 4 elif result["check"] == CFM_CHECK_GREATER_OR_EQUAL: return 5 else: raise ValueError ( "Unknown check type '{0}' in allowable {1} for component {2} in manifest {3}".format ( result["check"], manifest_name, component_type, xml_file)) def process_cfm_allowable_manifest (root, manifest_name, component_type, xml_file): output = {} output["manifest_id"] = [] for manifest_id in root.findall (XML_MANIFEST_ID_TAG): ids = [] for id in manifest_id.findall (XML_ID_TAG): ids.append (int (id.text.strip (), 16)) check = process_cfm_check (manifest_id, manifest_name, component_type, xml_file) endianness = 0 endian_tag = xml_find_single_tag (manifest_id, XML_ENDIANNESS_TAG, xml_file, False) if endian_tag is not None: endian_text = endian_tag.text.strip () if endian_text == CFM_ENDIANNESS_BIG_ENDIAN: endianness = 1 output["manifest_id"].append ({"check": check, "endianness": endianness, "ids": ids}) return output def process_cfm (root, xml_file, selection_list): """ Process CFM XML and generate list of element and attribute values. :param root: XML to utilize :param xml_file: Filename of XML :param selection_list: List of component types to include :return List of CFM values, manifest version, boolean for whether manifest is for an empty CFM """ xml = {} component_type = xml_extract_attrib (root, XML_TYPE_ATTRIB, True, xml_file) component_type = component_type.strip ('\"') if component_type not in selection_list: return None, manifest_types.VERSION_2, False xml[component_type] = {} component = xml[component_type] component["unique"] = -1 for element in root: if element.tag == "Measurement": component["unique"] = 0 break if element.tag == "MeasurementData": component["unique"] = 1 break result = xml_extract_attrib (root, XML_ATTESTATION_PROTOCOL_ATTRIB, True, xml_file) if result.lower () == "cerberus": component["attestation_protocol"] = 0 elif result.lower () == "spdm": component["attestation_protocol"] = 1 else: raise ValueError ("Component {0} has unknown attestation protocol '{1}' in {2}".format ( component_type, result, xml_file)) component["slot_num"] = int (xml_extract_attrib (root, XML_SLOT_NUM_ATTRIB, False, xml_file)) result = xml_extract_attrib (root, XML_TRANSCRIPT_HASH_TYPE_ATTRIB, True, xml_file) if result == "SHA256": component["transcript_hash_type"] = 0 elif result == "SHA384": component["transcript_hash_type"] = 1 elif result == "SHA512": component["transcript_hash_type"] = 2 else: raise ValueError ("Component {0} has unknown transcript hash type '{1}' in {2}".format ( component_type, result, xml_file)) result = xml_extract_attrib (root, XML_MEASUREMENT_HASH_TYPE_ATTRIB, True, xml_file) if result == "SHA256": component["measurement_hash_type"] = 0 elif result == "SHA384": component["measurement_hash_type"] = 1 elif result == "SHA512": component["measurement_hash_type"] = 2 else: raise ValueError ("Component {0} has unknown measurement hash type '{1}' in {2}".format ( component_type, result, xml_file)) for root_ca_digests in root.findall (XML_ROOT_CA_DIGEST_TAG): if "root_ca_digests" in component: raise ValueError ("Component {0} has multiple root CA digest entries in {1}".format ( component_type, xml_file)) component["root_ca_digests"] = {} component["root_ca_digests"]["allowable_digests"] = [] for allowable_digest in root_ca_digests.findall (XML_DIGEST_TAG): component["root_ca_digests"]["allowable_digests"].append ( binascii.a2b_hex (re.sub ("\s", "", allowable_digest.text.strip ()))) for pmr in root.findall (XML_PMR_TAG): if "pmr" not in component: component["pmr"] = {} pmr_id = int (xml_extract_attrib (pmr, XML_PMR_ID_ATTRIB, False, xml_file)) if pmr_id in component["pmr"]: raise ValueError ( "Too many PMR elements for PMR ID {0} for component {1} in manifest {2}".format ( pmr_id, component_type, xml_file)) component["pmr"][pmr_id] = xml_extract_single_value (pmr, {"initial_value": XML_INITIAL_VALUE_TAG}, xml_file) component["pmr"][pmr_id]["initial_value"] = binascii.a2b_hex ( re.sub ("\s", "", component["pmr"][pmr_id]["initial_value"])) for pmr_digest in root.findall (XML_PMR_DIGEST_TAG): if "pmr_digests" not in component: component["pmr_digests"] = {} pmr_id = int (xml_extract_attrib (pmr_digest, XML_PMR_ID_ATTRIB, False, xml_file)) if pmr_id in component["pmr_digests"]: raise ValueError ( "Too many rules for PMR ID {0} digests for component {1} in manifest {2}".format ( pmr_id, component_type, xml_file)) component["pmr_digests"][pmr_id] = {} component["pmr_digests"][pmr_id]["allowable_digests"] = [] for allowable_digest in pmr_digest.findall (XML_DIGEST_TAG): component["pmr_digests"][pmr_id]["allowable_digests"].append ( binascii.a2b_hex (re.sub ("\s", "", allowable_digest.text.strip ()))) for measurement in root.findall (XML_MEASUREMENT_TAG): if "measurements" not in component: component["measurements"] = {} pmr_id = int (xml_extract_attrib (measurement, XML_PMR_ID_ATTRIB, False, xml_file)) measurement_id = int (xml_extract_attrib (measurement, XML_MEASUREMENT_ID_ATTRIB, False, xml_file)) if pmr_id in component["measurements"]: if measurement_id in component["measurements"][pmr_id]: raise ValueError ( "Too many rules for PMR ID {0} measurement ID {1} for component {2} in manifest {3}".format ( pmr_id, measurement_id, component_type, xml_file)) else: component["measurements"][pmr_id] = {} component["measurements"][pmr_id][measurement_id] = {} component["measurements"][pmr_id][measurement_id]["allowable_digests"] = [] for allowable_digest in measurement.findall (XML_DIGEST_TAG): component["measurements"][pmr_id][measurement_id]["allowable_digests"].append ( binascii.a2b_hex (re.sub ("\s", "", allowable_digest.text.strip ()))) for measurement_data in root.findall (XML_MEASUREMENT_DATA_TAG): if "measurement_data" not in component: component["measurement_data"] = {} pmr_id = int (xml_extract_attrib (measurement_data, XML_PMR_ID_ATTRIB, False, xml_file)) measurement_id = int (xml_extract_attrib (measurement_data, XML_MEASUREMENT_ID_ATTRIB, False, xml_file)) if pmr_id in component["measurement_data"]: if measurement_id in component["measurement_data"][pmr_id]: raise ValueError ( "Too many rules for PMR ID {0} measurement data ID {1} for component {2} in manifest {3}".format ( pmr_id, measurement_id, component_type, xml_file)) else: component["measurement_data"][pmr_id] = {} component["measurement_data"][pmr_id][measurement_id] = {} component["measurement_data"][pmr_id][measurement_id]["allowable_data"] = [] for allowable_data in measurement_data.findall (XML_ALLOWABLE_DATA_TAG): data_dict = {} data_dict["data"] = [] bitmask_tag = xml_find_single_tag (allowable_data, XML_BITMASK_TAG, xml_file, False) if bitmask_tag is not None: bitmask_text = binascii.a2b_hex (re.sub ("\s", "", bitmask_tag.text.strip ())) data_dict["bitmask"] = bitmask_text data_dict["bitmask_length"] = len (bitmask_text) check = process_cfm_check (allowable_data, "data", component_type, xml_file) data_dict["check"] = check endianness_tag = xml_extract_single_value (allowable_data, {"endianness": XML_ENDIANNESS_TAG}, xml_file) if endianness_tag["endianness"] == CFM_ENDIANNESS_LITTLE_ENDIAN: data_dict["endianness"] = 0 elif endianness_tag["endianness"] == CFM_ENDIANNESS_BIG_ENDIAN: data_dict["endianness"] = 1 else: raise ValueError ( "Unknown endianness '{0}' in allowable data for component {1} in manifest {2}".format ( endianness_tag["endianness"], component_type, xml_file)) for data in allowable_data.findall (XML_DATA_TAG): data_text = data.text.strip () if data_text[0] == '"' and data_text[-1] == '"': data_text = binascii.hexlify (data_text.strip ('\"').encode ()) else: data_text = re.sub ("\s", "", data_text) data_text = binascii.a2b_hex (data_text) if "data_len" in component["measurement_data"][pmr_id][measurement_id]: data_len = component["measurement_data"][pmr_id][measurement_id]["data_len"] if len (data_text) != data_len: raise ValueError ( "Data {0} has different length than other data for component {1} in manifest {2}: {3} vs {4}".format ( data_text, component_type, xml_file, len (data_text), data_len)) else: component["measurement_data"][pmr_id][measurement_id]["data_len"] = \ len (data_text) if "bitmask" in data_dict and len (data_text) > len (data_dict["bitmask"]): raise ValueError ( "Data {0} length should be no greater than bitmask {1} for component {2} in manifest {3}".format ( data_text, data_dict["bitmask"], component_type, xml_file)) data_dict["data"].append (data_text) component["measurement_data"][pmr_id][measurement_id]["allowable_data"].append ( data_dict) for allowable_pfm in root.findall (XML_ALLOWABLE_PFM_TAG): if "allowable_pfm" not in component: component["allowable_pfm"] = {} port_id = int (xml_extract_attrib (allowable_pfm, XML_PORT_ATTRIB, False, xml_file)) platform = xml_extract_attrib (allowable_pfm, XML_PLATFORM_ATTRIB, True, xml_file) if port_id in component["allowable_pfm"]: raise ValueError ("Too many rules for port {0} PFMs for component {1} in manifest {2}".format ( port_id, component_type, xml_file)) component["allowable_pfm"][port_id] = process_cfm_allowable_manifest (allowable_pfm, "PFM", component_type, xml_file) component["allowable_pfm"][port_id]["platform"] = platform for allowable_cfm in root.findall (XML_ALLOWABLE_CFM_TAG): if "allowable_cfm" not in component: component["allowable_cfm"] = {} index = int (xml_extract_attrib (allowable_cfm, XML_INDEX_ATTRIB, False, xml_file)) platform = xml_extract_attrib (allowable_cfm, XML_PLATFORM_ATTRIB, True, xml_file) if index in component["allowable_cfm"]: raise ValueError ("Too many rules for index {0} CFMs for component {1} in manifest {2}".format ( index, component_type, xml_file)) component["allowable_cfm"][index] = process_cfm_allowable_manifest (allowable_cfm, "CFM", component_type, xml_file) component["allowable_cfm"][index]["platform"] = platform allowable_pcd = xml_find_single_tag (root, XML_ALLOWABLE_PCD_TAG, xml_file, False) if allowable_pcd is not None: platform = xml_extract_attrib (allowable_pcd, XML_PLATFORM_ATTRIB, True, xml_file) component["allowable_pcd"] = process_cfm_allowable_manifest (allowable_pcd, "PCD", component_type, xml_file) component["allowable_pcd"]["platform"] = platform return xml, manifest_types.VERSION_2, False def process_pcd (root, xml_file): """ Process PCD XML and generate list of element and attribute values. :param root: XML to utilize :param xml_file: Filename of XML :return List of PCD values, manifest version, boolean for whether manifest is for an empty PCD """ xml = {} result = xml_extract_attrib (root, XML_SKU_ATTRIB, True, xml_file) xml.update ({"platform_id":result}) result = xml_extract_attrib (root, XML_VERSION_ATTRIB, False, xml_file) xml.update ({"version":int (result, 16)}) result = xml_extract_attrib (root, XML_EMPTY_ATTRIB, True, xml_file, False) if result and result.lower () == "true": return xml, manifest_types.VERSION_2, True rot = xml_find_single_tag (root, XML_ROT_TAG, xml_file) xml["rot"] = {} result = xml_extract_attrib (rot, XML_TYPE_ATTRIB, True, xml_file) if result == PCD_ROT_TYPE_PA_ROT: result = 0 elif result == PCD_ROT_TYPE_AC_ROT: result = 1 else: raise ValueError ("Unknown RoT type: {0}".format (result)) xml["rot"].update ({"type":result}) result = xml_extract_attrib (rot, XML_MCTP_CTRL_TIMEOUT_ATTRIB, False, xml_file) xml["rot"].update ({"mctp_ctrl_timeout":int (result)}) result = xml_extract_attrib (rot, XML_MCTP_BRIDGE_GET_TABLE_WAIT_ATTRIB, False, xml_file) xml["rot"].update ({"mctp_bridge_get_table_wait":int (result)}) ports = xml_find_single_tag (rot, XML_PORTS_TAG, xml_file, False) if ports is not None: xml["rot"]["ports"] = {} for port in ports.findall (XML_PORT_TAG): port_id = xml_extract_attrib (port, XML_ID_ATTRIB, False, xml_file) xml["rot"]["ports"][port_id] = {} result = xml_extract_single_value (port, {"spi_freq": XML_SPIFREQ_TAG, "flash_mode": XML_FLASHMODE_TAG, "reset_ctrl": XML_RESETCTRL_TAG, "policy": XML_POLICY_TAG, "pulse_interval": XML_PULSEINTERVAL_TAG, "runtime_verification": XML_RUNTIMEVERIFICATION_TAG, "watchdog_monitoring": XML_WATCHDOGMONITORING_TAG, "host_reset_action": XML_HOSTRESETACTION_TAG}, xml_file) if result["flash_mode"] == PCD_FLASH_MODE_DUAL: result["flash_mode"] = 0 elif result["flash_mode"] == PCD_FLASH_MODE_SINGLE: result["flash_mode"] = 1 elif result["flash_mode"] == PCD_FLASH_MODE_DUAL_FILTERED_BYPASS: result["flash_mode"] = 2 elif result["flash_mode"] == PCD_FLASH_MODE_SINGLE_FILTERED_BYPASS: result["flash_mode"] = 3 else: raise ValueError ("Unknown port {0} flash mode: {1}".format (port_id, result["flash_mode"])) if result["reset_ctrl"] == PCD_RESET_CTRL_NOTIFY: result["reset_ctrl"] = 0 elif result["reset_ctrl"] == PCD_RESET_CTRL_RESET: result["reset_ctrl"] = 1 elif result["reset_ctrl"] == PCD_RESET_CTRL_PULSE: result["reset_ctrl"] = 2 else: raise ValueError ("Unknown port {0} reset control: {1}".format (port_id, result["reset_ctrl"])) if result["policy"] == PCD_POLICY_PASSIVE: result["policy"] = 0 elif result["policy"] == PCD_POLICY_ACTIVE: result["policy"] = 1 else: raise ValueError ("Unknown port {0} policy: {1}".format (port_id, result["policy"])) if result["runtime_verification"] == PCD_DISABLED: result["runtime_verification"] = 0 elif result["runtime_verification"] == PCD_ENABLED: result["runtime_verification"] = 1 else: raise ValueError ("Unknown port {0} runtime verification setting: {1}".format ( port_id, result["runtime_verification"])) if result["watchdog_monitoring"] == PCD_DISABLED: result["watchdog_monitoring"] = 0 elif result["watchdog_monitoring"] == PCD_ENABLED: result["watchdog_monitoring"] = 1 else: raise ValueError ("Unknown port {0} watchdog monitoring setting: {1}".format ( port_id, result["watchdog_monitoring"])) if result["host_reset_action"] == PCD_HOST_RESET_ACTION_NONE: result["host_reset_action"] = 0 elif result["host_reset_action"] == PCD_HOST_RESET_ACTION_RESET_FLASH: result["host_reset_action"] = 1 else: raise ValueError ("Unknown port {0} host reset action: {1}".format (port_id, result["host_reset_action"])) xml["rot"]["ports"].update ({port_id:result}) interface = xml_find_single_tag (rot, XML_INTERFACE_TAG, xml_file) xml["rot"]["interface"] = {} interface_type = xml_extract_attrib (interface, XML_TYPE_ATTRIB, True, xml_file) if interface_type == PCD_INTERFACE_TYPE_I2C: interface_type = 0 else: raise ValueError ("Unknown RoT interface type: {0}".format (interface_type)) xml["rot"]["interface"].update ({"type":interface_type}) result = xml_extract_single_value (interface, {"address": XML_ADDRESS_TAG, "rot_eid": XML_ROT_EID_TAG, "bridge_eid": XML_BRIDGE_EID_TAG, "bridge_address": XML_BRIDGE_ADDRESS_TAG}, xml_file) result["address"] = int (result["address"], 16) result["rot_eid"] = int (result["rot_eid"], 16) result["bridge_eid"] = int (result["bridge_eid"], 16) result["bridge_address"] = int (result["bridge_address"], 16) xml["rot"]["interface"].update (result) power_controller = xml_find_single_tag (root, XML_POWER_CONTROLLER_TAG, xml_file, False) if power_controller is not None: xml["power_controller"] = {} interface = xml_find_single_tag (power_controller, XML_INTERFACE_TAG, xml_file) xml["power_controller"]["interface"] = {} interface_type = xml_extract_attrib (interface, XML_TYPE_ATTRIB, True, xml_file) if interface_type == PCD_INTERFACE_TYPE_I2C: interface_type = 0 else: print ("Unknown PowerController interface type: {0}".format (interface_type)) xml["power_controller"]["interface"].update ({"type":interface_type}) result = xml_extract_single_value (interface, {"bus": XML_BUS_TAG, "eid": XML_EID_TAG, "address": XML_ADDRESS_TAG, "i2c_mode": XML_I2CMODE_TAG}, xml_file) result["eid"] = int (result["eid"], 16) result["address"] = int (result["address"], 16) if result["i2c_mode"] is None: raise ValueError ("PowerController missing I2C mode") if result["i2c_mode"] == PCD_INTERFACE_I2C_MODE_MM: result["i2c_mode"] = 0 elif result["i2c_mode"] == PCD_INTERFACE_I2C_MODE_MS: result["i2c_mode"] = 1 else: raise ValueError ("Unknown PowerController interface I2C mode: {0}".format ( result["i2c_mode"])) xml["power_controller"]["interface"].update (result) muxes = xml_find_single_tag (interface, XML_MUXES_TAG, xml_file, False) if muxes is not None: xml["power_controller"]["interface"]["muxes"] = {} for mux in muxes.findall (XML_MUX_TAG): level = xml_extract_attrib (mux, XML_LEVEL_ATTRIB, False, xml_file) result = xml_extract_single_value (mux, {"address": XML_ADDRESS_TAG, "channel": XML_CHANNEL_TAG}, xml_file) result["address"] = int (result["address"], 16) xml["power_controller"]["interface"]["muxes"].update ({level:result}) components = xml_find_single_tag (root, XML_COMPONENTS_TAG, xml_file, False) if components is not None: xml["components"]= [] for component in components.findall (XML_COMPONENT_TAG): curr_component = {} result = xml_extract_attrib (component, XML_TYPE_ATTRIB, True, xml_file) curr_component.update ({"type":result}) result = xml_extract_attrib (component, XML_ATTESTATION_SUCCESS_RETRY_ATTRIB, False, xml_file) curr_component.update ({"attestation_success_retry":int (result)}) result = xml_extract_attrib (component, XML_ATTESTATION_FAIL_RETRY_ATTRIB, False, xml_file) curr_component.update ({"attestation_fail_retry":int (result)}) result = xml_extract_attrib (component, XML_ATTESTATION_RSP_NOT_READY_MAX_DURATION_ATTRIB, False, xml_file) curr_component.update ({"attestation_rsp_not_ready_max_duration":int (result)}) result = xml_extract_attrib (component, XML_ATTESTATION_RSP_NOT_READY_MAX_RETRY_ATTRIB, False, xml_file) curr_component.update ({"attestation_rsp_not_ready_max_retry":int (result)}) cnxn_type = xml_extract_attrib (component, XML_CONNECTION_ATTRIB, True, xml_file) if cnxn_type == PCD_COMPONENT_CONNECTION_DIRECT: curr_component.update ({"connection":PCD_COMPONENT_CONNECTION_DIRECT}) interface = xml_find_single_tag (component, XML_INTERFACE_TAG, xml_file) curr_component["interface"] = {} interface_type = xml_extract_attrib (interface, XML_TYPE_ATTRIB, True, xml_file) if interface_type == PCD_INTERFACE_TYPE_I2C: interface_type = 0 else: raise ValueError ("Unknown component {0} interface type: {1}".format ( curr_component["type"], interface_type)) curr_component["interface"].update ({"type":interface_type}) result = xml_extract_single_value (interface, {"bus": XML_BUS_TAG, "eid": XML_EID_TAG, "address": XML_ADDRESS_TAG, "i2c_mode": XML_I2CMODE_TAG}, xml_file) result["eid"] = int (result["eid"], 16) result["address"] = int (result["address"], 16) if result["i2c_mode"] is None: raise ValueError ("Component {0} missing I2C mode".format ( curr_component["type"])) if result["i2c_mode"] == PCD_INTERFACE_I2C_MODE_MM: result["i2c_mode"] = 0 elif result["i2c_mode"] == PCD_INTERFACE_I2C_MODE_MS: result["i2c_mode"] = 1 else: print ("Unknown component {0} interface I2C mode: {1}".format ( curr_component["type"], result["i2c_mode"])) curr_component["interface"].update (result) muxes = xml_find_single_tag (interface, XML_MUXES_TAG, xml_file, False) if muxes is not None: curr_component["interface"]["muxes"] = {} for mux in muxes.findall (XML_MUX_TAG): level = xml_extract_attrib (mux, XML_LEVEL_ATTRIB, False, xml_file) result = xml_extract_single_value (mux, {"address": XML_ADDRESS_TAG, "channel": XML_CHANNEL_TAG}, xml_file) result["address"] = int (result["address"], 16) curr_component["interface"]["muxes"].update ({level:result}) elif cnxn_type == PCD_COMPONENT_CONNECTION_MCTP_BRIDGE: curr_component.update ({"connection":PCD_COMPONENT_CONNECTION_MCTP_BRIDGE}) count = xml_extract_attrib (component, XML_COUNT_ATTRIB, False, xml_file) curr_component.update ({"count": int (count)}) result = xml_extract_attrib (component, XML_DISCOVERY_FAIL_RETRY_ATTRIB, False, xml_file) curr_component.update ({"discovery_fail_retry":int (result)}) result = xml_extract_attrib (component, XML_MCTP_BRIDGE_ADDITIONAL_TIMEOUT_ATTRIB, False, xml_file) curr_component.update ({"mctp_bridge_additional_timeout":int (result)}) result = xml_extract_single_value (component, {"eid": XML_EID_TAG, "deviceid": XML_DEVICE_ID_TAG, "vendorid": XML_VENDOR_ID_TAG, "subdeviceid": XML_SUB_DEVICE_ID_TAG, "subvendorid": XML_SUB_VENDOR_ID_TAG}, xml_file) result["eid"] = int (result["eid"], 16) result["deviceid"] = int (result["deviceid"], 16) result["vendorid"] = int (result["vendorid"], 16) result["subdeviceid"] = int (result["subdeviceid"], 16) result["subvendorid"] = int (result["subvendorid"], 16) curr_component.update (result) else: raise ValueError ("Unknown component {0} connection type: {1}".format ( curr_component["type"], cnxn_type)) result = xml_extract_single_value (component, {"policy": XML_POLICY_TAG}, xml_file) if result["policy"] == PCD_POLICY_PASSIVE: result["policy"] = 0 elif result["policy"] == PCD_POLICY_ACTIVE: result["policy"] = 1 else: raise ValueError ("Unknown component {0} policy: {1}".format ( curr_component["type"], result["policy"])) curr_component.update (result) powerctrl = xml_find_single_tag (component, XML_PWRCTRL_TAG, xml_file) curr_component["powerctrl"] = {} result = xml_extract_single_value (powerctrl, {"register": XML_REGISTER_TAG, "mask": XML_MASK_TAG}, xml_file) result["register"] = int (result["register"], 16) result["mask"] = int (result["mask"], 16) curr_component["powerctrl"].update (result) xml["components"].append (curr_component) return xml, manifest_types.VERSION_2, False def load_and_process_xml (xml_file, xml_type, selection_list=None): """ Process XML and generate list of element and attribute values. :param xml_file: XML to utilize :param xml_type: Type of manifest :param selection_list: Optional list of units to select and include in manifest :return List of manifest values, manifest version, boolean for whether XML is for an empty manifest """ root = et.parse (xml_file).getroot () if xml_type is manifest_types.PFM: return process_pfm (root, xml_file) elif xml_type is manifest_types.CFM: return process_cfm (root, xml_file, selection_list["selection"]) elif xml_type is manifest_types.PCD: return process_pcd (root, xml_file) else: raise ValueError ("Unknown XML type: {0}".format (xml_type)) def load_and_process_selection_xml (xml_file): """ Process XML and generate dictionary of platform ID and selection list. :param xml_file: XML to utilize :return selection dictionary """ result_dict = {} root = et.parse (xml_file).getroot () result = xml_extract_attrib (root, XML_SKU_ATTRIB, True, xml_file) result_dict.update ({"platform_id":result}) result_dict["selection"] = [] for component in root.findall (XML_COMPONENT_TAG): component_text = component.text.strip ().strip ("\"") result_dict["selection"].append (component_text) return result_dict