1 // Copyright 2016 The Fuchsia 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
5 #include <ddk/debug.h>
6 #include <stdbool.h>
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <zircon/syscalls.h>
11 #include <lib/zx/time.h>
12 #include "tpm.h"
13
14 // TPM_ACCESS bitmasks
15 #define TPM_ACCESS_REG_VALID 0x80
16 #define TPM_ACCESS_ACTIVE_LOCALITY 0x20
17 #define TPM_ACCESS_BEEN_SEIZED 0x10
18 #define TPM_ACCESS_SEIZE 0x08
19 #define TPM_ACCESS_PENDING_REQ 0x04
20 #define TPM_ACCESS_REQUEST_USE 0x02
21 #define TPM_ACCESS_ESTABLISHMENT 0x01
22
23 // TPM_INTF_CAP bitmasks
24 #define TPM_INTF_CAP_IFACE_VER_MASK 0x70000000
25 #define TPM_INTF_CAP_IFACE_VER_1_3 0x20000000
26 #define TPM_INTF_CAP_IFACE_VER_1_2 0x00000000
27
28 // TPM_STS bitmasks
29 #define TPM_STS_FAMILY 0x0c000000
30 #define TPM_STS_RESET_ESTABLISHMENT 0x02000000
31 #define TPM_STS_CMD_CANCEL 0x01000000
32 #define TPM_STS_BURST_COUNT 0x00ffff00
33 #define TPM_STS_VALID 0x00000080
34 #define TPM_STS_CMD_RDY 0x00000040
35 #define TPM_STS_TPM_GO 0x00000020
36 #define TPM_STS_DATA_AVAIL 0x00000010
37 #define TPM_STS_EXPECT 0x00000008
38 #define TPM_STS_SELF_TEST_DONE 0x00000004
39 #define TPM_STS_RESPONSE_RETRY 0x00000002
40 #define TPM_STS_EXTRACT_BURST_COUNT(sts) (((sts) & TPM_STS_BURST_COUNT) >> 8)
41 #define TPM_STS_EXTRACT_FAMILY(sts) (((sts) & TPM_STS_FAMILY) >> 26)
42
43 // TPM_INT_ENABLE bitmasks
44 #define TPM_INT_ENABLE_GLOBAL_ENABLE 0x80000000
45 #define TPM_INT_ENABLE_HIGH_LEVEL (0 << 3)
46 #define TPM_INT_ENABLE_LOW_LEVEL (1 << 3)
47 #define TPM_INT_ENABLE_RISING_EDGE (2 << 3)
48 #define TPM_INT_ENABLE_FALLING_EDGE (3 << 3)
49
50 // TPM_INTERFACE_ID bitmasks
51 #define TPM_INTERFACE_ID_TYPE_MASK 0xf
52 #define TPM_INTERFACE_ID_TYPE_FIFO_2_0 0x0
53 #define TPM_INTERFACE_ID_TYPE_CRB 0x1
54 #define TPM_INTERFACE_ID_TYPE_FIFO_1_3 0xf
55
56 namespace {
57
58 constexpr zx::duration TIMEOUT_A = zx::msec(750);
59
60 constexpr zx::duration kWaitForProgressDelay = zx::msec(2);
61
62 } // namespace
63
64 namespace tpm {
65
66 constexpr size_t kNumRegisterTries = 3;
67
GetAccessField(HardwareInterface * iface,Locality loc,uint8_t * val)68 static zx_status_t GetAccessField(HardwareInterface* iface, Locality loc,
69 uint8_t* val) {
70 for (size_t attempt = 0; attempt < kNumRegisterTries; ++attempt) {
71 if (attempt) {
72 zx::nanosleep(zx::deadline_after(TIMEOUT_A));
73 }
74
75 uint8_t access;
76 zx_status_t status = iface->ReadAccess(loc, &access);
77 if (status != ZX_OK) {
78 return status;
79 }
80 if (access & TPM_ACCESS_REG_VALID) {
81 *val = access;
82 return ZX_OK;
83 }
84 }
85
86 return ZX_ERR_TIMED_OUT;
87 }
88
RequestLocalityLocked(Locality loc)89 zx_status_t Device::RequestLocalityLocked(Locality loc) {
90 uint8_t val;
91 zx_status_t status = GetAccessField(iface_.get(), loc, &val);
92 if (status != ZX_OK) {
93 return status;
94 }
95 if (!(val & TPM_ACCESS_REG_VALID)) {
96 return ZX_ERR_BAD_STATE;
97 }
98
99 if (val & TPM_ACCESS_REQUEST_USE) {
100 return ZX_ERR_UNAVAILABLE;
101 }
102
103 if (val & TPM_ACCESS_ACTIVE_LOCALITY) {
104 // We're already the active locality
105 return ZX_ERR_BAD_STATE;
106 }
107
108 return iface_->WriteAccess(loc, TPM_ACCESS_REQUEST_USE);
109 }
110
ReleaseLocalityLocked(Locality loc)111 zx_status_t Device::ReleaseLocalityLocked(Locality loc) {
112 uint8_t val;
113 zx_status_t status = GetAccessField(iface_.get(), loc, &val);
114 if (status != ZX_OK) {
115 return status;
116 }
117 if (!(val & TPM_ACCESS_REG_VALID)) {
118 return ZX_ERR_BAD_STATE;
119 }
120
121 if (val & TPM_ACCESS_REQUEST_USE) {
122 return ZX_ERR_BAD_STATE;
123 }
124
125 if (!(val & TPM_ACCESS_ACTIVE_LOCALITY)) {
126 // We're not the active locality
127 return ZX_ERR_BAD_STATE;
128 }
129
130 // Writing this bit triggers the release.
131 return iface_->WriteAccess(loc, TPM_ACCESS_ACTIVE_LOCALITY);
132 }
133
WaitForLocalityLocked(Locality loc)134 zx_status_t Device::WaitForLocalityLocked(Locality loc) {
135 uint8_t val;
136 zx_status_t status = GetAccessField(iface_.get(), loc, &val);
137 if (status != ZX_OK) {
138 return status;
139 }
140 if (val & TPM_ACCESS_ACTIVE_LOCALITY) {
141 return ZX_OK;
142 }
143 if (!(val & TPM_ACCESS_REQUEST_USE)) {
144 return ZX_ERR_BAD_STATE;
145 }
146 // We assume we're the only one using the TPM, so we need to wait at most
147 // TIMEOUT_A
148 zx::nanosleep(zx::deadline_after(TIMEOUT_A));
149
150 status = GetAccessField(iface_.get(), loc, &val);
151 if (status != ZX_OK) {
152 return status;
153 }
154 if (val & TPM_ACCESS_ACTIVE_LOCALITY) {
155 return ZX_OK;
156 }
157 if (val & TPM_ACCESS_REQUEST_USE) {
158 return ZX_ERR_TIMED_OUT;
159 }
160 return ZX_ERR_BAD_STATE;
161 }
162
GetStatusField(HardwareInterface * iface,Locality loc,uint32_t * val)163 static zx_status_t GetStatusField(HardwareInterface* iface, Locality loc,
164 uint32_t* val) {
165 for (size_t attempt = 0; attempt < kNumRegisterTries; ++attempt) {
166 if (attempt) {
167 zx::nanosleep(zx::deadline_after(TIMEOUT_A));
168 }
169
170 uint32_t sts;
171 zx_status_t status = iface->ReadStatus(loc, &sts);
172 if (status != ZX_OK) {
173 return status;
174 }
175 if (sts & TPM_STS_VALID) {
176 *val = sts;
177 return ZX_OK;
178 }
179 }
180
181 return ZX_ERR_TIMED_OUT;
182 }
183
GetBurstCount(HardwareInterface * iface,Locality loc,uint16_t * val)184 static zx_status_t GetBurstCount(HardwareInterface* iface, Locality loc,
185 uint16_t* val) {
186 for (size_t attempt = 0; attempt < kNumRegisterTries; ++attempt) {
187 if (attempt) {
188 zx::nanosleep(zx::deadline_after(TIMEOUT_A));
189 }
190
191 uint32_t sts;
192 zx_status_t status = iface->ReadStatus(loc, &sts);
193 if (status != ZX_OK) {
194 return status;
195 }
196 uint16_t burst = TPM_STS_EXTRACT_BURST_COUNT(sts);
197 if (burst > 0) {
198 *val = burst;
199 return ZX_OK;
200 }
201 }
202
203 return ZX_ERR_TIMED_OUT;
204 }
205
206 // Returns the true/false value of the the STS.EXPECT bit, or < 0 on error
GetStatusExpect(HardwareInterface * iface,Locality loc,bool * expect)207 static zx_status_t GetStatusExpect(HardwareInterface* iface, Locality loc,
208 bool* expect) {
209 uint32_t status_field;
210 zx_status_t status = GetStatusField(iface, loc, &status_field);
211 if (status != ZX_OK) {
212 return status;
213 }
214 *expect = !!(status_field & TPM_STS_EXPECT);
215 return ZX_OK;
216 }
217
218 // Returns the true/false value of the the STS.DATA_AVAIL bit, or < 0 on error
GetStatusDataAvail(HardwareInterface * iface,Locality loc,bool * data_avail)219 static zx_status_t GetStatusDataAvail(HardwareInterface* iface, Locality loc,
220 bool* data_avail) {
221 uint32_t status_field;
222 zx_status_t status = GetStatusField(iface, loc, &status_field);
223 if (status != ZX_OK) {
224 return status;
225 }
226 *data_avail = !!(status_field & TPM_STS_DATA_AVAIL);
227 return ZX_OK;
228 }
229
WaitForDataAvail(HardwareInterface * iface,Locality loc)230 static zx_status_t WaitForDataAvail(HardwareInterface* iface, Locality loc) {
231 // TODO(teisenbe): Add a timeout to this?
232 while (1) {
233 bool data_avail = false;
234 zx_status_t status = GetStatusDataAvail(iface, loc, &data_avail);
235 if (status != ZX_OK) {
236 return status;
237 }
238 if (data_avail) {
239 return ZX_OK;
240 }
241
242 zx::nanosleep(zx::deadline_after(kWaitForProgressDelay));
243 }
244 }
245
AbortCommand(HardwareInterface * iface,Locality loc)246 static zx_status_t AbortCommand(HardwareInterface* iface, Locality loc) {
247 return iface->WriteStatus(loc, TPM_STS_CMD_RDY);
248 }
249
250 // Returns the true/false value of the the ACCESS.ACTIVE bit, or < 0 on error
GetActiveLocality(HardwareInterface * iface,Locality loc,bool * active)251 static zx_status_t GetActiveLocality(HardwareInterface* iface, Locality loc,
252 bool* active) {
253 uint8_t val;
254 zx_status_t status = GetAccessField(iface, loc, &val);
255 if (status != ZX_OK) {
256 return status;
257 }
258 *active = !!(val & TPM_ACCESS_ACTIVE_LOCALITY);
259 return ZX_OK;
260 }
261
CheckExpectedState(zx_status_t status,bool actual,bool expected)262 static zx_status_t CheckExpectedState(zx_status_t status, bool actual, bool expected) {
263 if (status < 0) {
264 return status;
265 }
266 if (actual != expected) {
267 return ZX_ERR_BAD_STATE;
268 }
269 return ZX_OK;
270 }
271
SendCmdLocked(Locality loc,const uint8_t * cmd,size_t len)272 zx_status_t Device::SendCmdLocked(Locality loc, const uint8_t* cmd, size_t len) {
273 if (len <= 1) {
274 return ZX_ERR_INVALID_ARGS;
275 }
276
277 bool active = false;
278 zx_status_t status = GetActiveLocality(iface_.get(), loc, &active);
279 status = CheckExpectedState(status, active, true);
280 if (status < 0) {
281 return status;
282 }
283
284 // This procedure is described in section 5.5.2.2.1 of the TCG PC Client
285 // Platform TPM profile spec (family 2.0, which also describes 1.2)
286 status = iface_->WriteStatus(loc, TPM_STS_CMD_RDY);
287 if (status != ZX_OK) {
288 return status;
289 }
290
291 size_t bytes_sent = 0;
292
293 // Write the command to the FIFO, while respecting flow control
294 while (bytes_sent < len) {
295 uint16_t burst_count;
296 status = GetBurstCount(iface_.get(), loc, &burst_count);
297 if (status != ZX_OK) {
298 AbortCommand(iface_.get(), loc);
299 return status;
300 }
301 if (burst_count == 0) {
302 AbortCommand(iface_.get(), loc);
303 return ZX_ERR_IO;
304 }
305
306 // Write up to len - 1 bytes, since we should watch the EXPECT bit
307 // transition on the final byte
308 uint16_t to_write = burst_count;
309 if (to_write > len - 1) {
310 to_write = static_cast<uint16_t>(len - 1);
311 }
312
313 status = iface_->WriteDataFifo(loc, &cmd[bytes_sent], to_write);
314 if (status != ZX_OK) {
315 AbortCommand(iface_.get(), loc);
316 return status;
317 }
318 bytes_sent += to_write;
319 burst_count = static_cast<uint16_t>(burst_count - to_write);
320
321 if (burst_count > 0 && bytes_sent == len - 1) {
322 bool expect = false;
323 // Watch the EXPECT bit as we write the last byte, it should
324 // transition.
325 status = GetStatusExpect(iface_.get(), loc, &expect);
326 status = CheckExpectedState(status, expect, true);
327 if (status < 0) {
328 AbortCommand(iface_.get(), loc);
329 return status;
330 }
331
332 status = iface_->WriteDataFifo(loc, &cmd[bytes_sent], 1);
333 if (status != ZX_OK) {
334 AbortCommand(iface_.get(), loc);
335 return status;
336 }
337 ++bytes_sent;
338
339 status = GetStatusExpect(iface_.get(), loc, &expect);
340 status = CheckExpectedState(status, expect, false);
341 if (status < 0) {
342 AbortCommand(iface_.get(), loc);
343 return status;
344 }
345 }
346 }
347
348 // Run the command
349 status = iface_->WriteStatus(loc, TPM_STS_TPM_GO);
350 if (status != ZX_OK) {
351 AbortCommand(iface_.get(), loc);
352 return status;
353 }
354 return ZX_OK;
355 }
356
RecvRespLocked(Locality loc,uint8_t * resp,size_t max_len,size_t * actual)357 zx_status_t Device::RecvRespLocked(Locality loc, uint8_t* resp, size_t max_len, size_t* actual) {
358 bool active = false;
359 zx_status_t status = GetActiveLocality(iface_.get(), loc, &active);
360 status = CheckExpectedState(status, active, true);
361 if (status != ZX_OK) {
362 AbortCommand(iface_.get(), loc);
363 return status;
364 }
365
366 // This procedure is described in section 5.5.2.2.2 of the TCG PC Client
367 // Platform TPM profile spec (family 2.0, which also describes 1.2)
368
369 // Wait for data to be available
370 status = WaitForDataAvail(iface_.get(), loc);
371 if (status != ZX_OK) {
372 AbortCommand(iface_.get(), loc);
373 return status;
374 }
375
376 bool more_data = true;
377 size_t bytes_recvd = 0;
378 while (more_data) {
379 zxlogf(TRACE, "Reading response, %zu bytes read\n", bytes_recvd);
380 uint16_t burst_count;
381 status = GetBurstCount(iface_.get(), loc, &burst_count);
382 if (status != ZX_OK) {
383 AbortCommand(iface_.get(), loc);
384 return status;
385 }
386
387 size_t to_read = burst_count;
388 size_t remaining = max_len - bytes_recvd;
389 if (to_read > remaining) {
390 to_read = remaining;
391 }
392 status = iface_->ReadDataFifo(loc, &resp[bytes_recvd], to_read);
393 if (status != ZX_OK) {
394 AbortCommand(iface_.get(), loc);
395 return status;
396 }
397 bytes_recvd += to_read;
398
399 // See if there is any more data to read
400 bool data_avail = false;
401 status = GetStatusDataAvail(iface_.get(), loc, &data_avail);
402 if (status < 0) {
403 AbortCommand(iface_.get(), loc);
404 return status;
405 } else if (!data_avail) {
406 more_data = false;
407 break;
408 }
409
410 // If we have filled the buffer and there is more data, exit
411 // the loop so we will send a CMD_RDY (which doubles as an abort).
412 if (bytes_recvd >= max_len) {
413 more_data = false;
414 }
415 }
416
417 // Either abort a response if we filled our buffer, or acknowledge that
418 // we've finished receiving the data. (Transitions 30 and 37 in Table 22
419 // (State Transition Table)).
420 AbortCommand(iface_.get(), loc);
421
422 *actual = bytes_recvd;
423 return ZX_OK;
424 }
425
426 } // namespace tpm
427