• 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,
85                                      unsigned addr) {
86   unsigned char* response;
87   SpiFrameHeader header;
88 
89   usleep(10000);  // give it 10 ms. TODO(vbendeb): remove this once
90                   // cr50 SPS TPM driver performance is fixed.
91 
92   // The first byte of the frame header encodes the transaction type (read or
93   // write) and size (set to lenth - 1).
94   header.body[0] = (read_write ? 0x80 : 0) | 0x40 | (bytes - 1);
95 
96   // The rest of the frame header is the internal address in the TPM
97   for (int i = 0; i < 3; i++)
98     header.body[i + 1] = (addr >> (8 * (2 - i))) & 0xff;
99 
100   Start(mpsse_);
101 
102   response = Transfer(mpsse_, header.body, sizeof(header.body));
103 
104   // The TCG TPM over SPI specification itroduces the notion of SPI flow
105   // control (Section "6.4.5 Flow Control" of the TCG issued "TPM Profile
106   // (PTP) Specification Revision 00.43).
107 
108   // The slave (TPM device) expects each transaction to start with a 4 byte
109   // header trasmitted by master. If the slave needs to stall the transaction,
110   // it sets the MOSI bit to 0 during the last clock of the 4 byte header. In
111   // this case the master is supposed to start polling the line, byte at time,
112   // until the last bit in the received byte (transferred during the last
113   // clock of the byte) is set to 1.
114   while (!(response[3] & 1)) {
115     unsigned char* poll_state;
116 
117     poll_state = Read(mpsse_, 1);
118     response[3] = *poll_state;
119     free(poll_state);
120   }
121   free(response);
122 }
123 
FtdiWriteReg(unsigned reg_number,size_t bytes,const void * buffer)124 bool TrunksFtdiSpi::FtdiWriteReg(unsigned reg_number,
125                                  size_t bytes,
126                                  const void* buffer) {
127   if (!mpsse_)
128     return false;
129 
130   StartTransaction(false, bytes, reg_number + locality_ * 0x10000);
131   Write(mpsse_, buffer, bytes);
132   Stop(mpsse_);
133   return true;
134 }
135 
FtdiReadReg(unsigned reg_number,size_t bytes,void * buffer)136 bool TrunksFtdiSpi::FtdiReadReg(unsigned reg_number,
137                                 size_t bytes,
138                                 void* buffer) {
139   unsigned char* value;
140 
141   if (!mpsse_)
142     return false;
143 
144   StartTransaction(true, bytes, reg_number + locality_ * 0x10000);
145   value = Read(mpsse_, bytes);
146   if (buffer)
147     memcpy(buffer, value, bytes);
148   free(value);
149   Stop(mpsse_);
150   return true;
151 }
152 
GetBurstCount(void)153 size_t TrunksFtdiSpi::GetBurstCount(void) {
154   uint32_t status;
155 
156   ReadTpmSts(&status);
157   return (size_t)((status >> burstCountShift) & burstCountMask);
158 }
159 
Init()160 bool TrunksFtdiSpi::Init() {
161   uint32_t did_vid, status;
162   uint8_t cmd;
163 
164   if (mpsse_)
165     return true;
166 
167   mpsse_ = MPSSE(SPI0, ONE_MHZ, MSB);
168   if (!mpsse_)
169     return false;
170 
171   // Reset the TPM using GPIOL0, issue a 100 ms long pulse.
172   PinLow(mpsse_, GPIOL0);
173   usleep(100000);
174   PinHigh(mpsse_, GPIOL0);
175 
176   FtdiReadReg(TPM_DID_VID_REG, sizeof(did_vid), &did_vid);
177 
178   uint16_t vid = did_vid & 0xffff;
179   if ((vid != 0x15d1) && (vid != 0x1ae0)) {
180     LOG(ERROR) << "unknown did_vid: 0x" << std::hex << did_vid;
181     return false;
182   }
183 
184   // Try claiming locality zero.
185   FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
186   // tpmEstablishment can be either set or not.
187   if ((cmd & ~tpmEstablishment) != tpmRegValidSts) {
188     LOG(ERROR) << "invalid reset status: 0x" << std::hex << (unsigned)cmd;
189     return false;
190   }
191   cmd = requestUse;
192   FtdiWriteReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
193   FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
194   if ((cmd & ~tpmEstablishment) != (tpmRegValidSts | activeLocality)) {
195     LOG(ERROR) << "failed to claim locality, status: 0x" << std::hex
196                << (unsigned)cmd;
197     return false;
198   }
199 
200   ReadTpmSts(&status);
201   if (((status >> tpmFamilyShift) & tpmFamilyMask) != tpmFamilyTPM2) {
202     LOG(ERROR) << "unexpected TPM family value, status: 0x" << std::hex
203                << status;
204     return false;
205   }
206   FtdiReadReg(TPM_RID_REG, sizeof(cmd), &cmd);
207   printf("Connected to device vid:did:rid of %4.4x:%4.4x:%2.2x\n",
208          did_vid & 0xffff, did_vid >> 16, cmd);
209 
210   return true;
211 }
212 
SendCommand(const std::string & command,const ResponseCallback & callback)213 void TrunksFtdiSpi::SendCommand(const std::string& command,
214                                 const ResponseCallback& callback) {
215   printf("%s invoked\n", __func__);
216 }
217 
WaitForStatus(uint32_t statusMask,uint32_t statusExpected,int timeout_ms)218 bool TrunksFtdiSpi::WaitForStatus(uint32_t statusMask,
219                                   uint32_t statusExpected,
220                                   int timeout_ms) {
221   uint32_t status;
222   time_t target_time;
223 
224   target_time = time(NULL) + timeout_ms / 1000;
225   do {
226     usleep(10000);  // 10 ms polling period.
227     if (time(NULL) >= target_time) {
228       LOG(ERROR) << "failed to get expected status " << std::hex
229                  << statusExpected;
230       return false;
231     }
232     ReadTpmSts(&status);
233   } while ((status & statusMask) != statusExpected);
234   return true;
235 }
236 
SendCommandAndWait(const std::string & command)237 std::string TrunksFtdiSpi::SendCommandAndWait(const std::string& command) {
238   uint32_t status;
239   uint32_t expected_status_bits;
240   size_t transaction_size, handled_so_far(0);
241 
242   std::string rv("");
243 
244   if (!mpsse_) {
245     LOG(ERROR) << "attempt to use an uninitialized FTDI TPM!";
246     return rv;
247   }
248 
249   WriteTpmSts(commandReady);
250 
251   // No need to wait for the sts.Expect bit to be set, at least with the
252   // 15d1:001b device, let's just write the command into FIFO, not exceeding
253   // the minimum of the two values - burst_count and 64 (which is the protocol
254   // limitation)
255   do {
256     transaction_size = std::min(
257         std::min(command.size() - handled_so_far, GetBurstCount()), (size_t)64);
258 
259     if (transaction_size) {
260       LOG(INFO) << "will transfer " << transaction_size << " bytes";
261       FtdiWriteReg(TPM_DATA_FIFO_REG, transaction_size,
262                    command.c_str() + handled_so_far);
263       handled_so_far += transaction_size;
264     }
265   } while (handled_so_far != command.size());
266 
267   // And tell the device it can start processing it.
268   WriteTpmSts(tpmGo);
269 
270   expected_status_bits = stsValid | dataAvail;
271   if (!WaitForStatus(expected_status_bits, expected_status_bits))
272     return rv;
273 
274   // The response is ready, let's read it.
275   // First we read the FIFO payload header, to see how much data to expect.
276   // The header size is fixed to six bytes, the total payload size is stored
277   // in network order in the last four bytes of the header.
278   char data_header[6];
279 
280   // Let's read the header first.
281   FtdiReadReg(TPM_DATA_FIFO_REG, sizeof(data_header), data_header);
282 
283   // Figure out the total payload size.
284   uint32_t payload_size;
285   memcpy(&payload_size, data_header + 2, sizeof(payload_size));
286   payload_size = be32toh(payload_size);
287   // A FIFO message with the minimum required header and contents can not be
288   // less than 10 bytes long. It also should never be more than 4096 bytes
289   // long.
290   if ((payload_size < 10) || (payload_size > MAX_RESPONSE_SIZE)) {
291     // Something must be wrong...
292     LOG(ERROR) << "Bad total payload size value: " << payload_size;
293     return rv;
294   }
295 
296   LOG(INFO) << "Total payload size " << payload_size;
297 
298   // Let's read all but the last byte in the FIFO to make sure the status
299   // register is showing correct flow control bits: 'more data' until the last
300   // byte and then 'no more data' once the last byte is read.
301   handled_so_far = 0;
302   payload_size = payload_size - sizeof(data_header) - 1;
303   // Allow room for the last byte too.
304   uint8_t* payload = new uint8_t[payload_size + 1];
305   do {
306     transaction_size = std::min(
307         std::min(payload_size - handled_so_far, GetBurstCount()), (size_t)64);
308 
309     if (transaction_size) {
310       FtdiReadReg(TPM_DATA_FIFO_REG, transaction_size,
311                   payload + handled_so_far);
312       handled_so_far += transaction_size;
313     }
314   } while (handled_so_far != payload_size);
315 
316   // Verify that there is still data to come.
317   ReadTpmSts(&status);
318   if ((status & expected_status_bits) != expected_status_bits) {
319     LOG(ERROR) << "unexpected status 0x" << std::hex << status;
320     delete[] payload;
321     return rv;
322   }
323 
324   // Now, read the last byte of the payload.
325   FtdiReadReg(TPM_DATA_FIFO_REG, sizeof(uint8_t), payload + payload_size);
326 
327   // Verify that 'data available' is not asseretd any more.
328   ReadTpmSts(&status);
329   if ((status & expected_status_bits) != stsValid) {
330     LOG(ERROR) << "unexpected status 0x" << std::hex << status;
331     delete[] payload;
332     return rv;
333   }
334 
335   rv = std::string(data_header, sizeof(data_header)) +
336        std::string(reinterpret_cast<char*>(payload), payload_size + 1);
337 
338   /* Move the TPM back to idle state. */
339   WriteTpmSts(commandReady);
340 
341   delete[] payload;
342   return rv;
343 }
344 
345 }  // namespace trunks
346