Source code for chipsec.hal.smbus

# 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 iobar, hal_base
from chipsec.exceptions import IOBARNotFoundError, 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


[docs]class SMBus(hal_base.HALBase): def __init__(self, cs): super(SMBus, self).__init__(cs) self.iobar = iobar.IOBAR(self.cs) self.smb_reg_status = 'SMBUS_HST_STS' self.smb_reg_command = 'SMBUS_HST_CMD' self.smb_reg_address = 'SMBUS_HST_SLVA' self.smb_reg_control = 'SMBUS_HST_CNT' self.smb_reg_data0 = 'SMBUS_HST_D0' self.smb_reg_data1 = 'SMBUS_HST_D1'
[docs] def get_SMBus_Base_Address(self) -> int: if self.iobar.is_IO_BAR_defined('SMBUS_BASE'): (sba_base, _) = self.iobar.get_IO_BAR_base_address('SMBUS_BASE') return sba_base else: raise IOBARNotFoundError('IOBARAccessError: SMBUS_BASE')
[docs] def get_SMBus_HCFG(self) -> int: if self.cs.is_register_defined('SMBUS_HCFG'): reg_value = self.cs.read_register('SMBUS_HCFG') if self.logger.HAL: self.cs.print_register('SMBUS_HCFG', reg_value) return reg_value else: raise RegisterNotFoundError('RegisterNotFound: SMBUS_HCFG')
[docs] def display_SMBus_info(self) -> None: self.logger.log_hal(f'[smbus] SMBus Base Address: 0x{self.get_SMBus_Base_Address():04X}') self.get_SMBus_HCFG()
[docs] def is_SMBus_enabled(self) -> bool: return self.cs.is_device_enabled('SMBUS')
[docs] def is_SMBus_supported(self) -> bool: (did, vid) = self.cs.get_DeviceVendorID('SMBUS') self.logger.log_hal(f'[smbus] SMBus Controller (DID,VID) = (0x{did:04X},0x{vid:04X})') if (0x8086 == vid): return True else: self.logger.log_error(f'Unknown SMBus Controller (DID,VID) = (0x{did:04X},0x{vid:04X})') return False
[docs] def is_SMBus_host_controller_enabled(self) -> int: hcfg = self.get_SMBus_HCFG() return self.cs.get_register_field("SMBUS_HCFG", hcfg, "HST_EN")
[docs] def enable_SMBus_host_controller(self) -> None: # Enable SMBus Host Controller Interface in HCFG reg_value = self.cs.read_register('SMBUS_HCFG') if 0 == (reg_value & 0x1): self.cs.write_register('SMBUS_HCFG', (reg_value | 0x1)) # @TODO: check SBA is programmed sba = self.get_SMBus_Base_Address() # Enable SMBus I/O Space cmd = self.cs.read_register('SMBUS_CMD') if 0 == (cmd & 0x1): self.cs.write_register('SMBUS_CMD', (cmd | 0x1))
[docs] def reset_SMBus_controller(self) -> bool: reg_value = self.cs.read_register('SMBUS_HCFG') self.cs.write_register('SMBUS_HCFG', reg_value | 0x08) for i in range(SMBUS_POLL_COUNT): if (self.cs.read_register('SMBUS_HCFG') & 0x08) == 0: return True return False
# # SMBus commands # # waits for SMBus to become ready def _is_smbus_ready(self) -> bool: busy = None for i in range(SMBUS_POLL_COUNT): #time.sleep( SMBUS_POLL_SLEEP_INTERVAL ) busy = self.cs.read_register_field(self.smb_reg_status, 'BUSY') if 0 == busy: return True return 0 == busy # waits for SMBus transaction to complete def _wait_for_cycle(self) -> bool: busy = None for i in range(SMBUS_POLL_COUNT): #time.sleep( SMBUS_POLL_SLEEP_INTERVAL ) sts = self.cs.read_register(self.smb_reg_status) busy = self.cs.get_register_field(self.smb_reg_status, sts, 'BUSY') intr = self.cs.get_register_field(self.smb_reg_status, sts, 'INTR') failed = self.cs.get_register_field(self.smb_reg_status, sts, 'FAILED') if 0 == busy and 1 == intr: # if self.logger.HAL: # intr = chipsec.chipset.get_register_field( self.cs, self.smb_reg_status, sts, 'INTR' ) # self.logger.log( "[smbus]: INTR = {:d}".format(intr) ) break elif 1 == failed: #kill = 0 # if chipsec.chipset.register_has_field( self.cs, self.smb_reg_control, 'KILL' ): # kill = chipsec.chipset.read_register_field( self.cs, self.smb_reg_control, 'KILL' ) if self.logger.HAL: self.logger.log_error("SMBus transaction failed (FAILED/ERROR bit = 1)") return False else: if self.cs.register_has_field(self.smb_reg_status, 'DEV_ERR'): if 1 == self.cs.get_register_field(self.smb_reg_status, sts, 'DEV_ERR'): if self.logger.HAL: self.logger.log_error("SMBus device error (invalid cmd, unclaimed cycle or time-out error)") return False if self.cs.register_has_field(self.smb_reg_status, 'BUS_ERR'): if 1 == self.cs.get_register_field(self.smb_reg_status, sts, 'BUS_ERR'): if self.logger.HAL: self.logger.log_error("SMBus bus error") return False return 0 == busy
[docs] def read_byte(self, target_address: int, offset: int) -> int: # clear status bits self.cs.write_register(self.smb_reg_status, 0xFF) # SMBus txn RW direction = Read, SMBus slave address = target_address hst_sa = 0x0 hst_sa = self.cs.set_register_field(self.smb_reg_address, hst_sa, 'RW', SMBUS_COMMAND_READ) hst_sa = self.cs.set_register_field(self.smb_reg_address, hst_sa, 'Address', target_address, True) self.cs.write_register(self.smb_reg_address, hst_sa) # command data = byte offset (bus txn address) self.cs.write_register_field(self.smb_reg_command, 'DataOffset', offset) # command = Byte Data # if self.cs.register_has_field( self.smb_reg_control, 'SMB_CMD' ): self.cs.write_register_field(self.smb_reg_control, 'SMB_CMD', SMBUS_COMMAND_BYTE_DATA) # send SMBus txn self.cs.write_register_field(self.smb_reg_control, 'START', 1) # wait for cycle to complete if not self._wait_for_cycle(): return 0xFF # read the data value = self.cs.read_register_field(self.smb_reg_data0, 'Data') # clear status bits self.cs.write_register(self.smb_reg_status, 0xFF) # clear address/offset registers #chipsec.chipset.write_register( self.cs, self.smb_reg_address, 0x0 ) #chipsec.chipset.write_register( self.cs, self.smb_reg_command, 0x0 ) self.logger.log_hal(f'[smbus] read device {target_address:X} off {offset:X} = {value:X}') return value
[docs] def write_byte(self, target_address: int, offset: int, value: int) -> bool: # clear status bits self.cs.write_register(self.smb_reg_status, 0xFF) # SMBus txn RW direction = Write, SMBus slave address = target_address hst_sa = 0x0 hst_sa = self.cs.set_register_field(self.smb_reg_address, hst_sa, 'RW', SMBUS_COMMAND_WRITE) hst_sa = self.cs.set_register_field(self.smb_reg_address, hst_sa, 'Address', target_address, True) self.cs.write_register(self.smb_reg_address, hst_sa) # command data = byte offset (bus txn address) self.cs.write_register_field(self.smb_reg_command, 'DataOffset', offset) # write the data self.cs.write_register_field(self.smb_reg_data0, 'Data', value) # command = Byte Data # if self.cs.register_has_field( self.smb_reg_control, 'SMB_CMD' ): self.cs.write_register_field(self.smb_reg_control, 'SMB_CMD', SMBUS_COMMAND_BYTE_DATA) # send SMBus txn self.cs.write_register_field(self.smb_reg_control, 'START', 1) # wait for cycle to complete if not self._wait_for_cycle(): return False # clear status bits self.cs.write_register(self.smb_reg_status, 0xFF) # clear address/offset registers #chipsec.chipset.write_register( self.cs, self.smb_reg_address, 0x0 ) #chipsec.chipset.write_register( self.cs, self.smb_reg_command, 0x0 ) self.logger.log_hal(f'[smbus] write to device {target_address:X} off {offset:X} = {value:X}') return True
[docs] def read_range(self, target_address: int, start_offset: int, size: int) -> bytes: buffer = bytes(self.read_byte(target_address, start_offset + i) for i in range(size)) self.logger.log_hal(f'[smbus] reading {size:d} bytes from device 0x{target_address:X} at offset {start_offset:X}') return buffer
[docs] def write_range(self, target_address: int, start_offset: int, buffer: bytes) -> bool: for i, b in enumerate(buffer): self.write_byte(target_address, start_offset + i, b) self.logger.log_hal(f'[smbus] writing {size:d} bytes to device 0x{target_address:X} at offset {start_offset:X}') return True