• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.itervalues():
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