mirror of
https://github.com/chipsec/chipsec
synced 2026-06-08 13:31:00 +00:00
3dfc3c27b0
Signed-off-by: brentholtsclaw <brent.holtsclaw@intel.com>
430 lines
18 KiB
Python
430 lines
18 KiB
Python
# !/usr/bin/python
|
|
# CHIPSEC: Platform Security Assessment Framework
|
|
# Copyright (c) 2021, Intel Corporation
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; Version 2.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# MA 02110-1301, USA.
|
|
|
|
# Contact information:
|
|
# chipsec@intel.com
|
|
|
|
"""
|
|
>>> chipsec_util config show [config] <name>
|
|
|
|
Examples:
|
|
|
|
>>> chipsec_util config show ALL
|
|
>>> chipsec_util config show [CONFIG_PCI|MEMORY_RANGES|MM_MSGBUS|MSGBUS|IO|MSR]
|
|
>>> chipsec_util config show SCOPE
|
|
"""
|
|
|
|
from argparse import ArgumentParser
|
|
|
|
from chipsec.command import BaseCommand, toLoad
|
|
|
|
|
|
class CONFIGCommand(BaseCommand):
|
|
"""Command to display configuration information."""
|
|
|
|
# Initialize command
|
|
def __init__(self, argv, cs):
|
|
"""Initialize the command with base class and set options."""
|
|
super(CONFIGCommand, self).__init__(argv, cs)
|
|
self.skip_list = ["LOCKS", "CONTROLS", "ALL"]
|
|
try:
|
|
# Get parent keys from scope manager
|
|
self.all_options = (
|
|
self.cs.Cfg.scope_manager.parent_keys +
|
|
self.skip_list + ["SCOPE"]
|
|
)
|
|
except (AttributeError, TypeError):
|
|
# Fallback if scope_manager is not available
|
|
self.all_options = [
|
|
"CONFIG_PCI_RAW", "CONFIG_PCI", "MEMORY_RANGES", "MM_MSGBUS",
|
|
"MSGBUS", "IO", "MSR", "MMIO_BARS", "IO_BARS", "LOCKS",
|
|
"CONTROLS", "ALL", "SCOPE"
|
|
]
|
|
self.child_details = False # Default to not showing child details
|
|
|
|
# Command setup and infrastructure methods
|
|
def requirements(self):
|
|
"""Specify resource requirements for this command."""
|
|
return toLoad.All
|
|
|
|
def parse_arguments(self):
|
|
"""Parse command line arguments."""
|
|
parser = ArgumentParser(usage='chipsec_util config')
|
|
subparsers = parser.add_subparsers()
|
|
|
|
# Show command
|
|
parser_show = subparsers.add_parser('show')
|
|
parser_show.add_argument(
|
|
'config',
|
|
choices=self.all_options,
|
|
default="ALL"
|
|
)
|
|
parser_show.add_argument(
|
|
'-c', '--child-details',
|
|
action='store_true',
|
|
help='Include child details'
|
|
)
|
|
parser_show.set_defaults(func=self.show, config="ALL")
|
|
|
|
parser.parse_args(self.argv, namespace=self)
|
|
return True
|
|
|
|
# Main display methods
|
|
def show(self):
|
|
"""Show configuration information based on selected option."""
|
|
if self.config == "SCOPE":
|
|
self.scope_details()
|
|
return
|
|
|
|
if self.config == "ALL":
|
|
# Exclude SCOPE from all_options when processing ALL
|
|
config = [x for x in self.all_options if x != "SCOPE"]
|
|
# Also show scope information when 'ALL' is selected
|
|
self.scope_details()
|
|
else:
|
|
config = [self.config]
|
|
|
|
for mconfig in config:
|
|
if mconfig in self.skip_list:
|
|
continue
|
|
|
|
try:
|
|
cfg = getattr(self.cs.Cfg, mconfig)
|
|
except AttributeError:
|
|
self.logger.log(f"\n{mconfig} - Not available")
|
|
continue
|
|
|
|
self.logger.log(f"\n{mconfig}")
|
|
for vid in cfg.keys():
|
|
self.logger.log(f"{vid}:")
|
|
for name in cfg[vid].keys():
|
|
if mconfig in ["CONFIG_PCI_RAW"]:
|
|
cfg_data = cfg[vid][name]
|
|
self.logger.log(
|
|
f'\t{name} - {self.pci_details(cfg_data)}'
|
|
)
|
|
elif mconfig in ["CONFIG_PCI"]:
|
|
cfg_data = cfg[vid][name]
|
|
self.logger.log(
|
|
f'\t{name} - {self.pci_details(cfg_data)}'
|
|
)
|
|
elif mconfig == "MEMORY_RANGES":
|
|
self.logger.log(
|
|
f'\t{name} - {self.memory_details(cfg[vid][name])}'
|
|
)
|
|
elif mconfig in ["MM_MSGBUS", "MSGBUS", "IO"]:
|
|
port_details = self.get_port_details(cfg[vid][name])
|
|
self.logger.log(f'\t{name} - {port_details}')
|
|
elif mconfig in ["MMIO_BARS", "IO_BARS"]:
|
|
for cfg_data in cfg[vid][name]:
|
|
details = self.mmio_details(
|
|
cfg[vid][name][cfg_data]
|
|
)
|
|
self.logger.log(f'\t{cfg_data} - {details}')
|
|
elif mconfig == "MSR":
|
|
self.logger.log(
|
|
f'\t{name} - {self.msr_details(cfg[vid][name])}'
|
|
)
|
|
|
|
if (self.child_details and
|
|
mconfig not in ["CONFIG_PCI_RAW"]):
|
|
if mconfig not in ["MMIO_BARS", "IO_BARS"]:
|
|
self.get_child_details(vid, name)
|
|
else:
|
|
for cfg_data in cfg[vid][name]:
|
|
self.get_child_details(
|
|
vid, cfg[vid][name][cfg_data].name
|
|
)
|
|
|
|
if set(config).intersection({"LOCKS", "ALL"}):
|
|
self.lock_details()
|
|
if set(config).intersection({"CONTROLS", "ALL"}):
|
|
self.control_details()
|
|
return
|
|
|
|
# Detail formatting methods for different types of registers/configurations
|
|
def msr_details(self, regi):
|
|
"""Format MSR register details."""
|
|
ret = f'config: {regi.config}'
|
|
return ret
|
|
|
|
def memory_details(self, regi):
|
|
"""Format memory range details."""
|
|
try:
|
|
# Try to access as dictionary first
|
|
if isinstance(regi, dict):
|
|
# Basic attributes always displayed
|
|
ret = (f"access: {regi.get('access', 'N/A')}, "
|
|
f"address: {regi.get('address', 'N/A')}, "
|
|
f"size: {regi.get('size', 'N/A')}")
|
|
|
|
# Add configuration if available
|
|
if 'config' in regi:
|
|
ret += f", config: {regi['config']}"
|
|
|
|
# BEGIN: Extended MMIO Functionality
|
|
# Display extended MMIO attributes if available
|
|
if 'base_ref' in regi:
|
|
ret += f", base_ref: {regi['base_ref']}"
|
|
if 'base_value' in regi:
|
|
ret += f" (0x{regi['base_value']:x})"
|
|
|
|
if 'ip_ref' in regi:
|
|
ret += f", ip_ref: {regi['ip_ref']}"
|
|
if 'ip_value' in regi:
|
|
ret += f" (0x{regi['ip_value']:x})"
|
|
|
|
if 'scope' in regi:
|
|
ret += f", scope: {regi['scope']}"
|
|
# END: Extended MMIO Functionality
|
|
|
|
else:
|
|
# Fall back to attribute access if it's an object
|
|
ret = (f"access: {getattr(regi, 'access', 'N/A')}, "
|
|
f"address: {getattr(regi, 'address', 'N/A')}, "
|
|
f"size: {getattr(regi, 'size', 'N/A')}")
|
|
|
|
# Add configuration if available
|
|
if hasattr(regi, 'config'):
|
|
ret += f", config: {regi.config}"
|
|
|
|
# BEGIN: Extended MMIO Functionality
|
|
# Display extended MMIO attributes if available
|
|
if hasattr(regi, 'base_ref') and regi.base_ref:
|
|
ret += f", base_ref: {regi.base_ref}"
|
|
if hasattr(regi, 'base_value') and regi.base_value:
|
|
ret += f" (0x{regi.base_value:x})"
|
|
|
|
if hasattr(regi, 'ip_ref') and regi.ip_ref:
|
|
ret += f", ip_ref: {regi.ip_ref}"
|
|
if hasattr(regi, 'ip_value') and regi.ip_value:
|
|
ret += f" (0x{regi.ip_value:x})"
|
|
|
|
if hasattr(regi, 'scope') and regi.scope:
|
|
ret += f", scope: {regi.scope}"
|
|
# END: Extended MMIO Functionality
|
|
|
|
except (KeyError, AttributeError):
|
|
# In case of any error, just return the string representation
|
|
ret = str(regi)
|
|
return ret
|
|
|
|
def get_port_details(self, regi):
|
|
"""Format port details."""
|
|
ret = ''
|
|
if regi:
|
|
ret = f'port: {regi.port}, config: {regi.config}'
|
|
return ret
|
|
|
|
def ima_details(self, regi):
|
|
"""Format IMA register details."""
|
|
base_value = regi['base'] if 'base' in regi.keys() else None
|
|
ret = f"index: {regi['index']}, data: {regi['data']}, " \
|
|
f"base: {base_value}"
|
|
return ret
|
|
|
|
def register_details(self, regi):
|
|
"""Format register details based on type."""
|
|
ret = ""
|
|
if regi['type'] == 'pcicfg' or regi['type'] == 'mmcfg':
|
|
ret = f"device: {regi['device']}, offset: {regi['offset']}, " \
|
|
f"size: {regi['size']}"
|
|
elif regi['type'] == 'mmio':
|
|
ret = f"bar: {regi['bar']}, offset: {regi['offset']}, " \
|
|
f"size: {regi['size']}"
|
|
elif regi['type'] == 'mm_msgbus':
|
|
ret = f"offset: {regi['offset']}, size: {regi['size']}"
|
|
elif regi['type'] == 'io':
|
|
ret = f"size: {regi['size']}"
|
|
elif regi['type'] == 'iobar':
|
|
ret = f"bar: {regi['bar']}, offset: {regi['offset']}, " \
|
|
f"size: {regi['size']}"
|
|
elif regi['type'] == 'msr':
|
|
if 'size' in regi:
|
|
ret = f"msr: {regi['msr']}, size: {regi['size']}"
|
|
else:
|
|
ret = f"msr: {regi['msr']}"
|
|
elif regi['type'] == 'R Byte':
|
|
if 'size' in regi:
|
|
ret = f"offset: {regi['offset']}, size: {regi['size']}"
|
|
else:
|
|
ret = f"offset: {regi['offset']}"
|
|
elif regi['type'] == 'memory':
|
|
ret = f"access: {regi['access']}, offset: {regi['offset']}, " \
|
|
f"size: {regi['size']}"
|
|
|
|
# Add field information if available
|
|
try:
|
|
if isinstance(regi, (list, tuple)) and len(regi) > 0:
|
|
reg_obj = regi[0]
|
|
if hasattr(reg_obj, 'fields') and reg_obj.fields:
|
|
for key in reg_obj.fields:
|
|
bit = reg_obj.fields[key]['bit']
|
|
size = reg_obj.fields[key]['size']
|
|
end_bit = bit + size - 1
|
|
ret += f'\n\t\t\t{key} - bit {bit}:{end_bit}'
|
|
except (IndexError, AttributeError, TypeError):
|
|
# Silently ignore errors when accessing fields
|
|
pass
|
|
|
|
return ret
|
|
|
|
def pci_details(self, regi):
|
|
"""Format PCI configuration details."""
|
|
return str(regi)
|
|
|
|
def mmio_details(self, regi):
|
|
"""Format MMIO details."""
|
|
return str(regi)
|
|
|
|
def io_details(self, regi):
|
|
"""Format IO details."""
|
|
if 'register' in regi:
|
|
fixed_addr = (
|
|
regi['fixed_address'] if 'fixed_address' in regi else None
|
|
)
|
|
ret = (f"register: {regi['register']}, "
|
|
f"base_field: {regi['base_field']}, "
|
|
f"size: {regi['size']}, "
|
|
f"fixed_addr: {fixed_addr}")
|
|
else:
|
|
size = regi['size'] if 'size' in regi else None
|
|
fixed_addr = (
|
|
regi['fixed_address'] if 'fixed_address' in regi else None
|
|
)
|
|
ret = (f"bus: {regi['bus']}, "
|
|
f"dev: {regi['dev']}, "
|
|
f"func: {regi['fun']}, "
|
|
f"reg: {regi['reg']}, "
|
|
f"mask: {regi['mask']}, "
|
|
f"size: {size}, "
|
|
f"fixed_addr: {fixed_addr}")
|
|
return ret
|
|
|
|
# Data display methods for child details, controls, and locks
|
|
def get_child_details(self, vid, dev):
|
|
"""Get and display child configuration details."""
|
|
self.logger.log(f'get_child details {dev}')
|
|
config = self.cs.Cfg.child_keys
|
|
for mconfig in config:
|
|
if mconfig in ["CONTROLS", "LOCKS", "LOCKEDBY"]:
|
|
continue
|
|
|
|
cfg = getattr(self.cs.Cfg, mconfig)
|
|
self.logger.log(f"\t{mconfig}:")
|
|
|
|
if mconfig == "IMA_REGISTERS":
|
|
if vid in cfg and dev in cfg[vid]:
|
|
for name in cfg[vid][dev].keys():
|
|
details = self.ima_details(cfg[vid][dev][name])
|
|
self.logger.log(f'\t\t{name} - {details}')
|
|
elif mconfig == "REGISTERS":
|
|
try:
|
|
if vid in cfg and dev in cfg[vid]:
|
|
for name in cfg[vid][dev].keys():
|
|
details = self.register_details(
|
|
cfg[vid][dev][name]
|
|
)
|
|
self.logger.log(f'\t\t{name} - {details}')
|
|
except KeyError:
|
|
pass
|
|
self.logger.log("")
|
|
|
|
def control_details(self):
|
|
"""Display control details."""
|
|
self.logger.log("\nCONTROLS")
|
|
try:
|
|
cfg = getattr(self.cs.Cfg, "CONTROLS")
|
|
for regi in cfg.keys():
|
|
self.logger.log(f'\t{regi}')
|
|
except AttributeError as e:
|
|
self.logger.log(f"\tError retrieving controls: {e}")
|
|
return
|
|
|
|
def lock_details(self):
|
|
"""Display lock details."""
|
|
self.logger.log("\nLOCKS")
|
|
try:
|
|
locks = self.cs.Cfg.get_lock_list()
|
|
for lock in locks:
|
|
lock_cfg = self.cs.Cfg.get_lock_obj(lock)
|
|
self.logger.log(f'\t{lock_cfg}')
|
|
except AttributeError as e:
|
|
self.logger.log(f"\tError retrieving locks: {e}")
|
|
return
|
|
|
|
def scope_details(self):
|
|
"""Display the contents of the scope manager."""
|
|
self.logger.log("\nSCOPE MANAGER")
|
|
|
|
# Display parent keys
|
|
self.logger.log("\nParent Keys:")
|
|
for key in self.cs.Cfg.scope_manager.parent_keys:
|
|
self.logger.log(f"\t{key}")
|
|
|
|
# Display child keys
|
|
self.logger.log("\nChild Keys:")
|
|
for key in self.cs.Cfg.scope_manager.child_keys:
|
|
self.logger.log(f"\t{key}")
|
|
|
|
# Display current scope
|
|
self.logger.log("\nCurrent Scope:")
|
|
for scope_key, scope_value in self.cs.Cfg.scope.items():
|
|
key_name = scope_key if scope_key is not None else "DEFAULT"
|
|
self.logger.log(f"\t{key_name}: {scope_value}")
|
|
|
|
# BEGIN: Extended MMIO Functionality
|
|
# Display memory bases if available
|
|
if hasattr(self.cs.Cfg, 'memory_bases') and self.cs.Cfg.memory_bases:
|
|
self.logger.log("\nMemory Bases:")
|
|
for base_name, base_value in self.cs.Cfg.memory_bases.items():
|
|
self.logger.log(f"\t{base_name}: 0x{base_value:x}")
|
|
|
|
# Display IP addresses if available
|
|
if hasattr(self.cs.Cfg, 'ip_addresses') and self.cs.Cfg.ip_addresses:
|
|
self.logger.log("\nIP Addresses:")
|
|
for ip_name, ip_value in self.cs.Cfg.ip_addresses.items():
|
|
self.logger.log(f"\t{ip_name}: 0x{ip_value:x}")
|
|
# END: Extended MMIO Functionality
|
|
|
|
# Display available scopes in the configuration
|
|
self.logger.log("\nAvailable Configuration Items:")
|
|
|
|
# Go through parent keys and display available items
|
|
for parent_key in self.cs.Cfg.scope_manager.parent_keys:
|
|
if hasattr(self.cs.Cfg, parent_key):
|
|
cfg_data = getattr(self.cs.Cfg, parent_key)
|
|
if cfg_data:
|
|
self.logger.log(f"\n{parent_key}:")
|
|
for vid in cfg_data.keys():
|
|
self.logger.log(f"\t{vid}:")
|
|
if isinstance(cfg_data[vid], dict):
|
|
# Limit to first 10 items for readability
|
|
items = list(cfg_data[vid].keys())
|
|
for name in items[:10]:
|
|
self.logger.log(f"\t\t{name}")
|
|
if len(items) > 10:
|
|
remaining = len(items) - 10
|
|
self.logger.log(
|
|
f"\t\t... ({remaining} more items)"
|
|
)
|
|
return
|
|
|
|
|
|
commands = {'config': CONFIGCommand}
|