Files
Nathaniel Mitchell 1259e568de Change all HAL objects in cs.hals to be lowercase
Signed-off-by: Nathaniel Mitchell <nathaniel.p.mitchell@intel.com>
2025-12-09 13:49:37 -08:00

612 lines
23 KiB
Python

# CHIPSEC: Platform Security Assessment Framework
# Copyright (c) 2010-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: Platform Hardware Security Assessment Framework
#
# -------------------------------------------------------------------------------
"""
Access to SMBus Controller
"""
from typing import List
from chipsec.hal import hal_base
from chipsec.hal.common import iobar
from chipsec.library.exceptions import IOBARNotFoundError, ObjectInstanceNotFoundError, RegisterNotFoundError
SMBUS_COMMAND_QUICK = 0
SMBUS_COMMAND_BYTE = 1
SMBUS_COMMAND_BYTE_DATA = 2
SMBUS_COMMAND_WORD_DATA = 3
SMBUS_COMMAND_PROCESS_CALL = 4
SMBUS_COMMAND_BLOCK = 5
SMBUS_COMMAND_I2C_READ = 6
SMBUS_COMMAND_BLOCK_PROCESS = 7
SMBUS_POLL_COUNT = 1000
SMBUS_COMMAND_WRITE = 0
SMBUS_COMMAND_READ = 1
class SMBus(hal_base.HALBase):
def __init__(self, cs):
super(SMBus, self).__init__(cs)
self.instance = None
self.set_instance()
self.get_common_registers()
self.get_registers()
self.i2c_mode = False
def set_i2c_mode(self, is_i2c_mode_enabled):
self.i2c_mode = is_i2c_mode_enabled
def get_instances(self):
smbus_obj = self.cs.device.get_list_by_name('8086.SMBUS')[0]
if smbus_obj and smbus_obj.instances:
return smbus_obj.instances
raise ObjectInstanceNotFoundError('SMBus instance not found')
def set_instance(self, instance=None):
_instances = self.get_instances()
if instance is None:
self.instance = _instances[0]
elif instance in _instances:
self.instance = instance
else:
raise RegisterNotFoundError(f'Instance {instance} is not within supported list {_instances}')
def get_registers(self):
self.smb_reg_status = self.cs.register.get_instance_by_name('8086.SMBUS.HST_STS', self.instance)
self.smb_reg_control = self.cs.register.get_instance_by_name('8086.SMBUS.HST_CNT', self.instance)
self.smb_reg_command = self.cs.register.get_instance_by_name('8086.SMBUS.HST_CMD', self.instance)
self.smb_reg_address = self.cs.register.get_instance_by_name('8086.SMBUS.HST_SLVA', self.instance)
self.smb_reg_data0 = self.cs.register.get_instance_by_name('8086.SMBUS.HST_D0', self.instance)
self.smb_reg_data1 = self.cs.register.get_instance_by_name('8086.SMBUS.HST_D1', self.instance)
self.smb_reg_aux_ctl = self.cs.register.get_instance_by_name('8086.SMBUS.HST_AUX_CTL', self.instance)
self.smb_reg_block_db = self.cs.register.get_instance_by_name('8086.SMBUS.HST_BLOCK_DB', self.instance)
self.smb_hcfg = self.cs.register.get_instance_by_name('8086.SMBUS.HCFG', self.instance)
def get_common_registers(self):
self.smb_cmd = self.cs.register.get_instance_by_name('8086.SMBUS.CMD', self.instance)
def enable(self):
self.logger.log_hal("[SMBUS] Enabling SMBus...")
if not self.is_SMBus_host_controller_enabled():
self.logger.log_hal("SMBus disabled; enabling...")
self.enable_SMBus_host_controller()
if self.i2c_mode is True:
self.logger.log_hal("i2c mode is selected")
if not self.is_pch_i2c_enabled():
self.logger.log_hal("Intel PCH is not enabled to communicate with i2c devices; enabling...")
self.enable_pch_i2c_comm()
else:
self.logger.log_hal("SMBUS mode is selected. disabling i2c mode")
if self.is_pch_i2c_enabled():
self.logger.log_hal("Intel PCH is enabled to communicate with i2c devices; disabling...")
self.disable_pch_i2c_comm()
if not self.is_SMBus_io_mem_space_enabled():
self.logger.log_hal("SMBus io/mem space disabled; enabling...")
self.enable_SMBus_io_mem_space()
if not self.is_SMBus_host_controller_enabled():
self.logger.log_hal("[SMBUS] SMBus disabled; enabling...")
self.enable_SMBus_host_controller()
self.logger.log_hal("[SMBUS] SMBus enabled attempted")
def _txn_rw_read(self, target_address):
self.smb_reg_address.set_value(0x0)
self.smb_reg_address.set_field('RW', SMBUS_COMMAND_READ)
hst_sa = self.smb_reg_address.set_field('Address', target_address)
self.smb_reg_address.write(hst_sa)
self.smb_reg_address.read()
self.logger.log_hal(self.smb_reg_address)
def _txn_rw_write(self, target_address):
self.smb_reg_address.set_value(0x0)
self.smb_reg_address.set_field('RW', SMBUS_COMMAND_WRITE)
hst_sa = self.smb_reg_address.set_field('Address', target_address)
self.smb_reg_address.write(hst_sa)
def get_SMBus_Base_Address(self):
if self.cs.hals.iobar.is_IO_BAR_defined('8086.SMBUS.SMBUS_BASE'):
(sba_base, _) = self.cs.hals.iobar.get_IO_BAR_base_address('8086.SMBUS.SMBUS_BASE', self.instance)
return sba_base
else:
raise IOBARNotFoundError('IOBARAccessError: SMBUS_BASE')
def display_SMBus_info(self):
self.logger.log(f"[smbus] SMBus Base Address: 0x{self.get_SMBus_Base_Address():04X}")
self.smb_hcfg.read()
self.logger.log(self.smb_hcfg)
def is_SMBus_enabled(self):
return self.cs.device.get_bus('8086.SMBUS')
def is_SMBus_supported(self):
if self.cs.device.get_bus('8086.SMBUS') is not None:
return True
else:
return False
def is_SMBus_host_controller_enabled(self):
return self.smb_hcfg.get_field("HST_EN") == 1
def is_pch_i2c_enabled(self):
return ((self.smb_hcfg.read() & 4) >> 2) == 1
def is_SMBus_io_mem_space_enabled(self):
cmd = self.smb_cmd.read()
self.logger.log_hal(self.smb_cmd)
return (cmd & 0x3) == 0x3
def enable_SMBus_host_controller(self):
# Enable SMBus Host Controller Interface in HCFG
reg_value = self.smb_hcfg.read()
if 0 == (reg_value & 0x1):
self.smb_hcfg.write(reg_value | 0x1)
def disable_pch_i2c_comm(self):
# Disable PCH connection to I2c devices
reg_value = self.smb_hcfg.read()
if not 0 == (reg_value & 0x04):
self.smb_hcfg.write(reg_value & ~ 0x4)
reg_value = self.smb_hcfg.read()
if not 0 == (reg_value & 0x04):
self.logger.log("PCH is enabled to connect with i2c devices")
else:
self.logger.log("PCH is disabled to connect with i2c devices")
def enable_pch_i2c_comm(self):
# Enable PCH connection to I2c devices
reg_value = self.smb_hcfg.read()
if 0 == (reg_value & 0x04):
self.smb_hcfg.write(reg_value | 0x05)
reg_value = self.smb_hcfg.read()
if not 0 == (reg_value & 0x04):
self.logger.log("PCH is enabled to connect with i2c devices")
else:
self.logger.log("PCH is not enabled to connect with i2c devices")
def enable_SMBus_io_mem_space(self):
# @TODO: check SBA is programmed
# sba = self.get_SMBus_Base_Address()
# Enable SMBus I/O Space
cmd = self.smb_cmd.read()
if 0 == (cmd & 0x1):
self.smb_cmd.write(cmd | 0x1)
def reset_SMBus_controller(self):
reg_value = self.smb_hcfg.read()
self.smb_hcfg.write(reg_value | 0x08)
for _ in range(SMBUS_POLL_COUNT):
if (self.smb_hcfg.read() & 0x08) == 0:
return True
return False
# waits for SMBus to become ready
def _is_smbus_ready(self):
for i in range(SMBUS_POLL_COUNT):
self.smb_reg_status.read()
self.logger.log_hal(f"Status: 0x{self.smb_reg_status.value:X}")
busy = self.smb_reg_status.get_field('BUSY')
if 1 == busy:
self.logger.log_hal("SMBus busy, waiting...")
continue
self.smb_reg_status.write(0xFF)
break
return 0 == busy
# waits for SMBus transaction to complete
def _wait_for_cycle(self):
for i in range(SMBUS_POLL_COUNT):
self.smb_reg_status.read()
busy = self.smb_reg_status.get_field('BUSY')
failed = self.smb_reg_status.get_field('FAILED')
if 1 == busy:
self.logger.log_hal("SMBus busy, waiting...")
continue
elif 1 == failed:
self.logger.log_hal("SMBus transaction failed (FAILED/ERROR bit = 1)")
reg_value = self.smb_hcfg.read()
self.smb_hcfg.write(reg_value | 0x08)
return False
if self.cs.register.has_field(self.smb_reg_status.name, 'DEV_ERR'):
if 1 == self.smb_reg_status.get_field('DEV_ERR'):
self.logger.log_hal("SMBus device error (invalid cmd, unclaimed cycle or time-out error)")
reg_value = self.smb_hcfg.read()
self.smb_hcfg.write(reg_value | 0x08)
return False
if self.cs.register.has_field(self.smb_reg_status.name, 'BUS_ERR'):
if 1 == self.smb_reg_status.get_field('BUS_ERR'):
self.logger.log_hal("SMBus bus error")
reg_value = self.smb_hcfg.read()
self.smb_hcfg.write(reg_value | 0x08)
return False
break
return (0 == busy)
#
# SMBus commands
#
def quick_write(self, target_address):
ret_code = False
if not self._is_smbus_ready():
return ret_code
self.logger.log_verbose(f"[smbus] quick write to device {target_address:X}")
# clear status bits
self.smb_reg_status.write(0xFF)
# SMBus txn RW direction = Write, SMBus slave address = target_address
self._txn_rw_write(target_address)
# command = Byte Data
self.smb_reg_control.write_field('SMB_CMD', SMBUS_COMMAND_QUICK)
self.smb_reg_control.read()
# send SMBus txn
self.smb_reg_control.write_field('START', 1)
self.smb_reg_control.read()
# wait for cycle to complete
ret_code = self._wait_for_cycle()
# clear status bits
self.smb_reg_data0.write(0x0)
self.smb_reg_data0.read()
self.logger.log_hal(f"[smbus] quick write to device {target_address:X} returned {str(ret_code)}")
return ret_code
def read_byte(self, target_address, offset):
if not self._is_smbus_ready():
self.logger.log(f"[smbus] controller is not ready {target_address:X}")
return False
# clear status bits
self.smb_reg_status.write(0xFF)
# SMBus txn RW direction = Read, SMBus slave address = target_address
self._txn_rw_read(target_address)
# command data = byte offset (bus txn address)
self.smb_reg_command.write_field('DataOffset', offset)
self.smb_reg_command.read()
self.logger.log_hal(self.smb_reg_command)
# command = Byte Data
self.smb_reg_control.write_field('SMB_CMD', SMBUS_COMMAND_BYTE_DATA)
self.smb_reg_control.read()
# send SMBus txn
self.smb_reg_control.write_field('START', 1)
self.smb_reg_control.read()
self.logger.log_hal(self.smb_reg_control)
# wait for cycle to complete
if not self._wait_for_cycle():
return False
# read the data
value = self.smb_reg_data0.read()
self.logger.log_hal(self.smb_reg_data0)
self.smb_reg_data0.write(0x00)
self.smb_reg_data0.read()
self.logger.log_hal(f"[smbus] read device {target_address:X} off {offset:X} = {value:X}")
return [value]
def read_word(self, target_address, offset):
if not self._is_smbus_ready():
return False
# clear status bits
self.smb_reg_status.write(0xFF)
self.smb_reg_status.read()
# SMBus txn RW direction = Read, SMBus slave address = target_address
self._txn_rw_read(target_address)
# command data = byte offset (bus txn address)
self.smb_reg_command.write_field('DataOffset', offset)
self.smb_reg_command.read()
self.logger.log_hal(self.smb_reg_command)
# command = Byte Data
self.smb_reg_control.write_field('SMB_CMD', SMBUS_COMMAND_WORD_DATA)
self.smb_reg_control.read()
# send SMBus txn
self.smb_reg_control.write_field('START', 1)
self.smb_reg_control.read()
self.logger.log_hal(self.smb_reg_control)
# wait for cycle to complete
if not self._wait_for_cycle():
return False
# read the data
valueL = self.smb_reg_data0.read()
self.logger.log_hal(self.smb_reg_data0)
self.smb_reg_data0.write(0x00)
self.smb_reg_data0.read()
valueH = self.smb_reg_data1.read()
self.logger.log_hal(self.smb_reg_data1)
self.smb_reg_data1.write(0x00)
self.smb_reg_data1.read()
self.logger.log_verbose(f"[smbus] read device {target_address:X} off {offset:X} = {valueH:X} {valueL:X}")
return [valueL, valueH]
def read_block(self, target_address, command):
if not self._is_smbus_ready():
return False
# clear status bits
self.smb_reg_status.write(0xFF)
self.smb_reg_control.write_field('LAST_BYTE', 0)
self.smb_reg_control.read()
# SMBus txn RW direction = Read, SMBus slave address = target_address
self._txn_rw_read(target_address)
# auxiliary control reg
self.smb_reg_aux_ctl.write_field('E32B', 1)
self.smb_reg_aux_ctl.read()
self.logger.log_hal(self.smb_reg_aux_ctl)
# command data = byte offset (bus txn address)
self.smb_reg_command.write_field('DataOffset', command)
self.smb_reg_command.read()
self.logger.log_hal(self.smb_reg_command)
# command = Byte Data
self.smb_reg_control.write_field('SMB_CMD', SMBUS_COMMAND_BLOCK)
self.smb_reg_control.read()
self.logger.log_hal(self.smb_reg_control)
# send SMBus txn
self.smb_reg_control.write_field('START', 1)
self.smb_reg_control.read()
self.logger.log_hal(self.smb_reg_control)
# wait for cycle to complete
if not self._wait_for_cycle():
return False
read_list = []
to_read = self.smb_reg_data0.read()
if to_read <= 0 or to_read > 32:
return False
while to_read:
read_list.append(self.smb_reg_block_db.read())
to_read -= 1
self.smb_reg_control.write_field('LAST_BYTE', 1)
self.smb_reg_control.read()
self.smb_reg_data0.write(0x00)
self.smb_reg_data0.read()
self.logger.log_verbose(f"[smbus] block read device 0x{target_address:x} off 0x{command:x} = " + " 0x".join(f"{c:02x}" for c in read_list))
return read_list
def write_byte(self, target_address, offset, value):
if not self._is_smbus_ready():
return False
# clear status bits
self.smb_reg_status.write(0xFF)
self.smb_reg_status.read()
# SMBus txn RW direction = Write, SMBus slave address = target_address
self._txn_rw_write(target_address)
self.smb_reg_address.read()
# command data = byte offset (bus txn address)
self.smb_reg_command.write_field('DataOffset', offset)
self.smb_reg_command.read()
# write the data
self.smb_reg_data0.write_field('Data', value)
self.smb_reg_data0.read()
# command = Byte Data
self.smb_reg_control.write_field('SMB_CMD', SMBUS_COMMAND_BYTE_DATA)
self.smb_reg_control.read()
# send SMBus txn
self.smb_reg_control.write_field('START', 1)
self.smb_reg_control.read()
# wait for cycle to complete
if not self._wait_for_cycle():
return False
# clear status bits
self.smb_reg_status.write(0x0)
self.smb_reg_status.read()
self.logger.log_verbose(f'[smbus] write to device {target_address:X} off {offset:X} = {value:X}')
return True
def write_word(self, target_address, offset, valueH, valueL):
if not self._is_smbus_ready():
return False
# clear status bits
self.smb_reg_status.write(0xFF)
# SMBus txn RW direction = Write, SMBus slave address = target_address
self._txn_rw_write(target_address)
self.smb_reg_address.read()
# command data = byte offset (bus txn address)
self.smb_reg_command.write_field('DataOffset', offset)
self.smb_reg_command.read()
# write the data
self.smb_reg_data0.write_field('Data', valueL)
self.smb_reg_data0.read()
self.smb_reg_data1.write_field('Data', valueH)
self.smb_reg_data1.read()
# command = Byte Data
self.smb_reg_control.write_field('SMB_CMD', SMBUS_COMMAND_WORD_DATA)
self.smb_reg_control.read()
# send SMBus txn
self.smb_reg_control.write_field('START', 1)
self.smb_reg_control.read()
# wait for cycle to complete
if not self._wait_for_cycle():
return False
# clear status bits
self.smb_reg_data0.write(0x00)
self.smb_reg_data0.read()
self.smb_reg_data1.write(0x00)
self.smb_reg_data1.read()
self.logger.log_verbose(f'[smbus] write to device {target_address:X} off {offset:X} = {valueH:X} {valueL:X}')
return True
def process_call(self, target_address, offset, valueH, valueL):
if not self._is_smbus_ready():
return False
# clear status bits
self.smb_reg_status.write(0xFF)
# SMBus txn RW direction = Write, SMBus slave address = target_address
self._txn_rw_write(target_address)
self.smb_reg_address.read()
# command data = byte offset (bus txn address)
self.smb_reg_command.write_field('DataOffset', offset)
self.smb_reg_command.read()
# write the data
self.smb_reg_data0.write(valueL)
self.smb_reg_data0.read()
self.logger.log_hal(self.smb_reg_data0)
self.smb_reg_data1.write(valueH)
self.smb_reg_data1.read()
self.logger.log_hal(self.smb_reg_data1)
# command = Byte Data
self.smb_reg_control.write_field('SMB_CMD', SMBUS_COMMAND_PROCESS_CALL)
self.smb_reg_control.read()
# send SMBus txn
self.smb_reg_control.write_field('START', 1)
self.smb_reg_control.read()
# wait for cycle to complete
if not self._wait_for_cycle():
return False
# read the data
valueL_R = self.smb_reg_data0.read()
self.logger.log_hal(self.smb_reg_data0)
valueH_R = self.smb_reg_data1.read()
self.logger.log_hal(self.smb_reg_data1)
# clear status bits
self.smb_reg_data0.write(0x00)
self.smb_reg_data0.read()
self.smb_reg_data1.write(0x00)
self.smb_reg_data1.read()
self.logger.log_debug(f"[smbus] read device {target_address:X} off {offset:X} = {valueH_R:X} {valueL_R:X}")
return [valueL_R, valueH_R]
class SMBusMMIO(SMBus):
def __init__(self, cs):
super(SMBusMMIO, self).__init__(cs)
def get_registers(self):
super(SMBusMMIO, self).get_registers()
self.smb_reg_status = self.cs.register.get_instance_by_name('8086.SMBUS.HST_STS_MMIO', self.instance)
self.smb_reg_control = self.cs.register.get_instance_by_name('8086.SMBUS.HST_CNT_MMIO', self.instance)
self.smb_reg_command = self.cs.register.get_instance_by_name('8086.SMBUS.HST_CMD_MMIO', self.instance)
self.smb_reg_address = self.cs.register.get_instance_by_name('8086.SMBUS.HST_SLVA_MMIO', self.instance)
self.smb_reg_data0 = self.cs.register.get_instance_by_name('8086.SMBUS.HST_D0_MMIO', self.instance)
self.smb_reg_data1 = self.cs.register.get_instance_by_name('8086.SMBUS.HST_D1_MMIO', self.instance)
self.smb_reg_aux_ctl = self.cs.register.get_instance_by_name('8086.SMBUS.HST_AUX_CTL_MMIO', self.instance)
self.smb_reg_block_db = self.cs.register.get_instance_by_name('8086.SMBUS.HST_BLOCK_DB_MMIO', self.instance)
def enable(self) -> None:
self.logger.log_hal("[SMBUSMMIO] Enabling SMBusMMIO...")
if not self.is_SMBus_mmio_mem_space_enabled():
self.logger.log_hal("SMBus mmio space disabled; enabling...")
self.enable_SMBus_mmio_mem_space()
else:
self.logger.log_hal("SMBus mmio space enabled...")
if not self.is_SMBus_host_controller_enabled():
self.logger.log_hal("SMBus disabled; enabling...")
self.enable_SMBus_host_controller()
if self.i2c_mode is True:
self.logger.log_hal("i2c mode is selected")
if not self.is_pch_i2c_enabled():
self.logger.log_hal("Intel PCH is not enabled to communicate with i2c devices; enabling...")
self.enable_pch_i2c_comm()
else:
self.logger.log_hal("SMBUS mode is selected. disabling i2c mode")
if self.is_pch_i2c_enabled():
self.logger.log_hal("Intel PCH is enabled to communicate with i2c devices; disabling...")
self.disable_pch_i2c_comm()
if not self.is_SMBus_io_mem_space_enabled():
self.logger.log_hal("SMBus io/mem space disabled; enabling...")
self.enable_SMBus_io_mem_space()
if not self.is_SMBus_host_controller_enabled():
self.logger.log_hal("SMBus disabled; enabling...")
self.enable_SMBus_host_controller()
self.logger.log_hal("[SMBUSMMIO] SMBusMMIO Enabled attempted")
def is_SMBus_mmio_mem_space_enabled(self):
cmd = self.smb_cmd.read()
self.logger.log_hal(self.smb_cmd)
return (cmd.value & 0x3) == 0x3
def get_SMBus_mmio_Base_Address(self):
if self.cs.hals.mmio.is_MMIO_BAR_defined('8086.SMBUS.SMBUS_MMIOBAR'):
(smb_mmio_base, _) = self.cs.hals.mmio.get_MMIO_BAR_base_address('8086.SMBUS.SMBUS_MMIOBAR', self.instance)
self.logger.log_hal(f"SMBUS MMIO base: 0x{smb_mmio_base:016X} (assuming below 4GB)")
return smb_mmio_base
else:
return False
def enable_SMBus_mmio_mem_space(self):
# @TODO: check SBA is programmed
# sba = self.get_SMBus_mmio_Base_Address()
# Enable SMBus I/O Space
cmd = self.smb_cmd.read()
if 0 == (cmd.value & 0x2):
self.smb_cmd.write(cmd.value | 0x2)
haldata = {"arch":[hal_base.HALBase.MfgIds.Any], 'name': {'smbus': "SMBus", 'smbusmmio': "SMBusMMIO"}}