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