blob: 444eac39b769a5ff07c421d236a24840206e060c [file] [log] [blame]
Stefan Bergerdfbc8852015-03-23 14:22:15 -04001// Implementation of a TPM driver for the TPM TIS interface
2//
3// Copyright (C) 2006-2011 IBM Corporation
4//
5// Authors:
6// Stefan Berger <stefanb@linux.vnet.ibm.com>
7//
8// This file may be distributed under the terms of the GNU LGPLv3 license.
9
10#include "config.h" // CONFIG_TPM_TIS_SHA1THRESHOLD
11#include "string.h" // memcpy
12#include "util.h" // msleep
13#include "x86.h" // readl
14#include "hw/tpm_drivers.h" // struct tpm_driver
15#include "tcgbios.h" // TCG_*
16
17static const u32 tis_default_timeouts[4] = {
18 TIS_DEFAULT_TIMEOUT_A,
19 TIS_DEFAULT_TIMEOUT_B,
20 TIS_DEFAULT_TIMEOUT_C,
21 TIS_DEFAULT_TIMEOUT_D,
22};
23
24static const u32 tpm_default_durations[3] = {
25 TPM_DEFAULT_DURATION_SHORT,
26 TPM_DEFAULT_DURATION_MEDIUM,
27 TPM_DEFAULT_DURATION_LONG,
28};
29
30/* determined values */
31static u32 tpm_default_dur[3];
32static u32 tpm_default_to[4];
33
34
35/* if device is not there, return '0', '1' otherwise */
36static u32 tis_probe(void)
37{
Stefan Bergerb310dfa2015-03-23 14:22:16 -040038 if (!CONFIG_TCGBIOS)
39 return 0;
40
Stefan Bergerdfbc8852015-03-23 14:22:15 -040041 u32 rc = 0;
42 u32 didvid = readl(TIS_REG(0, TIS_REG_DID_VID));
43
44 if ((didvid != 0) && (didvid != 0xffffffff))
45 rc = 1;
46
47 return rc;
48}
49
50static u32 tis_init(void)
51{
Stefan Bergerb310dfa2015-03-23 14:22:16 -040052 if (!CONFIG_TCGBIOS)
53 return 1;
54
Stefan Bergerdfbc8852015-03-23 14:22:15 -040055 writeb(TIS_REG(0, TIS_REG_INT_ENABLE), 0);
56
57 if (tpm_drivers[TIS_DRIVER_IDX].durations == NULL) {
58 u32 *durations = tpm_default_dur;
59 memcpy(durations, tpm_default_durations,
60 sizeof(tpm_default_durations));
61 tpm_drivers[TIS_DRIVER_IDX].durations = durations;
62 }
63
64 if (tpm_drivers[TIS_DRIVER_IDX].timeouts == NULL) {
65 u32 *timeouts = tpm_default_to;
66 memcpy(timeouts, tis_default_timeouts,
67 sizeof(tis_default_timeouts));
68 tpm_drivers[TIS_DRIVER_IDX].timeouts = timeouts;
69 }
70
71 return 1;
72}
73
74
75static void set_timeouts(u32 timeouts[4], u32 durations[3])
76{
Stefan Bergerb310dfa2015-03-23 14:22:16 -040077 if (!CONFIG_TCGBIOS)
78 return;
79
Stefan Bergerdfbc8852015-03-23 14:22:15 -040080 u32 *tos = tpm_drivers[TIS_DRIVER_IDX].timeouts;
81 u32 *dus = tpm_drivers[TIS_DRIVER_IDX].durations;
82
83 if (tos && tos != tis_default_timeouts && timeouts)
84 memcpy(tos, timeouts, 4 * sizeof(u32));
85 if (dus && dus != tpm_default_durations && durations)
86 memcpy(dus, durations, 3 * sizeof(u32));
87}
88
89
90static u32 tis_wait_sts(u8 locty, u32 time, u8 mask, u8 expect)
91{
Stefan Bergerb310dfa2015-03-23 14:22:16 -040092 if (!CONFIG_TCGBIOS)
93 return 0;
94
Stefan Bergerdfbc8852015-03-23 14:22:15 -040095 u32 rc = 1;
96
97 while (time > 0) {
98 u8 sts = readb(TIS_REG(locty, TIS_REG_STS));
99 if ((sts & mask) == expect) {
100 rc = 0;
101 break;
102 }
103 msleep(1);
104 time--;
105 }
106 return rc;
107}
108
109static u32 tis_activate(u8 locty)
110{
Stefan Bergerb310dfa2015-03-23 14:22:16 -0400111 if (!CONFIG_TCGBIOS)
112 return 0;
113
Stefan Bergerdfbc8852015-03-23 14:22:15 -0400114 u32 rc = 0;
115 u8 acc;
116 int l;
117 u32 timeout_a = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_A];
118
119 if (!(readb(TIS_REG(locty, TIS_REG_ACCESS)) &
120 TIS_ACCESS_ACTIVE_LOCALITY)) {
121 /* release locality in use top-downwards */
122 for (l = 4; l >= 0; l--)
123 writeb(TIS_REG(l, TIS_REG_ACCESS),
124 TIS_ACCESS_ACTIVE_LOCALITY);
125 }
126
127 /* request access to locality */
128 writeb(TIS_REG(locty, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE);
129
130 acc = readb(TIS_REG(locty, TIS_REG_ACCESS));
131 if ((acc & TIS_ACCESS_ACTIVE_LOCALITY)) {
132 writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY);
133 rc = tis_wait_sts(locty, timeout_a,
134 TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
135 }
136
137 return rc;
138}
139
140static u32 tis_find_active_locality(void)
141{
Stefan Bergerb310dfa2015-03-23 14:22:16 -0400142 if (!CONFIG_TCGBIOS)
143 return 0;
144
Stefan Bergerdfbc8852015-03-23 14:22:15 -0400145 u8 locty;
146
147 for (locty = 0; locty <= 4; locty++) {
148 if ((readb(TIS_REG(locty, TIS_REG_ACCESS)) &
149 TIS_ACCESS_ACTIVE_LOCALITY))
150 return locty;
151 }
152
153 tis_activate(0);
154
155 return 0;
156}
157
158static u32 tis_ready(void)
159{
Stefan Bergerb310dfa2015-03-23 14:22:16 -0400160 if (!CONFIG_TCGBIOS)
161 return 0;
162
Stefan Bergerdfbc8852015-03-23 14:22:15 -0400163 u32 rc = 0;
164 u8 locty = tis_find_active_locality();
165 u32 timeout_b = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_B];
166
167 writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY);
168 rc = tis_wait_sts(locty, timeout_b,
169 TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
170
171 return rc;
172}
173
174static u32 tis_senddata(const u8 *const data, u32 len)
175{
Stefan Bergerb310dfa2015-03-23 14:22:16 -0400176 if (!CONFIG_TCGBIOS)
177 return 0;
178
Stefan Bergerdfbc8852015-03-23 14:22:15 -0400179 u32 rc = 0;
180 u32 offset = 0;
181 u32 end = 0;
182 u16 burst = 0;
183 u32 ctr = 0;
184 u8 locty = tis_find_active_locality();
185 u32 timeout_d = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_D];
186
187 do {
188 while (burst == 0 && ctr < timeout_d) {
189 burst = readl(TIS_REG(locty, TIS_REG_STS)) >> 8;
190 if (burst == 0) {
191 msleep(1);
192 ctr++;
193 }
194 }
195
196 if (burst == 0) {
197 rc = TCG_RESPONSE_TIMEOUT;
198 break;
199 }
200
201 while (1) {
202 writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), data[offset++]);
203 burst--;
204
205 if (burst == 0 || offset == len)
206 break;
207 }
208
209 if (offset == len)
210 end = 1;
211 } while (end == 0);
212
213 return rc;
214}
215
216static u32 tis_readresp(u8 *buffer, u32 *len)
217{
Stefan Bergerb310dfa2015-03-23 14:22:16 -0400218 if (!CONFIG_TCGBIOS)
219 return 0;
220
Stefan Bergerdfbc8852015-03-23 14:22:15 -0400221 u32 rc = 0;
222 u32 offset = 0;
223 u32 sts;
224 u8 locty = tis_find_active_locality();
225
226 while (offset < *len) {
227 buffer[offset] = readb(TIS_REG(locty, TIS_REG_DATA_FIFO));
228 offset++;
229 sts = readb(TIS_REG(locty, TIS_REG_STS));
230 /* data left ? */
231 if ((sts & TIS_STS_DATA_AVAILABLE) == 0)
232 break;
233 }
234
235 *len = offset;
236
237 return rc;
238}
239
240
241static u32 tis_waitdatavalid(void)
242{
Stefan Bergerb310dfa2015-03-23 14:22:16 -0400243 if (!CONFIG_TCGBIOS)
244 return 0;
245
Stefan Bergerdfbc8852015-03-23 14:22:15 -0400246 u32 rc = 0;
247 u8 locty = tis_find_active_locality();
248 u32 timeout_c = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_C];
249
250 if (tis_wait_sts(locty, timeout_c, TIS_STS_VALID, TIS_STS_VALID) != 0)
251 rc = TCG_NO_RESPONSE;
252
253 return rc;
254}
255
256static u32 tis_waitrespready(enum tpmDurationType to_t)
257{
Stefan Bergerb310dfa2015-03-23 14:22:16 -0400258 if (!CONFIG_TCGBIOS)
259 return 0;
260
Stefan Bergerdfbc8852015-03-23 14:22:15 -0400261 u32 rc = 0;
262 u8 locty = tis_find_active_locality();
263 u32 timeout = tpm_drivers[TIS_DRIVER_IDX].durations[to_t];
264
265 writeb(TIS_REG(locty ,TIS_REG_STS), TIS_STS_TPM_GO);
266
267 if (tis_wait_sts(locty, timeout,
268 TIS_STS_DATA_AVAILABLE, TIS_STS_DATA_AVAILABLE) != 0)
269 rc = TCG_NO_RESPONSE;
270
271 return rc;
272}
273
274
275struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = {
276 [TIS_DRIVER_IDX] =
277 {
278 .timeouts = NULL,
279 .durations = NULL,
280 .set_timeouts = set_timeouts,
281 .probe = tis_probe,
282 .init = tis_init,
283 .activate = tis_activate,
284 .ready = tis_ready,
285 .senddata = tis_senddata,
286 .readresp = tis_readresp,
287 .waitdatavalid = tis_waitdatavalid,
288 .waitrespready = tis_waitrespready,
289 .sha1threshold = 100 * 1024,
290 },
291};