From da143f80da04479a833cacaba287709ceae6f301 Mon Sep 17 00:00:00 2001 From: brentholtsclaw Date: Mon, 7 Jul 2025 10:41:55 -0700 Subject: [PATCH] Update Register objects Signed-off-by: brentholtsclaw --- chipsec/cfg/parsers/registers/controls.py | 211 +++++++++++++-- chipsec/cfg/parsers/registers/io.py | 167 ++++++++++-- chipsec/cfg/parsers/registers/iobar.py | 243 ++++++++++++++--- chipsec/cfg/parsers/registers/locks.py | 201 +++++++++++++- chipsec/cfg/parsers/registers/memory.py | 214 +++++++++++++-- chipsec/cfg/parsers/registers/mm_msgbus.py | 187 +++++++++++-- chipsec/cfg/parsers/registers/mmcfg.py | 261 ++++++++++++++---- chipsec/cfg/parsers/registers/mmio.py | 293 +++++++++++++++++---- chipsec/cfg/parsers/registers/msgbus.py | 178 +++++++++++-- chipsec/cfg/parsers/registers/msr.py | 235 ++++++++++++++--- chipsec/cfg/parsers/registers/pci.py | 159 ++++++++++- 11 files changed, 2067 insertions(+), 282 deletions(-) diff --git a/chipsec/cfg/parsers/registers/controls.py b/chipsec/cfg/parsers/registers/controls.py index 38cded43..0272f2fc 100644 --- a/chipsec/cfg/parsers/registers/controls.py +++ b/chipsec/cfg/parsers/registers/controls.py @@ -17,33 +17,200 @@ # Contact information: # chipsec@intel.com +""" +Control Register Helper configuration parser and accessor. + +This module provides CONTROLHelper class for parsing and accessing control register fields +in the CHIPSEC framework. Control helpers provide access to specific fields within registers. +""" + +from typing import Dict, Any, Optional from chipsec.parsers import BaseConfigHelper +from chipsec.library.exceptions import CSConfigError + + +class ControlHelperError(CSConfigError): + """Exception raised for control helper-specific errors.""" + pass class CONTROLHelper(BaseConfigHelper): - def __init__(self, cfg_obj, reg_obj): - super(CONTROLHelper, self).__init__(cfg_obj) - self.name = cfg_obj['name'] - self.value = None - self.desc = cfg_obj['desc'] - self.__reg = reg_obj - self.instance = self.__reg.instance - self.field = cfg_obj['field'] + """ + Control Register Helper configuration parser and accessor. - def read(self): - """Read the object""" - self.value = self.__reg.read_field(self.field) + This class handles parsing and access to specific fields within registers, + extending the base configuration helper with field-specific functionality. + + Attributes: + name (str): The name of the control + desc (str): Description of the control + value (Optional[int]): Current control field value + field (str): Name of the register field + instance (Optional[int]): Register instance identifier + + Example: + >>> control = CONTROLHelper( + ... {'name': 'LOCK_BIT', 'desc': 'Lock control bit', 'field': 'LOCK'}, + ... register_object + ... ) + >>> value = control.read() + """ + + def __init__(self, cfg_obj: Dict[str, Any], reg_obj: Any) -> None: + """ + Initialize control helper configuration. + + Args: + cfg_obj: Dictionary containing control configuration data + reg_obj: Register object that contains the field + + Raises: + ControlHelperError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.name = cfg_obj['name'] + self.value: Optional[int] = None + self.desc = cfg_obj['desc'] + self.__reg = reg_obj + self.instance = getattr(reg_obj, 'instance', None) + self.field = cfg_obj['field'] + self._validate_control_config() + except KeyError as e: + raise ControlHelperError(f"Missing required field in control configuration: {e}") from e + except Exception as e: + raise ControlHelperError(f"Failed to initialize control helper configuration: {e}") from e + + def _validate_control_config(self) -> None: + """ + Validate control helper-specific configuration requirements. + + Raises: + ControlHelperError: If configuration is invalid + """ + if not self.name: + raise ControlHelperError("Control helper configuration must have a valid name") + + if not self.field: + raise ControlHelperError("Control helper configuration must have a valid field name") + + if self.__reg is None: + raise ControlHelperError("Control helper must have a valid register object") + + # Verify that the register has the required field access methods + if not hasattr(self.__reg, 'read_field') or not hasattr(self.__reg, 'write_field'): + raise ControlHelperError("Register object must support field read/write operations") + + def get_register_name(self) -> str: + """ + Get the name of the associated register. + + Returns: + Name of the register containing this control field + """ + return getattr(self.__reg, 'name', 'Unknown') + + def get_field_name(self) -> str: + """ + Get the name of the register field. + + Returns: + Name of the register field + """ + return self.field + + def get_register_object(self) -> Any: + """ + Get the associated register object. + + Returns: + Register object containing this control field + """ + return self.__reg + + def is_field_available(self) -> bool: + """ + Check if the field is available in the register. + + Returns: + True if field is available, False otherwise + """ + try: + # Try to check if the field exists in the register + if hasattr(self.__reg, 'fields') and self.field in self.__reg.fields: + return True + # If we can't determine availability, assume it's available + return True + except Exception: + return False + + def read(self) -> int: + """ + Read the control field value. + + Returns: + Current field value + + Raises: + ControlHelperError: If read operation fails + """ + try: + if not self.is_field_available(): + raise ControlHelperError(f"Field '{self.field}' not available in register '{self.get_register_name()}'") + + self.value = self.__reg.read_field(self.field) + return self.value + except Exception as e: + raise ControlHelperError(f"Failed to read control field '{self.field}' from register '{self.get_register_name()}': {e}") from e + + def write(self, value: int) -> None: + """ + Write a value to the control field. + + Args: + value: Value to write to the field + + Raises: + ControlHelperError: If write operation fails + """ + try: + if not self.is_field_available(): + raise ControlHelperError(f"Field '{self.field}' not available in register '{self.get_register_name()}'") + + self.__reg.write_field(self.field, value) + self.value = value + except Exception as e: + raise ControlHelperError(f"Failed to write to control field '{self.field}' in register '{self.get_register_name()}': {e}") from e + + def get_current_value(self) -> Optional[int]: + """ + Get the current cached value without reading from hardware. + + Returns: + Current cached value, or None if not read yet + """ return self.value - def write(self, value): - """Write the object""" - self.__reg.write_field(self.field, value) - - def get_register_name(self): - return self.__reg.name - def __str__(self) -> str: - return f"""Name {self.name} - Register {self.__reg.name} - Field {self.field} - Value {self.value}""" + """ + String representation of control helper. + + Returns: + Formatted string with control details + """ + value_str = f"0x{self.value:X}" if self.value is not None else "Not Read" + return (f"Control: {self.name}\n" + f" Register: {self.get_register_name()}\n" + f" Field: {self.field}\n" + f" Value: {value_str}\n" + f" Description: {self.desc}") + + def __repr__(self) -> str: + """ + Detailed string representation for debugging. + + Returns: + Detailed string representation + """ + return (f"CONTROLHelper(name='{self.name}', field='{self.field}', " + f"register='{self.get_register_name()}', value={self.value})") diff --git a/chipsec/cfg/parsers/registers/io.py b/chipsec/cfg/parsers/registers/io.py index 5a7f696d..a542760c 100644 --- a/chipsec/cfg/parsers/registers/io.py +++ b/chipsec/cfg/parsers/registers/io.py @@ -17,52 +17,183 @@ # Contact information: # chipsec@intel.com +""" +I/O Register configuration parser and accessor. + +This module provides IORegisters class for parsing and accessing I/O port-based registers +in the CHIPSEC framework. I/O registers are accessed through CPU I/O port instructions. +""" + +from typing import Dict, Any from chipsec.library.register import BaseConfigRegisterHelper +from chipsec.library.exceptions import CSConfigError from chipsec.chipset import cs +class IORegisterError(CSConfigError): + """Exception raised for I/O register-specific errors.""" + pass + + class IORegisters(BaseConfigRegisterHelper): - def __init__(self, cfg_obj): - super(IORegisters, self).__init__(cfg_obj) - self.io_port = cfg_obj['port'] - self.size = cfg_obj['size'] - self.bar_size = None + """ + I/O Register configuration parser and accessor. + + This class handles parsing and access to I/O port-based registers, extending + the base register helper with I/O-specific functionality. + + Attributes: + name (str): The name of the register + desc (str): Description of the register + io_port (int): I/O port address + size (int): Size of the register in bytes + value (Optional[int]): Current register value + default (Optional[int]): Default register value + bar_size (Optional[int]): BAR size (always None for I/O registers) + + Example: + >>> io_reg = IORegisters({'name': 'PM1_STS', 'port': 0x400, 'size': 2}) + >>> value = io_reg.read() + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize I/O register configuration. + + Args: + cfg_obj: Dictionary containing I/O register configuration data + + Raises: + IORegisterError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.io_port = cfg_obj['port'] + self.size = cfg_obj['size'] + self.bar_size = None # I/O registers don't have BAR size + self._validate_io_config() + except KeyError as e: + raise IORegisterError(f"Missing required field in I/O register configuration: {e}") from e + except Exception as e: + raise IORegisterError(f"Failed to initialize I/O register configuration: {e}") from e + + def _validate_io_config(self) -> None: + """ + Validate I/O register-specific configuration requirements. + + Raises: + IORegisterError: If configuration is invalid + """ + if not self.name: + raise IORegisterError("I/O register configuration must have a valid name") + + if not isinstance(self.io_port, int) or self.io_port < 0 or self.io_port > 0xFFFF: + raise IORegisterError(f"Invalid I/O port: {self.io_port}. Must be 0-65535 range") + + if not isinstance(self.size, int) or self.size not in [1, 2, 4]: + raise IORegisterError(f"Invalid register size: {self.size}. Must be 1, 2, or 4 bytes") + + def get_port_address(self) -> int: + """ + Get the I/O port address. + + Returns: + I/O port address as integer + """ + return self.io_port + + def get_port_hex(self) -> str: + """ + Get the I/O port address as hexadecimal string. + + Returns: + I/O port address as hex string (e.g., '0x400') + """ + return f"0x{self.io_port:X}" + + def is_valid_port(self) -> bool: + """ + Check if the I/O port address is valid. + + Returns: + True if port is valid, False otherwise + """ + return isinstance(self.io_port, int) and 0 <= self.io_port <= 0xFFFF def __repr__(self) -> str: + """ + Detailed string representation for debugging. + + Returns: + Formatted string with register details including fields + """ reg_str = '' if self.value is not None: reg_val_str = f'0x{self.value:0{self.size * 2}X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) + if self.default is not None: default = f'{self.default:X}' else: default = 'Not Provided' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (I/O port 0x{self.io_port:X}) [default: {default}]' + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'(I/O port 0x{self.io_port:X}) [default: {default}]') reg_str += self._register_fields_str(True) return reg_str def __str__(self) -> str: + """ + String representation of I/O register. + + Returns: + Formatted string with register details + """ reg_str = '' if self.value is not None: reg_val_str = f'0x{self.value:0{self.size * 2}X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (I/O port 0x{self.io_port:X})' reg_str += self._register_fields_str() return reg_str - def read(self): - """Read the object""" - self.logger.log_debug(f'reading {self.name}') - _cs = cs() - self.value = _cs.hals.Io.read(self.io_port, self.size) - return self.value + def read(self) -> int: + """ + Read the I/O register value. - def write(self, value): - """Write the object""" - _cs = cs() - _cs.hals.Io.write(self.io_port, value, self.size) + Returns: + Current register value + + Raises: + IORegisterError: If read operation fails + """ + try: + self.logger.log_debug(f'reading {self.name}') + _cs = cs() + self.value = _cs.hals.Io.read(self.io_port, self.size) + return self.value + except Exception as e: + raise IORegisterError(f"Failed to read I/O register {self.name} at port 0x{self.io_port:X}: {e}") from e + + def write(self, value: int) -> None: + """ + Write a value to the I/O register. + + Args: + value: Value to write to the register + + Raises: + IORegisterError: If write operation fails + """ + try: + self.logger.log_debug(f'writing 0x{value:X} to {self.name}') + _cs = cs() + _cs.hals.Io.write(self.io_port, value, self.size) + self.value = value + except Exception as e: + raise IORegisterError(f"Failed to write to I/O register {self.name} at port 0x{self.io_port:X}: {e}") from e diff --git a/chipsec/cfg/parsers/registers/iobar.py b/chipsec/cfg/parsers/registers/iobar.py index bbe385b1..7c4bbd81 100644 --- a/chipsec/cfg/parsers/registers/iobar.py +++ b/chipsec/cfg/parsers/registers/iobar.py @@ -17,42 +17,189 @@ # Contact information: # chipsec@intel.com +""" +I/O BAR Register configuration parser and accessor. + +This module provides IOBARRegisters class for parsing and accessing I/O Base Address Register (BAR) registers +in the CHIPSEC framework. I/O BAR registers provide access to device registers through I/O port spaces. +""" + +from typing import Dict, Any, Optional, Tuple from chipsec.library.register import BaseConfigRegisterHelper +from chipsec.library.exceptions import CSConfigError from chipsec.chipset import cs +class IOBARRegisterError(CSConfigError): + """Exception raised for I/O BAR register-specific errors.""" + pass + + class IOBARRegisters(BaseConfigRegisterHelper): - def __init__(self, cfg_obj): - super(IOBARRegisters, self).__init__(cfg_obj) - self.size = cfg_obj['size'] - self.offset = cfg_obj['offset'] - self.bar = cfg_obj['bar'] - self.bar_base = None - self.bar_size = None - self.io_port = None + """ + I/O BAR Register configuration parser and accessor. + + This class handles parsing and access to I/O Base Address Register (BAR) registers, + extending the base register helper with I/O BAR-specific functionality. + + Attributes: + name (str): The name of the register + desc (str): Description of the register + size (int): Size of the register in bytes + offset (int): Offset within the I/O BAR + bar (str): BAR identifier or name + bar_base (Optional[int]): Base address of the I/O BAR + bar_size (Optional[int]): Size of the I/O BAR + io_port (Optional[int]): Effective I/O port address + value (Optional[int]): Current register value + default (Optional[int]): Default register value + + Example: + >>> iobar_reg = IOBARRegisters({ + ... 'name': 'CMD_REG', 'size': 4, 'offset': 0x04, 'bar': 'BAR0', + ... 'FIELDS': {} + ... }) + >>> value = iobar_reg.read() + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize I/O BAR register configuration. + + Args: + cfg_obj: Dictionary containing I/O BAR register configuration data + + Raises: + IOBARRegisterError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.size = cfg_obj['size'] + self.offset = cfg_obj['offset'] + self.bar = cfg_obj['bar'] + self.bar_base: Optional[int] = None + self.bar_size: Optional[int] = None + self.io_port: Optional[int] = None + self._validate_iobar_config() + except KeyError as e: + raise IOBARRegisterError(f"Missing required field in I/O BAR register configuration: {e}") from e + except Exception as e: + raise IOBARRegisterError(f"Failed to initialize I/O BAR register configuration: {e}") from e + + def _validate_iobar_config(self) -> None: + """ + Validate I/O BAR register-specific configuration requirements. + + Raises: + IOBARRegisterError: If configuration is invalid + """ + if not self.name: + raise IOBARRegisterError("I/O BAR register configuration must have a valid name") + + if not isinstance(self.size, int) or self.size not in [1, 2, 4, 8]: + raise IOBARRegisterError(f"Invalid register size: {self.size}. Must be 1, 2, 4, or 8 bytes") + + if not isinstance(self.offset, int) or self.offset < 0: + raise IOBARRegisterError(f"Invalid offset: {self.offset}. Must be a non-negative integer") + + if not self.bar: + raise IOBARRegisterError("I/O BAR register configuration must have a valid BAR identifier") + + def get_bar_info(self) -> Tuple[Optional[int], Optional[int]]: + """ + Get BAR base address and size information. + + Returns: + Tuple of (base_address, size) or (None, None) if not available + """ + return (self.bar_base, self.bar_size) + + def get_effective_port(self) -> Optional[int]: + """ + Get the effective I/O port address. + + Returns: + Effective I/O port address, or None if not computed yet + """ + return self.io_port + + def get_effective_port_hex(self) -> str: + """ + Get the effective I/O port address as hexadecimal string. + + Returns: + Effective I/O port address as hex string, or 'Unknown' if not available + """ + return f"0x{self.io_port:X}" if self.io_port is not None else "Unknown" + + def is_bar_resolved(self) -> bool: + """ + Check if the BAR has been resolved to a base address. + + Returns: + True if BAR base address is available, False otherwise + """ + return self.bar_base is not None + + def is_valid_port(self) -> bool: + """ + Check if the effective port address is valid for I/O operations. + + Returns: + True if port address is valid, False otherwise + """ + return self.io_port is not None and 0 <= self.io_port <= 0xFFFF + + def _resolve_bar_address(self) -> None: + """ + Resolve the BAR base address using CHIPSEC HAL. + + Raises: + IOBARRegisterError: If BAR resolution fails + """ + try: + _cs = cs() + (self.bar_base, self.bar_size) = _cs.hals.IOBAR.get_IO_BAR_base_address(self.bar, self.get_instance()) + self.io_port = self.bar_base + self.offset + except Exception as e: + raise IOBARRegisterError(f"Failed to resolve BAR {self.bar} for register {self.name}: {e}") from e def __repr__(self) -> str: - reg_str = '' + """ + Detailed string representation for debugging. + + Returns: + Formatted string with register details including fields + """ if self.value is not None: reg_val_str = f'0x{self.value:0{self.size * 2}X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) + instance = f'{self.instance}' if self.instance is not None else 'Fixed' + if self.default is not None: default = f'{self.default:X}' else: default = 'Not Provided' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} ({self.bar} + 0x{self.offset:X} Bus {instance}) [default: {default}]' + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.bar} + 0x{self.offset:X} Bus {instance}) [default: {default}]') reg_str += self._register_fields_str(True) return reg_str def __str__(self) -> str: - reg_str = '' + """ + String representation of I/O BAR register. + + Returns: + Formatted string with register details + """ if self.value is not None: reg_val_str = f'0x{self.value:0{self.size * 2}X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) instance = f'{self.instance}' if self.instance is not None else 'Fixed' reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} ({self.bar} + 0x{self.offset:X} Bus {instance})' @@ -60,21 +207,57 @@ class IOBARRegisters(BaseConfigRegisterHelper): reg_str += self._register_fields_str() return reg_str - def read(self): - """Read the object""" - self.logger.log_debug(f'reading {self.name}') - _cs = cs() - if self.io_port is None: - (self.bar_base, self.bar_size) = _cs.hals.IOBAR.get_IO_BAR_base_address(self.bar, self.get_instance()) - self.io_port = self.bar_base + self.offset - self.value = _cs.hals.Io.read(self.io_port, self.size) - self.logger.log_debug('done reading') - return self.value + def read(self) -> int: + """ + Read the I/O BAR register value. - def write(self, value): - """Write the object""" - _cs = cs() - if self.io_port is None: - (self.bar_base, self.bar_size) = _cs.hals.IOBAR.get_IO_BAR_base_address(self.bar, self.get_instance()) - self.io_port = self.bar_base + self.offset - _cs.hals.Io.write(self.io_port, value, self.size) + Returns: + Current register value + + Raises: + IOBARRegisterError: If read operation fails + """ + try: + self.logger.log_debug(f'reading {self.name}') + + # Resolve BAR address if not already done + if self.io_port is None: + self._resolve_bar_address() + + if not self.is_valid_port(): + raise IOBARRegisterError(f"Invalid I/O port for register {self.name}: {self.get_effective_port_hex()}") + + _cs = cs() + self.value = _cs.hals.Io.read(self.io_port, self.size) + self.logger.log_debug('done reading') + + return self.value + except Exception as e: + raise IOBARRegisterError(f"Failed to read I/O BAR register {self.name}: {e}") from e + + def write(self, value: int) -> None: + """ + Write a value to the I/O BAR register. + + Args: + value: Value to write to the register + + Raises: + IOBARRegisterError: If write operation fails + """ + try: + self.logger.log_debug(f'writing 0x{value:X} to {self.name}') + + # Resolve BAR address if not already done + if self.io_port is None: + self._resolve_bar_address() + + if not self.is_valid_port(): + raise IOBARRegisterError(f"Invalid I/O port for register {self.name}: {self.get_effective_port_hex()}") + + _cs = cs() + _cs.hals.Io.write(self.io_port, value, self.size) + self.value = value + + except Exception as e: + raise IOBARRegisterError(f"Failed to write to I/O BAR register {self.name}: {e}") from e diff --git a/chipsec/cfg/parsers/registers/locks.py b/chipsec/cfg/parsers/registers/locks.py index 91c81a54..acc16438 100644 --- a/chipsec/cfg/parsers/registers/locks.py +++ b/chipsec/cfg/parsers/registers/locks.py @@ -17,17 +17,212 @@ # Contact information: # chipsec@intel.com +""" +Lock Register Helper configuration parser and accessor. + +This module provides LOCKSHelper class for parsing and managing lock register configurations +in the CHIPSEC framework. Lock helpers provide access to lock bits and dependency management. +""" + +from typing import Optional from collections import namedtuple +from chipsec.library.exceptions import CSConfigError + + +class LockHelperError(CSConfigError): + """Exception raised for lock helper-specific errors.""" + pass class LOCKSHelper(namedtuple('LocksHelper', 'register field attributes lock_value dependency dependency_value')): + """ + Lock Register Helper configuration parser and accessor. + + This class handles parsing and access to lock register configurations, providing + functionality for lock bit management and dependency tracking. + + Attributes: + register (str): Name of the register containing the lock + field (str): Name of the field within the register + attributes (str): Access attributes for the lock + lock_value (Optional[int]): Value that indicates locked state + dependency (Optional[str]): Name of dependency register/field + dependency_value (Optional[int]): Value of dependency that enables this lock + + Example: + >>> lock = LOCKSHelper( + ... register='CONTROL_REG', + ... field='LOCK_BIT', + ... attributes='RW1S', + ... lock_value=1, + ... dependency=None, + ... dependency_value=None + ... ) + >>> print(lock.has_lock_value()) + True + """ + __slots__ = () - def has_lock_value(self): + def has_lock_value(self) -> bool: + """ + Check if this lock has a defined lock value. + + Returns: + True if lock_value is defined, False otherwise + """ return self.lock_value is not None - def is_access_type(self, attributes): + def is_access_type(self, attributes: str) -> bool: + """ + Check if this lock matches the specified access attributes. + + Args: + attributes: Access attributes to check against + + Returns: + True if attributes match, False otherwise + """ return self.attributes == attributes + def has_dependency(self) -> bool: + """ + Check if this lock has a dependency. + + Returns: + True if dependency is defined, False otherwise + """ + return self.dependency is not None + + def has_dependency_value(self) -> bool: + """ + Check if this lock has a dependency value defined. + + Returns: + True if dependency_value is defined, False otherwise + """ + return self.dependency_value is not None + + def is_read_only(self) -> bool: + """ + Check if this lock is read-only. + + Returns: + True if attributes indicate read-only access + """ + return self.attributes in ['RO', 'ROS'] + + def is_write_once(self) -> bool: + """ + Check if this lock is write-once (write 1 to set). + + Returns: + True if attributes indicate write-once behavior + """ + return self.attributes in ['RW1S', 'WO1S'] + + def is_clearable(self) -> bool: + """ + Check if this lock can be cleared. + + Returns: + True if attributes allow clearing the lock + """ + return self.attributes in ['RW', 'RW1C', 'WO1C'] + + def get_lock_info(self) -> dict: + """ + Get comprehensive lock information. + + Returns: + Dictionary containing lock configuration details + """ + return { + 'register': self.register, + 'field': self.field, + 'attributes': self.attributes, + 'lock_value': self.lock_value, + 'dependency': self.dependency, + 'dependency_value': self.dependency_value, + 'has_lock_value': self.has_lock_value(), + 'has_dependency': self.has_dependency(), + 'is_read_only': self.is_read_only(), + 'is_write_once': self.is_write_once(), + 'is_clearable': self.is_clearable() + } + def __str__(self) -> str: - return super().__str__() + """ + String representation of lock helper. + + Returns: + Formatted string with lock details + """ + parts = [f"Lock: {self.register}.{self.field}"] + parts.append(f"Attributes: {self.attributes}") + + if self.has_lock_value(): + parts.append(f"Lock Value: 0x{self.lock_value:X}") + + if self.has_dependency(): + dep_str = f"Dependency: {self.dependency}" + if self.has_dependency_value(): + dep_str += f" = 0x{self.dependency_value:X}" + parts.append(dep_str) + + return f"LOCKSHelper({', '.join(parts)})" + + def __repr__(self) -> str: + """ + Detailed string representation for debugging. + + Returns: + Detailed string representation + """ + return (f"LOCKSHelper(register='{self.register}', field='{self.field}', " + f"attributes='{self.attributes}', lock_value={self.lock_value}, " + f"dependency={self.dependency}, dependency_value={self.dependency_value})") + + +def create_lock_helper(register: str, field: str, attributes: str, + lock_value: Optional[int] = None, + dependency: Optional[str] = None, + dependency_value: Optional[int] = None) -> LOCKSHelper: + """ + Create a LOCKSHelper instance with validation. + + Args: + register: Name of the register containing the lock + field: Name of the field within the register + attributes: Access attributes for the lock + lock_value: Value that indicates locked state + dependency: Name of dependency register/field + dependency_value: Value of dependency that enables this lock + + Returns: + LOCKSHelper instance + + Raises: + LockHelperError: If configuration is invalid + """ + if not register: + raise LockHelperError("Register name cannot be empty") + + if not field: + raise LockHelperError("Field name cannot be empty") + + if not attributes: + raise LockHelperError("Attributes cannot be empty") + + valid_attributes = ['RO', 'RW', 'ROS', 'RW1S', 'RW1C', 'WO1S', 'WO1C'] + if attributes not in valid_attributes: + raise LockHelperError(f"Invalid attributes '{attributes}'. Must be one of: {', '.join(valid_attributes)}") + + return LOCKSHelper( + register=register, + field=field, + attributes=attributes, + lock_value=lock_value, + dependency=dependency, + dependency_value=dependency_value + ) diff --git a/chipsec/cfg/parsers/registers/memory.py b/chipsec/cfg/parsers/registers/memory.py index c42004fd..8ab165fc 100644 --- a/chipsec/cfg/parsers/registers/memory.py +++ b/chipsec/cfg/parsers/registers/memory.py @@ -17,62 +17,222 @@ # Contact information: # chipsec@intel.com +""" +Memory Register configuration parser and accessor. + +This module provides MEMORYRegisters class for parsing and accessing memory-mapped registers +in the CHIPSEC framework. Memory registers can be accessed through DRAM or MMIO methods. +""" + +from typing import Dict, Any from chipsec.library.exceptions import CSConfigError from chipsec.library.register import BaseConfigRegisterHelper from chipsec.chipset import cs +class MemoryRegisterError(CSConfigError): + """Exception raised for memory register-specific errors.""" + pass + + class MEMORYRegisters(BaseConfigRegisterHelper): - def __init__(self, cfg_obj): - super(MEMORYRegisters, self).__init__(cfg_obj) - self.offset = cfg_obj['offset'] - self.range = cfg_obj['range'] - self.size = cfg_obj['size'] - self.address = cfg_obj['address'] - self.limit = cfg_obj['limit'] - self.access = cfg_obj['access'] + """ + Memory Register configuration parser and accessor. + + This class handles parsing and access to memory-mapped registers, extending + the base register helper with memory-specific functionality including both + DRAM and MMIO access methods. + + Attributes: + name (str): The name of the register + desc (str): Description of the register + offset (int): Offset within the memory range + range (int): Memory range identifier + size (int): Size of the register in bytes + address (int): Base memory address + limit (int): Memory limit + access (str): Access method ('dram' or 'mmio') + value (int): Current register value + default (int): Default register value + + Example: + >>> mem_reg = MEMORYRegisters({ + ... 'name': 'TOLUD', 'address': 0xFED00000, 'offset': 0x100, + ... 'size': 4, 'access': 'mmio' + ... }) + >>> value = mem_reg.read() + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize memory register configuration. + + Args: + cfg_obj: Dictionary containing memory register configuration data + + Raises: + MemoryRegisterError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.offset = cfg_obj['offset'] + self.range = cfg_obj['range'] + self.size = cfg_obj['size'] + self.address = cfg_obj['address'] + self.limit = cfg_obj['limit'] + self.access = cfg_obj['access'] + self._validate_memory_config() + except KeyError as e: + raise MemoryRegisterError(f"Missing required field in memory register configuration: {e}") from e + except Exception as e: + raise MemoryRegisterError(f"Failed to initialize memory register configuration: {e}") from e + + def _validate_memory_config(self) -> None: + """ + Validate memory register-specific configuration requirements. + + Raises: + MemoryRegisterError: If configuration is invalid + """ + if not self.name: + raise MemoryRegisterError("Memory register configuration must have a valid name") + + if not isinstance(self.address, int) or self.address < 0: + raise MemoryRegisterError(f"Invalid address: {self.address}. Must be a positive integer") + + if not isinstance(self.offset, int) or self.offset < 0: + raise MemoryRegisterError(f"Invalid offset: {self.offset}. Must be a non-negative integer") + + if not isinstance(self.size, int) or self.size not in [1, 2, 4, 8]: + raise MemoryRegisterError(f"Invalid register size: {self.size}. Must be 1, 2, 4, or 8 bytes") + + if self.access not in ['dram', 'mmio']: + raise MemoryRegisterError(f"Invalid access method: {self.access}. Must be 'dram' or 'mmio'") + + def get_physical_address(self) -> int: + """ + Get the physical memory address (base + offset). + + Returns: + Physical memory address as integer + """ + return self.address + self.offset + + def get_address_hex(self) -> str: + """ + Get the physical memory address as hexadecimal string. + + Returns: + Physical memory address as hex string (e.g., '0xFED00100') + """ + return f"0x{self.get_physical_address():X}" + + def is_dram_access(self) -> bool: + """ + Check if this register uses DRAM access method. + + Returns: + True if access method is 'dram', False otherwise + """ + return self.access == 'dram' + + def is_mmio_access(self) -> bool: + """ + Check if this register uses MMIO access method. + + Returns: + True if access method is 'mmio', False otherwise + """ + return self.access == 'mmio' def __repr__(self) -> str: + """ + Detailed string representation for debugging. + + Returns: + Formatted string with register details including fields + """ reg_str = '' if self.value is not None: reg_val_str = f'0x{self.value:08X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) + if self.default is not None: default = f'{self.default:X}' else: default = 'Not Provided' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (0x{self.address:X} + 0x{self.offset:X}) [default: {default}]' + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'(0x{self.address:X} + 0x{self.offset:X}) [default: {default}]') reg_str += self._register_fields_str(True) return reg_str def __str__(self) -> str: + """ + String representation of memory register. + + Returns: + Formatted string with register details + """ reg_str = '' if self.value is not None: reg_val_str = f'0x{self.value:08X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (0x{self.address:X} + 0x{self.offset:X})' reg_str += self._register_fields_str() return reg_str - def read(self): - """Read the object""" - self.logger.log_debug(f'reading {self.name}') - _cs = cs() - if self.access == 'dram': - self.value = _cs.hals.MemRange.read(self.address + self.offset, self.size) - elif self.access == 'mmio': - self.value = _cs.hals.MMIO.read_MMIO_reg(self.address, self.offset, self.size) - return self.value + def read(self) -> int: + """ + Read the memory register value. - def write(self, value): - """Write the object""" - _cs = cs() - if self.access == 'dram': - _cs = _cs.hals.Memory.write_physical_mem(self.address + self.offset, self.size, value) - elif self.access == 'mmio': - _cs.hals.MMIO.write_MMIO_reg(self.address, self.offset, value, self.size, None) + Returns: + Current register value + + Raises: + MemoryRegisterError: If read operation fails + """ + try: + self.logger.log_debug(f'reading {self.name}') + _cs = cs() + + if self.access == 'dram': + self.value = _cs.hals.MemRange.read(self.address + self.offset, self.size) + elif self.access == 'mmio': + self.value = _cs.hals.MMIO.read_MMIO_reg(self.address, self.offset, self.size) + else: + raise MemoryRegisterError(f"Unsupported access method: {self.access}") + + return self.value + except Exception as e: + raise MemoryRegisterError(f"Failed to read memory register {self.name} at {self.get_address_hex()}: {e}") from e + + def write(self, value: int) -> None: + """ + Write a value to the memory register. + + Args: + value: Value to write to the register + + Raises: + MemoryRegisterError: If write operation fails + """ + try: + self.logger.log_debug(f'writing 0x{value:X} to {self.name}') + _cs = cs() + + if self.access == 'dram': + _cs.hals.Memory.write_physical_mem(self.address + self.offset, self.size, value) + elif self.access == 'mmio': + _cs.hals.MMIO.write_MMIO_reg(self.address, self.offset, value, self.size, None) + else: + raise MemoryRegisterError(f"Unsupported access method: {self.access}") + + self.value = value + except Exception as e: + raise MemoryRegisterError(f"Failed to write to memory register {self.name} at {self.get_address_hex()}: {e}") from e diff --git a/chipsec/cfg/parsers/registers/mm_msgbus.py b/chipsec/cfg/parsers/registers/mm_msgbus.py index 69b173e0..e892c56d 100644 --- a/chipsec/cfg/parsers/registers/mm_msgbus.py +++ b/chipsec/cfg/parsers/registers/mm_msgbus.py @@ -17,56 +17,197 @@ # Contact information: # chipsec@intel.com +""" +MM_MSGBUS Register configuration parser and accessor. + +This module provides MM_MSGBUSRegisters class for parsing and accessing memory-mapped message bus registers +in the CHIPSEC framework. MM_MSGBUS registers provide access to various hardware interfaces through +memory-mapped message bus protocols. +""" + +from typing import Dict, Any from chipsec.library.register import BaseConfigRegisterHelper +from chipsec.library.exceptions import CSConfigError from chipsec.chipset import cs +class MM_MSGBUSRegisterError(CSConfigError): + """Exception raised for MM_MSGBUS register-specific errors.""" + pass + + class MM_MSGBUSRegisters(BaseConfigRegisterHelper): - def __init__(self, cfg_obj): - super(MM_MSGBUSRegisters, self).__init__(cfg_obj) - self.offset = cfg_obj['offset'] - self.bar_size = None - self.port = cfg_obj['port'] - if 'size' in cfg_obj: - self.size = cfg_obj['size'] - else: - self.size = 4 + """ + MM_MSGBUS Register configuration parser and accessor. + + This class handles parsing and access to memory-mapped message bus registers, extending + the base register helper with MM_MSGBUS-specific functionality including port management + and memory-mapped message bus protocol operations. + + Attributes: + name (str): The name of the register + desc (str): Description of the register + offset (int): Offset within the MM_MSGBUS address space + size (int): Size of the register in bytes (default 4) + port (int): MM_MSGBUS port identifier + value (int): Current register value + default (int): Default register value + bar_size (Optional[int]): Size of the BAR (if applicable) + + Example: + >>> mm_msgbus_reg = MM_MSGBUSRegisters({ + ... 'name': 'PUNIT_REG', 'port': 0x04, 'offset': 0x100, 'size': 4 + ... }) + >>> value = mm_msgbus_reg.read() + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize MM_MSGBUS register configuration. + + Args: + cfg_obj: Dictionary containing MM_MSGBUS register configuration data + + Raises: + MM_MSGBUSRegisterError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.offset = cfg_obj['offset'] + self.port = cfg_obj['port'] + self.size = cfg_obj.get('size', 4) # Default to 4 bytes + self.bar_size = None + self._validate_mm_msgbus_config() + except KeyError as e: + raise MM_MSGBUSRegisterError(f"Missing required field in MM_MSGBUS register configuration: {e}") from e + except Exception as e: + raise MM_MSGBUSRegisterError(f"Failed to initialize MM_MSGBUS register configuration: {e}") from e + + def _validate_mm_msgbus_config(self) -> None: + """ + Validate MM_MSGBUS register-specific configuration requirements. + + Raises: + MM_MSGBUSRegisterError: If configuration is invalid + """ + if not self.name: + raise MM_MSGBUSRegisterError("MM_MSGBUS register configuration must have a valid name") + + if not isinstance(self.offset, int) or self.offset < 0: + raise MM_MSGBUSRegisterError(f"Invalid offset: {self.offset}. Must be a non-negative integer") + + if not isinstance(self.port, int) or self.port < 0: + raise MM_MSGBUSRegisterError(f"Invalid port: {self.port}. Must be a non-negative integer") + + if not isinstance(self.size, int) or self.size not in [1, 2, 4, 8]: + raise MM_MSGBUSRegisterError(f"Invalid register size: {self.size}. Must be 1, 2, 4, or 8 bytes") + + def get_port_hex(self) -> str: + """ + Get the MM_MSGBUS port as hexadecimal string. + + Returns: + Port as hex string (e.g., '0x04') + """ + return f"0x{self.port:X}" + + def get_offset_hex(self) -> str: + """ + Get the register offset as hexadecimal string. + + Returns: + Offset as hex string (e.g., '0x100') + """ + return f"0x{self.offset:X}" + + def get_address_info(self) -> str: + """ + Get formatted address information for this register. + + Returns: + Formatted string with port and offset information + """ + return f"mm_msgbus port {self.get_port_hex()}, off {self.get_offset_hex()}" def __repr__(self) -> str: + """ + Detailed string representation for debugging. + + Returns: + Formatted string with register details including fields + """ reg_str = '' if self.value is not None: reg_val_str = f'0x{self.value:08X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) + if self.default is not None: default = f'{self.default:X}' else: default = 'Not Provided' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (mm_msgbus port 0x{self.port:X}, off 0x{self.offset:X}) [default: {default}]' + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()}) [default: {default}]') reg_str += self._register_fields_str(True) return reg_str def __str__(self) -> str: + """ + String representation of MM_MSGBUS register. + + Returns: + Formatted string with register details + """ reg_str = '' if self.value is not None: reg_val_str = f'0x{self.value:08X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (mm_msgbus port 0x{self.port:X}, off 0x{self.offset:X})' + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()})') reg_str += self._register_fields_str() return reg_str - def read(self): - """Read the object""" - self.logger.log_debug(f'reading {self.name}') - _cs = cs() - self.value = _cs.hals.MMMsgBus.read(self.port, self.offset) - return self.value + def read(self) -> int: + """ + Read the MM_MSGBUS register value. - def write(self, value): - """Write the object""" - _cs = cs() - _cs.hals.MMMsgBus.write(self.port, self.offset, value) + Returns: + Current register value + + Raises: + MM_MSGBUSRegisterError: If read operation fails + """ + try: + self.logger.log_debug(f'reading {self.name}') + _cs = cs() + self.value = _cs.hals.MMMsgBus.read(self.port, self.offset) + return self.value + except Exception as e: + raise MM_MSGBUSRegisterError( + f"Failed to read MM_MSGBUS register {self.name} at {self.get_address_info()}: {e}" + ) from e + + def write(self, value: int) -> None: + """ + Write a value to the MM_MSGBUS register. + + Args: + value: Value to write to the register + + Raises: + MM_MSGBUSRegisterError: If write operation fails + """ + try: + self.logger.log_debug(f'writing 0x{value:X} to {self.name}') + _cs = cs() + _cs.hals.MMMsgBus.write(self.port, self.offset, value) + self.value = value + except Exception as e: + raise MM_MSGBUSRegisterError( + f"Failed to write to MM_MSGBUS register {self.name} at {self.get_address_info()}: {e}" + ) from e diff --git a/chipsec/cfg/parsers/registers/mmcfg.py b/chipsec/cfg/parsers/registers/mmcfg.py index 7921c200..ddd7c423 100644 --- a/chipsec/cfg/parsers/registers/mmcfg.py +++ b/chipsec/cfg/parsers/registers/mmcfg.py @@ -17,76 +17,237 @@ # Contact information: # chipsec@intel.com +""" +MMCFG Register configuration parser and accessor. + +This module provides MMCFGRegisters class for parsing and accessing memory-mapped configuration +space registers in the CHIPSEC framework. MMCFG registers provide access to PCI configuration +space through memory-mapped I/O. +""" + +from typing import Dict, Any from chipsec.chipset import cs from chipsec.library.register import BaseConfigRegisterHelper -from chipsec.library.exceptions import CSReadError +from chipsec.library.exceptions import CSReadError, CSConfigError + + +class MMCFGRegisterError(CSConfigError): + """Exception raised for MMCFG register-specific errors.""" + pass class MMCFGRegisters(BaseConfigRegisterHelper): - def __init__(self, cfg_obj, pci_obj): - super(MMCFGRegisters, self).__init__(cfg_obj) - self.size = cfg_obj['size'] - self.offset = cfg_obj['offset'] - self.pci = pci_obj + """ + MMCFG Register configuration parser and accessor. + + This class handles parsing and access to memory-mapped configuration space registers, + extending the base register helper with MMCFG-specific functionality including PCI + device management and memory-mapped configuration space operations. + + Attributes: + name (str): The name of the register + desc (str): Description of the register + size (int): Size of the register in bytes + offset (int): Offset within the MMCFG address space + pci: PCI device object containing bus, device, and function information + value (int): Current register value + default (int): Default register value + + Example: + >>> mmcfg_reg = MMCFGRegisters({ + ... 'name': 'PCI_REG', 'size': 4, 'offset': 0x10 + ... }, pci_obj) + >>> value = mmcfg_reg.read() + """ + + def __init__(self, cfg_obj: Dict[str, Any], pci_obj: Any) -> None: + """ + Initialize MMCFG register configuration. + + Args: + cfg_obj: Dictionary containing MMCFG register configuration data + pci_obj: PCI device object with bus, device, and function information + + Raises: + MMCFGRegisterError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.size = cfg_obj['size'] + self.offset = cfg_obj['offset'] + self.pci = pci_obj + self._validate_mmcfg_config() + except KeyError as e: + raise MMCFGRegisterError(f"Missing required field in MMCFG register configuration: {e}") from e + except Exception as e: + raise MMCFGRegisterError(f"Failed to initialize MMCFG register configuration: {e}") from e + + def _validate_mmcfg_config(self) -> None: + """ + Validate MMCFG register-specific configuration requirements. + + Raises: + MMCFGRegisterError: If configuration is invalid + """ + if not self.name: + raise MMCFGRegisterError("MMCFG register configuration must have a valid name") + + if not isinstance(self.size, int) or self.size not in [1, 2, 4, 8]: + raise MMCFGRegisterError(f"Invalid register size: {self.size}. Must be 1, 2, 4, or 8 bytes") + + if not isinstance(self.offset, int) or self.offset < 0: + raise MMCFGRegisterError(f"Invalid offset: {self.offset}. Must be a non-negative integer") + + if self.pci is None: + raise MMCFGRegisterError("PCI object must be provided for MMCFG register") + + def is_device_present(self) -> bool: + """ + Check if the PCI device is present and accessible. + + Returns: + True if device is present, False otherwise + """ + return self.pci is not None and self.pci.bus is not None + + def get_pci_address(self) -> str: + """ + Get the PCI device address in b:d.f format. + + Returns: + PCI address string (e.g., '00:1f.0') + """ + if not self.is_device_present(): + return "Device not present" + return f"{self.pci.bus:02d}:{self.pci.dev:02d}.{self.pci.fun:d}" + + def get_mmcfg_offset(self) -> int: + """ + Calculate the MMCFG offset for this register. + + Returns: + MMCFG offset value + """ + if not self.is_device_present(): + return 0 + return (self.pci.bus * 32 * 8 + self.pci.dev * 8 + self.pci.fun) * 0x1000 + self.offset + + def get_address_info(self) -> str: + """ + Get formatted address information for this register. + + Returns: + Formatted string with PCI and MMCFG address information + """ + if not self.is_device_present(): + return "Device not present" + + mmcfg_offset = self.get_mmcfg_offset() + return (f"b:d.f {self.get_pci_address()} + 0x{self.offset:X}, " + f"MMCFG + 0x{mmcfg_offset:X}") + + def _get_formatted_value(self) -> str: + """ + Get formatted register value based on size. + + Returns: + Formatted value string + """ + if self.value is not None: + return f'0x{self.value:0{self.size * 2}X}' + return str(self.value) def __repr__(self) -> str: - reg_str = '' - if self.value is not None: - reg_val_str = f'0x{self.value:0{self.size * 2}X}' - else: - reg_val_str = self.value - if self.pci.bus is not None: - b = self.pci.bus - else: + """ + Detailed string representation for debugging. + + Returns: + Formatted string with register details including fields + """ + if not self.is_device_present(): return 'Device not present' - d = self.pci.dev - f = self.pci.fun - o = self.offset - mmcfg_off_str = '' - mmcfg_off_str += f', MMCFG + 0x{(b * 32 * 8 + d * 8 + f) * 0x1000 + o:X}' + reg_val_str = self._get_formatted_value() + if self.default is not None: default = f'{self.default:X}' else: default = 'Not Provided' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (b:d.f {b:02d}:{d:02d}.{f:d} + 0x{o:X}{mmcfg_off_str}) [default: {default}]' + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()}) [default: {default}]') reg_str += self._register_fields_str(True) return reg_str def __str__(self) -> str: - reg_str = '' - if self.value is not None: - reg_val_str = f'0x{self.value:0{self.size * 2}X}' - else: - reg_val_str = self.value - if self.pci.bus is not None: - b = self.pci.bus - else: + """ + String representation of MMCFG register. + + Returns: + Formatted string with register details + """ + if not self.is_device_present(): return 'Device not present' - d = self.pci.dev - f = self.pci.fun - o = self.offset - mmcfg_off_str = '' - mmcfg_off_str += f', MMCFG + 0x{(b * 32 * 8 + d * 8 + f) * 0x1000 + o:X}' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (b:d.f {b:02d}:{d:02d}.{f:d} + 0x{o:X}{mmcfg_off_str})' + reg_val_str = self._get_formatted_value() + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()})') reg_str += self._register_fields_str() return reg_str - def read(self): - """Read the object""" - self.logger.log_debug(f'reading {self.name}') - _cs = cs() - if self.pci.bus is not None: - self.value = _cs.hals.MMCFG.read_mmcfg_reg(self.pci.bus, self.pci.dev, self.pci.fun, self.offset, self.size) - else: - raise CSReadError(f'PCI Device is not available ({self.pci.bus}:{self.pci.dev}.{self.pci.fun})') - return self.value + def read(self) -> int: + """ + Read the MMCFG register value. - def write(self, value): - """Write the object""" - _cs = cs() - if self.pci.bus is not None: - _cs.hals.MMCFG.write_mmcfg_reg(self.pci.bus, self.pci.dev, self.pci.fun, self.offset, self.size, value) - else: - raise CSReadError(f'PCI Device is not available ({self.pci.bus}:{self.pci.dev}.{self.pci.fun})') + Returns: + Current register value + + Raises: + MMCFGRegisterError: If read operation fails or device is not present + """ + try: + self.logger.log_debug(f'reading {self.name}') + + if not self.is_device_present(): + raise MMCFGRegisterError( + f'PCI Device is not available ({self.get_pci_address()})' + ) + + _cs = cs() + self.value = _cs.hals.MMCFG.read_mmcfg_reg( + self.pci.bus, self.pci.dev, self.pci.fun, self.offset, self.size + ) + return self.value + except CSReadError as e: + raise MMCFGRegisterError(f"Failed to read MMCFG register {self.name}: {e}") from e + except Exception as e: + raise MMCFGRegisterError(f"Failed to read MMCFG register {self.name}: {e}") from e + + def write(self, value: int) -> None: + """ + Write a value to the MMCFG register. + + Args: + value: Value to write to the register + + Raises: + MMCFGRegisterError: If write operation fails or device is not present + """ + try: + self.logger.log_debug(f'writing 0x{value:X} to {self.name}') + + if not self.is_device_present(): + raise MMCFGRegisterError( + f'PCI Device is not available ({self.get_pci_address()})' + ) + + _cs = cs() + _cs.hals.MMCFG.write_mmcfg_reg( + self.pci.bus, self.pci.dev, self.pci.fun, self.offset, self.size, value + ) + self.value = value + except CSReadError as e: + raise MMCFGRegisterError(f"Failed to write to MMCFG register {self.name}: {e}") from e + except Exception as e: + raise MMCFGRegisterError(f"Failed to write to MMCFG register {self.name}: {e}") from e diff --git a/chipsec/cfg/parsers/registers/mmio.py b/chipsec/cfg/parsers/registers/mmio.py index a40cc9bc..eda28160 100644 --- a/chipsec/cfg/parsers/registers/mmio.py +++ b/chipsec/cfg/parsers/registers/mmio.py @@ -17,64 +17,206 @@ # Contact information: # chipsec@intel.com -from typing import Any +""" +MMIO Register configuration parser and accessor. + +This module provides MMIORegisters class for parsing and accessing Memory-Mapped I/O registers +in the CHIPSEC framework. MMIO registers provide access to hardware interfaces through +memory-mapped address spaces. +""" + +from typing import Dict, Any from chipsec.chipset import cs from chipsec.library.register import BaseConfigRegisterHelper -from chipsec.library.exceptions import CSReadError, CSConfigError +from chipsec.library.exceptions import CSConfigError + + +class MMIORegisterError(CSConfigError): + """Exception raised for MMIO register-specific errors.""" + pass class MMIORegisters(BaseConfigRegisterHelper): - def __init__(self, cfg_obj): - super(MMIORegisters, self).__init__(cfg_obj) - self.cs = cs() - self.size = cfg_obj['size'] - self.offset = cfg_obj['offset'] - self.bar_base = None - self.bar_size = None - self.bar = None - self.range = None - if 'bar' in cfg_obj: - self.bar = cfg_obj['bar'] - elif 'range' in cfg_obj: - self.range = cfg_obj['range'] - + """ + MMIO Register configuration parser and accessor. + + This class handles parsing and access to Memory-Mapped I/O registers, extending + the base register helper with MMIO-specific functionality including BAR management, + memory range handling, and MMIO protocol operations. + + Attributes: + name (str): The name of the register + desc (str): Description of the register + size (int): Size of the register in bytes + offset (int): Offset within the MMIO address space + bar (Optional[str]): BAR identifier for MMIO access + range (Optional[str]): Memory range identifier + bar_base (Optional[int]): Base address of the BAR + bar_size (Optional[int]): Size of the BAR + value (int): Current register value + default (int): Default register value + instance: Instance identifier for the register + cs: Chipset interface object + + Example: + >>> mmio_reg = MMIORegisters({ + ... 'name': 'MMIO_REG', 'size': 4, 'offset': 0x100, 'bar': 'MMIO_BAR' + ... }) + >>> value = mmio_reg.read() + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize MMIO register configuration. + + Args: + cfg_obj: Dictionary containing MMIO register configuration data + + Raises: + MMIORegisterError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.cs = cs() + self.size = cfg_obj['size'] + self.offset = cfg_obj['offset'] + self.bar_base = None + self.bar_size = None + self.bar = cfg_obj.get('bar') + self.range = cfg_obj.get('range') + self._validate_mmio_config() + except KeyError as e: + raise MMIORegisterError(f"Missing required field in MMIO register configuration: {e}") from e + except Exception as e: + raise MMIORegisterError(f"Failed to initialize MMIO register configuration: {e}") from e + + def _validate_mmio_config(self) -> None: + """ + Validate MMIO register-specific configuration requirements. + + Raises: + MMIORegisterError: If configuration is invalid + """ + if not self.name: + raise MMIORegisterError("MMIO register configuration must have a valid name") + + if not isinstance(self.size, int) or self.size not in [1, 2, 4, 8]: + raise MMIORegisterError(f"Invalid register size: {self.size}. Must be 1, 2, 4, or 8 bytes") + + if not isinstance(self.offset, int) or self.offset < 0: + raise MMIORegisterError(f"Invalid offset: {self.offset}. Must be a non-negative integer") + + if not self.bar and not self.range: + raise MMIORegisterError("MMIO register must specify either 'bar' or 'range'") + + if self.bar and self.range: + raise MMIORegisterError("MMIO register cannot specify both 'bar' and 'range'") + + def get_offset_hex(self) -> str: + """ + Get the register offset as hexadecimal string. + + Returns: + Offset as hex string (e.g., '0x100') + """ + return f"0x{self.offset:X}" + + def get_instance_str(self) -> str: + """ + Get the instance as a formatted string. + + Returns: + Instance as string ('Fixed' if None, otherwise the instance value) + """ + return f'{self.instance}' if self.instance is not None else 'Fixed' + + def get_address_info(self) -> str: + """ + Get formatted address information for this register. + + Returns: + Formatted string with BAR/range and offset information + """ + if self.bar: + return f"{self.bar} + {self.get_offset_hex()} {self.get_instance_str()}" + elif self.range: + return f"Range:{self.range} + {self.get_offset_hex()} {self.get_instance_str()}" + else: + return f"Unknown + {self.get_offset_hex()} {self.get_instance_str()}" + + def _get_formatted_value(self) -> str: + """ + Get formatted register value based on size. + + Returns: + Formatted value string + """ + if self.value is not None: + return f'0x{self.value:0{self.size * 2}X}' + return str(self.value) def __repr__(self) -> str: - reg_str = '' - if self.value is not None: - reg_val_str = f'0x{self.value:0{self.size * 2}X}' - else: - reg_val_str = self.value - instance = f'{self.instance}' if self.instance is not None else 'Fixed' + """ + Detailed string representation for debugging. + + Returns: + Formatted string with register details including fields + """ + reg_val_str = self._get_formatted_value() + if self.default is not None: default = f'{self.default:X}' else: default = 'Not Provided' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} ({self.bar} + 0x{self.offset:X} {instance}) [default: {default}]' + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()}) [default: {default}]') reg_str += self._register_fields_str(True) return reg_str def __str__(self) -> str: - reg_str = '' - if self.value is not None: - reg_val_str = f'0x{self.value:0{self.size * 2}X}' - else: - reg_val_str = self.value + """ + String representation of MMIO register. - instance = f'{self.instance}' if self.instance is not None else 'Fixed' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} ({self.bar} + 0x{self.offset:X} {instance})' + Returns: + Formatted string with register details + """ + reg_val_str = self._get_formatted_value() + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()})') reg_str += self._register_fields_str() return reg_str def get_instance(self) -> Any: + """ + Get the instance value, handling nested instance attributes. + + Returns: + The instance value + """ return self.instance if not hasattr(self.instance, 'instance') else self.instance.instance - def populate_base_address(self): - if self.bar_base is None: + def populate_base_address(self) -> None: + """ + Populate the base address for MMIO operations. + + This method resolves the BAR or memory range to get the actual base address + for MMIO register access. + + Raises: + MMIORegisterError: If base address cannot be populated + """ + if self.bar_base is not None: + return # Already populated + + try: if self.bar: - (self.bar_base, self.bar_size) = self.cs.hals.MMIO.get_MMIO_BAR_base_address(self.bar, self.get_instance()) + self.bar_base, self.bar_size = self.cs.hals.MMIO.get_MMIO_BAR_base_address( + self.bar, self.get_instance() + ) elif self.range: mem_range_def = self.cs.hals.MemRange.get_def(self.range) if mem_range_def: @@ -82,28 +224,77 @@ class MMIORegisters(BaseConfigRegisterHelper): self.bar_size = mem_range_def.size self.bar_base = mem_range_def.address else: - raise CSConfigError(f"Memory Range ({self.range}) access type ({mem_range_def.access}) is not MMIO.") + raise MMIORegisterError( + f"Memory Range ({self.range}) access type ({mem_range_def.access}) is not MMIO." + ) else: - raise CSConfigError(f"Memory Range ({self.range}) cannot be found.") + raise MMIORegisterError(f"Memory Range ({self.range}) cannot be found.") else: - raise CSConfigError(f"Unable to populate MMIO Base Address: {self.name}") + raise MMIORegisterError(f"Unable to populate MMIO Base Address: {self.name}") + except Exception as e: + raise MMIORegisterError(f"Failed to populate base address for {self.name}: {e}") from e - def read(self): - """Read the object""" - self.logger.log_debug(f'reading {self.name}') - self.populate_base_address() - self.value = self.cs.hals.MMIO.read_MMIO_reg(self.bar_base, self.offset, self.size) - self.logger.log_debug('done reading') - return self.value + def read(self) -> int: + """ + Read the MMIO register value. - def write(self, value): - """Write the object""" - self.populate_base_address() - self.cs.hals.MMIO.write_MMIO_reg(self.bar_base, self.offset, value, self.size) + Returns: + Current register value - def write_subset(self, value, size, offset=0): - if offset < self.size and size <= self.size - offset: + Raises: + MMIORegisterError: If read operation fails + """ + try: + self.logger.log_debug(f'reading {self.name}') + self.populate_base_address() + self.value = self.cs.hals.MMIO.read_MMIO_reg(self.bar_base, self.offset, self.size) + self.logger.log_debug('done reading') + return self.value + except Exception as e: + raise MMIORegisterError(f"Failed to read MMIO register {self.name}: {e}") from e + + def write(self, value: int) -> None: + """ + Write a value to the MMIO register. + + Args: + value: Value to write to the register + + Raises: + MMIORegisterError: If write operation fails + """ + try: + self.logger.log_debug(f'writing 0x{value:X} to {self.name}') + self.populate_base_address() + self.cs.hals.MMIO.write_MMIO_reg(self.bar_base, self.offset, value, self.size) + self.value = value + except Exception as e: + raise MMIORegisterError(f"Failed to write to MMIO register {self.name}: {e}") from e + + def write_subset(self, value: int, size: int, offset: int = 0) -> None: + """ + Write a subset of the MMIO register. + + Args: + value: Value to write + size: Size of the write operation in bytes + offset: Offset within the register for the write operation + + Raises: + MMIORegisterError: If write operation fails or parameters are invalid + """ + try: + if offset < 0 or size <= 0: + raise MMIORegisterError("Offset must be non-negative and size must be positive") + + if offset >= self.size or size > self.size - offset: + raise MMIORegisterError( + f"Improper Offset ({offset}) or Size ({size}) requested in write subset for {self.name}. " + f"Register size is {self.size} bytes." + ) + + self.logger.log_debug(f'writing subset 0x{value:X} to {self.name} at offset {offset}, size {size}') self.populate_base_address() self.cs.hals.MMIO.write_MMIO_reg(self.bar_base, self.offset + offset, value, size) - else: - raise CSReadError(f"Improper Offset or Size requested in write subset for {self.name}") + except Exception as e: + raise MMIORegisterError(f"Failed to write subset to MMIO register {self.name}: {e}") from e diff --git a/chipsec/cfg/parsers/registers/msgbus.py b/chipsec/cfg/parsers/registers/msgbus.py index 3446ae62..542821f0 100644 --- a/chipsec/cfg/parsers/registers/msgbus.py +++ b/chipsec/cfg/parsers/registers/msgbus.py @@ -17,52 +17,192 @@ # Contact information: # chipsec@intel.com +""" +MSGBUS Register configuration parser and accessor. + +This module provides MSGBUSRegisters class for parsing and accessing message bus registers +in the CHIPSEC framework. Message bus registers provide access to various hardware interfaces +through Intel's Message Bus protocol. +""" + +from typing import Dict, Any from chipsec.chipset import cs from chipsec.library.register import BaseConfigRegisterHelper +from chipsec.library.exceptions import CSConfigError + + +class MSGBUSRegisterError(CSConfigError): + """Exception raised for MSGBUS register-specific errors.""" + pass class MSGBUSRegisters(BaseConfigRegisterHelper): - def __init__(self, cfg_obj): - super(MSGBUSRegisters, self).__init__(cfg_obj) - self.offset = cfg_obj['offset'] - self.bar_size = None - self.port = cfg_obj['port'] + """ + MSGBUS Register configuration parser and accessor. + + This class handles parsing and access to message bus registers, extending the base + register helper with MSGBUS-specific functionality including port management and + message bus protocol operations. + + Attributes: + name (str): The name of the register + desc (str): Description of the register + offset (int): Offset within the MSGBUS address space + port (int): MSGBUS port identifier + value (int): Current register value + default (int): Default register value + bar_size (Optional[int]): Size of the BAR (if applicable) + + Example: + >>> msgbus_reg = MSGBUSRegisters({ + ... 'name': 'PUNIT_REG', 'port': 0x04, 'offset': 0x100 + ... }) + >>> value = msgbus_reg.read() + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize MSGBUS register configuration. + + Args: + cfg_obj: Dictionary containing MSGBUS register configuration data + + Raises: + MSGBUSRegisterError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.offset = cfg_obj['offset'] + self.port = cfg_obj['port'] + self.bar_size = None + self._validate_msgbus_config() + except KeyError as e: + raise MSGBUSRegisterError(f"Missing required field in MSGBUS register configuration: {e}") from e + except Exception as e: + raise MSGBUSRegisterError(f"Failed to initialize MSGBUS register configuration: {e}") from e + + def _validate_msgbus_config(self) -> None: + """ + Validate MSGBUS register-specific configuration requirements. + + Raises: + MSGBUSRegisterError: If configuration is invalid + """ + if not self.name: + raise MSGBUSRegisterError("MSGBUS register configuration must have a valid name") + + if not isinstance(self.offset, int) or self.offset < 0: + raise MSGBUSRegisterError(f"Invalid offset: {self.offset}. Must be a non-negative integer") + + if not isinstance(self.port, int) or self.port < 0: + raise MSGBUSRegisterError(f"Invalid port: {self.port}. Must be a non-negative integer") + + def get_port_hex(self) -> str: + """ + Get the MSGBUS port as hexadecimal string. + + Returns: + Port as hex string (e.g., '0x04') + """ + return f"0x{self.port:X}" + + def get_offset_hex(self) -> str: + """ + Get the register offset as hexadecimal string. + + Returns: + Offset as hex string (e.g., '0x100') + """ + return f"0x{self.offset:X}" + + def get_address_info(self) -> str: + """ + Get formatted address information for this register. + + Returns: + Formatted string with port and offset information + """ + return f"mm_msgbus port {self.get_port_hex()}, off {self.get_offset_hex()}" def __repr__(self) -> str: + """ + Detailed string representation for debugging. + + Returns: + Formatted string with register details including fields + """ reg_str = '' if self.value is not None: reg_val_str = f'0x{self.value:08X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) + if self.default is not None: default = f'{self.default:X}' else: default = 'Not Provided' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (mm_msgbus port 0x{self.port:X}, off 0x{self.offset:X}) [default: {default}]' + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()}) [default: {default}]') reg_str += self._register_fields_str(True) return reg_str def __str__(self) -> str: + """ + String representation of MSGBUS register. + + Returns: + Formatted string with register details + """ reg_str = '' if self.value is not None: reg_val_str = f'0x{self.value:08X}' else: - reg_val_str = self.value + reg_val_str = str(self.value) - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (mm_msgbus port 0x{self.port:X}, off 0x{self.offset:X})' + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()})') reg_str += self._register_fields_str() return reg_str - def read(self): - """Read the object""" - self.logger.log_debug(f'reading {self.name}') - _cs = cs() - self.value = _cs.hals.MsgBus.msgbus_reg_read(self.port, self.offset) - return self.value + def read(self) -> int: + """ + Read the MSGBUS register value. - def write(self, value): - """Write the object""" - _cs = cs() - _cs.hals.MsgBus.msgbus_reg_write(self.port, self.offset, value) + Returns: + Current register value + + Raises: + MSGBUSRegisterError: If read operation fails + """ + try: + self.logger.log_debug(f'reading {self.name}') + _cs = cs() + self.value = _cs.hals.MsgBus.msgbus_reg_read(self.port, self.offset) + return self.value + except Exception as e: + raise MSGBUSRegisterError( + f"Failed to read MSGBUS register {self.name} at {self.get_address_info()}: {e}" + ) from e + + def write(self, value: int) -> None: + """ + Write a value to the MSGBUS register. + + Args: + value: Value to write to the register + + Raises: + MSGBUSRegisterError: If write operation fails + """ + try: + self.logger.log_debug(f'writing 0x{value:X} to {self.name}') + _cs = cs() + _cs.hals.MsgBus.msgbus_reg_write(self.port, self.offset, value) + self.value = value + except Exception as e: + raise MSGBUSRegisterError( + f"Failed to write to MSGBUS register {self.name} at {self.get_address_info()}: {e}" + ) from e diff --git a/chipsec/cfg/parsers/registers/msr.py b/chipsec/cfg/parsers/registers/msr.py index edfe8cbf..304d7a23 100644 --- a/chipsec/cfg/parsers/registers/msr.py +++ b/chipsec/cfg/parsers/registers/msr.py @@ -17,58 +17,227 @@ # Contact information: # chipsec@intel.com +""" +MSR Register configuration parser and accessor. + +This module provides MSRRegisters class for parsing and accessing Model Specific Registers (MSRs) +in the CHIPSEC framework. MSR registers provide access to processor-specific configuration and +control settings. +""" + +from typing import Dict, Any, Tuple from chipsec.chipset import cs from chipsec.library.register import BaseConfigRegisterHelper +from chipsec.library.exceptions import CSConfigError + + +class MSRRegisterError(CSConfigError): + """Exception raised for MSR register-specific errors.""" + pass class MSRRegisters(BaseConfigRegisterHelper): - def __init__(self, cfg_obj): - super(MSRRegisters, self).__init__(cfg_obj) - if 'size' in cfg_obj: - self.size = cfg_obj['size'] - else: - self.size = 8 - self.thread = cfg_obj['instance'] - self.msr = cfg_obj['msr'] + """ + MSR Register configuration parser and accessor. + + This class handles parsing and access to Model Specific Registers (MSRs), extending + the base register helper with MSR-specific functionality including thread management + and MSR protocol operations. + + Attributes: + name (str): The name of the register + desc (str): Description of the register + size (int): Size of the register in bytes (default 8 for MSRs) + thread (int): Thread/CPU instance identifier + msr (int): MSR register number + value (int): Current register value + default (int): Default register value + + Example: + >>> msr_reg = MSRRegisters({ + ... 'name': 'IA32_FEATURE_CONTROL', 'msr': 0x3A, 'instance': 0 + ... }) + >>> value = msr_reg.read() + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize MSR register configuration. + + Args: + cfg_obj: Dictionary containing MSR register configuration data + + Raises: + MSRRegisterError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.size = cfg_obj.get('size', 8) # MSRs are typically 8 bytes + self.thread = cfg_obj['instance'] + self.msr = cfg_obj['msr'] + self._validate_msr_config() + except KeyError as e: + raise MSRRegisterError(f"Missing required field in MSR register configuration: {e}") from e + except Exception as e: + raise MSRRegisterError(f"Failed to initialize MSR register configuration: {e}") from e + + def _validate_msr_config(self) -> None: + """ + Validate MSR register-specific configuration requirements. + + Raises: + MSRRegisterError: If configuration is invalid + """ + if not self.name: + raise MSRRegisterError("MSR register configuration must have a valid name") + + if not isinstance(self.thread, int) or self.thread < 0: + raise MSRRegisterError(f"Invalid thread/instance: {self.thread}. Must be a non-negative integer") + + if not isinstance(self.msr, int) or self.msr < 0: + raise MSRRegisterError(f"Invalid MSR number: {self.msr}. Must be a non-negative integer") + + if not isinstance(self.size, int) or self.size not in [1, 2, 4, 8]: + raise MSRRegisterError(f"Invalid register size: {self.size}. Must be 1, 2, 4, or 8 bytes") + + def get_msr_hex(self) -> str: + """ + Get the MSR number as hexadecimal string. + + Returns: + MSR number as hex string (e.g., '0x3A') + """ + return f"0x{self.msr:X}" + + def get_thread_hex(self) -> str: + """ + Get the thread/instance as hexadecimal string. + + Returns: + Thread number as hex string (e.g., '0x0') + """ + return f"0x{self.thread:X}" + + def get_address_info(self) -> str: + """ + Get formatted address information for this register. + + Returns: + Formatted string with MSR and thread information + """ + return f"MSR {self.get_msr_hex()} Thread {self.get_thread_hex()}" + + def _get_formatted_value(self) -> str: + """ + Get formatted register value based on size. + + Returns: + Formatted value string + """ + if self.value is not None: + return f'0x{self.value:0{self.size * 2}X}' + return str(self.value) + + def _split_64bit_value(self, value: int) -> Tuple[int, int]: + """ + Split a 64-bit value into EAX (low 32-bits) and EDX (high 32-bits). + + Args: + value: 64-bit value to split + + Returns: + Tuple of (eax, edx) values + """ + eax = value & 0xFFFFFFFF + edx = (value >> 32) & 0xFFFFFFFF + return eax, edx + + def _combine_32bit_values(self, eax: int, edx: int) -> int: + """ + Combine EAX and EDX values into a 64-bit value. + + Args: + eax: Low 32-bit value + edx: High 32-bit value + + Returns: + Combined 64-bit value + """ + return (edx << 32) | eax def __repr__(self) -> str: - reg_str = '' - if self.value is not None: - reg_val_str = f'0x{self.value:0{self.size * 2}X}' - else: - reg_val_str = self.value + """ + Detailed string representation for debugging. + + Returns: + Formatted string with register details including fields + """ + reg_val_str = self._get_formatted_value() + if self.default is not None: default = f'{self.default:X}' else: default = 'Not Provided' - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (MSR 0x{self.msr:X} Thread 0x{self.thread:X}) [default: {default}]' + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()}) [default: {default}]') reg_str += self._register_fields_str(True) return reg_str def __str__(self) -> str: - reg_str = '' - if self.value is not None: - reg_val_str = f'0x{self.value:0{self.size * 2}X}' - else: - reg_val_str = self.value + """ + String representation of MSR register. - reg_str = f'[*] {self.name} = {reg_val_str} << {self.desc} (MSR 0x{self.msr:X} Thread 0x{self.thread:X})' + Returns: + Formatted string with register details + """ + reg_val_str = self._get_formatted_value() + + reg_str = (f'[*] {self.name} = {reg_val_str} << {self.desc} ' + f'({self.get_address_info()})') reg_str += self._register_fields_str() return reg_str - def read(self): - """Read the object""" - self.logger.log_debug(f'reading {self.name}') - _cs = cs() - (eax, edx) = _cs.hals.Msr.read_msr(self.thread, self.msr) - self.value = (edx << 32) | eax - return self.value + def read(self) -> int: + """ + Read the MSR register value. - def write(self, value): - """Write the object""" - _cs = cs() - eax = value & 0xFFFFFFFF - edx = (value >> 32) & 0xFFFFFFFF - _cs.hals.Msr.write(self.thread, self.msr, eax, edx) + Returns: + Current register value + + Raises: + MSRRegisterError: If read operation fails + """ + try: + self.logger.log_debug(f'reading {self.name}') + _cs = cs() + eax, edx = _cs.hals.Msr.read_msr(self.thread, self.msr) + self.value = self._combine_32bit_values(eax, edx) + return self.value + except Exception as e: + raise MSRRegisterError( + f"Failed to read MSR register {self.name} at {self.get_address_info()}: {e}" + ) from e + + def write(self, value: int) -> None: + """ + Write a value to the MSR register. + + Args: + value: Value to write to the register + + Raises: + MSRRegisterError: If write operation fails + """ + try: + self.logger.log_debug(f'writing 0x{value:X} to {self.name}') + _cs = cs() + eax, edx = self._split_64bit_value(value) + _cs.hals.Msr.write(self.thread, self.msr, eax, edx) + self.value = value + except Exception as e: + raise MSRRegisterError( + f"Failed to write to MSR register {self.name} at {self.get_address_info()}: {e}" + ) from e diff --git a/chipsec/cfg/parsers/registers/pci.py b/chipsec/cfg/parsers/registers/pci.py index 88d03596..c30c5434 100644 --- a/chipsec/cfg/parsers/registers/pci.py +++ b/chipsec/cfg/parsers/registers/pci.py @@ -17,17 +17,164 @@ # Contact information: # chipsec@intel.com +""" +PCI Register Configuration Helper + +Provides PCI configuration space register-specific functionality. +""" + +from typing import Dict, Any, Union + from chipsec.chipset import cs from chipsec.library.register import BaseConfigRegisterHelper -from chipsec.library.exceptions import CSReadError +from chipsec.library.exceptions import CSReadError, CSConfigError + + +class PCIRegisterError(CSConfigError): + """Custom exception for PCI register configuration errors.""" + pass class PCIRegisters(BaseConfigRegisterHelper): - def __init__(self, cfg_obj, pci_obj): - super(PCIRegisters, self).__init__(cfg_obj) - self.size = cfg_obj['size'] - self.offset = cfg_obj['offset'] - self.pci = pci_obj + """ + PCI register configuration helper for PCI configuration space registers. + + Manages PCI configuration space register access including bus, device, + function addressing and register offset handling. + """ + + def __init__(self, cfg_obj: Dict[str, Any], pci_obj): + """ + Initialize PCI register configuration helper. + + Args: + cfg_obj: Configuration object containing PCI register fields + pci_obj: PCI object containing bus/device/function information + + Raises: + PCIRegisterError: If PCI register configuration is invalid + """ + try: + super().__init__(cfg_obj) + + # Required fields for PCI registers + required_fields = ['size', 'offset'] + missing_fields = [field for field in required_fields + if field not in cfg_obj] + if missing_fields: + raise PCIRegisterError( + f"Missing required PCI register fields: {missing_fields}") + + self.size: int = cfg_obj['size'] + self.offset: Union[int, str] = cfg_obj['offset'] + self.pci = pci_obj + + # Validate configuration + if not self.validate_pci_register_config(): + raise PCIRegisterError("Invalid PCI register configuration") + + except Exception as e: + if isinstance(e, (PCIRegisterError, CSConfigError)): + raise + raise PCIRegisterError( + f"Error initializing PCI register: {str(e)}") from e + + def validate_pci_register_config(self) -> bool: + """ + Validate PCI register-specific configuration. + + Returns: + True if PCI register configuration is valid, False otherwise + """ + try: + # Validate size + if not isinstance(self.size, int) or self.size <= 0: + return False + + # Validate offset + if isinstance(self.offset, str): + try: + int(self.offset, 16 if self.offset.startswith('0x') else 10) + except ValueError: + return False + elif not isinstance(self.offset, int): + return False + + # Validate PCI object + if self.pci is None: + return False + + return True + except Exception: + return False + + def get_offset_int(self) -> int: + """ + Get register offset as integer value. + + Returns: + Offset as integer + + Raises: + PCIRegisterError: If offset cannot be converted to integer + """ + try: + if isinstance(self.offset, int): + return self.offset + elif isinstance(self.offset, str): + return int(self.offset, + 16 if self.offset.startswith('0x') else 10) + else: + raise PCIRegisterError( + f"Invalid offset type: {type(self.offset)}") + except ValueError as e: + raise PCIRegisterError( + f"Cannot convert offset to integer: {self.offset}") from e + + def get_bdf_string(self) -> str: + """ + Get Bus:Device:Function as formatted string. + + Returns: + BDF string or 'Unknown' if PCI object is invalid + """ + try: + if self.pci and hasattr(self.pci, 'bus') and self.pci.bus is not None: + bus = f'{self.pci.bus:02d}' + dev = f'{self.pci.dev:02d}' if self.pci.dev is not None else 'XX' + fun = f'{self.pci.fun:01d}' if self.pci.fun is not None else 'X' + return f'{bus}:{dev}.{fun}' + return 'Unknown' + except Exception: + return 'Unknown' + + def get_register_summary(self) -> Dict[str, Any]: + """ + Get summary of PCI register configuration. + + Returns: + Dictionary with PCI register configuration summary + """ + try: + return { + 'name': self.name, + 'bdf': self.get_bdf_string(), + 'offset': self.get_offset_int(), + 'size': self.size, + 'desc': getattr(self, 'desc', None), + 'is_valid': self.validate_pci_register_config(), + 'has_value': self.value is not None + } + except Exception: + return { + 'name': getattr(self, 'name', 'Unknown'), + 'bdf': 'Unknown', + 'offset': 0, + 'size': 0, + 'desc': None, + 'is_valid': False, + 'has_value': False + } def __repr__(self) -> str: reg_str = ''