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 output = self.os_if.run_shell_command_get_output(cmd) 45 if not output: 46 raise TpmError('Failed to read Nvram') 47 nvram_data = output[0].split() 48 self.contents = [int(x, 16) for x in nvram_data] 49 if self.pattern: 50 pattern_offset = self.pattern[0] 51 pattern_data = self.pattern[1] 52 contents_pattern = self.contents[pattern_offset:pattern_offset + 53 len(pattern_data)] 54 if contents_pattern != pattern_data: 55 raise TpmError('Nvram pattern does not match') 56 57 def get_body_version(self): 58 return self.contents[self.version_offset + 1] * 256 + self.contents[ 59 self.version_offset] 60 61 def get_key_version(self): 62 return self.contents[self.version_offset + 3] * 256 + self.contents[ 63 self.version_offset + 2] 64 65 66class TpmNvRamOptions(object): 67 """An object representing a number of potential TPM NvRam objects. 68 69 @ivar nvrams: A list of TpmNvRam objects. The first TpmNvRam that 70 successfully initializes is selected and used so more specific 71 selectors should be listed earlier. 72 """ 73 74 def __init__(self, nvrams): 75 self._nvrams = nvrams 76 self._valid_nvram = None 77 78 def init(self): 79 """Selects a valid TpmNvRam and initializes it.""" 80 for nvram in self._nvrams: 81 try: 82 nvram.init() 83 except TpmError: 84 continue 85 else: 86 self._valid_nvram = nvram 87 break 88 else: 89 raise TpmError('No Nvram pattern matched') 90 91 def get_body_version(self): 92 return self._valid_nvram.get_body_version() 93 94 def get_key_version(self): 95 return self._valid_nvram.get_key_version() 96 97 98class TpmHandler(object): 99 """An object to control TPM device's NVRAM. 100 101 @ivar os_if: an instance of the OS interface (os_interface or a mock object) 102 @ivar nvrams: A dictionary where the keys are nvram names, and the values 103 are instances of TpmNvRam objects, providing access to the 104 appropriate TPM NvRam sections. 105 @ivar tpm_version: Either "1.2" or "2.0". 106 @type os_if: autotest_lib.client.cros.faft.utils.os_interface.OSInterface 107 """ 108 109 def __init__(self, os_if): 110 self.os_if = os_if 111 self.nvrams = { 112 'kernel': 113 TpmNvRamOptions([ 114 TpmNvRam( 115 self.os_if, 116 addr=KERNEL_NV_ADDRESS, 117 size=13, 118 version_offset=5, 119 data_pattern=(1, [0x4c, 0x57, 0x52, 0x47])), 120 TpmNvRam( 121 self.os_if, 122 addr=KERNEL_NV_ADDRESS, 123 size=0x28, 124 version_offset=4, 125 data_pattern=(0, [0x10, 0x28])), 126 ]), 127 'bios': 128 TpmNvRam( 129 self.os_if, 130 addr=FW_NV_ADDRESS, 131 size=10, 132 version_offset=2) 133 } 134 self.tpm_version = None 135 self.trunksd_started = False 136 self.tcsd_started = False 137 self.initialized = False 138 139 def init(self): 140 """Initialize the values of the nvram sections. 141 142 This is separate from object creation so it can be done only when a test 143 actually uses it. 144 """ 145 self.stop_daemon() 146 for nvram in self.nvrams.values(): 147 nvram.init() 148 self.restart_daemon() 149 self.initialized = True 150 151 def get_fw_version(self): 152 return self.nvrams['bios'].get_body_version() 153 154 def get_fw_key_version(self): 155 return self.nvrams['bios'].get_key_version() 156 157 def get_kernel_version(self): 158 return self.nvrams['kernel'].get_body_version() 159 160 def get_kernel_key_version(self): 161 return self.nvrams['kernel'].get_key_version() 162 163 def get_tpm_version(self): 164 if self.tpm_version is None: 165 self.tpm_version = self.os_if.run_shell_command_get_output( 166 'tpmc tpmver')[0] 167 return self.tpm_version 168 169 def stop_daemon(self): 170 """Stop TPM related daemon.""" 171 if self.trunksd_started or self.tcsd_started: 172 raise TpmError('Called stop_daemon() before') 173 174 cmd = 'initctl status tcsd || initctl status trunksd' 175 status = self.os_if.run_shell_command_get_output(cmd) or [''] 176 # Expected status is like ['trunksd start/running, process 2375'] 177 self.trunksd_started = status[0].startswith('trunksd start/running') 178 if self.trunksd_started: 179 self.os_if.run_shell_command('stop trunksd') 180 else: 181 self.tcsd_started = status[0].startswith('tcsd start/running') 182 if self.tcsd_started: 183 self.os_if.run_shell_command('stop tcsd') 184 185 def restart_daemon(self): 186 """Restart TPM related daemon which was stopped by stop_daemon().""" 187 if self.trunksd_started: 188 self.os_if.run_shell_command('start trunksd') 189 self.trunksd_started = False 190 elif self.tcsd_started: 191 self.os_if.run_shell_command('start tcsd') 192 self.tcsd_started = False 193