/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/i2c/tpmdd.C $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2011,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ /* 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. */ /* */ /* IBM_PROLOG_END_TAG */ /** * @file tpmdd.C * * @brief Implementation of the TPM device driver, * which will access the TPM within the * system via the I2C device driver * */ // ---------------------------------------------- // Includes // ---------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for TRACE_ERR_FMT, TRACE_ERR_ARGS #include "tpmdd.H" #include "errlud_i2c.H" // ---------------------------------------------- // Globals // ---------------------------------------------- mutex_t g_tpmMutex = MUTEX_INITIALIZER; // ---------------------------------------------- // Trace definitions // ---------------------------------------------- trace_desc_t* g_trac_tpmdd = NULL; TRAC_INIT( & g_trac_tpmdd, TPMDD_COMP_NAME, KILOBYTE ); // Easy macro replace for unit testing //#define TRACUCOMP(args...) TRACFCOMP(args) #define TRACUCOMP(args...) // ---------------------------------------------- // Defines // ---------------------------------------------- // ---------------------------------------------- namespace TPMDD { static const size_t MAX_BYTE_ADDR = 2; static const size_t TPM_MAX_RETRIES = 5; static const size_t TPM_MAX_RETRY_DELAY_NS = (250 * NS_PER_MSEC); // Register the perform Op with the routing code for TPM DEVICE_REGISTER_ROUTE(DeviceFW::READ, DeviceFW::PRESENT, TARGETING::TYPE_TPM, tpmPresenceDetect); DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD, DeviceFW::TPM, TARGETING::TYPE_TPM, tpmPerformOp ); // ------------------------------------------------------------------ // tpmPerformOp // ------------------------------------------------------------------ errlHndl_t tpmPerformOp( DeviceFW::OperationType i_opType, TARGETING::Target * i_target, void * io_buffer, size_t & io_buflen, int64_t i_accessType, va_list i_args ) { errlHndl_t err = NULL; tpm_info_t tpmInfo; uint64_t commandSize = 0; bool unlock = false; tpm_locality_t locality = TPM_LOCALITY_0; tpmInfo.operation = static_cast (va_arg( i_args, uint64_t )); commandSize = va_arg( i_args, uint64_t ); locality = static_cast(va_arg( i_args, uint64_t )); TRACDCOMP( g_trac_tpmdd, ENTER_MRK"tpmPerformOp()" ); TRACUCOMP (g_trac_tpmdd, ENTER_MRK"tpmPerformOp(): " "i_opType=%d, operation=%d, loc=%d, buflen=%d, cmdlen=%d", (uint64_t) i_opType, tpmInfo.operation, locality, io_buflen, commandSize); do { // Read Attributes needed to complete the operation err = tpmReadAttributes( i_target, tpmInfo, locality); if( err ) { break; } // Ensure the TPM is enabled if (!tpmInfo.tpmEnabled) { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmPerformOp(): TPM requested not enabled!" "e/p/dA=%d/%d/0x%X, OP=%d", tpmInfo.engine, tpmInfo.port, tpmInfo.devAddr, tpmInfo.operation); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmPerformOp(): " "muxSelector=0x%X, muxPath=%s", tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_DISABLED_VIA_MRW * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_PERFORM_OP * @userdata1 TPM * @userdata2 Operation Type * @devdesc Invalid operation type. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_PERFORM_OP, TPM_DISABLED_VIA_MRW, TARGETING::get_huid(i_target), i_opType, true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); break; } // Lock to sequence operations mutex_lock( &g_tpmMutex ); unlock = true; // TPM_OP_READVENDORID operation // Only supported with a DeviceFW::READ operation if( TPMDD::TPM_OP_READVENDORID == tpmInfo.operation && DeviceFW::READ == i_opType) { if (io_buflen > 4) { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmPerformOp(): Operation Overflow! " "e/p/dA=%d/%d/0x%X, OP=%d, " "blen=%d", tpmInfo.engine, tpmInfo.port, tpmInfo.devAddr, tpmInfo.operation, io_buflen); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmPerformOp(): " "muxSelector=0x%X, muxPath=%s", tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_OVERFLOW_ERROR * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_PERFORM_OP * @userdata1 TPM * @userdata2[0-31] Operation * @userdata2[32-63] Buffer Length (in Bytes) * @devdesc TPM buffer length > 4 for read vendor op * @custdesc A problem occurred during the IPL of the * system: TPM buffer is too large. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_PERFORM_OP, TPM_OVERFLOW_ERROR, TARGETING::get_huid(i_target), TWO_UINT32_TO_UINT64( tpmInfo.operation, io_buflen ), true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); break; } // Set the offset for the vendor reg tpmInfo.offset = tpmInfo.vendorIdOffset; err = tpmRead( io_buffer, io_buflen, tpmInfo ); if ( err ) { break; } } // TPM_OP_TRANSMIT // Ignoring read/write type since transmit really does both anyway else if( TPMDD::TPM_OP_TRANSMIT == tpmInfo.operation ) { err = tpmTransmit( io_buffer, io_buflen, commandSize, tpmInfo ); if ( err ) { break; } #ifdef CONFIG_DRTM // TPM_OP_DRTMRESET } else if (TPMDD::TPM_OP_DRTMRESET == tpmInfo.operation ) { assert(locality == TPM_LOCALITY_4, "DRTMReset only available from locality 4, actual %d", locality); err = tpmDrtmReset(tpmInfo); if ( err ) { break; } #endif } else { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmPerformOp(): Invalid TPM Operation!" "e/p/dA=%d/%d/0x%X, OP=%d, Type=%d", tpmInfo.engine, tpmInfo.port, tpmInfo.devAddr, tpmInfo.operation, i_opType); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmPerformOp(): " "muxSelector=0x%X, muxPath=%s", tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_INVALID_OPERATION * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_PERFORM_OP * @userdata1 TPM * @userdata2 Operation Type * @devdesc Invalid operation type. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_PERFORM_OP, TPM_INVALID_OPERATION, TARGETING::get_huid(i_target), i_opType, true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); break; } } while( 0 ); if ( err != nullptr ) { // Add Security Registers to the error log SECUREBOOT::addSecurityRegistersToErrlog(err); } if( unlock ) { mutex_unlock( & g_tpmMutex ); } TRACDCOMP( g_trac_tpmdd, EXIT_MRK"tpmPerformOp() - %s", ((NULL == err) ? "No Error" : "With Error") ); return err; } // end tpmPerformOp //------------------------------------------------------------------- //tpmPresence //------------------------------------------------------------------- bool tpmPresence (TARGETING::Target* i_pTpm) { TRACFCOMP(g_trac_tpmdd, ENTER_MRK "tpmPresence: Attempting to detect TPM with HUID=0x%08X.", TARGETING::get_huid(i_pTpm)); assert(i_pTpm != nullptr, "BUG! Caller passed in nullptr for TPM target"); // Input target must have a TPM_INFO attribute (only applicable to TPM // targets), enforced by call to tpmReadAttributes errlHndl_t pError = nullptr; bool present = false; const auto forceTrace = false; // For debug const auto tpmRequired = TRUSTEDBOOT::isTpmRequired(); const auto verbose = tpmRequired || forceTrace; tpm_info_t tpmInfo; do { pError = tpmReadAttributes(i_pTpm, tpmInfo, TPM_LOCALITY_0); if(pError) { TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmPresence: Bug! Failed in call to tpmReadAttributes() for " "TPM with HUID=0x%08X. " TRACE_ERR_FMT, get_huid(i_pTpm), TRACE_ERR_ARGS(pError)); break; } // Ensure the TPM is enabled if (!tpmInfo.tpmEnabled) { // It is not an error condition for a TPM to be disabled in the // object model; in that case don't complain if the TPM is not // present. TRACFCOMP(g_trac_tpmdd,INFO_MRK "tpmPresence: TPM with HUID=0x%08X is defined " "in the object model blueprint but is flagged as " "disabled/ignored.", get_huid(i_pTpm)); break; } // Treat TPM as not present if it is being driven by a processor that is // not yet available via XSCOM. The remote processor's FSI accessible // I2C master does not have a path to the TPM, so defer discovery to // after the point when the SMP is established. if( tpmInfo.i2cTarget->getAttr() == TARGETING::TYPE_PROC) { const auto scomSwitches = tpmInfo.i2cTarget->getAttr< TARGETING::ATTR_SCOM_SWITCHES>(); if(!scomSwitches.useXscom) { TRACFCOMP(g_trac_tpmdd,INFO_MRK "tpmPresence: TPM with HUID=0x%08X is not " "accessible, as the proc that drives it (HUID 0x%08X) " "is not XSCOM accessible", get_huid(i_pTpm), get_huid(tpmInfo.i2cTarget)); break; } } // Verify the TPM is supported by this driver by reading and // comparing the vendor ID uint32_t vendorId = 0; const size_t vendorIdSize = sizeof(vendorId); // Set the offset for the vendor reg tpmInfo.offset = tpmInfo.vendorIdOffset; pError = tpmRead(&vendorId, vendorIdSize, tpmInfo, true /* silent */ ); if (pError) { if(verbose) { TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmPresence: tpmRead: Failed to read TPM vendor ID! " "TPM HUID=0x%08X, e/p/dA=%d/%d/0x%02X. " TRACE_ERR_FMT, TARGETING::get_huid(i_pTpm), tpmInfo.engine, tpmInfo.port, static_cast(tpmInfo.devAddr), TRACE_ERR_ARGS(pError)); } break; } else if ((TPMDD::TPM_VENDORID_MASK & vendorId) != tpmInfo.vendorId) { if(verbose) { TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmPresence: Sampled TPM vendor ID did not match expected " "vendor ID! " "TPM HUID=0x%08X, e/p/dA=%d/%d/0x%02X" "Actual vendor ID=0x%08X, expected vendor ID=0x%08X.", TARGETING::get_huid(i_pTpm), tpmInfo.engine, tpmInfo.port, static_cast(tpmInfo.devAddr), vendorId, tpmInfo.vendorId); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmPresence: " "muxSelector=0x%X, muxPath=%s", tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; } /*@ * @errortype * @moduleid TPMDD_TPMPRESENCE * @reasoncode TPM_RC_UNEXPECTED_VENDOR_ID * @userdata1[0:31] Expected vendor ID * @userdata1[32:63] Actual vendor ID * @userdata2 TPM HUID * @devdesc Unexpected vendor ID read from TPM * @custdesc Trusted boot problem detected */ pError = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_TPMPRESENCE, TPM_RC_UNEXPECTED_VENDOR_ID, TWO_UINT32_TO_UINT64(tpmInfo.vendorId,vendorId), get_huid(i_pTpm), ERRORLOG::ErrlEntry::NO_SW_CALLOUT); break; } // TPM Nuvoton Model 75x does not support the familyId, and requires // some additional setup for locality if (tpmInfo.model == TPM_MODEL_75x) { TRACFCOMP(g_trac_tpmdd,INFO_MRK "tpmPresence: TPM Detected! " "TPM HUID=0x%08X, e/p/dA=%d/%d/0x%02X, " "vendor ID=0x%08X (no family ID for model 75x)", TARGETING::get_huid(i_pTpm), tpmInfo.engine, tpmInfo.port, static_cast(tpmInfo.devAddr), vendorId); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, INFO_MRK"tpmPresence: " "muxSelector=0x%X, muxPath=%s", tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; // Commands to enable locality 0 uint8_t locality = 0; size_t locSize = 1; // Set the offset for the loc_sel reg tpmInfo.offset = TPM_REG_75x_LOC_SEL; pError = tpmWrite( &locality, locSize, tpmInfo); if (pError) { TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmPresence: Error on 1st cmd to set up Locality for 75x " "TPM HUID=0x%08X. Treat TPM as not present", TARGETING::get_huid(i_pTpm)); break; } uint8_t val = 0x02; size_t valSize = 1; // Set the offset for the loc_sel reg tpmInfo.offset = TPM_REG_75x_TPM_ACCESS; pError = tpmWrite( &val, valSize, tpmInfo); if (pError) { TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmPresence: Error on 2nd cmd to set up Locality for 75x " "TPM HUID=0x%08X. Treat TPM as not present", TARGETING::get_huid(i_pTpm)); break; } present = true; } else { // Verify the TPM is supported by this driver by reading and // comparing the family ID uint8_t familyId = 0; const size_t familyIdSize = sizeof(familyId); tpmInfo.offset = TPM_REG_65x_FAMILYID_OFFSET; pError = tpmRead(&familyId, familyIdSize, tpmInfo, true /* silent */); if (pError) { if(verbose) { TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmPresence: tpmRead: Failed to read TPM family ID! " "TPM HUID=0x%08X, e/p/dA=%d/%d/0x%02X" TRACE_ERR_FMT, TARGETING::get_huid(i_pTpm), tpmInfo.engine, tpmInfo.port, static_cast(tpmInfo.devAddr), TRACE_ERR_ARGS(pError)); // Printing mux info separately, if combined, // nothing is displayed char* l_muxPath = tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmPresence: " "muxSelector=0x%X, muxPath=%s", tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; } break; } else if ( (TPMDD::TPM_FAMILYID_MASK & familyId) != (TPMDD::TPM_FAMILYID_65x)) { if(verbose) { TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmPresence: Sampled family ID did not match expected " "family ID! " "TPM HUID=0x%08X, e/p/dA=%d/%d/0x%02X, " "actual family ID=0x%02X, expected family ID=0x%02X.", TARGETING::get_huid(i_pTpm), tpmInfo.engine, tpmInfo.port, static_cast(tpmInfo.devAddr), familyId, TPMDD::TPM_FAMILYID_65x); // Printing mux info separately, if combined, // nothing is displayed char* l_muxPath = tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmPresence: " "muxSelector=0x%X, muxPath=%s", tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; } /*@ * @errortype * @moduleid TPMDD_TPMPRESENCE * @reasoncode TPM_RC_UNEXPECTED_FAMILY_ID * @userdata1[24:31] Expected family ID * @userdata1[56:63] Actual family ID * @userdata2 TPM HUID * @devdesc Unexpected family ID read from TPM * @custdesc Trusted boot problem detected */ pError = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_TPMPRESENCE, TPM_RC_UNEXPECTED_FAMILY_ID, TWO_UINT32_TO_UINT64(TPMDD::TPM_FAMILYID_65x,familyId), get_huid(i_pTpm), ERRORLOG::ErrlEntry::NO_SW_CALLOUT); break; } else { TRACFCOMP(g_trac_tpmdd,INFO_MRK "tpmPresence: TPM Detected! " "TPM HUID=0x%08X, e/p/dA=%d/%d/0x%02X, " "vendor ID=0x%08X, family ID=0x%02X.", TARGETING::get_huid(i_pTpm), tpmInfo.engine, tpmInfo.port, static_cast(tpmInfo.devAddr), vendorId, familyId); // Printing mux info separately, if combined, // nothing is displayed char* l_muxPath = tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, INFO_MRK"tpmPresence: " "muxSelector=0x%X, muxPath=%s", tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; present = true; } } } while( 0 ); if(pError) { // If a TPM is required to boot the system, then escalate TPM // presence failure as an unrecoverable error log, and link its PLID to // a new log explicitly indicating the TPM was not detected properly. if(tpmRequired) { pError->collectTrace(TPMDD_COMP_NAME); pError->collectTrace(SECURE_COMP_NAME); pError->collectTrace(TRBOOT_COMP_NAME); pError->collectTrace(I2C_COMP_NAME); ERRORLOG::ErrlUserDetailsTarget(i_pTpm).addToLog(pError); const auto original_eid = pError->eid(); const auto original_plid = pError->plid(); pError->setSev(ERRORLOG::ERRL_SEV_UNRECOVERABLE); errlCommit(pError,TPMDD_COMP_ID); /*@ * @errortype * @moduleid TPMDD_TPMPRESENCE * @reasoncode TPM_RC_TPM_NOT_DETECTED * @userdata1 TPM HUID * @devdesc The system's "TPM Required" policy is set to * "TPM Required" and a TPM that was expected to be present was * not detected properly. The TPM in question will eventually * be flagged as TPM_UNUSABLE for redundancy calculations, * Possible causes: (1) absent or improperly seated TPM, * (2) TPM hardware failure, (3) firmware bug, (4) incorrect TPM * part, (5) I2C bus failure, (6) processor failure, (7) other * I2C device holding the I2C bus. See earlier error logs with * same PLID for additional details. * @custdesc A trusted platform module (TPM) that was expected * to be present was not detected properly */ pError = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_TPMPRESENCE, TPM_RC_TPM_NOT_DETECTED, get_huid(i_pTpm), 0, ERRORLOG::ErrlEntry::NO_SW_CALLOUT); pError->plid(original_plid); ERRORLOG::ErrlUserDetailsTarget(i_pTpm).addToLog(pError); TRACFCOMP(g_trac_tpmdd, ERR_MRK "tpmPresence: Due to Error eid=0x%.8X plid=0x%.8X involving " "TPM with HUID=0x%08X, committing Unrecoverable Error " "eid=0x%.8X with same plid=0x%.8X", original_eid, original_plid, TARGETING::get_huid(i_pTpm), pError->eid(), pError->plid()); // Hardware/Procedure callouts/trace should have been added to the // original log but the main HW/SW callouts/traces are replicated here // just in case. pError->addHwCallout(i_pTpm, HWAS::SRCI_PRIORITY_HIGH, HWAS::NO_DECONFIG, HWAS::GARD_NULL); pError->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_LOW); pError->collectTrace(TPMDD_COMP_NAME); pError->collectTrace(SECURE_COMP_NAME); pError->collectTrace(TRBOOT_COMP_NAME); pError->collectTrace(I2C_COMP_NAME); errlCommit(pError,TPMDD_COMP_ID); } else { delete pError; pError = nullptr; } } TRACFCOMP(g_trac_tpmdd, EXIT_MRK "tpmPresence: TPM with HUID=0x%08X, presence=%d", TARGETING::get_huid(i_pTpm), present); return present; } // ------------------------------------------------------------------ // tpmPresenceDetect // ------------------------------------------------------------------ errlHndl_t tpmPresenceDetect(DeviceFW::OperationType i_opType, TARGETING::Target* i_target, void* io_buffer, size_t& io_buflen, int64_t i_accessType, va_list i_args) { errlHndl_t err = NULL; if (unlikely(io_buflen < sizeof(bool))) { TRACFCOMP(g_trac_tpmdd, ERR_MRK "tpmPresenceDetect> Invalid data length: %d", io_buflen); /*@ * @errortype * @moduleid TPMDD_TPMPRESENCEDETECT * @reasoncode TPM_INVALID_OPERATION * @userdata1 Data Length * @devdesc presenceDetect> Invalid data length (!= 1 bytes) * @custdesc Problem occurred during TPM presence detection */ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_TPMPRESENCEDETECT, TPM_INVALID_OPERATION, TO_UINT64(io_buflen), true /*SW error*/); io_buflen = 0; } else { bool present = tpmPresence (i_target); memcpy(io_buffer, &present, sizeof(present)); io_buflen = sizeof(present); } return err; } // ------------------------------------------------------------------ // tpmRead // ------------------------------------------------------------------ errlHndl_t tpmRead ( void * o_buffer, size_t i_buflen, const tpm_info_t & i_tpmInfo, bool i_silent) { errlHndl_t err = NULL; errlHndl_t err_RETRY = NULL; uint8_t byteAddr[MAX_BYTE_ADDR]; size_t byteAddrSize = 0; TRACDCOMP( g_trac_tpmdd, ENTER_MRK"tpmRead()" ); do { TRACUCOMP( g_trac_tpmdd, "TPM READ START : Offset %.2X : Len %d", i_tpmInfo.offset, i_buflen ); err = tpmPrepareAddress( &byteAddr, byteAddrSize, i_tpmInfo ); if( err ) { break; } /***********************************************************/ /* Attempt read multiple times on fails */ /***********************************************************/ for (size_t retry = 0; retry <= TPM_MAX_RETRIES; retry++) { // Only write the byte address if we have data to write if( 0 != byteAddrSize ) { // Use the I2C OFFSET Interface for the READ err = deviceOp( DeviceFW::READ, i_tpmInfo.i2cTarget, o_buffer, i_buflen, DEVICE_I2C_ADDRESS_OFFSET( i_tpmInfo.port, i_tpmInfo.engine, i_tpmInfo.devAddr, byteAddrSize, reinterpret_cast(&byteAddr), i_tpmInfo.i2cMuxBusSelector, &(i_tpmInfo.i2cMuxPath) ) ); if( err ) { TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmRead(): I2C Read-Offset failed! " "e/p/dA=%d/%d/0x%X, OP=%d, " "offset=0x%X, aS=%d, len=%d", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, i_tpmInfo.offset, byteAddrSize, i_buflen); TRACFBIN(g_trac_tpmdd, "byteAddr[]", &byteAddr, byteAddrSize); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmRead(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; // Don't break here -- error handled below } } else { // Do the actual read via I2C err = deviceOp( DeviceFW::READ, i_tpmInfo.i2cTarget, o_buffer, i_buflen, DEVICE_I2C_ADDRESS( i_tpmInfo.port, i_tpmInfo.engine, i_tpmInfo.devAddr, i_tpmInfo.i2cMuxBusSelector, &(i_tpmInfo.i2cMuxPath) ) ); if( err ) { TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmRead(): I2C Read failed! " "e/p/dA=%d/%d/0x%X, OP=%d, " "len=%d", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, i_buflen); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmRead(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; // Don't break here -- error handled below } } if ( err == NULL ) { // Operation completed successfully // break from retry loop break; } else // Handle error { // If op will be attempted again: save log and continue if ( retry < TPM_MAX_RETRIES ) { // Only save original RETRY error if ( err_RETRY == NULL ) { // Save original RETRY error err_RETRY = err; TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmRead(): Error! " "e/p/dA=%d/%d/0x%X, OP=%d, " "rc=0x%X, eid=0x%X, " "retry/MAX=%d/%d. Save error and retry", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, err_RETRY->reasonCode(), err_RETRY->eid(), retry, TPM_MAX_RETRIES); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmRead(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; err_RETRY->collectTrace(TPMDD_COMP_NAME); } else { // Add data to original error TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmRead(): Another Error! " "e/p/dA=%d/%d/0x%X, OP=%d, " "rc=0x%X, eid=0x%X, " "plid=0x%X, retry/MAX=%d/%d. " "Delete error and retry", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, err->reasonCode(), err->eid(), err->plid(), retry, TPM_MAX_RETRIES); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmRead(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; ERRORLOG::ErrlUserDetailsString( "Another ERROR found") .addToLog(err_RETRY); // Delete this new error delete err; err = NULL; } // Add 250ms delay before retry: TRACFCOMP( g_trac_tpmdd, "tpmRead(): sleep for 250ms before retry"); nanosleep(0, TPM_MAX_RETRY_DELAY_NS); // continue to retry continue; } else // no more retries: trace and break { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmRead(): No More Retries! " "e/p/dA=%d/%d/0x%X, OP=%d, " "Error rc=0x%X, eid=%d, " "retry/MAX=%d/%d. Returning Error", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, err->reasonCode(), err->eid(), retry, TPM_MAX_RETRIES); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmRead(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; err->collectTrace(TPMDD_COMP_NAME); // break from retry loop break; } } } // end of retry loop // Handle saved error, if any if (err_RETRY) { if (err) { if (!i_silent) { // commit original RETRY error with new err PLID err_RETRY->plid(err->plid()); TRACFCOMP(g_trac_tpmdd, "tpmRead(): Committing saved RETRY " "err eid=0x%X with plid of returned err: 0x%X", err_RETRY->eid(), err_RETRY->plid()); ERRORLOG::ErrlUserDetailsTarget(i_tpmInfo.i2cTarget) .addToLog(err_RETRY); errlCommit(err_RETRY, TPMDD_COMP_ID); } } else { // Since we eventually succeeded, delete original RETRY error TRACFCOMP(g_trac_tpmdd, "tpmRead(): Op successful, " "deleting saved RETRY err eid=0x%X, plid=0x%X", err_RETRY->eid(), err_RETRY->plid()); delete err_RETRY; err_RETRY = NULL; } } TRACUCOMP( g_trac_tpmdd, "TPM READ END : Offset %.2X : Len %d : %016llx", i_tpmInfo.offset, i_buflen, *(reinterpret_cast(o_buffer)) ); } while( 0 ); TRACDCOMP( g_trac_tpmdd, EXIT_MRK"tpmRead()" ); return err; } // end tpmRead // ------------------------------------------------------------------ // tpmWrite // ------------------------------------------------------------------ errlHndl_t tpmWrite ( void * i_buffer, size_t i_buflen, const tpm_info_t & i_tpmInfo ) { errlHndl_t err = NULL; errlHndl_t err_RETRY = NULL; uint8_t byteAddr[MAX_BYTE_ADDR]; size_t byteAddrSize = 0; TRACDCOMP( g_trac_tpmdd, ENTER_MRK"tpmWrite()" ); do { TRACUCOMP( g_trac_tpmdd, "TPM WRITE START : Offset %.2X : Len %d : %016llx", i_tpmInfo.offset, i_buflen, *(reinterpret_cast(i_buffer)) ); err = tpmPrepareAddress( &byteAddr, byteAddrSize, i_tpmInfo ); if( err ) { break; } /***********************************************************/ /* Attempt write multiple times ONLY fails */ /***********************************************************/ for (size_t retry = 0; retry <= TPM_MAX_RETRIES; retry++) { // Only write the byte address if we have data to write if( 0 != byteAddrSize ) { // Use the I2C OFFSET Interface for the WRITE err = deviceOp( DeviceFW::WRITE, i_tpmInfo.i2cTarget, i_buffer, i_buflen, DEVICE_I2C_ADDRESS_OFFSET( i_tpmInfo.port, i_tpmInfo.engine, i_tpmInfo.devAddr, byteAddrSize, reinterpret_cast(&byteAddr), i_tpmInfo.i2cMuxBusSelector, &(i_tpmInfo.i2cMuxPath) ) ); if( err ) { TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWrite(): I2C Write-Offset! " "e/p/dA=%d/%d/0x%X, OP=%d, " "offset=0x%X, aS=%d, len=%d", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, i_tpmInfo.offset, byteAddrSize, i_buflen); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWrite(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; TRACFBIN(g_trac_tpmdd, "byteAddr[]", &byteAddr, byteAddrSize); // Don't break here -- error handled below } } else { // Do the actual write via I2C err = deviceOp( DeviceFW::WRITE, i_tpmInfo.i2cTarget, i_buffer, i_buflen, DEVICE_I2C_ADDRESS( i_tpmInfo.port, i_tpmInfo.engine, i_tpmInfo.devAddr, i_tpmInfo.i2cMuxBusSelector, &(i_tpmInfo.i2cMuxPath) ) ); if( err ) { TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWrite(): I2C Write failed! " "e/p/dA=%d/%d/0x%X, OP=%d, " "len=%d", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, i_buflen); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWrite(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; // Don't break here -- error handled below } } if ( err == NULL ) { // Operation completed successfully // break from retry loop break; } else // Handle error { // If op will be attempted again: save log and continue if ( retry < TPM_MAX_RETRIES ) { // Only save original RETRY error if ( err_RETRY == NULL ) { // Save original RETRY error err_RETRY = err; TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmWrite(): RETRY Error! " "e/p/dA=%d/%d/0x%X, OP=%d, " "rc=0x%X, eid=0x%X, " "retry/MAX=%d/%d. Save error and retry", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, err_RETRY->reasonCode(), err_RETRY->eid(), retry, TPM_MAX_RETRIES); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWrite(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; err_RETRY->collectTrace(TPMDD_COMP_NAME); } else { // Add data to original RETRY error TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmWrite(): Another RETRY Error! " "e/p/dA=%d/%d/0x%X, OP=%d, " "rc=0x%X, eid=0x%X " "plid=0x%X, retry/MAX=%d/%d. " "Delete error and retry", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, err->reasonCode(), err->eid(), err->plid(), retry, TPM_MAX_RETRIES); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWrite(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; ERRORLOG::ErrlUserDetailsString( "Another ERROR found") .addToLog(err_RETRY); // Delete this new error delete err; err = NULL; } // Add 250ms delay before retry: TRACFCOMP( g_trac_tpmdd, "tpmWrite(): sleep for 250ms before retry"); nanosleep(0, TPM_MAX_RETRY_DELAY_NS); // continue to retry continue; } else // no more retries: trace and break { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmWrite(): No More Retries! " "e/p/dA=%d/%d/0x%X, OP=%d, " "Error rc=0x%X, eid=%d, " "retry/MAX=%d/%d. Returning Error", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, err->reasonCode(), err->eid(), retry, TPM_MAX_RETRIES); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWrite(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; err->collectTrace(TPMDD_COMP_NAME); // break from retry loop break; } } } // end of retry loop // Handle saved RETRY error, if any if (err_RETRY) { if (err) { // commit original RETRY error with new err PLID err_RETRY->plid(err->plid()); TRACFCOMP(g_trac_tpmdd, "tpmWrite(): Committing saved RETRY " "err eid=0x%X with plid of returned err: 0x%X", err_RETRY->eid(), err_RETRY->plid()); ERRORLOG::ErrlUserDetailsTarget(i_tpmInfo.i2cTarget) .addToLog(err_RETRY); errlCommit(err_RETRY, TPMDD_COMP_ID); } else { // Since we eventually succeeded, delete original RETRY error TRACFCOMP(g_trac_tpmdd, "tpmWrite(): Op successful, " "deleting saved RETRY err eid=0x%X, plid=0x%X", err_RETRY->eid(), err_RETRY->plid()); delete err_RETRY; err_RETRY = NULL; } } TRACSCOMP( g_trac_tpmdd, "TPM WRITE END : Offset %.2X : Len %d", i_tpmInfo.offset, i_buflen); } while( 0 ); TRACDCOMP( g_trac_tpmdd, EXIT_MRK"tpmWrite()" ); return err; } // end tpmWrite // ------------------------------------------------------------------ // tpmTransmit // ------------------------------------------------------------------ errlHndl_t tpmTransmit ( void * io_buffer, size_t & io_buflen, size_t i_commandlen, const tpm_info_t & i_tpmInfo ) { errlHndl_t err = NULL; bool isReady = false; TRACDCOMP( g_trac_tpmdd, ENTER_MRK"tpmTransmit()" ); do { TRACUCOMP( g_trac_tpmdd, "TPM TRANSMIT START : BufLen %d : CmdLen %d : %016llx", io_buflen, i_commandlen, *(reinterpret_cast(io_buffer)) ); err = tpmIsCommandReady(i_tpmInfo, isReady); if( err ) { break; } if (!isReady) { err = tpmWriteCommandReady(i_tpmInfo); if( err ) { break; } } // Verify the TPM is ready to receive our command err = tpmPollForCommandReady(i_tpmInfo); if( err ) { break; } // Write the command into the TPM FIFO err = tpmWriteFifo(i_tpmInfo, io_buffer, i_commandlen); if( err ) { break; } err = tpmWriteTpmGo(i_tpmInfo); if( err ) { break; } // Read the response from the TPM FIFO err = tpmReadFifo(i_tpmInfo, io_buffer, io_buflen); if( err ) { break; } err = tpmWriteCommandReady(i_tpmInfo); if( err ) { break; } } while( 0 ); TRACUCOMP( g_trac_tpmdd, "TPM TRANSMIT END : BufLen %d : CmdLen %d : %016llx", io_buflen, i_commandlen, *(reinterpret_cast(io_buffer)) ); TRACDCOMP( g_trac_tpmdd, EXIT_MRK"tpmTransmit()" ); return err; } // end tpmTransmit #ifdef CONFIG_DRTM // ------------------------------------------------------------------ // tpmDrtmReset // ------------------------------------------------------------------ errlHndl_t tpmDrtmReset (tpm_info_t i_tpmInfo) { errlHndl_t err = nullptr; uint8_t regData = 0; TRACDCOMP( g_trac_tpmdd, ENTER_MRK"tpmDrtmReset()" ); do { i_tpmInfo.offset = TPM_REG_TPM_HASH; regData = TPM_HASH_START; err = tpmWrite ( ®Data, sizeof(regData), i_tpmInfo ); if (err) { break; } regData = TPM_HASH_END; err = tpmWrite ( ®Data, sizeof(regData), i_tpmInfo ); if (err) { break; } } while( 0 ); TRACDCOMP( g_trac_tpmdd, EXIT_MRK"tpmDrtmReset()" ); return err; } // end tpmDrtmReset #endif // ------------------------------------------------------------------ // tpmPrepareAddress // ------------------------------------------------------------------ errlHndl_t tpmPrepareAddress ( void * io_buffer, size_t & o_bufSize, const tpm_info_t & i_tpmInfo ) { errlHndl_t err = NULL; o_bufSize = 0; TRACDCOMP( g_trac_tpmdd, ENTER_MRK"tpmPrepareAddress()" ); do { // -------------------------------------------------------------------- // Currently only supporting I2C devices and that use 0, 1, or 2 bytes // to set the offset (ie, internal address) into the device. // -------------------------------------------------------------------- switch( i_tpmInfo.addrSize ) { case TWO_BYTE_ADDR: o_bufSize = 2; *((uint8_t*)io_buffer) = (i_tpmInfo.offset & 0xFF00ull) >> 8; *((uint8_t*)io_buffer+1) = (i_tpmInfo.offset & 0x00FFull); break; case ONE_BYTE_ADDR: o_bufSize = 1; *((uint8_t*)io_buffer) = (i_tpmInfo.offset & 0xFFull); break; default: TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmPrepareAddress() - Invalid Device " "Address Size: 0x%08x", i_tpmInfo.addrSize); /*@ * @errortype * @reasoncode TPM_INVALID_DEVICE_TYPE * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_PREPAREADDRESS * @userdata1 TPM * @userdata2 Address Size (aka Device Type) * @devdesc The Device type not supported (addrSize) * @custdesc A problem was detected during the IPL of * the system: Device type not supported. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_PREPAREADDRESS, TPM_INVALID_DEVICE_TYPE, TARGETING::get_huid(i_tpmInfo.tpmTarget), i_tpmInfo.addrSize, true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); break; } } while( 0 ); TRACDCOMP( g_trac_tpmdd, EXIT_MRK"tpmPrepareAddress()" ); return err; } // end tpmPrepareAddress // ------------------------------------------------------------------ // tpmReadAttributes // ------------------------------------------------------------------ errlHndl_t tpmReadAttributes ( TARGETING::Target * i_target, tpm_info_t & io_tpmInfo, tpm_locality_t i_locality ) { errlHndl_t err = NULL; TRACDCOMP( g_trac_tpmdd, ENTER_MRK"tpmReadAttributes()" ); // These variables will be used to hold the TPM attribute data TARGETING::TpmInfo tpmData; uint8_t tpmModel = TPM_MODEL_UNDETERMINED; do { if( !( i_target-> tryGetAttr ( tpmData ) ) ) { const auto type = i_target->getAttr(); TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmReadAttributes: Failed to read TPM_INFO " "attribute from target HUID=0x%08X of type=0x%08X.", TARGETING::get_huid(i_target), type); /*@ * @errortype * @reasoncode TPM_ATTR_INFO_NOT_FOUND * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_READATTRIBUTES * @userdata1 HUID of target * @userdata2 Type of target * @devdesc TPM_INFO attribute was not found for the * requested target * @custdesc Unexpected trusted boot related failure */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_READATTRIBUTES, TPM_ATTR_INFO_NOT_FOUND, TARGETING::get_huid(i_target), type); // Could be FSP or HB code's fault err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_MED); err->addProcedureCallout(HWAS::EPUB_PRC_SP_CODE, HWAS::SRCI_PRIORITY_MED); err->collectTrace( TPMDD_COMP_NAME ); break; } if( !( i_target-> tryGetAttr ( tpmModel ) ) ) { const auto type = i_target->getAttr(); TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmReadAttributes: Failed to read TPM_MODEL " "attribute from target HUID=0x%08X of type=0x%08X.", TARGETING::get_huid(i_target), type); /*@ * @errortype * @reasoncode TPM_ATTR_MODEL_NOT_FOUND * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_READATTRIBUTES * @userdata1 HUID of target * @userdata2 Type of target * @devdesc TPM_MODEL attribute was not found for the * requested target * @custdesc Unexpected trusted boot related failure */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_READATTRIBUTES, TPM_ATTR_MODEL_NOT_FOUND, TARGETING::get_huid(i_target), type); // Could be FSP or HB code's fault err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_MED); err->addProcedureCallout(HWAS::EPUB_PRC_SP_CODE, HWAS::SRCI_PRIORITY_LOW); err->collectTrace( TPMDD_COMP_NAME ); break; } // Hostboot code only supports Nuvoton 65x and 75x Models at this time // so set model-specific attributes appropriately if (tpmModel == TPM_MODEL_65x) { io_tpmInfo.model = tpmModel; io_tpmInfo.sts = TPM_REG_65x_STS; io_tpmInfo.burstCount = TPM_REG_65x_BURSTCOUNT; io_tpmInfo.tpmHash = TPM_REG_65x_TPM_HASH; io_tpmInfo.wrFifo = TPM_REG_65x_WR_FIFO; io_tpmInfo.rdFifo = TPM_REG_65x_RD_FIFO; io_tpmInfo.vendorIdOffset = TPM_REG_65x_VENDOR_ID_OFFSET; io_tpmInfo.vendorId = TPM_VENDORID_65x; } else if (tpmModel == TPM_MODEL_75x) { io_tpmInfo.model = tpmModel; io_tpmInfo.sts = TPM_REG_75x_STS; io_tpmInfo.burstCount = TPM_REG_75x_BURSTCOUNT; io_tpmInfo.tpmHash = TPM_REG_75x_TPM_HASH; io_tpmInfo.wrFifo = TPM_REG_75x_WR_FIFO; io_tpmInfo.rdFifo = TPM_REG_75x_RD_FIFO; io_tpmInfo.vendorIdOffset = TPM_REG_75x_VENDOR_ID_OFFSET; io_tpmInfo.vendorId = TPM_VENDORID_75x; } else { // Fail since invalid/unsupported TPM model is found TRACFCOMP(g_trac_tpmdd,ERR_MRK "tpmReadAttributes: Invalid TPM_MODEL %d from " "attribute from target HUID=0x%08X", tpmModel, TARGETING::get_huid(i_target)); /*@ * @errortype * @reasoncode TPM_ATTR_INVALID_MODEL * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_READATTRIBUTES * @userdata1 TPM Model of target * @userdata2 HUID of target * @devdesc TPM_MODEL attribute was set to a value that * is not currently supported * @custdesc Unexpected trusted boot related failure */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_READATTRIBUTES, TPM_ATTR_INVALID_MODEL, tpmModel, TARGETING::get_huid(i_target)); // Could be FSP or HB code's fault err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_MED); err->addProcedureCallout(HWAS::EPUB_PRC_SP_CODE, HWAS::SRCI_PRIORITY_LOW); err->collectTrace( TPMDD_COMP_NAME ); break; } // Successful reading of the Attributes, so extract the data io_tpmInfo.port = tpmData.port; switch(i_locality) { case TPM_LOCALITY_0: io_tpmInfo.devAddr = tpmData.devAddrLocality0; break; case TPM_LOCALITY_2: io_tpmInfo.devAddr = tpmData.devAddrLocality2; break; case TPM_LOCALITY_4: io_tpmInfo.devAddr = tpmData.devAddrLocality4; break; default: assert(false, "BUG! Locality %d is not supported.", i_locality); } // Nuvoton 75x only supports one locality i2c address // (i.e. locality is handled differently) if (tpmModel == TPM_MODEL_75x) { io_tpmInfo.devAddr = tpmData.devAddrLocality0; } io_tpmInfo.engine = tpmData.engine; io_tpmInfo.i2cMasterPath = tpmData.i2cMasterPath; io_tpmInfo.tpmEnabled = tpmData.tpmEnabled; io_tpmInfo.tpmTarget = i_target; io_tpmInfo.i2cMuxBusSelector = tpmData.i2cMuxBusSelector; io_tpmInfo.i2cMuxPath = tpmData.i2cMuxPath; // Convert attribute info to tpm_addr_size_t enum if ( tpmData.byteAddrOffset == 0x2 ) { io_tpmInfo.addrSize = TWO_BYTE_ADDR; } else if ( tpmData.byteAddrOffset == 0x1 ) { io_tpmInfo.addrSize = ONE_BYTE_ADDR; } else { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmReadAttributes() - INVALID ADDRESS " "OFFSET SIZE %d!", tpmData.byteAddrOffset ); /*@ * @errortype * @reasoncode TPM_INVALID_ADDR_OFFSET_SIZE * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_READATTRIBUTES * @userdata1 HUID of target * @userdata2 Address Offset Size * @devdesc Invalid address offset size */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_READATTRIBUTES, TPM_INVALID_ADDR_OFFSET_SIZE, TARGETING::get_huid(i_target), tpmData.byteAddrOffset, true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); break; } // Get i2c master target err = tpmGetI2CMasterTarget( i_target, io_tpmInfo ); if( err ) { TRACFCOMP(g_trac_tpmdd, ERR_MRK"Error in tpmReadAttributes::tpmGetI2Cmaster() " "RC 0x%X", err->reasonCode()); break; } // Lookup bus speed TARGETING::ATTR_I2C_BUS_SPEED_ARRAY_type speeds; if( io_tpmInfo.i2cTarget-> tryGetAttr(speeds) && (io_tpmInfo.engine < I2C_BUS_MAX_ENGINE(speeds)) && (io_tpmInfo.port < I2C_BUS_MAX_PORT(speeds)) ) { io_tpmInfo.busFreq = speeds[io_tpmInfo.engine][io_tpmInfo.port]; io_tpmInfo.busFreq *= 1000; //convert KHz->Hz } else { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmReadAttributes() - BUS SPEED LOOKUP FAIL"); /*@ * @errortype * @reasoncode TPM_BUS_SPEED_LOOKUP_FAIL * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_READATTRIBUTES * @userdata1 HUID of target * @userdata2 Address Offset Size * @devdesc Can't find TPM in ATTR_I2C_BUS_SPEED_ARRAY */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_READATTRIBUTES, TPM_BUS_SPEED_LOOKUP_FAIL, TARGETING::get_huid(i_target), tpmData.byteAddrOffset, true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); break; } } while( 0 ); TRACUCOMP(g_trac_tpmdd,"tpmReadAttributes() tgt=0x%X, e/p/dA=%d/%d/0x%X, " "En=%d, aS=%d, aO=%d", TARGETING::get_huid(i_target), io_tpmInfo.engine, io_tpmInfo.port, io_tpmInfo.devAddr, io_tpmInfo.tpmEnabled, io_tpmInfo.addrSize, tpmData.byteAddrOffset); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = io_tpmInfo.i2cMuxPath.toString(); TRACUCOMP(g_trac_tpmdd, "tpmReadAttributes(): " "muxSelector=0x%X, muxPath=%s", io_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; TRACDCOMP( g_trac_tpmdd, EXIT_MRK"tpmReadAttributes()" ); return err; } // end tpmReadAttributes // ------------------------------------------------------------------ // tpmGetI2CMasterTarget // ------------------------------------------------------------------ errlHndl_t tpmGetI2CMasterTarget ( TARGETING::Target * i_target, tpm_info_t & io_tpmInfo ) { errlHndl_t err = NULL; TRACDCOMP( g_trac_tpmdd, ENTER_MRK"tpmGetI2CMasterTarget()" ); do { TARGETING::TargetService& tS = TARGETING::targetService(); // The path from i_target to its I2C Master was read from the // attribute via tpmReadAttributes() and passed to this function // in io_tpmInfo.i2cMasterPath // check that the path exists bool exists = false; tS.exists( io_tpmInfo.i2cMasterPath, exists ); if( !exists ) { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmGetI2CMasterTarget() - " "i2cMasterPath attribute path doesn't exist!" ); // Compress the i2cMasterPath entity path into a uint64_t value // Format is up to 4 path elements defined like type:8 instance:8 uint64_t l_epCompressed = 0; for( uint32_t i = 0; i < io_tpmInfo.i2cMasterPath.size(); i++ ) { // Path element: type:8 instance:8 l_epCompressed |= io_tpmInfo.i2cMasterPath[i].type << ((16*(3-i))+8); l_epCompressed |= io_tpmInfo.i2cMasterPath[i].instance << (16*(3-i)); // Can only fit 4 path elements into 64 bits if ( i == 3 ) { break; } } /*@ * @errortype * @reasoncode TPM_I2C_MASTER_PATH_ERROR * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_GETI2CMASTERTARGET * @userdata1 HUID of target * @userdata2 Compressed Entity Path * @devdesc I2C master entity path doesn't exist. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_GETI2CMASTERTARGET, TPM_I2C_MASTER_PATH_ERROR, TARGETING::get_huid(i_target), l_epCompressed, true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); char* l_masterPath = io_tpmInfo.i2cMasterPath.toString(); ERRORLOG::ErrlUserDetailsString(l_masterPath).addToLog(err); free(l_masterPath); l_masterPath = nullptr; break; } // Since it exists, convert to a target io_tpmInfo.i2cTarget = tS.toTarget( io_tpmInfo.i2cMasterPath ); if( NULL == io_tpmInfo.i2cTarget ) { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmGetI2CMasterTarget() - I2C Master " "Path target was NULL!" ); // Compress the entity path uint64_t l_epCompressed = 0; for( uint32_t i = 0; i < io_tpmInfo.i2cMasterPath.size(); i++ ) { // Path element: type:8 instance:8 l_epCompressed |= io_tpmInfo.i2cMasterPath[i].type << ((16*(3-i))+8); l_epCompressed |= io_tpmInfo.i2cMasterPath[i].instance << (16*(3-i)); // Can only fit 4 path elements into 64 bits if ( i == 3 ) { break; } } /*@ * @errortype * @reasoncode TPM_TARGET_NULL * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_GETI2CMASTERTARGET * @userdata1 HUID of target * @userdata2 Compressed Entity Path * @devdesc I2C master path target is null. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD::TPMDD_GETI2CMASTERTARGET, TPMDD::TPM_TARGET_NULL, TARGETING::get_huid(i_target), l_epCompressed, true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); char* l_masterPath = io_tpmInfo.i2cMasterPath.toString(); ERRORLOG::ErrlUserDetailsString(l_masterPath).addToLog(err); free(l_masterPath); l_masterPath = nullptr; break; } } while( 0 ); TRACDCOMP( g_trac_tpmdd, EXIT_MRK"tpmGetI2CMasterTarget()" ); return err; } // end tpmGetI2CMasterTarget errlHndl_t tpmWriteReg ( tpm_info_t i_tpmInfo, size_t i_offset, size_t i_buflen, void * i_buffer) { i_tpmInfo.offset = i_offset; return tpmWrite(i_buffer, i_buflen, i_tpmInfo); } // end tpmWriteReg errlHndl_t tpmReadReg ( tpm_info_t i_tpmInfo, size_t i_offset, size_t i_buflen, void * o_buffer) { i_tpmInfo.offset = i_offset; return tpmRead(o_buffer, i_buflen, i_tpmInfo); } // end tpmReadReg errlHndl_t tpmReadSTSReg ( tpm_info_t i_tpmInfo, tpm_sts_reg_t & o_stsReg) { i_tpmInfo.offset = i_tpmInfo.sts; return tpmRead(reinterpret_cast(&o_stsReg), 1, i_tpmInfo); } // end tpmReadSTSReg errlHndl_t tpmReadSTSRegValid ( tpm_info_t i_tpmInfo, tpm_sts_reg_t & o_stsReg) { errlHndl_t err = NULL; i_tpmInfo.offset = i_tpmInfo.sts; size_t polls = 0; do { err = tpmRead(reinterpret_cast(&o_stsReg), 1, i_tpmInfo); if (err) { break; } if (polls > TPMDD::MAX_STSVALID_POLLS) { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmReadSTSRegValid(): Timeout! " "e/p/dA=%d/%d/0x%X, %02X", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, o_stsReg.value); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmReadSTSRegValid(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_TIMEOUT * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_READSTSREGVALID * @userdata1 TPM * @userdata2[0:31] Operation * @userdata2[32:63] STS Reg * @devdesc TPM timeout waiting for stsValid * @custdesc A problem occurred during the IPL of the * system: TPM timeout */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_READSTSREGVALID, TPM_TIMEOUT, TARGETING::get_huid(i_tpmInfo.tpmTarget), TWO_UINT32_TO_UINT64( i_tpmInfo.operation, o_stsReg.value), true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); break; } else if (!o_stsReg.stsValid) { // Sleep 10ms before attempting another read nanosleep(0, 10 * NS_PER_MSEC); polls++; } } while (!o_stsReg.stsValid); return err; } // end tpmReadSTSRegValid errlHndl_t tpmIsCommandReady( const tpm_info_t & i_tpmInfo, bool & o_isReady) { tpm_sts_reg_t stsReg; errlHndl_t err = tpmReadSTSReg(i_tpmInfo, stsReg); o_isReady = false; if (NULL == err && stsReg.isCommandReady) { o_isReady = true; } return err; } // end tpmIsCommandReady errlHndl_t tpmPollForCommandReady( const tpm_info_t & i_tpmInfo) { tpm_sts_reg_t stsReg; errlHndl_t err = NULL; // Operation TIMEOUT_B defined by TCG spec for command ready for (size_t delay = 0; delay < TPMDD::TPM_TIMEOUT_B; delay += 10) { err = tpmReadSTSReg(i_tpmInfo, stsReg); if ((NULL == err && stsReg.isCommandReady) || (NULL != err)) { break; } // Sleep 10ms before attempting another read nanosleep(0, 10 * NS_PER_MSEC); } if (NULL == err && !stsReg.isCommandReady) { // The first write to command ready may have just aborted // an outstanding command, we will write it again and poll once // more err = tpmWriteCommandReady(i_tpmInfo); if (NULL == err) { // Ok, poll again // Operation TIMEOUT_B defined by TCG spec for command ready for (size_t delay = 0; delay < TPMDD::TPM_TIMEOUT_B; delay += 10) { err = tpmReadSTSReg(i_tpmInfo, stsReg); if ((NULL == err && stsReg.isCommandReady) || (NULL != err)) { break; } // Sleep 10ms before attempting another read nanosleep(0, 10 * NS_PER_MSEC); } } } if (NULL == err && !stsReg.isCommandReady) { // Timed out waiting for TPM to be ready TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmPollForCommandReady() - " "Timeout polling for command ready! " "e/p/dA=%d/%d/0x%X, OP=%d, " "STS=0x%X", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, stsReg.value ); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmPollForCommandReady(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_TIMEOUT * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_POLLFORCOMMMANDREADY * @userdata1 TPM * @userdata2 STS Reg * @devdesc Timeout waiting for TPM to enter command ready state. * @custdesc TPM operation failure */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_POLLFORCOMMMANDREADY, TPM_TIMEOUT, TARGETING::get_huid(i_tpmInfo.tpmTarget), stsReg.value, true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); char* l_masterPath = i_tpmInfo.i2cMasterPath.toString(); ERRORLOG::ErrlUserDetailsString(l_masterPath).addToLog(err); free(l_masterPath); l_masterPath = nullptr; } return err; } // end tpmPollForCommandReady errlHndl_t tpmIsExpecting( const tpm_info_t & i_tpmInfo, bool & o_isExpecting) { tpm_sts_reg_t stsReg; errlHndl_t err = tpmReadSTSRegValid(i_tpmInfo, stsReg); o_isExpecting = false; if (NULL == err && stsReg.expect) { o_isExpecting = true; } return err; } // end tpmIsExpecting errlHndl_t tpmIsDataAvail( const tpm_info_t & i_tpmInfo, bool & o_isDataAvail) { tpm_sts_reg_t stsReg; errlHndl_t err = tpmReadSTSRegValid(i_tpmInfo, stsReg); o_isDataAvail = false; if (NULL == err && stsReg.dataAvail) { o_isDataAvail = true; } return err; } // end tpmIsDataAvail errlHndl_t tpmPollForDataAvail( const tpm_info_t & i_tpmInfo) { tpm_sts_reg_t stsReg; errlHndl_t err = NULL; // Use the longer timeout B here since some of the TPM commands may take // more than timeout A to complete for (size_t delay = 0; delay < TPMDD::TPM_TIMEOUT_B; delay += 10) { err = tpmReadSTSRegValid(i_tpmInfo, stsReg); if ((NULL == err && stsReg.dataAvail) || (NULL != err)) { break; } // Sleep 10ms before attempting another read nanosleep(0, 10 * NS_PER_MSEC); } if (NULL == err && !stsReg.dataAvail) { // Timed out waiting for TPM to have more data available TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmPollForDataAvail() - " "Timeout polling for dataAvail! " "e/p/dA=%d/%d/0x%X, OP=%d, STS=0x%X", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_tpmInfo.operation, stsReg.value ); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmPollForDataAvail(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_TIMEOUT * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_POLLFORDATAAVAIL * @userdata1 TPM * @userdata2 STS Reg * @devdesc Timeout waiting for TPM data available. * @custdesc TPM operation failure */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_POLLFORDATAAVAIL, TPM_TIMEOUT, TARGETING::get_huid(i_tpmInfo.tpmTarget), stsReg.value, true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); char* l_masterPath = i_tpmInfo.i2cMasterPath.toString(); ERRORLOG::ErrlUserDetailsString(l_masterPath).addToLog(err); free(l_masterPath); l_masterPath = nullptr; } return err; } // end tpmPollForDataAvail errlHndl_t tpmReadBurstCount( const tpm_info_t & i_tpmInfo, uint16_t & o_burstCount) { errlHndl_t err = NULL; o_burstCount = 0; // Read the burst count uint16_t burstCount = 0; if (NULL == err) { err = tpmReadReg(i_tpmInfo, i_tpmInfo.burstCount, 2, reinterpret_cast(&burstCount)); } if (NULL == err) { o_burstCount = (burstCount & 0x00FF) << 8; o_burstCount |= (burstCount & 0xFF00) >> 8; } TRACUCOMP( g_trac_tpmdd, "tpmReadBurstCount() - BurstCount %d", o_burstCount); return err; } // end tpmReadBurstCount errlHndl_t tpmWriteCommandReady( const tpm_info_t & i_tpmInfo) { tpm_sts_reg_t stsReg; stsReg.value = 0; stsReg.isCommandReady = 1; return tpmWriteReg(i_tpmInfo, i_tpmInfo.sts, 1, reinterpret_cast(&stsReg)); } // end tpmWriteCommandReady errlHndl_t tpmWriteTpmGo( const tpm_info_t & i_tpmInfo) { tpm_sts_reg_t stsReg; stsReg.value = 0; stsReg.tpmGo = 1; return tpmWriteReg(i_tpmInfo, i_tpmInfo.sts, 1, reinterpret_cast(&stsReg)); } // end tpmWriteTpmGo errlHndl_t tpmWriteResponseRetry( const tpm_info_t & i_tpmInfo) { tpm_sts_reg_t stsReg; stsReg.value = 0; stsReg.responseRetry = 1; return tpmWriteReg(i_tpmInfo, i_tpmInfo.sts, 1, reinterpret_cast(&stsReg)); } // end tpmWriteResponseRetry errlHndl_t tpmWriteFifo( const tpm_info_t & i_tpmInfo, void * i_buffer, size_t i_buflen) { size_t delay = 0; size_t curByte = 0; uint8_t* bytePtr = (uint8_t*)i_buffer; uint8_t* curBytePtr = NULL; uint16_t burstCount = 0; errlHndl_t err = NULL; bool expecting = false; // We will transfer the command except for the last byte // that will be transfered separately to allow for // overflow checking size_t length = i_buflen - 1; size_t tx_len = 0; do { err = tpmReadBurstCount(i_tpmInfo, burstCount); if (err) { break; } else if (0 == burstCount) { // Need to delay to allow the TPM time nanosleep(0, 10 * NS_PER_MSEC); // 10ms delay += 10; continue; } // Send in some data delay = 0; curBytePtr = &(bytePtr[curByte]); tx_len = (curByte + burstCount > length ? (length - curByte) : burstCount); err = tpmWriteReg(i_tpmInfo, i_tpmInfo.wrFifo, tx_len, curBytePtr); if (err) { break; } curByte += tx_len; // TPM should be expecting more data from the command err = tpmIsExpecting(i_tpmInfo, expecting); if (NULL == err && !expecting) { // TPM is not expecting more data, we overflowed TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmWriteFifo(): Data Overflow! " "e/p/dA=%d/%d/0x%X, blen=%d, clen=%d", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_buflen, curByte); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWriteFifo(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_OVERFLOW_ERROR * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_WRITEFIFO * @userdata1 TPM * @userdata2[0:31] Current byte * @userdata2[32:63] Buffer Length (in Bytes) * @devdesc TPM expected less data during FIFO write * @custdesc A problem occurred during the IPL of the * system: TPM overflow */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_WRITEFIFO, TPM_OVERFLOW_ERROR, TARGETING::get_huid(i_tpmInfo.tpmTarget), TWO_UINT32_TO_UINT64( curByte, i_buflen ), true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); } if (err) { break; } // Everything but the last byte sent? if (curByte >= length) { break; } // Operation TIMEOUT_D defined by TCG spec for FIFO availability } while (delay < TPMDD::TPM_TIMEOUT_D); if (NULL == err && delay < TPMDD::TPM_TIMEOUT_D) { delay = 0; // Send the final byte do { err = tpmReadBurstCount(i_tpmInfo, burstCount); if (err) { break; } else if (0 == burstCount) { // Need to delay to allow the TPM time nanosleep(0, 10 * NS_PER_MSEC); // 10ms delay += 10; continue; } // Send in some data delay = 0; curBytePtr = &(bytePtr[curByte]); err = tpmWriteReg(i_tpmInfo, i_tpmInfo.wrFifo, 1, curBytePtr); // done break; // Operation TIMEOUT_D defined by TCG spec for FIFO availability } while (delay < TPMDD::TPM_TIMEOUT_D); } if (NULL == err && delay >= TPMDD::TPM_TIMEOUT_D) { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmWriteFifo(): Timeout! " "e/p/dA=%d/%d/0x%X, blen=%d, clen=%d", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_buflen, curByte); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWriteFifo(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_TIMEOUT * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_WRITEFIFO * @userdata1 TPM * @userdata2[0:31] Current Byte * @userdata2[32:63] Buffer Length (in Bytes) * @devdesc TPM timeout writing to FIFO * @custdesc A problem occurred during the IPL of the * system: TPM timeout */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_WRITEFIFO, TPM_TIMEOUT, TARGETING::get_huid(i_tpmInfo.tpmTarget), TWO_UINT32_TO_UINT64( curByte, i_buflen ), true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); } if (NULL == err) { err = tpmIsExpecting(i_tpmInfo, expecting); if (NULL == err && expecting) { // TPM is expecting more data even though we think we are done TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmWriteFifo(): Data Underflow! " "e/p/dA=%d/%d/0x%X, blen=%d, clen=%d", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, i_buflen, curByte); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmWriteFifo(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_UNDERFLOW_ERROR * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_WRITEFIFO * @userdata1 TPM * @userdata2[0:31] Current Byte * @userdata2[32:63] Buffer Length (in Bytes) * @devdesc TPM expected more data during FIFO write * @custdesc A problem occurred during the IPL of the * system: TPM underflow */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_WRITEFIFO, TPM_UNDERFLOW_ERROR, TARGETING::get_huid(i_tpmInfo.tpmTarget), TWO_UINT32_TO_UINT64( curByte, i_buflen ), true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); } } return err; } // end tpmWriteFifo errlHndl_t tpmReadFifo( const tpm_info_t & i_tpmInfo, void * o_buffer, size_t & io_buflen) { size_t delay = 0; size_t curByte = 0; uint8_t* bytePtr = (uint8_t*)o_buffer; uint8_t* curBytePtr = NULL; uint16_t burstCount = 0; errlHndl_t err = NULL; bool dataAvail = false; // Verify the TPM has data waiting for us err = tpmPollForDataAvail(i_tpmInfo); if( !err ) { do { err = tpmReadBurstCount(i_tpmInfo, burstCount); if (err) { break; } else if (0 == burstCount) { // Need to delay to allow the TPM time nanosleep(0, 10 * NS_PER_MSEC); // 10ms delay += 10; continue; } // Check for a buffer overflow if (curByte + burstCount > io_buflen) { // TPM is expecting more data even though we think we are done TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmReadFifo(): Data Overflow! " "e/p/dA=%d/%d/0x%X, blen=%d, clen=%d", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, io_buflen, curByte + burstCount); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmReadFifo(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_OVERFLOW_ERROR * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_READFIFO * @userdata1 TPM * @userdata2[0:15] Operation * @userdata2[16:31] Current Byte * @userdata2[32:63] Buffer Length (in Bytes) * @devdesc TPM provided more data during FIFO read * then buffer space provided * @custdesc A problem occurred during the IPL of the * system: TPM overflow */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_READFIFO, TPM_OVERFLOW_ERROR, TARGETING::get_huid(i_tpmInfo.tpmTarget), TWO_UINT32_TO_UINT64( TWO_UINT16_TO_UINT32(i_tpmInfo.operation, curByte), io_buflen), true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); break; } // Read some data delay = 0; curBytePtr = &(bytePtr[curByte]); err = tpmReadReg(i_tpmInfo, i_tpmInfo.rdFifo, burstCount, curBytePtr); if (err) { break; } curByte += burstCount; err = tpmIsDataAvail(i_tpmInfo, dataAvail); if (err || !dataAvail) { break; } // Operation TIMEOUT_D defined by TCG spec for FIFO availability } while (delay < TPMDD::TPM_TIMEOUT_D); } if (!err && delay >= TPMDD::TPM_TIMEOUT_D) { TRACFCOMP( g_trac_tpmdd, ERR_MRK"tpmReadFifo(): Timeout! " "e/p/dA=%d/%d/0x%X, blen=%d, clen=%d", i_tpmInfo.engine, i_tpmInfo.port, i_tpmInfo.devAddr, io_buflen, curByte); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = i_tpmInfo.i2cMuxPath.toString(); TRACFCOMP(g_trac_tpmdd, ERR_MRK"tpmReadFifo(): " "muxSelector=0x%X, muxPath=%s", i_tpmInfo.i2cMuxBusSelector, l_muxPath); free(l_muxPath); l_muxPath = nullptr; /*@ * @errortype * @reasoncode TPM_TIMEOUT * @severity ERRL_SEV_UNRECOVERABLE * @moduleid TPMDD_READFIFO * @userdata1 TPM * @userdata2[0:15] Operation * @userdata2[16:31] Current Byte * @userdata2[32:63] Buffer Length (in Bytes) * @devdesc TPM timeout writing to FIFO * @custdesc A problem occurred during the IPL of the * system: TPM timeout */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, TPMDD_READ, TPM_TIMEOUT, TARGETING::get_huid(i_tpmInfo.tpmTarget), TWO_UINT32_TO_UINT64( TWO_UINT16_TO_UINT32(i_tpmInfo.operation, curByte), io_buflen), true /*Add HB SW Callout*/ ); err->collectTrace( TPMDD_COMP_NAME ); } if (NULL == err) { // We read it properly tell the caller the result length io_buflen = curByte; } else { io_buflen = 0; } return err; } // end tpmReadFifo } // end namespace TPMDD