193 lines
7.0 KiB
Python
193 lines
7.0 KiB
Python
# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
"""A module containing TPM handler class used by SAFT."""
|
|
|
|
FW_NV_ADDRESS = 0x1007
|
|
KERNEL_NV_ADDRESS = 0x1008
|
|
|
|
|
|
class TpmError(Exception):
|
|
"""An object to represent TPM errors"""
|
|
pass
|
|
|
|
|
|
class TpmNvRam(object):
|
|
"""An object representing TPM NvRam.
|
|
|
|
@ivar addr: a number, NvRAm address in TPM.
|
|
@ivar size: a number, count of bites in this NvRam section.
|
|
@ivar os_if: an instance of the OS interface (os_interface or a mock object)
|
|
@ivar version_offset: - a number, offset into the NvRam contents where the
|
|
the versions are stored. The total version field size is 4 bytes, the
|
|
first two bytes are the body version, the second two bytes are the key
|
|
version. Numbers are stored in little endian format.
|
|
@ivar pattern: optional, a tuple of two elements, the first element is the
|
|
offset of the pattern expected to be present in the NvRam, and the
|
|
second element is an array of bytes the pattern must match.
|
|
@ivar contents: an array of bytes, the contents of the NvRam.
|
|
@type os_if: autotest_lib.client.cros.faft.utils.os_interface.OSInterface
|
|
"""
|
|
|
|
def __init__(self, os_if, addr, size, version_offset, data_pattern=None):
|
|
self.os_if = os_if
|
|
self.addr = addr
|
|
self.size = size
|
|
self.version_offset = version_offset
|
|
self.pattern = data_pattern
|
|
self.contents = []
|
|
|
|
def init(self):
|
|
"""Initialize the object, loading tpm nvram information from the device.
|
|
"""
|
|
cmd = 'tpmc read 0x%x 0x%x' % (self.addr, self.size)
|
|
output = self.os_if.run_shell_command_get_output(cmd)
|
|
if not output:
|
|
raise TpmError('Failed to read Nvram')
|
|
nvram_data = output[0].split()
|
|
self.contents = [int(x, 16) for x in nvram_data]
|
|
if self.pattern:
|
|
pattern_offset = self.pattern[0]
|
|
pattern_data = self.pattern[1]
|
|
contents_pattern = self.contents[pattern_offset:pattern_offset +
|
|
len(pattern_data)]
|
|
if contents_pattern != pattern_data:
|
|
raise TpmError('Nvram pattern does not match')
|
|
|
|
def get_body_version(self):
|
|
return self.contents[self.version_offset + 1] * 256 + self.contents[
|
|
self.version_offset]
|
|
|
|
def get_key_version(self):
|
|
return self.contents[self.version_offset + 3] * 256 + self.contents[
|
|
self.version_offset + 2]
|
|
|
|
|
|
class TpmNvRamOptions(object):
|
|
"""An object representing a number of potential TPM NvRam objects.
|
|
|
|
@ivar nvrams: A list of TpmNvRam objects. The first TpmNvRam that
|
|
successfully initializes is selected and used so more specific
|
|
selectors should be listed earlier.
|
|
"""
|
|
|
|
def __init__(self, nvrams):
|
|
self._nvrams = nvrams
|
|
self._valid_nvram = None
|
|
|
|
def init(self):
|
|
"""Selects a valid TpmNvRam and initializes it."""
|
|
for nvram in self._nvrams:
|
|
try:
|
|
nvram.init()
|
|
except TpmError:
|
|
continue
|
|
else:
|
|
self._valid_nvram = nvram
|
|
break
|
|
else:
|
|
raise TpmError('No Nvram pattern matched')
|
|
|
|
def get_body_version(self):
|
|
return self._valid_nvram.get_body_version()
|
|
|
|
def get_key_version(self):
|
|
return self._valid_nvram.get_key_version()
|
|
|
|
|
|
class TpmHandler(object):
|
|
"""An object to control TPM device's NVRAM.
|
|
|
|
@ivar os_if: an instance of the OS interface (os_interface or a mock object)
|
|
@ivar nvrams: A dictionary where the keys are nvram names, and the values
|
|
are instances of TpmNvRam objects, providing access to the
|
|
appropriate TPM NvRam sections.
|
|
@ivar tpm_version: Either "1.2" or "2.0".
|
|
@type os_if: autotest_lib.client.cros.faft.utils.os_interface.OSInterface
|
|
"""
|
|
|
|
def __init__(self, os_if):
|
|
self.os_if = os_if
|
|
self.nvrams = {
|
|
'kernel':
|
|
TpmNvRamOptions([
|
|
TpmNvRam(
|
|
self.os_if,
|
|
addr=KERNEL_NV_ADDRESS,
|
|
size=13,
|
|
version_offset=5,
|
|
data_pattern=(1, [0x4c, 0x57, 0x52, 0x47])),
|
|
TpmNvRam(
|
|
self.os_if,
|
|
addr=KERNEL_NV_ADDRESS,
|
|
size=0x28,
|
|
version_offset=4,
|
|
data_pattern=(0, [0x10, 0x28])),
|
|
]),
|
|
'bios':
|
|
TpmNvRam(
|
|
self.os_if,
|
|
addr=FW_NV_ADDRESS,
|
|
size=10,
|
|
version_offset=2)
|
|
}
|
|
self.tpm_version = None
|
|
self.trunksd_started = False
|
|
self.tcsd_started = False
|
|
self.initialized = False
|
|
|
|
def init(self):
|
|
"""Initialize the values of the nvram sections.
|
|
|
|
This is separate from object creation so it can be done only when a test
|
|
actually uses it.
|
|
"""
|
|
self.stop_daemon()
|
|
for nvram in self.nvrams.itervalues():
|
|
nvram.init()
|
|
self.restart_daemon()
|
|
self.initialized = True
|
|
|
|
def get_fw_version(self):
|
|
return self.nvrams['bios'].get_body_version()
|
|
|
|
def get_fw_key_version(self):
|
|
return self.nvrams['bios'].get_key_version()
|
|
|
|
def get_kernel_version(self):
|
|
return self.nvrams['kernel'].get_body_version()
|
|
|
|
def get_kernel_key_version(self):
|
|
return self.nvrams['kernel'].get_key_version()
|
|
|
|
def get_tpm_version(self):
|
|
if self.tpm_version is None:
|
|
self.tpm_version = self.os_if.run_shell_command_get_output(
|
|
'tpmc tpmver')[0]
|
|
return self.tpm_version
|
|
|
|
def stop_daemon(self):
|
|
"""Stop TPM related daemon."""
|
|
if self.trunksd_started or self.tcsd_started:
|
|
raise TpmError('Called stop_daemon() before')
|
|
|
|
cmd = 'initctl status tcsd || initctl status trunksd'
|
|
status = self.os_if.run_shell_command_get_output(cmd) or ['']
|
|
# Expected status is like ['trunksd start/running, process 2375']
|
|
self.trunksd_started = status[0].startswith('trunksd start/running')
|
|
if self.trunksd_started:
|
|
self.os_if.run_shell_command('stop trunksd')
|
|
else:
|
|
self.tcsd_started = status[0].startswith('tcsd start/running')
|
|
if self.tcsd_started:
|
|
self.os_if.run_shell_command('stop tcsd')
|
|
|
|
def restart_daemon(self):
|
|
"""Restart TPM related daemon which was stopped by stop_daemon()."""
|
|
if self.trunksd_started:
|
|
self.os_if.run_shell_command('start trunksd')
|
|
self.trunksd_started = False
|
|
elif self.tcsd_started:
|
|
self.os_if.run_shell_command('start tcsd')
|
|
self.tcsd_started = False
|