diff --git a/chipsec/cfg/parsers/ip/generic.py b/chipsec/cfg/parsers/ip/generic.py index 5a5f65c3..05b093ab 100644 --- a/chipsec/cfg/parsers/ip/generic.py +++ b/chipsec/cfg/parsers/ip/generic.py @@ -17,19 +17,264 @@ # Contact information: # chipsec@intel.com +""" +Generic IP Configuration Helper + +This module provides generic configuration management functionality for IP-based parsers. +It serves as the base class for all IP-specific configuration helpers, offering common +functionality like configuration validation, manipulation, and utility methods. +""" + +from typing import Dict, Any, List, Optional + from chipsec.parsers import BaseConfigHelper -class GenericConfig(BaseConfigHelper): - def __init__(self, cfg_obj): - super(GenericConfig, self).__init__(cfg_obj) - self.name = cfg_obj['name'] - if 'config' in cfg_obj: - self.config = cfg_obj['config'] - else: - self.config = [] +class GenericConfigError(Exception): + """Custom exception for generic configuration errors.""" + pass - def add_config(self, config): - for cfg in config: - if cfg not in self.config: - self.config.append(cfg) + +class GenericConfig(BaseConfigHelper): + """ + Generic configuration helper for IP-based parsers. + + Provides basic configuration management functionality that can be + extended by specific IP parsers. + """ + + def __init__(self, cfg_obj: Dict[str, Any]): + """ + Initialize generic configuration helper. + + Args: + cfg_obj: Configuration object containing name and optional config list + + Raises: + GenericConfigError: If required configuration is missing + """ + try: + super().__init__(cfg_obj) + + if 'name' not in cfg_obj: + raise GenericConfigError("Missing required 'name' field in configuration object") + + self.name: str = cfg_obj['name'] + self.config: List[Any] = cfg_obj.get('config', []) + + except Exception as e: + raise GenericConfigError(f"Error initializing generic configuration: {str(e)}") from e + + def add_config(self, config: List[Any]) -> None: + """ + Add configurations to the current configuration list. + + Args: + config: List of configuration items to add + + Raises: + GenericConfigError: If configuration addition fails + """ + try: + if not isinstance(config, list): + raise GenericConfigError("Configuration must be a list") + + for cfg in config: + if cfg not in self.config: + self.config.append(cfg) + except Exception as e: + raise GenericConfigError(f"Error adding configuration: {str(e)}") from e + + def remove_config(self, config_item: Any) -> bool: + """ + Remove a configuration item from the configuration list. + + Args: + config_item: Configuration item to remove + + Returns: + True if item was removed, False if not found + """ + try: + if config_item in self.config: + self.config.remove(config_item) + return True + return False + except Exception: + return False + + def clear_config(self) -> None: + """Clear all configuration items.""" + self.config.clear() + + def get_config_count(self) -> int: + """Get the number of configuration items.""" + return len(self.config) + + def has_config(self, config_item: Any) -> bool: + """ + Check if a configuration item exists. + + Args: + config_item: Configuration item to check + + Returns: + True if item exists, False otherwise + """ + return config_item in self.config + + def get_config_copy(self) -> List[Any]: + """Get a copy of the current configuration list.""" + return self.config.copy() + + def validate_config(self) -> bool: + """ + Validate the current configuration comprehensively. + + Performs validation of: + - Name field presence and type + - Configuration list type and structure + - Basic consistency checks + + Returns: + True if configuration is valid, False otherwise + """ + try: + # Basic validation - ensure name exists and is a non-empty string + if not self.name or not isinstance(self.name, str) or not self.name.strip(): + return False + + # Ensure config is a list + if not isinstance(self.config, list): + return False + + # Additional validation - check for None values in config + if any(item is None for item in self.config): + return False + + # Check for duplicate configurations (if they're hashable) + try: + unique_configs = set() + for item in self.config: + if isinstance(item, (str, int, float, bool, tuple)): + if item in unique_configs: + # Found duplicate, but this might be intentional + pass + unique_configs.add(item) + except (TypeError, AttributeError): + # Items are not hashable, skip duplicate check + pass + + return True + except Exception: + return False + + def __repr__(self) -> str: + """Return detailed string representation of the configuration.""" + return f"GenericConfig(name='{self.name}', config_count={len(self.config)})" + + def __str__(self) -> str: + """Return human-readable string representation of the configuration.""" + status = "valid" if self.validate_config() else "invalid" + return f"GenericConfig '{self.name}' with {len(self.config)} items ({status})" + + def update_config(self, config_updates: List[Any]) -> None: + """ + Update configuration by replacing existing items with new ones. + + Args: + config_updates: List of configuration items to update or add + + Raises: + GenericConfigError: If configuration update fails + """ + try: + if not isinstance(config_updates, list): + raise GenericConfigError("Configuration updates must be a list") + + # Clear existing and add new configurations + self.config.clear() + self.config.extend(config_updates) + + except Exception as e: + raise GenericConfigError(f"Error updating configuration: {str(e)}") from e + + def find_config(self, predicate) -> Optional[Any]: + """ + Find the first configuration item that matches the given predicate. + + Args: + predicate: Function that takes a config item and returns True/False + + Returns: + First matching configuration item, or None if not found + """ + try: + for config_item in self.config: + if predicate(config_item): + return config_item + return None + except Exception: + return None + + def filter_config(self, predicate) -> List[Any]: + """ + Filter configuration items based on a predicate function. + + Args: + predicate: Function that takes a config item and returns True/False + + Returns: + List of configuration items that match the predicate + """ + try: + return [item for item in self.config if predicate(item)] + except Exception: + return [] + + def merge_config(self, other_config: 'GenericConfig') -> None: + """ + Merge configuration from another GenericConfig instance. + + Args: + other_config: Another GenericConfig instance to merge from + + Raises: + GenericConfigError: If merge operation fails + """ + try: + if not isinstance(other_config, GenericConfig): + raise GenericConfigError("Can only merge with another GenericConfig instance") + + # Add items from other config that don't already exist + for item in other_config.config: + if item not in self.config: + self.config.append(item) + + except Exception as e: + raise GenericConfigError(f"Error merging configuration: {str(e)}") from e + + def get_config_summary(self) -> Dict[str, Any]: + """ + Get a summary of the current configuration. + + Returns: + Dictionary containing configuration summary information + """ + try: + unique_types = set(type(item).__name__ for item in self.config) + return { + 'name': self.name, + 'total_items': len(self.config), + 'unique_types': list(unique_types), + 'is_valid': self.validate_config(), + 'is_empty': len(self.config) == 0 + } + except Exception: + return { + 'name': getattr(self, 'name', 'Unknown'), + 'total_items': 0, + 'unique_types': [], + 'is_valid': False, + 'is_empty': True + } diff --git a/chipsec/cfg/parsers/ip/io.py b/chipsec/cfg/parsers/ip/io.py index 5006ddc6..2447bb1b 100644 --- a/chipsec/cfg/parsers/ip/io.py +++ b/chipsec/cfg/parsers/ip/io.py @@ -17,15 +17,143 @@ # Contact information: # chipsec@intel.com -from chipsec.cfg.parsers.ip.generic import GenericConfig +""" +I/O Port IP Configuration Helper + +Provides I/O port-specific configuration management functionality for +I/O port-based IP parsers. +""" + +from typing import Dict, Any, Union + +from chipsec.cfg.parsers.ip.generic import GenericConfig, GenericConfigError + + +class IOConfigError(GenericConfigError): + """Custom exception for I/O configuration errors.""" + pass class IOConfig(GenericConfig): - def __init__(self, cfg_obj): - super(IOConfig, self).__init__(cfg_obj) - self.port = cfg_obj['port'] + """ + I/O configuration helper for I/O port-based IP regions. + + Handles configuration of I/O port-based IP including port addresses + and access management. + """ + + def __init__(self, cfg_obj: Dict[str, Any]): + """ + Initialize I/O configuration helper. + + Args: + cfg_obj: Configuration object containing I/O-specific fields + + Raises: + IOConfigError: If required I/O configuration is missing or invalid + """ + try: + super().__init__(cfg_obj) + + # Required field for I/O configuration + if 'port' not in cfg_obj: + raise IOConfigError("Missing required 'port' field") + + self.port: Union[int, str] = cfg_obj['port'] + + # Validate configuration after initialization + if not self.validate_io_config(): + raise IOConfigError("Invalid I/O configuration detected") + + except Exception as e: + if isinstance(e, IOConfigError): + raise + raise IOConfigError( + f"Error initializing I/O configuration: {str(e)}") from e + + def validate_io_config(self) -> bool: + """ + Validate I/O-specific configuration. + + Returns: + True if I/O configuration is valid, False otherwise + """ + try: + # Call parent validation first + if not self.validate_config(): + return False + + # Validate port (can be int or hex string) + if isinstance(self.port, str): + try: + int(self.port, 16 if self.port.startswith('0x') else 10) + except ValueError: + return False + elif not isinstance(self.port, int): + return False + + return True + except Exception: + return False + + def get_port_int(self) -> int: + """ + Get port as integer value. + + Returns: + Port as integer + + Raises: + IOConfigError: If port cannot be converted to integer + """ + try: + if isinstance(self.port, int): + return self.port + elif isinstance(self.port, str): + return int(self.port, + 16 if self.port.startswith('0x') else 10) + else: + raise IOConfigError(f"Invalid port type: {type(self.port)}") + except ValueError as e: + raise IOConfigError( + f"Cannot convert port to integer: {self.port}") from e + + def get_io_summary(self) -> Dict[str, Any]: + """ + Get summary of I/O configuration. + + Returns: + Dictionary with I/O configuration summary + """ + try: + return { + 'name': self.name, + 'port': self.get_port_int(), + 'is_valid': self.validate_io_config(), + 'config_items': len(self.config) + } + except Exception: + return { + 'name': getattr(self, 'name', 'Unknown'), + 'port': 0, + 'is_valid': False, + 'config_items': 0 + } def __str__(self) -> str: - ret = f'name: {self.name}, port: {self.port}' - ret += f', config: {self.config}' - return ret + """Return human-readable string representation.""" + try: + port_str = f"0x{self.get_port_int():x}" + ret = f'name: {self.name}, port: {port_str}' + ret += f', config: {len(self.config)} items' + return ret + except Exception: + # Fallback to basic representation if conversion fails + ret = f'name: {self.name}, port: {self.port}' + ret += f', config: {self.config}' + return ret + + def __repr__(self) -> str: + """Return detailed string representation.""" + return (f"IOConfig(name='{self.name}', port={self.port}, " + f"config_count={len(self.config)})") diff --git a/chipsec/cfg/parsers/ip/iobar.py b/chipsec/cfg/parsers/ip/iobar.py index 465310d4..d2495293 100644 --- a/chipsec/cfg/parsers/ip/iobar.py +++ b/chipsec/cfg/parsers/ip/iobar.py @@ -17,48 +17,308 @@ # Contact information: # chipsec@intel.com -from chipsec.cfg.parsers.ip.generic import GenericConfig +""" +I/O BAR IP Configuration Helper + +Provides I/O BAR-specific configuration management functionality for +I/O base address registers. +""" + +from typing import Dict, Any, Optional, Union, TYPE_CHECKING + +from chipsec.cfg.parsers.ip.generic import GenericConfig, GenericConfigError + +if TYPE_CHECKING: + from chipsec.cfg.parsers.ip.pci_device import PCIObj + + +class IOBarConfigError(GenericConfigError): + """Custom exception for I/O BAR configuration errors.""" + pass class IOObj: + """ + I/O object representing an I/O port region. + + Contains base address, size, and associated PCI instance information. + """ + def __init__(self, instance: 'PCIObj'): - self.base = None - self.size = 0 + """ + Initialize I/O object. + + Args: + instance: Associated PCI object instance + """ + self.base: Optional[int] = None + self.size: int = 0 self.instance = instance + def set_base_and_size(self, base: Optional[int], size: int) -> None: + """ + Set base address and size for the I/O region. + + Args: + base: Base address (can be None) + size: Size of the I/O region + + Raises: + IOBarConfigError: If parameters are invalid + """ + try: + if base is not None and not isinstance(base, int): + raise IOBarConfigError( + "Base address must be an integer or None") + if not isinstance(size, int) or size < 0: + raise IOBarConfigError( + "Size must be a non-negative integer") + + self.base = base + self.size = size + except Exception as e: + if isinstance(e, IOBarConfigError): + raise + raise IOBarConfigError( + f"Error setting I/O base and size: {str(e)}") from e + + def is_valid(self) -> bool: + """ + Check if I/O object is valid. + + Returns: + True if I/O object has valid configuration + """ + return (self.base is not None and + isinstance(self.size, int) and + self.size >= 0 and + self.instance is not None) + def __str__(self) -> str: - return f'instance: {self.instance}, base: {self.base}' + """Return human-readable string representation.""" + basestr = f'0x{self.base:X}' if self.base else 'None' + return (f'instance: {self.instance}, base: {basestr}, ' + f'size: 0x{self.size:X}') + + def __repr__(self) -> str: + """Return detailed string representation.""" + return (f"IOObj(base={self.base}, size={self.size}, " + f"instance={self.instance})") + + def __eq__(self, other) -> bool: + """ + Check equality with another I/O object. + + Args: + other: Object to compare with + + Returns: + True if objects are equal, False otherwise + """ + if not isinstance(other, IOObj): + return False + return (self.base == other.base and + self.size == other.size and + self.instance == other.instance) + + def __hash__(self) -> int: + """ + Generate a hash value for the IOObj instance. + + This allows IOObj instances to be used as dictionary keys. + + Returns: + Hash value based on base, size, and instance + """ + # Use instance hash if it implements __hash__, otherwise use id() + instance_hash = hash(self.instance) \ + if hasattr(self.instance, '__hash__') else id(self.instance) + return hash((self.base, self.size, instance_hash)) class IOBarConfig(GenericConfig): - def __init__(self, cfg_obj): - super(IOBarConfig, self).__init__(cfg_obj) - self.device = cfg_obj['device'] - self.register = cfg_obj['register'] - self.base_field = cfg_obj['base_field'] - self.fixed_address = cfg_obj['fixed_address'] if 'fixed_address' in cfg_obj else None - self.mask = cfg_obj['mask'] if 'mask' in cfg_obj else None - self.offset = cfg_obj['offset'] if 'offset' in cfg_obj else None - self.size = cfg_obj['size'] if 'did' in cfg_obj else None - self.enable_field = cfg_obj['enable_field'] if 'enable_field' in cfg_obj else None - self.desc = cfg_obj['desc'] - self.instances = {} - for key in cfg_obj['ids']: - self.add_obj(key) + """ + I/O BAR configuration helper for I/O base address registers. - def add_obj(self, key): - self.instances[key] = IOObj(key) + Manages I/O BAR configurations including register mappings, base fields, + and device instances. + """ - def update_base_address(self, base, instance): - if instance in self.instances: + def __init__(self, cfg_obj: Dict[str, Any]): + """ + Initialize I/O BAR configuration helper. + + Args: + cfg_obj: Configuration object containing I/O BAR-specific fields + + Raises: + IOBarConfigError: If I/O BAR configuration initialization fails + """ + try: + super().__init__(cfg_obj) + + # Required fields + required_fields = ['device', 'register', 'base_field', 'desc'] + missing_fields = [field for field in required_fields + if field not in cfg_obj] + if missing_fields: + raise IOBarConfigError( + f"Missing required I/O BAR fields: {missing_fields}") + + self.device: str = cfg_obj['device'] + self.register: str = cfg_obj['register'] + self.base_field: str = cfg_obj['base_field'] + self.desc: str = cfg_obj['desc'] + + # Optional fields + self.fixed_address: Optional[Union[int, str]] = cfg_obj.get( + 'fixed_address') + self.mask: Optional[Union[int, str]] = cfg_obj.get('mask') + self.offset: Optional[Union[int, str]] = cfg_obj.get('offset') + self.size: Optional[Union[int, str]] = cfg_obj.get('size') + self.enable_field: Optional[str] = cfg_obj.get('enable_field') + + # Initialize instances + self.instances: Dict[Any, IOObj] = {} + if 'ids' in cfg_obj: + for key in cfg_obj['ids']: + self.add_obj(key) + + except Exception as e: + if isinstance(e, (IOBarConfigError, GenericConfigError)): + raise + raise IOBarConfigError( + f"Error initializing I/O BAR configuration: {str(e)}") from e + + def add_obj(self, key) -> None: + """ + Add a new I/O object instance. + + Args: + key: Key identifier for the I/O instance + + Raises: + IOBarConfigError: If I/O object creation fails + """ + try: + self.instances[key] = IOObj(key) + except Exception as e: + raise IOBarConfigError( + f"Error adding I/O object: {str(e)}") from e + + def remove_instance(self, key) -> bool: + """ + Remove an I/O instance by key. + + Args: + key: Key identifier for the instance to remove + + Returns: + True if instance was removed, False if not found + """ + if key in self.instances: + del self.instances[key] + return True + return False + + def update_base_address(self, base: Optional[int], instance) -> None: + """ + Update base address for a specific instance. + + Args: + base: New base address + instance: Instance identifier + + Raises: + IOBarConfigError: If instance not found or update fails + """ + try: + if instance not in self.instances: + raise IOBarConfigError(f"Instance {instance} not found") self.instances[instance].base = base + except Exception as e: + if isinstance(e, IOBarConfigError): + raise + raise IOBarConfigError( + f"Error updating base address: {str(e)}") from e def get_base(self, instance): + """ + Get base address and size for a specific instance. + + Args: + instance: Instance identifier + + Returns: + Tuple of (base_address, size) or (None, 0) if not found + """ if instance in self.instances: - return self.instances[instance].base, self.instances[instance].size + return self.instances[instance].base, self.size else: return (None, 0) + def get_instance_count(self) -> int: + """Get the total number of I/O instances.""" + return len(self.instances) + + def validate_iobar_config(self) -> bool: + """ + Validate I/O BAR-specific configuration. + + Returns: + True if I/O BAR configuration is valid, False otherwise + """ + try: + # Call parent validation first + if not self.validate_config(): + return False + + # Validate required fields + required_attrs = ['device', 'register', 'base_field', 'desc'] + for attr in required_attrs: + value = getattr(self, attr, None) + if not value or not isinstance(value, str): + return False + + # Validate all I/O instances + for inst in self.instances.values(): + if not inst.is_valid(): + return False + + return True + except Exception: + return False + + def get_iobar_summary(self) -> Dict[str, Any]: + """ + Get summary of I/O BAR configuration. + + Returns: + Dictionary with I/O BAR configuration summary + """ + try: + return { + 'name': self.name, + 'device': self.device, + 'register': self.register, + 'base_field': self.base_field, + 'size': self.size, + 'total_instances': len(self.instances), + 'is_valid': self.validate_iobar_config(), + 'config_items': len(self.config) + } + except Exception: + return { + 'name': getattr(self, 'name', 'Unknown'), + 'device': getattr(self, 'device', None), + 'register': getattr(self, 'register', None), + 'base_field': getattr(self, 'base_field', None), + 'size': getattr(self, 'size', None), + 'total_instances': 0, + 'is_valid': False, + 'config_items': 0 + } + def __str__(self) -> str: ret = f'name: {self.name}, device: {self.device}' ret += f', register:{self.register}, base_field:{self.base_field}' diff --git a/chipsec/cfg/parsers/ip/memory.py b/chipsec/cfg/parsers/ip/memory.py index 8928f8fa..92cc4703 100644 --- a/chipsec/cfg/parsers/ip/memory.py +++ b/chipsec/cfg/parsers/ip/memory.py @@ -17,18 +17,198 @@ # Contact information: # chipsec@intel.com -from chipsec.cfg.parsers.ip.generic import GenericConfig +""" +Memory IP Configuration Helper + +Provides memory-specific configuration management functionality for +memory-mapped regions. +""" + +from typing import Dict, Any, Union + +from chipsec.cfg.parsers.ip.generic import GenericConfig, GenericConfigError + + +class MemoryConfigError(GenericConfigError): + """Custom exception for memory configuration errors.""" + pass class MemoryConfig(GenericConfig): - def __init__(self, cfg_obj): - super(MemoryConfig, self).__init__(cfg_obj) - self.access = cfg_obj['access'] - self.address = cfg_obj['address'] - self.limit = cfg_obj['limit'] + """ + Memory configuration helper for memory-mapped IP regions. + + Handles configuration of memory-mapped regions including access + permissions, address ranges, and size limits. + """ + + def __init__(self, cfg_obj: Dict[str, Any]): + """ + Initialize memory configuration helper. + + Args: + cfg_obj: Configuration object containing memory-specific fields + + Raises: + MemoryConfigError: If required memory configuration is missing + or invalid + """ + try: + super().__init__(cfg_obj) + + # Required fields for memory configuration + required_fields = ['access', 'address', 'limit'] + missing_fields = [field for field in required_fields + if field not in cfg_obj] + if missing_fields: + raise MemoryConfigError( + f"Missing required memory configuration fields: " + f"{missing_fields}") + + self.access: str = cfg_obj['access'] + self.address: Union[int, str] = cfg_obj['address'] + self.limit: Union[int, str] = cfg_obj['limit'] + + # Validate configuration after initialization + if not self.validate_memory_config(): + raise MemoryConfigError( + "Invalid memory configuration detected") + + except Exception as e: + if isinstance(e, MemoryConfigError): + raise + raise MemoryConfigError( + f"Error initializing memory configuration: {str(e)}") from e + + def validate_memory_config(self) -> bool: + """ + Validate memory-specific configuration. + + Returns: + True if memory configuration is valid, False otherwise + """ + try: + # Call parent validation first + if not self.validate_config(): + return False + + # Validate access field + if not isinstance(self.access, str) or not self.access.strip(): + return False + + # Validate address and limit (can be int or hex string) + for field_name, field_value in [('address', self.address), + ('limit', self.limit)]: + if isinstance(field_value, str): + # Try to parse as hex if it's a string + try: + int(field_value, + 16 if field_value.startswith('0x') else 10) + except ValueError: + return False + elif not isinstance(field_value, int): + return False + + return True + except Exception: + return False + + def get_address_int(self) -> int: + """ + Get address as integer value. + + Returns: + Address as integer + + Raises: + MemoryConfigError: If address cannot be converted to integer + """ + try: + if isinstance(self.address, int): + return self.address + elif isinstance(self.address, str): + return int(self.address, + 16 if self.address.startswith('0x') else 10) + else: + raise MemoryConfigError( + f"Invalid address type: {type(self.address)}") + except ValueError as e: + raise MemoryConfigError( + f"Cannot convert address to integer: {self.address}") from e + + def get_limit_int(self) -> int: + """ + Get limit as integer value. + + Returns: + Limit as integer + + Raises: + MemoryConfigError: If limit cannot be converted to integer + """ + try: + if isinstance(self.limit, int): + return self.limit + elif isinstance(self.limit, str): + return int(self.limit, + 16 if self.limit.startswith('0x') else 10) + else: + raise MemoryConfigError( + f"Invalid limit type: {type(self.limit)}") + except ValueError as e: + raise MemoryConfigError( + f"Cannot convert limit to integer: {self.limit}") from e + + def get_memory_range(self) -> int: + """ + Calculate the memory range (limit - address). + + Returns: + Memory range size as integer + + Raises: + MemoryConfigError: If range calculation fails + """ + try: + address_int = self.get_address_int() + limit_int = self.get_limit_int() + + if limit_int < address_int: + raise MemoryConfigError( + f"Invalid memory range: limit ({limit_int:x}) < " + f"address ({address_int:x})") + + return limit_int - address_int + except Exception as e: + if isinstance(e, MemoryConfigError): + raise + raise MemoryConfigError( + f"Error calculating memory range: {str(e)}") from e def __str__(self) -> str: - ret = f'name: {self.name}, access: {self.access}' - ret += f', address: {self.address}, limit: {self.limit}' - ret += f', config: {self.config}' - return ret + """Return human-readable string representation.""" + try: + addr_str = (f"0x{self.get_address_int():x}" + if isinstance(self.address, int) + else str(self.address)) + limit_str = (f"0x{self.get_limit_int():x}" + if isinstance(self.limit, int) + else str(self.limit)) + range_size = self.get_memory_range() + + ret = f'name: {self.name}, access: {self.access}' + ret += f', address: {addr_str}, limit: {limit_str}' + ret += f', range: 0x{range_size:x}, config: {len(self.config)} items' + return ret + except Exception: + # Fallback to basic representation if conversion fails + ret = f'name: {self.name}, access: {self.access}' + ret += f', address: {self.address}, limit: {self.limit}' + ret += f', config: {self.config}' + return ret + + def __repr__(self) -> str: + """Return detailed string representation.""" + return (f"MemoryConfig(name='{self.name}', access='{self.access}', " + f"address={self.address}, limit={self.limit}, " + f"config_count={len(self.config)})") diff --git a/chipsec/cfg/parsers/ip/mm_msgbus.py b/chipsec/cfg/parsers/ip/mm_msgbus.py index 885e0cbc..3b863b5f 100644 --- a/chipsec/cfg/parsers/ip/mm_msgbus.py +++ b/chipsec/cfg/parsers/ip/mm_msgbus.py @@ -17,15 +17,136 @@ # Contact information: # chipsec@intel.com +""" +MM_MSGBUS (Memory-Mapped Message Bus) configuration parser. + +This module provides MM_MSGBUSConfig class for parsing and managing memory-mapped message bus configurations +in the CHIPSEC framework. Memory-mapped message buses provide MMIO-based communication interfaces. +""" + +from typing import Dict, Any, Optional from chipsec.cfg.parsers.ip.generic import GenericConfig +from chipsec.library.exceptions import CSConfigError + + +class MM_MSGBUSConfigError(CSConfigError): + """Exception raised for MM_MSGBUS configuration-specific errors.""" + pass class MM_MSGBUSConfig(GenericConfig): - def __init__(self, cfg_obj): - super(MM_MSGBUSConfig, self).__init__(cfg_obj) - self.port = cfg_obj['port'] + """ + MM_MSGBUS (Memory-Mapped Message Bus) configuration parser. + + This class handles parsing and validation of memory-mapped message bus configurations, + extending the base GenericConfig with MM_MSGBUS-specific functionality including port management. + + Attributes: + name (str): The name of the MM_MSGBUS configuration + config (Dict[str, Any]): The raw configuration data + port (Union[int, str]): The memory-mapped message bus port identifier + + Example: + >>> mm_msgbus_cfg = MM_MSGBUSConfig({'name': 'PUNIT_MM_MSGBUS', 'port': 0x04}) + >>> print(mm_msgbus_cfg.port) + 4 + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize MM_MSGBUS configuration. + + Args: + cfg_obj: Dictionary containing MM_MSGBUS configuration data + + Raises: + MM_MSGBUSConfigError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.port = cfg_obj['port'] + self._validate_mm_msgbus_config() + except KeyError as e: + raise MM_MSGBUSConfigError(f"Missing required field in MM_MSGBUS configuration: {e}") from e + except Exception as e: + raise MM_MSGBUSConfigError(f"Failed to initialize MM_MSGBUS configuration: {e}") from e + + def _validate_mm_msgbus_config(self) -> None: + """ + Validate MM_MSGBUS-specific configuration requirements. + + Raises: + MM_MSGBUSConfigError: If configuration is invalid + """ + if not self.name: + raise MM_MSGBUSConfigError("MM_MSGBUS configuration must have a valid name") + + if self.port is None: + raise MM_MSGBUSConfigError("MM_MSGBUS configuration must have a valid port") + + # Validate port format and range + port_int = self.get_port_as_int() + if port_int is None or port_int < 0 or port_int > 0xFF: + raise MM_MSGBUSConfigError(f"Invalid port value: {self.port}. Must be 0-255 range") + + def get_port_as_int(self) -> Optional[int]: + """ + Get the port as an integer value. + + Returns: + Port as integer, or None if conversion fails + """ + try: + if isinstance(self.port, str): + return int(self.port, 16) if self.port.startswith('0x') else int(self.port) + return int(self.port) + except (ValueError, TypeError): + return None + + def get_port_as_hex(self) -> str: + """ + Get the port as a hexadecimal string. + + Returns: + Port as hex string (e.g., '0x04') + """ + port_int = self.get_port_as_int() + return f"0x{port_int:02X}" if port_int is not None else "0x00" + + def is_valid_port(self) -> bool: + """ + Check if the port value is valid. + + Returns: + True if port is valid, False otherwise + """ + port_int = self.get_port_as_int() + return port_int is not None and 0 <= port_int <= 0xFF + + def is_memory_mapped(self) -> bool: + """ + Check if this is a memory-mapped message bus configuration. + + Returns: + True (always, as this is an MM_MSGBUS) + """ + return True def __str__(self) -> str: - ret = f'name: {self.name}, port: {self.port}' - ret += f', config: {self.config}' - return ret + """ + String representation of MM_MSGBUS configuration. + + Returns: + Formatted string with MM_MSGBUS details + """ + port_hex = self.get_port_as_hex() + return f"MM_MSGBUSConfig(name='{self.name}', port={port_hex}, config={self.config})" + + def __repr__(self) -> str: + """ + Detailed string representation for debugging. + + Returns: + Detailed string representation + """ + return f"MM_MSGBUSConfig(name='{self.name}', port={self.port}, config={self.config})" diff --git a/chipsec/cfg/parsers/ip/mmio_bar.py b/chipsec/cfg/parsers/ip/mmio_bar.py index f644940f..80ef7047 100644 --- a/chipsec/cfg/parsers/ip/mmio_bar.py +++ b/chipsec/cfg/parsers/ip/mmio_bar.py @@ -17,95 +17,353 @@ # Contact information: # chipsec@intel.com -from chipsec.cfg.parsers.ip.generic import GenericConfig +""" +MMIO BAR IP Configuration Helper + +Provides MMIO BAR-specific configuration management functionality for +memory-mapped I/O base address registers. +""" + +from typing import Optional, TYPE_CHECKING + +from chipsec.cfg.parsers.ip.generic import GenericConfig, GenericConfigError from chipsec.cfg.parsers.ip.platform import RegisterList +if TYPE_CHECKING: + from chipsec.cfg.parsers.ip.pci_device import PCIObj + + +class MMIOBarConfigError(GenericConfigError): + """Custom exception for MMIO BAR configuration errors.""" + pass + class MMIOObj: + """ + MMIO object representing a memory-mapped I/O region. + + Contains base address, size, and associated PCI instance information. + """ + def __init__(self, instance: 'PCIObj'): - self.base = None - self.size = 0 + """ + Initialize MMIO object. + + Args: + instance: Associated PCI object instance + """ + self.base: Optional[int] = None + self.size: int = 0 self.instance = instance + def set_base_and_size(self, base: Optional[int], size: int) -> None: + """ + Set base address and size for the MMIO region. + + Args: + base: Base address (can be None) + size: Size of the MMIO region + + Raises: + MMIOBarConfigError: If parameters are invalid + """ + try: + if base is not None and not isinstance(base, int): + raise MMIOBarConfigError( + "Base address must be an integer or None") + if not isinstance(size, int) or size < 0: + raise MMIOBarConfigError( + "Size must be a non-negative integer") + + self.base = base + self.size = size + except Exception as e: + if isinstance(e, MMIOBarConfigError): + raise + raise MMIOBarConfigError( + f"Error setting MMIO base and size: {str(e)}") from e + + def is_valid(self) -> bool: + """ + Check if MMIO object is valid. + + Returns: + True if MMIO object has valid configuration + """ + return (self.base is not None and + isinstance(self.size, int) and + self.size >= 0 and + self.instance is not None) + def __str__(self) -> str: + """Return human-readable string representation.""" basestr = f'0x{self.base:X}' if self.base else 'None' - return f'instance: {self.instance}, base: {basestr}' + return (f'instance: {self.instance}, base: {basestr}, ' + f'size: 0x{self.size:X}') + + def __repr__(self) -> str: + """Return detailed string representation.""" + return (f"MMIOObj(base={self.base}, size={self.size}, " + f"instance={self.instance})") + + def __eq__(self, other) -> bool: + """ + Check equality with another MMIO object. + + Args: + other: Object to compare with + + Returns: + True if objects are equal, False otherwise + """ + if not isinstance(other, MMIOObj): + return False + return (self.base == other.base and + self.size == other.size and + self.instance == other.instance) + + def __hash__(self) -> int: + """ + Generate a hash value for the MMIOObj instance. + + This allows MMIOObj instances to be used as dictionary keys. + + Returns: + Hash value based on base, size, and instance + """ + # Use instance hash if it implements __hash__, otherwise use id() + instance_hash = hash(self.instance) \ + if hasattr(self.instance, '__hash__') else id(self.instance) + return hash((self.base, self.size, instance_hash)) class MMIOBarConfig(GenericConfig, RegisterList): + """ + MMIO BAR configuration helper for memory-mapped I/O base address registers. + + Manages MMIO BAR configurations including register mappings, base fields, + size information, and device instances. + """ + def __init__(self, cfg_obj): - GenericConfig.__init__(self, cfg_obj) - RegisterList.__init__(self) - self.device = cfg_obj['device'] if 'device' in cfg_obj else cfg_obj['component'] if 'component' in cfg_obj else None - self.register = cfg_obj['register'] - self.base_field = cfg_obj['base_field'] - self.size = cfg_obj['size'] if 'size' in cfg_obj else None - self.desc = cfg_obj['desc'] if 'desc' in cfg_obj else self.name - self.reg_align = cfg_obj['reg_align'] if 'reg_align' in cfg_obj else None - self.registerh = cfg_obj['registerh'] if 'registerh' in cfg_obj else None - self.reg_alignh = cfg_obj['reg_alignh'] if 'reg_alignh' in cfg_obj else None - self.baseh_field = cfg_obj['baseh_field'] if 'baseh_field' in cfg_obj else None - self.registertype = cfg_obj['registertype'] if 'registertype' in cfg_obj else None - self.offset = cfg_obj['offset'] if 'offset' in cfg_obj else 0 - self.mmio_base = cfg_obj['mmio_base'] if 'mmio_base' in cfg_obj else None - self.mmio_align = cfg_obj['mmio_align'] if 'mmio_align' in cfg_obj else None - self.limit_field = cfg_obj['limit_field'] if 'limit_field' in cfg_obj else None - self.limit_register = cfg_obj['limit_register'] if 'limit_register' in cfg_obj else None - self.limit_align = cfg_obj['limit_align'] if 'limit_align' in cfg_obj else None - self.fixed_address = cfg_obj['fixed_address'] if 'fixed_address' in cfg_obj else None - self.enable_field = cfg_obj['enable_field'] if 'enable_field' in cfg_obj else None - self.enable_bit = cfg_obj['enable_bit'] if 'enable_bit' in cfg_obj else None - self.valid = cfg_obj['valid'] if 'valid' in cfg_obj else None - self.instances = {} - for key in cfg_obj['ids']: - self.add_obj(key) + """ + Initialize MMIO BAR configuration helper. + + Args: + cfg_obj: Configuration object containing MMIO BAR-specific fields + + Raises: + MMIOBarConfigError: If MMIO BAR configuration initialization fails + """ + try: + GenericConfig.__init__(self, cfg_obj) + RegisterList.__init__(self) + + # Required fields + if 'register' not in cfg_obj or 'base_field' not in cfg_obj: + raise MMIOBarConfigError( + "Missing required fields: register and/or base_field") + + # Device field (can be 'device' or 'component') + self.device = (cfg_obj.get('device') or + cfg_obj.get('component')) + + # Core configuration + self.register = cfg_obj['register'] + self.base_field = cfg_obj['base_field'] + self.size = cfg_obj.get('size') + self.desc = cfg_obj.get('desc', self.name) + self.reg_align = cfg_obj.get('reg_align') + self.registerh = cfg_obj.get('registerh') + self.reg_alignh = cfg_obj.get('reg_alignh') + self.baseh_field = cfg_obj.get('baseh_field') + self.registertype = cfg_obj.get('registertype') + self.offset = cfg_obj.get('offset', 0) + self.mmio_base = cfg_obj.get('mmio_base') + self.mmio_align = cfg_obj.get('mmio_align') + self.limit_field = cfg_obj.get('limit_field') + self.limit_register = cfg_obj.get('limit_register') + self.limit_align = cfg_obj.get('limit_align') + self.fixed_address = cfg_obj.get('fixed_address') + self.enable_field = cfg_obj.get('enable_field') + self.enable_bit = cfg_obj.get('enable_bit') + self.valid = cfg_obj.get('valid') + + # Initialize instances + self.instances = {} + if 'ids' in cfg_obj: + for key in cfg_obj['ids']: + self.add_obj(key) + + except Exception as e: + if isinstance(e, (MMIOBarConfigError, GenericConfigError)): + raise + raise MMIOBarConfigError( + f"Error initializing MMIO BAR configuration: {str(e)}") from e def add_obj(self, key): - self.instances[key] = MMIOObj(key) + """ + Add a new MMIO object instance. - def update_base_address(self, base, instance): - if instance in self.instances: + Args: + key: Key identifier for the MMIO instance + + Raises: + MMIOBarConfigError: If MMIO object creation fails + """ + try: + self.instances[key] = MMIOObj(key) + except Exception as e: + raise MMIOBarConfigError( + f"Error adding MMIO object: {str(e)}") from e + + def remove_instance(self, key) -> bool: + """ + Remove an MMIO instance by key. + + Args: + key: Key identifier for the instance to remove + + Returns: + True if instance was removed, False if not found + """ + if key in self.instances: + del self.instances[key] + return True + return False + + def update_base_address(self, base: Optional[int], instance) -> None: + """ + Update base address for a specific instance. + + Args: + base: New base address + instance: Instance identifier + + Raises: + MMIOBarConfigError: If instance not found or update fails + """ + try: + if instance not in self.instances: + raise MMIOBarConfigError(f"Instance {instance} not found") self.instances[instance].base = base + except Exception as e: + if isinstance(e, MMIOBarConfigError): + raise + raise MMIOBarConfigError( + f"Error updating base address: {str(e)}") from e def get_base(self, instance): + """ + Get base address and size for a specific instance. + + Args: + instance: Instance identifier + + Returns: + Tuple of (base_address, size) or (None, 0) if not found + """ if instance in self.instances: return self.instances[instance].base, self.size else: return (None, 0) + def get_instance_count(self) -> int: + """Get the total number of MMIO instances.""" + return len(self.instances) + + def validate_mmio_config(self) -> bool: + """ + Validate MMIO BAR-specific configuration. + + Returns: + True if MMIO BAR configuration is valid, False otherwise + """ + try: + # Call parent validation first + if not self.validate_config(): + return False + + # Validate required fields + if not self.register or not self.base_field: + return False + + # Validate all MMIO instances + for inst in self.instances.values(): + if not inst.is_valid(): + return False + + return True + except Exception: + return False + + def get_mmio_summary(self): + """ + Get summary of MMIO BAR configuration. + + Returns: + Dictionary with MMIO BAR configuration summary + """ + try: + return { + 'name': self.name, + 'device': self.device, + 'register': self.register, + 'base_field': self.base_field, + 'size': self.size, + 'total_instances': len(self.instances), + 'is_valid': self.validate_mmio_config(), + 'config_items': len(self.config) + } + except Exception: + return { + 'name': getattr(self, 'name', 'Unknown'), + 'device': getattr(self, 'device', None), + 'register': getattr(self, 'register', None), + 'base_field': getattr(self, 'base_field', None), + 'size': getattr(self, 'size', None), + 'total_instances': 0, + 'is_valid': False, + 'config_items': 0 + } + def __str__(self) -> str: + """Return human-readable string representation.""" ret = f'name: {self.name}, device: {self.device}' - ret += f', register:{self.register}, base_field:{self.base_field}' - ret += f', size:{self.size}' - ret += f', config: {self.config}' - if self.reg_align: - ret += f', reg_align:{self.reg_align}' - if self.registerh: - ret += f', registerh:{self.registerh}' - if self.reg_alignh: - ret += f', reg_alignh:{self.reg_alignh}' - if self.baseh_field: - ret += f', baseh_field:{self.baseh_field}' - if self.registertype: - ret += f', registertype:{self.registertype}' - if self.mmio_base: - ret += f', mmio_base:{self.mmio_base}' - if self.mmio_align: - ret += f', mmio_align:{self.mmio_align}' - if self.limit_field: - ret += f', limit_field:{self.limit_field}' - if self.limit_register: - ret += f', limit_register:{self.limit_register}' - if self.limit_align: - ret += f', limit_align:{self.limit_align}' - if self.fixed_address: - ret += f', fixed_address:{self.fixed_address}' - if self.enable_field: - ret += f', enable_field:{self.enable_field}' - if self.valid: - ret += f', valid:{self.valid}' - ret += f', desc:{self.desc}' + ret += f', register: {self.register}, base_field: {self.base_field}' + ret += f', size: {self.size}' + ret += f', config: {len(self.config)} items' + + # Add optional fields if present + optional_fields = [ + ('reg_align', self.reg_align), + ('registerh', self.registerh), + ('reg_alignh', self.reg_alignh), + ('baseh_field', self.baseh_field), + ('registertype', self.registertype), + ('mmio_base', self.mmio_base), + ('mmio_align', self.mmio_align), + ('limit_field', self.limit_field), + ('limit_register', self.limit_register), + ('limit_align', self.limit_align), + ('fixed_address', self.fixed_address), + ('enable_field', self.enable_field), + ('valid', self.valid) + ] + + for field_name, field_value in optional_fields: + if field_value: + ret += f', {field_name}: {field_value}' + + ret += f', desc: {self.desc}' ret += ', instances: [' - ret += ' '.join(f'{{{str(inst)}}}' for (_, inst) in self.instances.items()) + ret += ' '.join(f'{{{str(inst)}}}' + for (_, inst) in self.instances.items()) ret += ']' return ret + + def __repr__(self) -> str: + """Return detailed string representation.""" + return (f"MMIOBarConfig(name='{self.name}', device='{self.device}', " + f"register='{self.register}', instances={len(self.instances)}, " + f"config_count={len(self.config)})") diff --git a/chipsec/cfg/parsers/ip/msgbus.py b/chipsec/cfg/parsers/ip/msgbus.py index 63d2ec44..5918d877 100644 --- a/chipsec/cfg/parsers/ip/msgbus.py +++ b/chipsec/cfg/parsers/ip/msgbus.py @@ -17,15 +17,127 @@ # Contact information: # chipsec@intel.com +""" +MSGBUS (Message Bus) configuration parser. + +This module provides MSGBUSConfig class for parsing and managing message bus configurations in the CHIPSEC framework. +Message buses provide communication interfaces between different platform components. +""" + +from typing import Dict, Any, Optional from chipsec.cfg.parsers.ip.generic import GenericConfig +from chipsec.library.exceptions import CSConfigError + + +class MSGBUSConfigError(CSConfigError): + """Exception raised for MSGBUS configuration-specific errors.""" + pass class MSGBUSConfig(GenericConfig): - def __init__(self, cfg_obj): - super(MSGBUSConfig, self).__init__(cfg_obj) - self.port = cfg_obj['port'] + """ + MSGBUS (Message Bus) configuration parser. + + This class handles parsing and validation of message bus configurations, extending + the base GenericConfig with MSGBUS-specific functionality including port management. + + Attributes: + name (str): The name of the MSGBUS configuration + config (Dict[str, Any]): The raw configuration data + port (Union[int, str]): The message bus port identifier + + Example: + >>> msgbus_cfg = MSGBUSConfig({'name': 'PUNIT_MSGBUS', 'port': 0x04}) + >>> print(msgbus_cfg.port) + 4 + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize MSGBUS configuration. + + Args: + cfg_obj: Dictionary containing MSGBUS configuration data + + Raises: + MSGBUSConfigError: If configuration validation fails + """ + try: + super().__init__(cfg_obj) + self.port = cfg_obj['port'] + self._validate_msgbus_config() + except KeyError as e: + raise MSGBUSConfigError(f"Missing required field in MSGBUS configuration: {e}") from e + except Exception as e: + raise MSGBUSConfigError(f"Failed to initialize MSGBUS configuration: {e}") from e + + def _validate_msgbus_config(self) -> None: + """ + Validate MSGBUS-specific configuration requirements. + + Raises: + MSGBUSConfigError: If configuration is invalid + """ + if not self.name: + raise MSGBUSConfigError("MSGBUS configuration must have a valid name") + + if self.port is None: + raise MSGBUSConfigError("MSGBUS configuration must have a valid port") + + # Validate port format and range + port_int = self.get_port_as_int() + if port_int is None or port_int < 0 or port_int > 0xFF: + raise MSGBUSConfigError(f"Invalid port value: {self.port}. Must be 0-255 range") + + def get_port_as_int(self) -> Optional[int]: + """ + Get the port as an integer value. + + Returns: + Port as integer, or None if conversion fails + """ + try: + if isinstance(self.port, str): + return int(self.port, 16) if self.port.startswith('0x') else int(self.port) + return int(self.port) + except (ValueError, TypeError): + return None + + def get_port_as_hex(self) -> str: + """ + Get the port as a hexadecimal string. + + Returns: + Port as hex string (e.g., '0x04') + """ + port_int = self.get_port_as_int() + return f"0x{port_int:02X}" if port_int is not None else "0x00" + + def is_valid_port(self) -> bool: + """ + Check if the port value is valid. + + Returns: + True if port is valid, False otherwise + """ + port_int = self.get_port_as_int() + return port_int is not None and 0 <= port_int <= 0xFF def __str__(self) -> str: - ret = f'name: {self.name}, port: {self.port}' - ret += f', config: {self.config}' - return ret + """ + String representation of MSGBUS configuration. + + Returns: + Formatted string with MSGBUS details + """ + port_hex = self.get_port_as_hex() + return f"MSGBUSConfig(name='{self.name}', port={port_hex}, config={self.config})" + + def __repr__(self) -> str: + """ + Detailed string representation for debugging. + + Returns: + Detailed string representation + """ + return f"MSGBUSConfig(name='{self.name}', port={self.port}, config={self.config})" diff --git a/chipsec/cfg/parsers/ip/msr.py b/chipsec/cfg/parsers/ip/msr.py index 3e21d37a..d478ee40 100644 --- a/chipsec/cfg/parsers/ip/msr.py +++ b/chipsec/cfg/parsers/ip/msr.py @@ -17,14 +17,122 @@ # Contact information: # chipsec@intel.com +""" +MSR (Model Specific Register) configuration parser. + +This module provides MSRConfig class for parsing and managing MSR configurations in the CHIPSEC framework. +MSRs are CPU-specific registers that provide access to processor features and debugging capabilities. +""" + +from typing import Dict, Any, Optional from chipsec.cfg.parsers.ip.generic import GenericConfig +from chipsec.library.exceptions import CSConfigError + + +class MSRConfigError(CSConfigError): + """Exception raised for MSR configuration-specific errors.""" + pass class MSRConfig(GenericConfig): - def __init__(self, cfg_obj): - super(MSRConfig, self).__init__(cfg_obj) + """ + MSR (Model Specific Register) configuration parser. + + This class handles parsing and validation of MSR configurations, extending + the base GenericConfig with MSR-specific functionality. + + Attributes: + name (str): The name of the MSR configuration + config (Dict[str, Any]): The raw configuration data + + Example: + >>> msr_cfg = MSRConfig({'name': 'IA32_MTRR_CAP', 'address': 0xFE}) + >>> print(msr_cfg.name) + IA32_MTRR_CAP + """ + + def __init__(self, cfg_obj: Dict[str, Any]) -> None: + """ + Initialize MSR configuration. + + Args: + cfg_obj: Dictionary containing MSR configuration data + + Raises: + MSRConfigError: If configuration validation fails + """ + try: + self._cfg_obj = cfg_obj # Store original config for reference + super().__init__(cfg_obj) + self._validate_msr_config() + except Exception as e: + raise MSRConfigError(f"Failed to initialize MSR configuration: {e}") from e + + def _validate_msr_config(self) -> None: + """ + Validate MSR-specific configuration requirements. + + Raises: + MSRConfigError: If configuration is invalid + """ + if not self.name: + raise MSRConfigError("MSR configuration must have a valid name") + + # MSRs typically should have an address or some identifier + # Check both in config dict and as top-level attributes + has_address = ('address' in self.config or 'msr' in self.config or + hasattr(self, 'address') or hasattr(self, 'msr')) + if not has_address: + # This is a warning rather than an error for backward compatibility + pass + + def get_address(self) -> Optional[int]: + """ + Get the MSR address. + + Returns: + MSR address as integer, or None if not specified + """ + # First check in the original cfg_obj if available + if hasattr(self, '_cfg_obj'): + address = self._cfg_obj.get('address') or self._cfg_obj.get('msr') + else: + # Fallback to checking if we have direct attributes + address = getattr(self, 'address', None) or getattr(self, 'msr', None) + + if address is not None: + try: + return int(address, 16) if isinstance(address, str) else int(address) + except (ValueError, TypeError): + return None + return None + + def is_valid_address(self) -> bool: + """ + Check if the MSR has a valid address. + + Returns: + True if address is valid, False otherwise + """ + address = self.get_address() + return address is not None and 0 <= address <= 0xFFFFFFFF def __str__(self) -> str: - ret = f'name: {self.name}' - ret += f', config: {self.config}' - return ret + """ + String representation of MSR configuration. + + Returns: + Formatted string with MSR details + """ + address = self.get_address() + address_str = f"0x{address:X}" if address is not None else "N/A" + return f"MSRConfig(name='{self.name}', address={address_str}, config={self.config})" + + def __repr__(self) -> str: + """ + Detailed string representation for debugging. + + Returns: + Detailed string representation + """ + return f"MSRConfig(name='{self.name}', config={self.config})" diff --git a/chipsec/cfg/parsers/ip/pci_device.py b/chipsec/cfg/parsers/ip/pci_device.py index ed143b49..f3fcdd53 100644 --- a/chipsec/cfg/parsers/ip/pci_device.py +++ b/chipsec/cfg/parsers/ip/pci_device.py @@ -17,67 +17,379 @@ # Contact information: # chipsec@intel.com -from chipsec.cfg.parsers.ip.generic import GenericConfig +""" +PCI Device IP Configuration Helper + +Provides PCI device-specific configuration management functionality for +PCI-based IP parsers. +""" + +from typing import Dict, Any, Optional, List, Union + +from chipsec.cfg.parsers.ip.generic import GenericConfig, GenericConfigError + + +class PCIConfigError(GenericConfigError): + """Custom exception for PCI configuration errors.""" + pass + class PCIObj: - def __init__(self, cfg_obj): - self.bus = cfg_obj['bus'] - self.dev = cfg_obj['dev'] - self.fun = cfg_obj['fun'] - self.rid = cfg_obj['rid'] if 'rid' in cfg_obj else 0xff + """ + PCI device object representing a single PCI device instance. + + Contains bus, device, function, and revision ID information for a + PCI device. + """ + + def __init__(self, cfg_obj: Dict[str, Any]): + """ + Initialize PCI device object. + + Args: + cfg_obj: Configuration object containing PCI device information + + Raises: + PCIConfigError: If required PCI configuration is missing + """ + try: + required_fields = ['bus', 'dev', 'fun'] + missing_fields = [field for field in required_fields + if field not in cfg_obj] + if missing_fields: + raise PCIConfigError( + f"Missing required PCI fields: {missing_fields}") + + self.bus: Union[int, str] = cfg_obj['bus'] + self.dev: Union[int, str] = cfg_obj['dev'] + self.fun: Union[int, str] = cfg_obj['fun'] + self.rid: Union[int, str] = cfg_obj.get('rid', 0xff) + + except Exception as e: + if isinstance(e, PCIConfigError): + raise + raise PCIConfigError( + f"Error initializing PCI object: {str(e)}") from e + + def get_bdf_tuple(self) -> tuple: + """ + Get Bus:Device:Function as a tuple. + + Returns: + Tuple of (bus, dev, fun) + """ + return (self.bus, self.dev, self.fun) + + def get_bdf_string(self) -> str: + """ + Get Bus:Device:Function as a formatted string. + + Returns: + BDF string in format "bus:dev.fun" + """ + return f"{self.bus:02x}:{self.dev:02x}.{self.fun}" + + def validate_pci_obj(self) -> bool: + """ + Validate PCI object configuration. + + Returns: + True if PCI object is valid, False otherwise + """ + try: + # Check that all fields are present and valid + for field_name, field_value in [('bus', self.bus), + ('dev', self.dev), + ('fun', self.fun), + ('rid', self.rid)]: + if isinstance(field_value, str): + try: + int(field_value, + 16 if field_value.startswith('0x') else 10) + except ValueError: + return False + elif not isinstance(field_value, int): + return False + return True + except Exception: + return False def __str__(self) -> str: + """Return human-readable string representation.""" ret = f'bus: {self.bus}, dev: {self.dev}, func: {self.fun}' - ret += f', rid:{self.rid}' + ret += f', rid: {self.rid}' return ret - - def __repr__(self) -> str: - return str(self) + def __repr__(self) -> str: + """Return detailed string representation.""" + return (f"PCIObj(bus={self.bus}, dev={self.dev}, " + f"fun={self.fun}, rid={self.rid})") + + def __eq__(self, other) -> bool: + """Check equality with another PCI object.""" + if not isinstance(other, PCIObj): + return False + return (self.bus == other.bus and self.dev == other.dev and + self.fun == other.fun and self.rid == other.rid) + + def __hash__(self) -> int: + """ + Generate a hash value for the PCIObj instance. + + This allows PCIObj instances to be used as dictionary keys. + + Returns: + Hash value based on bus, device, function, and revision ID + """ + # Convert fields to integers for consistent hashing + bus = self._convert_to_int(self.bus) + dev = self._convert_to_int(self.dev) + fun = self._convert_to_int(self.fun) + rid = self._convert_to_int(self.rid) + + return hash((bus, dev, fun, rid)) + + def _convert_to_int(self, value: Union[int, str]) -> int: + """ + Convert a value to integer for hashing purposes. + + Args: + value: Value to convert (int or str) + + Returns: + Integer representation of the value + """ + if isinstance(value, str): + base = 16 if value.startswith('0x') else 10 + return int(value, base) + return value class PCIConfig(GenericConfig): - def __init__(self, cfg_obj): - self.did = cfg_obj['did'] if 'did' in cfg_obj else None - if 'name' not in cfg_obj: - cfg_obj['name'] = self.did - super(PCIConfig, self).__init__(cfg_obj) - self.instances = {} - self.component = cfg_obj['component'] if 'component' in cfg_obj else None - self.__instCounter = 0 - self.add_obj(cfg_obj) + """ + PCI configuration helper for PCI device-based IP regions. - def add_obj(self, cfg_obj): - self.instances[self.__instCounter] = PCIObj(cfg_obj) - self.__instCounter += 1 + Manages PCI device configurations including device IDs, components, + and multiple device instances. + """ - def get_rid(self, bus, dev, fun): + def __init__(self, cfg_obj: Dict[str, Any]): + """ + Initialize PCI configuration helper. + + Args: + cfg_obj: Configuration object containing PCI-specific fields + + Raises: + PCIConfigError: If PCI configuration initialization fails + """ + try: + # Handle device ID and name + self.did: Optional[Union[int, str]] = cfg_obj.get('did', None) + if 'name' not in cfg_obj and self.did is not None: + cfg_obj['name'] = str(self.did) + + super().__init__(cfg_obj) + + self.instances: Dict[int, PCIObj] = {} + self.component: Optional[str] = cfg_obj.get('component', None) + self.__instCounter: int = 0 + + # Add the initial PCI object + self.add_obj(cfg_obj) + + except Exception as e: + if isinstance(e, (PCIConfigError, GenericConfigError)): + raise + raise PCIConfigError( + f"Error initializing PCI configuration: {str(e)}") from e + + def add_obj(self, cfg_obj: Dict[str, Any]) -> int: + """ + Add a new PCI object instance. + + Args: + cfg_obj: Configuration object for the PCI device + + Returns: + Instance counter for the added object + + Raises: + PCIConfigError: If PCI object creation fails + """ + try: + pci_obj = PCIObj(cfg_obj) + self.instances[self.__instCounter] = pci_obj + current_counter = self.__instCounter + self.__instCounter += 1 + return current_counter + except Exception as e: + raise PCIConfigError( + f"Error adding PCI object: {str(e)}") from e + + def remove_instance(self, instance_id: int) -> bool: + """ + Remove a PCI instance by ID. + + Args: + instance_id: Instance ID to remove + + Returns: + True if instance was removed, False if not found + """ + if instance_id in self.instances: + del self.instances[instance_id] + return True + return False + + def get_rid(self, bus: Union[int, str], dev: Union[int, str], + fun: Union[int, str]) -> Union[int, str]: + """ + Get revision ID for a specific Bus:Device:Function. + + Args: + bus: PCI bus number + dev: PCI device number + fun: PCI function number + + Returns: + Revision ID if found, 0xff otherwise + """ rid = 0xff for inst in self.instances.values(): if inst.bus == bus and inst.dev == dev and inst.fun == fun: rid = inst.rid break return rid - - def get_enabled_instances(self) -> bool: + + def get_enabled_instances(self) -> List[PCIObj]: + """ + Get list of enabled PCI instances (those with valid bus numbers). + + Returns: + List of enabled PCI objects + """ enabled = [] for inst in self.instances.values(): if inst.bus is not None: enabled.append(inst) return enabled + def get_instance_count(self) -> int: + """Get the total number of PCI instances.""" + return len(self.instances) - def update_name(self, name): - self.name = name + def find_instance_by_bdf(self, bus: Union[int, str], + dev: Union[int, str], + fun: Union[int, str]) -> Optional[PCIObj]: + """ + Find PCI instance by Bus:Device:Function. + + Args: + bus: PCI bus number + dev: PCI device number + fun: PCI function number + + Returns: + PCIObj if found, None otherwise + """ + for inst in self.instances.values(): + if inst.bus == bus and inst.dev == dev and inst.fun == fun: + return inst + return None + + def validate_pci_config(self) -> bool: + """ + Validate PCI-specific configuration. + + Returns: + True if PCI configuration is valid, False otherwise + """ + try: + # Call parent validation first + if not self.validate_config(): + return False + + # Validate all PCI instances + for inst in self.instances.values(): + if not inst.validate_pci_obj(): + return False + + # Validate component if present + if self.component is not None and not isinstance(self.component, str): + return False + + return True + except Exception: + return False + + def update_name(self, name: str) -> None: + """ + Update the configuration name. + + Args: + name: New name for the configuration + + Raises: + PCIConfigError: If name update fails + """ + try: + if not isinstance(name, str) or not name.strip(): + raise PCIConfigError("Name must be a non-empty string") + self.name = name + except Exception as e: + if isinstance(e, PCIConfigError): + raise + raise PCIConfigError( + f"Error updating name: {str(e)}") from e + + def get_pci_summary(self) -> Dict[str, Any]: + """ + Get summary of PCI configuration. + + Returns: + Dictionary with PCI configuration summary + """ + try: + enabled_instances = self.get_enabled_instances() + return { + 'name': self.name, + 'did': self.did, + 'component': self.component, + 'total_instances': len(self.instances), + 'enabled_instances': len(enabled_instances), + 'is_valid': self.validate_pci_config(), + 'config_items': len(self.config) + } + except Exception: + return { + 'name': getattr(self, 'name', 'Unknown'), + 'did': getattr(self, 'did', None), + 'component': getattr(self, 'component', None), + 'total_instances': 0, + 'enabled_instances': 0, + 'is_valid': False, + 'config_items': 0 + } def __str__(self) -> str: + """Return human-readable string representation.""" if self.did: - ret = f'name:{self.name}, did:{self.did:04X}' + if isinstance(self.did, int): + ret = f'name: {self.name}, did: {self.did:04X}' + else: + ret = f'name: {self.name}, did: {self.did}' else: - ret = f'name:{self.name}, did:{self.did}' + ret = f'name: {self.name}, did: {self.did}' ret += f', component: {self.component}' - ret += f', config: {self.config}' + ret += f', config: {len(self.config)} items' ret += ', instances: [' ret += ' '.join(f'{{{str(inst)}}}' for inst in self.instances.values()) ret += ']' return ret + + def __repr__(self) -> str: + """Return detailed string representation.""" + return (f"PCIConfig(name='{self.name}', did={self.did}, " + f"component='{self.component}', instances={len(self.instances)}, " + f"config_count={len(self.config)})") diff --git a/chipsec/cfg/parsers/ip/platform.py b/chipsec/cfg/parsers/ip/platform.py index 7f34c32b..3f4749c3 100644 --- a/chipsec/cfg/parsers/ip/platform.py +++ b/chipsec/cfg/parsers/ip/platform.py @@ -17,51 +17,156 @@ # Contact information: # chipsec@intel.com -from chipsec.library.exceptions import CSConfigError, RegisterNotFoundError, ScopeNotFoundError, NonRegisterInScopeError, BARNotFoundError -from chipsec.library.register import BaseConfigRegisterHelper, ObjList -from chipsec.library.logger import logger +""" +Platform configuration parser and hierarchy management. + +This module provides classes for managing platform configurations in a hierarchical structure: +Platform -> Vendor -> IP -> Bar -> Register. It supports pattern matching, scoping, +and register access across the configuration hierarchy. +""" + +from typing import List, Union, Any from re import match -class Recursable: - def _get_next_level_list(self) -> list: - raise NotImplementedError('get_next_level_list() not implemented') - - def get_next_levels(self, key): - key = key.replace('*', '.*') - next_options = [] - next_options_list = [key] if '*' not in key and key in self._get_next_level_list() else self._get_next_level_list() - for option in next_options_list: - if match(key, option): - next_option = self._get_next_level(option) - if isinstance(next_option, list): - next_options.extend(next_option) - else: - next_options.append(next_option) - return next_options +from chipsec.library.exceptions import ( + CSConfigError, RegisterNotFoundError, ScopeNotFoundError, BARNotFoundError +) +from chipsec.library.register import ObjList +from chipsec.library.logger import logger + + +class PlatformConfigError(CSConfigError): + """Exception raised for platform configuration-specific errors.""" + pass + + +class Recursable: + """ + Base class for objects that support recursive navigation and pattern matching. + + This class provides the foundation for hierarchical traversal of platform configurations, + supporting wildcard matching and recursive object discovery. + """ + + def _get_next_level_list(self) -> List[str]: + """ + Get list of available keys at the next level. + + Returns: + List of string keys for the next hierarchy level + + Raises: + NotImplementedError: Must be implemented by subclasses + """ + raise NotImplementedError('_get_next_level_list() not implemented') + + def get_next_levels(self, key: str) -> List['Recursable']: + """ + Get objects matching the given key pattern. + + Args: + key: Key pattern, supports wildcards (*) + + Returns: + List of matching objects at the next level + """ + key = key.replace('*', '.*') + next_options = [] + + # Determine search scope + if '*' not in key and key in self._get_next_level_list(): + next_options_list = [key] + else: + next_options_list = self._get_next_level_list() + + # Find matching options + for option in next_options_list: + if match(key, option): + next_option = self._get_next_level(option) + if isinstance(next_option, list): + next_options.extend(next_option) + else: + next_options.append(next_option) + + return next_options + + def _get_next_level(self, key: str) -> Union['Recursable', List['Recursable']]: + """ + Get the object(s) at the next level for a specific key. + + Args: + key: Specific key to retrieve + + Returns: + Object or list of objects at the next level + + Raises: + NotImplementedError: Must be implemented by subclasses + """ + raise NotImplementedError("_get_next_level() not implemented") + - def _get_next_level(self): - raise NotImplementedError("get_next_level() not implemented") - class RegisterList: - def __init__(self): + """ + Container for managing register objects with pattern matching support. + + This class provides functionality to add, retrieve, and search for registers + using both exact names and wildcard patterns. + """ + + def __init__(self) -> None: + """Initialize empty register list.""" self.register_list = {} - def add_register(self, register_name, register_object): + def add_register(self, register_name: str, register_object: Any) -> None: + """ + Add a register to the list. + + Args: + register_name: Name of the register + register_object: Register object to add + """ self.register_list[register_name] = register_object self.__setattr__(register_name, register_object) - def get_register(self, register_name): + def get_register(self, register_name: str) -> ObjList: + """ + Get a specific register by name. + + Args: + register_name: Name of the register to retrieve + + Returns: + ObjList containing the register + + Raises: + RegisterNotFoundError: If register not found + """ if register_name in self.register_list: return ObjList(self.__getattribute__(register_name)) else: raise RegisterNotFoundError(f'Invalid register name: {register_name}') - - def get_register_matches(self, register_name): + + def get_register_matches(self, register_name: str) -> ObjList: + """ + Get registers matching a pattern. + + Args: + register_name: Register name pattern (supports wildcards) + + Returns: + ObjList containing matching registers + + Raises: + RegisterNotFoundError: If no matches found + """ registers = ObjList() reg_name = register_name.replace('*', '.*') + for reg in self.register_list: if match(reg_name, reg): registers.extend(self.get_register(reg)) + if registers: return registers else: @@ -69,48 +174,125 @@ class RegisterList: class Platform(Recursable): - def __init__(self): + """ + Top-level platform configuration container. + + This class manages vendors and provides hierarchical access to the entire + platform configuration structure. It supports complex scope resolution + and pattern matching across the configuration hierarchy. + """ + + def __init__(self) -> None: + """Initialize empty platform.""" + super().__init__() self.vendor_list = [] - - def add_vendor(self, vendor): - if isinstance(vendor, Vendor): - self.vendor_list.append(vendor.name) - self.__setattr__(f'_{vendor.name}', vendor) - else: - raise CSConfigError(f'Invalid vendor object: {vendor}') - - def get_vendor(self, vendor_name) -> 'Vendor': + + def add_vendor(self, vendor: 'Vendor') -> None: + """ + Add a vendor to the platform. + + Args: + vendor: Vendor object to add + + Raises: + PlatformConfigError: If vendor object is invalid + """ + if not isinstance(vendor, Vendor): + raise PlatformConfigError(f'Invalid vendor object: {vendor}') + + self.vendor_list.append(vendor.name) + self.__setattr__(f'_{vendor.name}', vendor) + + def get_vendor(self, vendor_name: str) -> 'Vendor': + """ + Get a vendor by name. + + Args: + vendor_name: Name of the vendor + + Returns: + Vendor object + + Raises: + PlatformConfigError: If vendor not found + """ if vendor_name in self.vendor_list: return self.__getattribute__(f'_{vendor_name}') else: - raise CSConfigError(f'Invalid vendor name: {vendor_name}') - - def remove_vendor(self, vendor): - if isinstance(vendor, Vendor) and vendor in self.vendor_list: - self.vendor_list.remove(vendor.name) - self.__delattr__(f'_{vendor.name}') - else: - raise CSConfigError(f'Invalid vendor object: {vendor}') - - def _get_next_level_list(self): + raise PlatformConfigError(f'Invalid vendor name: {vendor_name}') + + def remove_vendor(self, vendor: 'Vendor') -> None: + """ + Remove a vendor from the platform. + + Args: + vendor: Vendor object to remove + + Raises: + PlatformConfigError: If vendor object is invalid + """ + if not isinstance(vendor, Vendor) or vendor.name not in self.vendor_list: + raise PlatformConfigError(f'Invalid vendor object: {vendor}') + + self.vendor_list.remove(vendor.name) + self.__delattr__(f'_{vendor.name}') + + def _get_next_level_list(self) -> List[str]: + """Get list of vendor names.""" return self.vendor_list - - def _get_next_level(self, name): + + def _get_next_level(self, name: str) -> 'Vendor': + """Get vendor by name.""" return self.get_vendor(name) - - def get_obj_from_fullname(self, full_name: str): + + def get_obj_from_fullname(self, full_name: str) -> Any: + """ + Get object from full dotted name. + + Args: + full_name: Dotted name path (e.g., 'vendor.ip.bar.register') + + Returns: + Object at the specified path + """ return self.get_obj_from_scope(full_name.split('.')) - def get_obj_from_scope(self, scope: list): + def get_obj_from_scope(self, scope: List[str]) -> Any: + """ + Get object from scope list. + + Args: + scope: List of scope components + + Returns: + Object at the specified scope + + Raises: + ScopeNotFoundError: If scope contains wildcards or is invalid + """ logger().log_debug(f'Getting obj from scope: {scope}') if any('*' in s for s in scope): - raise Exception(f'Invalid scope: {scope}. No wildcards allowed for this function.') + raise ScopeNotFoundError(f'Invalid scope: {scope}. No wildcards allowed for this function.') return Platform._get_obj_from_split_scope(self, scope) @staticmethod - def _get_obj_from_split_scope(obj, scope: list): + def _get_obj_from_split_scope(obj: Any, scope: List[str]) -> Any: + """ + Recursively traverse scope to find object. + + Args: + obj: Current object in traversal + scope: Remaining scope components + + Returns: + Object at the end of scope traversal + + Raises: + ScopeNotFoundError: If scope cannot be resolved + """ if not scope: raise ScopeNotFoundError(f'Scope {scope} on obj {obj} was not found') + root_scope = scope.pop(0) next_level = obj._get_next_level(root_scope) @@ -118,17 +300,49 @@ class Platform(Recursable): return next_level return Platform._get_obj_from_split_scope(next_level, scope) - def get_matches_from_fullname(self, full_name: str): + def get_matches_from_fullname(self, full_name: str) -> List[Any]: + """ + Get matching objects from full dotted name with wildcards. + + Args: + full_name: Dotted name path with wildcards (e.g., 'vendor.*.register') + + Returns: + List of matching objects + """ return self.get_matches_from_scope(full_name.split('.')) - def get_matches_from_scope(self, scope: list): + def get_matches_from_scope(self, scope: List[str]) -> List[Any]: + """ + Get matching objects from scope list with wildcards. + + Args: + scope: List of scope components with wildcards + + Returns: + List of matching objects + """ logger().log_debug(f'Getting matches from scope: {scope}') return Platform._get_matches_from_split_scope([self], scope) @staticmethod - def _get_matches_from_split_scope(objs: list, scope: list): + def _get_matches_from_split_scope(objs: List[Any], scope: List[str]) -> List[Any]: + """ + Recursively find matching objects in scope. + + Args: + objs: Current objects in traversal + scope: Remaining scope components + + Returns: + List of matching objects + + Raises: + ScopeNotFoundError: If scope cannot be resolved + """ if not scope: raise ScopeNotFoundError(f'Scope {scope} on objs: {objs} was not found') + root_scope = scope.pop(0) next_level_list = [] for obj in objs: @@ -137,20 +351,55 @@ class Platform(Recursable): if len(scope) == 0: return next_level_list return Platform._get_matches_from_split_scope(next_level_list, scope) - - def get_register_from_fullname(self, full_name: str): + + def get_register_from_fullname(self, full_name: str) -> ObjList: + """ + Get register from full dotted name. + + Args: + full_name: Dotted name path to register + + Returns: + Register object list + """ return self.get_register_from_scope(full_name.split('.')) - def get_register_from_scope(self, scope: str): + def get_register_from_scope(self, scope: List[str]) -> ObjList: + """ + Get register from scope list. + + Args: + scope: List of scope components to register + + Returns: + Register object list + + Raises: + ScopeNotFoundError: If scope contains wildcards or is invalid + """ logger().log_debug(f'Getting register from scope: {scope}') if any('*' in s for s in scope): - raise Exception(f'Invalid scope: {scope}. No wildcards allowed for this function.') + raise ScopeNotFoundError(f'Invalid scope: {scope}. No wildcards allowed for this function.') return Platform._get_register_from_split_scope(self, scope) - + @staticmethod - def _get_register_from_split_scope(obj, scope: list): + def _get_register_from_split_scope(obj: Any, scope: List[str]) -> ObjList: + """ + Recursively traverse scope to find register. + + Args: + obj: Current object in traversal + scope: Remaining scope components + + Returns: + Register object list + + Raises: + ScopeNotFoundError: If scope cannot be resolved + """ if not scope: raise ScopeNotFoundError(f'Scope {scope} on obj {obj} was not found') + root_scope = scope.pop(0) if len(scope) == 0: return obj.get_register(root_scope) @@ -159,18 +408,50 @@ class Platform(Recursable): return Platform._get_register_from_split_scope(next_level, scope) - def get_register_matches_from_fullname(self, full_name: str): + def get_register_matches_from_fullname(self, full_name: str) -> ObjList: + """ + Get matching registers from full dotted name with wildcards. + + Args: + full_name: Dotted name path with wildcards + + Returns: + List of matching register objects + """ return self.get_register_matches_from_scope(full_name.split('.')) - def get_register_matches_from_scope(self, scope: list): + def get_register_matches_from_scope(self, scope: List[str]) -> ObjList: + """ + Get matching registers from scope list with wildcards. + + Args: + scope: List of scope components with wildcards + + Returns: + List of matching register objects + """ logger().log_debug(f'Getting registers from matchscope: {scope}') objects = Platform._get_register_matches_from_split_scope([self], scope) return ObjList(objects) - + @staticmethod - def _get_register_matches_from_split_scope(objs: list, scope: list): + def _get_register_matches_from_split_scope(objs: List[Any], scope: List[str]) -> List[Any]: + """ + Recursively find matching registers in scope. + + Args: + objs: Current objects in traversal + scope: Remaining scope components + + Returns: + List of matching register objects + + Raises: + ScopeNotFoundError: If scope cannot be resolved + """ if not scope: raise ScopeNotFoundError(f'Scope {scope} on objs: {objs} was not found') + root_scope = scope.pop(0) next_level_list = [] if len(scope) == 0: @@ -184,70 +465,178 @@ class Platform(Recursable): return Platform._get_register_matches_from_split_scope(next_level_list, scope) - - class Vendor(Recursable): - def __init__(self, name): + """ + Vendor configuration container. + + This class manages IP configurations for a specific vendor and provides + hierarchical access to vendor-specific platform components. + """ + + def __init__(self, name: str) -> None: + """ + Initialize vendor configuration. + + Args: + name: Name of the vendor + """ + super().__init__() self.ip_list = [] self.name = name - def add_ip(self, ip_name: str, obj): + def add_ip(self, ip_name: str, obj: Any) -> None: + """ + Add an IP configuration to the vendor. + + Args: + ip_name: Name of the IP + obj: IP configuration object + """ ip = IP(ip_name, obj) self.ip_list.append(ip.name) self.__setattr__(ip.name, ip) - - def get_ip(self, ip_name) -> 'IP': + + def get_ip(self, ip_name: str) -> 'IP': + """ + Get an IP configuration by name. + + Args: + ip_name: Name of the IP + + Returns: + IP configuration object + + Raises: + PlatformConfigError: If IP not found + """ if ip_name in self.ip_list: return self.__getattribute__(ip_name) else: - raise CSConfigError(f'Device: {ip_name} not found in Vendor: {self.name}') - - def _get_next_level_list(self): + raise PlatformConfigError(f'Device: {ip_name} not found in Vendor: {self.name}') + + def _get_next_level_list(self) -> List[str]: + """Get list of IP names.""" return self.ip_list - - def _get_next_level(self, ip_name): + + def _get_next_level(self, ip_name: str) -> 'IP': + """Get IP by name.""" return self.get_ip(ip_name) + class IP(Recursable, RegisterList): - def __init__(self, name, ipobj): + """ + IP (Intellectual Property) configuration container. + + This class manages BAR configurations and registers for a specific IP block, + providing hierarchical access to IP-specific components. + """ + + def __init__(self, name: str, ipobj: Any) -> None: + """ + Initialize IP configuration. + + Args: + name: Name of the IP + ipobj: IP configuration object + """ + super().__init__() RegisterList.__init__(self) self.bar_list = [] self.name = name self.obj = ipobj - def add_bar(self, bar_name: str, barobj): + def add_bar(self, bar_name: str, barobj: Any) -> None: + """ + Add a BAR configuration to the IP. + + Args: + bar_name: Name of the BAR + barobj: BAR configuration object + """ bar = Bar(bar_name, barobj) self.bar_list.append(bar.name) self.__setattr__(f'{bar.name}_', bar) - - def get_bar(self, bar_name: str): + + def get_bar(self, bar_name: str) -> 'Bar': + """ + Get a BAR configuration by name. + + Args: + bar_name: Name of the BAR + + Returns: + BAR configuration object + + Raises: + BARNotFoundError: If BAR not found + """ if bar_name in self.bar_list: return self.__getattribute__(f'{bar_name}_') else: raise BARNotFoundError(f'Bar: {bar_name} not found in IP: {self.name}') - - def _get_next_level_list(self): + + def _get_next_level_list(self) -> List[str]: + """Get list of BAR names.""" return self.bar_list - - def _get_next_level(self, id): - if id in self._get_next_level_list(): - return self.get_bar(id) + + def _get_next_level(self, bar_id: str) -> 'Bar': + """ + Get next level object (BAR) by ID. + + Args: + bar_id: BAR identifier + + Returns: + BAR object + + Raises: + PlatformConfigError: If BAR not found + """ + if bar_id in self._get_next_level_list(): + return self.get_bar(bar_id) else: - raise CSConfigError(f'Next Level: {id} not found in in IP: {self.name}') - + raise PlatformConfigError(f'Next Level: {bar_id} not found in IP: {self.name}') + class Bar(Recursable, RegisterList): - def __init__(self, name, barobj): + """ + BAR (Base Address Register) configuration container. + + This class manages registers within a specific BAR and provides + hierarchical access to BAR-specific registers. + """ + + def __init__(self, name: str, barobj: Any) -> None: + """ + Initialize BAR configuration. + + Args: + name: Name of the BAR + barobj: BAR configuration object + """ + super().__init__() RegisterList.__init__(self) self.name = name self.obj = barobj - def _get_next_level_list(self): - return self.register_list - - def _get_next_level(self, id): - if id in self.register_list.keys(): - return self.get_register(id) + def _get_next_level_list(self) -> List[str]: + """Get list of register names.""" + return list(self.register_list.keys()) + + def _get_next_level(self, register_id: str) -> ObjList: + """ + Get next level object (register) by ID. + + Args: + register_id: Register identifier + + Returns: + Register object list + + Raises: + PlatformConfigError: If register not found + """ + if register_id in self.register_list.keys(): + return self.get_register(register_id) else: - raise CSConfigError(f'Bar: {id} not found in in IP: {self.name}') - \ No newline at end of file + raise PlatformConfigError(f'Register: {register_id} not found in BAR: {self.name}')