diff --git a/source/tool/chipsec/chipset.py b/source/tool/chipsec/chipset.py index 9ecc0f8b..0edc0b32 100644 --- a/source/tool/chipsec/chipset.py +++ b/source/tool/chipsec/chipset.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -18,8 +18,8 @@ #Contact information: #chipsec@intel.com # - - + + # ------------------------------------------------------------------------------- # @@ -46,9 +46,8 @@ from chipsec.hal.physmem import Memory from chipsec.hal.msr import Msr from chipsec.hal.ucode import Ucode from chipsec.hal.io import PortIO -#from chipsec.hal.cr import CrRegs -from chipsec.hal.cpuid import CpuID -#from chipsec.hal.mmio import * +#from chipsec.hal.cpuid import CpuID +from chipsec.hal.msgbus import MsgBus import chipsec.hal.cpu import chipsec.hal.mmio as mmio @@ -65,8 +64,6 @@ import traceback #try: import importlib #except ImportError: _importlib = False -# - class RegisterType: PCICFG = 'pcicfg' @@ -75,6 +72,7 @@ class RegisterType: MSR = 'msr' PORTIO = 'io' IOBAR = 'iobar' + MSGBUS = 'msgbus' ################################################################################## @@ -94,6 +92,7 @@ CHIPSET_ID_QRK = 8 CHIPSET_ID_AVN = 9 CHIPSET_ID_HSX = 10 CHIPSET_ID_SKL = 11 +CHIPSET_ID_BSW = 12 CHIPSET_CODE_COMMON = 'COMMON' CHIPSET_CODE_UNKNOWN = '' @@ -109,10 +108,11 @@ CHIPSET_CODE_QRK = 'QRK' CHIPSET_CODE_AVN = 'AVN' CHIPSET_CODE_HSX = 'HSX' CHIPSET_CODE_SKL = 'SKL' +CHIPSET_CODE_BSW = 'BSW' CHIPSET_FAMILY_XEON = [CHIPSET_ID_JKT,CHIPSET_ID_IVT,CHIPSET_ID_HSX] CHIPSET_FAMILY_CORE = [CHIPSET_ID_SNB,CHIPSET_ID_IVB,CHIPSET_ID_HSW,CHIPSET_ID_BDW,CHIPSET_ID_SKL] -CHIPSET_FAMILY_ATOM = [CHIPSET_ID_BYT,CHIPSET_ID_AVN] +CHIPSET_FAMILY_ATOM = [CHIPSET_ID_BYT,CHIPSET_ID_AVN,CHIPSET_CODE_BSW] CHIPSET_FAMILY_QUARK = [CHIPSET_ID_QRK] @@ -150,8 +150,8 @@ Chipset_Dictionary = { 0x0A08 : {'name' : 'Haswell', 'id' : CHIPSET_ID_HSW , 'code' : CHIPSET_CODE_HSW, 'longname' : '4th Generation Core Processor (Haswell U/Y)' }, # 5th Generation Core Processor Family (Broadwell) -0x1600 : {'name' : 'Broadwell', 'id' : CHIPSET_ID_BDW , 'code' : 'BDW', 'longname' : 'Desktop 5th Generation Core Processor (Broadwell CPU / Wildcat Point PCH)' }, -0x1604 : {'name' : 'Broadwell', 'id' : CHIPSET_ID_BDW , 'code' : 'BDW', 'longname' : 'Mobile 5th Generation Core Processor (Broadwell M/H / Wildcat Point PCH)' }, +0x1600 : {'name' : 'Broadwell', 'id' : CHIPSET_ID_BDW , 'code' : CHIPSET_CODE_BDW, 'longname' : 'Desktop 5th Generation Core Processor (Broadwell CPU / Wildcat Point PCH)' }, +0x1604 : {'name' : 'Broadwell', 'id' : CHIPSET_ID_BDW , 'code' : CHIPSET_CODE_BDW, 'longname' : 'Mobile 5th Generation Core Processor (Broadwell M/H / Wildcat Point PCH)' }, # 6th Generation Core Processor Family (Skylake) 0x1904 : {'name' : 'Skylake', 'id' : CHIPSET_ID_SKL , 'code' : CHIPSET_CODE_SKL, 'longname' : 'Mobile 6th Generation Core Processor (Skylake U)' }, @@ -213,7 +213,6 @@ class DeviceNotFoundError (RuntimeError): class Chipset: def __init__(self, helper=None): - if logger().VERBOSE: logger().log("[Chipset] __init__") if helper is None: self.helper = OsHelper() else: @@ -237,7 +236,8 @@ class Chipset: self.io = PortIO ( self ) self.cpu = chipsec.hal.cpu.CPU( self ) #self.cr = CrRegs ( self ) - self.cpuid = CpuID ( self ) + #self.cpuid = CpuID ( self ) + self.msgbus = MsgBus ( self ) # # All HAL components which use above 'basic primitive' HAL components # should be instantiated in modules/utilcmd with an instance of chipset @@ -273,6 +273,7 @@ class Chipset: self.code = data_dict['code'].lower() self.longname = data_dict['longname'] self.id = data_dict['id'] + else: _unknown_platform = True self.longname = 'UnknownPlatform' @@ -512,8 +513,11 @@ def read_register( _cs, reg_name, cpu_thread=0 ): size = int(reg['size'],16) reg_value = _cs.io._read_port( port, size ) elif RegisterType.IOBAR == rtype: - iobar = chipsec.hal.iobar.iobar( _cs ) - reg_value = iobar.read_IO_BAR_reg( reg['bar'], int(reg['offset'],16), int(reg['size'],16) ) + _iobar = chipsec.hal.iobar.iobar( _cs ) + reg_value = _iobar.read_IO_BAR_reg( reg['bar'], int(reg['offset'],16), int(reg['size'],16) ) + elif RegisterType.MSGBUS == rtype: + reg_value = _cs.msgbus.message_reg_read( int(reg['port'],16), int(reg['offset'],16) ) + return reg_value def write_register( _cs, reg_name, reg_value, cpu_thread=0 ): @@ -539,13 +543,15 @@ def write_register( _cs, reg_name, reg_value, cpu_thread=0 ): eax = (reg_value & 0xFFFFFFFF) edx = ((reg_value >> 32) & 0xFFFFFFFF) _cs.msr.write_msr( cpu_thread, int(reg['msr'],16), eax, edx ) - elif RegisterType.PORT == rtype: + elif RegisterType.PORTIO == rtype: port = int(reg['port'],16) size = int(reg['size'],16) _cs.io._write_port( port, reg_value, size ) elif RegisterType.IOBAR == rtype: - iobar = chipsec.hal.iobar( _cs ) - iobar.write_IO_BAR_reg( reg['bar'], int(reg['offset'],16), reg_value ) + _iobar = chipsec.hal.iobar.iobar( _cs ) + _iobar.write_IO_BAR_reg( reg['bar'], int(reg['offset'],16), int(reg['size'],16), reg_value ) + elif RegisterType.MSGBUS == rtype: + _cs.msgbus.message_reg_write( int(reg['port'],16), int(reg['offset'],16), reg_value ) def read_register_dict( _cs, reg_name): reg_value = read_register( _cs, reg_name) @@ -568,21 +574,22 @@ def get_register_field( _cs, reg_name, reg_value, field_name, preserve_field_pos if preserve_field_position: return reg_value & (field_mask << field_bit) else: return (reg_value >> field_bit) & field_mask -def set_register_field( _cs, reg_name, reg_value, field_name, field_value ): +def set_register_field( _cs, reg_name, reg_value, field_name, field_value, preserve_field_position=False ): field_attrs = get_register_def( _cs, reg_name )['FIELDS'][field_name] field_bit = int(field_attrs['bit']) field_mask = (1 << int(field_attrs['size'])) - 1 - reg_value &= ~(field_mask << field_bit) - reg_value |= ((field_value & field_mask) << field_bit) + reg_value &= ~(field_mask << field_bit) # keep other fields + if preserve_field_position: reg_value |= (field_value & (field_mask << field_bit)) + else: reg_value |= ((field_value & field_mask) << field_bit) return reg_value def read_register_field( _cs, reg_name, field_name, preserve_field_position=False, cpu_thread=0 ): reg_value = read_register( _cs, reg_name ) return get_register_field( _cs, reg_name, reg_value, field_name, preserve_field_position ) -def write_register_field( _cs, reg_name, field_name, field_value, cpu_thread=0 ): +def write_register_field( _cs, reg_name, field_name, field_value, preserve_field_position=False, cpu_thread=0 ): reg_value = read_register( _cs, reg_name, cpu_thread ) - reg_value_new = set_register_field( _cs, reg_name, reg_value, field_name, field_value ) + reg_value_new = set_register_field( _cs, reg_name, reg_value, field_name, field_value, preserve_field_position ) #logger().log("set register %s (0x%x) field %s = 0x%x ==> 0x%x" % (reg_name, reg_value, field_name, field_value, reg_value_new)) return write_register( _cs, reg_name, reg_value_new, cpu_thread ) @@ -632,6 +639,8 @@ def print_register( _cs, reg_name, reg_val ): reg_str = "[*] %s = %s << %s (I/O port 0x%X)" % (reg_name, reg_val_str, reg['desc'], int(reg['port'],16)) elif RegisterType.IOBAR == rtype: reg_str = "[*] %s = %s << %s (I/O %s + 0x%X)" % (reg_name, reg_val_str, reg['desc'], reg['bar'], int(reg['offset'],16)) + elif RegisterType.MSGBUS == rtype: + reg_str = "[*] %s = %s << %s (msgbus port 0x%X, off 0x%X)" % (reg_name, reg_val_str, reg['desc'], int(reg['port'],16), int(reg['offset'],16)) reg_str += _register_fields_str( reg, reg_val ) logger().log( reg_str ) @@ -653,6 +662,12 @@ def set_control( _cs, control_name, control_value, cpu_thread=0 ): field = control['field'] return chipsec.chipset.write_register_field( _cs, reg, field, control_value, cpu_thread ) +def is_control_defined( _cs, control_name ): + try: + return (_cs.Cfg.CONTROLS[ control_name ] is not None) + except KeyError: + return False + _chipset = None diff --git a/source/tool/chipsec/defines.py b/source/tool/chipsec/defines.py index f19654f1..6c15cf1d 100644 --- a/source/tool/chipsec/defines.py +++ b/source/tool/chipsec/defines.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -32,6 +32,7 @@ # __chipsec/defines.py__ - common defines __version__ = '1.0' +import struct BIT0 = 0x0001 BIT1 = 0x0002 @@ -98,10 +99,15 @@ BIT61 = 0x2000000000000000 BIT62 = 0x4000000000000000 BIT63 = 0x8000000000000000 - +BOUNDARY_1KB = 0x400 +BOUNDARY_2KB = 0x800 BOUNDARY_4KB = 0x1000 BOUNDARY_1MB = 0x100000 +BOUNDARY_2MB = 0x200000 +BOUNDARY_4MB = 0x400000 BOUNDARY_8MB = 0x800000 +BOUNDARY_16MB = 0x1000000 +BOUNDARY_32MB = 0x2000000 BOUNDARY_64MB = 0x4000000 BOUNDARY_128MB = 0x8000000 BOUNDARY_256MB = 0x10000000 @@ -117,7 +123,13 @@ ALIGNED_64MB = 0x3FFFFFF ALIGNED_128MB = 0x7FFFFFF ALIGNED_256MB = 0xFFFFFFF -def scan_single_bit_mask(self,mask): +MASK_8b = 0xFF +MASK_16b = 0xFFFF +MASK_32b = 0xFFFFFFFF +MASK_64b = 0xFFFFFFFFFFFFFFFF + + +def scan_single_bit_mask(mask): for bit in range(0,7): if mask>>bit == 1: return bit @@ -129,3 +141,12 @@ COMPRESSION_TYPE_NONE = 0 COMPRESSION_TYPE_TIANO = 1 COMPRESSION_TYPE_LZMA = 2 COMPRESSION_TYPES = [COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_TIANO, COMPRESSION_TYPE_LZMA] + +def DB(val): + return struct.pack('>> write_command( command ) + >>> write_data( data ) + >>> read_data() + >>> read_memory( offset ) + >>> write_memory( offset, data ) + >>> read_memory_extended( word_offset ) + >>> write_memory_extended( word_offset, data ) + >>> read_range( start_offset, size ) + >>> write_range( start_offset, buffer ) + +""" + +from chipsec.logger import * +from chipsec.cfg.common import * + +# +# Embedded Controller ACPI ports +# +IO_PORT_EC_DATA = 0x62 +IO_PORT_EC_COMMAND = 0x66 +IO_PORT_EC_STATUS = 0x66 + +IO_PORT_EC_INDEX = 0x380 +IO_PORT_EC_INDEX_ADDRH = (IO_PORT_EC_INDEX + 0x1) +IO_PORT_EC_INDEX_ADDRL = (IO_PORT_EC_INDEX + 0x2) +IO_PORT_EC_INDEX_DATA = (IO_PORT_EC_INDEX + 0x3) + + +EC_STS_OBF = 0x01 # EC Output buffer full +EC_STS_IBF = 0x02 # EC Input buffer empty + + +# +# Embedded Controller ACPI commands +# These commands should be submitted to EC ACPI I/O ports +# +EC_COMMAND_ACPI_READ = 0x080 # Read EC ACPI memory +EC_COMMAND_ACPI_WRITE = 0x081 # Write EC ACPI memory +EC_COMMAND_ACPI_LOCK = 0x082 # Lock EC for burst use +EC_COMMAND_ACPI_UNLOCK = 0x083 # Unlock EC from burst use +EC_COMMAND_ACPI_QUERY = 0x084 # Query EC event +EC_COMMAND_ACPI_READ_EXT = 0x0F0 # Read EC ACPI extended memory +EC_COMMAND_ACPI_WRITE_EXT = 0x0F1 # Write EC ACPI extended memory + +class EC: + def __init__( self, cs ): + self.cs = cs + + + # + # EC ACPI memory access + # + + # Wait for EC input buffer empty + def _wait_ec_inbuf_empty( self ): + to = 1000 + while (self.cs.io.read_port_byte(IO_PORT_EC_STATUS) & EC_STS_IBF) and to: to = to - 1 + return True + + # Wait for EC output buffer full + def _wait_ec_outbuf_full( self ): + to = 1000 + while not ( self.cs.io.read_port_byte(IO_PORT_EC_STATUS) & EC_STS_OBF ) and to: to = to - 1 + return True + + def write_command( self, command ): + self._wait_ec_inbuf_empty() + return self.cs.io.write_port_byte( IO_PORT_EC_COMMAND, command ) + + def write_data( self, data ): + self._wait_ec_inbuf_empty() + return self.cs.io.write_port_byte( IO_PORT_EC_DATA, data ) + + def read_data( self ): + if not self._wait_ec_outbuf_full(): return None + return self.cs.io.read_port_byte( IO_PORT_EC_DATA ) + + def read_memory( self, offset ): + self.write_command( EC_COMMAND_ACPI_READ ) + self.write_data( offset ) + return self.read_data() + + def write_memory( self, offset, data ): + self.write_command( EC_COMMAND_ACPI_WRITE ) + self.write_data( offset ) + return self.write_data( data ) + + def read_memory_extended( self, word_offset ): + self.write_command( EC_COMMAND_ACPI_READ ) + self.write_data( 0x2 ) + self.write_data( word_offset & 0xFF ) + self.write_command( EC_COMMAND_ACPI_READ_EXT ) + self.write_data( word_offset >> 8 ) + return self.read_data() + + def write_memory_extended( self, word_offset, data ): + self.write_command( EC_COMMAND_ACPI_WRITE ) + self.write_data( 0x2 ) + self.write_data( word_offset & 0xFF ) + self.write_command( EC_COMMAND_ACPI_WRITE_EXT ) + self.write_data( word_offset >> 8 ) + return self.write_data( data ) + + def read_range( self, start_offset, size ): + buffer = [chr(0xFF)]*size + #self.write_command( EC_COMMAND_ACPI_READ ) + for i in range (size): + #self.write_data( start_offset + i ) + #buffer[i] = chr( self.read_data() ) + if start_offset + i < 0x100: + buffer[i] = chr( self.read_memory( start_offset + i ) ) + else: + buffer[i] = chr( self.read_memory_extended( start_offset + i ) ) + + if logger().VERBOSE: + logger().log( "[ec] read EC memory from offset %X size %X:" % (start_offset, size) ) + print_buffer( buffer ) + return buffer + + def write_range( self, start_offset, buffer ): + size = len(buffer) + for i in range(size): + self.write_memory( start_offset + i, ord(buffer[i]) ) + if logger().VERBOSE: + logger().log( "[ec] write EC memory to offset %X size %X:" % (start_offset, size) ) + print_buffer( buffer ) + return True + + # + # EC Intex I/O access + # + def read_idx( self, offset ): + self.cs.io.write_port_byte( IO_PORT_EC_INDEX_ADDRL, offset & 0xFF ) + self.cs.io.write_port_byte( IO_PORT_EC_INDEX_ADDRH, (offset>>8) & 0xFF ) + value = self.cs.io.read_port_byte( IO_PORT_EC_INDEX_DATA ) + if logger().HAL: logger().log( "[ec] index read: offset 0x%02X > 0x%02X:" % (offset, value) ) + return value + + def write_idx( self, offset, value ): + if logger().HAL: logger().log( "[ec] index write: offset 0x%02X < 0x%02X:" % (offset, value) ) + self.cs.io.write_port_byte( IO_PORT_EC_INDEX_ADDRL, offset & 0xFF ) + self.cs.io.write_port_byte( IO_PORT_EC_INDEX_ADDRH, (offset>>8) & 0xFF ) + self.cs.io.write_port_byte( IO_PORT_EC_INDEX_DATA, value & 0xFF ) + return True diff --git a/source/tool/chipsec/hal/hal_base.py b/source/tool/chipsec/hal/hal_base.py index 47549bb2..de4ffe7a 100644 --- a/source/tool/chipsec/hal/hal_base.py +++ b/source/tool/chipsec/hal/hal_base.py @@ -1,5 +1,5 @@ #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -26,6 +26,7 @@ Base for HAL Components import chipsec.logger import chipsec.chipset +import chipsec.defines class HALBase( object ): def __init__(self): diff --git a/source/tool/chipsec/hal/iobar.py b/source/tool/chipsec/hal/iobar.py index 16cf558a..faaac3c3 100644 --- a/source/tool/chipsec/hal/iobar.py +++ b/source/tool/chipsec/hal/iobar.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -104,7 +104,7 @@ class iobar: # Read I/O register from I/O range defined by I/O BAR name # def read_IO_BAR_reg( self, bar_name, offset, size ): - if logger().VERBOSE: logger().log('[iobar] read %s + %u (%u)' % (bar_name, offset, size)) + if logger().VERBOSE: logger().log('[iobar] read %s + 0x%X (%u)' % (bar_name, offset, size)) (bar_base,bar_size) = self.get_IO_BAR_base_address( bar_name ) io_port = bar_base + offset if offset > bar_size: logger().warn( 'offset 0x%X is ouside %s size (0x%X)' % (offset,bar_name,size) ) @@ -117,7 +117,7 @@ class iobar: # def write_IO_BAR_reg( self, bar_name, offset, size, value ): (bar_base,bar_size) = self.get_IO_BAR_base_address( bar_name ) - if logger().VERBOSE: logger().log( '[iobar] write IO reg 0x%X from %s (0x%X): 0x%X' % (bar_name,bar_base,offset,value) ) + if logger().VERBOSE: logger().log( '[iobar] write %s + 0x%X (%u): 0x%X' % (bar_name,offset,size,value) ) io_port = bar_base + offset if offset > bar_size: logger().warn( 'offset 0x%X is ouside %s size (0x%X)' % (offset,bar_name,size) ) return self.cs.io._write_port( io_port, value, size ) diff --git a/source/tool/chipsec/hal/iommu.py b/source/tool/chipsec/hal/iommu.py index 5f10fa58..a4580605 100644 --- a/source/tool/chipsec/hal/iommu.py +++ b/source/tool/chipsec/hal/iommu.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -30,6 +30,7 @@ from chipsec.logger import * import chipsec.hal.iobar import chipsec.hal.mmio +import chipsec.hal.paging IOMMU_ENGINE_DEFAULT = 'VTD' IOMMU_ENGINE_GFX = 'GFXVTD' @@ -90,7 +91,7 @@ class iommu: logger().log( "Engine enabled : %d" % enabled ) te = self.is_IOMMU_Translation_Enabled( iommu_engine ) logger().log( "Translation enabled : %d" % te ) - rtaddr_rta = chipsec.chipset.read_register_field( self.cs, vtd + '_RTADDR', 'RTA' ) + rtaddr_rta = chipsec.chipset.read_register_field( self.cs, vtd + '_RTADDR', 'RTA', True ) logger().log( "Root Table Address : 0x%016X" % rtaddr_rta ) irta = chipsec.chipset.read_register_field( self.cs, vtd + '_IRTA', 'IRTA' ) logger().log( "Interrupt Remapping Table : 0x%016X" % irta ) @@ -116,6 +117,38 @@ class iommu: chipsec.chipset.print_register( self.cs, vtd + '_ECAP', ecap_reg ) logger().log( '' ) + + def dump_IOMMU_page_tables( self, iommu_engine ): + vtd = IOMMU_ENGINES[ iommu_engine ] + te = self.is_IOMMU_Translation_Enabled( iommu_engine ) + logger().log( "[iommu] Translation enabled : %d" % te ) + rtaddr_reg = chipsec.chipset.read_register( self.cs, vtd + '_RTADDR' ) + rtaddr_rta = chipsec.chipset.get_register_field( self.cs, vtd + '_RTADDR', rtaddr_reg, 'RTA', True ) + rtaddr_rtt = chipsec.chipset.get_register_field( self.cs, vtd + '_RTADDR', rtaddr_reg, 'RTT' ) + #rtaddr_rta = chipsec.chipset.read_register_field( self.cs, vtd + '_RTADDR', 'RTA', True ) + #rtaddr_rtt = chipsec.chipset.read_register_field( self.cs, vtd + '_RTADDR', 'RTT' ) + logger().log( "[iommu] Root Table Address/Type: 0x%016X/%X" % (rtaddr_rta,rtaddr_rtt) ) + + ecap_reg = chipsec.chipset.read_register( self.cs, vtd + '_ECAP' ) + ecs = chipsec.chipset.get_register_field( self.cs, vtd + '_ECAP', ecap_reg, 'ECS' ) + pasid = chipsec.chipset.get_register_field( self.cs, vtd + '_ECAP', ecap_reg, 'PASID' ) + logger().log( '[iommu] PASID / ECS : %x / %x' % (pasid, ecs)) + + if 0xFFFFFFFFFFFFFFFF != rtaddr_reg: + if te: + logger().log( '[iommu] dumping VT-d page table hierarchy at 0x%016X (vtd_context_%08x)..' % (rtaddr_rta,rtaddr_rta) ) + paging_vtd = chipsec.hal.paging.c_vtd_page_tables( self.cs ) + paging_vtd.read_vtd_context('vtd_context_%08x' % rtaddr_rta, rtaddr_rta) + logger().log( '[iommu] total VTd domains: %d' % len(paging_vtd.domains)) + for domain in paging_vtd.domains: + paging_vtd.read_pt_and_show_status('vtd_%08x' % domain, 'VTd', domain) + #if paging_vtd.failure: logger().error( "couldn't dump VT-d page tables" ) + else: + logger().log( "[iommu] translation via VT-d engine '%s' is not enabled" % iommu_engine ) + else: + logger().error( "cannot access VT-d registers" ) + + def dump_IOMMU_status( self, iommu_engine ): vtd = IOMMU_ENGINES[ iommu_engine ] logger().log( "==================================================================" ) diff --git a/source/tool/chipsec/hal/msgbus.py b/source/tool/chipsec/hal/msgbus.py new file mode 100644 index 00000000..5f6d1acf --- /dev/null +++ b/source/tool/chipsec/hal/msgbus.py @@ -0,0 +1,204 @@ +#!/usr/local/bin/python +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# (c) 2010-2016 Intel Corporation +# +# ------------------------------------------------------------------------------- + +""" +Access to message bus (IOSF sideband) interface registers on Intel SoCs + +References: +- Intel(R) Atom(TM) Processor E3800 Product Family Datasheet, May 2016, Revision 4.0 + http://www.intel.com/content/www/us/en/embedded/products/bay-trail/atom-e3800-family-datasheet.html (sections 3.6 and 13.4.6 - 13.4.8) +- Intel(R) Atom(TM) Processor D2000 and N2000 Series Datasheet, Volume 2, July 2012, Revision 003 + http://www.intel.com/content/dam/doc/datasheet/atom-d2000-n2000-vol-2-datasheet.pdf (section 1.10.2) + +usage: + >>> msgbus_reg_read( port, register ) + >>> msgbus_reg_write( port, register, data ) + + >>> msgbus_read_message( port, register, opcode ) + >>> msgbus_write_message( port, register, opcode, data ) + >>> msgbus_send_message( port, register, opcode, data ) +""" + +__version__ = '1.0' + +import struct +import sys +import os.path +import chipsec.chipset +from chipsec.logger import logger + +# +# IOSF Message bus message opcodes +# Reference: http://lxr.free-electrons.com/source/arch/x86/include/asm/iosf_mbi.h +# +class MessageBusOpcode: + MB_OPCODE_MMIO_READ = 0x00 + MB_OPCODE_MMIO_WRITE = 0x01 + MB_OPCODE_IO_READ = 0x02 + MB_OPCODE_IO_WRITE = 0x03 + MB_OPCODE_CFG_READ = 0x04 + MB_OPCODE_CFG_WRITE = 0x05 + MB_OPCODE_CR_READ = 0x06 + MB_OPCODE_CR_WRITE = 0x07 + MB_OPCODE_REG_READ = 0x10 + MB_OPCODE_REG_WRITE = 0x11 + MB_OPCODE_ESRAM_READ = 0x12 + MB_OPCODE_ESRAM_WRITE = 0x13 + +# +# IOSF Message bus unit ports +# Reference: http://lxr.free-electrons.com/source/arch/x86/include/asm/iosf_mbi.h +# @TODO: move these to per-platform XML config? +# +class MessageBusPort_Atom: + UNIT_AUNIT = 0x00 + UNIT_SMC = 0x01 + UNIT_CPU = 0x02 + UNIT_BUNIT = 0x03 + UNIT_PMC = 0x04 + UNIT_GFX = 0x06 + UNIT_SMI = 0x0C + UNIT_USB = 0x43 + UNIT_SATA = 0xA3 + UNIT_PCIE = 0xA6 + +class MessageBusPort_Quark: + UNIT_HBA = 0x00 + UNIT_HB = 0x03 + UNIT_RMU = 0x04 + UNIT_MM = 0x05 + UNIT_SOC = 0x31 + + +def MB_MESSAGE_MCR( _cs, port, reg, opcode ): + mcr = 0x0 + mcr = chipsec.chipset.set_register_field( _cs, 'MSG_CTRL_REG', mcr, 'MESSAGE_WR_BYTE_ENABLES', 0xF ) + mcr = chipsec.chipset.set_register_field( _cs, 'MSG_CTRL_REG', mcr, 'MESSAGE_ADDRESS_OFFSET', reg ) + mcr = chipsec.chipset.set_register_field( _cs, 'MSG_CTRL_REG', mcr, 'MESSAGE_PORT', port ) + mcr = chipsec.chipset.set_register_field( _cs, 'MSG_CTRL_REG', mcr, 'MESSAGE_OPCODE', opcode ) + return mcr + +def MB_MESSAGE_MCRX( _cs, reg ): + mcrx = 0x0 + mcrx = chipsec.chipset.set_register_field( _cs, 'MSG_CTRL_REG_EXT', mcrx, 'MESSAGE_ADDRESS_OFFSET_EXT', reg, preserve_field_position=True ) + return mcrx + +def MB_MESSAGE_MDR( _cs, data ): + mdr = 0x0 + mdr = chipsec.chipset.set_register_field( _cs, 'MSG_DATA_REG', mdr, 'MESSAGE_DATA', data ) + return mdr + + +class MsgBusRuntimeError (RuntimeError): + pass + +class MsgBus: + + def __init__( self, cs ): + self.helper = cs.helper + self.cs = cs + + + # + # Issues read message on the message bus + # + def msgbus_read_message( self, port, register, opcode ): + mcr = MB_MESSAGE_MCR (self.cs, port, register, opcode) + mcrx = MB_MESSAGE_MCRX(self.cs, register) + + if logger().HAL: logger().log( "[msgbus] read: port 0x%02X + 0x%08X (op = 0x%02X)" % (port, register, opcode) ) + if logger().VERBOSE: logger().log( "[msgbus] MCR = 0x%08X, MCRX = 0x%08X" % (mcr, mcrx) ) + + mdr_out = self.helper.msgbus_send_read_message( mcr, mcrx ) + + if logger().HAL: logger().log( "[msgbus] < 0x%08X" % mdr_out ) + + return mdr_out + + # + # Issues write message on the message bus + # + def msgbus_write_message( self, port, register, opcode, data ): + mcr = MB_MESSAGE_MCR (self.cs, port, register, opcode) + mcrx = MB_MESSAGE_MCRX(self.cs, register) + mdr = MB_MESSAGE_MDR (self.cs, data) + + if logger().HAL: logger().log( "[msgbus] write: port 0x%02X + 0x%08X (op = 0x%02X) < data = 0x%08X" % (port, register, opcode, data) ) + if logger().VERBOSE: logger().log( "[msgbus] MCR = 0x%08X, MCRX = 0x%08X, MDR = 0x%08X" % (mcr, mcrx, mdr) ) + + return self.helper.msgbus_send_write_message( mcr, mcrx, mdr ) + + # + # Issues generic message on the message bus + # + def msgbus_send_message( self, port, register, opcode, data=None ): + mcr = MB_MESSAGE_MCR(self.cs, port, register, opcode) + mcrx = MB_MESSAGE_MCRX(self.cs, register) + mdr = None if data is None else MB_MESSAGE_MDR(self.cs, data) + + if logger().HAL: + logger().log( "[msgbus] message: port 0x%02X + 0x%08X (op = 0x%02X)" % (port, register, opcode) ) + if data is not None: logger().log( "[msgbus] data = 0x%08X" % data ) + if logger().VERBOSE: logger().log( "[msgbus] MCR = 0x%08X, MCRX = 0x%08X, MDR = 0x%08X" % (mcr, mcrx, mdr) ) + + mdr_out = self.helper.msgbus_send_message( mcr, mcrx, mdr ) + + if logger().HAL: logger().log( "[msgbus] < 0x%08X" % mdr_out ) + + return mdr_out + + # + # Message bus register read/write + # + + def msgbus_reg_read( self, port, register ): + return self.msgbus_read_message( port, register, MessageBusOpcode.MB_OPCODE_REG_READ ) + + def msgbus_reg_write( self, port, register, data ): + return self.msgbus_write_message( port, register, MessageBusOpcode.MB_OPCODE_REG_WRITE, data ) + + """ + # py implementation of msgbus -- doesn't seem to work properly becaise it's not atomic + def msgbus_send_message( self, port, register, opcode, data=None ): + if logger().HAL: + logger().log( "[msgbus] message - port: 0x%02X, reg: 0x%08X (op: 0x%02X)" % (port, register, opcode) ) + if data is not None: logger().log( "[msgbus] message - data: 0x%08X" % data ) + if (register & 0xFFFFFF00): + # write extended register address (bits [31:08]) to Message Control Register Extension (MCRX) + chipsec.chipset.write_register_field( self.cs, 'MSG_CTRL_REG_EXT', 'MESSAGE_ADDRESS_OFFSET_EXT', register, preserve_field_position=True ) + res = None + # write data to Message Data Register (MDR) for writes + if data is not None: chipsec.chipset.write_register( self.cs, 'MSG_DATA_REG', data ) + # write message (byte enables, address bits [08:00], port and opcode) to Message Control Register (MCR) + chipsec.chipset.write_register( self.cs, 'MSG_CTRL_REG', MB_MESSAGE(self.cs, port, register, opcode) ) + # read the data from Message Data Register (MDR) for reads + if data is None: res = chipsec.chipset.read_register( self.cs, 'MSG_DATA_REG' ) + return res + """ diff --git a/source/tool/chipsec/hal/paging.py b/source/tool/chipsec/hal/paging.py new file mode 100644 index 00000000..fb12d277 --- /dev/null +++ b/source/tool/chipsec/hal/paging.py @@ -0,0 +1,604 @@ +#!/usr/local/bin/python +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# (c) 2010-2016 Intel Corporation +# +# ------------------------------------------------------------------------------- +## \addtogroup hal +# chipsec/hal/paging.py +# ==================== +# x64/IA-64 Paging functionality including x86 page tables, Extended Page Tables (EPT) and VT-d page tables +# +# ~~~ + +import os +import sys +import time +import struct + +import chipsec.defines +from chipsec.logger import logger + +ADDR_MASK = chipsec.defines.MASK_64b +MAXPHYADDR = 0x000FFFFFFFFFF000 + +SIZE_4KB = chipsec.defines.BOUNDARY_4KB +SIZE_2MB = chipsec.defines.BOUNDARY_2MB +SIZE_1GB = chipsec.defines.BOUNDARY_1GB +ADDR_4KB = 0xFFFFFFFFFFFFF000 & MAXPHYADDR +ADDR_2MB = 0xFFFFFFFFFFE00000 & MAXPHYADDR +ADDR_1GB = 0xFFFFFFFFC0000000 & MAXPHYADDR + +class InvalidMemoryAddress (RuntimeError): + pass + + +class c_translation(object): + + def __init__(self): + self.translation = {} + + def is_translation_exist(self, addr, mask, size): + return ((addr & mask) in self.translation) and (self.translation[addr & mask]['size'] == size) + + def get_translation(self, addr): + if len(self.translation) == 0: + return addr + ADDR_4KB = 0xFFFFFFFFFFFFF000 + ADDR_2MB = 0xFFFFFFFFFFE00000 + ADDR_1GB = 0xFFFFFFFFC0000000 + if self.is_translation_exist(addr, ADDR_4KB, '4KB'): + result = self.translation[addr & ADDR_4KB]['addr'] | (addr & ~ADDR_4KB) + elif self.is_translation_exist(addr, ADDR_2MB, '2MB'): + result = self.translation[addr & ADDR_2MB]['addr'] | (addr & ~ADDR_2MB) + elif self.is_translation_exist(addr, ADDR_1GB, '1GB'): + result = self.translation[addr & ADDR_1GB]['addr'] | (addr & ~ADDR_1GB) + else: + result = None + return result + + def get_pages_by_physaddr(self, addr): + SIZE = {'4KB': ADDR_4KB, '2MB': ADDR_2MB, '1GB': ADDR_1GB} + result = [] + for i in self.translation.keys(): + page = self.translation[i] + size = SIZE[page['size']] + if (page['addr'] & size) == (addr & size): + result.append(page) + return result + + def get_address_space(self): + total = 0 + mem_range = self.get_mem_range() + for i in mem_range: + total += i[1] - i[0] + return total + + def get_mem_range(self, noattr=False): + SIZE = {'4KB': SIZE_4KB, '2MB': SIZE_2MB, '1GB': SIZE_1GB} + perm = {self.translation[a]['addr']:self.translation[a] for a in self.translation.keys()} + mem_range = [] + for addr in sorted(perm.keys()): + attr = perm[addr]['attr'] + size = SIZE[perm[addr]['size']] + if noattr: + attr = '' + if (mem_range == []): + mem_range += [[addr, addr + size, attr]] + elif (mem_range[-1][1] == addr) and (mem_range[-1][2] == attr): + mem_range[-1][1] += size + else: + mem_range += [[addr, addr + size, attr]] + return mem_range + + def add_page(self, virt, phys, size, attr): + if size not in ['4KB', '2MB', '4MB', '1GB']: + raise Exception('Invalid size!') + self.translation[virt] = {'addr': phys, 'size': size, 'attr': attr} + return + + def del_page(self, addr): + if addr in self.translation: + del self.translation[addr] + return + + def expand_pages(self, exp_size): + SIZE = {'1GB': '2MB', '2MB': '4KB'} + for virt in self.translation.keys(): + size = self.translation[virt]['size'] + attr = self.translation[virt]['attr'] + phys = self.translation[virt]['addr'] + pgsize = (1<<12) if size == '2MB' else (1<<20) + if size == exp_size: + for i in xrange(512): + self.add_page(virt + i*pgsize, phys + i*pgsize, SIZE[exp_size], attr) + return + +class c_reverse_translation(object): + + def __init__(self, translation): + self.reverse_translation = {} + for virt in translation.keys(): + phys = translation[virt]['addr'] + size = translation[virt]['size'] + attr = translation[virt]['attr'] + if phys not in self.reverse_translation: + self.reverse_translation[phys] = [] + self.reverse_translation[phys].append({'addr': virt, 'size': size, 'attr': attr}) + + def get_reverse_translation(self, addr): + ADDR_4KB = 0xFFFFFFFFFFFFF000 + addr &= ADDR_4KB + return self.reverse_translation[addr] if addr in self.reverse_translation else [] + + +class c_paging_memory_access(object): + + def __init__(self, cs): + self.cs = cs + + def readmem(self, name, addr, size = 4096): + return self.cs.mem.read_physical_mem(addr, size) + +class c_paging_with_2nd_level_translation(c_paging_memory_access): + + def __init__(self, cs): + c_paging_memory_access.__init__(self, cs) + self.translation_level2 = c_translation() + + def readmem(self, name, addr, size = 4096): + phys = self.translation_level2.get_translation(addr) + if phys != addr: + name += '_0x%08x' % phys + return super(c_paging_with_2nd_level_translation, self).readmem(name, phys, size) + +class c_paging(c_paging_with_2nd_level_translation, c_translation): + def __init__(self, cs): + c_paging_with_2nd_level_translation.__init__(self, cs) + c_translation.__init__(self) + # variables + self.did = 0 + self.out = sys.stdout + self.name = '' + self.pt = {} + self.pointer = None + self.failure = False + self.canonical_msb = 47 + + def get_canonical(self, va): + canonical_mask = (ADDR_MASK << (self.canonical_msb + 1)) & ADDR_MASK + canonical_va = (va | canonical_mask) if (va >> self.canonical_msb) & 0x1 else va + return canonical_va + + def get_field(self, entry, desc): + return (entry >> desc['offset']) & desc['mask'] + + def set_field(self, value, desc): + return (value & desc['mask']) << desc['offset'] + + def read_entries(self, info, addr, size = 8): + data = self.readmem('%s_%s_0x%08x' % (self.name, info, addr), addr, 0x1000) + entries = struct.unpack('<512Q', data) + if size == 16: + entries = [[entries[i], entries[i + 1]] for i in xrange(0, 512, 2)] + + same = True + for i in xrange(len(entries)): + same = same and (entries[0] == entries[i]) + if same: + return [entries[0]] + return entries + + def print_info(self, name): + logger().log('\n %s physical address ranges:' % name) + mem_range = self.get_mem_range() + for index in xrange(len(mem_range)): + i = mem_range[index] + logger().log(' 0x%013x - 0x%013x %8d %s' % (i[0], i[1] - 1, (i[1] - i[0]) >> 12, i[2])) + + logger().log('\n %s pages:' % name) + for i in sorted(self.pt.keys()): + logger().log(' 0x%013x %s' % (i, self.pt[i])) + logger().log('\n') + logger().log(' %s size: %d KB, address space: %d MB' % (name, len(self.pt.keys()) * 4, self.get_address_space() >> 20)) + return + + def check_misconfig(self, addr_list): + addr_list = [x & MAXPHYADDR for x in addr_list] + mem_range = self.get_mem_range() + for addr in addr_list: + for i in xrange(len(mem_range)): + if (mem_range[i][0] <= addr) and (addr < mem_range[i][1]): + print ('*** WARNING: PAGE TABLES MISCONFIGURATION 0x%013x' % addr) + return + + def save_configuration(self, path): + cfg = open(path, 'w') + try: + cfg.write(str(self.translation_level2.translation) + '\n') + cfg.write(str(self.translation) + '\n') + cfg.write(str(self.pt)) + finally: + cfg.close() + return + + def load_configuration(self, path): + cfg = open(path, 'r') + try: + self.translation_level2.translation = eval(cfg.readline()) + self.translation = eval(cfg.readline()) + self.pt = eval(cfg.readline()) + finally: + cfg.close() + return + + def read_pt_and_show_status(self, path, name, ptr): + #txt = open(path, 'w') + try: + if logger().HAL: logger().log( '[paging] reading %s page tables at 0x%016X ...' % (name, ptr) ) + #self.out = txt + self.read_page_tables(ptr) + self.print_info('[paging] %s page tables' % name) + #self.out = sys.stdout + self.failure = False + if logger().HAL: logger().log( '[paging] size: %d KB, address space: %d MB' % (len(self.pt.keys()) * 4, self.get_address_space() >> 20) ) + except InvalidMemoryAddress: + self.translation_level2.translation = {} + self.translation = {} + self.pt = {} + self.failure = True + logger().error( ' ERROR: Invalid %s Page Tables!' % name ) + #finally: + # #txt.close() + return + +class c_4level_page_tables(c_paging): + + def __init__(self, cs): + c_paging.__init__(self, cs) + # constants + self.PHYSICAL_ADDR_NAME = '' + self.PML4_INDX = {'mask': 0x1FF, 'offset': 39} + self.PDPT_INDX = {'mask': 0x1FF, 'offset': 30} + self.PD_INDX = {'mask': 0x1FF, 'offset': 21} + self.PT_INDX = {'mask': 0x1FF, 'offset': 12} + self.PT_NAME = ['EPTP', 'PML4E', 'PDPTE', 'PDE', 'PTE'] + self.PT_SIZE = ['', '', '1GB', '2MB', '4KB'] + + def get_virt_addr(self, pml4e_index, pdpte_index = 0, pde_index = 0, pte_index = 0): + ofs1 = self.set_field(pml4e_index, self.PML4_INDX) + ofs2 = self.set_field(pdpte_index, self.PDPT_INDX) + ofs3 = self.set_field(pde_index, self.PD_INDX) + ofs4 = self.set_field(pte_index, self.PT_INDX) + return (ofs1 | ofs2 | ofs3 | ofs4) + + def print_entry(self, lvl, pa, va = 0, perm = ''): + canonical_va = self.get_canonical(va) + info = ' %s%6s: %013X' % (' ' *lvl, self.PT_NAME[lvl], pa) + if perm <> '': + size = self.PT_SIZE[lvl] + info += ' - %s PAGE %s' % (size, perm) + info = info.ljust(64) + if pa == va: + info += '1:1 mapping' + else: + info += '%s: %013X' % (self.PHYSICAL_ADDR_NAME, canonical_va) + + self.add_page(canonical_va, pa, size, perm) + + logger().log(info) + return + + def read_page_tables(self, ptr): + addr = ptr & ADDR_4KB + self.pointer = addr + self.pt = {addr: 'pml4'} + self.translation = {} + self.print_entry(0, addr) + self.read_pml4(addr) + return + + def read_pml4(self, addr): + pml4 = self.read_entries('pml4', addr) + for pml4e_index in xrange(len(pml4)): + pml4e = pml4[pml4e_index] + if self.is_present(pml4e): + addr = pml4e & ADDR_4KB + self.pt[addr] = 'pdpt' + self.print_entry(1, addr) + self.read_pdpt(addr, pml4e_index) + return + + def read_pdpt(self, addr, pml4e_index): + pdpt = self.read_entries('pdpt', addr) + for pdpte_index in xrange(len(pdpt)): + pdpte = pdpt[pdpte_index] + if self.is_present(pdpte): + if self.is_bigpage(pdpte): + virt = self.get_virt_addr(pml4e_index, pdpte_index) + phys = pdpte & ADDR_1GB + self.print_entry(2, phys, virt, self.get_attr(pdpte)) + else: + addr = pdpte & ADDR_4KB + self.pt[addr] = 'pd' + self.print_entry(2, addr) + self.read_pd(addr, pml4e_index, pdpte_index) + return + + def read_pd(self, addr, pml4e_index, pdpte_index): + pd = self.read_entries('pd', addr) + for pde_index in xrange(len(pd)): + pde = pd[pde_index] + if self.is_present(pde): + if self.is_bigpage(pde): + virt = self.get_virt_addr(pml4e_index, pdpte_index, pde_index) + phys = pde & ADDR_2MB + self.print_entry(3, phys, virt, self.get_attr(pde)) + else: + addr = pde & ADDR_4KB + self.pt[addr] = 'pt' + self.print_entry(3, addr) + self.read_pt(addr, pml4e_index, pdpte_index, pde_index) + return + + def read_pt(self, addr, pml4e_index, pdpte_index, pde_index): + pt = self.read_entries('pt', addr) + for pte_index in xrange(len(pt)): + pte = pt[pte_index] + if self.is_present(pte): + virt = self.get_virt_addr(pml4e_index, pdpte_index, pde_index, pte_index) + phys = pte & ADDR_4KB + self.print_entry(4, phys, virt, self.get_attr(pte)) + return + + def read_entry_by_virt_addr(self, virt): + if self.pointer == None: + raise Exception('Page Table pointer is undefined!') + addr = self.pointer + pml4 = self.read_entries('pml4', addr) + pml4e = pml4[self.get_field(virt, self.PML4_INDX)] + if self.is_present(pml4e): + addr = pml4e & ADDR_4KB + pdpt = self.read_entries('pdpt', addr) + pdpte = pdpt[self.get_field(virt, self.PDPT_INDX)] + if self.is_present(pdpte): + if self.is_bigpage(pdpte): + addr = (pdpte & ADDR_1GB) | (virt & ~ADDR_1GB) + return {'addr': addr, 'attr': self.get_attr(pdpte), 'size': '1GB'} + else: + addr = pdpte & ADDR_4KB + pd = self.read_entries('pd', addr) + pde = pd[self.get_field(virt, self.PD_INDX)] + if self.is_present(pde): + if self.is_bigpage(pde): + addr = (pde & ADDR_2MB) | (virt & ~ADDR_2MB) + return {'addr': addr, 'attr': self.get_attr(pde), 'size': '2MB'} + else: + addr = pde & ADDR_4KB + pt = self.read_entries('pt', addr) + pte = pt[self.get_field(virt, self.PT_INDX)] + if self.is_present(pte): + addr = (pte & ADDR_4KB) | (virt & ~ADDR_4KB) + return {'addr': addr, 'attr': self.get_attr(pte), 'size': '4KB'} + return None + +class c_ia32e_page_tables(c_4level_page_tables): + + def __init__(self, cs): + c_4level_page_tables.__init__(self, cs) + # constants + self.PHYSICAL_ADDR_NAME = 'VA' + self.PT_NAME = ['CR3P', 'PML4E', 'PDPTE', 'PDE', 'PTE'] + self.P = {'mask': 0x1, 'offset': 0} + self.RW = {'mask': 0x1, 'offset': 1} + self.US = {'mask': 0x1, 'offset': 2} + self.BIGPAGE = {'mask': 0x1, 'offset': 7} + + def is_present(self, entry): + return self.get_field(entry, self.P) <> 0 + + def is_bigpage(self, entry): + return self.get_field(entry, self.BIGPAGE) <> 0 + + def get_attr(self, entry): + RW_DESC = ['R', 'W'] + US_DESC = ['S', 'U'] + return RW_DESC[self.get_field(entry, self.RW)] + ' ' + US_DESC[self.get_field(entry, self.US)] + +class c_pae_page_tables(c_ia32e_page_tables): + + def __init__(self): + c_ia32e_page_tables.__init__(self) + # constants + self.PML4_INDX = {'mask': 0x000, 'offset': 39} + self.PDPT_INDX = {'mask': 0x003, 'offset': 30} + self.PT_NAME = ['', 'CR3', 'PDPTE', 'PDE', 'PTE'] + + def read_page_tables(self, ptr): + addr = ptr & ADDR_4KB + self.pointer = addr + self.pt = {addr: 'pdpt'} + self.translation = {} + self.print_entry(1, addr) + self.read_pdpt(addr) + return + + def read_pml4(self, addr): + raise Exception('PAE Page tables have no PML4!') + return + + def read_pdpt(self, addr): + pdpt = self.read_entries('pdpt', addr) + for pdpte_index in xrange(4): + pdpte = pdpt[pdpte_index] + if self.is_present(pdpte): + if self.is_bigpage(pdpte): + virt = self.get_virt_addr(0, pdpte_index) + phys = pdpte & ADDR_1GB + self.print_entry(2, phys, virt, self.get_attr(pdpte)) + else: + addr = pdpte & ADDR_4KB + self.pt[addr] = 'pd' + self.print_entry(2, addr) + self.read_pd(addr, 0, pdpte_index) + return + +class c_extended_page_tables(c_4level_page_tables): + + def __init__(self, cs): + c_4level_page_tables.__init__(self, cs) + # constants + self.PHYSICAL_ADDR_NAME = 'GPA' + self.XWR = {'mask': 0x7, 'offset': 0} + self.MEM_TYPE = {'mask': 0x7, 'offset': 3} + self.BIGPAGE = {'mask': 0x1, 'offset': 7} + self.canonical_msb = 63 + + def is_present(self, entry): + return self.get_field(entry, self.XWR) <> 0 + + def is_bigpage(self, entry): + return self.get_field(entry, self.BIGPAGE) <> 0 + + def get_attr(self, entry): + XWR_DESC = ['---', '--R', '-W-', '-WR', 'X--', 'X-R', 'XW-', 'XWR'] + MEM_DESC = ['UC', 'WC', '02', '03', 'WT', 'WP', 'WB', 'UC-'] + return XWR_DESC[self.get_field(entry, self.XWR)] + ' ' + MEM_DESC[self.get_field(entry, self.MEM_TYPE)] + + def read_pt_and_show_status(self, path, name, ptr): + super(c_extended_page_tables, self).read_pt_and_show_status(path, name, ptr) + self.check_misconfig(self.pt) + return + + def map_bigpage_1G(self, virt, i): + if self.pointer == None: + raise Exception('Page Table pointer is undefined!') + addr = self.pointer + pml4 = self.read_entries('pml4', addr) + pml4e = pml4[self.get_field(virt, self.PML4_INDX)] + if self.is_present(pml4e): + addr = pml4e & ADDR_4KB + pdpt = self.read_entries('pdpt', addr) + new_entry = struct.pack(' PA (0x%016X)' % (va, pa) ) + if logger().HAL: logger().log( '[mem] VA (0x%016X) -> PA (0x%016X)' % (va, pa) ) if error_code: logger().log( '[mem] Looks like VA (0x%016X) not mapped' % (va) ) return return pa + # Map physical address to virtual - #################################################################################### - # - # DEPRECATED - # Physical memory API using 64b Physical Address split into 32b High and Low parts - # - #################################################################################### + def map_io_space(self, pa, length, cache_type): + va = self.helper.map_io_space(pa, length, cache_type) + if logger().HAL: logger().log( '[mem] Mapped: PA = 0x%016X, VA = 0x%016X' % (pa, va) ) + return va - def read_phys_mem_64( self, phys_address_hi, phys_address_lo, length ): - out_buf = self.helper.read_phys_mem( phys_address_hi, phys_address_lo, length ) - return out_buf + # Free physical memory buffer - def read_phys_mem_dword_64(self, phys_address_hi, phys_address_lo ): - out_buf = self.read_phys_mem_64( phys_address_hi, phys_address_lo, 4 ) - try: - value = struct.unpack( 'L', out_buf.raw )[0] - except: - raise MemoryAccessError, "read_phys_mem did not return hex dword" - if logger().VERBOSE: - logger().log( '[mem] dword at PA = 0x%08X_%08X: 0x%08X' % (phys_address_hi, phys_address_lo, value) ) - return value + def free_physical_mem(self, pa): + self.helper.free_physical_mem(pa) + if logger().HAL: logger().log( '[mem] Deallocated : PA = 0x%016X' % pa ) + return - def read_phys_mem_word_64(self, phys_address_hi, phys_address_lo ): - out_buf = self.read_phys_mem_64( phys_address_hi, phys_address_lo, 2 ) - try: - value = struct.unpack( 'H', out_buf.raw )[0] - except: - raise MemoryAccessError, "read_phys_mem did not return hex word" - if logger().VERBOSE: - logger().log( '[mem] word at PA = 0x%08X_%08X: 0x%04X' % (phys_address_hi, phys_address_lo, value) ) - return value - - def read_phys_mem_byte_64(self, phys_address_hi, phys_address_lo ): - out_buf = self.read_phys_mem_64( phys_address_hi, phys_address_lo, 1 ) - try: - value = struct.unpack( 'B', out_buf.raw )[0] - except: - raise MemoryAccessError, "read_phys_mem did not return 1 Byte" - if logger().VERBOSE: - logger().log( '[mem] byte at PA = 0x%08X_%08X: 0x%02X' % (phys_address_hi, phys_address_lo, value) ) - return value - - def write_phys_mem_64( self, phys_address_hi, phys_address_lo, length, buf ): - return self.helper.write_phys_mem( phys_address_hi, phys_address_lo, length, buf ) - - def write_phys_mem_dword_64( self, phys_address_hi, phys_address_lo, dword_value ): - if logger().VERBOSE: - logger().log( '[mem] dword to PA = 0x%08X_%08X <- 0x%08X' % (phys_address_hi, phys_address_lo, dword_value) ) - return self.write_phys_mem_64( phys_address_hi, phys_address_lo, 4, struct.pack( 'I', dword_value ) ) - - def write_phys_mem_word_64( self, phys_address_hi, phys_address_lo, word_value ): - if logger().VERBOSE: - logger().log( '[mem] word to PA = 0x%08X_%08X <- 0x%04X' % (phys_address_hi, phys_address_lo, word_value) ) - return self.write_phys_mem_64( phys_address_hi, phys_address_lo, 2, struct.pack( 'H', word_value ) ) - - def write_phys_mem_byte_64( self, phys_address_hi, phys_address_lo, byte_value ): - if logger().VERBOSE: - logger().log( '[mem] byte to PA = 0x%08X_%08X <- 0x%02X' % (phys_address_hi, phys_address_lo, byte_value) ) - return self.write_phys_mem_64( phys_address_hi, phys_address_lo, 1, struct.pack( 'B', byte_value ) ) - - - #################################################################################### - # - # DEPRECATED - # Physical memory API using 32b Physical Address - # - #################################################################################### - - def read_phys_mem_byte(self, phys_address ): - return self.read_phys_mem_byte_64( 0, phys_address ) - - def read_phys_mem_word(self, phys_address ): - return self.read_phys_mem_word_64( 0, phys_address ) - - def read_phys_mem_dword(self, phys_address ): - return self.read_phys_mem_dword_64( 0, phys_address ) - - def read_phys_mem(self, phys_address, length ): - return self.read_phys_mem_64( 0, phys_address, length ) - - def write_phys_mem_byte( self, phys_address, byte_value ): - return self.write_phys_mem_byte_64( 0, phys_address, byte_value ) - - def write_phys_mem_word( self, phys_address, word_value ): - return self.write_phys_mem_word_64( 0, phys_address, word_value ) - - def write_phys_mem_dword( self, phys_address, dword_value ): - return self.write_phys_mem_dword_64( 0, phys_address, dword_value ) - - def write_phys_mem(self, phys_address, length, buf ): - return self.write_phys_mem_64( 0, phys_address, length, buf ) + def set_mem_bit(self, addr, bit): + addr += bit >> 3 + byte = self.read_physical_mem_byte(addr) + self.write_physical_mem_byte(addr, (byte | (0x1 << (bit & 0x7)))) + return byte diff --git a/source/tool/chipsec/hal/smbus.py b/source/tool/chipsec/hal/smbus.py index e2e1a4a1..9d8e3d40 100644 --- a/source/tool/chipsec/hal/smbus.py +++ b/source/tool/chipsec/hal/smbus.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -24,7 +24,6 @@ # ------------------------------------------------------------------------------- # # CHIPSEC: Platform Hardware Security Assessment Framework -# (c) 2010-2012 Intel Corporation # # ------------------------------------------------------------------------------- @@ -33,14 +32,33 @@ Access to SMBus Controller """ from chipsec.logger import * -#from chipsec.cfg.common import * import chipsec.hal.iobar +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: def __init__( self, cs ): self.cs = cs self.iobar = chipsec.hal.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' ): @@ -66,7 +84,7 @@ class SMBus: def is_SMBus_supported( self ): (did,vid) = self.cs.get_DeviceVendorID( 'SMBUS' ) - if logger().VERBOSE: logger().log( "[*] SMBus Controller (DID,VID) = (0x%04X,0x%04X)" % (did,vid) ) + if logger().VERBOSE: logger().log( "[smbus] SMBus Controller (DID,VID) = (0x%04X,0x%04X)" % (did,vid) ) if (0x8086 == vid): return True else: logger().error( "Unknown SMBus Controller (DID,VID) = (0x%04X,0x%04X)" % (did,vid) ) @@ -87,82 +105,120 @@ class SMBus: if 0 == (cmd & 0x1): chipsec.chipset.write_register( self.cs, 'SMBUS_CMD', (cmd|0x1) ) - def _wait_for_cycle( self, smbus_io_base ): - # wait for cycle to complete - #while True: - for i in range(1000): - sts = self.cs.io.read_port_byte( smbus_io_base ) - if (sts & 0x02): break - elif (sts & 0x04): - if logger().VERBOSE: logger().error( "SMBus cycle failed: Device error" ) - elif (sts & 0x08): - if logger().VERBOSE: logger().error( "SMBus cycle failed: Bus Error" ) - elif (sts & 0x10): - if logger().VERBOSE: logger().error( "SMBus cycle failed: Unknown Error" ) - return ((sts & 0x02) > 0) + # + # SMBus commands + # - def _read_byte( self, smbus_io_base, target_address, offset ): - self.cs.io.write_port_byte( smbus_io_base + 0x0, 0xFF ) # Clear status bits - ##self.cs.io.write_port_byte( smbus_io_base + 0x1, 0x1F ) - #for i in range(100): - # self.cs.io.write_port_byte( smbus_io_base + 0x0, 0xFF ) # Clear status bits - # sts = self.cs.io.read_port_byte( smbus_io_base ) - # if (0 == (sts & 0x9F)): break - #if (sts & 0x9F): - # logger().error( "SMBus is not ready for whatever reason" ) - # return 0xFF + # 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 = chipsec.chipset.read_register_field( self.cs, self.smb_reg_status, 'BUSY' ) + if 0 == busy: return True + return (0 == busy) - self.cs.io.write_port_byte( smbus_io_base + 0x4, (target_address | 0x1) ) # Byte Read from SMBus device at target_address - self.cs.io.write_port_byte( smbus_io_base + 0x3, offset ) # Byte offset - self.cs.io.write_port_byte( smbus_io_base + 0x2, 0x48 ) # Send command - # wait for cycle to complete - if not self._wait_for_cycle( smbus_io_base ): return 0xFF - # read the data - value = self.cs.io.read_port_byte( smbus_io_base + 0x5 ) - # Clear status bits - self.cs.io.write_port_byte( smbus_io_base + 0x0, 0xFF ) - return value - - def _write_byte( self, smbus_io_base, target_address, offset, value ): - self.cs.io.write_port_byte( smbus_io_base + 0x0, 0xFF ) # Clear status bits - self.cs.io.write_port_byte( smbus_io_base + 0x4, target_address ) # Byte Write to SMBus device at target_address - self.cs.io.write_port_byte( smbus_io_base + 0x3, offset ) # Byte offset - self.cs.io.write_port_byte( smbus_io_base + 0x5, value ) # Byte data to write - self.cs.io.write_port_byte( smbus_io_base + 0x2, 0x48 ) # Send command - # wait for cycle to complete - if not self._wait_for_cycle( smbus_io_base ): return False - # Clear status bits - self.cs.io.write_port_byte( smbus_io_base + 0x0, 0xFF ) - return True + # 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 = chipsec.chipset.read_register( self.cs, self.smb_reg_status ) + busy = chipsec.chipset.get_register_field( self.cs, self.smb_reg_status, sts, 'BUSY' ) + failed = chipsec.chipset.get_register_field( self.cs, self.smb_reg_status, sts, 'FAILED' ) + if 0 == busy: + #if logger().VERBOSE: + # intr = chipsec.chipset.get_register_field( self.cs, self.smb_reg_status, sts, 'INTR' ) + # logger().log( "[smbus]: INTR = %d" % 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 chipsec.chipset.register_has_field( self.cs, self.smb_reg_status, 'DEV_ERR' ): + if 1 == chipsec.chipset.get_register_field( self.cs, 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 chipsec.chipset.register_has_field( self.cs, self.smb_reg_status, 'BUS_ERR' ): + if 1 == chipsec.chipset.get_register_field( self.cs, 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 ): - smbus_io_base = self.get_SMBus_Base_Address() - value = self._read_byte( smbus_io_base, target_address, offset ) + # clear status bits + chipsec.chipset.write_register( self.cs, self.smb_reg_status, 0xFF ) + + # SMBus txn RW direction = Read, SMBus slave address = target_address + hst_sa = 0x0 + hst_sa = chipsec.chipset.set_register_field( self.cs, self.smb_reg_address, hst_sa, 'RW', SMBUS_COMMAND_READ ) + hst_sa = chipsec.chipset.set_register_field( self.cs, self.smb_reg_address, hst_sa, 'Address', target_address, True ) + chipsec.chipset.write_register( self.cs, self.smb_reg_address, hst_sa ) + # command data = byte offset (bus txn address) + chipsec.chipset.write_register_field( self.cs, self.smb_reg_command, 'DataOffset', offset ) + # command = Byte Data + #if chipsec.chipset.register_has_field( self.cs, self.smb_reg_control, 'SMB_CMD' ): + chipsec.chipset.write_register_field( self.cs, self.smb_reg_control, 'SMB_CMD', SMBUS_COMMAND_BYTE_DATA ) + # send SMBus txn + chipsec.chipset.write_register_field( self.cs, self.smb_reg_control, 'START', 1 ) + + # wait for cycle to complete + if not self._wait_for_cycle(): return 0xFF + # read the data + value = chipsec.chipset.read_register_field( self.cs, self.smb_reg_data0, 'Data' ) + # clear status bits + chipsec.chipset.write_register( self.cs, 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().VERBOSE: logger().log( "[smbus] read device %X off %X = %X" % (target_address, offset, value) ) return value def write_byte( self, target_address, offset, value ): - smbus_io_base = self.get_SMBus_Base_Address() - sts = self._write_byte( smbus_io_base, target_address, offset, value ) + # clear status bits + chipsec.chipset.write_register( self.cs, self.smb_reg_status, 0xFF ) + + # SMBus txn RW direction = Write, SMBus slave address = target_address + hst_sa = 0x0 + hst_sa = chipsec.chipset.set_register_field( self.cs, self.smb_reg_address, hst_sa, 'RW', SMBUS_COMMAND_WRITE ) + hst_sa = chipsec.chipset.set_register_field( self.cs, self.smb_reg_address, hst_sa, 'Address', target_address, True ) + chipsec.chipset.write_register( self.cs, self.smb_reg_address, hst_sa ) + # command data = byte offset (bus txn address) + chipsec.chipset.write_register_field( self.cs, self.smb_reg_command, 'DataOffset', offset ) + # write the data + chipsec.chipset.write_register_field( self.cs, self.smb_reg_data0, 'Data', value ) + # command = Byte Data + #if chipsec.chipset.register_has_field( self.cs, self.smb_reg_control, 'SMB_CMD' ): + chipsec.chipset.write_register_field( self.cs, self.smb_reg_control, 'SMB_CMD', SMBUS_COMMAND_BYTE_DATA ) + # send SMBus txn + chipsec.chipset.write_register_field( self.cs, self.smb_reg_control, 'START', 1 ) + + # wait for cycle to complete + if not self._wait_for_cycle(): return False + # clear status bits + chipsec.chipset.write_register( self.cs, 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().VERBOSE: logger().log( "[smbus] write to device %X off %X = %X" % (target_address, offset, value) ) - return sts + return True + def read_range( self, target_address, start_offset, size ): buffer = [chr(0xFF)]*size - smbus_io_base = self.get_SMBus_Base_Address() for i in range (size): - buffer[i] = chr( self._read_byte( smbus_io_base, target_address, start_offset + i ) ) - if logger().VERBOSE: - logger().log( "[smbus] read device %X from offset %X size %X:" % (target_address, start_offset, size) ) - print_buffer( buffer ) + buffer[i] = chr( self.read_byte( target_address, start_offset + i ) ) + if logger().HAL: + logger().log( "[smbus] reading %u bytes from device 0x%X at offset %X" % (size, target_address, start_offset) ) + #print_buffer( buffer ) return buffer def write_range( self, target_address, start_offset, buffer ): size = len(buffer) - smbus_io_base = self.get_SMBus_Base_Address() for i in range(size): - self._write_byte( smbus_io_base, target_address, start_offset + i, ord(buffer[i]) ) - if logger().VERBOSE: - logger().log( "[smbus] write device %X to offset %X size %X:" % (target_address, start_offset, size) ) - print_buffer( buffer ) + self.write_byte( target_address, start_offset + i, ord(buffer[i]) ) + if logger().HAL: + logger().log( "[smbus] writing %u bytes to device 0x%X at offset %X" % (size, target_address, start_offset) ) + #print_buffer( buffer ) return True diff --git a/source/tool/chipsec/hal/spd.py b/source/tool/chipsec/hal/spd.py index fc00f1cd..94e5a5db 100644 --- a/source/tool/chipsec/hal/spd.py +++ b/source/tool/chipsec/hal/spd.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -309,10 +309,16 @@ class SPD: return self.smbus.write_byte( device, offset, value ) def read_range( self, start_offset, size, device=SPD_SMBUS_ADDRESS ): - return self.smbus.read_range( device, start_offset, size ) + buffer = [chr(0xFF)]*size + for i in range (size): + buffer[i] = chr( self.read_byte( start_offset + i, device ) ) + return buffer def write_range( self, start_offset, buffer, device=SPD_SMBUS_ADDRESS ): - return self.smbus.write_range( device, start_offset, buffer ) + size = len(buffer) + for i in range(size): + self.write_byte( start_offset + i, ord(buffer[i]), device ) + return True def dump( self, device=SPD_SMBUS_ADDRESS ): buf = self.read_range( 0, 0x100, device ) @@ -383,7 +389,7 @@ class SPD: def decode( self, device=SPD_SMBUS_ADDRESS ): spd = None device_type = self.getDRAMDeviceType( device ) - spd_buffer = ''.join(self.dump()) + spd_buffer = ''.join(self.dump( device )) if DRAM_DEVICE_TYPE_DDR == device_type: spd = SPD_DDR ( *struct.unpack_from( SPD_DDR_FORMAT, spd_buffer ) ) diff --git a/source/tool/chipsec/hal/spi.py b/source/tool/chipsec/hal/spi.py index 470f832a..ec9bf2e0 100644 --- a/source/tool/chipsec/hal/spi.py +++ b/source/tool/chipsec/hal/spi.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -44,11 +44,10 @@ usage: If you want to change logic to read SPI Flash in 4 byte chunks: SPI_READ_WRITE_MAX_DBC = 4 - SPI write cycles operate on 4 byte chunks (not optimized yet) + @TBD: SPI write cycles operate on 4 byte chunks (not optimized yet) - Approximate performance (on 2 core HT Sandy Bridge CPU 2.6GHz): - SPI read: ~25 sec per 1MB (DBC=64) - SPI write: ~140 sec per 1MB (DBC=4) + Approximate performance (on 2-core SMT Intel Core i5-4300U (Haswell) CPU 1.9GHz): + SPI read: ~7 sec per 1MB (with DBC=64) """ __version__ = '1.0' @@ -57,6 +56,7 @@ import struct import sys import time +import chipsec.defines import chipsec.chipset from chipsec.file import * from chipsec.hal.hal_base import HALBase @@ -309,7 +309,7 @@ class SPI: logger().log( "BFPREG = %08X:" % bfpreg ) logger().log( " Base : %08X" % ((bfpreg & Cfg.PCH_RCBA_SPI_FREGx_BASE_MASK) << 12) ) logger().log( " Limit : %08X" % ((bfpreg & Cfg.PCH_RCBA_SPI_FREGx_LIMIT_MASK) >> 4) ) - logger().log( " Shadowed BIOS Select: %d" % ((bfpreg & Cfg.BIT31)>>31) ) + logger().log( " Shadowed BIOS Select: %d" % ((bfpreg & chipsec.defines.BIT31)>>31) ) def display_SPI_Ranges_Access_Permissions( self ): @@ -386,74 +386,29 @@ class SPI: if logger().VERBOSE: self.display_BIOS_write_protection() ble = chipsec.chipset.get_control( self.cs, 'BiosLockEnable' ) bioswe = chipsec.chipset.get_control( self.cs, 'BiosWriteEnable' ) - if ble and (not bioswe): - if logger().HAL: logger().log( "[spi] BIOS write protection is enabled" ) - return False - elif bioswe: - if logger().HAL: logger().log( "[spi] BIOS write protection is not enabled" ) - return True - else: - if logger().HAL: logger().log( "[spi] BIOS write protection is enabled but not locked. Disabling.." ) + smmbwp = chipsec.chipset.get_control( self.cs, 'SmmBiosWriteProtection' ) - # @TODO: hack - update to use write_register_field - reg_value = chipsec.chipset.read_register( self.cs, 'BC' ) - reg_value |= 0x1 - chipsec.chipset.write_register( self.cs, 'BC', reg_value ) + if smmbwp == 1: + if logger().HAL: logger().log( "[spi] SMM BIOS write protection (SmmBiosWriteProtection) is enabled" ) + + if bioswe == 1: + if logger().HAL: logger().log( "[spi] BIOS write protection (BiosWriteEnable) is not enabled" ) + return True + elif ble == 0: + if logger().HAL: logger().log( "[spi] BIOS write protection is enabled but not locked. Disabling.." ) + else: # bioswe == 0 and ble == 1 + if logger().HAL: logger().log( "[spi] BIOS write protection is enabled. Attempting to disable.." ) + + # Set BiosWriteEnable control bit + chipsec.chipset.set_control( self.cs, 'BiosWriteEnable', 1 ) # read BiosWriteEnable back to check if BIOS writes are enabled bioswe = chipsec.chipset.get_control( self.cs, 'BiosWriteEnable' ) + if logger().VERBOSE: self.display_BIOS_write_protection() - if logger().HAL: logger().log_important( "BIOS write protection is %s" % ('disabled' if bioswe else 'still enabled') ) - return bioswe + if logger().HAL: logger().log_important( "BIOS write protection is %s (BiosWriteEnable = %d)" % ('disabled' if bioswe else 'still enabled', bioswe) ) - - """ - def get_BIOS_Control_fallback( self ): - # - # BIOS Control (BC) 0:31:0 PCIe CFG register - # - reg_value = self.cs.pci.read_byte( 0, 31, 0, Cfg.LPC_BC_REG_OFF ) - BcRegister = Cfg.LPC_BC_REG( reg_value, (reg_value>>5)&0x1, (reg_value>>4)&0x1, (reg_value>>2)&0x3, (reg_value>>1)&0x1, reg_value&0x1 ) - return (BcRegister, reg_value) - - def get_BIOS_Control( self ): - if chipsec.chipset.is_register_defined( self.cs, 'BC' ): - reg_value = chipsec.chipset.read_register( self.cs, 'BC' ) - - BcRegister = Cfg.LPC_BC_REG( reg_value, \ - chipsec.chipset.get_register_field(self.cs, 'BC', reg_value, 'SMM_BWP'), \ - chipsec.chipset.get_register_field(self.cs, 'BC', reg_value, 'TSS'), \ - chipsec.chipset.get_register_field(self.cs, 'BC', reg_value, 'SRC'), \ - chipsec.chipset.get_register_field(self.cs, 'BC', reg_value, 'BLE'), \ - chipsec.chipset.get_register_field(self.cs, 'BC', reg_value, 'BIOSWE') ) - return (BcRegister, reg_value) - else: - if logger().HAL: logger().error( "Could not locate the definition of 'BIOS Control' register. Using hardcoded location (results may be incorrect).." ) - return self.get_BIOS_Control_fallback() - - def disable_BIOS_write_protection_OLD( self ): - (BcRegister, reg_value) = self.get_BIOS_Control() - if logger().VERBOSE: logger().log( BcRegister ) - - if BcRegister.BLE and (not BcRegister.BIOSWE): - logger().log( "[spi] BIOS write protection enabled" ) - return False - elif BcRegister.BIOSWE: - logger().log( "[spi] BIOS write protection not enabled" ) - return True - else: - logger().log( "[spi] BIOS write protection enabled but not locked. Disabling.." ) - - reg_value |= 0x1 - chipsec.chipset.write_register( self.cs, 'BC', reg_value ) - (BcRegister, reg_value) = self.get_BIOS_Control() - if logger().VERBOSE: logger().log( BcRegister ) - if BcRegister.BIOSWE: - logger().log_important( "BIOS write protection is disabled" ) - return True - else: - return False - """ + return (bioswe==1) ############################################################################################################## @@ -552,7 +507,7 @@ class SPI: n = data_byte_count / dbc r = data_byte_count % dbc - if logger().UTIL_TRACE or logger().VERBOSE: + if logger().UTIL_TRACE or logger().DEBUG: logger().log( "[spi] reading 0x%x bytes from SPI at FLA = 0x%X (in %d 0x%x-byte chunks + 0x%x-byte remainder)" % (data_byte_count, spi_fla, n, dbc, r) ) cycle_done = self._wait_SPI_flash_cycle_done() @@ -561,7 +516,7 @@ class SPI: return None for i in range(n): - if logger().UTIL_TRACE or logger().VERBOSE: + if logger().DEBUG: logger().log( "[spi] reading chunk %d of 0x%x bytes from 0x%X" % (i, dbc, spi_fla + i*dbc) ) if not self._send_spi_cycle( HSFCTL_READ_CYCLE, dbc-1, spi_fla + i*dbc ): logger().error( "SPI flash read failed" ) @@ -573,7 +528,7 @@ class SPI: buf += [ chr((dword_value>>(8*j))&0xff) for j in range(4) ] #buf += tuple( struct.pack("I", dword_value) ) if (0 != r): - if logger().UTIL_TRACE or logger().VERBOSE: + if logger().DEBUG: logger().log( "[spi] reading remaining 0x%x bytes from 0x%X" % (r, spi_fla + n*dbc) ) if not self._send_spi_cycle( HSFCTL_READ_CYCLE, r-1, spi_fla + n*dbc ): logger().error( "SPI flash read failed" ) diff --git a/source/tool/chipsec/hal/spi_uefi.py b/source/tool/chipsec/hal/spi_uefi.py index 4d157473..28cd632e 100644 --- a/source/tool/chipsec/hal/spi_uefi.py +++ b/source/tool/chipsec/hal/spi_uefi.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -134,7 +134,7 @@ def parse_uefi_section( _uefi, data, Size, offset, polarity, parent_offset, pare if efi_file != None: os.rename(os.path.join(parent_path, efi_file), os.path.join(parent_path, ui_string)) efi_file = None - if (SecType in (EFI_SECTION_COMPRESSION, EFI_SECTION_GUID_DEFINED, EFI_SECTION_FIRMWARE_VOLUME_IMAGE)): + if (SecType in (EFI_SECTION_COMPRESSION, EFI_SECTION_GUID_DEFINED, EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW)): section_dir_path = "%s.dir" % section_path os.makedirs( section_dir_path ) if (SecType == EFI_SECTION_COMPRESSION): @@ -161,7 +161,7 @@ def parse_uefi_section( _uefi, data, Size, offset, polarity, parent_offset, pare #else: # write_file( os.path.join(section_dir_path, "%s-%04X" % (sguid, Attributes)), SecBody[DataOffset:] ) pass - elif (SecType == EFI_SECTION_FIRMWARE_VOLUME_IMAGE): + elif (SecType == EFI_SECTION_FIRMWARE_VOLUME_IMAGE or SecType == EFI_SECTION_RAW): parse_uefi_region(_uefi, SecBody[SecHeaderSize:], section_dir_path) sec_offset, next_sec_offset, SecName, SecType, SecBody, SecHeaderSize = NextFwFileSection(data, Size, next_sec_offset, polarity) secn = secn + 1 diff --git a/source/tool/chipsec/hal/tpm.py b/source/tool/chipsec/hal/tpm.py new file mode 100644 index 00000000..ee287ec1 --- /dev/null +++ b/source/tool/chipsec/hal/tpm.py @@ -0,0 +1,469 @@ +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# + + + +""" +Trusted Platform Module (TPM) HAL component + +https://trustedcomputinggroup.org +""" + +__version__ = '0.1' + +import struct +import sys +import time +from collections import namedtuple + +from chipsec.logger import * +from chipsec.file import * + +import chipsec.hal.tpm12_commands + +class TpmRuntimeError (RuntimeError): + pass + +COMMANDREADY = 0x40 +TPMGO = 0x20 +HEADERSIZE = 0x0A +HEADERFORMAT = '>HII' +BEENSEIZED = 0x10 +REQUESTUSE = 0x2 +ACTIVELOCALITY = 0x20 +DATAAVAIL = 0x10 + +TPM_DATAFIFO = 0x0024 +TPM_STS = 0x0018 +TPM_DIDVID = 0x0F00 +TPM_ACCESS = 0x0000 +TPM_RID = 0x0F04 +TPM_INTCAP = 0x0014 +TPM_INTENABLE = 0x0008 + +STATUS = { + 0x00:"Success", + 0x01:"ERROR: Authentication Failed", + 0x02:"ERROR: The index to a PCR, DIR or other register is incorrect", + 0x03:"ERROR: One or more parameter is bad", + 0x04:"ERROR: An operation completed successfully but the auditing of that operation failed", + 0x05:"ERROR: The clear disable flag is set and all clear operations now require physical access", + 0x06:"ERROR: The TPM is deactivated", + 0x07:"ERROR: The TPM is disabled", + 0x08:"ERROR: The target command has been disabled", + 0x09:"ERROR: The operation failed", + 0x0A:"ERROR: The ordinal was unknown or inconsistent", + 0x0B:"ERROR: The ability to install an owner is disabled", + 0x0C:"ERROR: The key handle can not be interpreted", + 0x0D:"ERROR: The key handle points to an invalid key", + 0x0E:"ERROR: Unacceptable encryption scheme", + 0x0F:"ERROR: Migration authorization failed", + 0x10:"ERROR: PCR information could not be interpreted", + 0x11:"ERROR: No room to load key", + 0x12:"ERROR: There is no SRK set", + 0x13:"ERROR: An encrypted blob is invalid or was not created by this TPM", + 0x14:"ERROR: There is already an Owner", + 0x15:"ERROR: The TPM has insufficient internal resources to perform the requested action", + 0x16:"ERROR: A random string was too short", + 0x17:"ERROR: The TPM does not have the space to perform the operation", + 0x18:"ERROR: The named PCR value does not match the current PCR value.", + 0x19:"ERROR: The paramSize argument to the command has the incorrect value", + 0x1A:"ERROR: There is no existing SHA-1 thread.", + 0x1B:"ERROR: The calculation is unable to proceed because the existing SHA-1 thread has already encountered an error", + 0x1C:"ERROR: Self-test has failed and the TPM has shut-down", + 0x1D:"ERROR: The authorization for the second key in a 2 key function failed authorization", + 0x1E:"ERROR: The tag value sent to for a command is invalid", + 0x1F:"ERROR: An IO error occurred transmitting information to the TPM", + 0x20:"ERROR: The encryption process had a problem", + 0x21:"ERROR: The decryption process did not complete", + 0x22:"ERROR: An invalid handle was used", + 0x23:"ERROR: The TPM does not a EK installed", + 0x24:"ERROR: The usage of a key is not allowed", + 0x25:"ERROR: The submitted entity type is not allowed", + 0x26:"ERROR: The command was received in the wrong sequence relative to TPM_Init and a subsequent TPM_Startup", + 0x27:"ERROR: Signed data cannot include additional DER information", + 0x28:"ERROR: The key properties in TPM_KEY_PARMs are not supported by this TPM", + 0x29:"ERROR: The migration properties of this key are incorrect", + 0x2A:"ERROR: The signature or encryption scheme for this key is incorrect or not permitted in this situation", + 0x2B:"ERROR: The size of the data (or blob) parameter is bad or inconsistent with the referenced key", + 0x2C:"ERROR: A parameter is bad", + 0x2D:"ERROR: Either the physicalPresence or physicalPresenceLock bits have the wrong value", + 0x2E:"ERROR: The TPM cannot perform this version of the capability", + 0x2F:"ERROR: The TPM does not allow for wrapped transport sessions", + 0x30:"ERROR: TPM audit construction failed and the underlying command was returning a failure code also", + 0x31:"ERROR: TPM audit construction failed and the underlying command was returning success", + 0x32:"ERROR: Attempt to reset a PCR register that does not have the resettable attribute", + 0x33:"ERROR: Attempt to reset a PCR register that requires locality and locality modifier not part of command transport", + 0x34:"ERROR: Make identity blob not properly typed", + 0x35:"ERROR: When saving context identified resource type does not match actual resource", + 0x36:"ERROR: The TPM is attempting to execute a command only available when in FIPS mode", + 0x37:"ERROR: The command is attempting to use an invalid family ID", + 0x38:"ERROR: The permission to manipulate the NV storage is not available", + 0x39:"ERROR: The operation requires a signed command", + 0x3A:"ERROR: Wrong operation to load an NV key", + 0x3B:"ERROR: NV_LoadKey blob requires both owner and blob authorization", + 0x3C:"ERROR: The NV area is locked and not writeable", + 0x3D:"ERROR: The locality is incorrect for the attempted operation", + 0x3E:"ERROR: The NV area is read only and can?t be written to", + 0x3F:"ERROR: There is no protection on the write to the NV area", + 0x40:"ERROR: The family count value does not match", + 0x41:"ERROR: The NV area has already been written to", + 0x42:"ERROR: The NV area attributes conflict", + 0x43:"ERROR: The structure tag and version are invalid or inconsistent", + 0x44:"ERROR: The key is under control of the TPM Owner and can only be evicted by the TPM Owner", + 0x45:"ERROR: The counter handle is incorrect", + 0x46:"ERROR: The write is not a complete write of the area", + 0x47:"ERROR: The gap between saved context counts is too large", + 0x48:"ERROR: The maximum number of NV writes without an owner has been exceeded", + 0x49:"ERROR: No operator AuthData value is set", + 0x4A:"ERROR: The resource pointed to by context is not loaded", + 0x4B:"ERROR: The delegate administration is locked", + 0x4C:"ERROR: Attempt to manage a family other then the delegated family", + 0x4D:"ERROR: Delegation table management not enabled", + 0x4E:"ERROR: There was a command executed outside of an exclusive transport session", + 0x4F:"ERROR: Attempt to context save a owner evict controlled key", + 0x50:"ERROR: The DAA command has no resources available to execute the command", + 0x51:"ERROR: The consistency check on DAA parameter inputData0 has failed", + 0x52:"ERROR: The consistency check on DAA parameter inputData1 has failed", + 0x53:"ERROR: The consistency check on DAA_issuerSettings has failed", + 0x54:"ERROR: The consistency check on DAA_tpmSpecific has failed", + 0x55:"ERROR: The atomic process indicated by the submitted DAA command is not the expected process", + 0x56:"ERROR: The issuer's validity check has detected an inconsistency", + 0x57:"ERROR: The consistency check on w has failed", + 0x58:"ERROR: The handle is incorrect", + 0x59:"ERROR: Delegation is not correct", + 0x5A:"ERROR: The context blob is invalid", + 0x5B:"ERROR: Too many contexts held by the TPM", + 0x5C:"ERROR: Migration authority signature validation failure", + 0x5D:"ERROR: Migration destination not authenticated", + 0x5E:"ERROR: Migration source incorrect", + 0x5F:"ERROR: Incorrect migration authority", + 0x60:"ERROR: TBD", + 0x61:"ERROR: Attempt to revoke the EK and the EK is not revocable", + 0x62:"ERROR: Bad signature of CMK ticket", + 0x63:"ERROR: There is no room in the context list for additional contexts", + 0x800:"NON-FATAL ERROR: The TPM is too busy to respond to the command immediately, but the command could be resubmitted at a later time", + 0x801:"NON-FATAL ERROR: TPM_ContinueSelfTest has not been run.", + 0x802:"NON-FATAL ERROR: The TPM is currently executing the actions of TPM_ContinueSelfTest because the ordinal required resources that have not been tested", + 0x803:"NON-FATAL ERROR: The TPM is defending against dictionary attacks and is in some time-out period." +} + +LOCALITY = { + '0':0x0000, + '1':0x1000, + '2':0x2000, + '3':0x3000, + '4':0x4000 +} + +COMMANDS = { + "pcrread" : chipsec.hal.tpm12_commands.pcrread, + "nvread" : chipsec.hal.tpm12_commands.nvread, + "startup" : chipsec.hal.tpm12_commands.startup, + "continueselftest" : chipsec.hal.tpm12_commands.continueselftest, + "forceclear" : chipsec.hal.tpm12_commands.forceclear +} + +class TPM_RESPONSE_HEADER( namedtuple('TPM_RESPONSE_HEADER', 'ResponseTag DataSize ReturnCode') ): + __slots__ = () + def __str__(self): + _str = """---------------------------------------------------------------- + TPM response header +---------------------------------------------------------------- + Response TAG: 0x%x + Data Size : 0x%x + Return Code : 0x%x +""" % ( self.ResponseTag, self.DataSize, self.ReturnCode ) + _str += "\t" + try: + _str += STATUS[self.ReturnCode] + except: + _str += "Invalid return code" + _str += "\n" + return _str + +class TPM(): + def __init__( self, cs ): + self.cs = cs + self.helper = cs.helper + self.TPM_BASE = int(self.cs.Cfg.MEMORY_RANGES["TPM"]["address"], 16) + + def command( self, commandName, locality, command_argv ): + """ + Send command to the TPM and receive data + """ + try: + Locality = LOCALITY[locality] + except: + logger().log_bad("Invalid locality value\n") + return + + requestedUse = False + + # + # Request locality use if needed + # + access_address = self.TPM_BASE | Locality | TPM_ACCESS + if self.helper.read_mmio_reg( access_address, 4 ) == BEENSEIZED: + self.helper.write_mmio_reg( access_address, 4, REQUESTUSE ) + requestedUse = True + + # + # Build command (big endian) and send/receive + # + ( command, size ) = COMMANDS[commandName]( command_argv ) + self._send_command( Locality, command, size ) + + ( header, data, header_blob, data_blob ) = self._read_response( Locality ) + logger().log( header ) + print_buffer( str(data_blob) ) + logger().log( '\n' ) + + # + # Release locality if needed + # + if requestedUse==True: + self.helper.write_mmio_reg( access_address, 4, BEENSEIZED ) + self.helper.write_mmio_reg( access_address, 1, ACTIVELOCALITY ) + + def _send_command( self, Locality, command, size ): + """ + Send a command to the TPM using the locality specified + """ + count = 0 + + datafifo_address = self.TPM_BASE | Locality | TPM_DATAFIFO + sts_address = self.TPM_BASE | Locality| TPM_STS + access_address = self.TPM_BASE | Locality| TPM_ACCESS + + self.helper.write_mmio_reg( access_address, 1, REQUESTUSE ) + # + # Set status to command ready + # + sts_value = self.helper.read_mmio_reg( sts_address, 1 ) + while ( 0 == ( sts_value & COMMANDREADY ) ): + self.helper.write_mmio_reg( sts_address, 1, COMMANDREADY ) + sts_value = self.helper.read_mmio_reg( sts_address, 1 ) + + while count < size: + sts_value = self.helper.read_mmio_reg( sts_address, 4 ) + burst_count = ( ( sts_value>>8 ) & 0xFFFFFF ) + burst_index = 0 + while ( burst_index < burst_count ) and ( count < size ): + self.helper.write_mmio_reg( datafifo_address, 1, struct.unpack("=B", command[count])[0] ) + count += 1 + burst_index += 0x1 + + self.helper.write_mmio_reg( sts_address, 1, TPMGO ) + + def _read_response(self, Locality): + """ + Read the TPM's response using the specified locality + """ + count = 0 + header = "" + header_blob = bytearray() + data = "" + data_blob = bytearray() + # + # Build FIFO address + # + datafifo_address = self.TPM_BASE | Locality | TPM_DATAFIFO + access_address = self.TPM_BASE | Locality| TPM_ACCESS + sts_address = self.TPM_BASE | Locality| TPM_STS + + sts_value = self.helper.read_mmio_reg( sts_address, 1 ) + data_avail = bin( sts_value & ( 1<<4 ) )[2] + # + # Read data available + # + # watchdog? + while data_avail == '0': + sts_value = self.helper.read_mmio_reg( sts_address, 1 ) + self.helper.write_mmio_reg( sts_address, 1, DATAAVAIL ) + data_avail = bin( sts_value & ( 1<<4 ) )[2] + + while count < HEADERSIZE: + sts_value = self.helper.read_mmio_reg( sts_address, 4 ) + burst_count = ( ( sts_value>>8 ) & 0xFFFFFF ) + burst_index = 0 + while ( burst_index < burst_count ) and ( count < HEADERSIZE ): + header_blob.append(self.helper.read_mmio_reg( datafifo_address, 1 )) + count += 1 + burst_index += 0x1 + + header = TPM_RESPONSE_HEADER( *struct.unpack_from( HEADERFORMAT, header_blob ) ) + + count = 0 + if header.DataSize > 10 and header.ReturnCode == 0: + length = header.DataSize - HEADERSIZE + while count < length: + sts_value = self.helper.read_mmio_reg( sts_address, 4 ) + burst_count = ( ( sts_value>>8 ) & 0xFFFFFF ) + burst_index = 0 + while ( burst_index < burst_count ) and ( count < length ): + data_blob.append( self.helper.read_mmio_reg( datafifo_address, 1 ) ) + count += 1 + burst_index += 0x1 + + return ( header, data, header_blob, data_blob ) + + def dump_access( self, locality ): + """ + View the contents of the register used to gain ownership of the TPM + """ + try: + Locality = LOCALITY[locality] + except: + logger().log_bad("Invalid locality value\n") + return + + access_address = self.TPM_BASE | Locality| TPM_ACCESS + access_value = self.helper.read_mmio_reg( access_address, 1 ) + + logger().log( "================================================================" ) + logger().log( " TPM Access" ) + logger().log( "================================================================" ) + logger().log( "\ttpmRegValidSts : 0x%s" % bin( access_value & ( 1<<7 ) )[2] ) + logger().log( "\treserved : 0x%s" % bin( access_value & ( 1<<6 ) )[2] ) + logger().log( "\tactiveLocality : 0x%s" % bin( access_value & ( 1<<5 ) )[2] ) + logger().log( "\tbeenSeized : 0x%s" % bin( access_value & ( 1<<4 ) )[2] ) + logger().log( "\tSeize : 0x%s" % bin( access_value & ( 1<<3 ) )[2] ) + logger().log( "\tpendingRequest : 0x%s" % bin( access_value & ( 1<<2 ) )[2] ) + logger().log( "\trequestUse : 0x%s" % bin( access_value & ( 1<<1 ) )[2] ) + logger().log( "\ttpmEstablishment: 0x%s" % bin( access_value & ( 1<<0 ) )[2] ) + + def dump_status( self, locality ): + """ + View general status details + """ + try: + Locality = LOCALITY[locality] + except: + logger().log_bad("Invalid locality value\n") + return + + sts_address = self.TPM_BASE | Locality| TPM_STS + sts_value = self.helper.read_mmio_reg( sts_address, 4 ) + + logger().log( "================================================================" ) + logger().log( " TPM Status" ) + logger().log( "================================================================" ) + logger().log( "\tburstCount : 0x%x" % ( ( sts_value>>8 ) & 0xFFFFFF ) ) + logger().log( "\tstsValid : 0x%s" % bin( sts_value & ( 1<<7 ) )[2] ) + logger().log( "\tcommandReady : 0x%s" % bin( sts_value & ( 1<<6 ) )[2] ) + logger().log( "\ttpmGo : 0x%s" % bin( sts_value & ( 1<<5 ) )[2] ) + logger().log( "\tdataAvail : 0x%s" % bin( sts_value & ( 1<<4 ) )[2] ) + logger().log( "\tExpect : 0x%s" % bin( sts_value & ( 1<<3 ) )[2] ) + logger().log( "\tReserved : 0x%s" % bin( sts_value & ( 1<<2 ) )[2] ) + logger().log( "\tresponseRetry: 0x%s" % bin( sts_value & ( 1<<1 ) )[2] ) + logger().log( "\tReserved : 0x%s" % bin( sts_value & ( 1<<0 ) )[2] ) + + def dump_didvid( self, locality ): + """ + TPM's Vendor and Device ID + """ + try: + Locality = LOCALITY[locality] + except: + logger().log_bad("Invalid locality value\n") + return + + didvid_address = self.TPM_BASE | Locality| TPM_DIDVID + didvid_value = self.helper.read_mmio_reg( didvid_address, 4 ) + + logger().log( "================================================================" ) + logger().log( " TPM DID VID" ) + logger().log( "================================================================" ) + logger().log( "\tdid: 0x%x" % ( ( didvid_value>>16 ) & 0xFFFF ) ) + logger().log( "\tvid: 0x%x" % ( didvid_value & 0xFFFF) ) + + def dump_rid( self, locality ): + """ + TPM's Revision ID + """ + try: + Locality = LOCALITY[locality] + except: + logger().log_bad("Invalid locality value\n") + return + + rid_address = self.TPM_BASE | Locality| TPM_RID + rid_value = self.helper.read_mmio_reg( rid_address, 1 ) + + logger().log( "================================================================" ) + logger().log( " TPM RID" ) + logger().log( "================================================================" ) + logger().log( "\trid: 0x%x" % rid_value ) + + def dump_intcap( self, locality ): + """ + Provides information of which interrupts that particular TPM supports + """ + try: + Locality = LOCALITY[locality] + except: + logger().log_bad("Invalid locality value\n") + return + + intcap_address = self.TPM_BASE | Locality| TPM_INTCAP + intcap_value = self.helper.read_mmio_reg( intcap_address, 4 ) + + logger().log( "================================================================" ) + logger().log( " TPM INTF CAPABILITY" ) + logger().log( "================================================================" ) + logger().log( "\tReserved : 0x%x" % ( ( intcap_value>>8 ) & 0xFFFFFE ) ) + logger().log( "\tBurstCountStatic : 0x%s" % bin( intcap_value & ( 1<<8 ) )[2] ) + logger().log( "\tCommandReadyIntSupport : 0x%s" % bin( intcap_value & ( 1<<7 ) )[2] ) + logger().log( "\tInterruptEdgeFalling : 0x%s" % bin( intcap_value & ( 1<<6 ) )[2] ) + logger().log( "\tInterruptEdgeRising : 0x%s" % bin( intcap_value & ( 1<<5 ) )[2] ) + logger().log( "\tInterruptLevelLow : 0x%s" % bin( intcap_value & ( 1<<4 ) )[2] ) + logger().log( "\tInterruptLevelHigh : 0x%s" % bin( intcap_value & ( 1<<3 ) )[2] ) + logger().log( "\tLocalityChangeIntSupport: 0x%s" % bin( intcap_value & ( 1<<2 ) )[2] ) + logger().log( "\tstsValidIntSupport : 0x%s" % bin( intcap_value & ( 1<<1 ) )[2] ) + logger().log( "\tdataAvailIntSupport : 0x%s" % bin( intcap_value & ( 1<<0 ) )[2] ) + + def dump_intenable( self, locality ): + """ + View the contents of the register used to enable specific interrupts + """ + polType = { 0:"High Level", 1:"Low Level", 2:"Rising edge", 3:"Failing edge" } + + try: + Locality = LOCALITY[locality] + except: + logger().log_bad("Invalid locality value\n") + return + + intenable_address = self.TPM_BASE | Locality| TPM_INTENABLE + intenable_value = self.helper.read_mmio_reg( intenable_address, 4 ) + + logger().log( "================================================================" ) + logger().log( " TPM INT ENABLE" ) + logger().log( "================================================================" ) + logger().log( "\tglobalIntEnable : 0x%s" % bin( intenable_value & ( 1<<31 ) )[2] ) + logger().log( "\tReserved : 0x%x" % ( (intenable_value>>8) & 0x7FFFFF00 ) ) + logger().log( "\tcommandReadyEnable : 0x%s" % bin( intenable_value & ( 1<<7 ) )[2] ) + logger().log( "\tReserved : 0x%x" % ( (intenable_value>>5) & 0x3 ) ) + type = ( ( intenable_value>>3 ) & 0x3 ) + logger().log( "\ttypePolarity : 0x%x %s" % ( type, polType[type] ) ) + logger().log( "\tlocalityChangeIntEnable: 0x%s" % bin( intenable_value & ( 1<<2 ) )[2] ) + logger().log( "\tstsValidIntEnable : 0x%s" % bin( intenable_value & ( 1<<1 ) )[2] ) + logger().log( "\tdataAvailIntEnable : 0x%s" % bin( intenable_value & ( 1<<0 ) )[2] ) diff --git a/source/tool/chipsec/hal/tpm12_commands.py b/source/tool/chipsec/hal/tpm12_commands.py new file mode 100644 index 00000000..d1f3a59a --- /dev/null +++ b/source/tool/chipsec/hal/tpm12_commands.py @@ -0,0 +1,185 @@ +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# + + + +""" +Definition for TPMv1.2 commands to use with TPM HAL + +TCG PC Client TPM Specification +TCG TPM v1.2 Specification +""" + +import struct +from chipsec.logger import * + +COMMAND_FORMAT = "=HIIIII" + +TPM_TAG_RQU_COMMAND = 0xc100 +TPM_TAG_RQU_AUTH1_COMMAND = 0xc200 +TPM_TAG_RQU_AUTH2_COMMAND = 0xC300 +TPM_TAG_RSP_COMMAND = 0xC400 +TPM_TAG_RSP_AUTH1_COMMAND = 0xC500 +TPM_TAG_RSP_AUTH2_COMMAND = 0xC600 + +TPM_ORD_CONTINUESELFTEST = 0x53000000 +TPM_ORD_FORCECLEAR = 0x5D000000 +TPM_ORD_GETCAPABILITY = 0x65000000 +TPM_ORD_NV_DEFINESPACE = 0xCC000000 +TPM_ORD_NV_READVALUE = 0xCF000000 +TPM_ORD_NV_WRITEVALUE = 0xCD000000 +TPM_ORD_PCRREAD = 0x15000000 +TPM_ORD_PHYSICALDISABLE = 0x70000000 +TPM_ORD_PHYSICALENABLE = 0x6F000000 +TPM_ORD_PHYSICALSETDEACTIVATED = 0x72000000 +TPM_ORD_STARTUP = 0x99000000 +TPM_ORD_SAVESTATE = 0x98000000 +TSC_ORD_PHYSICALPRESENCE = 0x0A000040 +TSC_ORD_RESETESTABLISHMENTBIT = 0x0B000040 + +STARTUP = { +1:0x0100, +2:0x0200, +3:0x0300 +} + +PCR= { + 0 :0x00000000, + 1 :0x01000000, + 2 :0x02000000, + 3 :0x03000000, + 4 :0x04000000, + 5 :0x05000000, + 6 :0x06000000, + 7 :0x07000000, + 8 :0x08000000, + 9 :0x09000000, + 10:0x0a000000, + 11:0x0b000000, + 12:0x0c000000, + 13:0x0d000000, + 14:0x0e000000, + 15:0x0f000000, + 16:0x10000000, + 17:0x11000000, + 18:0x12000000, + 19:0x13000000, + 20:0x14000000, + 21:0x15000000, + 22:0x16000000, + 23:0x17000000, + 24:0x18000000, + 25:0x19000000, + 26:0x1a000000, + 27:0x1b000000, + 28:0x1c000000, + 29:0x1d000000, + 30:0x1e000000 +} + +def pcrread( command_argv ): + """ + The TPM_PCRRead operation provides non-cryptographic reporting of the contents of a named PCR + """ + Size = 0x0E000000 + try: + Pcr = PCR[int(command_argv[0])] + except: + logger().log_bad("Invalid PCR value\n") + return + command = struct.pack( COMMAND_FORMAT, TPM_TAG_RQU_COMMAND, Size, TPM_ORD_PCRREAD, Pcr, 0, 0 ) + size = Size >> 0x18 + return ( command, size ) + +def nvread( command_argv ): + """ + Read a value from the NV store + Index, Offset, Size + """ + Size = 0x18000000 + command = struct.pack( COMMAND_FORMAT, TPM_TAG_RQU_COMMAND, Size, TPM_ORD_NV_READVALUE, int(command_argv[0], 16), int(command_argv[1], 16), int(command_argv[2], 16) ) + size = Size >> 0x18 + return ( command, size ) + +def startup( command_argv ): + """ + Execute a tpm_startup command. TPM_Startup is always preceded by TPM_Init, which is the physical indication (a system wide reset) that TPM initialization is necessary + Type of Startup to be used: + 1: TPM_ST_CLEAR + 2: TPM_ST_STATE + 3: TPM_ST_DEACTIVATED + """ + try: + startupType = STARTUP[int(command_argv[0])] + except: + logger().log_bad("Invalid startup type option value\n") + return + Size = 0x0E000000 + command = struct.pack( COMMAND_FORMAT, TPM_TAG_RQU_COMMAND, Size, TPM_ORD_STARTUP, startupType, 0, 0 ) + size = Size >> 0x18 + return ( command, size ) + +def continueselftest( command_argv ): + """ + TPM_ContinueSelfTest informs the TPM that it should complete self-test of all TPM functions. The TPM may return success immediately and then perform the self-test, or it may perform the self-test and then return success or failure. + """ + Size = 0x0A000000 + command = struct.pack( COMMAND_FORMAT, TPM_TAG_RQU_COMMAND, Size, TPM_ORD_CONTINUESELFTEST, 0, 0, 0 ) + size = Size >> 0x18 + return ( command, size ) + +def getcap( command_argv ): + """ + Returns current information regarding the TPM + CapArea - Capabilities Area + SubCapSize - Size of SubCapabilities + SubCap - Subcapabilities + """ + Size = 0x18000000 + command = struct.pack( COMMAND_FORMAT, TPM_TAG_RQU_COMMAND, Size, TPM_ORD_GETCAPABILITY, int(command_argv[0], 16), int(command_argv[1], 16), int(command_argv[2], 16) ) + size = Size >> 0x18 + return ( command, size ) + +def forceclear( command_argv ): + Size = 0x0A000000 + command = struct.pack( COMMAND_FORMAT, TPM_TAG_RQU_COMMAND, Size, TPM_ORD_FORCECLEAR, 0, 0, 0 ) + size = Size >> 0x18 + return ( command, size ) + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/tool/chipsec/hal/uefi.py b/source/tool/chipsec/hal/uefi.py index fc24a72a..f55fe295 100644 --- a/source/tool/chipsec/hal/uefi.py +++ b/source/tool/chipsec/hal/uefi.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -68,6 +68,8 @@ def parse_script( script, log_script=False ): while (off < len_s) and (entry_type != S3BootScriptOpcode.EFI_BOOT_SCRIPT_TERMINATE_OPCODE): entry_type,s3script_entry = parse_s3bootscript_entry( script_type, script, off, log_script ) + # couldn't parse the next entry - return what has been parsed so far + if s3script_entry is None: return s3_boot_script_entries s3_boot_script_entries.append( s3script_entry ) off += s3script_entry.length @@ -443,7 +445,7 @@ NVRAM: EFI Variable Store # Decode the S3 Resume Boot-Script into a sequence of operations/opcodes # # @TODO: should be dumping memory contents in a loop until end opcode is found or id'ing actual size - script_buffer = self.helper.read_physical_mem( bootscript_pa, 0x100000 ) + script_buffer = self.helper.read_physical_mem( bootscript_pa, 0x200000 ) if logger().HAL: logger().log( '[uefi] Decoding S3 Resume Boot-Script..' ) script_entries = parse_script( script_buffer, log_script ) parsed_scripts[ bootscript_pa ] = script_entries @@ -466,23 +468,22 @@ NVRAM: EFI Variable Store print_buffer( var ) return var - def set_EFI_variable( self, name, guid, var, attrs=None ): - if logger().UTIL_TRACE or logger().VERBOSE: + def set_EFI_variable( self, name, guid, var, datasize=None, attrs=None ): + if logger().HAL: logger().log( '[uefi] writing EFI variable %s:%s %s' % (guid, name, '' if attrs is None else ('(attributes = %s)' % attrs)) ) #print_buffer( var ) - return self.helper.set_EFI_variable( name, guid, var, attrs ) - - def set_EFI_variable_from_file( self, name, guid, filename, attrs=None ): + return self.helper.set_EFI_variable( name, guid, var, datasize, attrs ) + + def set_EFI_variable_from_file( self, name, guid, filename, datasize=None, attrs=None ): if filename is None: logger().error( 'File with EFI variable is not specified' ) return False var = read_file( filename ) - return self.set_EFI_variable( name, guid, var, attrs ) + return self.set_EFI_variable( name, guid, var, datasize, attrs ) - def delete_EFI_variable( self, name, guid, attrs=None ): - if logger().UTIL_TRACE or logger().VERBOSE: - logger().log( '[uefi] deleting EFI variable %s:%s %s' % (guid, name, '' if attrs is None else ('(attributes = %s)' % attrs)) ) - return self.helper.set_EFI_variable( name, guid, None, attrs ) + def delete_EFI_variable( self, name, guid ): + if logger().HAL: logger().log( '[uefi] deleting EFI variable %s:%s' % (guid, name) ) + return self.helper.delete_EFI_variable( name, guid ) ###################################################################### @@ -555,16 +556,16 @@ NVRAM: EFI Variable Store (isFound,est_pa,est_header,est,est_buf) = self.find_EFI_System_Table() if isFound and est is not None: if 0 != est.BootServices: - logger().log( "[uefi] UEFI appears to be in Boot mode" ) + if logger().HAL: logger().log( "[uefi] UEFI appears to be in Boot mode" ) ect_pa = est.ConfigurationTable else: - logger().log( "[uefi] UEFI appears to be in Runtime mode" ) + if logger().HAL: logger().log( "[uefi] UEFI appears to be in Runtime mode" ) ect_pa = self.cs.mem.va2pa( est.ConfigurationTable ) if not ect_pa: - print "[uefi] Cann't find UEFI ConfigurationTable" + logger().error( "Can't find UEFI ConfigurationTable" ) return (None,ect_pa,ect,ect_buf) - logger().log( "[uefi] EFI Configuration Table (%d entries): VA = 0x%016X, PA = 0x%016X" % (est.NumberOfTableEntries,est.ConfigurationTable,ect_pa) ) + if logger().HAL: logger().log( "[uefi] EFI Configuration Table (%d entries): VA = 0x%016X, PA = 0x%016X" % (est.NumberOfTableEntries,est.ConfigurationTable,ect_pa) ) found = (ect_pa is not None) if found: diff --git a/source/tool/chipsec/hal/uefi_common.py b/source/tool/chipsec/hal/uefi_common.py index 537272cb..44cf213e 100644 --- a/source/tool/chipsec/hal/uefi_common.py +++ b/source/tool/chipsec/hal/uefi_common.py @@ -1,6 +1,6 @@ #!/usr/local/bin/python #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -551,7 +551,7 @@ def NextFwFile(FvImage, FvLength, fof, polarity): next_offset = None res = None update_or_deleted = False - if (fof + file_header_size) < FvLength: + if (fof + file_header_size) <= len(FvImage[fof:]): fheader = FvImage[fof:fof+file_header_size] Name0, Name1, Name2, Name3, IntegrityCheck, Type, Attributes, Size, State = struct.unpack(EFI_FFS_FILE_HEADER, fheader) fsize = get_3b_size(Size); diff --git a/source/tool/chipsec/hal/uefi_platform.py b/source/tool/chipsec/hal/uefi_platform.py index fd322075..207f2b7f 100644 --- a/source/tool/chipsec/hal/uefi_platform.py +++ b/source/tool/chipsec/hal/uefi_platform.py @@ -18,8 +18,8 @@ #Contact information: #chipsec@intel.com # - - + + # ------------------------------------------------------------------------------- # @@ -253,6 +253,10 @@ NVAR_NVRAM_FS_FILE = "CEF5B9A3-476D-497F-9FDC-E98143E0422C" def getNVstore_NVAR( nvram_buf ): l = (-1, -1, None) FvOffset, FsGuid, FvLength, FvAttributes, FvHeaderLength, FvChecksum, ExtHeaderOffset, FvImage, CalcSum = NextFwVolume(nvram_buf) + if (FvOffset >= len(nvram_buf)): + return l + if (FvOffset + FvLength) > len(nvram_buf): + FvLength = len(nvram_buf) - FvOffset while FvOffset != None: polarity = bit_set(FvAttributes, EFI_FVB2_ERASE_POLARITY) cur_offset, next_offset, Name, Type, Attributes, State, Checksum, Size, FileImage, HeaderSize, UD, fCalcSum = NextFwFile(FvImage, FvLength, FvHeaderLength, polarity) @@ -951,9 +955,15 @@ def parse_s3bootscript_entry( s3bootscript_type, script, off, log_script=False ) opcode = None entry_data = None + remaining_len = len(script[off:]) + if S3BootScriptType.EFI_BOOT_SCRIPT_TYPE_EDKCOMPAT == s3bootscript_type: fhdr = ' MAX_S3_BOOTSCRIPT_ENTRY_LENGTH: logger().error( '[uefi] Unrecognized S3 boot script format (entry length = 0x%X)' % entry_length ) - return None + return (0,None) s3script_entry = S3BOOTSCRIPT_ENTRY( s3bootscript_type, entry_index, off, entry_length, entry_data ) @@ -970,6 +980,10 @@ def parse_s3bootscript_entry( s3bootscript_type, script, off, log_script=False ) fhdr = ' MAX_S3_BOOTSCRIPT_ENTRY_LENGTH: logger().error( '[uefi] Unrecognized S3 boot script format (entry length = 0x%X)' % entry_length ) - return None + return (0,None) s3script_entry = S3BOOTSCRIPT_ENTRY( s3bootscript_type, entry_index, off, entry_length, entry_data ) s3script_entry.header_length = hdr_length diff --git a/source/tool/chipsec/hal/vmm.py b/source/tool/chipsec/hal/vmm.py new file mode 100644 index 00000000..8f301910 --- /dev/null +++ b/source/tool/chipsec/hal/vmm.py @@ -0,0 +1,108 @@ +#!/usr/local/bin/python +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# (c) 2010-2016 Intel Corporation +# +# ------------------------------------------------------------------------------- +## \addtogroup hal +# chipsec/hal/vmm.py +# ==================== +# VMM specific functionality +# 1. Hypercall interfaces +# 2. Second-level Address Translation (SLAT) +# 3. .. +# +# ~~~ + +__version__ = '1.0' + +import struct +import sys +import os.path + +from chipsec.logger import logger + +class VMMRuntimeError (RuntimeError): + pass + +class VMM: + + def __init__( self, cs ): + self.cs = cs + self.helper = cs.helper + self.output = '' + (self.membuf0_va, self.membuf0_pa) = (0, 0) + (self.membuf1_va, self.membuf1_pa) = (0, 0) + + def __del__(self): + if self.membuf0_va <> 0: + #self.helper.free_physical_mem(self.membuf0_va) + (self.membuf0_va, self.membuf0_pa) = (0, 0) + (self.membuf1_va, self.membuf1_pa) = (0, 0) + + def init(self): + (self.membuf0_va, self.membuf0_pa) = self.cs.mem.alloc_physical_mem(0x2000, 0xFFFFFFFFFFFFFFFF) + (self.membuf1_va, self.membuf1_pa) = (self.membuf0_va + 0x1000, self.membuf0_pa + 0x1000) + if self.membuf0_va == 0: + logger().log( "[vmm] Could not allocate memory!") + raise + + # Generic hypercall interface + + def hypercall(self, rax, rbx, rcx, rdx, rdi, rsi, r8=0, r9=0, r10=0, r11=0, xmm_buffer=0): + return self.helper.hypercall(rcx, rdx, r8, r9, r10, r11, rax, rbx, rdi, rsi, xmm_buffer) + + # Hypervisor-specific hypercall interfaces + + def hypercall64_five_args(self, vector, arg1=0, arg2=0, arg3=0, arg4=0, arg5=0): + return self.helper.hypercall(0, arg3, arg5, 0, arg4, 0, vector, 0, arg1, arg2) + + def hypercall64_memory_based(self, hypervisor_input_value, parameters, size = 0): + self.cs.mem.write_physical_mem(self.membuf0_pa, len(parameters[:0x1000]), parameters[:0x1000]) + regs = self.helper.hypercall(hypervisor_input_value & ~0x00010000, self.membuf0_pa, self.membuf1_pa) + self.output = self.helper.read_physical_mem(self.membuf1_pa, size) if size > 0 else '' + return regs + + def hypercall64_fast(self, hypervisor_input_value, param0 = 0, param1 = 0): + return self.helper.hypercall(hypervisor_input_value | 0x00010000, param0, param1) + + def hypercall64_extended_fast(self, hypervisor_input_value, parameter_block): + (param0, param1, xmm_regs) = struct.unpack(' SetFirmwareEnvironmentVariable( name='%s', GUID='%s', length=0x%X ).." % (name, "{%s}" % guid, var_len) ) @@ -755,6 +794,9 @@ class Win32Helper(Helper): #raise WinError(errno.EIO, "Unable to set EFI variable") return status + def delete_EFI_variable(self, name, guid): + return self.set_EFI_variable( name, guid, None, datasize=0, attrs=None ) + def list_EFI_variables( self, infcls=2 ): if logger().VERBOSE: logger().log( '[helper] -> NtEnumerateSystemEnvironmentValuesEx( infcls=%d )..' % infcls ) efi_vars = create_string_buffer( EFI_VAR_MAX_BUFFER_SIZE ) @@ -856,6 +898,22 @@ class Win32Helper(Helper): return tBuffer[:retVal], xsdt + # + # IOSF Message Bus access + # + + def msgbus_send_read_message( self, mcr, mcrx ): + logger().error( "[helper] Message Bus is not supported yet" ) + return None + + def msgbus_send_write_message( self, mcr, mcrx, mdr ): + logger().error( "[helper] Message Bus is not supported yet" ) + return None + + def msgbus_send_message( self, mcr, mcrx, mdr=None ): + logger().error( "[helper] Message Bus is not supported yet" ) + return None + # # File system # diff --git a/source/tool/chipsec/modules/common/bios_ts.py b/source/tool/chipsec/modules/common/bios_ts.py index 4968435b..984c086b 100644 --- a/source/tool/chipsec/modules/common/bios_ts.py +++ b/source/tool/chipsec/modules/common/bios_ts.py @@ -1,5 +1,5 @@ #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -21,9 +21,9 @@ """ -`BIOS Boot Hijacking and VMware Vulnerabilities Digging `_ - Sun Bing +Checks for BIOS Interface Lock including Top Swap Mode -Checks for BIOS Top Swap Mode +`BIOS Boot Hijacking and VMware Vulnerabilities Digging `_ - Sun Bing """ from chipsec.module_common import * @@ -34,62 +34,39 @@ class bios_ts(chipsec.module_common.BaseModule): BaseModule.__init__(self) def is_supported(self): - if not chipsec.chipset.is_register_defined( self.cs, 'GCS' ): - self.logger.error( "Couldn't find definition of required configuration registers (GCS)... skipping" ) - return False - if not chipsec.chipset.register_has_field( self.cs, 'GCS', 'BILD'): - self.logger.error( "Couldn't locate 'BIOS Interface Lock Down' bit in the configuration for the BUC register... skipping" ) - return False + #return (self.cs.get_chipset_id() not in chipsec.chipset.CHIPSET_FAMILY_ATOM) return True - def check_top_swap_mode(self): + def check_bios_iface_lock(self): + self.logger.start_test( "BIOS Interface Lock (including Top Swap Mode)" ) - self.logger.start_test( "BIOS Interface Lock and Top Swap Mode" ) - - if not chipsec.chipset.is_register_defined( self.cs, 'BC' ): - self.logger.warn("Couldn't locate the 'BC' register definition") + bild = 0 + if chipsec.chipset.is_control_defined( self.cs, 'BiosInterfaceLockDown' ): + bild = chipsec.chipset.get_control( self.cs, 'BiosInterfaceLockDown' ) + self.logger.log( "[*] BiosInterfaceLockDown (BILD) control = %d" % bild ) else: - bc_reg = chipsec.chipset.read_register( self.cs, 'BC' ) - chipsec.chipset.print_register( self.cs, 'BC', bc_reg ) - if not chipsec.chipset.register_has_field( self.cs, 'BC', 'TSS' ): - self.logger.warn( "Couldn't locate 'TSS' bit in the 'BC' register definition" ) - else: - tss = chipsec.chipset.get_register_field( self.cs, 'BC', bc_reg, 'TSS' ) - self.logger.log( "[*] BIOS Top Swap mode is %s" % ('enabled' if (1==tss) else 'disabled') ) - - if not chipsec.chipset.is_register_defined( self.cs, 'BUC' ): - self.logger.warn( "Couldn't locate the 'BUC' register definition" ) - else: - buc_reg = chipsec.chipset.read_register( self.cs, 'BUC' ) - chipsec.chipset.print_register( self.cs, 'BUC', buc_reg ) - if not chipsec.chipset.register_has_field( self.cs, 'BUC', 'TS' ): - self.logger.warn( "Couldn't locate 'TS' bit in the 'BUC' register definition" ) - else: - ts = chipsec.chipset.get_register_field( self.cs, 'BUC', buc_reg, 'TS' ) - self.logger.log( "[*] RTC version of TS = %x" % ts ) - - if not chipsec.chipset.is_register_defined( self.cs, 'GCS' ): - self.logger.error( "Couldn't locate required 'GCS' register definition" ) + self.logger.error( "BiosInterfaceLockDown (BILD) control is not defined" ) return ModuleResult.ERROR - gcs_reg = chipsec.chipset.read_register( self.cs, 'GCS' ) - chipsec.chipset.print_register( self.cs, 'GCS', gcs_reg ) - if not chipsec.chipset.register_has_field( self.cs, 'GCS', 'BILD' ): - self.logger.error( "Couldn't locate 'BILD' bit in the 'GCS' register definition" ) - return ModuleResult.ERROR - bild = chipsec.chipset.get_register_field( self.cs, 'GCS', gcs_reg, 'BILD' ) - self.logger.log( '' ) + if chipsec.chipset.is_control_defined( self.cs, 'TopSwapStatus' ): + tss = chipsec.chipset.get_control( self.cs, 'TopSwapStatus' ) + self.logger.log( "[*] BIOS Top Swap mode is %s (TSS = %d)" % ('enabled' if (1==tss) else 'disabled', tss) ) + + if chipsec.chipset.is_control_defined( self.cs, 'TopSwap' ): + ts = chipsec.chipset.get_control( self.cs, 'TopSwap' ) + self.logger.log( "[*] RTC TopSwap control (TS) = %x" % ts ) + if 0 == bild: + res = ModuleResult.FAILED self.logger.log_failed_check( "BIOS Interface is not locked (including Top Swap Mode)" ) - return ModuleResult.FAILED else: + res = ModuleResult.PASSED self.logger.log_passed_check( "BIOS Interface is locked (including Top Swap Mode)" ) - return ModuleResult.PASSED - + return res # -------------------------------------------------------------------------- # run( module_argv ) # Required function: run here all tests from this module # -------------------------------------------------------------------------- def run(self, module_argv ): - return self.check_top_swap_mode() + return self.check_bios_iface_lock() diff --git a/source/tool/chipsec/modules/common/uefi/s3bootscript.py b/source/tool/chipsec/modules/common/uefi/s3bootscript.py index 2e9b3f48..200355e9 100644 --- a/source/tool/chipsec/modules/common/uefi/s3bootscript.py +++ b/source/tool/chipsec/modules/common/uefi/s3bootscript.py @@ -1,5 +1,5 @@ #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -138,7 +138,8 @@ class s3bootscript(BaseModule): else: found,bootscript_PAs = self._uefi.find_s3_bootscript() if not found: - self.logger.log_good( "S3 Boot-Script wasn't found in EFI variables (BIOS may still be using some other mechanism to locate it)" ) + self.logger.log_good( "Didn't find any S3 boot-scripts in EFI variables" ) + self.logger.log_warn_check( "S3 Boot-Script was not found. Firmware may be using other ways to store/locate it" ) return ModuleResult.WARNING self.logger.log_important( 'Found %d S3 boot-script(s) in EFI variables' % len(bootscript_PAs) ) @@ -165,7 +166,7 @@ class s3bootscript(BaseModule): else: # BOOTSCRIPT_INSIDE_SMRAM status = ModuleResult.WARNING - self.logger.log( "S3 Boot-Script is inside SMRAM. The script is protected but Dispatch opcodes cannot be inspected" ) + self.logger.log_warn_check( "S3 Boot-Script is inside SMRAM. The script is protected but Dispatch opcodes cannot be inspected" ) self.logger.log_important( "Additional testing of the S3 boot-script can be done using tools.uefi.s3script_modify" ) @@ -183,14 +184,15 @@ class s3bootscript(BaseModule): script_pa = int(module_argv[0],16) self.logger.log( '[*] Using manually assigned S3 Boot-Script table base: 0x%016X' % script_pa ) (self.smrambase, self.smramlimit, self.smramsize) = self.cs.cpu.get_SMRAM() - self.logger.log( '[*] SMRAM: Base = 0x%016X, Limit = 0x%016X, Size = 0x%08X' % (self.smrambase, self.smramlimit, self.smramsize) ) + if self.smrambase is not None and self.smramlimit is not None: + self.logger.log( '[*] SMRAM: Base = 0x%016X, Limit = 0x%016X, Size = 0x%08X' % (self.smrambase, self.smramlimit, self.smramsize) ) try: if script_pa is not None: return self.check_s3_bootscripts( script_pa ) else: return self.check_s3_bootscripts( ) except: - logger().error("The module was not able to recognize the S3 resume boot script on this platform.") - if logger().VERBOSE: raise + self.logger.error("The module was not able to recognize the S3 resume boot script on this platform.") + if self.logger.VERBOSE: raise return ModuleResult.ERROR diff --git a/source/tool/chipsec/modules/remap.py b/source/tool/chipsec/modules/remap.py index f31f678e..293b3d25 100644 --- a/source/tool/chipsec/modules/remap.py +++ b/source/tool/chipsec/modules/remap.py @@ -17,8 +17,8 @@ #Contact information: #chipsec@intel.com # - - + + """ `Preventing & Detecting Xen Hypervisor Subversions `_ by Joanna Rutkowska & Rafal Wojtczuk @@ -83,7 +83,7 @@ class remap(BaseModule): self.logger.log( "[*] Top Of Upper Memory: 0x%016X" % touud ) self.logger.log( "[*] Remap Limit Address: 0x%016X" % (remaplimit|0xFFFFF) ) self.logger.log( "[*] Remap Base Address : 0x%016X" % remapbase ) - self.logger.log( "[*] 4GB : 0x%016X" % self.cs.Cfg.BIT32 ) + self.logger.log( "[*] 4GB : 0x%016X" % chipsec.defines.BIT32 ) self.logger.log( "[*] Top Of Low Memory : 0x%016X" % tolud ) self.logger.log( "[*] TSEG (SMRAM) Base : 0x%016X\n" % tsegmb ) @@ -100,10 +100,10 @@ class remap(BaseModule): if ok: self.logger.log_good( " Remap window configuration is correct: REMAPBASE <= REMAPLIMIT < TOUUD" ) else: self.logger.log_bad( " Remap window configuration is not correct" ) - ok = (0 == tolud & self.cs.Cfg.ALIGNED_1MB) and \ - (0 == touud & self.cs.Cfg.ALIGNED_1MB) and \ - (0 == remapbase & self.cs.Cfg.ALIGNED_1MB) and \ - (0 == remaplimit & self.cs.Cfg.ALIGNED_1MB) + ok = (0 == tolud & chipsec.defines.ALIGNED_1MB) and \ + (0 == touud & chipsec.defines.ALIGNED_1MB) and \ + (0 == remapbase & chipsec.defines.ALIGNED_1MB) and \ + (0 == remaplimit & chipsec.defines.ALIGNED_1MB) remap_ok = remap_ok and ok if ok: self.logger.log_good( " All addresses are 1MB aligned" ) else: self.logger.log_bad( " Not all addresses are 1MB aligned" ) diff --git a/source/tool/chipsec/modules/tools/secureboot/te.cfg b/source/tool/chipsec/modules/tools/secureboot/te.cfg new file mode 100644 index 00000000..2de16bdf --- /dev/null +++ b/source/tool/chipsec/modules/tools/secureboot/te.cfg @@ -0,0 +1 @@ +EFI\Boot\Shell.efi diff --git a/source/tool/chipsec/modules/tools/smm/smm_config.ini b/source/tool/chipsec/modules/tools/smm/smm_config.ini new file mode 100644 index 00000000..b5de490b --- /dev/null +++ b/source/tool/chipsec/modules/tools/smm/smm_config.ini @@ -0,0 +1,14 @@ +Name=SMI_name +Desc=custom_description +SMI_code=* +SMI_data=* +RAX=PTR +RBX=PTR +RCX=PTR +RDX=PTR +RSI=PTR +RDI=PTR +PTR_OFFSET=0x10 +SIG=4141 +SIG_OFFSET=0x0 + diff --git a/source/tool/chipsec/modules/tools/uefi/s3script_modify.py b/source/tool/chipsec/modules/tools/uefi/s3script_modify.py index 413d4b13..b6d07b16 100644 --- a/source/tool/chipsec/modules/tools/uefi/s3script_modify.py +++ b/source/tool/chipsec/modules/tools/uefi/s3script_modify.py @@ -1,5 +1,5 @@ #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -23,32 +23,54 @@ """ .. note:: This module will attempt to modify the S3 Boot Script on the platform. Doing this could cause the platform to malfunction. Use with care! + Usage: + Replacing existing opcode: + chipsec_main.py -m tools.uefi.s3script_modify -a replace_op,,
, + = pci_wr|mmio_wr|io_wr|pci_rw|mmio_rw|io_rw + chipsec_main.py -m tools.uefi.s3script_modify -a replace_op,mem[,
,] + chipsec_main.py -m tools.uefi.s3script_modify -a replace_op,dispatch + chipsec_main.py -m tools.uefi.s3script_modify -a replace_op,dispatch_ep + + Adding new opcode: + chipsec_main.py -m tools.uefi.s3script_modify -a add_op,,
,, + = pci_wr|mmio_wr|io_wr + chipsec_main.py -m tools.uefi.s3script_modify -a add_op,dispatch[,] + Examples: - ``chipsec_main.py -m tools.uefi.s3script_modify -a ,
,`` + ``chipsec_main.py -m tools.uefi.s3script_modify -a replace_op,,
,`` = pci_wr|mmio_wr|io_wr|pci_rw|mmio_rw|io_rw The option will look for a script opcode that writes to PCI config, MMIO or I/O registers and modify the opcode to write the given value to the register with the given address. After executing this, if the system is vulnerable to boot script modification, the hardware configuration will have changed according to given . - ``chipsec_main.py -m tools.uefi.s3script_modify -a mem`` + ``chipsec_main.py -m tools.uefi.s3script_modify -a replace_op,mem`` The option will look for a script opcode that writes to memory and modify the opcode to write the given value to the given address. By default this test will allocate memory and write write 0xB007B007 that location. After executing this, if the system is vulnerable to boot script modification, you should find the given value in the allocated memory location. - ``chipsec_main.py -m tools.uefi.s3script_modify -a dispatch`` - The modify_dispatch option will look for a dispatch opcode in the script and + ``chipsec_main.py -m tools.uefi.s3script_modify -a replace_op,dispatch`` + The option will look for a dispatch opcode in the script and modify the opcode to point to a different entry point. The new entry point will contain a HLT instruction. After executing this, if the system is vulnerable to boot script modification, the system should hang on resume from S3. - ``chipsec_main.py -m tools.uefi.s3script_modify -a dispatch_ep`` - The modify_dispatch_ep option will look for a dispatch opcode in the script and + ``chipsec_main.py -m tools.uefi.s3script_modify -a replace_op,dispatch_ep`` + The option will look for a dispatch opcode in the script and will modify memory at the entry point for that opcode. The modified instructions will contain a HLT instruction. After executing this, if the system is vulnerable to dispatch opcode entry point modification, the system should hang on resume from S3. + + ``chipsec_main.py -m tools.uefi.s3script_modify -a add_op,,
,,`` + = pci_wr|mmio_wr|io_wr + The option will add a new opcode which writes to PCI config, MMIO or I/O + registers with specified values. + ``chipsec_main.py -m tools.uefi.s3script_modify -a add_op,dispatch`` + The option will add a new DISPATCH opcode to the script with entry point to + either existing or newly allocated memory. + """ examples_str = """ Examples: diff --git a/source/tool/chipsec/modules/tools/vmm/cpuid_fuzz.py b/source/tool/chipsec/modules/tools/vmm/cpuid_fuzz.py index 0a27412e..031605d8 100644 --- a/source/tool/chipsec/modules/tools/vmm/cpuid_fuzz.py +++ b/source/tool/chipsec/modules/tools/vmm/cpuid_fuzz.py @@ -1,5 +1,5 @@ #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -74,12 +74,12 @@ class cpuid_fuzz (BaseModule): self.logger.log( "[*] CPUID EAX: 0x%08X" % eax ) if _FUZZ_ECX_RANDOM: ecx = random.randint( 0, 0xFFFFFFFF ) - (r_eax, r_ebx, r_ecx, r_edx) = self.cs.cpuid.cpuid( eax, ecx ) + (r_eax, r_ebx, r_ecx, r_edx) = self.cs.cpu.cpuid( eax, ecx ) else: for ecx in range( _MAX_ECX ): logger.log( " > ECX: 0x%08X" % ecx ) if _FLUSH_LOG_EACH_ITER: self.logger.flush() - (r_eax, r_ebx, r_ecx, r_edx) = self.cs.cpuid.cpuid( eax, ecx ) + (r_eax, r_ebx, r_ecx, r_edx) = self.cs.cpu.cpuid( eax, ecx ) if _LOG_OUT_RESULTS: logger.log( " Out: EAX=0x%08X, EBX=0x%08X, ECX=0x%08X, EDX=0x%08X" % (r_eax,r_ebx,r_ecx,r_edx) ) it += 1 return True diff --git a/source/tool/chipsec/modules/tools/vmm/pcie_fuzz.py b/source/tool/chipsec/modules/tools/vmm/pcie_fuzz.py index 1cddfe7f..59b94ecd 100644 --- a/source/tool/chipsec/modules/tools/vmm/pcie_fuzz.py +++ b/source/tool/chipsec/modules/tools/vmm/pcie_fuzz.py @@ -1,5 +1,5 @@ #CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, Intel Corporation +#Copyright (c) 2010-2016, Intel Corporation # #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License @@ -173,7 +173,7 @@ def fuzz_pcie_device( b, d, f ): if CALC_MMIO_SIZE: size = get_mmio_range_size( b, d, f, bar_off ) logger.log( "[*] + 0x%02X (%X): MMIO BAR at 0x%016X (64-bit? %d) with size: 0x%08X. Fuzzing.." % (bar_off,bar_reg,bar,is64bit,size) ) - if ACTIVE_RANGE and size > 0x1000: #Vbox GT MMIO size = 0x02000000 + if ACTIVE_RANGE and size > 0x1000: list = [] list = find_active_range(bar, size) if len(list) > 0: diff --git a/source/tool/chipsec/modules/tools/vmm/vbox/__init__.py b/source/tool/chipsec/modules/tools/vmm/vbox/__init__.py new file mode 100644 index 00000000..55ab1083 --- /dev/null +++ b/source/tool/chipsec/modules/tools/vmm/vbox/__init__.py @@ -0,0 +1,21 @@ +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# + + diff --git a/source/tool/chipsec/modules/tools/vmm/vbox/vbox_crash_apicbase.py b/source/tool/chipsec/modules/tools/vmm/vbox/vbox_crash_apicbase.py new file mode 100644 index 00000000..e6d323dc --- /dev/null +++ b/source/tool/chipsec/modules/tools/vmm/vbox/vbox_crash_apicbase.py @@ -0,0 +1,51 @@ +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# + + + +""" +PoC test for Host OS Crash when writing to IA32_APIC_BASE MSR (Oracle VirtualBox CVE-2015-0377) +http://www.oracle.com/technetwork/topics/security/cpujan2015-1972971.html + + Usage: + ``chipsec_main.py -i -m tools.vmm.vbox_crash_apicbase`` +""" + +from chipsec.module_common import * + +_MODULE_NAME = 'vbox_crash_apicbase' + +class vbox_crash_apicbase (BaseModule): + + def run( self, module_argv ): + self.logger.start_test( "Host OS Crash due to IA32_APIC_BASE (Oracle VirtualBox CVE-2015-0377)" ) + + tid = 0 + #(eax, edx) = self.cs.msr.read_msr( tid, 0x1B ) + #self.cs.msr.write_msr( tid, 0x1B, eax, 0xDEADBEEF ) + apicbase_msr = chipsec.chipset.read_register( self.cs, 'IA32_APIC_BASE', tid ) + chipsec.chipset.print_register( self.cs, 'IA32_APIC_BASE', apicbase_msr ) + apicbase_msr = 0xDEADBEEF00000000 | (apicbase_msr & 0xFFFFFFFF) + self.logger.log( "[*] writing 0x%016X to IA32_APIC_BASE MSR.." % apicbase_msr ) + chipsec.chipset.write_register( self.cs, 'IA32_APIC_BASE', apicbase_msr, tid ) + + # If we are here, then we are fine ;) + self.logger.log_passed_check( "VMM/Host OS didn't crash (not vulnerable)" ) + return ModuleResult.PASSED diff --git a/source/tool/chipsec/utilcmd/cpu_cmd.py b/source/tool/chipsec/utilcmd/cpu_cmd.py index 625c1ddf..3c0b3eba 100644 --- a/source/tool/chipsec/utilcmd/cpu_cmd.py +++ b/source/tool/chipsec/utilcmd/cpu_cmd.py @@ -38,12 +38,16 @@ class CPUCommand(BaseCommand): """ >>> chipsec_util cpu info >>> chipsec_util cpu cr [value] + >>> chipsec_util cpu cpuid [ecx] + >>> chipsec_util cpu pt [paging_base_cr3] Examples: >>> chipsec_util cpu info >>> chipsec_util cpu cr 0 0 >>> chipsec_util cpu cr 0 4 0x0 + >>> chipsec_util cpu cpuid 40000000 + >>> chipsec_util cpu pt """ def requires_driver(self): @@ -59,44 +63,86 @@ class CPUCommand(BaseCommand): op = self.argv[2] t = time.time() - try: - _cpu = chipsec.hal.cpu.CPU(self.cs) - except chipsec.hal.cpu.CPURuntimeError, msg: - print msg - return - if 'info' == op: self.logger.log( "[CHIPSEC] CPU information:" ) - ht = _cpu.is_HT_active() - threads_per_core = _cpu.get_number_logical_processor_per_core() - threads_per_pkg = _cpu.get_number_logical_processor_per_package() - cores_per_pkg = _cpu.get_number_physical_processor_per_package() - threads_count = _cpu.get_number_threads_from_APIC_table() - sockets_count = _cpu.get_number_sockets_from_APIC_table() + ht = self.cs.cpu.is_HT_active() + threads_per_core = self.cs.cpu.get_number_logical_processor_per_core() + threads_per_pkg = self.cs.cpu.get_number_logical_processor_per_package() + cores_per_pkg = self.cs.cpu.get_number_physical_processor_per_package() self.logger.log( " Hyper-Threading : %s" % ('Enabled' if ht else 'Disabled') ) self.logger.log( " CPU cores per package : %d" % cores_per_pkg ) self.logger.log( " CPU threads per core : %d" % threads_per_core ) self.logger.log( " CPU threads per package : %d" % threads_per_pkg ) - self.logger.log( " Number of sockets : %d" % sockets_count ) - self.logger.log( " Number of CPU threads : %d" % threads_count ) + try: + threads_count = self.cs.cpu.get_number_threads_from_APIC_table() + sockets_count = self.cs.cpu.get_number_sockets_from_APIC_table() + self.logger.log( " Number of sockets : %d" % sockets_count ) + self.logger.log( " Number of CPU threads : %d" % threads_count ) + except: + pass elif 'cr' == op: - if len(self.argv) < 5: + + if len(self.argv) > 5: + cpu_thread_id = int(self.argv[3],10) + cr_number = int(self.argv[4],16) + value = int(self.argv[5], 16) + self.logger.log( "[CHIPSEC] CPU%d: write CR%d <- 0x%08X" % (cpu_thread_id, cr_number, value) ) + self.cs.cpu.write_cr( cpu_thread_id, cr_number, value ) + return True + elif len(self.argv) > 4: + cpu_thread_id = int(self.argv[3],10) + cr_number = int(self.argv[4],16) + value = self.cs.cpu.read_cr( cpu_thread_id, cr_number ) + self.logger.log( "[CHIPSEC] CPU%d: read CR%d -> 0x%08X" % (cpu_thread_id, cr_number, value) ) + return value + else: + for tid in range(self.cs.msr.get_cpu_thread_count()): + cr0 = self.cs.cpu.read_cr( tid, 0 ) + cr2 = self.cs.cpu.read_cr( tid, 2 ) + cr3 = self.cs.cpu.read_cr( tid, 3 ) + cr4 = self.cs.cpu.read_cr( tid, 4 ) + cr8 = self.cs.cpu.read_cr( tid, 8 ) + self.logger.log( "[CHIPSEC][cpu%d] x86 Control Registers:" % tid ) + self.logger.log( " CR0: 0x%016X" % cr0 ) + self.logger.log( " CR2: 0x%016X" % cr2 ) + self.logger.log( " CR3: 0x%016X" % cr3 ) + self.logger.log( " CR4: 0x%016X" % cr4 ) + self.logger.log( " CR8: 0x%016X" % cr8 ) + + elif 'cpuid' == op: + if len(self.argv) < 4: print CPUCommand.__doc__ return - cpu_thread_id = int(self.argv[3],10) - cr_number = int(self.argv[4],16) + eax = int(self.argv[3],16) + ecx = int(self.argv[4],16) if 5 == len(self.argv) else 0 - if len(self.argv) > 5: - value = int(self.argv[5], 16) - self.logger.log( "[CHIPSEC] CPU: %d write CR%d <- 0x%08X" % (cpu_thread_id, cr_number, value) ) - self.cs.cpu.write_cr( cpu_thread_id, cr_number, value ) - return True + self.logger.log( "[CHIPSEC] CPUID < EAX: 0x%08X" % eax) + self.logger.log( "[CHIPSEC] ECX: 0x%08X" % ecx) + + (_eax,_ebx,_ecx,_edx) = self.cs.cpu.cpuid( eax, ecx ) + + self.logger.log( "[CHIPSEC] CPUID > EAX: 0x%08X" % _eax ) + self.logger.log( "[CHIPSEC] EBX: 0x%08X" % _ebx ) + self.logger.log( "[CHIPSEC] ECX: 0x%08X" % _ecx ) + self.logger.log( "[CHIPSEC] EDX: 0x%08X" % _edx ) + + elif op == "pt": + + if len(self.argv) == 4: + cr3 = int(self.argv[3],16) + pt_fname = 'pt_%08X' % cr3 + self.logger.log( "[CHIPSEC] paging physical base (CR3): 0x%016X" % cr3 ) + self.logger.log( "[CHIPSEC] dumping paging hierarchy to '%s'..." % pt_fname ) + self.cs.cpu.dump_page_tables( cr3, pt_fname ) else: - value = self.cs.cpu.read_cr( cpu_thread_id, cr_number ) - self.logger.log( "[CHIPSEC] CPU: %d read CR%d -> 0x%08X" % (cpu_thread_id, cr_number, value) ) - return value + for tid in range(self.cs.msr.get_cpu_thread_count()): + cr3 = self.cs.cpu.read_cr( tid, 3 ) + pt_fname = 'cpu%d_pt_%08X' % (tid,cr3) + self.logger.log( "[CHIPSEC][cpu%d] paging physical base (CR3): 0x%016X" % (tid,cr3) ) + self.logger.log( "[CHIPSEC][cpu%d] dumping paging hierarchy to '%s'..." % (tid,pt_fname) ) + self.cs.cpu.dump_page_tables( cr3, pt_fname ) else: print CPUCommand.__doc__ diff --git a/source/tool/chipsec/utilcmd/cpuid_cmd.py b/source/tool/chipsec/utilcmd/cpuid_cmd.py deleted file mode 100644 index d28082be..00000000 --- a/source/tool/chipsec/utilcmd/cpuid_cmd.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/local/bin/python -#CHIPSEC: Platform Security Assessment Framework -#Copyright (c) 2010-2015, 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 -# - - - - -__version__ = '1.0' - -from chipsec.command import BaseCommand - -# ################################################################### -# -# CPUid -# -# ################################################################### -class CPUIDCommand(BaseCommand): - """ - >>> chipsec_util cpuid [ecx] - - Examples: - - >>> chipsec_util cpuid 40000000 - """ - - def requires_driver(self): - # No driver required when printing the util documentation - if len(self.argv) < 3: - return False - return True - - def run(self): - if len(self.argv) < 3: - print CPUIDCommand.__doc__ - return - - eax = int(self.argv[2],16) - ecx = int(self.argv[3],16) if 4 == len(self.argv) else 0 - - self.logger.log( "[CHIPSEC] CPUID < EAX: 0x%08X" % eax) - self.logger.log( "[CHIPSEC] ECX: 0x%08X" % ecx) - - val = self.cs.cpuid.cpuid( eax, ecx ) - - self.logger.log( "[CHIPSEC] CPUID > EAX: 0x%08X" % (val[0]) ) - self.logger.log( "[CHIPSEC] EBX: 0x%08X" % (val[1]) ) - self.logger.log( "[CHIPSEC] ECX: 0x%08X" % (val[2]) ) - self.logger.log( "[CHIPSEC] EDX: 0x%08X" % (val[3]) ) - -commands = { 'cpuid': CPUIDCommand } diff --git a/source/tool/chipsec/utilcmd/ec_cmd.py b/source/tool/chipsec/utilcmd/ec_cmd.py new file mode 100644 index 00000000..ffa77473 --- /dev/null +++ b/source/tool/chipsec/utilcmd/ec_cmd.py @@ -0,0 +1,120 @@ +#!/usr/local/bin/python +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# + + + +__version__ = '1.0' + +import os +import sys +import time + +import chipsec_util +from chipsec.command import BaseCommand + +from chipsec.logger import * +from chipsec.file import * +from chipsec.hal.ec import * + + +# Embedded Controller +class ECCommand(BaseCommand): + """ + >>> chipsec_util ec dump [] + >>> chipsec_util ec command + >>> chipsec_util ec read [] + >>> chipsec_util ec write + >>> chipsec_util ec index [] + + Examples: + + >>> chipsec_util ec dump + >>> chipsec_util ec command 0x001 + >>> chipsec_util ec read 0x2F + >>> chipsec_util ec write 0x2F 0x00 + >>> chipsec_util ec index + """ + def requires_driver(self): + # No driver required when printing the util documentation + if len(self.argv) < 3: + return False + return True + + def run(self): + if len(self.argv) < 3: + print ECCommand.__doc__ + return + + op = self.argv[2] + t = time.time() + + try: + _ec = EC( self.cs ) + except BaseException, msg: + print msg + return + + if ( 'command' == op ): + cmd = int(self.argv[3],16) + self.logger.log( "[CHIPSEC] Sending EC command 0x%X" % cmd ) + _ec.write_command( cmd ) + elif ( 'dump' == op ): + size = int(self.argv[3],16) if len(self.argv) > 3 else 0x100 + buf = _ec.read_range( 0, size ) + self.logger.log( "[CHIPSEC] EC RAM:" ) + print_buffer( buf ) + elif ( 'read' == op ): + start_off = int(self.argv[3],16) + if len(self.argv) > 4: + size = int(self.argv[4],16) + buf = _ec.read_range( start_off, size ) + self.logger.log( "[CHIPSEC] EC memory read: offset 0x%X size 0x%X" % (start_off, size) ) + print_buffer( buf ) + else: + val = _ec.read_memory( start_off ) if start_off < 0x100 else _ec.read_memory_extended( start_off ) + self.logger.log( "[CHIPSEC] EC memory read: offset 0x%X = 0x%X" % (start_off, val) ) + elif ( 'write' == op ): + off = int(self.argv[3],16) + val = int(self.argv[4],16) + self.logger.log( "[CHIPSEC] EC memory write: offset 0x%X = 0x%X" % (off, val) ) + if off < 0x100: _ec.write_memory( off, val ) + else: _ec.write_memory_extended( off, val ) + elif ( 'index' == op ): + if len(self.argv) == 3: + self.logger.log( "[CHIPSEC] EC index I/O: dumping memory..." ) + mem = [] + for off in range(0x10000): + mem.append( chr(_ec.read_idx( off )) ) + print_buffer( mem ) + del mem + elif len(self.argv) == 4: + off = int(self.argv[3],16) + val = _ec.read_idx(off) + self.logger.log( "[CHIPSEC] EC index I/O: reading memory offset 0x%X: 0x%X" % (off, val) ) + else: + self.logger.error( "unknown command-line option '%.32s'" % op ) + print ECCommand.__doc__ + return + + self.logger.log( "[CHIPSEC] (ec) time elapsed %.3f" % (time.time()-t) ) + + +commands = { 'ec': ECCommand } diff --git a/source/tool/chipsec/utilcmd/iommu_cmd.py b/source/tool/chipsec/utilcmd/iommu_cmd.py index a070d5b6..cbb8758d 100644 --- a/source/tool/chipsec/utilcmd/iommu_cmd.py +++ b/source/tool/chipsec/utilcmd/iommu_cmd.py @@ -45,6 +45,7 @@ class IOMMUCommand(BaseCommand): >>> chipsec_util iommu config [iommu_engine] >>> chipsec_util iommu status [iommu_engine] >>> chipsec_util iommu enable|disable + >>> chipsec_util iommu pt Examples: @@ -52,6 +53,7 @@ class IOMMUCommand(BaseCommand): >>> chipsec_util iommu config VTD >>> chipsec_util iommu status GFXVTD >>> chipsec_util iommu enable VTD + >>> chipsec_util iommu pt """ def requires_driver(self): @@ -102,6 +104,7 @@ class IOMMUCommand(BaseCommand): for e in _iommu_engines: if 'config' == op: _iommu.dump_IOMMU_configuration( e ) + elif 'pt' == op: _iommu.dump_IOMMU_page_tables( e ) elif 'status' == op: _iommu.dump_IOMMU_status( e ) elif 'enable' == op: _iommu.set_IOMMU_Translation( e, 1 ) elif 'disable' == op: _iommu.set_IOMMU_Translation( e, 0 ) @@ -112,4 +115,4 @@ class IOMMUCommand(BaseCommand): self.logger.log( "[CHIPSEC] (iommu) time elapsed %.3f" % (time.time()-t) ) -commands = { 'iommu': IOMMUCommand } +commands = { 'iommu': IOMMUCommand } diff --git a/source/tool/chipsec/utilcmd/msgbus_cmd.py b/source/tool/chipsec/utilcmd/msgbus_cmd.py new file mode 100644 index 00000000..569affd1 --- /dev/null +++ b/source/tool/chipsec/utilcmd/msgbus_cmd.py @@ -0,0 +1,100 @@ +#!/usr/local/bin/python +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# + + + +__version__ = '1.0' + +import os +import sys +import time + +import chipsec_util +from chipsec.command import BaseCommand + +from chipsec.logger import * +from chipsec.file import * +from chipsec.hal.msgbus import MsgBus + + +# Message Bus +class MsgBusCommand(BaseCommand): + """ + >>> chipsec_util msgbus read + >>> chipsec_util msgbus write + >>> chipsec_util msgbus message [value] + >>> + >>> : message bus port of the target unit + >>> : message bus register/offset in the target unit port + >>> : value to be written to the message bus register/offset + >>> : opcode of the message on the message bus + + Examples: + + >>> chipsec_util msgbus read 0x3 0x2E + >>> chipsec_util msgbus write 0x3 0x27 0xE0000001 + >>> chipsec_util msgbus message 0x3 0x2E 0x10 + >>> chipsec_util msgbus message 0x3 0x2E 0x11 0x0 + """ + def requires_driver(self): + # No driver required when printing the util documentation + if len(self.argv) < 3: + return False + return True + + def run(self): + if len(self.argv) > 7 or len(self.argv) < 5: + print MsgBusCommand.__doc__ + return + + op = self.argv[2] + t = time.time() + + _msgbus = self.cs.msgbus + + res = None + port = int(self.argv[3], 16) + reg = int(self.argv[4], 16) + + if 'read' == op: + self.logger.log("[CHIPSEC] msgbus read: port 0x%02X + 0x%08X" % (port, reg)) + res = _msgbus.msgbus_reg_read( port, reg ) + elif 'write' == op: + if len(self.argv) < 6: + print msgbuscmd.__doc__ + return + val = int(self.argv[5], 16) + self.logger.log("[CHIPSEC] msgbus write: port 0x%02X + 0x%08X < 0x%08X" % (port, reg, val)) + res = _msgbus.msgbus_reg_write( port, reg, val ) + elif 'message' == op: + opcode = int(self.argv[5], 16) + val = None if len(self.argv) < 7 else int(self.argv[6], 16) + self.logger.log("[CHIPSEC] msgbus message: port 0x%02X + 0x%08X, opcode: 0x%02X" % (port, reg, opcode)) + if val is not None: self.logger.log("[CHIPSEC] data: 0x%08X" % val) + res = _msgbus.msgbus_send_message( port, reg, opcode, val ) + else: + print msgbuscmd.__doc__ + return + + if res is not None: self.logger.log("[CHIPSEC] result: 0x%08X" % res) + self.logger.log( "[CHIPSEC] (msgbus) time elapsed %.3f" % (time.time()-t) ) + +commands = { 'msgbus': MsgBusCommand } diff --git a/source/tool/chipsec/utilcmd/spi_cmd.py b/source/tool/chipsec/utilcmd/spi_cmd.py index bff5f999..4039fa08 100644 --- a/source/tool/chipsec/utilcmd/spi_cmd.py +++ b/source/tool/chipsec/utilcmd/spi_cmd.py @@ -18,8 +18,8 @@ #Contact information: #chipsec@intel.com # - - + + """ CHIPSEC includes functionality for reading and writing the SPI flash. When an image file is created from reading the SPI flash, this image can be parsed to reveal sections, files, variables, etc. @@ -75,61 +75,60 @@ class SPICommand(BaseCommand): t = time.time() + _msg = "it may take a few minutes (use DEBUG or VERBOSE logger options to see progress)" if ( 'erase' == spi_op ): spi_fla = int(self.argv[3],16) - self.logger.log( "[CHIPSEC] Erasing SPI Flash block at FLA = 0x%X" % spi_fla ) - #if not _spi.disable_BIOS_write_protection(): - # self.logger.error( "Could not disable SPI Flash protection. Still trying.." ) + self.logger.log( "[CHIPSEC] erasing SPI flash memory block at FLA = 0x%X" % spi_fla ) ok = _spi.erase_spi_block( spi_fla ) - if ok: self.logger.log_result( "SPI Flash erase done" ) - else: self.logger.warn( "SPI Flash erase returned error (turn on VERBOSE)" ) + if ok: self.logger.log_result( "completed SPI flash memory erase" ) + else: self.logger.warn( "SPI flash erase returned error (turn on VERBOSE)" ) elif ( 'write' == spi_op and 5 == len(self.argv) ): spi_fla = int(self.argv[3],16) filename = self.argv[4] - self.logger.log( "[CHIPSEC] Writing to SPI Flash at FLA = 0x%X from '%.64s'" % (spi_fla, filename) ) - #if not _spi.disable_BIOS_write_protection(): - # self.logger.error( "Could not disable SPI Flash protection. Still trying.." ) + self.logger.log( "[CHIPSEC] writing to SPI flash memory at FLA = 0x%X from '%.64s'" % (spi_fla, filename) ) ok = _spi.write_spi_from_file( spi_fla, filename ) - if ok: self.logger.log_result( "SPI Flash write done" ) - else: self.logger.warn( "SPI Flash write returned error (turn on VERBOSE)" ) + if ok: self.logger.log( "[CHIPSEC] completed SPI flash memory write" ) + else: self.logger.warn( "SPI flash write returned error (turn on VERBOSE)" ) elif ( 'read' == spi_op ): spi_fla = int(self.argv[3],16) length = int(self.argv[4],16) - self.logger.log( "[CHIPSEC] Reading 0x%x bytes from SPI Flash starting at FLA = 0x%X" % (length, spi_fla) ) + self.logger.log( "[CHIPSEC] reading 0x%x bytes from SPI Flash starting at FLA = 0x%X" % (length, spi_fla) ) + self.logger.log( "[CHIPSEC] %s" % _msg ) out_file = None if 6 == len(self.argv): out_file = self.argv[5] buf = _spi.read_spi_to_file( spi_fla, length, out_file ) - if (buf is None): self.logger.error( "SPI Flash read didn't return any data (turn on VERBOSE)" ) - else: self.logger.log_result( "SPI Flash read done" ) + if (buf is None): self.logger.error( "SPI flash read didn't return any data (turn on VERBOSE)" ) + else: self.logger.log( "[CHIPSEC] completed SPI flash memory read" ) elif ( 'info' == spi_op ): - self.logger.log( "[CHIPSEC] SPI Flash Info\n" ) + self.logger.log( "[CHIPSEC] SPI flash memory information\n" ) ok = _spi.display_SPI_map() elif ( 'dump' == spi_op ): out_file = 'rom.bin' if 4 == len(self.argv): out_file = self.argv[3] - self.logger.log( "[CHIPSEC] Dumping entire SPI Flash to '%s'" % out_file ) + self.logger.log( "[CHIPSEC] dumping entire SPI flash memory to '%s'" % out_file ) + self.logger.log( "[CHIPSEC] %s" % _msg ) # @TODO: don't assume SPI Flash always ends with BIOS region (base,limit,freg) = _spi.get_SPI_region( BIOS ) spi_size = limit + 1 - self.logger.log( "[CHIPSEC] BIOS Region: Base = 0x%08X, Limit = 0x%08X" % (base,limit) ) - self.logger.log( "[CHIPSEC] Dumping 0x%08X bytes (to the end of BIOS region)" % spi_size ) + self.logger.log( "[CHIPSEC] BIOS region: base = 0x%08X, limit = 0x%08X" % (base,limit) ) + self.logger.log( "[CHIPSEC] dumping 0x%08X bytes (to the end of BIOS region)" % spi_size ) buf = _spi.read_spi_to_file( 0, spi_size, out_file ) - if (buf is None): self.logger.error( "Dumping SPI Flash didn't return any data (turn on VERBOSE)" ) - else: self.logger.log_result( "Done dumping SPI Flash" ) + if (buf is None): self.logger.error( "dumping SPI Flash didn't return any data (turn on VERBOSE)" ) + else: self.logger.log( "[CHIPSEC] completed SPI flash dump to '%s'" % out_file ) elif ( 'disable-wp' == spi_op ): - self.logger.log( "[CHIPSEC] Trying to disable BIOS write protection.." ) + self.logger.log( "[CHIPSEC] trying to disable BIOS write protection.." ) # # This write protection only matters for BIOS range in SPI flash memory # if _spi.disable_BIOS_write_protection(): self.logger.log_good( "BIOS region write protection is disabled in SPI flash" ) else: - self.logger.log_bad( "Couldn't disable BIOS region write protection in SPI flash" ) + self.logger.log_bad( "couldn't disable BIOS region write protection in SPI flash" ) else: print SPICommand.__doc__ return diff --git a/source/tool/chipsec/utilcmd/vmm_cmd.py b/source/tool/chipsec/utilcmd/vmm_cmd.py new file mode 100644 index 00000000..519be8b7 --- /dev/null +++ b/source/tool/chipsec/utilcmd/vmm_cmd.py @@ -0,0 +1,124 @@ +#!/usr/local/bin/python +#CHIPSEC: Platform Security Assessment Framework +#Copyright (c) 2010-2016, 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 +# + + + +__version__ = '1.0' + +import os +import sys +import time + +import chipsec_util +from chipsec.command import BaseCommand + +from chipsec.logger import * +from chipsec.file import * +from chipsec.hal.vmm import * + +class VMMCommand(BaseCommand): + """ + >>> chipsec_util vmm hypercall [r8] [r9] [r10] [r11] + >>> chipsec_util vmm hypercall + >>> chipsec_util vmm pt|ept + + Examples: + + >>> chipsec_util vmm hypercall 32 0 0 0 0 0 + >>> chipsec_util vmm pt 0x524B01E + + """ + + def requires_driver(self): + # No driver required when printing the util documentation + if len(self.argv) < 3: + return False + return True + + def run(self): + + if len(self.argv) < 3: + print VMMCommand.__doc__ + return + + op = self.argv[2] + t = time.time() + + try: + vmm = VMM( self.cs ) + except VMMRuntimeError, msg: + print msg + return + + vmm.init(); + + if op == "hypercall": + + gprs_cnt = len(self.argv) - 3 + if (gprs_cnt < 6) or (gprs_cnt > 10): + print VMMCommand.__doc__ + return + + gpr = self.argv[3:] + while (len(gpr) < 10): + gpr.append('0') + + (rax, rbx, rcx, rdx, rsi, rdi, r8, r9, r10, r11) = tuple([int(x, 16) for x in gpr]) + + self.logger.log( "[CHIPSEC] > hypercall" ) + self.logger.log( "[CHIPSEC] RAX: 0x%016x" % rax ) + self.logger.log( "[CHIPSEC] RBX: 0x%016x" % rbx ) + self.logger.log( "[CHIPSEC] RCX: 0x%016x" % rcx ) + self.logger.log( "[CHIPSEC] RDX: 0x%016x" % rdx ) + self.logger.log( "[CHIPSEC] RSI: 0x%016x" % rsi ) + self.logger.log( "[CHIPSEC] RDI: 0x%016x" % rdi ) + self.logger.log( "[CHIPSEC] R8 : 0x%016x" % r8 ) + self.logger.log( "[CHIPSEC] R9 : 0x%016x" % r9 ) + self.logger.log( "[CHIPSEC] R10: 0x%016x" % r10 ) + self.logger.log( "[CHIPSEC] R11: 0x%016x" % r11 ) + + rax = vmm.hypercall( rax, rbx, rcx, rdx, rsi, rdi, r8, r9, r10, r11 ) + + self.logger.log( "[CHIPSEC] < RAX: 0x%016x" % rax ) + + elif op in ['pt','ept']: + + if len(self.argv) == 4: + eptp = int(self.argv[3],16) + pt_fname = 'ept_%08X' % eptp + self.logger.log( "[CHIPSEC] EPT physical base: 0x%016X" % eptp ) + self.logger.log( "[CHIPSEC] dumping EPT to '%s'..." % pt_fname ) + vmm.dump_SLAT_page_tables( eptp, pt_fname ) + else: + self.logger.log( "[CHIPSEC] finding EPT hierarchy in memory is not implemented yet" ) + print VMMCommand.__doc__ + return + + else: + self.logger.log( "Unknown command: %s" % op ) + print VMMCommand.__doc__ + return + + self.logger.log( "[CHIPSEC] (vmm) time elapsed %.3f" % (time.time()-t) ) + + +commands = { 'vmm': VMMCommand } +