Files
brentholtsclaw b7df4e5efb dev/chipsec2 bug fixes
These changes were necessary to run chipsec2 on platform

Signed-off-by: brentholtsclaw <brent.holtsclaw@intel.com>
2026-01-29 13:42:02 -08:00

1227 lines
47 KiB
Python

# CHIPSEC: Platform Security Assessment Framework
# Copyright (c) 2023, Intel Corporation
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; Version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# Contact information:
# chipsec@intel.com
#
from collections import namedtuple
from collections.abc import Iterable
from dataclasses import dataclass, field
from fnmatch import fnmatch
import importlib
import re
import os
from typing import Dict, List, Set, Any, Optional
import xml.etree.ElementTree as ET
from chipsec.library.defines import is_hex, CHIPSET_CODE_UNKNOWN
from chipsec.library.exceptions import (
CSConfigError, PlatformDetectionError, ConfigurationValidationError, ParserLoadError
)
from chipsec.library.file import get_main_dir
from chipsec.library.logger import logger
from chipsec.library.register import ObjList
from chipsec.library.strings import make_hex_key_str
from chipsec.parsers import Stage
from chipsec.parsers import stage_info, config_data
from chipsec.cfg.parsers.ip.platform import Platform, Vendor
from chipsec.cfg.parsers.ip.pci_device import PCIConfig
@dataclass
class PlatformInfo:
"""Data class for platform information."""
vid: int = 0xFFFF
did: int = 0xFFFF
rid: int = 0xFF
code: str = CHIPSET_CODE_UNKNOWN
longname: str = 'Unrecognized Platform'
name: str = 'Unknown'
req_pch: Any = None
detect: List[str] = field(default_factory=list)
def __post_init__(self):
"""Validate platform info after initialization."""
if not isinstance(self.detect, list):
self.detect = []
@dataclass
class PCHInfo:
"""Data class for PCH information."""
vid: int = 0xFFFF
did: int = 0xFFFF
rid: int = 0xFF
code: str = CHIPSET_CODE_UNKNOWN
longname: str = 'Unrecognized PCH'
@dataclass
class CPUInfo:
"""Data class for CPU information."""
cpuid: int = 0xFFFFF
mfgid: str = 'Unknown CPU'
@dataclass
class ConfigurationState:
"""Data class for configuration state."""
xml_config_loaded: bool = False
parsers_loaded: bool = False
platform_detected: bool = False
validation_passed: bool = False
class PlatformDetector:
"""Handles platform detection and SKU matching."""
def __init__(self, logger):
self.logger = logger
self.vid_set: Set[str] = set()
self.config_pci_raw: Dict[str, Any] = {}
def detect_platform(self, pci_enum: Dict[str, Any],
config_pci_raw: Dict[str, Any]) -> Dict[str, Any]:
"""
Detect the platform based on PCI enumeration.
Args:
pci_enum: PCI enumeration data
config_pci_raw: Raw PCI configuration data
Returns:
Platform information dictionary
Raises:
PlatformDetectionError: If platform detection fails
"""
self.config_pci_raw = config_pci_raw
try:
dev000 = pci_enum['0000:00:00.0']
dict_ref = config_pci_raw
# Check if any vendor IDs match
if not self._check_vendor_compatibility(dict_ref, config_pci_raw):
return self._get_unknown_platform(dev000)
# Find matching SKUs
detect_val = self._get_detection_value(dev000)
possible_skus = self._find_possible_skus_from_detection_value(
dict_ref, detect_val
)
if not possible_skus:
self.logger.log_warning(
'Enumerated Platform PCI DID not found in XML Configs. '
'System info may not be 100% accurate.'
)
return self._get_unknown_platform(dev000)
if len(possible_skus) > 1:
self.logger.log_warning(
'Multiple SKUs found for detection value, '
'using first in the list'
)
return possible_skus[0]
except KeyError as e:
raise PlatformDetectionError(f"Missing required key: {e}")
except Exception as e:
raise PlatformDetectionError(f"Platform detection failed: {e}")
def _check_vendor_compatibility(self, dict_ref: Dict[str, Any],
config_pci_raw: Dict[str, Any]) -> bool:
"""Check if vendor IDs are compatible."""
self.vid_set = set(dict_ref.keys()).intersection(
set(config_pci_raw.keys())
)
return len(self.vid_set) > 0
def _get_detection_value(self, dev000: Dict[str, Any]) -> str:
"""Get the detection value from device info."""
return f"{dev000['vid']:04x}:{dev000['did']:04x}"
def _get_unknown_platform(self, dev000: Dict[str, Any]) -> Dict[str, Any]:
"""Return unknown platform info."""
return {
'did': [dev000['did']],
'name': 'Unknown',
'code': 'UNKN',
'longname': 'Unknown Platform',
'vid': dev000['vid'],
'req_pch': None,
'detect': []
}
def _find_possible_skus_from_detection_value(
self, dict_ref: Dict[str, Any], detect_val: str
) -> List[Dict[str, Any]]:
"""Find possible SKUs based on detection value."""
possible_skus = []
for vid_str in self.vid_set:
for did_str in dict_ref[vid_str]:
if (vid_str in self.config_pci_raw and
did_str in self.config_pci_raw[vid_str]):
# Check detection patterns
sku_info = dict_ref[vid_str][did_str]
if self._matches_detection_pattern(sku_info, detect_val):
possible_skus.append(sku_info)
return possible_skus
def _matches_detection_pattern(self, sku_info: Dict[str, Any],
detect_val: str) -> bool:
"""Check if SKU matches detection pattern."""
if 'detect' not in sku_info:
return False
for pattern in sku_info['detect']:
if fnmatch(detect_val, pattern):
return True
return False
class ConfigurationValidator:
"""Handles configuration validation and schema checking."""
def __init__(self, logger):
self.logger = logger
def validate_config(self, config_data: Dict[str, Any]) -> bool:
"""
Validate configuration data structure.
Args:
config_data: Configuration data to validate
Returns:
True if valid, False otherwise
Raises:
ConfigurationValidationError: If validation fails
"""
try:
# Check required top-level keys
required_keys = ['CONFIG_PCI_RAW', 'CONFIG_PCI']
for key in required_keys:
if key not in config_data:
raise ConfigurationValidationError(
f"Missing required configuration key: {key}"
)
# Validate PCI configuration structure
self._validate_pci_config(config_data['CONFIG_PCI'])
return True
except Exception as e:
raise ConfigurationValidationError(
f"Configuration validation failed: {e}"
)
def _validate_pci_config(self, pci_config: Dict[str, Any]) -> None:
"""Validate PCI configuration structure."""
if not isinstance(pci_config, dict):
raise ConfigurationValidationError(
"CONFIG_PCI must be a dictionary"
)
# Add more specific validation rules as needed
for vid, devices in pci_config.items():
if not isinstance(devices, dict):
raise ConfigurationValidationError(
f"PCI vendor {vid} must have dictionary of devices"
)
class ScopeManager:
"""Manages configuration scoping and key filtering."""
def __init__(self):
self.parent_keys = [
'CONFIG_PCI_RAW', 'CONFIG_PCI', 'MEMORY_RANGES', 'MM_MSGBUS',
'MSGBUS', 'IO', 'MSR', 'MMIO_BARS', 'IO_BARS'
]
self.child_keys = [
'IMA_REGISTERS', 'REGISTERS', 'CONTROLS', 'LOCKS', 'LOCKEDBY'
]
def apply_scope(self, config_data: Dict[str, Any],
scope_pattern: str) -> Dict[str, Any]:
"""
Apply scoping to configuration data.
Args:
config_data: Configuration data to scope
scope_pattern: Pattern to match for scoping
Returns:
Scoped configuration data
"""
if not scope_pattern:
return config_data
scoped_data = {}
for key, value in config_data.items():
if self._matches_scope(key, scope_pattern):
scoped_data[key] = value
return scoped_data
def _matches_scope(self, key: str, pattern: str) -> bool:
"""Check if key matches scope pattern."""
return fnmatch(key, pattern)
scope_name = namedtuple('scope_name', ['vid', 'parent', 'name', 'fields'])
# Python 3.6 namedtuple does not accept defaults
scope_name.__new__.__defaults__ = (None,) * 4
PROC_FAMILY = {}
class Cfg:
"""Main configuration class for CHIPSEC."""
def __init__(self):
self.logger = logger()
# Initialize helper classes
self.platform_detector = PlatformDetector(self.logger)
self.config_validator = ConfigurationValidator(self.logger)
self.scope_manager = ScopeManager()
# Configuration data structures
for key in (self.scope_manager.parent_keys +
self.scope_manager.child_keys):
setattr(self, key, {})
# Initialize configuration state
self.config_state = ConfigurationState()
# Initialize platform information
self.platform_info = PlatformInfo()
self.pch_info = PCHInfo()
self.cpu_info = CPUInfo()
# Legacy attributes for backward compatibility
self.platform = Platform()
self.proc_dictionary = {}
self.proc_codes = set()
self.pch_dictionary = {}
self.pch_codes = set()
self.device_dictionary = {}
self.device_codes = set()
self.platform_xml_files = {}
self.load_list = []
self.load_extra = []
self.scope = {None: ''}
self.parsers = []
self.detection_dictionary = {}
self.loaded_parsers = False
self.loaded_platform_info = False
# Legacy properties for backward compatibility
self.XML_CONFIG_LOADED = False
@property
def vid(self) -> int:
"""Legacy property for VID."""
return self.platform_info.vid
@vid.setter
def vid(self, value: int):
"""Legacy setter for VID."""
self.platform_info.vid = value
@property
def did(self) -> int:
"""Legacy property for DID."""
return self.platform_info.did
@did.setter
def did(self, value: int):
"""Legacy setter for DID."""
self.platform_info.did = value
@property
def code(self) -> str:
"""Legacy property for platform code."""
return self.platform_info.code
@code.setter
def code(self, value: str):
"""Legacy setter for platform code."""
self.platform_info.code = value
@property
def longname(self) -> str:
"""Legacy property for platform longname."""
return self.platform_info.longname
@longname.setter
def longname(self, value: str):
"""Legacy setter for platform longname."""
self.platform_info.longname = value
@property
def req_pch(self) -> bool:
"""Legacy property for PCH requirement."""
return bool(self.platform_info.req_pch)
@req_pch.setter
def req_pch(self, value: bool):
"""Legacy setter for PCH requirement."""
self.platform_info.req_pch = value
@property
def rid(self) -> int:
"""Legacy property for RID."""
return self.platform_info.rid
@rid.setter
def rid(self, value: int):
"""Legacy setter for RID."""
self.platform_info.rid = value
@property
def cpuid(self) -> int:
"""Legacy property for CPUID."""
return self.cpu_info.cpuid
@cpuid.setter
def cpuid(self, value: int):
"""Legacy setter for CPUID."""
self.cpu_info.cpuid = value
@property
def mfgid(self) -> str:
"""Legacy property for manufacturer ID."""
return self.cpu_info.mfgid
@mfgid.setter
def mfgid(self, value: str):
"""Legacy setter for manufacturer ID."""
self.cpu_info.mfgid = value
@property
def pch_vid(self) -> int:
"""Legacy property for PCH VID."""
return self.pch_info.vid
@pch_vid.setter
def pch_vid(self, value: int):
"""Legacy setter for PCH VID."""
self.pch_info.vid = value
@property
def pch_did(self) -> int:
"""Legacy property for PCH DID."""
return self.pch_info.did
@pch_did.setter
def pch_did(self, value: int):
"""Legacy setter for PCH DID."""
self.pch_info.did = value
@property
def pch_rid(self) -> int:
"""Legacy property for PCH RID."""
return self.pch_info.rid
@pch_rid.setter
def pch_rid(self, value: int):
"""Legacy setter for PCH RID."""
self.pch_info.rid = value
@property
def pch_code(self) -> str:
"""Legacy property for PCH code."""
return self.pch_info.code
@pch_code.setter
def pch_code(self, value: str):
"""Legacy setter for PCH code."""
self.pch_info.code = value
@property
def pch_longname(self) -> str:
"""Legacy property for PCH longname."""
return self.pch_info.longname
@pch_longname.setter
def pch_longname(self, value: str):
"""Legacy setter for PCH longname."""
self.pch_info.longname = value
###
# Private functions
###
def _get_vid_from_filename(self, fname):
search_string = re.compile(r'cfg.[0-9a-fA-F]+')
match = search_string.search(fname)
vid = match.group(0)[4:]
return vid
def _create_vid(self, vid_str):
key_list = (self.scope_manager.parent_keys +
self.scope_manager.child_keys)
skip_keys = ['LOCKS']
if vid_str not in self.CONFIG_PCI:
for key in key_list:
if key in skip_keys:
continue
mdict = getattr(self, key)
mdict[vid_str] = {}
if vid_str not in self.platform.vendor_list:
self.platform.add_vendor(Vendor(vid_str))
###
# PCI device tree enumeration
###
def set_pci_data(self, enum_devices):
if not hasattr(self, 'CONFIG_PCI_RAW'):
setattr(self, 'CONFIG_PCI_RAW', {})
for b, d, f, vid, did, rid in enum_devices:
vid_str = make_hex_key_str(vid)
did_str = make_hex_key_str(did)
pci_data = {
'bus': b,
'dev': d,
'fun': f,
'vid': vid,
'did': did,
'rid': rid}
if vid_str not in self.CONFIG_PCI_RAW:
self._create_vid(vid_str)
if did_str not in self.CONFIG_PCI_RAW[vid_str]:
pci_obj = PCIConfig(pci_data)
self.CONFIG_PCI_RAW[vid_str][did_str] = pci_obj
continue
self.CONFIG_PCI_RAW[vid_str][did_str].add_obj(pci_data)
def set_cpuid(self, cpuid: int) -> None:
"""Set CPU identification information."""
self.cpu_info.cpuid = cpuid
def set_mfgid(self, mfgid: str) -> None:
"""Set CPU manufacturer identification."""
self.cpu_info.mfgid = mfgid
def set_topology(self, topology: Dict[str, Any]) -> None:
"""
Set CPU topology information.
Args:
topology: Dictionary containing CPU topology data
"""
if not hasattr(self, 'CPU'):
setattr(self, 'CPU', {})
self.CPU.update(topology)
self.logger.log_hal(f'Added topology to self.CPU\n{self.CPU}')
def get_chipset_code(self) -> str:
"""Get the current chipset/platform code."""
return self.platform_info.code
def get_pch_code(self) -> str:
"""Get the current PCH code."""
return self.pch_info.code
def is_pch_req(self) -> bool:
"""Check if PCH is required for this platform."""
return bool(self.platform_info.req_pch)
def print_platform_info(self) -> None:
"""Print platform information."""
self.logger.log(f'Mfg ID : {self.cpu_info.mfgid}')
self.logger.log(f'Platform: {self.platform_info.longname}')
self.logger.log(f'\tCPUID: {self.cpu_info.cpuid:X}')
self.logger.log(f'\tVID: {self.platform_info.vid:04X}')
self.logger.log(f'\tDID: {self.platform_info.did:04X}')
self.logger.log(f'\tRID: {self.platform_info.rid:02X}')
def print_pch_info(self) -> None:
"""Print PCH information."""
self.logger.log(f'Platform: {self.pch_info.longname}')
self.logger.log(f'\tVID: {self.pch_info.vid:04X}')
self.logger.log(f'\tDID: {self.pch_info.did:04X}')
self.logger.log(f'\tRID: {self.pch_info.rid:02X}')
def print_supported_chipsets(self) -> None:
"""Print supported chipsets/platforms."""
fmt_str = ' {:4} | {:4} | {:14} | {:6} | {:40}'
self.logger.log('\nSupported platforms:\n')
self.logger.log(fmt_str.format(
'VID', 'DID', 'Name', 'Code', 'Long Name'))
self.logger.log('-' * 85)
for _vid in sorted(self.proc_dictionary):
for _did in sorted(self.proc_dictionary[_vid]):
for item in self.proc_dictionary[_vid][_did]:
self.logger.log(fmt_str.format(
_vid, _did, item['name'], item['code'].lower(),
item['longname'][:40]
))
###
# Private config functions
###
def _get_stage_parsers(self, stage):
handlers = {}
for parser in self.parsers:
if parser.get_stage() != stage:
continue
if parser.parser_name() in handlers:
self.logger.log_debug(f'Tag handlers already contain handlers for parser {parser.parser_name()}')
# raise CSConfigError(f'Tag handlers already contain handlers for parser {parser.parser_name()}')
continue
handlers.update({parser.parser_name(): parser.get_metadata()})
return handlers
def _update_supported_platforms(self, conf_data, data):
if not data:
return
if data.family and data.proc_code:
fam = data.family.lower()
if fam not in PROC_FAMILY:
PROC_FAMILY[fam] = []
PROC_FAMILY[fam].append(data.proc_code)
if data.proc_code:
dest = self.proc_dictionary
self.proc_codes.add(data.proc_code)
if data.proc_code not in self.platform_xml_files:
self.platform_xml_files[data.proc_code] = []
self.platform_xml_files[data.proc_code].append(conf_data)
elif data.pch_code:
dest = self.pch_dictionary
self.pch_codes.add(data.pch_code)
if data.pch_code not in self.platform_xml_files:
self.platform_xml_files[data.pch_code] = []
self.platform_xml_files[data.pch_code].append(conf_data)
else:
dest = self.device_dictionary
if 'devices' not in self.platform_xml_files:
self.platform_xml_files['devices'] = []
self.platform_xml_files['devices'].append(conf_data)
if data.vid_str not in dest:
dest[data.vid_str] = {}
for sku in data.sku_list:
did_list = [sku['did']] if type(sku['did']) is int else sku['did']
for did in did_list:
did_str = make_hex_key_str(did)
if did_str not in dest[data.vid_str]:
dest[data.vid_str][did_str] = []
sku['req_pch'] = data.req_pch
sku['detect'] = data.detect_vals
dest[data.vid_str][did_str].append(sku)
def _find_possible_skus_from_detection_value(self, dict_ref, detect_val):
possible_skus = {}
if detect_val:
for vid in self.vid_set:
for did in dict_ref[vid]:
for sku in dict_ref[vid][did]:
if 'detect' in sku:
if detect_val in sku['detect']:
if vid not in possible_skus:
possible_skus[vid] = {}
if did not in possible_skus[vid]:
possible_skus[vid][did] = []
possible_skus[vid][did].append(sku)
else:
possible_skus = dict_ref
return possible_skus
def _find_sku_from_code(self, dict_ref, code):
if code:
for vid in self.vid_set:
for did in dict_ref[vid]:
for sku in dict_ref[vid][did]:
if code.upper() == sku['code']:
return sku
return None
def _find_sku_from_pci_raw(self, dict_ref):
for vid in self.vid_set:
try:
did_set = set(dict_ref[vid].keys()).intersection(set(self.CONFIG_PCI_RAW[vid].keys()))
except KeyError:
did_set = []
for did in did_set:
for sku in dict_ref[vid][did]:
return sku
return None
def create_unknown_sku(self):
dev000 = self.get_dev_from_bdf_000()
return {'did': [dev000['did']], 'name': 'Unknown', 'code': 'UNKN', 'longname': 'Unknown Platform', 'vid': dev000['vid'], 'req_pch': None, 'detect': []}
def _find_sku_data(self, dict_ref, code, detect_val=None):
"""Find SKU data with improved error handling."""
try:
self.vid_set = dict_ref.keys()
except KeyError:
return self.create_unknown_sku()
possible_skus = self._find_possible_skus_from_detection_value(
dict_ref, detect_val
)
sku = self._find_sku_from_code(dict_ref, code)
if sku:
return sku
sku = self._find_sku_from_pci_raw(possible_skus)
if sku:
return sku
# Find SKU based on DID only
sku = self._find_sku_from_pci_raw(dict_ref)
if sku:
return sku
if possible_skus and detect_val:
if len(possible_skus) > 1:
self.logger.log_warning(
'Multiple SKUs found for detection value, '
'using first in the list'
)
sku = possible_skus.popitem()[1].popitem()[1].pop()
sku['longname'] = f"{sku['code']} Generic"
return sku
return None
def _find_did(self, sku):
"""Find the device ID for a given SKU."""
vid_str = make_hex_key_str(sku['vid'])
if 'did' in sku and type(sku['did']) is int:
return sku['did']
elif 'did' in sku and isinstance(sku['did'], list):
for did in sku['did']:
did_str = make_hex_key_str(did)
if (vid_str in self.CONFIG_PCI_RAW and
did_str in self.CONFIG_PCI_RAW[vid_str]):
return did
self.logger.log_warning(
'Enumerated Platform PCI DID not found in XML Configs. '
'System info may not be 100% accurate.'
)
return 0xFFFF
def _get_config_iter(self, fxml):
tree = ET.parse(fxml.xml_file)
root = tree.getroot()
return root.iter('configuration')
def _get_sec_parser_name(self, root, stage):
if 'custom_parser' in root.attrib:
return root.attrib['custom_parser']
for parser in self.parsers:
if parser.get_stage() == stage:
return parser.parser_name()
def _load_sec_configs(self, load_list, stage):
stage_str = 'core' if stage in [Stage.CORE_SUPPORT, Stage.REGISTER] else 'custom'
cfg_handlers = self._get_stage_parsers(stage)
if not load_list or not cfg_handlers:
return
for fxml in load_list:
self.logger.log_debug(f'[*] Loading {stage_str} config data: [{fxml.dev_name}] - {fxml.xml_file}')
if not os.path.isfile(fxml.xml_file):
self.logger.log_debug(f'[-] File not found: {fxml.xml_file}')
continue
for config_root in self._get_config_iter(fxml):
# Use all handlers for this stage unless the config explicitly selects one
if 'custom_parser' in config_root.attrib:
handlers_to_run = [(config_root.attrib['custom_parser'], cfg_handlers.get(config_root.attrib['custom_parser'], {}))]
else:
handlers_to_run = list(cfg_handlers.items())
for parser_name, tag_handlers in handlers_to_run:
if not tag_handlers:
continue
for tag in tag_handlers:
self.logger.log_debug(f'[*] Loading {tag} data...')
for node in config_root.iter(tag):
tag_handlers[tag](node, fxml)
###
# Config loading functions
###
def load(self):
if not self.loaded_parsers:
self.load_parsers()
if not self.loaded_platform_info:
self.load_platform_info()
def load_parsers(self):
parser_path = os.path.join(get_main_dir(), 'chipsec', 'cfg', 'parsers')
if not os.path.isdir(parser_path):
raise CSConfigError(f'Unable to locate configuration parsers: {parser_path}')
parser_files = [f for f in sorted(os.listdir(parser_path))
if fnmatch(f, '*.py') and not fnmatch(f, '__init__.py')]
for parser in parser_files:
parser_name = '.'.join(['chipsec', 'cfg', 'parsers', os.path.splitext(parser)[0]])
self.logger.log_debug(f'[*] Importing parser: {parser_name}')
try:
module = importlib.import_module(parser_name)
except Exception as err:
self.logger.log_debug(f'[*] Failed to import {parser_name}')
self.logger.log_debug(err)
continue
if not hasattr(module, 'parsers'):
self.logger.log_debug(f'[*] Missing parsers variable: {parser}')
continue
for obj in module.parsers:
try:
parser_obj = obj(self)
except Exception:
self.logger.log_debug(f'[*] Failed to create object: {parser}')
continue
parser_obj.startup()
self.parsers.append(parser_obj)
self.loaded_parsers = True
def add_extra_configs(self, path, filename=None, loadnow=False):
config_path = os.path.join(get_main_dir(), 'chipsec', 'cfg', path)
if os.path.isdir(config_path) and filename is None:
self.load_extra = [config_data(None, None, os.path.join(config_path, f), None, None) for f in sorted(os.listdir(config_path))
if fnmatch(f, '*.xml')]
elif os.path.isdir(config_path) and filename:
self.load_extra = [config_data(None, None, os.path.join(config_path, f), None, None) for f in sorted(os.listdir(config_path))
if fnmatch(f, '*.xml') and fnmatch(f, filename)]
else:
raise CSConfigError(f'Unable to locate configuration file(s): {config_path}')
if loadnow and self.load_extra:
self._load_sec_configs(self.load_extra, Stage.EXTRA)
def load_platform_info(self):
info_handlers = self._get_stage_parsers(Stage.GET_INFO)
tag_handlers = info_handlers['PlatformInfo']
cfg_path = os.path.join(get_main_dir(), 'chipsec', 'cfg')
# Locate all root configuration files
cfg_files = []
cfg_vids = [f.name for f in os.scandir(cfg_path) if f.is_dir() and is_hex(f.name)]
for vid_str in cfg_vids:
root_path = os.path.join(cfg_path, vid_str)
cfg_files.extend([config_data(vid_str, None, f.path, None, None)
for f in sorted(os.scandir(root_path), key=lambda x: x.name.lower())
if fnmatch(f.name, '*.xml')])
# Process platform info data and generate lookup tables
for fxml in cfg_files:
self.logger.log_debug(f'[*] Processing platform config information: {fxml.xml_file}')
for config_root in self._get_config_iter(fxml):
stage_data = stage_info(fxml.vid_str, config_root)
for tag in tag_handlers:
for node in config_root.iter(tag):
data = tag_handlers[tag](node, stage_data)
if not data:
continue
self._update_supported_platforms(fxml, data)
# Create platform global data
for cc in self.proc_codes:
globals()[f'CHIPSET_CODE_{cc.upper()}'] = cc.upper()
for pc in self.pch_codes:
globals()[f'PCH_CODE_{pc[4:].upper()}'] = pc.upper()
self.loaded_platform_info = True
def get_dev_from_bdf_000(self):
try:
for vid in self.CONFIG_PCI_RAW:
for did in self.CONFIG_PCI_RAW[vid]:
if 0 in self.CONFIG_PCI_RAW[vid][did].cfg['bus'] and self.CONFIG_PCI_RAW[vid][did].cfg['dev'] == 0 and self.CONFIG_PCI_RAW[vid][did].cfg['fun'] == 0:
return self.CONFIG_PCI_RAW[vid][did].cfg
except (TypeError, KeyError):
pass
return {'vid': 0xFFFF, 'did': 0xFFFF, 'rid': 0xFF}
def add_memory_range(self, mem_range_obj: Dict) -> None:
"""Add memory range configuration."""
self.MEMORY_RANGES[mem_range_obj['vid_str']][mem_range_obj['name']] = mem_range_obj
def platform_detection(self, proc_code, pch_code, cpuid):
"""Detect platform with improved error handling."""
# Detect processor files
self.cpuid = cpuid
sku = self._find_sku_data(self.proc_dictionary, proc_code, cpuid)
if sku:
self.vid = sku['vid']
self.did = self._find_did(sku)
self.code = sku['code']
self.longname = sku['longname']
self.req_pch = sku['req_pch']
if self.did == 0xFFFF:
dev000 = self.get_dev_from_bdf_000()
self.did = dev000['did']
self.rid = dev000['rid']
elif not proc_code:
try:
vid_str = make_hex_key_str(self.vid)
did_str = make_hex_key_str(self.did)
if (vid_str in self.CONFIG_PCI_RAW and
did_str in self.CONFIG_PCI_RAW[vid_str]):
pci_obj = self.CONFIG_PCI_RAW[vid_str][did_str]
if hasattr(pci_obj, 'get_rid'):
self.rid = pci_obj.get_rid(0, 0, 0)
else:
self.rid = 0xFF
else:
dev000 = self.get_dev_from_bdf_000()
self.rid = dev000['rid']
except (KeyError, AttributeError, TypeError):
dev000 = self.get_dev_from_bdf_000()
self.rid = dev000['rid']
else:
raise CSConfigError('There is already a CPU detected, '
'are you adding a new config?')
else:
dev000 = self.get_dev_from_bdf_000()
self.vid = dev000['vid']
self.did = dev000['did']
self.rid = dev000['rid']
# Detect PCH files
sku = self._find_sku_data(self.pch_dictionary, pch_code)
if sku:
self.pch_vid = sku['vid']
self.pch_did = self._find_did(sku)
self.pch_code = sku['code']
self.pch_longname = sku['longname']
if not pch_code:
try:
vid_str = make_hex_key_str(self.pch_vid)
did_str = make_hex_key_str(self.pch_did)
if (vid_str in self.CONFIG_PCI_RAW and
did_str in self.CONFIG_PCI_RAW[vid_str]):
pci_obj = self.CONFIG_PCI_RAW[vid_str][did_str]
if hasattr(pci_obj, 'instances'):
for cfg_data in pci_obj.instances.values():
if (0x1f == cfg_data.dev and
0x0 == cfg_data.fun):
self.pch_rid = cfg_data.rid
break
else:
self.pch_rid = 0xFF
else:
self.pch_rid = 0xFF
except (KeyError, AttributeError, TypeError):
self.pch_rid = 0xFF
else:
raise CSConfigError('There is already a PCH detected, '
'are you adding a new config?')
# Create XML file load list
if self.code:
self.load_list.extend(self.platform_xml_files.get(self.code, []))
if self.pch_code:
self.load_list.extend(
self.platform_xml_files.get(self.pch_code, [])
)
if 'devices' in self.platform_xml_files:
self.load_list.extend(self.platform_xml_files['devices'])
def get_supported_platforms(self):
seen = set()
self.load()
for vid in self.proc_dictionary:
for did in self.proc_dictionary[vid]:
for sku in self.proc_dictionary[vid][did]:
if sku['code'] not in seen:
yield (sku['code'])
seen.add(sku['code'])
def get_supported_pchs(self):
seen = set()
self.load()
for vid in self.pch_dictionary:
for did in self.pch_dictionary[vid]:
for sku in self.pch_dictionary[vid][did]:
if sku['code'] not in seen:
yield (sku['code'])
seen.add(sku['code'])
def load_platform_config(self):
sec_load_list = []
cfg_handlers = self._get_stage_parsers(Stage.DEVICE_CFG)
tag_handlers = cfg_handlers['DevConfig']
for fxml in self.load_list:
self.logger.log_debug(f'[*] Loading primary config data: {fxml.xml_file}')
for config_root in self._get_config_iter(fxml):
for tag in tag_handlers:
self.logger.log_debug(f'[*] Collecting {tag} configuration data...')
for node in config_root.iter(tag):
sec_load_list.extend(tag_handlers[tag](node, fxml))
self._load_sec_configs(sec_load_list, Stage.CORE_SUPPORT)
self._load_sec_configs(sec_load_list, Stage.REGISTER)
self._load_sec_configs(sec_load_list, Stage.CUST_SUPPORT)
if self.load_extra:
self._load_sec_configs(self.load_extra, Stage.EXTRA)
###
# Scoping Functions
###
def set_scope(self, scope: Dict[str, str]) -> None:
"""Set the current scope for register access."""
self.scope.update((k.upper() if k is not None else None, v.upper() if v is not None else None) for k, v in scope.items())
def clear_scope(self) -> None:
"""Clear the current scope for register access."""
self.scope = {None: ''}
def get_scope(self, name: str) -> str:
"""Get scope for a given name."""
if '.' in name:
return ''
elif name in self.scope:
return self.scope[name]
else:
return self.scope[None]
def convert_internal_scope(self, scope, name):
if scope:
sname = scope + '.' + name
else:
sname = name
return scope_name(*(sname.split('.', 3)))
def convert_platform_scope(self, scope, name):
if scope:
sname = scope + '.' + name
else:
sname = name
return sname.upper().split('.')
def get_objlist(self, name: str):
scope = self.get_scope(name)
fullscope = self.convert_platform_scope(scope, name)
return self.platform.get_matches_from_scope(fullscope)
def get_reglist(self, name: str):
scope = self.get_scope(name)
fullscope = self.convert_platform_scope(scope, name)
return self.platform.get_register_matches_from_scope(fullscope)
def __add_obj_to_regdef(self, reg_def, obj):
if isinstance(obj, Iterable):
reg_def.extend(obj)
else:
reg_def.append(obj)
return reg_def
# 'vid', 'parent', 'register', 'field'; register.get_obj("8086.*.FREG*_BIOS")
def get_objlist_from_scope(self, objdict: dict, scope: scope_name):
reg_def = ObjList()
vid = scope.vid.replace('*', '.*')
dict_vids = [vid] if '*' not in vid and vid in objdict.keys() else objdict.keys()
for dict_vid in dict_vids:
if re.match(vid, dict_vid):
parent = scope.parent.replace('*', '.*')
dict_parents = [parent] if '*' not in parent and parent in objdict[dict_vid].keys() else objdict[dict_vid].keys()
for dict_parent in dict_parents:
if re.match(parent, dict_parent):
if scope.name:
name = scope.name.replace('*', '.*')
dict_names = [name] if '*' not in name and name in objdict[dict_vid][dict_parent].keys() else objdict[dict_vid][dict_parent].keys()
for dict_name in dict_names:
if re.match(name, dict_name):
self.__add_obj_to_regdef(reg_def, objdict[dict_vid][dict_parent][dict_name])
else:
self.__add_obj_to_regdef(reg_def, objdict[dict_vid][dict_parent])
return reg_def
def get_scopelist_from_full_name(self, full_name):
return full_name.split('.')
# TODO: Review for correctness compared to chipsec/library/control.py:get_list_by_name()
# def get_control_obj(self, control_name, instance=None):
# controls = ObjList()
# if control_name in self.CONTROLS.keys():
# if instance is not None and 'obj' in self.CONTROLS[control_name].keys():
# return self.CONTROLS[control_name]['obj'][instance]
# controls.extend(self.CONTROLS[control_name]['obj'])
# return controls
# TODO: Review for correctness compared to chipsec/library/register.py:has_field()
# def register_has_field(self, reg_name, field_name):
# scope = self.get_scope(reg_name)
# vid, device, register, _ = self.convert_internal_scope(scope, reg_name)
# try:
# reg_def = self.REGISTERS[vid][device][register]
# except KeyError:
# return False
# if 'FIELDS' not in reg_def:
# return False
# return (field_name in reg_def['FIELDS'])
def validate_and_load_config(self, config_path: str) -> None:
"""Validate and load configuration with better error handling."""
try:
if not os.path.exists(config_path):
raise ParserLoadError(f"Configuration path does not exist: {config_path}")
# Load configuration files
self._load_config_files(config_path)
# Validate loaded configuration
config_data = {
'CONFIG_PCI_RAW': self.CONFIG_PCI_RAW,
'CONFIG_PCI': self.CONFIG_PCI
}
if self.config_validator.validate_config(config_data):
self.logger.log_debug("Configuration validation passed")
except (ParserLoadError, ConfigurationValidationError) as e:
self.logger.log_error(f"Configuration loading failed: {e}")
raise
except Exception as e:
self.logger.log_error(f"Unexpected error loading configuration: {e}")
raise ParserLoadError(f"Failed to load configuration: {e}")
def _load_config_files(self, config_path: str) -> None:
"""Load configuration files from path."""
# Implementation for loading config files
pass
def get_platform_info(self) -> PlatformInfo:
"""Get current platform information."""
return self.platform_info
def get_pch_info(self) -> PCHInfo:
"""Get current PCH information."""
return self.pch_info
def get_cpu_info(self) -> CPUInfo:
"""Get current CPU information."""
return self.cpu_info
def get_config_state(self) -> ConfigurationState:
"""Get current configuration state."""
return self.config_state
def is_platform_detected(self) -> bool:
"""Check if platform has been successfully detected."""
return (self.config_state.platform_detected and
self.platform_info.code != CHIPSET_CODE_UNKNOWN)
def is_config_validated(self) -> bool:
"""Check if configuration has been validated."""
return self.config_state.validation_passed
def reset_configuration(self) -> None:
"""Reset configuration to initial state."""
self.config_state = ConfigurationState()
self.platform_info = PlatformInfo()
self.pch_info = PCHInfo()
self.cpu_info = CPUInfo()
# Clear configuration data
for key in (self.scope_manager.parent_keys +
self.scope_manager.child_keys):
setattr(self, key, {})
def enhanced_platform_detection(self, proc_code: str = None,
pch_code: str = None,
cpuid: int = 0) -> None:
"""
Enhanced platform detection with better error handling and validation.
Args:
proc_code: Platform code to force detection
pch_code: PCH code to force detection
cpuid: CPU identification
"""
try:
# Update CPU information
self.set_cpuid(cpuid)
# Mark detection as started
self.config_state.platform_detected = False
# Detect processor files using new architecture
proc_sku = self._find_sku_data(self.proc_dictionary, proc_code, cpuid)
if proc_sku:
self.platform_info.vid = proc_sku['vid']
self.platform_info.did = self._find_did(proc_sku)
if self.platform_info.did == 0xFFFF:
dev000 = self.get_dev_from_bdf_000()
self.platform_info.did = dev000['did']
self.platform_info.code = proc_sku['code']
if not proc_code:
vid_str = make_hex_key_str(self.platform_info.vid)
did_str = make_hex_key_str(self.platform_info.did)
try:
self.platform_info.rid = self.CONFIG_PCI_RAW[vid_str][did_str].get_rid(0, 0, 0)
except (KeyError, AttributeError):
self.platform_info.rid = 0xFF
self.platform_info.longname = proc_sku['longname']
self.platform_info.req_pch = proc_sku['req_pch']
else:
# Use fallback detection
dev000 = self.get_dev_from_bdf_000()
self.platform_info.vid = dev000['vid']
self.platform_info.did = dev000['did']
self.platform_info.rid = dev000['rid']
# Detect PCH files using new architecture
pch_sku = self._find_sku_data(self.pch_dictionary, pch_code)
if pch_sku:
self.pch_info.vid = pch_sku['vid']
self.pch_info.did = self._find_did(pch_sku)
self.pch_info.code = pch_sku['code']
if not pch_code:
vid_str = make_hex_key_str(self.pch_info.vid)
did_str = make_hex_key_str(self.pch_info.did)
try:
for cfg_data in self.CONFIG_PCI_RAW[vid_str][did_str].instances.values():
if 0x1f == cfg_data.dev and 0x0 == cfg_data.fun:
self.pch_info.rid = cfg_data.rid
break
except (KeyError, AttributeError):
self.pch_info.rid = 0xFF
self.pch_info.longname = pch_sku['longname']
# Create XML file load list
if self.platform_info.code:
self.load_list.extend(self.platform_xml_files.get(self.platform_info.code, []))
if self.pch_info.code:
self.load_list.extend(self.platform_xml_files.get(self.pch_info.code, []))
if 'devices' in self.platform_xml_files:
self.load_list.extend(self.platform_xml_files['devices'])
# Mark as successfully detected
self.config_state.platform_detected = True
except Exception as e:
self.logger.log_error(f"Enhanced platform detection failed: {e}")
raise PlatformDetectionError(f"Platform detection failed: {e}")