1# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4"""A module containing TPM handler class used by SAFT.""" 5 6FW_NV_ADDRESS = 0x1007 7KERNEL_NV_ADDRESS = 0x1008 8 9 10class TpmError(Exception): 11 """An object to represent TPM errors""" 12 pass 13 14 15class TpmNvRam(object): 16 """An object representing TPM NvRam. 17 18 @ivar addr: a number, NvRAm address in TPM. 19 @ivar size: a number, count of bites in this NvRam section. 20 @ivar os_if: an instance of the OS interface (os_interface or a mock object) 21 @ivar version_offset: - a number, offset into the NvRam contents where the 22 the versions are stored. The total version field size is 4 bytes, the 23 first two bytes are the body version, the second two bytes are the key 24 version. Numbers are stored in little endian format. 25 @ivar pattern: optional, a tuple of two elements, the first element is the 26 offset of the pattern expected to be present in the NvRam, and the 27 second element is an array of bytes the pattern must match. 28 @ivar contents: an array of bytes, the contents of the NvRam. 29 @type os_if: autotest_lib.client.cros.faft.utils.os_interface.OSInterface 30 """ 31 32 def __init__(self, os_if, addr, size, version_offset, data_pattern=None): 33 self.os_if = os_if 34 self.addr = addr 35 self.size = size 36 self.version_offset = version_offset 37 self.pattern = data_pattern 38 self.contents = [] 39 40 def init(self): 41 """Initialize the object, loading tpm nvram information from the device. 42 """ 43 cmd = 'tpmc read 0x%x 0x%x' % (self.addr, self.size) 44 nvram_data = self.os_if.run_shell_command_get_output(cmd)[0].split() 45 self.contents = [int(x, 16) for x in nvram_data] 46 if self.pattern: 47 pattern_offset = self.pattern[0] 48 pattern_data = self.pattern[1] 49 contents_pattern = self.contents[pattern_offset:pattern_offset + 50 len(pattern_data)] 51 if contents_pattern != pattern_data: 52 raise TpmError('Nvram pattern does not match') 53 54 def get_body_version(self): 55 return self.contents[self.version_offset + 1] * 256 + self.contents[ 56 self.version_offset] 57 58 def get_key_version(self): 59 return self.contents[self.version_offset + 3] * 256 + self.contents[ 60 self.version_offset + 2] 61 62 63class TpmHandler(object): 64 """An object to control TPM device's NVRAM. 65 66 @ivar os_if: an instance of the OS interface (os_interface or a mock object) 67 @ivar nvrams: A dictionary where the keys are nvram names, and the values 68 are instances of TpmNvRam objects, providing access to the 69 appropriate TPM NvRam sections. 70 @ivar tpm_version: Either "1.2" or "2.0". 71 @type os_if: autotest_lib.client.cros.faft.utils.os_interface.OSInterface 72 """ 73 74 def __init__(self, os_if): 75 self.os_if = os_if 76 self.nvrams = { 77 'kernel': 78 TpmNvRam( 79 self.os_if, 80 addr=KERNEL_NV_ADDRESS, 81 size=13, 82 version_offset=5, 83 data_pattern=(1, [0x4c, 0x57, 0x52, 0x47])), 84 'bios': 85 TpmNvRam( 86 self.os_if, 87 addr=FW_NV_ADDRESS, 88 size=10, 89 version_offset=2) 90 } 91 self.tpm_version = None 92 self.trunksd_started = False 93 self.tcsd_started = False 94 self.initialized = False 95 96 def init(self): 97 """Initialize the values of the nvram sections. 98 99 This is separate from object creation so it can be done only when a test 100 actually uses it. 101 """ 102 self.stop_daemon() 103 for nvram in self.nvrams.itervalues(): 104 nvram.init() 105 self.restart_daemon() 106 self.initialized = True 107 108 def get_fw_version(self): 109 return self.nvrams['bios'].get_body_version() 110 111 def get_fw_key_version(self): 112 return self.nvrams['bios'].get_key_version() 113 114 def get_kernel_version(self): 115 return self.nvrams['kernel'].get_body_version() 116 117 def get_kernel_key_version(self): 118 return self.nvrams['kernel'].get_key_version() 119 120 def get_tpm_version(self): 121 if self.tpm_version is None: 122 self.tpm_version = self.os_if.run_shell_command_get_output( 123 'tpmc tpmver')[0] 124 return self.tpm_version 125 126 def stop_daemon(self): 127 """Stop TPM related daemon.""" 128 if self.trunksd_started or self.tcsd_started: 129 raise TpmError('Called stop_daemon() before') 130 131 cmd = 'initctl status tcsd || initctl status trunksd' 132 status = self.os_if.run_shell_command_get_output(cmd) or [''] 133 # Expected status is like ['trunksd start/running, process 2375'] 134 self.trunksd_started = status[0].startswith('trunksd start/running') 135 if self.trunksd_started: 136 self.os_if.run_shell_command('stop trunksd') 137 else: 138 self.tcsd_started = status[0].startswith('tcsd start/running') 139 if self.tcsd_started: 140 self.os_if.run_shell_command('stop tcsd') 141 142 def restart_daemon(self): 143 """Restart TPM related daemon which was stopped by stop_daemon().""" 144 if self.trunksd_started: 145 self.os_if.run_shell_command('start trunksd') 146 self.trunksd_started = False 147 elif self.tcsd_started: 148 self.os_if.run_shell_command('start tcsd') 149 self.tcsd_started = False 150