upload android base code part6
This commit is contained in:
parent
421e214c7d
commit
4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions
345
android/system/tpm/trunks/trunks_ftdi_spi.cc
Normal file
345
android/system/tpm/trunks/trunks_ftdi_spi.cc
Normal file
|
@ -0,0 +1,345 @@
|
|||
//
|
||||
// Copyright (C) 2015 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <base/logging.h>
|
||||
|
||||
#include "trunks/tpm_generated.h"
|
||||
#include "trunks/trunks_ftdi_spi.h"
|
||||
|
||||
// Assorted TPM2 registers for interface type FIFO.
|
||||
#define TPM_ACCESS_REG 0
|
||||
#define TPM_STS_REG 0x18
|
||||
#define TPM_DATA_FIFO_REG 0x24
|
||||
#define TPM_DID_VID_REG 0xf00
|
||||
#define TPM_RID_REG 0xf04
|
||||
|
||||
namespace trunks {
|
||||
|
||||
// Locality management bits (in TPM_ACCESS_REG)
|
||||
enum TpmAccessBits {
|
||||
tpmRegValidSts = (1 << 7),
|
||||
activeLocality = (1 << 5),
|
||||
requestUse = (1 << 1),
|
||||
tpmEstablishment = (1 << 0),
|
||||
};
|
||||
|
||||
enum TpmStsBits {
|
||||
tpmFamilyShift = 26,
|
||||
tpmFamilyMask = ((1 << 2) - 1), // 2 bits wide
|
||||
tpmFamilyTPM2 = 1,
|
||||
resetEstablishmentBit = (1 << 25),
|
||||
commandCancel = (1 << 24),
|
||||
burstCountShift = 8,
|
||||
burstCountMask = ((1 << 16) - 1), // 16 bits wide
|
||||
stsValid = (1 << 7),
|
||||
commandReady = (1 << 6),
|
||||
tpmGo = (1 << 5),
|
||||
dataAvail = (1 << 4),
|
||||
Expect = (1 << 3),
|
||||
selfTestDone = (1 << 2),
|
||||
responseRetry = (1 << 1),
|
||||
};
|
||||
|
||||
// SPI frame header for TPM transactions is 4 bytes in size, it is described
|
||||
// in section "6.4.6 Spi Bit Protocol" of the TCG issued "TPM Profile (PTP)
|
||||
// Specification Revision 00.43.
|
||||
struct SpiFrameHeader {
|
||||
unsigned char body[4];
|
||||
};
|
||||
|
||||
TrunksFtdiSpi::~TrunksFtdiSpi() {
|
||||
if (mpsse_)
|
||||
Close(mpsse_);
|
||||
|
||||
mpsse_ = NULL;
|
||||
}
|
||||
|
||||
bool TrunksFtdiSpi::ReadTpmSts(uint32_t* status) {
|
||||
return FtdiReadReg(TPM_STS_REG, sizeof(*status), status);
|
||||
}
|
||||
|
||||
bool TrunksFtdiSpi::WriteTpmSts(uint32_t status) {
|
||||
return FtdiWriteReg(TPM_STS_REG, sizeof(status), &status);
|
||||
}
|
||||
|
||||
void TrunksFtdiSpi::StartTransaction(bool read_write,
|
||||
size_t bytes,
|
||||
unsigned addr) {
|
||||
unsigned char* response;
|
||||
SpiFrameHeader header;
|
||||
|
||||
usleep(10000); // give it 10 ms. TODO(vbendeb): remove this once
|
||||
// cr50 SPS TPM driver performance is fixed.
|
||||
|
||||
// The first byte of the frame header encodes the transaction type (read or
|
||||
// write) and size (set to lenth - 1).
|
||||
header.body[0] = (read_write ? 0x80 : 0) | 0x40 | (bytes - 1);
|
||||
|
||||
// The rest of the frame header is the internal address in the TPM
|
||||
for (int i = 0; i < 3; i++)
|
||||
header.body[i + 1] = (addr >> (8 * (2 - i))) & 0xff;
|
||||
|
||||
Start(mpsse_);
|
||||
|
||||
response = Transfer(mpsse_, header.body, sizeof(header.body));
|
||||
|
||||
// The TCG TPM over SPI specification itroduces the notion of SPI flow
|
||||
// control (Section "6.4.5 Flow Control" of the TCG issued "TPM Profile
|
||||
// (PTP) Specification Revision 00.43).
|
||||
|
||||
// The slave (TPM device) expects each transaction to start with a 4 byte
|
||||
// header trasmitted by master. If the slave needs to stall the transaction,
|
||||
// it sets the MOSI bit to 0 during the last clock of the 4 byte header. In
|
||||
// this case the master is supposed to start polling the line, byte at time,
|
||||
// until the last bit in the received byte (transferred during the last
|
||||
// clock of the byte) is set to 1.
|
||||
while (!(response[3] & 1)) {
|
||||
unsigned char* poll_state;
|
||||
|
||||
poll_state = Read(mpsse_, 1);
|
||||
response[3] = *poll_state;
|
||||
free(poll_state);
|
||||
}
|
||||
free(response);
|
||||
}
|
||||
|
||||
bool TrunksFtdiSpi::FtdiWriteReg(unsigned reg_number,
|
||||
size_t bytes,
|
||||
const void* buffer) {
|
||||
if (!mpsse_)
|
||||
return false;
|
||||
|
||||
StartTransaction(false, bytes, reg_number + locality_ * 0x10000);
|
||||
Write(mpsse_, buffer, bytes);
|
||||
Stop(mpsse_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrunksFtdiSpi::FtdiReadReg(unsigned reg_number,
|
||||
size_t bytes,
|
||||
void* buffer) {
|
||||
unsigned char* value;
|
||||
|
||||
if (!mpsse_)
|
||||
return false;
|
||||
|
||||
StartTransaction(true, bytes, reg_number + locality_ * 0x10000);
|
||||
value = Read(mpsse_, bytes);
|
||||
if (buffer)
|
||||
memcpy(buffer, value, bytes);
|
||||
free(value);
|
||||
Stop(mpsse_);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t TrunksFtdiSpi::GetBurstCount(void) {
|
||||
uint32_t status;
|
||||
|
||||
ReadTpmSts(&status);
|
||||
return (size_t)((status >> burstCountShift) & burstCountMask);
|
||||
}
|
||||
|
||||
bool TrunksFtdiSpi::Init() {
|
||||
uint32_t did_vid, status;
|
||||
uint8_t cmd;
|
||||
|
||||
if (mpsse_)
|
||||
return true;
|
||||
|
||||
mpsse_ = MPSSE(SPI0, ONE_MHZ, MSB);
|
||||
if (!mpsse_)
|
||||
return false;
|
||||
|
||||
// Reset the TPM using GPIOL0, issue a 100 ms long pulse.
|
||||
PinLow(mpsse_, GPIOL0);
|
||||
usleep(100000);
|
||||
PinHigh(mpsse_, GPIOL0);
|
||||
|
||||
FtdiReadReg(TPM_DID_VID_REG, sizeof(did_vid), &did_vid);
|
||||
|
||||
uint16_t vid = did_vid & 0xffff;
|
||||
if ((vid != 0x15d1) && (vid != 0x1ae0)) {
|
||||
LOG(ERROR) << "unknown did_vid: 0x" << std::hex << did_vid;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try claiming locality zero.
|
||||
FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
|
||||
// tpmEstablishment can be either set or not.
|
||||
if ((cmd & ~tpmEstablishment) != tpmRegValidSts) {
|
||||
LOG(ERROR) << "invalid reset status: 0x" << std::hex << (unsigned)cmd;
|
||||
return false;
|
||||
}
|
||||
cmd = requestUse;
|
||||
FtdiWriteReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
|
||||
FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
|
||||
if ((cmd & ~tpmEstablishment) != (tpmRegValidSts | activeLocality)) {
|
||||
LOG(ERROR) << "failed to claim locality, status: 0x" << std::hex
|
||||
<< (unsigned)cmd;
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadTpmSts(&status);
|
||||
if (((status >> tpmFamilyShift) & tpmFamilyMask) != tpmFamilyTPM2) {
|
||||
LOG(ERROR) << "unexpected TPM family value, status: 0x" << std::hex
|
||||
<< status;
|
||||
return false;
|
||||
}
|
||||
FtdiReadReg(TPM_RID_REG, sizeof(cmd), &cmd);
|
||||
printf("Connected to device vid:did:rid of %4.4x:%4.4x:%2.2x\n",
|
||||
did_vid & 0xffff, did_vid >> 16, cmd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TrunksFtdiSpi::SendCommand(const std::string& command,
|
||||
const ResponseCallback& callback) {
|
||||
printf("%s invoked\n", __func__);
|
||||
}
|
||||
|
||||
bool TrunksFtdiSpi::WaitForStatus(uint32_t statusMask,
|
||||
uint32_t statusExpected,
|
||||
int timeout_ms) {
|
||||
uint32_t status;
|
||||
time_t target_time;
|
||||
|
||||
target_time = time(NULL) + timeout_ms / 1000;
|
||||
do {
|
||||
usleep(10000); // 10 ms polling period.
|
||||
if (time(NULL) >= target_time) {
|
||||
LOG(ERROR) << "failed to get expected status " << std::hex
|
||||
<< statusExpected;
|
||||
return false;
|
||||
}
|
||||
ReadTpmSts(&status);
|
||||
} while ((status & statusMask) != statusExpected);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string TrunksFtdiSpi::SendCommandAndWait(const std::string& command) {
|
||||
uint32_t status;
|
||||
uint32_t expected_status_bits;
|
||||
size_t transaction_size, handled_so_far(0);
|
||||
|
||||
std::string rv("");
|
||||
|
||||
if (!mpsse_) {
|
||||
LOG(ERROR) << "attempt to use an uninitialized FTDI TPM!";
|
||||
return rv;
|
||||
}
|
||||
|
||||
WriteTpmSts(commandReady);
|
||||
|
||||
// No need to wait for the sts.Expect bit to be set, at least with the
|
||||
// 15d1:001b device, let's just write the command into FIFO, not exceeding
|
||||
// the minimum of the two values - burst_count and 64 (which is the protocol
|
||||
// limitation)
|
||||
do {
|
||||
transaction_size = std::min(
|
||||
std::min(command.size() - handled_so_far, GetBurstCount()), (size_t)64);
|
||||
|
||||
if (transaction_size) {
|
||||
LOG(INFO) << "will transfer " << transaction_size << " bytes";
|
||||
FtdiWriteReg(TPM_DATA_FIFO_REG, transaction_size,
|
||||
command.c_str() + handled_so_far);
|
||||
handled_so_far += transaction_size;
|
||||
}
|
||||
} while (handled_so_far != command.size());
|
||||
|
||||
// And tell the device it can start processing it.
|
||||
WriteTpmSts(tpmGo);
|
||||
|
||||
expected_status_bits = stsValid | dataAvail;
|
||||
if (!WaitForStatus(expected_status_bits, expected_status_bits))
|
||||
return rv;
|
||||
|
||||
// The response is ready, let's read it.
|
||||
// First we read the FIFO payload header, to see how much data to expect.
|
||||
// The header size is fixed to six bytes, the total payload size is stored
|
||||
// in network order in the last four bytes of the header.
|
||||
char data_header[6];
|
||||
|
||||
// Let's read the header first.
|
||||
FtdiReadReg(TPM_DATA_FIFO_REG, sizeof(data_header), data_header);
|
||||
|
||||
// Figure out the total payload size.
|
||||
uint32_t payload_size;
|
||||
memcpy(&payload_size, data_header + 2, sizeof(payload_size));
|
||||
payload_size = be32toh(payload_size);
|
||||
// A FIFO message with the minimum required header and contents can not be
|
||||
// less than 10 bytes long. It also should never be more than 4096 bytes
|
||||
// long.
|
||||
if ((payload_size < 10) || (payload_size > MAX_RESPONSE_SIZE)) {
|
||||
// Something must be wrong...
|
||||
LOG(ERROR) << "Bad total payload size value: " << payload_size;
|
||||
return rv;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Total payload size " << payload_size;
|
||||
|
||||
// Let's read all but the last byte in the FIFO to make sure the status
|
||||
// register is showing correct flow control bits: 'more data' until the last
|
||||
// byte and then 'no more data' once the last byte is read.
|
||||
handled_so_far = 0;
|
||||
payload_size = payload_size - sizeof(data_header) - 1;
|
||||
// Allow room for the last byte too.
|
||||
uint8_t* payload = new uint8_t[payload_size + 1];
|
||||
do {
|
||||
transaction_size = std::min(
|
||||
std::min(payload_size - handled_so_far, GetBurstCount()), (size_t)64);
|
||||
|
||||
if (transaction_size) {
|
||||
FtdiReadReg(TPM_DATA_FIFO_REG, transaction_size,
|
||||
payload + handled_so_far);
|
||||
handled_so_far += transaction_size;
|
||||
}
|
||||
} while (handled_so_far != payload_size);
|
||||
|
||||
// Verify that there is still data to come.
|
||||
ReadTpmSts(&status);
|
||||
if ((status & expected_status_bits) != expected_status_bits) {
|
||||
LOG(ERROR) << "unexpected status 0x" << std::hex << status;
|
||||
delete[] payload;
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Now, read the last byte of the payload.
|
||||
FtdiReadReg(TPM_DATA_FIFO_REG, sizeof(uint8_t), payload + payload_size);
|
||||
|
||||
// Verify that 'data available' is not asseretd any more.
|
||||
ReadTpmSts(&status);
|
||||
if ((status & expected_status_bits) != stsValid) {
|
||||
LOG(ERROR) << "unexpected status 0x" << std::hex << status;
|
||||
delete[] payload;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = std::string(data_header, sizeof(data_header)) +
|
||||
std::string(reinterpret_cast<char*>(payload), payload_size + 1);
|
||||
|
||||
/* Move the TPM back to idle state. */
|
||||
WriteTpmSts(commandReady);
|
||||
|
||||
delete[] payload;
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace trunks
|
Loading…
Add table
Add a link
Reference in a new issue