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-07
- Revision:
- 2:526bf792254d
- Parent:
- 1:fd0a59e55a85
- Child:
- 3:4b9ad18eae02
File content as of revision 2:526bf792254d:
/* 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" #include "TPM20Tables.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 = LE_BYTEARRAY_TO_UINT32(dataRegister, 0); #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; } #ifndef TPM_TPM_TIS_NO_COMMAND_FILTERING TIS_TPM20::TIS_RESULT TIS_TPM20::ApplyFilter(TIS_TPM20::TIS_LOCALITY locality, uint8_t* pbCmd, uint32_t cbCmd) { uint32_t cursor = sizeof(uint16_t) + sizeof(uint32_t); uint32_t ordinal = BE_BYTEARRAY_TO_UINT32(pbCmd, cursor); int32_t handleCount = -1; // First filter ordinals based on locality if(locality == TIS_LOCALITY_0) { switch(ordinal) { // We will not allow certain from locality 0 - aka the external TPM channel case TPM_CC_ChangeEPS: case TPM_CC_ChangePPS: case TPM_CC_Clear: case TPM_CC_ClearControl: case TPM_CC_ClockSet: case TPM_CC_PCR_Allocate: case TPM_CC_PCR_SetAuthPolicy: case TPM_CC_Shutdown: return TIS_SESSION_RESULT_FILTERED; default: break; } } // Move on the and lets look at the handle filter cursor += sizeof(ordinal); // Lookup the number of handles in the command for(uint32_t n = 0; s_ccAttr[n].commandIndex != 0x0000 ;n++) { if(s_ccAttr[n].commandIndex == ordinal) { handleCount = s_ccAttr[n].cHandles; break; } } // Easy elimination of invalid cases if((handleCount < 0) || (handleCount > 3) || (cbCmd < (cursor + (sizeof(uint32_t) * handleCount)))) { return TIS_SESSION_RESULT_FILTERED; } // Read the handles from the command and see if they are allowed for(uint32_t n = 0 ;n < handleCount; n++) { uint32_t objectHandle = BE_BYTEARRAY_TO_UINT32(pbCmd, cursor); cursor += sizeof(objectHandle); if(locality == TIS_LOCALITY_0) { switch(objectHandle) { // We will not allow the platform entity to be used from locality 0 case TPM_RH_PLATFORM: return TIS_SESSION_RESULT_FILTERED; default: break; } } } return TIS_SESSION_RESULT_COMPLETE; } #endif TIS_TPM20::TIS_RESULT TIS_TPM20::SendCommand( uint8_t* pbCmd, uint32_t cbCmd, TIS_TPM20::TIS_LOCALITY locality) { TIS_RESULT result = TIS_SESSION_RESULT_COMPLETE; uint8_t tisStatus = 0; uint16_t burstCount = 0; uint32_t index = 0; // 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; } } #ifndef TPM_TIS_NO_COMMAND_FILTERING // Apply command filtering if(ApplyFilter(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 // Assert the locality if(!RequestLocality(locality)) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } // Make sure the TPM is ready for a command if(!ReadRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } // Get TPM ready if((tisStatus & TIS_STS_COMMAND_READY) == 0) { if(!AbortCommand()) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } } #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); // 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; uint8_t tisStatus = 0; 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; } if((!ReadRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) || (!(tisStatus & TIS_STS_VALID) || !(tisStatus & TIS_STS_DATA_AVAIL))) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.DataAvailable\r\n"); #endif // 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 = BE_BYTEARRAY_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 { uint16_t iteration = 0; // Check to make sure the TPM has still data for us if((!ReadRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) || (!(tisStatus & TIS_STS_VALID) || !(tisStatus & TIS_STS_DATA_AVAIL)) || ((burstCount = GetBurstCount()) == 0)) { result = TIS_SESSION_RESULT_FAILED; goto Cleanup; } iteration = min((rspSize - index), min((cbRsp - index), min(burstCount, TIS_MAX_HW_FRAME_SIZE))); #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)) { 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(); } // Release the locality again #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.ReleaseLocality\r\n"); #endif ReleaseLocality(); return result; } uint32_t TIS_TPM20::ParseResponseHeader(uint8_t* pbRsp, uint32_t cbRsp, uint16_t* rspTag, uint32_t* rspSize) { uint32_t rspResponseCode = 0; uint32_t cursor = 0; // Check that the response header is well formatted if(cbRsp < (sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t))) { #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.ResponseHdr: Too short = 0x%08x\n\r", cbRsp); #endif rspResponseCode = TPM_RC_FAILURE; goto Cleanup; } // Read the header components *rspTag = BE_BYTEARRAY_TO_UINT16(pbRsp, cursor); cursor += sizeof(*rspTag); *rspSize = BE_BYTEARRAY_TO_UINT32(pbRsp, cursor); cursor += sizeof(*rspSize); rspResponseCode = BE_BYTEARRAY_TO_UINT32(pbRsp, cursor); cursor += sizeof(rspResponseCode); // Check the components if(((*rspTag != TPM_ST_NO_SESSIONS) && (*rspTag != TPM_ST_SESSIONS)) || (*rspSize != cbRsp)) { #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.ResponseHdr: Tag=0x%04x, Len=0x%08x, RC=0x%08x\n\r", *rspTag, *rspSize, rspResponseCode); #endif rspResponseCode = TPM_RC_FAILURE; goto Cleanup; } Cleanup: return rspResponseCode; } 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(LE_BYTEARRAY_TO_UINT16(dataBytes , 0), 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) { int_enable |= (uint32_t)TIS_INT_ENABLE_GLOBAL_INT_ENABLE | intEnable; } // Read the Interrupt state ReadRegister(TIS_INT_STATUS_REGISTER, &dataByte, sizeof(dataByte)); dataByte = TIS_INT_STATUS_RESET_ALL; LE_UINT32_TO_BYTEARRAY(int_enable, dataRegister, 0); #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.ArmTpmInterupt = 0x%08x\n\r", intEnable); #endif return ((WriteRegister(TIS_INT_STATUS_REGISTER, &dataByte, sizeof(dataByte))) && (WriteRegister(TIS_INT_ENABLE_REGISTER, dataRegister, sizeof(dataRegister)))); } bool TIS_TPM20::AbortCommand() { uint8_t tisStatus = TIS_STS_COMMAND_READY; #ifdef TPM_TIS_DEBUG_OUTPUT printf("TIS.AbortCommand\n\r"); #endif return((WriteRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) && (ReadRegister(TIS_STS_REGISTER, &tisStatus, sizeof(tisStatus))) && ((tisStatus & TIS_STS_COMMAND_READY) != 0)); }