mirror of
https://github.com/chipsec/chipsec
synced 2026-06-08 13:31:00 +00:00
234 lines
10 KiB
Python
234 lines
10 KiB
Python
#!/usr/bin/python
|
|
#CHIPSEC: Platform Security Assessment Framework
|
|
#Copyright (c) 2010-2019, 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 chipsec.hal import iobar, hal_base
|
|
|
|
from chipsec.logger import *
|
|
|
|
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.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'
|
|
|
|
def get_SMBus_Base_Address( self ):
|
|
if self.iobar.is_IO_BAR_defined( 'SMBUS_BASE' ):
|
|
(sba_base, sba_size) = self.iobar.get_IO_BAR_base_address( 'SMBUS_BASE' )
|
|
return sba_base
|
|
else:
|
|
raise iobar.IOBARNotFoundError ('IOBARAccessError: SMBUS_BASE')
|
|
|
|
def get_SMBus_HCFG( self ):
|
|
if self.cs.is_register_defined( 'SMBUS_HCFG' ):
|
|
reg_value = self.cs.read_register( 'SMBUS_HCFG' )
|
|
if logger().HAL: self.cs.print_register( 'SMBUS_HCFG', reg_value )
|
|
return reg_value
|
|
else:
|
|
raise self.cs.RegisterNotFoundError ('RegisterNotFound: SMBUS_HCFG')
|
|
|
|
def display_SMBus_info( self ):
|
|
if logger().HAL: logger().log( "[smbus] SMBus Base Address: 0x{:04X}".format(self.get_SMBus_Base_Address()) )
|
|
self.get_SMBus_HCFG()
|
|
|
|
def is_SMBus_enabled( self ):
|
|
return self.cs.is_device_enabled( 'SMBUS' )
|
|
|
|
def is_SMBus_supported( self ):
|
|
(did,vid) = self.cs.get_DeviceVendorID( 'SMBUS' )
|
|
if logger().HAL: logger().log( "[smbus] SMBus Controller (DID,VID) = (0x{:04X},0x{:04X})".format(did,vid) )
|
|
if (0x8086 == vid): return True
|
|
else:
|
|
logger().error( "Unknown SMBus Controller (DID,VID) = (0x{:04X},0x{:04X})".format(did,vid) )
|
|
return False
|
|
|
|
def is_SMBus_host_controller_enabled( self ):
|
|
hcfg = self.get_SMBus_HCFG()
|
|
return hcfg.CFG_REG_PCH_SMB_HCFG_HST_EN
|
|
|
|
def enable_SMBus_host_controller( self ):
|
|
# 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) )
|
|
|
|
def reset_SMBus_controller(self):
|
|
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 ):
|
|
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 ):
|
|
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 logger().HAL:
|
|
# intr = chipsec.chipset.get_register_field( self.cs, self.smb_reg_status, sts, 'INTR' )
|
|
# 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 logger().HAL: logger().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 logger().HAL: logger().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 logger().HAL: logger().error( "SMBus bus error" )
|
|
return False
|
|
return (0 == busy)
|
|
|
|
def read_byte( self, target_address, offset ):
|
|
# 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 )
|
|
if logger().HAL: logger().log( "[smbus] read device {:X} off {:X} = {:X}".format(target_address, offset, value) )
|
|
return value
|
|
|
|
def write_byte( self, target_address, offset, value ):
|
|
# 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 )
|
|
if logger().HAL: logger().log( "[smbus] write to device {:X} off {:X} = {:X}".format(target_address, offset, value) )
|
|
return True
|
|
|
|
|
|
def read_range( self, target_address, start_offset, size ):
|
|
buffer = [chr(0xFF)]*size
|
|
for i in range (size):
|
|
buffer[i] = chr( self.read_byte( target_address, start_offset + i ) )
|
|
if logger().HAL:
|
|
logger().log( "[smbus] reading {:d} bytes from device 0x{:X} at offset {:X}".format(size, target_address, start_offset) )
|
|
#print_buffer( buffer )
|
|
return buffer
|
|
|
|
def write_range( self, target_address, start_offset, buffer ):
|
|
size = len(buffer)
|
|
for i in range(size):
|
|
self.write_byte( target_address, start_offset + i, ord(buffer[i]) )
|
|
if logger().HAL:
|
|
logger().log( "[smbus] writing {:d} bytes to device 0x{:X} at offset {:X}".format(size, target_address, start_offset) )
|
|
#print_buffer( buffer )
|
|
return True
|