• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2015 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include <algorithm>
18 #include <ctime>
19 #include <string>
20 #include <unistd.h>
21 
22 #include <base/logging.h>
23 
24 #include "trunks/tpm_generated.h"
25 #include "trunks/trunks_ftdi_spi.h"
26 
27 // Assorted TPM2 registers for interface type FIFO.
28 #define TPM_ACCESS_REG       0
29 #define TPM_STS_REG       0x18
30 #define TPM_DATA_FIFO_REG 0x24
31 #define TPM_DID_VID_REG  0xf00
32 #define TPM_RID_REG      0xf04
33 
34 namespace trunks {
35 
36 // Locality management bits (in TPM_ACCESS_REG)
37 enum TpmAccessBits {
38   tpmRegValidSts = (1 << 7),
39   activeLocality = (1 << 5),
40   requestUse = (1 << 1),
41   tpmEstablishment = (1 << 0),
42 };
43 
44 enum TpmStsBits {
45   tpmFamilyShift = 26,
46   tpmFamilyMask = ((1 << 2) - 1),  // 2 bits wide
47   tpmFamilyTPM2 = 1,
48   resetEstablishmentBit = (1 << 25),
49   commandCancel = (1 << 24),
50   burstCountShift = 8,
51   burstCountMask = ((1 << 16) -1),  // 16 bits wide
52   stsValid = (1 << 7),
53   commandReady = (1 << 6),
54   tpmGo = (1 << 5),
55   dataAvail = (1 << 4),
56   Expect = (1 << 3),
57   selfTestDone = (1 << 2),
58   responseRetry = (1 << 1),
59 };
60 
61   // SPI frame header for TPM transactions is 4 bytes in size, it is described
62   // in section "6.4.6 Spi Bit Protocol" of the TCG issued "TPM Profile (PTP)
63   // Specification Revision 00.43.
64 struct SpiFrameHeader {
65   unsigned char body[4];
66 };
67 
~TrunksFtdiSpi()68 TrunksFtdiSpi::~TrunksFtdiSpi() {
69   if (mpsse_)
70     Close(mpsse_);
71 
72   mpsse_ = NULL;
73 }
74 
ReadTpmSts(uint32_t * status)75 bool TrunksFtdiSpi::ReadTpmSts(uint32_t *status) {
76   return FtdiReadReg(TPM_STS_REG, sizeof(*status), status);
77 }
78 
WriteTpmSts(uint32_t status)79 bool TrunksFtdiSpi::WriteTpmSts(uint32_t status) {
80   return FtdiWriteReg(TPM_STS_REG, sizeof(status), &status);
81 }
82 
StartTransaction(bool read_write,size_t bytes,unsigned addr)83 void TrunksFtdiSpi::StartTransaction(bool read_write,
84                                      size_t bytes, unsigned addr) {
85   unsigned char *response;
86   SpiFrameHeader header;
87 
88   usleep(10000);  // give it 10 ms. TODO(vbendeb): remove this once
89                   // cr50 SPS TPM driver performance is fixed.
90 
91   // The first byte of the frame header encodes the transaction type (read or
92   // write) and size (set to lenth - 1).
93   header.body[0] = (read_write ? 0x80 : 0) | 0x40 | (bytes - 1);
94 
95   // The rest of the frame header is the internal address in the TPM
96   for (int i = 0; i < 3; i++)
97     header.body[i + 1] = (addr >> (8 * (2 - i))) & 0xff;
98 
99   Start(mpsse_);
100 
101   response = Transfer(mpsse_, header.body, sizeof(header.body));
102 
103   // The TCG TPM over SPI specification itroduces the notion of SPI flow
104   // control (Section "6.4.5 Flow Control" of the TCG issued "TPM Profile
105   // (PTP) Specification Revision 00.43).
106 
107   // The slave (TPM device) expects each transaction to start with a 4 byte
108   // header trasmitted by master. If the slave needs to stall the transaction,
109   // it sets the MOSI bit to 0 during the last clock of the 4 byte header. In
110   // this case the master is supposed to start polling the line, byte at time,
111   // until the last bit in the received byte (transferred during the last
112   // clock of the byte) is set to 1.
113   while (!(response[3] & 1)) {
114     unsigned char *poll_state;
115 
116     poll_state = Read(mpsse_, 1);
117     response[3] = *poll_state;
118     free(poll_state);
119   }
120   free(response);
121 }
122 
FtdiWriteReg(unsigned reg_number,size_t bytes,const void * buffer)123 bool TrunksFtdiSpi::FtdiWriteReg(unsigned reg_number, size_t bytes,
124                                  const void *buffer) {
125   if (!mpsse_)
126     return false;
127 
128   StartTransaction(false, bytes, reg_number + locality_ * 0x10000);
129   Write(mpsse_, buffer, bytes);
130   Stop(mpsse_);
131   return true;
132 }
133 
FtdiReadReg(unsigned reg_number,size_t bytes,void * buffer)134 bool TrunksFtdiSpi::FtdiReadReg(unsigned reg_number, size_t bytes,
135                                 void *buffer) {
136   unsigned char *value;
137 
138   if (!mpsse_)
139     return false;
140 
141   StartTransaction(true, bytes, reg_number + locality_ * 0x10000);
142   value = Read(mpsse_, bytes);
143   if (buffer)
144     memcpy(buffer, value, bytes);
145   free(value);
146   Stop(mpsse_);
147   return true;
148 }
149 
GetBurstCount(void)150 size_t TrunksFtdiSpi::GetBurstCount(void) {
151   uint32_t status;
152 
153   ReadTpmSts(&status);
154   return (size_t)((status >> burstCountShift) & burstCountMask);
155 }
156 
Init()157 bool TrunksFtdiSpi::Init() {
158   uint32_t did_vid, status;
159   uint8_t cmd;
160 
161   if (mpsse_)
162     return true;
163 
164   mpsse_ = MPSSE(SPI0, ONE_MHZ, MSB);
165   if (!mpsse_)
166     return false;
167 
168   // Reset the TPM using GPIOL0, issue a 100 ms long pulse.
169   PinLow(mpsse_, GPIOL0);
170   usleep(100000);
171   PinHigh(mpsse_, GPIOL0);
172 
173   FtdiReadReg(TPM_DID_VID_REG, sizeof(did_vid), &did_vid);
174 
175   uint16_t vid = did_vid & 0xffff;
176   if ((vid != 0x15d1) && (vid != 0x1ae0)) {
177     LOG(ERROR) << "unknown did_vid: 0x" << std::hex << did_vid;
178     return false;
179   }
180 
181   // Try claiming locality zero.
182   FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
183   // tpmEstablishment can be either set or not.
184   if ((cmd & ~tpmEstablishment) != tpmRegValidSts) {
185     LOG(ERROR) << "invalid reset status: 0x" << std::hex << (unsigned)cmd;
186     return false;
187   }
188   cmd = requestUse;
189   FtdiWriteReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
190   FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
191   if ((cmd &  ~tpmEstablishment) != (tpmRegValidSts | activeLocality)) {
192     LOG(ERROR) << "failed to claim locality, status: 0x" << std::hex
193                << (unsigned)cmd;
194     return false;
195   }
196 
197   ReadTpmSts(&status);
198   if (((status >> tpmFamilyShift) & tpmFamilyMask) != tpmFamilyTPM2) {
199     LOG(ERROR) << "unexpected TPM family value, status: 0x" << std::hex
200                << status;
201     return false;
202   }
203   FtdiReadReg(TPM_RID_REG, sizeof(cmd), &cmd);
204   printf("Connected to device vid:did:rid of %4.4x:%4.4x:%2.2x\n",
205          did_vid & 0xffff, did_vid >> 16, cmd);
206 
207   return true;
208 }
209 
SendCommand(const std::string & command,const ResponseCallback & callback)210 void TrunksFtdiSpi::SendCommand(const std::string& command,
211                                 const ResponseCallback& callback) {
212   printf("%s invoked\n", __func__);
213 }
214 
WaitForStatus(uint32_t statusMask,uint32_t statusExpected,int timeout_ms)215 bool TrunksFtdiSpi::WaitForStatus(uint32_t statusMask,
216                                   uint32_t statusExpected, int timeout_ms) {
217   uint32_t status;
218   time_t target_time;
219 
220   target_time = time(NULL) + timeout_ms / 1000;
221   do {
222     usleep(10000);  // 10 ms polling period.
223     if (time(NULL) >= target_time) {
224       LOG(ERROR) << "failed to get expected status " << std::hex
225                  << statusExpected;
226       return false;
227     }
228     ReadTpmSts(&status);
229   } while ((status & statusMask) != statusExpected);
230   return true;
231 }
232 
SendCommandAndWait(const std::string & command)233 std::string TrunksFtdiSpi::SendCommandAndWait(const std::string& command) {
234   uint32_t status;
235   uint32_t expected_status_bits;
236   size_t transaction_size, handled_so_far(0);
237 
238   std::string rv("");
239 
240   if (!mpsse_) {
241     LOG(ERROR) << "attempt to use an uninitialized FTDI TPM!";
242     return rv;
243   }
244 
245   WriteTpmSts(commandReady);
246 
247   // No need to wait for the sts.Expect bit to be set, at least with the
248   // 15d1:001b device, let's just write the command into FIFO, not exceeding
249   // the minimum of the two values - burst_count and 64 (which is the protocol
250   // limitation)
251   do {
252     transaction_size = std::min(std::min(command.size() - handled_so_far,
253                                          GetBurstCount()),
254                                 (size_t)64);
255 
256     if (transaction_size) {
257       LOG(INFO) << "will transfer " << transaction_size << " bytes";
258       FtdiWriteReg(TPM_DATA_FIFO_REG, transaction_size,
259                    command.c_str() + handled_so_far);
260       handled_so_far += transaction_size;
261     }
262   } while (handled_so_far != command.size());
263 
264   // And tell the device it can start processing it.
265   WriteTpmSts(tpmGo);
266 
267   expected_status_bits = stsValid | dataAvail;
268   if (!WaitForStatus(expected_status_bits, expected_status_bits))
269       return rv;
270 
271   // The response is ready, let's read it.
272   // First we read the FIFO payload header, to see how much data to expect.
273   // The header size is fixed to six bytes, the total payload size is stored
274   // in network order in the last four bytes of the header.
275   char data_header[6];
276 
277   // Let's read the header first.
278   FtdiReadReg(TPM_DATA_FIFO_REG, sizeof(data_header), data_header);
279 
280   // Figure out the total payload size.
281   uint32_t payload_size;
282   memcpy(&payload_size, data_header + 2, sizeof(payload_size));
283   payload_size = be32toh(payload_size);
284   // A FIFO message with the minimum required header and contents can not be
285   // less than 10 bytes long. It also should never be more than 4096 bytes
286   // long.
287   if ((payload_size < 10) || (payload_size > MAX_RESPONSE_SIZE)) {
288     // Something must be wrong...
289     LOG(ERROR) << "Bad total payload size value: " << payload_size;
290     return rv;
291   }
292 
293   LOG(INFO) << "Total payload size " << payload_size;
294 
295 
296   // Let's read all but the last byte in the FIFO to make sure the status
297   // register is showing correct flow control bits: 'more data' until the last
298   // byte and then 'no more data' once the last byte is read.
299   handled_so_far = 0;
300   payload_size = payload_size - sizeof(data_header) - 1;
301   // Allow room for the last byte too.
302   uint8_t *payload = new uint8_t[payload_size + 1];
303   do {
304     transaction_size = std::min(std::min(payload_size - handled_so_far,
305                                          GetBurstCount()),
306                                 (size_t)64);
307 
308     if (transaction_size) {
309       FtdiReadReg(TPM_DATA_FIFO_REG, transaction_size,
310                   payload + handled_so_far);
311       handled_so_far += transaction_size;
312     }
313   } while (handled_so_far != payload_size);
314 
315   // Verify that there is still data to come.
316   ReadTpmSts(&status);
317   if ((status & expected_status_bits) != expected_status_bits) {
318     LOG(ERROR) << "unexpected status 0x" << std::hex << status;
319     delete[] payload;
320     return rv;
321   }
322 
323   // Now, read the last byte of the payload.
324   FtdiReadReg(TPM_DATA_FIFO_REG, sizeof(uint8_t), payload + payload_size);
325 
326   // Verify that 'data available' is not asseretd any more.
327   ReadTpmSts(&status);
328   if ((status & expected_status_bits) != stsValid) {
329     LOG(ERROR) << "unexpected status 0x" << std::hex << status;
330     delete[] payload;
331     return rv;
332   }
333 
334   rv = std::string(data_header, sizeof(data_header)) +
335     std::string(reinterpret_cast<char *>(payload), payload_size + 1);
336 
337   /* Move the TPM back to idle state. */
338   WriteTpmSts(commandReady);
339 
340   delete[] payload;
341   return rv;
342 }
343 
344 }  // namespace trunks
345