Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
SPITIS_TPM20.cpp
- Committer:
- LordOfDorks
- Date:
- 2015-04-11
- Revision:
- 3:4b9ad18eae02
- Parent:
- 2:526bf792254d
File content as of revision 3:4b9ad18eae02:
/* mbed TCG SPI TPM 2.0 TIS 1.3 driver, * Copyright (c) 2015, Microsoft Coprporation Inc. * by Stefan Thom (LordOfDorks) StefanTh@Microsoft.com, Stefan@ThomsR.Us * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "SPITIS_TPM20.h" TIS_TPM20::TIS_RESULT TIS_TPM20::InitializeTis() { TIS_RESULT result = TIS_SESSION_RESULT_COMPLETE; uint8_t dataRegister[sizeof(m_intfCabability)] = {0}; if(RequestLocality(TIS_LOCALITY_0) == false) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } // Read the TIS capabilities if(ReadRegister(TIS_INTF_CAPABILITY_REGISTER, dataRegister, sizeof(dataRegister)) == false) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } m_intfCabability = BYTE_ARRAY_TO_LEUINT32(dataRegister); #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.InitializeTis.IntfCapability = 0x%08x\n\r", m_intfCabability); #endif // Check mandatory 0 bits to see if we read a valid register if(m_intfCabability & 0x0FFFF800) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } // If the TIS interface has a fixed burst count use that number instead of asking the TPM over and over and save cycles m_fixedBurstCount = (m_intfCabability & TIS_INTF_CAPPABILITY_BURST_COUNT_STATIC); #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.InitializeTis.FixedBurstCount = %s\n\r", m_fixedBurstCount ? "YES" : "NO"); #endif if((m_intfCabability & TIS_INTF_CAPPABILITY_DATA_TRANSFER_SIZE_SUPPORT_MASK) == TIS_INTF_CAPPABILITY_DATA_TRANSFER_SIZE_SUPPORT_8B) { m_maxBurstCount = 8; } else if((m_intfCabability & TIS_INTF_CAPPABILITY_DATA_TRANSFER_SIZE_SUPPORT_MASK) == TIS_INTF_CAPPABILITY_DATA_TRANSFER_SIZE_SUPPORT_32B) { m_maxBurstCount = 32; } else if ((m_intfCabability & TIS_INTF_CAPPABILITY_DATA_TRANSFER_SIZE_SUPPORT_MASK) == TIS_INTF_CAPPABILITY_DATA_TRANSFER_SIZE_SUPPORT_64B) { m_maxBurstCount = 64; } #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.InitializeTis.MaxBurstCount = %d\n\r", m_maxBurstCount); #endif result = TIS_SESSION_RESULT_COMPLETE; Cleanup: ReleaseLocality(); return result; } TIS_TPM20::TIS_RESULT TIS_TPM20::StartSession( TIS_TPM20::TIS_LOCALITY locality ) { TIS_RESULT result = TIS_SESSION_RESULT_COMPLETE; // Is the driver busy? if(m_locality != TIS_NOT_IN_USE) { #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.Schedule: TIS busy at locality %d\n\r", m_locality); #endif // Can we preemt? if(m_locality < locality) { result = TIS_SESSION_RESULT_OCCUPIED; goto Cleanup; } else { result = TIS_SESSION_RESULT_PREEMPTED; goto Cleanup; } } // Request the locality if(!RequestLocality(locality)) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } Cleanup: return result; } TIS_TPM20::TIS_RESULT TIS_TPM20::EndSession() { if(m_locality != TIS_NOT_IN_USE) { // Release the locality again #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.ReleaseLocality\r\n"); #endif if(!ReleaseLocality()) { return TIS_SESSION_RESULT_FAILED; } } return TIS_SESSION_RESULT_COMPLETE; } TIS_TPM20::TIS_RESULT TIS_TPM20::AbortCommand() { TIS_RESULT result = TIS_SESSION_RESULT_COMPLETE; uint8_t tisStatus = TIS_STS_COMMAND_READY; #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.AbortCommand\n\r"); #endif if((!WriteRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) || (!ReadRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) || ((tisStatus & TIS_STS_COMMAND_READY) == 0)) { result = TIS_SESSION_RESULT_FAILED; } return result; } TIS_TPM20::TIS_RESULT TIS_TPM20::SendCommand( uint8_t* pbCmd, uint32_t cbCmd, uint8_t* pbTrailingData, uint32_t cbTrailingData) { TIS_RESULT result = TIS_SESSION_RESULT_COMPLETE; uint8_t tisStatus = 0; uint16_t burstCount = 0; uint32_t index = 0; // In all TPM, a buffer size of 1,024 octets is allowed. if(cbTrailingData > 1024) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } #ifndef TPM_TIS_NO_COMMAND_FILTERING // Apply command filtering if(ApplyFilter(m_locality, pbCmd, cbCmd) == TIS_SESSION_RESULT_FILTERED) { #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.Schedule: Command filtered\n\r"); #endif result = TIS_SESSION_RESULT_FILTERED; goto Cleanup; } #endif #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.Command: "); for(uint32_t n = 0; n < cbCmd; n++) printf("%02x ", pbCmd[n]); printf("\n\r"); #endif // Make sure the TPM is ready for a command do { tisStatus = TIS_STS_COMMAND_READY; if(!WriteRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } if(!ReadRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } } while((tisStatus & TIS_STS_COMMAND_READY) == 0); #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.ReadyForCommand\n\r"); #endif // Submit Command do { uint16_t iteration = 0; if((burstCount = GetBurstCount()) == 0) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } // Assemble the buffer for transmission iteration = min((cbCmd - index), min(burstCount, TIS_MAX_HW_FRAME_SIZE)); #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.SendingBurst = %d\r\n", iteration); #endif if(!WriteRegister(TIS_DATA_FIFO, &pbCmd[index], iteration)) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } // Update the index index += iteration; } while((cbCmd - index) > 0); // Submit trailing data if there is any if((pbTrailingData != NULL) && (cbTrailingData != 0)) { index = 0; do { uint16_t iteration = 0; if((burstCount = GetBurstCount()) == 0) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } // Assemble the buffer for transmission iteration = min((cbTrailingData - index), min(burstCount, TIS_MAX_HW_FRAME_SIZE)); #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.SendingBurst = %d\r\n", iteration); #endif if(!WriteRegister(TIS_DATA_FIFO, &pbTrailingData[index], iteration)) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } // Update the index index += iteration; } while((cbTrailingData - index) > 0); } // Command complete? if((!ReadRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) || (!(tisStatus & TIS_STS_VALID) || (tisStatus & TIS_STS_DATA_EXPECT))) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.CommandComplete\n\r"); #endif // Arm the Interrupt if(!TpmInteruptOn(TIS_INT_ENABLE_DATA_AVAILABLE_INT_ENABLE)) { goto Cleanup; } // Kick the command off tisStatus = TIS_STS_GO; if(!WriteRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.Go."); #endif result = TIS_SESSION_RESULT_COMPLETE; Cleanup: if(result != TIS_SESSION_RESULT_COMPLETE) { AbortCommand(); } return result; } TIS_TPM20::TIS_RESULT TIS_TPM20::RetrieveResponse( uint8_t* pbRsp, uint32_t cbRsp, uint32_t* pcbRsp ) { TIS_RESULT result = TIS_SESSION_RESULT_COMPLETE; uint16_t burstCount = 0; uint32_t index = 0; uint32_t rspSize = 0; if(m_interrupt != 0) { #ifdef TPM_TIS_DEBUG_OUTPUT printf("."); #endif result = TIS_SESSION_RESULT_PENDING; return result; } #ifdef TPM_TIS_DEBUG_OUTPUT printf("IRQ!\r\n"); #endif // Disarm the Interrupt if(!TpmInteruptOn(0)) { goto Cleanup; } // Get the response header from the TPM if(((burstCount = GetBurstCount()) == 0) || (burstCount < (sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t))) || (!ReadRegister(TIS_DATA_FIFO, &pbRsp[index], (sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t))))) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } index += (sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t)); rspSize = BYTE_ARRAY_TO_UINT32(&pbRsp[sizeof(uint16_t)]); #ifdef TPM_TIS_DEBUG_OUTPUT printf("Tis.ResponseSize = %d\r\n", rspSize); #endif if(min(rspSize, cbRsp) > index) { do { // Calculate the response iteration size uint16_t iteration = min((rspSize - index), (cbRsp - index)); if((burstCount = GetBurstCount()) == 0) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } iteration = min(iteration, burstCount); #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.ReceivingBurst = %d\r\n", iteration); #endif // Read the data for this iteration if(!ReadRegister(TIS_DATA_FIFO, &pbRsp[index], iteration)) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } index += iteration; } while(index < min(rspSize, cbRsp)); } *pcbRsp = index; #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.Response: "); for(uint32_t n = 0; n < *pcbRsp; n++) printf("%02x ", pbRsp[n]); printf("\n\r"); #endif result = TIS_SESSION_RESULT_COMPLETE; Cleanup: if((result != TIS_SESSION_RESULT_COMPLETE) && (result != TIS_SESSION_RESULT_PENDING)) { AbortCommand(); } return result; } bool TIS_TPM20::FullDuplex( bool readCycle, uint16_t reg, uint8_t* pbBuffer, uint16_t cbBuffer ) { bool result = false; uint8_t dataByteIn; uint8_t dataByteOut; // Lock the bus for this operation m_chipSelect = 0; // Send the TIS header uint32_t tisHdr = TIS_HEADER(m_locality, readCycle, reg, cbBuffer); #ifdef TPM_TIS_INTERFACE_DEBUG_OUTPUT printf("TIS(LC:%d,RG:%02x,SZ:%02x,%s):", m_locality, reg, cbBuffer, (readCycle) ? "RD" : "WR"); #endif for(uint8_t n = 0; n < sizeof(tisHdr); n++) { dataByteOut = tisHdr >> (8 * (3 - n)); dataByteIn = m_spi.write(dataByteOut); //#ifdef TPM_TIS_INTERFACE_DEBUG_OUTPUT // if(n < (sizeof(tisHdr) - 1)) printf("%02x ", dataByteOut); // else printf("%02x", dataByteOut); //#endif } // The last bit we read full duplex is the first wait state indicator int16_t waitCycleRetry = 100; while(!(dataByteIn & 0x01)) { #ifdef TPM_TIS_INTERFACE_DEBUG_OUTPUT printf("."); #endif // Read the next byte to see is we still have to wait if((dataByteIn = m_spi.write(0x00)) == 0x01) { break; } // Check the timeout if(waitCycleRetry-- <= 0) { result = false; goto Cleanup; } } // Full duplex the payload for(uint8_t n = 0; n < cbBuffer; n++) { dataByteOut = (readCycle) ? 0x00 : pbBuffer[n]; dataByteIn = m_spi.write(dataByteOut); if(readCycle) pbBuffer[n] = dataByteIn; #ifdef TPM_TIS_INTERFACE_DEBUG_OUTPUT printf("%02x ", (readCycle) ? dataByteIn : dataByteOut); #endif } result = true; Cleanup: #ifdef TPM_TIS_INTERFACE_DEBUG_OUTPUT printf("\r\n"); #endif // Make sure to release the bus before we leave m_chipSelect = 1; return result; } uint16_t TIS_TPM20::GetBurstCount() { if(m_fixedBurstCount) { return m_maxBurstCount; } else { uint16_t burstCount = 0; uint8_t dataBytes[sizeof(uint16_t)] = {0}; if(!ReadRegister(TIS_STS_BURSTCOUNT_REGISTER, dataBytes, sizeof(dataBytes))) { return 0; } burstCount = min(BYTE_ARRAY_TO_LEUINT16(dataBytes), m_maxBurstCount); #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.BurstCount = %d\r\n", burstCount); #endif return burstCount; } } bool TIS_TPM20::RequestLocality(TIS_TPM20::TIS_LOCALITY locality) { m_locality = locality; for(uint8_t n = 0; n < 100; n++) { uint8_t dataByte = 0; // Read a valid access register. Turns out, it may take a couple of times if the TPM was sleeping do { dataByte = 0; if(!ReadRegister(TIS_ACCESS_REGISTER, &dataByte, sizeof(dataByte))) { goto Cleanup; } // First time we hit that, the TPM has to wake up give it some time if(!(dataByte & TIS_ACCESS_VALID)) { wait_us(5000); } } while(!(dataByte & TIS_ACCESS_VALID)); // If we have the locality we are done if(dataByte & TIS_ACCESS_ACTIVE_LOCALITY) { #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.LocalityAquired\n\r"); #endif return true; } // Request the locality dataByte = TIS_ACCESS_REQUEST_USE; if(!WriteRegister(TIS_ACCESS_REGISTER, &dataByte, sizeof(dataByte))) { goto Cleanup; } } m_locality = TIS_NOT_IN_USE; #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.LocalityRequest = FAILED\n\r"); #endif Cleanup: return false; } bool TIS_TPM20::ReleaseLocality() { for(uint8_t n = 0; n < 100; n++) { uint8_t dataByte = 0; // Read a valid access register. Turns out, it may take a couple of times if the TPM was sleeping do { if(!ReadRegister(TIS_ACCESS_REGISTER, &dataByte, sizeof(dataByte))) { break; } // First time we hit that, the TPM has to wake up give it some time if(!(dataByte & TIS_ACCESS_VALID)) { wait_us(5000); } } while(!(dataByte & TIS_ACCESS_VALID)); // If we don't have the locality we are done if(!(dataByte & TIS_ACCESS_ACTIVE_LOCALITY)) { #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.LocalityReleased\n\r"); #endif m_locality = TIS_NOT_IN_USE; return true; } // Drop the locality dataByte = TIS_ACCESS_ACTIVE_LOCALITY; if(!WriteRegister(TIS_ACCESS_REGISTER, &dataByte, sizeof(dataByte))) { break; } } #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.LocalityReleased = FAILED\n\r"); #endif return false; } bool TIS_TPM20::TpmInteruptOn(uint32_t intEnable) { uint32_t int_enable = TIS_INT_ENABLE_TYPE_POLARITY_LOW; uint8_t dataRegister[sizeof(uint32_t)] = {0}; uint8_t dataByte = 0; if(intEnable == 0) { // Reset the interrupt. Turns out the TPM needs some time to actually do this let me tell you: // That was a very interesting bug hunt! What we end up with here is the following - We reset // the interrupts now and don't look at them. While the TPM actually resets the line, we use // that time to send the next command but before interrupts are turned on for the next command // execution we have to wait until the line is back up again (see below). dataByte = TIS_INT_STATUS_RESET_ALL; if(!WriteRegister(TIS_INT_STATUS_REGISTER, &dataByte, sizeof(dataByte))) { return false; } } else { // Make sure the interrupt is really, really clear before we turn it back on. The TPM takes // around 5-10ms to transition the line back high. #ifdef TPM_TIS_DEBUG_OUTPUT if(intEnable == 0) printf("TIS.InteruptStillActive\n\r"); #endif while(m_interrupt == 0) { wait_us(1000); } int_enable |= (uint32_t)TIS_INT_ENABLE_GLOBAL_INT_ENABLE | intEnable; } LEUINT32_TO_BYTE_ARRAY(int_enable, dataRegister); #ifdef TPM_TIS_DEBUG_OUTPUT if(intEnable) printf("TIS.TpmInterupt = 0x%08x\n\r", intEnable); else printf("TIS.TpmInterupt = OFF\n\r"); #endif return(WriteRegister(TIS_INT_ENABLE_REGISTER, dataRegister, sizeof(dataRegister))); }